/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2real_rewriter.cpp Abstract: Basic rewriting rules for bv2real propagation. Author: Nikolaj (nbjorner) 2011-08-05 Notes: --*/ #include"bv2real_rewriter.h" #include"rewriter_def.h" #include"ast_pp.h" #include"for_each_expr.h" bv2real_util::bv2real_util(ast_manager& m, rational const& default_root, rational const& default_divisor, unsigned max_num_bits) : m_manager(m), m_arith(m), m_bv(m), m_decls(m), m_pos_le(m), m_pos_lt(m), m_side_conditions(m), m_default_root(default_root), m_default_divisor(default_divisor), m_max_divisor(rational(2)*default_divisor), m_max_num_bits(max_num_bits) { sort* real = m_arith.mk_real(); sort* domain[2] = { real, real }; m_pos_lt = m.mk_fresh_func_decl("<","",2,domain,m.mk_bool_sort()); m_pos_le = m.mk_fresh_func_decl("<=","",2,domain,m.mk_bool_sort()); m_decls.push_back(m_pos_lt); m_decls.push_back(m_pos_le); } bool bv2real_util::is_bv2real(func_decl* f) const { return m_decl2sig.contains(f); } bool bv2real_util::is_bv2real(func_decl* f, unsigned num_args, expr* const* args, expr*& m, expr*& n, rational& d, rational& r) const { bvr_sig sig; if (!m_decl2sig.find(f, sig)) { return false; } SASSERT(num_args == 2); m = args[0]; n = args[1]; d = sig.m_d; r = sig.m_r; SASSERT(sig.m_d.is_int() && sig.m_d.is_pos()); SASSERT(sig.m_r.is_int() && sig.m_r.is_pos()); SASSERT(m_bv.get_bv_size(m) == sig.m_msz); SASSERT(m_bv.get_bv_size(n) == sig.m_nsz); return true; } bool bv2real_util::is_bv2real(expr* e, expr*& m, expr*& n, rational& d, rational& r) const { if (!is_app(e)) return false; func_decl* f = to_app(e)->get_decl(); return is_bv2real(f, to_app(e)->get_num_args(), to_app(e)->get_args(), m, n, d, r); } class bv2real_util::contains_bv2real_proc { bv2real_util const& m_util; public: class found {}; contains_bv2real_proc(bv2real_util const& u): m_util(u) {} void operator()(app* a) { if (m_util.is_bv2real(a->get_decl())) { throw found(); } } void operator()(var*) {} void operator()(quantifier*) {} }; bool bv2real_util::contains_bv2real(expr* e) const { contains_bv2real_proc p(*this); try { for_each_expr(p, e); } catch (contains_bv2real_proc::found) { return true; } return false; } bool bv2real_util::mk_bv2real(expr* _s, expr* _t, rational& d, rational& r, expr_ref& result) { expr_ref s(_s,m()), t(_t,m()); if (align_divisor(s, t, d)) { result = mk_bv2real_c(s, t, d, r); return true; } else { return false; } } expr* bv2real_util::mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r) { bvr_sig sig; sig.m_msz = m_bv.get_bv_size(s); sig.m_nsz = m_bv.get_bv_size(t); sig.m_d = d; sig.m_r = r; func_decl* f; if (!m_sig2decl.find(sig, f)) { sort* domain[2] = { m_manager.get_sort(s), m_manager.get_sort(t) }; sort* real = m_arith.mk_real(); f = m_manager.mk_fresh_func_decl("bv2real", "", 2, domain, real); m_decls.push_back(f); m_sig2decl.insert(sig, f); m_decl2sig.insert(f, sig); } return m_manager.mk_app(f, s, t); } void bv2real_util::mk_bv2real_reduced(expr* s, expr* t, rational const& d, rational const& r, expr_ref & result) { expr_ref s1(m()), t1(m()), r1(m()); rational num; mk_sbv2real(s, s1); mk_sbv2real(t, t1); mk_div(s1, d, s1); mk_div(t1, d, t1); r1 = a().mk_power(a().mk_numeral(r, false), a().mk_numeral(rational(1,2),false)); t1 = a().mk_mul(t1, r1); result = a().mk_add(s1, t1); } void bv2real_util::mk_div(expr* e, rational const& d, expr_ref& result) { result = a().mk_div(e, a().mk_numeral(rational(d), false)); } void bv2real_util::mk_sbv2real(expr* e, expr_ref& result) { rational r; unsigned bv_size = m_bv.get_bv_size(e); rational bsize = power(rational(2), bv_size); expr_ref bvr(a().mk_to_real(m_bv.mk_bv2int(e)), m()); expr_ref c(m_bv.mk_sle(m_bv.mk_numeral(rational(0), bv_size), e), m()); result = m().mk_ite(c, bvr, a().mk_sub(bvr, a().mk_numeral(bsize, false))); } expr* bv2real_util::mk_bv_mul(rational const& n, expr* t) { if (n.is_one()) return t; expr* s = mk_sbv(n); return mk_bv_mul(s, t); } void bv2real_util::align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr_ref& t2, rational& d1, rational& d2) { if (d1 == d2) { return; } // s/d1 ~ t/d2 <=> lcm*s/d1 ~ lcm*t/d2 <=> (lcm/d1)*s ~ (lcm/d2)*t // s/d1 ~ t/d2 <=> s/gcd*d1' ~ t/gcd*d2' <=> d2'*s/lcm ~ d1'*t/lcm rational g = gcd(d1,d2); rational l = lcm(d1,d2); rational d1g = d1/g; rational d2g = d2/g; s1 = mk_bv_mul(d2g, s1); s2 = mk_bv_mul(d2g, s2); t1 = mk_bv_mul(d1g, t1); t2 = mk_bv_mul(d1g, t2); d1 = l; d2 = l; } expr* bv2real_util::mk_bv_mul(expr* s, expr* t) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return s; } if (is_zero(t)) { return t; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); unsigned n = m_bv.get_bv_size(t1); unsigned max_bits = get_max_num_bits(); bool add_side_conds = 2*n > max_bits; if (n >= max_bits) { // nothing } else if (2*n > max_bits) { s1 = mk_extend(max_bits-n, s1); t1 = mk_extend(max_bits-n, t1); } else { s1 = mk_extend(n, s1); t1 = mk_extend(n, t1); } if (add_side_conds) { add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1)); add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1)); } return m_bv.mk_bv_mul(s1, t1); } bool bv2real_util::is_zero(expr* n) { rational r; unsigned sz; return m_bv.is_numeral(n, r, sz) && r.is_zero(); } expr* bv2real_util::mk_bv_add(expr* s, expr* t) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return t; } if (is_zero(t)) { return s; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); s1 = mk_extend(1, s1); t1 = mk_extend(1, t1); return m_bv.mk_bv_add(s1, t1); } void bv2real_util::align_sizes(expr_ref& s, expr_ref& t) { unsigned sz1 = m_bv.get_bv_size(s); unsigned sz2 = m_bv.get_bv_size(t); if (sz1 > sz2) { t = mk_extend(sz1-sz2, t); } else if (sz1 < sz2) { s = mk_extend(sz2-sz1, s); } } expr* bv2real_util::mk_sbv(rational const& n) { SASSERT(n.is_int()); if (n.is_neg()) { rational m = abs(n); unsigned nb = m.get_num_bits(); return m_bv.mk_bv_neg(m_bv.mk_numeral(m, nb+1)); } else { unsigned nb = n.get_num_bits(); return m_bv.mk_numeral(n, nb+1); } } expr* bv2real_util::mk_bv_sub(expr* s, expr* t) { expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); s1 = mk_extend(1, s1); t1 = mk_extend(1, t1); return m_bv.mk_bv_sub(s1, t1); } expr* bv2real_util::mk_extend(unsigned sz, expr* b) { if (sz == 0) { return b; } rational r; unsigned bv_sz; if (m_bv.is_numeral(b, r, bv_sz) && power(rational(2),bv_sz-1) > r) { return m_bv.mk_numeral(r, bv_sz + sz); } return m_bv.mk_sign_extend(sz, b); } bool bv2real_util::is_bv2real(expr* n, expr_ref& s, expr_ref& t, rational& d, rational& r) { expr* _s, *_t; if (is_bv2real(n, _s, _t, d, r)) { s = _s; t = _t; return true; } rational k; bool is_int; if (m_arith.is_numeral(n, k, is_int) && !is_int) { d = denominator(k); r = default_root(); s = mk_sbv(numerator(k)); t = mk_sbv(rational(0)); return true; } return false; } bool bv2real_util::align_divisor(expr_ref& s, expr_ref& t, rational& d) { if (d > max_divisor()) { // // if divisor is over threshold, then divide s and t // add side condition that s, t are divisible. // rational overflow = d / max_divisor(); if (!overflow.is_int()) return false; if (!mk_is_divisible_by(s, overflow)) return false; if (!mk_is_divisible_by(t, overflow)) return false; d = max_divisor(); } return true; } bool bv2real_util::mk_is_divisible_by(expr_ref& s, rational const& _overflow) { rational overflow(_overflow); SASSERT(overflow.is_int()); SASSERT(overflow.is_pos()); SASSERT(!overflow.is_one()); TRACE("bv2real_rewriter", tout << mk_pp(s, m()) << " " << overflow << "\n";); unsigned power2 = 0; while ((overflow % rational(2)) == rational(0)) { power2++; overflow = div(overflow, rational(2)); } if (power2 > 0) { unsigned sz = m_bv.get_bv_size(s); if (sz <= power2) { add_side_condition(m().mk_eq(s, m_bv.mk_numeral(rational(0), sz))); s = m_bv.mk_numeral(rational(0), 1); } else { expr* s1 = m_bv.mk_extract(power2-1, 0, s); add_side_condition(m().mk_eq(s1, m_bv.mk_numeral(rational(0), power2))); s = m_bv.mk_extract(sz-1, power2, s); } } TRACE("bv2real_rewriter", tout << mk_pp(s, m()) << " " << overflow << "\n";); return overflow.is_one(); } // --------------------------------------------------------------------- // bv2real_rewriter bv2real_rewriter::bv2real_rewriter(ast_manager& m, bv2real_util& util): m_manager(m), m_util(util), m_bv(m), m_arith(m) {} br_status bv2real_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { TRACE("bv2real_rewriter", tout << mk_pp(f, m()) << " "; for (unsigned i = 0; i < num_args; ++i) { tout << mk_pp(args[i], m()) << " "; } tout << "\n";); if(f->get_family_id() == m_arith.get_family_id()) { switch (f->get_decl_kind()) { case OP_NUM: return BR_FAILED; case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result); case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result); case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result); case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result); case OP_ADD: return mk_add(num_args, args, result); case OP_MUL: return mk_mul(num_args, args, result); case OP_SUB: return mk_sub(num_args, args, result); case OP_DIV: SASSERT(num_args == 2); return mk_div(args[0], args[1], result); case OP_IDIV: return BR_FAILED; case OP_MOD: return BR_FAILED; case OP_REM: return BR_FAILED; case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result); case OP_TO_REAL: return BR_FAILED; // TBD case OP_TO_INT: return BR_FAILED; // TBD case OP_IS_INT: return BR_FAILED; // TBD default: return BR_FAILED; } } if (f->get_family_id() == m().get_basic_family_id()) { switch (f->get_decl_kind()) { case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result); case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result); default: return BR_FAILED; } } if (u().is_pos_ltf(f)) { SASSERT(num_args == 2); return mk_lt_pos(args[0], args[1], result); } if (u().is_pos_lef(f)) { SASSERT(num_args == 2); return mk_le_pos(args[0], args[1], result); } return BR_FAILED; } bool bv2real_rewriter::mk_le(expr* s, expr* t, bool is_pos, bool is_neg, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; SASSERT(is_pos || is_neg); if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2 && r1 == rational(2)) { // // (s1 + s2*sqrt(2))/d1 <= (t1 + t2*sqrt(2))/d2 // <=> // // let s1 = s1*d2-t1*d1, t2 = s2*d2-t2*d1 // // // s1 + s2*sqrt(2) <= 0 // <= // s1 + s2*approx(sign(s2),sqrt(2)) <= 0 // or (s1 = 0 & s2 = 0) // // If s2 is negative use an under-approximation for sqrt(r). // If s2 is positive use an over-approximation for sqrt(r). // e.g., r = 2, then 5/4 and 3/2 are under/over approximations. // Then s1 + s2*approx(sign(s2), r) <= 0 => s1 + s2*sqrt(r) <= 0 u().align_divisors(s1, s2, t1, t2, d1, d2); s1 = u().mk_bv_sub(s1, t1); s2 = u().mk_bv_sub(s2, t2); unsigned s2_size = m_bv.get_bv_size(s2); expr_ref le_proxy(m().mk_fresh_const("le_proxy",m().mk_bool_sort()), m()); u().add_aux_decl(to_app(le_proxy)->get_decl()); expr_ref gt_proxy(m().mk_not(le_proxy), m()); expr_ref s2_is_nonpos(m_bv.mk_sle(s2, m_bv.mk_numeral(rational(0), s2_size)), m()); expr_ref under(u().mk_bv_add(u().mk_bv_mul(rational(4), s1), u().mk_bv_mul(rational(5), s2)), m()); expr_ref z1(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(under)), m()); expr_ref le_under(m_bv.mk_sle(under, z1), m()); expr_ref over(u().mk_bv_add(u().mk_bv_mul(rational(2), s1), u().mk_bv_mul(rational(3), s2)), m()); expr_ref z2(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(over)), m()); expr_ref le_over(m_bv.mk_sle(over, z2), m()); // predicate may occur in positive polarity. if (is_pos) { // s1 + s2*sqrt(2) <= 0 <== s2 <= 0 & s1 + s2*(5/4) <= 0; 4*s1 + 5*s2 <= 0 expr* e1 = m().mk_implies(m().mk_and(le_proxy, s2_is_nonpos), le_under); // s1 + s2*sqrt(2) <= 0 <== s2 > 0 & s1 + s2*(3/2); 0 <=> 2*s1 + 3*s2 <= 0 expr* e2 = m().mk_implies(m().mk_and(le_proxy, m().mk_not(s2_is_nonpos)), le_over); u().add_side_condition(e1); u().add_side_condition(e2); } // predicate may occur in negative polarity. if (is_neg) { // s1 + s2*sqrt(2) > 0 <== s2 > 0 & s1 + s2*(5/4) > 0; 4*s1 + 5*s2 > 0 expr* e3 = m().mk_implies(m().mk_and(gt_proxy, m().mk_not(s2_is_nonpos)), m().mk_not(le_under)); // s1 + s2*sqrt(2) > 0 <== s2 <= 0 & s1 + s2*(3/2) > 0 <=> 2*s1 + 3*s2 > 0 expr* e4 = m().mk_implies(m().mk_and(gt_proxy, s2_is_nonpos), m().mk_not(le_over)); u().add_side_condition(e3); u().add_side_condition(e4); } TRACE("bv2real_rewriter", tout << "mk_le\n";); if (is_pos) { result = le_proxy; } else { result = gt_proxy; } return true; } return false; } br_status bv2real_rewriter::mk_le_pos(expr * s, expr * t, expr_ref & result) { if (mk_le(s, t, true, false, result)) { return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_lt_pos(expr * s, expr * t, expr_ref & result) { if (mk_le(t, s, false, true, result)) { return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_le(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (mk_le(s, t, true, true, result)) { return BR_DONE; } if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { // // somewhat expensive approach without having // polarity information for sound approximation. // // Convert to: // t1 + t2*sqrt(r) >= 0 // then to: // // (t1 >= 0 && t2 <= 0 => t1^2 >= t2^2*r) // (t1 <= 0 && t2 >= 0 => t1^2 <= t2^2*r) // (t1 >= 0 || t2 >= 0) // // A cheaper approach is to approximate > under the assumption // that > occurs in positive polarity. // then if t2 is negative use an over-approximation for sqrt(r) // if t2 is positive use an under-approximation for sqrt(r). // e.g., r = 2, then 5/4 and 3/2 are under/over approximations. // Then t1 + t2*approx(sign(t2), r) > 0 => t1 + t2*sqrt(r) > 0 // u().align_divisors(s1, s2, t1, t2, d1, d2); t1 = u().mk_bv_sub(t1, s1); t2 = u().mk_bv_sub(t2, s2); expr_ref z1(m()), z2(m()); z1 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t1)); z2 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t2)); expr* gz1 = m_bv.mk_sle(z1, t1); expr* lz1 = m_bv.mk_sle(t1, z1); expr* gz2 = m_bv.mk_sle(z2, t2); expr* lz2 = m_bv.mk_sle(t2, z2); expr_ref t12(u().mk_bv_mul(t1, t1), m()); expr_ref t22(u().mk_bv_mul(r1, u().mk_bv_mul(t2, t2)), m()); u().align_sizes(t12, t22); expr* ge = m_bv.mk_sle(t22, t12); expr* le = m_bv.mk_sle(t12, t22); expr* e1 = m().mk_or(gz1, gz2); expr* e2 = m().mk_or(m().mk_not(gz1), m().mk_not(lz2), ge); expr* e3 = m().mk_or(m().mk_not(gz2), m().mk_not(lz1), le); result = m().mk_and(e1, e2, e3); TRACE("bv2real_rewriter", tout << "\n";); return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg2, arg1)); return BR_REWRITE2; } br_status bv2real_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { return mk_le(arg2, arg1, result); } br_status bv2real_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg1, arg2)); return BR_REWRITE2; } br_status bv2real_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); u().align_sizes(s1, t1); u().align_sizes(s2, t2); if (u().mk_bv2real(m().mk_ite(c, s1, t1), m().mk_ite(c, s2, t2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); u().align_sizes(s1, t1); u().align_sizes(s2, t2); result = m().mk_and(m().mk_eq(s1, t1), m().mk_eq(s2, t2)); return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_uminus(expr * s, expr_ref & result) { expr_ref s1(m()), s2(m()); rational d1, r1; if (u().is_bv2real(s, s1, s2, d1, r1)) { s1 = u().mk_extend(1, s1); s2 = u().mk_extend(1, s2); if (u().mk_bv2real(m_bv.mk_bv_neg(s1), m_bv.mk_bv_neg(s2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_add(result, args[i], result); } return r; } br_status bv2real_rewriter::mk_add(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); if (u().mk_bv2real(u().mk_bv_add(s1, t1), u().mk_bv_add(t2, s2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_mul(result, args[i], result); } return r; } br_status bv2real_rewriter::mk_div(expr* s, expr* t, expr_ref& result) { return BR_FAILED; } br_status bv2real_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) { // TBD: optimize expr_ref s1(m()), t1(m()), s2(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { // s1*t1 + r1*(s2*t2) + (s1*t2 + s2*t2)*r1 expr_ref u1(m()), u2(m()); u1 = u().mk_bv_add(u().mk_bv_mul(s1, t1), u().mk_bv_mul(r1, u().mk_bv_mul(t2, s2))); u2 = u().mk_bv_add(u().mk_bv_mul(s1, t2), u().mk_bv_mul(s2, t1)); rational tmp = d1*d2; if (u().mk_bv2real(u1, u2, tmp, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_sub(result, args[i], result); } return r; } br_status bv2real_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); if (u().mk_bv2real(u().mk_bv_sub(s1, t1), u().mk_bv_sub(s2, t2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } template class rewriter_tpl; br_status bv2real_elim_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { expr* m, *n; rational d, r; if (m_util.is_bv2real(f, num_args, args, m, n, d, r)) { m_util.mk_bv2real_reduced(m, n, d, r, result); return BR_REWRITE_FULL; } return BR_FAILED; } template class rewriter_tpl;