use rustc_hash::FxHashMap;
use std::collections::hash_map::Entry;
const NONE: usize = usize::MAX;
type Edge = (u32, u32);
#[derive(Default)]
struct Links { bytes: Vec<u8>, width: usize }
impl Links {
fn reset(&mut self, slots: usize) {
self.width = if slots <= u16::MAX as usize { 2 } else { 4 };
self.bytes.clear();
self.bytes.reserve_exact(slots * self.width);
self.bytes.resize(slots * self.width, 0xff);
}
#[inline]
fn get(&self, slot: usize) -> usize {
let start = slot * self.width;
if self.width == 2 {
let value = u16::from_ne_bytes(self.bytes[start..start + 2].try_into().unwrap());
if value == u16::MAX { NONE } else { value as usize }
} else {
let value = u32::from_ne_bytes(self.bytes[start..start + 4].try_into().unwrap());
if value == u32::MAX { NONE } else { value as usize }
}
}
#[inline]
fn set(&mut self, slot: usize, value: usize) {
let start = slot * self.width;
if self.width == 2 {
self.bytes[start..start + 2].copy_from_slice(&(value as u16).to_ne_bytes());
} else {
self.bytes[start..start + 4].copy_from_slice(&(value as u32).to_ne_bytes());
}
}
}
enum Storage {
Dense { first: FxHashMap<Edge, usize>, links: Links },
Wide(FxHashMap<Edge, EdgeInc>),
}
pub(super) struct EdgeAdjacency {
storage: Storage,
#[cfg(test)]
force_wide: bool,
}
impl Default for EdgeAdjacency {
fn default() -> Self {
Self {
storage: Storage::Dense { first: FxHashMap::default(), links: Links::default() },
#[cfg(test)]
force_wide: false,
}
}
}
impl EdgeAdjacency {
pub(super) fn reset(&mut self, triangles: usize) {
let slots = triangles * 3;
let wide = slots > u32::MAX as usize;
#[cfg(test)]
let wide = wide || self.force_wide;
if wide != matches!(self.storage, Storage::Wide(_)) {
self.storage = if wide { Storage::Wide(FxHashMap::default()) }
else { Storage::Dense { first: FxHashMap::default(), links: Links::default() } };
}
match &mut self.storage {
Storage::Dense { first, links } => {
first.clear();
first.reserve(triangles * 2);
links.reset(slots);
}
Storage::Wide(edges) => {
edges.clear();
edges.reserve(triangles * 2);
}
}
}
pub(super) fn push(&mut self, edge: Edge, slot: usize) {
match &mut self.storage {
Storage::Dense { first, links } => match first.entry(edge) {
Entry::Vacant(entry) => { entry.insert(slot); }
Entry::Occupied(mut entry) => {
let previous = *entry.get();
if previous == NONE { return; }
let paired = links.get(previous);
if paired == NONE {
links.set(previous, slot);
links.set(slot, previous);
} else {
links.set(previous, NONE);
links.set(paired, NONE);
*entry.get_mut() = NONE;
}
}
},
Storage::Wide(edges) => { edges.entry(edge).or_default().push(slot / 3); }
}
}
#[inline]
pub(super) fn neighbors(&self, edge: Edge, slot: usize) -> (bool, [usize; 2]) {
match &self.storage {
Storage::Dense { links, .. } => {
let neighbor = links.get(slot);
(neighbor != NONE, [if neighbor == NONE { NONE } else { neighbor / 3 }, NONE])
}
Storage::Wide(edges) => {
let inc = &edges[&edge];
let mut neighbors = [NONE; 2];
if inc.count() <= 2 {
for (target, &source) in neighbors.iter_mut().zip(inc.incident()) { *target = source; }
}
(inc.count() == 2, neighbors)
}
}
}
#[cfg(test)]
pub(super) fn wide_for_test() -> Self {
Self { storage: Storage::Wide(FxHashMap::default()), force_wide: true }
}
}
#[derive(Clone, Copy)]
pub(super) struct EdgeInc { pub(super) tris: [usize; 2] }
impl Default for EdgeInc {
fn default() -> Self { Self { tris: [usize::MAX; 2] } }
}
impl EdgeInc {
pub(super) const NONMANIFOLD: usize = usize::MAX - 1;
#[inline]
pub(super) fn push(&mut self, t: usize) {
if self.tris[1] == Self::NONMANIFOLD {
let count = self.tris[0] as u32 + 1;
if count == 0 { *self = Self::default(); }
else { self.tris[0] = count as usize; }
} else if self.tris[0] == usize::MAX {
self.tris[0] = t;
} else if self.tris[1] == usize::MAX {
self.tris[1] = t;
} else {
self.tris = [3, Self::NONMANIFOLD];
}
}
#[inline]
pub(super) fn count(&self) -> u32 {
if self.tris[1] == Self::NONMANIFOLD { self.tris[0] as u32 }
else if self.tris[0] == usize::MAX { 0 }
else if self.tris[1] == usize::MAX { 1 }
else { 2 }
}
#[inline]
pub(super) fn incident(&self) -> &[usize] { &self.tris[..self.count() as usize] }
}