use core::ops::Neg;
use num_derive::{Float, FromPrimitive, Num, NumCast, NumOps, One, Signed, ToPrimitive, Zero};
use num_traits::{Euclid, Float, Inv};
use crate::traits::NonZero;
macro_rules! define_epsilon_metric {
($name:ident, $inner:ty, $epsilon:expr, $doc:expr) => {
#[doc = $doc]
#[derive(
Debug,
Clone,
Copy,
Float,
Num,
Signed,
Zero,
One,
NumOps,
NumCast,
ToPrimitive,
FromPrimitive,
)]
pub struct $name(pub $inner);
impl PartialEq for $name {
fn eq(&self, other: &Self) -> bool {
let a = self.0;
let b = other.0;
let diff = (a - b).abs();
let magnitude = a.abs().max(b.abs());
diff < $epsilon || diff < magnitude * $epsilon
}
}
impl PartialOrd for $name {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
if self == other {
Some(core::cmp::Ordering::Equal)
} else {
self.0.partial_cmp(&other.0)
}
}
}
impl Euclid for $name {
fn div_euclid(&self, v: &Self) -> Self {
Self(<$inner as Euclid>::div_euclid(&self.0, &v.0))
}
fn rem_euclid(&self, v: &Self) -> Self {
Self(<$inner as Euclid>::rem_euclid(&self.0, &v.0))
}
}
impl Neg for $name {
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
}
}
impl core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Inv for NonZero<$name> {
type Output = Self;
fn inv(self) -> Self {
Self::new_unchecked(self.0.recip())
}
}
};
}
define_epsilon_metric!(
R64,
f64,
1e-12,
"A tolerance-comparison `f64`, treating values within `1e-12` as equal."
);
define_epsilon_metric!(
R32,
f32,
1e-5,
"A tolerance-comparison `f32`, treating values within `1e-5` as equal."
);