"""Check Dedekind eta branch conventions and stored values -- numberdb.org/T506.

Run with SageMath; it reads the live table unless T506-document.json is beside
this file. A private draft needs NUMBERDB_API_KEY, or a key piped to stdin
with NUMBERDB_KEY_FROM_STDIN=1:
    sage -python check_eta_repair.py
In the campaign use agents/sage.sh check_eta_repair.py T506-document.json.
This reads no key and writes no table. Results are JSON on stdout.

For tau in the upper half-plane, -i*tau has positive real part. Its principal
square root is analytic there and positive for tau positive imaginary.
DLMF 23.18.5-7 with (a,b,c,d)=(0,-1,1,0) has epsilon=1 (empty Dedekind sum),
so this is the root in eta(-1/tau)=sqrt(-i*tau)*eta(tau).

The independent eta product at tau=i*sqrt(r) is enclosed by truncating at
M=256: 1-S <= product(n>M)(1-Q**n) <= 1, S=Q**(M+1)/(1-Q).
S bounds the sum of omitted factors; the lower bound follows by induction.
"""
import json
import os
import sys
from pathlib import Path
from decimal import Decimal
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.complex_arb import ComplexBallField
from sage.rings.real_arb import RealBallField
from sage.env import SAGE_VERSION

C=ComplexBallField(512)
R=RealBallField(512)
i=C.gen(0)
eps=R(10)**-140

def narrow(z):
    assert z.real().is_finite() and z.imag().is_finite(), str(z)
    if hasattr(z,'imag'):
        assert z.real().rad_as_ball()<eps and z.imag().rad_as_ball()<eps, str(z)
    else:
        assert z.rad_as_ball()<eps, str(z)
    return z

def agrees(a,b):
    narrow(a); narrow(b)
    assert a.overlaps(b), (str(a),str(b))

def decimal_rational(s):
    d=Decimal(s)
    t=d.as_tuple()
    coefficient=int(''.join(map(str,t.digits))) * (-1 if t.sign else 1)
    return QQ(coefficient)*QQ(10)**int(t.exponent),QQ(10)**int(t.exponent)

# Known-value and sign controls precede the comparisons.
eta_i=narrow(i.modular_eta())
gamma=narrow(C(QQ(1)/4).gamma()/(2*C.pi()**(QQ(3)/4)))
agrees(eta_i,gamma)
assert not eta_i.overlaps(-gamma)
assert (-i*(1+i)) == C(1,-1)

branch_results=[]
for tau in [C(1,1),C(-1,1),C(QQ(1)/2)+i*C(3).sqrt()/2]:
    radicand=-i*tau
    root=narrow(radicand.sqrt())
    assert root.real()>0 and not root.imag().contains_zero()
    lhs=narrow((-1/tau).modular_eta())
    rhs=narrow(root*tau.modular_eta())
    agrees(lhs,rhs)
    assert not lhs.overlaps(-rhs)
    branch_results.append({'tau':str(tau),'principal_root':str(root),'identity':True,'opposite_root_rejected':True})

export = Path(__file__).with_name('T506-document.json')
if export.exists():
    doc = json.loads(export.read_text())
else:
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        numberdb.configure(api_key=sys.stdin.read().strip())
    doc = numberdb.table('T506')
rows=[]
for key,entry in doc['Numbers'].items():
    r=int(key)
    text=entry['number'] if isinstance(entry,dict) else entry
    center,ulp=decimal_rational(text)
    y=R(r).sqrt()
    value=narrow((i*C(r).sqrt()).modular_eta())
    assert value.imag().contains_zero() and value.real()>0
    assert value.real().lower().exact_rational()>center-ulp and value.real().upper().exact_rational()<center+ulp
    Q=(-2*R.pi()*y).exp()
    partial=(-R.pi()*y/12).exp()
    for n in range(1,257): partial*=1-Q**n
    tail=Q**257/(1-Q)
    # The interval [1-S,1] encloses the omitted product.
    enclosure=(1-tail).union(R(1))
    product=narrow(partial*enclosure)
    agrees(product,value.real())
    assert product.lower().exact_rational()>center-ulp and product.upper().exact_rational()<center+ulp
    # Wrong stored decimals must be rejected even though the real value is small.
    assert not (product.lower().exact_rational()<=center+11*ulp and product.upper().exact_rational()>=center+9*ulp)
    root=narrow((-i*(i*C(r).sqrt())).sqrt())
    assert root.real()>0 and root.imag().contains_zero()
    agrees(root,C(r)**(QQ(1)/4))
    agrees((i/C(r).sqrt()).modular_eta(),root*value)
    rows.append({'r':r,'stored_encloses_arb_and_product':True,'positive_root':True,'reciprocity':True,'ten_ulp_control_rejected':True,'eta_accuracy_bits':int(value.real().accuracy())})
print(json.dumps({'sage':SAGE_VERSION,'numberdb':numberdb.__version__,'bits':512,'known_value_control':True,'wrong_sign_control':True,'branch_checks':branch_results,'rows':rows,'all_passed':True},indent=2))
