pub fn build_dom_children_csr(n: usize, idom: &[u32]) -> (Vec<u32>, Vec<u32>) {
let undef = u32::MAX;
let mut child_off: Vec<u32> = vec![0u32; n + 2];
for u in 0..n {
let p = idom[u];
if p == undef || p == u as u32 {
continue;
}
child_off[p as usize + 1] += 1;
}
for i in 0..=n {
child_off[i + 1] += child_off[i];
}
let total_children = child_off[n + 1] as usize;
let mut child_tgt: Vec<u32> = vec![u32::MAX; total_children];
for u in 0..n {
let p = idom[u];
if p == undef || p == u as u32 {
continue;
}
child_tgt[child_off[p as usize] as usize] = u as u32;
child_off[p as usize] += 1;
}
for i in (1..=n + 1).rev() {
child_off[i] = child_off[i - 1];
}
child_off[0] = 0;
(child_off, child_tgt)
}
pub fn compute_retained(
n: usize,
idom: &[u32],
shallow: &[u32],
class_idx: &[u32],
class_count: usize,
class_obj_class_idx: &std::collections::HashMap<u32, u32>,
child_off: &[u32],
child_tgt: &[u32],
) -> (Vec<u64>, crate::bitset::Bitset, Vec<u64>) {
let vroot = n as u32;
let undef = u32::MAX;
let mut depth_counts: Vec<u64> = Vec::new();
let mut retained: Vec<u64> = shallow.iter().map(|&s| s as u64).collect();
crate::trace::probe("retained: before hasSame DFS");
let mut has_same = crate::bitset::Bitset::with_len(n);
let mut class_to_last_depth: Vec<u32> = vec![0u32; class_count];
let mut class_obj_depth: Vec<u32> = vec![0u32; class_count];
let mut stk_node: Vec<u32> = Vec::new();
let mut stk_child_idx: Vec<u32> = Vec::new();
let mut stk_saved_depth: Vec<u32> = Vec::new(); let mut stk_saved_obj_depth: Vec<u32> = Vec::new(); let mut stk_cls: Vec<u32> = Vec::new(); let mut stk_ci: Vec<u32> = Vec::new();
stk_node.push(vroot);
stk_child_idx.push(child_off[n]);
stk_saved_depth.push(0);
stk_saved_obj_depth.push(0);
stk_cls.push(undef);
stk_ci.push(undef);
while !stk_node.is_empty() {
let top = stk_node.len() - 1;
let v = stk_node[top];
let next_child_pos = stk_child_idx[top];
let end_child = child_off[v as usize + 1];
if next_child_pos < end_child {
let child = child_tgt[next_child_pos as usize];
stk_child_idx[top] = next_child_pos + 1;
let cls = if (child as usize) < n {
class_idx[child as usize]
} else {
undef
};
let ci = class_obj_class_idx.get(&child).copied().unwrap_or(undef);
let sp_new = (stk_node.len() + 1) as u32;
let b2_depth = (sp_new - 1) as usize;
if b2_depth > depth_counts.len() {
depth_counts.resize(b2_depth, 0);
}
depth_counts[b2_depth - 1] += 1;
let saved_depth = if cls != undef && (cls as usize) < class_count {
if class_to_last_depth[cls as usize] > 0 || class_obj_depth[cls as usize] > 0 {
has_same.set(child as usize);
}
let sd = class_to_last_depth[cls as usize];
class_to_last_depth[cls as usize] = sp_new;
sd
} else {
0u32
};
let saved_obj_depth = if ci != undef && (ci as usize) < class_count {
let sod = class_obj_depth[ci as usize];
class_obj_depth[ci as usize] = sp_new;
sod
} else {
0u32
};
stk_node.push(child);
stk_child_idx.push(child_off[child as usize]);
stk_saved_depth.push(saved_depth);
stk_saved_obj_depth.push(saved_obj_depth);
stk_cls.push(cls);
stk_ci.push(ci);
} else {
let parent = idom[v as usize];
if parent != undef && parent != vroot {
retained[parent as usize] += retained[v as usize];
}
let cls = stk_cls[top];
let ci = stk_ci[top];
if cls != undef && (cls as usize) < class_count {
class_to_last_depth[cls as usize] = stk_saved_depth[top];
}
if ci != undef && (ci as usize) < class_count {
class_obj_depth[ci as usize] = stk_saved_obj_depth[top];
}
stk_node.pop();
stk_child_idx.pop();
stk_saved_depth.pop();
stk_saved_obj_depth.pop();
stk_cls.pop();
stk_ci.pop();
}
}
crate::trace::probe("retained: after hasSame DFS");
(retained, has_same, depth_counts)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chain_retained() {
let n = 3;
let idom = vec![3u32, 0, 1, 3];
let shallow = vec![10u32, 20, 30];
let class_idx = vec![0u32, 0, 0];
let class_obj_class_idx = std::collections::HashMap::<u32, u32>::new();
let (retained, _has_same, _depth) = {
let (co, ct) = build_dom_children_csr(n, &idom);
compute_retained(
n,
&idom,
&shallow,
&class_idx,
1,
&class_obj_class_idx,
&co,
&ct,
)
};
assert_eq!(retained[0], 60, "0 retains all 3");
assert_eq!(retained[1], 50, "1 retains 1+2");
assert_eq!(retained[2], 30, "2 retains itself");
}
#[test]
fn diamond_retained() {
let n = 4;
let idom = vec![4u32, 0, 0, 0, 4]; let shallow = vec![1u32, 2, 3, 4];
let class_idx = vec![0u32, 0, 0, 0];
let class_obj_class_idx = std::collections::HashMap::<u32, u32>::new();
let (retained, _, _) = {
let (co, ct) = build_dom_children_csr(n, &idom);
compute_retained(
n,
&idom,
&shallow,
&class_idx,
1,
&class_obj_class_idx,
&co,
&ct,
)
};
assert_eq!(retained[0], 10);
assert_eq!(retained[1], 2);
assert_eq!(retained[2], 3);
assert_eq!(retained[3], 4);
}
#[test]
fn has_same_class_ancestor() {
let n = 3;
let idom = vec![3u32, 0, 1, 3];
let shallow = vec![10u32, 20, 30];
let class_idx = vec![0u32, 1, 0];
let class_obj_class_idx = std::collections::HashMap::<u32, u32>::new();
let (_, has_same, _) = {
let (co, ct) = build_dom_children_csr(n, &idom);
compute_retained(
n,
&idom,
&shallow,
&class_idx,
2,
&class_obj_class_idx,
&co,
&ct,
)
};
assert!(!has_same.get(0), "node 0 has no class-0 ancestor");
assert!(!has_same.get(1), "node 1 has no class-1 ancestor");
assert!(has_same.get(2), "node 2 has class-0 ancestor (node 0)");
}
#[test]
fn has_same_class_ancestor_via_class_obj() {
let n = 3;
let idom = vec![3u32, 0, 1, 3];
let shallow = vec![10u32, 20, 30];
let class_idx = vec![0u32, 1u32, 0u32];
let mut class_obj_class_idx = std::collections::HashMap::<u32, u32>::new();
class_obj_class_idx.insert(0u32, 1u32);
let (_, has_same, _) = {
let (co, ct) = build_dom_children_csr(n, &idom);
compute_retained(
n,
&idom,
&shallow,
&class_idx,
2,
&class_obj_class_idx,
&co,
&ct,
)
};
assert!(
!has_same.get(0),
"node 0 has no ancestor of class 0 (nor class-obj for any class)"
);
assert!(
has_same.get(1),
"node 1 has class-object-for-class-1 as ancestor"
);
assert!(has_same.get(2), "node 2 has class-0 ancestor (node 0)");
}
}