#!/usr/bin/env php
<?php
declare(strict_types=1);

function arg_value(array $argv, string $name, ?string $default = null): ?string {
    foreach ($argv as $i => $arg) {
        if ($arg === $name && isset($argv[$i + 1])) {
            return $argv[$i + 1];
        }
        if (str_starts_with($arg, $name . '=')) {
            return substr($arg, strlen($name) + 1);
        }
    }
    return $default;
}

function arg_list(array $argv, string $name): ?array {
    $value = arg_value($argv, $name);
    if ($value === null || trim($value) === '') {
        return null;
    }
    return array_values(array_filter(array_map('trim', explode(',', $value)), static fn($x) => $x !== ''));
}

function arg_bool(array $argv, string $name, bool $default): bool {
    $value = arg_value($argv, $name);
    if ($value === null) {
        return $default;
    }
    $value = strtolower(trim($value));
    return in_array($value, ['1', 'true', 'yes', 'on'], true);
}

function mkdirp(string $dir): void {
    if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
        throw new RuntimeException("Cannot create directory: {$dir}");
    }
}

function read_smaps_rollup_kb(int $pid): array {
    $path = "/proc/{$pid}/smaps_rollup";
    $keys = [
        'Rss' => 'rss_kb',
        'Pss' => 'pss_kb',
        'Private_Clean' => 'private_clean_kb',
        'Private_Dirty' => 'private_dirty_kb',
        'Anonymous' => 'anonymous_kb',
        'AnonHugePages' => 'anon_huge_pages_kb',
    ];
    $out = [
        'rss_kb' => 0,
        'pss_kb' => 0,
        'private_clean_kb' => 0,
        'private_dirty_kb' => 0,
        'anonymous_kb' => 0,
        'anon_huge_pages_kb' => 0,
    ];
    if (!is_readable($path)) {
        return $out;
    }
    $lines = @file($path, FILE_IGNORE_NEW_LINES);
    if ($lines === false) {
        return $out;
    }
    foreach ($lines as $line) {
        if (!preg_match('/^([A-Za-z_]+):\s+(\d+)\s+kB$/', $line, $m)) {
            continue;
        }
        if (isset($keys[$m[1]])) {
            $out[$keys[$m[1]]] = (int)$m[2];
        }
    }
    return $out;
}

function peak_max(array $peak, array $current): array {
    foreach ($current as $key => $value) {
        $peak[$key] = max((int)($peak[$key] ?? 0), (int)$value);
    }
    $peak['private_total_kb'] = ($peak['private_clean_kb'] ?? 0) + ($peak['private_dirty_kb'] ?? 0);
    $peak['anonymous_total_kb'] = ($peak['anonymous_kb'] ?? 0) + ($peak['anon_huge_pages_kb'] ?? 0);
    return $peak;
}

function set_nonblocking(array $pipes): void {
    foreach ($pipes as $pipe) {
        stream_set_blocking($pipe, false);
    }
}

function run_measured(array $command, int $timeoutSeconds = 900): array {
    $desc = [
        0 => ['file', '/dev/null', 'r'],
        1 => ['pipe', 'w'],
        2 => ['pipe', 'w'],
    ];
    $started = hrtime(true);
    $proc = proc_open($command, $desc, $pipes);
    if (!is_resource($proc)) {
        throw new RuntimeException('Cannot start command: ' . implode(' ', $command));
    }
    set_nonblocking($pipes);

    $status = proc_get_status($proc);
    $pid = (int)($status['pid'] ?? 0);
    $stdout = '';
    $stderr = '';
    $peak = [];
    $timedOut = false;

    while (true) {
        $stdout .= stream_get_contents($pipes[1]);
        $stderr .= stream_get_contents($pipes[2]);
        $status = proc_get_status($proc);
        if ($pid > 0) {
            $peak = peak_max($peak, read_smaps_rollup_kb($pid));
        }
        $elapsed = (hrtime(true) - $started) / 1_000_000_000;
        if (!$status['running']) {
            break;
        }
        if ($elapsed > $timeoutSeconds) {
            $timedOut = true;
            proc_terminate($proc, 15);
            usleep(100000);
            $status = proc_get_status($proc);
            if ($status['running']) {
                proc_terminate($proc, 9);
            }
            break;
        }
        usleep(10000);
    }

    $stdout .= stream_get_contents($pipes[1]);
    $stderr .= stream_get_contents($pipes[2]);
    foreach ($pipes as $pipe) {
        fclose($pipe);
    }
    $exitCode = proc_close($proc);
    $wall = (hrtime(true) - $started) / 1_000_000_000;

    preg_match('/^RESULT\s+(\S+)\s+(\d+)\s+(.+)$/m', $stdout, $m);
    $iterations = isset($m[2]) ? (int)$m[2] : null;
    $checksum = $m[3] ?? null;

    return [
        'command' => $command,
        'exit_code' => $exitCode,
        'timed_out' => $timedOut,
        'wall_seconds' => $wall,
        'iterations' => $iterations,
        'throughput_per_second' => $iterations !== null && $wall > 0 ? $iterations / $wall : null,
        'checksum' => $checksum,
        'stdout' => $stdout,
        'stderr' => $stderr,
        'stdout_sha256' => hash('sha256', $stdout),
        'stderr_sha256' => hash('sha256', $stderr),
        'stdout_tail' => substr($stdout, -2000),
        'stderr_tail' => substr($stderr, -2000),
        'peak' => [
            'rss_kb' => (int)($peak['rss_kb'] ?? 0),
            'pss_kb' => (int)($peak['pss_kb'] ?? 0),
            'private_clean_kb' => (int)($peak['private_clean_kb'] ?? 0),
            'private_dirty_kb' => (int)($peak['private_dirty_kb'] ?? 0),
            'private_total_kb' => (int)(($peak['private_clean_kb'] ?? 0) + ($peak['private_dirty_kb'] ?? 0)),
            'anonymous_kb' => (int)($peak['anonymous_kb'] ?? 0),
            'anon_huge_pages_kb' => (int)($peak['anon_huge_pages_kb'] ?? 0),
            'anonymous_total_kb' => (int)(($peak['anonymous_kb'] ?? 0) + ($peak['anon_huge_pages_kb'] ?? 0)),
        ],
    ];
}

function median(array $values): ?float {
    $values = array_values(array_filter($values, static fn($v) => is_numeric($v)));
    if (!$values) {
        return null;
    }
    sort($values, SORT_NUMERIC);
    $n = count($values);
    $mid = intdiv($n, 2);
    if (($n % 2) === 1) {
        return (float)$values[$mid];
    }
    return ((float)$values[$mid - 1] + (float)$values[$mid]) / 2.0;
}

function fmt(?float $value, int $decimals = 3): string {
    return $value === null ? 'n/a' : number_format($value, $decimals, '.', '');
}

function fmt_human(?float $value, int $decimals = 3): string {
    return $value === null ? 'n/a' : number_format($value, $decimals, '.', ',');
}

function md_escape(string $value): string {
    return str_replace(["\r", "\n", '|'], ['\\r', ' ', '\\|'], $value);
}

function padded_md_row(array $cells, array $widths): string {
    $out = [];
    foreach ($cells as $i => $cell) {
        $width = $widths[$i] ?? strlen((string)$cell);
        $out[] = str_pad(md_escape((string)$cell), $width, ' ', STR_PAD_RIGHT);
    }
    return '| ' . implode(' | ', $out) . " |\n";
}

function padded_md_separator(array $widths): string {
    return '| ' . implode(' | ', array_map(static fn($width) => str_repeat('-', $width), $widths)) . " |\n";
}

function benchmark_cell(?array $metric, array $spec): string {
    if ($metric === null) {
        return 'NO COMMAND';
    }
    if (!empty($metric['compile_error'])) {
        return 'COMPILE ERROR';
    }
    if (($metric['ok'] ?? 0) <= 0) {
        return 'FAILED';
    }
    $value = $metric[$spec['key']] ?? null;
    if (!is_numeric($value)) {
        return 'n/a';
    }
    $decimals = (int)($spec['decimals'] ?? 0);
    return fmt_human((float)$value, $decimals) . (string)($spec['suffix'] ?? '');
}

