#[cfg(not(feature = "std"))]
use core::fmt::Debug;
#[cfg(feature = "std")]
use std::fmt::Debug;
pub trait EpsTolerance<Rhs = Self>
where
Rhs: ?Sized,
{
type T: Debug + Copy;
const DEFAULT: Self::T;
}
pub type EpsToleranceType<Lhs, Rhs = Lhs> = <Lhs as EpsTolerance<Rhs>>::T;
pub trait UlpsTolerance<Rhs = Self>
where
Rhs: ?Sized,
{
type T: Debug + Copy;
const DEFAULT: Self::T;
}
pub type UlpsToleranceType<Lhs, Rhs = Lhs> = <Lhs as UlpsTolerance<Rhs>>::T;
pub trait EpsAndUlpsTolerance<Rhs = Self>: EpsTolerance<Rhs> + UlpsTolerance<Rhs>
where
Rhs: ?Sized,
{
}
impl<Lhs, Rhs> EpsAndUlpsTolerance<Rhs> for Lhs
where
Lhs: ?Sized + EpsTolerance<Rhs> + UlpsTolerance<Rhs>,
Rhs: ?Sized,
{
}
impl EpsTolerance for f32 {
type T = f32;
const DEFAULT: f32 = 1e-6;
}
impl UlpsTolerance for f32 {
type T = i32;
const DEFAULT: i32 = 4;
}
impl EpsTolerance for f64 {
type T = f64;
const DEFAULT: f64 = 1e-14;
}
impl UlpsTolerance for f64 {
type T = i64;
const DEFAULT: i64 = 4;
}
pub type ToleranceF32 = Tolerance<f32>;
pub type ToleranceF64 = Tolerance<f64>;
#[derive(Clone, Copy, Debug)]
pub struct Tolerance<Lhs, Rhs = Lhs>
where
Lhs: ?Sized + EpsAndUlpsTolerance<Rhs>,
Rhs: ?Sized,
{
pub eps: EpsToleranceType<Lhs, Rhs>,
pub ulps: UlpsToleranceType<Lhs, Rhs>,
}
impl<Lhs, Rhs> Tolerance<Lhs, Rhs>
where
Lhs: ?Sized + EpsAndUlpsTolerance<Rhs>,
Rhs: ?Sized,
{
pub fn new(eps: EpsToleranceType<Lhs, Rhs>, ulps: UlpsToleranceType<Lhs, Rhs>) -> Self {
Tolerance::<Lhs, Rhs> { eps, ulps }
}
}
impl<Lhs, Rhs> Default for Tolerance<Lhs, Rhs>
where
Lhs: ?Sized + EpsAndUlpsTolerance<Rhs>,
Rhs: ?Sized,
{
fn default() -> Self {
Tolerance::<Lhs, Rhs> {
eps: <Lhs as EpsTolerance<Rhs>>::DEFAULT,
ulps: <Lhs as UlpsTolerance<Rhs>>::DEFAULT,
}
}
}
impl<Lhs, Rhs> From<(EpsToleranceType<Lhs, Rhs>, UlpsToleranceType<Lhs, Rhs>)>
for Tolerance<Lhs, Rhs>
where
Lhs: ?Sized + EpsAndUlpsTolerance<Rhs>,
Rhs: ?Sized,
{
fn from(tuple: (EpsToleranceType<Lhs, Rhs>, UlpsToleranceType<Lhs, Rhs>)) -> Self {
Tolerance::<Lhs, Rhs> {
eps: tuple.0,
ulps: tuple.1,
}
}
}
impl<Lhs, Rhs> From<Tolerance<Lhs, Rhs>>
for (EpsToleranceType<Lhs, Rhs>, UlpsToleranceType<Lhs, Rhs>)
where
Lhs: ?Sized + EpsAndUlpsTolerance<Rhs>,
Rhs: ?Sized,
{
fn from(val: Tolerance<Lhs, Rhs>) -> Self {
(val.eps, val.ulps)
}
}
#[cfg(test)]
mod tests {
use super::{EpsToleranceType, Tolerance, UlpsToleranceType};
#[test]
fn new_f32() {
let tolerance = Tolerance::<f32>::new(0.01, 5);
assert_eq!(tolerance.eps, 0.01);
assert_eq!(tolerance.ulps, 5);
}
#[test]
fn new_f64() {
let tolerance = Tolerance::<f64>::new(0.01, 5);
assert_eq!(tolerance.eps, 0.01);
assert_eq!(tolerance.ulps, 5);
}
#[test]
fn default_f32() {
let tolerance = Tolerance::<f32>::default();
assert_ne!(tolerance.eps, 0.0);
assert_ne!(tolerance.ulps, 0);
}
#[test]
fn default_f64() {
let tolerance = Tolerance::<f64>::default();
assert_ne!(tolerance.eps, 0.0);
assert_ne!(tolerance.ulps, 0);
}
#[test]
fn from_tuple_f32() {
let tuple: (EpsToleranceType<f32>, UlpsToleranceType<f32>) = (0.01, 5);
let tolerance = Tolerance::<f32>::from(tuple);
assert_eq!(tolerance.eps, 0.01);
assert_eq!(tolerance.ulps, 5);
}
#[test]
fn from_tuple_f64() {
let tuple: (EpsToleranceType<f64>, UlpsToleranceType<f64>) = (0.01, 5);
let tolerance = Tolerance::<f64>::from(tuple);
assert_eq!(tolerance.eps, 0.01);
assert_eq!(tolerance.ulps, 5);
}
#[test]
fn into_tuple_f32() {
let tolerance = Tolerance::<f32> { eps: 0.01, ulps: 5 };
let tuple: (EpsToleranceType<f32>, UlpsToleranceType<f32>) = tolerance.into();
assert_eq!(tuple.0, 0.01);
assert_eq!(tuple.1, 5);
}
#[test]
fn into_tuple_f64() {
let tolerance = Tolerance::<f64> { eps: 0.01, ulps: 5 };
let tuple: (EpsToleranceType<f64>, UlpsToleranceType<f64>) = tolerance.into();
assert_eq!(tuple.0, 0.01);
assert_eq!(tuple.1, 5);
}
}