use core::cmp::Ordering;
use core::convert::Infallible;
use crate::{with_primitives, Primitive, ToCanonical};
pub trait CanonicalEq {
fn eq_canonical(&self, other: &Self) -> bool;
}
impl<T> CanonicalEq for T
where
T: ToCanonical,
{
fn eq_canonical(&self, other: &Self) -> bool {
self.to_canonical() == other.to_canonical()
}
}
impl<T, const N: usize> CanonicalEq for [T; N]
where
T: CanonicalEq,
{
fn eq_canonical(&self, other: &Self) -> bool {
self.iter()
.zip(other.iter())
.all(|(a, b)| a.eq_canonical(b))
}
}
impl<T> CanonicalEq for [T]
where
T: CanonicalEq,
{
fn eq_canonical(&self, other: &Self) -> bool {
if self.len() == other.len() {
self.iter()
.zip(other.iter())
.all(|(a, b)| a.eq_canonical(b))
}
else {
false
}
}
}
pub trait CanonicalOrd {
fn cmp_canonical(&self, other: &Self) -> Ordering;
}
impl<T> CanonicalOrd for T
where
T: Primitive,
{
fn cmp_canonical(&self, other: &Self) -> Ordering {
match self.partial_cmp(other) {
Some(ordering) => ordering,
None => {
if self.is_nan() {
if other.is_nan() {
Ordering::Equal
}
else {
Ordering::Greater
}
}
else {
Ordering::Less
}
}
}
}
}
impl<T> CanonicalOrd for [T]
where
T: CanonicalOrd,
{
fn cmp_canonical(&self, other: &Self) -> Ordering {
match self
.iter()
.zip(other.iter())
.map(|(a, b)| a.cmp_canonical(b))
.find(|ordering| *ordering != Ordering::Equal)
{
Some(ordering) => ordering,
None => self.len().cmp(&other.len()),
}
}
}
pub trait EmptyInhabitant {
fn empty() -> Self;
}
impl EmptyInhabitant for () {
fn empty() -> Self {}
}
impl<T> EmptyInhabitant for Option<T> {
#[inline(always)]
fn empty() -> Self {
None
}
}
impl<T, E> EmptyInhabitant for Result<T, E>
where
E: EmptyInhabitant,
{
#[inline(always)]
fn empty() -> Self {
Err(E::empty())
}
}
pub trait EmptyOrd: PartialOrd {
type Empty;
fn from_empty(empty: Self::Empty) -> Self;
fn is_empty(&self) -> bool;
fn cmp_empty(&self, other: &Self) -> Result<Ordering, Self::Empty>;
}
impl<T> EmptyOrd for Option<T>
where
T: Ord,
{
type Empty = Self;
#[inline(always)]
fn from_empty(empty: <Self as EmptyOrd>::Empty) -> Self {
empty
}
fn is_empty(&self) -> bool {
self.is_none()
}
fn cmp_empty(&self, other: &Self) -> Result<Ordering, Self::Empty> {
self.as_ref()
.zip(other.as_ref())
.map_or_else(|| Err(EmptyInhabitant::empty()), |(a, b)| Ok(a.cmp(b)))
}
}
impl<T, E> EmptyOrd for Result<T, E>
where
Self: PartialOrd,
T: Ord,
E: EmptyInhabitant,
{
type Empty = Self;
#[inline(always)]
fn from_empty(empty: Self::Empty) -> Self {
empty
}
fn is_empty(&self) -> bool {
self.is_err()
}
fn cmp_empty(&self, other: &Self) -> Result<Ordering, Self::Empty> {
match (self.as_ref(), other.as_ref()) {
(Ok(a), Ok(b)) => Ok(a.cmp(b)),
_ => Err(EmptyInhabitant::empty()),
}
}
}
macro_rules! impl_empty_ord_for_float_primitive {
() => {
with_primitives!(impl_empty_ord_for_float_primitive);
};
(primitive => $t:ty) => {
impl EmptyOrd for $t {
type Empty = Self;
#[inline(always)]
fn from_empty(empty: Self::Empty) -> Self {
empty
}
fn is_empty(&self) -> bool {
self.is_nan()
}
fn cmp_empty(&self, other: &Self) -> Result<Ordering, Self::Empty> {
self.partial_cmp(other)
.ok_or_else(|| EmptyInhabitant::empty())
}
}
};
}
impl_empty_ord_for_float_primitive!();
macro_rules! impl_empty_ord_for_total_primitive {
() => {
impl_empty_ord_for_total_primitive!(primitive => isize);
impl_empty_ord_for_total_primitive!(primitive => i8);
impl_empty_ord_for_total_primitive!(primitive => i16);
impl_empty_ord_for_total_primitive!(primitive => i32);
impl_empty_ord_for_total_primitive!(primitive => i64);
impl_empty_ord_for_total_primitive!(primitive => i128);
impl_empty_ord_for_total_primitive!(primitive => usize);
impl_empty_ord_for_total_primitive!(primitive => u8);
impl_empty_ord_for_total_primitive!(primitive => u16);
impl_empty_ord_for_total_primitive!(primitive => u32);
impl_empty_ord_for_total_primitive!(primitive => u64);
impl_empty_ord_for_total_primitive!(primitive => u128);
};
(primitive => $t:ty) => {
impl EmptyOrd for $t {
type Empty = Infallible;
fn from_empty(_: Self::Empty) -> Self {
unreachable!()
}
#[inline(always)]
fn is_empty(&self) -> bool {
false
}
#[inline(always)]
fn cmp_empty(&self, other: &Self) -> Result<Ordering, Self::Empty> {
Ok(self.cmp(other))
}
}
};
}
impl_empty_ord_for_total_primitive!();
macro_rules! impl_empty_inhabitant_for_float_primitive {
() => {
with_primitives!(impl_empty_inhabitant_for_float_primitive);
};
(primitive => $t:ty) => {
impl EmptyInhabitant for $t {
#[inline(always)]
fn empty() -> Self {
Self::NAN
}
}
};
}
impl_empty_inhabitant_for_float_primitive!();
pub fn max_or_empty<T>(a: T, b: T) -> T
where
T: EmptyOrd,
{
match a.cmp_empty(&b) {
Ok(Ordering::Less | Ordering::Equal) => b,
Ok(Ordering::Greater) => a,
Err(empty) => T::from_empty(empty),
}
}
pub fn min_or_empty<T>(a: T, b: T) -> T
where
T: EmptyOrd,
{
match a.cmp_empty(&b) {
Ok(Ordering::Less | Ordering::Equal) => a,
Ok(Ordering::Greater) => b,
Err(empty) => T::from_empty(empty),
}
}
pub fn min_max_or_empty<T>(a: T, b: T) -> (T, T)
where
T: EmptyOrd,
T::Empty: Copy,
{
match a.cmp_empty(&b) {
Ok(Ordering::Less | Ordering::Equal) => (a, b),
Ok(Ordering::Greater) => (b, a),
Err(empty) => (T::from_empty(empty), T::from_empty(empty)),
}
}
#[cfg(test)]
mod tests {
use num_traits::{One, Zero};
use crate::cmp::{self, CanonicalEq, EmptyOrd};
use crate::{NanEncoding, Total};
#[test]
#[allow(clippy::eq_op)]
#[allow(clippy::zero_divided_by_zero)]
fn primitive_eq() {
let x = 0.0f64 / 0.0f64; let y = f64::INFINITY + f64::NEG_INFINITY; let xs = [1.0f64, f64::NAN, f64::INFINITY];
let ys = [1.0f64, f64::NAN, f64::INFINITY];
assert!(x.eq_canonical(&y));
assert!(xs.eq_canonical(&ys));
}
#[test]
fn empty_ord_option() {
let zero = Some(0u64);
let one = Some(1u64);
assert_eq!(zero, cmp::min_or_empty(zero, one));
assert_eq!(one, cmp::max_or_empty(zero, one));
assert!(cmp::min_or_empty(None, zero).is_empty());
}
#[test]
#[allow(clippy::float_cmp)]
fn empty_ord_primitive() {
let zero = 0.0f64;
let one = 1.0f64;
assert_eq!(zero, cmp::min_or_empty(zero, one));
assert_eq!(one, cmp::max_or_empty(zero, one));
assert!(cmp::min_or_empty(f64::NAN, zero).is_empty());
}
#[test]
fn empty_ord_proxy() {
let nan = Total::<f64>::NAN;
let zero = Total::zero();
let one = Total::one();
assert_eq!((zero, one), cmp::min_max_or_empty(zero, one));
assert_eq!((zero, one), cmp::min_max_or_empty(one, zero));
assert_eq!((nan, nan), cmp::min_max_or_empty(nan, zero));
assert_eq!((nan, nan), cmp::min_max_or_empty(zero, nan));
assert_eq!((nan, nan), cmp::min_max_or_empty(nan, nan));
assert_eq!(nan, cmp::min_or_empty(nan, zero));
assert_eq!(nan, cmp::max_or_empty(nan, zero));
assert_eq!(nan, cmp::min_or_empty(nan, nan));
assert_eq!(nan, cmp::max_or_empty(nan, nan));
}
}