use super::basis::bits;
use super::terms::{merge, wedge_terms};
use crate::scalar::Scalar;
use std::collections::BTreeMap;
use std::fmt;
use std::ops::{Add, BitAnd, Neg, Sub};
#[derive(Clone, Debug, PartialEq)]
pub struct Multivector<S: Scalar> {
pub(crate) terms: BTreeMap<u128, S>,
}
impl<S: Scalar> Multivector<S> {
pub fn terms(&self) -> &BTreeMap<u128, S> {
&self.terms
}
pub fn is_zero(&self) -> bool {
self.terms.is_empty()
}
pub fn display(&self) -> String {
self.to_string()
}
}
impl<S: Scalar> fmt::Display for Multivector<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.terms.is_empty() {
return write!(f, "{}", S::zero());
}
let one = S::one();
let neg_one = S::one().neg();
let mut parts = Vec::new();
for (&blade, coeff) in &self.terms {
if blade == 0 {
parts.push(format!("{coeff}"));
continue;
}
let label: String = bits(blade)
.iter()
.map(|i| format!("e{i}"))
.collect::<Vec<_>>()
.join("∧");
if *coeff == one {
parts.push(label); } else if *coeff == neg_one {
parts.push(format!("-{label}")); } else {
parts.push(crate::scalar::poly::attach_coeff(coeff, &label));
}
}
let mut out = String::new();
for (idx, part) in parts.iter().enumerate() {
if let Some(stripped) = part.strip_prefix('-') {
if idx == 0 {
out.push('-');
out.push_str(stripped);
} else {
out.push_str(" - ");
out.push_str(stripped);
}
} else {
if idx != 0 {
out.push_str(" + ");
}
out.push_str(part);
}
}
write!(f, "{out}")
}
}
impl<S: Scalar> Add for Multivector<S> {
type Output = Multivector<S>;
fn add(self, rhs: Multivector<S>) -> Multivector<S> {
let mut terms = self.terms;
merge(&mut terms, rhs.terms);
Multivector { terms }
}
}
impl<S: Scalar> Neg for Multivector<S> {
type Output = Multivector<S>;
fn neg(self) -> Multivector<S> {
let terms = self
.terms
.into_iter()
.map(|(blade, coeff)| (blade, coeff.neg()))
.filter(|(_, coeff)| !coeff.is_zero())
.collect();
Multivector { terms }
}
}
impl<S: Scalar> Sub for Multivector<S> {
type Output = Multivector<S>;
fn sub(self, mut rhs: Multivector<S>) -> Multivector<S> {
for coeff in rhs.terms.values_mut() {
*coeff = coeff.neg();
}
let mut terms = self.terms;
merge(&mut terms, rhs.terms);
Multivector { terms }
}
}
impl<S: Scalar> BitAnd for Multivector<S> {
type Output = Multivector<S>;
fn bitand(self, rhs: Multivector<S>) -> Multivector<S> {
Multivector {
terms: wedge_terms(&self.terms, &rhs.terms),
}
}
}
#[cfg(test)]
mod tests {
use crate::clifford::{CliffordAlgebra, Metric};
use crate::scalar::{Integer, Nimber};
#[test]
fn char2_wedge_blade_and_coefficients() {
let alg = CliffordAlgebra::new(2, Metric::diagonal(vec![Nimber(1), Nimber(1)]));
let e0e1 = alg.wedge(&alg.e(0), &alg.e(1));
assert_eq!(e0e1.to_string(), "e0∧e1");
let three_e0e1 = alg.scalar_mul(&Nimber(3), &e0e1);
assert_eq!(three_e0e1.to_string(), "*3⋅e0∧e1");
let zero = e0e1.clone() - e0e1;
assert!(zero.is_zero());
assert_eq!(zero.to_string(), "*0");
}
#[test]
fn integer_grassmann_negative_and_join_rule() {
let alg = CliffordAlgebra::new(2, Metric::<Integer>::grassmann(2));
let e0e1 = alg.wedge(&alg.e(0), &alg.e(1));
let neg2 = alg.scalar_mul(&Integer(-2), &e0e1);
assert_eq!(neg2.to_string(), "-2⋅e0∧e1");
let mixed = alg.scalar_mul(&Integer(3), &alg.e(0)) - alg.scalar_mul(&Integer(2), &alg.e(1));
assert_eq!(mixed.to_string(), "3⋅e0 - 2⋅e1");
assert_eq!(alg.e(0).to_string(), "e0");
let neg_e0 = alg.scalar_mul(&Integer(-1), &alg.e(0));
assert_eq!(neg_e0.to_string(), "-e0");
let z = alg.e(0) - alg.e(0);
assert_eq!(z.to_string(), "0");
}
}