use crate::defaults::accuracy; use kissunits::geo::Coordinate; use smallvec::{Array, SmallVec}; use std::{ cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, fmt::Debug, }; #[derive(Clone, Copy, Debug)] pub struct Approx(pub T); impl PartialOrd for Approx<&T> where T: PartialOrd + Copy, Approx: PartialOrd, { fn partial_cmp(&self, other: &Approx<&T>) -> Option { Approx(*self.0).partial_cmp(&Approx(*other.0)) } } impl Ord for Approx<&T> where T: Copy + Ord, Approx: Ord, { fn cmp(&self, other: &Self) -> Ordering { Approx(*self.0).cmp(&Approx(*other.0)) } } impl PartialEq for Approx<&T> where T: Copy + PartialEq, Approx: PartialEq, { fn eq(&self, other: &Approx<&T>) -> bool { Approx(*self.0) == Approx(*other.0) } } impl Eq for Approx<&T> where T: Copy + Eq, Approx: Eq, { } impl PartialOrd for Approx> where T: Copy + PartialOrd, Approx: PartialOrd, { fn partial_cmp(&self, other: &Approx>) -> Option { match (self.0, other.0) { (None, None) => Some(Ordering::Equal), (None, Some(_)) => Some(Ordering::Less), (Some(_), None) => Some(Ordering::Greater), (Some(a), Some(b)) => Approx(a).partial_cmp(&Approx(b)), } } } impl Ord for Approx> where T: Copy + Ord, Approx: Ord, { fn cmp(&self, other: &Self) -> Ordering { match (self.0, other.0) { (None, None) => Ordering::Equal, (None, Some(_)) => Ordering::Less, (Some(_), None) => Ordering::Greater, (Some(a), Some(b)) => Approx(a).cmp(&Approx(b)), } } } impl PartialEq for Approx> where T: Copy + PartialEq, Approx: PartialEq, { fn eq(&self, other: &Approx>) -> bool { match (self.0, other.0) { (None, None) => true, (None, Some(_)) | (Some(_), None) => false, (Some(a), Some(b)) => Approx(a) == Approx(b), } } } impl Eq for Approx> where T: Copy + Eq, Approx: Eq, { } impl PartialOrd for Approx<&[T]> where T: PartialOrd + Copy, Approx: PartialOrd, { fn partial_cmp(&self, other: &Approx<&[T]>) -> Option { let mut iterator = self .0 .iter() .zip(other.0) .map(|(a, b)| Approx(a).partial_cmp(&Approx(b))); let cmp_0 = iterator.next()?; if iterator.any(|cmp_i| cmp_0 != cmp_i) { None } else { cmp_0 } } fn le(&self, other: &Approx<&[T]>) -> bool { for cmp in self .0 .iter() .zip(other.0) .map(|(a, b)| Approx(a).partial_cmp(&Approx(b))) { if cmp == Some(Ordering::Greater) { return false; } } true } fn ge(&self, other: &Approx<&[T]>) -> bool { for cmp in self .0 .iter() .zip(other.0) .map(|(a, b)| Approx(a).partial_cmp(&Approx(b))) { if cmp == Some(Ordering::Less) { return false; } } true } } impl PartialEq for Approx<&[T]> where T: PartialEq + Copy, Approx: PartialEq, { fn eq(&self, other: &Approx<&[T]>) -> bool { self.0 .iter() .zip(other.0) .fold(true, |acc, (&a, &b)| acc && Approx(a) == Approx(b)) } } impl Eq for Approx<&[T]> where T: Eq + Copy, Approx: Eq, { } impl Approx { pub fn approx(&self) -> f64 { (self.0 / accuracy::F64_ABS).round() * accuracy::F64_ABS } } impl PartialOrd for Approx { fn partial_cmp(&self, other: &Approx) -> Option { if self == other { Some(Ordering::Equal) } else { // now: they are at least different by tolerance // -> compare normally self.0.partial_cmp(&other.0) } } } impl Ord for Approx { fn cmp(&self, other: &Self) -> Ordering { self.partial_cmp(other).expect(&format!( "No comparison for {:?} and {:?} possible.", self, other )) } } impl PartialEq for Approx { fn eq(&self, other: &Approx) -> bool { Approx(self.0 - other.0).approx().abs() <= accuracy::F64_ABS } } impl Eq for Approx {} impl Approx { pub fn approx(&self) -> Coordinate { Coordinate { lat: Approx(self.0.lat).approx(), lon: Approx(self.0.lon).approx(), } } } impl PartialEq for Approx { fn eq(&self, other: &Approx) -> bool { Approx(self.0.lat) == Approx(other.0.lat) && Approx(self.0.lon) == Approx(other.0.lon) } } impl Eq for Approx {} impl PartialOrd for Approx<&SmallVec> where T: PartialOrd + Copy, Approx: PartialOrd, A: Array, { fn partial_cmp(&self, other: &Approx<&SmallVec>) -> Option { Approx(&self.0[..]).partial_cmp(&Approx(&other.0[..])) } fn le(&self, other: &Approx<&SmallVec>) -> bool { Approx(&self.0[..]).le(&Approx(&other.0[..])) } fn ge(&self, other: &Approx<&SmallVec>) -> bool { Approx(&self.0[..]).ge(&Approx(&other.0[..])) } } impl PartialEq for Approx<&SmallVec> where T: PartialEq + Copy, Approx: PartialEq, A: Array, { fn eq(&self, other: &Approx<&SmallVec>) -> bool { Approx(&self.0[..]) == Approx(&other.0[..]) } } impl Eq for Approx<&SmallVec> where T: PartialEq + Copy, Approx: PartialEq, A: Array, { }