use crate::sparse::monomial_lcm;
pub fn hilbert_numerator(generators: &[Vec<usize>]) -> Vec<(usize, i64)> {
use std::collections::BTreeMap;
let mut coeffs: BTreeMap<usize, i64> = BTreeMap::new();
let s = generators.len();
for mask in 1..(1u64 << s) {
let mut lcm: Option<Vec<usize>> = None;
let mut bits = 0;
for (i, g) in generators.iter().enumerate() {
if mask & (1 << i) != 0 {
bits += 1;
lcm = Some(match lcm {
None => g.clone(),
Some(prev) => monomial_lcm(&prev, g).to_vec(),
});
}
}
let deg: usize = lcm.map(|l| l.iter().sum()).unwrap_or(0);
let sign: i64 = if bits % 2 == 1 { 1 } else { -1 };
*coeffs.entry(deg).or_insert(0) += sign;
}
coeffs.into_iter().filter(|&(_, c)| c != 0).collect()
}
pub fn regularity_bound(generators: &[Vec<usize>]) -> usize {
hilbert_numerator(generators)
.iter()
.map(|&(d, _)| d)
.max()
.unwrap_or(0)
}
pub fn staircase_dimension(generators: &[Vec<usize>]) -> Option<usize> {
let sum: i64 = hilbert_numerator(generators).iter().map(|&(_, c)| c).sum();
if sum == 0 {
None
} else {
Some(sum.unsigned_abs() as usize)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hilbert_numerator_single_generator() {
let coeffs = hilbert_numerator(&[vec![2]]);
assert_eq!(coeffs, vec![(2, 1)]);
}
#[test]
fn hilbert_numerator_two_generators() {
let coeffs = hilbert_numerator(&[vec![2, 0], vec![0, 2]]);
assert_eq!(coeffs, vec![(2, 2), (4, -1)]);
assert_eq!(regularity_bound(&[vec![2, 0], vec![0, 2]]), 4);
assert_eq!(staircase_dimension(&[vec![2, 0], vec![0, 2]]), Some(1));
}
#[test]
fn hilbert_numerator_linear() {
let coeffs = hilbert_numerator(&[vec![1, 0], vec![0, 1]]);
assert_eq!(coeffs, vec![(1, 2), (2, -1)]);
assert_eq!(regularity_bound(&[vec![1, 0], vec![0, 1]]), 2);
assert_eq!(staircase_dimension(&[vec![1, 0], vec![0, 1]]), Some(1));
}
}