"""v631 -- PRIME.WEIL.DICT.01: the W1 dictionary round -- the v630 non-scalar
residual IS the zeta pole term, and after separating it the smooth
conversion is the SCALAR -4D: the smooth half of the W1 identification
closes at the measured level, with the conversion DERIVED, not fitted.

CONVENTION ERRATUM (2026-08-02, corrected the same day; machine
evidence: v643_w1_theorem.py C0.1):
  This chain read Suzuki's eq. (1.3) with Lerch coefficient -1; the
  paper's own Sec. 2.2 data (the printed origin constant
  A = (1/2)(log 2 pi - psi(2)), g(0) = 0, r_1'' = rho - 1/(2|t|)) LOCK
  the coefficient to +1/4.  Everything below is therefore stated for
  the kernel gtil := g - (5/4) e^{-|t|/2} Phi(e^{-2|t|}, 2, 1/4) (the
  module's g_vec), whose identities are all CORRECT identities of
  gtil.  The exact transfer identity cgal_sm(gtil) = -4 cgal_sm(g)
  makes every measured ratio transfer verbatim,
  ratio(gtil)/(-4D) == ratio(g)/(+D) number for number, so NO check
  and NO number below changes.  Only the labels do: Suzuki's own
  smooth layer is +rho (not -4 rho), the true dictionary is the ONE
  scalar +1/D on BOTH layers (sign-compatible with positivity -- the
  TFPT window form and Suzuki's Q_W^a sit on the same side), and the
  origin constant vanishes, kappa = 0 exactly (v643): the v563
  near-cell scheme IS Suzuki's origin bookkeeping.  Where the text
  below says "Suzuki's smooth layer = -4 x arch density", read: "in
  the gtil-normalization used by this chain"; see v643 for the
  measure-level theorem on the true g.

The chain (Suzuki arXiv:2606.09096, eq. 1.3; gtil-normalization per
the erratum above):

  (D1) THE LERCH COLLAPSE [E, exact]: term-wise
       (2n + 1/2)^2 / (n + 1/4)^2 = 4 EXACTLY, so
       d^2/dt^2 [e^{-t/2} Phi(e^{-2t}, 2, 1/4)]
         = 4 sum_n e^{-(2n+1/2)t} = 4 e^{-t/2}/(1 - e^{-2t})
       -- the Hurwitz-Lerch block of the screw function is a
       GEOMETRIC series in disguise (sympy-symbolic + 30-digit
       certificates).

  (D2) THE STRUCTURE THEOREM OF THE SMOOTH LAYER [E]: hence for t > 0
       off the atoms,
         gtil''(t) = -2 cosh(t/2) - 4 e^{-t/2}/(1 - e^{-2t}),
       i.e. the smooth layer OF THE CHAIN'S gtil-KERNEL = -4 x (THE
       TFPT ARCH DENSITY) MINUS THE POLE BLOCK 2 cosh(t/2) = e^{t/2}
       + e^{-t/2} -- the zeta pole weights at s = 0, 1, which TFPT
       tracks SEPARATELY as the exact rank-one pole piece (v591).
       (Erratum: Suzuki's OWN eq. (1.3) carries +1/4, so his smooth
       layer is +rho; see the erratum block and v643.)

  (D3) THE CLOSED RATIO LAW [E-float]: per lag,
         c_gal(d)/c_arch(d) = -D [4 + (e^t + 1)(1 - e^{-2t})] + O(disc),
       verified on the declared window (h = 184, lags 4..9, < 0.5%);
       the v630 drift profile is this closed formula, not a mystery.

  (D4) THE CLOSURE [E-float, central]: with the pole block removed
       from g, the ratio c_galtilde(d) / (-4D c_arch(d)) converges
       monotonically to 1 (residual ~ d^{-2}: the declared
       discretization order): the smooth half of W1 is a SCALAR
       dictionary after pole separation --
       A_TFPT-arch = -(1/4D) x Galerkin-smooth + pole rank-one.

  (D5) THE ATOM LAYER CONSTANT [E-float]: the per-atom Galerkin mass
       against the TFPT tent mass is ONE constant across different
       atoms (log 2 and log 3 agree to < 1e-3); the lag-level layer
       constants differ between smooth and atomic parts exactly by the
       DECLARED per-layer conventions of v563 (atom lags are tent
       HEIGHTS, arch lags are tent MASSES = D x density) -- at the
       measure level there is one dictionary; both lattice constants
       measured and frozen.

  (D6) THE TYPED REMAINDER [C]: for theorem-level W1 there remain the
       second-order discretization term, the d <= 2 boundary cells
       (the delta_0 conventions gamma + log pi vs psi(1/4) -- one
       Gauss/duplication identity), and the L^2_0 projection; the
       fold factor 4 = (mirror fold) x (PV fold) is typed.  Contract
       PRIME.WEIL.OPERATOR.01: W1 smooth half now DERIVED-measured
       (was: open residual), atomic half literal (v630).  No
       positivity claim, no RH statement.

Verdict enums (frozen): W1-DICTIONARY-DERIVED (all pass),
DICTIONARY-FAILS, MIXED.

FIREWALL: no marker changes; no positivity claim, no RH statement.

PROVENANCE: discovery probe w1_dictionary_probe.py (2026-08-02, 7/7,
verdict W1-DICTIONARY-DERIVED; Suzuki arXiv:2606.09096 eq. 1.3).

Machinery: v563 read-only; sympy exact for D1/D2; the Galerkin double
integrals use a vectorized series form of the screw function (route
certified against mpmath lerchphi at 30 digits).  Python-only, counted
per GATE.WOLFRAM.02.
"""
import math
import os
import sys
import time

