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}