"""v299 -- the hypergraph / Wolfram-program bridge, hardened.  This does NOT claim
"P2 is derived" or "E8 emerges from nothing".  It proves the precise, honest statement:

    P2 (the 3-arm (2,3,r) exceptional branch type) is the irreducible seed on which a
    LOCAL, witness-based spectral growth dynamics REACHES E8 = T(2,3,5) as the LAST FINITE
    point: it runs E6 -> E7 -> E8 and stops at the affine boundary Ê8 (the critical rho=2
    edge).  The Wolfram floor and the TFPT floor fall back to the SAME (2,3,5) seed.

On the groups (a deliberate precision): the icosahedron GRAPH has automorphism group
I_h = A5 x Z2 of order 120 (rotations A5 order 60 + reflections).  The McKay group is the
binary icosahedral group 2I, the SU(2) double cover of the rotation group A5 -- also order
120 but a DIFFERENT (perfect) group; it is 2I, not the graph automorphism group, whose
McKay graph is affine E8 (built from quaternions in v219).

Negative controls (the built-in guards against over-reach): a path A_n seed and a D_n seed
have the EXACT closed forms rho(A_n)=2cos(pi/(n+1)) and rho(D_n)=2cos(pi/(2n-2)), both < 2
for ALL n -- so rho<=2 selects the ADE world, NOT E8; only the exceptional (1,2,r) arm does.

Claims (hard-typed):
  [E]  HYP.ICO.01     icosahedron = 3-uniform hypergraph (12 V, 20 triangular hyperedges,
                      30 edges = h(E8)); |Aut(graph)| = 120 = |I_h| = |A5 x Z2| (the McKay
                      group is the SU(2) binary lift 2I, a different order-120 group; v219).
  [E]  HYP.REWRITE.01 the 1->4 subdivision rewrite preserves all 120 icosahedral
                      symmetries at every scale (V: 12 -> 42 -> 162).
  [E]  HYP.FUSION.01  the 2I fusion tensor N^k_ij is a nonneg-integer weighted 3-uniform
                      hypergraph on 9 nodes; its 2-dim-rep slice = the affine E8 McKay
                      graph (A.deg = 2 deg, top eigenvalue 2).
  [E]  HYP.SMITH.01   Smith: rho<=2 selects (affine) ADE; rho(A_n,E6,E7,E8) < 2,
                      rho(Ê8) = 2, rho(E10) > 2; E8 = maximal finite exceptional in
                      T(2,3,r).
  [E]  HYP.CW.01      Collatz-Wielandt: rho<=2 <=> a positive witness m with A m <= 2m
                      exists (a LOCAL per-node balance), holds for E8/Ê8, fails for E10.
  [E*] HYP.AUTO.01    the autonomous rule (self-generated witness via projectively-
                      normalised diffusion + LOCAL gate A m <= 2m, NO global rho) runs
                      E6 -> E7 -> E8 -> Ê8 then stops -- exact UNDER the 3-arm (2,3,*)
                      seed and the grow-longest-leg policy.  Critical witness = the Kac
                      marks.
  [E]  HYP.NEG.AN.01  control: rho(A_n)=2cos(pi/(n+1))<2 for ALL n (closed form), so the
                      A-family never reaches E8.
  [E]  HYP.NEG.DN.01  control: rho(D_n)=2cos(pi/(2n-2))<2 for ALL n (closed form), so the
                      D-family never reaches E8; only the (1,2,r) arm does.
  [O]  HYP.SEED.01    the 3-arm (2,3,r) exceptional branch is the SINGLE irreducible input
                      = TFPT's P2 / the icosahedral choice (NOT "P2 derived").

Status: [E] the hypergraph facts (ICO/REWRITE/FUSION), the spectral selection
(SMITH/CW), the two negative controls; [E*] the autonomous rule under the stated seed +
policy; [O] the seed itself = P2.  A second, network-theoretic reading of the SAME
(2,3,5)/E8 core, honestly bounded.  Builds on v219/v298.  Python (numpy; imports v219
for the 2I group + characters).
"""
import itertools

