use crate::clifford::{CliffordAlgebra, Metric};
use crate::forms::{
arf_fpn_char2, arf_invariant, arf_ordinal_finite, bw_class_complex, bw_class_finite_odd,
bw_class_function_field, bw_class_nimber, bw_class_rational, bw_class_real,
classify_finite_odd, classify_rational, classify_surcomplex, classify_surreal, finite_odd_witt,
isometric_finite_odd, isometric_fpn_char2, isometric_nimber, isometric_ordinal_finite,
isometric_rational, isometric_real, isometric_surcomplex,
ordinal_metric_finite_subfield_degree, witt_decompose_finite_odd, witt_decompose_real,
ArfInvariants, BrauerWallClass, CliffordInvariants, FiniteOddField,
FunctionFieldBrauerWallClass, OddCharInvariants, OddWittDecomp, RationalBrauerWallClass,
RationalCliffordInvariants, RealWittDecomp, WittClassG,
};
use crate::scalar::{
Fp, Fpn, Nimber, Ordinal, Rational, RationalFunction, Scalar, Surcomplex, Surreal,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FiniteFieldInvariants {
Odd(OddCharInvariants),
Char2(ArfInvariants),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FiniteFieldWittDecomp {
Odd(OddWittDecomp),
Char2(Char2WittDecomp),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Char2WittDecomp {
pub field_degree: u128,
pub witt_index: usize,
pub core_anisotropic_dim: usize,
pub radical_dim: usize,
pub radical_anisotropic: bool,
pub arf: u128,
}
impl Char2WittDecomp {
fn from_arf(field_degree: u128, arf: &ArfInvariants) -> Self {
let core_anisotropic_dim = if arf.arf == 0 { 0 } else { 2 };
let witt_index = arf.rank.saturating_sub(core_anisotropic_dim) / 2;
Char2WittDecomp {
field_degree,
witt_index,
core_anisotropic_dim,
radical_dim: arf.radical_dim,
radical_anisotropic: arf.radical_anisotropic,
arf: arf.arf,
}
}
pub fn display(&self) -> String {
self.to_string()
}
}
impl std::fmt::Display for Char2WittDecomp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Char2WittDecomp(field_degree={}, witt_index={}, core_anisotropic_dim={}, radical_dim={}, radical_anisotropic={}, arf={}{})",
self.field_degree,
self.witt_index,
self.core_anisotropic_dim,
self.radical_dim,
self.radical_anisotropic,
self.arf,
if self.radical_anisotropic {
" (complement-dependent)"
} else {
""
},
)
}
}
impl FiniteFieldWittDecomp {
pub fn display(&self) -> String {
self.to_string()
}
}
impl std::fmt::Display for FiniteFieldWittDecomp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FiniteFieldWittDecomp::Odd(d) => write!(f, "{d}"),
FiniteFieldWittDecomp::Char2(d) => write!(f, "{d}"),
}
}
}
impl FiniteFieldInvariants {
pub fn display(&self) -> String {
self.to_string()
}
}
impl std::fmt::Display for FiniteFieldInvariants {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FiniteFieldInvariants::Odd(c) => write!(f, "{c}"),
FiniteFieldInvariants::Char2(c) => {
write!(
f,
"char 2: Arf {} rank {} radical {}{} ({})",
c.arf,
c.rank,
c.radical_dim,
if c.radical_anisotropic {
" defective"
} else {
""
},
c.o_type()
)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ClassifyError {
GeneralBilinearMetric,
DiagonalizerFailure,
UnsupportedFieldOrWindow,
SingularForm {
radical_dim: usize,
radical_anisotropic: bool,
},
}
impl std::fmt::Display for ClassifyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ClassifyError::GeneralBilinearMetric => {
f.write_str("classifier requires a pure (q, b) metric, not general bilinear")
}
ClassifyError::DiagonalizerFailure => {
f.write_str("metric could not be diagonalized over this scalar backend")
}
ClassifyError::UnsupportedFieldOrWindow => {
f.write_str("field or ordinal window is outside the supported classifier domain")
}
ClassifyError::SingularForm {
radical_dim,
radical_anisotropic,
} => write!(
f,
"singular form: radical_dim={radical_dim}, radical_anisotropic={radical_anisotropic}"
),
}
}
}
impl From<crate::forms::WittClassError> for ClassifyError {
fn from(e: crate::forms::WittClassError) -> Self {
match e {
crate::forms::WittClassError::GeneralBilinearMetric => {
ClassifyError::GeneralBilinearMetric
}
crate::forms::WittClassError::Singular {
radical_dim,
radical_anisotropic,
} => ClassifyError::SingularForm {
radical_dim,
radical_anisotropic,
},
}
}
}
fn char0_failure<S: crate::scalar::Scalar>(metric: &Metric<S>) -> ClassifyError {
if metric.a().values().any(|v| !v.is_zero()) {
return ClassifyError::GeneralBilinearMetric;
}
if crate::forms::as_diagonal(metric).is_none() {
return ClassifyError::DiagonalizerFailure;
}
ClassifyError::UnsupportedFieldOrWindow
}
fn char2_nimber_failure(metric: &Metric<Nimber>) -> ClassifyError {
if metric.a().values().any(|v| !v.is_zero()) {
return ClassifyError::GeneralBilinearMetric;
}
if let Some(arf) = arf_invariant(metric) {
if arf.radical_dim != 0 {
return ClassifyError::SingularForm {
radical_dim: arf.radical_dim,
radical_anisotropic: arf.radical_anisotropic,
};
}
}
ClassifyError::UnsupportedFieldOrWindow
}
fn generic_failure<S: crate::scalar::Scalar>(metric: &Metric<S>) -> ClassifyError {
if metric.a().values().any(|v| !v.is_zero()) {
return ClassifyError::GeneralBilinearMetric;
}
ClassifyError::UnsupportedFieldOrWindow
}
fn two_metric_failure<S: crate::scalar::Scalar>(
m1: &Metric<S>,
m2: &Metric<S>,
diagnose: impl Fn(&Metric<S>) -> ClassifyError,
) -> ClassifyError {
match diagnose(m1) {
ClassifyError::UnsupportedFieldOrWindow => diagnose(m2),
e => e,
}
}
pub trait ClassifyForm: Scalar {
type Class;
fn classify(metric: &Metric<Self>) -> Result<Self::Class, ClassifyError>;
}
pub trait ClassifyWitt: Scalar {
fn witt_class(metric: &Metric<Self>) -> Result<WittClassG, ClassifyError>;
}
pub trait ClassifyIsometry: Scalar {
fn isometric(m1: &Metric<Self>, m2: &Metric<Self>) -> Result<bool, ClassifyError>;
}
pub trait DecomposeWitt: Scalar {
type Decomp;
fn witt_decompose(metric: &Metric<Self>) -> Result<Self::Decomp, ClassifyError>;
}
pub trait ClassifyBrauerWall: Scalar {
type BrauerWallClass;
fn bw_class(metric: &Metric<Self>) -> Result<Self::BrauerWallClass, ClassifyError>;
}
impl ClassifyForm for Surreal {
type Class = CliffordInvariants;
fn classify(metric: &Metric<Self>) -> Result<CliffordInvariants, ClassifyError> {
classify_surreal(metric).ok_or_else(|| char0_failure(metric))
}
}
impl ClassifyForm for Surcomplex<Surreal> {
type Class = CliffordInvariants;
fn classify(metric: &Metric<Self>) -> Result<CliffordInvariants, ClassifyError> {
classify_surcomplex(metric).ok_or_else(|| char0_failure(metric))
}
}
impl ClassifyForm for Rational {
type Class = RationalCliffordInvariants;
fn classify(metric: &Metric<Self>) -> Result<RationalCliffordInvariants, ClassifyError> {
classify_rational(metric).ok_or_else(|| char0_failure(metric))
}
}
impl<const P: u128> ClassifyForm for Fp<P> {
type Class = OddCharInvariants;
fn classify(metric: &Metric<Self>) -> Result<OddCharInvariants, ClassifyError> {
classify_finite_odd(metric).ok_or_else(|| char0_failure(metric))
}
}
impl<const P: u128, const N: usize> ClassifyForm for Fpn<P, N> {
type Class = FiniteFieldInvariants;
fn classify(metric: &Metric<Self>) -> Result<FiniteFieldInvariants, ClassifyError> {
if P == 2 {
arf_fpn_char2(metric)
.map(FiniteFieldInvariants::Char2)
.ok_or_else(|| generic_failure(metric))
} else {
classify_finite_odd(metric)
.map(FiniteFieldInvariants::Odd)
.ok_or_else(|| char0_failure(metric))
}
}
}
impl ClassifyForm for Nimber {
type Class = ArfInvariants;
fn classify(metric: &Metric<Self>) -> Result<ArfInvariants, ClassifyError> {
arf_invariant(metric).ok_or_else(|| generic_failure(metric))
}
}
impl ClassifyForm for Ordinal {
type Class = ArfInvariants;
fn classify(metric: &Metric<Self>) -> Result<ArfInvariants, ClassifyError> {
arf_ordinal_finite(metric).ok_or_else(|| generic_failure(metric))
}
}
impl ClassifyWitt for Surreal {
fn witt_class(metric: &Metric<Self>) -> Result<WittClassG, ClassifyError> {
let (p, q, _r) =
crate::forms::char0::surreal_signature(metric).ok_or_else(|| char0_failure(metric))?;
Ok(WittClassG::char0(p, q))
}
}
impl<const P: u128> ClassifyWitt for Fp<P> {
fn witt_class(metric: &Metric<Self>) -> Result<WittClassG, ClassifyError> {
finite_odd_witt(metric).ok_or_else(|| char0_failure(metric))
}
}
impl<const P: u128, const N: usize> ClassifyWitt for Fpn<P, N> {
fn witt_class(metric: &Metric<Self>) -> Result<WittClassG, ClassifyError> {
if P == 2 {
let arf = arf_fpn_char2(metric).ok_or_else(|| generic_failure(metric))?;
if arf.radical_dim != 0 {
return Err(ClassifyError::SingularForm {
radical_dim: arf.radical_dim,
radical_anisotropic: arf.radical_anisotropic,
});
}
Ok(WittClassG::Char2 {
field_degree: N as u128,
arf: arf.arf,
})
} else {
finite_odd_witt(metric).ok_or_else(|| char0_failure(metric))
}
}
}
impl ClassifyWitt for Nimber {
fn witt_class(metric: &Metric<Self>) -> Result<WittClassG, ClassifyError> {
WittClassG::try_char2_from_metric(metric).map_err(ClassifyError::from)
}
}
impl ClassifyWitt for Ordinal {
fn witt_class(metric: &Metric<Self>) -> Result<WittClassG, ClassifyError> {
let arf = arf_ordinal_finite(metric).ok_or_else(|| generic_failure(metric))?;
if arf.radical_dim != 0 {
return Err(ClassifyError::SingularForm {
radical_dim: arf.radical_dim,
radical_anisotropic: arf.radical_anisotropic,
});
}
Ok(WittClassG::Char2 {
field_degree: ordinal_char2_field_degree(metric)
.ok_or(ClassifyError::UnsupportedFieldOrWindow)?,
arf: arf.arf,
})
}
}
impl ClassifyIsometry for Surreal {
fn isometric(m1: &Metric<Self>, m2: &Metric<Self>) -> Result<bool, ClassifyError> {
isometric_real(m1, m2).ok_or_else(|| two_metric_failure(m1, m2, char0_failure))
}
}
impl ClassifyIsometry for Surcomplex<Surreal> {
fn isometric(m1: &Metric<Self>, m2: &Metric<Self>) -> Result<bool, ClassifyError> {
isometric_surcomplex(m1, m2).ok_or_else(|| two_metric_failure(m1, m2, char0_failure))
}
}
impl ClassifyIsometry for Rational {
fn isometric(m1: &Metric<Self>, m2: &Metric<Self>) -> Result<bool, ClassifyError> {
isometric_rational(m1, m2).ok_or_else(|| two_metric_failure(m1, m2, char0_failure))
}
}
impl<const P: u128> ClassifyIsometry for Fp<P> {
fn isometric(m1: &Metric<Self>, m2: &Metric<Self>) -> Result<bool, ClassifyError> {
isometric_finite_odd(m1, m2).ok_or_else(|| two_metric_failure(m1, m2, char0_failure))
}
}
impl<const P: u128, const N: usize> ClassifyIsometry for Fpn<P, N> {
fn isometric(m1: &Metric<Self>, m2: &Metric<Self>) -> Result<bool, ClassifyError> {
if P == 2 {
isometric_fpn_char2(m1, m2).ok_or_else(|| two_metric_failure(m1, m2, generic_failure))
} else {
isometric_finite_odd(m1, m2).ok_or_else(|| two_metric_failure(m1, m2, char0_failure))
}
}
}
impl ClassifyIsometry for Nimber {
fn isometric(m1: &Metric<Self>, m2: &Metric<Self>) -> Result<bool, ClassifyError> {
isometric_nimber(m1, m2).ok_or_else(|| two_metric_failure(m1, m2, generic_failure))
}
}
impl ClassifyIsometry for Ordinal {
fn isometric(m1: &Metric<Self>, m2: &Metric<Self>) -> Result<bool, ClassifyError> {
isometric_ordinal_finite(m1, m2).ok_or_else(|| two_metric_failure(m1, m2, generic_failure))
}
}
impl DecomposeWitt for Surreal {
type Decomp = RealWittDecomp;
fn witt_decompose(metric: &Metric<Self>) -> Result<Self::Decomp, ClassifyError> {
witt_decompose_real(metric).ok_or_else(|| char0_failure(metric))
}
}
impl<const P: u128> DecomposeWitt for Fp<P> {
type Decomp = OddWittDecomp;
fn witt_decompose(metric: &Metric<Self>) -> Result<Self::Decomp, ClassifyError> {
witt_decompose_finite_odd(metric).ok_or_else(|| char0_failure(metric))
}
}
impl<const P: u128, const N: usize> DecomposeWitt for Fpn<P, N> {
type Decomp = FiniteFieldWittDecomp;
fn witt_decompose(metric: &Metric<Self>) -> Result<Self::Decomp, ClassifyError> {
if P == 2 {
let arf = arf_fpn_char2(metric).ok_or_else(|| generic_failure(metric))?;
Ok(FiniteFieldWittDecomp::Char2(Char2WittDecomp::from_arf(
N as u128, &arf,
)))
} else {
witt_decompose_finite_odd(metric)
.map(FiniteFieldWittDecomp::Odd)
.ok_or_else(|| char0_failure(metric))
}
}
}
impl ClassifyBrauerWall for Surreal {
type BrauerWallClass = BrauerWallClass;
fn bw_class(metric: &Metric<Self>) -> Result<BrauerWallClass, ClassifyError> {
bw_class_real(metric).ok_or_else(|| char0_failure(metric))
}
}
impl ClassifyBrauerWall for Surcomplex<Surreal> {
type BrauerWallClass = BrauerWallClass;
fn bw_class(metric: &Metric<Self>) -> Result<BrauerWallClass, ClassifyError> {
bw_class_complex(metric).ok_or_else(|| char0_failure(metric))
}
}
impl ClassifyBrauerWall for Rational {
type BrauerWallClass = RationalBrauerWallClass;
fn bw_class(metric: &Metric<Self>) -> Result<RationalBrauerWallClass, ClassifyError> {
bw_class_rational(metric).ok_or_else(|| char0_failure(metric))
}
}
impl<const P: u128> ClassifyBrauerWall for Fp<P> {
type BrauerWallClass = BrauerWallClass;
fn bw_class(metric: &Metric<Self>) -> Result<BrauerWallClass, ClassifyError> {
bw_class_finite_odd(metric).ok_or_else(|| char0_failure(metric))
}
}
impl<const P: u128, const N: usize> ClassifyBrauerWall for Fpn<P, N> {
type BrauerWallClass = BrauerWallClass;
fn bw_class(metric: &Metric<Self>) -> Result<BrauerWallClass, ClassifyError> {
if P == 2 {
let arf = arf_fpn_char2(metric).ok_or_else(|| generic_failure(metric))?;
if arf.radical_dim != 0 {
return Err(ClassifyError::SingularForm {
radical_dim: arf.radical_dim,
radical_anisotropic: arf.radical_anisotropic,
});
}
Ok(BrauerWallClass::Char2 {
field_degree: N as u128,
arf: arf.arf,
})
} else {
bw_class_finite_odd(metric).ok_or_else(|| char0_failure(metric))
}
}
}
impl<S: FiniteOddField> ClassifyBrauerWall for RationalFunction<S> {
type BrauerWallClass = FunctionFieldBrauerWallClass<S>;
fn bw_class(metric: &Metric<Self>) -> Result<FunctionFieldBrauerWallClass<S>, ClassifyError> {
bw_class_function_field(metric).ok_or_else(|| char0_failure(metric))
}
}
impl ClassifyBrauerWall for Nimber {
type BrauerWallClass = BrauerWallClass;
fn bw_class(metric: &Metric<Self>) -> Result<BrauerWallClass, ClassifyError> {
bw_class_nimber(metric).ok_or_else(|| char2_nimber_failure(metric))
}
}
impl ClassifyBrauerWall for Ordinal {
type BrauerWallClass = BrauerWallClass;
fn bw_class(metric: &Metric<Self>) -> Result<BrauerWallClass, ClassifyError> {
let arf = arf_ordinal_finite(metric).ok_or_else(|| generic_failure(metric))?;
if arf.radical_dim != 0 {
return Err(ClassifyError::SingularForm {
radical_dim: arf.radical_dim,
radical_anisotropic: arf.radical_anisotropic,
});
}
Ok(BrauerWallClass::Char2 {
field_degree: ordinal_char2_field_degree(metric)
.ok_or(ClassifyError::UnsupportedFieldOrWindow)?,
arf: arf.arf,
})
}
}
fn ordinal_char2_field_degree(metric: &Metric<Ordinal>) -> Option<u128> {
ordinal_metric_finite_subfield_degree(metric)
}
impl<S: ClassifyForm> Metric<S> {
pub fn classify(&self) -> Result<S::Class, ClassifyError> {
S::classify(self)
}
}
impl<S: ClassifyWitt> Metric<S> {
pub fn witt_class(&self) -> Result<WittClassG, ClassifyError> {
S::witt_class(self)
}
}
impl<S: ClassifyIsometry> Metric<S> {
pub fn isometric_to(&self, other: &Self) -> Result<bool, ClassifyError> {
S::isometric(self, other)
}
}
impl<S: DecomposeWitt> Metric<S> {
pub fn witt_decompose(&self) -> Result<S::Decomp, ClassifyError> {
S::witt_decompose(self)
}
}
impl<S: ClassifyBrauerWall> Metric<S> {
pub fn bw_class(&self) -> Result<S::BrauerWallClass, ClassifyError> {
S::bw_class(self)
}
}
impl<S: ClassifyForm> CliffordAlgebra<S> {
pub fn classify(&self) -> Result<S::Class, ClassifyError> {
S::classify(&self.metric)
}
}
impl<S: ClassifyWitt> CliffordAlgebra<S> {
pub fn witt_class(&self) -> Result<WittClassG, ClassifyError> {
S::witt_class(&self.metric)
}
}
impl<S: ClassifyIsometry> CliffordAlgebra<S> {
pub fn isometric_to(&self, other: &Self) -> Result<bool, ClassifyError> {
S::isometric(&self.metric, &other.metric)
}
}
impl<S: DecomposeWitt> CliffordAlgebra<S> {
pub fn witt_decompose(&self) -> Result<S::Decomp, ClassifyError> {
S::witt_decompose(&self.metric)
}
}
impl<S: ClassifyBrauerWall> CliffordAlgebra<S> {
pub fn bw_class(&self) -> Result<S::BrauerWallClass, ClassifyError> {
S::bw_class(&self.metric)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::clifford::Metric;
#[test]
fn classify_dispatches_on_scalar_type() {
let m = Metric::diagonal(vec![Surreal::one(), Surreal::one()]);
assert_eq!(m.classify().ok(), classify_surreal(&m));
assert!(m.classify().is_ok());
let n = Metric::diagonal(vec![Nimber::one(), Nimber::one()]);
assert_eq!(n.classify().ok(), arf_invariant(&n));
assert_eq!(
n.witt_class().ok(),
WittClassG::try_char2_from_metric(&n).ok()
);
assert_eq!(n.bw_class().ok(), bw_class_nimber(&n));
let f = Metric::diagonal(vec![Fp::<5>::from_int(1), Fp::<5>::from_int(2)]);
assert_eq!(f.classify().ok(), classify_finite_odd(&f));
assert_eq!(f.witt_class().ok(), finite_odd_witt(&f));
let f9 = Metric::diagonal(vec![Fpn::<3, 2>::constant(1), Fpn::<3, 2>::generator()]);
assert_eq!(
f9.classify().ok(),
classify_finite_odd(&f9).map(FiniteFieldInvariants::Odd)
);
assert_eq!(f9.witt_class().ok(), finite_odd_witt(&f9));
let mut b = std::collections::BTreeMap::new();
b.insert((0usize, 1usize), Fpn::<2, 3>::one());
let f8 = Metric::new(vec![Fpn::<2, 3>::generator(), Fpn::<2, 3>::generator()], b);
assert_eq!(
f8.classify().ok(),
arf_fpn_char2(&f8).map(FiniteFieldInvariants::Char2)
);
assert!(matches!(f8.classify(), Ok(FiniteFieldInvariants::Char2(_))));
let mut b = std::collections::BTreeMap::new();
b.insert((0usize, 1usize), Ordinal::one());
let omega = Ordinal::omega();
let ord = Metric::new(vec![omega.clone(), omega], b);
let arf = arf_ordinal_finite(&ord).unwrap();
assert_eq!(ord.classify().ok(), Some(arf.clone()));
assert_eq!(
ord.witt_class().ok(),
Some(WittClassG::Char2 {
field_degree: 6,
arf: arf.arf
})
);
assert_eq!(
ord.bw_class().ok(),
Some(BrauerWallClass::Char2 {
field_degree: 6,
arf: arf.arf
})
);
let outside_window = Metric::diagonal(vec![Ordinal::omega_pow(Ordinal::omega())]);
assert!(outside_window.classify().is_ok());
assert_eq!(ordinal_char2_field_degree(&outside_window), Some(20));
let outside_segment = Metric::diagonal(vec![Ordinal::omega_pow(Ordinal::omega_pow(
Ordinal::omega(),
))]);
assert!(outside_segment.classify().is_err());
assert!(outside_segment.bw_class().is_err());
}
#[test]
fn algebra_classify_matches_metric_classify() {
let alg = CliffordAlgebra::new(
2,
Metric::diagonal(vec![Surreal::one(), Surreal::one().neg()]),
);
assert_eq!(alg.classify(), alg.metric.classify());
assert_eq!(alg.witt_class(), alg.metric.witt_class());
assert_eq!(alg.witt_decompose(), alg.metric.witt_decompose());
assert_eq!(alg.bw_class(), alg.metric.bw_class());
}
#[test]
fn structural_facades_dispatch() {
let f = Metric::diagonal(vec![Fp::<5>::from_int(1), Fp::<5>::from_int(1)]);
let g = Metric::diagonal(vec![Fp::<5>::from_int(2), Fp::<5>::from_int(3)]);
assert_eq!(f.isometric_to(&g).ok(), isometric_finite_odd(&f, &g));
assert_eq!(f.witt_decompose().ok(), witt_decompose_finite_odd(&f));
assert_eq!(f.bw_class().ok(), bw_class_finite_odd(&f));
let f9 = Metric::diagonal(vec![Fpn::<3, 2>::constant(1), Fpn::<3, 2>::constant(1)]);
let g9 = Metric::diagonal(vec![Fpn::<3, 2>::constant(2), Fpn::<3, 2>::constant(2)]);
assert_eq!(f9.isometric_to(&g9).ok(), isometric_finite_odd(&f9, &g9));
assert_eq!(
f9.witt_decompose().ok(),
witt_decompose_finite_odd(&f9).map(FiniteFieldWittDecomp::Odd)
);
assert_eq!(f9.bw_class().ok(), bw_class_finite_odd(&f9));
let mut b = std::collections::BTreeMap::new();
b.insert((0usize, 1usize), Fpn::<2, 3>::one());
let f8 = Metric::new(vec![Fpn::<2, 3>::zero(), Fpn::<2, 3>::zero()], b);
assert_eq!(
f8.witt_decompose().ok(),
Some(FiniteFieldWittDecomp::Char2(Char2WittDecomp {
field_degree: 3,
witt_index: 1,
core_anisotropic_dim: 0,
radical_dim: 0,
radical_anisotropic: false,
arf: 0,
}))
);
assert_eq!(
f8.bw_class().ok(),
Some(BrauerWallClass::Char2 {
field_degree: 3,
arf: 0
})
);
let mut b = std::collections::BTreeMap::new();
b.insert((0usize, 1usize), Nimber::one());
let n = Metric::new(vec![Nimber::zero(), Nimber::zero()], b);
assert_eq!(n.bw_class().ok(), bw_class_nimber(&n));
let mut b = std::collections::BTreeMap::new();
b.insert((0usize, 1usize), Ordinal::one());
let ord = Metric::new(vec![Ordinal::omega(), Ordinal::omega()], b);
assert_eq!(ord.isometric_to(&ord).ok(), Some(true));
}
#[test]
fn classify_error_distinguishes_general_bilinear_from_window() {
let mut a = std::collections::BTreeMap::new();
a.insert((0usize, 1usize), Nimber(1));
let metric = Metric::general(
vec![Nimber(1), Nimber(1)],
std::collections::BTreeMap::<(usize, usize), Nimber>::new(),
a,
);
assert!(matches!(
metric.witt_class(),
Err(ClassifyError::GeneralBilinearMetric)
));
assert!(matches!(
metric.classify(),
Err(ClassifyError::GeneralBilinearMetric)
));
}
#[test]
fn classify_error_reports_singular_form_with_radical_data() {
let metric = Metric::diagonal(vec![Nimber(1), Nimber(0)]);
match metric.witt_class() {
Err(ClassifyError::SingularForm {
radical_dim,
radical_anisotropic,
}) => {
assert_eq!(radical_dim, 2);
assert!(radical_anisotropic);
}
other => panic!("expected SingularForm, got {other:?}"),
}
}
#[test]
fn char2_witt_decomp_defective_radical_matches_documented_caveat() {
let mut b = std::collections::BTreeMap::new();
b.insert((0usize, 1usize), Fpn::<2, 3>::one());
let zero = Fpn::<2, 3>::zero();
let one = Fpn::<2, 3>::one();
let split_complement = Metric::new(vec![zero, zero, one], b.clone());
let anisotropic_complement = Metric::new(vec![one, one, one], b);
let d1 = match split_complement.witt_decompose() {
Ok(FiniteFieldWittDecomp::Char2(d)) => d,
other => panic!("expected a Char2 decomp, got {other:?}"),
};
let d2 = match anisotropic_complement.witt_decompose() {
Ok(FiniteFieldWittDecomp::Char2(d)) => d,
other => panic!("expected a Char2 decomp, got {other:?}"),
};
assert!(d1.radical_anisotropic && d2.radical_anisotropic);
assert_eq!(d1.radical_dim, 1);
assert_eq!(d2.radical_dim, 1);
assert_ne!(d1.arf, d2.arf);
assert_ne!(
(d1.witt_index, d1.core_anisotropic_dim),
(d2.witt_index, d2.core_anisotropic_dim)
);
assert_eq!(
crate::forms::isometric_finite_char2(&split_complement, &anisotropic_complement),
Some(true)
);
}
#[test]
fn char2_witt_decomp_display_marks_complement_dependence() {
let defective = Char2WittDecomp {
field_degree: 3,
witt_index: 1,
core_anisotropic_dim: 0,
radical_dim: 1,
radical_anisotropic: true,
arf: 0,
};
assert_eq!(
defective.to_string(),
"Char2WittDecomp(field_degree=3, witt_index=1, core_anisotropic_dim=0, radical_dim=1, radical_anisotropic=true, arf=0 (complement-dependent))"
);
assert_eq!(defective.display(), defective.to_string());
let nonsingular = Char2WittDecomp {
field_degree: 3,
witt_index: 1,
core_anisotropic_dim: 0,
radical_dim: 0,
radical_anisotropic: false,
arf: 0,
};
assert_eq!(
nonsingular.to_string(),
"Char2WittDecomp(field_degree=3, witt_index=1, core_anisotropic_dim=0, radical_dim=0, radical_anisotropic=false, arf=0)"
);
assert_eq!(
FiniteFieldWittDecomp::Char2(nonsingular).to_string(),
nonsingular.to_string()
);
assert_eq!(
FiniteFieldWittDecomp::Char2(nonsingular).display(),
nonsingular.to_string()
);
}
}