"""Check the unchanged numbers and displayed program of numberdb.org/T481.

Download generate.py from the table into this directory. Run with:
    sage -python repair_checks.py

Reads the live document with numberdb (NUMBERDB_API_KEY is needed for a
private draft), or T481-before.json alongside this file for an offline
snapshot check. No data is written. This checks the attached generator's
enclosures against the stored intervals; it is not an independent derivation
of the planar graph enumeration theorems.
"""
import contextlib
import io
import json
from decimal import Decimal
from pathlib import Path
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.env import SAGE_VERSION
from generate import constants


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


snapshot = Path(__file__).with_name('T481-before.json')
doc = json.loads(snapshot.read_text()) if snapshot.exists() else numberdb.table('T481')
computed = constants(100)
results = {'sage_version': SAGE_VERSION, 'entries': {}, 'scope': __doc__}
for graph_class, branch in doc['Numbers'].items():
    stored = branch['vertices']['labelled']['number']
    result = computed[graph_class]
    if '/' in stored:
        assert result == QQ(stored)
        results['entries'][graph_class] = {'exact_match': True, 'value': str(result)}
    else:
        center = rational_decimal(stored)
        unit = QQ(10) ** Decimal(stored).as_tuple().exponent
        assert result.is_finite()
        assert center - unit < result < center + unit
        assert result.rad() < QQ(1) / 10**108
        results['entries'][graph_class] = {
            'enclosure_inside_stored_interval': True,
            'radius': str(result.rad()),
            'radius_below_1e_minus_108': True,
        }

output = io.StringIO()
namespace = {}
with contextlib.redirect_stdout(output):
    exec(compile(doc['Programs']['Sage']['code'], 'Programs.Sage', 'exec'), namespace)
field = namespace['R']
program_ball = (272 + 112*field(7).sqrt())/27
stored = doc['Numbers']['3-connected']['vertices']['labelled']['number']
center = rational_decimal(stored)
unit = QQ(10) ** Decimal(stored).as_tuple().exponent
assert program_ball.is_finite() and program_ball.rad() < QQ(1)/10**108
assert center-unit < program_ball < center+unit
assert namespace['QQ'](256)/27 == QQ(doc['Numbers']['maximal']['vertices']['labelled']['number'])
assert len(output.getvalue().splitlines()) == 2
results['program'] = {'code_executed_unchanged': True, 'both_outputs_agree': True,
                      'stdout': output.getvalue()}
print(json.dumps(results, indent=2))
