rinia 0.0.16

Stable numeric foundations for graphics, animation, and simulation.
Documentation
#![cfg(test)]

use alloc::vec::Vec;

use crate::{
    algebra::{ApproxEqAbs, ApproxEqRel},
    approx_eq_abs, approx_eq_rel, approx_ne_abs, approx_ne_rel, assert_approx_eq_abs,
    assert_approx_eq_rel, assert_approx_ne_abs, assert_approx_ne_rel,
};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Thin<T>(T);

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct ThickSingle<T> {
    pub value: T,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct ThickMulti<T> {
    pub value1: T,
    pub value2: T,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct ThickConst<T, const N: usize> {
    pub value1: T,
    pub value2: T,
}

#[derive(Clone, Copy, Debug, PartialEq)]
struct SignedEq<T> {
    value: T,
}

crate::impl_approx_eq_wrapper!([
    T
], impl: Thin<T>, item: T);

crate::impl_approx_eq_wrapper!([
    T
], impl: ThickSingle<T>, item: T, field: value);

crate::impl_approx_eq_wrapper!([
    T
], impl: ThickMulti<T>, item: T, fields: [value1, value2]);

crate::impl_approx_eq_wrapper!([
    T, const N: usize
], impl: ThickConst<T, N>, item: T, fields: [value1, value2]);

crate::impl_approx_eq_wrapper!(
    [T],
    impl: SignedEq<T>,
    item: T,
    extra_bounds: [core::ops::Neg<Output = T>],
    compare_abs: |lhs, rhs, tol| {
        lhs.value.approx_eq_abs_tol(&rhs.value, tol) || lhs.value.approx_eq_abs_tol(&-rhs.value, tol)
    },
    compare_rel: |lhs, rhs, tol| {
        lhs.value.approx_eq_rel_tol(&rhs.value, tol) || lhs.value.approx_eq_rel_tol(&-rhs.value, tol)
    },
);

#[test]
fn approx_eq_wrapper_thin_surface() {
    let lhs = Thin(1.0_f32);
    let rhs_close = Thin(1.0005_f32);
    let rhs_far = Thin(1.01_f32);

    assert!(lhs.approx_eq_abs_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_abs_tol(&rhs_far, 0.001_f32));

    assert!(lhs.approx_eq_rel_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_rel_tol(&rhs_far, 0.001_f32));

    assert!(lhs.approx_eq_abs(&lhs));
    assert!(lhs.approx_eq_rel(&lhs));

    assert!(<Thin<f32> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_close, 0.001_f32));
    assert!(!<Thin<f32> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_far, 0.001_f32));
    assert!(<Thin<f32> as ApproxEqAbs>::approx_eq_abs(&lhs, &lhs));

    assert!(<Thin<f32> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_close, 0.001_f32));
    assert!(!<Thin<f32> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_far, 0.001_f32));
    assert!(<Thin<f32> as ApproxEqRel>::approx_eq_rel(&lhs, &lhs));
}

#[test]
fn approx_eq_wrapper_thick_single_surface() {
    let lhs = ThickSingle { value: 1.0_f32 };
    let rhs_close = ThickSingle { value: 1.0005_f32 };
    let rhs_far = ThickSingle { value: 1.01_f32 };

    assert!(lhs.approx_eq_abs_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_abs_tol(&rhs_far, 0.001_f32));

    assert!(lhs.approx_eq_rel_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_rel_tol(&rhs_far, 0.001_f32));

    assert!(lhs.approx_eq_abs(&lhs));
    assert!(lhs.approx_eq_rel(&lhs));

    assert!(<ThickSingle<f32> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_close, 0.001_f32));
    assert!(!<ThickSingle<f32> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_far, 0.001_f32));
    assert!(<ThickSingle<f32> as ApproxEqAbs>::approx_eq_abs(&lhs, &lhs));

    assert!(<ThickSingle<f32> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_close, 0.001_f32));
    assert!(!<ThickSingle<f32> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_far, 0.001_f32));
    assert!(<ThickSingle<f32> as ApproxEqRel>::approx_eq_rel(&lhs, &lhs));
}

