#![macro_use]
use std::collections::{HashSet, HashMap};
use std::fs::File;
use std::io::Write;
use std::process::Command; use crate::{simp, nid::NID};
use crate::vid::VID;
use crate::reg::Reg;
pub trait Base {
fn new()->Self where Self:Sized;
fn when_hi(&mut self, v:VID, n:NID)->NID;
fn when_lo(&mut self, v:VID, n:NID)->NID;
fn and(&mut self, x:NID, y:NID)->NID;
fn xor(&mut self, x:NID, y:NID)->NID;
fn or(&mut self, x:NID, y:NID)->NID;
fn def(&mut self, s:String, v:VID)->NID;
fn tag(&mut self, n:NID, s:String)->NID;
fn get(&self, s:&str)->Option<NID>;
fn sub(&mut self, v:VID, n:NID, ctx:NID)->NID;
fn _eval_aux(&mut self, _n:NID, _kv: &HashMap<VID,NID>, _cache:&mut HashMap<NID,NID>)->NID {
todo!("_eval_aux not yet implemented for this type") }
fn eval_all(&mut self, nids: &[NID], kv: &HashMap<VID,NID>)->Vec<NID> {
let mut cache = HashMap::new();
nids.iter().map(|&n| self._eval_aux(n, kv, &mut cache)).collect() }
fn eval(&mut self, nid:NID, kv:&HashMap<VID, NID>)->NID {
self.eval_all(&[nid], kv)[0] }
fn dot(&self, n:NID, wr: &mut dyn std::fmt::Write);
fn solution_set(&self, _n:NID, _nvars:usize)->HashSet<Reg> { unimplemented!() }
fn init_stats(&mut self) { }
fn print_stats(&mut self) { }}
pub trait GraphViz {
fn write_dot(&self, n:NID, wr: &mut dyn std::fmt::Write);
fn save_dot(&self, n:NID, path:&str) {
let mut s = String::new(); self.write_dot(n, &mut s);
let mut txt = File::create(path).expect("couldn't create dot file");
txt.write_all(s.as_bytes()).expect("failed to write text to dot file"); }
fn show_named(&self, n:NID, s:&str) {
self.save_dot(n, format!("{}.dot", s).as_str());
let out = Command::new("dot").args(["-Tsvg",format!("{}.dot",s).as_str()])
.output().expect("failed to run 'dot' command");
let mut svg = File::create(format!("{}.svg",s).as_str()).expect("couldn't create svg");
svg.write_all(&out.stdout).expect("couldn't write svg");
let _ = Command::new("firefox").args([format!("{}.svg",s).as_str()])
.spawn().expect("failed to launch firefox").wait(); }
fn show(&self, n:NID) { self.show_named(n, "+bdd") }
}
impl<T:Base> GraphViz for T {
fn write_dot(&self, n:NID, wr: &mut dyn std::fmt::Write) {
T::dot(self,n, wr)}}
#[macro_export] macro_rules! inherit {
( $($i:ident),* ) => { $( inherit!(@fn $i); )* };
(@fn new) => { #[inline] fn new()->Self where Self:Sized { Self { base: T::new() }} };
(@fn when_hi) => { #[inline] fn when_hi(&mut self, v:VID, n:NID)->NID { self.base.when_hi(v, n) }};
(@fn when_lo) => { #[inline] fn when_lo(&mut self, v:VID, n:NID)->NID { self.base.when_lo(v, n) }};
(@fn and) => { #[inline] fn and(&mut self, x:NID, y:NID)->NID { self.base.and(x, y) }};
(@fn xor) => { #[inline] fn xor(&mut self, x:NID, y:NID)->NID { self.base.xor(x, y) }};
(@fn or) => { #[inline] fn or(&mut self, x:NID, y:NID)->NID { self.base.or(x, y) }};
(@fn def) => { #[inline] fn def(&mut self, s:String, i:VID)->NID { self.base.def(s, i) }};
(@fn tag) => { #[inline] fn tag(&mut self, n:NID, s:String)->NID { self.base.tag(n, s) }};
(@fn get) => { #[inline] fn get(&self, s:&str)->Option<NID> { self.base.get(s) }};
(@fn sub) => { #[inline] fn sub(&mut self, v:VID, n:NID, ctx:NID)->NID { self.base.sub(v, n, ctx) }};
(@fn dot) => { #[inline] fn dot(&self, n:NID, wr: &mut dyn std::fmt::Write) { self.base.dot(n, wr) }}; }
pub struct Simplify<T:Base> { pub base: T }
impl<T:Base> Base for Simplify<T> {
inherit![ new, when_hi, when_lo, xor, or, def, tag, get, sub, dot ];
fn and(&mut self, x:NID, y:NID)->NID {
if let Some(nid) = simp::and(x,y) { nid }
else {
let (a, b) = if x < y { (x,y) } else { (y,x) };
self.base.and(a, b) }}}
#[macro_export] macro_rules! expr {
(@op $b:ident, $x:tt $op:ident $y:tt) => {{
let x = expr![$b, $x];
let y = expr![$b, $y];
$b.$op(x,y) }};
($_:ident, $id:ident) => { $id };
($b:ident, ($x:tt ^ $y:tt)) => { expr![@op $b, $x xor $y] };
($b:ident, ($x:tt | $y:tt)) => { expr![@op $b, $x or $y] };
($b:ident, ($x:tt & $y:tt)) => { expr![@op $b, $x and $y] };}
#[macro_export] macro_rules! vid_map {
($($x:ident : $y:expr),*) => {
vec![$(($x, $y)),*].iter().copied().collect::<HashMap<VID,NID>>() }}
macro_rules! base_test {
($name:ident, $basename:ident, $tt:tt) => {
macro_rules! $name {
($BaseType:ident) => {
#[test] fn $name() {
use crate::base::Base;
let mut $basename = <$BaseType as Base>::new();
$tt }}}}}
base_test!(test_base_consts, b, {
use crate::{O,I};
assert!(O<I, "expect O<I");
assert!(O==b.and(O,O), "O∧O"); assert!(O==b.and(I,O), "I∧O");
assert!(O==b.and(O,I), "O∧I"); assert!(I==b.and(I,I), "I∧I");
assert!(O==b.xor(O,O), "O≠O"); assert!(I==b.xor(I,O), "I≠O");
assert!(I==b.xor(O,I), "O≠I"); assert!(O==b.xor(I,I), "I≠I"); });
base_test!(test_base_when, b, {
use crate::nid::{O,I, named::{x0, x1}};
let (vx0, vx1) = (x0.vid(), x1.vid());
assert_eq!(b.when_lo(vx0, O), O, "vx0=O should not affect O");
assert_eq!(b.when_hi(vx0, O), O, "vx0=I should not affect O");
assert_eq!(b.when_lo(vx0, I), I, "vx0=O should not affect I");
assert_eq!(b.when_hi(vx0, I), I, "vx0=I should not affect I");
assert_eq!(b.when_lo(vx0, x0), O, "when_lo(vx0, x0) should be O");
assert_eq!(b.when_hi(vx0, x0), I, "when_hi(vx0, x0) should be I");
assert_eq!(b.when_lo(vx0, x1), x1, "vx0=O should not affect x1");
assert_eq!(b.when_hi(vx0, x1), x1, "vx0=I should not affect x1");
assert_eq!(b.when_lo(vx1, x0), x0, "vx1=O should not affect x0");
assert_eq!(b.when_hi(vx1, x0), x0, "vx1=I should not affect x0");
assert_eq!(b.when_lo(vx1, x1), O, "when_lo(vx1, x1) should be O");
assert_eq!(b.when_hi(vx1, x1), I, "when_hi(vx1, x1) should be I");
});