use std::cmp::Ordering;
use crate::numeric::Domain;
use crate::{Bound, Bounding, MaybeEmpty, Side};
use super::{BoundCase, Finite, HalfBounded};
fn non_empty_cmp_side<T: PartialOrd>(
side: Side,
left: Option<&Bound<T>>,
right: Option<&Bound<T>>,
) -> std::cmp::Ordering {
match (left, right) {
(None, None) => Ordering::Equal,
(None, Some(right)) => match side {
Side::Left => Ordering::Less,
Side::Right => Ordering::Greater,
},
(Some(left), None) => match side {
Side::Left => Ordering::Greater,
Side::Right => Ordering::Less,
},
(Some(left), Some(right)) => {
if left == right {
return Ordering::Equal;
}
match side {
Side::Left => {
if left.contains(side, right.value()) {
Ordering::Less
} else {
Ordering::Greater
}
}
Side::Right => {
if left.contains(side, right.value()) {
Ordering::Greater
} else {
Ordering::Less
}
}
}
}
}
}
fn impl_cmp<U, T>(lhs: &U, rhs: &U) -> std::cmp::Ordering
where
T: Clone + PartialOrd,
U: Bounding<T>,
{
match non_empty_cmp_side(Side::Left, lhs.left(), rhs.left()) {
Ordering::Equal => non_empty_cmp_side(Side::Right, lhs.right(), rhs.right()),
ordering => ordering,
}
}
fn impl_partial_cmp<U, T>(lhs: &U, rhs: &U) -> Option<std::cmp::Ordering>
where
T: Clone + PartialOrd,
U: Bounding<T> + MaybeEmpty,
{
if lhs.is_empty() || rhs.is_empty() {
return None;
}
impl_cmp(lhs, rhs).into()
}
impl<T: Domain> PartialOrd for BoundCase<T> {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
impl_partial_cmp(self, rhs)
}
}
impl<T: Domain> PartialOrd for HalfBounded<T> {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
impl_cmp(self, rhs).into()
}
}
impl<T: Domain> PartialOrd for Finite<T> {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
impl_partial_cmp(self, rhs)
}
}
fn impl_total_ord_cmp<T, S>(lhs: &S, rhs: &S) -> std::cmp::Ordering
where
T: Domain + Eq + Ord,
S: Bounding<T> + MaybeEmpty,
{
match (lhs.is_empty(), rhs.is_empty()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
(false, false) => impl_cmp(lhs, rhs),
}
}
impl<T: Domain + Eq> Eq for Finite<T> {}
impl<T: Domain + Ord> Ord for Finite<T> {
fn cmp(&self, other: &Self) -> Ordering {
impl_total_ord_cmp(self, other)
}
}
impl<T: Domain + Eq> Eq for HalfBounded<T> {}
impl<T: Domain + Ord> Ord for HalfBounded<T> {
fn cmp(&self, other: &Self) -> Ordering {
impl_cmp(self, other)
}
}
impl<T: Domain + Eq> Eq for BoundCase<T> {}
impl<T: Domain + Ord> Ord for BoundCase<T> {
fn cmp(&self, other: &Self) -> Ordering {
impl_total_ord_cmp(self, other)
}
}
#[cfg(test)]
mod tests {
use super::*;
use itertools::Itertools;
#[quickcheck]
fn test_finite_ordering(a: f32, b: f32, c: f32, d: f32) {
if a.is_nan() || b.is_nan() || c.is_nan() || d.is_nan() {
return;
}
let mut p = vec![a, b, c, d];
p.sort_by(|a, b| a.partial_cmp(b).unwrap());
if p.iter().dedup().count() == 1 {
return;
}
let x = Finite::new(Bound::closed(p[0]), Bound::closed(p[2]));
let y = Finite::new(Bound::closed(p[1]), Bound::closed(p[3]));
assert!(x < y);
assert!(y > x);
}
}