Skip to content

Commit

Permalink
Merge pull request #48 from jturner314/generic-rhs
Browse files Browse the repository at this point in the history
Add generic Rhs parameter for traits and structs and generalize implementations for [T]
  • Loading branch information
brendanzab committed Jan 13, 2019
2 parents 648bf19 + e589ac7 commit 492e9b5
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 67 deletions.
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

0 comments on commit 492e9b5

Please sign in to comment.