# Copyright Léon Fernando Vlegels -- CC BY 4.0
"""
probe_oi287_z3selection.py
OI-287-1 (corrected), pre-registered gated probe P2 (A323).

The corrected OI-287-1 (A321 reversion, A322 grounding): derive WHY the lepton
mass correction law assigns charged-lepton family n the degree-n Taylor
coefficient c_n of the density rho, the integers c=(2,3,16) read directly. A322
gated IN exactly one corpus-faithful object: O-Z3-grading -- the three families
are the omega^k eigenspaces (k=0,1,2) of the layer-cycle operator T_cycle, a
representation-theoretic grading, NOT an expectation value. The withdrawn
overlap-diagonal <psi_n|c_k pi^k x^k|psi_n>/lambda_n (A299-A320, withdrawn A321)
is NOT revived here. This probe works ONLY within O-Z3-grading, at the
structural / representation-theory level. No eigenstate overlap is formed.

THE HYPOTHESIS P2 TESTS (index-matching by Z_3 character SELECTION):
If the corpus layer-cycle Z_3 grades the degree-k Taylor monomial of rho with
character k mod 3, then by Schur orthogonality a coupling between family-k (the
omega^k eigenspace) and a term of rho is nonzero ONLY for the matching-character
term, i.e. ONLY degree == k (mod 3). That would DERIVE "family k draws degree-k
coefficient" as a Z_3 selection rule.

THE CORPUS Z_3 ACTION (P18 S3.5, P06 S3, P20 Family Origin -- all cited):
  - P18 S3.5 prop "Canonical selection of T_cycle": T_cycle acts as the forward
    fiber rotation varphi -> varphi + 2pi/3 on the Hopf fiber coordinate; the two
    order-3 generators are T_cycle (-> L(3,1)) and T_cycle^2 (-> L(3,2) = -L(3,1)),
    L(3,1) selected by P06 + P28 orientation. T_cycle^3 = I, eigenvalues the cube
    roots of unity omega^k (P18 prop "Z_3 Eigenspaces"; P20 prop "Family Origin").
  - The Hopf fibration writes S^3 subset C^2 with the fiber acting as the common
    phase: a fiber rotation by varphi sends the complex base coordinate z -> e^{i
    varphi} z. The forward layer-cycle generator (varphi = 2pi/3) therefore sends
    z -> omega z (omega = e^{2pi i/3}). This is the standard Hopf relation between
    the fiber phase and the C^2 coordinate; it is what makes the omega^k eigenspace
    the fiber-momentum sector m == k (mod 3) used in A317.
  - rho(x) is a function of the (radial/base) coordinate. With x the modulus of
    the complex base coordinate carrying the fiber phase (x ~ |z|, the degree-k
    harmonic ~ z^k), the layer-cycle sends the degree-k monomial x^k -> omega^k x^k.
    So the degree-k term of rho carries Z_3 character omega^k = character k mod 3.

This is a finite-group character computation: light, exact, no eigenstates.

Writes Outputs/oi287_z3selection.json (four levels up from verify/).
Prints VERDICT (HIT / PARTIAL / NULL).

PRE-REGISTRATION (before computing):
  HIT  iff the corpus-faithful Z_3 action grades rho's degree-k term with a
       definite character such that Z_3 selection forces each family to couple
       only to ONE degree, AND that forced pairing matches the corpus's actual
       family-degree assignment (n<->n literal, or the mod-3 version stated
       honestly).
  PARTIAL iff Z_3 selection holds structurally (degree-k carries character k mod
       3; off-character Schur sums vanish) but the forced pairing is the mod-3
       version (degree 3 -> character 0, the k=0 family) which matches the corpus
       assignment only under a stated cyclic reindexing -- report the exact
       correspondence and whether it is corpus-consistent.
  NULL iff the corpus Z_3 action does NOT grade rho's degrees by character
       (selection does not apply) -- then this candidate "why" fails; redirect
       to P3 (lens-space decomposition).

CRITICAL: rep-theory / grading ONLY. No eigenstate overlap, no expectation value
(that is the withdrawn functional). The objects are: the Z_3 character of each
rho-degree, and the Schur selection sum. The mod-3 reindexing subtlety (degrees
1,2,3 vs characters 1,2,0) is reported honestly, not papered over.
"""
import json, os, math, cmath
import numpy as np