function ordered_engine_group(array $engineIds, array $wanted): array {
    $out = [];
    foreach ($wanted as $engineId) {
        if (in_array($engineId, $engineIds, true)) {
            $out[] = $engineId;
        }
    }
    return $out;
}

function build_path_engine_order(array $engineIds): array {
    return ordered_engine_group($engineIds, [
        'php-cli',
        'php-cli-opcache-nojit',
        'php-cli-opcache-jit',
        'pwphpbytecode',
        'pwphpbytecode-jit',
        'php2vala-embed-opt1',
        'php2vala-pwphp-opt1',
        'php2vala-both-opt1',
        'php2vala-native-opt1',
    ]);
}

function php2vala_optimizer_engine_order(array $engineIds): array {
    $wanted = [];
    foreach (['embed', 'pwphp', 'both', 'native'] as $mode) {
        for ($opt = 1; $opt <= 4; $opt++) {
            $wanted[] = "php2vala-{$mode}-opt{$opt}";
        }
    }
    return ordered_engine_group($engineIds, $wanted);
}

function other_engine_order(array $engineIds, array ...$groups): array {
    $used = [];
    foreach ($groups as $group) {
        foreach ($group as $engineId) {
            $used[$engineId] = true;
        }
    }
    $out = [];
    foreach ($engineIds as $engineId) {
        if (!isset($used[$engineId])) {
            $out[] = $engineId;
        }
    }
    return $out;
}

function benchmark_matrix_md(array $metrics, array $caseIds, array $engineIds, array $spec): string {
    if ($engineIds === []) {
        return '';
    }
    $matrix = [];
    $widths = [max(11, strlen('Test script'))];
    foreach ($engineIds as $i => $engineId) {
        $widths[$i + 1] = max(strlen($engineId), strlen('COMPILE ERROR'), strlen('NO COMMAND'), strlen('FAILED'));
    }

    foreach ($caseIds as $caseId) {
        $row = [$caseId];
        $widths[0] = max($widths[0], strlen($caseId));
        foreach ($engineIds as $i => $engineId) {
            $value = benchmark_cell($metrics[$caseId][$engineId] ?? null, $spec);
            $row[] = $value;
            $widths[$i + 1] = max($widths[$i + 1], strlen($value));
        }
        $matrix[] = $row;
    }

    $md = padded_md_row(array_merge(['Test script'], $engineIds), $widths);
    $md .= padded_md_separator($widths);
    foreach ($matrix as $row) {
        $md .= padded_md_row($row, $widths);
    }
    return $md . "\n";
}

function build_failure_reason(array $failure): string {
    $stage = (string)($failure['stage'] ?? 'build');
    $details = $failure['failure'] ?? [];
    $tail = '';
    if (is_array($details)) {
        $tail = trim((string)($details['stderr_tail'] ?? ''));
        if ($tail === '') {
            $tail = trim((string)($details['stdout_tail'] ?? ''));
        }
        $exit = array_key_exists('exit_code', $details) ? ' exit ' . (string)$details['exit_code'] : '';
    } else {
        $exit = '';
    }
    $line = '';
    if ($tail !== '') {
        $parts = preg_split('/\R/', $tail);
        if ($parts !== false) {
            foreach ($parts as $part) {
                $part = trim($part);
                if ($part !== '') {
                    $line = ': ' . substr($part, 0, 240);
                    break;
                }
            }
        }
    }
    return 'COMPILE ERROR at ' . $stage . $exit . $line;
}

function load_build_failures(string $buildReportFile, ?array $filterEngines, ?array $filterCases): array {
    $out = [
        'failures' => [],
        'engine_ids' => [],
        'case_ids' => [],
        'skips' => [],
    ];
    if (!is_file($buildReportFile)) {
        return $out;
    }
    $report = json_decode((string)file_get_contents($buildReportFile), true, flags: JSON_THROW_ON_ERROR);
    foreach (array_keys($report['engines'] ?? []) as $engineId) {
        $engineId = (string)$engineId;
        if ($filterEngines !== null && !in_array($engineId, $filterEngines, true)) {
            continue;
        }
        $out['engine_ids'][] = $engineId;
    }
    foreach (($report['skips'] ?? []) as $skip) {
        $engineId = (string)($skip['engine_id'] ?? '');
        $caseId = (string)($skip['case_id'] ?? '');
        if ($engineId === '' || $caseId === '') {
            continue;
        }
        if ($filterEngines !== null && !in_array($engineId, $filterEngines, true)) {
            continue;
        }
        if ($filterCases !== null && !in_array($caseId, $filterCases, true)) {
            continue;
        }
        $out['skips'][$caseId][$engineId] = $skip;
        $out['case_ids'][] = $caseId;
        if (!in_array($engineId, $out['engine_ids'], true)) {
            $out['engine_ids'][] = $engineId;
        }
    }
    foreach (($report['failures'] ?? []) as $failure) {
        $engineId = (string)($failure['engine_id'] ?? '');
        $caseId = (string)($failure['case_id'] ?? '');
        if ($engineId === '' || $caseId === '') {
            continue;
        }
        if ($filterEngines !== null && !in_array($engineId, $filterEngines, true)) {
            continue;
        }
        if ($filterCases !== null && !in_array($caseId, $filterCases, true)) {
            continue;
        }
        $out['failures'][$caseId][$engineId] = $failure;
        $out['case_ids'][] = $caseId;
        if (!in_array($engineId, $out['engine_ids'], true)) {
            $out['engine_ids'][] = $engineId;
        }
    }
    $out['engine_ids'] = array_values(array_unique($out['engine_ids']));
    $out['case_ids'] = array_values(array_unique($out['case_ids']));
    return $out;
}

function write_benchmark_matrix(string $dir, array $metrics, array $caseIds, array $engineIds): void {
    $specs = [
        [
            'key' => 'throughput',
            'title' => 'Median Throughput',
            'description' => 'Median `RESULT` iterations per second.',
            'decimals' => 1,
            'suffix' => '/s',
        ],
        [
            'key' => 'wall',
            'title' => 'Median Wall Time',
            'description' => 'Median wall-clock runtime in seconds.',
            'decimals' => 3,
            'suffix' => 's',
        ],
        [
            'key' => 'rss',
            'title' => 'Peak RSS',
            'description' => 'Peak `Rss` from `/proc/<pid>/smaps_rollup`, in KiB.',
        ],
        [
            'key' => 'pss',
            'title' => 'Peak PSS',
            'description' => 'Peak `Pss` from `/proc/<pid>/smaps_rollup`, in KiB.',
        ],
        [
            'key' => 'private_clean',
            'title' => 'Peak Private Clean',
            'description' => 'Peak `Private_Clean` from `/proc/<pid>/smaps_rollup`, in KiB.',
        ],
        [
            'key' => 'private_dirty',
            'title' => 'Peak Private Dirty',
            'description' => 'Peak `Private_Dirty` from `/proc/<pid>/smaps_rollup`, in KiB.',
        ],
        [
            'key' => 'private_total',
            'title' => 'Peak Private Total',
            'description' => 'Peak `Private_Clean + Private_Dirty`, in KiB.',
        ],
        [
            'key' => 'anonymous',
            'title' => 'Peak Anonymous',
            'description' => 'Peak `Anonymous` from `/proc/<pid>/smaps_rollup`, in KiB.',
        ],
        [
            'key' => 'anon_huge_pages',
            'title' => 'Peak Anonymous Huge Pages',
            'description' => 'Peak `AnonHugePages` from `/proc/<pid>/smaps_rollup`, in KiB.',
        ],
        [
            'key' => 'anonymous_total',
            'title' => 'Peak Anonymous Total',
            'description' => 'Peak `Anonymous + AnonHugePages`, in KiB.',
        ],
    ];

    $md = "# Benchmark Matrices\n\n";
    $md .= "Rows are generated test scripts; columns are engines. Each metric is split into a build-path comparison table and a php2vala optimizer-level table. Time/throughput tables use medians over measured non-warmup runs. Memory tables use the peak value seen across measured non-warmup runs. `FAILED` means the engine/case ran but no measured run completed successfully; `COMPILE ERROR` means the build step failed before a command existed; `NO COMMAND` means no runnable command was produced and no build failure was recorded.\n\n";

    $buildPathEngines = build_path_engine_order($engineIds);
    $optimizerEngines = php2vala_optimizer_engine_order($engineIds);
    $otherEngines = other_engine_order($engineIds, $buildPathEngines, $optimizerEngines);

    foreach ($specs as $spec) {
        $md .= "## {$spec['title']}\n\n";
        $md .= "{$spec['description']}\n\n";
        if ($buildPathEngines !== []) {
            $md .= "### Build Path Comparison\n\n";
            $md .= benchmark_matrix_md($metrics, $caseIds, $buildPathEngines, $spec);
        }
        if ($optimizerEngines !== []) {
            $md .= "### php2vala Optimization Levels\n\n";
            $md .= benchmark_matrix_md($metrics, $caseIds, $optimizerEngines, $spec);
        }
        if ($otherEngines !== []) {
            $md .= "### Other Engines\n\n";
            $md .= benchmark_matrix_md($metrics, $caseIds, $otherEngines, $spec);
        }
    }

    file_put_contents($dir . '/benchmark.md', $md);
}

