Skip to main content

math_utils/types/
mod.rs

1use std::marker::PhantomData;
2use derive_more::{Deref, Display, From, Neg};
3
4use crate::{approx, num};
5
6#[cfg(feature = "derive_serdes")]
7use serde::{Deserialize, Serialize};
8
9use crate::traits::*;
10
11pub mod coordinate;
12pub mod frame;
13
14pub use vek::Vec2 as Vector2;
15pub use vek::Vec3 as Vector3;
16pub use vek::Vec4 as Vector4;
17pub use vek::Mat2 as Matrix2;
18pub use vek::Mat3 as Matrix3;
19pub use vek::Mat4 as Matrix4;
20pub use vek::Quaternion as Quaternion;
21
22pub use self::enums::{Axis2, Axis3, Axis4, Octant, Quadrant, Sign, SignedAxis1,
23  SignedAxis2, SignedAxis3, SignedAxis4};
24
25////////////////////////////////////////////////////////////////////////////////////////
26//  enums                                                                             //
27////////////////////////////////////////////////////////////////////////////////////////
28
29// we define enums in a separate module so that the generated iterator types don't
30// pollute the namespace
31mod enums {
32  use strum::{EnumCount, EnumIter, FromRepr};
33  #[cfg(feature = "derive_serdes")]
34  use serde::{Deserialize, Serialize};
35
36  /// Negative, zero, or positive
37  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
38  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
39  #[repr(u8)]
40  pub enum Sign {
41    Negative,
42    Zero,
43    Positive
44  }
45
46  /// 2D quadrant
47  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
48  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
49  #[repr(u8)]
50  pub enum Quadrant {
51    /// +X, +Y
52    PosPos,
53    /// -X, +Y
54    NegPos,
55    /// +X, -Y
56    PosNeg,
57    /// -X, -Y
58    NegNeg
59  }
60
61  /// 3D octant
62  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
63  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
64  #[repr(u8)]
65  pub enum Octant {
66    /// +X, +Y, +Z
67    PosPosPos,
68    /// -X, +Y, +Z
69    NegPosPos,
70    /// +X, -Y, +Z
71    PosNegPos,
72    /// -X, -Y, +Z
73    NegNegPos,
74    /// +X, +Y, -Z
75    PosPosNeg,
76    /// -X, +Y, -Z
77    NegPosNeg,
78    /// +X, -Y, -Z
79    PosNegNeg,
80    /// -X, -Y, -Z
81    NegNegNeg
82  }
83
84  /// X or Y axis
85  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
86  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
87  #[repr(u8)]
88  pub enum Axis2 {
89    X=0,
90    Y
91  }
92
93  /// X, Y, or Z axis
94  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
95  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
96  #[repr(u8)]
97  pub enum Axis3 {
98    X=0,
99    Y,
100    Z
101  }
102
103  /// X, Y, Z, or W axis
104  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
105  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
106  #[repr(u8)]
107  pub enum Axis4 {
108    X=0,
109    Y,
110    Z,
111    W
112  }
113
114  /// Positive or negative X axis
115  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
116  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
117  #[repr(u8)]
118  pub enum SignedAxis1 {
119    NegX, PosX
120  }
121
122  /// Positive or negative X or Y axis
123  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
124  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
125  #[repr(u8)]
126  pub enum SignedAxis2 {
127    NegX, PosX,
128    NegY, PosY
129  }
130
131  /// Positive or negative X, Y, or Z axis
132  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
133  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
134  #[repr(u8)]
135  pub enum SignedAxis3 {
136    NegX, PosX,
137    NegY, PosY,
138    NegZ, PosZ
139  }
140
141  /// Positive or negative X, Y, Z, or W axis
142  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
143  #[derive(Clone, Copy, Debug, Eq, PartialEq, EnumCount, EnumIter, FromRepr)]
144  #[repr(u8)]
145  pub enum SignedAxis4 {
146    NegX, PosX,
147    NegY, PosY,
148    NegZ, PosZ,
149    NegW, PosW
150  }
151}
152
153////////////////////////////////////////////////////////////////////////////////////////
154//  structs                                                                           //
155////////////////////////////////////////////////////////////////////////////////////////
156
157/// Strictly positive scalars (non-zero)
158#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
159#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Display)]
160pub struct Positive <S> (S);
161
162/// Non-negative scalars (may be zero)
163#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
164#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Display)]
165pub struct NonNegative <S> (S);
166
167/// Non-zero scalars
168#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
169#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Display)]
170pub struct NonZero <S> (S);
171
172/// Scalars in the closed unit interval [0, 1]
173#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
174#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Display)]
175pub struct Normalized <S> (S);
176
177/// Scalars in the closed interval [-1, 1]
178#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
179#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Display)]
180pub struct NormalSigned <S> (S);
181
182/// Scalars in the discrete set {-1, 1}
183#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
184#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Display)]
185pub struct Unit <S> (S);
186
187/// (Euler angles) representation of a 3D orientation. Internally the angles are wrapped
188/// to $[0, 2\pi)$.
189///
190/// Throughout this library this is usually interpreted as intrinsic ZX'Y'' Euler
191/// angles.
192#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
193#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
194pub struct Angles3 <S> {
195  pub yaw   : AngleWrapped <S>,
196  pub pitch : AngleWrapped <S>,
197  pub roll  : AngleWrapped <S>
198}
199
200/// Representation of a 3D position + orientation
201#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
202#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
203pub struct Pose3 <S> {
204  pub position : Point3  <S>,
205  pub angles   : Angles3 <S>
206}
207
208/// Invertible linear map
209#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Display)]
210#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
211#[display("{}", _0)]
212pub struct LinearIso <S, V, W, M> (M, PhantomData <(S, V, W)>);
213
214/// Invertible linear endomorphism
215#[derive(Clone, Copy, Debug, Default, PartialEq, Deref, Display, From)]
216#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
217#[display("{}", _0)]
218pub struct LinearAuto <S, V> (pub LinearIso <S, V, V, V::LinearEndo>) where
219  V : Module <S>,
220  V::LinearEndo : MaybeSerDes,
221  S : Ring;
222
223/// Orthogonal 2x2 matrix with determinant +1, i.e. a member of the circle group $SO(2)$
224/// of special orthogonal matrices
225#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
226#[cfg_attr(feature = "derive_serdes", serde(bound = "S : MaybeSerDes"))]
227#[derive(Clone, Copy, Debug, Default, PartialEq)]
228pub struct Rotation2 <S> (LinearAuto <S, Vector2 <S>>) where S : Ring + MaybeSerDes;
229
230/// Orthogonal 3x3 matrix with determinant +1, i.e. a member of the 3D rotation group
231/// $SO(3)$ of special orthogonal matrices
232#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
233#[cfg_attr(feature = "derive_serdes", serde(bound = "S : MaybeSerDes"))]
234#[derive(Clone, Copy, Debug, Default, PartialEq)]
235pub struct Rotation3 <S> (LinearAuto <S, Vector3 <S>>) where S : Ring + MaybeSerDes;
236
237/// Unit quaternion representing an orientation in $\mathbb{R}^3$
238#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
239#[derive(Clone, Copy, Debug, Eq, PartialEq)]
240pub struct Versor <S> (Quaternion <S>);
241
242/// Homomorphism of affine spaces: a combination of a linear map and a translation
243#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Display)]
244#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
245#[display("({}, {})", linear_map, translation)]
246pub struct AffineMap <S, A, B, M> where
247  A : AffineSpace <S>,
248  B : AffineSpace <S>,
249  S : Field,
250  M : LinearMap <S, A::Translation, B::Translation>
251{
252  pub linear_map  : M,
253  pub translation : B::Translation,
254  pub _phantom    : PhantomData <(A, S)>
255}
256
257/// Affine isomorphism
258#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Display)]
259#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
260#[display("({}, {})", linear_iso, translation)]
261pub struct Affinity <S, A, B, M> where
262  S : Field,
263  A : AffineSpace <S>,
264  B : AffineSpace <S>,
265  M : LinearMap <S, A::Translation, B::Translation>
266{
267  pub linear_iso  : LinearIso <S, A::Translation, B::Translation, M>,
268  pub translation : B::Translation
269}
270
271/// Isomorphism of projective spaces (a.k.a. homography or projective collineation)
272#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Display)]
273#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
274#[display("{}", _0)]
275pub struct Projectivity <S, P, Q, M> (
276  pub LinearIso <S, P::Translation, Q::Translation, M>
277) where
278  S : Field,
279  P : ProjectiveSpace <S>,
280  Q : ProjectiveSpace <S>;
281
282////////////////////////////////////////////////////////////////////////////////////////
283//  impls                                                                             //
284////////////////////////////////////////////////////////////////////////////////////////
285
286//
287//  impl Deg
288//
289impl <S : OrderedField> Angle <S> for Deg <S> {
290  fn full_turn() -> Self {
291    Deg (S::ten() * S::nine() * S::two() * S::two())
292  }
293  fn half_turn() -> Self {
294    Deg (S::ten() * S::nine() * S::two())
295  }
296}
297impl <S : Real> Trig for Deg <S> {
298  fn sin     (self) -> Self {
299    Rad::from (self).sin().into()
300  }
301  fn sin_cos (self) -> (Self, Self) {
302    let (sin, cos) = Rad::from (self).sin_cos();
303    (sin.into(), cos.into())
304  }
305  fn cos     (self) -> Self {
306    Rad::from (self).cos().into()
307  }
308  fn tan     (self) -> Self {
309    Rad::from (self).tan().into()
310  }
311  fn asin    (self) -> Self {
312    Rad::from (self).asin().into()
313  }
314  fn acos    (self) -> Self {
315    Rad::from (self).acos().into()
316  }
317  fn atan    (self) -> Self {
318    Rad::from (self).atan().into()
319  }
320  fn atan2   (self, other : Self) -> Self {
321    Rad::from (self).atan2 (Rad::from (other)).into()
322  }
323}
324impl <S : Real> From <Rad <S>> for Deg <S> {
325  fn from (radians : Rad <S>) -> Self {
326    let full_turns = radians / Rad::full_turn();
327    Deg::full_turn() * full_turns
328  }
329}
330impl <S : OrderedField> From <Turn <S>> for Deg <S> {
331  fn from (turns : Turn <S>) -> Self {
332    Deg (turns.0 * Deg::full_turn().0)
333  }
334}
335
336//
337//  impl Rad
338//
339impl <S : Real> Angle <S> for Rad <S> {
340  fn full_turn() -> Self {
341    Rad (S::pi() * S::two())
342  }
343}
344impl <S : Trig> Trig for Rad <S> {
345  fn sin     (self) -> Self {
346    Rad (self.0.sin())
347  }
348  fn sin_cos (self) -> (Self, Self) {
349    let (sin, cos) = self.0.sin_cos();
350    (Rad (sin), Rad (cos))
351  }
352  fn cos     (self) -> Self {
353    Rad (self.0.cos())
354  }
355  fn tan     (self) -> Self {
356    Rad (self.0.tan())
357  }
358  fn asin    (self) -> Self {
359    Rad (self.0.asin())
360  }
361  fn acos    (self) -> Self {
362    Rad (self.0.acos())
363  }
364  fn atan    (self) -> Self {
365    Rad (self.0.atan())
366  }
367  fn atan2   (self, other : Self) -> Self {
368    Rad (self.0.atan2 (other.0))
369  }
370}
371impl <S : Real> From <Deg <S>> for Rad <S> {
372  fn from (degrees : Deg <S>) -> Self {
373    let full_turns = degrees / Deg::full_turn();
374    Rad::full_turn() * full_turns
375  }
376}
377impl <S : Real> From <Turn <S>> for Rad <S> {
378  fn from (turns : Turn <S>) -> Self {
379    Rad (turns.0 * Rad::full_turn().0)
380  }
381}
382
383//
384//  impl Turn
385//
386impl <S : OrderedField> Angle <S> for Turn <S> {
387  fn full_turn() -> Self {
388    Turn (S::one())
389  }
390  fn half_turn() -> Self {
391    Turn (S::half())
392  }
393}
394impl <S : Real> Trig for Turn <S> {
395  fn sin     (self) -> Self {
396    Rad::from (self).sin().into()
397  }
398  fn sin_cos (self) -> (Self, Self) {
399    let (sin, cos) = Rad::from (self).sin_cos();
400    (sin.into(), cos.into())
401  }
402  fn cos     (self) -> Self {
403    Rad::from (self).cos().into()
404  }
405  fn tan     (self) -> Self {
406    Rad::from (self).tan().into()
407  }
408  fn asin    (self) -> Self {
409    Rad::from (self).asin().into()
410  }
411  fn acos    (self) -> Self {
412    Rad::from (self).acos().into()
413  }
414  fn atan    (self) -> Self {
415    Rad::from (self).atan().into()
416  }
417  fn atan2   (self, other : Self) -> Self {
418    Rad::from (self).atan2 (Rad::from (other)).into()
419  }
420}
421impl <S : Real> From <Rad <S>> for Turn <S> {
422  fn from (radians : Rad <S>) -> Self {
423    Turn (radians.0 / Rad::full_turn().0)
424  }
425}
426impl <S : OrderedField> From <Deg <S>> for Turn <S> {
427  fn from (degrees : Deg <S>) -> Self {
428    Turn (degrees.0 / Deg::full_turn().0)
429  }
430}
431
432//
433//  impl AngleWrapped
434//
435impl <S : Real> AngleWrapped <S> {
436  pub fn wrap (angle : Rad <S>) -> Self {
437    AngleWrapped (angle.wrap_unsigned())
438  }
439
440  #[must_use]
441  pub fn map <F> (self, mut f : F) -> Self where F : FnMut (Rad <S>) -> Rad <S> {
442    AngleWrapped (f (self.0).wrap_unsigned())
443  }
444}
445
446//
447//  impl AngleWrappedSigned
448//
449impl <S : Real> AngleWrappedSigned <S> {
450  pub fn wrap (angle : Rad <S>) -> Self {
451    AngleWrappedSigned (angle.wrap_signed())
452  }
453
454  #[must_use]
455  pub fn map <F> (self, mut f : F) -> Self where F : FnMut (Rad <S>) -> Rad <S> {
456    AngleWrappedSigned (f (self.0).wrap_signed())
457  }
458}
459
460//
461//  impl Angles3
462//
463
464impl <S> Angles3 <S> {
465  pub const fn new (
466    yaw   : AngleWrapped <S>,
467    pitch : AngleWrapped <S>,
468    roll  : AngleWrapped <S>
469  ) -> Self {
470    Angles3 { yaw, pitch, roll }
471  }
472
473  pub fn zero() -> Self where S : Real {
474    use num::Zero;
475    Angles3::new (
476      AngleWrapped::zero(),
477      AngleWrapped::zero(),
478      AngleWrapped::zero())
479  }
480
481  pub fn wrap (yaw : Rad <S>, pitch : Rad <S>, roll : Rad <S>) -> Self where S : Real {
482    let yaw   = AngleWrapped::wrap (yaw);
483    let pitch = AngleWrapped::wrap (pitch);
484    let roll  = AngleWrapped::wrap (roll);
485    Angles3 { yaw, pitch, roll }
486  }
487}
488
489impl <S> From <Rotation3 <S>> for Angles3 <S> where S : Real + MaybeSerDes {
490  fn from (rotation : Rotation3 <S>) -> Self {
491    let (yaw, pitch, roll) = {
492      let (yaw, pitch, roll) = rotation.intrinsic_angles();
493      ( AngleWrapped::wrap (yaw),
494        AngleWrapped::wrap (pitch),
495        AngleWrapped::wrap (roll)
496      )
497    };
498    Angles3 { yaw, pitch, roll }
499  }
500}
501
502//
503//  impl Axis2
504//
505impl Axis2 {
506  #[inline]
507  pub const fn component (self) -> usize {
508    self as usize
509  }
510}
511
512//
513//  impl Axis3
514//
515impl Axis3 {
516  #[inline]
517  pub const fn component (self) -> usize {
518    self as usize
519  }
520}
521
522//
523//  impl SignedAxis3
524//
525impl SignedAxis3 {
526  pub fn to_vec <S : Field> (self) -> Vector3 <S> {
527    match self {
528      SignedAxis3::NegX => [-S::one(),   S::zero(),  S::zero()].into(),
529      SignedAxis3::PosX => [ S::one(),   S::zero(),  S::zero()].into(),
530      SignedAxis3::NegY => [ S::zero(), -S::one(),   S::zero()].into(),
531      SignedAxis3::PosY => [ S::zero(),  S::one(),   S::zero()].into(),
532      SignedAxis3::NegZ => [ S::zero(),  S::zero(), -S::one() ].into(),
533      SignedAxis3::PosZ => [ S::zero(),  S::zero(),  S::one() ].into()
534    }
535  }
536  #[inline]
537  pub fn to_unit <S> (self) -> Unit3 <S> where S : Real + std::fmt::Debug {
538    Unit3::unchecked (self.to_vec())
539  }
540}
541impl <S : Field> TryFrom <Vector3 <S>> for SignedAxis3 {
542  type Error = Vector3 <S>;
543  fn try_from (vector : Vector3 <S>) -> Result <Self, Self::Error> {
544    let axis = if vector == Vector3::unit_x() {
545      SignedAxis3::PosX
546    } else if vector == -Vector3::unit_x() {
547      SignedAxis3::NegX
548    } else if vector == Vector3::unit_y() {
549      SignedAxis3::PosY
550    } else if vector == -Vector3::unit_y() {
551      SignedAxis3::NegY
552    } else if vector == Vector3::unit_z() {
553      SignedAxis3::PosZ
554    } else if vector == -Vector3::unit_z() {
555      SignedAxis3::NegZ
556    } else {
557      return Err (vector)
558    };
559    Ok (axis)
560  }
561}
562
563//
564//  impl Quadrant
565//
566impl Quadrant {
567  /// Returns some 'Some (quadrant)' if the vector is strictly contained within a
568  /// quadrant and returns 'None' otherwise
569  pub fn from_vec_strict <S> (vector : Vector2 <S>) -> Option <Quadrant> where
570    S : Ring + SignedExt
571  {
572    let sign_x = vector.x.sign();
573    let sign_y = vector.y.sign();
574    let quadrant = match (sign_x, sign_y) {
575      (Sign::Zero, _) | (_, Sign::Zero) => return None,
576      (Sign::Positive, Sign::Positive)  => Quadrant::PosPos,
577      (Sign::Negative, Sign::Positive)  => Quadrant::NegPos,
578      (Sign::Positive, Sign::Negative)  => Quadrant::PosNeg,
579      (Sign::Negative, Sign::Negative)  => Quadrant::NegNeg
580    };
581    Some (quadrant)
582  }
583  /// Same as `from_vec_strict` for a point
584  #[inline]
585  pub fn from_point_strict <S> (point : Point2 <S>) -> Option <Quadrant> where
586    S : Ring + SignedExt
587  {
588    Quadrant::from_vec_strict (point.to_vector())
589  }
590}
591//  end impl Quadrant
592
593//
594//  impl Octant
595//
596impl Octant {
597  /// Returns some 'Some (octant)' if the vector is strictly contained within an octant
598  /// and returns 'None' otherwise
599  pub fn from_vec_strict <S> (vector : Vector3 <S>) -> Option <Octant> where
600    S : Ring + SignedExt
601  {
602    let sign_x = vector.x.sign();
603    let sign_y = vector.y.sign();
604    let sign_z = vector.z.sign();
605    let octant = match (sign_x, sign_y, sign_z) {
606      (Sign::Zero,          _,          _) |
607      (         _, Sign::Zero,          _) |
608      (         _,          _, Sign::Zero) => return None,
609      (Sign::Positive, Sign::Positive, Sign::Positive) => Octant::PosPosPos,
610      (Sign::Negative, Sign::Positive, Sign::Positive) => Octant::NegPosPos,
611      (Sign::Positive, Sign::Negative, Sign::Positive) => Octant::PosNegPos,
612      (Sign::Negative, Sign::Negative, Sign::Positive) => Octant::NegNegPos,
613      (Sign::Positive, Sign::Positive, Sign::Negative) => Octant::PosPosNeg,
614      (Sign::Negative, Sign::Positive, Sign::Negative) => Octant::NegPosNeg,
615      (Sign::Positive, Sign::Negative, Sign::Negative) => Octant::PosNegNeg,
616      (Sign::Negative, Sign::Negative, Sign::Negative) => Octant::NegNegNeg
617    };
618    Some (octant)
619  }
620  /// Same as `from_vec_strict` for a point
621  #[inline]
622  pub fn from_point_strict <S> (point : Point3 <S>) -> Option <Octant> where
623    S : Ring + SignedExt
624  {
625    Octant::from_vec_strict (point.to_vector())
626  }
627}
628//  end impl Octant
629
630//
631//  impl Positive
632//
633impl <S> Positive <S> where S : PartialOrd + num::Zero {
634  /// Returns 'None' when called with a negative or zero value.
635  ///
636  /// # Example
637  ///
638  /// ```
639  /// # use math_utils::Positive;
640  /// assert!(Positive::new (1.0).is_some());
641  /// assert!(Positive::new (0.0).is_none());
642  /// assert!(Positive::new (-1.0).is_none());
643  /// ```
644  pub fn new (value : S) -> Option <Self> {
645    if value > S::zero() {
646      Some (Positive (value))
647    } else {
648      None
649    }
650  }
651  /// Panics if negative or zero.
652  ///
653  /// # Panics
654  ///
655  /// ```should_panic
656  /// # use math_utils::Positive;
657  /// let x = Positive::noisy (0.0);  // panic!
658  /// ```
659  pub fn noisy (value : S) -> Self {
660    assert!(value > S::zero());
661    Positive (value)
662  }
663  /// Create a new positive number without checking the value.
664  ///
665  /// In debug builds this will fail with a debug assertion if the value is negative:
666  ///
667  /// ```should_panic
668  /// # use math_utils::Positive;
669  /// let negative = Positive::unchecked (-1.0);  // panic!
670  /// ```
671  pub fn unchecked (value : S) -> Self {
672    debug_assert!(value > S::zero());
673    Positive (value)
674  }
675  /// Map an operation on the underlying scalar, panicking if the result is zero or
676  /// negative.
677  ///
678  /// # Example
679  ///
680  /// ```
681  /// # use math_utils::Positive;
682  /// # use math_utils::num::One;
683  /// assert_eq!(*Positive::<f64>::one().map_noisy (|x| 2.0 * x), 2.0);
684  /// ```
685  ///
686  /// # Panics
687  ///
688  /// Panics of the result is negative or zero:
689  ///
690  /// ```should_panic
691  /// # use math_utils::Positive;
692  /// # use math_utils::num::One;
693  /// let v = Positive::<f64>::one().map_noisy (|_| 0.0);  // panic!
694  /// ```
695  #[must_use]
696  pub fn map_noisy <F> (self, mut fun : F) -> Self where F : FnMut (S) -> S {
697    Self::noisy (fun (self.0))
698  }
699}
700impl <S> Positive <S> where S : num::float::FloatCore {
701  /// Positive infinity
702  pub fn infinity() -> Self {
703    Positive (S::infinity())
704  }
705}
706impl Positive <f32> {
707  /// Const constructor for `f32`
708  pub const fn new_f32 (value : f32) -> Option <Self> {
709    if 0.0 < value {
710      Some (Positive (value))
711    } else {
712      None
713    }
714  }
715  /// Const constructor for `f32` with assertions
716  pub const fn noisy_f32 (value : f32) -> Self {
717    assert!(0.0 < value);
718    Positive (value)
719  }
720  /// Const constructor for `f32` with debug assertions
721  pub const fn unchecked_f32 (value : f32) -> Self {
722    debug_assert!(0.0 < value);
723    Positive (value)
724  }
725}
726impl Positive <f64> {
727  /// Const constructor for `f64`
728  pub const fn new_f64 (value : f64) -> Option <Self> {
729    if 0.0 < value {
730      Some (Positive (value))
731    } else {
732      None
733    }
734  }
735  /// Const constructor for `f64` with assertions
736  pub const fn noisy_f64 (value : f64) -> Self {
737    assert!(0.0 < value);
738    Positive (value)
739  }
740  /// Const constructor for `f64` with debug assertions
741  pub const fn unchecked_f64 (value : f64) -> Self {
742    debug_assert!(0.0 < value);
743    Positive (value)
744  }
745}
746impl <S : Ring> num::One for Positive <S> {
747  fn one() -> Self {
748    Positive (S::one())
749  }
750}
751impl <S> std::ops::Deref for Positive <S> {
752  type Target = S;
753  fn deref (&self) -> &S {
754    &self.0
755  }
756}
757impl <S : Ring> std::ops::Mul <Positive <S>> for Positive <S> {
758  type Output = Positive <S>;
759  fn mul (self, rhs : Positive <S>) -> Self::Output {
760    Positive (self.0 * *rhs)
761  }
762}
763impl <S : Ring> std::ops::MulAssign <Positive <S>> for Positive <S> {
764  fn mul_assign (&mut self, rhs : Positive <S>) {
765    self.0 *= *rhs
766  }
767}
768impl <S : Ring> std::ops::Mul <NonNegative <S>> for Positive <S> {
769  type Output = NonNegative <S>;
770  fn mul (self, rhs : NonNegative <S>) -> Self::Output {
771    NonNegative (self.0 * *rhs)
772  }
773}
774impl <S : Ring> std::ops::MulAssign <NonNegative <S>> for Positive <S> {
775  fn mul_assign (&mut self, rhs : NonNegative <S>) {
776    self.0 *= *rhs
777  }
778}
779impl <S : Field> std::ops::Div for Positive <S> {
780  type Output = Self;
781  fn div (self, rhs : Self) -> Self::Output {
782    Positive (self.0 / rhs.0)
783  }
784}
785impl <S : Field> std::ops::DivAssign for Positive <S> {
786  fn div_assign (&mut self, rhs : Self) {
787    self.0 /= rhs.0
788  }
789}
790impl <S : Ring> std::ops::Add for Positive <S> {
791  type Output = Self;
792  fn add (self, rhs : Self) -> Self::Output {
793    Positive (self.0 + rhs.0)
794  }
795}
796impl <S : Ring> std::ops::AddAssign for Positive <S> {
797  fn add_assign (&mut self, rhs : Self) {
798    self.0 += rhs.0
799  }
800}
801impl <S : Ring> std::ops::Add <NonNegative <S>> for Positive <S> {
802  type Output = Self;
803  fn add (self, rhs : NonNegative <S>) -> Self::Output {
804    Positive (self.0 + rhs.0)
805  }
806}
807impl <S : Ring> std::ops::AddAssign <NonNegative <S>> for Positive <S> {
808  fn add_assign (&mut self, rhs : NonNegative <S>) {
809    self.0 += rhs.0
810  }
811}
812//  end impl Positive
813
814//
815//  impl NonNegative
816//
817impl <S> NonNegative <S> where S : PartialOrd + num::Zero {
818  /// Returns 'None' when called with a negative value.
819  ///
820  /// # Example
821  ///
822  /// ```
823  /// # use math_utils::NonNegative;
824  /// assert!(NonNegative::new (1.0).is_some());
825  /// assert!(NonNegative::new (0.0).is_some());
826  /// assert!(NonNegative::new (-1.0).is_none());
827  /// ```
828  pub fn new (value : S) -> Option <Self> {
829    if value >= S::zero() {
830      Some (NonNegative (value))
831    } else {
832      None
833    }
834  }
835  /// Panics if negative.
836  ///
837  /// # Panics
838  ///
839  /// ```should_panic
840  /// # use math_utils::NonNegative;
841  /// let x = NonNegative::noisy (-1.0);  // panic!
842  /// ```
843  pub fn noisy (value : S) -> Self {
844    assert!(S::zero() <= value);
845    NonNegative (value)
846  }
847  /// Create a new non-negative number without checking the value.
848  ///
849  /// This method is completely unchecked for release builds. Debug builds will panic if
850  /// the value is negative:
851  ///
852  /// ```should_panic
853  /// # use math_utils::NonNegative;
854  /// let negative = NonNegative::unchecked (-1.0);   // panic!
855  /// ```
856  pub fn unchecked (value : S) -> Self {
857    debug_assert!(value >= S::zero());
858    NonNegative (value)
859  }
860  /// Map an operation on the underlying scalar, panicking if the result is negative.
861  ///
862  /// # Example
863  ///
864  /// ```
865  /// # use math_utils::NonNegative;
866  /// assert_eq!(*NonNegative::abs (1.0).map_noisy (|x| 2.0 * x), 2.0);
867  /// ```
868  ///
869  /// # Panics
870  ///
871  /// Panics of the result is negative:
872  ///
873  /// ```should_panic
874  /// # use math_utils::NonNegative;
875  /// let v = NonNegative::abs (1.0).map_noisy (|x| -1.0 * x);  // panic!
876  /// ```
877  #[must_use]
878  pub fn map_noisy <F> (self, mut fun : F) -> Self where F : FnMut (S) -> S {
879    Self::noisy (fun (self.0))
880  }
881}
882impl <S> NonNegative <S> where S : num::Signed {
883  /// Converts negative input to absolute value.
884  ///
885  /// # Example
886  ///
887  /// ```
888  /// # use math_utils::NonNegative;
889  /// assert_eq!(*NonNegative::abs (1.0), 1.0);
890  /// assert_eq!(*NonNegative::abs (-1.0), 1.0);
891  /// ```
892  pub fn abs (value : S) -> Self {
893    NonNegative (value.abs())
894  }
895  /// Map an operation on the underlying scalar, converting negative result to an
896  /// absolute value.
897  ///
898  /// # Example
899  ///
900  /// ```
901  /// # use math_utils::NonNegative;
902  /// assert_eq!(*NonNegative::abs (1.0).map_abs (|x| -2.0 * x), 2.0);
903  /// ```
904  #[must_use]
905  pub fn map_abs <F> (self, mut fun : F) -> Self where F : FnMut (S) -> S {
906    Self::abs (fun (self.0))
907  }
908}
909impl <S : Sqrt> Sqrt for NonNegative <S> {
910  fn sqrt (self) -> Self {
911    NonNegative (self.0.sqrt())
912  }
913}
914impl <S : MinMax> MinMax for NonNegative <S> {
915  fn min (self, other : Self) -> Self {
916    NonNegative (S::min (self.0, other.0))
917  }
918  fn max (self, other : Self) -> Self {
919    NonNegative (S::max (self.0, other.0))
920  }
921  fn clamp (self, min : Self, max : Self) -> Self {
922    NonNegative (self.0.clamp (min.0, max.0))
923  }
924}
925impl NonNegative <f32> {
926  /// Const constructor for `f32`
927  pub const fn new_f32 (value : f32) -> Option <Self> {
928    if 0.0 <= value {
929      Some (NonNegative (value))
930    } else {
931      None
932    }
933  }
934  /// Const constructor for `f32` with assertions
935  pub const fn noisy_f32 (value : f32) -> Self {
936    assert!(0.0 <= value);
937    NonNegative (value)
938  }
939  /// Const constructor for `f32` with debug assertions
940  pub const fn unchecked_f32 (value : f32) -> Self {
941    debug_assert!(0.0 <= value);
942    NonNegative (value)
943  }
944}
945impl NonNegative <f64> {
946  /// Const constructor for `f64`
947  pub const fn new_f64 (value : f64) -> Option <Self> {
948    if 0.0 <= value {
949      Some (NonNegative (value))
950    } else {
951      None
952    }
953  }
954  /// Const constructor for `f64` with assertions
955  pub const fn noisy_f64 (value : f64) -> Self {
956    assert!(0.0 <= value);
957    NonNegative (value)
958  }
959  /// Const constructor for `f64` with debug assertions
960  pub const fn unchecked_f64 (value : f64) -> Self {
961    debug_assert!(0.0 <= value);
962    NonNegative (value)
963  }
964}
965impl <S : Ring> From <Positive <S>> for NonNegative <S> {
966  fn from (positive : Positive <S>) -> Self {
967    NonNegative (*positive)
968  }
969}
970impl <S : Ring> num::Zero for NonNegative <S> {
971  fn zero() -> Self {
972    NonNegative (S::zero())
973  }
974  fn is_zero (&self) -> bool {
975    self.0.is_zero()
976  }
977}
978impl <S : Field> num::One for NonNegative <S> {
979  fn one() -> Self {
980    NonNegative (S::one())
981  }
982}
983impl <S> std::ops::Deref for NonNegative <S> {
984  type Target = S;
985  fn deref (&self) -> &S {
986    &self.0
987  }
988}
989impl <S : Ring> std::ops::Mul for NonNegative <S> {
990  type Output = Self;
991  fn mul (self, rhs : Self) -> Self::Output {
992    NonNegative (self.0 * rhs.0)
993  }
994}
995impl <S : Ring> std::ops::Mul <Positive <S>> for NonNegative <S> {
996  type Output = Self;
997  fn mul (self, rhs : Positive <S>) -> Self::Output {
998    NonNegative (self.0 * rhs.0)
999  }
1000}
1001impl <S : Field> std::ops::Div for NonNegative <S> {
1002  type Output = Self;
1003  fn div (self, rhs : Self) -> Self::Output {
1004    NonNegative (self.0 / rhs.0)
1005  }
1006}
1007impl <S : Ring> std::ops::Add for NonNegative <S> {
1008  type Output = Self;
1009  fn add (self, rhs : Self) -> Self::Output {
1010    NonNegative (self.0 + rhs.0)
1011  }
1012}
1013//  end impl NonNegative
1014
1015//
1016//  impl NonZero
1017//
1018impl <S> NonZero <S> where S : PartialEq + num::Zero {
1019  /// Returns 'None' when called with zero.
1020  ///
1021  /// # Example
1022  ///
1023  /// ```
1024  /// # use math_utils::NonZero;
1025  /// assert!(NonZero::new (2.0).is_some());
1026  /// assert!(NonZero::new (0.0).is_none());
1027  /// assert!(NonZero::new (-2.0).is_some());
1028  /// ```
1029  #[inline]
1030  pub fn new (value : S) -> Option <Self> {
1031    if S::zero() != value {
1032      Some (NonZero (value))
1033    } else {
1034      None
1035    }
1036  }
1037  /// Panics if called with zero.
1038  ///
1039  /// # Panics
1040  ///
1041  /// ```should_panic
1042  /// # use math_utils::NonZero;
1043  /// let x = NonZero::noisy (0.0);  // panic!
1044  /// ```
1045  #[inline]
1046  pub fn noisy (value : S) -> Self where S : std::fmt::Debug {
1047    assert_ne!(S::zero(), value);
1048    NonZero (value)
1049  }
1050  /// Debug panic if called with zero.
1051  ///
1052  /// # Panics
1053  ///
1054  /// ```should_panic
1055  /// # use math_utils::NonZero;
1056  /// let x = NonZero::unchecked (0.0);  // panic!
1057  /// ```
1058  #[inline]
1059  pub fn unchecked (value : S) -> Self where S : std::fmt::Debug {
1060    debug_assert_ne!(S::zero(), value);
1061    NonZero (value)
1062  }
1063  /// Map an operation on the underlying scalar, panicking if the result is zero.
1064  ///
1065  /// # Example
1066  ///
1067  /// ```
1068  /// # use math_utils::NonZero;
1069  /// # use math_utils::num::One;
1070  /// assert_eq!(*NonZero::<f64>::one().map_noisy (|x| 0.5 * x), 0.5);
1071  /// ```
1072  ///
1073  /// # Panics
1074  ///
1075  /// Panics of the result is zero.
1076  ///
1077  /// ```should_panic
1078  /// # use math_utils::NonZero;
1079  /// # use math_utils::num::One;
1080  /// let v = NonZero::<f64>::one().map_noisy (|x| 0.0 * x);  // panic!
1081  /// ```
1082  #[inline]
1083  #[must_use]
1084  pub fn map_noisy <F> (self, mut fun : F) -> Self where
1085    S : std::fmt::Debug,
1086    F : FnMut (S) -> S
1087  {
1088    Self::noisy (fun (self.0))
1089  }
1090}
1091impl NonZero <f32> {
1092  /// Const constructor for `f32`
1093  pub const fn new_f32 (value : f32) -> Option <Self> {
1094    if value != 0.0 {
1095      Some (NonZero (value))
1096    } else {
1097      None
1098    }
1099  }
1100  /// Const constructor for `f32` with assertions
1101  pub const fn noisy_f32 (value : f32) -> Self {
1102    assert!(value != 0.0);
1103    NonZero (value)
1104  }
1105  /// Const constructor for `f32` with debug assertions
1106  pub const fn unchecked_f32 (value : f32) -> Self {
1107    debug_assert!(value != 0.0);
1108    NonZero (value)
1109  }
1110}
1111impl NonZero <f64> {
1112  /// Const constructor for `f64`
1113  pub const fn new_f64 (value : f64) -> Option <Self> {
1114    if value != 0.0 {
1115      Some (NonZero (value))
1116    } else {
1117      None
1118    }
1119  }
1120  /// Const constructor for `f64` with assertions
1121  pub const fn noisy_f64 (value : f64) -> Self {
1122    assert!(value != 0.0);
1123    NonZero (value)
1124  }
1125  /// Const constructor for `f64` with debug assertions
1126  pub const fn unchecked_f64 (value : f64) -> Self {
1127    debug_assert!(value != 0.0);
1128    NonZero (value)
1129  }
1130}
1131impl <S : MultiplicativeMonoid> num::One for NonZero <S> {
1132  fn one() -> Self {
1133    NonZero (S::one())
1134  }
1135}
1136impl <S> std::ops::Deref for NonZero <S> {
1137  type Target = S;
1138  fn deref (&self) -> &S {
1139    &self.0
1140  }
1141}
1142impl <S : MultiplicativeMonoid> std::ops::Mul for NonZero <S> {
1143  type Output = Self;
1144  fn mul (self, rhs : Self) -> Self::Output {
1145    NonZero (self.0 * rhs.0)
1146  }
1147}
1148impl <S : PartialEq> Eq  for NonZero <S> { }
1149//  end impl NonZero
1150
1151//
1152//  impl Normalized
1153//
1154impl <S> Normalized <S> where S : num::One + num::Zero {
1155  // NOTE: num::Zero requires std::ops::Add which Normalized does not implement
1156  #[inline]
1157  pub fn zero() -> Self {
1158    Normalized (S::zero())
1159  }
1160  #[inline]
1161  pub fn is_zero (&self) -> bool {
1162    self.0.is_zero()
1163  }
1164  /// Returns 'None' when called with a value outside of the closed unit interval
1165  /// \[0,1\].
1166  ///
1167  /// # Example
1168  ///
1169  /// ```
1170  /// # use math_utils::Normalized;
1171  /// assert!(Normalized::new (2.0).is_none());
1172  /// assert!(Normalized::new (1.0).is_some());
1173  /// assert!(Normalized::new (0.5).is_some());
1174  /// assert!(Normalized::new (0.0).is_some());
1175  /// assert!(Normalized::new (-1.0).is_none());
1176  /// ```
1177  #[inline]
1178  pub fn new (value : S) -> Option <Self> where S : PartialOrd {
1179    if S::zero() <= value && value <= S::one() {
1180      Some (Normalized (value))
1181    } else {
1182      None
1183    }
1184  }
1185  /// Clamps to the closed unit interval \[0,1\].
1186  ///
1187  /// # Example
1188  ///
1189  /// ```
1190  /// # use math_utils::Normalized;
1191  /// assert_eq!(*Normalized::clamp (2.0), 1.0);
1192  /// assert_eq!(*Normalized::clamp (-1.0), 0.0);
1193  /// ```
1194  // TODO: can we get rid of this method and impl MinMax::clamp ?
1195  #[inline]
1196  #[expect(clippy::same_name_method)]
1197  pub fn clamp (value : S) -> Self where S : MinMax {
1198    Normalized (value.clamp (S::zero(), S::one()))
1199  }
1200  /// Panics if outside the closed unit interval \[0,1\].
1201  ///
1202  /// # Panics
1203  ///
1204  /// ```should_panic
1205  /// # use math_utils::Normalized;
1206  /// let x = Normalized::noisy (-1.0);  // panic!
1207  /// ```
1208  ///
1209  /// ```should_panic
1210  /// # use math_utils::Normalized;
1211  /// let x = Normalized::noisy (2.0);  // panic!
1212  /// ```
1213  #[inline]
1214  pub fn noisy (value : S) -> Self where S : PartialOrd {
1215    assert!(value <= S::one());
1216    assert!(S::zero() <= value);
1217    Normalized (value)
1218  }
1219  /// Create a new normalized number without checking the value.
1220  ///
1221  /// This method is completely unchecked for release builds. Debug builds will panic if
1222  /// the value is negative:
1223  ///
1224  /// ```should_panic
1225  /// # use math_utils::Normalized;
1226  /// let normalized = Normalized::unchecked (1.5);   // panic!
1227  /// ```
1228  pub fn unchecked (value : S) -> Self where S : PartialOrd {
1229    debug_assert!(value <= S::one());
1230    debug_assert!(S::zero() <= value);
1231    Normalized (value)
1232  }
1233  /// Map an operation on the underlying scalar, clamping results to the closed unit
1234  /// interval \[0,1\].
1235  ///
1236  /// # Example
1237  ///
1238  /// ```
1239  /// # use math_utils::Normalized;
1240  /// # use math_utils::num::One;
1241  /// assert_eq!(*Normalized::<f64>::one().map_clamp (|x|  2.0 * x), 1.0);
1242  /// assert_eq!(*Normalized::<f64>::one().map_clamp (|x| -2.0 * x), 0.0);
1243  /// ```
1244  #[inline]
1245  #[must_use]
1246  pub fn map_clamp <F> (self, mut fun : F) -> Self where
1247    S : MinMax,
1248    F : FnMut (S) -> S
1249  {
1250    Self::clamp (fun (self.0))
1251  }
1252  /// Map an operation on the underlying scalar, panicking if the result is outside the
1253  /// unit interval \[0,1\].
1254  ///
1255  /// # Example
1256  ///
1257  /// ```
1258  /// # use math_utils::Normalized;
1259  /// # use math_utils::num::One;
1260  /// assert_eq!(*Normalized::<f64>::one().map_noisy (|x| 0.5 * x), 0.5);
1261  /// ```
1262  ///
1263  /// # Panics
1264  ///
1265  /// Panics of the result is outside \[0,1\]:
1266  ///
1267  /// ```should_panic
1268  /// # use math_utils::Normalized;
1269  /// # use math_utils::num::One;
1270  /// let v = Normalized::<f64>::one().map_noisy (|x| -1.0 * x);  // panic!
1271  /// ```
1272  ///
1273  /// ```should_panic
1274  /// # use math_utils::Normalized;
1275  /// # use math_utils::num::One;
1276  /// let v = Normalized::<f64>::one().map_noisy (|x|  2.0 * x);  // panic!
1277  /// ```
1278  #[inline]
1279  #[must_use]
1280  pub fn map_noisy <F> (self, mut fun : F) -> Self where
1281    S : PartialOrd,
1282    F : FnMut (S) -> S
1283  {
1284    Self::noisy (fun (self.0))
1285  }
1286}
1287impl Normalized <f32> {
1288  /// Const constructor for `f32`
1289  pub const fn new_f32 (value : f32) -> Option <Self> {
1290    if 0.0 <= value && value <= 1.0 {
1291      Some (Normalized (value))
1292    } else {
1293      None
1294    }
1295  }
1296  /// Const constructor for `f32` with assertions
1297  pub const fn noisy_f32 (value : f32) -> Self {
1298    assert!(value <= 1.0);
1299    assert!(0.0 <= value);
1300    Normalized (value)
1301  }
1302  /// Const constructor for `f32` with debug assertions
1303  pub const fn unchecked_f32 (value : f32) -> Self {
1304    debug_assert!(value <= 1.0);
1305    debug_assert!(0.0 <= value);
1306    Normalized (value)
1307  }
1308}
1309impl Normalized <f64> {
1310  /// Const constructor for `f64`
1311  pub const fn new_f64 (value : f64) -> Option <Self> {
1312    if 0.0 <= value && value <= 1.0 {
1313      Some (Normalized (value))
1314    } else {
1315      None
1316    }
1317  }
1318  /// Const constructor for `f64` with assertions
1319  pub const fn noisy_f64 (value : f64) -> Self {
1320    assert!(value <= 1.0);
1321    assert!(0.0 <= value);
1322    Normalized (value)
1323  }
1324  /// Const constructor for `f64` with debug assertions
1325  pub const fn unchecked_f64 (value : f64) -> Self {
1326    debug_assert!(value <= 1.0);
1327    debug_assert!(0.0 <= value);
1328    Normalized (value)
1329  }
1330}
1331impl <S : Ring> num::One for Normalized <S> {
1332  fn one() -> Self {
1333    Normalized (S::one())
1334  }
1335}
1336impl <S> std::ops::Deref for Normalized <S> {
1337  type Target = S;
1338  fn deref (&self) -> &S {
1339    &self.0
1340  }
1341}
1342impl <S : Ring> std::ops::Mul for Normalized <S> {
1343  type Output = Self;
1344  fn mul (self, rhs : Self) -> Self::Output {
1345    Normalized (self.0 * rhs.0)
1346  }
1347}
1348impl <S : PartialEq> Eq  for Normalized <S> { }
1349#[expect(clippy::derive_ord_xor_partial_ord)]
1350impl <S : PartialOrd> Ord for Normalized <S> {
1351  fn cmp (&self, other : &Self) -> std::cmp::Ordering {
1352    // safe to unwrap: normalized values are never NaN
1353    self.partial_cmp (other).unwrap()
1354  }
1355}
1356//  end impl Normalized
1357
1358//
1359//  impl NormalSigned
1360//
1361impl <S> NormalSigned <S> {
1362  // NOTE: num::Zero requires std::ops::Add which Normalized does not implement
1363  #[inline]
1364  pub fn zero() -> Self where S : num::Zero {
1365    NormalSigned (S::zero())
1366  }
1367  #[inline]
1368  pub fn is_zero (&self) -> bool where S : num::Zero {
1369    self.0.is_zero()
1370  }
1371  /// Returns 'None' when called with a value outside of the closed interval \[-1,1\].
1372  ///
1373  /// # Example
1374  ///
1375  /// ```
1376  /// # use math_utils::NormalSigned;
1377  /// assert!(NormalSigned::new (2.0).is_none());
1378  /// assert!(NormalSigned::new (1.0).is_some());
1379  /// assert!(NormalSigned::new (0.5).is_some());
1380  /// assert!(NormalSigned::new (0.0).is_some());
1381  /// assert!(NormalSigned::new (-1.0).is_some());
1382  /// assert!(NormalSigned::new (-2.0).is_none());
1383  /// ```
1384  #[inline]
1385  pub fn new (value : S) -> Option <Self> where S : OrderedRing {
1386    if -S::one() <= value && value <= S::one() {
1387      Some (NormalSigned (value))
1388    } else {
1389      None
1390    }
1391  }
1392  /// Clamps to the closed interval [-1,1].
1393  ///
1394  /// # Example
1395  ///
1396  /// ```
1397  /// # use math_utils::NormalSigned;
1398  /// assert_eq!(*NormalSigned::clamp (2.0), 1.0);
1399  /// assert_eq!(*NormalSigned::clamp (-2.0), -1.0);
1400  /// ```
1401  // TODO: can we get rid of this method and impl MinMax::clamp ?
1402  #[inline]
1403  #[expect(clippy::same_name_method)]
1404  pub fn clamp (value : S) -> Self where S : Ring + MinMax {
1405    NormalSigned (value.clamp (-S::one(), S::one()))
1406  }
1407  /// Panics if outside the closed interval [-1,1].
1408  ///
1409  /// # Panics
1410  ///
1411  /// ```should_panic
1412  /// # use math_utils::NormalSigned;
1413  /// let x = NormalSigned::noisy (-2.0);   // panic!
1414  /// ```
1415  ///
1416  /// ```should_panic
1417  /// # use math_utils::NormalSigned;
1418  /// let x = NormalSigned::noisy (2.0);    // panic!
1419  /// ```
1420  #[inline]
1421  pub fn noisy (value : S) -> Self where S : OrderedRing {
1422    assert!(value <= S::one());
1423    assert!(-S::one() <= value);
1424    NormalSigned (value)
1425  }
1426  /// Map an operation on the underlying scalar, clamping results to the closed interval
1427  /// [-1,1].
1428  ///
1429  /// # Example
1430  ///
1431  /// ```
1432  /// # use math_utils::NormalSigned;
1433  /// # use math_utils::num::One;
1434  /// assert_eq!(*NormalSigned::<f64>::one().map_clamp (|x|  2.0 * x),  1.0);
1435  /// assert_eq!(*NormalSigned::<f64>::one().map_clamp (|x| -2.0 * x), -1.0);
1436  /// ```
1437  #[inline]
1438  #[must_use]
1439  pub fn map_clamp <F> (self, mut fun : F) -> Self where
1440    S : Ring + MinMax,
1441    F : FnMut (S) -> S
1442  {
1443    Self::clamp (fun (self.0))
1444  }
1445  /// Map an operation on the underlying scalar, panicking if the result is outside the
1446  /// interval [-1, 1].
1447  ///
1448  /// # Example
1449  ///
1450  /// ```
1451  /// # use math_utils::NormalSigned;
1452  /// # use math_utils::num::One;
1453  /// assert_eq!(*NormalSigned::<f64>::one().map_noisy (|x| 0.5 * x), 0.5);
1454  /// ```
1455  ///
1456  /// # Panics
1457  ///
1458  /// Panics of the result is outside [-1, 1]:
1459  ///
1460  /// ```should_panic
1461  /// # use math_utils::NormalSigned;
1462  /// # use math_utils::num::One;
1463  /// let v = NormalSigned::<f64>::one().map_noisy (|x| -2.0 * x);  // panic!
1464  /// ```
1465  ///
1466  /// ```should_panic
1467  /// # use math_utils::NormalSigned;
1468  /// # use math_utils::num::One;
1469  /// let v = NormalSigned::<f64>::one().map_noisy (|x|  2.0 * x);  // panic!
1470  /// ```
1471  #[inline]
1472  #[must_use]
1473  pub fn map_noisy <F> (self, mut fun : F) -> Self where
1474    S : OrderedRing,
1475    F : FnMut (S) -> S
1476  {
1477    Self::noisy (fun (self.0))
1478  }
1479}
1480impl NormalSigned <f32> {
1481  /// Const constructor for `f32`
1482  pub const fn new_f32 (value : f32) -> Option <Self> {
1483    if -1.0 <= value && value <= 1.0 {
1484      Some (NormalSigned (value))
1485    } else {
1486      None
1487    }
1488  }
1489  /// Const constructor for `f32` with assertions
1490  pub const fn noisy_f32 (value : f32) -> Self {
1491    assert!(value <= 1.0);
1492    assert!(-1.0 <= value);
1493    NormalSigned (value)
1494  }
1495  /// Const constructor for `f32` with debug assertions
1496  pub const fn unchecked_f32 (value : f32) -> Self {
1497    debug_assert!(value <= 1.0);
1498    debug_assert!(-1.0 <= value);
1499    NormalSigned (value)
1500  }
1501}
1502impl NormalSigned <f64> {
1503  /// Const constructor for `f64`
1504  pub const fn new_f64 (value : f64) -> Option <Self> {
1505    if -1.0 <= value && value <= 1.0 {
1506      Some (NormalSigned (value))
1507    } else {
1508      None
1509    }
1510  }
1511  /// Const constructor for `f64` with assertions
1512  pub const fn noisy_f64 (value : f64) -> Self {
1513    assert!(value <= 1.0);
1514    assert!(-1.0 <= value);
1515    NormalSigned (value)
1516  }
1517  /// Const constructor for `f64` with debug assertions
1518  pub const fn unchecked_f64 (value : f64) -> Self {
1519    debug_assert!(value <= 1.0);
1520    debug_assert!(-1.0 <= value);
1521    NormalSigned (value)
1522  }
1523}
1524impl <S : OrderedField> From <Normalized <S>> for NormalSigned <S> {
1525  fn from (Normalized (value) : Normalized <S>) -> Self {
1526    debug_assert!(S::zero() <= value);
1527    debug_assert!(value <= S::one());
1528    NormalSigned (value)
1529  }
1530}
1531impl <S : Field> num::One for NormalSigned <S> {
1532  fn one() -> Self {
1533    NormalSigned (S::one())
1534  }
1535}
1536impl <S> std::ops::Deref for NormalSigned <S> {
1537  type Target = S;
1538  fn deref (&self) -> &S {
1539    &self.0
1540  }
1541}
1542impl <S : Field> std::ops::Mul for NormalSigned <S> {
1543  type Output = Self;
1544  fn mul (self, rhs : Self) -> Self::Output {
1545    NormalSigned (self.0 * rhs.0)
1546  }
1547}
1548impl <S : Field> Eq  for NormalSigned <S> { }
1549#[expect(clippy::derive_ord_xor_partial_ord)]
1550impl <S : OrderedField> Ord for NormalSigned <S> {
1551  fn cmp (&self, other : &Self) -> std::cmp::Ordering {
1552    // safe to unwrap: normalized values are never NaN
1553    self.partial_cmp (other).unwrap()
1554  }
1555}
1556impl <S : Field> std::ops::Neg for NormalSigned <S> {
1557  type Output = Self;
1558  fn neg (self) -> Self {
1559    NormalSigned (-self.0)
1560  }
1561}
1562//  end impl NormalSigned
1563
1564//
1565//  impl Vector2
1566//
1567impl <S : Ring> Vector2Ext <S> for Vector2 <S> {
1568  /// Exterior or wedge product: $(a,b) \wedge (c,d) = ad - bc$
1569  fn exterior_product (self, rhs : Vector2 <S>) -> S {
1570    self.x * rhs.y - self.y * rhs.x
1571  }
1572}
1573fn ortho_vec2 <S> (vector : Vector2 <S>) -> Vector2 <S> {
1574  vector.yx()
1575}
1576
1577//
1578//  impl Vector3
1579//
1580fn ortho_vec3 <S> (vector : Vector3 <S>) -> Vector3 <S> where
1581  S : Ring + approx::AbsDiffEq <Epsilon = S>
1582{
1583  // NOTE: for floating-point numbers, an algorithm using copysign may be 5-10% faster,
1584  // but copysign is only available for floating-point numbers
1585  // (https://math.stackexchange.com/a/4112622)
1586  let mut ortho = vector.cross (Vector3::unit_x());
1587  if approx::abs_diff_eq!(ortho, Vector3::zero()) {
1588    ortho = vector.cross (Vector3::unit_y())
1589  }
1590  ortho
1591}
1592
1593//
1594//  impl Vector4
1595//
1596fn ortho_vec4 <S> (_vector : Vector4 <S>) -> Vector4 <S> {
1597  unimplemented!("TODO")
1598}
1599
1600//
1601//  impl Unit
1602//
1603impl <S> std::ops::Deref for Unit <S> {
1604  type Target = S;
1605  fn deref (&self) -> &S {
1606    &self.0
1607  }
1608}
1609
1610//
1611//  impl Rotation2
1612//
1613impl <S> Rotation2 <S> where S : Ring + MaybeSerDes {
1614  /// Identity rotation
1615  pub fn identity() -> Self {
1616    use num::One;
1617    Self::one()
1618  }
1619  /// Returns `None` if called with a matrix that is not orthonormal with determinant
1620  /// +1.
1621  ///
1622  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`.
1623  pub fn new (mat : Matrix2 <S>) -> Option <Self> {
1624    let transform = LinearAuto (LinearIso::new (mat)?);
1625    if !transform.is_rotation() {
1626      None
1627    } else {
1628      Some (Rotation2 (transform))
1629    }
1630  }
1631  /// Returns `None` if called with a matrix that is not orthonormal with determinant
1632  /// +1.
1633  ///
1634  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`
1635  /// (approximately using relative equality).
1636  pub fn new_approx (mat : Matrix2 <S>) -> Option <Self> where
1637    S : approx::RelativeEq <Epsilon=S>
1638  {
1639    let four         = S::one() + S::one() + S::one() + S::one();
1640    let thirtytwo    = four * four * (S::one() + S::one());
1641    let epsilon      = S::default_epsilon() * thirtytwo;
1642    let max_relative = S::default_max_relative() * four;
1643    if approx::relative_ne!(mat * mat.transposed(), Matrix2::identity(),
1644      max_relative=max_relative, epsilon=epsilon
1645    ) || approx::relative_ne!(mat.determinant(), S::one(), max_relative=max_relative) {
1646      None
1647    } else {
1648      Some (Rotation2 (LinearAuto (LinearIso (mat, PhantomData))))
1649    }
1650  }
1651  /// Create a rotation for the given angle
1652  pub fn from_angle (angle : Rad <S>) -> Self where S : num::real::Real {
1653    Rotation2 (LinearAuto (LinearIso (Matrix2::rotation_z (angle.0), PhantomData)))
1654  }
1655  /// Panic if the given matrix is not orthogonal with determinant +1.
1656  ///
1657  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`.
1658  pub fn noisy (mat : Matrix2 <S>) -> Self where S : std::fmt::Debug {
1659    assert_eq!(mat * mat.transposed(), Matrix2::identity());
1660    assert_eq!(mat.determinant(), S::one());
1661    Rotation2 (LinearAuto (LinearIso (mat, PhantomData)))
1662  }
1663  /// Panic if the given matrix is not orthogonal with determinant +1.
1664  ///
1665  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`
1666  /// (approximately using relative equality).
1667  pub fn noisy_approx (mat : Matrix2 <S>) -> Self where
1668    S : approx::RelativeEq <Epsilon=S> + std::fmt::Debug
1669  {
1670    let four         = S::one() + S::one() + S::one() + S::one();
1671    let epsilon      = S::default_epsilon() * four;
1672    let max_relative = S::default_max_relative() * four;
1673    approx::assert_relative_eq!(mat * mat.transposed(), Matrix2::identity(),
1674      epsilon=epsilon, max_relative=max_relative);
1675    approx::assert_relative_eq!(mat.determinant(), S::one(), max_relative=max_relative);
1676    Rotation2 (LinearAuto (LinearIso (mat, PhantomData)))
1677  }
1678  /// It is a debug panic if the given matrix is not orthogonal with determinant +1.
1679  ///
1680  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`.
1681  pub fn unchecked (mat : Matrix2 <S>) -> Self where S : std::fmt::Debug {
1682    debug_assert!(mat * mat.transposed() == Matrix2::identity());
1683    debug_assert!(mat.determinant() == S::one());
1684    Rotation2 (LinearAuto (LinearIso (mat, PhantomData)))
1685  }
1686  /// It is a debug panic if the given matrix is not orthogonal with determinant +1.
1687  ///
1688  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`
1689  /// (approximately using relative equality).
1690  pub fn unchecked_approx (mat : Matrix2 <S>) -> Self where
1691    S : approx::RelativeEq <Epsilon=S> + std::fmt::Debug
1692  {
1693    if cfg!(debug_assertions) {
1694      let four         = S::one() + S::one() + S::one() + S::one();
1695      let epsilon      = S::default_epsilon() * four;
1696      let max_relative = S::default_max_relative() * four;
1697      approx::assert_relative_eq!(mat * mat.transposed(), Matrix2::identity(),
1698        max_relative=max_relative, epsilon=epsilon);
1699      approx::assert_relative_eq!(mat.determinant(), S::one(),
1700        max_relative=max_relative);
1701    }
1702    Rotation2 (LinearAuto (LinearIso (mat, PhantomData)))
1703  }
1704  /// Get the underlying matrix
1705  pub const fn mat (self) -> Matrix2 <S> {
1706    self.0.0.0
1707  }
1708  /// Rotates a point around the origin
1709  pub fn rotate (self, point : Point2 <S>) -> Point2 <S> {
1710    Point2::from (self * point.0)
1711  }
1712  /// Rotate around a center point
1713  pub fn rotate_around (self, point : Point2 <S>, center : Point2 <S>) -> Point2 <S> {
1714    self.rotate (point - center.0) + center.0
1715  }
1716}
1717impl <S> std::ops::Deref for Rotation2 <S> where S : Ring + MaybeSerDes {
1718  type Target = LinearAuto <S, Vector2 <S>>;
1719  fn deref (&self) -> &Self::Target {
1720    &self.0
1721  }
1722}
1723impl <S> num::Inv for Rotation2 <S> where S : Ring + MaybeSerDes {
1724  type Output = Self;
1725  fn inv (self) -> Self::Output {
1726    Rotation2 (LinearAuto (LinearIso (self.0.0.0.transpose(), PhantomData)))
1727  }
1728}
1729impl <S> std::ops::Mul <Vector2 <S>> for Rotation2 <S> where S : Ring + MaybeSerDes {
1730  type Output = Vector2 <S>;
1731  fn mul (self, rhs : Vector2 <S>) -> Self::Output {
1732    self.0 * rhs
1733  }
1734}
1735impl <S> std::ops::Mul <NonZero2 <S>> for Rotation2 <S> where S : Ring + MaybeSerDes {
1736  type Output = NonZero2 <S>;
1737  fn mul (self, rhs : NonZero2 <S>) -> Self::Output {
1738    NonZero2 (self.0 * rhs.0)
1739  }
1740}
1741impl <S> std::ops::Mul <Unit2 <S>> for Rotation2 <S> where S : Ring + MaybeSerDes {
1742  type Output = Unit2 <S>;
1743  fn mul (self, rhs : Unit2 <S>) -> Self::Output {
1744    Unit2 (self.0 * rhs.0)
1745  }
1746}
1747impl <S> std::ops::Mul <Rotation2 <S>> for Vector2 <S> where S : Ring + MaybeSerDes {
1748  type Output = Vector2 <S>;
1749  fn mul (self, rhs : Rotation2 <S>) -> Self::Output {
1750    self * rhs.0
1751  }
1752}
1753impl <S> std::ops::Mul for Rotation2 <S> where S : Ring + MaybeSerDes {
1754  type Output = Self;
1755  fn mul (self, rhs : Self) -> Self::Output {
1756    Rotation2 (self.0 * rhs.0)
1757  }
1758}
1759impl <S> std::ops::MulAssign <Self> for Rotation2 <S> where S : Ring + MaybeSerDes {
1760  fn mul_assign (&mut self, rhs : Self) {
1761    self.0 *= rhs.0
1762  }
1763}
1764impl <S> std::ops::Div for Rotation2 <S> where S : Ring + MaybeSerDes {
1765  type Output = Self;
1766  #[expect(clippy::suspicious_arithmetic_impl)]
1767  fn div (self, rhs : Self) -> Self::Output {
1768    use num::Inv;
1769    self * rhs.inv()
1770  }
1771}
1772impl <S> std::ops::DivAssign <Self> for Rotation2 <S> where S : Ring + MaybeSerDes {
1773  #[expect(clippy::suspicious_op_assign_impl)]
1774  fn div_assign (&mut self, rhs : Self) {
1775    use num::Inv;
1776    *self *= rhs.inv()
1777  }
1778}
1779impl <S> num::One for Rotation2 <S> where S : Ring + MaybeSerDes + num::Zero + num::One {
1780  fn one() -> Self {
1781    Rotation2 (LinearAuto (LinearIso (Matrix2::identity(), PhantomData)))
1782  }
1783}
1784
1785//
1786//  impl Rotation3
1787//
1788impl <S> Rotation3 <S> where S : Ring + MaybeSerDes {
1789  /// Identity rotation
1790  pub fn identity() -> Self {
1791    use num::One;
1792    Self::one()
1793  }
1794  /// Returns `None` if called with a matrix that is not orthonormal with determinant
1795  /// +1.
1796  ///
1797  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`.
1798  pub fn new (mat : Matrix3 <S>) -> Option <Self> {
1799    if mat * mat.transposed() != Matrix3::identity() || mat.determinant() != S::one() {
1800      None
1801    } else {
1802      Some (Rotation3 (LinearAuto (LinearIso (mat, PhantomData))))
1803    }
1804  }
1805  /// Returns `None` if called with a matrix that is not orthonormal with determinant
1806  /// +1.
1807  ///
1808  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`
1809  /// (approximately using relative equality).
1810  pub fn new_approx (mat : Matrix3 <S>) -> Option <Self> where
1811    S : approx::RelativeEq <Epsilon=S>
1812  {
1813    let four         = S::one() + S::one() + S::one() + S::one();
1814    let thirtytwo    = four * four * (S::one() + S::one());
1815    let epsilon      = S::default_epsilon() * thirtytwo;
1816    let max_relative = S::default_max_relative() * four;
1817    if approx::relative_ne!(mat * mat.transposed(), Matrix3::identity(),
1818      max_relative=max_relative, epsilon=epsilon
1819    ) || approx::relative_ne!(mat.determinant(), S::one(), max_relative=max_relative) {
1820      None
1821    } else {
1822      Some (Rotation3 (LinearAuto (LinearIso (mat, PhantomData))))
1823    }
1824  }
1825  /// Panic if the given matrix is not orthogonal with determinant +1.
1826  ///
1827  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`.
1828  pub fn noisy (mat : Matrix3 <S>) -> Self {
1829    assert!(mat * mat.transposed() == Matrix3::identity());
1830    assert!(mat.determinant() == S::one());
1831    Rotation3 (LinearAuto (LinearIso (mat, PhantomData)))
1832  }
1833  /// Panic if the given matrix is not orthogonal with determinant +1.
1834  ///
1835  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`
1836  /// (approximately using relative equality).
1837  pub fn noisy_approx (mat : Matrix3 <S>) -> Self where
1838    S : approx::RelativeEq <Epsilon=S> + std::fmt::Debug
1839  {
1840    let four         = S::one() + S::one() + S::one() + S::one();
1841    let eight        = four + S::one() + S::one() + S::one() + S::one();
1842    let epsilon      = S::default_epsilon() * four;
1843    let max_relative = S::default_max_relative() * eight;
1844    approx::assert_relative_eq!(mat * mat.transposed(), Matrix3::identity(),
1845      max_relative=max_relative, epsilon=epsilon);
1846    approx::assert_relative_eq!(mat.determinant(), S::one(), max_relative=max_relative);
1847    Rotation3 (LinearAuto (LinearIso (mat, PhantomData)))
1848  }
1849  /// It is a debug panic if the given matrix is not orthogonal with determinant +1.
1850  ///
1851  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`.
1852  pub fn unchecked (mat : Matrix3 <S>) -> Self where S : std::fmt::Debug {
1853    debug_assert!(mat * mat.transposed() == Matrix3::identity());
1854    debug_assert!(mat.determinant() == S::one());
1855    Rotation3 (LinearAuto (LinearIso (mat, PhantomData)))
1856  }
1857  /// It is a debug panic if the given matrix is not orthogonal with determinant +1.
1858  ///
1859  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`
1860  /// (approximately using relative equality).
1861  pub fn unchecked_approx (mat : Matrix3 <S>) -> Self where
1862    S : approx::RelativeEq <Epsilon=S> + std::fmt::Debug
1863  {
1864    if cfg!(debug_assertions) {
1865      let four         = S::one() + S::one() + S::one() + S::one();
1866      let epsilon      = S::default_epsilon() * four;
1867      let max_relative = S::default_max_relative() * four;
1868      approx::assert_relative_eq!(mat * mat.transposed(), Matrix3::identity(),
1869        max_relative=max_relative, epsilon=epsilon);
1870      approx::assert_relative_eq!(mat.determinant(), S::one(),
1871        max_relative=max_relative);
1872    }
1873    Rotation3 (LinearAuto (LinearIso (mat, PhantomData)))
1874  }
1875  /// Construct a rotation around the X axis.
1876  ///
1877  /// Positive angles are counter-clockwise.
1878  pub fn from_angle_x (angle : Rad <S>) -> Self where S : num::real::Real {
1879    Rotation3 (LinearAuto (LinearIso (Matrix3::rotation_x (angle.0), PhantomData)))
1880  }
1881  /// Construct a rotation around the Y axis
1882  ///
1883  /// Positive angles are counter-clockwise.
1884  pub fn from_angle_y (angle : Rad <S>) -> Self where S : num::real::Real {
1885    Rotation3 (LinearAuto (LinearIso (Matrix3::rotation_y (angle.0), PhantomData)))
1886  }
1887  /// Construct a rotation around the Z axis
1888  ///
1889  /// Positive angles are counter-clockwise.
1890  pub fn from_angle_z (angle : Rad <S>) -> Self where S : num::real::Real {
1891    Rotation3 (LinearAuto (LinearIso (Matrix3::rotation_z (angle.0), PhantomData)))
1892  }
1893  /// Construct a rotation from intrinsic ZX'Y'' Euler angles:
1894  ///
1895  /// - yaw: rotation around Z axis
1896  /// - pitch: rotation around X' axis
1897  /// - roll: rotation around Y'' axis
1898  pub fn from_angles_intrinsic (yaw : Rad <S>, pitch : Rad <S>, roll : Rad <S>)
1899    -> Self where S : num::real::Real
1900  {
1901    let rotation1 = Rotation3::from_angle_z (yaw);
1902    let rotation2 = Rotation3::from_axis_angle (rotation1.cols.x, pitch) * rotation1;
1903    Rotation3::from_axis_angle (rotation2.cols.y, roll) * rotation2
1904  }
1905  /// Construct a rotation around the given axis vector with the given angle
1906  pub fn from_axis_angle (axis : Vector3 <S>, angle : Rad <S>) -> Self where
1907    S : num::real::Real
1908  {
1909    Rotation3 (LinearAuto (LinearIso (
1910      Matrix3::rotation_3d (angle.0, axis), PhantomData)))
1911  }
1912  /// Construct an orthonormal matrix from a set of linearly-independent vectors using
1913  /// the Gram-Schmidt process
1914  pub fn orthonormalize (v1 : Vector3 <S>, v2 : Vector3 <S>, v3 : Vector3 <S>)
1915    -> Option <Self> where S : OrderedField + Sqrt
1916  {
1917    if Matrix3::from_col_arrays ([v1, v2, v3].map (Vector3::into_array)).determinant()
1918      == S::zero()
1919    {
1920      None
1921    } else {
1922      let project = |u : Vector3 <S>, v : Vector3 <S>| u * (u.dot (v) / *u.self_dot());
1923      let u1 = v1;
1924      let u2 = v2 - project (u1, v2);
1925      let u3 = v3 - project (u1, v3) - project (u2, v3);
1926      Some (Rotation3 (LinearAuto (LinearIso (Matrix3::from_col_arrays ([
1927        u1.normalize().into_array(),
1928        u2.normalize().into_array(),
1929        u3.normalize().into_array()
1930      ]), PhantomData))))
1931    }
1932  }
1933
1934  /// Returns a rotation with zero roll and oriented towards the target point.
1935  ///
1936  /// Returns the identity rotation if `point` is the origin.
1937  pub fn look_at (point : Point3 <S>) -> Self where
1938    S : num::Float + num::FloatConst + approx::RelativeEq <Epsilon=S> + std::fmt::Debug
1939  {
1940    if point == Point::origin() {
1941      Self::identity()
1942    } else {
1943      use num::Zero;
1944      let forward = point.0.normalized();
1945      let right = {
1946        let projected = forward.with_z (S::zero());
1947        if !projected.is_zero() {
1948          Self::from_angle_z (-Rad (S::FRAC_PI_2())) * projected.normalized()
1949        } else {
1950          Vector3::unit_x()
1951        }
1952      };
1953      let up = right.cross (forward);
1954      let mat =
1955        Matrix3::from_col_arrays ([right, forward, up].map (Vector3::into_array));
1956      Self::noisy_approx (mat)
1957    }
1958  }
1959
1960  /// Return intrinsic yaw (Z), pitch (X'), roll (Y'') angles.
1961  ///
1962  /// NOTE: this function returns the raw output of the `atan2` operations used to
1963  /// compute the angles. This function returns values in the range `[-\pi, \pi]`.
1964  pub fn intrinsic_angles (&self) -> (Rad <S>, Rad <S>, Rad <S>) where S : Real {
1965    let cols  = &self.cols;
1966    let yaw   = Rad (S::atan2 (-cols.y.x, cols.y.y));
1967    let pitch = Rad (S::atan2 (cols.y.z, S::sqrt (S::one() - cols.y.z * cols.y.z)));
1968    let roll  = Rad (S::atan2 (-cols.x.z, cols.z.z));
1969    (yaw, pitch, roll)
1970  }
1971
1972  /// Convert to versor (unit quaternion) representation.
1973  ///
1974  /// <https://stackoverflow.com/a/63745757>
1975  pub fn versor (self) -> Versor <S> where
1976    S : Real + MaybeSerDes + num::real::Real + approx::RelativeEq <Epsilon=S>
1977  {
1978    let diagonal = self.diagonal();
1979    let t = diagonal.sum();
1980    let m =
1981      MinMax::max (MinMax::max (MinMax::max (diagonal.x, diagonal.y), diagonal.z), t);
1982    let qmax  = S::half() * Sqrt::sqrt (S::one() - t + S::two() * m);
1983    let qmax4_recip = (S::four() * qmax).recip();
1984    let [qx, qy, qz, qw] : [S; 4];
1985    let cols = self.cols;
1986    if m == diagonal.x {
1987      qx = qmax;
1988      qy = qmax4_recip * (cols.x.y + cols.y.x);
1989      qz = qmax4_recip * (cols.x.z + cols.z.x);
1990      qw = -qmax4_recip * (cols.z.y - cols.y.z);
1991    } else if m == diagonal.y {
1992      qx = qmax4_recip * (cols.x.y + cols.y.x);
1993      qy = qmax;
1994      qz = qmax4_recip * (cols.y.z + cols.z.y);
1995      qw = -qmax4_recip * (cols.x.z - cols.z.x);
1996    } else if m == diagonal.z {
1997      qx = qmax4_recip * (cols.x.z + cols.z.x);
1998      qy = qmax4_recip * (cols.y.z + cols.z.y);
1999      qz = qmax;
2000      qw = -qmax4_recip * (cols.y.x - cols.x.y);
2001    } else {
2002      qx = -qmax4_recip * (cols.z.y - cols.y.z);
2003      qy = -qmax4_recip * (cols.x.z - cols.z.x);
2004      qz = -qmax4_recip * (cols.y.x - cols.x.y);
2005      qw = qmax;
2006    }
2007    let quat = Quaternion::from_xyzw (qx, qy, qz, qw);
2008    Versor::unchecked_approx (quat)
2009  }
2010  /// Get the underlying matrix
2011  pub const fn mat (self) -> Matrix3 <S> {
2012    self.0.0.0
2013  }
2014  /// Rotates a point around the origin
2015  pub fn rotate (self, point : Point3 <S>) -> Point3 <S> {
2016    Point3::from (self * point.0)
2017  }
2018  /// Rotate around a center point
2019  pub fn rotate_around (self, point : Point3 <S>, center : Point3 <S>) -> Point3 <S> {
2020    self.rotate (point - center.0) + center.0
2021  }
2022}
2023impl <S> std::ops::Deref for Rotation3 <S> where S : Ring + MaybeSerDes {
2024  type Target = LinearAuto <S, Vector3 <S>>;
2025  fn deref (&self) -> &Self::Target {
2026    &self.0
2027  }
2028}
2029impl <S> num::Inv for Rotation3 <S> where S : Ring + MaybeSerDes {
2030  type Output = Self;
2031  fn inv (self) -> Self::Output {
2032    Rotation3 (LinearAuto (LinearIso (self.0.0.0.transpose(), PhantomData)))
2033  }
2034}
2035impl <S> std::ops::Mul <Vector3 <S>> for Rotation3 <S> where S : Ring + MaybeSerDes {
2036  type Output = Vector3 <S>;
2037  fn mul (self, rhs : Vector3 <S>) -> Self::Output {
2038    self.0 * rhs
2039  }
2040}
2041impl <S> std::ops::Mul <NonZero3 <S>> for Rotation3 <S> where S : Ring + MaybeSerDes {
2042  type Output = NonZero3 <S>;
2043  fn mul (self, rhs : NonZero3 <S>) -> Self::Output {
2044    NonZero3 (self.0 * rhs.0)
2045  }
2046}
2047impl <S> std::ops::Mul <Unit3 <S>> for Rotation3 <S> where S : Ring + MaybeSerDes {
2048  type Output = Unit3 <S>;
2049  fn mul (self, rhs : Unit3 <S>) -> Self::Output {
2050    Unit3 (self.0 * rhs.0)
2051  }
2052}
2053impl <S> std::ops::Mul <Rotation3 <S>> for Vector3 <S> where S : Ring + MaybeSerDes {
2054  type Output = Vector3 <S>;
2055  fn mul (self, rhs : Rotation3 <S>) -> Self::Output {
2056    self * rhs.0
2057  }
2058}
2059impl <S> std::ops::Mul for Rotation3 <S> where S : Ring + MaybeSerDes {
2060  type Output = Self;
2061  fn mul (self, rhs : Self) -> Self::Output {
2062    Rotation3 (self.0 * rhs.0)
2063  }
2064}
2065impl <S> std::ops::MulAssign <Self> for Rotation3 <S> where S : Ring + MaybeSerDes {
2066  fn mul_assign (&mut self, rhs : Self) {
2067    self.0 *= rhs.0
2068  }
2069}
2070impl <S> std::ops::Div for Rotation3 <S> where S : Ring + MaybeSerDes {
2071  type Output = Self;
2072  #[expect(clippy::suspicious_arithmetic_impl)]
2073  fn div (self, rhs : Self) -> Self::Output {
2074    use num::Inv;
2075    self * rhs.inv()
2076  }
2077}
2078impl <S> std::ops::DivAssign <Self> for Rotation3 <S> where S : Ring + MaybeSerDes {
2079  #[expect(clippy::suspicious_op_assign_impl)]
2080  fn div_assign (&mut self, rhs : Self) {
2081    use num::Inv;
2082    *self *= rhs.inv()
2083  }
2084}
2085impl <S> num::One for Rotation3 <S> where S : Ring + MaybeSerDes + num::Zero + num::One {
2086  fn one() -> Self {
2087    Rotation3 (LinearAuto (LinearIso (Matrix3::identity(), PhantomData)))
2088  }
2089}
2090impl <S> From <Angles3 <S>> for Rotation3 <S> where
2091  S : Real + num::real::Real + MaybeSerDes
2092{
2093  fn from (angles : Angles3 <S>) -> Self {
2094    Rotation3::from_angles_intrinsic (
2095      angles.yaw.angle(), angles.pitch.angle(), angles.roll.angle())
2096  }
2097}
2098
2099//
2100//  impl Versor
2101//
2102impl <S : Real> Versor <S> {
2103  /// Normalizes the given quaternion.
2104  ///
2105  /// Panics if the zero quaternion is given.
2106  pub fn normalize (quaternion : Quaternion <S>) -> Self where S : std::fmt::Debug {
2107    assert_ne!(quaternion, Quaternion::zero());
2108    Versor (normalize_quaternion (quaternion))
2109  }
2110  /// Panic if the given quaternion is not a unit quaternion.
2111  ///
2112  /// This method checks whether `quaternion == quaternion.normalized()`.
2113  pub fn noisy (unit_quaternion : Quaternion <S>) -> Self where
2114    S : num::real::Real + std::fmt::Debug
2115  {
2116    assert_eq!(unit_quaternion, normalize_quaternion (unit_quaternion));
2117    Versor (unit_quaternion)
2118  }
2119  /// Panic if the given quaternion is not a unit quaternion.
2120  ///
2121  /// Checks if the magnitude is approximately one.
2122  pub fn noisy_approx (unit_quaternion : Quaternion <S>) -> Self where
2123    S : num::real::Real + approx::RelativeEq <Epsilon=S>
2124  {
2125    assert!(unit_quaternion.into_vec4().is_normalized());
2126    Versor (unit_quaternion)
2127  }
2128  /// It is a debug panic if the given quaternion is not a unit quaternion.
2129  ///
2130  /// This method checks whether `quaternion == quaternion.normalized()`.
2131  pub fn unchecked (unit_quaternion : Quaternion <S>) -> Self where S : std::fmt::Debug {
2132    debug_assert_eq!(unit_quaternion, normalize_quaternion (unit_quaternion));
2133    Versor (unit_quaternion)
2134  }
2135  /// It is a debug panic if the given quaternion is not a unit quaternion.
2136  ///
2137  /// Checks if the magnitude is approximately one.
2138  pub fn unchecked_approx (unit_quaternion : Quaternion <S>) -> Self where
2139    S : num::real::Real + approx::RelativeEq <Epsilon=S>
2140  {
2141    debug_assert!(unit_quaternion.into_vec4().is_normalized());
2142    Versor (unit_quaternion)
2143  }
2144  /// Constructs the rotation using the direction and magnitude of the given vector
2145  // TODO: work around rotation_3d Float constraint
2146  pub fn from_scaled_axis (scaled_axis : Vector3 <S>) -> Self where
2147    S : std::fmt::Debug + num::Float
2148  {
2149    if scaled_axis == Vector3::zero() {
2150      let versor = Quaternion::rotation_3d (S::zero(), scaled_axis);
2151      debug_assert_eq!(versor.magnitude(), S::one());
2152      Versor (versor)
2153    } else {
2154      let angle = scaled_axis.magnitude();
2155      debug_assert!(S::zero() < angle);
2156      let axis  = scaled_axis / angle;
2157      Versor (Quaternion::rotation_3d (angle, axis))
2158    }
2159  }
2160}
2161impl <S> Default for Versor <S> where S : num::One + num::Zero {
2162  fn default() -> Self {
2163    Versor (Quaternion::default())
2164  }
2165}
2166impl <S> std::ops::Mul <Vector3 <S>> for Versor <S> where S : Real + num::Float {
2167  type Output = Vector3 <S>;
2168  fn mul (self, rhs : Vector3 <S>) -> Self::Output {
2169    self.0 * rhs
2170  }
2171}
2172impl <S> From <Rotation3 <S>> for Versor <S> where
2173  S : Real + MaybeSerDes + num::real::Real + approx::RelativeEq <Epsilon=S>
2174{
2175  fn from (rot : Rotation3 <S>) -> Self {
2176    rot.versor()
2177  }
2178}
2179impl <S> std::ops::Deref for Versor <S> {
2180  type Target = Quaternion <S>;
2181  fn deref (&self) -> &Quaternion <S> {
2182    &self.0
2183  }
2184}
2185//  end impl Versor
2186
2187//
2188//  impl LinearIso
2189//
2190impl <S, V, W, M> LinearIso <S, V, W, M> where
2191  M : LinearMap <S, V, W> + Copy,
2192  V : Module <S>,
2193  W : Module <S>,
2194  S : Ring
2195{
2196  pub fn mat (self) -> M {
2197    *self
2198  }
2199  /// Checks whether the determinant is non-zero
2200  pub fn is_invertible (linear_map : M) -> bool {
2201    linear_map.determinant() != S::zero()
2202  }
2203  /// Checks whether the determinant is non-zero with approximate equality
2204  pub fn is_invertible_approx (linear_map : M, epsilon : Option <S>) -> bool where
2205    S : approx::AbsDiffEq <Epsilon=S>
2206  {
2207    let epsilon = epsilon.unwrap_or_else (||{
2208      let four = S::one() + S::one() + S::one() + S::one();
2209      S::default_epsilon() * four
2210    });
2211    approx::abs_diff_ne!(linear_map.determinant(), S::zero(), epsilon=epsilon)
2212  }
2213  /// Checks if map is invertible
2214  pub fn new (linear_map : M) -> Option <Self> {
2215    if Self::is_invertible (linear_map) {
2216      Some (LinearIso (linear_map, PhantomData))
2217    } else {
2218      None
2219    }
2220  }
2221  /// Checks if map is invertible with approximate equality
2222  pub fn new_approx (linear_map : M, epsilon : Option <S>) -> Option <Self> where
2223    S : approx::RelativeEq <Epsilon=S>
2224  {
2225    if Self::is_invertible_approx (linear_map, epsilon) {
2226      Some (LinearIso (linear_map, PhantomData))
2227    } else {
2228      None
2229    }
2230  }
2231}
2232impl <S, V, W, M> std::ops::Deref for LinearIso <S, V, W, M> where
2233  M : LinearMap <S, V, W>,
2234  V : Module <S>,
2235  W : Module <S>,
2236  S : Ring
2237{
2238  type Target = M;
2239  fn deref (&self) -> &Self::Target {
2240    &self.0
2241  }
2242}
2243impl <S, V> num::One for LinearIso <S, V, V, V::LinearEndo> where
2244  V : Module <S>,
2245  S : Ring
2246{
2247  fn one () -> Self {
2248    LinearIso (V::LinearEndo::one(), PhantomData)
2249  }
2250}
2251impl <S, V, W, M> std::ops::Mul <V> for LinearIso <S, V, W, M> where
2252  M : LinearMap <S, V, W>,
2253  V : Module <S>,
2254  W : Module <S>,
2255  S : Ring
2256{
2257  type Output = W;
2258  fn mul (self, rhs : V) -> Self::Output {
2259    self.0 * rhs
2260  }
2261}
2262impl <S, V> std::ops::Mul for LinearIso <S, V, V, V::LinearEndo> where
2263  V : Module <S>,
2264  S : Ring
2265{
2266  type Output = Self;
2267  fn mul (self, rhs : Self) -> Self::Output {
2268    LinearIso (self.0 * rhs.0, PhantomData)
2269  }
2270}
2271impl <S, V> std::ops::MulAssign for LinearIso <S, V, V, V::LinearEndo> where
2272  V : Module <S>,
2273  S : Ring
2274{
2275  fn mul_assign (&mut self, rhs : Self) {
2276    self.0 *= rhs.0
2277  }
2278}
2279
2280//
2281//  impl LinearAuto
2282//
2283impl <S, V> LinearAuto <S, V> where
2284  V : Module <S>,
2285  V::LinearEndo : MaybeSerDes,
2286  S : Ring
2287{
2288  pub fn mat (self) -> V::LinearEndo {
2289    **self
2290  }
2291  /// Returns false if called with a matrix that is not orthogonal with determinant
2292  /// +/-1.
2293  ///
2294  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == +/-1`.
2295  pub fn is_orthonormal (&self) -> bool {
2296    use num::One;
2297    let determinant = self.0.0.determinant();
2298    self.0.0 * self.0.0.transpose() == V::LinearEndo::one() &&
2299    (determinant == S::one() || determinant == -S::one())
2300  }
2301  /// Returns `None` if called with a matrix that is not orthogonal with determinant
2302  /// +/-1.
2303  ///
2304  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == +/-1`
2305  /// (approximately using relative equality).
2306  pub fn is_orthonormal_approx (&self) -> bool where
2307    S : approx::RelativeEq <Epsilon=S>,
2308    V::LinearEndo : approx::RelativeEq <Epsilon=S>
2309  {
2310    use num::One;
2311    let four         = S::one() + S::one() + S::one() + S::one();
2312    let epsilon      = S::default_epsilon() * four;
2313    let max_relative = S::default_max_relative() * four;
2314    let determinant  = self.0.0.determinant();
2315    approx::relative_eq!(self.0.0 * self.0.0.transpose(), V::LinearEndo::one(),
2316      max_relative=max_relative, epsilon=epsilon) &&
2317    ( approx::relative_eq!(determinant,  S::one(), max_relative=max_relative) ||
2318      approx::relative_eq!(determinant, -S::one(), max_relative=max_relative) )
2319  }
2320  /// Checks whether the transformation is a special orthogonal matrix.
2321  ///
2322  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`.
2323  pub fn is_rotation (&self) -> bool {
2324    use num::One;
2325    let determinant = self.0.0.determinant();
2326    self.0.0 * self.0.0.transpose() == V::LinearEndo::one() &&
2327    determinant == S::one()
2328  }
2329  /// Checks whether the transformation is a special orthogonal matrix.
2330  ///
2331  /// This method checks whether `mat * mat^T == I` and `mat.determinant() == 1`
2332  /// (approximately using relative equality).
2333  pub fn is_rotation_approx (&self) -> bool where
2334    S : approx::RelativeEq <Epsilon=S>,
2335    V::LinearEndo : approx::RelativeEq <Epsilon=S>
2336  {
2337    use num::One;
2338    let four         = S::one() + S::one() + S::one() + S::one();
2339    let epsilon      = S::default_epsilon() * four;
2340    let max_relative = S::default_max_relative() * four;
2341    let determinant  = self.0.0.determinant();
2342    approx::relative_eq!(self.0.0 * self.0.0.transpose(), V::LinearEndo::one(),
2343      max_relative=max_relative, epsilon=epsilon) &&
2344    approx::relative_eq!(determinant,  S::one(), max_relative=max_relative)
2345  }
2346}
2347impl <S, V> std::ops::Mul <V> for LinearAuto <S, V> where
2348  V : Module <S>,
2349  V::LinearEndo : MaybeSerDes,
2350  S : Ring
2351{
2352  type Output = V;
2353  fn mul (self, rhs : V) -> Self::Output {
2354    self.0 * rhs
2355  }
2356}
2357impl <S, V> std::ops::Mul for LinearAuto <S, V> where
2358  V : Module <S>,
2359  V::LinearEndo : MaybeSerDes,
2360  S : Ring
2361{
2362  type Output = Self;
2363  fn mul (self, rhs : Self) -> Self::Output {
2364    LinearAuto (self.0 * rhs.0)
2365  }
2366}
2367impl <S, V> std::ops::MulAssign <Self> for LinearAuto <S, V> where
2368  V : Module <S>,
2369  V::LinearEndo : MaybeSerDes,
2370  S : Ring
2371{
2372  fn mul_assign (&mut self, rhs : Self) {
2373    self.0 *= rhs.0
2374  }
2375}
2376impl <S, V> num::One for LinearAuto <S, V> where
2377  V : Module <S>,
2378  V::LinearEndo : MaybeSerDes,
2379  S : Ring
2380{
2381  fn one() -> Self {
2382    LinearAuto (LinearIso::one())
2383  }
2384}
2385
2386//
2387//  impl AffineMap
2388//
2389impl <S, A, B, M> AffineMap <S, A, B, M> where
2390  A : AffineSpace <S>,
2391  B : AffineSpace <S>,
2392  M : LinearMap <S, A::Translation, B::Translation>,
2393  S : Field
2394{
2395  pub const fn new (linear_map : M, translation : B::Translation) -> Self {
2396    AffineMap { linear_map, translation, _phantom: PhantomData }
2397  }
2398  pub fn map (self, point : A) -> B {
2399    B::from_vector (self.linear_map * point.to_vector() + self.translation)
2400  }
2401  pub fn transform (self, translation : A::Translation) -> B::Translation {
2402    self.linear_map * translation
2403  }
2404}
2405
2406//
2407//  impl Affinity
2408//
2409impl <S, A, B, M> Affinity <S, A, B, M> where
2410  M : LinearMap <S, A::Translation, B::Translation>,
2411  A : AffineSpace <S>,
2412  B : AffineSpace <S>,
2413  S : Field
2414{
2415  pub const fn new (
2416    linear_iso  : LinearIso <S, A::Translation, B::Translation, M>,
2417    translation : B::Translation
2418  ) -> Self {
2419    Affinity { linear_iso, translation }
2420  }
2421  pub fn mat (self) -> M {
2422    *self.linear_iso
2423  }
2424  pub fn map (self, point : A) -> B {
2425    B::from_vector (self.linear_iso * point.to_vector() + self.translation)
2426  }
2427  pub fn transform (self, translation : A::Translation) -> B::Translation {
2428    self.linear_iso * translation
2429  }
2430}
2431
2432//
2433//  impl Projectivity
2434//
2435impl <S, P, Q, M> Projectivity <S, P, Q, M> where
2436  M : LinearMap <S, P::Translation, Q::Translation>,
2437  P : ProjectiveSpace <S>,
2438  Q : ProjectiveSpace <S>,
2439  S : Field
2440{
2441  pub const fn new (linear_iso : LinearIso <S, P::Translation, Q::Translation, M>)
2442    -> Self
2443  {
2444    Projectivity (linear_iso)
2445  }
2446  pub fn mat (self) -> M {
2447    **self
2448  }
2449}
2450impl <S, P, Q, M> std::ops::Deref for Projectivity <S, P, Q, M> where
2451  M : LinearMap <S, P::Translation, Q::Translation>,
2452  P : ProjectiveSpace <S>,
2453  Q : ProjectiveSpace <S>,
2454  S : Field
2455{
2456  type Target = LinearIso <S, P::Translation, Q::Translation, M>;
2457  fn deref (&self) -> &Self::Target {
2458    &self.0
2459  }
2460}
2461impl <S, P, Q, M> std::ops::Mul <P> for Projectivity <S, P, Q, M> where
2462  M : LinearMap <S, P::Translation, Q::Translation>,
2463  P : ProjectiveSpace <S>,
2464  Q : ProjectiveSpace <S>,
2465  S : Field
2466{
2467  type Output = Q;
2468  fn mul (self, rhs : P) -> Q {
2469    Q::from_vector (self.0 * rhs.to_vector())
2470  }
2471}
2472
2473pub macro point {
2474  ($x:expr, $y:expr) => {
2475    $crate::point2 ($x, $y)
2476  },
2477  ($x:expr, $y:expr, $z:expr) => {
2478    $crate::point3 ($x, $y, $z)
2479  },
2480  ($x:expr, $y:expr, $z:expr, $w:expr) => {
2481    $crate::point4 ($x, $y, $z, $w)
2482  }
2483}
2484
2485pub macro vector {
2486  ($x:expr, $y:expr) => {
2487    $crate::vector2 ($x, $y)
2488  },
2489  ($x:expr, $y:expr, $z:expr) => {
2490    $crate::vector3 ($x, $y, $z)
2491  },
2492  ($x:expr, $y:expr, $z:expr, $w:expr) => {
2493    $crate::vector4 ($x, $y, $z, $w)
2494  }
2495}
2496
2497pub macro matrix {
2498  ($v00:expr, $v01:expr; $v10:expr, $v11:expr$(;)?) => {
2499    $crate::Matrix2::new ($v00, $v01, $v10, $v11)
2500  },
2501  ( $v00:expr, $v01:expr, $v02:expr;
2502    $v10:expr, $v11:expr, $v12:expr;
2503    $v20:expr, $v21:expr, $v22:expr$(;)?
2504  ) => {
2505    $crate::Matrix3::new ($v00, $v01, $v02, $v10, $v11, $v12, $v20, $v21, $v22)
2506  },
2507  ( $v00:expr, $v01:expr, $v02:expr, $v03:expr;
2508    $v10:expr, $v11:expr, $v12:expr, $v13:expr;
2509    $v20:expr, $v21:expr, $v22:expr, $v23:expr;
2510    $v30:expr, $v31:expr, $v32:expr, $v33:expr$(;)?
2511  ) => {
2512    $crate::Matrix4::new (
2513      $v00, $v01, $v02, $v03,
2514      $v10, $v11, $v12, $v13,
2515      $v20, $v21, $v22, $v23,
2516      $v30, $v31, $v32, $v33)
2517  }
2518}
2519
2520macro_rules! impl_angle {
2521  ( $angle:ident, $docname:expr ) => {
2522    #[doc = $docname]
2523    #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
2524    // TODO: derive From here causes a test failure in the intrinsic angles test
2525    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd)]
2526    pub struct $angle <S> (pub S);
2527    impl_numcast_primitive_map!($angle);
2528    impl <S : Ring> std::iter::Sum for $angle <S> {
2529      fn sum <I> (iter : I) -> Self where I : Iterator <Item = Self> {
2530        use num::Zero;
2531        iter.fold ($angle::zero(), |acc, item| acc + item)
2532      }
2533    }
2534    impl <S : Ring> std::ops::Neg for $angle <S> {
2535      type Output = Self;
2536      fn neg (self) -> Self {
2537        $angle (-self.0)
2538      }
2539    }
2540    impl <S : Ring> std::ops::Add for $angle <S> {
2541      type Output = Self;
2542      fn add (self, other : Self) -> Self::Output {
2543        $angle (self.0 + other.0)
2544      }
2545    }
2546    impl <S : Ring> std::ops::AddAssign for $angle <S> {
2547      fn add_assign (&mut self, other : Self) {
2548        self.0 += other.0
2549      }
2550    }
2551    impl <S : Ring> std::ops::Sub for $angle <S> {
2552      type Output = Self;
2553      fn sub (self, other : Self) -> Self::Output {
2554        $angle (self.0 - other.0)
2555      }
2556    }
2557    impl <S : Ring> std::ops::SubAssign for $angle <S> {
2558      fn sub_assign (&mut self, other : Self) {
2559        self.0 -= other.0
2560      }
2561    }
2562    impl <S : Ring> std::ops::Mul <S> for $angle <S> {
2563      type Output = Self;
2564      fn mul (self, scalar : S) -> Self::Output {
2565        $angle (scalar * self.0)
2566      }
2567    }
2568    impl <S : Ring> std::ops::MulAssign <S> for $angle <S> {
2569      fn mul_assign (&mut self, scalar : S) {
2570        self.0 *= scalar
2571      }
2572    }
2573    impl <S : Field> std::ops::Div for $angle <S> {
2574      type Output = S;
2575      fn div (self, other : Self) -> Self::Output {
2576        self.0 / other.0
2577      }
2578    }
2579    impl <S : Field> std::ops::Div <S> for $angle <S> {
2580      type Output = Self;
2581      fn div (self, scalar : S) -> Self::Output {
2582        $angle (self.0 / scalar)
2583      }
2584    }
2585    impl <S : Field> std::ops::DivAssign <S> for $angle <S> {
2586      fn div_assign (&mut self, scalar : S) {
2587        self.0 /= scalar
2588      }
2589    }
2590    impl <S : OrderedField> std::ops::Rem for $angle <S> {
2591      type Output = Self;
2592      fn rem (self, other : Self) -> Self::Output {
2593        $angle (self.0 % other.0)
2594      }
2595    }
2596    impl <S : Ring> num::Zero for $angle <S> {
2597      fn zero() -> Self {
2598        $angle (S::zero())
2599      }
2600      fn is_zero (&self) -> bool {
2601        self.0.is_zero()
2602      }
2603    }
2604    impl <S : Ring> From <S> for $angle <S> {
2605      fn from (s : S) -> Self {
2606        $angle (s)
2607      }
2608    }
2609  }
2610}
2611
2612macro_rules! impl_angle_wrapped {
2613  ( $angle:ident, $comment:expr ) => {
2614    #[doc = $comment]
2615    #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
2616    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd)]
2617    pub struct $angle <S> (Rad <S>);
2618
2619    impl_numcast!($angle);
2620    impl <S : Copy> $angle <S> {
2621      pub const fn angle (&self) -> Rad <S> {
2622        self.0
2623      }
2624    }
2625    impl <S : Real> std::iter::Sum for $angle <S> {
2626      fn sum <I> (iter : I) -> Self where I : Iterator <Item = Self> {
2627        use num::Zero;
2628        iter.fold ($angle::zero(), |acc, item| acc + item)
2629      }
2630    }
2631    impl <S : Real> std::ops::Neg for $angle <S> {
2632      type Output = Self;
2633      fn neg (self) -> Self {
2634        self.map (Rad::neg)
2635      }
2636    }
2637    impl <S : Real> std::ops::Add for $angle <S> {
2638      type Output = Self;
2639      fn add (self, other : Self) -> Self::Output {
2640        self.map (|angle| angle + other.0)
2641      }
2642    }
2643    impl <S : Real> std::ops::AddAssign for $angle <S> {
2644      fn add_assign (&mut self, other : Self) {
2645        *self = *self + other
2646      }
2647    }
2648    impl <S : Real> std::ops::Add <Rad <S>> for $angle <S> {
2649      type Output = Self;
2650      fn add (self, angle : Rad <S>) -> Self::Output {
2651        self.map (|a| a + angle)
2652      }
2653    }
2654    impl <S : Real> std::ops::AddAssign <Rad <S>> for $angle <S> {
2655      fn add_assign (&mut self, angle : Rad <S>) {
2656        *self = *self + angle
2657      }
2658    }
2659    impl <S : Real> std::ops::Sub for $angle <S> {
2660      type Output = Self;
2661      fn sub (self, other : Self) -> Self::Output {
2662        self.map (|angle| angle - other.0)
2663      }
2664    }
2665    impl <S : Real> std::ops::SubAssign for $angle <S> {
2666      fn sub_assign (&mut self, other : Self) {
2667        *self = *self - other
2668      }
2669    }
2670    impl <S : Real> std::ops::Sub <Rad <S>> for $angle <S> {
2671      type Output = Self;
2672      fn sub (self, angle : Rad <S>) -> Self::Output {
2673        self.map (|a| a - angle)
2674      }
2675    }
2676    impl <S : Real> std::ops::SubAssign <Rad <S>> for $angle <S> {
2677      fn sub_assign (&mut self, angle : Rad <S>) {
2678        *self = *self - angle
2679      }
2680    }
2681    impl <S : Real> std::ops::Mul <S> for $angle <S> {
2682      type Output = Self;
2683      fn mul (self, scalar : S) -> Self::Output {
2684        self.map (|angle| angle * scalar)
2685      }
2686    }
2687    impl <S : Real> std::ops::MulAssign <S> for $angle <S> {
2688      fn mul_assign (&mut self, scalar : S) {
2689        *self = *self * scalar
2690      }
2691    }
2692    impl <S : Real> std::ops::Div for $angle <S> {
2693      type Output = S;
2694      fn div (self, other : Self) -> Self::Output {
2695        self.0 / other.0
2696      }
2697    }
2698    impl <S : Real> std::ops::Div <S> for $angle <S> {
2699      type Output = Self;
2700      fn div (self, scalar : S) -> Self::Output {
2701        self.map (|angle| angle / scalar)
2702      }
2703    }
2704    impl <S : Real> std::ops::DivAssign <S> for $angle <S> {
2705      fn div_assign (&mut self, scalar : S) {
2706        *self = *self / scalar
2707      }
2708    }
2709    impl <S : Real> std::ops::Rem for $angle <S> {
2710      type Output = Self;
2711      fn rem (self, other : Self) -> Self::Output {
2712        self.map (|angle| angle % other.0)
2713      }
2714    }
2715    impl <S : Real> num::Zero for $angle <S> {
2716      fn zero() -> Self {
2717        $angle (Rad::zero())
2718      }
2719      fn is_zero (&self) -> bool {
2720        self.0.is_zero()
2721      }
2722    }
2723  }
2724}
2725
2726macro_rules! impl_dimension {
2727  ( $point:ident, $vector:ident, $nonzero:ident, $unit:ident,
2728    $point_fun:ident, $vector_fun:ident, $matrix_fun:ident, $ortho_fun:ident,
2729    [$($component:ident),+],
2730    [$(($axis_method:ident, $unit_method:ident)),+], [$($patch:ident)?],
2731    [$($projective:ident)?],
2732    $matrix:ident, [$($submatrix:ident)?], $ndims:expr, $dimension:expr
2733  ) => {
2734    #[doc = $dimension]
2735    #[doc = "position"]
2736    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Display, From, Neg)]
2737    #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
2738    #[display("{}", _0)]
2739    #[repr(C)]
2740    pub struct $point <S> (pub $vector <S>);
2741
2742    #[doc = $dimension]
2743    #[doc = "non-zero vector"]
2744    #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
2745    #[derive(Clone, Copy, Debug, Eq, PartialEq, Display)]
2746    #[display("{}", _0)]
2747    #[repr(C)]
2748    pub struct $nonzero <S> ($vector <S>);
2749
2750    #[doc = $dimension]
2751    #[doc = "unit vector"]
2752    #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
2753    #[derive(Clone, Copy, Debug, Eq, PartialEq, Display)]
2754    #[display("{}", _0)]
2755    #[repr(C)]
2756    pub struct $unit <S> ($vector <S>);
2757
2758    //
2759    //  impl PointN
2760    //
2761    #[doc = "Construct a"]
2762    #[doc = $dimension]
2763    #[doc = "point"]
2764    pub const fn $point_fun <S> ($($component : S),+) -> $point <S> {
2765      $point::new ($($component),+)
2766    }
2767    impl_numcast!($point);
2768    impl <S> $point <S> {
2769      pub const fn new ($($component : S),+) -> Self {
2770        $point ($vector::new ($($component),+))
2771      }
2772      $(
2773      pub const fn $component (&self) -> S where S : Copy {
2774        self.0.$component
2775      }
2776      )+
2777      pub fn distance (self, other : $point <S>) -> NonNegative <S> where
2778        S : OrderedField + Sqrt
2779      {
2780        MetricSpace::distance (self.0, other.0)
2781      }
2782      pub fn distance_squared (self, other : $point <S>) -> NonNegative <S> where
2783        S : OrderedField
2784      {
2785        MetricSpace::distance_squared (self.0, other.0)
2786      }
2787    }
2788    // projective completion
2789    $(
2790    impl <S : Field> ProjectiveSpace <S> for $projective <S> {
2791      type Patch = $point <S>;
2792    }
2793    )?
2794    impl <S : Field> AffineSpace <S> for $point <S> {
2795      type Translation = $vector <S>;
2796    }
2797    impl <S : Ring> Point <$vector <S>> for $point <S> {
2798      fn to_vector (self) -> $vector <S> {
2799        self.0
2800      }
2801      fn from_vector (vector : $vector <S>) -> Self {
2802        $point (vector)
2803      }
2804    }
2805    impl <S> From <[S; $ndims]> for $point <S> {
2806      fn from (array : [S; $ndims]) -> Self {
2807        $point (array.into())
2808      }
2809    }
2810    $(
2811    impl <S> From <($patch <S>, S)> for $point <S> {
2812      fn from ((patch, scale) : ($patch <S>, S)) -> Self {
2813        $point ((patch.0, scale).into())
2814      }
2815    }
2816    )?
2817    impl <S> std::cmp::PartialOrd for $point <S> where S : PartialOrd {
2818      fn partial_cmp (&self, other : &Self) -> Option <std::cmp::Ordering> {
2819        self.0.as_slice().partial_cmp (&other.0.as_slice())
2820      }
2821    }
2822    impl <S> std::ops::Add <$vector <S>> for $point <S> where S : AdditiveGroup {
2823      type Output = Self;
2824      fn add (self, displacement : $vector <S>) -> Self::Output {
2825        $point (self.0 + displacement)
2826      }
2827    }
2828    impl <S> std::ops::Add <&$vector <S>> for $point <S> where S : AdditiveGroup + Copy {
2829      type Output = Self;
2830      fn add (self, displacement : &$vector <S>) -> Self::Output {
2831        $point (self.0 + *displacement)
2832      }
2833    }
2834    impl <S> std::ops::Add <$vector <S>> for &$point <S> where S : AdditiveGroup + Copy {
2835      type Output = $point <S>;
2836      fn add (self, displacement : $vector <S>) -> Self::Output {
2837        $point (self.0 + displacement)
2838      }
2839    }
2840    impl <S> std::ops::Add <&$vector <S>> for &$point <S> where
2841      S : AdditiveGroup + Copy
2842    {
2843      type Output = $point <S>;
2844      fn add (self, displacement : &$vector <S>) -> Self::Output {
2845        $point (self.0 + *displacement)
2846      }
2847    }
2848    impl <S> std::ops::AddAssign <$vector <S>> for $point <S> where S : AdditiveGroup {
2849      fn add_assign (&mut self, displacement : $vector <S>) {
2850        self.0 += displacement
2851      }
2852    }
2853    impl <S> std::ops::AddAssign <&$vector <S>> for $point <S> where
2854      S : AdditiveGroup + Copy
2855    {
2856      fn add_assign (&mut self, displacement : &$vector <S>) {
2857        self.0 += *displacement
2858      }
2859    }
2860    impl <S> std::ops::Sub <$vector <S>> for $point <S> where S : AdditiveGroup {
2861      type Output = Self;
2862      fn sub (self, displacement : $vector <S>) -> Self::Output {
2863        $point (self.0 - displacement)
2864      }
2865    }
2866    impl <S> std::ops::Sub <&$vector <S>> for $point <S> where S : AdditiveGroup + Copy {
2867      type Output = Self;
2868      fn sub (self, displacement : &$vector <S>) -> Self::Output {
2869        $point (self.0 - *displacement)
2870      }
2871    }
2872    impl <S> std::ops::Sub <$point <S>> for $point <S> where S : AdditiveGroup {
2873      type Output = $vector <S>;
2874      fn sub (self, other : Self) -> Self::Output {
2875        self.0 - other.0
2876      }
2877    }
2878    impl <S> std::ops::Sub <&$point <S>> for $point <S> where S : AdditiveGroup + Copy {
2879      type Output = $vector <S>;
2880      fn sub (self, other : &Self) -> Self::Output {
2881        self.0 - other.0
2882      }
2883    }
2884    impl <S> std::ops::Sub <$vector <S>> for &$point <S> where S : AdditiveGroup + Copy {
2885      type Output = $point <S>;
2886      fn sub (self, displacement : $vector <S>) -> Self::Output {
2887        $point (self.0 - displacement)
2888      }
2889    }
2890    impl <S> std::ops::Sub <&$vector <S>> for &$point <S> where
2891      S : AdditiveGroup + Copy
2892    {
2893      type Output = $point <S>;
2894      fn sub (self, displacement : &$vector <S>) -> Self::Output {
2895        $point (self.0 - *displacement)
2896      }
2897    }
2898    impl <S> std::ops::Sub <$point <S>> for &$point <S> where S : AdditiveGroup + Copy {
2899      type Output = $vector <S>;
2900      fn sub (self, other : $point <S>) -> Self::Output {
2901        self.0 - other.0
2902      }
2903    }
2904    impl <S> std::ops::Sub <&$point <S>> for &$point <S> where S : AdditiveGroup + Copy {
2905      type Output = $vector <S>;
2906      fn sub (self, other : &$point <S>) -> Self::Output {
2907        self.0 - other.0
2908      }
2909    }
2910    impl <S> std::ops::SubAssign <$vector <S>> for $point <S> where
2911      S : AdditiveGroup
2912    {
2913      fn sub_assign (&mut self, displacement : $vector <S>) {
2914        self.0 -= displacement
2915      }
2916    }
2917    impl <S> std::ops::SubAssign <&$vector <S>> for $point <S> where
2918      S : AdditiveGroup + Copy
2919    {
2920      fn sub_assign (&mut self, displacement : &$vector <S>) {
2921        self.0 -= *displacement
2922      }
2923    }
2924    impl <S> approx::AbsDiffEq for $point <S> where
2925      S : approx::AbsDiffEq,
2926      S::Epsilon : Copy
2927    {
2928      type Epsilon = S::Epsilon;
2929      fn default_epsilon() -> Self::Epsilon {
2930        S::default_epsilon()
2931      }
2932      #[inline]
2933      fn abs_diff_eq (&self, other : &Self, epsilon : Self::Epsilon) -> bool {
2934        self.0.abs_diff_eq (&other.0, epsilon)
2935      }
2936    }
2937    impl <S> approx::RelativeEq for $point <S> where
2938      S : approx::RelativeEq,
2939      S::Epsilon : Copy
2940    {
2941      fn default_max_relative() -> Self::Epsilon {
2942        S::default_max_relative()
2943      }
2944      #[inline]
2945      fn relative_eq (&self,
2946        other : &Self, epsilon : Self::Epsilon, max_relative : Self::Epsilon
2947      ) -> bool {
2948        self.0.relative_eq (&other.0, epsilon, max_relative)
2949      }
2950    }
2951    impl <S> approx::UlpsEq for $point <S> where
2952      S : approx::UlpsEq,
2953      S::Epsilon : Copy
2954    {
2955      fn default_max_ulps() -> u32 {
2956        S::default_max_ulps()
2957      }
2958      #[inline]
2959      fn ulps_eq (&self, other : &Self, epsilon : Self::Epsilon, max_ulps : u32)
2960        -> bool
2961      {
2962        self.0.ulps_eq (&other.0, epsilon, max_ulps)
2963      }
2964    }
2965    //
2966    //  impl VectorN
2967    //
2968    #[doc = "Construct a"]
2969    #[doc = $dimension]
2970    #[doc = "vector"]
2971    pub const fn $vector_fun <S> ($($component : S),+) -> $vector <S> {
2972      $vector::new ($($component),+)
2973    }
2974    impl <S> NormedVectorSpace <S> for $vector <S> where S : OrderedField {
2975      type Unit = $unit <S>;
2976      fn norm_squared (self) -> NonNegative <S> {
2977        self.self_dot()
2978      }
2979      fn norm_max (self) -> NonNegative <S> where S : SignedExt {
2980        NonNegative::unchecked (self.map (|x| x.abs()).reduce_partial_max())
2981      }
2982      fn normalize (self) -> Self::Unit where S : Sqrt {
2983        $unit (self / *self.norm())
2984      }
2985    }
2986    impl <S> InnerProductSpace <S> for $vector <S> where S : Field {
2987      fn outer_product (self, rhs : Self) -> $matrix <S> {
2988        let mut cols : $vector <$vector <S>> = [$vector::zero(); $ndims].into();
2989        for col in 0..$ndims {
2990          for row in 0..$ndims {
2991            cols[col][row] = self[row] * rhs[col];
2992          }
2993        }
2994        $matrix { cols }
2995      }
2996      fn orthogonal (self) -> Self where S : approx::AbsDiffEq <Epsilon = S> {
2997        $ortho_fun (self)
2998      }
2999    }
3000    impl <S : Ring> Dot <S> for $vector <S> {
3001      fn dot (self, other : Self) -> S {
3002        self.dot (other)
3003      }
3004    }
3005    impl <S : Field> VectorSpace <S> for $vector <S> {
3006      type NonZero = $nonzero <S>;
3007      fn map <F> (self, f : F) -> Self where F : FnMut (S) -> S {
3008        self.map (f)
3009      }
3010    }
3011    impl <S> Module <S> for $vector <S> where S : Ring + num::MulAdd {
3012      type LinearEndo = $matrix <S>;
3013    }
3014    impl <S> GroupAction <$point <S>> for $vector <S> where S : AdditiveGroup {
3015      fn action (self, point : $point <S>) -> $point <S> {
3016        point + self
3017      }
3018    }
3019    impl <S : AdditiveGroup> Group for $vector <S> {
3020      fn identity() -> Self {
3021        Self::zero()
3022      }
3023      fn operation (a : Self, b : Self) -> Self {
3024        a + b
3025      }
3026    }
3027    impl <S> std::ops::Mul <LinearAuto <S, Self>> for $vector <S> where
3028      S : Ring + MaybeSerDes
3029    {
3030      type Output = Self;
3031      fn mul (self, rhs : LinearAuto <S, Self>) -> Self::Output {
3032        self * rhs.0
3033      }
3034    }
3035    impl <S> std::ops::Mul <LinearIso <S, Self, Self, $matrix <S>>> for $vector <S> where
3036      S : Ring + MaybeSerDes
3037    {
3038      type Output = Self;
3039      fn mul (self, rhs : LinearIso <S, Self, Self, $matrix <S>>) -> Self::Output {
3040        self * rhs.0
3041      }
3042    }
3043    impl <S> num::Inv for LinearAuto <S, $vector <S>> where
3044      S : Ring + num::real::Real + MaybeSerDes
3045    {
3046      type Output = Self;
3047      fn inv (self) -> Self::Output {
3048        LinearAuto (self.0.inv())
3049      }
3050    }
3051    impl <S> std::ops::Div for LinearAuto <S, $vector <S>> where
3052      S : Ring + num::Float + MaybeSerDes
3053    {
3054      type Output = Self;
3055      fn div (self, rhs : Self) -> Self::Output {
3056        LinearAuto (self.0 / rhs.0)
3057      }
3058    }
3059    impl <S> std::ops::DivAssign <Self> for LinearAuto <S, $vector <S>> where
3060      S : Ring + num::Float + MaybeSerDes
3061    {
3062      fn div_assign (&mut self, rhs : Self) {
3063        self.0 /= rhs.0
3064      }
3065    }
3066    impl <S> From <$unit <S>> for $vector <S> where S : Copy {
3067      fn from (unit : $unit <S>) -> Self {
3068        *unit
3069      }
3070    }
3071    //
3072    //  impl MatrixN
3073    //
3074    #[doc = "Construct a"]
3075    #[doc = $dimension]
3076    #[doc = "matrix"]
3077    pub const fn $matrix_fun <S> ($($component : $vector <S>),+) -> $matrix <S> {
3078      $matrix {
3079        cols: $vector_fun ($($component),+)
3080      }
3081    }
3082    impl <S, V, W> num::Inv for LinearIso <S, V, W, $matrix <S>> where
3083      V : Module <S>,
3084      W : Module <S>,
3085      S : Ring + num::real::Real
3086    {
3087      type Output = LinearIso <S, W, V, $matrix <S>>;
3088      fn inv (self) -> Self::Output {
3089        // TODO: currently the vek crate only implements invert for Mat4
3090        let mat4 = Matrix4::from (self.0);
3091        LinearIso (mat4.inverted().into(), PhantomData::default())
3092      }
3093    }
3094    impl <S, V> std::ops::Div for LinearIso <S, V, V, $matrix <S>> where
3095      V : Module <S, LinearEndo=$matrix <S>>,
3096      S : Ring + num::real::Real
3097    {
3098      type Output = Self;
3099      #[expect(clippy::suspicious_arithmetic_impl)]
3100      fn div (self, rhs : Self) -> Self::Output {
3101        use num::Inv;
3102        self * rhs.inv()
3103      }
3104    }
3105    impl <S, V> std::ops::DivAssign for LinearIso <S, V, V, $matrix <S>> where
3106      V : Module <S, LinearEndo=$matrix <S>>,
3107      S : Ring + num::real::Real
3108    {
3109      #[expect(clippy::suspicious_op_assign_impl)]
3110      fn div_assign (&mut self, rhs : Self) {
3111        use num::Inv;
3112        *self *= rhs.inv()
3113      }
3114    }
3115    $(
3116    impl <S : Copy> Matrix <S> for $matrix <S> {
3117      type Rows = $vector <$vector <S>>;
3118      type Submatrix = $submatrix <S>;
3119      fn rows (self) -> $vector <$vector <S>> {
3120        self.into_row_arrays().map ($vector::from).into()
3121      }
3122      fn submatrix (self, i : usize, j : usize) -> $submatrix <S> {
3123        let a : [S; ($ndims-1) * ($ndims-1)] = std::array::from_fn (|index|{
3124          let i = (i + 1 + index / ($ndims-1)) % $ndims;
3125          let j = (j + 1 + index % ($ndims-1)) % $ndims;
3126          self.cols[i][j]
3127        });
3128        $submatrix::from_col_array (a)
3129      }
3130      /// Fill additional row and column with zeros
3131      fn fill_zeros (submatrix : $submatrix <S>) -> Self where S : num::Zero{
3132        let cols = $vector::from (
3133          std::array::from_fn (|i| if i < $ndims-1 {
3134            $vector::from (submatrix.cols[i])
3135          } else {
3136            $vector::zero()
3137          })
3138        );
3139        $matrix { cols }
3140      }
3141    }
3142    )?
3143    impl <S : Ring> LinearMap <S, $vector <S>, $vector <S>> for $matrix <S> {
3144      fn determinant (self) -> S {
3145        self.determinant()
3146      }
3147      fn transpose (self) -> Self {
3148        self.transposed()
3149      }
3150    }
3151    //
3152    //  impl NonZeroN
3153    //
3154    impl_numcast!($nonzero);
3155    impl <S> $nonzero <S> {
3156      /// Returns 'None' if called with the zero vector
3157      // TODO: move broken doctests to unit tests
3158      // # Example
3159      //
3160      // ```
3161      // # use math_utils::$nonzero;
3162      // assert!($nonzero::new ([1.0, 0.0].into()).is_some());
3163      // assert!($nonzero::new ([0.0, 0.0].into()).is_none());
3164      // ```
3165      pub fn new (vector : $vector <S>) -> Option <Self> where S : Ring {
3166        use num::Zero;
3167        if !vector.is_zero() {
3168          Some ($nonzero (vector))
3169        } else {
3170          None
3171        }
3172      }
3173      /// Panics if zero vector is given.
3174      // TODO: move broken doctests to unit tests
3175      // # Panics
3176      //
3177      // ```should_panic
3178      // # use math_utils::$nonzero;
3179      // let x = $nonzero::noisy ([0.0, 0.0].into());  // panic!
3180      // ```
3181      pub fn noisy (vector : $vector <S>) -> Self where S : Ring {
3182        use num::Zero;
3183        assert!(!vector.is_zero());
3184        $nonzero (vector)
3185      }
3186      /// It is a debug assertion if the given vector is zero.
3187      ///
3188      /// In debug builds, method checks that `vector != Zero::zero()`.
3189      #[inline]
3190      pub fn unchecked (vector : $vector <S>) -> Self where S : Ring + std::fmt::Debug {
3191        debug_assert_ne!(vector, $vector::zero());
3192        $nonzero (vector)
3193      }
3194      /// Map an operation on the underlying vector, panicking if the result is zero
3195      // TODO: move broken doctests to unit tests
3196      // # Panics
3197      //
3198      // Panics of the result is zero:
3199      //
3200      // ```should_panic
3201      // # use math_utils::$nonzero;
3202      // let v = $nonzero::noisy ([1.0, 1.0].into())
3203      //   .map_noisy (|x| 0.0 * x);  // panic!
3204      // ```
3205      #[must_use]
3206      pub fn map_noisy (self, fun : fn ($vector <S>) -> $vector <S>) -> Self
3207        where S : Ring
3208      {
3209        Self::noisy (fun (self.0))
3210      }
3211      #[must_use]
3212      pub fn map_unchecked (self, fun : fn ($vector <S>) -> $vector <S>) -> Self
3213        where S : Ring + std::fmt::Debug
3214      {
3215        Self::unchecked (fun (self.0))
3216      }
3217    }
3218    impl <S> std::ops::Deref for $nonzero <S> {
3219      type Target = $vector <S>;
3220      fn deref (&self) -> &$vector <S> {
3221        &self.0
3222      }
3223    }
3224    impl <S : Ring> std::ops::Neg for $nonzero <S> {
3225      type Output = Self;
3226      fn neg (self) -> Self {
3227        $nonzero (-self.0)
3228      }
3229    }
3230    impl <S> From <$unit <S>> for $nonzero <S> where S : Ring + std::fmt::Debug {
3231      fn from (unit : $unit <S>) -> Self {
3232        $nonzero::unchecked (*unit)
3233      }
3234    }
3235
3236    //
3237    //  impl UnitN
3238    //
3239    impl_numcast!($unit);
3240    impl <S> $unit <S> {
3241      // TODO: move broken doc tests to unit tests
3242      // ```
3243      // # use math_utils::$unit;
3244      // assert_eq!($unit::axis_x(), $unit::new ([1.0, 0.0].into()).unwrap());
3245      // ```
3246      $(
3247      #[inline]
3248      pub fn $axis_method() -> Self where S : num::One + num::Zero {
3249        $unit ($vector::$unit_method())
3250      }
3251      )+
3252      /// Returns 'None' if called with a non-normalized vector.
3253      ///
3254      /// This method checks whether `vector == vector.normalize()`.
3255      pub fn new (vector : $vector <S>) -> Option <Self> where S : OrderedField + Sqrt {
3256        if vector == *vector.normalize() {
3257          Some ($unit (vector))
3258        } else {
3259          None
3260        }
3261      }
3262      /// Returns 'None' if called with an non-normalized vector determined by checking
3263      /// if the magnitude is approximately one.
3264      ///
3265      /// Note the required `RelativeEq` trait is only implemented for floating-point
3266      /// types.
3267      // TODO: implement required methods to avoid num::real::Real constraint
3268      pub fn new_approx (vector : $vector <S>) -> Option <Self> where
3269        S : num::real::Real + approx::RelativeEq <Epsilon=S>
3270      {
3271        if vector.is_normalized() {
3272          Some ($unit (vector))
3273        } else {
3274          None
3275        }
3276      }
3277      /// Normalizes a given non-zero vector.
3278      // TODO: move broken doc tests to unit tests
3279      // # Example
3280      //
3281      // ```
3282      // # use math_utils::$unit;
3283      // assert_eq!(
3284      //   *$unit::normalize ([2.0, 0.0].into()),
3285      //   [1.0, 0.0].into()
3286      // );
3287      // ```
3288      //
3289      // # Panics
3290      //
3291      // Panics if the zero vector is given:
3292      //
3293      // ```should_panic
3294      // # use math_utils::$unit;
3295      // let x = $unit::normalize ([0.0, 0.0].into());  // panic!
3296      // ```
3297      pub fn normalize (vector : $vector <S>) -> Self where S : OrderedField + Sqrt {
3298        use num::Zero;
3299        assert!(!vector.is_zero());
3300        vector.normalize()
3301      }
3302      /// Normalizes a given non-zero vector only if the vector is not already
3303      /// normalized.
3304      // TODO: implement required methods to avoid num::real::Real constraint
3305      pub fn normalize_approx (vector : $vector <S>) -> Self where
3306        S : num::real::Real + approx::RelativeEq <Epsilon=S>
3307      {
3308        use num::Zero;
3309        assert!(!vector.is_zero());
3310        let vector = if vector.is_normalized() {
3311          vector
3312        } else {
3313          vector.normalized()
3314        };
3315        $unit (vector)
3316      }
3317      /// Panics if a non-normalized vector is given.
3318      ///
3319      /// This method checks whether `vector == vector.normalize()`.
3320      // TODO: move broken doc tests to unit tests
3321      // ```should_panic
3322      // # use math_utils::$unit;
3323      // let x = $unit::noisy ([2.0, 0.0].into());  // panic!
3324      // ```
3325      pub fn noisy (vector : $vector <S>) -> Self where
3326        S : OrderedField + Sqrt + std::fmt::Debug
3327      {
3328        assert_eq!(vector, *vector.normalize());
3329        $unit (vector)
3330      }
3331      /// Panics if a non-normalized vector is given.
3332      ///
3333      /// Checks if the magnitude is approximately one.
3334      // TODO: move broken doc tests to unit tests
3335      // ```should_panic
3336      // # use math_utils::$unit;
3337      // let x = $unit::noisy ([2.0, 0.0].into());  // panic!
3338      // ```
3339      pub fn noisy_approx (vector : $vector <S>) -> Self where
3340        S : num::real::Real + approx::RelativeEq <Epsilon=S>
3341      {
3342        assert!(vector.is_normalized());
3343        $unit (vector)
3344      }
3345      /// It is a debug assertion if the given vector is not normalized.
3346      ///
3347      /// In debug builds, method checks that `vector == vector.normalize()`.
3348      #[inline]
3349      pub fn unchecked (vector : $vector <S>) -> Self where
3350        S : OrderedField + Sqrt + std::fmt::Debug
3351      {
3352        debug_assert_eq!(vector, *vector.normalize());
3353        $unit (vector)
3354      }
3355      /// It is a debug assertion if the given vector is not normalized.
3356      ///
3357      /// In debug builds, checks if the magnitude is approximately one.
3358      // TODO: move broken doc tests to unit tests
3359      // ```should_panic
3360      // # use math_utils::$unit;
3361      // let n = $unit::unchecked ([2.0, 0.0].into());  // panic!
3362      // ```
3363      // TODO: implement required methods to avoid num::real::Real constraint
3364      #[inline]
3365      pub fn unchecked_approx (vector : $vector <S>) -> Self where
3366        S : num::real::Real + approx::RelativeEq <Epsilon=S>
3367      {
3368        debug_assert!(vector.is_normalized());
3369        $unit (vector)
3370      }
3371      /// Generate a random unit vector. Uses naive algorithm.
3372      pub fn random_unit <R : rand::RngExt> (rng : &mut R) -> Self where
3373        S : OrderedField + Sqrt + rand::distr::uniform::SampleUniform
3374      {
3375        let vector = $vector {
3376          $($component: rng.random_range (-S::one()..S::one())),+
3377        };
3378        vector.normalize()
3379      }
3380      /// Return the unit vector pointing in the opposite direction
3381      // TODO: implement num::Inv ?
3382      pub fn invert (mut self) -> Self where S : Ring {
3383        self.0 = -self.0;
3384        self
3385      }
3386      /// Map an operation on the underlying vector, panicking if the result is zero.
3387      ///
3388      /// # Panics
3389      ///
3390      /// Panics of the result is not normalized.
3391      // TODO: move broken doc tests to unit tests
3392      // ```should_panic
3393      // # use math_utils::$unit;
3394      // // panic!
3395      // let v = $unit::noisy ([1.0, 0.0].into()).map_noisy (|x| 2.0 * x);
3396      // ```
3397      #[must_use]
3398      pub fn map_noisy (self, fun : fn ($vector <S>) -> $vector <S>) -> Self where
3399        S : OrderedField + Sqrt + std::fmt::Debug
3400      {
3401        Self::noisy (fun (self.0))
3402      }
3403      /// Map an operation on the underlying vector, panicking if the result is zero.
3404      ///
3405      /// # Panics
3406      ///
3407      /// Panics of the result is not normalized.
3408      // TODO: move broken doc tests to unit tests
3409      // ```should_panic
3410      // # use math_utils::$unit;
3411      // // panic!
3412      // let v = $unit::noisy ([1.0, 0.0].into()).map_noisy (|x| 2.0 * x);
3413      // ```
3414      #[must_use]
3415      pub fn map_noisy_approx (self, fun : fn ($vector <S>) -> $vector <S>) -> Self where
3416        S : num::real::Real + approx::RelativeEq <Epsilon=S>
3417      {
3418        Self::noisy_approx (fun (self.0))
3419      }
3420    }
3421    impl <S> std::ops::Deref for $unit <S> {
3422      type Target = $vector <S>;
3423      fn deref (&self) -> &$vector <S> {
3424        &self.0
3425      }
3426    }
3427    impl <S : Ring> std::ops::Neg for $unit <S> {
3428      type Output = Self;
3429      fn neg (self) -> Self {
3430        $unit (-self.0)
3431      }
3432    }
3433    //  end UnitN
3434  }
3435}
3436
3437macro_rules! impl_numcast {
3438  ($type:ident) => {
3439    impl <S> $type <S> {
3440      #[inline]
3441      pub fn numcast <T> (self) -> Option <$type <T>> where
3442        S : num::NumCast,
3443        T : num::NumCast
3444      {
3445        self.0.numcast().map ($type)
3446      }
3447    }
3448  }
3449}
3450
3451macro_rules! impl_numcast_primitive {
3452  ($type:ident) => {
3453    impl <S> $type <S> {
3454      #[inline]
3455      pub fn numcast <T> (self) -> Option <$type <T>> where
3456        S : num::NumCast,
3457        T : num::NumCast
3458      {
3459        T::from (self.0).map ($type)
3460      }
3461    }
3462    impl <S> num::ToPrimitive for $type <S> where S : num::ToPrimitive {
3463      fn to_isize (&self) -> Option <isize> {
3464        self.0.to_isize()
3465      }
3466      fn to_i8 (&self) -> Option <i8> {
3467        self.0.to_i8()
3468      }
3469      fn to_i16 (&self) -> Option <i16> {
3470        self.0.to_i16()
3471      }
3472      fn to_i32 (&self) -> Option <i32> {
3473        self.0.to_i32()
3474      }
3475      fn to_i64 (&self) -> Option <i64> {
3476        self.0.to_i64()
3477      }
3478      fn to_usize (&self) -> Option <usize> {
3479        self.0.to_usize()
3480      }
3481      fn to_u8 (&self) -> Option <u8> {
3482        self.0.to_u8()
3483      }
3484      fn to_u16 (&self) -> Option <u16> {
3485        self.0.to_u16()
3486      }
3487      fn to_u32 (&self) -> Option <u32> {
3488        self.0.to_u32()
3489      }
3490      fn to_u64 (&self) -> Option <u64> {
3491        self.0.to_u64()
3492      }
3493    }
3494  }
3495}
3496
3497macro_rules! impl_numcast_primitive_option {
3498  ($type:ident$(, $bound:path)*) => {
3499    impl_numcast_primitive!{$type}
3500    impl <S> num::NumCast for $type <S> where
3501      S : num::NumCast $(+ $bound)*
3502    {
3503      fn from <T : num::ToPrimitive> (n : T) -> Option <$type <S>> {
3504        S::from (n).and_then ($type::new)
3505      }
3506    }
3507  }
3508}
3509
3510macro_rules! impl_numcast_primitive_map {
3511  ($type:ident) => {
3512    impl_numcast_primitive!{$type}
3513    impl <S> num::NumCast for $type <S> where S : num::NumCast {
3514      fn from <T : num::ToPrimitive> (n : T) -> Option <$type <S>> {
3515        S::from (n).map ($type)
3516      }
3517    }
3518  }
3519}
3520
3521#[doc(hidden)]
3522#[macro_export]
3523macro_rules! impl_numcast_fields {
3524  ($type:ident, $($field:ident),+) => {
3525    impl <S> $type <S> {
3526      #[inline]
3527      pub fn numcast <T> (self) -> Option <$type <T>> where
3528        S : num::NumCast + PartialOrd + num::Zero,
3529        T : num::NumCast + PartialOrd + num::Zero
3530      {
3531        Some ($type {
3532          $($field: self.$field.numcast()?),+
3533        })
3534      }
3535    }
3536  }
3537}
3538
3539impl_angle!(Deg, "Degrees");
3540impl_angle!(Rad, "Radians");
3541impl_angle!(Turn, "Turns");
3542impl_angle_wrapped!(AngleWrapped, "Unsigned wrapped angle restricted to $[0, 2\\pi)$");
3543impl_angle_wrapped!(AngleWrappedSigned,
3544  "Signed wrapped angle restricted to $(-\\pi, \\pi]$");
3545
3546impl_dimension!(Point2, Vector2, NonZero2, Unit2, point2, vector2, matrix2, ortho_vec2,
3547  [x, y], [(axis_x, unit_x), (axis_y, unit_y)],
3548  [], [Point3], Matrix2, [], 2, "2D");
3549impl_dimension!(Point3, Vector3, NonZero3, Unit3, point3, vector3, matrix3, ortho_vec3,
3550  [x, y, z], [(axis_x, unit_x), (axis_y, unit_y), (axis_z, unit_z)],
3551  [Point2], [Point4], Matrix3, [Matrix2], 3, "3D");
3552impl_dimension!(Point4, Vector4, NonZero4, Unit4, point4, vector4, matrix4, ortho_vec4,
3553  [x, y, z, w], [(axis_x, unit_x), (axis_y, unit_y), (axis_z, unit_z), (axis_w, unit_w)],
3554  [Point3], [], Matrix4, [Matrix3], 4, "4D");
3555
3556impl_numcast_primitive_option!(Positive, PartialOrd, num::Zero);
3557impl_numcast_primitive_option!(NonNegative, PartialOrd, num::Zero);
3558impl_numcast_primitive_option!(NonZero, PartialEq, num::Zero);
3559impl_numcast_primitive_option!(Normalized, PartialOrd, num::One, num::Zero);
3560impl_numcast_primitive_option!(NormalSigned, OrderedRing);
3561impl_numcast_fields!(Angles3, yaw, pitch, roll);
3562
3563//
3564//  private
3565//
3566#[inline]
3567fn normalize_quaternion <S : Real> (quaternion : Quaternion <S>) -> Quaternion <S> {
3568  Quaternion::from_vec4 (*quaternion.into_vec4().normalize())
3569}
3570
3571#[cfg(test)]
3572mod tests {
3573  extern crate test;
3574
3575  use super::*;
3576  use crate::num;
3577  use approx;
3578  use rand;
3579  use rand_distr;
3580  use rand_xorshift;
3581
3582  #[expect(clippy::cast_possible_truncation)]
3583  static ORTHO_VEC_BENCH_SEED : std::sync::LazyLock <u64> = std::sync::LazyLock::new (
3584    || std::time::SystemTime::UNIX_EPOCH.elapsed().unwrap().as_nanos() as u64);
3585
3586  #[test]
3587  fn defaults() {
3588    use num::Zero;
3589
3590    assert_eq!(Positive::<f32>::default().0, 0.0);
3591    assert_eq!(NonNegative::<f32>::default().0, 0.0);
3592    assert_eq!(Normalized::<f32>::default().0, 0.0);
3593    assert_eq!(NormalSigned::<f32>::default().0, 0.0);
3594    assert_eq!(Deg::<f32>::default().0, 0.0);
3595    assert_eq!(Rad::<f32>::default().0, 0.0);
3596    assert_eq!(Turn::<f32>::default().0, 0.0);
3597    assert_eq!(
3598      Angles3::<f32>::default(),
3599      Angles3::new (AngleWrapped::zero(), AngleWrapped::zero(), AngleWrapped::zero()));
3600    assert_eq!(
3601      Pose3::<f32>::default(),
3602      Pose3 { position: Point3::origin(), angles: Angles3::default() });
3603    assert_eq!(
3604      LinearIso::<f32, Vector3 <f32>, Vector3 <f32>, Matrix3 <f32>>::default().0,
3605      Matrix3::identity());
3606    assert_eq!(LinearAuto::<f32, Vector3 <f32>>::default().0.0, Matrix3::identity());
3607    assert_eq!(Rotation2::<f32>::default().0.0.0, Matrix2::identity());
3608    assert_eq!(Rotation3::<f32>::default().0.0.0, Matrix3::identity());
3609    assert_eq!(Versor::<f32>::default().0, Quaternion::from_xyzw (0.0, 0.0, 0.0, 1.0));
3610    assert_eq!(
3611      AffineMap::<f32, Point3 <f32>, Point3 <f32>, Matrix3 <f32>>::default(),
3612      AffineMap::new (Matrix3::identity(), Vector3::zero()));
3613    assert_eq!(
3614      Affinity::<f32, Point3 <f32>, Point3 <f32>, Matrix3 <f32>>::default(),
3615      Affinity::new (LinearIso::new (Matrix3::identity()).unwrap(), Vector3::zero()));
3616    assert_eq!(
3617      Projectivity::<f32, Point4 <f32>, Point4 <f32>, Matrix4 <f32>>::default().0,
3618      LinearIso::new (Matrix4::identity()).unwrap());
3619  }
3620
3621  #[test]
3622  fn vector_sum() {
3623    let s = [
3624      [0.0, 1.0],
3625      [1.0, 1.0],
3626      [0.5, 0.5]
3627    ].map (Vector2::<f32>::from);
3628    let _sum = s.iter().copied().sum::<Vector2 <f32>>();
3629  }
3630
3631  #[test]
3632  fn vector_outer_product() {
3633    let v1 = vector3 (1.0, 2.0, 3.0);
3634    let v2 = vector3 (2.0, 3.0, 4.0);
3635    assert_eq!(v1.outer_product (v2).into_row_arrays(), [
3636      [2.0, 3.0,  4.0],
3637      [4.0, 6.0,  8.0],
3638      [6.0, 9.0, 12.0]
3639    ]);
3640  }
3641
3642  #[test]
3643  fn rotation3_noisy_approx() {
3644    use rand::{RngExt, SeedableRng};
3645    // construct orthonormal matrices and verify they are orthonormal using noisy_approx
3646    // constructor
3647    let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (0);
3648    let up = Vector3::unit_z();
3649    for _ in 0..1000 {
3650      let forward = vector3 (
3651        rng.random_range (-1000.0f32..1000.0),
3652        rng.random_range (-1000.0f32..1000.0),
3653        0.0
3654      ).normalized();
3655      let right = forward.cross (up);
3656      let mat = Matrix3::from_col_arrays ([
3657        right.into_array(),
3658        forward.into_array(),
3659        up.into_array()
3660      ]);
3661      let _ = Rotation3::noisy_approx (mat);
3662    }
3663  }
3664
3665  #[test]
3666  fn rotation3_intrinsic_angles() {
3667    use std::f32::consts::PI;
3668    use num::Zero;
3669    use rand::{RngExt, SeedableRng};
3670    // yaw: CCW quarter turn around Z (world up) axis
3671    let rot = Rotation3::from_angles_intrinsic (
3672      Turn (0.25).into(), Rad::zero(), Rad::zero());
3673    approx::assert_relative_eq!(rot.mat(),
3674      Matrix3::from_col_arrays ([
3675        [ 0.0, 1.0, 0.0],
3676        [-1.0, 0.0, 0.0],
3677        [ 0.0, 0.0, 1.0]
3678      ])
3679    );
3680    assert_eq!((Turn (0.25).into(), Rad::zero(), Rad::zero()), rot.intrinsic_angles());
3681    let rot = Rotation3::from_angles_intrinsic (
3682      Turn (0.5).into(), Rad::zero(), Rad::zero());
3683    approx::assert_relative_eq!(rot.mat(),
3684      Matrix3::from_col_arrays ([
3685        [-1.0,  0.0, 0.0],
3686        [ 0.0, -1.0, 0.0],
3687        [ 0.0,  0.0, 1.0]
3688      ])
3689    );
3690    assert_eq!((Turn (0.5).into(), Rad::zero(), Rad::zero()), rot.intrinsic_angles());
3691    // pitch: CCW quarter turn around X (world right) axis
3692    let rot = Rotation3::from_angles_intrinsic (
3693      Rad::zero(), Turn (0.25).into(), Rad::zero());
3694    approx::assert_relative_eq!(rot.mat(),
3695      Matrix3::from_col_arrays ([
3696        [1.0,  0.0, 0.0],
3697        [0.0,  0.0, 1.0],
3698        [0.0, -1.0, 0.0]
3699      ])
3700    );
3701    assert_eq!((Rad::zero(), Turn (0.25).into(), Rad::zero()), rot.intrinsic_angles());
3702    // roll: CCW quarter turn around Y (world forward) axis
3703    let rot = Rotation3::from_angles_intrinsic (
3704      Rad::zero(), Rad::zero(), Turn (0.25).into());
3705    approx::assert_relative_eq!(rot.mat(),
3706      Matrix3::from_col_arrays ([
3707        [0.0, 0.0, -1.0],
3708        [0.0, 1.0,  0.0],
3709        [1.0, 0.0,  0.0]
3710      ])
3711    );
3712    assert_eq!((Rad::zero(), Rad::zero(), Turn (0.25).into()), rot.intrinsic_angles());
3713    // pitch after yaw
3714    let rot = Rotation3::from_angles_intrinsic (
3715      Turn (0.25).into(), Turn (0.25).into(), Rad::zero());
3716    approx::assert_relative_eq!(rot.mat(),
3717      Matrix3::from_col_arrays ([
3718        [0.0, 1.0, 0.0],
3719        [0.0, 0.0, 1.0],
3720        [1.0, 0.0, 0.0]
3721      ])
3722    );
3723    let angles = rot.intrinsic_angles();
3724    approx::assert_relative_eq!(Rad::from (Turn (0.25)).0, angles.0.0);
3725    approx::assert_relative_eq!(Rad::from (Turn (0.25)).0, angles.1.0);
3726    approx::assert_relative_eq!(0.0, angles.2.0);
3727    // roll after pitch after yaw
3728    let rot = Rotation3::from_angles_intrinsic (
3729      Turn (0.25).into(), Turn (0.25).into(), Turn (0.25).into());
3730    approx::assert_relative_eq!(rot.mat(),
3731      Matrix3::from_col_arrays ([
3732        [-1.0, 0.0, 0.0],
3733        [ 0.0, 0.0, 1.0],
3734        [ 0.0, 1.0, 0.0]
3735      ])
3736    );
3737    let angles = rot.intrinsic_angles();
3738    approx::assert_relative_eq!(Rad::from (Turn (0.25)).0, angles.0.0);
3739    approx::assert_relative_eq!(Rad::from (Turn (0.25)).0, angles.1.0);
3740    approx::assert_relative_eq!(Rad::from (Turn (0.25)).0, angles.2.0);
3741
3742    // construct random orthonormal matrices and verify the intrinsic angles are in the
3743    // range [-pi, pi]
3744    let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (0);
3745    let mut random_vector = || vector3 (
3746      rng.random_range (-100.0f32..100.0),
3747      rng.random_range (-100.0f32..100.0),
3748      rng.random_range (-100.0f32..100.0));
3749    for _ in 0..1000 {
3750      let rot = Rotation3::orthonormalize (
3751        random_vector(), random_vector(), random_vector()
3752      ).unwrap();
3753      let (yaw, pitch, roll) = rot.intrinsic_angles();
3754      assert!(yaw.0   <  PI);
3755      assert!(yaw.0   > -PI);
3756      assert!(pitch.0 <  PI);
3757      assert!(pitch.0 > -PI);
3758      assert!(roll.0  <  PI);
3759      assert!(roll.0  > -PI);
3760    }
3761  }
3762
3763  #[test]
3764  fn rotation3_into_versor() {
3765    use std::f32::consts::PI;
3766    let rot  = Rotation3::from_angle_x (Rad (0.01));
3767    let quat = rot.versor().0;
3768    approx::assert_relative_eq!(rot.mat(), quat.into());
3769    let rot  = Rotation3::from_angle_x (Rad (-0.01));
3770    let quat = rot.versor().0;
3771    approx::assert_relative_eq!(rot.mat(), quat.into());
3772
3773    let rot  = Rotation3::from_angle_y (Rad (0.01));
3774    let quat = rot.versor().0;
3775    approx::assert_relative_eq!(rot.mat(), quat.into());
3776    let rot  = Rotation3::from_angle_y (Rad (-0.01));
3777    let quat = rot.versor().0;
3778    approx::assert_relative_eq!(rot.mat(), quat.into());
3779
3780    let rot  = Rotation3::from_angle_z (Rad (0.01));
3781    let quat = rot.versor().0;
3782    approx::assert_relative_eq!(rot.mat(), quat.into());
3783    let rot  = Rotation3::from_angle_z (Rad (-0.01));
3784    let quat = rot.versor().0;
3785    approx::assert_relative_eq!(rot.mat(), quat.into());
3786
3787    let rot  = Rotation3::from_angle_x (Rad (PI / 2.0));
3788    let quat = rot.versor().0;
3789    approx::assert_relative_eq!(rot.mat(), quat.into());
3790    let rot  = Rotation3::from_angle_x (Rad (-PI / 2.0));
3791    let quat = rot.versor().0;
3792    approx::assert_relative_eq!(rot.mat(), quat.into());
3793
3794    let rot  = Rotation3::from_angle_y (Rad (PI / 2.0));
3795    let quat = rot.versor().0;
3796    approx::assert_relative_eq!(rot.mat(), quat.into());
3797    let rot  = Rotation3::from_angle_y (Rad (-PI / 2.0));
3798    let quat = rot.versor().0;
3799    approx::assert_relative_eq!(rot.mat(), quat.into());
3800
3801    let rot  = Rotation3::from_angle_z (Rad (PI / 2.0));
3802    let quat = rot.versor().0;
3803    approx::assert_relative_eq!(rot.mat(), quat.into());
3804    let rot  = Rotation3::from_angle_z (Rad (-PI / 2.0));
3805    let quat = rot.versor().0;
3806    approx::assert_relative_eq!(rot.mat(), quat.into());
3807  }
3808
3809  // this method bencharked faster than the sign method but slower than the copysign
3810  // method (by about 5-10%); the copysign method is branch-free but relies on
3811  // floating-point copysign method
3812  #[bench]
3813  fn bench_orthogonal_cross_product (b : &mut test::Bencher) {
3814    use approx::AbsDiffEq;
3815    use rand::{RngExt, SeedableRng};
3816    use rand_distr::Distribution;
3817    let epsilon = f64::default_epsilon() * 256.0;
3818    let std_normal = rand_distr::StandardNormal;
3819    println!("ORTHO_VEC_BENCH_SEED: {}", *ORTHO_VEC_BENCH_SEED);
3820    let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (*ORTHO_VEC_BENCH_SEED);
3821    let randf = |rng : &mut rand_xorshift::XorShiftRng| {
3822      let x : f64 = std_normal.sample (rng);
3823      let y : f64 = std_normal.sample (rng);
3824      x / y
3825    };
3826    b.iter(||{
3827      let vec : Vector3 <f64> = match rng.random_range (0..=10) {
3828        0..=4 => vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng)),
3829        5     => vector3 (randf (&mut rng), 0.0, 0.0),
3830        6     => vector3 (0.0, randf (&mut rng), 0.0),
3831        7     => vector3 (0.0, 0.0, randf (&mut rng)),
3832        8     => vector3 (0.0, randf (&mut rng), randf (&mut rng)),
3833        9     => vector3 (randf (&mut rng), 0.0, randf (&mut rng)),
3834        10    => vector3 (randf (&mut rng), randf (&mut rng), 0.0),
3835        _     => unreachable!()
3836      };
3837      approx::assert_abs_diff_ne!(vec, Vector3::zero(), epsilon = epsilon);
3838      let mut ortho = vec.cross (Vector3::unit_x());
3839      if approx::abs_diff_eq!(ortho, Vector3::zero(), epsilon = epsilon) {
3840        ortho = vec.cross (Vector3::unit_y())
3841      }
3842      if approx::abs_diff_eq!(ortho, Vector3::zero(), epsilon = epsilon) ||
3843        approx::abs_diff_ne!(vec.dot (ortho), 0.0, epsilon = epsilon)
3844      {
3845        println!("VEC:   {vec}");
3846        println!("ORTHO: {ortho}");
3847        println!("DOT:   {}", vec.dot (ortho));
3848      }
3849      approx::assert_abs_diff_ne!(ortho, Vector3::zero(), epsilon = epsilon);
3850      approx::assert_abs_diff_eq!(vec.dot (ortho), 0.0, epsilon = epsilon);
3851    });
3852  }
3853
3854  // method by Ken Whatmough: https://math.stackexchange.com/a/4112622
3855  // this method turned out to be the fastest in benchmarks, however it relies on the
3856  // floating-point copysign function
3857  #[bench]
3858  fn bench_orthogonal_copysign (b : &mut test::Bencher) {
3859    use approx::AbsDiffEq;
3860    use rand::{RngExt, SeedableRng};
3861    use rand_distr::Distribution;
3862    let epsilon = f64::default_epsilon() * 2.0.powi (32);
3863    let std_normal = rand_distr::StandardNormal;
3864    println!("ORTHO_VEC_BENCH_SEED: {}", *ORTHO_VEC_BENCH_SEED);
3865    let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (*ORTHO_VEC_BENCH_SEED);
3866    let randf = |rng : &mut rand_xorshift::XorShiftRng| {
3867      let x : f64 = std_normal.sample (rng);
3868      let y : f64 = std_normal.sample (rng);
3869      x / y
3870    };
3871    b.iter(||{
3872      let vec : Vector3 <f64> = match rng.random_range (0..=10) {
3873        0..=4 => vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng)),
3874        5     => vector3 (randf (&mut rng), 0.0, 0.0),
3875        6     => vector3 (0.0, randf (&mut rng), 0.0),
3876        7     => vector3 (0.0, 0.0, randf (&mut rng)),
3877        8     => vector3 (0.0, randf (&mut rng), randf (&mut rng)),
3878        9     => vector3 (randf (&mut rng), 0.0, randf (&mut rng)),
3879        10    => vector3 (randf (&mut rng), randf (&mut rng), 0.0),
3880        _     => unreachable!()
3881      };
3882      if approx::abs_diff_eq!(vec, Vector3::zero(), epsilon = epsilon) {
3883        return
3884      }
3885      let ortho = vector3 (
3886        vec.z.copysign (vec.x),
3887        vec.z.copysign (vec.y),
3888        // the following two lines should be equivalent
3889        -vec.x.copysign (vec.z) - vec.y.copysign (vec.z)
3890        //-(vec.x.abs() + vec.y.abs()).copysign (vec.z)
3891      );
3892      if approx::abs_diff_eq!(ortho, Vector3::zero(), epsilon = epsilon) ||
3893        approx::abs_diff_ne!(vec.dot (ortho), 0.0, epsilon = epsilon)
3894      {
3895        println!("VEC:   {vec}");
3896        println!("ORTHO: {ortho}");
3897        println!("DOT:   {:.}", vec.dot (ortho));
3898      }
3899      approx::assert_abs_diff_ne!(ortho, Vector3::zero(), epsilon = epsilon);
3900      approx::assert_abs_diff_eq!(vec.dot (ortho), 0.0, epsilon = epsilon);
3901    });
3902  }
3903
3904  // method by Ken Whatmough: https://math.stackexchange.com/a/4112622
3905  // after running benchmarks this method is slower than both the copysign and cross
3906  // product methods, but it doesn't rely on floating-point specific copysign method
3907  #[bench]
3908  fn bench_orthogonal_sign (b : &mut test::Bencher) {
3909    use approx::AbsDiffEq;
3910    use rand::{RngExt, SeedableRng};
3911    use rand_distr::Distribution;
3912    let epsilon = f64::default_epsilon() * 2.0.powi (32);
3913    let std_normal = rand_distr::StandardNormal;
3914    println!("ORTHO_VEC_BENCH_SEED: {}", *ORTHO_VEC_BENCH_SEED);
3915    let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (*ORTHO_VEC_BENCH_SEED);
3916    let randf = |rng : &mut rand_xorshift::XorShiftRng| {
3917      let x : f64 = std_normal.sample (rng);
3918      let y : f64 = std_normal.sample (rng);
3919      x / y
3920    };
3921    b.iter(||{
3922      let vec : Vector3 <f64> = match rng.random_range (0..=10) {
3923        0..=4 => vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng)),
3924        5     => vector3 (randf (&mut rng), 0.0, 0.0),
3925        6     => vector3 (0.0, randf (&mut rng), 0.0),
3926        7     => vector3 (0.0, 0.0, randf (&mut rng)),
3927        8     => vector3 (0.0, randf (&mut rng), randf (&mut rng)),
3928        9     => vector3 (randf (&mut rng), 0.0, randf (&mut rng)),
3929        10    => vector3 (randf (&mut rng), randf (&mut rng), 0.0),
3930        _     => unreachable!()
3931      };
3932      if approx::abs_diff_eq!(vec, Vector3::zero(), epsilon = epsilon) {
3933        return
3934      }
3935      let sign = |n : f64| if n == 0.0 {
3936        0.0
3937      } else if n > 0.0 {
3938        1.0
3939      } else {
3940        -1.0
3941      };
3942      let signfn = |n : f64| sign ((sign (n) + 0.5) * (sign (vec.z) + 0.5));
3943      let ortho = vector3 (
3944        signfn (vec.x) * vec.z,
3945        signfn (vec.y) * vec.z,
3946        (-signfn (vec.x)).mul_add (vec.x, -signfn (vec.y) * vec.y)
3947        //-signfn (vec.x) * vec.x - signfn (vec.y) * vec.y
3948      );
3949      if approx::abs_diff_eq!(ortho, Vector3::zero(), epsilon = epsilon) ||
3950        approx::abs_diff_ne!(vec.dot (ortho), 0.0, epsilon = epsilon)
3951      {
3952        println!("VEC:   {vec}");
3953        println!("ORTHO: {ortho}");
3954        println!("DOT:   {:.}", vec.dot (ortho));
3955      }
3956      approx::assert_abs_diff_ne!(ortho, Vector3::zero(), epsilon = epsilon);
3957      approx::assert_abs_diff_eq!(vec.dot (ortho), 0.0, epsilon = epsilon);
3958    });
3959  }
3960}