#include "util/obj_hashtable.h"
#include "ast/normal_forms/defined_names.h"
#include "ast/ast_translation.h"
#include "ast/used_vars.h"
#include "ast/rewriter/var_subst.h"
#include "ast/ast_smt2_pp.h"
#include "ast/ast_pp.h"
#include "ast/ast_util.h"
#include "ast/array_decl_plugin.h"
struct defined_names::impl {
typedef obj_map<expr, app *> expr2name;
typedef obj_map<expr, proof *> expr2proof;
ast_manager & m;
symbol m_z3name;
expr2name m_expr2name;
expr2proof m_expr2proof;
expr_ref_vector m_exprs;
expr_ref_vector m_names; proof_ref_vector m_apply_proofs;
unsigned_vector m_lims;
impl(ast_manager & m, char const * prefix);
virtual ~impl() = default;
app * gen_name(expr * e, sort_ref_buffer & var_sorts, buffer<symbol> & var_names);
void cache_new_name(expr * e, app * name);
void cache_new_name_intro_proof(expr * e, proof * pr);
void bound_vars(sort_ref_buffer const & sorts, buffer<symbol> const & names, expr * def_conjunct, app * name, expr_ref & result, symbol const& qid = symbol::null);
void bound_vars(sort_ref_buffer const & sorts, buffer<symbol> const & names, expr * def_conjunct, app * name, expr_ref_buffer & result, symbol const& qid = symbol::null);
virtual void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer<symbol> & var_names, expr_ref & new_def);
bool mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr);
void push_scope();
void pop_scope(unsigned num_scopes);
void reset();
void translate(impl const& src, ast_translation& tr);
unsigned get_num_names() const { return m_names.size(); }
func_decl * get_name_decl(unsigned i) const { return to_app(m_names.get(i))->get_decl(); }
};
struct defined_names::pos_impl : public defined_names::impl {
pos_impl(ast_manager & m, char const * fresh_prefix):impl(m, fresh_prefix) {}
void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer<symbol> & var_names, expr_ref & new_def) override;
};
void defined_names::impl::translate(impl const& src, ast_translation& tr) {
SASSERT(src.m_lims.empty());
SASSERT(m_exprs.empty());
for (unsigned i = 0; i < src.m_exprs.size(); ++i) {
expr* e = tr(src.m_exprs.get(i));
app* n = to_app(tr(src.m_names.get(i)));
cache_new_name(e, n);
if (src.m.proofs_enabled()) {
proof* p = to_app(tr(src.m_apply_proofs.get(i)));
cache_new_name_intro_proof(e, p);
}
}
}
defined_names::impl::impl(ast_manager & m, char const * prefix):
m(m),
m_exprs(m),
m_names(m),
m_apply_proofs(m) {
if (prefix)
m_z3name = prefix;
}
void defined_names::translate(defined_names const& src, ast_translation& tr) {
m_impl->translate(*src.m_impl, tr);
m_pos_impl->translate(*src.m_pos_impl, tr);
}
app * defined_names::impl::gen_name(expr * e, sort_ref_buffer & var_sorts, buffer<symbol> & var_names) {
used_vars uv;
uv(e);
unsigned num_vars = uv.get_max_found_var_idx_plus_1();
ptr_buffer<expr> new_args;
ptr_buffer<sort> domain;
for (unsigned i = 0; i < num_vars; ++i) {
sort * s = uv.get(i);
if (s) {
domain.push_back(s);
new_args.push_back(m.mk_var(i, s));
var_sorts.push_back(s);
}
else {
domain.push_back(m.mk_bool_sort());
new_args.push_back(m.mk_true());
var_sorts.push_back(m.mk_bool_sort()); }
var_names.push_back(symbol(i));
}
sort * range = e->get_sort();
func_decl * new_skolem_decl = m.mk_fresh_func_decl(m_z3name, symbol::null, domain.size(), domain.data(), range);
app * n = m.mk_app(new_skolem_decl, new_args.size(), new_args.data());
return n;
}
void defined_names::impl::cache_new_name(expr * e, app * n) {
m_expr2name.insert(e, n);
m_exprs.push_back(e);
m_names.push_back(n);
}
void defined_names::impl::cache_new_name_intro_proof(expr * e, proof * pr) {
SASSERT(m_expr2name.contains(e));
m_expr2proof.insert(e, pr);
m_apply_proofs.push_back(pr);
}
void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer<symbol> const & names, expr * def_conjunct, app * name, expr_ref & result, symbol const& qid) {
SASSERT(sorts.size() == names.size());
if (sorts.empty())
result = def_conjunct;
else {
expr * patterns[1] = { m.mk_pattern(name) };
quantifier_ref q(m);
q = m.mk_forall(sorts.size(),
sorts.data(),
names.data(),
def_conjunct,
1, qid, symbol::null,
1, patterns);
TRACE(mk_definition_bug, tout << "before elim_unused_vars:\n" << mk_ismt2_pp(q, m) << "\n";);
result = elim_unused_vars(m, q, params_ref());
TRACE(mk_definition_bug, tout << "after elim_unused_vars:\n" << result << "\n";);
}
}
void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer<symbol> const & names, expr * def_conjunct, app * name, expr_ref_buffer & result, symbol const& qid) {
expr_ref tmp(m);
bound_vars(sorts, names, def_conjunct, name, tmp, qid);
result.push_back(tmp);
}
#define MK_OR m.mk_or
#define MK_NOT m.mk_not
#define MK_EQ m.mk_eq
void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer<symbol> & var_names, expr_ref & new_def) {
expr_ref_buffer defs(m);
if (m.is_bool(e)) {
bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, defs);
bound_vars(var_sorts, var_names, MK_OR(n, MK_NOT(e)), n, defs);
}
else if (m.is_term_ite(e)) {
expr_ref not_condition(MK_NOT(to_app(e)->get_arg(0)), m);
expr_ref then_eq(MK_EQ(n, to_app(e)->get_arg(1)), m);
bound_vars(var_sorts, var_names, MK_OR(not_condition, then_eq), n, defs);
bound_vars(var_sorts, var_names, MK_OR(to_app(e)->get_arg(0), MK_EQ(n, to_app(e)->get_arg(2))), n, defs);
}
else {
bound_vars(var_sorts, var_names, MK_EQ(e, n), n, defs);
}
new_def = mk_and(m, defs.size(), defs.data());
}
void defined_names::pos_impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer<symbol> & var_names, expr_ref & new_def) {
bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, new_def);
}
bool defined_names::impl::mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) {
TRACE(mk_definition_bug, tout << "making name for:\n" << mk_ismt2_pp(e, m) << "\n";);
app * n_ptr;
if (m_expr2name.find(e, n_ptr)) {
TRACE(mk_definition_bug, tout << "name for expression is already cached..., returning false...\n";);
n = n_ptr;
if (m.proofs_enabled()) {
proof * pr_ptr = nullptr;
m_expr2proof.find(e, pr_ptr);
SASSERT(pr_ptr);
pr = pr_ptr;
}
return false;
}
else {
sort_ref_buffer var_sorts(m);
buffer<symbol> var_names;
n = gen_name(e, var_sorts, var_names);
cache_new_name(e, n);
TRACE(mk_definition_bug, tout << "name: " << mk_ismt2_pp(n, m) << "\n";);
std::reverse(var_sorts.data(), var_sorts.data() + var_sorts.size());
std::reverse(var_names.data(), var_names.data() + var_names.size());
mk_definition(e, n, var_sorts, var_names, new_def);
TRACE(mk_definition_bug, tout << "new_def:\n" << mk_ismt2_pp(new_def, m) << "\n";);
if (m.proofs_enabled()) {
new_def_pr = m.mk_def_intro(new_def);
pr = m.mk_apply_def(e, n, new_def_pr);
cache_new_name_intro_proof(e, pr);
}
return true;
}
}
void defined_names::impl::push_scope() {
SASSERT(m_exprs.size() == m_names.size());
m_lims.push_back(m_exprs.size());
}
void defined_names::impl::pop_scope(unsigned num_scopes) {
unsigned lvl = m_lims.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
unsigned old_sz = m_lims[new_lvl];
unsigned sz = m_exprs.size();
SASSERT(old_sz <= sz);
SASSERT(sz == m_names.size());
while (old_sz != sz) {
--sz;
if (m.proofs_enabled()) {
m_expr2proof.erase(m_exprs.back());
m_apply_proofs.pop_back();
}
m_expr2name.erase(m_exprs.back());
m_exprs.pop_back();
m_names.pop_back();
}
SASSERT(m_exprs.size() == old_sz);
m_lims.shrink(new_lvl);
}
void defined_names::impl::reset() {
m_expr2name.reset();
m_expr2proof.reset();
m_exprs.reset();
m_names.reset();
m_apply_proofs.reset();
m_lims.reset();
}
defined_names::defined_names(ast_manager & m, char const * fresh_prefix) {
m_impl = alloc(impl, m, fresh_prefix);
m_pos_impl = alloc(pos_impl, m, fresh_prefix);
}
defined_names::~defined_names() {
dealloc(m_impl);
dealloc(m_pos_impl);
}
bool defined_names::mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) {
return m_impl->mk_name(e, new_def, new_def_pr, n, pr);
}
bool defined_names::mk_pos_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) {
return m_pos_impl->mk_name(e, new_def, new_def_pr, n, pr);
}
expr_ref defined_names::mk_definition(expr * e, app * n) {
ast_manager& m = m_impl->m;
sort_ref_buffer var_sorts(m);
expr_ref new_def(m);
buffer<symbol> var_names;
m_impl->mk_definition(e, n, var_sorts, var_names, new_def);
return new_def;
}
void defined_names::push() {
m_impl->push_scope();
m_pos_impl->push_scope();
}
void defined_names::pop(unsigned num_scopes) {
m_impl->pop_scope(num_scopes);
m_pos_impl->pop_scope(num_scopes);
}
void defined_names::reset() {
m_impl->reset();
m_pos_impl->reset();
}
unsigned defined_names::get_num_names() const {
return m_impl->get_num_names() + m_pos_impl->get_num_names();
}
func_decl * defined_names::get_name_decl(unsigned i) const {
SASSERT(i < get_num_names());
unsigned n1 = m_impl->get_num_names();
return i < n1 ? m_impl->get_name_decl(i) : m_pos_impl->get_name_decl(i - n1);
}