nearly 0.4.0

Compare IEEE floating point types
Documentation
#![cfg(feature = "std")]

use mockall::predicate::eq;
use nearly::{
    NearlyEqEps, NearlyEqTol, NearlyEqUlps, NearlyOrdEps, NearlyOrdTol, NearlyOrdUlps, Tolerance,
};
use paste::paste;
use std::collections::{BTreeMap, HashMap};

mod common;
use common::{MockLhs, Rhs};

macro_rules! checkpoint {
    ($container: expr) => {
        for i in $container.iter_mut() {
            i.1.checkpoint();
        }
    };
}

macro_rules! lhs_value {
    (hashmap) => {
        HashMap::from([
            (0, MockLhs::new()),
            (1, MockLhs::new()),
            (2, MockLhs::new()),
        ])
    };
    (btree_map) => {
        BTreeMap::from([
            (0, MockLhs::new()),
            (1, MockLhs::new()),
            (2, MockLhs::new()),
        ])
    };
}

macro_rules! lhs_value_short {
    (hashmap) => {
        HashMap::from([(0, MockLhs::new()), (1, MockLhs::new())])
    };
    (btree_map) => {
        BTreeMap::from([(0, MockLhs::new()), (1, MockLhs::new())])
    };
}

macro_rules! rhs_value {
    (hashmap) => {
        HashMap::from([(0, Rhs(3)), (1, Rhs(7)), (2, Rhs(11))])
    };
    (btree_map) => {
        BTreeMap::from([(0, Rhs(3)), (1, Rhs(7)), (2, Rhs(11))])
    };
}

macro_rules! rhs_value_short {
    (hashmap) => {
        HashMap::from([(0, Rhs(3)), (1, Rhs(7))])
    };
    (btree_map) => {
        BTreeMap::from([(0, Rhs(3)), (1, Rhs(7))])
    };
}

macro_rules! get_type {
    ($inner: ty, hashmap) => {
        HashMap<i32, $inner>
    };
    ($inner: ty, btree_map) => {
        BTreeMap<i32, $inner>
    }
}

macro_rules! lhs_type {
    ($coll: tt) => {
        get_type!(MockLhs, $coll)
    };
}

macro_rules! rhs_type {
    ($coll: tt) => {
        get_type!(Rhs, $coll)
    };
}

macro_rules! get_element {
    ($container: expr, $idx: expr) => {
        *$container.get_mut(&$idx).expect("Invalid key")
    };
}

macro_rules! impl_test {
    ($lhs: tt, $rhs: tt) => {
        impl_test_fn!($lhs, $rhs, eq);
        impl_test_fn!($lhs, $rhs, lt);
        impl_test_fn!($lhs, $rhs, le);
        impl_test_fn!($lhs, $rhs, gt);
        impl_test_fn!($lhs, $rhs, ge);
    };
}