import numpy as np

import v219_icosahedral_mckay as mck
from tfpt_constants import check, summary, reset, N_fam, g_car

PHI = (1 + np.sqrt(5)) / 2


# ---------- icosahedron (3-uniform hypergraph) ----------------------------
def _icosahedron():
    V = []
    for a, b in itertools.product([1, -1], repeat=2):
        V += [(0, a, b * PHI), (a, b * PHI, 0), (b * PHI, 0, a)]
    V = np.array(sorted(set(map(tuple, np.round(V, 9)))))
    D = np.round(((V[:, None] - V[None]) ** 2).sum(-1), 6)
    el2 = np.min(D[D > 1e-9])
    A = (np.abs(D - el2) < 1e-6).astype(int)
    faces = [t for t in itertools.combinations(range(len(V)), 3)
             if A[t[0], t[1]] and A[t[0], t[2]] and A[t[1], t[2]]]
    return V, A, faces


def _graph_autos(A):
    n = len(A)
    nbr = [set(np.where(A[i])[0]) for i in range(n)]
    autos = []

    def bt(perm, used):
        k = len(perm)
        if k == n:
            autos.append(tuple(perm))
            return
        for img in range(n):
            if img in used:
                continue
            if all((perm[j] in nbr[img]) == (j in nbr[k]) for j in range(k)):
                bt(perm + [img], used | {img})
    bt([], set())
    return autos


# ---------- star graphs T_{p,q,r} (Dynkin) -------------------------------
def _star(legs):
    nodes = 1
    adj = []
    for L in legs:
        prev = 0
        for _ in range(L):
            adj.append((prev, nodes))
            prev = nodes
            nodes += 1
    A = np.zeros((nodes, nodes))
    for a, b in adj:
        A[a, b] = A[b, a] = 1
    return A


def _rho(A):
    return float(max(np.linalg.eigvalsh(A)))


def _witness_localgate(A):
    """Self-generate the Perron witness by projectively-normalised lazy diffusion, then
    test the LOCAL per-node balance A m <= 2 m (no global rho in the decision)."""
    d = A.shape[0]
    M = (A + 2 * np.eye(d)) / 4
    rng = np.random.default_rng(0)
    m = rng.random(d)
    for _ in range(5000):
        m = M @ m
        m = m / m.min()                      # projective normalisation
    return m, bool(np.all(A @ m <= 2 * m + 1e-6))


def _grow(seed, policy_longest=True, cap=12):
    """Grow the longest leg while the LOCAL gate accepts; return the leg-sequence."""
    legs = list(seed)
    seq = []
    for _ in range(cap):
        seq.append(tuple(sorted(legs)))
        legs2 = legs[:]
        legs2[legs2.index(max(legs2))] += 1
        if _witness_localgate(_star(legs2))[1]:
            legs = legs2
        else:
            break
    return seq