PI = math.pi
ALPHA_INV = 4*PI**3 + PI**2 + PI          # corpus constant; sanity only

# ---------------------------------------------------------------------------
# 0. Z_3 structure: omega and the layer-cycle operator (P18 S3.5; A304 forced).
#    Represent T_cycle on its three eigenspaces as diag(omega^0, omega^1, omega^2).
#    (Any faithful order-3 generator is conjugate to this; A304 verified T_cycle
#    is the forced generator via the L(3,1) orientation.)
# ---------------------------------------------------------------------------
omega = cmath.exp(2j*PI/3)                                  # e^{2pi i/3}
T_cycle = np.diag([omega**0, omega**1, omega**2])           # on the 3 eigenspaces
T3 = T_cycle @ T_cycle @ T_cycle
T_cubed_is_I = np.allclose(T3, np.eye(3), atol=1e-12)
eigvals = np.linalg.eigvals(T_cycle)
# the eigenvalue set is exactly {1, omega, omega^2}
target_eigs = np.array([1.0+0j, omega, omega**2])
def setmatch(a, b, tol=1e-12):
    a = sorted(a, key=lambda z: (round(z.real, 9), round(z.imag, 9)))
    b = sorted(b, key=lambda z: (round(z.real, 9), round(z.imag, 9)))
    return all(abs(x - y) < tol for x, y in zip(a, b))
eigs_are_cube_roots = setmatch(list(eigvals), list(target_eigs))

# ---------------------------------------------------------------------------
# 1. The corpus Z_3 action on the base monomials.
#    Layer-cycle (forward, P18 S3.5): varphi -> varphi + 2pi/3 on the Hopf fiber.
#    Hopf: fiber phase rotates the complex base coordinate z -> e^{i varphi} z, so
#    the forward generator sends z -> omega z. Hence x^k -> omega^k x^k: the
#    degree-k monomial carries character omega^k = character (k mod 3).
#    This is the action whose fiber-momentum form A317 used (e^{i m psi} -> omega^m;
#    omega^k eigenspace = {m == k mod 3}).
# ---------------------------------------------------------------------------
def character_of_degree(k):
    """Z_3 character (as an integer in {0,1,2}) carried by the degree-k monomial
    under the corpus layer-cycle action z -> omega z (so x^k -> omega^k x^k)."""
    return k % 3

# rho(x) = 16 pi^3 x^3 + 3 pi^2 x^2 + 2 pi x.  Degrees present and their integer
# coefficients c_n = rho^(n)(0)/(pi^n n!): deg1->2, deg2->3, deg3->16.
rho_terms = {1: 2, 2: 3, 3: 16}            # degree -> c_n (the integers, A296/A322)
degree_characters = {k: character_of_degree(k) for k in rho_terms}   # {1:1, 2:2, 3:0}

# Does degree-k carry character k mod 3?  By construction yes; record explicitly
# for each degree present.
degree_k_carries_char_k = all(degree_characters[k] == (k % 3) for k in rho_terms)

# ---------------------------------------------------------------------------
# 2. The corpus family-degree assignment (the mass law A287) and the family
#    characters (P20 Family Origin / P06 S3).
#    Mass law: family n (n=1,2,3 = e,mu,tau) draws degree-n coefficient c_n.
#    P20/P06: family characters are e->k=0 (omega^0), mu->k=1 (omega^1),
#    tau->k=2 (omega^2).
# ---------------------------------------------------------------------------
families = ["e", "mu", "tau"]
family_index_n = {"e": 1, "mu": 2, "tau": 3}        # mass-law level n (1-based)
family_char_k  = {"e": 0, "mu": 1, "tau": 2}        # Z_3 character k (P06/P20)
# corpus assignment: family n -> degree n  (literal n<->n)
corpus_pairing_family_to_degree = {f: family_index_n[f] for f in families}
# -> {e:1, mu:2, tau:3}, coefficients (2,3,16)

