"""Báez-Duarte coefficients -- numberdb.org/T644.

Run with SageMath (or a compatible passagemath installation):
    sage -pip install numberdb
    sage -python generate.py                 # verify every stored entry
    sage -python generate.py --fill          # write entries to the existing draft
    sage -python checks.py                   # independent arithmetic checks

The --fill action needs NUMBERDB_API_KEY and does not publish a table.
A private draft also needs that key for verification. A campaign may supply
its key on stdin by setting NUMBERDB_KEY_FROM_STDIN=1.

The signed definition is c_k = sum_j (-1)^j binomial(k,j)/zeta(2*j+2).
We take successive differences f[j]-f[j+1] in Arb real ball arithmetic.
At order k the absolute interval error can grow like 2^k. Working precision
therefore includes one extra bit for each difference order, plus 80 bits for
relative smallness, accumulated arithmetic error, and decimal serialization.
All operations enclose the exact inputs. No infinite sum is truncated.
The finite bound 256 is a reference cutoff, not part of the domain.
See argument-selection.md and numerical-evidence.json for the selection and
measured accuracy. checks.py recomputes using exact Bernoulli recurrence and
Horner evaluation of rational polynomials in pi^(-2).
"""
import os
import re
import sys
from functools import lru_cache

import numberdb.sage as numberdb
from sage.rings.real_arb import RealBallField

MAX_K = 256
WORKING_GUARD = 80


@lru_cache(maxsize=4)
def coefficient_balls(digits, limit=MAX_K):
    field = RealBallField(numberdb.bits(digits, losing=limit + WORKING_GUARD))
    row = [1 / field(2*j + 2).zeta() for j in range(limit + 1)]
    result = []
    while row:
        value = row[0]
        if not value.is_finite():
            raise ArithmeticError('Nonfinite coefficient enclosure')
        result.append(value)
        row = [row[j] - row[j+1] for j in range(len(row)-1)]
    return tuple(result)


class BaezDuarte(numberdb.Generator):
    table = 'T644'
    parameters = ('k',)
    type = 'R'
    digits = 100
    rigour = 'proven'
    files = ('generate.py', 'checks.py', 'check.py', 'argument-selection.md')

    def enumerate(self, limit=MAX_K):
        if not isinstance(limit, int) or limit < 0:
            raise ValueError('The last coefficient index must be nonnegative')
        for k in range(limit + 1):
            yield {'k': str(k)}

    def value(self, params, digits):
        k = int(params['k'])
        if k < 0 or str(k) != str(params['k']):
            raise ValueError('k must be a nonnegative integer')
        return coefficient_balls(digits, max(MAX_K, k))[k]


def configure_key_from_stdin():
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        key = sys.stdin.read().strip()
        if re.match(r'^[A-Z_][A-Z0-9_]*\s*=', key):
            key = key.split('=', 1)[1].strip().strip('\"\'')
        numberdb.configure(api_key=key)


if __name__ == '__main__':
    configure_key_from_stdin()
    generator = BaezDuarte()
    if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
        print(generator.publish(message='Fill signed coefficients with 100 certified digits'))
    else:
        report = generator.verify(sample=None)
        print(report)
        sys.exit(0 if report.ok else 1)
