use std::collections::HashSet;
use crate::scc::scc;
use crate::toposort::toposort;
#[derive(Debug, Clone)]
pub struct Condensation<N> {
pub components: Vec<Vec<N>>,
pub edges: Vec<(usize, usize)>,
}
pub fn condensation<N>(
nodes: impl IntoIterator<Item = N>,
edges: impl IntoIterator<Item = (N, N)>,
) -> Condensation<N>
where
N: Eq + std::hash::Hash + Clone,
{
let nodes: Vec<N> = nodes.into_iter().collect();
let edges: Vec<(N, N)> = edges.into_iter().collect();
let components = scc(nodes.clone(), edges.iter().cloned());
let comp_of: std::collections::HashMap<N, usize> = components
.iter()
.enumerate()
.flat_map(|(i, c)| c.iter().map(move |n| (n.clone(), i)))
.collect();
let mut edge_set: HashSet<(usize, usize)> = HashSet::new();
for (a, b) in &edges {
if let (Some(&i), Some(&j)) = (comp_of.get(a), comp_of.get(b)) {
if i != j {
edge_set.insert((i, j));
}
}
}
let edges = edge_set.into_iter().collect();
Condensation { components, edges }
}
pub fn condensation_by_key<N, K>(
nodes: impl IntoIterator<Item = N>,
edges: impl IntoIterator<Item = (N, N)>,
key: impl Fn(&N) -> K,
) -> Condensation<N>
where
N: Eq + std::hash::Hash + Clone,
K: Ord,
{
let mut cond = condensation(nodes, edges);
for comp in &mut cond.components {
comp.sort_by_key(&key);
}
cond
}
pub fn toposort_scc<N>(
nodes: impl IntoIterator<Item = N>,
edges: impl IntoIterator<Item = (N, N)>,
) -> Vec<Vec<N>>
where
N: Eq + std::hash::Hash + Clone,
{
let cond = condensation(nodes, edges);
let order = toposort(
0..cond.components.len(),
cond.edges.iter().copied(),
)
.expect("condensation is a DAG");
order
.into_iter()
.map(|i| cond.components[i].clone())
.collect()
}
pub fn toposort_scc_by_key<N, K>(
nodes: impl IntoIterator<Item = N>,
edges: impl IntoIterator<Item = (N, N)>,
key: impl Fn(&N) -> K,
) -> Vec<Vec<N>>
where
N: Eq + std::hash::Hash + Clone,
K: Ord,
{
let mut sccs = toposort_scc(nodes, edges);
for comp in &mut sccs {
comp.sort_by_key(&key);
}
sccs
}