Skip to content

Commit

Permalink
Merge branch 'main' into benchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffparsons authored Feb 7, 2024
2 parents 3738d36 + ba9a3e2 commit 9d9d66e
Show file tree
Hide file tree
Showing 8 changed files with 457 additions and 16 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
push:
branches: [master, staging, trying]
branches: [main, staging, trying]
pull_request:
branches: [main]

Expand All @@ -16,7 +16,7 @@ jobs:
matrix:
toolchain:
# Don't forget to update the README when bumping MSRV.
- 1.65.0
- 1.66.0
- stable
- beta
- nightly
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
### v1.5.0 (unreleased)

- **Changes**:
- Bump minimum supported Rust version to 1.65.0 (released 2022-11-03). This is for compatibility with new versions of some of rangemap's test dependencies.
- Bump minimum supported Rust version to 1.66.0 (released 2022-12-15). This is to gain access to `BTreeMap::first_key_value` and `BTreeMap::last_key_value`, and for compatibility with new versions of some of rangemap's test dependencies.
- TODO: All of xfbs's PRs!

### v1.4.0 (2023-09-19)
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Map and set data structures whose keys are stored as ranges.
Contiguous and overlapping ranges that map to the same value are coalesced into a single range.
"""
categories = ["data-structures"]
rust-version = "1.65.0"
rust-version = "1.66.0"

[dependencies]
serde = { version = "1", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![Crate](https://img.shields.io/crates/v/rangemap.svg)](https://crates.io/crates/rangemap)
[![Docs](https://docs.rs/rangemap/badge.svg)](https://docs.rs/rangemap)
[![Build status](https://github.com/jeffparsons/rangemap/workflows/CI/badge.svg)](https://github.com/jeffparsons/rangemap/actions)
[![Rust](https://img.shields.io/badge/rust-1.65.0%2B-blue.svg?maxAge=3600)](https://github.com/jeffparsons/rangemap) <!-- Don't forget to update the GitHub actions config when bumping minimum Rust version. -->
[![Rust](https://img.shields.io/badge/rust-1.66.0%2B-blue.svg?maxAge=3600)](https://github.com/jeffparsons/rangemap) <!-- Don't forget to update the GitHub actions config when bumping minimum Rust version. -->

[`RangeMap`] and [`RangeInclusiveMap`] are map data structures whose keys
are stored as ranges. Contiguous and overlapping ranges that map to the same
Expand Down
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 9d9d66e

Please sign in to comment.