Skip to main content

amari_core/
lib.rs

1//! High-performance Geometric Algebra/Clifford Algebra library
2//!
3//! This crate provides the core implementation of Clifford algebras with arbitrary signatures,
4//! supporting the geometric product and related operations fundamental to geometric algebra.
5//!
6//! # Basis Blade Indexing
7//!
8//! Basis blades are indexed using binary representation where bit i indicates whether
9//! basis vector e_i is present in the blade. For example:
10//! - 0b001 (1) = e1
11//! - 0b010 (2) = e2
12//! - 0b011 (3) = e1 ∧ e2 (bivector)
13//! - 0b111 (7) = e1 ∧ e2 ∧ e3 (trivector)
14
15#![cfg_attr(not(feature = "std"), no_std)]
16
17extern crate alloc;
18use alloc::boxed::Box;
19use alloc::vec;
20use alloc::vec::Vec;
21use core::ops::{Add, Mul, Neg, Sub};
22use num_traits::Zero;
23
24#[allow(dead_code)]
25pub(crate) mod aligned_alloc;
26pub mod basis;
27pub mod cayley;
28pub mod error;
29pub mod generic;
30pub mod precision;
31pub mod rotor;
32pub mod unicode_ops;
33
34#[cfg(feature = "gf2")]
35pub mod gf2;
36
37#[allow(dead_code)]
38#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
39pub(crate) mod simd;
40
41// Re-export error types
42pub use error::{CoreError, CoreResult};
43
44// Re-export precision types for high-precision arithmetic
45pub use precision::{PrecisionFloat, StandardFloat};
46
47#[cfg(any(
48    feature = "high-precision",
49    feature = "wasm-precision",
50    feature = "native-precision"
51))]
52pub use precision::ExtendedFloat;
53
54// For backward compatibility, re-export ExtendedFloat as HighPrecisionFloat
55#[cfg(any(
56    feature = "high-precision",
57    feature = "wasm-precision",
58    feature = "native-precision"
59))]
60pub use precision::ExtendedFloat as HighPrecisionFloat;
61
62#[cfg(feature = "phantom-types")]
63pub mod verified;
64
65#[cfg(feature = "formal-verification")]
66pub mod verified_contracts;
67
68#[cfg(test)]
69pub mod property_tests;
70
71#[cfg(test)]
72pub mod comprehensive_tests;
73
74pub use cayley::CayleyTable;
75
76/// A multivector in a Clifford algebra Cl(P,Q,R)
77///
78/// # Type Parameters
79/// - `P`: Number of basis vectors that square to +1
80/// - `Q`: Number of basis vectors that square to -1  
81/// - `R`: Number of basis vectors that square to 0
82#[derive(Debug, Clone, PartialEq)]
83#[repr(C, align(32))] // AVX2-optimal alignment for SIMD performance
84pub struct Multivector<const P: usize, const Q: usize, const R: usize> {
85    /// Coefficients for each basis blade, indexed by binary representation
86    /// Memory layout optimized for cache lines and SIMD operations
87    coefficients: Box<[f64]>,
88}
89
90impl<const P: usize, const Q: usize, const R: usize> Multivector<P, Q, R> {
91    /// Total dimension of the algebra's vector space
92    pub const DIM: usize = P + Q + R;
93
94    /// Total number of basis blades (2^DIM)
95    pub const BASIS_COUNT: usize = 1 << Self::DIM;
96
97    /// Create a new zero multivector
98    #[inline(always)]
99    pub fn zero() -> Self {
100        Self {
101            coefficients: vec![0.0; Self::BASIS_COUNT].into_boxed_slice(),
102        }
103    }
104
105    /// Create a scalar multivector
106    #[inline(always)]
107    pub fn scalar(value: f64) -> Self {
108        let mut mv = Self::zero();
109        mv.coefficients[0] = value;
110        mv
111    }
112
113    /// Create multivector from vector
114    pub fn from_vector(vector: &Vector<P, Q, R>) -> Self {
115        vector.mv.clone()
116    }
117
118    /// Create multivector from bivector
119    pub fn from_bivector(bivector: &Bivector<P, Q, R>) -> Self {
120        bivector.mv.clone()
121    }
122
123    /// Create a basis vector e_i (i starts from 0)
124    #[inline(always)]
125    pub fn basis_vector(i: usize) -> Self {
126        assert!(i < Self::DIM, "Basis vector index out of range");
127        let mut mv = Self::zero();
128        mv.coefficients[1 << i] = 1.0;
129        mv
130    }
131
132    /// Create a multivector from coefficients
133    #[inline(always)]
134    pub fn from_coefficients(coefficients: Vec<f64>) -> Self {
135        assert_eq!(
136            coefficients.len(),
137            Self::BASIS_COUNT,
138            "Coefficient array must have {} elements",
139            Self::BASIS_COUNT
140        );
141        Self {
142            coefficients: coefficients.into_boxed_slice(),
143        }
144    }
145
146    /// Create a multivector from a slice (convenience for tests)
147    #[inline(always)]
148    pub fn from_slice(coefficients: &[f64]) -> Self {
149        Self::from_coefficients(coefficients.to_vec())
150    }
151
152    /// Get coefficient for a specific basis blade (by index)
153    #[inline(always)]
154    pub fn get(&self, index: usize) -> f64 {
155        self.coefficients.get(index).copied().unwrap_or(0.0)
156    }
157
158    /// Set coefficient for a specific basis blade
159    #[inline(always)]
160    pub fn set(&mut self, index: usize, value: f64) {
161        if index < self.coefficients.len() {
162            self.coefficients[index] = value;
163        }
164    }
165
166    /// Get the scalar part (grade 0)
167    #[inline(always)]
168    pub fn scalar_part(&self) -> f64 {
169        self.coefficients[0]
170    }
171
172    /// Set the scalar part
173    #[inline(always)]
174    pub fn set_scalar(&mut self, value: f64) {
175        self.coefficients[0] = value;
176    }
177
178    /// Get vector part as a Vector type
179    pub fn vector_part(&self) -> Vector<P, Q, R> {
180        Vector::from_multivector(&self.grade_projection(1))
181    }
182
183    /// Get bivector part as a Multivector (grade 2 projection)
184    pub fn bivector_part(&self) -> Self {
185        self.grade_projection(2)
186    }
187
188    /// Get bivector part as a Bivector type wrapper
189    pub fn bivector_type(&self) -> Bivector<P, Q, R> {
190        Bivector::from_multivector(&self.grade_projection(2))
191    }
192
193    /// Get trivector part (scalar for 3D)
194    pub fn trivector_part(&self) -> f64 {
195        if Self::DIM >= 3 {
196            self.get(7) // e123 for 3D
197        } else {
198            0.0
199        }
200    }
201
202    /// Set vector component
203    pub fn set_vector_component(&mut self, index: usize, value: f64) {
204        if index < Self::DIM {
205            self.coefficients[1 << index] = value;
206        }
207    }
208
209    /// Set bivector component
210    pub fn set_bivector_component(&mut self, index: usize, value: f64) {
211        // Map index to bivector blade indices
212        let bivector_indices = match Self::DIM {
213            3 => [3, 5, 6], // e12, e13, e23
214            _ => [3, 5, 6], // Default mapping
215        };
216        if index < bivector_indices.len() {
217            self.coefficients[bivector_indices[index]] = value;
218        }
219    }
220
221    /// Get vector component
222    pub fn vector_component(&self, index: usize) -> f64 {
223        if index < Self::DIM {
224            self.get(1 << index)
225        } else {
226            0.0
227        }
228    }
229
230    /// Get coefficients as slice for comparison
231    pub fn as_slice(&self) -> &[f64] {
232        &self.coefficients
233    }
234
235    /// Convert coefficients to owned vector
236    ///
237    /// Useful for interoperability with other libraries and serialization.
238    pub fn to_vec(&self) -> Vec<f64> {
239        self.coefficients.to_vec()
240    }
241
242    /// Grade projection with alternative name for clarity
243    ///
244    /// Alias for `grade_projection` that emphasizes the projection operation.
245    pub fn grade_project(&self, grade: usize) -> Self {
246        self.grade_projection(grade)
247    }
248
249    /// Wedge product (alias for outer product)
250    ///
251    /// The wedge product ∧ is the antisymmetric outer product,
252    /// fundamental to exterior algebra and differential forms.
253    pub fn wedge(&self, other: &Self) -> Self {
254        self.outer_product(other)
255    }
256
257    /// Dot product (alias for inner product)
258    ///
259    /// The dot product · is the grade-lowering inner product,
260    /// generalizing the vector dot product to all grades.
261    pub fn dot(&self, other: &Self) -> Self {
262        self.inner_product(other)
263    }
264
265    /// Add method for convenience
266    pub fn add(&self, other: &Self) -> Self {
267        self + other
268    }
269
270    /// Get the grade of a multivector (returns the highest non-zero grade)
271    pub fn grade(&self) -> usize {
272        for grade in (0..=Self::DIM).rev() {
273            let projection = self.grade_projection(grade);
274            if !projection.is_zero() {
275                return grade;
276            }
277        }
278        0 // Zero multivector has grade 0
279    }
280
281    /// Outer product with a vector (convenience method)
282    pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Self {
283        self.outer_product(&other.mv)
284    }
285
286    /// Geometric product with another multivector
287    ///
288    /// The geometric product is the fundamental operation in geometric algebra,
289    /// combining both the inner and outer products.
290    pub fn geometric_product(&self, rhs: &Self) -> Self {
291        self.geometric_product_scalar(rhs)
292    }
293
294    /// Scalar implementation of geometric product (fallback)
295    pub(crate) fn geometric_product_scalar(&self, rhs: &Self) -> Self {
296        let table = CayleyTable::<P, Q, R>::get();
297        let mut result = Self::zero();
298
299        for i in 0..Self::BASIS_COUNT {
300            if self.coefficients[i].abs() < 1e-14 {
301                continue;
302            }
303
304            for j in 0..Self::BASIS_COUNT {
305                if rhs.coefficients[j].abs() < 1e-14 {
306                    continue;
307                }
308
309                let (sign, index) = table.get_product(i, j);
310                result.coefficients[index] += sign * self.coefficients[i] * rhs.coefficients[j];
311            }
312        }
313
314        result
315    }
316
317    /// Inner product (grade-lowering, dot product for vectors)
318    pub fn inner_product(&self, rhs: &Self) -> Self {
319        let self_grades = self.grade_decomposition();
320        let rhs_grades = rhs.grade_decomposition();
321        let mut result = Self::zero();
322
323        // Inner product selects terms where grade(result) = |grade(a) - grade(b)|
324        for (grade_a, mv_a) in self_grades.iter().enumerate() {
325            for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
326                if !mv_a.is_zero() && !mv_b.is_zero() {
327                    let target_grade = grade_a.abs_diff(grade_b);
328                    let product = mv_a.geometric_product(mv_b);
329                    let projected = product.grade_projection(target_grade);
330                    result = result + projected;
331                }
332            }
333        }
334
335        result
336    }
337
338    /// Outer product (wedge product, grade-raising)
339    pub fn outer_product(&self, rhs: &Self) -> Self {
340        let self_grades = self.grade_decomposition();
341        let rhs_grades = rhs.grade_decomposition();
342        let mut result = Self::zero();
343
344        // Outer product selects terms where grade(result) = grade(a) + grade(b)
345        for (grade_a, mv_a) in self_grades.iter().enumerate() {
346            for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
347                if !mv_a.is_zero() && !mv_b.is_zero() {
348                    let target_grade = grade_a + grade_b;
349                    if target_grade <= Self::DIM {
350                        let product = mv_a.geometric_product(mv_b);
351                        let projected = product.grade_projection(target_grade);
352                        result = result + projected;
353                    }
354                }
355            }
356        }
357
358        result
359    }
360
361    /// Scalar product (grade-0 part of geometric product)
362    pub fn scalar_product(&self, rhs: &Self) -> f64 {
363        self.geometric_product(rhs).scalar_part()
364    }
365
366    /// Calculate the sign change for reversing a blade of given grade
367    ///
368    /// The reverse operation introduces a sign change of (-1)^(grade*(grade-1)/2)
369    /// This comes from the fact that reversing a k-blade requires k(k-1)/2 swaps
370    /// of basis vectors, and each swap introduces a sign change.
371    #[inline]
372    fn reverse_sign_for_grade(grade: usize) -> f64 {
373        if grade == 0 {
374            return 1.0;
375        }
376        if (grade * (grade - 1) / 2).is_multiple_of(2) {
377            1.0
378        } else {
379            -1.0
380        }
381    }
382
383    /// Reverse operation (reverses order of basis vectors in each blade)
384    pub fn reverse(&self) -> Self {
385        let mut result = Self::zero();
386
387        for i in 0..Self::BASIS_COUNT {
388            let grade = i.count_ones() as usize;
389            let sign = Self::reverse_sign_for_grade(grade);
390            result.coefficients[i] = sign * self.coefficients[i];
391        }
392
393        result
394    }
395
396    /// Grade projection - extract components of a specific grade
397    pub fn grade_projection(&self, grade: usize) -> Self {
398        let mut result = Self::zero();
399
400        for i in 0..Self::BASIS_COUNT {
401            if i.count_ones() as usize == grade {
402                result.coefficients[i] = self.coefficients[i];
403            }
404        }
405
406        result
407    }
408
409    /// Compute the magnitude of a specific grade component.
410    ///
411    /// This extracts the k-vector part and computes its magnitude.
412    /// Useful for determining grade dominance in holographic representations.
413    ///
414    /// # Arguments
415    /// * `grade` - The grade to compute magnitude for (0=scalar, 1=vector, 2=bivector, etc.)
416    ///
417    /// # Returns
418    /// The magnitude of the grade-k component, or 0.0 if no such component exists.
419    ///
420    /// # Example
421    /// ```rust
422    /// use amari_core::Multivector;
423    /// let mv = Multivector::<3,0,0>::basis_vector(0) + Multivector::<3,0,0>::scalar(2.0);
424    /// assert!((mv.grade_magnitude(0) - 2.0).abs() < 1e-10);  // scalar part
425    /// assert!((mv.grade_magnitude(1) - 1.0).abs() < 1e-10);  // vector part
426    /// ```
427    pub fn grade_magnitude(&self, grade: usize) -> f64 {
428        self.grade_projection(grade).magnitude()
429    }
430
431    /// Get all grade magnitudes as a vector.
432    ///
433    /// Returns the magnitude of each grade component from 0 to DIM.
434    /// This provides a "grade spectrum" view of the multivector.
435    pub fn grade_spectrum(&self) -> Vec<f64> {
436        (0..=Self::DIM).map(|g| self.grade_magnitude(g)).collect()
437    }
438
439    /// Decompose into grade components
440    fn grade_decomposition(&self) -> Vec<Self> {
441        let mut grades = Vec::with_capacity(Self::DIM + 1);
442        for _ in 0..=Self::DIM {
443            grades.push(Self::zero());
444        }
445
446        for i in 0..Self::BASIS_COUNT {
447            let grade = i.count_ones() as usize;
448            grades[grade].coefficients[i] = self.coefficients[i];
449        }
450
451        grades
452    }
453
454    /// Check if this is (approximately) zero
455    pub fn is_zero(&self) -> bool {
456        self.coefficients.iter().all(|&c| c.abs() < 1e-14)
457    }
458
459    /// Compute the norm squared (scalar product with reverse)
460    pub fn norm_squared(&self) -> f64 {
461        self.scalar_product(&self.reverse())
462    }
463
464    /// Compute the magnitude (length) of this multivector
465    ///
466    /// The magnitude is defined as |a| = √(a·ã) where ã is the reverse of a.
467    /// This provides the natural norm inherited from the underlying vector space.
468    ///
469    /// # Mathematical Properties
470    /// - Always non-negative: |a| ≥ 0
471    /// - Zero iff a = 0: |a| = 0 ⟺ a = 0
472    /// - Sub-multiplicative: |ab| ≤ |a||b|
473    ///
474    /// # Examples
475    /// ```rust
476    /// use amari_core::Multivector;
477    /// let v = Multivector::<3,0,0>::basis_vector(0);
478    /// assert_eq!(v.magnitude(), 1.0);
479    /// ```
480    pub fn magnitude(&self) -> f64 {
481        self.norm_squared().abs().sqrt()
482    }
483
484    /// Compute the norm (magnitude) of this multivector
485    ///
486    /// **Note**: This method is maintained for backward compatibility.
487    /// New code should prefer [`magnitude()`](Self::magnitude) for clarity.
488    pub fn norm(&self) -> f64 {
489        self.magnitude()
490    }
491
492    /// Absolute value (same as magnitude/norm for multivectors)
493    pub fn abs(&self) -> f64 {
494        self.magnitude()
495    }
496
497    /// Approximate equality comparison
498    pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool {
499        (self.clone() - other.clone()).magnitude() < epsilon
500    }
501
502    /// Normalize this multivector
503    pub fn normalize(&self) -> Option<Self> {
504        let norm = self.norm();
505        if norm > 1e-14 {
506            Some(self * (1.0 / norm))
507        } else {
508            None
509        }
510    }
511
512    /// Compute multiplicative inverse if it exists
513    pub fn inverse(&self) -> Option<Self> {
514        let rev = self.reverse();
515        let norm_sq = self.scalar_product(&rev);
516
517        if norm_sq.abs() > 1e-14 {
518            Some(rev * (1.0 / norm_sq))
519        } else {
520            None
521        }
522    }
523
524    /// Exponential map for bivectors (creates rotors)
525    ///
526    /// For a bivector B, exp(B) creates a rotor that performs rotation
527    /// in the plane defined by B.
528    pub fn exp(&self) -> Self {
529        // Check if this is a bivector (grade 2)
530        let grade2 = self.grade_projection(2);
531        if (self - &grade2).norm() > 1e-10 {
532            // For general multivectors, use series expansion
533            return self.exp_series();
534        }
535
536        // For pure bivectors, use closed form
537        let b_squared = grade2.geometric_product(&grade2).scalar_part();
538
539        if b_squared > -1e-14 {
540            // Hyperbolic case: exp(B) = cosh(|B|) + sinh(|B|) * B/|B|
541            let norm = b_squared.abs().sqrt();
542            if norm < 1e-14 {
543                return Self::scalar(1.0);
544            }
545            let cosh_norm = norm.cosh();
546            let sinh_norm = norm.sinh();
547            Self::scalar(cosh_norm) + grade2 * (sinh_norm / norm)
548        } else {
549            // Circular case: exp(B) = cos(|B|) + sin(|B|) * B/|B|
550            let norm = (-b_squared).sqrt();
551            let cos_norm = norm.cos();
552            let sin_norm = norm.sin();
553            Self::scalar(cos_norm) + grade2 * (sin_norm / norm)
554        }
555    }
556
557    /// Series expansion for exponential (fallback for general multivectors)
558    fn exp_series(&self) -> Self {
559        let mut result = Self::scalar(1.0);
560        let mut term = Self::scalar(1.0);
561
562        for n in 1..20 {
563            term = term.geometric_product(self) * (1.0 / n as f64);
564            let old_result = result.clone();
565            result = result + term.clone();
566
567            // Check convergence
568            if (result.clone() - old_result).norm() < 1e-14 {
569                break;
570            }
571        }
572
573        result
574    }
575
576    /// Left contraction: a ⌟ b
577    ///
578    /// Generalized inner product where the grade of the result
579    /// is |grade(b) - grade(a)|. The left operand's grade is reduced.
580    pub fn left_contraction(&self, other: &Self) -> Self {
581        let self_grades = self.grade_decomposition();
582        let other_grades = other.grade_decomposition();
583        let mut result = Self::zero();
584
585        for (a_grade, mv_a) in self_grades.iter().enumerate() {
586            if mv_a.is_zero() {
587                continue;
588            }
589
590            for (b_grade, mv_b) in other_grades.iter().enumerate() {
591                if mv_b.is_zero() {
592                    continue;
593                }
594
595                // Left contraction: grade of result is |b_grade - a_grade|
596                if b_grade >= a_grade {
597                    let target_grade = b_grade - a_grade;
598                    let product = mv_a.geometric_product(mv_b);
599                    let projected = product.grade_projection(target_grade);
600                    result = result + projected;
601                }
602            }
603        }
604
605        result
606    }
607
608    /// Right contraction: a ⌞ b  
609    ///
610    /// Generalized inner product where the grade of the result
611    /// is |grade(a) - grade(b)|. The right operand's grade is reduced.
612    pub fn right_contraction(&self, other: &Self) -> Self {
613        let self_grades = self.grade_decomposition();
614        let other_grades = other.grade_decomposition();
615        let mut result = Self::zero();
616
617        for (a_grade, mv_a) in self_grades.iter().enumerate() {
618            if mv_a.is_zero() {
619                continue;
620            }
621
622            for (b_grade, mv_b) in other_grades.iter().enumerate() {
623                if mv_b.is_zero() {
624                    continue;
625                }
626
627                // Right contraction: grade of result is |a_grade - b_grade|
628                if a_grade >= b_grade {
629                    let target_grade = a_grade - b_grade;
630                    let product = mv_a.geometric_product(mv_b);
631                    let projected = product.grade_projection(target_grade);
632                    result = result + projected;
633                }
634            }
635        }
636
637        result
638    }
639
640    /// Hodge dual: ⋆a
641    ///
642    /// Maps k-vectors to (n-k)-vectors in n-dimensional space.
643    /// Defined such that `e_A ∧ ⋆e_A = |e_A|² · I` where I is the pseudoscalar.
644    ///
645    /// In 3D Euclidean (Cl(3,0,0)): ⋆e1 = e23, ⋆e2 = -e13, ⋆e3 = e12
646    ///
647    /// For degenerate algebras (R > 0), the pseudoscalar squares to zero
648    /// and the Hodge dual is not well-defined. In this case, the function
649    /// computes a formal complement using only the permutation sign.
650    pub fn hodge_dual(&self) -> Self {
651        let n = Self::DIM;
652        if n == 0 {
653            return self.clone();
654        }
655
656        let mut result = Self::zero();
657        let pseudoscalar_index = (1 << n) - 1; // All bits set
658
659        for i in 0..Self::BASIS_COUNT {
660            if self.coefficients[i].abs() < 1e-14 {
661                continue;
662            }
663
664            let dual_index = i ^ pseudoscalar_index;
665
666            // Compute permutation sign: e_A ∧ e_complement = sign * I
667            // Count swaps needed to interleave bits of i with bits of dual_index
668            // into canonical order (ascending bit positions)
669            let mut swaps = 0u32;
670            for bit_pos in 0..n {
671                if (i >> bit_pos) & 1 == 1 {
672                    // Count bits in dual_index at positions below bit_pos
673                    let below_mask = (1 << bit_pos) - 1;
674                    swaps += (dual_index & below_mask).count_ones();
675                }
676            }
677            let permutation_sign: f64 = if swaps.is_multiple_of(2) { 1.0 } else { -1.0 };
678
679            // Apply metric factor for the blade being dualized
680            // |e_A|² = product of e_i² for each basis vector in A
681            let mut metric_factor = 1.0;
682            for j in 0..n {
683                if (i >> j) & 1 == 1 {
684                    if j >= P + Q {
685                        // Null basis vector: |e_A|² = 0
686                        metric_factor = 0.0;
687                        break;
688                    } else if j >= P {
689                        // Negative basis vector: contributes -1
690                        metric_factor *= -1.0;
691                    }
692                    // Positive basis vector: contributes +1
693                }
694            }
695
696            // ⋆e_A = metric(A) * permutation_sign * e_complement
697            let sign = metric_factor * permutation_sign;
698
699            result.coefficients[dual_index] += sign * self.coefficients[i];
700        }
701
702        result
703    }
704}
705
706// Operator implementations
707impl<const P: usize, const Q: usize, const R: usize> Add for Multivector<P, Q, R> {
708    type Output = Self;
709
710    #[inline(always)]
711    fn add(mut self, rhs: Self) -> Self {
712        for i in 0..Self::BASIS_COUNT {
713            self.coefficients[i] += rhs.coefficients[i];
714        }
715        self
716    }
717}
718
719impl<const P: usize, const Q: usize, const R: usize> Add for &Multivector<P, Q, R> {
720    type Output = Multivector<P, Q, R>;
721
722    #[inline(always)]
723    fn add(self, rhs: Self) -> Multivector<P, Q, R> {
724        let mut result = self.clone();
725        for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
726            result.coefficients[i] += rhs.coefficients[i];
727        }
728        result
729    }
730}
731
732impl<const P: usize, const Q: usize, const R: usize> Sub for Multivector<P, Q, R> {
733    type Output = Self;
734
735    #[inline(always)]
736    fn sub(mut self, rhs: Self) -> Self {
737        for i in 0..Self::BASIS_COUNT {
738            self.coefficients[i] -= rhs.coefficients[i];
739        }
740        self
741    }
742}
743
744impl<const P: usize, const Q: usize, const R: usize> Sub for &Multivector<P, Q, R> {
745    type Output = Multivector<P, Q, R>;
746
747    #[inline(always)]
748    fn sub(self, rhs: Self) -> Multivector<P, Q, R> {
749        let mut result = self.clone();
750        for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
751            result.coefficients[i] -= rhs.coefficients[i];
752        }
753        result
754    }
755}
756
757impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for Multivector<P, Q, R> {
758    type Output = Self;
759
760    #[inline(always)]
761    fn mul(mut self, scalar: f64) -> Self {
762        for i in 0..Self::BASIS_COUNT {
763            self.coefficients[i] *= scalar;
764        }
765        self
766    }
767}
768
769impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for &Multivector<P, Q, R> {
770    type Output = Multivector<P, Q, R>;
771
772    #[inline(always)]
773    fn mul(self, scalar: f64) -> Multivector<P, Q, R> {
774        let mut result = self.clone();
775        for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
776            result.coefficients[i] *= scalar;
777        }
778        result
779    }
780}
781
782impl<const P: usize, const Q: usize, const R: usize> Neg for Multivector<P, Q, R> {
783    type Output = Self;
784
785    #[inline(always)]
786    fn neg(mut self) -> Self {
787        for i in 0..Self::BASIS_COUNT {
788            self.coefficients[i] = -self.coefficients[i];
789        }
790        self
791    }
792}
793
794impl<const P: usize, const Q: usize, const R: usize> Zero for Multivector<P, Q, R> {
795    fn zero() -> Self {
796        Self::zero()
797    }
798
799    fn is_zero(&self) -> bool {
800        self.is_zero()
801    }
802}
803
804/// Scalar type - wrapper around Multivector with only grade 0
805#[derive(Debug, Clone, PartialEq)]
806pub struct Scalar<const P: usize, const Q: usize, const R: usize> {
807    pub mv: Multivector<P, Q, R>,
808}
809
810impl<const P: usize, const Q: usize, const R: usize> Scalar<P, Q, R> {
811    pub fn from(value: f64) -> Self {
812        Self {
813            mv: Multivector::scalar(value),
814        }
815    }
816
817    pub fn one() -> Self {
818        Self::from(1.0)
819    }
820
821    pub fn geometric_product(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
822        self.mv.geometric_product(other)
823    }
824
825    pub fn geometric_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
826        self.mv.geometric_product(&other.mv)
827    }
828}
829
830impl<const P: usize, const Q: usize, const R: usize> From<f64> for Scalar<P, Q, R> {
831    fn from(value: f64) -> Self {
832        Self::from(value)
833    }
834}
835
836/// Vector type - wrapper around Multivector with only grade 1
837#[derive(Debug, Clone, PartialEq)]
838pub struct Vector<const P: usize, const Q: usize, const R: usize> {
839    pub mv: Multivector<P, Q, R>,
840}
841
842impl<const P: usize, const Q: usize, const R: usize> Vector<P, Q, R> {
843    /// Create zero vector
844    pub fn zero() -> Self {
845        Self {
846            mv: Multivector::zero(),
847        }
848    }
849
850    pub fn from_components(x: f64, y: f64, z: f64) -> Self {
851        let mut mv = Multivector::zero();
852        if P + Q + R >= 1 {
853            mv.set_vector_component(0, x);
854        }
855        if P + Q + R >= 2 {
856            mv.set_vector_component(1, y);
857        }
858        if P + Q + R >= 3 {
859            mv.set_vector_component(2, z);
860        }
861        Self { mv }
862    }
863
864    pub fn e1() -> Self {
865        Self {
866            mv: Multivector::basis_vector(0),
867        }
868    }
869
870    pub fn e2() -> Self {
871        Self {
872            mv: Multivector::basis_vector(1),
873        }
874    }
875
876    pub fn e3() -> Self {
877        Self {
878            mv: Multivector::basis_vector(2),
879        }
880    }
881
882    pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
883        Self {
884            mv: mv.grade_projection(1),
885        }
886    }
887
888    pub fn geometric_product(&self, other: &Self) -> Multivector<P, Q, R> {
889        self.mv.geometric_product(&other.mv)
890    }
891
892    pub fn geometric_product_with_multivector(
893        &self,
894        other: &Multivector<P, Q, R>,
895    ) -> Multivector<P, Q, R> {
896        self.mv.geometric_product(other)
897    }
898
899    pub fn geometric_product_with_bivector(
900        &self,
901        other: &Bivector<P, Q, R>,
902    ) -> Multivector<P, Q, R> {
903        self.mv.geometric_product(&other.mv)
904    }
905
906    pub fn geometric_product_with_scalar(&self, other: &Scalar<P, Q, R>) -> Multivector<P, Q, R> {
907        self.mv.geometric_product(&other.mv)
908    }
909
910    pub fn add(&self, other: &Self) -> Self {
911        Self {
912            mv: &self.mv + &other.mv,
913        }
914    }
915
916    pub fn magnitude(&self) -> f64 {
917        self.mv.magnitude()
918    }
919
920    pub fn as_slice(&self) -> &[f64] {
921        self.mv.as_slice()
922    }
923
924    /// Inner product with another vector
925    pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
926        self.mv.inner_product(&other.mv)
927    }
928
929    /// Inner product with a multivector
930    pub fn inner_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
931        self.mv.inner_product(other)
932    }
933
934    /// Inner product with a bivector
935    pub fn inner_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
936        self.mv.inner_product(&other.mv)
937    }
938
939    /// Outer product with another vector
940    pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
941        self.mv.outer_product(&other.mv)
942    }
943
944    /// Outer product with a multivector
945    pub fn outer_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
946        self.mv.outer_product(other)
947    }
948
949    /// Outer product with a bivector
950    pub fn outer_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
951        self.mv.outer_product(&other.mv)
952    }
953
954    /// Left contraction with bivector
955    pub fn left_contraction(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
956        // Left contraction: a ⌊ b = grade_projection(a * b, |grade(b) - grade(a)|)
957        let product = self.mv.geometric_product(&other.mv);
958        let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
959        product.grade_projection(target_grade)
960    }
961
962    /// Normalize the vector (return unit vector if possible)
963    pub fn normalize(&self) -> Option<Self> {
964        self.mv
965            .normalize()
966            .map(|normalized| Self { mv: normalized })
967    }
968
969    /// Compute the squared norm of the vector
970    pub fn norm_squared(&self) -> f64 {
971        self.mv.norm_squared()
972    }
973
974    /// Compute the reverse (for vectors, this is the same as the original)
975    pub fn reverse(&self) -> Self {
976        Self {
977            mv: self.mv.reverse(),
978        }
979    }
980
981    /// Compute the norm (magnitude) of the vector
982    ///
983    /// **Note**: This method is maintained for backward compatibility.
984    /// New code should prefer [`magnitude()`](Self::magnitude) for clarity.
985    pub fn norm(&self) -> f64 {
986        self.magnitude()
987    }
988
989    /// Hodge dual of the vector
990    /// Maps vectors to bivectors in 3D space
991    pub fn hodge_dual(&self) -> Bivector<P, Q, R> {
992        Bivector {
993            mv: self.mv.hodge_dual(),
994        }
995    }
996}
997
998/// Bivector type - wrapper around Multivector with only grade 2
999#[derive(Debug, Clone, PartialEq)]
1000pub struct Bivector<const P: usize, const Q: usize, const R: usize> {
1001    pub mv: Multivector<P, Q, R>,
1002}
1003
1004impl<const P: usize, const Q: usize, const R: usize> Bivector<P, Q, R> {
1005    pub fn from_components(xy: f64, xz: f64, yz: f64) -> Self {
1006        let mut mv = Multivector::zero();
1007        if P + Q + R >= 2 {
1008            mv.set_bivector_component(0, xy);
1009        } // e12
1010        if P + Q + R >= 3 {
1011            mv.set_bivector_component(1, xz);
1012        } // e13
1013        if P + Q + R >= 3 {
1014            mv.set_bivector_component(2, yz);
1015        } // e23
1016        Self { mv }
1017    }
1018
1019    pub fn e12() -> Self {
1020        let mut mv = Multivector::zero();
1021        mv.set_bivector_component(0, 1.0); // e12
1022        Self { mv }
1023    }
1024
1025    pub fn e13() -> Self {
1026        let mut mv = Multivector::zero();
1027        mv.set_bivector_component(1, 1.0); // e13
1028        Self { mv }
1029    }
1030
1031    pub fn e23() -> Self {
1032        let mut mv = Multivector::zero();
1033        mv.set_bivector_component(2, 1.0); // e23
1034        Self { mv }
1035    }
1036
1037    pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
1038        Self {
1039            mv: mv.grade_projection(2),
1040        }
1041    }
1042
1043    pub fn geometric_product(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1044        self.mv.geometric_product(&other.mv)
1045    }
1046
1047    /// Geometric product with another bivector
1048    pub fn geometric_product_with_bivector(&self, other: &Self) -> Multivector<P, Q, R> {
1049        self.mv.geometric_product(&other.mv)
1050    }
1051
1052    pub fn magnitude(&self) -> f64 {
1053        self.mv.magnitude()
1054    }
1055
1056    /// Index access for bivector components
1057    pub fn get(&self, index: usize) -> f64 {
1058        match index {
1059            0 => self.mv.get(3), // e12
1060            1 => self.mv.get(5), // e13
1061            2 => self.mv.get(6), // e23
1062            _ => 0.0,
1063        }
1064    }
1065
1066    /// Inner product with another bivector
1067    pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
1068        self.mv.inner_product(&other.mv)
1069    }
1070
1071    /// Inner product with a vector
1072    pub fn inner_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1073        self.mv.inner_product(&other.mv)
1074    }
1075
1076    /// Outer product with another bivector
1077    pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
1078        self.mv.outer_product(&other.mv)
1079    }
1080
1081    /// Outer product with a vector
1082    pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1083        self.mv.outer_product(&other.mv)
1084    }
1085
1086    /// Right contraction with vector
1087    pub fn right_contraction(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1088        // Right contraction: a ⌋ b = grade_projection(a * b, |grade(a) - grade(b)|)
1089        let product = self.mv.geometric_product(&other.mv);
1090        let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
1091        product.grade_projection(target_grade)
1092    }
1093}
1094
1095impl<const P: usize, const Q: usize, const R: usize> core::ops::Index<usize> for Bivector<P, Q, R> {
1096    type Output = f64;
1097
1098    fn index(&self, index: usize) -> &Self::Output {
1099        match index {
1100            0 => &self.mv.coefficients[3], // e12
1101            1 => &self.mv.coefficients[5], // e13
1102            2 => &self.mv.coefficients[6], // e23
1103            _ => panic!("Bivector index out of range: {}", index),
1104        }
1105    }
1106}
1107
1108/// Convenience type alias for basis vector E
1109pub type E<const P: usize, const Q: usize, const R: usize> = Vector<P, Q, R>;
1110
1111// Re-export the Rotor type from the rotor module
1112pub use rotor::Rotor;
1113
1114#[cfg(test)]
1115mod tests {
1116    use super::*;
1117    use approx::assert_relative_eq;
1118
1119    type Cl3 = Multivector<3, 0, 0>; // 3D Euclidean space
1120
1121    #[test]
1122    fn test_basis_vectors() {
1123        let e1 = Cl3::basis_vector(0);
1124        let e2 = Cl3::basis_vector(1);
1125
1126        // e1 * e1 = 1
1127        let e1_squared = e1.geometric_product(&e1);
1128        assert_eq!(e1_squared.scalar_part(), 1.0);
1129
1130        // e1 * e2 = -e2 * e1 (anticommute)
1131        let e12 = e1.geometric_product(&e2);
1132        let e21 = e2.geometric_product(&e1);
1133        assert_eq!(e12.coefficients[3], -e21.coefficients[3]); // e1∧e2 component
1134    }
1135
1136    #[test]
1137    fn test_wedge_product() {
1138        let e1 = Cl3::basis_vector(0);
1139        let e2 = Cl3::basis_vector(1);
1140
1141        let e12 = e1.outer_product(&e2);
1142        assert!(e12.get(3).abs() - 1.0 < 1e-10); // e1∧e2 has index 0b11 = 3
1143
1144        // e1 ∧ e1 = 0
1145        let e11 = e1.outer_product(&e1);
1146        assert!(e11.is_zero());
1147    }
1148
1149    #[test]
1150    fn test_rotor_from_bivector() {
1151        let e1 = Cl3::basis_vector(0);
1152        let e2 = Cl3::basis_vector(1);
1153        let e12 = e1.outer_product(&e2);
1154
1155        // Create 90 degree rotation in e1-e2 plane
1156        let angle = core::f64::consts::PI / 4.0; // Half angle for rotor
1157        let bivector = e12 * angle;
1158        let rotor = bivector.exp();
1159
1160        // Check that rotor has unit norm
1161        assert!((rotor.norm() - 1.0).abs() < 1e-10);
1162    }
1163
1164    #[test]
1165    fn test_algebraic_identities() {
1166        let e1 = Cl3::basis_vector(0);
1167        let e2 = Cl3::basis_vector(1);
1168        let e3 = Cl3::basis_vector(2);
1169
1170        // Associativity: (ab)c = a(bc)
1171        let a = e1.clone() + e2.clone() * 2.0;
1172        let b = e2.clone() + e3.clone() * 3.0;
1173        let c = e3.clone() + e1.clone() * 0.5;
1174
1175        let left = a.geometric_product(&b).geometric_product(&c);
1176        let right = a.geometric_product(&b.geometric_product(&c));
1177        assert_relative_eq!(left.norm(), right.norm(), epsilon = 1e-12);
1178
1179        // Distributivity: a(b + c) = ab + ac
1180        let ab_plus_ac = a.geometric_product(&b) + a.geometric_product(&c);
1181        let a_times_b_plus_c = a.geometric_product(&(b.clone() + c.clone()));
1182        assert!((ab_plus_ac - a_times_b_plus_c).norm() < 1e-12);
1183
1184        // Reverse property: (ab)† = b†a†
1185        let ab_reverse = a.geometric_product(&b).reverse();
1186        let b_reverse_a_reverse = b.reverse().geometric_product(&a.reverse());
1187        assert!((ab_reverse - b_reverse_a_reverse).norm() < 1e-12);
1188    }
1189
1190    #[test]
1191    fn test_metric_signature() {
1192        // Test different signatures
1193        type Spacetime = Multivector<1, 3, 0>; // Minkowski signature
1194
1195        let e0 = Spacetime::basis_vector(0); // timelike
1196        let e1 = Spacetime::basis_vector(1); // spacelike
1197
1198        // e0^2 = +1, e1^2 = -1
1199        assert_eq!(e0.geometric_product(&e0).scalar_part(), 1.0);
1200        assert_eq!(e1.geometric_product(&e1).scalar_part(), -1.0);
1201    }
1202
1203    #[test]
1204    fn test_grade_operations() {
1205        let e1 = Cl3::basis_vector(0);
1206        let e2 = Cl3::basis_vector(1);
1207        let scalar = Cl3::scalar(2.0);
1208
1209        let mv = scalar + e1.clone() * 3.0 + e2.clone() * 4.0 + e1.outer_product(&e2) * 5.0;
1210
1211        // Test grade projections
1212        let grade0 = mv.grade_projection(0);
1213        let grade1 = mv.grade_projection(1);
1214        let grade2 = mv.grade_projection(2);
1215
1216        assert_eq!(grade0.scalar_part(), 2.0);
1217        assert_eq!(grade1.get(1), 3.0); // e1 component
1218        assert_eq!(grade1.get(2), 4.0); // e2 component
1219        assert_eq!(grade2.get(3), 5.0); // e12 component
1220
1221        // Sum of grade projections should equal original
1222        let reconstructed = grade0 + grade1 + grade2;
1223        assert!((mv - reconstructed).norm() < 1e-12);
1224    }
1225
1226    #[test]
1227    fn test_inner_and_outer_products() {
1228        let e1 = Cl3::basis_vector(0);
1229        let e2 = Cl3::basis_vector(1);
1230        let e3 = Cl3::basis_vector(2);
1231
1232        // Inner product of orthogonal vectors is zero
1233        assert!(e1.inner_product(&e2).norm() < 1e-12);
1234
1235        // Inner product of parallel vectors
1236        let v1 = e1.clone() + e2.clone();
1237        let v2 = e1.clone() * 2.0 + e2.clone() * 2.0;
1238        let inner = v1.inner_product(&v2);
1239        assert_relative_eq!(inner.scalar_part(), 4.0, epsilon = 1e-12);
1240
1241        // Outer product creates higher grade
1242        let bivector = e1.outer_product(&e2);
1243        let trivector = bivector.outer_product(&e3);
1244        assert_eq!(trivector.get(7), 1.0); // e123 component
1245    }
1246}