Skip to content

Commit

Permalink
Merge branch 'main' into benchmark
Browse files Browse the repository at this point in the history
jeffparsons authored Feb 7, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 3738d36 + ba9a3e2 commit 9d9d66e
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
@@ -2,7 +2,7 @@ name: CI

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

@@ -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
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)
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 }
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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
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;
@@ -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)`.
@@ -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`.
@@ -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::*;
@@ -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| {
@@ -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>>;
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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)]
Loading

0 comments on commit 9d9d66e

Please sign in to comment.