use crate::algebra::blade_new::{canonical_reorder, BladeKey};
use crate::algebra::signature::Signature;
pub fn blade_product_new(a: &BladeKey, b: &BladeKey, sig: &Signature) -> (BladeKey, i8) {
let result = BladeKey::symmetric_difference(a, b);
let mut sign = canonical_reorder(a, b) as i16;
let shared = BladeKey::intersection(a, b);
for &k in shared.indices() {
let sq = sig.generator_square_u32(k as u32);
if sq == 0 {
return (BladeKey::SCALAR, 0);
}
sign *= sq as i16;
}
(result, sign as i8)
}
pub fn blade_product(a: BladeMask, b: BladeMask, sig: &Signature) -> (BladeMask, i8) {
let result = a ^ b;
let mut sign = crate::algebra::blade_new::canonical_reorder_mask(a, b) as i16;
let mut shared = a & b;
while shared != 0 {
let k = shared.trailing_zeros() as u8;
let sq = sig.generator_square(k);
if sq == 0 {
return (0, 0);
}
sign *= sq as i16;
shared &= shared - 1;
}
(result, sign as i8)
}
use crate::algebra::blade_new::BladeMask;
#[cfg(test)]
mod tests {
use super::*;
use crate::algebra::blade_new::mask_to_blade;
use crate::algebra::blade_new::BladeMask;
use crate::algebra::product_new::blade_product;
fn sig_vga3() -> Signature {
Signature::new(0, 0, 3).unwrap()
}
fn sig_pga3() -> Signature {
Signature::new(0, 1, 3).unwrap()
}
fn sig_cga3() -> Signature {
Signature::new(1, 0, 4).unwrap()
}
fn bk(k: u16) -> BladeKey {
BladeKey::generator(k)
}
#[test]
fn e0_squared_positive() {
let sig = sig_vga3();
let (mask, sign) = blade_product_new(&bk(0), &bk(0), &sig);
assert!(mask.is_scalar());
assert_eq!(sign, 1);
}
#[test]
fn e0_squared_negative() {
let sig = Signature::new(1, 0, 0).unwrap();
let (mask, sign) = blade_product_new(&bk(0), &bk(0), &sig);
assert!(mask.is_scalar());
assert_eq!(sign, -1);
}
#[test]
fn e0_squared_degenerate() {
let sig = Signature::new(0, 1, 0).unwrap();
let (_, sign) = blade_product_new(&bk(0), &bk(0), &sig);
assert_eq!(sign, 0);
}
#[test]
fn e0_e1() {
let sig = sig_vga3();
let (mask, sign) = blade_product_new(&bk(0), &bk(1), &sig);
assert_eq!(mask.indices(), &[0, 1]);
assert_eq!(sign, 1);
}
#[test]
fn e1_e0_anticommute() {
let sig = sig_vga3();
let (mask, sign) = blade_product_new(&bk(1), &bk(0), &sig);
assert_eq!(mask.indices(), &[0, 1]);
assert_eq!(sign, -1);
}
#[test]
fn degenerate_kills_product() {
let sig = sig_pga3();
let (_, sign) = blade_product_new(&bk(0), &bk(0), &sig);
assert_eq!(sign, 0);
}
#[test]
fn matches_legacy_cga3() {
let sig = sig_cga3();
let n = sig.n();
let max_mask: BladeMask = (1u64 << n) - 1;
for a_mask in 0..=max_mask {
for b_mask in 0..=max_mask {
let (legacy_mask, legacy_sign) = blade_product(a_mask, b_mask, &sig);
let a_blade = mask_to_blade(a_mask);
let b_blade = mask_to_blade(b_mask);
let (new_blade, new_sign) = blade_product_new(&a_blade, &b_blade, &sig);
assert_eq!(
legacy_sign, new_sign,
"sign mismatch: a={:#b} b={:#b}",
a_mask, b_mask
);
if legacy_sign != 0 {
let new_mask = crate::algebra::blade_new::blade_to_mask(&new_blade);
assert_eq!(
legacy_mask, new_mask,
"mask mismatch: a={:#b} b={:#b}",
a_mask, b_mask
);
}
}
}
}
#[test]
fn associativity_fuzz() {
let sig = sig_cga3();
let blades: Vec<BladeKey> = vec![
bk(0),
bk(1),
bk(2),
bk(3),
bk(4),
BladeKey::from_sorted(&[0, 1]),
BladeKey::from_sorted(&[0, 2]),
BladeKey::from_sorted(&[1, 3]),
BladeKey::from_sorted(&[0, 1, 2, 3, 4]),
];
for a in &blades {
for b in &blades {
for c in &blades {
let (ab, ab_sign) = blade_product_new(a, b, &sig);
let (abc_left, left_sign) = if ab_sign == 0 {
(BladeKey::SCALAR, 0)
} else {
let (m, s) = blade_product_new(&ab, c, &sig);
(m, (ab_sign as i16 * s as i16) as i8)
};
let (bc, bc_sign) = blade_product_new(b, c, &sig);
let (abc_right, right_sign) = if bc_sign == 0 {
(BladeKey::SCALAR, 0)
} else {
let (m, s) = blade_product_new(a, &bc, &sig);
(m, (bc_sign as i16 * s as i16) as i8)
};
assert_eq!(
left_sign, right_sign,
"associativity sign: a={:?} b={:?} c={:?}",
a, b, c
);
if left_sign != 0 {
assert_eq!(
abc_left, abc_right,
"associativity blade: a={:?} b={:?} c={:?}",
a, b, c
);
}
}
}
}
}
}