"""Check the 48 free-case equality links of numberdb.org/T512.
Run with SageMath: sage -python check_free_case.py < API_KEY_FILE
Reads tables using the key on stdin; makes no writes.
"""
import sys,json,urllib.request,platform
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.complex_arb import ComplexBallField
import mpmath as mp
key=sys.stdin.read().strip()
def read(tid):
 req=urllib.request.Request('https://numberdb.org/api/table?id='+tid,headers={'Authorization':'Bearer '+key})
 with urllib.request.urlopen(req,timeout=60) as r: return json.load(r)
def rational(s):
 mantissa,_,exp=s.lower().partition('e')
 power=int(exp or '0')
 if '.' in mantissa:
  whole,frac=mantissa.split('.');power-=len(frac);mantissa=whole+frac
 return QQ(int(mantissa))*QQ(10)**power
def bounds(s):
 m,_,e=s.lower().partition('e')
 unit=QQ(10)**(int(e or '0')-(len(m.split('.')[1]) if '.' in m else 0))
 c=rational(s)
 return c-unit,c+unit
C=ComplexBallField(600)
# Nonzero controls and a pi bracket ensure a zero-valued checker cannot pass.
assert C(1).bessel_J(C(QQ(1)/2)).real()>0
assert C(4).bessel_J(C(QQ(1)/2)).real()<0
assert C(3).bessel_J(C(QQ(1)/2)).real()>0
mp.mp.dps=170
assert abs(mp.besseljzero(mp.mpf('0.5'),1)-mp.pi)<mp.mpf('1e-165')
t=read('T512'); bessel=read('T20')
rows=[]
for ell,etas in t['Numbers'].items():
 nu=str(QQ(2*int(ell)+1)/2)
 for n,row in etas['0'].items():
  target='Zeros_of_Bessel_J_functions#'+nu+','+n
  assert row['equals'].startswith('HREF{'+target+'}')
  other=bessel['Numbers'][nu][n]
  s=other['number'] if isinstance(other,dict) else other
  lo,hi=bounds(row['number']);blo,bhi=bounds(s)
  assert max(lo,blo)<min(hi,bhi)
  endpoints=[]
  for x,expected in ((lo,(-1)**(int(n)-1)),(hi,(-1)**int(n))):
   v=C(x).bessel_J(C(QQ(nu)))
   assert v.real().is_finite() and v.imag().is_finite() and v.imag().contains_zero()
   sign=1 if v.real()>0 else -1 if v.real()<0 else 0
   assert sign==expected
   endpoints.append(sign)
  root=mp.besseljzero(mp.mpf(2*int(ell)+1)/2,int(n))
  rootq=rational(mp.nstr(root,165))
  assert lo<rootq<hi and blo<rootq<bhi
  if ell=='0': assert abs(root-int(n)*mp.pi)<mp.mpf('1e-160')
  rows.append({'ell':ell,'n':n,'target':target,'endpoint_signs':endpoints,'mpmath_containment':True})
assert len(rows)==48
print(json.dumps({'passed':True,'count':48,'ball_bits':600,'mpmath_dps':170,'python':platform.python_version(),'mpmath':mp.__version__,'rows':rows},indent=2))
