augmented_rbtree/interval_tree.rs
1//! A production-grade interval tree built on the augmented red-black tree.
2//!
3//! An interval tree stores closed intervals `[lo, hi]` and supports efficient
4//! overlap and containment queries in O(log n) average / O(k log n) for k results.
5//!
6//! ## How it works
7//!
8//! Each node stores an interval `[lo, hi]`. The tree is ordered by `lo` (left endpoint).
9//! Each subtree tracks the maximum `hi` value it contains. This augmentation makes it
10//! possible to prune entire subtrees during overlap queries without visiting every node.
11//!
12//! ## Example
13//!
14//! ```
15//! use augmented_rbtree::interval_tree::{Interval, IntervalTree};
16//!
17//! let mut tree = IntervalTree::new();
18//! tree.insert(Interval::new(1, 5), "task A");
19//! tree.insert(Interval::new(3, 8), "task B");
20//! tree.insert(Interval::new(10, 15), "task C");
21//!
22//! // All intervals overlapping [4, 6]
23//! let matches: Vec<_> = tree.query_overlap(&4, &6).collect();
24//! assert_eq!(matches.len(), 2); // "task A" [1,5] and "task B" [3,8] overlap [4,6]
25//!
26//! // Check if any interval contains a point
27//! assert!(tree.any_contains_point(&4));
28//! assert!(!tree.any_contains_point(&9));
29//! ```
30
31use core::{borrow::Borrow, fmt, marker::PhantomData};
32
33use crate::{
34 Augment, AugmentedRBTree, TreeLocation,
35 alloc_proxy::proxy::{Allocator, Global},
36 search::{InOrderIter, InOrderPruningPolicy},
37};
38
39// ============================================================================
40// Interval type
41// ============================================================================
42
43/// A closed interval `[lo, hi]` used as a key in [`IntervalTree`].
44///
45/// Intervals are ordered by their lower endpoint (`lo`). Two intervals with the
46/// same `lo` are further ordered by their upper endpoint (`hi`).
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct Interval<T> {
49 /// Inclusive lower bound.
50 pub lo: T,
51 /// Inclusive upper bound.
52 pub hi: T,
53}
54
55impl<T: Ord> Interval<T> {
56 /// Creates a new interval `[lo, hi]`.
57 ///
58 /// # Panics
59 ///
60 /// Panics if `lo > hi`.
61 #[must_use]
62 pub fn new(lo: T, hi: T) -> Self {
63 assert!(lo <= hi, "Interval requires lo <= hi");
64 Self { lo, hi }
65 }
66
67 /// Returns `true` if this interval overlaps with `other`.
68 ///
69 /// Two intervals overlap if they share at least one point:
70 /// `[a, b]` overlaps `[c, d]` iff `a <= d && c <= b`.
71 #[must_use]
72 pub fn overlaps(&self, other: &Self) -> bool {
73 self.lo <= other.hi && other.lo <= self.hi
74 }
75
76 /// Returns `true` if this interval contains `point`.
77 #[must_use]
78 pub fn contains_point(&self, point: &T) -> bool {
79 &self.lo <= point && point <= &self.hi
80 }
81
82 /// Returns the length of the interval as `hi - lo`.
83 #[must_use]
84 pub fn len(&self) -> T
85 where
86 T: core::ops::Sub<Output = T> + Copy,
87 {
88 self.hi - self.lo
89 }
90
91 /// Returns `true` if `lo == hi` (a degenerate point interval).
92 #[must_use]
93 pub fn is_point(&self) -> bool
94 where
95 T: PartialEq,
96 {
97 self.lo == self.hi
98 }
99}
100
101impl<T: Ord> PartialOrd for Interval<T> {
102 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
103 Some(self.cmp(other))
104 }
105}
106
107impl<T: Ord> Ord for Interval<T> {
108 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
109 self.lo.cmp(&other.lo).then_with(|| self.hi.cmp(&other.hi))
110 }
111}
112
113impl<T: fmt::Display> fmt::Display for Interval<T> {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 write!(f, "[{}, {}]", self.lo, self.hi)
116 }
117}
118
119// ============================================================================
120// Augmentation: max endpoint per subtree
121// ============================================================================
122/// The augmentation tracks the maximum `hi` value in each subtree, enabling efficient
123/// pruning during overlap queries.
124#[derive(Debug, Clone, Copy)]
125pub struct MaxHi<T>(core::marker::PhantomData<T>);
126
127impl<T: Ord + Clone + Default, V> Augment<Interval<T>, V> for MaxHi<T> {
128 /// Maximum `hi` value in this subtree. `None` only for the transient uninitialized state.
129 type Stats = T;
130
131 fn compute(
132 key: &Interval<T>,
133 _value: &V,
134 left: Option<(&Interval<T>, &V, &Self::Stats)>,
135 right: Option<(&Interval<T>, &V, &Self::Stats)>,
136 ) -> Self::Stats {
137 let mut max = key.hi.clone();
138 if let Some((_, _, l_max)) = left {
139 if l_max > &max {
140 max = l_max.clone();
141 }
142 }
143 if let Some((_, _, r_max)) = right {
144 if r_max > &max {
145 max = r_max.clone();
146 }
147 }
148 max
149 }
150}
151
152/// internal details
153pub mod internal_details {
154 use core::marker::PhantomData;
155
156 /// An internal pruning strategy that governs interval intersection queries.
157 #[derive(Debug)]
158 pub struct IntervalOverlapPolicy<T, KBound> {
159 pub(crate) lo: KBound,
160 pub(crate) hi: KBound,
161 pub(crate) _marker: PhantomData<T>,
162 }
163}
164
165impl<T: Ord, KBound, V> InOrderPruningPolicy<Interval<T>, V, T>
166 for internal_details::IntervalOverlapPolicy<T, KBound>
167where
168 KBound: Borrow<T>,
169{
170 #[inline]
171 fn is_match(&self, key: &Interval<T>, _value: &V, _stats: &T) -> bool {
172 key.lo <= *self.hi.borrow() && key.hi >= *self.lo.borrow()
173 }
174
175 #[inline]
176 fn should_explore_left(
177 &self,
178 left: (&Interval<T>, &V, &T),
179 _current: (&Interval<T>, &V, &T),
180 ) -> bool {
181 *left.2 >= *self.lo.borrow()
182 }
183
184 #[inline]
185 fn should_explore_right(
186 &self,
187 right: (&Interval<T>, &V, &T),
188 current: (&Interval<T>, &V, &T),
189 ) -> bool {
190 *right.2 >= *self.lo.borrow() && current.0.lo <= *self.hi.borrow()
191 }
192}
193
194// ============================================================================
195// IntervalTree
196// ============================================================================
197
198/// Am interval tree that supports O(log n) overlap queries.
199///
200/// Built on an augmented red-black tree where each subtree tracks the maximum
201/// upper bound (`hi`) of all intervals it contains. This enables efficient
202/// pruning during overlap queries.
203///
204/// # Type Parameters
205///
206/// - `T`: The endpoint type. Must be `Ord + Clone`.
207/// - `V`: The value associated with each interval.
208/// - `A`: Allocator (defaults to `Global`]).
209///
210/// # Examples
211///
212/// ```
213/// use augmented_rbtree::interval_tree::{Interval, IntervalTree};
214///
215/// let mut tree = IntervalTree::new();
216/// tree.insert(Interval::new(1, 5), "a");
217/// tree.insert(Interval::new(3, 9), "b");
218/// tree.insert(Interval::new(7, 10), "c");
219///
220/// let overlapping: Vec<_> = tree.query_overlap(4, 8).map(|(iv, v)| (*iv, *v)).collect();
221/// assert_eq!(overlapping.len(), 3);
222/// ```
223pub struct IntervalTree<T: Ord + Clone + Default, V, A: Allocator = Global> {
224 inner: AugmentedRBTree<Interval<T>, V, MaxHi<T>, A>,
225}
226
227impl<T: Ord + Clone + Default, V> IntervalTree<T, V> {
228 /// Creates a new, empty interval tree.
229 #[must_use]
230 pub fn new() -> Self {
231 Self {
232 inner: AugmentedRBTree::new(),
233 }
234 }
235}
236
237impl<T: Ord + Clone + Default, V> Default for IntervalTree<T, V> {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243impl<T: Ord + Clone + Default, V, A: Allocator> IntervalTree<T, V, A> {
244 /// Creates a new interval tree with the given allocator.
245 #[must_use]
246 pub fn new_in(alloc: A) -> Self {
247 Self {
248 inner: AugmentedRBTree::new_in(alloc),
249 }
250 }
251
252 /// Returns a reference to the underlying augmented red-black tree.
253 /// This can be used for advanced operations or visualization.
254 #[must_use]
255 pub fn inner_tree(&self) -> &AugmentedRBTree<Interval<T>, V, MaxHi<T>, A> {
256 &self.inner
257 }
258
259 /// Inserts an interval-value pair.
260 ///
261 /// If the exact interval already exists, the old value is replaced and returned.
262 pub fn insert(&mut self, interval: Interval<T>, value: V) -> Option<V> {
263 self.inner.insert(interval, value)
264 }
265
266 /// Removes an interval from the tree, returning its value if it existed.
267 pub fn remove(&mut self, interval: &Interval<T>) -> Option<V> {
268 self.inner.remove(interval)
269 }
270
271 /// Returns a reference to the value associated with `interval`, if present.
272 #[must_use]
273 pub fn get(&self, interval: &Interval<T>) -> Option<&V> {
274 self.inner.get(interval)
275 }
276
277 /// Returns `true` if the tree contains the exact interval.
278 #[must_use]
279 pub fn contains(&self, interval: &Interval<T>) -> bool {
280 self.inner.contains_key(interval)
281 }
282
283 /// Returns the number of intervals in the tree.
284 #[must_use]
285 pub fn len(&self) -> usize {
286 self.inner.len()
287 }
288
289 /// Returns `true` if the tree is empty.
290 #[must_use]
291 pub fn is_empty(&self) -> bool {
292 self.inner.is_empty()
293 }
294
295 /// Returns an iterator over all `(interval, value)` pairs in sorted order (by `lo`, then `hi`).
296 pub fn iter(&self) -> impl Iterator<Item = (&Interval<T>, &V)> {
297 self.inner.iter().map(|(k, v, _)| (k, v))
298 }
299
300 /// Returns an iterator over all intervals that **overlap** with `[lo, hi]`.
301 ///
302 /// Complexity: O(k log n) where k is the number of overlapping intervals.
303 ///
304 /// # Examples
305 ///
306 /// ```
307 /// use augmented_rbtree::interval_tree::{Interval, IntervalTree};
308 ///
309 /// let mut tree = IntervalTree::new();
310 /// tree.insert(Interval::new(1, 5), ());
311 /// tree.insert(Interval::new(6, 10), ());
312 /// tree.insert(Interval::new(3, 8), ());
313 ///
314 /// let overlapping: Vec<_> = tree.query_overlap(4, 7).collect();
315 /// assert_eq!(overlapping.len(), 3); // [1,5], [3,8] and [6,10] all overlap [4,7]
316 /// ```
317 pub fn query_overlap<K>(&self, lo: K, hi: K) -> impl Iterator<Item = (&Interval<T>, &V)>
318 where
319 K: Borrow<T>,
320 {
321 // The policy owns the reference `&Q`, which is perfectly fine
322 // since the reference lives for the duration of the query.
323 let policy = internal_details::IntervalOverlapPolicy {
324 lo,
325 hi,
326 _marker: PhantomData,
327 };
328
329 InOrderIter::new(self.inner_tree(), TreeLocation::Root, policy).map(|(k, v, _)| (k, v))
330 }
331 /// Returns an iterator over all intervals that **contain** the point `p`.
332 ///
333 /// An interval `[a, b]` contains `p` iff `a <= p <= b`.
334 ///
335 /// Complexity: O(k log n) where k is the number of matching intervals.
336 pub fn query_point<K>(&self, point: K) -> impl Iterator<Item = (&Interval<T>, &V)>
337 where
338 K: Borrow<T> + Clone,
339 {
340 let lo = point.clone();
341 let hi = point;
342 self.query_overlap::<K>(lo, hi)
343 }
344
345 /// Returns `true` if any interval in the tree overlaps with `[lo, hi]`.
346 ///
347 /// Complexity: O(log n).
348 #[must_use]
349 pub fn any_overlaps<K>(&self, lo: K, hi: K) -> bool
350 where
351 K: Borrow<T>,
352 {
353 self.query_overlap(lo, hi).next().is_some()
354 }
355
356 /// Returns `true` if any interval contains the given point.
357 ///
358 /// Complexity: O(log n).
359 #[must_use]
360 pub fn any_contains_point<K>(&self, point: K) -> bool
361 where
362 K: Borrow<T> + Clone,
363 {
364 self.any_overlaps(point.clone(), point)
365 }
366
367 /// Returns the first overlapping interval with `[lo, hi]`, if any.
368 ///
369 /// When multiple intervals overlap, returns the one with the smallest `lo`.
370 ///
371 /// Complexity: O(log n).
372 #[must_use]
373 pub fn first_overlap<K>(&self, lo: K, hi: K) -> Option<(&'_ Interval<T>, &'_ V)>
374 where
375 K: Borrow<T>,
376 {
377 self.query_overlap(lo, hi).next()
378 }
379}
380
381impl<T: Ord + Clone + Default + fmt::Debug, V: fmt::Debug> fmt::Debug for IntervalTree<T, V> {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 f.debug_map().entries(self.iter()).finish()
384 }
385}
386
387// ============================================================================
388// Iterator
389// ============================================================================
390
391/// Iterator over overlapping intervals. Created by [`IntervalTree::query_overlap`].
392pub type OverlapIter<'a, T, V, K> =
393 InOrderIter<'a, Interval<T>, V, T, internal_details::IntervalOverlapPolicy<T, K>>;
394
395#[cfg(test)]
396mod tests {
397 #[test]
398 fn test_interval_functions() {
399 use super::Interval;
400
401 let iv1 = Interval::new(1, 5);
402 let iv2 = Interval::new(4, 8);
403 let iv3 = Interval::new(6, 10);
404
405 assert!(iv1.overlaps(&iv2));
406 assert!(!iv1.overlaps(&iv3));
407 assert!(iv2.overlaps(&iv3));
408
409 assert!(iv1.contains_point(&3));
410 assert!(!iv1.contains_point(&6));
411
412 assert_eq!(iv1.len(), 4);
413 assert!(!iv1.is_point());
414 }
415}