Skip to main content

oxihuman_core/
interval_query.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Interval query tree — stores intervals and answers overlap queries.
6
7/// A closed interval [lo, hi] with an associated value.
8#[derive(Debug, Clone)]
9pub struct Interval<V: Clone> {
10    pub lo: f64,
11    pub hi: f64,
12    pub val: V,
13}
14
15impl<V: Clone> Interval<V> {
16    /// Create a new interval.
17    pub fn new(lo: f64, hi: f64, val: V) -> Self {
18        Interval {
19            lo: lo.min(hi),
20            hi: lo.max(hi),
21            val,
22        }
23    }
24
25    /// True if this interval overlaps with [qlo, qhi].
26    pub fn overlaps(&self, qlo: f64, qhi: f64) -> bool {
27        self.lo <= qhi && self.hi >= qlo
28    }
29}
30
31/// Node in the interval tree (augmented BST on interval midpoint).
32#[derive(Debug, Clone)]
33struct INode<V: Clone> {
34    interval: Interval<V>,
35    max_hi: f64,
36    left: Option<Box<INode<V>>>,
37    right: Option<Box<INode<V>>>,
38}
39
40impl<V: Clone> INode<V> {
41    fn new(interval: Interval<V>) -> Box<Self> {
42        let hi = interval.hi;
43        Box::new(INode {
44            interval,
45            max_hi: hi,
46            left: None,
47            right: None,
48        })
49    }
50
51    fn update_max(&mut self) {
52        self.max_hi = self.interval.hi;
53        if let Some(l) = &self.left {
54            if l.max_hi > self.max_hi {
55                self.max_hi = l.max_hi;
56            }
57        }
58        if let Some(r) = &self.right {
59            if r.max_hi > self.max_hi {
60                self.max_hi = r.max_hi;
61            }
62        }
63    }
64
65    fn insert(node: Option<Box<INode<V>>>, interval: Interval<V>) -> Box<INode<V>> {
66        let Some(mut n) = node else {
67            return INode::new(interval);
68        };
69        let mid = (n.interval.lo + n.interval.hi) / 2.0;
70        let new_mid = (interval.lo + interval.hi) / 2.0;
71        if new_mid <= mid {
72            n.left = Some(Self::insert(n.left.take(), interval));
73        } else {
74            n.right = Some(Self::insert(n.right.take(), interval));
75        }
76        n.update_max();
77        n
78    }
79
80    fn query<'a>(&'a self, qlo: f64, qhi: f64, result: &mut Vec<&'a Interval<V>>) {
81        if self.max_hi < qlo {
82            return;
83        }
84        if let Some(l) = &self.left {
85            l.query(qlo, qhi, result);
86        }
87        if self.interval.overlaps(qlo, qhi) {
88            result.push(&self.interval);
89        }
90        if self.interval.lo <= qhi {
91            if let Some(r) = &self.right {
92                r.query(qlo, qhi, result);
93            }
94        }
95    }
96}
97
98/// Interval overlap query tree.
99pub struct IntervalQueryTree<V: Clone> {
100    root: Option<Box<INode<V>>>,
101    count: usize,
102}
103
104impl<V: Clone> IntervalQueryTree<V> {
105    /// Create a new empty interval query tree.
106    pub fn new() -> Self {
107        IntervalQueryTree {
108            root: None,
109            count: 0,
110        }
111    }
112
113    /// Insert an interval.
114    pub fn insert(&mut self, lo: f64, hi: f64, val: V) {
115        let iv = Interval::new(lo, hi, val);
116        self.root = Some(INode::insert(self.root.take(), iv));
117        self.count += 1;
118    }
119
120    /// Query all intervals overlapping [qlo, qhi].
121    pub fn query(&self, qlo: f64, qhi: f64) -> Vec<&Interval<V>> {
122        let mut result = Vec::new();
123        if let Some(r) = &self.root {
124            r.query(qlo, qhi, &mut result);
125        }
126        result
127    }
128
129    /// Number of intervals stored.
130    pub fn len(&self) -> usize {
131        self.count
132    }
133
134    /// True if empty.
135    pub fn is_empty(&self) -> bool {
136        self.count == 0
137    }
138}
139
140impl<V: Clone> Default for IntervalQueryTree<V> {
141    fn default() -> Self {
142        Self::new()
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn test_insert_and_query_overlap() {
152        let mut t: IntervalQueryTree<u32> = IntervalQueryTree::new();
153        t.insert(1.0, 5.0, 1);
154        let res = t.query(3.0, 4.0);
155        assert!(!res.is_empty() /* [1,5] overlaps [3,4] */);
156    }
157
158    #[test]
159    fn test_no_overlap() {
160        let mut t: IntervalQueryTree<u32> = IntervalQueryTree::new();
161        t.insert(1.0, 2.0, 1);
162        let res = t.query(5.0, 6.0);
163        assert!(res.is_empty() /* [1,2] does not overlap [5,6] */);
164    }
165
166    #[test]
167    fn test_len() {
168        let mut t: IntervalQueryTree<u32> = IntervalQueryTree::new();
169        t.insert(0.0, 1.0, 0);
170        t.insert(2.0, 3.0, 1);
171        assert_eq!(t.len(), 2);
172    }
173
174    #[test]
175    fn test_is_empty() {
176        let t: IntervalQueryTree<u32> = IntervalQueryTree::new();
177        assert!(t.is_empty());
178    }
179
180    #[test]
181    fn test_multiple_overlaps() {
182        let mut t: IntervalQueryTree<u32> = IntervalQueryTree::new();
183        t.insert(0.0, 10.0, 0);
184        t.insert(5.0, 15.0, 1);
185        t.insert(12.0, 20.0, 2);
186        let res = t.query(6.0, 13.0);
187        /* [0,10] and [5,15] and [12,20] all overlap [6,13] */
188        assert_eq!(res.len(), 3);
189    }
190
191    #[test]
192    fn test_point_query() {
193        let mut t: IntervalQueryTree<u32> = IntervalQueryTree::new();
194        t.insert(1.0, 5.0, 10);
195        t.insert(6.0, 9.0, 20);
196        let res = t.query(3.0, 3.0);
197        assert_eq!(res.len(), 1 /* only [1,5] contains point 3 */);
198    }
199
200    #[test]
201    fn test_interval_overlap_method() {
202        let iv = Interval::new(1.0, 5.0, ());
203        assert!(iv.overlaps(4.0, 6.0) /* partial overlap right */);
204        assert!(!iv.overlaps(6.0, 8.0) /* no overlap */);
205    }
206
207    #[test]
208    fn test_reversed_lo_hi_normalized() {
209        let iv = Interval::new(5.0, 1.0, 42u32);
210        assert!(iv.lo < iv.hi /* lo and hi are normalized */);
211    }
212
213    #[test]
214    fn test_default() {
215        let t: IntervalQueryTree<u32> = IntervalQueryTree::default();
216        assert!(t.is_empty());
217    }
218}