# ---------------------------------------------------------------------------
# 3. Z_3 SELECTION CHECK (Schur orthogonality).
#    Form the Z_3-invariant pairing between a family of character a and a rho-term
#    of character b: P(a,b) = (1/3) sum_{j=0}^{2} omega^{(a-b) j} = delta_{a == b (mod 3)}.
#    A nonzero coupling requires matching characters. Off-character couplings vanish.
# ---------------------------------------------------------------------------
def schur_pairing(a, b):
    s = sum(omega**(((a - b) % 3) * j) for j in range(3))
    return s / 3.0
# selection matrix S[family_char][degree_char]
chars = [0, 1, 2]
schur_matrix = {a: {b: schur_pairing(a, b) for b in chars} for a in chars}
# verify Schur: sum_j omega^{(a-b)j} = 3 delta_{a,b mod 3}, i.e. pairing = delta.
schur_is_delta = all(
    abs(schur_pairing(a, b) - (1.0 if (a - b) % 3 == 0 else 0.0)) < 1e-12
    for a in chars for b in chars
)
# each family-char couples to exactly one degree-char (the matching one)
selection_forces_one_degree = all(
    sum(1 for b in chars if abs(schur_pairing(a, b)) > 1e-9) == 1
    for a in chars
)

# ---------------------------------------------------------------------------
# 4. The FORCED pairing implied by Z_3 selection, vs the corpus assignment.
#    Family of character a couples only to the rho-degree d whose character
#    matches: character_of_degree(d) == a. Among degrees present {1,2,3} with
#    characters {1,2,0}, the unique matching degree for each family character:
# ---------------------------------------------------------------------------
def forced_degree_for_char(a):
    matches = [d for d in rho_terms if character_of_degree(d) == a % 3]
    return matches[0] if len(matches) == 1 else None   # unique among deg {1,2,3}
forced_pairing_family_to_degree = {f: forced_degree_for_char(family_char_k[f]) for f in families}
# e(char0)->deg3, mu(char1)->deg1, tau(char2)->deg2
forced_pairing_family_to_coeff = {f: rho_terms[forced_pairing_family_to_degree[f]] for f in families}

# Does the forced (selection) pairing match the corpus literal n<->n pairing?
matches_literal = (forced_pairing_family_to_degree == corpus_pairing_family_to_degree)

# The mod-3 reindexing: the forced pairing assigns family character a the degree d
# with d == a (mod 3). The corpus mass law assigns family LEVEL n the degree n.
# These coincide iff the family LEVEL equals its character mod 3, i.e. n == k (mod
# 3). But P06/P20 set e->k=0,mu->k=1,tau->k=2 while the mass law levels are
# e->n=1,mu->n=2,tau->n=3. So n = k+1: level n and character k differ by 1, and
# the forced pairing is a CYCLIC SHIFT of the corpus pairing, NOT the identity.
# Concretely the forced pairing gives (e,mu,tau)->coeffs (16,2,3); the corpus
# gives (2,3,16). They are related by the cyclic relabel d -> d (mod 3) only if
# one redefines which family is which.
forced_coeffs_in_family_order = [forced_pairing_family_to_coeff[f] for f in families]   # [16,2,3]
corpus_coeffs_in_family_order = [rho_terms[corpus_pairing_family_to_degree[f]] for f in families]  # [2,3,16]
forced_matches_corpus_coeffs = (forced_coeffs_in_family_order == corpus_coeffs_in_family_order)

# Is the forced pairing a cyclic permutation (mod-3 reindex) of the corpus one?
def is_cyclic_shift(a, b):
    n = len(a)
    return any(a == b[s:] + b[:s] for s in range(n))
forced_is_cyclic_of_corpus = is_cyclic_shift(forced_coeffs_in_family_order,
                                             corpus_coeffs_in_family_order)

# ---------------------------------------------------------------------------
# 5. VERDICT (per pre-registration).
# ---------------------------------------------------------------------------
selection_holds_structurally = (degree_k_carries_char_k and schur_is_delta
                                and selection_forces_one_degree)
if not selection_holds_structurally:
    verdict = "NULL"
