#!/usr/bin/env python3
"""
============================================================================
fell_symbolic.py
----------------------------------------------------------------------------
THE ELLIPTIC PART OF THE TWO-POINT NNLO N=4 EEC -- self-contained
open-source (sympy + mpmath) form of the recorded BootLoops EEC
machine forms.  Every definition and data row below was derived
mechanically from the earlier Wolfram-language transcription of the
same content (fell_symbolic.m, sha256
9ef9c315887f8928e324e4b7b7e895bc373f19dda6ef51d7240a99b3dcca01a8)
by parsing that file with sympy's Mathematica parser and printing the
result as Python; nothing mathematical was retyped by hand.  This
file EXECUTES: `python3 fell_symbolic.py --selftest` (see below).

OBJECT.  F^ell(ze), the finite elliptic two-fold residual of eq.(19) of
Henn-Sokatchev-Yan-Zhiboedov, arXiv:1903.05314 (the NNLO
energy-energy-correlation remainder in N=4 sYM at angle variable
ze = zeta):

  F^ell(ze) = Integrate[ outer*(R1*P1 + R2*P2),
                         {zb, 0, 1}, {t, 0, zb} ],
  outer = (ze-1)/den,  den = t(ze-zb) + (1-ze) zb,
  z     = ze t (t-zb)/den,
  R1    = z zb/(1-z-zb),   R2 = z^2 zb/((1-z)^2 (1-z zb)),
  P1,P2 = the weight-3 HPL blocks of eq.(19) (transcribed below).

All of its elliptic content lives on the equal-mass-sunrise Gamma_1(6)
curve (Phase 0 of this work, proven): the quartic

  Q(zb; ze) = zb^4 - 2 zb^3 + (4 ze^2 - 2 ze + 1) zb^2
              - (4 ze^2 - 2 ze) zb + ze^2,

which is disc_t(D_K), the t-discriminant of the K1 denominator.

THE DECOMPOSITION (recorded in CLOSURE.json, sha below, and
CLOSURE_REPORT.md).
The K2-kernel piece of R2 is GENUINELY divergent alone (non-integrable
1/(1-zb)^2 boundary pole, cancelled only by its reducible remainder --
proven two independent ways), so the correct finite split is

  F^ell(ze) = F^K1(ze) + F^R2(ze) + F^red1(ze),   all finite, all real,

gated >= 40 digits at the points ze = 3/11, 5/13 (never used in the
construction) against the recorded dps100 oracle.  This file carries all three pieces:

  FK1(ze)    the irreducible elliptic K1 sub-integral (two-fold), plus
             its certified sqrtQ-free 12-block collapsed form
             (FK1blocks(ze)) and the complete OP3 symbolic word form
             (159 elliptic words, DERIVED Q(ze) coefficients, zero
             fitted -- the data section FK1Words below);
  FR2(ze)    the full R2 term (elliptic K2 + reducible2 bound together,
             finite); its Moebius-mirror one-fold representation on the
             SAME Gamma_1(6) curve is transcribed as structural data;
  Fred1(ze)  the reducible R1 part; its exact 112-term GPL t-fold
             (rational alphabet, NO elliptic kernel -- constructive) is
             transcribed as the data section Fred1PhiWords;
  FEll(ze)   := FK1(ze) + FR2(ze) + Fred1(ze)  (+ FEllDirect(ze), the
             definitional eq.(19) two-fold, as an independent check).

HONEST CLOSURE STATUS (mirrors the accompanying note's published framing;
no overclaim):
  * F^K1: solved in explicit symbolic form F^K1(ze) = (ze-1)(S1 - S2)
    with S1, S2 in the same Gamma_1(6) word basis -- "159
    iterated-Eisenstein words with derived Q(ze) coefficients, checked
    to more than fifty digits" (note, sec. "Source normal form and
    certified closure").  OP3 gates of record: 59 d at ze = 1/3
    (bar 50), 51 d at 1/2 (bar 45), 55 d at 2/7 (bar 30; a point never
    used in the construction),
    stable across two working precisions (dps65/dps90) and two
    truncation heights (h28/h32).  Receipt: op3_fk1/fk1_gates.json +
    OP3_RESULT.md.
  * F^R2: CLOSED on the same curve -- "the Moebius mirror of the K1
    reduction gives disc2 = (zb-1)^4 Q(zb/(zb-1), ze) (the SAME
    Gamma_1(6) curve, with z(s+-) = 1/zb exactly and
    Integrate[1/sqrt(disc2), {zb,0,1}] = varpi_0 itself) and a one-fold
    representation, an exact Griffiths certificate, and an L-source
    normal form, each verified to at least 32 digits, including at the
    held-out angles" (note, sec. "The companion sectors").
  * F^red1: "the inner integral is elementary and no elliptic kernel
    survives (a constructive proof): the t-fold is an exact 112-term
    combination of generalized polylogarithms (GPLs) in the rational
    alphabet {0, zb, 1, -zb(1-ze)/ze, (1-ze)zb/(zb-ze)}, both folds
    are linearly reducible, and the resulting one-fold evaluation
    reproduces F^red1 to 40 digits at all four test angles, including
    both held-out test points" (note, ibid.).  The explicit weight-5
    ze-polylogarithm form is a mechanical lift left open there; the
    112-term machine form is transcribed below in full.
  * T0/T2 moment constants: OPEN as standalone constants.  The
    evaluator of record prints certified numerical values only --
    "These two constants are NOT identified in closed form (archived
    100-digit PSLQ searches saturate honestly)" (fk1-evaluate.py) --
    and the recorded artifacts agree: "T0_T2: NOT identified ... honest
    saturation" (CLOSURE_FINAL.json, sha below).  The note's published
    frame reduces them exactly as T_k = A_k S0 + B_k F^K1/(ze-1)
    + CL_k (rational A_k, B_k) with the companion constant S0
    determined in the same 159-word basis, its final three-word
    remainder verified at the 30-digit precision floor of those
    evaluations; nothing beyond that is claimed here.

VERIFICATION (this file executes; read before trusting).
`python3 fell_symbolic.py --selftest` runs, in order:
  (1) the exact identities of the rational layer as sympy identities
      (the curve, the Picard-Fuchs splits, the Moebius mirror, the
      letter roots, r = (ze-1) rS0 for all 159 words, the 12-block
      decode of P1);
  (2) P1hpl/P2hpl against the evaluators of record (fk1-evaluate.py
      and fell-evaluate.py, sha256 pinned below) at four recorded
      spot points, bar 40 digits;
  (3) the 159 word rows against the pinned fk1_words.json and the
      112 phi rows against fell-evaluate.py's vendored table, when
      those files sit beside this one;
  (4) FK1, FR2, Fred1 and FEll at ze = 1/3 and 1/2 by tanh-sinh
      quadrature at two degrees (the two-degree agreement is this
      file's own digit certificate) against the values a run of
      fell-evaluate.py printed, to that run's gated digits;
      FEllDirect (the definitional two-fold) against FEll at 1/3.
`--mutate` dents P1hpl by 1e-12 and must make (1), (2) and (4) fail.
The certified digits quoted in comments are those of the RECORDED
runs, identified by the sha256 table below; the digits this file
prints are certified only by its own two-degree agreement, printed
at every run.

PROVENANCE (sha256 of the transcription sources):

  CLOSURE.json             043bd76b55dd272cae15bca3d8f0ed4a4eec83e8c4ce37ab9d0b06598040311a
  CLOSURE_FINAL.json       135e163d00ac21d5192d3995e961a812b12178df5ed1774b3f91e4e84ce3277d
  PF_R.json                7db5b37d9a45dd03446a7016947c8d0d34c861a10a569cd5da947b6d052a890e
  close9_red1_form.json    b22a21c33e7b632d0eae062d61d78a92ab7609c9f26692f9c9f007d840951364
  close9_red1_gate.json    8916c6a24a20e505e110f463e835037a4a4d53427ff73395c2e4a71783f839f7
  fk1-evaluate.py          1ae3fc29efaf5979afc3f8f8e1029b2f297c6f47af4608060a39638e54e53408
                           (shipped copy, sha refreshed 2026-09-03: the
                            sibling's page-speed default + liveness edit,
                            then a comment-only vocabulary edit;
                            numeric engine unchanged since transcription)
  fk1_gates.json           bbdbfd3e278377a478a2151967d5d2525fa08f8129dafd3d8742b3389a417d47
  fk1_words.json           77dfc3a3b80e1774c79fa3144cf93726c43311e5b6ad3062d22d773eb893cc77

Source identification: the transcription sources are identified by the
sha256 table above; the same content accompanies the sunrise paper as
ancillary material.

NOT THIS FILE: E4C (the FOUR-point object) is a different
observable and is deliberately not touched here.

DERIVATION: generated by derive_fell_py.py (sha256 37a3da16fb896031...)
from fell_symbolic.m (sha256 9ef9c315887f8928...); the generator's
fidelity gate re-imported this module and matched every definition,
table row and integrand to the parsed source exactly. The emitted
bytes are a pure function of those two files and the two pinned
reference tables (no wall-clock stamp), so the derivation can be
re-run and compared byte for byte.

Conventions: ze = the EEC angle variable zeta in (0,1); zb = the inner
Feynman-parameter variable; exact rationals throughout (sympy
Rational), no floats in any definition.  Every function of the
symbolic layer takes and returns sympy expressions in the module
symbols t, zb, ze, z; the numeric layer (twofold / onefold_t) turns an
integrand expression into an mpmath integral at a settable working
precision dps and tanh-sinh degree md.
Reference values in comments are truncated to <= 10
digits; full-precision values live in the sha-identified sources above.
=========================================================================
"""
import argparse
import hashlib
import importlib.util
import json
import os
import sys
import time

import mpmath as mp
import sympy as sp
from sympy import Integer, Rational, log, pi, polylog, sqrt, zeta

# module symbols (see Conventions in the header); x = the S12nielsen
# argument, tau = the pole position of the FR2J2 one-fold
t, zb, ze, z, x, tau = sp.symbols("t zb ze z x tau")
# word-data symbols of section 3c (zb-constants and the coefficient-ring c0)
lz, l1, La2, La3, Si12, c0 = sp.symbols("lz l1 La2 La3 Si12 c0")
# phi-word symbols of section 5b
C0, CB1, CB2, CL2, CL3, CS12 = sp.symbols("C0 CB1 CB2 CL2 CL3 CS12")

# numeric-layer defaults (see twofold): working digits and tanh-sinh degree;
# --selftest certifies each value by its agreement with the md-1 run
DPS_DEFAULT, MD_DEFAULT = 20, 6

