pub use crate::cnf::{Literal, VarId};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
pub struct VtreeIdx(pub u32);
impl VtreeIdx {
#[inline(always)]
pub fn idx(self) -> usize {
self.0 as usize
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub(crate) enum RotationKind {
Left,
Right,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum VtreeNode {
Leaf {
var: VarId,
parent: Option<VtreeIdx>,
},
Internal {
left: VtreeIdx,
right: VtreeIdx,
parent: Option<VtreeIdx>,
},
}
impl VtreeNode {
pub fn parent(&self) -> Option<VtreeIdx> {
match self {
VtreeNode::Leaf { parent, .. } => *parent,
VtreeNode::Internal { parent, .. } => *parent,
}
}
pub fn is_leaf(&self) -> bool {
matches!(self, VtreeNode::Leaf { .. })
}
}
#[track_caller]
fn require_nonempty(num_vars: u32) {
assert!(num_vars > 0, "a vtree needs at least one variable");
}
#[derive(Clone, Debug)]
pub struct Vtree {
nodes: Vec<VtreeNode>,
root: VtreeIdx,
var_to_leaf: Vec<VtreeIdx>,
leaf_count: Option<u32>,
topo: Vec<VtreeIdx>,
topo_pos: Vec<u32>,
internal_topo: Vec<VtreeIdx>,
leaf_topo: Vec<VtreeIdx>,
}
impl Vtree {
#[inline]
pub fn num_leaves(&self) -> u32 {
self.leaf_count.unwrap_or(self.var_to_leaf.len() as u32)
}
pub fn bottomup(&self) -> impl DoubleEndedIterator<Item = VtreeIdx> + ExactSizeIterator + '_ {
self.topo.iter().copied()
}
pub fn leaf_bottomup(
&self,
) -> impl DoubleEndedIterator<Item = (VtreeIdx, VarId)> + ExactSizeIterator + '_ {
self.leaf_topo.iter().map(move |&t| {
let var = match self.node(t) {
VtreeNode::Leaf { var, .. } => *var,
_ => unreachable!("leaf_topo entry is not a leaf"),
};
(t, var)
})
}
pub(crate) fn internal_bottomup(
&self,
) -> impl DoubleEndedIterator<Item = (VtreeIdx, VtreeIdx, VtreeIdx)> + ExactSizeIterator + '_
{
self.internal_topo.iter().map(move |&t| match self.node(t) {
VtreeNode::Internal { left, right, .. } => (t, *left, *right),
_ => unreachable!("internal_topo entry is not internal"),
})
}
pub fn balanced(num_vars: u32) -> Self {
require_nonempty(num_vars);
let vars: Vec<VarId> = (0..num_vars).map(VarId).collect();
let mut nodes = VtreeArena::new();
let root = Self::build_balanced_recursive(&vars, &mut nodes);
Self::from_nodes(nodes.into_nodes(), root, num_vars)
}
pub(crate) fn build_balanced_recursive(vars: &[VarId], nodes: &mut VtreeArena) -> VtreeIdx {
if vars.len() == 1 {
return nodes.leaf(vars[0]);
}
let mid = vars.len() / 2;
let left = Self::build_balanced_recursive(&vars[..mid], nodes);
let right = Self::build_balanced_recursive(&vars[mid..], nodes);
nodes.internal(left, right)
}
pub fn linear(num_vars: u32) -> Self {
require_nonempty(num_vars);
let vars: Vec<VarId> = (0..num_vars).map(VarId).collect();
Self::linear_from_order(&vars)
}
pub fn reverse_linear(num_vars: u32) -> Self {
require_nonempty(num_vars);
let vars: Vec<VarId> = (0..num_vars).rev().map(VarId).collect();
Self::linear_from_order(&vars)
}
pub fn linear_from_order(vars: &[VarId]) -> Self {
require_nonempty(vars.len() as u32);
let num_vars = vars.iter().map(|v| v.0).max().unwrap() + 1;
let mut nodes = VtreeArena::new();
let root = Self::build_linear_iterative(vars, &mut nodes);
Self::from_nodes(nodes.into_nodes(), root, num_vars)
}
fn build_linear_iterative(vars: &[VarId], nodes: &mut VtreeArena) -> VtreeIdx {
let mut right = nodes.leaf(*vars.last().unwrap());
for &var in vars[..vars.len() - 1].iter().rev() {
let left = nodes.leaf(var);
right = nodes.internal(left, right);
}
right
}
pub fn random(num_vars: u32, seed: u64) -> Self {
use rand::SeedableRng;
use rand::rngs::SmallRng;
let mut rng = SmallRng::seed_from_u64(seed);
Self::random_with_rng(num_vars, &mut rng)
}
pub(crate) fn random_with_rng(num_vars: u32, rng: &mut impl rand::Rng) -> Self {
use rand::RngExt;
require_nonempty(num_vars);
let mut nodes = VtreeArena::new();
use rand::seq::SliceRandom;
let mut var_ids: Vec<u32> = (0..num_vars).collect();
var_ids.shuffle(rng);
let mut forest: Vec<VtreeIdx> = var_ids.iter().map(|&v| nodes.leaf(VarId(v))).collect();
while forest.len() > 1 {
let i = rng.random_range(0..forest.len());
let left = forest.swap_remove(i);
let j = rng.random_range(0..forest.len());
let right = forest.swap_remove(j);
forest.push(nodes.internal(left, right));
}
let root = forest[0];
Self::from_nodes(nodes.into_nodes(), root, num_vars)
}
fn reindex_bottomup(
root: VtreeIdx,
old_nodes: Vec<VtreeNode>,
mut var_to_leaf: Vec<VtreeIdx>,
) -> Self {
use std::collections::VecDeque;
let n = old_nodes.len();
let mut old_to_new = vec![VtreeIdx(0); n];
let mut levels: Vec<Vec<VtreeIdx>> = Vec::new();
let mut queue = VecDeque::new();
queue.push_back(root);
while !queue.is_empty() {
let level_size = queue.len();
let mut level = Vec::with_capacity(level_size);
for _ in 0..level_size {
let idx = queue.pop_front().unwrap();
level.push(idx);
if let VtreeNode::Internal { left, right, .. } = &old_nodes[idx.idx()] {
queue.push_back(*left);
queue.push_back(*right);
}
}
levels.push(level);
}
let mut new_nodes = Vec::with_capacity(n);
for level in levels.iter().rev() {
for &old_idx in level {
if let VtreeNode::Leaf { var, .. } = &old_nodes[old_idx.idx()] {
let new_idx = VtreeIdx(new_nodes.len() as u32);
old_to_new[old_idx.idx()] = new_idx;
new_nodes.push(VtreeNode::Leaf {
var: *var,
parent: None,
});
var_to_leaf[var.idx()] = new_idx;
}
}
}
let actual_leaf_count = new_nodes.len() as u32;
for level in levels.iter().rev() {
for &old_idx in level {
if let VtreeNode::Internal { left, right, .. } = &old_nodes[old_idx.idx()] {
let new_left = old_to_new[left.idx()];
let new_right = old_to_new[right.idx()];
let new_idx = VtreeIdx(new_nodes.len() as u32);
old_to_new[old_idx.idx()] = new_idx;
new_nodes.push(VtreeNode::Internal {
left: new_left,
right: new_right,
parent: None,
});
Self::set_parent(&mut new_nodes, new_left, new_idx);
Self::set_parent(&mut new_nodes, new_right, new_idx);
}
}
}
let new_root = old_to_new[root.idx()];
let leaf_count = if actual_leaf_count != var_to_leaf.len() as u32 {
Some(actual_leaf_count)
} else {
None
};
let n = new_nodes.len();
let topo: Vec<VtreeIdx> = (0..n as u32).map(VtreeIdx).collect();
let topo_pos: Vec<u32> = (0..n as u32).collect();
let mut leaf_topo = Vec::with_capacity(actual_leaf_count as usize);
let mut internal_topo = Vec::with_capacity(n - actual_leaf_count as usize);
for &t in &topo {
if new_nodes[t.idx()].is_leaf() {
leaf_topo.push(t);
} else {
internal_topo.push(t);
}
}
Vtree {
nodes: new_nodes,
root: new_root,
var_to_leaf,
leaf_count,
topo,
topo_pos,
internal_topo,
leaf_topo,
}
}
pub(crate) fn fixup_topo_after_rotate(
&mut self,
info: &rotate::RotationInfo,
kind: RotationKind,
) {
self.fixup_topo_pointers_only_after_rotate(info, kind);
self.refresh_filtered_topo();
}
pub(crate) fn fixup_topo_pointers_only_after_rotate(
&mut self,
info: &rotate::RotationInfo,
kind: RotationKind,
) {
let w_pos = self.topo_pos[info.w_idx.idx()] as usize;
let misplaced_root = match kind {
RotationKind::Left => info.a_idx,
RotationKind::Right => info.c_idx,
};
let m_end = self.topo_pos[misplaced_root.idx()] as usize;
if m_end < w_pos {
return;
}
debug_assert!(
m_end > w_pos,
"misplaced_root and w cannot share a topo position"
);
self.topo[w_pos..=m_end].rotate_left(1);
for (offset, &node) in self.topo[w_pos..=m_end].iter().enumerate() {
self.topo_pos[node.idx()] = (w_pos + offset) as u32;
}
}
pub(crate) fn refresh_filtered_topo(&mut self) {
self.internal_topo.clear();
self.leaf_topo.clear();
for &t in &self.topo {
if self.nodes[t.idx()].is_leaf() {
self.leaf_topo.push(t);
} else {
self.internal_topo.push(t);
}
}
}
#[inline]
pub(crate) fn topo_pos(&self, idx: VtreeIdx) -> u32 {
self.topo_pos[idx.idx()]
}
pub(crate) fn set_parent(nodes: &mut [VtreeNode], child: VtreeIdx, parent: VtreeIdx) {
match &mut nodes[child.idx()] {
VtreeNode::Leaf { parent: p, .. } => *p = Some(parent),
VtreeNode::Internal { parent: p, .. } => *p = Some(parent),
}
}
pub(crate) fn from_nodes(nodes: Vec<VtreeNode>, root: VtreeIdx, num_vars: u32) -> Self {
let var_to_leaf = vec![VtreeIdx(0); num_vars as usize];
Self::reindex_bottomup(root, nodes, var_to_leaf)
}
#[inline]
pub fn node(&self, idx: VtreeIdx) -> &VtreeNode {
&self.nodes[idx.idx()]
}
#[inline]
pub fn root(&self) -> VtreeIdx {
self.root
}
#[inline]
pub fn leaf_of(&self, var: VarId) -> VtreeIdx {
self.var_to_leaf[var.idx()]
}
#[inline]
pub fn num_vars(&self) -> u32 {
self.var_to_leaf.len() as u32
}
pub fn num_nodes(&self) -> usize {
self.nodes.len()
}
pub fn children(&self, idx: VtreeIdx) -> (VtreeIdx, VtreeIdx) {
match &self.nodes[idx.idx()] {
VtreeNode::Internal { left, right, .. } => (*left, *right),
VtreeNode::Leaf { .. } => panic!("children() called on leaf node"),
}
}
pub fn same_tree(&self, other: &Vtree) -> bool {
let mut pairs = vec![(self.root(), other.root())];
while let Some((a, b)) = pairs.pop() {
match (self.node(a), other.node(b)) {
(VtreeNode::Leaf { var: va, .. }, VtreeNode::Leaf { var: vb, .. }) => {
if va != vb {
return false;
}
}
(VtreeNode::Internal { .. }, VtreeNode::Internal { .. }) => {
let (a_left, a_right) = self.children(a);
let (b_left, b_right) = other.children(b);
pairs.push((a_left, b_left));
pairs.push((a_right, b_right));
}
_ => return false,
}
}
true
}
pub fn leaf_var(&self, idx: VtreeIdx) -> VarId {
match &self.nodes[idx.idx()] {
VtreeNode::Leaf { var, .. } => *var,
VtreeNode::Internal { .. } => panic!("leaf_var() called on internal node"),
}
}
pub fn sibling(&self, idx: VtreeIdx) -> VtreeIdx {
let parent = self.node(idx).parent().expect("sibling() called on root");
let (left, right) = self.children(parent);
if left == idx { right } else { left }
}
pub fn lca(&self, mut a: VtreeIdx, mut b: VtreeIdx) -> VtreeIdx {
while a != b {
if self.topo_pos[a.idx()] < self.topo_pos[b.idx()] {
a = self.node(a).parent().expect("nodes should share a root");
} else {
b = self.node(b).parent().expect("nodes should share a root");
}
}
a
}
}
mod arena;
pub(crate) use arena::VtreeArena;
mod text;
pub mod rotate;
#[cfg(test)]
mod tests;