elif matches_literal:
    verdict = "HIT"
else:
    # selection holds, each family forced to one degree, but the forced pairing
    # is the mod-3 version (deg 3 -> char-0 family) which is a cyclic shift of the
    # corpus n<->n assignment, not the identity. Honest: PARTIAL.
    verdict = "PARTIAL"

out = {
    "addendum": "A323",
    "probe": "P2",
    "problem": ("OI-287-1 (corrected): why does the lepton mass correction law assign "
                "level n the degree-n Taylor coefficient c_n of rho? P2 tests whether "
                "the corpus layer-cycle Z_3 grades degree-k of rho with character k mod 3 "
                "so that Schur selection forces family-k to couple only to degree-k."),
    "gated_object": "O-Z3-grading (A322); rep-theory/grading level only; overlap-diagonal NOT revived",
    "verdict": verdict,
    "z3_structure": {
        "omega": [omega.real, omega.imag],
        "T_cycle_cubed_is_I": bool(T_cubed_is_I),
        "eigenvalues_are_cube_roots_of_unity": bool(eigs_are_cube_roots),
        "cite": "P18 S3.5 prop 'Z_3 Eigenspaces' / 'Canonical selection of T_cycle'; A304 forced",
    },
    "corpus_action": {
        "fiber_rotation": "varphi -> varphi + 2pi/3 on the Hopf fiber (P18 S3.5)",
        "induced_on_base": "z -> omega z (Hopf: fiber phase rotates the C^2 base coordinate)",
        "induced_on_monomials": "x^k -> omega^k x^k, so degree-k carries character k mod 3",
        "cite": "P18 S3.5; P06 S3 (L(3,1)=S^3/Z_3); A317 fiber-momentum m==k mod 3",
    },
    "rho_degree_characters": {str(k): degree_characters[k] for k in rho_terms},
    "rho_terms_degree_to_coeff": {str(k): rho_terms[k] for k in rho_terms},
    "degree_k_carries_character_k_mod_3": bool(degree_k_carries_char_k),
    "schur_selection": {
        "identity": "(1/3) sum_j omega^{(a-b)j} = delta_{a==b mod 3}",
        "schur_is_delta": bool(schur_is_delta),
        "selection_forces_one_degree_per_family": bool(selection_forces_one_degree),
        "matrix": {str(a): {str(b): [schur_matrix[a][b].real, schur_matrix[a][b].imag]
                            for b in chars} for a in chars},
    },
    "family_characters": family_char_k,
    "family_levels_n": family_index_n,
    "corpus_pairing_family_to_degree": corpus_pairing_family_to_degree,
    "corpus_coeffs_in_family_order_e_mu_tau": corpus_coeffs_in_family_order,
    "forced_pairing_family_to_degree": forced_pairing_family_to_degree,
    "forced_coeffs_in_family_order_e_mu_tau": forced_coeffs_in_family_order,
    "forced_matches_corpus_literal_n_to_n": bool(matches_literal),
    "forced_matches_corpus_coeffs": bool(forced_matches_corpus_coeffs),
    "forced_is_cyclic_shift_of_corpus": bool(forced_is_cyclic_of_corpus),
    "mod3_reindex_note": (
        "Degrees present {1,2,3} carry characters {1,2,0}: degree 3 -> character 0 "
        "(the trivial / k=0 family). The corpus family characters are e->0, mu->1, "
        "tau->2 (P06/P20), while the mass-law levels are e->1, mu->2, tau->3, so "
        "level n = character k + 1. Z_3 selection therefore forces (e,mu,tau) -> "
        "coeffs (16,2,3), a CYCLIC SHIFT of the corpus (2,3,16); it does NOT "
        "reproduce the literal n<->n assignment. The selection rule is structurally "
        "exact but the character-based pairing and the level-based pairing differ by "
        "the offset n = k+1; degree 3 lands on character 0, not on level 3."
    ),
    "selection_holds_structurally": bool(selection_holds_structurally),
    "interpretation": {
        "HIT": "Z_3 selection forces each family to one degree AND reproduces the corpus n<->n assignment.",
        "PARTIAL": ("Z_3 selection is structurally exact (degree-k carries character k mod 3; "
                    "off-character Schur sums vanish; each family forced to one degree), but the "
                    "forced character-pairing is a cyclic shift of the corpus level-pairing because "
                    "degree 3 carries character 0, not character/level 3. The selection rule is real; "
                    "the index-matching is a Z_3 selection rule at the character level, but the "
                    "match to the mass law's n<->n labelling needs the level/character offset n=k+1 "
                    "reconciled, which the corpus does not yet fix. Redirect the residual to P3 "
                    "(is c_n = rho^(n)(0)/(pi^n n!) itself the lens-space Z_3 grading, which would "
                    "fix the labelling)."),
        "NULL": "The corpus Z_3 action does not grade rho's degrees by character; selection does not apply.",
    }[verdict],
    "withdrawn_stays_withdrawn": ("The A299-A320 overlap-diagonal <psi_n|c_k pi^k x^k|psi_n>/lambda_n "
                                  "(A321) is NOT revived. No eigenstate overlap or expectation value "
                                  "is formed in this probe; the objects are the Z_3 character of each "
                                  "rho-degree and the Schur selection sum."),
    "no_published_number_changes": True,
}

