use std::collections::HashSet;
use crate::vhl::{HiLo, HiLoBase, Walkable};
use crate::{vid::VID, nid::{NID,I,O}, bdd::BddBase, reg::Reg};
use crate::cur::{Cursor, CursorPlan};
impl HiLoBase for BddBase {
fn get_hilo(&self, n:NID)->Option<HiLo> {
let (hi, lo) = self.swarm.tup(n);
Some(HiLo{ hi, lo }) }}
impl 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 !lo.is_const() { self.step(lo, f, seen, topdown) }
if !hi.is_const() { self.step(hi, f, seen, topdown) }
if !topdown { f(n, n.vid(), hi, lo) }}}}
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 Iterator for BDDSolIterator<'_> {
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 }}}
impl CursorPlan for BddBase {}
impl BddBase {
pub fn solutions(&mut self, n:NID)->BDDSolIterator {
let nvars = if n.is_const() { 1 } else if n.vid().is_var() { n.vid().var_ix() }
else if n.vid().is_vir() {
panic!("It probably doesn't make sense to call solutions(n) when n.vid().is_vir(), but you can try solutions_pad() if you think it makes sense.") }
else { panic!("Don't know how to find solutions({:?}). Maybe try solutions_pad()...?", n) };
self.solutions_pad(n, nvars)}
pub fn solutions_pad(&self, n:NID, nvars:usize)->BDDSolIterator {
BDDSolIterator::from_bdd(self, n, nvars)}
pub fn make_cursor(&self, n: NID, watch_vars: &[usize], nvars: usize) -> Option<Cursor> {
if n == O { return None; }
let base_nvars = if n.is_const() { 0 } else { n.vid().var_ix() + 1 };
let real_nvars = std::cmp::max(base_nvars, nvars);
let mut cur = Cursor::new(real_nvars, n);
for &idx in watch_vars { cur.watch.put(idx, true); }
cur.descend(self);
self.mark_skippable(&mut cur);
debug_assert!(cur.node.is_const());
debug_assert!(self.in_solution(&cur), "{:?}", cur.scope);
Some(cur)}
pub fn make_dontcare_cursor(&self, n: NID, nvars: usize) -> Option<Cursor> {
self.make_cursor(n, &[], nvars)}
pub fn make_solution_cursor(&self, n: NID, nvars: usize) -> Option<Cursor> {
let mut cur = self.make_cursor(n, &[], nvars)?;
for i in 0..cur.nvars { cur.watch.put(i, true); }
Some(cur)}
pub fn first_solution(&self, n: NID, nvars: usize) -> Option<Cursor> {
if n == O || nvars == 0 { None }
else { self.make_solution_cursor(n, nvars)}}
pub fn in_solution(&self, cur:&Cursor)->bool {
self.includes_leaf(cur.node) }
fn find_next_leaf(&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.ascend();
{ let iv = cur.node.vid();
if cur.nstack.is_empty() && cur.scope.var_get(iv) {
let top = cur.nvars-1;
if cur.scope.ripple(iv.var_ix(), top).is_some() { rippled = true; }
else { return None }}} }
if rippled { cur.clear_trailing_bits() }
else if cur.var_get() { return None }
else { cur.put_step(self, true); }
cur.descend(self);
Some(cur.node) }
pub fn next_solution(&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 cur.increment().is_some() {
if cur.node.is_const() { return Some(cur) } cur.put_step(self, cur.var_get());
cur.descend(self); }
else { return None }}
while !self.in_solution(&cur) { self.find_next_leaf(&mut cur)?; }
self.mark_skippable(&mut cur);
Some(cur) }
fn mark_skippable(&self, cur: &mut Cursor) {
let mut can_skip = Reg::new(cur.nvars);
let mut prev = 0;
let path: Vec<usize> = cur.nstack.iter().map(|nid|nid.vid().var_ix()).collect();
for (i,&level) in path.iter().rev().enumerate() {
if i == 0 { for j in 0..level { can_skip.put(j, true); }}
else if level > prev + 1 {
for j in (prev + 1)..level { can_skip.put(j, true); }}
prev = level; }
if !cur.nstack.is_empty() {
for i in path[0]+1..cur.nvars { can_skip.put(i, true); }}
cur.can_skip = can_skip; }
}