use std::cell::Cell;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::hash_map::Entry;
use std::hash::Hash;
type Order = usize;
#[derive(Debug)]
pub struct DiGraph<V, E>
where
V: Eq + Hash + Copy,
{
nodes: HashMap<V, Node<V, E>>,
unused_order: Vec<Order>,
}
#[derive(Debug)]
struct Node<V, E>
where
V: Eq + Hash + Clone,
{
in_edges: HashSet<V>,
out_edges: HashMap<V, E>,
ord: Cell<Order>,
}
impl<V, E> DiGraph<V, E>
where
V: Eq + Hash + Copy,
{
fn add_node(&mut self, n: V) -> (&mut HashSet<V>, &mut HashMap<V, E>, Order) {
let fallback_id = self.nodes.len();
let node = self.nodes.entry(n).or_insert_with(|| {
let order = if let Some(id) = self.unused_order.pop() {
id
} else {
fallback_id
};
Node {
ord: Cell::new(order),
in_edges: Default::default(),
out_edges: Default::default(),
}
});
(&mut node.in_edges, &mut node.out_edges, node.ord.get())
}
pub(crate) fn remove_node(&mut self, n: V) -> bool {
match self.nodes.remove(&n) {
None => false,
Some(Node {
out_edges,
in_edges,
ord,
}) => {
self.unused_order.push(ord.get());
out_edges.into_keys().for_each(|m| {
self.nodes.get_mut(&m).unwrap().in_edges.remove(&n);
});
in_edges.into_iter().for_each(|m| {
self.nodes.get_mut(&m).unwrap().out_edges.remove(&n);
});
true
}
}
}
pub(crate) fn add_edge(&mut self, x: V, y: V, e: impl FnOnce() -> E) -> Result<(), Vec<E>>
where
E: Clone,
{
if x == y {
return Err(Vec::new());
}
let (_, out_edges, ub) = self.add_node(x);
match out_edges.entry(y) {
Entry::Occupied(_) => {
return Ok(());
}
Entry::Vacant(entry) => entry.insert(e()),
};
let (in_edges, _, lb) = self.add_node(y);
in_edges.insert(x);
if lb < ub {
let mut visited = [x, y].into_iter().collect();
let mut delta_f = Vec::new();
let mut delta_b = Vec::new();
if let Err(cycle) = self.dfs_f(&self.nodes[&y], ub, &mut visited, &mut delta_f) {
self.nodes.get_mut(&y).map(|node| node.in_edges.remove(&x));
self.nodes.get_mut(&x).map(|node| node.out_edges.remove(&y));
return Err(cycle);
}
self.dfs_b(&self.nodes[&x], lb, &mut visited, &mut delta_b);
drop(visited);
self.reorder(delta_f, delta_b);
}
Ok(())
}
fn dfs_f<'a>(
&'a self,
n: &'a Node<V, E>,
ub: Order,
visited: &mut HashSet<V>,
delta_f: &mut Vec<&'a Node<V, E>>,
) -> Result<(), Vec<E>>
where
E: Clone,
{
delta_f.push(n);
for (w, e) in &n.out_edges {
let node = &self.nodes[w];
let ord = node.ord.get();
if ord == ub {
return Err(vec![e.clone()]);
} else if !visited.contains(w) && ord < ub {
visited.insert(*w);
if let Err(mut chain) = self.dfs_f(node, ub, visited, delta_f) {
chain.push(e.clone());
return Err(chain);
}
}
}
Ok(())
}
fn dfs_b<'a>(
&'a self,
n: &'a Node<V, E>,
lb: Order,
visited: &mut HashSet<V>,
delta_b: &mut Vec<&'a Node<V, E>>,
) {
delta_b.push(n);
for w in &n.in_edges {
let node = &self.nodes[w];
if !visited.contains(w) && lb < node.ord.get() {
visited.insert(*w);
self.dfs_b(node, lb, visited, delta_b);
}
}
}
fn reorder(&self, mut delta_f: Vec<&Node<V, E>>, mut delta_b: Vec<&Node<V, E>>) {
self.sort(&mut delta_f);
self.sort(&mut delta_b);
let mut l = Vec::with_capacity(delta_f.len() + delta_b.len());
let mut orders = Vec::with_capacity(delta_f.len() + delta_b.len());
for v in delta_b.into_iter().chain(delta_f) {
orders.push(v.ord.get());
l.push(v);
}
orders.sort_unstable();
for (node, order) in l.into_iter().zip(orders) {
node.ord.set(order);
}
}
fn sort(&self, ids: &mut [&Node<V, E>]) {
ids.sort_unstable_by_key(|v| &v.ord);
}
}
impl<V, E> Default for DiGraph<V, E>
where
V: Eq + Hash + Copy,
{
fn default() -> Self {
Self {
nodes: Default::default(),
unused_order: Default::default(),
}
}
}
#[cfg(test)]
mod tests {
use rand::seq::SliceRandom;
use rand::thread_rng;
use super::*;
fn nop() {}
#[test]
fn test_no_self_cycle() {
let mut graph = DiGraph::default();
assert!(graph.add_edge(1, 1, nop).is_err());
}
#[test]
fn test_digraph() {
let mut graph = DiGraph::default();
assert!(graph.add_edge(0, 1, nop).is_ok());
assert!(graph.add_edge(1, 2, nop).is_ok());
assert!(graph.add_edge(2, 3, nop).is_ok());
assert!(graph.add_edge(4, 2, nop).is_ok());
assert!(graph.add_edge(3, 1, nop).is_err());
assert!(graph.add_edge(4, 0, nop).is_ok());
}
#[test]
fn fuzz_digraph() {
const NUM_NODES: usize = 100;
let mut edges = Vec::with_capacity(NUM_NODES * NUM_NODES);
for i in 0..NUM_NODES {
for j in i..NUM_NODES {
if i != j {
edges.push((i, j));
}
}
}
edges.shuffle(&mut thread_rng());
let mut graph = DiGraph::default();
for (x, y) in edges {
assert!(graph.add_edge(x, y, nop).is_ok());
}
}
}