use crate::clifford::Metric;
use crate::scalar::{Qp, Qq};
use super::local::{springer_decompose_local, LocalSpringerDecomp};
pub fn springer_decompose_qp<const P: u128, const K: u128>(
metric: &Metric<Qp<P, K>>,
) -> Option<LocalSpringerDecomp> {
springer_decompose_local(metric)
}
pub fn springer_decompose_qq<const P: u128, const N: usize, const F: usize>(
metric: &Metric<Qq<P, N, F>>,
) -> Option<LocalSpringerDecomp> {
springer_decompose_local(metric)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::Scalar;
type Q5 = Qp<5, 4>;
#[test]
fn two_residue_layers_survive() {
let m = Metric::diagonal(vec![Q5::from_int(1), Q5::from_int(5)]);
let d = springer_decompose_qp(&m).unwrap();
assert_eq!(d.graded.len(), 2);
assert_eq!(d.graded[0].valuation, 1); assert_eq!(d.graded[1].valuation, 0);
assert_eq!(
d.graded.iter().map(|g| g.dim).sum::<usize>() + d.radical_dim,
2
);
assert_eq!(d.parity_layer(0).len(), 1); assert_eq!(d.parity_layer(1).len(), 1); assert!(d.graded[0].disc_is_square);
assert!(d.graded[1].disc_is_square);
}
#[test]
fn residue_square_class_tracks_nonsquares() {
let m = Metric::diagonal(vec![Q5::from_int(2), Q5::from_int(10)]);
let d = springer_decompose_qp(&m).unwrap();
assert_eq!(d.graded.len(), 2);
assert!(!d.graded[0].disc_is_square); assert!(!d.graded[1].disc_is_square);
let m2 = Metric::diagonal(vec![Q5::from_int(2), Q5::from_int(3)]);
let d2 = springer_decompose_qp(&m2).unwrap();
assert_eq!(d2.graded.len(), 1);
assert_eq!(d2.graded[0].dim, 2);
assert!(d2.graded[0].disc_is_square); }
#[test]
fn radical_and_rejections() {
let m = Metric::diagonal(vec![Q5::zero(), Q5::from_int(5)]);
let d = springer_decompose_qp(&m).unwrap();
assert_eq!(d.radical_dim, 1);
assert_eq!(d.graded.len(), 1);
assert!(springer_decompose_qp(&Metric::diagonal(vec![Qp::<2, 4>::from_int(1)])).is_none());
}
#[test]
fn unramified_qq_recovers_qp_when_residue_degree_is_one() {
type Q5Unram = Qq<5, 4, 1>;
let mqq = Metric::diagonal(vec![Q5Unram::from_int(1), Q5Unram::from_int(5)]);
let dqq = springer_decompose_qq(&mqq).unwrap();
let mqp = Metric::diagonal(vec![Q5::from_int(1), Q5::from_int(5)]);
let dqp = springer_decompose_qp(&mqp).unwrap();
assert_eq!(dqq, dqp);
}
#[test]
fn unramified_qq_reads_f9_square_class() {
use crate::scalar::{Fpn, WittVec};
type Q9 = Qq<3, 3, 2>;
let ns = (0..9u128)
.map(|c| Fpn::<3, 2>::from_coeffs(&[c % 3, c / 3]))
.find(|x| !x.is_zero() && !x.is_square())
.expect("F_9 has nonsquares");
let m = Metric::diagonal(vec![
Q9::from_witt(WittVec::<3, 3, 2>(ns.mul(&ns).into_coeffs())),
Q9::from_witt(WittVec::<3, 3, 2>(ns.into_coeffs())).mul(&Q9::from_int(3)),
]);
let d = springer_decompose_qq(&m).unwrap();
assert_eq!(d.graded.len(), 2);
assert_eq!(d.graded[0].valuation, 1); assert!(!d.graded[0].disc_is_square, "ns is a nonsquare in F_9");
assert!(d.graded[1].disc_is_square, "ns² is a square in F_9");
assert!(
springer_decompose_qq(&Metric::diagonal(vec![Qq::<2, 4, 2>::from_int(1)])).is_none()
);
}
}