use super::matrix::GF2Matrix;
use super::scalar::GF2;
use alloc::vec::Vec;
#[must_use]
pub fn gaussian_binomial(n: usize, k: usize, q: u64) -> u64 {
if k > n {
return 0;
}
if k == 0 || k == n {
return 1;
}
let mut result: u64 = 1;
for i in 0..k {
let num = q.pow((n - i) as u32) - 1;
let den = q.pow((i + 1) as u32) - 1;
result = result * num / den; }
result
}
#[must_use]
pub fn binary_grassmannian_size(k: usize, n: usize) -> u64 {
gaussian_binomial(n, k, 2)
}
pub fn enumerate_subspaces(k: usize, n: usize) -> Vec<GF2Matrix> {
assert!(n <= 20, "enumerate_subspaces: n={} too large (max 20)", n);
if k == 0 {
return vec![GF2Matrix::zero(0, n)];
}
if k > n {
return Vec::new();
}
let mut results = Vec::new();
let pivot_sets = k_subsets(n, k);
for pivots in &pivot_sets {
let free_cols: Vec<usize> = (0..n).filter(|c| !pivots.contains(c)).collect();
let num_free = free_cols.len(); let num_free_entries = k * num_free;
for pattern in 0u64..(1u64 << num_free_entries) {
let mut m = GF2Matrix::zero(k, n);
for (row, &pc) in pivots.iter().enumerate() {
m.set(row, pc, GF2::ONE);
}
for (row_idx, _) in pivots.iter().enumerate() {
for (fi, &fc) in free_cols.iter().enumerate() {
let bit_pos = row_idx * num_free + fi;
if (pattern >> bit_pos) & 1 == 1 {
m.set(row_idx, fc, GF2::ONE);
}
}
}
results.push(m);
}
}
let mut canonical: Vec<GF2Matrix> = results
.into_iter()
.map(|mut m| {
m.reduced_row_echelon();
m
})
.collect();
canonical.sort_by(|a, b| {
for i in 0..a.nrows() {
let wa = a.row(i).as_words();
let wb = b.row(i).as_words();
match wa.cmp(wb) {
core::cmp::Ordering::Equal => continue,
other => return other,
}
}
core::cmp::Ordering::Equal
});
canonical.dedup();
canonical
}
#[must_use]
pub fn schubert_cell_size(partition: &[usize]) -> u64 {
let sum: usize = partition.iter().sum();
1u64 << sum
}
#[must_use]
pub fn schubert_cell_of(subspace: &GF2Matrix) -> Vec<usize> {
let mut rref = subspace.clone();
let pivots = rref.reduced_row_echelon();
pivots.iter().enumerate().map(|(i, &p)| p - i).collect()
}
fn k_subsets(n: usize, k: usize) -> Vec<Vec<usize>> {
let mut result = Vec::new();
let mut current = Vec::with_capacity(k);
k_subsets_helper(n, k, 0, &mut current, &mut result);
result
}
fn k_subsets_helper(
n: usize,
k: usize,
start: usize,
current: &mut Vec<usize>,
result: &mut Vec<Vec<usize>>,
) {
if current.len() == k {
result.push(current.clone());
return;
}
let remaining = k - current.len();
for i in start..=(n - remaining) {
current.push(i);
k_subsets_helper(n, k, i + 1, current, result);
current.pop();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gaussian_binomial_known_values() {
assert_eq!(gaussian_binomial(3, 1, 2), 7);
assert_eq!(gaussian_binomial(4, 2, 2), 35);
assert_eq!(gaussian_binomial(5, 2, 2), 155);
}
#[test]
fn test_gaussian_binomial_edge_cases() {
assert_eq!(gaussian_binomial(5, 0, 2), 1);
assert_eq!(gaussian_binomial(5, 5, 2), 1);
assert_eq!(gaussian_binomial(3, 5, 2), 0);
}
#[test]
fn test_binary_grassmannian_size() {
assert_eq!(binary_grassmannian_size(1, 3), 7);
assert_eq!(binary_grassmannian_size(2, 4), 35);
}
#[test]
fn test_enumerate_subspaces_count() {
let subs = enumerate_subspaces(1, 3);
assert_eq!(subs.len() as u64, binary_grassmannian_size(1, 3));
let subs2 = enumerate_subspaces(2, 4);
assert_eq!(subs2.len() as u64, binary_grassmannian_size(2, 4));
}
#[test]
fn test_enumerate_subspaces_valid_rref() {
let subs = enumerate_subspaces(2, 4);
for s in &subs {
assert_eq!(s.nrows(), 2);
assert_eq!(s.ncols(), 4);
assert_eq!(s.rank(), 2, "subspace must have full rank k");
}
}
#[test]
fn test_schubert_cell_sizes_sum() {
let k = 1;
let n = 3;
let subs = enumerate_subspaces(k, n);
let total = binary_grassmannian_size(k, n);
let mut cell_counts: alloc::collections::BTreeMap<Vec<usize>, usize> =
alloc::collections::BTreeMap::new();
for s in &subs {
let cell = schubert_cell_of(s);
*cell_counts.entry(cell).or_insert(0) += 1;
}
let counted_total: usize = cell_counts.values().sum();
assert_eq!(counted_total as u64, total);
}
#[test]
fn test_schubert_cell_size_formula() {
assert_eq!(schubert_cell_size(&[0, 0]), 1);
assert_eq!(schubert_cell_size(&[1, 0]), 2);
assert_eq!(schubert_cell_size(&[1, 1]), 4);
assert_eq!(schubert_cell_size(&[2]), 4);
}
}