# Copyright Léon Fernando Vlegels -- CC BY 4.0
"""
verify_P325.py
Verifier for Addendum 325: OI-287-1 (corrected) probe P4 -- the level<->character
offset. A323/A324 found the mechanism (a Z_3 character selection rule) and named
a uniform +1 twist between two SEPARATE corpus statements:

  (P20, line 201)  e, mu, tau  <->  characters 0, 1, 2  (T_cycle eigenvalues 1, omega, omega^2).
  (A287, lines 60-64)  e <-> edge(deg1,c=2,anchor), mu <-> boundary(deg2,c=3),
                       tau <-> bulk(deg3,c=16).

Combined with the grading char = degree mod 3 (deg1->char1, deg2->char2, deg3->char0):
A287's layer assignment implies e<->char1, mu<->char2, tau<->char0. P20 says
e<->char0, mu<->char1, tau<->char2. Every family is off by a uniform +1:
a family with P20-character k draws the coefficient whose degree carries character k+1.

P4's question: is the +1 twist DERIVABLE/FORCED, or a genuine open reindexing?

This verifier recomputes every claim from scratch (numpy/sympy only). It tests the
three candidate resolutions A (trivial-rep-is-anchor), B (Dirichlet-zero ground
shift), C (one unit of omega in the coupling), and the decisive convention scan:
does ANY single corpus-consistent choice of (generator orientation, root of unity,
ground-degree convention) make P20's (e,mu,tau)=(char0,1,2) and A287's layer
assignment (edge,boundary,bulk = degrees 1,2,3) COINCIDE on which coefficient each
family draws?

NO wavefunctions, NO overlap magnitudes. The withdrawn overlap-diagonal (A321) is
not revived. Representation theory / corpus identities only.
"""
import math, cmath, sys
from itertools import product

PI = math.pi
PASS = 0
FAIL = 0


def check(n, desc, cond):
    global PASS, FAIL
    tag = "[PASS]" if cond else "[FAIL]"
    if cond:
        PASS += 1
    else:
        FAIL += 1
    print(f"  {tag} ({n}) {desc}")


# ===========================================================================
print("=" * 70)
print("A325 / P4  --  the level<->character offset for OI-287-1 (corrected)")
print("=" * 70)

# ---------------------------------------------------------------------------
print("\n-- Section 1: the two corpus labellings, recomputed from scratch --")

# rho(x) = 16 pi^3 x^3 + 3 pi^2 x^2 + 2 pi x ; c_d = rho^(d)(0)/(pi^d d!)
deriv0 = {1: 2 * PI, 2: 6 * PI ** 2, 3: 96 * PI ** 3}
c = {d: deriv0[d] / (PI ** d * math.factorial(d)) for d in (1, 2, 3)}  # (2,3,16)
check(1, f"c_d = rho^(d)(0)/(pi^d d!) = (2,3,16)  [got ({c[1]:.0f},{c[2]:.0f},{c[3]:.0f})]",
      abs(c[1] - 2) < 1e-9 and abs(c[2] - 3) < 1e-9 and abs(c[3] - 16) < 1e-9)

# rho has NO degree-0 (constant) term: rho(0) = 0 (Dirichlet/edge condition).
rho0 = 16 * PI ** 3 * 0 ** 3 + 3 * PI ** 2 * 0 ** 2 + 2 * PI * 0
check(2, f"rho(0) = 0 exactly: NO degree-0 constant term (Dirichlet/edge condition)  "
         f"[got {rho0:.0e}]",
      abs(rho0) < 1e-15)

# Layer <-> degree <-> coefficient dictionary (from ALPHA_INV = 4pi^3 + pi^2 + pi
# and kernel/math/quat_s3.py FRAC_BULK/BOUNDARY/EDGE; the three terms map
# bulk<->4pi^3<->deg3, boundary<->pi^2<->deg2, edge<->pi<->deg1).
layer_deg = {"edge": 1, "boundary": 2, "bulk": 3}
layer_coeff = {"edge": c[1], "boundary": c[2], "bulk": c[3]}  # 2, 3, 16

