"""Chowla–Selberg period constants -- numberdb.org/T669.

Run with SageMath or passagemath and numberdb installed:
    sage -python generate.py              # verify every stored entry, read only
    sage -python checks.py                # independent checks, no website needed

The default selection is every negative fundamental discriminant |D| <= 1000,
in increasing |D|. See argument-selection.md for its arithmetic justification.
The normalization is Li, Long and Tu, SIGMA 14 (2018), 090, equation (2.1).
All roots and logarithms are real and positive where applicable.

For 100 significant digits we use 429-bit Arb balls (96 extra bits), then
require at least 365 relative accuracy bits; insufficient precision escalates
by 64 bits, never silently loses recorded digits. No precision escalation was
needed in the initial build. Checks record the measured worst-case accuracy.
The class number is obtained exactly from Dirichlet's finite character sum;
checks.py independently counts reduced forms and compares PARI class numbers.

The build reads the matching archived canonical skill versions in
agents/runs/20261102T045202Z-build-skills/manifest.json. Both have SHA-256
 a2b28d9953264d7b0ca62663785c2952190c5be6d6d3597fd695d9a54d606278.
"""
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField
from functools import lru_cache

LIMIT = 1000
WORKING_GUARD = 96


def fundamental(D):
    D = int(D)
    return D < 0 and ((D % 4 == 1 and ZZ(D).is_squarefree()) or
                     (D % 4 == 0 and (D // 4) % 4 in (2, 3)
                      and ZZ(D // 4).is_squarefree()))


@lru_cache(maxsize=None)
def character_data(D):
    D = int(D)
    if not fundamental(D):
        raise ValueError('D must be a negative fundamental discriminant')
    d = -D
    w = 6 if D == -3 else 4 if D == -4 else 2
    chi = tuple(int(ZZ(D).kronecker(a)) for a in range(1, d))
    numerator = -w * sum(a * c for a, c in enumerate(chi, 1))
    assert numerator > 0 and numerator % (2 * d) == 0
    h = numerator // (2 * d)
    return chi, h, w


def period(D, digits=100, extra_bits=WORKING_GUARD):
    D = int(D)
    chi, h, w = character_data(D)
    precision = numberdb.bits(digits, losing=extra_bits)
    for _ in range(8):
        R = RealBallField(precision)
        S = sum((c * R(QQ(a) / (-D)).log_gamma()
                 for a, c in enumerate(chi, 1) if c), R(0))
        b = R.pi().sqrt() * (QQ(w) / (4*h) * S).exp()
        if b.is_finite() and b > 0 and b.accuracy() >= numberdb.bits(digits, losing=32):
            return b
        precision += 64
    raise ArithmeticError('No sufficiently narrow finite enclosure')


class ChowlaSelbergPeriods(numberdb.Generator):
    table = 'T669'
    parameters = ('D',)
    type = 'R'
    digits = 100
    rigour = 'proven'
    files = ('generate.py', 'checks.py', 'check.py', 'argument-selection.md',
             'numerical-evidence.json', 'skill-evidence.json', 'source-notes.md')

    def enumerate(self, limit=LIMIT):
        for d in range(3, int(limit) + 1):
            if fundamental(-d):
                yield {'D': str(-d)}

    def value(self, params, digits):
        D = int(params['D'])
        value = period(D, digits)
        if D == -3:
            return {'number': value,
                    'comment': r'The field $\mathbb{Q}(\sqrt{-3})$ has $h_D=1$ and $w_D=6$. Its Gamma expression is CITE{formula-small-fields}.'}
        if D == -4:
            return {'number': value,
                    'comment': r'The Gaussian field $\mathbb{Q}(i)$ has $h_D=1$ and $w_D=4$. The square-lattice identities are CITE{formula-square}.',
                    'equals': 'HREF{Values_of_the_Beta_function_at_pairs_of_rational_numbers#1/4,1/2}'}
        return value


if __name__ == '__main__':
    import sys
    result = ChowlaSelbergPeriods().verify(sample=None)
    print(result)
    sys.exit(0 if result.ok else 1)