#[test]
fn approx_eq_wrapper_thick_multi_surface() {
    let lhs = ThickMulti { value1: 10.0_f32, value2: 20.0_f32 };
    let rhs_close = ThickMulti { value1: 10.0_f32, value2: 20.01_f32 };
    let rhs_far = ThickMulti { value1: 10.0_f32, value2: 20.5_f32 };

    assert!(lhs.approx_eq_abs_tol(&rhs_close, 0.02_f32));
    assert!(!lhs.approx_eq_abs_tol(&rhs_far, 0.02_f32));

    assert!(lhs.approx_eq_rel_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_rel_tol(&rhs_far, 0.001_f32));

    assert!(lhs.approx_eq_abs(&lhs));
    assert!(lhs.approx_eq_rel(&lhs));

    assert!(<ThickMulti<f32> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_close, 0.02_f32));
    assert!(!<ThickMulti<f32> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_far, 0.02_f32));
    assert!(<ThickMulti<f32> as ApproxEqAbs>::approx_eq_abs(&lhs, &lhs));

    assert!(<ThickMulti<f32> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_close, 0.001_f32));
    assert!(!<ThickMulti<f32> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_far, 0.001_f32));
    assert!(<ThickMulti<f32> as ApproxEqRel>::approx_eq_rel(&lhs, &lhs));
}

#[test]
fn approx_eq_wrapper_thick_const_surface() {
    let lhs = ThickConst::<f32, 2> { value1: 10.0_f32, value2: 20.0_f32 };
    let rhs_close = ThickConst::<f32, 2> { value1: 10.0_f32, value2: 20.01_f32 };
    let rhs_far = ThickConst::<f32, 2> { value1: 10.0_f32, value2: 20.5_f32 };

    assert!(lhs.approx_eq_abs_tol(&rhs_close, 0.02_f32));
    assert!(!lhs.approx_eq_abs_tol(&rhs_far, 0.02_f32));

    assert!(lhs.approx_eq_rel_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_rel_tol(&rhs_far, 0.001_f32));

    assert!(lhs.approx_eq_abs(&lhs));
    assert!(lhs.approx_eq_rel(&lhs));

    assert!(<ThickConst<f32, 2> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_close, 0.02_f32));
    assert!(!<ThickConst<f32, 2> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_far, 0.02_f32));
    assert!(<ThickConst<f32, 2> as ApproxEqAbs>::approx_eq_abs(&lhs, &lhs));

    assert!(<ThickConst<f32, 2> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_close, 0.001_f32));
    assert!(!<ThickConst<f32, 2> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_far, 0.001_f32));
    assert!(<ThickConst<f32, 2> as ApproxEqRel>::approx_eq_rel(&lhs, &lhs));
}

#[test]
fn approx_eq_wrapper_signed_callback_surface() {
    let lhs = SignedEq { value: 2.0_f32 };
    let rhs_sign_flipped = SignedEq { value: -2.0_f32 };
    let rhs_far = SignedEq { value: -3.0_f32 };

    assert!(lhs.approx_eq_abs_tol(&rhs_sign_flipped, 0.001_f32));
    assert!(!lhs.approx_eq_abs_tol(&rhs_far, 0.001_f32));

    assert!(lhs.approx_eq_rel_tol(&rhs_sign_flipped, 0.001_f32));
    assert!(!lhs.approx_eq_rel_tol(&rhs_far, 0.001_f32));

    assert!(lhs.approx_eq_abs(&lhs));
    assert!(lhs.approx_eq_rel(&lhs));

    assert!(<SignedEq<f32> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_sign_flipped, 0.001_f32));
    assert!(!<SignedEq<f32> as ApproxEqAbs>::approx_eq_abs_tol(&lhs, &rhs_far, 0.001_f32));
    assert!(<SignedEq<f32> as ApproxEqAbs>::approx_eq_abs(&lhs, &lhs));

    assert!(<SignedEq<f32> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_sign_flipped, 0.001_f32));
    assert!(!<SignedEq<f32> as ApproxEqRel>::approx_eq_rel_tol(&lhs, &rhs_far, 0.001_f32));
    assert!(<SignedEq<f32> as ApproxEqRel>::approx_eq_rel(&lhs, &lhs));
}

