utils: add RangeSet early implementation based on RBTree
This commit is contained in:
@@ -2,6 +2,8 @@ use num_traits::Signed;
|
||||
use std::fmt::Display;
|
||||
use std::ops::{Add, AddAssign};
|
||||
|
||||
pub mod range;
|
||||
|
||||
const POW10MAX: usize = u64::MAX.ilog10() as usize;
|
||||
pub const POW10: [u64; POW10MAX] = pow10_lut();
|
||||
|
||||
@@ -105,6 +107,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_pow10() {
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..POW10MAX {
|
||||
assert_eq!(POW10[i], 10u64.pow(i as u32))
|
||||
}
|
||||
|
||||
124
utils/misc/src/range.rs
Normal file
124
utils/misc/src/range.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
use intrusive_collections::Bound as IBound;
|
||||
use intrusive_collections::{KeyAdapter, intrusive_adapter};
|
||||
use intrusive_collections::{RBTreeLink, rbtree::RBTree};
|
||||
use std::cmp::{max, min};
|
||||
use std::ops::{Add, Bound, RangeBounds, Sub};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NaiveRange<T> {
|
||||
link: RBTreeLink,
|
||||
pub start: T,
|
||||
pub end: T,
|
||||
}
|
||||
|
||||
intrusive_adapter!(
|
||||
pub NaiveRangeAdapter<T> = Box<NaiveRange<T>>: NaiveRange<T> { link: RBTreeLink }
|
||||
);
|
||||
|
||||
impl<'a, T: Ord + Clone> KeyAdapter<'a> for NaiveRangeAdapter<T> {
|
||||
type Key = T;
|
||||
fn get_key(&self, node: &'a NaiveRange<T>) -> T {
|
||||
node.start.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord> NaiveRange<T> {
|
||||
fn new(start: T, end: T) -> Self {
|
||||
Self {
|
||||
link: RBTreeLink::new(),
|
||||
start,
|
||||
end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RangeSet<T: Ord> {
|
||||
pub store: RBTree<NaiveRangeAdapter<T>>,
|
||||
}
|
||||
|
||||
fn normalize_range<R: RangeBounds<T>, T: Copy + Ord + Add<Output = T> + From<u8>>(
|
||||
r: &R,
|
||||
) -> NaiveRange<T> {
|
||||
let start = match r.start_bound() {
|
||||
Bound::Included(x) => *x,
|
||||
Bound::Excluded(x) => *x + T::from(1),
|
||||
Bound::Unbounded => panic!(),
|
||||
};
|
||||
let end = match r.end_bound() {
|
||||
Bound::Included(x) => *x + T::from(1),
|
||||
Bound::Excluded(x) => *x,
|
||||
Bound::Unbounded => panic!(),
|
||||
};
|
||||
|
||||
NaiveRange::new(start, end)
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord + Add<Output = T> + Sub<Output = T> + From<u8>> Default for RangeSet<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord + Add<Output = T> + Sub<Output = T> + From<u8>> RangeSet<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
store: RBTree::new(NaiveRangeAdapter::<T>::new()),
|
||||
}
|
||||
}
|
||||
pub fn add<R: RangeBounds<T>>(&mut self, r: &R) {
|
||||
//normalize r to Range
|
||||
let mut new_r = normalize_range(r);
|
||||
|
||||
// Find the position of nearest entry >= and <= than new_r's start
|
||||
let mut cur = self.store.lower_bound_mut(IBound::Included(&new_r.start));
|
||||
|
||||
while let Some(val) = cur.get()
|
||||
&& new_r.end >= val.start
|
||||
{
|
||||
// then remove them and update our new range
|
||||
new_r.start = min(new_r.start, val.start);
|
||||
new_r.end = max(new_r.end, val.end);
|
||||
cur.remove(); // moves the cursor ahead
|
||||
}
|
||||
|
||||
// Move the cursor back one (this will be the element before our insertion point, 'below' start)
|
||||
cur.move_prev();
|
||||
while let Some(val) = cur.get()
|
||||
&& new_r.start <= val.end
|
||||
{
|
||||
new_r.start = min(new_r.start, val.start);
|
||||
new_r.end = max(new_r.end, val.end);
|
||||
cur.remove();
|
||||
cur.move_prev();
|
||||
}
|
||||
|
||||
// We are already in position, we just checked the previous element and it's before us
|
||||
// since we fell out of the loop, so we need to go after this position
|
||||
cur.insert_after(Box::new(new_r));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_ranges() {
|
||||
let mut set = RangeSet::new();
|
||||
// edge cases are tricky to construct, run the aoc2025 day 5 puzzle and compare :)
|
||||
set.add(&(0..10));
|
||||
set.add(&(100..1000));
|
||||
set.add(&(50..100));
|
||||
set.add(&(50..100));
|
||||
set.add(&(1001..2000));
|
||||
set.add(&(2000..2001));
|
||||
set.add(&(-10..-2));
|
||||
set.add(&(-1..-1));
|
||||
let end = set
|
||||
.store
|
||||
.iter()
|
||||
.map(|r| (r.start, r.end))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(end, [(-10, 10), (50, 2001)])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user