Skip to main content

clifford_codegen/algebra/
versor.rs

1//! Versor identification and classification.
2//!
3//! A versor in Geometric Algebra is an element that can be expressed as
4//! a geometric product of invertible vectors. Versors are fundamental for
5//! representing transformations via the sandwich product: `X' = V * X * rev(V)`.
6//!
7//! # Versor Types
8//!
9//! | Type | Grades | Parity | Transformation |
10//! |------|--------|--------|----------------|
11//! | Unit Vector | [1] | Odd | Reflection |
12//! | Rotor (2D/3D) | [0, 2] | Even | Rotation |
13//! | Motor (PGA) | [0, 2, 4] | Even | Rigid motion |
14//! | Flector (PGA) | [1, 3] | Odd | Reflection + translation |
15//!
16//! # Properties
17//!
18//! 1. **Grade Parity**: All grades have the same parity (all even or all odd)
19//! 2. **Versor Constraint**: `V * rev(V) = scalar` (or pseudoscalar for odd versors)
20//! 3. **Closure**: Even * Even = Even, Even * Odd = Odd
21
22/// Versor classification by grade parity.
23///
24/// Versors are classified as even or odd based on the parity of their grades.
25/// Even versors (rotors, motors) preserve orientation; odd versors (reflectors,
26/// flectors) reverse orientation.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum VersorParity {
29    /// Even versor: grades 0, 2, 4, ... (rotors, motors).
30    ///
31    /// Even versors are products of an even number of vectors and preserve
32    /// orientation under the sandwich product.
33    Even,
34
35    /// Odd versor: grades 1, 3, 5, ... (reflectors, flectors).
36    ///
37    /// Odd versors are products of an odd number of vectors and reverse
38    /// orientation under the sandwich product.
39    Odd,
40}
41
42impl VersorParity {
43    /// Returns the parity value (0 for even, 1 for odd).
44    #[inline]
45    pub fn value(&self) -> usize {
46        match self {
47            VersorParity::Even => 0,
48            VersorParity::Odd => 1,
49        }
50    }
51
52    /// Returns the result of composing two versors.
53    ///
54    /// Even * Even = Even, Odd * Odd = Even, Even * Odd = Odd.
55    #[inline]
56    pub fn compose(self, other: VersorParity) -> VersorParity {
57        if self.value() == other.value() {
58            VersorParity::Even
59        } else {
60            VersorParity::Odd
61        }
62    }
63}
64
65/// Determines the versor parity of a type based on its grades.
66///
67/// Returns `Some(parity)` if all grades have the same parity, `None` otherwise.
68///
69/// # Examples
70///
71/// ```ignore
72/// // Even versors
73/// assert_eq!(versor_parity(&[0, 2]), Some(VersorParity::Even));    // Rotor
74/// assert_eq!(versor_parity(&[0, 2, 4]), Some(VersorParity::Even)); // Motor
75///
76/// // Odd versors
77/// assert_eq!(versor_parity(&[1]), Some(VersorParity::Odd));        // Vector
78/// assert_eq!(versor_parity(&[1, 3]), Some(VersorParity::Odd));     // Flector
79///
80/// // Not a versor (mixed parity)
81/// assert_eq!(versor_parity(&[0, 1, 2]), None);
82/// ```
83pub fn versor_parity(grades: &[usize]) -> Option<VersorParity> {
84    if grades.is_empty() {
85        return None;
86    }
87
88    let first_parity = grades[0] % 2;
89    if grades.iter().all(|&g| g % 2 == first_parity) {
90        Some(if first_parity == 0 {
91            VersorParity::Even
92        } else {
93            VersorParity::Odd
94        })
95    } else {
96        None
97    }
98}
99
100/// Information about a verified versor type.
101#[derive(Debug, Clone)]
102pub struct VersorInfo {
103    /// The parity of the versor (even or odd).
104    pub parity: VersorParity,
105
106    /// Whether this is a unit versor (`V * rev(V) = 1`).
107    ///
108    /// Unit versors have a simpler sandwich product formula since
109    /// `V⁻¹ = rev(V)` for unit versors.
110    pub is_unit: bool,
111
112    /// Types that this versor can transform via sandwich product.
113    ///
114    /// A type can be transformed if the sandwich product `V * X * rev(V)`
115    /// produces output of the same grades as the input.
116    pub sandwich_targets: Vec<String>,
117}
118
119impl Default for VersorInfo {
120    fn default() -> Self {
121        Self {
122            parity: VersorParity::Even,
123            is_unit: false,
124            sandwich_targets: Vec::new(),
125        }
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn even_versor_identification() {
135        // Scalar (grade 0)
136        assert_eq!(versor_parity(&[0]), Some(VersorParity::Even));
137
138        // Rotor (grades 0, 2)
139        assert_eq!(versor_parity(&[0, 2]), Some(VersorParity::Even));
140
141        // Motor (grades 0, 2, 4)
142        assert_eq!(versor_parity(&[0, 2, 4]), Some(VersorParity::Even));
143
144        // Even multivector (grades 0, 2, 4, 6)
145        assert_eq!(versor_parity(&[0, 2, 4, 6]), Some(VersorParity::Even));
146    }
147
148    #[test]
149    fn odd_versor_identification() {
150        // Vector (grade 1)
151        assert_eq!(versor_parity(&[1]), Some(VersorParity::Odd));
152
153        // Flector (grades 1, 3)
154        assert_eq!(versor_parity(&[1, 3]), Some(VersorParity::Odd));
155
156        // Odd multivector (grades 1, 3, 5)
157        assert_eq!(versor_parity(&[1, 3, 5]), Some(VersorParity::Odd));
158    }
159
160    #[test]
161    fn mixed_parity_not_versor() {
162        // Mixed grades are not versors
163        assert_eq!(versor_parity(&[0, 1]), None);
164        assert_eq!(versor_parity(&[0, 1, 2]), None);
165        assert_eq!(versor_parity(&[1, 2]), None);
166        assert_eq!(versor_parity(&[0, 2, 3]), None);
167    }
168
169    #[test]
170    fn empty_grades_not_versor() {
171        assert_eq!(versor_parity(&[]), None);
172    }
173
174    #[test]
175    fn parity_composition() {
176        // Even * Even = Even
177        assert_eq!(
178            VersorParity::Even.compose(VersorParity::Even),
179            VersorParity::Even
180        );
181
182        // Odd * Odd = Even
183        assert_eq!(
184            VersorParity::Odd.compose(VersorParity::Odd),
185            VersorParity::Even
186        );
187
188        // Even * Odd = Odd
189        assert_eq!(
190            VersorParity::Even.compose(VersorParity::Odd),
191            VersorParity::Odd
192        );
193
194        // Odd * Even = Odd
195        assert_eq!(
196            VersorParity::Odd.compose(VersorParity::Even),
197            VersorParity::Odd
198        );
199    }
200}