# Outputs path: four levels up from verify/  (verify -> addenda -> corpus -> Lumen -> LumenOS)
here = os.path.dirname(os.path.abspath(__file__))
root = os.path.abspath(os.path.join(here, "..", "..", "..", ".."))
outdir = os.path.join(root, "Outputs")
os.makedirs(outdir, exist_ok=True)
outpath = os.path.join(outdir, "oi287_z3selection.json")
with open(outpath, "w") as f:
    json.dump(out, f, indent=2)

# NaN/Inf guard
def has_bad(o):
    if isinstance(o, float): return math.isnan(o) or math.isinf(o)
    if isinstance(o, dict):  return any(has_bad(v) for v in o.values())
    if isinstance(o, list):  return any(has_bad(v) for v in o)
    return False
assert not has_bad(out), "NaN/Inf in output"

# -------- report --------
print("="*72)
print("OI-287-1 (corrected) PROBE P2  --  Z_3 character SELECTION  --  A323")
print("="*72)
print(f"\nZ_3 structure (P18 S3.5; A304 forced):")
print(f"  omega = e^{{2pi i/3}} = ({omega.real:+.6f}, {omega.imag:+.6f}i)")
print(f"  T_cycle^3 = I : {T_cubed_is_I}   eigenvalues = {{1, omega, omega^2}} : {eigs_are_cube_roots}")
print(f"\nCorpus action (P18 S3.5 / P06 / A317):")
print(f"  fiber: varphi -> varphi + 2pi/3   =>   z -> omega z   =>   x^k -> omega^k x^k")
print(f"\nrho degree -> Z_3 character (k mod 3):")
for k in rho_terms:
    print(f"  degree {k} (coeff c={rho_terms[k]:>2}) -> character {degree_characters[k]}")
print(f"  degree-k carries character k mod 3 ? {degree_k_carries_char_k}")
print(f"\nSchur selection  (1/3) sum_j omega^{{(a-b)j}} = delta_{{a==b mod 3}} : {schur_is_delta}")
print(f"  each family character forced to exactly ONE degree : {selection_forces_one_degree}")
print(f"\nFamily characters (P06/P20): e->0, mu->1, tau->2 ; mass-law levels: e->1,mu->2,tau->3")
print(f"  corpus pairing (n<->n): (e,mu,tau) -> coeffs {corpus_coeffs_in_family_order}")
print(f"  Z_3-forced pairing    : (e,mu,tau) -> coeffs {forced_coeffs_in_family_order}")
print(f"  forced == corpus literal n<->n ? {matches_literal}")
print(f"  forced is a cyclic shift of corpus ? {forced_is_cyclic_of_corpus}")
print(f"\n  mod-3 subtlety: degree 3 -> character 0 (k=0 family), not level 3;")
print(f"                  level n = character k + 1, so the pairing shifts cyclically.")
print(f"\nVERDICT: {verdict}")
print(f"wrote {outpath}")
