use num_traits::{Float, ToPrimitive};
#[derive(Debug, Clone, Copy)]
pub struct Tolerance<F> {
pub(super) relative: F,
pub(super) absolute: F,
}
impl<F: Float> Default for Tolerance<F> {
fn default() -> Self {
Self::balanced()
}
}
impl<F: Float> Tolerance<F> {
pub fn strict() -> Self {
Self {
relative: F::from(0.00).unwrap(),
absolute: F::from(64).unwrap() * F::min_positive_value(),
}
}
pub fn balanced() -> Self {
Self {
relative: F::from(0.005).unwrap(), absolute: F::from(1e-5).unwrap(),
}
}
pub fn permissive() -> Self {
Self {
relative: F::from(0.01).unwrap(), absolute: F::from(0.01).unwrap(),
}
}
pub fn rel_abs<FF: ToPrimitive>(relative: FF, absolute: FF) -> Self {
let relative = Self::check_relative(relative);
let absolute = Self::check_absolute(absolute);
Self { relative, absolute }
}
pub fn relative<FF: ToPrimitive>(tolerance: FF) -> Self {
let relative = Self::check_relative(tolerance);
Self {
relative,
absolute: F::from(0.0).unwrap(),
}
}
pub fn absolute<FF: ToPrimitive>(tolerance: FF) -> Self {
let absolute = Self::check_absolute(tolerance);
Self {
relative: F::from(0.0).unwrap(),
absolute,
}
}
pub fn set_relative<FF: ToPrimitive>(mut self, tolerance: FF) -> Self {
self.relative = Self::check_relative(tolerance);
self
}
pub fn set_half_precision_relative<FF: ToPrimitive>(mut self, tolerance: FF) -> Self {
if core::mem::size_of::<F>() == 2 {
self.relative = Self::check_relative(tolerance);
}
self
}
pub fn set_single_precision_relative<FF: ToPrimitive>(mut self, tolerance: FF) -> Self {
if core::mem::size_of::<F>() == 4 {
self.relative = Self::check_relative(tolerance);
}
self
}
pub fn set_double_precision_relative<FF: ToPrimitive>(mut self, tolerance: FF) -> Self {
if core::mem::size_of::<F>() == 8 {
self.relative = Self::check_relative(tolerance);
}
self
}
pub fn set_absolute<FF: ToPrimitive>(mut self, tolerance: FF) -> Self {
self.absolute = Self::check_absolute(tolerance);
self
}
pub fn set_half_precision_absolute<FF: ToPrimitive>(mut self, tolerance: FF) -> Self {
if core::mem::size_of::<F>() == 2 {
self.absolute = Self::check_absolute(tolerance);
}
self
}
pub fn set_single_precision_absolute<FF: ToPrimitive>(mut self, tolerance: FF) -> Self {
if core::mem::size_of::<F>() == 4 {
self.absolute = Self::check_absolute(tolerance);
}
self
}
pub fn set_double_precision_absolute<FF: ToPrimitive>(mut self, tolerance: FF) -> Self {
if core::mem::size_of::<F>() == 8 {
self.absolute = Self::check_absolute(tolerance);
}
self
}
pub fn approx_eq(&self, x: F, y: F) -> bool {
if x == y {
return true;
}
let diff = (x - y).abs();
let max = F::max(x.abs(), y.abs());
diff < self.absolute.max(self.relative * max)
}
fn check_relative<FF: ToPrimitive>(tolerance: FF) -> F {
let tolerance = F::from(tolerance).unwrap();
assert!(tolerance <= F::one());
tolerance
}
fn check_absolute<FF: ToPrimitive>(tolerance: FF) -> F {
let tolerance = F::from(tolerance).unwrap();
assert!(tolerance >= F::zero());
tolerance
}
}