"""Reproduce the T479 repair's mathematical checks without writing to NumberDB.

Download generate.py, sources.json and this file from T479, then run:
    sage -pip install numberdb
    sage -python repair_checks.py
For a private draft, set NUMBERDB_API_KEY or pipe it on stdin with
NUMBERDB_KEY_FROM_STDIN=1. All comparisons read the current document.
The six source polynomials are checked independently with exact Sturm counts.
The normalisation test checks compatibility, not optimality or source accuracy.
"""
import json, os, sys
from decimal import Decimal
from pathlib import Path
if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
    os.environ['NUMBERDB_API_KEY']=sys.stdin.read().strip()
import numberdb.sage as numberdb
import generate
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.env import SAGE_VERSION

def rat(text):
    numerator,denominator=Decimal(str(text)).as_integer_ratio()
    return QQ(numerator)/QQ(denominator)

def decimal_ball(text):
    d=Decimal(text)
    radius=QQ(10)**d.as_tuple().exponent if '.' in text or 'e' in text.lower() else QQ(0)
    return R(rat(text),radius)

def sturm(polynomial):
    sequence=[polynomial,polynomial.derivative()]
    while sequence[-1]:
        remainder=-(sequence[-2] % sequence[-1])
        if not remainder:break
        sequence.append(remainder)
    return sequence

def variations(sequence,point):
    signs=[1 if value>0 else -1 for f in sequence if (value:=f(point))!=0]
    return sum(a!=b for a,b in zip(signs,signs[1:]))

R=RealBallField(768);P=PolynomialRing(QQ,'x');x=P.gen()
# Known-answer controls for exact Sturm counts and degree/radian conversion.
control=sturm(x*x-2)
assert variations(control,QQ(1))-variations(control,QQ(2))==1
assert variations(control,QQ(2))-variations(control,QQ(3))==0
assert (2*(R.pi()*90/360).sin()).overlaps(R(2).sqrt())
assert 2 in (2*(R.pi()*180/360).sin())
doc=numberdb.table('T479')
changed=(17,18,22,38,50,52);sturm_results=[]
for n in changed:
    row=generate.DATA['rows'][str(n)]
    polynomial=P(row['polynomial'].replace('^','**'))
    center=rat(row['cosine']);radius=QQ(1)/10**10
    sequence=sturm(polynomial)
    roots=variations(sequence,center-radius)-variations(sequence,center+radius)
    assert roots==1 and polynomial(center-radius)*polynomial(center+radius)<0
    sturm_results.append({'n':n,'degree':int(polynomial.degree()),'root_count':roots,'center':row['cosine'],'radius':'1e-10'})

normalisations=[]
for n, rows in doc['Numbers'].items():
    theta=decimal_ball(rows['angle']['number']);chord=decimal_ball(rows['chord']['number'])
    recovered_chord=2*(R.pi()*theta/360).sin()
    recovered_theta=360*(chord/2).arcsin()/R.pi()
    for value in (theta,chord,recovered_chord,recovered_theta):
        assert value.is_finite() and QQ(value.rad())<QQ(1)/10**7
    assert recovered_chord.overlaps(chord) and recovered_theta.overlaps(theta),n
    normalisations.append(int(n))
report=generate.Tammes().verify(sample=None)
assert report.ok, str(report)
result={'sage_version':SAGE_VERSION,'controls_passed':True,'sturm_checks':sturm_results,
        'normalisation_configurations_checked':len(normalisations),
        'generator_verification':str(report),
        'invertibility_argument':'For 0 < theta <= 180 degrees, pi*theta/360 lies in (0, pi/2], where sine is one-to-one and principal arcsine is its inverse.',
        'limitations':'Checks do not prove global optimality or improve the source-accuracy assumptions.'}
print(json.dumps(result,indent=2),flush=True)
