/*++
Copyright (c) 2006 Microsoft Corporation

Module Name:

    bit_blaster.cpp

Abstract:

    <abstract>

Author:

    Leonardo de Moura (leonardo) 2008-06-05.

Revision History:

--*/

#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/reg_decl_plugins.h"
#include "ast/rewriter/bit_blaster/bit_blaster.h"
#include "model/model.h"
#include "model/model_evaluator.h"

void mk_bits(ast_manager & m, char const * prefix, unsigned sz, expr_ref_vector & r) {
    sort_ref b(m);
    b = m.mk_bool_sort();
    for (unsigned i = 0; i < sz; ++i) {
        char buffer[128];
#ifdef _WINDOWS
        sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "%s%d.smt", prefix, i);
#else
        sprintf(buffer, "%s%d.smt", prefix, i);
#endif
        r.push_back(m.mk_const(symbol(buffer), b));
    }
}

void display(std::ostream & out, expr_ref_vector & r, bool ll=true) {
    for (unsigned i = 0; i < r.size(); i++) {
        out << "bit " << i << ":\n";
        if (ll)
            ast_ll_pp(out, r.get_manager(), r.get(i));
        else
            out << mk_pp(r.get(i), r.get_manager()) << "\n";
    }
}

static unsigned to_int(model_core & mdl, expr_ref_vector & out) {
    SASSERT(out.size() <= sizeof(unsigned) * 8);
    ast_manager & m = mdl.get_manager();
    model_evaluator eval(mdl);
    expr_ref bit(m);
    unsigned actual = 0;
    for (unsigned i = 0; i < out.size(); i++) {
        eval(out.get(i), bit);
        if (m.is_true(bit))
            actual |= 1 << i;
        else
            ENSURE(m.is_false(bit));
    }
    return actual;
}

#define ENSURE_INT(mdl, out, expected) \
    do { \
        unsigned actual = to_int(mdl, out); \
        TRACE("bit_blaster", \
            display(tout, out); \
            tout << "expected=" << (expected) << ", actual=" << actual << "\n"; \
        ); \
        ENSURE(actual == (expected)); \
    } while (0)

void tst_adder(ast_manager & m, bit_blaster & blaster) {
    model mdl(m);
    expr_ref_vector c(m);
    app_ref b1(m.mk_const("b1", m.mk_bool_sort()), m);
    app_ref b2(m.mk_const("b2", m.mk_bool_sort()), m);
    expr_ref not_b1(m.mk_not(b1), m);

    {
        expr * const a[] = { b1, b1, b1 };
        expr * const b[] = { m.mk_false(), m.mk_true(), m.mk_true() };
        c.reset();
        blaster.mk_adder(3, a, b, c);
    }

    mdl.register_decl(b1->get_decl(), m.mk_false());
    ENSURE_INT(mdl, c, 6); // b000 + b110

    mdl.register_decl(b1->get_decl(), m.mk_true());
    ENSURE_INT(mdl, c, 5); // b111 + b110

    {
        expr * const a[] = { m.mk_false(), m.mk_true(), m.mk_true() };
        expr * const b[] = { b1, not_b1, m.mk_false() };
        c.reset();
        blaster.mk_adder(3, a, b, c);
    }

    mdl.register_decl(b1->get_decl(), m.mk_false());
    ENSURE_INT(mdl, c, 0); // b110 + b010

    mdl.register_decl(b1->get_decl(), m.mk_true());
    ENSURE_INT(mdl, c, 7); // b110 + b001

    {
        expr * const a[] = { b1, b2, m.mk_true() };
        expr * const b[] = { b1, not_b1, m.mk_false() };
        c.reset();
        blaster.mk_adder(3, a, b, c);
    }

    mdl.register_decl(b1->get_decl(), m.mk_false());
    mdl.register_decl(b2->get_decl(), m.mk_false());
    ENSURE_INT(mdl, c, 6); // b100 + b010

    mdl.register_decl(b1->get_decl(), m.mk_false());
    mdl.register_decl(b2->get_decl(), m.mk_true());
    ENSURE_INT(mdl, c, 0); // b110 + b010

    mdl.register_decl(b1->get_decl(), m.mk_true());
    mdl.register_decl(b2->get_decl(), m.mk_false());
    ENSURE_INT(mdl, c, 6); // b101 + b001

    mdl.register_decl(b1->get_decl(), m.mk_true());
    mdl.register_decl(b2->get_decl(), m.mk_true());
    ENSURE_INT(mdl, c, 0); // b111 + b001
}

