Skip to main content

grafeo_core/execution/
selection.rs

1//! SelectionVector for filtering.
2
3/// A selection vector indicating which rows are active.
4///
5/// Used for efficient filtering without copying data.
6#[derive(Debug, Clone)]
7pub struct SelectionVector {
8    /// Indices of selected rows.
9    indices: Vec<u16>,
10}
11
12impl SelectionVector {
13    /// Maximum capacity (limited to u16 for space efficiency).
14    pub const MAX_CAPACITY: usize = u16::MAX as usize;
15
16    /// Creates a new selection vector selecting all rows up to count.
17    #[must_use]
18    pub fn new_all(count: usize) -> Self {
19        assert!(count <= Self::MAX_CAPACITY);
20        Self {
21            indices: (0..count as u16).collect(),
22        }
23    }
24
25    /// Creates a new empty selection vector.
26    #[must_use]
27    pub fn new_empty() -> Self {
28        Self {
29            indices: Vec::new(),
30        }
31    }
32
33    /// Creates a new selection vector with the given capacity.
34    #[must_use]
35    pub fn with_capacity(capacity: usize) -> Self {
36        Self {
37            indices: Vec::with_capacity(capacity.min(Self::MAX_CAPACITY)),
38        }
39    }
40
41    /// Creates a selection vector from a predicate.
42    ///
43    /// Selects all indices where the predicate returns true.
44    #[must_use]
45    pub fn from_predicate<F>(count: usize, predicate: F) -> Self
46    where
47        F: Fn(usize) -> bool,
48    {
49        let indices: Vec<u16> = (0..count)
50            .filter(|&i| predicate(i))
51            .map(|i| i as u16)
52            .collect();
53        Self { indices }
54    }
55
56    /// Returns the number of selected rows.
57    #[must_use]
58    pub fn len(&self) -> usize {
59        self.indices.len()
60    }
61
62    /// Returns true if no rows are selected.
63    #[must_use]
64    pub fn is_empty(&self) -> bool {
65        self.indices.is_empty()
66    }
67
68    /// Gets the actual row index at position.
69    #[must_use]
70    pub fn get(&self, position: usize) -> Option<usize> {
71        self.indices.get(position).map(|&i| i as usize)
72    }
73
74    /// Pushes a new index.
75    pub fn push(&mut self, index: usize) {
76        assert!(index <= Self::MAX_CAPACITY);
77        self.indices.push(index as u16);
78    }
79
80    /// Returns the indices as a slice.
81    #[must_use]
82    pub fn as_slice(&self) -> &[u16] {
83        &self.indices
84    }
85
86    /// Clears all selections.
87    pub fn clear(&mut self) {
88        self.indices.clear();
89    }
90
91    /// Filters this selection by another predicate.
92    ///
93    /// Returns a new selection containing only indices that pass the predicate.
94    #[must_use]
95    pub fn filter<F>(&self, predicate: F) -> Self
96    where
97        F: Fn(usize) -> bool,
98    {
99        let indices: Vec<u16> = self
100            .indices
101            .iter()
102            .copied()
103            .filter(|&i| predicate(i as usize))
104            .collect();
105        Self { indices }
106    }
107
108    /// Computes the intersection of two selection vectors.
109    #[must_use]
110    pub fn intersect(&self, other: &Self) -> Self {
111        // Assumes both are sorted (which they typically are)
112        let mut result = Vec::new();
113        let mut i = 0;
114        let mut j = 0;
115
116        while i < self.indices.len() && j < other.indices.len() {
117            match self.indices[i].cmp(&other.indices[j]) {
118                std::cmp::Ordering::Less => i += 1,
119                std::cmp::Ordering::Greater => j += 1,
120                std::cmp::Ordering::Equal => {
121                    result.push(self.indices[i]);
122                    i += 1;
123                    j += 1;
124                }
125            }
126        }
127
128        Self { indices: result }
129    }
130
131    /// Computes the union of two selection vectors.
132    #[must_use]
133    pub fn union(&self, other: &Self) -> Self {
134        let mut result = Vec::new();
135        let mut i = 0;
136        let mut j = 0;
137
138        while i < self.indices.len() && j < other.indices.len() {
139            match self.indices[i].cmp(&other.indices[j]) {
140                std::cmp::Ordering::Less => {
141                    result.push(self.indices[i]);
142                    i += 1;
143                }
144                std::cmp::Ordering::Greater => {
145                    result.push(other.indices[j]);
146                    j += 1;
147                }
148                std::cmp::Ordering::Equal => {
149                    result.push(self.indices[i]);
150                    i += 1;
151                    j += 1;
152                }
153            }
154        }
155
156        result.extend_from_slice(&self.indices[i..]);
157        result.extend_from_slice(&other.indices[j..]);
158
159        Self { indices: result }
160    }
161
162    /// Returns an iterator over selected indices.
163    pub fn iter(&self) -> impl Iterator<Item = usize> + '_ {
164        self.indices.iter().map(|&i| i as usize)
165    }
166
167    /// Checks if a given index is in the selection.
168    #[must_use]
169    pub fn contains(&self, index: usize) -> bool {
170        if index > u16::MAX as usize {
171            return false;
172        }
173        // Since indices are typically sorted, use binary search
174        self.indices.binary_search(&(index as u16)).is_ok()
175    }
176}
177
178impl Default for SelectionVector {
179    fn default() -> Self {
180        Self::new_empty()
181    }
182}
183
184impl IntoIterator for SelectionVector {
185    type Item = usize;
186    type IntoIter = std::iter::Map<std::vec::IntoIter<u16>, fn(u16) -> usize>;
187
188    fn into_iter(self) -> Self::IntoIter {
189        self.indices.into_iter().map(|i| i as usize)
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn test_selection_all() {
199        let sel = SelectionVector::new_all(10);
200        assert_eq!(sel.len(), 10);
201
202        for i in 0..10 {
203            assert_eq!(sel.get(i), Some(i));
204        }
205    }
206
207    #[test]
208    fn test_selection_from_predicate() {
209        let sel = SelectionVector::from_predicate(10, |i| i % 2 == 0);
210
211        assert_eq!(sel.len(), 5);
212        assert_eq!(sel.get(0), Some(0));
213        assert_eq!(sel.get(1), Some(2));
214        assert_eq!(sel.get(2), Some(4));
215    }
216
217    #[test]
218    fn test_selection_filter() {
219        let sel = SelectionVector::new_all(10);
220        let filtered = sel.filter(|i| i >= 5);
221
222        assert_eq!(filtered.len(), 5);
223        assert_eq!(filtered.get(0), Some(5));
224    }
225
226    #[test]
227    fn test_selection_intersect() {
228        let sel1 = SelectionVector::from_predicate(10, |i| i % 2 == 0); // 0, 2, 4, 6, 8
229        let sel2 = SelectionVector::from_predicate(10, |i| i % 3 == 0); // 0, 3, 6, 9
230
231        let intersection = sel1.intersect(&sel2);
232        // Intersection: 0, 6
233
234        assert_eq!(intersection.len(), 2);
235        assert_eq!(intersection.get(0), Some(0));
236        assert_eq!(intersection.get(1), Some(6));
237    }
238
239    #[test]
240    fn test_selection_union() {
241        let sel1 = SelectionVector::from_predicate(10, |i| i == 1 || i == 3); // 1, 3
242        let sel2 = SelectionVector::from_predicate(10, |i| i == 2 || i == 3); // 2, 3
243
244        let union = sel1.union(&sel2);
245        // Union: 1, 2, 3
246
247        assert_eq!(union.len(), 3);
248        assert_eq!(union.get(0), Some(1));
249        assert_eq!(union.get(1), Some(2));
250        assert_eq!(union.get(2), Some(3));
251    }
252
253    #[test]
254    fn test_selection_iterator() {
255        let sel = SelectionVector::from_predicate(5, |i| i < 3);
256        let collected: Vec<_> = sel.iter().collect();
257
258        assert_eq!(collected, vec![0, 1, 2]);
259    }
260}