1use rudb_common::{Error, LogicalType, Result};
35use rudb_vector::{Data, Form, Selection, Validity, Vector};
36
37use crate::fallback::{self, Kernel};
38use crate::logic::is_true;
39use crate::shape::{identity, nulls_of};
40
41#[must_use]
46pub fn selection(flags: &Vector, rows: usize) -> Selection {
47 let rows = rows.min(flags.len());
48 if let Some(kept) = swept(flags, rows) {
49 return kept;
50 }
51 fallback::record(Kernel::Select, flags.form(), flags.form());
54 Selection::from_predicate(rows, |index| is_true(&flags.value_at(index)))
55}
56
57pub fn refine(flags: &Vector, kept: &Selection) -> Result<Selection> {
71 if kept.indices().iter().any(|&row| row as usize >= flags.len()) {
72 return Err(Error::internal(format!(
73 "a selection past the end of a {} row vector",
74 flags.len()
75 )));
76 }
77 if kept.is_empty() {
78 return Ok(Selection::empty());
79 }
80 if let Some(narrowed) = swept_within(flags, kept) {
81 return Ok(narrowed);
82 }
83 fallback::record(Kernel::Select, flags.form(), flags.form());
84 let mut out = Vec::with_capacity(kept.len());
85 for &row in kept.indices() {
88 if is_true(&flags.value_at(row as usize)) {
89 out.push(row);
90 }
91 }
92 Ok(Selection::from_indices(out))
93}
94
95fn swept_within(flags: &Vector, kept: &Selection) -> Option<Selection> {
96 if *flags.logical_type() != LogicalType::Boolean {
97 return None;
98 }
99 match flags.form() {
100 Form::Constant => {
101 Some(if is_true(flags.constant_value()?) { kept.clone() } else { Selection::empty() })
102 }
103 Form::Flat => {
104 let Data::Bool(values) = flags.data()? else {
105 return None;
106 };
107 if values.len() < flags.len() {
108 return None;
109 }
110 Some(picked_within(values, identity, kept.indices(), &nulls_of(flags)))
111 }
112 Form::Dictionary => {
113 let (codes, inner) = flags.dictionary_parts()?;
114 if codes.len() < flags.len() {
115 return None;
116 }
117 let Data::Bool(values) = inner.data()? else {
118 return None;
119 };
120 Some(picked_within(values, |row| codes[row] as usize, kept.indices(), &nulls_of(flags)))
121 }
122 _ => None,
123 }
124}
125
126fn picked_within<M: Fn(usize) -> usize>(
131 values: &[bool],
132 at: M,
133 rows: &[u32],
134 nulls: &Validity,
135) -> Selection {
136 let mut out = vec![0_u32; rows.len()];
137 let mut count = 0;
138 match nulls {
139 Validity::AllValid => {
140 for &row in rows {
141 out[count] = row;
142 count += usize::from(values[at(row as usize)]);
143 }
144 }
145 Validity::AllInvalid => {}
146 Validity::Mask(mask) => {
147 for &row in rows {
148 out[count] = row;
149 count += usize::from(mask.get(row as usize) & values[at(row as usize)]);
152 }
153 }
154 }
155 out.truncate(count);
156 Selection::from_indices(out)
157}
158
159fn swept(flags: &Vector, rows: usize) -> Option<Selection> {
160 if *flags.logical_type() != LogicalType::Boolean {
161 return None;
162 }
163 if rows > u32::MAX as usize {
166 return None;
167 }
168 match flags.form() {
169 Form::Constant => Some(if is_true(flags.constant_value()?) {
171 Selection::identity(rows)
172 } else {
173 Selection::empty()
174 }),
175 Form::Flat => {
176 let Data::Bool(values) = flags.data()? else {
177 return None;
178 };
179 if values.len() < rows {
180 return None;
181 }
182 Some(picked(values, identity, rows, &nulls_of(flags)))
183 }
184 Form::Dictionary => {
185 let (codes, inner) = flags.dictionary_parts()?;
186 if codes.len() < rows {
187 return None;
188 }
189 let Data::Bool(values) = inner.data()? else {
190 return None;
191 };
192 Some(picked(values, |index| codes[index] as usize, rows, &nulls_of(flags)))
195 }
196 _ => None,
197 }
198}
199
200#[expect(
201 clippy::cast_possible_truncation,
202 reason = "the caller checked that the row count fits in a u32 before getting here"
203)]
204fn picked<M: Fn(usize) -> usize>(
205 values: &[bool],
206 at: M,
207 rows: usize,
208 nulls: &Validity,
209) -> Selection {
210 let mut out = vec![0_u32; rows];
211 let mut kept = 0;
212 match nulls {
213 Validity::AllValid => {
214 for index in 0..rows {
215 out[kept] = index as u32;
216 kept += usize::from(values[at(index)]);
217 }
218 }
219 Validity::AllInvalid => {}
220 Validity::Mask(mask) => {
221 for start in (0..rows).step_by(64) {
222 let word = mask.word(start / 64);
223 for index in start..(start + 64).min(rows) {
224 out[kept] = index as u32;
225 let live = word >> (index - start) & 1 == 1;
226 kept += usize::from(live & values[at(index)]);
229 }
230 }
231 }
232 }
233 out.truncate(kept);
234 Selection::from_indices(out)
235}
236
237#[cfg(test)]
238mod tests {
239 use rudb_common::Value;
240
241 use super::*;
242
243 fn flags(values: &[Value]) -> Vector {
244 Vector::from_values(LogicalType::Boolean, values).expect("a vector of booleans")
245 }
246
247 const YES: Value = Value::Boolean(true);
248 const NO: Value = Value::Boolean(false);
249
250 fn oracle(vector: &Vector, rows: usize) -> Selection {
252 Selection::from_predicate(rows, |index| is_true(&vector.value_at(index)))
253 }
254
255 struct Rng(u64);
256
257 impl Rng {
258 fn next(&mut self) -> u64 {
259 self.0 ^= self.0 << 13;
260 self.0 ^= self.0 >> 7;
261 self.0 ^= self.0 << 17;
262 self.0
263 }
264 }
265
266 #[test]
267 fn a_null_flag_is_not_a_true_flag() {
268 let vector = flags(&[YES, Value::Null, NO, YES]);
269 let kept = selection(&vector, 4);
270 assert_eq!(kept.indices(), &[0, 3]);
271 assert_eq!(kept, oracle(&vector, 4));
272 }
273
274 #[test]
277 fn the_rows_kept_are_the_rows_the_row_at_a_time_path_keeps() {
278 let mut rng = Rng(0x5eed_ca11_ab1e_0005);
279 for nulls in [0_usize, 8, 3, 1] {
280 for share in [0_u64, 1, 16, 50, 84, 99, 100] {
281 let values: Vec<Value> = (0..251)
282 .map(|index| {
283 if nulls > 0 && index % nulls == 0 {
284 Value::Null
285 } else {
286 Value::Boolean(rng.next() % 100 < share)
287 }
288 })
289 .collect();
290 let vector = flags(&values);
291 let note = format!("{share} percent true, one null in {nulls}");
292 assert_eq!(selection(&vector, 251), oracle(&vector, 251), "{note}, flat");
293 let codes: Vec<u32> = (0..251).map(|index| (index % 37) as u32).collect();
294 let coded = Vector::dictionary(codes, vector).expect("codes are in range");
295 assert_eq!(selection(&coded, 251), oracle(&coded, 251), "{note}, dictionary");
296 }
297 }
298 }
299
300 fn within(vector: &Vector, kept: &Selection) -> Selection {
302 let mut out = Vec::new();
303 for &row in kept.indices() {
306 if is_true(&vector.value_at(row as usize)) {
307 out.push(row);
308 }
309 }
310 Selection::from_indices(out)
311 }
312
313 #[test]
317 fn a_threaded_selection_keeps_what_was_still_in_play_and_true() {
318 let mut rng = Rng(0x5eed_ca11_ab1e_0006);
319 let len = 251;
320 let selections = [
321 Selection::identity(len),
322 Selection::from_indices((0..len as u32).filter(|row| row % 7 == 0).collect()),
323 Selection::from_indices(vec![0, 1, 128, 250]),
324 Selection::empty(),
325 ];
326 for nulls in [0_usize, 8, 3, 1] {
327 for share in [0_u64, 1, 16, 50, 84, 99, 100] {
328 let values: Vec<Value> = (0..len)
329 .map(|index| {
330 if nulls > 0 && index % nulls == 0 {
331 Value::Null
332 } else {
333 Value::Boolean(rng.next() % 100 < share)
334 }
335 })
336 .collect();
337 let vector = flags(&values);
338 let codes: Vec<u32> = (0..len).map(|index| (index % 37) as u32).collect();
339 let coded = Vector::dictionary(codes, vector.clone()).expect("codes are in range");
340 let all = Vector::constant(LogicalType::Boolean, YES, len);
341 for kept in &selections {
342 let note = format!("{share} percent true, one null in {nulls}");
343 let threaded = refine(&vector, kept).expect("in range");
344 assert_eq!(threaded, within(&vector, kept), "{note}, flat");
345 assert_eq!(
346 refine(&coded, kept).expect("in range"),
347 within(&coded, kept),
348 "{note}, dictionary"
349 );
350 assert_eq!(refine(&all, kept).expect("in range"), *kept, "{note}, constant");
352 let full = selection(&vector, len);
354 assert!(
355 threaded.indices().iter().all(|row| full.indices().contains(row)),
356 "{note}, threaded is within the full pass"
357 );
358 }
359 }
360 }
361 }
362
363 #[test]
364 fn a_threaded_selection_past_the_end_is_caught() {
365 let vector = flags(&[YES, YES]);
366 let past = Selection::from_indices(vec![0, 2]);
367 let error = refine(&vector, &past).expect_err("out of range");
368 assert!(error.message().contains("2 row vector"), "{error}");
369 }
370
371 #[test]
372 fn a_constant_is_answered_without_a_loop_and_a_non_boolean_is_not_answered_at_all() {
373 fallback::reset();
374 let all = Vector::constant(LogicalType::Boolean, YES, 500);
375 assert_eq!(selection(&all, 500), Selection::identity(500));
376 let none = Vector::constant(LogicalType::Boolean, Value::Null, 500);
377 assert!(selection(&none, 500).is_empty());
378 assert_eq!(fallback::count(Kernel::Select, Form::Constant, Form::Constant), 0);
379
380 let numbers = Vector::from_values(LogicalType::Integer, &[Value::Integer(1)])
383 .expect("a vector of integers");
384 assert!(selection(&numbers, 1).is_empty());
385 assert_eq!(fallback::count(Kernel::Select, Form::Flat, Form::Flat), 1);
386 fallback::reset();
387 }
388
389 #[test]
391 fn only_the_rows_asked_for_are_looked_at() {
392 let vector = flags(&[YES, YES, YES, YES]);
393 assert_eq!(selection(&vector, 2).indices(), &[0, 1]);
394 assert_eq!(selection(&vector, 9).indices(), &[0, 1, 2, 3]);
396 }
397}