1pub fn blade_product(
20 p: usize,
21 q: usize,
22 _r: usize,
23 dim: usize,
24 i: usize,
25 j: usize,
26) -> (usize, f64) {
27 let k = i ^ j; let mut sign = 1.0f64;
33 let mut swap_count = 0u32;
34
35 let mut remaining_i = i;
39 for bit in 0..dim {
40 if (j >> bit) & 1 == 1 {
41 let higher_bits = (remaining_i >> (bit + 1)).count_ones();
43 swap_count += higher_bits;
44 }
45 remaining_i &= !(1 << bit);
46 }
47
48 if swap_count % 2 == 1 {
49 sign = -sign;
50 }
51
52 let common = i & j;
55 for bit in 0..dim {
56 if (common >> bit) & 1 == 1 {
57 if bit < p {
58 } else if bit < p + q {
60 sign = -sign;
62 } else {
63 return (k, 0.0);
65 }
66 }
67 }
68
69 (k, sign)
70}
71
72pub fn generic_geometric_product(p: usize, q: usize, r: usize, a: &[f64], b: &[f64]) -> Vec<f64> {
77 let dim = p + q + r;
78 let basis_count = 1 << dim;
79 let mut result = vec![0.0; basis_count];
80
81 for (i, &ai) in a.iter().enumerate() {
82 if ai.abs() < f64::MIN_POSITIVE {
83 continue;
84 }
85
86 for (j, &bj) in b.iter().enumerate() {
87 if bj.abs() < f64::MIN_POSITIVE {
88 continue;
89 }
90
91 let (k, sign) = blade_product(p, q, r, dim, i, j);
92 if sign != 0.0 {
93 result[k] += sign * ai * bj;
94 }
95 }
96 }
97
98 result
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::Multivector;
105
106 #[test]
109 fn test_blade_product_e1_e1_cl300() {
110 let (k, sign) = blade_product(3, 0, 0, 3, 1, 1);
112 assert_eq!(k, 0); assert!((sign - 1.0).abs() < 1e-10);
114 }
115
116 #[test]
117 fn test_blade_product_e1_e2_cl300() {
118 let (k, sign) = blade_product(3, 0, 0, 3, 1, 2);
120 assert_eq!(k, 3); assert!((sign - 1.0).abs() < 1e-10);
122 }
123
124 #[test]
125 fn test_blade_product_e2_e1_cl300() {
126 let (k, sign) = blade_product(3, 0, 0, 3, 2, 1);
128 assert_eq!(k, 3);
129 assert!((sign + 1.0).abs() < 1e-10);
130 }
131
132 #[test]
133 fn test_blade_product_e3_e3_cl210() {
134 let (k, sign) = blade_product(2, 1, 0, 3, 4, 4);
137 assert_eq!(k, 0);
138 assert!((sign + 1.0).abs() < 1e-10);
139 }
140
141 #[test]
142 fn test_blade_product_null_signature() {
143 let (k, sign) = blade_product(0, 0, 1, 1, 1, 1);
145 assert_eq!(k, 0);
146 assert!((sign - 0.0).abs() < 1e-10);
147 }
148
149 #[test]
150 fn test_blade_product_e12_e1_cl300() {
151 let (k, sign) = blade_product(3, 0, 0, 3, 3, 1);
154 assert_eq!(k, 2); assert!((sign + 1.0).abs() < 1e-10);
156 }
157
158 #[test]
159 fn test_blade_product_scalar_scalar() {
160 let (k, sign) = blade_product(3, 0, 0, 3, 0, 0);
162 assert_eq!(k, 0);
163 assert!((sign - 1.0).abs() < 1e-10);
164 }
165
166 #[test]
169 fn test_generic_gp_matches_const_generic_cl300() {
170 let mv_a = Multivector::<3, 0, 0>::basis_vector(0);
171 let mv_b = Multivector::<3, 0, 0>::basis_vector(1);
172 let expected = mv_a.geometric_product(&mv_b);
173
174 let a_coeffs: Vec<f64> = (0..8).map(|i| mv_a.get(i)).collect();
175 let b_coeffs: Vec<f64> = (0..8).map(|i| mv_b.get(i)).collect();
176 let result = generic_geometric_product(3, 0, 0, &a_coeffs, &b_coeffs);
177
178 for i in 0..8 {
179 assert!(
180 (result[i] - expected.get(i)).abs() < 1e-10,
181 "Coefficient {} differs: {} vs {}",
182 i,
183 result[i],
184 expected.get(i)
185 );
186 }
187 }
188
189 #[test]
190 fn test_generic_gp_matches_const_generic_cl210() {
191 let mv_a = Multivector::<2, 1, 0>::basis_vector(2);
193 let mv_b = Multivector::<2, 1, 0>::basis_vector(2);
194 let expected = mv_a.geometric_product(&mv_b);
195
196 let a_coeffs: Vec<f64> = (0..8).map(|i| mv_a.get(i)).collect();
197 let b_coeffs: Vec<f64> = (0..8).map(|i| mv_b.get(i)).collect();
198 let result = generic_geometric_product(2, 1, 0, &a_coeffs, &b_coeffs);
199
200 for i in 0..8 {
201 assert!(
202 (result[i] - expected.get(i)).abs() < 1e-10,
203 "Coefficient {} differs: {} vs {}",
204 i,
205 result[i],
206 expected.get(i)
207 );
208 }
209 }
210
211 #[test]
212 fn test_generic_gp_random_cl410() {
213 let a_coeffs: Vec<f64> = (0..32).map(|i| ((i as f64) * 0.137) % 1.5 - 0.75).collect();
216 let b_coeffs: Vec<f64> = (0..32)
217 .map(|i| ((i as f64 + 17.0) * 0.097) % 1.5 - 0.75)
218 .collect();
219
220 let mv_a = Multivector::<4, 1, 0>::from_coefficients(a_coeffs.clone());
221 let mv_b = Multivector::<4, 1, 0>::from_coefficients(b_coeffs.clone());
222 let expected = mv_a.geometric_product(&mv_b);
223
224 let result = generic_geometric_product(4, 1, 0, &a_coeffs, &b_coeffs);
225
226 for i in 0..32 {
227 assert!(
228 (result[i] - expected.get(i)).abs() < 1e-8,
229 "Coefficient {} differs: {} vs {}",
230 i,
231 result[i],
232 expected.get(i)
233 );
234 }
235 }
236
237 #[test]
238 fn test_generic_gp_scalar_only() {
239 let a = vec![2.0];
241 let b = vec![3.0];
242 let result = generic_geometric_product(0, 0, 0, &a, &b);
243 assert_eq!(result.len(), 1);
244 assert!((result[0] - 6.0).abs() < 1e-10);
245 }
246}