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