use super::*;
fn right_spine_left_vars(vtree: &Vtree, start: VtreeIdx) -> (Vec<VarId>, VarId) {
let mut left_vars = Vec::new();
let mut cur = start;
loop {
match vtree.node(cur) {
VtreeNode::Internal { left, right, .. } => {
assert!(
vtree.node(*left).is_leaf(),
"expected leaf-on-left for spine step"
);
left_vars.push(vtree.leaf_var(*left));
cur = *right;
}
VtreeNode::Leaf { var, .. } => return (left_vars, *var),
}
}
}
#[test]
fn test_reverse_rrp_leaf_is_identity() {
let vtree = Vtree::balanced(1);
let t_tilde = vtree.reverse_rightmost_path_recursive();
assert_eq!(t_tilde.num_nodes(), 1);
assert!(t_tilde.node(t_tilde.root()).is_leaf());
assert_eq!(t_tilde.leaf_var(t_tilde.root()), VarId(0));
}
#[test]
fn test_reverse_rrp_two_vars_swaps_children() {
let vtree = Vtree::balanced(2);
let t_tilde = vtree.reverse_rightmost_path_recursive();
assert_eq!(t_tilde.num_nodes(), 3);
let (l, r) = t_tilde.children(t_tilde.root());
assert_eq!(t_tilde.leaf_var(l), VarId(1));
assert_eq!(t_tilde.leaf_var(r), VarId(0));
}
#[test]
fn test_reverse_rrp_right_linear_chain_reverses_order() {
let vtree = Vtree::linear_from_order(&[VarId(0), VarId(1), VarId(2), VarId(3)]);
let t_tilde = vtree.reverse_rightmost_path_recursive();
assert_eq!(t_tilde.num_nodes(), vtree.num_nodes());
let (lefts, last) = right_spine_left_vars(&t_tilde, t_tilde.root());
assert_eq!(lefts, vec![VarId(3), VarId(2), VarId(1)]);
assert_eq!(last, VarId(0));
}
#[test]
fn test_reverse_rrp_balanced_4_becomes_right_linear_reversed() {
let vtree = Vtree::balanced(4);
let t_tilde = vtree.reverse_rightmost_path_recursive();
assert_eq!(t_tilde.num_nodes(), vtree.num_nodes());
let (lefts, last) = right_spine_left_vars(&t_tilde, t_tilde.root());
assert_eq!(lefts, vec![VarId(3), VarId(2), VarId(1)]);
assert_eq!(last, VarId(0));
}
#[test]
fn test_reverse_rrp_recursive_side_subtree() {
let format = "vtree 7\n\
L 0 1\n\
L 1 2\n\
L 2 3\n\
I 3 1 2\n\
I 4 0 3\n\
L 5 4\n\
I 6 4 5\n";
let vtree = Vtree::from_vtree_text(format).expect("parse failed");
let t_tilde = vtree.reverse_rightmost_path_recursive();
assert_eq!(t_tilde.num_nodes(), vtree.num_nodes());
let (lefts, last) = right_spine_left_vars(&t_tilde, t_tilde.root());
assert_eq!(lefts, vec![VarId(3), VarId(2), VarId(1)]);
assert_eq!(last, VarId(0));
}
#[test]
fn test_reverse_rrp_preserves_invariants() {
let inputs = [
Vtree::balanced(8),
Vtree::linear(8),
Vtree::random(8, 7),
Vtree::random(11, 31),
];
for vtree in inputs {
let t_tilde = vtree.reverse_rightmost_path_recursive();
assert_eq!(t_tilde.num_nodes(), vtree.num_nodes());
for i in 0..t_tilde.num_nodes() {
if let VtreeNode::Internal { left, right, .. } = t_tilde.node(VtreeIdx(i as u32)) {
assert!(left.0 < i as u32, "left {} >= parent {}", left.0, i);
assert!(right.0 < i as u32, "right {} >= parent {}", right.0, i);
}
}
let num_leaves = t_tilde.num_leaves() as usize;
for i in 0..t_tilde.num_nodes() {
if t_tilde.node(VtreeIdx(i as u32)).is_leaf() {
assert!(
i < num_leaves,
"leaf at {} but num_leaves={}",
i,
num_leaves
);
} else {
assert!(
i >= num_leaves,
"internal at {} but num_leaves={}",
i,
num_leaves
);
}
}
assert_eq!(t_tilde.root().0 as usize, t_tilde.num_nodes() - 1);
for v in 0..vtree.num_leaves() {
let leaf = t_tilde.leaf_of(VarId(v));
assert_eq!(t_tilde.leaf_var(leaf), VarId(v));
}
}
}
#[test]
fn test_reverse_rrp_preserves_var_set() {
let inputs = [
Vtree::balanced(6),
Vtree::linear(6),
Vtree::balanced(7),
Vtree::random(9, 13),
];
for vtree in inputs {
let mut before: Vec<VarId> = vtree.leaf_bottomup().map(|(_, v)| v).collect();
let t_tilde = vtree.reverse_rightmost_path_recursive();
let mut after: Vec<VarId> = t_tilde.leaf_bottomup().map(|(_, v)| v).collect();
before.sort();
after.sort();
assert_eq!(before, after);
}
}
impl Vtree {
#[must_use]
fn reverse_rightmost_path_recursive(&self) -> Self {
let mut nodes = VtreeArena::with_capacity(self.num_nodes());
let new_root = Self::reverse_rrp_rec(self.root(), self, &mut nodes);
Self::from_nodes(nodes.into_nodes(), new_root, self.num_vars())
}
fn reverse_rrp_rec(old_idx: VtreeIdx, old: &Vtree, nodes: &mut VtreeArena) -> VtreeIdx {
if let VtreeNode::Leaf { var, .. } = old.node(old_idx) {
return nodes.leaf(*var);
}
let mut left_subtrees: Vec<VtreeIdx> = Vec::new();
let mut cur = old_idx;
let rightmost_var: VarId = loop {
match old.node(cur) {
VtreeNode::Internal { left, right, .. } => {
left_subtrees.push(*left);
cur = *right;
}
VtreeNode::Leaf { var, .. } => break *var,
}
};
let transformed: Vec<VtreeIdx> = left_subtrees
.iter()
.map(|&t| Self::reverse_rrp_rec(t, old, nodes))
.collect();
let right_subtree = if transformed.len() == 1 {
transformed[0]
} else {
let mut spine = nodes.internal(transformed[1], transformed[0]);
for &l in transformed.iter().skip(2) {
spine = nodes.internal(l, spine);
}
spine
};
let leaf_idx = nodes.leaf(rightmost_var);
nodes.internal(leaf_idx, right_subtree)
}
}