#!/usr/bin/env python3
"""Independent hostile audit for two FPRD result-review units.

This script checks the binary-alphabet boundary in the relative-degree lemma,
the universal folded-carry and mirrored-digit statements on a broad finite
domain, and the complete decimal one-step-palindrome census through 10^6.
It makes no assertion about the infinite reverse-and-add orbit of 196.
"""

from __future__ import annotations

import argparse
import json
from pathlib import Path


def row_matrix(row: tuple[int, ...], matrix: tuple[tuple[int, ...], ...]) -> tuple[int, ...]:
    return tuple(sum(row[k] * matrix[k][j] for k in range(len(row))) for j in range(len(row)))


def row_column(row: tuple[int, ...], column: tuple[int, ...]) -> int:
    return sum(a * b for a, b in zip(row, column))


def relative_degree_hostile_example() -> dict[str, object]:
    # The public two-letter criterion tests only B.  Over {a,b,c}, this
    # representation passes that test at r=1 but has f(c)=1, so the support is
    # not contained in a Sigma*.  This demonstrates why the alphabet
    # hypothesis cannot be dropped without adding a C-annihilation condition.
    lam = (1, 0)
    gamma = (0, 1)
    a = ((0, 1), (0, 0))
    b = ((0, 0), (0, 1))
    c = ((0, 1), (0, 0))
    safe_stop = row_column(lam, gamma)
    safe_b = row_matrix(lam, b)
    witness = row_column(row_matrix(row_matrix(lam, a), b), gamma)
    c_value = row_column(row_matrix(lam, c), gamma)
    assert safe_stop == 0 and safe_b == (0, 0)
    assert witness == 1 and c_value == 1
    return {
        "dimension": 2,
        "candidate_degree": 1,
        "lambda_gamma": safe_stop,
        "lambda_B": list(safe_b),
        "f_ab": witness,
        "f_c": c_value,
        "conclusion": "the B-only safe-row test passes, but c lies outside a Sigma* and has nonzero coefficient",
    }


def digits_lsd(n: int, base: int) -> list[int]:
    if n == 0:
        return [0]
    out: list[int] = []
    while n:
        out.append(n % base)
        n //= base
    return out


def from_digits_lsd(digits: list[int], base: int) -> int:
    value = 0
    for digit in reversed(digits):
        value = value * base + digit
    return value


def reverse_value(n: int, base: int) -> int:
    return from_digits_lsd(list(reversed(digits_lsd(n, base))), base)


def folded_sweep(n: int, base: int) -> tuple[int, list[int], list[int]]:
    digits = digits_lsd(n, base)
    sums = [digits[i] + digits[-1 - i] for i in range(len(digits))]
    carry = 0
    output: list[int] = []
    carries = [0]
    for total in sums:
        value = total + carry
        output.append(value % base)
        carry = value // base
        carries.append(carry)
    if carry:
        output.append(carry)
    return from_digits_lsd(output, base), sums, carries


def is_palindrome(n: int, base: int) -> bool:
    digits = digits_lsd(n, base)
    return digits == list(reversed(digits))


def reverse_add_checks() -> dict[str, object]:
    folded_cases = 0
    non_growth_pairs = 0
    for base in range(2, 17):
        for n in range(0, 200_001):
            swept, _sums, carries = folded_sweep(n, base)
            expected = n + reverse_value(n, base)
            assert swept == expected
            folded_cases += 1
            source_length = len(digits_lsd(n, base))
            output = digits_lsd(expected, base)
            if len(output) == source_length:
                for i, digit in enumerate(output):
                    partner = output[-1 - i]
                    assert (digit - partner) % base in (0, 1, base - 1)
                    non_growth_pairs += 1
            assert set(carries) <= {0, 1}

    carry_free = 0
    saturated = 0
    uncovered: list[int] = []
    one_step_palindromes = 0
    for n in range(1, 1_000_001):
        output, sums, _carries = folded_sweep(n, 10)
        if not is_palindrome(output, 10):
            continue
        one_step_palindromes += 1
        if all(total <= 9 for total in sums):
            carry_free += 1
        elif sums[0] == 11 and all(total in (0, 11) for total in sums):
            saturated += 1
        else:
            uncovered.append(n)
    assert (one_step_palindromes, carry_free, saturated, uncovered) == (
        152_058,
        151_250,
        808,
        [],
    )

    leading_zero_controls = {}
    for n in (10, 100, 1_200, 10_010):
        swept, sums, carries = folded_sweep(n, 10)
        direct = n + int(str(n)[::-1])
        assert swept == direct
        leading_zero_controls[str(n)] = {
            "reversed_integer": int(str(n)[::-1]),
            "output": swept,
            "column_sums_lsd_first": sums,
            "carry_bits": carries,
        }

    return {
        "folded_carry": {
            "bases": [2, 16],
            "inputs_per_base": 200_001,
            "cases": folded_cases,
            "mismatches": 0,
        },
        "non_growth_mirrored_digit_law": {
            "ordered_pairs_checked": non_growth_pairs,
            "violations": 0,
        },
        "decimal_one_step_census": {
            "input_interval": [1, 1_000_000],
            "palindromic_outputs": one_step_palindromes,
            "carry_free": carry_free,
            "saturated_0_or_11_with_outer_11": saturated,
            "uncovered": uncovered,
        },
        "leading_zero_controls": leading_zero_controls,
    }


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()
    payload = {
        "protocol": "fprd.result-review.2026-09-01.batch-16.v1",
        "scope_boundary": "finite audit plus one explicit hostile representation; no infinite-orbit assertion",
        "relative_degree": relative_degree_hostile_example(),
        "reverse_and_add": reverse_add_checks(),
        "result": "pass",
    }
    rendered = json.dumps(payload, indent=2, sort_keys=True) + "\n"
    if args.output:
        args.output.write_text(rendered, encoding="utf-8")
    else:
        print(rendered, end="")


if __name__ == "__main__":
    main()
