math_utils/
traits.rs

1//! Abstract traits
2
3use either::Either;
4#[cfg(feature = "derive_serdes")]
5use serde;
6
7use crate::num_traits as num;
8use crate::types::{Affinity, LinearIso, Projectivity, Sign};
9
10/// Projective completion (homogeneous coordinates)
11pub trait ProjectiveSpace <S : Field> : AffineSpace <S> {
12  /// Affine subspace
13  type Patch : AffineSpace <S>;
14  /// Construct an augmented matrix for the affinity
15  #[expect(clippy::type_complexity)]
16  fn homography (affinity :
17    Affinity <S, Self::Patch, Self::Patch,
18      <<Self::Patch as AffineSpace <S>>::Translation as Module <S>>::LinearEndo>
19  ) -> Projectivity <S, Self, Self, <Self::Translation as Module <S>>::LinearEndo> where
20    <Self::Translation as Module <S>>::LinearEndo :
21      From <<<Self::Patch as AffineSpace <S>>::Translation as Module <S>>::LinearEndo>
22  {
23    Projectivity::new (LinearIso::new ((*affinity.linear_iso).into()).unwrap())
24  }
25  /// Return the projective completion (homogeneous coordinates) of the affine point or
26  /// vector
27  fn homogeneous (point_or_vector :
28    Either <Self::Patch, <Self::Patch as AffineSpace <S>>::Translation>
29  ) -> Self where Self : From <(Self::Patch, S)> {
30    match point_or_vector {
31      Either::Left  (point)  => (point, S::one()).into(),
32      Either::Right (vector) => (Self::Patch::from_vector (vector), S::zero()).into()
33    }
34  }
35}
36
37/// `AffineSpace` with translations in a (Euclidean) real inner product space
38pub trait EuclideanSpace <S : Real> : AffineSpace <S> + MetricSpace <S> { }
39
40/// Space of `Point`s (positions) and `Vector`s (displacements)
41pub trait AffineSpace <S : Field> : Point <Self::Translation> {
42  type Translation : VectorSpace <S> + GroupAction <Self>;
43}
44
45/// Point types convertible to and from a vector type, with difference function that
46/// follows from the free and transitive group action
47pub trait Point <V> : Sized + std::ops::Sub <Self, Output=V> where
48  V : AdditiveGroup + GroupAction <Self>
49{
50  fn to_vector (self) -> V;
51  fn from_vector (vector : V) -> Self;
52  fn origin() -> Self {
53    Self::from_vector (V::zero())
54  }
55}
56
57/// (Right) action of a group on a set
58pub trait GroupAction <X> : Group {
59  fn action (self, x : X) -> X;
60}
61
62impl <G> GroupAction <G> for G where G : Group {
63  #[expect(clippy::renamed_function_params)]
64  fn action (self, g : Self) -> Self {
65    Self::operation (g, self)
66  }
67}
68
69/// Bilinear form on a `VectorSpace`
70pub trait InnerProductSpace <S : Field> : VectorSpace <S> + Dot <S> {
71  fn inner_product (self, other : Self) -> S {
72    self.dot (other)
73  }
74  fn outer_product (self, other : Self) -> Self::LinearEndo;
75}
76
77impl <V, S> NormedVectorSpace <S> for V where
78  V : InnerProductSpace <S>,
79  S : Field
80{
81  fn norm_squared (self) -> S {
82    self.self_dot()
83  }
84}
85
86/// Scalar product (bilinear form) on a `Module`
87pub trait Dot <S : Ring> : Module <S> {
88  fn dot (self, other : Self) -> S;
89  /// Self dot product
90  fn self_dot (self) -> S {
91    self.dot (self)
92  }
93}
94
95/// `VectorSpace` with vector length/magnitude function
96pub trait NormedVectorSpace <S : Field> : InnerProductSpace <S> {
97  fn norm_squared (self) -> S;
98  fn norm (self) -> S where S : Sqrt {
99    self.norm_squared().sqrt()
100  }
101  #[must_use]
102  fn normalize (self) -> Self where S : Sqrt {
103    self / self.norm()
104  }
105  fn unit_sigvec (self) -> Self where S : SignedExt + Sqrt {
106    let v = self.sigvec();
107    if v.is_zero() {
108      v
109    } else {
110      v.normalize()
111    }
112  }
113}
114
115/// Set of points with distance function
116pub trait MetricSpace <S : Field> : NormedVectorSpace <S> {
117  fn distance_squared (self, other : Self) -> S {
118    (self - other).norm_squared()
119  }
120  fn distance (self, other : Self) -> S where S : Sqrt {
121    self.distance_squared (other).sqrt()
122  }
123}
124
125/// Module with scalars taken from a `Field`
126pub trait VectorSpace <S : Field> : Module <S> + std::ops::Div <S, Output=Self> + Copy {
127  // required
128  fn map <F> (self, f : F) -> Self where F : FnMut (S) -> S;
129  // provided
130  /// Map `signum_or_zero` over each element of the given vector
131  fn sigvec (self) -> Self where S : SignedExt {
132    self.map (SignedExt::signum_or_zero)
133  }
134}
135
136/// Additive group combined with scalar multiplication
137pub trait Module <S : Ring> : AdditiveGroup + std::ops::Mul <S, Output=Self> + Copy {
138  /// Linear endomorphism represented by a square matrix type
139  type LinearEndo : LinearMap <S, Self, Self> + Ring;
140}
141
142/// Module homomorphism
143pub trait LinearMap <S, V, W> : std::ops::Mul <V, Output=W> + Copy where
144  S : Ring,
145  V : Module <S>,
146  W : Module <S>
147{
148  fn determinant (self) -> S;
149  fn transpose   (self) -> Self;
150}
151
152/// Additional matrix methods
153pub trait Matrix <S> : Copy {
154  type Rows;
155  type Submatrix;
156  /// Returns the row-major matrix
157  fn rows (self) -> Self::Rows;
158  /// Returns the submatrix formed by removing the given (col,row)
159  fn submatrix (self, i : usize, j : usize) -> Self::Submatrix;
160  /// Returns the matrix formed by filling additional row and column with zeros
161  fn fill_zeros (submatrix : Self::Submatrix) -> Self where S : num::Zero;
162  /// Computes the determinant of the (i,j)-submatrix
163  #[inline]
164  fn minor <V, W> (self, i : usize, j : usize) -> S where
165    S : Ring,
166    V : Module <S>,
167    W : Module <S>,
168    Self::Submatrix : LinearMap <S, V, W>
169  {
170    self.submatrix (i, j).determinant()
171  }
172}
173
174/// Householder transformation
175pub trait ElementaryReflector {
176  type Vector;
177  /// Construct a Householder transformation
178  fn elementary_reflector (v : Self::Vector, index : usize) -> Self;
179}
180
181/// `OrderedField` with special functions
182pub trait Real : OrderedField + Exp + Powf + Sqrt + Trig {
183  // TODO: more constants
184  fn pi() -> Self;
185  fn frac_pi_3() -> Self;
186  fn sqrt_3() -> Self;
187  fn frac_1_sqrt_3() -> Self;
188  fn floor (self) -> Self;
189  fn ceil (self) -> Self;
190  fn trunc (self) -> Self;
191  fn fract (self) -> Self;
192}
193
194/// A `Field` with an ordering
195pub trait OrderedField : Field + OrderedRing { }
196
197/// A (commutative) `Ring` where $1 \neq 0$ and all non-zero elements are invertible
198pub trait Field : Ring + MultiplicativeGroup + Powi {
199  fn half() -> Self {
200    Self::one() / Self::two()
201  }
202  fn two() -> Self {
203    Self::one() + Self::one()
204  }
205  fn three() -> Self {
206    Self::one() + Self::one() + Self::one()
207  }
208  fn four() -> Self {
209    Self::two() * Self::two()
210  }
211  fn five() -> Self {
212    Self::two() + Self::three()
213  }
214  fn six() -> Self {
215    Self::two() * Self::three()
216  }
217  fn seven() -> Self {
218    Self::three() + Self::four()
219  }
220  fn eight() -> Self {
221    Self::two() * Self::two() * Self::two()
222  }
223  fn nine() -> Self {
224    Self::three() * Self::three()
225  }
226  fn ten() -> Self {
227    Self::two() * Self::five()
228  }
229}
230
231/// Interface for a group with identity represented by `one`, operation defined by `*`
232/// and `/`
233pub trait MultiplicativeGroup : MultiplicativeMonoid +
234  std::ops::Div <Self, Output=Self> + std::ops::DivAssign + num::Inv <Output=Self>
235{ }
236
237/// Ring of integers
238pub trait Integer : OrderedRing + SignedExt + num::PrimInt { }
239
240/// A Ring with an ordering
241pub trait OrderedRing : Ring + SignedExt + MinMax + PartialOrd + std::ops::Rem { }
242
243/// Interface for a ring as a combination of an additive group and a distributive
244/// multiplication operation
245pub trait Ring : AdditiveGroup + MultiplicativeMonoid
246  + num::MulAdd <Self, Self, Output=Self> + num::MulAddAssign <Self, Self>
247{ }
248
249/// Interface for a group with identity represented by `zero`, and operation defined by
250/// `+` and `-`
251pub trait AdditiveGroup : AdditiveMonoid +
252  std::ops::Sub <Self, Output=Self> + std::ops::SubAssign + std::ops::Neg <Output=Self>
253{ }
254
255/// Set with identity, inverses, and (associative) binary operation
256pub trait Group : PartialEq + std::ops::Neg <Output=Self> {
257  fn identity() -> Self;
258  fn operation (a : Self, b : Self) -> Self;
259}
260
261/// Set with identity represented by `one` and (associative) binary operation defined by
262/// `*`
263pub trait MultiplicativeMonoid : Copy + PartialEq +
264  std::ops::Mul <Self, Output=Self> + std::ops::MulAssign + num::One
265{
266  fn squared (self) -> Self {
267    self * self
268  }
269  fn cubed (self) -> Self {
270    self * self * self
271  }
272}
273
274/// Set with identity represented by `zero` and (associative) binary operation defined
275/// by `+`
276pub trait AdditiveMonoid : Sized + PartialEq + std::iter::Sum
277  + std::ops::Add <Self, Output=Self> + std::ops::AddAssign + num::Zero
278{ }
279
280/// Interface for angle units
281pub trait Angle <S : Real> : Clone + Copy + PartialEq + PartialOrd + Sized +
282  AdditiveGroup + std::ops::Div <Self, Output=S> + std::ops::Mul <S, Output=Self> +
283  std::ops::Div <S, Output=Self> + std::ops::Rem <Self, Output=Self>
284{
285  // required
286  /// Full rotation
287  fn full_turn() -> Self;
288  // provided
289  /// Half rotation
290  fn half_turn() -> Self {
291    Self::full_turn() / S::two()
292  }
293  /// Restrict to `(-half_turn, half_turn]`
294  fn wrap_signed (self) -> Self {
295    if self > Self::half_turn() || self <= -Self::half_turn() {
296      let out = (self + Self::half_turn()).wrap_unsigned() - Self::half_turn();
297      if out == -Self::half_turn() {
298        Self::half_turn()
299      } else {
300        out
301      }
302    } else {
303      self
304    }
305  }
306  /// Restrict to `[0, full_turn)`
307  fn wrap_unsigned (self) -> Self {
308    if self >= Self::full_turn() {
309      self % Self::full_turn()
310    } else if self < Self::zero() {
311      self + Self::full_turn() * ((self / Self::full_turn()).trunc().abs() + S::one())
312    } else {
313      self
314    }
315  }
316}
317
318/// Unsigned integer power function
319pub trait Pow {
320  fn pow (self, exp : u32) -> Self;
321}
322/// Signed integer power function
323pub trait Powi {
324  fn powi (self, n : i32) -> Self;
325}
326/// Fractional power function
327pub trait Powf {
328  fn powf (self, n : Self) -> Self;
329}
330/// Exponential function
331pub trait Exp {
332  fn exp (self) -> Self;
333}
334/// Square root function
335pub trait Sqrt {
336  fn sqrt (self) -> Self;
337}
338/// Cube root function
339pub trait Cbrt {
340  fn cbrt (self) -> Self;
341}
342/// Trigonometric functions
343pub trait Trig : Sized {
344  fn sin      (self) -> Self;
345  fn sin_cos  (self) -> (Self, Self);
346  fn cos      (self) -> Self;
347  fn tan      (self) -> Self;
348  fn asin     (self) -> Self;
349  fn acos     (self) -> Self;
350  fn atan     (self) -> Self;
351  fn atan2    (self, other : Self) -> Self;
352}
353
354/// Provides `min`, `max`, and `clamp` that are not necessarily consistent with those
355/// from `Ord`. This is provided because `f32` and `f64` do not implement `Ord`, so this
356/// trait is defined to give a uniform interface with `Ord` types.
357pub trait MinMax {
358  fn min   (self, other : Self) -> Self;
359  fn max   (self, other : Self) -> Self;
360  fn clamp (self, min : Self, max : Self) -> Self;
361}
362
363/// Function returning number representing sign of self
364pub trait SignedExt : num::Signed {
365  #[inline]
366  fn sign (self) -> Sign {
367    if self.is_zero() {
368      Sign::Zero
369    } else if self.is_positive() {
370      Sign::Positive
371    } else {
372      debug_assert!(self.is_negative());
373      Sign::Negative
374    }
375  }
376  /// Maps `0.0` to `0.0`, otherwise equal to `S::signum` (which would otherwise map
377  /// `+0.0 -> 1.0` and `-0.0 -> -1.0`)
378  #[inline]
379  fn signum_or_zero (self) -> Self where Self : num::Zero {
380    if self.is_zero() {
381      Self::zero()
382    } else {
383      self.signum()
384    }
385  }
386}
387
388/// Adds serde `Serialize` and `DeserializeOwned` constraints.
389///
390/// This makes it easier to conditionally add these constraints to type definitions when
391/// `derive_serdes` feature is enabled.
392#[cfg(not(feature = "derive_serdes"))]
393pub trait MaybeSerDes { }
394#[cfg(feature = "derive_serdes")]
395pub trait MaybeSerDes : serde::Serialize + serde::de::DeserializeOwned { }
396
397impl <S, T> MetricSpace <S> for T where S : Field, T : NormedVectorSpace <S> { }
398impl <T> OrderedField for T where T : Field + OrderedRing { }
399impl <T> Field        for T where T : Ring + MultiplicativeGroup + Powi + Powf { }
400impl <T> Integer      for T where T : OrderedRing + num::PrimInt { }
401impl <T> OrderedRing  for T where
402  T : Ring + SignedExt + MinMax + PartialOrd + std::ops::Rem
403{ }
404impl <T> Ring         for T where
405  T : AdditiveGroup + MultiplicativeMonoid
406    + num::MulAdd<Self, Self, Output=Self> + num::MulAddAssign <Self, Self>
407{ }
408impl <T> AdditiveGroup for T where
409  T : AdditiveMonoid + std::ops::Sub <Self, Output=Self> + std::ops::SubAssign
410    + std::ops::Neg <Output=Self>
411{ }
412impl <T> MultiplicativeGroup for T where
413  T : MultiplicativeMonoid + std::ops::Div <Self, Output=Self> + std::ops::DivAssign
414    + num::Inv <Output=Self>
415{ }
416impl <T> AdditiveMonoid for T where
417  T : Sized + PartialEq + std::iter::Sum + std::ops::Add <Self, Output=Self>
418    + std::ops::AddAssign + num::Zero
419{ }
420impl <T> MultiplicativeMonoid for T where
421  T : Copy + PartialEq + std::ops::Mul <Self, Output=Self> + std::ops::MulAssign
422    + num::One
423{ }
424impl <T : num::Signed> SignedExt for T { }
425
426impl <
427  #[cfg(not(feature = "derive_serdes"))]
428  T,
429  #[cfg(feature = "derive_serdes")]
430  T : serde::Serialize + serde::de::DeserializeOwned
431> MaybeSerDes for T { }
432
433macro impl_integer ($type:ty) {
434  impl MinMax         for $type {
435    fn min (self, other : Self) -> Self {
436      Ord::min (self, other)
437    }
438    fn max (self, other : Self) -> Self {
439      Ord::max (self, other)
440    }
441    fn clamp (self, min : Self, max : Self) -> Self {
442      Ord::clamp (self, min, max)
443    }
444  }
445  impl Pow for $type {
446    fn pow (self, exp : u32) -> Self {
447      self.pow (exp)
448    }
449  }
450}
451impl_integer!(i8);
452impl_integer!(i16);
453impl_integer!(i32);
454impl_integer!(i64);
455
456macro impl_real_float ($type:ident) {
457  impl VectorSpace <Self> for $type {
458    fn map <F> (self, mut f : F) -> Self where F : FnMut (Self) -> Self {
459      f (self)
460    }
461  }
462  impl Module <Self> for $type {
463    type LinearEndo = Self;
464  }
465  impl LinearMap <Self, Self, Self> for $type {
466    fn determinant (self) -> Self {
467      self
468    }
469    fn transpose (self) -> Self {
470      self
471    }
472  }
473  impl Real for $type {
474    fn pi() -> Self {
475      std::$type::consts::PI
476    }
477    fn frac_pi_3() -> Self {
478      std::$type::consts::FRAC_PI_3
479    }
480    #[expect(clippy::excessive_precision)]
481    #[allow(clippy::allow_attributes)]
482    #[allow(clippy::cast_possible_truncation)]
483    #[allow(trivial_numeric_casts)]
484    fn sqrt_3() -> Self {
485      1.732050807568877293527446341505872366942805253810380628055f64 as $type
486    }
487    #[expect(clippy::excessive_precision)]
488    #[allow(clippy::allow_attributes)]
489    #[allow(clippy::cast_possible_truncation)]
490    #[allow(trivial_numeric_casts)]
491    fn frac_1_sqrt_3() -> Self {
492      (1.0f64 / 1.732050807568877293527446341505872366942805253810380628055f64) as $type
493    }
494    fn floor (self) -> Self {
495      self.floor()
496    }
497    fn ceil (self) -> Self {
498      self.ceil()
499    }
500    fn trunc (self) -> Self {
501      self.trunc()
502    }
503    fn fract (self) -> Self {
504      self.fract()
505    }
506  }
507  impl MinMax               for $type {
508    fn min (self, other : Self) -> Self {
509      self.min (other)
510    }
511    fn max (self, other : Self) -> Self {
512      self.max (other)
513    }
514    fn clamp (self, min : Self, max : Self) -> Self {
515      self.clamp (min, max)
516    }
517  }
518  impl Powi for $type {
519    fn powi (self, n : i32) -> Self {
520      self.powi (n)
521    }
522  }
523  impl Powf for $type {
524    fn powf (self, n : Self) -> Self {
525      self.powf (n)
526    }
527  }
528  impl Exp for $type {
529    fn exp (self) -> Self {
530      self.exp()
531    }
532  }
533  impl Sqrt for $type {
534    fn sqrt (self) -> Self {
535      self.sqrt()
536    }
537  }
538  impl Cbrt for $type {
539    fn cbrt (self) -> Self {
540      self.cbrt()
541    }
542  }
543  impl Trig for $type {
544    fn sin (self) -> Self {
545      self.sin()
546    }
547    fn sin_cos (self) -> (Self, Self) {
548      self.sin_cos()
549    }
550    fn cos (self) -> Self {
551      self.cos()
552    }
553    fn tan (self) -> Self {
554      self.tan()
555    }
556    fn asin (self) -> Self {
557      self.asin()
558    }
559    fn acos (self) -> Self {
560      self.acos()
561    }
562    fn atan (self) -> Self {
563      self.atan()
564    }
565    fn atan2 (self, other : Self) -> Self {
566      self.atan2 (other)
567    }
568  }
569}
570
571impl_real_float!(f32);
572impl_real_float!(f64);