num_quaternion/
unit_quaternion.rs

1use {
2    crate::Quaternion,
3    core::{
4        borrow::Borrow,
5        ops::{Add, Mul, Neg, Sub},
6    },
7    num_traits::{ConstOne, ConstZero, Inv, Num, One, Zero},
8};
9
10#[cfg(feature = "unstable")]
11#[cfg(any(feature = "std", feature = "libm"))]
12use crate::PureQuaternion;
13
14#[cfg(any(feature = "std", feature = "libm"))]
15use {
16    core::num::FpCategory,
17    num_traits::{Float, FloatConst},
18};
19
20/// A quaternion with norm $1$.
21///
22/// Unit quaternions form a non-commutative group that can be conveniently used
23/// for rotating 3D vectors. A 3D vector can be interpreted as a pure
24/// quaternion (a quaternion with a real part of zero). Such a pure quaternion
25/// $v$ can be rotated in 3D space by computing $q^{-1} \cdot v \cdot q$ for a
26/// unit quaternion $q$. The resulting product is again a pure quaternion,
27/// which is $v$ rotated around the axis given by the imaginary part of $q$.
28/// The method [`rotate_vector`](UnitQuaternion::rotate_vector) performs this
29/// operation efficiently. The angle of rotation is double the angle between
30/// $1$ and $q$ interpreted as 4D vectors.
31///
32/// You can create a `UnitQuaternion` by normalizing a `Quaternion` using the
33/// [`Quaternion::normalize`](Quaternion::normalize) method. Alternatively, you
34/// can use [`from_euler_angles`](UnitQuaternion::from_euler_angles),
35/// [`from_rotation_vector`](UnitQuaternion::from_rotation_vector), or
36/// [`from_rotation_matrix3x3`](UnitQuaternion::from_rotation_matrix3x3) to
37/// obtain one. The inverse functions
38/// [`to_euler_angles`](UnitQuaternion::to_euler_angles),
39/// [`to_rotation_vector`](UnitQuaternion::to_rotation_vector), and
40/// [`to_rotation_matrix3x3`](UnitQuaternion::to_rotation_matrix3x3) are also
41/// provided.
42///
43/// [`UnitQuaternion`] offers the same arithmetic operations as [`Quaternion`].
44/// Multiplying two unit quaternions yields a unit quaternion in theory.
45/// However, due to limited machine precision, rounding errors accumulate
46/// in practice and the resulting norm may deviate from $1$ over time.
47/// Thus, when you multiply unit quaternions many times, you may need
48/// to adjust the norm to maintain accuracy. This can be done by calling
49/// the function [`adjust_norm`](UnitQuaternion::adjust_norm).
50///
51/// Furthermore, you can interpolate uniformly between two quaternions using
52/// the [`slerp`](UnitQuaternion::slerp) method, which stands for spherical
53/// linear interpolation. This can be used for smooth transitions between
54/// 3D rotations.
55///
56/// See also [`Quaternion`].
57///
58/// # Examples
59///
60/// Basic usage:
61///
62/// ```rust
63/// # #[cfg(feature = "std")]
64/// # {
65/// # use num_quaternion::UnitQuaternion;
66/// // Creating a UnitQuaternion from Euler angles
67/// let (roll, pitch, yaw) = (1.5, 1.0, 3.0);
68/// let uq = UnitQuaternion::from_euler_angles(roll, pitch, yaw);
69///
70/// // Rotating a vector using the UnitQuaternion
71/// let vector = [1.0, 0.0, 0.0];
72/// let rotated_vector = uq.rotate_vector(vector);
73/// # }
74/// ```
75#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
76pub struct UnitQuaternion<T>(Quaternion<T>);
77
78/// Alias for a [`UnitQuaternion<f32>`].
79pub type UQ32 = UnitQuaternion<f32>;
80/// Alias for a [`UnitQuaternion<f64>`].
81pub type UQ64 = UnitQuaternion<f64>;
82
83#[cfg(feature = "unstable")]
84#[cfg(any(feature = "std", feature = "libm"))]
85impl<T> UnitQuaternion<T> {
86    pub(crate) fn new(w: T, x: T, y: T, z: T) -> Self {
87        Self(Quaternion::new(w, x, y, z))
88    }
89}
90
91/// Contains the roll, pitch and yaw angle of a rotation.
92#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
93pub struct EulerAngles<T> {
94    /// The roll angle.
95    pub roll: T,
96    /// The pitch angle.
97    pub pitch: T,
98    /// The yaw angle.
99    pub yaw: T,
100}
101
102#[cfg(feature = "serde")]
103impl<T> serde::Serialize for EulerAngles<T>
104where
105    T: serde::Serialize,
106{
107    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        S: serde::Serializer,
110    {
111        (&self.roll, &self.pitch, &self.yaw).serialize(serializer)
112    }
113}
114
115#[cfg(feature = "serde")]
116impl<'de, T> serde::Deserialize<'de> for EulerAngles<T>
117where
118    T: serde::Deserialize<'de>,
119{
120    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
121    where
122        D: serde::Deserializer<'de>,
123    {
124        let (roll, pitch, yaw) = serde::Deserialize::deserialize(deserializer)?;
125        Ok(Self { roll, pitch, yaw })
126    }
127}
128
129#[cfg(any(feature = "std", feature = "libm"))]
130impl<T> UnitQuaternion<T>
131where
132    T: Float,
133{
134    /// Creates a new Quaternion from roll, pitch and yaw angles.
135    ///
136    /// # Example
137    ///
138    /// ```
139    /// # use num_quaternion::UnitQuaternion;
140    /// let uq = UnitQuaternion::from_euler_angles(1.5, 1.0, 3.0);
141    /// ```
142    pub fn from_euler_angles(roll: T, pitch: T, yaw: T) -> Self {
143        let half = T::one() / (T::one() + T::one());
144        let (sr, cr) = (roll * half).sin_cos();
145        let (sp, cp) = (pitch * half).sin_cos();
146        let (sy, cy) = (yaw * half).sin_cos();
147        Self(Quaternion::new(
148            cr * cp * cy + sr * sp * sy,
149            sr * cp * cy - cr * sp * sy,
150            cr * sp * cy + sr * cp * sy,
151            cr * cp * sy - sr * sp * cy,
152        ))
153    }
154
155    /// Creates a new Quaternion from Euler angles.
156    ///
157    /// *Note.* The reason that this function is marked as `unstable` is that I'm not 100%
158    /// confident about the naming of the function.
159    ///
160    /// # Example
161    ///
162    /// ```
163    /// # use num_quaternion::UnitQuaternion;
164    /// # use num_quaternion::EulerAngles;
165    /// let angles = EulerAngles { roll: 1.5, pitch: 1.0, yaw: 3.0 };
166    /// let uq = UnitQuaternion::from_euler_angles_struct(angles);
167    /// ```
168    #[cfg(feature = "unstable")]
169    pub fn from_euler_angles_struct(angles: EulerAngles<T>) -> Self {
170        let EulerAngles { roll, pitch, yaw } = angles;
171        Self::from_euler_angles(roll, pitch, yaw)
172    }
173}
174
175#[cfg(any(feature = "std", feature = "libm"))]
176impl<T> UnitQuaternion<T>
177where
178    T: Float + FloatConst,
179{
180    /// Converts the UnitQuaternion to roll, pitch, and yaw angles.
181    ///
182    /// # Example
183    ///
184    /// ```
185    /// # use num_quaternion::UnitQuaternion;
186    /// let uq = UnitQuaternion::from_euler_angles(1.5, 1.0, 3.0);
187    /// let angles = uq.to_euler_angles();
188    /// ```
189    pub fn to_euler_angles(&self) -> EulerAngles<T> {
190        let &Self(Quaternion { w, x, y, z }) = self;
191
192        let one = T::one();
193        let two = one + one;
194        let epsilon = T::epsilon();
195        let half_pi = T::FRAC_PI_2();
196
197        // Compute the sin of the pitch angle
198        let sin_pitch = two * (w * y - z * x);
199
200        // Check for gimbal lock, which occurs when sin_pitch is close to 1 or -1
201        if sin_pitch.abs() >= one - epsilon {
202            // Gimbal lock case
203            if sin_pitch >= one - epsilon {
204                let pitch = half_pi; // 90 degrees
205                let roll = T::zero();
206                let yaw = -two * T::atan2(x, w);
207                EulerAngles { roll, pitch, yaw }
208            } else {
209                let pitch = -half_pi; // -90 degrees
210                let roll = T::zero();
211                let yaw = two * T::atan2(x, w);
212                EulerAngles { roll, pitch, yaw }
213            }
214        } else {
215            // General case
216            let pitch = sin_pitch.asin();
217            let roll =
218                T::atan2(two * (w * x + y * z), one - two * (x * x + y * y));
219            let yaw =
220                T::atan2(two * (w * z + x * y), one - two * (y * y + z * z));
221            EulerAngles { roll, pitch, yaw }
222        }
223    }
224}
225
226#[cfg(any(feature = "std", feature = "libm"))]
227impl<T> UnitQuaternion<T>
228where
229    T: Float,
230{
231    /// Returns a quaternion from a vector which is parallel to the rotation
232    /// axis and whose norm is the rotation angle.
233    ///
234    /// This function is the inverse of
235    /// [`to_rotation_vector`](UnitQuaternion::to_rotation_vector).
236    ///
237    /// The results of this function may not be accurate, if the input has a
238    /// very large norm. If the input vector is not finite (i. e. it contains
239    /// an infinite or `NaN` component), then the result is filled with `NaN`.
240    ///
241    /// # Example
242    ///
243    /// ```
244    /// # use num_quaternion::UnitQuaternion;
245    /// let v = [1.0, 0.0, 0.0];
246    /// let uq = UnitQuaternion::from_rotation_vector(&v);
247    /// ```
248    pub fn from_rotation_vector(v: &[T; 3]) -> Self {
249        let sqr_norm = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
250        const PI_SQUARED_F64: f64 =
251            core::f64::consts::PI * core::f64::consts::PI;
252        let pi_square = T::from(PI_SQUARED_F64).unwrap();
253        if T::epsilon() >= T::from(f32::EPSILON).unwrap()
254            && sqr_norm <= pi_square
255        {
256            Self::from_rotation_vector_f32_polynomial(v, sqr_norm)
257        } else {
258            Self::from_rotation_vector_generic(v, sqr_norm)
259        }
260    }
261
262    /// This function is public for internal benchmarking purposes only. It
263    /// computes the unit quaternion corresponding to a rotation vector using a
264    /// Chebyshev polynomial optimized for f32 precision and angles less than π.
265    ///
266    /// This method is used internally for rotation vectors whose norm is less
267    /// than or equal to π and when the floating-point type `T` has an epsilon
268    /// greater than or equal to `f32::EPSILON`. It provides a fast and accurate
269    /// approximation for the given rotation angles by evaluating a polynomial
270    /// with precomputed coefficients for the sinc and cosine functions.
271    /// Absolute errors are less than `2.0 * f32::EPSILON`.
272    ///
273    /// The polynomial coefficients were generated using Chebyshev approximation,
274    /// as documented in `examples/chebyshev_approximation.rs`.
275    ///
276    /// # Parameters
277    ///
278    /// - `v`: The rotation vector, whose direction is the rotation axis and
279    ///   whose norm is the rotation angle.
280    /// - `sqr_norm`: The squared norm of the rotation vector. It is expected to
281    ///   be less than or equal to π².
282    ///
283    /// # Returns
284    ///
285    /// A unit quaternion representing the rotation described by the input vector.
286    ///
287    /// # Panics
288    ///
289    /// Panics if the conversion between `T` and `f32` fails. This never happens
290    /// for built-in floating-point types (`f32`, `f64`), but may occur for
291    /// custom types.
292    #[inline]
293    pub fn from_rotation_vector_f32_polynomial(
294        v: &[T; 3],
295        sqr_norm: T,
296    ) -> Self {
297        let x = sqr_norm.to_f32().unwrap();
298        // The magic numbers below are calculated in
299        // `examples/chebyshev_approximation.rs`.
300        let half_sinc_half_norm = (((2.6051662e-6f32 / 512.0f32 * x
301            - 0.00019809046f32 / 128.0f32)
302            * x
303            + 0.008333051f32 / 32.0f32)
304            * x
305            - 0.16666658f32 / 8.0f32)
306            * x
307            + 1.0f32 / 2.0f32;
308        let cos_half_norm = (((2.3153174e-5f32 / 256.0f32 * x
309            - 0.0013853667f32 / 64.0f32)
310            * x
311            + 0.04166358f32 / 16.0f32)
312            * x
313            - 0.49999905f32 / 4.0f32)
314            * x
315            + 0.99999994f32;
316        let sinc_half_norm = T::from(half_sinc_half_norm).unwrap();
317        let cos_half_norm = T::from(cos_half_norm).unwrap();
318        Self(Quaternion::new(
319            cos_half_norm,
320            v[0] * sinc_half_norm,
321            v[1] * sinc_half_norm,
322            v[2] * sinc_half_norm,
323        ))
324    }
325
326    /// This function is public for internal benchmarking purposes only. It is
327    /// a generic implementation of `from_rotation_vector()`.
328    ///
329    /// This method is used to implement `from_rotation_vector()` for floating
330    /// point types `T` whose epsilon is less than `f32::EPSILON` or when the
331    /// norm of the rotation vector is greater than π.
332    #[inline]
333    pub fn from_rotation_vector_generic(v: &[T; 3], sqr_norm: T) -> Self {
334        let two = T::one() + T::one();
335        match sqr_norm.classify() {
336            FpCategory::Normal => {
337                let norm = sqr_norm.sqrt();
338                let (sine, cosine) = (norm / two).sin_cos();
339                let f = sine / norm;
340                Self(Quaternion::new(cosine, v[0] * f, v[1] * f, v[2] * f))
341            }
342            FpCategory::Zero | FpCategory::Subnormal => Self(Quaternion::new(
343                // This formula could be used for norm <= epsilon generally,
344                // where epsilon is the floating point epsilon.
345                T::one(),
346                v[0] / two,
347                v[1] / two,
348                v[2] / two,
349            )),
350            FpCategory::Nan | FpCategory::Infinite => Self(Quaternion::nan()),
351        }
352    }
353}
354
355#[cfg(any(feature = "std", feature = "libm"))]
356impl<T> UnitQuaternion<T>
357where
358    T: Float + FloatConst,
359{
360    /// Returns a rotation vector which is parallel to the rotation
361    /// axis and whose norm is the rotation angle.
362    ///
363    /// This function is the inverse of
364    /// [`from_rotation_vector`](UnitQuaternion::from_rotation_vector).
365    ///
366    /// # Example
367    ///
368    /// ```
369    /// # use num_quaternion::UnitQuaternion;
370    /// let uq = UnitQuaternion::from_euler_angles(1.5, 1.0, 3.0);
371    /// let v = uq.to_rotation_vector();
372    /// ```
373    pub fn to_rotation_vector(&self) -> [T; 3] {
374        let q = self.as_quaternion();
375        let one = T::one();
376        let two = one + one;
377        let epsilon = T::epsilon();
378
379        // Check if the absolute value of the quaternion's real part is small
380        // enough, so the angle can be computed quickly via `2 * q.w.acos()`.
381        // If the value is too large, then the arccosine becomes numerically
382        // unstable and we need to compute the angle differently.
383        let small_abs_real_part = q.w.abs() < T::from(0.9).unwrap();
384
385        // Compute the sin of half the angle
386        let sin_half_angle = if small_abs_real_part {
387            (one - q.w * q.w).sqrt()
388        } else {
389            // This is more expensive, but numerically more accurate than
390            // the first branch, if small_abs_real_part is false.
391            (q.x * q.x + q.y * q.y + q.z * q.z).sqrt()
392        };
393
394        // Compute the angle
395        let angle = two
396            * if small_abs_real_part {
397                q.w.acos()
398            } else if q.w.is_sign_positive() {
399                // The angle is less than 180 degrees.
400                if sin_half_angle < epsilon.sqrt() {
401                    // The angle is very close to zero. In this case we can
402                    // avoid division by zero and make the computation cheap
403                    // at the same time by returning the following immediately.
404                    return [two * q.x, two * q.y, two * q.z];
405                }
406                sin_half_angle.asin()
407            } else {
408                // The angle is more than 180 degrees.
409                if sin_half_angle.is_zero() {
410                    // The angle is exactly 360 degrees. To avoid division by zero we
411                    // return the following immediately.
412                    return [two * T::PI(), T::zero(), T::zero()];
413                }
414                let pi_minus_half_angle = sin_half_angle.asin();
415                T::PI() - pi_minus_half_angle
416            };
417
418        // Compute the normalized rotation vector components
419        let x = q.x / sin_half_angle;
420        let y = q.y / sin_half_angle;
421        let z = q.z / sin_half_angle;
422
423        [x * angle, y * angle, z * angle]
424    }
425}
426
427impl<T> UnitQuaternion<T>
428where
429    T: Add<Output = T> + Sub<Output = T> + Mul<Output = T> + One + Clone,
430{
431    /// Computes the rotation matrix implied by a unit quaternion.
432    ///
433    /// The matrix is returned in row major order, i. e. the indices into the
434    /// result array yield the elements in the following order:
435    ///
436    /// ```text
437    ///     [0, 1, 2,
438    ///      3, 4, 5,
439    ///      6, 7, 8]
440    /// ```
441    ///
442    /// Multiplying by the returned matrix gives the same result as using
443    /// [`rotate_vector`](UnitQuaternion::rotate_vector) modulo slightly
444    /// different rounding errors.
445    ///
446    /// # Runtime Considerations
447    ///
448    /// The matrix multiplication itself can be assumed to be more runtime
449    /// efficient than [`rotate_vector`](UnitQuaternion::rotate_vector).
450    /// However, computing the matrix also comes with additional cost. Thus
451    /// general advice is: Use [`rotate_vector`](UnitQuaternion::rotate_vector),
452    /// if you want to rotate a single vector. Perform the matrix
453    /// multiplication, if more than one vector needs to be rotated.
454    ///
455    /// # Example
456    ///
457    /// ```
458    /// # use num_quaternion::UQ64;
459    /// let uq = UQ64::I;
460    /// let matrix = uq.to_rotation_matrix3x3();
461    /// ```
462    #[inline]
463    pub fn to_rotation_matrix3x3(self) -> [T; 9] {
464        let two = T::one() + T::one();
465
466        let Self(Quaternion { w, x, y, z }) = self;
467
468        let wx = two.clone() * w.clone() * x.clone();
469        let wy = two.clone() * w.clone() * y.clone();
470        let wz = two.clone() * w * z.clone();
471        let xx = two.clone() * x.clone() * x.clone();
472        let xy = two.clone() * x.clone() * y.clone();
473        let xz = two.clone() * x * z.clone();
474        let yy = two.clone() * y.clone() * y.clone();
475        let yz = two.clone() * y * z.clone();
476        let zz = two * z.clone() * z;
477
478        [
479            T::one() - yy.clone() - zz.clone(),
480            xy.clone() - wz.clone(),
481            xz.clone() + wy.clone(),
482            xy + wz,
483            T::one() - xx.clone() - zz,
484            yz.clone() - wx.clone(),
485            xz - wy,
486            yz + wx,
487            T::one() - xx - yy,
488        ]
489    }
490}
491
492/// Interface for reading entries of a 3x3 matrix.
493pub trait ReadMat3x3<T> {
494    /// Returns the entry at the given row and column.
495    ///
496    /// Both `row` and `col` are zero-based.
497    fn at(&self, row: usize, col: usize) -> &T;
498}
499
500impl<T> ReadMat3x3<T> for [T; 9] {
501    fn at(&self, row: usize, col: usize) -> &T {
502        &self[col + 3 * row]
503    }
504}
505
506impl<T> ReadMat3x3<T> for [[T; 3]; 3] {
507    fn at(&self, row: usize, col: usize) -> &T {
508        &self[row][col]
509    }
510}
511
512// TODO: Provide interop with other linear algebra libraries, such as
513// * nalgebra
514// * cgmath
515// * ndarray
516// In other words, implement `ReadMat3x3` for the 3x3 matrix implementations.
517
518#[cfg(any(feature = "std", feature = "libm"))]
519impl<T> UnitQuaternion<T>
520where
521    T: Float,
522{
523    /// Computes a quaternion from a 3x3 rotation matrix.
524    ///
525    /// The input matrix $O$ is required to be an actual rotation matrix, i. e.
526    /// $O^TO$ is the identity matrix and $\det O = 1$ (neglecting floating
527    /// point rounding errors).
528    ///
529    /// The quaternion solution with non-negative real part is returned. This
530    /// function reverses the method
531    /// [`to_rotation_matrix3x3`](UnitQuaternion::to_rotation_matrix3x3).
532    ///
533    /// # Example
534    ///
535    /// ```
536    /// # use num_quaternion::UnitQuaternion;
537    /// let matrix = [[0.0, 1.0, 0.0],
538    ///               [0.0, 0.0, 1.0],
539    ///               [1.0, 0.0, 0.0]];
540    /// let uq = UnitQuaternion::from_rotation_matrix3x3(&matrix);
541    /// ```
542    pub fn from_rotation_matrix3x3(
543        mat: &impl ReadMat3x3<T>,
544    ) -> UnitQuaternion<T> {
545        let zero = T::zero();
546        let one = T::one();
547        let two = one + one;
548        let quarter = one / (two * two);
549        let m00 = mat.at(0, 0);
550        let m11 = mat.at(1, 1);
551        let m22 = mat.at(2, 2);
552        let trace: T = *m00 + *m11 + *m22;
553
554        // We distinguish different cases and select a computation method which
555        // provides robust results. See
556        // https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Rotation_matrix_%E2%86%94_quaternion
557        // where some details of the computation are described.
558        if trace > zero {
559            let s = (trace + one).sqrt() * two; // s=4*qw
560            let qw = quarter * s;
561            let qx = (*mat.at(2, 1) - *mat.at(1, 2)) / s;
562            let qy = (*mat.at(0, 2) - *mat.at(2, 0)) / s;
563            let qz = (*mat.at(1, 0) - *mat.at(0, 1)) / s;
564            Self(Quaternion::new(qw, qx, qy, qz))
565        } else if (m00 > m11) && (m00 > m22) {
566            let s = (one + *m00 - *m11 - *m22).sqrt() * two; // s=4*qx
567            let qw = (*mat.at(2, 1) - *mat.at(1, 2)) / s;
568            let qx = quarter * s;
569            let qy = (*mat.at(0, 1) + *mat.at(1, 0)) / s;
570            let qz = (*mat.at(0, 2) + *mat.at(2, 0)) / s;
571            if *mat.at(2, 1) >= *mat.at(1, 2) {
572                Self(Quaternion::new(qw, qx, qy, qz))
573            } else {
574                Self(Quaternion::new(-qw, -qx, -qy, -qz))
575            }
576        } else if m11 > m22 {
577            let s = (one + *m11 - *m00 - *m22).sqrt() * two; // s=4*qy
578            let qw = (*mat.at(0, 2) - *mat.at(2, 0)) / s;
579            let qx = (*mat.at(0, 1) + *mat.at(1, 0)) / s;
580            let qy = quarter * s;
581            let qz = (*mat.at(1, 2) + *mat.at(2, 1)) / s;
582            if *mat.at(0, 2) >= *mat.at(2, 0) {
583                Self(Quaternion::new(qw, qx, qy, qz))
584            } else {
585                Self(Quaternion::new(-qw, -qx, -qy, -qz))
586            }
587        } else {
588            let s = (one + *m22 - *m00 - *m11).sqrt() * two; // s=4*qz
589            let qw = (*mat.at(1, 0) - *mat.at(0, 1)) / s;
590            let qx = (*mat.at(0, 2) + *mat.at(2, 0)) / s;
591            let qy = (*mat.at(1, 2) + *mat.at(2, 1)) / s;
592            let qz = quarter * s;
593            if *mat.at(1, 0) >= *mat.at(0, 1) {
594                Self(Quaternion::new(qw, qx, qy, qz))
595            } else {
596                Self(Quaternion::new(-qw, -qx, -qy, -qz))
597            }
598        }
599    }
600}
601
602#[cfg(any(feature = "std", feature = "libm"))]
603impl<T> UnitQuaternion<T>
604where
605    T: Float,
606{
607    /// Given a unit vector $\vec a$, returns a unit vector that is
608    /// perpendicular to $\vec a$.
609    fn make_perpendicular_unit_vector(a: &[T; 3]) -> [T; 3] {
610        // Find the component of `a` with the smallest absolute value and form
611        // a normalized perpendicular vector using the other two components.
612        let zero = T::zero();
613        let a_sqr = [a[0] * a[0], a[1] * a[1], a[2] * a[2]];
614        if a_sqr[0] <= a_sqr[1] {
615            if a_sqr[0] <= a_sqr[2] {
616                // component 0 is minimal
617                let norm = (a_sqr[1] + a_sqr[2]).sqrt();
618                [zero, a[2] / norm, -a[1] / norm]
619            } else {
620                // component 2 is minimal
621                let norm = (a_sqr[0] + a_sqr[1]).sqrt();
622                [a[1] / norm, -a[0] / norm, zero]
623            }
624        } else if a_sqr[1] <= a_sqr[2] {
625            // component 1 is minimal
626            let norm = (a_sqr[0] + a_sqr[2]).sqrt();
627            [a[2] / norm, zero, -a[0] / norm]
628        } else {
629            // component 2 is minimal
630            let norm = (a_sqr[0] + a_sqr[1]).sqrt();
631            [a[1] / norm, -a[0] / norm, zero]
632        }
633    }
634
635    /// Returns a unit quaternion that rotates vector $\vec a$ to vector
636    /// $\vec b$ with the minimum angle of rotation.
637    ///
638    /// The method [`rotate_vector`](UnitQuaternion::rotate_vector) can be used
639    /// to apply the rotation. The resulting unit quaternion maps the ray
640    /// $\{t\vec{a} : t > 0\}$ to the ray $\{t\vec{b} : t > 0\}$.
641    ///
642    /// Note that the input vectors neither need to be normalized nor have the
643    /// same magnitude. In the case where the input vectors point in opposite
644    /// directions, there are multiple solutions to the problem, and one will
645    /// be returned. If one (or both) of the input vectors is the zero vector,
646    /// the unit quaternion $1$ is returned.
647    ///
648    /// # Example
649    ///
650    /// ```
651    /// # use num_quaternion::UQ64;
652    /// let a = [1.0, 0.0, 0.0];
653    /// let b = [0.0, 1.0, 0.0];
654    /// let uq = UQ64::from_two_vectors(&a, &b);
655    /// let angles = uq.to_euler_angles();
656    /// assert!((angles.yaw - std::f64::consts::FRAC_PI_2).abs() < 1e-10);
657    /// assert!((angles.pitch).abs() < 1e-10);
658    /// assert!((angles.roll).abs() < 1e-10);
659    /// ```
660    pub fn from_two_vectors(a: &[T; 3], b: &[T; 3]) -> UnitQuaternion<T> {
661        // Normalize vectors `a` and `b`. Let alpha be the angle between `a`
662        // and `b`. We aim to compute the quaternion
663        //     q = cos(alpha / 2) + sin(alpha / 2) * normalized_axis
664        // Start with the cross product and the dot product:
665        //     v := a × b = normalized_axis * sin(alpha)
666        //     d := a.dot(b) = cos(alpha) = 2 * cos(alpha / 2)² - 1
667        // Thus, we can compute double the real part as
668        //     wx2 := 2 * cos(alpha / 2) = √(2d + 2).
669        // Since
670        //     sin(alpha) = 2 * sin(alpha / 2) * cos(alpha / 2),
671        // it follows that
672        //     sin(alpha / 2) = sin(alpha) / cos(alpha / 2) / 2
673        //                    = sin(alpha) / wx2.
674        // Consequently, we can compute the quaternion as
675        //     q = wx2 / 2 + v / wx2.
676        // If d -> -1, then wx2 -> 0, leading to large relative errors in
677        // the computation of wx2, making the imaginary part of q inaccurate.
678        // This occurs when `a` and `b` point in nearly opposite directions.
679        // We can check if they are exactly opposite by testing if `v` is a
680        // null vector. If not, then
681        //     sin(alpha) = |v|, and
682        //     cos(alpha) = -√(1-|v|²) = d
683        //     wx2 = √(2d + 2)
684        //         = √(2 - 2√(1-|v|²))
685        //         = 2|v| / √(2 + 2√(1-|v|²))
686        //         = |v| / √(1/2 - d/2).
687        // This last formula is numerically stable for negative values of d.
688        // Therefore,
689        //     q = |v| / √(1/2 - d/2) / 2 + v / |v| * √(1/2 - d/2)
690
691        // Constants
692        let zero = T::zero();
693        let one = T::one();
694        let two = one + one;
695        let half = one / two;
696
697        // Normalize vector inputs
698        let a_norm = a[0].hypot(a[1].hypot(a[2]));
699        let b_norm = b[0].hypot(b[1].hypot(b[2]));
700        if a_norm.is_zero() || b_norm.is_zero() {
701            return UnitQuaternion::one();
702        }
703        let a = [a[0] / a_norm, a[1] / a_norm, a[2] / a_norm];
704        let b = [b[0] / b_norm, b[1] / b_norm, b[2] / b_norm];
705
706        // Cross product
707        let v = [
708            a[1] * b[2] - a[2] * b[1],
709            a[2] * b[0] - a[0] * b[2],
710            a[0] * b[1] - a[1] * b[0],
711        ];
712
713        // Dot product
714        let d = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
715
716        let wx2 = if d >= -half {
717            // Simple stable formula for the general case
718            (two * d + two).sqrt()
719        } else {
720            // `a` and `b` may be close to anti-parallel
721            let v_norm_sqr = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
722            if v_norm_sqr.is_zero() {
723                // Exactly anti-parallel
724                let [x, y, z] = Self::make_perpendicular_unit_vector(&a);
725                return UnitQuaternion(Quaternion::new(zero, x, y, z));
726            }
727            // Stable, more expensive formula for wx2
728            (v_norm_sqr / (half - d * half)).sqrt()
729        };
730
731        // Return the computed quaternion
732        UnitQuaternion(Quaternion::new(
733            wx2 / two,
734            v[0] / wx2,
735            v[1] / wx2,
736            v[2] / wx2,
737        ))
738    }
739}
740
741#[cfg(any(feature = "std", feature = "libm"))]
742impl<T> UnitQuaternion<T>
743where
744    T: Float,
745{
746    /// Normalizes the quaternion to length $1$.
747    ///
748    /// The sign of the real part will be the same as the sign of the input.
749    /// If the input quaternion
750    ///
751    /// * is zero, or
752    /// * has infinite length, or
753    /// * has a `NaN` value,
754    ///
755    /// then `None` will be returned.
756    ///
757    /// Note, that it may be more natural to use the method
758    /// [`Quaternion::normalize`] instead of this function. Both functions are
759    /// equivalent, but the method is more idiomatic in most cases.
760    ///
761    /// # Example
762    ///
763    /// ```
764    /// # use num_quaternion::UnitQuaternion;
765    /// let q = UnitQuaternion::from_euler_angles(1.5, 1.0, 3.0);
766    /// let normalized = UnitQuaternion::normalize(q.into_inner());
767    /// assert_eq!(normalized, Some(q));
768    /// ```
769    #[inline]
770    pub fn normalize(q: Quaternion<T>) -> Option<Self> {
771        let norm = q.norm();
772        match norm.classify() {
773            FpCategory::Normal | FpCategory::Subnormal => {
774                Some(UnitQuaternion(q / norm))
775            }
776            _ => None,
777        }
778    }
779}
780
781impl<T> Default for UnitQuaternion<T>
782where
783    T: Num + Clone,
784{
785    #[inline]
786    fn default() -> Self {
787        Self::one()
788    }
789}
790
791impl<T> UnitQuaternion<T>
792where
793    T: ConstZero + ConstOne,
794{
795    /// A constant `UnitQuaternion` of value $1$.
796    ///
797    /// See also [`UnitQuaternion::one`], [`Quaternion::ONE`].
798    ///
799    /// # Example
800    ///
801    /// ```
802    /// # use num_quaternion::UQ32;
803    /// assert_eq!(UQ32::ONE, UQ32::one());
804    /// ```
805    pub const ONE: Self = Self(Quaternion::ONE);
806
807    /// A constant `UnitQuaternion` of value $i$.
808    ///
809    /// See also [`UnitQuaternion::i`], [`Quaternion::I`].
810    ///
811    /// # Example
812    ///
813    /// ```
814    /// # use num_quaternion::UQ32;
815    /// assert_eq!(UQ32::I, UQ32::i());
816    /// ```
817    pub const I: Self = Self(Quaternion::I);
818
819    /// A constant `UnitQuaternion` of value $j$.
820    ///
821    /// See also [`UnitQuaternion::j`], [`Quaternion::J`].
822    ///
823    /// # Example
824    ///
825    /// ```
826    /// # use num_quaternion::UQ32;
827    /// assert_eq!(UQ32::J, UQ32::j());
828    /// ```
829    pub const J: Self = Self(Quaternion::J);
830
831    /// A constant `UnitQuaternion` of value $k$.
832    ///
833    /// See also [`UnitQuaternion::k`], [`Quaternion::K`].
834    ///
835    /// # Example
836    ///
837    /// ```
838    /// # use num_quaternion::UQ32;
839    /// assert_eq!(UQ32::K, UQ32::k());
840    /// ```
841    pub const K: Self = Self(Quaternion::K);
842}
843
844impl<T> ConstOne for UnitQuaternion<T>
845where
846    T: ConstZero + ConstOne + Num + Clone,
847{
848    const ONE: Self = Self::ONE;
849}
850
851impl<T> One for UnitQuaternion<T>
852where
853    T: Num + Clone,
854{
855    #[inline]
856    fn one() -> Self {
857        Self(Quaternion::one())
858    }
859
860    #[inline]
861    fn is_one(&self) -> bool {
862        self.0.is_one()
863    }
864
865    #[inline]
866    fn set_one(&mut self) {
867        self.0.set_one();
868    }
869}
870
871impl<T> UnitQuaternion<T>
872where
873    T: Zero + One,
874{
875    /// Returns the identity quaternion $1$.
876    ///
877    /// See also [`UnitQuaternion::ONE`], [`Quaternion::ONE`].
878    ///
879    /// # Example
880    ///
881    /// ```
882    /// # use num_quaternion::{Q32, UQ32};
883    /// assert_eq!(UQ32::one().into_inner(), Q32::one());
884    /// ```
885    #[inline]
886    pub fn one() -> Self {
887        Self(Quaternion::new(T::one(), T::zero(), T::zero(), T::zero()))
888    }
889
890    /// Returns the imaginary unit $i$.
891    ///
892    /// See also [`UnitQuaternion::I`], [`Quaternion::i`].
893    ///
894    /// # Example
895    ///
896    /// ```
897    /// # use num_quaternion::{Q32, UQ32};
898    /// assert_eq!(UQ32::i().into_inner(), Q32::new(0.0, 1.0, 0.0, 0.0));
899    /// ```
900    #[inline]
901    pub fn i() -> Self {
902        Self(Quaternion::i())
903    }
904
905    /// Returns the imaginary unit $j$.
906    ///
907    /// See also [`UnitQuaternion::J`], [`Quaternion::j`].
908    ///
909    /// # Example
910    ///
911    /// ```
912    /// # use num_quaternion::{Q32, UQ32};
913    /// assert_eq!(UQ32::j().into_inner(), Q32::new(0.0, 0.0, 1.0, 0.0));
914    /// ```
915    #[inline]
916    pub fn j() -> Self {
917        Self(Quaternion::j())
918    }
919
920    /// Returns the imaginary unit $k$.
921    ///
922    /// See also [`UnitQuaternion::K`], [`Quaternion::k`].
923    ///
924    /// # Example
925    ///
926    /// ```
927    /// # use num_quaternion::{Q32, UQ32};
928    /// assert_eq!(UQ32::k().into_inner(), Q32::new(0.0, 0.0, 0.0, 1.0));
929    /// ```
930    #[inline]
931    pub fn k() -> Self {
932        Self(Quaternion::k())
933    }
934}
935
936impl<T> UnitQuaternion<T> {
937    /// Returns the inner quaternion.
938    ///
939    /// This function does the same as
940    /// [`into_inner`](UnitQuaternion::into_inner). Client code can decide
941    /// which function to use based on the naming preference and context.
942    ///
943    /// # Example
944    ///
945    /// ```
946    /// # use num_quaternion::{Q64, UQ64};
947    /// let uq = UQ64::I;
948    /// let q = uq.into_quaternion();
949    /// assert_eq!(q, Q64::I);
950    /// ```
951    #[inline]
952    pub fn into_quaternion(self) -> Quaternion<T> {
953        self.0
954    }
955
956    /// Returns the inner quaternion.
957    ///
958    /// This function does the same as
959    /// [`into_quaternion`](UnitQuaternion::into_quaternion). Client code can
960    /// decide which function to use based on the naming preference and
961    /// context.
962    ///
963    /// # Example
964    ///
965    /// ```
966    /// # use num_quaternion::{Q64, UQ64};
967    /// let uq = UQ64::I;
968    /// let q = uq.into_inner();
969    /// assert_eq!(q, Q64::I);
970    /// ```
971    #[inline]
972    pub fn into_inner(self) -> Quaternion<T> {
973        self.into_quaternion()
974    }
975
976    /// Returns a reference to the inner quaternion.
977    ///
978    /// # Example
979    ///
980    /// ```
981    /// # use num_quaternion::{Q64, UQ64};
982    /// let uq = UQ64::I;
983    /// let q = uq.as_quaternion();
984    /// assert_eq!(q, &Q64::I);
985    /// ```
986    #[inline]
987    pub fn as_quaternion(&self) -> &Quaternion<T> {
988        &self.0
989    }
990}
991
992impl<T> Borrow<Quaternion<T>> for UnitQuaternion<T> {
993    fn borrow(&self) -> &Quaternion<T> {
994        self.as_quaternion()
995    }
996}
997
998impl<T> UnitQuaternion<T>
999where
1000    T: Clone + Neg<Output = T>,
1001{
1002    /// Returns the conjugate quaternion, i. e. the imaginary part is negated.
1003    ///
1004    /// # Example
1005    ///
1006    /// ```
1007    /// # use num_quaternion::UQ64;
1008    /// let uq = UQ64::I;
1009    /// let conj = uq.conj();
1010    /// assert_eq!(conj, -uq);
1011    /// ```
1012    #[inline]
1013    pub fn conj(&self) -> Self {
1014        Self(self.0.conj())
1015    }
1016}
1017
1018impl<T> UnitQuaternion<T>
1019where
1020    T: Clone + Neg<Output = T>,
1021{
1022    /// Returns the multiplicative inverse `1/self`.
1023    ///
1024    /// This is the same as the conjugate of `self`.
1025    ///
1026    /// # Example
1027    ///
1028    /// ```
1029    /// # use num_quaternion::UQ32;
1030    /// let uq = UQ32::I;
1031    /// let inv = uq.inv();
1032    /// assert_eq!(inv, -UQ32::I);
1033    /// ```
1034    #[inline]
1035    pub fn inv(&self) -> Self {
1036        self.conj()
1037    }
1038}
1039
1040impl<T> Inv for &UnitQuaternion<T>
1041where
1042    T: Clone + Neg<Output = T>,
1043{
1044    type Output = UnitQuaternion<T>;
1045
1046    #[inline]
1047    fn inv(self) -> Self::Output {
1048        self.conj()
1049    }
1050}
1051
1052impl<T> Inv for UnitQuaternion<T>
1053where
1054    T: Clone + Neg<Output = T>,
1055{
1056    type Output = UnitQuaternion<T>;
1057
1058    #[inline]
1059    fn inv(self) -> Self::Output {
1060        self.conj()
1061    }
1062}
1063
1064impl<T> Mul<UnitQuaternion<T>> for UnitQuaternion<T>
1065where
1066    Quaternion<T>: Mul<Output = Quaternion<T>>,
1067{
1068    type Output = UnitQuaternion<T>;
1069
1070    #[inline]
1071    fn mul(self, rhs: UnitQuaternion<T>) -> Self::Output {
1072        Self(self.into_inner() * rhs.into_inner())
1073    }
1074}
1075
1076impl<T> Neg for UnitQuaternion<T>
1077where
1078    T: Neg<Output = T>,
1079{
1080    type Output = UnitQuaternion<T>;
1081
1082    fn neg(self) -> Self::Output {
1083        Self(-self.0)
1084    }
1085}
1086
1087impl<T> UnitQuaternion<T>
1088where
1089    T: Add<T, Output = T> + Mul<T, Output = T>,
1090{
1091    /// Computes the dot product of two unit quaternions interpreted as
1092    /// 4D real vectors.
1093    ///
1094    /// # Example
1095    ///
1096    /// ```
1097    /// # use num_quaternion::UQ32;
1098    /// let uq1 = UQ32::I;
1099    /// let uq2 = UQ32::J;
1100    /// let dot = uq1.dot(uq2);
1101    /// assert_eq!(dot, 0.0);
1102    /// ```
1103    #[inline]
1104    pub fn dot(self, other: Self) -> T {
1105        self.0.dot(other.0)
1106    }
1107}
1108
1109#[cfg(any(feature = "std", feature = "libm"))]
1110impl<T> UnitQuaternion<T>
1111where
1112    T: Float,
1113{
1114    /// Renormalizes `self`.
1115    ///
1116    /// By many multiplications of unit quaternions, round off errors can lead
1117    /// to norms which are deviating from $1$ significantly. This function
1118    /// fixes that inaccuracy.
1119    ///
1120    /// # Panics
1121    ///
1122    /// Panics if the norm of the quaternion is too inaccurate to be
1123    /// renormalized.
1124    ///
1125    /// # Example
1126    ///
1127    /// ```
1128    /// # use num_quaternion::UnitQuaternion;
1129    /// let uq = UnitQuaternion::from_euler_angles(1.5, 1.0, 3.0);
1130    /// let adjusted = uq.adjust_norm();
1131    /// assert!((adjusted - uq).norm() < 1e-10);
1132    /// ```
1133    #[inline]
1134    pub fn adjust_norm(self) -> Self {
1135        // TODO: Optimize for norms which are close to 1.
1136        self.0
1137            .normalize()
1138            .expect("Unit quaternion value too inaccurate. Cannot renormalize.")
1139    }
1140}
1141
1142impl<T> UnitQuaternion<T>
1143where
1144    T: Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Clone,
1145{
1146    /// Rotates a vector using a quaternion.
1147    ///
1148    /// Given a unit quaternion $q$ and a pure quaternion $v$ (i. e. a
1149    /// quaternion with real part zero), the mapping $v \mapsto q^*vq$
1150    /// is a 3D rotation in the space of pure quaternions. This function
1151    /// performs this 3D rotation efficiently.
1152    ///
1153    /// # Example
1154    ///
1155    /// ```
1156    /// # use num_quaternion::UQ64;
1157    /// let uq = UQ64::I;
1158    /// let rotated = uq.rotate_vector([1.0, 2.0, 3.0]);
1159    /// assert_eq!(rotated, [1.0, -2.0, -3.0]);
1160    /// ```
1161    pub fn rotate_vector(self, vector: [T; 3]) -> [T; 3] {
1162        let q = self.into_quaternion();
1163        let [vx, vy, vz] = vector;
1164        let v_q_inv = Quaternion::<T>::new(
1165            vx.clone() * q.x.clone()
1166                + vy.clone() * q.y.clone()
1167                + vz.clone() * q.z.clone(),
1168            vx.clone() * q.w.clone() - vy.clone() * q.z.clone()
1169                + vz.clone() * q.y.clone(),
1170            vx.clone() * q.z.clone() + vy.clone() * q.w.clone()
1171                - vz.clone() * q.x.clone(),
1172            vy * q.x.clone() - vx * q.y.clone() + vz * q.w.clone(),
1173        );
1174        let result = q * v_q_inv;
1175        [result.x, result.y, result.z]
1176    }
1177}
1178
1179#[cfg(any(feature = "std", feature = "libm"))]
1180impl<T> UnitQuaternion<T>
1181where
1182    T: Float,
1183{
1184    /// Spherical linear interpolation between two unit quaternions.
1185    ///
1186    /// `t` should be in the range [0, 1], where 0 returns `self` and 1 returns
1187    /// `other` or `-other`, whichever is closer to `self`.
1188    ///
1189    /// # Example
1190    ///
1191    /// ```
1192    /// # use num_quaternion::UnitQuaternion;
1193    /// let uq1 = UnitQuaternion::from_euler_angles(1.5, 1.0, 3.0);
1194    /// let uq2 = UnitQuaternion::from_euler_angles(0.5, 2.0, 1.0);
1195    /// let uq = uq1.slerp(&uq2, 0.5);
1196    /// ```
1197    pub fn slerp(&self, other: &Self, t: T) -> Self {
1198        let one = T::one();
1199        let dot = self.dot(*other);
1200
1201        // If the dot product is negative, slerp won't take the shorter path.
1202        // We fix this by reversing one quaternion.
1203        let (dot, other) = if dot.is_sign_positive() {
1204            (dot, *other)
1205        } else {
1206            (-dot, -*other)
1207        };
1208
1209        // Use a threshold to decide when to use linear interpolation to avoid
1210        // precision issues
1211        let threshold = one - T::epsilon().sqrt();
1212        if dot > threshold {
1213            // Perform linear interpolation and normalize the result
1214            return Self(*self + (other - *self) * t);
1215        }
1216
1217        // theta_0 = angle between input quaternions
1218        let theta_0 = dot.acos();
1219        // theta = angle between self and result
1220        let theta = theta_0 * t;
1221
1222        // Compute the spherical interpolation coefficients
1223        let sin_theta = theta.sin();
1224        let sin_theta_0 = theta_0.sin();
1225        let s0 = ((one - t) * theta_0).sin() / sin_theta_0;
1226        let s1 = sin_theta / sin_theta_0;
1227
1228        // The following result is already normalized, if the inputs are
1229        // normalized (which we assume).
1230        Self(*self * s0 + other * s1)
1231    }
1232}
1233
1234#[cfg(any(feature = "std", feature = "libm"))]
1235impl<T> UnitQuaternion<T>
1236where
1237    T: Float + FloatConst,
1238{
1239    /// Computes the square root of a unit quaternion.
1240    ///
1241    /// Given an input unit quaternion $c$, this function returns the unit
1242    /// quaternion $q$ which satisfies $q^2 = c$ and has a real part with a
1243    /// positive sign.
1244    ///
1245    /// For $c = -1$, there are multiple solutions to these constraints. In
1246    /// that case $q = \pm i$ is returned. The sign is determined by the input
1247    /// coefficient of the imaginary unit $i$.
1248    ///
1249    /// In any case, the three imaginary parts of the result have the same sign
1250    /// as the three imaginary parts of the input.
1251    ///
1252    /// # Example
1253    ///
1254    /// ```
1255    /// # use num_quaternion::UnitQuaternion;
1256    /// let uq = UnitQuaternion::from_euler_angles(1.5, 1.0, 3.0);
1257    /// let sqrt = uq.sqrt();
1258    /// assert!((sqrt * sqrt - uq).norm() < 1e-10);
1259    /// ```
1260    pub fn sqrt(self) -> Self {
1261        let zero = T::zero();
1262        let one = T::one();
1263        let two = one + one;
1264        let half = one / two;
1265        let UnitQuaternion(c) = self;
1266
1267        if c.w >= -half {
1268            // Compute double the real part of the result directly and
1269            // robustly.
1270            //
1271            // Note: We could also compute the real part directly.
1272            // However, this would be inferior for the following reasons:
1273            //
1274            // - To compute the imaginary parts of the result, we would need
1275            //   to double the real part anyway, which would require an
1276            //   extra arithmetic operation, adding to the latency of the
1277            //   computation.
1278            // - To avoid this latency, we could also multiply `c.x`, `c.y`,
1279            //   and `c.z` by 1/2 and then divide by the real part (which
1280            //   takes longer to compute). However, this could cost some
1281            //   accuracy for subnormal imaginary parts.
1282            let wx2 = (c.w * two + two).sqrt();
1283
1284            UnitQuaternion(Quaternion::new(
1285                wx2 * half,
1286                c.x / wx2,
1287                c.y / wx2,
1288                c.z / wx2,
1289            ))
1290        } else {
1291            // For cases where the real part is too far in the negative direction.
1292            //
1293            // Note: We could also use the formula 1 - c.w * c.w for the
1294            // square norm of the imaginary part. However, if the real part
1295            // `c.w` is close to -1, then this becomes inaccurate. This is
1296            // especially the case, if the actual norm of the input
1297            // quaternion $c$ is not close enough to one.
1298            let im_norm_sqr = c.y * c.y + (c.x * c.x + c.z * c.z);
1299            if im_norm_sqr >= T::min_positive_value() {
1300                // Robust computation for negative real part inputs.
1301                let wx2 = (im_norm_sqr * two / (one - c.w)).sqrt();
1302                UnitQuaternion(Quaternion::new(
1303                    wx2 * half,
1304                    c.x / wx2,
1305                    c.y / wx2,
1306                    c.z / wx2,
1307                ))
1308            } else if c.x.is_zero() && c.y.is_zero() && c.z.is_zero() {
1309                // Special case: input is -1. The result is `±i` with the same
1310                // signs for the imaginary parts as the input.
1311                UnitQuaternion(Quaternion::new(
1312                    zero,
1313                    one.copysign(c.x),
1314                    c.y,
1315                    c.z,
1316                ))
1317            } else {
1318                // `im_norm_sqr` is subnormal, scale up first.
1319                let s = one / T::min_positive_value();
1320                let sx = s * c.x;
1321                let sy = s * c.y;
1322                let sz = s * c.z;
1323                let im_norm = (sy * sy + (sx * sx + sz * sz)).sqrt() / s;
1324                UnitQuaternion(Quaternion::new(
1325                    im_norm * half,
1326                    c.x / im_norm,
1327                    c.y / im_norm,
1328                    c.z / im_norm,
1329                ))
1330            }
1331        }
1332    }
1333}
1334
1335#[cfg(feature = "unstable")]
1336#[cfg(any(feature = "std", feature = "libm"))]
1337impl<T> UnitQuaternion<T>
1338where
1339    T: Float + FloatConst,
1340{
1341    /// Computes the natural logarithm of a unit quaternion.
1342    ///
1343    /// The function implements the following guarantees for extreme input
1344    /// values:
1345    ///
1346    /// - The function is continuous onto the branch cut taking into account
1347    ///   the sign of the coefficient of $i$.
1348    /// - For all quaternions $q$ it holds `q.conj().ln() == q.ln().conj()`.
1349    /// - The signs of the coefficients of the imaginary parts of the outputs
1350    ///   are equal to the signs of the respective coefficients of the inputs.
1351    ///   This also holds for signs of zeros, but not for `NaNs`.
1352    /// - If the input has a `NaN` value, then the result is `NaN` in all
1353    ///   components.
1354    ///
1355    /// # Example
1356    ///
1357    /// ```
1358    /// # use num_quaternion::Quaternion;
1359    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0).normalize().unwrap();
1360    /// let ln_q = q.ln();
1361    /// ```
1362    pub fn ln(self) -> PureQuaternion<T> {
1363        // The square norm of the imaginary part.
1364        let sqr_norm_im =
1365            self.0.x * self.0.x + self.0.y * self.0.y + self.0.z * self.0.z;
1366
1367        if sqr_norm_im <= T::epsilon() {
1368            // We're close to or on the positive real axis
1369            if self.0.w.is_sign_positive() {
1370                // This approximation leaves a relative error of less
1371                // than a floating point epsilon for the imaginary part
1372                PureQuaternion::new(self.0.x, self.0.y, self.0.z)
1373            } else if self.0.x.is_zero()
1374                && self.0.y.is_zero()
1375                && self.0.z.is_zero()
1376            {
1377                // We're on the negative real axis.
1378                PureQuaternion::new(
1379                    T::PI().copysign(self.0.x),
1380                    self.0.y,
1381                    self.0.z,
1382                )
1383            } else if sqr_norm_im.is_normal() {
1384                // We're close to the negative real axis. Compute the
1385                let norm_im = sqr_norm_im.sqrt();
1386
1387                // The angle of `self` to the positive real axis is
1388                // pi minus the angle from the negative real axis.
1389                // The angle from the negative real axis
1390                // can be approximated by `norm_im / self.w.abs()`
1391                // which is equal to `-norm_im / self.w`. This the
1392                // angle from the positive real axis is
1393                // `pi + norm_im / self.w`. We obtain the imaginary
1394                // part of the result by multiplying this value by
1395                // the imaginary part of the input normalized, or
1396                // equivalently, by multiplying the imaginary part
1397                // of the input by the following factor:
1398                let f = T::PI() / norm_im + self.0.w.recip();
1399
1400                PureQuaternion::new(self.0.x, self.0.y, self.0.z) * f
1401            } else {
1402                // The imaginary part is so small, that the norm of the
1403                // resulting imaginary part differs from `pi` by way
1404                // less than half an ulp. Therefore, it's sufficient to
1405                // normalize the imaginary part and multiply it by
1406                // `pi`.
1407                let f = T::min_positive_value().sqrt() * T::epsilon();
1408                let xf = self.0.x / f;
1409                let yf = self.0.y / f;
1410                let zf = self.0.z / f;
1411                let sqr_sum = xf * xf + yf * yf + zf * zf;
1412                let im_norm_div_f = sqr_sum.sqrt();
1413                let pi_div_f = T::PI() / f;
1414                // We could try to reduce the number of divisions by
1415                // computing `pi_div_f / im_norm_div_f` and then
1416                // multiplying the imaginary part by this value.
1417                // However, this reduces numerical accuracy, if the
1418                // pi times the norm of the imaginary part is
1419                // subnormal. We could also introduce another branch
1420                // here, but this would make the code more complex
1421                // and extend the worst case latency. Therefore, we
1422                // keep the divisions like that.
1423                PureQuaternion::new(
1424                    self.0.x * pi_div_f / im_norm_div_f,
1425                    self.0.y * pi_div_f / im_norm_div_f,
1426                    self.0.z * pi_div_f / im_norm_div_f,
1427                )
1428            }
1429        } else {
1430            // The most natural case: We're far enough from the real
1431            // axis and the norm of the input quaternion is large
1432            // enough to exclude any numerical instabilities.
1433            let norm_im = if sqr_norm_im.is_normal() {
1434                // `sqr_norm_im` has maximum precision.
1435                sqr_norm_im.sqrt()
1436            } else {
1437                // Otherwise, using `sqr_norm_im` is imprecise.
1438                // We magnify the imaginary part first, so we can
1439                // get around this problem.
1440                let f = T::min_positive_value().sqrt() * T::epsilon();
1441                let xf = self.0.x / f;
1442                let yf = self.0.y / f;
1443                let zf = self.0.z / f;
1444                let sqr_sum = xf * xf + yf * yf + zf * zf;
1445                sqr_sum.sqrt() * f
1446            };
1447            let angle = norm_im.atan2(self.0.w);
1448            let x = self.0.x * angle / norm_im;
1449            let y = self.0.y * angle / norm_im;
1450            let z = self.0.z * angle / norm_im;
1451            PureQuaternion::new(x, y, z)
1452        }
1453    }
1454}
1455
1456#[cfg(feature = "serde")]
1457impl<T> serde::Serialize for UnitQuaternion<T>
1458where
1459    T: serde::Serialize,
1460{
1461    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1462    where
1463        S: serde::Serializer,
1464    {
1465        self.0.serialize(serializer)
1466    }
1467}
1468
1469#[cfg(feature = "serde")]
1470impl<'de, T> serde::Deserialize<'de> for UnitQuaternion<T>
1471where
1472    T: serde::Deserialize<'de>,
1473{
1474    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1475    where
1476        D: serde::Deserializer<'de>,
1477    {
1478        let q = serde::Deserialize::deserialize(deserializer)?;
1479        Ok(Self(q))
1480    }
1481}
1482
1483#[cfg(all(feature = "rand", any(feature = "std", feature = "libm")))]
1484impl<T> rand::distr::Distribution<UnitQuaternion<T>>
1485    for rand::distr::StandardUniform
1486where
1487    T: Float,
1488    rand_distr::StandardNormal: rand_distr::Distribution<T>,
1489{
1490    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> UnitQuaternion<T> {
1491        loop {
1492            let s = rand_distr::StandardNormal;
1493            let w = s.sample(rng);
1494            let x = s.sample(rng);
1495            let y = s.sample(rng);
1496            let z = s.sample(rng);
1497            let q = Quaternion::new(w, x, y, z);
1498            if let Some(q) = q.normalize() {
1499                return q;
1500            }
1501        }
1502    }
1503}
1504
1505#[cfg(test)]
1506mod tests {
1507    use {
1508        crate::{Quaternion, UnitQuaternion, Q32, UQ32, UQ64},
1509        core::borrow::Borrow,
1510        num_traits::{ConstOne, One},
1511    };
1512
1513    #[cfg(feature = "std")]
1514    use {
1515        core::hash::{Hash, Hasher},
1516        std::collections::hash_map::DefaultHasher,
1517    };
1518
1519    #[cfg(any(feature = "std", feature = "libm"))]
1520    use {crate::Q64, num_traits::Inv};
1521
1522    #[cfg(any(feature = "std", feature = "libm", feature = "serde"))]
1523    use crate::EulerAngles;
1524
1525    #[cfg(any(feature = "std", feature = "libm"))]
1526    #[cfg(feature = "unstable")]
1527    use crate::PureQuaternion;
1528
1529    /// Computes the hash value of `val` using the default hasher.
1530    #[cfg(feature = "std")]
1531    fn compute_hash(val: impl Hash) -> u64 {
1532        let mut hasher = DefaultHasher::new();
1533        val.hash(&mut hasher);
1534        hasher.finish()
1535    }
1536
1537    #[cfg(feature = "std")]
1538    #[test]
1539    fn test_hash_of_unit_quaternion_equals_hash_of_inner_quaternion() {
1540        // We test if the hash value of a unit quaternion is equal to the hash
1541        // value of the inner quaternion. This is required because
1542        // `UnitQuaternion` implements both `Hash` and `Borrow<Quaternion>`.
1543        assert_eq!(
1544            compute_hash(UnitQuaternion::<u32>::ONE),
1545            compute_hash(Quaternion::<u32>::ONE)
1546        );
1547        assert_eq!(
1548            compute_hash(UnitQuaternion::<i32>::I),
1549            compute_hash(Quaternion::<i32>::I)
1550        );
1551        assert_eq!(
1552            compute_hash(UnitQuaternion::<isize>::J),
1553            compute_hash(Quaternion::<isize>::J)
1554        );
1555        assert_eq!(
1556            compute_hash(UnitQuaternion::<i128>::K),
1557            compute_hash(Quaternion::<i128>::K)
1558        );
1559    }
1560
1561    #[cfg(feature = "serde")]
1562    #[test]
1563    fn test_serde_euler_angles() {
1564        // Create a sample angles
1565        let angles = EulerAngles {
1566            roll: 1.0,
1567            pitch: 2.0,
1568            yaw: 3.0,
1569        };
1570
1571        // Serialize the angles to a JSON string
1572        let serialized =
1573            serde_json::to_string(&angles).expect("Failed to serialize angles");
1574
1575        // Deserialize the JSON string back into angles
1576        let deserialized: EulerAngles<f64> = serde_json::from_str(&serialized)
1577            .expect("Failed to deserialize angles");
1578
1579        // Assert that the deserialized angles are equal to the original
1580        assert_eq!(angles, deserialized);
1581    }
1582
1583    #[cfg(any(feature = "std", feature = "libm"))]
1584    #[test]
1585    fn test_from_euler_angles() {
1586        // Test the conversion from Euler angles to quaternions
1587        assert!(
1588            (UQ32::from_euler_angles(core::f32::consts::PI, 0.0, 0.0)
1589                .into_quaternion()
1590                - Q32::I)
1591                .norm()
1592                < f32::EPSILON
1593        );
1594        assert!(
1595            (UQ64::from_euler_angles(0.0, core::f64::consts::PI, 0.0)
1596                .into_quaternion()
1597                - Q64::J)
1598                .norm()
1599                < f64::EPSILON
1600        );
1601        assert!(
1602            (UQ32::from_euler_angles(0.0, 0.0, core::f32::consts::PI)
1603                .into_quaternion()
1604                - Q32::K)
1605                .norm()
1606                < f32::EPSILON
1607        );
1608        assert!(
1609            (UQ64::from_euler_angles(1.0, 2.0, 3.0).into_quaternion()
1610                - (UQ64::from_euler_angles(0.0, 0.0, 3.0)
1611                    * UQ64::from_euler_angles(0.0, 2.0, 0.0)
1612                    * UQ64::from_euler_angles(1.0, 0.0, 0.0))
1613                .into_quaternion())
1614            .norm()
1615                < 4.0 * f64::EPSILON
1616        );
1617    }
1618
1619    #[cfg(all(
1620        feature = "unstable",
1621        feature = "rand",
1622        any(feature = "std", feature = "libm")
1623    ))]
1624    #[test]
1625    fn test_from_euler_angles_struct() {
1626        // Test the conversion from Euler angles to quaternions using the
1627        // function `from_euler_angles_struct`.
1628        let angles = EulerAngles {
1629            roll: 1.0,
1630            pitch: 2.0,
1631            yaw: 3.0,
1632        };
1633        let q = UQ64::from_euler_angles_struct(angles);
1634        let expected = UQ64::from_euler_angles(1.0, 2.0, 3.0);
1635        assert_eq!(q, expected);
1636    }
1637
1638    #[cfg(any(feature = "std", feature = "libm"))]
1639    #[test]
1640    fn test_to_euler_angles() {
1641        // Test the conversion from quaternions to Euler angles
1642        let test_data = [
1643            Q64::new(1.0, 0.0, 0.0, 0.0),   // identity
1644            Q64::new(0.0, 1.0, 0.0, 0.0),   // 180 degree x axis
1645            Q64::new(0.0, 0.0, 1.0, 0.0),   // 180 degree y axis
1646            Q64::new(0.0, 0.0, 0.0, 1.0),   // 180 degree z axis
1647            Q64::new(1.0, 1.0, 1.0, 1.0),   // 120 degree xyz
1648            Q64::new(1.0, -2.0, 3.0, -4.0), // arbitrary
1649            Q64::new(4.0, 3.0, 2.0, 1.0),   // arbitrary
1650            Q64::new(1.0, 0.0, 1.0, 0.0),   // gimbal lock 1
1651            Q64::new(1.0, 1.0, 1.0, -1.0),  // gimbal lock 2
1652            Q64::new(1.0, 0.0, -1.0, 0.0),  // gimbal lock 3
1653            Q64::new(1.0, 1.0, -1.0, 1.0),  // gimbal lock 4
1654        ];
1655        for q in test_data.into_iter().map(|q| q.normalize().unwrap()) {
1656            let EulerAngles { roll, pitch, yaw } = q.to_euler_angles();
1657            let p = UQ64::from_euler_angles(roll, pitch, yaw);
1658            assert!((p - q).norm() < f64::EPSILON);
1659        }
1660    }
1661
1662    #[cfg(any(feature = "std", feature = "libm"))]
1663    #[test]
1664    fn test_from_rotation_vector() {
1665        // Test the conversion from rotation vectors to quaternions
1666        assert!(
1667            (UQ32::from_rotation_vector(&[core::f32::consts::PI, 0.0, 0.0])
1668                - Q32::I)
1669                .norm()
1670                < f32::EPSILON
1671        );
1672        assert!(
1673            (UQ64::from_rotation_vector(&[0.0, core::f64::consts::PI, 0.0])
1674                - Q64::J)
1675                .norm()
1676                < f64::EPSILON
1677        );
1678        assert!(
1679            (UQ32::from_rotation_vector(&[0.0, 0.0, core::f32::consts::PI])
1680                - Q32::K)
1681                .norm()
1682                < f32::EPSILON
1683        );
1684        let x = 2.0 * core::f64::consts::FRAC_PI_3 * (1.0f64 / 3.0).sqrt();
1685        assert!(
1686            (UQ64::from_rotation_vector(&[x, x, x])
1687                - Q64::new(0.5, 0.5, 0.5, 0.5))
1688            .norm()
1689                < 4.0 * f64::EPSILON
1690        );
1691        assert!(
1692            (UQ64::from_rotation_vector(&[-x, x, -x])
1693                - Q64::new(0.5, -0.5, 0.5, -0.5))
1694            .norm()
1695                < 4.0 * f64::EPSILON
1696        );
1697    }
1698
1699    #[cfg(any(feature = "std", feature = "libm"))]
1700    #[test]
1701    fn test_from_rotation_vector_infinite() {
1702        // Test `from_rotation_vector` for a vector with infinite components.
1703        let inf = f32::INFINITY;
1704        assert!(UQ32::from_rotation_vector(&[inf, 0.0, 0.0])
1705            .into_inner()
1706            .is_all_nan());
1707        assert!(UQ32::from_rotation_vector(&[inf, inf, inf])
1708            .into_inner()
1709            .is_all_nan());
1710    }
1711
1712    #[cfg(any(feature = "std", feature = "libm"))]
1713    #[test]
1714    fn test_from_rotation_vector_nan_input() {
1715        // Test `from_rotation_vector` for a vector with infinite components.
1716        let nan = f64::NAN;
1717        assert!(UQ64::from_rotation_vector(&[nan, 0.0, 0.0])
1718            .into_inner()
1719            .is_all_nan());
1720        assert!(UQ64::from_rotation_vector(&[nan, nan, nan])
1721            .into_inner()
1722            .is_all_nan());
1723    }
1724
1725    #[cfg(any(feature = "std", feature = "libm"))]
1726    #[test]
1727    fn test_to_rotation_vector_zero_rotation() {
1728        // Quaternion representing no rotation (identity quaternion)
1729        assert_eq!(UQ32::ONE.to_rotation_vector(), [0.0, 0.0, 0.0]);
1730        assert_eq!(UQ64::ONE.to_rotation_vector(), [0.0, 0.0, 0.0]);
1731    }
1732
1733    #[cfg(any(feature = "std", feature = "libm"))]
1734    #[test]
1735    fn test_to_rotation_vector_90_degree_rotation_x_axis() {
1736        // Quaternion representing a 90-degree rotation around the x-axis
1737        let q = Q32::new(1.0, 1.0, 0.0, 0.0).normalize().unwrap();
1738        let rotation_vector = q.to_rotation_vector();
1739        assert!(
1740            (rotation_vector[0] - core::f32::consts::FRAC_PI_2).abs()
1741                < f32::EPSILON
1742        );
1743        assert!((rotation_vector[1]).abs() < f32::EPSILON);
1744        assert!((rotation_vector[2]).abs() < f32::EPSILON);
1745    }
1746
1747    #[cfg(any(feature = "std", feature = "libm"))]
1748    #[test]
1749    fn test_to_rotation_vector_180_degree_rotation_y_axis() {
1750        // Quaternion representing a 180-degree rotation around the x-axis
1751        let q = UQ64::J;
1752        let rotation_vector = q.to_rotation_vector();
1753        assert!((rotation_vector[0]).abs() < f64::EPSILON);
1754        assert!(
1755            (rotation_vector[1] - core::f64::consts::PI).abs() < f64::EPSILON
1756        );
1757        assert!((rotation_vector[2]).abs() < f64::EPSILON);
1758    }
1759
1760    #[cfg(any(feature = "std", feature = "libm"))]
1761    #[test]
1762    fn test_to_rotation_vector_180_degree_rotation_arbitrary_axis() {
1763        // Quaternion representing a 180-degree rotation around an arbitrary axis
1764        let q = Q32::new(0.0, 1.0, 1.0, 1.0).normalize().unwrap();
1765        let rotation_vector = q.to_rotation_vector();
1766        let expected = [
1767            core::f32::consts::PI / (1.0f32 + 1.0 + 1.0).sqrt(),
1768            core::f32::consts::PI / (1.0f32 + 1.0 + 1.0).sqrt(),
1769            core::f32::consts::PI / (1.0f32 + 1.0 + 1.0).sqrt(),
1770        ];
1771        assert!((rotation_vector[0] - expected[0]).abs() < 4.0 * f32::EPSILON);
1772        assert!((rotation_vector[1] - expected[1]).abs() < 4.0 * f32::EPSILON);
1773        assert!((rotation_vector[2] - expected[2]).abs() < 4.0 * f32::EPSILON);
1774    }
1775
1776    #[cfg(any(feature = "std", feature = "libm"))]
1777    #[test]
1778    fn test_to_rotation_vector_small_rotation() {
1779        // Quaternion representing a small rotation
1780        let angle = 1e-6f32;
1781        let q = Q32::new((angle / 2.0).cos(), (angle / 2.0).sin(), 0.0, 0.0)
1782            .normalize()
1783            .unwrap();
1784        let rotation_vector = q.to_rotation_vector();
1785        assert!((rotation_vector[0] - angle).abs() < f32::EPSILON);
1786        assert!((rotation_vector[1]).abs() < f32::EPSILON);
1787        assert!((rotation_vector[2]).abs() < f32::EPSILON);
1788    }
1789
1790    #[cfg(any(feature = "std", feature = "libm"))]
1791    #[test]
1792    fn test_to_rotation_vector_general_case() {
1793        // Quaternion representing a general rotation
1794        // Here we first compute the rotation vector and then
1795        // check if `from_rotation_vector` restores the original
1796        // quaternion appropriately.
1797        let min_pos = f64::MIN_POSITIVE;
1798        for q in [
1799            [1.0, 0.0, 0.0, 0.0],
1800            [0.0, 0.0, 0.0, 1.0],
1801            [1.0, 1.0, 0.0, 0.0],
1802            [1.0, 1.0, 1.0, 0.0],
1803            [1.0, 1.0, 1.0, 1.0],
1804            [0.0, 1.0, 1.0, -1.0],
1805            [1.0, 0.0, 2.0, 5.0],
1806            [1.0, 0.0, 1.0e-10, 2.0e-10],
1807            [-1.0, 0.0, 0.0, 0.0],
1808            [1.0, f64::EPSILON, 0.0, 0.0],
1809            [1.0, 0.0, 0.0, min_pos],
1810            [-1.0, 3.0 * min_pos, 2.0 * min_pos, min_pos],
1811            [1.0, 0.1, 0.0, 0.0],
1812            [-1.0, 0.0, 0.1, 0.0],
1813        ]
1814        .into_iter()
1815        .map(|[w, x, y, z]| Q64::new(w, x, y, z).normalize().unwrap())
1816        {
1817            let rot = q.to_rotation_vector();
1818            let p = UQ64::from_rotation_vector(&rot);
1819            assert!((p - q).norm() <= 6.0 * f64::EPSILON);
1820        }
1821    }
1822
1823    #[test]
1824    fn test_rotation_matrix_identity() {
1825        // Test the rotation matrix of the identity quaternion
1826        let q = UQ64::ONE;
1827        let rot_matrix = q.to_rotation_matrix3x3();
1828        let expected = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0];
1829        assert_eq!(rot_matrix, expected);
1830    }
1831
1832    #[cfg(any(feature = "std", feature = "libm"))]
1833    #[test]
1834    fn test_rotation_matrix_90_degrees_x() {
1835        // Test the rotation matrix of a 90-degree rotation around the x-axis
1836        let q = Q64::new(1.0, 1.0, 0.0, 0.0).normalize().unwrap();
1837        let rot_matrix = q.to_rotation_matrix3x3();
1838        let expected = [
1839            1.0, 0.0, 0.0, //
1840            0.0, 0.0, -1.0, //
1841            0.0, 1.0, 0.0,
1842        ];
1843        for (r, e) in rot_matrix.iter().zip(expected) {
1844            assert!((r - e).abs() <= f64::EPSILON);
1845        }
1846    }
1847
1848    #[cfg(any(feature = "std", feature = "libm"))]
1849    #[test]
1850    fn test_rotation_matrix_90_degrees_y() {
1851        // Test the rotation matrix of a 90-degree rotation around the y-axis
1852        let q = Q64::new(1.0, 0.0, 1.0, 0.0).normalize().unwrap();
1853        let rot_matrix = q.to_rotation_matrix3x3();
1854        let expected = [
1855            0.0, 0.0, 1.0, //
1856            0.0, 1.0, 0.0, //
1857            -1.0, 0.0, 0.0,
1858        ];
1859        for (r, e) in rot_matrix.iter().zip(expected) {
1860            assert!((r - e).abs() <= f64::EPSILON);
1861        }
1862    }
1863
1864    #[cfg(any(feature = "std", feature = "libm"))]
1865    #[test]
1866    fn test_rotation_matrix_90_degrees_z() {
1867        // Test the rotation matrix of a 90-degree rotation around the z-axis
1868        let q = Q64::new(1.0, 0.0, 0.0, 1.0).normalize().unwrap();
1869        let rot_matrix = q.to_rotation_matrix3x3();
1870        let expected = [
1871            0.0, -1.0, 0.0, //
1872            1.0, 0.0, 0.0, //
1873            0.0, 0.0, 1.0,
1874        ];
1875        for (r, e) in rot_matrix.iter().zip(expected) {
1876            assert!((r - e).abs() <= f64::EPSILON);
1877        }
1878    }
1879
1880    #[cfg(any(feature = "std", feature = "libm"))]
1881    #[test]
1882    fn test_rotation_matrix_120_degrees_xyz() {
1883        // Test the rotation matrix of a 120-degree rotation which rotates the
1884        // x-axis onto the y-axis, the y-axis onto the z-axis, and the z-axis
1885        // onto the x-axis
1886        let q = Q64::new(1.0, 1.0, 1.0, 1.0).normalize().unwrap();
1887        let rot_matrix = q.to_rotation_matrix3x3();
1888        let expected = [
1889            0.0, 0.0, 1.0, //
1890            1.0, 0.0, 0.0, //
1891            0.0, 1.0, 0.0,
1892        ];
1893        for (r, e) in rot_matrix.iter().zip(expected) {
1894            assert!((r - e).abs() <= f64::EPSILON);
1895        }
1896    }
1897
1898    #[cfg(any(feature = "std", feature = "libm"))]
1899    #[test]
1900    fn test_rotation_matrix_general() {
1901        // Test the rotation matrix of a general rotation
1902        let q = Q64::new(-1.0, 2.0, -3.0, 4.0).normalize().unwrap();
1903        let rot_matrix = q.to_rotation_matrix3x3();
1904        let [x1, y1, z1] = q.rotate_vector([1.0, 0.0, 0.0]);
1905        let [x2, y2, z2] = q.rotate_vector([0.0, 1.0, 0.0]);
1906        let [x3, y3, z3] = q.rotate_vector([0.0, 0.0, 1.0]);
1907        let expected = [
1908            x1, x2, x3, //
1909            y1, y2, y3, //
1910            z1, z2, z3,
1911        ];
1912        for (r, e) in rot_matrix.iter().zip(expected) {
1913            assert!((r - e).abs() <= f64::EPSILON);
1914        }
1915    }
1916
1917    #[cfg(any(feature = "std", feature = "libm"))]
1918    #[test]
1919    fn test_identity_matrix() {
1920        // Test the quaternion corresponding to the identity matrix
1921        let identity: [[f32; 3]; 3] =
1922            [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
1923        let q = UQ32::from_rotation_matrix3x3(&identity);
1924        let expected = UQ32::ONE;
1925        assert_eq!(q, expected);
1926    }
1927
1928    #[cfg(any(feature = "std", feature = "libm"))]
1929    #[test]
1930    fn test_rotation_x() {
1931        // Test the quaternion corresponding to a rotation around the x-axis
1932        let angle = core::f32::consts::PI / 5.0;
1933        let rotation_x: [[f32; 3]; 3] = [
1934            [1.0, 0.0, 0.0],
1935            [0.0, angle.cos(), -angle.sin()],
1936            [0.0, angle.sin(), angle.cos()],
1937        ];
1938        let q = UQ32::from_rotation_matrix3x3(&rotation_x);
1939        let expected = UQ32::from_rotation_vector(&[angle, 0.0, 0.0]);
1940        assert!((q - expected).norm() <= f32::EPSILON);
1941    }
1942
1943    #[cfg(any(feature = "std", feature = "libm"))]
1944    #[test]
1945    fn test_rotation_y() {
1946        // Test the quaternion corresponding to a rotation around the y-axis
1947        let angle = 4.0 * core::f32::consts::PI / 5.0;
1948        let rotation_y: [[f32; 3]; 3] = [
1949            [angle.cos(), 0.0, angle.sin()],
1950            [0.0, 1.0, 0.0],
1951            [-angle.sin(), 0.0, angle.cos()],
1952        ];
1953        let q = UQ32::from_rotation_matrix3x3(&rotation_y);
1954        let expected = UQ32::from_rotation_vector(&[0.0, angle, 0.0]);
1955        assert!((q - expected).norm() <= f32::EPSILON);
1956    }
1957
1958    #[cfg(any(feature = "std", feature = "libm"))]
1959    #[test]
1960    fn test_rotation_z() {
1961        // Test the quaternion corresponding to a rotation around the z-axis
1962        let angle = 3.0 * core::f64::consts::PI / 5.0;
1963        let rotation_z: [[f64; 3]; 3] = [
1964            [angle.cos(), -angle.sin(), 0.0],
1965            [angle.sin(), angle.cos(), 0.0],
1966            [0.0, 0.0, 1.0],
1967        ];
1968        let q = UQ64::from_rotation_matrix3x3(&rotation_z);
1969        let expected = UQ64::from_rotation_vector(&[0.0, 0.0, angle]);
1970        assert!((q - expected).norm() <= f64::EPSILON);
1971    }
1972
1973    #[cfg(any(feature = "std", feature = "libm"))]
1974    #[test]
1975    fn test_arbitrary_rotation() {
1976        // Test the quaternion corresponding to an arbitrary rotation matrix
1977        let arbitrary_rotation: [[f32; 3]; 3] =
1978            [[0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
1979        let q = UnitQuaternion::from_rotation_matrix3x3(&arbitrary_rotation);
1980        let expected = Q32::new(1.0, 1.0, 1.0, 1.0).normalize().unwrap();
1981        assert!((q - expected).norm() <= f32::EPSILON);
1982    }
1983
1984    #[cfg(any(feature = "std", feature = "libm"))]
1985    #[test]
1986    fn test_flat_array() {
1987        // Test the conversion between a flat array and a quaternion
1988        let angle = core::f32::consts::PI / 2.0;
1989        let rotation_z: [f32; 9] = [
1990            0.0, -1.0, 0.0, //
1991            1.0, 0.0, 0.0, //
1992            0.0, 0.0, 1.0,
1993        ];
1994        let q = UQ32::from_rotation_matrix3x3(&rotation_z);
1995        let expected = UQ32::from_rotation_vector(&[0.0, 0.0, angle]);
1996        assert!((q - expected).norm() <= f32::EPSILON);
1997    }
1998
1999    #[cfg(any(feature = "std", feature = "libm"))]
2000    #[test]
2001    fn test_from_rotation_to_rotation() {
2002        // Test the conversion from a rotation matrix to a quaternion and back
2003        let mat = [
2004            0.36f64, 0.864, -0.352, 0.48, 0.152, 0.864, 0.8, -0.48, -0.36,
2005        ];
2006        let q = UnitQuaternion::from_rotation_matrix3x3(&mat);
2007        let restored_mat = q.to_rotation_matrix3x3();
2008        for (x, e) in restored_mat.iter().zip(mat) {
2009            assert!((x - e).abs() <= 4.0 * f64::EPSILON);
2010        }
2011    }
2012
2013    #[cfg(all(feature = "rand", any(feature = "std", feature = "libm")))]
2014    #[test]
2015    fn test_to_rotation_from_rotation() {
2016        // Test the conversion from a unit quaternion to a 3x3 matrix and back
2017        // in a randomized way.
2018        use rand::Rng;
2019        let mut rng = make_seeded_rng();
2020        for _ in 0..100000 {
2021            let q = rng.random::<UQ32>();
2022            let mat = q.to_rotation_matrix3x3();
2023            let restored_q = UQ32::from_rotation_matrix3x3(&mat);
2024            assert!(restored_q.0.w >= 0.0);
2025            let expected = if q.0.w >= 0.0 { q } else { -q };
2026            if (restored_q - expected).norm() > 4.0 * f32::EPSILON {
2027                assert!((restored_q - expected).norm() <= 8.0 * f32::EPSILON);
2028            }
2029        }
2030    }
2031
2032    #[cfg(any(feature = "std", feature = "libm"))]
2033    #[test]
2034    fn test_zero_vector_a() {
2035        // Test `from_two_vectors` for the case where the first vector is the
2036        // zero vector
2037        let a = [0.0, 0.0, 0.0];
2038        let b = [1.0, 0.0, 0.0];
2039        let q = UnitQuaternion::from_two_vectors(&a, &b);
2040        assert_eq!(q, UnitQuaternion::one());
2041    }
2042
2043    #[cfg(any(feature = "std", feature = "libm"))]
2044    #[test]
2045    fn test_zero_vector_b() {
2046        // Test `from_two_vectors` for the case where the second vector is the
2047        // zero vector
2048        let a = [1.0, 0.0, 0.0];
2049        let b = [0.0, 0.0, 0.0];
2050        let q = UnitQuaternion::from_two_vectors(&a, &b);
2051        assert_eq!(q, UnitQuaternion::one());
2052    }
2053
2054    #[cfg(any(feature = "std", feature = "libm"))]
2055    #[test]
2056    fn test_parallel_vectors() {
2057        // Test `from_two_vectors` for the case where the vectors are parallel
2058        let a = [1.0, 0.0, 0.0];
2059        let b = [2.0, 0.0, 0.0];
2060        let q = UnitQuaternion::from_two_vectors(&a, &b);
2061        assert_eq!(q, UnitQuaternion::one());
2062    }
2063
2064    #[cfg(any(feature = "std", feature = "libm"))]
2065    #[test]
2066    fn test_opposite_vectors() {
2067        // Test `from_two_vectors` for the case where the vectors are opposite
2068        let a = [1.0, 0.0, 0.0];
2069        let b = [-1.0, 0.0, 0.0];
2070        let q = UnitQuaternion::from_two_vectors(&a, &b);
2071        assert_eq!(q.as_quaternion().w, 0.0);
2072    }
2073
2074    #[cfg(all(feature = "rand", any(feature = "std", feature = "libm")))]
2075    #[test]
2076    fn test_opposite_vectors_randomized() {
2077        // Test `from_two_vectors` for the case where the vectors are opposite
2078        // in a randomized way.
2079        use rand::Rng;
2080        let mut rng = make_seeded_rng();
2081        let mut gen_coord = move || rng.random::<f32>() * 2.0 - 1.0;
2082        for _ in 0..10000 {
2083            let a = [gen_coord(), gen_coord(), gen_coord()];
2084            let b = [-a[0], -a[1], -a[2]];
2085            let q = UnitQuaternion::from_two_vectors(&a, &b);
2086            assert!(q.as_quaternion().w.abs() <= f32::EPSILON);
2087        }
2088    }
2089
2090    #[cfg(any(feature = "std", feature = "libm"))]
2091    #[test]
2092    fn test_perpendicular_vectors() {
2093        // Test `from_two_vectors` for the case where the vectors are
2094        // perpendicular
2095        let a = [1.0f32, 0.0, 0.0];
2096        let b = [0.0f32, 1.0, 0.0];
2097        let q = UQ32::from_two_vectors(&a, &b);
2098        let expected = Q32::new(1.0, 0.0, 0.0, 1.0).normalize().unwrap();
2099        assert!((q - expected).norm() <= 2.0 * f32::EPSILON);
2100    }
2101
2102    #[cfg(any(feature = "std", feature = "libm"))]
2103    #[test]
2104    fn test_non_normalized_vectors() {
2105        // Test `from_two_vectors` for the case where the vectors are not
2106        // normalized
2107        let a = [0.0, 3.0, 0.0];
2108        let b = [0.0, 5.0, 5.0];
2109        let q = UQ64::from_two_vectors(&a, &b);
2110        let expected =
2111            Q64::new(1.0, core::f64::consts::FRAC_PI_8.tan(), 0.0, 0.0)
2112                .normalize()
2113                .unwrap();
2114        assert!((q - expected).norm() <= 2.0 * f64::EPSILON);
2115
2116        let a = [0.0, 3.0, 0.0];
2117        let b = [0.0, -5.0, 5.0];
2118        let q = UQ64::from_two_vectors(&a, &b);
2119        let expected =
2120            Q64::new(1.0, (3.0 * core::f64::consts::FRAC_PI_8).tan(), 0.0, 0.0)
2121                .normalize()
2122                .unwrap();
2123        assert!((q - expected).norm() <= 2.0 * f64::EPSILON);
2124    }
2125
2126    #[cfg(any(feature = "std", feature = "libm"))]
2127    #[test]
2128    fn test_same_vector() {
2129        // Test `from_two_vectors` for the case where the vectors are the same
2130        let a = [1.0, 1.0, 1.0];
2131        let q = UnitQuaternion::from_two_vectors(&a, &a);
2132        assert_eq!(q, UnitQuaternion::one());
2133    }
2134
2135    #[cfg(any(feature = "std", feature = "libm"))]
2136    #[test]
2137    fn test_arbitrary_vectors() {
2138        // Test `from_two_vectors` for arbitrary vectors
2139        let a = [1.0, 2.0, 3.0];
2140        let b = [4.0, 5.0, 6.0];
2141        let q = UQ64::from_two_vectors(&a, &b);
2142        let v = [-3.0, 6.0, -3.0]; // cross product
2143        let v_norm = 54.0f64.sqrt();
2144        let dir = [v[0] / v_norm, v[1] / v_norm, v[2] / v_norm];
2145        let cos_angle = (a[0] * b[0] + a[1] * b[1] + a[2] * b[2])
2146            / ((a[0] * a[0] + a[1] * a[1] + a[2] * a[2])
2147                * (b[0] * b[0] + b[1] * b[1] + b[2] * b[2]))
2148                .sqrt();
2149        let angle = cos_angle.acos();
2150        let expected = UQ64::from_rotation_vector(&[
2151            dir[0] * angle,
2152            dir[1] * angle,
2153            dir[2] * angle,
2154        ]);
2155        assert!((q - expected).norm() <= 2.0 * f64::EPSILON);
2156    }
2157
2158    #[cfg(all(feature = "rand", any(feature = "std", feature = "libm")))]
2159    #[test]
2160    fn test_from_to_vectors_randomized() {
2161        // Test `from_two_vectors` in a randomized way.
2162        use rand::Rng;
2163        let mut rng = make_seeded_rng();
2164        let mut gen_coord = move || rng.random::<f32>() * 2.0 - 1.0;
2165        for _ in 0..100000 {
2166            let a = [gen_coord(), gen_coord(), gen_coord()];
2167            let b = [gen_coord(), gen_coord(), gen_coord()];
2168            let q = UQ32::from_two_vectors(&a, &b);
2169            let rotated_a = q.rotate_vector(a);
2170            let dot =
2171                rotated_a[0] * b[0] + rotated_a[1] * b[1] + rotated_a[2] * b[2];
2172            let b_norm_sqr = b[0] * b[0] + b[1] * b[1] + b[2] * b[2];
2173            let a_norm_sqr = a[0] * a[0] + a[1] * a[1] + a[2] * a[2];
2174            let expected = (a_norm_sqr * b_norm_sqr).sqrt();
2175            let cos_angle = dot / expected;
2176            assert!((cos_angle - 1.0).abs() <= 8.0 * f32::EPSILON);
2177        }
2178    }
2179
2180    #[test]
2181    fn test_default_unit_quaternion() {
2182        // Test that the default unit quaternion is equal to the identity
2183        assert_eq!(UQ32::default().into_quaternion(), Q32::ONE);
2184    }
2185
2186    #[test]
2187    fn test_constant_one() {
2188        // Test that the constant `ONE` is equal to the identity quaternion
2189        assert_eq!(UQ32::ONE.into_quaternion(), Q32::ONE);
2190        assert_eq!(
2191            UnitQuaternion::<i32>::ONE.into_quaternion(),
2192            Quaternion::<i32>::ONE
2193        );
2194    }
2195
2196    #[test]
2197    fn test_constant_i() {
2198        // Test that the constant unit quaternion `I` is equal to the
2199        // quaternion `I`
2200        assert_eq!(UQ32::I.into_quaternion(), Q32::I);
2201    }
2202
2203    #[test]
2204    fn test_constant_j() {
2205        // Test that the constant unit quaternion `J` is equal to the
2206        // quaternion `J`
2207        assert_eq!(UQ32::J.into_quaternion(), Q32::J);
2208    }
2209
2210    #[test]
2211    fn test_constant_k() {
2212        // Test that the constant unit quaternion `K` is equal to the
2213        // quaternion `K`
2214        assert_eq!(UQ32::K.into_quaternion(), Q32::K);
2215    }
2216
2217    #[test]
2218    fn test_const_one() {
2219        // Test that the constant `ONE` is equal to the identity quaternion
2220        assert_eq!(<UQ32 as ConstOne>::ONE.into_quaternion(), Q32::ONE);
2221    }
2222
2223    #[test]
2224    fn test_one_trait() {
2225        // Test the functions of the `One` trait for `UnitQuaternion<T>`
2226        assert_eq!(<UQ32 as One>::one().into_quaternion(), Q32::ONE);
2227        assert!(UQ64::ONE.is_one());
2228        assert!(!UQ64::I.is_one());
2229        assert!(!UQ64::J.is_one());
2230        assert!(!UQ64::K.is_one());
2231        let mut uq = UQ32::I;
2232        uq.set_one();
2233        assert!(uq.is_one());
2234    }
2235
2236    #[test]
2237    fn test_one_func() {
2238        // Test the `one` method of the `UnitQuaternion` struct
2239        assert_eq!(UQ32::one().into_quaternion(), Q32::ONE);
2240    }
2241
2242    #[test]
2243    fn test_unit_quaternion_i_func() {
2244        // Test the `i` method of the `UnitQuaternion` struct
2245        assert_eq!(UQ32::i().into_quaternion(), Q32::i());
2246    }
2247
2248    #[test]
2249    fn test_unit_quaternion_j_func() {
2250        // Test the `j` method of the `UnitQuaternion` struct
2251        assert_eq!(UQ32::j().into_quaternion(), Q32::j());
2252    }
2253
2254    #[test]
2255    fn test_unit_quaternion_k_func() {
2256        // Test the `k` method of the `UnitQuaternion` struct
2257        assert_eq!(UQ32::k().into_quaternion(), Q32::k());
2258    }
2259
2260    #[test]
2261    fn test_into_quaternion() {
2262        // Test that the conversion from a unit quaternion to a quaternion
2263        // is correct.
2264        assert_eq!(UQ32::ONE.into_quaternion(), Q32::ONE);
2265        assert_eq!(UQ32::I.into_quaternion(), Q32::I);
2266        assert_eq!(UQ32::J.into_quaternion(), Q32::J);
2267        assert_eq!(UQ32::K.into_quaternion(), Q32::K);
2268    }
2269
2270    #[test]
2271    fn test_into_inner() {
2272        // Test that the conversion from a unit quaternion to a quaternion
2273        // is correct.
2274        assert_eq!(UQ32::ONE.into_inner(), Q32::ONE);
2275        assert_eq!(UQ32::I.into_inner(), Q32::I);
2276        assert_eq!(UQ32::J.into_inner(), Q32::J);
2277        assert_eq!(UQ32::K.into_inner(), Q32::K);
2278    }
2279
2280    #[test]
2281    fn test_as_quaternion() {
2282        // Test that the conversion from a unit quaternion to a quaternion
2283        // is correct.
2284        assert_eq!(UQ32::ONE.as_quaternion(), &Q32::ONE);
2285        assert_eq!(UQ32::I.as_quaternion(), &Q32::I);
2286        assert_eq!(UQ32::J.as_quaternion(), &Q32::J);
2287        assert_eq!(UQ32::K.as_quaternion(), &Q32::K);
2288    }
2289
2290    #[test]
2291    fn test_borrow() {
2292        // Test that the conversion from a unit quaternion to a quaternion
2293        // is correct.
2294        assert_eq!(<UQ32 as Borrow<Q32>>::borrow(&UQ32::ONE), &Q32::ONE);
2295        assert_eq!(<UQ32 as Borrow<Q32>>::borrow(&UQ32::I), &Q32::I);
2296        assert_eq!(<UQ32 as Borrow<Q32>>::borrow(&UQ32::J), &Q32::J);
2297        assert_eq!(<UQ32 as Borrow<Q32>>::borrow(&UQ32::K), &Q32::K);
2298    }
2299
2300    #[test]
2301    fn test_unit_quaternion_conj() {
2302        // Test the conjugate of unit quaternions
2303        assert_eq!(UQ32::ONE.conj(), UQ32::ONE);
2304        assert_eq!(UQ64::I.conj(), -UQ64::I);
2305        assert_eq!(UQ32::J.conj(), -UQ32::J);
2306        assert_eq!(UQ64::K.conj(), -UQ64::K);
2307    }
2308
2309    #[cfg(any(feature = "std", feature = "libm"))]
2310    #[test]
2311    fn test_unit_quaternion_conj_with_normalize() {
2312        // Test the conjugate of unit quaternions
2313        assert_eq!(
2314            Q32::new(1.0, 2.0, 3.0, 4.0).normalize().unwrap().conj(),
2315            Q32::new(1.0, -2.0, -3.0, -4.0).normalize().unwrap()
2316        )
2317    }
2318
2319    #[cfg(any(feature = "std", feature = "libm"))]
2320    #[test]
2321    fn test_unit_quaternion_inv_func() {
2322        // Test the inverse of unit quaternions
2323        assert_eq!(
2324            UQ32::inv(&Q32::new(1.0, 2.0, 3.0, 4.0).normalize().unwrap()),
2325            Q32::new(1.0, 2.0, 3.0, 4.0).normalize().unwrap().conj()
2326        )
2327    }
2328
2329    #[cfg(any(feature = "std", feature = "libm"))]
2330    #[test]
2331    fn test_unit_quaternion_inv_trait() {
2332        // Test the `Inv` trait for unit quaternions
2333        assert_eq!(
2334            <UQ32 as Inv>::inv(
2335                Q32::new(1.0, 2.0, 3.0, 4.0).normalize().unwrap()
2336            ),
2337            Q32::new(1.0, 2.0, 3.0, 4.0).normalize().unwrap().conj()
2338        )
2339    }
2340
2341    #[cfg(any(feature = "std", feature = "libm"))]
2342    #[test]
2343    fn test_unit_quaternion_ref_inv_trait() {
2344        // Test the `Inv` trait for unit quaternion references
2345        assert_eq!(
2346            <&UQ32 as Inv>::inv(
2347                &Q32::new(1.0, 2.0, 3.0, 4.0).normalize().unwrap()
2348            ),
2349            Q32::new(1.0, 2.0, 3.0, 4.0).normalize().unwrap().conj()
2350        )
2351    }
2352
2353    #[test]
2354    fn test_unit_quaternion_neg() {
2355        // Test the negation of unit quaternions
2356        assert_eq!(
2357            (-UQ32::ONE).into_quaternion(),
2358            Q32::new(-1.0, 0.0, 0.0, 0.0)
2359        );
2360        assert_eq!((-UQ32::I).into_quaternion(), Q32::new(0.0, -1.0, 0.0, 0.0));
2361        assert_eq!((-UQ32::J).into_quaternion(), Q32::new(0.0, 0.0, -1.0, 0.0));
2362        assert_eq!((-UQ32::K).into_quaternion(), Q32::new(0.0, 0.0, 0.0, -1.0));
2363    }
2364
2365    #[cfg(any(feature = "std", feature = "libm"))]
2366    #[test]
2367    fn test_unit_quaternion_adjust_norm() {
2368        // Test the adjustment of the norm of unit quaternions
2369        let mut q = UQ32::from_euler_angles(1.0, 0.5, 1.5);
2370        for _ in 0..25 {
2371            q = q * q;
2372        }
2373        assert!((q.into_quaternion().norm() - 1.0).abs() > 0.5);
2374        assert!(
2375            (q.adjust_norm().into_quaternion().norm() - 1.0).abs()
2376                <= 2.0 * f32::EPSILON
2377        );
2378    }
2379
2380    #[test]
2381    fn test_unit_quaternion_rotate_vector_units() {
2382        // Test the rotation of unit vectors by unit quaternions
2383        let v = [1.0, 2.0, 3.0];
2384        assert_eq!(UQ32::I.rotate_vector(v), [1.0, -2.0, -3.0]);
2385        assert_eq!(UQ32::J.rotate_vector(v), [-1.0, 2.0, -3.0]);
2386        assert_eq!(UQ32::K.rotate_vector(v), [-1.0, -2.0, 3.0]);
2387    }
2388
2389    #[cfg(any(feature = "std", feature = "libm"))]
2390    #[test]
2391    fn test_unit_quaternion_rotate_vector_normalized() {
2392        // Test the rotation of normalized vectors by unit quaternions
2393        let q = Q32::new(1.0, 1.0, 1.0, 1.0).normalize().unwrap();
2394        let v = [1.0, 2.0, 3.0];
2395        let result = q.rotate_vector(v);
2396        assert_eq!(result, [3.0, 1.0, 2.0]);
2397    }
2398
2399    // Generates an iterator over unit quaternion test data
2400    #[cfg(any(feature = "std", feature = "libm"))]
2401    fn generate_unit_quaternion_data() -> impl Iterator<Item = UQ32> {
2402        [
2403            UQ32::ONE,
2404            UQ32::I,
2405            UQ32::J,
2406            UQ32::K,
2407            Q32::new(1.0, 1.0, 1.0, 1.0).normalize().unwrap(),
2408            Q32::new(10.0, 1.0, 1.0, 1.0).normalize().unwrap(),
2409            Q32::new(1.0, 10.0, 1.0, 1.0).normalize().unwrap(),
2410            Q32::new(1.0, 1.0, 3.0, 4.0).normalize().unwrap(),
2411            Q32::new(1.0, -1.0, 3.0, -4.0).normalize().unwrap(),
2412        ]
2413        .into_iter()
2414    }
2415
2416    #[cfg(any(feature = "std", feature = "libm"))]
2417    #[test]
2418    fn test_slerp_t_zero() {
2419        // Test the spherical linear interpolation of unit quaternions
2420        // with `t = 0.0`
2421        for q1 in generate_unit_quaternion_data() {
2422            for q2 in generate_unit_quaternion_data() {
2423                let result = q1.slerp(&q2, 0.0);
2424                assert!((result - q1).norm() <= f32::EPSILON);
2425            }
2426        }
2427    }
2428
2429    #[cfg(any(feature = "std", feature = "libm"))]
2430    #[test]
2431    fn test_slerp_t_one() {
2432        // Test the spherical linear interpolation of unit quaternions
2433        // with `t = 1.0`
2434        use core::cmp::Ordering;
2435
2436        for q1 in generate_unit_quaternion_data() {
2437            for q2 in generate_unit_quaternion_data() {
2438                let result = q1.slerp(&q2, 1.0);
2439                match q1.dot(q2).partial_cmp(&0.0) {
2440                    Some(Ordering::Greater) => {
2441                        assert!((result - q2).norm() <= f32::EPSILON)
2442                    }
2443                    Some(Ordering::Less) => {
2444                        assert!((result + q2).norm() <= f32::EPSILON)
2445                    }
2446                    _ => {}
2447                }
2448            }
2449        }
2450    }
2451
2452    #[cfg(any(feature = "std", feature = "libm"))]
2453    #[test]
2454    fn test_slerp_t_half() {
2455        // Test the spherical linear interpolation of unit quaternions
2456        // with `t = 0.5`
2457        use core::cmp::Ordering;
2458
2459        for q1 in generate_unit_quaternion_data() {
2460            for q2 in generate_unit_quaternion_data() {
2461                let result = q1.slerp(&q2, 0.5);
2462                let dot_sign = match q1.dot(q2).partial_cmp(&0.0) {
2463                    Some(Ordering::Greater) => 1.0,
2464                    Some(Ordering::Less) => -1.0,
2465                    _ => continue, // uncertain due to rounding, better skip it
2466                };
2467                assert!(
2468                    (result - (q1 + dot_sign * q2).normalize().unwrap()).norm()
2469                        <= f32::EPSILON
2470                )
2471            }
2472        }
2473    }
2474
2475    #[cfg(any(feature = "std", feature = "libm"))]
2476    #[test]
2477    fn test_slerp_small_angle() {
2478        // Test the spherical linear interpolation of unit quaternions
2479        // with a small angles
2480        let q1 = UQ32::ONE;
2481        let q2 = Q32::new(999_999.0, 1.0, 0.0, 0.0).normalize().unwrap();
2482        let t = 0.5;
2483        let result = q1.slerp(&q2, t);
2484        let expected = Q32::new(999_999.75, 0.5, 0.0, 0.0).normalize().unwrap();
2485        assert!((result - expected).norm() <= f32::EPSILON);
2486    }
2487
2488    #[cfg(any(feature = "std", feature = "libm"))]
2489    #[test]
2490    fn test_sqrt_of_identity() {
2491        // Test the square root of the identity unit quaternion
2492        assert_eq!(UQ32::ONE.sqrt(), UQ32::ONE);
2493    }
2494
2495    #[cfg(any(feature = "std", feature = "libm"))]
2496    #[test]
2497    fn test_sqrt_of_negative_identity() {
2498        // Test the square root of the negative identity unit quaternion
2499        let q = Q64::new(-1.0, 0.0, -0.0, -0.0).normalize().unwrap();
2500        assert_eq!(q.sqrt(), UQ64::I);
2501        assert!(q.sqrt().0.w.is_sign_positive());
2502        assert!(q.sqrt().0.y.is_sign_negative());
2503        assert!(q.sqrt().0.z.is_sign_negative());
2504
2505        let q = Q64::new(-1.0, -0.0, 0.0, 0.0).normalize().unwrap();
2506        assert_eq!(q.sqrt(), -UQ64::I);
2507        assert!(q.sqrt().0.w.is_sign_positive());
2508        assert!(q.sqrt().0.y.is_sign_positive());
2509        assert!(q.sqrt().0.z.is_sign_positive());
2510    }
2511
2512    #[cfg(any(feature = "std", feature = "libm"))]
2513    #[test]
2514    fn test_sqrt_general_case() {
2515        // Test the square root of a general unit quaternion
2516        let c = Q64::new(1.0, 2.0, -3.0, 4.0).normalize().unwrap();
2517        let q = c.sqrt();
2518        assert!((q * q - c).norm() <= f64::EPSILON);
2519    }
2520
2521    #[cfg(any(feature = "std", feature = "libm"))]
2522    #[test]
2523    fn test_sqrt_with_negative_real_part() {
2524        // Test the square root of a unit quaternion with a negative real part
2525        let c = Q64::new(-4.0, 2.0, -3.0, 1.0).normalize().unwrap();
2526        let q = c.sqrt();
2527        assert!((q * q - c).norm() <= f64::EPSILON);
2528    }
2529
2530    #[cfg(any(feature = "std", feature = "libm"))]
2531    #[test]
2532    fn test_sqrt_with_subnormal_imaginary_parts() {
2533        // Test the square root of a unit quaternion with subnormal imaginary parts
2534        let min_positive = f64::MIN_POSITIVE;
2535        let q = Quaternion::new(-1.0, min_positive, min_positive, min_positive)
2536            .normalize()
2537            .unwrap();
2538        let result = q.sqrt();
2539        let expected = Quaternion::new(
2540            min_positive * 0.75f64.sqrt(),
2541            (1.0f64 / 3.0).sqrt(),
2542            (1.0f64 / 3.0).sqrt(),
2543            (1.0f64 / 3.0).sqrt(),
2544        )
2545        .normalize()
2546        .unwrap();
2547        assert!(
2548            (result.0.w - expected.0.w).abs()
2549                <= 2.0 * expected.0.w * f64::EPSILON
2550        );
2551        assert!(
2552            (result.0.x - expected.0.x).abs()
2553                <= 2.0 * expected.0.x * f64::EPSILON
2554        );
2555        assert!(
2556            (result.0.y - expected.0.y).abs()
2557                <= 2.0 * expected.0.y * f64::EPSILON
2558        );
2559        assert!(
2560            (result.0.z - expected.0.z).abs()
2561                <= 2.0 * expected.0.z * f64::EPSILON
2562        );
2563    }
2564
2565    #[cfg(any(feature = "std", feature = "libm"))]
2566    #[cfg(feature = "unstable")]
2567    #[test]
2568    fn test_ln_of_identity() {
2569        // Test the natural logarithm of the identity unit quaternion
2570        assert_eq!(UQ32::ONE.ln(), PureQuaternion::ZERO);
2571    }
2572
2573    #[cfg(any(feature = "std", feature = "libm"))]
2574    #[cfg(feature = "unstable")]
2575    #[test]
2576    fn test_ln_of_normal_case() {
2577        // Test the natural logarithm of a unit quaternion
2578        let q = Q64::new(1.0, 2.0, 3.0, 4.0);
2579        let p = q.normalize().expect("Failed to normalize quaternion").ln();
2580        assert!((p.z / p.x - q.z / q.x).abs() <= 4.0 * f64::EPSILON);
2581        assert!((p.y / p.x - q.y / q.x).abs() <= 4.0 * f64::EPSILON);
2582        assert!((p.norm() - 29.0f64.sqrt().atan()).abs() <= 4.0 * f64::EPSILON);
2583    }
2584
2585    #[cfg(any(feature = "std", feature = "libm"))]
2586    #[cfg(feature = "unstable")]
2587    #[test]
2588    fn test_ln_near_positive_real_axis() {
2589        // Test close to the positive real axis
2590        let q = Quaternion::new(1.0, 1e-10, 1e-10, 1e-10)
2591            .normalize()
2592            .unwrap();
2593        let ln_q = q.ln();
2594        let expected = PureQuaternion::new(1e-10, 1e-10, 1e-10); // ln(1) = 0 and imaginary parts small
2595        assert!((ln_q - expected).norm() <= 1e-11);
2596    }
2597
2598    #[cfg(any(feature = "std", feature = "libm"))]
2599    #[cfg(feature = "unstable")]
2600    #[test]
2601    fn test_ln_negative_real_axis() {
2602        // Test on the negative real axis
2603        let q = Q32::new(-1.0, 0.0, 0.0, 0.0).normalize().unwrap();
2604        let ln_q = q.ln();
2605        let expected = PureQuaternion::new(core::f32::consts::PI, 0.0, 0.0); // ln(-1) = pi*i
2606        assert!(
2607            (ln_q - expected).norm() <= core::f32::consts::PI * f32::EPSILON
2608        );
2609    }
2610
2611    #[cfg(any(feature = "std", feature = "libm"))]
2612    #[cfg(feature = "unstable")]
2613    #[test]
2614    fn test_ln_near_negative_real_axis() {
2615        // Test a quaternion with a tiny imaginary part
2616        use core::f32;
2617        let q = Q32::new(-2.0, 346.0 * f32::EPSILON, 0.0, 0.0);
2618        let uq = q.normalize().unwrap();
2619        let ln_uq = uq.ln();
2620        let expected =
2621            PureQuaternion::new(f32::consts::PI + q.x / q.w, 0.0, 0.0);
2622        assert!((ln_uq - expected).norm() <= 8.0 * f32::EPSILON);
2623
2624        let q = Q32::new(-1.0, f32::MIN_POSITIVE / 192.0, 0.0, 0.0);
2625        let uq = q.normalize().unwrap();
2626        let ln_uq = uq.ln();
2627        let expected = PureQuaternion::new(f32::consts::PI, 0.0, 0.0);
2628        assert_eq!(ln_uq, expected);
2629    }
2630
2631    #[cfg(all(feature = "serde", any(feature = "std", feature = "libm")))]
2632    #[test]
2633    fn test_serde_unit_quaternion() {
2634        // Create a sample quaternion
2635        let q = Q64::new(1.0, 2.0, 3.0, 4.0).normalize().unwrap();
2636
2637        // Serialize the quaternion to a JSON string
2638        let serialized =
2639            serde_json::to_string(&q).expect("Failed to serialize quaternion");
2640
2641        // Deserialize the JSON string back into a quaternion
2642        let deserialized: UQ64 = serde_json::from_str(&serialized)
2643            .expect("Failed to deserialize quaternion");
2644
2645        // Assert that the deserialized quaternion is equal to the original
2646        assert_eq!(q, deserialized);
2647    }
2648
2649    #[cfg(feature = "serde")]
2650    #[test]
2651    fn test_serde_unit_quaternion_k() {
2652        // Create a sample quaternion
2653        let q = UQ64::K;
2654
2655        // Serialize the quaternion to a JSON string
2656        let serialized =
2657            serde_json::to_string(&q).expect("Failed to serialize quaternion");
2658
2659        // Deserialize the JSON string back into a quaternion
2660        let deserialized: UQ64 = serde_json::from_str(&serialized)
2661            .expect("Failed to deserialize quaternion");
2662
2663        // Assert that the deserialized quaternion is equal to the original
2664        assert_eq!(q, deserialized);
2665    }
2666
2667    #[cfg(all(feature = "rand", any(feature = "std", feature = "libm")))]
2668    fn make_seeded_rng() -> impl rand::Rng {
2669        use rand::SeedableRng;
2670        rand::rngs::SmallRng::seed_from_u64(0x7F0829AE4D31C6B5)
2671    }
2672
2673    #[cfg(all(feature = "rand", any(feature = "std", feature = "libm")))]
2674    #[test]
2675    fn test_unit_quaternion_sample_six_sigma() {
2676        // Test that the sample distribution of unit quaternions is uniform
2677        use rand::distr::{Distribution, StandardUniform};
2678        let num_iters = 1_000_000;
2679        let mut sum: Q64 = num_traits::Zero::zero();
2680        let rng = make_seeded_rng();
2681        for q in Distribution::<UQ64>::sample_iter(StandardUniform, rng)
2682            .take(num_iters)
2683        {
2684            sum += q;
2685        }
2686
2687        let sum_std_dev = (num_iters as f64).sqrt();
2688        // The statistical probability of failure is 1.973e-9, unless there is
2689        // a bug.
2690        assert!(sum.norm() < 6.0 * sum_std_dev);
2691    }
2692
2693    #[cfg(all(feature = "rand", any(feature = "std", feature = "libm")))]
2694    #[test]
2695    fn test_unit_quaternion_sample_half_planes() {
2696        // Test that the sample distribution of unit quaternions is uniform
2697        use rand::{
2698            distr::{Distribution, StandardUniform},
2699            Rng,
2700        };
2701        let num_iters = 1_000_000;
2702        let mut rng = make_seeded_rng();
2703        const NUM_DIRS: usize = 10;
2704        let dirs: [UQ64; NUM_DIRS] = [
2705            UQ64::ONE,
2706            UQ64::I,
2707            UQ64::J,
2708            UQ64::K,
2709            Q64::new(1.0, 2.0, 3.0, 4.0).normalize().unwrap(),
2710            Q64::new(4.0, -3.0, 2.0, -1.0).normalize().unwrap(),
2711            rng.random(),
2712            rng.random(),
2713            rng.random(),
2714            rng.random(),
2715        ];
2716        let mut counters = [0; NUM_DIRS];
2717        for q in Distribution::<UQ64>::sample_iter(StandardUniform, rng)
2718            .take(num_iters)
2719        {
2720            for (dir, counter) in dirs.iter().zip(counters.iter_mut()) {
2721                if q.dot(*dir) > 0.0 {
2722                    *counter += 1;
2723                }
2724            }
2725        }
2726
2727        let six_sigma = 3 * (num_iters as f64).sqrt() as i32;
2728        let expected_count = num_iters as i32 / 2;
2729        // The statistical probability of failure of the following loop is
2730        // 1.973e-8, unless there is a bug.
2731        for counter in counters {
2732            assert!((counter - expected_count).abs() < six_sigma);
2733        }
2734    }
2735}