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    /// Decompose into grade components
414    fn grade_decomposition(&self) -> Vec<Self> {
415        let mut grades = Vec::with_capacity(Self::DIM + 1);
416        for _ in 0..=Self::DIM {
417            grades.push(Self::zero());
418        }
419
420        for i in 0..Self::BASIS_COUNT {
421            let grade = i.count_ones() as usize;
422            grades[grade].coefficients[i] = self.coefficients[i];
423        }
424
425        grades
426    }
427
428    /// Check if this is (approximately) zero
429    pub fn is_zero(&self) -> bool {
430        self.coefficients.iter().all(|&c| c.abs() < 1e-14)
431    }
432
433    /// Compute the norm squared (scalar product with reverse)
434    pub fn norm_squared(&self) -> f64 {
435        self.scalar_product(&self.reverse())
436    }
437
438    /// Compute the magnitude (length) of this multivector
439    ///
440    /// The magnitude is defined as |a| = √(a·ã) where ã is the reverse of a.
441    /// This provides the natural norm inherited from the underlying vector space.
442    ///
443    /// # Mathematical Properties
444    /// - Always non-negative: |a| ≥ 0
445    /// - Zero iff a = 0: |a| = 0 ⟺ a = 0
446    /// - Sub-multiplicative: |ab| ≤ |a||b|
447    ///
448    /// # Examples
449    /// ```rust
450    /// use amari_core::Multivector;
451    /// let v = Multivector::<3,0,0>::basis_vector(0);
452    /// assert_eq!(v.magnitude(), 1.0);
453    /// ```
454    pub fn magnitude(&self) -> f64 {
455        self.norm_squared().abs().sqrt()
456    }
457
458    /// Compute the norm (magnitude) of this multivector
459    ///
460    /// **Note**: This method is maintained for backward compatibility.
461    /// New code should prefer [`magnitude()`](Self::magnitude) for clarity.
462    pub fn norm(&self) -> f64 {
463        self.magnitude()
464    }
465
466    /// Absolute value (same as magnitude/norm for multivectors)
467    pub fn abs(&self) -> f64 {
468        self.magnitude()
469    }
470
471    /// Approximate equality comparison
472    pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool {
473        (self.clone() - other.clone()).magnitude() < epsilon
474    }
475
476    /// Normalize this multivector
477    pub fn normalize(&self) -> Option<Self> {
478        let norm = self.norm();
479        if norm > 1e-14 {
480            Some(self * (1.0 / norm))
481        } else {
482            None
483        }
484    }
485
486    /// Compute multiplicative inverse if it exists
487    pub fn inverse(&self) -> Option<Self> {
488        let rev = self.reverse();
489        let norm_sq = self.scalar_product(&rev);
490
491        if norm_sq.abs() > 1e-14 {
492            Some(rev * (1.0 / norm_sq))
493        } else {
494            None
495        }
496    }
497
498    /// Exponential map for bivectors (creates rotors)
499    ///
500    /// For a bivector B, exp(B) creates a rotor that performs rotation
501    /// in the plane defined by B.
502    pub fn exp(&self) -> Self {
503        // Check if this is a bivector (grade 2)
504        let grade2 = self.grade_projection(2);
505        if (self - &grade2).norm() > 1e-10 {
506            // For general multivectors, use series expansion
507            return self.exp_series();
508        }
509
510        // For pure bivectors, use closed form
511        let b_squared = grade2.geometric_product(&grade2).scalar_part();
512
513        if b_squared > -1e-14 {
514            // Hyperbolic case: exp(B) = cosh(|B|) + sinh(|B|) * B/|B|
515            let norm = b_squared.abs().sqrt();
516            if norm < 1e-14 {
517                return Self::scalar(1.0);
518            }
519            let cosh_norm = norm.cosh();
520            let sinh_norm = norm.sinh();
521            Self::scalar(cosh_norm) + grade2 * (sinh_norm / norm)
522        } else {
523            // Circular case: exp(B) = cos(|B|) + sin(|B|) * B/|B|
524            let norm = (-b_squared).sqrt();
525            let cos_norm = norm.cos();
526            let sin_norm = norm.sin();
527            Self::scalar(cos_norm) + grade2 * (sin_norm / norm)
528        }
529    }
530
531    /// Series expansion for exponential (fallback for general multivectors)
532    fn exp_series(&self) -> Self {
533        let mut result = Self::scalar(1.0);
534        let mut term = Self::scalar(1.0);
535
536        for n in 1..20 {
537            term = term.geometric_product(self) * (1.0 / n as f64);
538            let old_result = result.clone();
539            result = result + term.clone();
540
541            // Check convergence
542            if (result.clone() - old_result).norm() < 1e-14 {
543                break;
544            }
545        }
546
547        result
548    }
549
550    /// Left contraction: a ⌟ b
551    ///
552    /// Generalized inner product where the grade of the result
553    /// is |grade(b) - grade(a)|. The left operand's grade is reduced.
554    pub fn left_contraction(&self, other: &Self) -> Self {
555        let self_grades = self.grade_decomposition();
556        let other_grades = other.grade_decomposition();
557        let mut result = Self::zero();
558
559        for (a_grade, mv_a) in self_grades.iter().enumerate() {
560            if mv_a.is_zero() {
561                continue;
562            }
563
564            for (b_grade, mv_b) in other_grades.iter().enumerate() {
565                if mv_b.is_zero() {
566                    continue;
567                }
568
569                // Left contraction: grade of result is |b_grade - a_grade|
570                if b_grade >= a_grade {
571                    let target_grade = b_grade - a_grade;
572                    let product = mv_a.geometric_product(mv_b);
573                    let projected = product.grade_projection(target_grade);
574                    result = result + projected;
575                }
576            }
577        }
578
579        result
580    }
581
582    /// Right contraction: a ⌞ b  
583    ///
584    /// Generalized inner product where the grade of the result
585    /// is |grade(a) - grade(b)|. The right operand's grade is reduced.
586    pub fn right_contraction(&self, other: &Self) -> Self {
587        let self_grades = self.grade_decomposition();
588        let other_grades = other.grade_decomposition();
589        let mut result = Self::zero();
590
591        for (a_grade, mv_a) in self_grades.iter().enumerate() {
592            if mv_a.is_zero() {
593                continue;
594            }
595
596            for (b_grade, mv_b) in other_grades.iter().enumerate() {
597                if mv_b.is_zero() {
598                    continue;
599                }
600
601                // Right contraction: grade of result is |a_grade - b_grade|
602                if a_grade >= b_grade {
603                    let target_grade = a_grade - b_grade;
604                    let product = mv_a.geometric_product(mv_b);
605                    let projected = product.grade_projection(target_grade);
606                    result = result + projected;
607                }
608            }
609        }
610
611        result
612    }
613
614    /// Hodge dual: ⋆a
615    ///
616    /// Maps k-vectors to (n-k)-vectors in n-dimensional space.
617    /// Essential for electromagnetic field theory and differential forms.
618    /// In 3D: scalar -> pseudoscalar, vector -> bivector, bivector -> vector, pseudoscalar -> scalar
619    pub fn hodge_dual(&self) -> Self {
620        let n = Self::DIM;
621        if n == 0 {
622            return self.clone();
623        }
624
625        let mut result = Self::zero();
626
627        // Create the pseudoscalar (highest grade basis element)
628        let pseudoscalar_index = (1 << n) - 1; // All bits set
629
630        for i in 0..Self::BASIS_COUNT {
631            if self.coefficients[i].abs() < 1e-14 {
632                continue;
633            }
634
635            // The Hodge dual of basis element e_i is obtained by
636            // complementing the basis indices and applying the pseudoscalar
637            let dual_index = i ^ pseudoscalar_index;
638
639            // Calculate the sign based on the number of swaps needed
640            let _grade_i = i.count_ones() as usize;
641            let _grade_dual = dual_index.count_ones() as usize;
642
643            // Sign depends on the permutation parity
644            let mut sign = 1.0;
645
646            // Count the number of index swaps needed (simplified calculation)
647            let temp_i = i;
648            let temp_dual = dual_index;
649            let mut swaps = 0;
650
651            for bit_pos in 0..n {
652                let bit_mask = 1 << bit_pos;
653                if (temp_i & bit_mask) != 0 && (temp_dual & bit_mask) == 0 {
654                    // Count bits to the right in dual that are set
655                    let right_bits = temp_dual & ((1 << bit_pos) - 1);
656                    swaps += right_bits.count_ones();
657                }
658            }
659
660            if swaps % 2 == 1 {
661                sign = -1.0;
662            }
663
664            // Apply metric signature for negative basis vectors
665            for j in 0..n {
666                let bit_mask = 1 << j;
667                if (dual_index & bit_mask) != 0 {
668                    if j >= P + Q {
669                        // R signature (negative)
670                        sign *= -1.0;
671                    } else if j >= P {
672                        // Q signature (negative)
673                        sign *= -1.0;
674                    }
675                    // P signature remains positive
676                }
677            }
678
679            result.coefficients[dual_index] += sign * self.coefficients[i];
680        }
681
682        result
683    }
684}
685
686// Operator implementations
687impl<const P: usize, const Q: usize, const R: usize> Add for Multivector<P, Q, R> {
688    type Output = Self;
689
690    #[inline(always)]
691    fn add(mut self, rhs: Self) -> Self {
692        for i in 0..Self::BASIS_COUNT {
693            self.coefficients[i] += rhs.coefficients[i];
694        }
695        self
696    }
697}
698
699impl<const P: usize, const Q: usize, const R: usize> Add for &Multivector<P, Q, R> {
700    type Output = Multivector<P, Q, R>;
701
702    #[inline(always)]
703    fn add(self, rhs: Self) -> 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] += rhs.coefficients[i];
707        }
708        result
709    }
710}
711
712impl<const P: usize, const Q: usize, const R: usize> Sub for Multivector<P, Q, R> {
713    type Output = Self;
714
715    #[inline(always)]
716    fn sub(mut self, rhs: Self) -> Self {
717        for i in 0..Self::BASIS_COUNT {
718            self.coefficients[i] -= rhs.coefficients[i];
719        }
720        self
721    }
722}
723
724impl<const P: usize, const Q: usize, const R: usize> Sub for &Multivector<P, Q, R> {
725    type Output = Multivector<P, Q, R>;
726
727    #[inline(always)]
728    fn sub(self, rhs: Self) -> Multivector<P, Q, R> {
729        let mut result = self.clone();
730        for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
731            result.coefficients[i] -= rhs.coefficients[i];
732        }
733        result
734    }
735}
736
737impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for Multivector<P, Q, R> {
738    type Output = Self;
739
740    #[inline(always)]
741    fn mul(mut self, scalar: f64) -> Self {
742        for i in 0..Self::BASIS_COUNT {
743            self.coefficients[i] *= scalar;
744        }
745        self
746    }
747}
748
749impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for &Multivector<P, Q, R> {
750    type Output = Multivector<P, Q, R>;
751
752    #[inline(always)]
753    fn mul(self, scalar: f64) -> Multivector<P, Q, R> {
754        let mut result = self.clone();
755        for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
756            result.coefficients[i] *= scalar;
757        }
758        result
759    }
760}
761
762impl<const P: usize, const Q: usize, const R: usize> Neg for Multivector<P, Q, R> {
763    type Output = Self;
764
765    #[inline(always)]
766    fn neg(mut self) -> Self {
767        for i in 0..Self::BASIS_COUNT {
768            self.coefficients[i] = -self.coefficients[i];
769        }
770        self
771    }
772}
773
774impl<const P: usize, const Q: usize, const R: usize> Zero for Multivector<P, Q, R> {
775    fn zero() -> Self {
776        Self::zero()
777    }
778
779    fn is_zero(&self) -> bool {
780        self.is_zero()
781    }
782}
783
784/// Scalar type - wrapper around Multivector with only grade 0
785#[derive(Debug, Clone, PartialEq)]
786pub struct Scalar<const P: usize, const Q: usize, const R: usize> {
787    pub mv: Multivector<P, Q, R>,
788}
789
790impl<const P: usize, const Q: usize, const R: usize> Scalar<P, Q, R> {
791    pub fn from(value: f64) -> Self {
792        Self {
793            mv: Multivector::scalar(value),
794        }
795    }
796
797    pub fn one() -> Self {
798        Self::from(1.0)
799    }
800
801    pub fn geometric_product(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
802        self.mv.geometric_product(other)
803    }
804
805    pub fn geometric_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
806        self.mv.geometric_product(&other.mv)
807    }
808}
809
810impl<const P: usize, const Q: usize, const R: usize> From<f64> for Scalar<P, Q, R> {
811    fn from(value: f64) -> Self {
812        Self::from(value)
813    }
814}
815
816/// Vector type - wrapper around Multivector with only grade 1
817#[derive(Debug, Clone, PartialEq)]
818pub struct Vector<const P: usize, const Q: usize, const R: usize> {
819    pub mv: Multivector<P, Q, R>,
820}
821
822impl<const P: usize, const Q: usize, const R: usize> Vector<P, Q, R> {
823    /// Create zero vector
824    pub fn zero() -> Self {
825        Self {
826            mv: Multivector::zero(),
827        }
828    }
829
830    pub fn from_components(x: f64, y: f64, z: f64) -> Self {
831        let mut mv = Multivector::zero();
832        if P + Q + R >= 1 {
833            mv.set_vector_component(0, x);
834        }
835        if P + Q + R >= 2 {
836            mv.set_vector_component(1, y);
837        }
838        if P + Q + R >= 3 {
839            mv.set_vector_component(2, z);
840        }
841        Self { mv }
842    }
843
844    pub fn e1() -> Self {
845        Self {
846            mv: Multivector::basis_vector(0),
847        }
848    }
849
850    pub fn e2() -> Self {
851        Self {
852            mv: Multivector::basis_vector(1),
853        }
854    }
855
856    pub fn e3() -> Self {
857        Self {
858            mv: Multivector::basis_vector(2),
859        }
860    }
861
862    pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
863        Self {
864            mv: mv.grade_projection(1),
865        }
866    }
867
868    pub fn geometric_product(&self, other: &Self) -> Multivector<P, Q, R> {
869        self.mv.geometric_product(&other.mv)
870    }
871
872    pub fn geometric_product_with_multivector(
873        &self,
874        other: &Multivector<P, Q, R>,
875    ) -> Multivector<P, Q, R> {
876        self.mv.geometric_product(other)
877    }
878
879    pub fn geometric_product_with_bivector(
880        &self,
881        other: &Bivector<P, Q, R>,
882    ) -> Multivector<P, Q, R> {
883        self.mv.geometric_product(&other.mv)
884    }
885
886    pub fn geometric_product_with_scalar(&self, other: &Scalar<P, Q, R>) -> Multivector<P, Q, R> {
887        self.mv.geometric_product(&other.mv)
888    }
889
890    pub fn add(&self, other: &Self) -> Self {
891        Self {
892            mv: &self.mv + &other.mv,
893        }
894    }
895
896    pub fn magnitude(&self) -> f64 {
897        self.mv.magnitude()
898    }
899
900    pub fn as_slice(&self) -> &[f64] {
901        self.mv.as_slice()
902    }
903
904    /// Inner product with another vector
905    pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
906        self.mv.inner_product(&other.mv)
907    }
908
909    /// Inner product with a multivector
910    pub fn inner_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
911        self.mv.inner_product(other)
912    }
913
914    /// Inner product with a bivector
915    pub fn inner_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
916        self.mv.inner_product(&other.mv)
917    }
918
919    /// Outer product with another vector
920    pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
921        self.mv.outer_product(&other.mv)
922    }
923
924    /// Outer product with a multivector
925    pub fn outer_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
926        self.mv.outer_product(other)
927    }
928
929    /// Outer product with a bivector
930    pub fn outer_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
931        self.mv.outer_product(&other.mv)
932    }
933
934    /// Left contraction with bivector
935    pub fn left_contraction(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
936        // Left contraction: a ⌊ b = grade_projection(a * b, |grade(b) - grade(a)|)
937        let product = self.mv.geometric_product(&other.mv);
938        let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
939        product.grade_projection(target_grade)
940    }
941
942    /// Normalize the vector (return unit vector if possible)
943    pub fn normalize(&self) -> Option<Self> {
944        self.mv
945            .normalize()
946            .map(|normalized| Self { mv: normalized })
947    }
948
949    /// Compute the squared norm of the vector
950    pub fn norm_squared(&self) -> f64 {
951        self.mv.norm_squared()
952    }
953
954    /// Compute the reverse (for vectors, this is the same as the original)
955    pub fn reverse(&self) -> Self {
956        Self {
957            mv: self.mv.reverse(),
958        }
959    }
960
961    /// Compute the norm (magnitude) of the vector
962    ///
963    /// **Note**: This method is maintained for backward compatibility.
964    /// New code should prefer [`magnitude()`](Self::magnitude) for clarity.
965    pub fn norm(&self) -> f64 {
966        self.magnitude()
967    }
968
969    /// Hodge dual of the vector
970    /// Maps vectors to bivectors in 3D space
971    pub fn hodge_dual(&self) -> Bivector<P, Q, R> {
972        Bivector {
973            mv: self.mv.hodge_dual(),
974        }
975    }
976}
977
978/// Bivector type - wrapper around Multivector with only grade 2
979#[derive(Debug, Clone, PartialEq)]
980pub struct Bivector<const P: usize, const Q: usize, const R: usize> {
981    pub mv: Multivector<P, Q, R>,
982}
983
984impl<const P: usize, const Q: usize, const R: usize> Bivector<P, Q, R> {
985    pub fn from_components(xy: f64, xz: f64, yz: f64) -> Self {
986        let mut mv = Multivector::zero();
987        if P + Q + R >= 2 {
988            mv.set_bivector_component(0, xy);
989        } // e12
990        if P + Q + R >= 3 {
991            mv.set_bivector_component(1, xz);
992        } // e13
993        if P + Q + R >= 3 {
994            mv.set_bivector_component(2, yz);
995        } // e23
996        Self { mv }
997    }
998
999    pub fn e12() -> Self {
1000        let mut mv = Multivector::zero();
1001        mv.set_bivector_component(0, 1.0); // e12
1002        Self { mv }
1003    }
1004
1005    pub fn e13() -> Self {
1006        let mut mv = Multivector::zero();
1007        mv.set_bivector_component(1, 1.0); // e13
1008        Self { mv }
1009    }
1010
1011    pub fn e23() -> Self {
1012        let mut mv = Multivector::zero();
1013        mv.set_bivector_component(2, 1.0); // e23
1014        Self { mv }
1015    }
1016
1017    pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
1018        Self {
1019            mv: mv.grade_projection(2),
1020        }
1021    }
1022
1023    pub fn geometric_product(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1024        self.mv.geometric_product(&other.mv)
1025    }
1026
1027    /// Geometric product with another bivector
1028    pub fn geometric_product_with_bivector(&self, other: &Self) -> Multivector<P, Q, R> {
1029        self.mv.geometric_product(&other.mv)
1030    }
1031
1032    pub fn magnitude(&self) -> f64 {
1033        self.mv.magnitude()
1034    }
1035
1036    /// Index access for bivector components
1037    pub fn get(&self, index: usize) -> f64 {
1038        match index {
1039            0 => self.mv.get(3), // e12
1040            1 => self.mv.get(5), // e13
1041            2 => self.mv.get(6), // e23
1042            _ => 0.0,
1043        }
1044    }
1045
1046    /// Inner product with another bivector
1047    pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
1048        self.mv.inner_product(&other.mv)
1049    }
1050
1051    /// Inner product with a vector
1052    pub fn inner_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1053        self.mv.inner_product(&other.mv)
1054    }
1055
1056    /// Outer product with another bivector
1057    pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
1058        self.mv.outer_product(&other.mv)
1059    }
1060
1061    /// Outer product with a vector
1062    pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1063        self.mv.outer_product(&other.mv)
1064    }
1065
1066    /// Right contraction with vector
1067    pub fn right_contraction(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1068        // Right contraction: a ⌋ b = grade_projection(a * b, |grade(a) - grade(b)|)
1069        let product = self.mv.geometric_product(&other.mv);
1070        let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
1071        product.grade_projection(target_grade)
1072    }
1073}
1074
1075impl<const P: usize, const Q: usize, const R: usize> core::ops::Index<usize> for Bivector<P, Q, R> {
1076    type Output = f64;
1077
1078    fn index(&self, index: usize) -> &Self::Output {
1079        match index {
1080            0 => &self.mv.coefficients[3], // e12
1081            1 => &self.mv.coefficients[5], // e13
1082            2 => &self.mv.coefficients[6], // e23
1083            _ => panic!("Bivector index out of range: {}", index),
1084        }
1085    }
1086}
1087
1088/// Convenience type alias for basis vector E
1089pub type E<const P: usize, const Q: usize, const R: usize> = Vector<P, Q, R>;
1090
1091// Re-export the Rotor type from the rotor module
1092pub use rotor::Rotor;
1093
1094#[cfg(test)]
1095mod tests {
1096    use super::*;
1097    use approx::assert_relative_eq;
1098
1099    type Cl3 = Multivector<3, 0, 0>; // 3D Euclidean space
1100
1101    #[test]
1102    fn test_basis_vectors() {
1103        let e1 = Cl3::basis_vector(0);
1104        let e2 = Cl3::basis_vector(1);
1105
1106        // e1 * e1 = 1
1107        let e1_squared = e1.geometric_product(&e1);
1108        assert_eq!(e1_squared.scalar_part(), 1.0);
1109
1110        // e1 * e2 = -e2 * e1 (anticommute)
1111        let e12 = e1.geometric_product(&e2);
1112        let e21 = e2.geometric_product(&e1);
1113        assert_eq!(e12.coefficients[3], -e21.coefficients[3]); // e1∧e2 component
1114    }
1115
1116    #[test]
1117    fn test_wedge_product() {
1118        let e1 = Cl3::basis_vector(0);
1119        let e2 = Cl3::basis_vector(1);
1120
1121        let e12 = e1.outer_product(&e2);
1122        assert!(e12.get(3).abs() - 1.0 < 1e-10); // e1∧e2 has index 0b11 = 3
1123
1124        // e1 ∧ e1 = 0
1125        let e11 = e1.outer_product(&e1);
1126        assert!(e11.is_zero());
1127    }
1128
1129    #[test]
1130    fn test_rotor_from_bivector() {
1131        let e1 = Cl3::basis_vector(0);
1132        let e2 = Cl3::basis_vector(1);
1133        let e12 = e1.outer_product(&e2);
1134
1135        // Create 90 degree rotation in e1-e2 plane
1136        let angle = core::f64::consts::PI / 4.0; // Half angle for rotor
1137        let bivector = e12 * angle;
1138        let rotor = bivector.exp();
1139
1140        // Check that rotor has unit norm
1141        assert!((rotor.norm() - 1.0).abs() < 1e-10);
1142    }
1143
1144    #[test]
1145    fn test_algebraic_identities() {
1146        let e1 = Cl3::basis_vector(0);
1147        let e2 = Cl3::basis_vector(1);
1148        let e3 = Cl3::basis_vector(2);
1149
1150        // Associativity: (ab)c = a(bc)
1151        let a = e1.clone() + e2.clone() * 2.0;
1152        let b = e2.clone() + e3.clone() * 3.0;
1153        let c = e3.clone() + e1.clone() * 0.5;
1154
1155        let left = a.geometric_product(&b).geometric_product(&c);
1156        let right = a.geometric_product(&b.geometric_product(&c));
1157        assert_relative_eq!(left.norm(), right.norm(), epsilon = 1e-12);
1158
1159        // Distributivity: a(b + c) = ab + ac
1160        let ab_plus_ac = a.geometric_product(&b) + a.geometric_product(&c);
1161        let a_times_b_plus_c = a.geometric_product(&(b.clone() + c.clone()));
1162        assert!((ab_plus_ac - a_times_b_plus_c).norm() < 1e-12);
1163
1164        // Reverse property: (ab)† = b†a†
1165        let ab_reverse = a.geometric_product(&b).reverse();
1166        let b_reverse_a_reverse = b.reverse().geometric_product(&a.reverse());
1167        assert!((ab_reverse - b_reverse_a_reverse).norm() < 1e-12);
1168    }
1169
1170    #[test]
1171    fn test_metric_signature() {
1172        // Test different signatures
1173        type Spacetime = Multivector<1, 3, 0>; // Minkowski signature
1174
1175        let e0 = Spacetime::basis_vector(0); // timelike
1176        let e1 = Spacetime::basis_vector(1); // spacelike
1177
1178        // e0^2 = +1, e1^2 = -1
1179        assert_eq!(e0.geometric_product(&e0).scalar_part(), 1.0);
1180        assert_eq!(e1.geometric_product(&e1).scalar_part(), -1.0);
1181    }
1182
1183    #[test]
1184    fn test_grade_operations() {
1185        let e1 = Cl3::basis_vector(0);
1186        let e2 = Cl3::basis_vector(1);
1187        let scalar = Cl3::scalar(2.0);
1188
1189        let mv = scalar + e1.clone() * 3.0 + e2.clone() * 4.0 + e1.outer_product(&e2) * 5.0;
1190
1191        // Test grade projections
1192        let grade0 = mv.grade_projection(0);
1193        let grade1 = mv.grade_projection(1);
1194        let grade2 = mv.grade_projection(2);
1195
1196        assert_eq!(grade0.scalar_part(), 2.0);
1197        assert_eq!(grade1.get(1), 3.0); // e1 component
1198        assert_eq!(grade1.get(2), 4.0); // e2 component
1199        assert_eq!(grade2.get(3), 5.0); // e12 component
1200
1201        // Sum of grade projections should equal original
1202        let reconstructed = grade0 + grade1 + grade2;
1203        assert!((mv - reconstructed).norm() < 1e-12);
1204    }
1205
1206    #[test]
1207    fn test_inner_and_outer_products() {
1208        let e1 = Cl3::basis_vector(0);
1209        let e2 = Cl3::basis_vector(1);
1210        let e3 = Cl3::basis_vector(2);
1211
1212        // Inner product of orthogonal vectors is zero
1213        assert!(e1.inner_product(&e2).norm() < 1e-12);
1214
1215        // Inner product of parallel vectors
1216        let v1 = e1.clone() + e2.clone();
1217        let v2 = e1.clone() * 2.0 + e2.clone() * 2.0;
1218        let inner = v1.inner_product(&v2);
1219        assert_relative_eq!(inner.scalar_part(), 4.0, epsilon = 1e-12);
1220
1221        // Outer product creates higher grade
1222        let bivector = e1.outer_product(&e2);
1223        let trivector = bivector.outer_product(&e3);
1224        assert_eq!(trivector.get(7), 1.0); // e123 component
1225    }
1226}