use alloc::{collections::VecDeque, vec::Vec};
use num_traits::AsPrimitive;
use super::NodeSorter;
use crate::traits::MonoplexMonopartiteGraph;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
pub enum TraversalSeedStrategy {
#[default]
NodeIdAscending,
MaxOutDegree,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
pub enum TraversalNeighborOrder {
#[default]
NodeIdAscending,
NodeIdDescending,
OutDegreeDescending,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
pub struct BfsTraversalSorter {
seed_strategy: TraversalSeedStrategy,
neighbor_order: TraversalNeighborOrder,
}
impl BfsTraversalSorter {
#[inline]
#[must_use]
pub const fn new(
seed_strategy: TraversalSeedStrategy,
neighbor_order: TraversalNeighborOrder,
) -> Self {
Self { seed_strategy, neighbor_order }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
pub struct DfsTraversalSorter {
seed_strategy: TraversalSeedStrategy,
neighbor_order: TraversalNeighborOrder,
}
impl DfsTraversalSorter {
#[inline]
#[must_use]
pub const fn new(
seed_strategy: TraversalSeedStrategy,
neighbor_order: TraversalNeighborOrder,
) -> Self {
Self { seed_strategy, neighbor_order }
}
}
fn pick_seed<G>(graph: &G, visited: &[bool], strategy: TraversalSeedStrategy) -> Option<G::NodeId>
where
G: MonoplexMonopartiteGraph,
{
let mut best_node: Option<G::NodeId> = None;
let mut best_degree = 0usize;
for node in graph.node_ids() {
let node_index = node.as_();
if visited[node_index] {
continue;
}
if strategy == TraversalSeedStrategy::NodeIdAscending {
return Some(node);
}
let node_degree = graph.out_degree(node).as_();
let is_better = best_node.is_none()
|| node_degree > best_degree
|| (node_degree == best_degree
&& node_index < best_node.expect("best node must exist").as_());
if is_better {
best_node = Some(node);
best_degree = node_degree;
}
}
best_node
}
fn ordered_successors<G>(
graph: &G,
node: G::NodeId,
neighbor_order: TraversalNeighborOrder,
) -> Vec<G::NodeId>
where
G: MonoplexMonopartiteGraph,
{
let mut successors: Vec<G::NodeId> = graph.successors(node).collect();
if neighbor_order == TraversalNeighborOrder::NodeIdAscending {
successors.sort_unstable_by_key(|left| (*left).as_());
return successors;
}
if neighbor_order == TraversalNeighborOrder::NodeIdDescending {
successors.sort_unstable_by_key(|right| core::cmp::Reverse((*right).as_()));
return successors;
}
successors.sort_unstable_by(|left, right| {
let left_index = (*left).as_();
let right_index = (*right).as_();
graph
.out_degree(*right)
.as_()
.cmp(&graph.out_degree(*left).as_())
.then_with(|| left_index.cmp(&right_index))
});
successors
}
impl<G> NodeSorter<G> for BfsTraversalSorter
where
G: MonoplexMonopartiteGraph,
{
fn sort_nodes(&self, graph: &G) -> Vec<G::NodeId> {
let n = graph.number_of_nodes().as_();
let mut visited = vec![false; n];
let mut queue = VecDeque::with_capacity(n);
let mut order = Vec::with_capacity(n);
while let Some(seed) = pick_seed(graph, &visited, self.seed_strategy) {
let seed_index = seed.as_();
visited[seed_index] = true;
queue.push_back(seed);
while let Some(node) = queue.pop_front() {
order.push(node);
for successor in ordered_successors(graph, node, self.neighbor_order) {
let successor_index = successor.as_();
if visited[successor_index] {
continue;
}
visited[successor_index] = true;
queue.push_back(successor);
}
}
}
order
}
}
impl<G> NodeSorter<G> for DfsTraversalSorter
where
G: MonoplexMonopartiteGraph,
{
fn sort_nodes(&self, graph: &G) -> Vec<G::NodeId> {
let n = graph.number_of_nodes().as_();
let mut visited = vec![false; n];
let mut stack = Vec::with_capacity(n);
let mut order = Vec::with_capacity(n);
while let Some(seed) = pick_seed(graph, &visited, self.seed_strategy) {
let seed_index = seed.as_();
visited[seed_index] = true;
stack.push(seed);
while let Some(node) = stack.pop() {
order.push(node);
let successors = ordered_successors(graph, node, self.neighbor_order);
for successor in successors.into_iter().rev() {
let successor_index = successor.as_();
if visited[successor_index] {
continue;
}
visited[successor_index] = true;
stack.push(successor);
}
}
}
order
}
}