use crate::forms::{arf_f2, ArfInvariants};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QuadricFit {
pub constant: bool,
pub qd: Vec<bool>,
pub bmat: Vec<u128>,
pub arf: ArfInvariants,
}
impl QuadricFit {
pub fn is_genuinely_quadratic(&self) -> bool {
self.arf.rank > 0
}
pub fn bias(&self) -> u128 {
self.arf.arf ^ (self.constant as u128)
}
pub fn display(&self) -> String {
self.to_string()
}
}
impl std::fmt::Display for QuadricFit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"QuadricFit(quadratic={}, constant={}, rank={}, arf={}, radical_dim={}, bias={})",
self.is_genuinely_quadratic(),
self.constant,
self.arf.rank,
self.arf.arf,
self.arf.radical_dim,
if self.arf.radical_dim == 0 {
self.bias().to_string()
} else {
"n/a (degenerate)".to_string()
},
)
}
}
pub fn fit_f2_quadratic(set: &[u128], k: usize) -> Option<QuadricFit> {
const MAX_ANF_DIM: usize = 20;
assert!(
k <= MAX_ANF_DIM,
"fit_f2_quadratic is exponential in k; max supported k is {MAX_ANF_DIM}"
);
let n = 1usize << k;
let domain_mask = if k == 0 { 0 } else { (1u128 << k) - 1 };
if set.iter().any(|&v| v & !domain_mask != 0) {
return None;
}
let mut coeffs = vec![true; n];
for &v in set {
coeffs[v as usize] = false;
}
for i in 0..k {
let bit = 1usize << i;
for mask in 0..n {
if mask & bit != 0 {
coeffs[mask] ^= coeffs[mask ^ bit];
}
}
}
if coeffs
.iter()
.enumerate()
.any(|(mask, &c)| c && mask.count_ones() > 2)
{
return None;
}
let constant = coeffs[0];
let qd: Vec<bool> = (0..k).map(|i| coeffs[1usize << i]).collect();
let mut bmat = vec![0u128; k];
for i in 0..k {
for j in (i + 1)..k {
if coeffs[(1usize << i) | (1usize << j)] {
bmat[i] |= 1 << j;
bmat[j] |= 1 << i;
}
}
}
let arf = arf_f2(k, &qd, &bmat);
Some(QuadricFit {
constant,
qd,
bmat,
arf,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn eval_fit(fit: &QuadricFit, v: u128) -> bool {
let mut acc = fit.constant;
for i in 0..fit.qd.len() {
if fit.qd[i] && v & (1 << i) != 0 {
acc ^= true;
}
}
for i in 0..fit.qd.len() {
for j in (i + 1)..fit.qd.len() {
if fit.bmat[i] & (1 << j) != 0 && v & (1 << i) != 0 && v & (1 << j) != 0 {
acc ^= true;
}
}
}
acc
}
#[test]
fn fit_recovers_known_quadrics() {
let h = fit_f2_quadratic(&[0, 1, 2], 2).unwrap();
assert!(h.is_genuinely_quadratic());
assert_eq!(h.arf.arf, 0);
assert!(!h.constant);
let a = fit_f2_quadratic(&[0], 2).unwrap();
assert!(a.is_genuinely_quadratic());
assert_eq!(a.arf.arf, 1);
let lin = fit_f2_quadratic(&[0, 3], 2).unwrap();
assert!(!lin.is_genuinely_quadratic());
assert_eq!(lin.arf.rank, 0);
}
#[test]
fn display_shows_bias_only_for_nonsingular_fits() {
let h = fit_f2_quadratic(&[0, 1, 2], 2).unwrap();
assert_eq!(h.arf.radical_dim, 0);
assert_eq!(
h.to_string(),
"QuadricFit(quadratic=true, constant=false, rank=2, arf=0, radical_dim=0, bias=0)"
);
assert_eq!(h.display(), h.to_string());
let a = fit_f2_quadratic(&[0], 2).unwrap();
assert_eq!(a.arf.radical_dim, 0);
assert_eq!(
a.to_string(),
"QuadricFit(quadratic=true, constant=false, rank=2, arf=1, radical_dim=0, bias=1)"
);
let lin = fit_f2_quadratic(&[0, 3], 2).unwrap();
assert_eq!(lin.arf.radical_dim, 2);
assert_eq!(
lin.to_string(),
"QuadricFit(quadratic=false, constant=false, rank=0, arf=0, radical_dim=2, bias=n/a (degenerate))"
);
}
#[test]
fn fit_supports_high_dimensional_quadratic_coefficient_layout() {
let set: Vec<u128> = (0..(1u128 << 16)).collect();
let fit = fit_f2_quadratic(&set, 16).unwrap();
assert_eq!(fit.qd.len(), 16);
assert_eq!(fit.arf.rank, 0);
assert!(!fit.constant);
}
#[test]
fn quadric_count_and_roundtrip_over_f2_cubed() {
let mut count = 0;
for s in 0u128..(1 << 8) {
let set: Vec<u128> = (0..8u128).filter(|&v| s & (1 << v) != 0).collect();
if let Some(fit) = fit_f2_quadratic(&set, 3) {
count += 1;
let recovered: Vec<u128> = (0..8u128).filter(|&v| !eval_fit(&fit, v)).collect();
assert_eq!(recovered, set, "fit did not reproduce its own set");
}
}
assert_eq!(count, 128, "expected exactly 2^7 quadrics over F₂³");
}
#[test]
fn cubic_truth_table_is_rejected() {
let set: Vec<u128> = (0..8u128).filter(|&v| v != 7).collect();
assert!(fit_f2_quadratic(&set, 3).is_none());
}
#[test]
fn point_outside_domain_returns_none_not_panic() {
assert_eq!(fit_f2_quadratic(&[0, 1, 4], 2), None);
}
#[test]
fn bias_matches_brute_force_zero_count_on_nonsingular_forms_up_to_k4() {
for k in 1usize..=4 {
let pair_count = k * (k - 1) / 2;
for qd_bits in 0u128..(1 << k) {
let qd: Vec<bool> = (0..k).map(|i| qd_bits & (1 << i) != 0).collect();
for bmat_bits in 0u128..(1u128 << pair_count) {
let mut bmat = vec![0u128; k];
let mut idx = 0;
for i in 0..k {
for j in (i + 1)..k {
if bmat_bits & (1 << idx) != 0 {
bmat[i] |= 1 << j;
bmat[j] |= 1 << i;
}
idx += 1;
}
}
let arf = arf_f2(k, &qd, &bmat);
if arf.radical_dim != 0 {
continue; }
let probe = QuadricFit {
constant: false,
qd: qd.clone(),
bmat: bmat.clone(),
arf: arf.clone(),
};
let expected = |bias: u128| -> i128 {
let base = 1i128 << (k - 1);
let swing = 1i128 << (k / 2 - 1);
if bias == 0 {
base + swing
} else {
base - swing
}
};
let zero_set: Vec<u128> = (0..(1u128 << k))
.filter(|&v| !eval_fit(&probe, v))
.collect();
let fit0 = fit_f2_quadratic(&zero_set, k).unwrap();
assert!(!fit0.constant);
assert_eq!(zero_set.len() as i128, expected(fit0.bias()));
let complement: Vec<u128> = (0..(1u128 << k))
.filter(|v| !zero_set.contains(v))
.collect();
let fit1 = fit_f2_quadratic(&complement, k).unwrap();
assert!(fit1.constant);
assert_eq!(complement.len() as i128, expected(fit1.bias()));
}
}
}
}
}