use crate::forms::integral::diagonal::{
rational_congruence_diagonal, signature_from_diagonal, DegenerateBehavior,
};
use crate::linalg::field::inverse_matrix;
use crate::linalg::integer::{gcd, smith_normal_form};
use crate::scalar::{Nimber, Rational, Scalar};
use std::collections::BTreeMap;
pub(super) fn lcm_i128(a: i128, b: i128) -> i128 {
if a == 0 || b == 0 {
return 0;
}
let g = gcd(a, b);
(a / g)
.checked_mul(b)
.expect("lattice level exceeds i128")
.abs()
}
pub(super) fn bareiss_det(mut a: Vec<Vec<i128>>) -> i128 {
let n = a.len();
if n == 0 {
return 1;
}
let mut sign = 1i128;
let mut prev = 1i128;
for k in 0..n - 1 {
if a[k][k] == 0 {
match (k + 1..n).find(|&r| a[r][k] != 0) {
Some(r) => {
a.swap(k, r);
sign = -sign;
}
None => return 0,
}
}
for i in k + 1..n {
for j in k + 1..n {
let p1 = a[i][j]
.checked_mul(a[k][k])
.expect("Bareiss determinant exceeds i128");
let p2 = a[i][k]
.checked_mul(a[k][j])
.expect("Bareiss determinant exceeds i128");
a[i][j] = (p1 - p2) / prev; }
}
prev = a[k][k];
}
sign * a[n - 1][n - 1]
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IntegralForm {
pub(super) gram: Vec<Vec<i128>>,
}
impl IntegralForm {
pub fn new(gram: Vec<Vec<i128>>) -> Option<Self> {
let n = gram.len();
if gram.iter().any(|row| row.len() != n) {
return None;
}
for i in 0..n {
for j in 0..n {
if gram[i][j] != gram[j][i] {
return None;
}
}
}
Some(IntegralForm { gram })
}
pub fn diagonal(diag: &[i128]) -> Self {
let n = diag.len();
let mut gram = vec![vec![0i128; n]; n];
for (i, &d) in diag.iter().enumerate() {
gram[i][i] = d;
}
IntegralForm { gram }
}
pub fn dim(&self) -> usize {
self.gram.len()
}
pub fn gram(&self) -> &[Vec<i128>] {
&self.gram
}
pub fn inner(&self, x: &[i128], y: &[i128]) -> i128 {
let n = self.dim();
debug_assert_eq!(x.len(), n);
debug_assert_eq!(y.len(), n);
let mut acc = 0i128;
for i in 0..n {
if x[i] == 0 {
continue;
}
let mut row = 0i128;
for j in 0..n {
row = row
.checked_add(
self.gram[i][j]
.checked_mul(y[j])
.expect("lattice inner product exceeds i128"),
)
.expect("lattice inner product exceeds i128");
}
acc = acc
.checked_add(x[i].checked_mul(row).expect("lattice norm exceeds i128"))
.expect("lattice norm exceeds i128");
}
acc
}
pub fn norm(&self, x: &[i128]) -> i128 {
self.inner(x, x)
}
pub fn determinant(&self) -> i128 {
bareiss_det(self.gram.clone())
}
pub fn is_unimodular(&self) -> bool {
self.determinant().abs() == 1
}
pub fn is_even(&self) -> bool {
(0..self.dim()).all(|i| self.gram[i][i] % 2 == 0)
}
pub fn is_positive_definite(&self) -> bool {
let n = self.dim();
for k in 1..=n {
let minor: Vec<Vec<i128>> = (0..k).map(|i| self.gram[i][..k].to_vec()).collect();
if bareiss_det(minor) <= 0 {
return false;
}
}
true
}
pub fn signature(&self) -> (usize, usize) {
let diag = rational_congruence_diagonal(&self.gram, DegenerateBehavior::StopAtRadical);
signature_from_diagonal(&diag)
}
pub fn invariant_factors(&self) -> Vec<i128> {
smith_normal_form(self.gram.clone())
}
pub fn level(&self) -> Option<i128> {
let n = self.dim();
if n == 0 {
return Some(1);
}
let mat: Vec<Vec<Rational>> = self
.gram
.iter()
.map(|row| row.iter().map(|&x| Rational::from_int(x)).collect())
.collect();
let inv = inverse_matrix(mat)?;
let mut level = 1i128;
for i in 0..n {
for j in 0..n {
let e = &inv[i][j];
let den = e.denom(); let modulus = if i == j && e.numer() % 2 != 0 {
den.checked_mul(2).expect("lattice level exceeds i128")
} else {
den
};
level = lcm_i128(level, modulus);
}
}
Some(level)
}
pub fn clifford_metric(&self) -> crate::clifford::Metric<Rational> {
let n = self.dim();
let q = (0..n)
.map(|i| Rational::from_int(self.gram[i][i]))
.collect();
let mut b = BTreeMap::new();
for i in 0..n {
for j in (i + 1)..n {
let v = self.gram[i][j]
.checked_mul(2)
.expect("lattice Clifford metric exceeds i128");
if v != 0 {
b.insert((i, j), Rational::from_int(v));
}
}
}
crate::clifford::Metric::new(q, b)
}
pub fn clifford_metric_f2(&self) -> Option<crate::clifford::Metric<Nimber>> {
if !self.is_even() {
return None;
}
let n = self.dim();
let q = (0..n)
.map(|i| Nimber((self.gram[i][i] / 2).rem_euclid(2) as u128))
.collect();
let mut b = BTreeMap::new();
for i in 0..n {
for j in (i + 1)..n {
let v = self.gram[i][j].rem_euclid(2) as u128;
if v != 0 {
b.insert((i, j), Nimber(v));
}
}
}
Some(crate::clifford::Metric::new(q, b))
}
pub fn direct_sum(&self, other: &IntegralForm) -> IntegralForm {
let n = self.dim();
let m = other.dim();
let mut gram = vec![vec![0i128; n + m]; n + m];
for i in 0..n {
for j in 0..n {
gram[i][j] = self.gram[i][j];
}
}
for i in 0..m {
for j in 0..m {
gram[n + i][n + j] = other.gram[i][j];
}
}
IntegralForm { gram }
}
pub(super) fn matvec(&self, x: &[i128]) -> Vec<i128> {
let n = self.dim();
(0..n)
.map(|i| {
let mut acc = 0i128;
for j in 0..n {
acc = acc
.checked_add(
self.gram[i][j]
.checked_mul(x[j])
.expect("lattice matvec exceeds i128"),
)
.expect("lattice matvec exceeds i128");
}
acc
})
.collect()
}
}
pub(super) fn dot(a: &[i128], b: &[i128]) -> i128 {
let mut acc = 0i128;
for (&x, &y) in a.iter().zip(b) {
acc = acc
.checked_add(x.checked_mul(y).expect("lattice dot exceeds i128"))
.expect("lattice dot exceeds i128");
}
acc
}