macro_rules! impl_test_fn {
    ($lhs: tt, $rhs: tt, $fn: ident) => {
        paste! {
            #[test]
            fn [<nearly_ $fn _eps_ $lhs _ $rhs>]() {
                let mut a: lhs_type!($lhs) = lhs_value!($lhs);
                let b: rhs_type!($rhs)  = rhs_value!($rhs);

                get_element!(a, 0).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(3)), eq(0.1))
                    .times(1)
                    .return_const(true);
                get_element!(a, 1).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(7)), eq(0.1))
                    .times(1)
                    .return_const(true);
                get_element!(a, 2).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(11)), eq(0.1))
                    .times(1)
                    .return_const(true);

                assert!(a.[<nearly_ $fn _eps>](&b, &0.1));

                checkpoint!(a);
                get_element!(a, 0).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(3)), eq(0.1))
                    .times(1)
                    .return_const(false);
                get_element!(a, 1).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(7)), eq(0.1))
                    .return_const(true);
                get_element!(a, 2).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(11)), eq(0.1))
                    .return_const(true);

                assert!(!a.[<nearly_ $fn _eps>](&b, &0.1));

                checkpoint!(a);
                get_element!(a, 0).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(3)), eq(0.1))
                    .return_const(true);
                get_element!(a, 1).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(7)), eq(0.1))
                    .times(1)
                    .return_const(false);
                get_element!(a, 2).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(11)), eq(0.1))
                    .return_const(true);

                assert!(!a.[<nearly_ $fn _eps>](&b, &0.1));

                checkpoint!(a);
                get_element!(a, 0).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(3)), eq(0.1))
                    .return_const(true);
                get_element!(a, 1).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(7)), eq(0.1))
                    .return_const(true);
                get_element!(a, 2).[<expect_nearly_ $fn _eps>]()
                    .with(eq(Rhs(11)), eq(0.1))
                    .times(1)
                    .return_const(false);

                assert!(!a.[<nearly_ $fn _eps>](&b, &0.1));
            }

            #[test]
            fn [<nearly_ $fn _ulps_ $lhs _ $rhs>]() {
                #[allow(unused_mut)]
                let mut a: lhs_type!($lhs) = lhs_value!($lhs);
                let b: rhs_type!($rhs)  = rhs_value!($rhs);

                get_element!(a, 0).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(3)), eq(5))
                    .times(1)
                    .return_const(true);
                get_element!(a, 1).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(7)), eq(5))
                    .times(1)
                    .return_const(true);
                get_element!(a, 2).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(11)), eq(5))
                    .times(1)
                    .return_const(true);

                assert!(a.[<nearly_ $fn _ulps>](&b, &5));

                checkpoint!(a);
                get_element!(a, 0).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(3)), eq(5))
                    .times(1)
                    .return_const(false);
                get_element!(a, 1).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(7)), eq(5))
                    .return_const(true);
                get_element!(a, 2).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(11)), eq(5))
                    .return_const(true);

                assert!(!a.[<nearly_ $fn _ulps>](&b, &5));

                checkpoint!(a);
                get_element!(a, 0).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(3)), eq(5))
                    .return_const(true);
                get_element!(a, 1).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(7)), eq(5))
                    .times(1)
                    .return_const(false);
                get_element!(a, 2).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(11)), eq(5))
                    .return_const(true);

                assert!(!a.[<nearly_ $fn _ulps>](&b, &5));

                checkpoint!(a);
                get_element!(a, 0).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(3)), eq(5))
                    .return_const(true);
                get_element!(a, 1).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(7)), eq(5))
                    .return_const(true);
                get_element!(a, 2).[<expect_nearly_ $fn _ulps>]()
                    .with(eq(Rhs(11)), eq(5))
                    .times(1)
                    .return_const(false);

                assert!(!a.[<nearly_ $fn _ulps>](&b, &5));
            }

            #[test]
            fn [<nearly_ $fn _tol_ $lhs _ $rhs>]() {
                let mut a: lhs_type!($lhs) = lhs_value!($lhs);
                let b: rhs_type!($rhs)  = rhs_value!($rhs);

                get_element!(a, 0).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(3)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .times(1)
                    .return_const(true);
                get_element!(a, 1).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(7)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .times(1)
                    .return_const(true);
                get_element!(a, 2).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(11)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .times(1)
                    .return_const(true);

                assert!(a.[<nearly_ $fn _tol>](&b, &Tolerance::<MockLhs, Rhs>::new(0.1, 5)));

                checkpoint!(a);
                get_element!(a, 0).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(3)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .times(1)
                    .return_const(false);
                get_element!(a, 1).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(7)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .return_const(true);
                get_element!(a, 2).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(11)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .return_const(true);

                assert!(!a.[<nearly_ $fn _tol>](&b, &Tolerance::<MockLhs, Rhs>::new(0.1, 5)));

                checkpoint!(a);
                get_element!(a, 0).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(3)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .return_const(true);
                get_element!(a, 1).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(7)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .times(1)
                    .return_const(false);
                get_element!(a, 2).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(11)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .return_const(true);

                assert!(!a.[<nearly_ $fn _tol>](&b, &Tolerance::<MockLhs, Rhs>::new(0.1, 5)));

                checkpoint!(a);
                get_element!(a, 0).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(3)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .return_const(true);
                get_element!(a, 1).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(7)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .return_const(true);
                get_element!(a, 2).[<expect_nearly_ $fn _tol>]()
                    .with(eq(Rhs(11)), eq(Tolerance::<MockLhs, Rhs>::new(0.1, 5)))
                    .times(1)
                    .return_const(false);

                assert!(!a.[<nearly_ $fn _tol>](&b, &Tolerance::<MockLhs, Rhs>::new(0.1, 5)));
            }

            #[test]
            fn [<nearly_ $fn _eps_different_length_ $lhs _ $rhs>]() {
                {
                    let mut a: lhs_type!($lhs) = lhs_value!($lhs);
                    let b: rhs_type!($rhs)  = rhs_value_short!($rhs);

                    assert!(a.len() > b.len());

                    get_element!(a, 0).[<expect_nearly_ $fn _eps>]().times(0);
                    get_element!(a, 1).[<expect_nearly_ $fn _eps>]().times(0);
                    get_element!(a, 2).[<expect_nearly_ $fn _eps>]().times(0);

                    assert!(!a.[<nearly_ $fn _eps>](&b, &0.1));
                }
                {
                    let mut a: lhs_type!($lhs) = lhs_value_short!($lhs);
                    let b: rhs_type!($rhs)  = rhs_value!($rhs);

                    assert!(a.len() < b.len());

                    get_element!(a, 0).[<expect_nearly_ $fn _eps>]().times(0);
                    get_element!(a, 1).[<expect_nearly_ $fn _eps>]().times(0);

                    assert!(!a.[<nearly_ $fn _eps>](&b, &0.1));
                }
            }

            #[test]
            fn [<nearly_ $fn _ulps_different_length_ $lhs _ $rhs>]() {
                {
                    let mut a: lhs_type!($lhs) = lhs_value!($lhs);
                    let b: rhs_type!($rhs)  = rhs_value_short!($rhs);

                    assert!(a.len() > b.len());

                    get_element!(a, 0).[<expect_nearly_ $fn _ulps>]().times(0);
                    get_element!(a, 1).[<expect_nearly_ $fn _ulps>]().times(0);
                    get_element!(a, 2).[<expect_nearly_ $fn _ulps>]().times(0);

                    assert!(!a.[<nearly_ $fn _ulps>](&b, &5));
                }
                {
                    let mut a: lhs_type!($lhs) = lhs_value_short!($lhs);
                    let b: rhs_type!($rhs)  = rhs_value!($rhs);

                    assert!(a.len() < b.len());

                    get_element!(a, 0).[<expect_nearly_ $fn _ulps>]().times(0);
                    get_element!(a, 1).[<expect_nearly_ $fn _ulps>]().times(0);

                    assert!(!a.[<nearly_ $fn _ulps>](&b, &5));
                }
            }

            #[test]
            fn [<nearly_ $fn _tol_different_length_ $lhs _ $rhs>]() {
                {
                    let mut a: lhs_type!($lhs) = lhs_value!($lhs);
                    let b: rhs_type!($rhs)  = rhs_value_short!($rhs);

                    assert!(a.len() > b.len());

                    get_element!(a, 0).[<expect_nearly_ $fn _tol>]().times(0);
                    get_element!(a, 1).[<expect_nearly_ $fn _tol>]().times(0);
                    get_element!(a, 2).[<expect_nearly_ $fn _tol>]().times(0);

                    assert!(!a.[<nearly_ $fn _tol>](&b, &Tolerance::<MockLhs, Rhs>::new(0.1, 5)));
                }
                {
                    let mut a: lhs_type!($lhs) = lhs_value_short!($lhs);
                    let b: rhs_type!($rhs)  = rhs_value!($rhs);

                    assert!(a.len() < b.len());

                    get_element!(a, 0).[<expect_nearly_ $fn _tol>]().times(0);
                    get_element!(a, 1).[<expect_nearly_ $fn _tol>]().times(0);

                    assert!(!a.[<nearly_ $fn _tol>](&b, &Tolerance::<MockLhs, Rhs>::new(0.1, 5)));
                }
            }
        }
    };
}

impl_test!(btree_map, btree_map);
impl_test!(hashmap, hashmap);