/*++ 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 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 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 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 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 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 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 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;