param(
    [Parameter(Mandatory = $true)]
    [string]$InputDirectory
)

$ErrorActionPreference = 'Stop'
$invariant = [Globalization.CultureInfo]::InvariantCulture
[Threading.Thread]::CurrentThread.CurrentCulture = $invariant
[Threading.Thread]::CurrentThread.CurrentUICulture = $invariant

function ConvertTo-Number($Value) {
    if ($Value -is [double] -or $Value -is [float] -or $Value -is [decimal]) {
        return [double]$Value
    }
    $text = ([string]$Value).Trim()
    if ($text.Contains(',') -and -not $text.Contains('.')) {
        $text = $text.Replace(',', '.')
    }
    return [double]::Parse($text, [Globalization.NumberStyles]::Float, $invariant)
}

$directory = if ([IO.Path]::IsPathRooted($InputDirectory)) {
    [IO.Path]::GetFullPath($InputDirectory)
} else {
    [IO.Path]::GetFullPath((Join-Path (Resolve-Path '.').Path $InputDirectory))
}
$input = Join-Path $directory 'cell-medians-large.csv'
if (-not (Test-Path -LiteralPath $input -PathType Leaf)) {
    throw "Missing cell medians: $input"
}

function Get-GeometricMean($Values) {
    $positive = @($Values | Where-Object { (ConvertTo-Number $_) -gt 0 })
    if ($positive.Count -eq 0) { return [double]::NaN }
    $meanLog = ($positive | ForEach-Object {
        [math]::Log((ConvertTo-Number $_))
    } | Measure-Object -Average).Average
    return [math]::Exp($meanLog)
}

$medians = @(Import-Csv -LiteralPath $input)
$comparisons = @($medians | Group-Object profile,type,workload,n | ForEach-Object {
    $rows = @($_.Group)
    $fixed = @($rows | Where-Object {
        $_.structure -like 'tiered_forced_leaf_*'
    } | Sort-Object { ConvertTo-Number $_.median_ns_per_op })
    $adaptive = @($rows | Where-Object {
        $_.structure -like 'adaptive_shape_deferred*'
    })[0]
    $vector = @($rows | Where-Object {
        $_.structure -eq 'std::vector_reserved'
    })[0]
    if ($fixed.Count -ne 5 -or $null -eq $adaptive -or $null -eq $vector) {
        throw "Incomplete seven-candidate cell: $($_.Name)"
    }

    $best = $fixed[0]
    $ppmMatch = [regex]::Match($adaptive.workload, '_edit_ppm_(\d+)$')
    if (-not $ppmMatch.Success) { throw "Invalid large workload: $($adaptive.workload)" }
    $editPpm = [int]$ppmMatch.Groups[1].Value
    $pattern = $adaptive.workload.Substring(
        0, $adaptive.workload.Length - $ppmMatch.Value.Length)
    $bestLeaf = [regex]::Match($best.structure, 'leaf_(\d+)').Groups[1].Value
    $fixedNs = ConvertTo-Number $best.median_ns_per_op
    $adaptiveNs = ConvertTo-Number $adaptive.median_ns_per_op
    $vectorNs = ConvertTo-Number $vector.median_ns_per_op

    [pscustomobject]@{
        profile = $adaptive.profile
        type = $adaptive.type
        n = [long]$adaptive.n
        pattern = $pattern
        edit_ppm = $editPpm
        workload = $adaptive.workload
        best_fixed_leaf = [int]$bestLeaf
        best_fixed_ns_per_op = $fixedNs
        adaptive_ns_per_op = $adaptiveNs
        vector_ns_per_op = $vectorNs
        adaptive_over_best_fixed = $adaptiveNs / $fixedNs
        vector_over_best_fixed = $vectorNs / $fixedNs
        adaptive_over_vector = $adaptiveNs / $vectorNs
        adaptive_switches = ConvertTo-Number $adaptive.median_switches
        adaptive_leaf_rebuilds = ConvertTo-Number $adaptive.median_leaf_rebuilds
        adaptive_final_mode = $adaptive.final_mode
        adaptive_final_leaf = [int]$adaptive.final_leaf
        adaptive_transition_ns = ConvertTo-Number $adaptive.median_max_transition_ns
        adaptive_construction_ns = ConvertTo-Number $adaptive.median_construction_ns
        vector_construction_ns = ConvertTo-Number $vector.median_construction_ns
        best_fixed_construction_ns = ConvertTo-Number $best.median_construction_ns
    }
})

