crate::ix!();
#[instrument(level = "trace", skip(skel))]
pub fn build_node_level_map(skel: &Skeleton) -> HashMap<u16, u8> {
trace!("Building node->level map with BFS for Skeleton.");
let mut visited = HashSet::new();
let mut map = HashMap::<u16, u8>::new();
let mut queue = VecDeque::new();
if let Some(rid) = skel.root_id() {
queue.push_back((*rid, 0u8));
} else {
debug!("No root => empty map");
return map;
}
while let Some((nid, lvl)) = queue.pop_front() {
if !visited.insert(nid) {
continue;
}
map.insert(nid, lvl);
if let Some(node) = skel.nodes().iter().find(|n| n.id() == nid) {
for &child_id in node.child_ids() {
let next_lvl = lvl.saturating_add(1);
queue.push_back((child_id, next_lvl));
}
}
}
trace!("Finished BFS => discovered {} connected nodes", map.len());
map
}
#[cfg(test)]
mod build_node_level_map_assessment {
use super::*;
fn make_node(id: u16, child_ids: &[u16]) -> SkeletonNode {
let k = if child_ids.is_empty() {
NodeKind::LeafHolder
} else {
NodeKind::Dispatch
};
SkeletonNodeBuilder::default()
.id(id)
.child_ids(child_ids.to_vec())
.name(format!("Node_{id}"))
.original_key(format!("Node_{id}"))
.build(k)
.unwrap()
}
#[traced_test]
fn check_empty_skeleton() {
let skel = SkeletonBuilder::default().build().unwrap();
let map = build_node_level_map(&skel);
assert!(map.is_empty());
}
#[traced_test]
fn check_single_node() {
let node0 = make_node(0, &[]);
let skel = SkeletonBuilder::default()
.nodes(vec![node0])
.root_id(Some(0))
.build()
.unwrap();
let map = build_node_level_map(&skel);
assert_eq!(map.len(), 1);
assert_eq!(map.get(&0), Some(&0u8));
}
#[traced_test]
fn check_linear_chain() {
let n0 = make_node(0, &[1]);
let n1 = make_node(1, &[2]);
let n2 = make_node(2, &[]);
let skel = SkeletonBuilder::default()
.nodes(vec![n0, n1, n2])
.root_id(Some(0))
.build()
.unwrap();
let map = build_node_level_map(&skel);
assert_eq!(map.get(&0), Some(&0u8));
assert_eq!(map.get(&1), Some(&1u8));
assert_eq!(map.get(&2), Some(&2u8));
assert_eq!(map.len(), 3);
}
#[traced_test]
fn check_branching() {
let n0 = make_node(0, &[1,2]);
let n1 = make_node(1, &[3]);
let n2 = make_node(2, &[]);
let n3 = make_node(3, &[]);
let skel = SkeletonBuilder::default()
.nodes(vec![n0,n1,n2,n3])
.root_id(Some(0))
.build()
.unwrap();
let map = build_node_level_map(&skel);
assert_eq!(map.get(&0), Some(&0u8));
assert_eq!(map.get(&1), Some(&1u8));
assert_eq!(map.get(&2), Some(&1u8));
assert_eq!(map.get(&3), Some(&2u8));
}
#[traced_test]
fn check_unconnected_nodes() {
let n0 = make_node(0, &[1]);
let n1 = make_node(1, &[]);
let n2 = make_node(2, &[3]);
let n3 = make_node(3, &[]);
let skel = SkeletonBuilder::default()
.nodes(vec![n0,n1,n2,n3])
.root_id(Some(0))
.build()
.unwrap();
let map = build_node_level_map(&skel);
assert_eq!(map.len(), 2);
assert_eq!(map.get(&0), Some(&0u8));
assert_eq!(map.get(&1), Some(&1u8));
}
#[traced_test]
fn check_cycle_doesnt_loop() {
let n0 = make_node(0, &[1]);
let n1 = make_node(1, &[0]);
let skel = SkeletonBuilder::default()
.nodes(vec![n0,n1])
.root_id(Some(0))
.build()
.unwrap();
let map = build_node_level_map(&skel);
assert_eq!(map.len(), 2);
assert_eq!(map.get(&0), Some(&0u8));
assert_eq!(map.get(&1), Some(&1u8));
}
}