use crate::*;
use fxhash::FxBuildHasher;
use std::cmp::{max, min};
impl Bdd {
pub fn not(&self) -> Bdd {
return if self.is_true() {
Bdd::mk_false(self.num_vars())
} else if self.is_false() {
Bdd::mk_true(self.num_vars())
} else {
let mut result_vector = self.0.clone();
for i in 2..result_vector.len() {
result_vector[i].high_link.flip_if_terminal();
result_vector[i].low_link.flip_if_terminal();
}
Bdd(result_vector)
};
}
pub fn and(&self, right: &Bdd) -> Bdd {
return apply(self, right, |l, r| match (l, r) {
(Some(true), Some(true)) => Some(true),
(Some(false), _) => Some(false),
(_, Some(false)) => Some(false),
_ => None,
});
}
pub fn or(&self, right: &Bdd) -> Bdd {
return apply(self, right, |l, r| match (l, r) {
(Some(false), Some(false)) => Some(false),
(Some(true), _) => Some(true),
(_, Some(true)) => Some(true),
_ => None,
});
}
pub fn imp(&self, right: &Bdd) -> Bdd {
return apply(self, right, |l, r| match (l, r) {
(Some(true), Some(false)) => Some(false),
(Some(false), _) => Some(true),
(_, Some(true)) => Some(true),
_ => None,
});
}
pub fn iff(&self, right: &Bdd) -> Bdd {
return apply(self, right, |l, r| match (l, r) {
(Some(l), Some(r)) => Some(l == r),
_ => None,
});
}
pub fn xor(&self, right: &Bdd) -> Bdd {
return apply(self, right, |l, r| match (l, r) {
(Some(l), Some(r)) => Some(l ^ r),
_ => None,
});
}
pub fn and_not(&self, right: &Bdd) -> Bdd {
return apply(self, right, |l, r| match (l, r) {
(Some(false), _) => Some(false),
(_, Some(true)) => Some(false),
(Some(true), Some(false)) => Some(true),
_ => None,
});
}
}
fn apply<T>(left: &Bdd, right: &Bdd, terminal_lookup: T) -> Bdd
where
T: Fn(Option<bool>, Option<bool>) -> Option<bool>,
{
let num_vars = left.num_vars();
if right.num_vars() != num_vars {
panic!(
"Var count mismatch: BDDs are not compatible. {} != {}",
num_vars,
right.num_vars()
);
}
let mut result: Bdd = Bdd::mk_true(num_vars);
let mut is_not_empty = false;
let mut existing: HashMap<BddNode, BddPointer, FxBuildHasher> =
HashMap::with_capacity_and_hasher(max(left.size(), right.size()), FxBuildHasher::default());
existing.insert(BddNode::mk_zero(num_vars), BddPointer::zero());
existing.insert(BddNode::mk_one(num_vars), BddPointer::one());
#[derive(Eq, PartialEq, Hash, Copy, Clone)]
struct Task {
left: BddPointer,
right: BddPointer,
};
let mut stack: Vec<Task> = Vec::with_capacity(max(left.size(), right.size()));
stack.push(Task {
left: left.root_pointer(),
right: right.root_pointer(),
});
let mut finished: HashMap<Task, BddPointer, FxBuildHasher> =
HashMap::with_capacity_and_hasher(max(left.size(), right.size()), FxBuildHasher::default());
while let Some(on_stack) = stack.last() {
if finished.contains_key(on_stack) {
stack.pop();
} else {
let (l, r) = (on_stack.left, on_stack.right);
let (l_v, r_v) = (left.var_of(l), right.var_of(r));
let decision_var = min(l_v, r_v);
let (l_low, l_high) = if l_v != decision_var {
(l, l)
} else {
(left.low_link_of(l), left.high_link_of(l))
};
let (r_low, r_high) = if r_v != decision_var {
(r, r)
} else {
(right.low_link_of(r), right.high_link_of(r))
};
let comp_low = Task {
left: l_low,
right: r_low,
};
let comp_high = Task {
left: l_high,
right: r_high,
};
let new_low = terminal_lookup(l_low.as_bool(), r_low.as_bool())
.map(BddPointer::from_bool)
.or_else(|| finished.get(&comp_low).cloned());
let new_high = terminal_lookup(l_high.as_bool(), r_high.as_bool())
.map(BddPointer::from_bool)
.or_else(|| finished.get(&comp_high).cloned());
if let (Some(new_low), Some(new_high)) = (new_low, new_high) {
if new_low.is_one() || new_high.is_one() {
is_not_empty = true
}
if new_low == new_high {
finished.insert(*on_stack, new_low);
} else {
let node = BddNode::mk_node(decision_var, new_low, new_high);
if let Some(index) = existing.get(&node) {
finished.insert(*on_stack, *index);
} else {
result.push_node(node);
existing.insert(node, result.root_pointer());
finished.insert(*on_stack, result.root_pointer());
}
}
stack.pop();
} else {
if new_low.is_none() {
stack.push(comp_low);
}
if new_high.is_none() {
stack.push(comp_high);
}
}
}
}
return if is_not_empty {
result
} else {
Bdd::mk_false(num_vars)
};
}