Skip to content

Commit

Permalink
Merge pull request #80 from xfbs/intersection
Browse files Browse the repository at this point in the history
Intersection and Union support for sets
  • Loading branch information
jeffparsons authored Feb 7, 2024
2 parents 9e6977d + 842ec1d commit ba9a3e2
Show file tree
Hide file tree
Showing 4 changed files with 452 additions and 11 deletions.
110 changes: 105 additions & 5 deletions src/inclusive_set.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use core::borrow::Borrow;
use core::fmt::{self, Debug};
use core::iter::{DoubleEndedIterator, FromIterator};
use core::ops::RangeInclusive;
use core::ops::{BitAnd, BitOr, RangeInclusive};

#[cfg(feature = "serde1")]
use core::marker::PhantomData;
Expand All @@ -14,6 +14,12 @@ use serde::{
use crate::std_ext::*;
use crate::RangeInclusiveMap;

/// Intersection iterator over two [`RangeInclusiveSet`].
pub type Intersection<'a, T> = crate::operations::Intersection<'a, RangeInclusive<T>, Iter<'a, T>>;

/// Union iterator over two [`RangeInclusiveSet`].
pub type Union<'a, T> = crate::operations::Union<'a, RangeInclusive<T>, Iter<'a, T>>;

#[derive(Clone, Hash, Default, Eq, PartialEq, PartialOrd, Ord)]
/// A set whose items are stored as ranges bounded
/// inclusively below and above `(start..=end)`.
Expand Down Expand Up @@ -172,6 +178,16 @@ where
pub fn last(&self) -> Option<&RangeInclusive<T>> {
self.rm.last_range_value().map(|(range, _)| range)
}

/// Return an iterator over the union of two range sets.
pub fn union<'a>(&'a self, other: &'a Self) -> Union<'a, T> {
Union::new(self.iter(), other.iter())
}

/// Return an iterator over the intersection of two range sets.
pub fn intersection<'a>(&'a self, other: &'a Self) -> Intersection<'a, T> {
Intersection::new(self.iter(), other.iter())
}
}

/// An iterator over the ranges of a `RangeInclusiveSet`.
Expand Down Expand Up @@ -437,6 +453,22 @@ macro_rules! range_inclusive_set {
}};
}

impl<T: Ord + Clone + StepLite> BitAnd for &RangeInclusiveSet<T> {
type Output = RangeInclusiveSet<T>;

fn bitand(self, other: Self) -> Self::Output {
self.intersection(other).collect()
}
}

impl<T: Ord + Clone + StepLite> BitOr for &RangeInclusiveSet<T> {
type Output = RangeInclusiveSet<T>;

fn bitor(self, other: Self) -> Self::Output {
self.union(other).collect()
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -493,12 +525,17 @@ mod tests {
assert_eq!(forward, backward);
}

#[proptest]
fn test_arbitrary_set_u8(ranges: Vec<RangeInclusive<u8>>) {
let ranges: Vec<_> = ranges
// neccessary due to assertion on empty ranges
fn filter_ranges<T: Ord>(ranges: Vec<RangeInclusive<T>>) -> Vec<RangeInclusive<T>> {
ranges
.into_iter()
.filter(|range| range.start() != range.end())
.collect();
.collect()
}

#[proptest]
fn test_arbitrary_set_u8(ranges: Vec<RangeInclusive<u8>>) {
let ranges: Vec<_> = filter_ranges(ranges);
let set = ranges
.iter()
.fold(RangeInclusiveSet::new(), |mut set, range| {
Expand Down Expand Up @@ -530,6 +567,69 @@ mod tests {
);
}

#[proptest]
fn test_union_overlaps_u8(left: Vec<RangeInclusive<u8>>, right: Vec<RangeInclusive<u8>>) {
let left: RangeInclusiveSet<_> = filter_ranges(left).into_iter().collect();
let right: RangeInclusiveSet<_> = filter_ranges(right).into_iter().collect();

let mut union = RangeInclusiveSet::new();
for range in left.union(&right) {
// there should not be any overlaps in the ranges returned by the union
assert!(union.overlapping(&range).next().is_none());
union.insert(range);
}
}

#[proptest]
fn test_union_contains_u8(left: Vec<RangeInclusive<u8>>, right: Vec<RangeInclusive<u8>>) {
let left: RangeInclusiveSet<_> = filter_ranges(left).into_iter().collect();
let right: RangeInclusiveSet<_> = filter_ranges(right).into_iter().collect();
let union: RangeInclusiveSet<_> = left.union(&right).collect();

// value should be in the union if and only if it is in either set
for value in 0..u8::MAX {
assert_eq!(
union.contains(&value),
left.contains(&value) || right.contains(&value)
);
}
}

#[proptest]
fn test_intersection_contains_u8(
left: Vec<RangeInclusive<u8>>,
right: Vec<RangeInclusive<u8>>,
) {
let left: RangeInclusiveSet<_> = filter_ranges(left).into_iter().collect();
let right: RangeInclusiveSet<_> = filter_ranges(right).into_iter().collect();
let union: RangeInclusiveSet<_> = left.intersection(&right).collect();

// value should be in the union if and only if it is in either set
for value in 0..u8::MAX {
assert_eq!(
union.contains(&value),
left.contains(&value) && right.contains(&value)
);
}
}

#[proptest]
fn test_intersection_overlaps_u8(
left: Vec<RangeInclusive<u8>>,
right: Vec<RangeInclusive<u8>>,
) {
let left: RangeInclusiveSet<_> = filter_ranges(left).into_iter().collect();
let right: RangeInclusiveSet<_> = filter_ranges(right).into_iter().collect();

let mut union = RangeInclusiveSet::new();
for range in left.intersection(&right) {
// there should not be any overlaps in the ranges returned by the
// intersection
assert!(union.overlapping(&range).next().is_none());
union.insert(range);
}
}

trait RangeInclusiveSetExt<T> {
fn to_vec(&self) -> Vec<RangeInclusive<T>>;
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ extern crate alloc;
pub mod inclusive_map;
pub mod inclusive_set;
pub mod map;
pub(crate) mod operations;
pub mod set;

#[cfg(test)]
Expand Down
Loading

0 comments on commit ba9a3e2

Please sign in to comment.