pub type E8Vec = [i64; 8];
#[must_use]
pub fn dot(a: &E8Vec, b: &E8Vec) -> i64 {
let mut s = 0i64;
for i in 0..8 {
s += a[i] * b[i];
}
s
}
#[must_use]
pub fn norm2(v: &E8Vec) -> i64 {
dot(v, v)
}
#[must_use]
pub fn in_e8(v: &E8Vec) -> bool {
let all_even = v.iter().all(|x| x.rem_euclid(2) == 0);
let all_odd = v.iter().all(|x| x.rem_euclid(2) == 1);
if !all_even && !all_odd {
return false;
}
v.iter().sum::<i64>().rem_euclid(4) == 0
}
#[must_use]
pub fn roots() -> Vec<E8Vec> {
let mut out = Vec::with_capacity(240);
for i in 0..8 {
for j in (i + 1)..8 {
for &si in &[2i64, -2] {
for &sj in &[2i64, -2] {
let mut v = [0i64; 8];
v[i] = si;
v[j] = sj;
out.push(v);
}
}
}
}
for mask in 0u32..256 {
if mask.count_ones() % 2 != 0 {
continue;
}
let mut v = [0i64; 8];
for (i, e) in v.iter_mut().enumerate() {
*e = if mask & (1 << i) == 0 { 1 } else { -1 };
}
out.push(v);
}
out
}
#[must_use]
pub fn simple_roots() -> [E8Vec; 8] {
[
[1, -1, -1, -1, -1, -1, -1, 1],
[2, 2, 0, 0, 0, 0, 0, 0],
[-2, 2, 0, 0, 0, 0, 0, 0],
[0, -2, 2, 0, 0, 0, 0, 0],
[0, 0, -2, 2, 0, 0, 0, 0],
[0, 0, 0, -2, 2, 0, 0, 0],
[0, 0, 0, 0, -2, 2, 0, 0],
[0, 0, 0, 0, 0, -2, 2, 0],
]
}
#[must_use]
pub fn reflect(v: &E8Vec, r: &E8Vec) -> E8Vec {
let d = dot(v, r);
debug_assert_eq!(norm2(r), 8, "reflect expects a root (doubled norm 8), got {}", norm2(r));
debug_assert_eq!(d.rem_euclid(4), 0, "<v,r> must be divisible by 4 for a lattice v; got {d}");
let k = d / 4;
let mut out = *v;
for i in 0..8 {
out[i] -= k * r[i];
}
out
}
#[must_use]
pub fn is_dominant(v: &E8Vec) -> bool {
simple_roots().iter().all(|a| dot(v, a) >= 0)
}
#[must_use]
pub fn canonicalize(v: &E8Vec) -> E8Vec {
let simples = simple_roots();
let mut cur = *v;
const MAX_STEPS: usize = 480;
for _ in 0..MAX_STEPS {
let mut moved = false;
for a in &simples {
if dot(&cur, a) < 0 {
cur = reflect(&cur, a);
moved = true;
}
}
if !moved {
return cur;
}
}
panic!("E8 canonicalize did not reach the dominant chamber in {MAX_STEPS} steps for {v:?}");
}
#[must_use]
pub fn same_orbit(a: &E8Vec, b: &E8Vec) -> bool {
canonicalize(a) == canonicalize(b)
}
#[must_use]
pub fn snap(v: &E8Vec) -> E8Vec {
let a = snap_coset(v, 0);
let b = snap_coset(v, 1);
if dist2(v, &a) <= dist2(v, &b) { a } else { b }
}
fn dist2(a: &E8Vec, b: &E8Vec) -> i64 {
let mut s = 0i64;
for i in 0..8 {
let d = a[i] - b[i];
s += d * d;
}
s
}
fn snap_coset(v: &E8Vec, parity: i64) -> E8Vec {
let mut out = [0i64; 8];
let (mut worst_i, mut worst_cost, mut worst_dir) = (0usize, -1i64, 0i64);
for i in 0..8 {
let x = v[i];
let mut r = if x.rem_euclid(2) == parity { x } else { x + 1 };
if (x - (r - 2)).abs() < (x - r).abs() {
r -= 2;
}
out[i] = r;
let cost = (x - r).abs();
if cost > worst_cost {
worst_cost = cost;
worst_i = i;
worst_dir = if x >= r { 2 } else { -2 };
}
}
if out.iter().sum::<i64>().rem_euclid(4) != 0 {
out[worst_i] += worst_dir;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_root_system_is_240_vectors_of_norm_2_closed_under_negation() {
let rs = roots();
assert_eq!(rs.len(), 240, "E8 has exactly 240 roots");
let set: std::collections::BTreeSet<E8Vec> = rs.iter().copied().collect();
assert_eq!(set.len(), 240, "the roots are distinct");
for r in &rs {
assert_eq!(norm2(r), 8, "every root has doubled norm 8 (true norm 2): {r:?}");
assert!(in_e8(r), "every root is a lattice point: {r:?}");
assert!(set.contains(&r.map(|x| -x)), "the root system is closed under negation: {r:?}");
}
}
#[test]
fn simple_roots_are_roots_and_define_a_one_sided_chamber() {
let set: std::collections::BTreeSet<E8Vec> = roots().into_iter().collect();
for a in &simple_roots() {
assert_eq!(norm2(a), 8, "simple root has norm 2: {a:?}");
assert!(set.contains(a), "simple root is in the root system: {a:?}");
}
let dom = canonicalize(&[2, 4, 6, 8, 10, 12, 14, 16]);
assert!(is_dominant(&dom));
assert!(!is_dominant(&dom.map(|x| -x)), "the chamber is one-sided, not everything");
}
#[test]
fn all_240_roots_are_one_orbit() {
let rs = roots();
let first = canonicalize(&rs[0]);
assert!(is_dominant(&first), "the representative is dominant: {first:?}");
for r in &rs {
let c = canonicalize(r);
assert_eq!(c, first, "root {r:?} canonicalized to {c:?}, expected the single root orbit {first:?}");
}
assert_eq!(norm2(&first), 8, "the orbit did not leave the root shell");
}
#[test]
fn canonicalize_is_idempotent_norm_preserving_and_lands_dominant() {
let mut s = 0x9E37_79B9_7F4A_7C15u64;
let mut next = || {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
(s % 21) as i64 - 10
};
for _ in 0..200 {
let mut v = [0i64; 8];
for e in &mut v {
*e = next() * 2;
}
let v = snap(&v);
let c = canonicalize(&v);
assert!(is_dominant(&c), "canonical form is dominant: {v:?} -> {c:?}");
assert_eq!(canonicalize(&c), c, "canonicalization is idempotent for {v:?}");
assert_eq!(norm2(&c), norm2(&v), "reflections are isometries - the norm is preserved");
}
}
#[test]
fn a_vector_and_its_weyl_images_share_one_representative() {
let rs = roots();
let mut s = 0xDEAD_BEEF_1234_5678u64;
let mut next = |n: u64| {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
(s % n) as usize
};
for _ in 0..100 {
let mut v = [0i64; 8];
for e in &mut v {
*e = (next(11) as i64 - 5) * 2;
}
let v = snap(&v);
assert!(in_e8(&v), "snap produces a lattice point: {v:?}");
let base = canonicalize(&v);
let mut w = v;
for _ in 0..6 {
w = reflect(&w, &rs[next(240)]);
}
assert!(in_e8(&w), "the group preserves the lattice: {w:?}");
assert_eq!(canonicalize(&w), base, "a Weyl image shares the representative: {v:?} -> {w:?}");
}
}
#[test]
fn snap_reaches_the_lattice_and_fixes_lattice_points() {
for r in roots() {
assert_eq!(snap(&r), r, "a lattice point is its own snap: {r:?}");
}
let mut s = 0x0123_4567_89AB_CDEFu64;
let mut next = || {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
(s % 41) as i64 - 20
};
for _ in 0..300 {
let mut v = [0i64; 8];
for e in &mut v {
*e = next();
}
let p = snap(&v);
assert!(in_e8(&p), "snap({v:?}) = {p:?} must be a lattice point");
}
}
#[test]
fn membership_rejects_mixed_parity_and_odd_sums() {
assert!(in_e8(&[2, 2, 0, 0, 0, 0, 0, 0]), "an integer root is in");
assert!(in_e8(&[1, 1, 1, 1, 1, 1, 1, 1]), "the all-halves vector with even sum is in");
assert!(!in_e8(&[1, 2, 0, 0, 0, 0, 0, 0]), "mixed parity is out");
assert!(!in_e8(&[3, 1, 1, 1, 1, 1, 1, 1]), "all-odd with an odd true sum is out");
assert!(in_e8(&[3, 1, 1, 1, -1, -1, -1, 1]), "all-odd with an even true sum is in");
}
}