Compare commits

...

2 commits

2 changed files with 211 additions and 1 deletions

View file

@ -2,4 +2,62 @@
mod section_range;
use std::mem;
pub use section_range::{EmptyRange, InvalidSectionString, SectionRange};
pub enum UpToTwo<T> {
Zero,
One(T),
Two(T, T),
}
impl<T> UpToTwo<T> {
pub fn is_empty(&self) -> bool {
matches!(self, UpToTwo::Zero)
}
pub fn first(&self) -> Option<&T> {
match self {
UpToTwo::Zero => None,
UpToTwo::One(x) | UpToTwo::Two(x, _) => Some(x),
}
}
pub fn second(&self) -> Option<&T> {
match self {
UpToTwo::Zero | UpToTwo::One(_) => None,
UpToTwo::Two(_, x) => Some(x),
}
}
}
impl<T> IntoIterator for UpToTwo<T> {
type Item = T;
type IntoIter = UpToTwoIter<T>;
fn into_iter(self) -> Self::IntoIter {
UpToTwoIter(self)
}
}
pub struct UpToTwoIter<T>(UpToTwo<T>);
impl<T> Iterator for UpToTwoIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
let mut content = UpToTwo::Zero;
mem::swap(&mut self.0, &mut content);
let (ret, mut content) = match content {
UpToTwo::Zero => (None, UpToTwo::Zero),
UpToTwo::One(x) => (Some(x), UpToTwo::Zero),
UpToTwo::Two(x, y) => (Some(x), UpToTwo::One(y)),
};
mem::swap(&mut self.0, &mut content);
ret
}
}

View file

@ -2,10 +2,12 @@ use std::{
error::Error,
fmt,
num::NonZeroU64,
ops::{BitAnd, RangeInclusive},
ops::{BitAnd, BitOr, RangeInclusive, Sub},
str::FromStr,
};
use crate::UpToTwo;
/// Error returned when an attempt is made to construct an empty `SectionRange`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EmptyRange;
@ -131,6 +133,65 @@ impl BitAnd for SectionRange {
}
}
impl BitOr for SectionRange {
type Output = UpToTwo<Self>;
fn bitor(self, other: Self) -> Self::Output {
let first_start = u64::min(self.start, other.start);
let first_end = u64::min(self.end, other.end);
let second_start = u64::max(self.start, other.start);
let second_end = u64::max(self.end, other.end);
if first_end < second_start {
let first = Self {
start: first_start,
end: first_end,
};
let second = Self {
start: second_start,
end: second_end,
};
UpToTwo::Two(first, second)
} else {
UpToTwo::One(Self {
start: first_start,
end: second_end,
})
}
}
}
impl Sub for SectionRange {
type Output = UpToTwo<Self>;
fn sub(self, other: Self) -> Self::Output {
if other.encompasses(&self) {
return UpToTwo::Zero;
}
// Closures to prevent integer overflow
let first = || Self {
start: self.start,
end: u64::min(self.end, other.start - 1),
};
let second = || Self {
start: u64::max(self.start, other.end + 1),
end: self.end,
};
// The other range does not encompass this one entirely - find out if the remaining bits
// are at the start, at the end or both
if other.end >= self.end {
UpToTwo::One(first())
} else if other.start <= self.start {
UpToTwo::One(second())
} else {
UpToTwo::Two(first(), second())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -168,10 +229,12 @@ mod tests {
check_intersection(range1, range3, None);
check_intersection(range2, range3, Some(range3));
// self-intersection is always encompassing
assert!(range1.encompasses(&range1));
assert!(range2.encompasses(&range2));
assert!(range3.encompasses(&range3));
// only 2 includes all of 3
assert!(!range1.encompasses(&range2));
assert!(!range1.encompasses(&range3));
@ -181,4 +244,93 @@ mod tests {
assert!(!range3.encompasses(&range1));
assert!(!range3.encompasses(&range2));
}
#[test]
fn section_range_operations() {
fn elements(r: SectionRange) -> Vec<u64> {
RangeInclusive::<_>::from(r).collect()
}
fn elements_multi(r: UpToTwo<SectionRange>) -> Vec<u64> {
let is_empty = r.is_empty();
let sections: Vec<_> = r.into_iter().flat_map(elements).collect();
if !is_empty {
let mut it = sections.iter().copied();
let mut prev_section = it.next().unwrap();
for section in it {
assert!(section > prev_section);
prev_section = section;
}
}
sections
}
let ranges = (0..=5).flat_map(|start| (start..=5).map(move |end| start..=end));
for range_left in ranges.clone() {
for range_right in ranges.clone() {
// SETUP
println!("testing {:?} against {:?}", range_left, range_right);
let left = SectionRange::try_from(range_left.clone()).unwrap();
let right = SectionRange::try_from(range_right.clone()).unwrap();
let e_left: Vec<_> = elements(left);
let e_right: Vec<_> = elements(right);
// TESTS
assert_eq!(RangeInclusive::<_>::from(left), range_left);
assert_eq!(RangeInclusive::<_>::from(right), range_right);
// len()
assert_eq!(
left.len().get(),
*range_left.end() - *range_left.start() + 1
);
// contains()
for i in 0..=10 {
assert_eq!(
left.contains(i),
i >= *range_left.start() && i <= *range_left.end()
);
}
// encompasses()
assert!(left.encompasses(&left));
assert_eq!(
left.encompasses(&right),
e_right.iter().all(|e| e_left.contains(e))
);
// intersection
let e_intersection: Vec<_> = e_right
.iter()
.copied()
.filter(|e| e_left.contains(e))
.collect();
match left & right {
None => assert_eq!(e_intersection, []),
Some(intersection) => assert_eq!(elements(intersection), e_intersection),
}
// union
let mut e_union: Vec<_> = e_left.iter().chain(e_right.iter()).copied().collect();
e_union.sort_unstable();
e_union.dedup();
assert_eq!(elements_multi(left | right), e_union);
// difference
let e_difference: Vec<_> = e_left
.iter()
.copied()
.filter(|e| !e_right.contains(e))
.collect();
assert_eq!(elements_multi(left - right), e_difference);
}
}
}
}