# Independent E6 model (G5 audit): Bourbaki realization inside R^8, exact Fractions.
# Roots: +-e_i+-e_j (1<=i<j<=5) and +-1/2(e8-e7-e6+sum_{i<=5} (-1)^{nu_i} e_i), sum nu_i even.
# Simple roots (Bourbaki): a1=1/2(e1+e8)-1/2(e2+...+e7), a2=e1+e2, a3=e2-e1, a4=e3-e2, a5=e4-e3, a6=e5-e4.
from fractions import Fraction as F
from itertools import product, combinations
H = F(1, 2)
def vec(d):
    v = [F(0)] * 8
    for i, c in d.items(): v[i - 1] = F(c)
    return tuple(v)
def add(a, b): return tuple(x + y for x, y in zip(a, b))
def sub(a, b): return tuple(x - y for x, y in zip(a, b))
def sc(c, a): return tuple(F(c) * x for x in a)
def dot(a, b): return sum(x * y for x, y in zip(a, b))
roots = set()
for i, j in combinations(range(1, 6), 2):
    for si, sj in product([1, -1], repeat=2):
        roots.add(vec({i: si, j: sj}))
for nus in product([0, 1], repeat=5):
    if sum(nus) % 2 == 0:
        v = [F(0)] * 8
        for i in range(5): v[i] = H * (-1) ** nus[i]
        v[5] = -H; v[6] = -H; v[7] = H
        roots.add(tuple(v)); roots.add(tuple(-x for x in v))
roots = sorted(roots)
assert len(roots) == 72 and all(dot(r, r) == 2 for r in roots)
al = {1: tuple([H, -H, -H, -H, -H, -H, -H, H]), 2: vec({1: 1, 2: 1}), 3: vec({2: 1, 1: -1}),
      4: vec({3: 1, 2: -1}), 5: vec({4: 1, 3: -1}), 6: vec({5: 1, 4: -1})}
assert all(al[i] in roots for i in al)
CART = [[dot(al[i], al[j]) for j in range(1, 7)] for i in range(1, 7)]
# expected Bourbaki Cartan matrix: chain 1-3-4-5-6, 2 attached to 4
EXP = [[2,0,-1,0,0,0],[0,2,0,-1,0,0],[-1,0,2,-1,0,0],[0,-1,-1,2,-1,0],[0,0,0,-1,2,-1],[0,0,0,0,-1,2]]
assert CART == EXP, CART
def refl(r, v): return sub(v, sc(dot(r, v), r))   # roots have norm 2 so alpha^vee = alpha
# express a vector of the E6 span in simple-root coordinates (solve Gram system)
def solve(M, b):
    n = len(M); A = [list(M[i]) + [b[i]] for i in range(n)]
    for c in range(n):
        p = next(r for r in range(c, n) if A[r][c] != 0); A[c], A[p] = A[p], A[c]
        pv = A[c][c]; A[c] = [x / pv for x in A[c]]
        for r in range(n):
            if r != c and A[r][c] != 0:
                f = A[r][c]; A[r] = [x - f * y for x, y in zip(A[r], A[c])]
    return [A[i][n] for i in range(n)]
G = [[dot(al[i], al[j]) for j in range(1, 7)] for i in range(1, 7)]
def coords(v):  # v = sum c_i alpha_i  (v must lie in the span)
    c = solve(G, [dot(v, al[i]) for i in range(1, 7)])
    w = (F(0),) * 8
    for i in range(6): w = add(w, sc(c[i], al[i + 1]))
    assert w == tuple(v), "vector not in E6 span"
    return c
# fundamental weights (= fundamental coweights, simply laced): <om_i, al_j> = delta_ij, in the span
om = {}
for i in range(1, 7):
    c = solve(G, [F(1 if j == i else 0) for j in range(1, 7)])
    w = (F(0),) * 8
    for k in range(6): w = add(w, sc(c[k], al[k + 1]))
    om[i] = w
rho = (F(0),) * 8
for i in range(1, 7): rho = add(rho, om[i])
pos = [r for r in roots if all(x >= 0 for x in coords(r))]
assert len(pos) == 36
assert sc(H, tuple(sum(r[k] for r in pos) for k in range(8))) == rho
def fund(v): return tuple(dot(v, al[i]) for i in range(1, 7))   # fundamental-weight coordinates
