/*++
Copyright (c) 2020 Microsoft Corporation

Module Name:

    bv2int_translator
    
Author:

    Nikolaj Bjorner (nbjorner) 2024-10-27

--*/

#include "ast/ast.h"
#include "ast/arith_decl_plugin.h"
#include "ast/bv_decl_plugin.h"
#include "ast/rewriter/bv2int_translator.h"
#include "ast/ast_ll_pp.h"
#include "ast/ast_pp.h"

bv2int_translator::bv2int_translator(ast_manager& m, bv2int_translator_trail& ctx) :
    m(m),
    ctx(ctx),
    bv(m),
    a(m),
    m_translate(m),
    m_args(m),
    m_pinned(m),
    m_vars(m),
    m_preds(m)
{}

void bv2int_translator::reset(bool is_plugin) {
    m_vars.reset();
    m_preds.reset();
    for (unsigned i = m_translate.size(); i-- > 0; )
        m_translate[i] = nullptr;
    m_is_plugin = is_plugin;
}


void bv2int_translator::set_translated(expr* e, expr* r) {
    SASSERT(r);
    SASSERT(!is_translated(e));
    m_translate.setx(e->get_id(), r);
    ctx.push_idx(set_vector_idx_trail(m_translate, e->get_id()));
}

void bv2int_translator::internalize_bv(app* e) {
    ensure_translated(e);
    if (m.is_bool(e)) {
        m_preds.push_back(e);
        ctx.push(push_back_vector(m_preds));
    }
}

void bv2int_translator::ensure_translated(expr* e) {
    if (m_translate.get(e->get_id(), nullptr))
        return;
    ptr_vector<expr> todo;
    ast_fast_mark1 visited;
    todo.push_back(e);
    visited.mark(e);
    for (unsigned i = 0; i < todo.size(); ++i) {
        expr* e = todo[i];
        if (!is_app(e))
            continue;
        app* a = to_app(e);
        if (m.is_bool(e) && a->get_family_id() != bv.get_family_id())
            continue;
        for (auto arg : *a)
            if (!visited.is_marked(arg) && !m_translate.get(arg->get_id(), nullptr)) {
                visited.mark(arg);
                todo.push_back(arg);
            }
    }
    std::stable_sort(todo.begin(), todo.end(), [&](expr* a, expr* b) { return get_depth(a) < get_depth(b); });
    for (expr* e : todo)
        translate_expr(e);
}

rational bv2int_translator::bv_size(expr* bv_expr) {
    return rational::power_of_two(bv.get_bv_size(bv_expr->get_sort()));
}

void bv2int_translator::translate_expr(expr* e) {
    if (is_quantifier(e))
        translate_quantifier(to_quantifier(e));
    else if (is_var(e))
        translate_var(to_var(e));
    else {
        app* ap = to_app(e);
        if (m_is_plugin && ap->get_family_id() == basic_family_id && m.is_bool(ap)) {
            set_translated(e, e);
            return;
        }
        m_args.reset();
        for (auto arg : *ap)
            m_args.push_back(translated(arg));

        if (ap->get_family_id() == basic_family_id)
            translate_basic(ap);
        else if (ap->get_family_id() == bv.get_family_id())
            translate_bv(ap);
        else
            translate_app(ap);
    }
}

void bv2int_translator::translate_quantifier(quantifier* q) {
    if (m_is_plugin) {
        set_translated(q, q);
        return;
    }
    if (is_lambda(q))
        throw default_exception("lambdas are not supported in intblaster");
    expr* b = q->get_expr();
    unsigned nd = q->get_num_decls();
    ptr_vector<sort> sorts;
    for (unsigned i = 0; i < nd; ++i) {
        auto s = q->get_decl_sort(i);
        if (bv.is_bv_sort(s)) {
            NOT_IMPLEMENTED_YET();
            sorts.push_back(a.mk_int());
        }
        else
            sorts.push_back(s);
    }
    b = translated(b);
    // TODO if sorts contain integer, then created bounds variables.       
    set_translated(q, m.update_quantifier(q, b));
}

void bv2int_translator::translate_var(var* v) {
    if (bv.is_bv_sort(v->get_sort()))
        set_translated(v, m.mk_var(v->get_idx(), a.mk_int()));
    else
        set_translated(v, v);
}

// Translate functions that are not built-in or bit-vectors.
// Base method uses fresh functions.
// Other method could use bv2int, int2bv axioms and coercions.
// f(args) = bv2int(f(int2bv(args'))
//

