3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-10 19:27:06 +00:00
z3/lib/bool_rewriter.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

990 lines
32 KiB
C++

/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
bool_rewriter.h
Abstract:
Basic rewrites for Boolean operators.
Author:
Leonardo (leonardo) 2011-04-04
Notes:
--*/
#include"bool_rewriter.h"
#include"rewriter_def.h"
void bool_rewriter::updt_params(params_ref const & p) {
m_flat = p.get_bool(":flat", true);
m_elim_and = p.get_bool(":elim-and", false);
m_local_ctx = p.get_bool(":local-ctx", false);
m_local_ctx_limit = p.get_uint(":local-ctx-limit", UINT_MAX);
m_blast_distinct = p.get_bool(":blast-distinct", false);
m_ite_extra_rules = p.get_bool(":ite-extra-rules", false);
}
void bool_rewriter::get_param_descrs(param_descrs & r) {
r.insert(":ite-extra-rules", CPK_BOOL, "(default: false) extra ite simplifications, these additional simplifications may reduce size locally but increase globally.");
r.insert(":flat", CPK_BOOL, "(default: true) create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor.");
r.insert(":elim-and", CPK_BOOL, "(default: false) conjunctions are rewritten using negation and disjunctions.");
r.insert(":local-ctx", CPK_BOOL, "(default: false) perform local (i.e., cheap) context simplifications.");
r.insert(":local-ctx-limit", CPK_UINT, "(default: inf) limit for applying local context simplifier.");
r.insert(":blast-distinct", CPK_BOOL, "(default: false) expand a distinct predicate into a quadratic number of disequalities.");
}
br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(f->get_family_id() == m().get_basic_family_id());
switch (f->get_decl_kind()) {
case OP_EQ:
case OP_IFF:
SASSERT(num_args == 2);
return mk_eq_core(args[0], args[1], result);
case OP_DISTINCT:
return mk_distinct_core(num_args, args, result);
case OP_AND:
return mk_and_core(num_args, args, result);
case OP_OR:
return mk_or_core(num_args, args, result);
case OP_NOT:
SASSERT(num_args == 1);
return mk_not_core(args[0], result);
case OP_ITE:
SASSERT(num_args == 3);
return mk_ite_core(args[0], args[1], args[2], result);
case OP_IMPLIES:
SASSERT(num_args == 2);
mk_implies(args[0], args[1], result);
return BR_DONE;
case OP_XOR:
SASSERT(num_args == 2);
mk_xor(args[0], args[1], result);
return BR_DONE;
default:
return BR_FAILED;
}
}
void bool_rewriter::mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref_buffer new_args(m());
for (unsigned i = 0; i < num_args; i++) {
expr_ref tmp(m());
mk_not(args[i], tmp);
new_args.push_back(tmp);
}
expr_ref tmp(m());
mk_or(new_args.size(), new_args.c_ptr(), tmp);
mk_not(tmp, result);
}
br_status bool_rewriter::mk_nflat_and_core(unsigned num_args, expr * const * args, expr_ref & result) {
bool s = false;
ptr_buffer<expr> buffer;
expr_fast_mark1 neg_lits;
expr_fast_mark2 pos_lits;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (m().is_true(arg)) {
s = true;
continue;
}
if (m().is_false(arg)) {
result = m().mk_false();
return BR_DONE;
}
if (m().is_not(arg)) {
expr * atom = to_app(arg)->get_arg(0);
if (neg_lits.is_marked(atom)) {
s = true;
continue;
}
if (pos_lits.is_marked(atom)) {
result = m().mk_false();
return BR_DONE;
}
neg_lits.mark(atom);
}
else {
if (pos_lits.is_marked(arg)) {
s = true;
continue;
}
if (neg_lits.is_marked(arg)) {
result = m().mk_false();
return BR_DONE;
}
pos_lits.mark(arg);
}
buffer.push_back(arg);
}
unsigned sz = buffer.size();
switch(sz) {
case 0:
result = m().mk_true();
return BR_DONE;
case 1:
result = buffer.back();
return BR_DONE;
default:
if (s) {
result = m().mk_and(sz, buffer.c_ptr());
return BR_DONE;
}
return BR_FAILED;
}
}
br_status bool_rewriter::mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result) {
unsigned i;
for (i = 0; i < num_args; i++) {
if (m().is_and(args[i]))
break;
}
if (i < num_args) {
// has nested ANDs
ptr_buffer<expr> flat_args;
flat_args.append(i, args);
for (; i < num_args; i++) {
expr * arg = args[i];
// Remark: all rewrites are depth 1.
if (m().is_and(arg)) {
unsigned num = to_app(arg)->get_num_args();
for (unsigned j = 0; j < num; j++)
flat_args.push_back(to_app(arg)->get_arg(j));
}
else {
flat_args.push_back(arg);
}
}
if (mk_nflat_and_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED)
result = m().mk_and(flat_args.size(), flat_args.c_ptr());
return BR_DONE;
}
return mk_nflat_and_core(num_args, args, result);
}
br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result) {
bool s = false;
ptr_buffer<expr> buffer;
expr_fast_mark1 neg_lits;
expr_fast_mark2 pos_lits;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (m().is_true(arg)) {
result = m().mk_true();
return BR_DONE;
}
if (m().is_false(arg)) {
s = true;
continue;
}
if (m().is_not(arg)) {
expr * atom = to_app(arg)->get_arg(0);
if (neg_lits.is_marked(atom)) {
s = true;
continue;
}
if (pos_lits.is_marked(atom)) {
result = m().mk_true();
return BR_DONE;
}
neg_lits.mark(atom);
}
else {
if (pos_lits.is_marked(arg)) {
s = true;
continue;
}
if (neg_lits.is_marked(arg)) {
result = m().mk_true();
return BR_DONE;
}
pos_lits.mark(arg);
}
buffer.push_back(arg);
}
unsigned sz = buffer.size();
switch(sz) {
case 0:
result = m().mk_false();
return BR_DONE;
case 1:
result = buffer.back();
return BR_DONE;
default:
if (m_local_ctx && m_local_ctx_cost <= m_local_ctx_limit) {
neg_lits.reset();
pos_lits.reset();
if (local_ctx_simp(sz, buffer.c_ptr(), result))
return BR_DONE;
}
if (s) {
result = m().mk_or(sz, buffer.c_ptr());
return BR_DONE;
}
return BR_FAILED;
}
}
br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result) {
unsigned i;
for (i = 0; i < num_args; i++) {
if (m().is_or(args[i]))
break;
}
if (i < num_args) {
// has nested ORs
ptr_buffer<expr> flat_args;
flat_args.append(i, args);
for (; i < num_args; i++) {
expr * arg = args[i];
// Remark: all rewrites are depth 1.
if (m().is_or(arg)) {
unsigned num = to_app(arg)->get_num_args();
for (unsigned j = 0; j < num; j++)
flat_args.push_back(to_app(arg)->get_arg(j));
}
else {
flat_args.push_back(arg);
}
}
if (mk_nflat_or_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED)
result = m().mk_or(flat_args.size(), flat_args.c_ptr());
return BR_DONE;
}
return mk_nflat_or_core(num_args, args, result);
}
expr * bool_rewriter::mk_or_app(unsigned num_args, expr * const * args) {
switch(num_args) {
case 0: return m().mk_false();
case 1: return args[0];
default: return m().mk_or(num_args, args);
}
}
/**
\brief Auxiliary method for local_ctx_simp.
Replace args[i] by true if marked in neg_lits.
Replace args[i] by false if marked in pos_lits.
*/
bool bool_rewriter::simp_nested_not_or(unsigned num_args, expr * const * args,
expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) {
ptr_buffer<expr> new_args;
bool simp = false;
m_local_ctx_cost += num_args;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (neg_lits.is_marked(arg)) {
result = m().mk_false();
return true;
}
if (pos_lits.is_marked(arg)) {
simp = true;
continue;
}
if (m().is_not(arg)) {
expr * atom = to_app(arg)->get_arg(0);
if (neg_lits.is_marked(atom)) {
simp = true;
continue;
}
if (pos_lits.is_marked(atom)) {
result = m().mk_false();
return true;
}
}
new_args.push_back(arg);
}
if (simp) {
switch(new_args.size()) {
case 0:
result = m().mk_true();
return true;
case 1:
mk_not(new_args[0], result);
return true;
default:
result = m().mk_not(m().mk_or(new_args.size(), new_args.c_ptr()));
return true;
}
}
return false;
}
expr * bool_rewriter::simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified) {
if (m().is_not(arg)) {
expr * atom = to_app(arg)->get_arg(0);
if (neg_lits.is_marked(atom)) {
modified = true;
return m().mk_false();
}
if (pos_lits.is_marked(atom)) {
modified = true;
return m().mk_true();
}
return arg;
}
else {
if (neg_lits.is_marked(arg)) {
modified = true;
return m().mk_true();
}
if (pos_lits.is_marked(arg)) {
modified = true;
return m().mk_false();
}
return arg;
}
}
/**
\brief Simpler version of mk_ite, that will not invoke mk_or/mk_and.
It is used byt local_ctx_simp to prevent a recursive call to local_ctx_simp.
See comment at simp_nested_eq_ite.
*/
void bool_rewriter::mk_nested_ite(expr * c, expr * t, expr * e, expr_ref & result) {
if (m().is_true(c)) {
result = t;
return;
}
if (m().is_false(c)) {
result = e;
return;
}
if (t == e) {
result = t;
return;
}
if (m().is_bool(t)) {
if (m().is_true(t)) {
if (m().is_false(e)) {
result = c;
return;
}
result = m().mk_or(c, e);
return;
}
if (m().is_false(t)) {
if (m().is_true(e)) {
mk_not(c, result);
return;
}
expr_ref tmp(m());
mk_not(e, tmp);
result = m().mk_not(m().mk_or(c, tmp));
return;
}
if (m().is_true(e)) {
expr_ref tmp(m());
mk_not(c, tmp);
result = m().mk_or(tmp, t);
return;
}
if (m().is_false(e) || c == e) {
expr_ref tmp1(m());
expr_ref tmp2(m());
mk_not(c, tmp1);
mk_not(t, tmp2);
result = m().mk_not(m().mk_or(tmp1, tmp2));
return;
}
if (c == t) {
result = m().mk_or(c, e);
return;
}
if (m().is_complement_core(t, e)) { // t = not(e)
mk_eq(c, t, result);
return;
}
if (m().is_complement_core(e, t)) { // e = not(t)
mk_eq(c, t, result);
return;
}
}
result = m().mk_ite(c, t, e);
}
bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) {
bool neg = false;
m_local_ctx_cost += 3;
if (m().is_not(t)) {
neg = true;
t = to_app(t)->get_arg(0);
}
if (m().is_iff(t) || m().is_eq(t)) {
bool modified = false;
expr * new_lhs = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified);
expr * new_rhs = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified);
if (!modified)
return false;
mk_eq(new_lhs, new_rhs, result);
if (neg)
mk_not(result, result);
return true;
}
if (m().is_ite(t)) {
bool modified = false;
expr * new_c = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified);
expr * new_t = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified);
expr * new_e = simp_arg(to_app(t)->get_arg(2), neg_lits, pos_lits, modified);
if (!modified)
return false;
// It is not safe to invoke mk_ite here, since it can recursively call
// local_ctx_simp by
// - transforming the ITE into an OR
// - and invoked mk_or, that will invoke local_ctx_simp
// mk_ite(new_c, new_t, new_e, result);
mk_nested_ite(new_c, new_t, new_e, result);
if (neg)
mk_not(result, result);
return true;
}
return false;
}
/**
\brief Apply local context simplification at (OR args[0] ... args[num_args-1])
Basic idea:
- Replace args[i] by false in the other arguments
- If args[i] is of the form (not t), then replace t by true in the other arguments.
To make sure the simplification is efficient we bound the depth.
*/
bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref_vector old_args(m());
expr_ref_vector new_args(m());
expr_ref new_arg(m());
expr_fast_mark1 neg_lits;
expr_fast_mark2 pos_lits;
bool simp = false;
bool modified = false;
bool forward = true;
unsigned rounds = 0;
while (true) {
rounds++;
#if 0
if (rounds > 10)
verbose_stream() << "rounds: " << rounds << "\n";
#endif
#define PUSH_NEW_ARG(ARG) { \
new_args.push_back(ARG); \
if (m().is_not(ARG)) \
neg_lits.mark(to_app(ARG)->get_arg(0)); \
else \
pos_lits.mark(ARG); \
}
#define PROCESS_ARG() \
{ \
expr * arg = args[i]; \
if (m().is_not(arg) && m().is_or(to_app(arg)->get_arg(0)) && \
simp_nested_not_or(to_app(to_app(arg)->get_arg(0))->get_num_args(), \
to_app(to_app(arg)->get_arg(0))->get_args(), \
neg_lits, \
pos_lits, \
new_arg)) { \
modified = true; simp = true; \
arg = new_arg; \
} \
if (simp_nested_eq_ite(arg, neg_lits, pos_lits, new_arg)) { \
modified = true; simp = true; \
arg = new_arg; \
} \
if (m().is_false(arg)) \
continue; \
if (m().is_true(arg)) { \
result = arg; \
return true; \
} \
if (m_flat && m().is_or(arg)) { \
unsigned sz = to_app(arg)->get_num_args(); \
for (unsigned j = 0; j < sz; j++) { \
expr * arg_arg = to_app(arg)->get_arg(j); \
PUSH_NEW_ARG(arg_arg); \
} \
} \
else { \
PUSH_NEW_ARG(arg); \
} \
}
m_local_ctx_cost += 2*num_args;
#if 0
static unsigned counter = 0;
counter++;
if (counter % 10000 == 0)
verbose_stream() << "local-ctx-cost: " << m_local_ctx_cost << "\n";
#endif
if (forward) {
for (unsigned i = 0; i < num_args; i++) {
PROCESS_ARG();
}
forward = false;
}
else {
unsigned i = num_args;
while (i > 0) {
--i;
PROCESS_ARG();
}
if (!modified) {
if (simp) {
result = mk_or_app(num_args, args);
return true;
}
return false; // didn't simplify
}
// preserve the original order...
std::reverse(new_args.c_ptr(), new_args.c_ptr() + new_args.size());
modified = false;
forward = true;
}
pos_lits.reset();
neg_lits.reset();
old_args.reset();
old_args.swap(new_args);
SASSERT(new_args.empty());
args = old_args.c_ptr();
num_args = old_args.size();
}
}
/**
\brief Apply simplification if ite is an if-then-else tree where every leaf is a value.
This is an efficient way to
*/
br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) {
SASSERT(m().is_ite(ite));
SASSERT(m().is_value(val));
expr * t = ite->get_arg(1);
expr * e = ite->get_arg(2);
if (!m().is_value(t) || !m().is_value(e))
return BR_FAILED;
if (t != val && e != val) {
TRACE("try_ite_value", tout << mk_ismt2_pp(t, m()) << " " << mk_ismt2_pp(e, m()) << " " << mk_ismt2_pp(val, m()) << "\n";
tout << t << " " << e << " " << val << "\n";);
result = m().mk_false();
return BR_DONE;
}
if (t == val && e == val) {
result = m().mk_true();
return BR_DONE;
}
if (t == val) {
result = ite->get_arg(0);
return BR_DONE;
}
SASSERT(e == val);
mk_not(ite->get_arg(0), result);
return BR_DONE;
}
#if 0
// Return true if ite is an if-then-else tree where the leaves are values,
// and they are all different from val
static bool is_ite_value_tree_neq_value(ast_manager & m, app * ite, app * val) {
SASSERT(m.is_ite(ite));
SASSERT(m.is_value(val));
expr_fast_mark1 visited;
ptr_buffer<app> todo;
todo.push_back(ite);
#define VISIT(ARG) { \
if (m.is_value(ARG)) { \
if (ARG == val) \
return false; \
} \
else if (m.is_ite(ARG)) { \
if (!visited.is_marked(ARG)) { \
visited.mark(ARG); \
todo.push_back(to_app(ARG)); \
} \
} \
else { \
return false; \
} \
}
while (!todo.empty()) {
app * ite = todo.back();
todo.pop_back();
SASSERT(m.is_ite(ite));
expr * t = ite->get_arg(1);
expr * e = ite->get_arg(2);
VISIT(t);
VISIT(e);
}
return true;
}
#endif
br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
if (lhs == rhs) {
result = m().mk_true();
return BR_DONE;
}
if (m().are_distinct(lhs, rhs)) {
result = m().mk_false();
return BR_DONE;
}
br_status r = BR_FAILED;
if (m().is_ite(lhs) && m().is_value(rhs)) {
// if (is_ite_value_tree_neq_value(m(), to_app(lhs), to_app(rhs))) {
// result = m().mk_false();
// return BR_DONE;
// }
r = try_ite_value(to_app(lhs), to_app(rhs), result);
CTRACE("try_ite_value", r != BR_FAILED,
tout << mk_ismt2_pp(lhs, m()) << "\n" << mk_ismt2_pp(rhs, m()) << "\n--->\n" << mk_ismt2_pp(result, m()) << "\n";);
}
else if (m().is_ite(rhs) && m().is_value(lhs)) {
// if (is_ite_value_tree_neq_value(m(), to_app(rhs), to_app(lhs))) {
// result = m().mk_false();
// return BR_DONE;
// }
r = try_ite_value(to_app(rhs), to_app(lhs), result);
CTRACE("try_ite_value", r != BR_FAILED,
tout << mk_ismt2_pp(lhs, m()) << "\n" << mk_ismt2_pp(rhs, m()) << "\n--->\n" << mk_ismt2_pp(result, m()) << "\n";);
}
if (r != BR_FAILED)
return r;
if (m().is_bool(lhs)) {
bool unfolded = false;
if (m().is_not(lhs) && m().is_not(rhs)) {
lhs = to_app(lhs)->get_arg(0);
rhs = to_app(rhs)->get_arg(0);
unfolded = true;
}
if (m().is_true(lhs)) {
result = rhs;
return BR_DONE;
}
if (m().is_false(lhs)) {
mk_not(rhs, result);
return BR_DONE;
}
if (m().is_true(rhs)) {
result = lhs;
return BR_DONE;
}
if (m().is_false(rhs)) {
mk_not(lhs, result);
return BR_DONE;
}
if (m().is_complement(lhs, rhs)) {
result = m().mk_false();
return BR_DONE;
}
if (unfolded) {
result = m().mk_eq(lhs, rhs);
return BR_DONE;
}
}
return BR_FAILED;
}
br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args, expr_ref & result) {
if (num_args <= 1) {
result = m().mk_true();
return BR_DONE;
}
if (num_args == 2) {
expr_ref tmp(m());
result = m().mk_not(m().mk_eq(args[0], args[1]));
return BR_REWRITE2; // mk_eq may be dispatched to other rewriters.
}
expr_fast_mark1 visited;
bool all_value = true;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
if (visited.is_marked(arg)) {
result = m().mk_false();
return BR_DONE;
}
visited.mark(arg);
if (!m().is_value(arg))
all_value = false;
}
if (all_value) {
result = m().mk_true();
return BR_DONE;
}
SASSERT(num_args > 2);
if (m().is_bool(args[0])) {
result = m().mk_false();
return BR_DONE;
}
if (m_blast_distinct) {
ptr_buffer<expr> new_diseqs;
for (unsigned i = 0; i < num_args; i++) {
for (unsigned j = i + 1; j < num_args; j++)
new_diseqs.push_back(m().mk_not(m().mk_eq(args[i], args[j])));
}
result = m().mk_and(new_diseqs.size(), new_diseqs.c_ptr());
return BR_REWRITE3;
}
return BR_FAILED;
}
br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) {
bool s = false;
// (ite (not c) a b) ==> (ite c b a)
if (m().is_not(c)) {
c = to_app(c)->get_arg(0);
std::swap(t, e);
s = true;
}
// (ite c (ite c t1 t2) t3) ==> (ite c t1 t3)
if (m().is_ite(t) && to_app(t)->get_arg(0) == c) {
// Remark: (ite c (ite (not c) t1 t2) t3) ==> (ite c t2 t3) does not happen if applying rewrites bottom up
t = to_app(t)->get_arg(1);
s = true;
}
// (ite c t1 (ite c t2 t3)) ==> (ite c t1 t3)
if (m().is_ite(e) && to_app(e)->get_arg(0) == c) {
// Remark: (ite c t1 (ite (not c) t2 t3)) ==> (ite c t1 t2) does not happen if applying rewrites bottom up
e = to_app(e)->get_arg(2);
s = true;
}
if (m().is_true(c)) {
result = t;
return BR_DONE;
}
if (m().is_false(c)) {
result = e;
return BR_DONE;
}
if (t == e) {
result = t;
return BR_DONE;
}
if (m().is_bool(t)) {
if (m().is_true(t)) {
if (m().is_false(e)) {
result = c;
return BR_DONE;
}
mk_or(c, e, result);
return BR_DONE;
}
if (m().is_false(t)) {
if (m().is_true(e)) {
mk_not(c, result);
return BR_DONE;
}
expr_ref tmp(m());
mk_not(c, tmp);
mk_and(tmp, e, result);
return BR_DONE;
}
if (m().is_true(e)) {
expr_ref tmp(m());
mk_not(c, tmp);
mk_or(tmp, t, result);
return BR_DONE;
}
if (m().is_false(e)) {
mk_and(c, t, result);
return BR_DONE;
}
if (c == e) {
mk_and(c, t, result);
return BR_DONE;
}
if (c == t) {
mk_or(c, e, result);
return BR_DONE;
}
if (m().is_complement_core(t, e)) { // t = not(e)
mk_eq(c, t, result);
return BR_DONE;
}
if (m().is_complement_core(e, t)) { // e = not(t)
mk_eq(c, t, result);
return BR_DONE;
}
}
if (m().is_ite(t) && m_ite_extra_rules) {
// (ite c1 (ite c2 t1 t2) t1) ==> (ite (and c1 (not c2)) t2 t1)
if (e == to_app(t)->get_arg(1)) {
expr_ref not_c2(m());
mk_not(to_app(t)->get_arg(0), not_c2);
expr_ref new_c(m());
mk_and(c, not_c2, new_c);
result = m().mk_ite(new_c, to_app(t)->get_arg(2), e);
return BR_REWRITE1;
}
// (ite c1 (ite c2 t1 t2) t2) ==> (ite (and c1 c2) t1 t2)
if (e == to_app(t)->get_arg(2)) {
expr_ref new_c(m());
mk_and(c, to_app(t)->get_arg(0), new_c);
result = m().mk_ite(new_c, to_app(t)->get_arg(1), e);
return BR_REWRITE1;
}
if (m().is_ite(e)) {
// (ite c1 (ite c2 t1 t2) (ite c3 t1 t2)) ==> (ite (or (and c1 c2) (and (not c1) c3)) t1 t2)
if (to_app(t)->get_arg(1) == to_app(e)->get_arg(1) &&
to_app(t)->get_arg(2) == to_app(e)->get_arg(2)) {
expr_ref and1(m());
expr_ref and2(m());
expr_ref notc(m());
mk_and(c, to_app(t)->get_arg(0), and1);
mk_not(c, notc);
mk_and(notc, to_app(e)->get_arg(0), and2);
expr_ref new_c(m());
mk_or(and1, and2, new_c);
result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2));
return BR_REWRITE1;
}
// (ite c1 (ite c2 t1 t2) (ite c3 t2 t1)) ==> (ite (or (and c1 c2) (and (not c1) (not c3))) t1 t2)
if (to_app(t)->get_arg(1) == to_app(e)->get_arg(2) &&
to_app(t)->get_arg(2) == to_app(e)->get_arg(1)) {
expr_ref and1(m());
expr_ref and2(m());
expr_ref notc(m());
mk_and(c, to_app(t)->get_arg(0), and1);
mk_not(c, notc);
expr_ref notc3(m());
mk_not(to_app(e)->get_arg(0), notc3);
mk_and(notc, notc3, and2);
expr_ref new_c(m());
mk_or(and1, and2, new_c);
result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2));
return BR_REWRITE1;
}
}
}
if (m().is_ite(e) && m_ite_extra_rules) {
// (ite c1 t1 (ite c2 t1 t2)) ==> (ite (or c1 c2) t1 t2)
if (t == to_app(e)->get_arg(1)) {
expr_ref new_c(m());
mk_or(c, to_app(e)->get_arg(0), new_c);
result = m().mk_ite(new_c, t, to_app(e)->get_arg(2));
return BR_REWRITE1;
}
// (ite c1 t1 (ite c2 t2 t1)) ==> (ite (or c1 (not c2)) t1 t2)
if (t == to_app(e)->get_arg(2)) {
expr_ref not_c2(m());
mk_not(to_app(e)->get_arg(0), not_c2);
expr_ref new_c(m());
mk_or(c, not_c2, new_c);
result = m().mk_ite(new_c, t, to_app(e)->get_arg(1));
return BR_REWRITE1;
}
}
if (s) {
result = m().mk_ite(c, t, e);
return BR_DONE;
}
return BR_FAILED;
}
br_status bool_rewriter::mk_not_core(expr * t, expr_ref & result) {
if (m().is_not(t)) {
result = to_app(t)->get_arg(0);
return BR_DONE;
}
if (m().is_true(t)) {
result = m().mk_false();
return BR_DONE;
}
if (m().is_false(t)) {
result = m().mk_true();
return BR_DONE;
}
if (is_eq(t) && m().is_bool(to_app(t)->get_arg(0))) {
expr_ref tmp(m());
mk_not(to_app(t)->get_arg(0), tmp);
mk_eq(tmp, to_app(t)->get_arg(1), result);
return BR_DONE;
}
return BR_FAILED;
}
void bool_rewriter::mk_xor(expr * lhs, expr * rhs, expr_ref & result) {
expr_ref tmp(m());
mk_not(lhs, tmp);
mk_eq(tmp, rhs, result);
}
void bool_rewriter::mk_implies(expr * lhs, expr * rhs, expr_ref & result) {
expr_ref tmp(m());
mk_not(lhs, tmp);
mk_or(tmp, rhs, result);
}
void bool_rewriter::mk_nand(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref tmp(m_manager);
mk_and(num_args, args, tmp);
mk_not(tmp, result);
}
void bool_rewriter::mk_nor(unsigned num_args, expr * const * args, expr_ref & result) {
expr_ref tmp(m_manager);
mk_or(num_args, args, tmp);
mk_not(tmp, result);
}
void bool_rewriter::mk_nand(expr * arg1, expr * arg2, expr_ref & result) {
expr_ref tmp(m_manager);
mk_and(arg1, arg2, tmp);
mk_not(tmp, result);
}
void bool_rewriter::mk_nor(expr * arg1, expr * arg2, expr_ref & result) {
expr_ref tmp(m_manager);
mk_or(arg1, arg2, tmp);
mk_not(tmp, result);
}
template class rewriter_tpl<bool_rewriter_cfg>;