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