Skip to content
Snippets Groups Projects
Commit 5fe51129 authored by Eduardo Trujillo's avatar Eduardo Trujillo
Browse files

feat(rangeset): Introduce a range set data structure

parent 28c45be9
No related merge requests found
Checking pipeline status
......@@ -8,6 +8,8 @@ pub mod cli;
pub mod config;
/// Range dictionary data structure
pub mod rangedict;
/// Range set data structure
pub mod rangeset;
/// Ephemeral working directories.
pub mod rundir;
/// String utilities.
......
use std::collections::BTreeMap;
use thiserror::Error;
/// Error type for [`RangeSet`]
#[derive(Error, Debug, PartialEq)]
pub enum RangeSetError {
/// Used when an invalid range is provided (e.g. End is before start).
#[error("Invalid range provided")]
InvalidRange,
/// Used when an insertion would result in an overlap.
#[error("Overlapping ranges")]
RangeOverlap,
}
/// A set of non-overlapping ranges.
#[derive(Debug)]
pub struct RangeSet<RK: Ord + Copy> {
ranges: BTreeMap<RK, RK>, // Maps start -> end
}
impl<RK: Ord + Copy> RangeSet<RK> {
/// Creates a new empty `RangeSet`.
pub fn new() -> Self {
Self {
ranges: BTreeMap::new(),
}
}
/// Inserts a new range `[start, end)`, ensuring no overlaps.
pub fn insert(&mut self, start: RK, end: RK) -> Result<(), RangeSetError> {
if start >= end {
return Err(RangeSetError::InvalidRange);
}
// Find adjacent or overlapping ranges
if let Some((&_prev_start, &prev_end)) = self.ranges.range(..=start).next_back() {
if prev_end >= start {
return Err(RangeSetError::RangeOverlap);
}
}
if let Some((&next_start, _)) = self.ranges.range(start..).next() {
if next_start < end {
return Err(RangeSetError::RangeOverlap);
}
}
self.ranges.insert(start, end);
Ok(())
}
/// Checks if a value exists in any range.
pub fn contains(&self, value: RK) -> bool {
if let Some((&_start, &end)) = self.ranges.range(..=value).next_back() {
return value < end;
}
false
}
/// Removes a range if it exists.
pub fn remove(&mut self, start: RK, end: RK) -> bool {
if let Some(range_end) = self.ranges.get(&start) {
if *range_end == end {
self.ranges.remove(&start);
return true;
}
}
false
}
/// Returns all stored ranges.
pub fn iter(&self) -> impl Iterator<Item = (RK, RK)> + '_ {
self.ranges.iter().map(|(&s, &e)| (s, e))
}
}
impl<RK: Copy + Ord> Default for RangeSet<RK> {
fn default() -> Self {
RangeSet::new()
}
}
#[cfg(test)]
mod tests {
use crate::rangeset::{RangeSet, RangeSetError};
#[test]
fn test_insertions() {
let mut rs = RangeSet::new();
assert_eq!(rs.insert(10, 20), Ok(()));
assert_eq!(rs.insert(30, 40), Ok(()));
assert_eq!(rs.insert(15, 25), Err(RangeSetError::RangeOverlap));
assert_eq!(rs.contains(15), true);
assert_eq!(rs.contains(25), false);
}
#[test]
fn test_removal() {
let mut rs = RangeSet::new();
assert_eq!(rs.insert(10, 20), Ok(()));
assert_eq!(rs.insert(30, 40), Ok(()));
assert_eq!(rs.remove(10, 11), false);
assert_eq!(rs.remove(10, 20), true);
assert_eq!(rs.remove(30, 40), true);
assert_eq!(rs.remove(30, 40), false);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment