use super::ContainerResultExt;
use super::AlgorithmError;
use crate::containers::{maps::MapTrait, queues::Deque};
use crate::graph::{Graph, NodeIndex};
pub fn kahns<'a, G, NI, D, M>(
graph: &G,
mut queue: D,
mut in_degree_map: M,
sorted_nodes: &'a mut [NI],
) -> Result<&'a [NI], AlgorithmError<NI>>
where
G: Graph<NI>,
NI: NodeIndex,
D: Deque<NI>,
M: MapTrait<NI, isize>,
AlgorithmError<NI>: From<G::Error>,
{
queue.clear();
let mut sort_index = 0;
let mut append_to_list = |node: NI| -> Result<(), AlgorithmError<NI>> {
if sort_index >= sorted_nodes.len() {
return Err(AlgorithmError::ResultCapacityExceeded);
}
sorted_nodes[sort_index] = node;
sort_index += 1;
Ok(())
};
for node in graph.iter_nodes()? {
let in_degree = graph.incoming_edges(node)?.count() as isize;
in_degree_map.insert(node, in_degree).capacity_error()?;
}
for (node, °ree) in in_degree_map.iter() {
if degree == 0 {
queue
.push_back(*node)
.map_err(|_| AlgorithmError::QueueCapacityExceeded)?;
}
}
while let Some(node) = queue.pop_front() {
append_to_list(node)?;
for target in graph.outgoing_edges(node)? {
if let Some(degree) = in_degree_map.get_mut(&target) {
*degree -= 1;
if *degree == 0 {
queue
.push_back(target)
.map_err(|_| AlgorithmError::QueueCapacityExceeded)?;
}
}
}
}
for (_, °ree) in in_degree_map.iter() {
if degree > 0 {
return Err(AlgorithmError::CycleDetected);
}
}
Ok(&sorted_nodes[..sort_index])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::containers::{maps::staticdict::Dictionary, queues::CircularQueue};
use crate::edgelist::edge_list::EdgeList;
#[test]
fn test_kahns_simple() {
let graph = EdgeList::<8, _, _>::new([(0usize, 1usize), (1, 2)]);
let queue = CircularQueue::<usize, 8>::new();
let in_degree_map = Dictionary::<usize, isize, 8>::new();
let mut sorted_nodes = [0usize; 8];
let result = kahns(&graph, queue, in_degree_map, &mut sorted_nodes).unwrap();
assert_eq!(result, &[0, 1, 2]);
}
#[test]
fn test_kahns_complex() {
let graph = EdgeList::<8, _, _>::new([(0usize, 1usize), (0, 2), (1, 3), (2, 3)]);
let queue = CircularQueue::<usize, 8>::new();
let in_degree_map = Dictionary::<usize, isize, 8>::new();
let mut sorted_nodes = [0usize; 8];
let result = kahns(&graph, queue, in_degree_map, &mut sorted_nodes).unwrap();
assert_eq!(result.len(), 4);
assert_eq!(result[0], 0);
assert_eq!(result[result.len() - 1], 3);
assert!(result.contains(&1));
assert!(result.contains(&2));
}
#[test]
fn test_kahns_cycle_detection() {
let graph = EdgeList::<8, _, _>::new([(0usize, 1usize), (1, 2), (2, 0)]);
let queue = CircularQueue::<usize, 8>::new();
let in_degree_map = Dictionary::<usize, isize, 8>::new();
let mut sorted_nodes = [0usize; 8];
let error = kahns(&graph, queue, in_degree_map, &mut sorted_nodes);
assert_eq!(error, Err(AlgorithmError::CycleDetected));
}
#[test]
fn test_kahns_disconnected() {
let graph = EdgeList::<8, _, _>::new([(0usize, 1usize), (2, 3)]);
let queue = CircularQueue::<usize, 8>::new();
let in_degree_map = Dictionary::<usize, isize, 8>::new();
let mut sorted_nodes = [0usize; 8];
let result = kahns(&graph, queue, in_degree_map, &mut sorted_nodes).unwrap();
assert_eq!(result.len(), 4);
let pos_0 = result.iter().position(|&x| x == 0).unwrap();
let pos_1 = result.iter().position(|&x| x == 1).unwrap();
let pos_2 = result.iter().position(|&x| x == 2).unwrap();
let pos_3 = result.iter().position(|&x| x == 3).unwrap();
assert!(pos_0 < pos_1); assert!(pos_2 < pos_3); }
#[test]
fn test_kahns_self_loop() {
let graph = EdgeList::<8, _, _>::new([(0usize, 0usize)]);
let queue = CircularQueue::<usize, 8>::new();
let in_degree_map = Dictionary::<usize, isize, 8>::new();
let mut sorted_nodes = [0usize; 8];
let error = kahns(&graph, queue, in_degree_map, &mut sorted_nodes);
assert_eq!(error, Err(AlgorithmError::CycleDetected));
}
}