# P20 labelling (line 201): e,mu,tau carry T_cycle eigenvalues 1,omega,omega^2,
# i.e. characters 0,1,2.
P20_char = {"e": 0, "mu": 1, "tau": 2}
check(3, "P20 (line 201): (e,mu,tau) carry characters (0,1,2)  [T_cycle eigenvalues 1,omega,omega^2]",
      [P20_char[f] for f in ("e", "mu", "tau")] == [0, 1, 2])

# A287 layer assignment (lines 60-64): e<->edge, mu<->boundary, tau<->bulk.
A287_layer = {"e": "edge", "mu": "boundary", "tau": "bulk"}
A287_deg = {f: layer_deg[A287_layer[f]] for f in ("e", "mu", "tau")}  # 1,2,3
A287_coeff = {f: layer_coeff[A287_layer[f]] for f in ("e", "mu", "tau")}  # 2,3,16
check(4, f"A287 (lines 60-64): (e,mu,tau) <-> layers (edge,boundary,bulk) = degrees (1,2,3) "
         f"= coeffs (2,3,16)  [deg {[A287_deg[f] for f in ('e','mu','tau')]}, "
         f"c {[int(round(A287_coeff[f])) for f in ('e','mu','tau')]}]",
      [A287_deg[f] for f in ("e", "mu", "tau")] == [1, 2, 3]
      and [int(round(A287_coeff[f])) for f in ("e", "mu", "tau")] == [2, 3, 16])

# ---------------------------------------------------------------------------
print("\n-- Section 2: the grading and the uniform +1 twist --")

# Grading (A323/A324, corpus action z->omega z so x^d -> omega^d x^d):
# degree d carries character d mod 3.
grade = lambda d: d % 3
# A287's degree for each family => the character that degree carries:
A287_char_via_grade = {f: grade(A287_deg[f]) for f in ("e", "mu", "tau")}
check(5, f"grading char = degree mod 3: A287 layers (deg 1,2,3) carry characters "
         f"(1,2,0)  [got {[A287_char_via_grade[f] for f in ('e','mu','tau')]}]",
      [A287_char_via_grade[f] for f in ("e", "mu", "tau")] == [1, 2, 0])

# The uniform twist: A287-implied character minus P20 character, per family.
twist = {f: (A287_char_via_grade[f] - P20_char[f]) % 3 for f in ("e", "mu", "tau")}
check(6, f"the twist (A287-char minus P20-char) is the SAME for every family: +1 (mod 3)  "
         f"[got {[twist[f] for f in ('e','mu','tau')]}]",
      all(twist[f] == 1 for f in ("e", "mu", "tau")))

# So the family with P20-character k draws the coefficient whose degree carries
# character k+1 (the n=k+1 offset, also stated A323/A324).
draws_char = {f: (P20_char[f] + 1) % 3 for f in ("e", "mu", "tau")}
deg_of_char = {grade(d): d for d in (1, 2, 3)}  # char->degree: {1:1,2:2,0:3}
draws_coeff = {f: c[deg_of_char[draws_char[f]]] for f in ("e", "mu", "tau")}
check(7, f"family of P20-char k draws coeff of degree carrying char k+1; reproduces A287 "
         f"(2,3,16)  [got {[int(round(draws_coeff[f])) for f in ('e','mu','tau')]}]",
      [int(round(draws_coeff[f])) for f in ("e", "mu", "tau")] == [2, 3, 16])

# ---------------------------------------------------------------------------
print("\n-- Section 3: Resolution A (trivial-rep-is-anchor) -- structural fact --")
# The trivial character (char 0) would be carried by a degree-0 (constant) term.
# rho has NO degree-0 term (Section 1, check 2). The lowest char-0 term reappears
# only at degree 3. So the char-0 family is the one with no degree-MATCHED term
# at the bottom of the ladder; it is the natural anchor.
# Test: among degrees actually present {1,2,3}, is char 0 the ONLY character whose
# *lowest* representative is not the minimal degree (i.e. it skips degree 0)?
present_degs = [1, 2, 3]
lowest_deg_for_char = {k: min(d for d in present_degs if grade(d) == k) for k in (0, 1, 2)}
# char 1 -> deg 1 (minimal), char 2 -> deg 2, char 0 -> deg 3 (skips deg 0).
check(8, f"char 0 (trivial rep) has no degree-0 term present; its lowest term is degree 3, "
         f"while char1->deg1, char2->deg2  [lowest {lowest_deg_for_char}]",
      lowest_deg_for_char == {0: 3, 1: 1, 2: 2})

