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