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 to_vec(&self) -> Vec<f64> {
233 self.coefficients.to_vec()
234 }
235
236 pub fn grade_project(&self, grade: usize) -> Self {
240 self.grade_projection(grade)
241 }
242
243 pub fn wedge(&self, other: &Self) -> Self {
248 self.outer_product(other)
249 }
250
251 pub fn dot(&self, other: &Self) -> Self {
256 self.inner_product(other)
257 }
258
259 pub fn add(&self, other: &Self) -> Self {
261 self + other
262 }
263
264 pub fn grade(&self) -> usize {
266 for grade in (0..=Self::DIM).rev() {
267 let projection = self.grade_projection(grade);
268 if !projection.is_zero() {
269 return grade;
270 }
271 }
272 0 }
274
275 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Self {
277 self.outer_product(&other.mv)
278 }
279
280 pub fn geometric_product(&self, rhs: &Self) -> Self {
285 self.geometric_product_scalar(rhs)
296 }
297
298 pub fn geometric_product_scalar(&self, rhs: &Self) -> Self {
300 let table = CayleyTable::<P, Q, R>::get();
301 let mut result = Self::zero();
302
303 for i in 0..Self::BASIS_COUNT {
304 if self.coefficients[i].abs() < 1e-14 {
305 continue;
306 }
307
308 for j in 0..Self::BASIS_COUNT {
309 if rhs.coefficients[j].abs() < 1e-14 {
310 continue;
311 }
312
313 let (sign, index) = table.get_product(i, j);
314 result.coefficients[index] += sign * self.coefficients[i] * rhs.coefficients[j];
315 }
316 }
317
318 result
319 }
320
321 pub fn inner_product(&self, rhs: &Self) -> Self {
323 let self_grades = self.grade_decomposition();
324 let rhs_grades = rhs.grade_decomposition();
325 let mut result = Self::zero();
326
327 for (grade_a, mv_a) in self_grades.iter().enumerate() {
329 for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
330 if !mv_a.is_zero() && !mv_b.is_zero() {
331 let target_grade = grade_a.abs_diff(grade_b);
332 let product = mv_a.geometric_product(mv_b);
333 let projected = product.grade_projection(target_grade);
334 result = result + projected;
335 }
336 }
337 }
338
339 result
340 }
341
342 pub fn outer_product(&self, rhs: &Self) -> Self {
344 let self_grades = self.grade_decomposition();
345 let rhs_grades = rhs.grade_decomposition();
346 let mut result = Self::zero();
347
348 for (grade_a, mv_a) in self_grades.iter().enumerate() {
350 for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
351 if !mv_a.is_zero() && !mv_b.is_zero() {
352 let target_grade = grade_a + grade_b;
353 if target_grade <= Self::DIM {
354 let product = mv_a.geometric_product(mv_b);
355 let projected = product.grade_projection(target_grade);
356 result = result + projected;
357 }
358 }
359 }
360 }
361
362 result
363 }
364
365 pub fn scalar_product(&self, rhs: &Self) -> f64 {
367 self.geometric_product(rhs).scalar_part()
368 }
369
370 #[inline]
376 fn reverse_sign_for_grade(grade: usize) -> f64 {
377 if grade == 0 {
378 return 1.0;
379 }
380 if (grade * (grade - 1) / 2).is_multiple_of(2) {
381 1.0
382 } else {
383 -1.0
384 }
385 }
386
387 pub fn reverse(&self) -> Self {
389 let mut result = Self::zero();
390
391 for i in 0..Self::BASIS_COUNT {
392 let grade = i.count_ones() as usize;
393 let sign = Self::reverse_sign_for_grade(grade);
394 result.coefficients[i] = sign * self.coefficients[i];
395 }
396
397 result
398 }
399
400 pub fn grade_projection(&self, grade: usize) -> Self {
402 let mut result = Self::zero();
403
404 for i in 0..Self::BASIS_COUNT {
405 if i.count_ones() as usize == grade {
406 result.coefficients[i] = self.coefficients[i];
407 }
408 }
409
410 result
411 }
412
413 fn grade_decomposition(&self) -> Vec<Self> {
415 let mut grades = Vec::with_capacity(Self::DIM + 1);
416 for _ in 0..=Self::DIM {
417 grades.push(Self::zero());
418 }
419
420 for i in 0..Self::BASIS_COUNT {
421 let grade = i.count_ones() as usize;
422 grades[grade].coefficients[i] = self.coefficients[i];
423 }
424
425 grades
426 }
427
428 pub fn is_zero(&self) -> bool {
430 self.coefficients.iter().all(|&c| c.abs() < 1e-14)
431 }
432
433 pub fn norm_squared(&self) -> f64 {
435 self.scalar_product(&self.reverse())
436 }
437
438 pub fn magnitude(&self) -> f64 {
455 self.norm_squared().abs().sqrt()
456 }
457
458 pub fn norm(&self) -> f64 {
463 self.magnitude()
464 }
465
466 pub fn abs(&self) -> f64 {
468 self.magnitude()
469 }
470
471 pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool {
473 (self.clone() - other.clone()).magnitude() < epsilon
474 }
475
476 pub fn normalize(&self) -> Option<Self> {
478 let norm = self.norm();
479 if norm > 1e-14 {
480 Some(self * (1.0 / norm))
481 } else {
482 None
483 }
484 }
485
486 pub fn inverse(&self) -> Option<Self> {
488 let rev = self.reverse();
489 let norm_sq = self.scalar_product(&rev);
490
491 if norm_sq.abs() > 1e-14 {
492 Some(rev * (1.0 / norm_sq))
493 } else {
494 None
495 }
496 }
497
498 pub fn exp(&self) -> Self {
503 let grade2 = self.grade_projection(2);
505 if (self - &grade2).norm() > 1e-10 {
506 return self.exp_series();
508 }
509
510 let b_squared = grade2.geometric_product(&grade2).scalar_part();
512
513 if b_squared > -1e-14 {
514 let norm = b_squared.abs().sqrt();
516 if norm < 1e-14 {
517 return Self::scalar(1.0);
518 }
519 let cosh_norm = norm.cosh();
520 let sinh_norm = norm.sinh();
521 Self::scalar(cosh_norm) + grade2 * (sinh_norm / norm)
522 } else {
523 let norm = (-b_squared).sqrt();
525 let cos_norm = norm.cos();
526 let sin_norm = norm.sin();
527 Self::scalar(cos_norm) + grade2 * (sin_norm / norm)
528 }
529 }
530
531 fn exp_series(&self) -> Self {
533 let mut result = Self::scalar(1.0);
534 let mut term = Self::scalar(1.0);
535
536 for n in 1..20 {
537 term = term.geometric_product(self) * (1.0 / n as f64);
538 let old_result = result.clone();
539 result = result + term.clone();
540
541 if (result.clone() - old_result).norm() < 1e-14 {
543 break;
544 }
545 }
546
547 result
548 }
549
550 pub fn left_contraction(&self, other: &Self) -> Self {
555 let self_grades = self.grade_decomposition();
556 let other_grades = other.grade_decomposition();
557 let mut result = Self::zero();
558
559 for (a_grade, mv_a) in self_grades.iter().enumerate() {
560 if mv_a.is_zero() {
561 continue;
562 }
563
564 for (b_grade, mv_b) in other_grades.iter().enumerate() {
565 if mv_b.is_zero() {
566 continue;
567 }
568
569 if b_grade >= a_grade {
571 let target_grade = b_grade - a_grade;
572 let product = mv_a.geometric_product(mv_b);
573 let projected = product.grade_projection(target_grade);
574 result = result + projected;
575 }
576 }
577 }
578
579 result
580 }
581
582 pub fn right_contraction(&self, other: &Self) -> Self {
587 let self_grades = self.grade_decomposition();
588 let other_grades = other.grade_decomposition();
589 let mut result = Self::zero();
590
591 for (a_grade, mv_a) in self_grades.iter().enumerate() {
592 if mv_a.is_zero() {
593 continue;
594 }
595
596 for (b_grade, mv_b) in other_grades.iter().enumerate() {
597 if mv_b.is_zero() {
598 continue;
599 }
600
601 if a_grade >= b_grade {
603 let target_grade = a_grade - b_grade;
604 let product = mv_a.geometric_product(mv_b);
605 let projected = product.grade_projection(target_grade);
606 result = result + projected;
607 }
608 }
609 }
610
611 result
612 }
613
614 pub fn hodge_dual(&self) -> Self {
620 let n = Self::DIM;
621 if n == 0 {
622 return self.clone();
623 }
624
625 let mut result = Self::zero();
626
627 let pseudoscalar_index = (1 << n) - 1; for i in 0..Self::BASIS_COUNT {
631 if self.coefficients[i].abs() < 1e-14 {
632 continue;
633 }
634
635 let dual_index = i ^ pseudoscalar_index;
638
639 let _grade_i = i.count_ones() as usize;
641 let _grade_dual = dual_index.count_ones() as usize;
642
643 let mut sign = 1.0;
645
646 let temp_i = i;
648 let temp_dual = dual_index;
649 let mut swaps = 0;
650
651 for bit_pos in 0..n {
652 let bit_mask = 1 << bit_pos;
653 if (temp_i & bit_mask) != 0 && (temp_dual & bit_mask) == 0 {
654 let right_bits = temp_dual & ((1 << bit_pos) - 1);
656 swaps += right_bits.count_ones();
657 }
658 }
659
660 if swaps % 2 == 1 {
661 sign = -1.0;
662 }
663
664 for j in 0..n {
666 let bit_mask = 1 << j;
667 if (dual_index & bit_mask) != 0 {
668 if j >= P + Q {
669 sign *= -1.0;
671 } else if j >= P {
672 sign *= -1.0;
674 }
675 }
677 }
678
679 result.coefficients[dual_index] += sign * self.coefficients[i];
680 }
681
682 result
683 }
684}
685
686impl<const P: usize, const Q: usize, const R: usize> Add for Multivector<P, Q, R> {
688 type Output = Self;
689
690 #[inline(always)]
691 fn add(mut self, rhs: Self) -> Self {
692 for i in 0..Self::BASIS_COUNT {
693 self.coefficients[i] += rhs.coefficients[i];
694 }
695 self
696 }
697}
698
699impl<const P: usize, const Q: usize, const R: usize> Add for &Multivector<P, Q, R> {
700 type Output = Multivector<P, Q, R>;
701
702 #[inline(always)]
703 fn add(self, rhs: Self) -> 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] += rhs.coefficients[i];
707 }
708 result
709 }
710}
711
712impl<const P: usize, const Q: usize, const R: usize> Sub for Multivector<P, Q, R> {
713 type Output = Self;
714
715 #[inline(always)]
716 fn sub(mut self, rhs: Self) -> Self {
717 for i in 0..Self::BASIS_COUNT {
718 self.coefficients[i] -= rhs.coefficients[i];
719 }
720 self
721 }
722}
723
724impl<const P: usize, const Q: usize, const R: usize> Sub for &Multivector<P, Q, R> {
725 type Output = Multivector<P, Q, R>;
726
727 #[inline(always)]
728 fn sub(self, rhs: Self) -> Multivector<P, Q, R> {
729 let mut result = self.clone();
730 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
731 result.coefficients[i] -= rhs.coefficients[i];
732 }
733 result
734 }
735}
736
737impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for Multivector<P, Q, R> {
738 type Output = Self;
739
740 #[inline(always)]
741 fn mul(mut self, scalar: f64) -> Self {
742 for i in 0..Self::BASIS_COUNT {
743 self.coefficients[i] *= scalar;
744 }
745 self
746 }
747}
748
749impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for &Multivector<P, Q, R> {
750 type Output = Multivector<P, Q, R>;
751
752 #[inline(always)]
753 fn mul(self, scalar: f64) -> Multivector<P, Q, R> {
754 let mut result = self.clone();
755 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
756 result.coefficients[i] *= scalar;
757 }
758 result
759 }
760}
761
762impl<const P: usize, const Q: usize, const R: usize> Neg for Multivector<P, Q, R> {
763 type Output = Self;
764
765 #[inline(always)]
766 fn neg(mut self) -> Self {
767 for i in 0..Self::BASIS_COUNT {
768 self.coefficients[i] = -self.coefficients[i];
769 }
770 self
771 }
772}
773
774impl<const P: usize, const Q: usize, const R: usize> Zero for Multivector<P, Q, R> {
775 fn zero() -> Self {
776 Self::zero()
777 }
778
779 fn is_zero(&self) -> bool {
780 self.is_zero()
781 }
782}
783
784#[derive(Debug, Clone, PartialEq)]
786pub struct Scalar<const P: usize, const Q: usize, const R: usize> {
787 pub mv: Multivector<P, Q, R>,
788}
789
790impl<const P: usize, const Q: usize, const R: usize> Scalar<P, Q, R> {
791 pub fn from(value: f64) -> Self {
792 Self {
793 mv: Multivector::scalar(value),
794 }
795 }
796
797 pub fn one() -> Self {
798 Self::from(1.0)
799 }
800
801 pub fn geometric_product(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
802 self.mv.geometric_product(other)
803 }
804
805 pub fn geometric_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
806 self.mv.geometric_product(&other.mv)
807 }
808}
809
810impl<const P: usize, const Q: usize, const R: usize> From<f64> for Scalar<P, Q, R> {
811 fn from(value: f64) -> Self {
812 Self::from(value)
813 }
814}
815
816#[derive(Debug, Clone, PartialEq)]
818pub struct Vector<const P: usize, const Q: usize, const R: usize> {
819 pub mv: Multivector<P, Q, R>,
820}
821
822impl<const P: usize, const Q: usize, const R: usize> Vector<P, Q, R> {
823 pub fn zero() -> Self {
825 Self {
826 mv: Multivector::zero(),
827 }
828 }
829
830 pub fn from_components(x: f64, y: f64, z: f64) -> Self {
831 let mut mv = Multivector::zero();
832 if P + Q + R >= 1 {
833 mv.set_vector_component(0, x);
834 }
835 if P + Q + R >= 2 {
836 mv.set_vector_component(1, y);
837 }
838 if P + Q + R >= 3 {
839 mv.set_vector_component(2, z);
840 }
841 Self { mv }
842 }
843
844 pub fn e1() -> Self {
845 Self {
846 mv: Multivector::basis_vector(0),
847 }
848 }
849
850 pub fn e2() -> Self {
851 Self {
852 mv: Multivector::basis_vector(1),
853 }
854 }
855
856 pub fn e3() -> Self {
857 Self {
858 mv: Multivector::basis_vector(2),
859 }
860 }
861
862 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
863 Self {
864 mv: mv.grade_projection(1),
865 }
866 }
867
868 pub fn geometric_product(&self, other: &Self) -> Multivector<P, Q, R> {
869 self.mv.geometric_product(&other.mv)
870 }
871
872 pub fn geometric_product_with_multivector(
873 &self,
874 other: &Multivector<P, Q, R>,
875 ) -> Multivector<P, Q, R> {
876 self.mv.geometric_product(other)
877 }
878
879 pub fn geometric_product_with_bivector(
880 &self,
881 other: &Bivector<P, Q, R>,
882 ) -> Multivector<P, Q, R> {
883 self.mv.geometric_product(&other.mv)
884 }
885
886 pub fn geometric_product_with_scalar(&self, other: &Scalar<P, Q, R>) -> Multivector<P, Q, R> {
887 self.mv.geometric_product(&other.mv)
888 }
889
890 pub fn add(&self, other: &Self) -> Self {
891 Self {
892 mv: &self.mv + &other.mv,
893 }
894 }
895
896 pub fn magnitude(&self) -> f64 {
897 self.mv.magnitude()
898 }
899
900 pub fn as_slice(&self) -> &[f64] {
901 self.mv.as_slice()
902 }
903
904 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
906 self.mv.inner_product(&other.mv)
907 }
908
909 pub fn inner_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
911 self.mv.inner_product(other)
912 }
913
914 pub fn inner_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
916 self.mv.inner_product(&other.mv)
917 }
918
919 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
921 self.mv.outer_product(&other.mv)
922 }
923
924 pub fn outer_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
926 self.mv.outer_product(other)
927 }
928
929 pub fn outer_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
931 self.mv.outer_product(&other.mv)
932 }
933
934 pub fn left_contraction(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
936 let product = self.mv.geometric_product(&other.mv);
938 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
939 product.grade_projection(target_grade)
940 }
941
942 pub fn normalize(&self) -> Option<Self> {
944 self.mv
945 .normalize()
946 .map(|normalized| Self { mv: normalized })
947 }
948
949 pub fn norm_squared(&self) -> f64 {
951 self.mv.norm_squared()
952 }
953
954 pub fn reverse(&self) -> Self {
956 Self {
957 mv: self.mv.reverse(),
958 }
959 }
960
961 pub fn norm(&self) -> f64 {
966 self.magnitude()
967 }
968
969 pub fn hodge_dual(&self) -> Bivector<P, Q, R> {
972 Bivector {
973 mv: self.mv.hodge_dual(),
974 }
975 }
976}
977
978#[derive(Debug, Clone, PartialEq)]
980pub struct Bivector<const P: usize, const Q: usize, const R: usize> {
981 pub mv: Multivector<P, Q, R>,
982}
983
984impl<const P: usize, const Q: usize, const R: usize> Bivector<P, Q, R> {
985 pub fn from_components(xy: f64, xz: f64, yz: f64) -> Self {
986 let mut mv = Multivector::zero();
987 if P + Q + R >= 2 {
988 mv.set_bivector_component(0, xy);
989 } if P + Q + R >= 3 {
991 mv.set_bivector_component(1, xz);
992 } if P + Q + R >= 3 {
994 mv.set_bivector_component(2, yz);
995 } Self { mv }
997 }
998
999 pub fn e12() -> Self {
1000 let mut mv = Multivector::zero();
1001 mv.set_bivector_component(0, 1.0); Self { mv }
1003 }
1004
1005 pub fn e13() -> Self {
1006 let mut mv = Multivector::zero();
1007 mv.set_bivector_component(1, 1.0); Self { mv }
1009 }
1010
1011 pub fn e23() -> Self {
1012 let mut mv = Multivector::zero();
1013 mv.set_bivector_component(2, 1.0); Self { mv }
1015 }
1016
1017 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
1018 Self {
1019 mv: mv.grade_projection(2),
1020 }
1021 }
1022
1023 pub fn geometric_product(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1024 self.mv.geometric_product(&other.mv)
1025 }
1026
1027 pub fn geometric_product_with_bivector(&self, other: &Self) -> Multivector<P, Q, R> {
1029 self.mv.geometric_product(&other.mv)
1030 }
1031
1032 pub fn magnitude(&self) -> f64 {
1033 self.mv.magnitude()
1034 }
1035
1036 pub fn get(&self, index: usize) -> f64 {
1038 match index {
1039 0 => self.mv.get(3), 1 => self.mv.get(5), 2 => self.mv.get(6), _ => 0.0,
1043 }
1044 }
1045
1046 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
1048 self.mv.inner_product(&other.mv)
1049 }
1050
1051 pub fn inner_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1053 self.mv.inner_product(&other.mv)
1054 }
1055
1056 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
1058 self.mv.outer_product(&other.mv)
1059 }
1060
1061 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1063 self.mv.outer_product(&other.mv)
1064 }
1065
1066 pub fn right_contraction(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1068 let product = self.mv.geometric_product(&other.mv);
1070 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
1071 product.grade_projection(target_grade)
1072 }
1073}
1074
1075impl<const P: usize, const Q: usize, const R: usize> core::ops::Index<usize> for Bivector<P, Q, R> {
1076 type Output = f64;
1077
1078 fn index(&self, index: usize) -> &Self::Output {
1079 match index {
1080 0 => &self.mv.coefficients[3], 1 => &self.mv.coefficients[5], 2 => &self.mv.coefficients[6], _ => panic!("Bivector index out of range: {}", index),
1084 }
1085 }
1086}
1087
1088pub type E<const P: usize, const Q: usize, const R: usize> = Vector<P, Q, R>;
1090
1091pub use rotor::Rotor;
1093
1094#[cfg(test)]
1095mod tests {
1096 use super::*;
1097 use approx::assert_relative_eq;
1098
1099 type Cl3 = Multivector<3, 0, 0>; #[test]
1102 fn test_basis_vectors() {
1103 let e1 = Cl3::basis_vector(0);
1104 let e2 = Cl3::basis_vector(1);
1105
1106 let e1_squared = e1.geometric_product(&e1);
1108 assert_eq!(e1_squared.scalar_part(), 1.0);
1109
1110 let e12 = e1.geometric_product(&e2);
1112 let e21 = e2.geometric_product(&e1);
1113 assert_eq!(e12.coefficients[3], -e21.coefficients[3]); }
1115
1116 #[test]
1117 fn test_wedge_product() {
1118 let e1 = Cl3::basis_vector(0);
1119 let e2 = Cl3::basis_vector(1);
1120
1121 let e12 = e1.outer_product(&e2);
1122 assert!(e12.get(3).abs() - 1.0 < 1e-10); let e11 = e1.outer_product(&e1);
1126 assert!(e11.is_zero());
1127 }
1128
1129 #[test]
1130 fn test_rotor_from_bivector() {
1131 let e1 = Cl3::basis_vector(0);
1132 let e2 = Cl3::basis_vector(1);
1133 let e12 = e1.outer_product(&e2);
1134
1135 let angle = core::f64::consts::PI / 4.0; let bivector = e12 * angle;
1138 let rotor = bivector.exp();
1139
1140 assert!((rotor.norm() - 1.0).abs() < 1e-10);
1142 }
1143
1144 #[test]
1145 fn test_algebraic_identities() {
1146 let e1 = Cl3::basis_vector(0);
1147 let e2 = Cl3::basis_vector(1);
1148 let e3 = Cl3::basis_vector(2);
1149
1150 let a = e1.clone() + e2.clone() * 2.0;
1152 let b = e2.clone() + e3.clone() * 3.0;
1153 let c = e3.clone() + e1.clone() * 0.5;
1154
1155 let left = a.geometric_product(&b).geometric_product(&c);
1156 let right = a.geometric_product(&b.geometric_product(&c));
1157 assert_relative_eq!(left.norm(), right.norm(), epsilon = 1e-12);
1158
1159 let ab_plus_ac = a.geometric_product(&b) + a.geometric_product(&c);
1161 let a_times_b_plus_c = a.geometric_product(&(b.clone() + c.clone()));
1162 assert!((ab_plus_ac - a_times_b_plus_c).norm() < 1e-12);
1163
1164 let ab_reverse = a.geometric_product(&b).reverse();
1166 let b_reverse_a_reverse = b.reverse().geometric_product(&a.reverse());
1167 assert!((ab_reverse - b_reverse_a_reverse).norm() < 1e-12);
1168 }
1169
1170 #[test]
1171 fn test_metric_signature() {
1172 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);
1180 assert_eq!(e1.geometric_product(&e1).scalar_part(), -1.0);
1181 }
1182
1183 #[test]
1184 fn test_grade_operations() {
1185 let e1 = Cl3::basis_vector(0);
1186 let e2 = Cl3::basis_vector(1);
1187 let scalar = Cl3::scalar(2.0);
1188
1189 let mv = scalar + e1.clone() * 3.0 + e2.clone() * 4.0 + e1.outer_product(&e2) * 5.0;
1190
1191 let grade0 = mv.grade_projection(0);
1193 let grade1 = mv.grade_projection(1);
1194 let grade2 = mv.grade_projection(2);
1195
1196 assert_eq!(grade0.scalar_part(), 2.0);
1197 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;
1203 assert!((mv - reconstructed).norm() < 1e-12);
1204 }
1205
1206 #[test]
1207 fn test_inner_and_outer_products() {
1208 let e1 = Cl3::basis_vector(0);
1209 let e2 = Cl3::basis_vector(1);
1210 let e3 = Cl3::basis_vector(2);
1211
1212 assert!(e1.inner_product(&e2).norm() < 1e-12);
1214
1215 let v1 = e1.clone() + e2.clone();
1217 let v2 = e1.clone() * 2.0 + e2.clone() * 2.0;
1218 let inner = v1.inner_product(&v2);
1219 assert_relative_eq!(inner.scalar_part(), 4.0, epsilon = 1e-12);
1220
1221 let bivector = e1.outer_product(&e2);
1223 let trivector = bivector.outer_product(&e3);
1224 assert_eq!(trivector.get(7), 1.0); }
1226}