$comparisons | Sort-Object n,pattern,edit_ppm,profile |
    Export-Csv -LiteralPath (Join-Path $directory 'comparison-large.csv') `
        -NoTypeInformation -Encoding utf8

$bySize = @($comparisons | Group-Object n | ForEach-Object {
    $cells = @($_.Group)
    [pscustomobject]@{
        n = [long]$cells[0].n
        cells = $cells.Count
        gm_adaptive_over_best_fixed = Get-GeometricMean $cells.adaptive_over_best_fixed
        gm_vector_over_best_fixed = Get-GeometricMean $cells.vector_over_best_fixed
        gm_adaptive_over_vector = Get-GeometricMean $cells.adaptive_over_vector
        adaptive_beats_best_fixed = @($cells | Where-Object {
            $_.adaptive_over_best_fixed -lt 1
        }).Count
        adaptive_beats_vector = @($cells | Where-Object {
            $_.adaptive_over_vector -lt 1
        }).Count
        adaptive_final_vector = @($cells | Where-Object {
            $_.adaptive_final_mode -eq 'vector'
        }).Count
        adaptive_final_tiered = @($cells | Where-Object {
            $_.adaptive_final_mode -eq 'tiered'
        }).Count
    }
})
$bySize | Sort-Object n | Export-Csv `
    -LiteralPath (Join-Path $directory 'comparison-by-size.csv') `
    -NoTypeInformation -Encoding utf8

$byWorkload = @($comparisons | Group-Object n,pattern,edit_ppm | ForEach-Object {
    $cells = @($_.Group)
    [pscustomobject]@{
        n = [long]$cells[0].n
        pattern = $cells[0].pattern
        edit_ppm = [int]$cells[0].edit_ppm
        profiles = $cells.Count
        best_fixed_leaf_mode = ($cells | Group-Object best_fixed_leaf |
            Sort-Object Count -Descending | Select-Object -First 1).Name
        gm_adaptive_over_best_fixed = Get-GeometricMean $cells.adaptive_over_best_fixed
        gm_vector_over_best_fixed = Get-GeometricMean $cells.vector_over_best_fixed
        gm_adaptive_over_vector = Get-GeometricMean $cells.adaptive_over_vector
        mean_switches = ($cells.adaptive_switches | Measure-Object -Average).Average
        mean_leaf_rebuilds = ($cells.adaptive_leaf_rebuilds | Measure-Object -Average).Average
        final_modes = (($cells.adaptive_final_mode | Group-Object | ForEach-Object {
            "$($_.Name):$($_.Count)"
        }) -join ';')
    }
})
$byWorkload | Sort-Object n,pattern,edit_ppm | Export-Csv `
    -LiteralPath (Join-Path $directory 'comparison-by-workload.csv') `
    -NoTypeInformation -Encoding utf8

$winners = @($comparisons | Group-Object best_fixed_leaf | ForEach-Object {
    [pscustomobject]@{
        leaf = [int]$_.Name
        wins = $_.Count
        percent = 100.0 * $_.Count / $comparisons.Count
    }
})
$winners | Sort-Object wins -Descending | Export-Csv `
    -LiteralPath (Join-Path $directory 'fixed-leaf-winners.csv') `
    -NoTypeInformation -Encoding utf8

Write-Host "Large comparison analysis: $directory"
$bySize | Sort-Object n | Format-Table -AutoSize
$winners | Sort-Object wins -Descending | Format-Table -AutoSize
