use crate::algebra::blade_new::BladeMask;
use crate::algebra::mv::Mv;
use crate::algebra::signature::Signature;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct GeneratorProfile {
pub i_participation: u64,
pub d_participation: u64,
pub h_participation: u64,
}
impl GeneratorProfile {
pub const EMPTY: GeneratorProfile = GeneratorProfile {
i_participation: 0,
d_participation: 0,
h_participation: 0,
};
pub fn compute(mv: &Mv, sig: &Signature) -> Self {
let mut profile = Self::EMPTY;
for (mask, _coeff) in mv.blades() {
profile.absorb_mask(mask, sig);
}
profile
}
fn absorb_mask(&mut self, mask: BladeMask, sig: &Signature) {
for k in 0..sig.n() {
if mask & (1u64 << k) != 0 {
match sig.generator_square(k) {
-1 => self.i_participation |= 1u64 << k,
0 => self.d_participation |= 1u64 << k,
1 => self.h_participation |= 1u64 << k,
_ => {}
}
}
}
}
#[inline]
pub fn has_degenerate(&self) -> bool {
self.d_participation != 0
}
#[inline]
pub fn has_imaginary(&self) -> bool {
self.i_participation != 0
}
#[inline]
pub fn is_euclidean(&self) -> bool {
self.i_participation == 0 && self.d_participation == 0
}
pub fn union(&self, other: &Self) -> Self {
GeneratorProfile {
i_participation: self.i_participation | other.i_participation,
d_participation: self.d_participation | other.d_participation,
h_participation: self.h_participation | other.h_participation,
}
}
pub fn count(&self) -> u32 {
self.i_participation.count_ones()
+ self.d_participation.count_ones()
+ self.h_participation.count_ones()
}
pub fn all_participation(&self) -> u64 {
self.i_participation | self.d_participation | self.h_participation
}
pub fn predict_geometric(a: &Self, b: &Self) -> Self {
a.union(b)
}
pub fn predict_outer(a: &Self, b: &Self) -> Self {
a.union(b)
}
pub fn predict_sandwich(_m: &Self, v: &Self) -> Self {
*v
}
pub fn predict_unary(a: &Self) -> Self {
*a
}
}
impl std::fmt::Display for GeneratorProfile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Profile(i={:#b}, d={:#b}, h={:#b})",
self.i_participation, self.d_participation, self.h_participation
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::Rat;
#[test]
fn vga_vector_profile() {
let sig = Signature::new(0, 0, 3).unwrap();
let mv = Mv::from_rat_terms(&[
(0b001, Rat::from(3)),
(0b010, Rat::from(4)),
(0b100, Rat::from(5)),
]);
let p = GeneratorProfile::compute(&mv, &sig);
assert_eq!(p.h_participation, 0b111);
assert_eq!(p.i_participation, 0);
assert_eq!(p.d_participation, 0);
assert!(p.is_euclidean());
}
#[test]
fn pga_point_has_degenerate() {
let sig = Signature::new(0, 1, 3).unwrap();
let mv = Mv::from_rat_terms(&[
(0b0111, Rat::from(1)), (0b1110, Rat::from(1)), ]);
let p = GeneratorProfile::compute(&mv, &sig);
assert!(p.has_degenerate());
assert!(!p.is_euclidean());
}
#[test]
fn cga_point_has_imaginary() {
let sig = Signature::new(1, 0, 4).unwrap();
let mv = Mv::from_rat_terms(&[
(0b00001, Rat::from(1)), (0b00010, Rat::from(1)), (0b00100, Rat::from(2)), ]);
let p = GeneratorProfile::compute(&mv, &sig);
assert!(p.has_imaginary());
assert!(!p.has_degenerate());
}
#[test]
fn empty_mv_profile() {
let sig = Signature::new(0, 0, 3).unwrap();
let mv = Mv::new();
let p = GeneratorProfile::compute(&mv, &sig);
assert_eq!(p, GeneratorProfile::EMPTY);
}
#[test]
fn union_profiles() {
let a = GeneratorProfile {
i_participation: 0b01,
d_participation: 0,
h_participation: 0b10,
};
let b = GeneratorProfile {
i_participation: 0,
d_participation: 0b01,
h_participation: 0b01,
};
let u = a.union(&b);
assert_eq!(u.i_participation, 0b01);
assert_eq!(u.d_participation, 0b01);
assert_eq!(u.h_participation, 0b11);
}
#[test]
fn sandwich_preserves_v_profile() {
let m = GeneratorProfile {
i_participation: 0b11,
d_participation: 0,
h_participation: 0b1111,
};
let v = GeneratorProfile {
i_participation: 0,
d_participation: 0,
h_participation: 0b111,
};
let result = GeneratorProfile::predict_sandwich(&m, &v);
assert_eq!(result, v);
}
}