Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add generic Rhs parameter for traits and structs and generalize implementations for [T] #48

Merged
merged 3 commits into from
Jan 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 14 additions & 10 deletions src/abs_diff_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use num_traits::float::FloatCore;
use std::{cell, f32, f64};

/// Equality that is defined using the absolute difference of two numbers.
pub trait AbsDiffEq: PartialEq {
pub trait AbsDiffEq<Rhs = Self>: PartialEq<Rhs>
where
Rhs: ?Sized,
{
/// Used for specifying relative comparisons.
type Epsilon;

Expand All @@ -17,10 +20,10 @@ pub trait AbsDiffEq: PartialEq {

/// A test for equality that uses the absolute difference to compute the approximate
/// equality of two numbers.
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool;
fn abs_diff_eq(&self, other: &Rhs, epsilon: Self::Epsilon) -> bool;

/// The inverse of `ApproxEq::abs_diff_eq`.
fn abs_diff_ne(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
fn abs_diff_ne(&self, other: &Rhs, epsilon: Self::Epsilon) -> bool {
!Self::abs_diff_eq(self, other, epsilon)
}
}
Expand Down Expand Up @@ -143,21 +146,22 @@ impl<T: AbsDiffEq + ?Sized> AbsDiffEq for cell::RefCell<T> {
}
}

impl<T: AbsDiffEq> AbsDiffEq for [T]
impl<A, B> AbsDiffEq<[B]> for [A]
where
T::Epsilon: Clone,
A: AbsDiffEq<B>,
A::Epsilon: Clone,
{
type Epsilon = T::Epsilon;
type Epsilon = A::Epsilon;

#[inline]
fn default_epsilon() -> T::Epsilon {
T::default_epsilon()
fn default_epsilon() -> A::Epsilon {
A::default_epsilon()
}

#[inline]
fn abs_diff_eq(&self, other: &[T], epsilon: T::Epsilon) -> bool {
fn abs_diff_eq(&self, other: &[B], epsilon: A::Epsilon) -> bool {
self.len() == other.len()
&& Iterator::zip(self.iter(), other).all(|(x, y)| T::abs_diff_eq(x, y, epsilon.clone()))
&& Iterator::zip(self.iter(), other).all(|(x, y)| A::abs_diff_eq(x, y, epsilon.clone()))
}
}

Expand Down
110 changes: 70 additions & 40 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,40 +188,49 @@ pub use ulps_eq::UlpsEq;
/// AbsDiff::default().eq(&1.0, &1.0);
/// AbsDiff::default().epsilon(f64::EPSILON).eq(&1.0, &1.0);
/// ```
pub struct AbsDiff<T: AbsDiffEq + ?Sized> {
pub struct AbsDiff<A, B = A>
where
A: AbsDiffEq<B> + ?Sized,
B: ?Sized,
{
/// The tolerance to use when testing values that are close together.
pub epsilon: T::Epsilon,
pub epsilon: A::Epsilon,
}

impl<T: AbsDiffEq + ?Sized> Default for AbsDiff<T> {
impl<A, B> Default for AbsDiff<A, B>
where
A: AbsDiffEq<B> + ?Sized,
B: ?Sized,
{
#[inline]
fn default() -> AbsDiff<T> {
fn default() -> AbsDiff<A, B> {
AbsDiff {
epsilon: T::default_epsilon(),
epsilon: A::default_epsilon(),
}
}
}

impl<T> AbsDiff<T>
impl<A, B> AbsDiff<A, B>
where
T: AbsDiffEq + ?Sized,
A: AbsDiffEq<B> + ?Sized,
B: ?Sized,
{
/// Replace the epsilon value with the one specified.
#[inline]
pub fn epsilon(self, epsilon: T::Epsilon) -> AbsDiff<T> {
pub fn epsilon(self, epsilon: A::Epsilon) -> AbsDiff<A, B> {
AbsDiff { epsilon, ..self }
}

/// Peform the equality comparison
#[inline]
pub fn eq(self, lhs: &T, rhs: &T) -> bool {
T::abs_diff_eq(lhs, rhs, self.epsilon)
pub fn eq(self, lhs: &A, rhs: &B) -> bool {
A::abs_diff_eq(lhs, rhs, self.epsilon)
}

/// Peform the inequality comparison
#[inline]
pub fn ne(self, lhs: &T, rhs: &T) -> bool {
T::abs_diff_ne(lhs, rhs, self.epsilon)
pub fn ne(self, lhs: &A, rhs: &B) -> bool {
A::abs_diff_ne(lhs, rhs, self.epsilon)
}
}

Expand All @@ -243,33 +252,45 @@ where
/// Relative::default().epsilon(f64::EPSILON).max_relative(1.0).eq(&1.0, &1.0);
/// Relative::default().max_relative(1.0).epsilon(f64::EPSILON).eq(&1.0, &1.0);
/// ```
pub struct Relative<T: RelativeEq + ?Sized> {
pub struct Relative<A, B = A>
where
A: RelativeEq<B> + ?Sized,
B: ?Sized,
{
/// The tolerance to use when testing values that are close together.
pub epsilon: T::Epsilon,
pub epsilon: A::Epsilon,
/// The relative tolerance for testing values that are far-apart.
pub max_relative: T::Epsilon,
pub max_relative: A::Epsilon,
}

impl<T: RelativeEq + ?Sized> Default for Relative<T> {
impl<A, B> Default for Relative<A, B>
where
A: RelativeEq<B> + ?Sized,
B: ?Sized,
{
#[inline]
fn default() -> Relative<T> {
fn default() -> Relative<A, B> {
Relative {
epsilon: T::default_epsilon(),
max_relative: T::default_max_relative(),
epsilon: A::default_epsilon(),
max_relative: A::default_max_relative(),
}
}
}

impl<T: RelativeEq + ?Sized> Relative<T> {
impl<A, B> Relative<A, B>
where
A: RelativeEq<B> + ?Sized,
B: ?Sized,
{
/// Replace the epsilon value with the one specified.
#[inline]
pub fn epsilon(self, epsilon: T::Epsilon) -> Relative<T> {
pub fn epsilon(self, epsilon: A::Epsilon) -> Relative<A, B> {
Relative { epsilon, ..self }
}

/// Replace the maximum relative value with the one specified.
#[inline]
pub fn max_relative(self, max_relative: T::Epsilon) -> Relative<T> {
pub fn max_relative(self, max_relative: A::Epsilon) -> Relative<A, B> {
Relative {
max_relative,
..self
Expand All @@ -278,14 +299,14 @@ impl<T: RelativeEq + ?Sized> Relative<T> {

/// Peform the equality comparison
#[inline]
pub fn eq(self, lhs: &T, rhs: &T) -> bool {
T::relative_eq(lhs, rhs, self.epsilon, self.max_relative)
pub fn eq(self, lhs: &A, rhs: &B) -> bool {
A::relative_eq(lhs, rhs, self.epsilon, self.max_relative)
}

/// Peform the inequality comparison
#[inline]
pub fn ne(self, lhs: &T, rhs: &T) -> bool {
T::relative_ne(lhs, rhs, self.epsilon, self.max_relative)
pub fn ne(self, lhs: &A, rhs: &B) -> bool {
A::relative_ne(lhs, rhs, self.epsilon, self.max_relative)
}
}

Expand All @@ -307,48 +328,57 @@ impl<T: RelativeEq + ?Sized> Relative<T> {
/// Ulps::default().epsilon(f64::EPSILON).max_ulps(4).eq(&1.0, &1.0);
/// Ulps::default().max_ulps(4).epsilon(f64::EPSILON).eq(&1.0, &1.0);
/// ```
pub struct Ulps<T: UlpsEq + ?Sized> {
pub struct Ulps<A, B = A>
where
A: UlpsEq<B> + ?Sized,
B: ?Sized,
{
/// The tolerance to use when testing values that are close together.
pub epsilon: T::Epsilon,
pub epsilon: A::Epsilon,
/// The ULPs to tolerate when testing values that are far-apart.
pub max_ulps: u32,
}

impl<T: UlpsEq + ?Sized> Default for Ulps<T>
impl<A, B> Default for Ulps<A, B>
where
T: UlpsEq,
A: UlpsEq<B> + ?Sized,
B: ?Sized,
{
#[inline]
fn default() -> Ulps<T> {
fn default() -> Ulps<A, B> {
Ulps {
epsilon: T::default_epsilon(),
max_ulps: T::default_max_ulps(),
epsilon: A::default_epsilon(),
max_ulps: A::default_max_ulps(),
}
}
}

impl<T: UlpsEq + ?Sized> Ulps<T> {
impl<A, B> Ulps<A, B>
where
A: UlpsEq<B> + ?Sized,
B: ?Sized,
{
/// Replace the epsilon value with the one specified.
#[inline]
pub fn epsilon(self, epsilon: T::Epsilon) -> Ulps<T> {
pub fn epsilon(self, epsilon: A::Epsilon) -> Ulps<A, B> {
Ulps { epsilon, ..self }
}

/// Replace the max ulps value with the one specified.
#[inline]
pub fn max_ulps(self, max_ulps: u32) -> Ulps<T> {
pub fn max_ulps(self, max_ulps: u32) -> Ulps<A, B> {
Ulps { max_ulps, ..self }
}

/// Peform the equality comparison
#[inline]
pub fn eq(self, lhs: &T, rhs: &T) -> bool {
T::ulps_eq(lhs, rhs, self.epsilon, self.max_ulps)
pub fn eq(self, lhs: &A, rhs: &B) -> bool {
A::ulps_eq(lhs, rhs, self.epsilon, self.max_ulps)
}

/// Peform the inequality comparison
#[inline]
pub fn ne(self, lhs: &T, rhs: &T) -> bool {
T::ulps_ne(lhs, rhs, self.epsilon, self.max_ulps)
pub fn ne(self, lhs: &A, rhs: &B) -> bool {
A::ulps_ne(lhs, rhs, self.epsilon, self.max_ulps)
}
}
22 changes: 13 additions & 9 deletions src/relative_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use AbsDiffEq;

/// Equality comparisons between two numbers using both the absolute difference and
/// relative based comparisons.
pub trait RelativeEq: AbsDiffEq {
pub trait RelativeEq<Rhs = Self>: AbsDiffEq<Rhs>
where
Rhs: ?Sized,
{
/// The default relative tolerance for testing values that are far-apart.
///
/// This is used when no `max_relative` value is supplied to the `relative_eq` macro.
Expand All @@ -17,15 +20,15 @@ pub trait RelativeEq: AbsDiffEq {
/// A test for equality that uses a relative comparison if the values are far apart.
fn relative_eq(
&self,
other: &Self,
other: &Rhs,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool;

/// The inverse of `ApproxEq::relative_eq`.
fn relative_ne(
&self,
other: &Self,
other: &Rhs,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
Expand Down Expand Up @@ -152,20 +155,21 @@ impl<T: RelativeEq + ?Sized> RelativeEq for cell::RefCell<T> {
}
}

impl<T: RelativeEq> RelativeEq for [T]
impl<A, B> RelativeEq<[B]> for [A]
where
T::Epsilon: Clone,
A: RelativeEq<B>,
A::Epsilon: Clone,
{
#[inline]
fn default_max_relative() -> T::Epsilon {
T::default_max_relative()
fn default_max_relative() -> A::Epsilon {
A::default_max_relative()
}

#[inline]
fn relative_eq(&self, other: &[T], epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
fn relative_eq(&self, other: &[B], epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool {
self.len() == other.len()
&& Iterator::zip(self.iter(), other)
.all(|(x, y)| T::relative_eq(x, y, epsilon.clone(), max_relative.clone()))
.all(|(x, y)| A::relative_eq(x, y, epsilon.clone(), max_relative.clone()))
}
}

Expand Down
20 changes: 12 additions & 8 deletions src/ulps_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ use AbsDiffEq;

/// Equality comparisons between two numbers using both the absolute difference and ULPs
/// (Units in Last Place) based comparisons.
pub trait UlpsEq: AbsDiffEq {
pub trait UlpsEq<Rhs = Self>: AbsDiffEq<Rhs>
where
Rhs: ?Sized,
{
/// The default ULPs to tolerate when testing values that are far-apart.
///
/// This is used when no `max_ulps` value is supplied to the `ulps_eq` macro.
fn default_max_ulps() -> u32;

/// A test for equality that uses units in the last place (ULP) if the values are far apart.
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool;
fn ulps_eq(&self, other: &Rhs, epsilon: Self::Epsilon, max_ulps: u32) -> bool;

/// The inverse of `ApproxEq::ulps_eq`.
fn ulps_ne(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
fn ulps_ne(&self, other: &Rhs, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
!Self::ulps_eq(self, other, epsilon, max_ulps)
}
}
Expand Down Expand Up @@ -114,20 +117,21 @@ impl<T: UlpsEq + ?Sized> UlpsEq for cell::RefCell<T> {
}
}

impl<T: UlpsEq> UlpsEq for [T]
impl<A, B> UlpsEq<[B]> for [A]
where
T::Epsilon: Clone,
A: UlpsEq<B>,
A::Epsilon: Clone,
{
#[inline]
fn default_max_ulps() -> u32 {
T::default_max_ulps()
A::default_max_ulps()
}

#[inline]
fn ulps_eq(&self, other: &[T], epsilon: T::Epsilon, max_ulps: u32) -> bool {
fn ulps_eq(&self, other: &[B], epsilon: A::Epsilon, max_ulps: u32) -> bool {
self.len() == other.len()
&& Iterator::zip(self.iter(), other)
.all(|(x, y)| T::ulps_eq(x, y, epsilon.clone(), max_ulps.clone()))
.all(|(x, y)| A::ulps_eq(x, y, epsilon.clone(), max_ulps.clone()))
}
}

Expand Down