use crate::forms::IntegralForm;
use crate::linalg::integer::normalize_relation_rows;
pub const E8_WEYL_GROUP_ORDER: u128 = 696_729_600;
fn cartan(n: usize, edges: &[(usize, usize)]) -> IntegralForm {
let mut g = vec![vec![0i128; n]; n];
for (i, row) in g.iter_mut().enumerate() {
row[i] = 2;
}
for &(a, b) in edges {
g[a][b] = -1;
g[b][a] = -1;
}
IntegralForm::new(g).expect("Cartan matrix is symmetric")
}
fn gram_from_basis(b: &[Vec<i128>]) -> IntegralForm {
let n = b.len();
let mut g = vec![vec![0i128; n]; n];
for i in 0..n {
for j in 0..n {
g[i][j] = b[i].iter().zip(&b[j]).map(|(&x, &y)| x * y).sum();
}
}
IntegralForm::new(g).expect("Gram matrix B·Bᵀ is symmetric")
}
pub fn a_n(n: usize) -> Option<IntegralForm> {
if n < 1 {
return None;
}
let edges: Vec<(usize, usize)> = (0..n.saturating_sub(1)).map(|i| (i, i + 1)).collect();
Some(cartan(n, &edges))
}
pub fn d_n(n: usize) -> Option<IntegralForm> {
if n < 2 {
return None;
}
let mut b = vec![vec![0i128; n]; n];
for i in 0..n - 1 {
b[i][i] = 1;
b[i][i + 1] = -1;
}
b[n - 1][n - 2] = 1;
b[n - 1][n - 1] = 1;
Some(gram_from_basis(&b))
}
pub fn e_6() -> IntegralForm {
cartan(6, &[(0, 1), (1, 2), (2, 3), (3, 4), (2, 5)])
}
pub fn e_7() -> IntegralForm {
cartan(7, &[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (2, 6)])
}
pub fn e_8() -> IntegralForm {
cartan(8, &[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (4, 7)])
}
pub fn coxeter_number(l: &IntegralForm) -> Option<i128> {
if l.minimum()? != 2 {
return None;
}
let roots = l.kissing_number()? as i128;
let n = l.dim() as i128;
if n == 0 || roots % n != 0 {
return None;
}
Some(roots / n)
}
pub fn is_root_lattice(l: &IntegralForm) -> bool {
if !l.is_positive_definite() {
return false;
}
let Some(min) = l.minimum() else {
return false;
};
if min != 2 {
return false;
}
let Some(roots) = l.minimal_vectors() else {
return false;
};
let n = l.dim();
let hnf = normalize_relation_rows(roots);
if hnf.len() != n {
return false; }
let mut index = 1i128;
for (i, row) in hnf.iter().enumerate() {
index *= row[i]; }
index == 1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_n_invariants() {
for n in 1..=5 {
let l = a_n(n).unwrap();
assert_eq!(l.determinant(), n as i128 + 1, "det A_{n}");
assert_eq!(l.minimum(), Some(2));
assert_eq!(l.kissing_number(), Some(n * (n + 1)), "kissing A_{n}");
assert_eq!(coxeter_number(&l), Some(n as i128 + 1));
assert!(is_root_lattice(&l));
}
assert_eq!(a_n(1).unwrap().automorphism_group_order(), Some(2));
assert_eq!(a_n(2).unwrap().automorphism_group_order(), Some(12)); assert_eq!(a_n(3).unwrap().automorphism_group_order(), Some(48)); assert_eq!(a_n(4).unwrap().automorphism_group_order(), Some(240)); assert_eq!(a_n(0), None);
}
#[test]
fn d_n_invariants() {
for n in 2..=6 {
let l = d_n(n).unwrap();
assert_eq!(l.determinant(), 4, "det D_{n}");
if n >= 3 {
assert_eq!(l.minimum(), Some(2));
assert_eq!(l.kissing_number(), Some(2 * n * (n - 1)), "kissing D_{n}");
assert_eq!(coxeter_number(&l), Some(2 * n as i128 - 2));
assert!(is_root_lattice(&l));
}
}
assert_eq!(
d_n(2).unwrap().gram(),
IntegralForm::diagonal(&[2, 2]).gram()
);
assert_eq!(d_n(4).unwrap().automorphism_group_order(), Some(1152));
assert_eq!(d_n(5).unwrap().automorphism_group_order(), Some(3840));
assert_eq!(d_n(0), None);
assert_eq!(d_n(1), None);
}
#[test]
fn e_series_invariants() {
let e6 = e_6();
assert_eq!(e6.dim(), 6);
assert_eq!(e6.determinant(), 3);
assert!(e6.is_even());
assert_eq!(e6.automorphism_group_order_bounded(1), Some(103_680));
let e7 = e_7();
assert_eq!(e7.dim(), 7);
assert_eq!(e7.determinant(), 2);
assert!(e7.is_even());
assert_eq!(e7.automorphism_group_order_bounded(1), Some(2_903_040));
let e8 = e_8();
assert_eq!(e8.dim(), 8);
assert_eq!(e8.determinant(), 1);
assert!(e8.is_unimodular());
assert!(e8.is_even());
assert_eq!(e8.level(), Some(1));
assert_eq!(
e8.automorphism_group_order_bounded(1),
Some(E8_WEYL_GROUP_ORDER)
);
}
#[test]
fn root_lattice_predicate() {
assert!(!is_root_lattice(&IntegralForm::diagonal(&[1, 1, 1])));
assert!(!is_root_lattice(&IntegralForm::diagonal(&[4, 4])));
assert!(!is_root_lattice(&IntegralForm::diagonal(&[2, 8])));
assert!(is_root_lattice(&IntegralForm::diagonal(&[2, 2, 2])));
}
#[test]
fn e8_is_the_unique_even_unimodular_rank8() {
let e8 = e_8();
assert!(e8.is_even() && e8.is_unimodular() && e8.dim() == 8);
assert_eq!(e8.level(), Some(1));
}
}