void bv2int_translator::translate_app(app* e) {

    if (m_is_plugin && m.is_bool(e)) {
        set_translated(e, e);
        return;
    }

    bool has_bv_sort = bv.is_bv(e);
    func_decl* f = e->get_decl();

    for (unsigned i = 0; i < m_args.size(); ++i)
        if (bv.is_bv(e->get_arg(i)))
            m_args[i] = bv.mk_int2bv(bv.get_bv_size(e->get_arg(i)), m_args.get(i));

    if (has_bv_sort)
        m_vars.push_back(e);
    if (m_is_plugin) {
        expr* r = m.mk_app(f, m_args);
        if (has_bv_sort) {
            ctx.push(push_back_vector(m_vars));
            r = bv.mk_bv2int(r);
        }
        set_translated(e, r);
        return;
    }
    else if (has_bv_sort) {
        if (f->get_family_id() != null_family_id)
            throw default_exception("conversion for interpreted functions is not supported by intblast solver");
        func_decl* g = nullptr;
        if (!m_new_funs.find(f, g)) {
            g = m.mk_fresh_func_decl(e->get_decl()->get_name(), symbol("bv"), f->get_arity(), f->get_domain(), a.mk_int());
            m_new_funs.insert(f, g);
        }
        f = g;
        m_pinned.push_back(f);
    }
    set_translated(e, m.mk_app(f, m_args));
}

expr_ref bv2int_translator::mk_le(expr* x, expr* y) {
    if (a.is_numeral(y))
        return expr_ref(a.mk_le(x, y), m);
    if (a.is_numeral(x))
        return expr_ref(a.mk_ge(y, x), m);
    return expr_ref(a.mk_le(a.mk_sub(x, y), a.mk_numeral(rational(0), x->get_sort())), m);
}

expr_ref bv2int_translator::mk_lt(expr* x, expr* y) {
    return expr_ref(m.mk_not(mk_le(y, x)), m);
}