# A287's anchor IS the char-0 family? P20 char 0 = e. A287 anchor = e (edge,
# absorbed by normalization). So "electron = char 0 = trivial rep = anchor".
electron_is_char0 = (P20_char["e"] == 0)
electron_is_anchor = (A287_layer["e"] == "edge")  # the anchored/normalization slot
check(9, "electron = char 0 (P20) = trivial rep = A287 anchor (edge slot, absorbed by "
         "normalization): all three coincide on the electron",
      electron_is_char0 and electron_is_anchor)

# Does Resolution A EXPLAIN the offset or only relabel it? Honest test:
# Resolution A correctly identifies WHY char-0 is special (it is the rep with no
# degree-matched term in rho, because rho(0)=0). But it does NOT by itself force
# WHICH coefficient the two NON-trivial families draw, nor that the electron draws
# the EDGE coefficient (deg 1, c=2) rather than its degree-matched char-0 term
# (deg 3, c=16). A287 puts the electron at EDGE (deg 1), not at its own char-0
# degree (deg 3). So A287's electron draws a degree (1) whose character (1) is NOT
# the electron's P20 character (0) -- the +1 twist again, unexplained by A only.
electron_A287_drawn_char = grade(A287_deg["e"])  # grade(1) = 1, not 0
check(10, "Resolution A is NECESSARY but NOT SUFFICIENT: it forces char-0 = anchor, but the "
          "electron's A287 slot is EDGE (deg 1, char 1), not its degree-matched char-0 (deg 3); "
          "the +1 twist on the electron itself remains (drawn char 1 != P20 char 0)",
      electron_A287_drawn_char == 1 and electron_A287_drawn_char != P20_char["e"])

# ---------------------------------------------------------------------------
print("\n-- Section 4: Resolution B (Dirichlet-zero ground shift) -- the ground-relative index --")
# Because rho(0)=0, the available degrees are {1,2,3}, not {0,1,2}. The natural
# ground/lowest layer is degree 1 (edge), not degree 0. Define the *ground-relative*
# index   n_rel(d) = d - 1   (so the edge/lowest layer is n_rel=0).
ground_rel = {d: d - 1 for d in (1, 2, 3)}  # {1:0, 2:1, 3:2}
check(11, f"Dirichlet ground shift: ground-relative degree index n_rel(d)=d-1 runs 0,1,2 over "
          f"the present degrees (1,2,3), with the EDGE (deg 1) as ground (n_rel=0)  "
          f"[got {ground_rel}]",
      ground_rel == {1: 0, 2: 1, 3: 2})

# The literal grading assigns character grade(d) = d mod 3 (degrees 1,2,3 -> 1,2,0).
# The ground-relative index assigns d-1 (degrees 1,2,3 -> 0,1,2). These DIFFER:
# grade(d) - (d-1) = +1 (mod 3) for every present degree -- the same uniform +1.
# So Resolution B reframes the twist as: the literal degree-grading is offset by +1
# from the ground-relative index, BECAUSE the character ladder starts its count at
# degree 0 (absent) while the layer ladder starts at degree 1 (edge, present).
ladder_gap = {d: (grade(d) - ground_rel[d]) % 3 for d in (1, 2, 3)}
check(12, f"the literal degree-grading char=d mod 3 is offset by a uniform +1 from the "
          f"ground-relative index d-1: grade(d)-(d-1)=+1 (mod 3) for all present d  "
          f"[got {ladder_gap}]",
      all(ladder_gap[d] == 1 for d in (1, 2, 3)))

