use rudb_common::LogicalType;
use rudb_vector::{Data, Form, Selection, Validity, Vector};
use crate::fallback::{self, Kernel};
use crate::logic::is_true;
use crate::shape::{identity, nulls_of};
#[must_use]
pub fn selection(flags: &Vector, rows: usize) -> Selection {
let rows = rows.min(flags.len());
if let Some(kept) = swept(flags, rows) {
return kept;
}
fallback::record(Kernel::Select, flags.form(), flags.form());
Selection::from_predicate(rows, |index| is_true(&flags.value_at(index)))
}
fn swept(flags: &Vector, rows: usize) -> Option<Selection> {
if *flags.logical_type() != LogicalType::Boolean {
return None;
}
if rows > u32::MAX as usize {
return None;
}
match flags.form() {
Form::Constant => Some(if is_true(flags.constant_value()?) {
Selection::identity(rows)
} else {
Selection::empty()
}),
Form::Flat => {
let Data::Bool(values) = flags.data()? else {
return None;
};
if values.len() < rows {
return None;
}
Some(picked(values, identity, rows, &nulls_of(flags)))
}
Form::Dictionary => {
let (codes, inner) = flags.dictionary_parts()?;
if codes.len() < rows {
return None;
}
let Data::Bool(values) = inner.data()? else {
return None;
};
Some(picked(values, |index| codes[index] as usize, rows, &nulls_of(flags)))
}
_ => None,
}
}
#[expect(
clippy::cast_possible_truncation,
reason = "the caller checked that the row count fits in a u32 before getting here"
)]
fn picked<M: Fn(usize) -> usize>(
values: &[bool],
at: M,
rows: usize,
nulls: &Validity,
) -> Selection {
let mut out = vec![0_u32; rows];
let mut kept = 0;
match nulls {
Validity::AllValid => {
for index in 0..rows {
out[kept] = index as u32;
kept += usize::from(values[at(index)]);
}
}
Validity::AllInvalid => {}
Validity::Mask(mask) => {
for start in (0..rows).step_by(64) {
let word = mask.word(start / 64);
for index in start..(start + 64).min(rows) {
out[kept] = index as u32;
let live = word >> (index - start) & 1 == 1;
kept += usize::from(live & values[at(index)]);
}
}
}
}
out.truncate(kept);
Selection::from_indices(out)
}
#[cfg(test)]
mod tests {
use rudb_common::Value;
use super::*;
fn flags(values: &[Value]) -> Vector {
Vector::from_values(LogicalType::Boolean, values).expect("a vector of booleans")
}
const YES: Value = Value::Boolean(true);
const NO: Value = Value::Boolean(false);
fn oracle(vector: &Vector, rows: usize) -> Selection {
Selection::from_predicate(rows, |index| is_true(&vector.value_at(index)))
}
struct Rng(u64);
impl Rng {
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
}
#[test]
fn a_null_flag_is_not_a_true_flag() {
let vector = flags(&[YES, Value::Null, NO, YES]);
let kept = selection(&vector, 4);
assert_eq!(kept.indices(), &[0, 3]);
assert_eq!(kept, oracle(&vector, 4));
}
#[test]
fn the_rows_kept_are_the_rows_the_row_at_a_time_path_keeps() {
let _turn = fallback::TURN.lock().expect("no test panics while holding this");
let mut rng = Rng(0x5eed_ca11_ab1e_0005);
for nulls in [0_usize, 8, 3, 1] {
for share in [0_u64, 1, 16, 50, 84, 99, 100] {
let values: Vec<Value> = (0..251)
.map(|index| {
if nulls > 0 && index % nulls == 0 {
Value::Null
} else {
Value::Boolean(rng.next() % 100 < share)
}
})
.collect();
let vector = flags(&values);
let note = format!("{share} percent true, one null in {nulls}");
assert_eq!(selection(&vector, 251), oracle(&vector, 251), "{note}, flat");
let codes: Vec<u32> = (0..251).map(|index| (index % 37) as u32).collect();
let coded = Vector::dictionary(codes, vector).expect("codes are in range");
assert_eq!(selection(&coded, 251), oracle(&coded, 251), "{note}, dictionary");
}
}
}
#[test]
fn a_constant_is_answered_without_a_loop_and_a_non_boolean_is_not_answered_at_all() {
let _turn = fallback::TURN.lock().expect("no test panics while holding this");
fallback::reset();
let all = Vector::constant(LogicalType::Boolean, YES, 500);
assert_eq!(selection(&all, 500), Selection::identity(500));
let none = Vector::constant(LogicalType::Boolean, Value::Null, 500);
assert!(selection(&none, 500).is_empty());
assert_eq!(fallback::count(Kernel::Select, Form::Constant, Form::Constant), 0);
let numbers = Vector::from_values(LogicalType::Integer, &[Value::Integer(1)])
.expect("a vector of integers");
assert!(selection(&numbers, 1).is_empty());
assert_eq!(fallback::count(Kernel::Select, Form::Flat, Form::Flat), 1);
fallback::reset();
}
#[test]
fn only_the_rows_asked_for_are_looked_at() {
let vector = flags(&[YES, YES, YES, YES]);
assert_eq!(selection(&vector, 2).indices(), &[0, 1]);
assert_eq!(selection(&vector, 9).indices(), &[0, 1, 2, 3]);
}
}