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