use crate::clifford::{CliffordAlgebra, Multivector};
use crate::scalar::Scalar;
pub fn versor_grade_parity<S: Scalar>(v: &Multivector<S>) -> Option<u128> {
let mut parity: Option<u128> = None;
for &blade in v.terms.keys() {
let p = (blade.count_ones() % 2) as u128;
match parity {
None => parity = Some(p),
Some(q) if q != p => return None,
_ => {}
}
}
parity
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VersorInvariants<S: Scalar> {
pub spinor_norm: S,
pub dickson: u128,
}
impl<S: Scalar> VersorInvariants<S> {
pub fn display(&self) -> String {
self.to_string()
}
}
impl<S: Scalar> std::fmt::Display for VersorInvariants<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"VersorInvariants(spinor_norm={}, dickson={})",
self.spinor_norm, self.dickson
)
}
}
impl<S: Scalar> CliffordAlgebra<S> {
pub fn spinor_norm(&self, v: &Multivector<S>) -> Option<S> {
self.pure_scalar_norm(v)
}
pub fn classify_versor(&self, v: &Multivector<S>) -> Option<VersorInvariants<S>> {
let dickson = versor_grade_parity(v)?;
let spinor_norm = self.spinor_norm(v)?;
Some(VersorInvariants {
spinor_norm,
dickson,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::clifford::Metric;
use crate::scalar::{Nimber, Rational};
fn cl3() -> CliffordAlgebra<Rational> {
CliffordAlgebra::new(
3,
Metric::diagonal(vec![Rational::one(), Rational::one(), Rational::one()]),
)
}
#[test]
fn spinor_norm_of_a_reflection_is_q_of_the_vector() {
let alg = cl3();
assert_eq!(alg.spinor_norm(&alg.e(0)), Some(Rational::one()));
let v = alg.add(&alg.e(0), &alg.e(1));
assert_eq!(alg.spinor_norm(&v), Some(Rational::from_int(2)));
}
#[test]
fn spinor_norm_is_multiplicative_on_versors() {
let alg = cl3();
let v = alg.add(&alg.e(0), &alg.e(1)); let w = alg.e(2); let vw = alg.mul(&v, &w);
let nv = alg.spinor_norm(&v).unwrap();
let nw = alg.spinor_norm(&w).unwrap();
let nvw = alg.spinor_norm(&vw).unwrap();
assert_eq!(nvw, nv.mul(&nw)); }
#[test]
fn dickson_parity_counts_reflections_mod_two() {
let alg = cl3();
let scalar_one = alg.scalar(Rational::one());
let e0 = alg.e(0);
let e0e1 = alg.mul(&alg.e(0), &alg.e(1));
let e0e1e2 = alg.mul(&e0e1, &alg.e(2));
assert_eq!(versor_grade_parity(&scalar_one), Some(0)); assert_eq!(versor_grade_parity(&e0), Some(1)); assert_eq!(versor_grade_parity(&e0e1), Some(0)); assert_eq!(versor_grade_parity(&e0e1e2), Some(1)); let mixed = alg.add(&e0, &e0e1);
assert_eq!(versor_grade_parity(&mixed), None);
assert_eq!(alg.classify_versor(&mixed), None);
}
#[test]
fn classify_versor_bundles_both() {
let alg = cl3();
let e0e1 = alg.mul(&alg.e(0), &alg.e(1));
let c = alg.classify_versor(&e0e1).unwrap();
assert_eq!(c.dickson, 0); assert_eq!(c.spinor_norm, Rational::one()); assert_eq!(c.to_string(), "VersorInvariants(spinor_norm=1, dickson=0)");
assert_eq!(c.display(), c.to_string());
}
#[test]
fn null_homogeneous_elements_are_not_versors() {
let alg = CliffordAlgebra::<Rational>::new(1, Metric::grassmann(1));
let e0 = alg.e(0);
assert_eq!(versor_grade_parity(&e0), Some(1));
assert_eq!(alg.spinor_norm(&e0), None);
assert_eq!(alg.classify_versor(&e0), None);
}
#[test]
fn generic_parity_agrees_with_char2_dickson() {
let alg = CliffordAlgebra::new(2, Metric::diagonal(vec![Nimber(1), Nimber(1)]));
let e0 = alg.e(0);
let e0e1 = alg.mul(&alg.e(0), &alg.e(1));
assert_eq!(
versor_grade_parity(&e0),
crate::forms::dickson_of_versor(&alg, &e0)
);
assert_eq!(
versor_grade_parity(&e0e1),
crate::forms::dickson_of_versor(&alg, &e0e1)
);
}
#[test]
fn char2_spinor_norm_and_classify_versor_pin_current_behavior() {
let alg1 = CliffordAlgebra::new(1, Metric::diagonal(vec![Nimber(1)]));
let e0 = alg1.e(0);
assert_eq!(alg1.spinor_norm(&e0), Some(Nimber(1)));
let c1 = alg1.classify_versor(&e0).unwrap();
assert_eq!(c1.dickson, 1);
assert_eq!(c1.spinor_norm, Nimber(1));
assert_eq!(
c1.to_string(),
"VersorInvariants(spinor_norm=*1, dickson=1)"
);
let mut b = std::collections::BTreeMap::new();
b.insert((0usize, 1usize), Nimber(1));
let alg2 = CliffordAlgebra::new(2, Metric::new(vec![Nimber(1), Nimber(1)], b));
let rotor = alg2.mul(&alg2.e(0), &alg2.e(1));
assert_eq!(alg2.spinor_norm(&rotor), Some(Nimber(1)));
let c2 = alg2.classify_versor(&rotor).unwrap();
assert_eq!(c2.dickson, 0);
assert_eq!(c2.spinor_norm, Nimber(1));
}
}