function write_summary(string $dir, array $rows, array $buildFailures, array $knownEngineIds): array {
    $groups = [];
    $caseIds = [];
    $engineIds = [];
    foreach ($rows as $row) {
        if (!empty($row['warmup'])) {
            continue;
        }
        if (!in_array($row['case_id'], $caseIds, true)) {
            $caseIds[] = $row['case_id'];
        }
        if (!in_array($row['engine_id'], $engineIds, true)) {
            $engineIds[] = $row['engine_id'];
        }
        $key = $row['case_id'] . "\0" . $row['engine_id'];
        $groups[$key][] = $row;
    }
    foreach ($knownEngineIds as $engineId) {
        if (!in_array($engineId, $engineIds, true)) {
            $engineIds[] = $engineId;
        }
    }
    foreach ($buildFailures as $caseId => $engines) {
        if (!in_array($caseId, $caseIds, true)) {
            $caseIds[] = $caseId;
        }
        foreach (array_keys($engines) as $engineId) {
            if (!in_array($engineId, $engineIds, true)) {
                $engineIds[] = $engineId;
            }
        }
    }

    $csv = "case,engine,runs,ok,median_wall_s,median_throughput_s,peak_rss_kb,peak_pss_kb,peak_private_clean_kb,peak_private_dirty_kb,peak_private_total_kb,peak_anonymous_kb,peak_anon_huge_pages_kb,peak_anonymous_total_kb\n";
    $md = "# Benchmark Summary\n\n";
    $md .= "The overview table uses fixed-width columns so values align vertically. Full machine-readable values remain in `results.jsonl` and `summary.csv`.\n\n";
    $widths = [
        'case' => 32,
        'engine' => 28,
        'runs' => 5,
        'ok' => 5,
        'median' => 10,
        'throughput' => 14,
        'private' => 15,
        'anon' => 12,
    ];
    $md .= markdown_fixed_row([
        'case' => 'Case',
        'engine' => 'Engine',
        'runs' => 'Runs',
        'ok' => 'OK',
        'median' => 'Median s',
        'throughput' => 'Throughput/s',
        'private' => 'Peak private KB',
        'anon' => 'Peak anon KB',
    ], $widths);
    $md .= markdown_fixed_separator($widths);
    $metrics = [];

    foreach ($groups as $items) {
        $first = $items[0];
        $ok = count(array_filter($items, static fn($r) => $r['exit_code'] === 0 && !$r['timed_out']));
        $wall = median(array_column($items, 'wall_seconds'));
        $throughput = median(array_column($items, 'throughput_per_second'));
        $rss = max(array_map(static fn($r) => (int)$r['peak']['rss_kb'], $items));
        $pss = max(array_map(static fn($r) => (int)$r['peak']['pss_kb'], $items));
        $privateClean = max(array_map(static fn($r) => (int)$r['peak']['private_clean_kb'], $items));
        $privateDirty = max(array_map(static fn($r) => (int)$r['peak']['private_dirty_kb'], $items));
        $private = max(array_map(static fn($r) => (int)$r['peak']['private_total_kb'], $items));
        $anon = max(array_map(static fn($r) => (int)$r['peak']['anonymous_kb'], $items));
        $anonHuge = max(array_map(static fn($r) => (int)$r['peak']['anon_huge_pages_kb'], $items));
        $anonTotal = max(array_map(static fn($r) => (int)$r['peak']['anonymous_total_kb'], $items));
        $md .= markdown_fixed_row([
            'case' => (string)$first['case_id'],
            'engine' => (string)$first['engine_id'],
            'runs' => (string)count($items),
            'ok' => (string)$ok,
            'median' => fmt($wall),
            'throughput' => fmt_human($throughput, 1),
            'private' => fmt_human((float)$private, 0),
            'anon' => fmt_human((float)$anonTotal, 0),
        ], $widths);
        $csv .= implode(',', [
            $first['case_id'],
            $first['engine_id'],
            count($items),
            $ok,
            fmt($wall),
            fmt($throughput, 1),
            $rss,
            $pss,
            $privateClean,
            $privateDirty,
            $private,
            $anon,
            $anonHuge,
            $anonTotal,
        ]) . "\n";
        $metrics[$first['case_id']][$first['engine_id']] = [
            'ok' => $ok,
            'wall' => $wall,
            'throughput' => $throughput,
            'rss' => $rss,
            'pss' => $pss,
            'private_clean' => $privateClean,
            'private_dirty' => $privateDirty,
            'private_total' => $private,
            'anonymous' => $anon,
            'anon_huge_pages' => $anonHuge,
            'anonymous_total' => $anonTotal,
        ];
    }
    foreach ($buildFailures as $caseId => $engines) {
        foreach ($engines as $engineId => $failure) {
            $metrics[$caseId][$engineId] = [
                'compile_error' => true,
                'reason' => build_failure_reason($failure),
            ];
        }
    }

    file_put_contents($dir . '/summary.md', $md);
    file_put_contents($dir . '/summary.csv', $csv);
    write_benchmark_matrix($dir, $metrics, $caseIds, $engineIds);
    return $metrics;
}

function optimizer_engine_parts(string $engineId): ?array {
    if (!preg_match('/^php2vala-(embed|pwphp|native|both)-opt([1-4])$/', $engineId, $m)) {
        return null;
    }
    return [
        'mode' => $m[1],
        'opt' => (int)$m[2],
    ];
}

function metric_is_usable(?array $metric): bool {
    return $metric !== null
        && empty($metric['compile_error'])
        && (int)($metric['ok'] ?? 0) > 0
        && is_numeric($metric['wall'] ?? null)
        && is_numeric($metric['private_total'] ?? null);
}

function compare_speed_or_memory(?array $candidate, ?array $baseline): array {
    if (!metric_is_usable($candidate)) {
        return [
            'ok' => false,
            'reason' => !empty($candidate['compile_error'] ?? null) ? 'candidate compile error' : 'candidate missing usable metrics',
        ];
    }
    if (!metric_is_usable($baseline)) {
        return [
            'ok' => false,
            'reason' => !empty($baseline['compile_error'] ?? null) ? 'baseline compile error' : 'baseline missing usable metrics',
        ];
    }

    $candidateWall = (float)$candidate['wall'];
    $baselineWall = (float)$baseline['wall'];
    $candidatePrivate = (int)$candidate['private_total'];
    $baselinePrivate = (int)$baseline['private_total'];
    $fasterOrEqual = $candidateWall <= $baselineWall;
    $lessOrEqualMemory = $candidatePrivate <= $baselinePrivate;

    return [
        'ok' => $fasterOrEqual || $lessOrEqualMemory,
        'candidate_wall_s' => $candidateWall,
        'baseline_wall_s' => $baselineWall,
        'candidate_private_total_kb' => $candidatePrivate,
        'baseline_private_total_kb' => $baselinePrivate,
        'wall_ratio' => $baselineWall > 0 ? $candidateWall / $baselineWall : null,
        'private_total_ratio' => $baselinePrivate > 0 ? $candidatePrivate / $baselinePrivate : null,
        'faster_or_equal' => $fasterOrEqual,
        'less_or_equal_memory' => $lessOrEqualMemory,
    ];
}