# CRITICAL honest test (the scan in Section 5 confirms this): it is the GROUND-
# RELATIVE index d-1, NOT the literal grade d mod 3, that equals the P20 character.
# So the reconciling rule is char = n_rel(d) = d-1, i.e. the character ladder must
# be anchored at the EDGE (deg 1 -> char 0), not at the absent degree-0 term.
# Check that ground-relative index d-1 maps A287 degrees (1,2,3) onto P20 (0,1,2):
ground_rel_matches_P20 = all((A287_deg[f] - 1) % 3 == P20_char[f] for f in ("e", "mu", "tau"))
check(13, f"the GROUND-RELATIVE index (d-1) -- NOT the literal grading (d mod 3) -- maps A287 "
          f"degrees (1,2,3) onto P20 characters (0,1,2): edge(deg1)->char0=e, "
          f"boundary(deg2)->char1=mu, bulk(deg3)->char2=tau  [match {ground_rel_matches_P20}]",
      ground_rel_matches_P20)

# ---------------------------------------------------------------------------
print("\n-- Section 5: Resolution C + the decisive convention scan --")
# Two SEPARATE Z_3 actions on the SAME group element are in play, and the twist is
# the relation between them:
#  (1) The LAYER CYCLE T_cycle (P18 line 250): bulk -> boundary -> fiber -> bulk,
#      i.e. deg3 -> deg2 -> deg1 -> deg3 (DECREASING degree, forward cycle).
#  (2) The GRADED action on monomials (A323/A324): x^d -> omega^d x^d, so
#      char(deg d) = d (INCREASING character with degree).
# We now scan EVERY admissible convention and ask: can a single corpus-consistent
# choice make P20's (e,mu,tau)=(char 0,1,2) and A287's (e,mu,tau)=(edge,boundary,
# bulk = deg 1,2,3) AGREE on which coefficient each family draws?

# Free choices, all corpus-touching:
#  gen   in {+1, -1}: generator C (root omega, A304 FORCED) vs C^2 (root omega^2).
#                     C^2 conjugates all characters k -> -k mod 3.
#  g0    in {0, 1, 2}: a constant anchor-shift of the character ladder relative to
#                     the degree ladder; g0=0 is the literal x^d->omega^d x^d.
# Character ASSIGNED to a present degree d under (gen,g0):  chi(d) = gen*(d+g0) mod 3.
# A287 says family f draws degree A287_deg[f]. To COINCIDE with P20 we need
# chi(A287_deg[f]) == P20_char[f] for ALL three families.

def chi(d, gen, g0):
    return (gen * (d + g0)) % 3

recon_table = {(gen, g0): all(chi(A287_deg[f], gen, g0) == P20_char[f]
                              for f in ("e", "mu", "tau"))
               for gen, g0 in product((1, -1), (0, 1, 2))}
reconciled = [(gen, g0) for (gen, g0), ok in recon_table.items() if ok]
check(14, f"convention scan over (gen in {{+1,-1}}) x (anchor-shift g0 in {{0,1,2}}): "
          f"exactly the reconciling conventions are {sorted(reconciled)}",
      isinstance(reconciled, list) and len(reconciled) >= 1)

# The LITERAL corpus convention (gen=+1 forward generator A304; g0=0 literal grading)
# must NOT reconcile -- that absence IS the open twist.
literal_reconciles = recon_table[(1, 0)]
check(15, "the LITERAL corpus convention (A304 forward generator gen=+1; literal grading "
          "x^d->omega^d x^d, g0=0) does NOT reconcile P20 and A287 -- this is the open twist",
      literal_reconciles is False)

# A304 FORCES gen=+1 (forward generator C=T_cycle; C^2 excluded by L(3,1) positive
# orientation). So gen=-1 (root omega^2 / generator C^2) is corpus-EXCLUDED. Among
# the gen=+1 conventions, which g0 reconciles?
reconciled_gen_fixed = sorted([(gen, g0) for (gen, g0) in reconciled if gen == 1])
check(16, f"A304 forces gen=+1 (C^2/omega^2 excluded by L(3,1) orientation). Among gen=+1 "
          f"conventions the UNIQUE reconciler is {reconciled_gen_fixed} (i.e. anchor-shift "
          f"g0=2 == -1: char = (d-1) mod 3, the ground-relative index)",
      reconciled_gen_fixed == [(1, 2)])

