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