# ============================================================================
# 1. THE CURVE AND THE RATIONAL LAYER (exact; PF_R.json + fk1_words.json)
# =========================================================================

# the sunrise quartic Q(zb; ze) = disc_t(D_K); verified against the recorded
# artifacts (fk1_words.json "exact" block: Q = M^2 + 4 ze^3 (1-ze) with
# M = zb^2 - zb + 2 ze^2 - ze; g4_words.json letters block, string
# identical)

def QQuartic(zb, ze):
    return zb**4 - 2*zb**3 + zb**2*(4*ze**2 - 2*ze + 1) - zb*(4*ze**2 - 2*ze) + ze**2

# M-polynomial and the exact splits used by the OP3 second-kind reduction
# (fk1_words.json "exact"):
#   zb (1-zb) = -M + ze (2 ze - 1),   zb^2 = M + zb - (2 ze^2 - ze),
#   Q = M^2 + 4 ze^3 (1-ze),  Q(1-zb) = Q(zb),  M(1-zb) = M(zb)

def Mpoly(zb, ze):
    return zb**2 - zb + 2*ze**2 - ze

# K1 denominator (the sunrise Symanzik in the (t, zb) chart) and the
# inner-variable map z(t, zb; ze); den written exactly as in the sources

def den(t, zb, ze):
    return t*(-zb + ze) + zb*(1 - ze)

def DK(t, zb, ze):
    return zb*(1 - t)*(1 - zb) + ze*(t - zb)*(-t - zb + 1)

def zOf(t, zb, ze):
    return t*ze*(t - zb)/den(t, zb, ze)

# outer prefactor and the eq.(19) rational weights

def outerPref(t, zb, ze):
    return (ze - 1)/den(t, zb, ze)

def R1rat(z, zb):
    return z*zb/(-z - zb + 1)

def R2rat(z, zb):
    return z**2*zb/((1 - z)**2*(-z*zb + 1))

# the exact t-partial-fraction split (PF_R.json, verified residual
# 8.3e-51 at dps50):
#   outer*R1 = a1/D_K   + red1kernel     (a1/D_K = K1 of eq.(21) exactly)
#   outer*R2 = a2/D_K2  + red2exact      (a2/D_K2 = K2 of eq.(22) exactly)

def a1pref(zb, ze):
    return -zb*(zb - 1)*(ze - 1)

def red1kernel(t, zb, ze):
    return zb*(1 - ze)/den(t, zb, ze)

# K2 curve: D_K2 = D_K under the Moebius t -> t/(t-1), zb -> zb/(zb-1)
# (PF_R.json D_K2, ratio to the Moebius-built denominator = 1)

def DK2(t, zb, ze):
    return t**2*zb*ze - t*zb**2*ze + t*zb - t*ze + zb*ze - zb

def a2pref(zb, ze):
    return -zb*(ze - 1)/(zb - 1)**2

# reducible2: the exact linearly-reducible remainder of outer*R2
# (PF_R.json A2_reducible, copied from this work's own record; denominator =
# (t-1)^2 (zb-1)^2 (t ze - zb ze + zb)^2)

def red2exact(t, zb, ze):
    return (-t**2*zb**2*ze**2 + t**2*zb**2*ze + 2*t**2*zb*ze**2 - 2*t**2*zb*ze + t*zb**3*ze**2 - t*zb**3*ze - 2*t*zb**2*ze**2 + 3*t*zb**2*ze - t*zb**2 - t*zb*ze**2 + t*zb*ze + zb**2*ze**2 - 2*zb**2*ze + zb**2)/(t**4*zb**2*ze**2 - 2*t**4*zb*ze**2 + t**4*ze**2 - 2*t**3*zb**3*ze**2 + 2*t**3*zb**3*ze + 2*t**3*zb**2*ze**2 - 4*t**3*zb**2*ze + 2*t**3*zb*ze**2 + 2*t**3*zb*ze - 2*t**3*ze**2 + t**2*zb**4*ze**2 - 2*t**2*zb**4*ze + t**2*zb**4 + 2*t**2*zb**3*ze**2 - 2*t**2*zb**3 - 6*t**2*zb**2*ze**2 + 6*t**2*zb**2*ze + t**2*zb**2 + 2*t**2*zb*ze**2 - 4*t**2*zb*ze + t**2*ze**2 - 2*t*zb**4*ze**2 + 4*t*zb**4*ze - 2*t*zb**4 + 2*t*zb**3*ze**2 - 6*t*zb**3*ze + 4*t*zb**3 + 2*t*zb**2*ze**2 - 2*t*zb**2 - 2*t*zb*ze**2 + 2*t*zb*ze + zb**4*ze**2 - 2*zb**4*ze + zb**4 - 2*zb**3*ze**2 + 4*zb**3*ze - 2*zb**3 + zb**2*ze**2 - 2*zb**2*ze + zb**2)

# elliptic roots of D_K in t (g4_words.json letters; z(tPlus) = z(tMinus)
# = 1 - zb exactly), and the exact PF split over the curve:
#   a1/D_K = (-a1/sqrt(Q)) (1/(t - tPlus) - 1/(t - tMinus))
# (g4_words.json pf_split; equivalently fk1_words.json "exact":
#   1/(t-t+) - 1/(t-t-) = -sqrt(Q)/D_K)

def tPlus(zb, ze):
    return (zb**2 - zb + ze + sqrt(QQuartic(zb, ze)))/(2*ze)

def tMinus(zb, ze):
    return (zb**2 - zb + ze - sqrt(QQuartic(zb, ze)))/(2*ze)

# ============================================================================
# 2. THE WEIGHT-3 HPL BLOCKS P1, P2 OF eq.(19)
# (exact transcription of the source integrand layer: oracle_K.py /
# dintegrand2.py as vendored in fk1-evaluate.py make_inner_K1 and
# oracle_K2.py make_inner_K2; z < 0 on the integration domain, so every
# Log below is real there)
# =========================================================================

# Nielsen S_{1,2}(x) = -Li3(1-x) + Log(1-x) Li2(1-x)
# + (1/2) Log(x) Log(1-x)^2 + Zeta(3)   (Nielsen's S_{1,2})

def S12nielsen(x):
    return log(x)*log(1 - x)**2/2 + log(1 - x)*polylog(2, 1 - x) - polylog(3, 1 - x) + zeta(3)

def P1hpl(z, zb):
    H0mz = log(-z)
    H1z = -log(1 - z)
    H2z = polylog(2, z)
    H3z = polylog(3, z)
    H11z = log(1 - z)**2/2
    H21z = S12nielsen(z)
    H111z = -log(1 - z)**3/6
    H1o = -log(zb)
    H1zb = -log(1 - zb)
    H2o = polylog(2, 1 - zb)
    H3o = polylog(3, 1 - zb)
    H11o = log(zb)**2/2
    S12o = S12nielsen(1 - zb)
    H21o = S12o
    H12o = -2*S12o - log(zb)*polylog(2, 1 - zb)
    H111o = -log(zb)**3/6
    return -H0mz**2*(-H1o + H1z)/8 - H0mz*(H2o - H2z)/2 - H111o/4 + H111z/4 - H11z*H1o/4 - H12o/4 + H1z*(H11o + H2o)/4 - H1zb*(H0mz*(H1o - H1z) + H11o + H11z - H1o*H1z - H2o + H2z)/4 - H21o/4 - H21z/4 + 3*H3o/4 - 3*H3z/4

def P2hpl(z, zb):
    H0mz = log(-z)
    H1z = -log(1 - z)
    H2z = polylog(2, z)
    H3z = polylog(3, z)
    H12z = -2*S12nielsen(z) - log(1 - z)*polylog(2, z)
    H1o = -log(zb)
    H1zb = -log(1 - zb)
    H2o = polylog(2, 1 - zb)
    H3o = polylog(3, 1 - zb)
    H11o = log(zb)**2/2
    H111o = -log(zb)**3/6
    return -H0mz**2*(-H1o - H1z + H1zb)/4 + H0mz*(3*H1o*H1z + 3*H1o*H1zb + 6*H2o - 6*H2z + pi**2)/6 - H111o/2 + H11o*H1zb/2 + H12z - pi**2*H1o/12 + H1z*(-4*H11o + 8*H1o*H1zb + 8*H2o + 2*pi**2/3)/8 + H1zb*H2o - H1zb*H2z - pi**2*H1zb/12 + 2*H3o + 2*H3z

# ============================================================================
# 3. F^K1 -- THE IRREDUCIBLE ELLIPTIC K1 SUB-INTEGRAL
# =========================================================================

# 3a. Definitional two-fold (the certified quadrature-oracle route of
# fk1-evaluate.py fk1_direct):
#   F^K1(ze) = Integrate[(a1/D_K) P1, {zb,0,1}, {t,0,zb}]
# Equivalently F^K1 = (ze-1)(S1 - S2), S_k = Integrate[zb^k K, {zb,0,1}],
# K(zb) = Integrate[P1/D_K, {t,0,zb}]   (paper normalization K = Inner/a1).
#
# The two-fold is mpmath tanh-sinh quadrature (twofold below); dps =
# working digits, md = maximum tanh-sinh degree.  The integrand is
# verified by --selftest.

def FK1_integrand():
    """the (t, zb; ze) integrand of FK1 (ported from the .m
    definition; two-fold over {zb, 0, 1}, {t, 0, zb})"""
    return P1hpl(zOf(t, zb, ze), zb)*a1pref(zb, ze)/DK(t, zb, ze)


