use rudb_common::{Error, LogicalType, Result, Value};
use rudb_vector::{Buffer, Data, Form, Validity, Vector};
use crate::fallback::{self, Kernel};
use crate::shape::{identity, nulls_of};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Connective {
And,
Or,
}
pub fn combine<V: AsRef<Vector>>(op: Connective, children: &[V]) -> Result<Vector> {
let first = children
.first()
.map(AsRef::as_ref)
.ok_or_else(|| Error::internal("a conjunction with no children"))?;
let rows = first.len();
for (at, child) in children.iter().enumerate() {
if child.as_ref().len() != rows {
return Err(Error::internal(format!(
"child {at} of a conjunction is {} rows and child 0 is {rows}",
child.as_ref().len()
)));
}
}
if let Some(vector) = folded(op, children, rows) {
return Ok(vector);
}
let left = first.form();
fallback::record(Kernel::Logic, left, children.get(1).map_or(left, |c| c.as_ref().form()));
let mut values = Vec::with_capacity(rows);
for index in 0..rows {
let mut answer = Some(matches!(op, Connective::And));
for child in children {
let held = match child.as_ref().value_at(index) {
Value::Boolean(held) => Some(held),
Value::Null => None,
other => {
return Err(Error::internal(format!(
"a conjunction over a {} value",
other.logical_type()
)));
}
};
answer = fold(op, answer, held);
}
values.push(match answer {
Some(held) => Value::Boolean(held),
None => Value::Null,
});
}
Vector::from_values(LogicalType::Boolean, &values)
}
fn folded<V: AsRef<Vector>>(op: Connective, children: &[V], rows: usize) -> Option<Vector> {
match op {
Connective::And => fold_runs::<false, _>(children, rows),
Connective::Or => fold_runs::<true, _>(children, rows),
}
}
fn fold_runs<const DOMINANT: bool, V: AsRef<Vector>>(
children: &[V],
rows: usize,
) -> Option<Vector> {
if rows == 0 {
return Vector::flat(LogicalType::Boolean, Data::Bool(Buffer::new())).ok();
}
if children.iter().any(|child| child.as_ref().logical_type() != &LogicalType::Boolean) {
return None;
}
let mut decided = vec![false; rows];
let mut unknown = vec![false; rows];
let mut nullable = false;
for child in children {
let child = child.as_ref();
let nulls = nulls_of(child);
nullable |= nulls.has_nulls(rows);
match child.form() {
Form::Constant => match child.value_at(0) {
Value::Boolean(held) if held == DOMINANT => decided.fill(true),
Value::Boolean(_) => {}
Value::Null => unknown.fill(true),
_ => return None,
},
Form::Flat => {
let Some(Data::Bool(values)) = child.data() else {
return None;
};
if values.len() < rows {
return None;
}
absorb::<DOMINANT, _>(values, identity, &nulls, &mut decided, &mut unknown);
}
Form::Dictionary => {
let (codes, values) = child.dictionary_parts()?;
let Some(Data::Bool(held)) = values.data() else {
return None;
};
if codes.len() < rows {
return None;
}
absorb::<DOMINANT, _>(
held,
|index| codes[index] as usize,
&nulls,
&mut decided,
&mut unknown,
);
}
_ => return None,
}
}
let validity = if nullable {
let live: Vec<bool> =
decided.iter().zip(&unknown).map(|(&hit, &null)| hit || !null).collect();
Validity::from_run(&live)
} else {
Validity::AllValid
};
let data = if DOMINANT {
decided
} else {
decided.iter().zip(&unknown).map(|(&hit, &null)| !(hit | null)).collect()
};
Some(Vector::flat(LogicalType::Boolean, Data::Bool(data.into())).ok()?.with_validity(validity))
}
fn absorb<const DOMINANT: bool, M: Fn(usize) -> usize>(
values: &[bool],
at: M,
nulls: &Validity,
decided: &mut [bool],
unknown: &mut [bool],
) {
match nulls {
Validity::AllValid => {
for (index, slot) in decided.iter_mut().enumerate() {
*slot |= values[at(index)] == DOMINANT;
}
}
Validity::AllInvalid => unknown.fill(true),
Validity::Mask(mask) => {
for (word_at, (hits, nulls)) in
decided.chunks_mut(64).zip(unknown.chunks_mut(64)).enumerate()
{
let word = mask.word(word_at);
let base = word_at * 64;
for (bit, (hit, null)) in hits.iter_mut().zip(nulls.iter_mut()).enumerate() {
let valid = word >> bit & 1 == 1;
*hit |= valid & (values[at(base + bit)] == DOMINANT);
*null |= !valid;
}
}
}
}
}
fn fold(op: Connective, left: Option<bool>, right: Option<bool>) -> Option<bool> {
match op {
Connective::And => match (left, right) {
(Some(false), _) | (_, Some(false)) => Some(false),
(Some(true), Some(true)) => Some(true),
_ => None,
},
Connective::Or => match (left, right) {
(Some(true), _) | (_, Some(true)) => Some(true),
(Some(false), Some(false)) => Some(false),
_ => None,
},
}
}
#[must_use]
pub fn is_true(value: &Value) -> bool {
matches!(value, Value::Boolean(true))
}
#[cfg(test)]
mod tests {
use super::*;
fn vector(values: &[Value]) -> Vector {
Vector::from_values(LogicalType::Boolean, values).expect("booleans")
}
const TRUE: Value = Value::Boolean(true);
const FALSE: Value = Value::Boolean(false);
#[test]
fn a_false_wins_an_and_even_against_an_unknown() {
let result = combine(Connective::And, &[vector(&[Value::Null]), vector(&[FALSE])])
.expect("two booleans");
assert_eq!(result.value_at(0), FALSE);
}
#[test]
fn a_true_wins_an_or_even_against_an_unknown() {
let result = combine(Connective::Or, &[vector(&[Value::Null]), vector(&[TRUE])])
.expect("two booleans");
assert_eq!(result.value_at(0), TRUE);
}
#[test]
fn an_unknown_survives_when_nothing_decides_it() {
let result = combine(Connective::And, &[vector(&[Value::Null]), vector(&[TRUE])])
.expect("two booleans");
assert_eq!(result.value_at(0), Value::Null);
let result = combine(Connective::Or, &[vector(&[Value::Null]), vector(&[FALSE])])
.expect("two booleans");
assert_eq!(result.value_at(0), Value::Null);
}
#[test]
fn a_flat_conjunction_of_more_than_two_children_is_one_pass() {
let result = combine(
Connective::And,
&[vector(&[TRUE]), vector(&[TRUE]), vector(&[TRUE]), vector(&[FALSE])],
)
.expect("four booleans");
assert_eq!(result.value_at(0), FALSE);
}
#[test]
fn a_where_clause_drops_the_rows_it_cannot_decide() {
assert!(is_true(&TRUE));
assert!(!is_true(&FALSE));
assert!(!is_true(&Value::Null));
}
#[test]
fn a_conjunction_with_no_children_is_caught() {
let nothing: &[Vector] = &[];
let error = combine(Connective::And, nothing).expect_err("nothing to combine");
assert!(error.message().contains("no children"), "{error}");
}
fn oracle(op: Connective, children: &[Vector]) -> Result<Vector> {
let rows = children.first().map_or(0, Vector::len);
let mut values = Vec::with_capacity(rows);
for index in 0..rows {
let mut answer = Some(matches!(op, Connective::And));
for child in children {
let held = match child.value_at(index) {
Value::Boolean(held) => Some(held),
Value::Null => None,
other => {
return Err(Error::internal(format!(
"a conjunction over a {} value",
other.logical_type()
)));
}
};
answer = fold(op, answer, held);
}
values.push(match answer {
Some(held) => Value::Boolean(held),
None => Value::Null,
});
}
Vector::from_values(LogicalType::Boolean, &values)
}
fn agrees(op: Connective, children: &[Vector]) {
let fast = combine(op, children);
let slow = oracle(op, children);
match (fast, slow) {
(Ok(fast), Ok(slow)) => assert_eq!(fast, slow, "{op:?} over {children:?}"),
(Err(fast), Err(slow)) => {
assert_eq!(fast.message(), slow.message(), "{op:?} over {children:?}");
}
(fast, slow) => panic!("{op:?} over {children:?} gave {fast:?} and {slow:?}"),
}
}
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
}
}
fn sample(rng: &mut Rng, rows: usize, nulls: u64) -> Vector {
let values: Vec<Value> = (0..rows)
.map(|_| {
let draw = rng.next();
if nulls > 0 && draw % nulls == 0 {
Value::Null
} else {
Value::Boolean(draw % 2 == 0)
}
})
.collect();
vector(&values)
}
#[test]
fn every_form_and_null_density_agrees_with_the_row_at_a_time_path() {
let mut rng = Rng(0x5eed_1eaf_c0ff_ee01);
let rows = 97;
for op in [Connective::And, Connective::Or] {
for nulls in [0, 2, 7] {
let flat = sample(&mut rng, rows, nulls);
let other = sample(&mut rng, rows, nulls);
let third = sample(&mut rng, rows, nulls);
agrees(op, &[flat.clone(), other.clone()]);
agrees(op, &[flat.clone(), other.clone(), third.clone()]);
agrees(op, std::slice::from_ref(&flat));
for held in [TRUE, FALSE, Value::Null] {
let constant = Vector::constant(LogicalType::Boolean, held, rows);
agrees(op, &[flat.clone(), constant.clone()]);
agrees(op, &[constant.clone(), flat.clone()]);
agrees(op, &[constant.clone(), flat.clone(), other.clone()]);
}
let dictionary = Vector::dictionary(
(0..rows)
.map(|index| u32::try_from(index % 3).expect("a code under three"))
.collect(),
vector(&[TRUE, FALSE, Value::Null]),
)
.expect("three codes into three values");
agrees(op, &[dictionary.clone(), flat.clone()]);
agrees(op, &[flat.clone(), dictionary.clone()]);
agrees(op, &[dictionary.clone(), dictionary.clone()]);
}
}
}
#[test]
fn a_child_that_is_all_null_still_lets_a_decided_row_through() {
let rows = 8;
let gone = Vector::constant(LogicalType::Boolean, Value::Null, rows);
let mixed = vector(&[TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE]);
agrees(Connective::And, &[gone.clone(), mixed.clone()]);
agrees(Connective::Or, &[gone.clone(), mixed.clone()]);
let result = combine(Connective::And, &[gone, mixed]).expect("two booleans");
assert_eq!(result.value_at(0), Value::Null);
assert_eq!(result.value_at(1), FALSE);
}
#[test]
fn an_empty_conjunction_of_empty_children_is_an_empty_answer() {
let empty = vector(&[]);
agrees(Connective::And, &[empty.clone(), empty.clone()]);
agrees(Connective::Or, &[empty.clone(), empty]);
}
#[test]
fn a_child_that_is_not_boolean_is_still_caught_by_name() {
let numbers = Vector::from_values(
LogicalType::Integer,
&[Value::Integer(1), Value::Integer(0), Value::Integer(3)],
)
.expect("integers");
let error = combine(Connective::And, &[vector(&[TRUE, TRUE, TRUE]), numbers])
.expect_err("a conjunction over integers");
assert!(error.message().contains("conjunction over"), "{error}");
assert!(error.message().contains("INTEGER"), "{error}");
}
#[test]
fn a_form_pair_with_no_loop_is_still_right_and_says_so() {
let before = fallback::count(Kernel::Logic, Form::Sequence, Form::Flat);
let rows = 4;
let ids = Vector::sequence(0, 1, rows);
let flat = vector(&[TRUE, FALSE, TRUE, FALSE]);
let error = combine(Connective::And, &[ids, flat]).expect_err("a conjunction over bigints");
assert!(error.message().contains("conjunction over"), "{error}");
assert!(fallback::count(Kernel::Logic, Form::Sequence, Form::Flat) > before);
}
#[test]
fn a_children_length_mismatch_names_the_child_that_is_wrong() {
let error = combine(Connective::And, &[vector(&[TRUE, TRUE]), vector(&[TRUE])])
.expect_err("two lengths");
assert!(error.message().contains("child 1"), "{error}");
}
}