void tst_multiplier(ast_manager & m, bit_blaster & blaster) {
    model mdl(m);
    expr_ref_vector c(m);
    app_ref b1(m.mk_const("b1", m.mk_bool_sort()), m);
    app_ref b2(m.mk_const("b2", m.mk_bool_sort()), m);
    expr_ref not_b1(m.mk_not(b1), m);

    {
        expr * const a[] = { b1, b1, b1 };
        expr * const b[] = { m.mk_false(), m.mk_true(), m.mk_true() };
        c.reset();
        blaster.mk_multiplier(3, a, b, c);
    }

    mdl.register_decl(b1->get_decl(), m.mk_false());
    ENSURE_INT(mdl, c, 0); // b000 * b110

    mdl.register_decl(b1->get_decl(), m.mk_true());
    ENSURE_INT(mdl, c, 2); // b111 * b110

    {
        expr * const a[] = { m.mk_false(), m.mk_true(), m.mk_true() };
        expr * const b[] = { b1, not_b1, m.mk_false() };
        c.reset();
        blaster.mk_multiplier(3, a, b, c);
    }

    mdl.register_decl(b1->get_decl(), m.mk_false());
    ENSURE_INT(mdl, c, 4); // b110 * b010

    mdl.register_decl(b1->get_decl(), m.mk_true());
    ENSURE_INT(mdl, c, 6); // b110 * b001

    {
        expr * const a[] = { b1, b2, m.mk_true() };
        expr * const b[] = { b1, not_b1, m.mk_false() };
        c.reset();
        blaster.mk_multiplier(3, a, b, c);
    }

    mdl.register_decl(b1->get_decl(), m.mk_false());
    mdl.register_decl(b2->get_decl(), m.mk_false());
    ENSURE_INT(mdl, c, 0); // b100 * b010

    mdl.register_decl(b1->get_decl(), m.mk_false());
    mdl.register_decl(b2->get_decl(), m.mk_true());
    ENSURE_INT(mdl, c, 4); // b110 * b010

    mdl.register_decl(b1->get_decl(), m.mk_true());
    mdl.register_decl(b2->get_decl(), m.mk_false());
    ENSURE_INT(mdl, c, 5); // b101 * b001

    mdl.register_decl(b1->get_decl(), m.mk_true());
    mdl.register_decl(b2->get_decl(), m.mk_true());
    ENSURE_INT(mdl, c, 7); // b111 * b001
}

void tst_le(ast_manager & m, unsigned sz) {
//     expr_ref_vector a(m);
//     expr_ref_vector b(m);
//     expr_ref out(m);
//     mk_bits(m, "a", sz, a);
//     mk_bits(m, "b", sz, b);
//     bool t = true;
//     bit_blaster blaster(m, t);
//     blaster.mk_ule(sz, a.c_ptr(), b.c_ptr(), out); 
//     TRACE("bit_blaster", tout << mk_pp(out, m) << "\n";);
//     blaster.mk_sle(sz, a.c_ptr(), b.c_ptr(), out);
//     TRACE("bit_blaster", tout << mk_pp(out, m) << "\n";);
}

void tst_eqs(ast_manager & m, unsigned sz) {
//     expr_ref_vector a(m);
//     expr_ref_vector b(m);
//     expr_ref out(m);
//     mk_bits(m, "a", sz, a);
//     bool t = true;
//     bit_blaster blaster(m, t);
//     blaster.mk_eqs(sz, a.c_ptr(), b); 
//     TRACE("bit_blaster", display(tout, b, false););
}

void tst_sh(ast_manager & m, unsigned sz) {
//     expr_ref_vector a(m);
//     expr_ref_vector b(m);
//     expr_ref_vector c(m);
//     mk_bits(m, "a", sz, a);
//     mk_bits(m, "b", sz, b);
//     bool t = true;
//     bit_blaster blaster(m, t);
//     blaster.mk_shl(sz, a.c_ptr(), b.c_ptr(), c);
//     TRACE("bit_blaster", tout << "shl\n"; display(tout, c););
//     c.reset();
//     blaster.mk_lshr(sz, a.c_ptr(), b.c_ptr(), c);
//     TRACE("bit_blaster", tout << "lshr\n"; display(tout, c););
//     c.reset();
//     blaster.mk_ashr(sz, a.c_ptr(), b.c_ptr(), c);
//     TRACE("bit_blaster", tout << "ashr " << c.size() << "\n"; display(tout, c, false););
}

void tst_bit_blaster() {
    ast_manager m;
    reg_decl_plugins(m);
    bit_blaster_params params;
    bit_blaster blaster(m, params);

    tst_adder(m, blaster);
    tst_multiplier(m, blaster);
    tst_le(m, 4);
    tst_eqs(m, 8);
    tst_sh(m, 4);
}