def run():
    reset()
    print("v299  hypergraph/Wolfram bridge: the (2,3,r) seed reaches E8 as the last finite point of a local ADE rule")

    # HYP.ICO.01
    V, A, faces = _icosahedron()
    nV, nE, nF = len(V), int(A.sum() // 2), len(faces)
    naut = len(_graph_autos(A))
    check("HYP.ICO.01 [E]: the icosahedron is a 3-uniform hypergraph (V=%d, triangular "
          "hyperedges=%d, edges=%d=h(E8)); |Aut(graph)|=%d=|I_h|=|A5xZ2| (rotations A5 "
          "order 60 + reflections). The McKay group is the SU(2) binary lift 2I (order "
          "120, perfect, != I_h), built from quaternions in v219" % (nV, nF, nE, naut),
          nV == 12 and nF == 20 and nE == 30 == 2 * N_fam * g_car and naut == 120)

    # HYP.REWRITE.01: 1->4 subdivision preserves all 120 symmetries
    autos = _graph_autos(A)
    Os = [np.linalg.lstsq(V, V[list(s)], rcond=None)[0].T for s in autos]
    nbr = [set(np.where(A[i])[0]) for i in range(12)]
    edges = sorted({(min(i, j), max(i, j)) for i in range(12) for j in nbr[i]})
    scale = np.linalg.norm(V[0])
    mids = [((V[i] + V[j]) / 2) / np.linalg.norm((V[i] + V[j]) / 2) * scale for i, j in edges]
    Vsub = np.vstack([V, np.array(mids)])
    preserved = sum(all(np.min(np.abs(Vsub - w).sum(1)) < 1e-6 for w in (O @ Vsub.T).T)
                    for O in Os)
    check("HYP.REWRITE.01 [E]: the 1->4 triangle-subdivision rewrite is equivariant -- "
          "all %d/%d full icosahedral symmetries (I_h, whose rotation subgroup A5 has "
          "the binary lift 2I) still permute the subdivided hypergraph (V 12->%d->162); "
          "the icosahedral symmetry is carried to every scale"
          % (preserved, len(Os), len(Vsub)), preserved == 120 and len(Vsub) == 42)

    # HYP.FUSION.01: 2I fusion tensor (via v219) is a 3-uniform hypergraph; V-slice = E8
    G = mck._icosians()
    n = len(G)
    index = {mck._key(q): i for i, q in enumerate(G)}
    table = np.empty((n, n), int)
    for i in range(n):
        for j in range(n):
            table[i, j] = index[mck._key(mck._quat_mult(G[i], G[j]))]
    classes = mck._conjugacy_classes(table, n)
    degs, chars, _ = mck._dixon(table, classes, n)
    chars = np.array(chars)
    sizes = np.array([len(c) for c in classes])
    nc = len(classes)
    Nt = np.zeros((nc, nc, nc))
    for i in range(nc):
        for j in range(nc):
            for k in range(nc):
                Nt[i, j, k] = np.real(np.sum(sizes * chars[i] * chars[j] * np.conj(chars[k])) / n)
    Nt = np.round(Nt).astype(int)
    chiV = np.array([2 * G[classes[c][0]][0] for c in range(nc)])
    vi = [i for i in range(nc) if degs[i] == 2 and np.allclose(chars[i], chiV, atol=1e-6)][0]
    deg_vec = np.array(degs, float)
    slice_ok = np.allclose(Nt[vi].dot(deg_vec), 2 * deg_vec)
    check("HYP.FUSION.01 [E]: the 2I fusion tensor N^k_ij is a nonneg-integer weighted "
          "3-uniform hypergraph on 9 nodes (entries %s); its 2-dim-rep slice = the "
          "affine E8 McKay graph (A.deg=2deg, top eig 2)"
          % sorted(set(Nt.flatten().tolist())),
          (Nt >= 0).all() and slice_ok)

    # HYP.SMITH.01
    rs = {nm: _rho(_star(lg)) for nm, lg in
          [("A8", [7, 0, 0]), ("E6", [1, 2, 2]), ("E7", [1, 2, 3]), ("E8", [1, 2, 4]),
           ("Ê8", [1, 2, 5]), ("E10", [1, 2, 6])]}
    check("HYP.SMITH.01 [E]: Smith -- rho<=2 selects (affine) ADE; rho(A8,E6,E7,E8)<2 "
          "(%.3f,%.3f,%.3f,%.3f), rho(Ê8)=%.3f, rho(E10)=%.3f>2; E8 = maximal finite "
          "exceptional in T(2,3,r)"
          % (rs["A8"], rs["E6"], rs["E7"], rs["E8"], rs["Ê8"], rs["E10"]),
          rs["E8"] < 2 < rs["E10"] and abs(rs["Ê8"] - 2) < 1e-9)

    # HYP.CW.01: the LOCAL gate is decided by the DIFFUSION-generated witness (no global
    # rho); the Perron eigenvector is compared only as an independent audit.
    cw = {nm: _witness_localgate(_star(lg))[1] for nm, lg in
          [("E8", [1, 2, 4]), ("Ê8", [1, 2, 5]), ("E10", [1, 2, 6])]}

    def _perron_gate(lg):
        Ad = _star(lg).astype(float)
        m = np.abs(np.linalg.eigh(Ad)[1][:, -1]); m = m / m.min()
        return bool(np.all(Ad @ m <= 2 * m + 1e-6))
    audit_ok = all(_perron_gate(lg) == cw[nm] for nm, lg in
                   [("E8", [1, 2, 4]), ("Ê8", [1, 2, 5]), ("E10", [1, 2, 6])])
    check("HYP.CW.01 [E]: Collatz-Wielandt -- the LOCAL gate (diffusion-generated "
          "witness, A m <= 2m, no global rho in the decision): E8=%s, Ê8=%s, E10=%s; "
          "the independent Perron-eigenvector audit agrees (%s)"
          % (cw["E8"], cw["Ê8"], cw["E10"], audit_ok),
          cw["E8"] and cw["Ê8"] and not cw["E10"] and audit_ok)

    # HYP.AUTO.01: autonomous rule from the (2,3,*) seed -> E6,E7,E8,Ê8
    seq = _grow([1, 2, 2])
    expected = [(1, 2, 2), (1, 2, 3), (1, 2, 4), (1, 2, 5)]
    m_aff, _ = _witness_localgate(_star([1, 2, 5]))
    marks = sorted(np.round(m_aff).astype(int).tolist())
    check("HYP.AUTO.01 [E*]: the autonomous rule (self-generated witness via "
          "projectively-normalised diffusion + LOCAL gate A m<=2m, no global rho) runs "
          "E6->E7->E8->Ê8 then stops (seq %s); critical witness = Kac marks %s -- exact "
          "UNDER the (2,3,*) seed + grow-longest-leg policy"
          % (seq, marks),
          seq == expected and marks == [1, 2, 2, 3, 3, 4, 4, 5, 6])

    # HYP.NEG.AN.01: path control, EXACT for all n (closed form rho(A_m)=2cos(pi/(m+1)))
    an_ok = all(abs(_rho(_star([m - 1, 0, 0])) - 2 * np.cos(np.pi / (m + 1))) < 1e-6
                for m in range(4, 12))
    seq_a = _grow([1, 0, 0], cap=8)
    check("HYP.NEG.AN.01 [E]: control -- A_n is a path with rho(A_n)=2cos(pi/(n+1))<2 "
          "for ALL n (closed form verified, n=4..11; finite growth %d steps never "
          "reaches E8). So rho<=2 selects the ADE world, NOT E8"
          % len(seq_a), an_ok and (1, 2, 4) not in seq_a)

    # HYP.NEG.DN.01: D_n control, EXACT for all n (rho(D_m)=2cos(pi/(2m-2)))
    dn_ok = all(abs(_rho(_star([1, 1, m - 3])) - 2 * np.cos(np.pi / (2 * m - 2))) < 1e-6
                for m in range(4, 12))
    seq_d = _grow([1, 1, 2], cap=8)
    check("HYP.NEG.DN.01 [E]: control -- D_n has rho(D_n)=2cos(pi/(2n-2))<2 for ALL n "
          "(closed form verified, n=4..11; finite growth %d steps never reaches E8). "
          "Only the exceptional (1,2,r) arm reaches E8"
          % len(seq_d), dn_ok and (1, 2, 4) not in seq_d)

    # HYP.SEED.01: the seed is the irreducible input = P2 (NOT derived)
    check("HYP.SEED.01 [O]: the 3-arm (2,3,r) exceptional branch type is the SINGLE "
          "irreducible input = TFPT's P2 / the icosahedral choice (the A_n and D_n "
          "controls confirm rho<=2 alone is not enough) -- this is NOT 'P2 derived', it "
          "is 'P2 = the minimal seed on which the local rule REACHES E8 as the last "
          "finite point'", True)

    return summary("v299 hypergraph bridge: the 3-arm (2,3,r) seed is the irreducible input on which a local witness-based rule REACHES E8 as the last finite point (E6->E7->E8->Ê8; A_n/D_n controls with exact rho=2cos formulas: rho<=2 selects ADE, not E8); a second network reading of the (2,3,5)/E8 core, NOT a derivation of P2")


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