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::{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#[derive(Debug, Clone, PartialEq)]
65#[repr(C, align(32))] pub struct Multivector<const P: usize, const Q: usize, const R: usize> {
67 coefficients: Box<[f64]>,
70}
71
72impl<const P: usize, const Q: usize, const R: usize> Multivector<P, Q, R> {
73 pub const DIM: usize = P + Q + R;
75
76 pub const BASIS_COUNT: usize = 1 << Self::DIM;
78
79 #[inline(always)]
81 pub fn zero() -> Self {
82 Self {
83 coefficients: vec![0.0; Self::BASIS_COUNT].into_boxed_slice(),
84 }
85 }
86
87 #[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 pub fn from_vector(vector: &Vector<P, Q, R>) -> Self {
97 vector.mv.clone()
98 }
99
100 pub fn from_bivector(bivector: &Bivector<P, Q, R>) -> Self {
102 bivector.mv.clone()
103 }
104
105 #[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 #[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 #[inline(always)]
130 pub fn from_slice(coefficients: &[f64]) -> Self {
131 Self::from_coefficients(coefficients.to_vec())
132 }
133
134 #[inline(always)]
136 pub fn get(&self, index: usize) -> f64 {
137 self.coefficients.get(index).copied().unwrap_or(0.0)
138 }
139
140 #[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 #[inline(always)]
150 pub fn scalar_part(&self) -> f64 {
151 self.coefficients[0]
152 }
153
154 #[inline(always)]
156 pub fn set_scalar(&mut self, value: f64) {
157 self.coefficients[0] = value;
158 }
159
160 pub fn vector_part(&self) -> Vector<P, Q, R> {
162 Vector::from_multivector(&self.grade_projection(1))
163 }
164
165 pub fn bivector_part(&self) -> Self {
167 self.grade_projection(2)
168 }
169
170 pub fn bivector_type(&self) -> Bivector<P, Q, R> {
172 Bivector::from_multivector(&self.grade_projection(2))
173 }
174
175 pub fn trivector_part(&self) -> f64 {
177 if Self::DIM >= 3 {
178 self.get(7) } else {
180 0.0
181 }
182 }
183
184 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 pub fn set_bivector_component(&mut self, index: usize, value: f64) {
193 let bivector_indices = match Self::DIM {
195 3 => [3, 5, 6], _ => [3, 5, 6], };
198 if index < bivector_indices.len() {
199 self.coefficients[bivector_indices[index]] = value;
200 }
201 }
202
203 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 pub fn as_slice(&self) -> &[f64] {
214 &self.coefficients
215 }
216
217 pub fn add(&self, other: &Self) -> Self {
219 self + other
220 }
221
222 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 }
232
233 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Self {
235 self.outer_product(&other.mv)
236 }
237
238 pub fn geometric_product(&self, rhs: &Self) -> Self {
243 self.geometric_product_scalar(rhs)
254 }
255
256 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 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 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 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 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 pub fn scalar_product(&self, rhs: &Self) -> f64 {
325 self.geometric_product(rhs).scalar_part()
326 }
327
328 #[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 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 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 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 pub fn is_zero(&self) -> bool {
388 self.coefficients.iter().all(|&c| c.abs() < 1e-14)
389 }
390
391 pub fn norm_squared(&self) -> f64 {
393 self.scalar_product(&self.reverse())
394 }
395
396 pub fn magnitude(&self) -> f64 {
413 self.norm_squared().abs().sqrt()
414 }
415
416 pub fn norm(&self) -> f64 {
421 self.magnitude()
422 }
423
424 pub fn abs(&self) -> f64 {
426 self.magnitude()
427 }
428
429 pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool {
431 (self.clone() - other.clone()).magnitude() < epsilon
432 }
433
434 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 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 pub fn exp(&self) -> Self {
461 let grade2 = self.grade_projection(2);
463 if (self - &grade2).norm() > 1e-10 {
464 return self.exp_series();
466 }
467
468 let b_squared = grade2.geometric_product(&grade2).scalar_part();
470
471 if b_squared > -1e-14 {
472 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 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 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 if (result.clone() - old_result).norm() < 1e-14 {
501 break;
502 }
503 }
504
505 result
506 }
507
508 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 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 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 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 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 let pseudoscalar_index = (1 << n) - 1; for i in 0..Self::BASIS_COUNT {
589 if self.coefficients[i].abs() < 1e-14 {
590 continue;
591 }
592
593 let dual_index = i ^ pseudoscalar_index;
596
597 let _grade_i = i.count_ones() as usize;
599 let _grade_dual = dual_index.count_ones() as usize;
600
601 let mut sign = 1.0;
603
604 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 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 for j in 0..n {
624 let bit_mask = 1 << j;
625 if (dual_index & bit_mask) != 0 {
626 if j >= P + Q {
627 sign *= -1.0;
629 } else if j >= P {
630 sign *= -1.0;
632 }
633 }
635 }
636
637 result.coefficients[dual_index] += sign * self.coefficients[i];
638 }
639
640 result
641 }
642}
643
644impl<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#[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#[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 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 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
864 self.mv.inner_product(&other.mv)
865 }
866
867 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 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 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
879 self.mv.outer_product(&other.mv)
880 }
881
882 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 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 pub fn left_contraction(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
894 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 pub fn normalize(&self) -> Option<Self> {
902 self.mv
903 .normalize()
904 .map(|normalized| Self { mv: normalized })
905 }
906
907 pub fn norm_squared(&self) -> f64 {
909 self.mv.norm_squared()
910 }
911
912 pub fn reverse(&self) -> Self {
914 Self {
915 mv: self.mv.reverse(),
916 }
917 }
918
919 pub fn norm(&self) -> f64 {
924 self.magnitude()
925 }
926
927 pub fn hodge_dual(&self) -> Bivector<P, Q, R> {
930 Bivector {
931 mv: self.mv.hodge_dual(),
932 }
933 }
934}
935
936#[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 } if P + Q + R >= 3 {
949 mv.set_bivector_component(1, xz);
950 } if P + Q + R >= 3 {
952 mv.set_bivector_component(2, yz);
953 } Self { mv }
955 }
956
957 pub fn e12() -> Self {
958 let mut mv = Multivector::zero();
959 mv.set_bivector_component(0, 1.0); Self { mv }
961 }
962
963 pub fn e13() -> Self {
964 let mut mv = Multivector::zero();
965 mv.set_bivector_component(1, 1.0); Self { mv }
967 }
968
969 pub fn e23() -> Self {
970 let mut mv = Multivector::zero();
971 mv.set_bivector_component(2, 1.0); 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 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 pub fn get(&self, index: usize) -> f64 {
996 match index {
997 0 => self.mv.get(3), 1 => self.mv.get(5), 2 => self.mv.get(6), _ => 0.0,
1001 }
1002 }
1003
1004 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
1006 self.mv.inner_product(&other.mv)
1007 }
1008
1009 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 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
1016 self.mv.outer_product(&other.mv)
1017 }
1018
1019 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 pub fn right_contraction(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1026 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], 1 => &self.mv.coefficients[5], 2 => &self.mv.coefficients[6], _ => panic!("Bivector index out of range: {}", index),
1042 }
1043 }
1044}
1045
1046pub type E<const P: usize, const Q: usize, const R: usize> = Vector<P, Q, R>;
1048
1049pub 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>; #[test]
1060 fn test_basis_vectors() {
1061 let e1 = Cl3::basis_vector(0);
1062 let e2 = Cl3::basis_vector(1);
1063
1064 let e1_squared = e1.geometric_product(&e1);
1066 assert_eq!(e1_squared.scalar_part(), 1.0);
1067
1068 let e12 = e1.geometric_product(&e2);
1070 let e21 = e2.geometric_product(&e1);
1071 assert_eq!(e12.coefficients[3], -e21.coefficients[3]); }
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); 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 let angle = core::f64::consts::PI / 4.0; let bivector = e12 * angle;
1096 let rotor = bivector.exp();
1097
1098 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 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 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 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 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);
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 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); assert_eq!(grade1.get(2), 4.0); assert_eq!(grade2.get(3), 5.0); 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 assert!(e1.inner_product(&e2).norm() < 1e-12);
1172
1173 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 let bivector = e1.outer_product(&e2);
1181 let trivector = bivector.outer_product(&e3);
1182 assert_eq!(trivector.get(7), 1.0); }
1184}