use core::ops::{
Bound,
Range,
RangeBounds,
};
pub trait RangeExt<T>: RangeBounds<T>
where T: Ord
{
fn normalize(
self,
start: impl Into<Option<T>>,
end: impl Into<Option<T>>,
) -> Range<T>;
fn intersection<R>(self, other: R) -> Option<Range<T>>
where R: RangeExt<T>;
fn union<R>(self, other: R) -> Option<Range<T>>
where R: RangeExt<T>;
}
impl<R> RangeExt<usize> for R
where R: RangeBounds<usize>
{
fn normalize(
self,
start: impl Into<Option<usize>>,
end: impl Into<Option<usize>>,
) -> Range<usize> {
let start = match self.start_bound() {
Bound::Unbounded => start.into().unwrap_or(0),
Bound::Included(&v) => v,
Bound::Excluded(&v) => v.saturating_add(1),
};
let end = match self.end_bound() {
Bound::Unbounded => end.into().unwrap_or(!0),
Bound::Included(&v) => v.saturating_add(1),
Bound::Excluded(&v) => v,
};
if start > end {
end .. start
}
else {
start .. end
}
}
fn intersection<R2>(self, other: R2) -> Option<Range<usize>>
where R2: RangeExt<usize> {
let Range { start: a1, end: a2 } = self.normalize(None, None);
let Range { start: b1, end: b2 } = other.normalize(None, None);
if b1 < a1 {
return (b1 .. b2).intersection(a1 .. a2);
}
if !(a1 .. a2).contains(&b1) {
return None;
}
let start = a1.max(b1);
let end = a2.min(b2);
if start > end {
Some(end .. start)
}
else {
Some(start .. end)
}
}
fn union<R2>(self, other: R2) -> Option<Range<usize>>
where R2: RangeExt<usize> {
let Range { start: a1, end: a2 } = self.normalize(None, None);
let Range { start: b1, end: b2 } = other.normalize(None, None);
if b1 < a1 {
return (b1 .. b2).intersection(a1 .. a2);
}
if !(a1 .. a2).contains(&b1) {
return None;
}
let start = a1.min(b1);
let end = a2.max(b2);
if start > end {
Some(end .. start)
}
else {
Some(start .. end)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalize() {
let r = (..).normalize(1, 10);
assert!(r.contains(&1));
assert!(r.contains(&9));
assert!(!r.contains(&0));
assert!(!r.contains(&10));
let r = (.. 10).normalize(1, 20);
assert!(r.contains(&1));
assert!(r.contains(&9));
assert!(!r.contains(&0));
assert!(!r.contains(&10));
let r = (4 ..).normalize(6, 10);
assert!(r.contains(&4));
assert!(r.contains(&9));
assert!(!r.contains(&3));
assert!(!r.contains(&10));
let r = (4 ..= 10).normalize(6, 8);
assert!(r.contains(&4));
assert!(r.contains(&10));
assert!(!r.contains(&3));
assert!(!r.contains(&11));
let r = (..= 10).normalize(1, 8);
assert!(r.contains(&1));
assert!(r.contains(&10));
assert!(!r.contains(&0));
assert!(!r.contains(&11));
}
#[test]
fn intersect() {
let a = 3 .. 10;
let b = 7 ..= 20;
assert_eq!(a.intersection(b), Some(7 .. 10));
let c = 3 .. 10;
let d = 13 ..= 20;
assert!(c.intersection(d).is_none());
}
#[test]
fn union() {
let a = 3 .. 10;
let b = 7 ..= 20;
assert_eq!(a.union(b), Some(3 .. 21));
let c = 3 .. 10;
let d = 13 ..= 20;
assert!(c.union(d).is_none());
}
}