1#![cfg_attr(not(feature = "std"), no_std)]
16
17extern crate alloc;
18use alloc::boxed::Box;
19use alloc::vec::Vec;
20use core::ops::{Add, Mul, Neg, Sub};
21use num_traits::Zero;
22
23pub mod aligned_alloc;
24pub mod basis;
25pub mod cayley;
26pub mod error;
27pub mod rotor;
28pub mod unicode_ops;
29
30#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
31pub mod simd;
32
33pub use error::{CoreError, CoreResult};
35
36#[cfg(feature = "phantom-types")]
37pub mod verified;
38
39#[cfg(feature = "formal-verification")]
40pub mod verified_contracts;
41
42#[cfg(test)]
43pub mod property_tests;
44
45#[cfg(test)]
46pub mod comprehensive_tests;
47
48pub use cayley::CayleyTable;
49
50#[derive(Debug, Clone, PartialEq)]
57#[repr(C, align(32))] pub struct Multivector<const P: usize, const Q: usize, const R: usize> {
59 coefficients: Box<[f64]>,
62}
63
64impl<const P: usize, const Q: usize, const R: usize> Multivector<P, Q, R> {
65 pub const DIM: usize = P + Q + R;
67
68 pub const BASIS_COUNT: usize = 1 << Self::DIM;
70
71 #[inline(always)]
73 pub fn zero() -> Self {
74 Self {
75 coefficients: vec![0.0; Self::BASIS_COUNT].into_boxed_slice(),
76 }
77 }
78
79 #[inline(always)]
81 pub fn scalar(value: f64) -> Self {
82 let mut mv = Self::zero();
83 mv.coefficients[0] = value;
84 mv
85 }
86
87 pub fn from_vector(vector: &Vector<P, Q, R>) -> Self {
89 vector.mv.clone()
90 }
91
92 pub fn from_bivector(bivector: &Bivector<P, Q, R>) -> Self {
94 bivector.mv.clone()
95 }
96
97 #[inline(always)]
99 pub fn basis_vector(i: usize) -> Self {
100 assert!(i < Self::DIM, "Basis vector index out of range");
101 let mut mv = Self::zero();
102 mv.coefficients[1 << i] = 1.0;
103 mv
104 }
105
106 #[inline(always)]
108 pub fn from_coefficients(coefficients: Vec<f64>) -> Self {
109 assert_eq!(
110 coefficients.len(),
111 Self::BASIS_COUNT,
112 "Coefficient array must have {} elements",
113 Self::BASIS_COUNT
114 );
115 Self {
116 coefficients: coefficients.into_boxed_slice(),
117 }
118 }
119
120 #[inline(always)]
122 pub fn from_slice(coefficients: &[f64]) -> Self {
123 Self::from_coefficients(coefficients.to_vec())
124 }
125
126 #[inline(always)]
128 pub fn get(&self, index: usize) -> f64 {
129 self.coefficients.get(index).copied().unwrap_or(0.0)
130 }
131
132 #[inline(always)]
134 pub fn set(&mut self, index: usize, value: f64) {
135 if index < self.coefficients.len() {
136 self.coefficients[index] = value;
137 }
138 }
139
140 #[inline(always)]
142 pub fn scalar_part(&self) -> f64 {
143 self.coefficients[0]
144 }
145
146 #[inline(always)]
148 pub fn set_scalar(&mut self, value: f64) {
149 self.coefficients[0] = value;
150 }
151
152 pub fn vector_part(&self) -> Vector<P, Q, R> {
154 Vector::from_multivector(&self.grade_projection(1))
155 }
156
157 pub fn bivector_part(&self) -> Self {
159 self.grade_projection(2)
160 }
161
162 pub fn bivector_type(&self) -> Bivector<P, Q, R> {
164 Bivector::from_multivector(&self.grade_projection(2))
165 }
166
167 pub fn trivector_part(&self) -> f64 {
169 if Self::DIM >= 3 {
170 self.get(7) } else {
172 0.0
173 }
174 }
175
176 pub fn set_vector_component(&mut self, index: usize, value: f64) {
178 if index < Self::DIM {
179 self.coefficients[1 << index] = value;
180 }
181 }
182
183 pub fn set_bivector_component(&mut self, index: usize, value: f64) {
185 let bivector_indices = match Self::DIM {
187 3 => [3, 5, 6], _ => [3, 5, 6], };
190 if index < bivector_indices.len() {
191 self.coefficients[bivector_indices[index]] = value;
192 }
193 }
194
195 pub fn vector_component(&self, index: usize) -> f64 {
197 if index < Self::DIM {
198 self.get(1 << index)
199 } else {
200 0.0
201 }
202 }
203
204 pub fn as_slice(&self) -> &[f64] {
206 &self.coefficients
207 }
208
209 pub fn add(&self, other: &Self) -> Self {
211 self + other
212 }
213
214 pub fn grade(&self) -> usize {
216 for grade in (0..=Self::DIM).rev() {
217 let projection = self.grade_projection(grade);
218 if !projection.is_zero() {
219 return grade;
220 }
221 }
222 0 }
224
225 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Self {
227 self.outer_product(&other.mv)
228 }
229
230 pub fn geometric_product(&self, rhs: &Self) -> Self {
235 self.geometric_product_scalar(rhs)
246 }
247
248 pub fn geometric_product_scalar(&self, rhs: &Self) -> Self {
250 let table = CayleyTable::<P, Q, R>::get();
251 let mut result = Self::zero();
252
253 for i in 0..Self::BASIS_COUNT {
254 if self.coefficients[i].abs() < 1e-14 {
255 continue;
256 }
257
258 for j in 0..Self::BASIS_COUNT {
259 if rhs.coefficients[j].abs() < 1e-14 {
260 continue;
261 }
262
263 let (sign, index) = table.get_product(i, j);
264 result.coefficients[index] += sign * self.coefficients[i] * rhs.coefficients[j];
265 }
266 }
267
268 result
269 }
270
271 pub fn inner_product(&self, rhs: &Self) -> Self {
273 let self_grades = self.grade_decomposition();
274 let rhs_grades = rhs.grade_decomposition();
275 let mut result = Self::zero();
276
277 for (grade_a, mv_a) in self_grades.iter().enumerate() {
279 for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
280 if !mv_a.is_zero() && !mv_b.is_zero() {
281 let target_grade = grade_a.abs_diff(grade_b);
282 let product = mv_a.geometric_product(mv_b);
283 let projected = product.grade_projection(target_grade);
284 result = result + projected;
285 }
286 }
287 }
288
289 result
290 }
291
292 pub fn outer_product(&self, rhs: &Self) -> Self {
294 let self_grades = self.grade_decomposition();
295 let rhs_grades = rhs.grade_decomposition();
296 let mut result = Self::zero();
297
298 for (grade_a, mv_a) in self_grades.iter().enumerate() {
300 for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
301 if !mv_a.is_zero() && !mv_b.is_zero() {
302 let target_grade = grade_a + grade_b;
303 if target_grade <= Self::DIM {
304 let product = mv_a.geometric_product(mv_b);
305 let projected = product.grade_projection(target_grade);
306 result = result + projected;
307 }
308 }
309 }
310 }
311
312 result
313 }
314
315 pub fn scalar_product(&self, rhs: &Self) -> f64 {
317 self.geometric_product(rhs).scalar_part()
318 }
319
320 #[inline]
326 fn reverse_sign_for_grade(grade: usize) -> f64 {
327 if grade == 0 {
328 return 1.0;
329 }
330 if (grade * (grade - 1) / 2).is_multiple_of(2) {
331 1.0
332 } else {
333 -1.0
334 }
335 }
336
337 pub fn reverse(&self) -> Self {
339 let mut result = Self::zero();
340
341 for i in 0..Self::BASIS_COUNT {
342 let grade = i.count_ones() as usize;
343 let sign = Self::reverse_sign_for_grade(grade);
344 result.coefficients[i] = sign * self.coefficients[i];
345 }
346
347 result
348 }
349
350 pub fn grade_projection(&self, grade: usize) -> Self {
352 let mut result = Self::zero();
353
354 for i in 0..Self::BASIS_COUNT {
355 if i.count_ones() as usize == grade {
356 result.coefficients[i] = self.coefficients[i];
357 }
358 }
359
360 result
361 }
362
363 fn grade_decomposition(&self) -> Vec<Self> {
365 let mut grades = Vec::with_capacity(Self::DIM + 1);
366 for _ in 0..=Self::DIM {
367 grades.push(Self::zero());
368 }
369
370 for i in 0..Self::BASIS_COUNT {
371 let grade = i.count_ones() as usize;
372 grades[grade].coefficients[i] = self.coefficients[i];
373 }
374
375 grades
376 }
377
378 pub fn is_zero(&self) -> bool {
380 self.coefficients.iter().all(|&c| c.abs() < 1e-14)
381 }
382
383 pub fn norm_squared(&self) -> f64 {
385 self.scalar_product(&self.reverse())
386 }
387
388 pub fn magnitude(&self) -> f64 {
405 self.norm_squared().abs().sqrt()
406 }
407
408 pub fn norm(&self) -> f64 {
413 self.magnitude()
414 }
415
416 pub fn abs(&self) -> f64 {
418 self.magnitude()
419 }
420
421 pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool {
423 (self.clone() - other.clone()).magnitude() < epsilon
424 }
425
426 pub fn normalize(&self) -> Option<Self> {
428 let norm = self.norm();
429 if norm > 1e-14 {
430 Some(self * (1.0 / norm))
431 } else {
432 None
433 }
434 }
435
436 pub fn inverse(&self) -> Option<Self> {
438 let rev = self.reverse();
439 let norm_sq = self.scalar_product(&rev);
440
441 if norm_sq.abs() > 1e-14 {
442 Some(rev * (1.0 / norm_sq))
443 } else {
444 None
445 }
446 }
447
448 pub fn exp(&self) -> Self {
453 let grade2 = self.grade_projection(2);
455 if (self - &grade2).norm() > 1e-10 {
456 return self.exp_series();
458 }
459
460 let b_squared = grade2.geometric_product(&grade2).scalar_part();
462
463 if b_squared > -1e-14 {
464 let norm = b_squared.abs().sqrt();
466 if norm < 1e-14 {
467 return Self::scalar(1.0);
468 }
469 let cosh_norm = norm.cosh();
470 let sinh_norm = norm.sinh();
471 Self::scalar(cosh_norm) + grade2 * (sinh_norm / norm)
472 } else {
473 let norm = (-b_squared).sqrt();
475 let cos_norm = norm.cos();
476 let sin_norm = norm.sin();
477 Self::scalar(cos_norm) + grade2 * (sin_norm / norm)
478 }
479 }
480
481 fn exp_series(&self) -> Self {
483 let mut result = Self::scalar(1.0);
484 let mut term = Self::scalar(1.0);
485
486 for n in 1..20 {
487 term = term.geometric_product(self) * (1.0 / n as f64);
488 let old_result = result.clone();
489 result = result + term.clone();
490
491 if (result.clone() - old_result).norm() < 1e-14 {
493 break;
494 }
495 }
496
497 result
498 }
499
500 pub fn left_contraction(&self, other: &Self) -> Self {
505 let self_grades = self.grade_decomposition();
506 let other_grades = other.grade_decomposition();
507 let mut result = Self::zero();
508
509 for (a_grade, mv_a) in self_grades.iter().enumerate() {
510 if mv_a.is_zero() {
511 continue;
512 }
513
514 for (b_grade, mv_b) in other_grades.iter().enumerate() {
515 if mv_b.is_zero() {
516 continue;
517 }
518
519 if b_grade >= a_grade {
521 let target_grade = b_grade - a_grade;
522 let product = mv_a.geometric_product(mv_b);
523 let projected = product.grade_projection(target_grade);
524 result = result + projected;
525 }
526 }
527 }
528
529 result
530 }
531
532 pub fn right_contraction(&self, other: &Self) -> Self {
537 let self_grades = self.grade_decomposition();
538 let other_grades = other.grade_decomposition();
539 let mut result = Self::zero();
540
541 for (a_grade, mv_a) in self_grades.iter().enumerate() {
542 if mv_a.is_zero() {
543 continue;
544 }
545
546 for (b_grade, mv_b) in other_grades.iter().enumerate() {
547 if mv_b.is_zero() {
548 continue;
549 }
550
551 if a_grade >= b_grade {
553 let target_grade = a_grade - b_grade;
554 let product = mv_a.geometric_product(mv_b);
555 let projected = product.grade_projection(target_grade);
556 result = result + projected;
557 }
558 }
559 }
560
561 result
562 }
563
564 pub fn hodge_dual(&self) -> Self {
570 let n = Self::DIM;
571 if n == 0 {
572 return self.clone();
573 }
574
575 let mut result = Self::zero();
576
577 let pseudoscalar_index = (1 << n) - 1; for i in 0..Self::BASIS_COUNT {
581 if self.coefficients[i].abs() < 1e-14 {
582 continue;
583 }
584
585 let dual_index = i ^ pseudoscalar_index;
588
589 let _grade_i = i.count_ones() as usize;
591 let _grade_dual = dual_index.count_ones() as usize;
592
593 let mut sign = 1.0;
595
596 let temp_i = i;
598 let temp_dual = dual_index;
599 let mut swaps = 0;
600
601 for bit_pos in 0..n {
602 let bit_mask = 1 << bit_pos;
603 if (temp_i & bit_mask) != 0 && (temp_dual & bit_mask) == 0 {
604 let right_bits = temp_dual & ((1 << bit_pos) - 1);
606 swaps += right_bits.count_ones();
607 }
608 }
609
610 if swaps % 2 == 1 {
611 sign = -1.0;
612 }
613
614 for j in 0..n {
616 let bit_mask = 1 << j;
617 if (dual_index & bit_mask) != 0 {
618 if j >= P + Q {
619 sign *= -1.0;
621 } else if j >= P {
622 sign *= -1.0;
624 }
625 }
627 }
628
629 result.coefficients[dual_index] += sign * self.coefficients[i];
630 }
631
632 result
633 }
634}
635
636impl<const P: usize, const Q: usize, const R: usize> Add for Multivector<P, Q, R> {
638 type Output = Self;
639
640 #[inline(always)]
641 fn add(mut self, rhs: Self) -> Self {
642 for i in 0..Self::BASIS_COUNT {
643 self.coefficients[i] += rhs.coefficients[i];
644 }
645 self
646 }
647}
648
649impl<const P: usize, const Q: usize, const R: usize> Add for &Multivector<P, Q, R> {
650 type Output = Multivector<P, Q, R>;
651
652 #[inline(always)]
653 fn add(self, rhs: Self) -> Multivector<P, Q, R> {
654 let mut result = self.clone();
655 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
656 result.coefficients[i] += rhs.coefficients[i];
657 }
658 result
659 }
660}
661
662impl<const P: usize, const Q: usize, const R: usize> Sub for Multivector<P, Q, R> {
663 type Output = Self;
664
665 #[inline(always)]
666 fn sub(mut self, rhs: Self) -> Self {
667 for i in 0..Self::BASIS_COUNT {
668 self.coefficients[i] -= rhs.coefficients[i];
669 }
670 self
671 }
672}
673
674impl<const P: usize, const Q: usize, const R: usize> Sub for &Multivector<P, Q, R> {
675 type Output = Multivector<P, Q, R>;
676
677 #[inline(always)]
678 fn sub(self, rhs: Self) -> Multivector<P, Q, R> {
679 let mut result = self.clone();
680 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
681 result.coefficients[i] -= rhs.coefficients[i];
682 }
683 result
684 }
685}
686
687impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for Multivector<P, Q, R> {
688 type Output = Self;
689
690 #[inline(always)]
691 fn mul(mut self, scalar: f64) -> Self {
692 for i in 0..Self::BASIS_COUNT {
693 self.coefficients[i] *= scalar;
694 }
695 self
696 }
697}
698
699impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for &Multivector<P, Q, R> {
700 type Output = Multivector<P, Q, R>;
701
702 #[inline(always)]
703 fn mul(self, scalar: f64) -> Multivector<P, Q, R> {
704 let mut result = self.clone();
705 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
706 result.coefficients[i] *= scalar;
707 }
708 result
709 }
710}
711
712impl<const P: usize, const Q: usize, const R: usize> Neg for Multivector<P, Q, R> {
713 type Output = Self;
714
715 #[inline(always)]
716 fn neg(mut self) -> Self {
717 for i in 0..Self::BASIS_COUNT {
718 self.coefficients[i] = -self.coefficients[i];
719 }
720 self
721 }
722}
723
724impl<const P: usize, const Q: usize, const R: usize> Zero for Multivector<P, Q, R> {
725 fn zero() -> Self {
726 Self::zero()
727 }
728
729 fn is_zero(&self) -> bool {
730 self.is_zero()
731 }
732}
733
734#[derive(Debug, Clone, PartialEq)]
736pub struct Scalar<const P: usize, const Q: usize, const R: usize> {
737 pub mv: Multivector<P, Q, R>,
738}
739
740impl<const P: usize, const Q: usize, const R: usize> Scalar<P, Q, R> {
741 pub fn from(value: f64) -> Self {
742 Self {
743 mv: Multivector::scalar(value),
744 }
745 }
746
747 pub fn one() -> Self {
748 Self::from(1.0)
749 }
750
751 pub fn geometric_product(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
752 self.mv.geometric_product(other)
753 }
754
755 pub fn geometric_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
756 self.mv.geometric_product(&other.mv)
757 }
758}
759
760impl<const P: usize, const Q: usize, const R: usize> From<f64> for Scalar<P, Q, R> {
761 fn from(value: f64) -> Self {
762 Self::from(value)
763 }
764}
765
766#[derive(Debug, Clone, PartialEq)]
768pub struct Vector<const P: usize, const Q: usize, const R: usize> {
769 pub mv: Multivector<P, Q, R>,
770}
771
772impl<const P: usize, const Q: usize, const R: usize> Vector<P, Q, R> {
773 pub fn zero() -> Self {
775 Self {
776 mv: Multivector::zero(),
777 }
778 }
779
780 pub fn from_components(x: f64, y: f64, z: f64) -> Self {
781 let mut mv = Multivector::zero();
782 if P + Q + R >= 1 {
783 mv.set_vector_component(0, x);
784 }
785 if P + Q + R >= 2 {
786 mv.set_vector_component(1, y);
787 }
788 if P + Q + R >= 3 {
789 mv.set_vector_component(2, z);
790 }
791 Self { mv }
792 }
793
794 pub fn e1() -> Self {
795 Self {
796 mv: Multivector::basis_vector(0),
797 }
798 }
799
800 pub fn e2() -> Self {
801 Self {
802 mv: Multivector::basis_vector(1),
803 }
804 }
805
806 pub fn e3() -> Self {
807 Self {
808 mv: Multivector::basis_vector(2),
809 }
810 }
811
812 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
813 Self {
814 mv: mv.grade_projection(1),
815 }
816 }
817
818 pub fn geometric_product(&self, other: &Self) -> Multivector<P, Q, R> {
819 self.mv.geometric_product(&other.mv)
820 }
821
822 pub fn geometric_product_with_multivector(
823 &self,
824 other: &Multivector<P, Q, R>,
825 ) -> Multivector<P, Q, R> {
826 self.mv.geometric_product(other)
827 }
828
829 pub fn geometric_product_with_bivector(
830 &self,
831 other: &Bivector<P, Q, R>,
832 ) -> Multivector<P, Q, R> {
833 self.mv.geometric_product(&other.mv)
834 }
835
836 pub fn geometric_product_with_scalar(&self, other: &Scalar<P, Q, R>) -> Multivector<P, Q, R> {
837 self.mv.geometric_product(&other.mv)
838 }
839
840 pub fn add(&self, other: &Self) -> Self {
841 Self {
842 mv: &self.mv + &other.mv,
843 }
844 }
845
846 pub fn magnitude(&self) -> f64 {
847 self.mv.magnitude()
848 }
849
850 pub fn as_slice(&self) -> &[f64] {
851 self.mv.as_slice()
852 }
853
854 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
856 self.mv.inner_product(&other.mv)
857 }
858
859 pub fn inner_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
861 self.mv.inner_product(other)
862 }
863
864 pub fn inner_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
866 self.mv.inner_product(&other.mv)
867 }
868
869 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
871 self.mv.outer_product(&other.mv)
872 }
873
874 pub fn outer_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
876 self.mv.outer_product(other)
877 }
878
879 pub fn outer_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
881 self.mv.outer_product(&other.mv)
882 }
883
884 pub fn left_contraction(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
886 let product = self.mv.geometric_product(&other.mv);
888 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
889 product.grade_projection(target_grade)
890 }
891
892 pub fn normalize(&self) -> Option<Self> {
894 self.mv
895 .normalize()
896 .map(|normalized| Self { mv: normalized })
897 }
898
899 pub fn norm_squared(&self) -> f64 {
901 self.mv.norm_squared()
902 }
903
904 pub fn reverse(&self) -> Self {
906 Self {
907 mv: self.mv.reverse(),
908 }
909 }
910
911 pub fn norm(&self) -> f64 {
916 self.magnitude()
917 }
918
919 pub fn hodge_dual(&self) -> Bivector<P, Q, R> {
922 Bivector {
923 mv: self.mv.hodge_dual(),
924 }
925 }
926}
927
928#[derive(Debug, Clone, PartialEq)]
930pub struct Bivector<const P: usize, const Q: usize, const R: usize> {
931 pub mv: Multivector<P, Q, R>,
932}
933
934impl<const P: usize, const Q: usize, const R: usize> Bivector<P, Q, R> {
935 pub fn from_components(xy: f64, xz: f64, yz: f64) -> Self {
936 let mut mv = Multivector::zero();
937 if P + Q + R >= 2 {
938 mv.set_bivector_component(0, xy);
939 } if P + Q + R >= 3 {
941 mv.set_bivector_component(1, xz);
942 } if P + Q + R >= 3 {
944 mv.set_bivector_component(2, yz);
945 } Self { mv }
947 }
948
949 pub fn e12() -> Self {
950 let mut mv = Multivector::zero();
951 mv.set_bivector_component(0, 1.0); Self { mv }
953 }
954
955 pub fn e13() -> Self {
956 let mut mv = Multivector::zero();
957 mv.set_bivector_component(1, 1.0); Self { mv }
959 }
960
961 pub fn e23() -> Self {
962 let mut mv = Multivector::zero();
963 mv.set_bivector_component(2, 1.0); Self { mv }
965 }
966
967 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
968 Self {
969 mv: mv.grade_projection(2),
970 }
971 }
972
973 pub fn geometric_product(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
974 self.mv.geometric_product(&other.mv)
975 }
976
977 pub fn geometric_product_with_bivector(&self, other: &Self) -> Multivector<P, Q, R> {
979 self.mv.geometric_product(&other.mv)
980 }
981
982 pub fn magnitude(&self) -> f64 {
983 self.mv.magnitude()
984 }
985
986 pub fn get(&self, index: usize) -> f64 {
988 match index {
989 0 => self.mv.get(3), 1 => self.mv.get(5), 2 => self.mv.get(6), _ => 0.0,
993 }
994 }
995
996 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
998 self.mv.inner_product(&other.mv)
999 }
1000
1001 pub fn inner_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1003 self.mv.inner_product(&other.mv)
1004 }
1005
1006 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
1008 self.mv.outer_product(&other.mv)
1009 }
1010
1011 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1013 self.mv.outer_product(&other.mv)
1014 }
1015
1016 pub fn right_contraction(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1018 let product = self.mv.geometric_product(&other.mv);
1020 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
1021 product.grade_projection(target_grade)
1022 }
1023}
1024
1025impl<const P: usize, const Q: usize, const R: usize> core::ops::Index<usize> for Bivector<P, Q, R> {
1026 type Output = f64;
1027
1028 fn index(&self, index: usize) -> &Self::Output {
1029 match index {
1030 0 => &self.mv.coefficients[3], 1 => &self.mv.coefficients[5], 2 => &self.mv.coefficients[6], _ => panic!("Bivector index out of range: {}", index),
1034 }
1035 }
1036}
1037
1038pub type E<const P: usize, const Q: usize, const R: usize> = Vector<P, Q, R>;
1040
1041pub use rotor::Rotor;
1043
1044#[cfg(test)]
1045mod tests {
1046 use super::*;
1047 use approx::assert_relative_eq;
1048
1049 type Cl3 = Multivector<3, 0, 0>; #[test]
1052 fn test_basis_vectors() {
1053 let e1 = Cl3::basis_vector(0);
1054 let e2 = Cl3::basis_vector(1);
1055
1056 let e1_squared = e1.geometric_product(&e1);
1058 assert_eq!(e1_squared.scalar_part(), 1.0);
1059
1060 let e12 = e1.geometric_product(&e2);
1062 let e21 = e2.geometric_product(&e1);
1063 assert_eq!(e12.coefficients[3], -e21.coefficients[3]); }
1065
1066 #[test]
1067 fn test_wedge_product() {
1068 let e1 = Cl3::basis_vector(0);
1069 let e2 = Cl3::basis_vector(1);
1070
1071 let e12 = e1.outer_product(&e2);
1072 assert!(e12.get(3).abs() - 1.0 < 1e-10); let e11 = e1.outer_product(&e1);
1076 assert!(e11.is_zero());
1077 }
1078
1079 #[test]
1080 fn test_rotor_from_bivector() {
1081 let e1 = Cl3::basis_vector(0);
1082 let e2 = Cl3::basis_vector(1);
1083 let e12 = e1.outer_product(&e2);
1084
1085 let angle = core::f64::consts::PI / 4.0; let bivector = e12 * angle;
1088 let rotor = bivector.exp();
1089
1090 assert!((rotor.norm() - 1.0).abs() < 1e-10);
1092 }
1093
1094 #[test]
1095 fn test_algebraic_identities() {
1096 let e1 = Cl3::basis_vector(0);
1097 let e2 = Cl3::basis_vector(1);
1098 let e3 = Cl3::basis_vector(2);
1099
1100 let a = e1.clone() + e2.clone() * 2.0;
1102 let b = e2.clone() + e3.clone() * 3.0;
1103 let c = e3.clone() + e1.clone() * 0.5;
1104
1105 let left = a.geometric_product(&b).geometric_product(&c);
1106 let right = a.geometric_product(&b.geometric_product(&c));
1107 assert_relative_eq!(left.norm(), right.norm(), epsilon = 1e-12);
1108
1109 let ab_plus_ac = a.geometric_product(&b) + a.geometric_product(&c);
1111 let a_times_b_plus_c = a.geometric_product(&(b.clone() + c.clone()));
1112 assert!((ab_plus_ac - a_times_b_plus_c).norm() < 1e-12);
1113
1114 let ab_reverse = a.geometric_product(&b).reverse();
1116 let b_reverse_a_reverse = b.reverse().geometric_product(&a.reverse());
1117 assert!((ab_reverse - b_reverse_a_reverse).norm() < 1e-12);
1118 }
1119
1120 #[test]
1121 fn test_metric_signature() {
1122 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);
1130 assert_eq!(e1.geometric_product(&e1).scalar_part(), -1.0);
1131 }
1132
1133 #[test]
1134 fn test_grade_operations() {
1135 let e1 = Cl3::basis_vector(0);
1136 let e2 = Cl3::basis_vector(1);
1137 let scalar = Cl3::scalar(2.0);
1138
1139 let mv = scalar + e1.clone() * 3.0 + e2.clone() * 4.0 + e1.outer_product(&e2) * 5.0;
1140
1141 let grade0 = mv.grade_projection(0);
1143 let grade1 = mv.grade_projection(1);
1144 let grade2 = mv.grade_projection(2);
1145
1146 assert_eq!(grade0.scalar_part(), 2.0);
1147 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;
1153 assert!((mv - reconstructed).norm() < 1e-12);
1154 }
1155
1156 #[test]
1157 fn test_inner_and_outer_products() {
1158 let e1 = Cl3::basis_vector(0);
1159 let e2 = Cl3::basis_vector(1);
1160 let e3 = Cl3::basis_vector(2);
1161
1162 assert!(e1.inner_product(&e2).norm() < 1e-12);
1164
1165 let v1 = e1.clone() + e2.clone();
1167 let v2 = e1.clone() * 2.0 + e2.clone() * 2.0;
1168 let inner = v1.inner_product(&v2);
1169 assert_relative_eq!(inner.scalar_part(), 4.0, epsilon = 1e-12);
1170
1171 let bivector = e1.outer_product(&e2);
1173 let trivector = bivector.outer_product(&e3);
1174 assert_eq!(trivector.get(7), 1.0); }
1176}