clifford_codegen/algebra/blade.rs
1//! Blade representation and operations.
2//!
3//! A blade is a basis element in a geometric algebra, represented by its
4//! bitmask index where bit `i` indicates the presence of basis vector `eᵢ`.
5
6use std::fmt;
7
8/// Maximum supported dimension.
9///
10/// Limits the algebra to at most 6 basis vectors (64 blades).
11pub const MAX_DIM: usize = 6;
12
13/// A basis blade in a geometric algebra.
14///
15/// Blades are represented as bitmasks where bit `i` indicates the presence
16/// of basis vector `eᵢ`. This representation is always canonical since
17/// bits are inherently ordered.
18///
19/// # Examples
20///
21/// ```
22/// use clifford_codegen::algebra::Blade;
23///
24/// // Scalar (grade 0)
25/// let scalar = Blade::scalar();
26/// assert_eq!(scalar.grade(), 0);
27/// assert_eq!(scalar.index(), 0);
28///
29/// // Basis vector e₁
30/// let e1 = Blade::basis(0);
31/// assert_eq!(e1.grade(), 1);
32/// assert_eq!(e1.index(), 1);
33///
34/// // Bivector e₁₂
35/// let e12 = Blade::from_index(0b11);
36/// assert_eq!(e12.grade(), 2);
37/// ```
38#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
39pub struct Blade {
40 /// Bitmask representation where bit `i` = 1 means `eᵢ` is present.
41 index: usize,
42}
43
44impl Blade {
45 /// Creates a blade from its bitmask index.
46 ///
47 /// # Example
48 ///
49 /// ```
50 /// use clifford_codegen::algebra::Blade;
51 ///
52 /// let e12 = Blade::from_index(0b11);
53 /// assert_eq!(e12.index(), 3);
54 /// ```
55 #[inline]
56 pub const fn from_index(index: usize) -> Self {
57 Self { index }
58 }
59
60 /// Creates the scalar blade (grade 0, index 0).
61 ///
62 /// # Example
63 ///
64 /// ```
65 /// use clifford_codegen::algebra::Blade;
66 ///
67 /// let s = Blade::scalar();
68 /// assert_eq!(s.index(), 0);
69 /// assert_eq!(s.grade(), 0);
70 /// ```
71 #[inline]
72 pub const fn scalar() -> Self {
73 Self { index: 0 }
74 }
75
76 /// Creates a basis vector blade.
77 ///
78 /// `Blade::basis(i)` creates blade `eᵢ₊₁` (using 0-based indexing internally).
79 ///
80 /// # Example
81 ///
82 /// ```
83 /// use clifford_codegen::algebra::Blade;
84 ///
85 /// let e1 = Blade::basis(0);
86 /// assert_eq!(e1.index(), 1);
87 ///
88 /// let e2 = Blade::basis(1);
89 /// assert_eq!(e2.index(), 2);
90 ///
91 /// let e3 = Blade::basis(2);
92 /// assert_eq!(e3.index(), 4);
93 /// ```
94 #[inline]
95 pub const fn basis(i: usize) -> Self {
96 Self { index: 1 << i }
97 }
98
99 /// Returns the blade's bitmask index.
100 ///
101 /// # Example
102 ///
103 /// ```
104 /// use clifford_codegen::algebra::Blade;
105 ///
106 /// let e12 = Blade::from_index(3);
107 /// assert_eq!(e12.index(), 3);
108 /// ```
109 #[inline]
110 pub const fn index(&self) -> usize {
111 self.index
112 }
113
114 /// Returns the grade (number of basis vectors in this blade).
115 ///
116 /// The grade equals the number of 1-bits in the index.
117 ///
118 /// # Example
119 ///
120 /// ```
121 /// use clifford_codegen::algebra::Blade;
122 ///
123 /// assert_eq!(Blade::scalar().grade(), 0);
124 /// assert_eq!(Blade::basis(0).grade(), 1);
125 /// assert_eq!(Blade::from_index(0b111).grade(), 3);
126 /// ```
127 #[inline]
128 pub const fn grade(&self) -> usize {
129 self.index.count_ones() as usize
130 }
131
132 /// Checks if this blade contains basis vector `i`.
133 ///
134 /// # Example
135 ///
136 /// ```
137 /// use clifford_codegen::algebra::Blade;
138 ///
139 /// let e12 = Blade::from_index(0b11);
140 /// assert!(e12.contains(0)); // contains e1
141 /// assert!(e12.contains(1)); // contains e2
142 /// assert!(!e12.contains(2)); // does not contain e3
143 /// ```
144 #[inline]
145 pub const fn contains(&self, i: usize) -> bool {
146 (self.index >> i) & 1 == 1
147 }
148
149 /// Returns an iterator over the basis vector indices in this blade.
150 ///
151 /// Indices are yielded in ascending order.
152 ///
153 /// # Example
154 ///
155 /// ```
156 /// use clifford_codegen::algebra::Blade;
157 ///
158 /// let e135 = Blade::from_index(0b10101);
159 /// let indices: Vec<_> = e135.basis_vectors().collect();
160 /// assert_eq!(indices, vec![0, 2, 4]);
161 /// ```
162 pub fn basis_vectors(&self) -> impl Iterator<Item = usize> + '_ {
163 (0..MAX_DIM).filter(move |&i| self.contains(i))
164 }
165
166 /// Returns the number of basis vectors in this blade.
167 ///
168 /// This is equivalent to `self.grade()`.
169 #[inline]
170 pub const fn len(&self) -> usize {
171 self.grade()
172 }
173
174 /// Returns true if this is the scalar blade.
175 #[inline]
176 pub const fn is_empty(&self) -> bool {
177 self.index == 0
178 }
179
180 /// Computes the XOR of two blade indices.
181 ///
182 /// This gives the result blade of the geometric product (before
183 /// considering sign).
184 ///
185 /// # Example
186 ///
187 /// ```
188 /// use clifford_codegen::algebra::Blade;
189 ///
190 /// let e1 = Blade::basis(0);
191 /// let e2 = Blade::basis(1);
192 /// let e12 = e1.xor(e2);
193 /// assert_eq!(e12.index(), 3);
194 /// ```
195 #[inline]
196 pub const fn xor(&self, other: Self) -> Self {
197 Self {
198 index: self.index ^ other.index,
199 }
200 }
201
202 /// Returns true if this blade shares any basis vectors with another.
203 ///
204 /// # Example
205 ///
206 /// ```
207 /// use clifford_codegen::algebra::Blade;
208 ///
209 /// let e12 = Blade::from_index(0b11);
210 /// let e23 = Blade::from_index(0b110);
211 /// let e45 = Blade::from_index(0b11000);
212 ///
213 /// assert!(e12.overlaps(e23)); // share e2
214 /// assert!(!e12.overlaps(e45)); // disjoint
215 /// ```
216 #[inline]
217 pub const fn overlaps(&self, other: Self) -> bool {
218 (self.index & other.index) != 0
219 }
220
221 /// Returns the blade name using standard notation.
222 ///
223 /// Uses 1-based indexing for display: `e1`, `e12`, `e123`, etc.
224 ///
225 /// # Example
226 ///
227 /// ```
228 /// use clifford_codegen::algebra::Blade;
229 ///
230 /// assert_eq!(Blade::scalar().name(), "1");
231 /// assert_eq!(Blade::basis(0).name(), "e1");
232 /// assert_eq!(Blade::from_index(0b11).name(), "e12");
233 /// ```
234 pub fn name(&self) -> String {
235 if self.index == 0 {
236 return "1".to_string();
237 }
238
239 let mut name = String::from("e");
240 for i in self.basis_vectors() {
241 name.push_str(&(i + 1).to_string());
242 }
243 name
244 }
245}
246
247impl fmt::Display for Blade {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 write!(f, "{}", self.name())
250 }
251}
252
253impl Default for Blade {
254 fn default() -> Self {
255 Self::scalar()
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn scalar_properties() {
265 let s = Blade::scalar();
266 assert_eq!(s.index(), 0);
267 assert_eq!(s.grade(), 0);
268 assert!(s.is_empty());
269 assert_eq!(s.name(), "1");
270 }
271
272 #[test]
273 fn basis_vector_properties() {
274 for i in 0..MAX_DIM {
275 let e = Blade::basis(i);
276 assert_eq!(e.index(), 1 << i);
277 assert_eq!(e.grade(), 1);
278 assert!(e.contains(i));
279 assert!(!e.is_empty());
280
281 for j in 0..MAX_DIM {
282 if j != i {
283 assert!(!e.contains(j));
284 }
285 }
286 }
287 }
288
289 #[test]
290 fn bivector_properties() {
291 let e12 = Blade::from_index(0b11);
292 assert_eq!(e12.grade(), 2);
293 assert!(e12.contains(0));
294 assert!(e12.contains(1));
295 assert!(!e12.contains(2));
296 assert_eq!(e12.name(), "e12");
297 }
298
299 #[test]
300 fn xor_computes_product_blade() {
301 let e1 = Blade::basis(0);
302 let e2 = Blade::basis(1);
303 let e12 = e1.xor(e2);
304 assert_eq!(e12.index(), 0b11);
305
306 // e12 * e2 = e1
307 let e1_back = e12.xor(e2);
308 assert_eq!(e1_back.index(), e1.index());
309 }
310
311 #[test]
312 fn basis_vectors_iterator() {
313 let e135 = Blade::from_index(0b10101);
314 let indices: Vec<_> = e135.basis_vectors().collect();
315 assert_eq!(indices, vec![0, 2, 4]);
316 }
317
318 #[test]
319 fn overlaps_detection() {
320 let e12 = Blade::from_index(0b11);
321 let e23 = Blade::from_index(0b110);
322 let e34 = Blade::from_index(0b1100);
323
324 assert!(e12.overlaps(e23)); // share e2
325 assert!(e23.overlaps(e34)); // share e3
326 assert!(!e12.overlaps(e34)); // disjoint
327 }
328
329 #[test]
330 fn ordering() {
331 let blades: Vec<Blade> = (0..8).map(Blade::from_index).collect();
332
333 // Verify they sort by index
334 let mut sorted = blades.clone();
335 sorted.sort();
336 assert_eq!(blades, sorted);
337 }
338}