pub fn reachability_counts_edges(n: usize, edges: &[(usize, usize)]) -> (Vec<usize>, Vec<usize>) {
let mut fwd: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut rev: Vec<Vec<usize>> = vec![Vec::new(); n];
for &(u, v) in edges {
if u >= n || v >= n {
continue;
}
fwd[u].push(v);
rev[v].push(u);
}
let mut dependencies = vec![0usize; n];
let mut dependents = vec![0usize; n];
let mut visited: Vec<u32> = vec![0u32; n];
let mut stamp: u32 = 0;
let mut q: Vec<usize> = Vec::new();
for start in 0..n {
stamp = stamp.wrapping_add(1);
q.clear();
visited[start] = stamp; q.push(start);
let mut head = 0usize;
let mut count = 0usize;
while head < q.len() {
let cur = q[head];
head += 1;
for &nx in &fwd[cur] {
if visited[nx] != stamp {
visited[nx] = stamp;
q.push(nx);
count += 1;
}
}
}
dependencies[start] = count;
stamp = stamp.wrapping_add(1);
q.clear();
visited[start] = stamp; q.push(start);
let mut head = 0usize;
let mut count = 0usize;
while head < q.len() {
let cur = q[head];
head += 1;
for &nx in &rev[cur] {
if visited[nx] != stamp {
visited[nx] = stamp;
q.push(nx);
count += 1;
}
}
}
dependents[start] = count;
}
(dependents, dependencies)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reachability_counts_edges_small() {
let n = 4;
let edges = vec![(0, 1), (1, 2), (0, 2)];
let (dependents, dependencies) = reachability_counts_edges(n, &edges);
assert_eq!(dependencies[0], 2); assert_eq!(dependencies[1], 1); assert_eq!(dependencies[2], 0);
assert_eq!(dependencies[3], 0);
assert_eq!(dependents[2], 2); assert_eq!(dependents[1], 1); assert_eq!(dependents[0], 0);
assert_eq!(dependents[3], 0);
}
#[test]
fn test_reachability_does_not_count_start_via_cycle() {
let n = 3;
let edges = vec![(0, 1), (1, 2), (2, 0)];
let (dependents, dependencies) = reachability_counts_edges(n, &edges);
assert_eq!(dependencies[0], 2);
assert_eq!(dependencies[1], 2);
assert_eq!(dependencies[2], 2);
assert_eq!(dependents[0], 2);
assert_eq!(dependents[1], 2);
assert_eq!(dependents[2], 2);
}
}