# Spell out the unique reconciling convention explicitly so nothing hides behind a flag.
chi_R = {f: chi(A287_deg[f], 1, 2) for f in ("e", "mu", "tau")}
matches_P20 = [chi_R[f] for f in ("e", "mu", "tau")] == [P20_char[f] for f in ("e", "mu", "tau")]
check(17, f"explicit: under (gen=+1, g0=2), A287 degrees (1,2,3) carry characters "
          f"{[chi_R[f] for f in ('e','mu','tau')]} = P20 (0,1,2): they COINCIDE  [match {matches_P20}]",
      matches_P20)

# Honest contrast: the literal grading (g0=0) gives the +1-twisted labelling that
# does NOT match; the Dirichlet +1 shift (g0=1) ALSO does not match (it gives
# (2,0,1)); only g0=2 (== -1, the ground-relative index char=d-1) matches. So the
# reconciler is NOT "char = d mod 3" and NOT "char = (d+1) mod 3"; it is
# "char = (d-1) mod 3" -- the EDGE-anchored ground-relative degree.
g0_1_chars = [chi(A287_deg[f], 1, 1) for f in ("e", "mu", "tau")]
check(18, f"neither the literal grading (char=d: gives (1,2,0)) nor the +1 shift "
          f"(char=d+1: gives {g0_1_chars}) reconciles; ONLY char=(d-1) (edge-anchored "
          f"ground-relative index) reconciles -- the reconciler is uniquely fixed and is NOT "
          f"the naive Dirichlet +1",
      recon_table[(1, 0)] is False and recon_table[(1, 1)] is False and recon_table[(1, 2)] is True)

# ---------------------------------------------------------------------------
print("\n-- Section 6: structural origin of the twist (handedness mismatch) --")
# Do the two corpus actions on the SAME Z_3 element agree on a degree->character map
# without an extra choice? They do NOT -- and the disagreement is the twist's origin.
#   Layer cycle (P18 line 250): bulk->boundary->fiber->bulk = deg 3->2->1->3,
#     the 3-cycle (3 2 1) on degrees (DECREASING degree per step).
#   Graded action (A323): x^d->omega^d x^d, char(deg d) = d mod 3 (INCREASING with degree).
# One layer-cycle step (deg d -> layer_perm[d]) changes the graded character by:
layer_perm = {3: 2, 2: 1, 1: 3}  # bulk->boundary->fiber->bulk (P18 line 250)
char_step = {d: (grade(layer_perm[d]) - grade(d)) % 3 for d in (1, 2, 3)}
check(19, f"the layer cycle (P18 line 250: deg 3->2->1) and the degree-grading (x^d->omega^d x^d) "
          f"have OPPOSITE handedness on the degree ladder: one layer step changes graded "
          f"character by a uniform -1 (mod 3)  [got {char_step}]",
      all(char_step[d] == 2 for d in (1, 2, 3)))  # 2 == -1 mod 3

# This handedness mismatch IS Resolution C's "one unit of omega": the family index
# follows the LAYER CYCLE (the T_cycle eigenvalue, which IS the layer generator),
# while the coefficient-drawing follows the DEGREE grading; the two run opposite, so
# matching them costs one omega-character twist. Crucially, the SIGN is NOT free:
# A304 forces the layer cycle forward (gen=+1) AND the grading inherits z->omega z
# from the SAME forward rotation, so both are the forward action; the residual is the
# genuine handedness GAP between "cycle the layers" and "grade the degrees", a fixed
# -1 (mod 3), not a choosable +1 vs -1.
origin_named = all(char_step[d] == 2 for d in (1, 2, 3))
# The reconciling rule char=(d-1) is exactly this: the layer-cycle handedness, read
# off the ground-relative (edge=ground) degree, rather than the literal monomial degree.
reconciler_is_groundrel = (reconciled_gen_fixed == [(1, 2)]) and ground_rel_matches_P20
check(20, "the reconciling rule char=(d-1) mod 3 IS the layer-cycle handedness read off the "
          "ground-relative (edge=ground) degree, not the literal monomial degree: the structural "
          "origin (handedness mismatch / one unit of omega) and the reconciler coincide",
      origin_named and reconciler_is_groundrel)

