use super::{PairQuery, indices::IndexWorkspace, search::validate_cutoff, visit};
use crate::{CellList, SpatialBackend, SpatialError};
use molframe_core::parallel::{BlockExecutionError, BlockPlan, try_for_each_block_in};
pub fn count_pairs_within(query: &PairQuery<'_>) -> Result<u64, SpatialError> {
validate_cutoff(query.cutoff)?;
let indices = IndexWorkspace::new(
query.left,
query.right,
query.positions.len(),
query.context,
)?;
let left = indices.left();
let right = indices.right();
let plan = query.options.plan(left.len(), right.len(), query.cutoff)?;
let mut total = 0_u64;
if !matches!(
plan.backend,
SpatialBackend::CellList | SpatialBackend::NeighborList
) {
visit::visit_indices(query, left, right, |_| total += 1)?;
return Ok(total);
}
let index = CellList::build_in(
query.positions,
right,
query.cutoff,
query.periodic.copied(),
query.options.cell_grid,
query.context,
)?;
let same = left == right;
let owned_cells = if same { index.owned_cell_count() } else { None };
let blocks = owned_cells.map_or_else(
|| BlockPlan::new(left.len(), if same { 64 } else { 256 }),
|count| BlockPlan::new(count, 64),
);
try_for_each_block_in(
blocks,
query.context,
size_of::<u64>(),
|_, range| {
let mut count = 0_u64;
if owned_cells.is_some() {
index.for_each_owned_cell(range, query.cutoff, |_| count += 1)?;
return Ok(count);
}
if same {
index.for_each_upper_candidate(&left[range], query.cutoff, |_| count += 1)?;
return Ok(count);
}
index.for_each_candidate(&left[range], query.cutoff, |a, b, distance| {
visit::emit_unique_cross_candidate(a, b, distance, left, right, &mut |_| {
count += 1;
});
})?;
Ok(count)
},
|count| {
total = total
.checked_add(count)
.ok_or(SpatialError::NumericRangeExceeded)?;
Ok(())
},
)
.map_err(|error| match error {
BlockExecutionError::Memory(error) => SpatialError::Memory(error),
BlockExecutionError::Cancelled => SpatialError::Cancelled,
BlockExecutionError::Worker(_) => SpatialError::WorkerPanicked,
BlockExecutionError::Operation(error) => error,
})?;
Ok(total)
}
#[cfg(test)]
#[path = "count_tests.rs"]
mod tests;