#!/usr/bin/env python3
"""Reproduce bounded arithmetic checks from Galton's 1901 tables.

This is an audit of a historical construction, NOT a model of human worth.
Standard library only. Reads preserved transcriptions; writes derived results.
Table III's literal modern implementation is exploratory, not exact reproduction.
"""
from __future__ import annotations
import csv
import json
import math
from pathlib import Path
from statistics import NormalDist

ROOT = Path(__file__).resolve().parents[1]
DATA = ROOT / 'data'
OUT = ROOT / 'results'


def read_csv(name: str) -> list[dict[str, str]]:
    path = DATA / name
    if not path.is_file():
        raise FileNotFoundError(f'Missing transcription: {path}')
    with path.open(encoding='utf-8', newline='') as f:
        return list(csv.DictReader(f))


def write_csv(name: str, rows: list[dict]) -> None:
    if not rows:
        raise ValueError('Refusing to write an empty result table.')
    with (OUT / name).open('w', encoding='utf-8', newline='') as f:
        w = csv.DictWriter(f, fieldnames=list(rows[0]))
        w.writeheader()
        w.writerows(rows)


def main() -> None:
    OUT.mkdir(exist_ok=True)
    norm = NormalDist()
    q = norm.inv_cdf(0.75)
    edges = [-math.inf] + [k*q for k in range(-4, 5)] + [math.inf]
    mass = [norm.cdf(b)-norm.cdf(a) for a, b in zip(edges, edges[1:])]
    t1 = read_csv('galton_1901_table_I.csv')
    assert len(t1) == 10 and abs(sum(mass)-1) < 1e-12
    t1check = []
    for row, w in zip(t1, mass):
        expected = 10000*w
        printed = int(row['printed_per_10000'])
        t1check.append(dict(class_label=row['class'], printed=printed,
                            normal_integral=expected, rounded=round(expected),
                            rounded_minus_printed=round(expected)-printed))
    assert all(r['rounded_minus_printed'] == 0 for r in t1check)
    write_csv('table_I_check.csv', t1check)

    inputs = read_csv('galton_1901_table_II_input.csv')
    maps = read_csv('galton_1901_table_II_mapping.csv')
    approx = {r['booth_group']: int(r['galton_approximate_per_1000']) for r in inputs}
    outputs: dict[str, float] = {}
    weights: dict[str, float] = {}
    for r in maps:
        f, t, w = r['from_group'], r['to_class'], float(r['weight_on_approximated_group'])
        contribution = approx[f]*w
        assert abs(contribution-int(r['printed_contribution_per_1000'])) < 1e-12
        outputs[t] = outputs.get(t, 0)+contribution
        weights[f] = weights.get(f, 0)+w
    assert all(abs(w-1) < 1e-12 for w in weights.values())
    assert sum(int(r['galton_reported_per_1000']) for r in inputs) == 1000
    assert sum(approx.values()) == sum(outputs.values()) == 1000
    t2labels = ['T_and_above','S','R','r','s','t_and_below']
    comparator = [89,161,250,250,161,89]
    t2check = [dict(class_label=c, regrouped_per_1000=outputs[c],
                    normal_comparator_per_1000=n, difference=outputs[c]-n)
               for c,n in zip(t2labels, comparator)]
    write_csv('table_II_check.csv',t2check)
    tv = sum(abs(r['difference']) for r in t2check)/2000

    cells = read_csv('galton_1901_table_III_cells.csv')
    margins = read_csv('galton_1901_table_III_margins.csv')
    labels = ['v','u','t','s','r','R','S','T','U','V']
    index = {c:i for i,c in enumerate(labels)}
    matrix = [[0]*10 for _ in labels]
    for r in cells:
        matrix[index[r['parent_class']]][index[r['child_class']]] = int(r['arithmetic_value'])
    parent = [int(r['row_label_parent_count']) for r in margins]
    child = [int(r['printed_child_margin']) for r in margins]
    rowsums = [sum(r) for r in matrix]
    colsums = [sum(matrix[i][j] for i in range(10)) for j in range(10)]
    assert colsums == child
    assert sum(rowsums) == 10004 and sum(parent) == 10000
    t3margin = [dict(class_label=c, header_parent_count_as_read=int(margins[i]['header_parent_count_as_read']),
                     row_label_parent_count=parent[i], computed_row_sum=rowsums[i],
                     row_difference=rowsums[i]-parent[i], computed_child_margin=colsums[i],
                     printed_child_margin=child[i]) for i,c in enumerate(labels)]
    write_csv('table_III_margin_check.csv', t3margin)

    # A direct modern transcription of the verbal specification. It is NOT fit
    # to the historical table. Preserve rather than optimize away discrepancies.
    beta = 2/3
    spread = 3/4
    medians = [norm.inv_cdf((norm.cdf(a)+norm.cdf(b))/2) for a,b in zip(edges,edges[1:])]
    literal=[]
    for i,c in enumerate(labels):
        center = beta*medians[i]
        for j,d in enumerate(labels):
            prob = norm.cdf((edges[j+1]-center)/spread)-norm.cdf((edges[j]-center)/spread)
            val = parent[i]*prob
            literal.append(dict(parent_class=c,child_class=d,printed=matrix[i][j],
                                modern_literal_value=val,modern_literal_rounded=round(val),
                                rounded_minus_printed=round(val)-matrix[i][j]))
    write_csv('table_III_literal_implementation_NOT_exact.csv',literal)

    result = dict(
        source='Galton 1901, Nature 64, 659–665; Tables I–III',
        source_status='One-analyst transcription of web-rendered primary page images; independent review pending',
        table_I=dict(probable_error_in_sigma=q,all_ten_rounded_cells_match=True,total=10000),
        table_II=dict(reported_total=1000,approximate_total=1000,regrouped_total=1000,
                      regrouped=[outputs[c] for c in t2labels],normal_comparator=comparator,
                      differences=[r['difference'] for r in t2check],
                      total_variation_distance_after_construction=tv,
                      interpretation='Descriptive agreement after approximation/regrouping, NOT inferential evidence or validation of civic worth'),
        table_III=dict(row_label_parent_total=sum(parent),header_parent_total_as_read=sum(int(r['header_parent_count_as_read']) for r in margins),
                       child_total=sum(child),cells_match_printed_child_margins=True,
                       exact_reproduction=False,
                       literal_implementation_cells_differ_after_rounding=sum(r['rounded_minus_printed']!=0 for r in literal),
                       literal_implementation_max_absolute_rounded_cell_difference=max(abs(r['rounded_minus_printed']) for r in literal),
                       variance_beta_squared_plus_spread_squared=beta**2+spread**2,
                       exact_stationary_spread_for_this_continuous_model=math.sqrt(1-beta**2),
                       note='Stationarity is imposed in the source. Finite bins, median approximation and historical rounding differ from an exact continuous stationary model. This calculation does not identify the sole cause of every residual.'),
        conditional_yield_ratio=dict(formula='(6/35)/(3/2500)',value=(6/35)/(3/2500),
                                    meaning='Ratio within Galton’s stipulated Table III model, not a measured comparison of human value'),
        limits=['No upstream Booth table has been independently reconstructed.',
                'No exact Table III reproduction or Figure/Table IV audit is claimed.',
                'No historical person-level data or causal effects on later populations are estimated.',
                'No hypothesis about the distribution of civic worth is empirically validated.'])
    (OUT/'arithmetic_checks.json').write_text(json.dumps(result,indent=2),encoding='utf-8')
    print(json.dumps(result,indent=2))

if __name__ == '__main__':
    main()
