use crate::algebra::blade_new::BladeMask;
use crate::error::SignatureError;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Signature {
pub(crate) i: u8,
pub(crate) d: u8,
pub(crate) h: u8,
}
impl Signature {
pub fn new(i: u8, d: u8, h: u8) -> Result<Self, SignatureError> {
let total = i as u16 + d as u16 + h as u16;
if total > 64 {
return Err(SignatureError::TooManyGenerators { i, d, h, total });
}
Ok(Signature { i, d, h })
}
#[inline]
pub fn i(&self) -> u8 {
self.i
}
#[inline]
pub fn d(&self) -> u8 {
self.d
}
#[inline]
pub fn h(&self) -> u8 {
self.h
}
#[inline]
pub fn n(&self) -> u8 {
self.i + self.d + self.h
}
#[inline]
pub fn dimension(&self) -> u64 {
1u64 << self.n()
}
#[inline]
pub fn generator_square(&self, k: u8) -> i8 {
assert!(
k < self.n(),
"Signature: generator {} out of range (n={})",
k,
self.n()
);
if k < self.i {
-1
} else if k < self.i + self.d {
0
} else {
1
}
}
#[inline]
pub fn generator_square_u32(&self, k: u32) -> i8 {
let n = self.i as u32 + self.d as u32 + self.h as u32;
assert!(k < n, "Signature: generator {} out of range (n={})", k, n);
if k < self.i as u32 {
-1
} else if k < (self.i as u32 + self.d as u32) {
0
} else {
1
}
}
#[inline]
pub fn is_degenerate_u32(&self, k: u32) -> bool {
self.generator_square_u32(k) == 0
}
#[inline]
pub fn is_degenerate(&self, k: u8) -> bool {
self.generator_square(k) == 0
}
#[inline]
pub fn pseudoscalar_mask(&self) -> BladeMask {
(1u64 << self.n()) - 1
}
pub fn generator_name(&self, k: u8) -> String {
let (kind, idx) = self.generator_typed(k);
format!("{}{}", kind, idx)
}
pub fn generator_typed(&self, k: u8) -> (char, u8) {
assert!(
k < self.n(),
"Signature: generator {} out of range (n={})",
k,
self.n()
);
if k < self.i {
('i', k)
} else if k < self.i + self.d {
('d', k - self.i)
} else {
('h', k - self.i - self.d)
}
}
pub fn resolve_typed(&self, kind: char, index: u8) -> Option<u8> {
match kind {
'i' => {
if index < self.i {
Some(index)
} else {
None
}
}
'd' => {
if index < self.d {
Some(self.i + index)
} else {
None
}
}
'h' => {
if index < self.h {
Some(self.i + self.d + index)
} else {
None
}
}
_ => None,
}
}
pub fn all_generator_names(&self) -> Vec<String> {
(0..self.n()).map(|k| self.generator_name(k)).collect()
}
}
impl std::fmt::Display for Signature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Cl({},{},{})", self.i, self.d, self.h)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vga3() {
let sig = Signature::new(0, 0, 3).unwrap();
assert_eq!(sig.n(), 3);
assert_eq!(sig.dimension(), 8);
assert_eq!(sig.generator_square(0), 1);
assert_eq!(sig.generator_square(1), 1);
assert_eq!(sig.generator_square(2), 1);
assert!(!sig.is_degenerate(0));
assert_eq!(sig.pseudoscalar_mask(), 0b111);
}
#[test]
fn pga3() {
let sig = Signature::new(0, 1, 3).unwrap();
assert_eq!(sig.n(), 4);
assert_eq!(sig.dimension(), 16);
assert_eq!(sig.generator_square(0), 0); assert_eq!(sig.generator_square(1), 1);
assert_eq!(sig.generator_square(2), 1);
assert_eq!(sig.generator_square(3), 1);
assert!(sig.is_degenerate(0));
assert!(!sig.is_degenerate(1));
}
#[test]
fn cga3() {
let sig = Signature::new(1, 0, 4).unwrap();
assert_eq!(sig.n(), 5);
assert_eq!(sig.dimension(), 32);
assert_eq!(sig.generator_square(0), -1); assert_eq!(sig.generator_square(1), 1);
assert_eq!(sig.generator_square(4), 1);
assert!(!sig.is_degenerate(0));
assert_eq!(sig.pseudoscalar_mask(), 0b11111);
}
#[test]
fn sta_wc() {
let sig = Signature::new(3, 0, 1).unwrap();
assert_eq!(sig.n(), 4);
assert_eq!(sig.generator_square(0), -1);
assert_eq!(sig.generator_square(1), -1);
assert_eq!(sig.generator_square(2), -1);
assert_eq!(sig.generator_square(3), 1);
}
#[test]
fn display() {
assert_eq!(format!("{}", Signature::new(1, 0, 4).unwrap()), "Cl(1,0,4)");
}
#[test]
fn boundary_values() {
let sig = Signature::new(2, 3, 1).unwrap();
assert_eq!(sig.generator_square(0), -1);
assert_eq!(sig.generator_square(1), -1);
assert_eq!(sig.generator_square(2), 0);
assert_eq!(sig.generator_square(3), 0);
assert_eq!(sig.generator_square(4), 0);
assert_eq!(sig.generator_square(5), 1);
}
#[test]
fn semantic_names_cga3() {
let sig = Signature::new(1, 0, 4).unwrap();
assert_eq!(sig.generator_name(0), "i0");
assert_eq!(sig.generator_name(1), "h0");
assert_eq!(sig.generator_name(2), "h1");
assert_eq!(sig.generator_name(3), "h2");
assert_eq!(sig.generator_name(4), "h3");
}
#[test]
fn semantic_names_pga3() {
let sig = Signature::new(0, 1, 3).unwrap();
assert_eq!(sig.generator_name(0), "d0");
assert_eq!(sig.generator_name(1), "h0");
assert_eq!(sig.generator_name(2), "h1");
assert_eq!(sig.generator_name(3), "h2");
}
#[test]
fn semantic_names_sta_wc() {
let sig = Signature::new(3, 0, 1).unwrap();
assert_eq!(sig.generator_name(0), "i0");
assert_eq!(sig.generator_name(1), "i1");
assert_eq!(sig.generator_name(2), "i2");
assert_eq!(sig.generator_name(3), "h0");
}
#[test]
fn resolve_typed_cga3() {
let sig = Signature::new(1, 0, 4).unwrap();
assert_eq!(sig.resolve_typed('i', 0), Some(0));
assert_eq!(sig.resolve_typed('h', 0), Some(1));
assert_eq!(sig.resolve_typed('h', 2), Some(3));
assert_eq!(sig.resolve_typed('h', 4), None); assert_eq!(sig.resolve_typed('d', 0), None); }
#[test]
fn resolve_typed_mixed() {
let sig = Signature::new(2, 3, 1).unwrap();
assert_eq!(sig.resolve_typed('i', 0), Some(0));
assert_eq!(sig.resolve_typed('i', 1), Some(1));
assert_eq!(sig.resolve_typed('d', 0), Some(2));
assert_eq!(sig.resolve_typed('d', 2), Some(4));
assert_eq!(sig.resolve_typed('h', 0), Some(5));
assert_eq!(sig.resolve_typed('h', 1), None);
}
#[test]
fn all_names() {
let sig = Signature::new(1, 1, 2).unwrap();
assert_eq!(sig.all_generator_names(), vec!["i0", "d0", "h0", "h1"]);
}
#[test]
fn too_many_generators() {
assert!(Signature::new(30, 20, 20).is_err());
assert!(Signature::new(0, 0, 65).is_err());
assert!(Signature::new(0, 0, 64).is_ok());
}
}