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#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
79pub struct Quaternion<T> {
80 pub w: T,
82 pub x: T,
84 pub y: T,
86 pub z: T,
88}
89
90pub type Q32 = Quaternion<f32>;
92pub type Q64 = Quaternion<f64>;
94
95impl<T> Quaternion<T> {
96 #[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 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 pub const ONE: Self = Self::new(T::ONE, T::ZERO, T::ZERO, T::ZERO);
178
179 pub const I: Self = Self::new(T::ZERO, T::ONE, T::ZERO, T::ZERO);
191
192 pub const J: Self = Self::new(T::ZERO, T::ZERO, T::ONE, T::ZERO);
204
205 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 #[inline]
268 pub fn one() -> Self {
269 Self::new(T::one(), T::zero(), T::zero(), T::zero())
270 }
271
272 #[inline]
284 pub fn i() -> Self {
285 Self::new(T::zero(), T::one(), T::zero(), T::zero())
286 }
287
288 #[inline]
300 pub fn j() -> Self {
301 Self::new(T::zero(), T::zero(), T::one(), T::zero())
302 }
303
304 #[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 #[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 #[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 #[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 #[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 #[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 T::zero()
483 } else {
484 (self / s).fast_norm() * s
487 }
488 } else {
489 (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 #[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 #[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 #[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 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 #[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 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 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 let sqr_angle =
763 self.x * self.x + self.y * self.y + self.z * self.z;
764
765 if sqr_angle <= T::epsilon() {
766 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 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 let map = |a: T| {
795 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 Self::new(inf, map(self.x), map(self.y), map(self.z))
808 } else if sqr_angle.is_finite() {
809 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 debug_assert!(
826 sqr_angle.is_infinite() || sqr_angle.is_nan()
827 );
828 Self::nan()
829 }
830 }
831 None => {
832 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 #[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 #[inline]
969 pub fn powf(self, exponent: T) -> Self {
970 if exponent.is_finite() {
971 (self.ln() * exponent).exp()
973 } else if exponent > T::zero() {
974 if self.x.is_zero() && self.y.is_zero() && self.z.is_zero() {
976 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 Self::zero()
985 } else {
986 Self::nan()
988 }
989 } else if exponent < T::zero() {
990 if self.x.is_zero() && self.y.is_zero() && self.z.is_zero() {
992 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 Self::zero()
1001 } else {
1002 Self::nan()
1004 }
1005 } else {
1006 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 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 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 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 pub fn ln(self) -> Self {
1100 let sqr_norm_im = self.x * self.x + self.y * self.y + self.z * self.z;
1102 let norm_sqr = self.w * self.w + sqr_norm_im;
1104
1105 match norm_sqr.classify() {
1106 FpCategory::Normal => {
1107 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 if self.w.is_sign_positive() {
1114 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 Self::new(w, T::PI().copysign(self.x), self.y, self.z)
1126 } else if sqr_norm_im.is_normal() {
1127 let norm_im = sqr_norm_im.sqrt();
1130
1131 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 let f = T::one()
1152 / (T::min_positive_value().sqrt() * T::epsilon());
1153 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 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 let norm_im = if sqr_norm_im.is_normal() {
1182 sqr_norm_im.sqrt()
1184 } else {
1185 let f = T::one() / T::min_positive_value().sqrt();
1189 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 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 if self.is_finite() {
1214 let factor = T::one() / T::max_value().sqrt();
1217 (self * factor).ln() - factor.ln()
1218 } else {
1219 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 q.ln() + T::infinity()
1235 }
1236 }
1237 _ => {
1238 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 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(); let norm_sqr = self.norm_sqr();
1295 match norm_sqr.classify() {
1296 FpCategory::Normal => {
1297 let norm = norm_sqr.sqrt();
1299
1300 if self.w.is_sign_positive() {
1301 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 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 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 Self::new(
1349 zero,
1350 (-self.w).sqrt().copysign(self.x),
1351 self.y,
1352 self.z,
1353 )
1354 } else {
1355 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 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 (self / s).sqrt() * s.sqrt()
1402 }
1403 }
1404 FpCategory::Nan => Self::nan(),
1405 _ => {
1406 (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 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 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 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 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 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 assert_eq!(Q32::ONE, Q32::new(1.0, 0.0, 0.0, 0.0))
1502 }
1503
1504 #[test]
1505 fn test_i_constant() {
1506 assert_eq!(Q64::I, Q64::new(0.0, 1.0, 0.0, 0.0))
1508 }
1509
1510 #[test]
1511 fn test_j_constant() {
1512 assert_eq!(Q32::J, Q32::new(0.0, 0.0, 1.0, 0.0))
1514 }
1515
1516 #[test]
1517 fn test_k_constant() {
1518 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 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 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 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 assert_eq!(Q32::one(), Q32::new(1.0, 0.0, 0.0, 0.0));
1547 }
1548
1549 #[test]
1550 fn test_i_func() {
1551 assert_eq!(Q64::i(), Q64::new(0.0, 1.0, 0.0, 0.0));
1553 }
1554
1555 #[test]
1556 fn test_j_func() {
1557 assert_eq!(Q64::j(), Q64::new(0.0, 0.0, 1.0, 0.0));
1559 }
1560
1561 #[test]
1562 fn test_k_func() {
1563 assert_eq!(Q64::k(), Q64::new(0.0, 0.0, 0.0, 1.0));
1565 }
1566
1567 #[test]
1568 fn test_norm_sqr() {
1569 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 assert_eq!(Q64::ZERO.normalize(), None);
1806 }
1807
1808 #[cfg(any(feature = "std", feature = "libm"))]
1809 #[test]
1810 fn test_normalize_infinity() {
1811 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let angle = f32::PI() * 0.75; 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 let angle = f64::PI() * 1.25; 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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); 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 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); 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 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 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 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 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); assert_eq!(ln_q, expected);
2568 }
2569
2570 #[cfg(any(feature = "std", feature = "libm"))]
2571 #[test]
2572 fn test_ln_finite_and_infinite() {
2573 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let q = Q32::new(1.0, 2.0, 3.0, 4.0);
2843
2844 let serialized =
2846 serde_json::to_string(&q).expect("Failed to serialize quaternion");
2847
2848 let deserialized: Quaternion<f32> = serde_json::from_str(&serialized)
2850 .expect("Failed to deserialize quaternion");
2851
2852 assert_eq!(q, deserialized);
2854 }
2855}