z3-z3-4.13.0.src.muz.ddnf.ddnf.cpp Maven / Gradle / Ivy
The newest version!
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
ddnf.cpp
Abstract:
DDNF based engine.
Author:
Nikolaj Bjorner (nbjorner) 2014-08-21
Revision History:
- inherits from Nuno Lopes Hassel utilities
and Garvit Juniwal's DDNF engine.
--*/
#include "muz/ddnf/ddnf.h"
#include "muz/base/dl_rule_set.h"
#include "muz/base/dl_context.h"
#include "ast/scoped_proof.h"
#include "ast/bv_decl_plugin.h"
#include "util/tbv.h"
#include
namespace datalog {
class ddnf_mgr;
class ddnf_node;
typedef ref_vector ddnf_node_vector;
class ddnf_node {
public:
struct eq {
tbv_manager& m;
eq(tbv_manager& m):m(m) {}
bool operator()(ddnf_node* n1, ddnf_node* n2) const {
return m.equals(n1->get_tbv(), n2->get_tbv());
}
};
struct hash {
tbv_manager& m;
hash(tbv_manager& m):m(m) {}
unsigned operator()(ddnf_node* n) const {
return m.hash(n->get_tbv());
}
};
typedef ptr_hashtable ddnf_nodes;
private:
tbv_manager& tbvm;
tbv const& m_tbv;
ddnf_node_vector m_children;
unsigned m_refs;
unsigned m_id;
ddnf_node::hash m_hash;
ddnf_node::eq m_eq;
ddnf_nodes m_descendants;
friend class ddnf_mgr;
public:
ddnf_node(ddnf_mgr& m, tbv_manager& tbvm, tbv const& tbv, unsigned id):
tbvm(tbvm),
m_tbv(tbv),
m_children(m),
m_refs(0),
m_id(id),
m_hash(tbvm),
m_eq(tbvm),
m_descendants(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq) {
}
~ddnf_node() {}
unsigned inc_ref() {
return ++m_refs;
}
void dec_ref() {
SASSERT(m_refs > 0);
--m_refs;
if (m_refs == 0) {
dealloc(this);
}
}
ddnf_nodes& descendants() { return m_descendants; }
unsigned get_id() const { return m_id; }
unsigned num_children() const { return m_children.size(); }
ddnf_node* operator[](unsigned index) { return m_children[index].get(); }
tbv const& get_tbv() const { return m_tbv; }
void add_child(ddnf_node* n);
void remove_child(ddnf_node* n);
bool contains_child(ddnf_node* n) const;
void display(std::ostream& out) const {
out << "node[" << get_id() << ": ";
tbvm.display(out, m_tbv);
for (unsigned i = 0; i < m_children.size(); ++i) {
out << " " << m_children[i]->get_id();
}
out << "]";
}
};
typedef ddnf_node::ddnf_nodes ddnf_nodes;
class ddnf_mgr {
struct stats {
unsigned m_num_inserts;
unsigned m_num_comparisons;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
ddnf_node* m_root;
ddnf_node_vector m_noderefs;
bool m_internalized;
tbv_manager m_tbv;
ddnf_node::hash m_hash;
ddnf_node::eq m_eq;
ddnf_nodes m_nodes;
bool_vector m_marked;
stats m_stats;
public:
ddnf_mgr(unsigned n): m_noderefs(*this), m_internalized(false), m_tbv(n),
m_hash(m_tbv), m_eq(m_tbv),
m_nodes(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq) {
tbv* bX = m_tbv.allocateX();
m_root = alloc(ddnf_node, *this, m_tbv, *bX, m_nodes.size());
m_noderefs.push_back(m_root);
m_nodes.insert(m_root);
}
~ddnf_mgr() {
m_noderefs.reset();
m_tbv.reset();
}
void inc_ref(ddnf_node* n) {
n->inc_ref();
}
void dec_ref(ddnf_node* n) {
n->dec_ref();
}
void reset_accumulate() {
m_marked.resize(m_nodes.size());
for (unsigned i = 0; i < m_marked.size(); ++i) {
m_marked[i] = false;
}
}
void accumulate(tbv const& t, unsigned_vector& acc) {
ddnf_node* n = find(t);
ptr_vector todo;
todo.push_back(n);
while (!todo.empty()) {
n = todo.back();
todo.pop_back();
unsigned id = n->get_id();
if (m_marked[id]) continue;
acc.push_back(id);
m_marked[id] = true;
unsigned sz = n->num_children();
for (unsigned i = 0; i < sz; ++i) {
todo.push_back((*n)[i]);
}
}
}
ddnf_node* insert(tbv const& t) {
SASSERT(!m_internalized);
ptr_vector new_tbvs;
new_tbvs.push_back(&t);
for (unsigned i = 0; i < new_tbvs.size(); ++i) {
tbv const& nt = *new_tbvs[i];
IF_VERBOSE(10, m_tbv.display(verbose_stream() << "insert: ", nt); verbose_stream() << "\n";);
ddnf_node* n;
if (contains(nt)) {
n = find(nt);
}
else {
n = alloc(ddnf_node, *this, m_tbv, nt, m_noderefs.size());
m_noderefs.push_back(n);
m_nodes.insert(n);
}
insert(*m_root, n, new_tbvs);
}
return find(t);
}
tbv* allocate(uint64_t v, unsigned hi, unsigned lo) {
return m_tbv.allocate(v, hi, lo);
}
tbv_manager& get_tbv_manager() {
return m_tbv;
}
unsigned size() const {
return m_noderefs.size();
}
ddnf_nodes const& lookup(tbv const& t) {
internalize();
return find(t)->descendants();
}
void display_statistics(std::ostream& out) const {
out << "Number of insertions: " << m_stats.m_num_inserts << "\n"
"Number of comparisons: " << m_stats.m_num_comparisons << "\n"
"Number of nodes: " << size() << "\n";
}
void display(std::ostream& out) const {
for (unsigned i = 0; i < m_noderefs.size(); ++i) {
m_noderefs[i]->display(out);
out << "\n";
}
}
bool contains(tbv const& t) {
ddnf_node dummy(*this, m_tbv, t, 0);
return m_nodes.contains(&dummy);
}
bool well_formed() {
ptr_vector todo;
todo.push_back(m_root);
reset_accumulate();
while (!todo.empty()) {
ddnf_node* n = todo.back();
todo.pop_back();
if (m_marked[n->get_id()]) continue;
m_marked[n->get_id()] = true;
unsigned sz = n->num_children();
for (unsigned i = 0; i < sz; ++i) {
ddnf_node* child = (*n)[i];
if (!m_tbv.contains(n->get_tbv(), child->get_tbv())) {
IF_VERBOSE(0,
m_tbv.display(verbose_stream() << "parent ", n->get_tbv());
m_tbv.display(verbose_stream() << " does not contains child: ", child->get_tbv());
display(verbose_stream());
);
return false;
}
todo.push_back(child);
}
}
return true;
}
private:
ddnf_node* find(tbv const& t) {
ddnf_node dummy(*this, m_tbv, t, 0);
return *(m_nodes.find(&dummy));
}
void insert(ddnf_node& root, ddnf_node* new_n, ptr_vector& new_intersections) {
tbv const& new_tbv = new_n->get_tbv();
IF_VERBOSE(10, m_tbv.display(verbose_stream() << "root: ", root.get_tbv());
m_tbv.display(verbose_stream() << " new node ", new_tbv); verbose_stream() << "\n";);
SASSERT(m_tbv.contains(root.get_tbv(), new_tbv));
if (m_eq(&root, new_n)) return;
++m_stats.m_num_inserts;
bool inserted = false;
for (unsigned i = 0; i < root.num_children(); ++i) {
ddnf_node& child = *(root[i]);
++m_stats.m_num_comparisons;
IF_VERBOSE(10, m_tbv.display(verbose_stream() << "child ", child.get_tbv());
verbose_stream() << " contains: " << m_tbv.contains(child.get_tbv(), new_tbv) << "\n";);
if (m_tbv.contains(child.get_tbv(), new_tbv)) {
inserted = true;
insert(child, new_n, new_intersections);
}
}
if (inserted) {
return;
}
ddnf_node_vector subset_children(*this);
tbv* intr = m_tbv.allocate();
for (unsigned i = 0; i < root.num_children(); ++i) {
ddnf_node& child = *(root[i]);
// cannot be superset
SASSERT(!m_tbv.contains(child.get_tbv(),new_tbv));
// checking for subset
if (m_tbv.contains(new_tbv, child.get_tbv())) {
subset_children.push_back(&child);
IF_VERBOSE(10, m_tbv.display(verbose_stream() << "contains child", child.get_tbv()); verbose_stream() << "\n";);
++m_stats.m_num_comparisons;
}
else if (m_tbv.intersect(child.get_tbv(), new_tbv, *intr)) {
// this means there is a non-full intersection
new_intersections.push_back(intr);
IF_VERBOSE(10, m_tbv.display(verbose_stream() << "intersect child ", child.get_tbv()); verbose_stream() << "\n";);
intr = m_tbv.allocate();
m_stats.m_num_comparisons += 2;
}
else {
m_stats.m_num_comparisons += 2;
}
}
m_tbv.deallocate(intr);
for (unsigned i = 0; i < subset_children.size(); ++i) {
root.remove_child(subset_children[i].get());
new_n->add_child(subset_children[i].get());
}
root.add_child(new_n);
}
void internalize() {
// populate maps (should be bit-sets) of descendants.
if (m_internalized) {
return;
}
ptr_vector todo;
todo.push_back(m_root);
bool_vector done(m_noderefs.size(), false);
while (!todo.empty()) {
ddnf_node& n = *todo.back();
if (done[n.get_id()]) {
todo.pop_back();
continue;
}
unsigned sz = n.num_children();
bool all_done = true;
for (unsigned i = 0; i < sz; ++i) {
ddnf_node* child = n[i];
if (!done[child->get_id()]) {
all_done = false;
todo.push_back(child);
}
}
if (all_done) {
n.descendants().insert(&n);
for (unsigned i = 0; i < sz; ++i) {
ddnf_node* child = n[i];
add_table(n.descendants(), child->descendants());
}
done[n.get_id()] = true;
todo.pop_back();
}
}
m_internalized = true;
}
void add_table(ddnf_nodes& dst, ddnf_nodes const& src) {
ddnf_nodes::iterator it = src.begin(), end = src.end();
for (; it != end; ++it) {
dst.insert(*it);
}
}
};
ddnf_core::ddnf_core(unsigned n) {
m_imp = alloc(ddnf_mgr, n);
}
ddnf_core::~ddnf_core() {
dealloc(m_imp);
}
ddnf_node* ddnf_core::insert(tbv const& t) {
return m_imp->insert(t);
}
tbv_manager& ddnf_core::get_tbv_manager() {
return m_imp->get_tbv_manager();
}
unsigned ddnf_core::size() const {
return m_imp->size();
}
bool ddnf_core::contains(tbv const& t) {
return m_imp->contains(t);
}
bool ddnf_core::well_formed() {
return m_imp->well_formed();
}
void ddnf_core::reset_accumulate() {
return m_imp->reset_accumulate();
}
void ddnf_core::accumulate(tbv const& t, unsigned_vector& acc) {
return m_imp->accumulate(t, acc);
}
void ddnf_core::display(std::ostream& out) const {
m_imp->display(out);
}
void ddnf_core::display_statistics(std::ostream& out) const {
m_imp->display_statistics(out);
}
void ddnf_node::add_child(ddnf_node* n) {
//SASSERT(!m_tbv.is_subset(n->m_tbv));
m_children.push_back(n);
}
void ddnf_node::remove_child(ddnf_node* n) {
m_children.erase(n);
}
bool ddnf_node::contains_child(ddnf_node* n) const {
return m_children.contains(n);
}
class ddnfs {
u_map m_mgrs;
public:
ddnfs() {}
~ddnfs() {
u_map::iterator it = m_mgrs.begin(), end = m_mgrs.end();
for (; it != end; ++it) {
dealloc(it->m_value);
}
}
tbv* allocate(unsigned num_bits, uint64_t v, unsigned hi, unsigned lo) {
return get(num_bits).allocate(v, hi, lo);
}
void insert(unsigned num_bits, tbv const& t) {
get(num_bits).insert(t);
}
ddnf_mgr& get(unsigned num_bits) {
return *insert(num_bits);
}
ddnf_nodes const& lookup(unsigned n, tbv const& t) const {
return m_mgrs.find(n)->lookup(t);
}
void display(std::ostream& out) const {
for (auto const& kv : m_mgrs)
kv.m_value->display(out);
}
private:
ddnf_mgr* insert(unsigned n) {
ddnf_mgr* m = nullptr;
if (!m_mgrs.find(n, m)) {
m = alloc(ddnf_mgr, n);
m_mgrs.insert(n, m);
}
return m;
}
};
class ddnf::imp {
struct stats {
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
context& m_ctx;
ast_manager& m;
rule_manager& rm;
bv_util bv;
ptr_vector m_todo;
ast_mark m_visited1, m_visited2;
ddnfs m_ddnfs;
stats m_stats;
obj_map m_expr2tbv;
obj_map m_cache;
expr_ref_vector m_trail;
context m_inner_ctx;
public:
imp(context& ctx):
m_ctx(ctx),
m(ctx.get_manager()),
rm(ctx.get_rule_manager()),
bv(m),
m_trail(m),
m_inner_ctx(m, m_ctx.get_register_engine(), m_ctx.get_fparams())
{
params_ref params;
params.set_sym("engine", symbol("datalog"));
m_inner_ctx.updt_params(params);
}
~imp() {}
lbool query(expr* query) {
m_ctx.ensure_opened();
rule_set& old_rules = m_ctx.get_rules();
rm.mk_query(query, old_rules);
rule_set new_rules(m_ctx);
IF_VERBOSE(10, verbose_stream() << "(ddnf.preprocess)\n";);
if (!pre_process_rules(old_rules)) {
return l_undef;
}
IF_VERBOSE(10, verbose_stream() << "(ddnf.compile)\n";);
if (!compile_rules1(old_rules, new_rules)) {
return l_undef;
}
IF_VERBOSE(15, m_ddnfs.display(verbose_stream()););
dump_rules(new_rules);
return l_undef;
// return execute_rules(new_rules);
}
void reset_statistics() {
m_stats.reset();
}
void collect_statistics(statistics& st) const {
}
void display_certificate(std::ostream& out) const {
expr_ref ans = get_answer();
out << mk_pp(ans, m) << "\n";
}
expr_ref get_answer() const {
UNREACHABLE();
return expr_ref(m.mk_true(), m);
}
private:
proof_ref get_proof() const {
scoped_proof sp(m);
proof_ref pr(m);
return pr;
}
bool pre_process_rules(rule_set const& rules) {
m_visited1.reset();
m_todo.reset();
m_cache.reset();
m_expr2tbv.reset();
for (auto* r : rules)
if (!pre_process_rule(*r))
return false;
return true;
}
bool pre_process_rule(rule const& r) {
// all predicates are monadic.
unsigned utsz = r.get_uninterpreted_tail_size();
unsigned sz = r.get_tail_size();
for (unsigned i = utsz; i < sz; ++i) {
m_todo.push_back(r.get_tail(i));
}
if (process_todo()) {
return true;
}
else {
r.display(m_ctx, std::cout);
return false;
}
}
bool process_todo() {
while (!m_todo.empty()) {
expr* e = m_todo.back();
m_todo.pop_back();
if (m_visited1.is_marked(e)) {
continue;
}
m_visited1.mark(e, true);
if (is_var(e)) {
continue;
}
if (is_quantifier(e)) {
return false;
}
if (m.is_and(e) ||
m.is_or(e) ||
m.is_iff(e) ||
m.is_not(e) ||
m.is_implies(e)) {
m_todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
continue;
}
if (is_ground(e)) {
continue;
}
if (process_atomic(e)) {
continue;
}
IF_VERBOSE(0, verbose_stream() << "Could not handle: " << mk_pp(e, m) << "\n";);
return false;
}
return true;
}
bool process_atomic(expr* e) {
expr* e1, *e2, *e3;
unsigned lo, hi;
if (m.is_eq(e, e1, e2) && bv.is_bv(e1)) {
if (is_var(e1) && is_ground(e2)) {
return process_eq(e, to_var(e1), bv.get_bv_size(e1)-1, 0, e2);
}
if (is_var(e2) && is_ground(e1)) {
return process_eq(e, to_var(e2), bv.get_bv_size(e2)-1, 0, e1);
}
if (bv.is_extract(e1, lo, hi, e3) && is_var(e3) && is_ground(e2)) {
return process_eq(e, to_var(e3), hi, lo, e2);
}
if (bv.is_extract(e2, lo, hi, e3) && is_var(e3) && is_ground(e1)) {
return process_eq(e, to_var(e3), hi, lo, e1);
}
if (is_var(e1) && is_var(e2)) {
return true;
}
}
return false;
}
bool process_eq(expr* e, var* v, unsigned hi, unsigned lo, expr* c) {
rational val;
unsigned sz_c;
unsigned sz_v = bv.get_bv_size(v);
if (!bv.is_numeral(c, val, sz_c)) {
return false;
}
if (!val.is_uint64()) {
return false;
}
// v[hi:lo] = val
tbv* tv = m_ddnfs.allocate(sz_v, val.get_uint64(), hi, lo);
m_ddnfs.insert(sz_v, *tv);
m_expr2tbv.insert(e, tv);
// std::cout << mk_pp(v, m) << " " << lo << " " << hi << " " << v << " " << tbv << "\n";
return true;
}
void init_ctx(rule_set& rules) {
m_inner_ctx.reset();
func_decl_set const& predicates = m_ctx.get_predicates();
for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) {
m_inner_ctx.register_predicate(*fit, false);
}
m_inner_ctx.ensure_opened();
m_inner_ctx.replace_rules(rules);
m_inner_ctx.close();
}
void dump_rules(rule_set& rules) {
init_ctx(rules);
m_inner_ctx.display_smt2(0, nullptr, std::cout);
}
lbool execute_rules(rule_set& rules) {
init_ctx(rules);
ptr_vector heads;
rule_set::decl2rules::iterator dit = rules.begin_grouped_rules();
rule_set::decl2rules::iterator dend = rules.end_grouped_rules();
for (; dit != dend; ++dit) {
heads.push_back(dit->m_key);
}
return m_inner_ctx.rel_query(heads.size(), heads.data());
}
bool compile_rules1(rule_set const& rules, rule_set& new_rules) {
datalog::rule_set::iterator it = rules.begin();
datalog::rule_set::iterator end = rules.end();
unsigned idx = 0;
for (; it != end; ++idx, ++it) {
if (!compile_rule1(**it, rules, new_rules)) {
return false;
}
}
return true;
}
bool compile_rule1(rule& r, rule_set const& old_rules, rule_set& new_rules) {
app_ref head(m), pred(m);
app_ref_vector body(m);
expr_ref tmp(m);
compile_predicate(r.get_head(), head);
unsigned utsz = r.get_uninterpreted_tail_size();
unsigned sz = r.get_tail_size();
for (unsigned i = 0; i < utsz; ++i) {
compile_predicate(r.get_tail(i), pred);
body.push_back(pred);
}
for (unsigned i = utsz; i < sz; ++i) {
compile_expr(r.get_tail(i), tmp);
body.push_back(to_app(tmp));
}
rule* r_new = rm.mk(head, body.size(), body.data(), nullptr, r.name(), false);
new_rules.add_rule(r_new);
IF_VERBOSE(20, r_new->display(m_ctx, verbose_stream()););
if (old_rules.is_output_predicate(r.get_decl())) {
new_rules.set_output_predicate(r_new->get_decl());
}
return true;
}
void compile_predicate(app* p, app_ref& result) {
sort_ref_vector domain(m);
func_decl* d = p->get_decl();
SASSERT(d->get_family_id() == null_family_id);
for (unsigned i = 0; i < p->get_num_args(); ++i) {
domain.push_back(compile_sort(p->get_arg(i)->get_sort()));
}
func_decl_ref fn(m);
fn = m.mk_func_decl(d->get_name(), domain.size(), domain.data(), m.mk_bool_sort());
m_ctx.register_predicate(fn, false);
expr_ref_vector args(m);
expr_ref tmp(m);
for (unsigned i = 0; i < p->get_num_args(); ++i) {
compile_expr(p->get_arg(i), tmp);
args.push_back(tmp);
}
result = m.mk_app(fn, args.size(), args.data());
}
void insert_cache(expr* e, expr* r) {
m_trail.push_back(r);
m_cache.insert(e, r);
}
void compile_var(var* v, var_ref& result) {
expr* r;
if (m_cache.find(v, r)) {
result = to_var(r);
}
else {
unsigned idx = v->get_idx();
result = m.mk_var(idx, compile_sort(v->get_sort()));
insert_cache(v, result);
}
}
sort* compile_sort(sort* s) {
if (m.is_bool(s)) {
return s;
}
if (bv.is_bv_sort(s)) {
unsigned sz = bv.get_bv_size(s);
ddnf_mgr const& mgr = m_ddnfs.get(sz);
unsigned num_elems = mgr.size();
unsigned nb = 1;
while ((1UL << nb) <= num_elems) {
++nb;
}
return bv.mk_sort(nb);
}
UNREACHABLE();
return nullptr;
}
void compile_expr(expr* e, expr_ref& result) {
expr* r = nullptr;
if (m_cache.find(e, r)) {
result = r;
return;
}
if (is_ground(e)) {
result = e;
m_cache.insert(e, result);
return;
}
if (is_var(e)) {
var_ref w(m);
compile_var(to_var(e), w);
result = w;
return;
}
if (m.is_and(e) ||
m.is_or(e) ||
m.is_iff(e) ||
m.is_not(e) ||
m.is_implies(e)) {
app* a = to_app(e);
expr_ref tmp(m);
expr_ref_vector args(m);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
compile_expr(a->get_arg(i), tmp);
args.push_back(tmp);
}
result = m.mk_app(a->get_decl(), args.size(), args.data());
insert_cache(e, result);
return;
}
expr* e1, *e2, *e3;
unsigned lo, hi;
if (m.is_eq(e, e1, e2) && bv.is_bv(e1)) {
if (is_var(e1) && is_ground(e2)) {
compile_eq(e, result, to_var(e1), bv.get_bv_size(e1)-1, 0, e2);
}
else if (is_var(e2) && is_ground(e1)) {
compile_eq(e, result, to_var(e2), bv.get_bv_size(e2)-1, 0, e1);
}
else if (bv.is_extract(e1, lo, hi, e3) && is_var(e3) && is_ground(e2)) {
compile_eq(e, result, to_var(e3), hi, lo, e2);
}
else if (bv.is_extract(e2, lo, hi, e3) && is_var(e3) && is_ground(e1)) {
compile_eq(e, result, to_var(e3), hi, lo, e1);
}
else if (is_var(e1) && is_var(e2)) {
var_ref v1(m), v2(m);
compile_var(to_var(e1), v1);
compile_var(to_var(e2), v2);
result = m.mk_eq(v1, v2);
}
else {
UNREACHABLE();
}
insert_cache(e, result);
return;
}
std::cout << mk_pp(e, m) << "\n";
UNREACHABLE();
}
void compile_eq(expr* e, expr_ref& result, var* v, unsigned hi, unsigned lo, expr* c) {
tbv* t = nullptr;
// TBD: hi, lo are ignored.
VERIFY(m_expr2tbv.find(e, t));
var_ref w(m);
compile_var(v, w);
unsigned num_bits = bv.get_bv_size(c);
ddnf_nodes const& ns = m_ddnfs.lookup(num_bits, *t);
ddnf_nodes::iterator it = ns.begin(), end = ns.end();
expr_ref_vector eqs(m);
sort* s = w->get_sort();
for (; it != end; ++it) {
eqs.push_back(m.mk_eq(w, bv.mk_numeral(rational((*it)->get_id()), s)));
}
switch (eqs.size()) {
case 0:
UNREACHABLE();
result = m.mk_false();
break;
case 1:
result = eqs[0].get();
break;
default:
result = m.mk_or(eqs.size(), eqs.data());
break;
}
}
};
ddnf::ddnf(context& ctx):
datalog::engine_base(ctx.get_manager(),"tabulation"),
m_imp(alloc(imp, ctx)) {
}
ddnf::~ddnf() {
dealloc(m_imp);
}
lbool ddnf::query(expr* query) {
return m_imp->query(query);
}
void ddnf::reset_statistics() {
m_imp->reset_statistics();
}
void ddnf::collect_statistics(statistics& st) const {
m_imp->collect_statistics(st);
}
void ddnf::display_certificate(std::ostream& out) const {
m_imp->display_certificate(out);
}
expr_ref ddnf::get_answer() {
return m_imp->get_answer();
}
};