use {
core::{
cmp::Ordering,
hash::Hash,
iter,
ops,
},
facet::Facet,
};
pub trait Scalar {}
impl Scalar for f32 {}
impl Scalar for f64 {}
impl Scalar for i8 {}
impl Scalar for i16 {}
impl Scalar for i32 {}
impl Scalar for i64 {}
impl Scalar for i128 {}
impl Scalar for isize {}
pub trait EqReport<T: ?Sized> {
type Report;
fn eq_report(
&self,
lhs: &T,
rhs: &T,
) -> Result<(), Mismatch<Self::Report>>;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Facet)]
#[repr(C)]
pub struct AbsTol<T>(pub T);
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Facet)]
pub struct Exact;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Facet)]
#[repr(C)]
pub struct RelTol<T>(pub T);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Facet)]
#[repr(C)]
pub struct AbsRelTol<T>(pub T, pub T);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Facet)]
pub struct ReportAt<I, R> {
pub loc: I,
pub report: R,
}
use crate::Box;
impl<T, Cmp: EqReport<T>> EqReport<T> for &Cmp {
type Report = Cmp::Report;
fn eq_report(
&self,
lhs: &T,
rhs: &T,
) -> Result<(), Mismatch<Self::Report>> {
Cmp::eq_report(*self, lhs, rhs)
}
}
impl<T, Cmp: EqReport<T>> EqReport<[T]> for Box<Cmp> {
type Report = Vec<ReportAt<usize, Mismatch<<Cmp as EqReport<T>>::Report>>>;
fn eq_report(
&self,
lhs: &[T],
rhs: &[T],
) -> Result<(), Mismatch<Self::Report>> {
let mut v = alloc::vec![];
for (loc, (l, r)) in iter::zip(lhs, rhs).enumerate() {
match self.0.eq_report(l, r) {
Ok(()) => {},
Err(report) => {
if v.len() + 1 >= lhs.len() / 2 {
v.reserve_exact(lhs.len());
}
v.push(ReportAt { loc, report });
},
}
}
for loc in Ord::min(lhs.len(), rhs.len())..Ord::max(lhs.len(), rhs.len()) {
v.push(ReportAt {
loc,
report: Mismatch::Unrelated,
})
}
if v.is_empty() {
Ok(())
} else {
Err(Mismatch::Report(v))
}
}
}
impl<T, Cmp: EqReport<T>> EqReport<Vec<T>> for Box<Cmp> {
type Report = <Self as EqReport<[T]>>::Report;
fn eq_report(
&self,
lhs: &Vec<T>,
rhs: &Vec<T>,
) -> Result<(), Mismatch<Self::Report>> {
<Self as EqReport<[T]>>::eq_report(self, &**lhs, &**rhs)
}
}
impl<T, Cmp: EqReport<T>> EqReport<alloc::boxed::Box<[T]>> for Box<Cmp> {
type Report = <Self as EqReport<[T]>>::Report;
fn eq_report(
&self,
lhs: &alloc::boxed::Box<[T]>,
rhs: &alloc::boxed::Box<[T]>,
) -> Result<(), Mismatch<Self::Report>> {
<Self as EqReport<[T]>>::eq_report(self, &**lhs, &**rhs)
}
}
impl<K: Ord + Clone, V, Cmp: EqReport<V>> EqReport<alloc::collections::BTreeMap<K, V>>
for Box<Cmp>
{
type Report = Vec<ReportAt<K, Mismatch<<Cmp as EqReport<V>>::Report>>>;
fn eq_report(
&self,
lhs: &alloc::collections::BTreeMap<K, V>,
rhs: &alloc::collections::BTreeMap<K, V>,
) -> Result<(), Mismatch<Self::Report>> {
let mut seen = alloc::collections::BTreeSet::new();
let mut v = Vec::new();
for (k, lhs) in lhs.iter() {
seen.insert(k);
if let Some(rhs) = rhs.get(k) {
if let Err(report) = self.0.eq_report(lhs, rhs) {
v.push(ReportAt {
loc: k.clone(),
report,
});
}
} else {
v.push(ReportAt {
loc: k.clone(),
report: Mismatch::Unrelated,
})
}
}
for (k, rhs) in rhs.iter() {
if seen.contains(k) {
continue;
}
if let Some(lhs) = lhs.get(k) {
if let Err(report) = self.0.eq_report(lhs, rhs) {
v.push(ReportAt {
loc: k.clone(),
report,
});
}
} else {
v.push(ReportAt {
loc: k.clone(),
report: Mismatch::Unrelated,
})
}
}
if v.is_empty() {
Ok(())
} else {
Err(Mismatch::Report(v))
}
}
}
#[cfg(feature = "std")]
impl<K: Hash + Eq + Clone, V, S: core::hash::BuildHasher, Cmp: EqReport<V>>
EqReport<std::collections::HashMap<K, V, S>> for Box<Cmp>
{
type Report = Vec<ReportAt<K, Mismatch<<Cmp as EqReport<V>>::Report>>>;
fn eq_report(
&self,
lhs: &std::collections::HashMap<K, V, S>,
rhs: &std::collections::HashMap<K, V, S>,
) -> Result<(), Mismatch<Self::Report>> {
let mut seen = std::collections::HashSet::new();
let mut v = Vec::new();
for (k, lhs) in lhs.iter() {
seen.insert(k);
if let Some(rhs) = rhs.get(k) {
if let Err(report) = self.0.eq_report(lhs, rhs) {
v.push(ReportAt {
loc: k.clone(),
report,
});
}
} else {
v.push(ReportAt {
loc: k.clone(),
report: Mismatch::Unrelated,
})
}
}
for (k, rhs) in rhs.iter() {
if seen.contains(k) {
continue;
}
if let Some(lhs) = lhs.get(k) {
if let Err(report) = self.0.eq_report(lhs, rhs) {
v.push(ReportAt {
loc: k.clone(),
report,
});
}
} else {
v.push(ReportAt {
loc: k.clone(),
report: Mismatch::Unrelated,
})
}
}
if v.is_empty() {
Ok(())
} else {
Err(Mismatch::Report(v))
}
}
}
fn cmp_early(cmp0: Option<Ordering>) -> Option<Result<(), Mismatch>> {
match cmp0 {
None => Some(Err(Mismatch::Unrelated)),
Some(Ordering::Equal) => Some(Ok(())),
_ => None,
}
}
fn cmp_late(
cmp0: Option<Ordering>,
cmp1: Option<Ordering>,
) -> Result<(), Mismatch> {
match (cmp0, cmp1) {
(Some(Ordering::Less), Some(Ordering::Less))
| (Some(Ordering::Greater), Some(Ordering::Greater))
| (Some(Ordering::Equal), Some(_))
| (Some(_), Some(Ordering::Equal)) => Ok(()),
_ => Err(Mismatch::Unrelated),
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Facet)]
pub struct Never {}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Facet)]
#[repr(u8)]
pub enum Mismatch<R = Never> {
Unrelated,
Report(R),
}
impl<T> EqReport<T> for AbsTol<T>
where
for<'a> &'a T: ops::Sub<&'a T, Output = T>,
T: Scalar + Clone + PartialOrd + ops::Neg<Output = T>,
{
type Report = Never;
fn eq_report(
&self,
lhs: &T,
rhs: &T,
) -> Result<(), Mismatch<Self::Report>> {
let diff = lhs - rhs;
let tol = &self.0;
let cmp0 = diff.partial_cmp(tol);
if let Some(ret) = cmp_early(cmp0) {
return match ret {
Ok(()) => Ok(()),
Err(_) => Err(Mismatch::Unrelated),
};
}
let cmp1 = (-diff).partial_cmp(tol);
match cmp_late(cmp0, cmp1) {
Ok(()) => Ok(()),
Err(_) => Err(Mismatch::Unrelated),
}
}
}
impl<T: PartialEq> EqReport<T> for Exact {
type Report = Never;
fn eq_report(
&self,
lhs: &T,
rhs: &T,
) -> Result<(), Mismatch<Self::Report>> {
if lhs == rhs {
Ok(())
} else {
Err(Mismatch::Unrelated)
}
}
}
impl<T> EqReport<T> for RelTol<T>
where
for<'a> &'a T: ops::Sub<&'a T, Output = T> + ops::Mul<&'a T, Output = T>,
T: Scalar + Clone + PartialOrd + ops::Neg<Output = T>,
{
type Report = Never;
fn eq_report(
&self,
lhs: &T,
rhs: &T,
) -> Result<(), Mismatch<Self::Report>> {
let diff = lhs - rhs;
let tol_lhs = lhs * &self.0;
let cmp0_lhs = diff.partial_cmp(&tol_lhs);
let cmp_early_lhs = cmp_early(cmp0_lhs);
if let Some(Ok(())) = cmp_early_lhs {
return Ok(());
}
let tol_rhs = rhs * &self.0;
let cmp0_rhs = diff.partial_cmp(&tol_rhs);
let cmp_early_rhs = cmp_early(cmp0_rhs);
if let Some(Ok(())) = cmp_early_rhs {
return Ok(());
}
if matches!((cmp_early_lhs, cmp_early_rhs), (Some(Err(_)), Some(Err(_)))) {
return Err(Mismatch::Unrelated);
}
let diff = -diff;
let cmp1_lhs = diff.partial_cmp(&tol_lhs);
if let Ok(()) = cmp_late(cmp0_lhs, cmp1_lhs) {
return Ok(());
}
let cmp1_rhs = diff.partial_cmp(&tol_rhs);
if let Ok(()) = cmp_late(cmp0_rhs, cmp1_rhs) {
return Ok(());
}
Err(Mismatch::Unrelated)
}
}
impl<T> EqReport<T> for AbsRelTol<T>
where
for<'a> &'a T: ops::Sub<&'a T, Output = T> + ops::Mul<&'a T, Output = T>,
T: Scalar + Clone + PartialOrd + ops::Neg<Output = T>,
{
type Report = Never;
fn eq_report(
&self,
lhs: &T,
rhs: &T,
) -> Result<(), Mismatch<Self::Report>> {
let diff = lhs - rhs;
let tol = &self.0;
let cmp0 = diff.partial_cmp(tol);
if let Some(Ok(())) = cmp_early(cmp0) {
return Ok(());
}
let ref tol_lhs = lhs * &self.1;
let cmp0_lhs = diff.partial_cmp(tol_lhs);
if let Some(Ok(())) = cmp_early(cmp0_lhs) {
return Ok(());
}
let ref tol_rhs = rhs * &self.1;
let cmp0_rhs = diff.partial_cmp(tol_rhs);
if let Some(Ok(())) = cmp_early(cmp0_rhs) {
return Ok(());
}
if matches!(
(cmp_early(cmp0), cmp_early(cmp0_lhs), cmp_early(cmp0_rhs)),
(Some(Err(_)), Some(Err(_)), Some(Err(_)))
) {
return Err(Mismatch::Unrelated);
}
let diff = -diff;
let cmp1 = diff.partial_cmp(tol);
if let Ok(()) = cmp_late(cmp0, cmp1) {
return Ok(());
}
let cmp1_lhs = diff.partial_cmp(tol_lhs);
if let Ok(()) = cmp_late(cmp0_lhs, cmp1_lhs) {
return Ok(());
}
let cmp1_rhs = diff.partial_cmp(tol_rhs);
if let Ok(()) = cmp_late(cmp0_rhs, cmp1_rhs) {
return Ok(());
}
Err(Mismatch::Unrelated)
}
}