"""Check T471 repair against saved API documents; run with sage -python.

Pass T471-response.json, T471-proposed.json and T468-response.json beside
this file. The script executes original and proposed snippets, tests exact
controls, and recomputes all stored numbers. It never writes to NumberDB.
"""
import contextlib
import io
import json
import multiprocessing
from pathlib import Path

import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField

here = Path(__file__).resolve().parent
original = json.loads((here/'T471-response.json').read_text())
proposed = json.loads((here/'T471-proposed.json').read_text())
related = json.loads((here/'T468-response.json').read_text())


def functions(doc, section):
    scope = {}
    with contextlib.redirect_stdout(io.StringIO()):
        exec(compile(doc['Programs'][section]['code'], section, 'exec'), scope)
    return scope


def raw_number(value):
    return value['number'] if isinstance(value, dict) else value


def decimal_exact(text):
    # These API values are fixed-point decimal strings.
    whole, frac = text.split('.')
    return QQ(int(whole + frac)) / (10**len(frac)), QQ(1)/(10**len(frac))


before = functions(original, 'polygon')
after = functions(proposed, 'polygon')
report = {'polygon_controls': [], 'polygon_stored_checks': [], 'ellipse_stored_checks': []}
for function, region, four in [('triangle_vertices','equilateral-triangle',QQ(11)/3),('parallelogram_vertices','unit-square',QQ(133)/36)]:
    assert before[function](1) == 0
    for n, want in [(1,QQ(1)),(2,QQ(2)),(3,QQ(3)),(4,four)]:
        got=after[function](n)
        assert got == want
        report['polygon_controls'].append({'function':function,'n':n,'before':str(before[function](n)),'after':str(got),'exact':True})
    for key, value in original['Numbers'][region].items():
        n=int(key); got=after[function](n)
        assert got == QQ(raw_number(value)) == before[function](n)
        report['polygon_stored_checks'].append({'region':region,'n':n,'value':str(got)})
    assert after[function](21) == before[function](21)
print('Both polygon defects reproduced; corrected controls n=1,2,3,4 and all 34 exact stored rows pass.',flush=True)

original_ellipse=functions(original,'ellipse')['ellipse_vertices']
ellipse=functions(proposed,'ellipse')['ellipse_vertices']
assert ellipse(1) == QQ(1)
report['ellipse_repaired_n1'] = {'result': str(ellipse(1)), 'exact': True}

def singleton(queue):
    try:
        v=original_ellipse(1)
        queue.put({'result':str(v),'finite':bool(v.is_finite()),'is_one':bool(v==1)})
    except Exception as exc:
        queue.put({'exception':type(exc).__name__,'message':str(exc)})

ctx=multiprocessing.get_context('fork')
queue=ctx.Queue(); proc=ctx.Process(target=singleton,args=(queue,)); proc.start(); proc.join(20)
if proc.is_alive():
    proc.terminate(); proc.join()
    report['ellipse_original_n1']={'timeout_seconds':20}
else:
    report['ellipse_original_n1']=queue.get(timeout=2)
print('Original ellipse(1):',report['ellipse_original_n1'],flush=True)

B=RealBallField(512)
closed=B(4)-B(35)/(12*B.pi()**2)
for n in [2,3]:
    v=ellipse(n)
    assert v.is_finite() and n in v and B(v.rad())<B(10)**(-110)
for key,value in original['Numbers']['unit-disk'].items():
    n=int(key); v=ellipse(n)
    center,ulp=decimal_exact(raw_number(value))
    assert v.is_finite() and B(v.rad())<B(10)**(-110)
    assert abs(B(v)-B(center))<B(ulp)
    if n==4:
        assert B(v).overlaps(closed)
    report['ellipse_stored_checks'].append({'n':n,'recomputed':str(v),'radius':str(v.rad()),'error_ulps_upper':str((abs(B(v)-B(center))/B(ulp)).upper())})
    print('Ellipse n=%s agrees with stored enclosure.'%n,flush=True)

prob_center,prob_ulp=decimal_exact(related['Numbers']['unit-disk']['4']['number'])
prob=B(1)-B(35)/(12*B.pi()**2)
assert prob.is_finite() and abs(prob-B(prob_center))<B(prob_ulp)
assert (3+prob).overlaps(closed)
report['linked_four_point_probability']={'tid':'T468','entry':'unit-disk,4','comment':related['Numbers']['unit-disk']['4']['comment'],'closed_form':str(prob),'e_equals_3_plus_p':True}
report['all_checks_passed']=True
print('RESULT_JSON='+json.dumps(report,sort_keys=True),flush=True)