void bv2int_translator::translate_bv(app* e) {

    auto bnot = [&](expr* e) {
        return a.mk_sub(a.mk_int(-1), e);
        };

    auto band = [&](expr_ref_vector const& args) {
        expr* r = arg(0);
        for (unsigned i = 1; i < args.size(); ++i)
            r = a.mk_band(bv.get_bv_size(e), r, arg(i));
        return r;
        };

    auto rotate_left = [&](unsigned n) {
        auto sz = bv.get_bv_size(e);
        n = n % sz;
        expr* r = arg(0);
        if (n != 0 && sz != 1) {
            // r[sz - n - 1 : 0] ++ r[sz - 1 : sz - n]
            // r * 2^(sz - n) + (r div 2^n) mod 2^(sz - n)???
            // r * A + (r div B) mod A
            auto N = bv_size(e);
            auto A = rational::power_of_two(sz - n);
            auto B = rational::power_of_two(n);
            auto hi = mul(r, a.mk_int(A));
            auto lo = amod(e, a.mk_idiv(umod(e, 0), a.mk_int(B)), A);
            r = add(hi, lo);
        }
        return r;
        };

    expr* bv_expr = e;
    expr_ref r(m);
    auto const& args = m_args;
    switch (e->get_decl_kind()) {
    case OP_BADD:
        r = a.mk_add(args);
        break;
    case OP_BSUB:
        r = a.mk_sub(args.size(), args.data());
        break;
    case OP_BMUL:
        r = a.mk_mul(args);
        break;
    case OP_ULEQ:
        bv_expr = e->get_arg(0);
        r = mk_le(umod(bv_expr, 0), umod(bv_expr, 1));
        break;
    case OP_UGEQ:
        bv_expr = e->get_arg(0);
        r = mk_ge(umod(bv_expr, 0), umod(bv_expr, 1));
        break;
    case OP_ULT:
        bv_expr = e->get_arg(0);
        r = mk_lt(umod(bv_expr, 0), umod(bv_expr, 1));
        break;
    case OP_UGT:
        bv_expr = e->get_arg(0);
        r = mk_gt(umod(bv_expr, 0), umod(bv_expr, 1));
        break;
    case OP_SLEQ:
        bv_expr = e->get_arg(0);
        r = mk_le(smod(bv_expr, 0), smod(bv_expr, 1));
        break;
    case OP_SGEQ:
        bv_expr = e->get_arg(0);
        r = mk_ge(smod(bv_expr, 0), smod(bv_expr, 1));
        break;
    case OP_SLT:
        bv_expr = e->get_arg(0);
        r = mk_lt(smod(bv_expr, 0), smod(bv_expr, 1));
        break;
    case OP_SGT:
        bv_expr = e->get_arg(0);
        r = mk_gt(smod(bv_expr, 0), smod(bv_expr, 1));
        break;
    case OP_BNEG:
        r = a.mk_uminus(arg(0));
        break;
    case OP_CONCAT: {
        unsigned sz = 0;
        expr_ref new_arg(m);
        for (unsigned i = args.size(); i-- > 0;) {
            expr* old_arg = e->get_arg(i);
            new_arg = umod(old_arg, i);
            if (sz > 0) {
                new_arg = mul(new_arg, a.mk_int(rational::power_of_two(sz)));
                r = add(r, new_arg);
            }
            else
                r = new_arg;
            sz += bv.get_bv_size(old_arg->get_sort());
        }
        break;
    }
    case OP_EXTRACT: {
        unsigned lo, hi;
        expr* old_arg;
        VERIFY(bv.is_extract(e, lo, hi, old_arg));
        r = arg(0);
        if (lo > 0)
            r = a.mk_idiv(r, a.mk_int(rational::power_of_two(lo)));
        break;
    }
    case OP_BV_NUM: {
        rational val;
        unsigned sz;
        VERIFY(bv.is_numeral(e, val, sz));
        r = a.mk_int(val);
        break;
    }
    case OP_BUREM:
    case OP_BUREM_I: {
        expr* x = umod(e, 0), * y = umod(e, 1);
        r = if_eq(y, 0, x, a.mk_mod(x, y));
        break;
    }
    case OP_BUDIV:
    case OP_BUDIV_I: {
        expr* x = umod(e, 0), * y = umod(e, 1);
        r = if_eq(y, 0, a.mk_int(-1), a.mk_idiv(x, y));
        break;
    }
    case OP_BUMUL_NO_OVFL: {
        bv_expr = e->get_arg(0);
        r = mk_lt(mul(umod(bv_expr, 0), umod(bv_expr, 1)), a.mk_int(bv_size(bv_expr)));
        break;
    }
    case OP_BSHL: {
        if (!a.is_numeral(arg(0)) && !a.is_numeral(arg(1)))
            r = a.mk_shl(bv.get_bv_size(e), arg(0), arg(1));
        else {
            expr* x = arg(0), * y = umod(e, 1);
            r = a.mk_int(0);
            for (unsigned i = 0; i < bv.get_bv_size(e); ++i)
                r = if_eq(y, i, mul(x, a.mk_int(rational::power_of_two(i))), r);
        }
        break;
    }
    case OP_BNOT:
        r = bnot(arg(0));
        break;
    case OP_BLSHR:
        if (!a.is_numeral(arg(0)) && !a.is_numeral(arg(1)))
            r = a.mk_lshr(bv.get_bv_size(e), arg(0), arg(1));
        else {
            expr* x = arg(0), * y = umod(e, 1);
            r = a.mk_int(0);
            IF_VERBOSE(4, verbose_stream() << "lshr " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
            for (unsigned i = 0; i < bv.get_bv_size(e); ++i)
                r = if_eq(y, i, a.mk_idiv(x, a.mk_int(rational::power_of_two(i))), r);
        }
        break;
    case OP_BASHR:
        if (!a.is_numeral(arg(1)))
            r = a.mk_ashr(bv.get_bv_size(e), arg(0), arg(1));
        else {

            //
            // ashr(x, y)
            // if y = k & x >= 0 -> x / 2^k   
            // if y = k & x < 0  -> (x / 2^k) - 2^{N-k}
            //
            unsigned sz = bv.get_bv_size(e);
            rational N = bv_size(e);
            expr* x = umod(e, 0), * y = umod(e, 1);
            expr* signx = a.mk_ge(x, a.mk_int(N / 2));
            r = m.mk_ite(signx, a.mk_int(-1), a.mk_int(0));
            IF_VERBOSE(4, verbose_stream() << "ashr " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
            for (unsigned i = 0; i < sz; ++i) {
                expr* d = a.mk_idiv(x, a.mk_int(rational::power_of_two(i)));
                r = if_eq(y, i,
                    m.mk_ite(signx, add(d, a.mk_int(-rational::power_of_two(sz - i))), d),
                    r);
            }
        }
        break;
    case OP_BOR:
        // p | q := (p + q) - band(p, q)
        IF_VERBOSE(4, verbose_stream() << "bor " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
        r = arg(0);
        for (unsigned i = 1; i < args.size(); ++i)
            r = a.mk_sub(add(r, arg(i)), a.mk_band(bv.get_bv_size(e), r, arg(i)));
        break;
    case OP_BNAND:
        r = bnot(band(args));
        break;
    case OP_BAND:
        IF_VERBOSE(4, verbose_stream() << "band " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
        r = band(args);
        break;
    case OP_BXNOR:
    case OP_BXOR: {
        // p ^ q := (p + q) - 2*band(p, q);
        unsigned sz = bv.get_bv_size(e);
        IF_VERBOSE(4, verbose_stream() << "bxor " << bv.get_bv_size(e) << "\n");
        r = arg(0);
        for (unsigned i = 1; i < args.size(); ++i) {
            expr* q = arg(i);
            r = a.mk_sub(add(r, q), mul(a.mk_int(2), a.mk_band(sz, r, q)));
        }
        if (e->get_decl_kind() == OP_BXNOR)
            r = bnot(r);
        break;
    }
    case OP_ZERO_EXT:
        bv_expr = e->get_arg(0);
        r = umod(bv_expr, 0);
        SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr));
        break;
    case OP_SIGN_EXT: {
        bv_expr = e->get_arg(0);
        r = umod(bv_expr, 0);
        SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr));
        unsigned arg_sz = bv.get_bv_size(bv_expr);
        //unsigned sz = bv.get_bv_size(e);
        // rational N = rational::power_of_two(sz);
        rational M = rational::power_of_two(arg_sz);
        expr* signbit = a.mk_ge(r, a.mk_int(M / 2));
        r = m.mk_ite(signbit, a.mk_sub(r, a.mk_int(M)), r);
        break;
    }
    case OP_INT2BV:
        m_int2bv.push_back(e);
        ctx.push(push_back_vector(m_int2bv));
        r = arg(0);
        break;
    case OP_BV2INT:
        m_bv2int.push_back(e);
        ctx.push(push_back_vector(m_bv2int));
        r = umod(e->get_arg(0), 0);
        break;
    case OP_BCOMP:
        bv_expr = e->get_arg(0);
        r = m.mk_ite(m.mk_eq(umod(bv_expr, 0), umod(bv_expr, 1)), a.mk_int(1), a.mk_int(0));
        break;
    case OP_BSMOD_I:
    case OP_BSMOD: {
        expr* x = umod(e, 0), * y = umod(e, 1);
        rational N = bv_size(e);
        expr* signx = a.mk_ge(x, a.mk_int(N / 2));
        expr* signy = a.mk_ge(y, a.mk_int(N / 2));
        expr* u = a.mk_mod(x, y);
        // u = 0 ->  0
        // y = 0 ->  x
        // x < 0, y < 0 ->  -u
        // x < 0, y >= 0 ->  y - u
        // x >= 0, y < 0 ->  y + u
        // x >= 0, y >= 0 ->  u
        r = a.mk_uminus(u);
        r = m.mk_ite(m.mk_and(m.mk_not(signx), signy), add(u, y), r);
        r = m.mk_ite(m.mk_and(signx, m.mk_not(signy)), a.mk_sub(y, u), r);
        r = m.mk_ite(m.mk_and(m.mk_not(signx), m.mk_not(signy)), u, r);
        r = if_eq(u, 0, a.mk_int(0), r);
        r = if_eq(y, 0, x, r);
        break;
    }
    case OP_BSDIV_I:
    case OP_BSDIV: {
        // d = udiv(abs(x), abs(y))
        // y = 0, x > 0 -> 1
        // y = 0, x <= 0 -> -1
        // x = 0, y != 0 -> 0
        // x > 0, y < 0 -> -d
        // x < 0, y > 0 -> -d
        // x > 0, y > 0 -> d
        // x < 0, y < 0 -> d
        expr* x = umod(e, 0), * y = umod(e, 1);
        rational N = bv_size(e);
        expr* signx = a.mk_ge(x, a.mk_int(N / 2));
        expr* signy = a.mk_ge(y, a.mk_int(N / 2));
        x = m.mk_ite(signx, a.mk_sub(a.mk_int(N), x), x);
        y = m.mk_ite(signy, a.mk_sub(a.mk_int(N), y), y);
        expr* d = a.mk_idiv(x, y);
        r = m.mk_ite(m.mk_iff(signx, signy), d, a.mk_uminus(d));
        r = if_eq(y, 0, m.mk_ite(signx, a.mk_int(1), a.mk_int(-1)), r);
        break;
    }
    case OP_BSREM_I:
    case OP_BSREM: {
        // y = 0 -> x
        // else x - sdiv(x, y) * y
        expr* x = umod(e, 0), * y = umod(e, 1);
        rational N = bv_size(e);
        expr* signx = a.mk_ge(x, a.mk_int(N / 2));
        expr* signy = a.mk_ge(y, a.mk_int(N / 2));
        expr* absx = m.mk_ite(signx, a.mk_sub(a.mk_int(N), x), x);
        expr* absy = m.mk_ite(signy, a.mk_sub(a.mk_int(N), y), y);
        expr* d = a.mk_idiv(absx, absy);
        d = m.mk_ite(m.mk_iff(signx, signy), d, a.mk_uminus(d));
        r = a.mk_sub(x, mul(d, y));
        r = if_eq(y, 0, x, r);
        break;
    }
    case OP_ROTATE_LEFT: {
        auto n = e->get_parameter(0).get_int();
        r = rotate_left(n);
        break;
    }
    case OP_ROTATE_RIGHT: {
        unsigned sz = bv.get_bv_size(e);
        auto n = e->get_parameter(0).get_int();
        r = rotate_left(sz - n);
        break;
    }
    case OP_EXT_ROTATE_LEFT: {
        unsigned sz = bv.get_bv_size(e);
        expr* y = umod(e, 1);
        r = a.mk_int(0);
        for (unsigned i = 0; i < sz; ++i)
            r = if_eq(y, i, rotate_left(i), r);
        break;
    }
    case OP_EXT_ROTATE_RIGHT: {
        unsigned sz = bv.get_bv_size(e);
        expr* y = umod(e, 1);
        r = a.mk_int(0);
        for (unsigned i = 0; i < sz; ++i)
            r = if_eq(y, i, rotate_left(sz - i), r);
        break;
    }
    case OP_REPEAT: {
        unsigned n = e->get_parameter(0).get_int();
        expr* x = umod(e->get_arg(0), 0);
        r = x;
        rational N = bv_size(e->get_arg(0));
        rational N0 = N;
        for (unsigned i = 1; i < n; ++i)
            r = add(mul(a.mk_int(N), x), r), N *= N0;
        break;
    }
    case OP_BREDOR: {
        r = umod(e->get_arg(0), 0);
        r = m.mk_not(m.mk_eq(r, a.mk_int(0)));
        break;
    }
    case OP_BREDAND: {
        rational N = bv_size(e->get_arg(0));
        r = umod(e->get_arg(0), 0);
        r = m.mk_not(m.mk_eq(r, a.mk_int(N - 1)));
        break;
    }
    default:
        verbose_stream() << mk_pp(e, m) << "\n";
        NOT_IMPLEMENTED_YET();
    }
    set_translated(e, r);
}

