1#![cfg_attr(not(feature = "std"), no_std)]
16
17extern crate alloc;
18use alloc::boxed::Box;
19use alloc::vec::Vec;
20use core::ops::{Add, Mul, Neg, Sub};
21use num_traits::Zero;
22
23pub mod aligned_alloc;
24pub mod basis;
25pub mod cayley;
26pub mod error;
27pub mod precision;
28pub mod rotor;
29pub mod unicode_ops;
30
31#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
32pub mod simd;
33
34pub use error::{CoreError, CoreResult};
36
37pub use precision::{ExtendedFloat, PrecisionFloat, StandardFloat};
39
40#[cfg(feature = "high-precision")]
41pub use precision::HighPrecisionFloat;
42
43#[cfg(feature = "phantom-types")]
44pub mod verified;
45
46#[cfg(feature = "formal-verification")]
47pub mod verified_contracts;
48
49#[cfg(test)]
50pub mod property_tests;
51
52#[cfg(test)]
53pub mod comprehensive_tests;
54
55pub use cayley::CayleyTable;
56
57#[derive(Debug, Clone, PartialEq)]
64#[repr(C, align(32))] pub struct Multivector<const P: usize, const Q: usize, const R: usize> {
66 coefficients: Box<[f64]>,
69}
70
71impl<const P: usize, const Q: usize, const R: usize> Multivector<P, Q, R> {
72 pub const DIM: usize = P + Q + R;
74
75 pub const BASIS_COUNT: usize = 1 << Self::DIM;
77
78 #[inline(always)]
80 pub fn zero() -> Self {
81 Self {
82 coefficients: vec![0.0; Self::BASIS_COUNT].into_boxed_slice(),
83 }
84 }
85
86 #[inline(always)]
88 pub fn scalar(value: f64) -> Self {
89 let mut mv = Self::zero();
90 mv.coefficients[0] = value;
91 mv
92 }
93
94 pub fn from_vector(vector: &Vector<P, Q, R>) -> Self {
96 vector.mv.clone()
97 }
98
99 pub fn from_bivector(bivector: &Bivector<P, Q, R>) -> Self {
101 bivector.mv.clone()
102 }
103
104 #[inline(always)]
106 pub fn basis_vector(i: usize) -> Self {
107 assert!(i < Self::DIM, "Basis vector index out of range");
108 let mut mv = Self::zero();
109 mv.coefficients[1 << i] = 1.0;
110 mv
111 }
112
113 #[inline(always)]
115 pub fn from_coefficients(coefficients: Vec<f64>) -> Self {
116 assert_eq!(
117 coefficients.len(),
118 Self::BASIS_COUNT,
119 "Coefficient array must have {} elements",
120 Self::BASIS_COUNT
121 );
122 Self {
123 coefficients: coefficients.into_boxed_slice(),
124 }
125 }
126
127 #[inline(always)]
129 pub fn from_slice(coefficients: &[f64]) -> Self {
130 Self::from_coefficients(coefficients.to_vec())
131 }
132
133 #[inline(always)]
135 pub fn get(&self, index: usize) -> f64 {
136 self.coefficients.get(index).copied().unwrap_or(0.0)
137 }
138
139 #[inline(always)]
141 pub fn set(&mut self, index: usize, value: f64) {
142 if index < self.coefficients.len() {
143 self.coefficients[index] = value;
144 }
145 }
146
147 #[inline(always)]
149 pub fn scalar_part(&self) -> f64 {
150 self.coefficients[0]
151 }
152
153 #[inline(always)]
155 pub fn set_scalar(&mut self, value: f64) {
156 self.coefficients[0] = value;
157 }
158
159 pub fn vector_part(&self) -> Vector<P, Q, R> {
161 Vector::from_multivector(&self.grade_projection(1))
162 }
163
164 pub fn bivector_part(&self) -> Self {
166 self.grade_projection(2)
167 }
168
169 pub fn bivector_type(&self) -> Bivector<P, Q, R> {
171 Bivector::from_multivector(&self.grade_projection(2))
172 }
173
174 pub fn trivector_part(&self) -> f64 {
176 if Self::DIM >= 3 {
177 self.get(7) } else {
179 0.0
180 }
181 }
182
183 pub fn set_vector_component(&mut self, index: usize, value: f64) {
185 if index < Self::DIM {
186 self.coefficients[1 << index] = value;
187 }
188 }
189
190 pub fn set_bivector_component(&mut self, index: usize, value: f64) {
192 let bivector_indices = match Self::DIM {
194 3 => [3, 5, 6], _ => [3, 5, 6], };
197 if index < bivector_indices.len() {
198 self.coefficients[bivector_indices[index]] = value;
199 }
200 }
201
202 pub fn vector_component(&self, index: usize) -> f64 {
204 if index < Self::DIM {
205 self.get(1 << index)
206 } else {
207 0.0
208 }
209 }
210
211 pub fn as_slice(&self) -> &[f64] {
213 &self.coefficients
214 }
215
216 pub fn add(&self, other: &Self) -> Self {
218 self + other
219 }
220
221 pub fn grade(&self) -> usize {
223 for grade in (0..=Self::DIM).rev() {
224 let projection = self.grade_projection(grade);
225 if !projection.is_zero() {
226 return grade;
227 }
228 }
229 0 }
231
232 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Self {
234 self.outer_product(&other.mv)
235 }
236
237 pub fn geometric_product(&self, rhs: &Self) -> Self {
242 self.geometric_product_scalar(rhs)
253 }
254
255 pub fn geometric_product_scalar(&self, rhs: &Self) -> Self {
257 let table = CayleyTable::<P, Q, R>::get();
258 let mut result = Self::zero();
259
260 for i in 0..Self::BASIS_COUNT {
261 if self.coefficients[i].abs() < 1e-14 {
262 continue;
263 }
264
265 for j in 0..Self::BASIS_COUNT {
266 if rhs.coefficients[j].abs() < 1e-14 {
267 continue;
268 }
269
270 let (sign, index) = table.get_product(i, j);
271 result.coefficients[index] += sign * self.coefficients[i] * rhs.coefficients[j];
272 }
273 }
274
275 result
276 }
277
278 pub fn inner_product(&self, rhs: &Self) -> Self {
280 let self_grades = self.grade_decomposition();
281 let rhs_grades = rhs.grade_decomposition();
282 let mut result = Self::zero();
283
284 for (grade_a, mv_a) in self_grades.iter().enumerate() {
286 for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
287 if !mv_a.is_zero() && !mv_b.is_zero() {
288 let target_grade = grade_a.abs_diff(grade_b);
289 let product = mv_a.geometric_product(mv_b);
290 let projected = product.grade_projection(target_grade);
291 result = result + projected;
292 }
293 }
294 }
295
296 result
297 }
298
299 pub fn outer_product(&self, rhs: &Self) -> Self {
301 let self_grades = self.grade_decomposition();
302 let rhs_grades = rhs.grade_decomposition();
303 let mut result = Self::zero();
304
305 for (grade_a, mv_a) in self_grades.iter().enumerate() {
307 for (grade_b, mv_b) in rhs_grades.iter().enumerate() {
308 if !mv_a.is_zero() && !mv_b.is_zero() {
309 let target_grade = grade_a + grade_b;
310 if target_grade <= Self::DIM {
311 let product = mv_a.geometric_product(mv_b);
312 let projected = product.grade_projection(target_grade);
313 result = result + projected;
314 }
315 }
316 }
317 }
318
319 result
320 }
321
322 pub fn scalar_product(&self, rhs: &Self) -> f64 {
324 self.geometric_product(rhs).scalar_part()
325 }
326
327 #[inline]
333 fn reverse_sign_for_grade(grade: usize) -> f64 {
334 if grade == 0 {
335 return 1.0;
336 }
337 if (grade * (grade - 1) / 2).is_multiple_of(2) {
338 1.0
339 } else {
340 -1.0
341 }
342 }
343
344 pub fn reverse(&self) -> Self {
346 let mut result = Self::zero();
347
348 for i in 0..Self::BASIS_COUNT {
349 let grade = i.count_ones() as usize;
350 let sign = Self::reverse_sign_for_grade(grade);
351 result.coefficients[i] = sign * self.coefficients[i];
352 }
353
354 result
355 }
356
357 pub fn grade_projection(&self, grade: usize) -> Self {
359 let mut result = Self::zero();
360
361 for i in 0..Self::BASIS_COUNT {
362 if i.count_ones() as usize == grade {
363 result.coefficients[i] = self.coefficients[i];
364 }
365 }
366
367 result
368 }
369
370 fn grade_decomposition(&self) -> Vec<Self> {
372 let mut grades = Vec::with_capacity(Self::DIM + 1);
373 for _ in 0..=Self::DIM {
374 grades.push(Self::zero());
375 }
376
377 for i in 0..Self::BASIS_COUNT {
378 let grade = i.count_ones() as usize;
379 grades[grade].coefficients[i] = self.coefficients[i];
380 }
381
382 grades
383 }
384
385 pub fn is_zero(&self) -> bool {
387 self.coefficients.iter().all(|&c| c.abs() < 1e-14)
388 }
389
390 pub fn norm_squared(&self) -> f64 {
392 self.scalar_product(&self.reverse())
393 }
394
395 pub fn magnitude(&self) -> f64 {
412 self.norm_squared().abs().sqrt()
413 }
414
415 pub fn norm(&self) -> f64 {
420 self.magnitude()
421 }
422
423 pub fn abs(&self) -> f64 {
425 self.magnitude()
426 }
427
428 pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool {
430 (self.clone() - other.clone()).magnitude() < epsilon
431 }
432
433 pub fn normalize(&self) -> Option<Self> {
435 let norm = self.norm();
436 if norm > 1e-14 {
437 Some(self * (1.0 / norm))
438 } else {
439 None
440 }
441 }
442
443 pub fn inverse(&self) -> Option<Self> {
445 let rev = self.reverse();
446 let norm_sq = self.scalar_product(&rev);
447
448 if norm_sq.abs() > 1e-14 {
449 Some(rev * (1.0 / norm_sq))
450 } else {
451 None
452 }
453 }
454
455 pub fn exp(&self) -> Self {
460 let grade2 = self.grade_projection(2);
462 if (self - &grade2).norm() > 1e-10 {
463 return self.exp_series();
465 }
466
467 let b_squared = grade2.geometric_product(&grade2).scalar_part();
469
470 if b_squared > -1e-14 {
471 let norm = b_squared.abs().sqrt();
473 if norm < 1e-14 {
474 return Self::scalar(1.0);
475 }
476 let cosh_norm = norm.cosh();
477 let sinh_norm = norm.sinh();
478 Self::scalar(cosh_norm) + grade2 * (sinh_norm / norm)
479 } else {
480 let norm = (-b_squared).sqrt();
482 let cos_norm = norm.cos();
483 let sin_norm = norm.sin();
484 Self::scalar(cos_norm) + grade2 * (sin_norm / norm)
485 }
486 }
487
488 fn exp_series(&self) -> Self {
490 let mut result = Self::scalar(1.0);
491 let mut term = Self::scalar(1.0);
492
493 for n in 1..20 {
494 term = term.geometric_product(self) * (1.0 / n as f64);
495 let old_result = result.clone();
496 result = result + term.clone();
497
498 if (result.clone() - old_result).norm() < 1e-14 {
500 break;
501 }
502 }
503
504 result
505 }
506
507 pub fn left_contraction(&self, other: &Self) -> Self {
512 let self_grades = self.grade_decomposition();
513 let other_grades = other.grade_decomposition();
514 let mut result = Self::zero();
515
516 for (a_grade, mv_a) in self_grades.iter().enumerate() {
517 if mv_a.is_zero() {
518 continue;
519 }
520
521 for (b_grade, mv_b) in other_grades.iter().enumerate() {
522 if mv_b.is_zero() {
523 continue;
524 }
525
526 if b_grade >= a_grade {
528 let target_grade = b_grade - a_grade;
529 let product = mv_a.geometric_product(mv_b);
530 let projected = product.grade_projection(target_grade);
531 result = result + projected;
532 }
533 }
534 }
535
536 result
537 }
538
539 pub fn right_contraction(&self, other: &Self) -> Self {
544 let self_grades = self.grade_decomposition();
545 let other_grades = other.grade_decomposition();
546 let mut result = Self::zero();
547
548 for (a_grade, mv_a) in self_grades.iter().enumerate() {
549 if mv_a.is_zero() {
550 continue;
551 }
552
553 for (b_grade, mv_b) in other_grades.iter().enumerate() {
554 if mv_b.is_zero() {
555 continue;
556 }
557
558 if a_grade >= b_grade {
560 let target_grade = a_grade - b_grade;
561 let product = mv_a.geometric_product(mv_b);
562 let projected = product.grade_projection(target_grade);
563 result = result + projected;
564 }
565 }
566 }
567
568 result
569 }
570
571 pub fn hodge_dual(&self) -> Self {
577 let n = Self::DIM;
578 if n == 0 {
579 return self.clone();
580 }
581
582 let mut result = Self::zero();
583
584 let pseudoscalar_index = (1 << n) - 1; for i in 0..Self::BASIS_COUNT {
588 if self.coefficients[i].abs() < 1e-14 {
589 continue;
590 }
591
592 let dual_index = i ^ pseudoscalar_index;
595
596 let _grade_i = i.count_ones() as usize;
598 let _grade_dual = dual_index.count_ones() as usize;
599
600 let mut sign = 1.0;
602
603 let temp_i = i;
605 let temp_dual = dual_index;
606 let mut swaps = 0;
607
608 for bit_pos in 0..n {
609 let bit_mask = 1 << bit_pos;
610 if (temp_i & bit_mask) != 0 && (temp_dual & bit_mask) == 0 {
611 let right_bits = temp_dual & ((1 << bit_pos) - 1);
613 swaps += right_bits.count_ones();
614 }
615 }
616
617 if swaps % 2 == 1 {
618 sign = -1.0;
619 }
620
621 for j in 0..n {
623 let bit_mask = 1 << j;
624 if (dual_index & bit_mask) != 0 {
625 if j >= P + Q {
626 sign *= -1.0;
628 } else if j >= P {
629 sign *= -1.0;
631 }
632 }
634 }
635
636 result.coefficients[dual_index] += sign * self.coefficients[i];
637 }
638
639 result
640 }
641}
642
643impl<const P: usize, const Q: usize, const R: usize> Add for Multivector<P, Q, R> {
645 type Output = Self;
646
647 #[inline(always)]
648 fn add(mut self, rhs: Self) -> Self {
649 for i in 0..Self::BASIS_COUNT {
650 self.coefficients[i] += rhs.coefficients[i];
651 }
652 self
653 }
654}
655
656impl<const P: usize, const Q: usize, const R: usize> Add for &Multivector<P, Q, R> {
657 type Output = Multivector<P, Q, R>;
658
659 #[inline(always)]
660 fn add(self, rhs: Self) -> Multivector<P, Q, R> {
661 let mut result = self.clone();
662 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
663 result.coefficients[i] += rhs.coefficients[i];
664 }
665 result
666 }
667}
668
669impl<const P: usize, const Q: usize, const R: usize> Sub for Multivector<P, Q, R> {
670 type Output = Self;
671
672 #[inline(always)]
673 fn sub(mut self, rhs: Self) -> Self {
674 for i in 0..Self::BASIS_COUNT {
675 self.coefficients[i] -= rhs.coefficients[i];
676 }
677 self
678 }
679}
680
681impl<const P: usize, const Q: usize, const R: usize> Sub for &Multivector<P, Q, R> {
682 type Output = Multivector<P, Q, R>;
683
684 #[inline(always)]
685 fn sub(self, rhs: Self) -> Multivector<P, Q, R> {
686 let mut result = self.clone();
687 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
688 result.coefficients[i] -= rhs.coefficients[i];
689 }
690 result
691 }
692}
693
694impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for Multivector<P, Q, R> {
695 type Output = Self;
696
697 #[inline(always)]
698 fn mul(mut self, scalar: f64) -> Self {
699 for i in 0..Self::BASIS_COUNT {
700 self.coefficients[i] *= scalar;
701 }
702 self
703 }
704}
705
706impl<const P: usize, const Q: usize, const R: usize> Mul<f64> for &Multivector<P, Q, R> {
707 type Output = Multivector<P, Q, R>;
708
709 #[inline(always)]
710 fn mul(self, scalar: f64) -> Multivector<P, Q, R> {
711 let mut result = self.clone();
712 for i in 0..Multivector::<P, Q, R>::BASIS_COUNT {
713 result.coefficients[i] *= scalar;
714 }
715 result
716 }
717}
718
719impl<const P: usize, const Q: usize, const R: usize> Neg for Multivector<P, Q, R> {
720 type Output = Self;
721
722 #[inline(always)]
723 fn neg(mut self) -> Self {
724 for i in 0..Self::BASIS_COUNT {
725 self.coefficients[i] = -self.coefficients[i];
726 }
727 self
728 }
729}
730
731impl<const P: usize, const Q: usize, const R: usize> Zero for Multivector<P, Q, R> {
732 fn zero() -> Self {
733 Self::zero()
734 }
735
736 fn is_zero(&self) -> bool {
737 self.is_zero()
738 }
739}
740
741#[derive(Debug, Clone, PartialEq)]
743pub struct Scalar<const P: usize, const Q: usize, const R: usize> {
744 pub mv: Multivector<P, Q, R>,
745}
746
747impl<const P: usize, const Q: usize, const R: usize> Scalar<P, Q, R> {
748 pub fn from(value: f64) -> Self {
749 Self {
750 mv: Multivector::scalar(value),
751 }
752 }
753
754 pub fn one() -> Self {
755 Self::from(1.0)
756 }
757
758 pub fn geometric_product(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
759 self.mv.geometric_product(other)
760 }
761
762 pub fn geometric_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
763 self.mv.geometric_product(&other.mv)
764 }
765}
766
767impl<const P: usize, const Q: usize, const R: usize> From<f64> for Scalar<P, Q, R> {
768 fn from(value: f64) -> Self {
769 Self::from(value)
770 }
771}
772
773#[derive(Debug, Clone, PartialEq)]
775pub struct Vector<const P: usize, const Q: usize, const R: usize> {
776 pub mv: Multivector<P, Q, R>,
777}
778
779impl<const P: usize, const Q: usize, const R: usize> Vector<P, Q, R> {
780 pub fn zero() -> Self {
782 Self {
783 mv: Multivector::zero(),
784 }
785 }
786
787 pub fn from_components(x: f64, y: f64, z: f64) -> Self {
788 let mut mv = Multivector::zero();
789 if P + Q + R >= 1 {
790 mv.set_vector_component(0, x);
791 }
792 if P + Q + R >= 2 {
793 mv.set_vector_component(1, y);
794 }
795 if P + Q + R >= 3 {
796 mv.set_vector_component(2, z);
797 }
798 Self { mv }
799 }
800
801 pub fn e1() -> Self {
802 Self {
803 mv: Multivector::basis_vector(0),
804 }
805 }
806
807 pub fn e2() -> Self {
808 Self {
809 mv: Multivector::basis_vector(1),
810 }
811 }
812
813 pub fn e3() -> Self {
814 Self {
815 mv: Multivector::basis_vector(2),
816 }
817 }
818
819 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
820 Self {
821 mv: mv.grade_projection(1),
822 }
823 }
824
825 pub fn geometric_product(&self, other: &Self) -> Multivector<P, Q, R> {
826 self.mv.geometric_product(&other.mv)
827 }
828
829 pub fn geometric_product_with_multivector(
830 &self,
831 other: &Multivector<P, Q, R>,
832 ) -> Multivector<P, Q, R> {
833 self.mv.geometric_product(other)
834 }
835
836 pub fn geometric_product_with_bivector(
837 &self,
838 other: &Bivector<P, Q, R>,
839 ) -> Multivector<P, Q, R> {
840 self.mv.geometric_product(&other.mv)
841 }
842
843 pub fn geometric_product_with_scalar(&self, other: &Scalar<P, Q, R>) -> Multivector<P, Q, R> {
844 self.mv.geometric_product(&other.mv)
845 }
846
847 pub fn add(&self, other: &Self) -> Self {
848 Self {
849 mv: &self.mv + &other.mv,
850 }
851 }
852
853 pub fn magnitude(&self) -> f64 {
854 self.mv.magnitude()
855 }
856
857 pub fn as_slice(&self) -> &[f64] {
858 self.mv.as_slice()
859 }
860
861 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
863 self.mv.inner_product(&other.mv)
864 }
865
866 pub fn inner_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
868 self.mv.inner_product(other)
869 }
870
871 pub fn inner_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
873 self.mv.inner_product(&other.mv)
874 }
875
876 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
878 self.mv.outer_product(&other.mv)
879 }
880
881 pub fn outer_product_with_mv(&self, other: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
883 self.mv.outer_product(other)
884 }
885
886 pub fn outer_product_with_bivector(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
888 self.mv.outer_product(&other.mv)
889 }
890
891 pub fn left_contraction(&self, other: &Bivector<P, Q, R>) -> Multivector<P, Q, R> {
893 let product = self.mv.geometric_product(&other.mv);
895 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
896 product.grade_projection(target_grade)
897 }
898
899 pub fn normalize(&self) -> Option<Self> {
901 self.mv
902 .normalize()
903 .map(|normalized| Self { mv: normalized })
904 }
905
906 pub fn norm_squared(&self) -> f64 {
908 self.mv.norm_squared()
909 }
910
911 pub fn reverse(&self) -> Self {
913 Self {
914 mv: self.mv.reverse(),
915 }
916 }
917
918 pub fn norm(&self) -> f64 {
923 self.magnitude()
924 }
925
926 pub fn hodge_dual(&self) -> Bivector<P, Q, R> {
929 Bivector {
930 mv: self.mv.hodge_dual(),
931 }
932 }
933}
934
935#[derive(Debug, Clone, PartialEq)]
937pub struct Bivector<const P: usize, const Q: usize, const R: usize> {
938 pub mv: Multivector<P, Q, R>,
939}
940
941impl<const P: usize, const Q: usize, const R: usize> Bivector<P, Q, R> {
942 pub fn from_components(xy: f64, xz: f64, yz: f64) -> Self {
943 let mut mv = Multivector::zero();
944 if P + Q + R >= 2 {
945 mv.set_bivector_component(0, xy);
946 } if P + Q + R >= 3 {
948 mv.set_bivector_component(1, xz);
949 } if P + Q + R >= 3 {
951 mv.set_bivector_component(2, yz);
952 } Self { mv }
954 }
955
956 pub fn e12() -> Self {
957 let mut mv = Multivector::zero();
958 mv.set_bivector_component(0, 1.0); Self { mv }
960 }
961
962 pub fn e13() -> Self {
963 let mut mv = Multivector::zero();
964 mv.set_bivector_component(1, 1.0); Self { mv }
966 }
967
968 pub fn e23() -> Self {
969 let mut mv = Multivector::zero();
970 mv.set_bivector_component(2, 1.0); Self { mv }
972 }
973
974 pub fn from_multivector(mv: &Multivector<P, Q, R>) -> Self {
975 Self {
976 mv: mv.grade_projection(2),
977 }
978 }
979
980 pub fn geometric_product(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
981 self.mv.geometric_product(&other.mv)
982 }
983
984 pub fn geometric_product_with_bivector(&self, other: &Self) -> Multivector<P, Q, R> {
986 self.mv.geometric_product(&other.mv)
987 }
988
989 pub fn magnitude(&self) -> f64 {
990 self.mv.magnitude()
991 }
992
993 pub fn get(&self, index: usize) -> f64 {
995 match index {
996 0 => self.mv.get(3), 1 => self.mv.get(5), 2 => self.mv.get(6), _ => 0.0,
1000 }
1001 }
1002
1003 pub fn inner_product(&self, other: &Self) -> Multivector<P, Q, R> {
1005 self.mv.inner_product(&other.mv)
1006 }
1007
1008 pub fn inner_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1010 self.mv.inner_product(&other.mv)
1011 }
1012
1013 pub fn outer_product(&self, other: &Self) -> Multivector<P, Q, R> {
1015 self.mv.outer_product(&other.mv)
1016 }
1017
1018 pub fn outer_product_with_vector(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1020 self.mv.outer_product(&other.mv)
1021 }
1022
1023 pub fn right_contraction(&self, other: &Vector<P, Q, R>) -> Multivector<P, Q, R> {
1025 let product = self.mv.geometric_product(&other.mv);
1027 let target_grade = if 2 >= 1 { 2 - 1 } else { 1 - 2 };
1028 product.grade_projection(target_grade)
1029 }
1030}
1031
1032impl<const P: usize, const Q: usize, const R: usize> core::ops::Index<usize> for Bivector<P, Q, R> {
1033 type Output = f64;
1034
1035 fn index(&self, index: usize) -> &Self::Output {
1036 match index {
1037 0 => &self.mv.coefficients[3], 1 => &self.mv.coefficients[5], 2 => &self.mv.coefficients[6], _ => panic!("Bivector index out of range: {}", index),
1041 }
1042 }
1043}
1044
1045pub type E<const P: usize, const Q: usize, const R: usize> = Vector<P, Q, R>;
1047
1048pub use rotor::Rotor;
1050
1051#[cfg(test)]
1052mod tests {
1053 use super::*;
1054 use approx::assert_relative_eq;
1055
1056 type Cl3 = Multivector<3, 0, 0>; #[test]
1059 fn test_basis_vectors() {
1060 let e1 = Cl3::basis_vector(0);
1061 let e2 = Cl3::basis_vector(1);
1062
1063 let e1_squared = e1.geometric_product(&e1);
1065 assert_eq!(e1_squared.scalar_part(), 1.0);
1066
1067 let e12 = e1.geometric_product(&e2);
1069 let e21 = e2.geometric_product(&e1);
1070 assert_eq!(e12.coefficients[3], -e21.coefficients[3]); }
1072
1073 #[test]
1074 fn test_wedge_product() {
1075 let e1 = Cl3::basis_vector(0);
1076 let e2 = Cl3::basis_vector(1);
1077
1078 let e12 = e1.outer_product(&e2);
1079 assert!(e12.get(3).abs() - 1.0 < 1e-10); let e11 = e1.outer_product(&e1);
1083 assert!(e11.is_zero());
1084 }
1085
1086 #[test]
1087 fn test_rotor_from_bivector() {
1088 let e1 = Cl3::basis_vector(0);
1089 let e2 = Cl3::basis_vector(1);
1090 let e12 = e1.outer_product(&e2);
1091
1092 let angle = core::f64::consts::PI / 4.0; let bivector = e12 * angle;
1095 let rotor = bivector.exp();
1096
1097 assert!((rotor.norm() - 1.0).abs() < 1e-10);
1099 }
1100
1101 #[test]
1102 fn test_algebraic_identities() {
1103 let e1 = Cl3::basis_vector(0);
1104 let e2 = Cl3::basis_vector(1);
1105 let e3 = Cl3::basis_vector(2);
1106
1107 let a = e1.clone() + e2.clone() * 2.0;
1109 let b = e2.clone() + e3.clone() * 3.0;
1110 let c = e3.clone() + e1.clone() * 0.5;
1111
1112 let left = a.geometric_product(&b).geometric_product(&c);
1113 let right = a.geometric_product(&b.geometric_product(&c));
1114 assert_relative_eq!(left.norm(), right.norm(), epsilon = 1e-12);
1115
1116 let ab_plus_ac = a.geometric_product(&b) + a.geometric_product(&c);
1118 let a_times_b_plus_c = a.geometric_product(&(b.clone() + c.clone()));
1119 assert!((ab_plus_ac - a_times_b_plus_c).norm() < 1e-12);
1120
1121 let ab_reverse = a.geometric_product(&b).reverse();
1123 let b_reverse_a_reverse = b.reverse().geometric_product(&a.reverse());
1124 assert!((ab_reverse - b_reverse_a_reverse).norm() < 1e-12);
1125 }
1126
1127 #[test]
1128 fn test_metric_signature() {
1129 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);
1137 assert_eq!(e1.geometric_product(&e1).scalar_part(), -1.0);
1138 }
1139
1140 #[test]
1141 fn test_grade_operations() {
1142 let e1 = Cl3::basis_vector(0);
1143 let e2 = Cl3::basis_vector(1);
1144 let scalar = Cl3::scalar(2.0);
1145
1146 let mv = scalar + e1.clone() * 3.0 + e2.clone() * 4.0 + e1.outer_product(&e2) * 5.0;
1147
1148 let grade0 = mv.grade_projection(0);
1150 let grade1 = mv.grade_projection(1);
1151 let grade2 = mv.grade_projection(2);
1152
1153 assert_eq!(grade0.scalar_part(), 2.0);
1154 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;
1160 assert!((mv - reconstructed).norm() < 1e-12);
1161 }
1162
1163 #[test]
1164 fn test_inner_and_outer_products() {
1165 let e1 = Cl3::basis_vector(0);
1166 let e2 = Cl3::basis_vector(1);
1167 let e3 = Cl3::basis_vector(2);
1168
1169 assert!(e1.inner_product(&e2).norm() < 1e-12);
1171
1172 let v1 = e1.clone() + e2.clone();
1174 let v2 = e1.clone() * 2.0 + e2.clone() * 2.0;
1175 let inner = v1.inner_product(&v2);
1176 assert_relative_eq!(inner.scalar_part(), 4.0, epsilon = 1e-12);
1177
1178 let bivector = e1.outer_product(&e2);
1180 let trivector = bivector.outer_product(&e3);
1181 assert_eq!(trivector.get(7), 1.0); }
1183}