pub struct RpoResult {
pub parent_pre: Vec<u32>,
pub dfn: Vec<u32>,
pub vertex: Vec<u32>,
}
pub fn rebuild_vertex(dfn: &[u32], count: usize) -> Vec<u32> {
let mut vertex = vec![0u32; count];
for (node, &pre) in dfn.iter().enumerate() {
if pre != u32::MAX {
vertex[pre as usize] = node as u32;
}
}
vertex
}
pub fn rpo_dfs(
n: usize,
roots: &[u32],
fwd_off: &[u32],
fwd_tgt: &crate::chunkvec::ChunkU32,
) -> RpoResult {
let vroot = n as u32;
let mut parent_pre: Vec<u32> = Vec::with_capacity(n + 1);
let mut dfn = vec![u32::MAX; n + 1];
let mut dfs_count: u32 = 0;
crate::trace::probe("rpo_dfs: after dfn+parent_pre alloc (before DFS loop)");
let mut node_stack: Vec<u32> = Vec::with_capacity(1024);
let mut cursor_stack: Vec<usize> = Vec::with_capacity(1024);
dfn[n] = dfs_count;
parent_pre.push(0); dfs_count += 1;
node_stack.push(vroot);
cursor_stack.push(0);
while !node_stack.is_empty() {
let top = *node_stack.last().unwrap();
let cursor = cursor_stack.last_mut().unwrap();
let child_count: usize = if top == vroot {
roots.len()
} else {
let v = top as usize;
(fwd_off[v + 1] - fwd_off[v]) as usize
};
let mut pushed = false;
if top == vroot {
while *cursor < child_count {
let child = roots[*cursor];
*cursor += 1;
if child as usize > n {
continue;
}
if dfn[child as usize] == u32::MAX {
dfn[child as usize] = dfs_count;
parent_pre.push(dfn[top as usize]);
dfs_count += 1;
node_stack.push(child);
cursor_stack.push(0);
pushed = true;
break;
}
}
} else {
let v = top as usize;
let lo = fwd_off[v] as usize;
let hi = fwd_off[v + 1] as usize;
let parent_dfn = dfn[top as usize];
if let Some(adj) = fwd_tgt.range_slice(lo + *cursor, hi) {
for &child in adj {
*cursor += 1;
if child as usize > n {
continue;
}
if dfn[child as usize] == u32::MAX {
dfn[child as usize] = dfs_count;
parent_pre.push(parent_dfn);
dfs_count += 1;
node_stack.push(child);
cursor_stack.push(0);
pushed = true;
break;
}
}
} else {
while *cursor < child_count {
let child = fwd_tgt.get(lo + *cursor);
*cursor += 1;
if child as usize > n {
continue;
}
if dfn[child as usize] == u32::MAX {
dfn[child as usize] = dfs_count;
parent_pre.push(parent_dfn);
dfs_count += 1;
node_stack.push(child);
cursor_stack.push(0);
pushed = true;
break;
}
}
}
}
if !pushed {
node_stack.pop();
cursor_stack.pop();
}
}
RpoResult {
parent_pre,
dfn,
vertex: Vec::new(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::chunkvec::ChunkU32;
fn make_fwd_tgt(v: Vec<u32>) -> ChunkU32 {
let mut c = ChunkU32::zeroed(v.len());
for (i, &x) in v.iter().enumerate() {
c.set(i, x);
}
c
}
#[test]
fn rpo_diamond() {
let fwd_off = vec![0u32, 2, 3, 4, 4];
let fwd_tgt = make_fwd_tgt(vec![1u32, 2, 3, 3]);
let r = rpo_dfs(4, &[0u32], &fwd_off, &fwd_tgt);
for v in 0..4usize {
assert_ne!(r.dfn[v], u32::MAX, "node {v} must be reachable");
}
assert!(r.dfn[0] < r.dfn[1], "root 0 visited before node 1");
assert!(r.dfn[1] < r.dfn[3], "node 3 discovered via node 1");
assert!(r.dfn[3] < r.dfn[2], "node 3 visited before node 2 (DFS)");
}
#[test]
fn rpo_unreachable() {
let fwd_off = vec![0u32, 1, 1, 1];
let fwd_tgt = make_fwd_tgt(vec![1u32]);
let r = rpo_dfs(3, &[0u32], &fwd_off, &fwd_tgt);
assert_ne!(r.dfn[0], u32::MAX);
assert_ne!(r.dfn[1], u32::MAX);
assert_eq!(r.dfn[2], u32::MAX);
}
}