expr_ref bv2int_translator::if_eq(expr* n, unsigned k, expr* th, expr* el) {
    rational r;
    expr_ref _th(th, m), _el(el, m);
    if (bv.is_numeral(n, r)) {
        if (r == k)
            return expr_ref(th, m);
        else
            return expr_ref(el, m);
    }
    return expr_ref(m.mk_ite(m.mk_eq(n, a.mk_int(k)), th, el), m);
}

void bv2int_translator::translate_basic(app* e) {
    if (m.is_eq(e)) {
        bool has_bv_arg = any_of(*e, [&](expr* arg) { return bv.is_bv(arg); });
        if (has_bv_arg) {
            expr* bv_expr = e->get_arg(0);
            rational N = rational::power_of_two(bv.get_bv_size(bv_expr));
            if (a.is_numeral(arg(0)) || a.is_numeral(arg(1)) ||
                is_bounded(arg(0), N) || is_bounded(arg(1), N)) {
                set_translated(e, m.mk_eq(umod(bv_expr, 0), umod(bv_expr, 1)));
            }
            else {
                m_args[0] = a.mk_sub(arg(0), arg(1));
                set_translated(e, m.mk_eq(umod(bv_expr, 0), a.mk_int(0)));
            }
        }
        else
            set_translated(e, m.mk_eq(arg(0), arg(1)));
    }
    else if (m.is_ite(e))
        set_translated(e, m.mk_ite(arg(0), arg(1), arg(2)));
    else if (m_is_plugin)
        set_translated(e, e);
    else
        set_translated(e, m.mk_app(e->get_decl(), m_args));
}

