use crate::algebra::inverse::{self, InverseError};
use crate::algebra::mv::Mv;
use crate::algebra::ops;
use crate::algebra::signature::Signature;
use crate::scalar::Scalar;
#[derive(Clone, Debug)]
pub struct Algebra {
sig: Signature,
derived_gens: Vec<Mv>,
derived_names: Vec<String>,
}
impl Algebra {
pub fn new(sig: Signature) -> Self {
Algebra {
sig,
derived_gens: Vec::new(),
derived_names: Vec::new(),
}
}
pub fn add_derived(&mut self, name: &str, mv: Mv) -> &mut Self {
self.derived_names.push(name.to_string());
self.derived_gens.push(mv);
self
}
pub fn sig(&self) -> &Signature {
&self.sig
}
pub fn n(&self) -> u8 {
self.sig.n()
}
pub fn gen(&self, k: u8) -> Mv {
Mv::generator(k)
}
pub fn dgen(&self, name: &str) -> Result<&Mv, crate::error::NotFoundError> {
let idx = self
.derived_names
.iter()
.position(|n| n == name)
.ok_or_else(|| crate::error::NotFoundError::DerivedGen(name.to_string()))?;
Ok(&self.derived_gens[idx])
}
pub fn dgen_by_index(&self, i: usize) -> &Mv {
&self.derived_gens[i]
}
pub fn derived_gens(&self) -> &[Mv] {
&self.derived_gens
}
pub fn geometric(&self, a: &Mv, b: &Mv) -> Mv {
ops::geometric(a, b, &self.sig)
}
pub fn outer(&self, a: &Mv, b: &Mv) -> Mv {
ops::outer(a, b, &self.sig)
}
pub fn inner(&self, a: &Mv, b: &Mv) -> Mv {
ops::inner(a, b, &self.sig)
}
pub fn left_contract(&self, a: &Mv, b: &Mv) -> Mv {
ops::left_contract(a, b, &self.sig)
}
pub fn right_contract(&self, a: &Mv, b: &Mv) -> Mv {
ops::right_contract(a, b, &self.sig)
}
pub fn scalar_product(&self, a: &Mv, b: &Mv) -> Mv {
ops::scalar_product(a, b, &self.sig)
}
pub fn scalar_product_value(&self, a: &Mv, b: &Mv) -> Scalar {
ops::scalar_product_value(a, b, &self.sig)
}
pub fn commutator(&self, a: &Mv, b: &Mv) -> Mv {
ops::commutator(a, b, &self.sig)
}
pub fn sandwich(&self, m: &Mv, v: &Mv) -> Mv {
ops::sandwich(m, v, &self.sig)
}
pub fn regressive(&self, a: &Mv, b: &Mv) -> Mv {
ops::regressive(a, b, &self.sig)
}
pub fn reverse(&self, a: &Mv) -> Mv {
ops::reverse(a)
}
pub fn grade_involution(&self, a: &Mv) -> Mv {
ops::grade_involution(a)
}
pub fn conjugate(&self, a: &Mv) -> Mv {
ops::conjugate(a)
}
pub fn dual(&self, a: &Mv) -> Mv {
ops::dual(a, &self.sig)
}
pub fn undual(&self, a: &Mv) -> Mv {
ops::undual(a, &self.sig)
}
pub fn grade_project(&self, a: &Mv, k: u8) -> Mv {
ops::grade_project(a, k)
}
pub fn norm_squared(&self, a: &Mv) -> Scalar {
ops::norm_squared(a, &self.sig)
}
pub fn inverse(&self, m: &Mv) -> Result<Mv, InverseError> {
inverse::inverse(m, &self.sig)
}
pub fn is_versor(&self, m: &Mv) -> bool {
inverse::is_versor(m, &self.sig)
}
pub fn sandwich_normalized(&self, m: &Mv, v: &Mv) -> Result<Mv, InverseError> {
inverse::sandwich_normalized(m, v, &self.sig)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::Rat;
#[test]
fn algebra_basic_ops() {
let alg = Algebra::new(Signature::new(0, 0, 3).unwrap());
let e0 = alg.gen(0);
let e1 = alg.gen(1);
let geo = alg.geometric(&e0, &e1);
assert_eq!(geo.coefficient(0b11), Scalar::from(1));
}
#[test]
fn algebra_derived_gen() {
let mut alg = Algebra::new(Signature::new(1, 0, 3).unwrap());
let eo = Mv::from_rat_terms(&[(0b0001, Rat::new(1, 2)), (0b0010, Rat::new(1, 2))]);
alg.add_derived("eo", eo.clone());
assert_eq!(*alg.dgen("eo").unwrap(), eo);
}
#[test]
fn algebra_missing_dgen() {
let alg = Algebra::new(Signature::new(0, 0, 3).unwrap());
assert!(alg.dgen("nonexistent").is_err());
}
#[test]
fn algebra_inverse() {
let alg = Algebra::new(Signature::new(0, 0, 3).unwrap());
let v = alg.gen(0);
let inv = alg.inverse(&v).unwrap();
let product = alg.geometric(&v, &inv);
assert_eq!(product.coefficient(0), Scalar::from(1));
}
}