extern crate self as aprox_eq;
use std::{borrow::Cow, ops::Deref};
pub use aprox_derive::AproxEq;
pub trait AproxEq<T = Self>
where
T: ?Sized,
{
#[must_use]
fn aprox_eq(&self, other: &T) -> bool;
#[inline]
#[must_use]
fn aprox_ne(&self, other: &T) -> bool {
!self.aprox_eq(other)
}
}
#[macro_export]
macro_rules! assert_aprox_eq {
($left:expr, $right:expr) => {
assert!(
aprox_eq::AproxEq::aprox_eq(&$left, &$right),
"assertion failed: `left.aprox_eq(right)`\n left: `{:#?}`,\n right: `{:#?}`",
$left,
$right,
);
};
}
#[macro_export]
macro_rules! assert_aprox_ne {
($left:expr, $right:expr) => {
assert!(
aprox_eq::AproxEq::aprox_ne(&$left, &$right),
"assertion failed: `left.aprox_ne(right)`\n left: `{:#?}`,\n right: `{:#?}`",
$left,
$right,
);
};
}
impl<T, U> AproxEq<&U> for &T
where
T: AproxEq<U>,
{
#[inline]
fn aprox_eq(&self, other: &&U) -> bool {
(*self).aprox_eq(*other)
}
}
impl<T, U> AproxEq<Vec<U>> for Vec<T>
where
T: AproxEq<U>,
{
#[inline]
fn aprox_eq(&self, other: &Vec<U>) -> bool {
self.as_slice().aprox_eq(other.as_slice())
}
}
impl<T, U> AproxEq<[U]> for [T]
where
T: AproxEq<U>,
{
fn aprox_eq(&self, other: &[U]) -> bool {
match self.len() == other.len() {
true => self.iter().zip(other).all(|(a, b)| a.aprox_eq(&b)),
false => false,
}
}
}
impl<T, U> AproxEq<&[U]> for &[T]
where
T: AproxEq<U>,
{
#[inline]
fn aprox_eq(&self, other: &&[U]) -> bool {
(*self).aprox_eq(*other)
}
}
impl<T, U, const N: usize> AproxEq<[U; N]> for [T; N]
where
T: AproxEq<U>,
{
fn aprox_eq(&self, other: &[U; N]) -> bool {
self.iter().zip(other).all(|(a, b)| a.aprox_eq(&b))
}
}
impl<T, U> AproxEq<Box<U>> for Box<T>
where
T: AproxEq<U>,
{
#[inline]
fn aprox_eq(&self, other: &Box<U>) -> bool {
self.deref().aprox_eq(other.deref())
}
}
impl<'a, T, U> AproxEq<Cow<'a, U>> for Cow<'a, T>
where
T: AproxEq<U> + Clone,
U: Clone,
{
#[inline]
fn aprox_eq(&self, other: &Cow<U>) -> bool {
self.deref().aprox_eq(other.deref())
}
}
impl AproxEq for f64 {
fn aprox_eq(&self, other: &Self) -> bool {
(self - other).abs() < 1e-12
}
}
impl AproxEq for f32 {
fn aprox_eq(&self, other: &Self) -> bool {
(self - other).abs() < 1e-6
}
}
#[cfg(test)]
mod tests {
use crate::AproxEq;
#[derive(AproxEq, Debug)]
struct SomeFoats {
pub a: f64,
pub b: f32,
}
#[derive(AproxEq, Debug)]
struct SomeAproxEq(SomeFoats, SomeFoats);
#[derive(Debug, AproxEq)]
enum MyEnum {
Var0(f32),
Var1(f64),
Var2(f32, f32, f64),
Var3 { a: f32, b: f64 },
}
#[test]
fn basic_aprox_eq() {
assert_aprox_eq!(1.0002_f64, 1.0001999999999999_f64);
assert_aprox_ne!(1.002_f32, 1.001_f32);
}
#[test]
fn ownership_retainance() {
let a = 1_f64;
let b = 1_f64;
assert_aprox_eq!(a, b);
let c = a + b;
assert_aprox_ne!(a, c);
}
#[test]
fn derive_struct() {
assert_aprox_eq!(
SomeFoats { a: 3f64, b: 2f32 },
SomeFoats { a: 3f64, b: 2f32 }
);
assert_aprox_eq!(
SomeAproxEq(
SomeFoats { a: 3f64, b: 2f32 },
SomeFoats { a: 5f64, b: 4f32 }
),
SomeAproxEq(
SomeFoats { a: 3f64, b: 2f32 },
SomeFoats { a: 5f64, b: 4f32 }
)
);
}
#[test]
fn derive_enum() {
assert_aprox_eq!(MyEnum::Var0(0f32), MyEnum::Var0(0f32));
assert_aprox_ne!(MyEnum::Var0(0f32), MyEnum::Var1(0f64));
assert_aprox_eq!(
MyEnum::Var2(0f32, 1f32, 2f64),
MyEnum::Var2(0f32, 1f32, 2f64)
);
assert_aprox_eq!(
MyEnum::Var3 { a: 3f32, b: 6f64 },
MyEnum::Var3 { a: 3f32, b: 6f64 }
);
}
#[test]
fn vec_aprox_eq() {
let vec0 = vec![0f32, 1f32, 1.2f32, 42f32];
let vec1 = vec0.clone();
let vec2 = vec![0.1f32, 0f32, 1.2f32, 42f32];
let vec3 = vec![0.1f32, 0f32, 1.2f32, 42f32, 32f32, 128f32, 0.3f32];
assert_aprox_eq!(vec0, vec1);
assert_aprox_ne!(vec0, vec2);
assert_aprox_ne!(vec0, vec3);
}
#[test]
fn slice_and_arr_aprox_eq() {
let arr0 = [0f32, 1f32, 1.2f32, 42f32];
let arr1 = [0f32, 1f32, 1.2f32, 42f32];
assert_aprox_eq!(arr0, arr1);
assert_aprox_eq!(arr0.as_slice(), arr1.as_slice());
}
#[test]
fn box_aprox_eq() {
let box0 = Box::new(12.2f32);
let box1 = Box::new(12.2f32);
assert_aprox_eq!(box0, box1);
}
}