bool bv2int_translator::is_bounded(expr* x, rational const& N) {
    return any_of(m_vars, [&](expr* v) {
        return is_translated(v) && translated(v) == x && bv_size(v) <= N;
        });
}

bool bv2int_translator::is_non_negative(expr* bv_expr, expr* e) {
    auto N = rational::power_of_two(bv.get_bv_size(bv_expr));
    rational r;
    if (a.is_numeral(e, r))
        return r >= 0;
    if (is_bounded(e, N))
        return true;
    expr* x = nullptr, * y = nullptr;
    if (a.is_mul(e, x, y))
        return is_non_negative(bv_expr, x) && is_non_negative(bv_expr, y);
    if (a.is_add(e, x, y))
        return is_non_negative(bv_expr, x) && is_non_negative(bv_expr, y);
    return false;
}

expr* bv2int_translator::umod(expr* bv_expr, unsigned i) {
    expr* x = arg(i);
    rational N = bv_size(bv_expr);
    return amod(bv_expr, x, N);
}

expr* bv2int_translator::smod(expr* bv_expr, unsigned i) {
    expr* x = arg(i);
    auto N = bv_size(bv_expr);
    auto shift = N / 2;
    rational r;
    if (a.is_numeral(x, r))
        return a.mk_int(mod(r + shift, N));
    return amod(bv_expr, add(x, a.mk_int(shift)), N);
}

