#[cfg(test)]
mod tests;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use std::default::Default;
use vob::Vob;
pub(crate) struct SetVob {
number_of_set_bits: u32,
vob: Vob<u32>,
}
impl SetVob {
pub(crate) fn fill_with_false(size: usize) -> Self {
let mut v: Vob<u32> = Vob::<u32>::new_with_storage_type(0);
v.resize(size, false);
Self {
number_of_set_bits: 0,
vob: v,
}
}
#[inline(always)]
pub(crate) fn get(&self, index: u32) -> bool {
self.vob.get(index as usize).unwrap()
}
#[inline(always)]
pub(crate) fn set(&mut self, index: u32) {
if self.vob.set(index as usize, true) {
self.number_of_set_bits += 1;
}
}
#[inline(always)]
pub(crate) fn number_of_set_bits(&self) -> u32 {
self.number_of_set_bits
}
}
fn unwind_line(
mut current: u32,
next: Option<u32>,
adjacency_map: &FxHashMap<u32, SmallVec<[u32; 2]>>,
termination_nodes: &SetVob,
visited: &mut SetVob,
) -> Vec<u32> {
let mut line = Vec::<u32>::default();
let mut prev = Option::<u32>::None;
if let Some(next) = next {
line.push(current);
if !termination_nodes.get(current) {
visited.set(current);
}
prev = Some(current);
current = next;
}
loop {
if visited.get(current) || termination_nodes.get(current) {
line.push(current);
break;
}
visited.set(current);
line.push(current);
if let Some(neighbours) = adjacency_map.get(¤t) {
if neighbours.len() != 2 {
break;
}
if !(visited.get(neighbours[0]) || prev.is_some() && prev.unwrap() == neighbours[0]) {
prev = Some(current);
current = neighbours[0];
} else if !(visited.get(neighbours[1])
|| prev.is_some() && prev.unwrap() == neighbours[1])
{
prev = Some(current);
current = neighbours[1]
} else {
break;
}
} else {
break;
}
}
line
}
fn adjacency_map(indices: &[u32]) -> (u32, FxHashMap<u32, SmallVec<[u32; 2]>>) {
let mut adjacency_map = FxHashMap::<u32, SmallVec<[u32; 2]>>::with_capacity_and_hasher(
indices.len(),
Default::default(),
);
let mut max_index = 0;
indices.chunks_exact(2).for_each(|chunk| {
let i0 = chunk[0];
let i1 = chunk[1];
max_index = max_index.max(i0).max(i1);
let entry = adjacency_map.entry(i0).or_default();
if !entry.contains(&i1) {
entry.push(i1);
}
let entry = adjacency_map.entry(i1).or_default();
if !entry.contains(&i0) {
entry.push(i0);
}
});
(max_index, adjacency_map)
}
#[allow(clippy::type_complexity)]
fn termination_candidate_nodes(
max_index: u32,
adjacency_map: &FxHashMap<u32, SmallVec<[u32; 2]>>,
) -> (SetVob, Vec<(u32, SmallVec<[u32; 2]>)>) {
let mut termination_nodes = SetVob::fill_with_false((max_index + 1) as usize);
let mut candidate_nodes = Vec::<(u32, SmallVec<[u32; 2]>)>::default();
for v_id in 0..(max_index + 1) {
if let Some(neighbours) = adjacency_map.get(&v_id) {
if neighbours.len() != 2 {
termination_nodes.set(v_id);
candidate_nodes.push((v_id, neighbours.clone()));
}
} else {
termination_nodes.set(v_id)
}
}
(termination_nodes, candidate_nodes)
}
pub fn divide_into_shapes(indices: &[u32]) -> (Vec<Vec<u32>>, Vob<u32>) {
let mut group_container = Vec::<Vec<u32>>::new();
let (max_index, adjacency_map) = adjacency_map(indices);
let (termination_nodes, mut candidate_nodes) =
termination_candidate_nodes(max_index, &adjacency_map);
let mut visited = SetVob::fill_with_false((max_index + 1) as usize);
let mut current: u32 = 0;
while !candidate_nodes.is_empty() {
let mut next_vertex = Option::<u32>::None;
'outer: while !candidate_nodes.is_empty() && next_vertex.is_none() {
if let Some((candidate, array)) = candidate_nodes.last_mut() {
current = *candidate;
while !array.is_empty() {
let n_vertex = array.pop().unwrap();
if termination_nodes.get(n_vertex) {
if current < n_vertex {
group_container.push(vec![current, n_vertex]);
}
} else if visited.get(n_vertex) {
continue;
} else {
next_vertex = Some(n_vertex);
break 'outer;
}
}
}
if let Some((_, a)) = candidate_nodes.pop() {
assert!(a.is_empty())
}
}
if next_vertex.is_some() {
group_container.push(unwind_line(
current,
next_vertex,
&adjacency_map,
&termination_nodes,
&mut visited,
));
}
}
if visited.number_of_set_bits() + termination_nodes.number_of_set_bits() < max_index + 1 {
let mut min_index = 0;
'outer: loop {
current = min_index;
while visited.get(current) || termination_nodes.get(current) {
current += 1;
min_index = current;
if current >= max_index {
current = min_index;
break;
}
}
if current > max_index {
if visited.number_of_set_bits() + termination_nodes.number_of_set_bits()
>= max_index
{
break 'outer;
}
current = min_index;
}
assert!(!visited.get(current));
{
let mut line = unwind_line(
current,
None,
&adjacency_map,
&termination_nodes,
&mut visited,
);
if line.len() > 1 {
line.push(*line.first().unwrap());
group_container.push(line);
}
}
if visited.number_of_set_bits() + termination_nodes.number_of_set_bits() >= max_index {
break;
}
}
}
(group_container, visited.vob)
}