1#![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
35pub use error::{CoreError, CoreResult};
37
38pub 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#[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#[derive(Debug, Clone, PartialEq)]
77#[repr(C, align(32))] pub struct Multivector<const P: usize, const Q: usize, const R: usize> {
79 coefficients: Box<[f64]>,
82}
83
84impl<const P: usize, const Q: usize, const R: usize> Multivector<P, Q, R> {
85 pub const DIM: usize = P + Q + R;
87
88 pub const BASIS_COUNT: usize = 1 << Self::DIM;
90
91 #[inline(always)]
93 pub fn zero() -> Self {
94 Self {
95 coefficients: vec![0.0; Self::BASIS_COUNT].into_boxed_slice(),
96 }
97 }
98
99 #[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 pub fn from_vector(vector: &Vector<P, Q, R>) -> Self {
109 vector.mv.clone()
110 }
111
112 pub fn from_bivector(bivector: &Bivector<P, Q, R>) -> Self {
114 bivector.mv.clone()
115 }
116
117 #[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 #[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 #[inline(always)]
142 pub fn from_slice(coefficients: &[f64]) -> Self {
143 Self::from_coefficients(coefficients.to_vec())
144 }
145
146 #[inline(always)]
148 pub fn get(&self, index: usize) -> f64 {
149 self.coefficients.get(index).copied().unwrap_or(0.0)
150 }
151
152 #[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 #[inline(always)]
162 pub fn scalar_part(&self) -> f64 {
163 self.coefficients[0]
164 }
165
166 #[inline(always)]
168 pub fn set_scalar(&mut self, value: f64) {
169 self.coefficients[0] = value;
170 }
171
172 pub fn vector_part(&self) -> Vector<P, Q, R> {
174 Vector::from_multivector(&self.grade_projection(1))
175 }
176
177 pub fn bivector_part(&self) -> Self {
179 self.grade_projection(2)
180 }
181
182 pub fn bivector_type(&self) -> Bivector<P, Q, R> {
184 Bivector::from_multivector(&self.grade_projection(2))
185 }
186
187 pub fn trivector_part(&self) -> f64 {
189 if Self::DIM >= 3 {
190 self.get(7) } else {
192 0.0
193 }
194 }
195
196 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 pub fn set_bivector_component(&mut self, index: usize, value: f64) {
205 let bivector_indices = match Self::DIM {
207 3 => [3, 5, 6], _ => [3, 5, 6], };
210 if index < bivector_indices.len() {
211 self.coefficients[bivector_indices[index]] = value;
212 }
213 }
214
215 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 pub fn as_slice(&self) -> &[f64] {
226 &self.coefficients
227 }
228
229 pub fn add(&self, other: &Self) -> Self {
231 self + other
232 }
233
234 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 }
244
245 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Self {
247 self.outer_product(&other.mv)
248 }
249
250 pub fn geometric_product(&self, rhs: &Self) -> Self {
255 self.geometric_product_scalar(rhs)
266 }
267
268 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 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 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 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 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 pub fn scalar_product(&self, rhs: &Self) -> f64 {
337 self.geometric_product(rhs).scalar_part()
338 }
339
340 #[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 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 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 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 pub fn is_zero(&self) -> bool {
400 self.coefficients.iter().all(|&c| c.abs() < 1e-14)
401 }
402
403 pub fn norm_squared(&self) -> f64 {
405 self.scalar_product(&self.reverse())
406 }
407
408 pub fn magnitude(&self) -> f64 {
425 self.norm_squared().abs().sqrt()
426 }
427
428 pub fn norm(&self) -> f64 {
433 self.magnitude()
434 }
435
436 pub fn abs(&self) -> f64 {
438 self.magnitude()
439 }
440
441 pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool {
443 (self.clone() - other.clone()).magnitude() < epsilon
444 }
445
446 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 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 pub fn exp(&self) -> Self {
473 let grade2 = self.grade_projection(2);
475 if (self - &grade2).norm() > 1e-10 {
476 return self.exp_series();
478 }
479
480 let b_squared = grade2.geometric_product(&grade2).scalar_part();
482
483 if b_squared > -1e-14 {
484 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 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 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 if (result.clone() - old_result).norm() < 1e-14 {
513 break;
514 }
515 }
516
517 result
518 }
519
520 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 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 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 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 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 let pseudoscalar_index = (1 << n) - 1; for i in 0..Self::BASIS_COUNT {
601 if self.coefficients[i].abs() < 1e-14 {
602 continue;
603 }
604
605 let dual_index = i ^ pseudoscalar_index;
608
609 let _grade_i = i.count_ones() as usize;
611 let _grade_dual = dual_index.count_ones() as usize;
612
613 let mut sign = 1.0;
615
616 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 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 for j in 0..n {
636 let bit_mask = 1 << j;
637 if (dual_index & bit_mask) != 0 {
638 if j >= P + Q {
639 sign *= -1.0;
641 } else if j >= P {
642 sign *= -1.0;
644 }
645 }
647 }
648
649 result.coefficients[dual_index] += sign * self.coefficients[i];
650 }
651
652 result
653 }
654}
655
656impl<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#[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#[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 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 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
876 self.mv.inner_product(&other.mv)
877 }
878
879 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 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 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
891 self.mv.outer_product(&other.mv)
892 }
893
894 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 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 pub fn left_contraction(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
906 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 pub fn normalize(&self) -> Option<Self> {
914 self.mv
915 .normalize()
916 .map(|normalized| Self { mv: normalized })
917 }
918
919 pub fn norm_squared(&self) -> f64 {
921 self.mv.norm_squared()
922 }
923
924 pub fn reverse(&self) -> Self {
926 Self {
927 mv: self.mv.reverse(),
928 }
929 }
930
931 pub fn norm(&self) -> f64 {
936 self.magnitude()
937 }
938
939 pub fn hodge_dual(&self) -> Bivector<P, Q, R> {
942 Bivector {
943 mv: self.mv.hodge_dual(),
944 }
945 }
946}
947
948#[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 } if P + Q + R >= 3 {
961 mv.set_bivector_component(1, xz);
962 } if P + Q + R >= 3 {
964 mv.set_bivector_component(2, yz);
965 } Self { mv }
967 }
968
969 pub fn e12() -> Self {
970 let mut mv = Multivector::zero();
971 mv.set_bivector_component(0, 1.0); Self { mv }
973 }
974
975 pub fn e13() -> Self {
976 let mut mv = Multivector::zero();
977 mv.set_bivector_component(1, 1.0); Self { mv }
979 }
980
981 pub fn e23() -> Self {
982 let mut mv = Multivector::zero();
983 mv.set_bivector_component(2, 1.0); 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 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 pub fn get(&self, index: usize) -> f64 {
1008 match index {
1009 0 => self.mv.get(3), 1 => self.mv.get(5), 2 => self.mv.get(6), _ => 0.0,
1013 }
1014 }
1015
1016 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
1018 self.mv.inner_product(&other.mv)
1019 }
1020
1021 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 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
1028 self.mv.outer_product(&other.mv)
1029 }
1030
1031 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 pub fn right_contraction(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1038 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], 1 => &self.mv.coefficients[5], 2 => &self.mv.coefficients[6], _ => panic!("Bivector index out of range: {}", index),
1054 }
1055 }
1056}
1057
1058pub type E<const P: usize, const Q: usize, const R: usize> = Vector<P, Q, R>;
1060
1061pub 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>; #[test]
1072 fn test_basis_vectors() {
1073 let e1 = Cl3::basis_vector(0);
1074 let e2 = Cl3::basis_vector(1);
1075
1076 let e1_squared = e1.geometric_product(&e1);
1078 assert_eq!(e1_squared.scalar_part(), 1.0);
1079
1080 let e12 = e1.geometric_product(&e2);
1082 let e21 = e2.geometric_product(&e1);
1083 assert_eq!(e12.coefficients[3], -e21.coefficients[3]); }
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); 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 let angle = core::f64::consts::PI / 4.0; let bivector = e12 * angle;
1108 let rotor = bivector.exp();
1109
1110 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 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 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 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 type Spacetime = Multivector<1, 3, 0>; let e0 = Spacetime::basis_vector(0); let e1 = Spacetime::basis_vector(1); 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 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); assert_eq!(grade1.get(2), 4.0); assert_eq!(grade2.get(3), 5.0); 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 assert!(e1.inner_product(&e2).norm() < 1e-12);
1184
1185 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 let bivector = e1.outer_product(&e2);
1193 let trivector = bivector.outer_product(&e3);
1194 assert_eq!(trivector.get(7), 1.0); }
1196}