use std::collections::HashMap;
use crate::{Error, Lattice};
pub(crate) fn perpendicular_widths(lattice: &Lattice) -> Result<[f64; 3], Error> {
let reciprocal = lattice.reciprocal(false)?;
Ok(reciprocal
.matrix()
.map(|row| 1.0 / row.iter().map(|value| value * value).sum::<f64>().sqrt()))
}
pub(crate) fn bin_counts(widths: [f64; 3], size: f64) -> [usize; 3] {
widths.map(|width| {
let bins = (width / size).floor();
if bins >= 1.0 { bins as usize } else { 1 }
})
}
pub(crate) fn bin_index(value: f64, count: usize) -> usize {
let scaled = value.rem_euclid(1.0) * count as f64;
let index = scaled.floor() as usize;
index.min(count.saturating_sub(1))
}
pub(crate) fn offsets(count: usize) -> &'static [isize] {
match count {
0 | 1 => &[0],
2 => &[-1, 0],
_ => &[-1, 0, 1],
}
}
pub(crate) fn wrap(index: usize, offset: isize, count: usize) -> usize {
let count = count as isize;
(index as isize + offset).rem_euclid(count) as usize
}
pub(crate) struct FractionalGrid<'a> {
counts: [usize; 3],
bins: HashMap<(&'a str, [usize; 3]), Vec<usize>>,
}
impl<'a> FractionalGrid<'a> {
pub(crate) fn new(widths: [f64; 3], tolerance: f64) -> Self {
let counts = if tolerance > 0.0 {
bin_counts(widths, tolerance)
} else {
[1, 1, 1]
};
Self {
counts,
bins: HashMap::new(),
}
}
pub(crate) fn for_each_candidate(
&self,
element: &str,
frac: [f64; 3],
mut visit: impl FnMut(usize) -> bool,
) {
let [count_x, count_y, count_z] = self.counts;
let [frac_x, frac_y, frac_z] = frac;
let base = [
bin_index(frac_x, count_x),
bin_index(frac_y, count_y),
bin_index(frac_z, count_z),
];
let [base_x, base_y, base_z] = base;
for &offset_x in offsets(count_x) {
let target_x = wrap(base_x, offset_x, count_x);
for &offset_y in offsets(count_y) {
let target_y = wrap(base_y, offset_y, count_y);
for &offset_z in offsets(count_z) {
let target_z = wrap(base_z, offset_z, count_z);
let Some(bucket) = self.bins.get(&(element, [target_x, target_y, target_z]))
else {
continue;
};
for &index in bucket {
if !visit(index) {
return;
}
}
}
}
}
}
pub(crate) fn insert(&mut self, element: &'a str, frac: [f64; 3], index: usize) {
let [count_x, count_y, count_z] = self.counts;
let [frac_x, frac_y, frac_z] = frac;
let key = (
element,
[
bin_index(frac_x, count_x),
bin_index(frac_y, count_y),
bin_index(frac_z, count_z),
],
);
self.bins.entry(key).or_default().push(index);
}
}