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 pub fn grade_magnitude(&self, grade: usize) -> f64 {
432 self.grade_projection(grade).magnitude()
433 }
434
435 pub fn grade_spectrum(&self) -> Vec<f64> {
440 (0..=Self::DIM).map(|g| self.grade_magnitude(g)).collect()
441 }
442
443 fn grade_decomposition(&self) -> Vec<Self> {
445 let mut grades = Vec::with_capacity(Self::DIM + 1);
446 for _ in 0..=Self::DIM {
447 grades.push(Self::zero());
448 }
449
450 for i in 0..Self::BASIS_COUNT {
451 let grade = i.count_ones() as usize;
452 grades[grade].coefficients[i] = self.coefficients[i];
453 }
454
455 grades
456 }
457
458 pub fn is_zero(&self) -> bool {
460 self.coefficients.iter().all(|&c| c.abs() < 1e-14)
461 }
462
463 pub fn norm_squared(&self) -> f64 {
465 self.scalar_product(&self.reverse())
466 }
467
468 pub fn magnitude(&self) -> f64 {
485 self.norm_squared().abs().sqrt()
486 }
487
488 pub fn norm(&self) -> f64 {
493 self.magnitude()
494 }
495
496 pub fn abs(&self) -> f64 {
498 self.magnitude()
499 }
500
501 pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool {
503 (self.clone() - other.clone()).magnitude() < epsilon
504 }
505
506 pub fn normalize(&self) -> Option<Self> {
508 let norm = self.norm();
509 if norm > 1e-14 {
510 Some(self * (1.0 / norm))
511 } else {
512 None
513 }
514 }
515
516 pub fn inverse(&self) -> Option<Self> {
518 let rev = self.reverse();
519 let norm_sq = self.scalar_product(&rev);
520
521 if norm_sq.abs() > 1e-14 {
522 Some(rev * (1.0 / norm_sq))
523 } else {
524 None
525 }
526 }
527
528 pub fn exp(&self) -> Self {
533 let grade2 = self.grade_projection(2);
535 if (self - &grade2).norm() > 1e-10 {
536 return self.exp_series();
538 }
539
540 let b_squared = grade2.geometric_product(&grade2).scalar_part();
542
543 if b_squared > -1e-14 {
544 let norm = b_squared.abs().sqrt();
546 if norm < 1e-14 {
547 return Self::scalar(1.0);
548 }
549 let cosh_norm = norm.cosh();
550 let sinh_norm = norm.sinh();
551 Self::scalar(cosh_norm) + grade2 * (sinh_norm / norm)
552 } else {
553 let norm = (-b_squared).sqrt();
555 let cos_norm = norm.cos();
556 let sin_norm = norm.sin();
557 Self::scalar(cos_norm) + grade2 * (sin_norm / norm)
558 }
559 }
560
561 fn exp_series(&self) -> Self {
563 let mut result = Self::scalar(1.0);
564 let mut term = Self::scalar(1.0);
565
566 for n in 1..20 {
567 term = term.geometric_product(self) * (1.0 / n as f64);
568 let old_result = result.clone();
569 result = result + term.clone();
570
571 if (result.clone() - old_result).norm() < 1e-14 {
573 break;
574 }
575 }
576
577 result
578 }
579
580 pub fn left_contraction(&self, other: &Self) -> Self {
585 let self_grades = self.grade_decomposition();
586 let other_grades = other.grade_decomposition();
587 let mut result = Self::zero();
588
589 for (a_grade, mv_a) in self_grades.iter().enumerate() {
590 if mv_a.is_zero() {
591 continue;
592 }
593
594 for (b_grade, mv_b) in other_grades.iter().enumerate() {
595 if mv_b.is_zero() {
596 continue;
597 }
598
599 if b_grade >= a_grade {
601 let target_grade = b_grade - a_grade;
602 let product = mv_a.geometric_product(mv_b);
603 let projected = product.grade_projection(target_grade);
604 result = result + projected;
605 }
606 }
607 }
608
609 result
610 }
611
612 pub fn right_contraction(&self, other: &Self) -> Self {
617 let self_grades = self.grade_decomposition();
618 let other_grades = other.grade_decomposition();
619 let mut result = Self::zero();
620
621 for (a_grade, mv_a) in self_grades.iter().enumerate() {
622 if mv_a.is_zero() {
623 continue;
624 }
625
626 for (b_grade, mv_b) in other_grades.iter().enumerate() {
627 if mv_b.is_zero() {
628 continue;
629 }
630
631 if a_grade >= b_grade {
633 let target_grade = a_grade - b_grade;
634 let product = mv_a.geometric_product(mv_b);
635 let projected = product.grade_projection(target_grade);
636 result = result + projected;
637 }
638 }
639 }
640
641 result
642 }
643
644 pub fn hodge_dual(&self) -> Self {
650 let n = Self::DIM;
651 if n == 0 {
652 return self.clone();
653 }
654
655 let mut result = Self::zero();
656
657 let pseudoscalar_index = (1 << n) - 1; for i in 0..Self::BASIS_COUNT {
661 if self.coefficients[i].abs() < 1e-14 {
662 continue;
663 }
664
665 let dual_index = i ^ pseudoscalar_index;
668
669 let _grade_i = i.count_ones() as usize;
671 let _grade_dual = dual_index.count_ones() as usize;
672
673 let mut sign = 1.0;
675
676 let temp_i = i;
678 let temp_dual = dual_index;
679 let mut swaps = 0;
680
681 for bit_pos in 0..n {
682 let bit_mask = 1 << bit_pos;
683 if (temp_i & bit_mask) != 0 && (temp_dual & bit_mask) == 0 {
684 let right_bits = temp_dual & ((1 << bit_pos) - 1);
686 swaps += right_bits.count_ones();
687 }
688 }
689
690 if swaps % 2 == 1 {
691 sign = -1.0;
692 }
693
694 for j in 0..n {
696 let bit_mask = 1 << j;
697 if (dual_index & bit_mask) != 0 {
698 if j >= P + Q {
699 sign *= -1.0;
701 } else if j >= P {
702 sign *= -1.0;
704 }
705 }
707 }
708
709 result.coefficients[dual_index] += sign * self.coefficients[i];
710 }
711
712 result
713 }
714}
715
716impl<const P: usize, const Q: usize, const R: usize> Add for Multivector<P, Q, R> {
718 type Output = Self;
719
720 #[inline(always)]
721 fn add(mut self, rhs: Self) -> Self {
722 for i in 0..Self::BASIS_COUNT {
723 self.coefficients[i] += rhs.coefficients[i];
724 }
725 self
726 }
727}
728
729impl<const P: usize, const Q: usize, const R: usize> Add for &Multivector<P, Q, R> {
730 type Output = Multivector<P, Q, R>;
731
732 #[inline(always)]
733 fn add(self, rhs: Self) -> Multivector<P, Q, R> {
734 let mut result = self.clone();
735 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
736 result.coefficients[i] += rhs.coefficients[i];
737 }
738 result
739 }
740}
741
742impl<const P: usize, const Q: usize, const R: usize> Sub for Multivector<P, Q, R> {
743 type Output = Self;
744
745 #[inline(always)]
746 fn sub(mut self, rhs: Self) -> Self {
747 for i in 0..Self::BASIS_COUNT {
748 self.coefficients[i] -= rhs.coefficients[i];
749 }
750 self
751 }
752}
753
754impl<const P: usize, const Q: usize, const R: usize> Sub for &Multivector<P, Q, R> {
755 type Output = Multivector<P, Q, R>;
756
757 #[inline(always)]
758 fn sub(self, rhs: Self) -> Multivector<P, Q, R> {
759 let mut result = self.clone();
760 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
761 result.coefficients[i] -= rhs.coefficients[i];
762 }
763 result
764 }
765}
766
767impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for Multivector<P, Q, R> {
768 type Output = Self;
769
770 #[inline(always)]
771 fn mul(mut self, scalar: f64) -> Self {
772 for i in 0..Self::BASIS_COUNT {
773 self.coefficients[i] *= scalar;
774 }
775 self
776 }
777}
778
779impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for &Multivector<P, Q, R> {
780 type Output = Multivector<P, Q, R>;
781
782 #[inline(always)]
783 fn mul(self, scalar: f64) -> Multivector<P, Q, R> {
784 let mut result = self.clone();
785 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
786 result.coefficients[i] *= scalar;
787 }
788 result
789 }
790}
791
792impl<const P: usize, const Q: usize, const R: usize> Neg for Multivector<P, Q, R> {
793 type Output = Self;
794
795 #[inline(always)]
796 fn neg(mut self) -> Self {
797 for i in 0..Self::BASIS_COUNT {
798 self.coefficients[i] = -self.coefficients[i];
799 }
800 self
801 }
802}
803
804impl<const P: usize, const Q: usize, const R: usize> Zero for Multivector<P, Q, R> {
805 fn zero() -> Self {
806 Self::zero()
807 }
808
809 fn is_zero(&self) -> bool {
810 self.is_zero()
811 }
812}
813
814#[derive(Debug, Clone, PartialEq)]
816pub struct Scalar<const P: usize, const Q: usize, const R: usize> {
817 pub mv: Multivector<P, Q, R>,
818}
819
820impl<const P: usize, const Q: usize, const R: usize> Scalar<P, Q, R> {
821 pub fn from(value: f64) -> Self {
822 Self {
823 mv: Multivector::scalar(value),
824 }
825 }
826
827 pub fn one() -> Self {
828 Self::from(1.0)
829 }
830
831 pub fn geometric_product(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
832 self.mv.geometric_product(other)
833 }
834
835 pub fn geometric_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
836 self.mv.geometric_product(&other.mv)
837 }
838}
839
840impl<const P: usize, const Q: usize, const R: usize> From<f64> for Scalar<P, Q, R> {
841 fn from(value: f64) -> Self {
842 Self::from(value)
843 }
844}
845
846#[derive(Debug, Clone, PartialEq)]
848pub struct Vector<const P: usize, const Q: usize, const R: usize> {
849 pub mv: Multivector<P, Q, R>,
850}
851
852impl<const P: usize, const Q: usize, const R: usize> Vector<P, Q, R> {
853 pub fn zero() -> Self {
855 Self {
856 mv: Multivector::zero(),
857 }
858 }
859
860 pub fn from_components(x: f64, y: f64, z: f64) -> Self {
861 let mut mv = Multivector::zero();
862 if P + Q + R >= 1 {
863 mv.set_vector_component(0, x);
864 }
865 if P + Q + R >= 2 {
866 mv.set_vector_component(1, y);
867 }
868 if P + Q + R >= 3 {
869 mv.set_vector_component(2, z);
870 }
871 Self { mv }
872 }
873
874 pub fn e1() -> Self {
875 Self {
876 mv: Multivector::basis_vector(0),
877 }
878 }
879
880 pub fn e2() -> Self {
881 Self {
882 mv: Multivector::basis_vector(1),
883 }
884 }
885
886 pub fn e3() -> Self {
887 Self {
888 mv: Multivector::basis_vector(2),
889 }
890 }
891
892 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
893 Self {
894 mv: mv.grade_projection(1),
895 }
896 }
897
898 pub fn geometric_product(&self, other: &Self) -> Multivector<P, Q, R> {
899 self.mv.geometric_product(&other.mv)
900 }
901
902 pub fn geometric_product_with_multivector(
903 &self,
904 other: &Multivector<P, Q, R>,
905 ) -> Multivector<P, Q, R> {
906 self.mv.geometric_product(other)
907 }
908
909 pub fn geometric_product_with_bivector(
910 &self,
911 other: &Bivector<P, Q, R>,
912 ) -> Multivector<P, Q, R> {
913 self.mv.geometric_product(&other.mv)
914 }
915
916 pub fn geometric_product_with_scalar(&self, other: &Scalar<P, Q, R>) -> Multivector<P, Q, R> {
917 self.mv.geometric_product(&other.mv)
918 }
919
920 pub fn add(&self, other: &Self) -> Self {
921 Self {
922 mv: &self.mv + &other.mv,
923 }
924 }
925
926 pub fn magnitude(&self) -> f64 {
927 self.mv.magnitude()
928 }
929
930 pub fn as_slice(&self) -> &[f64] {
931 self.mv.as_slice()
932 }
933
934 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
936 self.mv.inner_product(&other.mv)
937 }
938
939 pub fn inner_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
941 self.mv.inner_product(other)
942 }
943
944 pub fn inner_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
946 self.mv.inner_product(&other.mv)
947 }
948
949 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
951 self.mv.outer_product(&other.mv)
952 }
953
954 pub fn outer_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
956 self.mv.outer_product(other)
957 }
958
959 pub fn outer_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
961 self.mv.outer_product(&other.mv)
962 }
963
964 pub fn left_contraction(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
966 let product = self.mv.geometric_product(&other.mv);
968 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
969 product.grade_projection(target_grade)
970 }
971
972 pub fn normalize(&self) -> Option<Self> {
974 self.mv
975 .normalize()
976 .map(|normalized| Self { mv: normalized })
977 }
978
979 pub fn norm_squared(&self) -> f64 {
981 self.mv.norm_squared()
982 }
983
984 pub fn reverse(&self) -> Self {
986 Self {
987 mv: self.mv.reverse(),
988 }
989 }
990
991 pub fn norm(&self) -> f64 {
996 self.magnitude()
997 }
998
999 pub fn hodge_dual(&self) -> Bivector<P, Q, R> {
1002 Bivector {
1003 mv: self.mv.hodge_dual(),
1004 }
1005 }
1006}
1007
1008#[derive(Debug, Clone, PartialEq)]
1010pub struct Bivector<const P: usize, const Q: usize, const R: usize> {
1011 pub mv: Multivector<P, Q, R>,
1012}
1013
1014impl<const P: usize, const Q: usize, const R: usize> Bivector<P, Q, R> {
1015 pub fn from_components(xy: f64, xz: f64, yz: f64) -> Self {
1016 let mut mv = Multivector::zero();
1017 if P + Q + R >= 2 {
1018 mv.set_bivector_component(0, xy);
1019 } if P + Q + R >= 3 {
1021 mv.set_bivector_component(1, xz);
1022 } if P + Q + R >= 3 {
1024 mv.set_bivector_component(2, yz);
1025 } Self { mv }
1027 }
1028
1029 pub fn e12() -> Self {
1030 let mut mv = Multivector::zero();
1031 mv.set_bivector_component(0, 1.0); Self { mv }
1033 }
1034
1035 pub fn e13() -> Self {
1036 let mut mv = Multivector::zero();
1037 mv.set_bivector_component(1, 1.0); Self { mv }
1039 }
1040
1041 pub fn e23() -> Self {
1042 let mut mv = Multivector::zero();
1043 mv.set_bivector_component(2, 1.0); Self { mv }
1045 }
1046
1047 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
1048 Self {
1049 mv: mv.grade_projection(2),
1050 }
1051 }
1052
1053 pub fn geometric_product(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1054 self.mv.geometric_product(&other.mv)
1055 }
1056
1057 pub fn geometric_product_with_bivector(&self, other: &Self) -> Multivector<P, Q, R> {
1059 self.mv.geometric_product(&other.mv)
1060 }
1061
1062 pub fn magnitude(&self) -> f64 {
1063 self.mv.magnitude()
1064 }
1065
1066 pub fn get(&self, index: usize) -> f64 {
1068 match index {
1069 0 => self.mv.get(3), 1 => self.mv.get(5), 2 => self.mv.get(6), _ => 0.0,
1073 }
1074 }
1075
1076 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
1078 self.mv.inner_product(&other.mv)
1079 }
1080
1081 pub fn inner_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1083 self.mv.inner_product(&other.mv)
1084 }
1085
1086 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
1088 self.mv.outer_product(&other.mv)
1089 }
1090
1091 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1093 self.mv.outer_product(&other.mv)
1094 }
1095
1096 pub fn right_contraction(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1098 let product = self.mv.geometric_product(&other.mv);
1100 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
1101 product.grade_projection(target_grade)
1102 }
1103}
1104
1105impl<const P: usize, const Q: usize, const R: usize> core::ops::Index<usize> for Bivector<P, Q, R> {
1106 type Output = f64;
1107
1108 fn index(&self, index: usize) -> &Self::Output {
1109 match index {
1110 0 => &self.mv.coefficients[3], 1 => &self.mv.coefficients[5], 2 => &self.mv.coefficients[6], _ => panic!("Bivector index out of range: {}", index),
1114 }
1115 }
1116}
1117
1118pub type E<const P: usize, const Q: usize, const R: usize> = Vector<P, Q, R>;
1120
1121pub use rotor::Rotor;
1123
1124#[cfg(test)]
1125mod tests {
1126 use super::*;
1127 use approx::assert_relative_eq;
1128
1129 type Cl3 = Multivector<3, 0, 0>; #[test]
1132 fn test_basis_vectors() {
1133 let e1 = Cl3::basis_vector(0);
1134 let e2 = Cl3::basis_vector(1);
1135
1136 let e1_squared = e1.geometric_product(&e1);
1138 assert_eq!(e1_squared.scalar_part(), 1.0);
1139
1140 let e12 = e1.geometric_product(&e2);
1142 let e21 = e2.geometric_product(&e1);
1143 assert_eq!(e12.coefficients[3], -e21.coefficients[3]); }
1145
1146 #[test]
1147 fn test_wedge_product() {
1148 let e1 = Cl3::basis_vector(0);
1149 let e2 = Cl3::basis_vector(1);
1150
1151 let e12 = e1.outer_product(&e2);
1152 assert!(e12.get(3).abs() - 1.0 < 1e-10); let e11 = e1.outer_product(&e1);
1156 assert!(e11.is_zero());
1157 }
1158
1159 #[test]
1160 fn test_rotor_from_bivector() {
1161 let e1 = Cl3::basis_vector(0);
1162 let e2 = Cl3::basis_vector(1);
1163 let e12 = e1.outer_product(&e2);
1164
1165 let angle = core::f64::consts::PI / 4.0; let bivector = e12 * angle;
1168 let rotor = bivector.exp();
1169
1170 assert!((rotor.norm() - 1.0).abs() < 1e-10);
1172 }
1173
1174 #[test]
1175 fn test_algebraic_identities() {
1176 let e1 = Cl3::basis_vector(0);
1177 let e2 = Cl3::basis_vector(1);
1178 let e3 = Cl3::basis_vector(2);
1179
1180 let a = e1.clone() + e2.clone() * 2.0;
1182 let b = e2.clone() + e3.clone() * 3.0;
1183 let c = e3.clone() + e1.clone() * 0.5;
1184
1185 let left = a.geometric_product(&b).geometric_product(&c);
1186 let right = a.geometric_product(&b.geometric_product(&c));
1187 assert_relative_eq!(left.norm(), right.norm(), epsilon = 1e-12);
1188
1189 let ab_plus_ac = a.geometric_product(&b) + a.geometric_product(&c);
1191 let a_times_b_plus_c = a.geometric_product(&(b.clone() + c.clone()));
1192 assert!((ab_plus_ac - a_times_b_plus_c).norm() < 1e-12);
1193
1194 let ab_reverse = a.geometric_product(&b).reverse();
1196 let b_reverse_a_reverse = b.reverse().geometric_product(&a.reverse());
1197 assert!((ab_reverse - b_reverse_a_reverse).norm() < 1e-12);
1198 }
1199
1200 #[test]
1201 fn test_metric_signature() {
1202 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);
1210 assert_eq!(e1.geometric_product(&e1).scalar_part(), -1.0);
1211 }
1212
1213 #[test]
1214 fn test_grade_operations() {
1215 let e1 = Cl3::basis_vector(0);
1216 let e2 = Cl3::basis_vector(1);
1217 let scalar = Cl3::scalar(2.0);
1218
1219 let mv = scalar + e1.clone() * 3.0 + e2.clone() * 4.0 + e1.outer_product(&e2) * 5.0;
1220
1221 let grade0 = mv.grade_projection(0);
1223 let grade1 = mv.grade_projection(1);
1224 let grade2 = mv.grade_projection(2);
1225
1226 assert_eq!(grade0.scalar_part(), 2.0);
1227 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;
1233 assert!((mv - reconstructed).norm() < 1e-12);
1234 }
1235
1236 #[test]
1237 fn test_inner_and_outer_products() {
1238 let e1 = Cl3::basis_vector(0);
1239 let e2 = Cl3::basis_vector(1);
1240 let e3 = Cl3::basis_vector(2);
1241
1242 assert!(e1.inner_product(&e2).norm() < 1e-12);
1244
1245 let v1 = e1.clone() + e2.clone();
1247 let v2 = e1.clone() * 2.0 + e2.clone() * 2.0;
1248 let inner = v1.inner_product(&v2);
1249 assert_relative_eq!(inner.scalar_part(), 4.0, epsilon = 1e-12);
1250
1251 let bivector = e1.outer_product(&e2);
1253 let trivector = bivector.outer_product(&e3);
1254 assert_eq!(trivector.get(7), 1.0); }
1256}