#[test]
fn approx_eq_abs_array_blanket_impl() {
    let lhs = [1.0_f32, 2.0_f32, 3.0_f32];
    let rhs_close = [1.0_f32, 2.0005_f32, 3.0_f32];
    let rhs_far = [1.0_f32, 2.01_f32, 3.0_f32];

    assert!(lhs.approx_eq_abs_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_abs_tol(&rhs_far, 0.001_f32));
}

#[test]
fn approx_eq_rel_array_blanket_impl() {
    let lhs = [100.0_f32, 200.0_f32, 300.0_f32];
    let rhs_close = [100.05_f32, 200.0_f32, 300.0_f32];
    let rhs_far = [101.0_f32, 200.0_f32, 300.0_f32];

    assert!(lhs.approx_eq_rel_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_rel_tol(&rhs_far, 0.001_f32));
}

#[test]
fn approx_eq_abs_slice_blanket_impl() {
    let lhs = [1.0_f32, 2.0_f32, 3.0_f32, 4.0_f32];
    let rhs_close = [1.0_f32, 2.0_f32, 3.0005_f32, 4.0_f32];
    let rhs_short = [1.0_f32, 2.0_f32, 3.0_f32];

    assert!((lhs[..]).approx_eq_abs_tol(&rhs_close[..], 0.001_f32));
    assert!(!(lhs[..]).approx_eq_abs_tol(&rhs_short[..], 0.001_f32));
}

#[test]
fn approx_eq_rel_slice_blanket_impl() {
    let lhs = [10.0_f32, 20.0_f32, 30.0_f32, 40.0_f32];
    let rhs_close = [10.0_f32, 20.01_f32, 30.0_f32, 40.0_f32];
    let rhs_short = [10.0_f32, 20.0_f32, 30.0_f32];

    assert!((lhs[..]).approx_eq_rel_tol(&rhs_close[..], 0.001_f32));
    assert!(!(lhs[..]).approx_eq_rel_tol(&rhs_short[..], 0.001_f32));
}

#[test]
fn approx_eq_abs_vec_blanket_impl() {
    let lhs = Vec::from([1.0_f32, 2.0_f32, 3.0_f32]);
    let rhs_close = Vec::from([1.0_f32, 2.0_f32, 3.0005_f32]);
    let rhs_short = Vec::from([1.0_f32, 2.0_f32]);

    assert!(lhs.approx_eq_abs_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_abs_tol(&rhs_short, 0.001_f32));
}

#[test]
fn approx_eq_rel_vec_blanket_impl() {
    let lhs = Vec::from([10.0_f32, 20.0_f32, 30.0_f32]);
    let rhs_close = Vec::from([10.0_f32, 20.01_f32, 30.0_f32]);
    let rhs_short = Vec::from([10.0_f32, 20.0_f32]);

    assert!(lhs.approx_eq_rel_tol(&rhs_close, 0.001_f32));
    assert!(!lhs.approx_eq_rel_tol(&rhs_short, 0.001_f32));
}

