"""Read-back enclosure and companion checks for numberdb.org/T682.

Run: sage -python readback.py. Set NUMBERDB_API_KEY to read a private draft.
NUMBERDB_KEY_FROM_STDIN=1 accepts the key from stdin for the campaign wrapper.
"""
import json
import os
import sys
from decimal import Decimal
from fractions import Fraction

import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_mpfi import RealIntervalField
from generate import GeometricSumMahler, moment
from checks import independent


def interval(text, precision=600):
    value = Decimal(text)
    exponent = value.as_tuple().exponent
    unit = Fraction(10)**exponent
    center = Fraction(value)
    low, high = center-unit, center+unit
    R = RealIntervalField(precision)
    return R(QQ(low.numerator)/QQ(low.denominator),
             QQ(high.numerator)/QQ(high.denominator))


def flatten(document):
    block=document['Numbers']
    if isinstance(block,list):
        return [(int(r['params']['n']), int(r['params']['k']), r['number']) for r in block]
    return [(int(n), int(k), value['number'] if isinstance(value,dict) else value)
            for n, group in block.items() for k, value in group.items()]


def contained(saved, computation):
    other=RealIntervalField(600)(computation)
    assert saved.lower() <= other.lower() and other.upper() <= saved.upper()


def check_companion():
    companion={(n,k):text for n,k,text in flatten(numberdb.table('T681'))}
    checked=[]
    for params in GeometricSumMahler().enumerate():
        value=GeometricSumMahler().value(params,100)
        if 'equals' not in value:
            continue
        n,k=params['n'],params['k']; cn=1 if n==2 else n
        saved=interval(companion[(cn,k)])
        contained(saved,independent(n,k))
        contained(saved,moment(n,k))
        checked.append([n,k,cn])
    print('COMPANION_JSON='+json.dumps({'entries_checked':len(checked),'identities':checked}))


def check_stored():
    generator=GeometricSumMahler()
    report=generator.verify(sample=None)
    print(report)
    assert report.ok
    stored=flatten(numberdb.table('T682'))
    identities=[(p['n'],p['k']) for p in generator.enumerate()]
    assert [(n,k) for n,k,text in stored] == identities
    measurements=[]
    for n,k,text in stored:
        saved=interval(text)
        contained(saved,independent(n,k))
        contained(saved,moment(n,k,130))
        assert saved.lower()>0 if k==2 else saved.upper()<0
        measurements.append({'n':n,'k':k,'stored':text,'characters':len(text)})
    print('READBACK_JSON='+json.dumps({'table':'T682','verified':len(stored),
        'stored_contains_independent_enclosures':len(stored),
        'longest_stored_value':max(m['characters'] for m in measurements),
        'entries':measurements}))


if __name__ == '__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
        numberdb.configure(api_key=sys.stdin.read().strip())
    check_companion()
    if '--companion-only' not in sys.argv:
        check_stored()
