use std::clone::Clone;
use std::cmp::min;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
use std::fs::File;
use std::io::Write;
use std::marker::PhantomData;
use std::process::Command; use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;
use serde::Serialize;
use bincode;
use io;
pub type VID = u32;
pub type IDX = u32;
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Serialize, Deserialize)]
pub struct BDDNode { pub v:VID, pub hi:NID, pub lo:NID }
#[derive(PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
pub struct NID { n: u64 }
const INV:u64 = 1<<63;
const VAR:u64 = 1<<62;
const RVAR:u64 = 1<<60;
const T:u64 = 1<<61;
const IDX_MASK:u64 = (1<<32)-1;
pub const O:NID = NID{ n:T };
pub const I:NID = NID{ n:(T|INV) };
#[inline(always)] pub fn is_var(x:NID)->bool { (x.n & VAR) != 0 }
#[inline(always)] pub fn is_rvar(x:NID)->bool { (x.n & RVAR) != 0 }
#[inline(always)] pub fn is_inv(x:NID)->bool { (x.n & INV) != 0 }
#[inline(always)] pub fn is_const(x:NID)->bool { (x.n & T) != 0 }
#[inline(always)] pub fn idx(x:NID)->usize { (x.n & IDX_MASK) as usize }
#[inline(always)] pub fn var(x:NID)->VID { ((x.n & !(INV|VAR)) >> 32) as VID}
#[inline(always)] pub fn rvar(x:NID)->VID { ((x.n & !(INV|VAR|RVAR)) >> 32) as VID}
#[inline(always)] fn rv(v:VID)->usize { (v&!((RVAR>>32) as VID)) as usize}
#[inline(always)] pub fn not(x:NID)->NID { NID { n:x.n^INV } }
#[inline(always)] pub fn nv(v:VID)->NID { NID { n:((v as u64) << 32)|VAR }}
#[inline(always)] pub fn nvr(v:VID)->NID { NID { n:((v as u64) << 32)|VAR|RVAR }}
#[inline(always)] pub fn nvi(v:VID,i:IDX)->NID { NID{ n:((v as u64) << 32) + i as u64 }}
impl fmt::Display for NID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if is_const(*self) { if is_inv(*self) { write!(f, "I") } else { write!(f, "O") } }
else { if is_inv(*self) { write!(f, "¬")?; }
if is_var(*self) {
if is_rvar(*self) { write!(f, "x{}", rvar(*self)) }
else { write!(f, "v{}", var(*self)) }}
else if is_rvar(*self) { write!(f, "@[x{}:{}]", rvar(*self), idx(*self)) }
else { write!(f, "@[v{}:{}]", var(*self), idx(*self)) }}}}
impl fmt::Debug for NID { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self) }}
#[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:i, t:t, e:e } }
pub fn min_var(&self)->VID { return min(var(self.i), min(var(self.t), var(self.e))) }
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Norm {
Nid(NID),
Ite(ITE),
Not(ITE)}
pub type BDDHashMap<K,V> = hashbrown::hash_map::HashMap<K,V>;
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 is_const(f) { return Norm::Nid(if f==I { g } else { h }) } if g==h { return Norm::Nid(g) } if g==f { if is_const(h) { return Norm::Nid(if h==I { I } else { f }) } else { g=I }}
else if T==(T & g.n & h.n) { return if g==I { return Norm::Nid(f) } else { Norm::Nid(not(f)) }}
else {
let nf = not(f);
if g==nf { g=O } else if h==f { h=O } else if h==nf { h=I } else {
let (fv, fi) = (var(f), idx(f));
macro_rules! cmp { ($x0:expr,$x1:expr) => {
{ let x0=$x0; ((x0<fv) || ((x0==fv) && ($x1<fi))) }}}
if is_const(g) && cmp!(var(h),idx(h)) {
if g==I { g=f; f=h; h=g; g=I; } else { f=not(h); g=O; h=nf; }} else if is_const(h) && cmp!(var(g),idx(g)) {
if h==I { f=not(g); g=nf; h=I; } else { h=f; f=g; g=h; h=O; }} else {
let ng = not(g);
if (h==ng) && cmp!(var(g), idx(g)) { h=f; f=g; g=h; h=nf; } else if is_inv(f) { f=g; g=h; h=f; f=nf; } else if is_inv(g) { return match ITE::norm(f,ng,not(h)) {
Norm::Nid(nid) => Norm::Nid(not(nid)),
Norm::Not(ite) => Norm::Ite(ite),
Norm::Ite(ite) => Norm::Not(ite)}}
else { return Norm::Ite(ITE::new(f,g,h)) }}}}}} }
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Serialize, Deserialize)]
pub struct HILO {hi:NID, lo:NID}
impl HILO {
fn new(hi:NID, lo:NID)->HILO { HILO { hi:hi, lo:lo } }
#[inline] fn invert(self)-> HILO { HILO{ hi: not(self.hi), lo: not(self.lo) }} }
pub trait BddState : Sized + Serialize + Clone + Sync + Send {
fn new(nvars: usize)->Self;
#[inline] fn tup(&self, n:NID)-> (NID, NID) {
if is_const(n) { if n==I { (I, O) } else { (O, I) } }
else if is_var(n) { if is_inv(n) { (O, I) } else { (I, O) }}
else { let mut hilo = self.get_hilo(n);
if is_inv(n) { hilo = hilo.invert() };
(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 nvars(&self)->usize;
#[inline] fn get_hilo(&self, n:NID)->HILO;
#[inline] fn get_memo<'a>(&'a self, v:VID, ite:&ITE) -> Option<&'a NID>;
#[inline] fn put_xmemo(&mut self, ite:ITE, new_nid:NID);
#[inline] fn get_simple_node<'a>(&'a self, v:VID, hilo:HILO)-> Option<&'a NID>;
#[inline] fn put_simple_node(&mut self, v:VID, hilo:HILO)->NID; }
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SafeVarKeyedBddState {
nodes: Vec<Vec<HILO>>,
vmemo: Vec<BDDHashMap<HILO,NID>>,
xmemo: Vec<BDDHashMap<ITE, NID>> }
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UnsafeVarKeyedBddState {
nodes: Vec<Vec<HILO>>,
vmemo: Vec<BDDHashMap<HILO,NID>>,
xmemo: Vec<BDDHashMap<ITE, NID>> }
impl BddState for SafeVarKeyedBddState {
fn new(nvars:usize)->SafeVarKeyedBddState {
SafeVarKeyedBddState{
nodes: (0..nvars).map(|_| vec![]).collect(),
vmemo:(0..nvars).map(|_| BDDHashMap::default()).collect(),
xmemo:(0..nvars).map(|_| BDDHashMap::default()).collect() }}
fn nvars(&self)->usize { self.nodes.len() }
#[inline] fn get_hilo(&self, n:NID)->HILO {
self.nodes[rv(var(n))][idx(n)] }
#[inline] fn get_memo<'a>(&'a self, v:VID, ite:&ITE) -> Option<&'a NID> {
if is_var(ite.i) {
self.vmemo[rvar(ite.i) as usize].get(&HILO::new(ite.t,ite.e)) }
else { self.xmemo.as_slice().get(rv(v))?.get(&ite) }}
#[inline] fn put_xmemo(&mut self, ite:ITE, new_nid:NID) {
let v = ite.min_var();
self.xmemo[rv(v)].insert(ite, new_nid); }
#[inline] fn get_simple_node<'a>(&'a self, v:VID, hilo:HILO)-> Option<&'a NID> {
self.vmemo[rv(v)].get(&hilo) }
#[inline] fn put_simple_node(&mut self, v:VID, hilo:HILO)->NID {
let ref mut vnodes = self.nodes[rv(v)];
let res = nvi(v, vnodes.len() as IDX);
vnodes.push(hilo);
self.vmemo[rv(v) as usize].insert(hilo,res);
res } }
impl BddState for UnsafeVarKeyedBddState {
fn new(nvars:usize)->UnsafeVarKeyedBddState {
UnsafeVarKeyedBddState{
nodes: (0..nvars).map(|_| vec![]).collect(),
vmemo:(0..nvars).map(|_| BDDHashMap::default()).collect(),
xmemo:(0..nvars).map(|_| BDDHashMap::default()).collect() }}
fn nvars(&self)->usize { self.nodes.len() }
#[inline] fn get_hilo(&self, n:NID)->HILO {
unsafe { let bits = self.nodes.as_slice().get_unchecked(rv(var(n))).as_slice();
*bits.get_unchecked(idx(n)) }}
#[inline] fn get_memo<'a>(&'a self, v:VID, ite:&ITE) -> Option<&'a NID> {
unsafe { if is_var(ite.i) {
self.vmemo.as_slice().get_unchecked(rv(rvar(ite.i))).get(&HILO::new(ite.t,ite.e)) }
else { self.xmemo.as_slice().get_unchecked(rv(v)).get(&ite) }}}
#[inline] fn put_xmemo(&mut self, ite:ITE, new_nid:NID) { unsafe {
let v = ite.min_var();
self.xmemo.as_mut_slice().get_unchecked_mut(rv(v)).insert(ite, new_nid); }}
#[inline] fn get_simple_node<'a>(&'a self, v:VID, hilo:HILO)-> Option<&'a NID> {
unsafe { self.vmemo.as_slice().get_unchecked(rv(v)).get(&hilo) }}
#[inline] fn put_simple_node(&mut self, v:VID, hilo:HILO)->NID {
unsafe {
let vnodes = self.nodes.as_mut_slice().get_unchecked_mut(rv(v));
let res = nvi(v, vnodes.len() as IDX);
vnodes.push(hilo);
self.vmemo.as_mut_slice().get_unchecked_mut(rv(v) as usize).insert(hilo,res);
res }} }
pub trait BddWorker<S:BddState> : Sized + Serialize {
fn new(nvars:usize)->Self;
fn new_with_state(state: S)->Self;
fn nvars(&self)->usize;
fn tup(&self, n:NID)->(NID,NID);
fn ite(&mut self, f:NID, g:NID, h:NID)->NID;
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SimpleBddWorker<S:BddState> { state:S }
impl<S:BddState> BddWorker<S> for SimpleBddWorker<S> {
fn new(nvars:usize)->Self { Self{ state: S::new(nvars) }}
fn new_with_state(state: S)->Self { Self{ state }}
fn nvars(&self)->usize { self.state.nvars() }
fn tup(&self, n:NID)->(NID,NID) { self.state.tup(n) }
fn ite(&mut self, f:NID, g:NID, h:NID)->NID {
match ITE::norm(f,g,h) {
Norm::Nid(x) => x,
Norm::Ite(ite) => self.ite_norm(ite),
Norm::Not(ite) => not(self.ite_norm(ite)) }} }
impl<S:BddState> SimpleBddWorker<S> {
#[inline] fn ite_norm(&mut self, ite:ITE)->NID {
let ITE { i, t, e } = ite;
let (vi, vt, ve) = (var(i), var(t), var(e));
let v = min(vi, min(vt, ve));
match self.state.get_memo(v, &ite) {
Some(&n) => n,
None => {
let (hi_i, lo_i) = if v == vi {self.tup(i)} else {(i,i)};
let (hi_t, lo_t) = if v == vt {self.tup(t)} else {(t,t)};
let (hi_e, lo_e) = if v == ve {self.tup(e)} else {(e,e)};
let new_nid = {
let hi = self.ite(hi_i, hi_t, hi_e);
let lo = self.ite(lo_i, lo_t, lo_e);
if hi == lo {hi} else { self.state.simple_node(v, HILO::new(hi,lo)) }};
if !is_var(i) { self.state.put_xmemo(ite, new_nid) }
new_nid }}} }
type QID = usize;
#[derive(PartialEq)]
enum QMsg<S:BddState> { Ite(QID, ITE), Cache(Arc<S>) }
impl<S:BddState> std::fmt::Debug for QMsg<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
QMsg::Ite(qid, ite) => { write!(f, "Ite(q{}, {:?})", qid, ite) }
QMsg::Cache(_) => { write!(f, "QMsg::Cache") } } }}
#[derive(PartialEq,Debug)]
enum RMsg {
Nid(NID),
Vhl{v:VID, hi:NID, lo:NID, invert:bool},
Wip{v:VID, hi:Norm, lo:Norm, invert:bool},
Ret(NID)}
fn rmsg_not(rmsg:RMsg)->RMsg {
match rmsg {
RMsg::Nid(n) => RMsg::Nid(not(n)),
RMsg::Vhl{v,hi,lo,invert} => RMsg::Vhl{v,hi,lo,invert:!invert},
RMsg::Wip{v,hi,lo,invert} => RMsg::Wip{v,hi,lo,invert:!invert},
RMsg::Ret(n) => RMsg::Ret(not(n))}}
type QTx<S> = Sender<QMsg<S>>;
type QRx<S> = Receiver<QMsg<S>>;
type RTx = Sender<(QID, RMsg)>;
type RRx = Receiver<(QID, RMsg)>;
#[derive(PartialEq,Debug,Copy,Clone)]
enum BddPart { HiPart, LoPart }
#[derive(PartialEq,Debug,Copy,Clone)]
struct BddParts{ v:VID, hi:Option<NID>, lo:Option<NID>, invert:bool}
impl BddParts {
fn hilo(&self)->Option<HILO> {
if let (Some(hi), Some(lo)) = (self.hi, self.lo) { Some(HILO{hi,lo}) } else { None }}}
#[derive(PartialEq,Debug,Copy,Clone)]
enum BddWIP { Fresh, Done(NID), Parts(BddParts) }
#[derive(Debug,Copy,Clone)]
struct BddDep { qid: QID, part: BddPart, invert: bool }
impl BddDep{
fn new(qid: QID, part: BddPart, invert: bool)->BddDep { BddDep{qid, part, invert} }}
#[derive(Debug)]
pub struct BddSwarm <S:BddState+'static> {
rx: RRx,
me: RTx,
swarm: Vec<QTx<S>>,
stable: Arc<S>,
recent: S,
ites: Vec<ITE>,
wip:Vec<BddWIP>,
qid: BDDHashMap<ITE, QID>,
deps: Vec<Vec<BddDep>> }
use serde::ser::{Serializer};
impl<TState:BddState> Serialize for BddSwarm<TState> {
fn serialize<S:Serializer>(&self, ser: S)->Result<S::Ok, S::Error> {
self.stable.serialize::<S>(ser) } }
impl<S:BddState> BddWorker<S> for BddSwarm<S> {
fn new(nvars:usize)->Self {
let (me, rx) = channel::<(QID, RMsg)>();
let swarm = vec![];
let stable = Arc::new(S::new(nvars));
let recent = S::new(nvars);
Self{ me, rx, swarm, stable, recent,
ites:vec![], deps:vec![], wip:vec![], qid:BDDHashMap::new() }}
fn new_with_state(state: S)->Self {
println!("warning: new_with_state probably doesn't work so well yet..."); let mut res = Self::new(state.nvars());
res.stable = Arc::new(state.clone());
res.recent = state.clone();
res }
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<S:BddState> BddSwarm<S> {
fn add_task(&mut self, opt_dep:Option<BddDep>, ite:ITE) {
trace!("add_task({:?}, {:?})", opt_dep, ite);
let (qid, is_dup) = {
if let Some(&dup) = self.qid.get(&ite) { (dup, true) }
else { (self.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 BddWIP::Done(nid) = self.wip[qid] {
self.resolve_part(dep.qid, dep.part, nid, dep.invert); }
else { self.deps[qid].push(dep) }}
else { panic!("Got duplicate request, but no dep. This should never happen!") }}
else {
self.qid.insert(ite, qid); self.ites.push(ite);
let w:usize = qid % self.swarm.len();
self.swarm[w].send(QMsg::Ite(qid, ite)).expect("send to swarm failed");
self.wip.push(BddWIP::Fresh);
if let Some(dep) = opt_dep {
trace!("*** added task #{}: {:?} invert:{}", qid, ite, dep.invert);
self.deps.push(vec![dep]) }
else {
if qid == 0 {
trace!("*** added task #{}: {:?} (no deps!)", qid, ite);
self.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 BddWIP::Done(old) = self.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.deps[qid].clone());
self.wip[qid] = BddWIP::Done(nid);
let ite = self.ites[qid];
self.recent.put_xmemo(ite, nid);
for &dep in self.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{}, v{}, {:?}, invert:{}", qid, v, hilo, invert);
let HILO{hi:h0,lo:l0} = hilo;
let (h1,l1) = if invert { (not(h0), not(l0)) } else { (h0, l0) };
let nid = match ITE::norm(nv(v), h1, l1) {
Norm::Nid(n) => n,
Norm::Ite(ITE{i:vv,t:hi,e:lo}) => self.recent.simple_node(var(vv), HILO{hi,lo}),
Norm::Not(ITE{i:vv,t:hi,e:lo}) => not(self.recent.simple_node(var(vv), HILO{hi,lo})) };
trace!("resolved vhl: q{}=>{}. #deps: {}", qid, nid, self.deps[qid].len());
self.resolve_nid(qid, nid); }
fn resolve_part(&mut self, qid:QID, part:BddPart, nid0:NID, invert:bool) {
if let BddWIP::Parts(ref mut parts) = self.wip[qid] {
let nid = if invert { not(nid0) } else { nid0 };
trace!(" !! set {:?} for q{} to {}", part, qid, nid);
if part == BddPart::HiPart { parts.hi = Some(nid) } else { parts.lo = Some(nid) }}
else { warn!("???? got a part for a qid #{} that was already done!", qid) }
if let BddWIP::Parts(wip) = self.wip[qid] {
if let Some(hilo) = wip.hilo() { self.resolve_vhl(qid, wip.v, hilo, wip.invert) }}}
fn init_swarm(&mut self) {
self.wip = vec![]; self.ites = vec![]; self.deps = vec![]; self.qid = BDDHashMap::new();
let (me, rx) = channel::<(QID, RMsg)>(); self.me = me; self.rx = rx;
self.swarm = vec![];
while self.swarm.len() < 2 { let (tx, rx) = channel::<QMsg<S>>();
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 { ($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::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.wip[qid], BddWIP::Fresh);
self.wip[qid] = BddWIP::Parts(BddParts{ 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(BddDep::new(qid, $part, false)), ite),
Norm::Not(ite) => self.add_task(Some(BddDep::new(qid, $part, true)), ite)}}}
handle_part!(hi, BddPart::HiPart); handle_part!(lo, BddPart::LoPart); }
RMsg::Ret(n) => { result = Some(n) }}}
result.unwrap() }}}
match ITE::norm(i,t,e) {
Norm::Nid(n) => n,
Norm::Ite(ite) => { run_swarm_ite!(ite) }
Norm::Not(ite) => { not(run_swarm_ite!(ite)) }}}
}
fn swarm_ite<S:BddState>(state: &Arc<S>, 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) => rmsg_not(swarm_ite_norm(state, ite)) }}
fn swarm_vhl_norm<S:BddState>(state: &Arc<S>, ite:ITE)->RMsg {
let ITE{i:vv,t:hi,e:lo} = ite; let v = var(vv);
debug_assert!(is_var(vv)); debug_assert_eq!(v, ite.min_var());
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<S:BddState>(state: &Arc<S>, ite:ITE)->RMsg {
let ITE { i, t, e } = ite;
let (vi, vt, ve) = (var(i), var(t), var(e));
let v = min(vi, min(vt, ve));
match state.get_memo(v, &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(nv(v), hn, ln) {
Norm::Nid(n) => { RMsg::Nid(n) }
Norm::Ite(ite) => swarm_vhl_norm(state, ite),
Norm::Not(ite) => rmsg_not(swarm_vhl_norm(state, ite))}}
else { RMsg::Wip{ v, hi, lo, invert:false } }}}}
fn swarm_loop<S:BddState>(tx:RTx, rx:QRx<S>, mut state:Arc<S>) {
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 } }}}}
#[derive(Debug, Serialize, Deserialize)]
pub struct BddBase<S:BddState, W:BddWorker<S>> {
pub tags: HashMap<String, NID>,
phantom: PhantomData<S>,
worker: W}
impl<S:BddState, W:BddWorker<S>> BddBase<S,W> {
pub fn new(nvars:usize)->BddBase<S,W> {
BddBase{phantom: PhantomData,
worker: W::new(nvars),
tags:HashMap::new()}}
pub fn nvars(&self)->usize { self.worker.nvars() }
pub fn tag(&mut self, s:String, n:NID) { self.tags.insert(s, n); }
pub fn get(&self, s:&String)->Option<NID> { Some(*self.tags.get(s)?) }
#[inline] fn tup(&self, n:NID)->(NID,NID) { self.worker.tup(n) }
pub fn bdd(&self, n:NID)->BDDNode {
let t=self.tup(n); BDDNode{v:var(n), hi:t.0, lo:t.1 }}
pub fn walk<F>(&self, n:NID, f:&mut F) where F: FnMut(NID,VID,NID,NID) {
let mut seen = HashSet::new();
self.step(n,f,&mut seen)}
fn step<F>(&self, n:NID, f:&mut F, seen:&mut HashSet<NID>)
where F: FnMut(NID,VID,NID,NID) {
if !seen.contains(&n) {
seen.insert(n); let (hi,lo) = self.tup(n); f(n,var(n),hi,lo);
if !is_const(hi) { self.step(hi, f, seen); }
if !is_const(lo) { self.step(lo, f, seen); }}}
pub fn save(&self, path:&str)->::std::io::Result<()> {
let s = bincode::serialize(&self).unwrap();
return io::put(path, &s) }
pub fn load(path:&str)->::std::io::Result<(BDDBase)> {
let s = io::get(path)?;
return Ok(bincode::deserialize(&s).unwrap()); }
pub fn dot<T>(&self, n:NID, wr: &mut T) where T : ::std::fmt::Write {
macro_rules! w {
($x:expr $(,$xs:expr)*) => { writeln!(wr, $x $(,$xs)*).unwrap() }}
let fmt = |n:NID| {
if is_rvar(n) { format!("x{}", rvar(n)) }
else { format!("{}", n) }};
w!("digraph bdd {{");
w!(" I[label=⊤; shape=square];");
w!(" O[label=⊥; shape=square];");
w!("node[shape=circle];");
self.walk(n, &mut |n,_,_,_| w!(" \"{}\"[label=\"{}\"];", n, fmt(n)));
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!("}}"); }
pub fn save_dot(&self, n:NID, path:&str) {
let mut s = String::new(); self.dot(n, &mut s);
let mut txt = File::create(path).expect("couldn't create dot file");
txt.write_all(s.as_bytes()).expect("failet to write text to dot file"); }
pub fn show_named(&self, n:NID, s:&str) { self.save_dot(n, format!("{}.dot", s).as_str());
let out = Command::new("dot").args(&["-Tpng",format!("{}.dot",s).as_str()])
.output().expect("failed to run 'dot' command");
let mut png = File::create(format!("{}.png",s).as_str()).expect("couldn't create png");
png.write_all(&out.stdout).expect("couldn't write png");
Command::new("firefox").args(&[format!("{}.png",s).as_str()])
.spawn().expect("failed to launch firefox"); }
pub fn show(&self, n:NID) { self.show_named(n, "+bdd") }
pub fn and(&mut self, x:NID, y:NID)->NID { self.ite(x, y, O) }
pub fn xor(&mut self, x:NID, y:NID)->NID { self.ite(x, not(y), y) }
pub fn or(&mut self, x:NID, y:NID)->NID { self.ite(x, I, y) }
pub fn gt(&mut self, x:NID, y:NID)->NID { self.ite(x, not(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.worker.ite(f,g,h) }
pub fn when_hi(&mut self, x:VID, y:NID)->NID {
let yv = var(y);
if yv == x { self.tup(y).0 } else if yv > x { y } else { let (yt, ye) = self.tup(y);
let (th,el) = (self.when_hi(x,yt), self.when_hi(x,ye));
self.ite(nv(yv), th, el) }}
pub fn when_lo(&mut self, x:VID, y:NID)->NID {
let yv = var(y);
if yv == x { self.tup(y).1 } else if yv > x { y } else { let (yt, ye) = self.tup(y);
let (th,el) = (self.when_lo(x,yt), self.when_lo(x,ye));
self.ite(nv(yv), th, el) }}
#[inline] pub fn might_depend(&mut self, x:NID, y:VID)->bool {
if is_var(x) { var(x)==y } else { var(x) <= y }}
pub fn replace(&mut self, x:VID, y:NID, z:NID)->NID {
if self.might_depend(z, x) {
let (zt,ze) = self.tup(z); let zv = var(z);
if x==zv { self.ite(y, zt, ze) }
else {
let th = self.replace(x, y, zt);
let el = self.replace(x, y, ze);
self.ite(nv(zv), th, el) }}
else { z }}
pub fn swap(&mut self, n:NID, x:VID, y:VID)-> NID {
if y>x { 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(nv(y), xlo_ylo, xhi_ylo);
let hi = self.ite(nv(y), xlo_yhi, xhi_yhi);
self.ite(nv(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) {
if v as usize == self.nvars() { 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, v+1, lo, i*2);
let hi = self.when_hi(v,n); self.tt_aux(res, v+1, hi, i*2+1); }}
pub fn tt(&mut self, n0:NID)->Vec<u8> {
if self.nvars() > 16 {
panic!("refusing to generate a truth table of 2^{} bytes", self.nvars()) }
let mut res = vec![0;(1 << self.nvars()) as usize];
self.tt_aux(&mut res, 0, n0, 0);
res }
}
#[cfg(safe)]
type S = SafeVarKeyedBddState;
#[cfg(not(safe))]
type S = UnsafeVarKeyedBddState;
pub type BDDBase = BddBase<S,SimpleBddWorker<S>>;
#[test] fn test_nids() {
assert_eq!(O, NID{n:0x2000000000000000u64});
assert_eq!(I, NID{n:0xa000000000000000u64});
assert_eq!(nv(0), NID{n:0x4000000000000000u64});
assert_eq!(nvr(0), NID{n:0x5000000000000000u64});
assert_eq!(nv(1), NID{n:0x4000000100000000u64});
assert!(var(nv(0)) < var(nvr(0)));
assert_eq!(nvi(0,0), NID{n:0x0000000000000000u64});
assert_eq!(nvi(1,0), NID{n:0x0000000100000000u64}); }
#[test] fn test_base() {
let mut base = BDDBase::new(3);
let (v1, v2, v3) = (nv(1), nv(2), nv(3));
assert_eq!(base.nvars(), 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(3,v3));
assert_eq!(O, base.when_lo(3,v3))}
#[test] fn test_and() {
let mut base = BDDBase::new(3);
let (v1, v2) = (nv(1), nv(2));
let a = base.and(v1, v2);
assert_eq!(O, base.when_lo(1,a));
assert_eq!(v2, base.when_hi(1,a));
assert_eq!(O, base.when_lo(2,a));
assert_eq!(v1, base.when_hi(2,a));
assert_eq!(a, base.when_hi(3,a));
assert_eq!(a, base.when_lo(3,a))}
#[test] fn test_xor() {
let mut base = BDDBase::new(3);
let (v1, v2) = (nv(1), nv(2));
let x = base.xor(v1, v2);
assert_eq!(v2, base.when_lo(1,x));
assert_eq!(not(v2), base.when_hi(1,x));
assert_eq!(v1, base.when_lo(2,x));
assert_eq!(not(v1), base.when_hi(2,x));
assert_eq!(x, base.when_lo(3,x));
assert_eq!(x, base.when_hi(3,x))}
pub type BddSwarmBase = BddBase<SafeVarKeyedBddState,BddSwarm<SafeVarKeyedBddState>>;
#[test] fn test_swarm_xor() {
let mut base = BddSwarmBase::new(2);
let (x0, x1) = (nv(0), nv(1));
let x = base.xor(x0, x1);
assert_eq!(x1, base.when_lo(0,x));
assert_eq!(not(x1), base.when_hi(0,x));
assert_eq!(x0, base.when_lo(1,x));
assert_eq!(not(x0), base.when_hi(1,x));
assert_eq!(x, base.when_lo(2,x));
assert_eq!(x, base.when_hi(2,x))}
#[test] fn test_swarm_and() {
let mut base = BddSwarmBase::new(2);
let (x0, x1) = (nv(0), nv(1));
let a = base.and(x0, x1);
assert_eq!(O, base.when_lo(0,a));
assert_eq!(x1, base.when_hi(0,a));
assert_eq!(O, base.when_lo(1,a));
assert_eq!(x0, base.when_hi(1,a));
assert_eq!(a, base.when_hi(2,a));
assert_eq!(a, base.when_lo(2,a))}
#[test] fn test_swarm_ite() {
let mut base = BddSwarmBase::new(3);
let (x0,x1,x2) = (nv(0), nv(1), nv(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));
debug!("\n----| x = x0 xor x1 |----");
let x = base.xor(x0, x1);
assert_eq!(vec![0,0,1,1,1,1,0,0], base.tt(x));
debug!("\n----| a = x1 and x2 |----");
let a = base.and(x1, x2);
assert_eq!(vec![0,0,0,1,0,0,0,1], base.tt(a));
debug!("\n----| x ? a : !a |----");
let i = base.ite(x, a, not(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 = BddSwarmBase::new(4);
let (a,b,c,d) = (nv(0), nv(1), nv(2), nv(3));
let anb = base.and(a,not(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,not(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(not(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);
}