import numpy as np

_here = os.path.dirname(os.path.abspath(__file__))
for _cand in (_here, os.path.join(_here, "..", "..", "verification")):
    if os.path.exists(os.path.join(_cand, "v563_paper2_readouts.py")):
        sys.path.insert(0, os.path.abspath(_cand))
        break

T0 = time.time()
FAILS = []
N_CHK = 0


def check(name, ok, detail=""):
    global N_CHK
    N_CHK += 1
    if not ok:
        FAILS.append(name.split()[0])
    print("[%s] %s%s" % ("PASS" if ok else "FAIL", name,
                         (": " + detail) if detail else ""))


import v563_paper2_readouts as core  # noqa: E402  (READ-ONLY import)
import sympy as sp  # noqa: E402
import mpmath as mp  # noqa: E402

N_LERCH = 20000
_NN = np.arange(N_LERCH, dtype=float)
_WTS = 1.0 / (_NN + 0.25) ** 2


def run():
    global N_CHK, FAILS
    N_CHK = 0
    FAILS = []
    print("=" * 78)
    print("PRIME.WEIL.DICT -- the W1 dictionary round")
    print("=" * 78)

    # ---------------------------------------------------------------- D1
    n = sp.symbols("n", integer=True, nonnegative=True)
    ratio = (2 * n + sp.Rational(1, 2)) ** 2 / (n + sp.Rational(1, 4)) ** 2
    ok_term = sp.simplify(ratio - 4) == 0
    mp.mp.dps = 35
    ok_num = True
    for tv in (mp.mpf(3) / 10, mp.mpf(1), mp.mpf(2)):
        f = lambda t: mp.e ** (-t / 2) * mp.lerchphi(mp.e ** (-2 * t), 2,
                                                     mp.mpf(1) / 4)
        d2 = mp.diff(f, tv, 2)
        closed = 4 * mp.e ** (-tv / 2) / (1 - mp.e ** (-2 * tv))
        if abs(d2 - closed) / abs(closed) > mp.mpf(10) ** -25:
            ok_num = False
    check("D1.1 [E] the Lerch collapse: (2n+1/2)^2/(n+1/4)^2 = 4 exactly, "
          "so [e^{-t/2} Phi(e^{-2t},2,1/4)]'' = 4 e^{-t/2}/(1-e^{-2t}) "
          "(sympy-symbolic; 30-digit certificates at t = 0.3, 1, 2)",
          ok_term and ok_num)

    # ---------------------------------------------------------------- D2
    t = sp.symbols("t", positive=True)
    g_pole = -4 * (sp.exp(t / 2) + sp.exp(-t / 2) - 2)
    lerch_dd = 4 * sp.exp(-t / 2) / (1 - sp.exp(-2 * t))
    g_dd = sp.diff(g_pole, t, 2) - lerch_dd
    target = -2 * sp.cosh(t / 2) - 4 * sp.exp(-t / 2) / (1 - sp.exp(-2 * t))
    check("D2.1 [E] the structure theorem (gtil-normalization, see "
          "erratum): gtil''(t) = -2 cosh(t/2) - 4 e^{-t/2}/(1-e^{-2t}) "
          "off the atoms -- the chain-kernel smooth layer = -4 x (the "
          "TFPT arch density e^{-t/2}/(1-e^{-2t})) MINUS the pole block "
          "2 cosh(t/2) = e^{t/2}+e^{-t/2} (the zeta pole weights at "
          "s = 0, 1; TFPT's separate v591 rank-one); Suzuki's own "
          "(1.3) carries +1/4, smooth layer +rho (v643 C0.1)",
          sp.simplify(g_dd - target) == 0)

    # ------------------------------------------------ window and kernels
    kz = core.frame_a_zones()[0]
    r = core.build_window(kz)
    alpha, Mz = r["alpha"], r["M"]
    ka = core.atoms_in(alpha)
    uu, mm = core.U_ALL[:ka].copy(), core.MU_ALL[:ka].copy()
    c_at, D = core.atom_lags_at(alpha, Mz, uu, mm)
    c_ar = core.arch_lags(Mz, D)

    UU = np.array([float(u) for u in core.U_ALL])
    MU = np.array([float(m) for m in core.MU_ALL])
    CW = np.cumsum(MU / 2.0)
    CS = np.cumsum(MU / 2.0 * UU)
    mp.mp.dps = 30
    PSI14 = float(mp.digamma(mp.mpf(1) / 4))
    LOGPI = math.log(math.pi)
    PHI1 = float(mp.lerchphi(1, 2, mp.mpf(1) / 4))

    def g_vec(ts, pole=True, prime_only=False):
        """vectorized screw function on |t|; Lerch via 20000-term series
        (smooth truncation error killed by the second differences)."""
        x = np.abs(np.asarray(ts, dtype=float))
        k = np.searchsorted(UU, x, side="right")
        cw = np.where(k > 0, CW[np.maximum(k - 1, 0)], 0.0)
        cs = np.where(k > 0, CS[np.maximum(k - 1, 0)], 0.0)
        prime = x * cw - cs
        if prime_only:
            return prime
        out = prime - x / 2.0 * (PSI14 - LOGPI) - 0.25 * PHI1
        if pole:
            out += -4.0 * (np.exp(x / 2) + np.exp(-x / 2) - 2.0)
        # Lerch block, chunked outer product
        lb = np.empty_like(x)
        for a in range(0, x.size, 400):
            b = min(x.size, a + 400)
            E = np.exp(-np.outer(2.0 * x[a:b] + 0.0, _NN)
                       - 0.5 * x[a:b, None])
            lb[a:b] = E @ _WTS
        return out - lb

    from numpy.polynomial.legendre import leggauss
    GX, GW = leggauss(16)
    XS = 0.5 * D * (GX + 1)
    WS = 0.5 * D * GW
    DIFF = (XS[:, None] - XS[None, :]).ravel()
    W2 = np.outer(WS, WS).ravel()

    def II(k, **kw):
        return float(np.dot(W2, g_vec(k * D + DIFF, **kw)))

    def cgal(d, **kw):
        return (2.0 * II(d, **kw) - II(d - 1, **kw)
                - II(d + 1, **kw)) / (D * D)

    # route certificate: series g vs mpmath lerchphi g at 5 points
    ok_route = True
    for tv in (0.01, 0.05, 0.2, 0.7, 1.5):
        exact = (-4.0 * (math.exp(tv / 2) + math.exp(-tv / 2) - 2.0)
                 - tv / 2.0 * (PSI14 - LOGPI) - 0.25 * PHI1
                 - math.exp(-tv / 2.0) * float(
                     mp.lerchphi(math.exp(-2.0 * tv), 2, mp.mpf(1) / 4)))
        kk = np.searchsorted(UU, tv, side="right")
        if kk > 0:
            exact += tv * CW[kk - 1] - CS[kk - 1]
        fast = float(g_vec(np.array([tv]))[0])
        if abs(fast - exact) > 2e-4:
            ok_route = False
    check("D2.2 [E-float] route certificate: the vectorized series form "
          "of the screw function matches the mpmath lerchphi evaluation "
          "at 5 points (< 2e-4 absolute; the smooth truncation error "
          "cancels in the second differences)", ok_route)

    # ---------------------------------------------------------------- D3
    devs = []
    for d in range(4, 10):
        tv = d * D
        cg = cgal(d)
        pred = -D * (4.0 + (math.exp(tv) + 1.0) * (1.0 - math.exp(-2.0 * tv)))
        devs.append(abs(cg / c_ar[d] - pred) / abs(pred))
    ok_law = max(devs) < 1.2e-2 and all(
        devs[i + 1] < devs[i] for i in range(len(devs) - 1))
    check("D3.1 [E-float] the closed ratio law c_gal/c_arch = "
          "-D [4 + (e^t+1)(1-e^{-2t})] holds per lag (d = 4..9, worst "
          "%.2e < 1.2e-2, deviations monotonically shrinking -- the "
          "declared d^-2 discretization profile of D4): the v630 drift "
          "IS this closed formula" % max(devs), ok_law)

    # ---------------------------------------------------------------- D4
    vals = []
    for d in (3, 5, 7, 9, 12, 16):
        vals.append(cgal(d, pole=False) / (-4.0 * D * c_ar[d]))
    diffs = [abs(v - 1.0) for v in vals]
    mono = all(diffs[i + 1] < diffs[i] for i in range(len(diffs) - 1))
    check("D4.1 [E-float, central] pole-subtracted, the conversion is the "
          "SCALAR -4D in the gtil-normalization: ratio/(-4D) = %s -> 1 "
          "monotonically (last %.4f; residual ~ d^-2 discretization): "
          "the smooth half of W1 closes as A_arch = -(1/4D) "
          "Galerkin-smooth(gtil) + pole rank-one == +(1/D) "
          "Galerkin-smooth(g) via the exact transfer identity "
          "cgal_sm(gtil) = -4 cgal_sm(g) (erratum; v643 P2.4)"
          % (["%.4f" % v for v in vals], vals[-1]),
          mono and diffs[-1] < 3e-3)

    # ---------------------------------------------------------------- D5
    def atom_masses(j):
        u_j, mu_j = float(uu[j]), float(mm[j])
        d0 = int(round(u_j / D))
        m_gal = sum(cgal(d, prime_only=True)
                    for d in range(d0 - 3, d0 + 4)) * D
        c1, _ = core.atom_lags_at(alpha, Mz, uu[j:j + 1], mm[j:j + 1])
        m_tfpt = float(np.sum(c1))
        return m_gal, m_tfpt

    g1, t1 = atom_masses(0)   # log 2
    g2, t2 = atom_masses(1)   # log 3
    r1, r2 = g1 / t1, g2 / t2
    same = abs(r1 - r2) / abs(r1) < 1e-3
    is_D2 = abs(r1 / (D * D) - 1.0) < 1e-3
    check("D5.1 [E-float] the atom layer constant IS D^2: per-atom "
          "Galerkin mass / TFPT tent mass = %.6e = D^2 (%.4f x D^2; "
          "log 2 and log 3 agree < 1e-3) -- the lag-level smooth (-4D) "
          "vs atomic (D^2) constants differ exactly by the declared "
          "v563 per-layer conventions (atom lags are tent HEIGHTS with "
          "the +-u fold in MU, arch lags are tent MASSES = D x PV-folded "
          "density): one dictionary at the measure level, two lattice "
          "constants, both now closed forms in D"
          % (r1, r1 / (D * D)), same and is_D2,
          "masses: gal(%.4e, %.4e) tfpt(%.4e, %.4e)" % (g1, g2, t1, t2))

    # ---------------------------------------------------------------- D6
    check("D6.1 [C] typed remainder for theorem-level W1: the "
          "second-order discretization term, the d <= 2 boundary cells "
          "(delta_0 conventions: gamma + log pi vs psi(1/4) -- one "
          "duplication identity), the L^2_0 projection; fold factor 4 = "
          "mirror x PV fold.  Contract status: W1 smooth half "
          "DERIVED-measured (was open residual, v630), atomic half "
          "literal; no positivity claim, no RH statement", True)

    VERDICT = "W1-DICTIONARY-DERIVED" if not FAILS else "MIXED"
    print("\nVERDICT: %s -- the v630 residual IS the pole term; after "
          "separation the smooth conversion is the scalar -4D in the "
          "gtil-normalization (erratum: = +1/D on Suzuki's true g, "
          "one scalar for both layers; v643)" % VERDICT)
    print("checks: %d, failures: %d %s" % (N_CHK, len(FAILS), FAILS or ""))
    print("elapsed: %.1f s" % (time.time() - T0))
    return len(FAILS)


if __name__ == "__main__":
    raise SystemExit(1 if run() else 0)
