use core::fmt;
pub trait RelativeEq {
type Epsilon: Copy + fmt::Debug;
fn default_epsilon() -> Self::Epsilon;
fn default_max_relative() -> Self::Epsilon;
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool;
}
impl RelativeEq for f32 {
type Epsilon = Self;
fn default_epsilon() -> Self::Epsilon {
Self::EPSILON
}
fn default_max_relative() -> Self::Epsilon {
Self::EPSILON
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
if self == other {
return true;
}
if self.is_infinite() || other.is_infinite() {
return false;
}
let diff = (*self - *other).abs();
if diff <= epsilon {
return true;
}
let scale = self.abs().max(other.abs());
diff <= max_relative * scale
}
}
impl RelativeEq for f64 {
type Epsilon = Self;
fn default_epsilon() -> Self::Epsilon {
Self::EPSILON
}
fn default_max_relative() -> Self::Epsilon {
Self::EPSILON
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
if self == other {
return true;
}
if self.is_infinite() || other.is_infinite() {
return false;
}
let diff = (*self - *other).abs();
if diff <= epsilon {
return true;
}
let scale = self.abs().max(other.abs());
diff <= max_relative * scale
}
}
impl<T: RelativeEq, const N: usize> RelativeEq for [T; N] {
type Epsilon = T::Epsilon;
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.iter()
.zip(other.iter())
.all(|(a, b)| a.relative_eq(b, epsilon, max_relative))
}
}
impl<T: RelativeEq> RelativeEq for [T] {
type Epsilon = T::Epsilon;
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
if self.len() != other.len() {
return false;
}
self.iter()
.zip(other.iter())
.all(|(a, b)| a.relative_eq(b, epsilon, max_relative))
}
}
impl<T: RelativeEq + ?Sized> RelativeEq for &T {
type Epsilon = T::Epsilon;
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
<T as RelativeEq>::relative_eq(*self, *other, epsilon, max_relative)
}
}
impl<T: RelativeEq> RelativeEq for crate::Complex<T> {
type Epsilon = T::Epsilon;
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.re.relative_eq(&other.re, epsilon, max_relative)
&& self.im.relative_eq(&other.im, epsilon, max_relative)
}
}
#[doc(hidden)]
#[cold]
#[inline(never)]
pub fn __relative_eq_panic<T: fmt::Debug, E: fmt::Debug>(
left: &T,
right: &T,
epsilon: E,
max_relative: E,
) -> ! {
panic!(
"assert_relative_eq failed\n left: {:?}\n right: {:?}\n epsilon: {:?}\n max_relative: {:?}",
left, right, epsilon, max_relative
)
}
#[doc(hidden)]
pub fn __default_epsilon<T: RelativeEq + ?Sized>(_: &T) -> T::Epsilon {
T::default_epsilon()
}
#[doc(hidden)]
pub fn __default_max_relative<T: RelativeEq + ?Sized>(_: &T) -> T::Epsilon {
T::default_max_relative()
}
#[macro_export]
macro_rules! assert_relative_eq {
($left:expr, $right:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $crate::relative_eq::__default_epsilon(left);
let max_relative = $crate::relative_eq::__default_max_relative(left);
if !$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative) {
$crate::relative_eq::__relative_eq_panic(left, right, epsilon, max_relative);
}
}};
($left:expr, $right:expr, epsilon = $epsilon:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $epsilon;
let max_relative = $crate::relative_eq::__default_max_relative(left);
if !$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative) {
$crate::relative_eq::__relative_eq_panic(left, right, epsilon, max_relative);
}
}};
($left:expr, $right:expr, max_relative = $max_relative:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $crate::relative_eq::__default_epsilon(left);
let max_relative = $max_relative;
if !$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative) {
$crate::relative_eq::__relative_eq_panic(left, right, epsilon, max_relative);
}
}};
($left:expr, $right:expr, epsilon = $epsilon:expr, max_relative = $max_relative:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $epsilon;
let max_relative = $max_relative;
if !$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative) {
$crate::relative_eq::__relative_eq_panic(left, right, epsilon, max_relative);
}
}};
($left:expr, $right:expr, max_relative = $max_relative:expr, epsilon = $epsilon:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $epsilon;
let max_relative = $max_relative;
if !$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative) {
$crate::relative_eq::__relative_eq_panic(left, right, epsilon, max_relative);
}
}};
}
#[macro_export]
macro_rules! assert_abs_diff_eq {
($left:expr, $right:expr $(,)?) => {{
$crate::assert_relative_eq!($left, $right, max_relative = 0.0)
}};
($left:expr, $right:expr, epsilon = $epsilon:expr $(,)?) => {{
$crate::assert_relative_eq!($left, $right, epsilon = $epsilon, max_relative = 0.0)
}};
}
#[macro_export]
macro_rules! relative_eq {
($left:expr, $right:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $crate::relative_eq::__default_epsilon(left);
let max_relative = $crate::relative_eq::__default_max_relative(left);
$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative)
}};
($left:expr, $right:expr, epsilon = $epsilon:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $epsilon;
let max_relative = $crate::relative_eq::__default_max_relative(left);
$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative)
}};
($left:expr, $right:expr, max_relative = $max_relative:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $crate::relative_eq::__default_epsilon(left);
let max_relative = $max_relative;
$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative)
}};
($left:expr, $right:expr, epsilon = $epsilon:expr, max_relative = $max_relative:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $epsilon;
let max_relative = $max_relative;
$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative)
}};
($left:expr, $right:expr, max_relative = $max_relative:expr, epsilon = $epsilon:expr $(,)?) => {{
let left = &($left);
let right = &($right);
let epsilon = $epsilon;
let max_relative = $max_relative;
$crate::RelativeEq::relative_eq(left, right, epsilon, max_relative)
}};
}
#[macro_export]
macro_rules! abs_diff_eq {
($left:expr, $right:expr $(,)?) => {{
$crate::relative_eq!($left, $right, max_relative = 0.0)
}};
($left:expr, $right:expr, epsilon = $epsilon:expr $(,)?) => {{
$crate::relative_eq!($left, $right, epsilon = $epsilon, max_relative = 0.0)
}};
}
#[cfg(test)]
mod tests;