use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;
use std::cell::RefCell;
extern crate num_cpus;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use bincode;
use base::{Base};
use io;
use reg::Reg;
use {vhl, vhl::{HiLo, HiLoPart, HiLoBase, VHLParts, Walkable}};
use nid::{NID,O,I};
use vid::{VID,VidOrdering,topmost_of3};
use cur::{Cursor, CursorPlan};
use {wip, wip::{QID,Dep,WIP,WorkState}};
pub type BDDHashMap<K,V> = vhl::VHLHashMap<K,V>;
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Clone, Copy)]
pub struct ITE {i:NID, t:NID, e:NID}
impl ITE {
pub fn new (i:NID, t:NID, e:NID)-> ITE { ITE { i, t, e } }
pub fn top_vid(&self)->VID {
let (i,t,e) = (self.i.vid(), self.t.vid(), self.e.vid());
topmost_of3(i,t,e) }}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Norm {
Nid(NID),
Ite(ITE),
Not(ITE)}
impl ITE {
pub fn norm(f0:NID, g0:NID, h0:NID)->Norm {
let mut f = f0; let mut g = g0; let mut h = h0;
loop {
if f.is_const() { return Norm::Nid(if f==I { g } else { h }) } if g==h { return Norm::Nid(g) } if g==f { if h.is_const() {
return Norm::Nid(if h==I { I } else { f }) } else { g=I }}
else if g.is_const() && h.is_const() { return if g==I { return Norm::Nid(f) } else { Norm::Nid(!f) }}
else {
let nf = !f;
if g==nf { g=O }
else if h==f { h=O }
else if h==nf { h=I }
else {
let (fv, fi) = (f.vid(), f.idx());
macro_rules! cmp { ($x0:expr,$x1:expr) => {
{ let x0=$x0; ((x0.is_above(&fv)) || ((x0==fv) && ($x1<fi))) }}}
if g.is_const() && cmp!(h.vid(),h.idx()) {
if g==I { g = f; f = h; h = g; g = I; }
else { f = !h; g = O; h = nf; }}
else if h.is_const() && cmp!(g.vid(),g.idx()) {
if h==I { f = !g; g = nf; h = I; }
else { h = f; f = g; g = h; h = O; }}
else {
let ng = !g;
if (h==ng) && cmp!(g.vid(), g.idx()) { h=f; f=g; g=h; h=nf; }
else if f.is_inv() { f=g; g=h; h=f; f=nf; }
else if g.is_inv() { return match ITE::norm(f,ng,!h) {
Norm::Nid(nid) => Norm::Nid(!nid),
Norm::Not(ite) => Norm::Ite(ite),
Norm::Ite(ite) => Norm::Not(ite)}}
else { return Norm::Ite(ITE::new(f,g,h)) }}}}}} }
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BddState {
nvars: usize,
hilos: vhl::HiLoCache,
xmemo: BDDHashMap<ITE, NID> }
thread_local!{
pub static COUNT_XMEMO_TEST: RefCell<u64> = RefCell::new(0);
pub static COUNT_XMEMO_FAIL: RefCell<u64> = RefCell::new(0); }
impl BddState {
#[inline] fn tup(&self, n:NID)-> (NID, NID) {
if n.is_const() { if n==I { (I, O) } else { (O, I) } }
else if n.is_var() { if n.is_inv() { (O, I) } else { (I, O) }}
else { let hilo = self.get_hilo(n); (hilo.hi, hilo.lo) }}
#[inline] fn simple_node(&mut self, v:VID, hilo:HiLo)->NID {
match self.get_simple_node(v, hilo) {
Some(n) => n,
None => { self.put_simple_node(v, hilo) }}}
fn new(nvars:usize)->BddState {
BddState {
nvars,
hilos: vhl::HiLoCache::new(),
xmemo: BDDHashMap::default() }}
#[inline] fn put_xmemo(&mut self, ite:ITE, new_nid:NID) {
self.xmemo.insert(ite, new_nid); }
#[inline] fn get_memo(&self, ite:&ITE) -> Option<NID> {
if ite.i.is_var() {
debug_assert!(!ite.i.is_inv()); let hilo = if ite.i.is_inv() { HiLo::new(ite.e,ite.t) } else { HiLo::new(ite.t,ite.e) };
self.get_simple_node(ite.i.vid(), hilo) }
else {
COUNT_XMEMO_TEST.with(|c| *c.borrow_mut() += 1 );
let test = self.xmemo.get(&ite).copied();
if test == None { COUNT_XMEMO_FAIL.with(|c| *c.borrow_mut() += 1 ); }
test }}
#[inline] fn get_hilo(&self, n:NID)->HiLo {
self.hilos.get_hilo(n) }
#[inline] fn get_simple_node(&self, v:VID, hl:HiLo)-> Option<NID> {
self.hilos.get_node(v, hl)}
#[inline] fn put_simple_node(&mut self, v:VID, hl:HiLo)->NID {
self.hilos.insert(v, hl) }}
enum QMsg { Ite(QID, ITE), Cache(Arc<BddState>), Halt }
impl std::fmt::Debug for QMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
QMsg::Ite(qid, ite) => { write!(f, "QMsg::Ite(q{}, {:?})", qid, ite) }
QMsg::Cache(_) => { write!(f, "QMsg::Cache(...)") }
QMsg::Halt => { write!(f, "QMsg::Halt")} } }}
type RMsg = wip::RMsg<Norm>;
type QTx = Sender<QMsg>;
type QRx = Receiver<QMsg>;
type RTx = Sender<(QID, RMsg)>;
type RRx = Receiver<(QID, RMsg)>;
#[derive(Debug)]
pub struct BddSwarm {
rx: RRx,
me: RTx,
swarm: Vec<QTx>,
stable: Arc<BddState>,
recent: BddState,
work: WorkState<ITE>}
impl Serialize for BddSwarm {
fn serialize<S:Serializer>(&self, ser: S)->Result<S::Ok, S::Error> {
self.stable.serialize::<S>(ser) } }
impl<'de> Deserialize<'de> for BddSwarm {
fn deserialize<D:Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let mut res = Self::new(0);
res.stable = Arc::new(BddState::deserialize(d)?);
Ok(res) }}
impl BddSwarm {
fn new(nvars:usize)->Self {
let (me, rx) = channel::<(QID, RMsg)>();
let swarm = vec![];
let stable = Arc::new(BddState::new(nvars));
let recent = BddState::new(nvars);
Self{ me, rx, swarm, stable, recent, work:WorkState::new() }}
fn get_state(&self)->&BddState { &self.recent }
fn nvars(&self)->usize { self.recent.nvars }
fn tup(&self, n:NID)->(NID,NID) { self.recent.tup(n) }
fn ite(&mut self, i:NID, t:NID, e:NID)->NID { self.run_swarm(i,t,e) } }
impl BddSwarm {
fn add_task(&mut self, opt_dep:Option<Dep>, ite:ITE) {
trace!("add_task({:?}, {:?})", opt_dep, ite);
let (qid, is_dup) = {
if let Some(&dup) = self.work.qid.get(&ite) { (dup, true) }
else { (self.work.wip.len(), false) }};
if is_dup {
if let Some(dep) = opt_dep {
trace!("*** task {:?} is dup of q{} invert: {}", ite, qid, dep.invert);
if let WIP::Done(nid) = self.work.wip[qid] {
self.resolve_part(dep.qid, dep.part, nid, dep.invert); }
else { self.work.deps[qid].push(dep) }}
else { panic!("Got duplicate request, but no dep. This should never happen!") }}
else {
self.work.qid.insert(ite, qid); self.work.qs.push(ite);
let w:usize = qid % self.swarm.len();
self.swarm[w].send(QMsg::Ite(qid, ite)).expect("send to swarm failed");
self.work.wip.push(WIP::Fresh);
if let Some(dep) = opt_dep {
trace!("*** added task #{}: {:?} invert:{}", qid, ite, dep.invert);
self.work.deps.push(vec![dep]) }
else if qid == 0 {
trace!("*** added task #{}: {:?} (no deps!)", qid, ite);
self.work.deps.push(vec![]) }
else { panic!("non 0 qid with no deps!?") }}}
fn resolve_nid(&mut self, qid:QID, nid:NID) {
trace!("resolve_nid(q{}, {})", qid, nid);
if let WIP::Done(old) = self.work.wip[qid] {
warn!("resolving already resolved nid for q{}", qid);
assert_eq!(old, nid, "old and new resolutions didn't match!") }
else {
trace!("resolved_nid: q{}=>{}. deps: {:?}", qid, nid, self.work.deps[qid].clone());
self.work.wip[qid] = WIP::Done(nid);
let ite = self.work.qs[qid];
self.recent.put_xmemo(ite, nid);
for &dep in self.work.deps[qid].clone().iter() {
self.resolve_part(dep.qid, dep.part, nid, dep.invert) }
if qid == 0 { self.me.send((0, RMsg::Ret(nid))).expect("failed to send Ret"); }}}
fn resolve_vhl(&mut self, qid:QID, v:VID, hilo:HiLo, invert:bool) {
trace!("resolve_vhl(q{}, {:?}, {:?}, invert:{}", qid, v, hilo, invert);
let HiLo{hi:h0,lo:l0} = hilo;
let (h1,l1) = if invert { (!h0, !l0) } else { (h0, l0) };
let nid = match ITE::norm(NID::from_vid(v), h1, l1) {
Norm::Nid(n) => n,
Norm::Ite(ITE{i:vv,t:hi,e:lo}) => self.recent.simple_node(vv.vid(), HiLo{hi,lo}),
Norm::Not(ITE{i:vv,t:hi,e:lo}) => !self.recent.simple_node(vv.vid(), HiLo{hi,lo})};
trace!("resolved vhl: q{}=>{}. #deps: {}", qid, nid, self.work.deps[qid].len());
self.resolve_nid(qid, nid); }
fn resolve_part(&mut self, qid:QID, part:HiLoPart, nid:NID, invert:bool) {
self.work.resolve_part(qid, part, nid, invert);
if let WIP::Parts(wip) = self.work.wip[qid] {
if let Some(hilo) = wip.hilo() { self.resolve_vhl(qid, wip.v, hilo, wip.invert) }}}
fn init_swarm(&mut self) {
self.work.wip = vec![]; self.work.qs = vec![]; self.work.deps = vec![]; self.work.qid = BDDHashMap::new();
let (me, rx) = channel::<(QID, RMsg)>(); self.me = me; self.rx = rx;
self.swarm = vec![];
while self.swarm.len() < num_cpus::get() {
let (tx, rx) = channel::<QMsg>();
let me_clone = self.me.clone();
let state = self.stable.clone();
thread::spawn(move || swarm_loop(me_clone, rx, state));
self.swarm.push(tx); }
self.stable = Arc::new(self.recent.clone());
for tx in self.swarm.iter() {
tx.send(QMsg::Cache(self.stable.clone())).expect("failed to send QMsg::Cache"); }}
fn run_swarm(&mut self, i:NID, t:NID, e:NID)->NID {
macro_rules! run_swarm_ite { ($n:expr, $ite:expr) => {{
self.init_swarm(); self.add_task(None, $ite);
let mut result:Option<NID> = None;
while result.is_none() {
let (qid, rmsg) = self.rx.recv().expect("failed to read RMsg from queue!");
trace!("===> run_swarm got RMsg {}: {:?}", qid, rmsg);
match rmsg {
RMsg::MemoStats{ tests:_, fails:_ } => { panic!("got RMsg::MemoStats before sending QMsg::Halt"); }
RMsg::Nid(nid) => { self.resolve_nid(qid, nid); }
RMsg::Vhl{v,hi,lo,invert} => { self.resolve_vhl(qid, v, HiLo{hi, lo}, invert); }
RMsg::Wip{v,hi,lo,invert} => {
assert_eq!(self.work.wip[qid], WIP::Fresh);
self.work.wip[qid] = WIP::Parts(VHLParts{ v, hi:None, lo:None, invert });
macro_rules! handle_part { ($xx:ident, $part:expr) => {
match $xx {
Norm::Nid(nid) => self.resolve_part(qid, $part, nid, false),
Norm::Ite(ite) => self.add_task(Some(Dep::new(qid, $part, false)), ite),
Norm::Not(ite) => self.add_task(Some(Dep::new(qid, $part, true)), ite)}}}
handle_part!(hi, HiLoPart::HiPart); handle_part!(lo, HiLoPart::LoPart); }
RMsg::Ret(n) => {
result = Some(n);
for tx in self.swarm.iter() { tx.send(QMsg::Halt).expect("failed to send QMsg::Halt") }}}}
let (mut tests, mut fails, mut reports, mut shorts) = (0, 0, 0, 0);
while reports < self.swarm.len() {
let (_qid, rmsg) = self.rx.recv().expect("still expecting an Rmsg::MemoCount");
if let wip::RMsg::MemoStats{ tests:t, fails: f } = rmsg { reports += 1; tests+=t; fails += f }
else { shorts += 1; println!("extraneous rmsg from swarm: {:?}", rmsg) }}
if shorts > 0 { println!("----------- shorts: {}", shorts)} COUNT_XMEMO_TEST.with(|c| *c.borrow_mut() += tests );
COUNT_XMEMO_FAIL.with(|c| *c.borrow_mut() += fails );
result.unwrap() }}}
match ITE::norm(i,t,e) {
Norm::Nid(n) => n,
Norm::Ite(ite) => { run_swarm_ite!(0,ite) }
Norm::Not(ite) => { !run_swarm_ite!(0,ite) }}}
}
fn swarm_ite(state: &Arc<BddState>, ite0:ITE)->RMsg {
let ITE { i, t, e } = ite0;
match ITE::norm(i,t,e) {
Norm::Nid(n) => RMsg::Nid(n),
Norm::Ite(ite) => swarm_ite_norm(state, ite),
Norm::Not(ite) => !swarm_ite_norm(state, ite) }}
fn swarm_vhl_norm(state: &Arc<BddState>, ite:ITE)->RMsg {
let ITE{i:vv,t:hi,e:lo} = ite; let v = vv.vid();
if let Some(n) = state.get_simple_node(v, HiLo{hi,lo}) { RMsg::Nid(n) }
else { RMsg::Vhl{ v, hi, lo, invert:false } }}
fn swarm_ite_norm(state: &Arc<BddState>, ite:ITE)->RMsg {
let ITE { i, t, e } = ite;
let (vi, vt, ve) = (i.vid(), t.vid(), e.vid());
let v = ite.top_vid();
match state.get_memo(&ite) {
Some(n) => RMsg::Nid(n),
None => {
let (hi_i, lo_i) = if v == vi {state.tup(i)} else {(i,i)};
let (hi_t, lo_t) = if v == vt {state.tup(t)} else {(t,t)};
let (hi_e, lo_e) = if v == ve {state.tup(e)} else {(e,e)};
let hi = ITE::norm(hi_i, hi_t, hi_e);
let lo = ITE::norm(lo_i, lo_t, lo_e);
if let (Norm::Nid(hn), Norm::Nid(ln)) = (hi,lo) {
match ITE::norm(NID::from_vid(v), hn, ln) {
Norm::Nid(n) => { RMsg::Nid(n) }
Norm::Ite(ite) => swarm_vhl_norm(state, ite),
Norm::Not(ite) => !swarm_vhl_norm(state, ite)}}
else { RMsg::Wip{ v, hi, lo, invert:false } }}}}
fn swarm_loop(tx:RTx, rx:QRx, mut state:Arc<BddState>) {
for qmsg in rx.iter() {
match qmsg {
QMsg::Cache(s) => { state = s }
QMsg::Ite(qid, ite) => {
trace!("---> thread worker got qmsg {}: {:?}", qid, qmsg);
let rmsg = swarm_ite(&state, ite);
if tx.send((qid, rmsg)).is_err() { break } }
QMsg::Halt => {
let tests = COUNT_XMEMO_TEST.with(|c| c.replace(0));
let fails = COUNT_XMEMO_FAIL.with(|c| c.replace(0));
if tx.send((QID::MAX, RMsg::MemoStats{ tests, fails })).is_err() {
println!("failed to send memostats (tests:{} fails: {})", tests, fails)}
break; } }}}
#[derive(Debug, Serialize, Deserialize)]
pub struct BDDBase {
pub tags: HashMap<String, NID>,
swarm: BddSwarm}
impl vhl::Walkable for BDDBase {
fn step<F>(&self, n:NID, f:&mut F, seen:&mut HashSet<NID>, topdown:bool)
where F: FnMut(NID,VID,NID,NID) {
if !seen.contains(&n) {
seen.insert(n); let (hi,lo) = self.tup(n);
if topdown { f(n, n.vid(), hi,lo ) }
if !hi.is_const() { self.step(hi, f, seen, topdown) }
if !lo.is_const() { self.step(lo, f, seen, topdown) }
if !topdown { f(n, n.vid(), hi, lo) }}}}
impl BDDBase {
#[inline] fn tup(&self, n:NID)->(NID,NID) { self.swarm.tup(n) }
pub fn load(path:&str)->::std::io::Result<BDDBase> {
let s = io::get(path)?;
Ok(bincode::deserialize(&s).unwrap()) }
pub fn gt(&mut self, x:NID, y:NID)->NID { self.ite(x, !y, O) }
pub fn lt(&mut self, x:NID, y:NID)->NID { self.ite(x, O, y) }
#[inline] pub fn ite(&mut self, f:NID, g:NID, h:NID)->NID { self.swarm.ite(f,g,h) }
pub fn swap(&mut self, n:NID, x:VID, y:VID)-> NID {
if x.is_below(&y) { return self.swap(n,y,x) }
let (xlo, xhi) = (self.when_lo(x,n), self.when_hi(x,n));
let (xlo_ylo, xlo_yhi) = (self.when_lo(y,xlo), self.when_hi(y,xlo));
let (xhi_ylo, xhi_yhi) = (self.when_lo(y,xhi), self.when_hi(y,xhi));
let lo = self.ite(NID::from_vid(x), xlo_ylo, xhi_ylo);
let hi = self.ite(NID::from_vid(y), xlo_yhi, xhi_yhi);
self.ite(NID::from_vid(x), lo, hi) }
pub fn node_count(&self, n:NID)->usize {
let mut c = 0; self.walk(n, &mut |_,_,_,_| c+=1); c }
fn tt_aux(&mut self, res:&mut Vec<u8>, v:VID, n:NID, i:usize) {
let o = v.var_ix();
if o == self.num_vars() { match self.when_lo(v, n) {
O => {} I => { res[i] = 1; }
x => panic!("expected a leaf nid, got {}", x) }}
else {
let lo = self.when_lo(v,n); self.tt_aux(res, VID::var(1+o as u32), lo, i*2);
let hi = self.when_hi(v,n); self.tt_aux(res, VID::var(1+o as u32), hi, i*2+1); }}
pub fn tt(&mut self, n0:NID)->Vec<u8> {
if !n0.vid().is_var() { todo!("tt only works for actual variables. got {:?}", n0); }
if self.num_vars() > 16 {
panic!("refusing to generate a truth table of 2^{} bytes", self.num_vars()) }
let mut res = vec![0;(1 << self.num_vars()) as usize];
self.tt_aux(&mut res, VID::var(0), n0, 0);
res }
}
impl Base for BDDBase {
fn new(nvars:usize)->BDDBase {
BDDBase{swarm: BddSwarm::new(nvars), tags:HashMap::new()}}
fn num_vars(&self)->usize { self.swarm.nvars() }
fn when_hi(&mut self, x:VID, y:NID)->NID {
let yv = y.vid();
match x.cmp_depth(&yv) {
VidOrdering::Level => self.tup(y).0, VidOrdering::Above => y, VidOrdering::Below => { let (yt, ye) = self.tup(y);
let (th,el) = (self.when_hi(x,yt), self.when_hi(x,ye));
self.ite(NID::from_vid(yv), th, el) }}}
fn when_lo(&mut self, x:VID, y:NID)->NID {
let yv = y.vid();
match x.cmp_depth(&yv) {
VidOrdering::Level => self.tup(y).1, VidOrdering::Above => y, VidOrdering::Below => { let (yt, ye) = self.tup(y);
let (th,el) = (self.when_lo(x,yt), self.when_lo(x,ye));
self.ite(NID::from_vid(yv), th, el) }}}
fn def(&mut self, _s:String, _i:VID)->NID { todo!("BDDBase::def()") }
fn tag(&mut self, n:NID, s:String)->NID { self.tags.insert(s, n); n }
fn get(&self, s:&str)->Option<NID> { Some(*self.tags.get(s)?) }
fn and(&mut self, x:NID, y:NID)->NID { self.ite(x, y, O) }
fn xor(&mut self, x:NID, y:NID)->NID { self.ite(x, !y, y) }
fn or(&mut self, x:NID, y:NID)->NID { self.ite(x, I, y) }
#[cfg(todo)] fn mj(&mut self, x:NID, y:NID, z:NID)->NID { self.xor(x, self.xor(y, z)) } #[cfg(todo)] fn ch(&mut self, x:NID, y:NID, z:NID)->NID { self.ite(x, y, z) }
fn sub(&mut self, v:VID, n:NID, ctx:NID)->NID {
if ctx.might_depend_on(v) {
let (zt,ze) = self.tup(ctx); let zv = ctx.vid();
if v==zv { self.ite(n, zt, ze) }
else {
let th = self.sub(v, n, zt);
let el = self.sub(v, n, ze);
self.ite(NID::from_vid(zv), th, el) }}
else { ctx }}
fn save(&self, path:&str)->::std::io::Result<()> {
let s = bincode::serialize(&self).unwrap();
io::put(path, &s) }
fn dot(&self, n:NID, wr: &mut dyn std::fmt::Write) {
macro_rules! w { ($x:expr $(,$xs:expr)*) => { writeln!(wr, $x $(,$xs)*).unwrap(); }}
w!("digraph bdd {{");
w!("subgraph head {{ h1[shape=plaintext; label=\"BDD\"] }}");
w!(" I[label=⊤; shape=square];");
w!(" O[label=⊥; shape=square];");
w!("node[shape=circle];");
self.walk(n, &mut |n,_,_,_| w!(" \"{}\"[label=\"{}\"];", n, n.vid()));
w!("edge[style=solid];");
self.walk(n, &mut |n,_,t,_| w!(" \"{}\"->\"{}\";", n, t));
w!("edge[style=dashed];");
self.walk(n, &mut |n,_,_,e| w!(" \"{}\"->\"{}\";", n, e));
w!("}}"); }
fn solution_set(&self, n: NID, nvars: usize)->hashbrown::HashSet<Reg> {
self.solutions_trunc(n, nvars).collect() }}
test_base_consts!(BDDBase);
test_base_when!(BDDBase);
#[test] fn test_base() {
let mut base = BDDBase::new(3);
let (v1, v2, v3) = (NID::var(1), NID::var(2), NID::var(3));
assert_eq!(base.num_vars(), 3);
assert_eq!((I,O), base.tup(I));
assert_eq!((O,I), base.tup(O));
assert_eq!((I,O), base.tup(v1));
assert_eq!((I,O), base.tup(v2));
assert_eq!((I,O), base.tup(v3));
assert_eq!(I, base.when_hi(VID::var(3),v3));
assert_eq!(O, base.when_lo(VID::var(3),v3))}
#[test] fn test_and() {
let mut base = BDDBase::new(3);
let (v1, v2) = (NID::var(1), NID::var(2));
let a = base.and(v1, v2);
assert_eq!(O, base.when_lo(VID::var(1),a));
assert_eq!(v2, base.when_hi(VID::var(1),a));
assert_eq!(O, base.when_lo(VID::var(2),a));
assert_eq!(v1, base.when_hi(VID::var(2),a));
assert_eq!(a, base.when_hi(VID::var(3),a));
assert_eq!(a, base.when_lo(VID::var(3),a))}
#[test] fn test_xor() {
let mut base = BDDBase::new(3);
let (v1, v2) = (NID::var(1), NID::var(2));
let x = base.xor(v1, v2);
assert_eq!(v2, base.when_lo(VID::var(1),x));
assert_eq!(!v2, base.when_hi(VID::var(1),x));
assert_eq!(v1, base.when_lo(VID::var(2),x));
assert_eq!(!v1, base.when_hi(VID::var(2),x));
assert_eq!(x, base.when_lo(VID::var(3),x));
assert_eq!(x, base.when_hi(VID::var(3),x))}
#[test] fn test_swarm_xor() {
let mut base = BDDBase::new(2);
let (x0, x1) = (NID::var(0), NID::var(1));
let x = base.xor(x0, x1);
assert_eq!(x1, base.when_lo(VID::var(0),x));
assert_eq!(!x1, base.when_hi(VID::var(0),x));
assert_eq!(x0, base.when_lo(VID::var(1),x));
assert_eq!(!x0, base.when_hi(VID::var(1),x));
assert_eq!(x, base.when_lo(VID::var(2),x));
assert_eq!(x, base.when_hi(VID::var(2),x))}
#[test] fn test_swarm_and() {
let mut base = BDDBase::new(2);
let (x0, x1) = (NID::var(0), NID::var(1));
let a = base.and(x0, x1);
assert_eq!(O, base.when_lo(VID::var(0),a));
assert_eq!(x1, base.when_hi(VID::var(0),a));
assert_eq!(O, base.when_lo(VID::var(1),a));
assert_eq!(x0, base.when_hi(VID::var(1),a));
assert_eq!(a, base.when_hi(VID::var(2),a));
assert_eq!(a, base.when_lo(VID::var(2),a))}
#[test] fn test_swarm_ite() {
let mut base = BDDBase::new(3);
let (x0,x1,x2) = (NID::var(0), NID::var(1), NID::var(2));
assert_eq!(vec![0,0,0,0,1,1,1,1], base.tt(x0));
assert_eq!(vec![0,0,1,1,0,0,1,1], base.tt(x1));
assert_eq!(vec![0,1,0,1,0,1,0,1], base.tt(x2));
let x = base.xor(x0, x1);
assert_eq!(vec![0,0,1,1,1,1,0,0], base.tt(x));
let a = base.and(x1, x2);
assert_eq!(vec![0,0,0,1,0,0,0,1], base.tt(a));
let i = base.ite(x, a, !a);
assert_eq!(vec![1,1,0,1,0,0,1,0], base.tt(i))}
#[test] fn test_swarm_another() {
use simplelog::*; TermLogger::init(LevelFilter::Trace, Config::default()).unwrap();
let mut base = BDDBase::new(4);
let (a,b) = (NID::var(0), NID::var(1));
let anb = base.and(a,!b);
assert_eq!(vec![0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0], base.tt(anb));
let anb_nb = base.xor(anb,!b);
assert_eq!(vec![1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], base.tt(anb_nb));
let anb2 = base.xor(!b, anb_nb);
assert_eq!(vec![0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0], base.tt(anb2));
assert_eq!(anb, anb2)}
use std::iter::FromIterator; use std::hash::Hash;
pub fn hs<T: Eq+Hash>(xs: Vec<T>)->HashSet<T> { <HashSet<T>>::from_iter(xs) }
#[test] fn test_bdd_solutions_o() {
let mut base = BDDBase::new(2); let mut it = base.solutions(O);
assert_eq!(it.next(), None, "const O should yield no solutions.") }
#[test] fn test_bdd_solutions_i() {
let mut base = BDDBase::new(2);
let actual:HashSet<usize> = base.solutions(I).map(|r| r.as_usize()).collect();
assert_eq!(actual, hs(vec![0b00, 0b01, 0b10, 0b11]),
"const true should yield all solutions"); }
#[test] fn test_bdd_solutions_simple() {
let mut base = BDDBase::new(1); let a = NID::var(0);
let mut it = base.solutions(a);
assert_eq!(it.next().expect("expected solution!").as_usize(), 0b1);
assert_eq!(it.next(), None);}
#[test] fn test_bdd_solutions_extra() {
let mut base = BDDBase::new(5);
let (b, d) = (NID::var(1), NID::var(3));
let n = base.and(b,d);
let actual:Vec<_> = base.solutions(n).map(|r| r.as_usize()).collect();
assert_eq!(actual, vec![0b01010,
0b01011,
0b01110,
0b01111,
0b11010,
0b11011,
0b11110,
0b11111])}
#[test] fn test_bdd_solutions_xor() {
let mut base = BDDBase::new(3);
let (a, b) = (NID::var(0), NID::var(1));
let n = base.xor(a, b);
let actual:Vec<usize> = base.solutions(n).map(|x|x.as_usize()).collect();
let expect = vec![0b001, 0b010, 0b101, 0b110 ]; assert_eq!(actual, expect); }
impl BDDBase {
pub fn solutions(&mut self, n:NID)->BDDSolIterator {
self.solutions_trunc(n, self.num_vars())}
pub fn solutions_trunc(&self, n:NID, nvars:usize)->BDDSolIterator {
assert!(nvars <= self.num_vars(), "nvars arg to solutions_trunc must be <= self.num_vars");
BDDSolIterator::from_bdd(self, n, nvars)}}
impl HiLoBase for BDDBase {
fn get_hilo(&self, n:NID)->Option<HiLo> {
let (hi, lo) = self.swarm.get_state().tup(n);
Some(HiLo{ hi, lo }) }}
impl CursorPlan for BDDBase {}
impl BDDBase {
pub fn first_solution(&self, n:NID, nvars:usize)->Option<Cursor> {
if n== O || nvars == 0 { None }
else {
let mut cur = Cursor::new(nvars, n);
cur.descend(self);
debug_assert!(cur.node.is_const());
debug_assert!(self.in_solution(&cur), "{:?}", cur.scope);
Some(cur) }}
pub fn next_solution(&self, cur:Cursor)->Option<Cursor> {
self.log(&cur, "advance>"); self.log_indent(1);
let res = self.advance0(cur); self.log_indent(-1);
res }
pub fn in_solution(&self, cur:&Cursor)->bool {
self.includes_leaf(cur.node) }
fn log_indent(&self, _d:i8) { }
fn log(&self, _c:&Cursor, _msg: &str) {
#[cfg(test)]{
print!(" {}", if _c.invert { '¬' } else { ' ' });
print!("{:>10}", format!("{}", _c.node));
print!(" {:?}{}", _c.scope, if self.in_solution(&_c) { '.' } else { ' ' });
let s = format!("{}", _msg,);
println!(" {:50} {:?}", s, _c.nstack);}}
fn find_next_leaf(&self, cur:&mut Cursor)->Option<NID> {
self.log(cur, "find_next_leaf"); self.log_indent(1);
let res = self.find_next_leaf0(cur);
self.log(cur, format!("^ next leaf: {:?}", res.clone()).as_str());
self.log_indent(-1); res }
fn find_next_leaf0(&self, cur:&mut Cursor)->Option<NID> {
assert!(cur.node.is_const(), "find_next_leaf should always start by looking at a leaf");
if cur.nstack.is_empty() { assert!(cur.node == I); return None }
cur.step_up();
let tv = cur.node.vid(); let mut rippled = false;
if cur.scope.var_get(tv) {
cur.go_next_lo_var();
{ let iv = cur.node.vid();
if cur.nstack.is_empty() && cur.scope.var_get(iv) {
let top = cur.nvars-1;
if let Some(x) = cur.scope.ripple(iv.var_ix(), top) {
rippled = true;
self.log(cur, format!("rippled top to {}. restarting.", x).as_str()); }
else { self.log(cur, "no next leaf!"); return None }}} }
if rippled { cur.clear_trailing_bits() }
else if cur.var_get() { self.log(cur, "done with node."); return None }
else { cur.put_step(self, true); }
cur.descend(self);
Some(cur.node) }
fn advance0(&self, mut cur:Cursor)->Option<Cursor> {
assert!(cur.node.is_const(), "advance should always start by looking at a leaf");
if self.in_solution(&cur) {
if let Some(zpos) = cur.increment() {
self.log(&cur, format!("rebranch on {:?}",zpos).as_str());
if cur.node.is_const() { return Some(cur) } cur.put_step(self, cur.var_get());
cur.descend(self); }
else { self.log(&cur, "$ found all solutions!"); return None }}
while !self.in_solution(&cur) { self.find_next_leaf(&mut cur)?; }
Some(cur) }
}
pub struct BDDSolIterator<'a> {
bdd: &'a BDDBase,
next: Option<Cursor>}
impl<'a> BDDSolIterator<'a> {
pub fn from_bdd(bdd: &'a BDDBase, n:NID, nvars:usize)->BDDSolIterator<'a> {
let next = bdd.first_solution(n, nvars);
BDDSolIterator{ bdd, next }}}
impl<'a> Iterator for BDDSolIterator<'a> {
type Item = Reg;
fn next(&mut self)->Option<Self::Item> {
if let Some(cur) = self.next.take() {
assert!(self.bdd.in_solution(&cur));
let result = cur.scope.clone();
self.next = self.bdd.next_solution(cur);
Some(result)}
else { None }}}
#[test] fn test_simple_nodes() {
let mut state = BddState::new(8);
let hl = HiLo::new(NID::var(5), NID::var(6));
let x0 = VID::var(0);
let v0 = VID::vir(0);
let v1 = VID::vir(1);
assert!(state.get_simple_node(v0, hl).is_none());
let nv0 = state.put_simple_node(v0, hl);
assert_eq!(nv0, NID::from_vid_idx(v0, 0));
let nv1 = state.put_simple_node(v1, hl);
assert_eq!(nv1, NID::from_vid_idx(v1, 0));
let nx0 = state.put_simple_node(x0, hl);
assert_eq!(nx0, NID::from_vid_idx(x0, 0));
}