Skip to main content

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}