use crate::{distr::*, traits::Dot, Quaternion};
use approx::*;
use num_traits::One;
use rand_::prelude::*;
use rand_xorshift::XorShiftRng;
#[cfg(feature = "std")]
use std::boxed::Box;
const SAMPLE_ATTEMPTS: usize = 256;
type Qf = Quaternion<f64>;
#[test]
fn imaginary_units() {
type Qi = Quaternion<i32>;
assert_eq!(Qi::i() * Qi::i(), -Qi::one());
assert_eq!(Qi::j() * Qi::j(), -Qi::one());
assert_eq!(Qi::k() * Qi::k(), -Qi::one());
assert_eq!(Qi::i() * Qi::j() * Qi::k(), -Qi::one());
assert_eq!(Qi::i() * Qi::j(), Qi::k());
assert_eq!(Qi::j() * Qi::k(), Qi::i());
assert_eq!(Qi::k() * Qi::i(), Qi::j());
assert_eq!(Qi::j() * Qi::i(), -Qi::k());
assert_eq!(Qi::k() * Qi::j(), -Qi::i());
assert_eq!(Qi::i() * Qi::k(), -Qi::j());
}
#[test]
#[allow(clippy::eq_op)]
fn inversion() {
let mut rng = XorShiftRng::seed_from_u64(0xFEED0);
for _ in 0..SAMPLE_ATTEMPTS {
let a: Qf = rng.sample(&NonZero);
assert_abs_diff_eq!(a / a, Quaternion::one(), epsilon = 1e-14);
}
}
#[test]
fn law_of_cosines() {
for _ in 0..SAMPLE_ATTEMPTS {
let mut rng = XorShiftRng::seed_from_u64(0xFEED1);
let a: Qf = rng.sample(&Normal);
let b: Qf = rng.sample(&Normal);
assert_abs_diff_eq!(
a.norm_sqr() + b.norm_sqr() + 2.0 * a.dot(b),
(a + b).norm_sqr(),
epsilon = 1e-14,
);
}
}
#[test]
fn conjugation() {
for _ in 0..SAMPLE_ATTEMPTS {
let mut rng = XorShiftRng::seed_from_u64(0xFEED2);
let a: Qf = rng.sample(&Normal);
assert_abs_diff_eq!(a * a.conj(), Quaternion::<f64>::one() * a.norm_sqr());
assert_abs_diff_eq!(a.conj() * a, Quaternion::<f64>::one() * a.norm_sqr());
}
}
#[cfg(feature = "std")]
#[test]
fn derivation() {
let cases = [
(
Box::new(|p: Qf| p) as Box<dyn Fn(_) -> _>,
Box::new(|_: Qf, v: Qf| v) as Box<dyn Fn(_, _) -> _>,
),
(
Box::new(|p: Qf| p * p),
Box::new(|p: Qf, v: Qf| p * v + v * p),
),
(
Box::new(|p: Qf| p.inv()),
Box::new(|p: Qf, v: Qf| {
let p2 = p.norm_sqr();
(v.conj() - (2.0 * p.dot(v) / p2) * p.conj()) / p2
}),
),
];
const EPS: f64 = 1e-8;
let mut rng = XorShiftRng::seed_from_u64(0xFEED3);
for (f, dfdv) in cases.iter() {
for _ in 0..SAMPLE_ATTEMPTS {
let p = rng.sample(&Normal);
let v = rng.sample(&Unit);
let deriv = dfdv(p, v);
let dabs = deriv.norm();
assert_abs_diff_eq!(deriv, (f(p + EPS * v) - f(p)) / EPS, epsilon = 1e-6 * dabs,);
}
}
}
#[test]
fn into_matrix() {
let mut rng = XorShiftRng::seed_from_u64(0xFEED0);
for _ in 0..SAMPLE_ATTEMPTS {
let a: Qf = rng.sample(&NonZero);
let b: Qf = rng.sample(&NonZero);
assert_abs_diff_eq!(
(a * b).into_matrix(),
a.into_matrix().dot(b.into_matrix()),
epsilon = 1e-14,
);
}
}