use std::collections::HashMap;
pub mod morton {
fn part1by1(mut v: u64) -> u64 {
v &= 0x0000_0000_ffff_ffff;
v = (v | (v << 16)) & 0x0000_ffff_0000_ffff;
v = (v | (v << 8)) & 0x00ff_00ff_00ff_00ff;
v = (v | (v << 4)) & 0x0f0f_0f0f_0f0f_0f0f;
v = (v | (v << 2)) & 0x3333_3333_3333_3333;
v = (v | (v << 1)) & 0x5555_5555_5555_5555;
v
}
fn compact1by1(mut v: u64) -> u64 {
v &= 0x5555_5555_5555_5555;
v = (v | (v >> 1)) & 0x3333_3333_3333_3333;
v = (v | (v >> 2)) & 0x0f0f_0f0f_0f0f_0f0f;
v = (v | (v >> 4)) & 0x00ff_00ff_00ff_00ff;
v = (v | (v >> 8)) & 0x0000_ffff_0000_ffff;
v = (v | (v >> 16)) & 0x0000_0000_ffff_ffff;
v
}
pub fn encode_2d(x: u32, y: u32) -> u64 {
part1by1(x as u64) | (part1by1(y as u64) << 1)
}
pub fn decode_2d(code: u64) -> (u32, u32) {
(compact1by1(code) as u32, compact1by1(code >> 1) as u32)
}
fn part1by2(mut v: u64) -> u64 {
v &= 0x1f_ffff;
v = (v | (v << 32)) & 0x001f_0000_0000_ffff;
v = (v | (v << 16)) & 0x001f_0000_ff00_00ff;
v = (v | (v << 8)) & 0x100f_00f0_0f00_f00f;
v = (v | (v << 4)) & 0x10c3_0c30_c30c_30c3;
v = (v | (v << 2)) & 0x1249_2492_4924_9249;
v
}
fn compact1by2(mut v: u64) -> u64 {
v &= 0x1249_2492_4924_9249;
v = (v | (v >> 2)) & 0x10c3_0c30_c30c_30c3;
v = (v | (v >> 4)) & 0x100f_00f0_0f00_f00f;
v = (v | (v >> 8)) & 0x001f_0000_ff00_00ff;
v = (v | (v >> 16)) & 0x001f_0000_0000_ffff;
v = (v | (v >> 32)) & 0x1f_ffff;
v
}
pub fn encode_3d(x: u32, y: u32, z: u32) -> u64 {
part1by2(x as u64) | (part1by2(y as u64) << 1) | (part1by2(z as u64) << 2)
}
pub fn decode_3d(code: u64) -> (u32, u32, u32) {
(compact1by2(code) as u32, compact1by2(code >> 1) as u32, compact1by2(code >> 2) as u32)
}
}
#[derive(Debug, Clone, Copy)]
pub struct GridQuantizer {
origin_x: f32,
origin_y: f32,
cell_size: f32,
}
impl GridQuantizer {
pub fn new(origin: (f32, f32), cell_size: f32) -> Self {
if cell_size <= 0.0 || cell_size.is_nan() {
panic!("GridQuantizer cell_size must be positive");
}
Self {
origin_x: origin.0,
origin_y: origin.1,
cell_size,
}
}
pub fn cell(&self, x: f32, y: f32) -> (u32, u32) {
let cx = ((x - self.origin_x) / self.cell_size).floor().max(0.0);
let cy = ((y - self.origin_y) / self.cell_size).floor().max(0.0);
(cx as u32, cy as u32)
}
pub fn key(&self, x: f32, y: f32) -> u64 {
let (cx, cy) = self.cell(x, y);
morton::encode_2d(cx, cy)
}
pub fn corner(&self, cx: u32, cy: u32) -> (f32, f32) {
(
self.origin_x + cx as f32 * self.cell_size,
self.origin_y + cy as f32 * self.cell_size,
)
}
pub fn cells_for_radius(&self, radius: f32) -> u32 {
(radius / self.cell_size).ceil().max(0.0) as u32
}
pub fn keys_in_radius(&self, x: f32, y: f32, radius: f32) -> impl Iterator<Item = u64> + use<> {
self.cells_in_radius(x, y, radius).map(|(gx, gy)| morton::encode_2d(gx, gy))
}
pub fn cells_in_radius(&self, x: f32, y: f32, radius: f32) -> impl Iterator<Item = (u32, u32)> + use<> {
let (cx, cy) = self.cell(x, y);
let r = self.cells_for_radius(radius);
let (x0, x1) = (cx.saturating_sub(r), cx.saturating_add(r));
let (y0, y1) = (cy.saturating_sub(r), cy.saturating_add(r));
(y0..=y1).flat_map(move |gy| (x0..=x1).map(move |gx| (gx, gy)))
}
pub fn cell_size(&self) -> f32 {
self.cell_size
}
}
#[derive(Debug, Clone, Copy)]
pub struct CellSpace {
quantizer: GridQuantizer,
side: u32,
}
impl CellSpace {
pub fn new(quantizer: GridQuantizer, extent: f32) -> Self {
if !(extent > 0.0) {
panic!("CellSpace extent must be positive");
}
let side = (extent / quantizer.cell_size()).ceil() as u32 + 1;
Self { quantizer, side }
}
pub fn side(&self) -> u32 {
self.side
}
pub fn len(&self) -> usize {
self.side as usize * self.side as usize
}
pub fn is_empty(&self) -> bool {
self.side == 0
}
pub fn quantizer(&self) -> &GridQuantizer {
&self.quantizer
}
pub fn index_at(&self, cx: u32, cy: u32) -> usize {
let last = self.side.saturating_sub(1);
cy.min(last) as usize * self.side as usize + cx.min(last) as usize
}
pub fn index_of(&self, x: f32, y: f32) -> usize {
let (cx, cy) = self.quantizer.cell(x, y);
self.index_at(cx, cy)
}
pub fn cell_at(&self, index: usize) -> (u32, u32) {
let side = self.side as usize;
((index % side) as u32, (index / side) as u32)
}
pub fn corner(&self, index: usize) -> (f32, f32) {
let (cx, cy) = self.cell_at(index);
self.quantizer.corner(cx, cy)
}
pub fn indices_in_radius(&self, x: f32, y: f32, radius: f32) -> impl Iterator<Item = usize> + use<> {
let (cx, cy) = self.quantizer.cell(x, y);
let r = self.quantizer.cells_for_radius(radius);
let last = self.side.saturating_sub(1);
let (x0, x1) = (cx.saturating_sub(r).min(last), cx.saturating_add(r).min(last));
let (y0, y1) = (cy.saturating_sub(r).min(last), cy.saturating_add(r).min(last));
let side = self.side as usize;
(y0..=y1).flat_map(move |gy| (x0..=x1).map(move |gx| gy as usize * side + gx as usize))
}
}
#[derive(Debug, Clone)]
pub struct CellTable<T> {
space: CellSpace,
slots: Vec<T>,
}
impl<T: Default> CellTable<T> {
pub fn new(space: CellSpace) -> Self {
let mut slots = Vec::new();
slots.resize_with(space.len(), T::default);
Self { space, slots }
}
pub fn reset(&mut self) {
for slot in self.slots.iter_mut() {
*slot = T::default();
}
}
}
impl<T> CellTable<T> {
pub fn space(&self) -> &CellSpace {
&self.space
}
pub fn get(&self, index: usize) -> Option<&T> {
self.slots.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
self.slots.get_mut(index)
}
pub fn at(&self, x: f32, y: f32) -> Option<&T> {
self.slots.get(self.space.index_of(x, y))
}
pub fn at_mut(&mut self, x: f32, y: f32) -> Option<&mut T> {
let index = self.space.index_of(x, y);
self.slots.get_mut(index)
}
pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
self.slots.iter().enumerate()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
self.slots.iter_mut().enumerate()
}
pub fn len(&self) -> usize {
self.slots.len()
}
pub fn is_empty(&self) -> bool {
self.slots.is_empty()
}
}
impl<T: Clearable> CellTable<T> {
pub fn clear_each(&mut self) {
for slot in self.slots.iter_mut() {
slot.clear_in_place();
}
}
pub fn occupied(&self) -> impl Iterator<Item = (usize, &T)> {
self.slots.iter().enumerate().filter(|(_, slot)| !slot.is_empty_slot())
}
}
pub trait Clearable {
fn clear_in_place(&mut self);
fn is_empty_slot(&self) -> bool;
}
impl<T> Clearable for Vec<T> {
fn clear_in_place(&mut self) {
self.clear();
}
fn is_empty_slot(&self) -> bool {
self.is_empty()
}
}
impl<T> Clearable for Option<T> {
fn clear_in_place(&mut self) {
*self = None;
}
fn is_empty_slot(&self) -> bool {
self.is_none()
}
}
#[derive(Debug, Clone)]
pub struct SpatialGrid<Id: Copy> {
quantizer: GridQuantizer,
cells: HashMap<u64, Vec<Id>>,
}
impl<Id: Copy> SpatialGrid<Id> {
pub fn new(quantizer: GridQuantizer) -> Self {
Self {
quantizer,
cells: HashMap::new(),
}
}
pub fn clear(&mut self) {
for bucket in self.cells.values_mut() {
bucket.clear();
}
}
pub fn insert(&mut self, id: Id, x: f32, y: f32) {
let key = self.quantizer.key(x, y);
self.cells.entry(key).or_default().push(id);
}
pub fn query_radius(&self, x: f32, y: f32, radius: f32, out: &mut Vec<Id>) {
for key in self.quantizer.keys_in_radius(x, y, radius) {
out.extend_from_slice(self.members(key));
}
}
pub fn members(&self, key: u64) -> &[Id] {
self.cells.get(&key).map_or(&[], Vec::as_slice)
}
pub fn occupied(&self) -> impl Iterator<Item = (u64, &[Id])> {
self
.cells
.iter()
.filter(|(_, bucket)| !bucket.is_empty())
.map(|(key, bucket)| (*key, bucket.as_slice()))
}
pub fn quantizer(&self) -> &GridQuantizer {
&self.quantizer
}
}
#[derive(Debug, Clone)]
pub struct DenseGrid<Id: Copy> {
cells: CellTable<Vec<Id>>,
filled: Vec<usize>,
}
impl<Id: Copy> DenseGrid<Id> {
pub fn new(space: CellSpace) -> Self {
Self {
cells: CellTable::new(space),
filled: Vec::new(),
}
}
pub fn clear(&mut self) {
for index in self.filled.drain(..) {
if let Some(cell) = self.cells.get_mut(index) {
cell.clear();
}
}
}
pub fn insert(&mut self, id: Id, x: f32, y: f32) {
let index = self.cells.space().index_of(x, y);
if let Some(cell) = self.cells.get_mut(index) {
if cell.is_empty() {
self.filled.push(index);
}
cell.push(id);
}
}
pub fn query_radius(&self, x: f32, y: f32, radius: f32, out: &mut Vec<Id>) {
for index in self.cells.space().indices_in_radius(x, y, radius) {
out.extend_from_slice(self.members(index));
}
}
pub fn members(&self, index: usize) -> &[Id] {
self.cells.get(index).map_or(&[], Vec::as_slice)
}
pub fn occupied(&self) -> impl Iterator<Item = (usize, &[Id])> {
self.filled.iter().map(|&index| (index, self.members(index)))
}
pub fn space(&self) -> &CellSpace {
self.cells.space()
}
pub fn quantizer(&self) -> &GridQuantizer {
self.cells.space().quantizer()
}
}
#[derive(Debug, Clone, Copy)]
pub struct TierBoundary {
enter: f32,
leave: f32,
}
impl TierBoundary {
pub const fn new(enter: f32, leave: f32) -> Self {
debug_assert!(enter <= leave, "hysteresis needs enter <= leave");
Self {
enter,
leave: if leave < enter { enter } else { leave },
}
}
pub fn admits(&self, was_inside: bool, distance: f32) -> bool {
distance <= if was_inside { self.leave } else { self.enter }
}
}
#[derive(Debug, Clone, Default)]
pub struct VisibilitySet {
words: Vec<u64>,
}
impl VisibilitySet {
pub fn new() -> Self {
Self { words: Vec::new() }
}
pub fn with_capacity(max_index: u32) -> Self {
Self {
words: vec![0; (max_index as usize / 64) + 1],
}
}
pub fn clear(&mut self) {
for w in &mut self.words {
*w = 0;
}
}
pub fn insert(&mut self, index: u32) {
let (w, bit) = (index as usize / 64, index as usize % 64);
if w >= self.words.len() {
self.words.resize(w + 1, 0);
}
self.words[w] |= 1u64 << bit;
}
pub fn remove(&mut self, index: u32) {
let (w, bit) = (index as usize / 64, index as usize % 64);
if let Some(word) = self.words.get_mut(w) {
*word &= !(1u64 << bit);
}
}
pub fn contains(&self, index: u32) -> bool {
let (w, bit) = (index as usize / 64, index as usize % 64);
self.words.get(w).is_some_and(|word| word & (1u64 << bit) != 0)
}
pub fn diff(&self, previous: &VisibilitySet, entered: &mut Vec<u32>, left: &mut Vec<u32>) {
let n = self.words.len().max(previous.words.len());
for w in 0..n {
let cur = self.words.get(w).copied().unwrap_or(0);
let prev = previous.words.get(w).copied().unwrap_or(0);
emit_bits(cur & !prev, w, entered);
emit_bits(prev & !cur, w, left);
}
}
pub fn iter(&self) -> impl Iterator<Item = u32> + '_ {
self.words.iter().enumerate().flat_map(|(w, &word)| BitIter { word, base: (w * 64) as u32 })
}
pub fn digest(&self) -> u64 {
SetDigest::from_keys(self.iter().map(u64::from)).digest()
}
pub fn count(&self) -> usize {
self.words.iter().map(|w| w.count_ones() as usize).sum()
}
#[cfg(test)]
fn from_iter_for_test<I: IntoIterator<Item = u32>>(ids: I) -> Self {
let mut v = Self::new();
for i in ids {
v.insert(i);
}
v
}
}
pub use plaza_client_utils::digest::SetDigest;
fn emit_bits(mut word: u64, w: usize, out: &mut Vec<u32>) {
let base = (w * 64) as u32;
while word != 0 {
out.push(base + word.trailing_zeros());
word &= word - 1; }
}
struct BitIter {
word: u64,
base: u32,
}
impl Iterator for BitIter {
type Item = u32;
fn next(&mut self) -> Option<u32> {
if self.word == 0 {
return None;
}
let idx = self.base + self.word.trailing_zeros();
self.word &= self.word - 1;
Some(idx)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn morton_2d_round_trips_across_the_range() {
for &(x, y) in &[(0u32, 0u32), (1, 0), (0, 1), (12345, 67890), (u32::MAX, u32::MAX), (0xdead_beef, 0x0bad_f00d)] {
let code = morton::encode_2d(x, y);
assert_eq!(morton::decode_2d(code), (x, y), "2d round trip for ({x}, {y})");
}
}
#[test]
fn morton_3d_round_trips_within_21_bits() {
let max21 = (1u32 << 21) - 1;
for &(x, y, z) in &[(0u32, 0u32, 0u32), (1, 2, 3), (max21, max21, max21), (100_000, 5, 2_000_000)] {
let code = morton::encode_3d(x, y, z);
assert_eq!(morton::decode_3d(code), (x, y, z), "3d round trip for ({x}, {y}, {z})");
}
}
#[test]
fn morton_preserves_locality_within_a_cell_row() {
let a = morton::encode_2d(10, 10);
let b = morton::encode_2d(11, 10);
let far = morton::encode_2d(10, 1000);
assert!((a as i64 - b as i64).abs() < (a as i64 - far as i64).abs(), "neighbours are closer in code than a distant cell");
}
#[test]
fn the_quantizer_maps_points_to_cells_and_clamps_below_the_origin() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
assert_eq!(q.cell(5.0, 5.0), (0, 0));
assert_eq!(q.cell(15.0, 25.0), (1, 2));
assert_eq!(q.cell(-4.0, -100.0), (0, 0), "below the origin clamps to cell 0");
assert_eq!(q.cells_for_radius(25.0), 3, "25 world units spans 3 cells of 10");
}
#[test]
#[should_panic]
fn a_non_positive_cell_size_panics() {
let _ = GridQuantizer::new((0.0, 0.0), 0.0);
}
#[test]
fn the_grid_returns_ids_near_a_point_and_omits_distant_ones() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
let mut grid = SpatialGrid::new(q);
grid.insert(1u32, 5.0, 5.0); grid.insert(2, 12.0, 5.0); grid.insert(3, 500.0, 500.0);
let mut out = Vec::new();
grid.query_radius(5.0, 5.0, 10.0, &mut out); assert!(out.contains(&1) && out.contains(&2), "near ids returned: {out:?}");
assert!(!out.contains(&3), "distant id omitted");
}
#[test]
fn clearing_the_grid_keeps_capacity_and_empties_buckets() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
let mut grid = SpatialGrid::new(q);
grid.insert(1u32, 5.0, 5.0);
grid.clear();
let mut out = Vec::new();
grid.query_radius(5.0, 5.0, 10.0, &mut out);
assert!(out.is_empty(), "cleared grid returns nothing");
}
#[test]
fn keys_in_radius_walks_the_window_query_radius_reads() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
let keys: Vec<u64> = q.keys_in_radius(25.0, 25.0, 10.0).collect();
assert_eq!(keys.len(), 9, "one cell of slack each way around (2,2): {keys:?}");
for gx in 1..=3u32 {
for gy in 1..=3u32 {
assert!(keys.contains(&morton::encode_2d(gx, gy)), "missing cell ({gx},{gy})");
}
}
let clamped: Vec<u64> = q.keys_in_radius(5.0, 5.0, 10.0).collect();
assert_eq!(clamped.len(), 4, "the window clamps at the origin: {clamped:?}");
}
#[test]
fn a_cell_first_pass_reaches_everyone_a_viewer_first_pass_does() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
let mut grid = SpatialGrid::new(q);
for (id, x, y) in [(1u32, 5.0, 5.0), (2, 12.0, 5.0), (3, 500.0, 500.0)] {
grid.insert(id, x, y);
}
let placed: usize = grid.occupied().map(|(_, ids)| ids.len()).count();
assert_eq!(placed, 3, "three ids in three cells");
assert_eq!(grid.members(q.key(12.0, 5.0)), &[2]);
assert!(grid.members(q.key(50.0, 50.0)).is_empty(), "a vacant cell is empty, not an error");
let mut viewer_first = Vec::new();
grid.query_radius(5.0, 5.0, 10.0, &mut viewer_first);
let mut cell_first: Vec<u32> = q
.keys_in_radius(5.0, 5.0, 10.0)
.flat_map(|key| grid.members(key).iter().copied())
.collect();
viewer_first.sort_unstable();
cell_first.sort_unstable();
assert_eq!(viewer_first, cell_first);
}
#[test]
fn a_cell_space_indexes_the_same_cells_the_quantizer_names() {
let q = GridQuantizer::new((-100.0, -100.0), 10.0);
let space = CellSpace::new(q, 200.0);
assert_eq!(space.side(), 21);
assert_eq!(space.len(), 441);
let index = space.index_of(-95.0, -95.0);
assert_eq!(space.cell_at(index), q.cell(-95.0, -95.0));
assert_eq!(space.corner(index), (-100.0, -100.0));
let far = space.index_of(9000.0, 9000.0);
assert!(far < space.len());
assert_eq!(space.cell_at(far), (20, 20));
}
#[test]
fn a_dense_window_agrees_with_the_morton_one() {
let q = GridQuantizer::new((-100.0, -100.0), 10.0);
let space = CellSpace::new(q, 200.0);
let dense: Vec<(u32, u32)> = space.indices_in_radius(0.0, 0.0, 10.0).map(|i| space.cell_at(i)).collect();
let sparse: Vec<(u32, u32)> = q.cells_in_radius(0.0, 0.0, 10.0).collect();
assert_eq!(dense, sparse, "the two addressing schemes walk the same cells");
assert_eq!(dense.len(), 9);
}
#[test]
fn one_cell_space_keys_the_three_tables_a_publisher_needs() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
let space = CellSpace::new(q, 100.0);
let mut bodies: CellTable<Vec<u32>> = CellTable::new(space);
let mut payloads: CellTable<Option<Vec<u8>>> = CellTable::new(space);
let mut audience: CellTable<Vec<u16>> = CellTable::new(space);
bodies.clear_each();
for (id, x, y) in [(1u32, 5.0, 5.0), (2, 7.0, 5.0), (3, 55.0, 55.0)] {
bodies.at_mut(x, y).unwrap().push(id);
}
assert_eq!(bodies.occupied().count(), 2, "two cells hold anything");
payloads.clear_each();
for (index, ids) in bodies.occupied() {
payloads.get_mut(index).unwrap().replace(ids.iter().map(|i| *i as u8).collect());
}
assert_eq!(payloads.occupied().count(), 2);
audience.clear_each();
for (viewer, x, y) in [(10u16, 5.0, 5.0), (11, 55.0, 55.0)] {
for index in space.indices_in_radius(x, y, 10.0) {
if payloads.get(index).is_some_and(|p| p.is_some()) {
audience.get_mut(index).unwrap().push(viewer);
}
}
}
let listeners: usize = audience.occupied().map(|(_, v)| v.len()).sum();
assert_eq!(listeners, 2, "each viewer hears the one occupied cell it stands in");
assert_eq!(audience.at(5.0, 5.0).unwrap(), &[10]);
}
#[test]
fn clearing_a_table_keeps_each_cell_s_allocation() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
let mut table: CellTable<Vec<u32>> = CellTable::new(CellSpace::new(q, 100.0));
let index = table.space().index_of(5.0, 5.0);
table.get_mut(index).unwrap().extend([1, 2, 3, 4]);
let capacity = table.get(index).unwrap().capacity();
table.clear_each();
assert!(table.get(index).unwrap().is_empty());
assert_eq!(table.get(index).unwrap().capacity(), capacity, "a rebuild must not churn the heap");
assert_eq!(table.occupied().count(), 0);
}
#[test]
#[should_panic(expected = "extent must be positive")]
fn a_cell_space_needs_an_extent() {
CellSpace::new(GridQuantizer::new((0.0, 0.0), 10.0), 0.0);
}
#[test]
fn occupied_skips_buckets_a_clear_emptied() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
let mut grid = SpatialGrid::new(q);
grid.insert(1u32, 5.0, 5.0);
grid.insert(2, 25.0, 25.0);
grid.clear();
grid.insert(3u32, 25.0, 25.0);
let cells: Vec<(u64, Vec<u32>)> = grid.occupied().map(|(k, ids)| (k, ids.to_vec())).collect();
assert_eq!(cells, vec![(q.key(25.0, 25.0), vec![3])]);
}
#[test]
fn a_visibility_set_inserts_and_contains() {
let mut v = VisibilitySet::new();
v.insert(3);
v.insert(70); assert!(v.contains(3) && v.contains(70));
assert!(!v.contains(4));
assert_eq!(v.count(), 2);
let seen: Vec<u32> = v.iter().collect();
assert_eq!(seen, vec![3, 70], "iterates ascending across words");
}
#[test]
fn a_digest_ignores_order_but_not_contents() {
let a = SetDigest::from_keys([7u64, 1, 99, 4]);
let b = SetDigest::from_keys([99u64, 4, 7, 1]);
assert_eq!(a.digest(), b.digest(), "the same set in a different order agrees");
let c = SetDigest::from_keys([7u64, 1, 99, 5]);
assert_ne!(a.digest(), c.digest(), "a different member disagrees");
let d = SetDigest::from_keys([7u64, 1, 99]);
assert_ne!(a.digest(), d.digest(), "a missing member disagrees");
}
#[test]
fn maintaining_a_digest_incrementally_matches_rebuilding_it() {
let mut live = SetDigest::new();
for k in [3u64, 9, 21, 40] {
live.insert(k);
}
live.remove(9);
live.insert(77);
let rebuilt = SetDigest::from_keys([3u64, 21, 40, 77]);
assert_eq!(live.digest(), rebuilt.digest(), "O(1) updates agree with a full rebuild");
assert_eq!(live.len(), 4);
}
#[test]
fn a_delta_that_never_landed_is_detectable() {
let mut server = SetDigest::from_keys([1u64, 2, 3, 4]);
let client = SetDigest::from_keys([1u64, 2, 3, 4]);
assert_eq!(server.digest(), client.digest());
server.remove(3); assert_ne!(server.digest(), client.digest(), "the mirror is stale and it shows");
}
#[test]
fn a_visibility_set_digest_tracks_its_membership() {
let mut v = VisibilitySet::new();
for i in [2u32, 8, 300] {
v.insert(i);
}
let before = v.digest();
v.remove(8);
assert_ne!(before, v.digest());
let same = VisibilitySet::from_iter_for_test([2u32, 300]);
assert_eq!(v.digest(), same.digest(), "membership alone decides the digest");
}
#[test]
fn removing_clears_a_bit_so_a_reused_slot_reads_as_a_fresh_arrival() {
let mut v = VisibilitySet::new();
v.insert(5);
v.remove(5);
assert!(!v.contains(5));
let mut next = VisibilitySet::new();
next.insert(5);
let (mut entered, mut left) = (Vec::new(), Vec::new());
next.diff(&v, &mut entered, &mut left);
assert_eq!(entered, vec![5]);
assert!(left.is_empty());
}
#[test]
fn the_visibility_diff_reports_spawns_and_despawns() {
let mut prev = VisibilitySet::new();
for i in [1, 2, 3] {
prev.insert(i);
}
let mut cur = VisibilitySet::new();
for i in [2, 3, 4, 100] {
cur.insert(i);
}
let (mut entered, mut left) = (Vec::new(), Vec::new());
cur.diff(&prev, &mut entered, &mut left);
assert_eq!(entered, vec![4, 100], "newly visible: spawn these");
assert_eq!(left, vec![1], "no longer visible: despawn these");
}
#[test]
fn a_full_relevance_pass_matches_a_brute_force_check() {
let q = GridQuantizer::new((0.0, 0.0), 32.0);
let mut grid = SpatialGrid::new(q);
let radius = 50.0;
let entities: Vec<(u32, f32, f32)> = (0..200u32)
.map(|i| {
let x = ((i * 73 % 1000) as f32) + 0.5;
let y = ((i * 149 % 1000) as f32) + 0.5;
(i, x, y)
})
.collect();
for &(id, x, y) in &entities {
grid.insert(id, x, y);
}
let (vx, vy) = (500.0f32, 500.0f32); let brute: std::collections::BTreeSet<u32> = entities
.iter()
.filter(|(_, x, y)| ((x - vx).powi(2) + (y - vy).powi(2)).sqrt() <= radius)
.map(|(id, _, _)| *id)
.collect();
let mut candidates = Vec::new();
grid.query_radius(vx, vy, radius, &mut candidates);
let grid_result: std::collections::BTreeSet<u32> = candidates
.into_iter()
.filter(|id| {
let (_, x, y) = entities[*id as usize];
((x - vx).powi(2) + (y - vy).powi(2)).sqrt() <= radius
})
.collect();
assert_eq!(grid_result, brute, "grid + exact test equals brute force");
}
#[test]
fn a_peer_loitering_on_the_boundary_does_not_flap() {
let tier = TierBoundary::new(100.0, 120.0);
assert!(tier.admits(true, 110.0));
assert!(tier.admits(true, 119.0));
assert!(!tier.admits(false, 110.0));
assert!(!tier.admits(false, 101.0));
assert!(tier.admits(false, 99.0));
assert!(!tier.admits(true, 121.0));
}
#[test]
fn the_dense_grid_answers_what_the_hashed_one_answers() {
let q = GridQuantizer::new((-100.0, -100.0), 10.0);
let mut hashed = SpatialGrid::new(q);
let mut dense = DenseGrid::new(CellSpace::new(q, 200.0));
for id in 0..400u32 {
let (x, y) = (-100.0 + (id % 20) as f32 * 10.3, -100.0 + (id / 20) as f32 * 9.7 + 0.5);
hashed.insert(id, x, y);
dense.insert(id, x, y);
}
for (x, y) in [(0.0, 0.0), (-95.0, -95.0), (95.0, 95.0), (33.3, -12.0)] {
let (mut from_hashed, mut from_dense) = (Vec::new(), Vec::new());
hashed.query_radius(x, y, 15.0, &mut from_hashed);
dense.query_radius(x, y, 15.0, &mut from_dense);
from_hashed.sort_unstable();
from_dense.sort_unstable();
assert!(!from_dense.is_empty(), "query at ({x}, {y}) found nobody");
assert_eq!(from_dense, from_hashed, "query at ({x}, {y})");
}
assert_eq!(dense.occupied().count(), hashed.occupied().count());
}
#[test]
fn clearing_the_dense_grid_keeps_each_cell_s_capacity() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
let mut grid = DenseGrid::new(CellSpace::new(q, 100.0));
for id in 0..8u32 {
grid.insert(id, 5.0, 5.0);
}
let index = grid.space().index_of(5.0, 5.0);
let capacity = grid.cells.get(index).unwrap().capacity();
grid.clear();
assert!(grid.members(index).is_empty());
assert_eq!(grid.cells.get(index).unwrap().capacity(), capacity, "a rebuild must not churn the heap");
assert_eq!(grid.occupied().count(), 0);
assert!(grid.cells.occupied().count() == 0, "the table itself is empty, not only the list");
let mut out = Vec::new();
grid.query_radius(5.0, 5.0, 10.0, &mut out);
assert!(out.is_empty());
grid.insert(9, 5.0, 5.0);
grid.insert(10, 5.0, 5.0);
grid.insert(11, 95.0, 95.0);
let cells: Vec<usize> = grid.occupied().map(|(index, _)| index).collect();
assert_eq!(cells, vec![index, grid.space().index_of(95.0, 95.0)], "each filled cell listed once, as it filled");
}
#[test]
fn a_point_outside_the_space_is_filed_at_the_edge_and_found_there() {
let q = GridQuantizer::new((0.0, 0.0), 10.0);
let mut grid = DenseGrid::new(CellSpace::new(q, 100.0));
grid.insert(1u32, 5000.0, 5000.0);
grid.insert(2, -50.0, -50.0);
let last = grid.space().side() - 1;
assert_eq!(grid.members(grid.space().index_at(last, last)), &[1]);
assert_eq!(grid.members(grid.space().index_at(0, 0)), &[2]);
assert!(grid.members(grid.space().len()).is_empty(), "an index past the space is empty, not a panic");
let mut out = Vec::new();
grid.query_radius(100.0, 100.0, 5.0, &mut out);
assert_eq!(out, vec![1], "a query at the edge reaches the border cell");
}
}