use super::error::LatticeGeometryError;
use super::flint_backend;
use super::lattice::Lattice;
use super::lll::lattice_reduce_rows;
use rug::{Integer, Rational};
fn int_rows(rows: Vec<Vec<i64>>) -> Vec<Vec<Integer>> {
rows.into_iter()
.map(|r| r.into_iter().map(Integer::from).collect())
.collect()
}
pub fn zn(n: usize) -> Result<Lattice, LatticeGeometryError> {
if n == 0 {
return Err(LatticeGeometryError::InvalidParameter {
detail: "Z^n needs n >= 1",
});
}
let rows: Vec<Vec<i64>> = (0..n)
.map(|i| (0..n).map(|j| i64::from(i == j)).collect())
.collect();
Lattice::from_basis(&int_rows(rows))
}
pub fn a_n(n: usize) -> Result<Lattice, LatticeGeometryError> {
if n == 0 {
return Err(LatticeGeometryError::InvalidParameter {
detail: "A_n needs n >= 1",
});
}
let rows: Vec<Vec<i64>> = (0..n)
.map(|i| {
let mut r = vec![0i64; n + 1];
r[i] = 1;
r[i + 1] = -1;
r
})
.collect();
Lattice::from_basis(&int_rows(rows))
}
pub fn d_n(n: usize) -> Result<Lattice, LatticeGeometryError> {
if n < 2 {
return Err(LatticeGeometryError::InvalidParameter {
detail: "D_n needs n >= 2",
});
}
let mut rows: Vec<Vec<i64>> = (0..n - 1)
.map(|i| {
let mut r = vec![0i64; n];
r[i] = 1;
r[i + 1] = -1;
r
})
.collect();
let mut last = vec![0i64; n];
last[n - 2] = 1;
last[n - 1] = 1;
rows.push(last);
Lattice::from_basis(&int_rows(rows))
}
pub fn e8() -> Result<Lattice, LatticeGeometryError> {
let half = Rational::from((1, 2));
let mut rows: Vec<Vec<Rational>> = Vec::with_capacity(8);
rows.push(
[1i64, -1, -1, -1, -1, -1, -1, 1]
.iter()
.map(|&s| Rational::from(s) * &half)
.collect(),
);
let mut push_int = |v: [i64; 8]| rows.push(v.iter().map(|&x| Rational::from(x)).collect());
push_int([1, 1, 0, 0, 0, 0, 0, 0]); push_int([-1, 1, 0, 0, 0, 0, 0, 0]); push_int([0, -1, 1, 0, 0, 0, 0, 0]); push_int([0, 0, -1, 1, 0, 0, 0, 0]); push_int([0, 0, 0, -1, 1, 0, 0, 0]); push_int([0, 0, 0, 0, -1, 1, 0, 0]); push_int([0, 0, 0, 0, 0, -1, 1, 0]); Lattice::from_rational_basis(&rows)
}
const GOLAY_G: [usize; 7] = [0, 2, 4, 5, 6, 10, 11];
fn golay_generators() -> [u32; 12] {
let mut rows = [0u32; 12];
for (i, row) in rows.iter_mut().enumerate() {
let mut w = 0u32;
for d in GOLAY_G {
w |= 1 << (d + i);
}
if w.count_ones() % 2 == 1 {
w |= 1 << 23;
}
*row = w;
}
rows
}
fn golay_codewords() -> Option<Vec<u32>> {
let gens = golay_generators();
let mut words = Vec::with_capacity(4096);
for mask in 0u32..4096 {
let mut w = 0u32;
for (i, g) in gens.iter().enumerate() {
if mask >> i & 1 == 1 {
w ^= g;
}
}
words.push(w);
}
let mut dist = [0usize; 25];
for w in &words {
dist[w.count_ones() as usize] += 1;
}
let mut expected = [0usize; 25];
expected[0] = 1;
expected[8] = 759;
expected[12] = 2576;
expected[16] = 759;
expected[24] = 1;
if dist != expected {
return None;
}
Some(words)
}
pub fn leech() -> Result<Lattice, LatticeGeometryError> {
const REFUSAL: LatticeGeometryError = LatticeGeometryError::InvalidParameter {
detail: "Leech lattice construction failed its own verification",
};
let words = golay_codewords().ok_or(REFUSAL)?;
let gens = golay_generators();
debug_assert!(words.len() == 4096);
let mut rows: Vec<Vec<i64>> = Vec::new();
for i in 0..24 {
let mut r = vec![0i64; 24];
r[i] = 8;
rows.push(r);
}
for i in 1..24 {
let mut r = vec![0i64; 24];
r[0] = 4;
r[i] = 4;
rows.push(r);
}
for g in gens {
let mut r = vec![0i64; 24];
for (i, e) in r.iter_mut().enumerate() {
if g >> i & 1 == 1 {
*e = 2;
}
}
rows.push(r);
}
let mut odd = vec![1i64; 24];
odd[0] = -3;
rows.push(odd);
let basis = flint_backend::hnf_basis(&int_rows(rows), 24);
if basis.len() != 24 {
return Err(REFUSAL);
}
let basis = lattice_reduce_rows(&basis)?;
let eighth = Rational::from((1, 8));
let mut gram = vec![vec![Rational::new(); 24]; 24];
for i in 0..24 {
for j in 0..=i {
let mut acc = Integer::new();
for (a, b) in basis[i].iter().zip(basis[j].iter()) {
acc += Integer::from(a * b);
}
let v = Rational::from(acc) * &eighth;
if *v.denom() != 1i32 {
return Err(REFUSAL);
}
gram[i][j] = v.clone();
gram[j][i] = v;
}
}
let lattice = Lattice::from_gram(&gram)?;
if lattice.determinant() != 1 || !lattice.is_even() {
return Err(REFUSAL);
}
Ok(lattice)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn golay_code_has_the_right_weight_enumerator() {
let words = golay_codewords().expect("generated code is the Golay code");
assert_eq!(words.len(), 4096);
let gens = golay_generators();
for a in gens {
for b in gens {
assert_eq!((a & b).count_ones() % 2, 0, "G24 must be self-orthogonal");
}
}
assert!(words.contains(&0x00FF_FFFF));
}
}