expr_ref bv2int_translator::mul(expr* x, expr* y) {
    expr_ref _x(x, m), _y(y, m);
    if (a.is_zero(x))
        return _x;
    if (a.is_zero(y))
        return _y;
    if (a.is_one(x))
        return _y;
    if (a.is_one(y))
        return _x;
    rational v1, v2;
    if (a.is_numeral(x, v1) && a.is_numeral(y, v2))
        return expr_ref(a.mk_int(v1 * v2), m);
    _x = a.mk_mul(x, y);
    return _x;
}

expr_ref bv2int_translator::add(expr* x, expr* y) {
    expr_ref _x(x, m), _y(y, m);
    if (a.is_zero(x))
        return _y;
    if (a.is_zero(y))
        return _x;
    rational v1, v2;
    if (a.is_numeral(x, v1) && a.is_numeral(y, v2))
        return expr_ref(a.mk_int(v1 + v2), m);
    _x = a.mk_add(x, y);
    return _x;
}

/*
* Perform simplifications that are claimed sound when the bit-vector interpretations of
* mod/div always guard the mod and dividend to be non-zero.
* Potentially shady area is for arithmetic expressions created by int2bv.
* They will be guarded by a modulus which does not disappear.
*/
expr* bv2int_translator::amod(expr* bv_expr, expr* x, rational const& N) {
    rational v;
    expr* r = nullptr, * c = nullptr, * t = nullptr, * e = nullptr;
    if (m.is_ite(x, c, t, e))
        r = m.mk_ite(c, amod(bv_expr, t, N), amod(bv_expr, e, N));
    else if (a.is_idiv(x, t, e) && a.is_numeral(t, v) && 0 <= v && v < N && is_non_negative(bv_expr, e))
        r = x;
    else if (a.is_mod(x, t, e) && a.is_numeral(t, v) && 0 <= v && v < N)
        r = x;
    else if (a.is_numeral(x, v))
        r = a.mk_int(mod(v, N));
    else if (is_bounded(x, N))
        r = x;
    else
        r = a.mk_mod(x, a.mk_int(N));
    return r;
}

void bv2int_translator::translate_eq(expr* e) {
    expr* x = nullptr, * y = nullptr;
    VERIFY(m.is_eq(e, x, y));
    SASSERT(bv.is_bv(x));
    if (!is_translated(e)) {
        ensure_translated(x);
        ensure_translated(y);
        m_args.reset();
        m_args.push_back(a.mk_sub(translated(x), translated(y)));
        set_translated(e, m.mk_eq(umod(x, 0), a.mk_int(0)));
    }
    m_preds.push_back(e);
    TRACE("bv", tout << mk_pp(e, m) << " " << mk_pp(translated(e), m) << "\n");
    ctx.push(push_back_vector(m_preds));

}
