"""Masur–Veech volumes of Abelian strata -- numberdb.org/T560.

Run with SageMath (install the numberdb package once):
    sage -pip install numberdb
    sage -python generate.py                 # verify all stored entries
    sage -python generate.py --fill-draft    # write entries, API key required

The write operation fills the existing draft; it never offers or publishes it.
While T560 is private, verifying it also needs NUMBERDB_API_KEY.
Download argument-selection.md and the checks alongside this file.

EMZ, arXiv:math/0202134v2, Table 1, prints V/pi^(2g) for labelled zeros.
All 25 coefficients are transcribed exactly, including all components of all
18 positive partitions for 2 <= g <= 4. Sorting the profile does not remove
labels. Odd/even refer only to nonhyperelliptic components. No component
Cartesian product is taken. The two-zero hyperelliptic coefficient has the
labelled-zero factor 2!, as in AEZ Remark 1.2 and CMSZ section 6.5.

Arb evaluates each exact rational times pi^(2g). WORKING_GUARD = 64 bits is
checked by checks.py, which measures the least relative accuracy and separately
certifies every written interval with MPFI at 512 bits. The exact coefficients
are source data, not empirical decimals. This generator supports only the
source-backed finite selection, and raises for other indices.
"""
import os
import sys
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField

WORKING_GUARD = 64
# g, non-increasing positive zero profile, component, numerator, denominator.
# EMZ Table 1 orders odd before even; the enumeration here follows family #224.
ROWS = (
    (2, (2,), 'hyp', 1, 120),
    (2, (1, 1), 'hyp', 1, 135),
    (3, (4,), 'hyp', 1, 6720),
    (3, (4,), 'odd', 1, 2430),
    (3, (3, 1), 'connected', 16, 42525),
    (3, (2, 2), 'hyp', 1, 9450),
    (3, (2, 2), 'odd', 1, 4320),
    (3, (2, 1, 1), 'connected', 1, 3780),
    (3, (1, 1, 1, 1), 'connected', 1, 4860),
    (4, (6,), 'hyp', 1, 580608),
    (4, (6,), 'even', 32, 1913625),
    (4, (6,), 'odd', 1, 37800),
    (4, (5, 1), 'connected', 1, 36750),
    (4, (4, 2), 'even', 1, 107520),
    (4, (4, 2), 'odd', 1, 79380),
    (4, (4, 1, 1), 'connected', 11, 653184),
    (4, (3, 3), 'hyp', 1, 992250),
    (4, (3, 3), 'nonhyp', 1, 51030),
    (4, (3, 2, 1), 'connected', 1, 70875),
    (4, (3, 1, 1, 1), 'connected', 62, 5740875),
    (4, (2, 2, 2), 'even', 37, 6804000),
    (4, (2, 2, 2), 'odd', 31, 4354560),
    (4, (2, 2, 1, 1), 'connected', 131, 13608000),
    (4, (2, 1, 1, 1, 1), 'connected', 1, 136080),
    (4, (1, 1, 1, 1, 1, 1), 'connected', 377, 67359600),
)
COEFFICIENTS = {(g, mu, c): QQ(a) / QQ(b) for g, mu, c, a, b in ROWS}


def volume(g, mu, component, digits=100):
    """A certified volume in the labelled-zero EMZ hypersurface convention."""
    q = COEFFICIENTS[(int(g), tuple(mu), component)]
    field = RealBallField(numberdb.bits(digits, losing=WORKING_GUARD))
    return field(q) * field.pi() ** (2 * int(g))


class AbelianStratumVolumes(numberdb.Generator):
    table = 'T560'
    parameters = ('g', 'mu', 'component')
    type = 'R'
    digits = 100
    rigour = 'proven'
    files = ('generate.py', 'checks.py', 'check.py', 'independent.py',
             'reference-coefficients.json', 'argument-selection.md',
             'numerical-evidence.json', 'sources.json', 'skill-evidence.json')

    def enumerate(self):
        for g, mu, component, _, _ in ROWS:
            yield {'g': g, 'mu': ','.join(map(str, mu)), 'component': component}

    def value(self, params, digits):
        g = int(params['g'])
        mu = tuple(int(m) for m in params['mu'].split(','))
        c = params['component']
        q = COEFFICIENTS[(g, mu, c)]
        a, b = q.numerator(), q.denominator()
        formula = (r'\pi^{%d}/%s' % (2*g, b) if a == 1
                   else r'(%s/%s)\pi^{%d}' % (a, b, 2*g))
        return {'number': volume(g, mu, c, digits),
                'comment': 'The exact volume is $%s$ CITE{EMZ}.' % formula}


if __name__ == '__main__':
    generator = AbelianStratumVolumes()
    if '--fill-draft' in sys.argv:
        print(generator.publish(message='Fill 25 labelled Abelian-stratum volumes from exact coefficients'))
    else:
        result = generator.verify(sample=None)
        print(result)
        sys.exit(0 if result.ok else 1)
