ads2_2022/code/rust/src/graphs/tarjan.rs

196 lines
5.6 KiB
Rust

// ----------------------------------------------------------------
// IMPORTS
// ----------------------------------------------------------------
use std::fmt::Display;
use std::hash::Hash;
use std::collections::HashMap;
use crate::value_min;
use crate::core::log::log_debug;
use crate::stacks::stack::Stack;
use crate::graphs::graph::Graph;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CONSTANTS
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#[derive(Clone, Copy, PartialEq)]
enum State {
UNTOUCHED,
PENDING,
FINISHED,
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// METHOD Tarjan Algorithm
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// # Tarjan Algorithm #
/// Runs the Tarjan-Algorithm to compute the strongly connected components.
pub fn tarjan_algorithm<T>(gph: &Graph<T>, debug: bool) -> Vec<Vec<T>>
where T: Eq + Hash + Clone + Copy + Display
{
let mut ctx = Context::new(&gph, debug);
for u in gph.nodes.iter() {
if ctx.get_state(&u) == State::UNTOUCHED {
tarjan_visit(gph, &u, &mut ctx);
}
}
return ctx.components;
}
/// Recursive depth-first search algorithm to compute strongly components of a graph.
fn tarjan_visit<T>(gph: &Graph<T>, &u: &T, ctx: &mut Context<T>)
where T: Eq + Hash + Clone + Copy + Display
{
// Place node on stack + initialise visit-index + component-index.
ctx.max_index += 1;
ctx.stack_push(&u);
ctx.set_root(&u, ctx.max_index);
ctx.set_index(&u, ctx.max_index);
ctx.set_state(&u, State::PENDING);
/****************
* Compute strongly connected components of each child node.
* NOTE: Child nodes remain on stack, if and only if parent is in same component.
****************/
for v in gph.successors(&u) {
// Visit child node only if untouched:
if ctx.get_state(&v) == State::UNTOUCHED {
tarjan_visit(gph, &v, ctx);
}
// Update associated component-index of parent node, if in same component as child:
if ctx.stack_contains(&v) {
ctx.set_root(&u, value_min!(ctx.get_root(&u), ctx.get_root(&v)));
}
}
ctx.set_state(&u, State::FINISHED);
ctx.log_info(&u);
// If at root of component pop everything from stack up to root and add component:
if ctx.get_index(&u) == ctx.get_root(&u) {
let mut component: Vec<T> = Vec::new();
loop {
let v = ctx.stack_top();
ctx.stack_pop();
component.push(v.clone());
if u == v { break; }
}
ctx.components.push(component);
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AUXILIARY context variables for algorithm
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#[derive(Clone, Copy)]
struct NodeInformation<T> {
node: T,
root: usize,
index: usize,
state: State,
}
struct Context<T> {
stack: Stack<T>,
max_index: usize,
infos: HashMap<T, NodeInformation<T>>,
components: Vec<Vec<T>>,
debug: bool,
}
impl<T> Context<T>
where T: Eq + Hash + Clone + Copy + Display
{
fn new(gph: &Graph<T>, debug: bool) -> Context<T> {
let mut infos = HashMap::<T, NodeInformation<T>>::new();
for u in gph.nodes.iter() {
infos.entry(u.clone()).or_insert(NodeInformation::<T>::new(&u));
}
return Context {
stack: Stack::new(),
max_index: 0,
infos: infos,
components: vec![],
debug: debug,
};
}
fn stack_push(self: &mut Self, u: &T) {
self.stack.push(u.clone());
}
fn stack_top(self: &mut Self) -> T {
return self.stack.top();
}
fn stack_pop(self: &mut Self) -> T {
return self.stack.pop();
}
fn update_infos(self: &mut Self, u: &T, info: NodeInformation<T>) {
self.infos.insert(u.clone(), info);
}
fn set_state(self: &mut Self, u: &T, state: State) {
let mut info = *self.infos.get(u).unwrap();
info.state = state;
self.update_infos(u, info);
}
fn set_root(self: &mut Self, u: &T, root: usize) {
let mut info = *self.infos.get(u).unwrap();
info.root = root;
self.update_infos(u, info);
}
fn set_index(self: &mut Self, u: &T, index: usize) {
let mut info = *self.infos.get(u).unwrap();
info.index = index;
self.update_infos(u, info);
}
fn stack_contains(self: &Self, u: &T) -> bool {
return self.stack.contains(u);
}
fn get_info(self: &Self, u: &T) -> NodeInformation<T> {
return *self.infos.get(u).unwrap();
}
fn get_state(self: &Self, u: &T) -> State {
return self.get_info(u).state;
}
fn get_root(self: &Self, u: &T) -> usize {
return self.get_info(u).root;
}
fn get_index(self: &Self, u: &T) -> usize {
return self.get_info(u).index;
}
fn log_info(self: &Self, u: &T) {
if !self.debug { return; }
let info = self.get_info(&u);
log_debug!("node, index, component: ({}, {}, {})", info.node, info.index, info.root);
}
}
impl<T> NodeInformation<T>
where T: Clone + Copy
{
fn new(node: &T) -> NodeInformation<T> {
return NodeInformation {
node: node.clone(),
root: 0,
index: 0,
state: State::UNTOUCHED,
};
}
}