use std::ops::{Add, BitAnd, BitOr, Mul, Neg, Sub};
const N_BLADES: usize = 32;
const BASIS_LABELS: [&str; 32] = [
"1", "e1", "e2", "e12", "e3", "e13", "e23", "e123", "e+", "e1+", "e2+", "e12+", "e3+", "e13+",
"e23+", "e123+", "e-", "e1-", "e2-", "e12-", "e3-", "e13-", "e23-", "e123-", "e+-", "e1+-",
"e2+-", "e12+-", "e3+-", "e13+-", "e23+-", "e123+-",
];
const METRIC: [f64; 5] = [1.0, 1.0, 1.0, 1.0, -1.0];
#[derive(Debug, Clone, Copy)]
pub struct Multivector {
pub data: [f64; N_BLADES],
}
impl Multivector {
pub const ZERO: Self = Self {
data: [0.0; N_BLADES],
};
pub fn one() -> Self {
let mut m = Self::ZERO;
m.data[0] = 1.0;
m
}
pub fn basis(i: usize) -> Self {
assert!(i < 5);
let mut m = Self::ZERO;
m.data[1 << i] = 1.0;
m
}
pub fn blade(index: usize, value: f64) -> Self {
let mut m = Self::ZERO;
m.data[index] = value;
m
}
pub fn origin() -> Self {
let mut m = Self::ZERO;
m.data[1 << 4] = 0.5; m.data[1 << 3] = -0.5; m
}
pub fn infinity() -> Self {
let mut m = Self::ZERO;
m.data[1 << 4] = 1.0; m.data[1 << 3] = 1.0; m
}
pub fn scalar(&self) -> f64 {
self.data[0]
}
pub fn component(&self, blade: usize) -> f64 {
self.data[blade]
}
fn blade_grade(blade: usize) -> u32 {
(blade as u32).count_ones()
}
pub fn grade(&self, k: u32) -> Self {
let mut m = Self::ZERO;
for i in 0..N_BLADES {
if Self::blade_grade(i) == k {
m.data[i] = self.data[i];
}
}
m
}
pub fn reverse(&self) -> Self {
let mut m = Self::ZERO;
for i in 0..N_BLADES {
let k = Self::blade_grade(i);
let sign = if (k / 2) % 2 == 0 { 1.0 } else { -1.0 };
m.data[i] = sign * self.data[i];
}
m
}
pub fn involute(&self) -> Self {
let mut m = Self::ZERO;
for i in 0..N_BLADES {
let k = Self::blade_grade(i);
let sign = if k % 2 == 0 { 1.0 } else { -1.0 };
m.data[i] = sign * self.data[i];
}
m
}
pub fn conjugate(&self) -> Self {
self.reverse().involute()
}
pub fn norm_squared(&self) -> f64 {
(*self * self.reverse()).scalar()
}
pub fn norm(&self) -> f64 {
self.norm_squared().abs().sqrt()
}
pub fn normalized(&self) -> Self {
let n = self.norm();
if n < 1e-15 {
return *self;
}
*self * (1.0 / n)
}
fn geo_sign_blade(a: usize, b: usize) -> (f64, usize) {
let mut sign = 1.0;
let mut result = a;
for j in 0..5 {
if b & (1 << j) == 0 {
continue;
}
let mut swaps = 0;
for k in (j + 1)..5 {
if result & (1 << k) != 0 {
swaps += 1;
}
}
if swaps % 2 != 0 {
sign = -sign;
}
if result & (1 << j) != 0 {
sign *= METRIC[j];
result ^= 1 << j;
} else {
result ^= 1 << j;
}
}
(sign, result)
}
pub fn geo(&self, other: &Self) -> Self {
let mut result = Self::ZERO;
for i in 0..N_BLADES {
if self.data[i] == 0.0 {
continue;
}
for j in 0..N_BLADES {
if other.data[j] == 0.0 {
continue;
}
let (sign, blade) = Self::geo_sign_blade(i, j);
result.data[blade] += sign * self.data[i] * other.data[j];
}
}
result
}
pub fn outer(&self, other: &Self) -> Self {
let mut result = Self::ZERO;
for i in 0..N_BLADES {
if self.data[i] == 0.0 {
continue;
}
let gi = Self::blade_grade(i);
for j in 0..N_BLADES {
if other.data[j] == 0.0 {
continue;
}
let gj = Self::blade_grade(j);
let (sign, blade) = Self::geo_sign_blade(i, j);
if Self::blade_grade(blade) == gi + gj {
result.data[blade] += sign * self.data[i] * other.data[j];
}
}
}
result
}
pub fn inner(&self, other: &Self) -> Self {
let mut result = Self::ZERO;
for i in 0..N_BLADES {
if self.data[i] == 0.0 {
continue;
}
let gi = Self::blade_grade(i);
for j in 0..N_BLADES {
if other.data[j] == 0.0 {
continue;
}
let gj = Self::blade_grade(j);
if gj < gi {
continue;
}
let (sign, blade) = Self::geo_sign_blade(i, j);
if Self::blade_grade(blade) == gj - gi {
result.data[blade] += sign * self.data[i] * other.data[j];
}
}
}
result
}
pub fn sandwich(&self, x: &Self) -> Self {
self.geo(x).geo(&self.reverse())
}
pub fn is_zero(&self, tol: f64) -> bool {
self.data.iter().all(|&v| v.abs() < tol)
}
pub fn blade_label(i: usize) -> &'static str {
BASIS_LABELS[i]
}
}
impl Add for Multivector {
type Output = Self;
fn add(self, rhs: Self) -> Self {
let mut m = Self::ZERO;
for i in 0..N_BLADES {
m.data[i] = self.data[i] + rhs.data[i];
}
m
}
}
impl Sub for Multivector {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
let mut m = Self::ZERO;
for i in 0..N_BLADES {
m.data[i] = self.data[i] - rhs.data[i];
}
m
}
}
impl Neg for Multivector {
type Output = Self;
fn neg(self) -> Self {
let mut m = Self::ZERO;
for i in 0..N_BLADES {
m.data[i] = -self.data[i];
}
m
}
}
impl Mul<Multivector> for Multivector {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
self.geo(&rhs)
}
}
impl Mul<f64> for Multivector {
type Output = Self;
fn mul(self, rhs: f64) -> Self {
let mut m = Self::ZERO;
for i in 0..N_BLADES {
m.data[i] = self.data[i] * rhs;
}
m
}
}
impl Mul<Multivector> for f64 {
type Output = Multivector;
fn mul(self, rhs: Multivector) -> Multivector {
rhs * self
}
}
impl BitAnd for Multivector {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
self.outer(&rhs)
}
}
impl BitOr for Multivector {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
self.inner(&rhs)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basis_anticommute() {
for i in 0..5 {
for j in (i + 1)..5 {
let ei = Multivector::basis(i);
let ej = Multivector::basis(j);
let eij = ei * ej;
let eji = ej * ei;
let sum = eij + eji;
assert!(
sum.is_zero(1e-14),
"e{} e{} + e{} e{} is not zero",
i,
j,
j,
i
);
}
}
}
#[test]
fn test_basis_metric() {
for i in 0..5 {
let ei = Multivector::basis(i);
let sq = ei * ei;
assert!(
(sq.scalar() - METRIC[i]).abs() < 1e-14,
"e{}^2 = {}, expected {}",
i,
sq.scalar(),
METRIC[i]
);
}
}
#[test]
fn test_null_basis_properties() {
let ep = Multivector::basis(3); let em = Multivector::basis(4); let sum = ep * em + em * ep;
assert!(sum.is_zero(1e-14), "e+ e- + e- e+ should be 0");
let eo = Multivector::origin();
let einf = Multivector::infinity();
let dot = eo | einf;
assert!(
(dot.scalar() - (-1.0)).abs() < 1e-14,
"e_o · e_inf = {}, expected -1",
dot.scalar()
);
}
#[test]
fn test_reverse_identity_for_scalars() {
let s = Multivector::blade(0, std::f64::consts::PI);
let rev = s.reverse();
assert!((rev.scalar() - std::f64::consts::PI).abs() < 1e-14);
}
#[test]
fn test_unit_motor_reverse_is_identity() {
let angle = std::f64::consts::FRAC_PI_4; let half = angle / 2.0;
let mut r = Multivector::ZERO;
r.data[0] = half.cos(); r.data[3] = -half.sin(); let product = r * r.reverse();
assert!(
(product.scalar() - 1.0).abs() < 1e-12,
"R ~R scalar = {}, expected 1",
product.scalar()
);
for i in 1..32 {
assert!(
product.data[i].abs() < 1e-12,
"R ~R blade {} = {}, expected 0",
i,
product.data[i]
);
}
}
}