// ---------------------------------------------------------------- // IMPORTS // ---------------------------------------------------------------- use std::fmt::Display; use std::hash::Hash; use std::collections::HashMap; use crate::core::utils; 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(gph: &Graph, debug: bool) -> Vec> where T: Eq + Hash + Clone + Copy + Display { let mut ctx = Context::new(&gph, debug); for u in gph.nodes.iter() { tarjan_visit(gph, u, &mut ctx); } return ctx.components; } /// recursive depth-first search algorithm to compute components fn tarjan_visit(gph: &Graph, v: &T, ctx: &mut Context) where T: Eq + Hash + Clone + Copy + Display { if !(ctx.get_state(v) == State::UNTOUCHED) { return; } ctx.max_index += 1; ctx.push(v); ctx.set_root(v, ctx.max_index); ctx.set_index(v, ctx.max_index); ctx.set_state(v, State::PENDING); // depth first search: for u in gph.successors(&v) { tarjan_visit(gph, &u, ctx); // remains relevant for v, provided u still in Stack: if ctx.stack.elements.contains(&u) { let root = utils::min( ctx.get_root(&u), ctx.get_root(v) ); ctx.set_root(v, root); } } ctx.set_state(v, State::FINISHED); if ctx.debug { let info = ctx.get_info(&v); log_debug!("node, index, component: ({}, {}, {})", info.node, info.index, info.root); } if ctx.get_index(v) == ctx.get_root(v) { let mut component: Vec = Vec::new(); loop { let u = ctx.top(); ctx.pop(); component.push(u.clone()); if u == *v { break; } } ctx.components.push(component); } } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AUXILIARY context variables for algorithm // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #[derive(Clone, Copy)] struct NodeInformation { node: T, root: usize, index: usize, state: State, } struct Context { stack: Stack, max_index: usize, infos: HashMap>, components: Vec>, debug: bool, } impl Context where T: Eq + Hash + Clone + Copy + Display { fn new(gph: &Graph, debug: bool) -> Context { let mut infos = HashMap::>::new(); for u in gph.nodes.iter() { infos.entry(u.clone()).or_insert(NodeInformation::::new(&u)); } return Context { stack: Stack::new(), max_index: 0, infos: infos, components: vec![], debug: debug, }; } fn push(self: &mut Self, u: &T) { self.stack.push(u.clone()); } fn top(self: &mut Self) -> T { return self.stack.top(); } fn pop(self: &mut Self) -> T { return self.stack.pop(); } fn update_infos(self: &mut Self, u: &T, info: NodeInformation) { 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 get_info(self: &mut Self, u: &T) -> NodeInformation { return *self.infos.get(u).unwrap(); } fn get_state(self: &mut Self, u: &T) -> State { return self.get_info(u).state; } fn get_root(self: &mut Self, u: &T) -> usize { return self.get_info(u).root; } fn get_index(self: &mut Self, u: &T) -> usize { return self.get_info(u).index; } } impl NodeInformation where T: Clone + Copy { fn new(node: &T) -> NodeInformation { return NodeInformation { node: node.clone(), root: 0, index: 0, state: State::UNTOUCHED, }; } }