num_quaternion/
quaternion.rs

1use {
2    crate::UnitQuaternion,
3    core::ops::{Add, Mul, Neg},
4    num_traits::{ConstOne, ConstZero, Inv, Num, One, Zero},
5};
6
7#[cfg(any(feature = "std", feature = "libm"))]
8use {
9    core::num::FpCategory,
10    num_traits::{Float, FloatConst},
11};
12
13/// Quaternion type.
14///
15/// We follow the naming conventions from
16/// [Wikipedia](https://en.wikipedia.org/wiki/Quaternion) for quaternions.
17/// You can generate quaternions using the [`new`](Quaternion::new) function:
18///
19/// ```
20/// // 1 + 2i + 3j + 4k
21/// # use num_quaternion::Quaternion;
22/// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
23/// ```
24///
25/// Alternatively, you can construct quaternions directly with the member data
26/// fields:
27///
28/// ```
29/// # use num_quaternion::Q32;
30/// let q = Q32 { w: 1.0, x: 2.0, y: 3.0, z: 4.0 };
31/// ```
32///
33/// This is exactly equivalent to the first method. The latter example uses the
34/// shorthand `Q32` for `Quaternion<f32>`. For your convenience, there are also
35/// the member constants [`ONE`](Quaternion::ONE), [`I`](Quaternion::I),
36/// [`J`](Quaternion::J), and [`K`](Quaternion::K) for the mathematical
37/// values $1$, $i$, $j$, and $k$, respectively.
38///
39/// `Quaternion`s support the usual arithmetic operations of addition,
40/// subtraction, multiplication, and division. You can compute the
41/// norm with [`norm`](Quaternion::norm) or [`fast_norm`](Quaternion::fast_norm)
42/// and its square with [`norm_sqr`](Quaternion::norm_sqr). Quaternion
43/// conjugation is done by the member function [`conj`](Quaternion::conj).
44/// You can normalize a quaternion by calling
45/// [`normalize`](Quaternion::normalize), which returns a [`UnitQuaternion`].
46///
47/// Furthermore, the following functions are supported:
48///
49/// - [`dot`](Quaternion::dot): Computes the dot product of two quaternions.
50/// - [`exp`](Quaternion::exp): Computes the exponential of a quaternion.
51/// - [`expf`](Quaternion::expf): Raises a real value to a quaternion power.
52/// - [`inv`](Quaternion::inv): Computes the multiplicative inverse.
53/// - [`ln`](Quaternion::ln): Computes the natural logarithm of a quaternion.
54/// - [`powf`](Quaternion::powf): Raises a quaternion to a real power.
55/// - [`powi`](Quaternion::powi): Raises a quaternion to a signed integer power.
56/// - [`powu`](Quaternion::powu): Raises a quaternion to an unsigned integer power.
57///
58/// To work with rotations, please use [`UnitQuaternion`]s.
59///
60/// # Examples
61///
62/// Basic usage:
63///
64/// ```rust
65/// # use num_quaternion::Quaternion;
66/// let q1 = Quaternion::new(1.0f32, 0.0, 0.0, 0.0);
67/// let q2 = Quaternion::new(0.0, 1.0, 0.0, 0.0);
68/// let q3 = q1 + q2;
69/// assert_eq!(q3, Quaternion::new(1.0, 1.0, 0.0, 0.0));
70/// ```
71///
72/// # Fields
73///
74/// - `w`: Real part of the quaternion.
75/// - `x`: The coefficient of $i$.
76/// - `y`: The coefficient of $j$.
77/// - `z`: The coefficient of $k$.
78#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
79pub struct Quaternion<T> {
80    /// Real part of the quaternion.
81    pub w: T,
82    /// The coefficient of $i$.
83    pub x: T,
84    /// The coefficient of $j$.
85    pub y: T,
86    /// The coefficient of $k$.
87    pub z: T,
88}
89
90/// Alias for a [`Quaternion<f32>`].
91pub type Q32 = Quaternion<f32>;
92/// Alias for a [`Quaternion<f64>`].
93pub type Q64 = Quaternion<f64>;
94
95impl<T> Quaternion<T> {
96    /// Create a new quaternion $a + bi + cj + dk$.
97    ///
98    /// # Example
99    ///
100    /// ```
101    /// # use num_quaternion::Quaternion;
102    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
103    /// ```
104    #[inline]
105    pub const fn new(w: T, x: T, y: T, z: T) -> Self {
106        Self { w, x, y, z }
107    }
108}
109
110impl<T> Quaternion<T>
111where
112    T: ConstZero,
113{
114    /// A constant zero `Quaternion`.
115    ///
116    /// This is the additive identity element of the quaternion space.
117    /// See also [`Quaternion::zero`].
118    ///
119    /// # Example
120    ///
121    /// ```
122    /// # use num_quaternion::Quaternion;
123    /// let q = Quaternion::ZERO;
124    /// assert_eq!(q, Quaternion::new(0.0f32, 0.0, 0.0, 0.0));
125    /// ```
126    pub const ZERO: Self = Self::new(T::ZERO, T::ZERO, T::ZERO, T::ZERO);
127}
128
129impl<T> ConstZero for Quaternion<T>
130where
131    T: ConstZero,
132{
133    const ZERO: Self = Self::ZERO;
134}
135
136impl<T> Zero for Quaternion<T>
137where
138    T: Zero,
139{
140    #[inline]
141    fn zero() -> Self {
142        Self::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
143    }
144
145    #[inline]
146    fn is_zero(&self) -> bool {
147        self.w.is_zero()
148            && self.x.is_zero()
149            && self.y.is_zero()
150            && self.z.is_zero()
151    }
152
153    #[inline]
154    fn set_zero(&mut self) {
155        self.w.set_zero();
156        self.x.set_zero();
157        self.y.set_zero();
158        self.z.set_zero();
159    }
160}
161
162impl<T> Quaternion<T>
163where
164    T: ConstZero + ConstOne,
165{
166    /// A constant `Quaternion` of value $1$.
167    ///
168    /// See also [`Quaternion::one`].
169    ///
170    /// # Example
171    ///
172    /// ```
173    /// # use num_quaternion::Quaternion;
174    /// let q = Quaternion::ONE;
175    /// assert_eq!(q, Quaternion::new(1.0f32, 0.0, 0.0, 0.0));
176    /// ```
177    pub const ONE: Self = Self::new(T::ONE, T::ZERO, T::ZERO, T::ZERO);
178
179    /// A constant `Quaternion` of value $i$.
180    ///
181    /// See also [`Quaternion::i`].
182    ///
183    /// # Example
184    ///
185    /// ```
186    /// # use num_quaternion::Quaternion;
187    /// let q = Quaternion::I;
188    /// assert_eq!(q, Quaternion::new(0.0f32, 1.0, 0.0, 0.0));
189    /// ```
190    pub const I: Self = Self::new(T::ZERO, T::ONE, T::ZERO, T::ZERO);
191
192    /// A constant `Quaternion` of value $j$.
193    ///
194    /// See also [`Quaternion::j`].
195    ///
196    /// # Example
197    ///
198    /// ```
199    /// # use num_quaternion::Quaternion;
200    /// let q = Quaternion::J;
201    /// assert_eq!(q, Quaternion::new(0.0f32, 0.0, 1.0, 0.0));
202    /// ```
203    pub const J: Self = Self::new(T::ZERO, T::ZERO, T::ONE, T::ZERO);
204
205    /// A constant `Quaternion` of value $k$.
206    ///
207    /// See also [`Quaternion::k`].
208    ///
209    /// # Example
210    ///
211    /// ```
212    /// # use num_quaternion::Quaternion;
213    /// let q = Quaternion::K;
214    /// assert_eq!(q, Quaternion::new(0.0f32, 0.0, 0.0, 1.0));
215    /// ```
216    pub const K: Self = Self::new(T::ZERO, T::ZERO, T::ZERO, T::ONE);
217}
218
219impl<T> ConstOne for Quaternion<T>
220where
221    T: ConstZero + ConstOne + Num + Clone,
222{
223    const ONE: Self = Self::ONE;
224}
225
226impl<T> One for Quaternion<T>
227where
228    T: Num + Clone,
229{
230    #[inline]
231    fn one() -> Self {
232        Self::new(One::one(), Zero::zero(), Zero::zero(), Zero::zero())
233    }
234
235    #[inline]
236    fn is_one(&self) -> bool {
237        self.w.is_one()
238            && self.x.is_zero()
239            && self.y.is_zero()
240            && self.z.is_zero()
241    }
242
243    #[inline]
244    fn set_one(&mut self) {
245        self.w.set_one();
246        self.x.set_zero();
247        self.y.set_zero();
248        self.z.set_zero();
249    }
250}
251
252impl<T> Quaternion<T>
253where
254    T: Zero + One,
255{
256    /// Returns the real unit $1$.
257    ///
258    /// See also [`Quaternion::ONE`].
259    ///
260    /// # Example
261    ///
262    /// ```
263    /// # use num_quaternion::Quaternion;
264    /// let q = Quaternion::one();
265    /// assert_eq!(q, Quaternion::new(1.0f32, 0.0, 0.0, 0.0));
266    /// ```
267    #[inline]
268    pub fn one() -> Self {
269        Self::new(T::one(), T::zero(), T::zero(), T::zero())
270    }
271
272    /// Returns the imaginary unit $i$.
273    ///
274    /// See also [`Quaternion::I`].
275    ///
276    /// # Example
277    ///
278    /// ```
279    /// # use num_quaternion::Quaternion;
280    /// let q = Quaternion::i();
281    /// assert_eq!(q, Quaternion::new(0.0f32, 1.0, 0.0, 0.0));
282    /// ```
283    #[inline]
284    pub fn i() -> Self {
285        Self::new(T::zero(), T::one(), T::zero(), T::zero())
286    }
287
288    /// Returns the imaginary unit $j$.
289    ///
290    /// See also [`Quaternion::J`].
291    ///
292    /// # Example
293    ///
294    /// ```
295    /// # use num_quaternion::Quaternion;
296    /// let q = Quaternion::j();
297    /// assert_eq!(q, Quaternion::new(0.0f32, 0.0, 1.0, 0.0));
298    /// ```
299    #[inline]
300    pub fn j() -> Self {
301        Self::new(T::zero(), T::zero(), T::one(), T::zero())
302    }
303
304    /// Returns the imaginary unit $k$.
305    ///
306    /// See also [`Quaternion::K`].
307    ///
308    /// # Example
309    ///
310    /// ```
311    /// # use num_quaternion::Quaternion;
312    /// let q = Quaternion::k();
313    /// assert_eq!(q, Quaternion::new(0.0f32, 0.0, 0.0, 1.0));
314    /// ```
315    #[inline]
316    pub fn k() -> Self {
317        Self::new(T::zero(), T::zero(), T::zero(), T::one())
318    }
319}
320
321#[cfg(any(feature = "std", feature = "libm"))]
322impl<T> Quaternion<T>
323where
324    T: Float,
325{
326    /// Returns a quaternion filled with `NaN` values.
327    ///
328    /// # Example
329    ///
330    /// ```
331    /// # use num_quaternion::Q32;
332    /// let q = Q32::nan();
333    /// assert!(q.w.is_nan());
334    /// assert!(q.x.is_nan());
335    /// assert!(q.y.is_nan());
336    /// assert!(q.z.is_nan());
337    /// ```
338    #[inline]
339    pub fn nan() -> Self {
340        let nan = T::nan();
341        Self::new(nan, nan, nan, nan)
342    }
343}
344
345impl<T> Quaternion<T>
346where
347    T: Clone + Mul<T, Output = T> + Add<T, Output = T>,
348{
349    /// Returns the square of the norm.
350    ///
351    /// The result is $w^2 + x^2 + y^2 + z^2$ with some rounding errors.
352    /// The rounding error is at most 2
353    /// [ulps](https://en.wikipedia.org/wiki/Unit_in_the_last_place).
354    ///
355    /// This is guaranteed to be more efficient than [`norm`](Quaternion::norm()).
356    /// Furthermore, `T` only needs to support addition and multiplication
357    /// and therefore, this function works for more types than
358    /// [`norm`](Quaternion::norm()).
359    ///
360    /// # Example
361    ///
362    /// ```
363    /// # use num_quaternion::Quaternion;
364    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
365    /// assert_eq!(q.norm_sqr(), 30.0);
366    /// ```
367    #[inline]
368    pub fn norm_sqr(&self) -> T {
369        (self.w.clone() * self.w.clone() + self.y.clone() * self.y.clone())
370            + (self.x.clone() * self.x.clone()
371                + self.z.clone() * self.z.clone())
372    }
373}
374
375impl<T> Quaternion<T>
376where
377    T: Clone + Neg<Output = T>,
378{
379    /// Returns the conjugate quaternion, i. e. the imaginary part is negated.
380    ///
381    /// # Example
382    ///
383    /// ```
384    /// # use num_quaternion::Quaternion;
385    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
386    /// assert_eq!(q.conj(), Quaternion::new(1.0, -2.0, -3.0, -4.0));
387    /// ```
388    #[inline]
389    pub fn conj(&self) -> Self {
390        Self::new(
391            self.w.clone(),
392            -self.x.clone(),
393            -self.y.clone(),
394            -self.z.clone(),
395        )
396    }
397}
398
399impl<T> Quaternion<T>
400where
401    for<'a> &'a Self: Inv<Output = Quaternion<T>>,
402{
403    /// Returns the multiplicative inverse `1/self`.
404    ///
405    /// # Example
406    ///
407    /// ```
408    /// # use num_quaternion::Quaternion;
409    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
410    /// assert_eq!(q.inv(), Quaternion::new(
411    ///     1.0 / 30.0, -1.0 / 15.0, -0.1, -2.0 / 15.0));
412    #[inline]
413    pub fn inv(&self) -> Self {
414        Inv::inv(self)
415    }
416}
417
418impl<T> Inv for &Quaternion<T>
419where
420    T: Clone + Neg<Output = T> + Num,
421{
422    type Output = Quaternion<T>;
423
424    #[inline]
425    fn inv(self) -> Self::Output {
426        let norm_sqr = self.norm_sqr();
427        Quaternion::new(
428            self.w.clone() / norm_sqr.clone(),
429            -self.x.clone() / norm_sqr.clone(),
430            -self.y.clone() / norm_sqr.clone(),
431            -self.z.clone() / norm_sqr,
432        )
433    }
434}
435
436impl<T> Inv for Quaternion<T>
437where
438    for<'a> &'a Self: Inv<Output = Quaternion<T>>,
439{
440    type Output = Quaternion<T>;
441
442    #[inline]
443    fn inv(self) -> Self::Output {
444        Inv::inv(&self)
445    }
446}
447
448#[cfg(any(feature = "std", feature = "libm"))]
449impl<T> Quaternion<T>
450where
451    T: Float,
452{
453    /// Calculates |self|.
454    ///
455    /// The result is $\sqrt{w^2+x^2+y^2+z^2}$ with some possible rounding
456    /// errors. The total relative rounding error is at most two
457    /// [ulps](https://en.wikipedia.org/wiki/Unit_in_the_last_place).
458    ///
459    /// If any of the components of the input quaternion is `NaN`, then `NaN`
460    /// is returned. Otherwise, if any of the components is infinite, then
461    /// a positive infinite value is returned.
462    ///
463    /// # Example
464    ///
465    /// ```
466    /// # use num_quaternion::Quaternion;
467    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
468    /// assert_eq!(q.norm(), 30.0f32.sqrt());
469    /// ```
470    #[inline]
471    pub fn norm(self) -> T {
472        let one = T::one();
473        let two = one + one;
474        let s = T::min_positive_value();
475        let norm_sqr = self.norm_sqr();
476        if norm_sqr < T::infinity() {
477            if norm_sqr >= s * two {
478                norm_sqr.sqrt()
479            } else if self.is_zero() {
480                // Likely, the whole vector is zero. If so, we can return
481                // zero directly and avoid expensive floating point math.
482                T::zero()
483            } else {
484                // Otherwise, scale up, such that the norm will be in the
485                // normal floating point range, then scale down the result.
486                (self / s).fast_norm() * s
487            }
488        } else {
489            // There are three possible cases:
490            //   1. one of w, x, y, z is NaN,
491            //   2. neither is `NaN`, but at least one of them is infinite, or
492            //   3. all of them are finite.
493            // In the first case, multiplying by s or dividing by it does not
494            // change the that the result is `NaN`. The same applies in the
495            // second case: the result remains infinite. In the third case,
496            // multiplying by s makes sure that the square norm is a normal
497            // floating point number. Dividing by it will rescale the result
498            // to the correct magnitude.
499            (self * s).fast_norm() / s
500        }
501    }
502}
503
504#[cfg(any(feature = "std", feature = "libm"))]
505impl<T> Quaternion<T>
506where
507    T: Float,
508{
509    /// Calculates |self| without branching.
510    ///
511    /// This function returns the same result as [`norm`](Self::norm), if
512    /// |self|² is a normal floating point number (i. e. there is no overflow
513    /// nor underflow), or if `self` is zero. In these cases the maximum
514    /// relative error of the result is guaranteed to be less than two ulps.
515    /// In all other cases, there's no guarantee on the precision of the
516    /// result:
517    ///
518    /// * If |self|² overflows, then $\infty$ is returned.
519    /// * If |self|² underflows to zero, then zero will be returned.
520    /// * If |self|² is a subnormal number (very small floating point value
521    ///   with reduced relative precision), then the result is the square
522    ///   root of that.
523    ///
524    /// In other words, this function can be imprecise for very large and very
525    /// small floating point numbers, but it is generally faster than
526    /// [`norm`](Self::norm), because it does not do any branching. So if you
527    /// are interested in maximum speed of your code, feel free to use this
528    /// function. If you need to be precise results for the whole range of the
529    /// floating point type `T`, stay with [`norm`](Self::norm).
530    ///
531    /// # Example
532    ///
533    /// ```
534    /// # use num_quaternion::Quaternion;
535    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
536    /// assert_eq!(q.fast_norm(), q.norm());
537    /// ```
538    #[inline]
539    pub fn fast_norm(self) -> T {
540        self.norm_sqr().sqrt()
541    }
542}
543
544#[cfg(any(feature = "std", feature = "libm"))]
545impl<T> Quaternion<T>
546where
547    T: Float,
548{
549    /// Normalizes the quaternion to length $1$.
550    ///
551    /// The sign of the real part will be the same as the sign of the input.
552    /// If the input quaternion
553    ///
554    /// * is zero, or
555    /// * has infinite length, or
556    /// * has a `NaN` value,
557    ///
558    /// then `None` will be returned.
559    ///
560    /// # Example
561    ///
562    /// ```
563    /// # use num_quaternion::Quaternion;
564    /// # fn test() -> Option<()> {
565    /// let q = Quaternion::new(1.0f32, 2.0, 2.0, 4.0);
566    /// assert_eq!(q.normalize()?.into_quaternion(),
567    ///         Quaternion::new(0.2f32, 0.4, 0.4, 0.8));
568    /// # Some(())
569    /// # }
570    /// # assert!(test().is_some());
571    /// ```
572    #[inline]
573    pub fn normalize(self) -> Option<UnitQuaternion<T>> {
574        UnitQuaternion::normalize(self)
575    }
576}
577
578impl<T> From<T> for Quaternion<T>
579where
580    T: Zero,
581{
582    #[inline]
583    fn from(a: T) -> Self {
584        Self::new(a, T::zero(), T::zero(), T::zero())
585    }
586}
587
588impl<T> From<&T> for Quaternion<T>
589where
590    T: Clone + Zero,
591{
592    #[inline]
593    fn from(a: &T) -> Self {
594        From::from(a.clone())
595    }
596}
597
598impl<T> From<UnitQuaternion<T>> for Quaternion<T> {
599    #[inline]
600    fn from(q: UnitQuaternion<T>) -> Self {
601        q.into_inner()
602    }
603}
604
605impl<'a, T> From<&'a UnitQuaternion<T>> for &'a Quaternion<T> {
606    #[inline]
607    fn from(q: &'a UnitQuaternion<T>) -> Self {
608        q.as_quaternion()
609    }
610}
611
612impl<T> Quaternion<T>
613where
614    T: Add<T, Output = T> + Mul<T, Output = T>,
615{
616    /// Computes the dot product of two quaternions interpreted as
617    /// 4D real vectors.
618    ///
619    /// # Example
620    ///
621    /// ```
622    /// # use num_quaternion::Quaternion;
623    /// let q1 = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
624    /// let q2 = Quaternion::new(0.0f32, 0.0, 1.0, 1.0);
625    /// let d = q1.dot(q2);
626    /// assert_eq!(d, 7.0);
627    /// ```
628    #[inline]
629    pub fn dot(self, other: Self) -> T {
630        self.w * other.w
631            + self.y * other.y
632            + (self.x * other.x + self.z * other.z)
633    }
634}
635
636impl<T> Quaternion<T>
637where
638    T: Num + Clone,
639{
640    /// Raises `self` to an unsigned integer power `n`, i. e. $q^n$.
641    ///
642    /// # Example
643    ///
644    /// ```
645    /// # use num_quaternion::Quaternion;
646    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
647    /// let q_sqr = q.powu(2);
648    /// assert_eq!(q_sqr, q * q);
649    /// ```
650    pub fn powu(&self, mut n: u32) -> Self {
651        if n == 0 {
652            Self::one()
653        } else {
654            let mut base = self.clone();
655            while n & 1 == 0 {
656                n /= 2;
657                base = base.clone() * base;
658            }
659
660            if n == 1 {
661                return base;
662            }
663
664            let mut acc = base.clone();
665            while n > 1 {
666                n /= 2;
667                base = base.clone() * base;
668                if n & 1 == 1 {
669                    acc *= base.clone();
670                }
671            }
672            acc
673        }
674    }
675}
676
677impl<T> Quaternion<T>
678where
679    T: Clone + Num + Neg<Output = T>,
680{
681    /// Raises `self` to a signed integer power `n`, i. e. $q^n$
682    ///
683    /// For $n=0$ the result is exactly $1$.
684    ///
685    /// # Example
686    ///
687    /// ```
688    /// # use num_quaternion::Quaternion;
689    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
690    /// let q_sqr = q.powi(2);
691    /// assert_eq!(q_sqr, q * q);
692    /// ```
693    #[inline]
694    pub fn powi(&self, n: i32) -> Self {
695        if n >= 0 {
696            self.powu(n as u32)
697        } else {
698            self.inv().powu(n.wrapping_neg() as u32)
699        }
700    }
701}
702
703#[cfg(any(feature = "std", feature = "libm"))]
704impl<T> Quaternion<T>
705where
706    T: Float + FloatConst,
707{
708    /// Given a quaternion $q$, returns $e^q$, where $e$ is the base of the
709    /// natural logarithm.
710    ///
711    /// This method computes the exponential of a quaternion, handling various
712    /// edge cases to ensure numerical stability and correctness:
713    ///
714    /// 1. **Negative Real Part**: If the real part is sufficiently negative,
715    ///    such that $e^{\Re q}$ is approximately zero, the method returns
716    ///    zero. This is done even if the imaginary part contains infinite or
717    ///    NaN values.
718    ///
719    /// 2. **NaN Input**: If any component of the input quaternion is `NaN`,
720    ///    the method returns a quaternion filled with `NaN` values.
721    ///
722    /// 3. **Large Imaginary Norm**: If the norm of the imaginary part is too
723    ///    large, the method may return a `NaN` quaternion or a quaternion with
724    ///    the correct magnitude but inaccurate direction.
725    ///
726    /// 4. **Infinite Result**: If $e^{\Re q}$ results in `+∞`, the method
727    ///    computes the direction and returns an infinite quaternion in that
728    ///    direction, ensuring that `∞ * 0` values are mapped to zero instead
729    ///    of `NaN`.
730    ///
731    /// 5. **Finite Norm**: For finite norms, the method ensures a very small
732    ///    relative error in all components, depending on the accuracy of the
733    ///    underlying floating point function implementations.
734    ///
735    /// # Example
736    ///
737    /// ```
738    /// # use num_quaternion::Quaternion;
739    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
740    /// let exp_q = q.exp();
741    /// ```
742    pub fn exp(self) -> Self {
743        let one = T::one();
744        let two = one + one;
745        let four = two + two;
746        let half = one / two;
747        let quarter = one / four;
748        let inf = T::infinity();
749
750        // Compute the exponential of the real part, which gives the norm of
751        // the result
752        let result_norm = self.w.exp();
753
754        match result_norm.partial_cmp(&inf) {
755            Some(core::cmp::Ordering::Less) => {
756                if result_norm.is_zero() {
757                    return Self::zero();
758                }
759                // Case: 0 < result_norm < ∞
760
761                // Compute the squared norm of the imaginary part
762                let sqr_angle =
763                    self.x * self.x + self.y * self.y + self.z * self.z;
764
765                if sqr_angle <= T::epsilon() {
766                    // Use Taylor series approximation for small angles to
767                    // maintain numerical stability. By Taylor expansion of
768                    // `cos(angle)` we get
769                    //     cos(angle) >= 1 - angle² / 2
770                    // and thus |cos(angle) - 1| is less than half a floating
771                    // point epsilon. Similarly,
772                    //     sinc(angle) >= 1 - angle² / 6
773                    // and thus |sinc(angle) - 1| is less than a sixth of a
774                    // floating point epsilon.
775                    let w = result_norm;
776                    let x = result_norm * self.x;
777                    let y = result_norm * self.y;
778                    let z = result_norm * self.z;
779                    Self::new(w, x, y, z)
780                } else {
781                    // Standard computation for larger angles
782                    let angle = sqr_angle.sqrt();
783                    let cos_angle = angle.cos();
784                    let sinc_angle = angle.sin() / angle;
785                    let w = result_norm * cos_angle;
786                    let x = result_norm * self.x * sinc_angle;
787                    let y = result_norm * self.y * sinc_angle;
788                    let z = result_norm * self.z * sinc_angle;
789                    Self::new(w, x, y, z)
790                }
791            }
792            Some(_) => {
793                // Case: result_norm == ∞
794                let map = |a: T| {
795                    // Map zero to zero with same sign and everything else to
796                    // infinity with same sign as the input.
797                    if a.is_zero() {
798                        a
799                    } else {
800                        inf.copysign(a)
801                    }
802                };
803                let sqr_angle =
804                    self.x * self.x + self.y * self.y + self.z * self.z;
805                if sqr_angle < T::PI() * T::PI() * quarter {
806                    // Angle less than 90 degrees
807                    Self::new(inf, map(self.x), map(self.y), map(self.z))
808                } else if sqr_angle.is_finite() {
809                    // Angle 90 degrees or more -> careful sign handling
810                    let angle = sqr_angle.sqrt();
811                    let angle_revolutions_fract =
812                        (angle * T::FRAC_1_PI() * half).fract();
813                    let cos_angle_signum =
814                        (angle_revolutions_fract - half).abs() - quarter;
815                    let sin_angle_signum =
816                        one.copysign(half - angle_revolutions_fract);
817                    Self::new(
818                        inf.copysign(cos_angle_signum),
819                        map(self.x) * sin_angle_signum,
820                        map(self.y) * sin_angle_signum,
821                        map(self.z) * sin_angle_signum,
822                    )
823                } else {
824                    // Angle is super large or NaN
825                    debug_assert!(
826                        sqr_angle.is_infinite() || sqr_angle.is_nan()
827                    );
828                    Self::nan()
829                }
830            }
831            None => {
832                // Case: result_norm is NaN
833                debug_assert!(result_norm.is_nan());
834                Self::nan()
835            }
836        }
837    }
838}
839
840#[cfg(any(feature = "std", feature = "libm"))]
841impl<T> Quaternion<T>
842where
843    T: Float + FloatConst,
844{
845    /// Raises a real value (`base`) to a quaternion (`self`) power.
846    ///
847    /// Given a quaternion $q$ and a real value $t$, this function computes
848    /// $t^q := e^{q \ln t}$. The function handles special cases as follows:
849    ///
850    /// - If $t = \pm 0$ and $\Re q > 0$, or $t = +\infty$ and $\Re q < 0$,
851    ///   a zero quaternion is returned.
852    /// - If $t < 0$ or $t$ is `NaN`, a `NaN` quaternion (filled with `NaN`
853    ///   values) is returned.
854    /// - If $t$ is $+0$, $-0$, or $+\infty$ and $\Re q = 0$, a `NaN`
855    ///   quaternion is returned.
856    /// - If $t = +\infty$ and $q$ is a positive real number, $+\infty$ is
857    ///   returned. For other values of $q$ with $\Re q > 0$, a `NaN`
858    ///   quaternion is returned.
859    /// - If $t = \pm 0$ and $q$ is a negative real number, $+\infty$ is
860    ///   returned. For other values of $q$ with $\Re q < 0$, a `NaN`
861    ///   quaternion is returned.
862    ///
863    /// For finite positive $t$, the following conventions for boundary values
864    /// of $q$ are applied:
865    ///
866    /// - If any component of $q$ is `NaN` or any imaginary component of $q$ is
867    ///   infinite, a `NaN` quaternion is returned.
868    /// - Otherwise, if $\Re q = -\infty$ and $t > 1$, a zero quaternion is
869    ///   returned.
870    /// - Otherwise, if $\Re q = +\infty$ and $0 < t < 1$, a zero quaternion is
871    ///   returned.
872    /// - Otherwise, if $\Re q$ is infinite and $t = 1$, a `NaN` quaternion is
873    ///   returned.
874    /// - Otherwise, if $\Re q = +\infty$ and $t > 1$, an infinite quaternion
875    ///   without `NaN` values is returned.
876    /// - Otherwise, if $\Re q = -\infty$ and $0 < t < 1$, an infinite
877    ///   quaternion without `NaN` values is returned.
878    ///
879    /// If the true result's norm is neither greater than the largest
880    /// representable floating point value nor less than the smallest
881    /// representable floating point value, and the direction of the output
882    /// quaternion cannot be accurately determined, a `NaN` quaternion may or
883    /// may not be returned to indicate inaccuracy. This can occur when
884    /// $\|\Im(q) \ln t\|$ is on the order of $1/\varepsilon$, where
885    /// $\varepsilon$ is the machine precision of the floating point type used.
886    ///
887    /// # Example
888    ///
889    /// ```
890    /// # use num_quaternion::Quaternion;
891    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
892    /// let exp_q = q.expf(2.0);
893    /// ```
894    #[inline]
895    pub fn expf(self, base: T) -> Self {
896        if (base.is_infinite()
897            && self.w > T::zero()
898            && self.x.is_zero()
899            && self.y.is_zero()
900            && self.z.is_zero())
901            || (base.is_zero()
902                && self.w < T::zero()
903                && self.x.is_zero()
904                && self.y.is_zero()
905                && self.z.is_zero())
906        {
907            T::infinity().into()
908        } else {
909            (self * base.ln()).exp()
910        }
911    }
912}
913
914#[cfg(any(feature = "std", feature = "libm"))]
915impl<T> Quaternion<T>
916where
917    T: Float + FloatConst,
918{
919    /// Raises a quaternion (`self`) to a real value (`exponent`) power.
920    ///
921    /// Given a quaternion $q$ and a real value $t$, this function computes
922    /// $q^t := e^{t \ln q}$. The function handles special cases as follows:
923    ///
924    /// - If $q = 0$ and $t > 0$, a zero quaternion is returned.
925    /// - If $|q| = \infty$ and $t < 0$, a zero quaternion is returned.
926    /// - If $|q| = \infty$ and $t > 0$ is not too large to prevent
927    ///   numerical accuracy for the direction of the quaternion, then an
928    ///   infinite quaternion without `NaN` components is returned. For larger
929    ///   but finite values of $t$ this may still be hold, or alternatively a
930    ///   quaternion filled with `NaN` values is returned.
931    /// - If $q = +\infty$ and $t = +\infty$, then positive infinity is
932    ///   returned.
933    /// - If $|q| = \infty$, but $q \neq +\infty$ and $t = +\infty$, then `NaN`
934    ///   is returned.
935    /// - If $q = 0$ and $t < 0$, then positive infinity is returned.
936    /// - If $q$ contains a `NaN` component, or if $t$ is `NaN`, a `NaN`
937    ///   quaternion is returned.
938    /// - If $|q| = \infty$ and $t = 0$, a `NaN` quaternion is returned.
939    /// - If $q = 0$ and $t = 0$, a `NaN` quaternion is returned.
940    ///
941    /// For non-zero finite $q$, the following conventions for boundary values
942    /// of $t$ are applied:
943    ///
944    /// - If $t = +\infty$ and $q, |q| \ge 1$ is not real or $q = 1$ or
945    ///   $q \le -1$, a `NaN` quaternion is returned.
946    /// - If $t = +\infty$ and $q > 1$, positive infinity is returned.
947    /// - If $t = +\infty$ and $|q| < 1$, zero is returned.
948    /// - If $t = -\infty$ and $|q| > 1$, zero is returned.
949    /// - If $t = -\infty$ and $0 \le q < 1$, positive infinity is returned.
950    /// - If $t = -\infty$ and $q, |q| \le 1$ is not real or $q = 1$ or
951    ///   $-1 \le q < 0$, a `NaN` quaternion is returned.
952    ///
953    /// If the true result's norm is neither greater than the largest
954    /// representable floating point value nor less than the smallest
955    /// representable floating point value, and the direction of the output
956    /// quaternion cannot be accurately determined, a `NaN` quaternion may or
957    /// may not be returned to indicate inaccuracy. This can occur when
958    /// $\|t \Im(\ln q)\|$ is on the order of $1/\varepsilon$, where
959    /// $\varepsilon$ is the machine precision of the floating point type used.
960    ///
961    /// # Example
962    ///
963    /// ```
964    /// # use num_quaternion::Quaternion;
965    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
966    /// let exp_q = q.powf(2.0);
967    /// ```
968    #[inline]
969    pub fn powf(self, exponent: T) -> Self {
970        if exponent.is_finite() {
971            // -∞ < t < +∞ ==> apply the general formula.
972            (self.ln() * exponent).exp()
973        } else if exponent > T::zero() {
974            // t = +∞
975            if self.x.is_zero() && self.y.is_zero() && self.z.is_zero() {
976                // q is real --> handle special cases
977                match self.w.partial_cmp(&T::one()) {
978                    Some(core::cmp::Ordering::Greater) => T::infinity().into(),
979                    Some(core::cmp::Ordering::Less) => T::zero().into(),
980                    _ => Self::nan(),
981                }
982            } else if self.norm_sqr() < T::one() {
983                // |q| < 1
984                Self::zero()
985            } else {
986                // Otherwise, return NaN
987                Self::nan()
988            }
989        } else if exponent < T::zero() {
990            // t = -∞
991            if self.x.is_zero() && self.y.is_zero() && self.z.is_zero() {
992                // q is real --> handle special cases
993                match self.w.partial_cmp(&T::one()) {
994                    Some(core::cmp::Ordering::Greater) => T::zero().into(),
995                    Some(core::cmp::Ordering::Less) => T::infinity().into(),
996                    _ => Self::nan(),
997                }
998            } else if self.norm_sqr() > T::one() {
999                // |q| > 1
1000                Self::zero()
1001            } else {
1002                // Otherwise, return NaN
1003                Self::nan()
1004            }
1005        } else {
1006            // t is NaN
1007            debug_assert!(exponent.is_nan());
1008            Self::nan()
1009        }
1010    }
1011}
1012
1013#[cfg(any(feature = "std", feature = "libm"))]
1014impl<T> Quaternion<T>
1015where
1016    T: Float,
1017{
1018    /// Returns whether all components of the quaternion are finite.
1019    ///
1020    /// If a `Quaternion` has an infinite or `NaN` entry, the function returns
1021    /// `false`, otherwise `true`.
1022    ///
1023    /// # Example
1024    ///
1025    /// ```
1026    /// # use num_quaternion::Quaternion;
1027    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
1028    /// assert!(q.is_finite());
1029    /// ```
1030    pub fn is_finite(&self) -> bool {
1031        self.w.is_finite()
1032            && self.x.is_finite()
1033            && self.y.is_finite()
1034            && self.z.is_finite()
1035    }
1036
1037    /// Returns whether any component of the quaternion is `NaN`.
1038    ///
1039    /// # Example
1040    ///
1041    /// ```
1042    /// # use num_quaternion::Quaternion;
1043    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
1044    /// assert!(!q.has_nan());
1045    /// ```
1046    pub fn has_nan(&self) -> bool {
1047        self.w.is_nan() || self.x.is_nan() || self.y.is_nan() || self.z.is_nan()
1048    }
1049
1050    /// Returns whether all components of a quaternion are `NaN`.
1051    ///
1052    /// # Example
1053    ///
1054    /// ```
1055    /// # use num_quaternion::Q64;
1056    /// let q = Q64::nan();
1057    /// assert!(q.is_all_nan());
1058    /// ```
1059    pub fn is_all_nan(&self) -> bool {
1060        self.w.is_nan() && self.x.is_nan() && self.y.is_nan() && self.z.is_nan()
1061    }
1062}
1063
1064#[cfg(any(feature = "std", feature = "libm"))]
1065impl<T> Quaternion<T>
1066where
1067    T: Float + FloatConst,
1068{
1069    /// Computes the natural logarithm of a quaternion.
1070    ///
1071    /// The function implements the following guarantees for extreme input
1072    /// values:
1073    ///
1074    /// - The function is continuous onto the branch cut taking into account
1075    ///   the sign of the coefficient of $i$.
1076    /// - For all quaternions $q$ it holds `q.conj().ln() == q.ln().conj()`.
1077    /// - The signs of the coefficients of the imaginary parts of the outputs
1078    ///   are equal to the signs of the respective coefficients of the inputs.
1079    ///   This also holds for signs of zeros, but not for `NaNs`.
1080    /// - If $q = 0$, the result is $-\infty$. (The coefficients of $i$, $j$,
1081    ///   and $k$ are zero with the original signs copied.)
1082    /// - If the input has a `NaN` value, then the result is `NaN` in all
1083    ///   components.
1084    /// - Otherwise, if $q = w + xi + yj + zk$ where at least one of
1085    ///   $w, x, y, z$ is infinite, then the real part of the result is
1086    ///   $+\infty$ and the imaginary part is the imaginary part
1087    ///   of the logarithm of $f(w) + f(x)i + f(y)j + f(z)k$ where
1088    ///     - $f(+\infty) := 1$,
1089    ///     - $f(-\infty) :=-1$, and
1090    ///     - $f(s) := 0$ for finite values of $s$.
1091    ///
1092    /// # Example
1093    ///
1094    /// ```
1095    /// # use num_quaternion::Quaternion;
1096    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
1097    /// let ln_q = q.ln();
1098    /// ```
1099    pub fn ln(self) -> Self {
1100        // The square norm of the imaginary part.
1101        let sqr_norm_im = self.x * self.x + self.y * self.y + self.z * self.z;
1102        // The square norm of `self`.
1103        let norm_sqr = self.w * self.w + sqr_norm_im;
1104
1105        match norm_sqr.classify() {
1106            FpCategory::Normal => {
1107                // The normal case: First compute the real part of the result.
1108                let w =
1109                    norm_sqr.ln() * T::from(0.5).expect("Conversion failed");
1110
1111                if sqr_norm_im <= self.w * self.w * T::epsilon() {
1112                    // We're close to or on the positive real axis
1113                    if self.w.is_sign_positive() {
1114                        // This approximation leaves a relative error of less
1115                        // than a floating point epsilon for the imaginary part
1116                        let x = self.x / self.w;
1117                        let y = self.y / self.w;
1118                        let z = self.z / self.w;
1119                        Self::new(w, x, y, z)
1120                    } else if self.x.is_zero()
1121                        && self.y.is_zero()
1122                        && self.z.is_zero()
1123                    {
1124                        // We're on the negative real axis.
1125                        Self::new(w, T::PI().copysign(self.x), self.y, self.z)
1126                    } else if sqr_norm_im.is_normal() {
1127                        // We're close the the negative real axis. Compute the
1128                        // norm of the imaginary part.
1129                        let norm_im = sqr_norm_im.sqrt();
1130
1131                        // The angle of `self` to the positive real axis is
1132                        // pi minus the angle from the negative real axis.
1133                        // The angle from the negative real axis
1134                        // can be approximated by `norm_im / self.w.abs()`
1135                        // which is equal to `-norm_im / self.w`. This the
1136                        // angle from the positive real axis is
1137                        // `pi + norm_im / self.w`. We obtain the imaginary
1138                        // part of the result by multiplying this value by
1139                        // the imaginary part of the input normalized, or
1140                        // equivalently, by multiplying the imaginary part
1141                        // of the input by the following factor:
1142                        let f = T::PI() / norm_im + self.w.recip();
1143
1144                        Self::new(w, f * self.x, f * self.y, f * self.z)
1145                    } else {
1146                        // The imaginary part is so small, that the norm of the
1147                        // resulting imaginary part differs from `pi` by way
1148                        // less than half an ulp. Therefore, it's sufficient to
1149                        // normalize the imaginary part and multiply it by
1150                        // `pi`.
1151                        let f = T::one()
1152                            / (T::min_positive_value().sqrt() * T::epsilon());
1153                        // `f` is a power of two (for `f32` and `f64`), so the
1154                        // following multiplications are exact.
1155                        let xf = self.x * f;
1156                        let yf = self.y * f;
1157                        let zf = self.z * f;
1158                        let sqr_sum = xf * xf + yf * yf + zf * zf;
1159                        let im_norm_div_f = sqr_sum.sqrt();
1160                        let pi_div_f = T::PI() * f;
1161                        // We could try to reduce the number of divisions by
1162                        // computing `pi_div_f / im_norm_div_f` and then
1163                        // multiplying the imaginary part by this value.
1164                        // However, this reduces numerical accuracy, if the
1165                        // pi times the norm of the imaginary part is
1166                        // subnormal. We could also introduce another branch
1167                        // here, but this would make the code more complex
1168                        // and extend the worst case latency. Therefore, we
1169                        // keep the divisions like that.
1170                        Self::new(
1171                            w,
1172                            self.x * pi_div_f / im_norm_div_f,
1173                            self.y * pi_div_f / im_norm_div_f,
1174                            self.z * pi_div_f / im_norm_div_f,
1175                        )
1176                    }
1177                } else {
1178                    // The most natural case: We're far enough from the real
1179                    // axis and the norm of the input quaternion is large
1180                    // enough to exclude any numerical instabilities.
1181                    let norm_im = if sqr_norm_im.is_normal() {
1182                        // `sqr_norm_im` has maximum precision.
1183                        sqr_norm_im.sqrt()
1184                    } else {
1185                        // Otherwise, using `sqr_norm_im` is imprecise.
1186                        // We magnify the imaginary part first, so we can
1187                        // get around this problem.
1188                        let f = T::one() / T::min_positive_value().sqrt();
1189                        // `f` is a power of two (for `f32` and `f64`), so the
1190                        // following multiplications are exact.
1191                        let xf = self.x * f;
1192                        let yf = self.y * f;
1193                        let zf = self.z * f;
1194                        let sqr_sum = xf * xf + yf * yf + zf * zf;
1195                        // Since `f` is a power of two, the following line can
1196                        // be optimized into a multiplication without loss of
1197                        // precision.
1198                        sqr_sum.sqrt() / f
1199                    };
1200                    let angle = norm_im.atan2(self.w);
1201                    let x = self.x * angle / norm_im;
1202                    let y = self.y * angle / norm_im;
1203                    let z = self.z * angle / norm_im;
1204                    Self::new(w, x, y, z)
1205                }
1206            }
1207            FpCategory::Zero if self.is_zero() => {
1208                Self::new(T::neg_infinity(), self.x, self.y, self.z)
1209            }
1210            FpCategory::Nan => Self::nan(),
1211            FpCategory::Infinite => {
1212                // The square norm overflows.
1213                if self.is_finite() {
1214                    // There is no infinity entry in the quaternion. Hence,
1215                    // We can scale the quaternion down and recurse.
1216                    let factor = T::one() / T::max_value().sqrt();
1217                    (self * factor).ln() - factor.ln()
1218                } else {
1219                    // There is an infinite value in the input quaternion.
1220                    // Let's map the infinite entries to `±1` and all other
1221                    // entries to `±0` maintaining the sign.
1222                    let f = |r: T| {
1223                        if r.is_infinite() {
1224                            r.signum()
1225                        } else {
1226                            T::zero().copysign(r)
1227                        }
1228                    };
1229                    let q =
1230                        Self::new(f(self.w), f(self.x), f(self.y), f(self.z));
1231                    // TODO: Optimize this. There are only a few possible
1232                    // angles which could be hard-coded. Recursing here
1233                    // may be a bit heavy.
1234                    q.ln() + T::infinity()
1235                }
1236            }
1237            _ => {
1238                // Square norm is less than smallest positive normal value,
1239                // but `self` is not zero. Let's scale up the value to obtain
1240                // the precision by recursing and then fix the factor
1241                // afterwards.
1242                let factor = T::one() / T::min_positive_value();
1243                (self * factor).ln() - factor.ln()
1244            }
1245        }
1246    }
1247}
1248
1249#[cfg(any(feature = "std", feature = "libm"))]
1250impl<T> Quaternion<T>
1251where
1252    T: Float + FloatConst,
1253{
1254    // Computes the square root of a quaternion.
1255    ///
1256    /// Given the input quaternion $c$, this function returns the quaternion
1257    /// $q$ which satisfies $q^2 = c$ and has a real part with a positive sign.
1258    ///
1259    /// For extreme values, the following guarantees are implemented:
1260    ///
1261    /// - If any coefficient in $c$ is `NaN`, then the result is `NaN` in all
1262    ///   components.
1263    /// - Otherwise, for any input $c$, the expression `c.sqrt().conj()` is
1264    ///   exactly equivalent to `c.conj().sqrt()`, including the signs of
1265    ///   zeros and infinities, if any.
1266    /// - For any input $c$, `c.sqrt().w` always has a positive sign.
1267    /// - For any input $c$, the signs of the three output imaginary parts are
1268    ///   the same as the input imaginary parts in their respective order,
1269    ///   except in the case of a `NaN` input.
1270    /// - For negative real inputs $c$, the result is $\pm\sqrt{-c} i$, where
1271    ///   the sign is determined by the sign of the input's coefficient of $i$.
1272    /// - If there is at least one infinite coefficient in the imaginary part,
1273    ///   then the result will have the same infinite imaginary coefficients
1274    ///   and the real part is $+\infty$. All other coefficients of the result
1275    ///   are $0$ with the sign of the respective input.
1276    /// - If the real part is $-\infty$ and the imaginary part is finite, then
1277    ///   the result is $\pm\infty i$ with the sign of the coefficient of $i$
1278    ///   from the input.
1279    ///
1280    /// # Example
1281    ///
1282    /// ```
1283    /// # use num_quaternion::Quaternion;
1284    /// let q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0);
1285    /// let sqrt_q = q.sqrt();
1286    /// ```
1287    pub fn sqrt(self) -> Self {
1288        let zero = T::zero();
1289        let one = T::one();
1290        let two = one + one;
1291        let half = one / two;
1292        let inf = T::infinity();
1293        let s = one / T::min_positive_value(); // large scale factor
1294        let norm_sqr = self.norm_sqr();
1295        match norm_sqr.classify() {
1296            FpCategory::Normal => {
1297                // norm of the input
1298                let norm = norm_sqr.sqrt();
1299
1300                if self.w.is_sign_positive() {
1301                    // Compute double the real part of the result directly and
1302                    // robustly.
1303                    //
1304                    // Note: We could also compute the real part directly.
1305                    // However, this would be inferior for the following
1306                    //  reasons:
1307                    //
1308                    // - To compute the imaginary parts of the result, we
1309                    //   would need to double the real part anyway, which
1310                    //   would require an extra arithmetic operation, adding
1311                    //   to the latency of the computation.
1312                    // - To avoid this latency, we could also multiply
1313                    //   `self.x`, `self.y`, and `self.z` by 1/2 and then
1314                    //   divide by the real part (which takes longer to
1315                    //   compute). However, this could cost some accuracy
1316                    //   for subnormal imaginary parts.
1317                    let wx2 = ((self.w + norm) * two).sqrt();
1318
1319                    Self::new(
1320                        wx2 * half,
1321                        self.x / wx2,
1322                        self.y / wx2,
1323                        self.z / wx2,
1324                    )
1325                } else {
1326                    // The first formula for the real part of the result may
1327                    //  not be robust if the sign of the input real part is
1328                    // negative.
1329                    let im_norm_sqr =
1330                        self.y * self.y + (self.x * self.x + self.z * self.z);
1331                    if im_norm_sqr >= T::min_positive_value() {
1332                        // Second formula for the real part of the result,
1333                        // which is robust for inputs with a negative real
1334                        // part.
1335                        let wx2 = (im_norm_sqr * two / (norm - self.w)).sqrt();
1336
1337                        Self::new(
1338                            wx2 * half,
1339                            self.x / wx2,
1340                            self.y / wx2,
1341                            self.z / wx2,
1342                        )
1343                    } else if self.x.is_zero()
1344                        && self.y.is_zero()
1345                        && self.z.is_zero()
1346                    {
1347                        // The input is a negative real number.
1348                        Self::new(
1349                            zero,
1350                            (-self.w).sqrt().copysign(self.x),
1351                            self.y,
1352                            self.z,
1353                        )
1354                    } else {
1355                        // `im_norm_sqr` is subnormal. Compute the norm of the
1356                        // imaginary part by scaling up first.
1357                        let sx = s * self.x;
1358                        let sy = s * self.y;
1359                        let sz = s * self.z;
1360                        let im_norm =
1361                            (sy * sy + (sx * sx + sz * sz)).sqrt() / s;
1362
1363                        // Compute the real part according to the second
1364                        // formula from above.
1365                        let w = im_norm / (half * (norm - self.w)).sqrt();
1366
1367                        Self::new(w * half, self.x / w, self.y / w, self.z / w)
1368                    }
1369                }
1370            }
1371            FpCategory::Zero if self.is_zero() => {
1372                Self::new(zero, self.x, self.y, self.z)
1373            }
1374            FpCategory::Infinite => {
1375                if self.w == inf
1376                    || self.x.is_infinite()
1377                    || self.y.is_infinite()
1378                    || self.z.is_infinite()
1379                {
1380                    let f = |a: T| {
1381                        if a.is_infinite() {
1382                            a
1383                        } else {
1384                            zero.copysign(a)
1385                        }
1386                    };
1387                    Self::new(inf, f(self.x), f(self.y), f(self.z))
1388                } else if self.w == -inf {
1389                    Self::new(
1390                        zero,
1391                        inf.copysign(self.x),
1392                        zero.copysign(self.y),
1393                        zero.copysign(self.z),
1394                    )
1395                } else {
1396                    // Input has no infinities. Therefore, the square norm
1397                    // must have overflowed. Let's scale down.
1398                    // In release mode, the compiler turns the division into
1399                    // a multiplication, because `s` is a power of two. Thus,
1400                    // it's fast.
1401                    (self / s).sqrt() * s.sqrt()
1402                }
1403            }
1404            FpCategory::Nan => Self::nan(),
1405            _ => {
1406                // Square norm is subnormal or zero (underflow), but `self`
1407                // is not zero. Let's scale up.
1408                // In release mode, the compiler turns the division into a
1409                // multiplication, because `s.sqrt()` is a power of two. Thus,
1410                // it's fast.
1411                (self * s).sqrt() / s.sqrt()
1412            }
1413        }
1414    }
1415}
1416
1417#[cfg(feature = "serde")]
1418impl<T> serde::Serialize for Quaternion<T>
1419where
1420    T: serde::Serialize,
1421{
1422    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1423    where
1424        S: serde::Serializer,
1425    {
1426        (&self.w, &self.x, &self.y, &self.z).serialize(serializer)
1427    }
1428}
1429
1430#[cfg(feature = "serde")]
1431impl<'de, T> serde::Deserialize<'de> for Quaternion<T>
1432where
1433    T: serde::Deserialize<'de>,
1434{
1435    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1436    where
1437        D: serde::Deserializer<'de>,
1438    {
1439        let (w, x, y, z) = serde::Deserialize::deserialize(deserializer)?;
1440        Ok(Self::new(w, x, y, z))
1441    }
1442}
1443
1444#[cfg(test)]
1445mod tests {
1446
1447    use {
1448        crate::{Quaternion, Q32, Q64, UQ32, UQ64},
1449        num_traits::{ConstOne, ConstZero, Inv, One, Zero},
1450    };
1451
1452    #[cfg(any(feature = "std", feature = "libm"))]
1453    use num_traits::FloatConst;
1454
1455    #[test]
1456    fn test_new() {
1457        // Test the new function
1458        let q = Quaternion::new(1.0, 2.0, 3.0, 4.0);
1459        assert_eq!(q.w, 1.0);
1460        assert_eq!(q.x, 2.0);
1461        assert_eq!(q.y, 3.0);
1462        assert_eq!(q.z, 4.0);
1463    }
1464
1465    #[test]
1466    fn test_zero_constant() {
1467        // Test the zero function
1468        let q1 = Q32::ZERO;
1469        let q2 = Q32::default();
1470        assert_eq!(q1, q2);
1471    }
1472
1473    #[test]
1474    fn test_const_zero_trait() {
1475        // Test the ConstZero trait
1476        let q3 = <Q64 as ConstZero>::ZERO;
1477        let q4 = Q64::default();
1478        assert_eq!(q3, q4);
1479    }
1480
1481    #[test]
1482    fn test_zero_trait() {
1483        // Test the Zero trait's `zero` method
1484        let q1 = Q32::zero();
1485        let q2 = Q32::default();
1486        assert_eq!(q1, q2);
1487        assert!(q1.is_zero());
1488    }
1489
1490    #[test]
1491    fn test_zero_trait_set_zero() {
1492        // Test the Zero trait's `set_zero` method
1493        let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0);
1494        q.set_zero();
1495        assert!(q.is_zero());
1496    }
1497
1498    #[test]
1499    fn test_one_constant() {
1500        // Test the `ONE` constant
1501        assert_eq!(Q32::ONE, Q32::new(1.0, 0.0, 0.0, 0.0))
1502    }
1503
1504    #[test]
1505    fn test_i_constant() {
1506        // Test the `I` constant
1507        assert_eq!(Q64::I, Q64::new(0.0, 1.0, 0.0, 0.0))
1508    }
1509
1510    #[test]
1511    fn test_j_constant() {
1512        // Test the `J` constant
1513        assert_eq!(Q32::J, Q32::new(0.0, 0.0, 1.0, 0.0))
1514    }
1515
1516    #[test]
1517    fn test_k_constant() {
1518        // Test the `K` constant
1519        assert_eq!(Q64::K, Q64::new(0.0, 0.0, 0.0, 1.0))
1520    }
1521
1522    #[test]
1523    fn test_const_one_trait() {
1524        // Test the ConstOne trait
1525        assert_eq!(<Q32 as ConstOne>::ONE, Q32::new(1.0, 0.0, 0.0, 0.0))
1526    }
1527
1528    #[test]
1529    fn test_one_trait_one() {
1530        // Test the One trait's `one` method
1531        assert_eq!(<Q64 as One>::one(), Q64::ONE);
1532        assert!(Q64::ONE.is_one());
1533    }
1534
1535    #[test]
1536    fn test_one_trait_set_one() {
1537        // Test the One trait's `set_one` method
1538        let mut q = Q32::new(2.0, 3.0, 4.0, 5.0);
1539        q.set_one();
1540        assert!(q.is_one());
1541    }
1542
1543    #[test]
1544    fn test_one_func() {
1545        // Test the `one` function
1546        assert_eq!(Q32::one(), Q32::new(1.0, 0.0, 0.0, 0.0));
1547    }
1548
1549    #[test]
1550    fn test_i_func() {
1551        // Test the `i` function
1552        assert_eq!(Q64::i(), Q64::new(0.0, 1.0, 0.0, 0.0));
1553    }
1554
1555    #[test]
1556    fn test_j_func() {
1557        // Test the `j` function
1558        assert_eq!(Q64::j(), Q64::new(0.0, 0.0, 1.0, 0.0));
1559    }
1560
1561    #[test]
1562    fn test_k_func() {
1563        // Test the `k` function
1564        assert_eq!(Q64::k(), Q64::new(0.0, 0.0, 0.0, 1.0));
1565    }
1566
1567    #[test]
1568    fn test_norm_sqr() {
1569        // Test the norm_sqr function
1570        assert_eq!(Q32::ZERO.norm_sqr(), 0.0);
1571        assert_eq!(Q64::ONE.norm_sqr(), 1.0);
1572        assert_eq!(Q32::I.norm_sqr(), 1.0);
1573        assert_eq!(Q64::J.norm_sqr(), 1.0);
1574        assert_eq!(Q32::K.norm_sqr(), 1.0);
1575        assert_eq!(Q64::new(-8.0, 4.0, 2.0, -1.0).norm_sqr(), 85.0);
1576        assert_eq!(Q32::new(1.0, -2.0, 3.0, -4.0).norm_sqr(), 30.0);
1577    }
1578
1579    #[test]
1580    fn test_conj() {
1581        // Test the conj function
1582        assert_eq!(Q64::ONE.conj(), Q64::ONE);
1583        assert_eq!(Q32::I.conj(), -Q32::I);
1584        assert_eq!(Q64::J.conj(), -Q64::J);
1585        assert_eq!(Q32::K.conj(), -Q32::K);
1586        assert_eq!(
1587            Q64::new(-8.0, 4.0, 2.0, -1.0).conj(),
1588            Q64::new(-8.0, -4.0, -2.0, 1.0)
1589        );
1590        assert_eq!(
1591            Q32::new(1.0, -2.0, 3.0, -4.0).conj(),
1592            Q32::new(1.0, 2.0, -3.0, 4.0)
1593        );
1594    }
1595
1596    #[test]
1597    fn test_inv_func() {
1598        // Test the inv function
1599        assert_eq!(Q64::ONE.inv(), Q64::ONE);
1600        assert_eq!(Q32::I.inv(), -Q32::I);
1601        assert_eq!(Q64::J.inv(), -Q64::J);
1602        assert_eq!(Q32::K.inv(), -Q32::K);
1603        assert_eq!(
1604            Q64::new(1.0, 1.0, -1.0, -1.0).inv(),
1605            Q64::new(0.25, -0.25, 0.25, 0.25)
1606        );
1607        assert_eq!(
1608            Q32::new(1.0, -2.0, 2.0, -4.0).inv(),
1609            Q32::new(0.04, 0.08, -0.08, 0.16)
1610        );
1611    }
1612
1613    #[test]
1614    fn test_inv_trait_for_ref() {
1615        // Test the inv trait for references
1616        assert_eq!(Inv::inv(&Q64::ONE), Q64::ONE);
1617        assert_eq!(Inv::inv(&Q32::I), -Q32::I);
1618        assert_eq!(Inv::inv(&Q64::J), -Q64::J);
1619        assert_eq!(Inv::inv(&Q32::K), -Q32::K);
1620        assert_eq!(
1621            Inv::inv(&Q64::new(1.0, 1.0, -1.0, -1.0)),
1622            Q64::new(0.25, -0.25, 0.25, 0.25)
1623        );
1624        assert_eq!(
1625            Inv::inv(&Q32::new(1.0, -2.0, 2.0, -4.0)),
1626            Q32::new(0.04, 0.08, -0.08, 0.16)
1627        );
1628    }
1629
1630    #[test]
1631    fn test_inv_trait_for_val() {
1632        // Test the inv trait
1633        assert_eq!(Inv::inv(Q64::ONE), Q64::ONE);
1634        assert_eq!(Inv::inv(Q32::I), -Q32::I);
1635        assert_eq!(Inv::inv(Q64::J), -Q64::J);
1636        assert_eq!(Inv::inv(Q32::K), -Q32::K);
1637        assert_eq!(
1638            Inv::inv(Q64::new(1.0, 1.0, -1.0, -1.0)),
1639            Q64::new(0.25, -0.25, 0.25, 0.25)
1640        );
1641        assert_eq!(
1642            Inv::inv(Q32::new(1.0, -2.0, 2.0, -4.0)),
1643            Q32::new(0.04, 0.08, -0.08, 0.16)
1644        );
1645    }
1646
1647    #[cfg(any(feature = "std", feature = "libm"))]
1648    #[test]
1649    fn test_norm_normal_values() {
1650        // Test the norm function for normal floating point values
1651        let q = Q64::new(1.0, 2.0, 3.0, 4.0);
1652        assert_eq!(q.norm(), 30.0f64.sqrt());
1653    }
1654
1655    #[cfg(any(feature = "std", feature = "libm"))]
1656    #[test]
1657    fn test_norm_zero_quaternion() {
1658        // Test the norm function for a zero quaternion
1659        let q = Q32::new(0.0, 0.0, 0.0, 0.0);
1660        assert_eq!(q.norm(), 0.0, "Norm of zero quaternion should be 0");
1661    }
1662
1663    #[cfg(any(feature = "std", feature = "libm"))]
1664    #[test]
1665    fn test_norm_subnormal_values() {
1666        // Test the norm function for subnormal floating point values
1667        let s = f64::MIN_POSITIVE * 0.25;
1668        let q = Q64::new(s, s, s, s);
1669        assert!(
1670            (q.norm() - 2.0 * s).abs() <= 2.0 * s * f64::EPSILON,
1671            "Norm of subnormal is computed correctly"
1672        );
1673    }
1674
1675    #[cfg(any(feature = "std", feature = "libm"))]
1676    #[test]
1677    fn test_norm_large_values() {
1678        // Test the norm function for large floating point values
1679        let s = f64::MAX * 0.50;
1680        let q = Q64::new(s, s, s, s);
1681        assert!(
1682            (q.norm() - 2.0 * s).abs() <= 2.0 * s * f64::EPSILON,
1683            "Norm of large values is computed correctly"
1684        );
1685    }
1686
1687    #[cfg(any(feature = "std", feature = "libm"))]
1688    #[test]
1689    fn test_norm_infinite_values() {
1690        // Test the norm function for infinite floating point values
1691        let inf = f32::INFINITY;
1692        assert_eq!(Q32::new(inf, 1.0, 1.0, 1.0).norm(), inf);
1693        assert_eq!(Q32::new(1.0, inf, 1.0, 1.0).norm(), inf);
1694        assert_eq!(Q32::new(1.0, 1.0, inf, 1.0).norm(), inf);
1695        assert_eq!(Q32::new(1.0, 1.0, 1.0, inf).norm(), inf);
1696    }
1697
1698    #[cfg(any(feature = "std", feature = "libm"))]
1699    #[test]
1700    fn test_norm_nan_values() {
1701        // Test the norm function for NaN floating point values
1702        let nan = f32::NAN;
1703        assert!(Q32::new(nan, 1.0, 1.0, 1.0).norm().is_nan());
1704        assert!(Q32::new(1.0, nan, 1.0, 1.0).norm().is_nan());
1705        assert!(Q32::new(1.0, 1.0, nan, 1.0).norm().is_nan());
1706        assert!(Q32::new(1.0, 1.0, 1.0, nan).norm().is_nan());
1707    }
1708
1709    #[cfg(any(feature = "std", feature = "libm"))]
1710    #[test]
1711    fn test_fast_norm_normal_values() {
1712        // Test the fast_norm function for normal floating point values
1713        let q = Q64 {
1714            w: 1.1,
1715            x: 2.7,
1716            y: 3.4,
1717            z: 4.9,
1718        };
1719        assert_eq!(
1720            q.fast_norm(),
1721            q.norm(),
1722            "Fast norm is equal to norm for normal floating point values"
1723        );
1724    }
1725
1726    #[cfg(any(feature = "std", feature = "libm"))]
1727    #[test]
1728    fn test_fast_norm_zero_quaternion() {
1729        // Test the fast_norm function for a zero quaternion
1730        assert_eq!(
1731            Q32::zero().fast_norm(),
1732            0.0,
1733            "Fast norm of zero quaternion should be 0"
1734        );
1735    }
1736
1737    #[cfg(any(feature = "std", feature = "libm"))]
1738    #[test]
1739    fn test_fast_norm_infinite_values() {
1740        // Test the fast_norm function for infinite floating point values
1741        let inf = f32::INFINITY;
1742        assert_eq!(Q32::new(inf, 1.0, 1.0, 1.0).fast_norm(), inf);
1743        assert_eq!(Q32::new(1.0, inf, 1.0, 1.0).fast_norm(), inf);
1744        assert_eq!(Q32::new(1.0, 1.0, inf, 1.0).fast_norm(), inf);
1745        assert_eq!(Q32::new(1.0, 1.0, 1.0, inf).fast_norm(), inf);
1746    }
1747
1748    #[cfg(any(feature = "std", feature = "libm"))]
1749    #[test]
1750    fn test_fast_norm_nan_values() {
1751        // Test the fast_norm function for NaN floating point values
1752        let nan = f32::NAN;
1753        assert!(Q32::new(nan, 1.0, 1.0, 1.0).fast_norm().is_nan());
1754        assert!(Q32::new(1.0, nan, 1.0, 1.0).fast_norm().is_nan());
1755        assert!(Q32::new(1.0, 1.0, nan, 1.0).fast_norm().is_nan());
1756        assert!(Q32::new(1.0, 1.0, 1.0, nan).fast_norm().is_nan());
1757    }
1758
1759    #[cfg(any(feature = "std", feature = "libm"))]
1760    #[test]
1761    fn test_fast_norm_for_norm_sqr_underflow() {
1762        // Test the fast_norm function for underflowing norm_sqr
1763        let s = f64::MIN_POSITIVE;
1764        let q = Q64::new(s, s, s, s);
1765        assert_eq!(q.fast_norm(), 0.0);
1766    }
1767
1768    #[cfg(any(feature = "std", feature = "libm"))]
1769    #[test]
1770    fn test_fast_norm_for_norm_sqr_overflow() {
1771        // Test the fast_norm function for overflowing norm_sqr
1772        let s = f32::MAX / 16.0;
1773        let q = Q32::new(s, s, s, s);
1774        assert_eq!(q.fast_norm(), f32::INFINITY);
1775    }
1776
1777    #[cfg(any(feature = "std", feature = "libm"))]
1778    #[test]
1779    fn test_normalize() {
1780        // Test the normalize function
1781        assert_eq!(Q64::ONE.normalize().unwrap(), UQ64::ONE);
1782        assert_eq!(Q32::I.normalize().unwrap(), UQ32::I);
1783        assert_eq!(Q64::J.normalize().unwrap(), UQ64::J);
1784        assert_eq!(Q32::K.normalize().unwrap(), UQ32::K);
1785        assert_eq!(
1786            Q64::new(9.0, 12.0, -12.0, -16.0)
1787                .normalize()
1788                .unwrap()
1789                .into_quaternion(),
1790            Q64::new(0.36, 0.48, -0.48, -0.64)
1791        );
1792        assert_eq!(
1793            Q32::new(-1.0, -1.0, 1.0, -1.0)
1794                .normalize()
1795                .unwrap()
1796                .into_quaternion(),
1797            Q32::new(-0.5, -0.5, 0.5, -0.5)
1798        );
1799    }
1800
1801    #[cfg(any(feature = "std", feature = "libm"))]
1802    #[test]
1803    fn test_normalize_zero() {
1804        // Test the normalize function for zero
1805        assert_eq!(Q64::ZERO.normalize(), None);
1806    }
1807
1808    #[cfg(any(feature = "std", feature = "libm"))]
1809    #[test]
1810    fn test_normalize_infinity() {
1811        // Test the normalize function for infinite quaternions
1812        assert_eq!(Q64::new(f64::INFINITY, 0.0, 0.0, 0.0).normalize(), None);
1813        assert_eq!(
1814            Q64::new(0.0, f64::NEG_INFINITY, 0.0, 0.0).normalize(),
1815            None
1816        );
1817        assert_eq!(
1818            Q64::new(0.0, 0.0, f64::NEG_INFINITY, 0.0).normalize(),
1819            None
1820        );
1821        assert_eq!(Q64::new(0.0, 0.0, 0.0, f64::INFINITY).normalize(), None);
1822    }
1823
1824    #[cfg(any(feature = "std", feature = "libm"))]
1825    #[test]
1826    fn test_normalize_nan() {
1827        // Test the normalize function for NaN quaternions
1828        assert_eq!(Q64::new(f64::NAN, 0.0, 0.0, 0.0).normalize(), None);
1829        assert_eq!(Q64::new(0.0, f64::NAN, 0.0, 0.0).normalize(), None);
1830        assert_eq!(Q64::new(0.0, 0.0, f64::NAN, 0.0).normalize(), None);
1831        assert_eq!(Q64::new(0.0, 0.0, 0.0, f64::NAN).normalize(), None);
1832    }
1833
1834    #[cfg(any(feature = "std", feature = "libm"))]
1835    #[test]
1836    fn test_normalize_infinity_and_nan() {
1837        // Test the normalize function for quaternions with infinite and NaN
1838        // values
1839        assert_eq!(
1840            Q64::new(f64::INFINITY, f64::NAN, 1.0, 0.0).normalize(),
1841            None
1842        );
1843        assert_eq!(
1844            Q64::new(1.0, 0.0, f64::INFINITY, f64::NAN).normalize(),
1845            None
1846        );
1847    }
1848
1849    #[cfg(any(feature = "std", feature = "libm"))]
1850    #[test]
1851    fn test_normalize_subnormal() {
1852        // Test the normalize function for subnormal quaternions
1853        let s = f64::MIN_POSITIVE / 4.0;
1854        let q = Q64::new(s, s, s, s);
1855        assert_eq!(
1856            q.normalize().unwrap().into_inner(),
1857            Q64::new(0.5, 0.5, 0.5, 0.5)
1858        );
1859    }
1860
1861    #[test]
1862    fn test_from_underlying_type_val() {
1863        // Test the From trait for values
1864        assert_eq!(Q64::from(-5.0), Q64::new(-5.0, 0.0, 0.0, 0.0));
1865        assert_eq!(Into::<Q32>::into(42.0), Q32::new(42.0, 0.0, 0.0, 0.0));
1866    }
1867
1868    #[allow(clippy::needless_borrows_for_generic_args)]
1869    #[test]
1870    fn test_from_underlying_type_ref() {
1871        // Test the From trait for references
1872        assert_eq!(Q64::from(&-5.0), Q64::new(-5.0, 0.0, 0.0, 0.0));
1873        assert_eq!(Into::<Q32>::into(&42.0), Q32::new(42.0, 0.0, 0.0, 0.0));
1874    }
1875
1876    #[test]
1877    fn test_from_unit_quaternion() {
1878        // Test the From trait for unit quaternions
1879        assert_eq!(Q32::from(UQ32::ONE), Q32::ONE);
1880        assert_eq!(Q64::from(UQ64::I), Q64::I);
1881        assert_eq!(Q32::from(UQ32::J), Q32::J);
1882        assert_eq!(Q64::from(UQ64::K), Q64::K);
1883    }
1884
1885    #[allow(clippy::needless_borrows_for_generic_args)]
1886    #[test]
1887    fn test_from_unit_quaternion_ref() {
1888        // Test the From trait for references to unit quaternions
1889        assert_eq!(<&Q32 as From<&UQ32>>::from(&UQ32::ONE), &Q32::ONE);
1890        assert_eq!(<&Q64 as From<&UQ64>>::from(&UQ64::I), &Q64::I);
1891        assert_eq!(<&Q32 as From<&UQ32>>::from(&UQ32::J), &Q32::J);
1892        assert_eq!(<&Q64 as From<&UQ64>>::from(&UQ64::K), &Q64::K);
1893    }
1894
1895    #[test]
1896    fn test_powu() {
1897        // Test the power function for unsigned integer exponents
1898        for q in [
1899            Q32::ONE,
1900            Q32::ZERO,
1901            Q32::I,
1902            Q32::new(1.0, 1.0, 1.0, 1.0),
1903            Q32::new(1.0, 2.0, -3.0, 4.0),
1904        ] {
1905            let mut expected = Q32::ONE;
1906            for e in 0..16 {
1907                assert_eq!(q.powu(e), expected);
1908                expected *= q;
1909            }
1910        }
1911    }
1912
1913    #[test]
1914    fn test_powi() {
1915        // Test the power function for signed integer exponents
1916        for q in [
1917            Q32::ONE,
1918            Q32::I,
1919            Q32::new(1.0, 1.0, 1.0, 1.0),
1920            Q32::new(1.0, 2.0, -3.0, 4.0),
1921        ] {
1922            let mut expected = Q32::ONE;
1923            for e in 0..16 {
1924                assert_eq!(q.powi(e), expected);
1925                assert!(
1926                    (q.powi(-e) - expected.inv()).norm_sqr()
1927                        / expected.norm_sqr()
1928                        < f32::EPSILON
1929                );
1930                expected *= q;
1931            }
1932        }
1933    }
1934
1935    #[cfg(any(feature = "std", feature = "libm"))]
1936    #[test]
1937    fn test_exp_zero_quaternion() {
1938        // Test the exponential function for the zero quaternion
1939        assert_eq!(Q32::ZERO.exp(), Q32::ONE);
1940    }
1941
1942    #[cfg(any(feature = "std", feature = "libm"))]
1943    #[test]
1944    fn test_exp_real_part_only() {
1945        // Test the exponential function for quaternions with real part only
1946        assert_eq!(Q32::ONE.exp(), core::f32::consts::E.into())
1947    }
1948
1949    #[cfg(any(feature = "std", feature = "libm"))]
1950    #[test]
1951    fn test_exp_imaginary_part_only() {
1952        // Test the exponential function for quaternions with imaginary part only
1953        assert!(
1954            (Q64::I.exp() - Q64::new(1.0f64.cos(), 1.0f64.sin(), 0.0, 0.0))
1955                .norm()
1956                <= f64::EPSILON
1957        );
1958    }
1959
1960    #[cfg(any(feature = "std", feature = "libm"))]
1961    #[test]
1962    fn test_exp_complex_quaternion() {
1963        // Test the exponential function for a complex quaternion
1964        let q = Q32::new(1.0, 1.0, 1.0, 1.0);
1965        let exp_q = q.exp();
1966        let expected_norm = 1.0f32.exp();
1967        let angle = 3.0f32.sqrt();
1968        assert!(
1969            (exp_q
1970                - Q32::new(
1971                    expected_norm * angle.cos(),
1972                    expected_norm * angle.sin() / angle,
1973                    expected_norm * angle.sin() / angle,
1974                    expected_norm * angle.sin() / angle
1975                ))
1976            .norm()
1977                <= 2.0 * expected_norm * f32::EPSILON
1978        );
1979    }
1980
1981    #[cfg(any(feature = "std", feature = "libm"))]
1982    #[test]
1983    fn test_exp_negative_real_part() {
1984        // Test the exponential function for quaternions with negative real part
1985        let q = Q64::new(-1000.0, 0.0, f64::INFINITY, f64::NAN);
1986        let exp_q = q.exp();
1987        assert_eq!(exp_q, Q64::zero());
1988    }
1989
1990    #[cfg(any(feature = "std", feature = "libm"))]
1991    #[test]
1992    fn test_exp_nan_input() {
1993        // Test the exponential function for quaternions with NaN input
1994        let q = Q32::new(f32::NAN, 1.0, 1.0, 1.0);
1995        let exp_q = q.exp();
1996        assert!(exp_q.w.is_nan());
1997        assert!(exp_q.x.is_nan());
1998        assert!(exp_q.y.is_nan());
1999        assert!(exp_q.z.is_nan());
2000    }
2001
2002    #[cfg(any(feature = "std", feature = "libm"))]
2003    #[test]
2004    fn test_exp_large_imaginary_norm() {
2005        // Test the exponential function for quaternions with large imaginary norm
2006        let q = Q32::new(1.0, 1e30, 1e30, 1e30);
2007        let exp_q = q.exp();
2008        assert!(exp_q.w.is_nan());
2009        assert!(exp_q.x.is_nan());
2010        assert!(exp_q.y.is_nan());
2011        assert!(exp_q.z.is_nan());
2012    }
2013
2014    #[cfg(any(feature = "std", feature = "libm"))]
2015    #[test]
2016    fn test_exp_infinite_real_part() {
2017        // Test the exponential function for quaternions with infinite real part
2018        let inf = f64::INFINITY;
2019        let q = Quaternion::new(inf, 1.0, 1.0, 1.0);
2020        let exp_q = q.exp();
2021        assert_eq!(exp_q.w, -inf);
2022        assert_eq!(exp_q.x, inf);
2023        assert_eq!(exp_q.y, inf);
2024        assert_eq!(exp_q.z, inf);
2025    }
2026
2027    #[cfg(any(feature = "std", feature = "libm"))]
2028    #[test]
2029    fn test_exp_infinite_imaginary_part() {
2030        // Test the exponential function for quaternions with infinite
2031        // imaginary part
2032        let q = Q32::new(1.0, f32::INFINITY, 0.0, 0.0);
2033        let exp_q = q.exp();
2034        assert!(exp_q.w.is_nan());
2035        assert!(exp_q.x.is_nan());
2036        assert!(exp_q.y.is_nan());
2037        assert!(exp_q.z.is_nan());
2038    }
2039
2040    #[cfg(any(feature = "std", feature = "libm"))]
2041    #[test]
2042    fn test_exp_small_imaginary_norm() {
2043        // Test the exponential function for quaternions with small norm of the imaginary part
2044        let epsilon = f32::EPSILON;
2045        let q = Quaternion::new(0.5, epsilon, epsilon, epsilon);
2046        let exp_q = q.exp();
2047        let result_norm = q.w.exp();
2048        let expected_exp_q = Quaternion::new(
2049            result_norm,
2050            result_norm * q.x,
2051            result_norm * q.y,
2052            result_norm * q.z,
2053        );
2054        assert!((exp_q - expected_exp_q).norm() <= epsilon);
2055    }
2056
2057    #[cfg(any(feature = "std", feature = "libm"))]
2058    #[test]
2059    fn test_exp_infinite_result_angle_greater_than_90_degrees() {
2060        // Test the exponential function for quaternions with an angle greater
2061        // than 90 degrees
2062        let angle = f32::PI() * 0.75; // Angle > 90 degrees
2063        let q = Q32::new(f32::INFINITY, angle, 0.0, 0.0);
2064        let exp_q = q.exp();
2065        assert_eq!(exp_q.w, -f32::INFINITY);
2066        assert_eq!(exp_q.x, f32::INFINITY);
2067        assert_eq!(exp_q.y, 0.0);
2068        assert_eq!(exp_q.z, 0.0);
2069    }
2070
2071    #[cfg(any(feature = "std", feature = "libm"))]
2072    #[test]
2073    fn test_exp_infinite_result_angle_greater_than_180_degrees() {
2074        // Test the exponential function for quaternions with an angle greater
2075        // than 180 degrees
2076        let angle = f64::PI() * 1.25; // Angle > 180 degrees
2077        let q = Q64::new(f64::INFINITY, 0.0, angle, 0.0);
2078        let exp_q = q.exp();
2079        assert_eq!(exp_q.w, -f64::INFINITY);
2080        assert_eq!(exp_q.x, 0.0);
2081        assert_eq!(exp_q.y, -f64::INFINITY);
2082        assert_eq!(exp_q.z, 0.0);
2083    }
2084
2085    #[cfg(any(feature = "std", feature = "libm"))]
2086    #[test]
2087    fn test_expf_zero_base_positive_real_quaternion() {
2088        // Test the exponential function for a quaternion with a zero base and a
2089        // positive real part
2090        assert_eq!(Q64::new(1.0, 0.0, 0.0, 0.0).expf(0.0), Q64::ZERO);
2091    }
2092
2093    #[cfg(any(feature = "std", feature = "libm"))]
2094    #[test]
2095    fn test_expf_zero_base_negative_real_quaternion() {
2096        // Test the exponential function for a quaternion with a zero base and a
2097        // negative real part
2098        assert_eq!(
2099            Q32::new(-1.0, 0.0, 0.0, 0.0).expf(0.0),
2100            f32::INFINITY.into()
2101        );
2102    }
2103
2104    #[cfg(any(feature = "std", feature = "libm"))]
2105    #[test]
2106    fn test_expf_infinity_base_positive_real_quaternion() {
2107        // Test the exponential function for a quaternion with an infinite base
2108        // and a positive real part
2109        let inf = f64::INFINITY;
2110        assert_eq!(Q64::new(1.0, 0.0, 0.0, 0.0).expf(inf), inf.into());
2111    }
2112
2113    #[cfg(any(feature = "std", feature = "libm"))]
2114    #[test]
2115    fn test_expf_infinity_base_negative_real_quaternion() {
2116        // Test the exponential function for a quaternion with an infinite base
2117        // and a negative real part
2118        assert_eq!(
2119            Q32::new(-1.0, 0.0, 0.0, 0.0).expf(f32::INFINITY),
2120            0.0f32.into()
2121        );
2122    }
2123
2124    #[cfg(any(feature = "std", feature = "libm"))]
2125    #[test]
2126    fn test_expf_negative_base() {
2127        // Test the expf power function for negative exponent and positive base
2128        let q = Q64::new(1.0, 0.0, 0.0, 0.0).expf(-1.0);
2129        assert!(q.w.is_nan());
2130        assert!(q.x.is_nan());
2131        assert!(q.y.is_nan());
2132        assert!(q.z.is_nan());
2133    }
2134
2135    #[cfg(any(feature = "std", feature = "libm"))]
2136    #[test]
2137    fn test_expf_nan_base() {
2138        // Test the expf power function for a NaN base
2139        let q = Q32::new(1.0, 0.0, 0.0, 0.0).expf(f32::NAN);
2140        assert!(q.w.is_nan());
2141        assert!(q.x.is_nan());
2142        assert!(q.y.is_nan());
2143        assert!(q.z.is_nan());
2144    }
2145
2146    #[cfg(any(feature = "std", feature = "libm"))]
2147    #[test]
2148    fn test_expf_finite_positive_base() {
2149        // Test the expf power function for a finite positive base
2150        let q = Q64::new(1.0, 2.0, 3.0, 4.0);
2151        let base = 2.0;
2152        let result = q.expf(base);
2153        let expected = (q * base.ln()).exp();
2154        assert!((result - expected).norm() <= expected.norm() * f64::EPSILON);
2155    }
2156
2157    #[cfg(any(feature = "std", feature = "libm"))]
2158    #[test]
2159    fn test_expf_nan_quaternion_component() {
2160        // Test the expf power function for a base with a NaN component
2161        let q = Q64::new(f64::NAN, 1.0, 1.0, 1.0).expf(3.0);
2162        assert!(q.w.is_nan());
2163        assert!(q.x.is_nan());
2164        assert!(q.y.is_nan());
2165        assert!(q.z.is_nan());
2166    }
2167
2168    #[cfg(any(feature = "std", feature = "libm"))]
2169    #[test]
2170    fn test_expf_infinity_quaternion_component() {
2171        // Test the expf power function for a base with an infinite component
2172        let q = Q32::new(1.0, f32::INFINITY, 1.0, 1.0).expf(2.0);
2173        assert!(q.w.is_nan());
2174        assert!(q.x.is_nan());
2175        assert!(q.y.is_nan());
2176        assert!(q.z.is_nan());
2177    }
2178
2179    #[cfg(any(feature = "std", feature = "libm"))]
2180    #[test]
2181    fn test_expf_infinite_real_component_with_t_greater_than_1() {
2182        // Test the expf power function for an infinite real base with a
2183        // finite real exponent greater than `1`
2184        let inf = f64::INFINITY;
2185        assert!(!Q64::new(inf, 0.0, 0.0, 0.0).expf(5.0).is_finite());
2186        assert!(!Q64::new(inf, 0.0, 0.0, 0.0).expf(5.0).has_nan());
2187        assert!(!Q64::new(inf, 1.0, 2.0, 3.0).expf(42.0).is_finite());
2188        assert!(!Q64::new(inf, 1.0, 2.0, 3.0).expf(42.0).has_nan());
2189    }
2190
2191    #[cfg(any(feature = "std", feature = "libm"))]
2192    #[test]
2193    fn test_expf_neg_infinite_real_component_with_t_between_0_and_1() {
2194        // Test the expf power function for a negative infinite real base with
2195        // an exponent between 0 and 1
2196        assert!(!Q32::new(f32::NEG_INFINITY, 0.0, 0.0, 0.0)
2197            .expf(0.5)
2198            .is_finite());
2199        assert!(!Q32::new(f32::NEG_INFINITY, 0.0, 0.0, 0.0)
2200            .expf(0.5)
2201            .has_nan());
2202        assert!(!Q32::new(f32::NEG_INFINITY, 1.0, 2.0, 3.0)
2203            .expf(0.75)
2204            .is_finite());
2205        assert!(!Q32::new(f32::NEG_INFINITY, 1.0, 2.0, 3.0)
2206            .expf(0.75)
2207            .has_nan());
2208    }
2209
2210    #[cfg(any(feature = "std", feature = "libm"))]
2211    #[test]
2212    fn test_powf_zero_q_positive_t() {
2213        // Test the power function for a zero quaternion and a positive
2214        // exponent
2215        let q = Quaternion::zero();
2216        let t = 1.0;
2217        let result = q.powf(t);
2218        assert_eq!(result, Quaternion::zero());
2219    }
2220
2221    #[cfg(any(feature = "std", feature = "libm"))]
2222    #[test]
2223    fn test_powf_infinite_q_negative_t() {
2224        // Test the power function for an infinite quaternion and a negative
2225        // exponent
2226        let q = Quaternion::new(f64::INFINITY, 0.0, 0.0, 0.0);
2227        let t = -1.0;
2228        let result = q.powf(t);
2229        assert_eq!(result, Quaternion::zero());
2230    }
2231
2232    #[cfg(any(feature = "std", feature = "libm"))]
2233    #[test]
2234    fn test_powf_infinite_q_positive_t_not_too_large() {
2235        // Test the power function for an infinite quaternion and a positive
2236        // exponent
2237        let q = Quaternion::new(f64::INFINITY, 0.0, 0.0, 0.0);
2238        let t = 1.0;
2239        let result = q.powf(t);
2240        assert_eq!(result, f64::INFINITY.into());
2241    }
2242
2243    #[cfg(any(feature = "std", feature = "libm"))]
2244    #[test]
2245    fn test_powf_infinite_q_large_positive_t() {
2246        // Test the power function for an infinite quaternion and a very large
2247        // positive exponent
2248        let q = Quaternion::new(0.0, f64::INFINITY, 0.0, 0.0);
2249        let t = f64::MAX;
2250        let result = q.powf(t);
2251        assert!(result.w.is_nan());
2252        assert!(result.x.is_nan());
2253        assert!(result.y.is_nan());
2254        assert!(result.z.is_nan());
2255    }
2256
2257    #[cfg(any(feature = "std", feature = "libm"))]
2258    #[test]
2259    fn test_powf_infinite_q_infinite_t() {
2260        // Test the power function for an infinite quaternion and an infinite
2261        // exponent
2262        let q = Quaternion::new(f64::INFINITY, 0.0, 0.0, 0.0);
2263        let t = f64::INFINITY;
2264        let result = q.powf(t);
2265        assert_eq!(result, f64::INFINITY.into());
2266    }
2267
2268    #[cfg(any(feature = "std", feature = "libm"))]
2269    #[test]
2270    fn test_powf_infinite_q_infinite_t_not_positive() {
2271        // Test the power function for an infinite non-positive quaternion
2272        // and an infinite positive exponent
2273        let q = Quaternion::new(f64::INFINITY, 1.0, 0.0, 0.0);
2274        let t = f64::INFINITY;
2275        let result = q.powf(t);
2276        assert!(result.w.is_nan());
2277        assert!(result.x.is_nan());
2278        assert!(result.y.is_nan());
2279        assert!(result.z.is_nan());
2280    }
2281
2282    #[cfg(any(feature = "std", feature = "libm"))]
2283    #[test]
2284    fn test_powf_zero_q_negative_t() {
2285        // Test that the power function for a zero quaternion and a negative
2286        // exponent returns an infinite quaternion
2287        let q = Quaternion::zero();
2288        let t = -1.0;
2289        let result = q.powf(t);
2290        assert_eq!(result, f64::INFINITY.into());
2291    }
2292
2293    #[cfg(any(feature = "std", feature = "libm"))]
2294    #[test]
2295    fn test_powf_nan_q_or_t() {
2296        // Test the power function for a quaternion or exponent with a NaN
2297        // component.
2298        let q = Quaternion::new(0.0, 0.0, f64::NAN, 0.0);
2299        let t = 1.0;
2300        let result = q.powf(t);
2301        assert!(result.w.is_nan());
2302        assert!(result.x.is_nan());
2303        assert!(result.y.is_nan());
2304        assert!(result.z.is_nan());
2305
2306        let q = Quaternion::one();
2307        let t = f64::NAN;
2308        let result = q.powf(t);
2309        assert!(result.w.is_nan());
2310        assert!(result.x.is_nan());
2311        assert!(result.y.is_nan());
2312        assert!(result.z.is_nan());
2313    }
2314
2315    #[cfg(any(feature = "std", feature = "libm"))]
2316    #[test]
2317    fn test_powf_infinite_q_zero_t() {
2318        // Test the power function for an infinite quaternion and a zero
2319        // exponent
2320        let q = Quaternion::new(f64::INFINITY, 0.0, 0.0, 0.0);
2321        let t = 0.0;
2322        let result = q.powf(t);
2323        assert!(result.w.is_nan());
2324        assert!(result.x.is_nan());
2325        assert!(result.y.is_nan());
2326        assert!(result.z.is_nan());
2327    }
2328
2329    #[cfg(any(feature = "std", feature = "libm"))]
2330    #[test]
2331    fn test_powf_zero_q_zero_t() {
2332        // Test the power function for a zero quaternion and a zero exponent
2333        let q = Q32::zero();
2334        let t = 0.0;
2335        let result = q.powf(t);
2336        assert!(result.w.is_nan());
2337        assert!(result.x.is_nan());
2338        assert!(result.y.is_nan());
2339        assert!(result.z.is_nan());
2340    }
2341
2342    #[cfg(any(feature = "std", feature = "libm"))]
2343    #[test]
2344    fn test_powf_non_zero_q_positive_infinite_t() {
2345        // Test the power function for a non-zero finite quaternion and a
2346        // positive infinite exponent
2347        let q = Quaternion::new(2.0, 0.0, 0.0, 0.0);
2348        let t = f64::INFINITY;
2349        let result = q.powf(t);
2350        assert_eq!(result, f64::INFINITY.into());
2351
2352        let q = Quaternion::new(0.5, 0.0, 0.0, 0.0);
2353        let result = q.powf(t);
2354        assert_eq!(result, Quaternion::zero());
2355
2356        let q = Quaternion::new(0.25, 0.25, 0.25, 0.25);
2357        let result = q.powf(t);
2358        assert_eq!(result, Quaternion::zero());
2359
2360        let q = Quaternion::new(1.0, 0.0, 0.0, 0.0);
2361        let result = q.powf(t);
2362        assert!(result.w.is_nan());
2363        assert!(result.x.is_nan());
2364        assert!(result.y.is_nan());
2365        assert!(result.z.is_nan());
2366    }
2367
2368    #[cfg(any(feature = "std", feature = "libm"))]
2369    #[test]
2370    fn test_powf_non_zero_q_negative_infinite_t() {
2371        // Test the power function for a non-zero finite quaternion and a
2372        // negative infinite exponent
2373        let q = Quaternion::new(2.0, 0.0, 0.0, 0.0);
2374        let t = f64::NEG_INFINITY;
2375        let result = q.powf(t);
2376        assert_eq!(result, Quaternion::zero());
2377
2378        let q = Quaternion::new(1.0, 0.0, 0.0, 1.0);
2379        let result = q.powf(t);
2380        assert_eq!(result, Quaternion::zero());
2381
2382        let q = Quaternion::new(0.5, 0.0, 0.0, 0.0);
2383        let result = q.powf(t);
2384        assert_eq!(result, f64::INFINITY.into());
2385
2386        let q = Quaternion::new(0.25, 0.25, 0.25, 0.25);
2387        let result = q.powf(t);
2388        assert!(result.is_all_nan());
2389
2390        let q = Quaternion::new(1.0, 0.0, 0.0, 0.0);
2391        let result = q.powf(t);
2392        assert!(result.w.is_nan());
2393        assert!(result.x.is_nan());
2394        assert!(result.y.is_nan());
2395        assert!(result.z.is_nan());
2396    }
2397
2398    #[cfg(any(feature = "std", feature = "libm"))]
2399    #[test]
2400    fn test_is_finite_for_finite_values() {
2401        // Test the is_finite method for finite values
2402        let q = Q64::new(1.0, 2.0, 3.0, 4.0);
2403        assert!(q.is_finite());
2404    }
2405
2406    #[cfg(any(feature = "std", feature = "libm"))]
2407    #[test]
2408    fn test_is_finite_for_zero() {
2409        // Test the is_finite method for the zero quaternion
2410        let q = Q32::zero();
2411        assert!(q.is_finite());
2412    }
2413
2414    #[cfg(any(feature = "std", feature = "libm"))]
2415    #[test]
2416    fn test_is_finite_for_infinite_values() {
2417        // Test the is_finite method for infinite values
2418        let inf = f32::INFINITY;
2419        assert!(!Q32::new(inf, 1.0, 1.0, 1.0).is_finite());
2420        assert!(!Q32::new(1.0, inf, 1.0, 1.0).is_finite());
2421        assert!(!Q32::new(1.0, 1.0, inf, 1.0).is_finite());
2422        assert!(!Q32::new(1.0, 1.0, 1.0, inf).is_finite());
2423        assert!(!Q32::new(-inf, 1.0, 1.0, 1.0).is_finite());
2424        assert!(!Q32::new(1.0, -inf, 1.0, 1.0).is_finite());
2425        assert!(!Q32::new(1.0, 1.0, -inf, 1.0).is_finite());
2426        assert!(!Q32::new(1.0, 1.0, 1.0, -inf).is_finite());
2427        assert!(!Q32::new(inf, -inf, inf, -inf).is_finite());
2428    }
2429
2430    #[cfg(any(feature = "std", feature = "libm"))]
2431    #[test]
2432    fn test_is_finite_for_nan_values() {
2433        // Test the is_finite method for nan values
2434        let nan = f64::NAN;
2435        assert!(!Q64::new(nan, 1.0, 1.0, 1.0).is_finite());
2436        assert!(!Q64::new(1.0, nan, 1.0, 1.0).is_finite());
2437        assert!(!Q64::new(1.0, 1.0, nan, 1.0).is_finite());
2438        assert!(!Q64::new(1.0, 1.0, 1.0, nan).is_finite());
2439        assert!(!Q64::new(-nan, 1.0, 1.0, 1.0).is_finite());
2440        assert!(!Q64::new(1.0, -nan, 1.0, 1.0).is_finite());
2441        assert!(!Q64::new(1.0, 1.0, -nan, 1.0).is_finite());
2442        assert!(!Q64::new(1.0, 1.0, 1.0, -nan).is_finite());
2443        assert!(!Q64::new(nan, -nan, nan, -nan).is_finite());
2444    }
2445
2446    #[cfg(any(feature = "std", feature = "libm"))]
2447    #[test]
2448    fn test_has_nan() {
2449        // Test the has_nan method for nan values
2450        let nan = f64::NAN;
2451        let inf = f64::INFINITY;
2452        assert!(!Q64::new(0.0, 0.0, 0.0, 0.0).has_nan());
2453        assert!(!Q64::new(1.0, 1.0, 1.0, 1.0).has_nan());
2454        assert!(!Q64::new(inf, inf, inf, inf).has_nan());
2455        assert!(Q64::new(nan, 1.0, 1.0, 1.0).has_nan());
2456        assert!(Q64::new(1.0, nan, 1.0, 1.0).has_nan());
2457        assert!(Q64::new(1.0, 1.0, nan, 1.0).has_nan());
2458        assert!(Q64::new(1.0, 1.0, 1.0, nan).has_nan());
2459        assert!(Q64::new(-nan, 1.0, 1.0, 1.0).has_nan());
2460        assert!(Q64::new(1.0, -nan, 1.0, 1.0).has_nan());
2461        assert!(Q64::new(1.0, 1.0, -nan, 1.0).has_nan());
2462        assert!(Q64::new(1.0, 1.0, 1.0, -nan).has_nan());
2463        assert!(Q64::new(nan, -nan, nan, -nan).has_nan());
2464    }
2465
2466    #[cfg(any(feature = "std", feature = "libm"))]
2467    #[test]
2468    fn test_is_all_nan() {
2469        // Test the is_all_nan method for nan values
2470        let nan = f64::NAN;
2471        let inf = f64::INFINITY;
2472        assert!(!Q64::new(0.0, 0.0, 0.0, 0.0).is_all_nan());
2473        assert!(!Q64::new(1.0, 1.0, 1.0, 1.0).is_all_nan());
2474        assert!(!Q64::new(inf, inf, inf, inf).is_all_nan());
2475        assert!(!Q64::new(nan, 1.0, 1.0, 1.0).is_all_nan());
2476        assert!(!Q64::new(1.0, nan, 1.0, 1.0).is_all_nan());
2477        assert!(!Q64::new(1.0, 1.0, nan, 1.0).is_all_nan());
2478        assert!(!Q64::new(1.0, 1.0, 1.0, nan).is_all_nan());
2479        assert!(!Q64::new(-nan, 1.0, 1.0, 1.0).is_all_nan());
2480        assert!(!Q64::new(1.0, -nan, 1.0, 1.0).is_all_nan());
2481        assert!(!Q64::new(1.0, 1.0, -nan, 1.0).is_all_nan());
2482        assert!(!Q64::new(1.0, 1.0, 1.0, -nan).is_all_nan());
2483        assert!(Q64::new(nan, nan, nan, nan).is_all_nan());
2484        assert!(Q64::new(-nan, -nan, -nan, -nan).is_all_nan());
2485        assert!(Q64::new(nan, -nan, nan, -nan).is_all_nan());
2486    }
2487
2488    #[cfg(any(feature = "std", feature = "libm"))]
2489    #[test]
2490    fn test_ln_normal_case() {
2491        // Test a normal quaternion
2492        let q = Quaternion::new(1.0, 2.0, 3.0, 4.0);
2493        let ln_q = q.ln();
2494        assert!((q.w - 30.0f64.ln() / 2.0) <= 4.0 * f64::EPSILON);
2495        assert!((ln_q.z / ln_q.x - q.z / q.x) <= 2.0 * f64::EPSILON);
2496        assert!((ln_q.y / ln_q.x - q.y / q.x) <= 2.0 * f64::EPSILON);
2497        assert!(
2498            (ln_q.x.hypot(ln_q.y.hypot(ln_q.z)) - 29.0f64.sqrt().atan())
2499                <= 4.0 * f64::EPSILON
2500        );
2501    }
2502
2503    #[cfg(any(feature = "std", feature = "libm"))]
2504    #[test]
2505    fn test_ln_positive_real_axis() {
2506        // Test close to the positive real axis
2507        let q = Quaternion::new(1.0, 1e-10, 1e-10, 1e-10);
2508        let ln_q = q.ln();
2509        let expected = Quaternion::new(0.0, 1e-10, 1e-10, 1e-10); // ln(1) = 0 and imaginary parts small
2510        assert!((ln_q - expected).norm() <= 1e-11);
2511    }
2512
2513    #[cfg(any(feature = "std", feature = "libm"))]
2514    #[test]
2515    fn test_ln_negative_real_axis() {
2516        // Test close to the negative real axis
2517        let q = Q32::new(-1.0, 0.0, 0.0, 0.0);
2518        let ln_q = q.ln();
2519        let expected = Q32::new(0.0, core::f32::consts::PI, 0.0, 0.0); // ln(-1) = pi*i
2520        assert!(
2521            (ln_q - expected).norm() <= core::f32::consts::PI * f32::EPSILON
2522        );
2523    }
2524
2525    #[cfg(any(feature = "std", feature = "libm"))]
2526    #[test]
2527    fn test_ln_zero() {
2528        // Test the zero quaternion
2529        let q = Q32::new(0.0, 0.0, 0.0, 0.0);
2530        let ln_q = q.ln();
2531        let expected = f32::NEG_INFINITY.into();
2532        assert_eq!(ln_q, expected);
2533    }
2534
2535    #[cfg(any(feature = "std", feature = "libm"))]
2536    #[test]
2537    fn test_ln_negative_zero() {
2538        // Test the negative zero quaternion
2539        let q = Q64::new(-0.0, 0.0, 0.0, 0.0);
2540        let ln_q = q.ln();
2541        let expected = Q64::new(f64::NEG_INFINITY, 0.0, 0.0, 0.0);
2542        assert_eq!(ln_q.w, expected.w);
2543        assert_eq!(ln_q.x, expected.x);
2544        assert_eq!(ln_q.y, expected.y);
2545        assert_eq!(ln_q.z, expected.z);
2546    }
2547
2548    #[cfg(any(feature = "std", feature = "libm"))]
2549    #[test]
2550    fn test_ln_nan() {
2551        // Test a quaternion with NaN
2552        let q = Quaternion::new(f32::NAN, 1.0, 1.0, 1.0);
2553        let ln_q = q.ln();
2554        assert!(ln_q.w.is_nan());
2555        assert!(ln_q.x.is_nan());
2556        assert!(ln_q.y.is_nan());
2557        assert!(ln_q.z.is_nan());
2558    }
2559
2560    #[cfg(any(feature = "std", feature = "libm"))]
2561    #[test]
2562    fn test_ln_infinite() {
2563        // Test a quaternion with infinite components
2564        let q = Q32::new(f32::INFINITY, 1.0, 1.0, 1.0);
2565        let ln_q = q.ln();
2566        let expected = Quaternion::new(f32::INFINITY, 0.0, 0.0, 0.0); // Real part infinity, imaginary parts 0
2567        assert_eq!(ln_q, expected);
2568    }
2569
2570    #[cfg(any(feature = "std", feature = "libm"))]
2571    #[test]
2572    fn test_ln_finite_and_infinite() {
2573        // Test a quaternion with finite and infinite components
2574
2575        use num_traits::Signed;
2576        let q = Quaternion::new(1.0, f32::INFINITY, -f32::INFINITY, -1.0);
2577        let ln_q = q.ln();
2578        let expected = Quaternion::new(
2579            f32::INFINITY,
2580            core::f32::consts::PI / 8.0f32.sqrt(),
2581            -core::f32::consts::PI / 8.0f32.sqrt(),
2582            0.0,
2583        );
2584        assert_eq!(ln_q.w, expected.w);
2585        assert!((ln_q.x - expected.x).abs() <= 4.0f32 * f32::EPSILON);
2586        assert!((ln_q.y - expected.y).abs() <= 4.0f32 * f32::EPSILON);
2587        assert_eq!(ln_q.z, 0.0);
2588        assert!(ln_q.z.is_negative());
2589    }
2590
2591    #[cfg(any(feature = "std", feature = "libm"))]
2592    #[test]
2593    fn test_ln_negative_real_part_tiny_imaginary_part() {
2594        // Test a quaternion with a tiny imaginary part
2595        use core::f32;
2596        let q = Q32::new(-2.0, 346.0 * f32::EPSILON, 0.0, 0.0);
2597        let ln_q = q.ln();
2598        let expected =
2599            Q32::new(2.0f32.ln(), f32::consts::PI + q.x / q.w, 0.0, 0.0);
2600        assert!((ln_q - expected).norm() <= 8.0 * f32::EPSILON);
2601
2602        let q = Q32::new(-3.0, f32::MIN_POSITIVE / 64.0, 0.0, 0.0);
2603        let ln_q = q.ln();
2604        let expected = Q32::new(3.0f32.ln(), f32::consts::PI, 0.0, 0.0);
2605        assert_eq!(ln_q, expected);
2606    }
2607
2608    #[cfg(any(feature = "std", feature = "libm"))]
2609    #[test]
2610    fn test_ln_tiny_real_part_and_tiny_imaginary_part() {
2611        // Test a quaternion with a tiny real and imaginary part
2612
2613        use core::f32;
2614        let w = f32::MIN_POSITIVE.sqrt();
2615        let q = Q32::new(w, 0.0, w / 2.0, 0.0);
2616        let ln_q = q.ln();
2617        let expected = Q32::new(
2618            (1.25 * f32::MIN_POSITIVE).ln() / 2.0,
2619            0.0,
2620            0.5f32.atan(),
2621            0.0,
2622        );
2623        assert_eq!(ln_q, expected);
2624
2625        let w = f32::MIN_POSITIVE;
2626        let q = Q32::new(w, w, w, w);
2627        let ln_q = q.ln();
2628        let expected = Q32::new(
2629            (2.0 * f32::MIN_POSITIVE).ln(),
2630            f32::consts::PI / 27.0f32.sqrt(),
2631            f32::consts::PI / 27.0f32.sqrt(),
2632            f32::consts::PI / 27.0f32.sqrt(),
2633        );
2634        assert!(
2635            (ln_q - expected).norm() <= expected.norm() * 2.0 * f32::EPSILON
2636        );
2637    }
2638
2639    #[cfg(any(feature = "std", feature = "libm"))]
2640    #[test]
2641    fn test_ln_very_large_inputs() {
2642        // Test the logarithm of very large inputs
2643
2644        use core::f64;
2645        let q = Q64::new(f64::MAX, 0.0, 0.0, f64::MAX);
2646        let ln_q = q.ln();
2647        let expected = Q64::new(
2648            f64::MAX.ln() + 2.0f64.ln() / 2.0,
2649            0.0,
2650            0.0,
2651            f64::consts::PI / 4.0,
2652        );
2653        assert!(
2654            (ln_q - expected).norm() <= expected.norm() * 2.0 * f64::EPSILON
2655        );
2656    }
2657
2658    #[cfg(any(feature = "std", feature = "libm"))]
2659    #[test]
2660    fn test_sqrt_normal() {
2661        // Test the square root of a normal quaternion
2662        let q = Q64::new(1.0, 2.0, 3.0, 4.0);
2663        assert!(((q * q).sqrt() - q).norm() <= q.norm() * f64::EPSILON);
2664    }
2665
2666    #[cfg(any(feature = "std", feature = "libm"))]
2667    #[test]
2668    fn test_sqrt_zero() {
2669        // Test the square root of the zero quaternion
2670        assert_eq!(Q32::ZERO.sqrt(), Q32::ZERO);
2671        let zero = Q32::new(-0.0, 0.0, -0.0, -0.0);
2672        assert!(zero.sqrt().w.is_sign_positive());
2673        assert!(zero.sqrt().x.is_sign_positive());
2674        assert!(zero.sqrt().y.is_sign_negative());
2675        assert!(zero.sqrt().z.is_sign_negative());
2676    }
2677
2678    #[cfg(any(feature = "std", feature = "libm"))]
2679    #[test]
2680    fn test_sqrt_negative_real() {
2681        // Test the square root of a negative real quaternion
2682        let q = Q64::new(-4.0, -0.0, 0.0, 0.0);
2683        let sqrt_q = q.sqrt();
2684        let expected = Q64::new(0.0, -2.0, 0.0, 0.0);
2685        assert_eq!(sqrt_q, expected);
2686    }
2687
2688    #[cfg(any(feature = "std", feature = "libm"))]
2689    #[test]
2690    fn test_sqrt_nan() {
2691        // Test the square root of a quaternion with NaN
2692        let q = Q32::new(f32::NAN, 0.0, 0.0, 0.0);
2693        let sqrt_q = q.sqrt();
2694        assert!(sqrt_q.w.is_nan());
2695        assert!(sqrt_q.x.is_nan());
2696        assert!(sqrt_q.y.is_nan());
2697        assert!(sqrt_q.z.is_nan());
2698    }
2699
2700    #[cfg(any(feature = "std", feature = "libm"))]
2701    #[test]
2702    fn test_sqrt_infinity() {
2703        // Test the square root of a quaternion with infinite components
2704        let q = Q64::new(f64::INFINITY, -0.0, -0.0, 0.0);
2705        let sqrt_q = q.sqrt();
2706        assert!(sqrt_q.w.is_infinite());
2707        assert!(sqrt_q.x.is_zero());
2708        assert!(sqrt_q.y.is_zero());
2709        assert!(sqrt_q.z.is_zero());
2710        assert!(sqrt_q.w.is_sign_positive());
2711        assert!(sqrt_q.x.is_sign_negative());
2712        assert!(sqrt_q.y.is_sign_negative());
2713        assert!(sqrt_q.z.is_sign_positive());
2714
2715        let q = Q32::new(0.0, 0.0, -f32::INFINITY, -0.0);
2716        let sqrt_q = q.sqrt();
2717        assert!(sqrt_q.w.is_infinite());
2718        assert!(sqrt_q.x.is_zero());
2719        assert!(sqrt_q.y.is_infinite());
2720        assert!(sqrt_q.z.is_zero());
2721        assert!(sqrt_q.w.is_sign_positive());
2722        assert!(sqrt_q.x.is_sign_positive());
2723        assert!(sqrt_q.y.is_sign_negative());
2724        assert!(sqrt_q.z.is_sign_negative());
2725    }
2726
2727    #[cfg(any(feature = "std", feature = "libm"))]
2728    #[test]
2729    fn test_sqrt_negative_infinity_real() {
2730        // Test the square root of a quaternion with a negative infinite real
2731        // part
2732        let q = Quaternion::new(-f64::INFINITY, 0.0, -1.0, 0.0);
2733        let sqrt_q = q.sqrt();
2734        assert!(sqrt_q.w.is_zero());
2735        assert!(sqrt_q.x.is_infinite());
2736        assert!(sqrt_q.y.is_zero());
2737        assert!(sqrt_q.z.is_zero());
2738        assert!(sqrt_q.w.is_sign_positive());
2739        assert!(sqrt_q.x.is_sign_positive());
2740        assert!(sqrt_q.y.is_sign_negative());
2741        assert!(sqrt_q.z.is_sign_positive());
2742    }
2743
2744    #[cfg(any(feature = "std", feature = "libm"))]
2745    #[test]
2746    fn test_sqrt_commutativity_with_conjugate() {
2747        // Test the commutativity of the square root with the conjugate
2748        let q = Q32::new(1.0, 2.0, 3.0, 4.0);
2749        assert_eq!(q.conj().sqrt(), q.sqrt().conj());
2750    }
2751
2752    #[cfg(any(feature = "std", feature = "libm"))]
2753    #[test]
2754    fn test_sqrt_subnormal_values() {
2755        // Test the square root of subnormal values
2756        let subnormal = f64::MIN_POSITIVE / 2.0;
2757        let q = Quaternion::new(subnormal, subnormal, subnormal, subnormal);
2758        let sqrt_q = q.sqrt();
2759        let norm_sqr = sqrt_q.norm_sqr();
2760        assert!(
2761            (norm_sqr - f64::MIN_POSITIVE).abs()
2762                <= 4.0 * subnormal * f64::EPSILON
2763        );
2764    }
2765
2766    #[cfg(any(feature = "std", feature = "libm"))]
2767    #[test]
2768    fn test_sqrt_mixed_infinities() {
2769        // Test the square root of a quaternion with all infinite components
2770        // with different signs
2771        let q = Q32::new(
2772            -f32::INFINITY,
2773            -f32::INFINITY,
2774            f32::INFINITY,
2775            -f32::INFINITY,
2776        );
2777        let sqrt_q = q.sqrt();
2778        assert_eq!(sqrt_q.w, f32::INFINITY);
2779        assert_eq!(sqrt_q.x, -f32::INFINITY);
2780        assert_eq!(sqrt_q.y, f32::INFINITY);
2781        assert_eq!(sqrt_q.z, -f32::INFINITY);
2782    }
2783
2784    #[cfg(any(feature = "std", feature = "libm"))]
2785    #[test]
2786    fn test_sqrt_positive_real() {
2787        // Test the square root of a positive real quaternion
2788        let q = Q64::new(4.0, 0.0, 0.0, 0.0);
2789        let sqrt_q = q.sqrt();
2790        let expected = Q64::new(2.0, 0.0, 0.0, 0.0);
2791        assert_eq!(sqrt_q, expected);
2792    }
2793
2794    #[cfg(any(feature = "std", feature = "libm"))]
2795    #[test]
2796    fn test_sqrt_purely_imaginary() {
2797        // Test the square root of a purely imaginary quaternion
2798        let q = Q32::new(0.0, 3.0, 4.0, 0.0);
2799        let sqrt_q = q.sqrt();
2800        assert!(sqrt_q.w > 0.0);
2801        assert!((sqrt_q * sqrt_q - q).norm() <= 2.0 * q.norm() * f32::EPSILON);
2802    }
2803
2804    #[cfg(any(feature = "std", feature = "libm"))]
2805    #[test]
2806    fn test_sqrt_negative_imaginary() {
2807        // Test the square root of a pure quaternion with negative imaginary
2808        // parts
2809        let q = Q64::new(0.0, -3.0, -4.0, 0.0);
2810        let sqrt_q = q.sqrt();
2811        assert!(sqrt_q.w > 0.0);
2812        assert!((sqrt_q * sqrt_q - q).norm() <= 16.0 * q.norm() * f64::EPSILON);
2813    }
2814
2815    #[cfg(any(feature = "std", feature = "libm"))]
2816    #[test]
2817    fn test_sqrt_negative_real_part_subnormal_imaginary_part() {
2818        // Test the square root of a quaternion with a negative real part and
2819        // subnormal imaginary parts
2820        let q = Q32::new(-1.0, f32::MIN_POSITIVE / 64.0, 0.0, 0.0);
2821        let sqrt_q = q.sqrt();
2822        let expected = Q32::new(q.x / 2.0, 1.0, 0.0, 0.0);
2823        assert_eq!(sqrt_q, expected);
2824    }
2825
2826    #[cfg(any(feature = "std", feature = "libm"))]
2827    #[test]
2828    fn test_sqrt_for_overflowing_norm_sqr_of_input() {
2829        // Test the square root of a quaternion with an overflowing norm_sqr
2830        let n = f64::MAX / 2.0;
2831        let q = Q64::new(-n, n, n, n);
2832        let sqrt_q = q.sqrt();
2833        let sqrt_n = f64::MAX.sqrt() / 2.0;
2834        let expected = Q64::new(sqrt_n, sqrt_n, sqrt_n, sqrt_n);
2835        assert!((sqrt_q - expected).norm() <= expected.norm() * f64::EPSILON);
2836    }
2837
2838    #[cfg(feature = "serde")]
2839    #[test]
2840    fn test_serde_quaternion() {
2841        // Create a sample quaternion
2842        let q = Q32::new(1.0, 2.0, 3.0, 4.0);
2843
2844        // Serialize the quaternion to a JSON string
2845        let serialized =
2846            serde_json::to_string(&q).expect("Failed to serialize quaternion");
2847
2848        // Deserialize the JSON string back into a quaternion
2849        let deserialized: Quaternion<f32> = serde_json::from_str(&serialized)
2850            .expect("Failed to deserialize quaternion");
2851
2852        // Assert that the deserialized quaternion is equal to the original
2853        assert_eq!(q, deserialized);
2854    }
2855}