use super::dataflow_fixpoint::reachability_closure;
#[must_use]
pub fn fusable_pairs(adj: &[u32], n: u32, max_iters: u32) -> Vec<u32> {
if n == 0 {
return Vec::new();
}
let Some(cells) = n.checked_mul(n).map(|v| v as usize) else {
return Vec::new();
};
if adj.len() != cells {
return Vec::new();
}
let closure = reachability_closure(adj, n, max_iters.max(1));
let n_usize = n as usize;
let mut out = vec![0u32; n_usize * n_usize];
for i in 0..n_usize {
for j in 0..n_usize {
if i != j && closure[i * n_usize + j] == 0 && closure[j * n_usize + i] == 0 {
out[i * n_usize + j] = 1;
}
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn independent_passes_are_fusable() {
#[rustfmt::skip]
let adj = vec![
0, 0, 0,
0, 0, 0,
0, 0, 0,
];
let fused = fusable_pairs(&adj, 3, 3);
assert_eq!(fused[0 * 3 + 1], 1);
assert_eq!(fused[1 * 3 + 0], 1);
assert_eq!(fused[0 * 3 + 2], 1);
assert_eq!(fused[2 * 3 + 0], 1);
assert_eq!(fused[0], 0);
}
#[test]
fn dependent_passes_are_not_fusable() {
#[rustfmt::skip]
let adj = vec![
0, 1, 0,
0, 0, 1,
0, 0, 0,
];
let fused = fusable_pairs(&adj, 3, 3);
assert_eq!(fused[0 * 3 + 1], 0);
assert_eq!(fused[0 * 3 + 2], 0);
assert_eq!(fused[1 * 3 + 2], 0);
}
#[test]
fn diamond_top_and_bottom_not_fusable() {
#[rustfmt::skip]
let adj = vec![
0, 1, 1, 0,
0, 0, 0, 1,
0, 0, 0, 1,
0, 0, 0, 0,
];
let fused = fusable_pairs(&adj, 4, 4);
assert_eq!(fused[1 * 4 + 2], 1);
assert_eq!(fused[2 * 4 + 1], 1);
assert_eq!(fused[0 * 4 + 3], 0);
}
#[test]
fn empty_graph_returns_empty() {
let fused = fusable_pairs(&[], 0, 0);
assert!(fused.is_empty());
}
}