use crate::brute::finite;
use molframe_core::numeric::{f32_to_i64, usize_to_f64};
const SAMPLE: usize = 65_536;
const CROWDING_LIMIT: f64 = 8.0;
pub(super) fn is_crowded(positions: &[[f32; 3]], atoms: &[u32], cutoff: f32) -> bool {
if atoms.len() < 2 * 1_024 || !cutoff.is_finite() || cutoff <= 0.0 {
return false;
}
let stride = atoms.len().div_ceil(SAMPLE);
let mut keys: Vec<[i64; 3]> = atoms
.iter()
.step_by(stride)
.filter_map(|&atom| positions.get(atom as usize).copied())
.filter(|&point| finite(point))
.map(|point| point.map(|value| f32_to_i64((value / cutoff).floor())))
.collect();
if keys.is_empty() {
return false;
}
keys.sort_unstable();
let mut sum_squares = 0.0_f64;
let mut run = 0.0_f64;
let mut previous = keys[0];
for key in &keys {
if *key == previous {
run += 1.0;
} else {
sum_squares += run * run;
run = 1.0;
previous = *key;
}
}
sum_squares += run * run;
let sampled = usize_to_f64(keys.len());
let rate = 1.0 / usize_to_f64(stride);
(sum_squares - sampled) / (sampled * rate) + 1.0 > CROWDING_LIMIT
}