use core::ops::Range;
use core::panic;
use smallvec::SmallVec;
use crate::id::id_from_value;
use crate::id::id_into_value;
use crate::id::ID_LEN;
use crate::inline::encodings::genid::GenId;
use crate::inline::InlineEncoding;
use crate::inline::RawInline;
use crate::inline::INLINE_LEN;
use crate::query::Binding;
use crate::query::Candidates;
use crate::query::Constraint;
use crate::query::Frontier;
use crate::query::ProposalBuffer;
use crate::query::RawTerm;
use crate::query::Term;
use crate::query::VariableId;
use crate::query::VariableSet;
use crate::trible::TribleSet;
const SORTED_PROBE_MIN: usize = 2;
#[cfg(feature = "parallel")]
const PARALLEL_CONFIRM_MIN: usize = 1024;
#[cfg(all(test, feature = "parallel"))]
static PARALLEL_CONFIRM_SPLITS: std::sync::atomic::AtomicUsize =
std::sync::atomic::AtomicUsize::new(0);
#[inline]
fn retain_range(
cands: &mut Candidates<'_>,
range: Range<usize>,
mut keep: impl FnMut(&RawInline) -> bool,
) {
let mut memo: Option<(RawInline, bool)> = None;
for i in range {
if !cands.is_live(i) {
continue;
}
let value = cands.values()[i];
let verdict = match memo {
Some((seen, verdict)) if seen == value => verdict,
_ => {
let verdict = keep(&value);
memo = Some((value, verdict));
verdict
}
};
if !verdict {
cands.kill(i);
}
}
}
#[inline]
fn kill_range(cands: &mut Candidates<'_>, range: Range<usize>) {
for i in range {
cands.kill(i);
}
}
pub struct TribleSetConstraint {
term_e: RawTerm,
term_a: RawTerm,
term_v: RawTerm,
set: TribleSet,
}
impl TribleSetConstraint {
pub fn new<V: InlineEncoding>(
e: impl Into<Term<GenId>>,
a: impl Into<Term<GenId>>,
v: impl Into<Term<V>>,
set: TribleSet,
) -> Self {
TribleSetConstraint {
term_e: e.into().erase(),
term_a: a.into().erase(),
term_v: v.into().erase(),
set,
}
}
}
impl TribleSetConstraint {
fn confirm_at(
&self,
variable: VariableId,
binding: &Binding,
cands: &mut Candidates<'_>,
range: Range<usize>,
) {
let e_var = self.term_e.is_var(variable);
let a_var = self.term_a.is_var(variable);
let v_var = self.term_v.is_var(variable);
if !e_var && !a_var && !v_var {
return;
}
let e_bound = if let Some(e) = self.term_e.position_value(binding) {
let Some(e) = id_from_value(e) else {
kill_range(cands, range);
return;
};
Some(e)
} else {
None
};
let a_bound = if let Some(a) = self.term_a.position_value(binding) {
let Some(a) = id_from_value(a) else {
kill_range(cands, range);
return;
};
Some(a)
} else {
None
};
let v_bound = self.term_v.position_value(binding);
match (e_bound, a_bound, v_bound, e_var, a_var, v_var) {
(None, None, None, true, false, false) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
self.set.eav.has_prefix(&id)
}),
(None, None, None, false, true, false) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
self.set.aev.has_prefix(&id)
}),
(None, None, None, false, false, true) => {
retain_range(cands, range, |value| self.set.vea.has_prefix(value))
}
(Some(e), None, None, false, true, false) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&e[..]);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&id);
self.set.eav.has_prefix(&prefix)
}),
(Some(e), None, None, false, false, true) => retain_range(cands, range, |value| {
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&e[..]);
prefix[ID_LEN..ID_LEN + INLINE_LEN].copy_from_slice(value);
self.set.eva.has_prefix(&prefix)
}),
(None, Some(a), None, true, false, false) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&a[..]);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&id);
self.set.aev.has_prefix(&prefix)
}),
(None, Some(a), None, false, false, true) => retain_range(cands, range, |value| {
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&a[..]);
prefix[ID_LEN..ID_LEN + INLINE_LEN].copy_from_slice(value);
self.set.ave.has_prefix(&prefix)
}),
(None, None, Some(v), true, false, false) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; INLINE_LEN + ID_LEN];
prefix[0..INLINE_LEN].copy_from_slice(&v[..]);
prefix[INLINE_LEN..INLINE_LEN + ID_LEN].copy_from_slice(&id);
self.set.vea.has_prefix(&prefix)
}),
(None, None, Some(v), false, true, false) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; INLINE_LEN + ID_LEN];
prefix[0..INLINE_LEN].copy_from_slice(&v[..]);
prefix[INLINE_LEN..INLINE_LEN + ID_LEN].copy_from_slice(&id);
self.set.vae.has_prefix(&prefix)
}),
(None, Some(a), Some(v), true, false, false) => {
retain_range(cands, range, |value: &[u8; 32]| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + INLINE_LEN + ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&a);
prefix[ID_LEN..ID_LEN + INLINE_LEN].copy_from_slice(v);
prefix[ID_LEN + INLINE_LEN..ID_LEN + INLINE_LEN + ID_LEN].copy_from_slice(&id);
self.set.ave.has_prefix(&prefix)
})
}
(Some(e), None, Some(v), false, true, false) => {
retain_range(cands, range, |value: &[u8; 32]| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + INLINE_LEN + ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&e);
prefix[ID_LEN..ID_LEN + INLINE_LEN].copy_from_slice(v);
prefix[ID_LEN + INLINE_LEN..ID_LEN + INLINE_LEN + ID_LEN].copy_from_slice(&id);
self.set.eva.has_prefix(&prefix)
})
}
(Some(e), Some(a), None, false, false, true) => {
retain_range(cands, range, |value: &[u8; 32]| {
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&e);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&a);
prefix[ID_LEN + ID_LEN..ID_LEN + ID_LEN + INLINE_LEN].copy_from_slice(value);
self.set.eav.has_prefix(&prefix)
})
}
(_, Some(a), _, true, false, true) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&id);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&a[..]);
prefix[ID_LEN + ID_LEN..].copy_from_slice(&id_into_value(&id));
self.set.eav.has_prefix(&prefix)
}),
(_, None, _, true, false, true) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&id);
prefix[ID_LEN..].copy_from_slice(&id_into_value(&id));
self.set.eva.has_prefix(&prefix)
}),
(_, _, Some(v), true, true, false) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&id);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&id);
prefix[ID_LEN + ID_LEN..].copy_from_slice(&v[..]);
self.set.eav.has_prefix(&prefix)
}),
(_, _, None, true, true, false) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&id);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&id);
self.set.eav.has_prefix(&prefix)
}),
(Some(e), _, _, false, true, true) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&e);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&id);
prefix[ID_LEN + ID_LEN..].copy_from_slice(&id_into_value(&id));
self.set.eav.has_prefix(&prefix)
}),
(None, _, _, false, true, true) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&id);
prefix[ID_LEN..].copy_from_slice(&id_into_value(&id));
self.set.ave.has_prefix(&prefix)
}),
(_, _, _, true, true, true) => retain_range(cands, range, |value| {
let Some(id) = id_from_value(value) else {
return false;
};
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&id);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&id);
prefix[ID_LEN + ID_LEN..].copy_from_slice(&id_into_value(&id));
self.set.eav.has_prefix(&prefix)
}),
_ => panic!("invalid trible constraint state"),
}
}
fn confirm_grouped(
&self,
variable: VariableId,
frontier: &Frontier<'_>,
group: &[u32],
cands: &mut Candidates<'_>,
) {
let entries = cands.len();
let mut run_start = 0;
while run_start < entries {
let lead = cands.parent(run_start);
let label = group[lead as usize];
let mut run_end = run_start + 1;
while run_end < entries && group[cands.parent(run_end) as usize] == label {
run_end += 1;
}
let binding = frontier.row(lead as usize);
self.confirm_at(variable, &binding, cands, run_start..run_end);
run_start = run_end;
}
}
#[cfg(feature = "parallel")]
fn confirm_parallel(
&self,
variable: VariableId,
frontier: &Frontier<'_>,
group: &[u32],
cands: Candidates<'_>,
) {
if cands.len() < PARALLEL_CONFIRM_MIN || rayon::current_num_threads() == 1 {
let mut cands = cands;
self.confirm_grouped(variable, frontier, group, &mut cands);
return;
}
let bit_offset = cands.bit_offset();
let midpoint = bit_offset + cands.len() / 2;
let boundary =
midpoint / crate::query::LIVENESS_WORD_BITS * crate::query::LIVENESS_WORD_BITS;
let mid = boundary.saturating_sub(bit_offset);
if mid == 0 || mid >= cands.len() {
let mut cands = cands;
self.confirm_grouped(variable, frontier, group, &mut cands);
return;
}
let (left, right) = cands.split_at_word_boundary(mid);
#[cfg(test)]
PARALLEL_CONFIRM_SPLITS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
rayon::join(
|| self.confirm_parallel(variable, frontier, group, left),
|| self.confirm_parallel(variable, frontier, group, right),
);
}
fn propose_row(&self, variable: VariableId, binding: &Binding, proposals: &mut ProposalBuffer) {
let e_var = self.term_e.is_var(variable);
let a_var = self.term_a.is_var(variable);
let v_var = self.term_v.is_var(variable);
if !e_var && !a_var && !v_var {
return;
}
let e_bound = if let Some(e) = self.term_e.position_value(binding) {
let Some(e) = id_from_value(e) else {
return;
};
Some(e)
} else {
None
};
let a_bound = if let Some(a) = self.term_a.position_value(binding) {
let Some(a) = id_from_value(a) else {
return;
};
Some(a)
} else {
None
};
let v_bound = self.term_v.position_value(binding);
match (e_bound, a_bound, v_bound, e_var, a_var, v_var) {
(None, None, None, true, false, false) => {
self.set.eav.infixes(&[0; 0], &mut |e: &[u8; 16]| {
proposals.push(id_into_value(e))
});
}
(None, None, None, false, true, false) => {
self.set.aev.infixes(&[0; 0], &mut |a: &[u8; 16]| {
proposals.push(id_into_value(a))
});
}
(None, None, None, false, false, true) => {
self.set
.vea
.infixes(&[0; 0], &mut |&v: &[u8; 32]| proposals.push(v));
}
(Some(e), None, None, false, true, false) => {
self.set
.eav
.infixes(&e, &mut |a: &[u8; 16]| proposals.push(id_into_value(a)));
}
(Some(e), None, None, false, false, true) => {
self.set
.eva
.infixes(&e, &mut |&v: &[u8; 32]| proposals.push(v));
}
(None, Some(a), None, true, false, false) => {
self.set
.aev
.infixes(&a, &mut |e: &[u8; 16]| proposals.push(id_into_value(e)));
}
(None, Some(a), None, false, false, true) => {
self.set
.ave
.infixes(&a, &mut |&v: &[u8; 32]| proposals.push(v));
}
(None, None, Some(v), true, false, false) => {
self.set
.vea
.infixes(v, &mut |e: &[u8; 16]| proposals.push(id_into_value(e)));
}
(None, None, Some(v), false, true, false) => {
self.set
.vae
.infixes(v, &mut |a: &[u8; 16]| proposals.push(id_into_value(a)));
}
(None, Some(a), Some(v), true, false, false) => {
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&a[..]);
prefix[ID_LEN..ID_LEN + INLINE_LEN].copy_from_slice(&v[..]);
self.set.ave.infixes(&prefix, &mut |e: &[u8; 16]| {
proposals.push(id_into_value(e))
});
}
(Some(e), None, Some(v), false, true, false) => {
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&e[..]);
prefix[ID_LEN..ID_LEN + INLINE_LEN].copy_from_slice(&v[..]);
self.set.eva.infixes(&prefix, &mut |a: &[u8; 16]| {
proposals.push(id_into_value(a))
});
}
(Some(e), Some(a), None, false, false, true) => {
let mut prefix = [0u8; ID_LEN + ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&e[..]);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&a[..]);
self.set
.eav
.infixes(&prefix, &mut |&v: &[u8; 32]| proposals.push(v));
}
(_, Some(a), _, true, false, true) => {
self.set.aev.infixes(&a, &mut |e: &[u8; 16]| {
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(e);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&a[..]);
prefix[ID_LEN + ID_LEN..].copy_from_slice(&id_into_value(e));
if self.set.eav.has_prefix(&prefix) {
proposals.push(id_into_value(e));
}
});
}
(_, None, _, true, false, true) => {
self.set.eav.infixes(&[0; 0], &mut |e: &[u8; 16]| {
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(e);
prefix[ID_LEN..].copy_from_slice(&id_into_value(e));
if self.set.eva.has_prefix(&prefix) {
proposals.push(id_into_value(e));
}
});
}
(_, _, Some(v), true, true, false) => {
self.set.vae.infixes(v, &mut |a: &[u8; 16]| {
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(a);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(a);
prefix[ID_LEN + ID_LEN..].copy_from_slice(&v[..]);
if self.set.eav.has_prefix(&prefix) {
proposals.push(id_into_value(a));
}
});
}
(_, _, None, true, true, false) => {
self.set.aev.infixes(&[0; 0], &mut |a: &[u8; 16]| {
let mut prefix = [0u8; ID_LEN + ID_LEN];
prefix[0..ID_LEN].copy_from_slice(a);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(a);
if self.set.eav.has_prefix(&prefix) {
proposals.push(id_into_value(a));
}
});
}
(Some(e), _, _, false, true, true) => {
self.set.eav.infixes(&e, &mut |a: &[u8; 16]| {
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&e[..]);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(a);
prefix[ID_LEN + ID_LEN..].copy_from_slice(&id_into_value(a));
if self.set.eav.has_prefix(&prefix) {
proposals.push(id_into_value(a));
}
});
}
(None, _, _, false, true, true) => {
self.set.aev.infixes(&[0; 0], &mut |a: &[u8; 16]| {
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(a);
prefix[ID_LEN..].copy_from_slice(&id_into_value(a));
if self.set.ave.has_prefix(&prefix) {
proposals.push(id_into_value(a));
}
});
}
(_, _, _, true, true, true) => {
self.set.eav.infixes(&[0; 0], &mut |e: &[u8; 16]| {
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(e);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(e);
prefix[ID_LEN + ID_LEN..].copy_from_slice(&id_into_value(e));
if self.set.eav.has_prefix(&prefix) {
proposals.push(id_into_value(e));
}
});
}
_ => panic!("TribleSetConstraint: unreachable position-bound combo"),
}
}
fn touches(&self, variable: VariableId) -> bool {
self.term_e.is_var(variable) || self.term_a.is_var(variable) || self.term_v.is_var(variable)
}
fn write_probe_key(&self, binding: &Binding, out: &mut SmallVec<[u8; 128]>) {
for term in [&self.term_e, &self.term_a, &self.term_v] {
if let Some(value) = term.position_value(binding) {
out.extend_from_slice(value);
}
}
}
fn probe_groups(&self, frontier: &Frontier<'_>) -> (Vec<u32>, Vec<u32>) {
let rows = frontier.len();
let order: Vec<u32> = (0..rows as u32).collect();
if rows < SORTED_PROBE_MIN {
return (order.clone(), order);
}
let mut keys: SmallVec<[u8; 128]> = SmallVec::new();
for row in 0..rows {
self.write_probe_key(&frontier.row(row), &mut keys);
}
let stride = keys.len() / rows;
let key = |row: u32| {
let row = row as usize;
&keys[row * stride..(row + 1) * stride]
};
let mut order = order;
if stride != 0 {
order.sort_unstable_by(|&a, &b| key(a).cmp(key(b)).then(a.cmp(&b)));
}
let mut group = vec![0u32; rows];
let mut label = 0u32;
for i in 1..rows {
if key(order[i]) != key(order[i - 1]) {
label += 1;
}
group[order[i] as usize] = label;
}
(group, order)
}
}
impl<'a> Constraint<'a> for TribleSetConstraint {
fn variables(&self) -> VariableSet {
let mut variables = VariableSet::new_empty();
self.term_e.add_to(&mut variables);
self.term_a.add_to(&mut variables);
self.term_v.add_to(&mut variables);
variables
}
fn estimate(&self, variable: VariableId, binding: &Binding) -> Option<usize> {
let e_var = self.term_e.is_var(variable);
let a_var = self.term_a.is_var(variable);
let v_var = self.term_v.is_var(variable);
if !e_var && !a_var && !v_var {
return None;
}
let e_bound = if let Some(e) = self.term_e.position_value(binding) {
let Some(e) = id_from_value(e) else {
return Some(0);
};
Some(e)
} else {
None
};
let a_bound = if let Some(a) = self.term_a.position_value(binding) {
let Some(a) = id_from_value(a) else {
return Some(0);
};
Some(a)
} else {
None
};
let v_bound = self.term_v.position_value(binding);
Some(match (e_bound, a_bound, v_bound, e_var, a_var, v_var) {
(None, None, None, true, false, false) => self.set.eav.segmented_len(&[0; 0]),
(None, None, None, false, true, false) => self.set.aev.segmented_len(&[0; 0]),
(None, None, None, false, false, true) => self.set.vea.segmented_len(&[0; 0]),
(Some(e), None, None, false, true, false) => {
let mut prefix = [0u8; ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&e[..]);
self.set.eav.segmented_len(&prefix)
}
(Some(e), None, None, false, false, true) => {
let mut prefix = [0u8; ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&e[..]);
self.set.eva.segmented_len(&prefix)
}
(None, Some(a), None, true, false, false) => {
let mut prefix = [0u8; ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&a[..]);
self.set.aev.segmented_len(&prefix)
}
(None, Some(a), None, false, false, true) => {
let mut prefix = [0u8; ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&a[..]);
self.set.ave.segmented_len(&prefix)
}
(None, None, Some(v), true, false, false) => {
let mut prefix = [0u8; INLINE_LEN];
prefix[0..INLINE_LEN].copy_from_slice(&v[..]);
self.set.vea.segmented_len(&prefix)
}
(None, None, Some(v), false, true, false) => {
let mut prefix = [0u8; INLINE_LEN];
prefix[0..INLINE_LEN].copy_from_slice(&v[..]);
self.set.vae.segmented_len(&prefix)
}
(None, Some(a), Some(v), true, false, false) => {
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&a);
prefix[ID_LEN..ID_LEN + INLINE_LEN].copy_from_slice(v);
self.set.ave.segmented_len(&prefix)
}
(Some(e), None, Some(v), false, true, false) => {
let mut prefix = [0u8; ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&e);
prefix[ID_LEN..ID_LEN + INLINE_LEN].copy_from_slice(v);
self.set.eva.segmented_len(&prefix)
}
(Some(e), Some(a), None, false, false, true) => {
let mut prefix = [0u8; ID_LEN + ID_LEN];
prefix[0..ID_LEN].copy_from_slice(&e);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&a);
self.set.eav.segmented_len(&prefix)
}
(_, Some(a), _, true, false, true) => {
let mut prefix = [0u8; ID_LEN];
prefix.copy_from_slice(&a[..]);
self.set.aev.segmented_len(&prefix)
}
(_, None, _, true, false, true) => {
self.set.eav.segmented_len(&[0; 0])
}
(_, _, Some(v), true, true, false) => {
let mut prefix = [0u8; INLINE_LEN];
prefix.copy_from_slice(&v[..]);
self.set.vae.segmented_len(&prefix)
}
(_, _, None, true, true, false) => {
self.set.aev.segmented_len(&[0; 0])
}
(Some(e), _, _, false, true, true) => {
let mut prefix = [0u8; ID_LEN];
prefix.copy_from_slice(&e[..]);
self.set.eav.segmented_len(&prefix)
}
(None, _, _, false, true, true) => {
self.set.aev.segmented_len(&[0; 0])
}
(_, _, _, true, true, true) => {
self.set.eav.segmented_len(&[0; 0])
}
_ => panic!("TribleSetConstraint: unreachable position-bound combo"),
} as usize)
}
fn propose(
&self,
variable: VariableId,
frontier: &Frontier<'_>,
proposals: &mut ProposalBuffer,
) {
let rows = frontier.len();
if rows == 0 || !self.touches(variable) {
return;
}
let (group, order) = self.probe_groups(frontier);
let mut shared: Vec<RawInline> = Vec::new();
let mut run_start = 0;
while run_start < rows {
let lead = order[run_start];
let label = group[lead as usize];
let mut run_end = run_start + 1;
while run_end < rows && group[order[run_end] as usize] == label {
run_end += 1;
}
let base = proposals.len();
proposals.open(lead);
self.propose_row(variable, &frontier.row(lead as usize), proposals);
if run_end - run_start > 1 {
shared.clear();
shared.extend_from_slice(&proposals[base..]);
for &row in &order[run_start + 1..run_end] {
proposals.open(row);
proposals.extend_from_slice(&shared);
}
}
run_start = run_end;
}
}
fn confirm(&self, variable: VariableId, frontier: &Frontier<'_>, cands: &mut Candidates<'_>) {
let entries = cands.len();
if entries == 0 || frontier.is_empty() || !self.touches(variable) {
return;
}
let (group, _) = self.probe_groups(frontier);
#[cfg(feature = "parallel")]
{
if frontier.parallel() {
self.confirm_parallel(variable, frontier, &group, cands.reborrow());
} else {
self.confirm_grouped(variable, frontier, &group, cands);
}
}
#[cfg(not(feature = "parallel"))]
{
self.confirm_grouped(variable, frontier, &group, cands);
}
}
fn satisfied(&self, binding: &Binding) -> bool {
let e = self.term_e.position_value(binding);
let a = self.term_a.position_value(binding);
let v = self.term_v.position_value(binding);
match (e, a, v) {
(Some(e_raw), Some(a_raw), Some(v_raw)) => {
let Some(e) = id_from_value(e_raw) else {
return false;
};
let Some(a) = id_from_value(a_raw) else {
return false;
};
let mut prefix = [0u8; ID_LEN + ID_LEN + INLINE_LEN];
prefix[0..ID_LEN].copy_from_slice(&e);
prefix[ID_LEN..ID_LEN + ID_LEN].copy_from_slice(&a);
prefix[ID_LEN + ID_LEN..].copy_from_slice(v_raw);
self.set.eav.has_prefix(&prefix)
}
_ => true,
}
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "parallel")]
use std::collections::BTreeSet;
#[cfg(feature = "parallel")]
use crate::and;
use crate::find;
use crate::id::rngid;
use crate::inline::encodings::UnknownInline;
use crate::inline::Inline;
use crate::query::TriblePattern;
use crate::query::Variable;
use crate::trible::Trible;
use crate::trible::TribleSet;
#[cfg(feature = "parallel")]
use rayon::iter::{IntoParallelIterator, ParallelIterator};
#[cfg(feature = "parallel")]
fn raw_value(i: u64) -> Inline<UnknownInline> {
let mut raw = [0u8; 32];
raw[24..].copy_from_slice(&i.to_be_bytes());
Inline::new(raw)
}
#[cfg(feature = "parallel")]
fn id_inline(id: &[u8; 16]) -> Inline<crate::inline::encodings::genid::GenId> {
let mut raw = [0u8; 32];
raw[16..].copy_from_slice(id);
Inline::new(raw)
}
#[cfg(feature = "parallel")]
fn row_digest(rows: &[(Inline<UnknownInline>,)]) -> blake3::Hash {
let mut hasher = blake3::Hasher::new();
for (value,) in rows {
hasher.update(&value.raw);
}
hasher.finalize()
}
#[cfg(feature = "parallel")]
#[test]
fn parallel_confirm_preserves_bag_set_digest_and_cancellation() {
const PROPOSALS: u64 = 8192;
let entity = rngid();
let attribute = rngid();
let entity_inline = id_inline(&entity);
let attribute_inline = id_inline(&attribute);
let mut proposer = TribleSet::new();
for i in 0..PROPOSALS {
proposer.insert(&Trible::new(&entity, &attribute, &raw_value(i)));
}
let mut confirmer = TribleSet::new();
for i in (0..PROPOSALS).step_by(2) {
confirmer.insert(&Trible::new(&entity, &attribute, &raw_value(i)));
}
for i in PROPOSALS * 2..PROPOSALS * 3 {
confirmer.insert(&Trible::new(&entity, &attribute, &raw_value(i)));
}
let mut expected: Vec<_> = (0..PROPOSALS).step_by(2).map(|i| (raw_value(i),)).collect();
expected.sort_unstable();
let mut sequential: Vec<_> = find! {
(value: Inline<UnknownInline>),
and!(
proposer.pattern(entity_inline, attribute_inline, value),
confirmer.pattern(entity_inline, attribute_inline, value)
)
}
.collect();
sequential.sort_unstable();
assert_eq!(sequential, expected);
let expected_set: BTreeSet<_> = expected.iter().copied().collect();
macro_rules! collect_parallel {
($pool:expr) => {
$pool.install(|| {
find! {
(value: Inline<UnknownInline>),
and!(
proposer.pattern(entity_inline, attribute_inline, value),
confirmer.pattern(entity_inline, attribute_inline, value)
)
}
.into_par_iter()
.collect::<Vec<_>>()
})
};
}
let one_thread = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.unwrap();
let splits_before =
super::PARALLEL_CONFIRM_SPLITS.load(std::sync::atomic::Ordering::Relaxed);
let mut one_thread_rows = collect_parallel!(&one_thread);
assert_eq!(
super::PARALLEL_CONFIRM_SPLITS.load(std::sync::atomic::Ordering::Relaxed),
splits_before,
"a one-thread pool should use the serial confirm leaf"
);
one_thread_rows.sort_unstable();
assert_eq!(one_thread_rows, sequential);
for threads in [2, 4] {
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(threads)
.build()
.unwrap();
let splits_before =
super::PARALLEL_CONFIRM_SPLITS.load(std::sync::atomic::Ordering::Relaxed);
let mut parallel = collect_parallel!(&pool);
assert!(
super::PARALLEL_CONFIRM_SPLITS.load(std::sync::atomic::Ordering::Relaxed)
> splits_before,
"fixture did not split on a {threads}-thread pool"
);
parallel.sort_unstable();
assert_eq!(parallel, sequential, "parallel execution changed the bag");
assert_eq!(row_digest(¶llel), row_digest(&sequential));
assert_eq!(
parallel.iter().copied().collect::<BTreeSet<_>>(),
expected_set
);
if threads == 4 {
let partial: Vec<_> = pool.install(|| {
find! {
(value: Inline<UnknownInline>),
and!(
proposer.pattern(entity_inline, attribute_inline, value),
confirmer.pattern(entity_inline, attribute_inline, value)
)
}
.into_par_iter()
.take_any(17)
.collect()
});
assert_eq!(partial.len(), 17);
let partial_set: BTreeSet<_> = partial.iter().copied().collect();
assert_eq!(
partial_set.len(),
partial.len(),
"cancellation duplicated rows"
);
assert!(partial_set.is_subset(&expected_set));
}
}
}
#[test]
fn constant() {
let mut set = TribleSet::new();
set.insert(&Trible::new(
&rngid(),
&rngid(),
&Inline::<UnknownInline>::new([0; 32]),
));
let q = find! {
(e: Inline<_>, a: Inline<_>, v: Inline<_>),
set.pattern(e, a, v as Variable<UnknownInline>)
};
let r: Vec<_> = q.collect();
assert_eq!(1, r.len())
}
#[test]
fn self_edge_pattern_e_eq_v() {
use crate::and;
use crate::inline::encodings::genid::GenId;
fn id_as_inline(id: &[u8; 16]) -> Inline<GenId> {
let mut bytes = [0u8; 32];
bytes[16..32].copy_from_slice(id);
Inline::<GenId>::new(bytes)
}
let mut set = TribleSet::new();
let a = rngid();
let self1 = rngid();
let self2 = rngid();
let self3 = rngid();
let other = rngid();
for x in [&self1, &self2, &self3] {
set.insert(&Trible::new(x, &a, &id_as_inline(x)));
}
set.insert(&Trible::new(&self1, &a, &id_as_inline(&other)));
set.insert(&Trible::new(&other, &a, &id_as_inline(&self2)));
let q = find! {
(x: Inline<GenId>, attr: Inline<GenId>),
set.pattern(x, attr, x)
};
let r: Vec<_> = q.collect();
assert_eq!(3, r.len(), "expected 3 self-edges, got {}", r.len());
let q = find! {
(x: Inline<GenId>, attr: Inline<GenId>),
and!(
attr.is(id_as_inline(&a)),
set.pattern(x, attr, x)
)
};
let r: Vec<_> = q.collect();
assert_eq!(
3,
r.len(),
"expected 3 self-edges with bound attr, got {}",
r.len()
);
}
#[test]
fn entity_attr_dup_pattern() {
use crate::inline::encodings::genid::GenId;
fn id_as_inline(id: &[u8; 16]) -> Inline<GenId> {
let mut bytes = [0u8; 32];
bytes[16..32].copy_from_slice(id);
Inline::<GenId>::new(bytes)
}
let mut set = TribleSet::new();
let dup1 = rngid();
let dup2 = rngid();
let other = rngid();
let v1 = rngid();
let v2 = rngid();
set.insert(&Trible::new(&dup1, &dup1, &id_as_inline(&v1)));
set.insert(&Trible::new(&dup2, &dup2, &id_as_inline(&v2)));
set.insert(&Trible::new(&dup1, &other, &id_as_inline(&v1)));
set.insert(&Trible::new(&other, &dup1, &id_as_inline(&v1)));
let q = find! {
(x: Inline<GenId>, val: Inline<GenId>),
set.pattern(x, x, val)
};
let r: Vec<_> = q.collect();
assert_eq!(2, r.len(), "expected 2 entity-attr dups, got {}", r.len());
}
#[test]
fn attr_value_dup_pattern() {
use crate::inline::encodings::genid::GenId;
fn id_as_inline(id: &[u8; 16]) -> Inline<GenId> {
let mut bytes = [0u8; 32];
bytes[16..32].copy_from_slice(id);
Inline::<GenId>::new(bytes)
}
let mut set = TribleSet::new();
let dup1 = rngid(); let dup2 = rngid();
let other_attr = rngid();
let e1 = rngid();
let e2 = rngid();
let e3 = rngid();
set.insert(&Trible::new(&e1, &dup1, &id_as_inline(&dup1)));
set.insert(&Trible::new(&e2, &dup2, &id_as_inline(&dup2)));
set.insert(&Trible::new(&e3, &dup1, &id_as_inline(&dup2)));
set.insert(&Trible::new(&e3, &other_attr, &id_as_inline(&dup1)));
let q = find! {
(e: Inline<GenId>, x: Inline<GenId>),
set.pattern(e, x, x)
};
let r: Vec<_> = q.collect();
assert_eq!(2, r.len(), "expected 2 attr-value dups, got {}", r.len());
}
#[test]
fn all_three_same_pattern() {
use crate::inline::encodings::genid::GenId;
fn id_as_inline(id: &[u8; 16]) -> Inline<GenId> {
let mut bytes = [0u8; 32];
bytes[16..32].copy_from_slice(id);
Inline::<GenId>::new(bytes)
}
let mut set = TribleSet::new();
let xxx1 = rngid();
let xxx2 = rngid();
let other = rngid();
set.insert(&Trible::new(&xxx1, &xxx1, &id_as_inline(&xxx1)));
set.insert(&Trible::new(&xxx2, &xxx2, &id_as_inline(&xxx2)));
set.insert(&Trible::new(&xxx1, &xxx1, &id_as_inline(&other)));
set.insert(&Trible::new(&xxx2, &other, &id_as_inline(&xxx2)));
set.insert(&Trible::new(&other, &xxx1, &id_as_inline(&xxx1)));
let q = find! {
(x: Inline<GenId>),
set.pattern(x, x, x)
};
let r: Vec<_> = q.collect();
assert_eq!(
2,
r.len(),
"expected 2 self-self-self triples, got {}",
r.len()
);
}
}