# ---------------------------------------------------------------------------
print("\n-- Section 7: verdict (PARTIAL -- origin named, forcing not airtight) --")
# HIT would require ONE corpus-FIXED convention forced end-to-end under which P20 and
# A287 coincide. A unique reconciling convention EXISTS under the A304-forced
# generator (char = ground-relative degree d-1, edge=ground). Its structural origin
# is NAMED (handedness mismatch between the layer cycle and the degree grading, =
# one unit of omega, Resolution C) and the EDGE=ground anchoring is motivated by
# rho(0)=0 (Resolution B, the degree ladder starts at the edge). BUT the corpus
# never STATES the bridge that fixes which of the two actions the family index
# follows: P18/P20 define the family character as the T_cycle EIGENVALUE exponent
# (the layer-cycle side), with no reference to rho's degree; A287 assigns by LAYER
# (the degree/coefficient side), with no reference to the T_cycle eigenvalue. The
# reconciler is the value that bridge MUST take, but the bridge itself is unstated.
# So: NOT HIT (the alignment is not forced by an existing corpus statement), NOT
# open-with-no-structure (origin and reconciler are pinned and unique). => PARTIAL.
verdict_partial = (
    origin_named                                   # C: handedness mismatch = one omega
    and reconciled_gen_fixed == [(1, 2)]           # unique reconciler under A304 gen=+1
    and ground_rel_matches_P20                     # reconciler = ground-relative degree
    and electron_is_char0 and electron_is_anchor   # A: char0 = trivial = anchor (necessary)
    and literal_reconciles is False                # literal corpus convention does NOT reconcile
)
check(21, "VERDICT PARTIAL: a UNIQUE reconciling convention exists under the A304-forced "
          "generator (char = ground-relative degree d-1, edge=ground), with a NAMED structural "
          "origin (Resolution C handedness mismatch = one unit of omega) and EDGE=ground "
          "motivated by rho(0)=0 (Resolution B); but the forcing is NOT airtight -- the corpus "
          "must add ONE pin fixing that the family index follows the LAYER CYCLE eigenvalue "
          "while the coefficient is drawn by DEGREE, i.e. k = (drawn degree - 1) mod 3. "
          "Resolution A (char0=trivial=anchor) is necessary but only relabels.",
      verdict_partial)

# The precise pin the corpus must add, as the equality it would enforce:
pin_holds = all(P20_char[f] == (A287_deg[f] - 1) % 3 for f in ("e", "mu", "tau"))
check(22, "the EXACT pin needed (self-consistent): for every family, P20 character k = "
          "(drawn rho-degree - 1) mod 3 -- the family's T_cycle eigenvalue index equals the "
          "ground-relative (edge=ground) degree of its drawn coefficient. This is the single "
          "statement that upgrades PARTIAL -> HIT; it holds numerically here",
      pin_holds)

# Guard: confirm NO admissible convention makes the LITERAL grading reconcile, so the
# verdict cannot be quietly upgraded by re-reading the existing corpus alone.
literal_never = all(recon_table[(1, g0)] == (g0 == 2) for g0 in (0, 1, 2))
check(23, "guard: under the A304-forced generator, the literal/Dirichlet conventions (g0=0,1) "
          "NEVER reconcile -- only the edge-anchored ground-relative rule (g0=2) does; the "
          "twist cannot be closed by re-reading the existing corpus, only by adding the pin",
      literal_never)

# ===========================================================================
print(f"\n{'=' * 70}")
print(f"RESULT: {PASS} PASS / {FAIL} FAIL")
print(f"VERDICT: PARTIAL -- +1 twist named (handedness mismatch / one unit of omega,")
print(f"         size forced by rho(0)=0) but alignment bridge unstated; corpus pin:")
print(f"         family T_cycle-index k = (drawn rho-degree - 1) mod 3 (edge=ground).")
print(f"{'=' * 70}")
sys.exit(0 if FAIL == 0 else 1)
