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