use core::ops::{Bound, Not};
use crate::{Arrangement, Domain, GenericRange, OperationResult};
impl<T> GenericRange<T> {
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn invert_border(bound: Bound<T>) -> Bound<T> {
match bound {
Bound::Unbounded => Bound::Unbounded,
Bound::Excluded(val) => Bound::Included(val),
Bound::Included(val) => Bound::Excluded(val),
}
}
#[must_use]
pub fn invert_border_cloned(bound: &Bound<T>) -> Bound<T>
where
T: Clone,
{
Self::invert_border(bound.clone())
}
pub fn invert(self) -> OperationResult<T>
where
T: Domain,
{
let domain_range = Self {
start: T::minimum(),
end: T::maximum(),
};
match self.arrangement(&domain_range) {
Arrangement::Disjoint { .. }
| Arrangement::Touching { .. }
| Arrangement::Overlapping { .. }
| Arrangement::Containing { self_shorter: false } => {
unreachable!("constructor guarantees broken: self is outside of domain")
}
Arrangement::Containing { self_shorter: true } => OperationResult::Double(
Self {
start: T::minimum(),
end: Self::invert_border(self.start),
},
Self {
start: Self::invert_border(self.end),
end: T::maximum(),
},
),
Arrangement::Starting { .. } => OperationResult::Single(Self {
start: Self::invert_border(self.end),
end: T::maximum(),
}),
Arrangement::Ending { .. } => OperationResult::Single(Self {
start: T::minimum(),
end: Self::invert_border(self.start),
}),
Arrangement::Equal => OperationResult::Empty,
Arrangement::Empty { self_empty: Some(true) } => {
OperationResult::Single(domain_range)
}
#[allow(clippy::panic)]
Arrangement::Empty {
self_empty: Some(false),
}
| Arrangement::Empty { self_empty: None } => panic!("domain is empty, which makes inversion impossible"),
}
}
}
impl<T: Domain> Not for GenericRange<T> {
type Output = OperationResult<T>;
#[must_use]
fn not(self) -> Self::Output {
self.invert()
}
}
#[cfg(test)]
mod tests {
use core::ops::Bound;
use crate::{GenericRange, OperationResult};
#[test]
fn invert_border() {
assert_eq!(GenericRange::invert_border(Bound::<usize>::Unbounded), Bound::Unbounded);
assert_eq!(GenericRange::invert_border(Bound::Excluded(42)), Bound::Included(42));
assert_eq!(GenericRange::invert_border(Bound::Included(42)), Bound::Excluded(42));
}
#[test]
fn invert() {
assert_eq!(GenericRange::<usize>::full().invert(), OperationResult::<usize>::Empty);
assert_eq!(
GenericRange::new_at_most(5).invert(),
OperationResult::Single(GenericRange::new_greater_than(5))
);
assert_eq!(
GenericRange::new_at_least(42).invert(),
OperationResult::Single(GenericRange::new_less_than(42))
);
assert_eq!(
GenericRange::from(5..42).invert(),
OperationResult::Double(GenericRange::new_less_than(5), GenericRange::new_at_least(42))
)
}
#[test]
fn invert_via_not() {
assert_eq!(!GenericRange::<usize>::full(), OperationResult::<usize>::Empty);
assert_eq!(
!GenericRange::new_at_most(5),
OperationResult::Single(GenericRange::new_greater_than(5))
);
assert_eq!(
!GenericRange::new_at_least(42),
OperationResult::Single(GenericRange::new_less_than(42))
);
assert_eq!(
!GenericRange::from(5..42),
OperationResult::Double(GenericRange::new_less_than(5), GenericRange::new_at_least(42))
)
}
}