use std::cmp::Ordering;
use std::ops::Bound;
use crate::error::RangeBoundError;
use crate::upper::UpperBound;
#[derive(Debug, Clone, Copy, Hash)]
pub struct LowerBound<T>(pub Bound<T>);
impl<T> PartialEq<Self> for LowerBound<T>
where
LowerBound<T>: Ord,
{
fn eq(&self, other: &Self) -> bool {
self.cmp(other).is_eq()
}
}
impl<T> Eq for LowerBound<T> where LowerBound<T>: Ord {}
impl<T> PartialOrd for LowerBound<T>
where
LowerBound<T>: Ord,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
macro_rules! impl_try_from {
(
$ty:ty
) => {
impl TryFrom<UpperBound<$ty>> for LowerBound<$ty> {
type Error = RangeBoundError;
fn try_from(value: UpperBound<$ty>) -> Result<Self, Self::Error> {
let bound = match value.0 {
Bound::Included(a) => Ok(Bound::Included(a)),
Bound::Excluded(a) => Ok(Bound::Excluded(a - 2)),
Bound::Unbounded => Err(Self::Error::UpperBoundConversionError),
};
bound.map(|bound| LowerBound(bound))
}
}
};
}
impl_try_from!(i8);
impl_try_from!(u8);
impl_try_from!(i16);
impl_try_from!(u16);
impl_try_from!(i32);
impl_try_from!(u32);
impl_try_from!(i64);
impl_try_from!(u64);
impl_try_from!(isize);
impl_try_from!(usize);
macro_rules! impl_lower_ord {
(
$ty:ty
) => {
impl Ord for LowerBound<$ty> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (&self.0, &other.0) {
(Bound::Included(a), Bound::Included(b)) => a.cmp(&b),
(Bound::Included(a), Bound::Excluded(b)) => a.cmp(&(b + 1)),
(Bound::Excluded(a), Bound::Included(b)) => (a + 1).cmp(&b),
(Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
(Bound::Included(_), Bound::Unbounded) => Ordering::Greater,
(Bound::Excluded(_), Bound::Unbounded) => Ordering::Greater,
(Bound::Unbounded, Bound::Included(_)) => Ordering::Less,
(Bound::Unbounded, Bound::Excluded(_)) => Ordering::Less,
(Bound::Unbounded, Bound::Unbounded) => Ordering::Equal,
}
}
}
};
}
impl_lower_ord!(i8);
impl_lower_ord!(u8);
impl_lower_ord!(i16);
impl_lower_ord!(u16);
impl_lower_ord!(i32);
impl_lower_ord!(u32);
impl_lower_ord!(i64);
impl_lower_ord!(u64);
impl_lower_ord!(isize);
impl_lower_ord!(usize);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cmp() {
let inc_0 = [
LowerBound(Bound::Included(0)),
LowerBound(Bound::Excluded(-1)),
];
let inc_1 = [
LowerBound(Bound::Included(1)),
LowerBound(Bound::Excluded(0)),
];
let unb = [LowerBound(Bound::Unbounded)];
assert!(inc_0.iter().all(|a| inc_0.iter().all(|b| a == b)));
assert!(inc_0.iter().all(|a| inc_1.iter().all(|b| a < b)));
assert!(inc_1.iter().all(|a| unb.iter().all(|b| a > b)));
}
#[test]
fn test_try_from() {
let inc = [
UpperBound(Bound::Included(9)),
UpperBound(Bound::Excluded(10)),
];
let unb = UpperBound(Bound::Unbounded);
assert!(
inc.into_iter()
.map(|a| LowerBound::try_from(a))
.all(|a| dbg!(a).is_ok_and(|bound| bound == LowerBound(Bound::Included(9))))
);
assert!(
dbg!(LowerBound::<i32>::try_from(unb))
.is_err_and(|err| err == RangeBoundError::UpperBoundConversionError)
);
}
}