"""Reproducible checks for T560. Run: sage -python checks.py [--stored].

Run beside generate.py, independent.py, reference-coefficients.json and check.py.
The last file is the campaign's unmodified general exactness/size checker.
--stored needs NUMBERDB_API_KEY while the draft is private. It checks the exact
interval asserted by every stored decimal, not a binary64 search result.
"""
import json
import sys
from pathlib import Path
from fractions import Fraction as F
from decimal import Decimal
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_mpfi import RealIntervalField
from sage.env import SAGE_VERSION
from numberdb._write import to_text
from numberdb._generate import _stored_entries
from check import exactness, measure, agrees_with, names_its_rings
import generate
import independent as other


def rational(x):
    return QQ(x.numerator) / QQ(x.denominator)


def decimal_bounds(text):
    mid = F(text)
    ulp = F(10) ** Decimal(text).as_tuple().exponent
    return mid-ulp, mid+ulp


def run(stored=False):
    # Controls run first and have nonzero known answers.
    other.controls()
    I = RealIntervalField(512)
    third = I(1)/I(3)
    assert third.lower().exact_rational() < QQ(1)/QQ(3) < third.upper().exact_rational()
    assert 0 < third.upper().exact_rational()-third.lower().exact_rational() < QQ(2)**(-500)
    machin = I(16)*I(QQ(1)/QQ(5)).arctan()-I(4)*I(QQ(1)/QQ(239)).arctan()
    assert (machin - I.pi()).contains_zero()
    assert machin.upper().exact_rational()-machin.lower().exact_rational() < QQ(2)**(-500)

    q = {(g,mu,c): F(a,b) for g,mu,c,a,b in generate.ROWS}
    expected = other.selection()
    assert list(q) == expected
    gen = generate.AbelianStratumVolumes()
    params = list(gen.enumerate())
    assert [(int(p['g']),tuple(map(int,p['mu'].split(','))),p['component']) for p in params] == expected
    assert len(q) == 25 and len(set((g,mu) for g,mu,c in q)) == 18
    assert not names_its_rings(str(Path(__file__).with_name('generate.py')))
    values = {key:gen.value(p,100) for key,p in zip(expected,params)}
    assert not exactness(values)

    independent = {}
    for g,mu,c in q:
        if c == 'hyp':
            independent[(g,mu,c)] = other.hyperelliptic(g,len(mu))
    for g in range(2,5):
        mu=(2*g-2,)
        total, delta = other.minimal_total(g), other.spin_difference(g)
        assert sum(v for (gg,mm,cc),v in q.items() if (gg,mm)==(g,mu)) == total
        hyp = other.hyperelliptic(g,1)
        spin = ((g+1)//2) % 2
        for c,sign,parity in [('even',1,0),('odd',-1,1)]:
            if (g,mu,c) in q:
                independent[(g,mu,c)] = (total + sign*delta)/2 - (hyp if spin==parity else 0)
    assert not agrees_with({k:q[k] for k in independent}, lambda k:independent[k])

    ref = json.loads(Path(__file__).with_name('reference-coefficients.json').read_text())
    zeta_pi = {2:F(1,90),3:F(1,945),4:F(1,9450)}
    for row in ref['rows']:
        key=(row['g'],tuple(map(int,row['mu'].split(','))),row['component'])
        assert q[key] == F(row['zeta_coefficient'])*zeta_pi[row['g']]
    principal_ratios=[]
    for g,target in [(2,F(27,8)),(3,F(162,7)),(4,F(22275,377))]:
        mu1=(2,)+(1,)*(2*g-4); mu2=(1,)*(2*g-2)
        a=sum(v for (gg,mu,c),v in q.items() if (gg,mu)==(g,mu1))
        b=sum(v for (gg,mu,c),v in q.items() if (gg,mu)==(g,mu2))
        ratio=3*(g-1)*(2*g-3)*a/b
        assert ratio == target
        principal_ratios.append(str(ratio))
    assert (F(1,6)**2-F(1,90))/2 == q[(2,(2,),'hyp')]

    numbers = (_stored_entries(numberdb.table('T560')) if stored else
               {','.join(str(p[k]) for k in gen.parameters):to_text(values[key]['number'],100)
                for key,p in zip(expected,params)})
    assert set(numbers) == {','.join(str(p[k]) for k in gen.parameters) for p in params}
    evidence=[]
    stored_intervals={}
    for key,p in zip(expected,params):
        g,mu,c=key
        identity=','.join(str(p[k]) for k in gen.parameters)
        text=numbers[identity]
        lo,hi=decimal_bounds(text)
        # Independent MPFI evaluation, never a fixed-precision result wrapped
        # in an interval. Exact input fractions are outward-rounded by MPFI.
        v=I(rational(q[key]))*I.pi()**(2*g)
        a,b=v.lower().exact_rational(),v.upper().exact_rational()
        assert rational(lo) <= a <= b <= rational(hi), identity
        assert 0 < b-a < a*QQ(10)**(-145)
        ball=values[key]['number']
        bits=int(ball.accuracy())
        assert ball.is_finite() and bits >= 370
        stored_intervals[key]=I(rational(lo),rational(hi))
        evidence.append({'params':p,'coefficient':str(q[key]),'written':text,
                         'relative_accuracy_bits':bits,'mpfi_interval_contained':True})
    # Identities on the stored intervals themselves, independently of verify().
    for key,coefficient in independent.items():
        g=key[0]
        assert (stored_intervals[key] - I(rational(coefficient))*I.pi()**(2*g)).contains_zero()
    for g,target in [(2,F(27,8)),(3,F(162,7)),(4,F(22275,377))]:
        mu1=(2,)+(1,)*(2*g-4);mu2=(1,)*(2*g-2)
        aa=sum((v for (gg,mm,cc),v in stored_intervals.items() if (gg,mm)==(g,mu1)),I(0))
        bb=sum((v for (gg,mm,cc),v in stored_intervals.items() if (gg,mm)==(g,mu2)),I(0))
        assert (3*(g-1)*(2*g-3)*aa/bb-I(rational(target))).contains_zero()
    return {'table':'T560','source_mode':'stored' if stored else 'dry',
            'skill_manifest':'agents/runs/20261027T201044Z-build-skills/manifest.json',
            'skill_sha256':'e37efb7e2ededf6c1d7134c673f152f648f26e0267c556aebdf3ec65765d8b4c',
            'versions':{'Sage':SAGE_VERSION,'numberdb':numberdb.__version__},
            'controls_passed':['series exp(t)','series 1/(1-t)','genus-one total and signed volume','both genus-two hyperelliptic coefficients','MPFI rational 1/3 with narrow enclosure','Machin pi identity'],
            'selection':{'profiles':18,'components':25,'component_counts':[2,7,16],'order_checked':True},
            'mathematically_independent_rows':len(independent),
            'independent_checks':['six hyperelliptic coefficients from AEZ/CMSZ','minimal total volumes and spin differences from CMSZ rational series; six minimal-component rows'],
            'same_source_second_transcription_rows':len(ref['rows']),
            'principal_saddle_connection_ratios':principal_ratios,
            'outside_family_identity':'H(2) volume = (zeta(2)^2-zeta(4))/2 = zeta(2,2)',
            'all_written_intervals_certified_with_mpfi':True,
            'minimum_relative_accuracy_bits':min(r['relative_accuracy_bits'] for r in evidence),
            'size':measure(values),'rows':evidence,
            'limitations':'General nonhyperelliptic multizero volumes are exact source transcriptions, not independently recomputed from intersection theory. The 23-row software table cites EMZ and therefore independently checks transcription and normalization only. All 25 EMZ coefficients were visually checked against Table 1. The two remaining rows are also covered by the genus-four principal-stratum ratio from EMZ Table 2.'}


if __name__ == '__main__':
    print(json.dumps(run('--stored' in sys.argv),indent=2,default=str))
