atlas_embeddings/arithmetic/
mod.rs1mod matrix;
22
23pub use matrix::{RationalMatrix, RationalVector};
24
25use num_rational::Ratio;
26use num_traits::Zero;
27use std::fmt;
28use std::ops::{Add, Mul, Neg, Sub};
29
30pub type Rational = Ratio<i64>;
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
69pub struct HalfInteger {
70 numerator: i64,
71}
72
73impl HalfInteger {
74 #[must_use]
86 pub const fn new(numerator: i64) -> Self {
87 Self { numerator }
88 }
89
90 #[must_use]
101 pub const fn from_integer(n: i64) -> Self {
102 Self { numerator: n * 2 }
103 }
104
105 #[must_use]
107 pub const fn numerator(self) -> i64 {
108 self.numerator
109 }
110
111 #[must_use]
113 pub fn to_rational(self) -> Rational {
114 Rational::new(self.numerator, 2)
115 }
116
117 #[must_use]
123 pub fn from_rational(r: Rational) -> Self {
124 let numer = *r.numer();
126 let denom = *r.denom();
127
128 match denom {
130 1 => Self::from_integer(numer),
131 2 => Self::new(numer),
132 _ => panic!("Rational {numer}/{denom} cannot be represented as half-integer"),
133 }
134 }
135
136 #[must_use]
138 pub fn square(self) -> Rational {
139 Rational::new(self.numerator * self.numerator, 4)
140 }
141
142 #[must_use]
144 pub const fn is_integer(self) -> bool {
145 self.numerator % 2 == 0
146 }
147
148 #[must_use]
150 pub const fn as_integer(self) -> Option<i64> {
151 if self.is_integer() {
152 Some(self.numerator / 2)
153 } else {
154 None
155 }
156 }
157}
158
159impl Zero for HalfInteger {
160 fn zero() -> Self {
161 Self::new(0)
162 }
163
164 fn is_zero(&self) -> bool {
165 self.numerator == 0
166 }
167}
168
169impl Add for HalfInteger {
174 type Output = Self;
175
176 fn add(self, other: Self) -> Self {
177 Self::new(self.numerator + other.numerator)
178 }
179}
180
181impl Sub for HalfInteger {
182 type Output = Self;
183
184 fn sub(self, other: Self) -> Self {
185 Self::new(self.numerator - other.numerator)
186 }
187}
188
189impl Mul for HalfInteger {
190 type Output = Rational;
191
192 fn mul(self, other: Self) -> Rational {
193 Rational::new(self.numerator * other.numerator, 4)
194 }
195}
196
197impl Neg for HalfInteger {
198 type Output = Self;
199
200 fn neg(self) -> Self {
201 Self::new(-self.numerator)
202 }
203}
204
205impl fmt::Display for HalfInteger {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 if self.is_integer() {
208 write!(f, "{}", self.numerator / 2)
209 } else {
210 write!(f, "{}/2", self.numerator)
211 }
212 }
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
240pub struct Vector8 {
241 coords: [HalfInteger; 8],
242}
243
244impl Vector8 {
245 #[must_use]
247 pub const fn new(coords: [HalfInteger; 8]) -> Self {
248 Self { coords }
249 }
250
251 #[must_use]
253 pub fn zero() -> Self {
254 Self::new([HalfInteger::zero(); 8])
255 }
256
257 #[must_use]
259 pub const fn get(&self, index: usize) -> HalfInteger {
260 self.coords[index]
261 }
262
263 #[must_use]
265 pub const fn coords(&self) -> &[HalfInteger; 8] {
266 &self.coords
267 }
268
269 #[must_use]
273 pub fn inner_product(&self, other: &Self) -> Rational {
274 let mut sum = Rational::zero();
275 for i in 0..8 {
276 sum += self.coords[i] * other.coords[i];
277 }
278 sum
279 }
280
281 #[must_use]
283 pub fn norm_squared(&self) -> Rational {
284 self.inner_product(self)
285 }
286
287 #[must_use]
289 pub fn is_root(&self) -> bool {
290 self.norm_squared() == Rational::new(2, 1)
291 }
292
293 #[must_use]
295 pub fn add(&self, other: &Self) -> Self {
296 let result: [HalfInteger; 8] = std::array::from_fn(|i| self.coords[i] + other.coords[i]);
297 Self::new(result)
298 }
299
300 #[must_use]
302 pub fn sub(&self, other: &Self) -> Self {
303 let result: [HalfInteger; 8] = std::array::from_fn(|i| self.coords[i] - other.coords[i]);
304 Self::new(result)
305 }
306
307 #[must_use]
309 pub fn scale(&self, scalar: i64) -> Self {
310 let result: [HalfInteger; 8] =
311 std::array::from_fn(|i| HalfInteger::new(self.coords[i].numerator() * scalar));
312 Self::new(result)
313 }
314
315 #[must_use]
319 pub fn scale_rational(&self, scalar: Rational) -> Self {
320 let result: [HalfInteger; 8] = std::array::from_fn(|i| {
321 let coord_rational = self.coords[i].to_rational();
322 let product = coord_rational * scalar;
323 HalfInteger::from_rational(product)
324 });
325 Self::new(result)
326 }
327
328 #[must_use]
330 pub fn negate(&self) -> Self {
331 let result: [HalfInteger; 8] = std::array::from_fn(|i| -self.coords[i]);
332 Self::new(result)
333 }
334}
335
336impl Add for Vector8 {
337 type Output = Self;
338
339 fn add(self, other: Self) -> Self {
340 Self::add(&self, &other)
341 }
342}
343
344impl Sub for Vector8 {
345 type Output = Self;
346
347 fn sub(self, other: Self) -> Self {
348 Self::sub(&self, &other)
349 }
350}
351
352impl Neg for Vector8 {
353 type Output = Self;
354
355 fn neg(self) -> Self {
356 self.negate()
357 }
358}
359
360impl fmt::Display for Vector8 {
361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362 write!(f, "(")?;
363 for (i, coord) in self.coords.iter().enumerate() {
364 if i > 0 {
365 write!(f, ", ")?;
366 }
367 write!(f, "{coord}")?;
368 }
369 write!(f, ")")
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[test]
378 fn test_half_integer_creation() {
379 let half = HalfInteger::new(1);
380 assert_eq!(half.numerator(), 1);
381 assert_eq!(half.to_rational(), Rational::new(1, 2));
382
383 let one = HalfInteger::from_integer(1);
384 assert_eq!(one.numerator(), 2);
385 assert!(one.is_integer());
386 }
387
388 #[test]
389 fn test_half_integer_arithmetic() {
390 let a = HalfInteger::new(1); let b = HalfInteger::new(3); let sum = a + b; assert_eq!(sum.numerator(), 4);
395 assert!(sum.is_integer());
396
397 let diff = b - a; assert_eq!(diff.numerator(), 2);
399
400 let prod = a * b; assert_eq!(prod, Rational::new(3, 4));
402 }
403
404 #[test]
405 fn test_half_integer_square() {
406 let half = HalfInteger::new(1); assert_eq!(half.square(), Rational::new(1, 4));
408
409 let one = HalfInteger::from_integer(1); assert_eq!(one.square(), Rational::new(1, 1));
411 }
412
413 #[test]
414 fn test_vector8_creation() {
415 let v = Vector8::zero();
416 assert!(v.norm_squared().is_zero());
417
418 let ones = Vector8::new([HalfInteger::from_integer(1); 8]);
419 assert_eq!(ones.norm_squared(), Rational::new(8, 1));
420 }
421
422 #[test]
423 fn test_vector8_inner_product() {
424 let v = Vector8::new([HalfInteger::new(1); 8]); let w = Vector8::new([HalfInteger::new(2); 8]); assert_eq!(v.inner_product(&w), Rational::new(4, 1));
429 }
430
431 #[test]
432 fn test_vector8_norm_squared() {
433 let v = Vector8::new([HalfInteger::new(1); 8]); assert_eq!(v.norm_squared(), Rational::new(2, 1));
437 assert!(v.is_root());
438 }
439
440 #[test]
441 fn test_vector8_arithmetic() {
442 let v = Vector8::new([HalfInteger::new(1); 8]);
443 let w = Vector8::new([HalfInteger::new(1); 8]);
444
445 let sum = v + w;
446 assert_eq!(sum.coords[0].numerator(), 2);
447
448 let diff = v - w;
449 assert!(diff.norm_squared().is_zero());
450
451 let neg = -v;
452 assert_eq!(neg.coords[0].numerator(), -1);
453 }
454
455 #[test]
456 fn test_vector8_scaling() {
457 let v = Vector8::new([HalfInteger::from_integer(1); 8]);
458 let scaled = v.scale(3);
459
460 assert_eq!(scaled.coords[0].numerator(), 6);
461 assert_eq!(scaled.norm_squared(), Rational::new(72, 1));
462 }
463
464 #[test]
465 fn test_rational_exact_arithmetic() {
466 let a = Rational::new(1, 3);
467 let b = Rational::new(1, 6);
468
469 let sum = a + b;
470 assert_eq!(sum, Rational::new(1, 2));
471
472 let prod = a * b;
473 assert_eq!(prod, Rational::new(1, 18));
474 }
475}