hcomplex 0.2.0

Lightweight complex and hypercomplex algebra library
Documentation
use rand::{prelude::*};
use rand_xorshift::XorShiftRng;
use ::approx::*;
use crate::{prelude::*, *, random::*};


#[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]
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(&StandardNormal);
        let b: Qf = rng.sample(&StandardNormal);
        assert_abs_diff_eq!(a.norm_sqr() + b.norm_sqr() + 2.0*a.dot(b), (a + b).norm_sqr());
    }
}

#[test]
fn conjugation() {
    for _ in 0..SAMPLE_ATTEMPTS {
        let mut rng = XorShiftRng::seed_from_u64(0xFEED2);
        let a: Qf = rng.sample(&StandardNormal);
        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(&StandardNormal);
            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,
            );
        }
    }
}