use rudb_common::{Error, LogicalType, Result};
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)))
}
pub fn refine(flags: &Vector, kept: &Selection) -> Result<Selection> {
if kept.indices().iter().any(|&row| row as usize >= flags.len()) {
return Err(Error::internal(format!(
"a selection past the end of a {} row vector",
flags.len()
)));
}
if kept.is_empty() {
return Ok(Selection::empty());
}
if let Some(narrowed) = swept_within(flags, kept) {
return Ok(narrowed);
}
fallback::record(Kernel::Select, flags.form(), flags.form());
let mut out = Vec::with_capacity(kept.len());
for &row in kept.indices() {
if is_true(&flags.value_at(row as usize)) {
out.push(row);
}
}
Ok(Selection::from_indices(out))
}
fn swept_within(flags: &Vector, kept: &Selection) -> Option<Selection> {
if *flags.logical_type() != LogicalType::Boolean {
return None;
}
match flags.form() {
Form::Constant => {
Some(if is_true(flags.constant_value()?) { kept.clone() } else { Selection::empty() })
}
Form::Flat => {
let Data::Bool(values) = flags.data()? else {
return None;
};
if values.len() < flags.len() {
return None;
}
Some(picked_within(values, identity, kept.indices(), &nulls_of(flags)))
}
Form::Dictionary => {
let (codes, inner) = flags.dictionary_parts()?;
if codes.len() < flags.len() {
return None;
}
let Data::Bool(values) = inner.data()? else {
return None;
};
Some(picked_within(values, |row| codes[row] as usize, kept.indices(), &nulls_of(flags)))
}
_ => None,
}
}
fn picked_within<M: Fn(usize) -> usize>(
values: &[bool],
at: M,
rows: &[u32],
nulls: &Validity,
) -> Selection {
let mut out = vec![0_u32; rows.len()];
let mut count = 0;
match nulls {
Validity::AllValid => {
for &row in rows {
out[count] = row;
count += usize::from(values[at(row as usize)]);
}
}
Validity::AllInvalid => {}
Validity::Mask(mask) => {
for &row in rows {
out[count] = row;
count += usize::from(mask.get(row as usize) & values[at(row as usize)]);
}
}
}
out.truncate(count);
Selection::from_indices(out)
}
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 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");
}
}
}
fn within(vector: &Vector, kept: &Selection) -> Selection {
let mut out = Vec::new();
for &row in kept.indices() {
if is_true(&vector.value_at(row as usize)) {
out.push(row);
}
}
Selection::from_indices(out)
}
#[test]
fn a_threaded_selection_keeps_what_was_still_in_play_and_true() {
let mut rng = Rng(0x5eed_ca11_ab1e_0006);
let len = 251;
let selections = [
Selection::identity(len),
Selection::from_indices((0..len as u32).filter(|row| row % 7 == 0).collect()),
Selection::from_indices(vec![0, 1, 128, 250]),
Selection::empty(),
];
for nulls in [0_usize, 8, 3, 1] {
for share in [0_u64, 1, 16, 50, 84, 99, 100] {
let values: Vec<Value> = (0..len)
.map(|index| {
if nulls > 0 && index % nulls == 0 {
Value::Null
} else {
Value::Boolean(rng.next() % 100 < share)
}
})
.collect();
let vector = flags(&values);
let codes: Vec<u32> = (0..len).map(|index| (index % 37) as u32).collect();
let coded = Vector::dictionary(codes, vector.clone()).expect("codes are in range");
let all = Vector::constant(LogicalType::Boolean, YES, len);
for kept in &selections {
let note = format!("{share} percent true, one null in {nulls}");
let threaded = refine(&vector, kept).expect("in range");
assert_eq!(threaded, within(&vector, kept), "{note}, flat");
assert_eq!(
refine(&coded, kept).expect("in range"),
within(&coded, kept),
"{note}, dictionary"
);
assert_eq!(refine(&all, kept).expect("in range"), *kept, "{note}, constant");
let full = selection(&vector, len);
assert!(
threaded.indices().iter().all(|row| full.indices().contains(row)),
"{note}, threaded is within the full pass"
);
}
}
}
}
#[test]
fn a_threaded_selection_past_the_end_is_caught() {
let vector = flags(&[YES, YES]);
let past = Selection::from_indices(vec![0, 2]);
let error = refine(&vector, &past).expect_err("out of range");
assert!(error.message().contains("2 row vector"), "{error}");
}
#[test]
fn a_constant_is_answered_without_a_loop_and_a_non_boolean_is_not_answered_at_all() {
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]);
}
}