#!/usr/bin/env python3
"""
Computation 52 -- Dixon-algebra foundation: chain algebra of O on C tensor O
==============================================================================
Per research/ngen_3.md section 11/12, Attack Line A proper requires
extending H_F = C^8 to a larger structure carrying the chain algebra
that Furey 2018 / 2022 identifies with three generations.

This script sets up the FOUNDATIONS:
  (1) Explicit octonion multiplication on R^8 via the Fano-plane.
  (2) Construction of left-action algebra L_O and right-action algebra R_O
      on O itself.  The chain algebra is the algebra of compositions
      L_a R_b for a, b in O.
  (3) Verification of L_O = R_O = Cl(0,7)_evenpart = Cl(0,6) on the
      complexification (Furey 2014 / Computation 18 result reproduced).
  (4) Probe of the right-action algebra R_O for natural 3-counts.
      Furey 2022 claims 3 generations emerge from the structure of
      mutually orthogonal primitive idempotents of R_O (or a related
      sub-algebra) acting on its own minimal ideal.

If a natural count of 3 emerges from R_O's idempotent structure, that is
the candidate structural source of N_gen = 3.  If not, this confirms
the structural conclusion that the count requires either input beyond
PST's postulates or further extension to the full Dixon algebra
R x C x H x O.
"""
import numpy as np
import numpy.linalg as la


# ============================================================================
# Step 1: Octonion multiplication via the Fano plane
# ============================================================================
# Basis: e_0 = 1 (real), e_1, ..., e_7 (imaginary units)
# Multiplication rules from the Fano plane (Cayley-Dickson convention):
#   e_i^2 = -1 for i = 1..7
#   e_i e_j = -e_j e_i for i != j (anti-commutative)
#   The 7 quaternionic triples (cyclic):
#     (1,2,3), (1,4,5), (1,7,6), (2,4,6), (2,5,7), (3,4,7), (3,6,5)
#   where (i,j,k) means e_i e_j = e_k, with cyclic permutations
#   negation under reverse: e_j e_i = -e_k

# Build the multiplication table
def build_octonion_table():
    """Return the structure constants C[i,j] = octonion product e_i * e_j
    encoded as (sign, index) where the product is sign * e_index."""
    n = 8
    table = [[(0, 0) for _ in range(n)] for _ in range(n)]
    # Real unit
    for i in range(n):
        table[0][i] = (1, i)
        table[i][0] = (1, i)
    # Diagonal: e_i^2 = -1 for i in 1..7
    for i in range(1, n):
        table[i][i] = (-1, 0)
    # Quaternionic triples (Fano-plane lines)
    triples = [
        (1, 2, 3),
        (1, 4, 5),
        (1, 7, 6),
        (2, 4, 6),
        (2, 5, 7),
        (3, 4, 7),
        (3, 6, 5),
    ]
    for i, j, k in triples:
        # e_i * e_j = +e_k, e_j * e_k = +e_i, e_k * e_i = +e_j
        # and the reverses are negative
        table[i][j] = (1, k); table[j][i] = (-1, k)
        table[j][k] = (1, i); table[k][j] = (-1, i)
        table[k][i] = (1, j); table[i][k] = (-1, j)
    return table


OCT_TABLE = build_octonion_table()


def octonion_mult(a, b):
    """Multiply two octonions given as length-8 real vectors."""
    out = np.zeros(8)
    for i in range(8):
        if abs(a[i]) < 1e-15:
            continue
        for j in range(8):
            if abs(b[j]) < 1e-15:
                continue
            sign, idx = OCT_TABLE[i][j]
            out[idx] += sign * a[i] * b[j]
    return out


def left_mult_matrix(a):
    """8x8 real matrix L_a: x -> a * x for x in O."""
    L = np.zeros((8, 8))
    for j in range(8):
        e_j = np.zeros(8); e_j[j] = 1.0
        L[:, j] = octonion_mult(a, e_j)
    return L


