#![warn(missing_docs)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
mod readme {}
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::ops::{Bound, RangeBounds};
use integer::NumericInteger;
use range::AnyRange;
pub mod integer;
pub mod range;
fn range_to_bounds<T: NumericInteger, R: RangeBounds<T>>(range: &R) -> Option<(T, T)> {
let min = match range.start_bound() {
Bound::Included(t) => *t,
Bound::Excluded(t) => {
if *t == T::max_value() {
return None;
}
*t + T::one()
}
Bound::Unbounded => T::min_value(),
};
let max = match range.end_bound() {
Bound::Included(t) => *t,
Bound::Excluded(t) => {
if *t == T::min_value() {
return None;
}
*t - T::one()
}
Bound::Unbounded => T::max_value(),
};
if min > max {
None
} else {
Some((min, max))
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RangeSet<T: NumericInteger>(
) and all the odd index represent the upper bounds (inclusive).
pub Vec<T>,
);
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de, T: NumericInteger + Deserialize<'de>> Deserialize<'de> for RangeSet<T> {
fn deserialize<D>(deserializer: D) -> Result<RangeSet<T>, D::Error>
where
D: serde::Deserializer<'de>,
{
let bounds = Vec::<T>::deserialize(deserializer)?;
RangeSet::new_from_bounds(bounds).ok_or_else(|| {
serde::de::Error::custom(
"invalid RangeSet: expected an even number of inclusive bounds describing sorted, non-overlapping and non-adjacent ranges",
)
})
}
}
impl<T: NumericInteger> std::fmt::Display for RangeSet<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[ ")?;
for bounds in self.0.chunks_exact(2) {
write!(f, "{}..={} ", bounds[0], bounds[1])?;
}
write!(f, "]")
}
}
#[derive(Clone, Debug)]
pub struct RangeSetIter<'a, T: NumericInteger> {
bounds: &'a [T],
front: usize,
back: usize,
current: Option<(T, T)>,
current_back: Option<(T, T)>,
}
impl<T: NumericInteger> Iterator for RangeSetIter<'_, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if let Some((min, max)) = self.current {
self.current = if min < max {
Some((min + T::one(), max))
} else {
None
};
return Some(min);
}
if self.front < self.back {
let (min, max) = (self.bounds[self.front], self.bounds[self.front + 1]);
self.front += 2;
if min < max {
self.current = Some((min + T::one(), max));
}
return Some(min);
}
let (min, max) = self.current_back?;
self.current_back = if min < max {
Some((min + T::one(), max))
} else {
None
};
Some(min)
}
}
impl<T: NumericInteger> DoubleEndedIterator for RangeSetIter<'_, T> {
fn next_back(&mut self) -> Option<Self::Item> {
if let Some((min, max)) = self.current_back {
self.current_back = if min < max {
Some((min, max - T::one()))
} else {
None
};
return Some(max);
}
if self.front < self.back {
let (min, max) = (self.bounds[self.back - 2], self.bounds[self.back - 1]);
self.back -= 2;
if min > max {
return Some(min);
}
if min < max {
self.current_back = Some((min, max - T::one()));
}
return Some(max);
}
let (min, max) = self.current?;
self.current = if min < max {
Some((min, max - T::one()))
} else {
None
};
Some(max)
}
}
impl<T: NumericInteger> std::iter::FusedIterator for RangeSetIter<'_, T> {}
impl<T: NumericInteger> RangeSet<T> {
pub fn iter(&self) -> RangeSetIter<'_, T> {
let len = self.0.len();
RangeSetIter {
bounds: &self.0,
front: 0,
back: len - len % 2,
current: None,
current_back: None,
}
}
pub fn new_from_range<R: RangeBounds<T>>(range: R) -> RangeSet<T> {
match range_to_bounds(&range) {
Some((min, max)) => RangeSet(vec![min, max]),
None => RangeSet::empty(),
}
}
pub fn new_from_ranges(ranges: &[AnyRange<T>]) -> RangeSet<T> {
let mut ranges: Vec<(T, T)> = ranges
.iter()
.map(|range| range.get_bounds())
.filter(|(min, max)| max >= min)
.copied()
.collect();
let mut is_sorted = true;
if let Some((first_min, _)) = ranges.first() {
let mut previous_min = *first_min;
for (min, _) in &ranges[1..] {
if *min < previous_min {
is_sorted = false;
break;
}
previous_min = *min;
}
}
if !is_sorted {
ranges.sort_by_key(|range| range.0);
}
let mut bounds = Vec::with_capacity(ranges.len() * 2);
let mut current_max = T::min_value();
for (min, max) in ranges {
if bounds.is_empty() || (current_max < T::max_value() && min > current_max + T::one()) {
bounds.extend_from_slice(&[min, max]);
current_max = max;
} else if max > current_max {
*bounds.last_mut().unwrap() = max;
current_max = max;
}
}
bounds.shrink_to_fit();
RangeSet(bounds)
}
pub fn new_from_bounds(bounds: Vec<T>) -> Option<RangeSet<T>> {
if bounds.len() % 2 == 1 {
return None;
}
let mut previous_max: Option<T> = None;
for range in bounds.chunks_exact(2) {
let (min, max) = (range[0], range[1]);
if min > max {
return None;
}
if let Some(previous_max) = previous_max {
if previous_max >= T::max_value() || min <= previous_max + T::one() {
return None;
}
}
previous_max = Some(max);
}
Some(RangeSet(bounds))
}
#[inline]
pub fn empty() -> RangeSet<T> {
RangeSet(vec![])
}
#[inline]
pub fn total() -> RangeSet<T> {
RangeSet(vec![T::min_value(), T::max_value()])
}
#[inline]
pub fn is_total(&self) -> bool {
self.0.len() >= 2 && self.0[0] == T::min_value() && self.0[1] >= T::max_value()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn contains(&self, value: T) -> bool {
if self.is_empty() {
return false;
}
let first = *self.0.first().unwrap();
let last = *self.0.last().unwrap();
if value < first || value > last {
return false;
} else if value == first || value == last {
return true;
}
let position = self.0.partition_point(|&x| x < value);
self.0[position] == value || position % 2 != 0
}
pub fn contains_all(&self, that: &RangeSet<T>) -> bool {
if self.is_total() || that.is_empty() {
return true;
}
if self.is_empty() || that.is_total() {
return false;
}
if that.0[0] < self.0[0] || that.0[that.0.len() - 1] > self.0[self.0.len() - 1] {
return false;
}
let mut self_i = 0;
for bounds in that.0.chunks_exact(2) {
let (that_min, that_max) = (bounds[0], bounds[1]);
while self_i + 1 < self.0.len() && self.0[self_i + 1] < that_min {
self_i += 2;
}
if self_i + 1 >= self.0.len()
|| self.0[self_i] > that_min
|| self.0[self_i + 1] < that_max
{
return false;
}
}
true
}
pub fn union(&self, that: &RangeSet<T>) -> RangeSet<T> {
if self.is_empty() || that.is_total() {
return that.clone();
} else if that.is_empty() || self.is_total() {
return self.clone();
}
let mut new_range = Vec::with_capacity(self.0.len() + that.0.len());
let mut self_i = 0;
let mut that_i = 0;
let mut current: Option<(T, T)> = None;
while self_i + 1 < self.0.len() || that_i + 1 < that.0.len() {
let (min, max) = if that_i + 1 < that.0.len()
&& (self_i + 1 >= self.0.len() || self.0[self_i] > that.0[that_i])
{
that_i += 2;
(that.0[that_i - 2], that.0[that_i - 1])
} else {
self_i += 2;
(self.0[self_i - 2], self.0[self_i - 1])
};
match current {
Some((current_min, current_max))
if min <= current_max
|| (current_max < T::max_value() && min <= current_max + T::one()) =>
{
if max > current_max {
current = Some((current_min, max));
}
}
Some((current_min, current_max)) => {
new_range.extend_from_slice(&[current_min, current_max]);
current = Some((min, max));
}
None => current = Some((min, max)),
}
}
if let Some((current_min, current_max)) = current {
new_range.extend_from_slice(&[current_min, current_max]);
}
new_range.shrink_to_fit();
RangeSet(new_range)
}
pub fn has_intersection(&self, that: &RangeSet<T>) -> bool {
let mut i = 0;
let mut j = 0;
while i + 1 < self.0.len() && j + 1 < that.0.len() {
let self_min = self.0[i];
let self_max = self.0[i + 1];
let that_min = that.0[j];
let that_max = that.0[j + 1];
if self_max < that_min {
i += 2;
} else if that_max < self_min {
j += 2;
} else {
return true;
}
}
false
}
pub fn intersection(&self, that: &RangeSet<T>) -> RangeSet<T> {
if self.is_empty() || that.is_empty() {
return RangeSet::empty();
} else if self.is_total() {
return that.clone();
} else if that.is_total() {
return self.clone();
}
let mut new_range = Vec::with_capacity(self.0.len() + that.0.len());
let mut i = 0;
let mut j = 0;
while i + 1 < self.0.len() && j + 1 < that.0.len() {
let self_min = self.0[i];
let self_max = self.0[i + 1];
let that_min = that.0[j];
let that_max = that.0[j + 1];
if self_max < that_min {
i += 2;
} else if that_max < self_min {
j += 2;
} else {
new_range.push(std::cmp::max(self_min, that_min));
new_range.push(std::cmp::min(self_max, that_max));
if self_max < that_max {
i += 2;
} else {
j += 2;
}
}
}
new_range.shrink_to_fit();
RangeSet(new_range)
}
pub fn complement(&self) -> RangeSet<T> {
if self.is_empty() {
return Self::total();
} else if self.is_total() {
return Self::empty();
}
let mut new_range = Vec::with_capacity(self.0.len() + 2);
let first = self.0[0];
if first > T::min_value() {
new_range.extend_from_slice(&[T::min_value(), first - T::one()]);
}
let inner = if self.0.len() > 2 {
&self.0[1..self.0.len() - 1]
} else {
&[][..]
};
for gap in inner.chunks_exact(2) {
let (previous_max, next_min) = (gap[0], gap[1]);
if previous_max < T::max_value() && next_min > T::min_value() {
let (min, max) = (previous_max + T::one(), next_min - T::one());
if min <= max {
new_range.extend_from_slice(&[min, max]);
}
}
}
let last = self.0[self.0.len() - 1];
if last < T::max_value() {
new_range.extend_from_slice(&[last + T::one(), T::max_value()]);
}
new_range.shrink_to_fit();
RangeSet(new_range)
}
pub fn difference(&self, that: &RangeSet<T>) -> RangeSet<T> {
if self.is_empty() || that.is_empty() {
return self.clone();
} else if that.is_total() {
return RangeSet::empty();
} else if self.is_total() {
return that.complement();
}
let mut new_range = Vec::with_capacity(self.0.len() + that.0.len());
let mut that_i = 0;
for bounds in self.0.chunks_exact(2) {
let (mut min, max) = (bounds[0], bounds[1]);
while that_i + 1 < that.0.len() && that.0[that_i + 1] < min {
that_i += 2;
}
let mut j = that_i;
let mut covered = false;
while j + 1 < that.0.len() {
let (that_min, that_max) = (that.0[j], that.0[j + 1]);
if that_min > max {
break;
}
if that_min > min {
new_range.extend_from_slice(&[min, that_min - T::one()]);
}
if that_max >= max {
covered = true;
break;
}
min = that_max + T::one();
j += 2;
}
if !covered {
new_range.extend_from_slice(&[min, max]);
}
}
new_range.shrink_to_fit();
RangeSet(new_range)
}
}
impl<T: NumericInteger> Default for RangeSet<T> {
#[inline]
fn default() -> RangeSet<T> {
RangeSet::empty()
}
}
impl<'a, T: NumericInteger> IntoIterator for &'a RangeSet<T> {
type Item = T;
type IntoIter = RangeSetIter<'a, T>;
#[inline]
fn into_iter(self) -> RangeSetIter<'a, T> {
self.iter()
}
}
impl<T: NumericInteger> FromIterator<AnyRange<T>> for RangeSet<T> {
fn from_iter<I: IntoIterator<Item = AnyRange<T>>>(iter: I) -> RangeSet<T> {
RangeSet::new_from_ranges(&iter.into_iter().collect::<Vec<_>>())
}
}
impl<T: NumericInteger> std::ops::BitOr<&RangeSet<T>> for &RangeSet<T> {
type Output = RangeSet<T>;
#[inline]
fn bitor(self, that: &RangeSet<T>) -> RangeSet<T> {
self.union(that)
}
}
impl<T: NumericInteger> std::ops::BitAnd<&RangeSet<T>> for &RangeSet<T> {
type Output = RangeSet<T>;
#[inline]
fn bitand(self, that: &RangeSet<T>) -> RangeSet<T> {
self.intersection(that)
}
}
impl<T: NumericInteger> std::ops::Sub<&RangeSet<T>> for &RangeSet<T> {
type Output = RangeSet<T>;
#[inline]
fn sub(self, that: &RangeSet<T>) -> RangeSet<T> {
self.difference(that)
}
}
impl<T: NumericInteger> std::ops::Not for &RangeSet<T> {
type Output = RangeSet<T>;
#[inline]
fn not(self) -> RangeSet<T> {
self.complement()
}
}
#[cfg(test)]
mod tests {
use std::collections::{BTreeSet, HashSet};
use super::*;
macro_rules! test_empty_and_total_for_types {
($($t:ty),*) => {
$(
let empty = RangeSet::<$t>::empty();
assert!(empty.is_empty());
assert!(!empty.is_total());
let total = RangeSet::<$t>::total();
assert!(!total.is_empty());
assert!(total.is_total());
)*
};
}
#[test]
fn test_empty_and_total() -> Result<(), String> {
test_empty_and_total_for_types!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
Ok(())
}
#[test]
fn test_new_from_range() -> Result<(), String> {
assert_eq!(
RangeSet::<u8>(vec![3, 4]),
RangeSet::<u8>::new_from_range(3..5)
);
assert_eq!(
RangeSet::<u8>(vec![3, 5]),
RangeSet::<u8>::new_from_range(3..=5)
);
assert_eq!(
RangeSet::<u8>(vec![0, 5]),
RangeSet::<u8>::new_from_range(..=5)
);
assert_eq!(
RangeSet::<u8>(vec![2, 255]),
RangeSet::<u8>::new_from_range(2..)
);
assert_eq!(
RangeSet::<u8>(vec![0, 255]),
RangeSet::<u8>::new_from_range(..)
);
Ok(())
}
#[test]
#[allow(clippy::reversed_empty_ranges)] fn test_new_from_ranges() -> Result<(), String> {
assert_eq!(
RangeSet(vec![3, 5, 9, 14]),
RangeSet::new_from_ranges(&[AnyRange::from(3..=5), AnyRange::from(9..15)])
);
assert_eq!(
RangeSet(vec![3, 5, 9, 14]),
RangeSet::new_from_ranges(&[AnyRange::from(9..15), AnyRange::from(3..=5)])
);
assert_eq!(
RangeSet(vec![3, 5, 9, 14]),
RangeSet::new_from_ranges(&[
AnyRange::from(9..15),
AnyRange::from(23..18),
AnyRange::from(3..=5),
AnyRange::from(4..=4)
])
);
assert_eq!(
RangeSet(vec![3, 5, 9, 14]),
RangeSet::new_from_ranges(&[
AnyRange::from(9..15),
AnyRange::from(23..18),
AnyRange::from(3..=5)
])
);
assert_eq!(
RangeSet(vec![3, 255]),
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(4..), AnyRange::from(3..=5)])
);
assert_eq!(
RangeSet(vec![0, 5]),
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(..=4), AnyRange::from(3..=5)])
);
assert_eq!(
RangeSet::total(),
RangeSet::new_from_ranges(&[AnyRange::from(..), AnyRange::from(3..=5)])
);
assert_eq!(
RangeSet::empty(),
RangeSet::new_from_ranges(&[AnyRange::from(3..1), AnyRange::from(10..=5)])
);
Ok(())
}
#[test]
fn test_contains_all() -> Result<(), String> {
assert!(RangeSet::<u8>::empty().contains_all(&RangeSet::empty()));
assert!(RangeSet::<u8>::total().contains_all(&RangeSet::empty()));
assert!(!RangeSet::<u8>::empty().contains_all(&RangeSet::total()));
assert!(!RangeSet(vec![2, 44]).contains_all(&RangeSet(vec![56, 60])));
assert!(!RangeSet(vec![56, 60]).contains_all(&RangeSet(vec![2, 44])));
assert!(RangeSet(vec![56, 60]).contains_all(&RangeSet(vec![56, 60])));
assert!(RangeSet(vec![56, 61]).contains_all(&RangeSet(vec![56, 61])));
assert!(RangeSet(vec![55, 61]).contains_all(&RangeSet(vec![56, 60])));
assert!(RangeSet(vec![56, 60]).contains_all(&RangeSet(vec![57, 59])));
assert!(RangeSet(vec![56, 60]).contains_all(&RangeSet(vec![57, 60])));
assert!(RangeSet(vec![56, 60]).contains_all(&RangeSet(vec![56, 59])));
assert!(RangeSet(vec![19, 33]).contains_all(&RangeSet(vec![20, 21, 30, 32])));
assert!(
RangeSet(vec![19, 33, 53, 70]).contains_all(&RangeSet(vec![20, 21, 30, 32, 66, 69]))
);
assert!(
!RangeSet(vec![19, 33, 53, 70]).contains_all(&RangeSet(vec![20, 21, 30, 32, 66, 71]))
);
Ok(())
}
#[test]
fn test_iter_and_contains() -> Result<(), String> {
let empty = RangeSet::<u8>::empty();
assert_eq!(0, empty.iter().count());
assert!(!empty.contains(0));
let total = RangeSet::<u8>::total();
assert_eq!(256, total.iter().collect::<HashSet<_>>().len());
assert!(total.iter().all(|v| total.contains(v)));
let range = RangeSet(vec![19, 33, 53, 70]);
assert_eq!(33, range.iter().collect::<HashSet<_>>().len()); assert!(range.iter().all(|v| range.contains(v)));
assert!(!range.contains(34));
Ok(())
}
#[test]
fn test_union() -> Result<(), String> {
assert!(RangeSet::<u8>::empty().union(&RangeSet::empty()).is_empty());
assert!(RangeSet::<u8>::total().union(&RangeSet::empty()).is_total());
assert!(RangeSet::<u8>::empty().union(&RangeSet::total()).is_total());
assert_eq!(
vec![2, 44, 56, 60],
RangeSet(vec![2, 44]).union(&RangeSet(vec![56, 60])).0
);
assert_eq!(
vec![0, 44],
RangeSet(vec![2, 44]).union(&RangeSet(vec![0, 3])).0
);
assert_eq!(
vec![2, 48],
RangeSet(vec![2, 44]).union(&RangeSet(vec![5, 48])).0
);
assert_eq!(
vec![2, 44],
RangeSet(vec![2, 44]).union(&RangeSet(vec![2, 44])).0
);
assert_eq!(
vec![2, 44],
RangeSet(vec![2, 44]).union(&RangeSet(vec![5, 20])).0
);
assert_eq!(
vec![2, 50],
RangeSet(vec![2, 44]).union(&RangeSet(vec![45, 50])).0
);
assert_eq!(
vec![2, 44],
RangeSet(vec![2, 44])
.union(&RangeSet(vec![5, 20, 23, 40, 42, 43]))
.0
);
assert_eq!(
vec![1, 44],
RangeSet(vec![2, 44]).union(&RangeSet(vec![1, 2, 4, 5])).0
);
assert_eq!(
vec![0, 9, 11, 97],
RangeSet(vec![0, 9, 11, 96])
.union(&RangeSet(vec![97, 97]))
.0
);
let range1 = RangeSet::new_from_ranges(&[
AnyRange::from(..=12),
AnyRange::from(15..=15),
AnyRange::from(18..),
]);
let range2 = RangeSet::new_from_ranges(&[AnyRange::from(..=12), AnyRange::from(15..)]);
let range3 = RangeSet::new_from_ranges(&[AnyRange::from(..=12), AnyRange::from(15..)]);
assert_eq!(range3, range1.union(&range2));
Ok(())
}
#[test]
fn test_intersection() -> Result<(), String> {
assert!(RangeSet::<u32>::empty()
.intersection(&RangeSet::empty())
.is_empty());
assert!(RangeSet::<u32>::total()
.intersection(&RangeSet::empty())
.is_empty());
assert!(RangeSet::<u32>::empty()
.intersection(&RangeSet::total())
.is_empty());
assert!(RangeSet::<u32>::total()
.intersection(&RangeSet::total())
.is_total());
assert_eq!(
RangeSet::empty(),
RangeSet(vec![2, 44]).intersection(&RangeSet(vec![56, 60]))
);
assert_eq!(
vec![2, 3],
RangeSet(vec![2, 44]).intersection(&RangeSet(vec![0, 3])).0
);
assert_eq!(
vec![5, 44],
RangeSet(vec![2, 44]).intersection(&RangeSet(vec![5, 48])).0
);
assert_eq!(
vec![2, 44],
RangeSet(vec![2, 44]).intersection(&RangeSet(vec![2, 44])).0
);
assert_eq!(
vec![5, 20],
RangeSet(vec![2, 44]).intersection(&RangeSet(vec![5, 20])).0
);
assert_eq!(
vec![5, 20, 23, 40],
RangeSet(vec![2, 44])
.intersection(&RangeSet(vec![5, 20, 23, 40]))
.0
);
assert_eq!(
vec![5, 20, 23, 40],
RangeSet(vec![5, 20, 23, 40])
.intersection(&RangeSet(vec![2, 44]))
.0
);
assert!(RangeSet(vec![99, 99])
.intersection(&RangeSet(vec![i32::MIN, 98, 100, i32::MAX]))
.is_empty());
Ok(())
}
#[test]
fn test_complement() -> Result<(), String> {
assert!(RangeSet::<u32>::total().complement().is_empty());
assert!(RangeSet::<u32>::empty().complement().is_total());
assert_eq!(
vec![0, 1, 45, u32::MAX],
RangeSet(vec![2, 44]).complement().0
);
assert_eq!(
vec![45, i64::MAX],
RangeSet(vec![i64::MIN, 44]).complement().0
);
assert_eq!(
vec![45, 238],
RangeSet(vec![0, 44, 239, u32::MAX]).complement().0
);
Ok(())
}
#[cfg(feature = "serde")]
macro_rules! serde_test {
($($t:ty),*) => {
$(
let range = RangeSet::<$t>::empty();
let serialized = serde_json::to_string(&range).unwrap();
let unserialized: RangeSet<$t> = serde_json::from_str(&serialized).unwrap();
assert_eq!(range, unserialized);
let range = RangeSet::<$t>::total();
let serialized = serde_json::to_string(&range).unwrap();
let unserialized: RangeSet<$t> = serde_json::from_str(&serialized).unwrap();
assert_eq!(range, unserialized);
let range =
RangeSet::<$t>::new_from_ranges(&[AnyRange::from(3..=4), AnyRange::from(7..9)]);
let serialized = serde_json::to_string(&range).unwrap();
let unserialized: RangeSet<$t> = serde_json::from_str(&serialized).unwrap();
assert_eq!(range, unserialized);
)*
};
}
#[test]
#[cfg(feature = "serde")]
fn serde_test() {
serde_test!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
}
struct ExcludedMaxStart;
impl RangeBounds<u8> for ExcludedMaxStart {
fn start_bound(&self) -> Bound<&u8> {
Bound::Excluded(&u8::MAX)
}
fn end_bound(&self) -> Bound<&u8> {
Bound::Unbounded
}
}
struct ExcludedBothEnds(u8, u8);
impl RangeBounds<u8> for ExcludedBothEnds {
fn start_bound(&self) -> Bound<&u8> {
Bound::Excluded(&self.0)
}
fn end_bound(&self) -> Bound<&u8> {
Bound::Excluded(&self.1)
}
}
fn range_set_from_values(values: &[u8]) -> RangeSet<u8> {
RangeSet::new_from_ranges(
&values
.iter()
.map(|&value| AnyRange::from(value..=value))
.collect::<Vec<_>>(),
)
}
fn assert_canonical(range: &RangeSet<u8>) {
assert_eq!(
0,
range.0.len() % 2,
"odd number of bounds in {:?}",
range.0
);
for bounds in range.0.chunks(2) {
assert!(
bounds[0] <= bounds[1],
"inverted range {}..={} in {:?}",
bounds[0],
bounds[1],
range.0
);
}
for pair in range.0.chunks(2).collect::<Vec<_>>().windows(2) {
assert!(
u16::from(pair[1][0]) > u16::from(pair[0][1]) + 1,
"ranges {}..={} and {}..={} should have been merged in {:?}",
pair[0][0],
pair[0][1],
pair[1][0],
pair[1][1],
range.0
);
}
}
#[test]
fn test_new_from_ranges_merges_adjacent_ranges() -> Result<(), String> {
assert_eq!(
RangeSet::<u8>::new_from_range(1..=4),
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(1..=2), AnyRange::from(3..=4)])
);
assert_eq!(
vec![1, 4],
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(3..=4), AnyRange::from(1..=2)]).0
);
assert_eq!(
vec![0, 9],
RangeSet::<u8>::new_from_ranges(&[
AnyRange::from(0..=2),
AnyRange::from(3..=3),
AnyRange::from(4..8),
AnyRange::from(8..=9)
])
.0
);
assert_eq!(
vec![0, 9],
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(0..=9), AnyRange::from(3..=4)]).0
);
assert_eq!(
vec![0, 2, 4, 9],
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(0..=2), AnyRange::from(4..=9)]).0
);
assert_eq!(
vec![250, 255],
RangeSet::<u8>::new_from_ranges(&[
AnyRange::from(250..=254),
AnyRange::from(255..=255)
])
.0
);
assert!(
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(..), AnyRange::from(200..=255)])
.is_total()
);
Ok(())
}
#[test]
fn test_complement_and_difference_with_adjacent_ranges() -> Result<(), String> {
let range =
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(1..=2), AnyRange::from(3..=4)]);
assert_canonical(&range);
let complement = range.complement();
assert_canonical(&complement);
assert_eq!(vec![0, 0, 5, 255], complement.0);
let range1 = RangeSet::<u8>::new_from_range(9..=12);
let range2 =
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(11..=11), AnyRange::from(12..=17)]);
let difference = range1.difference(&range2);
assert_canonical(&difference);
assert_eq!(vec![9, 10], difference.0);
let range =
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(3..=6), AnyRange::from(7..=11)]);
assert!(range.contains_all(&RangeSet::new_from_range(3..=8)));
assert!(range.contains_all(&RangeSet::new_from_range(3..=11)));
assert!(!range.contains_all(&RangeSet::new_from_range(3..=12)));
Ok(())
}
#[test]
fn test_new_from_empty_range() -> Result<(), String> {
assert!(RangeSet::<u8>::new_from_range(5..5).is_empty());
assert!(RangeSet::<u8>::new_from_range(0..0).is_empty());
assert!(RangeSet::<u8>::new_from_range(..0).is_empty());
assert!(RangeSet::<u8>::new_from_range(ExcludedMaxStart).is_empty());
assert!(RangeSet::<i32>::new_from_range(i32::MIN..i32::MIN).is_empty());
assert!(RangeSet::<i8>::new_from_range(..i8::MIN).is_empty());
assert!(RangeSet::<usize>::new_from_range(0..0).is_empty());
assert!(!RangeSet::<u8>::new_from_range(0..0).is_total());
assert!(!RangeSet::<i32>::new_from_range(i32::MIN..i32::MIN).is_total());
assert!(RangeSet::<u8>::new_from_ranges(&[AnyRange::from(0..0)]).is_empty());
assert_eq!(
RangeSet::<u8>::new_from_range(7..=9),
RangeSet::<u8>::new_from_ranges(&[AnyRange::from(0..0), AnyRange::from(7..=9)])
);
assert_eq!(
vec![4, 6],
RangeSet::<u8>::new_from_range(ExcludedBothEnds(3, 7)).0
);
assert_eq!(vec![0, 254], RangeSet::<u8>::new_from_range(..u8::MAX).0);
Ok(())
}
#[test]
fn test_malformed_range_set_does_not_panic() -> Result<(), String> {
for bounds in [
vec![0u8],
vec![1, 5, 9],
vec![5, 3],
vec![10, 20, 0, 5],
vec![1, 255, 3, 5],
vec![255, 0],
vec![0, 0, 255, 255],
vec![255, 255, 0, 0],
] {
let malformed = RangeSet::<u8>(bounds);
let _ = malformed.is_total();
let _ = malformed.is_empty();
let _ = format!("{malformed}");
let _ = malformed.contains(4);
let _ = malformed.iter().take(512).count();
let _ = malformed.complement();
let _ = malformed.union(&RangeSet(vec![1, 2]));
let _ = malformed.intersection(&RangeSet(vec![1, 2]));
let _ = malformed.difference(&RangeSet(vec![1, 2]));
let _ = malformed.has_intersection(&RangeSet(vec![1, 2]));
let _ = malformed.contains_all(&RangeSet(vec![1, 2]));
let _ = RangeSet(vec![1u8, 2]).contains_all(&malformed);
let _ = RangeSet(vec![1u8, 2]).union(&malformed);
let _ = RangeSet(vec![1u8, 2]).intersection(&malformed);
let _ = RangeSet(vec![1u8, 2]).has_intersection(&malformed);
}
Ok(())
}
#[test]
fn test_exhaustive_against_btree_set() -> Result<(), String> {
const UNIVERSE: u8 = 7;
let subsets: Vec<(BTreeSet<u8>, RangeSet<u8>)> = (0..(1u32 << UNIVERSE))
.map(|mask| {
let values: Vec<u8> = (0..UNIVERSE)
.filter(|value| mask & (1 << value) != 0)
.collect();
(
values.iter().copied().collect(),
range_set_from_values(&values),
)
})
.collect();
for (values, range) in &subsets {
assert_canonical(range);
assert_eq!(*values, range.iter().collect::<BTreeSet<_>>());
assert_eq!(values.is_empty(), range.is_empty());
let complement = range.complement();
assert_canonical(&complement);
assert_eq!(
(0..=u8::MAX)
.filter(|v| !values.contains(v))
.collect::<BTreeSet<_>>(),
complement.iter().collect::<BTreeSet<_>>()
);
for value in 0..=UNIVERSE {
assert_eq!(values.contains(&value), range.contains(value));
}
}
for (values1, range1) in &subsets {
for (values2, range2) in &subsets {
let union = range1.union(range2);
assert_canonical(&union);
assert_eq!(
values1.union(values2).copied().collect::<BTreeSet<_>>(),
union.iter().collect::<BTreeSet<_>>(),
"{range1} union {range2}"
);
let intersection = range1.intersection(range2);
assert_canonical(&intersection);
assert_eq!(
values1
.intersection(values2)
.copied()
.collect::<BTreeSet<_>>(),
intersection.iter().collect::<BTreeSet<_>>(),
"{range1} intersection {range2}"
);
let difference = range1.difference(range2);
assert_canonical(&difference);
assert_eq!(
values1
.difference(values2)
.copied()
.collect::<BTreeSet<_>>(),
difference.iter().collect::<BTreeSet<_>>(),
"{range1} difference {range2}"
);
assert_eq!(
!values1.is_disjoint(values2),
range1.has_intersection(range2),
"{range1} has_intersection {range2}"
);
assert_eq!(
values2.is_subset(values1),
range1.contains_all(range2),
"{range1} contains_all {range2}"
);
if values1 == values2 {
assert_eq!(range1, range2);
}
}
}
Ok(())
}
#[test]
fn test_randomized_against_btree_set_over_the_whole_universe() -> Result<(), String> {
let mut state = 0x2545_f491_4f6c_dd1du64;
let mut next = move || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
let subsets: Vec<(BTreeSet<u8>, RangeSet<u8>)> = (0..200)
.map(|_| {
let density = (next() % 100) as u8;
let values: Vec<u8> = (0..=u8::MAX)
.filter(|_| (next() % 100) as u8 <= density)
.collect();
(
values.iter().copied().collect(),
range_set_from_values(&values),
)
})
.collect();
for (values, range) in &subsets {
assert_canonical(range);
assert_eq!(*values, range.iter().collect::<BTreeSet<_>>());
let complement = range.complement();
assert_canonical(&complement);
assert_eq!(
(0..=u8::MAX)
.filter(|value| !values.contains(value))
.collect::<BTreeSet<_>>(),
complement.iter().collect::<BTreeSet<_>>(),
"complement of {range}"
);
}
for (values1, range1) in &subsets {
for (values2, range2) in &subsets {
let union = range1.union(range2);
assert_canonical(&union);
assert_eq!(
values1.union(values2).copied().collect::<BTreeSet<_>>(),
union.iter().collect::<BTreeSet<_>>(),
"{range1} union {range2}"
);
let intersection = range1.intersection(range2);
assert_canonical(&intersection);
assert_eq!(
values1
.intersection(values2)
.copied()
.collect::<BTreeSet<_>>(),
intersection.iter().collect::<BTreeSet<_>>(),
"{range1} intersection {range2}"
);
let difference = range1.difference(range2);
assert_canonical(&difference);
assert_eq!(
values1
.difference(values2)
.copied()
.collect::<BTreeSet<_>>(),
difference.iter().collect::<BTreeSet<_>>(),
"{range1} difference {range2}"
);
assert_eq!(
!values1.is_disjoint(values2),
range1.has_intersection(range2),
"{range1} has_intersection {range2}"
);
assert_eq!(
values2.is_subset(values1),
range1.contains_all(range2),
"{range1} contains_all {range2}"
);
}
}
Ok(())
}
#[test]
fn test_new_from_bounds_validation() -> Result<(), String> {
assert_eq!(
Some(RangeSet::<u8>::empty()),
RangeSet::<u8>::new_from_bounds(vec![])
);
assert_eq!(
Some(RangeSet::<u8>::new_from_range(2..=5)),
RangeSet::<u8>::new_from_bounds(vec![2, 5])
);
assert_eq!(
Some(RangeSet::<u8>::total()),
RangeSet::<u8>::new_from_bounds(vec![0, 255])
);
assert_eq!(
Some(RangeSet::<u8>(vec![2, 5, 7, 9])),
RangeSet::<u8>::new_from_bounds(vec![2, 5, 7, 9])
);
assert_eq!(None, RangeSet::<u8>::new_from_bounds(vec![0]));
assert_eq!(None, RangeSet::<u8>::new_from_bounds(vec![0, 5, 7]));
assert_eq!(None, RangeSet::<u8>::new_from_bounds(vec![5, 3]));
assert_eq!(None, RangeSet::<u8>::new_from_bounds(vec![10, 20, 0, 5]));
assert_eq!(None, RangeSet::<u8>::new_from_bounds(vec![0, 5, 4, 9]));
assert_eq!(None, RangeSet::<u8>::new_from_bounds(vec![0, 5, 6, 9]));
assert_eq!(
None,
RangeSet::<u8>::new_from_bounds(vec![0, 255, 255, 255])
);
Ok(())
}
#[test]
#[cfg(feature = "serde")]
fn serde_rejects_malformed_range_set() {
assert!(serde_json::from_str::<RangeSet<u8>>("[]").is_ok());
assert!(serde_json::from_str::<RangeSet<u8>>("[0,5]").is_ok());
assert!(serde_json::from_str::<RangeSet<u8>>("[0,5,7,9]").is_ok());
assert!(serde_json::from_str::<RangeSet<u8>>("[0]").is_err());
assert!(serde_json::from_str::<RangeSet<u8>>("[0,5,7]").is_err());
assert!(serde_json::from_str::<RangeSet<u8>>("[5,3]").is_err());
assert!(serde_json::from_str::<RangeSet<u8>>("[10,20,0,5]").is_err());
assert!(serde_json::from_str::<RangeSet<u8>>("[0,5,4,9]").is_err());
assert!(serde_json::from_str::<RangeSet<u8>>("[0,5,6,9]").is_err());
}
}