simd_intervaltree/
builder.rs

1//! Builder for constructing interval trees.
2
3use alloc::vec;
4use alloc::vec::Vec;
5
6use crate::tree::{IntervalTree, Node};
7use crate::Interval;
8
9/// Builder for constructing an [`IntervalTree`].
10///
11/// # Example
12///
13/// ```
14/// use simd_intervaltree::IntervalTree;
15///
16/// let tree = IntervalTree::builder()
17///     .insert(0..10, "first")
18///     .insert(5..15, "second")
19///     .build();
20/// ```
21#[derive(Debug, Clone)]
22pub struct IntervalTreeBuilder<T, V> {
23    intervals: Vec<(Interval<T>, V)>,
24}
25
26impl<T, V> Default for IntervalTreeBuilder<T, V> {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl<T, V> IntervalTreeBuilder<T, V> {
33    /// Creates a new empty builder.
34    #[must_use]
35    pub fn new() -> Self {
36        Self {
37            intervals: Vec::new(),
38        }
39    }
40
41    /// Creates a new builder with the specified capacity.
42    #[must_use]
43    pub fn with_capacity(capacity: usize) -> Self {
44        Self {
45            intervals: Vec::with_capacity(capacity),
46        }
47    }
48
49    /// Inserts an interval with its associated value.
50    #[must_use]
51    pub fn insert<R: Into<Interval<T>>>(mut self, range: R, value: V) -> Self {
52        self.intervals.push((range.into(), value));
53        self
54    }
55
56    /// Returns the number of intervals added.
57    #[must_use]
58    pub fn len(&self) -> usize {
59        self.intervals.len()
60    }
61
62    /// Returns true if no intervals have been added.
63    #[must_use]
64    pub fn is_empty(&self) -> bool {
65        self.intervals.is_empty()
66    }
67}
68
69impl<T: Ord + Copy, V> IntervalTreeBuilder<T, V> {
70    /// Builds the interval tree.
71    ///
72    /// Data is laid out contiguously per node for SIMD-friendly scanning.
73    /// Each node's intervals are sorted by start (ascending) in the main arrays,
74    /// with a separate index array for by-end (descending) ordering.
75    #[must_use]
76    pub fn build(self) -> IntervalTree<T, V> {
77        let n = self.intervals.len();
78
79        if n == 0 {
80            return IntervalTree {
81                starts: Vec::new(),
82                ends: Vec::new(),
83                values: Vec::new(),
84                nodes: Vec::new(),
85                by_end_indices: Vec::new(),
86                ends_desc: Vec::new(),
87            };
88        }
89
90        // Phase 1: Build tree structure and collect intervals per node
91        let mut input_starts: Vec<T> = Vec::with_capacity(n);
92        let mut input_ends: Vec<T> = Vec::with_capacity(n);
93        let mut input_values: Vec<V> = Vec::with_capacity(n);
94
95        for (interval, value) in self.intervals {
96            input_starts.push(interval.start);
97            input_ends.push(interval.end);
98            input_values.push(value);
99        }
100
101        // Build tree and collect intervals per node
102        let indices: Vec<usize> = (0..n).collect();
103        let mut planner = TreePlanner::new(&input_starts, &input_ends);
104        planner.plan_node(&indices);
105
106        // Phase 2: Output data in node order (contiguous per node, sorted by start)
107        let mut starts: Vec<T> = Vec::with_capacity(n);
108        let mut ends: Vec<T> = Vec::with_capacity(n);
109        let mut values: Vec<V> = Vec::with_capacity(n);
110        let mut by_end_indices: Vec<usize> = Vec::with_capacity(n);
111        let mut ends_desc: Vec<T> = Vec::with_capacity(n);
112        let mut nodes: Vec<Node> = Vec::with_capacity(planner.node_intervals.len());
113
114        // We need to map old indices to new positions
115        let mut index_map: Vec<usize> = vec![0; n];
116
117        for node_data in &planner.node_intervals {
118            let node_start_pos = starts.len();
119
120            // Sort by start ascending and output
121            let mut sorted_indices = node_data.containing.clone();
122            sorted_indices.sort_by_key(|&i| input_starts[i]);
123
124            for &old_idx in &sorted_indices {
125                let new_idx = starts.len();
126                index_map[old_idx] = new_idx;
127                starts.push(input_starts[old_idx]);
128                ends.push(input_ends[old_idx]);
129            }
130
131            let node_end_pos = starts.len();
132
133            // Build by_end indices (sorted by end descending, pointing to new positions)
134            // Also build ends_desc for SIMD scanning
135            let mut by_end = sorted_indices.clone();
136            by_end.sort_by(|&a, &b| input_ends[b].cmp(&input_ends[a]));
137
138            let by_end_begin = by_end_indices.len();
139            for &old_idx in &by_end {
140                by_end_indices.push(index_map[old_idx]);
141                ends_desc.push(input_ends[old_idx]);
142            }
143            let by_end_end = by_end_indices.len();
144
145            // Find pivot in new positions
146            let pivot_new_idx = index_map[node_data.pivot_idx];
147
148            nodes.push(Node {
149                pivot_idx: pivot_new_idx,
150                data_begin: node_start_pos,
151                data_end: node_end_pos,
152                by_end_begin,
153                by_end_end,
154                left: node_data.left,
155                right: node_data.right,
156            });
157        }
158
159        // Move values in correct order
160        // We need to iterate in the order we added to starts/ends
161        let mut value_order: Vec<(usize, usize)> = index_map
162            .iter()
163            .enumerate()
164            .map(|(old, &new)| (new, old))
165            .collect();
166        value_order.sort_by_key(|(new, _)| *new);
167
168        // Use a temporary to allow moving values
169        let mut temp_values: Vec<Option<V>> = input_values.into_iter().map(Some).collect();
170        for (_, old_idx) in value_order {
171            let val: Option<V> = temp_values[old_idx].take();
172            values.push(val.unwrap());
173        }
174
175        IntervalTree {
176            starts,
177            ends,
178            values,
179            nodes,
180            by_end_indices,
181            ends_desc,
182        }
183    }
184}
185
186/// Intermediate node data during planning phase.
187struct NodeData {
188    pivot_idx: usize,
189    containing: Vec<usize>,
190    left: usize,
191    right: usize,
192}
193
194/// First pass: plan tree structure without moving data.
195struct TreePlanner<'a, T> {
196    starts: &'a [T],
197    ends: &'a [T],
198    node_intervals: Vec<NodeData>,
199}
200
201impl<'a, T: Ord + Copy> TreePlanner<'a, T> {
202    fn new(starts: &'a [T], ends: &'a [T]) -> Self {
203        Self {
204            starts,
205            ends,
206            node_intervals: Vec::new(),
207        }
208    }
209
210    fn plan_node(&mut self, indices: &[usize]) -> usize {
211        if indices.is_empty() {
212            return Node::NULL;
213        }
214
215        // Find median endpoint as pivot
216        let pivot_idx = self.find_median_endpoint(indices);
217        let pivot = self.starts[pivot_idx];
218
219        // Partition intervals
220        let mut containing = Vec::new();
221        let mut left_indices = Vec::new();
222        let mut right_indices = Vec::new();
223
224        for &idx in indices {
225            let start = self.starts[idx];
226            let end = self.ends[idx];
227
228            if end <= pivot {
229                left_indices.push(idx);
230            } else if start > pivot {
231                right_indices.push(idx);
232            } else {
233                containing.push(idx);
234            }
235        }
236
237        // Allocate node
238        let node_idx = self.node_intervals.len();
239        self.node_intervals.push(NodeData {
240            pivot_idx,
241            containing,
242            left: Node::NULL,
243            right: Node::NULL,
244        });
245
246        // Build children
247        let left_child = self.plan_node(&left_indices);
248        let right_child = self.plan_node(&right_indices);
249
250        self.node_intervals[node_idx].left = left_child;
251        self.node_intervals[node_idx].right = right_child;
252
253        node_idx
254    }
255
256    fn find_median_endpoint(&self, indices: &[usize]) -> usize {
257        let mut endpoints: Vec<T> = Vec::with_capacity(indices.len() * 2);
258        for &idx in indices {
259            endpoints.push(self.starts[idx]);
260            endpoints.push(self.ends[idx]);
261        }
262        endpoints.sort();
263
264        let median = endpoints[endpoints.len() / 2];
265
266        for &idx in indices {
267            if self.starts[idx] <= median && median < self.ends[idx] {
268                return idx;
269            }
270        }
271
272        indices[0]
273    }
274}