"""Check the mass definition and all 37 values of numberdb.org/T497.

    sage -pip install numberdb
    sage -python repair_checks.py
    sage -python repair_checks.py T497-before.json

Keep the table's generate.py beside this file. Without a JSON document argument,
read the current table; private drafts require NUMBERDB_API_KEY. The campaign
wrapper may instead supply that key on stdin with NUMBERDB_KEY_FROM_STDIN=1.

The rank-one integral unimodular Gram matrix is [1]. Its integral invertible
isometries have u^2=1, hence u=+1 or -1; the full-group mass is exactly 1/2.
The general genus routine provides a separate local-density computation for
all positive dimensions. Dimension zero is the trivial-group case.
"""
import json
import os
import sys

import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.matrix.constructor import matrix
from sage.symbolic.ring import SR
from sage.symbolic.function import BuiltinFunction
import sage.functions.all
from sage.quadratic_forms.genera.genus import Genus, genera
from sage.env import SAGE_VERSION
from generate import mass


def independent(parity, n):
    if n == 0:
        return QQ(1)
    if parity == 'I':
        return Genus(matrix.identity(ZZ, n)).mass()
    candidates = genera((n, 0), 1, even=True)
    assert len(candidates) == 1
    return candidates[0].mass()


def check(document):
    # GL_1(Z) consists precisely of the units +1 and -1.
    gram = matrix(ZZ, 1, 1, [1])
    units = [matrix(ZZ, 1, 1, [u]) for u in (-1, 1)]
    assert all(u.transpose() * gram * u == gram for u in units)
    rank_one = QQ(1) / len(units)
    assert rank_one == QQ(1) / 2
    controls = [('I', 1, rank_one), ('I', 2, QQ(1)/8),
                ('II', 8, QQ(1)/696729600)]
    for parity, n, expected in controls:
        assert independent(parity, n) == expected
    values = {(p, int(n)): QQ(v['number'] if isinstance(v, dict) else v)
              for p, rows in document['Numbers'].items() for n, v in rows.items()}
    assert set(values) == ({('I', n) for n in range(1, 33)} |
                           {('II', n) for n in range(0, 33, 8)})
    for (parity, n), value in values.items():
        assert value == mass(parity, n) == independent(parity, n), (parity, n)
    assert values['I', 1] == rank_one
    assert 2 * values['I', 1] != independent('I', 1)
    for n in range(8, 33, 8):
        assert 2 * values['I', n] == (2**(n//2)-1) * (2**(n//2-1)+1) * values['II', n]
    print(json.dumps({'sage_version': SAGE_VERSION, 'entries_checked': len(values),
                      'generator_matches': len(values), 'independent_matches': len(values),
                      'known_controls_passed': len(controls), 'rank_one_mass': str(rank_one),
                      'doubled_rank_one_rejected': True, 'parity_relations_passed': 4}, indent=2))


if __name__ == '__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        os.environ['NUMBERDB_API_KEY'] = sys.stdin.read().strip()
    if len(sys.argv) > 1:
        with open(sys.argv[1]) as source:
            document = json.load(source)
    else:
        document = numberdb.table('T497')
    check(document)
