use rustc_hash::FxHashMap;
use std::fmt;
pub type VarId = u32;
pub type NodeId = usize;
pub const BDD_FALSE: NodeId = 0;
pub const BDD_TRUE: NodeId = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct BddNode {
var: VarId,
low: NodeId,
high: NodeId,
}
#[derive(Debug, Clone)]
pub struct BddManager {
nodes: Vec<BddNode>,
unique_table: FxHashMap<(VarId, NodeId, NodeId), NodeId>,
op_cache: FxHashMap<(BddOp, NodeId, NodeId), NodeId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum BddOp {
And,
Or,
Xor,
}
impl BddManager {
pub fn new() -> Self {
let mut manager = Self {
nodes: Vec::new(),
unique_table: FxHashMap::default(),
op_cache: FxHashMap::default(),
};
manager.nodes.push(BddNode {
var: u32::MAX,
low: 0,
high: 0,
});
manager.nodes.push(BddNode {
var: u32::MAX,
low: 1,
high: 1,
});
manager
}
pub fn constant_false(&self) -> NodeId {
BDD_FALSE
}
pub fn constant_true(&self) -> NodeId {
BDD_TRUE
}
pub fn variable(&mut self, var: VarId) -> NodeId {
self.make_node(var, BDD_FALSE, BDD_TRUE)
}
fn make_node(&mut self, var: VarId, low: NodeId, high: NodeId) -> NodeId {
if low == high {
return low;
}
let key = (var, low, high);
if let Some(&node_id) = self.unique_table.get(&key) {
return node_id;
}
let node_id = self.nodes.len();
self.nodes.push(BddNode { var, low, high });
self.unique_table.insert(key, node_id);
node_id
}
pub fn not(&mut self, node: NodeId) -> NodeId {
self.apply_not_rec(node)
}
fn apply_not_rec(&mut self, node: NodeId) -> NodeId {
if node == BDD_FALSE {
return BDD_TRUE;
}
if node == BDD_TRUE {
return BDD_FALSE;
}
let n = self.nodes[node];
let low = self.apply_not_rec(n.low);
let high = self.apply_not_rec(n.high);
self.make_node(n.var, low, high)
}
pub fn and(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(BddOp::And, left, right)
}
pub fn or(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(BddOp::Or, left, right)
}
pub fn xor(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(BddOp::Xor, left, right)
}
pub fn implies(&mut self, left: NodeId, right: NodeId) -> NodeId {
let not_left = self.not(left);
self.or(not_left, right)
}
pub fn iff(&mut self, left: NodeId, right: NodeId) -> NodeId {
let not_xor = self.xor(left, right);
self.not(not_xor)
}
fn apply(&mut self, op: BddOp, left: NodeId, right: NodeId) -> NodeId {
match op {
BddOp::And => {
if left == BDD_FALSE || right == BDD_FALSE {
return BDD_FALSE;
}
if left == BDD_TRUE {
return right;
}
if right == BDD_TRUE {
return left;
}
if left == right {
return left;
}
}
BddOp::Or => {
if left == BDD_TRUE || right == BDD_TRUE {
return BDD_TRUE;
}
if left == BDD_FALSE {
return right;
}
if right == BDD_FALSE {
return left;
}
if left == right {
return left;
}
}
BddOp::Xor => {
if left == BDD_FALSE {
return right;
}
if right == BDD_FALSE {
return left;
}
if left == BDD_TRUE {
return self.apply_not_rec(right);
}
if right == BDD_TRUE {
return self.apply_not_rec(left);
}
if left == right {
return BDD_FALSE;
}
}
}
let cache_key = (op, left, right);
if let Some(&result) = self.op_cache.get(&cache_key) {
return result;
}
let left_node = self.nodes[left];
let right_node = self.nodes[right];
let result = if left_node.var == right_node.var {
let low = self.apply(op, left_node.low, right_node.low);
let high = self.apply(op, left_node.high, right_node.high);
self.make_node(left_node.var, low, high)
} else if left_node.var < right_node.var {
let low = self.apply(op, left_node.low, right);
let high = self.apply(op, left_node.high, right);
self.make_node(left_node.var, low, high)
} else {
let low = self.apply(op, left, right_node.low);
let high = self.apply(op, left, right_node.high);
self.make_node(right_node.var, low, high)
};
self.op_cache.insert(cache_key, result);
result
}
pub fn ite(&mut self, cond: NodeId, then_node: NodeId, else_node: NodeId) -> NodeId {
if cond == BDD_TRUE {
return then_node;
}
if cond == BDD_FALSE {
return else_node;
}
if then_node == else_node {
return then_node;
}
if then_node == BDD_TRUE && else_node == BDD_FALSE {
return cond;
}
let cond_node = self.nodes[cond];
let then_n = if then_node <= BDD_TRUE {
None
} else {
Some(self.nodes[then_node])
};
let else_n = if else_node <= BDD_TRUE {
None
} else {
Some(self.nodes[else_node])
};
let mut top_var = cond_node.var;
if let Some(n) = then_n
&& n.var < top_var
{
top_var = n.var;
}
if let Some(n) = else_n
&& n.var < top_var
{
top_var = n.var;
}
let cond_low = if cond_node.var == top_var {
cond_node.low
} else {
cond
};
let cond_high = if cond_node.var == top_var {
cond_node.high
} else {
cond
};
let then_low = if let Some(n) = then_n {
if n.var == top_var { n.low } else { then_node }
} else {
then_node
};
let then_high = if let Some(n) = then_n {
if n.var == top_var { n.high } else { then_node }
} else {
then_node
};
let else_low = if let Some(n) = else_n {
if n.var == top_var { n.low } else { else_node }
} else {
else_node
};
let else_high = if let Some(n) = else_n {
if n.var == top_var { n.high } else { else_node }
} else {
else_node
};
let low = self.ite(cond_low, then_low, else_low);
let high = self.ite(cond_high, then_high, else_high);
self.make_node(top_var, low, high)
}
pub fn eval(&self, node: NodeId, assignment: &FxHashMap<VarId, bool>) -> bool {
if node == BDD_FALSE {
return false;
}
if node == BDD_TRUE {
return true;
}
let n = self.nodes[node];
let var_value = assignment.get(&n.var).copied().unwrap_or(false);
if var_value {
self.eval(n.high, assignment)
} else {
self.eval(n.low, assignment)
}
}
pub fn sat_count(&self, node: NodeId, num_vars: u32) -> u64 {
let mut cache = FxHashMap::default();
self.sat_count_rec(node, &mut cache)
* (1u64 << num_vars.saturating_sub(self.max_var(node).map_or(0, |v| v + 1)))
}
fn sat_count_rec(&self, node: NodeId, cache: &mut FxHashMap<NodeId, u64>) -> u64 {
if node == BDD_FALSE {
return 0;
}
if node == BDD_TRUE {
return 1;
}
if let Some(&count) = cache.get(&node) {
return count;
}
let n = self.nodes[node];
let low_count = self.sat_count_rec(n.low, cache);
let high_count = self.sat_count_rec(n.high, cache);
let low_mult = if n.low <= BDD_TRUE {
1
} else {
let low_var = self.nodes[n.low].var;
1u64 << (low_var - n.var - 1)
};
let high_mult = if n.high <= BDD_TRUE {
1
} else {
let high_var = self.nodes[n.high].var;
1u64 << (high_var - n.var - 1)
};
let count = low_count * low_mult + high_count * high_mult;
cache.insert(node, count);
count
}
fn max_var(&self, node: NodeId) -> Option<VarId> {
if node <= BDD_TRUE {
return None;
}
Some(self.nodes[node].var)
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn clear_cache(&mut self) {
self.op_cache.clear();
}
}
impl Default for BddManager {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for BddManager {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "BDD Manager: {} nodes", self.nodes.len())?;
writeln!(f, " Cache size: {}", self.op_cache.len())?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ZddManager {
nodes: Vec<BddNode>,
unique_table: FxHashMap<(VarId, NodeId, NodeId), NodeId>,
op_cache: FxHashMap<(ZddOp, NodeId, NodeId), NodeId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum ZddOp {
Union,
Intersection,
Difference,
}
pub const ZDD_EMPTY: NodeId = 0;
pub const ZDD_BASE: NodeId = 1;
impl ZddManager {
pub fn new() -> Self {
let mut manager = Self {
nodes: Vec::new(),
unique_table: FxHashMap::default(),
op_cache: FxHashMap::default(),
};
manager.nodes.push(BddNode {
var: u32::MAX,
low: 0,
high: 0,
});
manager.nodes.push(BddNode {
var: u32::MAX,
low: 1,
high: 1,
});
manager
}
pub fn empty(&self) -> NodeId {
ZDD_EMPTY
}
pub fn base(&self) -> NodeId {
ZDD_BASE
}
pub fn singleton(&mut self, var: VarId) -> NodeId {
self.make_node(var, ZDD_EMPTY, ZDD_BASE)
}
fn make_node(&mut self, var: VarId, low: NodeId, high: NodeId) -> NodeId {
if high == ZDD_EMPTY {
return low;
}
let key = (var, low, high);
if let Some(&node_id) = self.unique_table.get(&key) {
return node_id;
}
let node_id = self.nodes.len();
self.nodes.push(BddNode { var, low, high });
self.unique_table.insert(key, node_id);
node_id
}
pub fn union(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(ZddOp::Union, left, right)
}
pub fn intersection(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(ZddOp::Intersection, left, right)
}
pub fn difference(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(ZddOp::Difference, left, right)
}
fn apply(&mut self, op: ZddOp, left: NodeId, right: NodeId) -> NodeId {
match op {
ZddOp::Union => {
if left == ZDD_EMPTY {
return right;
}
if right == ZDD_EMPTY {
return left;
}
if left == right {
return left;
}
}
ZddOp::Intersection => {
if left == ZDD_EMPTY || right == ZDD_EMPTY {
return ZDD_EMPTY;
}
if left == right {
return left;
}
if left == ZDD_BASE || right == ZDD_BASE {
return ZDD_EMPTY;
}
}
ZddOp::Difference => {
if left == ZDD_EMPTY {
return ZDD_EMPTY;
}
if right == ZDD_EMPTY {
return left;
}
if left == right {
return ZDD_EMPTY;
}
}
}
let cache_key = (op, left, right);
if let Some(&result) = self.op_cache.get(&cache_key) {
return result;
}
let left_node = self.nodes[left];
let right_node = self.nodes[right];
let result = if left_node.var == right_node.var {
let low = self.apply(op, left_node.low, right_node.low);
let high = self.apply(op, left_node.high, right_node.high);
self.make_node(left_node.var, low, high)
} else if left_node.var < right_node.var {
let low = self.apply(op, left_node.low, right);
let high = match op {
ZddOp::Union => left_node.high,
ZddOp::Intersection => self.apply(op, left_node.high, right),
ZddOp::Difference => left_node.high,
};
self.make_node(left_node.var, low, high)
} else {
let low = self.apply(op, left, right_node.low);
let high = match op {
ZddOp::Union => right_node.high,
ZddOp::Intersection => self.apply(op, left, right_node.high),
ZddOp::Difference => ZDD_EMPTY,
};
self.make_node(right_node.var, low, high)
};
self.op_cache.insert(cache_key, result);
result
}
pub fn count(&self, node: NodeId) -> u64 {
let mut cache = FxHashMap::default();
self.count_rec(node, &mut cache)
}
fn count_rec(&self, node: NodeId, cache: &mut FxHashMap<NodeId, u64>) -> u64 {
if node == ZDD_EMPTY {
return 0;
}
if node == ZDD_BASE {
return 1;
}
if let Some(&count) = cache.get(&node) {
return count;
}
let n = self.nodes[node];
let count = self.count_rec(n.low, cache) + self.count_rec(n.high, cache);
cache.insert(node, count);
count
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn clear_cache(&mut self) {
self.op_cache.clear();
}
}
impl Default for ZddManager {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for ZddManager {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "ZDD Manager: {} nodes", self.nodes.len())?;
writeln!(f, " Cache size: {}", self.op_cache.len())?;
Ok(())
}
}
use num_rational::BigRational;
use num_traits::Zero;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum AddNode {
Terminal(BigRational),
Internal {
var: VarId,
low: NodeId,
high: NodeId,
},
}
#[derive(Debug, Clone)]
pub struct AddManager {
nodes: Vec<AddNode>,
unique_table: FxHashMap<AddNode, NodeId>,
op_cache: FxHashMap<(AddOp, NodeId, NodeId), NodeId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum AddOp {
Add,
Mul,
Max,
Min,
}
impl AddManager {
pub fn new() -> Self {
let mut manager = Self {
nodes: Vec::new(),
unique_table: FxHashMap::default(),
op_cache: FxHashMap::default(),
};
let zero = AddNode::Terminal(BigRational::zero());
manager.nodes.push(zero.clone());
manager.unique_table.insert(zero, 0);
manager
}
pub fn constant(&mut self, value: BigRational) -> NodeId {
let node = AddNode::Terminal(value);
if let Some(&id) = self.unique_table.get(&node) {
return id;
}
let id = self.nodes.len();
self.nodes.push(node.clone());
self.unique_table.insert(node, id);
id
}
pub fn variable(&mut self, var: VarId) -> NodeId {
let zero = self.constant(BigRational::zero());
let one = self.constant(BigRational::from_integer(1.into()));
self.make_node(var, zero, one)
}
fn make_node(&mut self, var: VarId, low: NodeId, high: NodeId) -> NodeId {
if low == high {
return low;
}
let node = AddNode::Internal { var, low, high };
if let Some(&id) = self.unique_table.get(&node) {
return id;
}
let id = self.nodes.len();
self.nodes.push(node.clone());
self.unique_table.insert(node, id);
id
}
pub fn eval(&self, node: NodeId, assignment: &FxHashMap<VarId, bool>) -> BigRational {
match &self.nodes[node] {
AddNode::Terminal(value) => value.clone(),
AddNode::Internal { var, low, high } => {
if *assignment.get(var).unwrap_or(&false) {
self.eval(*high, assignment)
} else {
self.eval(*low, assignment)
}
}
}
}
pub fn add(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(AddOp::Add, left, right)
}
pub fn mul(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(AddOp::Mul, left, right)
}
pub fn max(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(AddOp::Max, left, right)
}
pub fn min(&mut self, left: NodeId, right: NodeId) -> NodeId {
self.apply(AddOp::Min, left, right)
}
pub fn scale(&mut self, node: NodeId, scalar: &BigRational) -> NodeId {
let scalar_node = self.constant(scalar.clone());
self.mul(node, scalar_node)
}
fn apply(&mut self, op: AddOp, left: NodeId, right: NodeId) -> NodeId {
if let (AddNode::Terminal(l), AddNode::Terminal(r)) =
(&self.nodes[left], &self.nodes[right])
{
let result_value = match op {
AddOp::Add => l + r,
AddOp::Mul => l * r,
AddOp::Max => {
if l > r {
l.clone()
} else {
r.clone()
}
}
AddOp::Min => {
if l < r {
l.clone()
} else {
r.clone()
}
}
};
return self.constant(result_value);
}
let cache_key = (op, left, right);
if let Some(&result) = self.op_cache.get(&cache_key) {
return result;
}
let (var, left_low, left_high, right_low, right_high) = self.split_nodes(left, right);
let low = self.apply(op, left_low, right_low);
let high = self.apply(op, left_high, right_high);
let result = self.make_node(var, low, high);
self.op_cache.insert(cache_key, result);
result
}
fn split_nodes(&self, left: NodeId, right: NodeId) -> (VarId, NodeId, NodeId, NodeId, NodeId) {
let (left_var, left_low, left_high) = match &self.nodes[left] {
AddNode::Terminal(_) => (u32::MAX, left, left),
AddNode::Internal { var, low, high } => (*var, *low, *high),
};
let (right_var, right_low, right_high) = match &self.nodes[right] {
AddNode::Terminal(_) => (u32::MAX, right, right),
AddNode::Internal { var, low, high } => (*var, *low, *high),
};
if left_var == u32::MAX && right_var == u32::MAX {
panic!("Both nodes are terminals - should have been handled earlier");
}
if left_var == right_var {
(left_var, left_low, left_high, right_low, right_high)
} else if left_var < right_var {
(left_var, left_low, left_high, right, right)
} else {
(right_var, left, left, right_low, right_high)
}
}
pub fn ite(&mut self, cond: NodeId, then_add: NodeId, else_add: NodeId) -> NodeId {
let one = self.constant(BigRational::from_integer(1.into()));
let neg_one = BigRational::from_integer((-1).into());
let scaled_cond = self.scale(cond, &neg_one);
let not_cond = self.apply(AddOp::Add, one, scaled_cond);
let then_part = self.mul(cond, then_add);
let else_part = self.mul(not_cond, else_add);
self.add(then_part, else_part)
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn clear_cache(&mut self) {
self.op_cache.clear();
}
pub fn get_var(&self, node: NodeId) -> Option<VarId> {
match &self.nodes[node] {
AddNode::Terminal(_) => None,
AddNode::Internal { var, .. } => Some(*var),
}
}
pub fn get_value(&self, node: NodeId) -> Option<&BigRational> {
match &self.nodes[node] {
AddNode::Terminal(value) => Some(value),
AddNode::Internal { .. } => None,
}
}
}
impl Default for AddManager {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for AddManager {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "ADD Manager: {} nodes", self.nodes.len())?;
writeln!(f, " Cache size: {}", self.op_cache.len())?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bdd_constants() {
let manager = BddManager::new();
assert_eq!(manager.constant_false(), BDD_FALSE);
assert_eq!(manager.constant_true(), BDD_TRUE);
}
#[test]
fn test_bdd_variable() {
let mut manager = BddManager::new();
let x = manager.variable(0);
let mut assignment = FxHashMap::default();
assignment.insert(0, true);
assert!(manager.eval(x, &assignment));
assignment.insert(0, false);
assert!(!manager.eval(x, &assignment));
}
#[test]
fn test_bdd_not() {
let mut manager = BddManager::new();
let x = manager.variable(0);
let not_x = manager.not(x);
let mut assignment = FxHashMap::default();
assignment.insert(0, true);
assert!(!manager.eval(not_x, &assignment));
assignment.insert(0, false);
assert!(manager.eval(not_x, &assignment));
}
#[test]
fn test_bdd_and() {
let mut manager = BddManager::new();
let x = manager.variable(0);
let y = manager.variable(1);
let x_and_y = manager.and(x, y);
let mut assignment = FxHashMap::default();
assignment.insert(0, true);
assignment.insert(1, true);
assert!(manager.eval(x_and_y, &assignment));
assignment.insert(0, false);
assert!(!manager.eval(x_and_y, &assignment));
}
#[test]
fn test_bdd_or() {
let mut manager = BddManager::new();
let x = manager.variable(0);
let y = manager.variable(1);
let x_or_y = manager.or(x, y);
let mut assignment = FxHashMap::default();
assignment.insert(0, false);
assignment.insert(1, false);
assert!(!manager.eval(x_or_y, &assignment));
assignment.insert(0, true);
assert!(manager.eval(x_or_y, &assignment));
}
#[test]
fn test_bdd_xor() {
let mut manager = BddManager::new();
let x = manager.variable(0);
let y = manager.variable(1);
let x_xor_y = manager.xor(x, y);
let mut assignment = FxHashMap::default();
assignment.insert(0, true);
assignment.insert(1, true);
assert!(!manager.eval(x_xor_y, &assignment));
assignment.insert(0, true);
assignment.insert(1, false);
assert!(manager.eval(x_xor_y, &assignment));
}
#[test]
fn test_bdd_ite() {
let mut manager = BddManager::new();
let x = manager.variable(0);
let y = manager.variable(1);
let z = manager.variable(2);
let result = manager.ite(x, y, z);
let mut assignment = FxHashMap::default();
assignment.insert(0, true);
assignment.insert(1, true);
assignment.insert(2, false);
assert!(manager.eval(result, &assignment));
assignment.insert(0, false);
assignment.insert(1, true);
assignment.insert(2, false);
assert!(!manager.eval(result, &assignment)); }
#[test]
fn test_bdd_implies() {
let mut manager = BddManager::new();
let x = manager.variable(0);
let y = manager.variable(1);
let x_implies_y = manager.implies(x, y);
let mut assignment = FxHashMap::default();
assignment.insert(0, true);
assignment.insert(1, false);
assert!(!manager.eval(x_implies_y, &assignment));
assignment.insert(0, false);
assignment.insert(1, false);
assert!(manager.eval(x_implies_y, &assignment));
}
#[test]
fn test_bdd_sharing() {
let mut manager = BddManager::new();
let x = manager.variable(0);
let y = manager.variable(0); assert_eq!(x, y); }
#[test]
fn test_zdd_empty_and_base() {
let manager = ZddManager::new();
assert_eq!(manager.empty(), ZDD_EMPTY);
assert_eq!(manager.base(), ZDD_BASE);
assert_eq!(manager.count(ZDD_EMPTY), 0);
assert_eq!(manager.count(ZDD_BASE), 1);
}
#[test]
fn test_zdd_singleton() {
let mut manager = ZddManager::new();
let x = manager.singleton(0);
assert_eq!(manager.count(x), 1); assert_ne!(x, ZDD_EMPTY);
assert_ne!(x, ZDD_BASE);
}
#[test]
fn test_zdd_union() {
let mut manager = ZddManager::new();
let x = manager.singleton(0);
let y = manager.singleton(1);
let union = manager.union(x, y);
assert_eq!(manager.count(union), 2);
}
#[test]
fn test_zdd_intersection() {
let mut manager = ZddManager::new();
let x = manager.singleton(0);
let y = manager.singleton(1);
let intersection = manager.intersection(x, y);
assert_eq!(intersection, ZDD_EMPTY);
assert_eq!(manager.count(intersection), 0);
}
#[test]
fn test_zdd_difference() {
let mut manager = ZddManager::new();
let x = manager.singleton(0);
let y = manager.singleton(1);
let diff1 = manager.difference(x, y);
assert_eq!(manager.count(diff1), 1);
let diff2 = manager.difference(x, x);
assert_eq!(diff2, ZDD_EMPTY); }
#[test]
fn test_zdd_union_associative() {
let mut manager = ZddManager::new();
let x = manager.singleton(0);
let y = manager.singleton(1);
let z = manager.singleton(2);
let xy = manager.union(x, y);
let xyz1 = manager.union(xy, z);
let yz = manager.union(y, z);
let xyz2 = manager.union(x, yz);
assert_eq!(manager.count(xyz1), manager.count(xyz2));
assert_eq!(manager.count(xyz1), 3); }
#[test]
fn test_add_constant() {
use num_bigint::BigInt;
let mut manager = AddManager::new();
let five = manager.constant(BigRational::from_integer(BigInt::from(5)));
let assignment = FxHashMap::default();
let result = manager.eval(five, &assignment);
assert_eq!(result, BigRational::from_integer(BigInt::from(5)));
}
#[test]
fn test_add_variable() {
use num_bigint::BigInt;
let mut manager = AddManager::new();
let x = manager.variable(0);
let mut assignment = FxHashMap::default();
assignment.insert(0, true);
let result = manager.eval(x, &assignment);
assert_eq!(result, BigRational::from_integer(BigInt::from(1)));
assignment.insert(0, false);
let result = manager.eval(x, &assignment);
assert_eq!(result, BigRational::from_integer(BigInt::from(0)));
}
#[test]
fn test_add_addition() {
use num_bigint::BigInt;
let mut manager = AddManager::new();
let three = manager.constant(BigRational::from_integer(BigInt::from(3)));
let five = manager.constant(BigRational::from_integer(BigInt::from(5)));
let sum = manager.add(three, five);
let assignment = FxHashMap::default();
let result = manager.eval(sum, &assignment);
assert_eq!(result, BigRational::from_integer(BigInt::from(8)));
}
#[test]
fn test_add_multiplication() {
use num_bigint::BigInt;
let mut manager = AddManager::new();
let three = manager.constant(BigRational::from_integer(BigInt::from(3)));
let five = manager.constant(BigRational::from_integer(BigInt::from(5)));
let product = manager.mul(three, five);
let assignment = FxHashMap::default();
let result = manager.eval(product, &assignment);
assert_eq!(result, BigRational::from_integer(BigInt::from(15)));
}
#[test]
fn test_add_max_min() {
use num_bigint::BigInt;
let mut manager = AddManager::new();
let three = manager.constant(BigRational::from_integer(BigInt::from(3)));
let five = manager.constant(BigRational::from_integer(BigInt::from(5)));
let max_val = manager.max(three, five);
let min_val = manager.min(three, five);
let assignment = FxHashMap::default();
assert_eq!(
manager.eval(max_val, &assignment),
BigRational::from_integer(BigInt::from(5))
);
assert_eq!(
manager.eval(min_val, &assignment),
BigRational::from_integer(BigInt::from(3))
);
}
#[test]
fn test_add_scale() {
use num_bigint::BigInt;
let mut manager = AddManager::new();
let x = manager.variable(0);
let scaled = manager.scale(x, &BigRational::from_integer(BigInt::from(5)));
let mut assignment = FxHashMap::default();
assignment.insert(0, true);
let result = manager.eval(scaled, &assignment);
assert_eq!(result, BigRational::from_integer(BigInt::from(5)));
assignment.insert(0, false);
let result = manager.eval(scaled, &assignment);
assert_eq!(result, BigRational::from_integer(BigInt::from(0)));
}
#[test]
fn test_add_sharing() {
use num_bigint::BigInt;
let mut manager = AddManager::new();
let x = manager.constant(BigRational::from_integer(BigInt::from(5)));
let y = manager.constant(BigRational::from_integer(BigInt::from(5)));
assert_eq!(x, y); }
}