mirror of
https://github.com/tlsnotary/tlsn-utils.git
synced 2026-01-09 12:48:03 -05:00
feat(utils): additional set operations (#39)
* rename traits * subset * intersection * prove set intersection * assert invariants * fix doctest
This commit is contained in:
@@ -13,10 +13,6 @@ libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] }
|
||||
[dependencies.tlsn-utils]
|
||||
path = ".."
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
||||
|
||||
@@ -44,6 +40,18 @@ path = "fuzz_targets/range_diff_set.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "range_intersection_set"
|
||||
path = "fuzz_targets/range_intersection_set.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "range_subset_set"
|
||||
path = "fuzz_targets/range_subset_set.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "set_union_range"
|
||||
path = "fuzz_targets/set_union_range.rs"
|
||||
@@ -62,6 +70,18 @@ path = "fuzz_targets/set_diff_set.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "set_intersection_set"
|
||||
path = "fuzz_targets/set_intersection_set.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "set_subset_set"
|
||||
path = "fuzz_targets/set_subset_set.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "set_diff_range"
|
||||
path = "fuzz_targets/set_diff_range.rs"
|
||||
|
||||
25
utils/fuzz/fuzz_targets/range_intersection_set.rs
Normal file
25
utils/fuzz/fuzz_targets/range_intersection_set.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
#![no_main]
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Range;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use tlsn_utils_fuzz::{assert_invariants, SmallSet};
|
||||
|
||||
use utils::range::*;
|
||||
|
||||
fuzz_target!(|r: (Range<u8>, SmallSet)| {
|
||||
let s1 = r.0;
|
||||
let s2: RangeSet<u8> = r.1.into();
|
||||
|
||||
let h1: HashSet<u8> = HashSet::from_iter(s1.clone());
|
||||
let h2: HashSet<u8> = HashSet::from_iter(s2.iter());
|
||||
|
||||
let intersection = s1.intersection(&s2);
|
||||
let h3: HashSet<u8> = HashSet::from_iter(intersection.iter());
|
||||
|
||||
assert_eq!(h3, h1.intersection(&h2).copied().collect::<HashSet<_>>());
|
||||
|
||||
assert_invariants(intersection);
|
||||
});
|
||||
20
utils/fuzz/fuzz_targets/range_subset_set.rs
Normal file
20
utils/fuzz/fuzz_targets/range_subset_set.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Range;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use tlsn_utils_fuzz::SmallSet;
|
||||
|
||||
use utils::range::*;
|
||||
|
||||
fuzz_target!(|r: (Range<u8>, SmallSet)| {
|
||||
let s1 = r.0;
|
||||
let s2: RangeSet<u8> = r.1.into();
|
||||
|
||||
let h1: HashSet<u8> = HashSet::from_iter(s1.clone());
|
||||
let h2: HashSet<u8> = HashSet::from_iter(s2.iter());
|
||||
|
||||
assert_eq!(s1.is_subset(&s2), h1.is_subset(&h2));
|
||||
});
|
||||
24
utils/fuzz/fuzz_targets/set_intersection_set.rs
Normal file
24
utils/fuzz/fuzz_targets/set_intersection_set.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
#![no_main]
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use tlsn_utils_fuzz::{assert_invariants, SmallSet};
|
||||
|
||||
use utils::range::*;
|
||||
|
||||
fuzz_target!(|r: (SmallSet, SmallSet)| {
|
||||
let s1: RangeSet<u8> = r.0.into();
|
||||
let s2: RangeSet<u8> = r.1.into();
|
||||
|
||||
let h1: HashSet<u8> = HashSet::from_iter(s1.iter());
|
||||
let h2: HashSet<u8> = HashSet::from_iter(s2.iter());
|
||||
|
||||
let intersection = s1.intersection(&s2);
|
||||
let h3: HashSet<u8> = HashSet::from_iter(intersection.iter());
|
||||
|
||||
assert_eq!(h3, h1.intersection(&h2).copied().collect::<HashSet<_>>());
|
||||
|
||||
assert_invariants(intersection);
|
||||
});
|
||||
19
utils/fuzz/fuzz_targets/set_subset_set.rs
Normal file
19
utils/fuzz/fuzz_targets/set_subset_set.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
#![no_main]
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use tlsn_utils_fuzz::SmallSet;
|
||||
|
||||
use utils::range::*;
|
||||
|
||||
fuzz_target!(|r: (SmallSet, SmallSet)| {
|
||||
let s1: RangeSet<u8> = r.0.into();
|
||||
let s2: RangeSet<u8> = r.1.into();
|
||||
|
||||
let h1: HashSet<u8> = HashSet::from_iter(s1.iter());
|
||||
let h2: HashSet<u8> = HashSet::from_iter(s2.iter());
|
||||
|
||||
assert_eq!(s1.is_subset(&s2), h1.is_subset(&h2));
|
||||
});
|
||||
@@ -1,10 +1,8 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::range::{
|
||||
RangeDifference, RangeDisjoint, RangeSet, RangeSubset, RangeSuperset, RangeUnion,
|
||||
};
|
||||
use crate::range::{Difference, Disjoint, RangeSet, Subset, Union};
|
||||
|
||||
impl<T: Copy + Ord> RangeDifference<Range<T>> for Range<T> {
|
||||
impl<T: Copy + Ord> Difference<Range<T>> for Range<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn difference(&self, other: &Range<T>) -> Self::Output {
|
||||
@@ -14,8 +12,8 @@ impl<T: Copy + Ord> RangeDifference<Range<T>> for Range<T> {
|
||||
return RangeSet::from(self.clone());
|
||||
}
|
||||
|
||||
// If other is a superset of self, return an empty set.
|
||||
if other.is_superset(self) {
|
||||
// If other contains self, return an empty set.
|
||||
if self.is_subset(other) {
|
||||
return RangeSet::default();
|
||||
}
|
||||
|
||||
@@ -38,9 +36,9 @@ impl<T: Copy + Ord> RangeDifference<Range<T>> for Range<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeDifference<RangeSet<T>> for Range<T>
|
||||
impl<T: Copy + Ord> Difference<RangeSet<T>> for Range<T>
|
||||
where
|
||||
RangeSet<T>: RangeDifference<Range<T>, Output = RangeSet<T>>,
|
||||
RangeSet<T>: Difference<Range<T>, Output = RangeSet<T>>,
|
||||
{
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
@@ -59,7 +57,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeDifference<Range<T>> for RangeSet<T> {
|
||||
impl<T: Copy + Ord> Difference<Range<T>> for RangeSet<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn difference(&self, other: &Range<T>) -> Self::Output {
|
||||
@@ -80,7 +78,7 @@ impl<T: Copy + Ord> RangeDifference<Range<T>> for RangeSet<T> {
|
||||
break;
|
||||
}
|
||||
// If the current range is entirely contained within other
|
||||
else if other.is_superset(&ranges[i]) {
|
||||
else if ranges[i].is_subset(other) {
|
||||
ranges.remove(i);
|
||||
continue;
|
||||
}
|
||||
@@ -113,7 +111,7 @@ impl<T: Copy + Ord> RangeDifference<Range<T>> for RangeSet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeDifference<RangeSet<T>> for RangeSet<T> {
|
||||
impl<T: Copy + Ord> Difference<RangeSet<T>> for RangeSet<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn difference(&self, other: &RangeSet<T>) -> Self::Output {
|
||||
|
||||
197
utils/src/range/intersection.rs
Normal file
197
utils/src/range/intersection.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
use crate::range::{Intersection, Range, RangeSet};
|
||||
|
||||
impl<T: Copy + Ord> Intersection<Range<T>> for Range<T> {
|
||||
type Output = Option<Range<T>>;
|
||||
|
||||
fn intersection(&self, other: &Range<T>) -> Self::Output {
|
||||
let start = self.start.max(other.start);
|
||||
let end = self.end.min(other.end);
|
||||
|
||||
if start < end {
|
||||
Some(Range { start, end })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> Intersection<RangeSet<T>> for Range<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn intersection(&self, other: &RangeSet<T>) -> Self::Output {
|
||||
let mut set = RangeSet::default();
|
||||
|
||||
for other in &other.ranges {
|
||||
if self.end <= other.start {
|
||||
// `self` is leftward of `other`, so we can break early.
|
||||
break;
|
||||
} else if let Some(intersection) = self.intersection(other) {
|
||||
// Given that `other` contains sorted, non-adjacent, non-intersecting, and non-empty
|
||||
// ranges, the new set will also have these properties.
|
||||
set.ranges.push(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> Intersection<Range<T>> for RangeSet<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn intersection(&self, other: &Range<T>) -> Self::Output {
|
||||
other.intersection(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> Intersection<RangeSet<T>> for RangeSet<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn intersection(&self, other: &RangeSet<T>) -> Self::Output {
|
||||
let mut set = RangeSet::default();
|
||||
|
||||
let mut i = 0;
|
||||
let mut j = 0;
|
||||
|
||||
while i < self.ranges.len() && j < other.ranges.len() {
|
||||
let a = &self.ranges[i];
|
||||
let b = &other.ranges[j];
|
||||
|
||||
if a.end <= b.start {
|
||||
// `a` is leftward of `b`, so we can proceed to the next range in `self`.
|
||||
i += 1;
|
||||
} else if b.end <= a.start {
|
||||
// `b` is leftward of `a`, so we can proceed to the next range in `other`.
|
||||
j += 1;
|
||||
} else if let Some(intersection) = a.intersection(b) {
|
||||
// Given that `self` and `other` contain sorted, non-adjacent, non-intersecting, and
|
||||
// non-empty ranges, the new set will also have these properties.
|
||||
set.ranges.push(intersection);
|
||||
|
||||
if a.end <= b.end {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if b.end <= a.end {
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashSet;
|
||||
|
||||
use itertools::iproduct;
|
||||
|
||||
use crate::range::assert_invariants;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_range_intersection_range() {
|
||||
assert!((0..0).intersection(&(0..0)).is_none());
|
||||
assert!((0..1).intersection(&(0..0)).is_none());
|
||||
assert!((0..0).intersection(&(0..1)).is_none());
|
||||
assert_eq!((0..1).intersection(&(0..1)), Some(0..1));
|
||||
assert_eq!((0..2).intersection(&(0..1)), Some(0..1));
|
||||
assert_eq!((0..1).intersection(&(0..2)), Some(0..1));
|
||||
assert_eq!((0..2).intersection(&(0..2)), Some(0..2));
|
||||
assert_eq!((0..2).intersection(&(1..2)), Some(1..2));
|
||||
assert_eq!((1..2).intersection(&(0..2)), Some(1..2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_intersection_set() {
|
||||
let set = RangeSet::from(vec![0..1, 2..3, 4..5]);
|
||||
|
||||
assert_eq!(set.intersection(&(0..0)), RangeSet::default());
|
||||
assert_eq!(set.intersection(&(0..1)), RangeSet::from(vec![0..1]));
|
||||
assert_eq!(set.intersection(&(0..2)), RangeSet::from(vec![0..1]));
|
||||
assert_eq!(set.intersection(&(0..3)), RangeSet::from(vec![0..1, 2..3]));
|
||||
assert_eq!(set.intersection(&(0..4)), RangeSet::from(vec![0..1, 2..3]));
|
||||
assert_eq!(set.intersection(&(1..3)), RangeSet::from(vec![2..3]));
|
||||
assert_eq!(
|
||||
set.intersection(&(0..6)),
|
||||
RangeSet::from(vec![0..1, 2..3, 4..5])
|
||||
);
|
||||
assert_eq!(
|
||||
set.intersection(&(0..6)),
|
||||
RangeSet::from(vec![0..1, 2..3, 4..5])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_intersection_set() {
|
||||
let set = RangeSet::from(vec![0..1, 2..3, 5..6]);
|
||||
|
||||
assert_eq!(set.intersection(&RangeSet::default()), RangeSet::default());
|
||||
assert_eq!(
|
||||
set.intersection(&RangeSet::from(vec![1..2])),
|
||||
RangeSet::default()
|
||||
);
|
||||
assert_eq!(
|
||||
set.intersection(&RangeSet::from(vec![3..5])),
|
||||
RangeSet::default()
|
||||
);
|
||||
assert_eq!(
|
||||
set.intersection(&RangeSet::from(vec![7..8])),
|
||||
RangeSet::default()
|
||||
);
|
||||
assert_eq!(
|
||||
set.intersection(&RangeSet::from(vec![0..1])),
|
||||
RangeSet::from(vec![0..1])
|
||||
);
|
||||
assert_eq!(
|
||||
set.intersection(&RangeSet::from(vec![0..2])),
|
||||
RangeSet::from(vec![0..1])
|
||||
);
|
||||
assert_eq!(
|
||||
set.intersection(&RangeSet::from(vec![0..3])),
|
||||
RangeSet::from(vec![0..1, 2..3])
|
||||
);
|
||||
assert_eq!(set.intersection(&RangeSet::from(vec![0..6])), set);
|
||||
assert_eq!(
|
||||
set.intersection(&RangeSet::from(vec![1..6])),
|
||||
RangeSet::from(vec![2..3, 5..6])
|
||||
);
|
||||
assert_eq!(
|
||||
set.intersection(&RangeSet::from(vec![2..3, 5..6])),
|
||||
RangeSet::from(vec![2..3, 5..6])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "expensive"]
|
||||
fn test_prove_set_intersection_set_8x2_8x2() {
|
||||
for (xs, xe, ys, ye, ws, we, zs, ze) in
|
||||
iproduct!(0..8, 0..8, 0..8, 0..8, 0..8, 0..8, 0..8, 0..8)
|
||||
{
|
||||
let s1 = RangeSet::new(&[(xs..xe), (ys..ye)]);
|
||||
let s2 = RangeSet::new(&[(ws..we), (zs..ze)]);
|
||||
|
||||
let h1 = s1.iter().collect::<HashSet<_>>();
|
||||
let h2 = s2.iter().collect::<HashSet<_>>();
|
||||
|
||||
let actual = s1.intersection(&s2);
|
||||
let h3 = HashSet::<usize>::from_iter(actual.iter());
|
||||
|
||||
assert_invariants(&actual);
|
||||
|
||||
assert_eq!(
|
||||
h3,
|
||||
h1.intersection(&h2).copied().collect::<HashSet<_>>(),
|
||||
"{:?} {:?} {:?} {:?} => {:?}",
|
||||
xs..xe,
|
||||
ys..ye,
|
||||
ws..we,
|
||||
zs..ze,
|
||||
h3
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
mod difference;
|
||||
mod index;
|
||||
mod intersection;
|
||||
mod subset;
|
||||
mod union;
|
||||
|
||||
pub use index::IndexRanges;
|
||||
@@ -38,7 +40,6 @@ use std::ops::{Add, Range, Sub};
|
||||
/// assert_eq!(a.union(&(0..0)), RangeSet::from([10..20]));
|
||||
///
|
||||
/// // Comparison
|
||||
/// assert!(a.is_superset(&(15..18)));
|
||||
/// assert!(a.is_subset(&(0..30)));
|
||||
/// assert!(a.is_disjoint(&(0..10)));
|
||||
/// assert_eq!(a.clone(), RangeSet::from(a));
|
||||
@@ -98,7 +99,7 @@ impl<T: Copy + Ord> RangeSet<T> {
|
||||
/// The `RangeSet` is constructed by computing the union of the given ranges.
|
||||
pub fn new(ranges: &[Range<T>]) -> Self
|
||||
where
|
||||
Self: RangeUnion<Range<T>, Output = Self>,
|
||||
Self: Union<Range<T>, Output = Self>,
|
||||
{
|
||||
let mut set = Self::default();
|
||||
|
||||
@@ -384,25 +385,25 @@ impl<T: Copy + Ord> ToRangeSet<T> for Range<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RangeDisjoint<Rhs> {
|
||||
pub trait Disjoint<Rhs> {
|
||||
/// Returns `true` if the range is disjoint with `other`.
|
||||
#[must_use]
|
||||
fn is_disjoint(&self, other: &Rhs) -> bool;
|
||||
}
|
||||
|
||||
pub trait RangeSuperset<Rhs> {
|
||||
/// Returns `true` if `self` is a superset of `other`.
|
||||
pub trait Contains<Rhs> {
|
||||
/// Returns `true` if `self` contains `other`.
|
||||
#[must_use]
|
||||
fn is_superset(&self, other: &Rhs) -> bool;
|
||||
fn contains(&self, other: &Rhs) -> bool;
|
||||
}
|
||||
|
||||
pub trait RangeSubset<Rhs> {
|
||||
pub trait Subset<Rhs> {
|
||||
/// Returns `true` if `self` is a subset of `other`.
|
||||
#[must_use]
|
||||
fn is_subset(&self, other: &Rhs) -> bool;
|
||||
}
|
||||
|
||||
pub trait RangeDifference<Rhs> {
|
||||
pub trait Difference<Rhs> {
|
||||
type Output;
|
||||
|
||||
/// Returns the set difference of `self` and `other`.
|
||||
@@ -410,7 +411,7 @@ pub trait RangeDifference<Rhs> {
|
||||
fn difference(&self, other: &Rhs) -> Self::Output;
|
||||
}
|
||||
|
||||
pub trait RangeUnion<Rhs> {
|
||||
pub trait Union<Rhs> {
|
||||
type Output;
|
||||
|
||||
/// Returns the set union of `self` and `other`.
|
||||
@@ -418,6 +419,14 @@ pub trait RangeUnion<Rhs> {
|
||||
fn union(&self, other: &Rhs) -> Self::Output;
|
||||
}
|
||||
|
||||
pub trait Intersection<Rhs> {
|
||||
type Output;
|
||||
|
||||
/// Returns the set intersection of `self` and `other`.
|
||||
#[must_use]
|
||||
fn intersection(&self, other: &Rhs) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A type which successor and predecessor operations can be performed on.
|
||||
///
|
||||
/// Similar to `std::iter::Step`, but not nightly-only.
|
||||
@@ -447,52 +456,37 @@ macro_rules! impl_step {
|
||||
|
||||
impl_step!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
|
||||
|
||||
impl<T: Copy + Ord> RangeDisjoint<Range<T>> for Range<T> {
|
||||
impl<T: Copy + Ord> Disjoint<Range<T>> for Range<T> {
|
||||
fn is_disjoint(&self, other: &Range<T>) -> bool {
|
||||
self.start >= other.end || self.end <= other.start
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeDisjoint<RangeSet<T>> for Range<T> {
|
||||
impl<T: Copy + Ord> Disjoint<RangeSet<T>> for Range<T> {
|
||||
fn is_disjoint(&self, other: &RangeSet<T>) -> bool {
|
||||
other.ranges.iter().all(|range| self.is_disjoint(range))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeDisjoint<RangeSet<T>> for RangeSet<T> {
|
||||
impl<T: Copy + Ord> Disjoint<RangeSet<T>> for RangeSet<T> {
|
||||
fn is_disjoint(&self, other: &RangeSet<T>) -> bool {
|
||||
self.ranges.iter().all(|range| range.is_disjoint(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeDisjoint<Range<T>> for RangeSet<T> {
|
||||
impl<T: Copy + Ord> Disjoint<Range<T>> for RangeSet<T> {
|
||||
fn is_disjoint(&self, other: &Range<T>) -> bool {
|
||||
other.is_disjoint(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeSuperset<Range<T>> for Range<T> {
|
||||
fn is_superset(&self, other: &Range<T>) -> bool {
|
||||
self.start <= other.start && self.end >= other.end
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeSuperset<RangeSet<T>> for Range<T> {
|
||||
fn is_superset(&self, other: &RangeSet<T>) -> bool {
|
||||
other.ranges.iter().all(|range| self.is_superset(range))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeSubset<Range<T>> for Range<T> {
|
||||
fn is_subset(&self, other: &Range<T>) -> bool {
|
||||
self.start >= other.start && self.end <= other.end
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeSubset<RangeSet<T>> for Range<T> {
|
||||
fn is_subset(&self, other: &RangeSet<T>) -> bool {
|
||||
other.ranges.iter().any(|range| self.is_subset(range))
|
||||
}
|
||||
/// Asserts that the ranges of the given set are sorted, non-adjacent, non-intersecting, and non-empty.
|
||||
#[cfg(test)]
|
||||
pub fn assert_invariants<T: Copy + Ord>(set: &RangeSet<T>) {
|
||||
assert!(set.ranges.windows(2).all(|w| w[0].start < w[1].start
|
||||
&& w[0].end < w[1].start
|
||||
&& w[0].start < w[0].end
|
||||
&& w[1].start < w[1].end));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -523,50 +517,6 @@ mod tests {
|
||||
assert!(!a.is_disjoint(&(10..20)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_superset() {
|
||||
let a = 10..20;
|
||||
|
||||
// rightward
|
||||
assert!(!a.is_superset(&(20..30)));
|
||||
// rightward aligned
|
||||
assert!(!a.is_superset(&(19..25)));
|
||||
// leftward
|
||||
assert!(!a.is_superset(&(0..10)));
|
||||
// leftward aligned
|
||||
assert!(!a.is_superset(&(5..11)));
|
||||
// rightward subset
|
||||
assert!(a.is_superset(&(15..20)));
|
||||
// leftward subset
|
||||
assert!(a.is_superset(&(10..15)));
|
||||
// superset
|
||||
assert!(!a.is_superset(&(5..25)));
|
||||
// equal
|
||||
assert!(a.is_superset(&(10..20)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_subset() {
|
||||
let a = 10..20;
|
||||
|
||||
// rightward
|
||||
assert!(!a.is_subset(&(20..30)));
|
||||
// rightward aligned
|
||||
assert!(!a.is_subset(&(19..25)));
|
||||
// leftward
|
||||
assert!(!a.is_subset(&(0..10)));
|
||||
// leftward aligned
|
||||
assert!(!a.is_subset(&(5..11)));
|
||||
// rightward subset
|
||||
assert!(!a.is_subset(&(15..20)));
|
||||
// leftward subset
|
||||
assert!(!a.is_subset(&(10..15)));
|
||||
// superset
|
||||
assert!(a.is_subset(&(5..25)));
|
||||
// equal
|
||||
assert!(a.is_subset(&(10..20)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_set_iter() {
|
||||
let a = RangeSet::from([(10..20), (30..40), (50..60)]);
|
||||
|
||||
166
utils/src/range/subset.rs
Normal file
166
utils/src/range/subset.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
use crate::range::{Range, RangeSet, Subset};
|
||||
|
||||
impl<T: Copy + Ord> Subset<Range<T>> for Range<T> {
|
||||
fn is_subset(&self, other: &Range<T>) -> bool {
|
||||
self.start >= other.start && self.end <= other.end
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> Subset<RangeSet<T>> for Range<T> {
|
||||
fn is_subset(&self, other: &RangeSet<T>) -> bool {
|
||||
if self.is_empty() {
|
||||
// empty range is subset of any set
|
||||
return true;
|
||||
} else if other.ranges.is_empty() {
|
||||
// non-empty range is not subset of empty set
|
||||
return false;
|
||||
}
|
||||
|
||||
for other in &other.ranges {
|
||||
if self.start >= other.end {
|
||||
// self is rightward of other, proceed to next other
|
||||
continue;
|
||||
} else {
|
||||
return self.is_subset(other);
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> Subset<Range<T>> for RangeSet<T> {
|
||||
fn is_subset(&self, other: &Range<T>) -> bool {
|
||||
let (Some(start), Some(end)) = (self.min(), self.end()) else {
|
||||
// empty set is subset of any set
|
||||
return true;
|
||||
};
|
||||
|
||||
// check if the outer bounds of this set are within the range
|
||||
start >= other.start && end <= other.end
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> Subset<RangeSet<T>> for RangeSet<T> {
|
||||
fn is_subset(&self, other: &RangeSet<T>) -> bool {
|
||||
if self.ranges.is_empty() {
|
||||
// empty set is subset of any set
|
||||
return true;
|
||||
} else if other.ranges.is_empty() {
|
||||
// non-empty set is not subset of empty set
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
let mut j = 0;
|
||||
|
||||
while i < self.ranges.len() && j < other.ranges.len() {
|
||||
let a = &self.ranges[i];
|
||||
let b = &other.ranges[j];
|
||||
|
||||
if a.start >= b.end {
|
||||
// a is rightward of b, proceed to next b
|
||||
j += 1;
|
||||
} else if a.is_subset(b) {
|
||||
// a is subset of b, proceed to next a
|
||||
i += 1;
|
||||
} else {
|
||||
// self contains values not in other
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've reached the end of self, then all ranges are contained in other.
|
||||
i == self.ranges.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_range_subset_of_range() {
|
||||
let a = 10..20;
|
||||
|
||||
// empty
|
||||
assert!(!a.is_subset(&(0..0)));
|
||||
// rightward
|
||||
assert!(!a.is_subset(&(20..30)));
|
||||
// rightward aligned
|
||||
assert!(!a.is_subset(&(19..25)));
|
||||
// leftward
|
||||
assert!(!a.is_subset(&(0..10)));
|
||||
// leftward aligned
|
||||
assert!(!a.is_subset(&(5..11)));
|
||||
// rightward subset
|
||||
assert!(!a.is_subset(&(15..20)));
|
||||
// leftward subset
|
||||
assert!(!a.is_subset(&(10..15)));
|
||||
// superset
|
||||
assert!(a.is_subset(&(5..25)));
|
||||
// equal
|
||||
assert!(a.is_subset(&(10..20)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_subset_of_rangeset() {
|
||||
let a = 10..20;
|
||||
|
||||
let empty = RangeSet::<i32>::default();
|
||||
|
||||
// empty set is subset of any range
|
||||
assert!(empty.is_subset(&a));
|
||||
// non-empty range is not subset of empty set
|
||||
assert!(!a.is_subset(&empty));
|
||||
|
||||
assert!(a.is_subset(&RangeSet::from(vec![0..20])));
|
||||
assert!(a.is_subset(&RangeSet::from(vec![10..20])));
|
||||
assert!(!a.is_subset(&RangeSet::from(vec![0..10])));
|
||||
assert!(!a.is_subset(&RangeSet::from(vec![20..30])));
|
||||
assert!(!a.is_subset(&RangeSet::from(vec![0..10, 20..30])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rangeset_subset_of_range() {
|
||||
let a = RangeSet::from(vec![10..20, 30..40]);
|
||||
|
||||
assert!(!a.is_subset(&(0..0)));
|
||||
assert!(!a.is_subset(&(0..10)));
|
||||
assert!(!a.is_subset(&(0..15)));
|
||||
assert!(!a.is_subset(&(0..20)));
|
||||
assert!(!a.is_subset(&(20..30)));
|
||||
assert!(!a.is_subset(&(20..40)));
|
||||
assert!(!a.is_subset(&(20..50)));
|
||||
assert!(!a.is_subset(&(30..40)));
|
||||
assert!(!a.is_subset(&(11..40)));
|
||||
assert!(!a.is_subset(&(10..39)));
|
||||
|
||||
assert!(a.is_subset(&(10..40)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rangeset_subset_of_rangeset() {
|
||||
let empty = RangeSet::<i32>::default();
|
||||
|
||||
// empty set is subset of itself
|
||||
assert!(empty.is_subset(&empty));
|
||||
// empty set is subset of non-empty set
|
||||
assert!(empty.is_subset(&RangeSet::from(vec![10..20])));
|
||||
// non-empty set is not subset of empty set
|
||||
assert!(!RangeSet::from(vec![10..20]).is_subset(&empty));
|
||||
|
||||
let a = RangeSet::from(vec![10..20, 30..40]);
|
||||
|
||||
// equal
|
||||
assert!(a.is_subset(&a));
|
||||
|
||||
assert!(a.is_subset(&RangeSet::from(vec![0..20, 30..50])));
|
||||
assert!(a.is_subset(&RangeSet::from(vec![10..20, 30..40, 40..50])));
|
||||
assert!(!a.is_subset(&RangeSet::from(vec![10..20])));
|
||||
assert!(!a.is_subset(&RangeSet::from(vec![30..40])));
|
||||
assert!(!a.is_subset(&RangeSet::from(vec![0..20, 30..39])));
|
||||
assert!(!a.is_subset(&RangeSet::from(vec![10..19, 30..40])));
|
||||
assert!(!a.is_subset(&RangeSet::from(vec![0..10, 30..40])));
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::range::{RangeDisjoint, RangeSet, RangeSuperset, RangeUnion};
|
||||
use crate::range::{Disjoint, RangeSet, Subset, Union};
|
||||
|
||||
impl<T: Copy + Ord> RangeUnion<Range<T>> for Range<T> {
|
||||
impl<T: Copy + Ord> Union<Range<T>> for Range<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn union(&self, other: &Range<T>) -> Self::Output {
|
||||
// If the two are equal, or other is a subset, return self.
|
||||
if self == other || self.is_superset(other) {
|
||||
if self == other || other.is_subset(self) {
|
||||
return RangeSet::from(self.clone());
|
||||
}
|
||||
|
||||
// If other is a superset, return other.
|
||||
if other.is_superset(self) {
|
||||
// If other contains self, return other.
|
||||
if self.is_subset(other) {
|
||||
return RangeSet::from(other.clone());
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ impl<T: Copy + Ord> RangeUnion<Range<T>> for Range<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeUnion<RangeSet<T>> for Range<T> {
|
||||
impl<T: Copy + Ord> Union<RangeSet<T>> for Range<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn union(&self, other: &RangeSet<T>) -> Self::Output {
|
||||
@@ -74,7 +74,7 @@ impl<T: Copy + Ord> RangeUnion<RangeSet<T>> for Range<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeUnion<Range<T>> for RangeSet<T> {
|
||||
impl<T: Copy + Ord> Union<Range<T>> for RangeSet<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn union(&self, other: &Range<T>) -> Self::Output {
|
||||
@@ -82,7 +82,7 @@ impl<T: Copy + Ord> RangeUnion<Range<T>> for RangeSet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> RangeUnion<RangeSet<T>> for RangeSet<T> {
|
||||
impl<T: Copy + Ord> Union<RangeSet<T>> for RangeSet<T> {
|
||||
type Output = RangeSet<T>;
|
||||
|
||||
fn union(&self, other: &RangeSet<T>) -> Self::Output {
|
||||
|
||||
Reference in New Issue
Block a user