use crate::knowledge_compilation::bdd::bdd_kernel::{is_const, BddKernel};
use super::bdd_kernel::{is_one, is_zero, pair, Operand, BDD_FALSE, BDD_TRUE, OPCODE_NOT};
const CACHEID_RESTRICT: usize = 0x1;
const CACHEID_FORALL: usize = 0x1;
pub fn ith_var(i: usize, kernel: &mut BddKernel) -> usize {
assert!(i < kernel.varnum, "Illegal variable number: {i}");
kernel.vars[i * 2]
}
pub fn nith_var(i: usize, kernel: &mut BddKernel) -> usize {
assert!(i < kernel.varnum, "Illegal variable number: {i}");
kernel.vars[i * 2 + 1]
}
pub fn bdd_var(root: usize, kernel: &mut BddKernel) -> usize {
assert!(root >= 2, "Illegal node number: {root}");
kernel.level2var[kernel.level(root)]
}
pub fn bdd_low(root: usize, kernel: &mut BddKernel) -> usize {
assert!(root >= 2, "Illegal node number: {root}");
kernel.low(root).unwrap()
}
pub fn bdd_high(root: usize, kernel: &mut BddKernel) -> usize {
assert!(root >= 2, "Illegal node number: {root}");
kernel.high(root)
}
pub fn and(l: usize, r: usize, kernel: &mut BddKernel) -> usize {
kernel.apply(l, r, &Operand::and())
}
pub fn or(l: usize, r: usize, kernel: &mut BddKernel) -> usize {
kernel.apply(l, r, &Operand::or())
}
pub fn implication(l: usize, r: usize, kernel: &mut BddKernel) -> usize {
kernel.apply(l, r, &Operand::imp())
}
pub fn equivalence(l: usize, r: usize, kernel: &mut BddKernel) -> usize {
kernel.apply(l, r, &Operand::equiv())
}
pub fn not(r: usize, kernel: &mut BddKernel) -> usize {
not_rec(r, kernel)
}
pub fn restrict(r: usize, var: usize, kernel: &mut BddKernel) -> usize {
if var < 2 {
return r;
}
varset2svartable(var, kernel);
restrict_rec(r, (var << 3) | CACHEID_RESTRICT, kernel)
}
pub fn exists(r: usize, var: usize, kernel: &mut BddKernel) -> usize {
if var < 2 {
return r;
}
varset2vartable(var, kernel);
quant_rec(r, &Operand::or(), var << 3, kernel)
}
pub fn for_all(r: usize, var: usize, kernel: &mut BddKernel) -> usize {
if var < 2 {
return r;
}
varset2vartable(var, kernel);
quant_rec(r, &Operand::and(), (var << 3) | CACHEID_FORALL, kernel)
}
fn not_rec(r: usize, kernel: &mut BddKernel) -> usize {
if is_zero(r) {
return BDD_TRUE;
}
if is_one(r) {
return BDD_FALSE;
}
let cache_abc = kernel.applycache.lookup(r);
if cache_abc.0 == Some(r) && cache_abc.2 == OPCODE_NOT {
return kernel.applycache.lookup_res(r);
}
let ref1 = not_rec(kernel.low(r).unwrap(), kernel);
kernel.push_ref(ref1);
let ref2 = not_rec(kernel.high(r), kernel);
kernel.push_ref(ref2);
let res = kernel.make_node(kernel.level(r), kernel.read_ref(2), kernel.read_ref(1));
kernel.pop_ref(2);
kernel.applycache.set_with_res(r, (r, 0, OPCODE_NOT), res);
res
}
fn restrict_rec(r: usize, miscid: usize, kernel: &mut BddKernel) -> usize {
if is_const(r) || kernel.level(r) > kernel.quantlast {
return r;
}
let search_hash = pair(r, miscid);
let cache_abc = kernel.misccache.lookup(search_hash);
if cache_abc.0 == Some(r) && cache_abc.2 == miscid {
return kernel.misccache.lookup_res(search_hash);
}
let res: usize;
if kernel.insvarset(kernel.level(r)) {
if kernel.quantvarset[kernel.level(r)] > 0 {
res = restrict_rec(kernel.high(r), miscid, kernel);
} else {
res = restrict_rec(kernel.low(r).unwrap(), miscid, kernel);
}
} else {
let ref1 = restrict_rec(kernel.low(r).unwrap(), miscid, kernel);
kernel.push_ref(ref1);
let ref2 = restrict_rec(kernel.high(r), miscid, kernel);
kernel.push_ref(ref2);
res = kernel.make_node(kernel.level(r), kernel.read_ref(2), kernel.read_ref(1));
kernel.pop_ref(2);
}
kernel.misccache.set_with_res(search_hash, (r, 0, miscid), res);
res
}
fn quant_rec(r: usize, op: &Operand, quantid: usize, kernel: &mut BddKernel) -> usize {
if r < 2 || kernel.level(r) > kernel.quantlast {
return r;
}
let cache_abc = kernel.quantcache.lookup(r);
if cache_abc.0 == Some(r) && cache_abc.2 == quantid {
return kernel.quantcache.lookup_res(r);
}
let ref1 = quant_rec(kernel.low(r).unwrap(), op, quantid, kernel);
kernel.push_ref(ref1);
let ref2 = quant_rec(kernel.high(r), op, quantid, kernel);
kernel.push_ref(ref2);
let res = if kernel.invarset(kernel.level(r)) {
kernel.apply_rec(kernel.read_ref(2), kernel.read_ref(1), op)
} else {
kernel.make_node(kernel.level(r), kernel.read_ref(2), kernel.read_ref(1))
};
kernel.pop_ref(2);
kernel.quantcache.set_with_res(r, (r, 0, quantid), res);
res
}
#[allow(clippy::cast_possible_wrap)]
fn varset2svartable(r: usize, kernel: &mut BddKernel) {
assert!(r >= 2, "Illegal variable: {r}");
kernel.quantvarset_id += 1;
if kernel.quantvarset_id == u32::MAX / 2 {
kernel.quantvarset = vec![0; kernel.varnum];
kernel.quantvarset_id = 1;
}
let mut n = r;
while !is_const(n) {
let id = kernel.quantvarset_id as i32;
let level = kernel.level(n);
if is_zero(kernel.low(n).unwrap()) {
kernel.quantvarset[level] = id;
n = kernel.high(n);
} else {
kernel.quantvarset[level] = -id;
n = kernel.low(n).unwrap();
}
kernel.quantlast = kernel.level(n);
}
}
#[allow(clippy::cast_possible_wrap)]
fn varset2vartable(r: usize, kernel: &mut BddKernel) {
assert!(r >= 2, "Illegal variable: {r}");
kernel.quantvarset_id += 1;
if kernel.quantvarset_id == u32::MAX {
kernel.quantvarset = vec![0; kernel.varnum];
kernel.quantvarset_id = 1;
}
let mut n = r;
while n > 1 {
let level = kernel.level(n);
kernel.quantvarset[level] = kernel.quantvarset_id as i32;
kernel.quantlast = level;
n = kernel.high(n);
}
}
#[cfg(test)]
mod tests {
use crate::knowledge_compilation::bdd::bdd_construction::{bdd_high, bdd_low, bdd_var, ith_var, nith_var};
use crate::knowledge_compilation::bdd::bdd_kernel::BddKernel;
#[test]
fn test_simple_methods() {
let mut kernel = BddKernel::new_with_num_vars(3, 1000, 1000);
assert_eq!(ith_var(0, &mut kernel), 2);
assert_eq!(nith_var(0, &mut kernel), 3);
assert_eq!(bdd_var(2, &mut kernel), 0);
assert_eq!(bdd_low(2, &mut kernel), 0);
assert_eq!(bdd_high(2, &mut kernel), 1);
}
}