Skip to main content

clifford_codegen/algebra/
table.rs

1//! Precomputed product tables for efficient lookup.
2//!
3//! Product tables precompute all basis blade products for an algebra,
4//! enabling O(1) lookup during code generation. This is essential for
5//! generating optimized product implementations.
6
7use super::signature::Algebra;
8
9/// Precomputed product table for a geometric algebra.
10///
11/// Stores the sign and result blade for all pairs of basis blades.
12/// This enables O(1) lookup of any product during code generation.
13///
14/// # Example
15///
16/// ```
17/// use clifford_codegen::algebra::{Algebra, ProductTable};
18///
19/// let algebra = Algebra::euclidean(3);
20/// let table = ProductTable::new(&algebra);
21///
22/// // e1 * e2 = e12 with sign +1
23/// let (sign, result) = table.geometric(1, 2);
24/// assert_eq!(sign, 1);
25/// assert_eq!(result, 3);
26///
27/// // e2 * e1 = -e12 (anticommutative)
28/// let (sign, result) = table.geometric(2, 1);
29/// assert_eq!(sign, -1);
30/// assert_eq!(result, 3);
31/// ```
32#[derive(Clone, Debug)]
33pub struct ProductTable {
34    /// The algebra dimension.
35    dim: usize,
36    /// Signs for geometric product: signs[a * n + b] = sign of e_a * e_b.
37    signs: Vec<i8>,
38    /// Result indices: results[a * n + b] = index of e_a * e_b.
39    results: Vec<usize>,
40}
41
42impl ProductTable {
43    /// Builds a product table for the given algebra.
44    ///
45    /// Precomputes all `n²` products where `n = 2^dim`.
46    ///
47    /// # Example
48    ///
49    /// ```
50    /// use clifford_codegen::algebra::{Algebra, ProductTable};
51    ///
52    /// let algebra = Algebra::euclidean(3);
53    /// let table = ProductTable::new(&algebra);
54    ///
55    /// // Table has 8*8 = 64 entries for 3D
56    /// assert_eq!(table.dim(), 3);
57    /// ```
58    pub fn new(algebra: &Algebra) -> Self {
59        let n = algebra.num_blades();
60        let mut signs = vec![0i8; n * n];
61        let mut results = vec![0usize; n * n];
62
63        for a in 0..n {
64            for b in 0..n {
65                let (sign, result) = algebra.basis_product(a, b);
66                signs[a * n + b] = sign;
67                results[a * n + b] = result;
68            }
69        }
70
71        Self {
72            dim: algebra.dim(),
73            signs,
74            results,
75        }
76    }
77
78    /// Returns the algebra dimension.
79    #[inline]
80    pub fn dim(&self) -> usize {
81        self.dim
82    }
83
84    /// Returns the number of blades (2^dim).
85    #[inline]
86    pub fn num_blades(&self) -> usize {
87        1 << self.dim
88    }
89
90    /// Looks up the geometric product of two basis blades.
91    ///
92    /// # Returns
93    ///
94    /// A tuple `(sign, result)` where:
95    /// - `sign` is the sign factor (-1, 0, or +1)
96    /// - `result` is the blade index of the product
97    ///
98    /// # Panics
99    ///
100    /// Panics if `a` or `b` is out of bounds.
101    ///
102    /// # Example
103    ///
104    /// ```
105    /// use clifford_codegen::algebra::{Algebra, ProductTable};
106    ///
107    /// let algebra = Algebra::euclidean(3);
108    /// let table = ProductTable::new(&algebra);
109    ///
110    /// // e12 * e12 = -1 (bivector squares to -1)
111    /// let (sign, result) = table.geometric(3, 3);
112    /// assert_eq!(sign, -1);
113    /// assert_eq!(result, 0);
114    /// ```
115    #[inline]
116    pub fn geometric(&self, a: usize, b: usize) -> (i8, usize) {
117        let n = self.num_blades();
118        let idx = a * n + b;
119        (self.signs[idx], self.results[idx])
120    }
121
122    /// Returns the sign of the product of two basis blades.
123    #[inline]
124    pub fn sign(&self, a: usize, b: usize) -> i8 {
125        self.signs[a * self.num_blades() + b]
126    }
127
128    /// Returns the result blade of the product of two basis blades.
129    #[inline]
130    pub fn result(&self, a: usize, b: usize) -> usize {
131        self.results[a * self.num_blades() + b]
132    }
133
134    /// Returns the metric value for a basis vector.
135    /// Finds all products that contribute to a given result blade.
136    ///
137    /// Given sets of input blades A and B, returns all (a, b) pairs
138    /// such that `a * b = result` (with non-zero sign).
139    ///
140    /// # Arguments
141    ///
142    /// * `a_blades` - Set of blade indices from the first operand
143    /// * `b_blades` - Set of blade indices from the second operand
144    /// * `result_blade` - The target result blade index
145    ///
146    /// # Returns
147    ///
148    /// Vector of `(sign, a_blade, b_blade)` tuples contributing to the result.
149    ///
150    /// # Example
151    ///
152    /// ```
153    /// use clifford_codegen::algebra::{Algebra, ProductTable};
154    ///
155    /// let algebra = Algebra::euclidean(3);
156    /// let table = ProductTable::new(&algebra);
157    ///
158    /// // Which vector * vector products contribute to e12?
159    /// let vectors = vec![1, 2, 4]; // e1, e2, e3
160    /// let contributions = table.product_contributions(&vectors, &vectors, 3);
161    ///
162    /// // e1 * e2 = +e12, e2 * e1 = -e12
163    /// assert_eq!(contributions.len(), 2);
164    /// assert!(contributions.contains(&(1, 1, 2)));  // e1 * e2 = +e12
165    /// assert!(contributions.contains(&(-1, 2, 1))); // e2 * e1 = -e12
166    /// ```
167    pub fn product_contributions(
168        &self,
169        a_blades: &[usize],
170        b_blades: &[usize],
171        result_blade: usize,
172    ) -> Vec<(i8, usize, usize)> {
173        let mut contributions = Vec::new();
174
175        for &a in a_blades {
176            for &b in b_blades {
177                let (sign, result) = self.geometric(a, b);
178                if result == result_blade && sign != 0 {
179                    contributions.push((sign, a, b));
180                }
181            }
182        }
183
184        contributions
185    }
186
187    /// Checks if a product contributes to a given grade.
188    ///
189    /// Returns true if any `a * b` product (for a in a_blades, b in b_blades)
190    /// produces a blade of the target grade.
191    pub fn has_contributions_to_grade(
192        &self,
193        a_blades: &[usize],
194        b_blades: &[usize],
195        target_grade: usize,
196    ) -> bool {
197        for &a in a_blades {
198            for &b in b_blades {
199                let (sign, result) = self.geometric(a, b);
200                if sign != 0 && result.count_ones() as usize == target_grade {
201                    return true;
202                }
203            }
204        }
205        false
206    }
207
208    /// Returns all result blades from products of a_blades × b_blades.
209    ///
210    /// # Returns
211    ///
212    /// Vector of (blade_index, contributions) pairs, sorted by blade index.
213    /// Each contribution is (sign, a_blade, b_blade).
214    #[allow(clippy::type_complexity)]
215    pub fn all_products(
216        &self,
217        a_blades: &[usize],
218        b_blades: &[usize],
219    ) -> Vec<(usize, Vec<(i8, usize, usize)>)> {
220        use std::collections::BTreeMap;
221
222        let mut result_map: BTreeMap<usize, Vec<(i8, usize, usize)>> = BTreeMap::new();
223
224        for &a in a_blades {
225            for &b in b_blades {
226                let (sign, result) = self.geometric(a, b);
227                if sign != 0 {
228                    result_map.entry(result).or_default().push((sign, a, b));
229                }
230            }
231        }
232
233        result_map.into_iter().collect()
234    }
235
236    /// Computes the right complement of a blade.
237    ///
238    /// The right complement maps a grade-k blade to a grade-(n-k) blade where n = dim.
239    /// For blade index `i`, the complement index is `(2^dim - 1) XOR i`.
240    ///
241    /// The right complement is defined by: `u ∧ ū = I` (pseudoscalar)
242    /// where ∧ is the exterior (wedge) product.
243    ///
244    /// # Returns
245    ///
246    /// A tuple `(sign, complement_index)` where sign is ±1 based on the
247    /// permutation required to bring the blade and its complement into canonical
248    /// order to form the pseudoscalar via the exterior product.
249    ///
250    /// # Example
251    ///
252    /// ```
253    /// use clifford_codegen::algebra::{Algebra, ProductTable};
254    ///
255    /// let algebra = Algebra::euclidean(3);
256    /// let table = ProductTable::new(&algebra);
257    ///
258    /// // In 3D: complement(e1) = e23 (with some sign)
259    /// let (sign, result) = table.complement(1);
260    /// assert_eq!(result, 6); // e23 = 0b110
261    /// ```
262    pub fn complement(&self, blade: usize) -> (i8, usize) {
263        let pseudoscalar = self.num_blades() - 1; // All bits set
264        let complement_blade = pseudoscalar ^ blade;
265
266        // Sign is determined by: blade ∧ complement_blade = ±pseudoscalar
267        // Since blade and complement_blade don't share any basis vectors,
268        // the exterior product equals the geometric product for this pair.
269        // We compute the sign by counting transpositions needed to put
270        // the combined basis vectors into canonical order.
271        let sign = self.exterior_sign(blade, complement_blade);
272
273        (sign, complement_blade)
274    }
275
276    /// Computes the exterior (wedge) product of two basis blades.
277    ///
278    /// The exterior product `a ∧ b`:
279    /// - Is zero if the blades share any basis vectors (a & b != 0)
280    /// - Otherwise equals `a | b` with a sign from reordering
281    ///
282    /// # Returns
283    ///
284    /// A tuple `(sign, result)` where:
285    /// - `sign` is 0 if blades overlap, or ±1 from reordering
286    /// - `result` is the blade index of the product
287    ///
288    /// # Example
289    ///
290    /// ```
291    /// use clifford_codegen::algebra::{Algebra, ProductTable};
292    ///
293    /// let algebra = Algebra::euclidean(3);
294    /// let table = ProductTable::new(&algebra);
295    ///
296    /// // e1 ∧ e2 = e12
297    /// let (sign, result) = table.exterior(1, 2);
298    /// assert_eq!(sign, 1);
299    /// assert_eq!(result, 3);
300    ///
301    /// // e1 ∧ e1 = 0 (same basis vector)
302    /// let (sign, result) = table.exterior(1, 1);
303    /// assert_eq!(sign, 0);
304    /// ```
305    pub fn exterior(&self, a: usize, b: usize) -> (i8, usize) {
306        // Exterior product is zero if blades share any basis vectors
307        if a & b != 0 {
308            return (0, 0);
309        }
310
311        // Result blade is the union of basis vectors
312        let result = a | b;
313
314        // Sign from reordering basis vectors into canonical order
315        let sign = self.exterior_sign(a, b);
316
317        (sign, result)
318    }
319
320    /// Computes the regressive (meet) product of two basis blades.
321    ///
322    /// The regressive product is defined as: `a ∨ b = ∁(∁a ∧ ∁b)`
323    /// where `∁` is the right complement.
324    ///
325    /// This is the dual of the exterior product - while the exterior product
326    /// computes the "join" (smallest subspace containing both), the regressive
327    /// product computes the "meet" (intersection).
328    ///
329    /// # Returns
330    ///
331    /// A tuple `(sign, result)` where:
332    /// - `sign` is the sign factor (-1, 0, or +1)
333    /// - `result` is the blade index of the regressive product
334    ///
335    /// Note: The sign may be 0 if the product vanishes.
336    ///
337    /// # Example
338    ///
339    /// ```
340    /// use clifford_codegen::algebra::{Algebra, ProductTable};
341    ///
342    /// let algebra = Algebra::pga(2); // 2D PGA: Cl(2,0,1)
343    /// let table = ProductTable::new(&algebra);
344    ///
345    /// // In 2D PGA, Line (grade 2) ∨ Line (grade 2) = Point (grade 1)
346    /// // Result grade = 2 + 2 - 3 = 1
347    /// let e12 = 0b011; // grade 2 blade
348    /// let e01 = 0b101; // grade 2 blade
349    /// let (sign, result) = table.regressive(e12, e01);
350    /// assert_ne!(sign, 0, "regressive product should be non-zero");
351    /// ```
352    pub fn regressive(&self, a: usize, b: usize) -> (i8, usize) {
353        // Get complements
354        let (sign_ca, comp_a) = self.complement(a);
355        let (sign_cb, comp_b) = self.complement(b);
356
357        // Exterior product of complements
358        let (sign_ext, ext_result) = self.exterior(comp_a, comp_b);
359        if sign_ext == 0 {
360            return (0, 0);
361        }
362
363        // Complement of the result
364        let (sign_result, result) = self.complement(ext_result);
365
366        // Total sign
367        let total_sign = sign_ca * sign_cb * sign_ext * sign_result;
368
369        (total_sign, result)
370    }
371
372    /// Computes the sign of the exterior product of two non-overlapping blades.
373    ///
374    /// For blades that don't share basis vectors, this is the sign from
375    /// reordering the basis vectors into canonical order.
376    fn exterior_sign(&self, a: usize, b: usize) -> i8 {
377        debug_assert_eq!(a & b, 0, "blades must not overlap for exterior sign");
378
379        // Count transpositions: for each bit in b, count how many bits in a
380        // are to the right of it (i.e., have higher index but appear earlier)
381        let mut transpositions = 0;
382        let mut b_remaining = b;
383        while b_remaining != 0 {
384            // Find lowest set bit in b
385            let lowest_b = b_remaining & b_remaining.wrapping_neg();
386            let b_pos = lowest_b.trailing_zeros();
387
388            // Count bits in a that are above this position (need to swap past)
389            let a_above = a >> (b_pos + 1);
390            transpositions += a_above.count_ones();
391
392            b_remaining &= !lowest_b;
393        }
394
395        if transpositions % 2 == 0 { 1 } else { -1 }
396    }
397
398    /// Computes the geometric antiproduct of two blades.
399    ///
400    /// The antiproduct is defined as:
401    /// `a ⊛ b = ∁(∁a × ∁b)`
402    ///
403    /// where `×` is the **regular** geometric product and `∁` is the complement.
404    ///
405    /// # Returns
406    ///
407    /// A tuple `(sign, result)` where:
408    /// - `sign` is the sign factor (-1, 0, or +1)
409    /// - `result` is the blade index of the antiproduct
410    ///
411    /// Note: The sign may be 0 if the product vanishes.
412    pub fn antiproduct(&self, a: usize, b: usize) -> (i8, usize) {
413        // Get complements
414        let (sign_ca, comp_a) = self.complement(a);
415        let (sign_cb, comp_b) = self.complement(b);
416
417        // Geometric product of complements using REGULAR metric
418        let (sign_prod, prod) = self.geometric(comp_a, comp_b);
419        if sign_prod == 0 {
420            return (0, 0);
421        }
422
423        // Complement of the product
424        let (sign_result, result) = self.complement(prod);
425
426        // Total sign
427        let total_sign = sign_ca * sign_cb * sign_prod * sign_result;
428
429        (total_sign, result)
430    }
431
432    /// Computes the exterior antiproduct (regressive/antiwedge product) of two blades.
433    ///
434    /// This is an alias for `regressive()` - the dual of the exterior product.
435    /// The antiwedge `a ∨ b` combines the "empty dimensions" of its operands.
436    ///
437    /// # Returns
438    ///
439    /// A tuple `(sign, result)` where:
440    /// - `sign` is the sign factor (-1, 0, or +1)
441    /// - `result` is the blade index of the antiwedge product
442    #[inline]
443    pub fn exterior_anti(&self, a: usize, b: usize) -> (i8, usize) {
444        self.regressive(a, b)
445    }
446
447    /// Computes the left contraction (interior product) of two basis blades.
448    ///
449    /// The left contraction `a ⌋ b` extracts the grade `grade(b) - grade(a)`
450    /// part of the geometric product. It is zero if `grade(a) > grade(b)`.
451    ///
452    /// Geometrically, this "removes" the component of `b` that is parallel to `a`.
453    ///
454    /// # Returns
455    ///
456    /// A tuple `(sign, result)` where:
457    /// - `sign` is the sign factor (-1, 0, or +1)
458    /// - `result` is the blade index of the contraction
459    ///
460    /// # Example
461    ///
462    /// ```
463    /// use clifford_codegen::algebra::{Algebra, ProductTable};
464    ///
465    /// let algebra = Algebra::euclidean(3);
466    /// let table = ProductTable::new(&algebra);
467    ///
468    /// // e1 ⌋ e12 = e2 (grade 2 - 1 = 1)
469    /// let (sign, result) = table.left_contraction(1, 3);
470    /// assert_eq!(sign, 1);
471    /// assert_eq!(result, 2); // e2
472    ///
473    /// // e12 ⌋ e1 = 0 (grade 2 > grade 1)
474    /// let (sign, _) = table.left_contraction(3, 1);
475    /// assert_eq!(sign, 0);
476    /// ```
477    pub fn left_contraction(&self, a: usize, b: usize) -> (i8, usize) {
478        let grade_a = a.count_ones() as usize;
479        let grade_b = b.count_ones() as usize;
480
481        // Left contraction is zero if grade(a) > grade(b)
482        if grade_a > grade_b {
483            return (0, 0);
484        }
485
486        let target_grade = grade_b - grade_a;
487        let (sign, result) = self.geometric(a, b);
488
489        // Check if result has the correct grade
490        if sign != 0 && result.count_ones() as usize == target_grade {
491            (sign, result)
492        } else {
493            (0, 0)
494        }
495    }
496
497    /// Computes the right contraction of two basis blades.
498    ///
499    /// The right contraction `a ⌊ b` extracts the grade `grade(a) - grade(b)`
500    /// part of the geometric product. It is zero if `grade(b) > grade(a)`.
501    ///
502    /// # Returns
503    ///
504    /// A tuple `(sign, result)` where:
505    /// - `sign` is the sign factor (-1, 0, or +1)
506    /// - `result` is the blade index of the contraction
507    ///
508    /// # Example
509    ///
510    /// ```
511    /// use clifford_codegen::algebra::{Algebra, ProductTable};
512    ///
513    /// let algebra = Algebra::euclidean(3);
514    /// let table = ProductTable::new(&algebra);
515    ///
516    /// // e12 ⌊ e2 = e1 (grade 2 - 1 = 1)
517    /// let (sign, result) = table.right_contraction(3, 2);
518    /// assert_eq!(sign, 1);   // e12 * e2 = e1 e2 e2 = e1 * (+1) = e1
519    /// assert_eq!(result, 1); // e1
520    ///
521    /// // e1 ⌊ e12 = 0 (grade 1 < grade 2)
522    /// let (sign, _) = table.right_contraction(1, 3);
523    /// assert_eq!(sign, 0);
524    /// ```
525    pub fn right_contraction(&self, a: usize, b: usize) -> (i8, usize) {
526        let grade_a = a.count_ones() as usize;
527        let grade_b = b.count_ones() as usize;
528
529        // Right contraction is zero if grade(b) > grade(a)
530        if grade_b > grade_a {
531            return (0, 0);
532        }
533
534        let target_grade = grade_a - grade_b;
535        let (sign, result) = self.geometric(a, b);
536
537        // Check if result has the correct grade
538        if sign != 0 && result.count_ones() as usize == target_grade {
539            (sign, result)
540        } else {
541            (0, 0)
542        }
543    }
544
545    /// Computes the interior product (symmetric contraction) of two basis blades.
546    ///
547    /// The interior product extracts the grade `|grade(a) - grade(b)|` part
548    /// of the geometric product. It's the symmetric version of the contractions.
549    ///
550    /// # Returns
551    ///
552    /// A tuple `(sign, result)` where:
553    /// - `sign` is the sign factor (-1, 0, or +1)
554    /// - `result` is the blade index of the interior product
555    ///
556    /// # Example
557    ///
558    /// ```
559    /// use clifford_codegen::algebra::{Algebra, ProductTable};
560    ///
561    /// let algebra = Algebra::euclidean(3);
562    /// let table = ProductTable::new(&algebra);
563    ///
564    /// // e12 · e2 = e1 (grade |2 - 1| = 1)
565    /// let (sign, result) = table.interior(3, 2);
566    /// assert_eq!(sign, 1);
567    /// assert_eq!(result, 1); // e1
568    ///
569    /// // e1 · e2 = 0 (orthogonal, grade |1 - 1| = 0 but no scalar part)
570    /// let (sign, _) = table.interior(1, 2);
571    /// assert_eq!(sign, 0);
572    /// ```
573    pub fn interior(&self, a: usize, b: usize) -> (i8, usize) {
574        let grade_a = a.count_ones() as usize;
575        let grade_b = b.count_ones() as usize;
576        let target_grade = grade_a.abs_diff(grade_b);
577
578        let (sign, result) = self.geometric(a, b);
579
580        if sign != 0 && result.count_ones() as usize == target_grade {
581            (sign, result)
582        } else {
583            (0, 0)
584        }
585    }
586
587    /// Computes the scalar product of two basis blades.
588    ///
589    /// The scalar product extracts only the grade-0 (scalar) part of the
590    /// geometric product. This is equivalent to the dot product for same-grade
591    /// blades, but returns zero for different grades.
592    ///
593    /// # Returns
594    ///
595    /// A tuple `(sign, result)` where:
596    /// - `sign` is the sign factor (-1, 0, or +1)
597    /// - `result` is always 0 (scalar) when non-zero
598    ///
599    /// # Example
600    ///
601    /// ```
602    /// use clifford_codegen::algebra::{Algebra, ProductTable};
603    ///
604    /// let algebra = Algebra::euclidean(3);
605    /// let table = ProductTable::new(&algebra);
606    ///
607    /// // e1 * e1 = 1 (scalar)
608    /// let (sign, result) = table.scalar(1, 1);
609    /// assert_eq!(sign, 1);
610    /// assert_eq!(result, 0);
611    ///
612    /// // e1 * e2 has no scalar part
613    /// let (sign, _) = table.scalar(1, 2);
614    /// assert_eq!(sign, 0);
615    /// ```
616    pub fn scalar(&self, a: usize, b: usize) -> (i8, usize) {
617        let (sign, result) = self.geometric(a, b);
618
619        if sign != 0 && result == 0 {
620            (sign, 0)
621        } else {
622            (0, 0)
623        }
624    }
625
626    /// Computes the antiscalar product of two basis blades.
627    ///
628    /// The antiscalar product extracts only the grade-n (pseudoscalar) part
629    /// of the antiproduct, where n is the dimension of the algebra.
630    ///
631    /// # Returns
632    ///
633    /// A tuple `(sign, result)` where:
634    /// - `sign` is the sign factor (-1, 0, or +1)
635    /// - `result` is the pseudoscalar blade index when non-zero
636    ///
637    /// # Example
638    ///
639    /// ```
640    /// use clifford_codegen::algebra::{Algebra, ProductTable};
641    ///
642    /// let algebra = Algebra::pga(3);
643    /// let table = ProductTable::new(&algebra);
644    ///
645    /// // Pseudoscalar index for 4D algebra (PGA 3D has 4 basis vectors)
646    /// let pseudoscalar = (1 << 4) - 1; // 0b1111 = 15
647    ///
648    /// // e1234 ∨ e1234 = e1234 (antiscalar part)
649    /// // In the antiproduct, pseudoscalar acts like scalar does in geometric product
650    /// let (sign, result) = table.antiscalar(pseudoscalar, pseudoscalar);
651    /// assert_eq!(sign, 1);
652    /// assert_eq!(result, pseudoscalar);
653    ///
654    /// // 1 ∨ e1234 has no antiscalar part (result is scalar, not pseudoscalar)
655    /// let (sign, _) = table.antiscalar(0, pseudoscalar);
656    /// assert_eq!(sign, 0);
657    /// ```
658    pub fn antiscalar(&self, a: usize, b: usize) -> (i8, usize) {
659        let dim = self.dim;
660        let pseudoscalar = (1 << dim) - 1;
661
662        let (sign, result) = self.antiproduct(a, b);
663
664        if sign != 0 && result == pseudoscalar {
665            (sign, result)
666        } else {
667            (0, 0)
668        }
669    }
670
671    /// Computes the dot product (scalar product) of two basis blades.
672    ///
673    /// The dot product `a • b` is non-zero only when the blades have the
674    /// same grade. It extracts the scalar part of the geometric product
675    /// for equal-grade blades.
676    ///
677    /// # Returns
678    ///
679    /// A tuple `(sign, result)` where:
680    /// - `sign` is the sign factor (-1, 0, or +1)
681    /// - `result` is always 0 (scalar) when non-zero
682    ///
683    /// # Example
684    ///
685    /// ```
686    /// use clifford_codegen::algebra::{Algebra, ProductTable};
687    ///
688    /// let algebra = Algebra::euclidean(3);
689    /// let table = ProductTable::new(&algebra);
690    ///
691    /// // e1 • e1 = 1 (same grade, scalar result)
692    /// let (sign, result) = table.dot(1, 1);
693    /// assert_eq!(sign, 1);
694    /// assert_eq!(result, 0); // scalar
695    ///
696    /// // e1 • e2 = 0 (orthogonal vectors)
697    /// let (sign, _) = table.dot(1, 2);
698    /// assert_eq!(sign, 0);
699    ///
700    /// // e1 • e12 = 0 (different grades)
701    /// let (sign, _) = table.dot(1, 3);
702    /// assert_eq!(sign, 0);
703    /// ```
704    pub fn dot(&self, a: usize, b: usize) -> (i8, usize) {
705        let grade_a = a.count_ones() as usize;
706        let grade_b = b.count_ones() as usize;
707
708        // Dot product is zero if grades don't match
709        if grade_a != grade_b {
710            return (0, 0);
711        }
712
713        let (sign, result) = self.geometric(a, b);
714
715        // For equal grades, dot product extracts the scalar part
716        if sign != 0 && result == 0 {
717            (sign, result)
718        } else {
719            (0, 0)
720        }
721    }
722
723    /// Computes the antidot product of two basis blades.
724    ///
725    /// The antidot product `a ⊚ b` is the De Morgan dual of the dot product:
726    /// `a ⊚ b = ∁a • ∁b` (complement dot complement).
727    ///
728    /// Like the dot product, it is non-zero only when blades have the same
729    /// antigrade (which is equivalent to same grade). Returns a scalar.
730    ///
731    /// # Returns
732    ///
733    /// A tuple `(sign, result)` where:
734    /// - `sign` is the sign factor (-1, 0, or +1)
735    /// - `result` is always 0 (scalar) when non-zero
736    ///
737    /// # Example
738    ///
739    /// ```
740    /// use clifford_codegen::algebra::{Algebra, ProductTable};
741    ///
742    /// let algebra = Algebra::euclidean(3);
743    /// let table = ProductTable::new(&algebra);
744    ///
745    /// // e1 ⊚ e1 (same grade/antigrade)
746    /// let (sign, result) = table.antidot(1, 1);
747    /// assert_eq!(result, 0); // scalar
748    /// ```
749    pub fn antidot(&self, a: usize, b: usize) -> (i8, usize) {
750        let grade_a = a.count_ones() as usize;
751        let grade_b = b.count_ones() as usize;
752
753        // Antidot product is zero if grades don't match
754        // (same antigrade means same grade since antigrade = dim - grade)
755        if grade_a != grade_b {
756            return (0, 0);
757        }
758
759        // Get complements
760        let (sign_ca, comp_a) = self.complement(a);
761        let (sign_cb, comp_b) = self.complement(b);
762
763        // Dot product of complements
764        let (sign_dot, result) = self.dot(comp_a, comp_b);
765        if sign_dot == 0 {
766            return (0, 0);
767        }
768
769        // Total sign
770        let total_sign = sign_ca * sign_cb * sign_dot;
771
772        (total_sign, result)
773    }
774
775    /// Computes the left anti-contraction of two basis blades.
776    ///
777    /// The left anti-contraction is the dual of the left contraction:
778    /// `a ⌋̄ b = ∁(∁a ⌋ ∁b)`
779    ///
780    /// This extracts the antigrade `antigrade(b) - antigrade(a)` part.
781    ///
782    /// # Returns
783    ///
784    /// A tuple `(sign, result)` where:
785    /// - `sign` is the sign factor (-1, 0, or +1)
786    /// - `result` is the blade index of the anti-contraction
787    pub fn left_contraction_anti(&self, a: usize, b: usize) -> (i8, usize) {
788        // Get complements
789        let (sign_ca, comp_a) = self.complement(a);
790        let (sign_cb, comp_b) = self.complement(b);
791
792        // Left contraction of complements
793        let (sign_contract, contract_result) = self.left_contraction(comp_a, comp_b);
794        if sign_contract == 0 {
795            return (0, 0);
796        }
797
798        // Complement of the result
799        let (sign_result, result) = self.complement(contract_result);
800
801        // Total sign
802        let total_sign = sign_ca * sign_cb * sign_contract * sign_result;
803
804        (total_sign, result)
805    }
806
807    /// Computes the right anti-contraction of two basis blades.
808    ///
809    /// The right anti-contraction is the dual of the right contraction:
810    /// `a ⌊̄ b = ∁(∁a ⌊ ∁b)`
811    ///
812    /// # Returns
813    ///
814    /// A tuple `(sign, result)` where:
815    /// - `sign` is the sign factor (-1, 0, or +1)
816    /// - `result` is the blade index of the anti-contraction
817    pub fn right_contraction_anti(&self, a: usize, b: usize) -> (i8, usize) {
818        // Get complements
819        let (sign_ca, comp_a) = self.complement(a);
820        let (sign_cb, comp_b) = self.complement(b);
821
822        // Right contraction of complements
823        let (sign_contract, contract_result) = self.right_contraction(comp_a, comp_b);
824        if sign_contract == 0 {
825            return (0, 0);
826        }
827
828        // Complement of the result
829        let (sign_result, result) = self.complement(contract_result);
830
831        // Total sign
832        let total_sign = sign_ca * sign_cb * sign_contract * sign_result;
833
834        (total_sign, result)
835    }
836
837    /// Computes the antidot product of two basis blades.
838    ///
839    /// The antidot product is the dual of the dot product:
840    /// `a ◯ b = ∁(∁a • ∁b)`
841    ///
842    /// It is non-zero only when the blades have the same antigrade (dual grade).
843    ///
844    /// # Returns
845    ///
846    /// A tuple `(sign, result)` where:
847    /// - `sign` is the sign factor (-1, 0, or +1)
848    /// - `result` is the blade index (pseudoscalar when non-zero)
849    ///
850    /// # Example
851    ///
852    /// ```
853    /// use clifford_codegen::algebra::{Algebra, ProductTable};
854    ///
855    /// let algebra = Algebra::euclidean(3);
856    /// let table = ProductTable::new(&algebra);
857    ///
858    /// // e23 ◯ e23 in 3D: complements are e1, e1 • e1 = 1, complement(1) = e123
859    /// let (sign, result) = table.dot_anti(6, 6);
860    /// assert_ne!(sign, 0);
861    /// assert_eq!(result, 7); // pseudoscalar
862    /// ```
863    pub fn dot_anti(&self, a: usize, b: usize) -> (i8, usize) {
864        // Get complements
865        let (sign_ca, comp_a) = self.complement(a);
866        let (sign_cb, comp_b) = self.complement(b);
867
868        // Dot product of complements
869        let (sign_dot, dot_result) = self.dot(comp_a, comp_b);
870        if sign_dot == 0 {
871            return (0, 0);
872        }
873
874        // Complement of the result
875        let (sign_result, result) = self.complement(dot_result);
876
877        // Total sign
878        let total_sign = sign_ca * sign_cb * sign_dot * sign_result;
879
880        (total_sign, result)
881    }
882
883    /// Computes the bulk dual (metric dual) of a basis blade.
884    ///
885    /// The bulk dual u★ is defined as: u★ = ũ ⋉ 𝟙
886    /// where ũ is the reverse and ⋉ is the geometric product with the pseudoscalar.
887    ///
888    /// The bulk dual is the "complement of the bulk components" - it uses the
889    /// metric to map a blade to its orthogonal complement in the full algebra.
890    ///
891    /// # Returns
892    ///
893    /// A tuple `(sign, result)` where:
894    /// - `sign` is the sign factor (-1, 0, or +1)
895    /// - `result` is the blade index of the bulk dual
896    ///
897    /// # Example
898    ///
899    /// ```
900    /// use clifford_codegen::algebra::{Algebra, ProductTable};
901    ///
902    /// let algebra = Algebra::euclidean(3);
903    /// let table = ProductTable::new(&algebra);
904    ///
905    /// // e1★ in 3D: reverse(e1) = e1, e1 * e123 = e23
906    /// let (sign, result) = table.bulk_dual(1);
907    /// assert_eq!(result, 6); // e23
908    /// ```
909    pub fn bulk_dual(&self, blade: usize) -> (i8, usize) {
910        let pseudoscalar = self.num_blades() - 1;
911        let grade = blade.count_ones() as usize;
912
913        // Compute reverse sign: (-1)^(k(k-1)/2)
914        let reverse_sign = super::grade::reverse_sign(grade);
915
916        // Compute geometric product with pseudoscalar
917        let (geo_sign, result) = self.geometric(blade, pseudoscalar);
918
919        // Total sign = reverse_sign * geo_sign
920        (reverse_sign * geo_sign, result)
921    }
922
923    /// Computes the weight dual (metric antidual) of a basis blade.
924    ///
925    /// The weight dual u☆ is defined as: u☆ = ũ ⋇ 1
926    /// where ũ is the reverse and ⋇ is the geometric antiproduct with the scalar.
927    ///
928    /// The weight dual is the "complement of the weight components" - it uses the
929    /// anti-metric to map a blade to its orthogonal complement.
930    ///
931    /// # Returns
932    ///
933    /// A tuple `(sign, result)` where:
934    /// - `sign` is the sign factor (-1, 0, or +1)
935    /// - `result` is the blade index of the weight dual
936    ///
937    /// # Example
938    ///
939    /// ```
940    /// use clifford_codegen::algebra::{Algebra, ProductTable};
941    ///
942    /// let algebra = Algebra::euclidean(3);
943    /// let table = ProductTable::new(&algebra);
944    ///
945    /// // e1☆ in 3D uses the antiproduct with scalar
946    /// let (sign, result) = table.weight_dual(1);
947    /// assert_ne!(sign, 0);
948    /// ```
949    pub fn weight_dual(&self, blade: usize) -> (i8, usize) {
950        let scalar = 0;
951        let grade = blade.count_ones() as usize;
952
953        // Compute reverse sign: (-1)^(k(k-1)/2)
954        let reverse_sign = super::grade::reverse_sign(grade);
955
956        // Compute antiproduct with scalar
957        let (anti_sign, result) = self.antiproduct(blade, scalar);
958
959        // Total sign = reverse_sign * anti_sign
960        (reverse_sign * anti_sign, result)
961    }
962
963    /// Computes the left bulk dual of a basis blade.
964    ///
965    /// The left bulk dual is: ★u = 𝟙 ⋉ ũ
966    /// This is the "left version" where the pseudoscalar is on the left.
967    ///
968    /// # Returns
969    ///
970    /// A tuple `(sign, result)` where:
971    /// - `sign` is the sign factor (-1, 0, or +1)
972    /// - `result` is the blade index of the left bulk dual
973    pub fn left_bulk_dual(&self, blade: usize) -> (i8, usize) {
974        let pseudoscalar = self.num_blades() - 1;
975        let grade = blade.count_ones() as usize;
976
977        // Compute reverse sign
978        let reverse_sign = super::grade::reverse_sign(grade);
979
980        // Compute geometric product: pseudoscalar * blade
981        let (geo_sign, result) = self.geometric(pseudoscalar, blade);
982
983        (reverse_sign * geo_sign, result)
984    }
985
986    /// Computes the left weight dual of a basis blade.
987    ///
988    /// The left weight dual is: ☆u = 1 ⋇ ũ
989    /// This is the "left version" where the scalar is on the left in the antiproduct.
990    ///
991    /// # Returns
992    ///
993    /// A tuple `(sign, result)` where:
994    /// - `sign` is the sign factor (-1, 0, or +1)
995    /// - `result` is the blade index of the left weight dual
996    pub fn left_weight_dual(&self, blade: usize) -> (i8, usize) {
997        let scalar = 0;
998        let grade = blade.count_ones() as usize;
999
1000        // Compute reverse sign
1001        let reverse_sign = super::grade::reverse_sign(grade);
1002
1003        // Compute antiproduct: scalar ⊛ blade
1004        let (anti_sign, result) = self.antiproduct(scalar, blade);
1005
1006        (reverse_sign * anti_sign, result)
1007    }
1008
1009    // ========================================================================
1010    // Interior Products (Contractions and Expansions)
1011    // ========================================================================
1012
1013    /// Computes the bulk contraction of two basis blades.
1014    ///
1015    /// The bulk contraction is defined as: a ∨ b★
1016    /// where ∨ is the antiwedge and ★ is the bulk dual.
1017    ///
1018    /// This is one of the four RGA interior products. It reduces grade.
1019    ///
1020    /// # Returns
1021    ///
1022    /// A tuple `(sign, result)` where:
1023    /// - `sign` is the sign factor (-1, 0, or +1)
1024    /// - `result` is the blade index of the bulk contraction
1025    pub fn bulk_contraction(&self, a: usize, b: usize) -> (i8, usize) {
1026        let (dual_sign, b_dual) = self.bulk_dual(b);
1027        if dual_sign == 0 {
1028            return (0, 0);
1029        }
1030        let (reg_sign, result) = self.regressive(a, b_dual);
1031        (dual_sign * reg_sign, result)
1032    }
1033
1034    /// Computes the weight contraction of two basis blades.
1035    ///
1036    /// The weight contraction is defined as: a ∨ b☆
1037    /// where ∨ is the antiwedge and ☆ is the weight dual.
1038    ///
1039    /// This is one of the four RGA interior products. It reduces grade.
1040    ///
1041    /// # Returns
1042    ///
1043    /// A tuple `(sign, result)` where:
1044    /// - `sign` is the sign factor (-1, 0, or +1)
1045    /// - `result` is the blade index of the weight contraction
1046    pub fn weight_contraction(&self, a: usize, b: usize) -> (i8, usize) {
1047        let (dual_sign, b_dual) = self.weight_dual(b);
1048        if dual_sign == 0 {
1049            return (0, 0);
1050        }
1051        let (reg_sign, result) = self.regressive(a, b_dual);
1052        (dual_sign * reg_sign, result)
1053    }
1054
1055    /// Computes the bulk expansion of two basis blades.
1056    ///
1057    /// The bulk expansion is defined as: a ∧ b★
1058    /// where ∧ is the wedge and ★ is the bulk dual.
1059    ///
1060    /// This is one of the four RGA interior products. It reduces antigrade.
1061    ///
1062    /// # Returns
1063    ///
1064    /// A tuple `(sign, result)` where:
1065    /// - `sign` is the sign factor (-1, 0, or +1)
1066    /// - `result` is the blade index of the bulk expansion
1067    pub fn bulk_expansion(&self, a: usize, b: usize) -> (i8, usize) {
1068        let (dual_sign, b_dual) = self.bulk_dual(b);
1069        if dual_sign == 0 {
1070            return (0, 0);
1071        }
1072        let (ext_sign, result) = self.exterior(a, b_dual);
1073        (dual_sign * ext_sign, result)
1074    }
1075
1076    /// Computes the weight expansion of two basis blades.
1077    ///
1078    /// The weight expansion is defined as: a ∧ b☆
1079    /// where ∧ is the wedge and ☆ is the weight dual.
1080    ///
1081    /// This is one of the four RGA interior products. It reduces antigrade.
1082    ///
1083    /// # Returns
1084    ///
1085    /// A tuple `(sign, result)` where:
1086    /// - `sign` is the sign factor (-1, 0, or +1)
1087    /// - `result` is the blade index of the weight expansion
1088    pub fn weight_expansion(&self, a: usize, b: usize) -> (i8, usize) {
1089        let (dual_sign, b_dual) = self.weight_dual(b);
1090        if dual_sign == 0 {
1091            return (0, 0);
1092        }
1093        let (ext_sign, result) = self.exterior(a, b_dual);
1094        (dual_sign * ext_sign, result)
1095    }
1096
1097    /// Computes the projection of `a` onto `b`.
1098    ///
1099    /// The projection is defined as: b ∨ (a ∧ b☆)
1100    /// where ∨ is the antiwedge, ∧ is the wedge, and ☆ is the weight dual.
1101    ///
1102    /// # Returns
1103    ///
1104    /// A tuple `(sign, result)` where:
1105    /// - `sign` is the sign factor (-1, 0, or +1)
1106    /// - `result` is the blade index of the projection
1107    pub fn project(&self, a: usize, b: usize) -> (i8, usize) {
1108        // Step 1: Compute weight dual of b
1109        let (dual_sign, b_dual) = self.weight_dual(b);
1110        if dual_sign == 0 {
1111            return (0, 0);
1112        }
1113
1114        // Step 2: Compute a ∧ b☆
1115        let (ext_sign, wedge_result) = self.exterior(a, b_dual);
1116        if ext_sign == 0 {
1117            return (0, 0);
1118        }
1119
1120        // Step 3: Compute b ∨ (a ∧ b☆)
1121        let (reg_sign, result) = self.regressive(b, wedge_result);
1122        (dual_sign * ext_sign * reg_sign, result)
1123    }
1124
1125    /// Computes the projection contribution for a triple of blades.
1126    ///
1127    /// For multi-blade types, the projection `B ∨ (A ∧ B☆)` expands to:
1128    /// `Σ_{i,j,k} b_k ∨ (a_i ∧ b_j☆)`
1129    ///
1130    /// This method computes one term: `b_antiwedge ∨ (a ∧ b_dual☆)`
1131    ///
1132    /// # Arguments
1133    /// - `a`: The source blade index
1134    /// - `b_dual`: The target blade to take weight dual of
1135    /// - `b_antiwedge`: The target blade for the final antiwedge
1136    ///
1137    /// # Returns
1138    /// A tuple `(sign, result)` for this contribution.
1139    pub fn project_triple(&self, a: usize, b_dual: usize, b_antiwedge: usize) -> (i8, usize) {
1140        // Step 1: Compute weight dual of b_dual
1141        let (dual_sign, b_dual_result) = self.weight_dual(b_dual);
1142        if dual_sign == 0 {
1143            return (0, 0);
1144        }
1145
1146        // Step 2: Compute a ∧ b_dual☆
1147        let (ext_sign, wedge_result) = self.exterior(a, b_dual_result);
1148        if ext_sign == 0 {
1149            return (0, 0);
1150        }
1151
1152        // Step 3: Compute b_antiwedge ∨ (a ∧ b_dual☆)
1153        let (reg_sign, result) = self.regressive(b_antiwedge, wedge_result);
1154        (dual_sign * ext_sign * reg_sign, result)
1155    }
1156
1157    /// Computes the antiprojection contribution for a triple of blades.
1158    ///
1159    /// For multi-blade types, the antiprojection `B ∧ (A ∨ B☆)` expands to:
1160    /// `Σ_{i,j,k} b_k ∧ (a_i ∨ b_j☆)`
1161    ///
1162    /// This method computes one term: `b_wedge ∧ (a ∨ b_dual☆)`
1163    ///
1164    /// # Arguments
1165    /// - `a`: The source blade index
1166    /// - `b_dual`: The target blade to take weight dual of
1167    /// - `b_wedge`: The target blade for the final wedge
1168    ///
1169    /// # Returns
1170    /// A tuple `(sign, result)` for this contribution.
1171    pub fn antiproject_triple(&self, a: usize, b_dual: usize, b_wedge: usize) -> (i8, usize) {
1172        // Step 1: Compute weight dual of b_dual
1173        let (dual_sign, b_dual_result) = self.weight_dual(b_dual);
1174        if dual_sign == 0 {
1175            return (0, 0);
1176        }
1177
1178        // Step 2: Compute a ∨ b_dual☆
1179        let (reg_sign, antiwedge_result) = self.regressive(a, b_dual_result);
1180        if reg_sign == 0 {
1181            return (0, 0);
1182        }
1183
1184        // Step 3: Compute b_wedge ∧ (a ∨ b_dual☆)
1185        let (ext_sign, result) = self.exterior(b_wedge, antiwedge_result);
1186        (dual_sign * reg_sign * ext_sign, result)
1187    }
1188
1189    /// Computes the antiprojection of `a` onto `b`.
1190    ///
1191    /// The antiprojection is defined as: b ∧ (a ∨ b☆)
1192    /// where ∧ is the wedge, ∨ is the antiwedge, and ☆ is the weight dual.
1193    ///
1194    /// # Returns
1195    ///
1196    /// A tuple `(sign, result)` where:
1197    /// - `sign` is the sign factor (-1, 0, or +1)
1198    /// - `result` is the blade index of the antiprojection
1199    pub fn antiproject(&self, a: usize, b: usize) -> (i8, usize) {
1200        // Step 1: Compute weight dual of b
1201        let (dual_sign, b_dual) = self.weight_dual(b);
1202        if dual_sign == 0 {
1203            return (0, 0);
1204        }
1205
1206        // Step 2: Compute a ∨ b☆
1207        let (reg_sign, antiwedge_result) = self.regressive(a, b_dual);
1208        if reg_sign == 0 {
1209            return (0, 0);
1210        }
1211
1212        // Step 3: Compute b ∧ (a ∨ b☆)
1213        let (ext_sign, result) = self.exterior(b, antiwedge_result);
1214        (dual_sign * reg_sign * ext_sign, result)
1215    }
1216}
1217
1218#[cfg(test)]
1219mod tests {
1220    use super::*;
1221
1222    #[test]
1223    fn euclidean_3d_basic() {
1224        let algebra = Algebra::euclidean(3);
1225        let table = ProductTable::new(&algebra);
1226
1227        assert_eq!(table.dim(), 3);
1228        assert_eq!(table.num_blades(), 8);
1229
1230        // Scalar * anything = anything
1231        for b in 0..8 {
1232            let (sign, result) = table.geometric(0, b);
1233            assert_eq!(sign, 1);
1234            assert_eq!(result, b);
1235        }
1236
1237        // Vectors square to +1
1238        assert_eq!(table.geometric(1, 1), (1, 0));
1239        assert_eq!(table.geometric(2, 2), (1, 0));
1240        assert_eq!(table.geometric(4, 4), (1, 0));
1241
1242        // Vectors anticommute
1243        assert_eq!(table.geometric(1, 2), (1, 3));
1244        assert_eq!(table.geometric(2, 1), (-1, 3));
1245    }
1246
1247    #[test]
1248    fn euclidean_3d_bivectors() {
1249        let algebra = Algebra::euclidean(3);
1250        let table = ProductTable::new(&algebra);
1251
1252        // Bivectors square to -1
1253        assert_eq!(table.geometric(3, 3), (-1, 0)); // e12 * e12 = -1
1254        assert_eq!(table.geometric(5, 5), (-1, 0)); // e13 * e13 = -1
1255        assert_eq!(table.geometric(6, 6), (-1, 0)); // e23 * e23 = -1
1256
1257        // e12 * e23 = e1 * e2 * e2 * e3 = e1 * e3 = e13
1258        let (sign, result) = table.geometric(3, 6);
1259        assert_eq!(result, 5); // e13
1260        assert_eq!(sign, 1);
1261    }
1262
1263    #[test]
1264    fn euclidean_3d_pseudoscalar() {
1265        let algebra = Algebra::euclidean(3);
1266        let table = ProductTable::new(&algebra);
1267
1268        // e123 * e123 = -1
1269        assert_eq!(table.geometric(7, 7), (-1, 0));
1270
1271        // e1 * e23 = e123
1272        assert_eq!(table.geometric(1, 6), (1, 7));
1273
1274        // e23 * e1 = e2 e3 e1 = -e2 e1 e3 = e1 e2 e3 = e123
1275        assert_eq!(table.geometric(6, 1), (1, 7));
1276    }
1277
1278    #[test]
1279    fn pga_degenerate() {
1280        let algebra = Algebra::pga(3);
1281        let table = ProductTable::new(&algebra);
1282
1283        // e4 is degenerate (index 8)
1284        assert_eq!(table.geometric(8, 8), (0, 0));
1285
1286        // Products involving e4 * e4 are zero
1287        // e14 * e14 = e1 e4 e1 e4 = -e1 e1 e4 e4 = -1 * 0 = 0
1288        assert_eq!(table.geometric(9, 9), (0, 0));
1289    }
1290
1291    #[test]
1292    fn product_contributions_vectors() {
1293        let algebra = Algebra::euclidean(3);
1294        let table = ProductTable::new(&algebra);
1295
1296        let vectors = vec![1, 2, 4];
1297
1298        // Contributions to e12 from vectors
1299        let contrib = table.product_contributions(&vectors, &vectors, 3);
1300        assert_eq!(contrib.len(), 2);
1301        assert!(contrib.contains(&(1, 1, 2)));
1302        assert!(contrib.contains(&(-1, 2, 1)));
1303
1304        // Contributions to scalar from vectors
1305        let contrib = table.product_contributions(&vectors, &vectors, 0);
1306        assert_eq!(contrib.len(), 3); // e1*e1, e2*e2, e3*e3
1307    }
1308
1309    #[test]
1310    fn all_products_vectors() {
1311        let algebra = Algebra::euclidean(3);
1312        let table = ProductTable::new(&algebra);
1313
1314        let vectors = vec![1, 2, 4];
1315        let products = table.all_products(&vectors, &vectors);
1316
1317        // vector * vector produces scalar + bivectors
1318        // Scalars: e1*e1, e2*e2, e3*e3
1319        // Bivectors: e1*e2, e2*e1, e1*e3, e3*e1, e2*e3, e3*e2
1320
1321        // Check we get scalar and 3 bivectors
1322        let blades: Vec<usize> = products.iter().map(|(b, _)| *b).collect();
1323        assert!(blades.contains(&0)); // scalar
1324        assert!(blades.contains(&3)); // e12
1325        assert!(blades.contains(&5)); // e13
1326        assert!(blades.contains(&6)); // e23
1327    }
1328
1329    #[test]
1330    fn has_contributions_to_grade_check() {
1331        let algebra = Algebra::euclidean(3);
1332        let table = ProductTable::new(&algebra);
1333
1334        let vectors = vec![1, 2, 4];
1335        let bivectors = vec![3, 5, 6];
1336
1337        // vector * vector contributes to grades 0 and 2
1338        assert!(table.has_contributions_to_grade(&vectors, &vectors, 0));
1339        assert!(table.has_contributions_to_grade(&vectors, &vectors, 2));
1340        assert!(!table.has_contributions_to_grade(&vectors, &vectors, 1));
1341        assert!(!table.has_contributions_to_grade(&vectors, &vectors, 3));
1342
1343        // bivector * vector contributes to grades 1 and 3
1344        assert!(table.has_contributions_to_grade(&bivectors, &vectors, 1));
1345        assert!(table.has_contributions_to_grade(&bivectors, &vectors, 3));
1346        assert!(!table.has_contributions_to_grade(&bivectors, &vectors, 0));
1347        assert!(!table.has_contributions_to_grade(&bivectors, &vectors, 2));
1348    }
1349
1350    #[test]
1351    fn pga_antiproduct_pseudoscalar_is_identity() {
1352        // PGA (3,0,1): e1, e2, e3 square to +1, e4 squares to 0
1353        // The PSEUDOSCALAR (e1234) acts as identity for antiproduct
1354        // (dual to scalar being identity for geometric product)
1355        let algebra = Algebra::pga(3);
1356        let table = ProductTable::new(&algebra);
1357
1358        let pseudoscalar = 15; // e1234
1359        let e1 = 1;
1360        let e2 = 2;
1361        let e3 = 4;
1362        let e4 = 8;
1363
1364        // Pseudoscalar ⊛ anything = ±anything (identity up to sign)
1365        let (sign, result) = table.antiproduct(pseudoscalar, e1);
1366        assert_eq!(result, e1, "pseudoscalar ⊛ e1 should give e1");
1367        assert_ne!(sign, 0);
1368
1369        let (sign, result) = table.antiproduct(pseudoscalar, e2);
1370        assert_eq!(result, e2, "pseudoscalar ⊛ e2 should give e2");
1371        assert_ne!(sign, 0);
1372
1373        let (sign, result) = table.antiproduct(pseudoscalar, e3);
1374        assert_eq!(result, e3, "pseudoscalar ⊛ e3 should give e3");
1375        assert_ne!(sign, 0);
1376
1377        let (sign, result) = table.antiproduct(pseudoscalar, e4);
1378        assert_eq!(result, e4, "pseudoscalar ⊛ e4 should give e4");
1379        assert_ne!(sign, 0);
1380    }
1381
1382    #[test]
1383    fn pga_antiproduct_scalar_behavior() {
1384        // With the correct antiproduct formula (∁(∁a × ∁b) using regular geometric):
1385        // scalar ⊛ non-degenerate-basis: complement(e1234 × complement(ei)) vanishes
1386        //   because e1234 × e_ijk involves degenerate e0 in the metric
1387        // scalar ⊛ degenerate-basis (e4/e0): complement(e1234 × e123) is NON-ZERO
1388        //   because e1234 × e123 = ±e0 (only Euclidean bases in metric)
1389        let algebra = Algebra::pga(3);
1390        let table = ProductTable::new(&algebra);
1391
1392        let scalar = 0;
1393        let e1 = 1; // non-degenerate
1394        let e4 = 8; // degenerate
1395
1396        // scalar ⊛ e1 vanishes because the intermediate product involves e0
1397        let (sign, _result) = table.antiproduct(scalar, e1);
1398        assert_eq!(
1399            sign, 0,
1400            "scalar ⊛ e1 vanishes (intermediate uses degenerate e0)"
1401        );
1402
1403        // scalar ⊛ e4 (e0) is NON-ZERO because e1234 × e123 uses only Euclidean bases
1404        let (sign, result) = table.antiproduct(scalar, e4);
1405        assert_ne!(sign, 0, "scalar ⊛ e4 should not vanish");
1406        assert_eq!(result, 7, "scalar ⊛ e4 should give e123");
1407    }
1408
1409    #[test]
1410    fn pga_antiproduct_motor_point_nonzero() {
1411        // Motor components and point components should produce non-zero antiproducts
1412        let algebra = Algebra::pga(3);
1413        let table = ProductTable::new(&algebra);
1414
1415        // Motor has scalar and e0i components (bivectors containing e4)
1416        // In 4D PGA: e14 = 9, e24 = 10, e34 = 12
1417        let e14 = 9;
1418        let e1 = 1;
1419
1420        // e14 ⊛ e1 should be non-zero (translation affects points)
1421        let (sign, _result) = table.antiproduct(e14, e1);
1422        assert_ne!(sign, 0, "e14 ⊛ e1 should not vanish");
1423    }
1424
1425    #[test]
1426    fn euclidean_antiproduct_same_as_geometric() {
1427        // In Euclidean algebras (no degenerate directions), the antiproduct
1428        // should behave similarly to the geometric product (up to sign/complement)
1429        let algebra = Algebra::euclidean(3);
1430        let table = ProductTable::new(&algebra);
1431
1432        // Antiscalar in 3D Euclidean is e123 (index 7)
1433        let antiscalar = 7;
1434        let e1 = 1;
1435
1436        // e123 ⊛ e1 = e1 (identity property)
1437        assert_eq!(table.antiproduct(antiscalar, e1), (1, e1));
1438    }
1439}
1440
1441// =============================================================================
1442// Comprehensive Cayley Table Tests
1443// =============================================================================
1444// These tests verify all products match the RGA wiki definitions:
1445// https://rigidgeometricalgebra.org/wiki/index.php
1446//
1447// All products are derived SOLELY from the signature (p, q, r).
1448// No algebra-specific branching or name checking.
1449// =============================================================================
1450// Complete 16×16 Cayley table tests for 3D PGA (G₃,₀,₁)
1451// Each test verifies all 256 entries of a product table.
1452// =============================================================================
1453
1454#[cfg(test)]
1455mod full_cayley_tests {
1456    use crate::algebra::{Algebra, ProductTable};
1457
1458    #[test]
1459    fn pga_3d_full_geometric_table() {
1460        let algebra = Algebra::pga(3);
1461        let table = ProductTable::new(&algebra);
1462
1463        // Row 1
1464        assert_eq!(table.geometric(0, 0), (1, 0));
1465        assert_eq!(table.geometric(0, 1), (1, 1));
1466        assert_eq!(table.geometric(0, 2), (1, 2));
1467        assert_eq!(table.geometric(0, 3), (1, 3));
1468        assert_eq!(table.geometric(0, 4), (1, 4));
1469        assert_eq!(table.geometric(0, 5), (1, 5));
1470        assert_eq!(table.geometric(0, 6), (1, 6));
1471        assert_eq!(table.geometric(0, 7), (1, 7));
1472        assert_eq!(table.geometric(0, 8), (1, 8));
1473        assert_eq!(table.geometric(0, 9), (1, 9));
1474        assert_eq!(table.geometric(0, 10), (1, 10));
1475        assert_eq!(table.geometric(0, 11), (1, 11));
1476        assert_eq!(table.geometric(0, 12), (1, 12));
1477        assert_eq!(table.geometric(0, 13), (1, 13));
1478        assert_eq!(table.geometric(0, 14), (1, 14));
1479        assert_eq!(table.geometric(0, 15), (1, 15));
1480        // Row e1
1481        assert_eq!(table.geometric(1, 0), (1, 1));
1482        assert_eq!(table.geometric(1, 1), (1, 0));
1483        assert_eq!(table.geometric(1, 2), (1, 3));
1484        assert_eq!(table.geometric(1, 3), (1, 2));
1485        assert_eq!(table.geometric(1, 4), (1, 5));
1486        assert_eq!(table.geometric(1, 5), (1, 4));
1487        assert_eq!(table.geometric(1, 6), (1, 7));
1488        assert_eq!(table.geometric(1, 7), (1, 6));
1489        assert_eq!(table.geometric(1, 8), (1, 9));
1490        assert_eq!(table.geometric(1, 9), (1, 8));
1491        assert_eq!(table.geometric(1, 10), (1, 11));
1492        assert_eq!(table.geometric(1, 11), (1, 10));
1493        assert_eq!(table.geometric(1, 12), (1, 13));
1494        assert_eq!(table.geometric(1, 13), (1, 12));
1495        assert_eq!(table.geometric(1, 14), (1, 15));
1496        assert_eq!(table.geometric(1, 15), (1, 14));
1497        // Row e2
1498        assert_eq!(table.geometric(2, 0), (1, 2));
1499        assert_eq!(table.geometric(2, 1), (-1, 3));
1500        assert_eq!(table.geometric(2, 2), (1, 0));
1501        assert_eq!(table.geometric(2, 3), (-1, 1));
1502        assert_eq!(table.geometric(2, 4), (1, 6));
1503        assert_eq!(table.geometric(2, 5), (-1, 7));
1504        assert_eq!(table.geometric(2, 6), (1, 4));
1505        assert_eq!(table.geometric(2, 7), (-1, 5));
1506        assert_eq!(table.geometric(2, 8), (1, 10));
1507        assert_eq!(table.geometric(2, 9), (-1, 11));
1508        assert_eq!(table.geometric(2, 10), (1, 8));
1509        assert_eq!(table.geometric(2, 11), (-1, 9));
1510        assert_eq!(table.geometric(2, 12), (1, 14));
1511        assert_eq!(table.geometric(2, 13), (-1, 15));
1512        assert_eq!(table.geometric(2, 14), (1, 12));
1513        assert_eq!(table.geometric(2, 15), (-1, 13));
1514        // Row e12
1515        assert_eq!(table.geometric(3, 0), (1, 3));
1516        assert_eq!(table.geometric(3, 1), (-1, 2));
1517        assert_eq!(table.geometric(3, 2), (1, 1));
1518        assert_eq!(table.geometric(3, 3), (-1, 0));
1519        assert_eq!(table.geometric(3, 4), (1, 7));
1520        assert_eq!(table.geometric(3, 5), (-1, 6));
1521        assert_eq!(table.geometric(3, 6), (1, 5));
1522        assert_eq!(table.geometric(3, 7), (-1, 4));
1523        assert_eq!(table.geometric(3, 8), (1, 11));
1524        assert_eq!(table.geometric(3, 9), (-1, 10));
1525        assert_eq!(table.geometric(3, 10), (1, 9));
1526        assert_eq!(table.geometric(3, 11), (-1, 8));
1527        assert_eq!(table.geometric(3, 12), (1, 15));
1528        assert_eq!(table.geometric(3, 13), (-1, 14));
1529        assert_eq!(table.geometric(3, 14), (1, 13));
1530        assert_eq!(table.geometric(3, 15), (-1, 12));
1531        // Row e3
1532        assert_eq!(table.geometric(4, 0), (1, 4));
1533        assert_eq!(table.geometric(4, 1), (-1, 5));
1534        assert_eq!(table.geometric(4, 2), (-1, 6));
1535        assert_eq!(table.geometric(4, 3), (1, 7));
1536        assert_eq!(table.geometric(4, 4), (1, 0));
1537        assert_eq!(table.geometric(4, 5), (-1, 1));
1538        assert_eq!(table.geometric(4, 6), (-1, 2));
1539        assert_eq!(table.geometric(4, 7), (1, 3));
1540        assert_eq!(table.geometric(4, 8), (1, 12));
1541        assert_eq!(table.geometric(4, 9), (-1, 13));
1542        assert_eq!(table.geometric(4, 10), (-1, 14));
1543        assert_eq!(table.geometric(4, 11), (1, 15));
1544        assert_eq!(table.geometric(4, 12), (1, 8));
1545        assert_eq!(table.geometric(4, 13), (-1, 9));
1546        assert_eq!(table.geometric(4, 14), (-1, 10));
1547        assert_eq!(table.geometric(4, 15), (1, 11));
1548        // Row e13
1549        assert_eq!(table.geometric(5, 0), (1, 5));
1550        assert_eq!(table.geometric(5, 1), (-1, 4));
1551        assert_eq!(table.geometric(5, 2), (-1, 7));
1552        assert_eq!(table.geometric(5, 3), (1, 6));
1553        assert_eq!(table.geometric(5, 4), (1, 1));
1554        assert_eq!(table.geometric(5, 5), (-1, 0));
1555        assert_eq!(table.geometric(5, 6), (-1, 3));
1556        assert_eq!(table.geometric(5, 7), (1, 2));
1557        assert_eq!(table.geometric(5, 8), (1, 13));
1558        assert_eq!(table.geometric(5, 9), (-1, 12));
1559        assert_eq!(table.geometric(5, 10), (-1, 15));
1560        assert_eq!(table.geometric(5, 11), (1, 14));
1561        assert_eq!(table.geometric(5, 12), (1, 9));
1562        assert_eq!(table.geometric(5, 13), (-1, 8));
1563        assert_eq!(table.geometric(5, 14), (-1, 11));
1564        assert_eq!(table.geometric(5, 15), (1, 10));
1565        // Row e23
1566        assert_eq!(table.geometric(6, 0), (1, 6));
1567        assert_eq!(table.geometric(6, 1), (1, 7));
1568        assert_eq!(table.geometric(6, 2), (-1, 4));
1569        assert_eq!(table.geometric(6, 3), (-1, 5));
1570        assert_eq!(table.geometric(6, 4), (1, 2));
1571        assert_eq!(table.geometric(6, 5), (1, 3));
1572        assert_eq!(table.geometric(6, 6), (-1, 0));
1573        assert_eq!(table.geometric(6, 7), (-1, 1));
1574        assert_eq!(table.geometric(6, 8), (1, 14));
1575        assert_eq!(table.geometric(6, 9), (1, 15));
1576        assert_eq!(table.geometric(6, 10), (-1, 12));
1577        assert_eq!(table.geometric(6, 11), (-1, 13));
1578        assert_eq!(table.geometric(6, 12), (1, 10));
1579        assert_eq!(table.geometric(6, 13), (1, 11));
1580        assert_eq!(table.geometric(6, 14), (-1, 8));
1581        assert_eq!(table.geometric(6, 15), (-1, 9));
1582        // Row e123
1583        assert_eq!(table.geometric(7, 0), (1, 7));
1584        assert_eq!(table.geometric(7, 1), (1, 6));
1585        assert_eq!(table.geometric(7, 2), (-1, 5));
1586        assert_eq!(table.geometric(7, 3), (-1, 4));
1587        assert_eq!(table.geometric(7, 4), (1, 3));
1588        assert_eq!(table.geometric(7, 5), (1, 2));
1589        assert_eq!(table.geometric(7, 6), (-1, 1));
1590        assert_eq!(table.geometric(7, 7), (-1, 0));
1591        assert_eq!(table.geometric(7, 8), (1, 15));
1592        assert_eq!(table.geometric(7, 9), (1, 14));
1593        assert_eq!(table.geometric(7, 10), (-1, 13));
1594        assert_eq!(table.geometric(7, 11), (-1, 12));
1595        assert_eq!(table.geometric(7, 12), (1, 11));
1596        assert_eq!(table.geometric(7, 13), (1, 10));
1597        assert_eq!(table.geometric(7, 14), (-1, 9));
1598        assert_eq!(table.geometric(7, 15), (-1, 8));
1599        // Row e4
1600        assert_eq!(table.geometric(8, 0), (1, 8));
1601        assert_eq!(table.geometric(8, 1), (-1, 9));
1602        assert_eq!(table.geometric(8, 2), (-1, 10));
1603        assert_eq!(table.geometric(8, 3), (1, 11));
1604        assert_eq!(table.geometric(8, 4), (-1, 12));
1605        assert_eq!(table.geometric(8, 5), (1, 13));
1606        assert_eq!(table.geometric(8, 6), (1, 14));
1607        assert_eq!(table.geometric(8, 7), (-1, 15));
1608        assert_eq!(table.geometric(8, 8).0, 0);
1609        assert_eq!(table.geometric(8, 9).0, 0);
1610        assert_eq!(table.geometric(8, 10).0, 0);
1611        assert_eq!(table.geometric(8, 11).0, 0);
1612        assert_eq!(table.geometric(8, 12).0, 0);
1613        assert_eq!(table.geometric(8, 13).0, 0);
1614        assert_eq!(table.geometric(8, 14).0, 0);
1615        assert_eq!(table.geometric(8, 15).0, 0);
1616        // Row e14
1617        assert_eq!(table.geometric(9, 0), (1, 9));
1618        assert_eq!(table.geometric(9, 1), (-1, 8));
1619        assert_eq!(table.geometric(9, 2), (-1, 11));
1620        assert_eq!(table.geometric(9, 3), (1, 10));
1621        assert_eq!(table.geometric(9, 4), (-1, 13));
1622        assert_eq!(table.geometric(9, 5), (1, 12));
1623        assert_eq!(table.geometric(9, 6), (1, 15));
1624        assert_eq!(table.geometric(9, 7), (-1, 14));
1625        assert_eq!(table.geometric(9, 8).0, 0);
1626        assert_eq!(table.geometric(9, 9).0, 0);
1627        assert_eq!(table.geometric(9, 10).0, 0);
1628        assert_eq!(table.geometric(9, 11).0, 0);
1629        assert_eq!(table.geometric(9, 12).0, 0);
1630        assert_eq!(table.geometric(9, 13).0, 0);
1631        assert_eq!(table.geometric(9, 14).0, 0);
1632        assert_eq!(table.geometric(9, 15).0, 0);
1633        // Row e24
1634        assert_eq!(table.geometric(10, 0), (1, 10));
1635        assert_eq!(table.geometric(10, 1), (1, 11));
1636        assert_eq!(table.geometric(10, 2), (-1, 8));
1637        assert_eq!(table.geometric(10, 3), (-1, 9));
1638        assert_eq!(table.geometric(10, 4), (-1, 14));
1639        assert_eq!(table.geometric(10, 5), (-1, 15));
1640        assert_eq!(table.geometric(10, 6), (1, 12));
1641        assert_eq!(table.geometric(10, 7), (1, 13));
1642        assert_eq!(table.geometric(10, 8).0, 0);
1643        assert_eq!(table.geometric(10, 9).0, 0);
1644        assert_eq!(table.geometric(10, 10).0, 0);
1645        assert_eq!(table.geometric(10, 11).0, 0);
1646        assert_eq!(table.geometric(10, 12).0, 0);
1647        assert_eq!(table.geometric(10, 13).0, 0);
1648        assert_eq!(table.geometric(10, 14).0, 0);
1649        assert_eq!(table.geometric(10, 15).0, 0);
1650        // Row e124
1651        assert_eq!(table.geometric(11, 0), (1, 11));
1652        assert_eq!(table.geometric(11, 1), (1, 10));
1653        assert_eq!(table.geometric(11, 2), (-1, 9));
1654        assert_eq!(table.geometric(11, 3), (-1, 8));
1655        assert_eq!(table.geometric(11, 4), (-1, 15));
1656        assert_eq!(table.geometric(11, 5), (-1, 14));
1657        assert_eq!(table.geometric(11, 6), (1, 13));
1658        assert_eq!(table.geometric(11, 7), (1, 12));
1659        assert_eq!(table.geometric(11, 8).0, 0);
1660        assert_eq!(table.geometric(11, 9).0, 0);
1661        assert_eq!(table.geometric(11, 10).0, 0);
1662        assert_eq!(table.geometric(11, 11).0, 0);
1663        assert_eq!(table.geometric(11, 12).0, 0);
1664        assert_eq!(table.geometric(11, 13).0, 0);
1665        assert_eq!(table.geometric(11, 14).0, 0);
1666        assert_eq!(table.geometric(11, 15).0, 0);
1667        // Row e34
1668        assert_eq!(table.geometric(12, 0), (1, 12));
1669        assert_eq!(table.geometric(12, 1), (1, 13));
1670        assert_eq!(table.geometric(12, 2), (1, 14));
1671        assert_eq!(table.geometric(12, 3), (1, 15));
1672        assert_eq!(table.geometric(12, 4), (-1, 8));
1673        assert_eq!(table.geometric(12, 5), (-1, 9));
1674        assert_eq!(table.geometric(12, 6), (-1, 10));
1675        assert_eq!(table.geometric(12, 7), (-1, 11));
1676        assert_eq!(table.geometric(12, 8).0, 0);
1677        assert_eq!(table.geometric(12, 9).0, 0);
1678        assert_eq!(table.geometric(12, 10).0, 0);
1679        assert_eq!(table.geometric(12, 11).0, 0);
1680        assert_eq!(table.geometric(12, 12).0, 0);
1681        assert_eq!(table.geometric(12, 13).0, 0);
1682        assert_eq!(table.geometric(12, 14).0, 0);
1683        assert_eq!(table.geometric(12, 15).0, 0);
1684        // Row e134
1685        assert_eq!(table.geometric(13, 0), (1, 13));
1686        assert_eq!(table.geometric(13, 1), (1, 12));
1687        assert_eq!(table.geometric(13, 2), (1, 15));
1688        assert_eq!(table.geometric(13, 3), (1, 14));
1689        assert_eq!(table.geometric(13, 4), (-1, 9));
1690        assert_eq!(table.geometric(13, 5), (-1, 8));
1691        assert_eq!(table.geometric(13, 6), (-1, 11));
1692        assert_eq!(table.geometric(13, 7), (-1, 10));
1693        assert_eq!(table.geometric(13, 8).0, 0);
1694        assert_eq!(table.geometric(13, 9).0, 0);
1695        assert_eq!(table.geometric(13, 10).0, 0);
1696        assert_eq!(table.geometric(13, 11).0, 0);
1697        assert_eq!(table.geometric(13, 12).0, 0);
1698        assert_eq!(table.geometric(13, 13).0, 0);
1699        assert_eq!(table.geometric(13, 14).0, 0);
1700        assert_eq!(table.geometric(13, 15).0, 0);
1701        // Row e234
1702        assert_eq!(table.geometric(14, 0), (1, 14));
1703        assert_eq!(table.geometric(14, 1), (-1, 15));
1704        assert_eq!(table.geometric(14, 2), (1, 12));
1705        assert_eq!(table.geometric(14, 3), (-1, 13));
1706        assert_eq!(table.geometric(14, 4), (-1, 10));
1707        assert_eq!(table.geometric(14, 5), (1, 11));
1708        assert_eq!(table.geometric(14, 6), (-1, 8));
1709        assert_eq!(table.geometric(14, 7), (1, 9));
1710        assert_eq!(table.geometric(14, 8).0, 0);
1711        assert_eq!(table.geometric(14, 9).0, 0);
1712        assert_eq!(table.geometric(14, 10).0, 0);
1713        assert_eq!(table.geometric(14, 11).0, 0);
1714        assert_eq!(table.geometric(14, 12).0, 0);
1715        assert_eq!(table.geometric(14, 13).0, 0);
1716        assert_eq!(table.geometric(14, 14).0, 0);
1717        assert_eq!(table.geometric(14, 15).0, 0);
1718        // Row e1234
1719        assert_eq!(table.geometric(15, 0), (1, 15));
1720        assert_eq!(table.geometric(15, 1), (-1, 14));
1721        assert_eq!(table.geometric(15, 2), (1, 13));
1722        assert_eq!(table.geometric(15, 3), (-1, 12));
1723        assert_eq!(table.geometric(15, 4), (-1, 11));
1724        assert_eq!(table.geometric(15, 5), (1, 10));
1725        assert_eq!(table.geometric(15, 6), (-1, 9));
1726        assert_eq!(table.geometric(15, 7), (1, 8));
1727        assert_eq!(table.geometric(15, 8).0, 0);
1728        assert_eq!(table.geometric(15, 9).0, 0);
1729        assert_eq!(table.geometric(15, 10).0, 0);
1730        assert_eq!(table.geometric(15, 11).0, 0);
1731        assert_eq!(table.geometric(15, 12).0, 0);
1732        assert_eq!(table.geometric(15, 13).0, 0);
1733        assert_eq!(table.geometric(15, 14).0, 0);
1734        assert_eq!(table.geometric(15, 15).0, 0);
1735    }
1736
1737    #[test]
1738    fn pga_3d_full_exterior_table() {
1739        let algebra = Algebra::pga(3);
1740        let table = ProductTable::new(&algebra);
1741
1742        // Row 1
1743        assert_eq!(table.exterior(0, 0), (1, 0));
1744        assert_eq!(table.exterior(0, 1), (1, 1));
1745        assert_eq!(table.exterior(0, 2), (1, 2));
1746        assert_eq!(table.exterior(0, 3), (1, 3));
1747        assert_eq!(table.exterior(0, 4), (1, 4));
1748        assert_eq!(table.exterior(0, 5), (1, 5));
1749        assert_eq!(table.exterior(0, 6), (1, 6));
1750        assert_eq!(table.exterior(0, 7), (1, 7));
1751        assert_eq!(table.exterior(0, 8), (1, 8));
1752        assert_eq!(table.exterior(0, 9), (1, 9));
1753        assert_eq!(table.exterior(0, 10), (1, 10));
1754        assert_eq!(table.exterior(0, 11), (1, 11));
1755        assert_eq!(table.exterior(0, 12), (1, 12));
1756        assert_eq!(table.exterior(0, 13), (1, 13));
1757        assert_eq!(table.exterior(0, 14), (1, 14));
1758        assert_eq!(table.exterior(0, 15), (1, 15));
1759        // Row e1
1760        assert_eq!(table.exterior(1, 0), (1, 1));
1761        assert_eq!(table.exterior(1, 1), (0, 0));
1762        assert_eq!(table.exterior(1, 2), (1, 3));
1763        assert_eq!(table.exterior(1, 3), (0, 0));
1764        assert_eq!(table.exterior(1, 4), (1, 5));
1765        assert_eq!(table.exterior(1, 5), (0, 0));
1766        assert_eq!(table.exterior(1, 6), (1, 7));
1767        assert_eq!(table.exterior(1, 7), (0, 0));
1768        assert_eq!(table.exterior(1, 8), (1, 9));
1769        assert_eq!(table.exterior(1, 9), (0, 0));
1770        assert_eq!(table.exterior(1, 10), (1, 11));
1771        assert_eq!(table.exterior(1, 11), (0, 0));
1772        assert_eq!(table.exterior(1, 12), (1, 13));
1773        assert_eq!(table.exterior(1, 13), (0, 0));
1774        assert_eq!(table.exterior(1, 14), (1, 15));
1775        assert_eq!(table.exterior(1, 15), (0, 0));
1776        // Row e2
1777        assert_eq!(table.exterior(2, 0), (1, 2));
1778        assert_eq!(table.exterior(2, 1), (-1, 3));
1779        assert_eq!(table.exterior(2, 2), (0, 0));
1780        assert_eq!(table.exterior(2, 3), (0, 0));
1781        assert_eq!(table.exterior(2, 4), (1, 6));
1782        assert_eq!(table.exterior(2, 5), (-1, 7));
1783        assert_eq!(table.exterior(2, 6), (0, 0));
1784        assert_eq!(table.exterior(2, 7), (0, 0));
1785        assert_eq!(table.exterior(2, 8), (1, 10));
1786        assert_eq!(table.exterior(2, 9), (-1, 11));
1787        assert_eq!(table.exterior(2, 10), (0, 0));
1788        assert_eq!(table.exterior(2, 11), (0, 0));
1789        assert_eq!(table.exterior(2, 12), (1, 14));
1790        assert_eq!(table.exterior(2, 13), (-1, 15));
1791        assert_eq!(table.exterior(2, 14), (0, 0));
1792        assert_eq!(table.exterior(2, 15), (0, 0));
1793        // Row e12
1794        assert_eq!(table.exterior(3, 0), (1, 3));
1795        assert_eq!(table.exterior(3, 1), (0, 0));
1796        assert_eq!(table.exterior(3, 2), (0, 0));
1797        assert_eq!(table.exterior(3, 3), (0, 0));
1798        assert_eq!(table.exterior(3, 4), (1, 7));
1799        assert_eq!(table.exterior(3, 5), (0, 0));
1800        assert_eq!(table.exterior(3, 6), (0, 0));
1801        assert_eq!(table.exterior(3, 7), (0, 0));
1802        assert_eq!(table.exterior(3, 8), (1, 11));
1803        assert_eq!(table.exterior(3, 9), (0, 0));
1804        assert_eq!(table.exterior(3, 10), (0, 0));
1805        assert_eq!(table.exterior(3, 11), (0, 0));
1806        assert_eq!(table.exterior(3, 12), (1, 15));
1807        assert_eq!(table.exterior(3, 13), (0, 0));
1808        assert_eq!(table.exterior(3, 14), (0, 0));
1809        assert_eq!(table.exterior(3, 15), (0, 0));
1810        // Row e3
1811        assert_eq!(table.exterior(4, 0), (1, 4));
1812        assert_eq!(table.exterior(4, 1), (-1, 5));
1813        assert_eq!(table.exterior(4, 2), (-1, 6));
1814        assert_eq!(table.exterior(4, 3), (1, 7));
1815        assert_eq!(table.exterior(4, 4), (0, 0));
1816        assert_eq!(table.exterior(4, 5), (0, 0));
1817        assert_eq!(table.exterior(4, 6), (0, 0));
1818        assert_eq!(table.exterior(4, 7), (0, 0));
1819        assert_eq!(table.exterior(4, 8), (1, 12));
1820        assert_eq!(table.exterior(4, 9), (-1, 13));
1821        assert_eq!(table.exterior(4, 10), (-1, 14));
1822        assert_eq!(table.exterior(4, 11), (1, 15));
1823        assert_eq!(table.exterior(4, 12), (0, 0));
1824        assert_eq!(table.exterior(4, 13), (0, 0));
1825        assert_eq!(table.exterior(4, 14), (0, 0));
1826        assert_eq!(table.exterior(4, 15), (0, 0));
1827        // Row e13
1828        assert_eq!(table.exterior(5, 0), (1, 5));
1829        assert_eq!(table.exterior(5, 1), (0, 0));
1830        assert_eq!(table.exterior(5, 2), (-1, 7));
1831        assert_eq!(table.exterior(5, 3), (0, 0));
1832        assert_eq!(table.exterior(5, 4), (0, 0));
1833        assert_eq!(table.exterior(5, 5), (0, 0));
1834        assert_eq!(table.exterior(5, 6), (0, 0));
1835        assert_eq!(table.exterior(5, 7), (0, 0));
1836        assert_eq!(table.exterior(5, 8), (1, 13));
1837        assert_eq!(table.exterior(5, 9), (0, 0));
1838        assert_eq!(table.exterior(5, 10), (-1, 15));
1839        assert_eq!(table.exterior(5, 11), (0, 0));
1840        assert_eq!(table.exterior(5, 12), (0, 0));
1841        assert_eq!(table.exterior(5, 13), (0, 0));
1842        assert_eq!(table.exterior(5, 14), (0, 0));
1843        assert_eq!(table.exterior(5, 15), (0, 0));
1844        // Row e23
1845        assert_eq!(table.exterior(6, 0), (1, 6));
1846        assert_eq!(table.exterior(6, 1), (1, 7));
1847        assert_eq!(table.exterior(6, 2), (0, 0));
1848        assert_eq!(table.exterior(6, 3), (0, 0));
1849        assert_eq!(table.exterior(6, 4), (0, 0));
1850        assert_eq!(table.exterior(6, 5), (0, 0));
1851        assert_eq!(table.exterior(6, 6), (0, 0));
1852        assert_eq!(table.exterior(6, 7), (0, 0));
1853        assert_eq!(table.exterior(6, 8), (1, 14));
1854        assert_eq!(table.exterior(6, 9), (1, 15));
1855        assert_eq!(table.exterior(6, 10), (0, 0));
1856        assert_eq!(table.exterior(6, 11), (0, 0));
1857        assert_eq!(table.exterior(6, 12), (0, 0));
1858        assert_eq!(table.exterior(6, 13), (0, 0));
1859        assert_eq!(table.exterior(6, 14), (0, 0));
1860        assert_eq!(table.exterior(6, 15), (0, 0));
1861        // Row e123
1862        assert_eq!(table.exterior(7, 0), (1, 7));
1863        assert_eq!(table.exterior(7, 1), (0, 0));
1864        assert_eq!(table.exterior(7, 2), (0, 0));
1865        assert_eq!(table.exterior(7, 3), (0, 0));
1866        assert_eq!(table.exterior(7, 4), (0, 0));
1867        assert_eq!(table.exterior(7, 5), (0, 0));
1868        assert_eq!(table.exterior(7, 6), (0, 0));
1869        assert_eq!(table.exterior(7, 7), (0, 0));
1870        assert_eq!(table.exterior(7, 8), (1, 15));
1871        assert_eq!(table.exterior(7, 9), (0, 0));
1872        assert_eq!(table.exterior(7, 10), (0, 0));
1873        assert_eq!(table.exterior(7, 11), (0, 0));
1874        assert_eq!(table.exterior(7, 12), (0, 0));
1875        assert_eq!(table.exterior(7, 13), (0, 0));
1876        assert_eq!(table.exterior(7, 14), (0, 0));
1877        assert_eq!(table.exterior(7, 15), (0, 0));
1878        // Row e4
1879        assert_eq!(table.exterior(8, 0), (1, 8));
1880        assert_eq!(table.exterior(8, 1), (-1, 9));
1881        assert_eq!(table.exterior(8, 2), (-1, 10));
1882        assert_eq!(table.exterior(8, 3), (1, 11));
1883        assert_eq!(table.exterior(8, 4), (-1, 12));
1884        assert_eq!(table.exterior(8, 5), (1, 13));
1885        assert_eq!(table.exterior(8, 6), (1, 14));
1886        assert_eq!(table.exterior(8, 7), (-1, 15));
1887        assert_eq!(table.exterior(8, 8), (0, 0));
1888        assert_eq!(table.exterior(8, 9), (0, 0));
1889        assert_eq!(table.exterior(8, 10), (0, 0));
1890        assert_eq!(table.exterior(8, 11), (0, 0));
1891        assert_eq!(table.exterior(8, 12), (0, 0));
1892        assert_eq!(table.exterior(8, 13), (0, 0));
1893        assert_eq!(table.exterior(8, 14), (0, 0));
1894        assert_eq!(table.exterior(8, 15), (0, 0));
1895        // Row e14
1896        assert_eq!(table.exterior(9, 0), (1, 9));
1897        assert_eq!(table.exterior(9, 1), (0, 0));
1898        assert_eq!(table.exterior(9, 2), (-1, 11));
1899        assert_eq!(table.exterior(9, 3), (0, 0));
1900        assert_eq!(table.exterior(9, 4), (-1, 13));
1901        assert_eq!(table.exterior(9, 5), (0, 0));
1902        assert_eq!(table.exterior(9, 6), (1, 15));
1903        assert_eq!(table.exterior(9, 7), (0, 0));
1904        assert_eq!(table.exterior(9, 8), (0, 0));
1905        assert_eq!(table.exterior(9, 9), (0, 0));
1906        assert_eq!(table.exterior(9, 10), (0, 0));
1907        assert_eq!(table.exterior(9, 11), (0, 0));
1908        assert_eq!(table.exterior(9, 12), (0, 0));
1909        assert_eq!(table.exterior(9, 13), (0, 0));
1910        assert_eq!(table.exterior(9, 14), (0, 0));
1911        assert_eq!(table.exterior(9, 15), (0, 0));
1912        // Row e24
1913        assert_eq!(table.exterior(10, 0), (1, 10));
1914        assert_eq!(table.exterior(10, 1), (1, 11));
1915        assert_eq!(table.exterior(10, 2), (0, 0));
1916        assert_eq!(table.exterior(10, 3), (0, 0));
1917        assert_eq!(table.exterior(10, 4), (-1, 14));
1918        assert_eq!(table.exterior(10, 5), (-1, 15));
1919        assert_eq!(table.exterior(10, 6), (0, 0));
1920        assert_eq!(table.exterior(10, 7), (0, 0));
1921        assert_eq!(table.exterior(10, 8), (0, 0));
1922        assert_eq!(table.exterior(10, 9), (0, 0));
1923        assert_eq!(table.exterior(10, 10), (0, 0));
1924        assert_eq!(table.exterior(10, 11), (0, 0));
1925        assert_eq!(table.exterior(10, 12), (0, 0));
1926        assert_eq!(table.exterior(10, 13), (0, 0));
1927        assert_eq!(table.exterior(10, 14), (0, 0));
1928        assert_eq!(table.exterior(10, 15), (0, 0));
1929        // Row e124
1930        assert_eq!(table.exterior(11, 0), (1, 11));
1931        assert_eq!(table.exterior(11, 1), (0, 0));
1932        assert_eq!(table.exterior(11, 2), (0, 0));
1933        assert_eq!(table.exterior(11, 3), (0, 0));
1934        assert_eq!(table.exterior(11, 4), (-1, 15));
1935        assert_eq!(table.exterior(11, 5), (0, 0));
1936        assert_eq!(table.exterior(11, 6), (0, 0));
1937        assert_eq!(table.exterior(11, 7), (0, 0));
1938        assert_eq!(table.exterior(11, 8), (0, 0));
1939        assert_eq!(table.exterior(11, 9), (0, 0));
1940        assert_eq!(table.exterior(11, 10), (0, 0));
1941        assert_eq!(table.exterior(11, 11), (0, 0));
1942        assert_eq!(table.exterior(11, 12), (0, 0));
1943        assert_eq!(table.exterior(11, 13), (0, 0));
1944        assert_eq!(table.exterior(11, 14), (0, 0));
1945        assert_eq!(table.exterior(11, 15), (0, 0));
1946        // Row e34
1947        assert_eq!(table.exterior(12, 0), (1, 12));
1948        assert_eq!(table.exterior(12, 1), (1, 13));
1949        assert_eq!(table.exterior(12, 2), (1, 14));
1950        assert_eq!(table.exterior(12, 3), (1, 15));
1951        assert_eq!(table.exterior(12, 4), (0, 0));
1952        assert_eq!(table.exterior(12, 5), (0, 0));
1953        assert_eq!(table.exterior(12, 6), (0, 0));
1954        assert_eq!(table.exterior(12, 7), (0, 0));
1955        assert_eq!(table.exterior(12, 8), (0, 0));
1956        assert_eq!(table.exterior(12, 9), (0, 0));
1957        assert_eq!(table.exterior(12, 10), (0, 0));
1958        assert_eq!(table.exterior(12, 11), (0, 0));
1959        assert_eq!(table.exterior(12, 12), (0, 0));
1960        assert_eq!(table.exterior(12, 13), (0, 0));
1961        assert_eq!(table.exterior(12, 14), (0, 0));
1962        assert_eq!(table.exterior(12, 15), (0, 0));
1963        // Row e134
1964        assert_eq!(table.exterior(13, 0), (1, 13));
1965        assert_eq!(table.exterior(13, 1), (0, 0));
1966        assert_eq!(table.exterior(13, 2), (1, 15));
1967        assert_eq!(table.exterior(13, 3), (0, 0));
1968        assert_eq!(table.exterior(13, 4), (0, 0));
1969        assert_eq!(table.exterior(13, 5), (0, 0));
1970        assert_eq!(table.exterior(13, 6), (0, 0));
1971        assert_eq!(table.exterior(13, 7), (0, 0));
1972        assert_eq!(table.exterior(13, 8), (0, 0));
1973        assert_eq!(table.exterior(13, 9), (0, 0));
1974        assert_eq!(table.exterior(13, 10), (0, 0));
1975        assert_eq!(table.exterior(13, 11), (0, 0));
1976        assert_eq!(table.exterior(13, 12), (0, 0));
1977        assert_eq!(table.exterior(13, 13), (0, 0));
1978        assert_eq!(table.exterior(13, 14), (0, 0));
1979        assert_eq!(table.exterior(13, 15), (0, 0));
1980        // Row e234
1981        assert_eq!(table.exterior(14, 0), (1, 14));
1982        assert_eq!(table.exterior(14, 1), (-1, 15));
1983        assert_eq!(table.exterior(14, 2), (0, 0));
1984        assert_eq!(table.exterior(14, 3), (0, 0));
1985        assert_eq!(table.exterior(14, 4), (0, 0));
1986        assert_eq!(table.exterior(14, 5), (0, 0));
1987        assert_eq!(table.exterior(14, 6), (0, 0));
1988        assert_eq!(table.exterior(14, 7), (0, 0));
1989        assert_eq!(table.exterior(14, 8), (0, 0));
1990        assert_eq!(table.exterior(14, 9), (0, 0));
1991        assert_eq!(table.exterior(14, 10), (0, 0));
1992        assert_eq!(table.exterior(14, 11), (0, 0));
1993        assert_eq!(table.exterior(14, 12), (0, 0));
1994        assert_eq!(table.exterior(14, 13), (0, 0));
1995        assert_eq!(table.exterior(14, 14), (0, 0));
1996        assert_eq!(table.exterior(14, 15), (0, 0));
1997        // Row e1234
1998        assert_eq!(table.exterior(15, 0), (1, 15));
1999        assert_eq!(table.exterior(15, 1), (0, 0));
2000        assert_eq!(table.exterior(15, 2), (0, 0));
2001        assert_eq!(table.exterior(15, 3), (0, 0));
2002        assert_eq!(table.exterior(15, 4), (0, 0));
2003        assert_eq!(table.exterior(15, 5), (0, 0));
2004        assert_eq!(table.exterior(15, 6), (0, 0));
2005        assert_eq!(table.exterior(15, 7), (0, 0));
2006        assert_eq!(table.exterior(15, 8), (0, 0));
2007        assert_eq!(table.exterior(15, 9), (0, 0));
2008        assert_eq!(table.exterior(15, 10), (0, 0));
2009        assert_eq!(table.exterior(15, 11), (0, 0));
2010        assert_eq!(table.exterior(15, 12), (0, 0));
2011        assert_eq!(table.exterior(15, 13), (0, 0));
2012        assert_eq!(table.exterior(15, 14), (0, 0));
2013        assert_eq!(table.exterior(15, 15), (0, 0));
2014    }
2015
2016    #[test]
2017    fn pga_3d_full_regressive_table() {
2018        let algebra = Algebra::pga(3);
2019        let table = ProductTable::new(&algebra);
2020
2021        // Row 1
2022        assert_eq!(table.regressive(0, 0), (0, 0));
2023        assert_eq!(table.regressive(0, 1), (0, 0));
2024        assert_eq!(table.regressive(0, 2), (0, 0));
2025        assert_eq!(table.regressive(0, 3), (0, 0));
2026        assert_eq!(table.regressive(0, 4), (0, 0));
2027        assert_eq!(table.regressive(0, 5), (0, 0));
2028        assert_eq!(table.regressive(0, 6), (0, 0));
2029        assert_eq!(table.regressive(0, 7), (0, 0));
2030        assert_eq!(table.regressive(0, 8), (0, 0));
2031        assert_eq!(table.regressive(0, 9), (0, 0));
2032        assert_eq!(table.regressive(0, 10), (0, 0));
2033        assert_eq!(table.regressive(0, 11), (0, 0));
2034        assert_eq!(table.regressive(0, 12), (0, 0));
2035        assert_eq!(table.regressive(0, 13), (0, 0));
2036        assert_eq!(table.regressive(0, 14), (0, 0));
2037        assert_eq!(table.regressive(0, 15), (1, 0));
2038        // Row e1
2039        assert_eq!(table.regressive(1, 0), (0, 0));
2040        assert_eq!(table.regressive(1, 1), (0, 0));
2041        assert_eq!(table.regressive(1, 2), (0, 0));
2042        assert_eq!(table.regressive(1, 3), (0, 0));
2043        assert_eq!(table.regressive(1, 4), (0, 0));
2044        assert_eq!(table.regressive(1, 5), (0, 0));
2045        assert_eq!(table.regressive(1, 6), (0, 0));
2046        assert_eq!(table.regressive(1, 7), (0, 0));
2047        assert_eq!(table.regressive(1, 8), (0, 0));
2048        assert_eq!(table.regressive(1, 9), (0, 0));
2049        assert_eq!(table.regressive(1, 10), (0, 0));
2050        assert_eq!(table.regressive(1, 11), (0, 0));
2051        assert_eq!(table.regressive(1, 12), (0, 0));
2052        assert_eq!(table.regressive(1, 13), (0, 0));
2053        assert_eq!(table.regressive(1, 14), (1, 0));
2054        assert_eq!(table.regressive(1, 15), (-1, 1));
2055        // Row e2
2056        assert_eq!(table.regressive(2, 0), (0, 0));
2057        assert_eq!(table.regressive(2, 1), (0, 0));
2058        assert_eq!(table.regressive(2, 2), (0, 0));
2059        assert_eq!(table.regressive(2, 3), (0, 0));
2060        assert_eq!(table.regressive(2, 4), (0, 0));
2061        assert_eq!(table.regressive(2, 5), (0, 0));
2062        assert_eq!(table.regressive(2, 6), (0, 0));
2063        assert_eq!(table.regressive(2, 7), (0, 0));
2064        assert_eq!(table.regressive(2, 8), (0, 0));
2065        assert_eq!(table.regressive(2, 9), (0, 0));
2066        assert_eq!(table.regressive(2, 10), (0, 0));
2067        assert_eq!(table.regressive(2, 11), (0, 0));
2068        assert_eq!(table.regressive(2, 12), (0, 0));
2069        assert_eq!(table.regressive(2, 13), (-1, 0));
2070        assert_eq!(table.regressive(2, 14), (0, 0));
2071        assert_eq!(table.regressive(2, 15), (-1, 2));
2072        // Row e12
2073        assert_eq!(table.regressive(3, 0), (0, 0));
2074        assert_eq!(table.regressive(3, 1), (0, 0));
2075        assert_eq!(table.regressive(3, 2), (0, 0));
2076        assert_eq!(table.regressive(3, 3), (0, 0));
2077        assert_eq!(table.regressive(3, 4), (0, 0));
2078        assert_eq!(table.regressive(3, 5), (0, 0));
2079        assert_eq!(table.regressive(3, 6), (0, 0));
2080        assert_eq!(table.regressive(3, 7), (0, 0));
2081        assert_eq!(table.regressive(3, 8), (0, 0));
2082        assert_eq!(table.regressive(3, 9), (0, 0));
2083        assert_eq!(table.regressive(3, 10), (0, 0));
2084        assert_eq!(table.regressive(3, 11), (0, 0));
2085        assert_eq!(table.regressive(3, 12), (1, 0));
2086        assert_eq!(table.regressive(3, 13), (-1, 1));
2087        assert_eq!(table.regressive(3, 14), (-1, 2));
2088        assert_eq!(table.regressive(3, 15), (1, 3));
2089        // Row e3
2090        assert_eq!(table.regressive(4, 0), (0, 0));
2091        assert_eq!(table.regressive(4, 1), (0, 0));
2092        assert_eq!(table.regressive(4, 2), (0, 0));
2093        assert_eq!(table.regressive(4, 3), (0, 0));
2094        assert_eq!(table.regressive(4, 4), (0, 0));
2095        assert_eq!(table.regressive(4, 5), (0, 0));
2096        assert_eq!(table.regressive(4, 6), (0, 0));
2097        assert_eq!(table.regressive(4, 7), (0, 0));
2098        assert_eq!(table.regressive(4, 8), (0, 0));
2099        assert_eq!(table.regressive(4, 9), (0, 0));
2100        assert_eq!(table.regressive(4, 10), (0, 0));
2101        assert_eq!(table.regressive(4, 11), (1, 0));
2102        assert_eq!(table.regressive(4, 12), (0, 0));
2103        assert_eq!(table.regressive(4, 13), (0, 0));
2104        assert_eq!(table.regressive(4, 14), (0, 0));
2105        assert_eq!(table.regressive(4, 15), (-1, 4));
2106        // Row e13
2107        assert_eq!(table.regressive(5, 0), (0, 0));
2108        assert_eq!(table.regressive(5, 1), (0, 0));
2109        assert_eq!(table.regressive(5, 2), (0, 0));
2110        assert_eq!(table.regressive(5, 3), (0, 0));
2111        assert_eq!(table.regressive(5, 4), (0, 0));
2112        assert_eq!(table.regressive(5, 5), (0, 0));
2113        assert_eq!(table.regressive(5, 6), (0, 0));
2114        assert_eq!(table.regressive(5, 7), (0, 0));
2115        assert_eq!(table.regressive(5, 8), (0, 0));
2116        assert_eq!(table.regressive(5, 9), (0, 0));
2117        assert_eq!(table.regressive(5, 10), (-1, 0));
2118        assert_eq!(table.regressive(5, 11), (1, 1));
2119        assert_eq!(table.regressive(5, 12), (0, 0));
2120        assert_eq!(table.regressive(5, 13), (0, 0));
2121        assert_eq!(table.regressive(5, 14), (-1, 4));
2122        assert_eq!(table.regressive(5, 15), (1, 5));
2123        // Row e23
2124        assert_eq!(table.regressive(6, 0), (0, 0));
2125        assert_eq!(table.regressive(6, 1), (0, 0));
2126        assert_eq!(table.regressive(6, 2), (0, 0));
2127        assert_eq!(table.regressive(6, 3), (0, 0));
2128        assert_eq!(table.regressive(6, 4), (0, 0));
2129        assert_eq!(table.regressive(6, 5), (0, 0));
2130        assert_eq!(table.regressive(6, 6), (0, 0));
2131        assert_eq!(table.regressive(6, 7), (0, 0));
2132        assert_eq!(table.regressive(6, 8), (0, 0));
2133        assert_eq!(table.regressive(6, 9), (1, 0));
2134        assert_eq!(table.regressive(6, 10), (0, 0));
2135        assert_eq!(table.regressive(6, 11), (1, 2));
2136        assert_eq!(table.regressive(6, 12), (0, 0));
2137        assert_eq!(table.regressive(6, 13), (1, 4));
2138        assert_eq!(table.regressive(6, 14), (0, 0));
2139        assert_eq!(table.regressive(6, 15), (1, 6));
2140        // Row e123
2141        assert_eq!(table.regressive(7, 0), (0, 0));
2142        assert_eq!(table.regressive(7, 1), (0, 0));
2143        assert_eq!(table.regressive(7, 2), (0, 0));
2144        assert_eq!(table.regressive(7, 3), (0, 0));
2145        assert_eq!(table.regressive(7, 4), (0, 0));
2146        assert_eq!(table.regressive(7, 5), (0, 0));
2147        assert_eq!(table.regressive(7, 6), (0, 0));
2148        assert_eq!(table.regressive(7, 7), (0, 0));
2149        assert_eq!(table.regressive(7, 8), (1, 0));
2150        assert_eq!(table.regressive(7, 9), (-1, 1));
2151        assert_eq!(table.regressive(7, 10), (-1, 2));
2152        assert_eq!(table.regressive(7, 11), (1, 3));
2153        assert_eq!(table.regressive(7, 12), (-1, 4));
2154        assert_eq!(table.regressive(7, 13), (1, 5));
2155        assert_eq!(table.regressive(7, 14), (1, 6));
2156        assert_eq!(table.regressive(7, 15), (-1, 7));
2157        // Row e4
2158        assert_eq!(table.regressive(8, 0), (0, 0));
2159        assert_eq!(table.regressive(8, 1), (0, 0));
2160        assert_eq!(table.regressive(8, 2), (0, 0));
2161        assert_eq!(table.regressive(8, 3), (0, 0));
2162        assert_eq!(table.regressive(8, 4), (0, 0));
2163        assert_eq!(table.regressive(8, 5), (0, 0));
2164        assert_eq!(table.regressive(8, 6), (0, 0));
2165        assert_eq!(table.regressive(8, 7), (-1, 0));
2166        assert_eq!(table.regressive(8, 8), (0, 0));
2167        assert_eq!(table.regressive(8, 9), (0, 0));
2168        assert_eq!(table.regressive(8, 10), (0, 0));
2169        assert_eq!(table.regressive(8, 11), (0, 0));
2170        assert_eq!(table.regressive(8, 12), (0, 0));
2171        assert_eq!(table.regressive(8, 13), (0, 0));
2172        assert_eq!(table.regressive(8, 14), (0, 0));
2173        assert_eq!(table.regressive(8, 15), (-1, 8));
2174        // Row e14
2175        assert_eq!(table.regressive(9, 0), (0, 0));
2176        assert_eq!(table.regressive(9, 1), (0, 0));
2177        assert_eq!(table.regressive(9, 2), (0, 0));
2178        assert_eq!(table.regressive(9, 3), (0, 0));
2179        assert_eq!(table.regressive(9, 4), (0, 0));
2180        assert_eq!(table.regressive(9, 5), (0, 0));
2181        assert_eq!(table.regressive(9, 6), (1, 0));
2182        assert_eq!(table.regressive(9, 7), (-1, 1));
2183        assert_eq!(table.regressive(9, 8), (0, 0));
2184        assert_eq!(table.regressive(9, 9), (0, 0));
2185        assert_eq!(table.regressive(9, 10), (0, 0));
2186        assert_eq!(table.regressive(9, 11), (0, 0));
2187        assert_eq!(table.regressive(9, 12), (0, 0));
2188        assert_eq!(table.regressive(9, 13), (0, 0));
2189        assert_eq!(table.regressive(9, 14), (-1, 8));
2190        assert_eq!(table.regressive(9, 15), (1, 9));
2191        // Row e24
2192        assert_eq!(table.regressive(10, 0), (0, 0));
2193        assert_eq!(table.regressive(10, 1), (0, 0));
2194        assert_eq!(table.regressive(10, 2), (0, 0));
2195        assert_eq!(table.regressive(10, 3), (0, 0));
2196        assert_eq!(table.regressive(10, 4), (0, 0));
2197        assert_eq!(table.regressive(10, 5), (-1, 0));
2198        assert_eq!(table.regressive(10, 6), (0, 0));
2199        assert_eq!(table.regressive(10, 7), (-1, 2));
2200        assert_eq!(table.regressive(10, 8), (0, 0));
2201        assert_eq!(table.regressive(10, 9), (0, 0));
2202        assert_eq!(table.regressive(10, 10), (0, 0));
2203        assert_eq!(table.regressive(10, 11), (0, 0));
2204        assert_eq!(table.regressive(10, 12), (0, 0));
2205        assert_eq!(table.regressive(10, 13), (1, 8));
2206        assert_eq!(table.regressive(10, 14), (0, 0));
2207        assert_eq!(table.regressive(10, 15), (1, 10));
2208        // Row e124
2209        assert_eq!(table.regressive(11, 0), (0, 0));
2210        assert_eq!(table.regressive(11, 1), (0, 0));
2211        assert_eq!(table.regressive(11, 2), (0, 0));
2212        assert_eq!(table.regressive(11, 3), (0, 0));
2213        assert_eq!(table.regressive(11, 4), (-1, 0));
2214        assert_eq!(table.regressive(11, 5), (1, 1));
2215        assert_eq!(table.regressive(11, 6), (1, 2));
2216        assert_eq!(table.regressive(11, 7), (-1, 3));
2217        assert_eq!(table.regressive(11, 8), (0, 0));
2218        assert_eq!(table.regressive(11, 9), (0, 0));
2219        assert_eq!(table.regressive(11, 10), (0, 0));
2220        assert_eq!(table.regressive(11, 11), (0, 0));
2221        assert_eq!(table.regressive(11, 12), (-1, 8));
2222        assert_eq!(table.regressive(11, 13), (1, 9));
2223        assert_eq!(table.regressive(11, 14), (1, 10));
2224        assert_eq!(table.regressive(11, 15), (-1, 11));
2225        // Row e34
2226        assert_eq!(table.regressive(12, 0), (0, 0));
2227        assert_eq!(table.regressive(12, 1), (0, 0));
2228        assert_eq!(table.regressive(12, 2), (0, 0));
2229        assert_eq!(table.regressive(12, 3), (1, 0));
2230        assert_eq!(table.regressive(12, 4), (0, 0));
2231        assert_eq!(table.regressive(12, 5), (0, 0));
2232        assert_eq!(table.regressive(12, 6), (0, 0));
2233        assert_eq!(table.regressive(12, 7), (-1, 4));
2234        assert_eq!(table.regressive(12, 8), (0, 0));
2235        assert_eq!(table.regressive(12, 9), (0, 0));
2236        assert_eq!(table.regressive(12, 10), (0, 0));
2237        assert_eq!(table.regressive(12, 11), (-1, 8));
2238        assert_eq!(table.regressive(12, 12), (0, 0));
2239        assert_eq!(table.regressive(12, 13), (0, 0));
2240        assert_eq!(table.regressive(12, 14), (0, 0));
2241        assert_eq!(table.regressive(12, 15), (1, 12));
2242        // Row e134
2243        assert_eq!(table.regressive(13, 0), (0, 0));
2244        assert_eq!(table.regressive(13, 1), (0, 0));
2245        assert_eq!(table.regressive(13, 2), (1, 0));
2246        assert_eq!(table.regressive(13, 3), (-1, 1));
2247        assert_eq!(table.regressive(13, 4), (0, 0));
2248        assert_eq!(table.regressive(13, 5), (0, 0));
2249        assert_eq!(table.regressive(13, 6), (1, 4));
2250        assert_eq!(table.regressive(13, 7), (-1, 5));
2251        assert_eq!(table.regressive(13, 8), (0, 0));
2252        assert_eq!(table.regressive(13, 9), (0, 0));
2253        assert_eq!(table.regressive(13, 10), (1, 8));
2254        assert_eq!(table.regressive(13, 11), (-1, 9));
2255        assert_eq!(table.regressive(13, 12), (0, 0));
2256        assert_eq!(table.regressive(13, 13), (0, 0));
2257        assert_eq!(table.regressive(13, 14), (1, 12));
2258        assert_eq!(table.regressive(13, 15), (-1, 13));
2259        // Row e234
2260        assert_eq!(table.regressive(14, 0), (0, 0));
2261        assert_eq!(table.regressive(14, 1), (-1, 0));
2262        assert_eq!(table.regressive(14, 2), (0, 0));
2263        assert_eq!(table.regressive(14, 3), (-1, 2));
2264        assert_eq!(table.regressive(14, 4), (0, 0));
2265        assert_eq!(table.regressive(14, 5), (-1, 4));
2266        assert_eq!(table.regressive(14, 6), (0, 0));
2267        assert_eq!(table.regressive(14, 7), (-1, 6));
2268        assert_eq!(table.regressive(14, 8), (0, 0));
2269        assert_eq!(table.regressive(14, 9), (-1, 8));
2270        assert_eq!(table.regressive(14, 10), (0, 0));
2271        assert_eq!(table.regressive(14, 11), (-1, 10));
2272        assert_eq!(table.regressive(14, 12), (0, 0));
2273        assert_eq!(table.regressive(14, 13), (-1, 12));
2274        assert_eq!(table.regressive(14, 14), (0, 0));
2275        assert_eq!(table.regressive(14, 15), (-1, 14));
2276        // Row e1234
2277        assert_eq!(table.regressive(15, 0), (1, 0));
2278        assert_eq!(table.regressive(15, 1), (-1, 1));
2279        assert_eq!(table.regressive(15, 2), (-1, 2));
2280        assert_eq!(table.regressive(15, 3), (1, 3));
2281        assert_eq!(table.regressive(15, 4), (-1, 4));
2282        assert_eq!(table.regressive(15, 5), (1, 5));
2283        assert_eq!(table.regressive(15, 6), (1, 6));
2284        assert_eq!(table.regressive(15, 7), (-1, 7));
2285        assert_eq!(table.regressive(15, 8), (-1, 8));
2286        assert_eq!(table.regressive(15, 9), (1, 9));
2287        assert_eq!(table.regressive(15, 10), (1, 10));
2288        assert_eq!(table.regressive(15, 11), (-1, 11));
2289        assert_eq!(table.regressive(15, 12), (1, 12));
2290        assert_eq!(table.regressive(15, 13), (-1, 13));
2291        assert_eq!(table.regressive(15, 14), (-1, 14));
2292        assert_eq!(table.regressive(15, 15), (1, 15));
2293    }
2294
2295    #[test]
2296    fn pga_3d_full_left_contraction_table() {
2297        let algebra = Algebra::pga(3);
2298        let table = ProductTable::new(&algebra);
2299
2300        // Row 1
2301        assert_eq!(table.left_contraction(0, 0), (1, 0));
2302        assert_eq!(table.left_contraction(0, 1), (1, 1));
2303        assert_eq!(table.left_contraction(0, 2), (1, 2));
2304        assert_eq!(table.left_contraction(0, 3), (1, 3));
2305        assert_eq!(table.left_contraction(0, 4), (1, 4));
2306        assert_eq!(table.left_contraction(0, 5), (1, 5));
2307        assert_eq!(table.left_contraction(0, 6), (1, 6));
2308        assert_eq!(table.left_contraction(0, 7), (1, 7));
2309        assert_eq!(table.left_contraction(0, 8), (1, 8));
2310        assert_eq!(table.left_contraction(0, 9), (1, 9));
2311        assert_eq!(table.left_contraction(0, 10), (1, 10));
2312        assert_eq!(table.left_contraction(0, 11), (1, 11));
2313        assert_eq!(table.left_contraction(0, 12), (1, 12));
2314        assert_eq!(table.left_contraction(0, 13), (1, 13));
2315        assert_eq!(table.left_contraction(0, 14), (1, 14));
2316        assert_eq!(table.left_contraction(0, 15), (1, 15));
2317        // Row e1
2318        assert_eq!(table.left_contraction(1, 0), (0, 0));
2319        assert_eq!(table.left_contraction(1, 1), (1, 0));
2320        assert_eq!(table.left_contraction(1, 2), (0, 0));
2321        assert_eq!(table.left_contraction(1, 3), (1, 2));
2322        assert_eq!(table.left_contraction(1, 4), (0, 0));
2323        assert_eq!(table.left_contraction(1, 5), (1, 4));
2324        assert_eq!(table.left_contraction(1, 6), (0, 0));
2325        assert_eq!(table.left_contraction(1, 7), (1, 6));
2326        assert_eq!(table.left_contraction(1, 8), (0, 0));
2327        assert_eq!(table.left_contraction(1, 9), (1, 8));
2328        assert_eq!(table.left_contraction(1, 10), (0, 0));
2329        assert_eq!(table.left_contraction(1, 11), (1, 10));
2330        assert_eq!(table.left_contraction(1, 12), (0, 0));
2331        assert_eq!(table.left_contraction(1, 13), (1, 12));
2332        assert_eq!(table.left_contraction(1, 14), (0, 0));
2333        assert_eq!(table.left_contraction(1, 15), (1, 14));
2334        // Row e2
2335        assert_eq!(table.left_contraction(2, 0), (0, 0));
2336        assert_eq!(table.left_contraction(2, 1), (0, 0));
2337        assert_eq!(table.left_contraction(2, 2), (1, 0));
2338        assert_eq!(table.left_contraction(2, 3), (-1, 1));
2339        assert_eq!(table.left_contraction(2, 4), (0, 0));
2340        assert_eq!(table.left_contraction(2, 5), (0, 0));
2341        assert_eq!(table.left_contraction(2, 6), (1, 4));
2342        assert_eq!(table.left_contraction(2, 7), (-1, 5));
2343        assert_eq!(table.left_contraction(2, 8), (0, 0));
2344        assert_eq!(table.left_contraction(2, 9), (0, 0));
2345        assert_eq!(table.left_contraction(2, 10), (1, 8));
2346        assert_eq!(table.left_contraction(2, 11), (-1, 9));
2347        assert_eq!(table.left_contraction(2, 12), (0, 0));
2348        assert_eq!(table.left_contraction(2, 13), (0, 0));
2349        assert_eq!(table.left_contraction(2, 14), (1, 12));
2350        assert_eq!(table.left_contraction(2, 15), (-1, 13));
2351        // Row e12
2352        assert_eq!(table.left_contraction(3, 0), (0, 0));
2353        assert_eq!(table.left_contraction(3, 1), (0, 0));
2354        assert_eq!(table.left_contraction(3, 2), (0, 0));
2355        assert_eq!(table.left_contraction(3, 3), (-1, 0));
2356        assert_eq!(table.left_contraction(3, 4), (0, 0));
2357        assert_eq!(table.left_contraction(3, 5), (0, 0));
2358        assert_eq!(table.left_contraction(3, 6), (0, 0));
2359        assert_eq!(table.left_contraction(3, 7), (-1, 4));
2360        assert_eq!(table.left_contraction(3, 8), (0, 0));
2361        assert_eq!(table.left_contraction(3, 9), (0, 0));
2362        assert_eq!(table.left_contraction(3, 10), (0, 0));
2363        assert_eq!(table.left_contraction(3, 11), (-1, 8));
2364        assert_eq!(table.left_contraction(3, 12), (0, 0));
2365        assert_eq!(table.left_contraction(3, 13), (0, 0));
2366        assert_eq!(table.left_contraction(3, 14), (0, 0));
2367        assert_eq!(table.left_contraction(3, 15), (-1, 12));
2368        // Row e3
2369        assert_eq!(table.left_contraction(4, 0), (0, 0));
2370        assert_eq!(table.left_contraction(4, 1), (0, 0));
2371        assert_eq!(table.left_contraction(4, 2), (0, 0));
2372        assert_eq!(table.left_contraction(4, 3), (0, 0));
2373        assert_eq!(table.left_contraction(4, 4), (1, 0));
2374        assert_eq!(table.left_contraction(4, 5), (-1, 1));
2375        assert_eq!(table.left_contraction(4, 6), (-1, 2));
2376        assert_eq!(table.left_contraction(4, 7), (1, 3));
2377        assert_eq!(table.left_contraction(4, 8), (0, 0));
2378        assert_eq!(table.left_contraction(4, 9), (0, 0));
2379        assert_eq!(table.left_contraction(4, 10), (0, 0));
2380        assert_eq!(table.left_contraction(4, 11), (0, 0));
2381        assert_eq!(table.left_contraction(4, 12), (1, 8));
2382        assert_eq!(table.left_contraction(4, 13), (-1, 9));
2383        assert_eq!(table.left_contraction(4, 14), (-1, 10));
2384        assert_eq!(table.left_contraction(4, 15), (1, 11));
2385        // Row e13
2386        assert_eq!(table.left_contraction(5, 0), (0, 0));
2387        assert_eq!(table.left_contraction(5, 1), (0, 0));
2388        assert_eq!(table.left_contraction(5, 2), (0, 0));
2389        assert_eq!(table.left_contraction(5, 3), (0, 0));
2390        assert_eq!(table.left_contraction(5, 4), (0, 0));
2391        assert_eq!(table.left_contraction(5, 5), (-1, 0));
2392        assert_eq!(table.left_contraction(5, 6), (0, 0));
2393        assert_eq!(table.left_contraction(5, 7), (1, 2));
2394        assert_eq!(table.left_contraction(5, 8), (0, 0));
2395        assert_eq!(table.left_contraction(5, 9), (0, 0));
2396        assert_eq!(table.left_contraction(5, 10), (0, 0));
2397        assert_eq!(table.left_contraction(5, 11), (0, 0));
2398        assert_eq!(table.left_contraction(5, 12), (0, 0));
2399        assert_eq!(table.left_contraction(5, 13), (-1, 8));
2400        assert_eq!(table.left_contraction(5, 14), (0, 0));
2401        assert_eq!(table.left_contraction(5, 15), (1, 10));
2402        // Row e23
2403        assert_eq!(table.left_contraction(6, 0), (0, 0));
2404        assert_eq!(table.left_contraction(6, 1), (0, 0));
2405        assert_eq!(table.left_contraction(6, 2), (0, 0));
2406        assert_eq!(table.left_contraction(6, 3), (0, 0));
2407        assert_eq!(table.left_contraction(6, 4), (0, 0));
2408        assert_eq!(table.left_contraction(6, 5), (0, 0));
2409        assert_eq!(table.left_contraction(6, 6), (-1, 0));
2410        assert_eq!(table.left_contraction(6, 7), (-1, 1));
2411        assert_eq!(table.left_contraction(6, 8), (0, 0));
2412        assert_eq!(table.left_contraction(6, 9), (0, 0));
2413        assert_eq!(table.left_contraction(6, 10), (0, 0));
2414        assert_eq!(table.left_contraction(6, 11), (0, 0));
2415        assert_eq!(table.left_contraction(6, 12), (0, 0));
2416        assert_eq!(table.left_contraction(6, 13), (0, 0));
2417        assert_eq!(table.left_contraction(6, 14), (-1, 8));
2418        assert_eq!(table.left_contraction(6, 15), (-1, 9));
2419        // Row e123
2420        assert_eq!(table.left_contraction(7, 0), (0, 0));
2421        assert_eq!(table.left_contraction(7, 1), (0, 0));
2422        assert_eq!(table.left_contraction(7, 2), (0, 0));
2423        assert_eq!(table.left_contraction(7, 3), (0, 0));
2424        assert_eq!(table.left_contraction(7, 4), (0, 0));
2425        assert_eq!(table.left_contraction(7, 5), (0, 0));
2426        assert_eq!(table.left_contraction(7, 6), (0, 0));
2427        assert_eq!(table.left_contraction(7, 7), (-1, 0));
2428        assert_eq!(table.left_contraction(7, 8), (0, 0));
2429        assert_eq!(table.left_contraction(7, 9), (0, 0));
2430        assert_eq!(table.left_contraction(7, 10), (0, 0));
2431        assert_eq!(table.left_contraction(7, 11), (0, 0));
2432        assert_eq!(table.left_contraction(7, 12), (0, 0));
2433        assert_eq!(table.left_contraction(7, 13), (0, 0));
2434        assert_eq!(table.left_contraction(7, 14), (0, 0));
2435        assert_eq!(table.left_contraction(7, 15), (-1, 8));
2436        // Row e4
2437        assert_eq!(table.left_contraction(8, 0), (0, 0));
2438        assert_eq!(table.left_contraction(8, 1), (0, 0));
2439        assert_eq!(table.left_contraction(8, 2), (0, 0));
2440        assert_eq!(table.left_contraction(8, 3), (0, 0));
2441        assert_eq!(table.left_contraction(8, 4), (0, 0));
2442        assert_eq!(table.left_contraction(8, 5), (0, 0));
2443        assert_eq!(table.left_contraction(8, 6), (0, 0));
2444        assert_eq!(table.left_contraction(8, 7), (0, 0));
2445        assert_eq!(table.left_contraction(8, 8), (0, 0));
2446        assert_eq!(table.left_contraction(8, 9), (0, 0));
2447        assert_eq!(table.left_contraction(8, 10), (0, 0));
2448        assert_eq!(table.left_contraction(8, 11), (0, 0));
2449        assert_eq!(table.left_contraction(8, 12), (0, 0));
2450        assert_eq!(table.left_contraction(8, 13), (0, 0));
2451        assert_eq!(table.left_contraction(8, 14), (0, 0));
2452        assert_eq!(table.left_contraction(8, 15), (0, 0));
2453        // Row e14
2454        assert_eq!(table.left_contraction(9, 0), (0, 0));
2455        assert_eq!(table.left_contraction(9, 1), (0, 0));
2456        assert_eq!(table.left_contraction(9, 2), (0, 0));
2457        assert_eq!(table.left_contraction(9, 3), (0, 0));
2458        assert_eq!(table.left_contraction(9, 4), (0, 0));
2459        assert_eq!(table.left_contraction(9, 5), (0, 0));
2460        assert_eq!(table.left_contraction(9, 6), (0, 0));
2461        assert_eq!(table.left_contraction(9, 7), (0, 0));
2462        assert_eq!(table.left_contraction(9, 8), (0, 0));
2463        assert_eq!(table.left_contraction(9, 9), (0, 0));
2464        assert_eq!(table.left_contraction(9, 10), (0, 0));
2465        assert_eq!(table.left_contraction(9, 11), (0, 0));
2466        assert_eq!(table.left_contraction(9, 12), (0, 0));
2467        assert_eq!(table.left_contraction(9, 13), (0, 0));
2468        assert_eq!(table.left_contraction(9, 14), (0, 0));
2469        assert_eq!(table.left_contraction(9, 15), (0, 0));
2470        // Row e24
2471        assert_eq!(table.left_contraction(10, 0), (0, 0));
2472        assert_eq!(table.left_contraction(10, 1), (0, 0));
2473        assert_eq!(table.left_contraction(10, 2), (0, 0));
2474        assert_eq!(table.left_contraction(10, 3), (0, 0));
2475        assert_eq!(table.left_contraction(10, 4), (0, 0));
2476        assert_eq!(table.left_contraction(10, 5), (0, 0));
2477        assert_eq!(table.left_contraction(10, 6), (0, 0));
2478        assert_eq!(table.left_contraction(10, 7), (0, 0));
2479        assert_eq!(table.left_contraction(10, 8), (0, 0));
2480        assert_eq!(table.left_contraction(10, 9), (0, 0));
2481        assert_eq!(table.left_contraction(10, 10), (0, 0));
2482        assert_eq!(table.left_contraction(10, 11), (0, 0));
2483        assert_eq!(table.left_contraction(10, 12), (0, 0));
2484        assert_eq!(table.left_contraction(10, 13), (0, 0));
2485        assert_eq!(table.left_contraction(10, 14), (0, 0));
2486        assert_eq!(table.left_contraction(10, 15), (0, 0));
2487        // Row e124
2488        assert_eq!(table.left_contraction(11, 0), (0, 0));
2489        assert_eq!(table.left_contraction(11, 1), (0, 0));
2490        assert_eq!(table.left_contraction(11, 2), (0, 0));
2491        assert_eq!(table.left_contraction(11, 3), (0, 0));
2492        assert_eq!(table.left_contraction(11, 4), (0, 0));
2493        assert_eq!(table.left_contraction(11, 5), (0, 0));
2494        assert_eq!(table.left_contraction(11, 6), (0, 0));
2495        assert_eq!(table.left_contraction(11, 7), (0, 0));
2496        assert_eq!(table.left_contraction(11, 8), (0, 0));
2497        assert_eq!(table.left_contraction(11, 9), (0, 0));
2498        assert_eq!(table.left_contraction(11, 10), (0, 0));
2499        assert_eq!(table.left_contraction(11, 11), (0, 0));
2500        assert_eq!(table.left_contraction(11, 12), (0, 0));
2501        assert_eq!(table.left_contraction(11, 13), (0, 0));
2502        assert_eq!(table.left_contraction(11, 14), (0, 0));
2503        assert_eq!(table.left_contraction(11, 15), (0, 0));
2504        // Row e34
2505        assert_eq!(table.left_contraction(12, 0), (0, 0));
2506        assert_eq!(table.left_contraction(12, 1), (0, 0));
2507        assert_eq!(table.left_contraction(12, 2), (0, 0));
2508        assert_eq!(table.left_contraction(12, 3), (0, 0));
2509        assert_eq!(table.left_contraction(12, 4), (0, 0));
2510        assert_eq!(table.left_contraction(12, 5), (0, 0));
2511        assert_eq!(table.left_contraction(12, 6), (0, 0));
2512        assert_eq!(table.left_contraction(12, 7), (0, 0));
2513        assert_eq!(table.left_contraction(12, 8), (0, 0));
2514        assert_eq!(table.left_contraction(12, 9), (0, 0));
2515        assert_eq!(table.left_contraction(12, 10), (0, 0));
2516        assert_eq!(table.left_contraction(12, 11), (0, 0));
2517        assert_eq!(table.left_contraction(12, 12), (0, 0));
2518        assert_eq!(table.left_contraction(12, 13), (0, 0));
2519        assert_eq!(table.left_contraction(12, 14), (0, 0));
2520        assert_eq!(table.left_contraction(12, 15), (0, 0));
2521        // Row e134
2522        assert_eq!(table.left_contraction(13, 0), (0, 0));
2523        assert_eq!(table.left_contraction(13, 1), (0, 0));
2524        assert_eq!(table.left_contraction(13, 2), (0, 0));
2525        assert_eq!(table.left_contraction(13, 3), (0, 0));
2526        assert_eq!(table.left_contraction(13, 4), (0, 0));
2527        assert_eq!(table.left_contraction(13, 5), (0, 0));
2528        assert_eq!(table.left_contraction(13, 6), (0, 0));
2529        assert_eq!(table.left_contraction(13, 7), (0, 0));
2530        assert_eq!(table.left_contraction(13, 8), (0, 0));
2531        assert_eq!(table.left_contraction(13, 9), (0, 0));
2532        assert_eq!(table.left_contraction(13, 10), (0, 0));
2533        assert_eq!(table.left_contraction(13, 11), (0, 0));
2534        assert_eq!(table.left_contraction(13, 12), (0, 0));
2535        assert_eq!(table.left_contraction(13, 13), (0, 0));
2536        assert_eq!(table.left_contraction(13, 14), (0, 0));
2537        assert_eq!(table.left_contraction(13, 15), (0, 0));
2538        // Row e234
2539        assert_eq!(table.left_contraction(14, 0), (0, 0));
2540        assert_eq!(table.left_contraction(14, 1), (0, 0));
2541        assert_eq!(table.left_contraction(14, 2), (0, 0));
2542        assert_eq!(table.left_contraction(14, 3), (0, 0));
2543        assert_eq!(table.left_contraction(14, 4), (0, 0));
2544        assert_eq!(table.left_contraction(14, 5), (0, 0));
2545        assert_eq!(table.left_contraction(14, 6), (0, 0));
2546        assert_eq!(table.left_contraction(14, 7), (0, 0));
2547        assert_eq!(table.left_contraction(14, 8), (0, 0));
2548        assert_eq!(table.left_contraction(14, 9), (0, 0));
2549        assert_eq!(table.left_contraction(14, 10), (0, 0));
2550        assert_eq!(table.left_contraction(14, 11), (0, 0));
2551        assert_eq!(table.left_contraction(14, 12), (0, 0));
2552        assert_eq!(table.left_contraction(14, 13), (0, 0));
2553        assert_eq!(table.left_contraction(14, 14), (0, 0));
2554        assert_eq!(table.left_contraction(14, 15), (0, 0));
2555        // Row e1234
2556        assert_eq!(table.left_contraction(15, 0), (0, 0));
2557        assert_eq!(table.left_contraction(15, 1), (0, 0));
2558        assert_eq!(table.left_contraction(15, 2), (0, 0));
2559        assert_eq!(table.left_contraction(15, 3), (0, 0));
2560        assert_eq!(table.left_contraction(15, 4), (0, 0));
2561        assert_eq!(table.left_contraction(15, 5), (0, 0));
2562        assert_eq!(table.left_contraction(15, 6), (0, 0));
2563        assert_eq!(table.left_contraction(15, 7), (0, 0));
2564        assert_eq!(table.left_contraction(15, 8), (0, 0));
2565        assert_eq!(table.left_contraction(15, 9), (0, 0));
2566        assert_eq!(table.left_contraction(15, 10), (0, 0));
2567        assert_eq!(table.left_contraction(15, 11), (0, 0));
2568        assert_eq!(table.left_contraction(15, 12), (0, 0));
2569        assert_eq!(table.left_contraction(15, 13), (0, 0));
2570        assert_eq!(table.left_contraction(15, 14), (0, 0));
2571        assert_eq!(table.left_contraction(15, 15), (0, 0));
2572    }
2573
2574    #[test]
2575    fn pga_3d_full_right_contraction_table() {
2576        let algebra = Algebra::pga(3);
2577        let table = ProductTable::new(&algebra);
2578
2579        // Row 1
2580        assert_eq!(table.right_contraction(0, 0), (1, 0));
2581        assert_eq!(table.right_contraction(0, 1), (0, 0));
2582        assert_eq!(table.right_contraction(0, 2), (0, 0));
2583        assert_eq!(table.right_contraction(0, 3), (0, 0));
2584        assert_eq!(table.right_contraction(0, 4), (0, 0));
2585        assert_eq!(table.right_contraction(0, 5), (0, 0));
2586        assert_eq!(table.right_contraction(0, 6), (0, 0));
2587        assert_eq!(table.right_contraction(0, 7), (0, 0));
2588        assert_eq!(table.right_contraction(0, 8), (0, 0));
2589        assert_eq!(table.right_contraction(0, 9), (0, 0));
2590        assert_eq!(table.right_contraction(0, 10), (0, 0));
2591        assert_eq!(table.right_contraction(0, 11), (0, 0));
2592        assert_eq!(table.right_contraction(0, 12), (0, 0));
2593        assert_eq!(table.right_contraction(0, 13), (0, 0));
2594        assert_eq!(table.right_contraction(0, 14), (0, 0));
2595        assert_eq!(table.right_contraction(0, 15), (0, 0));
2596        // Row e1
2597        assert_eq!(table.right_contraction(1, 0), (1, 1));
2598        assert_eq!(table.right_contraction(1, 1), (1, 0));
2599        assert_eq!(table.right_contraction(1, 2), (0, 0));
2600        assert_eq!(table.right_contraction(1, 3), (0, 0));
2601        assert_eq!(table.right_contraction(1, 4), (0, 0));
2602        assert_eq!(table.right_contraction(1, 5), (0, 0));
2603        assert_eq!(table.right_contraction(1, 6), (0, 0));
2604        assert_eq!(table.right_contraction(1, 7), (0, 0));
2605        assert_eq!(table.right_contraction(1, 8), (0, 0));
2606        assert_eq!(table.right_contraction(1, 9), (0, 0));
2607        assert_eq!(table.right_contraction(1, 10), (0, 0));
2608        assert_eq!(table.right_contraction(1, 11), (0, 0));
2609        assert_eq!(table.right_contraction(1, 12), (0, 0));
2610        assert_eq!(table.right_contraction(1, 13), (0, 0));
2611        assert_eq!(table.right_contraction(1, 14), (0, 0));
2612        assert_eq!(table.right_contraction(1, 15), (0, 0));
2613        // Row e2
2614        assert_eq!(table.right_contraction(2, 0), (1, 2));
2615        assert_eq!(table.right_contraction(2, 1), (0, 0));
2616        assert_eq!(table.right_contraction(2, 2), (1, 0));
2617        assert_eq!(table.right_contraction(2, 3), (0, 0));
2618        assert_eq!(table.right_contraction(2, 4), (0, 0));
2619        assert_eq!(table.right_contraction(2, 5), (0, 0));
2620        assert_eq!(table.right_contraction(2, 6), (0, 0));
2621        assert_eq!(table.right_contraction(2, 7), (0, 0));
2622        assert_eq!(table.right_contraction(2, 8), (0, 0));
2623        assert_eq!(table.right_contraction(2, 9), (0, 0));
2624        assert_eq!(table.right_contraction(2, 10), (0, 0));
2625        assert_eq!(table.right_contraction(2, 11), (0, 0));
2626        assert_eq!(table.right_contraction(2, 12), (0, 0));
2627        assert_eq!(table.right_contraction(2, 13), (0, 0));
2628        assert_eq!(table.right_contraction(2, 14), (0, 0));
2629        assert_eq!(table.right_contraction(2, 15), (0, 0));
2630        // Row e12
2631        assert_eq!(table.right_contraction(3, 0), (1, 3));
2632        assert_eq!(table.right_contraction(3, 1), (-1, 2));
2633        assert_eq!(table.right_contraction(3, 2), (1, 1));
2634        assert_eq!(table.right_contraction(3, 3), (-1, 0));
2635        assert_eq!(table.right_contraction(3, 4), (0, 0));
2636        assert_eq!(table.right_contraction(3, 5), (0, 0));
2637        assert_eq!(table.right_contraction(3, 6), (0, 0));
2638        assert_eq!(table.right_contraction(3, 7), (0, 0));
2639        assert_eq!(table.right_contraction(3, 8), (0, 0));
2640        assert_eq!(table.right_contraction(3, 9), (0, 0));
2641        assert_eq!(table.right_contraction(3, 10), (0, 0));
2642        assert_eq!(table.right_contraction(3, 11), (0, 0));
2643        assert_eq!(table.right_contraction(3, 12), (0, 0));
2644        assert_eq!(table.right_contraction(3, 13), (0, 0));
2645        assert_eq!(table.right_contraction(3, 14), (0, 0));
2646        assert_eq!(table.right_contraction(3, 15), (0, 0));
2647        // Row e3
2648        assert_eq!(table.right_contraction(4, 0), (1, 4));
2649        assert_eq!(table.right_contraction(4, 1), (0, 0));
2650        assert_eq!(table.right_contraction(4, 2), (0, 0));
2651        assert_eq!(table.right_contraction(4, 3), (0, 0));
2652        assert_eq!(table.right_contraction(4, 4), (1, 0));
2653        assert_eq!(table.right_contraction(4, 5), (0, 0));
2654        assert_eq!(table.right_contraction(4, 6), (0, 0));
2655        assert_eq!(table.right_contraction(4, 7), (0, 0));
2656        assert_eq!(table.right_contraction(4, 8), (0, 0));
2657        assert_eq!(table.right_contraction(4, 9), (0, 0));
2658        assert_eq!(table.right_contraction(4, 10), (0, 0));
2659        assert_eq!(table.right_contraction(4, 11), (0, 0));
2660        assert_eq!(table.right_contraction(4, 12), (0, 0));
2661        assert_eq!(table.right_contraction(4, 13), (0, 0));
2662        assert_eq!(table.right_contraction(4, 14), (0, 0));
2663        assert_eq!(table.right_contraction(4, 15), (0, 0));
2664        // Row e13
2665        assert_eq!(table.right_contraction(5, 0), (1, 5));
2666        assert_eq!(table.right_contraction(5, 1), (-1, 4));
2667        assert_eq!(table.right_contraction(5, 2), (0, 0));
2668        assert_eq!(table.right_contraction(5, 3), (0, 0));
2669        assert_eq!(table.right_contraction(5, 4), (1, 1));
2670        assert_eq!(table.right_contraction(5, 5), (-1, 0));
2671        assert_eq!(table.right_contraction(5, 6), (0, 0));
2672        assert_eq!(table.right_contraction(5, 7), (0, 0));
2673        assert_eq!(table.right_contraction(5, 8), (0, 0));
2674        assert_eq!(table.right_contraction(5, 9), (0, 0));
2675        assert_eq!(table.right_contraction(5, 10), (0, 0));
2676        assert_eq!(table.right_contraction(5, 11), (0, 0));
2677        assert_eq!(table.right_contraction(5, 12), (0, 0));
2678        assert_eq!(table.right_contraction(5, 13), (0, 0));
2679        assert_eq!(table.right_contraction(5, 14), (0, 0));
2680        assert_eq!(table.right_contraction(5, 15), (0, 0));
2681        // Row e23
2682        assert_eq!(table.right_contraction(6, 0), (1, 6));
2683        assert_eq!(table.right_contraction(6, 1), (0, 0));
2684        assert_eq!(table.right_contraction(6, 2), (-1, 4));
2685        assert_eq!(table.right_contraction(6, 3), (0, 0));
2686        assert_eq!(table.right_contraction(6, 4), (1, 2));
2687        assert_eq!(table.right_contraction(6, 5), (0, 0));
2688        assert_eq!(table.right_contraction(6, 6), (-1, 0));
2689        assert_eq!(table.right_contraction(6, 7), (0, 0));
2690        assert_eq!(table.right_contraction(6, 8), (0, 0));
2691        assert_eq!(table.right_contraction(6, 9), (0, 0));
2692        assert_eq!(table.right_contraction(6, 10), (0, 0));
2693        assert_eq!(table.right_contraction(6, 11), (0, 0));
2694        assert_eq!(table.right_contraction(6, 12), (0, 0));
2695        assert_eq!(table.right_contraction(6, 13), (0, 0));
2696        assert_eq!(table.right_contraction(6, 14), (0, 0));
2697        assert_eq!(table.right_contraction(6, 15), (0, 0));
2698        // Row e123
2699        assert_eq!(table.right_contraction(7, 0), (1, 7));
2700        assert_eq!(table.right_contraction(7, 1), (1, 6));
2701        assert_eq!(table.right_contraction(7, 2), (-1, 5));
2702        assert_eq!(table.right_contraction(7, 3), (-1, 4));
2703        assert_eq!(table.right_contraction(7, 4), (1, 3));
2704        assert_eq!(table.right_contraction(7, 5), (1, 2));
2705        assert_eq!(table.right_contraction(7, 6), (-1, 1));
2706        assert_eq!(table.right_contraction(7, 7), (-1, 0));
2707        assert_eq!(table.right_contraction(7, 8), (0, 0));
2708        assert_eq!(table.right_contraction(7, 9), (0, 0));
2709        assert_eq!(table.right_contraction(7, 10), (0, 0));
2710        assert_eq!(table.right_contraction(7, 11), (0, 0));
2711        assert_eq!(table.right_contraction(7, 12), (0, 0));
2712        assert_eq!(table.right_contraction(7, 13), (0, 0));
2713        assert_eq!(table.right_contraction(7, 14), (0, 0));
2714        assert_eq!(table.right_contraction(7, 15), (0, 0));
2715        // Row e4
2716        assert_eq!(table.right_contraction(8, 0), (1, 8));
2717        assert_eq!(table.right_contraction(8, 1), (0, 0));
2718        assert_eq!(table.right_contraction(8, 2), (0, 0));
2719        assert_eq!(table.right_contraction(8, 3), (0, 0));
2720        assert_eq!(table.right_contraction(8, 4), (0, 0));
2721        assert_eq!(table.right_contraction(8, 5), (0, 0));
2722        assert_eq!(table.right_contraction(8, 6), (0, 0));
2723        assert_eq!(table.right_contraction(8, 7), (0, 0));
2724        assert_eq!(table.right_contraction(8, 8), (0, 0));
2725        assert_eq!(table.right_contraction(8, 9), (0, 0));
2726        assert_eq!(table.right_contraction(8, 10), (0, 0));
2727        assert_eq!(table.right_contraction(8, 11), (0, 0));
2728        assert_eq!(table.right_contraction(8, 12), (0, 0));
2729        assert_eq!(table.right_contraction(8, 13), (0, 0));
2730        assert_eq!(table.right_contraction(8, 14), (0, 0));
2731        assert_eq!(table.right_contraction(8, 15), (0, 0));
2732        // Row e14
2733        assert_eq!(table.right_contraction(9, 0), (1, 9));
2734        assert_eq!(table.right_contraction(9, 1), (-1, 8));
2735        assert_eq!(table.right_contraction(9, 2), (0, 0));
2736        assert_eq!(table.right_contraction(9, 3), (0, 0));
2737        assert_eq!(table.right_contraction(9, 4), (0, 0));
2738        assert_eq!(table.right_contraction(9, 5), (0, 0));
2739        assert_eq!(table.right_contraction(9, 6), (0, 0));
2740        assert_eq!(table.right_contraction(9, 7), (0, 0));
2741        assert_eq!(table.right_contraction(9, 8), (0, 0));
2742        assert_eq!(table.right_contraction(9, 9), (0, 0));
2743        assert_eq!(table.right_contraction(9, 10), (0, 0));
2744        assert_eq!(table.right_contraction(9, 11), (0, 0));
2745        assert_eq!(table.right_contraction(9, 12), (0, 0));
2746        assert_eq!(table.right_contraction(9, 13), (0, 0));
2747        assert_eq!(table.right_contraction(9, 14), (0, 0));
2748        assert_eq!(table.right_contraction(9, 15), (0, 0));
2749        // Row e24
2750        assert_eq!(table.right_contraction(10, 0), (1, 10));
2751        assert_eq!(table.right_contraction(10, 1), (0, 0));
2752        assert_eq!(table.right_contraction(10, 2), (-1, 8));
2753        assert_eq!(table.right_contraction(10, 3), (0, 0));
2754        assert_eq!(table.right_contraction(10, 4), (0, 0));
2755        assert_eq!(table.right_contraction(10, 5), (0, 0));
2756        assert_eq!(table.right_contraction(10, 6), (0, 0));
2757        assert_eq!(table.right_contraction(10, 7), (0, 0));
2758        assert_eq!(table.right_contraction(10, 8), (0, 0));
2759        assert_eq!(table.right_contraction(10, 9), (0, 0));
2760        assert_eq!(table.right_contraction(10, 10), (0, 0));
2761        assert_eq!(table.right_contraction(10, 11), (0, 0));
2762        assert_eq!(table.right_contraction(10, 12), (0, 0));
2763        assert_eq!(table.right_contraction(10, 13), (0, 0));
2764        assert_eq!(table.right_contraction(10, 14), (0, 0));
2765        assert_eq!(table.right_contraction(10, 15), (0, 0));
2766        // Row e124
2767        assert_eq!(table.right_contraction(11, 0), (1, 11));
2768        assert_eq!(table.right_contraction(11, 1), (1, 10));
2769        assert_eq!(table.right_contraction(11, 2), (-1, 9));
2770        assert_eq!(table.right_contraction(11, 3), (-1, 8));
2771        assert_eq!(table.right_contraction(11, 4), (0, 0));
2772        assert_eq!(table.right_contraction(11, 5), (0, 0));
2773        assert_eq!(table.right_contraction(11, 6), (0, 0));
2774        assert_eq!(table.right_contraction(11, 7), (0, 0));
2775        assert_eq!(table.right_contraction(11, 8), (0, 0));
2776        assert_eq!(table.right_contraction(11, 9), (0, 0));
2777        assert_eq!(table.right_contraction(11, 10), (0, 0));
2778        assert_eq!(table.right_contraction(11, 11), (0, 0));
2779        assert_eq!(table.right_contraction(11, 12), (0, 0));
2780        assert_eq!(table.right_contraction(11, 13), (0, 0));
2781        assert_eq!(table.right_contraction(11, 14), (0, 0));
2782        assert_eq!(table.right_contraction(11, 15), (0, 0));
2783        // Row e34
2784        assert_eq!(table.right_contraction(12, 0), (1, 12));
2785        assert_eq!(table.right_contraction(12, 1), (0, 0));
2786        assert_eq!(table.right_contraction(12, 2), (0, 0));
2787        assert_eq!(table.right_contraction(12, 3), (0, 0));
2788        assert_eq!(table.right_contraction(12, 4), (-1, 8));
2789        assert_eq!(table.right_contraction(12, 5), (0, 0));
2790        assert_eq!(table.right_contraction(12, 6), (0, 0));
2791        assert_eq!(table.right_contraction(12, 7), (0, 0));
2792        assert_eq!(table.right_contraction(12, 8), (0, 0));
2793        assert_eq!(table.right_contraction(12, 9), (0, 0));
2794        assert_eq!(table.right_contraction(12, 10), (0, 0));
2795        assert_eq!(table.right_contraction(12, 11), (0, 0));
2796        assert_eq!(table.right_contraction(12, 12), (0, 0));
2797        assert_eq!(table.right_contraction(12, 13), (0, 0));
2798        assert_eq!(table.right_contraction(12, 14), (0, 0));
2799        assert_eq!(table.right_contraction(12, 15), (0, 0));
2800        // Row e134
2801        assert_eq!(table.right_contraction(13, 0), (1, 13));
2802        assert_eq!(table.right_contraction(13, 1), (1, 12));
2803        assert_eq!(table.right_contraction(13, 2), (0, 0));
2804        assert_eq!(table.right_contraction(13, 3), (0, 0));
2805        assert_eq!(table.right_contraction(13, 4), (-1, 9));
2806        assert_eq!(table.right_contraction(13, 5), (-1, 8));
2807        assert_eq!(table.right_contraction(13, 6), (0, 0));
2808        assert_eq!(table.right_contraction(13, 7), (0, 0));
2809        assert_eq!(table.right_contraction(13, 8), (0, 0));
2810        assert_eq!(table.right_contraction(13, 9), (0, 0));
2811        assert_eq!(table.right_contraction(13, 10), (0, 0));
2812        assert_eq!(table.right_contraction(13, 11), (0, 0));
2813        assert_eq!(table.right_contraction(13, 12), (0, 0));
2814        assert_eq!(table.right_contraction(13, 13), (0, 0));
2815        assert_eq!(table.right_contraction(13, 14), (0, 0));
2816        assert_eq!(table.right_contraction(13, 15), (0, 0));
2817        // Row e234
2818        assert_eq!(table.right_contraction(14, 0), (1, 14));
2819        assert_eq!(table.right_contraction(14, 1), (0, 0));
2820        assert_eq!(table.right_contraction(14, 2), (1, 12));
2821        assert_eq!(table.right_contraction(14, 3), (0, 0));
2822        assert_eq!(table.right_contraction(14, 4), (-1, 10));
2823        assert_eq!(table.right_contraction(14, 5), (0, 0));
2824        assert_eq!(table.right_contraction(14, 6), (-1, 8));
2825        assert_eq!(table.right_contraction(14, 7), (0, 0));
2826        assert_eq!(table.right_contraction(14, 8), (0, 0));
2827        assert_eq!(table.right_contraction(14, 9), (0, 0));
2828        assert_eq!(table.right_contraction(14, 10), (0, 0));
2829        assert_eq!(table.right_contraction(14, 11), (0, 0));
2830        assert_eq!(table.right_contraction(14, 12), (0, 0));
2831        assert_eq!(table.right_contraction(14, 13), (0, 0));
2832        assert_eq!(table.right_contraction(14, 14), (0, 0));
2833        assert_eq!(table.right_contraction(14, 15), (0, 0));
2834        // Row e1234
2835        assert_eq!(table.right_contraction(15, 0), (1, 15));
2836        assert_eq!(table.right_contraction(15, 1), (-1, 14));
2837        assert_eq!(table.right_contraction(15, 2), (1, 13));
2838        assert_eq!(table.right_contraction(15, 3), (-1, 12));
2839        assert_eq!(table.right_contraction(15, 4), (-1, 11));
2840        assert_eq!(table.right_contraction(15, 5), (1, 10));
2841        assert_eq!(table.right_contraction(15, 6), (-1, 9));
2842        assert_eq!(table.right_contraction(15, 7), (1, 8));
2843        assert_eq!(table.right_contraction(15, 8), (0, 0));
2844        assert_eq!(table.right_contraction(15, 9), (0, 0));
2845        assert_eq!(table.right_contraction(15, 10), (0, 0));
2846        assert_eq!(table.right_contraction(15, 11), (0, 0));
2847        assert_eq!(table.right_contraction(15, 12), (0, 0));
2848        assert_eq!(table.right_contraction(15, 13), (0, 0));
2849        assert_eq!(table.right_contraction(15, 14), (0, 0));
2850        assert_eq!(table.right_contraction(15, 15), (0, 0));
2851    }
2852
2853    #[test]
2854    fn pga_3d_full_dot_table() {
2855        let algebra = Algebra::pga(3);
2856        let table = ProductTable::new(&algebra);
2857
2858        // Row 1
2859        assert_eq!(table.dot(0, 0), (1, 0));
2860        assert_eq!(table.dot(0, 1), (0, 0));
2861        assert_eq!(table.dot(0, 2), (0, 0));
2862        assert_eq!(table.dot(0, 3), (0, 0));
2863        assert_eq!(table.dot(0, 4), (0, 0));
2864        assert_eq!(table.dot(0, 5), (0, 0));
2865        assert_eq!(table.dot(0, 6), (0, 0));
2866        assert_eq!(table.dot(0, 7), (0, 0));
2867        assert_eq!(table.dot(0, 8), (0, 0));
2868        assert_eq!(table.dot(0, 9), (0, 0));
2869        assert_eq!(table.dot(0, 10), (0, 0));
2870        assert_eq!(table.dot(0, 11), (0, 0));
2871        assert_eq!(table.dot(0, 12), (0, 0));
2872        assert_eq!(table.dot(0, 13), (0, 0));
2873        assert_eq!(table.dot(0, 14), (0, 0));
2874        assert_eq!(table.dot(0, 15), (0, 0));
2875        // Row e1
2876        assert_eq!(table.dot(1, 0), (0, 0));
2877        assert_eq!(table.dot(1, 1), (1, 0));
2878        assert_eq!(table.dot(1, 2), (0, 0));
2879        assert_eq!(table.dot(1, 3), (0, 0));
2880        assert_eq!(table.dot(1, 4), (0, 0));
2881        assert_eq!(table.dot(1, 5), (0, 0));
2882        assert_eq!(table.dot(1, 6), (0, 0));
2883        assert_eq!(table.dot(1, 7), (0, 0));
2884        assert_eq!(table.dot(1, 8), (0, 0));
2885        assert_eq!(table.dot(1, 9), (0, 0));
2886        assert_eq!(table.dot(1, 10), (0, 0));
2887        assert_eq!(table.dot(1, 11), (0, 0));
2888        assert_eq!(table.dot(1, 12), (0, 0));
2889        assert_eq!(table.dot(1, 13), (0, 0));
2890        assert_eq!(table.dot(1, 14), (0, 0));
2891        assert_eq!(table.dot(1, 15), (0, 0));
2892        // Row e2
2893        assert_eq!(table.dot(2, 0), (0, 0));
2894        assert_eq!(table.dot(2, 1), (0, 0));
2895        assert_eq!(table.dot(2, 2), (1, 0));
2896        assert_eq!(table.dot(2, 3), (0, 0));
2897        assert_eq!(table.dot(2, 4), (0, 0));
2898        assert_eq!(table.dot(2, 5), (0, 0));
2899        assert_eq!(table.dot(2, 6), (0, 0));
2900        assert_eq!(table.dot(2, 7), (0, 0));
2901        assert_eq!(table.dot(2, 8), (0, 0));
2902        assert_eq!(table.dot(2, 9), (0, 0));
2903        assert_eq!(table.dot(2, 10), (0, 0));
2904        assert_eq!(table.dot(2, 11), (0, 0));
2905        assert_eq!(table.dot(2, 12), (0, 0));
2906        assert_eq!(table.dot(2, 13), (0, 0));
2907        assert_eq!(table.dot(2, 14), (0, 0));
2908        assert_eq!(table.dot(2, 15), (0, 0));
2909        // Row e12
2910        assert_eq!(table.dot(3, 0), (0, 0));
2911        assert_eq!(table.dot(3, 1), (0, 0));
2912        assert_eq!(table.dot(3, 2), (0, 0));
2913        assert_eq!(table.dot(3, 3), (-1, 0));
2914        assert_eq!(table.dot(3, 4), (0, 0));
2915        assert_eq!(table.dot(3, 5), (0, 0));
2916        assert_eq!(table.dot(3, 6), (0, 0));
2917        assert_eq!(table.dot(3, 7), (0, 0));
2918        assert_eq!(table.dot(3, 8), (0, 0));
2919        assert_eq!(table.dot(3, 9), (0, 0));
2920        assert_eq!(table.dot(3, 10), (0, 0));
2921        assert_eq!(table.dot(3, 11), (0, 0));
2922        assert_eq!(table.dot(3, 12), (0, 0));
2923        assert_eq!(table.dot(3, 13), (0, 0));
2924        assert_eq!(table.dot(3, 14), (0, 0));
2925        assert_eq!(table.dot(3, 15), (0, 0));
2926        // Row e3
2927        assert_eq!(table.dot(4, 0), (0, 0));
2928        assert_eq!(table.dot(4, 1), (0, 0));
2929        assert_eq!(table.dot(4, 2), (0, 0));
2930        assert_eq!(table.dot(4, 3), (0, 0));
2931        assert_eq!(table.dot(4, 4), (1, 0));
2932        assert_eq!(table.dot(4, 5), (0, 0));
2933        assert_eq!(table.dot(4, 6), (0, 0));
2934        assert_eq!(table.dot(4, 7), (0, 0));
2935        assert_eq!(table.dot(4, 8), (0, 0));
2936        assert_eq!(table.dot(4, 9), (0, 0));
2937        assert_eq!(table.dot(4, 10), (0, 0));
2938        assert_eq!(table.dot(4, 11), (0, 0));
2939        assert_eq!(table.dot(4, 12), (0, 0));
2940        assert_eq!(table.dot(4, 13), (0, 0));
2941        assert_eq!(table.dot(4, 14), (0, 0));
2942        assert_eq!(table.dot(4, 15), (0, 0));
2943        // Row e13
2944        assert_eq!(table.dot(5, 0), (0, 0));
2945        assert_eq!(table.dot(5, 1), (0, 0));
2946        assert_eq!(table.dot(5, 2), (0, 0));
2947        assert_eq!(table.dot(5, 3), (0, 0));
2948        assert_eq!(table.dot(5, 4), (0, 0));
2949        assert_eq!(table.dot(5, 5), (-1, 0));
2950        assert_eq!(table.dot(5, 6), (0, 0));
2951        assert_eq!(table.dot(5, 7), (0, 0));
2952        assert_eq!(table.dot(5, 8), (0, 0));
2953        assert_eq!(table.dot(5, 9), (0, 0));
2954        assert_eq!(table.dot(5, 10), (0, 0));
2955        assert_eq!(table.dot(5, 11), (0, 0));
2956        assert_eq!(table.dot(5, 12), (0, 0));
2957        assert_eq!(table.dot(5, 13), (0, 0));
2958        assert_eq!(table.dot(5, 14), (0, 0));
2959        assert_eq!(table.dot(5, 15), (0, 0));
2960        // Row e23
2961        assert_eq!(table.dot(6, 0), (0, 0));
2962        assert_eq!(table.dot(6, 1), (0, 0));
2963        assert_eq!(table.dot(6, 2), (0, 0));
2964        assert_eq!(table.dot(6, 3), (0, 0));
2965        assert_eq!(table.dot(6, 4), (0, 0));
2966        assert_eq!(table.dot(6, 5), (0, 0));
2967        assert_eq!(table.dot(6, 6), (-1, 0));
2968        assert_eq!(table.dot(6, 7), (0, 0));
2969        assert_eq!(table.dot(6, 8), (0, 0));
2970        assert_eq!(table.dot(6, 9), (0, 0));
2971        assert_eq!(table.dot(6, 10), (0, 0));
2972        assert_eq!(table.dot(6, 11), (0, 0));
2973        assert_eq!(table.dot(6, 12), (0, 0));
2974        assert_eq!(table.dot(6, 13), (0, 0));
2975        assert_eq!(table.dot(6, 14), (0, 0));
2976        assert_eq!(table.dot(6, 15), (0, 0));
2977        // Row e123
2978        assert_eq!(table.dot(7, 0), (0, 0));
2979        assert_eq!(table.dot(7, 1), (0, 0));
2980        assert_eq!(table.dot(7, 2), (0, 0));
2981        assert_eq!(table.dot(7, 3), (0, 0));
2982        assert_eq!(table.dot(7, 4), (0, 0));
2983        assert_eq!(table.dot(7, 5), (0, 0));
2984        assert_eq!(table.dot(7, 6), (0, 0));
2985        assert_eq!(table.dot(7, 7), (-1, 0));
2986        assert_eq!(table.dot(7, 8), (0, 0));
2987        assert_eq!(table.dot(7, 9), (0, 0));
2988        assert_eq!(table.dot(7, 10), (0, 0));
2989        assert_eq!(table.dot(7, 11), (0, 0));
2990        assert_eq!(table.dot(7, 12), (0, 0));
2991        assert_eq!(table.dot(7, 13), (0, 0));
2992        assert_eq!(table.dot(7, 14), (0, 0));
2993        assert_eq!(table.dot(7, 15), (0, 0));
2994        // Row e4
2995        assert_eq!(table.dot(8, 0), (0, 0));
2996        assert_eq!(table.dot(8, 1), (0, 0));
2997        assert_eq!(table.dot(8, 2), (0, 0));
2998        assert_eq!(table.dot(8, 3), (0, 0));
2999        assert_eq!(table.dot(8, 4), (0, 0));
3000        assert_eq!(table.dot(8, 5), (0, 0));
3001        assert_eq!(table.dot(8, 6), (0, 0));
3002        assert_eq!(table.dot(8, 7), (0, 0));
3003        assert_eq!(table.dot(8, 8), (0, 0));
3004        assert_eq!(table.dot(8, 9), (0, 0));
3005        assert_eq!(table.dot(8, 10), (0, 0));
3006        assert_eq!(table.dot(8, 11), (0, 0));
3007        assert_eq!(table.dot(8, 12), (0, 0));
3008        assert_eq!(table.dot(8, 13), (0, 0));
3009        assert_eq!(table.dot(8, 14), (0, 0));
3010        assert_eq!(table.dot(8, 15), (0, 0));
3011        // Row e14
3012        assert_eq!(table.dot(9, 0), (0, 0));
3013        assert_eq!(table.dot(9, 1), (0, 0));
3014        assert_eq!(table.dot(9, 2), (0, 0));
3015        assert_eq!(table.dot(9, 3), (0, 0));
3016        assert_eq!(table.dot(9, 4), (0, 0));
3017        assert_eq!(table.dot(9, 5), (0, 0));
3018        assert_eq!(table.dot(9, 6), (0, 0));
3019        assert_eq!(table.dot(9, 7), (0, 0));
3020        assert_eq!(table.dot(9, 8), (0, 0));
3021        assert_eq!(table.dot(9, 9), (0, 0));
3022        assert_eq!(table.dot(9, 10), (0, 0));
3023        assert_eq!(table.dot(9, 11), (0, 0));
3024        assert_eq!(table.dot(9, 12), (0, 0));
3025        assert_eq!(table.dot(9, 13), (0, 0));
3026        assert_eq!(table.dot(9, 14), (0, 0));
3027        assert_eq!(table.dot(9, 15), (0, 0));
3028        // Row e24
3029        assert_eq!(table.dot(10, 0), (0, 0));
3030        assert_eq!(table.dot(10, 1), (0, 0));
3031        assert_eq!(table.dot(10, 2), (0, 0));
3032        assert_eq!(table.dot(10, 3), (0, 0));
3033        assert_eq!(table.dot(10, 4), (0, 0));
3034        assert_eq!(table.dot(10, 5), (0, 0));
3035        assert_eq!(table.dot(10, 6), (0, 0));
3036        assert_eq!(table.dot(10, 7), (0, 0));
3037        assert_eq!(table.dot(10, 8), (0, 0));
3038        assert_eq!(table.dot(10, 9), (0, 0));
3039        assert_eq!(table.dot(10, 10), (0, 0));
3040        assert_eq!(table.dot(10, 11), (0, 0));
3041        assert_eq!(table.dot(10, 12), (0, 0));
3042        assert_eq!(table.dot(10, 13), (0, 0));
3043        assert_eq!(table.dot(10, 14), (0, 0));
3044        assert_eq!(table.dot(10, 15), (0, 0));
3045        // Row e124
3046        assert_eq!(table.dot(11, 0), (0, 0));
3047        assert_eq!(table.dot(11, 1), (0, 0));
3048        assert_eq!(table.dot(11, 2), (0, 0));
3049        assert_eq!(table.dot(11, 3), (0, 0));
3050        assert_eq!(table.dot(11, 4), (0, 0));
3051        assert_eq!(table.dot(11, 5), (0, 0));
3052        assert_eq!(table.dot(11, 6), (0, 0));
3053        assert_eq!(table.dot(11, 7), (0, 0));
3054        assert_eq!(table.dot(11, 8), (0, 0));
3055        assert_eq!(table.dot(11, 9), (0, 0));
3056        assert_eq!(table.dot(11, 10), (0, 0));
3057        assert_eq!(table.dot(11, 11), (0, 0));
3058        assert_eq!(table.dot(11, 12), (0, 0));
3059        assert_eq!(table.dot(11, 13), (0, 0));
3060        assert_eq!(table.dot(11, 14), (0, 0));
3061        assert_eq!(table.dot(11, 15), (0, 0));
3062        // Row e34
3063        assert_eq!(table.dot(12, 0), (0, 0));
3064        assert_eq!(table.dot(12, 1), (0, 0));
3065        assert_eq!(table.dot(12, 2), (0, 0));
3066        assert_eq!(table.dot(12, 3), (0, 0));
3067        assert_eq!(table.dot(12, 4), (0, 0));
3068        assert_eq!(table.dot(12, 5), (0, 0));
3069        assert_eq!(table.dot(12, 6), (0, 0));
3070        assert_eq!(table.dot(12, 7), (0, 0));
3071        assert_eq!(table.dot(12, 8), (0, 0));
3072        assert_eq!(table.dot(12, 9), (0, 0));
3073        assert_eq!(table.dot(12, 10), (0, 0));
3074        assert_eq!(table.dot(12, 11), (0, 0));
3075        assert_eq!(table.dot(12, 12), (0, 0));
3076        assert_eq!(table.dot(12, 13), (0, 0));
3077        assert_eq!(table.dot(12, 14), (0, 0));
3078        assert_eq!(table.dot(12, 15), (0, 0));
3079        // Row e134
3080        assert_eq!(table.dot(13, 0), (0, 0));
3081        assert_eq!(table.dot(13, 1), (0, 0));
3082        assert_eq!(table.dot(13, 2), (0, 0));
3083        assert_eq!(table.dot(13, 3), (0, 0));
3084        assert_eq!(table.dot(13, 4), (0, 0));
3085        assert_eq!(table.dot(13, 5), (0, 0));
3086        assert_eq!(table.dot(13, 6), (0, 0));
3087        assert_eq!(table.dot(13, 7), (0, 0));
3088        assert_eq!(table.dot(13, 8), (0, 0));
3089        assert_eq!(table.dot(13, 9), (0, 0));
3090        assert_eq!(table.dot(13, 10), (0, 0));
3091        assert_eq!(table.dot(13, 11), (0, 0));
3092        assert_eq!(table.dot(13, 12), (0, 0));
3093        assert_eq!(table.dot(13, 13), (0, 0));
3094        assert_eq!(table.dot(13, 14), (0, 0));
3095        assert_eq!(table.dot(13, 15), (0, 0));
3096        // Row e234
3097        assert_eq!(table.dot(14, 0), (0, 0));
3098        assert_eq!(table.dot(14, 1), (0, 0));
3099        assert_eq!(table.dot(14, 2), (0, 0));
3100        assert_eq!(table.dot(14, 3), (0, 0));
3101        assert_eq!(table.dot(14, 4), (0, 0));
3102        assert_eq!(table.dot(14, 5), (0, 0));
3103        assert_eq!(table.dot(14, 6), (0, 0));
3104        assert_eq!(table.dot(14, 7), (0, 0));
3105        assert_eq!(table.dot(14, 8), (0, 0));
3106        assert_eq!(table.dot(14, 9), (0, 0));
3107        assert_eq!(table.dot(14, 10), (0, 0));
3108        assert_eq!(table.dot(14, 11), (0, 0));
3109        assert_eq!(table.dot(14, 12), (0, 0));
3110        assert_eq!(table.dot(14, 13), (0, 0));
3111        assert_eq!(table.dot(14, 14), (0, 0));
3112        assert_eq!(table.dot(14, 15), (0, 0));
3113        // Row e1234
3114        assert_eq!(table.dot(15, 0), (0, 0));
3115        assert_eq!(table.dot(15, 1), (0, 0));
3116        assert_eq!(table.dot(15, 2), (0, 0));
3117        assert_eq!(table.dot(15, 3), (0, 0));
3118        assert_eq!(table.dot(15, 4), (0, 0));
3119        assert_eq!(table.dot(15, 5), (0, 0));
3120        assert_eq!(table.dot(15, 6), (0, 0));
3121        assert_eq!(table.dot(15, 7), (0, 0));
3122        assert_eq!(table.dot(15, 8), (0, 0));
3123        assert_eq!(table.dot(15, 9), (0, 0));
3124        assert_eq!(table.dot(15, 10), (0, 0));
3125        assert_eq!(table.dot(15, 11), (0, 0));
3126        assert_eq!(table.dot(15, 12), (0, 0));
3127        assert_eq!(table.dot(15, 13), (0, 0));
3128        assert_eq!(table.dot(15, 14), (0, 0));
3129        assert_eq!(table.dot(15, 15), (0, 0));
3130    }
3131
3132    #[test]
3133    fn pga_3d_full_antiproduct_table() {
3134        let algebra = Algebra::pga(3);
3135        let table = ProductTable::new(&algebra);
3136
3137        // Row scalar
3138        assert_eq!(table.antiproduct(0, 0), (0, 0));
3139        assert_eq!(table.antiproduct(0, 1), (0, 0));
3140        assert_eq!(table.antiproduct(0, 2), (0, 0));
3141        assert_eq!(table.antiproduct(0, 3), (0, 0));
3142        assert_eq!(table.antiproduct(0, 4), (0, 0));
3143        assert_eq!(table.antiproduct(0, 5), (0, 0));
3144        assert_eq!(table.antiproduct(0, 6), (0, 0));
3145        assert_eq!(table.antiproduct(0, 7), (0, 0));
3146        assert_eq!(table.antiproduct(0, 8), (1, 7));
3147        assert_eq!(table.antiproduct(0, 9), (-1, 6));
3148        assert_eq!(table.antiproduct(0, 10), (1, 5));
3149        assert_eq!(table.antiproduct(0, 11), (-1, 4));
3150        assert_eq!(table.antiproduct(0, 12), (-1, 3));
3151        assert_eq!(table.antiproduct(0, 13), (1, 2));
3152        assert_eq!(table.antiproduct(0, 14), (-1, 1));
3153        assert_eq!(table.antiproduct(0, 15), (1, 0));
3154        // Row e1
3155        assert_eq!(table.antiproduct(1, 0), (0, 0));
3156        assert_eq!(table.antiproduct(1, 1), (0, 0));
3157        assert_eq!(table.antiproduct(1, 2), (0, 0));
3158        assert_eq!(table.antiproduct(1, 3), (0, 0));
3159        assert_eq!(table.antiproduct(1, 4), (0, 0));
3160        assert_eq!(table.antiproduct(1, 5), (0, 0));
3161        assert_eq!(table.antiproduct(1, 6), (0, 0));
3162        assert_eq!(table.antiproduct(1, 7), (0, 0));
3163        assert_eq!(table.antiproduct(1, 8), (-1, 6));
3164        assert_eq!(table.antiproduct(1, 9), (1, 7));
3165        assert_eq!(table.antiproduct(1, 10), (1, 4));
3166        assert_eq!(table.antiproduct(1, 11), (-1, 5));
3167        assert_eq!(table.antiproduct(1, 12), (-1, 2));
3168        assert_eq!(table.antiproduct(1, 13), (1, 3));
3169        assert_eq!(table.antiproduct(1, 14), (1, 0));
3170        assert_eq!(table.antiproduct(1, 15), (-1, 1));
3171        // Row e2
3172        assert_eq!(table.antiproduct(2, 0), (0, 0));
3173        assert_eq!(table.antiproduct(2, 1), (0, 0));
3174        assert_eq!(table.antiproduct(2, 2), (0, 0));
3175        assert_eq!(table.antiproduct(2, 3), (0, 0));
3176        assert_eq!(table.antiproduct(2, 4), (0, 0));
3177        assert_eq!(table.antiproduct(2, 5), (0, 0));
3178        assert_eq!(table.antiproduct(2, 6), (0, 0));
3179        assert_eq!(table.antiproduct(2, 7), (0, 0));
3180        assert_eq!(table.antiproduct(2, 8), (1, 5));
3181        assert_eq!(table.antiproduct(2, 9), (-1, 4));
3182        assert_eq!(table.antiproduct(2, 10), (1, 7));
3183        assert_eq!(table.antiproduct(2, 11), (-1, 6));
3184        assert_eq!(table.antiproduct(2, 12), (1, 1));
3185        assert_eq!(table.antiproduct(2, 13), (-1, 0));
3186        assert_eq!(table.antiproduct(2, 14), (1, 3));
3187        assert_eq!(table.antiproduct(2, 15), (-1, 2));
3188        // Row e12
3189        assert_eq!(table.antiproduct(3, 0), (0, 0));
3190        assert_eq!(table.antiproduct(3, 1), (0, 0));
3191        assert_eq!(table.antiproduct(3, 2), (0, 0));
3192        assert_eq!(table.antiproduct(3, 3), (0, 0));
3193        assert_eq!(table.antiproduct(3, 4), (0, 0));
3194        assert_eq!(table.antiproduct(3, 5), (0, 0));
3195        assert_eq!(table.antiproduct(3, 6), (0, 0));
3196        assert_eq!(table.antiproduct(3, 7), (0, 0));
3197        assert_eq!(table.antiproduct(3, 8), (-1, 4));
3198        assert_eq!(table.antiproduct(3, 9), (1, 5));
3199        assert_eq!(table.antiproduct(3, 10), (1, 6));
3200        assert_eq!(table.antiproduct(3, 11), (-1, 7));
3201        assert_eq!(table.antiproduct(3, 12), (1, 0));
3202        assert_eq!(table.antiproduct(3, 13), (-1, 1));
3203        assert_eq!(table.antiproduct(3, 14), (-1, 2));
3204        assert_eq!(table.antiproduct(3, 15), (1, 3));
3205        // Row e3
3206        assert_eq!(table.antiproduct(4, 0), (0, 0));
3207        assert_eq!(table.antiproduct(4, 1), (0, 0));
3208        assert_eq!(table.antiproduct(4, 2), (0, 0));
3209        assert_eq!(table.antiproduct(4, 3), (0, 0));
3210        assert_eq!(table.antiproduct(4, 4), (0, 0));
3211        assert_eq!(table.antiproduct(4, 5), (0, 0));
3212        assert_eq!(table.antiproduct(4, 6), (0, 0));
3213        assert_eq!(table.antiproduct(4, 7), (0, 0));
3214        assert_eq!(table.antiproduct(4, 8), (-1, 3));
3215        assert_eq!(table.antiproduct(4, 9), (1, 2));
3216        assert_eq!(table.antiproduct(4, 10), (-1, 1));
3217        assert_eq!(table.antiproduct(4, 11), (1, 0));
3218        assert_eq!(table.antiproduct(4, 12), (1, 7));
3219        assert_eq!(table.antiproduct(4, 13), (-1, 6));
3220        assert_eq!(table.antiproduct(4, 14), (1, 5));
3221        assert_eq!(table.antiproduct(4, 15), (-1, 4));
3222        // Row e13
3223        assert_eq!(table.antiproduct(5, 0), (0, 0));
3224        assert_eq!(table.antiproduct(5, 1), (0, 0));
3225        assert_eq!(table.antiproduct(5, 2), (0, 0));
3226        assert_eq!(table.antiproduct(5, 3), (0, 0));
3227        assert_eq!(table.antiproduct(5, 4), (0, 0));
3228        assert_eq!(table.antiproduct(5, 5), (0, 0));
3229        assert_eq!(table.antiproduct(5, 6), (0, 0));
3230        assert_eq!(table.antiproduct(5, 7), (0, 0));
3231        assert_eq!(table.antiproduct(5, 8), (1, 2));
3232        assert_eq!(table.antiproduct(5, 9), (-1, 3));
3233        assert_eq!(table.antiproduct(5, 10), (-1, 0));
3234        assert_eq!(table.antiproduct(5, 11), (1, 1));
3235        assert_eq!(table.antiproduct(5, 12), (1, 6));
3236        assert_eq!(table.antiproduct(5, 13), (-1, 7));
3237        assert_eq!(table.antiproduct(5, 14), (-1, 4));
3238        assert_eq!(table.antiproduct(5, 15), (1, 5));
3239        // Row e23
3240        assert_eq!(table.antiproduct(6, 0), (0, 0));
3241        assert_eq!(table.antiproduct(6, 1), (0, 0));
3242        assert_eq!(table.antiproduct(6, 2), (0, 0));
3243        assert_eq!(table.antiproduct(6, 3), (0, 0));
3244        assert_eq!(table.antiproduct(6, 4), (0, 0));
3245        assert_eq!(table.antiproduct(6, 5), (0, 0));
3246        assert_eq!(table.antiproduct(6, 6), (0, 0));
3247        assert_eq!(table.antiproduct(6, 7), (0, 0));
3248        assert_eq!(table.antiproduct(6, 8), (-1, 1));
3249        assert_eq!(table.antiproduct(6, 9), (1, 0));
3250        assert_eq!(table.antiproduct(6, 10), (-1, 3));
3251        assert_eq!(table.antiproduct(6, 11), (1, 2));
3252        assert_eq!(table.antiproduct(6, 12), (-1, 5));
3253        assert_eq!(table.antiproduct(6, 13), (1, 4));
3254        assert_eq!(table.antiproduct(6, 14), (-1, 7));
3255        assert_eq!(table.antiproduct(6, 15), (1, 6));
3256        // Row e123
3257        assert_eq!(table.antiproduct(7, 0), (0, 0));
3258        assert_eq!(table.antiproduct(7, 1), (0, 0));
3259        assert_eq!(table.antiproduct(7, 2), (0, 0));
3260        assert_eq!(table.antiproduct(7, 3), (0, 0));
3261        assert_eq!(table.antiproduct(7, 4), (0, 0));
3262        assert_eq!(table.antiproduct(7, 5), (0, 0));
3263        assert_eq!(table.antiproduct(7, 6), (0, 0));
3264        assert_eq!(table.antiproduct(7, 7), (0, 0));
3265        assert_eq!(table.antiproduct(7, 8), (1, 0));
3266        assert_eq!(table.antiproduct(7, 9), (-1, 1));
3267        assert_eq!(table.antiproduct(7, 10), (-1, 2));
3268        assert_eq!(table.antiproduct(7, 11), (1, 3));
3269        assert_eq!(table.antiproduct(7, 12), (-1, 4));
3270        assert_eq!(table.antiproduct(7, 13), (1, 5));
3271        assert_eq!(table.antiproduct(7, 14), (1, 6));
3272        assert_eq!(table.antiproduct(7, 15), (-1, 7));
3273        // Row e4
3274        assert_eq!(table.antiproduct(8, 0), (-1, 7));
3275        assert_eq!(table.antiproduct(8, 1), (1, 6));
3276        assert_eq!(table.antiproduct(8, 2), (-1, 5));
3277        assert_eq!(table.antiproduct(8, 3), (1, 4));
3278        assert_eq!(table.antiproduct(8, 4), (1, 3));
3279        assert_eq!(table.antiproduct(8, 5), (-1, 2));
3280        assert_eq!(table.antiproduct(8, 6), (1, 1));
3281        assert_eq!(table.antiproduct(8, 7), (-1, 0));
3282        assert_eq!(table.antiproduct(8, 8), (-1, 15));
3283        assert_eq!(table.antiproduct(8, 9), (1, 14));
3284        assert_eq!(table.antiproduct(8, 10), (-1, 13));
3285        assert_eq!(table.antiproduct(8, 11), (1, 12));
3286        assert_eq!(table.antiproduct(8, 12), (1, 11));
3287        assert_eq!(table.antiproduct(8, 13), (-1, 10));
3288        assert_eq!(table.antiproduct(8, 14), (1, 9));
3289        assert_eq!(table.antiproduct(8, 15), (-1, 8));
3290        // Row e14
3291        assert_eq!(table.antiproduct(9, 0), (-1, 6));
3292        assert_eq!(table.antiproduct(9, 1), (1, 7));
3293        assert_eq!(table.antiproduct(9, 2), (1, 4));
3294        assert_eq!(table.antiproduct(9, 3), (-1, 5));
3295        assert_eq!(table.antiproduct(9, 4), (-1, 2));
3296        assert_eq!(table.antiproduct(9, 5), (1, 3));
3297        assert_eq!(table.antiproduct(9, 6), (1, 0));
3298        assert_eq!(table.antiproduct(9, 7), (-1, 1));
3299        assert_eq!(table.antiproduct(9, 8), (1, 14));
3300        assert_eq!(table.antiproduct(9, 9), (-1, 15));
3301        assert_eq!(table.antiproduct(9, 10), (-1, 12));
3302        assert_eq!(table.antiproduct(9, 11), (1, 13));
3303        assert_eq!(table.antiproduct(9, 12), (1, 10));
3304        assert_eq!(table.antiproduct(9, 13), (-1, 11));
3305        assert_eq!(table.antiproduct(9, 14), (-1, 8));
3306        assert_eq!(table.antiproduct(9, 15), (1, 9));
3307        // Row e24
3308        assert_eq!(table.antiproduct(10, 0), (1, 5));
3309        assert_eq!(table.antiproduct(10, 1), (-1, 4));
3310        assert_eq!(table.antiproduct(10, 2), (1, 7));
3311        assert_eq!(table.antiproduct(10, 3), (-1, 6));
3312        assert_eq!(table.antiproduct(10, 4), (1, 1));
3313        assert_eq!(table.antiproduct(10, 5), (-1, 0));
3314        assert_eq!(table.antiproduct(10, 6), (1, 3));
3315        assert_eq!(table.antiproduct(10, 7), (-1, 2));
3316        assert_eq!(table.antiproduct(10, 8), (-1, 13));
3317        assert_eq!(table.antiproduct(10, 9), (1, 12));
3318        assert_eq!(table.antiproduct(10, 10), (-1, 15));
3319        assert_eq!(table.antiproduct(10, 11), (1, 14));
3320        assert_eq!(table.antiproduct(10, 12), (-1, 9));
3321        assert_eq!(table.antiproduct(10, 13), (1, 8));
3322        assert_eq!(table.antiproduct(10, 14), (-1, 11));
3323        assert_eq!(table.antiproduct(10, 15), (1, 10));
3324        // Row e124
3325        assert_eq!(table.antiproduct(11, 0), (1, 4));
3326        assert_eq!(table.antiproduct(11, 1), (-1, 5));
3327        assert_eq!(table.antiproduct(11, 2), (-1, 6));
3328        assert_eq!(table.antiproduct(11, 3), (1, 7));
3329        assert_eq!(table.antiproduct(11, 4), (-1, 0));
3330        assert_eq!(table.antiproduct(11, 5), (1, 1));
3331        assert_eq!(table.antiproduct(11, 6), (1, 2));
3332        assert_eq!(table.antiproduct(11, 7), (-1, 3));
3333        assert_eq!(table.antiproduct(11, 8), (1, 12));
3334        assert_eq!(table.antiproduct(11, 9), (-1, 13));
3335        assert_eq!(table.antiproduct(11, 10), (-1, 14));
3336        assert_eq!(table.antiproduct(11, 11), (1, 15));
3337        assert_eq!(table.antiproduct(11, 12), (-1, 8));
3338        assert_eq!(table.antiproduct(11, 13), (1, 9));
3339        assert_eq!(table.antiproduct(11, 14), (1, 10));
3340        assert_eq!(table.antiproduct(11, 15), (-1, 11));
3341        // Row e34
3342        assert_eq!(table.antiproduct(12, 0), (-1, 3));
3343        assert_eq!(table.antiproduct(12, 1), (1, 2));
3344        assert_eq!(table.antiproduct(12, 2), (-1, 1));
3345        assert_eq!(table.antiproduct(12, 3), (1, 0));
3346        assert_eq!(table.antiproduct(12, 4), (1, 7));
3347        assert_eq!(table.antiproduct(12, 5), (-1, 6));
3348        assert_eq!(table.antiproduct(12, 6), (1, 5));
3349        assert_eq!(table.antiproduct(12, 7), (-1, 4));
3350        assert_eq!(table.antiproduct(12, 8), (1, 11));
3351        assert_eq!(table.antiproduct(12, 9), (-1, 10));
3352        assert_eq!(table.antiproduct(12, 10), (1, 9));
3353        assert_eq!(table.antiproduct(12, 11), (-1, 8));
3354        assert_eq!(table.antiproduct(12, 12), (-1, 15));
3355        assert_eq!(table.antiproduct(12, 13), (1, 14));
3356        assert_eq!(table.antiproduct(12, 14), (-1, 13));
3357        assert_eq!(table.antiproduct(12, 15), (1, 12));
3358        // Row e134
3359        assert_eq!(table.antiproduct(13, 0), (-1, 2));
3360        assert_eq!(table.antiproduct(13, 1), (1, 3));
3361        assert_eq!(table.antiproduct(13, 2), (1, 0));
3362        assert_eq!(table.antiproduct(13, 3), (-1, 1));
3363        assert_eq!(table.antiproduct(13, 4), (-1, 6));
3364        assert_eq!(table.antiproduct(13, 5), (1, 7));
3365        assert_eq!(table.antiproduct(13, 6), (1, 4));
3366        assert_eq!(table.antiproduct(13, 7), (-1, 5));
3367        assert_eq!(table.antiproduct(13, 8), (-1, 10));
3368        assert_eq!(table.antiproduct(13, 9), (1, 11));
3369        assert_eq!(table.antiproduct(13, 10), (1, 8));
3370        assert_eq!(table.antiproduct(13, 11), (-1, 9));
3371        assert_eq!(table.antiproduct(13, 12), (-1, 14));
3372        assert_eq!(table.antiproduct(13, 13), (1, 15));
3373        assert_eq!(table.antiproduct(13, 14), (1, 12));
3374        assert_eq!(table.antiproduct(13, 15), (-1, 13));
3375        // Row e234
3376        assert_eq!(table.antiproduct(14, 0), (1, 1));
3377        assert_eq!(table.antiproduct(14, 1), (-1, 0));
3378        assert_eq!(table.antiproduct(14, 2), (1, 3));
3379        assert_eq!(table.antiproduct(14, 3), (-1, 2));
3380        assert_eq!(table.antiproduct(14, 4), (1, 5));
3381        assert_eq!(table.antiproduct(14, 5), (-1, 4));
3382        assert_eq!(table.antiproduct(14, 6), (1, 7));
3383        assert_eq!(table.antiproduct(14, 7), (-1, 6));
3384        assert_eq!(table.antiproduct(14, 8), (1, 9));
3385        assert_eq!(table.antiproduct(14, 9), (-1, 8));
3386        assert_eq!(table.antiproduct(14, 10), (1, 11));
3387        assert_eq!(table.antiproduct(14, 11), (-1, 10));
3388        assert_eq!(table.antiproduct(14, 12), (1, 13));
3389        assert_eq!(table.antiproduct(14, 13), (-1, 12));
3390        assert_eq!(table.antiproduct(14, 14), (1, 15));
3391        assert_eq!(table.antiproduct(14, 15), (-1, 14));
3392        // Row e1234
3393        assert_eq!(table.antiproduct(15, 0), (1, 0));
3394        assert_eq!(table.antiproduct(15, 1), (-1, 1));
3395        assert_eq!(table.antiproduct(15, 2), (-1, 2));
3396        assert_eq!(table.antiproduct(15, 3), (1, 3));
3397        assert_eq!(table.antiproduct(15, 4), (-1, 4));
3398        assert_eq!(table.antiproduct(15, 5), (1, 5));
3399        assert_eq!(table.antiproduct(15, 6), (1, 6));
3400        assert_eq!(table.antiproduct(15, 7), (-1, 7));
3401        assert_eq!(table.antiproduct(15, 8), (-1, 8));
3402        assert_eq!(table.antiproduct(15, 9), (1, 9));
3403        assert_eq!(table.antiproduct(15, 10), (1, 10));
3404        assert_eq!(table.antiproduct(15, 11), (-1, 11));
3405        assert_eq!(table.antiproduct(15, 12), (1, 12));
3406        assert_eq!(table.antiproduct(15, 13), (-1, 13));
3407        assert_eq!(table.antiproduct(15, 14), (-1, 14));
3408        assert_eq!(table.antiproduct(15, 15), (1, 15));
3409    }
3410
3411    #[test]
3412    fn pga_2d_antiproduct_pseudoscalar_identity() {
3413        // 2D PGA: Cl(2,0,1)
3414        let algebra = Algebra::new(2, 0, 1);
3415        let table = ProductTable::new(&algebra);
3416
3417        // Blade indices for 2D PGA (8 blades):
3418        // 0 = scalar (grade 0)
3419        // 1 = e1, 2 = e2, 4 = e0 (grade 1)
3420        // 3 = e12, 5 = e01, 6 = e02 (grade 2)
3421        // 7 = e012 (grade 3, pseudoscalar)
3422
3423        let pseudoscalar = 7;
3424
3425        // Test: pseudoscalar should act as identity for antiproduct
3426        // i.e., pseudoscalar ⊛ x = ±x for all x
3427        println!("2D PGA pseudoscalar (e012) antiproduct:");
3428        for i in 0..8 {
3429            let (sign, result) = table.antiproduct(pseudoscalar, i);
3430            println!("  e012 ⊛ blade[{}] = ({}, {})", i, sign, result);
3431            // The result blade should equal input blade (identity behavior)
3432            assert_eq!(
3433                result, i,
3434                "pseudoscalar ⊛ blade[{}] should give blade[{}]",
3435                i, i
3436            );
3437        }
3438    }
3439
3440    #[test]
3441    fn pga_2d_motor_point_antiproduct() {
3442        // 2D PGA: Cl(2,0,1)
3443        let algebra = Algebra::new(2, 0, 1);
3444        let table = ProductTable::new(&algebra);
3445
3446        // Motor blades: s=0, e01=5, e02=6, e12=3
3447        // Point blades: e1=1, e2=2, e0=4
3448
3449        println!("2D PGA Motor ⊛ Point antiproducts:");
3450        let motor_blades = [(0, "s"), (5, "e01"), (6, "e02"), (3, "e12")];
3451        let point_blades = [(1, "e1"), (2, "e2"), (4, "e0")];
3452
3453        for &(m, m_name) in &motor_blades {
3454            for &(p, p_name) in &point_blades {
3455                let (sign, result) = table.antiproduct(m, p);
3456                println!("  {} ⊛ {} = ({}, {})", m_name, p_name, sign, result);
3457            }
3458        }
3459    }
3460}