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