use std::cmp::Ordering;
use crate::Precision;
pub use crate::hash::{ApproxHash, ApproxHasher};
pub trait ApproxEq: std::fmt::Debug {
fn approx_eq(&self, other: &Self, prec: Precision) -> bool;
}
impl ApproxEq for f64 {
fn approx_eq(&self, other: &Self, prec: Precision) -> bool {
prec.f64_eq(*self, *other)
}
}
impl ApproxEq for f32 {
fn approx_eq(&self, other: &Self, prec: Precision) -> bool {
prec.f32_eq(*self, *other)
}
}
impl<T: ApproxEq> ApproxEq for [T] {
fn approx_eq(&self, other: &Self, prec: Precision) -> bool {
self.len() == other.len() && std::iter::zip(self, other).all(|(a, b)| a.approx_eq(b, prec))
}
}
impl<T: ApproxEq, const N: usize> ApproxEq for [T; N] {
fn approx_eq(&self, other: &Self, prec: Precision) -> bool {
std::iter::zip(self, other).all(|(a, b)| a.approx_eq(b, prec))
}
}
impl<T: ApproxEq> ApproxEq for &T {
fn approx_eq(&self, other: &Self, prec: Precision) -> bool {
T::approx_eq(self, other, prec)
}
}
pub trait ApproxEqZero {
fn approx_eq_zero(&self, prec: Precision) -> bool;
}
impl ApproxEqZero for f64 {
fn approx_eq_zero(&self, prec: Precision) -> bool {
prec.f64_eq_zero(*self)
}
}
impl ApproxEqZero for f32 {
fn approx_eq_zero(&self, prec: Precision) -> bool {
prec.f64_eq_zero(*self as f64)
}
}
impl<T: ApproxEqZero> ApproxEqZero for [T] {
fn approx_eq_zero(&self, prec: Precision) -> bool {
self.iter().all(|x| x.approx_eq_zero(prec))
}
}
impl<T: ApproxEqZero, const N: usize> ApproxEqZero for [T; N] {
fn approx_eq_zero(&self, prec: Precision) -> bool {
self.iter().all(|x| x.approx_eq_zero(prec))
}
}
impl<T: ApproxEqZero> ApproxEqZero for &T {
fn approx_eq_zero(&self, prec: Precision) -> bool {
T::approx_eq_zero(self, prec)
}
}
pub trait ApproxOrd: ApproxEq {
fn approx_cmp(&self, other: &Self, prec: Precision) -> Ordering;
}
impl ApproxOrd for f64 {
fn approx_cmp(&self, other: &Self, prec: Precision) -> Ordering {
match self.approx_eq(other, prec) {
true => Ordering::Equal,
false => self.total_cmp(other),
}
}
}
impl ApproxOrd for f32 {
fn approx_cmp(&self, other: &Self, prec: Precision) -> Ordering {
match self.approx_eq(other, prec) {
true => Ordering::Equal,
false => self.total_cmp(other),
}
}
}
impl<T: ApproxOrd> ApproxOrd for [T] {
fn approx_cmp(&self, other: &Self, prec: Precision) -> Ordering {
std::iter::zip(self, other)
.map(|(a, b)| a.approx_cmp(b, prec))
.find(|&ord| ord != Ordering::Equal)
.unwrap_or_else(|| self.len().cmp(&other.len()))
}
}
impl<T: ApproxOrd, const N: usize> ApproxOrd for [T; N] {
fn approx_cmp(&self, other: &Self, prec: Precision) -> Ordering {
std::iter::zip(self, other)
.map(|(a, b)| a.approx_cmp(b, prec))
.find(|&ord| ord != Ordering::Equal)
.unwrap_or(Ordering::Equal)
}
}
impl<T: ApproxOrd> ApproxOrd for &T {
fn approx_cmp(&self, other: &Self, prec: Precision) -> Ordering {
T::approx_cmp(self, other, prec)
}
}
pub trait VisitFloats {
fn visit_floats(&self, f: impl FnMut(&f64));
fn visit_floats_mut(&mut self, f: impl FnMut(&mut f64));
}
impl VisitFloats for f64 {
fn visit_floats(&self, mut f: impl FnMut(&f64)) {
f(self)
}
fn visit_floats_mut(&mut self, mut f: impl FnMut(&mut f64)) {
f(self)
}
}
impl VisitFloats for f32 {
fn visit_floats(&self, mut f: impl FnMut(&f64)) {
let x = *self as f64;
f(&x);
}
fn visit_floats_mut(&mut self, mut f: impl FnMut(&mut f64)) {
let mut x = *self as f64;
f(&mut x);
*self = x as f32;
}
}
impl<T: VisitFloats> VisitFloats for [T] {
fn visit_floats(&self, mut f: impl FnMut(&f64)) {
self.iter().for_each(|x| x.visit_floats(&mut f));
}
fn visit_floats_mut(&mut self, mut f: impl FnMut(&mut f64)) {
self.iter_mut().for_each(|x| x.visit_floats_mut(&mut f));
}
}
impl<T: VisitFloats, const N: usize> VisitFloats for [T; N] {
fn visit_floats(&self, mut f: impl FnMut(&f64)) {
self.iter().for_each(|x| x.visit_floats(&mut f));
}
fn visit_floats_mut(&mut self, mut f: impl FnMut(&mut f64)) {
self.iter_mut().for_each(|x| x.visit_floats_mut(&mut f));
}
}