def FK1(ze_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    """FK1(ze): two-fold of FK1_integrand at exact rational ze
    (see twofold for dps / md)"""
    return twofold(FK1_integrand(), ze_val, dps, md)

# 3b. The certified sqrtQ-free 12-BLOCK COLLAPSED FORM (the numerically
# practical representation; the OP3 numeric route of record).  By the
# certified Picard-Fuchs split the 159-word combination collapses
# POINTWISE to
#   K(zb)  = Sum_j coeff_j(zb) B_j(zb),
#   B_j    = Integrate[mono_j(z(t,zb), zb)/D_K, {t, 0, zb}],
#   F^K1   = (ze-1) Integrate[zb (1-zb) K(zb), {zb, 0, 1}],
# with the 12 block monomials over (L0, L1, Li2, Li3, S12) at z = z(t,zb),
# L0 = log(-z), L1 = log(1-z), and block coefficients exact in
#   lz = log(zb), l1 = log(1-zb), La2 = polylog(2, 1-zb),
#   La3 = polylog(3, 1-zb), Si12 = S12nielsen(1-zb).
# Pointwise identity (verified by --selftest, exactly and numerically):
#   Sum_j coeff_j(zb) mono_j(z) = P1(z, zb).
# Data transcribed from fk1_words.json "blocks" (order asserted).

# exponent vectors over (L0, L1, Li2, Li3, S12)

FK1BlockMonos = [
    (2, 1, 0, 0, 0),
    (2, 0, 0, 0, 0),
    (1, 1, 0, 0, 0),
    (1, 0, 1, 0, 0),
    (1, 0, 0, 0, 0),
    (0, 3, 0, 0, 0),
    (0, 2, 0, 0, 0),
    (0, 1, 0, 0, 0),
    (0, 0, 1, 0, 0),
    (0, 0, 0, 1, 0),
    (0, 0, 0, 0, 1),
    (0, 0, 0, 0, 0),
]

# block coefficients coeff_j(zb), exact, in the order above

def FK1BlockCoeffs(zb):
    lz = log(zb)
    l1 = log(1 - zb)
    La2 = polylog(2, 1 - zb)
    La3 = polylog(3, 1 - zb)
    Si12 = S12nielsen(1 - zb)
    return [
        Rational(1, 8),  # L0*L0*L1
        -lz/8,  # L0*L0
        l1/4,  # L0*L1
        Rational(1, 2),  # L0*Li2
        -La2/2 - l1*lz/4,  # L0
        -Rational(1, 24),  # L1*L1*L1
        l1/8 + lz/8,  # L1*L1
        -La2/4 - l1*lz/4 - lz**2/8,  # L1
        l1/4,  # Li2
        -Rational(3, 4),  # Li3
        -Rational(1, 4),  # S12
        -La2*l1/4 + La2*lz/4 + 3*La3/4 + Si12/4 + l1*lz**2/8 + lz**3/24,  # 1
    ]

def FK1BlockMono(z, j):
    """mono_j(z) over (L0, L1, Li2, Li3, S12): the .m's
    FK1BlockMono[z_, j_Integer] := With[{e = FK1BlockMonos[[j]]},
      Log[-z]^e[[1]] Log[1 - z]^e[[2]] PolyLog[2, z]^e[[3]] *
      PolyLog[3, z]^e[[4]] S12nielsen[z]^e[[5]]]   (j = 1..12)"""
    e = FK1BlockMonos[j - 1]
    return (log(-z)**e[0] * log(1 - z)**e[1] * polylog(2, z)**e[2]
            * polylog(3, z)**e[3] * S12nielsen(z)**e[4])

# the pointwise-collapsed kernel integrand: Sum_j coeff_j mono_j / D_K
# ( = P1/D_K pointwise, the verified decode identity )

def KBlockIntegrand(t, zb, ze):
    """the .m's KBlockIntegrand[t_, zb_, ze_] :=
      Total[FK1BlockCoeffs[zb]*Table[FK1BlockMono[zOf[t, zb, ze], j],
      {j, 12}]]/DK[t, zb, ze]"""
    zz = zOf(t, zb, ze)
    return sum(c * FK1BlockMono(zz, j)
               for j, c in enumerate(FK1BlockCoeffs(zb), start=1)) \
        / DK(t, zb, ze)

def FK1blocks_integrand():
    """the (t, zb; ze) integrand of FK1blocks (ported from the .m
    definition; two-fold over {zb, 0, 1}, {t, 0, zb})"""
    return zb*(1 - zb)*KBlockIntegrand(t, zb, ze)


def FK1blocks(ze_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    """FK1blocks(ze): two-fold of FK1blocks_integrand at exact rational ze
    (see twofold for dps / md)"""
    return _mpq((ze - 1).subs(ze, sp.Rational(str(ze_val)))) * twofold(FK1blocks_integrand(), ze_val, dps, md)

# 3c. THE OP3 SYMBOLIC WORD FORM (this work's closed symbolic
# representation of F^K1; fk1_words.json, 159 words, derived Q(ze)
# coefficients, ZERO fitted).  Definitions, quoted from the pinned data:
#
#   word_form:  FK1 = (ze-1) * Sum_(w,mu) rS0_(w,mu)(ze) *
#                     W_(w, zb(1-zb)*mu)
#                   =        Sum_(w,mu) r_(w,mu)(ze) *
#                     W_(w, zb(1-zb)*mu)
#     (the "r" field already includes the global (ze-1):
#      r = (ze-1)*rS0 exactly, asserted at build time for all 159 rows),
#
#   W_(w, nu) = Integrate[ nu(zb) (G[tPlus, w; zb] - G[tMinus, w; zb]) /
#                          sqrt(QQuartic(zb, ze)), {zb, 0, 1} ],
#
# where G[tau, w; x] is the Goncharov polylogarithm whose letter string is
# tau followed by the letters of w, evaluated at argument x = zb
# (convention: G[a1,..,an; x] = Integrate[G[a2,..,an; t]/(t - a1),
# {t, 0, x}], G[0; x] = log(x), trailing-zero words shuffle-regularized --
# per g4_words.json / s0_words.json, the convention bank of this basis).
# Letter codes of w (g4_words.json "letters", exact):
#   "0" -> 0
#   "b" -> zb
#   "d" -> zb (1 - ze)/(zb - ze)
#   "p" -> (zb ze - zb + ze + sqrt(4 zb ze (1 - ze) + (zb ze - zb + ze)^2))
#          /(2 ze)   which SIMPLIFIES EXACTLY to 1  (perfect-square
#          discriminant (zb(1-ze)+ze)^2)
#   "m" -> the conjugate root, which simplifies exactly to -zb (1 - ze)/ze
# i.e. the rational inner alphabet {0, zb, 1, -zb(1-ze)/ze,
# (1-ze) zb/(zb-ze)} -- identical to the S0 basis, no new letters.
# The measure twists mu are monomials in the same zb-constants
# (lz, l1, La2, La3, Si12) as the block coefficients; nu = zb(1-zb)*mu.
# The coefficient ring is QQ[c0]*(ze-1), c0 = log(ze/(1-ze)).
#
# Second-kind reduction (fk1_words.json, recorded exact relations used):
#   the zb, zb^2 measures introduce exactly ONE second-kind generator,
#   the M-measure:  FK1 = (ze-1)[ze(2ze-1) S0 - SM]; its pure-period
#   sector closes on I0 = Integrate[1/sqrt(Q),{zb,0,1}] = A/3 EXACT,
#   I1 = I0/2, and the proven quasi-period shift
#   sigma(ze) = -(4/3)(1-ze) [theorem, op2_prove].  No closed form for
#   the cycle quasi-period Omega_M itself is claimed (not needed for the
#   word form or its evaluation).
#
# Data rows: {w, mu, rS0, r}.  w = letter string ("" = empty word);
# mu in {1, lz, l1, La2, La3, Si12, and products}; rS0, r exact in
# QQ[c0][ze].  Outer measure polynomial for ALL rows: zb (1 - zb)
# (the "poly" field of every source row, asserted at build time).

FK1Words = [
    ("", Si12, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("", La3, -Rational(3, 4), Rational(3, 4) - 3*ze/4),
    ("", La2, c0/2, c0*ze/2 - c0/2),
    ("", La2*l1, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("", lz, c0**2/8, c0**2*ze/8 - c0**2/8),
    ("", La2*lz, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("", l1*lz, c0/4, c0*ze/4 - c0/4),
    ("", l1*lz**2, -Rational(1, 8), Rational(1, 8) - ze/8),
    ("", lz**3, -Rational(1, 24), Rational(1, 24) - ze/24),
    ("0", La2, Rational(1, 2), ze/2 - Rational(1, 2)),
    ("0", lz, c0/4, c0*ze/4 - c0/4),
    ("0", l1*lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("b", La2, Rational(1, 2), ze/2 - Rational(1, 2)),
    ("b", lz, c0/4, c0*ze/4 - c0/4),
    ("b", l1*lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("d", Integer(1), c0**2/8, c0**2*ze/8 - c0**2/8),
    ("d", La2, -Rational(3, 4), Rational(3, 4) - 3*ze/4),
    ("d", l1, c0/4, c0*ze/4 - c0/4),
    ("d", lz, -c0/4, -c0*ze/4 + c0/4),
    ("d", l1*lz, -Rational(1, 2), Rational(1, 2) - ze/2),
    ("d", lz**2, -Rational(1, 8), Rational(1, 8) - ze/8),
    ("m", Integer(1), -c0**2/8, -c0**2*ze/8 + c0**2/8),
    ("m", La2, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("m", l1, -c0/4, -c0*ze/4 + c0/4),
    ("m", l1*lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("m", lz**2, Rational(1, 8), ze/8 - Rational(1, 8)),
    ("p", Integer(1), -c0**2/8, -c0**2*ze/8 + c0**2/8),
    ("p", La2, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("p", l1, -c0/4, -c0*ze/4 + c0/4),
    ("p", l1*lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("p", lz**2, Rational(1, 8), ze/8 - Rational(1, 8)),
    ("00", lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("0b", lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("0d", Integer(1), -c0/4, -c0*ze/4 + c0/4),
    ("0d", lz, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("0m", Integer(1), c0/4, c0*ze/4 - c0/4),
    ("0p", Integer(1), c0/4, c0*ze/4 - c0/4),
    ("b0", lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bb", lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bd", Integer(1), -c0/4, -c0*ze/4 + c0/4),
    ("bd", lz, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("bm", Integer(1), c0/4, c0*ze/4 - c0/4),
    ("bp", Integer(1), c0/4, c0*ze/4 - c0/4),
    ("d0", Integer(1), c0/4, c0*ze/4 - c0/4),
    ("d0", l1, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("d0", lz, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("db", Integer(1), c0/4, c0*ze/4 - c0/4),
    ("db", l1, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("db", lz, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("dd", l1, -Rational(1, 2), Rational(1, 2) - ze/2),
    ("dm", Integer(1), -c0/4, -c0*ze/4 + c0/4),
    ("dm", l1, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("dm", lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("dp", Integer(1), -c0/4, -c0*ze/4 + c0/4),
    ("dp", l1, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("dp", lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("m0", Integer(1), -c0/4, -c0*ze/4 + c0/4),
    ("m0", l1, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mb", Integer(1), -c0/4, -c0*ze/4 + c0/4),
    ("mb", l1, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("md", Integer(1), c0/4, c0*ze/4 - c0/4),
    ("md", l1, Rational(1, 2), ze/2 - Rational(1, 2)),
    ("md", lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("mm", l1, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mm", lz, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mp", l1, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mp", lz, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("p0", Integer(1), -c0/4, -c0*ze/4 + c0/4),
    ("p0", l1, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pb", Integer(1), -c0/4, -c0*ze/4 + c0/4),
    ("pb", l1, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pd", Integer(1), c0/4, c0*ze/4 - c0/4),
    ("pd", l1, Rational(1, 2), ze/2 - Rational(1, 2)),
    ("pd", lz, Rational(1, 4), ze/4 - Rational(1, 4)),
    ("pm", l1, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pm", lz, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pp", l1, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pp", lz, -Rational(1, 4), Rational(1, 4) - ze/4),
    ("0d0", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("0db", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("0dd", Integer(1), Rational(1, 2), ze/2 - Rational(1, 2)),
    ("0dm", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("0dp", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("0m0", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("0mb", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("0md", Integer(1), -Rational(1, 2), Rational(1, 2) - ze/2),
    ("0mm", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("0mp", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("0p0", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("0pb", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("0pd", Integer(1), -Rational(1, 2), Rational(1, 2) - ze/2),
    ("0pm", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("0pp", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bd0", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("bdb", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("bdd", Integer(1), Rational(1, 2), ze/2 - Rational(1, 2)),
    ("bdm", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("bdp", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("bm0", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bmb", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bmd", Integer(1), -Rational(1, 2), Rational(1, 2) - ze/2),
    ("bmm", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bmp", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bp0", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bpb", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bpd", Integer(1), -Rational(1, 2), Rational(1, 2) - ze/2),
    ("bpm", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("bpp", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("d00", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("d0b", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("d0d", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("db0", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("dbb", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("dbd", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("ddd", Integer(1), -Rational(1, 2), Rational(1, 2) - ze/2),
    ("ddm", Integer(1), Rational(1, 2), ze/2 - Rational(1, 2)),
    ("ddp", Integer(1), Rational(1, 2), ze/2 - Rational(1, 2)),
    ("dm0", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("dmb", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("dmd", Integer(1), Rational(3, 4), 3*ze/4 - Rational(3, 4)),
    ("dmm", Integer(1), -Rational(1, 2), Rational(1, 2) - ze/2),
    ("dmp", Integer(1), -Rational(1, 2), Rational(1, 2) - ze/2),
    ("dp0", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("dpb", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("dpd", Integer(1), Rational(3, 4), 3*ze/4 - Rational(3, 4)),
    ("dpm", Integer(1), -Rational(1, 2), Rational(1, 2) - ze/2),
    ("dpp", Integer(1), -Rational(1, 2), Rational(1, 2) - ze/2),
    ("m00", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("m0b", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("m0d", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("mb0", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mbb", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mbd", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("md0", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("mdb", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("mdm", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mdp", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mmd", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mmm", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("mmp", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("mpd", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("mpm", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("mpp", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("p00", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("p0b", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("p0d", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("pb0", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pbb", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pbd", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("pd0", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("pdb", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("pdm", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pdp", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pmd", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("pmm", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("pmp", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("ppd", Integer(1), -Rational(1, 4), Rational(1, 4) - ze/4),
    ("ppm", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
    ("ppp", Integer(1), Rational(1, 4), ze/4 - Rational(1, 4)),
]

# structural asserts carried over from the pinned data

assert len(FK1Words) == 159, "FATAL: FK1Words must have 159 rows"

# helpers: substitute the documented meanings of the data symbols.
# mu-column symbols (zb-constants):  lz, l1, La2, La3, Si12;
# coefficient-ring symbol:           c0 = log(ze/(1-ze)).
# Build-time exact assert (logged): r = (ze-1)*rS0 for all 159 rows.

def FK1MuValue(mu, zb):
    """the .m's FK1MuValue[mu_, zb_] := mu /. {lz -> Log[zb],
      l1 -> Log[1 - zb], La2 -> PolyLog[2, 1 - zb],
      La3 -> PolyLog[3, 1 - zb], Si12 -> S12nielsen[1 - zb]}"""
    return sp.sympify(mu).xreplace({
        lz: log(zb), l1: log(1 - zb), La2: polylog(2, 1 - zb),
        La3: polylog(3, 1 - zb), Si12: S12nielsen(1 - zb)})

def FK1RValue(r, zeval):
    """the .m's FK1RValue[r_, zeval_] :=
      (r /. c0 -> Log[zeval/(1 - zeval)]) /. ze -> zeval"""
    return sp.sympify(r).xreplace({c0: log(zeval / (1 - zeval))}) \
        .xreplace({ze: zeval})

# ============================================================================
# 4. F^R2 -- THE FULL R2 TERM (elliptic K2 + reducible2, finite)
# =========================================================================

# 4a. Definitional two-fold (oracle_K2.py integrate_K2; the recorded dps50
# values FR2_zeta_*_dps50.json were computed from THIS object):
#   F^R2(ze) = Integrate[outer R2 P2, {zb,0,1}, {t,0,zb}]
# Finite: only log-divergent at zb -> 1 and t -> 0, t -> zb (integrable).
# The individual K2-kernel and reducible2 pieces are NOT separately
# finite (proven; see header).

def FR2_integrand():
    """the (t, zb; ze) integrand of FR2 (ported from the .m
    definition; two-fold over {zb, 0, 1}, {t, 0, zb})"""
    return P2hpl(zOf(t, zb, ze), zb)*R2rat(zOf(t, zb, ze), zb)*outerPref(t, zb, ze)


def FR2(ze_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    """FR2(ze): two-fold of FR2_integrand at exact rational ze
    (see twofold for dps / md)"""
    return twofold(FR2_integrand(), ze_val, dps, md)

# 4b. The Moebius-mirror ONE-FOLD representation (structural data;
# close5_fr2_onefold.py + CLOSURE_FINAL.json F_R2, exact identities
# sympy residual 0, value gates 38.0/37.6 d at 1/3, 1/2 plus 32.47 d
# (3/11) and 32.72 d (5/13) at points never used in the construction):
#
#   D_K2 = zb ze (t - sPlus)(t - sMinus),
#   disc2 = (zb-1)^4 QQuartic(zb/(zb-1), ze)   -- the SAME Gamma_1(6)
#           curve (j-invariant identical), and
#   z(sPlus) = z(sMinus) = 1/zb   EXACTLY  (K1 mirror of z(t+-) = 1-zb),
#   Integrate[1/sqrt(disc2), {zb,0,1}] = varpi_0 itself,
#
#   F^R2(ze) = Integrate[ (a2/sqrt(disc2)) (J2(sPlus) - J2(sMinus))
#                         + Tred(zb), {zb, 0, 1} ],
#   J2(tau)  = Integrate[P2hpl(zOf(t, zb, ze), zb)/(t - tau), {t, 0, zb}],
#   Tred(zb) = Integrate[red2exact P2hpl, {t, 0, zb}].
#
# NUMERICAL CAVEAT (from the source of record): the two pieces of the
# integrand separately behave like C/(1-zb)^2 as zb -> 1; only the sum is
# ~ log(1-zb).  The recorded evaluation therefore used the split form on
# [0, 1-delta] and the combined two-fold integrand on [1-delta, 1]
# (hybrid, delta ~ 1e-3); a naive full-range quadrature of the split form
# will lose digits at the boundary.  The two-fold FR2(ze) above is the
# robust evaluable route.

def disc2(zb, ze):
    return zb**4*ze**2 - 2*zb**3*ze - 2*zb**2*ze**2 + 4*zb**2*ze + zb**2 - 2*zb*ze + ze**2

def sPlus(zb, ze):
    return (zb**2*ze - zb + ze + sqrt(disc2(zb, ze)))/(2*zb*ze)

def sMinus(zb, ze):
    return (zb**2*ze - zb + ze - sqrt(disc2(zb, ze)))/(2*zb*ze)

def FR2J2_integrand():
    """the t-integrand of FR2J2 (ported from the .m definition;
    one-fold over {t, 0, zb} at numeric zb, ze, tau)"""
    return P2hpl(zOf(t, zb, ze), zb)/(t - tau)


def FR2J2(zb_val, ze_val, tau_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    return onefold_t(FR2J2_integrand(), {zb: zb_val, ze: ze_val, tau: tau_val}, dps, md)

def FR2Tred_integrand():
    """the t-integrand of FR2Tred (ported from the .m definition;
    one-fold over {t, 0, zb} at numeric zb, ze)"""
    return P2hpl(zOf(t, zb, ze), zb)*red2exact(t, zb, ze)


def FR2Tred(zb_val, ze_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    return onefold_t(FR2Tred_integrand(), {zb: zb_val, ze: ze_val}, dps, md)

def FR2InnerOneFold(zb_val, ze_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    """the .m's FR2InnerOneFold[zb_, ze_, wp_] :=
      a2pref[zb, ze]/Sqrt[disc2[zb, ze]] *
        (FR2J2[zb, ze, sPlus[zb, ze], wp] - FR2J2[zb, ze, sMinus[zb, ze], wp])
      + FR2Tred[zb, ze, wp]   (structural one-fold route; see the
      NUMERICAL CAVEAT above)"""
    with mp.workdps(dps):
        zbv, zev = _mpq(zb_val), _mpq(ze_val)
        a2 = _lamb(a2pref(zb, ze), (zb, ze))(zbv, zev)
        sq = _lamb(sqrt(disc2(zb, ze)), (zb, ze))(zbv, zev)
        sp_ = _lamb(sPlus(zb, ze), (zb, ze))(zbv, zev)
        sm_ = _lamb(sMinus(zb, ze), (zb, ze))(zbv, zev)
        return (a2 / sq * (FR2J2(zb_val, ze_val, sp_, dps, md)
                           - FR2J2(zb_val, ze_val, sm_, dps, md))
                + FR2Tred(zb_val, ze_val, dps, md))

# ============================================================================
# 5. F^red1 -- THE REDUCIBLE R1 PART (weight-5 MPL, constructive)
# =========================================================================

# 5a. Definitional two-fold (close9_red1_analytic.py; the 40-digit value
# gates of close9_red1_gate.json were run against THIS object):
#   F^red1(ze) = Integrate[red1kernel P1, {zb,0,1}, {t,0,zb}],
#   red1kernel = zb (1-ze)/den = the exact PF remainder A1_reducible of
#   PF_R.json (identical up to an overall sign flip of numerator and
#   denominator; asserted at build time).

def Fred1_integrand():
    """the (t, zb; ze) integrand of Fred1 (ported from the .m
    definition; two-fold over {zb, 0, 1}, {t, 0, zb})"""
    return P1hpl(zOf(t, zb, ze), zb)*red1kernel(t, zb, ze)


def Fred1(ze_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    """Fred1(ze): two-fold of Fred1_integrand at exact rational ze
    (see twofold for dps / md)"""
    return twofold(Fred1_integrand(), ze_val, dps, md)

# 5b. THE EXACT 112-TERM GPL FORM of the t-fold (close9_red1_form.json,
# transcribed in full; NO elliptic kernel survives -- constructive proof,
# 5/5 sympy identities, Phi == P1 pointwise 59.7 d worst, Arb-certified).
#
# Exact identities (sympy residual 0):
#   red1kernel = kappa/(t - tstar),        kappa = zb (1-ze)/(ze - zb),
#   tstar = (1-ze) zb/(zb - ze),           t6 = -zb (1-ze)/ze,
#   -z    = [ze/(1-ze)] t (1 - t/zb) / (1 - t/tstar),
#   1 - z = (1-t)(1 - t/t6) / (1 - t/tstar),
#   z'/z  = 1/t + 1/(t - zb) - 1/(t - tstar).
#
# Then, with G the Goncharov GPL over the RATIONAL t-alphabet with letter
# codes (close9_red1_form.json "letters")
#   "0" -> 0,  "b" -> zb,  "1" -> 1,  "s" -> t6,  "p" -> tstar,
# and zb-constants (close9_red1_form.json "consts")
#   C0 = log(ze/(1-ze)), CB1 = log(zb), CB2 = log(1-zb),
#   CL2 = polylog(2, 1-zb), CL3 = polylog(3, 1-zb),
#   CS12 = S12nielsen(1-zb):
#
#   P1hpl(zOf(t, zb, ze), zb) = Sum_w cw G[w; t]        (Phi, 112 words),
#   Integrate[red1kernel P1, {t, 0, zb}]
#      = kappa * Sum_w cw G[Prepend[w, "p"]; zb]        (one-fold),
#   F^red1(ze) = Integrate[kappa Sum_w cw G[Prepend[w, "p"]; zb],
#                          {zb, 0, 1}].
#
# The one-fold reproduces F^red1 to 40 digits at all four test angles
# (40.06 d at 1/3, 40.05 d at 1/2, plus 40.03 d at 3/11 and 40.12 d
# at 5/13 at points never used in the construction;
# close9_red1_gate.json).  NOTE (numeric GPL caveat from
# the source of record): the one-fold words contain the boundary letter
# "b" = zb equal to the GPL argument zb; some generic GPL evaluators
# cannot evaluate those words -- the identity itself is definitional
# given the pointwise Phi gate and the exact kappa identity.
#
# The ze-alphabet of the (open) explicit weight-5 ze-polylog lift was
# derived exactly: {ze, 1-ze, 2-ze} (close10_red1_zeta_alphabet.json);
# the lift itself is a mechanical fibration left open in the note.
#
# Data rows: {w, cw}; w a letter string over {0, b, 1, s, p}
# ("" = empty word, G["" ; x] = 1); cw exact in
# QQ[C0, CB1, CB2, CL2, CL3, CS12].

def Fred1Kappa(zb, ze):
    return zb*(1 - ze)/(-zb + ze)

def Fred1TStar(zb, ze):
    return zb*(1 - ze)/(zb - ze)

def Fred1T6(zb, ze):
    return -zb*(1 - ze)/ze

Fred1PhiWords = [
    ("010", -Rational(1, 4)),
    ("100", Rational(1, 4)),
    ("0s0", -Rational(1, 4)),
    ("s00", Rational(1, 4)),
    ("0p0", Rational(1, 4)),
    ("p00", -Rational(1, 4)),
    ("00", -CB1/4),
    ("01b", -Rational(1, 4)),
    ("10b", Rational(1, 4)),
    ("0sb", -Rational(1, 4)),
    ("s0b", Rational(1, 4)),
    ("0pb", Rational(1, 4)),
    ("p0b", -Rational(1, 4)),
    ("b10", -Rational(1, 4)),
    ("1b0", Rational(1, 4)),
    ("bs0", -Rational(1, 4)),
    ("sb0", Rational(1, 4)),
    ("bp0", Rational(1, 4)),
    ("pb0", -Rational(1, 4)),
    ("01", -C0/4),
    ("10", C0/4 + CB2/4),
    ("0s", -C0/4),
    ("s0", C0/4 + CB2/4),
    ("0p", C0/4 + CB1/4),
    ("p0", -C0/4 + CB1/4 - CB2/4),
    ("0b", -CB1/4),
    ("b0", -CB1/4),
    ("0", -C0*CB1/4 - CB1*CB2/4 - CL2/2),
    ("0p1", Rational(1, 4)),
    ("01p", Rational(1, 2)),
    ("10p", -Rational(1, 4)),
    ("0ps", Rational(1, 4)),
    ("0sp", Rational(1, 2)),
    ("s0p", -Rational(1, 4)),
    ("0pp", -Rational(1, 2)),
    ("p0p", Rational(1, 4)),
    ("p10", Rational(1, 4)),
    ("1p0", -Rational(1, 4)),
    ("ps0", Rational(1, 4)),
    ("sp0", -Rational(1, 4)),
    ("b1b", -Rational(1, 4)),
    ("1bb", Rational(1, 4)),
    ("bsb", -Rational(1, 4)),
    ("sbb", Rational(1, 4)),
    ("bpb", Rational(1, 4)),
    ("pbb", -Rational(1, 4)),
    ("b1", -C0/4),
    ("1b", C0/4 + CB2/4),
    ("bs", -C0/4),
    ("sb", C0/4 + CB2/4),
    ("bp", C0/4 + CB1/4),
    ("pb", -C0/4 + CB1/4 - CB2/4),
    ("1", C0**2/8 + C0*CB2/4 - CB1**2/8 - CB1*CB2/4 - CL2/4),
    ("s", C0**2/8 + C0*CB2/4 - CB1**2/8 - CB1*CB2/4 - CL2/4),
    ("p", -C0**2/8 + C0*CB1/4 - C0*CB2/4 + CB1**2/8 + CB1*CB2/2 + 3*CL2/4),
    ("bb", -CB1/4),
    ("b", -C0*CB1/4 - CB1*CB2/4 - CL2/2),
    ("", -C0**2*CB1/8 - C0*CB1*CB2/4 - C0*CL2/2 + CB1**3/24 + CB1**2*CB2/8 + CB1*CL2/4 - CB2*CL2/4 + 3*CL3/4 + CS12/4),
    ("bp1", Rational(1, 4)),
    ("b1p", Rational(1, 2)),
    ("1bp", -Rational(1, 4)),
    ("bps", Rational(1, 4)),
    ("bsp", Rational(1, 2)),
    ("sbp", -Rational(1, 4)),
    ("bpp", -Rational(1, 2)),
    ("pbp", Rational(1, 4)),
    ("p1b", Rational(1, 4)),
    ("1pb", -Rational(1, 4)),
    ("psb", Rational(1, 4)),
    ("spb", -Rational(1, 4)),
    ("p1", C0/4 - CB1/4 - CB2/4),
    ("1p", -C0/4 - CB1/4 - CB2/2),
    ("ps", C0/4 - CB1/4 - CB2/4),
    ("sp", -C0/4 - CB1/4 - CB2/2),
    ("pp", CB2/2),
    ("pp1", -Rational(1, 2)),
    ("p1p", -Rational(3, 4)),
    ("pps", -Rational(1, 2)),
    ("psp", -Rational(3, 4)),
    ("ppp", Rational(1, 2)),
    ("111", -Rational(1, 4)),
    ("11s", -Rational(1, 4)),
    ("1s1", -Rational(1, 4)),
    ("s11", -Rational(1, 4)),
    ("11p", Rational(1, 4)),
    ("1p1", Rational(1, 4)),
    ("p11", Rational(1, 2)),
    ("1ss", -Rational(1, 4)),
    ("s1s", -Rational(1, 4)),
    ("1sp", Rational(1, 4)),
    ("1ps", Rational(1, 4)),
    ("p1s", Rational(1, 2)),
    ("ss1", -Rational(1, 4)),
    ("s1p", Rational(1, 4)),
    ("sp1", Rational(1, 4)),
    ("ps1", Rational(1, 2)),
    ("sss", -Rational(1, 4)),
    ("ssp", Rational(1, 4)),
    ("sps", Rational(1, 4)),
    ("pss", Rational(1, 2)),
    ("11", CB1/4 + CB2/4),
    ("1s", CB1/4 + CB2/4),
    ("s1", CB1/4 + CB2/4),
    ("ss", CB1/4 + CB2/4),
    ("011", -Rational(1, 4)),
    ("b11", -Rational(1, 4)),
    ("01s", -Rational(1, 4)),
    ("b1s", -Rational(1, 4)),
    ("0s1", -Rational(1, 4)),
    ("bs1", -Rational(1, 4)),
    ("0ss", -Rational(1, 4)),
    ("bss", -Rational(1, 4)),
]

# the one-fold words: the same coefficients with the letter "p" prepended
# to every word (close9_red1_analytic.py stage_build onefold_words)

# the .m's Fred1OnefoldWords = Map[{StringJoin["p", #[[1]]], #[[2]]} &,
#   Fred1PhiWords]
Fred1OnefoldWords = [("p" + w, c) for (w, c) in Fred1PhiWords]

assert len(Fred1PhiWords) == 112, "FATAL: Fred1PhiWords must have 112 rows"

# ============================================================================
# 6. ASSEMBLY AND REFERENCE VALUES
# =========================================================================

def FEll(ze_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    """the .m's FEll[ze_, wp_] := FK1[ze, wp] + FR2[ze, wp] + Fred1[ze, wp]"""
    return FK1(ze_val, dps, md) + FR2(ze_val, dps, md) + Fred1(ze_val, dps, md)

# independent check: the definitional eq.(19) two-fold

def FEllDirect_integrand():
    """the (t, zb; ze) integrand of FEllDirect (ported from the .m
    definition; two-fold over {zb, 0, 1}, {t, 0, zb})"""
    return (P1hpl(zOf(t, zb, ze), zb)*R1rat(zOf(t, zb, ze), zb) + P2hpl(zOf(t, zb, ze), zb)*R2rat(zOf(t, zb, ze), zb))*outerPref(t, zb, ze)


def FEllDirect(ze_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    """FEllDirect(ze): two-fold of FEllDirect_integrand at exact rational ze
    (see twofold for dps / md)"""
    return twofold(FEllDirect_integrand(), ze_val, dps, md)

# ----------------------------------------------------------------------------
# REFERENCE VALUES (<= 10 digits printed -- display convention; the
# full-precision strings live in the sha256-identified source files).
#
# Recorded dps50 legs (CLOSURE.json "points_dps50", sha above; F_red1 by exact
# subtraction, real to ~1e-53):
#
#   ze     F^ell           F^K1            F^R2            F^red1
#   1/7    0.2833438784    -1.850122216    0.03014270493   2.103323390
#   1/5    0.3609659513    -1.588638220    0.04776273417   1.901841437
#   1/3    0.5039409303    -1.174649680    0.08767111227   1.590919498
#   1/2    0.6024549601    -0.8157559914   0.1196244057    1.298586545
#   2/3    0.5786138054    -0.5291479287   0.1131963986    0.9945653355
#   4/5    0.4154548968    -0.3221970183   0.06884246852   0.6688094466
#
# Decomposition gate at points never used in the construction (vs the
# recorded dps100 oracle; reconstructs F^ell to >= 40 d, CLOSURE.json
# "decomposition_gate_heldout"):
#   ze = 3/11:  F^ell = 0.4454321028,  F^K1 = -1.340792572,
#               F^R2 = 0.07031216088,  F^red1 = 1.715912514
#   ze = 5/13:  F^ell = 0.5445953242,  F^K1 = -1.052214967,
#               F^R2 = 0.1004562802,   F^red1 = 1.496354011
#
# F^K1 word-form gates of record (op3_fk1/fk1_gates.json; two working
# precisions dps65/dps90, two truncation heights h28/h32):
#   ze = 1/3:  FK1 = -1.174649680...  59 certified d   (bar 50)  PASS
#   ze = 1/2:  FK1 = -0.8157559914... 51 certified d   (bar 45)  PASS
#   ze = 2/7 (never used in the construction):
#              FK1 = -1.302765327...  55 d   (bar 30)  PASS
#     (fresh sliced oracle, dps60, im_max 2.9e-76)
#
# Evidence-gated hull of the word form: ze in [1/10, 3/4] (8-point
# battery, >= 30 d at 1/10, 1/7, 3/11, 1/3, 5/13, 1/2, 3/5, 3/4,
# measured gates 36.0-41.6 d; fk1-evaluate.py's docstring records the
# per-angle gates).
#
# F^R2 one-fold / L-source gates: 38.0/37.6 d at 1/3, 1/2; 32.47 d
# (3/11) and 32.72 d (5/13) at points never used in the construction;
# fresh-angle verifier value
# F^R2(4/11) = 0.09548125588  (CLOSURE_FINAL.json, sha above).
#
# F^red1 one-fold GPL gates: 40.06 d (1/3), 40.05 d (1/2), plus
# 40.03 d (3/11) and 40.12 d (5/13) at points never used in the
# construction  (close9_red1_gate.json, sha above).
#
# Live evaluator of record for F^K1 (blog/files/eec/fk1-evaluate.py,
# sha pinned in the header): fail-closed word-form evaluation at any
# ze in the gated hull, default 32 certified digits in minutes;
# --deep = the independent ODE-transport/quadrature second leg;
# --moments = live certified T0/T2 (no closed form claimed).
# --------------------------------------------------------------------------
# USAGE
#   << eec_nnlo_n4_elliptic.m
#   FK1(1/3, 40)        (* expect -1.174649680...; recorded 59 d gate *)
#   FR2(1/3, 40)        (* expect  0.08767111227...                    *)
#   Fred1(1/3, 40)      (* expect  1.590919498...                     *)
#   FEll(1/3, 40)       (* expect  0.5039409303...                    *)
#   FEllDirect(1/3, 40) (* same value, definitional route              *)
#   FK1blocks(1/3, 40)  (* block form == FK1 (certified PF collapse)   *)
# =========================================================================


# ============================================================================
# 7. NUMERIC LAYER (mpmath).  The symbolic functions above return sympy
#    expressions; these helpers turn an integrand expression into a tanh-sinh
#    integral (mpmath.quad) at working precision dps and maximum degree md.
#    ze is substituted as an EXACT rational before the expression is compiled
#    (sympy.lambdify -> mpmath, common subexpressions shared).
# ============================================================================
def _lamb(expr, args):
    return sp.lambdify(args, expr, modules="mpmath", cse=True)


def _mpq(q):
    """exact rational (string / Fraction / sympy Rational) -> mpf at the
    current working precision"""
    q = q if isinstance(q, sp.Rational) else sp.Rational(str(q))
    return mp.mpf(int(q.p)) / int(q.q)


def twofold(expr, ze_val, dps=DPS_DEFAULT, md=MD_DEFAULT):
    """int_0^1 dzb int_0^zb dt  expr(t, zb; ze)  by nested tanh-sinh
    quadrature at working precision dps, maximum degree md; ze_val an exact
    rational.  Returns the mpmath value (complex-typed: the imaginary part is
    quadrature noise from the polylogarithms at the domain edge and is
    reported as such by --selftest)."""
    zeR = sp.Rational(str(ze_val))
    f = _lamb(expr.xreplace({ze: zeR}), (t, zb))
    with mp.workdps(dps):
        return mp.quad(
            lambda zbv: mp.mpf(0) if (zbv <= 0 or zbv >= 1) else mp.quad(
                lambda tv: mp.mpf(0) if (tv <= 0 or tv >= zbv) else f(tv, zbv),
                [0, zbv], maxdegree=md),
            [0, 1], maxdegree=md)


def onefold_t(expr, params, dps=DPS_DEFAULT, md=MD_DEFAULT):
    """int_0^zb dt  expr(t; params)  at numeric parameter values (exact
    rationals, or mpf for a computed pole position such as sPlus)."""
    names = list(params)
    f = _lamb(expr, (t,) + tuple(names))
    with mp.workdps(dps):
        vals = [v if isinstance(v, (mp.mpf, mp.mpc)) else _mpq(v)
                for v in (params[s] for s in names)]
        zbv = vals[names.index(zb)]
        return mp.quad(lambda tv: mp.mpf(0) if (tv <= 0 or tv >= zbv)
                       else f(tv, *vals), [0, zbv], maxdegree=md)


def _digits(a, b):
    """agreement digits of the real parts (inf if equal); a reference given
    as a decimal string is parsed at 80 digits, never at the caller's
    working precision, so a comparison is never capped by the context"""
    with mp.workdps(80):
        a = mp.mpf(a) if isinstance(a, str) else mp.re(a)
        b = mp.mpf(b) if isinstance(b, str) else mp.re(b)
        if a == b:
            return mp.inf
        return -mp.log10(abs((a - b) / b))


def _fmt_d(d):
    return "inf" if d == mp.inf else "%.1f" % float(d)


def _cap(d, dps):
    """a digit certificate can never exceed the working precision"""
    return dps if (d == mp.inf or d > dps) else d


# ============================================================================
# 8. RECORDED REFERENCES FOR --selftest (transcribed from runs of the
#    evaluators of record that ship beside this file; nothing re-derived)
# ============================================================================
# P1hpl / P2hpl at four spot points (t, zb, ze): the evaluators of record --
# fk1-evaluate.py make_inner_K1 (P1 = K1 * D_K / a1), sha256
#   1ae3fc29efaf5979afc3f8f8e1029b2f297c6f47af4608060a39638e54e53408
# and fell-evaluate.py make_inner_P2, sha256
#   425b0115c558f67acc433d8b89b2fb11984f7075faf18a9327b81b50c49857dc
# -- run at 60 working digits; both files' second (derivative-layer)
# transcriptions of P1/P2 agreed bit-exactly at these points.  Real parts
# (the imaginary parts are <= 1e-63, evaluation noise).
P1P2_RECORD = [
    ("0.11", "0.6", "1/3",
     "2.153750299867828601671510356273362280751257351034318358838",
     "-8.190829488974792636523635733886787110908202860068131935006"),
    ("0.31", "0.44", "1/3",
     "2.798426541152284448601735791004091377160428010929637841545",
     "-6.890612161179021648598191514505713891318143402890151346639"),
    ("0.05", "0.3", "1/2",
     "3.828188634738097932124873385139837319263275934236886250854",
     "-6.391924308975284354434343987514948364329615760828583993488"),
    ("0.52", "0.83", "2/7",
     "1.071862159591714277105563255511526200282975595484011199963",
     "-6.748353121169324245205750873444226010274077003713690913691"),
]

# fell-evaluate.py (sha256 425b0115c558f67acc433d8b89b2fb11984f7075faf18a9327b81b50c49857dc) values as PRINTED by
#   python3 fell-evaluate.py --point P/Q --raw --workers 4
# at its default working depth (dps 14); run stamps raw13 rc=0 2026-09-03T14:14:21Z;raw12 rc=0 2026-09-03T14:19:54Z;.
# Each entry = (the value printed with --raw, the digits that run gated).
# The evaluator's default output prints 10 certified digits of each.
EVALUATOR_RUN = {
    "1/3": {
        "fk1": ("-1.17464968056080170067176", 21.5),
        "fr2": ("0.087671112274538580151787932", 16.5),
        "fred1": ("1.5909194986349630828678245", 17.2),
        "fell": ("0.50394093034869996234785055", 16.6),
    },
    "1/2": {
        "fk1": ("-0.815755991400036738603081", 21.6),
        "fr2": ("0.11962440573525472025536146", 17.4),
        "fred1": ("1.2985865457881284026895923", 17.1),
        "fell": ("0.60245496012334638434187252", 16.8),
    },
}
EVALUATOR_PRINTED_DIGITS = 10

# the source of this port and the pinned companion data (comment-only pins;
# the fk1-evaluate.py line carries the same string as the header table)
SOURCE_M_SHA256 = "9ef9c315887f8928e324e4b7b7e895bc373f19dda6ef51d7240a99b3dcca01a8"
FK1_WORDS_SHA256 = "77dfc3a3b80e1774c79fa3144cf93726c43311e5b6ad3062d22d773eb893cc77"


# ============================================================================
# 9. --selftest
# ============================================================================
class _Report(object):
    def __init__(self):
        self.fails = []
        self.rows = []

    def check(self, label, ok, detail=""):
        print("[%s] %s%s" % ("PASS" if ok else "FAIL", label,
                             ("  " + detail) if detail else ""))
        if not ok:
            self.fails.append(label)
        return ok


def _rat_zero(expr):
    """exact zero test for a rational expression (possibly carrying sqrt(Q)
    or sqrt(disc2), whose squares sympy reduces automatically)"""
    d = sp.expand(sp.numer(sp.together(expr)))
    if d == 0:
        return True
    return sp.simplify(d) == 0


def identities(rep, verbose=True):
    """the exact identities the .m states in its comments, as sympy checks"""
    Q = QQuartic(zb, ze)
    M = Mpoly(zb, ze)
    zo = zOf(t, zb, ze)
    tp, tm = tPlus(zb, ze), tMinus(zb, ze)
    sp_, sm_ = sPlus(zb, ze), sMinus(zb, ze)
    ts_, t6 = Fred1TStar(zb, ze), Fred1T6(zb, ze)
    checks = [
        ("Q == M^2 + 4 ze^3 (1 - ze)", Q - (M**2 + 4*ze**3*(1 - ze))),
        ("Q(1 - zb) == Q(zb)", QQuartic(1 - zb, ze) - Q),
        ("M(1 - zb) == M(zb)", Mpoly(1 - zb, ze) - M),
        ("zb (1 - zb) == -M + ze (2 ze - 1)", zb*(1 - zb) - (-M + ze*(2*ze - 1))),
        ("zb^2 == M + zb - (2 ze^2 - ze)", zb**2 - (M + zb - (2*ze**2 - ze))),
        ("D_K == -ze (t - tPlus)(t - tMinus)   [Q = disc_t(D_K)]",
         DK(t, zb, ze) + ze*(t - tp)*(t - tm)),
        ("z(tPlus) == 1 - zb", zo.subs(t, tp) - (1 - zb)),
        ("z(tMinus) == 1 - zb", zo.subs(t, tm) - (1 - zb)),
        ("1/(t - tPlus) - 1/(t - tMinus) == -sqrt(Q)/D_K",
         1/(t - tp) - 1/(t - tm) + sqrt(Q)/DK(t, zb, ze)),
        ("PF split K1: outer*R1 == a1/D_K + red1kernel",
         outerPref(t, zb, ze)*R1rat(zo, zb) - (a1pref(zb, ze)/DK(t, zb, ze)
                                               + red1kernel(t, zb, ze))),
        ("PF split K2: outer*R2 == a2/D_K2 + red2exact",
         outerPref(t, zb, ze)*R2rat(zo, zb) - (a2pref(zb, ze)/DK2(t, zb, ze)
                                               + red2exact(t, zb, ze))),
        ("red1kernel == kappa/(t - tstar)",
         red1kernel(t, zb, ze) - Fred1Kappa(zb, ze)/(t - ts_)),
        ("-z == [ze/(1-ze)] t (1 - t/zb)/(1 - t/tstar)",
         -zo - ze/(1 - ze)*t*(1 - t/zb)/(1 - t/ts_)),
        ("1 - z == (1 - t)(1 - t/t6)/(1 - t/tstar)",
         1 - zo - (1 - t)*(1 - t/t6)/(1 - t/ts_)),
        ("z'/z == 1/t + 1/(t - zb) - 1/(t - tstar)",
         sp.diff(zo, t)/zo - (1/t + 1/(t - zb) - 1/(t - ts_))),
        ("disc2 == (zb - 1)^4 Q(zb/(zb - 1); ze)",
         disc2(zb, ze) - (zb - 1)**4*QQuartic(zb/(zb - 1), ze)),
        ("D_K2 == zb ze (t - sPlus)(t - sMinus)",
         DK2(t, zb, ze) - zb*ze*(t - sp_)*(t - sm_)),
        ("z(sPlus) == 1/zb", zo.subs(t, sp_) - 1/zb),
        ("z(sMinus) == 1/zb", zo.subs(t, sm_) - 1/zb),
        ("letter p == 1 (perfect-square discriminant)",
         (zb*ze - zb + ze + (zb*(1 - ze) + ze))/(2*ze) - 1),
        ("letter m == -zb (1 - ze)/ze",
         (zb*ze - zb + ze - (zb*(1 - ze) + ze))/(2*ze) + zb*(1 - ze)/ze),
        ("discriminant of the letter roots == (zb (1 - ze) + ze)^2",
         4*zb*ze*(1 - ze) + (zb*ze - zb + ze)**2 - (zb*(1 - ze) + ze)**2),
    ]
    for label, expr in checks:
        rep.check("identity: " + label, _rat_zero(expr))
    # data-layer structure
    rep.check("data: 12 block monomials, 12 block coefficients",
              len(FK1BlockMonos) == 12 and len(FK1BlockCoeffs(zb)) == 12)
    rep.check("data: 159 word rows, 112 phi rows, 112 one-fold rows",
              len(FK1Words) == 159 and len(Fred1PhiWords) == 112
              and len(Fred1OnefoldWords) == 112)
    nbad = sum(1 for (w, mu, rs0, r) in FK1Words
               if sp.expand(sp.sympify(r) - (ze - 1)*sp.sympify(rs0)) != 0)
    rep.check("data: r == (ze - 1) rS0 for all 159 word rows", nbad == 0,
              "%d bad" % nbad)
    rep.check("data: Fred1OnefoldWords == 'p' + Fred1PhiWords",
              all(a == "p" + b and ca == cb for (a, ca), (b, cb)
                  in zip(Fred1OnefoldWords, Fred1PhiWords)))
    # the 12-block decode of P1 (the certified PF collapse), exactly
    blocksum = sum(c * FK1BlockMono(z, j)
                   for j, c in enumerate(FK1BlockCoeffs(zb), start=1))
    d = sp.expand(blocksum - P1hpl(z, zb))
    rep.check("identity: Sum_j coeff_j(zb) mono_j(z) == P1hpl(z, zb)  [exact]",
              d == 0)
    return rep


def _load_sibling(fname):
    """import an evaluator of record from the directory of this file (their
    argument parsing lives in main(), so the import has no side effects)."""
    path = os.path.join(os.path.dirname(os.path.abspath(__file__)), fname)
    if not os.path.exists(path):
        return None
    try:
        spec = importlib.util.spec_from_file_location(
            fname.replace("-", "_").replace(".py", ""), path)
        m = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(m)
        return m
    except Exception as e:  # noqa: BLE001 - a broken sibling is reported, not fatal
        print("[info] %s present but not importable (%s)" % (fname, e))
        return None


def spot_gate(rep, bar=40):
    """P1hpl / P2hpl (and the block decode) at the four recorded spot points,
    60 working digits, against the values of record; plus the live
    evaluators of record when they sit beside this file."""
    fk1m = _load_sibling("fk1-evaluate.py")
    fellm = _load_sibling("fell-evaluate.py")
    with mp.workdps(60):
        f1 = _lamb(P1hpl(z, zb), (z, zb))
        f2 = _lamb(P2hpl(z, zb), (z, zb))
        fz = _lamb(zOf(t, zb, ze), (t, zb, ze))
        fb = _lamb(sum(c * FK1BlockMono(z, j) for j, c in
                       enumerate(FK1BlockCoeffs(zb), start=1)), (z, zb))
        w1 = w2 = wb = wl1 = wl2 = mp.inf
        for (ts_, zbs, zes, p1s, p2s) in P1P2_RECORD:
            tv, zbv, zev = mp.mpf(ts_), mp.mpf(zbs), _mpq(zes)
            zv = fz(tv, zbv, zev)
            p1, p2 = f1(zv, zbv), f2(zv, zbv)
            w1 = min(w1, _digits(p1, p1s))
            w2 = min(w2, _digits(p2, p2s))
            wb = min(wb, _digits(fb(zv, zbv), p1))
            if fk1m is not None:
                k1 = fk1m.make_inner_K1(zbv, zev)(tv)
                p1live = k1 * fk1m.D_K(tv, zbv, zev) / (-zbv*(zbv - 1)*(zev - 1))
                wl1 = min(wl1, _digits(p1, p1live))
            if fellm is not None:
                wl2 = min(wl2, _digits(p2, fellm.make_inner_P2(zbv, zev)(tv)[1]))
    rep.rows.append(("P1hpl vs record (4 spots)", w1, bar))
    rep.rows.append(("P2hpl vs record (4 spots)", w2, bar))
    rep.check("P1hpl == record P1 at 4 spot points", w1 >= bar,
              "worst %s d (bar %d)" % (_fmt_d(w1), bar))
    rep.check("P2hpl == record P2 at 4 spot points", w2 >= bar,
              "worst %s d (bar %d)" % (_fmt_d(w2), bar))
    rep.check("12-block decode == P1hpl at 4 spot points (numeric)", wb >= bar,
              "worst %s d (bar %d)" % (_fmt_d(wb), bar))
    if fk1m is not None:
        rep.rows.append(("P1hpl vs LIVE fk1-evaluate.py", wl1, bar))
        rep.check("P1hpl == LIVE fk1-evaluate.py make_inner_K1 (4 spots)",
                  wl1 >= bar, "worst %s d" % _fmt_d(wl1))
    else:
        print("[skip] fk1-evaluate.py not beside this file: live P1 leg skipped")
    if fellm is not None:
        rep.rows.append(("P2hpl vs LIVE fell-evaluate.py", wl2, bar))
        rep.check("P2hpl == LIVE fell-evaluate.py make_inner_P2 (4 spots)",
                  wl2 >= bar, "worst %s d" % _fmt_d(wl2))
    else:
        print("[skip] fell-evaluate.py not beside this file: live P2 leg skipped")
    return fk1m, fellm


def data_gate(rep, fellm):
    """the 159 word rows against the pinned fk1_words.json and the 112 phi
    rows against fell-evaluate.py's vendored table, when present"""
    here = os.path.dirname(os.path.abspath(__file__))
    wpath = os.path.join(here, "fk1_words.json")
    if os.path.exists(wpath):
        sha = hashlib.sha256(open(wpath, "rb").read()).hexdigest()
        if rep.check("fk1_words.json sha256 == pinned %s..." % FK1_WORDS_SHA256[:16],
                     sha == FK1_WORDS_SHA256, "" if sha == FK1_WORDS_SHA256
                     else "found %s" % sha[:16]):
            wj = json.load(open(wpath))["words"]
            loc = {"c0": c0, "ze": ze, "lz": lz, "l1": l1, "La2": La2,
                   "La3": La3, "Si12": Si12}
            nbad = 0
            for (w, mu, rs0, r), row in zip(FK1Words, wj):
                ok = (w == row["w"]) and row["poly"] == "zb*(1-zb)"
                ok &= sp.expand(sp.sympify(mu) - sp.sympify(
                    row["mu"].replace("^", "**"), locals=loc)) == 0
                ok &= sp.expand(sp.sympify(rs0) - sp.sympify(row["r_S0"], locals=loc)) == 0
                ok &= sp.expand(sp.sympify(r) - sp.sympify(row["r"], locals=loc)) == 0
                nbad += 0 if ok else 1
            rep.check("159 word rows == fk1_words.json (w, mu, rS0, r; measure zb(1-zb))",
                      nbad == 0 and len(wj) == 159, "%d bad" % nbad)
    else:
        print("[skip] fk1_words.json not beside this file: word-row leg skipped")
    if fellm is not None and hasattr(fellm, "RED1_PHI_JSON"):
        phi = json.loads(fellm.RED1_PHI_JSON)
        loc = {k: v for k, v in zip(("C0", "CB1", "CB2", "CL2", "CL3", "CS12"),
                                    (C0, CB1, CB2, CL2, CL3, CS12))}
        mine = {w: sp.sympify(c) for (w, c) in Fred1PhiWords}
        nbad = sum(1 for w in mine if w not in phi or sp.expand(
            mine[w] - sp.sympify(phi[w], locals=loc)) != 0)
        rep.check("112 phi rows == fell-evaluate.py vendored table (as a word->coefficient map)",
                  nbad == 0 and set(mine) == set(phi), "%d bad" % nbad)
    else:
        print("[skip] fell-evaluate.py not beside this file: phi-row leg skipped")


def value_gate(rep, dps, md, points=("1/3", "1/2"), verbose=True):
    """FK1, FR2, Fred1, FEll by two-fold quadrature at (dps, md) and (dps,
    md-1) -- the two-degree agreement is this file's own digit certificate --
    against the values a run of fell-evaluate.py printed, plus FEllDirect at
    the first point.  Bar per value: min(the evaluator run's gated digits,
    this file's certificate) - 2, and never below the evaluator's 10
    printed certified digits."""
    fns = (("fk1", "F^K1", FK1), ("fr2", "F^R2", FR2), ("fred1", "F^red1", Fred1))
    imbar = mp.mpf(10) ** (-(dps // 2))
    print("\n== values: this file (dps %d, md %d; certificate = agreement with md %d) "
          "vs fell-evaluate.py run ==" % (dps, md, md - 1))
    print("  %-7s %-5s %-26s %-8s %-9s %-8s %-4s" % ("piece", "ze", "value (10 d)", "own d",
                                                     "evalr d", "agree d", "bar"))
    for key in points:
        tot_hi, tot_lo = mp.mpc(0), mp.mpc(0)
        for piece, label, fn in fns:
            t0 = time.time()
            v_hi = fn(key, dps, md)
            v_lo = fn(key, dps, md - 1)
            wall = time.time() - t0
            tot_hi += v_hi
            tot_lo += v_lo
            _value_row(rep, key, piece, label, v_hi, v_lo, wall, imbar, dps)
        _value_row(rep, key, "fell", "F^ell", tot_hi, tot_lo, 0.0, imbar, dps, is_sum=True)
    # the definitional route at the first point
    key = points[0]
    t0 = time.time()
    d_hi = FEllDirect(key, dps, md)
    d_lo = FEllDirect(key, dps, md - 1)
    wall = time.time() - t0
    own = _cap(_digits(d_hi, d_lo), dps)
    agree = _digits(d_hi, EVALUATOR_RUN[key]["fell"][0])
    bar = max(EVALUATOR_PRINTED_DIGITS,
              min(int(mp.floor(EVALUATOR_RUN[key]["fell"][1])), int(mp.floor(own))) - 2)
    rep.rows.append(("FEllDirect(%s) vs fell-evaluate.py" % key, agree, bar))
    rep.check("FEllDirect(%s) (definitional eq.(19) two-fold) == fell-evaluate.py F^ell"
              % key, agree >= bar,
              "%s d (own certificate %s d; bar %d; %.0f s)"
              % (_fmt_d(agree), _fmt_d(own), bar, wall))


def _value_row(rep, key, piece, label, v_hi, v_lo, wall, imbar, dps, is_sum=False):
    own = _cap(_digits(v_hi, v_lo), dps)
    ev_val, ev_d = EVALUATOR_RUN[key][piece]
    agree = _digits(v_hi, ev_val)
    bar = max(EVALUATOR_PRINTED_DIGITS, min(int(mp.floor(ev_d)), int(mp.floor(own))) - 2)
    im = abs(mp.im(v_hi))
    with mp.workdps(dps + 5):
        shown = mp.nstr(mp.re(v_hi), 10)
    print("  %-7s %-5s %-26s %-8s %-9s %-8s %-4d %s" % (
        piece, key, shown, _fmt_d(own), "%.1f" % ev_d, _fmt_d(agree), bar,
        "" if is_sum else "(%.0f s)" % wall))
    rep.rows.append(("%s(%s) vs fell-evaluate.py" % (label, key), agree, bar))
    rep.check("%s(%s) agrees with the fell-evaluate.py run to >= %d d" % (label, key, bar),
              agree >= bar, "%s d (evaluator gated %.1f d, own certificate %s d)"
              % (_fmt_d(agree), ev_d, _fmt_d(own)))
    rep.check("%s(%s) imaginary part is quadrature noise (< 1e-%d)" % (label, key, dps // 2),
              im < imbar, "|Im| = %s" % mp.nstr(im, 3))


def apply_mutation():
    """--mutate control: dent P1hpl by 1e-12.  Every gate that touches P1
    (the exact block decode, the spot values, the F^K1 / F^red1 / F^ell
    values) must then FAIL; the untouched identities still pass."""
    global P1hpl
    orig = P1hpl

    def P1hpl(z, zb):
        return orig(z, zb) + Rational(1, 10**12)
    globals()["P1hpl"] = P1hpl
    print("[mutate] P1hpl dented by +1e-12 (control: the selftest must FAIL)")


def selftest(dps, md, mutate=False, values=True):
    T0 = time.time()
    rep = _Report()
    print("fell_symbolic.py --selftest  (source .m sha256 %s...; dps %d, md %d)"
          % (SOURCE_M_SHA256[:16], dps, md))
    if mutate:
        apply_mutation()
    t0 = time.time()
    identities(rep)
    print("[time] symbolic identities %.1f s" % (time.time() - t0))
    t0 = time.time()
    fk1m, fellm = spot_gate(rep)
    data_gate(rep, fellm)
    print("[time] spot + data gates %.1f s" % (time.time() - t0))
    if values:
        t0 = time.time()
        value_gate(rep, dps, md)
        print("[time] value gates %.1f s" % (time.time() - t0))
    print("\n== digit table ==")
    for label, d, bar in rep.rows:
        print("  %-44s %8s d   (bar %d)  %s" % (label, _fmt_d(d), bar,
                                               "PASS" if d >= bar else "FAIL"))
    print("\nVERDICT: %s   (%.1f s)" % ("ALL PASS" if not rep.fails else
                                       "FAILURES: %s" % rep.fails, time.time() - T0))
    return 0 if not rep.fails else 1


def evaluate_point(key, dps, md, raw=False):
    """FK1, FR2, Fred1, FEll at ze = key with the two-degree certificate"""
    key = "%d/%d" % (sp.Rational(str(key)).p, sp.Rational(str(key)).q)
    print("F^ell pieces at ze = %s  (dps %d; value at md %d, certificate = agreement with md %d)"
          % (key, dps, md, md - 1))
    tot_hi, tot_lo = mp.mpc(0), mp.mpc(0)
    for piece, label, fn in (("fk1", "F^K1", FK1), ("fr2", "F^R2", FR2),
                             ("fred1", "F^red1", Fred1), ("fell", "F^ell", None)):
        t0 = time.time()
        if fn is not None:
            v_hi, v_lo = fn(key, dps, md), fn(key, dps, md - 1)
            tot_hi += v_hi
            tot_lo += v_lo
        else:
            v_hi, v_lo = tot_hi, tot_lo
        own = _cap(_digits(v_hi, v_lo), dps)
        n = dps if raw else max(1, min(10, int(own)))
        with mp.workdps(dps + 5):
            s = mp.nstr(mp.re(v_hi), n)
        extra = ""
        if key in EVALUATOR_RUN:
            ev_val, ev_d = EVALUATOR_RUN[key][piece]
            extra = "; agrees with the fell-evaluate.py run to %s d (its gate %.1f d)" % (
                _fmt_d(_digits(v_hi, ev_val)), ev_d)
        print("    %-7s = %s   (%d digits shown; own certificate %s d%s%s)"
              % (label, s, n, _fmt_d(own), extra,
                 "" if fn is None else "; %.0f s" % (time.time() - t0)))
    return 0


def main(argv=None):
    ap = argparse.ArgumentParser(
        description="F^ell(ze), the elliptic part of the two-point NNLO N=4 "
                    "EEC, in open-source symbolic form (sympy) with an mpmath "
                    "numeric layer.  Default action: --selftest.")
    ap.add_argument("--selftest", action="store_true",
                    help="exact identities + spot gates + value gates vs the "
                         "fell-evaluate.py run (default action)")
    ap.add_argument("--mutate", action="store_true",
                    help="control: dent P1hpl by 1e-12; the selftest must FAIL")
    ap.add_argument("--identities", action="store_true",
                    help="symbolic layer only (identities + spot + data gates)")
    ap.add_argument("--point", metavar="P/Q",
                    help="evaluate FK1, FR2, Fred1, FEll at this exact rational ze")
    ap.add_argument("--dps", type=int, default=DPS_DEFAULT,
                    help="working digits of the numeric layer (default %d)" % DPS_DEFAULT)
    ap.add_argument("--md", type=int, default=MD_DEFAULT,
                    help="tanh-sinh maximum degree (default %d; the certificate "
                         "uses md-1)" % MD_DEFAULT)
    ap.add_argument("--raw", action="store_true",
                    help="--point: print the full working precision")
    args = ap.parse_args(argv)
    try:
        sys.stdout.reconfigure(line_buffering=True)
    except Exception:  # noqa: BLE001
        pass
    if args.md < 2 or args.dps < 10:
        print("--md must be >= 2 and --dps >= 10")
        return 2
    if args.point:
        return evaluate_point(args.point, args.dps, args.md, args.raw)
    if args.identities:
        return selftest(args.dps, args.md, mutate=args.mutate, values=False)
    return selftest(args.dps, args.md, mutate=args.mutate, values=True)


if __name__ == "__main__":
    sys.exit(main())
