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
24#[allow(dead_code)]
25pub(crate) mod aligned_alloc;
26pub mod basis;
27pub mod cayley;
28pub mod error;
29pub mod generic;
30pub mod precision;
31pub mod rotor;
32pub mod unicode_ops;
33
34#[cfg(feature = "gf2")]
35pub mod gf2;
36
37#[allow(dead_code)]
38#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
39pub(crate) mod simd;
40
41pub use error::{CoreError, CoreResult};
43
44pub use precision::{PrecisionFloat, StandardFloat};
46
47#[cfg(any(
48 feature = "high-precision",
49 feature = "wasm-precision",
50 feature = "native-precision"
51))]
52pub use precision::ExtendedFloat;
53
54#[cfg(any(
56 feature = "high-precision",
57 feature = "wasm-precision",
58 feature = "native-precision"
59))]
60pub use precision::ExtendedFloat as HighPrecisionFloat;
61
62#[cfg(feature = "phantom-types")]
63pub mod verified;
64
65#[cfg(feature = "formal-verification")]
66pub mod verified_contracts;
67
68#[cfg(test)]
69pub mod property_tests;
70
71#[cfg(test)]
72pub mod comprehensive_tests;
73
74pub use cayley::CayleyTable;
75
76#[derive(Debug, Clone, PartialEq)]
83#[repr(C, align(32))] pub struct Multivector<const P: usize, const Q: usize, const R: usize> {
85 coefficients: Box<[f64]>,
88}
89
90impl<const P: usize, const Q: usize, const R: usize> Multivector<P, Q, R> {
91 pub const DIM: usize = P + Q + R;
93
94 pub const BASIS_COUNT: usize = 1 << Self::DIM;
96
97 #[inline(always)]
99 pub fn zero() -> Self {
100 Self {
101 coefficients: vec![0.0; Self::BASIS_COUNT].into_boxed_slice(),
102 }
103 }
104
105 #[inline(always)]
107 pub fn scalar(value: f64) -> Self {
108 let mut mv = Self::zero();
109 mv.coefficients[0] = value;
110 mv
111 }
112
113 pub fn from_vector(vector: &Vector<P, Q, R>) -> Self {
115 vector.mv.clone()
116 }
117
118 pub fn from_bivector(bivector: &Bivector<P, Q, R>) -> Self {
120 bivector.mv.clone()
121 }
122
123 #[inline(always)]
125 pub fn basis_vector(i: usize) -> Self {
126 assert!(i < Self::DIM, "Basis vector index out of range");
127 let mut mv = Self::zero();
128 mv.coefficients[1 << i] = 1.0;
129 mv
130 }
131
132 #[inline(always)]
134 pub fn from_coefficients(coefficients: Vec<f64>) -> Self {
135 assert_eq!(
136 coefficients.len(),
137 Self::BASIS_COUNT,
138 "Coefficient array must have {} elements",
139 Self::BASIS_COUNT
140 );
141 Self {
142 coefficients: coefficients.into_boxed_slice(),
143 }
144 }
145
146 #[inline(always)]
148 pub fn from_slice(coefficients: &[f64]) -> Self {
149 Self::from_coefficients(coefficients.to_vec())
150 }
151
152 #[inline(always)]
154 pub fn get(&self, index: usize) -> f64 {
155 self.coefficients.get(index).copied().unwrap_or(0.0)
156 }
157
158 #[inline(always)]
160 pub fn set(&mut self, index: usize, value: f64) {
161 if index < self.coefficients.len() {
162 self.coefficients[index] = value;
163 }
164 }
165
166 #[inline(always)]
168 pub fn scalar_part(&self) -> f64 {
169 self.coefficients[0]
170 }
171
172 #[inline(always)]
174 pub fn set_scalar(&mut self, value: f64) {
175 self.coefficients[0] = value;
176 }
177
178 pub fn vector_part(&self) -> Vector<P, Q, R> {
180 Vector::from_multivector(&self.grade_projection(1))
181 }
182
183 pub fn bivector_part(&self) -> Self {
185 self.grade_projection(2)
186 }
187
188 pub fn bivector_type(&self) -> Bivector<P, Q, R> {
190 Bivector::from_multivector(&self.grade_projection(2))
191 }
192
193 pub fn trivector_part(&self) -> f64 {
195 if Self::DIM >= 3 {
196 self.get(7) } else {
198 0.0
199 }
200 }
201
202 pub fn set_vector_component(&mut self, index: usize, value: f64) {
204 if index < Self::DIM {
205 self.coefficients[1 << index] = value;
206 }
207 }
208
209 pub fn set_bivector_component(&mut self, index: usize, value: f64) {
211 let bivector_indices = match Self::DIM {
213 3 => [3, 5, 6], _ => [3, 5, 6], };
216 if index < bivector_indices.len() {
217 self.coefficients[bivector_indices[index]] = value;
218 }
219 }
220
221 pub fn vector_component(&self, index: usize) -> f64 {
223 if index < Self::DIM {
224 self.get(1 << index)
225 } else {
226 0.0
227 }
228 }
229
230 pub fn as_slice(&self) -> &[f64] {
232 &self.coefficients
233 }
234
235 pub fn to_vec(&self) -> Vec<f64> {
239 self.coefficients.to_vec()
240 }
241
242 pub fn grade_project(&self, grade: usize) -> Self {
246 self.grade_projection(grade)
247 }
248
249 pub fn wedge(&self, other: &Self) -> Self {
254 self.outer_product(other)
255 }
256
257 pub fn dot(&self, other: &Self) -> Self {
262 self.inner_product(other)
263 }
264
265 pub fn add(&self, other: &Self) -> Self {
267 self + other
268 }
269
270 pub fn grade(&self) -> usize {
272 for grade in (0..=Self::DIM).rev() {
273 let projection = self.grade_projection(grade);
274 if !projection.is_zero() {
275 return grade;
276 }
277 }
278 0 }
280
281 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Self {
283 self.outer_product(&other.mv)
284 }
285
286 pub fn geometric_product(&self, rhs: &Self) -> Self {
291 self.geometric_product_scalar(rhs)
292 }
293
294 pub(crate) fn geometric_product_scalar(&self, rhs: &Self) -> Self {
296 let table = CayleyTable::<P, Q, R>::get();
297 let mut result = Self::zero();
298
299 for i in 0..Self::BASIS_COUNT {
300 if self.coefficients[i].abs() < 1e-14 {
301 continue;
302 }
303
304 for j in 0..Self::BASIS_COUNT {
305 if rhs.coefficients[j].abs() < 1e-14 {
306 continue;
307 }
308
309 let (sign, index) = table.get_product(i, j);
310 result.coefficients[index] += sign * self.coefficients[i] * rhs.coefficients[j];
311 }
312 }
313
314 result
315 }
316
317 pub fn inner_product(&self, rhs: &Self) -> Self {
319 let self_grades = self.grade_decomposition();
320 let rhs_grades = rhs.grade_decomposition();
321 let mut result = Self::zero();
322
323 for (grade_a, mv_a) in self_grades.iter().enumerate() {
325 for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
326 if !mv_a.is_zero() && !mv_b.is_zero() {
327 let target_grade = grade_a.abs_diff(grade_b);
328 let product = mv_a.geometric_product(mv_b);
329 let projected = product.grade_projection(target_grade);
330 result = result + projected;
331 }
332 }
333 }
334
335 result
336 }
337
338 pub fn outer_product(&self, rhs: &Self) -> Self {
340 let self_grades = self.grade_decomposition();
341 let rhs_grades = rhs.grade_decomposition();
342 let mut result = Self::zero();
343
344 for (grade_a, mv_a) in self_grades.iter().enumerate() {
346 for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
347 if !mv_a.is_zero() && !mv_b.is_zero() {
348 let target_grade = grade_a + grade_b;
349 if target_grade <= Self::DIM {
350 let product = mv_a.geometric_product(mv_b);
351 let projected = product.grade_projection(target_grade);
352 result = result + projected;
353 }
354 }
355 }
356 }
357
358 result
359 }
360
361 pub fn scalar_product(&self, rhs: &Self) -> f64 {
363 self.geometric_product(rhs).scalar_part()
364 }
365
366 #[inline]
372 fn reverse_sign_for_grade(grade: usize) -> f64 {
373 if grade == 0 {
374 return 1.0;
375 }
376 if (grade * (grade - 1) / 2).is_multiple_of(2) {
377 1.0
378 } else {
379 -1.0
380 }
381 }
382
383 pub fn reverse(&self) -> Self {
385 let mut result = Self::zero();
386
387 for i in 0..Self::BASIS_COUNT {
388 let grade = i.count_ones() as usize;
389 let sign = Self::reverse_sign_for_grade(grade);
390 result.coefficients[i] = sign * self.coefficients[i];
391 }
392
393 result
394 }
395
396 pub fn grade_projection(&self, grade: usize) -> Self {
398 let mut result = Self::zero();
399
400 for i in 0..Self::BASIS_COUNT {
401 if i.count_ones() as usize == grade {
402 result.coefficients[i] = self.coefficients[i];
403 }
404 }
405
406 result
407 }
408
409 pub fn grade_magnitude(&self, grade: usize) -> f64 {
428 self.grade_projection(grade).magnitude()
429 }
430
431 pub fn grade_spectrum(&self) -> Vec<f64> {
436 (0..=Self::DIM).map(|g| self.grade_magnitude(g)).collect()
437 }
438
439 fn grade_decomposition(&self) -> Vec<Self> {
441 let mut grades = Vec::with_capacity(Self::DIM + 1);
442 for _ in 0..=Self::DIM {
443 grades.push(Self::zero());
444 }
445
446 for i in 0..Self::BASIS_COUNT {
447 let grade = i.count_ones() as usize;
448 grades[grade].coefficients[i] = self.coefficients[i];
449 }
450
451 grades
452 }
453
454 pub fn is_zero(&self) -> bool {
456 self.coefficients.iter().all(|&c| c.abs() < 1e-14)
457 }
458
459 pub fn norm_squared(&self) -> f64 {
461 self.scalar_product(&self.reverse())
462 }
463
464 pub fn magnitude(&self) -> f64 {
481 self.norm_squared().abs().sqrt()
482 }
483
484 pub fn norm(&self) -> f64 {
489 self.magnitude()
490 }
491
492 pub fn abs(&self) -> f64 {
494 self.magnitude()
495 }
496
497 pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool {
499 (self.clone() - other.clone()).magnitude() < epsilon
500 }
501
502 pub fn normalize(&self) -> Option<Self> {
504 let norm = self.norm();
505 if norm > 1e-14 {
506 Some(self * (1.0 / norm))
507 } else {
508 None
509 }
510 }
511
512 pub fn inverse(&self) -> Option<Self> {
514 let rev = self.reverse();
515 let norm_sq = self.scalar_product(&rev);
516
517 if norm_sq.abs() > 1e-14 {
518 Some(rev * (1.0 / norm_sq))
519 } else {
520 None
521 }
522 }
523
524 pub fn exp(&self) -> Self {
529 let grade2 = self.grade_projection(2);
531 if (self - &grade2).norm() > 1e-10 {
532 return self.exp_series();
534 }
535
536 let b_squared = grade2.geometric_product(&grade2).scalar_part();
538
539 if b_squared > -1e-14 {
540 let norm = b_squared.abs().sqrt();
542 if norm < 1e-14 {
543 return Self::scalar(1.0);
544 }
545 let cosh_norm = norm.cosh();
546 let sinh_norm = norm.sinh();
547 Self::scalar(cosh_norm) + grade2 * (sinh_norm / norm)
548 } else {
549 let norm = (-b_squared).sqrt();
551 let cos_norm = norm.cos();
552 let sin_norm = norm.sin();
553 Self::scalar(cos_norm) + grade2 * (sin_norm / norm)
554 }
555 }
556
557 fn exp_series(&self) -> Self {
559 let mut result = Self::scalar(1.0);
560 let mut term = Self::scalar(1.0);
561
562 for n in 1..20 {
563 term = term.geometric_product(self) * (1.0 / n as f64);
564 let old_result = result.clone();
565 result = result + term.clone();
566
567 if (result.clone() - old_result).norm() < 1e-14 {
569 break;
570 }
571 }
572
573 result
574 }
575
576 pub fn left_contraction(&self, other: &Self) -> Self {
581 let self_grades = self.grade_decomposition();
582 let other_grades = other.grade_decomposition();
583 let mut result = Self::zero();
584
585 for (a_grade, mv_a) in self_grades.iter().enumerate() {
586 if mv_a.is_zero() {
587 continue;
588 }
589
590 for (b_grade, mv_b) in other_grades.iter().enumerate() {
591 if mv_b.is_zero() {
592 continue;
593 }
594
595 if b_grade >= a_grade {
597 let target_grade = b_grade - a_grade;
598 let product = mv_a.geometric_product(mv_b);
599 let projected = product.grade_projection(target_grade);
600 result = result + projected;
601 }
602 }
603 }
604
605 result
606 }
607
608 pub fn right_contraction(&self, other: &Self) -> Self {
613 let self_grades = self.grade_decomposition();
614 let other_grades = other.grade_decomposition();
615 let mut result = Self::zero();
616
617 for (a_grade, mv_a) in self_grades.iter().enumerate() {
618 if mv_a.is_zero() {
619 continue;
620 }
621
622 for (b_grade, mv_b) in other_grades.iter().enumerate() {
623 if mv_b.is_zero() {
624 continue;
625 }
626
627 if a_grade >= b_grade {
629 let target_grade = a_grade - b_grade;
630 let product = mv_a.geometric_product(mv_b);
631 let projected = product.grade_projection(target_grade);
632 result = result + projected;
633 }
634 }
635 }
636
637 result
638 }
639
640 pub fn hodge_dual(&self) -> Self {
651 let n = Self::DIM;
652 if n == 0 {
653 return self.clone();
654 }
655
656 let mut result = Self::zero();
657 let pseudoscalar_index = (1 << n) - 1; for i in 0..Self::BASIS_COUNT {
660 if self.coefficients[i].abs() < 1e-14 {
661 continue;
662 }
663
664 let dual_index = i ^ pseudoscalar_index;
665
666 let mut swaps = 0u32;
670 for bit_pos in 0..n {
671 if (i >> bit_pos) & 1 == 1 {
672 let below_mask = (1 << bit_pos) - 1;
674 swaps += (dual_index & below_mask).count_ones();
675 }
676 }
677 let permutation_sign: f64 = if swaps.is_multiple_of(2) { 1.0 } else { -1.0 };
678
679 let mut metric_factor = 1.0;
682 for j in 0..n {
683 if (i >> j) & 1 == 1 {
684 if j >= P + Q {
685 metric_factor = 0.0;
687 break;
688 } else if j >= P {
689 metric_factor *= -1.0;
691 }
692 }
694 }
695
696 let sign = metric_factor * permutation_sign;
698
699 result.coefficients[dual_index] += sign * self.coefficients[i];
700 }
701
702 result
703 }
704}
705
706impl<const P: usize, const Q: usize, const R: usize> Add for Multivector<P, Q, R> {
708 type Output = Self;
709
710 #[inline(always)]
711 fn add(mut self, rhs: Self) -> Self {
712 for i in 0..Self::BASIS_COUNT {
713 self.coefficients[i] += rhs.coefficients[i];
714 }
715 self
716 }
717}
718
719impl<const P: usize, const Q: usize, const R: usize> Add for &Multivector<P, Q, R> {
720 type Output = Multivector<P, Q, R>;
721
722 #[inline(always)]
723 fn add(self, rhs: Self) -> Multivector<P, Q, R> {
724 let mut result = self.clone();
725 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
726 result.coefficients[i] += rhs.coefficients[i];
727 }
728 result
729 }
730}
731
732impl<const P: usize, const Q: usize, const R: usize> Sub for Multivector<P, Q, R> {
733 type Output = Self;
734
735 #[inline(always)]
736 fn sub(mut self, rhs: Self) -> Self {
737 for i in 0..Self::BASIS_COUNT {
738 self.coefficients[i] -= rhs.coefficients[i];
739 }
740 self
741 }
742}
743
744impl<const P: usize, const Q: usize, const R: usize> Sub for &Multivector<P, Q, R> {
745 type Output = Multivector<P, Q, R>;
746
747 #[inline(always)]
748 fn sub(self, rhs: Self) -> Multivector<P, Q, R> {
749 let mut result = self.clone();
750 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
751 result.coefficients[i] -= rhs.coefficients[i];
752 }
753 result
754 }
755}
756
757impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for Multivector<P, Q, R> {
758 type Output = Self;
759
760 #[inline(always)]
761 fn mul(mut self, scalar: f64) -> Self {
762 for i in 0..Self::BASIS_COUNT {
763 self.coefficients[i] *= scalar;
764 }
765 self
766 }
767}
768
769impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for &Multivector<P, Q, R> {
770 type Output = Multivector<P, Q, R>;
771
772 #[inline(always)]
773 fn mul(self, scalar: f64) -> Multivector<P, Q, R> {
774 let mut result = self.clone();
775 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
776 result.coefficients[i] *= scalar;
777 }
778 result
779 }
780}
781
782impl<const P: usize, const Q: usize, const R: usize> Neg for Multivector<P, Q, R> {
783 type Output = Self;
784
785 #[inline(always)]
786 fn neg(mut self) -> Self {
787 for i in 0..Self::BASIS_COUNT {
788 self.coefficients[i] = -self.coefficients[i];
789 }
790 self
791 }
792}
793
794impl<const P: usize, const Q: usize, const R: usize> Zero for Multivector<P, Q, R> {
795 fn zero() -> Self {
796 Self::zero()
797 }
798
799 fn is_zero(&self) -> bool {
800 self.is_zero()
801 }
802}
803
804#[derive(Debug, Clone, PartialEq)]
806pub struct Scalar<const P: usize, const Q: usize, const R: usize> {
807 pub mv: Multivector<P, Q, R>,
808}
809
810impl<const P: usize, const Q: usize, const R: usize> Scalar<P, Q, R> {
811 pub fn from(value: f64) -> Self {
812 Self {
813 mv: Multivector::scalar(value),
814 }
815 }
816
817 pub fn one() -> Self {
818 Self::from(1.0)
819 }
820
821 pub fn geometric_product(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
822 self.mv.geometric_product(other)
823 }
824
825 pub fn geometric_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
826 self.mv.geometric_product(&other.mv)
827 }
828}
829
830impl<const P: usize, const Q: usize, const R: usize> From<f64> for Scalar<P, Q, R> {
831 fn from(value: f64) -> Self {
832 Self::from(value)
833 }
834}
835
836#[derive(Debug, Clone, PartialEq)]
838pub struct Vector<const P: usize, const Q: usize, const R: usize> {
839 pub mv: Multivector<P, Q, R>,
840}
841
842impl<const P: usize, const Q: usize, const R: usize> Vector<P, Q, R> {
843 pub fn zero() -> Self {
845 Self {
846 mv: Multivector::zero(),
847 }
848 }
849
850 pub fn from_components(x: f64, y: f64, z: f64) -> Self {
851 let mut mv = Multivector::zero();
852 if P + Q + R >= 1 {
853 mv.set_vector_component(0, x);
854 }
855 if P + Q + R >= 2 {
856 mv.set_vector_component(1, y);
857 }
858 if P + Q + R >= 3 {
859 mv.set_vector_component(2, z);
860 }
861 Self { mv }
862 }
863
864 pub fn e1() -> Self {
865 Self {
866 mv: Multivector::basis_vector(0),
867 }
868 }
869
870 pub fn e2() -> Self {
871 Self {
872 mv: Multivector::basis_vector(1),
873 }
874 }
875
876 pub fn e3() -> Self {
877 Self {
878 mv: Multivector::basis_vector(2),
879 }
880 }
881
882 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
883 Self {
884 mv: mv.grade_projection(1),
885 }
886 }
887
888 pub fn geometric_product(&self, other: &Self) -> Multivector<P, Q, R> {
889 self.mv.geometric_product(&other.mv)
890 }
891
892 pub fn geometric_product_with_multivector(
893 &self,
894 other: &Multivector<P, Q, R>,
895 ) -> Multivector<P, Q, R> {
896 self.mv.geometric_product(other)
897 }
898
899 pub fn geometric_product_with_bivector(
900 &self,
901 other: &Bivector<P, Q, R>,
902 ) -> Multivector<P, Q, R> {
903 self.mv.geometric_product(&other.mv)
904 }
905
906 pub fn geometric_product_with_scalar(&self, other: &Scalar<P, Q, R>) -> Multivector<P, Q, R> {
907 self.mv.geometric_product(&other.mv)
908 }
909
910 pub fn add(&self, other: &Self) -> Self {
911 Self {
912 mv: &self.mv + &other.mv,
913 }
914 }
915
916 pub fn magnitude(&self) -> f64 {
917 self.mv.magnitude()
918 }
919
920 pub fn as_slice(&self) -> &[f64] {
921 self.mv.as_slice()
922 }
923
924 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
926 self.mv.inner_product(&other.mv)
927 }
928
929 pub fn inner_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
931 self.mv.inner_product(other)
932 }
933
934 pub fn inner_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
936 self.mv.inner_product(&other.mv)
937 }
938
939 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
941 self.mv.outer_product(&other.mv)
942 }
943
944 pub fn outer_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
946 self.mv.outer_product(other)
947 }
948
949 pub fn outer_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
951 self.mv.outer_product(&other.mv)
952 }
953
954 pub fn left_contraction(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
956 let product = self.mv.geometric_product(&other.mv);
958 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
959 product.grade_projection(target_grade)
960 }
961
962 pub fn normalize(&self) -> Option<Self> {
964 self.mv
965 .normalize()
966 .map(|normalized| Self { mv: normalized })
967 }
968
969 pub fn norm_squared(&self) -> f64 {
971 self.mv.norm_squared()
972 }
973
974 pub fn reverse(&self) -> Self {
976 Self {
977 mv: self.mv.reverse(),
978 }
979 }
980
981 pub fn norm(&self) -> f64 {
986 self.magnitude()
987 }
988
989 pub fn hodge_dual(&self) -> Bivector<P, Q, R> {
992 Bivector {
993 mv: self.mv.hodge_dual(),
994 }
995 }
996}
997
998#[derive(Debug, Clone, PartialEq)]
1000pub struct Bivector<const P: usize, const Q: usize, const R: usize> {
1001 pub mv: Multivector<P, Q, R>,
1002}
1003
1004impl<const P: usize, const Q: usize, const R: usize> Bivector<P, Q, R> {
1005 pub fn from_components(xy: f64, xz: f64, yz: f64) -> Self {
1006 let mut mv = Multivector::zero();
1007 if P + Q + R >= 2 {
1008 mv.set_bivector_component(0, xy);
1009 } if P + Q + R >= 3 {
1011 mv.set_bivector_component(1, xz);
1012 } if P + Q + R >= 3 {
1014 mv.set_bivector_component(2, yz);
1015 } Self { mv }
1017 }
1018
1019 pub fn e12() -> Self {
1020 let mut mv = Multivector::zero();
1021 mv.set_bivector_component(0, 1.0); Self { mv }
1023 }
1024
1025 pub fn e13() -> Self {
1026 let mut mv = Multivector::zero();
1027 mv.set_bivector_component(1, 1.0); Self { mv }
1029 }
1030
1031 pub fn e23() -> Self {
1032 let mut mv = Multivector::zero();
1033 mv.set_bivector_component(2, 1.0); Self { mv }
1035 }
1036
1037 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
1038 Self {
1039 mv: mv.grade_projection(2),
1040 }
1041 }
1042
1043 pub fn geometric_product(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1044 self.mv.geometric_product(&other.mv)
1045 }
1046
1047 pub fn geometric_product_with_bivector(&self, other: &Self) -> Multivector<P, Q, R> {
1049 self.mv.geometric_product(&other.mv)
1050 }
1051
1052 pub fn magnitude(&self) -> f64 {
1053 self.mv.magnitude()
1054 }
1055
1056 pub fn get(&self, index: usize) -> f64 {
1058 match index {
1059 0 => self.mv.get(3), 1 => self.mv.get(5), 2 => self.mv.get(6), _ => 0.0,
1063 }
1064 }
1065
1066 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
1068 self.mv.inner_product(&other.mv)
1069 }
1070
1071 pub fn inner_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1073 self.mv.inner_product(&other.mv)
1074 }
1075
1076 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
1078 self.mv.outer_product(&other.mv)
1079 }
1080
1081 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1083 self.mv.outer_product(&other.mv)
1084 }
1085
1086 pub fn right_contraction(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1088 let product = self.mv.geometric_product(&other.mv);
1090 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
1091 product.grade_projection(target_grade)
1092 }
1093}
1094
1095impl<const P: usize, const Q: usize, const R: usize> core::ops::Index<usize> for Bivector<P, Q, R> {
1096 type Output = f64;
1097
1098 fn index(&self, index: usize) -> &Self::Output {
1099 match index {
1100 0 => &self.mv.coefficients[3], 1 => &self.mv.coefficients[5], 2 => &self.mv.coefficients[6], _ => panic!("Bivector index out of range: {}", index),
1104 }
1105 }
1106}
1107
1108pub type E<const P: usize, const Q: usize, const R: usize> = Vector<P, Q, R>;
1110
1111pub use rotor::Rotor;
1113
1114#[cfg(test)]
1115mod tests {
1116 use super::*;
1117 use approx::assert_relative_eq;
1118
1119 type Cl3 = Multivector<3, 0, 0>; #[test]
1122 fn test_basis_vectors() {
1123 let e1 = Cl3::basis_vector(0);
1124 let e2 = Cl3::basis_vector(1);
1125
1126 let e1_squared = e1.geometric_product(&e1);
1128 assert_eq!(e1_squared.scalar_part(), 1.0);
1129
1130 let e12 = e1.geometric_product(&e2);
1132 let e21 = e2.geometric_product(&e1);
1133 assert_eq!(e12.coefficients[3], -e21.coefficients[3]); }
1135
1136 #[test]
1137 fn test_wedge_product() {
1138 let e1 = Cl3::basis_vector(0);
1139 let e2 = Cl3::basis_vector(1);
1140
1141 let e12 = e1.outer_product(&e2);
1142 assert!(e12.get(3).abs() - 1.0 < 1e-10); let e11 = e1.outer_product(&e1);
1146 assert!(e11.is_zero());
1147 }
1148
1149 #[test]
1150 fn test_rotor_from_bivector() {
1151 let e1 = Cl3::basis_vector(0);
1152 let e2 = Cl3::basis_vector(1);
1153 let e12 = e1.outer_product(&e2);
1154
1155 let angle = core::f64::consts::PI / 4.0; let bivector = e12 * angle;
1158 let rotor = bivector.exp();
1159
1160 assert!((rotor.norm() - 1.0).abs() < 1e-10);
1162 }
1163
1164 #[test]
1165 fn test_algebraic_identities() {
1166 let e1 = Cl3::basis_vector(0);
1167 let e2 = Cl3::basis_vector(1);
1168 let e3 = Cl3::basis_vector(2);
1169
1170 let a = e1.clone() + e2.clone() * 2.0;
1172 let b = e2.clone() + e3.clone() * 3.0;
1173 let c = e3.clone() + e1.clone() * 0.5;
1174
1175 let left = a.geometric_product(&b).geometric_product(&c);
1176 let right = a.geometric_product(&b.geometric_product(&c));
1177 assert_relative_eq!(left.norm(), right.norm(), epsilon = 1e-12);
1178
1179 let ab_plus_ac = a.geometric_product(&b) + a.geometric_product(&c);
1181 let a_times_b_plus_c = a.geometric_product(&(b.clone() + c.clone()));
1182 assert!((ab_plus_ac - a_times_b_plus_c).norm() < 1e-12);
1183
1184 let ab_reverse = a.geometric_product(&b).reverse();
1186 let b_reverse_a_reverse = b.reverse().geometric_product(&a.reverse());
1187 assert!((ab_reverse - b_reverse_a_reverse).norm() < 1e-12);
1188 }
1189
1190 #[test]
1191 fn test_metric_signature() {
1192 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);
1200 assert_eq!(e1.geometric_product(&e1).scalar_part(), -1.0);
1201 }
1202
1203 #[test]
1204 fn test_grade_operations() {
1205 let e1 = Cl3::basis_vector(0);
1206 let e2 = Cl3::basis_vector(1);
1207 let scalar = Cl3::scalar(2.0);
1208
1209 let mv = scalar + e1.clone() * 3.0 + e2.clone() * 4.0 + e1.outer_product(&e2) * 5.0;
1210
1211 let grade0 = mv.grade_projection(0);
1213 let grade1 = mv.grade_projection(1);
1214 let grade2 = mv.grade_projection(2);
1215
1216 assert_eq!(grade0.scalar_part(), 2.0);
1217 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;
1223 assert!((mv - reconstructed).norm() < 1e-12);
1224 }
1225
1226 #[test]
1227 fn test_inner_and_outer_products() {
1228 let e1 = Cl3::basis_vector(0);
1229 let e2 = Cl3::basis_vector(1);
1230 let e3 = Cl3::basis_vector(2);
1231
1232 assert!(e1.inner_product(&e2).norm() < 1e-12);
1234
1235 let v1 = e1.clone() + e2.clone();
1237 let v2 = e1.clone() * 2.0 + e2.clone() * 2.0;
1238 let inner = v1.inner_product(&v2);
1239 assert_relative_eq!(inner.scalar_part(), 4.0, epsilon = 1e-12);
1240
1241 let bivector = e1.outer_product(&e2);
1243 let trivector = bivector.outer_product(&e3);
1244 assert_eq!(trivector.get(7), 1.0); }
1246}