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