function write_optimizer_gates(string $dir, array $metrics, bool $verifyOptimizerGates): int {
    $checks = [];
    foreach ($metrics as $caseId => $engineMetrics) {
        foreach ($engineMetrics as $engineId => $metric) {
            $parts = optimizer_engine_parts((string)$engineId);
            if ($parts === null || $parts['opt'] <= 1) {
                continue;
            }
            $baselineEngine = 'php2vala-' . $parts['mode'] . '-opt1';
            $result = compare_speed_or_memory($metric, $engineMetrics[$baselineEngine] ?? null);
            $checks[] = [
                'kind' => 'optimizer_vs_opt1',
                'case_id' => (string)$caseId,
                'engine_id' => (string)$engineId,
                'baseline_engine_id' => $baselineEngine,
            ] + $result;
        }

        foreach (['embed', 'pwphp', 'native', 'both'] as $mode) {
            $opt4Engine = 'php2vala-' . $mode . '-opt4';
            if (!array_key_exists($opt4Engine, $engineMetrics)) {
                continue;
            }
            foreach (['php-cli', 'pwphpbytecode'] as $baselineEngine) {
                if (!array_key_exists($baselineEngine, $engineMetrics)) {
                    continue;
                }
                $result = compare_speed_or_memory($engineMetrics[$opt4Engine], $engineMetrics[$baselineEngine]);
                $checks[] = [
                    'kind' => 'opt4_vs_runtime_baseline',
                    'case_id' => (string)$caseId,
                    'engine_id' => $opt4Engine,
                    'baseline_engine_id' => $baselineEngine,
                ] + $result;
            }
        }
    }

    $failures = array_values(array_filter($checks, static fn($check) => empty($check['ok'])));
    file_put_contents($dir . '/optimizer-gates.json', json_encode([
        'generated_at' => date(DATE_ATOM),
        'verified' => $verifyOptimizerGates,
        'rule' => 'opt2/3/4 must be faster-or-equal or use less-or-equal private memory than matching opt1; opt4 is also compared against php-cli and pwphpbytecode when metrics exist.',
        'checks' => $checks,
        'failures' => $failures,
    ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");

    $md = "# Optimizer Gates\n\n";
    $md .= "A check passes when the candidate is faster/equal by median wall time or uses less/equal peak private memory than its baseline.\n\n";
    $widths = [
        'status' => 6,
        'kind' => 26,
        'case' => 32,
        'engine' => 28,
        'baseline' => 24,
        'wall' => 10,
        'private' => 12,
        'reason' => 32,
    ];
    $md .= markdown_fixed_row([
        'status' => 'Status',
        'kind' => 'Kind',
        'case' => 'Case',
        'engine' => 'Engine',
        'baseline' => 'Baseline',
        'wall' => 'Wall x',
        'private' => 'Private x',
        'reason' => 'Reason',
    ], $widths);
    $md .= markdown_fixed_separator($widths);
    foreach ($checks as $check) {
        $md .= markdown_fixed_row([
            'status' => !empty($check['ok']) ? 'OK' : 'FAIL',
            'kind' => (string)$check['kind'],
            'case' => (string)$check['case_id'],
            'engine' => (string)$check['engine_id'],
            'baseline' => (string)$check['baseline_engine_id'],
            'wall' => isset($check['wall_ratio']) && $check['wall_ratio'] !== null ? fmt((float)$check['wall_ratio'], 3) : 'n/a',
            'private' => isset($check['private_total_ratio']) && $check['private_total_ratio'] !== null ? fmt((float)$check['private_total_ratio'], 3) : 'n/a',
            'reason' => (string)($check['reason'] ?? ''),
        ], $widths);
    }
    file_put_contents($dir . '/optimizer-gates.md', $md);

    return count($failures);
}

function count_php_zval_occurrences(string $path): ?int {
    if (!is_file($path) || !is_readable($path)) {
        return null;
    }
    return substr_count((string)file_get_contents($path), 'Php.zval');
}

function fmt_signed_int(int $value): string {
    return ($value > 0 ? '+' : '') . (string)$value;
}

function fmt_percent(?float $value): string {
    return $value === null ? 'n/a' : number_format($value, 1, '.', '') . '%';
}

function write_optimizer_zval_report(string $dir, array $commands): void {
    $counts = [];
    $countRows = [];

    foreach ($commands as $entry) {
        $engineId = (string)($entry['engine_id'] ?? '');
        $parts = optimizer_engine_parts($engineId);
        if ($parts === null) {
            continue;
        }

        $generatedVala = (string)($entry['generated_vala'] ?? '');
        $count = $generatedVala === '' ? null : count_php_zval_occurrences($generatedVala);
        $row = [
            'case_id' => (string)$entry['case_id'],
            'mode' => (string)$parts['mode'],
            'optimizer' => (int)$parts['opt'],
            'engine_id' => $engineId,
            'generated_vala' => $generatedVala,
            'php_zval_count' => $count,
        ];
        $countRows[] = $row;
        $counts[$row['case_id']][$row['mode']][$row['optimizer']] = $row;
    }

    $comparisons = [];
    foreach ($counts as $caseId => $modes) {
        foreach ($modes as $mode => $opts) {
            for ($opt = 2; $opt <= 4; $opt++) {
                $candidate = $opts[$opt] ?? null;
                $baseline = $opts[$opt - 1] ?? null;
                $candidateCount = $candidate['php_zval_count'] ?? null;
                $baselineCount = $baseline['php_zval_count'] ?? null;
                $delta = is_int($candidateCount) && is_int($baselineCount) ? $candidateCount - $baselineCount : null;
                $reduction = $delta !== null ? -$delta : null;
                $reductionPercent = is_int($baselineCount) && $baselineCount > 0 && $reduction !== null
                    ? ($reduction / $baselineCount) * 100.0
                    : null;
                $comparisons[] = [
                    'case_id' => (string)$caseId,
                    'mode' => (string)$mode,
                    'candidate_optimizer' => $opt,
                    'baseline_optimizer' => $opt - 1,
                    'candidate_engine_id' => (string)($candidate['engine_id'] ?? ('php2vala-' . $mode . '-opt' . $opt)),
                    'baseline_engine_id' => (string)($baseline['engine_id'] ?? ('php2vala-' . $mode . '-opt' . ($opt - 1))),
                    'candidate_php_zval_count' => $candidateCount,
                    'baseline_php_zval_count' => $baselineCount,
                    'delta' => $delta,
                    'reduction' => $reduction,
                    'reduction_percent' => $reductionPercent,
                    'best_possible' => 0,
                    'note' => $delta === null ? 'missing generated Vala for candidate or baseline' : '',
                ];
            }
        }
    }

    usort($countRows, static fn($a, $b) => [$a['case_id'], $a['mode'], $a['optimizer']] <=> [$b['case_id'], $b['mode'], $b['optimizer']]);
    usort($comparisons, static fn($a, $b) => [$a['case_id'], $a['mode'], $a['candidate_optimizer']] <=> [$b['case_id'], $b['mode'], $b['candidate_optimizer']]);

    $matrixModes = ['both', 'embed', 'native'];
    $matrixOptimizers = [1, 2, 3, 4];
    $matrixRows = [];
    $matrixWidths = ['case' => 32];
    foreach ($matrixModes as $mode) {
        foreach ($matrixOptimizers as $opt) {
            $matrixWidths[$mode . '_opt' . $opt] = 8;
        }
    }

    $caseIds = array_keys($counts);
    sort($caseIds, SORT_NATURAL);
    foreach ($caseIds as $caseId) {
        $matrixRow = ['case' => (string)$caseId];
        foreach ($matrixModes as $mode) {
            foreach ($matrixOptimizers as $opt) {
                $key = $mode . '_opt' . $opt;
                $count = $counts[$caseId][$mode][$opt]['php_zval_count'] ?? null;
                $matrixRow[$key] = $count === null ? 'n/a' : (string)$count;
            }
        }
        $matrixRows[] = $matrixRow;
    }

    $matrixMd = "# php2vala Php.zval Optimizer Matrix\n\n";
    $matrixMd .= "Rows are correctness cases; columns are php2vala runtime mode and optimizer level. Cells contain literal `Php.zval` occurrences in the generated intermediate `.vala` file. Lower is better; `0` is the full-optimize target.\n\n";
    $matrixMd .= markdown_fixed_row([
        'case' => 'Case',
        'both_opt1' => 'both o1',
        'both_opt2' => 'both o2',
        'both_opt3' => 'both o3',
        'both_opt4' => 'both o4',
        'embed_opt1' => 'embed o1',
        'embed_opt2' => 'embed o2',
        'embed_opt3' => 'embed o3',
        'embed_opt4' => 'embed o4',
        'native_opt1' => 'native o1',
        'native_opt2' => 'native o2',
        'native_opt3' => 'native o3',
        'native_opt4' => 'native o4',
    ], $matrixWidths);
    $matrixMd .= markdown_fixed_separator($matrixWidths);
    foreach ($matrixRows as $row) {
        $matrixMd .= markdown_fixed_row($row, $matrixWidths);
    }
    $matrixMd .= "\n";

    file_put_contents($dir . '/optimizer-zval-report.json', json_encode([
        'generated_at' => date(DATE_ATOM),
        'description' => 'Counts literal Php.zval occurrences in generated php2vala Vala files. Lower is better; zero is the best possible value.',
        'counts' => $countRows,
        'matrix' => $matrixRows,
        'comparisons' => $comparisons,
    ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");

    $md = "# php2vala Php.zval Optimizer Report\n\n";
    $md .= "Counts literal Php.zval occurrences in the generated intermediate .vala files. Lower is better; 0 is the best possible value. Comparison rows show opt2 vs opt1, opt3 vs opt2, and opt4 vs opt3 for each case/runtime mode. In correctness reports, php2vala embed, native, and both opt4 engines are marked FAILED FULL OPTIMIZE when this count is not 0.\n\n";
    $md .= $matrixMd;

    $widths = [
        'case' => 32,
        'mode' => 6,
        'comparison' => 12,
        'baseline' => 13,
        'candidate' => 13,
        'delta' => 8,
        'reduction' => 11,
        'note' => 42,
    ];
    $md .= "## Comparisons\n\n";
    $md .= markdown_fixed_row([
        'case' => 'Case',
        'mode' => 'Mode',
        'comparison' => 'Comparison',
        'baseline' => 'Baseline',
        'candidate' => 'Candidate',
        'delta' => 'Delta',
        'reduction' => 'Reduction',
        'note' => 'Note',
    ], $widths);
    $md .= markdown_fixed_separator($widths);
    foreach ($comparisons as $row) {
        $delta = $row['delta'];
        $reduction = $row['reduction'];
        $md .= markdown_fixed_row([
            'case' => (string)$row['case_id'],
            'mode' => (string)$row['mode'],
            'comparison' => 'opt' . $row['candidate_optimizer'] . ' vs opt' . $row['baseline_optimizer'],
            'baseline' => $row['baseline_php_zval_count'] === null ? 'n/a' : (string)$row['baseline_php_zval_count'],
            'candidate' => $row['candidate_php_zval_count'] === null ? 'n/a' : (string)$row['candidate_php_zval_count'],
            'delta' => $delta === null ? 'n/a' : fmt_signed_int((int)$delta),
            'reduction' => $reduction === null ? 'n/a' : fmt_signed_int((int)$reduction) . ' (' . fmt_percent($row['reduction_percent']) . ')',
            'note' => (string)$row['note'],
        ], $widths);
    }

    $md .= "\n## Raw Counts\n\n";
    $countWidths = [
        'case' => 32,
        'engine' => 28,
        'mode' => 6,
        'opt' => 3,
        'count' => 9,
        'file' => 48,
    ];
    $md .= markdown_fixed_row([
        'case' => 'Case',
        'engine' => 'Engine',
        'mode' => 'Mode',
        'opt' => 'Opt',
        'count' => 'Php.zval',
        'file' => 'Generated Vala',
    ], $countWidths);
    $md .= markdown_fixed_separator($countWidths);
    foreach ($countRows as $row) {
        $md .= markdown_fixed_row([
            'case' => (string)$row['case_id'],
            'engine' => (string)$row['engine_id'],
            'mode' => (string)$row['mode'],
            'opt' => (string)$row['optimizer'],
            'count' => $row['php_zval_count'] === null ? 'n/a' : (string)$row['php_zval_count'],
            'file' => (string)$row['generated_vala'],
        ], $countWidths);
    }

    file_put_contents($dir . '/optimizer-zval-report.md', $md);
    file_put_contents($dir . '/optimizer-zval-matrix.md', $matrixMd);
    file_put_contents($dir . '/benchmark.md', "\n" . $md, FILE_APPEND);
}

function printable_snippet(string $value): string {
    if (strlen($value) > 1200) {
        $value = substr($value, 0, 1200) . "\n...<truncated>";
    }
    return str_replace(["\r", "\0"], ['\\r', '\\0'], $value);
}

function first_reference_commands(array $commands, string $referenceEngine): array {
    $refs = [];
    foreach ($commands as $entry) {
        if (($entry['engine_id'] ?? null) !== $referenceEngine) {
            continue;
        }
        $caseId = (string)$entry['case_id'];
        if (!isset($refs[$caseId])) {
            $refs[$caseId] = $entry;
        }
    }
    return $refs;
}

function diagnostic_messages(string $stdout, string $stderr): array {
    $patterns = [
        '/\bPHP\s+(Fatal error|Parse error|Warning|Notice|Deprecated|Strict Standards|Recoverable fatal error):/i',
        '/\b(Fatal error|Parse error|Warning|Notice|Deprecated|Strict Standards|Recoverable fatal error):/i',
        '/\bUncaught\b/i',
        '/^Stack trace:/i',
        '/\b(Segmentation fault|Aborted|Trace\/breakpoint trap|core dumped)\b/i',
    ];
    $messages = [];
    foreach (['stdout' => $stdout, 'stderr' => $stderr] as $stream => $text) {
        $lines = preg_split('/\R/', $text);
        if ($lines === false) {
            continue;
        }
        foreach ($lines as $line) {
            $line = trim($line);
            if ($line === '') {
                continue;
            }
            foreach ($patterns as $pattern) {
                if (preg_match($pattern, $line)) {
                    $messages[] = $stream . ': ' . substr($line, 0, 500);
                    break;
                }
            }
        }
    }
    return array_values(array_unique($messages));
}

function correctness_compare(array $row, array $reference): array {
    $ok = true;
    $reasons = [];
    $referenceDiagnostics = diagnostic_messages((string)$reference['stdout'], (string)$reference['stderr']);
    $actualDiagnostics = diagnostic_messages((string)$row['stdout'], (string)$row['stderr']);
    if (!empty($reference['timed_out'])) {
        $ok = false;
        $reasons[] = 'reference timed out';
    }
    if (!empty($row['timed_out'])) {
        $ok = false;
        $reasons[] = 'timed out';
    }
    if (($reference['iterations'] ?? null) === null || ($reference['checksum'] ?? null) === null) {
        $ok = false;
        $reasons[] = 'reference RESULT missing';
    }
    if (($row['iterations'] ?? null) === null || ($row['checksum'] ?? null) === null) {
        $ok = false;
        $reasons[] = 'RESULT missing';
    }
    if ($referenceDiagnostics !== []) {
        $ok = false;
        $reasons[] = 'reference diagnostics present';
    }
    if ($actualDiagnostics !== []) {
        $ok = false;
        $reasons[] = 'engine diagnostics present';
    }
    if ((int)$row['exit_code'] !== (int)$reference['exit_code']) {
        $ok = false;
        $reasons[] = 'exit code differs';
    }
    if (($row['iterations'] ?? null) !== ($reference['iterations'] ?? null)) {
        $ok = false;
        $reasons[] = 'RESULT iterations differ';
    }
    if (($row['checksum'] ?? null) !== ($reference['checksum'] ?? null)) {
        $ok = false;
        $reasons[] = 'RESULT checksum differs';
    }
    if ((string)$row['stderr'] !== (string)$reference['stderr']) {
        $ok = false;
        $reasons[] = 'stderr differs';
    }
    return [
        'ok' => $ok,
        'case_id' => $row['case_id'],
        'engine_id' => $row['engine_id'],
        'run_index' => $row['run_index'] ?? null,
        'reasons' => $reasons,
        'expected_exit_code' => $reference['exit_code'],
        'actual_exit_code' => $row['exit_code'],
        'expected_iterations' => $reference['iterations'],
        'actual_iterations' => $row['iterations'],
        'expected_checksum' => $reference['checksum'],
        'actual_checksum' => $row['checksum'],
        'expected_stdout_sha256' => $reference['stdout_sha256'],
        'actual_stdout_sha256' => $row['stdout_sha256'],
        'expected_stderr_sha256' => $reference['stderr_sha256'],
        'actual_stderr_sha256' => $row['stderr_sha256'],
        'expected_stdout' => $reference['stdout'],
        'actual_stdout' => $row['stdout'],
        'expected_stderr' => $reference['stderr'],
        'actual_stderr' => $row['stderr'],
        'reference_diagnostics' => $referenceDiagnostics,
        'actual_diagnostics' => $actualDiagnostics,
    ];
}

function markdown_fixed_cell(string $value, int $width): string {
    $value = str_replace(["\r", "\n", '|'], ['\\r', ' ', '\\|'], $value);
    if (strlen($value) > $width) {
        $value = substr($value, 0, max(0, $width - 3)) . '...';
    }
    return str_pad($value, $width, ' ', STR_PAD_RIGHT);
}

function markdown_fixed_row(array $values, array $widths): string {
    $cells = [];
    foreach ($widths as $key => $width) {
        $cells[] = markdown_fixed_cell((string)($values[$key] ?? ''), $width);
    }
    return '| ' . implode(' | ', $cells) . " |\n";
}

function markdown_fixed_separator(array $widths): string {
    $cells = [];
    foreach ($widths as $width) {
        $cells[] = str_repeat('-', $width);
    }
    return '| ' . implode(' | ', $cells) . " |\n";
}

function short_status(string $value, int $width = 56): string {
    $value = trim($value);
    if ($value === '') {
        return 'OK';
    }
    if (strlen($value) > $width) {
        return substr($value, 0, max(0, $width - 3)) . '...';
    }
    return $value;
}

function compile_error_check(array $failure): array {
    return [
        'ok' => false,
        'type' => 'compile_error',
        'case_id' => (string)$failure['case_id'],
        'engine_id' => (string)$failure['engine_id'],
        'run_index' => null,
        'stage' => (string)($failure['stage'] ?? 'build'),
        'failure' => $failure['failure'] ?? null,
        'reasons' => [build_failure_reason($failure)],
    ];
}
function php2vala_opt4_zval_check(array $entry): ?array {
    $engineId = (string)($entry['engine_id'] ?? '');
    $parts = optimizer_engine_parts($engineId);
    if ($parts === null || (int)$parts['opt'] !== 4 || !in_array((string)$parts['mode'], ['embed', 'native', 'both'], true)) {
        return null;
    }

    $generatedVala = (string)($entry['generated_vala'] ?? '');
    $count = $generatedVala === '' ? null : count_php_zval_occurrences($generatedVala);
    $ok = $count === 0;
    $reason = $ok ? 'FULL OPTIMIZE OK' : 'FAILED FULL OPTIMIZE';

    return [
        'ok' => $ok,
        'type' => 'optimizer_zval_gate',
        'case_id' => (string)$entry['case_id'],
        'engine_id' => $engineId,
        'run_index' => null,
        'generated_vala' => $generatedVala,
        'php_zval_count' => $count,
        'reasons' => [$reason],
    ];
}


function php2vala_pwphp_bridge_check(array $entry): ?array {
    $engineId = (string)($entry['engine_id'] ?? '');
    if (!str_starts_with($engineId, 'php2vala-pwphp-')) {
        return null;
    }

    $caseId = (string)($entry['case_id'] ?? '');
    $generatedVala = (string)($entry['generated_vala'] ?? '');
    $reasons = [];
    if ($generatedVala === '' || !is_file($generatedVala)) {
        $reasons[] = 'vbin-pwphp generated .vala missing';
    } else {
        $vala = (string)file_get_contents($generatedVala);
        foreach (['PhpPwBytecode.begin_load_class', 'php_load_class_', 'PhpPwBytecode.call_static_method_zval_params'] as $needle) {
            if (!str_contains($vala, $needle)) {
                $reasons[] = "vbin-pwphp generated .vala missing {$needle}";
            }
        }
    }

    $wrapper = (string)($entry['command'][0] ?? '');
    $wrapperText = is_file($wrapper) ? (string)file_get_contents($wrapper) : '';
    $binary = '';
    $ldLibraryPath = '';
    if ($wrapperText === '') {
        $reasons[] = 'vbin-pwphp wrapper missing';
    } else {
        if (preg_match('/^export LD_LIBRARY_PATH=\'([^\']+)\'/m', $wrapperText, $m)) {
            $ldLibraryPath = $m[1];
        }
        if (preg_match('/^exec \'([^\']+)\'/m', $wrapperText, $m)) {
            $binary = $m[1];
        }
    }
    if ($binary === '' || !is_file($binary)) {
        $reasons[] = 'vbin-pwphp executable missing';
    } else {
        $cmd = 'LD_LIBRARY_PATH=' . escapeshellarg($ldLibraryPath) . ' ldd ' . escapeshellarg($binary) . ' 2>&1';
        $ldd = shell_exec($cmd) ?? '';
        if (!str_contains($ldd, 'libpwembedbytecodelibcase')) {
            $reasons[] = 'vbin-pwphp executable does not depend on pwphpbytecode test .so';
        }
        if (preg_match('/libpwembedbytecodelibcase[^\n]*not found/', $ldd)) {
            $reasons[] = 'vbin-pwphp pwphpbytecode test .so not resolvable via wrapper LD_LIBRARY_PATH';
        }
    }

    return [
        'ok' => $reasons === [],
        'type' => 'php2vala_pwphp_bridge_gate',
        'case_id' => $caseId,
        'engine_id' => $engineId,
        'run_index' => null,
        'generated_vala' => $generatedVala,
        'reasons' => $reasons === [] ? ['vbin-pwphp bridge link OK'] : $reasons,
    ];
}

function write_correctness_matrix(string $dir, array $checks, array $references, string $referenceEngine, array $knownEngineIds, array $buildSkips = []): void {
    $caseIds = array_values(array_unique(array_merge(
        array_keys($references),
        array_map(static fn($check) => (string)$check['case_id'], $checks)
    )));
    sort($caseIds, SORT_NATURAL);

    $engineIds = [$referenceEngine];
    foreach ($knownEngineIds as $engineId) {
        if (!in_array($engineId, $engineIds, true)) {
            $engineIds[] = $engineId;
        }
    }
    foreach ($checks as $check) {
        $engineId = (string)$check['engine_id'];
        if (!in_array($engineId, $engineIds, true)) {
            $engineIds[] = $engineId;
        }
    }

    $status = [];
    foreach ($references as $caseId => $reference) {
        $diagnostics = diagnostic_messages((string)$reference['stdout'], (string)$reference['stderr']);
        $reasons = [];
        if (!empty($reference['timed_out'])) {
            $reasons[] = 'reference timed out';
        }
        if (($reference['iterations'] ?? null) === null || ($reference['checksum'] ?? null) === null) {
            $reasons[] = 'reference RESULT missing';
        }
        if ($diagnostics !== []) {
            $reasons[] = 'reference diagnostics present';
        }
        if ((int)($reference['exit_code'] ?? 0) !== 0) {
            $reasons[] = 'reference exit ' . (string)$reference['exit_code'];
        }
        $status[(string)$caseId][$referenceEngine] = $reasons === [] ? 'OK' : implode('; ', $reasons);
    }
    foreach ($buildSkips as $caseId => $engines) {
        foreach ($engines as $engineId => $skip) {
            $status[(string)$caseId][(string)$engineId] = 'SKIP';
        }
    }
    foreach ($checks as $check) {
        $caseId = (string)$check['case_id'];
        $engineId = (string)$check['engine_id'];
        if (!empty($check['ok'])) {
            if (!isset($status[$caseId][$engineId])) {
                $status[$caseId][$engineId] = 'OK';
            }
            continue;
        }
        $status[$caseId][$engineId] = implode('; ', $check['reasons'] ?? ['failed']);
    }

    $md = "# Correctness Matrix\n\n";
    $md .= "Rows are generated test scripts; columns are engines. The first table compares build paths; the second table compares php2vala optimizer levels per runtime. Cells contain OK, SKIP, COMPILE ERROR, NO COMMAND, FAILED FULL OPTIMIZE, or the shortened failure reason. For php2vala embed, native, and both opt4 engines, OK also requires the generated .vala file to contain no literal Php.zval. Full failure details remain in correctness.md and correctness.json.\n\n";

    $buildPathEngines = build_path_engine_order($engineIds);
    $optimizerEngines = php2vala_optimizer_engine_order($engineIds);
    $otherEngines = other_engine_order($engineIds, $buildPathEngines, $optimizerEngines);
    foreach ([
        'Build Path Comparison' => $buildPathEngines,
        'php2vala Optimization Levels' => $optimizerEngines,
        'Other Engines' => $otherEngines,
    ] as $title => $groupEngineIds) {
        if ($groupEngineIds === []) {
            continue;
        }
        $widths = [max(11, strlen('Test script'))];
        foreach ($groupEngineIds as $i => $engineId) {
            $widths[$i + 1] = max(strlen($engineId), 7);
        }

        $rows = [];
        foreach ($caseIds as $caseId) {
            $row = [$caseId];
            $widths[0] = max($widths[0], strlen($caseId));
            foreach ($groupEngineIds as $i => $engineId) {
                $value = short_status($status[$caseId][$engineId] ?? 'NO COMMAND');
                $row[] = $value;
                $widths[$i + 1] = max($widths[$i + 1], strlen($value));
            }
            $rows[] = $row;
        }

        $md .= "## {$title}\n\n";
        $md .= padded_md_row(array_merge(['Test script'], $groupEngineIds), $widths);
        $md .= padded_md_separator($widths);
        foreach ($rows as $row) {
            $md .= padded_md_row($row, $widths);
        }
        $md .= "\n";
    }
    file_put_contents($dir . '/correctness-matrix.md', $md);
}

function write_correctness(string $dir, array $checks, array $references, string $referenceEngine, array $knownEngineIds, array $buildSkips = []): int {
    $failed = array_values(array_filter($checks, static fn($check) => empty($check['ok'])));
    $json = [
        'generated_at' => date(DATE_ATOM),
        'reference_engine' => $referenceEngine,
        'reference_cases' => array_keys($references),
        'checks' => $checks,
        'failures' => $failed,
    ];
    file_put_contents($dir . '/correctness.json', json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");

    $md = "# Correctness Report\n\n";
    $md .= "Reference engine: `{$referenceEngine}`\n\n";
    $md .= "The overview table uses fixed-width columns. Long values are shortened here; full details remain in `correctness.json` and in the failure sections below.\n\n";
    $widths = [
        'case' => 32,
        'engine' => 24,
        'run' => 5,
        'ok' => 3,
        'reason' => 80,
    ];
    $md .= markdown_fixed_row([
        'case' => 'Case',
        'engine' => 'Engine',
        'run' => 'Run',
        'ok' => 'OK',
        'reason' => 'Reason',
    ], $widths);
    $md .= markdown_fixed_separator($widths);
    foreach ($checks as $check) {
        $md .= markdown_fixed_row([
            'case' => (string)$check['case_id'],
            'engine' => (string)$check['engine_id'],
            'run' => $check['run_index'] === null ? '' : (string)$check['run_index'],
            'ok' => !empty($check['ok']) ? 'yes' : 'NO',
            'reason' => implode('; ', $check['reasons'] ?? []),
        ], $widths);
    }
    if ($failed) {
        $md .= "\n## Failures\n\n";
        foreach ($failed as $failure) {
            $md .= "### {$failure['case_id']} / {$failure['engine_id']}\n\n";
            $md .= "Reasons: " . implode('; ', $failure['reasons']) . "\n\n";
            if (($failure['type'] ?? '') === 'optimizer_zval_gate') {
                $bt = chr(96);
                $md .= "Generated Vala: " . $bt . (string)($failure['generated_vala'] ?? '') . $bt . "\n\n";
                $md .= "Php.zval count: " . $bt . (string)($failure['php_zval_count'] ?? 'n/a') . $bt . "\n\n";
                continue;
            }
            if (($failure['type'] ?? '') === 'compile_error') {
                $md .= "Stage: `" . (string)($failure['stage'] ?? 'build') . "`\n\n";
                $details = $failure['failure'] ?? null;
                if (is_array($details)) {
                    if (array_key_exists('command', $details)) {
                        $md .= "Command:\n\n```text\n" . printable_snippet(json_encode($details['command'], JSON_UNESCAPED_SLASHES) ?: '') . "\n```\n\n";
                    }
                    if ((string)($details['stdout_tail'] ?? '') !== '') {
                        $md .= "Build stdout tail:\n\n```text\n" . printable_snippet((string)$details['stdout_tail']) . "\n```\n\n";
                    }
                    if ((string)($details['stderr_tail'] ?? '') !== '') {
                        $md .= "Build stderr tail:\n\n```text\n" . printable_snippet((string)$details['stderr_tail']) . "\n```\n\n";
                    }
                }
                continue;
            }
            $md .= "Expected exit: `{$failure['expected_exit_code']}`, actual exit: `{$failure['actual_exit_code']}`\n\n";
            $md .= "Expected RESULT: iterations=`" . ($failure['expected_iterations'] ?? 'n/a') . "`, checksum=`" . ($failure['expected_checksum'] ?? 'n/a') . "`\n\n";
            $md .= "Actual RESULT: iterations=`" . ($failure['actual_iterations'] ?? 'n/a') . "`, checksum=`" . ($failure['actual_checksum'] ?? 'n/a') . "`\n\n";
            $md .= "Expected stdout:\n\n```text\n" . printable_snippet((string)$failure['expected_stdout']) . "\n```\n\n";
            $md .= "Actual stdout:\n\n```text\n" . printable_snippet((string)$failure['actual_stdout']) . "\n```\n\n";
            if (!empty($failure['reference_diagnostics'])) {
                $md .= "Reference diagnostics:\n\n```text\n" . printable_snippet(implode("\n", $failure['reference_diagnostics'])) . "\n```\n\n";
            }
            if (!empty($failure['actual_diagnostics'])) {
                $md .= "Actual diagnostics:\n\n```text\n" . printable_snippet(implode("\n", $failure['actual_diagnostics'])) . "\n```\n\n";
            }
            if ((string)($failure['expected_stderr'] ?? '') !== '') {
                $md .= "Expected stderr:\n\n```text\n" . printable_snippet((string)$failure['expected_stderr']) . "\n```\n\n";
            }
            if ((string)$failure['actual_stderr'] !== '') {
                $md .= "Actual stderr:\n\n```text\n" . printable_snippet((string)$failure['actual_stderr']) . "\n```\n\n";
            }
        }
    }
    file_put_contents($dir . '/correctness.md', $md);
    write_correctness_matrix($dir, $checks, $references, $referenceEngine, $knownEngineIds, $buildSkips);
    return count($failed);
}

$root = dirname(__DIR__);
$casesDir = arg_value($argv, '--cases-dir', $root . '/generated/cases');
$commandsFile = arg_value($argv, '--engine-commands', $root . '/build/engine-build/engine-commands.json');
$buildReportFile = arg_value($argv, '--build-report', $root . '/build/engine-build/build-report.json');
$resultsRoot = arg_value($argv, '--results-dir', $root . '/results');
$repeats = max(1, (int)arg_value($argv, '--repeats', '5'));
$warmups = max(0, (int)arg_value($argv, '--warmups', '1'));
$timeout = max(1, (int)arg_value($argv, '--timeout-seconds', '900'));
$stamp = arg_value($argv, '--touch-stamp');
$filterEngines = arg_list($argv, '--engines');
$filterCases = arg_list($argv, '--cases');
$verifyCorrectness = arg_bool($argv, '--verify-correctness', true);
$allowCorrectnessFailures = arg_bool($argv, '--allow-correctness-failures', false);
$verifyOptimizerGates = arg_bool($argv, '--verify-optimizer-gates', false);
$allowOptimizerGateFailures = arg_bool($argv, '--allow-optimizer-gate-failures', false);
$referenceEngine = arg_value($argv, '--reference-engine', 'php-cli');

if (!is_file($commandsFile)) {
    $fallback = [];
    foreach (glob($casesDir . '/*.php') ?: [] as $case) {
        $fallback[] = [
            'engine_id' => 'php-cli',
            'engine_label' => 'Zend PHP CLI',
            'case_id' => basename($case, '.php'),
            'case' => $case,
            'command' => ['php', '-d', 'memory_limit=-1', '-d', 'opcache.enable_cli=0', $case],
        ];
    }
    $allCommands = $fallback;
} else {
    $allCommands = json_decode((string)file_get_contents($commandsFile), true, flags: JSON_THROW_ON_ERROR);
}

$commands = array_values(array_filter($allCommands, static function (array $entry) use ($filterEngines, $filterCases): bool {
    if ($filterEngines !== null && !in_array($entry['engine_id'], $filterEngines, true)) {
        return false;
    }
    if ($filterCases !== null && !in_array($entry['case_id'], $filterCases, true)) {
        return false;
    }
    return true;
}));
$referenceCommands = array_values(array_filter($allCommands, static function (array $entry) use ($filterCases, $referenceEngine): bool {
    if (($entry['engine_id'] ?? null) !== $referenceEngine) {
        return false;
    }
    if ($filterCases !== null && !in_array($entry['case_id'], $filterCases, true)) {
        return false;
    }
    return true;
}));
$neededReferenceCases = [];
foreach ($commands as $entry) {
    if (($entry['engine_id'] ?? null) === $referenceEngine) {
        continue;
    }
    $neededReferenceCases[(string)$entry['case_id']] = true;
}
$referenceCommands = array_values(array_filter($referenceCommands, static fn(array $entry): bool => isset($neededReferenceCases[(string)$entry['case_id']])));
$buildInfo = load_build_failures($buildReportFile, $filterEngines, $filterCases);
$buildFailures = $buildInfo['failures'];
$buildSkips = $buildInfo['skips'];
$knownEngineIds = $buildInfo['engine_ids'];
foreach ($commands as $entry) {
    $engineId = (string)$entry['engine_id'];
    if (!in_array($engineId, $knownEngineIds, true)) {
        $knownEngineIds[] = $engineId;
    }
}

$timestamp = date('Ymd-His');
$resultDir = $resultsRoot . '/' . $timestamp;
mkdirp($resultDir);
$jsonl = fopen($resultDir . '/results.jsonl', 'wb');
if (!$jsonl) {
    throw new RuntimeException("Cannot open results.jsonl");
}

$totalCommands = count($commands);
$totalMeasuredRuns = $totalCommands * ($warmups + $repeats);
fwrite(STDERR, sprintf(
    "Benchmark target: %d commands x %d run(s) (%d warmup + %d measured) = %d process runs; results -> %s\n",
    $totalCommands,
    $warmups + $repeats,
    $warmups,
    $repeats,
    $totalMeasuredRuns,
    $resultDir
));

$rows = [];
$correctnessChecks = [];
$referenceRows = [];
if ($verifyCorrectness) {
    foreach ($buildFailures as $engines) {
        foreach ($engines as $failure) {
            $correctnessChecks[] = compile_error_check($failure);
        }
    }
    foreach ($commands as $entry) {
        $zvalCheck = php2vala_opt4_zval_check($entry);
        if ($zvalCheck !== null) {
            $correctnessChecks[] = $zvalCheck;
        }
        $bridgeCheck = php2vala_pwphp_bridge_check($entry);
        if ($bridgeCheck !== null) {
            $correctnessChecks[] = $bridgeCheck;
        }
    }
    foreach (first_reference_commands($referenceCommands, $referenceEngine) as $caseId => $entry) {
        fwrite(STDERR, sprintf(
            "[%s/%s] %s correctness reference\n",
            $entry['engine_id'],
            $entry['case_id'],
            date('H:i:s')
        ));
        $referenceRows[$caseId] = $entry + run_measured($entry['command'], $timeout) + [
            'run_index' => null,
            'warmup' => false,
            'measured_at' => date(DATE_ATOM),
        ];
    }
}
$commandIndex = 0;
foreach ($commands as $entry) {
    $commandIndex++;
    fwrite(STDERR, sprintf(
        "[benchmark command %d/%d] %s/%s\n",
        $commandIndex,
        $totalCommands,
        $entry['engine_id'],
        $entry['case_id']
    ));
    $totalRuns = $warmups + $repeats;
    for ($run = 0; $run < $totalRuns; $run++) {
        $warmup = $run < $warmups;
        fwrite(STDERR, sprintf(
            "[%s/%s] %s %s run %d/%d%s\n",
            $entry['engine_id'],
            $entry['case_id'],
            date('H:i:s'),
            basename((string)$entry['case']),
            $run + 1,
            $totalRuns,
            $warmup ? ' warmup' : ''
        ));
        $measured = run_measured($entry['command'], $timeout);
        $row = $entry + $measured + [
            'run_index' => $run,
            'warmup' => $warmup,
            'measured_at' => date(DATE_ATOM),
        ];
        fwrite($jsonl, json_encode($row, JSON_UNESCAPED_SLASHES) . "\n");
        fflush($jsonl);
        $rows[] = $row;
        if ($verifyCorrectness && !$warmup && $entry['engine_id'] !== $referenceEngine) {
            if (isset($referenceRows[$entry['case_id']])) {
                $correctnessChecks[] = correctness_compare($row, $referenceRows[$entry['case_id']]);
            } else {
                $correctnessChecks[] = [
                    'ok' => false,
                    'case_id' => $entry['case_id'],
                    'engine_id' => $entry['engine_id'],
                    'run_index' => $run,
                    'reasons' => ['missing reference run'],
                ];
            }
        }
    }
}
fclose($jsonl);

$metrics = write_summary($resultDir, $rows, $buildFailures, $knownEngineIds);
write_optimizer_zval_report($resultDir, $commands);
$optimizerGateFailures = write_optimizer_gates($resultDir, $metrics, $verifyOptimizerGates);
$correctnessFailures = 0;
if ($verifyCorrectness) {
    $correctnessFailures = write_correctness($resultDir, $correctnessChecks, $referenceRows, $referenceEngine, $knownEngineIds, $buildSkips);
}
if ($stamp !== null) {
    file_put_contents($stamp, "ran " . date(DATE_ATOM) . "\n");
}

fwrite(STDOUT, "Results: {$resultDir}\n");
fwrite(STDOUT, file_get_contents($resultDir . '/summary.md'));
if ($verifyCorrectness) {
    fwrite(STDOUT, "\n");
    fwrite(STDOUT, file_get_contents($resultDir . '/correctness.md'));
    if ($correctnessFailures > 0 && !$allowCorrectnessFailures) {
        fwrite(STDERR, "Correctness failures: {$correctnessFailures}\n");
        exit(1);
    }
}
if ($verifyOptimizerGates) {
    fwrite(STDOUT, "\n");
    fwrite(STDOUT, file_get_contents($resultDir . '/optimizer-gates.md'));
    if ($optimizerGateFailures > 0 && !$allowOptimizerGateFailures) {
        fwrite(STDERR, "Optimizer gate failures: {$optimizerGateFailures}\n");
        exit(1);
    }
}
