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