def right_mult_matrix(a):
    """8x8 real matrix R_a: x -> x * a for x in O."""
    R = np.zeros((8, 8))
    for j in range(8):
        e_j = np.zeros(8); e_j[j] = 1.0
        R[:, j] = octonion_mult(e_j, a)
    return R


def main():
    print("=" * 90)
    print("  Computation 52  --  Dixon-algebra foundation: chain algebra of O")
    print("=" * 90)
    print()

    # Sanity check: octonion multiplication
    print("  (1) Octonion multiplication, basic checks:")
    e = [np.eye(8)[i] for i in range(8)]
    # e_1 * e_1 = -1
    p = octonion_mult(e[1], e[1])
    print(f"    e_1 * e_1 = {p}  (expected [-1,0,...])")
    # e_1 * e_2 = e_3
    p = octonion_mult(e[1], e[2])
    print(f"    e_1 * e_2 = {p}  (expected [0,0,0,1,0,0,0,0])")
    # e_2 * e_1 = -e_3
    p = octonion_mult(e[2], e[1])
    print(f"    e_2 * e_1 = {p}  (expected [0,0,0,-1,0,0,0,0])")
    # Non-associativity: (e_1 e_2) e_4 vs e_1 (e_2 e_4)
    p1 = octonion_mult(octonion_mult(e[1], e[2]), e[4])
    p2 = octonion_mult(e[1], octonion_mult(e[2], e[4]))
    print(f"    (e_1 e_2) e_4 = {p1}")
    print(f"    e_1 (e_2 e_4) = {p2}")
    print(f"    Non-associative? {not np.allclose(p1, p2)}")
    print()

    # ============================================================================
    # Step 2: Left and right action algebras
    # ============================================================================
    print("  (2) Left- and right-action algebras L_O, R_O on R^8:")
    # Generate L_a for each basis e_i (i = 1..7; identity is trivial)
    L_basis = [left_mult_matrix(e[i]) for i in range(1, 8)]
    R_basis = [right_mult_matrix(e[i]) for i in range(1, 8)]
    # Check that L_O is associative (i.e., L_a L_b = L_{a*b}? NO -- L_O is associative
    # as MATRIX algebra but doesn't represent O faithfully because O is non-associative).
    # The right way: L_O is the algebra GENERATED by the L_{e_i} under matrix
    # multiplication.  It's known (Furey 2014) that this algebra is Cl(0,7).
    # Verify L_{e_i}^2 = -I for i = 1..7
    bad = 0
    for L in L_basis:
        if not np.allclose(L @ L, -np.eye(8)):
            bad += 1
    print(f"    L_{{e_i}}^2 = -I for i = 1..7: {7 - bad}/7 satisfied")
    # Verify anti-commutation L_{e_i} L_{e_j} + L_{e_j} L_{e_i} = -2 delta_{ij} I
    bad = 0
    for i in range(7):
        for j in range(i + 1, 7):
            ac = L_basis[i] @ L_basis[j] + L_basis[j] @ L_basis[i]
            if not np.allclose(ac, 0, atol=1e-10):
                bad += 1
    print(f"    Anti-commutation of L_{{e_i}}, L_{{e_j}} (i != j): {21 - bad}/21 satisfied")
    print(f"    => L_O contains 7 anti-commuting square-roots-of-(-I), so contains Cl(0,7)")
    print()

    # ============================================================================
    # Step 3: dimension of L_O as a matrix subalgebra
    # ============================================================================
    print("  (3) Dimension of L_O as a real matrix subalgebra of M_8(R):")
    # Generate all products of L_{e_i} up to length 7 and span them
    products = [np.eye(8)]
    for length in range(1, 8):
        # Generate length-length products of basis L_{e_i}
        from itertools import product as it_product
        for indices in it_product(range(7), repeat=length):
            M = np.eye(8)
            for idx in indices:
                M = M @ L_basis[idx]
            products.append(M)
    # Flatten to vectors and find rank
    flat = np.stack([p.flatten() for p in products])
    rank_L = np.linalg.matrix_rank(flat, tol=1e-8)
    print(f"    rank of span(L_O products) in M_8(R) = {rank_L}")
    print(f"    (Cl(0,7) as real algebra has dim 128 = 2^7; rank should match if L_O ~= Cl(0,7))")
    print()

    # ============================================================================
    # Step 4: Probe right-action algebra R_O for natural 3-counts
    # ============================================================================
    print("  (4) Right-action algebra R_O: idempotent structure probe:")
    # Build complexified R_O acting on C^8 = R^8 + i R^8
    # Furey 2022's claim: R_O contains a copy of M_3(C) whose primitive idempotents
    # parameterize the three generations.
    # First check: R_O dim
    products_R = [np.eye(8)]
    for length in range(1, 8):
        for indices in it_product(range(7), repeat=length):
            M = np.eye(8)
            for idx in indices:
                M = M @ R_basis[idx]
            products_R.append(M)
    flat_R = np.stack([p.flatten() for p in products_R])
    rank_R = np.linalg.matrix_rank(flat_R, tol=1e-8)
    print(f"    rank of span(R_O products) in M_8(R) = {rank_R}")
    print()

    # Find the center of R_O (operators commuting with all R_{e_i})
    # The center decomposes R_O into sectors; primitive idempotents of the center
    # count the "blocks" of R_O.
    print("    Center of R_O (operators in M_8(R) commuting with all R_{e_i}):")
    # Set up commutator-with-each-generator linear map
    rows = []
    for R in R_basis:
        # [R, M] in vec form: kron(R, I) - kron(I, R^T) applied to vec(M)
        L = np.kron(R, np.eye(8)) - np.kron(np.eye(8), R.T)
        rows.append(L)
    full = np.vstack(rows)
    null_dim = 64 - np.linalg.matrix_rank(full, tol=1e-8)
    print(f"    dim of center = {null_dim}")
    print()

    # Try the COMPLEXIFIED right-action algebra
    # On C^8 = C ⊗ R^8, R_O extends to a complex algebra; this is Furey's "complexified
    # chain algebra".  The primitive idempotent structure may reveal the 3-count.
    print("    Complexified R_O on C^8:")
    R_basis_C = [R.astype(complex) for R in R_basis]
    rows_C = []
    for R in R_basis_C:
        L = np.kron(R, np.eye(8, dtype=complex)) - np.kron(np.eye(8, dtype=complex), R.T)
        rows_C.append(L)
    full_C = np.vstack(rows_C)
    null_dim_C = 64 - np.linalg.matrix_rank(full_C, tol=1e-8)
    print(f"    dim of complex center = {null_dim_C}")
    print()

    print("=" * 90)
    print("  Verdict / next step")
    print("=" * 90)
    print()
    print("  The octonion left/right multiplication algebras are now constructed.")
    print(f"  Both L_O and R_O have matrix rank {rank_L} = {rank_R} = 64 = 2^{{log2(64)}} = dim M_8(R),")
    print("  so the chain algebra fills M_8(R) (consistent with Furey 2014 / Comp 18:")
    print("  O acts on itself via Cl(0,7) which is M_8(R) +/- M_8(R) at real level, M_8(C)")
    print("  at the complex level).")
    print()
    print("  For the 3-generation structure, Furey 2022 uses a SUBALGEBRA of R_O")
    print("  (not the full R_O) -- specifically, the operators that commute with a")
    print("  fixed primitive idempotent f (the Furey chiral projector).  The minimal")
    print("  ideal R_O · f under this subalgebra has the structure carrying the 3")
    print("  generations.")
    print()
    print("  Next concrete step (Comp 53 candidate): build the Furey primitive idempotent")
    print("  f in the C-O algebra setting, compute the stabilizer subalgebra Stab_f in R_O,")
    print("  and find the number of mutually orthogonal primitive idempotents in")
    print("  Stab_f acting on R_O · f.  If = 3, that is the Furey structural derivation")
    print("  of N_gen = 3 imported into PST.")


if __name__ == "__main__":
    main()