#[test]
fn approx_eq_macro_variants_surface() {
    assert!(approx_eq_abs!(1.0_f32, 1.0_f32 + 5e-7));
    assert!(approx_eq_abs!(1.0_f32, 1.0_f32 + 5e-7, 1e-6_f32));
    assert!(approx_ne_abs!(1.0_f32, 1.1_f32));
    assert!(approx_ne_abs!(1.0_f32, 1.1_f32, 1e-6_f32));

    assert!(approx_eq_rel!(1000.0_f32, 1000.001_f32));
    assert!(approx_eq_rel!(1000.0_f32, 1000.001_f32, 1e-5_f32));
    assert!(approx_ne_rel!(1.0_f32, 2.0_f32));
    assert!(approx_ne_rel!(1.0_f32, 2.0_f32, 1e-5_f32));

    assert_approx_eq_abs!(1.0_f32, 1.0_f32 + 5e-7);
    assert_approx_eq_abs!(1.0_f32, 1.0_f32 + 5e-7, 1e-6_f32);
    assert_approx_eq_abs!(1.0_f32, 1.0_f32 + 5e-7, message = "abs {} message form", "default");
    assert_approx_eq_abs!(
        1.0_f32,
        1.0_f32 + 5e-7,
        1e-6_f32,
        message = "abs {} message form",
        "tol"
    );

    assert_approx_ne_abs!(1.0_f32, 1.1_f32);
    assert_approx_ne_abs!(1.0_f32, 1.1_f32, 1e-6_f32);
    assert_approx_ne_abs!(1.0_f32, 1.1_f32, message = "ne abs {} message form", "default");
    assert_approx_ne_abs!(1.0_f32, 1.1_f32, 1e-6_f32, message = "ne abs {} message form", "tol");

    assert_approx_eq_rel!(1000.0_f32, 1000.001_f32);
    assert_approx_eq_rel!(1000.0_f32, 1000.001_f32, 1e-5_f32);
    assert_approx_eq_rel!(1000.0_f32, 1000.001_f32, message = "rel {} message form", "default");
    assert_approx_eq_rel!(
        1000.0_f32,
        1000.001_f32,
        1e-5_f32,
        message = "rel {} message form",
        "tol"
    );

    assert_approx_ne_rel!(1.0_f32, 2.0_f32);
    assert_approx_ne_rel!(1.0_f32, 2.0_f32, 1e-5_f32);
    assert_approx_ne_rel!(1.0_f32, 2.0_f32, message = "ne rel {} message form", "default");
    assert_approx_ne_rel!(1.0_f32, 2.0_f32, 1e-5_f32, message = "ne rel {} message form", "tol");
}

#[test]
#[should_panic(expected = "abs message panic: 1 2")]
fn approx_eq_abs_message_variant_panics() {
    assert_approx_eq_abs!(1.0_f32, 2.0_f32, message = "abs message panic: {} {}", 1, 2);
}

#[test]
#[should_panic(expected = "abs tol message panic: 0.000001")]
fn approx_eq_abs_tol_message_variant_panics() {
    assert_approx_eq_abs!(
        1.0_f32,
        2.0_f32,
        1e-6_f32,
        message = "abs tol message panic: {}",
        1e-6_f32
    );
}

#[test]
#[should_panic(expected = "ne abs message panic: x")]
fn approx_ne_abs_message_variant_panics() {
    assert_approx_ne_abs!(1.0_f32, 1.0_f32 + 5e-7, message = "ne abs message panic: {}", "x");
}

#[test]
#[should_panic(expected = "ne abs tol message panic: 7")]
fn approx_ne_abs_tol_message_variant_panics() {
    assert_approx_ne_abs!(
        1.0_f32,
        1.0_f32 + 5e-7,
        1e-6_f32,
        message = "ne abs tol message panic: {}",
        7
    );
}

#[test]
#[should_panic(expected = "rel message panic: lhs rhs")]
fn approx_eq_rel_message_variant_panics() {
    assert_approx_eq_rel!(1.0_f32, 2.0_f32, message = "rel message panic: {} {}", "lhs", "rhs");
}

#[test]
#[should_panic(expected = "rel tol message panic: 0.00001")]
fn approx_eq_rel_tol_message_variant_panics() {
    assert_approx_eq_rel!(
        1.0_f32,
        2.0_f32,
        1e-5_f32,
        message = "rel tol message panic: {}",
        1e-5_f32
    );
}

#[test]
#[should_panic(expected = "ne rel message panic: z")]
fn approx_ne_rel_message_variant_panics() {
    assert_approx_ne_rel!(100_000.0_f32, 100_000.5_f32, message = "ne rel message panic: {}", "z");
}

#[test]
#[should_panic(expected = "ne rel tol message panic: ok")]
fn approx_ne_rel_tol_message_variant_panics() {
    assert_approx_ne_rel!(
        1000.0_f32,
        1000.001_f32,
        1e-5_f32,
        message = "ne rel tol message panic: {}",
        "ok"
    );
}