From 392334f779c089deef73bec472d6eb5bca399219 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 22 Aug 2017 10:44:00 -0700
Subject: [PATCH 01/74] add ability to create and manipulate model objects

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 examples/c++/example.cpp |  2 +-
 src/api/api_model.cpp    | 54 ++++++++++++++++++++++++++++++++++++++++
 src/api/c++/z3++.h       | 16 ++++++++++++
 src/api/z3_api.h         | 30 ++++++++++++++++++++++
 4 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp
index 2d7d051c5..035d032bc 100644
--- a/examples/c++/example.cpp
+++ b/examples/c++/example.cpp
@@ -470,7 +470,7 @@ void unsat_core_example2() {
     // The solver s already contains p1 => F
     // To disable F, we add (not p1) as an additional assumption
     qs.push_back(!p1);
-    std::cout << s.check(qs.size(), &qs[0]) << "\n";
+    std::cout << s.check((unsigned)qs.size(), &qs[0]) << "\n";
     expr_vector core2 = s.unsat_core();
     std::cout << core2 << "\n";
     std::cout << "size: " << core2.size() << "\n";
diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp
index de917650d..1646c691f 100644
--- a/src/api/api_model.cpp
+++ b/src/api/api_model.cpp
@@ -30,6 +30,17 @@ Revision History:
 
 extern "C" {
 
+    Z3_model Z3_API Z3_mk_model(Z3_context c) {
+        Z3_TRY;
+        LOG_Z3_mk_model(c);
+        RESET_ERROR_CODE();
+        Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); 
+        m_ref->m_model = alloc(model, mk_c(c)->m());
+        mk_c(c)->save_object(m_ref);
+        RETURN_Z3(of_model(m_ref));
+        Z3_CATCH_RETURN(0);
+    }
+
     void Z3_API Z3_model_inc_ref(Z3_context c, Z3_model m) {
         Z3_TRY;
         LOG_Z3_model_inc_ref(c, m);
@@ -224,6 +235,31 @@ extern "C" {
         Z3_CATCH_RETURN(0);
     }
 
+    Z3_func_interp Z3_API Z3_add_func_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast else_val) {
+        Z3_TRY;
+        LOG_Z3_add_func_interp(c, m, f, else_val);
+        RESET_ERROR_CODE();
+        func_decl* d = to_func_decl(f);
+        model* mdl = to_model_ref(m);
+        Z3_func_interp_ref * f_ref = alloc(Z3_func_interp_ref, *mk_c(c), mdl); 
+        f_ref->m_func_interp = alloc(func_interp, mk_c(c)->m(), d->get_arity());
+        mk_c(c)->save_object(f_ref);
+        mdl->register_decl(d, f_ref->m_func_interp);
+        f_ref->m_func_interp->set_else(to_expr(else_val));
+        RETURN_Z3(of_func_interp(f_ref));
+        Z3_CATCH_RETURN(0);        
+    }
+
+    void Z3_API Z3_add_const_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast a) {
+        Z3_TRY;
+        LOG_Z3_add_const_interp(c, m, f, a);
+        RESET_ERROR_CODE();
+        func_decl* d = to_func_decl(f);
+        model* mdl = to_model_ref(m);
+        mdl->register_decl(d, to_expr(a));
+        Z3_CATCH;
+    }
+
     void Z3_API Z3_func_interp_inc_ref(Z3_context c, Z3_func_interp f) {
         Z3_TRY;
         LOG_Z3_func_interp_inc_ref(c, f);
@@ -292,6 +328,24 @@ extern "C" {
         Z3_CATCH_RETURN(0);
     }
 
+    void Z3_API Z3_add_func_entry(Z3_context c, Z3_func_interp fi, Z3_ast_vector args, Z3_ast value) {
+        Z3_TRY;
+        LOG_Z3_add_func_entry(c, fi, args, value);
+        //CHECK_NON_NULL(fi, void);
+        //CHECK_NON_NULL(args, void);
+        //CHECK_NON_NULL(value, void);
+        func_interp* _fi = to_func_interp_ref(fi);
+        expr* _value = to_expr(value);
+        if (to_ast_vector_ref(args).size() != _fi->get_arity()) {
+            SET_ERROR_CODE(Z3_IOB);
+            return;
+        }
+        // check sorts of value
+        expr* const* _args = (expr* const*) to_ast_vector_ref(args).c_ptr();
+        _fi->insert_entry(_args, _value);
+        Z3_CATCH;
+    }
+
     void Z3_API Z3_func_entry_inc_ref(Z3_context c, Z3_func_entry e) {
         Z3_TRY;
         LOG_Z3_func_entry_inc_ref(c, e);
diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h
index 42db6f352..cf1a5070a 100644
--- a/src/api/c++/z3++.h
+++ b/src/api/c++/z3++.h
@@ -1731,6 +1731,10 @@ namespace z3 {
         expr else_value() const { Z3_ast r = Z3_func_interp_get_else(ctx(), m_interp); check_error(); return expr(ctx(), r); }
         unsigned num_entries() const { unsigned r = Z3_func_interp_get_num_entries(ctx(), m_interp); check_error(); return r; }
         func_entry entry(unsigned i) const { Z3_func_entry e = Z3_func_interp_get_entry(ctx(), m_interp, i); check_error(); return func_entry(ctx(), e); }
+        void add_entry(expr_vector const& args, expr& value) {
+            Z3_add_func_entry(ctx(), m_interp, args, value);
+            check_error();
+        }
     };
 
     class model : public object {
@@ -1740,6 +1744,7 @@ namespace z3 {
             Z3_model_inc_ref(ctx(), m);
         }
     public:
+        model(context & c):object(c) { init(Z3_mk_model(c)); }
         model(context & c, Z3_model m):object(c) { init(m); }
         model(model const & s):object(s) { init(s.m_model); }
         ~model() { Z3_model_dec_ref(ctx(), m_model); }
@@ -1795,6 +1800,17 @@ namespace z3 {
             return 0 != Z3_model_has_interp(ctx(), m_model, f);
         }
 
+        func_interp add_func_interp(func_decl& f, expr& else_val) {
+            Z3_func_interp r = Z3_add_func_interp(ctx(), m_model, f, else_val);
+            check_error();
+            return func_interp(ctx(), r);
+        }
+
+        void add_const_interp(func_decl& f, expr& value) {
+            Z3_add_const_interp(ctx(), m_model, f, value);
+            check_error();
+        }
+
         friend std::ostream & operator<<(std::ostream & out, model const & m);
     };
     inline std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; }
diff --git a/src/api/z3_api.h b/src/api/z3_api.h
index 8d53c9255..46c8fbb30 100644
--- a/src/api/z3_api.h
+++ b/src/api/z3_api.h
@@ -4680,6 +4680,14 @@ extern "C" {
 
     /** @name Models */
     /*@{*/
+
+    /**
+       \brief Create a fresh model object. It has reference count 0.
+
+       def_API('Z3_mk_model', MODEL, (_in(CONTEXT),))
+    */
+    Z3_model Z3_API Z3_mk_model(Z3_context c);
+
     /**
        \brief Increment the reference counter of the given model.
 
@@ -4850,6 +4858,21 @@ extern "C" {
     */
     Z3_func_decl Z3_API Z3_get_as_array_func_decl(Z3_context c, Z3_ast a);
 
+    /**
+       \brief Create a fresh func_interp object, add it to a model for a specified function. 
+       It has reference count 0.       
+
+       def_API('Z3_add_func_interp', FUNC_INTERP, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL), _in(AST)))
+    */
+    Z3_func_interp Z3_API Z3_add_func_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast else_val);
+
+    /**
+       \brief Add a constant interpretation.
+
+       def_API('Z3_add_const_interp', VOID, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL), _in(AST)))
+     */
+    void Z3_API Z3_add_const_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast a);
+
     /**
        \brief Increment the reference counter of the given Z3_func_interp object.
 
@@ -4904,6 +4927,13 @@ extern "C" {
     */
     unsigned Z3_API Z3_func_interp_get_arity(Z3_context c, Z3_func_interp f);
 
+    /**
+       \brief add a function entry to a function interpretation.
+
+       def_API('Z3_add_func_entry', VOID, (_in(CONTEXT), _in(FUNC_INTERP), _in(AST_VECTOR), _in(AST)))
+     */
+    void Z3_API Z3_add_func_entry(Z3_context c, Z3_func_interp fi, Z3_ast_vector args, Z3_ast value);
+
     /**
        \brief Increment the reference counter of the given Z3_func_entry object.
 

From a206362cef8a6dce748f73a14f3aaec8ec14456c Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 22 Aug 2017 11:41:25 -0700
Subject: [PATCH 02/74] add comments addressing some questions #1223

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/z3_api.h | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/api/z3_api.h b/src/api/z3_api.h
index 46c8fbb30..bea40f30a 100644
--- a/src/api/z3_api.h
+++ b/src/api/z3_api.h
@@ -4862,9 +4862,14 @@ extern "C" {
        \brief Create a fresh func_interp object, add it to a model for a specified function. 
        It has reference count 0.       
 
+       \param c context
+       \param m model
+       \param f function declaration 
+       \param default_value default value for function interpretation
+
        def_API('Z3_add_func_interp', FUNC_INTERP, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL), _in(AST)))
     */
-    Z3_func_interp Z3_API Z3_add_func_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast else_val);
+    Z3_func_interp Z3_API Z3_add_func_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast default_value);
 
     /**
        \brief Add a constant interpretation.
@@ -4930,6 +4935,15 @@ extern "C" {
     /**
        \brief add a function entry to a function interpretation.
 
+       \param c logical context
+       \param fi a function interpregation to be updated.
+       \param args list of arguments. They should be constant values (such as integers) and be of the same types as the domain of the function.
+       \param value value of the function when the parameters match args.
+
+       It is assumed that entries added to a function cover disjoint arguments. 
+       If an two entries are added with the same arguments, only the second insertion survives and the
+       first inserted entry is removed.
+
        def_API('Z3_add_func_entry', VOID, (_in(CONTEXT), _in(FUNC_INTERP), _in(AST_VECTOR), _in(AST)))
      */
     void Z3_API Z3_add_func_entry(Z3_context c, Z3_func_interp fi, Z3_ast_vector args, Z3_ast value);

From e2b46257d699b6165c400d8707c1cd4d6d94d95f Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 22 Aug 2017 15:09:34 -0700
Subject: [PATCH 03/74] reducing dependencies on simplifier

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/arith_decl_plugin.h             |  18 +-
 src/ast/bv_decl_plugin.cpp              |  10 +-
 src/ast/bv_decl_plugin.h                |   7 +-
 src/ast/macros/macro_finder.cpp         |  20 +-
 src/ast/macros/macro_finder.h           |   3 +-
 src/ast/macros/macro_manager.cpp        |   2 +-
 src/ast/macros/macro_util.cpp           |  85 +++++---
 src/ast/macros/macro_util.h             |  21 +-
 src/ast/macros/quasi_macros.cpp         |  10 +-
 src/ast/macros/quasi_macros.h           |   7 +-
 src/ast/rewriter/arith_rewriter.h       |   2 +-
 src/ast/rewriter/poly_rewriter.h        |   4 +-
 src/ast/rewriter/poly_rewriter_def.h    |  60 ++++++
 src/smt/asserted_formulas.cpp           |   2 +-
 src/smt/smt_model_finder.cpp            | 246 ++++++++++++------------
 src/smt/smt_model_finder.h              |   3 +-
 src/smt/smt_quantifier.cpp              |   2 +-
 src/tactic/ufbv/quasi_macros_tactic.cpp |   2 +-
 18 files changed, 289 insertions(+), 215 deletions(-)

diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h
index a66ddd967..6c2b1c77a 100644
--- a/src/ast/arith_decl_plugin.h
+++ b/src/ast/arith_decl_plugin.h
@@ -389,16 +389,16 @@ public:
     app * mk_lt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LT, arg1, arg2); }
     app * mk_gt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GT, arg1, arg2); }
 
-    app * mk_add(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_afid, OP_ADD, num_args, args); }
-    app * mk_add(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2); }
-    app * mk_add(expr * arg1, expr * arg2, expr* arg3) { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2, arg3); }
+    app * mk_add(unsigned num_args, expr * const * args) const { return m_manager.mk_app(m_afid, OP_ADD, num_args, args); }
+    app * mk_add(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2); }
+    app * mk_add(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2, arg3); }
 
-    app * mk_sub(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_SUB, arg1, arg2); }
-    app * mk_sub(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_afid, OP_SUB, num_args, args); }
-    app * mk_mul(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2); }
-    app * mk_mul(expr * arg1, expr * arg2, expr* arg3) { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2, arg3); }
-    app * mk_mul(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_afid, OP_MUL, num_args, args); }
-    app * mk_uminus(expr * arg) { return m_manager.mk_app(m_afid, OP_UMINUS, arg); }
+    app * mk_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_SUB, arg1, arg2); }
+    app * mk_sub(unsigned num_args, expr * const * args) const { return m_manager.mk_app(m_afid, OP_SUB, num_args, args); }
+    app * mk_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2); }
+    app * mk_mul(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2, arg3); }
+    app * mk_mul(unsigned num_args, expr * const * args) const { return m_manager.mk_app(m_afid, OP_MUL, num_args, args); }
+    app * mk_uminus(expr * arg) const { return m_manager.mk_app(m_afid, OP_UMINUS, arg); }
     app * mk_div(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_DIV, arg1, arg2); }
     app * mk_idiv(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_IDIV, arg1, arg2); }
     app * mk_rem(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_REM, arg1, arg2); }
diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp
index 4632b6604..b5c79f662 100644
--- a/src/ast/bv_decl_plugin.cpp
+++ b/src/ast/bv_decl_plugin.cpp
@@ -784,6 +784,12 @@ bool bv_recognizers::is_numeral(expr const * n, rational & val, unsigned & bv_si
     return true;
 }
 
+bool bv_recognizers::is_numeral(expr const * n, rational & val) const {
+    unsigned bv_size = 0;
+    return is_numeral(n, val, bv_size);
+}
+
+
 bool bv_recognizers::is_allone(expr const * e) const {
     rational r;
     unsigned bv_size;
@@ -847,7 +853,7 @@ bv_util::bv_util(ast_manager & m):
     m_plugin = static_cast<bv_decl_plugin*>(m.get_plugin(m.mk_family_id("bv")));
 }
 
-app * bv_util::mk_numeral(rational const & val, sort* s) {
+app * bv_util::mk_numeral(rational const & val, sort* s) const {
     if (!is_bv_sort(s)) {
         return 0;
     }
@@ -855,7 +861,7 @@ app * bv_util::mk_numeral(rational const & val, sort* s) {
     return mk_numeral(val, bv_size);
 }
 
-app * bv_util::mk_numeral(rational const & val, unsigned bv_size) {
+app * bv_util::mk_numeral(rational const & val, unsigned bv_size) const {
     parameter p1(val);
     parameter p[2] = { p1, parameter(static_cast<int>(bv_size)) };
     return m_manager.mk_app(get_fid(), OP_BV_NUM, 2, p, 0, 0);
diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h
index 5e533cd98..a4ea7af80 100644
--- a/src/ast/bv_decl_plugin.h
+++ b/src/ast/bv_decl_plugin.h
@@ -293,6 +293,7 @@ public:
     family_id get_fid() const { return m_afid; }
     family_id get_family_id() const { return get_fid(); }
 
+    bool is_numeral(expr const * n, rational & val) const;
     bool is_numeral(expr const * n, rational & val, unsigned & bv_size) const;
     bool is_numeral(expr const * n) const { return is_app_of(n, get_fid(), OP_BV_NUM); }
     bool is_allone(expr const * e) const;
@@ -381,9 +382,9 @@ public:
 
     ast_manager & get_manager() const { return m_manager; }
 
-    app * mk_numeral(rational const & val, sort* s);
-    app * mk_numeral(rational const & val, unsigned bv_size);
-    app * mk_numeral(uint64 u, unsigned bv_size) { return mk_numeral(rational(u, rational::ui64()), bv_size); }
+    app * mk_numeral(rational const & val, sort* s) const;
+    app * mk_numeral(rational const & val, unsigned bv_size) const;
+    app * mk_numeral(uint64 u, unsigned bv_size) const { return mk_numeral(rational(u, rational::ui64()), bv_size); }
     sort * mk_sort(unsigned bv_size);
 
     unsigned get_bv_size(sort const * s) const {
diff --git a/src/ast/macros/macro_finder.cpp b/src/ast/macros/macro_finder.cpp
index 285c0e5fb..1d441aee7 100644
--- a/src/ast/macros/macro_finder.cpp
+++ b/src/ast/macros/macro_finder.cpp
@@ -48,14 +48,12 @@ bool macro_finder::is_macro(expr * n, app_ref & head, expr_ref & def) {
 bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
     if (!is_quantifier(n) || !to_quantifier(n)->is_forall())
         return false;
-    arith_simplifier_plugin * as = get_arith_simp();
-    arith_util & autil = as->get_arith_util();
     expr * body        = to_quantifier(n)->get_expr();
     unsigned num_decls = to_quantifier(n)->get_num_decls();
     
-    if (!autil.is_le(body) && !autil.is_ge(body) && !m_manager.is_eq(body))
+    if (!m_autil.is_le(body) && !m_autil.is_ge(body) && !m_manager.is_eq(body))
         return false;
-    if (!as->is_add(to_app(body)->get_arg(0)))
+    if (!m_autil.is_add(to_app(body)->get_arg(0)))
         return false;
     app_ref head(m_manager);
     expr_ref def(m_manager);
@@ -66,10 +64,10 @@ bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_ex
     
     if (!inv || m_manager.is_eq(body))
         new_body = m_manager.mk_app(to_app(body)->get_decl(), head, def);
-    else if (as->is_le(body))
-        new_body = autil.mk_ge(head, def);
+    else if (m_autil.is_le(body))
+        new_body = m_autil.mk_ge(head, def);
     else
-        new_body = autil.mk_le(head, def);
+        new_body = m_autil.mk_le(head, def);
 
     quantifier_ref new_q(m_manager); 
     new_q = m_manager.update_quantifier(to_quantifier(n), new_body);
@@ -88,10 +86,9 @@ bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_ex
     func_decl * k   = m_manager.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range());
     app * k_app     = m_manager.mk_app(k, head->get_num_args(), head->get_args());
     expr_ref_buffer new_rhs_args(m_manager);
-    expr_ref new_rhs2(m_manager);
-    as->mk_add(def, k_app, new_rhs2);
+    expr_ref new_rhs2(m_autil.mk_add(def, k_app), m_manager);
     expr * body1    = m_manager.mk_eq(head, new_rhs2);
-    expr * body2    = m_manager.mk_app(new_body->get_decl(), k_app, as->mk_numeral(rational(0)));
+    expr * body2    = m_manager.mk_app(new_body->get_decl(), k_app, m_autil.mk_int(0));
     quantifier * q1 = m_manager.update_quantifier(new_q, body1);
     expr * patterns[1] = { m_manager.mk_pattern(k_app) };
     quantifier * q2 = m_manager.update_quantifier(new_q, 1, patterns, body2);
@@ -158,7 +155,8 @@ static void pseudo_predicate_macro2macro(ast_manager & m, app * head, app * t, e
 macro_finder::macro_finder(ast_manager & m, macro_manager & mm):
     m_manager(m),
     m_macro_manager(mm),
-    m_util(mm.get_util()) {
+    m_util(mm.get_util()),
+    m_autil(m) {
 }
 
 macro_finder::~macro_finder() {
diff --git a/src/ast/macros/macro_finder.h b/src/ast/macros/macro_finder.h
index 7f1b27f0e..5807573ae 100644
--- a/src/ast/macros/macro_finder.h
+++ b/src/ast/macros/macro_finder.h
@@ -20,7 +20,6 @@ Revision History:
 #define MACRO_FINDER_H_
 
 #include "ast/macros/macro_manager.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
 
 
 bool is_macro_head(expr * n, unsigned num_decls);
@@ -37,7 +36,7 @@ class macro_finder {
     ast_manager &               m_manager; 
     macro_manager &             m_macro_manager;
     macro_util &                m_util;
-    arith_simplifier_plugin * get_arith_simp() { return m_util.get_arith_simp(); }
+    arith_util                  m_autil;
     bool expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
     bool is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
 
diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp
index bd330a2de..f26a87445 100644
--- a/src/ast/macros/macro_manager.cpp
+++ b/src/ast/macros/macro_manager.cpp
@@ -28,7 +28,7 @@ Revision History:
 macro_manager::macro_manager(ast_manager & m, simplifier & s):
     m_manager(m),
     m_simplifier(s),
-    m_util(m, s),
+    m_util(m),
     m_decls(m),
     m_macros(m),
     m_macro_prs(m),
diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp
index 35f2fbcfb..b7eb9657f 100644
--- a/src/ast/macros/macro_util.cpp
+++ b/src/ast/macros/macro_util.cpp
@@ -29,16 +29,17 @@ Revision History:
 #include "ast/well_sorted.h"
 #include "ast/rewriter/bool_rewriter.h"
 
-macro_util::macro_util(ast_manager & m, simplifier & s):
+macro_util::macro_util(ast_manager & m):
     m_manager(m),
     m_bv(m),
-    m_simplifier(s),
-    m_arith_simp(0),
-    m_bv_simp(0),
+    m_arith(m),
+    m_arith_rw(m),
+    m_bv_rw(m),
     m_forbidden_set(0),
     m_curr_clause(0) {
 }
 
+#if 0
 arith_simplifier_plugin * macro_util::get_arith_simp() const {
     if (m_arith_simp == 0) {
         const_cast<macro_util*>(this)->m_arith_simp = static_cast<arith_simplifier_plugin*>(m_simplifier.get_plugin(m_manager.mk_family_id("arith")));
@@ -54,7 +55,7 @@ bv_simplifier_plugin * macro_util::get_bv_simp() const {
     SASSERT(m_bv_simp != 0);
     return m_bv_simp;
 }
-
+#endif
 
 bool macro_util::is_bv(expr * n) const {
     return m_bv.is_bv(n);
@@ -65,32 +66,41 @@ bool macro_util::is_bv_sort(sort * s) const {
 }
 
 bool macro_util::is_add(expr * n) const {
-    return get_arith_simp()->is_add(n) || m_bv.is_bv_add(n);
+    return m_arith.is_add(n) || m_bv.is_bv_add(n);
 }
 
 bool macro_util::is_times_minus_one(expr * n, expr * & arg) const {
-    return get_arith_simp()->is_times_minus_one(n, arg) || get_bv_simp()->is_times_minus_one(n, arg);
+    return m_arith_rw.is_times_minus_one(n, arg) || m_bv_rw.is_times_minus_one(n, arg);
 }
 
 bool macro_util::is_le(expr * n) const {
-    return get_arith_simp()->is_le(n) || m_bv.is_bv_ule(n) || m_bv.is_bv_sle(n);
+    return m_arith.is_le(n) || m_bv.is_bv_ule(n) || m_bv.is_bv_sle(n);
 }
 
 bool macro_util::is_le_ge(expr * n) const {
-    return get_arith_simp()->is_le_ge(n) || m_bv.is_bv_ule(n) || m_bv.is_bv_sle(n);
+    return m_arith.is_ge(n) || m_arith.is_le(n) || m_bv.is_bv_ule(n) || m_bv.is_bv_sle(n);
 }
 
-poly_simplifier_plugin * macro_util::get_poly_simp_for(sort * s) const {
-    if (is_bv_sort(s))
-        return get_bv_simp();
-    else
-        return get_arith_simp();
+bool macro_util::is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) {
+    return m_arith_rw.is_var_plus_ground(n, inv, v, t) || m_bv_rw.is_var_plus_ground(n, inv, v, t);
+}
+
+bool macro_util::is_zero_safe(expr * n) const {
+    if (m_bv_rw.is_bv(n)) {
+        return m_bv.is_zero(n);
+    }
+    else {
+        return m_arith_rw.is_zero(n);
+    }
 }
 
 app * macro_util::mk_zero(sort * s) const {
-    poly_simplifier_plugin * ps = get_poly_simp_for(s);
-    ps->set_curr_sort(s);
-    return ps->mk_zero();
+    if (m_bv.is_bv_sort(s)) {
+        return m_bv.mk_numeral(rational(0), s);
+    }
+    else {
+        return m_arith.mk_numeral(rational(0), s);
+    }
 }
 
 void macro_util::mk_sub(expr * t1, expr * t2, expr_ref & r) const {
@@ -98,7 +108,7 @@ void macro_util::mk_sub(expr * t1, expr * t2, expr_ref & r) const {
         r = m_bv.mk_bv_sub(t1, t2);
     }
     else {
-        get_arith_simp()->mk_sub(t1, t2, r);
+        r = m_arith.mk_sub(t1, t2);
     }
 }
 
@@ -107,18 +117,32 @@ void macro_util::mk_add(expr * t1, expr * t2, expr_ref & r) const {
         r = m_bv.mk_bv_add(t1, t2);
     }
     else {
-        get_arith_simp()->mk_add(t1, t2, r);
+        r = m_arith.mk_add(t1, t2);
     }
 }
 
 void macro_util::mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const {
-    if (num_args == 0) {
+    switch (num_args) {
+    case 0: 
         r = mk_zero(s);
-        return;
+        break;
+    case 1:
+        r = args[0];
+        break;
+    default:
+        if (m_bv.is_bv_sort(s)) {
+            r = args[0];
+            while (num_args >= 2) {
+                --num_args;
+                ++args;
+                r = m_bv.mk_bv_add(r, args[0]);
+            }
+        }
+        else {
+            r = m_arith.mk_add(num_args, args);
+        }
+        break;
     }
-    poly_simplifier_plugin * ps = get_poly_simp_for(s);
-    ps->set_curr_sort(s);
-    ps->mk_add(num_args, args, r);
 }
 
 /**
@@ -241,13 +265,12 @@ bool macro_util::poly_contains_head(expr * n, func_decl * f, expr * exception) c
 
 bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const {
     // TODO: obsolete... we should move to collect_arith_macro_candidates
-    arith_simplifier_plugin * as = get_arith_simp();
-    if (!m_manager.is_eq(n) && !as->is_le(n) && !as->is_ge(n))
+    if (!m_manager.is_eq(n) && !m_arith.is_le(n) && !m_arith.is_ge(n))
         return false;
     expr * lhs = to_app(n)->get_arg(0);
     expr * rhs = to_app(n)->get_arg(1);
 
-    if (!as->is_numeral(rhs))
+    if (!m_arith.is_numeral(rhs))
         return false;
 
     inv = false;
@@ -272,7 +295,7 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex
             !poly_contains_head(lhs, to_app(arg)->get_decl(), arg)) {
             h = arg;
         }
-        else if (h == 0 && as->is_times_minus_one(arg, neg_arg) &&
+        else if (h == 0 && m_arith_rw.is_times_minus_one(arg, neg_arg) &&
                  is_macro_head(neg_arg, num_decls) &&
                  !is_forbidden(to_app(neg_arg)->get_decl()) &&
                  !poly_contains_head(lhs, to_app(neg_arg)->get_decl(), arg)) {
@@ -287,11 +310,11 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex
         return false;
     head = to_app(h);
     expr_ref tmp(m_manager);
-    as->mk_add(args.size(), args.c_ptr(), tmp);
+    tmp = m_arith.mk_add(args.size(), args.c_ptr());
     if (inv)
-        as->mk_sub(tmp, rhs, def);
+        def = m_arith.mk_sub(tmp, rhs);
     else
-        as->mk_sub(rhs, tmp, def);
+        def = m_arith.mk_sub(rhs, tmp);
     return true;
 }
 
diff --git a/src/ast/macros/macro_util.h b/src/ast/macros/macro_util.h
index d76f2f0d3..91d96cc5e 100644
--- a/src/ast/macros/macro_util.h
+++ b/src/ast/macros/macro_util.h
@@ -22,12 +22,8 @@ Revision History:
 
 #include "ast/ast.h"
 #include "util/obj_hashtable.h"
-#include "ast/simplifier/simplifier.h"
-
-class poly_simplifier_plugin;
-class arith_simplifier_plugin;
-class bv_simplifier_plugin;
-class basic_simplifier_plugin;
+#include "ast/rewriter/arith_rewriter.h"
+#include "ast/rewriter/bv_rewriter.h"
 
 class macro_util {
 public:
@@ -63,9 +59,9 @@ public:
 private:
     ast_manager &               m_manager;
     bv_util                     m_bv;
-    simplifier &                m_simplifier;
-    arith_simplifier_plugin *   m_arith_simp;
-    bv_simplifier_plugin    *   m_bv_simp;
+    arith_util                  m_arith;
+    arith_rewriter              m_arith_rw;
+    bv_rewriter                 m_bv_rw;
     obj_hashtable<func_decl> *  m_forbidden_set;
 
     bool is_forbidden(func_decl * f) const { return m_forbidden_set != 0 && m_forbidden_set->contains(f); }
@@ -94,11 +90,9 @@ private:
 
 
 public:
-    macro_util(ast_manager & m, simplifier & s);
+    macro_util(ast_manager & m);
     void set_forbidden_set(obj_hashtable<func_decl> * s) { m_forbidden_set = s; }
 
-    arith_simplifier_plugin * get_arith_simp() const;
-    bv_simplifier_plugin * get_bv_simp() const;
 
     bool is_macro_head(expr * n, unsigned num_decls) const;
     bool is_left_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const;
@@ -113,6 +107,8 @@ public:
         return is_arith_macro(n, num_decls, head, def, inv);
     }
 
+    bool is_zero_safe(expr * n) const;
+    bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t);
     bool is_pseudo_head(expr * n, unsigned num_decls, app_ref & head, app_ref & t);
     bool is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t, expr_ref & def);
 
@@ -137,7 +133,6 @@ public:
     void mk_sub(expr * t1, expr * t2, expr_ref & r) const;
     void mk_add(expr * t1, expr * t2, expr_ref & r) const;
     void mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const;
-    poly_simplifier_plugin * get_poly_simp_for(sort * s) const;
 };
 
 #endif
diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp
index b39adde03..822532532 100644
--- a/src/ast/macros/quasi_macros.cpp
+++ b/src/ast/macros/quasi_macros.cpp
@@ -22,10 +22,10 @@ Revision History:
 #include "util/uint_set.h"
 #include "ast/rewriter/var_subst.h"
 
-quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s) :
-  m_manager(m),
+quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm) :
+  m_manager(m), 
   m_macro_manager(mm),
-  m_simplifier(s),
+  m_rewriter(m),
   m_new_vars(m),
   m_new_eqs(m),
   m_new_qsorts(m) {
@@ -299,8 +299,8 @@ void quasi_macros::apply_macros(unsigned n, expr * const * exprs, proof * const
         proof_ref pr(m_manager), ps(m_manager);
         proof * p = m_manager.proofs_enabled() ? prs[i] : 0;
         m_macro_manager.expand_macros(exprs[i], p, r, pr);
-        m_simplifier(r, rs, ps);
-        new_exprs.push_back(rs);
+        m_rewriter(r);
+        new_exprs.push_back(r);
         new_prs.push_back(ps);    
     }
 }
diff --git a/src/ast/macros/quasi_macros.h b/src/ast/macros/quasi_macros.h
index 3a9b6074e..29efe63c7 100644
--- a/src/ast/macros/quasi_macros.h
+++ b/src/ast/macros/quasi_macros.h
@@ -21,8 +21,7 @@ Revision History:
 
 #include<sstream>
 #include "ast/macros/macro_manager.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/simplifier.h"
+#include "ast/rewriter/th_rewriter.h"
 
 /**
    \brief Finds quasi macros and applies them.
@@ -32,7 +31,7 @@ class quasi_macros {
 
     ast_manager &             m_manager;
     macro_manager &           m_macro_manager;
-    simplifier &              m_simplifier;
+    th_rewriter               m_rewriter;
     occurrences_map           m_occurrences;
     ptr_vector<expr>          m_todo;    
 
@@ -57,7 +56,7 @@ class quasi_macros {
     void apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
 
 public:
-    quasi_macros(ast_manager & m, macro_manager & mm, simplifier & s);
+    quasi_macros(ast_manager & m, macro_manager & mm);
     ~quasi_macros();
     
     /**
diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h
index de849dbd7..5d9fb1d66 100644
--- a/src/ast/rewriter/arith_rewriter.h
+++ b/src/ast/rewriter/arith_rewriter.h
@@ -35,7 +35,6 @@ protected:
     
     bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
     bool is_numeral(expr * n, numeral & r) const { return m_util.is_numeral(n, r); }
-    bool is_zero(expr * n) const { return m_util.is_zero(n); }
     bool is_minus_one(expr * n) const { return m_util.is_minus_one(n); }
     void normalize(numeral & c, sort * s) {}
     app * mk_numeral(numeral const & r, sort * s) { return m_util.mk_numeral(r, s); }
@@ -45,6 +44,7 @@ protected:
     decl_kind power_decl_kind() const { return OP_POWER; }
 public:
     arith_rewriter_core(ast_manager & m):m_util(m) {}
+    bool is_zero(expr * n) const { return m_util.is_zero(n); }
 };
 
 class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
diff --git a/src/ast/rewriter/poly_rewriter.h b/src/ast/rewriter/poly_rewriter.h
index 5d38e4b10..5269f25a8 100644
--- a/src/ast/rewriter/poly_rewriter.h
+++ b/src/ast/rewriter/poly_rewriter.h
@@ -39,7 +39,6 @@ protected:
 
     bool is_numeral(expr * n) const { return Config::is_numeral(n); }
     bool is_numeral(expr * n, numeral & r) const { return Config::is_numeral(n, r); }
-    bool is_zero(expr * n) const { return Config::is_zero(n); }
     bool is_minus_one(expr * n) const { return Config::is_minus_one(n); }
     void normalize(numeral & c) { Config::normalize(c, m_curr_sort); }
     app * mk_numeral(numeral const & r) { return Config::mk_numeral(r, m_curr_sort); }
@@ -111,6 +110,9 @@ public:
     bool is_mul(expr * n) const { return is_app_of(n, get_fid(), mul_decl_kind()); }
     bool is_add(func_decl * f) const { return is_decl_of(f, get_fid(), add_decl_kind()); }
     bool is_mul(func_decl * f) const { return is_decl_of(f, get_fid(), mul_decl_kind()); }
+    bool is_times_minus_one(expr * n, expr*& r) const;
+    bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t);
+
 
     br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
         SASSERT(num_args > 0);
diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h
index 5e2e39722..a8d115d64 100644
--- a/src/ast/rewriter/poly_rewriter_def.h
+++ b/src/ast/rewriter/poly_rewriter_def.h
@@ -931,3 +931,63 @@ expr* poly_rewriter<Config>::merge_muls(expr* x, expr* y) {
     m1[k] = mk_add_app(2, args);
     return mk_mul_app(k+1, m1.c_ptr());
 }
+
+template<typename Config>
+bool poly_rewriter<Config>::is_times_minus_one(expr * n, expr* & r) const {
+    if (is_mul(n) && to_app(n)->get_num_args() == 2 && is_minus_one(to_app(n)->get_arg(0))) {
+        r = to_app(n)->get_arg(1);
+        return true;
+    }
+    return false;
+}
+
+/**
+   \brief Return true if n is can be put into the form (+ v t) or (+ (- v) t)
+   \c inv = true will contain true if (- v) is found, and false otherwise.
+*/
+template<typename Config>
+bool poly_rewriter<Config>::is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) {
+    if (!is_add(n) || is_ground(n))
+        return false;
+    
+    ptr_buffer<expr> args;
+    v = 0;
+    expr * curr = to_app(n);
+    bool stop = false;
+    inv = false;
+    while (!stop) {
+        expr * arg;
+        expr * neg_arg;
+        if (is_add(curr)) {
+            arg  = to_app(curr)->get_arg(0);
+            curr = to_app(curr)->get_arg(1);
+        }
+        else {
+            arg  = curr;
+            stop = true;
+        }
+        if (is_ground(arg)) {
+            TRACE("model_checker_bug", tout << "pushing:\n" << mk_pp(arg, m()) << "\n";);
+            args.push_back(arg);
+        }
+        else if (is_var(arg)) {
+            if (v != 0)
+                return false; // already found variable
+            v = to_var(arg);
+        }
+        else if (is_times_minus_one(arg, neg_arg) && is_var(neg_arg)) {
+            if (v != 0)
+                return false; // already found variable
+            v = to_var(neg_arg);
+            inv = true;
+        }
+        else {
+            return false; // non ground term.
+        }
+    }
+    if (v == 0)
+        return false; // did not find variable
+    SASSERT(!args.empty());
+    mk_add(args.size(), args.c_ptr(), t);
+    return true;
+}
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index cbbb9a6bc..2f55cd704 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -410,7 +410,7 @@ void asserted_formulas::apply_quasi_macros() {
     TRACE("before_quasi_macros", display(tout););
     expr_ref_vector  new_exprs(m);
     proof_ref_vector new_prs(m);      
-    quasi_macros proc(m, m_macro_manager, m_simplifier);    
+    quasi_macros proc(m, m_macro_manager);    
     while (proc(m_asserted_formulas.size() - m_asserted_qhead, 
                 m_asserted_formulas.c_ptr() + m_asserted_qhead, 
                 m_asserted_formula_prs.c_ptr() + m_asserted_qhead,
diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp
index 73d1e9f22..2e6a34557 100644
--- a/src/smt/smt_model_finder.cpp
+++ b/src/smt/smt_model_finder.cpp
@@ -23,8 +23,6 @@ Revision History:
 #include "ast/arith_decl_plugin.h"
 #include "ast/bv_decl_plugin.h"
 #include "ast/array_decl_plugin.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/normal_forms/pull_quant.h"
 #include "ast/rewriter/var_subst.h"
 #include "ast/for_each_expr.h"
@@ -392,9 +390,9 @@ namespace smt {
            The idea is to create node objects based on the information produced by the quantifier_analyzer.
         */
         class auf_solver : public evaluator {
-            ast_manager &             m_manager;
-            arith_simplifier_plugin * m_asimp;
-            bv_simplifier_plugin *    m_bvsimp;
+            ast_manager &             m;
+            arith_util                m_arith;
+            bv_util                   m_bv;
             ptr_vector<node>          m_nodes;
             unsigned                  m_next_node_id;
             key2node                  m_uvars;
@@ -466,16 +464,16 @@ namespace smt {
             }
 
         public:
-            auf_solver(ast_manager & m, simplifier & s):
-                m_manager(m),
+            auf_solver(ast_manager & m):
+                m(m),
+                m_arith(m),
+                m_bv(m),
                 m_next_node_id(0),
                 m_context(0),
                 m_ks(m),
                 m_model(0),
                 m_eval_cache_range(m),
                 m_new_constraints(0) {
-                m_asimp  = static_cast<arith_simplifier_plugin*>(s.get_plugin(m.mk_family_id("arith")));
-                m_bvsimp = static_cast<bv_simplifier_plugin*>(s.get_plugin(m.mk_family_id("bv")));
             }
 
             virtual ~auf_solver() {
@@ -488,12 +486,8 @@ namespace smt {
                 m_context = ctx;
             }
             
-            ast_manager & get_manager() const { return m_manager; }
-            
-            arith_simplifier_plugin * get_arith_simp() const { return m_asimp; }
-
-            bv_simplifier_plugin * get_bv_simp() const { return m_bvsimp; }
-            
+            ast_manager & get_manager() const { return m; }
+                        
             void reset() {
                 flush_nodes();
                 m_nodes.reset();
@@ -538,7 +532,7 @@ namespace smt {
             void mk_instantiation_sets() {
                 for (node* curr : m_nodes) {
                     if (curr->is_root()) {
-                        curr->mk_instantiation_set(m_manager);
+                        curr->mk_instantiation_set(m);
                     }
                 }
             }
@@ -554,7 +548,7 @@ namespace smt {
                         for (auto const& kv : elems) {
                             expr * n     = kv.m_key;
                             expr * n_val = eval(n, true);
-                            if (!n_val || !m_manager.is_value(n_val))
+                            if (!n_val || !m.is_value(n_val))
                                 to_delete.push_back(n);
                         }
                         for (expr* e : to_delete) {
@@ -568,7 +562,7 @@ namespace smt {
                 display_key2node(out, m_uvars);
                 display_A_f_is(out);           
                 for (node* n : m_nodes) {
-                    n->display(out, m_manager);
+                    n->display(out, m);
                 }
             }
 
@@ -577,14 +571,14 @@ namespace smt {
                 if (m_eval_cache[model_completion].find(n, r)) {
                     return r;
                 }
-                expr_ref tmp(m_manager);
+                expr_ref tmp(m);
                 if (!m_model->eval(n, tmp, model_completion)) {
                     r = 0;
-                    TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n-----> null\n";);
+                    TRACE("model_finder", tout << "eval\n" << mk_pp(n, m) << "\n-----> null\n";);
                 }
                 else {
                     r = tmp;
-                    TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n----->\n" << mk_pp(r, m_manager) << "\n";);
+                    TRACE("model_finder", tout << "eval\n" << mk_pp(n, m) << "\n----->\n" << mk_pp(r, m) << "\n";);
                 }
                 m_eval_cache[model_completion].insert(n, r);
                 m_eval_cache_range.push_back(r);
@@ -636,7 +630,7 @@ namespace smt {
                     SASSERT(t_val != 0);
                     bool found = false;
                     for (expr* v : ex_vals) {
-                        if (!m_manager.are_distinct(t_val, v)) {
+                        if (!m.are_distinct(t_val, v)) {
                             found = true;
                             break;
                         }
@@ -652,7 +646,7 @@ namespace smt {
             bool is_infinite(sort * s) const {
                 // we should not assume that uninterpreted sorts are infinite in benchmarks with quantifiers.
                 return 
-                    !m_manager.is_uninterp(s) &&
+                    !m.is_uninterp(s) &&
                     s->is_infinite();
             }
 
@@ -665,7 +659,7 @@ namespace smt {
                 app * r = 0;
                 if (m_sort2k.find(s, r))
                     return r;
-                r = m_manager.mk_fresh_const("k", s);
+                r = m.mk_fresh_const("k", s);
                 m_model->register_aux_decl(r->get_decl());
                 m_sort2k.insert(s, r);
                 m_ks.push_back(r);
@@ -680,7 +674,7 @@ namespace smt {
                Remark: this method uses get_fresh_value, so it may fail.
             */
             expr * get_k_interp(app * k) {
-                sort * s = m_manager.get_sort(k);
+                sort * s = m.get_sort(k);
                 SASSERT(is_infinite(s));
                 func_decl * k_decl = k->get_decl();
                 expr * r = m_model->get_const_interp(k_decl);
@@ -691,7 +685,7 @@ namespace smt {
                     return 0;
                 m_model->register_decl(k_decl, r);
                 SASSERT(m_model->get_const_interp(k_decl) == r);
-                TRACE("model_finder", tout << mk_pp(r, m_manager) << "\n";);
+                TRACE("model_finder", tout << mk_pp(r, m) << "\n";);
                 return r;
             }
 
@@ -701,18 +695,18 @@ namespace smt {
                It invokes get_k_interp that may fail.
             */
             bool assert_k_diseq_exceptions(app * k, ptr_vector<expr> const & exceptions) {
-                TRACE("assert_k_diseq_exceptions", tout << "assert_k_diseq_exceptions, " << "k: " << mk_pp(k, m_manager) << "\nexceptions:\n";
-                      for (expr * e : exceptions) tout << mk_pp(e, m_manager) << "\n";);
+                TRACE("assert_k_diseq_exceptions", tout << "assert_k_diseq_exceptions, " << "k: " << mk_pp(k, m) << "\nexceptions:\n";
+                      for (expr * e : exceptions) tout << mk_pp(e, m) << "\n";);
                 expr * k_interp = get_k_interp(k);
                 if (k_interp == 0)
                     return false;
                 for (expr * ex : exceptions) {
                     expr * ex_val = eval(ex, true);
-                    if (!m_manager.are_distinct(k_interp, ex_val)) {
+                    if (!m.are_distinct(k_interp, ex_val)) {
                         SASSERT(m_new_constraints);
                         // This constraint cannot be asserted into m_context during model construction.
                         // We must save it, and assert it during a restart.
-                        m_new_constraints->push_back(m_manager.mk_not(m_manager.mk_eq(k, ex)));
+                        m_new_constraints->push_back(m.mk_not(m.mk_eq(k, ex)));
                     }
                 }
                 return true;
@@ -735,7 +729,7 @@ namespace smt {
                         return;
                     }
                     sort * s = n->get_sort();
-                    TRACE("model_finder", tout << "trying to create k for " << mk_pp(s, m_manager) << ", is_infinite: " << is_infinite(s) << "\n";);
+                    TRACE("model_finder", tout << "trying to create k for " << mk_pp(s, m) << ", is_infinite: " << is_infinite(s) << "\n";);
                     if (is_infinite(s)) {
                         app * k = get_k_for(s);
                         if (assert_k_diseq_exceptions(k, exceptions)) {
@@ -758,28 +752,33 @@ namespace smt {
             void add_mono_exceptions(node * n) {
                 SASSERT(n->is_mono_proj());
                 sort * s = n->get_sort();
-                arith_simplifier_plugin * as = get_arith_simp();
-                bv_simplifier_plugin    * bs = get_bv_simp();
-                bool is_int = as->is_int_sort(s);
-                bool is_bv  = bs->is_bv_sort(s);
-                if (!is_int && !is_bv)
-                    return;
-                poly_simplifier_plugin * ps = as;
-                if (is_bv)
-                    ps = bs;
-                ps->set_curr_sort(s);
-                expr_ref one(m_manager);
-                one = ps->mk_one();
+                arith_rewriter arw(m);
+                bv_rewriter brw(m);
                 ptr_vector<expr> const & exceptions  = n->get_exceptions();
-                for (expr * e : exceptions) {
-                    expr_ref e_plus_1(m_manager);
-                    expr_ref e_minus_1(m_manager);
-                    TRACE("mf_simp_bug", tout << "e:\n" << mk_ismt2_pp(e, m_manager) << "\none:\n" << mk_ismt2_pp(one, m_manager) << "\n";);
-                    ps->mk_add(e, one, e_plus_1); 
-                    ps->mk_sub(e, one, e_minus_1);
-                    // Note: exceptions come from quantifiers bodies. So, they have generation 0.
-                    n->insert(e_plus_1, 0);
-                    n->insert(e_minus_1, 0);
+                if (m_arith.is_int(s)) {
+                    expr_ref one(m_arith.mk_int(1), m);
+                    for (expr * e : exceptions) {
+                        expr_ref e_plus_1(m_arith.mk_add(e, one), m);
+                        expr_ref e_minus_1(m_arith.mk_sub(e, one), m);
+                        TRACE("mf_simp_bug", tout << "e:\n" << mk_ismt2_pp(e, m) << "\none:\n" << mk_ismt2_pp(one, m) << "\n";);
+                        // Note: exceptions come from quantifiers bodies. So, they have generation 0.
+                        n->insert(e_plus_1, 0);
+                        n->insert(e_minus_1, 0);
+                    }
+                }
+                else if (m_bv.is_bv_sort(s)) {
+                    expr_ref one(m_bv.mk_numeral(rational(1), s), m);
+                    for (expr * e : exceptions) {
+                        expr_ref e_plus_1(m_bv.mk_bv_add(e, one), m);
+                        expr_ref e_minus_1(m_bv.mk_bv_sub(e, one), m);
+                        TRACE("mf_simp_bug", tout << "e:\n" << mk_ismt2_pp(e, m) << "\none:\n" << mk_ismt2_pp(one, m) << "\n";);
+                        // Note: exceptions come from quantifiers bodies. So, they have generation 0.
+                        n->insert(e_plus_1, 0);
+                        n->insert(e_minus_1, 0);
+                    }
+                }
+                else {
+                    return;
                 }
             }
 
@@ -797,16 +796,17 @@ namespace smt {
                 }
                 TRACE("model_finder_bug", tout << "values for the instantiation_set of @" << n->get_id() << "\n";
                       for (expr * v : values) {
-                          tout << mk_pp(v, m_manager) << "\n";
+                          tout << mk_pp(v, m) << "\n";
                       });
             }
 
+            template<class T>
             struct numeral_lt {
-                poly_simplifier_plugin * m_p;
-                numeral_lt(poly_simplifier_plugin * p):m_p(p) {}
-                bool operator()(expr * e1, expr * e2) {
+                T& m_util;
+                numeral_lt(T& a): m_util(a) {}
+                bool operator()(expr* e1, expr* e2) {
                     rational v1, v2;
-                    if (m_p->is_numeral(e1, v1) && m_p->is_numeral(e2, v2)) {
+                    if (m_util.is_numeral(e1, v1) && m_util.is_numeral(e2, v1)) {
                         return v1 < v2;
                     }
                     else {
@@ -815,15 +815,16 @@ namespace smt {
                 }
             };
 
+
             struct signed_bv_lt {
-                bv_simplifier_plugin * m_bs;
+                bv_util& m_bv;
                 unsigned               m_bv_size;
-                signed_bv_lt(bv_simplifier_plugin * bs, unsigned sz):m_bs(bs), m_bv_size(sz) {}
+                signed_bv_lt(bv_util& bv, unsigned sz):m_bv(bv), m_bv_size(sz) {}
                 bool operator()(expr * e1, expr * e2) {
                     rational v1, v2;
-                    if (m_bs->is_numeral(e1, v1) && m_bs->is_numeral(e2, v2)) {
-                        v1 = m_bs->norm(v1, m_bv_size, true);
-                        v2 = m_bs->norm(v2, m_bv_size, true);
+                    if (m_bv.is_numeral(e1, v1) && m_bv.is_numeral(e2, v2)) {
+                        v1 = m_bv.norm(v1, m_bv_size, true);
+                        v2 = m_bv.norm(v2, m_bv_size, true);
                         return v1 < v2;
                     }
                     else {
@@ -834,15 +835,14 @@ namespace smt {
 
             void sort_values(node * n, ptr_buffer<expr> & values) {
                 sort * s = n->get_sort();
-                if (get_arith_simp()->is_arith_sort(s)) {
-                    std::sort(values.begin(), values.end(), numeral_lt(get_arith_simp()));
+                if (m_arith.is_int(s) || m_arith.is_real(s)) {
+                    std::sort(values.begin(), values.end(), numeral_lt<arith_util>(m_arith));
                 }
                 else if (!n->is_signed_proj()) {
-                    std::sort(values.begin(), values.end(), numeral_lt(get_bv_simp()));
+                    std::sort(values.begin(), values.end(), numeral_lt<bv_util>(m_bv));
                 }
                 else {
-                    bv_simplifier_plugin * bs = get_bv_simp();
-                    std::sort(values.begin(), values.end(), signed_bv_lt(bs, bs->get_bv_size(s)));
+                    std::sort(values.begin(), values.end(), signed_bv_lt(m_bv, m_bv.get_bv_size(s)));
                }
             }
 
@@ -853,27 +853,25 @@ namespace smt {
                 if (values.empty()) return;
                 sort_values(n, values);
                 sort * s = n->get_sort();
-                arith_simplifier_plugin * as = get_arith_simp();
-                bv_simplifier_plugin    * bs = get_bv_simp();
-                bool is_arith  = as->is_arith_sort(s);
+                bool is_arith  = m_arith.is_int(s) || m_arith.is_real(s);
                 bool is_signed = n->is_signed_proj();
                 unsigned sz = values.size();
                 SASSERT(sz > 0);
-                func_decl * p = m_manager.mk_fresh_func_decl(1, &s, s);
+                func_decl * p = m.mk_fresh_func_decl(1, &s, s);
                 expr * pi     = values[sz - 1];
-                expr_ref var(m_manager);
-                var = m_manager.mk_var(0, s);
+                expr_ref var(m);
+                var = m.mk_var(0, s);
                 for (unsigned i = sz - 1; i >= 1; i--) {
-                    expr_ref c(m_manager);
+                    expr_ref c(m);
                     if (is_arith) 
-                        as->mk_lt(var, values[i], c);
+                        c = m_arith.mk_lt(var, values[i]);
                     else if (!is_signed)
-                        bs->mk_ult(var, values[i], c);
+                        c = m.mk_not(m_bv.mk_ule(values[i], var));
                     else
-                        bs->mk_slt(var, values[i], c);
-                    pi = m_manager.mk_ite(c, values[i-1], pi);
+                        c = m.mk_not(m_bv.mk_sle(values[i], var));
+                    pi = m.mk_ite(c, values[i-1], pi);
                 }
-                func_interp * rpi = alloc(func_interp, m_manager, 1);
+                func_interp * rpi = alloc(func_interp, m, 1);
                 rpi->set_else(pi);
                 m_model->register_aux_decl(p, rpi);
                 n->set_proj(p);
@@ -884,8 +882,8 @@ namespace smt {
                 ptr_buffer<expr> values;
                 get_instantiation_set_values(n, values);
                 sort * s        = n->get_sort();
-                func_decl *   p = m_manager.mk_fresh_func_decl(1, &s, s);
-                func_interp * pi = alloc(func_interp, m_manager, 1);
+                func_decl *   p = m.mk_fresh_func_decl(1, &s, s);
+                func_interp * pi = alloc(func_interp, m, 1);
                 m_model->register_aux_decl(p, pi);
                 if (n->get_else()) {
                     expr * else_val = eval(n->get_else(), true);
@@ -916,7 +914,7 @@ namespace smt {
                     if (!r.contains(f)) {
                         func_interp * fi = m_model->get_func_interp(f);
                         if (fi == 0) {
-                            fi = alloc(func_interp, m_manager, f->get_arity());
+                            fi = alloc(func_interp, m, f->get_arity());
                             m_model->register_decl(f, fi);
                             SASSERT(fi->is_partial());
                         }
@@ -938,7 +936,7 @@ namespace smt {
                 for (node * n : m_root_nodes) {
                     SASSERT(n->is_root());
                     sort * s = n->get_sort();
-                    if (m_manager.is_uninterp(s) && 
+                    if (m.is_uninterp(s) && 
                         // Making all uninterpreted sorts finite.
                         // n->must_avoid_itself() && 
                         !m_model->is_finite(s)) {
@@ -962,7 +960,7 @@ namespace smt {
                         // If these module values "leak" inside the logical context, they may affect satisfiability.
                         // 
                         sort * ns = n->get_sort();
-                        if (m_manager.is_fully_interp(ns)) {
+                        if (m.is_fully_interp(ns)) {
                             n->insert(m_model->get_some_value(ns), 0);
                         }
                         else {
@@ -973,18 +971,18 @@ namespace smt {
                         sort2elems.insert(n->get_sort(), elems.begin()->m_key);
                     }
                 }
-                expr_ref_vector trail(m_manager);
+                expr_ref_vector trail(m);
                 for (unsigned i = 0; i < need_fresh.size(); ++i) {
                     expr * e;
                     node* n = need_fresh[i];
                     sort* s = n->get_sort();
                     if (!sort2elems.find(s, e)) {
-                        e = m_manager.mk_fresh_const("elem", s);
+                        e = m.mk_fresh_const("elem", s);
                         trail.push_back(e);
                         sort2elems.insert(s, e);
                     }
                     n->insert(e, 0);
-                    TRACE("model_finder", tout << "fresh constant: " << mk_pp(e, m_manager) << "\n";);
+                    TRACE("model_finder", tout << "fresh constant: " << mk_pp(e, m) << "\n";);
                 }
             }
 
@@ -1037,13 +1035,13 @@ namespace smt {
                     if (fi->is_constant())
                         continue; // there is no point in using the projection for fi, since fi is the constant function.
                     
-                    expr_ref_vector args(m_manager);
+                    expr_ref_vector args(m);
                     bool has_proj = false;
                     for (unsigned i = 0; i < arity; i++) {
-                        var * v = m_manager.mk_var(i, f->get_domain(i));
+                        var * v = m.mk_var(i, f->get_domain(i));
                         func_decl * pi = get_f_i_proj(f, i);
                         if (pi != 0) {
-                            args.push_back(m_manager.mk_app(pi, v));
+                            args.push_back(m.mk_app(pi, v));
                             has_proj = true;
                         }
                         else {
@@ -1052,11 +1050,11 @@ namespace smt {
                     }
                     if (has_proj) {
                         // f_aux will be assigned to the old interpretation of f.
-                        func_decl *   f_aux  = m_manager.mk_fresh_func_decl(f->get_name(), symbol::null, arity, f->get_domain(), f->get_range());
-                        func_interp * new_fi = alloc(func_interp, m_manager, arity);
-                        new_fi->set_else(m_manager.mk_app(f_aux, args.size(), args.c_ptr()));
+                        func_decl *   f_aux  = m.mk_fresh_func_decl(f->get_name(), symbol::null, arity, f->get_domain(), f->get_range());
+                        func_interp * new_fi = alloc(func_interp, m, arity);
+                        new_fi->set_else(m.mk_app(f_aux, args.size(), args.c_ptr()));
                         TRACE("model_finder", tout << "Setting new interpretation for " << f->get_name() << "\n" << 
-                              mk_pp(new_fi->get_else(), m_manager) << "\n";);
+                              mk_pp(new_fi->get_else(), m) << "\n";);
                         m_model->reregister_decl(f, new_fi, f_aux);
                     }
                 }
@@ -1256,21 +1254,21 @@ namespace smt {
                 if (A_f_i == S_j) {
                     // there is no finite fixpoint... we just copy the i-th arguments of A_f_i - m_offset
                     // hope for the best...
-                    arith_simplifier_plugin * as = s.get_arith_simp();
-                    bv_simplifier_plugin * bs    = s.get_bv_simp();
                     node * S_j = s.get_uvar(q, m_var_j);
                     enode_vector::const_iterator it  = ctx->begin_enodes_of(m_f);
                     enode_vector::const_iterator end = ctx->end_enodes_of(m_f);
                     for (; it != end; it++) {
                         enode * n = *it;
                         if (ctx->is_relevant(n)) {
+                            arith_util arith(ctx->get_manager());
+                            bv_util bv(ctx->get_manager());
                             enode * e_arg = n->get_arg(m_arg_i);
                             expr * arg    = e_arg->get_owner();
                             expr_ref arg_minus_k(ctx->get_manager());
-                            if (bs->is_bv(arg))
-                                bs->mk_sub(arg, m_offset, arg_minus_k);
+                            if (bv.is_bv(arg))
+                                arg_minus_k = bv.mk_bv_sub(arg, m_offset);
                             else
-                                as->mk_sub(arg, m_offset, arg_minus_k);
+                                arg_minus_k = arith.mk_sub(arg, m_offset);
                             S_j->insert(arg_minus_k, e_arg->get_generation());
                         }
                     }
@@ -1290,20 +1288,20 @@ namespace smt {
 
             template<bool PLUS>
             void copy_instances(node * from, node * to, auf_solver & s) {
-                arith_simplifier_plugin * as = s.get_arith_simp();
-                bv_simplifier_plugin * bs    = s.get_bv_simp();
-                poly_simplifier_plugin * ps  = as;
-                if (bs->is_bv_sort(from->get_sort()))
-                    ps = bs;
                 instantiation_set const * from_s        = from->get_instantiation_set();
                 obj_map<expr, unsigned> const & elems_s = from_s->get_elems();
+
+                arith_util arith(m_offset.get_manager());
+                bv_util bv(m_offset.get_manager());
+                bool is_bv = bv.is_bv_sort(from->get_sort());
+
                 for (auto const& kv : elems_s) {
                     expr * n = kv.m_key;
                     expr_ref n_k(m_offset.get_manager());
                     if (PLUS)
-                        ps->mk_add(n, m_offset, n_k);
+                        n_k = is_bv ? bv.mk_bv_add(n, m_offset) : arith.mk_add(n, m_offset);
                     else
-                        ps->mk_sub(n, m_offset, n_k);
+                        n_k = is_bv ? bv.mk_bv_sub(n, m_offset) : arith.mk_sub(n, m_offset);
                     to->insert(n_k, kv.m_value);
                 }
             }
@@ -1897,11 +1895,8 @@ namespace smt {
                 m_info->insert_qinfo(qi);
             }
 
-            arith_simplifier_plugin * get_arith_simp() const { return m_mutil.get_arith_simp(); }
-            bv_simplifier_plugin * get_bv_simp() const { return m_mutil.get_bv_simp(); }
-
-            bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) const {
-                return get_arith_simp()->is_var_plus_ground(n, inv, v, t) || get_bv_simp()->is_var_plus_ground(n, inv, v, t);
+            bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) {
+                return m_mutil.is_var_plus_ground(n, inv, v, t);
             }
 
             bool is_var_plus_ground(expr * n, var * & v, expr_ref & t) {
@@ -1917,10 +1912,7 @@ namespace smt {
             }
 
             bool is_zero(expr * n) const {
-                if (get_bv_simp()->is_bv(n))
-                    return get_bv_simp()->is_zero_safe(n);
-                else 
-                    return get_arith_simp()->is_zero_safe(n);
+                return m_mutil.is_zero_safe(n);
             }
 
             bool is_times_minus_one(expr * n, expr * & arg) const {
@@ -1951,7 +1943,7 @@ namespace smt {
                 m_mutil.mk_add(t1, t2, r);
             }
 
-            bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t, bool & inv) const {
+            bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t, bool & inv) {
                 inv = false; // true if invert the sign
                 TRACE("is_var_and_ground", tout << "is_var_and_ground: " << mk_ismt2_pp(lhs, m_manager) << " " << mk_ismt2_pp(rhs, m_manager) << "\n";);
                 if (is_var(lhs) && is_ground(rhs)) {
@@ -1986,12 +1978,12 @@ namespace smt {
                 return false;
             }
 
-            bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) const {
+            bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) {
                 bool inv;
                 return is_var_and_ground(lhs, rhs, v, t, inv);
             }
             
-            bool is_x_eq_t_atom(expr * n, var * & v, expr_ref & t) const {
+            bool is_x_eq_t_atom(expr * n, var * & v, expr_ref & t) {
                 if (!is_app(n))
                     return false;
                 if (m_manager.is_eq(n))
@@ -1999,7 +1991,7 @@ namespace smt {
                 return false;
             }
 
-            bool is_var_minus_var(expr * n, var * & v1, var * & v2) const {
+            bool is_var_minus_var(expr * n, var * & v1, var * & v2) {
                 if (!is_add(n))
                     return false;
                 expr * arg1 = to_app(n)->get_arg(0);
@@ -2018,7 +2010,7 @@ namespace smt {
                 return true;
             }
 
-            bool is_var_and_var(expr * lhs, expr * rhs, var * & v1, var * & v2) const {
+            bool is_var_and_var(expr * lhs, expr * rhs, var * & v1, var * & v2) {
                 if (is_var(lhs) && is_var(rhs)) {
                     v1 = to_var(lhs);
                     v2 = to_var(rhs);
@@ -2029,11 +2021,11 @@ namespace smt {
                     (is_var_minus_var(rhs, v1, v2) && is_zero(lhs));
             }
 
-            bool is_x_eq_y_atom(expr * n, var * & v1, var * & v2) const {
+            bool is_x_eq_y_atom(expr * n, var * & v1, var * & v2) {
                 return m_manager.is_eq(n) && is_var_and_var(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v1, v2);
             }
 
-            bool is_x_gle_y_atom(expr * n, var * & v1, var * & v2) const {
+            bool is_x_gle_y_atom(expr * n, var * & v1, var * & v2) {
                 return is_le_ge(n) && is_var_and_var(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v1, v2);
             }
 
@@ -2379,10 +2371,10 @@ namespace smt {
             
  
         public:
-            quantifier_analyzer(model_finder& mf, ast_manager & m, simplifier & s):
+            quantifier_analyzer(model_finder& mf, ast_manager & m):
                 m_mf(mf),
                 m_manager(m),
-                m_mutil(m, s),
+                m_mutil(m),
                 m_array_util(m), 
                 m_arith_util(m),
                 m_bv_util(m),
@@ -3152,11 +3144,11 @@ namespace smt {
     //
     // -----------------------------------
     
-    model_finder::model_finder(ast_manager & m, simplifier & s):
+    model_finder::model_finder(ast_manager & m):
         m_manager(m),
         m_context(0),
-        m_analyzer(alloc(quantifier_analyzer, *this, m, s)),
-        m_auf_solver(alloc(auf_solver, m, s)),
+        m_analyzer(alloc(quantifier_analyzer, *this, m)),
+        m_auf_solver(alloc(auf_solver, m)),
         m_dependencies(m),
         m_sm_solver(alloc(simple_macro_solver, m, m_q2info)),
         m_hint_solver(alloc(hint_solver, m, m_q2info)),
diff --git a/src/smt/smt_model_finder.h b/src/smt/smt_model_finder.h
index f02640659..2b79ab265 100644
--- a/src/smt/smt_model_finder.h
+++ b/src/smt/smt_model_finder.h
@@ -48,7 +48,6 @@ Revision History:
 
 #include "ast/ast.h"
 #include "ast/func_decl_dependencies.h"
-#include "ast/simplifier/simplifier.h"
 #include "smt/proto_model/proto_model.h"
 #include "util/cooperate.h"
 #include "tactic/tactic_exception.h"
@@ -107,7 +106,7 @@ namespace smt {
 
 
     public:
-        model_finder(ast_manager & m, simplifier & s);
+        model_finder(ast_manager & m);
         ~model_finder();
         void set_context(context * ctx);
         
diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp
index 84ed7c8cd..6a2f848d9 100644
--- a/src/smt/smt_quantifier.cpp
+++ b/src/smt/smt_quantifier.cpp
@@ -434,7 +434,7 @@ namespace smt {
 
             m_mam           = mk_mam(*m_context);
             m_lazy_mam      = mk_mam(*m_context);
-            m_model_finder  = alloc(model_finder, m, m_context->get_simplifier());
+            m_model_finder  = alloc(model_finder, m);
             m_model_checker = alloc(model_checker, m, *m_fparams, *(m_model_finder.get()));
 
             m_model_finder->set_context(m_context);
diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp
index ab68a2b63..f7312fd2e 100644
--- a/src/tactic/ufbv/quasi_macros_tactic.cpp
+++ b/src/tactic/ufbv/quasi_macros_tactic.cpp
@@ -63,7 +63,7 @@ class quasi_macros_tactic : public tactic {
             simp.register_plugin(bvsimp);
                 
             macro_manager mm(m_manager, simp);
-            quasi_macros qm(m_manager, mm, simp);
+            quasi_macros qm(m_manager, mm);
             bool more = true;
         
             expr_ref_vector forms(m_manager), new_forms(m_manager);

From f7ca7409cec5ce622725a9e35eb3ff935bdc890e Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 22 Aug 2017 17:05:40 -0700
Subject: [PATCH 04/74] fix regressions introduced when modifying macro_util

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/macros/macro_util.cpp | 13 +++++-----
 src/ast/macros/macro_util.h   |  4 +--
 src/smt/smt_model_finder.cpp  | 48 +++++++++++++++++++++++------------
 3 files changed, 41 insertions(+), 24 deletions(-)

diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp
index b7eb9657f..6bc1ee66e 100644
--- a/src/ast/macros/macro_util.cpp
+++ b/src/ast/macros/macro_util.cpp
@@ -105,19 +105,19 @@ app * macro_util::mk_zero(sort * s) const {
 
 void macro_util::mk_sub(expr * t1, expr * t2, expr_ref & r) const {
     if (is_bv(t1)) {
-        r = m_bv.mk_bv_sub(t1, t2);
+        m_bv_rw.mk_sub(t1, t2, r);
     }
     else {
-        r = m_arith.mk_sub(t1, t2);
+        m_arith_rw.mk_sub(t1, t2, r);
     }
 }
 
 void macro_util::mk_add(expr * t1, expr * t2, expr_ref & r) const {
     if (is_bv(t1)) {
-        r = m_bv.mk_bv_add(t1, t2);
+        m_bv_rw.mk_add(t1, t2, r);
     }
     else {
-        r = m_arith.mk_add(t1, t2);
+        m_arith_rw.mk_add(t1, t2, r);
     }
 }
 
@@ -312,9 +312,10 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex
     expr_ref tmp(m_manager);
     tmp = m_arith.mk_add(args.size(), args.c_ptr());
     if (inv)
-        def = m_arith.mk_sub(tmp, rhs);
+        mk_sub(tmp, rhs, def);
     else
-        def = m_arith.mk_sub(rhs, tmp);
+        mk_sub(rhs, tmp, def);
+    TRACE("macro_util", tout << def << "\n";);
     return true;
 }
 
diff --git a/src/ast/macros/macro_util.h b/src/ast/macros/macro_util.h
index 91d96cc5e..3ab00df2a 100644
--- a/src/ast/macros/macro_util.h
+++ b/src/ast/macros/macro_util.h
@@ -60,8 +60,8 @@ private:
     ast_manager &               m_manager;
     bv_util                     m_bv;
     arith_util                  m_arith;
-    arith_rewriter              m_arith_rw;
-    bv_rewriter                 m_bv_rw;
+    mutable arith_rewriter      m_arith_rw;
+    mutable bv_rewriter         m_bv_rw;
     obj_hashtable<func_decl> *  m_forbidden_set;
 
     bool is_forbidden(func_decl * f) const { return m_forbidden_set != 0 && m_forbidden_set->contains(f); }
diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp
index 2e6a34557..f678ee3e7 100644
--- a/src/smt/smt_model_finder.cpp
+++ b/src/smt/smt_model_finder.cpp
@@ -755,11 +755,13 @@ namespace smt {
                 arith_rewriter arw(m);
                 bv_rewriter brw(m);
                 ptr_vector<expr> const & exceptions  = n->get_exceptions();
+                expr_ref e_minus_1(m), e_plus_1(m);
                 if (m_arith.is_int(s)) {
                     expr_ref one(m_arith.mk_int(1), m);
+                    arith_rewriter arith_rw(m);
                     for (expr * e : exceptions) {
-                        expr_ref e_plus_1(m_arith.mk_add(e, one), m);
-                        expr_ref e_minus_1(m_arith.mk_sub(e, one), m);
+                        arith_rw.mk_sub(e, one, e_minus_1);
+                        arith_rw.mk_add(e, one, e_plus_1);
                         TRACE("mf_simp_bug", tout << "e:\n" << mk_ismt2_pp(e, m) << "\none:\n" << mk_ismt2_pp(one, m) << "\n";);
                         // Note: exceptions come from quantifiers bodies. So, they have generation 0.
                         n->insert(e_plus_1, 0);
@@ -768,9 +770,10 @@ namespace smt {
                 }
                 else if (m_bv.is_bv_sort(s)) {
                     expr_ref one(m_bv.mk_numeral(rational(1), s), m);
+                    bv_rewriter bv_rw(m);
                     for (expr * e : exceptions) {
-                        expr_ref e_plus_1(m_bv.mk_bv_add(e, one), m);
-                        expr_ref e_minus_1(m_bv.mk_bv_sub(e, one), m);
+                        bv_rw.mk_add(e, one, e_plus_1);
+                        bv_rw.mk_sub(e, one, e_minus_1);
                         TRACE("mf_simp_bug", tout << "e:\n" << mk_ismt2_pp(e, m) << "\none:\n" << mk_ismt2_pp(one, m) << "\n";);
                         // Note: exceptions come from quantifiers bodies. So, they have generation 0.
                         n->insert(e_plus_1, 0);
@@ -806,7 +809,7 @@ namespace smt {
                 numeral_lt(T& a): m_util(a) {}
                 bool operator()(expr* e1, expr* e2) {
                     rational v1, v2;
-                    if (m_util.is_numeral(e1, v1) && m_util.is_numeral(e2, v1)) {
+                    if (m_util.is_numeral(e1, v1) && m_util.is_numeral(e2, v2)) {
                         return v1 < v2;
                     }
                     else {
@@ -1260,15 +1263,16 @@ namespace smt {
                     for (; it != end; it++) {
                         enode * n = *it;
                         if (ctx->is_relevant(n)) {
-                            arith_util arith(ctx->get_manager());
+                            arith_rewriter arith_rw(ctx->get_manager());
                             bv_util bv(ctx->get_manager());
+                            bv_rewriter bv_rw(ctx->get_manager());
                             enode * e_arg = n->get_arg(m_arg_i);
                             expr * arg    = e_arg->get_owner();
                             expr_ref arg_minus_k(ctx->get_manager());
-                            if (bv.is_bv(arg))
-                                arg_minus_k = bv.mk_bv_sub(arg, m_offset);
+                            if (bv.is_bv(arg))                                
+                                bv_rw.mk_sub(arg, m_offset, arg_minus_k);
                             else
-                                arg_minus_k = arith.mk_sub(arg, m_offset);
+                                arith_rw.mk_sub(arg, m_offset, arg_minus_k);
                             S_j->insert(arg_minus_k, e_arg->get_generation());
                         }
                     }
@@ -1291,17 +1295,29 @@ namespace smt {
                 instantiation_set const * from_s        = from->get_instantiation_set();
                 obj_map<expr, unsigned> const & elems_s = from_s->get_elems();
 
-                arith_util arith(m_offset.get_manager());
-                bv_util bv(m_offset.get_manager());
-                bool is_bv = bv.is_bv_sort(from->get_sort());
+                arith_rewriter arith_rw(m_offset.get_manager());
+                bv_rewriter bv_rw(m_offset.get_manager());
+                bool is_bv = bv_util(m_offset.get_manager()).is_bv_sort(from->get_sort());
 
                 for (auto const& kv : elems_s) {
                     expr * n = kv.m_key;
                     expr_ref n_k(m_offset.get_manager());
-                    if (PLUS)
-                        n_k = is_bv ? bv.mk_bv_add(n, m_offset) : arith.mk_add(n, m_offset);
-                    else
-                        n_k = is_bv ? bv.mk_bv_sub(n, m_offset) : arith.mk_sub(n, m_offset);
+                    if (PLUS) {
+                        if (is_bv) {
+                            bv_rw.mk_add(n, m_offset, n_k);
+                        }
+                        else {
+                            arith_rw.mk_add(n, m_offset, n_k);
+                        }
+                    }
+                    else {
+                        if (is_bv) {
+                            bv_rw.mk_sub(n, m_offset, n_k);
+                        }
+                        else {
+                            arith_rw.mk_sub(n, m_offset, n_k);
+                        }
+                    }
                     to->insert(n_k, kv.m_value);
                 }
             }

From ce04c18a7a8965ffc1642e56779e1e5db9c6d750 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 22 Aug 2017 22:14:13 -0700
Subject: [PATCH 05/74] trying to get rid of last simplifier dependency in
 macros

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/fpa/fpa2bv_converter.h          |   4 +-
 src/ast/macros/macro_manager.cpp        | 159 ++++++++++++++++++++----
 src/ast/macros/macro_manager.h          |  14 ++-
 src/ast/rewriter/poly_rewriter_def.h    |   4 +
 src/muz/pdr/pdr_util.h                  |   2 +-
 src/muz/rel/dl_bound_relation.cpp       |   2 +-
 src/muz/rel/dl_bound_relation.h         |   3 +-
 src/smt/asserted_formulas.cpp           |   2 +-
 src/tactic/ufbv/macro_finder_tactic.cpp |   2 +-
 src/tactic/ufbv/quasi_macros_tactic.cpp |   2 +-
 10 files changed, 158 insertions(+), 36 deletions(-)

diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h
index 19a52dd23..1e3e5d9b3 100644
--- a/src/ast/fpa/fpa2bv_converter.h
+++ b/src/ast/fpa/fpa2bv_converter.h
@@ -29,7 +29,7 @@ Notes:
 #include "ast/dl_decl_plugin.h"
 #include "ast/pb_decl_plugin.h"
 #include "ast/seq_decl_plugin.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
+#include "ast/rewriter/bool_rewriter.h"
 
 class fpa2bv_converter {
 public:
@@ -39,7 +39,7 @@ public:
 
 protected:
     ast_manager              & m;
-    basic_simplifier_plugin    m_simp;
+    bool_rewriter              m_simp;
     fpa_util                   m_util;
     bv_util                    m_bv_util;
     arith_util                 m_arith_util;
diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp
index f26a87445..70434435b 100644
--- a/src/ast/macros/macro_manager.cpp
+++ b/src/ast/macros/macro_manager.cpp
@@ -22,12 +22,14 @@ Revision History:
 #include "ast/macros/macro_manager.h"
 #include "ast/for_each_expr.h"
 #include "ast/rewriter/var_subst.h"
+#include "ast/rewriter/th_rewriter.h"
+#include "ast/rewriter/rewriter_def.h"
 #include "ast/ast_pp.h"
 #include "ast/recurse_expr_def.h"
 
-macro_manager::macro_manager(ast_manager & m, simplifier & s):
-    m_manager(m),
-    m_simplifier(s),
+
+macro_manager::macro_manager(ast_manager & m):
+    m(m),
     m_util(m),
     m_decls(m),
     m_macros(m),
@@ -60,12 +62,12 @@ void macro_manager::restore_decls(unsigned old_sz) {
     for (unsigned i = old_sz; i < sz; i++) {
         m_decl2macro.erase(m_decls.get(i));
         m_deps.erase(m_decls.get(i));
-        if (m_manager.proofs_enabled())
+        if (m.proofs_enabled())
             m_decl2macro_pr.erase(m_decls.get(i));
     }
     m_decls.shrink(old_sz);
     m_macros.shrink(old_sz);
-    if (m_manager.proofs_enabled())
+    if (m.proofs_enabled())
         m_macro_prs.shrink(old_sz);
 }
 
@@ -88,8 +90,8 @@ void macro_manager::reset() {
     m_deps.reset();
 }
 
-bool macro_manager::insert(func_decl * f, quantifier * m, proof * pr) {
-    TRACE("macro_insert", tout << "trying to create macro: " << f->get_name() << "\n" << mk_pp(m, m_manager) << "\n";);
+bool macro_manager::insert(func_decl * f, quantifier * q, proof * pr) {
+    TRACE("macro_insert", tout << "trying to create macro: " << f->get_name() << "\n" << mk_pp(q, m) << "\n";);
 
     // if we already have a macro for f then return false;
     if (m_decls.contains(f)) {
@@ -99,7 +101,7 @@ bool macro_manager::insert(func_decl * f, quantifier * m, proof * pr) {
 
     app * head;
     expr * definition;
-    get_head_def(m, f, head, definition);
+    get_head_def(q, f, head, definition);
 
     func_decl_set * s = m_deps.mk_func_decl_set();
     m_deps.collect_func_decls(definition, s);
@@ -108,10 +110,10 @@ bool macro_manager::insert(func_decl * f, quantifier * m, proof * pr) {
     }
 
     // add macro
-    m_decl2macro.insert(f, m);
+    m_decl2macro.insert(f, q);
     m_decls.push_back(f);
-    m_macros.push_back(m);
-    if (m_manager.proofs_enabled()) {
+    m_macros.push_back(q);
+    if (m.proofs_enabled()) {
         m_macro_prs.push_back(pr);
         m_decl2macro_pr.insert(f, pr);
     }
@@ -152,7 +154,7 @@ void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) {
 
 void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const {
     app * body = to_app(q->get_expr());
-    SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body));
+    SASSERT(m.is_eq(body) || m.is_iff(body));
     expr * lhs = to_app(body)->get_arg(0);
     expr * rhs = to_app(body)->get_arg(1);
     SASSERT(is_app_of(lhs, d) || is_app_of(rhs, d));
@@ -177,7 +179,7 @@ void macro_manager::display(std::ostream & out) {
         expr * def;
         get_head_def(q, f, head, def);
         SASSERT(q);
-        out << mk_pp(head, m_manager) << " ->\n" << mk_pp(def, m_manager) << "\n";
+        out << mk_pp(head, m) << " ->\n" << mk_pp(def, m) << "\n";
     }
 }
 
@@ -188,12 +190,115 @@ func_decl * macro_manager::get_macro_interpretation(unsigned i, expr_ref & inter
     expr * def;
     get_head_def(q, f, head, def);
     TRACE("macro_bug",
-          tout << f->get_name() << "\n" << mk_pp(head, m_manager) << "\n" << mk_pp(q, m_manager) << "\n";);
+          tout << f->get_name() << "\n" << mk_pp(head, m) << "\n" << mk_pp(q, m) << "\n";);
     m_util.mk_macro_interpretation(head, q->get_num_decls(), def, interp);
     return f;
 }
 
-macro_manager::macro_expander::macro_expander(ast_manager & m, macro_manager & mm, simplifier & s):
+struct macro_manager::macro_expander_cfg : public default_rewriter_cfg {
+    ast_manager& m; 
+    macro_manager& mm;
+    expr_ref_vector m_trail;
+
+    macro_expander_cfg(ast_manager& m, macro_manager& mm):
+        m(m),
+        mm(mm),
+        m_trail(m)
+    {}
+
+    bool rewrite_patterns() const { return false; }
+    bool flat_assoc(func_decl * f) const { return false; }
+    br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
+        result_pr = 0;
+        return BR_FAILED;
+    }
+
+    bool reduce_quantifier(quantifier * old_q, 
+                           expr * new_body, 
+                           expr * const * new_patterns, 
+                           expr * const * new_no_patterns,
+                           expr_ref & result,
+                           proof_ref & result_pr) {
+
+        // If a macro was expanded in a pattern, we must erase it since it may not be a valid pattern anymore.
+        // The MAM assumes valid patterns, and it crashes if invalid patterns are provided.
+        // For example, it will crash if the pattern does not contain all variables.
+        //
+        // Alternative solution: use pattern_validation to check if the pattern is still valid.
+        // I'm not sure if this is a good solution, since the pattern may be meaningless after the macro expansion.
+        // So, I'm just erasing them.
+
+        bool erase_patterns = false;
+        for (unsigned i = 0; !erase_patterns && i < old_q->get_num_patterns(); i++) {
+            if (old_q->get_pattern(i) != new_patterns[i]) 
+                erase_patterns = true;
+        }
+        for (unsigned i = 0; !erase_patterns && i < old_q->get_num_no_patterns(); i++) {
+            if (old_q->get_no_pattern(i) != new_no_patterns[i])
+                erase_patterns = true;
+        }
+        if (erase_patterns) {
+            result = m.update_quantifier(old_q, 0, 0, 0, 0, new_body);
+        }
+        return erase_patterns;
+    }
+
+    bool get_subst(expr * _n, expr* & r, proof* & p) {
+        if (!is_app(_n))
+            return false;
+        app * n = to_app(_n);
+        quantifier * q = 0;
+        func_decl * d  = n->get_decl();
+        TRACE("macro_manager", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";);
+        if (mm.m_decl2macro.find(d, q)) {
+            TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";);
+            app * head = 0;
+            expr * def = 0;
+            mm.get_head_def(q, d, head, def);
+            unsigned num = n->get_num_args();
+            SASSERT(head && def);
+            ptr_buffer<expr> subst_args;
+            subst_args.resize(num, 0);
+            for (unsigned i = 0; i < num; i++) {
+                var * v = to_var(head->get_arg(i));
+                SASSERT(v->get_idx() < num);
+                unsigned nidx = num - v->get_idx() - 1;
+                SASSERT(subst_args[nidx] == 0);
+                subst_args[nidx] = n->get_arg(i);
+            }
+            var_subst s(m);
+            expr_ref rr(m);
+            s(def, num, subst_args.c_ptr(), rr);
+            m_trail.push_back(rr);
+            r = rr;
+            if (m.proofs_enabled()) {
+                expr_ref instance(m);
+                s(q->get_expr(), num, subst_args.c_ptr(), instance);
+                proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr());
+                proof * q_pr  = 0;
+                mm.m_decl2macro_pr.find(d, q_pr);
+                SASSERT(q_pr != 0);
+                proof * prs[2] = { qi_pr, q_pr };
+                p = m.mk_unit_resolution(2, prs);
+            }
+            else {
+                p = 0;
+            }
+            return true;
+        }
+        return false;
+    }
+};
+
+struct macro_manager::macro_expander_rw : public rewriter_tpl<macro_manager::macro_expander_cfg> {
+    macro_expander_cfg m_cfg;
+    macro_expander_rw(ast_manager& m, macro_manager& mm):
+        rewriter_tpl<macro_manager::macro_expander_cfg>(m, m.proofs_enabled(), m_cfg),
+        m_cfg(m, mm) 
+    {}
+};
+
+macro_manager::macro_expander::macro_expander(ast_manager & m, macro_manager & mm):
     simplifier(m),
     m_macro_manager(mm) {
     // REMARK: theory simplifier should not be used by macro_expander...
@@ -295,20 +400,30 @@ bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref
 void macro_manager::expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref & new_pr) {
     if (has_macros()) {
         // Expand macros with "real" proof production support (NO rewrite*)
-        expr_ref old_n(m_manager);
-        proof_ref old_pr(m_manager);
+        expr_ref old_n(m);
+        proof_ref old_pr(m);
         old_n  = n;
         old_pr = pr;
+        bool change = false;
         for (;;) {
-            macro_expander proc(m_manager, *this, m_simplifier);
-            proof_ref n_eq_r_pr(m_manager);
-            TRACE("macro_manager_bug", tout << "expand_macros:\n" << mk_pp(n, m_manager) << "\n";);
+            macro_expander_rw proc(m, *this);
+            proof_ref n_eq_r_pr(m);
+            TRACE("macro_manager_bug", tout << "expand_macros:\n" << mk_pp(n, m) << "\n";);
             proc(old_n, r, n_eq_r_pr);
-            new_pr = m_manager.mk_modus_ponens(old_pr, n_eq_r_pr);
+            new_pr = m.mk_modus_ponens(old_pr, n_eq_r_pr);
             if (r.get() == old_n.get())
-                return;
+                break;
             old_n  = r;
             old_pr = new_pr;
+            change = true;
+        }
+        // apply th_rewrite to the result.
+        if (change) {
+            th_rewriter rw(m);
+            proof_ref rw_pr(m);
+            expr_ref r1(r, m);
+            rw(r1, r, rw_pr);
+            new_pr = m.mk_modus_ponens(new_pr, rw_pr);
         }
     }
     else {
diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h
index a0a42b790..98e242cd8 100644
--- a/src/ast/macros/macro_manager.h
+++ b/src/ast/macros/macro_manager.h
@@ -19,8 +19,8 @@ Revision History:
 #ifndef MACRO_MANAGER_H_
 #define MACRO_MANAGER_H_
 
-#include "ast/ast_util.h"
 #include "util/obj_hashtable.h"
+#include "ast/ast_util.h"
 #include "ast/simplifier/simplifier.h"
 #include "ast/recurse_expr.h"
 #include "ast/func_decl_dependencies.h"
@@ -36,8 +36,7 @@ Revision History:
    It has support for backtracking and tagging declarations in an expression as forbidded for being macros.
 */
 class macro_manager {
-    ast_manager &                    m_manager;
-    simplifier  &                    m_simplifier;
+    ast_manager &                    m;
     macro_util                       m_util;
 
     obj_map<func_decl, quantifier *> m_decl2macro;    // func-decl -> quantifier
@@ -58,21 +57,24 @@ class macro_manager {
     void restore_decls(unsigned old_sz);
     void restore_forbidden(unsigned old_sz);
     
+    struct macro_expander_cfg;
+    struct macro_expander_rw;
+
     class macro_expander : public simplifier {
     protected:
         macro_manager &   m_macro_manager;
         virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
         virtual void reduce1_quantifier(quantifier * q);
     public:
-        macro_expander(ast_manager & m, macro_manager & mm, simplifier & s);
+        macro_expander(ast_manager & m, macro_manager & mm);
         ~macro_expander();
     };
     friend class macro_expander;
 
 public:
-    macro_manager(ast_manager & m, simplifier & s);
+    macro_manager(ast_manager & m);
     ~macro_manager();
-    ast_manager & get_manager() const { return m_manager; }
+    ast_manager & get_manager() const { return m; }
     macro_util & get_util() { return m_util; }
     bool insert(func_decl * f, quantifier * m, proof * pr);
     bool has_macros() const { return !m_macros.empty(); }
diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h
index a8d115d64..6b747756c 100644
--- a/src/ast/rewriter/poly_rewriter_def.h
+++ b/src/ast/rewriter/poly_rewriter_def.h
@@ -118,6 +118,9 @@ expr * poly_rewriter<Config>::mk_mul_app(numeral const & c, expr * arg) {
     if (c.is_one()) {
         return arg;
     }
+    else if (is_zero(arg)) {
+        return arg;
+    }
     else {
         expr * new_args[2] = { mk_numeral(c), arg };
         return mk_mul_app(2, new_args); 
@@ -654,6 +657,7 @@ br_status poly_rewriter<Config>::mk_sub(unsigned num_args, expr * const * args,
     ptr_buffer<expr> new_args;
     new_args.push_back(args[0]);
     for (unsigned i = 1; i < num_args; i++) {
+        if (is_zero(args[i])) continue;
         expr * aux_args[2] = { minus_one, args[i] };
         new_args.push_back(mk_mul_app(2, aux_args));
     }
diff --git a/src/muz/pdr/pdr_util.h b/src/muz/pdr/pdr_util.h
index fccb0c40f..51f6978ec 100644
--- a/src/muz/pdr/pdr_util.h
+++ b/src/muz/pdr/pdr_util.h
@@ -22,9 +22,9 @@ Revision History:
 
 #include "ast/ast.h"
 #include "ast/ast_pp.h"
+#include "ast/ast_util.h"
 #include "util/obj_hashtable.h"
 #include "util/ref_vector.h"
-#include "ast/simplifier/simplifier.h"
 #include "util/trace.h"
 #include "util/vector.h"
 #include "ast/arith_decl_plugin.h"
diff --git a/src/muz/rel/dl_bound_relation.cpp b/src/muz/rel/dl_bound_relation.cpp
index de6c76456..9dc0eb8d5 100644
--- a/src/muz/rel/dl_bound_relation.cpp
+++ b/src/muz/rel/dl_bound_relation.cpp
@@ -653,7 +653,7 @@ namespace datalog {
     void bound_relation::to_formula(expr_ref& fml) const {
         ast_manager& m = get_plugin().get_ast_manager();
         arith_util& arith = get_plugin().m_arith;
-        basic_simplifier_plugin& bsimp = get_plugin().m_bsimp;
+        bool_rewriter& bsimp = get_plugin().m_bsimp;
         expr_ref_vector conjs(m);
         relation_signature const& sig = get_signature();
         for (unsigned i = 0; i < sig.size(); ++i) {
diff --git a/src/muz/rel/dl_bound_relation.h b/src/muz/rel/dl_bound_relation.h
index 456df737b..1678e23b8 100644
--- a/src/muz/rel/dl_bound_relation.h
+++ b/src/muz/rel/dl_bound_relation.h
@@ -27,6 +27,7 @@ Revision History:
 #include "muz/rel/dl_interval_relation.h"
 #include "ast/arith_decl_plugin.h"
 #include "ast/simplifier/basic_simplifier_plugin.h"
+#include "ast/rewriter/bool_rewriter.h"
 
 namespace datalog {
 
@@ -44,7 +45,7 @@ namespace datalog {
         class filter_interpreted_fn;
         class filter_intersection_fn;
         arith_util m_arith;
-        basic_simplifier_plugin m_bsimp;
+        bool_rewriter m_bsimp;
     public:
         bound_relation_plugin(relation_manager& m);
         virtual bool can_handle_signature(const relation_signature & s);
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index 2f55cd704..7eb189ebf 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -52,7 +52,7 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p):
     m_asserted_formulas(m),
     m_asserted_formula_prs(m),
     m_asserted_qhead(0),
-    m_macro_manager(m, m_simplifier),
+    m_macro_manager(m),
     m_bit2int(m),
     m_bv_sharing(m),
     m_inconsistent(false){
diff --git a/src/tactic/ufbv/macro_finder_tactic.cpp b/src/tactic/ufbv/macro_finder_tactic.cpp
index 9b1835ff3..b3f258ba7 100644
--- a/src/tactic/ufbv/macro_finder_tactic.cpp
+++ b/src/tactic/ufbv/macro_finder_tactic.cpp
@@ -64,7 +64,7 @@ class macro_finder_tactic : public tactic {
             bv_simplifier_plugin * bvsimp = alloc(bv_simplifier_plugin, m_manager, *bsimp, bv_params);
             simp.register_plugin(bvsimp);
                 
-            macro_manager mm(m_manager, simp);
+            macro_manager mm(m_manager);
             macro_finder mf(m_manager, mm);
             
             expr_ref_vector forms(m_manager), new_forms(m_manager);
diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp
index f7312fd2e..8a91bde61 100644
--- a/src/tactic/ufbv/quasi_macros_tactic.cpp
+++ b/src/tactic/ufbv/quasi_macros_tactic.cpp
@@ -62,7 +62,7 @@ class quasi_macros_tactic : public tactic {
             bv_simplifier_plugin * bvsimp = alloc(bv_simplifier_plugin, m_manager, *bsimp, bv_params);
             simp.register_plugin(bvsimp);
                 
-            macro_manager mm(m_manager, simp);
+            macro_manager mm(m_manager);
             quasi_macros qm(m_manager, mm);
             bool more = true;
         

From e5826b957f97e2c43ad765ef77b127726b7255eb Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 23 Aug 2017 09:01:25 -0700
Subject: [PATCH 06/74] fix build

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/poly_rewriter.h     | 1 +
 src/ast/rewriter/poly_rewriter_def.h | 7 +++++++
 src/smt/proto_model/proto_model.cpp  | 8 ++++----
 src/smt/smt_model_generator.cpp      | 6 +++---
 4 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/ast/rewriter/poly_rewriter.h b/src/ast/rewriter/poly_rewriter.h
index 5269f25a8..8cbc7f864 100644
--- a/src/ast/rewriter/poly_rewriter.h
+++ b/src/ast/rewriter/poly_rewriter.h
@@ -112,6 +112,7 @@ public:
     bool is_mul(func_decl * f) const { return is_decl_of(f, get_fid(), mul_decl_kind()); }
     bool is_times_minus_one(expr * n, expr*& r) const;
     bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t);
+    bool is_zero(expr* e) const;
 
 
     br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h
index 6b747756c..731a0538a 100644
--- a/src/ast/rewriter/poly_rewriter_def.h
+++ b/src/ast/rewriter/poly_rewriter_def.h
@@ -64,6 +64,13 @@ expr * poly_rewriter<Config>::get_power_body(expr * t, rational & k) {
     return t;
 }
 
+template<typename Config>
+bool poly_rewriter<Config>::is_zero(expr* e) const {
+    rational v;
+    return is_numeral(e, v) && v.is_zero();
+}
+
+
 template<typename Config>
 expr * poly_rewriter<Config>::mk_mul_app(unsigned num_args, expr * const * args) { 
     switch (num_args) {
diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp
index 9b218037e..725c9fc51 100644
--- a/src/smt/proto_model/proto_model.cpp
+++ b/src/smt/proto_model/proto_model.cpp
@@ -161,8 +161,10 @@ void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_au
                     continue;
                 }
                 func_decl * f = t->get_decl();
-                if (m_aux_decls.contains(f))
+                if (m_aux_decls.contains(f)) {
+                    TRACE("model_bug", tout << f->get_name() << "\n";);
                     found_aux_fs.insert(f);
+                }
                 expr_ref new_t(m_manager);
                 new_t = m_rewrite.mk_app(f, args.size(), args.c_ptr());
                 if (t != new_t.get())
@@ -180,9 +182,7 @@ void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_au
         }
     }
 
-    if (!cache.find(fi_else, a)) {
-        UNREACHABLE();
-    }
+    VERIFY(cache.find(fi_else, a));
 
     fi->set_else(a);
 }
diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp
index 49e9083f2..953afd4b7 100644
--- a/src/smt/smt_model_generator.cpp
+++ b/src/smt/smt_model_generator.cpp
@@ -17,14 +17,14 @@ Revision History:
 
 --*/
 
-#include "smt/smt_context.h"
-#include "smt/smt_model_generator.h"
-#include "smt/proto_model/proto_model.h"
 #include "util/ref_util.h"
 #include "ast/for_each_expr.h"
 #include "ast/ast_ll_pp.h"
 #include "ast/ast_pp.h"
 #include "ast/ast_smt2_pp.h"
+#include "smt/smt_context.h"
+#include "smt/smt_model_generator.h"
+#include "smt/proto_model/proto_model.h"
 
 namespace smt {
 

From 655b3d9c1953b5deef01bcfd9ef523b032e05bc8 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 23 Aug 2017 12:17:30 -0700
Subject: [PATCH 07/74] removing dependency on simplifier in pattern_inference

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/macros/macro_manager.cpp              |  98 ---
 src/ast/macros/macro_manager.h                |  11 -
 src/ast/pattern/pattern_inference.cpp         | 698 ++++++++++++++++--
 src/ast/pattern/pattern_inference.h           | 211 +++++-
 .../dl_mk_quantifier_instantiation.cpp        |   2 +-
 src/smt/asserted_formulas.cpp                 |   3 +-
 src/smt/proto_model/proto_model.cpp           |   4 +-
 src/smt/smt_model_generator.cpp               |   1 -
 8 files changed, 861 insertions(+), 167 deletions(-)

diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp
index 70434435b..bff1e7dae 100644
--- a/src/ast/macros/macro_manager.cpp
+++ b/src/ast/macros/macro_manager.cpp
@@ -298,104 +298,6 @@ struct macro_manager::macro_expander_rw : public rewriter_tpl<macro_manager::mac
     {}
 };
 
-macro_manager::macro_expander::macro_expander(ast_manager & m, macro_manager & mm):
-    simplifier(m),
-    m_macro_manager(mm) {
-    // REMARK: theory simplifier should not be used by macro_expander...
-    // is_arith_macro rewrites a quantifer such as:
-    //   forall (x Int) (= (+ x (+ (f x) 1)) 2)
-    // into
-    //   forall (x Int) (= (f x) (+ 1 (* -1 x)))
-    // The goal is to make simple macro detection detect the arith macro.
-    // The arith simplifier will undo this transformation.
-    // borrow_plugins(s);
-    enable_ac_support(false);
-}
-
-macro_manager::macro_expander::~macro_expander() {
-    // release_plugins();
-}
-
-void macro_manager::macro_expander::reduce1_quantifier(quantifier * q) {
-    simplifier::reduce1_quantifier(q);
-    // If a macro was expanded in a pattern, we must erase it since it may not be a valid pattern anymore.
-    // The MAM assumes valid patterns, and it crashes if invalid patterns are provided.
-    // For example, it will crash if the pattern does not contain all variables.
-    //
-    // Alternative solution: use pattern_validation to check if the pattern is still valid.
-    // I'm not sure if this is a good solution, since the pattern may be meaningless after the macro expansion.
-    // So, I'm just erasing them.
-    expr *  new_q_expr = 0;
-    proof * new_q_pr = 0;
-    get_cached(q, new_q_expr, new_q_pr);
-    if (!is_quantifier(new_q_expr))
-        return;
-    quantifier * new_q = to_quantifier(new_q_expr);
-    bool erase_patterns = false;
-    if (q->get_num_patterns() != new_q->get_num_patterns() ||
-        q->get_num_no_patterns() != new_q->get_num_no_patterns()) {
-        erase_patterns = true;
-    }
-    else {
-        for (unsigned i = 0; !erase_patterns && i < q->get_num_patterns(); i++) {
-            if (q->get_pattern(i) != new_q->get_pattern(i))
-                erase_patterns = true;
-        }
-        for (unsigned i = 0; !erase_patterns && i < q->get_num_no_patterns(); i++) {
-            if (q->get_no_pattern(i) != new_q->get_no_pattern(i))
-                erase_patterns = true;
-        }
-    }
-    if (erase_patterns) {
-        ast_manager & m = get_manager();
-        expr * new_new_q = m.update_quantifier(new_q, 0, 0, 0, 0, new_q->get_expr());
-        // we can use the same proof since new_new_q and new_q are identical modulo patterns/annotations
-        cache_result(q, new_new_q, new_q_pr);
-    }
-}
-
-bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref & p) {
-    if (!is_app(_n))
-        return false;
-    app * n = to_app(_n);
-    quantifier * q = 0;
-    func_decl * d  = n->get_decl();
-    TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";);
-    if (m_macro_manager.m_decl2macro.find(d, q)) {
-        TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";);
-        app * head = 0;
-        expr * def = 0;
-        m_macro_manager.get_head_def(q, d, head, def);
-        unsigned num = n->get_num_args();
-        SASSERT(head && def);
-        ptr_buffer<expr> subst_args;
-        subst_args.resize(num, 0);
-        for (unsigned i = 0; i < num; i++) {
-            var * v = to_var(head->get_arg(i));
-            SASSERT(v->get_idx() < num);
-            unsigned nidx = num - v->get_idx() - 1;
-            SASSERT(subst_args[nidx] == 0);
-            subst_args[nidx] = n->get_arg(i);
-        }
-        var_subst s(m);
-        s(def, num, subst_args.c_ptr(), r);
-        if (m.proofs_enabled()) {
-            expr_ref instance(m);
-            s(q->get_expr(), num, subst_args.c_ptr(), instance);
-            proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr());
-            proof * q_pr  = 0;
-            m_macro_manager.m_decl2macro_pr.find(d, q_pr);
-            SASSERT(q_pr != 0);
-            proof * prs[2] = { qi_pr, q_pr };
-            p = m.mk_unit_resolution(2, prs);
-        }
-        else {
-            p = 0;
-        }
-        return true;
-    }
-    return false;
-}
 
 void macro_manager::expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref & new_pr) {
     if (has_macros()) {
diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h
index 98e242cd8..71864a699 100644
--- a/src/ast/macros/macro_manager.h
+++ b/src/ast/macros/macro_manager.h
@@ -60,17 +60,6 @@ class macro_manager {
     struct macro_expander_cfg;
     struct macro_expander_rw;
 
-    class macro_expander : public simplifier {
-    protected:
-        macro_manager &   m_macro_manager;
-        virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
-        virtual void reduce1_quantifier(quantifier * q);
-    public:
-        macro_expander(ast_manager & m, macro_manager & mm);
-        ~macro_expander();
-    };
-    friend class macro_expander;
-
 public:
     macro_manager(ast_manager & m);
     ~macro_manager();
diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp
index d97c2f094..94a90d829 100644
--- a/src/ast/pattern/pattern_inference.cpp
+++ b/src/ast/pattern/pattern_inference.cpp
@@ -16,15 +16,17 @@ Author:
 Revision History:
 
 --*/
+
+#include "util/warning.h"
 #include "ast/pattern/pattern_inference.h"
 #include "ast/ast_ll_pp.h"
 #include "ast/ast_pp.h"
 #include "ast/ast_util.h"
-#include "util/warning.h"
 #include "ast/arith_decl_plugin.h"
 #include "ast/normal_forms/pull_quant.h"
 #include "ast/well_sorted.h"
 #include "ast/for_each_expr.h"
+#include "ast/rewriter/rewriter_def.h"
 
 void smaller_pattern::save(expr * p1, expr * p2) {
     expr_pair e(p1, p2);
@@ -87,7 +89,7 @@ bool smaller_pattern::operator()(unsigned num_bindings, expr * p1, expr * p2) {
     return process(p1, p2);
 }
 
-pattern_inference::pattern_inference(ast_manager & m, pattern_inference_params & params):
+pattern_inference_old::pattern_inference_old(ast_manager & m, pattern_inference_params & params):
     simplifier(m),
     m_params(params),
     m_bfid(m.get_basic_family_id()),
@@ -105,7 +107,7 @@ pattern_inference::pattern_inference(ast_manager & m, pattern_inference_params &
     enable_ac_support(false);
 }
 
-void pattern_inference::collect::operator()(expr * n, unsigned num_bindings) {
+void pattern_inference_old::collect::operator()(expr * n, unsigned num_bindings) {
     SASSERT(m_info.empty());
     SASSERT(m_todo.empty());
     SASSERT(m_cache.empty());
@@ -125,7 +127,7 @@ void pattern_inference::collect::operator()(expr * n, unsigned num_bindings) {
     reset();
 }
 
-inline void pattern_inference::collect::visit(expr * n, unsigned delta, bool & visited) {
+inline void pattern_inference_old::collect::visit(expr * n, unsigned delta, bool & visited) {
     entry e(n, delta);
     if (!m_cache.contains(e)) {
         m_todo.push_back(e);
@@ -133,7 +135,7 @@ inline void pattern_inference::collect::visit(expr * n, unsigned delta, bool & v
     }
 }
 
-bool pattern_inference::collect::visit_children(expr * n, unsigned delta) {
+bool pattern_inference_old::collect::visit_children(expr * n, unsigned delta) {
     bool visited = true;
     unsigned i;
     switch (n->get_kind()) {
@@ -153,13 +155,13 @@ bool pattern_inference::collect::visit_children(expr * n, unsigned delta) {
     return visited;
 }
 
-inline void pattern_inference::collect::save(expr * n, unsigned delta, info * i) {
+inline void pattern_inference_old::collect::save(expr * n, unsigned delta, info * i) {
     m_cache.insert(entry(n, delta), i);
     if (i != 0)
         m_info.push_back(i);
 }
 
-void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
+void pattern_inference_old::collect::save_candidate(expr * n, unsigned delta) {
     switch (n->get_kind()) {
     case AST_VAR: {
         unsigned idx = to_var(n)->get_idx();
@@ -247,14 +249,14 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
 }
 
 
-void pattern_inference::collect::reset() {
+void pattern_inference_old::collect::reset() {
     m_cache.reset();
     std::for_each(m_info.begin(), m_info.end(), delete_proc<info>());
     m_info.reset();
     SASSERT(m_todo.empty());
 }
 
-void pattern_inference::add_candidate(app * n, uint_set const & free_vars, unsigned size) {
+void pattern_inference_old::add_candidate(app * n, uint_set const & free_vars, unsigned size) {
     for (unsigned i = 0; i < m_num_no_patterns; i++) {
         if (n == m_no_patterns[i])
             return;
@@ -271,7 +273,7 @@ void pattern_inference::add_candidate(app * n, uint_set const & free_vars, unsig
    \brief Copy the non-looping patterns in m_candidates to result when m_params.m_pi_block_loop_patterns = true.
    Otherwise, copy m_candidates to result.
 */
-void pattern_inference::filter_looping_patterns(ptr_vector<app> & result) {
+void pattern_inference_old::filter_looping_patterns(ptr_vector<app> & result) {
     unsigned num = m_candidates.size();
     for (unsigned i1 = 0; i1 < num; i1++) {
         app * n1 = m_candidates.get(i1);
@@ -310,7 +312,7 @@ void pattern_inference::filter_looping_patterns(ptr_vector<app> & result) {
 
 
 
-inline void pattern_inference::contains_subpattern::save(expr * n) {
+inline void pattern_inference_old::contains_subpattern::save(expr * n) {
     unsigned id = n->get_id();
     m_already_processed.assure_domain(id);
     if (!m_already_processed.contains(id)) {
@@ -319,7 +321,7 @@ inline void pattern_inference::contains_subpattern::save(expr * n) {
     }
 }
 
-bool pattern_inference::contains_subpattern::operator()(expr * n) {
+bool pattern_inference_old::contains_subpattern::operator()(expr * n) {
     m_already_processed.reset();
     m_todo.reset();
     expr2info::obj_map_entry * _e = m_owner.m_candidates_info.find_core(n);
@@ -360,7 +362,7 @@ bool pattern_inference::contains_subpattern::operator()(expr * n) {
    Return true if n contains a direct/indirect child that is also a
    pattern, and contains the same number of free variables.
 */
-inline bool pattern_inference::contains_subpattern(expr * n) {
+inline bool pattern_inference_old::contains_subpattern(expr * n) {
     return m_contains_subpattern(n);
 }
 
@@ -372,18 +374,15 @@ inline bool pattern_inference::contains_subpattern(expr * n) {
    Remark: Every pattern p in patterns is also a member of
    m_pattern_map.
 */
-void pattern_inference::filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result) {
-    ptr_vector<app>::const_iterator it  = patterns.begin();
-    ptr_vector<app>::const_iterator end = patterns.end();
-    for (; it != end; ++it) {
-        app * curr = *it;
+void pattern_inference_old::filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result) {
+    for (app * curr : patterns) {
         if (!contains_subpattern(curr))
             result.push_back(curr);
     }
 }
 
 
-bool pattern_inference::pattern_weight_lt::operator()(expr * n1, expr * n2) const {
+bool pattern_inference_old::pattern_weight_lt::operator()(expr * n1, expr * n2) const {
     expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1);
     expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2);
     SASSERT(e1 != 0);
@@ -401,13 +400,10 @@ bool pattern_inference::pattern_weight_lt::operator()(expr * n1, expr * n2) cons
    variables, then it is copied to remaining_candidate_patterns.  The
    new patterns are stored in result.
 */
-void pattern_inference::candidates2unary_patterns(ptr_vector<app> const & candidate_patterns,
+void pattern_inference_old::candidates2unary_patterns(ptr_vector<app> const & candidate_patterns,
                                                   ptr_vector<app> & remaining_candidate_patterns,
                                                   app_ref_buffer  & result) {
-    ptr_vector<app>::const_iterator it  = candidate_patterns.begin();
-    ptr_vector<app>::const_iterator end = candidate_patterns.end();
-    for (; it != end; ++it) {
-        app * candidate = *it;
+    for (app * candidate : candidate_patterns) {
         expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
         info const & i = e->get_data().m_value;
         if (i.m_free_vars.num_elems() == m_num_bindings) {
@@ -425,7 +421,7 @@ void pattern_inference::candidates2unary_patterns(ptr_vector<app> const & candid
 // HACK: limit the number of case-splits:
 #define MAX_SPLITS 32
 
-void pattern_inference::candidates2multi_patterns(unsigned max_num_patterns,
+void pattern_inference_old::candidates2multi_patterns(unsigned max_num_patterns,
                                                   ptr_vector<app> const & candidate_patterns,
                                                   app_ref_buffer & result) {
     SASSERT(!candidate_patterns.empty());
@@ -469,21 +465,19 @@ void pattern_inference::candidates2multi_patterns(unsigned max_num_patterns,
     }
 }
 
-void pattern_inference::reset_pre_patterns() {
+void pattern_inference_old::reset_pre_patterns() {
     std::for_each(m_pre_patterns.begin(), m_pre_patterns.end(), delete_proc<pre_pattern>());
     m_pre_patterns.reset();
 }
 
 #ifdef _TRACE
 static void dump_app_vector(std::ostream & out, ptr_vector<app> const & v, ast_manager & m) {
-    ptr_vector<app>::const_iterator it  = v.begin();
-    ptr_vector<app>::const_iterator end = v.end();
-    for (; it != end; ++it)
-        out << mk_pp(*it, m) << "\n";
+    for (app * e : v) 
+        out << mk_pp(e, m) << "\n";
 }
 #endif
 
-bool pattern_inference::is_forbidden(app * n) const {
+bool pattern_inference_old::is_forbidden(app * n) const {
     func_decl const * decl = n->get_decl();
     if (is_ground(n))
         return false;
@@ -499,14 +493,11 @@ bool pattern_inference::is_forbidden(app * n) const {
     return false;
 }
 
-bool pattern_inference::has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result) {
+bool pattern_inference_old::has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result) {
     if (m_preferred.empty())
         return false;
     bool found = false;
-    ptr_vector<app>::const_iterator it  = candidate_patterns.begin();
-    ptr_vector<app>::const_iterator end = candidate_patterns.end();
-    for (; it != end; ++it) {
-        app * candidate = *it;
+    for (app * candidate : candidate_patterns) {
         if (m_preferred.contains(to_app(candidate)->get_decl())) {
             expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
             info const & i = e->get_data().m_value;
@@ -521,7 +512,7 @@ bool pattern_inference::has_preferred_patterns(ptr_vector<app> & candidate_patte
     return found;
 }
 
-void pattern_inference::mk_patterns(unsigned num_bindings,
+void pattern_inference_old::mk_patterns(unsigned num_bindings,
                                     expr *   n,
                                     unsigned num_no_patterns,
                                     expr * const * no_patterns,
@@ -578,7 +569,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
 
 #include "ast/pattern/database.h"
 
-void pattern_inference::reduce1_quantifier(quantifier * q) {
+void pattern_inference_old::reduce1_quantifier(quantifier * q) {
     TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";);
     if (!q->is_forall()) {
         simplifier::reduce1_quantifier(q);
@@ -739,13 +730,626 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
 }
 
 
-#if 0
-// unused
-static void dump_expr_vector(std::ostream & out, ptr_vector<expr> const & v, ast_manager & m) {
-    ptr_vector<expr>::const_iterator it  = v.begin();
-    ptr_vector<expr>::const_iterator end = v.end();
-    for (; it != end; ++it)
-        out << mk_pp(*it, m) << "\n";
-}
-#endif
 
+pattern_inference_cfg::pattern_inference_cfg(ast_manager & m, pattern_inference_params & params):
+    m(m),
+    m_params(params),
+    m_bfid(m.get_basic_family_id()),
+    m_afid(m.mk_family_id("arith")),
+    m_le(m),
+    m_nested_arith_only(true),
+    m_block_loop_patterns(params.m_pi_block_loop_patterns),
+    m_candidates(m),
+    m_pattern_weight_lt(m_candidates_info),
+    m_collect(m, *this),
+    m_contains_subpattern(*this),
+    m_database(m) {
+    if (params.m_pi_arith == AP_NO)
+        register_forbidden_family(m_afid);
+}
+
+void pattern_inference_cfg::collect::operator()(expr * n, unsigned num_bindings) {
+    SASSERT(m_info.empty());
+    SASSERT(m_todo.empty());
+    SASSERT(m_cache.empty());
+    m_num_bindings = num_bindings;
+    m_todo.push_back(entry(n, 0));
+    while (!m_todo.empty()) {
+        entry & e      = m_todo.back();
+        n              = e.m_node;
+        unsigned delta = e.m_delta;
+        TRACE("collect", tout << "processing: " << n->get_id() << " " << delta << " kind: " << n->get_kind() << "\n";);
+        TRACE("collect_info", tout << mk_pp(n, m) << "\n";);
+        if (visit_children(n, delta)) {
+            m_todo.pop_back();
+            save_candidate(n, delta);
+        }
+    }
+    reset();
+}
+
+inline void pattern_inference_cfg::collect::visit(expr * n, unsigned delta, bool & visited) {
+    entry e(n, delta);
+    if (!m_cache.contains(e)) {
+        m_todo.push_back(e);
+        visited = false;
+    }
+}
+
+bool pattern_inference_cfg::collect::visit_children(expr * n, unsigned delta) {
+    bool visited = true;
+    unsigned i;
+    switch (n->get_kind()) {
+    case AST_APP:
+        i = to_app(n)->get_num_args();
+        while (i > 0) {
+            --i;
+            visit(to_app(n)->get_arg(i), delta, visited);
+        }
+        break;
+    case AST_QUANTIFIER:
+        visit(to_quantifier(n)->get_expr(), delta + to_quantifier(n)->get_num_decls(), visited);
+        break;
+    default:
+        break;
+    }
+    return visited;
+}
+
+inline void pattern_inference_cfg::collect::save(expr * n, unsigned delta, info * i) {
+    m_cache.insert(entry(n, delta), i);
+    if (i != 0)
+        m_info.push_back(i);
+}
+
+void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) {
+    switch (n->get_kind()) {
+    case AST_VAR: {
+        unsigned idx = to_var(n)->get_idx();
+        if (idx >= delta) {
+            idx = idx - delta;
+            uint_set free_vars;
+            if (idx < m_num_bindings)
+                free_vars.insert(idx);
+            info * i = 0;
+            if (delta == 0)
+                i = alloc(info, m, n, free_vars, 1);
+            else
+                i = alloc(info, m, m.mk_var(idx, to_var(n)->get_sort()), free_vars, 1);
+            save(n, delta, i);
+        }
+        else {
+            save(n, delta, 0);
+        }
+        return;
+    }
+    case AST_APP: {
+        app *       c    = to_app(n);
+        func_decl * decl = c->get_decl();
+        if (m_owner.is_forbidden(c)) {
+            save(n, delta, 0);
+            return;
+        }
+
+        if (c->get_num_args() == 0) {
+            save(n, delta, alloc(info, m, n, uint_set(), 1));
+            return;
+        }
+
+        ptr_buffer<expr> buffer;
+        bool changed   = false; // false if none of the children is mapped to a node different from itself.
+        uint_set free_vars;
+        unsigned size  = 1;
+        unsigned num   = c->get_num_args();
+        for (unsigned i = 0; i < num; i++) {
+            expr * child      = c->get_arg(i);
+            info * child_info = 0;
+#ifdef Z3DEBUG
+            bool found =
+#endif
+            m_cache.find(entry(child, delta), child_info);
+            SASSERT(found);
+            if (child_info == 0) {
+                save(n, delta, 0);
+                return;
+            }
+            buffer.push_back(child_info->m_node.get());
+            free_vars |= child_info->m_free_vars;
+            size      += child_info->m_size;
+            if (child != child_info->m_node.get())
+                changed = true;
+        }
+
+        app * new_node = 0;
+        if (changed)
+            new_node = m.mk_app(decl, buffer.size(), buffer.c_ptr());
+        else
+            new_node = to_app(n);
+        save(n, delta, alloc(info, m, new_node, free_vars, size));
+        // Remark: arithmetic patterns are only used if they are nested inside other terms.
+        // That is, we never consider x + 1 as pattern. On the other hand, f(x+1) can be a pattern
+        // if arithmetic is not in the forbidden list.
+        //
+        // Remark: The rule above has an exception. The operators (div, idiv, mod) are allowed to be
+        // used as patterns even when they are not nested in other terms. The motivation is that
+        // Z3 currently doesn't implement them (i.e., they are uninterpreted). So, some users add axioms
+        // stating properties about these operators.
+        family_id fid = c->get_family_id();
+        decl_kind k   = c->get_decl_kind();
+        if (!free_vars.empty() &&
+            (fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) {
+            TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m) << "\n";);
+            m_owner.add_candidate(new_node, free_vars, size);
+        }
+        return;
+    }
+    default:
+        save(n, delta, 0);
+        return;
+    }
+}
+
+
+void pattern_inference_cfg::collect::reset() {
+    m_cache.reset();
+    std::for_each(m_info.begin(), m_info.end(), delete_proc<info>());
+    m_info.reset();
+    SASSERT(m_todo.empty());
+}
+
+void pattern_inference_cfg::add_candidate(app * n, uint_set const & free_vars, unsigned size) {
+    for (unsigned i = 0; i < m_num_no_patterns; i++) {
+        if (n == m_no_patterns[i])
+            return;
+    }
+
+    if (!m_candidates_info.contains(n)) {
+        m_candidates_info.insert(n, info(free_vars, size));
+        m_candidates.push_back(n);
+    }
+}
+
+
+/**
+   \brief Copy the non-looping patterns in m_candidates to result when m_params.m_pi_block_loop_patterns = true.
+   Otherwise, copy m_candidates to result.
+*/
+void pattern_inference_cfg::filter_looping_patterns(ptr_vector<app> & result) {
+    unsigned num = m_candidates.size();
+    for (unsigned i1 = 0; i1 < num; i1++) {
+        app * n1 = m_candidates.get(i1);
+        expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1);
+        SASSERT(e1);
+        uint_set const & s1 = e1->get_data().m_value.m_free_vars;
+        if (m_block_loop_patterns) {
+            bool smaller = false;
+            for (unsigned i2 = 0; i2 < num; i2++) {
+                if (i1 != i2) {
+                    app * n2 = m_candidates.get(i2);
+                    expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2);
+                    if (e2) {
+                        uint_set const & s2 = e2->get_data().m_value.m_free_vars;
+                        // Remark: the comparison operator only makes sense if both AST nodes
+                        // contain the same number of variables.
+                        // Example:
+                        // (f X Y) <: (f (g X Z W) Y)
+                        if (s1 == s2 && m_le(m_num_bindings, n1, n2) && !m_le(m_num_bindings, n2, n1)) {
+                            smaller = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            if (!smaller)
+                result.push_back(n1);
+            else
+                m_candidates_info.erase(n1);
+        }
+        else {
+            result.push_back(n1);
+        }
+    }
+}
+
+
+
+inline void pattern_inference_cfg::contains_subpattern::save(expr * n) {
+    unsigned id = n->get_id();
+    m_already_processed.assure_domain(id);
+    if (!m_already_processed.contains(id)) {
+        m_todo.push_back(n);
+        m_already_processed.insert(id);
+    }
+}
+
+bool pattern_inference_cfg::contains_subpattern::operator()(expr * n) {
+    m_already_processed.reset();
+    m_todo.reset();
+    expr2info::obj_map_entry * _e = m_owner.m_candidates_info.find_core(n);
+    SASSERT(_e);
+    uint_set const & s1 = _e->get_data().m_value.m_free_vars;
+    save(n);
+    unsigned num;
+    while (!m_todo.empty()) {
+        expr * curr = m_todo.back();
+        m_todo.pop_back();
+        switch (curr->get_kind()) {
+        case AST_APP:
+            if (curr != n) {
+                expr2info::obj_map_entry * e = m_owner.m_candidates_info.find_core(curr);
+                if (e) {
+                    uint_set const & s2 = e->get_data().m_value.m_free_vars;
+                    SASSERT(s2.subset_of(s1));
+                    if (s1 == s2) {
+                        TRACE("pattern_inference", tout << mk_pp(n, m_owner.m) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m) << "\n";);
+                        return true;
+                    }
+                }
+            }
+            num = to_app(curr)->get_num_args();
+            for (unsigned i = 0; i < num; i++)
+                save(to_app(curr)->get_arg(i));
+            break;
+        case AST_VAR:
+            break;
+        default:
+            UNREACHABLE();
+        }
+    }
+    return false;
+}
+
+/**
+   Return true if n contains a direct/indirect child that is also a
+   pattern, and contains the same number of free variables.
+*/
+inline bool pattern_inference_cfg::contains_subpattern(expr * n) {
+    return m_contains_subpattern(n);
+}
+
+/**
+   \brief Copy a pattern p in patterns to result, if there is no
+   direct/indirect child of p in patterns which contains the same set
+   of variables.
+
+   Remark: Every pattern p in patterns is also a member of
+   m_pattern_map.
+*/
+void pattern_inference_cfg::filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result) {
+    for (app * curr : patterns) {
+        if (!contains_subpattern(curr))
+            result.push_back(curr);
+    }
+}
+
+
+bool pattern_inference_cfg::pattern_weight_lt::operator()(expr * n1, expr * n2) const {
+    expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1);
+    expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2);
+    SASSERT(e1 != 0);
+    SASSERT(e2 != 0);
+    info const & i1 = e1->get_data().m_value;
+    info const & i2 = e2->get_data().m_value;
+    unsigned num_free_vars1 = i1.m_free_vars.num_elems();
+    unsigned num_free_vars2 = i2.m_free_vars.num_elems();
+    return num_free_vars1 > num_free_vars2 || (num_free_vars1 == num_free_vars2 && i1.m_size < i2.m_size);
+}
+
+/**
+   \brief Create unary patterns (single expressions that contain all
+   bound variables).  If a candidate does not contain all bound
+   variables, then it is copied to remaining_candidate_patterns.  The
+   new patterns are stored in result.
+*/
+void pattern_inference_cfg::candidates2unary_patterns(ptr_vector<app> const & candidate_patterns,
+                                                  ptr_vector<app> & remaining_candidate_patterns,
+                                                  app_ref_buffer  & result) {
+    for (app * candidate : candidate_patterns) {
+        expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
+        info const & i = e->get_data().m_value;
+        if (i.m_free_vars.num_elems() == m_num_bindings) {
+            app * new_pattern = m.mk_pattern(candidate);
+            result.push_back(new_pattern);
+        }
+        else {
+            remaining_candidate_patterns.push_back(candidate);
+        }
+    }
+}
+
+// TODO: this code is too inefficient when the number of candidate
+// patterns is too big.
+// HACK: limit the number of case-splits:
+#define MAX_SPLITS 32
+
+void pattern_inference_cfg::candidates2multi_patterns(unsigned max_num_patterns,
+                                                  ptr_vector<app> const & candidate_patterns,
+                                                  app_ref_buffer & result) {
+    SASSERT(!candidate_patterns.empty());
+    m_pre_patterns.push_back(alloc(pre_pattern));
+    unsigned sz = candidate_patterns.size();
+    unsigned num_splits = 0;
+    for (unsigned j = 0; j < m_pre_patterns.size(); j++) {
+        pre_pattern * curr = m_pre_patterns[j];
+        if (curr->m_free_vars.num_elems() == m_num_bindings) {
+            app * new_pattern = m.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr());
+            result.push_back(new_pattern);
+            if (result.size() >= max_num_patterns)
+                return;
+        }
+        else if (curr->m_idx < sz) {
+            app * n                     = candidate_patterns[curr->m_idx];
+            expr2info::obj_map_entry * e = m_candidates_info.find_core(n);
+            uint_set const & s           = e->get_data().m_value.m_free_vars;
+            if (!s.subset_of(curr->m_free_vars)) {
+                pre_pattern * new_p = alloc(pre_pattern,*curr);
+                new_p->m_exprs.push_back(n);
+                new_p->m_free_vars |= s;
+                new_p->m_idx++;
+                m_pre_patterns.push_back(new_p);
+
+                if (num_splits < MAX_SPLITS) {
+                    m_pre_patterns[j] = 0;
+                    curr->m_idx++;
+                    m_pre_patterns.push_back(curr);
+                    num_splits++;
+                }
+            }
+            else {
+                m_pre_patterns[j] = 0;
+                curr->m_idx++;
+                m_pre_patterns.push_back(curr);
+            }
+        }
+        TRACE("pattern_inference", tout << "m_pre_patterns.size(): " << m_pre_patterns.size() <<
+              "\nnum_splits: " << num_splits << "\n";);
+    }
+}
+
+void pattern_inference_cfg::reset_pre_patterns() {
+    std::for_each(m_pre_patterns.begin(), m_pre_patterns.end(), delete_proc<pre_pattern>());
+    m_pre_patterns.reset();
+}
+
+
+bool pattern_inference_cfg::is_forbidden(app * n) const {
+    func_decl const * decl = n->get_decl();
+    if (is_ground(n))
+        return false;
+    // Remark: skolem constants should not be used in patterns, since they do not
+    // occur outside of the quantifier. That is, Z3 will never match this kind of
+    // pattern.
+    if (m_params.m_pi_avoid_skolems && decl->is_skolem()) {
+        CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m) << "\n";);
+        return true;
+    }
+    if (is_forbidden(decl))
+        return true;
+    return false;
+}
+
+bool pattern_inference_cfg::has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result) {
+    if (m_preferred.empty())
+        return false;
+    bool found = false;
+    for (app * candidate : candidate_patterns) {
+        if (m_preferred.contains(to_app(candidate)->get_decl())) {
+            expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
+            info const & i = e->get_data().m_value;
+            if (i.m_free_vars.num_elems() == m_num_bindings) {
+                TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m) << "\n";);
+                app * p = m.mk_pattern(candidate);
+                result.push_back(p);
+                found = true;
+            }
+        }
+    }
+    return found;
+}
+
+void pattern_inference_cfg::mk_patterns(unsigned num_bindings,
+                                    expr *   n,
+                                    unsigned num_no_patterns,
+                                    expr * const * no_patterns,
+                                    app_ref_buffer & result) {
+    m_num_bindings    = num_bindings;
+    m_num_no_patterns = num_no_patterns;
+    m_no_patterns     = no_patterns;
+
+    m_collect(n, num_bindings);
+
+    TRACE("pattern_inference",
+          tout << mk_pp(n, m);
+          tout << "\ncandidates:\n";
+          unsigned num = m_candidates.size();
+          for (unsigned i = 0; i < num; i++) {
+              tout << mk_pp(m_candidates.get(i), m) << "\n";
+          });
+
+    if (!m_candidates.empty()) {
+        m_tmp1.reset();
+        filter_looping_patterns(m_tmp1);
+        TRACE("pattern_inference",
+              tout << "candidates after removing looping-patterns:\n";
+              dump_app_vector(tout, m_tmp1, m););
+        SASSERT(!m_tmp1.empty());
+        if (!has_preferred_patterns(m_tmp1, result)) {
+            // continue if there are no preferred patterns
+            m_tmp2.reset();
+            filter_bigger_patterns(m_tmp1, m_tmp2);
+            SASSERT(!m_tmp2.empty());
+            TRACE("pattern_inference",
+                  tout << "candidates after removing bigger patterns:\n";
+                  dump_app_vector(tout, m_tmp2, m););
+            m_tmp1.reset();
+            candidates2unary_patterns(m_tmp2, m_tmp1, result);
+            unsigned num_extra_multi_patterns = m_params.m_pi_max_multi_patterns;
+            if (result.empty())
+                num_extra_multi_patterns++;
+            if (num_extra_multi_patterns > 0 && !m_tmp1.empty()) {
+                // m_pattern_weight_lt is not a total order
+                std::stable_sort(m_tmp1.begin(), m_tmp1.end(), m_pattern_weight_lt);
+                TRACE("pattern_inference",
+                      tout << "candidates after sorting:\n";
+                      dump_app_vector(tout, m_tmp1, m););
+                candidates2multi_patterns(num_extra_multi_patterns, m_tmp1, result);
+            }
+        }
+    }
+
+    reset_pre_patterns();
+    m_candidates_info.reset();
+    m_candidates.reset();
+}
+
+
+bool pattern_inference_cfg::reduce_quantifier(
+    quantifier * q, 
+    expr * new_body, 
+    expr * const *, // new_patterns 
+    expr * const * new_no_patterns,
+    expr_ref & result,
+    proof_ref & result_pr) {
+
+    TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";);
+    if (!q->is_forall()) {
+        return false;
+    }
+
+    int weight = q->get_weight();
+
+    if (m_params.m_pi_use_database) {
+        app_ref_vector new_patterns(m);
+        m_database.initialize(g_pattern_database);
+        unsigned new_weight;
+        if (m_database.match_quantifier(q, new_patterns, new_weight)) {
+            DEBUG_CODE(for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m, new_patterns.get(i))); });
+            quantifier_ref new_q(m);
+            if (q->get_num_patterns() > 0) {
+                // just update the weight...
+                TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m) << "\n";);
+                result = m.update_quantifier_weight(q, new_weight);
+            }
+            else {
+                quantifier_ref tmp(m);
+                tmp    = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr());
+                result = m.update_quantifier_weight(tmp, new_weight);
+                TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m) << "\n";);
+            }
+            if (m.fine_grain_proofs())
+                result_pr = m.mk_rewrite(q, new_q);
+            return true;
+        }
+    }
+
+    if (q->get_num_patterns() > 0) {
+        return false;
+    }
+
+    if (m_params.m_pi_nopat_weight >= 0)
+        weight = m_params.m_pi_nopat_weight;
+
+    SASSERT(q->get_num_patterns() == 0);
+
+    if (m_params.m_pi_arith == AP_CONSERVATIVE)
+        m_forbidden.push_back(m_afid);
+
+    app_ref_buffer new_patterns(m);
+    unsigned num_no_patterns = q->get_num_no_patterns();
+    mk_patterns(q->get_num_decls(), new_body, num_no_patterns, new_no_patterns, new_patterns);
+
+    if (new_patterns.empty() && num_no_patterns > 0) {
+        if (new_patterns.empty()) {
+            mk_patterns(q->get_num_decls(), new_body, 0, 0, new_patterns);
+            if (m_params.m_pi_warnings && !new_patterns.empty()) {
+                warning_msg("ignoring nopats annotation because Z3 couldn't find any other pattern (quantifier id: %s)", q->get_qid().str().c_str());
+            }
+        }
+    }
+
+    if (m_params.m_pi_arith == AP_CONSERVATIVE) {
+        m_forbidden.pop_back();
+        if (new_patterns.empty()) {
+            flet<bool> l1(m_block_loop_patterns, false); // allow looping patterns
+            mk_patterns(q->get_num_decls(), new_body, num_no_patterns, new_no_patterns, new_patterns);
+            if (!new_patterns.empty()) {
+                weight = std::max(weight, static_cast<int>(m_params.m_pi_arith_weight));
+                if (m_params.m_pi_warnings) {
+                    warning_msg("using arith. in pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_ARITH_WEIGHT=<val>).",
+                                q->get_qid().str().c_str(), weight);
+                }
+            }
+        }
+    }
+
+    if (m_params.m_pi_arith != AP_NO && new_patterns.empty()) {
+        if (new_patterns.empty()) {
+            flet<bool> l1(m_nested_arith_only, false); // try to find a non-nested arith pattern
+            flet<bool> l2(m_block_loop_patterns, false); // allow looping patterns
+            mk_patterns(q->get_num_decls(), new_body, num_no_patterns, new_no_patterns, new_patterns);
+            if (!new_patterns.empty()) {
+                weight = std::max(weight, static_cast<int>(m_params.m_pi_non_nested_arith_weight));
+                if (m_params.m_pi_warnings) {
+                    warning_msg("using non nested arith. pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_NON_NESTED_ARITH_WEIGHT=<val>).",
+                                q->get_qid().str().c_str(), weight);
+                }
+                // verbose_stream() << mk_pp(q, m) << "\n";
+            }
+        }
+    }
+
+    quantifier_ref new_q(m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body), m);
+    if (weight != q->get_weight())
+        new_q = m.update_quantifier_weight(new_q, weight);
+    if (m.fine_grain_proofs()) {
+        proof* new_body_pr = m.mk_reflexivity(new_body);
+        result_pr = m.mk_quant_intro(q, new_q, new_body_pr);
+    }
+
+    if (new_patterns.empty() && m_params.m_pi_pull_quantifiers) {
+        pull_quant pull(m);
+        expr_ref   new_expr(m);
+        proof_ref  new_pr(m);
+        pull(new_q, new_expr, new_pr);
+        quantifier * result2 = to_quantifier(new_expr);
+        if (result2 != new_q) {
+            mk_patterns(result2->get_num_decls(), result2->get_expr(), 0, 0, new_patterns);
+            if (!new_patterns.empty()) {
+                if (m_params.m_pi_warnings) {
+                    warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str());
+                }
+                new_q = m.update_quantifier(result2, new_patterns.size(), (expr**) new_patterns.c_ptr(), result2->get_expr());
+                if (m.fine_grain_proofs()) {
+                    result_pr = m.mk_transitivity(new_pr, m.mk_quant_intro(result2, new_q, m.mk_reflexivity(new_q->get_expr())));
+                }
+                TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";);
+            }
+        }
+    }
+
+    if (new_patterns.empty()) {
+        if (m_params.m_pi_warnings) {
+            warning_msg("failed to find a pattern for quantifier (quantifier id: %s)", q->get_qid().str().c_str());
+        }
+        TRACE("pi_failed", tout << mk_pp(q, m) << "\n";);
+    }
+
+    if (new_patterns.empty() && new_body == q->get_expr()) {
+        return false;
+    }
+
+    result = new_q;
+
+    IF_IVERBOSE(10,
+        verbose_stream() << "(smt.inferred-patterns :qid " << q->get_qid() << "\n";
+        for (unsigned i = 0; i < new_patterns.size(); i++)
+            verbose_stream() << "  " << mk_ismt2_pp(new_patterns[i], m, 2) << "\n";
+        verbose_stream() << ")\n"; );
+
+    return true;
+}
+
+pattern_inference_rw::pattern_inference_rw(ast_manager& m, pattern_inference_params & params):
+    rewriter_tpl<pattern_inference_cfg>(m, m.proofs_enabled(), m_cfg),
+    m_cfg(m, params)
+{}    
diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h
index a138d1033..7b0ec9cfe 100644
--- a/src/ast/pattern/pattern_inference.h
+++ b/src/ast/pattern/pattern_inference.h
@@ -21,6 +21,7 @@ Revision History:
 
 #include "ast/ast.h"
 #include "ast/simplifier/simplifier.h"
+#include "ast/rewriter/rewriter.h"
 #include "ast/pattern/pattern_inference_params.h"
 #include "util/vector.h"
 #include "util/uint_set.h"
@@ -60,7 +61,7 @@ public:
     bool operator()(unsigned num_bindings, expr * p1, expr * p2);
 };
 
-class pattern_inference : public simplifier {
+class pattern_inference_old : public simplifier {
     pattern_inference_params & m_params;
     family_id                  m_bfid;
     family_id                  m_afid;
@@ -88,7 +89,7 @@ class pattern_inference : public simplifier {
 
     typedef obj_map<expr, info> expr2info;
 
-    expr2info                  m_candidates_info; // candidate -> set of free vars + size
+    expr2info                 m_candidates_info; // candidate -> set of free vars + size
     app_ref_vector            m_candidates;
 
     ptr_vector<app>           m_tmp1;
@@ -136,7 +137,7 @@ class pattern_inference : public simplifier {
         };
         
         ast_manager &            m;
-        pattern_inference &      m_owner;
+        pattern_inference_old &      m_owner;
         family_id                m_afid;
         unsigned                 m_num_bindings;
         typedef map<entry, info *, obj_hash<entry>, default_eq<entry> > cache;
@@ -150,7 +151,7 @@ class pattern_inference : public simplifier {
         void save_candidate(expr * n, unsigned delta);
         void reset();
     public:
-        collect(ast_manager & m, pattern_inference & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {}
+        collect(ast_manager & m, pattern_inference_old & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {}
         void operator()(expr * n, unsigned num_bindings);
     };
 
@@ -165,12 +166,12 @@ class pattern_inference : public simplifier {
     void filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result);
 
     class contains_subpattern {
-        pattern_inference &  m_owner;
+        pattern_inference_old &  m_owner;
         nat_set              m_already_processed; 
         ptr_vector<expr>     m_todo;
         void save(expr * n);
     public:
-        contains_subpattern(pattern_inference & owner):
+        contains_subpattern(pattern_inference_old & owner):
             m_owner(owner) {}
         bool operator()(expr * n);
     };
@@ -217,7 +218,7 @@ class pattern_inference : public simplifier {
     virtual void reduce1_quantifier(quantifier * q);
 
 public:
-    pattern_inference(ast_manager & m, pattern_inference_params & params);
+    pattern_inference_old(ast_manager & m, pattern_inference_params & params);
     
     void register_forbidden_family(family_id fid) {
         SASSERT(fid != m_bfid);
@@ -244,5 +245,201 @@ public:
     bool is_forbidden(app * n) const;
 };
 
+class pattern_inference_cfg :  public default_rewriter_cfg {
+    ast_manager&               m;
+    pattern_inference_params & m_params;
+    family_id                  m_bfid;
+    family_id                  m_afid;
+    svector<family_id>         m_forbidden;
+    obj_hashtable<func_decl>   m_preferred;        
+    smaller_pattern            m_le;
+    unsigned                   m_num_bindings;
+    unsigned                   m_num_no_patterns;
+    expr * const *             m_no_patterns;
+    bool                       m_nested_arith_only;
+    bool                       m_block_loop_patterns;
+
+    struct info {
+        uint_set m_free_vars;
+        unsigned m_size;
+        info(uint_set const & vars, unsigned size):
+            m_free_vars(vars),
+            m_size(size) {
+        }
+        info():
+            m_free_vars(),
+            m_size(0) {
+        }
+    };
+
+    typedef obj_map<expr, info> expr2info;
+
+    expr2info                 m_candidates_info; // candidate -> set of free vars + size
+    app_ref_vector            m_candidates;
+
+    ptr_vector<app>           m_tmp1;
+    ptr_vector<app>           m_tmp2;
+    ptr_vector<app>           m_todo;
+
+    // Compare candidates patterns based on their usefulness
+    // p1 < p2 if
+    //  - p1 has more free variables than p2
+    //  - p1 and p2 has the same number of free variables,
+    //    and p1 is smaller than p2.
+    struct pattern_weight_lt {
+        expr2info & m_candidates_info;
+        pattern_weight_lt(expr2info & i):
+            m_candidates_info(i) {
+        }
+        bool operator()(expr * n1, expr * n2) const;
+    };
+
+    pattern_weight_lt          m_pattern_weight_lt;
+
+    //
+    // Functor for collecting candidates.
+    //
+    class collect {
+        struct entry {
+            expr *    m_node;
+            unsigned  m_delta;
+            entry():m_node(0), m_delta(0) {}
+            entry(expr * n, unsigned d):m_node(n), m_delta(d) {}
+            unsigned hash() const { 
+                return hash_u_u(m_node->get_id(), m_delta);
+            }
+            bool operator==(entry const & e) const { 
+                return m_node == e.m_node && m_delta == e.m_delta; 
+            }
+        };
+        
+        struct info {
+            expr_ref    m_node;
+            uint_set    m_free_vars;
+            unsigned    m_size;
+            info(ast_manager & m, expr * n, uint_set const & vars, unsigned sz):
+                m_node(n, m), m_free_vars(vars), m_size(sz) {}
+        };
+        
+        ast_manager &            m;
+        pattern_inference_cfg &     m_owner;
+        family_id                m_afid;
+        unsigned                 m_num_bindings;
+        typedef map<entry, info *, obj_hash<entry>, default_eq<entry> > cache;
+        cache                    m_cache;
+        ptr_vector<info>         m_info;
+        svector<entry>           m_todo;
+
+        void visit(expr * n, unsigned delta, bool & visited);
+        bool visit_children(expr * n, unsigned delta);
+        void save(expr * n, unsigned delta, info * i);
+        void save_candidate(expr * n, unsigned delta);
+        void reset();
+    public:
+        collect(ast_manager & m, pattern_inference_cfg & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {}
+        void operator()(expr * n, unsigned num_bindings);
+    };
+
+    collect                    m_collect;
+    
+    void add_candidate(app * n, uint_set const & s, unsigned size);
+
+    void filter_looping_patterns(ptr_vector<app> & result);
+
+    bool has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result);
+
+    void filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result);
+
+    class contains_subpattern {
+        pattern_inference_cfg & m_owner;
+        nat_set              m_already_processed; 
+        ptr_vector<expr>     m_todo;
+        void save(expr * n);
+    public:
+        contains_subpattern(pattern_inference_cfg & owner):
+            m_owner(owner) {}
+        bool operator()(expr * n);
+    };
+
+    contains_subpattern        m_contains_subpattern;
+        
+    bool contains_subpattern(expr * n);
+    
+    struct pre_pattern {
+        ptr_vector<app>  m_exprs;     // elements of the pattern.
+        uint_set         m_free_vars; // set of free variables in m_exprs
+        unsigned         m_idx;       // idx of the next candidate to process.
+        pre_pattern():
+            m_idx(0) {
+        }
+    };
+
+    ptr_vector<pre_pattern>      m_pre_patterns;
+    expr_pattern_match           m_database;
+
+    void candidates2unary_patterns(ptr_vector<app> const & candidate_patterns,
+                                   ptr_vector<app> & remaining_candidate_patterns,
+                                   app_ref_buffer & result);
+    
+    void candidates2multi_patterns(unsigned max_num_patterns, 
+                                   ptr_vector<app> const & candidate_patterns, 
+                                   app_ref_buffer & result);
+    
+    void reset_pre_patterns();
+
+    /**
+       \brief All minimal unary patterns (i.e., expressions that
+       contain all bound variables) are copied to result.  If there
+       are unary patterns, then at most num_extra_multi_patterns multi
+       patterns are created.  If there are no unary pattern, then at
+       most 1 + num_extra_multi_patterns multi_patterns are created.
+    */
+    void mk_patterns(unsigned num_bindings,              // IN number of bindings.
+                     expr * n,                           // IN node where the patterns are going to be extracted.
+                     unsigned num_no_patterns,           // IN num. patterns that should not be used.
+                     expr * const * no_patterns,         // IN patterns that should not be used.
+                     app_ref_buffer & result);           // OUT result
+    
+public:
+    pattern_inference_cfg(ast_manager & m, pattern_inference_params & params);
+    
+    void register_forbidden_family(family_id fid) {
+        SASSERT(fid != m_bfid);
+        m_forbidden.push_back(fid);
+    }
+
+    /**
+       \brief Register f as a preferred function symbol. The inference algorithm
+       gives preference to patterns rooted by this kind of function symbol.
+    */
+    void register_preferred(func_decl * f) {
+        m_preferred.insert(f);
+    }
+
+    bool reduce_quantifier(quantifier * old_q, 
+                           expr * new_body, 
+                           expr * const * new_patterns, 
+                           expr * const * new_no_patterns,
+                           expr_ref & result,
+                           proof_ref & result_pr);
+
+    void register_preferred(unsigned num, func_decl * const * fs) { for (unsigned i = 0; i < num; i++) register_preferred(fs[i]); }
+    
+    bool is_forbidden(func_decl const * decl) const {
+        family_id fid = decl->get_family_id();
+        if (fid == m_bfid && decl->get_decl_kind() != OP_TRUE && decl->get_decl_kind() != OP_FALSE) 
+            return true;
+        return std::find(m_forbidden.begin(), m_forbidden.end(), fid) != m_forbidden.end();
+    }
+
+    bool is_forbidden(app * n) const;
+};
+
+class pattern_inference_rw : public rewriter_tpl<pattern_inference_cfg> {
+    pattern_inference_cfg m_cfg;
+public:
+    pattern_inference_rw(ast_manager& m, pattern_inference_params & params);
+};
+
 #endif /* PATTERN_INFERENCE_H_ */
 
diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
index 7eb2284c0..cfb998a32 100644
--- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
+++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
@@ -70,7 +70,7 @@ namespace datalog {
         if (q->get_num_patterns() == 0) {
             proof_ref new_pr(m);
             pattern_inference_params params;
-            pattern_inference infer(m, params);
+            pattern_inference_old infer(m, params);
             infer(q, qe, new_pr);
             q = to_quantifier(qe);
         }
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index 7eb189ebf..26265a6f8 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -21,6 +21,7 @@ Revision History:
 #include "ast/ast_pp.h"
 #include "ast/for_each_expr.h"
 #include "ast/well_sorted.h"
+#include "ast/rewriter/rewriter_def.h"
 #include "ast/simplifier/arith_simplifier_plugin.h"
 #include "ast/simplifier/array_simplifier_plugin.h"
 #include "ast/simplifier/datatype_simplifier_plugin.h"
@@ -517,7 +518,7 @@ void asserted_formulas::reduce_and_solve() {
 void asserted_formulas::infer_patterns() {
     IF_IVERBOSE(10, verbose_stream() << "(smt.pattern-inference)\n";);
     TRACE("before_pattern_inference", display(tout););
-    pattern_inference infer(m, m_params);
+    pattern_inference_rw infer(m, m_params);
     expr_ref_vector  new_exprs(m);
     proof_ref_vector new_prs(m);
     unsigned i  = m_asserted_qhead;
diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp
index 725c9fc51..0a75ff700 100644
--- a/src/smt/proto_model/proto_model.cpp
+++ b/src/smt/proto_model/proto_model.cpp
@@ -207,8 +207,10 @@ void proto_model::remove_aux_decls_not_in_set(ptr_vector<func_decl> & decls, fun
    by their interpretations.
 */
 void proto_model::cleanup() {
+    TRACE("model_bug", model_v2_pp(tout, *this););
     func_decl_set found_aux_fs;
     for (auto const& kv : m_finterp) {
+        TRACE("model_bug", tout << kv.m_key->get_name() << "\n";);
         func_interp * fi = kv.m_value;
         cleanup_func_interp(fi, found_aux_fs);
     }
@@ -365,7 +367,7 @@ void proto_model::complete_partial_funcs() {
 }
 
 model * proto_model::mk_model() {
-    TRACE("proto_model", tout << "mk_model\n"; model_v2_pp(tout, *this););
+    TRACE("proto_model", model_v2_pp(tout << "mk_model\n", *this););
     model * m = alloc(model, m_manager);
 
     for (auto const& kv : m_interp) {
diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp
index 953afd4b7..5848fac62 100644
--- a/src/smt/smt_model_generator.cpp
+++ b/src/smt/smt_model_generator.cpp
@@ -19,7 +19,6 @@ Revision History:
 
 #include "util/ref_util.h"
 #include "ast/for_each_expr.h"
-#include "ast/ast_ll_pp.h"
 #include "ast/ast_pp.h"
 #include "ast/ast_smt2_pp.h"
 #include "smt/smt_context.h"

From f062e170373b1e1c3e7344b0b2a6c0bf93c14097 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 23 Aug 2017 12:30:33 -0700
Subject: [PATCH 08/74] remove simplifier dependencies from ufbv tactics

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/tactic/ufbv/macro_finder_tactic.cpp  | 16 ----------------
 src/tactic/ufbv/quasi_macros_tactic.cpp  | 17 +----------------
 src/tactic/ufbv/ufbv_rewriter.cpp        | 13 ++++++++-----
 src/tactic/ufbv/ufbv_rewriter.h          |  6 +++---
 src/tactic/ufbv/ufbv_rewriter_tactic.cpp |  6 +-----
 5 files changed, 13 insertions(+), 45 deletions(-)

diff --git a/src/tactic/ufbv/macro_finder_tactic.cpp b/src/tactic/ufbv/macro_finder_tactic.cpp
index b3f258ba7..3832339a8 100644
--- a/src/tactic/ufbv/macro_finder_tactic.cpp
+++ b/src/tactic/ufbv/macro_finder_tactic.cpp
@@ -17,10 +17,6 @@ Notes:
 
 --*/
 #include "tactic/tactical.h"
-#include "ast/simplifier/simplifier.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/macros/macro_manager.h"
 #include "ast/macros/macro_finder.h"
 #include "tactic/extension_model_converter.h"
@@ -52,18 +48,6 @@ class macro_finder_tactic : public tactic {
             fail_if_unsat_core_generation("macro-finder", g);
 
             bool produce_proofs = g->proofs_enabled();
-
-            simplifier simp(m_manager);
-            basic_simplifier_plugin * bsimp = alloc(basic_simplifier_plugin, m_manager);
-            bsimp->set_eliminate_and(m_elim_and);
-            simp.register_plugin(bsimp);
-            arith_simplifier_params a_params;
-            arith_simplifier_plugin * asimp = alloc(arith_simplifier_plugin, m_manager, *bsimp, a_params);
-            simp.register_plugin(asimp);
-            bv_simplifier_params bv_params;
-            bv_simplifier_plugin * bvsimp = alloc(bv_simplifier_plugin, m_manager, *bsimp, bv_params);
-            simp.register_plugin(bvsimp);
-                
             macro_manager mm(m_manager);
             macro_finder mf(m_manager, mm);
             
diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp
index 8a91bde61..8196dc664 100644
--- a/src/tactic/ufbv/quasi_macros_tactic.cpp
+++ b/src/tactic/ufbv/quasi_macros_tactic.cpp
@@ -17,10 +17,6 @@ Notes:
 
 --*/
 #include "tactic/tactical.h"
-#include "ast/simplifier/simplifier.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/macros/macro_manager.h"
 #include "ast/macros/macro_finder.h"
 #include "tactic/extension_model_converter.h"
@@ -50,18 +46,7 @@ class quasi_macros_tactic : public tactic {
             fail_if_unsat_core_generation("quasi-macros", g);
 
             bool produce_proofs = g->proofs_enabled();
-            
-            simplifier simp(m_manager);
-            basic_simplifier_plugin * bsimp = alloc(basic_simplifier_plugin, m_manager);
-            bsimp->set_eliminate_and(true);
-            simp.register_plugin(bsimp);
-            arith_simplifier_params a_params;
-            arith_simplifier_plugin * asimp = alloc(arith_simplifier_plugin, m_manager, *bsimp, a_params);
-            simp.register_plugin(asimp);
-            bv_simplifier_params bv_params;
-            bv_simplifier_plugin * bvsimp = alloc(bv_simplifier_plugin, m_manager, *bsimp, bv_params);
-            simp.register_plugin(bvsimp);
-                
+                            
             macro_manager mm(m_manager);
             quasi_macros qm(m_manager, mm);
             bool more = true;
diff --git a/src/tactic/ufbv/ufbv_rewriter.cpp b/src/tactic/ufbv/ufbv_rewriter.cpp
index 4f0f5e548..446d1a49c 100644
--- a/src/tactic/ufbv/ufbv_rewriter.cpp
+++ b/src/tactic/ufbv/ufbv_rewriter.cpp
@@ -20,20 +20,23 @@ Revision History:
 
 --*/
 
+#include "util/uint_set.h"
 #include "ast/ast_pp.h"
-#include "tactic/ufbv/ufbv_rewriter.h"
 #include "ast/for_each_expr.h"
 #include "ast/rewriter/var_subst.h"
-#include "util/uint_set.h"
+#include "tactic/ufbv/ufbv_rewriter.h"
 
-ufbv_rewriter::ufbv_rewriter(ast_manager & m, basic_simplifier_plugin & p):
+ufbv_rewriter::ufbv_rewriter(ast_manager & m):
     m_manager(m),
     m_match_subst(m),
-    m_bsimp(p),
+    m_bsimp(m),
     m_todo(m),
     m_rewrite_todo(m),
     m_rewrite_cache(m),
     m_new_exprs(m) {
+    params_ref p;
+    p.set_bool("elim_and", true);
+    m_bsimp.updt_params(p);
 }
 
 ufbv_rewriter::~ufbv_rewriter() {
@@ -396,7 +399,7 @@ expr * ufbv_rewriter::rewrite(expr * n) {
                         if (f->get_family_id() != m_manager.get_basic_family_id())
                             na = m_manager.mk_app(f, m_new_args.size(), m_new_args.c_ptr());
                         else
-                            m_bsimp.reduce(f, m_new_args.size(), m_new_args.c_ptr(), na);
+                            m_bsimp.mk_app(f, m_new_args.size(), m_new_args.c_ptr(), na);
                         TRACE("demodulator_bug", tout << "e:\n" << mk_pp(e, m_manager) << "\nnew_args: \n";
                               for (unsigned i = 0; i < m_new_args.size(); i++) { tout << mk_pp(m_new_args[i], m_manager) << "\n"; }
                               tout << "=====>\n";
diff --git a/src/tactic/ufbv/ufbv_rewriter.h b/src/tactic/ufbv/ufbv_rewriter.h
index d11453836..1e13f4fa4 100644
--- a/src/tactic/ufbv/ufbv_rewriter.h
+++ b/src/tactic/ufbv/ufbv_rewriter.h
@@ -23,10 +23,10 @@ Revision History:
 
 #include "ast/ast.h"
 #include "ast/substitution/substitution.h"
+#include "ast/rewriter/bool_rewriter.h"
 #include "util/obj_hashtable.h"
 #include "util/obj_pair_hashtable.h"
 #include "util/array_map.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
 
 /**
    \brief Apply demodulators as a preprocessing technique.
@@ -159,7 +159,7 @@ class ufbv_rewriter {
 
     ast_manager &       m_manager;
     match_subst         m_match_subst;
-    basic_simplifier_plugin & m_bsimp;
+    bool_rewriter       m_bsimp;
     fwd_idx_map         m_fwd_idx;
     back_idx_map        m_back_idx;
     demodulator2lhs_rhs m_demodulator2lhs_rhs;
@@ -194,7 +194,7 @@ protected:
     virtual int is_subset(expr * e1, expr * e2) const; 
 
 public:
-    ufbv_rewriter(ast_manager & m, basic_simplifier_plugin & p);
+    ufbv_rewriter(ast_manager & m);
     virtual ~ufbv_rewriter();
     
     void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
diff --git a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp
index b4bfabf65..615593317 100644
--- a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp
+++ b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp
@@ -17,8 +17,6 @@ Notes:
 
 --*/
 #include "tactic/tactical.h"
-#include "ast/simplifier/simplifier.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
 #include "tactic/ufbv/ufbv_rewriter.h"
 #include "tactic/ufbv/ufbv_rewriter_tactic.h"
 
@@ -45,9 +43,7 @@ class ufbv_rewriter_tactic : public tactic {
 
             bool produce_proofs = g->proofs_enabled();
             
-            basic_simplifier_plugin bsimp(m_manager);
-            bsimp.set_eliminate_and(true);
-            ufbv_rewriter dem(m_manager, bsimp);
+            ufbv_rewriter dem(m_manager);
             
             expr_ref_vector forms(m_manager), new_forms(m_manager);
             proof_ref_vector proofs(m_manager), new_proofs(m_manager);

From 7dd28781ab5f26703e8bb79ceee79e4c44bdc339 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 23 Aug 2017 16:33:36 -0700
Subject: [PATCH 09/74] remove simplifier dependencies from cmakelist.txt files

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/fpa/CMakeLists.txt                    |  2 +-
 src/ast/macros/CMakeLists.txt                 |  2 +-
 src/ast/pattern/CMakeLists.txt                |  2 +-
 src/ast/pattern/pattern_inference.cpp         | 19 ++++++++++++-------
 src/ast/pattern/pattern_inference.h           |  2 ++
 src/ast/rewriter/bit_blaster/CMakeLists.txt   |  1 -
 src/model/model_core.cpp                      | 16 ++++++----------
 .../dl_mk_quantifier_instantiation.cpp        |  2 +-
 src/smt/proto_model/CMakeLists.txt            |  2 +-
 9 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/src/ast/fpa/CMakeLists.txt b/src/ast/fpa/CMakeLists.txt
index 4a9506d16..2a6d0763c 100644
--- a/src/ast/fpa/CMakeLists.txt
+++ b/src/ast/fpa/CMakeLists.txt
@@ -5,7 +5,7 @@ z3_add_component(fpa
     fpa2bv_rewriter.cpp
   COMPONENT_DEPENDENCIES
     ast
-    simplifier
+    rewriter
     model
     util
   PYG_FILES
diff --git a/src/ast/macros/CMakeLists.txt b/src/ast/macros/CMakeLists.txt
index ca38b4759..ec6d7e26c 100644
--- a/src/ast/macros/CMakeLists.txt
+++ b/src/ast/macros/CMakeLists.txt
@@ -5,5 +5,5 @@ z3_add_component(macros
     macro_util.cpp
     quasi_macros.cpp
   COMPONENT_DEPENDENCIES
-    simplifier
+    rewriter
 )
diff --git a/src/ast/pattern/CMakeLists.txt b/src/ast/pattern/CMakeLists.txt
index 6e8301afc..5531bb29b 100644
--- a/src/ast/pattern/CMakeLists.txt
+++ b/src/ast/pattern/CMakeLists.txt
@@ -29,7 +29,7 @@ z3_add_component(pattern
     ${CMAKE_CURRENT_BINARY_DIR}/database.h
   COMPONENT_DEPENDENCIES
     normal_forms
-    simplifier
+    rewriter
     smt2parser
   PYG_FILES
     pattern_inference_params_helper.pyg
diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp
index 94a90d829..dfeb29ffe 100644
--- a/src/ast/pattern/pattern_inference.cpp
+++ b/src/ast/pattern/pattern_inference.cpp
@@ -89,6 +89,15 @@ bool smaller_pattern::operator()(unsigned num_bindings, expr * p1, expr * p2) {
     return process(p1, p2);
 }
 
+
+#ifdef _TRACE
+static void dump_app_vector(std::ostream & out, ptr_vector<app> const & v, ast_manager & m) {
+    for (app * e : v) 
+        out << mk_pp(e, m) << "\n";
+}
+#endif
+
+#if 0
 pattern_inference_old::pattern_inference_old(ast_manager & m, pattern_inference_params & params):
     simplifier(m),
     m_params(params),
@@ -470,12 +479,6 @@ void pattern_inference_old::reset_pre_patterns() {
     m_pre_patterns.reset();
 }
 
-#ifdef _TRACE
-static void dump_app_vector(std::ostream & out, ptr_vector<app> const & v, ast_manager & m) {
-    for (app * e : v) 
-        out << mk_pp(e, m) << "\n";
-}
-#endif
 
 bool pattern_inference_old::is_forbidden(app * n) const {
     func_decl const * decl = n->get_decl();
@@ -567,7 +570,6 @@ void pattern_inference_old::mk_patterns(unsigned num_bindings,
     m_candidates.reset();
 }
 
-#include "ast/pattern/database.h"
 
 void pattern_inference_old::reduce1_quantifier(quantifier * q) {
     TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";);
@@ -729,6 +731,9 @@ void pattern_inference_old::reduce1_quantifier(quantifier * q) {
     cache_result(q, new_q, pr);
 }
 
+#endif
+
+#include "ast/pattern/database.h"
 
 
 pattern_inference_cfg::pattern_inference_cfg(ast_manager & m, pattern_inference_params & params):
diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h
index 7b0ec9cfe..281c3ff8b 100644
--- a/src/ast/pattern/pattern_inference.h
+++ b/src/ast/pattern/pattern_inference.h
@@ -61,6 +61,7 @@ public:
     bool operator()(unsigned num_bindings, expr * p1, expr * p2);
 };
 
+#if 0
 class pattern_inference_old : public simplifier {
     pattern_inference_params & m_params;
     family_id                  m_bfid;
@@ -244,6 +245,7 @@ public:
 
     bool is_forbidden(app * n) const;
 };
+#endif
 
 class pattern_inference_cfg :  public default_rewriter_cfg {
     ast_manager&               m;
diff --git a/src/ast/rewriter/bit_blaster/CMakeLists.txt b/src/ast/rewriter/bit_blaster/CMakeLists.txt
index 9eea1558e..c8985a051 100644
--- a/src/ast/rewriter/bit_blaster/CMakeLists.txt
+++ b/src/ast/rewriter/bit_blaster/CMakeLists.txt
@@ -4,5 +4,4 @@ z3_add_component(bit_blaster
     bit_blaster_rewriter.cpp
   COMPONENT_DEPENDENCIES
     rewriter
-    simplifier
 )
diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp
index 5eb1eb00d..2fd2d8746 100644
--- a/src/model/model_core.cpp
+++ b/src/model/model_core.cpp
@@ -19,18 +19,14 @@ Revision History:
 #include "model/model_core.h"
 
 model_core::~model_core() {
-    decl2expr::iterator it1  = m_interp.begin();
-    decl2expr::iterator end1 = m_interp.end();
-    for (; it1 != end1; ++it1) {
-        m_manager.dec_ref(it1->m_key);
-        m_manager.dec_ref(it1->m_value);
+    for (auto & kv : m_interp) {
+        m_manager.dec_ref(kv.m_key);
+        m_manager.dec_ref(kv.m_value);
     }
 
-    decl2finterp::iterator it2  = m_finterp.begin();
-    decl2finterp::iterator end2 = m_finterp.end();
-    for (; it2 != end2; ++it2) {
-        m_manager.dec_ref(it2->m_key);
-        dealloc(it2->m_value);
+    for (auto & kv : m_finterp) {
+        m_manager.dec_ref(kv.m_key);
+        dealloc(kv.m_value);
     }
 }
 
diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
index cfb998a32..f32840c49 100644
--- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
+++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
@@ -70,7 +70,7 @@ namespace datalog {
         if (q->get_num_patterns() == 0) {
             proof_ref new_pr(m);
             pattern_inference_params params;
-            pattern_inference_old infer(m, params);
+            pattern_inference_rw infer(m, params);
             infer(q, qe, new_pr);
             q = to_quantifier(qe);
         }
diff --git a/src/smt/proto_model/CMakeLists.txt b/src/smt/proto_model/CMakeLists.txt
index 0539c0fb0..c5f6c4b18 100644
--- a/src/smt/proto_model/CMakeLists.txt
+++ b/src/smt/proto_model/CMakeLists.txt
@@ -8,6 +8,6 @@ z3_add_component(proto_model
     value_factory.cpp
   COMPONENT_DEPENDENCIES
     model
-    simplifier
+    rewriter
     smt_params
 )

From f91496f5fff77ad04647ff3c9f350a8e92a3641c Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 23 Aug 2017 16:56:55 -0700
Subject: [PATCH 10/74] pruning simplifier dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/simplifier/pull_ite_tree.cpp | 10 +++++-----
 src/ast/simplifier/pull_ite_tree.h   |  5 +++--
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/ast/simplifier/pull_ite_tree.cpp b/src/ast/simplifier/pull_ite_tree.cpp
index 072bbd12c..a092f9649 100644
--- a/src/ast/simplifier/pull_ite_tree.cpp
+++ b/src/ast/simplifier/pull_ite_tree.cpp
@@ -21,9 +21,9 @@ Revision History:
 #include "ast/for_each_expr.h"
 #include "ast/ast_pp.h"
 
-pull_ite_tree::pull_ite_tree(ast_manager & m, simplifier & s):
+pull_ite_tree::pull_ite_tree(ast_manager & m):
     m_manager(m),
-    m_simplifier(s),
+    m_rewriter(m),
     m_cache(m) {
 }
 
@@ -64,7 +64,7 @@ void pull_ite_tree::reduce(expr * n) {
         get_cached(e_old, e, e_pr);
         expr_ref r(m_manager);
         expr * args[3] = {c, t, e};
-        m_simplifier.mk_app(to_app(n)->get_decl(), 3, args, r);
+        r = m_rewriter.mk_app(to_app(n)->get_decl(), 3, args);
         if (!m_manager.proofs_enabled()) {
             // expr * r = m_manager.mk_ite(c, t, e);
             cache_result(n, r, 0);
@@ -117,7 +117,7 @@ void pull_ite_tree::reduce(expr * n) {
     else {
         expr_ref r(m_manager);
         m_args[m_arg_idx] = n;
-        m_simplifier.mk_app(m_p, m_args.size(), m_args.c_ptr(), r);
+        r = m_rewriter.mk_app(m_p, m_args.size(), m_args.c_ptr());
         if (!m_manager.proofs_enabled()) {
             // expr * r = m_manager.mk_app(m_p, m_args.size(), m_args.c_ptr());
             cache_result(n, r, 0);
@@ -181,7 +181,7 @@ void pull_ite_tree::operator()(app * n, app_ref & r, proof_ref & pr) {
 
 pull_ite_tree_star::pull_ite_tree_star(ast_manager & m, simplifier & s):
     simplifier(m),
-    m_proc(m, s) {
+    m_proc(m) {
     borrow_plugins(s);
 }
 
diff --git a/src/ast/simplifier/pull_ite_tree.h b/src/ast/simplifier/pull_ite_tree.h
index bc4a0bb68..4b35c124a 100644
--- a/src/ast/simplifier/pull_ite_tree.h
+++ b/src/ast/simplifier/pull_ite_tree.h
@@ -20,6 +20,7 @@ Revision History:
 #define PULL_ITE_TREE_H_
 
 #include "ast/ast.h"
+#include "ast/rewriter/th_rewriter.h"
 #include "ast/simplifier/simplifier.h"
 #include "ast/recurse_expr.h"
 #include "util/obj_hashtable.h"
@@ -34,7 +35,7 @@ Revision History:
 */
 class pull_ite_tree {
     ast_manager &         m_manager;
-    simplifier &          m_simplifier;
+    th_rewriter           m_rewriter;
     func_decl *           m_p;
     ptr_vector<expr>      m_args;
     unsigned              m_arg_idx; //!< position of the ite argument
@@ -56,7 +57,7 @@ class pull_ite_tree {
         return m_manager.mk_app(m_p, m_args.size(), m_args.c_ptr());
     }
 public:
-    pull_ite_tree(ast_manager & m, simplifier & s);
+    pull_ite_tree(ast_manager & m);
     /**
        \brief Apply the transformation above if n contains an ite-expression.
        Store the result in r. If n does not contain an ite-expression, then

From 8b2d60e3caceb6c6803d653c7e8a48b3b6e587b0 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 23 Aug 2017 17:57:03 -0700
Subject: [PATCH 11/74] using rewrite in push_app_ite

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/simplifier/pull_ite_tree.cpp | 36 +++++++++++++++
 src/ast/simplifier/pull_ite_tree.h   | 44 ++++++++++++++++++-
 src/ast/simplifier/push_app_ite.cpp  | 65 +++++++++++++++++++++++++++-
 src/ast/simplifier/push_app_ite.h    | 43 +++++++++++++++++-
 src/smt/asserted_formulas.cpp        |  6 +--
 5 files changed, 187 insertions(+), 7 deletions(-)

diff --git a/src/ast/simplifier/pull_ite_tree.cpp b/src/ast/simplifier/pull_ite_tree.cpp
index a092f9649..c317cafd8 100644
--- a/src/ast/simplifier/pull_ite_tree.cpp
+++ b/src/ast/simplifier/pull_ite_tree.cpp
@@ -209,5 +209,41 @@ bool pull_cheap_ite_tree_star::is_target(app * n) const {
 
 
 
+pull_ite_tree_cfg::pull_ite_tree_cfg(ast_manager & m):
+    m(m),
+    m_trail(m),
+    m_proc(m) {
+}
+
+bool pull_ite_tree_cfg::get_subst(expr * n, expr* & r, proof* & p) {
+    if (is_app(n) && is_target(to_app(n))) {
+        app_ref tmp(m);
+        proof_ref pr(m);
+        m_proc(to_app(n), tmp, pr);
+        if (tmp != n) {
+            r = tmp;
+            p = pr;
+            m_trail.push_back(r);
+            m_trail.push_back(p);
+            return true;
+        }
+    }
+    return false;
+}
+
+bool pull_cheap_ite_tree_cfg::is_target(app * n) const {
+    bool r = 
+        n->get_num_args() == 2 &&
+        n->get_family_id() != null_family_id &&
+        m.is_bool(n) &&
+        (m.is_value(n->get_arg(0)) || m.is_value(n->get_arg(1))) &&
+        (m.is_term_ite(n->get_arg(0)) || m.is_term_ite(n->get_arg(1)));
+    TRACE("pull_ite_target", tout << mk_pp(n, m) << "\nresult: " << r << "\n";);
+    return r;
+}
+
+
+
+
 
 
diff --git a/src/ast/simplifier/pull_ite_tree.h b/src/ast/simplifier/pull_ite_tree.h
index 4b35c124a..37837a3cf 100644
--- a/src/ast/simplifier/pull_ite_tree.h
+++ b/src/ast/simplifier/pull_ite_tree.h
@@ -20,11 +20,12 @@ Revision History:
 #define PULL_ITE_TREE_H_
 
 #include "ast/ast.h"
+#include "ast/rewriter/rewriter.h"
 #include "ast/rewriter/th_rewriter.h"
 #include "ast/simplifier/simplifier.h"
+#include "ast/expr_map.h"
 #include "ast/recurse_expr.h"
 #include "util/obj_hashtable.h"
-#include "ast/expr_map.h"
 
 /**
    \brief Functor for applying the following transformation
@@ -98,5 +99,46 @@ public:
     virtual bool is_target(app * n) const;
 };
 
+/**
+   \brief Functor for applying the pull_ite_tree on subexpressions n that
+   satisfy the is_target virtual predicate.
+*/
+class pull_ite_tree_cfg : public default_rewriter_cfg {
+protected:
+    ast_manager& m;
+    expr_ref_vector m_trail;
+    pull_ite_tree m_proc;
+public:
+    pull_ite_tree_cfg(ast_manager & m);
+    virtual ~pull_ite_tree_cfg() {}
+    virtual bool is_target(app * n) const = 0;
+    bool get_subst(expr * n, expr* & r, proof* & p);
+};
+
+/**
+   \brief Apply pull_ite_tree on predicates of the form
+   (p ite v) and (p v ite)
+
+   where:
+   - p is an interpreted predicate
+   - ite is an ite-term expression
+   - v is a value
+*/
+class pull_cheap_ite_tree_cfg : public pull_ite_tree_cfg {
+public:
+    pull_cheap_ite_tree_cfg(ast_manager & m):pull_ite_tree_cfg(m) {}
+    virtual ~pull_cheap_ite_tree_cfg() {}
+    virtual bool is_target(app * n) const;
+};
+
+class pull_cheap_ite_tree_rw  : public rewriter_tpl<pull_cheap_ite_tree_cfg> {
+    pull_cheap_ite_tree_cfg m_cfg;
+public:
+    pull_cheap_ite_tree_rw(ast_manager& m):
+        rewriter_tpl<pull_cheap_ite_tree_cfg>(m, m.proofs_enabled(), m_cfg),
+        m_cfg(m)
+    {}
+};
+
 #endif /* PULL_ITE_TREE_H_ */
 
diff --git a/src/ast/simplifier/push_app_ite.cpp b/src/ast/simplifier/push_app_ite.cpp
index 8b56dcd85..3d118e4ac 100644
--- a/src/ast/simplifier/push_app_ite.cpp
+++ b/src/ast/simplifier/push_app_ite.cpp
@@ -32,7 +32,7 @@ push_app_ite::~push_app_ite() {
     m_plugins.release();
 }
 
-int push_app_ite::has_ite_arg(unsigned num_args, expr * const * args) {
+static int has_ite_arg(ast_manager& m, unsigned num_args, expr * const * args) {
     for (unsigned i = 0; i < num_args; i++)
         if (m.is_ite(args[i]))
             return i;
@@ -41,7 +41,7 @@ int push_app_ite::has_ite_arg(unsigned num_args, expr * const * args) {
 
 void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & r) {
     TRACE("push_app_ite", tout << "pushing app...\n";);
-    int ite_arg_idx = has_ite_arg(num_args, args);
+    int ite_arg_idx = has_ite_arg(m, num_args, args);
     if (ite_arg_idx < 0) {
         mk_app(decl, num_args, args, r);
         return;
@@ -218,3 +218,64 @@ bool ng_push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * cons
 ng_push_app_ite::ng_push_app_ite(simplifier & s, bool conservative):
     push_app_ite(s, conservative) {
 }
+
+
+/**
+   \brief Default (conservative) implementation. Return true if there one and only one ite-term argument.
+*/
+bool push_app_ite_cfg::is_target(func_decl * decl, unsigned num_args, expr * const * args) {
+    if (m.is_ite(decl))
+        return false;
+    bool found_ite = false;
+    for (unsigned i = 0; i < num_args; i++) {
+        if (m.is_ite(args[i]) && !m.is_bool(args[i])) {
+            if (found_ite) {
+                if (m_conservative)
+                    return false;
+            }
+            else {
+                found_ite = true;
+            }
+        }
+    }
+    CTRACE("push_app_ite", found_ite, tout << "found target for push app ite:\n";
+          tout << decl->get_name();
+          for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m);
+          tout << "\n";);
+    return found_ite;
+}
+
+br_status push_app_ite_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
+    if (!is_target(f, num, args)) {
+        return BR_FAILED;
+    }
+    int ite_arg_idx = has_ite_arg(m, num, args);
+    if (ite_arg_idx < 0) {
+        return BR_FAILED;
+    }
+    app * ite               = to_app(args[ite_arg_idx]);
+    expr * c = 0, * t = 0, * e = 0;
+    VERIFY(m.is_ite(ite, c, t, e));
+    expr ** args_prime      = const_cast<expr**>(args);
+    expr * old              = args_prime[ite_arg_idx];
+    args_prime[ite_arg_idx] = t;
+    expr_ref t_new(m.mk_app(f, num, args_prime), m);
+    args_prime[ite_arg_idx] = e;
+    expr_ref e_new(m.mk_app(f, num, args_prime), m);
+    args_prime[ite_arg_idx] = old;
+    result = m.mk_ite(c, t_new, e_new);
+    if (m.proofs_enabled()) {
+        result_pr = m.mk_rewrite(m.mk_app(f, num, args), result);
+    }
+    return BR_REWRITE2;
+}
+
+bool ng_push_app_ite_cfg::is_target(func_decl * decl, unsigned num_args, expr * const * args) {
+    bool r = push_app_ite_cfg::is_target(decl, num_args, args);
+    if (!r) 
+        return false;
+    for (unsigned i = 0; i < num_args; i++)
+        if (!is_ground(args[i]))
+            return true;
+    return false;
+}
diff --git a/src/ast/simplifier/push_app_ite.h b/src/ast/simplifier/push_app_ite.h
index 96400b1af..4faf853ea 100644
--- a/src/ast/simplifier/push_app_ite.h
+++ b/src/ast/simplifier/push_app_ite.h
@@ -21,6 +21,7 @@ Revision History:
 
 #include "ast/ast.h"
 #include "ast/simplifier/simplifier.h"
+#include "ast/rewriter/rewriter.h"
 
 /**
    \brief Functor for applying the following transformation:
@@ -30,7 +31,6 @@ Revision History:
 class push_app_ite : public simplifier {
 protected:
     bool          m_conservative;
-    int has_ite_arg(unsigned num_args, expr * const * args);
     void apply(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result);
     virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
     void reduce_core(expr * n);
@@ -59,5 +59,46 @@ public:
     virtual ~ng_push_app_ite() {}
 };
 
+struct push_app_ite_cfg : public default_rewriter_cfg {
+    ast_manager& m;
+    bool m_conservative;
+    virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
+    br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr);
+    push_app_ite_cfg(ast_manager& m, bool conservative = true): m(m), m_conservative(conservative) {}
+};
+
+/**
+   \brief Variation of push_app_ite that applies the transformation on nonground terms only.
+
+   \remark This functor uses the app::is_ground method. This method is not
+   completly precise, for instance, any term containing a quantifier is marked as non ground.
+*/
+class ng_push_app_ite_cfg : public push_app_ite_cfg {
+protected:
+    virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
+public:
+    ng_push_app_ite_cfg(ast_manager& m, bool conservative = true): push_app_ite_cfg(m, conservative) {}
+    virtual ~ng_push_app_ite_cfg() {}
+};
+
+struct push_app_ite_rw : public rewriter_tpl<push_app_ite_cfg> {
+    push_app_ite_cfg m_cfg;
+public:
+    push_app_ite_rw(ast_manager& m, bool conservative = true):
+        rewriter_tpl<push_app_ite_cfg>(m, m.proofs_enabled(), m_cfg),
+        m_cfg(m, conservative)
+    {}
+};
+
+struct ng_push_app_ite_rw : public rewriter_tpl<ng_push_app_ite_cfg> {
+    ng_push_app_ite_cfg m_cfg;
+public:
+    ng_push_app_ite_rw(ast_manager& m, bool conservative = true):
+        rewriter_tpl<ng_push_app_ite_cfg>(m, m.proofs_enabled(), m_cfg),
+        m_cfg(m, conservative)
+    {}
+};
+
+
 #endif /* PUSH_APP_ITE_H_ */
 
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index 26265a6f8..b475ee778 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -755,7 +755,7 @@ void asserted_formulas::propagate_booleans() {
         return changed;                                                 \
     }
 
-MK_SIMPLIFIER(pull_cheap_ite_trees, pull_cheap_ite_tree_star functor(m, m_simplifier), "pull_cheap_ite_trees", "pull-cheap-ite-trees", false);
+MK_SIMPLIFIER(pull_cheap_ite_trees, pull_cheap_ite_tree_rw functor(m), "pull_cheap_ite_trees", "pull-cheap-ite-trees", false);
 
 MK_SIMPLIFIER(pull_nested_quantifiers, pull_nested_quant functor(m), "pull_nested_quantifiers", "pull-nested-quantifiers", false);
 
@@ -830,8 +830,8 @@ MK_SIMPLIFIER(elim_bvs_from_quantifiers, bv_elim_star functor(m), "bv_elim", "el
         reduce_and_solve();                                             \
     }
 
-LIFT_ITE(lift_ite, push_app_ite functor(m_simplifier, m_params.m_lift_ite == LI_CONSERVATIVE), "lifting ite");
-LIFT_ITE(ng_lift_ite, ng_push_app_ite functor(m_simplifier, m_params.m_ng_lift_ite == LI_CONSERVATIVE), "lifting ng ite");
+LIFT_ITE(lift_ite, push_app_ite_rw functor(m, m_params.m_lift_ite == LI_CONSERVATIVE), "lifting ite");
+LIFT_ITE(ng_lift_ite, ng_push_app_ite_rw functor(m, m_params.m_ng_lift_ite == LI_CONSERVATIVE), "lifting ng ite");
 
 unsigned asserted_formulas::get_total_size() const {
     expr_mark visited;

From a7bb41fd499556f3d30e50f06fcf4dfb0274d177 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Thu, 24 Aug 2017 09:19:35 -0700
Subject: [PATCH 12/74] fix build issues

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 .../transforms/dl_mk_quantifier_instantiation.cpp   |  1 +
 src/smt/asserted_formulas.cpp                       |  4 +++-
 src/smt/asserted_formulas.h                         | 13 +++++++------
 3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
index f32840c49..74d15bcdf 100644
--- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
+++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
@@ -26,6 +26,7 @@ Revision History:
 #include "muz/transforms/dl_mk_quantifier_instantiation.h"
 #include "muz/base/dl_context.h"
 #include "ast/pattern/pattern_inference.h"
+#include "ast/rewriter/rewriter_def.h"
 
 
 namespace datalog {
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index b475ee778..eac5f7c10 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -56,7 +56,8 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p):
     m_macro_manager(m),
     m_bit2int(m),
     m_bv_sharing(m),
-    m_inconsistent(false){
+    m_inconsistent(false), 
+    m_has_quantifiers(false) {
 
     m_bsimp = 0;
     m_bvsimp = 0;
@@ -143,6 +144,7 @@ void asserted_formulas::set_eliminate_and(bool flag) {
 void asserted_formulas::assert_expr(expr * e, proof * _in_pr) {
     if (inconsistent()) 
         return;
+    m_has_quantifiers |= ::has_quantifiers(e);
     if (!m_params.m_preprocess) {
         push_assertion(e, _in_pr, m_asserted_formulas, m_asserted_formula_prs);
         return;
diff --git a/src/smt/asserted_formulas.h b/src/smt/asserted_formulas.h
index 093680fd9..eaed4e405 100644
--- a/src/smt/asserted_formulas.h
+++ b/src/smt/asserted_formulas.h
@@ -19,17 +19,17 @@ Revision History:
 #ifndef ASSERTED_FORMULAS_H_
 #define ASSERTED_FORMULAS_H_
 
-#include "smt/params/smt_params.h"
+#include "util/statistics.h"
+#include "ast/static_features.h"
 #include "ast/simplifier/simplifier.h"
 #include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/static_features.h"
+#include "ast/simplifier/maximise_ac_sharing.h"
+#include "ast/simplifier/bit2int.h"
 #include "ast/macros/macro_manager.h"
 #include "ast/macros/macro_finder.h"
 #include "ast/normal_forms/defined_names.h"
-#include "ast/simplifier/maximise_ac_sharing.h"
-#include "ast/simplifier/bit2int.h"
-#include "util/statistics.h"
 #include "ast/pattern/pattern_inference.h"
+#include "smt/params/smt_params.h"
 
 class arith_simplifier_plugin;
 class bv_simplifier_plugin;
@@ -55,6 +55,7 @@ class asserted_formulas {
     maximise_bv_sharing         m_bv_sharing;
 
     bool                        m_inconsistent;
+    bool                        m_has_quantifiers;
 
     struct scope {
         unsigned                m_asserted_formulas_lim;
@@ -128,7 +129,7 @@ public:
     void display_ll(std::ostream & out, ast_mark & pp_visited) const;
     void collect_statistics(statistics & st) const;
     // TODO: improve precision of the following method.
-    bool has_quantifiers() const { return m_simplifier.visited_quantifier(); /* approximation */ }
+    bool has_quantifiers() const { return m_has_quantifiers; }
     
     // -----------------------------------
     //

From 23d1c0a9a8a7167992e6a823ed3affd93d7f2bc1 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Thu, 24 Aug 2017 11:13:01 -0700
Subject: [PATCH 13/74] move pull/push files

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/CMakeLists.txt                    | 2 ++
 src/ast/{simplifier => rewriter}/pull_ite_tree.cpp | 2 +-
 src/ast/{simplifier => rewriter}/pull_ite_tree.h   | 0
 src/ast/{simplifier => rewriter}/push_app_ite.cpp  | 2 +-
 src/ast/{simplifier => rewriter}/push_app_ite.h    | 0
 src/ast/simplifier/CMakeLists.txt                  | 2 --
 src/smt/asserted_formulas.cpp                      | 4 ++--
 7 files changed, 6 insertions(+), 6 deletions(-)
 rename src/ast/{simplifier => rewriter}/pull_ite_tree.cpp (99%)
 rename src/ast/{simplifier => rewriter}/pull_ite_tree.h (100%)
 rename src/ast/{simplifier => rewriter}/push_app_ite.cpp (99%)
 rename src/ast/{simplifier => rewriter}/push_app_ite.h (100%)

diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt
index abf09ff0c..72bca53d4 100644
--- a/src/ast/rewriter/CMakeLists.txt
+++ b/src/ast/rewriter/CMakeLists.txt
@@ -19,6 +19,8 @@ z3_add_component(rewriter
     mk_simplified_app.cpp
     pb_rewriter.cpp
     pb2bv_rewriter.cpp
+    push_app_ite.cpp
+    pull_ite_tree.cpp
     quant_hoist.cpp
     rewriter.cpp
     seq_rewriter.cpp
diff --git a/src/ast/simplifier/pull_ite_tree.cpp b/src/ast/rewriter/pull_ite_tree.cpp
similarity index 99%
rename from src/ast/simplifier/pull_ite_tree.cpp
rename to src/ast/rewriter/pull_ite_tree.cpp
index c317cafd8..bcb672ea7 100644
--- a/src/ast/simplifier/pull_ite_tree.cpp
+++ b/src/ast/rewriter/pull_ite_tree.cpp
@@ -16,7 +16,7 @@ Author:
 Revision History:
 
 --*/
-#include "ast/simplifier/pull_ite_tree.h"
+#include "ast/rewriter/pull_ite_tree.h"
 #include "ast/recurse_expr_def.h"
 #include "ast/for_each_expr.h"
 #include "ast/ast_pp.h"
diff --git a/src/ast/simplifier/pull_ite_tree.h b/src/ast/rewriter/pull_ite_tree.h
similarity index 100%
rename from src/ast/simplifier/pull_ite_tree.h
rename to src/ast/rewriter/pull_ite_tree.h
diff --git a/src/ast/simplifier/push_app_ite.cpp b/src/ast/rewriter/push_app_ite.cpp
similarity index 99%
rename from src/ast/simplifier/push_app_ite.cpp
rename to src/ast/rewriter/push_app_ite.cpp
index 3d118e4ac..fe6f8b408 100644
--- a/src/ast/simplifier/push_app_ite.cpp
+++ b/src/ast/rewriter/push_app_ite.cpp
@@ -17,7 +17,7 @@ Author:
 Revision History:
 
 --*/
-#include "ast/simplifier/push_app_ite.h"
+#include "ast/rewriter/push_app_ite.h"
 #include "ast/ast_pp.h"
 
 push_app_ite::push_app_ite(simplifier & s, bool conservative):
diff --git a/src/ast/simplifier/push_app_ite.h b/src/ast/rewriter/push_app_ite.h
similarity index 100%
rename from src/ast/simplifier/push_app_ite.h
rename to src/ast/rewriter/push_app_ite.h
diff --git a/src/ast/simplifier/CMakeLists.txt b/src/ast/simplifier/CMakeLists.txt
index 9575c5c89..c5c310c07 100644
--- a/src/ast/simplifier/CMakeLists.txt
+++ b/src/ast/simplifier/CMakeLists.txt
@@ -15,8 +15,6 @@ z3_add_component(simplifier
     inj_axiom.cpp
     maximise_ac_sharing.cpp
     poly_simplifier_plugin.cpp
-    pull_ite_tree.cpp
-    push_app_ite.cpp
     seq_simplifier_plugin.cpp
     simplifier.cpp
     simplifier_plugin.cpp
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index eac5f7c10..6f1c4cfcc 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -22,14 +22,14 @@ Revision History:
 #include "ast/for_each_expr.h"
 #include "ast/well_sorted.h"
 #include "ast/rewriter/rewriter_def.h"
+#include "ast/rewriter/pull_ite_tree.h"
+#include "ast/rewriter/push_app_ite.h"
 #include "ast/simplifier/arith_simplifier_plugin.h"
 #include "ast/simplifier/array_simplifier_plugin.h"
 #include "ast/simplifier/datatype_simplifier_plugin.h"
 #include "ast/simplifier/fpa_simplifier_plugin.h"
 #include "ast/simplifier/seq_simplifier_plugin.h"
 #include "ast/simplifier/bv_simplifier_plugin.h"
-#include "ast/simplifier/pull_ite_tree.h"
-#include "ast/simplifier/push_app_ite.h"
 #include "ast/simplifier/bv_elim.h"
 #include "ast/simplifier/inj_axiom.h"
 #include "ast/simplifier/elim_bounds.h"

From 5141477809cd70c90fa2e33b5ccb50a57f4ea7a1 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Thu, 24 Aug 2017 11:16:48 -0700
Subject: [PATCH 14/74] remove dead code

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/pull_ite_tree.cpp |  28 -----
 src/ast/rewriter/pull_ite_tree.h   |  31 -----
 src/ast/rewriter/push_app_ite.cpp  | 191 -----------------------------
 src/ast/rewriter/push_app_ite.h    |  31 -----
 4 files changed, 281 deletions(-)

diff --git a/src/ast/rewriter/pull_ite_tree.cpp b/src/ast/rewriter/pull_ite_tree.cpp
index bcb672ea7..651744bf9 100644
--- a/src/ast/rewriter/pull_ite_tree.cpp
+++ b/src/ast/rewriter/pull_ite_tree.cpp
@@ -179,34 +179,6 @@ void pull_ite_tree::operator()(app * n, app_ref & r, proof_ref & pr) {
     m_todo.reset();
 }
 
-pull_ite_tree_star::pull_ite_tree_star(ast_manager & m, simplifier & s):
-    simplifier(m),
-    m_proc(m) {
-    borrow_plugins(s);
-}
-
-bool pull_ite_tree_star::get_subst(expr * n, expr_ref & r, proof_ref & p) {
-    if (is_app(n) && is_target(to_app(n))) {
-        app_ref tmp(m);
-        m_proc(to_app(n), tmp, p);
-        r = tmp;
-        return true;
-    }
-    return false;
-}
-
-bool pull_cheap_ite_tree_star::is_target(app * n) const {
-    bool r = 
-        n->get_num_args() == 2 &&
-        n->get_family_id() != null_family_id &&
-        m.is_bool(n) &&
-        (m.is_value(n->get_arg(0)) || m.is_value(n->get_arg(1))) &&
-        (m.is_term_ite(n->get_arg(0)) || m.is_term_ite(n->get_arg(1)));
-    TRACE("pull_ite_target", tout << mk_pp(n, m) << "\nresult: " << r << "\n";);
-    return r;
-}
-
-
 
 
 pull_ite_tree_cfg::pull_ite_tree_cfg(ast_manager & m):
diff --git a/src/ast/rewriter/pull_ite_tree.h b/src/ast/rewriter/pull_ite_tree.h
index 37837a3cf..3ff0a716d 100644
--- a/src/ast/rewriter/pull_ite_tree.h
+++ b/src/ast/rewriter/pull_ite_tree.h
@@ -22,7 +22,6 @@ Revision History:
 #include "ast/ast.h"
 #include "ast/rewriter/rewriter.h"
 #include "ast/rewriter/th_rewriter.h"
-#include "ast/simplifier/simplifier.h"
 #include "ast/expr_map.h"
 #include "ast/recurse_expr.h"
 #include "util/obj_hashtable.h"
@@ -69,36 +68,6 @@ public:
     void operator()(app * n, app_ref & r, proof_ref & pr);
 };
 
-/**
-   \brief Functor for applying the pull_ite_tree on subexpressions n that
-   satisfy the is_target virtual predicate.
-*/
-class pull_ite_tree_star : public simplifier {
-protected:
-    pull_ite_tree m_proc;
-    virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
-public:
-    pull_ite_tree_star(ast_manager & m, simplifier & s);
-    virtual ~pull_ite_tree_star() { release_plugins(); }
-    virtual bool is_target(app * n) const = 0;
-};
-
-/**
-   \brief Apply pull_ite_tree on predicates of the form
-   (p ite v) and (p v ite)
-
-   where:
-   - p is an interpreted predicate
-   - ite is an ite-term expression
-   - v is a value
-*/
-class pull_cheap_ite_tree_star : public pull_ite_tree_star {
-public:
-    pull_cheap_ite_tree_star(ast_manager & m, simplifier & s):pull_ite_tree_star(m, s) {}
-    virtual ~pull_cheap_ite_tree_star() {}
-    virtual bool is_target(app * n) const;
-};
-
 /**
    \brief Functor for applying the pull_ite_tree on subexpressions n that
    satisfy the is_target virtual predicate.
diff --git a/src/ast/rewriter/push_app_ite.cpp b/src/ast/rewriter/push_app_ite.cpp
index fe6f8b408..386a4fb27 100644
--- a/src/ast/rewriter/push_app_ite.cpp
+++ b/src/ast/rewriter/push_app_ite.cpp
@@ -20,17 +20,6 @@ Revision History:
 #include "ast/rewriter/push_app_ite.h"
 #include "ast/ast_pp.h"
 
-push_app_ite::push_app_ite(simplifier & s, bool conservative):
-    simplifier(s.get_manager()),
-    m_conservative(conservative) {
-
-    borrow_plugins(s);
-}
-
-push_app_ite::~push_app_ite() {
-    // the plugins were borrowed. So, release ownership.
-    m_plugins.release();
-}
 
 static int has_ite_arg(ast_manager& m, unsigned num_args, expr * const * args) {
     for (unsigned i = 0; i < num_args; i++)
@@ -39,186 +28,6 @@ static int has_ite_arg(ast_manager& m, unsigned num_args, expr * const * args) {
     return -1;
 }
 
-void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & r) {
-    TRACE("push_app_ite", tout << "pushing app...\n";);
-    int ite_arg_idx = has_ite_arg(m, num_args, args);
-    if (ite_arg_idx < 0) {
-        mk_app(decl, num_args, args, r);
-        return;
-    }
-    app * ite               = to_app(args[ite_arg_idx]);
-    expr * c                = ite->get_arg(0);
-    expr * t                = ite->get_arg(1);
-    expr * e                = ite->get_arg(2);
-    expr ** args_prime      = const_cast<expr**>(args);
-    expr * old              = args_prime[ite_arg_idx];
-    args_prime[ite_arg_idx] = t;
-    expr_ref t_new(m);
-    apply(decl, num_args, args_prime, t_new);
-    args_prime[ite_arg_idx] = e;
-    expr_ref e_new(m);
-    apply(decl, num_args, args_prime, e_new);
-    args_prime[ite_arg_idx] = old;
-    expr * new_args[3] = { c, t_new, e_new };
-    mk_app(ite->get_decl(), 3, new_args, r);
-}
-
-/**
-   \brief Default (conservative) implementation. Return true if there one and only one ite-term argument.
-*/
-bool push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * args) {
-    if (m.is_ite(decl))
-        return false;
-    bool found_ite = false;
-    for (unsigned i = 0; i < num_args; i++) {
-        if (m.is_ite(args[i]) && !m.is_bool(args[i])) {
-            if (found_ite) {
-                if (m_conservative)
-                    return false;
-            }
-            else {
-                found_ite = true;
-            }
-        }
-    }
-    CTRACE("push_app_ite", found_ite, tout << "found target for push app ite:\n";
-          tout << decl->get_name();
-          for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m);
-          tout << "\n";);
-    return found_ite;
-}
-
-void push_app_ite::operator()(expr * s, expr_ref & r, proof_ref & p) {
-    expr  * result;
-    proof * result_proof;
-    reduce_core(s);
-    get_cached(s, result, result_proof);
-    r = result;
-    switch (m.proof_mode()) {
-    case PGM_DISABLED:
-        p = m.mk_undef_proof();
-        break;
-    case PGM_COARSE:
-        if (result == s)
-            p = m.mk_reflexivity(s);
-        else
-            p = m.mk_rewrite_star(s, result, 0, 0);
-        break;
-    case PGM_FINE:
-        if (result == s)
-            p = m.mk_reflexivity(s);
-        else
-            p = result_proof;
-        break;
-    }
-}
-
-void push_app_ite::reduce_core(expr * n) {
-    if (!is_cached(n)) {
-        unsigned sz = m_todo.size();
-        m_todo.push_back(n);
-        while (m_todo.size() != sz) {
-            expr * n = m_todo.back();
-            if (is_cached(n))
-                m_todo.pop_back();
-            else if (visit_children(n)) {
-                m_todo.pop_back();
-                reduce1(n);
-            }
-        }
-    }
-}
-
-bool push_app_ite::visit_children(expr * n) {
-    bool visited = true;
-    unsigned j;
-    switch(n->get_kind()) {
-    case AST_VAR:
-        return true;
-    case AST_APP:
-        j = to_app(n)->get_num_args();
-        while (j > 0) {
-            --j;
-            visit(to_app(n)->get_arg(j), visited);
-        }
-        return visited;
-    case AST_QUANTIFIER: 
-        visit(to_quantifier(n)->get_expr(), visited);
-        return visited;
-    default:
-        UNREACHABLE();
-        return true;
-    }
-}
-
-void push_app_ite::reduce1(expr * n) {
-    switch (n->get_kind()) {
-    case AST_VAR:
-        cache_result(n, n, 0);
-        break;
-    case AST_APP:
-        reduce1_app(to_app(n));
-        break;
-    case AST_QUANTIFIER:
-        reduce1_quantifier(to_quantifier(n));
-        break;
-    default:
-        UNREACHABLE();
-    }
-}
-
-void push_app_ite::reduce1_app(app * n) {
-    m_args.reset();
-    
-    func_decl * decl = n->get_decl();
-    proof_ref p1(m);
-    get_args(n, m_args, p1);
-
-    expr_ref r(m);
-    if (is_target(decl, m_args.size(), m_args.c_ptr()))
-        apply(decl, m_args.size(), m_args.c_ptr(), r);
-    else
-        mk_app(decl, m_args.size(), m_args.c_ptr(), r);
-    
-    if (!m.fine_grain_proofs())
-        cache_result(n, r, 0);
-    else {
-        expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr());
-        proof * p;
-        if (n == r)
-            p = 0;
-        else if (r != s) 
-            p = m.mk_transitivity(p1, m.mk_rewrite(s, r));
-        else
-            p = p1;
-        cache_result(n, r, p);
-    }
-}
-
-void push_app_ite::reduce1_quantifier(quantifier * q) {
-    expr *  new_body;
-    proof * new_body_pr;
-    get_cached(q->get_expr(), new_body, new_body_pr);
-    
-    quantifier * new_q = m.update_quantifier(q, new_body);
-    proof *      p     = q == new_q ? 0 : m.mk_quant_intro(q, new_q, new_body_pr);   
-    cache_result(q, new_q, p);
-}
-
-bool ng_push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * args) {
-    bool r = push_app_ite::is_target(decl, num_args, args);
-    if (!r) 
-        return false;
-    for (unsigned i = 0; i < num_args; i++)
-        if (!is_ground(args[i]))
-            return true;
-    return false;
-}
-
-ng_push_app_ite::ng_push_app_ite(simplifier & s, bool conservative):
-    push_app_ite(s, conservative) {
-}
-
 
 /**
    \brief Default (conservative) implementation. Return true if there one and only one ite-term argument.
diff --git a/src/ast/rewriter/push_app_ite.h b/src/ast/rewriter/push_app_ite.h
index 4faf853ea..e6c6b6fb2 100644
--- a/src/ast/rewriter/push_app_ite.h
+++ b/src/ast/rewriter/push_app_ite.h
@@ -20,7 +20,6 @@ Revision History:
 #define PUSH_APP_ITE_H_
 
 #include "ast/ast.h"
-#include "ast/simplifier/simplifier.h"
 #include "ast/rewriter/rewriter.h"
 
 /**
@@ -28,36 +27,6 @@ Revision History:
 
    (f s (ite c t1 t2)) ==> (ite c (f s t1) (f s t2))
 */
-class push_app_ite : public simplifier {
-protected:
-    bool          m_conservative;
-    void apply(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result);
-    virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
-    void reduce_core(expr * n);
-    bool visit_children(expr * n);
-    void reduce1(expr * n);
-    void reduce1_app(app * n);
-    void reduce1_quantifier(quantifier * q);
-
-public:
-    push_app_ite(simplifier & s, bool conservative = true);
-    virtual ~push_app_ite();
-    void operator()(expr * s, expr_ref & r, proof_ref & p);
-};
-
-/**
-   \brief Variation of push_app_ite that applies the transformation on nonground terms only.
-
-   \remark This functor uses the app::is_ground method. This method is not
-   completly precise, for instance, any term containing a quantifier is marked as non ground.
-*/
-class ng_push_app_ite : public push_app_ite {
-protected:
-    virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
-public:
-    ng_push_app_ite(simplifier & s, bool conservative = true);
-    virtual ~ng_push_app_ite() {}
-};
 
 struct push_app_ite_cfg : public default_rewriter_cfg {
     ast_manager& m;

From ebcacaa26d0a14c6d7893a28c154c8683243882b Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Fri, 25 Aug 2017 17:44:33 -0700
Subject: [PATCH 15/74] update new assertions

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/ast.h                     |  36 ++++++++
 src/ast/macros/macro_finder.cpp   | 141 +++++++++++++++++++++++++++++
 src/ast/macros/macro_finder.h     |   3 +
 src/ast/macros/macro_manager.cpp  |   8 ++
 src/ast/macros/macro_manager.h    |   1 +
 src/ast/macros/quasi_macros.cpp   |  62 +++++++++++++
 src/ast/macros/quasi_macros.h     |   3 +
 src/ast/rewriter/CMakeLists.txt   |   1 +
 src/ast/simplifier/CMakeLists.txt |   1 -
 src/ast/simplifier/bv_elim.cpp    |  52 +++++------
 src/ast/simplifier/bv_elim.h      |   4 +-
 src/ast/simplifier/inj_axiom.cpp  | 142 ------------------------------
 src/ast/simplifier/inj_axiom.h    |  27 ------
 src/smt/CMakeLists.txt            |   1 +
 src/smt/asserted_formulas.cpp     |   2 +-
 src/smt/elim_term_ite.cpp         |  18 ++++
 src/smt/elim_term_ite.h           |  28 ++++++
 17 files changed, 331 insertions(+), 199 deletions(-)
 delete mode 100644 src/ast/simplifier/inj_axiom.cpp
 delete mode 100644 src/ast/simplifier/inj_axiom.h

diff --git a/src/ast/ast.h b/src/ast/ast.h
index e6beec7e6..408ca4063 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -2470,6 +2470,42 @@ public:
     void operator()(AST * n) { m_manager.inc_ref(n); }
 };
 
+class justified_expr {
+    ast_manager& m;
+    expr*        m_fml;
+    proof*       m_proof;
+ public:
+ justified_expr(ast_manager& m, expr* fml, proof* p):
+    m(m),
+    m_fml(fml),
+    m_proof(p) {
+        m.inc_ref(fml);
+        m.inc_ref(p);
+    }
+
+    justified_expr& operator=(justified_expr& other) {
+        SASSERT(&m == &other.m);
+        if (this != &other) {
+            m.dec_ref(m_fml);
+            m.dec_ref(m_proof);
+            m_fml = other.get_fml();
+            m_proof = other.get_proof();
+            m.inc_ref(m_fml);
+            m.inc_ref(m_proof);
+        }
+        return *this;
+    }
+    
+    ~justified_expr() {
+        m.dec_ref(m_fml);
+        m.dec_ref(m_proof);
+    }
+    
+    expr* get_fml() const { return m_fml; }
+    proof* get_proof() const { return m_proof; }        
+};
+
+
 #endif /* AST_H_ */
 
 
diff --git a/src/ast/macros/macro_finder.cpp b/src/ast/macros/macro_finder.cpp
index 1d441aee7..9a8e552fc 100644
--- a/src/ast/macros/macro_finder.cpp
+++ b/src/ast/macros/macro_finder.cpp
@@ -111,6 +111,71 @@ bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_ex
     return true;
 }
 
+bool macro_finder::is_arith_macro(expr * n, proof * pr, vector<justified_expr>& new_fmls) {
+    if (!is_quantifier(n) || !to_quantifier(n)->is_forall())
+        return false;
+    expr * body        = to_quantifier(n)->get_expr();
+    unsigned num_decls = to_quantifier(n)->get_num_decls();
+    
+    if (!m_autil.is_le(body) && !m_autil.is_ge(body) && !m_manager.is_eq(body))
+        return false;
+    if (!m_autil.is_add(to_app(body)->get_arg(0)))
+        return false;
+    app_ref head(m_manager);
+    expr_ref def(m_manager);
+    bool inv = false;
+    if (!m_util.is_arith_macro(body, num_decls, head, def, inv))
+        return false;
+    app_ref new_body(m_manager);
+    
+    if (!inv || m_manager.is_eq(body))
+        new_body = m_manager.mk_app(to_app(body)->get_decl(), head, def);
+    else if (m_autil.is_le(body))
+        new_body = m_autil.mk_ge(head, def);
+    else
+        new_body = m_autil.mk_le(head, def);
+
+    quantifier_ref new_q(m_manager); 
+    new_q = m_manager.update_quantifier(to_quantifier(n), new_body);
+    proof * new_pr      = 0;
+    if (m_manager.proofs_enabled()) {
+        proof * rw  = m_manager.mk_rewrite(n, new_q);
+        new_pr      = m_manager.mk_modus_ponens(pr, rw);
+    }
+    if (m_manager.is_eq(body)) {
+        return m_macro_manager.insert(head->get_decl(), new_q, new_pr);
+    }
+    // is ge or le
+    // 
+    TRACE("macro_finder", tout << "is_arith_macro: is_ge or is_le\n";);
+    func_decl * f   = head->get_decl();
+    func_decl * k   = m_manager.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range());
+    app * k_app     = m_manager.mk_app(k, head->get_num_args(), head->get_args());
+    expr_ref_buffer new_rhs_args(m_manager);
+    expr_ref new_rhs2(m_autil.mk_add(def, k_app), m_manager);
+    expr * body1    = m_manager.mk_eq(head, new_rhs2);
+    expr * body2    = m_manager.mk_app(new_body->get_decl(), k_app, m_autil.mk_int(0));
+    quantifier * q1 = m_manager.update_quantifier(new_q, body1);
+    expr * patterns[1] = { m_manager.mk_pattern(k_app) };
+    quantifier * q2 = m_manager.update_quantifier(new_q, 1, patterns, body2);
+    proof* pr1 = 0, *pr2 = 0;
+    if (m_manager.proofs_enabled()) {
+        // new_pr  : new_q
+        // rw  : [rewrite] new_q ~ q1 & q2
+        // mp  : [modus_pones new_pr rw] q1 & q2
+        // pr1 : [and-elim mp] q1
+        // pr2 : [and-elim mp] q2
+        app * q1q2  = m_manager.mk_and(q1,q2);
+        proof * rw  = m_manager.mk_oeq_rewrite(new_q, q1q2);
+        proof * mp  = m_manager.mk_modus_ponens(new_pr, rw);
+        pr1 = m_manager.mk_and_elim(mp, 0);
+        pr2 = m_manager.mk_and_elim(mp, 1);
+    }
+    new_fmls.push_back(justified_expr(m_manager, q1, pr1));
+    new_fmls.push_back(justified_expr(m_manager, q2, pr2));
+    return true;
+}
+
 /**
    n is of the form: (forall (X) (iff (= (f X) t) def[X]))
 
@@ -152,6 +217,34 @@ static void pseudo_predicate_macro2macro(ast_manager & m, app * head, app * t, e
     }
 }
 
+static void pseudo_predicate_macro2macro(ast_manager & m, app * head, app * t, expr * def, quantifier * q, proof * pr, 
+                                         vector<justified_expr>& new_fmls) {
+    func_decl * f = head->get_decl();
+    func_decl * k = m.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range());
+    app * k_app   = m.mk_app(k, head->get_num_args(), head->get_args());
+    app * ite     = m.mk_ite(def, t, k_app);
+    app * body_1  = m.mk_eq(head, ite); 
+    app * body_2  = m.mk_not(m.mk_eq(k_app, t));
+    quantifier * q1 = m.update_quantifier(q, body_1);
+    proof * pr1 = 0, *pr2 = 0;
+    expr * pats[1] = { m.mk_pattern(k_app) };
+    quantifier * q2 = m.update_quantifier(q, 1, pats, body_2); // erase patterns
+    if (m.proofs_enabled()) {
+        // r  : [rewrite] q ~ q1 & q2
+        // pr : q
+        // mp : [modus_pones pr pr1] q1 & q2
+        // pr1 : [and-elim mp] q1
+        // pr2 : [and-elim mp] q2
+        app * q1q2  = m.mk_and(q1,q2);
+        proof * r   = m.mk_oeq_rewrite(q, q1q2);
+        proof * mp  = m.mk_modus_ponens(pr, r);
+        pr1 = m.mk_and_elim(mp, 0);
+        pr2 = m.mk_and_elim(mp, 1);
+    }
+    new_fmls.push_back(justified_expr(m, q1, pr1));
+    new_fmls.push_back(justified_expr(m, q2, pr2));
+}
+
 macro_finder::macro_finder(ast_manager & m, macro_manager & mm):
     m_manager(m),
     m_macro_manager(mm),
@@ -216,3 +309,51 @@ void macro_finder::operator()(unsigned num, expr * const * exprs, proof * const
 }
 
 
+
+bool macro_finder::expand_macros(unsigned num, justified_expr const * fmls, vector<justified_expr>& new_fmls) {
+    TRACE("macro_finder", tout << "starting expand_macros:\n";
+          m_macro_manager.display(tout););
+    bool found_new_macro = false;
+    for (unsigned i = 0; i < num; i++) {
+        expr * n       = fmls[i].get_fml();
+        proof * pr     = m_manager.proofs_enabled() ? fmls[i].get_proof() : 0;
+        expr_ref new_n(m_manager), def(m_manager);
+        proof_ref new_pr(m_manager);
+        m_macro_manager.expand_macros(n, pr, new_n, new_pr);
+        app_ref head(m_manager), t(m_manager);
+        if (is_macro(new_n, head, def) && m_macro_manager.insert(head->get_decl(), to_quantifier(new_n.get()), new_pr)) {
+            TRACE("macro_finder_found", tout << "found new macro: " << head->get_decl()->get_name() << "\n" << new_n << "\n";);
+            found_new_macro = true;
+        }
+        else if (is_arith_macro(new_n, new_pr, new_fmls)) {
+            TRACE("macro_finder_found", tout << "found new arith macro:\n" << new_n << "\n";);
+            found_new_macro = true;
+        }
+        else if (m_util.is_pseudo_predicate_macro(new_n, head, t, def)) { 
+            TRACE("macro_finder_found", tout << "found new pseudo macro:\n" << head << "\n" << t << "\n" << def << "\n";);
+            pseudo_predicate_macro2macro(m_manager, head, t, def, to_quantifier(new_n), new_pr, new_fmls);
+            found_new_macro = true;
+        }
+        else {
+            new_fmls.push_back(justified_expr(m_manager, new_n, new_pr));
+        }
+    }
+    return found_new_macro;
+}
+
+void macro_finder::operator()(unsigned n, justified_expr const* fmls, vector<justified_expr>& new_fmls) {
+    TRACE("macro_finder", tout << "processing macros...\n";);
+    vector<justified_expr> _new_fmls;
+    if (expand_macros(n, fmls, _new_fmls)) {
+        while (true) {
+            vector<justified_expr> old_fmls;
+            _new_fmls.swap(old_fmls);
+            SASSERT(_new_fmls.empty());
+            if (!expand_macros(old_fmls.size(), old_fmls.c_ptr(), _new_fmls))
+                break;
+        }
+    }
+    new_fmls.append(_new_fmls);
+}
+
+
diff --git a/src/ast/macros/macro_finder.h b/src/ast/macros/macro_finder.h
index 5807573ae..2bba07306 100644
--- a/src/ast/macros/macro_finder.h
+++ b/src/ast/macros/macro_finder.h
@@ -38,7 +38,9 @@ class macro_finder {
     macro_util &                m_util;
     arith_util                  m_autil;
     bool expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
+    bool expand_macros(unsigned n, justified_expr const * fmls, vector<justified_expr>& new_fmls);
     bool is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
+    bool is_arith_macro(expr * n, proof * pr, vector<justified_expr>& new_fmls);
 
     bool is_macro(expr * n, app_ref & head, expr_ref & def);
     bool is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t);
@@ -48,6 +50,7 @@ public:
     macro_finder(ast_manager & m, macro_manager & mm);
     ~macro_finder();
     void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
+    void operator()(unsigned n, justified_expr const* fmls, vector<justified_expr>& new_fmls);
 };
 
 #endif /* MACRO_FINDER_H_ */
diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp
index bff1e7dae..7a2642fa3 100644
--- a/src/ast/macros/macro_manager.cpp
+++ b/src/ast/macros/macro_manager.cpp
@@ -152,6 +152,14 @@ void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) {
         for_each_expr(p, visited, exprs[i]);
 }
 
+void macro_manager::mark_forbidden(unsigned n, justified_expr const * exprs) {
+    expr_mark visited;
+    macro_manager_ns::proc p(m_forbidden_set, m_forbidden);
+    for (unsigned i = 0; i < n; i++)
+        for_each_expr(p, visited, exprs[i].get_fml());
+}
+
+
 void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const {
     app * body = to_app(q->get_expr());
     SASSERT(m.is_eq(body) || m.is_iff(body));
diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h
index 71864a699..58fedf666 100644
--- a/src/ast/macros/macro_manager.h
+++ b/src/ast/macros/macro_manager.h
@@ -71,6 +71,7 @@ public:
     void pop_scope(unsigned num_scopes);
     void reset();
     void mark_forbidden(unsigned n, expr * const * exprs);
+    void mark_forbidden(unsigned n, justified_expr const * exprs);
     void mark_forbidden(expr * e) { mark_forbidden(1, &e); }
     bool is_forbidden(func_decl * d) const { return m_forbidden_set.contains(d); }
     obj_hashtable<func_decl> const & get_forbidden_set() const { return m_forbidden_set; }
diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp
index 822532532..527b9656d 100644
--- a/src/ast/macros/quasi_macros.cpp
+++ b/src/ast/macros/quasi_macros.cpp
@@ -293,6 +293,44 @@ bool quasi_macros::find_macros(unsigned n, expr * const * exprs) {
     return res;
 }
 
+bool quasi_macros::find_macros(unsigned n, justified_expr const * exprs) {
+    TRACE("quasi_macros", tout << "Finding quasi-macros in: " << std::endl;
+                          for (unsigned i = 0 ; i < n ; i++) 
+                              tout << i << ": " << mk_pp(exprs[i].get_fml(), m_manager) << std::endl; );
+    bool res = false;
+    m_occurrences.reset();
+    
+
+    // Find out how many non-ground appearences for each uninterpreted function there are    
+    for ( unsigned i = 0 ; i < n ; i++ )
+        find_occurrences(exprs[i].get_fml());
+
+    TRACE("quasi_macros", tout << "Occurrences: " << std::endl;
+    for (occurrences_map::iterator it = m_occurrences.begin(); 
+         it != m_occurrences.end(); 
+         it++)
+        tout << it->m_key->get_name() << ": " << it->m_value << std::endl; );
+   
+    // Find all macros
+    for ( unsigned i = 0 ; i < n ; i++ ) {
+        app_ref a(m_manager);
+        expr_ref t(m_manager);
+        if (is_quasi_macro(exprs[i].get_fml(), a, t)) {
+            quantifier_ref macro(m_manager);
+            quasi_macro_to_macro(to_quantifier(exprs[i].get_fml()), a, t, macro);
+            TRACE("quasi_macros", tout << "Found quasi macro: " << mk_pp(exprs[i].get_fml(), m_manager) << std::endl;
+                                  tout << "Macro: " << mk_pp(macro, m_manager) << std::endl; );
+            proof * pr = 0;
+            if (m_manager.proofs_enabled())
+                pr = m_manager.mk_def_axiom(macro);
+            if (m_macro_manager.insert(a->get_decl(), macro, pr))
+                res = true;
+        }
+    }
+
+    return res;
+}
+
 void quasi_macros::apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {     
     for ( unsigned i = 0 ; i < n ; i++ ) {
         expr_ref r(m_manager), rs(m_manager);
@@ -319,3 +357,27 @@ bool quasi_macros::operator()(unsigned n, expr * const * exprs, proof * const *
         return false;
     }    
 }
+
+void quasi_macros::apply_macros(unsigned n, justified_expr const* fmls, vector<justified_expr>& new_fmls) {     
+    for ( unsigned i = 0 ; i < n ; i++ ) {
+        expr_ref r(m_manager), rs(m_manager);
+        proof_ref pr(m_manager), ps(m_manager);
+        proof * p = m_manager.proofs_enabled() ? fmls[i].get_proof() : 0;
+        m_macro_manager.expand_macros(fmls[i].get_fml(), p, r, pr);
+        m_rewriter(r);
+        new_fmls.push_back(justified_expr(m_manager, r, pr));
+    }
+}
+
+bool quasi_macros::operator()(unsigned n, justified_expr const* fmls, vector<justified_expr>& new_fmls) {
+    if (find_macros(n, fmls)) {
+        apply_macros(n, fmls, new_fmls);
+        return true;
+    } else {
+        // just copy them over
+        for ( unsigned i = 0 ; i < n ; i++ ) {
+            new_fmls.push_back(fmls[i]);
+        }
+        return false;
+    }    
+}
diff --git a/src/ast/macros/quasi_macros.h b/src/ast/macros/quasi_macros.h
index 29efe63c7..7288ac601 100644
--- a/src/ast/macros/quasi_macros.h
+++ b/src/ast/macros/quasi_macros.h
@@ -53,7 +53,9 @@ class quasi_macros {
 
     void find_occurrences(expr * e);
     bool find_macros(unsigned n, expr * const * exprs);
+    bool find_macros(unsigned n, justified_expr const* expr);
     void apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
+    void apply_macros(unsigned n, justified_expr const* fmls, vector<justified_expr>& new_fmls);
 
 public:
     quasi_macros(ast_manager & m, macro_manager & mm);
@@ -63,6 +65,7 @@ public:
        \brief Find pure function macros and apply them.
     */
     bool operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs);    
+    bool operator()(unsigned n, justified_expr const* fmls, vector<justified_expr>& new_fmls);
 };
 
 #endif
diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt
index 72bca53d4..c1b99bcae 100644
--- a/src/ast/rewriter/CMakeLists.txt
+++ b/src/ast/rewriter/CMakeLists.txt
@@ -15,6 +15,7 @@ z3_add_component(rewriter
     expr_safe_replace.cpp
     factor_rewriter.cpp
     fpa_rewriter.cpp
+    inj_axiom.cpp
     label_rewriter.cpp
     mk_simplified_app.cpp
     pb_rewriter.cpp
diff --git a/src/ast/simplifier/CMakeLists.txt b/src/ast/simplifier/CMakeLists.txt
index c5c310c07..b6fe9b1cd 100644
--- a/src/ast/simplifier/CMakeLists.txt
+++ b/src/ast/simplifier/CMakeLists.txt
@@ -12,7 +12,6 @@ z3_add_component(simplifier
     datatype_simplifier_plugin.cpp
     elim_bounds.cpp
     fpa_simplifier_plugin.cpp
-    inj_axiom.cpp
     maximise_ac_sharing.cpp
     poly_simplifier_plugin.cpp
     seq_simplifier_plugin.cpp
diff --git a/src/ast/simplifier/bv_elim.cpp b/src/ast/simplifier/bv_elim.cpp
index 1875e333b..1c0048a07 100644
--- a/src/ast/simplifier/bv_elim.cpp
+++ b/src/ast/simplifier/bv_elim.cpp
@@ -12,16 +12,16 @@ Copyright (c) 2015 Microsoft Corporation
 void bv_elim::elim(quantifier* q, quantifier_ref& r) {
 
     svector<symbol>  names, _names;
-    sort_ref_buffer  sorts(m_manager), _sorts(m_manager);
-    expr_ref_buffer  pats(m_manager);
-    expr_ref_buffer  no_pats(m_manager);
-    expr_ref_buffer  subst_map(m_manager), _subst_map(m_manager);
-    var_subst        subst(m_manager);
-    bv_util          bv(m_manager);
-    expr_ref         new_body(m_manager);
+    sort_ref_buffer  sorts(m), _sorts(m);
+    expr_ref_buffer  pats(m);
+    expr_ref_buffer  no_pats(m);
+    expr_ref_buffer  subst_map(m), _subst_map(m);
+    var_subst        subst(m);
+    bv_util          bv(m);
+    expr_ref         new_body(m);
     expr*            old_body = q->get_expr();
     unsigned num_decls = q->get_num_decls();
-    family_id bfid = m_manager.mk_family_id("bv");
+    family_id bfid = m.mk_family_id("bv");
 
     //
     // Traverse sequence of bound variables to eliminate
@@ -37,23 +37,23 @@ void bv_elim::elim(quantifier* q, quantifier_ref& r) {
         if (bv.is_bv_sort(s)) {
             // convert n-bit bit-vector variable into sequence of n-Booleans.
             unsigned num_bits = bv.get_bv_size(s);
-            expr_ref_buffer args(m_manager);
-            expr_ref bv(m_manager);
+            expr_ref_buffer args(m);
+            expr_ref bv(m);
             for (unsigned j = 0; j < num_bits; ++j) {
                 std::ostringstream new_name;
                 new_name << nm.str();
                 new_name << "_";
                 new_name << j;
-                var* v = m_manager.mk_var(var_idx++, m_manager.mk_bool_sort());                
+                var* v = m.mk_var(var_idx++, m.mk_bool_sort());                
                 args.push_back(v);
-                _sorts.push_back(m_manager.mk_bool_sort());
+                _sorts.push_back(m.mk_bool_sort());
                 _names.push_back(symbol(new_name.str().c_str()));
             }
-            bv = m_manager.mk_app(bfid, OP_MKBV, 0, 0, args.size(), args.c_ptr());
+            bv = m.mk_app(bfid, OP_MKBV, 0, 0, args.size(), args.c_ptr());
             _subst_map.push_back(bv.get());
         }
         else {
-            _subst_map.push_back(m_manager.mk_var(var_idx++, s));
+            _subst_map.push_back(m.mk_var(var_idx++, s));
             _sorts.push_back(s);
             _names.push_back(nm);
         }
@@ -78,26 +78,26 @@ void bv_elim::elim(quantifier* q, quantifier_ref& r) {
     subst(old_body, sub_size, sub, new_body);
 
     for (unsigned j = 0; j < q->get_num_patterns(); j++) {
-        expr_ref pat(m_manager);        
+        expr_ref pat(m);        
         subst(q->get_pattern(j), sub_size, sub, pat);
         pats.push_back(pat);
     }
     for (unsigned j = 0; j < q->get_num_no_patterns(); j++) {
-        expr_ref nopat(m_manager);
+        expr_ref nopat(m);
         subst(q->get_no_pattern(j), sub_size, sub, nopat);
         no_pats.push_back(nopat);
     }
 
-    r = m_manager.mk_quantifier(true, 
-                                names.size(),
-                                sorts.c_ptr(),
-                                names.c_ptr(),
-                                new_body.get(),
-                                q->get_weight(),
-                                q->get_qid(),
-                                q->get_skid(),
-                                pats.size(), pats.c_ptr(),
-                                no_pats.size(), no_pats.c_ptr());
+    r = m.mk_quantifier(true, 
+                        names.size(),
+                        sorts.c_ptr(),
+                        names.c_ptr(),
+                        new_body.get(),
+                        q->get_weight(),
+                        q->get_qid(),
+                        q->get_skid(),
+                        pats.size(), pats.c_ptr(),
+                        no_pats.size(), no_pats.c_ptr());
 }
 
 bool bv_elim_star::visit_quantifier(quantifier* q) {
diff --git a/src/ast/simplifier/bv_elim.h b/src/ast/simplifier/bv_elim.h
index 2b8a4778a..c027b1a9e 100644
--- a/src/ast/simplifier/bv_elim.h
+++ b/src/ast/simplifier/bv_elim.h
@@ -24,9 +24,9 @@ Revision History:
 #include "ast/simplifier/simplifier.h"
 
 class bv_elim {
-    ast_manager& m_manager;
+    ast_manager& m;
 public:
-    bv_elim(ast_manager& m) : m_manager(m) {};
+    bv_elim(ast_manager& m) : m(m) {};
 
     void elim(quantifier* q, quantifier_ref& r);
 };
diff --git a/src/ast/simplifier/inj_axiom.cpp b/src/ast/simplifier/inj_axiom.cpp
deleted file mode 100644
index 2aa828ffa..000000000
--- a/src/ast/simplifier/inj_axiom.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    inj_axiom.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-06-23.
-
-Revision History:
-
---*/
-#include "ast/simplifier/inj_axiom.h"
-#include "ast/ast_pp.h"
-#include "ast/ast_ll_pp.h"
-#include "ast/has_free_vars.h"
-#include "ast/well_sorted.h"
-
-/**
-   \brief Little HACK for simplifying injectivity axioms
-   
-   \remark It is not covering all possible cases.
-*/
-bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) {
-    expr * n = q->get_expr();
-    if (q->is_forall() && m.is_or(n) && to_app(n)->get_num_args() == 2) {
-        expr * arg1 = to_app(n)->get_arg(0);
-        expr * arg2 = to_app(n)->get_arg(1);
-        if (m.is_not(arg2)) 
-            std::swap(arg1, arg2);
-        if (m.is_not(arg1) && 
-            m.is_eq(to_app(arg1)->get_arg(0)) && 
-            m.is_eq(arg2)) {
-            expr * app1 = to_app(to_app(arg1)->get_arg(0))->get_arg(0);
-            expr * app2 = to_app(to_app(arg1)->get_arg(0))->get_arg(1);
-            expr * var1 = to_app(arg2)->get_arg(0);
-            expr * var2 = to_app(arg2)->get_arg(1);
-            if (is_app(app1) &&
-                is_app(app2) && 
-                to_app(app1)->get_decl() == to_app(app2)->get_decl() &&
-                to_app(app1)->get_num_args() == to_app(app2)->get_num_args() &&
-                to_app(app1)->get_family_id() == null_family_id &&
-                to_app(app1)->get_num_args() > 0 &&
-                is_var(var1) && 
-                is_var(var2) && 
-                var1 != var2) {
-                app * f1          = to_app(app1);
-                app * f2          = to_app(app2);
-                bool found_vars   = false;
-                unsigned num      = f1->get_num_args();
-                unsigned idx      = UINT_MAX;
-                unsigned num_vars = 1;
-                for (unsigned i = 0; i < num; i++) {
-                    expr  * c1 = f1->get_arg(i);
-                    expr  * c2 = f2->get_arg(i);
-                    if (!is_var(c1) && !is_uninterp_const(c1))
-                        return false;
-                    if ((c1 == var1 && c2 == var2) || (c1 == var2 && c2 == var1)) {
-                        if (found_vars)
-                            return false;
-                        found_vars = true;
-                        idx = i;
-                    }
-                    else if (c1 == c2 && c1 != var1 && c1 != var2) {
-                        if (is_var(c1)) {
-                            ++num_vars;
-                        }
-                    }
-                    else {
-                        return false;
-                    }
-                }
-                if (found_vars && !has_free_vars(q)) {
-                    TRACE("inj_axiom", 
-                          tout << "Cadidate for simplification:\n" << mk_ll_pp(q, m) << mk_pp(app1, m) << "\n" << mk_pp(app2, m) << "\n" <<
-                          mk_pp(var1, m) << "\n" << mk_pp(var2, m) << "\nnum_vars: " << num_vars << "\n";);
-                    // Building new (optimized) axiom
-                    func_decl * decl      = f1->get_decl();
-                    unsigned var_idx      = 0;
-                    ptr_buffer<expr> f_args, inv_vars;
-                    ptr_buffer<sort> decls;
-                    buffer<symbol>   names;
-                    
-                    expr * var            = 0;
-                    for (unsigned i = 0; i < num; i++) {
-                        expr * c = f1->get_arg(i);
-                        if (is_var(c)) {
-                            names.push_back(symbol(i));
-                            sort * s = decl->get_domain(i);
-                            decls.push_back(s);
-                            expr * new_c = m.mk_var(var_idx, s);
-                            var_idx++;
-                            f_args.push_back(new_c);
-                            if (i == idx) {
-                                var = new_c;
-                            }
-                            else {
-                                inv_vars.push_back(new_c);
-                            }
-                        }
-                        else {
-                            SASSERT(is_uninterp_const(c));
-                            f_args.push_back(c);
-                        }
-                    }
-                    SASSERT(var != 0);
-                    app * f    = m.mk_app(decl, f_args.size(), f_args.c_ptr());
-
-                    ptr_vector<sort>  domain;
-                    inv_vars.push_back(f);
-                    for (unsigned i = 0; i < inv_vars.size(); ++i) {
-                        domain.push_back(m.get_sort(inv_vars[i]));
-                    }
-                    sort * d              = decl->get_domain(idx);
-                    func_decl * inv_decl  = m.mk_fresh_func_decl("inj", domain.size(), domain.c_ptr(), d);
-                    
-                    expr * proj = m.mk_app(inv_decl, inv_vars.size(), inv_vars.c_ptr());
-                    expr * eq   = m.mk_eq(proj, var);
-                    expr * p    = m.mk_pattern(f);
-                    
-                    // decls are in the wrong order...
-                    // Remark: the sort of the var 0 must be in the last position.
-                    std::reverse(decls.begin(), decls.end());
-                    
-                    result = m.mk_forall(decls.size(), decls.c_ptr(), names.c_ptr(), eq,
-                                         0, symbol(), symbol(), 1, &p);
-                    TRACE("inj_axiom", tout << "new axiom:\n" << mk_pp(result, m) << "\n";);
-                    SASSERT(is_well_sorted(m, result));
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
diff --git a/src/ast/simplifier/inj_axiom.h b/src/ast/simplifier/inj_axiom.h
deleted file mode 100644
index a6df16515..000000000
--- a/src/ast/simplifier/inj_axiom.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    inj_axiom.h
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-06-23.
-
-Revision History:
-
---*/
-#ifndef INJ_AXIOM_H_
-#define INJ_AXIOM_H_
-
-#include "ast/ast.h"
-
-bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result);
-
-#endif /* INJ_AXIOM_H_ */
-
diff --git a/src/smt/CMakeLists.txt b/src/smt/CMakeLists.txt
index 41890dd05..b117db89b 100644
--- a/src/smt/CMakeLists.txt
+++ b/src/smt/CMakeLists.txt
@@ -3,6 +3,7 @@ z3_add_component(smt
     arith_eq_adapter.cpp
     arith_eq_solver.cpp
     asserted_formulas.cpp
+    asserted_formulas_new.cpp
     cached_var_subst.cpp
     cost_evaluator.cpp
     dyn_ack.cpp
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index 6f1c4cfcc..8b25e9263 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -24,6 +24,7 @@ Revision History:
 #include "ast/rewriter/rewriter_def.h"
 #include "ast/rewriter/pull_ite_tree.h"
 #include "ast/rewriter/push_app_ite.h"
+#include "ast/rewriter/inj_axiom.h"
 #include "ast/simplifier/arith_simplifier_plugin.h"
 #include "ast/simplifier/array_simplifier_plugin.h"
 #include "ast/simplifier/datatype_simplifier_plugin.h"
@@ -31,7 +32,6 @@ Revision History:
 #include "ast/simplifier/seq_simplifier_plugin.h"
 #include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/simplifier/bv_elim.h"
-#include "ast/simplifier/inj_axiom.h"
 #include "ast/simplifier/elim_bounds.h"
 #include "ast/simplifier/bit2int.h"
 #include "ast/normal_forms/pull_quant.h"
diff --git a/src/smt/elim_term_ite.cpp b/src/smt/elim_term_ite.cpp
index b750e6bf5..d9cfac775 100644
--- a/src/smt/elim_term_ite.cpp
+++ b/src/smt/elim_term_ite.cpp
@@ -157,4 +157,22 @@ void elim_term_ite::reduce1_quantifier(quantifier * q) {
 }
 
 
+br_status elim_term_ite_cfg::reduce_app(func_decl* f, unsigned n, expr * const* args, expr_ref& result, proof_ref& result_pr) {
+    if (!m.is_term_ite(f)) {
+        return BR_FAILED;
+    }
+
+    expr_ref   new_def(m);
+    proof_ref  new_def_pr(m);
+    app_ref   r(m.mk_app(f, n, args), m);
+    app_ref    new_r(m);
+    if (!m_defined_names.mk_name(r, new_def, new_def_pr, new_r, result_pr)) {
+        return BR_FAILED;
+    }
+    result = new_r;
+     
+    CTRACE("elim_term_ite_bug", new_def.get() == 0, tout << mk_ismt2_pp(r, m) << "\n";);
+    m_new_defs.push_back(justified_expr(m, new_def, new_def_pr));
+    return BR_DONE;
+}
 
diff --git a/src/smt/elim_term_ite.h b/src/smt/elim_term_ite.h
index 2b9c66a64..94e5e0346 100644
--- a/src/smt/elim_term_ite.h
+++ b/src/smt/elim_term_ite.h
@@ -21,6 +21,7 @@ Revision History:
 
 #include "ast/simplifier/simplifier.h"
 #include "ast/normal_forms/defined_names.h"
+#include "ast/rewriter/rewriter.h"
 
 class elim_term_ite : public simplifier {
     defined_names &    m_defined_names;
@@ -46,5 +47,32 @@ public:
                     );
 };
 
+
+
+class elim_term_ite_cfg : public default_rewriter_cfg {
+    ast_manager&           m;
+    defined_names &        m_defined_names;
+    vector<justified_expr> m_new_defs;
+public:
+    elim_term_ite_cfg(ast_manager & m, defined_names & d): m(m), m_defined_names(d) { 
+        // TBD enable_ac_support(false);
+    }
+    virtual ~elim_term_ite_cfg() {}
+    vector<justified_expr> const& new_defs() const { return m_new_defs; }
+    br_status reduce_app(func_decl* f, unsigned n, expr *const* args, expr_ref& result, proof_ref& result_pr);
+};
+
+class elim_term_ite_rw : public rewriter_tpl<elim_term_ite_cfg> {
+    elim_term_ite_cfg m_cfg;
+public:
+    elim_term_ite_rw(ast_manager& m, defined_names & dn):
+        rewriter_tpl<elim_term_ite_cfg>(m, m.proofs_enabled(), m_cfg),
+        m_cfg(m, dn) 
+    {}
+    vector<justified_expr> const& new_defs() const { return m_cfg.new_defs(); }
+};
+
+
+
 #endif /* ELIM_TERM_ITE_H_ */
 

From 9438ff848fcaa149d0b58639f75c043d2730049f Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Fri, 25 Aug 2017 17:44:57 -0700
Subject: [PATCH 16/74] moved files

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/inj_axiom.cpp | 139 +++++++++++++++++++++++++++++++++
 src/ast/rewriter/inj_axiom.h   |  27 +++++++
 2 files changed, 166 insertions(+)
 create mode 100644 src/ast/rewriter/inj_axiom.cpp
 create mode 100644 src/ast/rewriter/inj_axiom.h

diff --git a/src/ast/rewriter/inj_axiom.cpp b/src/ast/rewriter/inj_axiom.cpp
new file mode 100644
index 000000000..d322f3228
--- /dev/null
+++ b/src/ast/rewriter/inj_axiom.cpp
@@ -0,0 +1,139 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    inj_axiom.cpp
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-06-23.
+
+Revision History:
+
+--*/
+#include "ast/rewriter/inj_axiom.h"
+#include "ast/ast_pp.h"
+#include "ast/ast_ll_pp.h"
+#include "ast/has_free_vars.h"
+#include "ast/well_sorted.h"
+
+/**
+   \brief Little HACK for simplifying injectivity axioms
+   
+   \remark It is not covering all possible cases.
+*/
+bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) {
+    expr * n = q->get_expr();
+    expr* arg1 = 0, * arg2 = 0, *narg = 0;
+    expr* app1 = 0, * app2 = 0;
+    expr* var1 = 0, * var2 = 0;
+    if (q->is_forall() && m.is_or(n, arg1, arg2)) {
+        if (m.is_not(arg2)) 
+            std::swap(arg1, arg2);
+        if (m.is_not(arg1, narg) && 
+            m.is_eq(narg, app1, app2) && 
+            m.is_eq(arg2, var1, var2)) {
+            if (is_app(app1) &&
+                is_app(app2) && 
+                to_app(app1)->get_decl() == to_app(app2)->get_decl() &&
+                to_app(app1)->get_num_args() == to_app(app2)->get_num_args() &&
+                to_app(app1)->get_family_id() == null_family_id &&
+                to_app(app1)->get_num_args() > 0 &&
+                is_var(var1) && 
+                is_var(var2) && 
+                var1 != var2) {
+                app * f1          = to_app(app1);
+                app * f2          = to_app(app2);
+                bool found_vars   = false;
+                unsigned num      = f1->get_num_args();
+                unsigned idx      = UINT_MAX;
+                unsigned num_vars = 1;
+                for (unsigned i = 0; i < num; i++) {
+                    expr  * c1 = f1->get_arg(i);
+                    expr  * c2 = f2->get_arg(i);
+                    if (!is_var(c1) && !is_uninterp_const(c1))
+                        return false;
+                    if ((c1 == var1 && c2 == var2) || (c1 == var2 && c2 == var1)) {
+                        if (found_vars)
+                            return false;
+                        found_vars = true;
+                        idx = i;
+                    }
+                    else if (c1 == c2 && c1 != var1 && c1 != var2) {
+                        if (is_var(c1)) {
+                            ++num_vars;
+                        }
+                    }
+                    else {
+                        return false;
+                    }
+                }
+                if (found_vars && !has_free_vars(q)) {
+                    TRACE("inj_axiom", 
+                          tout << "Cadidate for simplification:\n" << mk_ll_pp(q, m) << mk_pp(app1, m) << "\n" << mk_pp(app2, m) << "\n" <<
+                          mk_pp(var1, m) << "\n" << mk_pp(var2, m) << "\nnum_vars: " << num_vars << "\n";);
+                    // Building new (optimized) axiom
+                    func_decl * decl      = f1->get_decl();
+                    unsigned var_idx      = 0;
+                    ptr_buffer<expr> f_args, inv_vars;
+                    ptr_buffer<sort> decls;
+                    buffer<symbol>   names;
+                    
+                    expr * var            = 0;
+                    for (unsigned i = 0; i < num; i++) {
+                        expr * c = f1->get_arg(i);
+                        if (is_var(c)) {
+                            names.push_back(symbol(i));
+                            sort * s = decl->get_domain(i);
+                            decls.push_back(s);
+                            expr * new_c = m.mk_var(var_idx, s);
+                            var_idx++;
+                            f_args.push_back(new_c);
+                            if (i == idx) {
+                                var = new_c;
+                            }
+                            else {
+                                inv_vars.push_back(new_c);
+                            }
+                        }
+                        else {
+                            SASSERT(is_uninterp_const(c));
+                            f_args.push_back(c);
+                        }
+                    }
+                    SASSERT(var != 0);
+                    app * f    = m.mk_app(decl, f_args.size(), f_args.c_ptr());
+
+                    ptr_vector<sort>  domain;
+                    inv_vars.push_back(f);
+                    for (unsigned i = 0; i < inv_vars.size(); ++i) {
+                        domain.push_back(m.get_sort(inv_vars[i]));
+                    }
+                    sort * d              = decl->get_domain(idx);
+                    func_decl * inv_decl  = m.mk_fresh_func_decl("inj", domain.size(), domain.c_ptr(), d);
+                    
+                    expr * proj = m.mk_app(inv_decl, inv_vars.size(), inv_vars.c_ptr());
+                    expr * eq   = m.mk_eq(proj, var);
+                    expr * p    = m.mk_pattern(f);
+                    
+                    // decls are in the wrong order...
+                    // Remark: the sort of the var 0 must be in the last position.
+                    std::reverse(decls.begin(), decls.end());
+                    
+                    result = m.mk_forall(decls.size(), decls.c_ptr(), names.c_ptr(), eq,
+                                         0, symbol(), symbol(), 1, &p);
+                    TRACE("inj_axiom", tout << "new axiom:\n" << mk_pp(result, m) << "\n";);
+                    SASSERT(is_well_sorted(m, result));
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
diff --git a/src/ast/rewriter/inj_axiom.h b/src/ast/rewriter/inj_axiom.h
new file mode 100644
index 000000000..a6df16515
--- /dev/null
+++ b/src/ast/rewriter/inj_axiom.h
@@ -0,0 +1,27 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    inj_axiom.h
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-06-23.
+
+Revision History:
+
+--*/
+#ifndef INJ_AXIOM_H_
+#define INJ_AXIOM_H_
+
+#include "ast/ast.h"
+
+bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result);
+
+#endif /* INJ_AXIOM_H_ */
+

From ac0bb6a3d062095b5538a870c4e7ec26126932b7 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Fri, 25 Aug 2017 23:56:09 -0700
Subject: [PATCH 17/74] remove simplify dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/expr_substitution.h                  | 33 ++++++++++++
 src/ast/rewriter/CMakeLists.txt              |  4 ++
 src/ast/{simplifier => rewriter}/bit2int.cpp | 55 ++++++++++----------
 src/ast/{simplifier => rewriter}/bit2int.h   |  7 +--
 src/ast/rewriter/bv_rewriter.h               | 39 ++++++++++++--
 src/ast/simplifier/CMakeLists.txt            |  1 -
 src/smt/asserted_formulas.cpp                |  4 +-
 src/smt/asserted_formulas.h                  |  2 +-
 src/smt/elim_term_ite.h                      |  2 +
 src/smt/params/preprocessor_params.cpp       |  2 +-
 src/smt/params/preprocessor_params.h         |  4 +-
 11 files changed, 108 insertions(+), 45 deletions(-)
 rename src/ast/{simplifier => rewriter}/bit2int.cpp (87%)
 rename src/ast/{simplifier => rewriter}/bit2int.h (90%)

diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h
index d209ab6b4..924481dd8 100644
--- a/src/ast/expr_substitution.h
+++ b/src/ast/expr_substitution.h
@@ -52,4 +52,37 @@ public:
     void cleanup();
 };
 
+class scoped_expr_substitution {
+    expr_substitution& m_subst;
+    expr_ref_vector    m_trail;
+    unsigned_vector    m_trail_lim;
+public:
+
+    scoped_expr_substitution(expr_substitution& s): m_subst(s), m_trail(s.m()) {}
+    ~scoped_expr_substitution() {}
+
+    void insert(expr * s, expr * def, proof * def_pr = 0, expr_dependency * def_dep = 0) { 
+        if (!m_subst.contains(s)) {
+            m_subst.insert(s, def, def_pr, def_dep); 
+            m_trail.push_back(s);
+        }
+    }
+    void reset() { m_subst.reset(); m_trail.reset(); m_trail_lim.reset(); }
+    void push() { m_trail_lim.push_back(m_trail.size()); }
+    void pop(unsigned n) { 
+        if (n > 0) {
+            unsigned new_sz = m_trail_lim.size() - n;
+            unsigned old_sz = m_trail_lim[new_sz];
+            for (unsigned i = old_sz; i < m_trail.size(); ++i) m_subst.erase(m_trail[i].get());  
+            m_trail.resize(old_sz); 
+            m_trail_lim.resize(new_sz); 
+        }
+    }
+    bool empty() const { return m_subst.empty(); }
+    bool find(expr * s, expr * & def, proof * & def_pr) { return m_subst.find(s, def, def_pr); }
+    bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep) { return m_subst.find(s, def, def_pr, def_dep); }
+    bool contains(expr * s) { return m_subst.contains(s); }
+    void cleanup() { m_subst.cleanup(); }
+};
+
 #endif
diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt
index c1b99bcae..804fbcbed 100644
--- a/src/ast/rewriter/CMakeLists.txt
+++ b/src/ast/rewriter/CMakeLists.txt
@@ -3,13 +3,16 @@ z3_add_component(rewriter
     arith_rewriter.cpp
     array_rewriter.cpp
     ast_counter.cpp
+    bit2int.cpp
     bool_rewriter.cpp
     bv_bounds.cpp
+    bv_elim2.cpp
     bv_rewriter.cpp
     datatype_rewriter.cpp
     der.cpp
     distribute_forall.cpp
     dl_rewriter.cpp
+    elim_bounds2.cpp
     enum2bv_rewriter.cpp
     expr_replacer.cpp
     expr_safe_replace.cpp
@@ -17,6 +20,7 @@ z3_add_component(rewriter
     fpa_rewriter.cpp
     inj_axiom.cpp
     label_rewriter.cpp
+    maximize_ac_sharing.cpp
     mk_simplified_app.cpp
     pb_rewriter.cpp
     pb2bv_rewriter.cpp
diff --git a/src/ast/simplifier/bit2int.cpp b/src/ast/rewriter/bit2int.cpp
similarity index 87%
rename from src/ast/simplifier/bit2int.cpp
rename to src/ast/rewriter/bit2int.cpp
index 6f7dd1cbe..257740412 100644
--- a/src/ast/simplifier/bit2int.cpp
+++ b/src/ast/rewriter/bit2int.cpp
@@ -19,15 +19,16 @@ Revision History:
 
 --*/
 
-#include "ast/simplifier/bit2int.h"
 #include "ast/ast_pp.h"
 #include "ast/ast_ll_pp.h"
 #include "ast/for_each_ast.h"
+#include "ast/rewriter/bit2int.h"
+
 
 #define CHECK(_x_) if (!(_x_)) { UNREACHABLE(); }
 
 bit2int::bit2int(ast_manager & m) : 
-    m_manager(m), m_bv_util(m), m_arith_util(m), m_cache(m), m_bit0(m) {
+    m_manager(m), m_bv_util(m), m_rewriter(m), m_arith_util(m), m_cache(m), m_bit0(m) {
     m_bit0     = m_bv_util.mk_numeral(0,1);
 }
 
@@ -67,7 +68,7 @@ unsigned bit2int::get_numeral_bits(numeral const& k) {
 void bit2int::align_size(expr* e, unsigned sz, expr_ref& result) {
     unsigned sz1 = m_bv_util.get_bv_size(e);
     SASSERT(sz1 <= sz);
-    m_bv_simplifier->mk_zeroext(sz-sz1, e, result);    
+    result = m_rewriter.mk_zero_extend(sz-sz1, e);
 }
 
 void bit2int::align_sizes(expr_ref& a, expr_ref& b) {
@@ -75,11 +76,11 @@ void bit2int::align_sizes(expr_ref& a, expr_ref& b) {
     unsigned sz2 = m_bv_util.get_bv_size(b);
     expr_ref tmp(m_manager);
     if (sz1 > sz2) {
-        m_bv_simplifier->mk_zeroext(sz1-sz2, b, tmp);    
+        tmp = m_rewriter.mk_zero_extend(sz1-sz2, b);    
         b = tmp;
     }
     else if (sz2 > sz1) {
-        m_bv_simplifier->mk_zeroext(sz2-sz1, a, tmp);    
+        tmp = m_rewriter.mk_zero_extend(sz2-sz1, a);    
         a = tmp;
     }
 }
@@ -123,11 +124,11 @@ bool bit2int::mk_add(expr* e1, expr* e2, expr_ref& result) {
             return true;
         }
         align_sizes(tmp1, tmp2);
-        m_bv_simplifier->mk_zeroext(1, tmp1, tmp1);
-        m_bv_simplifier->mk_zeroext(1, tmp2, tmp2);
+        tmp1 = m_rewriter.mk_zero_extend(1, tmp1);
+        tmp2 = m_rewriter.mk_zero_extend(1, tmp2);
         SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2));
-        m_bv_simplifier->mk_add(tmp1, tmp2, tmp3);
-        m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
+        tmp3 = m_rewriter.mk_bv_add(tmp1, tmp2);
+        result = m_rewriter.mk_bv2int(tmp3);
         return true;
     }
     return false;
@@ -143,14 +144,14 @@ bool bit2int::mk_comp(eq_type ty, expr* e1, expr* e2, expr_ref& result) {
         SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2));
         switch(ty) {
         case lt:
-            m_bv_simplifier->mk_leq_core(false, tmp2, tmp1, tmp3);
+            tmp3 = m_rewriter.mk_ule(tmp2, tmp1);
             result = m_manager.mk_not(tmp3);
             break;
         case le:
-            m_bv_simplifier->mk_leq_core(false,tmp1, tmp2, result);
+            result = m_rewriter.mk_ule(tmp1, tmp2);
             break;
         case eq:
-            result = m_manager.mk_eq(tmp1,tmp2);
+            result = m_manager.mk_eq(tmp1, tmp2);
             break;
         }
         return true;
@@ -167,12 +168,12 @@ bool bit2int::mk_mul(expr* e1, expr* e2, expr_ref& result) {
     if (extract_bv(e1, sz1, sign1, tmp1) &&
         extract_bv(e2, sz2, sign2, tmp2)) {
         align_sizes(tmp1, tmp2);
-        m_bv_simplifier->mk_zeroext(m_bv_util.get_bv_size(tmp1), tmp1, tmp1);
-        m_bv_simplifier->mk_zeroext(m_bv_util.get_bv_size(tmp2), tmp2, tmp2);
+        tmp1 = m_rewriter.mk_zero_extend(m_bv_util.get_bv_size(tmp1), tmp1);
+        tmp2 = m_rewriter.mk_zero_extend(m_bv_util.get_bv_size(tmp2), tmp2);
 
         SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2));
-        m_bv_simplifier->mk_mul(tmp1, tmp2, tmp3);
-        m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
+        tmp3 = m_rewriter.mk_bv_mul(tmp1, tmp2);
+        result = m_rewriter.mk_bv2int(tmp3);
         if (sign1 != sign2) {
             result = m_arith_util.mk_uminus(result);
         }
@@ -187,8 +188,7 @@ bool bit2int::is_bv_poly(expr* n, expr_ref& pos, expr_ref& neg) {
     numeral k;
     bool is_int;
     todo.push_back(n);
-    m_bv_simplifier->mk_bv2int(m_bit0, m_arith_util.mk_int(), pos);
-    m_bv_simplifier->mk_bv2int(m_bit0, m_arith_util.mk_int(), neg);
+    neg = pos = m_rewriter.mk_bv2int(m_bit0);
         
     while (!todo.empty()) {
         n = todo.back();
@@ -372,8 +372,8 @@ void bit2int::visit(app* n) {
             tmp1 = tmp_p;
             tmp2 = e2bv;
             align_sizes(tmp1, tmp2);
-            m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3);
-            m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
+            tmp3 = m_rewriter.mk_bv_urem(tmp1, tmp2);
+            result = m_rewriter.mk_bv2int(tmp3);
             cache_result(n, result);
             return;
         }
@@ -382,25 +382,24 @@ void bit2int::visit(app* n) {
         tmp1 = tmp_n;
         tmp2 = e2bv;
         align_sizes(tmp1, tmp2);
-        m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3);
+        tmp3 = m_rewriter.mk_bv_urem(tmp1, tmp2);
         // e2 - (neg1 mod e2)
         tmp1 = e2bv;
         tmp2 = tmp3;
         align_sizes(tmp1, tmp2);
-        m_bv_simplifier->mk_sub(tmp1, tmp2, tmp3);
+        tmp3 = m_rewriter.mk_bv_sub(tmp1, tmp2);
         // pos1 + (e2 - (neg1 mod e2))
         tmp1 = tmp_p;
         tmp2 = tmp3;
         align_sizes(tmp1, tmp2);
-        m_bv_simplifier->mk_zeroext(1, tmp1, tmp_p);
-        m_bv_simplifier->mk_zeroext(1, tmp2, tmp_n);
-        m_bv_simplifier->mk_add(tmp_p, tmp_n, tmp1);
+        tmp_p = m_rewriter.mk_zero_extend(1, tmp1);
+        tmp_n = m_rewriter.mk_zero_extend(1, tmp2);
+        tmp1 = m_rewriter.mk_bv_add(tmp_p, tmp_n);
         // (pos1 + (e2 - (neg1 mod e2))) mod e2
         tmp2 = e2bv;
         align_sizes(tmp1, tmp2);
-        m_bv_simplifier->mk_bv_urem(tmp1, tmp2, tmp3);
-
-        m_bv_simplifier->mk_bv2int(tmp3, m_arith_util.mk_int(), result);
+        tmp3 = m_rewriter.mk_bv_urem(tmp1, tmp2);
+        result = m_rewriter.mk_bv2int(tmp3);
 
         cache_result(n, result);
     }
diff --git a/src/ast/simplifier/bit2int.h b/src/ast/rewriter/bit2int.h
similarity index 90%
rename from src/ast/simplifier/bit2int.h
rename to src/ast/rewriter/bit2int.h
index 84ae1f4a4..fe15d1ec5 100644
--- a/src/ast/simplifier/bit2int.h
+++ b/src/ast/rewriter/bit2int.h
@@ -22,9 +22,7 @@ Revision History:
 #include "ast/bv_decl_plugin.h"
 #include "ast/arith_decl_plugin.h"
 #include "ast/act_cache.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
-
+#include "ast/rewriter/bv_rewriter.h"
 
 class bit2int {
 protected:
@@ -60,8 +58,8 @@ protected:
     typedef act_cache expr_map;
     ast_manager &             m_manager;
     bv_util                   m_bv_util;
+    bv_rewriter               m_rewriter;
     arith_util                m_arith_util;
-    bv_simplifier_plugin    * m_bv_simplifier;
 
     expr_map                  m_cache;      // map: ast  -> ast    ref. counters are incremented when inserted here.
     expr_ref                  m_bit0;
@@ -88,7 +86,6 @@ protected:
 
 public:
     bit2int(ast_manager & m);
-    void set_bv_simplifier(bv_simplifier_plugin * p) { m_bv_simplifier = p; }
     void operator()(expr * m, expr_ref & result, proof_ref& p);
 };
 
diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h
index 1109251e0..45bd6c264 100644
--- a/src/ast/rewriter/bv_rewriter.h
+++ b/src/ast/rewriter/bv_rewriter.h
@@ -98,11 +98,10 @@ class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
     br_status mk_bv_rotate_right(unsigned n, expr * arg, expr_ref & result);
     br_status mk_bv_ext_rotate_left(expr * arg1, expr * arg2, expr_ref & result);
     br_status mk_bv_ext_rotate_right(expr * arg1, expr * arg2, expr_ref & result);
+    br_status mk_bv_add(expr* a, expr* b, expr_ref& result) { expr* args[2] = { a, b }; return mk_bv_add(2, args, result); }
+    br_status mk_bv_sub(expr* a, expr* b, expr_ref& result) { expr* args[2] = { a, b }; return mk_sub(2, args, result); }
+    br_status mk_bv_mul(expr* a, expr* b, expr_ref& result) { expr* args[2] = { a, b }; return mk_bv_mul(2, args, result); }
     br_status mk_bv_add(unsigned num_args, expr * const * args, expr_ref & result);
-    br_status mk_bv_add(expr * arg1, expr * arg2, expr_ref & result) {
-        expr * args[2] = { arg1, arg2 };
-        return mk_bv_add(2, args, result);
-    }
     br_status mk_bv_mul(unsigned num_args, expr * const * args, expr_ref & result);
     br_status mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result);
     br_status mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result);
@@ -185,6 +184,38 @@ public:
     bool hi_div0() const { return m_hi_div0; }
 
     bv_util & get_util() { return m_util; }
+
+#define MK_BV_BINARY(OP)                         \
+    expr_ref OP(expr* a, expr* b) {              \
+        expr_ref result(m());                    \
+        if (BR_FAILED == OP(a, b, result))       \
+            result = m_util.OP(a, b);            \
+        return result;                           \
+    }                                            \
+    
+    expr_ref mk_zero_extend(unsigned n, expr * arg) {       
+        expr_ref result(m());                   
+        if (BR_FAILED == mk_zero_extend(n, arg, result))    
+            result = m_util.mk_zero_extend(n, arg);         
+        return result;                          
+    }                                           
+
+    MK_BV_BINARY(mk_bv_urem);
+    MK_BV_BINARY(mk_ule);
+    MK_BV_BINARY(mk_bv_add);
+    MK_BV_BINARY(mk_bv_mul);
+    MK_BV_BINARY(mk_bv_sub);
+
+
+    expr_ref mk_bv2int(expr* a) {
+        expr_ref result(m());
+        if (BR_FAILED == mk_bv2int(a, result)) 
+            result = m_util.mk_bv2int(a);
+        return result;        
+    }
+
+
+
 };
 
 #endif
diff --git a/src/ast/simplifier/CMakeLists.txt b/src/ast/simplifier/CMakeLists.txt
index b6fe9b1cd..52a44595e 100644
--- a/src/ast/simplifier/CMakeLists.txt
+++ b/src/ast/simplifier/CMakeLists.txt
@@ -5,7 +5,6 @@ z3_add_component(simplifier
     array_simplifier_params.cpp
     array_simplifier_plugin.cpp
     basic_simplifier_plugin.cpp
-    bit2int.cpp
     bv_elim.cpp
     bv_simplifier_params.cpp
     bv_simplifier_plugin.cpp
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index 8b25e9263..f8efcbf4b 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -33,7 +33,6 @@ Revision History:
 #include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/simplifier/bv_elim.h"
 #include "ast/simplifier/elim_bounds.h"
-#include "ast/simplifier/bit2int.h"
 #include "ast/normal_forms/pull_quant.h"
 #include "ast/normal_forms/nnf.h"
 #include "ast/pattern/pattern_inference.h"
@@ -70,7 +69,6 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p):
     basic_simplifier_plugin * basic_simp = 0;
     bv_simplifier_plugin * bv_simp = 0;
     setup_simplifier_plugins(m_pre_simplifier, basic_simp, arith_simp, bv_simp);
-    m_bit2int.set_bv_simplifier(bv_simp);
     m_pre_simplifier.enable_presimp();
 }
 
@@ -262,7 +260,7 @@ void asserted_formulas::reduce() {
     INVOKE(m_params.m_propagate_values, propagate_values());
     INVOKE(m_params.m_macro_finder && has_quantifiers(), find_macros());
     INVOKE(m_params.m_nnf_cnf || (m_params.m_mbqi && has_quantifiers()), nnf_cnf());
-    INVOKE(m_params.m_eliminate_and, eliminate_and());
+    INVOKE(/*m_params.m_eliminate_and*/ true, eliminate_and());
     INVOKE(m_params.m_pull_cheap_ite_trees, pull_cheap_ite_trees());
     INVOKE(m_params.m_pull_nested_quantifiers && has_quantifiers(), pull_nested_quantifiers());
     INVOKE(m_params.m_ng_lift_ite != LI_NONE, ng_lift_ite());
diff --git a/src/smt/asserted_formulas.h b/src/smt/asserted_formulas.h
index eaed4e405..fb621000c 100644
--- a/src/smt/asserted_formulas.h
+++ b/src/smt/asserted_formulas.h
@@ -24,7 +24,7 @@ Revision History:
 #include "ast/simplifier/simplifier.h"
 #include "ast/simplifier/basic_simplifier_plugin.h"
 #include "ast/simplifier/maximise_ac_sharing.h"
-#include "ast/simplifier/bit2int.h"
+#include "ast/rewriter/bit2int.h"
 #include "ast/macros/macro_manager.h"
 #include "ast/macros/macro_finder.h"
 #include "ast/normal_forms/defined_names.h"
diff --git a/src/smt/elim_term_ite.h b/src/smt/elim_term_ite.h
index 94e5e0346..106a9f511 100644
--- a/src/smt/elim_term_ite.h
+++ b/src/smt/elim_term_ite.h
@@ -59,6 +59,7 @@ public:
     }
     virtual ~elim_term_ite_cfg() {}
     vector<justified_expr> const& new_defs() const { return m_new_defs; }
+    void reset() { m_new_defs.reset(); }
     br_status reduce_app(func_decl* f, unsigned n, expr *const* args, expr_ref& result, proof_ref& result_pr);
 };
 
@@ -70,6 +71,7 @@ public:
         m_cfg(m, dn) 
     {}
     vector<justified_expr> const& new_defs() const { return m_cfg.new_defs(); }
+    void reset() { m_cfg.reset(); }
 };
 
 
diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp
index 9267dbeba..fcdea850f 100644
--- a/src/smt/params/preprocessor_params.cpp
+++ b/src/smt/params/preprocessor_params.cpp
@@ -46,7 +46,7 @@ void preprocessor_params::display(std::ostream & out) const {
     DISPLAY_PARAM(m_pull_cheap_ite_trees);
     DISPLAY_PARAM(m_pull_nested_quantifiers);
     DISPLAY_PARAM(m_eliminate_term_ite);
-    DISPLAY_PARAM(m_eliminate_and);
+    //DISPLAY_PARAM(m_eliminate_and);
     DISPLAY_PARAM(m_macro_finder);
     DISPLAY_PARAM(m_propagate_values);
     DISPLAY_PARAM(m_propagate_booleans);
diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h
index c343c55fa..fe759417d 100644
--- a/src/smt/params/preprocessor_params.h
+++ b/src/smt/params/preprocessor_params.h
@@ -39,7 +39,7 @@ struct preprocessor_params : public pattern_inference_params,
     bool            m_pull_cheap_ite_trees;
     bool            m_pull_nested_quantifiers;
     bool            m_eliminate_term_ite;
-    bool            m_eliminate_and; // represent (and a b) as (not (or (not a) (not b)))
+//    bool            m_eliminate_and; // represent (and a b) as (not (or (not a) (not b)))
     bool            m_macro_finder;
     bool            m_propagate_values;
     bool            m_propagate_booleans;
@@ -62,7 +62,7 @@ public:
         m_pull_cheap_ite_trees(false),
         m_pull_nested_quantifiers(false),
         m_eliminate_term_ite(false),
-        m_eliminate_and(true),
+       // m_eliminate_and(true),
         m_macro_finder(false),
         m_propagate_values(true),
         m_propagate_booleans(false), // TODO << check peformance

From d3c00181ba526f92c5120b30509839b7eeb30613 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Fri, 25 Aug 2017 23:56:31 -0700
Subject: [PATCH 18/74] remove simplify dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/bv_elim2.cpp     | 115 +++++++++++++++++
 src/ast/rewriter/bv_elim2.h       |  50 ++++++++
 src/ast/rewriter/elim_bounds2.cpp | 203 ++++++++++++++++++++++++++++++
 src/ast/rewriter/elim_bounds2.h   |  77 ++++++++++++
 4 files changed, 445 insertions(+)
 create mode 100644 src/ast/rewriter/bv_elim2.cpp
 create mode 100644 src/ast/rewriter/bv_elim2.h
 create mode 100644 src/ast/rewriter/elim_bounds2.cpp
 create mode 100644 src/ast/rewriter/elim_bounds2.h

diff --git a/src/ast/rewriter/bv_elim2.cpp b/src/ast/rewriter/bv_elim2.cpp
new file mode 100644
index 000000000..5b542e59e
--- /dev/null
+++ b/src/ast/rewriter/bv_elim2.cpp
@@ -0,0 +1,115 @@
+
+/*++
+Copyright (c) 2015 Microsoft Corporation
+
+--*/
+
+#include "ast/rewriter/bv_elim2.h"
+#include "ast/bv_decl_plugin.h"
+#include "ast/rewriter/var_subst.h"
+#include "ast/rewriter/rewriter_def.h"
+#include <sstream>
+
+bool bv_elim_cfg::reduce_quantifier(quantifier * q, 
+                                expr * body, 
+                                expr * const * new_patterns, 
+                                expr * const * new_no_patterns,
+                                expr_ref & result,
+                                proof_ref & result_pr) {
+
+
+    svector<symbol>  names, _names;
+    sort_ref_buffer  sorts(m), _sorts(m);
+    expr_ref_buffer  pats(m);
+    expr_ref_buffer  no_pats(m);
+    expr_ref_buffer  subst_map(m), _subst_map(m);
+    var_subst        subst(m);
+    bv_util          bv(m);
+    expr_ref         new_body(m);
+    expr*            old_body = body;
+    unsigned num_decls = q->get_num_decls();
+    family_id bfid = m.mk_family_id("bv");
+
+    //
+    // Traverse sequence of bound variables to eliminate
+    // bit-vecctor variables and replace them by 
+    // Booleans.
+    // 
+    unsigned var_idx = 0;
+    bool found = false;
+    for (unsigned i = num_decls; i > 0; ) {
+        --i;
+        sort*  s  = q->get_decl_sort(i);
+        symbol nm = q->get_decl_name(i);
+
+        if (bv.is_bv_sort(s)) {
+            // convert n-bit bit-vector variable into sequence of n-Booleans.
+            unsigned num_bits = bv.get_bv_size(s);
+            expr_ref_buffer args(m);
+            expr_ref bv(m);
+            found = true;
+            for (unsigned j = 0; j < num_bits; ++j) {
+                std::ostringstream new_name;
+                new_name << nm.str();
+                new_name << "_";
+                new_name << j;
+                var* v = m.mk_var(var_idx++, m.mk_bool_sort());                
+                args.push_back(v);
+                _sorts.push_back(m.mk_bool_sort());
+                _names.push_back(symbol(new_name.str().c_str()));
+            }
+            bv = m.mk_app(bfid, OP_MKBV, 0, 0, args.size(), args.c_ptr());
+            _subst_map.push_back(bv.get());
+        }
+        else {
+            _subst_map.push_back(m.mk_var(var_idx++, s));
+            _sorts.push_back(s);
+            _names.push_back(nm);
+        }
+    }
+    if (!found) {
+        return false;
+    }
+    // 
+    // reverse the vectors.
+    // 
+    SASSERT(_names.size() == _sorts.size());
+    for (unsigned i = _names.size(); i > 0; ) {
+        --i;
+        names.push_back(_names[i]);
+        sorts.push_back(_sorts[i]);
+    }
+    for (unsigned i = _subst_map.size(); i > 0; ) {
+        --i;
+        subst_map.push_back(_subst_map[i]);
+    }
+
+    expr* const* sub  = subst_map.c_ptr();
+    unsigned sub_size = subst_map.size();
+
+    subst(old_body, sub_size, sub, new_body);
+
+    for (unsigned j = 0; j < q->get_num_patterns(); j++) {
+        expr_ref pat(m);        
+        subst(new_patterns[j], sub_size, sub, pat);
+        pats.push_back(pat);
+    }
+    for (unsigned j = 0; j < q->get_num_no_patterns(); j++) {
+        expr_ref nopat(m);
+        subst(new_no_patterns[j], sub_size, sub, nopat);
+        no_pats.push_back(nopat);
+    }
+
+    result = m.mk_quantifier(true, 
+                        names.size(),
+                        sorts.c_ptr(),
+                        names.c_ptr(),
+                        new_body.get(),
+                        q->get_weight(),
+                        q->get_qid(),
+                        q->get_skid(),
+                        pats.size(), pats.c_ptr(),
+                        no_pats.size(), no_pats.c_ptr());
+    result_pr = m.mk_rewrite(q, result);
+    return true;
+}
diff --git a/src/ast/rewriter/bv_elim2.h b/src/ast/rewriter/bv_elim2.h
new file mode 100644
index 000000000..a4829b207
--- /dev/null
+++ b/src/ast/rewriter/bv_elim2.h
@@ -0,0 +1,50 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    bv_elim.h
+
+Abstract:
+
+    Eliminate bit-vectors variables from clauses, by
+    replacing them by bound Boolean variables.
+
+Author:
+
+    Nikolaj Bjorner (nbjorner) 2008-12-16.
+
+Revision History:
+
+--*/
+#ifndef BV_ELIM2_H_
+#define BV_ELIM2_H_
+
+#include "ast/ast.h"
+#include "ast/rewriter/rewriter.h"
+
+class bv_elim_cfg : public default_rewriter_cfg {
+    ast_manager& m;
+public:
+    bv_elim_cfg(ast_manager& m) : m(m) {}
+
+    bool reduce_quantifier(quantifier * old_q, 
+                           expr * new_body, 
+                           expr * const * new_patterns, 
+                           expr * const * new_no_patterns,
+                           expr_ref & result,
+                           proof_ref & result_pr);
+};
+
+class bv_elim_rw : public rewriter_tpl<bv_elim_cfg> {
+protected:
+    bv_elim_cfg  m_cfg;
+public:
+    bv_elim_rw(ast_manager & m):
+        rewriter_tpl<bv_elim_cfg>(m, m.proofs_enabled(), m_cfg),
+        m_cfg(m)
+    {} 
+};
+
+#endif /* BV_ELIM_H_ */
+
diff --git a/src/ast/rewriter/elim_bounds2.cpp b/src/ast/rewriter/elim_bounds2.cpp
new file mode 100644
index 000000000..9691ade09
--- /dev/null
+++ b/src/ast/rewriter/elim_bounds2.cpp
@@ -0,0 +1,203 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    elim_bounds2.cpp
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-06-28.
+
+Revision History:
+
+--*/
+
+#ifndef ELIM_BOUNDS_H_
+#define ELIM_BOUNDS_H_
+
+#include "ast/used_vars.h"
+#include "util/obj_hashtable.h"
+#include "ast/rewriter/var_subst.h"
+#include "ast/rewriter/elim_bounds2.h"
+#include "ast/ast_pp.h"
+
+elim_bounds_cfg::elim_bounds_cfg(ast_manager & m):
+    m(m),
+    m_util(m) {
+}
+
+/**
+   \brief Find bounds of the form
+
+   (<= x k)
+   (<= (+ x (* -1 y)) k)
+   (<= (+ x (* -1 t)) k)
+   (<= (+ t (* -1 x)) k)
+
+   x and y are a bound variables, t is a ground term and k is a numeral
+
+   It also detects >=, and the atom can be negated.
+*/
+bool elim_bounds_cfg::is_bound(expr * n, var * & lower, var * & upper) {
+    upper    = 0;
+    lower    = 0;
+    bool neg = false;
+    if (m.is_not(n)) {
+        n   = to_app(n)->get_arg(0);
+        neg = true;
+    }
+
+    expr* l = 0, *r = 0;
+    bool le  = false;
+    if (m_util.is_le(n, l, r) && m_util.is_numeral(r)) {
+        n  = l;
+        le = true;
+    }
+    else if (m_util.is_ge(n, l, r) && m_util.is_numeral(r)) {
+        n  = l;
+        le = false;
+    }
+    else {
+        return false;
+    }
+
+    if (neg)
+        le = !le;
+
+    if (is_var(n)) {
+        upper = to_var(n);
+    }
+    else if (m_util.is_add(n, l, r)) {
+        expr * arg1 = l;
+        expr * arg2 = r;
+        if (is_var(arg1))
+            upper   = to_var(arg1);
+        else if (!is_ground(arg1))
+            return false;
+        rational k;
+        bool is_int;
+        if (m_util.is_mul(arg2) && m_util.is_numeral(to_app(arg2)->get_arg(0), k, is_int) && k.is_minus_one()) {
+            arg2    = to_app(arg2)->get_arg(1);
+            if (is_var(arg2))
+                lower = to_var(arg2);
+            else if (!is_ground(arg2))
+                return false; // not supported
+        }
+        else {
+            return false; // not supported
+        }
+    }
+    else {
+        return false;
+    }
+
+    if (!le)
+        std::swap(upper, lower);
+
+    return true;
+}
+
+bool elim_bounds_cfg::is_bound(expr * n) {
+    var * lower, * upper;
+    return is_bound(n, lower, upper);
+}
+
+
+bool elim_bounds_cfg::reduce_quantifier(quantifier * q, 
+                                     expr * n, 
+                                     expr * const * new_patterns, 
+                                     expr * const * new_no_patterns,
+                                     expr_ref & result,
+                                     proof_ref & result_pr) {
+    if (!q->is_forall()) {
+        return false;
+    }
+    unsigned num_vars = q->get_num_decls();
+    ptr_buffer<expr> atoms;
+    if (m.is_or(n))
+        atoms.append(to_app(n)->get_num_args(), to_app(n)->get_args());
+    else
+        atoms.push_back(n);
+    used_vars          used_vars;
+    // collect non-candidates
+    for (expr * a : atoms) {
+        if (!is_bound(a))
+            used_vars.process(a);
+    }
+    if (used_vars.uses_all_vars(q->get_num_decls())) {
+        return false;
+    }
+    // collect candidates
+    obj_hashtable<var> lowers;
+    obj_hashtable<var> uppers;
+    obj_hashtable<var> candidate_set;
+    ptr_buffer<var>    candidates;
+#define ADD_CANDIDATE(V) if (!lowers.contains(V) && !uppers.contains(V)) { candidate_set.insert(V); candidates.push_back(V); }
+    for (expr * a : atoms) {
+        var * lower = 0;
+        var * upper = 0;
+        if (is_bound(a, lower, upper)) {
+            if (lower != 0 && !used_vars.contains(lower->get_idx()) && lower->get_idx() < num_vars) {
+                ADD_CANDIDATE(lower);
+                lowers.insert(lower);
+            }
+            if (upper != 0 && !used_vars.contains(upper->get_idx()) && upper->get_idx() < num_vars) {
+                ADD_CANDIDATE(upper);
+                uppers.insert(upper);
+            }
+        }
+    }
+    TRACE("elim_bounds", tout << "candidates:\n"; for (unsigned i = 0; i < candidates.size(); i++) tout << mk_pp(candidates[i], m) << "\n";);
+    // remove candidates that have lower and upper bounds
+
+    for (var * v : candidates) {
+        if (lowers.contains(v) && uppers.contains(v))
+            candidate_set.erase(v);
+    }
+    TRACE("elim_bounds", tout << "candidates after filter:\n"; for (unsigned i = 0; i < candidates.size(); i++) tout << mk_pp(candidates[i], m) << "\n";);
+    if (candidate_set.empty()) {
+        return false;
+    }
+    // remove bounds that contain variables in candidate_set
+    unsigned j = 0;
+    for (unsigned i = 0; i < atoms.size(); ++i) {
+        expr * a = atoms[i];
+        var * lower = 0;
+        var * upper = 0;
+        if (is_bound(a, lower, upper) && ((lower != 0 && candidate_set.contains(lower)) || (upper != 0 && candidate_set.contains(upper))))
+            continue;
+        atoms[j] = a;
+        j++;
+    }
+    if (j == atoms.size()) {
+        return false;
+    }
+    atoms.resize(j);
+    expr * new_body = 0;
+    switch (atoms.size()) {
+    case 0:
+        result = m.mk_false();
+        result_pr = m.mk_rewrite(q, result);
+        TRACE("elim_bounds", tout << mk_pp(q, m) << "\n" << result << "\n";);
+        return true;
+    case 1:
+        new_body = atoms[0];
+        break;
+    default:
+        new_body = m.mk_or(atoms.size(), atoms.c_ptr());
+        break;
+    }
+    quantifier_ref new_q(m);
+    new_q = m.update_quantifier(q, new_body);
+    elim_unused_vars(m, new_q, params_ref(), result);
+    result_pr = m.mk_rewrite(q, result);
+    TRACE("elim_bounds", tout << mk_pp(q, m) << "\n" << result << "\n";);
+    return true;
+}
+
+#endif /* ELIM_BOUNDS_H_ */
diff --git a/src/ast/rewriter/elim_bounds2.h b/src/ast/rewriter/elim_bounds2.h
new file mode 100644
index 000000000..0d3b026cf
--- /dev/null
+++ b/src/ast/rewriter/elim_bounds2.h
@@ -0,0 +1,77 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    elim_bounds2.h
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-06-28.
+
+Revision History:
+
+--*/
+#ifndef ELIM_BOUNDS2_H_
+#define ELIM_BOUNDS2_H_
+
+#include "ast/ast.h"
+#include "ast/arith_decl_plugin.h"
+#include "ast/rewriter/rewrite.h"
+
+/**
+   \brief Functor for eliminating irrelevant bounds in quantified formulas.
+   
+   Example:
+   (forall (x Int) (y Int) (or (not (>= y x) (not (>= x 0)) (= (select a x) 1))))
+
+   The bound (>= y x) is irrelevant and can be eliminated.
+
+   This can be easily proved by using Fourier-Motzkin elimination.
+
+   Limitations & Assumptions:
+   - It assumes the input formula was already simplified.
+   - It can only handle bounds in the diff-logic fragment.
+
+   \remark This operation is subsumed by Fourier-Motzkin elimination.
+*/
+class elim_bounds_cfg : public default_rewriter_cfg {
+    ast_manager &      m;
+    arith_util         m_util;
+    bool is_bound(expr * n, var * & lower, var * & upper);
+    bool is_bound(expr * n);
+public:
+    elim_bounds_cfg(ast_manager & m);
+    
+    bool reduce_quantifier(quantifier * old_q, 
+                           expr * new_body, 
+                           expr * const * new_patterns, 
+                           expr * const * new_no_patterns,
+                           expr_ref & result,
+                           proof_ref & result_pr);
+};
+
+/**
+   \brief Functor for applying elim_bounds2 in all
+   universal quantifiers in an expression.
+
+   Assumption: the formula was already skolemized.
+*/
+class elim_bounds_rw : public rewriter_tpl<elim_bounds_cfg> {
+protected:
+    elim_bounds_cfg  m_cfg;
+public:
+    elim_bounds_rw(ast_manager & m):
+        rewriter_tpl<elim_bounds_cfg>(m, m.proofs_enabled(), m_cfg),
+        m_cfg(m)
+    {} 
+
+    virtual ~elim_bounds_rw() {}
+};
+
+#endif /* ELIM_BOUNDS2_H_ */
+

From b16a4ac4528d4b9399b5eb7b728e22e0df86b1c0 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Fri, 25 Aug 2017 23:57:10 -0700
Subject: [PATCH 19/74] remove simplify dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/elim_bounds2.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ast/rewriter/elim_bounds2.h b/src/ast/rewriter/elim_bounds2.h
index 0d3b026cf..e0bba4e60 100644
--- a/src/ast/rewriter/elim_bounds2.h
+++ b/src/ast/rewriter/elim_bounds2.h
@@ -21,7 +21,7 @@ Revision History:
 
 #include "ast/ast.h"
 #include "ast/arith_decl_plugin.h"
-#include "ast/rewriter/rewrite.h"
+#include "ast/rewriter/rewriter.h"
 
 /**
    \brief Functor for eliminating irrelevant bounds in quantified formulas.

From 2897b98ed2b712d0fec4ee84b4776830efed1f7c Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 00:37:22 -0700
Subject: [PATCH 20/74] remove simplify dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/muz/bmc/dl_bmc_engine.cpp            |  2 +-
 src/muz/pdr/pdr_smt_context_manager.cpp  |  2 +-
 src/muz/spacer/spacer_virtual_solver.cpp |  4 ++--
 src/opt/opt_solver.cpp                   |  4 ++--
 src/smt/asserted_formulas.cpp            |  1 +
 src/smt/asserted_formulas.h              |  5 ++++-
 src/smt/qi_queue.cpp                     |  2 +-
 src/smt/smt_context.cpp                  | 23 ++---------------------
 src/smt/smt_context.h                    | 14 ++++++--------
 src/smt/smt_kernel.cpp                   | 16 ++++++++++------
 src/smt/smt_kernel.h                     |  7 ++++++-
 src/smt/smt_quick_checker.cpp            |  4 ++--
 src/smt/smt_quick_checker.h              |  4 ++--
 src/smt/smt_setup.cpp                    | 12 +++++++++---
 src/smt/smt_solver.cpp                   |  2 +-
 src/smt/theory_arith_core.h              |  2 +-
 src/smt/theory_arith_int.h               |  3 +--
 src/smt/theory_arith_nl.h                |  2 +-
 src/smt/theory_array_full.cpp            |  9 ++++-----
 src/smt/theory_array_full.h              |  3 ---
 src/smt/theory_bv.cpp                    |  9 ++++-----
 src/smt/theory_bv.h                      | 10 ----------
 src/smt/theory_fpa.cpp                   |  2 +-
 23 files changed, 62 insertions(+), 80 deletions(-)

diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp
index 0f7914e0c..6d2f88019 100644
--- a/src/muz/bmc/dl_bmc_engine.cpp
+++ b/src/muz/bmc/dl_bmc_engine.cpp
@@ -1139,7 +1139,7 @@ namespace datalog {
             md->eval(path, path);
             IF_VERBOSE(2, verbose_stream() << mk_pp(trace, m) << "\n";
                        for (unsigned i = 0; i < b.m_solver.size(); ++i) {
-                           verbose_stream() << mk_pp(b.m_solver.get_formulas()[i], m) << "\n";
+                           verbose_stream() << mk_pp(b.m_solver.get_formula(i), m) << "\n";
                        });
             scoped_proof _sp(m);
             proof_ref pr(m);
diff --git a/src/muz/pdr/pdr_smt_context_manager.cpp b/src/muz/pdr/pdr_smt_context_manager.cpp
index e912bd658..b87d1e451 100644
--- a/src/muz/pdr/pdr_smt_context_manager.cpp
+++ b/src/muz/pdr/pdr_smt_context_manager.cpp
@@ -83,7 +83,7 @@ namespace pdr {
               {
                   ast_smt_pp pp(m);
                   for (unsigned i = 0; i < m_context.size(); ++i) {
-                      pp.add_assumption(m_context.get_formulas()[i]);
+                      pp.add_assumption(m_context.get_formula(i));
                   }
                   for (unsigned i = 0; i < assumptions.size(); ++i) {
                       pp.add_assumption(assumptions[i].get());
diff --git a/src/muz/spacer/spacer_virtual_solver.cpp b/src/muz/spacer/spacer_virtual_solver.cpp
index c080fa2c4..ebaef14f0 100644
--- a/src/muz/spacer/spacer_virtual_solver.cpp
+++ b/src/muz/spacer/spacer_virtual_solver.cpp
@@ -318,7 +318,7 @@ lbool virtual_solver::check_sat_core(unsigned num_assumptions,
             stopwatch sw2;
             smt::kernel kernel(m, p);
             for (unsigned i = 0, sz = m_context.size(); i < sz; ++i)
-            { kernel.assert_expr(m_context.get_formulas()[i]); }
+                { kernel.assert_expr(m_context.get_formula(i)); }
             sw2.start();
             kernel.check(num_assumptions, assumptions);
             sw2.stop();
@@ -450,7 +450,7 @@ void virtual_solver::to_smt2_benchmark(std::ostream &out,
 
 
     for (unsigned i = 0, sz = context.size(); i < sz; ++i) {
-        asserts.push_back(context.get_formulas()[i]);
+        asserts.push_back(context.get_formula(i));
         pp.collect(asserts.back());
     }
     pp.collect(num_assumptions, assumptions);
diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp
index f453d9fe2..69b62083f 100644
--- a/src/opt/opt_solver.cpp
+++ b/src/opt/opt_solver.cpp
@@ -161,7 +161,7 @@ namespace opt {
         TRACE("opt_verbose", {
             tout << "context size: " << m_context.size() << "\n";            
             for (unsigned i = 0; i < m_context.size(); ++i) {
-                tout << mk_pp(m_context.get_formulas()[i], m_context.m()) << "\n";
+                tout << mk_pp(m_context.get_formula(i), m_context.m()) << "\n";
             }
         });
         stopwatch w;
@@ -330,7 +330,7 @@ namespace opt {
     
     expr * opt_solver::get_assertion(unsigned idx) const {
         SASSERT(idx < get_num_assertions());
-        return m_context.get_formulas()[idx];
+        return m_context.get_formula(idx);
     }
         
     smt::theory_var opt_solver::add_objective(app* term) {
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index f8efcbf4b..3fb20d283 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -47,6 +47,7 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p):
     m_params(p),
     m_pre_simplifier(m),
     m_simplifier(m),
+    m_rewriter(m),
     m_defined_names(m),
     m_static_features(m),
     m_asserted_formulas(m),
diff --git a/src/smt/asserted_formulas.h b/src/smt/asserted_formulas.h
index fb621000c..3c5f424fb 100644
--- a/src/smt/asserted_formulas.h
+++ b/src/smt/asserted_formulas.h
@@ -30,6 +30,7 @@ Revision History:
 #include "ast/normal_forms/defined_names.h"
 #include "ast/pattern/pattern_inference.h"
 #include "smt/params/smt_params.h"
+#include "ast/rewriter/th_rewriter.h"
 
 class arith_simplifier_plugin;
 class bv_simplifier_plugin;
@@ -39,6 +40,7 @@ class asserted_formulas {
     smt_params &                 m_params;
     simplifier                   m_pre_simplifier;
     simplifier                   m_simplifier;
+    th_rewriter                  m_rewriter;
     basic_simplifier_plugin *    m_bsimp;
     bv_simplifier_plugin *       m_bvsimp;
     defined_names                m_defined_names;
@@ -121,7 +123,8 @@ public:
     proof * const * get_formula_proofs() const { return m_asserted_formula_prs.c_ptr(); }
     void init(unsigned num_formulas, expr * const * formulas, proof * const * prs);
     void register_simplifier_plugin(simplifier_plugin * p) { m_simplifier.register_plugin(p); }
-    simplifier & get_simplifier() { return m_simplifier; }
+    // simplifier & get_simplifier() { return m_simplifier; }
+    th_rewriter& get_rewriter() { return m_rewriter; }
     void get_assertions(ptr_vector<expr> & result);
     bool empty() const { return m_asserted_formulas.empty(); }
     void collect_static_features();
diff --git a/src/smt/qi_queue.cpp b/src/smt/qi_queue.cpp
index 36a2ce834..8f8e3df7b 100644
--- a/src/smt/qi_queue.cpp
+++ b/src/smt/qi_queue.cpp
@@ -227,7 +227,7 @@ namespace smt {
         TRACE("qi_queue_instance", tout << "new instance:\n" << mk_pp(instance, m_manager) << "\n";);
         expr_ref  s_instance(m_manager);
         proof_ref pr(m_manager);
-        simplifier & simp = m_context.get_simplifier();
+        th_rewriter & simp = m_context.get_rewriter();
         simp(instance, s_instance, pr);
         TRACE("qi_queue_bug", tout << "new instance after simplification:\n" << mk_pp(s_instance, m_manager) << "\n";);
         if (m_manager.is_true(s_instance)) {
diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp
index b966b8380..7b92bdd28 100644
--- a/src/smt/smt_context.cpp
+++ b/src/smt/smt_context.cpp
@@ -148,8 +148,8 @@ namespace smt {
         dst_ctx.set_logic(src_ctx.m_setup.get_logic());
         dst_ctx.copy_plugins(src_ctx, dst_ctx);
 
-        asserted_formulas& src_af = src_ctx.m_asserted_formulas;
-        asserted_formulas& dst_af = dst_ctx.m_asserted_formulas;
+        asserted_formulas_new& src_af = src_ctx.m_asserted_formulas;
+        asserted_formulas_new& dst_af = dst_ctx.m_asserted_formulas;
 
         // Copy asserted formulas.
         for (unsigned i = 0; i < src_af.get_num_formulas(); ++i) {
@@ -224,20 +224,6 @@ namespace smt {
 
     void context::copy_plugins(context& src, context& dst) {
 
-        // copy missing simplifier_plugins
-        // remark: some simplifier_plugins are automatically created by the asserted_formulas class.
-        simplifier & src_s = src.get_simplifier();
-        simplifier & dst_s = dst.get_simplifier();
-        ptr_vector<simplifier_plugin>::const_iterator it1  = src_s.begin_plugins();
-        ptr_vector<simplifier_plugin>::const_iterator end1 = src_s.end_plugins();
-        for (; it1 != end1; ++it1) {
-            simplifier_plugin * p = *it1;
-            if (dst_s.get_plugin(p->get_family_id()) == 0) {
-                dst.register_plugin(p->mk_fresh());
-            }
-            SASSERT(dst_s.get_plugin(p->get_family_id()) != 0);
-        }
-
         // copy theory plugins
         for (theory* old_th : src.m_theory_set) {
             theory * new_th = old_th->mk_fresh(&dst);
@@ -2845,11 +2831,6 @@ namespace smt {
         return false;
     }
 
-    void context::register_plugin(simplifier_plugin * s) {
-        SASSERT(!already_internalized());
-        SASSERT(m_scope_lvl == 0);
-        m_asserted_formulas.register_simplifier_plugin(s);
-    }
 
 #ifdef Z3DEBUG
     /**
diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h
index fecb42700..fbf7cbca3 100644
--- a/src/smt/smt_context.h
+++ b/src/smt/smt_context.h
@@ -36,7 +36,7 @@ Revision History:
 #include "smt/smt_case_split_queue.h"
 #include "smt/smt_almost_cg_table.h"
 #include "smt/smt_failure.h"
-#include "smt/asserted_formulas.h"
+#include "smt/asserted_formulas_new.h"
 #include "smt/smt_types.h"
 #include "smt/dyn_ack.h"
 #include "ast/ast_smt_pp.h"
@@ -81,7 +81,7 @@ namespace smt {
         params_ref                  m_params;
         setup                       m_setup;
         timer                       m_timer;
-        asserted_formulas           m_asserted_formulas;
+        asserted_formulas_new       m_asserted_formulas;
         scoped_ptr<quantifier_manager>   m_qmanager;
         scoped_ptr<model_generator>      m_model_generator;
         scoped_ptr<relevancy_propagator> m_relevancy_propagator;
@@ -245,8 +245,8 @@ namespace smt {
             return m_manager;
         }
 
-        simplifier & get_simplifier() {
-            return m_asserted_formulas.get_simplifier();
+        th_rewriter & get_rewriter() {
+            return m_asserted_formulas.get_rewriter();
         }
 
         smt_params & get_fparams() {
@@ -1467,8 +1467,6 @@ namespace smt {
 
         bool set_logic(symbol const& logic) { return m_setup.set_logic(logic); }
 
-        void register_plugin(simplifier_plugin * s);
-
         void register_plugin(theory * th);
 
         void assert_expr(expr * e);
@@ -1540,9 +1538,9 @@ namespace smt {
 
         proof * get_asserted_formula_proof(unsigned idx) const { return m_asserted_formulas.get_formula_proof(idx); }
 
-        expr * const * get_asserted_formulas() const { return m_asserted_formulas.get_formulas(); }
+        void get_asserted_formulas(ptr_vector<expr>& r) const { m_asserted_formulas.get_assertions(r); }
 
-        proof * const * get_asserted_formula_proofs() const { return m_asserted_formulas.get_formula_proofs(); }
+        //proof * const * get_asserted_formula_proofs() const { return m_asserted_formulas.get_formula_proofs(); }
 
         void get_assumptions_core(ptr_vector<expr> & result);
 
diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp
index e4ce30f91..a7948725c 100644
--- a/src/smt/smt_kernel.cpp
+++ b/src/smt/smt_kernel.cpp
@@ -60,10 +60,10 @@ namespace smt {
             // m_kernel.display(out); <<< for external users it is just junk
             // TODO: it will be replaced with assertion_stack.display
             unsigned num = m_kernel.get_num_asserted_formulas();
-            expr * const * fms = m_kernel.get_asserted_formulas();
             out << "(kernel";
             for (unsigned i = 0; i < num; i++) {
-                out << "\n  " << mk_ismt2_pp(fms[i], m(), 2);
+                expr* f = m_kernel.get_asserted_formula(i);
+                out << "\n  " << mk_ismt2_pp(f, m(), 2);
             }
             out << ")";
         }
@@ -81,8 +81,12 @@ namespace smt {
             return m_kernel.get_num_asserted_formulas();
         }
         
-        expr * const * get_formulas() const {
-            return m_kernel.get_asserted_formulas();
+        void get_formulas(ptr_vector<expr>& fmls) const {
+            m_kernel.get_asserted_formulas(fmls);
+        }
+
+        expr* get_formula(unsigned i) const {
+            return m_kernel.get_asserted_formula(i);
         }
         
         void push() {
@@ -241,8 +245,8 @@ namespace smt {
         return m_imp->size();
     }
     
-    expr * const * kernel::get_formulas() const {
-        return m_imp->get_formulas();
+    expr* kernel::get_formula(unsigned i) const {
+        return m_imp->get_formula(i);
     }
 
 
diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h
index 6ebb8546f..d10cab4f3 100644
--- a/src/smt/smt_kernel.h
+++ b/src/smt/smt_kernel.h
@@ -85,7 +85,12 @@ namespace smt {
         /**
            \brief Return the array of asserted formulas.
         */
-        expr * const * get_formulas() const;
+        void get_formulas(ptr_vector<expr>& r) const;
+
+        /**
+           \brief return the formula at index idx.
+        */
+        expr* get_formula(unsigned idx) const;
         
         /**
            \brief Create a backtracking point (aka scope level).
diff --git a/src/smt/smt_quick_checker.cpp b/src/smt/smt_quick_checker.cpp
index f8744318a..773768944 100644
--- a/src/smt/smt_quick_checker.cpp
+++ b/src/smt/smt_quick_checker.cpp
@@ -164,7 +164,7 @@ namespace smt {
     quick_checker::quick_checker(context & c):
         m_context(c),
         m_manager(c.get_manager()),
-        m_simplifier(c.get_simplifier()),
+        m_simplifier(c.get_rewriter()),
         m_collector(c),
         m_new_exprs(m_manager) {
     }
@@ -411,7 +411,7 @@ namespace smt {
             }
         }
         expr_ref new_expr(m_manager);
-        m_simplifier.mk_app(to_app(n)->get_decl(), num_args, new_args.c_ptr(), new_expr);
+        new_expr = m_simplifier.mk_app(to_app(n)->get_decl(), num_args, new_args.c_ptr());
         m_new_exprs.push_back(new_expr);
         m_canonize_cache.insert(n, new_expr);
         return new_expr;
diff --git a/src/smt/smt_quick_checker.h b/src/smt/smt_quick_checker.h
index eb07880f1..d07e10921 100644
--- a/src/smt/smt_quick_checker.h
+++ b/src/smt/smt_quick_checker.h
@@ -20,7 +20,7 @@ Revision History:
 #define SMT_QUICK_CHECKER_H_
 
 #include "ast/ast.h"
-#include "ast/simplifier/simplifier.h"
+#include "ast/rewriter/th_rewriter.h"
 #include "util/obj_hashtable.h"
 
 namespace smt {
@@ -77,7 +77,7 @@ namespace smt {
 
         context &            m_context;
         ast_manager &        m_manager;
-        simplifier &         m_simplifier;
+        th_rewriter &        m_simplifier;
         collector            m_collector;
         expr_ref_vector      m_new_exprs;
         vector<enode_vector> m_candidate_vectors; 
diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp
index a37668c7b..f0c44a574 100644
--- a/src/smt/smt_setup.cpp
+++ b/src/smt/smt_setup.cpp
@@ -142,7 +142,9 @@ namespace smt {
         }
         else {
             IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";);
-            st.collect(m_context.get_num_asserted_formulas(), m_context.get_asserted_formulas());
+            ptr_vector<expr> fmls;
+            m_context.get_asserted_formulas(fmls);
+            st.collect(fmls.size(), fmls.c_ptr());
             IF_VERBOSE(1000, st.display_primitive(verbose_stream()););
             if (m_logic == "QF_UF") 
                 setup_QF_UF(st);
@@ -742,7 +744,9 @@ namespace smt {
     void setup::setup_arith() {
         static_features    st(m_manager);
         IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";);
-        st.collect(m_context.get_num_asserted_formulas(), m_context.get_asserted_formulas());
+        ptr_vector<expr> fmls;
+        m_context.get_asserted_formulas(fmls);
+        st.collect(fmls.size(), fmls.c_ptr());
         IF_VERBOSE(1000, st.display_primitive(verbose_stream()););
         bool fixnum = st.arith_k_sum_is_small() && m_params.m_arith_fixnum;
         bool int_only = !st.m_has_rational && !st.m_has_real && m_params.m_arith_int_only;
@@ -877,7 +881,9 @@ namespace smt {
 
     void setup::setup_unknown() {
         static_features st(m_manager);
-        st.collect(m_context.get_num_asserted_formulas(), m_context.get_asserted_formulas());
+        ptr_vector<expr> fmls;
+        m_context.get_asserted_formulas(fmls);
+        st.collect(fmls.size(), fmls.c_ptr());
         TRACE("setup", tout << "setup_unknown\n";);        
         setup_arith();
         setup_arrays();
diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp
index ba86f017a..9d86436d9 100644
--- a/src/smt/smt_solver.cpp
+++ b/src/smt/smt_solver.cpp
@@ -216,7 +216,7 @@ namespace smt {
 
         virtual expr * get_assertion(unsigned idx) const {
             SASSERT(idx < get_num_assertions());
-            return m_context.get_formulas()[idx];
+            return m_context.get_formula(idx);
         }
 
         struct collect_fds_proc {
diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h
index a30e51133..70355502d 100644
--- a/src/smt/theory_arith_core.h
+++ b/src/smt/theory_arith_core.h
@@ -442,7 +442,7 @@ namespace smt {
     void theory_arith<Ext>::mk_axiom(expr * ante, expr * conseq) {
         ast_manager & m = get_manager();
         context & ctx   = get_context();
-        simplifier & s  = ctx.get_simplifier();
+        th_rewriter & s  = ctx.get_rewriter();
         expr_ref s_ante(m), s_conseq(m);
         expr* s_conseq_n, * s_ante_n;
         bool negated;
diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h
index d93e062f4..fbcc9c11a 100644
--- a/src/smt/theory_arith_int.h
+++ b/src/smt/theory_arith_int.h
@@ -455,9 +455,8 @@ namespace smt {
         pol = m_util.mk_add(_args.size(), _args.c_ptr());
         result = m_util.mk_ge(pol, m_util.mk_numeral(k, all_int));
         TRACE("arith_mk_polynomial", tout << "before simplification:\n" << result << "\n";);
-        simplifier & s = get_context().get_simplifier();
         proof_ref pr(m);
-        s(result, result, pr);
+        get_context().get_rewriter()(result, result, pr);
         TRACE("arith_mk_polynomial", tout << "after simplification:\n" << result << "\n";);
         SASSERT(is_well_sorted(get_manager(), result));
     }
diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h
index 9649440d2..a04c34706 100644
--- a/src/smt/theory_arith_nl.h
+++ b/src/smt/theory_arith_nl.h
@@ -2205,7 +2205,7 @@ namespace smt {
                 args.push_back(monomial2expr(eq->get_monomial(i), is_int));
         }
         context & ctx   = get_context();
-        simplifier & s  = ctx.get_simplifier();
+        th_rewriter& s = ctx.get_rewriter();
         expr_ref pol(get_manager());
         SASSERT(!args.empty());
         pol = mk_nary_add(args.size(), args.c_ptr());
diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp
index d5660e325..c7a3d7920 100644
--- a/src/smt/theory_array_full.cpp
+++ b/src/smt/theory_array_full.cpp
@@ -21,6 +21,7 @@ Revision History:
 #include "smt/theory_array_full.h"
 #include "ast/ast_ll_pp.h"
 #include "ast/ast_pp.h"
+#include "ast/ast_util.h"
 #include "ast/ast_smt2_pp.h"
 #include "util/stats.h"
 
@@ -515,7 +516,7 @@ namespace smt {
 
         expr_ref sel1(m), sel2(m);
         sel1 = mk_select(args1.size(), args1.c_ptr());
-        m_simp->mk_app(f, args2.size(), args2.c_ptr(), sel2);
+        sel2 = ctx.get_rewriter().mk_app(f, args2.size(), args2.c_ptr());
         ctx.internalize(sel1, false);
         ctx.internalize(sel2, false);
         
@@ -553,7 +554,7 @@ namespace smt {
 
         expr* def1 = mk_default(map);
         expr_ref def2(get_manager());
-        m_simp->mk_app(f, args2.size(), args2.c_ptr(), def2);
+        def2 = ctx.get_rewriter().mk_app(f, args2.size(), args2.c_ptr());
         ctx.internalize(def1, false);
         ctx.internalize(def2, false);
         return try_assign_eq(def1, def2);
@@ -722,9 +723,7 @@ namespace smt {
             }
             
             expr_ref eq(m);
-            simplifier_plugin* p = m_simp->get_plugin(m.get_basic_family_id());
-            basic_simplifier_plugin* bp = static_cast<basic_simplifier_plugin*>(p);
-            bp->mk_and(eqs.size(), eqs.c_ptr(), eq);
+            eq = mk_and(eqs);
             expr* defA = mk_default(store_app->get_arg(0));
             def2 = m.mk_ite(eq, store_app->get_arg(num_args-1), defA); 
 #if 0
diff --git a/src/smt/theory_array_full.h b/src/smt/theory_array_full.h
index e22d1d0e2..2cad3acbd 100644
--- a/src/smt/theory_array_full.h
+++ b/src/smt/theory_array_full.h
@@ -20,7 +20,6 @@ Revision History:
 #define THEORY_ARRAY_FULL_H_
 
 #include "smt/theory_array.h"
-#include "ast/simplifier/simplifier.h"
 #include "ast/ast_trail.h"
 
 namespace smt {
@@ -37,7 +36,6 @@ namespace smt {
         ptr_vector<var_data_full> m_var_data_full;
 
         ast2ast_trailmap<sort,app> m_sort2epsilon;
-        simplifier*                m_simp;
         obj_pair_map<expr,expr,bool> m_eqs;
         svector<literal>             m_eqsv;
 
@@ -100,7 +98,6 @@ namespace smt {
             // the parent class is theory_array.
             // theory::init(ctx); 
             theory_array::init(ctx); 
-            m_simp = &ctx->get_simplifier(); 
         }
 
     };
diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp
index 0981ccdbf..ec3913415 100644
--- a/src/smt/theory_bv.cpp
+++ b/src/smt/theory_bv.cpp
@@ -28,7 +28,6 @@ namespace smt {
 
     void theory_bv::init(context * ctx) {
         theory::init(ctx);
-        m_simplifier    = &(ctx->get_simplifier());
     }
 
     theory_var theory_bv::mk_var(enode * n) {
@@ -300,7 +299,7 @@ namespace smt {
     void theory_bv::simplify_bit(expr * s, expr_ref & r) {
         // proof_ref p(get_manager());
         // if (get_context().at_base_level())
-        //    m_simplifier->operator()(s, r, p);
+        //    ctx.get_rewriter()(s, r, p);
         // else
         r = s;
     }
@@ -605,8 +604,9 @@ namespace smt {
             args.push_back(m.mk_ite(b, n, zero));
             num *= numeral(2);
         }
-        expr_ref sum(m);
-        arith_simp().mk_add(sz, args.c_ptr(), sum);
+        expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m);
+        arith_rewriter arw(m);
+        ctx.get_rewriter()(sum);
         literal l(mk_eq(n, sum, false));
         TRACE("bv", 
               tout << mk_pp(n, m) << "\n";
@@ -1366,7 +1366,6 @@ namespace smt {
         m_params(params),
         m_util(m),
         m_autil(m),
-        m_simplifier(0),
         m_bb(m, bb_params),
         m_trail_stack(*this),
         m_find(*this),
diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h
index 5d2ea8da5..c35078ace 100644
--- a/src/smt/theory_bv.h
+++ b/src/smt/theory_bv.h
@@ -24,10 +24,7 @@ Revision History:
 #include "ast/rewriter/bit_blaster/bit_blaster.h"
 #include "util/trail.h"
 #include "util/union_find.h"
-#include "ast/simplifier/simplifier.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/arith_decl_plugin.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
 #include "smt/proto_model/numeral_factory.h"
 
 namespace smt {
@@ -112,7 +109,6 @@ namespace smt {
         theory_bv_params const & m_params;
         bv_util                  m_util;
         arith_util               m_autil;
-        simplifier *             m_simplifier;
         bit_blaster              m_bb;
         th_trail_stack           m_trail_stack;
         th_union_find            m_find;
@@ -218,12 +214,6 @@ namespace smt {
         void assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc);
         void assert_int2bv_axiom(app* n);
         void assert_bv2int_axiom(app* n);
-        arith_simplifier_plugin & arith_simp() const {
-            SASSERT(m_simplifier != 0);
-            arith_simplifier_plugin * as = static_cast<arith_simplifier_plugin*>(m_simplifier->get_plugin(m_autil.get_family_id()));
-            SASSERT(as != 0);
-            return *as;
-        }
 
     protected:
         virtual void init(context * ctx);
diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp
index e8edfc7ec..3f246b2c7 100644
--- a/src/smt/theory_fpa.cpp
+++ b/src/smt/theory_fpa.cpp
@@ -385,7 +385,7 @@ namespace smt {
     {
         ast_manager & m = get_manager();
         context & ctx = get_context();
-        simplifier & simp = ctx.get_simplifier();
+        th_rewriter & simp = ctx.get_rewriter();
 
         expr_ref res(m), t(m);
         proof_ref t_pr(m);

From 881f90d17d97f0a545503fb7ed3eb763321aaa37 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 00:48:49 -0700
Subject: [PATCH 21/74] remove simplify dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/macros/macro_manager.h                |   1 -
 src/ast/pattern/pattern_inference.h           | 187 ------------------
 .../dl_mk_quantifier_instantiation.cpp        |   1 +
 src/smt/elim_term_ite.h                       |   3 +-
 4 files changed, 2 insertions(+), 190 deletions(-)

diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h
index 58fedf666..87073004c 100644
--- a/src/ast/macros/macro_manager.h
+++ b/src/ast/macros/macro_manager.h
@@ -21,7 +21,6 @@ Revision History:
 
 #include "util/obj_hashtable.h"
 #include "ast/ast_util.h"
-#include "ast/simplifier/simplifier.h"
 #include "ast/recurse_expr.h"
 #include "ast/func_decl_dependencies.h"
 #include "ast/macros/macro_util.h"
diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h
index 281c3ff8b..905662477 100644
--- a/src/ast/pattern/pattern_inference.h
+++ b/src/ast/pattern/pattern_inference.h
@@ -20,7 +20,6 @@ Revision History:
 #define PATTERN_INFERENCE_H_
 
 #include "ast/ast.h"
-#include "ast/simplifier/simplifier.h"
 #include "ast/rewriter/rewriter.h"
 #include "ast/pattern/pattern_inference_params.h"
 #include "util/vector.h"
@@ -61,192 +60,6 @@ public:
     bool operator()(unsigned num_bindings, expr * p1, expr * p2);
 };
 
-#if 0
-class pattern_inference_old : public simplifier {
-    pattern_inference_params & m_params;
-    family_id                  m_bfid;
-    family_id                  m_afid;
-    svector<family_id>         m_forbidden;
-    obj_hashtable<func_decl>   m_preferred;        
-    smaller_pattern            m_le;
-    unsigned                   m_num_bindings;
-    unsigned                   m_num_no_patterns;
-    expr * const *             m_no_patterns;
-    bool                       m_nested_arith_only;
-    bool                       m_block_loop_patterns;
-
-    struct info {
-        uint_set m_free_vars;
-        unsigned m_size;
-        info(uint_set const & vars, unsigned size):
-            m_free_vars(vars),
-            m_size(size) {
-        }
-        info():
-            m_free_vars(),
-            m_size(0) {
-        }
-    };
-
-    typedef obj_map<expr, info> expr2info;
-
-    expr2info                 m_candidates_info; // candidate -> set of free vars + size
-    app_ref_vector            m_candidates;
-
-    ptr_vector<app>           m_tmp1;
-    ptr_vector<app>           m_tmp2;
-    ptr_vector<app>           m_todo;
-
-    // Compare candidates patterns based on their usefulness
-    // p1 < p2 if
-    //  - p1 has more free variables than p2
-    //  - p1 and p2 has the same number of free variables,
-    //    and p1 is smaller than p2.
-    struct pattern_weight_lt {
-        expr2info & m_candidates_info;
-        pattern_weight_lt(expr2info & i):
-            m_candidates_info(i) {
-        }
-        bool operator()(expr * n1, expr * n2) const;
-    };
-
-    pattern_weight_lt          m_pattern_weight_lt;
-
-    //
-    // Functor for collecting candidates.
-    //
-    class collect {
-        struct entry {
-            expr *    m_node;
-            unsigned  m_delta;
-            entry():m_node(0), m_delta(0) {}
-            entry(expr * n, unsigned d):m_node(n), m_delta(d) {}
-            unsigned hash() const { 
-                return hash_u_u(m_node->get_id(), m_delta);
-            }
-            bool operator==(entry const & e) const { 
-                return m_node == e.m_node && m_delta == e.m_delta; 
-            }
-        };
-        
-        struct info {
-            expr_ref    m_node;
-            uint_set    m_free_vars;
-            unsigned    m_size;
-            info(ast_manager & m, expr * n, uint_set const & vars, unsigned sz):
-                m_node(n, m), m_free_vars(vars), m_size(sz) {}
-        };
-        
-        ast_manager &            m;
-        pattern_inference_old &      m_owner;
-        family_id                m_afid;
-        unsigned                 m_num_bindings;
-        typedef map<entry, info *, obj_hash<entry>, default_eq<entry> > cache;
-        cache                    m_cache;
-        ptr_vector<info>         m_info;
-        svector<entry>           m_todo;
-
-        void visit(expr * n, unsigned delta, bool & visited);
-        bool visit_children(expr * n, unsigned delta);
-        void save(expr * n, unsigned delta, info * i);
-        void save_candidate(expr * n, unsigned delta);
-        void reset();
-    public:
-        collect(ast_manager & m, pattern_inference_old & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {}
-        void operator()(expr * n, unsigned num_bindings);
-    };
-
-    collect                    m_collect;
-    
-    void add_candidate(app * n, uint_set const & s, unsigned size);
-
-    void filter_looping_patterns(ptr_vector<app> & result);
-
-    bool has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result);
-
-    void filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result);
-
-    class contains_subpattern {
-        pattern_inference_old &  m_owner;
-        nat_set              m_already_processed; 
-        ptr_vector<expr>     m_todo;
-        void save(expr * n);
-    public:
-        contains_subpattern(pattern_inference_old & owner):
-            m_owner(owner) {}
-        bool operator()(expr * n);
-    };
-
-    contains_subpattern        m_contains_subpattern;
-        
-    bool contains_subpattern(expr * n);
-    
-    struct pre_pattern {
-        ptr_vector<app>  m_exprs;     // elements of the pattern.
-        uint_set         m_free_vars; // set of free variables in m_exprs
-        unsigned         m_idx;       // idx of the next candidate to process.
-        pre_pattern():
-            m_idx(0) {
-        }
-    };
-
-    ptr_vector<pre_pattern>      m_pre_patterns;
-    expr_pattern_match           m_database;
-
-    void candidates2unary_patterns(ptr_vector<app> const & candidate_patterns,
-                                   ptr_vector<app> & remaining_candidate_patterns,
-                                   app_ref_buffer & result);
-    
-    void candidates2multi_patterns(unsigned max_num_patterns, 
-                                   ptr_vector<app> const & candidate_patterns, 
-                                   app_ref_buffer & result);
-    
-    void reset_pre_patterns();
-
-    /**
-       \brief All minimal unary patterns (i.e., expressions that
-       contain all bound variables) are copied to result.  If there
-       are unary patterns, then at most num_extra_multi_patterns multi
-       patterns are created.  If there are no unary pattern, then at
-       most 1 + num_extra_multi_patterns multi_patterns are created.
-    */
-    void mk_patterns(unsigned num_bindings,              // IN number of bindings.
-                     expr * n,                           // IN node where the patterns are going to be extracted.
-                     unsigned num_no_patterns,           // IN num. patterns that should not be used.
-                     expr * const * no_patterns,         // IN patterns that should not be used.
-                     app_ref_buffer & result);           // OUT result
-    
-    virtual void reduce1_quantifier(quantifier * q);
-
-public:
-    pattern_inference_old(ast_manager & m, pattern_inference_params & params);
-    
-    void register_forbidden_family(family_id fid) {
-        SASSERT(fid != m_bfid);
-        m_forbidden.push_back(fid);
-    }
-
-    /**
-       \brief Register f as a preferred function symbol. The inference algorithm
-       gives preference to patterns rooted by this kind of function symbol.
-    */
-    void register_preferred(func_decl * f) {
-        m_preferred.insert(f);
-    }
-
-    void register_preferred(unsigned num, func_decl * const * fs) { for (unsigned i = 0; i < num; i++) register_preferred(fs[i]); }
-    
-    bool is_forbidden(func_decl const * decl) const {
-        family_id fid = decl->get_family_id();
-        if (fid == m_bfid && decl->get_decl_kind() != OP_TRUE && decl->get_decl_kind() != OP_FALSE) 
-            return true;
-        return std::find(m_forbidden.begin(), m_forbidden.end(), fid) != m_forbidden.end();
-    }
-
-    bool is_forbidden(app * n) const;
-};
-#endif
-
 class pattern_inference_cfg :  public default_rewriter_cfg {
     ast_manager&               m;
     pattern_inference_params & m_params;
diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
index 74d15bcdf..8986bf506 100644
--- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
+++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp
@@ -27,6 +27,7 @@ Revision History:
 #include "muz/base/dl_context.h"
 #include "ast/pattern/pattern_inference.h"
 #include "ast/rewriter/rewriter_def.h"
+#include "ast/ast_util.h"
 
 
 namespace datalog {
diff --git a/src/smt/elim_term_ite.h b/src/smt/elim_term_ite.h
index 106a9f511..86b9a79e2 100644
--- a/src/smt/elim_term_ite.h
+++ b/src/smt/elim_term_ite.h
@@ -19,9 +19,9 @@ Revision History:
 #ifndef ELIM_TERM_ITE_H_
 #define ELIM_TERM_ITE_H_
 
-#include "ast/simplifier/simplifier.h"
 #include "ast/normal_forms/defined_names.h"
 #include "ast/rewriter/rewriter.h"
+#include "ast/simplifier/simplifier.h"
 
 class elim_term_ite : public simplifier {
     defined_names &    m_defined_names;
@@ -48,7 +48,6 @@ public:
 };
 
 
-
 class elim_term_ite_cfg : public default_rewriter_cfg {
     ast_manager&           m;
     defined_names &        m_defined_names;

From 5371315f4c999b3a314a0d0023c1091f51ebc9aa Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 00:57:44 -0700
Subject: [PATCH 22/74] remove simplify dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/muz/spacer/spacer_legacy_mev.cpp | 3 ---
 src/muz/spacer/spacer_legacy_mev.h   | 1 -
 src/muz/spacer/spacer_util.h         | 3 ++-
 3 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/muz/spacer/spacer_legacy_mev.cpp b/src/muz/spacer/spacer_legacy_mev.cpp
index 69f479836..16e2cc734 100644
--- a/src/muz/spacer/spacer_legacy_mev.cpp
+++ b/src/muz/spacer/spacer_legacy_mev.cpp
@@ -5,11 +5,8 @@ Copyright (c) 2017 Arie Gurfinkel
 */
 
 #include <sstream>
-#include "ast/simplifier/arith_simplifier_plugin.h"
 #include "ast/array_decl_plugin.h"
 #include "ast/ast_pp.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/rewriter/bool_rewriter.h"
 #include "muz/base/dl_util.h"
 #include "ast/for_each_expr.h"
diff --git a/src/muz/spacer/spacer_legacy_mev.h b/src/muz/spacer/spacer_legacy_mev.h
index bebf1d0db..ff8f63d1f 100644
--- a/src/muz/spacer/spacer_legacy_mev.h
+++ b/src/muz/spacer/spacer_legacy_mev.h
@@ -10,7 +10,6 @@ Copyright (c) 2017 Arie Gurfinkel
 #include "ast/ast_pp.h"
 #include "util/obj_hashtable.h"
 #include "util/ref_vector.h"
-#include "ast/simplifier/simplifier.h"
 #include "util/trace.h"
 #include "util/vector.h"
 #include "ast/arith_decl_plugin.h"
diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h
index 5bbb84bfb..546b7df5b 100644
--- a/src/muz/spacer/spacer_util.h
+++ b/src/muz/spacer/spacer_util.h
@@ -26,12 +26,13 @@ Revision History:
 #include "ast/ast_pp.h"
 #include "util/obj_hashtable.h"
 #include "util/ref_vector.h"
-#include "ast/simplifier/simplifier.h"
 #include "util/trace.h"
 #include "util/vector.h"
 #include "ast/arith_decl_plugin.h"
 #include "ast/array_decl_plugin.h"
 #include "ast/bv_decl_plugin.h"
+#include "ast/ast_util.h"
+#include "ast/expr_map.h"
 #include "model/model.h"
 
 #include "util/stopwatch.h"

From 14e6b5b50008134a0d92379a69a4d537a7f2b2d0 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 01:38:55 -0700
Subject: [PATCH 23/74] mising files

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/maximize_ac_sharing.cpp | 175 +++++++++++++++++++++++
 src/ast/rewriter/maximize_ac_sharing.h   | 125 ++++++++++++++++
 2 files changed, 300 insertions(+)
 create mode 100644 src/ast/rewriter/maximize_ac_sharing.cpp
 create mode 100644 src/ast/rewriter/maximize_ac_sharing.h

diff --git a/src/ast/rewriter/maximize_ac_sharing.cpp b/src/ast/rewriter/maximize_ac_sharing.cpp
new file mode 100644
index 000000000..b560132db
--- /dev/null
+++ b/src/ast/rewriter/maximize_ac_sharing.cpp
@@ -0,0 +1,175 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    maximize_ac_sharing.cpp
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-10-22.
+
+Revision History:
+
+--*/
+
+#include "ast/rewriter/maximize_ac_sharing.h"
+#include "ast/ast_pp.h"
+
+
+void maximize_ac_sharing::register_kind(decl_kind k) {
+    m_kinds.push_back(k);
+}
+
+
+br_status maximize_ac_sharing::reduce_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result, proof_ref& result_pr) {
+    decl_kind k = f->get_kind();
+    if (!f->is_associative())
+        return BR_FAILED;
+    if (num_args <= 2)
+        return BR_FAILED;
+    if (std::find(m_kinds.begin(), m_kinds.end(), k) == m_kinds.end())
+        return BR_FAILED;
+    ptr_buffer<expr, 128> _args;
+    expr * numeral = 0;
+    if (is_numeral(args[0])) {
+        numeral = args[0];
+        for (unsigned i = 1; i < num_args; i++) 
+            _args.push_back(args[i]);
+        num_args--;
+    }
+    else {
+        _args.append(num_args, args);
+    }
+
+    TRACE("ac_sharing_detail", tout << "before-reuse: num_args: " << num_args << "\n";);
+
+#define MAX_NUM_ARGS_FOR_OPT 128
+
+    // Try to reuse already created circuits.
+    TRACE("ac_sharing_detail", tout << "args: "; for (unsigned i = 0; i < num_args; i++) tout << mk_pp(_args[i], m) << "\n";);
+    try_to_reuse:
+    if (num_args > 1 && num_args < MAX_NUM_ARGS_FOR_OPT) {
+        for (unsigned i = 0; i < num_args - 1; i++) {
+            for (unsigned j = i + 1; j < num_args; j++) {
+                if (contains(f, _args[i], _args[j])) {
+                    TRACE("ac_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";);
+                    _args[i] = m.mk_app(f, _args[i], _args[j]);
+                    SASSERT(num_args > 1);
+                    for (unsigned w = j; w < num_args - 1; w++) {
+                        _args[w] = _args[w+1];
+                    }
+                    num_args--;
+                    goto try_to_reuse;
+                }
+            }
+        }
+    }
+
+    
+    // Create "tree-like circuit"
+    while (true) {
+        TRACE("ac_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";);
+        unsigned j  = 0;
+        for (unsigned i = 0; i < num_args; i += 2, j++) {
+            if (i == num_args - 1) {
+                _args[j] = _args[i];
+            }
+            else {
+                insert(f, _args[i], _args[i+1]);
+                _args[j] = m.mk_app(f, _args[i], _args[i+1]);
+            }
+        }
+        num_args = j;
+        if (num_args == 1) {
+            if (numeral == 0) { 
+                result = _args[0];
+            }
+            else {
+                result = m.mk_app(f, numeral, _args[0]);
+            }
+            TRACE("ac_sharing_detail", tout << "result: " << mk_pp(result, m) << "\n";);
+            return BR_DONE;
+        }
+    }
+
+    UNREACHABLE();
+    return BR_FAILED;
+}
+
+bool maximize_ac_sharing::contains(func_decl * f, expr * arg1, expr * arg2) {
+    entry e(f, arg1, arg2);
+    return m_cache.contains(&e);
+}
+
+void maximize_ac_sharing::insert(func_decl * f, expr * arg1, expr * arg2) {
+    entry * e = new (m_region) entry(f, arg1, arg2);
+    m_entries.push_back(e);
+    m_cache.insert(e);
+    m.inc_ref(arg1);
+    m.inc_ref(arg2);
+}
+    
+maximize_ac_sharing::maximize_ac_sharing(ast_manager & m):
+    m(m),
+    m_init(false) {
+}
+
+maximize_ac_sharing::~maximize_ac_sharing() {
+    restore_entries(0);
+}
+
+
+void maximize_ac_sharing::push_scope() {
+    init();
+    m_scopes.push_back(m_entries.size());
+    m_region.push_scope();
+}
+
+void maximize_ac_sharing::pop_scope(unsigned num_scopes) {
+    SASSERT(num_scopes <= m_scopes.size());
+    unsigned new_lvl    = m_scopes.size() - num_scopes;
+    unsigned old_lim    = m_scopes[new_lvl];
+    restore_entries(old_lim);
+    m_region.pop_scope(num_scopes);
+    m_scopes.shrink(new_lvl);
+}
+
+void maximize_ac_sharing::restore_entries(unsigned old_lim) {
+    unsigned i = m_entries.size();
+    while (i != old_lim) {
+        --i;
+        entry * e = m_entries[i];
+        m.dec_ref(e->m_arg1);
+        m.dec_ref(e->m_arg2);
+    }
+    m_entries.shrink(old_lim);
+}
+
+void maximize_ac_sharing::reset() {
+    restore_entries(0);
+    m_entries.reset();
+    m_cache.reset();
+    m_region.reset();
+    m_scopes.reset();
+}
+
+void maximize_bv_sharing::init_core() {
+    register_kind(OP_BADD);
+    register_kind(OP_BMUL);
+    register_kind(OP_BOR);
+    register_kind(OP_BAND);
+}
+
+bool maximize_bv_sharing::is_numeral(expr * n) const {
+    return m_util.is_numeral(n);
+}
+
+maximize_bv_sharing::maximize_bv_sharing(ast_manager & m):
+    maximize_ac_sharing(m),
+    m_util(m) {
+}
diff --git a/src/ast/rewriter/maximize_ac_sharing.h b/src/ast/rewriter/maximize_ac_sharing.h
new file mode 100644
index 000000000..c0ee0d09f
--- /dev/null
+++ b/src/ast/rewriter/maximize_ac_sharing.h
@@ -0,0 +1,125 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    maximize_ac_sharing.h
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-10-22.
+
+Revision History:
+
+--*/
+#ifndef MAXIMIZE_AC_SHARING_H_
+#define MAXIMIZE_AC_SHARING_H_
+
+#include "util/hashtable.h"
+#include "util/region.h"
+#include "ast/bv_decl_plugin.h"
+#include "ast/rewriter/rewriter.h"
+
+/**
+   \brief Functor used to maximize the amount of shared terms in an expression.
+   The idea is to rewrite AC terms to maximize sharing.
+   Example:
+
+   (f (bvadd a (bvadd b c)) (bvadd a (bvadd b d)))
+
+   is rewritten to:
+
+   (f (bvadd (bvadd a b) c) (bvadd (bvadd a b) d))
+
+   \warning This class uses an opportunistic heuristic to maximize sharing.
+   There is no guarantee that the optimal expression will be produced.
+*/
+class maximize_ac_sharing : public default_rewriter_cfg {
+    
+    struct entry {
+        func_decl * m_decl;
+        expr *      m_arg1;
+        expr *      m_arg2;
+
+        entry(func_decl * d = 0, expr * arg1 = 0, expr * arg2 = 0):m_decl(d), m_arg1(arg1), m_arg2(arg2) {
+            SASSERT((d == 0 && arg1 == 0 && arg2 == 0) || (d != 0 && arg1 != 0 && arg2 != 0));
+            if (arg1->get_id() > arg2->get_id())
+                std::swap(m_arg1, m_arg2);
+        }
+
+        unsigned hash() const {
+            unsigned a = m_decl->get_id();
+            unsigned b = m_arg1->get_id();
+            unsigned c = m_arg2->get_id();
+            mix(a,b,c);
+            return c;
+        }
+
+        bool operator==(entry const & e) const {
+            return m_decl == e.m_decl && m_arg1 == e.m_arg1 && m_arg2 == e.m_arg2;
+        }
+    };
+
+    typedef ptr_hashtable<entry, obj_ptr_hash<entry>, deref_eq<entry> > cache;
+
+protected:
+    void register_kind(decl_kind k);
+
+private:
+    ast_manager &     m;
+    bool              m_init;
+    region            m_region;
+    cache             m_cache;
+    ptr_vector<entry> m_entries;
+    unsigned_vector   m_scopes;
+    svector<decl_kind>    m_kinds; //!< kinds to be processed
+
+    bool contains(func_decl * f, expr * arg1, expr * arg2);
+    void insert(func_decl * f, expr * arg1, expr * arg2);
+    void restore_entries(unsigned old_lim);
+    void init() {
+        if (!m_init) {
+            init_core();
+            m_init = true;
+        }
+    }
+protected:
+    virtual void init_core() = 0; 
+    virtual bool is_numeral(expr * n) const = 0;
+public:
+    maximize_ac_sharing(ast_manager & m);
+    virtual ~maximize_ac_sharing();
+    void push_scope();
+    void pop_scope(unsigned num_scopes);
+    void reset();
+    br_status reduce_app(func_decl* f, unsigned n, expr * const* args, expr_ref& result, proof_ref& result_pr);
+
+};
+
+class maximize_bv_sharing : public maximize_ac_sharing {
+    bv_util m_util;
+protected:
+    virtual void init_core();
+    virtual bool is_numeral(expr * n) const;
+public:
+    maximize_bv_sharing(ast_manager & m);
+};
+
+class maximize_bv_sharing_rw : public rewriter_tpl<maximize_bv_sharing> {
+    maximize_bv_sharing m_cfg;
+public:
+    maximize_bv_sharing_rw(ast_manager& m):
+        rewriter_tpl<maximize_bv_sharing>(m, m.proofs_enabled(), m_cfg),
+        m_cfg(m)
+    {} 
+    void push_scope() { m_cfg.push_scope(); }
+    void pop_scope(unsigned n) { m_cfg.pop_scope(n); }
+    void reset() { m_cfg.reset(); }
+};
+
+#endif /* MAXIMIZE_AC_SHARING_H_ */
+

From 9b53646a342dd65b5f48a45cad11da81ed5199f6 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 01:43:06 -0700
Subject: [PATCH 24/74] mising files

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/smt/asserted_formulas_new.cpp | 619 ++++++++++++++++++++++++++++++
 src/smt/asserted_formulas_new.h   | 269 +++++++++++++
 2 files changed, 888 insertions(+)
 create mode 100644 src/smt/asserted_formulas_new.cpp
 create mode 100644 src/smt/asserted_formulas_new.h

diff --git a/src/smt/asserted_formulas_new.cpp b/src/smt/asserted_formulas_new.cpp
new file mode 100644
index 000000000..51bba9f7f
--- /dev/null
+++ b/src/smt/asserted_formulas_new.cpp
@@ -0,0 +1,619 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    asserted_formulas_new.cpp
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-06-11.
+
+Revision History:
+
+--*/
+#include "util/warning.h"
+#include "ast/ast_ll_pp.h"
+#include "ast/ast_pp.h"
+#include "ast/for_each_expr.h"
+#include "ast/well_sorted.h"
+#include "ast/rewriter/rewriter_def.h"
+#include "ast/normal_forms/nnf.h"
+#include "ast/pattern/pattern_inference.h"
+#include "ast/macros/quasi_macros.h"
+#include "smt/asserted_formulas_new.h"
+
+asserted_formulas_new::asserted_formulas_new(ast_manager & m, smt_params & p):
+    m(m),
+    m_params(p),
+    m_rewriter(m),
+    m_substitution(m),
+    m_scoped_substitution(m_substitution),
+    m_defined_names(m),
+    m_static_features(m),
+    m_qhead(0),
+    m_macro_manager(m),
+    m_bv_sharing(m),
+    m_inconsistent(false), 
+    m_has_quantifiers(false),
+    m_reduce_asserted_formulas(*this),
+    m_distribute_forall(*this),
+    m_pattern_inference(*this),
+    m_refine_inj_axiom(*this),
+    m_max_bv_sharing_fn(*this),
+    m_elim_term_ite(*this),
+    m_pull_cheap_ite_trees(*this),
+    m_pull_nested_quantifiers(*this),
+    m_elim_bvs_from_quantifiers(*this),
+    m_cheap_quant_fourier_motzkin(*this),
+    m_apply_bit2int(*this),
+    m_lift_ite(*this),
+    m_ng_lift_ite(*this),
+    m_find_macros(*this),
+    m_propagate_values(*this), 
+    m_nnf_cnf(*this), 
+    m_apply_quasi_macros(*this) {
+
+    m_macro_finder = alloc(macro_finder, m, m_macro_manager);
+}
+
+void asserted_formulas_new::setup() {
+    switch (m_params.m_lift_ite) {
+    case LI_FULL:
+        m_params.m_ng_lift_ite = LI_NONE; 
+        break;
+    case LI_CONSERVATIVE:
+        if (m_params.m_ng_lift_ite == LI_CONSERVATIVE)
+            m_params.m_ng_lift_ite = LI_NONE;
+        break;
+    default:
+        break;
+    }
+ 
+    if (m_params.m_relevancy_lvl == 0)
+        m_params.m_relevancy_lemma = false;
+}
+
+
+asserted_formulas_new::~asserted_formulas_new() {
+}
+
+void asserted_formulas_new::push_assertion(expr * e, proof * pr, vector<justified_expr>& result) {
+    if (inconsistent()) {
+        SASSERT(!result.empty());
+        return;
+    }
+    expr* e1 = 0;
+    if (m.is_false(e)) {
+        result.push_back(justified_expr(m, e, pr));
+        m_inconsistent = true;        
+    }
+    else if (m.is_true(e)) {
+        // skip
+    }
+    else if (m.is_and(e)) {
+        for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) {
+            expr* arg = to_app(e)->get_arg(i);
+            proof_ref _pr(m.mk_and_elim(pr, i), m);
+            push_assertion(arg, _pr, result);
+        }
+    }
+    else if (m.is_not(e, e1) && m.is_or(e1)) {
+        for (unsigned i = 0; i < to_app(e1)->get_num_args(); ++i) {
+            expr* arg = to_app(e1)->get_arg(i), *e2;
+            proof_ref _pr(m.mk_not_or_elim(pr, i), m);
+            if (m.is_not(arg, e2)) {
+                push_assertion(e2, _pr, result);
+            }
+            else {
+                expr_ref narg(m.mk_not(arg), m);
+                push_assertion(narg, _pr, result);
+            }
+        }        
+    }
+    else {
+        result.push_back(justified_expr(m, e, pr));
+    }
+}
+
+void asserted_formulas_new::set_eliminate_and(bool flag) {
+    params_ref p;
+    p.set_bool("elim_and", true);
+    m_rewriter.updt_params(p);
+    flush_cache();
+}
+
+
+void asserted_formulas_new::assert_expr(expr * e, proof * _in_pr) {
+    proof_ref  in_pr(_in_pr, m), pr(_in_pr, m);
+    expr_ref   r(e, m);
+
+    if (inconsistent()) 
+        return;
+
+    m_has_quantifiers |= ::has_quantifiers(e);
+
+    if (m_params.m_preprocess) {
+        TRACE("assert_expr_bug", tout << r << "\n";);
+        set_eliminate_and(false); // do not eliminate and before nnf.
+        m_rewriter(e, r, pr);
+        if (m.proofs_enabled()) {
+            if (e == r)
+                pr = in_pr;
+            else
+                pr = m.mk_modus_ponens(in_pr, pr);
+        }
+        TRACE("assert_expr_bug", tout << "after...\n" << r << "\n";);
+    }
+    push_assertion(r, pr, m_formulas);
+    TRACE("asserted_formulas_bug", tout << "after assert_expr\n"; display(tout););
+}
+
+void asserted_formulas_new::assert_expr(expr * e) {
+    if (!inconsistent()) 
+        assert_expr(e, m.mk_asserted(e));
+}
+
+void asserted_formulas_new::get_assertions(ptr_vector<expr> & result) const {
+    for (justified_expr const& je : m_formulas) result.push_back(je.get_fml());
+}
+
+void asserted_formulas_new::push_scope() {
+    SASSERT(inconsistent() || m_qhead == m_formulas.size() || m.canceled());
+    TRACE("asserted_formulas_new_scopes", tout << "push:\n"; display(tout););    
+    m_scoped_substitution.push();
+    m_scopes.push_back(scope());
+    scope & s = m_scopes.back();
+    s.m_formulas_lim = m_formulas.size();
+    SASSERT(inconsistent() || s.m_formulas_lim == m_qhead || m.canceled());
+    s.m_inconsistent_old = m_inconsistent;
+    m_defined_names.push();
+    m_bv_sharing.push_scope();
+    m_macro_manager.push_scope();
+    commit();
+}
+ 
+void asserted_formulas_new::pop_scope(unsigned num_scopes) {
+    TRACE("asserted_formulas_new_scopes", tout << "before pop " << num_scopes << "\n"; display(tout););
+    m_bv_sharing.pop_scope(num_scopes);
+    m_macro_manager.pop_scope(num_scopes);
+    unsigned new_lvl    = m_scopes.size() - num_scopes;
+    scope & s           = m_scopes[new_lvl];
+    m_inconsistent      = s.m_inconsistent_old;
+    m_defined_names.pop(num_scopes);
+    m_scoped_substitution.pop(num_scopes);
+    m_formulas.shrink(s.m_formulas_lim);
+    m_qhead    = s.m_formulas_lim;
+    m_scopes.shrink(new_lvl);
+    flush_cache();
+    TRACE("asserted_formulas_new_scopes", tout << "after pop " << num_scopes << "\n"; display(tout););
+}
+
+void asserted_formulas_new::reset() {
+    m_defined_names.reset();
+    m_qhead = 0;
+    m_formulas.reset();
+    m_macro_manager.reset();
+    m_bv_sharing.reset();
+    m_rewriter.reset();
+    m_inconsistent = false;
+}
+
+bool asserted_formulas_new::check_well_sorted() const {
+    for (justified_expr const& je : m_formulas) {
+        if (!is_well_sorted(m, je.get_fml())) return false; 
+    }
+    return true;
+}
+
+void asserted_formulas_new::reduce() {
+    if (inconsistent())
+        return;
+    if (canceled())
+        return;
+    if (m_qhead == m_formulas.size())
+        return;
+    if (!m_params.m_preprocess) 
+        return;    
+    if (m_macro_manager.has_macros()) 
+        expand_macros();
+
+    TRACE("before_reduce", display(tout););
+    CASSERT("well_sorted", check_well_sorted());
+          
+    set_eliminate_and(false); // do not eliminate and before nnf.
+    if (!invoke(m_propagate_values)) return;
+    if (!invoke(m_find_macros)) return;
+    if (!invoke(m_nnf_cnf)) return;
+    set_eliminate_and(true);
+    if (!invoke(m_reduce_asserted_formulas)) return; 
+    if (!invoke(m_pull_cheap_ite_trees)) return;
+    if (!invoke(m_pull_nested_quantifiers)) return;
+    if (!invoke(m_lift_ite)) return;
+    if (!invoke(m_ng_lift_ite)) return;
+    if (!invoke(m_elim_term_ite)) return;
+    if (!invoke(m_refine_inj_axiom)) return;
+    if (!invoke(m_distribute_forall)) return;
+    if (!invoke(m_find_macros)) return;
+    if (!invoke(m_apply_quasi_macros)) return;
+    if (!invoke(m_apply_bit2int)) return;
+    if (!invoke(m_cheap_quant_fourier_motzkin)) return;
+    if (!invoke(m_pattern_inference)) return;
+    if (!invoke(m_max_bv_sharing_fn)) return;
+    if (!invoke(m_elim_bvs_from_quantifiers)) return;
+    if (!invoke(m_reduce_asserted_formulas)) return;
+
+    IF_VERBOSE(10, verbose_stream() << "(smt.simplifier-done)\n";);
+    TRACE("after_reduce", display(tout););
+    TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited););
+    TRACE("macros", m_macro_manager.display(tout););
+    flush_cache();
+    CASSERT("well_sorted",check_well_sorted());
+}
+
+
+unsigned asserted_formulas_new::get_formulas_last_level() const {
+    if (m_scopes.empty()) {
+        return 0;
+    }
+    else {
+        return m_scopes.back().m_formulas_lim;
+    }
+}
+
+bool asserted_formulas_new::invoke(simplify_fmls& s) {
+    if (!s.should_apply()) return true;
+    IF_VERBOSE(10, verbose_stream() << "(smt." << s.id() << ")\n";);
+    s();
+    IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); 
+    TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); 
+    CASSERT("well_sorted",check_well_sorted());                         
+    if (inconsistent() || canceled()) {                                 
+        TRACE("after_reduce", display(tout););                          
+        TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); 
+        return false;
+    }                
+    else {
+        return true;
+    }
+}
+
+void asserted_formulas_new::display(std::ostream & out) const {
+    out << "asserted formulas:\n";
+    for (unsigned i = 0; i < m_formulas.size(); i++) {
+        if (i == m_qhead)
+            out << "[HEAD] ==>\n";
+        out << mk_pp(m_formulas[i].get_fml(), m) << "\n";
+    }
+    out << "inconsistent: " << inconsistent() << "\n";
+}
+
+void asserted_formulas_new::display_ll(std::ostream & out, ast_mark & pp_visited) const {
+    if (!m_formulas.empty()) {
+        for (justified_expr const& f : m_formulas) 
+            ast_def_ll_pp(out, m, f.get_fml(), pp_visited, true, false);
+        out << "asserted formulas:\n";
+        for (justified_expr const& f : m_formulas) 
+            out << "#" << f.get_fml()->get_id() << " ";
+        out << "\n";
+    }
+}
+
+void asserted_formulas_new::collect_statistics(statistics & st) const {
+}
+
+
+void asserted_formulas_new::swap_asserted_formulas(vector<justified_expr>& formulas) {
+    SASSERT(!inconsistent() || !formulas.empty());
+    m_formulas.shrink(m_qhead);
+    m_formulas.append(formulas);
+}
+
+void asserted_formulas_new::find_macros_fn::operator()() {
+    TRACE("before_find_macros", af.display(tout););
+    af.find_macros_core();
+    TRACE("after_find_macros", af.display(tout););
+}
+
+void asserted_formulas_new::find_macros_core() {
+    vector<justified_expr> new_fmls;
+    unsigned sz = m_formulas.size();
+    (*m_macro_finder)(sz - m_qhead, m_formulas.c_ptr() + m_qhead, new_fmls);
+    swap_asserted_formulas(new_fmls);
+    reduce_and_solve();
+}
+
+
+void asserted_formulas_new::expand_macros() {
+    IF_IVERBOSE(10, verbose_stream() << "(smt.expand-macros)\n";);
+    find_macros_core();
+}
+
+void asserted_formulas_new::apply_quasi_macros() {
+    TRACE("before_quasi_macros", display(tout););
+    vector<justified_expr> new_fmls;
+    quasi_macros proc(m, m_macro_manager);    
+    while (proc(m_formulas.size() - m_qhead, 
+                m_formulas.c_ptr() + m_qhead, 
+                new_fmls)) {
+        swap_asserted_formulas(new_fmls);
+        new_fmls.reset();
+    }
+    TRACE("after_quasi_macros", display(tout););
+    reduce_and_solve();
+}
+
+void asserted_formulas_new::nnf_cnf() {
+    nnf              apply_nnf(m, m_defined_names);
+    vector<justified_expr> new_fmls;
+    expr_ref_vector  push_todo(m);
+    proof_ref_vector push_todo_prs(m);
+    
+    unsigned i  = m_qhead;
+    unsigned sz = m_formulas.size();
+    TRACE("nnf_bug", tout << "i: " << i << " sz: " << sz << "\n";);
+    for (; i < sz; i++) {
+        expr * n    = m_formulas[i].get_fml();
+        TRACE("nnf_bug", tout << "processing:\n" << mk_pp(n, m) << "\n";);
+        proof * pr  = m_formulas[i].get_proof();
+        expr_ref   r1(m);
+        proof_ref  pr1(m);
+        push_todo.reset();
+        push_todo_prs.reset();
+        CASSERT("well_sorted", is_well_sorted(m, n));
+        apply_nnf(n, push_todo, push_todo_prs, r1, pr1);
+        CASSERT("well_sorted",is_well_sorted(m, r1));
+        pr = m.mk_modus_ponens(pr, pr1);
+        push_todo.push_back(r1);
+        push_todo_prs.push_back(pr);
+
+        if (canceled()) {
+            return;
+        }
+        unsigned sz2 = push_todo.size();
+        for (unsigned k = 0; k < sz2; k++) {
+            expr * n   = push_todo.get(k);
+            pr = 0;
+            m_rewriter(n, r1, pr1);
+            CASSERT("well_sorted",is_well_sorted(m, r1));
+            if (canceled()) {
+                return;
+            }                    
+            if (m.proofs_enabled())
+                pr = m.mk_modus_ponens(push_todo_prs.get(k), pr1);
+            push_assertion(r1, pr, new_fmls);
+        }
+    }
+    swap_asserted_formulas(new_fmls);
+}
+
+void asserted_formulas_new::simplify_fmls::operator()() {
+    vector<justified_expr> new_fmls;
+    unsigned sz = af.m_formulas.size();                                
+    for (unsigned i = af.m_qhead; i < sz; i++) {                                           
+        auto& j = af.m_formulas[i];
+        expr_ref result(m);
+        proof_ref result_pr(m);
+        simplify(j, result, result_pr);
+        if (m.proofs_enabled()) {
+            if (!result_pr) result_pr = m.mk_rewrite(j.get_fml(), result);
+            result_pr = m.mk_modus_ponens(j.get_proof(), result_pr);
+        }
+        if (j.get_fml() == result) {
+            new_fmls.push_back(j);
+        }
+        else {
+            af.push_assertion(result, result_pr, new_fmls);
+        }
+        if (af.canceled()) return;
+    }    
+    af.swap_asserted_formulas(new_fmls);                    
+    TRACE("asserted_formulas", af.display(tout););
+    post_op();
+}
+
+
+void asserted_formulas_new::reduce_and_solve() {
+    IF_IVERBOSE(10, verbose_stream() << "(smt.reducing)\n";);
+    flush_cache(); // collect garbage
+    m_reduce_asserted_formulas();
+}
+
+
+void asserted_formulas_new::commit() {
+    commit(m_formulas.size());
+}
+
+void asserted_formulas_new::commit(unsigned new_qhead) {
+    m_macro_manager.mark_forbidden(new_qhead - m_qhead, m_formulas.c_ptr() + m_qhead);
+    m_expr2depth.reset();
+    for (unsigned i = m_qhead; i < new_qhead; ++i) {
+        justified_expr const& j = m_formulas[i];
+        update_substitution(j.get_fml(), j.get_proof());
+    }
+    m_qhead = new_qhead;
+}
+
+void asserted_formulas_new::propagate_values() {
+    TRACE("propagate_values", tout << "before:\n"; display(tout););
+    flush_cache();
+
+    unsigned num_prop = 0;
+    while (true) {
+        m_expr2depth.reset();
+        m_scoped_substitution.push();
+        unsigned prop = num_prop;
+        TRACE("propagate_values", tout << "before:\n"; display(tout););
+        IF_IVERBOSE(10, verbose_stream() << "(smt.propagate-values)\n";);
+        unsigned i  = m_qhead;
+        unsigned sz = m_formulas.size();
+        for (; i < sz; i++) {
+            prop += propagate_values(i);
+        }
+        flush_cache();
+        m_scoped_substitution.pop(1);
+        m_expr2depth.reset();
+        m_scoped_substitution.push();
+        TRACE("propagate_values", tout << "middle:\n"; display(tout););
+        i = sz;
+        while (i > m_qhead) {
+            --i;
+            prop += propagate_values(i);
+        }
+        m_scoped_substitution.pop(1);
+        flush_cache();
+        TRACE("propagate_values", tout << "after:\n"; display(tout););
+        if (num_prop == prop) {
+            break;
+        }
+        num_prop = prop;
+    }
+    if (num_prop > 0)
+        m_reduce_asserted_formulas();
+}
+
+unsigned asserted_formulas_new::propagate_values(unsigned i) {
+    expr * n = m_formulas[i].get_fml();
+    expr_ref new_n(m);                                                  
+    proof_ref new_pr(m);                                                
+    m_rewriter(n, new_n, new_pr);                                       
+    if (m.proofs_enabled()) {                                           
+        proof * pr  = m_formulas[i].get_proof();
+        new_pr = m.mk_modus_ponens(pr, new_pr);                         
+    }
+    m_formulas[i] = justified_expr(m, new_n, new_pr);
+    update_substitution(new_n, new_pr);
+    return n != new_n ? 1 : 0;
+}
+
+void asserted_formulas_new::update_substitution(expr* n, proof* pr) {
+    expr* lhs, *rhs, *n1;
+    if (is_ground(n) && (m.is_eq(n, lhs, rhs) || m.is_iff(n, lhs, rhs))) {
+        compute_depth(lhs);
+        compute_depth(rhs);
+        if (is_gt(lhs, rhs)) {
+            m_scoped_substitution.insert(lhs, rhs, pr);
+            return;
+        }
+        if (is_gt(rhs, lhs)) {
+            m_scoped_substitution.insert(rhs, lhs, m.mk_symmetry(pr));
+            return;
+        }
+    }
+    if (m.is_not(n, n1)) {
+        m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr)); 
+    }
+    else {
+        m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr)); 
+    }
+}
+
+/**
+   \brief implement a Knuth-Bendix ordering on expressions.
+*/
+
+bool asserted_formulas_new::is_gt(expr* lhs, expr* rhs) {
+    if (lhs == rhs) {
+        return false;
+    }
+    if (m.is_value(rhs)) {
+        return true;
+    }
+    SASSERT(is_ground(lhs) && is_ground(rhs));
+    if (depth(lhs) > depth(rhs)) {
+        return true;
+    }
+    if (depth(lhs) == depth(rhs) && is_app(lhs) && is_app(rhs)) {
+        app* l = to_app(lhs);
+        app* r = to_app(rhs);
+        if (l->get_decl()->get_id() != r->get_decl()->get_id()) {
+            return l->get_decl()->get_id() > r->get_decl()->get_id();
+        }
+        if (l->get_num_args() != r->get_num_args()) {
+            return l->get_num_args() > r->get_num_args();
+        }
+        for (unsigned i = 0; i < l->get_num_args(); ++i) {
+            if (l->get_arg(i) != r->get_arg(i)) {
+                return is_gt(l->get_arg(i), r->get_arg(i));
+            }
+        }
+        UNREACHABLE();
+    }
+    
+    return false;
+}
+
+void asserted_formulas_new::compute_depth(expr* e) {
+    ptr_vector<expr> todo;
+    todo.push_back(e);    
+    while (!todo.empty()) {
+        e = todo.back();
+        unsigned d = 0;
+        if (m_expr2depth.contains(e)) {
+            todo.pop_back();
+            continue;
+        }
+        if (is_app(e)) {
+            app* a = to_app(e);
+            bool visited = true;
+            for (expr* arg : *a) {
+                unsigned d1 = 0;
+                if (m_expr2depth.find(arg, d1)) {
+                    d = std::max(d, d1);
+                }
+                else {
+                    visited = false;
+                    todo.push_back(arg);
+                }
+            }
+            if (!visited) {
+                continue;
+            }
+        }
+        todo.pop_back();
+        m_expr2depth.insert(e, d + 1);
+    }
+}
+
+proof * asserted_formulas_new::get_inconsistency_proof() const {
+    if (!inconsistent())
+        return 0;
+    if (!m.proofs_enabled())
+        return 0;
+    for (justified_expr const& j : m_formulas) {
+        if (m.is_false(j.get_fml()))
+            return j.get_proof();
+    }
+    UNREACHABLE();
+    return 0;
+}
+
+void asserted_formulas_new::refine_inj_axiom_fn::simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { 
+    expr* f = j.get_fml();
+    if (is_quantifier(f) && simplify_inj_axiom(m, to_quantifier(f), n)) {
+        TRACE("inj_axiom", tout << "simplifying...\n" << mk_pp(f, m) << "\n" << n << "\n";);
+    }
+    else {
+        n = j.get_fml();
+    }
+}
+
+
+unsigned asserted_formulas_new::get_total_size() const {
+    expr_mark visited;
+    unsigned r  = 0;
+    for (justified_expr const& j : m_formulas)
+        r += get_num_exprs(j.get_fml(), visited);
+    return r;
+}
+
+#ifdef Z3DEBUG
+void pp(asserted_formulas_new & f) {
+    f.display(std::cout);
+}
+#endif
+
diff --git a/src/smt/asserted_formulas_new.h b/src/smt/asserted_formulas_new.h
new file mode 100644
index 000000000..dc51c86ae
--- /dev/null
+++ b/src/smt/asserted_formulas_new.h
@@ -0,0 +1,269 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    asserted_formulas_new.h
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-06-11.
+
+Revision History:
+
+--*/
+#ifndef ASSERTED_FORMULAS_NEW_H_
+#define ASSERTED_FORMULAS_NEW_H_
+
+#include "util/statistics.h"
+#include "ast/static_features.h"
+#include "ast/expr_substitution.h"
+#include "ast/rewriter/th_rewriter.h"
+#include "ast/rewriter/bit2int.h"
+#include "ast/rewriter/maximize_ac_sharing.h"
+#include "ast/rewriter/distribute_forall.h"
+#include "ast/rewriter/pull_ite_tree.h"
+#include "ast/rewriter/push_app_ite.h"
+#include "ast/rewriter/inj_axiom.h"
+#include "ast/rewriter/bv_elim2.h"
+#include "ast/rewriter/der.h"
+#include "ast/rewriter/elim_bounds2.h"
+#include "ast/macros/macro_manager.h"
+#include "ast/macros/macro_finder.h"
+#include "ast/normal_forms/defined_names.h"
+#include "ast/normal_forms/pull_quant.h"
+#include "ast/pattern/pattern_inference.h"
+#include "smt/params/smt_params.h"
+#include "smt/elim_term_ite.h"
+
+
+class asserted_formulas_new {
+    
+    ast_manager &               m;
+    smt_params &                m_params;
+    th_rewriter                 m_rewriter;
+    expr_substitution           m_substitution;
+    scoped_expr_substitution    m_scoped_substitution;
+    defined_names               m_defined_names;
+    static_features             m_static_features;
+    vector<justified_expr>      m_formulas;
+    unsigned                    m_qhead;
+    macro_manager               m_macro_manager;
+    scoped_ptr<macro_finder>    m_macro_finder;  
+    maximize_bv_sharing_rw      m_bv_sharing;
+    bool                        m_inconsistent;
+    bool                        m_has_quantifiers;
+    struct scope {
+        unsigned                m_formulas_lim;
+        bool                    m_inconsistent_old;
+    };
+    svector<scope>              m_scopes;
+    obj_map<expr, unsigned>     m_expr2depth;
+
+    class simplify_fmls {
+    protected:
+        asserted_formulas_new& af;
+        ast_manager&           m;
+        char const*            m_id;
+    public:
+        simplify_fmls(asserted_formulas_new& af, char const* id): af(af), m(af.m), m_id(id) {}
+        char const* id() const { return m_id; }
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) = 0;
+        virtual bool should_apply() const { return true;}
+        virtual void post_op() {}
+        virtual void operator()();
+    };
+
+    class reduce_asserted_formulas_fn : public simplify_fmls {
+    public:
+        reduce_asserted_formulas_fn(asserted_formulas_new& af): simplify_fmls(af, "reduce-asserted") {}
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { af.m_rewriter(j.get_fml(), n, p); }
+    };
+
+    class find_macros_fn : public simplify_fmls {
+    public:
+        find_macros_fn(asserted_formulas_new& af): simplify_fmls(af, "find-macros") {}
+        virtual void operator()();
+        virtual bool should_apply() const { return af.m_params.m_macro_finder && af.has_quantifiers(); }
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); }
+    };
+
+    class apply_quasi_macros_fn : public simplify_fmls {
+    public:
+        apply_quasi_macros_fn(asserted_formulas_new& af): simplify_fmls(af, "find-quasi-macros") {}
+        virtual void operator()() { af.apply_quasi_macros(); }
+        virtual bool should_apply() const { return af.m_params.m_quasi_macros && af.has_quantifiers(); }
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); }
+    };
+
+    class nnf_cnf_fn : public simplify_fmls {
+    public:
+        nnf_cnf_fn(asserted_formulas_new& af): simplify_fmls(af, "nnf-cnf") {}
+        virtual void operator()() { af.nnf_cnf(); }
+        virtual bool should_apply() const { return af.m_params.m_nnf_cnf || (af.m_params.m_mbqi && af.has_quantifiers()); }
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); }
+    };
+
+    class propagate_values_fn : public simplify_fmls {
+    public:
+        propagate_values_fn(asserted_formulas_new& af): simplify_fmls(af, "propagate-values") {}
+        virtual void operator()() { af.propagate_values(); }
+        virtual bool should_apply() const { return af.m_params.m_propagate_values; }
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); }
+    };
+
+    class distribute_forall_fn : public simplify_fmls {
+        distribute_forall m_functor;
+    public:
+        distribute_forall_fn(asserted_formulas_new& af): simplify_fmls(af, "distribute-forall"), m_functor(af.m) {}
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { m_functor(j.get_fml(), n); }
+        virtual bool should_apply() const { return af.m_params.m_distribute_forall && af.has_quantifiers(); }
+        virtual void post_op() { af.reduce_and_solve();  TRACE("asserted_formulas", af.display(tout);); }
+    };
+
+    class pattern_inference_fn : public simplify_fmls {
+        pattern_inference_rw m_infer;
+    public:
+        pattern_inference_fn(asserted_formulas_new& af): simplify_fmls(af, "pattern-inference"), m_infer(af.m, af.m_params) {}
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { m_infer(j.get_fml(), n, p); }
+        virtual bool should_apply() const { return af.m_params.m_ematching && af.has_quantifiers(); }
+    };
+
+    class refine_inj_axiom_fn : public simplify_fmls {
+    public:
+        refine_inj_axiom_fn(asserted_formulas_new& af): simplify_fmls(af, "refine-injectivity") {}
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p);
+        virtual bool should_apply() const { return af.m_params.m_refine_inj_axiom && af.has_quantifiers(); }
+    };
+
+    class max_bv_sharing_fn : public simplify_fmls {
+    public:
+        max_bv_sharing_fn(asserted_formulas_new& af): simplify_fmls(af, "maximizing-bv-sharing") {}
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { af.m_bv_sharing(j.get_fml(), n, p); }
+        virtual bool should_apply() const { return af.m_params.m_max_bv_sharing; }
+        virtual void post_op() { af.m_reduce_asserted_formulas(); }
+    };
+
+    class elim_term_ite_fn : public simplify_fmls {
+        elim_term_ite_rw m_elim;
+    public:
+        elim_term_ite_fn(asserted_formulas_new& af): simplify_fmls(af, "elim-term-ite"), m_elim(af.m, af.m_defined_names) {}
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { m_elim(j.get_fml(), n, p); }
+        virtual bool should_apply() const { return af.m_params.m_eliminate_term_ite && af.m_params.m_lift_ite != LI_FULL; }
+        virtual void post_op() { af.m_formulas.append(m_elim.new_defs()); af.reduce_and_solve(); m_elim.reset(); }
+    };
+
+#define MK_SIMPLIFIERA(NAME, FUNCTOR, MSG, APP, ARG, REDUCE)            \
+    class NAME : public simplify_fmls {                                 \
+        FUNCTOR m_functor;                                              \
+    public:                                                             \
+        NAME(asserted_formulas_new& af):simplify_fmls(af, MSG), m_functor ARG {} \
+        virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { \
+            m_functor(j.get_fml(), n, p);                               \
+        }                                                               \
+        virtual void post_op() { if (REDUCE) af.reduce_and_solve(); }   \
+        virtual bool should_apply() const { return APP; }               \
+    };                                                                  \
+
+#define MK_SIMPLIFIERF(NAME, FUNCTOR, MSG, APP, REDUCE) MK_SIMPLIFIERA(NAME, FUNCTOR, MSG, APP, (af.m), REDUCE)
+
+    MK_SIMPLIFIERF(pull_cheap_ite_trees, pull_cheap_ite_tree_rw, "pull-cheap-ite-trees", af.m_params.m_pull_cheap_ite_trees, false);
+    MK_SIMPLIFIERF(pull_nested_quantifiers, pull_nested_quant, "pull-nested-quantifiers", af.m_params.m_pull_nested_quantifiers && af.has_quantifiers(), false);
+    MK_SIMPLIFIERF(cheap_quant_fourier_motzkin, elim_bounds_rw, "cheap-fourier-motzkin", af.m_params.m_eliminate_bounds && af.has_quantifiers(), true);
+    MK_SIMPLIFIERF(elim_bvs_from_quantifiers, bv_elim_rw, "eliminate-bit-vectors-from-quantifiers", af.m_params.m_bb_quantifiers, true);
+    MK_SIMPLIFIERF(apply_bit2int, bit2int, "propagate-bit-vector-over-integers", af.m_params.m_simplify_bit2int, true);
+    MK_SIMPLIFIERA(lift_ite, push_app_ite_rw, "lift-ite", af.m_params.m_lift_ite != LI_NONE, (af.m, af.m_params.m_lift_ite == LI_CONSERVATIVE), true);
+    MK_SIMPLIFIERA(ng_lift_ite, ng_push_app_ite_rw, "lift-ite", af.m_params.m_ng_lift_ite != LI_NONE, (af.m, af.m_params.m_ng_lift_ite == LI_CONSERVATIVE), true);
+
+
+    reduce_asserted_formulas_fn m_reduce_asserted_formulas;
+    distribute_forall_fn        m_distribute_forall;
+    pattern_inference_fn        m_pattern_inference;
+    refine_inj_axiom_fn         m_refine_inj_axiom;
+    max_bv_sharing_fn           m_max_bv_sharing_fn;
+    elim_term_ite_fn            m_elim_term_ite;
+    pull_cheap_ite_trees        m_pull_cheap_ite_trees;
+    pull_nested_quantifiers     m_pull_nested_quantifiers;
+    elim_bvs_from_quantifiers   m_elim_bvs_from_quantifiers;
+    cheap_quant_fourier_motzkin m_cheap_quant_fourier_motzkin;
+    apply_bit2int               m_apply_bit2int;
+    lift_ite                    m_lift_ite;
+    ng_lift_ite                 m_ng_lift_ite;
+    find_macros_fn              m_find_macros;
+    propagate_values_fn         m_propagate_values;
+    nnf_cnf_fn                  m_nnf_cnf;
+    apply_quasi_macros_fn       m_apply_quasi_macros;
+
+    bool invoke(simplify_fmls& s);
+    void swap_asserted_formulas(vector<justified_expr>& new_fmls);
+    void push_assertion(expr * e, proof * pr, vector<justified_expr>& result);
+    bool canceled() { return m.canceled(); }
+    bool check_well_sorted() const;
+    unsigned get_total_size() const;
+
+    void find_macros_core();
+    void expand_macros();
+    void apply_quasi_macros();
+    void nnf_cnf();
+    void reduce_and_solve();
+    void flush_cache() { m_rewriter.reset(); }
+    void set_eliminate_and(bool flag);
+    void propagate_values();
+    unsigned propagate_values(unsigned i);
+    void update_substitution(expr* n, proof* p);
+    bool is_gt(expr* lhs, expr* rhs);
+    void compute_depth(expr* e);
+    unsigned depth(expr* e) { return m_expr2depth[e]; }
+    bool pull_cheap_ite_trees();
+
+public:
+    asserted_formulas_new(ast_manager & m, smt_params & p);
+    ~asserted_formulas_new();
+
+    bool has_quantifiers() const { return m_has_quantifiers; }
+    void setup();
+    void assert_expr(expr * e, proof * in_pr);
+    void assert_expr(expr * e);
+    void reset();
+    void push_scope();
+    void pop_scope(unsigned num_scopes);
+    bool inconsistent() const { return m_inconsistent; }
+    proof * get_inconsistency_proof() const;
+    void reduce();
+    unsigned get_num_formulas() const { return m_formulas.size(); }
+    unsigned get_formulas_last_level() const;
+    unsigned get_qhead() const { return m_qhead; }
+    void commit(); 
+    void commit(unsigned new_qhead); 
+    expr *  get_formula(unsigned idx) const { return m_formulas[idx].get_fml(); }
+    proof * get_formula_proof(unsigned idx) const { return m_formulas[idx].get_proof(); }
+    
+    th_rewriter & get_rewriter() { return m_rewriter; }
+    void get_assertions(ptr_vector<expr> & result) const;
+    bool empty() const { return m_formulas.empty(); }
+    void display(std::ostream & out) const;
+    void display_ll(std::ostream & out, ast_mark & pp_visited) const;
+    void collect_statistics(statistics & st) const;
+    
+    // -----------------------------------
+    //
+    // Macros
+    //
+    // -----------------------------------
+    unsigned get_num_macros() const { return m_macro_manager.get_num_macros(); }
+    unsigned get_first_macro_last_level() const { return m_macro_manager.get_first_macro_last_level(); }
+    func_decl * get_macro_func_decl(unsigned i) const { return m_macro_manager.get_macro_func_decl(i); }
+    func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const { return m_macro_manager.get_macro_interpretation(i, interp); }
+    quantifier * get_macro_quantifier(func_decl * f) const { return m_macro_manager.get_macro_quantifier(f); }
+    // auxiliary function used to create a logic context based on a model.
+    void insert_macro(func_decl * f, quantifier * m, proof * pr) { m_macro_manager.insert(f, m, pr); }
+    void insert_macro(func_decl * f, quantifier * m, proof * pr, expr_dependency* dep) { m_macro_manager.insert(f, m, pr, dep); }
+
+};
+
+#endif /* ASSERTED_FORMULAS_NEW_H_ */
+

From e3e965883f562de3ecd91ecfba6dd20c88437438 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 01:55:03 -0700
Subject: [PATCH 25/74] mising files

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/smt/asserted_formulas_new.cpp | 28 +++++-----------------------
 src/smt/asserted_formulas_new.h   |  2 +-
 2 files changed, 6 insertions(+), 24 deletions(-)

diff --git a/src/smt/asserted_formulas_new.cpp b/src/smt/asserted_formulas_new.cpp
index 51bba9f7f..14c8615a2 100644
--- a/src/smt/asserted_formulas_new.cpp
+++ b/src/smt/asserted_formulas_new.cpp
@@ -84,7 +84,6 @@ asserted_formulas_new::~asserted_formulas_new() {
 
 void asserted_formulas_new::push_assertion(expr * e, proof * pr, vector<justified_expr>& result) {
     if (inconsistent()) {
-        SASSERT(!result.empty());
         return;
     }
     expr* e1 = 0;
@@ -106,13 +105,8 @@ void asserted_formulas_new::push_assertion(expr * e, proof * pr, vector<justifie
         for (unsigned i = 0; i < to_app(e1)->get_num_args(); ++i) {
             expr* arg = to_app(e1)->get_arg(i), *e2;
             proof_ref _pr(m.mk_not_or_elim(pr, i), m);
-            if (m.is_not(arg, e2)) {
-                push_assertion(e2, _pr, result);
-            }
-            else {
-                expr_ref narg(m.mk_not(arg), m);
-                push_assertion(narg, _pr, result);
-            }
+            expr_ref  narg(mk_not(m, arg), m);
+            push_assertion(narg, _pr, result);            
         }        
     }
     else {
@@ -154,8 +148,7 @@ void asserted_formulas_new::assert_expr(expr * e, proof * _in_pr) {
 }
 
 void asserted_formulas_new::assert_expr(expr * e) {
-    if (!inconsistent()) 
-        assert_expr(e, m.mk_asserted(e));
+    assert_expr(e, m.mk_asserted(e));
 }
 
 void asserted_formulas_new::get_assertions(ptr_vector<expr> & result) const {
@@ -220,8 +213,8 @@ void asserted_formulas_new::reduce() {
     if (!m_params.m_preprocess) 
         return;    
     if (m_macro_manager.has_macros()) 
-        expand_macros();
-
+        invoke(m_find_macros);
+    
     TRACE("before_reduce", display(tout););
     CASSERT("well_sorted", check_well_sorted());
           
@@ -313,11 +306,6 @@ void asserted_formulas_new::swap_asserted_formulas(vector<justified_expr>& formu
     m_formulas.append(formulas);
 }
 
-void asserted_formulas_new::find_macros_fn::operator()() {
-    TRACE("before_find_macros", af.display(tout););
-    af.find_macros_core();
-    TRACE("after_find_macros", af.display(tout););
-}
 
 void asserted_formulas_new::find_macros_core() {
     vector<justified_expr> new_fmls;
@@ -327,12 +315,6 @@ void asserted_formulas_new::find_macros_core() {
     reduce_and_solve();
 }
 
-
-void asserted_formulas_new::expand_macros() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.expand-macros)\n";);
-    find_macros_core();
-}
-
 void asserted_formulas_new::apply_quasi_macros() {
     TRACE("before_quasi_macros", display(tout););
     vector<justified_expr> new_fmls;
diff --git a/src/smt/asserted_formulas_new.h b/src/smt/asserted_formulas_new.h
index dc51c86ae..60af46dea 100644
--- a/src/smt/asserted_formulas_new.h
+++ b/src/smt/asserted_formulas_new.h
@@ -87,7 +87,7 @@ class asserted_formulas_new {
     class find_macros_fn : public simplify_fmls {
     public:
         find_macros_fn(asserted_formulas_new& af): simplify_fmls(af, "find-macros") {}
-        virtual void operator()();
+        virtual void operator()() { af.find_macros_core(); }
         virtual bool should_apply() const { return af.m_params.m_macro_finder && af.has_quantifiers(); }
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); }
     };

From ce3ab6b170151953dde32f3b6f365754f3feb662 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 02:04:59 -0700
Subject: [PATCH 26/74] mising files

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/ast.h                     | 12 ++++++------
 src/smt/asserted_formulas_new.cpp |  3 ++-
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/ast/ast.h b/src/ast/ast.h
index 408ca4063..34ea96653 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -2474,15 +2474,15 @@ class justified_expr {
     ast_manager& m;
     expr*        m_fml;
     proof*       m_proof;
- public:
- justified_expr(ast_manager& m, expr* fml, proof* p):
-    m(m),
-    m_fml(fml),
-    m_proof(p) {
+public:
+    justified_expr(ast_manager& m, expr* fml, proof* p):
+        m(m),
+        m_fml(fml),
+        m_proof(p) {
         m.inc_ref(fml);
         m.inc_ref(p);
     }
-
+    
     justified_expr& operator=(justified_expr& other) {
         SASSERT(&m == &other.m);
         if (this != &other) {
diff --git a/src/smt/asserted_formulas_new.cpp b/src/smt/asserted_formulas_new.cpp
index 14c8615a2..39c432b61 100644
--- a/src/smt/asserted_formulas_new.cpp
+++ b/src/smt/asserted_formulas_new.cpp
@@ -467,7 +467,8 @@ unsigned asserted_formulas_new::propagate_values(unsigned i) {
         proof * pr  = m_formulas[i].get_proof();
         new_pr = m.mk_modus_ponens(pr, new_pr);                         
     }
-    m_formulas[i] = justified_expr(m, new_n, new_pr);
+    justified_expr j(m, new_n, new_pr);
+    m_formulas[i] = j;
     update_substitution(new_n, new_pr);
     return n != new_n ? 1 : 0;
 }

From 2955b0c2efe27ef220e51749647fe9f7e5653836 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 03:05:34 -0700
Subject: [PATCH 27/74] removing more dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/muz/base/dl_rule.cpp               |   3 +-
 src/muz/spacer/spacer_qe_project.cpp   |   1 +
 src/qe/CMakeLists.txt                  |   2 -
 src/qe/nlarith_util.cpp                |  11 +-
 src/qe/qe.cpp                          |  22 +++-
 src/qe/qe.h                            |  34 ++---
 src/qe/qe_cmd.cpp                      |   3 +-
 src/qe/vsubst_tactic.cpp               | 169 -------------------------
 src/qe/vsubst_tactic.h                 |  33 -----
 src/smt/CMakeLists.txt                 |   2 +-
 src/smt/arith_eq_adapter.cpp           |   1 -
 src/smt/params/CMakeLists.txt          |   1 -
 src/smt/params/preprocessor_params.cpp |   1 -
 src/smt/params/preprocessor_params.h   |   6 +-
 src/tactic/bv/elim_small_bv_tactic.cpp |  14 +-
 15 files changed, 38 insertions(+), 265 deletions(-)
 delete mode 100644 src/qe/vsubst_tactic.cpp
 delete mode 100644 src/qe/vsubst_tactic.h

diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp
index 26a5b748e..367795c9b 100644
--- a/src/muz/base/dl_rule.cpp
+++ b/src/muz/base/dl_rule.cpp
@@ -44,6 +44,7 @@ Revision History:
 #include "tactic/filter_model_converter.h"
 #include "ast/scoped_proof.h"
 #include "ast/datatype_decl_plugin.h"
+#include "ast/ast_util.h"
 
 namespace datalog {
 
@@ -757,7 +758,7 @@ namespace datalog {
             );
 
             proof_ref pr(m);
-            qe::expr_quant_elim_star1 simpl(m, m_ctx.get_fparams());
+            qe::simplify_rewriter_star simpl(m);
             simpl(quant_tail, fixed_tail, pr);
         }
         else {
diff --git a/src/muz/spacer/spacer_qe_project.cpp b/src/muz/spacer/spacer_qe_project.cpp
index 0eec500e9..8dbf87ca5 100644
--- a/src/muz/spacer/spacer_qe_project.cpp
+++ b/src/muz/spacer/spacer_qe_project.cpp
@@ -25,6 +25,7 @@ Revision History:
 #include "ast/ast_pp.h"
 #include "ast/expr_functors.h"
 #include "ast/expr_substitution.h"
+#include "ast/ast_util.h"
 
 #include "ast/rewriter/expr_replacer.h"
 #include "ast/rewriter/expr_safe_replace.h"
diff --git a/src/qe/CMakeLists.txt b/src/qe/CMakeLists.txt
index 2d2cf9579..2e6052382 100644
--- a/src/qe/CMakeLists.txt
+++ b/src/qe/CMakeLists.txt
@@ -18,7 +18,6 @@ z3_add_component(qe
     qe_sat_tactic.cpp
     qe_tactic.cpp
     qsat.cpp
-    vsubst_tactic.cpp
   COMPONENT_DEPENDENCIES
     nlsat_tactic
     nlsat
@@ -31,5 +30,4 @@ z3_add_component(qe
     qe_sat_tactic.h
     qe_tactic.h
     qsat.h
-    vsubst_tactic.h
 )
diff --git a/src/qe/nlarith_util.cpp b/src/qe/nlarith_util.cpp
index 1da5ef52a..4f07b59dd 100644
--- a/src/qe/nlarith_util.cpp
+++ b/src/qe/nlarith_util.cpp
@@ -11,7 +11,8 @@ Copyright (c) 2015 Microsoft Corporation
 #include "qe/qe.h"
 #include "ast/rewriter/expr_replacer.h"
 #include "ast/rewriter/arith_rewriter.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
+#include "ast/rewriter/bool_rewriter.h"
+#include "ast/rewriter/th_rewriter.h"
 #include "ast/expr_functors.h"
 
 namespace nlarith {
@@ -79,9 +80,8 @@ namespace nlarith {
         app_ref m_zero;
         app_ref m_one;
         smt_params m_params;
-        basic_simplifier_plugin m_bs;
-        arith_simplifier_plugin m_rw;
-        arith_rewriter m_rw1;
+        bool_rewriter  m_bs;
+        arith_rewriter m_rw;
         expr_ref_vector m_trail;
 
         ast_manager& m() const { return m_manager; }
@@ -105,8 +105,7 @@ namespace nlarith {
             m_enable_linear(false),
             m_zero(num(0),m), m_one(num(1),m), 
             m_bs(m),
-            m_rw(m, m_bs, m_params),
-            m_rw1(m), m_trail(m) {
+            m_rw(m), m_trail(m) {
         }
         
         //
diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp
index 7c7250c40..dd54f4441 100644
--- a/src/qe/qe.cpp
+++ b/src/qe/qe.cpp
@@ -1310,6 +1310,10 @@ namespace qe {
         m_s.mk_atom(e, p, result);
     }    
 
+    void i_solver_context::collect_statistics(statistics& st) const {
+       // tbd
+    }
+
     typedef ref_vector_ptr_hash<expr, ast_manager> expr_ref_vector_hash;
     typedef ref_vector_ptr_eq<expr, ast_manager>   expr_ref_vector_eq;
     typedef hashtable<expr_ref_vector*, expr_ref_vector_hash, expr_ref_vector_eq> clause_table;
@@ -2393,6 +2397,7 @@ namespace qe {
 
 
 
+#if 0
     // ------------------------------------------------
     // expr_quant_elim_star1
 
@@ -2433,13 +2438,7 @@ namespace qe {
         simplifier(m), m_quant_elim(m, p), m_assumption(m.mk_true())
     {
     }
-
-    void expr_quant_elim_star1::reduce_with_assumption(expr* ctx, expr* fml, expr_ref& result) {
-        proof_ref pr(m);
-        m_assumption = ctx;
-        (*this)(fml, result, pr);
-        m_assumption = m.mk_true();
-    }
+#endif
 
 
     void hoist_exists(expr_ref& fml, app_ref_vector& vars) {
@@ -2488,6 +2487,7 @@ namespace qe {
 
         virtual ~simplify_solver_context() { reset(); }    
         
+
         void solve(expr_ref& fml, app_ref_vector& vars) {
             init(fml, vars);
             bool solved  = true;
@@ -2580,6 +2580,10 @@ namespace qe {
             m_ctx.updt_params(p);
         }
 
+        void collect_statistics(statistics & st) const {
+            m_ctx.collect_statistics(st);
+        }
+
         bool reduce_quantifier(
             quantifier * old_q, 
             expr * new_body, 
@@ -2647,6 +2651,10 @@ namespace qe {
         imp->updt_params(p);
     }
 
+    void simplify_rewriter_cfg::collect_statistics(statistics & st) const {
+        imp->collect_statistics(st);
+    }
+
     bool simplify_rewriter_cfg::pre_visit(expr* e) {
         if (!is_quantifier(e)) return true;
         quantifier * q = to_quantifier(e);
diff --git a/src/qe/qe.h b/src/qe/qe.h
index 392bfb03b..b6754b384 100644
--- a/src/qe/qe.h
+++ b/src/qe/qe.h
@@ -26,7 +26,6 @@ Revision History:
 #include "util/statistics.h"
 #include "util/lbool.h"
 #include "ast/expr_functors.h"
-#include "ast/simplifier/simplifier.h"
 #include "ast/rewriter/rewriter.h"
 #include "model/model.h"
 #include "util/params.h"
@@ -106,6 +105,9 @@ namespace qe {
         i_expr_pred& get_is_relevant() { return m_is_relevant; }
         
         i_nnf_atom& get_mk_atom() { return m_mk_atom; }
+
+        void collect_statistics(statistics & st) const;
+
     };
 
     class conj_enum {
@@ -322,30 +324,6 @@ namespace qe {
         void init_qe();
     };
 
-    class expr_quant_elim_star1 : public simplifier {
-    protected:
-        expr_quant_elim m_quant_elim;
-        expr*       m_assumption;
-        virtual bool visit_quantifier(quantifier * q);
-        virtual void reduce1_quantifier(quantifier * q);
-        virtual bool is_target(quantifier * q) const { return q->get_num_patterns() == 0 && q->get_num_no_patterns() == 0; }
-    public:
-        expr_quant_elim_star1(ast_manager & m, smt_params const& p);
-        virtual ~expr_quant_elim_star1() {}
-
-        void collect_statistics(statistics & st) const {
-            m_quant_elim.collect_statistics(st);
-        }
-
-        void reduce_with_assumption(expr* ctx, expr* fml, expr_ref& result);
-
-        lbool first_elim(unsigned num_vars, app* const* vars, expr_ref& fml, def_vector& defs) {
-            return m_quant_elim.first_elim(num_vars, vars, fml, defs);
-        }
-
-
-    };
-
     void hoist_exists(expr_ref& fml, app_ref_vector& vars);
 
     void mk_exists(unsigned num_vars, app* const* vars, expr_ref& fml);
@@ -372,6 +350,7 @@ namespace qe {
 
         void updt_params(params_ref const& p);
 
+        void collect_statistics(statistics & st) const;
     };
     
     class simplify_rewriter_star : public rewriter_tpl<simplify_rewriter_cfg> {
@@ -382,6 +361,11 @@ namespace qe {
             m_cfg(m) {}
          
         void updt_params(params_ref const& p) { m_cfg.updt_params(p); }
+
+        void collect_statistics(statistics & st) const {
+            m_cfg.collect_statistics(st);
+        }
+
     };
 
 };
diff --git a/src/qe/qe_cmd.cpp b/src/qe/qe_cmd.cpp
index 553cf4471..3c55c0f49 100644
--- a/src/qe/qe_cmd.cpp
+++ b/src/qe/qe_cmd.cpp
@@ -44,9 +44,8 @@ public:
     }
     
     virtual void execute(cmd_context & ctx) {
-        smt_params par;
         proof_ref pr(ctx.m());
-        qe::expr_quant_elim_star1 qe(ctx.m(), par);
+        qe::simplify_rewriter_star qe(ctx.m());
         expr_ref result(ctx.m());
 
         qe(m_target, result, pr);            
diff --git a/src/qe/vsubst_tactic.cpp b/src/qe/vsubst_tactic.cpp
deleted file mode 100644
index 6bd8213d6..000000000
--- a/src/qe/vsubst_tactic.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/*++
-Copyright (c) 2011 Microsoft Corporation
-
-Module Name:
-
-    vsubst_tactic.cpp
-
-Abstract:
-
-    Check satisfiability of QF_NRA problems using virtual subsititution quantifier-elimination.
-
-Author:
-
-    Nikolaj (nbjorner) 2011-05-16
-
-Notes:
-  Ported to tactic framework on 2012-02-28
-  It was qfnra_vsubst.cpp
-
-  This goal transformation checks satsifiability
-  of quantifier-free non-linear constraints using
-  virtual substitutions (applies to second-degree polynomials).
-  . identify non-linear variables
-  . use the identified variables as non-linear variables.
-  . give up if there are non-linear variables under uninterpreted scope.
-    give up if there are no non-linear variables.
-  . call quantifier elimination with 
-      - non-linear elimination option.
-      - get-first-branch option.
-  . if the first branch is linear, then done.
-    if the result is unsat, then done.
-    if the first branch is non-linear then, 
-    check candidate model,
-    perhaps iterate using rewriting or just give up.
-
-  . helpful facilities: 
-    . linearize_rewriter 
-        a*a*b + a*b = 0 <=> (b+1) = 0 \/ a = 0 \/ b = 0
-    . sign analysis:
-        a*a + b*b + c < 0  => c < 0
-
---*/
-#include "tactic/tactic.h"
-#include "qe/qe.h"
-#include "ast/arith_decl_plugin.h"
-#include "ast/for_each_expr.h"
-#include "tactic/extension_model_converter.h"
-#include "ast/ast_smt2_pp.h"
-
-class vsubst_tactic : public tactic {
-    params_ref m_params;
-
-    class get_var_proc {
-        arith_util       m_arith;
-        ptr_vector<app>& m_vars;
-    public:
-        get_var_proc(ast_manager & m, ptr_vector<app>& vars) : m_arith(m), m_vars(vars) {}
-        
-        void operator()(expr* e) {
-            if (is_app(e)) {
-                app* a = to_app(e);
-                if (m_arith.is_real(e) &&
-                    a->get_num_args() == 0 &&
-                    a->get_family_id() == null_family_id) {
-                    m_vars.push_back(a);
-                }
-            }
-        }
-    };
-
-    void get_vars(ast_manager & m, expr* fml, ptr_vector<app>& vars) {
-        get_var_proc proc(m, vars);
-        for_each_expr(proc, fml);        
-    }
-
-    void main(goal & s, model_converter_ref & mc, params_ref const & p) {
-        ast_manager & m = s.m();
-
-        ptr_vector<expr> fs;
-        for (unsigned i = 0; i < s.size(); ++i) {
-            fs.push_back(s.form(i));
-        }
-        app_ref f(m.mk_and(fs.size(), fs.c_ptr()), m);
-        TRACE("vsubst", 
-              s.display(tout);
-              tout << "goal: " << mk_ismt2_pp(f.get(), m) << "\n";);
-        ptr_vector<app> vars;
-        get_vars(m, f.get(), vars);
-        
-        if (vars.empty()) {
-            TRACE("vsubst", tout << "no real variables\n";);
-            throw tactic_exception("there are no real variables");
-        }
-
-        smt_params params;
-        params.updt_params(p);
-        params.m_model = false;
-        flet<bool> fl1(params.m_nlquant_elim, true);
-        flet<bool> fl2(params.m_nl_arith_gb, false);
-        TRACE("quant_elim", tout << "Produce models: " << params.m_model << "\n";);
-
-        qe::expr_quant_elim_star1 qelim(m, params);
-        expr_ref g(f, m);
-        qe::def_vector defs(m);
-        lbool is_sat = qelim.first_elim(vars.size(), vars.c_ptr(), g, defs);
-        if (is_sat == l_undef) {
-            TRACE("vsubst", tout << mk_ismt2_pp(g, m) << "\n";);
-            throw tactic_exception("elimination was not successful");
-        }
-        if (!defs.empty()) {
-            extension_model_converter * ev = alloc(extension_model_converter, m);
-            mc = ev;
-            for (unsigned i = defs.size(); i > 0; ) {
-                --i;
-                ev->insert(defs.var(i), defs.def(i));
-            }
-        }
-        
-        s.reset();
-        // TBD: wasteful as we already know it is sat or unsat.
-        // TBD: extract model from virtual substitution.
-        s.assert_expr(g);
-
-        TRACE("qfnra_vsubst", 
-              tout << "v-subst result:\n";
-              s.display(tout););
-    }
-
-
-public:
-    vsubst_tactic(params_ref const & p):m_params(p) {}
-
-    virtual tactic * translate(ast_manager & m) {
-        return alloc(vsubst_tactic, m_params);
-    }
-
-    virtual ~vsubst_tactic() {}
-    
-    virtual void updt_params(params_ref const & p) {
-        m_params = p;
-    }
-
-    /**
-       \brief Check satisfiability of an assertion set of QF_NRA
-       by using virtual substitutions.
-    */
-    virtual void operator()(goal_ref const & g,
-                            goal_ref_buffer & result, 
-                            model_converter_ref & mc, 
-                            proof_converter_ref & pc,
-                            expr_dependency_ref & core) {
-        SASSERT(g->is_well_sorted());
-        fail_if_proof_generation("vsubst", g);
-        fail_if_unsat_core_generation("vsubst", g);
-        fail_if_model_generation("vsubst", g); // disable for now due to problems with infinitesimals.
-        mc = 0; pc = 0; core = 0; result.reset();
-        
-        main(*(g.get()), mc, m_params);
-        
-        result.push_back(g.get());
-        SASSERT(g->is_well_sorted());
-    }
-
-    virtual void cleanup(void) {}
-};
-
-tactic * mk_vsubst_tactic(ast_manager & m, params_ref const & p) {
-    return alloc(vsubst_tactic, p);
-}
diff --git a/src/qe/vsubst_tactic.h b/src/qe/vsubst_tactic.h
deleted file mode 100644
index 2f28bda71..000000000
--- a/src/qe/vsubst_tactic.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*++
-Copyright (c) 2011 Microsoft Corporation
-
-Module Name:
-
-    vsubst_tactic.h
-
-Abstract:
-
-    Check satisfiability of QF_NRA problems using virtual subsititution quantifier-elimination.
-
-Author:
-
-    Nikolaj (nbjorner) 2011-05-16
-
-Notes:
-
-
---*/
-#ifndef VSUBST_TACTIC_H_
-#define VSUBST_TACTIC_H_
-
-#include "util/params.h"
-class ast_manager;
-class tactic;
-
-tactic * mk_vsubst_tactic(ast_manager & m, params_ref const & p = params_ref());
-/*
-  ADD_TACTIC("vsubst", "checks satsifiability of quantifier-free non-linear constraints using virtual substitution.", "mk_vsubst_tactic(m, p)")
-*/
-
-#endif
-
diff --git a/src/smt/CMakeLists.txt b/src/smt/CMakeLists.txt
index b117db89b..5c4c6b0b9 100644
--- a/src/smt/CMakeLists.txt
+++ b/src/smt/CMakeLists.txt
@@ -2,7 +2,7 @@ z3_add_component(smt
   SOURCES
     arith_eq_adapter.cpp
     arith_eq_solver.cpp
-    asserted_formulas.cpp
+##  asserted_formulas.cpp
     asserted_formulas_new.cpp
     cached_var_subst.cpp
     cost_evaluator.cpp
diff --git a/src/smt/arith_eq_adapter.cpp b/src/smt/arith_eq_adapter.cpp
index 60c109cff..307bdc671 100644
--- a/src/smt/arith_eq_adapter.cpp
+++ b/src/smt/arith_eq_adapter.cpp
@@ -22,7 +22,6 @@ Revision History:
 #include "ast/ast_pp.h"
 #include "ast/ast_ll_pp.h"
 #include "util/stats.h"
-#include "ast/simplifier/simplifier.h"
 #include "ast/ast_smt2_pp.h"
 
 namespace smt {
diff --git a/src/smt/params/CMakeLists.txt b/src/smt/params/CMakeLists.txt
index 500423dcc..c965f0a62 100644
--- a/src/smt/params/CMakeLists.txt
+++ b/src/smt/params/CMakeLists.txt
@@ -13,7 +13,6 @@ z3_add_component(smt_params
     ast
     bit_blaster
     pattern
-    simplifier
   PYG_FILES
     smt_params_helper.pyg
 )
diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp
index 0b621870d..afca0196a 100644
--- a/src/smt/params/preprocessor_params.cpp
+++ b/src/smt/params/preprocessor_params.cpp
@@ -48,7 +48,6 @@ void preprocessor_params::display(std::ostream & out) const {
     DISPLAY_PARAM(m_pull_cheap_ite_trees);
     DISPLAY_PARAM(m_pull_nested_quantifiers);
     DISPLAY_PARAM(m_eliminate_term_ite);
-    //DISPLAY_PARAM(m_eliminate_and);
     DISPLAY_PARAM(m_macro_finder);
     DISPLAY_PARAM(m_propagate_values);
     DISPLAY_PARAM(m_propagate_booleans);
diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h
index fe759417d..6f763c0e1 100644
--- a/src/smt/params/preprocessor_params.h
+++ b/src/smt/params/preprocessor_params.h
@@ -31,15 +31,14 @@ enum lift_ite_kind {
 };
 
 struct preprocessor_params : public pattern_inference_params, 
-                             public bit_blaster_params, 
-                             public bv_simplifier_params, 
+                             public bit_blaster_params,
+                             public bv_simplifier_params,
                              public arith_simplifier_params {
     lift_ite_kind   m_lift_ite;
     lift_ite_kind   m_ng_lift_ite;  // lift ite for non ground terms
     bool            m_pull_cheap_ite_trees;
     bool            m_pull_nested_quantifiers;
     bool            m_eliminate_term_ite;
-//    bool            m_eliminate_and; // represent (and a b) as (not (or (not a) (not b)))
     bool            m_macro_finder;
     bool            m_propagate_values;
     bool            m_propagate_booleans;
@@ -62,7 +61,6 @@ public:
         m_pull_cheap_ite_trees(false),
         m_pull_nested_quantifiers(false),
         m_eliminate_term_ite(false),
-       // m_eliminate_and(true),
         m_macro_finder(false),
         m_propagate_values(true),
         m_propagate_booleans(false), // TODO << check peformance
diff --git a/src/tactic/bv/elim_small_bv_tactic.cpp b/src/tactic/bv/elim_small_bv_tactic.cpp
index c40927179..ab4b3920d 100644
--- a/src/tactic/bv/elim_small_bv_tactic.cpp
+++ b/src/tactic/bv/elim_small_bv_tactic.cpp
@@ -24,9 +24,7 @@ Revision History:
 #include "ast/used_vars.h"
 #include "ast/well_sorted.h"
 #include "ast/rewriter/var_subst.h"
-#include "ast/simplifier/simplifier.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
+#include "ast/rewriter/th_rewriter.h"
 
 #include "tactic/bv/elim_small_bv_tactic.h"
 
@@ -36,7 +34,7 @@ class elim_small_bv_tactic : public tactic {
         ast_manager               & m;
         params_ref                  m_params;
         bv_util                     m_util;
-        simplifier                  m_simp;
+        th_rewriter                 m_simp;
         ref<filter_model_converter> m_mc;
         goal *                      m_goal;
         unsigned                    m_max_bits;
@@ -56,14 +54,6 @@ class elim_small_bv_tactic : public tactic {
             updt_params(p);
             m_goal = 0;
             m_max_steps = UINT_MAX;
-
-            basic_simplifier_plugin * bsimp = alloc(basic_simplifier_plugin, m);
-            // bsimp->set_eliminate_and(true);
-            m_simp.register_plugin(bsimp);
-
-            bv_simplifier_params bv_params;
-            bv_simplifier_plugin * bvsimp = alloc(bv_simplifier_plugin, m, *bsimp, bv_params);
-            m_simp.register_plugin(bvsimp);
         }
 
         bool max_steps_exceeded(unsigned long long num_steps) const {

From 0d5cfe9292d9d9b60a6567c48bb48d5a2aa246a9 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 09:23:15 -0700
Subject: [PATCH 28/74] separate out, add copy constructor

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/ast.h                  | 35 -----------------------
 src/ast/justified_expr.h       | 52 ++++++++++++++++++++++++++++++++++
 src/ast/macros/macro_manager.h |  1 +
 src/ast/macros/quasi_macros.h  |  1 +
 src/smt/elim_term_ite.h        |  2 ++
 5 files changed, 56 insertions(+), 35 deletions(-)
 create mode 100644 src/ast/justified_expr.h

diff --git a/src/ast/ast.h b/src/ast/ast.h
index 34ea96653..699268bd0 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -2470,41 +2470,6 @@ public:
     void operator()(AST * n) { m_manager.inc_ref(n); }
 };
 
-class justified_expr {
-    ast_manager& m;
-    expr*        m_fml;
-    proof*       m_proof;
-public:
-    justified_expr(ast_manager& m, expr* fml, proof* p):
-        m(m),
-        m_fml(fml),
-        m_proof(p) {
-        m.inc_ref(fml);
-        m.inc_ref(p);
-    }
-    
-    justified_expr& operator=(justified_expr& other) {
-        SASSERT(&m == &other.m);
-        if (this != &other) {
-            m.dec_ref(m_fml);
-            m.dec_ref(m_proof);
-            m_fml = other.get_fml();
-            m_proof = other.get_proof();
-            m.inc_ref(m_fml);
-            m.inc_ref(m_proof);
-        }
-        return *this;
-    }
-    
-    ~justified_expr() {
-        m.dec_ref(m_fml);
-        m.dec_ref(m_proof);
-    }
-    
-    expr* get_fml() const { return m_fml; }
-    proof* get_proof() const { return m_proof; }        
-};
-
 
 #endif /* AST_H_ */
 
diff --git a/src/ast/justified_expr.h b/src/ast/justified_expr.h
new file mode 100644
index 000000000..49362940d
--- /dev/null
+++ b/src/ast/justified_expr.h
@@ -0,0 +1,52 @@
+
+#ifndef JUSTIFIED_EXPR_H_
+#define JUSTIFIED_EXPR_H_
+
+#include "ast/ast.h"
+
+class justified_expr {
+    ast_manager& m;
+    expr*        m_fml;
+    proof*       m_proof;
+public:
+    justified_expr(ast_manager& m, expr* fml, proof* p):
+        m(m),
+        m_fml(fml),
+        m_proof(p) {
+        SASSERT(fml);
+        m.inc_ref(fml);
+        m.inc_ref(p);
+    }
+    
+    justified_expr& operator=(justified_expr const& other) {
+        SASSERT(&m == &other.m);
+        if (this != &other) {
+            m.dec_ref(m_fml);
+            m.dec_ref(m_proof);
+            m_fml = other.get_fml();
+            m_proof = other.get_proof();
+            m.inc_ref(m_fml);
+            m.inc_ref(m_proof);
+        }
+        return *this;
+    }
+    
+    justified_expr(justified_expr const& other):
+        m(other.m),
+        m_fml(other.m_fml),
+        m_proof(other.m_proof)
+    {
+        m.inc_ref(m_fml);
+        m.inc_ref(m_proof);
+    }
+
+    ~justified_expr() {
+        m.dec_ref(m_fml);
+        m.dec_ref(m_proof);
+    }
+    
+    expr* get_fml() const { return m_fml; }
+    proof* get_proof() const { return m_proof; }        
+};
+
+#endif
diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h
index 46dfa059d..0205fb891 100644
--- a/src/ast/macros/macro_manager.h
+++ b/src/ast/macros/macro_manager.h
@@ -21,6 +21,7 @@ Revision History:
 
 #include "util/obj_hashtable.h"
 #include "ast/ast_util.h"
+#include "ast/justified_expr.h"
 #include "ast/recurse_expr.h"
 #include "ast/func_decl_dependencies.h"
 #include "ast/macros/macro_util.h"
diff --git a/src/ast/macros/quasi_macros.h b/src/ast/macros/quasi_macros.h
index fc7287554..1b1483a90 100644
--- a/src/ast/macros/quasi_macros.h
+++ b/src/ast/macros/quasi_macros.h
@@ -20,6 +20,7 @@ Revision History:
 #define QUASI_MACROS_H_
 
 #include<sstream>
+#include "ast/justified_expr.h"
 #include "ast/macros/macro_manager.h"
 #include "ast/rewriter/th_rewriter.h"
 
diff --git a/src/smt/elim_term_ite.h b/src/smt/elim_term_ite.h
index 86b9a79e2..a57ea1dad 100644
--- a/src/smt/elim_term_ite.h
+++ b/src/smt/elim_term_ite.h
@@ -22,6 +22,8 @@ Revision History:
 #include "ast/normal_forms/defined_names.h"
 #include "ast/rewriter/rewriter.h"
 #include "ast/simplifier/simplifier.h"
+#include "ast/justified_expr.h"
+
 
 class elim_term_ite : public simplifier {
     defined_names &    m_defined_names;

From 82a937d1af25f9466556670684ac9283023ef586 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 10:41:25 -0700
Subject: [PATCH 29/74] enforce arithmetic normalization

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/api_context.cpp              |  3 ++-
 src/ast/ast_smt2_pp.cpp              |  8 ++++----
 src/ast/rewriter/arith_rewriter.cpp  |  2 +-
 src/ast/rewriter/poly_rewriter_def.h | 17 +++++++++++++----
 src/smt/asserted_formulas_new.cpp    |  9 +++++++--
 5 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp
index c99d3f0c8..1e3f7a0a4 100644
--- a/src/api/api_context.cpp
+++ b/src/api/api_context.cpp
@@ -150,8 +150,9 @@ namespace api {
     
     void context::set_error_code(Z3_error_code err) {
         m_error_code = err; 
-        if (err != Z3_OK) 
+        if (err != Z3_OK) {
             invoke_error_handler(err); 
+        }
     }
 
     void context::check_searching() {
diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp
index 5c3eb93c2..51ccc1e7d 100644
--- a/src/ast/ast_smt2_pp.cpp
+++ b/src/ast/ast_smt2_pp.cpp
@@ -1222,15 +1222,15 @@ mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent, unsigned num
 
 std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) {
     smt2_pp_environment_dbg env(p.m_manager);    
-    if (is_expr(p.m_ast)) {
+    if (p.m_ast == 0) {
+        out << "null";
+    }
+    else if (is_expr(p.m_ast)) {
         ast_smt2_pp(out, to_expr(p.m_ast), env, p.m_params, p.m_indent, p.m_num_vars, p.m_var_prefix);
     }
     else if (is_sort(p.m_ast)) {
         ast_smt2_pp(out, to_sort(p.m_ast), env, p.m_params, p.m_indent);
     }
-    else if (p.m_ast == 0) {
-        out << "null";
-    }
     else {
         SASSERT(is_func_decl(p.m_ast));
         ast_smt2_pp(out, to_func_decl(p.m_ast), env, p.m_params, p.m_indent);
diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp
index 18556b71b..4b00cde45 100644
--- a/src/ast/rewriter/arith_rewriter.cpp
+++ b/src/ast/rewriter/arith_rewriter.cpp
@@ -371,7 +371,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
         (is_zero(arg2) && is_reduce_power_target(arg1, kind == EQ)))
         return reduce_power(arg1, arg2, kind, result);
     br_status st = cancel_monomials(arg1, arg2, m_arith_lhs, new_arg1, new_arg2);
-    TRACE("mk_le_bug", tout << "st: " << st << "\n";);
+    TRACE("mk_le_bug", tout << "st: " << st << " " << new_arg1 << " " << new_arg2 << "\n";);
     if (st != BR_FAILED) {
         arg1 = new_arg1;
         arg2 = new_arg2;
diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h
index 731a0538a..2a6bd2c50 100644
--- a/src/ast/rewriter/poly_rewriter_def.h
+++ b/src/ast/rewriter/poly_rewriter_def.h
@@ -704,8 +704,10 @@ br_status poly_rewriter<Config>::cancel_monomials(expr * lhs, expr * rhs, bool m
         }
     }
 
-    if (move && num_coeffs == 0 && is_numeral(rhs))
+    if (move && num_coeffs == 0 && is_numeral(rhs)) {
+        TRACE("mk_le_bug", tout << "no coeffs\n";);
         return BR_FAILED;
+    }
 
     for (unsigned i = 0; i < rhs_sz; i++) {
         expr * arg = rhs_monomials[i];
@@ -723,15 +725,21 @@ br_status poly_rewriter<Config>::cancel_monomials(expr * lhs, expr * rhs, bool m
     }
 
     normalize(c);
-    
+
+    TRACE("mk_le_bug", tout << c << "\n";);
+
     if (!has_multiple && num_coeffs <= 1) {
         if (move) {
-            if (is_numeral(rhs))
+            if (is_numeral(rhs)) {
+                TRACE("mk_le_bug", tout << "rhs is numeral\n";);
                 return BR_FAILED;
+            }
         }
         else {
-            if (num_coeffs == 0 || is_numeral(rhs))
+            if (num_coeffs == 0 || is_numeral(rhs)) {
+                TRACE("mk_le_bug", tout << "rhs is numeral or no coeffs\n";);
                 return BR_FAILED;
+            }
         }
     }
     
@@ -847,6 +855,7 @@ br_status poly_rewriter<Config>::cancel_monomials(expr * lhs, expr * rhs, bool m
     new_lhs_monomials[0] = insert_c_lhs ? mk_numeral(c) : NULL;
     lhs_result = mk_add_app(new_lhs_monomials.size() - lhs_offset, new_lhs_monomials.c_ptr() + lhs_offset);
     rhs_result = mk_add_app(new_rhs_monomials.size() - rhs_offset, new_rhs_monomials.c_ptr() + rhs_offset);
+    TRACE("mk_le_bug", tout << lhs_result << " " << rhs_result << "\n";);
     return BR_DONE;
 }
 
diff --git a/src/smt/asserted_formulas_new.cpp b/src/smt/asserted_formulas_new.cpp
index 39c432b61..5d4783bf8 100644
--- a/src/smt/asserted_formulas_new.cpp
+++ b/src/smt/asserted_formulas_new.cpp
@@ -59,6 +59,11 @@ asserted_formulas_new::asserted_formulas_new(ast_manager & m, smt_params & p):
     m_apply_quasi_macros(*this) {
 
     m_macro_finder = alloc(macro_finder, m, m_macro_manager);
+
+    params_ref pa;
+    pa.set_bool("arith_lhs", true);
+    m_rewriter.updt_params(pa);
+
 }
 
 void asserted_formulas_new::setup() {
@@ -103,7 +108,7 @@ void asserted_formulas_new::push_assertion(expr * e, proof * pr, vector<justifie
     }
     else if (m.is_not(e, e1) && m.is_or(e1)) {
         for (unsigned i = 0; i < to_app(e1)->get_num_args(); ++i) {
-            expr* arg = to_app(e1)->get_arg(i), *e2;
+            expr* arg = to_app(e1)->get_arg(i);
             proof_ref _pr(m.mk_not_or_elim(pr, i), m);
             expr_ref  narg(mk_not(m, arg), m);
             push_assertion(narg, _pr, result);            
@@ -117,6 +122,7 @@ void asserted_formulas_new::push_assertion(expr * e, proof * pr, vector<justifie
 void asserted_formulas_new::set_eliminate_and(bool flag) {
     params_ref p;
     p.set_bool("elim_and", true);
+    p.set_bool("arith_lhs", true);
     m_rewriter.updt_params(p);
     flush_cache();
 }
@@ -430,7 +436,6 @@ void asserted_formulas_new::propagate_values() {
         m_scoped_substitution.push();
         unsigned prop = num_prop;
         TRACE("propagate_values", tout << "before:\n"; display(tout););
-        IF_IVERBOSE(10, verbose_stream() << "(smt.propagate-values)\n";);
         unsigned i  = m_qhead;
         unsigned sz = m_formulas.size();
         for (; i < sz; i++) {

From bcf229dcfdad8002f73a3a8351609fa9c18c2884 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 11:23:41 -0700
Subject: [PATCH 30/74] removing dependencies on simplifier

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/CMakeLists.txt               |   4 +-
 .../rewriter/{bv_elim2.cpp => bv_elim.cpp}    |   2 +-
 src/ast/rewriter/{bv_elim2.h => bv_elim.h}    |   4 +-
 src/ast/rewriter/elim_bounds2.cpp             | 203 ----
 src/ast/rewriter/elim_bounds2.h               |  77 --
 src/ast/simplifier/CMakeLists.txt             |  32 +-
 src/smt/CMakeLists.txt                        |   1 -
 src/smt/asserted_formulas.cpp                 | 879 ------------------
 src/smt/asserted_formulas.h                   | 151 ---
 src/smt/asserted_formulas_new.cpp             |   8 +-
 src/smt/asserted_formulas_new.h               |   6 +-
 src/smt/elim_term_ite.cpp                     | 138 ---
 src/smt/elim_term_ite.h                       |  27 -
 src/smt/expr_context_simplifier.h             |   4 +-
 src/smt/params/CMakeLists.txt                 |   1 +
 src/smt/smt_consequences.cpp                  |   9 +-
 src/smt/smt_quantifier.cpp                    |   3 +-
 src/smt/theory_array.cpp                      |   1 +
 src/smt/theory_lra.cpp                        |   1 +
 src/smt/theory_seq.cpp                        |   3 +-
 src/smt/theory_str.h                          |   7 +-
 src/test/CMakeLists.txt                       |   1 -
 src/test/bv_simplifier_plugin.cpp             | 326 -------
 src/test/main.cpp                             |   1 -
 src/test/quant_elim.cpp                       |   6 -
 src/test/sorting_network.cpp                  |   6 +-
 26 files changed, 52 insertions(+), 1849 deletions(-)
 rename src/ast/rewriter/{bv_elim2.cpp => bv_elim.cpp} (99%)
 rename src/ast/rewriter/{bv_elim2.h => bv_elim.h} (96%)
 delete mode 100644 src/ast/rewriter/elim_bounds2.cpp
 delete mode 100644 src/ast/rewriter/elim_bounds2.h
 delete mode 100644 src/smt/asserted_formulas.cpp
 delete mode 100644 src/smt/asserted_formulas.h
 delete mode 100644 src/test/bv_simplifier_plugin.cpp

diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt
index 804fbcbed..57924b48a 100644
--- a/src/ast/rewriter/CMakeLists.txt
+++ b/src/ast/rewriter/CMakeLists.txt
@@ -6,13 +6,13 @@ z3_add_component(rewriter
     bit2int.cpp
     bool_rewriter.cpp
     bv_bounds.cpp
-    bv_elim2.cpp
+    bv_elim.cpp
     bv_rewriter.cpp
     datatype_rewriter.cpp
     der.cpp
     distribute_forall.cpp
     dl_rewriter.cpp
-    elim_bounds2.cpp
+    elim_bounds.cpp
     enum2bv_rewriter.cpp
     expr_replacer.cpp
     expr_safe_replace.cpp
diff --git a/src/ast/rewriter/bv_elim2.cpp b/src/ast/rewriter/bv_elim.cpp
similarity index 99%
rename from src/ast/rewriter/bv_elim2.cpp
rename to src/ast/rewriter/bv_elim.cpp
index 5b542e59e..270d7deb8 100644
--- a/src/ast/rewriter/bv_elim2.cpp
+++ b/src/ast/rewriter/bv_elim.cpp
@@ -4,7 +4,7 @@ Copyright (c) 2015 Microsoft Corporation
 
 --*/
 
-#include "ast/rewriter/bv_elim2.h"
+#include "ast/rewriter/bv_elim.h"
 #include "ast/bv_decl_plugin.h"
 #include "ast/rewriter/var_subst.h"
 #include "ast/rewriter/rewriter_def.h"
diff --git a/src/ast/rewriter/bv_elim2.h b/src/ast/rewriter/bv_elim.h
similarity index 96%
rename from src/ast/rewriter/bv_elim2.h
rename to src/ast/rewriter/bv_elim.h
index a4829b207..6468cb8b9 100644
--- a/src/ast/rewriter/bv_elim2.h
+++ b/src/ast/rewriter/bv_elim.h
@@ -17,8 +17,8 @@ Author:
 Revision History:
 
 --*/
-#ifndef BV_ELIM2_H_
-#define BV_ELIM2_H_
+#ifndef BV_ELIM_H_
+#define BV_ELIM_H_
 
 #include "ast/ast.h"
 #include "ast/rewriter/rewriter.h"
diff --git a/src/ast/rewriter/elim_bounds2.cpp b/src/ast/rewriter/elim_bounds2.cpp
deleted file mode 100644
index 9691ade09..000000000
--- a/src/ast/rewriter/elim_bounds2.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    elim_bounds2.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-06-28.
-
-Revision History:
-
---*/
-
-#ifndef ELIM_BOUNDS_H_
-#define ELIM_BOUNDS_H_
-
-#include "ast/used_vars.h"
-#include "util/obj_hashtable.h"
-#include "ast/rewriter/var_subst.h"
-#include "ast/rewriter/elim_bounds2.h"
-#include "ast/ast_pp.h"
-
-elim_bounds_cfg::elim_bounds_cfg(ast_manager & m):
-    m(m),
-    m_util(m) {
-}
-
-/**
-   \brief Find bounds of the form
-
-   (<= x k)
-   (<= (+ x (* -1 y)) k)
-   (<= (+ x (* -1 t)) k)
-   (<= (+ t (* -1 x)) k)
-
-   x and y are a bound variables, t is a ground term and k is a numeral
-
-   It also detects >=, and the atom can be negated.
-*/
-bool elim_bounds_cfg::is_bound(expr * n, var * & lower, var * & upper) {
-    upper    = 0;
-    lower    = 0;
-    bool neg = false;
-    if (m.is_not(n)) {
-        n   = to_app(n)->get_arg(0);
-        neg = true;
-    }
-
-    expr* l = 0, *r = 0;
-    bool le  = false;
-    if (m_util.is_le(n, l, r) && m_util.is_numeral(r)) {
-        n  = l;
-        le = true;
-    }
-    else if (m_util.is_ge(n, l, r) && m_util.is_numeral(r)) {
-        n  = l;
-        le = false;
-    }
-    else {
-        return false;
-    }
-
-    if (neg)
-        le = !le;
-
-    if (is_var(n)) {
-        upper = to_var(n);
-    }
-    else if (m_util.is_add(n, l, r)) {
-        expr * arg1 = l;
-        expr * arg2 = r;
-        if (is_var(arg1))
-            upper   = to_var(arg1);
-        else if (!is_ground(arg1))
-            return false;
-        rational k;
-        bool is_int;
-        if (m_util.is_mul(arg2) && m_util.is_numeral(to_app(arg2)->get_arg(0), k, is_int) && k.is_minus_one()) {
-            arg2    = to_app(arg2)->get_arg(1);
-            if (is_var(arg2))
-                lower = to_var(arg2);
-            else if (!is_ground(arg2))
-                return false; // not supported
-        }
-        else {
-            return false; // not supported
-        }
-    }
-    else {
-        return false;
-    }
-
-    if (!le)
-        std::swap(upper, lower);
-
-    return true;
-}
-
-bool elim_bounds_cfg::is_bound(expr * n) {
-    var * lower, * upper;
-    return is_bound(n, lower, upper);
-}
-
-
-bool elim_bounds_cfg::reduce_quantifier(quantifier * q, 
-                                     expr * n, 
-                                     expr * const * new_patterns, 
-                                     expr * const * new_no_patterns,
-                                     expr_ref & result,
-                                     proof_ref & result_pr) {
-    if (!q->is_forall()) {
-        return false;
-    }
-    unsigned num_vars = q->get_num_decls();
-    ptr_buffer<expr> atoms;
-    if (m.is_or(n))
-        atoms.append(to_app(n)->get_num_args(), to_app(n)->get_args());
-    else
-        atoms.push_back(n);
-    used_vars          used_vars;
-    // collect non-candidates
-    for (expr * a : atoms) {
-        if (!is_bound(a))
-            used_vars.process(a);
-    }
-    if (used_vars.uses_all_vars(q->get_num_decls())) {
-        return false;
-    }
-    // collect candidates
-    obj_hashtable<var> lowers;
-    obj_hashtable<var> uppers;
-    obj_hashtable<var> candidate_set;
-    ptr_buffer<var>    candidates;
-#define ADD_CANDIDATE(V) if (!lowers.contains(V) && !uppers.contains(V)) { candidate_set.insert(V); candidates.push_back(V); }
-    for (expr * a : atoms) {
-        var * lower = 0;
-        var * upper = 0;
-        if (is_bound(a, lower, upper)) {
-            if (lower != 0 && !used_vars.contains(lower->get_idx()) && lower->get_idx() < num_vars) {
-                ADD_CANDIDATE(lower);
-                lowers.insert(lower);
-            }
-            if (upper != 0 && !used_vars.contains(upper->get_idx()) && upper->get_idx() < num_vars) {
-                ADD_CANDIDATE(upper);
-                uppers.insert(upper);
-            }
-        }
-    }
-    TRACE("elim_bounds", tout << "candidates:\n"; for (unsigned i = 0; i < candidates.size(); i++) tout << mk_pp(candidates[i], m) << "\n";);
-    // remove candidates that have lower and upper bounds
-
-    for (var * v : candidates) {
-        if (lowers.contains(v) && uppers.contains(v))
-            candidate_set.erase(v);
-    }
-    TRACE("elim_bounds", tout << "candidates after filter:\n"; for (unsigned i = 0; i < candidates.size(); i++) tout << mk_pp(candidates[i], m) << "\n";);
-    if (candidate_set.empty()) {
-        return false;
-    }
-    // remove bounds that contain variables in candidate_set
-    unsigned j = 0;
-    for (unsigned i = 0; i < atoms.size(); ++i) {
-        expr * a = atoms[i];
-        var * lower = 0;
-        var * upper = 0;
-        if (is_bound(a, lower, upper) && ((lower != 0 && candidate_set.contains(lower)) || (upper != 0 && candidate_set.contains(upper))))
-            continue;
-        atoms[j] = a;
-        j++;
-    }
-    if (j == atoms.size()) {
-        return false;
-    }
-    atoms.resize(j);
-    expr * new_body = 0;
-    switch (atoms.size()) {
-    case 0:
-        result = m.mk_false();
-        result_pr = m.mk_rewrite(q, result);
-        TRACE("elim_bounds", tout << mk_pp(q, m) << "\n" << result << "\n";);
-        return true;
-    case 1:
-        new_body = atoms[0];
-        break;
-    default:
-        new_body = m.mk_or(atoms.size(), atoms.c_ptr());
-        break;
-    }
-    quantifier_ref new_q(m);
-    new_q = m.update_quantifier(q, new_body);
-    elim_unused_vars(m, new_q, params_ref(), result);
-    result_pr = m.mk_rewrite(q, result);
-    TRACE("elim_bounds", tout << mk_pp(q, m) << "\n" << result << "\n";);
-    return true;
-}
-
-#endif /* ELIM_BOUNDS_H_ */
diff --git a/src/ast/rewriter/elim_bounds2.h b/src/ast/rewriter/elim_bounds2.h
deleted file mode 100644
index e0bba4e60..000000000
--- a/src/ast/rewriter/elim_bounds2.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    elim_bounds2.h
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-06-28.
-
-Revision History:
-
---*/
-#ifndef ELIM_BOUNDS2_H_
-#define ELIM_BOUNDS2_H_
-
-#include "ast/ast.h"
-#include "ast/arith_decl_plugin.h"
-#include "ast/rewriter/rewriter.h"
-
-/**
-   \brief Functor for eliminating irrelevant bounds in quantified formulas.
-   
-   Example:
-   (forall (x Int) (y Int) (or (not (>= y x) (not (>= x 0)) (= (select a x) 1))))
-
-   The bound (>= y x) is irrelevant and can be eliminated.
-
-   This can be easily proved by using Fourier-Motzkin elimination.
-
-   Limitations & Assumptions:
-   - It assumes the input formula was already simplified.
-   - It can only handle bounds in the diff-logic fragment.
-
-   \remark This operation is subsumed by Fourier-Motzkin elimination.
-*/
-class elim_bounds_cfg : public default_rewriter_cfg {
-    ast_manager &      m;
-    arith_util         m_util;
-    bool is_bound(expr * n, var * & lower, var * & upper);
-    bool is_bound(expr * n);
-public:
-    elim_bounds_cfg(ast_manager & m);
-    
-    bool reduce_quantifier(quantifier * old_q, 
-                           expr * new_body, 
-                           expr * const * new_patterns, 
-                           expr * const * new_no_patterns,
-                           expr_ref & result,
-                           proof_ref & result_pr);
-};
-
-/**
-   \brief Functor for applying elim_bounds2 in all
-   universal quantifiers in an expression.
-
-   Assumption: the formula was already skolemized.
-*/
-class elim_bounds_rw : public rewriter_tpl<elim_bounds_cfg> {
-protected:
-    elim_bounds_cfg  m_cfg;
-public:
-    elim_bounds_rw(ast_manager & m):
-        rewriter_tpl<elim_bounds_cfg>(m, m.proofs_enabled(), m_cfg),
-        m_cfg(m)
-    {} 
-
-    virtual ~elim_bounds_rw() {}
-};
-
-#endif /* ELIM_BOUNDS2_H_ */
-
diff --git a/src/ast/simplifier/CMakeLists.txt b/src/ast/simplifier/CMakeLists.txt
index 52a44595e..649c0486e 100644
--- a/src/ast/simplifier/CMakeLists.txt
+++ b/src/ast/simplifier/CMakeLists.txt
@@ -1,21 +1,21 @@
 z3_add_component(simplifier
   SOURCES
-    arith_simplifier_params.cpp
-    arith_simplifier_plugin.cpp
-    array_simplifier_params.cpp
-    array_simplifier_plugin.cpp
-    basic_simplifier_plugin.cpp
-    bv_elim.cpp
-    bv_simplifier_params.cpp
-    bv_simplifier_plugin.cpp
-    datatype_simplifier_plugin.cpp
-    elim_bounds.cpp
-    fpa_simplifier_plugin.cpp
-    maximise_ac_sharing.cpp
-    poly_simplifier_plugin.cpp
-    seq_simplifier_plugin.cpp
-    simplifier.cpp
-    simplifier_plugin.cpp
+     arith_simplifier_params.cpp
+#    arith_simplifier_plugin.cpp
+     array_simplifier_params.cpp
+#    array_simplifier_plugin.cpp
+#    basic_simplifier_plugin.cpp
+#    bv_elim.cpp
+     bv_simplifier_params.cpp
+#    bv_simplifier_plugin.cpp
+#    datatype_simplifier_plugin.cpp
+#    elim_bounds.cpp
+#    fpa_simplifier_plugin.cpp
+#    maximise_ac_sharing.cpp
+#    poly_simplifier_plugin.cpp
+#    seq_simplifier_plugin.cpp
+#    simplifier.cpp
+#    simplifier_plugin.cpp
   COMPONENT_DEPENDENCIES
     rewriter
   PYG_FILES
diff --git a/src/smt/CMakeLists.txt b/src/smt/CMakeLists.txt
index 5c4c6b0b9..385d1aaa0 100644
--- a/src/smt/CMakeLists.txt
+++ b/src/smt/CMakeLists.txt
@@ -2,7 +2,6 @@ z3_add_component(smt
   SOURCES
     arith_eq_adapter.cpp
     arith_eq_solver.cpp
-##  asserted_formulas.cpp
     asserted_formulas_new.cpp
     cached_var_subst.cpp
     cost_evaluator.cpp
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
deleted file mode 100644
index 44ccd0bf6..000000000
--- a/src/smt/asserted_formulas.cpp
+++ /dev/null
@@ -1,879 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    asserted_formulas.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-06-11.
-
-Revision History:
-
---*/
-#include "util/warning.h"
-#include "ast/ast_ll_pp.h"
-#include "ast/ast_pp.h"
-#include "ast/for_each_expr.h"
-#include "ast/well_sorted.h"
-#include "ast/rewriter/rewriter_def.h"
-#include "ast/rewriter/pull_ite_tree.h"
-#include "ast/rewriter/push_app_ite.h"
-#include "ast/rewriter/inj_axiom.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
-#include "ast/simplifier/array_simplifier_plugin.h"
-#include "ast/simplifier/datatype_simplifier_plugin.h"
-#include "ast/simplifier/fpa_simplifier_plugin.h"
-#include "ast/simplifier/seq_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
-#include "ast/simplifier/bv_elim.h"
-#include "ast/simplifier/elim_bounds.h"
-#include "ast/normal_forms/pull_quant.h"
-#include "ast/normal_forms/nnf.h"
-#include "ast/pattern/pattern_inference.h"
-#include "ast/rewriter/der.h"
-#include "ast/rewriter/distribute_forall.h"
-#include "ast/macros/quasi_macros.h"
-#include "smt/asserted_formulas.h"
-#include "smt/elim_term_ite.h"
-
-asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p):
-    m(m),
-    m_params(p),
-    m_pre_simplifier(m),
-    m_simplifier(m),
-    m_rewriter(m),
-    m_defined_names(m),
-    m_static_features(m),
-    m_asserted_formulas(m),
-    m_asserted_formula_prs(m),
-    m_asserted_qhead(0),
-    m_macro_manager(m),
-    m_bit2int(m),
-    m_bv_sharing(m),
-    m_inconsistent(false), 
-    m_has_quantifiers(false) {
-
-    m_bsimp = 0;
-    m_bvsimp = 0;
-    arith_simplifier_plugin * arith_simp = 0;
-    setup_simplifier_plugins(m_simplifier, m_bsimp, arith_simp, m_bvsimp);
-    SASSERT(m_bsimp != 0);
-    SASSERT(arith_simp != 0);
-    m_macro_finder = alloc(macro_finder, m, m_macro_manager);
-
-    basic_simplifier_plugin * basic_simp = 0;
-    bv_simplifier_plugin * bv_simp = 0;
-    setup_simplifier_plugins(m_pre_simplifier, basic_simp, arith_simp, bv_simp);
-    m_pre_simplifier.enable_presimp();
-}
-
-void asserted_formulas::setup() {
-    switch (m_params.m_lift_ite) {
-    case LI_FULL:
-        m_params.m_ng_lift_ite = LI_NONE;
-        break;
-    case LI_CONSERVATIVE:
-        if (m_params.m_ng_lift_ite == LI_CONSERVATIVE)
-            m_params.m_ng_lift_ite = LI_NONE;
-        break;
-    default:
-        break;
-    }
-
-    if (m_params.m_relevancy_lvl == 0)
-        m_params.m_relevancy_lemma = false;
-}
-
-void asserted_formulas::setup_simplifier_plugins(simplifier & s, basic_simplifier_plugin * & bsimp, arith_simplifier_plugin * & asimp, bv_simplifier_plugin * & bvsimp) {
-    bsimp = alloc(basic_simplifier_plugin, m);
-    s.register_plugin(bsimp);
-    asimp = alloc(arith_simplifier_plugin, m, *bsimp, m_params);
-    s.register_plugin(asimp);
-    s.register_plugin(alloc(array_simplifier_plugin, m, *bsimp, s, m_params));
-    bvsimp = alloc(bv_simplifier_plugin, m, *bsimp, m_params);
-    s.register_plugin(bvsimp);
-    s.register_plugin(alloc(datatype_simplifier_plugin, m, *bsimp));
-    s.register_plugin(alloc(fpa_simplifier_plugin, m, *bsimp));
-    s.register_plugin(alloc(seq_simplifier_plugin, m, *bsimp));
-}
-
-void asserted_formulas::init(unsigned num_formulas, expr * const * formulas, proof * const * prs) {
-    SASSERT(m_asserted_formulas.empty());
-    SASSERT(m_asserted_formula_prs.empty());
-    SASSERT(!m_inconsistent);
-    SASSERT(m_scopes.empty());
-    m_asserted_formulas.append(num_formulas, formulas);
-    if (m.proofs_enabled())
-        m_asserted_formula_prs.append(num_formulas, prs);
-}
-
-bool asserted_formulas::has_bv() const {
-    // approaximated answer... assume the formula has bit-vectors if the bv_simplifier_plugin was invoked at least once.
-    return m_bvsimp->reduce_invoked();
-}
-
-asserted_formulas::~asserted_formulas() {
-}
-
-void asserted_formulas::push_assertion(expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
-    if (inconsistent()) {
-        SASSERT(!result.empty());
-        return;
-    }
-    if (m.is_false(e))
-        m_inconsistent = true;
-    ::push_assertion(m, e, pr, result, result_prs);
-}
-
-void asserted_formulas::set_eliminate_and(bool flag) {
-    if (m_bsimp->eliminate_and() == flag)
-        return;
-    TRACE("eliminate_and", tout << "flushing cache...\n";);
-    flush_cache();
-    m_bsimp->set_eliminate_and(flag);
-}
-
-
-void asserted_formulas::assert_expr(expr * e, proof * _in_pr) {
-    if (inconsistent())
-        return;
-    m_has_quantifiers |= ::has_quantifiers(e);
-    if (!m_params.m_preprocess) {
-        push_assertion(e, _in_pr, m_asserted_formulas, m_asserted_formula_prs);
-        return;
-    }
-    proof_ref  in_pr(_in_pr, m);
-    expr_ref   r1(m);
-    proof_ref  pr1(m);
-    expr_ref   r2(m);
-    proof_ref  pr2(m);
-    TRACE("assert_expr_before_simp", tout << mk_ll_pp(e, m) << "\n";);
-    TRACE("assert_expr_bug", tout << mk_pp(e, m) << "\n";);
-    if (m_params.m_pre_simplifier) {
-        m_pre_simplifier(e, r1, pr1);
-    }
-    else {
-        r1  = e;
-        pr1 = 0;
-    }
-    set_eliminate_and(false); // do not eliminate and before nnf.
-    m_simplifier(r1, r2, pr2);
-    TRACE("assert_expr_bug", tout << "after...\n" << mk_pp(r1, m) << "\n";);
-    if (m.proofs_enabled()) {
-        if (e == r2)
-            pr2 = in_pr;
-        else
-            pr2 = m.mk_modus_ponens(in_pr, m.mk_transitivity(pr1, pr2));
-    }
-    TRACE("assert_expr_after_simp", tout << mk_ll_pp(r1, m) << "\n";);
-    push_assertion(r2, pr2, m_asserted_formulas, m_asserted_formula_prs);
-    TRACE("asserted_formulas_bug", tout << "after assert_expr\n"; display(tout););
-}
-
-void asserted_formulas::assert_expr(expr * e) {
-    if (inconsistent())
-        return;
-    assert_expr(e, m.mk_asserted(e));
-}
-
-void asserted_formulas::get_assertions(ptr_vector<expr> & result) {
-    result.append(m_asserted_formulas.size(), m_asserted_formulas.c_ptr());
-}
-
-void asserted_formulas::push_scope() {
-    SASSERT(inconsistent() || m_asserted_qhead == m_asserted_formulas.size() || m.canceled());
-    TRACE("asserted_formulas_scopes", tout << "push:\n"; display(tout););
-    m_scopes.push_back(scope());
-    m_macro_manager.push_scope();
-    scope & s = m_scopes.back();
-    s.m_asserted_formulas_lim    = m_asserted_formulas.size();
-    SASSERT(inconsistent() || s.m_asserted_formulas_lim == m_asserted_qhead || m.canceled());
-    s.m_inconsistent_old         = m_inconsistent;
-    m_defined_names.push();
-    m_bv_sharing.push_scope();
-    commit();
-}
-
-void asserted_formulas::pop_scope(unsigned num_scopes) {
-    TRACE("asserted_formulas_scopes", tout << "before pop " << num_scopes << "\n"; display(tout););
-    m_bv_sharing.pop_scope(num_scopes);
-    m_macro_manager.pop_scope(num_scopes);
-    unsigned new_lvl    = m_scopes.size() - num_scopes;
-    scope & s           = m_scopes[new_lvl];
-    m_inconsistent      = s.m_inconsistent_old;
-    m_defined_names.pop(num_scopes);
-    m_asserted_formulas.shrink(s.m_asserted_formulas_lim);
-    if (m.proofs_enabled())
-        m_asserted_formula_prs.shrink(s.m_asserted_formulas_lim);
-    m_asserted_qhead    = s.m_asserted_formulas_lim;
-    m_scopes.shrink(new_lvl);
-    flush_cache();
-    TRACE("asserted_formulas_scopes", tout << "after pop " << num_scopes << "\n"; display(tout););
-}
-
-void asserted_formulas::reset() {
-    m_defined_names.reset();
-    m_asserted_qhead = 0;
-    m_asserted_formulas.reset();
-    m_asserted_formula_prs.reset();
-    m_macro_manager.reset();
-    m_bv_sharing.reset();
-    m_inconsistent = false;
-}
-
-
-#ifdef Z3DEBUG
-bool asserted_formulas::check_well_sorted() const {
-    for (unsigned i = 0; i < m_asserted_formulas.size(); i++) {
-        if (!is_well_sorted(m, m_asserted_formulas.get(i))) return false;
-    }
-    return true;
-}
-#endif
-
-void asserted_formulas::reduce() {
-    if (inconsistent())
-        return;
-    if (canceled()) {
-        return;
-    }
-    if (m_asserted_qhead == m_asserted_formulas.size())
-        return;
-    if (!m_params.m_preprocess)
-        return;
-
-    if (m_macro_manager.has_macros())
-        expand_macros();
-    TRACE("before_reduce", display(tout););
-    CASSERT("well_sorted", check_well_sorted());
-
-
-#define INVOKE(COND, FUNC) if (COND) { FUNC; IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); }  TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); TRACE("reduce_step", display(tout << #FUNC << " ");); CASSERT("well_sorted",check_well_sorted()); if (inconsistent() || canceled()) { TRACE("after_reduce", display(tout);); TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); return;  }
-
-    set_eliminate_and(false); // do not eliminate and before nnf.
-    INVOKE(m_params.m_propagate_booleans, propagate_booleans());
-    INVOKE(m_params.m_propagate_values, propagate_values());
-    INVOKE(m_params.m_macro_finder && has_quantifiers(), find_macros());
-    INVOKE(m_params.m_nnf_cnf || (m_params.m_mbqi && has_quantifiers()), nnf_cnf());
-    INVOKE(/*m_params.m_eliminate_and*/ true, eliminate_and());
-    INVOKE(m_params.m_pull_cheap_ite_trees, pull_cheap_ite_trees());
-    INVOKE(m_params.m_pull_nested_quantifiers && has_quantifiers(), pull_nested_quantifiers());
-    INVOKE(m_params.m_ng_lift_ite != LI_NONE, ng_lift_ite());
-    INVOKE(m_params.m_lift_ite != LI_NONE, lift_ite());
-    INVOKE(m_params.m_eliminate_term_ite && m_params.m_lift_ite != LI_FULL, eliminate_term_ite());
-    INVOKE(m_params.m_refine_inj_axiom && has_quantifiers(), refine_inj_axiom());
-    INVOKE(m_params.m_distribute_forall && has_quantifiers(), apply_distribute_forall());
-    TRACE("qbv_bug", tout << "after distribute_forall:\n"; display(tout););
-    INVOKE(m_params.m_macro_finder && has_quantifiers(), find_macros());
-    INVOKE(m_params.m_quasi_macros && has_quantifiers(), apply_quasi_macros());
-    INVOKE(m_params.m_simplify_bit2int, apply_bit2int());
-    INVOKE(m_params.m_eliminate_bounds && has_quantifiers(), cheap_quant_fourier_motzkin());
-    INVOKE(m_params.m_ematching && has_quantifiers(), infer_patterns());
-    INVOKE(m_params.m_max_bv_sharing && has_bv(), max_bv_sharing());
-    INVOKE(m_params.m_bb_quantifiers, elim_bvs_from_quantifiers());
-    // temporary HACK: make sure that arith & bv are list-assoc
-    // this may destroy some simplification steps such as max_bv_sharing
-    reduce_asserted_formulas();
-
-    CASSERT("well_sorted",check_well_sorted());
-
-    IF_VERBOSE(10, verbose_stream() << "(smt.simplifier-done)\n";);
-    TRACE("after_reduce", display(tout););
-    TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited););
-    TRACE("macros", m_macro_manager.display(tout););
-    flush_cache();
-}
-
-void asserted_formulas::eliminate_and() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.eliminating-and)\n";);
-    set_eliminate_and(true);
-    reduce_asserted_formulas();
-    TRACE("after_elim_and", display(tout););
-}
-
-unsigned asserted_formulas::get_formulas_last_level() const {
-    if (m_scopes.empty()) {
-        return 0;
-    }
-    else {
-        return m_scopes.back().m_asserted_formulas_lim;
-    }
-}
-
-void asserted_formulas::collect_static_features() {
-    if (m_params.m_display_features) {
-        unsigned sz   = m_asserted_formulas.size();
-        unsigned head = m_asserted_qhead;
-        while (head < sz) {
-            expr * f   = m_asserted_formulas.get(head);
-            head++;
-            m_static_features.collect(f);
-        }
-        m_static_features.display_primitive(std::cout);
-        m_static_features.display(std::cout);
-    }
-}
-
-void asserted_formulas::display(std::ostream & out) const {
-    out << "asserted formulas:\n";
-    for (unsigned i = 0; i < m_asserted_formulas.size(); i++) {
-        if (i == m_asserted_qhead)
-            out << "[HEAD] ==>\n";
-        out << mk_pp(m_asserted_formulas.get(i), m) << "\n";
-    }
-    out << "inconsistent: " << inconsistent() << "\n";
-}
-
-void asserted_formulas::display_ll(std::ostream & out, ast_mark & pp_visited) const {
-    if (!m_asserted_formulas.empty()) {
-        unsigned sz = m_asserted_formulas.size();
-        for (unsigned i = 0; i < sz; i++)
-            ast_def_ll_pp(out, m, m_asserted_formulas.get(i), pp_visited, true, false);
-        out << "asserted formulas:\n";
-        for (unsigned i = 0; i < sz; i++)
-            out << "#" << m_asserted_formulas[i]->get_id() << " ";
-        out << "\n";
-    }
-}
-
-void asserted_formulas::collect_statistics(statistics & st) const {
-}
-
-void asserted_formulas::reduce_asserted_formulas() {
-    if (inconsistent()) {
-        return;
-    }
-    expr_ref_vector  new_exprs(m);
-    proof_ref_vector new_prs(m);
-    unsigned i  = m_asserted_qhead;
-    unsigned sz = m_asserted_formulas.size();
-    for (; i < sz && !inconsistent(); i++) {
-        expr * n    = m_asserted_formulas.get(i);
-        SASSERT(n != 0);
-        proof * pr  = m_asserted_formula_prs.get(i, 0);
-        expr_ref new_n(m);
-        proof_ref new_pr(m);
-        m_simplifier(n, new_n, new_pr);
-        TRACE("reduce_asserted_formulas", tout << mk_pp(n, m) << " -> " << mk_pp(new_n, m) << "\n";);
-        if (n == new_n.get()) {
-            push_assertion(n, pr, new_exprs, new_prs);
-        }
-        else {
-            new_pr = m.mk_modus_ponens(pr, new_pr);
-            push_assertion(new_n, new_pr, new_exprs, new_prs);
-        }
-        if (canceled()) {
-            return;
-        }
-    }
-    swap_asserted_formulas(new_exprs, new_prs);
-}
-
-void asserted_formulas::swap_asserted_formulas(expr_ref_vector & new_exprs, proof_ref_vector & new_prs) {
-    SASSERT(!inconsistent() || !new_exprs.empty());
-    m_asserted_formulas.shrink(m_asserted_qhead);
-    m_asserted_formulas.append(new_exprs);
-    if (m.proofs_enabled()) {
-        m_asserted_formula_prs.shrink(m_asserted_qhead);
-        m_asserted_formula_prs.append(new_prs);
-    }
-}
-
-void asserted_formulas::find_macros_core() {
-    expr_ref_vector  new_exprs(m);
-    proof_ref_vector new_prs(m);
-    unsigned sz = m_asserted_formulas.size();
-    expr_dependency_ref_vector new_deps(m);
-    m_macro_finder->operator()(sz - m_asserted_qhead,
-                               m_asserted_formulas.c_ptr() + m_asserted_qhead,
-                               m_asserted_formula_prs.c_ptr() + m_asserted_qhead,
-                               0, // 0 == No dependency tracking
-                               new_exprs, new_prs, new_deps);
-    swap_asserted_formulas(new_exprs, new_prs);
-    reduce_and_solve();
-}
-
-void asserted_formulas::find_macros() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.find-macros)\n";);
-    TRACE("before_find_macros", display(tout););
-    find_macros_core();
-    TRACE("after_find_macros", display(tout););
-}
-
-void asserted_formulas::expand_macros() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.expand-macros)\n";);
-    find_macros_core();
-}
-
-void asserted_formulas::apply_quasi_macros() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.find-quasi-macros)\n";);
-    TRACE("before_quasi_macros", display(tout););
-    expr_ref_vector  new_exprs(m);
-    proof_ref_vector new_prs(m);      
-    expr_dependency_ref_vector new_deps(m);
-    quasi_macros proc(m, m_macro_manager);    
-    while (proc(m_asserted_formulas.size() - m_asserted_qhead, 
-                m_asserted_formulas.c_ptr() + m_asserted_qhead, 
-                m_asserted_formula_prs.c_ptr() + m_asserted_qhead,
-                0, // 0 == No dependency tracking
-                new_exprs, new_prs, new_deps)) {
-        swap_asserted_formulas(new_exprs, new_prs);
-        new_exprs.reset();
-        new_prs.reset();
-        new_deps.reset();
-    }
-    TRACE("after_quasi_macros", display(tout););
-    reduce_and_solve();
-}
-
-void asserted_formulas::nnf_cnf() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.nnf)\n";);
-    nnf              apply_nnf(m, m_defined_names);
-    expr_ref_vector  new_exprs(m);
-    proof_ref_vector new_prs(m);
-    expr_ref_vector  push_todo(m);
-    proof_ref_vector push_todo_prs(m);
-
-    unsigned i  = m_asserted_qhead;
-    unsigned sz = m_asserted_formulas.size();
-    TRACE("nnf_bug", tout << "i: " << i << " sz: " << sz << "\n";);
-    for (; i < sz; i++) {
-        expr * n    = m_asserted_formulas.get(i);
-        TRACE("nnf_bug", tout << "processing:\n" << mk_pp(n, m) << "\n";);
-        proof * pr  = m_asserted_formula_prs.get(i, 0);
-        expr_ref   r1(m);
-        proof_ref  pr1(m);
-        CASSERT("well_sorted",is_well_sorted(m, n));
-        push_todo.reset();
-        push_todo_prs.reset();
-        apply_nnf(n, push_todo, push_todo_prs, r1, pr1);
-        CASSERT("well_sorted",is_well_sorted(m, r1));
-        pr = m.mk_modus_ponens(pr, pr1);
-        push_todo.push_back(r1);
-        push_todo_prs.push_back(pr);
-
-        if (canceled()) {
-            return;
-        }
-        unsigned sz2 = push_todo.size();
-        for (unsigned k = 0; k < sz2; k++) {
-            expr * n   = push_todo.get(k);
-            proof * pr = 0;
-            m_simplifier(n, r1, pr1);
-            CASSERT("well_sorted",is_well_sorted(m, r1));
-            if (canceled()) {
-                return;
-            }
-
-            if (m.proofs_enabled())
-                pr = m.mk_modus_ponens(push_todo_prs.get(k), pr1);
-            else
-                pr = 0;
-            push_assertion(r1, pr, new_exprs, new_prs);
-        }
-    }
-    swap_asserted_formulas(new_exprs, new_prs);
-}
-
-#define MK_SIMPLE_SIMPLIFIER(NAME, FUNCTOR_DEF, LABEL, MSG)                                                             \
-void asserted_formulas::NAME() {                                                                                        \
-    IF_IVERBOSE(10, verbose_stream() << "(smt." << MSG << ")\n";);     \
-    TRACE(LABEL, tout << "before:\n"; display(tout););                                                                  \
-    FUNCTOR_DEF;                                                                                                        \
-    expr_ref_vector  new_exprs(m);                                                                              \
-    proof_ref_vector new_prs(m);                                                                                \
-    unsigned i  = m_asserted_qhead;                                                                                     \
-    unsigned sz = m_asserted_formulas.size();                                                                           \
-    for (; i < sz; i++) {                                                                                               \
-        expr * n    = m_asserted_formulas.get(i);                                                                       \
-        proof * pr  = m_asserted_formula_prs.get(i, 0);                                                                 \
-        expr_ref new_n(m);                                                                                      \
-        functor(n, new_n);                                                                                              \
-        TRACE("simplifier_simple_step", tout << mk_pp(n, m) << "\n" << mk_pp(new_n, m) << "\n";);       \
-        if (n == new_n.get()) {                                                                                         \
-            push_assertion(n, pr, new_exprs, new_prs);                                                                  \
-        }                                                                                                               \
-        else if (m.proofs_enabled()) {                                                                          \
-            proof_ref new_pr(m);                                                                                \
-            new_pr = m.mk_rewrite_star(n, new_n, 0, 0);            \
-            new_pr = m.mk_modus_ponens(pr, new_pr);                                                             \
-            push_assertion(new_n, new_pr, new_exprs, new_prs);                                                          \
-        }                                                                                                               \
-        else {                                                                                                          \
-            push_assertion(new_n, 0, new_exprs, new_prs);                                                               \
-        }                                                                                                               \
-    }                                                                                                                   \
-    swap_asserted_formulas(new_exprs, new_prs);                                                                         \
-    TRACE(LABEL, display(tout););                                                                                       \
-    reduce_and_solve();                                                                                                 \
-    TRACE(LABEL, display(tout););                                                                                       \
-}
-
-MK_SIMPLE_SIMPLIFIER(apply_distribute_forall, distribute_forall functor(m), "distribute_forall", "distribute-forall");
-
-void asserted_formulas::reduce_and_solve() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.reducing)\n";);
-    flush_cache(); // collect garbage
-    reduce_asserted_formulas();
-}
-
-void asserted_formulas::infer_patterns() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.pattern-inference)\n";);
-    TRACE("before_pattern_inference", display(tout););
-    pattern_inference_rw infer(m, m_params);
-    expr_ref_vector  new_exprs(m);
-    proof_ref_vector new_prs(m);
-    unsigned i  = m_asserted_qhead;
-    unsigned sz = m_asserted_formulas.size();
-    for (; i < sz; i++) {
-        expr * n    = m_asserted_formulas.get(i);
-        proof * pr  = m_asserted_formula_prs.get(i, 0);
-        expr_ref  new_n(m);
-        proof_ref new_pr(m);
-        infer(n, new_n, new_pr);
-        if (n == new_n.get()) {
-            push_assertion(n, pr, new_exprs, new_prs);
-        }
-        else if (m.proofs_enabled()) {
-            new_pr = m.mk_modus_ponens(pr, new_pr);
-            push_assertion(new_n, new_pr, new_exprs, new_prs);
-        }
-        else {
-            push_assertion(new_n, 0, new_exprs, new_prs);
-        }
-    }
-    swap_asserted_formulas(new_exprs, new_prs);
-    TRACE("after_pattern_inference", display(tout););
-}
-
-void asserted_formulas::commit() {
-    commit(m_asserted_formulas.size());
-}
-
-void asserted_formulas::commit(unsigned new_qhead) {
-    m_macro_manager.mark_forbidden(new_qhead - m_asserted_qhead, m_asserted_formulas.c_ptr() + m_asserted_qhead);
-    m_asserted_qhead = new_qhead;
-}
-
-void asserted_formulas::eliminate_term_ite() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.eliminating-ite-term)\n";);
-    TRACE("before_elim_term_ite", display(tout););
-    elim_term_ite   elim(m, m_defined_names);
-    expr_ref_vector  new_exprs(m);
-    proof_ref_vector new_prs(m);
-    unsigned i  = m_asserted_qhead;
-    unsigned sz = m_asserted_formulas.size();
-    for (; i < sz; i++) {
-        expr * n    = m_asserted_formulas.get(i);
-        proof * pr  = m_asserted_formula_prs.get(i, 0);
-        expr_ref  new_n(m);
-        proof_ref new_pr(m);
-        elim(n, new_exprs, new_prs, new_n, new_pr);
-        SASSERT(new_n.get() != 0);
-        DEBUG_CODE({
-                for (unsigned i = 0; i < new_exprs.size(); i++) {
-                    SASSERT(new_exprs.get(i) != 0);
-                }
-            });
-        if (n == new_n.get()) {
-            push_assertion(n, pr, new_exprs, new_prs);
-        }
-        else if (m.proofs_enabled()) {
-            new_pr = m.mk_modus_ponens(pr, new_pr);
-            push_assertion(new_n, new_pr, new_exprs, new_prs);
-        }
-        else {
-            push_assertion(new_n, 0, new_exprs, new_prs);
-        }
-    }
-    swap_asserted_formulas(new_exprs, new_prs);
-    TRACE("after_elim_term_ite", display(tout););
-    reduce_and_solve();
-    TRACE("after_elim_term_ite", display(tout););
-}
-
-void asserted_formulas::propagate_values() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.constant-propagation)\n";);
-    TRACE("propagate_values", tout << "before:\n"; display(tout););
-    flush_cache();
-    bool found = false;
-    // Separate the formulas in two sets: C and R
-    // C is a set which contains formulas of the form
-    // { x = n }, where x is a variable and n a numeral.
-    // R contains the rest.
-    //
-    // - new_exprs1 is the set C
-    // - new_exprs2 is the set R
-    //
-    // The loop also updates the m_cache. It adds the entries x -> n to it.
-    expr_ref_vector  new_exprs1(m);
-    proof_ref_vector new_prs1(m);
-    expr_ref_vector  new_exprs2(m);
-    proof_ref_vector new_prs2(m);
-    unsigned sz = m_asserted_formulas.size();
-    for (unsigned i = 0; i < sz; i++) {
-        expr_ref   n(m_asserted_formulas.get(i), m);
-        proof_ref pr(m_asserted_formula_prs.get(i, 0), m);
-        TRACE("simplifier", tout << mk_pp(n, m) << "\n";);
-        expr* lhs, *rhs;
-        if (m.is_eq(n, lhs, rhs) &&
-            (m.is_value(lhs) || m.is_value(rhs))) {
-            if (m.is_value(lhs)) {
-                std::swap(lhs, rhs);
-                n  = m.mk_eq(lhs, rhs);
-                pr = m.mk_symmetry(pr);
-            }
-            if (!m.is_value(lhs) && !m_simplifier.is_cached(lhs)) {
-                if (i >= m_asserted_qhead) {
-                    new_exprs1.push_back(n);
-                    if (m.proofs_enabled())
-                        new_prs1.push_back(pr);
-                }
-                TRACE("propagate_values", tout << "found:\n" << mk_pp(lhs, m) << "\n->\n" << mk_pp(rhs, m) << "\n";
-                      if (pr) tout << "proof: " << mk_pp(pr, m) << "\n";);
-                m_simplifier.cache_result(lhs, rhs, pr);
-                found = true;
-                continue;
-            }
-        }
-        if (i >= m_asserted_qhead) {
-            new_exprs2.push_back(n);
-            if (m.proofs_enabled())
-                new_prs2.push_back(pr);
-        }
-    }
-    TRACE("propagate_values", tout << "found: " << found << "\n";);
-    // If C is not empty, then reduce R using the updated simplifier cache with entries
-    // x -> n for each constraint 'x = n' in C.
-    if (found) {
-        unsigned sz = new_exprs2.size();
-        for (unsigned i = 0; i < sz; i++) {
-            expr * n    = new_exprs2.get(i);
-            proof * pr  = new_prs2.get(i, 0);
-            expr_ref new_n(m);
-            proof_ref new_pr(m);
-            m_simplifier(n, new_n, new_pr);
-            if (n == new_n.get()) {
-                push_assertion(n, pr, new_exprs1, new_prs1);
-            }
-            else {
-                new_pr = m.mk_modus_ponens(pr, new_pr);
-                push_assertion(new_n, new_pr, new_exprs1, new_prs1);
-            }
-        }
-        swap_asserted_formulas(new_exprs1, new_prs1);
-        // IMPORTANT: the cache MUST be flushed. This guarantees that all entries
-        // x->n will be removed from m_cache. If we don't do that, the next transformation
-        // may simplify constraints in C using these entries, and the variables x in C
-        // will be (silently) eliminated, and models produced by Z3 will not contain them.
-        flush_cache();
-    }
-    TRACE("propagate_values", tout << "after:\n"; display(tout););
-}
-
-void asserted_formulas::propagate_booleans() {
-    bool cont     = true;
-    bool modified = false;
-    flush_cache();
-    while (cont) {
-        TRACE("propagate_booleans", tout << "before:\n"; display(tout););
-        IF_IVERBOSE(10, verbose_stream() << "(smt.propagate-booleans)\n";);
-        cont        = false;
-        unsigned i  = m_asserted_qhead;
-        unsigned sz = m_asserted_formulas.size();
-#define PROCESS() {                                                     \
-            expr * n    = m_asserted_formulas.get(i);                   \
-            proof * pr  = m_asserted_formula_prs.get(i, 0);             \
-            expr_ref new_n(m);                                          \
-            proof_ref new_pr(m);                                        \
-            m_simplifier(n, new_n, new_pr);                             \
-            m_asserted_formulas.set(i, new_n);                          \
-            if (m.proofs_enabled()) {                                   \
-                new_pr = m.mk_modus_ponens(pr, new_pr);                 \
-                m_asserted_formula_prs.set(i, new_pr);                  \
-            }                                                           \
-            if (n != new_n) {                                           \
-                cont = true;                                            \
-                modified = true;                                        \
-            }                                                           \
-            if (m.is_not(new_n))                                        \
-                m_simplifier.cache_result(to_app(new_n)->get_arg(0), m.mk_false(), m.mk_iff_false(new_pr)); \
-            else                                                        \
-                m_simplifier.cache_result(new_n, m.mk_true(), m.mk_iff_true(new_pr)); \
-        }
-        for (; i < sz; i++) {
-            PROCESS();
-        }
-        flush_cache();
-        TRACE("propagate_booleans", tout << "middle:\n"; display(tout););
-        i = sz;
-        while (i > m_asserted_qhead) {
-            --i;
-            PROCESS();
-        }
-        flush_cache();
-        TRACE("propagate_booleans", tout << "after:\n"; display(tout););
-    }
-    if (modified)
-        reduce_asserted_formulas();
-}
-
-#define MK_SIMPLIFIER(NAME, FUNCTOR, TAG, MSG, REDUCE)                  \
-    bool asserted_formulas::NAME() {                                    \
-        IF_IVERBOSE(10, verbose_stream() << "(smt." << MSG << ")\n";);  \
-        TRACE(TAG, ast_mark visited; display_ll(tout, visited););       \
-        FUNCTOR;                                                        \
-        bool changed = false;                                           \
-        expr_ref_vector  new_exprs(m);                                  \
-        proof_ref_vector new_prs(m);                                    \
-        unsigned i  = m_asserted_qhead;                                 \
-        unsigned sz = m_asserted_formulas.size();                       \
-        for (; i < sz; i++) {                                           \
-            expr * n    = m_asserted_formulas.get(i);                   \
-            proof * pr  = m_asserted_formula_prs.get(i, 0);             \
-            expr_ref  new_n(m);                                         \
-            proof_ref new_pr(m);                                        \
-            functor(n, new_n, new_pr);                                  \
-            if (n == new_n.get()) {                                     \
-                push_assertion(n, pr, new_exprs, new_prs);              \
-            }                                                           \
-            else if (m.proofs_enabled()) {                              \
-                changed = true;                                         \
-                if (!new_pr) new_pr = m.mk_rewrite(n, new_n);           \
-                new_pr = m.mk_modus_ponens(pr, new_pr);                 \
-                push_assertion(new_n, new_pr, new_exprs, new_prs);      \
-            }                                                           \
-            else {                                                      \
-                changed = true;                                         \
-                push_assertion(new_n, 0, new_exprs, new_prs);           \
-            }                                                           \
-        }                                                               \
-        swap_asserted_formulas(new_exprs, new_prs);                     \
-        TRACE(TAG, ast_mark visited; display_ll(tout, visited););       \
-        if (changed && REDUCE) {                                        \
-            reduce_and_solve();                                         \
-            TRACE(TAG, ast_mark visited; display_ll(tout, visited););   \
-        }                                                               \
-        return changed;                                                 \
-    }
-
-MK_SIMPLIFIER(pull_cheap_ite_trees, pull_cheap_ite_tree_rw functor(m), "pull_cheap_ite_trees", "pull-cheap-ite-trees", false);
-
-MK_SIMPLIFIER(pull_nested_quantifiers, pull_nested_quant functor(m), "pull_nested_quantifiers", "pull-nested-quantifiers", false);
-
-proof * asserted_formulas::get_inconsistency_proof() const {
-    if (!inconsistent())
-        return 0;
-    if (!m.proofs_enabled())
-        return 0;
-    unsigned sz = m_asserted_formulas.size();
-    for (unsigned i = 0; i < sz; i++) {
-        expr * f = m_asserted_formulas.get(i);
-        if (m.is_false(f))
-            return m_asserted_formula_prs.get(i);
-    }
-    UNREACHABLE();
-    return 0;
-}
-
-void asserted_formulas::refine_inj_axiom() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.refine-injectivity)\n";);
-    TRACE("inj_axiom", display(tout););
-    unsigned i  = m_asserted_qhead;
-    unsigned sz = m_asserted_formulas.size();
-    for (; i < sz; i++) {
-        expr * n    = m_asserted_formulas.get(i);
-        proof * pr  = m_asserted_formula_prs.get(i, 0);
-        expr_ref new_n(m);
-        if (is_quantifier(n) && simplify_inj_axiom(m, to_quantifier(n), new_n)) {
-            TRACE("inj_axiom", tout << "simplifying...\n" << mk_pp(n, m) << "\n" << mk_pp(new_n, m) << "\n";);
-            m_asserted_formulas.set(i, new_n);
-            if (m.proofs_enabled()) {
-                proof_ref new_pr(m);
-                new_pr = m.mk_rewrite(n, new_n);
-                new_pr = m.mk_modus_ponens(pr, new_pr);
-                m_asserted_formula_prs.set(i, new_pr);
-            }
-        }
-    }
-    TRACE("inj_axiom", display(tout););
-}
-
-MK_SIMPLIFIER(apply_bit2int, bit2int& functor = m_bit2int, "bit2int", "propagate-bit-vector-over-integers", true);
-
-MK_SIMPLIFIER(cheap_quant_fourier_motzkin, elim_bounds_star functor(m), "elim_bounds", "cheap-fourier-motzkin", true);
-
-
-
-MK_SIMPLIFIER(elim_bvs_from_quantifiers, bv_elim_star functor(m), "bv_elim", "eliminate-bit-vectors-from-quantifiers", true);
-
-#define LIFT_ITE(NAME, FUNCTOR, MSG)                                    \
-    void asserted_formulas::NAME() {                                    \
-        IF_IVERBOSE(10, verbose_stream() << "(smt." << MSG << ")\n";);  \
-        TRACE("lift_ite", display(tout););                              \
-        FUNCTOR;                                                        \
-        unsigned i  = m_asserted_qhead;                                 \
-        unsigned sz = m_asserted_formulas.size();                       \
-        for (; i < sz; i++) {                                           \
-            expr * n    = m_asserted_formulas.get(i);                   \
-            proof * pr  = m_asserted_formula_prs.get(i, 0);             \
-            expr_ref  new_n(m);                                         \
-            proof_ref new_pr(m);                                        \
-            functor(n, new_n, new_pr);                                  \
-            TRACE("lift_ite_step", tout << mk_pp(n, m) << "\n";);       \
-            IF_IVERBOSE(10000, verbose_stream() << "lift before: " << get_num_exprs(n)  << ", after: " << get_num_exprs(new_n) << "\n";); \
-            m_asserted_formulas.set(i, new_n);                          \
-            if (m.proofs_enabled()) {                                   \
-                new_pr = m.mk_modus_ponens(pr, new_pr);                 \
-                m_asserted_formula_prs.set(i, new_pr);                  \
-            }                                                           \
-        }                                                               \
-        TRACE("lift_ite", display(tout););                              \
-        reduce_and_solve();                                             \
-    }
-
-LIFT_ITE(lift_ite, push_app_ite_rw functor(m, m_params.m_lift_ite == LI_CONSERVATIVE), "lifting ite");
-LIFT_ITE(ng_lift_ite, ng_push_app_ite_rw functor(m, m_params.m_ng_lift_ite == LI_CONSERVATIVE), "lifting ng ite");
-
-unsigned asserted_formulas::get_total_size() const {
-    expr_mark visited;
-    unsigned r  = 0;
-    unsigned sz = m_asserted_formulas.size();
-    for (unsigned i = 0; i < sz; i++)
-        r += get_num_exprs(m_asserted_formulas.get(i), visited);
-    return r;
-}
-
-void asserted_formulas::max_bv_sharing() {
-    IF_IVERBOSE(10, verbose_stream() << "(smt.maximizing-bv-sharing)\n";);
-    TRACE("bv_sharing", display(tout););
-    unsigned i  = m_asserted_qhead;
-    unsigned sz = m_asserted_formulas.size();
-    for (; i < sz; i++) {
-        expr * n    = m_asserted_formulas.get(i);
-        proof * pr  = m_asserted_formula_prs.get(i, 0);
-        expr_ref new_n(m);
-        proof_ref new_pr(m);
-        m_bv_sharing(n, new_n, new_pr);
-        m_asserted_formulas.set(i, new_n);
-        if (m.proofs_enabled()) {
-            new_pr = m.mk_modus_ponens(pr, new_pr);
-            m_asserted_formula_prs.set(i, new_pr);
-        }
-    }
-    reduce_asserted_formulas();
-    TRACE("bv_sharing", display(tout););
-
-}
-
-#ifdef Z3DEBUG
-void pp(asserted_formulas & f) {
-    f.display(std::cout);
-}
-#endif
diff --git a/src/smt/asserted_formulas.h b/src/smt/asserted_formulas.h
deleted file mode 100644
index 1e15e6300..000000000
--- a/src/smt/asserted_formulas.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    asserted_formulas.h
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-06-11.
-
-Revision History:
-
---*/
-#ifndef ASSERTED_FORMULAS_H_
-#define ASSERTED_FORMULAS_H_
-
-#include "util/statistics.h"
-#include "ast/static_features.h"
-#include "ast/simplifier/simplifier.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/maximise_ac_sharing.h"
-#include "ast/rewriter/bit2int.h"
-#include "ast/macros/macro_manager.h"
-#include "ast/macros/macro_finder.h"
-#include "ast/normal_forms/defined_names.h"
-#include "ast/pattern/pattern_inference.h"
-#include "smt/params/smt_params.h"
-#include "ast/rewriter/th_rewriter.h"
-
-class arith_simplifier_plugin;
-class bv_simplifier_plugin;
-
-class asserted_formulas {
-    ast_manager &                m;
-    smt_params &                 m_params;
-    simplifier                   m_pre_simplifier;
-    simplifier                   m_simplifier;
-    th_rewriter                  m_rewriter;
-    basic_simplifier_plugin *    m_bsimp;
-    bv_simplifier_plugin *       m_bvsimp;
-    defined_names                m_defined_names;
-    static_features              m_static_features;
-    expr_ref_vector              m_asserted_formulas;     // formulas asserted by user
-    proof_ref_vector             m_asserted_formula_prs;  // proofs for the asserted formulas.
-    unsigned                     m_asserted_qhead;
-
-    macro_manager               m_macro_manager;
-    scoped_ptr<macro_finder>    m_macro_finder;
-
-    bit2int                     m_bit2int;
-
-    maximise_bv_sharing         m_bv_sharing;
-
-    bool                        m_inconsistent;
-    bool                        m_has_quantifiers;
-
-    struct scope {
-        unsigned                m_asserted_formulas_lim;
-        bool                    m_inconsistent_old;
-    };
-    svector<scope>              m_scopes;
-
-    void setup_simplifier_plugins(simplifier & s, basic_simplifier_plugin * & bsimp, arith_simplifier_plugin * & asimp, bv_simplifier_plugin * & bvsimp);
-    void reduce_asserted_formulas();
-    void swap_asserted_formulas(expr_ref_vector & new_exprs, proof_ref_vector & new_prs);
-    void find_macros_core();
-    void find_macros();
-    void expand_macros();
-    void apply_quasi_macros();
-    void nnf_cnf();
-    void infer_patterns();
-    void eliminate_term_ite();
-    void reduce_and_solve();
-    void flush_cache() { m_pre_simplifier.reset(); m_simplifier.reset(); }
-    void set_eliminate_and(bool flag);
-    void propagate_values();
-    void propagate_booleans();
-    bool pull_cheap_ite_trees();
-    bool pull_nested_quantifiers();
-    void push_assertion(expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs);
-    void eliminate_and();
-    void refine_inj_axiom();
-    bool cheap_quant_fourier_motzkin();
-    void apply_distribute_forall();
-    bool apply_bit2int();
-    void lift_ite();
-    bool elim_bvs_from_quantifiers();
-    void ng_lift_ite();
-#ifdef Z3DEBUG
-    bool check_well_sorted() const;
-#endif
-    unsigned get_total_size() const;
-    bool has_bv() const;
-    void max_bv_sharing();
-    bool canceled() { return m.canceled(); }
-
-public:
-    asserted_formulas(ast_manager & m, smt_params & p);
-    ~asserted_formulas();
-
-    void setup();
-    void assert_expr(expr * e, proof * in_pr);
-    void assert_expr(expr * e);
-    void reset();
-    void push_scope();
-    void pop_scope(unsigned num_scopes);
-    bool inconsistent() const { return m_inconsistent; }
-    proof * get_inconsistency_proof() const;
-    void reduce();
-    unsigned get_num_formulas() const { return m_asserted_formulas.size(); }
-    unsigned get_formulas_last_level() const;
-    unsigned get_qhead() const { return m_asserted_qhead; }
-    void commit();
-    void commit(unsigned new_qhead);
-    expr * get_formula(unsigned idx) const { return m_asserted_formulas.get(idx); }
-    proof * get_formula_proof(unsigned idx) const { return m.proofs_enabled() ? m_asserted_formula_prs.get(idx) : 0; }
-    expr * const * get_formulas() const { return m_asserted_formulas.c_ptr(); }
-    proof * const * get_formula_proofs() const { return m_asserted_formula_prs.c_ptr(); }
-    void init(unsigned num_formulas, expr * const * formulas, proof * const * prs);
-    void register_simplifier_plugin(simplifier_plugin * p) { m_simplifier.register_plugin(p); }
-    // simplifier & get_simplifier() { return m_simplifier; }
-    th_rewriter& get_rewriter() { return m_rewriter; }
-    void get_assertions(ptr_vector<expr> & result);
-    bool empty() const { return m_asserted_formulas.empty(); }
-    void collect_static_features();
-    void display(std::ostream & out) const;
-    void display_ll(std::ostream & out, ast_mark & pp_visited) const;
-    void collect_statistics(statistics & st) const;
-    // TODO: improve precision of the following method.
-    bool has_quantifiers() const { return m_has_quantifiers; }
-
-    // -----------------------------------
-    //
-    // Macros
-    //
-    // -----------------------------------
-    unsigned get_num_macros() const { return m_macro_manager.get_num_macros(); }
-    unsigned get_first_macro_last_level() const { return m_macro_manager.get_first_macro_last_level(); }
-    func_decl * get_macro_func_decl(unsigned i) const { return m_macro_manager.get_macro_func_decl(i); }
-    func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const { return m_macro_manager.get_macro_interpretation(i, interp); }
-    quantifier * get_macro_quantifier(func_decl * f) const { return m_macro_manager.get_macro_quantifier(f); }
-    void insert_macro(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep) { m_macro_manager.insert(f, m, pr, dep); }
-};
-
-#endif /* ASSERTED_FORMULAS_H_ */
-
diff --git a/src/smt/asserted_formulas_new.cpp b/src/smt/asserted_formulas_new.cpp
index 5d4783bf8..8d16ef2cd 100644
--- a/src/smt/asserted_formulas_new.cpp
+++ b/src/smt/asserted_formulas_new.cpp
@@ -431,7 +431,7 @@ void asserted_formulas_new::propagate_values() {
     flush_cache();
 
     unsigned num_prop = 0;
-    while (true) {
+    while (!inconsistent()) {
         m_expr2depth.reset();
         m_scoped_substitution.push();
         unsigned prop = num_prop;
@@ -474,6 +474,9 @@ unsigned asserted_formulas_new::propagate_values(unsigned i) {
     }
     justified_expr j(m, new_n, new_pr);
     m_formulas[i] = j;
+    if (m.is_false(j.get_fml())) {
+        m_inconsistent = true;
+    }
     update_substitution(new_n, new_pr);
     return n != new_n ? 1 : 0;
 }
@@ -484,13 +487,16 @@ void asserted_formulas_new::update_substitution(expr* n, proof* pr) {
         compute_depth(lhs);
         compute_depth(rhs);
         if (is_gt(lhs, rhs)) {
+            TRACE("propagate_values", tout << "insert " << mk_pp(lhs, m) << " -> " << mk_pp(rhs, m) << "\n";);
             m_scoped_substitution.insert(lhs, rhs, pr);
             return;
         }
         if (is_gt(rhs, lhs)) {
+            TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";);
             m_scoped_substitution.insert(rhs, lhs, m.mk_symmetry(pr));
             return;
         }
+        TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";);
     }
     if (m.is_not(n, n1)) {
         m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr)); 
diff --git a/src/smt/asserted_formulas_new.h b/src/smt/asserted_formulas_new.h
index 60af46dea..82f18250d 100644
--- a/src/smt/asserted_formulas_new.h
+++ b/src/smt/asserted_formulas_new.h
@@ -29,9 +29,9 @@ Revision History:
 #include "ast/rewriter/pull_ite_tree.h"
 #include "ast/rewriter/push_app_ite.h"
 #include "ast/rewriter/inj_axiom.h"
-#include "ast/rewriter/bv_elim2.h"
+#include "ast/rewriter/bv_elim.h"
 #include "ast/rewriter/der.h"
-#include "ast/rewriter/elim_bounds2.h"
+#include "ast/rewriter/elim_bounds.h"
 #include "ast/macros/macro_manager.h"
 #include "ast/macros/macro_finder.h"
 #include "ast/normal_forms/defined_names.h"
@@ -210,7 +210,7 @@ class asserted_formulas_new {
     void apply_quasi_macros();
     void nnf_cnf();
     void reduce_and_solve();
-    void flush_cache() { m_rewriter.reset(); }
+    void flush_cache() { m_rewriter.reset(); m_rewriter.set_substitution(&m_substitution); }
     void set_eliminate_and(bool flag);
     void propagate_values();
     unsigned propagate_values(unsigned i);
diff --git a/src/smt/elim_term_ite.cpp b/src/smt/elim_term_ite.cpp
index d9cfac775..e8b70fc59 100644
--- a/src/smt/elim_term_ite.cpp
+++ b/src/smt/elim_term_ite.cpp
@@ -19,144 +19,6 @@ Revision History:
 #include "smt/elim_term_ite.h"
 #include "ast/ast_smt2_pp.h"
 
-void elim_term_ite::operator()(expr * n,                        
-                               expr_ref_vector & new_defs,        
-                               proof_ref_vector & new_def_proofs,
-                               expr_ref & r,                     
-                               proof_ref & pr) {
-
-    m_coarse_proofs.reset();
-    m_new_defs       = &new_defs;
-    m_new_def_proofs = &new_def_proofs;
-    reduce_core(n);
-    expr * r2;
-    proof * pr2;
-    get_cached(n, r2, pr2);
-    r = r2;
-    switch (m.proof_mode()) {
-    case PGM_DISABLED:
-        pr = m.mk_undef_proof();
-        break;
-    case PGM_COARSE:
-        remove_duplicates(m_coarse_proofs);
-        pr = n == r2 ? m.mk_oeq_reflexivity(n) : m.mk_apply_defs(n, r, m_coarse_proofs.size(), m_coarse_proofs.c_ptr());
-        break;
-    case PGM_FINE:
-        pr = pr2 == 0 ? m.mk_oeq_reflexivity(n) : pr2;
-        break;
-    }
-    m_coarse_proofs.reset();
-}
-
-void elim_term_ite::reduce_core(expr * n) {
-    m_todo.reset();
-    if (!is_cached(n)) {
-        m_todo.push_back(n);
-        while (!m_todo.empty()) {
-            expr * n = m_todo.back();
-            if (is_cached(n)) {
-                m_todo.pop_back();
-            }
-            else if (visit_children(n)) {
-                m_todo.pop_back();
-                reduce1(n);
-            }
-        }
-    }
-}
-
-bool elim_term_ite::visit_children(expr * n) {
-    bool visited = true;
-    unsigned j;
-    switch(n->get_kind()) {
-    case AST_VAR:
-        return true;
-    case AST_APP:
-        j = to_app(n)->get_num_args();
-        while (j > 0) {
-            --j;
-            visit(to_app(n)->get_arg(j), visited);
-        }
-        return visited;
-    case AST_QUANTIFIER: 
-        visit(to_quantifier(n)->get_expr(), visited);
-        return visited;
-    default:
-        UNREACHABLE();
-        return true;
-    }
-}
-
-void elim_term_ite::reduce1(expr * n) {
-    switch (n->get_kind()) {
-    case AST_VAR:
-        cache_result(n, n, 0);
-        break;
-    case AST_APP:
-        reduce1_app(to_app(n));
-        break;
-    case AST_QUANTIFIER:
-        reduce1_quantifier(to_quantifier(n));
-        break;
-    default:
-        UNREACHABLE();
-    }
-}
-
-void elim_term_ite::reduce1_app(app * n) {
-    m_args.reset();
-    
-    func_decl * decl = n->get_decl();
-    proof_ref p1(m);
-    get_args(n, m_args, p1);
-    if (!m.fine_grain_proofs())
-        p1 = 0;
-
-    expr_ref r(m);
-    r = m.mk_app(decl, m_args.size(), m_args.c_ptr());
-    if (m.is_term_ite(r)) {
-        expr_ref   new_def(m);
-        proof_ref  new_def_pr(m);
-        app_ref   new_r(m);
-        proof_ref  new_pr(m);
-        if (m_defined_names.mk_name(r, new_def, new_def_pr, new_r, new_pr)) {
-            CTRACE("elim_term_ite_bug", new_def.get() == 0, tout << mk_ismt2_pp(r, m) << "\n";);
-            SASSERT(new_def.get() != 0);
-            m_new_defs->push_back(new_def);
-            if (m.fine_grain_proofs()) {
-                m_new_def_proofs->push_back(new_def_pr);
-                new_pr = m.mk_transitivity(p1, new_pr);
-            }
-            else {
-                // [Leo] This looks fishy... why do we add 0 into m_coarse_proofs when fine_grain_proofs are disabled? 
-                new_pr = 0;
-                if (m.proofs_enabled())
-                    m_coarse_proofs.push_back(new_pr);
-            }
-        }
-        else {
-            SASSERT(new_def.get() == 0);
-            if (!m.fine_grain_proofs())
-                new_pr = 0;
-        }
-        cache_result(n, new_r, new_pr);
-    }
-    else {
-        cache_result(n, r, p1);
-    }
-}
-
-void elim_term_ite::reduce1_quantifier(quantifier * q) {
-    expr *  new_body;
-    proof * new_body_pr;
-    get_cached(q->get_expr(), new_body, new_body_pr);
-    
-    quantifier * new_q = m.update_quantifier(q, new_body);
-    proof *      p     = q == new_q ? 0 : m.mk_oeq_quant_intro(q, new_q, new_body_pr);   
-    cache_result(q, new_q, p);
-}
-
-
 br_status elim_term_ite_cfg::reduce_app(func_decl* f, unsigned n, expr * const* args, expr_ref& result, proof_ref& result_pr) {
     if (!m.is_term_ite(f)) {
         return BR_FAILED;
diff --git a/src/smt/elim_term_ite.h b/src/smt/elim_term_ite.h
index a57ea1dad..8e8340dc0 100644
--- a/src/smt/elim_term_ite.h
+++ b/src/smt/elim_term_ite.h
@@ -21,35 +21,8 @@ Revision History:
 
 #include "ast/normal_forms/defined_names.h"
 #include "ast/rewriter/rewriter.h"
-#include "ast/simplifier/simplifier.h"
 #include "ast/justified_expr.h"
 
-
-class elim_term_ite : public simplifier {
-    defined_names &    m_defined_names;
-    proof_ref_vector   m_coarse_proofs;
-    expr_ref_vector *  m_new_defs;
-    proof_ref_vector * m_new_def_proofs;
-    void reduce_core(expr * n);
-    bool visit_children(expr * n);
-    void reduce1(expr * n);
-    void reduce1_app(app * n);
-    void reduce1_quantifier(quantifier * q);
-public:
-    elim_term_ite(ast_manager & m, defined_names & d):simplifier(m), m_defined_names(d), m_coarse_proofs(m) { 
-        m_use_oeq = true; 
-        enable_ac_support(false);
-    }
-    virtual ~elim_term_ite() {}
-    void operator()(expr * n,                          // [IN]
-                    expr_ref_vector & new_defs,        // [OUT] new definitions
-                    proof_ref_vector & new_def_proofs, // [OUT] proofs of the new definitions 
-                    expr_ref & r,                      // [OUT] resultant expression
-                    proof_ref & pr                     // [OUT] proof for (~ n r)
-                    );
-};
-
-
 class elim_term_ite_cfg : public default_rewriter_cfg {
     ast_manager&           m;
     defined_names &        m_defined_names;
diff --git a/src/smt/expr_context_simplifier.h b/src/smt/expr_context_simplifier.h
index ed15044e7..b412d8646 100644
--- a/src/smt/expr_context_simplifier.h
+++ b/src/smt/expr_context_simplifier.h
@@ -21,10 +21,10 @@ Revision History:
 
 #include "ast/ast.h"
 #include "util/obj_hashtable.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
 #include "smt/params/smt_params.h"
 #include "smt/smt_kernel.h"
 #include "ast/arith_decl_plugin.h"
+#include "ast/rewriter/bool_rewriter.h"
 
 class expr_context_simplifier {
     typedef obj_map<expr, bool> context_map;
@@ -33,7 +33,7 @@ class expr_context_simplifier {
     arith_util   m_arith;
     context_map  m_context;
     expr_ref_vector m_trail;
-    basic_simplifier_plugin m_simp;
+    bool_rewriter m_simp;
     expr_mark m_mark;
     bool m_forward;
 public:
diff --git a/src/smt/params/CMakeLists.txt b/src/smt/params/CMakeLists.txt
index c965f0a62..500423dcc 100644
--- a/src/smt/params/CMakeLists.txt
+++ b/src/smt/params/CMakeLists.txt
@@ -13,6 +13,7 @@ z3_add_component(smt_params
     ast
     bit_blaster
     pattern
+    simplifier
   PYG_FILES
     smt_params_helper.pyg
 )
diff --git a/src/smt/smt_consequences.cpp b/src/smt/smt_consequences.cpp
index 65af3c0bd..0bf2a2939 100644
--- a/src/smt/smt_consequences.cpp
+++ b/src/smt/smt_consequences.cpp
@@ -16,12 +16,13 @@ Author:
 Revision History:
 
 --*/
-#include "smt/smt_context.h"
-#include "ast/ast_util.h"
-#include "ast/datatype_decl_plugin.h"
-#include "model/model_pp.h"
 #include "util/max_cliques.h"
 #include "util/stopwatch.h"
+#include "ast/ast_util.h"
+#include "ast/ast_pp.h"
+#include "ast/datatype_decl_plugin.h"
+#include "model/model_pp.h"
+#include "smt/smt_context.h"
 
 namespace smt {
 
diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp
index 6a2f848d9..22004ee78 100644
--- a/src/smt/smt_quantifier.cpp
+++ b/src/smt/smt_quantifier.cpp
@@ -16,6 +16,8 @@ Author:
 Revision History:
 
 --*/
+#include "ast/ast_pp.h"
+#include "ast/ast_smt2_pp.h"
 #include "smt/smt_quantifier.h"
 #include "smt/smt_context.h"
 #include "smt/smt_quantifier_stat.h"
@@ -24,7 +26,6 @@ Revision History:
 #include "smt/smt_quick_checker.h"
 #include "smt/mam.h"
 #include "smt/qi_queue.h"
-#include "ast/ast_smt2_pp.h"
 
 namespace smt {
 
diff --git a/src/smt/theory_array.cpp b/src/smt/theory_array.cpp
index 17eefb361..18fc9f50b 100644
--- a/src/smt/theory_array.cpp
+++ b/src/smt/theory_array.cpp
@@ -19,6 +19,7 @@ Revision History:
 #include "smt/smt_context.h"
 #include "smt/theory_array.h"
 #include "ast/ast_ll_pp.h"
+#include "ast/ast_pp.h"
 #include "util/stats.h"
 
 namespace smt {
diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp
index 124fea910..0c09d0403 100644
--- a/src/smt/theory_lra.cpp
+++ b/src/smt/theory_lra.cpp
@@ -28,6 +28,7 @@ Revision History:
 #include "util/optional.h"
 #include "util/lp/lp_params.hpp"
 #include "util/inf_rational.h"
+#include "ast/ast_pp.h"
 #include "smt/smt_theory.h"
 #include "smt/smt_context.h"
 #include "smt/theory_lra.h"
diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp
index c91882dad..8d6d70a79 100644
--- a/src/smt/theory_seq.cpp
+++ b/src/smt/theory_seq.cpp
@@ -19,11 +19,12 @@ Revision History:
 --*/
 
 #include <typeinfo>
+#include "ast/ast_pp.h"
+#include "ast/ast_trail.h"
 #include "smt/proto_model/value_factory.h"
 #include "smt/smt_context.h"
 #include "smt/smt_model_generator.h"
 #include "smt/theory_seq.h"
-#include "ast/ast_trail.h"
 #include "smt/theory_arith.h"
 #include "smt/smt_kernel.h"
 
diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h
index 5837980df..686fcdd57 100644
--- a/src/smt/theory_str.h
+++ b/src/smt/theory_str.h
@@ -17,13 +17,14 @@
 #ifndef _THEORY_STR_H_
 #define _THEORY_STR_H_
 
+#include "util/trail.h"
+#include "ast/ast_pp.h"
+#include "ast/arith_decl_plugin.h"
+#include "ast/rewriter/th_rewriter.h"
 #include "smt/smt_theory.h"
 #include "smt/params/theory_str_params.h"
-#include "util/trail.h"
-#include "ast/rewriter/th_rewriter.h"
 #include "smt/proto_model/value_factory.h"
 #include "smt/smt_model_generator.h"
-#include "ast/arith_decl_plugin.h"
 #include<set>
 #include<stack>
 #include<vector>
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index b395c09e6..fbcaec5ef 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -20,7 +20,6 @@ add_executable(test-z3
   bits.cpp
   bit_vector.cpp
   buffer.cpp
-  bv_simplifier_plugin.cpp
   chashtable.cpp
   check_assumptions.cpp
   cnf_backbones.cpp
diff --git a/src/test/bv_simplifier_plugin.cpp b/src/test/bv_simplifier_plugin.cpp
deleted file mode 100644
index 15ca9fac5..000000000
--- a/src/test/bv_simplifier_plugin.cpp
+++ /dev/null
@@ -1,326 +0,0 @@
-
-/*++
-Copyright (c) 2015 Microsoft Corporation
-
---*/
-
-#include "ast/simplifier/bv_simplifier_plugin.h"
-#include "ast/arith_decl_plugin.h"
-#include "ast/ast_pp.h"
-#include "ast/reg_decl_plugins.h"
-
-class tst_bv_simplifier_plugin_cls {
-    class mgr {
-    public:
-        mgr(ast_manager& m) {
-            reg_decl_plugins(m);
-        }
-    };
-    ast_manager m_manager;
-    mgr         m_mgr;
-    bv_simplifier_params m_bv_params;
-    basic_simplifier_plugin m_bsimp;
-    arith_util m_arith;
-    bv_simplifier_plugin m_simp;
-    bv_util     m_bv_util;
-    family_id   m_fid;
-
-    void get_num(expr* e, unsigned bv_size, rational& r) {
-        unsigned bv_size0;
-        if (!m_bv_util.is_numeral(e, r, bv_size0)) {
-            UNREACHABLE();
-        }
-        ENSURE(bv_size == bv_size0);
-    }
-
-    unsigned u32(expr* e) {
-        rational r;
-		std::cout << mk_pp(e,m_manager) << "\n";
-        get_num(e, 32, r);
-        return r.get_unsigned();
-    }
-
-    unsigned char u8(expr* e) {
-        rational r;
-        get_num(e, 8, r);
-        return static_cast<unsigned char>(r.get_unsigned());
-    }
-	int i32(expr* e) {
-        return static_cast<int>(u32(e));
-    }
-
-    uint64 u64(expr* e) {
-        rational r;
-        get_num(e, 64, r);
-        return r.get_uint64();
-    }
-
-    int64 i64(expr* e) {
-        rational r;
-        get_num(e, 64, r);
-        if (r >= power(rational(2), 63)) {
-            r -= power(rational(2), 64);
-        }
-        return r.get_int64();
-    }
-
-    bool ast2bool(expr* e) {
-        if (m_manager.is_true(e)) {
-            return true;
-        }
-        if (m_manager.is_false(e)) {
-            return false;
-        }
-        UNREACHABLE();
-        return false;        
-    }
-
-    bool bit2bool(expr* e) {
-        rational r;
-        get_num(e, 1, r);
-        return 0 != r.get_unsigned();
-    }
-
-    expr* mk_int(unsigned i) {
-        return m_arith.mk_numeral(rational(i), true);
-    }
-
-public:
-
-    tst_bv_simplifier_plugin_cls() : 
-        m_mgr(m_manager),
-        m_bsimp(m_manager),
-        m_arith(m_manager),
-        m_simp(m_manager, m_bsimp, m_bv_params), 
-        m_bv_util(m_manager), 
-        m_fid(0) {
-        m_fid = m_manager.mk_family_id("bv");
-    }
-
-    ~tst_bv_simplifier_plugin_cls() {}
-
-    void test_num(unsigned a) {
-        expr_ref e(m_manager), e1(m_manager);
-        app_ref ar(m_manager);
-        uint64 a64 = static_cast<uint64>(a);
-
-        e1 = m_bv_util.mk_numeral(rational(a), 32);
-        expr* const es[1] = { e1.get() };
-
-        ar = m_manager.mk_app(m_fid, OP_BNEG, e1.get());
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE((0-a) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BNOT, e1.get());
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE((~a) == u32(e.get()));
-
-        parameter params[2] = { parameter(32), parameter(32) };
-        ar = m_manager.mk_app(m_fid, OP_SIGN_EXT, 1, params, 1, es);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE(((int64)(int)a) == i64(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_ZERO_EXT, 1, params, 1, es);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE(((uint64)a) == u64(e.get()));
-
-        params[0] = parameter(7);
-        params[1] = parameter(0);
-        ar = m_manager.mk_app(m_fid, OP_EXTRACT, 2, params, 1, es);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE(((unsigned char)a) == u8(e.get()));
-
-        params[0] = parameter(2);
-        ar = m_manager.mk_app(m_fid, OP_REPEAT, 1, params, 1, es);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY(((a64 << 32) | a64) == u64(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BREDOR, e1.get());
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE((a != 0) == bit2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BREDAND, e1.get());
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE((a == 0xFFFFFFFF) == bit2bool(e.get()));
-
-        params[0] = parameter(8);
-
-        ar = m_manager.mk_app(m_fid, OP_ROTATE_LEFT, 1, params, 1, es);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE(((a << 8) | (a >> 24)) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_ROTATE_RIGHT, 1, params, 1, es);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE(((a >> 8) | (a << 24)) == u32(e.get()));
-
-        params[0] = parameter(m_manager.mk_sort(m_manager.mk_family_id("arith"), INT_SORT));
-        ar = m_manager.mk_app(m_fid, OP_BV2INT, 1, params, 1, es);
-		expr* es2[1] = { ar.get() };
-		params[0] = parameter(32);
-        ar = m_manager.mk_app(m_fid, OP_INT2BV, 1, params, 1, es2);
-		m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        ENSURE(a == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BIT0);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);        
-        VERIFY(!bit2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BIT1);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);        
-        VERIFY(bit2bool(e.get()));
-
-    }
-
-    void test_pair(unsigned a, unsigned b) {
-     
-        expr_ref e(m_manager), e1(m_manager), e2(m_manager);
-        app_ref ar(m_manager);
-        int sa = static_cast<int>(a);
-        int sb = static_cast<int>(b);
-        uint64 a64 = static_cast<uint64>(a);
-        uint64 b64 = static_cast<uint64>(b);
-        
-        e1 = m_bv_util.mk_numeral(rational(a), 32);
-        e2 = m_bv_util.mk_numeral(rational(b), 32);
-        expr* const e1e2[] = { e1.get(), e2.get() };
-        
-        
-        ar = m_manager.mk_app(m_fid, OP_BADD, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a + b) == u32(e.get()));
-        
-        ar = m_manager.mk_app(m_fid, OP_BSUB, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a - b) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BMUL, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a * b) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BAND, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a & b) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BOR, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a | b) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BNOR, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY(~(a | b) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BXOR, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a ^ b) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BXNOR, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((~(a ^ b)) == u32(e.get()));
-        
-        ar = m_manager.mk_app(m_fid, OP_BNAND, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((~(a & b)) == u32(e.get()));
-        
-        ar = m_manager.mk_app(m_fid, OP_ULEQ, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a <= b) == ast2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_UGEQ, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a >= b) == ast2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_ULT, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a < b) == ast2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_UGT, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a > b) == ast2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_SLEQ, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((sa <= sb) == ast2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_SGEQ, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((sa >= sb) == ast2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_SLT, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((sa < sb) == ast2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_SGT, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((sa > sb) == ast2bool(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BSHL, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY(((b>=32)?0:(a << b)) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BLSHR, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY(((b>=32)?0:(a >> b)) == u32(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BASHR, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-
-        std::cout << "compare: " << sa << " >> " << b << " = " << (sa >> b) << " with " << i32(e.get()) << "\n";
-        VERIFY(b >= 32 || ((sa >> b) == i32(e.get())));
-
-        if (b != 0) {
-            ar = m_manager.mk_app(m_fid, OP_BSDIV, 2, e1e2);
-            m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-            VERIFY((sa / sb) == i32(e.get()));
-
-            ar = m_manager.mk_app(m_fid, OP_BUDIV, 2, e1e2);
-            m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-            VERIFY((a / b) == u32(e.get()));
-
-            ar = m_manager.mk_app(m_fid, OP_BSREM, 2, e1e2);
-            m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-            //VERIFY((sa % sb) == i32(e.get()));
-
-            ar = m_manager.mk_app(m_fid, OP_BUREM, 2, e1e2);
-            m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-            VERIFY((a % b) == u32(e.get()));
-
-            // TBD: BSMOD.
-        }
-
-        ar = m_manager.mk_app(m_fid, OP_CONCAT, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY(((a64 << 32) | b64) == u64(e.get()));
-
-        ar = m_manager.mk_app(m_fid, OP_BCOMP, 2, e1e2);
-        m_simp.reduce(ar->get_decl(), ar->get_num_args(), ar->get_args(), e);
-        VERIFY((a == b) == bit2bool(e.get()));
-    }
-
-    void test() {
-        unsigned_vector nums;
-        nums.push_back(0);
-        nums.push_back(1);
-        nums.push_back(-1);
-        nums.push_back(2);
-        nums.push_back(31);
-        nums.push_back(32);
-        nums.push_back(33);
-        nums.push_back(435562);
-        nums.push_back(-43556211);
-        // TBD add some random numbers.
-
-
-        for (unsigned i = 0; i < nums.size(); ++i) {
-            test_num(nums[i]);
-            for (unsigned j = 0; j < nums.size(); ++j) {
-                test_pair(nums[i], nums[j]);
-            }
-        }
-    }
-};
-
-
-void tst_bv_simplifier_plugin() {
-    tst_bv_simplifier_plugin_cls tst_cls;
-    tst_cls.test();
-}
diff --git a/src/test/main.cpp b/src/test/main.cpp
index 17ac720dc..2c51df601 100644
--- a/src/test/main.cpp
+++ b/src/test/main.cpp
@@ -166,7 +166,6 @@ int main(int argc, char ** argv) {
     TST(timeout);
     TST(proof_checker);
     TST(simplifier);
-    TST(bv_simplifier_plugin);
     TST(bit_blaster);
     TST(var_subst);
     TST(simple_parser);
diff --git a/src/test/quant_elim.cpp b/src/test/quant_elim.cpp
index fec86d164..3674e6b70 100644
--- a/src/test/quant_elim.cpp
+++ b/src/test/quant_elim.cpp
@@ -6,12 +6,7 @@ Copyright (c) 2015 Microsoft Corporation
 
 #include "ast/ast.h"
 #include "smt/params/smt_params.h"
-#include "ast/simplifier/simplifier.h"
 #include "qe/qe.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
-#include "ast/simplifier/array_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/ast_pp.h"
 #include "parsers/smt/smtlib.h"
 #include "parsers/smt/smtparser.h"
@@ -38,7 +33,6 @@ static void test_qe(ast_manager& m, lbool expected_outcome, expr* fml, char cons
     // enable_trace("after_search");
     // enable_trace("bv_bit_prop");
 
-    simplifier simp(m);
     smt_params params;
     // params.m_quant_elim = true;
 
diff --git a/src/test/sorting_network.cpp b/src/test/sorting_network.cpp
index 909ea594e..2984e94e2 100644
--- a/src/test/sorting_network.cpp
+++ b/src/test/sorting_network.cpp
@@ -220,7 +220,7 @@ static void test_sorting_eq(unsigned n, unsigned k) {
         TRACE("pb",
               unsigned sz = solver.size();
               for (unsigned i = 0; i < sz; ++i) {
-                  tout << mk_pp(solver.get_formulas()[i], m) << "\n";
+                  tout << mk_pp(solver.get_formula(i), m) << "\n";
               });
         model_ref model;
         solver.get_model(model);
@@ -266,7 +266,7 @@ static void test_sorting_le(unsigned n, unsigned k) {
         TRACE("pb",
               unsigned sz = solver.size();
               for (unsigned i = 0; i < sz; ++i) {
-                  tout << mk_pp(solver.get_formulas()[i], m) << "\n";
+                  tout << mk_pp(solver.get_formula(i), m) << "\n";
               });
         model_ref model;
         solver.get_model(model);
@@ -314,7 +314,7 @@ void test_sorting_ge(unsigned n, unsigned k) {
         TRACE("pb",
               unsigned sz = solver.size();
               for (unsigned i = 0; i < sz; ++i) {
-                  tout << mk_pp(solver.get_formulas()[i], m) << "\n";
+                  tout << mk_pp(solver.get_formula(i), m) << "\n";
               });
         model_ref model;
         solver.get_model(model);

From 809a4efc6bd2f412027702994b897b96c81beb32 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 26 Aug 2017 11:24:19 -0700
Subject: [PATCH 31/74] removing dependencies on simplifier

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/elim_bounds.cpp | 203 +++++++++++++++++++++++++++++++
 src/ast/rewriter/elim_bounds.h   |  77 ++++++++++++
 2 files changed, 280 insertions(+)
 create mode 100644 src/ast/rewriter/elim_bounds.cpp
 create mode 100644 src/ast/rewriter/elim_bounds.h

diff --git a/src/ast/rewriter/elim_bounds.cpp b/src/ast/rewriter/elim_bounds.cpp
new file mode 100644
index 000000000..d3240e511
--- /dev/null
+++ b/src/ast/rewriter/elim_bounds.cpp
@@ -0,0 +1,203 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    elim_bounds.cpp
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-06-28.
+
+Revision History:
+
+--*/
+
+#ifndef ELIM_BOUNDS_H_
+#define ELIM_BOUNDS_H_
+
+#include "ast/used_vars.h"
+#include "util/obj_hashtable.h"
+#include "ast/rewriter/var_subst.h"
+#include "ast/rewriter/elim_bounds.h"
+#include "ast/ast_pp.h"
+
+elim_bounds_cfg::elim_bounds_cfg(ast_manager & m):
+    m(m),
+    m_util(m) {
+}
+
+/**
+   \brief Find bounds of the form
+
+   (<= x k)
+   (<= (+ x (* -1 y)) k)
+   (<= (+ x (* -1 t)) k)
+   (<= (+ t (* -1 x)) k)
+
+   x and y are a bound variables, t is a ground term and k is a numeral
+
+   It also detects >=, and the atom can be negated.
+*/
+bool elim_bounds_cfg::is_bound(expr * n, var * & lower, var * & upper) {
+    upper    = 0;
+    lower    = 0;
+    bool neg = false;
+    if (m.is_not(n)) {
+        n   = to_app(n)->get_arg(0);
+        neg = true;
+    }
+
+    expr* l = 0, *r = 0;
+    bool le  = false;
+    if (m_util.is_le(n, l, r) && m_util.is_numeral(r)) {
+        n  = l;
+        le = true;
+    }
+    else if (m_util.is_ge(n, l, r) && m_util.is_numeral(r)) {
+        n  = l;
+        le = false;
+    }
+    else {
+        return false;
+    }
+
+    if (neg)
+        le = !le;
+
+    if (is_var(n)) {
+        upper = to_var(n);
+    }
+    else if (m_util.is_add(n, l, r)) {
+        expr * arg1 = l;
+        expr * arg2 = r;
+        if (is_var(arg1))
+            upper   = to_var(arg1);
+        else if (!is_ground(arg1))
+            return false;
+        rational k;
+        bool is_int;
+        if (m_util.is_mul(arg2) && m_util.is_numeral(to_app(arg2)->get_arg(0), k, is_int) && k.is_minus_one()) {
+            arg2    = to_app(arg2)->get_arg(1);
+            if (is_var(arg2))
+                lower = to_var(arg2);
+            else if (!is_ground(arg2))
+                return false; // not supported
+        }
+        else {
+            return false; // not supported
+        }
+    }
+    else {
+        return false;
+    }
+
+    if (!le)
+        std::swap(upper, lower);
+
+    return true;
+}
+
+bool elim_bounds_cfg::is_bound(expr * n) {
+    var * lower, * upper;
+    return is_bound(n, lower, upper);
+}
+
+
+bool elim_bounds_cfg::reduce_quantifier(quantifier * q, 
+                                     expr * n, 
+                                     expr * const * new_patterns, 
+                                     expr * const * new_no_patterns,
+                                     expr_ref & result,
+                                     proof_ref & result_pr) {
+    if (!q->is_forall()) {
+        return false;
+    }
+    unsigned num_vars = q->get_num_decls();
+    ptr_buffer<expr> atoms;
+    if (m.is_or(n))
+        atoms.append(to_app(n)->get_num_args(), to_app(n)->get_args());
+    else
+        atoms.push_back(n);
+    used_vars          used_vars;
+    // collect non-candidates
+    for (expr * a : atoms) {
+        if (!is_bound(a))
+            used_vars.process(a);
+    }
+    if (used_vars.uses_all_vars(q->get_num_decls())) {
+        return false;
+    }
+    // collect candidates
+    obj_hashtable<var> lowers;
+    obj_hashtable<var> uppers;
+    obj_hashtable<var> candidate_set;
+    ptr_buffer<var>    candidates;
+#define ADD_CANDIDATE(V) if (!lowers.contains(V) && !uppers.contains(V)) { candidate_set.insert(V); candidates.push_back(V); }
+    for (expr * a : atoms) {
+        var * lower = 0;
+        var * upper = 0;
+        if (is_bound(a, lower, upper)) {
+            if (lower != 0 && !used_vars.contains(lower->get_idx()) && lower->get_idx() < num_vars) {
+                ADD_CANDIDATE(lower);
+                lowers.insert(lower);
+            }
+            if (upper != 0 && !used_vars.contains(upper->get_idx()) && upper->get_idx() < num_vars) {
+                ADD_CANDIDATE(upper);
+                uppers.insert(upper);
+            }
+        }
+    }
+    TRACE("elim_bounds", tout << "candidates:\n"; for (unsigned i = 0; i < candidates.size(); i++) tout << mk_pp(candidates[i], m) << "\n";);
+    // remove candidates that have lower and upper bounds
+
+    for (var * v : candidates) {
+        if (lowers.contains(v) && uppers.contains(v))
+            candidate_set.erase(v);
+    }
+    TRACE("elim_bounds", tout << "candidates after filter:\n"; for (unsigned i = 0; i < candidates.size(); i++) tout << mk_pp(candidates[i], m) << "\n";);
+    if (candidate_set.empty()) {
+        return false;
+    }
+    // remove bounds that contain variables in candidate_set
+    unsigned j = 0;
+    for (unsigned i = 0; i < atoms.size(); ++i) {
+        expr * a = atoms[i];
+        var * lower = 0;
+        var * upper = 0;
+        if (is_bound(a, lower, upper) && ((lower != 0 && candidate_set.contains(lower)) || (upper != 0 && candidate_set.contains(upper))))
+            continue;
+        atoms[j] = a;
+        j++;
+    }
+    if (j == atoms.size()) {
+        return false;
+    }
+    atoms.resize(j);
+    expr * new_body = 0;
+    switch (atoms.size()) {
+    case 0:
+        result = m.mk_false();
+        result_pr = m.mk_rewrite(q, result);
+        TRACE("elim_bounds", tout << mk_pp(q, m) << "\n" << result << "\n";);
+        return true;
+    case 1:
+        new_body = atoms[0];
+        break;
+    default:
+        new_body = m.mk_or(atoms.size(), atoms.c_ptr());
+        break;
+    }
+    quantifier_ref new_q(m);
+    new_q = m.update_quantifier(q, new_body);
+    elim_unused_vars(m, new_q, params_ref(), result);
+    result_pr = m.mk_rewrite(q, result);
+    TRACE("elim_bounds", tout << mk_pp(q, m) << "\n" << result << "\n";);
+    return true;
+}
+
+#endif /* ELIM_BOUNDS_H_ */
diff --git a/src/ast/rewriter/elim_bounds.h b/src/ast/rewriter/elim_bounds.h
new file mode 100644
index 000000000..e0bba4e60
--- /dev/null
+++ b/src/ast/rewriter/elim_bounds.h
@@ -0,0 +1,77 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    elim_bounds2.h
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-06-28.
+
+Revision History:
+
+--*/
+#ifndef ELIM_BOUNDS2_H_
+#define ELIM_BOUNDS2_H_
+
+#include "ast/ast.h"
+#include "ast/arith_decl_plugin.h"
+#include "ast/rewriter/rewriter.h"
+
+/**
+   \brief Functor for eliminating irrelevant bounds in quantified formulas.
+   
+   Example:
+   (forall (x Int) (y Int) (or (not (>= y x) (not (>= x 0)) (= (select a x) 1))))
+
+   The bound (>= y x) is irrelevant and can be eliminated.
+
+   This can be easily proved by using Fourier-Motzkin elimination.
+
+   Limitations & Assumptions:
+   - It assumes the input formula was already simplified.
+   - It can only handle bounds in the diff-logic fragment.
+
+   \remark This operation is subsumed by Fourier-Motzkin elimination.
+*/
+class elim_bounds_cfg : public default_rewriter_cfg {
+    ast_manager &      m;
+    arith_util         m_util;
+    bool is_bound(expr * n, var * & lower, var * & upper);
+    bool is_bound(expr * n);
+public:
+    elim_bounds_cfg(ast_manager & m);
+    
+    bool reduce_quantifier(quantifier * old_q, 
+                           expr * new_body, 
+                           expr * const * new_patterns, 
+                           expr * const * new_no_patterns,
+                           expr_ref & result,
+                           proof_ref & result_pr);
+};
+
+/**
+   \brief Functor for applying elim_bounds2 in all
+   universal quantifiers in an expression.
+
+   Assumption: the formula was already skolemized.
+*/
+class elim_bounds_rw : public rewriter_tpl<elim_bounds_cfg> {
+protected:
+    elim_bounds_cfg  m_cfg;
+public:
+    elim_bounds_rw(ast_manager & m):
+        rewriter_tpl<elim_bounds_cfg>(m, m.proofs_enabled(), m_cfg),
+        m_cfg(m)
+    {} 
+
+    virtual ~elim_bounds_rw() {}
+};
+
+#endif /* ELIM_BOUNDS2_H_ */
+

From 2ede4b2c805a97ddd63f0205d4ba5c2940295a5c Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 27 Aug 2017 09:31:16 -0700
Subject: [PATCH 32/74] fixes based on regression tests

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/arith_decl_plugin.cpp                 | 36 ++++----
 src/ast/arith_decl_plugin.h                   | 34 +++----
 src/ast/ast.cpp                               |  1 +
 src/ast/macros/macro_manager.cpp              | 11 ++-
 src/ast/rewriter/arith_rewriter.cpp           |  9 +-
 src/ast/rewriter/poly_rewriter_def.h          | 23 ++---
 src/ast/rewriter/push_app_ite.cpp             |  1 +
 src/ast/rewriter/push_app_ite.h               |  1 +
 src/ast/rewriter/rewriter_def.h               |  3 +-
 src/ast/rewriter/rewriter_params.pyg          |  1 +
 src/ast/rewriter/th_rewriter.cpp              |  4 +-
 src/qe/nlqsat.cpp                             | 12 +--
 src/smt/CMakeLists.txt                        |  2 +-
 ...formulas_new.cpp => asserted_formulas.cpp} | 88 +++++++++----------
 ...ted_formulas_new.h => asserted_formulas.h} | 40 ++++-----
 src/smt/smt_context.cpp                       |  4 +-
 src/smt/smt_context.h                         |  4 +-
 src/smt/theory_arith_core.h                   | 17 ++--
 src/smt/theory_array_full.cpp                 |  8 +-
 src/smt/theory_lra.cpp                        |  3 -
 src/tactic/arith/purify_arith_tactic.cpp      |  8 +-
 21 files changed, 159 insertions(+), 151 deletions(-)
 rename src/smt/{asserted_formulas_new.cpp => asserted_formulas.cpp} (86%)
 rename src/smt/{asserted_formulas_new.h => asserted_formulas.h} (88%)

diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp
index 01b671c99..0719ebdfa 100644
--- a/src/ast/arith_decl_plugin.cpp
+++ b/src/ast/arith_decl_plugin.cpp
@@ -178,7 +178,7 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) {
     MK_AC_OP(m_i_mul_decl, "*", OP_MUL, i);
     MK_LEFT_ASSOC_OP(m_i_div_decl, "div", OP_IDIV, i);
     MK_OP(m_i_rem_decl, "rem", OP_REM, i);
-    MK_OP(m_i_mod_decl, "mod", OP_MOD, i);
+    //MK_OP(m_i_mod_decl, "mod", OP_MOD, i);
     MK_UNARY(m_i_uminus_decl, "-", OP_UMINUS, i);
 
     m_to_real_decl = m->mk_func_decl(symbol("to_real"), i, r, func_decl_info(id, OP_TO_REAL));
@@ -215,18 +215,18 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) {
     m_e = m->mk_const(e_decl);
     m->inc_ref(m_e);
 
-    func_decl * z_pw_z_int = m->mk_const_decl(symbol("0^0-int"), i, func_decl_info(id, OP_0_PW_0_INT));
-    m_0_pw_0_int = m->mk_const(z_pw_z_int);
-    m->inc_ref(m_0_pw_0_int);
+    //func_decl * z_pw_z_int = m->mk_const_decl(symbol("0^0-int"), i, func_decl_info(id, OP_0_PW_0_INT));
+    //m_0_pw_0_int = m->mk_const(z_pw_z_int);
+    //m->inc_ref(m_0_pw_0_int);
 
-    func_decl * z_pw_z_real = m->mk_const_decl(symbol("0^0-real"), r, func_decl_info(id, OP_0_PW_0_REAL));
-    m_0_pw_0_real = m->mk_const(z_pw_z_real);
-    m->inc_ref(m_0_pw_0_real);
+    //func_decl * z_pw_z_real = m->mk_const_decl(symbol("0^0-real"), r, func_decl_info(id, OP_0_PW_0_REAL));
+    //m_0_pw_0_real = m->mk_const(z_pw_z_real);
+    //m->inc_ref(m_0_pw_0_real);
 
     MK_OP(m_neg_root_decl, "neg-root", OP_NEG_ROOT, r);
-    MK_UNARY(m_div_0_decl, "/0", OP_DIV_0, r);
-    MK_UNARY(m_idiv_0_decl, "div0", OP_IDIV_0, i);
-    MK_UNARY(m_mod_0_decl, "mod0", OP_MOD_0, i);
+    //MK_UNARY(m_div_0_decl, "/0", OP_DIV_0, r);
+    //MK_UNARY(m_idiv_0_decl, "div0", OP_IDIV_0, i);
+    //MK_UNARY(m_mod_0_decl, "mod0", OP_MOD_0, i);
     MK_UNARY(m_u_asin_decl, "asin-u", OP_U_ASIN, r);
     MK_UNARY(m_u_acos_decl, "acos-u", OP_U_ACOS, r);
 }
@@ -392,12 +392,12 @@ inline func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, bool is_real) {
     case OP_ATANH:     return m_atanh_decl;
     case OP_PI:        return m_pi->get_decl();
     case OP_E:         return m_e->get_decl();
-    case OP_0_PW_0_INT:  return m_0_pw_0_int->get_decl();
-    case OP_0_PW_0_REAL: return m_0_pw_0_real->get_decl();
+    //case OP_0_PW_0_INT:  return m_0_pw_0_int->get_decl();
+    //case OP_0_PW_0_REAL: return m_0_pw_0_real->get_decl();
     case OP_NEG_ROOT:    return m_neg_root_decl;
-    case OP_DIV_0:       return m_div_0_decl;
-    case OP_IDIV_0:      return m_idiv_0_decl;
-    case OP_MOD_0:       return m_mod_0_decl;
+    //case OP_DIV_0:       return m_div_0_decl;
+    //case OP_IDIV_0:      return m_idiv_0_decl;
+    //case OP_MOD_0:       return m_mod_0_decl;
     case OP_U_ASIN:      return m_u_asin_decl;
     case OP_U_ACOS:      return m_u_acos_decl;
     default: return 0;
@@ -489,9 +489,9 @@ static bool has_real_arg(ast_manager * m, unsigned num_args, expr * const * args
 static bool is_const_op(decl_kind k) {
     return
         k == OP_PI ||
-        k == OP_E  ||
-        k == OP_0_PW_0_INT ||
-        k == OP_0_PW_0_REAL;
+        k == OP_E;
+        //k == OP_0_PW_0_INT ||
+        //k == OP_0_PW_0_REAL;
 }
 
 func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h
index 6c2b1c77a..0238df3ae 100644
--- a/src/ast/arith_decl_plugin.h
+++ b/src/ast/arith_decl_plugin.h
@@ -70,12 +70,12 @@ enum arith_op_kind {
     OP_PI,
     OP_E,
     // under-specified symbols
-    OP_0_PW_0_INT,    // 0^0 for integers
-    OP_0_PW_0_REAL,   // 0^0 for reals
+    //OP_0_PW_0_INT,    // 0^0 for integers
+    //OP_0_PW_0_REAL,   // 0^0 for reals
     OP_NEG_ROOT,      // x^n when n is even and x is negative
-    OP_DIV_0,         // x/0
-    OP_IDIV_0,        // x div 0
-    OP_MOD_0,         // x mod 0
+    // OP_DIV_0,         // x/0
+    // OP_IDIV_0,        // x div 0
+    // OP_MOD_0,         // x mod 0
     OP_U_ASIN,        // asin(x) for x < -1 or x > 1
     OP_U_ACOS,        // acos(x) for x < -1 or x > 1
     LAST_ARITH_OP
@@ -218,12 +218,12 @@ public:
             return false;
         switch (f->get_decl_kind())
         {
-        case OP_0_PW_0_INT:
-        case OP_0_PW_0_REAL:
+        //case OP_0_PW_0_INT:
+        //case OP_0_PW_0_REAL:
         case OP_NEG_ROOT:
-        case OP_DIV_0:
-        case OP_IDIV_0:
-        case OP_MOD_0:
+        //case OP_DIV_0:
+        //case OP_IDIV_0:
+        //case OP_MOD_0:
         case OP_U_ASIN:
         case OP_U_ACOS:
             return true;
@@ -276,9 +276,9 @@ public:
     bool is_uminus(expr const * n) const { return is_app_of(n, m_afid, OP_UMINUS); }
     bool is_mul(expr const * n) const { return is_app_of(n, m_afid, OP_MUL); }
     bool is_div(expr const * n) const { return is_app_of(n, m_afid, OP_DIV); }
-    bool is_div0(expr const * n) const { return is_app_of(n, m_afid, OP_DIV_0); }
+    //bool is_div0(expr const * n) const { return is_app_of(n, m_afid, OP_DIV_0); }
     bool is_idiv(expr const * n) const { return is_app_of(n, m_afid, OP_IDIV); }
-    bool is_idiv0(expr const * n) const { return is_app_of(n, m_afid, OP_IDIV_0); }
+    //bool is_idiv0(expr const * n) const { return is_app_of(n, m_afid, OP_IDIV_0); }
     bool is_mod(expr const * n) const { return is_app_of(n, m_afid, OP_MOD); }
     bool is_rem(expr const * n) const { return is_app_of(n, m_afid, OP_REM); }
     bool is_to_real(expr const * n) const { return is_app_of(n, m_afid, OP_TO_REAL); }
@@ -425,11 +425,11 @@ public:
     app * mk_pi() { return plugin().mk_pi(); }
     app * mk_e()  { return plugin().mk_e(); }
 
-    app * mk_0_pw_0_int() { return plugin().mk_0_pw_0_int(); }
-    app * mk_0_pw_0_real() { return plugin().mk_0_pw_0_real(); }
-    app * mk_div0(expr * arg) { return m_manager.mk_app(m_afid, OP_DIV_0, arg); }
-    app * mk_idiv0(expr * arg) { return m_manager.mk_app(m_afid, OP_IDIV_0, arg); }
-    app * mk_mod0(expr * arg) { return m_manager.mk_app(m_afid, OP_MOD_0, arg); }
+ //   app * mk_0_pw_0_int() { return plugin().mk_0_pw_0_int(); }
+ //   app * mk_0_pw_0_real() { return plugin().mk_0_pw_0_real(); }
+ //   app * mk_div0(expr * arg) { return m_manager.mk_app(m_afid, OP_DIV_0, arg); }
+ //   app * mk_idiv0(expr * arg) { return m_manager.mk_app(m_afid, OP_IDIV_0, arg); }
+ //   app * mk_mod0(expr * arg) { return m_manager.mk_app(m_afid, OP_MOD_0, arg); }
     app * mk_neg_root(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_NEG_ROOT, arg1, arg2); }
     app * mk_u_asin(expr * arg) { return m_manager.mk_app(m_afid, OP_U_ASIN, arg); }
     app * mk_u_acos(expr * arg) { return m_manager.mk_app(m_afid, OP_U_ACOS, arg); }
diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp
index f347a8e49..029e2d90c 100644
--- a/src/ast/ast.cpp
+++ b/src/ast/ast.cpp
@@ -2355,6 +2355,7 @@ quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort *
     SASSERT(num_decls > 0);
     DEBUG_CODE({
             for (unsigned i = 0; i < num_patterns; ++i) {
+                TRACE("ast", tout << i << " " << mk_pp(patterns[i], *this) << "\n";);
                 SASSERT(is_pattern(patterns[i]));
             }});
     unsigned sz               = quantifier::get_obj_size(num_decls, num_patterns, num_no_patterns);
diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp
index b6ee62322..855cae107 100644
--- a/src/ast/macros/macro_manager.cpp
+++ b/src/ast/macros/macro_manager.cpp
@@ -213,11 +213,13 @@ func_decl * macro_manager::get_macro_interpretation(unsigned i, expr_ref & inter
 struct macro_manager::macro_expander_cfg : public default_rewriter_cfg {
     ast_manager& m; 
     macro_manager& mm;
+    expr_dependency_ref m_used_macro_dependencies; 
     expr_ref_vector m_trail;
 
     macro_expander_cfg(ast_manager& m, macro_manager& mm):
         m(m),
         mm(mm),
+        m_used_macro_dependencies(m),
         m_trail(m)
     {}
 
@@ -297,8 +299,10 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg {
                 p = m.mk_unit_resolution(2, prs);
             }
             else {
-                p = 0;
+                p = 0; 
             }
+            expr_dependency * ed = mm.m_decl2macro_dep.find(d); 
+            m_used_macro_dependencies = m.mk_join(m_used_macro_dependencies, ed); 
             return true;
         }
         return false;
@@ -307,6 +311,7 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg {
 
 struct macro_manager::macro_expander_rw : public rewriter_tpl<macro_manager::macro_expander_cfg> {
     macro_expander_cfg m_cfg;
+
     macro_expander_rw(ast_manager& m, macro_manager& mm):
         rewriter_tpl<macro_manager::macro_expander_cfg>(m, m.proofs_enabled(), m_cfg),
         m_cfg(m, mm) 
@@ -319,8 +324,10 @@ void macro_manager::expand_macros(expr * n, proof * pr, expr_dependency * dep, e
         // Expand macros with "real" proof production support (NO rewrite*)
         expr_ref old_n(m);
         proof_ref old_pr(m);
+        expr_dependency_ref old_dep(m);
         old_n  = n;
         old_pr = pr;
+        old_dep = dep;
         bool change = false;
         for (;;) {
             macro_expander_rw proc(m, *this);
@@ -328,10 +335,12 @@ void macro_manager::expand_macros(expr * n, proof * pr, expr_dependency * dep, e
             TRACE("macro_manager_bug", tout << "expand_macros:\n" << mk_pp(n, m) << "\n";);
             proc(old_n, r, n_eq_r_pr);
             new_pr = m.mk_modus_ponens(old_pr, n_eq_r_pr);
+            new_dep = m.mk_join(old_dep, proc.m_cfg.m_used_macro_dependencies); 
             if (r.get() == old_n.get())
                 break;
             old_n  = r;
             old_pr = new_pr;
+            old_dep = new_dep;
             change = true;
         }
         // apply th_rewrite to the result.
diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp
index 4b00cde45..1f2e9e2e9 100644
--- a/src/ast/rewriter/arith_rewriter.cpp
+++ b/src/ast/rewriter/arith_rewriter.cpp
@@ -680,8 +680,9 @@ br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & resul
     if (m_util.is_numeral(arg2, v2, is_int)) {
         SASSERT(!is_int);
         if (v2.is_zero()) {
-            result = m_util.mk_div0(arg1);
-            return BR_REWRITE1;
+            return BR_FAILED;
+            //result = m_util.mk_div0(arg1);
+            //return BR_REWRITE1;
         }
         else if (m_util.is_numeral(arg1, v1, is_int)) {
             result = m_util.mk_numeral(v1/v2, false);
@@ -735,8 +736,8 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu
         return BR_DONE;
     }
     if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) {
-        result = m_util.mk_idiv0(arg1);
-        return BR_REWRITE1;
+        //result = m_util.mk_idiv0(arg1);
+        //return BR_REWRITE1;
     }
     return BR_FAILED;
 }
diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h
index 2a6bd2c50..85044af08 100644
--- a/src/ast/rewriter/poly_rewriter_def.h
+++ b/src/ast/rewriter/poly_rewriter_def.h
@@ -508,22 +508,22 @@ br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * con
     expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once
     bool     has_multiple = false;
     expr *   prev = 0;
-    bool     ordered      = true;
+    bool     ordered  = true;
     for (unsigned i = 0; i < num_args; i++) {
         expr * arg = args[i];
+
         if (is_numeral(arg, a)) {
             num_coeffs++;
             c += a;
+            ordered = !m_sort_sums || i == 0;
         }
-        else {
-            // arg is not a numeral
-            if (m_sort_sums && ordered) {
-                if (prev != 0 && lt(arg, prev))
-                    ordered = false;
-                prev = arg;
-            }
+        else if (m_sort_sums && ordered) {
+            if (prev != 0 && lt(arg, prev)) 
+                ordered = false;
+            prev = arg;        
         }
 
+
         arg = get_power_product(arg);
         if (visited.is_marked(arg)) {
             multiple.mark(arg);
@@ -535,8 +535,8 @@ br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * con
     }
     normalize(c);
     SASSERT(m_sort_sums || ordered);
-    TRACE("sort_sums", 
-          tout << "ordered: " << ordered << "\n";
+    TRACE("rewriter", 
+          tout << "ordered: " << ordered << " sort sums: " << m_sort_sums << "\n";
           for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";);
 
     if (has_multiple) {
@@ -589,13 +589,14 @@ br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * con
             hoist_cmul(new_args);
         }
         else if (m_sort_sums) {
-            TRACE("sort_sums_bug", tout << "new_args.size(): " << new_args.size() << "\n";);
+            TRACE("rewriter_bug", tout << "new_args.size(): " << new_args.size() << "\n";);
             if (c.is_zero())
                 std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt());
             else
                 std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt());
         }
         result = mk_add_app(new_args.size(), new_args.c_ptr());
+        TRACE("rewriter", tout << result << "\n";);
         if (hoist_multiplication(result)) {
             return BR_REWRITE_FULL;
         }
diff --git a/src/ast/rewriter/push_app_ite.cpp b/src/ast/rewriter/push_app_ite.cpp
index 386a4fb27..f3df4d711 100644
--- a/src/ast/rewriter/push_app_ite.cpp
+++ b/src/ast/rewriter/push_app_ite.cpp
@@ -73,6 +73,7 @@ br_status push_app_ite_cfg::reduce_app(func_decl * f, unsigned num, expr * const
     expr_ref e_new(m.mk_app(f, num, args_prime), m);
     args_prime[ite_arg_idx] = old;
     result = m.mk_ite(c, t_new, e_new);
+    TRACE("push_app_ite", tout << result << "\n";);
     if (m.proofs_enabled()) {
         result_pr = m.mk_rewrite(m.mk_app(f, num, args), result);
     }
diff --git a/src/ast/rewriter/push_app_ite.h b/src/ast/rewriter/push_app_ite.h
index e6c6b6fb2..ae06aad30 100644
--- a/src/ast/rewriter/push_app_ite.h
+++ b/src/ast/rewriter/push_app_ite.h
@@ -34,6 +34,7 @@ struct push_app_ite_cfg : public default_rewriter_cfg {
     virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
     br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr);
     push_app_ite_cfg(ast_manager& m, bool conservative = true): m(m), m_conservative(conservative) {}
+    bool rewrite_patterns() const { return false; }
 };
 
 /**
diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h
index 163118d17..c511f00d0 100644
--- a/src/ast/rewriter/rewriter_def.h
+++ b/src/ast/rewriter/rewriter_def.h
@@ -496,6 +496,7 @@ void rewriter_tpl<Config>::process_quantifier(quantifier * q, frame & fr) {
     expr * const * new_pats;
     expr * const * new_no_pats;
     if (rewrite_patterns()) {
+        TRACE("reduce_quantifier_bug", tout << "rewrite patterns\n";);
         new_pats    = it + 1;
         new_no_pats = new_pats + q->get_num_patterns();
     }
@@ -518,7 +519,7 @@ void rewriter_tpl<Config>::process_quantifier(quantifier * q, frame & fr) {
     }
     else {
         expr_ref tmp(m());
-
+        TRACE("reduce_quantifier_bug", tout << mk_ismt2_pp(q, m()) << " " << mk_ismt2_pp(new_body, m()) << "\n";);
         if (!m_cfg.reduce_quantifier(q, new_body, new_pats, new_no_pats, m_r, m_pr)) {
             if (fr.m_new_child) {
                 m_r = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body);
diff --git a/src/ast/rewriter/rewriter_params.pyg b/src/ast/rewriter/rewriter_params.pyg
index 06500086a..18bb29e56 100644
--- a/src/ast/rewriter/rewriter_params.pyg
+++ b/src/ast/rewriter/rewriter_params.pyg
@@ -9,5 +9,6 @@ def_module_params('rewriter',
                           ("pull_cheap_ite", BOOL, False, "pull if-then-else terms when cheap."),
                           ("bv_ineq_consistency_test_max", UINT, 0, "max size of conjunctions on which to perform consistency test based on inequalities on bitvectors."),
                           ("cache_all", BOOL, False, "cache all intermediate results."),
+                          ("rewrite_patterns", BOOL, False, "rewrite patterns."),
                           ("ignore_patterns_on_ground_qbody", BOOL, True, "ignores patterns on quantifiers that don't mention their bound variables.")))
 
diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp
index 764fa8eef..a2ca12b24 100644
--- a/src/ast/rewriter/th_rewriter.cpp
+++ b/src/ast/rewriter/th_rewriter.cpp
@@ -55,6 +55,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
     bool                m_push_ite_arith;
     bool                m_push_ite_bv;
     bool                m_ignore_patterns_on_ground_qbody;
+    bool                m_rewrite_patterns;
 
     // substitution support
     expr_dependency_ref m_used_dependencies; // set of dependencies of used substitutions
@@ -72,6 +73,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
         m_push_ite_arith = p.push_ite_arith();
         m_push_ite_bv    = p.push_ite_bv();
         m_ignore_patterns_on_ground_qbody = p.ignore_patterns_on_ground_qbody();
+        m_rewrite_patterns = p.rewrite_patterns();
     }
 
     void updt_params(params_ref const & p) {
@@ -99,7 +101,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
         return false;
     }
 
-    bool rewrite_patterns() const { return false; }
+    bool rewrite_patterns() const { return m_rewrite_patterns; }
 
     bool cache_all_results() const { return m_cache_all; }
 
diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp
index 0f2437982..1f9c89f89 100644
--- a/src/qe/nlqsat.cpp
+++ b/src/qe/nlqsat.cpp
@@ -444,16 +444,12 @@ namespace qe {
             div_rewriter_cfg(nlqsat& s): m(s.m), a(s.m), m_zero(a.mk_real(0), m) {}
             ~div_rewriter_cfg() {}
             br_status reduce_app(func_decl* f, unsigned sz, expr* const* args, expr_ref& result, proof_ref& pr) {
-                if (is_decl_of(f, a.get_family_id(), OP_DIV) && sz == 2 && !a.is_numeral(args[1])) {                    
+                rational r(1);
+                if (is_decl_of(f, a.get_family_id(), OP_DIV) && sz == 2 && (!a.is_numeral(args[1], r) || r.is_zero())) {                    
                     result = m.mk_fresh_const("div", a.mk_real());
                     m_divs.push_back(div(m, args[0], args[1], to_app(result)));
                     return BR_DONE;
                 }
-                if (is_decl_of(f, a.get_family_id(), OP_DIV_0) && sz == 1 && !a.is_numeral(args[0])) {                    
-                    result = m.mk_fresh_const("div", a.mk_real());
-                    m_divs.push_back(div(m, args[0], m_zero, to_app(result)));
-                    return BR_DONE;
-                }
                 return BR_FAILED;
             }
             vector<div> const& divs() const { return m_divs; }
@@ -507,10 +503,6 @@ namespace qe {
                     m_has_divs = true;
                     return;
                 }
-                if (a.is_div0(n) && s.m_mode == qsat_t) {
-                    m_has_divs = true;
-                    return;
-                }
                 TRACE("qe", tout << "not NRA: " << mk_pp(n, s.m) << "\n";);
                 throw tactic_exception("not NRA");
             }
diff --git a/src/smt/CMakeLists.txt b/src/smt/CMakeLists.txt
index 385d1aaa0..41890dd05 100644
--- a/src/smt/CMakeLists.txt
+++ b/src/smt/CMakeLists.txt
@@ -2,7 +2,7 @@ z3_add_component(smt
   SOURCES
     arith_eq_adapter.cpp
     arith_eq_solver.cpp
-    asserted_formulas_new.cpp
+    asserted_formulas.cpp
     cached_var_subst.cpp
     cost_evaluator.cpp
     dyn_ack.cpp
diff --git a/src/smt/asserted_formulas_new.cpp b/src/smt/asserted_formulas.cpp
similarity index 86%
rename from src/smt/asserted_formulas_new.cpp
rename to src/smt/asserted_formulas.cpp
index 8d16ef2cd..626588ab3 100644
--- a/src/smt/asserted_formulas_new.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -3,7 +3,7 @@ Copyright (c) 2006 Microsoft Corporation
 
 Module Name:
 
-    asserted_formulas_new.cpp
+    asserted_formulas.cpp
 
 Abstract:
 
@@ -25,9 +25,9 @@ Revision History:
 #include "ast/normal_forms/nnf.h"
 #include "ast/pattern/pattern_inference.h"
 #include "ast/macros/quasi_macros.h"
-#include "smt/asserted_formulas_new.h"
+#include "smt/asserted_formulas.h"
 
-asserted_formulas_new::asserted_formulas_new(ast_manager & m, smt_params & p):
+asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p):
     m(m),
     m_params(p),
     m_rewriter(m),
@@ -60,13 +60,11 @@ asserted_formulas_new::asserted_formulas_new(ast_manager & m, smt_params & p):
 
     m_macro_finder = alloc(macro_finder, m, m_macro_manager);
 
-    params_ref pa;
-    pa.set_bool("arith_lhs", true);
-    m_rewriter.updt_params(pa);
+    set_eliminate_and(false);
 
 }
 
-void asserted_formulas_new::setup() {
+void asserted_formulas::setup() {
     switch (m_params.m_lift_ite) {
     case LI_FULL:
         m_params.m_ng_lift_ite = LI_NONE; 
@@ -84,10 +82,10 @@ void asserted_formulas_new::setup() {
 }
 
 
-asserted_formulas_new::~asserted_formulas_new() {
+asserted_formulas::~asserted_formulas() {
 }
 
-void asserted_formulas_new::push_assertion(expr * e, proof * pr, vector<justified_expr>& result) {
+void asserted_formulas::push_assertion(expr * e, proof * pr, vector<justified_expr>& result) {
     if (inconsistent()) {
         return;
     }
@@ -119,16 +117,18 @@ void asserted_formulas_new::push_assertion(expr * e, proof * pr, vector<justifie
     }
 }
 
-void asserted_formulas_new::set_eliminate_and(bool flag) {
+void asserted_formulas::set_eliminate_and(bool flag) {
     params_ref p;
-    p.set_bool("elim_and", true);
+    p.set_bool("elim_and", flag);
     p.set_bool("arith_lhs", true);
+    p.set_bool("sort_sums", true);
+    p.set_bool("rewrite_patterns", true);
     m_rewriter.updt_params(p);
     flush_cache();
 }
 
 
-void asserted_formulas_new::assert_expr(expr * e, proof * _in_pr) {
+void asserted_formulas::assert_expr(expr * e, proof * _in_pr) {
     proof_ref  in_pr(_in_pr, m), pr(_in_pr, m);
     expr_ref   r(e, m);
 
@@ -153,17 +153,17 @@ void asserted_formulas_new::assert_expr(expr * e, proof * _in_pr) {
     TRACE("asserted_formulas_bug", tout << "after assert_expr\n"; display(tout););
 }
 
-void asserted_formulas_new::assert_expr(expr * e) {
+void asserted_formulas::assert_expr(expr * e) {
     assert_expr(e, m.mk_asserted(e));
 }
 
-void asserted_formulas_new::get_assertions(ptr_vector<expr> & result) const {
+void asserted_formulas::get_assertions(ptr_vector<expr> & result) const {
     for (justified_expr const& je : m_formulas) result.push_back(je.get_fml());
 }
 
-void asserted_formulas_new::push_scope() {
+void asserted_formulas::push_scope() {
     SASSERT(inconsistent() || m_qhead == m_formulas.size() || m.canceled());
-    TRACE("asserted_formulas_new_scopes", tout << "push:\n"; display(tout););    
+    TRACE("asserted_formulas_scopes", tout << "push:\n"; display(tout););    
     m_scoped_substitution.push();
     m_scopes.push_back(scope());
     scope & s = m_scopes.back();
@@ -176,8 +176,8 @@ void asserted_formulas_new::push_scope() {
     commit();
 }
  
-void asserted_formulas_new::pop_scope(unsigned num_scopes) {
-    TRACE("asserted_formulas_new_scopes", tout << "before pop " << num_scopes << "\n"; display(tout););
+void asserted_formulas::pop_scope(unsigned num_scopes) {
+    TRACE("asserted_formulas_scopes", tout << "before pop " << num_scopes << "\n"; display(tout););
     m_bv_sharing.pop_scope(num_scopes);
     m_macro_manager.pop_scope(num_scopes);
     unsigned new_lvl    = m_scopes.size() - num_scopes;
@@ -189,10 +189,10 @@ void asserted_formulas_new::pop_scope(unsigned num_scopes) {
     m_qhead    = s.m_formulas_lim;
     m_scopes.shrink(new_lvl);
     flush_cache();
-    TRACE("asserted_formulas_new_scopes", tout << "after pop " << num_scopes << "\n"; display(tout););
+    TRACE("asserted_formulas_scopes", tout << "after pop " << num_scopes << "\n"; display(tout););
 }
 
-void asserted_formulas_new::reset() {
+void asserted_formulas::reset() {
     m_defined_names.reset();
     m_qhead = 0;
     m_formulas.reset();
@@ -202,14 +202,14 @@ void asserted_formulas_new::reset() {
     m_inconsistent = false;
 }
 
-bool asserted_formulas_new::check_well_sorted() const {
+bool asserted_formulas::check_well_sorted() const {
     for (justified_expr const& je : m_formulas) {
         if (!is_well_sorted(m, je.get_fml())) return false; 
     }
     return true;
 }
 
-void asserted_formulas_new::reduce() {
+void asserted_formulas::reduce() {
     if (inconsistent())
         return;
     if (canceled())
@@ -255,7 +255,7 @@ void asserted_formulas_new::reduce() {
 }
 
 
-unsigned asserted_formulas_new::get_formulas_last_level() const {
+unsigned asserted_formulas::get_formulas_last_level() const {
     if (m_scopes.empty()) {
         return 0;
     }
@@ -264,7 +264,7 @@ unsigned asserted_formulas_new::get_formulas_last_level() const {
     }
 }
 
-bool asserted_formulas_new::invoke(simplify_fmls& s) {
+bool asserted_formulas::invoke(simplify_fmls& s) {
     if (!s.should_apply()) return true;
     IF_VERBOSE(10, verbose_stream() << "(smt." << s.id() << ")\n";);
     s();
@@ -281,7 +281,7 @@ bool asserted_formulas_new::invoke(simplify_fmls& s) {
     }
 }
 
-void asserted_formulas_new::display(std::ostream & out) const {
+void asserted_formulas::display(std::ostream & out) const {
     out << "asserted formulas:\n";
     for (unsigned i = 0; i < m_formulas.size(); i++) {
         if (i == m_qhead)
@@ -291,7 +291,7 @@ void asserted_formulas_new::display(std::ostream & out) const {
     out << "inconsistent: " << inconsistent() << "\n";
 }
 
-void asserted_formulas_new::display_ll(std::ostream & out, ast_mark & pp_visited) const {
+void asserted_formulas::display_ll(std::ostream & out, ast_mark & pp_visited) const {
     if (!m_formulas.empty()) {
         for (justified_expr const& f : m_formulas) 
             ast_def_ll_pp(out, m, f.get_fml(), pp_visited, true, false);
@@ -302,18 +302,18 @@ void asserted_formulas_new::display_ll(std::ostream & out, ast_mark & pp_visited
     }
 }
 
-void asserted_formulas_new::collect_statistics(statistics & st) const {
+void asserted_formulas::collect_statistics(statistics & st) const {
 }
 
 
-void asserted_formulas_new::swap_asserted_formulas(vector<justified_expr>& formulas) {
+void asserted_formulas::swap_asserted_formulas(vector<justified_expr>& formulas) {
     SASSERT(!inconsistent() || !formulas.empty());
     m_formulas.shrink(m_qhead);
     m_formulas.append(formulas);
 }
 
 
-void asserted_formulas_new::find_macros_core() {
+void asserted_formulas::find_macros_core() {
     vector<justified_expr> new_fmls;
     unsigned sz = m_formulas.size();
     (*m_macro_finder)(sz - m_qhead, m_formulas.c_ptr() + m_qhead, new_fmls);
@@ -321,7 +321,7 @@ void asserted_formulas_new::find_macros_core() {
     reduce_and_solve();
 }
 
-void asserted_formulas_new::apply_quasi_macros() {
+void asserted_formulas::apply_quasi_macros() {
     TRACE("before_quasi_macros", display(tout););
     vector<justified_expr> new_fmls;
     quasi_macros proc(m, m_macro_manager);    
@@ -335,7 +335,7 @@ void asserted_formulas_new::apply_quasi_macros() {
     reduce_and_solve();
 }
 
-void asserted_formulas_new::nnf_cnf() {
+void asserted_formulas::nnf_cnf() {
     nnf              apply_nnf(m, m_defined_names);
     vector<justified_expr> new_fmls;
     expr_ref_vector  push_todo(m);
@@ -379,7 +379,7 @@ void asserted_formulas_new::nnf_cnf() {
     swap_asserted_formulas(new_fmls);
 }
 
-void asserted_formulas_new::simplify_fmls::operator()() {
+void asserted_formulas::simplify_fmls::operator()() {
     vector<justified_expr> new_fmls;
     unsigned sz = af.m_formulas.size();                                
     for (unsigned i = af.m_qhead; i < sz; i++) {                                           
@@ -405,18 +405,18 @@ void asserted_formulas_new::simplify_fmls::operator()() {
 }
 
 
-void asserted_formulas_new::reduce_and_solve() {
+void asserted_formulas::reduce_and_solve() {
     IF_IVERBOSE(10, verbose_stream() << "(smt.reducing)\n";);
     flush_cache(); // collect garbage
     m_reduce_asserted_formulas();
 }
 
 
-void asserted_formulas_new::commit() {
+void asserted_formulas::commit() {
     commit(m_formulas.size());
 }
 
-void asserted_formulas_new::commit(unsigned new_qhead) {
+void asserted_formulas::commit(unsigned new_qhead) {
     m_macro_manager.mark_forbidden(new_qhead - m_qhead, m_formulas.c_ptr() + m_qhead);
     m_expr2depth.reset();
     for (unsigned i = m_qhead; i < new_qhead; ++i) {
@@ -426,7 +426,7 @@ void asserted_formulas_new::commit(unsigned new_qhead) {
     m_qhead = new_qhead;
 }
 
-void asserted_formulas_new::propagate_values() {
+void asserted_formulas::propagate_values() {
     TRACE("propagate_values", tout << "before:\n"; display(tout););
     flush_cache();
 
@@ -463,7 +463,7 @@ void asserted_formulas_new::propagate_values() {
         m_reduce_asserted_formulas();
 }
 
-unsigned asserted_formulas_new::propagate_values(unsigned i) {
+unsigned asserted_formulas::propagate_values(unsigned i) {
     expr * n = m_formulas[i].get_fml();
     expr_ref new_n(m);                                                  
     proof_ref new_pr(m);                                                
@@ -481,7 +481,7 @@ unsigned asserted_formulas_new::propagate_values(unsigned i) {
     return n != new_n ? 1 : 0;
 }
 
-void asserted_formulas_new::update_substitution(expr* n, proof* pr) {
+void asserted_formulas::update_substitution(expr* n, proof* pr) {
     expr* lhs, *rhs, *n1;
     if (is_ground(n) && (m.is_eq(n, lhs, rhs) || m.is_iff(n, lhs, rhs))) {
         compute_depth(lhs);
@@ -510,7 +510,7 @@ void asserted_formulas_new::update_substitution(expr* n, proof* pr) {
    \brief implement a Knuth-Bendix ordering on expressions.
 */
 
-bool asserted_formulas_new::is_gt(expr* lhs, expr* rhs) {
+bool asserted_formulas::is_gt(expr* lhs, expr* rhs) {
     if (lhs == rhs) {
         return false;
     }
@@ -541,7 +541,7 @@ bool asserted_formulas_new::is_gt(expr* lhs, expr* rhs) {
     return false;
 }
 
-void asserted_formulas_new::compute_depth(expr* e) {
+void asserted_formulas::compute_depth(expr* e) {
     ptr_vector<expr> todo;
     todo.push_back(e);    
     while (!todo.empty()) {
@@ -573,7 +573,7 @@ void asserted_formulas_new::compute_depth(expr* e) {
     }
 }
 
-proof * asserted_formulas_new::get_inconsistency_proof() const {
+proof * asserted_formulas::get_inconsistency_proof() const {
     if (!inconsistent())
         return 0;
     if (!m.proofs_enabled())
@@ -586,7 +586,7 @@ proof * asserted_formulas_new::get_inconsistency_proof() const {
     return 0;
 }
 
-void asserted_formulas_new::refine_inj_axiom_fn::simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { 
+void asserted_formulas::refine_inj_axiom_fn::simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { 
     expr* f = j.get_fml();
     if (is_quantifier(f) && simplify_inj_axiom(m, to_quantifier(f), n)) {
         TRACE("inj_axiom", tout << "simplifying...\n" << mk_pp(f, m) << "\n" << n << "\n";);
@@ -597,7 +597,7 @@ void asserted_formulas_new::refine_inj_axiom_fn::simplify(justified_expr const&
 }
 
 
-unsigned asserted_formulas_new::get_total_size() const {
+unsigned asserted_formulas::get_total_size() const {
     expr_mark visited;
     unsigned r  = 0;
     for (justified_expr const& j : m_formulas)
@@ -606,7 +606,7 @@ unsigned asserted_formulas_new::get_total_size() const {
 }
 
 #ifdef Z3DEBUG
-void pp(asserted_formulas_new & f) {
+void pp(asserted_formulas & f) {
     f.display(std::cout);
 }
 #endif
diff --git a/src/smt/asserted_formulas_new.h b/src/smt/asserted_formulas.h
similarity index 88%
rename from src/smt/asserted_formulas_new.h
rename to src/smt/asserted_formulas.h
index 82f18250d..ea00cb71c 100644
--- a/src/smt/asserted_formulas_new.h
+++ b/src/smt/asserted_formulas.h
@@ -3,7 +3,7 @@ Copyright (c) 2006 Microsoft Corporation
 
 Module Name:
 
-    asserted_formulas_new.h
+    asserted_formulas.h
 
 Abstract:
 
@@ -16,8 +16,8 @@ Author:
 Revision History:
 
 --*/
-#ifndef ASSERTED_FORMULAS_NEW_H_
-#define ASSERTED_FORMULAS_NEW_H_
+#ifndef ASSERTED_FORMULAS_H_
+#define ASSERTED_FORMULAS_H_
 
 #include "util/statistics.h"
 #include "ast/static_features.h"
@@ -41,7 +41,7 @@ Revision History:
 #include "smt/elim_term_ite.h"
 
 
-class asserted_formulas_new {
+class asserted_formulas {
     
     ast_manager &               m;
     smt_params &                m_params;
@@ -66,11 +66,11 @@ class asserted_formulas_new {
 
     class simplify_fmls {
     protected:
-        asserted_formulas_new& af;
+        asserted_formulas& af;
         ast_manager&           m;
         char const*            m_id;
     public:
-        simplify_fmls(asserted_formulas_new& af, char const* id): af(af), m(af.m), m_id(id) {}
+        simplify_fmls(asserted_formulas& af, char const* id): af(af), m(af.m), m_id(id) {}
         char const* id() const { return m_id; }
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) = 0;
         virtual bool should_apply() const { return true;}
@@ -80,13 +80,13 @@ class asserted_formulas_new {
 
     class reduce_asserted_formulas_fn : public simplify_fmls {
     public:
-        reduce_asserted_formulas_fn(asserted_formulas_new& af): simplify_fmls(af, "reduce-asserted") {}
+        reduce_asserted_formulas_fn(asserted_formulas& af): simplify_fmls(af, "reduce-asserted") {}
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { af.m_rewriter(j.get_fml(), n, p); }
     };
 
     class find_macros_fn : public simplify_fmls {
     public:
-        find_macros_fn(asserted_formulas_new& af): simplify_fmls(af, "find-macros") {}
+        find_macros_fn(asserted_formulas& af): simplify_fmls(af, "find-macros") {}
         virtual void operator()() { af.find_macros_core(); }
         virtual bool should_apply() const { return af.m_params.m_macro_finder && af.has_quantifiers(); }
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); }
@@ -94,7 +94,7 @@ class asserted_formulas_new {
 
     class apply_quasi_macros_fn : public simplify_fmls {
     public:
-        apply_quasi_macros_fn(asserted_formulas_new& af): simplify_fmls(af, "find-quasi-macros") {}
+        apply_quasi_macros_fn(asserted_formulas& af): simplify_fmls(af, "find-quasi-macros") {}
         virtual void operator()() { af.apply_quasi_macros(); }
         virtual bool should_apply() const { return af.m_params.m_quasi_macros && af.has_quantifiers(); }
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); }
@@ -102,7 +102,7 @@ class asserted_formulas_new {
 
     class nnf_cnf_fn : public simplify_fmls {
     public:
-        nnf_cnf_fn(asserted_formulas_new& af): simplify_fmls(af, "nnf-cnf") {}
+        nnf_cnf_fn(asserted_formulas& af): simplify_fmls(af, "nnf-cnf") {}
         virtual void operator()() { af.nnf_cnf(); }
         virtual bool should_apply() const { return af.m_params.m_nnf_cnf || (af.m_params.m_mbqi && af.has_quantifiers()); }
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); }
@@ -110,7 +110,7 @@ class asserted_formulas_new {
 
     class propagate_values_fn : public simplify_fmls {
     public:
-        propagate_values_fn(asserted_formulas_new& af): simplify_fmls(af, "propagate-values") {}
+        propagate_values_fn(asserted_formulas& af): simplify_fmls(af, "propagate-values") {}
         virtual void operator()() { af.propagate_values(); }
         virtual bool should_apply() const { return af.m_params.m_propagate_values; }
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); }
@@ -119,7 +119,7 @@ class asserted_formulas_new {
     class distribute_forall_fn : public simplify_fmls {
         distribute_forall m_functor;
     public:
-        distribute_forall_fn(asserted_formulas_new& af): simplify_fmls(af, "distribute-forall"), m_functor(af.m) {}
+        distribute_forall_fn(asserted_formulas& af): simplify_fmls(af, "distribute-forall"), m_functor(af.m) {}
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { m_functor(j.get_fml(), n); }
         virtual bool should_apply() const { return af.m_params.m_distribute_forall && af.has_quantifiers(); }
         virtual void post_op() { af.reduce_and_solve();  TRACE("asserted_formulas", af.display(tout);); }
@@ -128,21 +128,21 @@ class asserted_formulas_new {
     class pattern_inference_fn : public simplify_fmls {
         pattern_inference_rw m_infer;
     public:
-        pattern_inference_fn(asserted_formulas_new& af): simplify_fmls(af, "pattern-inference"), m_infer(af.m, af.m_params) {}
+        pattern_inference_fn(asserted_formulas& af): simplify_fmls(af, "pattern-inference"), m_infer(af.m, af.m_params) {}
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { m_infer(j.get_fml(), n, p); }
         virtual bool should_apply() const { return af.m_params.m_ematching && af.has_quantifiers(); }
     };
 
     class refine_inj_axiom_fn : public simplify_fmls {
     public:
-        refine_inj_axiom_fn(asserted_formulas_new& af): simplify_fmls(af, "refine-injectivity") {}
+        refine_inj_axiom_fn(asserted_formulas& af): simplify_fmls(af, "refine-injectivity") {}
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p);
         virtual bool should_apply() const { return af.m_params.m_refine_inj_axiom && af.has_quantifiers(); }
     };
 
     class max_bv_sharing_fn : public simplify_fmls {
     public:
-        max_bv_sharing_fn(asserted_formulas_new& af): simplify_fmls(af, "maximizing-bv-sharing") {}
+        max_bv_sharing_fn(asserted_formulas& af): simplify_fmls(af, "maximizing-bv-sharing") {}
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { af.m_bv_sharing(j.get_fml(), n, p); }
         virtual bool should_apply() const { return af.m_params.m_max_bv_sharing; }
         virtual void post_op() { af.m_reduce_asserted_formulas(); }
@@ -151,7 +151,7 @@ class asserted_formulas_new {
     class elim_term_ite_fn : public simplify_fmls {
         elim_term_ite_rw m_elim;
     public:
-        elim_term_ite_fn(asserted_formulas_new& af): simplify_fmls(af, "elim-term-ite"), m_elim(af.m, af.m_defined_names) {}
+        elim_term_ite_fn(asserted_formulas& af): simplify_fmls(af, "elim-term-ite"), m_elim(af.m, af.m_defined_names) {}
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { m_elim(j.get_fml(), n, p); }
         virtual bool should_apply() const { return af.m_params.m_eliminate_term_ite && af.m_params.m_lift_ite != LI_FULL; }
         virtual void post_op() { af.m_formulas.append(m_elim.new_defs()); af.reduce_and_solve(); m_elim.reset(); }
@@ -161,7 +161,7 @@ class asserted_formulas_new {
     class NAME : public simplify_fmls {                                 \
         FUNCTOR m_functor;                                              \
     public:                                                             \
-        NAME(asserted_formulas_new& af):simplify_fmls(af, MSG), m_functor ARG {} \
+        NAME(asserted_formulas& af):simplify_fmls(af, MSG), m_functor ARG {} \
         virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { \
             m_functor(j.get_fml(), n, p);                               \
         }                                                               \
@@ -221,8 +221,8 @@ class asserted_formulas_new {
     bool pull_cheap_ite_trees();
 
 public:
-    asserted_formulas_new(ast_manager & m, smt_params & p);
-    ~asserted_formulas_new();
+    asserted_formulas(ast_manager & m, smt_params & p);
+    ~asserted_formulas();
 
     bool has_quantifiers() const { return m_has_quantifiers; }
     void setup();
@@ -265,5 +265,5 @@ public:
 
 };
 
-#endif /* ASSERTED_FORMULAS_NEW_H_ */
+#endif /* ASSERTED_FORMULAS_H_ */
 
diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp
index 7b92bdd28..eef84f773 100644
--- a/src/smt/smt_context.cpp
+++ b/src/smt/smt_context.cpp
@@ -148,8 +148,8 @@ namespace smt {
         dst_ctx.set_logic(src_ctx.m_setup.get_logic());
         dst_ctx.copy_plugins(src_ctx, dst_ctx);
 
-        asserted_formulas_new& src_af = src_ctx.m_asserted_formulas;
-        asserted_formulas_new& dst_af = dst_ctx.m_asserted_formulas;
+        asserted_formulas& src_af = src_ctx.m_asserted_formulas;
+        asserted_formulas& dst_af = dst_ctx.m_asserted_formulas;
 
         // Copy asserted formulas.
         for (unsigned i = 0; i < src_af.get_num_formulas(); ++i) {
diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h
index a6a67702a..3cc577b29 100644
--- a/src/smt/smt_context.h
+++ b/src/smt/smt_context.h
@@ -36,7 +36,7 @@ Revision History:
 #include "smt/smt_case_split_queue.h"
 #include "smt/smt_almost_cg_table.h"
 #include "smt/smt_failure.h"
-#include "smt/asserted_formulas_new.h"
+#include "smt/asserted_formulas.h"
 #include "smt/smt_types.h"
 #include "smt/dyn_ack.h"
 #include "ast/ast_smt_pp.h"
@@ -81,7 +81,7 @@ namespace smt {
         params_ref                  m_params;
         setup                       m_setup;
         timer                       m_timer;
-        asserted_formulas_new       m_asserted_formulas;
+        asserted_formulas           m_asserted_formulas;
         scoped_ptr<quantifier_manager>   m_qmanager;
         scoped_ptr<model_generator>      m_model_generator;
         scoped_ptr<relevancy_propagator> m_relevancy_propagator;
diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h
index 70355502d..16a49961e 100644
--- a/src/smt/theory_arith_core.h
+++ b/src/smt/theory_arith_core.h
@@ -395,7 +395,8 @@ namespace smt {
 
     template<typename Ext>
     theory_var theory_arith<Ext>::internalize_div(app * n) {
-        if (!m_util.is_numeral(n->get_arg(1))) found_underspecified_op(n);
+        rational r(1);
+        if (!m_util.is_numeral(n->get_arg(1), r) || r.is_zero()) found_underspecified_op(n);
         found_underspecified_op(n);
         theory_var s      = mk_binary_op(n);
         context & ctx     = get_context();
@@ -419,7 +420,8 @@ namespace smt {
     template<typename Ext>
     theory_var theory_arith<Ext>::internalize_mod(app * n) {
         TRACE("arith_mod", tout << "internalizing...\n" << mk_pp(n, get_manager()) << "\n";);
-        if (!m_util.is_numeral(n->get_arg(1))) found_underspecified_op(n);
+        rational r(1);
+        if (!m_util.is_numeral(n->get_arg(1), r) || r.is_zero()) found_underspecified_op(n);
         theory_var s      = mk_binary_op(n);
         context & ctx     = get_context();
         if (!ctx.relevancy())
@@ -429,7 +431,8 @@ namespace smt {
 
     template<typename Ext>
     theory_var theory_arith<Ext>::internalize_rem(app * n) {
-        if (!m_util.is_numeral(n->get_arg(1))) found_underspecified_op(n);
+        rational r(1);
+        if (!m_util.is_numeral(n->get_arg(1), r) || r.is_zero()) found_underspecified_op(n);
         theory_var s  = mk_binary_op(n);
         context & ctx = get_context();
         if (!ctx.relevancy()) {
@@ -734,11 +737,6 @@ namespace smt {
             return internalize_div(n);
         else if (m_util.is_idiv(n))
             return internalize_idiv(n);
-        else if (is_app_of(n, get_id(), OP_IDIV_0) || is_app_of(n, get_id(), OP_DIV_0)) {
-            ctx.internalize(n->get_arg(0), false);
-            enode * e = mk_enode(n);
-            return mk_var(e);
-        }
         else if (m_util.is_mod(n)) 
             return internalize_mod(n);
         else if (m_util.is_rem(n)) 
@@ -1226,7 +1224,8 @@ namespace smt {
         app * rhs      = to_app(n->get_arg(1));
         expr * rhs2;
         if (m_util.is_to_real(rhs, rhs2) && is_app(rhs2)) { rhs = to_app(rhs2); }
-        if (!m_util.is_numeral(rhs)) {
+        if (!m_util.is_numeral(rhs)) {        
+            UNREACHABLE();
             throw default_exception("malformed atomic constraint");
         }
         theory_var v   = internalize_term_core(lhs);
diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp
index c7a3d7920..274f89e8b 100644
--- a/src/smt/theory_array_full.cpp
+++ b/src/smt/theory_array_full.cpp
@@ -516,7 +516,8 @@ namespace smt {
 
         expr_ref sel1(m), sel2(m);
         sel1 = mk_select(args1.size(), args1.c_ptr());
-        sel2 = ctx.get_rewriter().mk_app(f, args2.size(), args2.c_ptr());
+        sel2 = m.mk_app(f, args2.size(), args2.c_ptr());
+        ctx.get_rewriter()(sel2);
         ctx.internalize(sel1, false);
         ctx.internalize(sel2, false);
         
@@ -537,6 +538,7 @@ namespace smt {
         SASSERT(is_map(mp));
                 
         app* map = mp->get_owner();
+        ast_manager& m = get_manager();
         context& ctx = get_context();
         if (!ctx.add_fingerprint(this, 0, 1, &mp)) {
             return false;
@@ -552,9 +554,9 @@ namespace smt {
             args2.push_back(mk_default(map->get_arg(i)));
         }
 
+        expr_ref def2(m.mk_app(f, args2.size(), args2.c_ptr()), m);
+        ctx.get_rewriter()(def2);
         expr* def1 = mk_default(map);
-        expr_ref def2(get_manager());
-        def2 = ctx.get_rewriter().mk_app(f, args2.size(), args2.c_ptr());
         ctx.internalize(def1, false);
         ctx.internalize(def2, false);
         return try_assign_eq(def1, def2);
diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp
index 0c09d0403..dd044b78a 100644
--- a/src/smt/theory_lra.cpp
+++ b/src/smt/theory_lra.cpp
@@ -293,9 +293,6 @@ namespace smt {
         }
 
         void found_not_handled(expr* n) {
-            if (a.is_div0(n)) {
-                return;
-            }
             m_not_handled = n;
             if (is_app(n) && is_underspecified(to_app(n))) {
                 m_underspecified.push_back(to_app(n));
diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp
index 0443a51ed..17a69f93e 100644
--- a/src/tactic/arith/purify_arith_tactic.cpp
+++ b/src/tactic/arith/purify_arith_tactic.cpp
@@ -301,7 +301,7 @@ struct purify_arith_proc {
             if (complete()) {
                 // y != 0 \/ k = div-0(x)
                 push_cnstr(OR(NOT(EQ(y, mk_real_zero())),
-                              EQ(k, u().mk_div0(x))));
+                              EQ(k, u().mk_div(x, mk_real_zero()))));
                 push_cnstr_pr(result_pr);
             }
         }
@@ -349,10 +349,10 @@ struct purify_arith_proc {
             push_cnstr_pr(mod_pr);
 
             if (complete()) {
-                push_cnstr(OR(NOT(EQ(y, zero)), EQ(k1, u().mk_idiv0(x))));
+                push_cnstr(OR(NOT(EQ(y, zero)), EQ(k1, u().mk_idiv(x, zero))));
                 push_cnstr_pr(result_pr);
 
-                push_cnstr(OR(NOT(EQ(y, zero)), EQ(k2, u().mk_mod0(x))));
+                push_cnstr(OR(NOT(EQ(y, zero)), EQ(k2, u().mk_mod(x, zero))));
                 push_cnstr_pr(mod_pr);
             }
         }
@@ -414,7 +414,7 @@ struct purify_arith_proc {
                 // (^ x 0) --> k  |  x != 0 implies k = 1,   x = 0 implies k = 0^0 
                 push_cnstr(OR(EQ(x, zero), EQ(k, one)));
                 push_cnstr_pr(result_pr);
-                push_cnstr(OR(NOT(EQ(x, zero)), EQ(k, is_int ? u().mk_0_pw_0_int() : u().mk_0_pw_0_real())));
+                push_cnstr(OR(NOT(EQ(x, zero)), EQ(k, u().mk_power(zero, zero))));
                 push_cnstr_pr(result_pr);
             }
             else if (!is_int) {

From d940516df3c60ca01c69e1569dcfbb3e950f52ec Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 27 Aug 2017 11:01:45 -0700
Subject: [PATCH 33/74] fixes

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/arith_decl_plugin.cpp            | 22 +---------------------
 src/ast/arith_decl_plugin.h              | 24 ------------------------
 src/muz/spacer/spacer_sym_mux.cpp        |  1 +
 src/smt/qi_queue.cpp                     |  5 ++---
 src/smt/smt_quick_checker.cpp            |  3 +--
 src/smt/smt_quick_checker.h              |  1 -
 src/smt/theory_arith_core.h              |  5 ++---
 src/smt/theory_fpa.cpp                   |  3 +--
 src/tactic/arith/purify_arith_tactic.cpp |  7 ++++---
 src/test/ext_numeral.cpp                 |  6 +++---
 10 files changed, 15 insertions(+), 62 deletions(-)

diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp
index 0719ebdfa..03ee77458 100644
--- a/src/ast/arith_decl_plugin.cpp
+++ b/src/ast/arith_decl_plugin.cpp
@@ -178,7 +178,7 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) {
     MK_AC_OP(m_i_mul_decl, "*", OP_MUL, i);
     MK_LEFT_ASSOC_OP(m_i_div_decl, "div", OP_IDIV, i);
     MK_OP(m_i_rem_decl, "rem", OP_REM, i);
-    //MK_OP(m_i_mod_decl, "mod", OP_MOD, i);
+    MK_OP(m_i_mod_decl, "mod", OP_MOD, i);
     MK_UNARY(m_i_uminus_decl, "-", OP_UMINUS, i);
 
     m_to_real_decl = m->mk_func_decl(symbol("to_real"), i, r, func_decl_info(id, OP_TO_REAL));
@@ -215,18 +215,8 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) {
     m_e = m->mk_const(e_decl);
     m->inc_ref(m_e);
 
-    //func_decl * z_pw_z_int = m->mk_const_decl(symbol("0^0-int"), i, func_decl_info(id, OP_0_PW_0_INT));
-    //m_0_pw_0_int = m->mk_const(z_pw_z_int);
-    //m->inc_ref(m_0_pw_0_int);
-
-    //func_decl * z_pw_z_real = m->mk_const_decl(symbol("0^0-real"), r, func_decl_info(id, OP_0_PW_0_REAL));
-    //m_0_pw_0_real = m->mk_const(z_pw_z_real);
-    //m->inc_ref(m_0_pw_0_real);
 
     MK_OP(m_neg_root_decl, "neg-root", OP_NEG_ROOT, r);
-    //MK_UNARY(m_div_0_decl, "/0", OP_DIV_0, r);
-    //MK_UNARY(m_idiv_0_decl, "div0", OP_IDIV_0, i);
-    //MK_UNARY(m_mod_0_decl, "mod0", OP_MOD_0, i);
     MK_UNARY(m_u_asin_decl, "asin-u", OP_U_ASIN, r);
     MK_UNARY(m_u_acos_decl, "acos-u", OP_U_ACOS, r);
 }
@@ -279,12 +269,7 @@ arith_decl_plugin::arith_decl_plugin():
     m_atanh_decl(0),
     m_pi(0),
     m_e(0),
-    m_0_pw_0_int(0),
-    m_0_pw_0_real(0),
     m_neg_root_decl(0),
-    m_div_0_decl(0),
-    m_idiv_0_decl(0),
-    m_mod_0_decl(0),
     m_u_asin_decl(0),
     m_u_acos_decl(0),
     m_convert_int_numerals_to_real(false) {
@@ -339,12 +324,7 @@ void arith_decl_plugin::finalize() {
     DEC_REF(m_atanh_decl);
     DEC_REF(m_pi);
     DEC_REF(m_e);
-    DEC_REF(m_0_pw_0_int);
-    DEC_REF(m_0_pw_0_real);
     DEC_REF(m_neg_root_decl);
-    DEC_REF(m_div_0_decl);
-    DEC_REF(m_idiv_0_decl);
-    DEC_REF(m_mod_0_decl);
     DEC_REF(m_u_asin_decl);
     DEC_REF(m_u_acos_decl);
     m_manager->dec_array_ref(m_small_ints.size(), m_small_ints.c_ptr());
diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h
index 0238df3ae..4b24ea5e6 100644
--- a/src/ast/arith_decl_plugin.h
+++ b/src/ast/arith_decl_plugin.h
@@ -70,12 +70,7 @@ enum arith_op_kind {
     OP_PI,
     OP_E,
     // under-specified symbols
-    //OP_0_PW_0_INT,    // 0^0 for integers
-    //OP_0_PW_0_REAL,   // 0^0 for reals
     OP_NEG_ROOT,      // x^n when n is even and x is negative
-    // OP_DIV_0,         // x/0
-    // OP_IDIV_0,        // x div 0
-    // OP_MOD_0,         // x mod 0
     OP_U_ASIN,        // asin(x) for x < -1 or x > 1
     OP_U_ACOS,        // acos(x) for x < -1 or x > 1
     LAST_ARITH_OP
@@ -141,12 +136,7 @@ protected:
     app       * m_pi;
     app       * m_e;
 
-    app       * m_0_pw_0_int;
-    app       * m_0_pw_0_real;
     func_decl * m_neg_root_decl;
-    func_decl * m_div_0_decl;
-    func_decl * m_idiv_0_decl;
-    func_decl * m_mod_0_decl;
     func_decl * m_u_asin_decl;
     func_decl * m_u_acos_decl;
     ptr_vector<app> m_small_ints;
@@ -207,10 +197,6 @@ public:
 
     app * mk_e() const { return m_e; }
 
-    app * mk_0_pw_0_int() const { return m_0_pw_0_int; }
-
-    app * mk_0_pw_0_real() const { return m_0_pw_0_real; }
-
     virtual expr * get_some_value(sort * s);
 
     virtual bool is_considered_uninterpreted(func_decl * f) {
@@ -218,12 +204,7 @@ public:
             return false;
         switch (f->get_decl_kind())
         {
-        //case OP_0_PW_0_INT:
-        //case OP_0_PW_0_REAL:
         case OP_NEG_ROOT:
-        //case OP_DIV_0:
-        //case OP_IDIV_0:
-        //case OP_MOD_0:
         case OP_U_ASIN:
         case OP_U_ACOS:
             return true;
@@ -425,11 +406,6 @@ public:
     app * mk_pi() { return plugin().mk_pi(); }
     app * mk_e()  { return plugin().mk_e(); }
 
- //   app * mk_0_pw_0_int() { return plugin().mk_0_pw_0_int(); }
- //   app * mk_0_pw_0_real() { return plugin().mk_0_pw_0_real(); }
- //   app * mk_div0(expr * arg) { return m_manager.mk_app(m_afid, OP_DIV_0, arg); }
- //   app * mk_idiv0(expr * arg) { return m_manager.mk_app(m_afid, OP_IDIV_0, arg); }
- //   app * mk_mod0(expr * arg) { return m_manager.mk_app(m_afid, OP_MOD_0, arg); }
     app * mk_neg_root(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_NEG_ROOT, arg1, arg2); }
     app * mk_u_asin(expr * arg) { return m_manager.mk_app(m_afid, OP_U_ASIN, arg); }
     app * mk_u_acos(expr * arg) { return m_manager.mk_app(m_afid, OP_U_ACOS, arg); }
diff --git a/src/muz/spacer/spacer_sym_mux.cpp b/src/muz/spacer/spacer_sym_mux.cpp
index c311d9990..ee1d3726d 100644
--- a/src/muz/spacer/spacer_sym_mux.cpp
+++ b/src/muz/spacer/spacer_sym_mux.cpp
@@ -368,6 +368,7 @@ public:
         app * a = to_app(s);
         func_decl * sym = a->get_decl();
         if (!m_parent.has_index(sym, m_from_idx)) {
+            (void) m_homogenous;
             SASSERT(!m_homogenous || !m_parent.is_muxed(sym));
             return false;
         }
diff --git a/src/smt/qi_queue.cpp b/src/smt/qi_queue.cpp
index 8f8e3df7b..f77fde29a 100644
--- a/src/smt/qi_queue.cpp
+++ b/src/smt/qi_queue.cpp
@@ -227,9 +227,8 @@ namespace smt {
         TRACE("qi_queue_instance", tout << "new instance:\n" << mk_pp(instance, m_manager) << "\n";);
         expr_ref  s_instance(m_manager);
         proof_ref pr(m_manager);
-        th_rewriter & simp = m_context.get_rewriter();
-        simp(instance, s_instance, pr);
-        TRACE("qi_queue_bug", tout << "new instance after simplification:\n" << mk_pp(s_instance, m_manager) << "\n";);
+        m_context.get_rewriter()(instance, s_instance, pr);
+        TRACE("qi_queue_bug", tout << "new instance after simplification:\n" << s_instance << "\n";);
         if (m_manager.is_true(s_instance)) {
             TRACE("checker", tout << "reduced to true, before:\n" << mk_ll_pp(instance, m_manager););
 
diff --git a/src/smt/smt_quick_checker.cpp b/src/smt/smt_quick_checker.cpp
index 773768944..72c28fd7d 100644
--- a/src/smt/smt_quick_checker.cpp
+++ b/src/smt/smt_quick_checker.cpp
@@ -164,7 +164,6 @@ namespace smt {
     quick_checker::quick_checker(context & c):
         m_context(c),
         m_manager(c.get_manager()),
-        m_simplifier(c.get_rewriter()),
         m_collector(c),
         m_new_exprs(m_manager) {
     }
@@ -411,7 +410,7 @@ namespace smt {
             }
         }
         expr_ref new_expr(m_manager);
-        new_expr = m_simplifier.mk_app(to_app(n)->get_decl(), num_args, new_args.c_ptr());
+        new_expr = m_context.get_rewriter().mk_app(to_app(n)->get_decl(), num_args, new_args.c_ptr());
         m_new_exprs.push_back(new_expr);
         m_canonize_cache.insert(n, new_expr);
         return new_expr;
diff --git a/src/smt/smt_quick_checker.h b/src/smt/smt_quick_checker.h
index d07e10921..21a570c3c 100644
--- a/src/smt/smt_quick_checker.h
+++ b/src/smt/smt_quick_checker.h
@@ -77,7 +77,6 @@ namespace smt {
 
         context &            m_context;
         ast_manager &        m_manager;
-        th_rewriter &        m_simplifier;
         collector            m_collector;
         expr_ref_vector      m_new_exprs;
         vector<enode_vector> m_candidate_vectors; 
diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h
index 16a49961e..13aba9686 100644
--- a/src/smt/theory_arith_core.h
+++ b/src/smt/theory_arith_core.h
@@ -449,9 +449,8 @@ namespace smt {
         expr_ref s_ante(m), s_conseq(m);
         expr* s_conseq_n, * s_ante_n;
         bool negated;
-        proof_ref pr(m);
 
-        s(ante, s_ante, pr);
+        s(ante, s_ante);
         if (ctx.get_cancel_flag()) return;
         negated = m.is_not(s_ante, s_ante_n);
         if (negated) s_ante = s_ante_n;
@@ -459,7 +458,7 @@ namespace smt {
         literal l_ante = ctx.get_literal(s_ante);
         if (negated) l_ante.neg();
 
-        s(conseq, s_conseq, pr);
+        s(conseq, s_conseq);
         if (ctx.get_cancel_flag()) return;
         negated = m.is_not(s_conseq, s_conseq_n);
         if (negated) s_conseq = s_conseq_n;
diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp
index 3f246b2c7..dfd295220 100644
--- a/src/smt/theory_fpa.cpp
+++ b/src/smt/theory_fpa.cpp
@@ -385,7 +385,6 @@ namespace smt {
     {
         ast_manager & m = get_manager();
         context & ctx = get_context();
-        th_rewriter & simp = ctx.get_rewriter();
 
         expr_ref res(m), t(m);
         proof_ref t_pr(m);
@@ -394,7 +393,7 @@ namespace smt {
         expr_ref_vector::iterator it = m_converter.m_extra_assertions.begin();
         expr_ref_vector::iterator end = m_converter.m_extra_assertions.end();
         for (; it != end; it++) {
-            simp(*it, t, t_pr);
+            ctx.get_rewriter()(*it, t, t_pr);
             res = m.mk_and(res, t);
         }
         m_converter.m_extra_assertions.reset();
diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp
index 17a69f93e..7e89794ce 100644
--- a/src/tactic/arith/purify_arith_tactic.cpp
+++ b/src/tactic/arith/purify_arith_tactic.cpp
@@ -297,8 +297,8 @@ struct purify_arith_proc {
             push_cnstr(OR(EQ(y, mk_real_zero()),
                           EQ(u().mk_mul(y, k), x)));
             push_cnstr_pr(result_pr);
-
-            if (complete()) {
+            rational r;
+            if (complete() && (!u().is_numeral(y, r) || r.is_zero())) {
                 // y != 0 \/ k = div-0(x)
                 push_cnstr(OR(NOT(EQ(y, mk_real_zero())),
                               EQ(k, u().mk_div(x, mk_real_zero()))));
@@ -348,7 +348,8 @@ struct purify_arith_proc {
             push_cnstr(OR(u().mk_ge(y, zero), u().mk_lt(k2, u().mk_mul(u().mk_numeral(rational(-1), true), y))));
             push_cnstr_pr(mod_pr);
 
-            if (complete()) {
+            rational r;
+            if (complete() && (!u().is_numeral(y, r) || r.is_zero())) {
                 push_cnstr(OR(NOT(EQ(y, zero)), EQ(k1, u().mk_idiv(x, zero))));
                 push_cnstr_pr(result_pr);
 
diff --git a/src/test/ext_numeral.cpp b/src/test/ext_numeral.cpp
index 4c82617c9..003aa272f 100644
--- a/src/test/ext_numeral.cpp
+++ b/src/test/ext_numeral.cpp
@@ -43,13 +43,13 @@ static void FUN_NAME(int a, ext_numeral_kind ak, int b, ext_numeral_kind bk, int
     scoped_mpq _a(m), _b(m), _c(m);                                     \
     m.set(_a, a);                                                       \
     m.set(_b, b);                                                       \
-    ext_numeral_kind ck;                                                \
+    ext_numeral_kind ck(EN_NUMERAL);                                    \
     OP_NAME(m, _a, ak, _b, bk, _c, ck);                                 \
-    ENSURE(ck == expected_ck);                                         \
+    ENSURE(ck == expected_ck);                                          \
     if (expected_ck == EN_NUMERAL) {                                    \
         scoped_mpq _expected_c(m);                                      \
         m.set(_expected_c, expected_c);                                 \
-        ENSURE(m.eq(_c, _expected_c));                                 \
+        ENSURE(m.eq(_c, _expected_c));                                  \
     }                                                                   \
 }
 

From f9dc6385b203621f810c1e44df5ea9facc989b41 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 27 Aug 2017 12:19:24 -0700
Subject: [PATCH 34/74] n/a

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/api_model.cpp |  9 +++++++++
 src/api/c++/z3++.h    |  4 ++++
 src/api/z3_api.h      | 10 ++++++++++
 3 files changed, 23 insertions(+)

diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp
index 1646c691f..66002baa0 100644
--- a/src/api/api_model.cpp
+++ b/src/api/api_model.cpp
@@ -319,6 +319,15 @@ extern "C" {
         Z3_CATCH_RETURN(0);
     }
 
+    void Z3_API Z3_func_interp_set_else(Z3_context c, Z3_func_interp f, Z3_ast else_value) {
+        Z3_TRY;
+        LOG_Z3_func_interp_set_else(c, f, else_value);
+        RESET_ERROR_CODE();
+        // CHECK_NON_NULL(f, 0);
+        to_func_interp_ref(f)->set_else(to_expr(else_value));
+        Z3_CATCH;
+    }
+
     unsigned Z3_API Z3_func_interp_get_arity(Z3_context c, Z3_func_interp f) {
         Z3_TRY;
         LOG_Z3_func_interp_get_arity(c, f);
diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h
index cf1a5070a..2f67cb72a 100644
--- a/src/api/c++/z3++.h
+++ b/src/api/c++/z3++.h
@@ -1735,6 +1735,10 @@ namespace z3 {
             Z3_add_func_entry(ctx(), m_interp, args, value);
             check_error();
         }
+        void set_else(expr& value) {
+            Z3_func_entry_set_else(ctx(), m_interp, value);
+            check_error();
+        }
     };
 
     class model : public object {
diff --git a/src/api/z3_api.h b/src/api/z3_api.h
index 2f14c3f2e..045417d38 100644
--- a/src/api/z3_api.h
+++ b/src/api/z3_api.h
@@ -4925,6 +4925,16 @@ extern "C" {
     */
     Z3_ast Z3_API Z3_func_interp_get_else(Z3_context c, Z3_func_interp f);
 
+    /**
+       \brief Return the 'else' value of the given function interpretation.
+
+       A function interpretation is represented as a finite map and an 'else' value.
+       This procedure can be used to update the 'else' value.
+
+       def_API('Z3_func_interp_set_else', VOID, (_in(CONTEXT), _in(FUNC_INTERP), _in(AST)))
+    */
+    void Z3_API Z3_func_interp_set_else(Z3_context c, Z3_func_interp f, Z3_ast else_value);
+
     /**
        \brief Return the arity (number of arguments) of the given function interpretation.
 

From 03f263b974a8300444a956877940c9967a425b3b Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 27 Aug 2017 13:02:59 -0700
Subject: [PATCH 35/74] update names

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/api_model.cpp | 4 ++--
 src/api/c++/z3++.h    | 4 ++--
 src/api/z3_api.h      | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp
index 66002baa0..540f014c6 100644
--- a/src/api/api_model.cpp
+++ b/src/api/api_model.cpp
@@ -337,9 +337,9 @@ extern "C" {
         Z3_CATCH_RETURN(0);
     }
 
-    void Z3_API Z3_add_func_entry(Z3_context c, Z3_func_interp fi, Z3_ast_vector args, Z3_ast value) {
+    void Z3_API Z3_func_interp_add_entry(Z3_context c, Z3_func_interp fi, Z3_ast_vector args, Z3_ast value) {
         Z3_TRY;
-        LOG_Z3_add_func_entry(c, fi, args, value);
+        LOG_Z3_func_interp_add_entry(c, fi, args, value);
         //CHECK_NON_NULL(fi, void);
         //CHECK_NON_NULL(args, void);
         //CHECK_NON_NULL(value, void);
diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h
index 2f67cb72a..6d8ff3b35 100644
--- a/src/api/c++/z3++.h
+++ b/src/api/c++/z3++.h
@@ -1732,11 +1732,11 @@ namespace z3 {
         unsigned num_entries() const { unsigned r = Z3_func_interp_get_num_entries(ctx(), m_interp); check_error(); return r; }
         func_entry entry(unsigned i) const { Z3_func_entry e = Z3_func_interp_get_entry(ctx(), m_interp, i); check_error(); return func_entry(ctx(), e); }
         void add_entry(expr_vector const& args, expr& value) {
-            Z3_add_func_entry(ctx(), m_interp, args, value);
+            Z3_func_interp_add_entry(ctx(), m_interp, args, value);
             check_error();
         }
         void set_else(expr& value) {
-            Z3_func_entry_set_else(ctx(), m_interp, value);
+            Z3_func_interp_set_else(ctx(), m_interp, value);
             check_error();
         }
     };
diff --git a/src/api/z3_api.h b/src/api/z3_api.h
index 045417d38..bed70cb6c 100644
--- a/src/api/z3_api.h
+++ b/src/api/z3_api.h
@@ -4954,9 +4954,9 @@ extern "C" {
        If an two entries are added with the same arguments, only the second insertion survives and the
        first inserted entry is removed.
 
-       def_API('Z3_add_func_entry', VOID, (_in(CONTEXT), _in(FUNC_INTERP), _in(AST_VECTOR), _in(AST)))
+       def_API('Z3_func_interp_add_entry', VOID, (_in(CONTEXT), _in(FUNC_INTERP), _in(AST_VECTOR), _in(AST)))
      */
-    void Z3_API Z3_add_func_entry(Z3_context c, Z3_func_interp fi, Z3_ast_vector args, Z3_ast value);
+    void Z3_API Z3_func_interp_add_entry(Z3_context c, Z3_func_interp fi, Z3_ast_vector args, Z3_ast value);
 
     /**
        \brief Increment the reference counter of the given Z3_func_entry object.

From 9e4b2a67959fa09cbfb7e09abea81c0d88d255af Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Mon, 28 Aug 2017 02:55:50 -0700
Subject: [PATCH 36/74] port simplifications on bv2int

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/bv_rewriter.cpp | 79 +++++++++++++++++++++++++++++++-
 src/ast/rewriter/bv_rewriter.h   |  5 ++
 2 files changed, 82 insertions(+), 2 deletions(-)

diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp
index edd601c9c..75f931c1d 100644
--- a/src/ast/rewriter/bv_rewriter.cpp
+++ b/src/ast/rewriter/bv_rewriter.cpp
@@ -1365,13 +1365,88 @@ br_status bv_rewriter::mk_bv2int(expr * arg, expr_ref & result) {
         result = m_autil.mk_numeral(v, true);
         return BR_DONE;
     }
-
-    // TODO: add other simplifications
+    if (m_util.is_concat(arg)) {
+        if (to_app(arg)->get_num_args() == 0) {
+            result = m_autil.mk_int(0);
+            return BR_DONE;
+        }
+        expr_ref_vector args(m());        
+        
+        unsigned num_args = to_app(arg)->get_num_args();
+        for (expr* x : *to_app(arg)) {
+            args.push_back(m_util.mk_bv2int(x));
+        }
+        unsigned sz = get_bv_size(to_app(arg)->get_arg(num_args-1));
+        for (unsigned i = num_args - 1; i > 0; ) {
+            expr_ref tmp(m());
+            --i;
+            tmp = args[i].get();
+            tmp = m_autil.mk_mul(m_autil.mk_numeral(power(numeral(2), sz), true), tmp);
+            args[i] = tmp;
+            sz += get_bv_size(to_app(arg)->get_arg(i));
+        }
+        result = m_autil.mk_add(args.size(), args.c_ptr());
+        return BR_REWRITE2;
+    }
+    if (is_mul_no_overflow(arg)) {
+        expr_ref_vector args(m());
+        for (expr* x : *to_app(arg)) args.push_back(m_util.mk_bv2int(x));
+        result = m_autil.mk_mul(args.size(), args.c_ptr());
+        return BR_REWRITE2;
+    }
+    if (is_add_no_overflow(arg)) {
+        expr_ref_vector args(m());
+        for (expr* x : *to_app(arg)) args.push_back(m_util.mk_bv2int(x));
+        result = m_autil.mk_add(args.size(), args.c_ptr());
+        return BR_REWRITE2;
+    }
 
     return BR_FAILED;
 }
 
 
+bool bv_rewriter::is_mul_no_overflow(expr* e) {
+    if (!m_util.is_bv_mul(e)) return false;
+    unsigned sz = get_bv_size(e);
+    unsigned sum = 0;
+    for (expr* x : *to_app(e)) sum += sz-num_leading_zero_bits(x);
+    return sum < sz;
+}
+
+bool bv_rewriter::is_add_no_overflow(expr* e) {
+    if (!is_add(e)) return false;
+    for (expr* x : *to_app(e)) {
+        if (0 == num_leading_zero_bits(x)) return false;
+    }
+    return true;
+}
+
+unsigned bv_rewriter::num_leading_zero_bits(expr* e) {
+    numeral v;
+    unsigned sz = get_bv_size(e);
+    if (m_util.is_numeral(e, v)) {
+        while (v.is_pos()) {
+            SASSERT(sz > 0);
+            --sz;
+            v = div(v, numeral(2));
+        }
+        return sz;
+    }
+    else if (m_util.is_concat(e)) {
+        app* a = to_app(e);
+        unsigned sz1 = get_bv_size(a->get_arg(0));
+        unsigned nb1 = num_leading_zero_bits(a->get_arg(0));
+        if (sz1 == nb1) {
+            nb1 += num_leading_zero_bits(a->get_arg(1));
+        }
+        return nb1;
+    }
+    return 0;
+}
+
+
+
+
 br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_ref & result) {
     expr_ref_buffer new_args(m());
     numeral v1;
diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h
index 45bd6c264..205ebbf8e 100644
--- a/src/ast/rewriter/bv_rewriter.h
+++ b/src/ast/rewriter/bv_rewriter.h
@@ -42,6 +42,7 @@ protected:
     decl_kind mul_decl_kind() const { return OP_BMUL; }
     bool use_power() const { return false; }
     decl_kind power_decl_kind() const { UNREACHABLE(); return static_cast<decl_kind>(UINT_MAX); }
+
 public:
     bv_rewriter_core(ast_manager & m):m_util(m) {}
 };
@@ -108,6 +109,10 @@ class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
     br_status mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result);
     bool is_minus_one_core(expr * arg) const;
     bool is_x_minus_one(expr * arg, expr * & x);
+    bool is_add_no_overflow(expr* e);
+    bool is_mul_no_overflow(expr* e);
+    unsigned num_leading_zero_bits(expr* e);
+
     br_status mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
     br_status mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);
     br_status mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result);

From f20e95184ee6532d7a538886c46cf6ad02ee9733 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Mon, 28 Aug 2017 13:29:51 -0700
Subject: [PATCH 37/74] remove old_simplify dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/CMakeLists.txt                           |   3 -
 src/ast/simplifier/CMakeLists.txt            |   9 +-
 src/ast/simplifier/array_simplifier_params.h |   6 +-
 src/ast/simplifier/bv_simplifier_params.cpp  |   2 +-
 src/ast/simplifier/maximise_ac_sharing.cpp   | 192 -------------------
 src/ast/simplifier/maximise_ac_sharing.h     | 124 ------------
 src/smt/params/CMakeLists.txt                |   1 -
 src/smt/params/preprocessor_params.cpp       |   4 -
 src/smt/params/preprocessor_params.h         |   6 +-
 src/smt/params/theory_arith_params.cpp       |   2 +
 src/smt/params/theory_arith_params.h         |   4 +
 src/smt/params/theory_array_params.h         |   8 +-
 src/smt/params/theory_bv_params.cpp          |   6 +-
 src/smt/params/theory_bv_params.h            |   2 +
 14 files changed, 26 insertions(+), 343 deletions(-)
 delete mode 100644 src/ast/simplifier/maximise_ac_sharing.cpp
 delete mode 100644 src/ast/simplifier/maximise_ac_sharing.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3df33aac9..277335ce9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -68,9 +68,6 @@ add_subdirectory(cmd_context)
 add_subdirectory(cmd_context/extra_cmds)
 add_subdirectory(parsers/smt2)
 add_subdirectory(ast/proof_checker)
-## Simplifier module will be deleted in the future.
-## It has been replaced with rewriter component.
-add_subdirectory(ast/simplifier)
 add_subdirectory(ast/fpa)
 add_subdirectory(ast/macros)
 add_subdirectory(ast/pattern)
diff --git a/src/ast/simplifier/CMakeLists.txt b/src/ast/simplifier/CMakeLists.txt
index 649c0486e..37d96b1a5 100644
--- a/src/ast/simplifier/CMakeLists.txt
+++ b/src/ast/simplifier/CMakeLists.txt
@@ -2,11 +2,11 @@ z3_add_component(simplifier
   SOURCES
      arith_simplifier_params.cpp
 #    arith_simplifier_plugin.cpp
-     array_simplifier_params.cpp
+#    array_simplifier_params.cpp
 #    array_simplifier_plugin.cpp
 #    basic_simplifier_plugin.cpp
 #    bv_elim.cpp
-     bv_simplifier_params.cpp
+#    bv_simplifier_params.cpp
 #    bv_simplifier_plugin.cpp
 #    datatype_simplifier_plugin.cpp
 #    elim_bounds.cpp
@@ -18,8 +18,5 @@ z3_add_component(simplifier
 #    simplifier_plugin.cpp
   COMPONENT_DEPENDENCIES
     rewriter
-  PYG_FILES
-    arith_simplifier_params_helper.pyg
-    array_simplifier_params_helper.pyg
-    bv_simplifier_params_helper.pyg
+
 )
diff --git a/src/ast/simplifier/array_simplifier_params.h b/src/ast/simplifier/array_simplifier_params.h
index 559e3de54..07ead5fd6 100644
--- a/src/ast/simplifier/array_simplifier_params.h
+++ b/src/ast/simplifier/array_simplifier_params.h
@@ -21,11 +21,9 @@ Revision History:
 
 #include "util/params.h"
 
-struct array_simplifier_params {
-    bool            m_array_canonize_simplify;
-    bool            m_array_simplify; // temporary hack for disabling array simplifier plugin.
+struct array_simplifier_params1 {
 
-    array_simplifier_params(params_ref const & p = params_ref()) {
+    array_simplifier_params1(params_ref const & p = params_ref()) {
         updt_params(p);
     }
 
diff --git a/src/ast/simplifier/bv_simplifier_params.cpp b/src/ast/simplifier/bv_simplifier_params.cpp
index 4c6b4a5fa..07f854179 100644
--- a/src/ast/simplifier/bv_simplifier_params.cpp
+++ b/src/ast/simplifier/bv_simplifier_params.cpp
@@ -33,4 +33,4 @@ void bv_simplifier_params::updt_params(params_ref const & _p) {
 void bv_simplifier_params::display(std::ostream & out) const {
     DISPLAY_PARAM(m_hi_div0);
     DISPLAY_PARAM(m_bv2int_distribute);
-}
\ No newline at end of file
+}
diff --git a/src/ast/simplifier/maximise_ac_sharing.cpp b/src/ast/simplifier/maximise_ac_sharing.cpp
deleted file mode 100644
index 93e5a43f0..000000000
--- a/src/ast/simplifier/maximise_ac_sharing.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    maximise_ac_sharing.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-10-22.
-
-Revision History:
-
---*/
-
-#include "ast/simplifier/maximise_ac_sharing.h"
-#include "ast/ast_pp.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-
-maximise_ac_sharing::ac_plugin::ac_plugin(symbol const & fname, ast_manager & m, maximise_ac_sharing & owner):
-    simplifier_plugin(fname, m),
-    m_owner(owner) {
-}
-
-void maximise_ac_sharing::ac_plugin::register_kind(decl_kind k) {
-    m_kinds.push_back(k);
-}
-
-maximise_ac_sharing::ac_plugin::~ac_plugin() {
-}
-
-bool maximise_ac_sharing::ac_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
-    decl_kind k = f->get_kind();
-    if (!f->is_associative())
-        return false;
-    if (num_args <= 2)
-        return false;
-    if (std::find(m_kinds.begin(), m_kinds.end(), k) == m_kinds.end())
-        return false;
-    ptr_buffer<expr, 128> _args;
-    expr * numeral = 0;
-    if (m_owner.is_numeral(args[0])) {
-        numeral = args[0];
-        for (unsigned i = 1; i < num_args; i++) 
-            _args.push_back(args[i]);
-        num_args--;
-    }
-    else {
-        _args.append(num_args, args);
-    }
-
-    TRACE("ac_sharing_detail", tout << "before-reuse: num_args: " << num_args << "\n";);
-
-#define MAX_NUM_ARGS_FOR_OPT 128
-
-    // Try to reuse already created circuits.
-    TRACE("ac_sharing_detail", tout << "args: "; for (unsigned i = 0; i < num_args; i++) tout << mk_pp(_args[i], m_manager) << "\n";);
-    try_to_reuse:
-    if (num_args > 1 && num_args < MAX_NUM_ARGS_FOR_OPT) {
-        for (unsigned i = 0; i < num_args - 1; i++) {
-            for (unsigned j = i + 1; j < num_args; j++) {
-                if (m_owner.contains(f, _args[i], _args[j])) {
-                    TRACE("ac_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";);
-                    _args[i] = m_manager.mk_app(f, _args[i], _args[j]);
-                    SASSERT(num_args > 1);
-                    for (unsigned w = j; w < num_args - 1; w++) {
-                        _args[w] = _args[w+1];
-                    }
-                    num_args--;
-                    goto try_to_reuse;
-                }
-            }
-        }
-    }
-
-    
-    // Create "tree-like circuit"
-    while (true) {
-        TRACE("ac_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";);
-        unsigned j  = 0;
-        for (unsigned i = 0; i < num_args; i += 2, j++) {
-            if (i == num_args - 1) {
-                _args[j] = _args[i];
-            }
-            else {
-                m_owner.insert(f, _args[i], _args[i+1]);
-                _args[j] = m_manager.mk_app(f, _args[i], _args[i+1]);
-            }
-        }
-        num_args = j;
-        if (num_args == 1) {
-            if (numeral == 0) { 
-                result = _args[0];
-            }
-            else {
-                result = m_manager.mk_app(f, numeral, _args[0]);
-            }
-            TRACE("ac_sharing_detail", tout << "result: " << mk_pp(result, m_manager) << "\n";);
-            return true;
-        }
-    }
-
-    UNREACHABLE();
-    return false;
-}
-
-bool maximise_ac_sharing::contains(func_decl * f, expr * arg1, expr * arg2) {
-    entry e(f, arg1, arg2);
-    return m_cache.contains(&e);
-}
-
-void maximise_ac_sharing::insert(func_decl * f, expr * arg1, expr * arg2) {
-    entry * e = new (m_region) entry(f, arg1, arg2);
-    m_entries.push_back(e);
-    m_cache.insert(e);
-    m_manager.inc_ref(arg1);
-    m_manager.inc_ref(arg2);
-}
-    
-maximise_ac_sharing::maximise_ac_sharing(ast_manager & m):
-    m_manager(m),
-    m_simplifier(m),
-    m_init(false) {
-    basic_simplifier_plugin* basic_simp = alloc(basic_simplifier_plugin,m);
-    m_simplifier.register_plugin(basic_simp);
-}
-
-maximise_ac_sharing::~maximise_ac_sharing() {
-    restore_entries(0);
-}
-
-void maximise_ac_sharing::operator()(expr * s, expr_ref & r, proof_ref & p) {
-    init();
-    m_simplifier.operator()(s, r, p);
-}
-
-void maximise_ac_sharing::push_scope() {
-    init();
-    m_scopes.push_back(m_entries.size());
-    m_region.push_scope();
-}
-
-void maximise_ac_sharing::pop_scope(unsigned num_scopes) {
-    SASSERT(num_scopes <= m_scopes.size());
-    unsigned new_lvl    = m_scopes.size() - num_scopes;
-    unsigned old_lim    = m_scopes[new_lvl];
-    restore_entries(old_lim);
-    m_region.pop_scope(num_scopes);
-    m_scopes.shrink(new_lvl);
-}
-
-void maximise_ac_sharing::restore_entries(unsigned old_lim) {
-    unsigned i = m_entries.size();
-    while (i != old_lim) {
-        --i;
-        entry * e = m_entries[i];
-        m_manager.dec_ref(e->m_arg1);
-        m_manager.dec_ref(e->m_arg2);
-    }
-    m_entries.shrink(old_lim);
-}
-
-void maximise_ac_sharing::reset() {
-    restore_entries(0);
-    m_entries.reset();
-    m_cache.reset();
-    m_simplifier.reset();
-    m_region.reset();
-    m_scopes.reset();
-}
-
-void maximise_bv_sharing::init_core() {
-    maximise_ac_sharing::ac_plugin * p = alloc(maximise_ac_sharing::ac_plugin, symbol("bv"), get_manager(), *this);
-    p->register_kind(OP_BADD);
-    p->register_kind(OP_BMUL);
-    p->register_kind(OP_BOR);
-    p->register_kind(OP_BAND);
-    register_plugin(p);
-}
-
-bool maximise_bv_sharing::is_numeral(expr * n) const {
-    return m_util.is_numeral(n);
-}
-
-maximise_bv_sharing::maximise_bv_sharing(ast_manager & m):
-    maximise_ac_sharing(m),
-    m_util(m) {
-}
diff --git a/src/ast/simplifier/maximise_ac_sharing.h b/src/ast/simplifier/maximise_ac_sharing.h
deleted file mode 100644
index cf488e20d..000000000
--- a/src/ast/simplifier/maximise_ac_sharing.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    maximise_ac_sharing.h
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-10-22.
-
-Revision History:
-
---*/
-#ifndef MAXIMISE_AC_SHARING_H_
-#define MAXIMISE_AC_SHARING_H_
-
-#include "ast/simplifier/simplifier.h"
-#include "util/hashtable.h"
-#include "ast/bv_decl_plugin.h"
-#include "util/region.h"
-
-/**
-   \brief Functor used to maximise the amount of shared terms in an expression.
-   The idea is to rewrite AC terms to maximise sharing.
-   Example:
-
-   (f (bvadd a (bvadd b c)) (bvadd a (bvadd b d)))
-
-   is rewritten to:
-
-   (f (bvadd (bvadd a b) c) (bvadd (bvadd a b) d))
-
-   \warning This class uses an opportunistic heuristic to maximise sharing.
-   There is no guarantee that the optimal expression will be produced.
-*/
-class maximise_ac_sharing {
-    
-    struct entry {
-        func_decl * m_decl;
-        expr *      m_arg1;
-        expr *      m_arg2;
-
-        entry(func_decl * d = 0, expr * arg1 = 0, expr * arg2 = 0):m_decl(d), m_arg1(arg1), m_arg2(arg2) {
-            SASSERT((d == 0 && arg1 == 0 && arg2 == 0) || (d != 0 && arg1 != 0 && arg2 != 0));
-            if (arg1->get_id() > arg2->get_id())
-                std::swap(m_arg1, m_arg2);
-        }
-
-        unsigned hash() const {
-            unsigned a = m_decl->get_id();
-            unsigned b = m_arg1->get_id();
-            unsigned c = m_arg2->get_id();
-            mix(a,b,c);
-            return c;
-        }
-
-        bool operator==(entry const & e) const {
-            return m_decl == e.m_decl && m_arg1 == e.m_arg1 && m_arg2 == e.m_arg2;
-        }
-    };
-
-    typedef ptr_hashtable<entry, obj_ptr_hash<entry>, deref_eq<entry> > cache;
-
-protected:
-    class ac_plugin : public simplifier_plugin {
-        maximise_ac_sharing & m_owner;
-        svector<decl_kind>    m_kinds; //!< kinds to be processed
-    public:
-        ac_plugin(symbol const & fname, ast_manager & m, maximise_ac_sharing & owner);
-        void register_kind(decl_kind k);
-        virtual ~ac_plugin();
-        virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
-    };
-
-    friend class ac_plugin;
-
-private:
-    ast_manager &     m_manager;
-    simplifier        m_simplifier;
-    bool              m_init;
-    region            m_region;
-    cache             m_cache;
-    ptr_vector<entry> m_entries;
-    unsigned_vector   m_scopes;
-
-    bool contains(func_decl * f, expr * arg1, expr * arg2);
-    void insert(func_decl * f, expr * arg1, expr * arg2);
-    void restore_entries(unsigned old_lim);
-    void init() {
-        if (!m_init) {
-            init_core();
-            m_init = true;
-        }
-    }
-protected:
-    virtual void init_core() = 0; 
-    virtual bool is_numeral(expr * n) const = 0;
-    void register_plugin(ac_plugin * p) { m_simplifier.register_plugin(p); }
-public:
-    maximise_ac_sharing(ast_manager & m);
-    virtual ~maximise_ac_sharing();
-    void operator()(expr * s, expr_ref & r, proof_ref & p);
-    void push_scope();
-    void pop_scope(unsigned num_scopes);
-    void reset();
-    ast_manager & get_manager() { return m_manager; }
-};
-
-class maximise_bv_sharing : public maximise_ac_sharing {
-    bv_util m_util;
-protected:
-    virtual void init_core();
-    virtual bool is_numeral(expr * n) const;
-public:
-    maximise_bv_sharing(ast_manager & m);
-};
-
-#endif /* MAXIMISE_AC_SHARING_H_ */
-
diff --git a/src/smt/params/CMakeLists.txt b/src/smt/params/CMakeLists.txt
index 500423dcc..c965f0a62 100644
--- a/src/smt/params/CMakeLists.txt
+++ b/src/smt/params/CMakeLists.txt
@@ -13,7 +13,6 @@ z3_add_component(smt_params
     ast
     bit_blaster
     pattern
-    simplifier
   PYG_FILES
     smt_params_helper.pyg
 )
diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp
index afca0196a..93ea794fa 100644
--- a/src/smt/params/preprocessor_params.cpp
+++ b/src/smt/params/preprocessor_params.cpp
@@ -30,8 +30,6 @@ void preprocessor_params::updt_local_params(params_ref const & _p) {
 
 void preprocessor_params::updt_params(params_ref const & p) {
     pattern_inference_params::updt_params(p);
-    bv_simplifier_params::updt_params(p);
-    arith_simplifier_params::updt_params(p);
     updt_local_params(p);
 }
 
@@ -40,8 +38,6 @@ void preprocessor_params::updt_params(params_ref const & p) {
 void preprocessor_params::display(std::ostream & out) const {
     pattern_inference_params::display(out);
     bit_blaster_params::display(out);
-    bv_simplifier_params::display(out);
-    arith_simplifier_params::display(out);
 
     DISPLAY_PARAM(m_lift_ite);
     DISPLAY_PARAM(m_ng_lift_ite);
diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h
index 6f763c0e1..dc16d6244 100644
--- a/src/smt/params/preprocessor_params.h
+++ b/src/smt/params/preprocessor_params.h
@@ -21,8 +21,6 @@ Revision History:
 
 #include "ast/pattern/pattern_inference_params.h"
 #include "ast/rewriter/bit_blaster/bit_blaster_params.h"
-#include "ast/simplifier/bv_simplifier_params.h"
-#include "ast/simplifier/arith_simplifier_params.h"
 
 enum lift_ite_kind {
     LI_NONE,
@@ -31,9 +29,7 @@ enum lift_ite_kind {
 };
 
 struct preprocessor_params : public pattern_inference_params, 
-                             public bit_blaster_params,
-                             public bv_simplifier_params,
-                             public arith_simplifier_params {
+                             public bit_blaster_params {
     lift_ite_kind   m_lift_ite;
     lift_ite_kind   m_ng_lift_ite;  // lift ite for non ground terms
     bool            m_pull_cheap_ite_trees;
diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp
index 2b5a14493..ab80f0c67 100644
--- a/src/smt/params/theory_arith_params.cpp
+++ b/src/smt/params/theory_arith_params.cpp
@@ -42,6 +42,8 @@ void theory_arith_params::updt_params(params_ref const & _p) {
 #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl;
 
 void theory_arith_params::display(std::ostream & out) const {
+    DISPLAY_PARAM(m_arith_expand_eqs);
+    DISPLAY_PARAM(m_arith_process_all_eqs);
     DISPLAY_PARAM(m_arith_mode);
     DISPLAY_PARAM(m_arith_auto_config_simplex); //!< force simplex solver in auto_config
     DISPLAY_PARAM(m_arith_blands_rule_threshold);
diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h
index d73a67985..89c4fe46c 100644
--- a/src/smt/params/theory_arith_params.h
+++ b/src/smt/params/theory_arith_params.h
@@ -49,6 +49,8 @@ enum arith_pivot_strategy {
 };
 
 struct theory_arith_params {
+    bool                    m_arith_expand_eqs;
+    bool                    m_arith_process_all_eqs;
     arith_solver_id         m_arith_mode;
     bool                    m_arith_auto_config_simplex; //!< force simplex solver in auto_config
     unsigned                m_arith_blands_rule_threshold;
@@ -108,6 +110,8 @@ struct theory_arith_params {
 
 
     theory_arith_params(params_ref const & p = params_ref()):
+        m_arith_expand_eqs(false),
+        m_arith_process_all_eqs(false),
         m_arith_mode(AS_ARITH),
         m_arith_auto_config_simplex(false),
         m_arith_blands_rule_threshold(1000),
diff --git a/src/smt/params/theory_array_params.h b/src/smt/params/theory_array_params.h
index af3fdebeb..edda4d7cf 100644
--- a/src/smt/params/theory_array_params.h
+++ b/src/smt/params/theory_array_params.h
@@ -19,7 +19,7 @@ Revision History:
 #ifndef THEORY_ARRAY_PARAMS_H_
 #define THEORY_ARRAY_PARAMS_H_
 
-#include "ast/simplifier/array_simplifier_params.h"
+#include "util/params.h"
 
 enum array_solver_id {
     AR_NO_ARRAY,
@@ -28,7 +28,9 @@ enum array_solver_id {
     AR_FULL
 };
 
-struct theory_array_params : public array_simplifier_params {
+struct theory_array_params {
+    bool            m_array_canonize_simplify;
+    bool            m_array_simplify; // temporary hack for disabling array simplifier plugin.
     array_solver_id m_array_mode;
     bool            m_array_weak;
     bool            m_array_extensional;
@@ -40,6 +42,8 @@ struct theory_array_params : public array_simplifier_params {
     unsigned        m_array_lazy_ieq_delay;
 
     theory_array_params():
+        m_array_canonize_simplify(false),
+        m_array_simplify(true),
         m_array_mode(AR_FULL),
         m_array_weak(false),
         m_array_extensional(true),
diff --git a/src/smt/params/theory_bv_params.cpp b/src/smt/params/theory_bv_params.cpp
index 4b4808a1f..156fda592 100644
--- a/src/smt/params/theory_bv_params.cpp
+++ b/src/smt/params/theory_bv_params.cpp
@@ -18,9 +18,12 @@ Revision History:
 --*/
 #include "smt/params/theory_bv_params.h"
 #include "smt/params/smt_params_helper.hpp"
+#include "ast/rewriter/bv_rewriter_params.hpp"
 
 void theory_bv_params::updt_params(params_ref const & _p) {
     smt_params_helper p(_p);
+    bv_rewriter_params rp(_p);
+    m_hi_div0 = rp.hi_div0();
     m_bv_reflect = p.bv_reflect();
     m_bv_enable_int2bv2int = p.bv_enable_int2bv(); 
 }
@@ -29,9 +32,10 @@ void theory_bv_params::updt_params(params_ref const & _p) {
 
 void theory_bv_params::display(std::ostream & out) const {
     DISPLAY_PARAM(m_bv_mode);
+    DISPLAY_PARAM(m_hi_div0);
     DISPLAY_PARAM(m_bv_reflect);
     DISPLAY_PARAM(m_bv_lazy_le);
     DISPLAY_PARAM(m_bv_cc);
     DISPLAY_PARAM(m_bv_blast_max_size);
     DISPLAY_PARAM(m_bv_enable_int2bv2int);
-}
\ No newline at end of file
+}
diff --git a/src/smt/params/theory_bv_params.h b/src/smt/params/theory_bv_params.h
index 52aaa4c9c..e0bff3f17 100644
--- a/src/smt/params/theory_bv_params.h
+++ b/src/smt/params/theory_bv_params.h
@@ -28,6 +28,7 @@ enum bv_solver_id {
 
 struct theory_bv_params {
     bv_solver_id m_bv_mode;
+    bool  m_hi_div0; //!< if true, uses the hardware interpretation for div0, mod0, ... if false, div0, mod0, ... are considered uninterpreted.
     bool         m_bv_reflect;
     bool         m_bv_lazy_le;
     bool         m_bv_cc;
@@ -35,6 +36,7 @@ struct theory_bv_params {
     bool         m_bv_enable_int2bv2int;
     theory_bv_params(params_ref const & p = params_ref()):
         m_bv_mode(BS_BLASTER),
+        m_hi_div0(false),
         m_bv_reflect(true),
         m_bv_lazy_le(false),
         m_bv_cc(false),

From 597f77cd7766a439d64cff22cc8f1950d00eb450 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Mon, 28 Aug 2017 20:03:31 -0700
Subject: [PATCH 38/74] initial sketch for dominator based simplifiation

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 scripts/mk_project.py                   |  15 +-
 src/tactic/core/dom_simplify_tactic.cpp | 348 ++++++++++++++++++++++++
 2 files changed, 354 insertions(+), 9 deletions(-)
 create mode 100644 src/tactic/core/dom_simplify_tactic.cpp

diff --git a/scripts/mk_project.py b/scripts/mk_project.py
index 64dd93faa..923b948a6 100644
--- a/scripts/mk_project.py
+++ b/scripts/mk_project.py
@@ -23,10 +23,7 @@ def init_project_def():
     add_lib('subpaving', ['interval'], 'math/subpaving')
     add_lib('ast', ['util', 'polynomial'])
     add_lib('rewriter', ['ast', 'polynomial', 'automata'], 'ast/rewriter')
-    # Simplifier module will be deleted in the future.
-    # It has been replaced with rewriter module.
-    add_lib('simplifier', ['rewriter'], 'ast/simplifier')
-    add_lib('macros', ['simplifier'], 'ast/macros')
+    add_lib('macros', ['rewriter'], 'ast/macros')
     add_lib('normal_forms', ['rewriter'], 'ast/normal_forms')
     add_lib('model', ['rewriter'])
     add_lib('tactic', ['ast', 'model'])
@@ -47,11 +44,11 @@ def init_project_def():
     add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'arith_tactics'], 'cmd_context/extra_cmds')
     add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2')
     add_lib('proof_checker', ['rewriter'], 'ast/proof_checker')
-    add_lib('fpa', ['ast', 'util', 'simplifier', 'model'], 'ast/fpa')
-    add_lib('pattern', ['normal_forms', 'smt2parser', 'simplifier'], 'ast/pattern')
-    add_lib('bit_blaster', ['rewriter', 'simplifier'], 'ast/rewriter/bit_blaster')
-    add_lib('smt_params', ['ast', 'simplifier', 'pattern', 'bit_blaster'], 'smt/params')
-    add_lib('proto_model', ['model', 'simplifier', 'smt_params'], 'smt/proto_model')
+    add_lib('fpa', ['ast', 'util', 'rewriter', 'model'], 'ast/fpa')
+    add_lib('pattern', ['normal_forms', 'smt2parser', 'rewriter'], 'ast/pattern')
+    add_lib('bit_blaster', ['rewriter', 'rewriter'], 'ast/rewriter/bit_blaster')
+    add_lib('smt_params', ['ast', 'rewriter', 'pattern', 'bit_blaster'], 'smt/params')
+    add_lib('proto_model', ['model', 'rewriter', 'smt_params'], 'smt/proto_model')
     add_lib('smt', ['bit_blaster', 'macros', 'normal_forms', 'cmd_context', 'proto_model',
                     'substitution', 'grobner', 'euclid', 'simplex', 'proof_checker', 'pattern', 'parser_util', 'fpa', 'lp'])
     add_lib('bv_tactics', ['tactic', 'bit_blaster', 'core_tactics'], 'tactic/bv')
diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp
new file mode 100644
index 000000000..b4c0b6d82
--- /dev/null
+++ b/src/tactic/core/dom_simplify_tactic.cpp
@@ -0,0 +1,348 @@
+/*++
+Copyright (c) 2017 Microsoft Corporation
+
+Module Name:
+
+    dom_simplify_tactic.cpp
+
+Abstract:
+
+    Dominator-based context simplifer.
+
+Author:
+
+    Nikolaj and Nuno 
+
+Notes:
+
+--*/
+
+#if 0
+
+#include "ast/ast.h"
+
+class expr_dominators {
+public:
+    typedef obj_map<expr, ptr_vector<expr>> tree_t;
+private:
+    ast_manager&          m;
+    expr_ref              m_root;
+    obj_map<unsigned>     m_expr2post; // reverse post-order number
+    ptr_vector<expr>      m_post2expr;
+    tree_t                m_parents;
+    obj_map<expr, expr*>  m_doms;
+    tree_t                m_tree;
+
+    void add_edge(tree_t& tree, expr * src, expr* dst) {        
+        tree.insert_if_not_there(src, ptr_vector<expr>()).push_back(dst);        
+    }
+
+    /**
+       \brief compute a post-order traversal for e.
+       Also populate the set of parents
+     */
+    void compute_post_order() {
+        unsigned post_num = 0;
+        SASSERT(m_post2expr.empty());
+        SASSERT(m_expr2post.empty());
+        ast_mark mark;
+        ptr_vector<expr> todo;
+        todo.push_back(m_root);
+        while (!todo.empty()) {
+            expr* e = todo.back();
+            if (is_marked(e)) {
+                todo.pop_back();
+                continue;
+            }
+            if (is_app(e)) {
+                app* a = to_app(e);
+                bool done = true;
+                for (expr* arg : *a) {
+                    if (!is_marked(arg)) {
+                        todo.push_back(arg);
+                        done = false;
+                    }
+                }
+                if (done) {
+                    mark.mark(e);
+                    m_expr2post.insert(e, post_num++);
+                    m_post2expr.push_back(e);
+                    todo.pop_back();
+                    for (expr* arg : *a) {
+                        add_edge(m_parents, a, arg);
+                    }
+                }
+            }
+            else {
+                todo.pop_back();
+            }
+        }
+    }
+
+    expr* intersect(expr* x, expr * y) {
+        unsigned n1 = m_expr2post[x];
+        unsigned n2 = m_expr2post[y];
+        while (n1 != n2) {
+            if (n1 < n2)
+                n1 = m_doms[n1];
+            else if (n1 > n2)
+                n2 = m_doms[n2];
+        }
+        return n1;
+    }
+
+    void compute_dominators() {
+        expr * e = m_root;
+        SASSERT(m_doms.empty());
+        m_doms.insert(e, e);
+        bool change = true;
+        while (change) {
+            change = false;
+            SASSERT(m_post2expr.back() == e);
+            for (unsigned i = 0; i < m_post2expr.size() - 1; ++i) {
+                expr * child = m_post2expr[i];
+                ptr_vector<expr> const& p = m_parents[child];
+                SASSERT(!p.empty());
+                expr * new_idom = p[0], * idom2 = 0;
+                for (unsigned j = 1; j < p.size(); ++j) {
+                    if (m_doms.find(p[j], idom2)) {
+                        new_idom = intersect(new_idom, idom2);
+                    }
+                }
+                if (!m_doms.find(child, idom2) || idom2 != new_idom) {
+                    m_doms.insert(child, new_idom);
+                }
+            }
+        }
+    }
+
+    void extract_tree() {
+        for (auto const& kv : m_doms) {
+            expr * child = kv.m_key;
+            for (expr * parent : kv.m_value) {
+                add_edge(m_tree, parent, child);
+            }
+        }
+    }    
+
+    void reset() {
+        m_expr2post.reset();
+        m_post2expr.reset();
+        m_parents.reset();
+        m_doms.reset();
+        m_tree.reset();
+        m_root.reset();
+    }
+
+
+public:
+    expr_dominators(ast_manager& m): m(m), m_root(m) {}
+
+    void compile(expr * e) {
+        reset();
+        m_root = e;
+        compute_post_order();
+        compute_dominators();
+        extract_tree();
+    }
+
+    void compile(unsigned sz, expr * const* es) {
+        expr_ref e(m.mk_and(sz, es), m);
+        compile(e);
+    }
+
+    tree_t const& get_tree() { return m_tree; }
+
+};
+
+// goes to header file:
+
+class dom_simplify_tactic : public tactic {
+public:
+    class simplifier {
+    public:
+        virtual ~simplifier() {}
+        /**
+           \brief assert_expr performs an implicit push
+         */
+        virtual bool assert_expr(expr * t, bool sign) = 0;
+
+        /**
+           \brief apply simplification.
+        */
+        virtual void operator()(expr_ref& r) = 0;
+
+        /**
+           \brief pop scopes accumulated from assertions.
+         */
+        virtual void pop(unsigned num_scopes) = 0;
+    };
+private:
+    simplifier&          m_simplifier;
+    params_ref           m_params;
+    expr_ref_vector      m_trail;
+    obj_map<expr, expr*> m_result;
+    expr_dominators      m_dominators;
+
+    expr_ref simplify(expr* t);
+    expr_ref simplify_ite(app * ite);
+    expr_ref simplify_and(app * ite) { return simplify_and_or(true, ite); }
+    expr_ref simplify_or(app * ite) { return simplify_and_or(false, ite); }
+    expr_ref simplify_and_or(bool is_and app * ite);
+
+    expr_ref get_cache(expr* t) { if (!m_result.find(r, r)) r = t; return expr_ref(r, m); }
+    void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); }
+
+    void simplify_goal(goal_ref& g);
+
+public:
+    dom_simplify_tactic(ast_manager & m, simplifier& s, params_ref const & p = params_ref());
+
+    virtual tactic * translate(ast_manager & m);
+
+    virtual ~dom_simplify_tactic();
+
+    virtual void updt_params(params_ref const & p);
+    static  void get_param_descrs(param_descrs & r);
+    virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
+    
+    virtual void operator()(goal_ref const & in, 
+                            goal_ref_buffer & result, 
+                            model_converter_ref & mc, 
+                            proof_converter_ref & pc,
+                            expr_dependency_ref & core);
+
+    virtual void cleanup();
+};
+
+// implementation:
+
+expr_ref dom_simplifier_tactic::simplify_ite(app * ite) {
+    expr_ref r(m);
+    expr * c = 0, * t = 0, * e = 0;
+    VERIFY(m.is_ite(ite, c, t, e));
+    unsigned old_lvl = scope_level();
+    expr_ref new_c = simplify(c);
+    if (m.is_true(new_c)) {
+        r = simplify(t);
+    }
+    else if (m.is_false(new_c) || !assert_expr(new_c, false)) {
+        r = simplify(e);
+    }
+    else {
+        expr_ref t = simplify(t);
+        pop(scope_level() - old_lvl);
+        if (!assert_expr(new_c, true)) {
+            return new_t;
+        }
+        expr_ref new_e = simplify(e);
+        pop(scope_level() - old_lvl);
+        if (c == new_c && t == new_t && e == new_e) {
+            r = ite;
+        }
+        else if (new_t == new_e) {
+            r = new_t;
+        }
+        else {
+            TRACE("tactic", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";);
+            r = m.mk_ite(new_c, new_t, new_c);
+        }        
+    }    
+    return r;
+}
+
+expr_ref dom_simplifier_tactic::simplify(expr * e0) {
+    expr_ref r(m);
+    expr* e = 0;
+    if (!m_result.find(e0, e)) {
+        e = e0;
+    }
+
+    if (m.is_ite(e)) {
+        r = simplify_ite(to_app(e));
+    }
+    else if (m.is_and(e)) {
+        r = simplify_and(to_app(e));
+    }
+    else if (m.is_or(e)) {
+        r = simplify_or(to_app(e));
+    }
+    else {
+        tree_t const& t = m_dominators.get_tree();
+        if (t.contains(e)) {
+            ptr_vector<expr> const& children = t[e];
+            for (expr * child : children) {
+                simplify(child);
+            }
+        }
+        if (is_app(e)) {
+            m_args.reset();
+            for (expr* arg : *to_app(e)) {
+                m_args.push_back(get_cached(arg));
+            }
+            r = m.mk_app(to_app(e)->get_decl(), m_args.size(), m_args.c_ptr());
+        }
+        else {
+            r = e;
+        }
+    }
+    m_simplifier(r);
+    cache(e0, r);
+    return r;
+}
+
+expr_ref dom_simplifier_tactic::simplify_or_and(bool is_and, app * e) {
+    expr_ref r(m);
+    unsigned old_lvl = scope_level();
+    m_args.reset();
+    for (expr * arg : *e) {
+        r = simplify(arg);
+        if (!assert_expr(r, is_and)) {
+            r = is_and ? m.mk_false() : m.mk_true();
+        }
+        m_args.push_back(r);
+    }
+    pop(scope_level() - old_lvl);
+    m_args.reverse();
+    m_args2.reset();
+    for (expr * arg : m_args) {
+        r = simplify(arg);
+        if (!assert_expr(r, is_and)) {
+            r = is_and ? m.mk_false() : m.mk_true();
+        }
+        m_args2.push_back(r);
+    }
+    pop(scope_level() - old_lvl);
+    m_args2.reverse();
+    r = is_and ? mk_and(m_args2) : mk_or(m_args2);
+    return r;
+}
+
+void dom_simplifier_tactic::simplify_goal(goal& g) {
+    expr_ref_vector args(m);
+    expr_ref fml(m);
+    for (unsigned i = 0; i < sz; ++i) args.push_back(g.form(i));
+    fml = mk_and(args);
+    expr_ref tmp(fml);
+    // TBD: deal with dependencies.
+    do {
+        m_result.reset();
+        m_trail.reset();
+        m_dominators.compile(fml);
+#if 0
+        for (unsigned i = 0; i < sz; ++i) {
+            r = simplify(g.form(i));
+            // TBD: simplfy goal as a conjuction ?
+            //
+        }
+#endif
+        tmp = fml;
+        fml = simplify(fml);
+    }
+    while (tmp != fml);
+    //g.reset();
+    //g.add(fml);
+}
+
+
+#endif

From 8d8e4cbc513dd7171fd3e1439f38d4bdeb214b63 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Mon, 28 Aug 2017 20:11:46 -0700
Subject: [PATCH 39/74] fix some basic mistakes in dominator code

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/tactic/core/dom_simplify_tactic.cpp | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp
index b4c0b6d82..3b444ef1a 100644
--- a/src/tactic/core/dom_simplify_tactic.cpp
+++ b/src/tactic/core/dom_simplify_tactic.cpp
@@ -69,7 +69,7 @@ private:
                     m_post2expr.push_back(e);
                     todo.pop_back();
                     for (expr* arg : *a) {
-                        add_edge(m_parents, a, arg);
+                        add_edge(m_parents, arg, a);
                     }
                 }
             }
@@ -83,12 +83,17 @@ private:
         unsigned n1 = m_expr2post[x];
         unsigned n2 = m_expr2post[y];
         while (n1 != n2) {
-            if (n1 < n2)
-                n1 = m_doms[n1];
-            else if (n1 > n2)
-                n2 = m_doms[n2];
+            if (n1 < n2) {
+                x = m_doms[x];
+                n1 = m_expr2post[x];
+            }
+            else if (n1 > n2) {
+                y = m_doms[y];
+                n2 = m_expr2post[y];
+            }
         }
-        return n1;
+        SASSERT(x == y);
+        return x;
     }
 
     void compute_dominators() {
@@ -111,6 +116,7 @@ private:
                 }
                 if (!m_doms.find(child, idom2) || idom2 != new_idom) {
                     m_doms.insert(child, new_idom);
+                    change = true;
                 }
             }
         }
@@ -118,10 +124,7 @@ private:
 
     void extract_tree() {
         for (auto const& kv : m_doms) {
-            expr * child = kv.m_key;
-            for (expr * parent : kv.m_value) {
-                add_edge(m_tree, parent, child);
-            }
+            add_edge(m_tree, kv.m_value, kv.m_key);
         }
     }    
 

From cf87b6d622d8cb71b4dff4a3cbd034dccbaddcbf Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 29 Aug 2017 09:22:27 -0700
Subject: [PATCH 40/74] remove simplifier files

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/macros/macro_util.cpp                 |    2 -
 src/ast/simplifier/CMakeLists.txt             |   22 -
 src/ast/simplifier/README                     |    2 -
 .../simplifier/arith_simplifier_params.cpp    |   33 -
 src/ast/simplifier/arith_simplifier_params.h  |   38 -
 .../arith_simplifier_params_helper.pyg        |    7 -
 .../simplifier/arith_simplifier_plugin.cpp    |  449 ----
 src/ast/simplifier/arith_simplifier_plugin.h  |   96 -
 .../simplifier/array_simplifier_params.cpp    |   26 -
 src/ast/simplifier/array_simplifier_params.h  |   34 -
 .../array_simplifier_params_helper.pyg        |    6 -
 .../simplifier/array_simplifier_plugin.cpp    |  877 -------
 src/ast/simplifier/array_simplifier_plugin.h  |  154 --
 src/ast/simplifier/base_simplifier.h          |   76 -
 .../simplifier/basic_simplifier_plugin.cpp    |  147 --
 src/ast/simplifier/basic_simplifier_plugin.h  |   78 -
 src/ast/simplifier/bv_elim.cpp                |  119 -
 src/ast/simplifier/bv_elim.h                  |   45 -
 src/ast/simplifier/bv_simplifier_params.cpp   |   36 -
 src/ast/simplifier/bv_simplifier_params.h     |   38 -
 .../bv_simplifier_params_helper.pyg           |    4 -
 src/ast/simplifier/bv_simplifier_plugin.cpp   | 2261 -----------------
 src/ast/simplifier/bv_simplifier_plugin.h     |  187 --
 .../simplifier/datatype_simplifier_plugin.cpp |  115 -
 .../simplifier/datatype_simplifier_plugin.h   |   42 -
 src/ast/simplifier/elim_bounds.cpp            |  224 --
 src/ast/simplifier/elim_bounds.h              |   69 -
 src/ast/simplifier/fpa_simplifier_plugin.cpp  |   39 -
 src/ast/simplifier/fpa_simplifier_plugin.h    |   39 -
 src/ast/simplifier/poly_simplifier_plugin.cpp |  835 ------
 src/ast/simplifier/poly_simplifier_plugin.h   |  155 --
 src/ast/simplifier/seq_simplifier_plugin.cpp  |   39 -
 src/ast/simplifier/seq_simplifier_plugin.h    |   39 -
 src/ast/simplifier/simplifier.cpp             |  962 -------
 src/ast/simplifier/simplifier.h               |  232 --
 src/ast/simplifier/simplifier_plugin.cpp      |   46 -
 src/ast/simplifier/simplifier_plugin.h        |   94 -
 src/muz/pdr/pdr_util.cpp                      |   27 +-
 src/muz/rel/dl_bound_relation.h               |    1 -
 src/muz/rel/dl_interval_relation.h            |    5 +-
 src/muz/spacer/spacer_legacy_mbp.cpp          |    3 -
 src/smt/arith_eq_adapter.h                    |    1 -
 src/smt/theory_arith.h                        |    1 -
 src/smt/theory_arith_int.h                    |    7 +-
 44 files changed, 17 insertions(+), 7695 deletions(-)
 delete mode 100644 src/ast/simplifier/CMakeLists.txt
 delete mode 100644 src/ast/simplifier/README
 delete mode 100644 src/ast/simplifier/arith_simplifier_params.cpp
 delete mode 100644 src/ast/simplifier/arith_simplifier_params.h
 delete mode 100644 src/ast/simplifier/arith_simplifier_params_helper.pyg
 delete mode 100644 src/ast/simplifier/arith_simplifier_plugin.cpp
 delete mode 100644 src/ast/simplifier/arith_simplifier_plugin.h
 delete mode 100644 src/ast/simplifier/array_simplifier_params.cpp
 delete mode 100644 src/ast/simplifier/array_simplifier_params.h
 delete mode 100644 src/ast/simplifier/array_simplifier_params_helper.pyg
 delete mode 100644 src/ast/simplifier/array_simplifier_plugin.cpp
 delete mode 100644 src/ast/simplifier/array_simplifier_plugin.h
 delete mode 100644 src/ast/simplifier/base_simplifier.h
 delete mode 100644 src/ast/simplifier/basic_simplifier_plugin.cpp
 delete mode 100644 src/ast/simplifier/basic_simplifier_plugin.h
 delete mode 100644 src/ast/simplifier/bv_elim.cpp
 delete mode 100644 src/ast/simplifier/bv_elim.h
 delete mode 100644 src/ast/simplifier/bv_simplifier_params.cpp
 delete mode 100644 src/ast/simplifier/bv_simplifier_params.h
 delete mode 100644 src/ast/simplifier/bv_simplifier_params_helper.pyg
 delete mode 100644 src/ast/simplifier/bv_simplifier_plugin.cpp
 delete mode 100644 src/ast/simplifier/bv_simplifier_plugin.h
 delete mode 100644 src/ast/simplifier/datatype_simplifier_plugin.cpp
 delete mode 100644 src/ast/simplifier/datatype_simplifier_plugin.h
 delete mode 100644 src/ast/simplifier/elim_bounds.cpp
 delete mode 100644 src/ast/simplifier/elim_bounds.h
 delete mode 100644 src/ast/simplifier/fpa_simplifier_plugin.cpp
 delete mode 100644 src/ast/simplifier/fpa_simplifier_plugin.h
 delete mode 100644 src/ast/simplifier/poly_simplifier_plugin.cpp
 delete mode 100644 src/ast/simplifier/poly_simplifier_plugin.h
 delete mode 100644 src/ast/simplifier/seq_simplifier_plugin.cpp
 delete mode 100644 src/ast/simplifier/seq_simplifier_plugin.h
 delete mode 100644 src/ast/simplifier/simplifier.cpp
 delete mode 100644 src/ast/simplifier/simplifier.h
 delete mode 100644 src/ast/simplifier/simplifier_plugin.cpp
 delete mode 100644 src/ast/simplifier/simplifier_plugin.h

diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp
index 6bc1ee66e..55c5436e4 100644
--- a/src/ast/macros/macro_util.cpp
+++ b/src/ast/macros/macro_util.cpp
@@ -20,8 +20,6 @@ Revision History:
 #include "ast/macros/macro_util.h"
 #include "ast/occurs.h"
 #include "ast/ast_util.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/rewriter/var_subst.h"
 #include "ast/ast_pp.h"
 #include "ast/ast_ll_pp.h"
diff --git a/src/ast/simplifier/CMakeLists.txt b/src/ast/simplifier/CMakeLists.txt
deleted file mode 100644
index 37d96b1a5..000000000
--- a/src/ast/simplifier/CMakeLists.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-z3_add_component(simplifier
-  SOURCES
-     arith_simplifier_params.cpp
-#    arith_simplifier_plugin.cpp
-#    array_simplifier_params.cpp
-#    array_simplifier_plugin.cpp
-#    basic_simplifier_plugin.cpp
-#    bv_elim.cpp
-#    bv_simplifier_params.cpp
-#    bv_simplifier_plugin.cpp
-#    datatype_simplifier_plugin.cpp
-#    elim_bounds.cpp
-#    fpa_simplifier_plugin.cpp
-#    maximise_ac_sharing.cpp
-#    poly_simplifier_plugin.cpp
-#    seq_simplifier_plugin.cpp
-#    simplifier.cpp
-#    simplifier_plugin.cpp
-  COMPONENT_DEPENDENCIES
-    rewriter
-
-)
diff --git a/src/ast/simplifier/README b/src/ast/simplifier/README
deleted file mode 100644
index 4725d9de9..000000000
--- a/src/ast/simplifier/README
+++ /dev/null
@@ -1,2 +0,0 @@
-Simplifier module is now obsolete.
-It is still being used in many places, but we will eventually replace all occurrences with the new rewriter module.
diff --git a/src/ast/simplifier/arith_simplifier_params.cpp b/src/ast/simplifier/arith_simplifier_params.cpp
deleted file mode 100644
index 73bbbaa1a..000000000
--- a/src/ast/simplifier/arith_simplifier_params.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    arith_simplifier_params.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2012-12-02.
-
-Revision History:
-
---*/
-#include "ast/simplifier/arith_simplifier_params.h"
-#include "ast/simplifier/arith_simplifier_params_helper.hpp"
-
-void arith_simplifier_params::updt_params(params_ref const & _p) {
-    arith_simplifier_params_helper p(_p);
-    m_arith_expand_eqs      = p.arith_expand_eqs();
-    m_arith_process_all_eqs = p.arith_process_all_eqs();
-}
-
-#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl;
-
-void arith_simplifier_params::display(std::ostream & out) const {
-    DISPLAY_PARAM(m_arith_expand_eqs);
-    DISPLAY_PARAM(m_arith_process_all_eqs);
-}
\ No newline at end of file
diff --git a/src/ast/simplifier/arith_simplifier_params.h b/src/ast/simplifier/arith_simplifier_params.h
deleted file mode 100644
index 8a4150099..000000000
--- a/src/ast/simplifier/arith_simplifier_params.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    arith_simplifier_params.h
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-05-09.
-
-Revision History:
-
---*/
-#ifndef ARITH_SIMPLIFIER_PARAMS_H_
-#define ARITH_SIMPLIFIER_PARAMS_H_
-
-#include "util/params.h"
-
-struct arith_simplifier_params { 
-    bool    m_arith_expand_eqs;
-    bool    m_arith_process_all_eqs;
-
-    arith_simplifier_params(params_ref const & p = params_ref()) {
-        updt_params(p);
-    }
-
-    void updt_params(params_ref const & _p);
-
-    void display(std::ostream & out) const;
-};
-    
-#endif /* ARITH_SIMPLIFIER_PARAMS_H_ */
-
diff --git a/src/ast/simplifier/arith_simplifier_params_helper.pyg b/src/ast/simplifier/arith_simplifier_params_helper.pyg
deleted file mode 100644
index 49a7cf3d2..000000000
--- a/src/ast/simplifier/arith_simplifier_params_helper.pyg
+++ /dev/null
@@ -1,7 +0,0 @@
-def_module_params(class_name='arith_simplifier_params_helper',
-                  module_name="old_simplify", # Parameters will be in the old_simplify module
-                  description="old simplification (stack) still used in the smt module",
-                  export=True,
-                  params=(
-                          ('arith.expand_eqs', BOOL, False, 'expand equalities into two inequalities'),
-                          ('arith.process_all_eqs', BOOL, False, 'put all equations in the form (= t c), where c is a numeral')))
diff --git a/src/ast/simplifier/arith_simplifier_plugin.cpp b/src/ast/simplifier/arith_simplifier_plugin.cpp
deleted file mode 100644
index bfe72b232..000000000
--- a/src/ast/simplifier/arith_simplifier_plugin.cpp
+++ /dev/null
@@ -1,449 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    arith_simplifier_plugin.cpp
-
-Abstract:
-
-    Simplifier for the arithmetic family.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-08
-    
---*/
-#include "ast/simplifier/arith_simplifier_plugin.h"
-#include "ast/ast_pp.h"
-#include "ast/ast_ll_pp.h"
-#include "ast/ast_smt2_pp.h"
-
-arith_simplifier_plugin::~arith_simplifier_plugin() {
-}
-
-arith_simplifier_plugin::arith_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, arith_simplifier_params & p):
-    poly_simplifier_plugin(symbol("arith"), m, OP_ADD, OP_MUL, OP_UMINUS, OP_SUB, OP_NUM),
-    m_params(p),
-    m_util(m),
-    m_bsimp(b),
-    m_int_zero(m),
-    m_real_zero(m) {
-    m_int_zero  = m_util.mk_numeral(rational(0), true);
-    m_real_zero = m_util.mk_numeral(rational(0), false);
-}
-
-/**
-   \brief Return true if the first monomial of t is negative.
-*/
-bool arith_simplifier_plugin::is_neg_poly(expr * t) const {
-    if (m_util.is_add(t)) {
-        t = to_app(t)->get_arg(0);
-    }
-    if (m_util.is_mul(t)) {
-        t = to_app(t)->get_arg(0);
-        rational r;
-        if (is_numeral(t, r))
-            return r.is_neg();
-    }
-    return false;
-}
-
-void arith_simplifier_plugin::get_monomial_gcd(expr_ref_vector& monomials, numeral& g) {
-    g = numeral::zero();
-    numeral n;
-    for (unsigned  i = 0; !g.is_one() && i < monomials.size(); ++i) {
-        expr* e = monomials[i].get();
-        if (is_numeral(e, n)) {
-            g = gcd(abs(n), g);
-        }
-        else if (is_mul(e) && is_numeral(to_app(e)->get_arg(0), n)) {
-            g = gcd(abs(n), g);        
-        }
-        else {
-            g = numeral::one();
-            return;
-        }
-    }
-    if (g.is_zero()) {
-        g = numeral::one();
-    }
-}
-
-void arith_simplifier_plugin::div_monomial(expr_ref_vector& monomials, numeral const& g) {
-    numeral n;
-    for (unsigned  i = 0; i < monomials.size(); ++i) {
-        expr* e = monomials[i].get();
-        if (is_numeral(e, n)) {
-            SASSERT((n/g).is_int());
-            monomials[i] = mk_numeral(n/g);
-        }
-        else if (is_mul(e) && is_numeral(to_app(e)->get_arg(0), n)) {
-            SASSERT((n/g).is_int());
-            monomials[i] = mk_mul(n/g, to_app(e)->get_arg(1));
-        }
-        else {
-            UNREACHABLE();
-        }
-    }
-}
-
-void arith_simplifier_plugin::gcd_reduce_monomial(expr_ref_vector& monomials, numeral& k) {
-    numeral g, n;
-
-    get_monomial_gcd(monomials, g);
-    g = gcd(abs(k), g);
-
-    if (g.is_one()) {
-        return;
-    }
-    SASSERT(g.is_pos());
-
-    k = k / g;
-    div_monomial(monomials, g);
-    
-}
-
-template<arith_simplifier_plugin::op_kind Kind>
-void arith_simplifier_plugin::mk_le_ge_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
-    set_curr_sort(arg1);
-    bool is_int = m_curr_sort->get_decl_kind() == INT_SORT;
-    expr_ref_vector monomials(m_manager);
-    rational k;
-    TRACE("arith_eq_bug", tout << mk_ismt2_pp(arg1, m_manager) << "\n" << mk_ismt2_pp(arg2, m_manager) << "\n";);
-    process_sum_of_monomials(false, arg1, monomials, k);
-    process_sum_of_monomials(true,  arg2, monomials, k);
-    k.neg();
-    if (is_int) {
-        numeral g;
-        get_monomial_gcd(monomials, g);
-        if (!g.is_one()) {
-            div_monomial(monomials, g);
-            switch(Kind) {
-            case LE:
-                //
-                //   g*monmials' <= k
-                // <=> 
-                //   monomials' <= floor(k/g)
-                //
-                k = floor(k/g);
-                break;
-            case GE:
-                //
-                //   g*monmials' >= k
-                // <=> 
-                //   monomials' >= ceil(k/g)
-                //
-                k = ceil(k/g);
-                break;
-            case EQ:
-                k = k/g;
-                if (!k.is_int()) {
-                    result = m_manager.mk_false();
-                    return;
-                }
-                break;
-            }
-        }
-    }
-    expr_ref lhs(m_manager);
-    mk_sum_of_monomials(monomials, lhs);
-    if (m_util.is_numeral(lhs)) {
-        SASSERT(lhs == mk_zero());
-        if (( Kind == LE && numeral::zero() <= k) ||
-            ( Kind == GE && numeral::zero() >= k) ||
-            ( Kind == EQ && numeral::zero() == k))
-            result = m_manager.mk_true();
-        else
-            result = m_manager.mk_false();
-    }
-    else {
-        
-        if (is_neg_poly(lhs)) {
-            expr_ref neg_lhs(m_manager);
-            mk_uminus(lhs, neg_lhs);
-            lhs = neg_lhs;
-            k.neg();
-            expr * rhs = m_util.mk_numeral(k, is_int);
-            switch (Kind) {
-            case LE:
-                result = m_util.mk_ge(lhs, rhs); 
-                break;
-            case GE:
-                result = m_util.mk_le(lhs, rhs);
-                break;
-            case EQ:
-                result = m_manager.mk_eq(lhs, rhs);
-                break;
-            }
-        }
-        else {
-            expr * rhs = m_util.mk_numeral(k, is_int);
-            switch (Kind) {
-            case LE:
-                result = m_util.mk_le(lhs, rhs); 
-                break;
-            case GE:
-                result = m_util.mk_ge(lhs, rhs);
-                break;
-            case EQ:
-                result = m_manager.mk_eq(lhs, rhs);
-                break;
-            }
-        }
-    }
-}
-
-void arith_simplifier_plugin::mk_arith_eq(expr * arg1, expr * arg2, expr_ref & result) {
-    mk_le_ge_eq_core<EQ>(arg1, arg2, result);
-}
-
-void arith_simplifier_plugin::mk_le(expr * arg1, expr * arg2, expr_ref & result) {
-    mk_le_ge_eq_core<LE>(arg1, arg2, result);
-}
-
-void arith_simplifier_plugin::mk_ge(expr * arg1, expr * arg2, expr_ref & result) {
-    mk_le_ge_eq_core<GE>(arg1, arg2, result);
-}
-
-void arith_simplifier_plugin::mk_lt(expr * arg1, expr * arg2, expr_ref & result) {
-    expr_ref tmp(m_manager);
-    mk_le(arg2, arg1, tmp);
-    m_bsimp.mk_not(tmp, result);
-}
- 
-void arith_simplifier_plugin::mk_gt(expr * arg1, expr * arg2, expr_ref & result) {
-    expr_ref tmp(m_manager);
-    mk_le(arg1, arg2, tmp);
-    m_bsimp.mk_not(tmp, result);
-}
-
-void arith_simplifier_plugin::gcd_normalize(numeral & coeff, expr_ref& term) {
-    if (!abs(coeff).is_one()) {
-        set_curr_sort(term);
-        SASSERT(m_curr_sort->get_decl_kind() == INT_SORT);
-        expr_ref_vector monomials(m_manager);
-        rational k;
-        monomials.push_back(mk_numeral(numeral(coeff), true));
-        process_sum_of_monomials(false, term, monomials, k);
-        gcd_reduce_monomial(monomials, k);
-        numeral coeff1;
-        if (!is_numeral(monomials[0].get(), coeff1)) {
-            UNREACHABLE();
-        }
-        if (coeff1 == coeff) {
-            return;
-        }
-        monomials[0] = mk_numeral(k, true);
-        coeff = coeff1;        
-        mk_sum_of_monomials(monomials, term);        
-    }
-}
-
-
-void arith_simplifier_plugin::mk_div(expr * arg1, expr * arg2, expr_ref & result) {
-    set_curr_sort(arg1);
-    numeral v1, v2;
-    bool is_int;
-    if (m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) {
-        SASSERT(!is_int);
-        if (m_util.is_numeral(arg1, v1, is_int))
-            result = m_util.mk_numeral(v1/v2, false);
-        else {
-            numeral k(1);
-            k /= v2;
-            
-            expr_ref inv_arg2(m_util.mk_numeral(k, false), m_manager);
-            mk_mul(inv_arg2, arg1, result);
-        }
-    }
-    else
-        result = m_util.mk_div(arg1, arg2);
-}
-
-void arith_simplifier_plugin::mk_idiv(expr * arg1, expr * arg2, expr_ref & result) {
-    set_curr_sort(arg1);
-    numeral v1, v2;
-    bool is_int;
-    if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero())
-        result = m_util.mk_numeral(div(v1, v2), is_int);
-    else 
-        result = m_util.mk_idiv(arg1, arg2);
-}
-
-void arith_simplifier_plugin::prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result) {
-    SASSERT(m_util.is_int(e));
-    SASSERT(k.is_int() && k.is_pos());
-    numeral n;
-    bool is_int;
-
-    if (depth == 0) {
-        result = e;
-    }
-    else if (is_add(e) || is_mul(e)) {
-        expr_ref_vector args(m_manager);
-        expr_ref tmp(m_manager);
-        app* a = to_app(e);
-        for (unsigned i = 0; i < a->get_num_args(); ++i) {
-            prop_mod_const(a->get_arg(i), depth - 1, k, tmp);
-            args.push_back(tmp);
-        }
-        reduce(a->get_decl(), args.size(), args.c_ptr(), result);
-    }
-    else if (m_util.is_numeral(e, n, is_int) && is_int) {
-        result = mk_numeral(mod(n, k), true);
-    }
-    else {
-        result = e;
-    }
-}
-
-void arith_simplifier_plugin::mk_mod(expr * arg1, expr * arg2, expr_ref & result) {
-    set_curr_sort(arg1);
-    numeral v1, v2;
-    bool is_int;
-    if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) {
-        result = m_util.mk_numeral(mod(v1, v2), is_int);
-    }
-    else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) {
-        result = m_util.mk_numeral(numeral(0), true);
-    }
-    else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos()) {
-        expr_ref tmp(m_manager);
-        prop_mod_const(arg1, 5, v2, tmp);
-        result = m_util.mk_mod(tmp, arg2);
-    }
-    else {
-        result = m_util.mk_mod(arg1, arg2);
-    }
-}
-
-void arith_simplifier_plugin::mk_rem(expr * arg1, expr * arg2, expr_ref & result) {
-    set_curr_sort(arg1);
-    numeral v1, v2;
-    bool is_int;
-    if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) {
-        numeral m = mod(v1, v2);
-        //
-        // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2)
-        //
-        if (v2.is_neg()) {
-            m.neg();
-        }
-        result = m_util.mk_numeral(m, is_int);
-    }
-    else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) {
-        result = m_util.mk_numeral(numeral(0), true);
-    }
-    else if (m_util.is_numeral(arg2, v2, is_int) && is_int && !v2.is_zero()) {
-        expr_ref tmp(m_manager);
-        prop_mod_const(arg1, 5, v2, tmp);
-        result = m_util.mk_mod(tmp, arg2);
-        if (v2.is_neg()) {
-            result = m_util.mk_uminus(result);
-        }
-    }
-    else {
-        result = m_util.mk_rem(arg1, arg2);
-    }
-}
-
-void arith_simplifier_plugin::mk_to_real(expr * arg, expr_ref & result) {
-    numeral v;
-    if (m_util.is_numeral(arg, v))
-        result = m_util.mk_numeral(v, false);
-    else
-        result = m_util.mk_to_real(arg);
-}
-
-void arith_simplifier_plugin::mk_to_int(expr * arg, expr_ref & result) {
-    numeral v;
-    if (m_util.is_numeral(arg, v))
-        result = m_util.mk_numeral(floor(v), true);
-    else if (m_util.is_to_real(arg)) 
-        result = to_app(arg)->get_arg(0);
-    else 
-        result = m_util.mk_to_int(arg);
-}
-
-void arith_simplifier_plugin::mk_is_int(expr * arg, expr_ref & result) {
-    numeral v;
-    if (m_util.is_numeral(arg, v))
-        result = v.is_int()?m_manager.mk_true():m_manager.mk_false();
-    else if (m_util.is_to_real(arg)) 
-        result = m_manager.mk_true();
-    else 
-        result = m_util.mk_is_int(arg);
-}
-
-bool arith_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-    SASSERT(f->get_family_id() == m_fid);
-    TRACE("arith_simplifier_plugin", tout << mk_pp(f, m_manager) << "\n"; 
-          for (unsigned i = 0; i < num_args; i++) tout << mk_pp(args[i], m_manager) << "\n";);
-    arith_op_kind k = static_cast<arith_op_kind>(f->get_decl_kind());
-    switch (k) {
-    case OP_NUM:      return false;
-    case OP_LE:       if (m_presimp) return false; SASSERT(num_args == 2); mk_le(args[0], args[1], result); break;
-    case OP_GE:       if (m_presimp) return false; SASSERT(num_args == 2); mk_ge(args[0], args[1], result); break;
-    case OP_LT:       if (m_presimp) return false; SASSERT(num_args == 2); mk_lt(args[0], args[1], result); break;
-    case OP_GT:       if (m_presimp) return false; SASSERT(num_args == 2); mk_gt(args[0], args[1], result); break;
-    case OP_ADD:      mk_add(num_args, args, result); break;
-    case OP_SUB:      mk_sub(num_args, args, result); break;
-    case OP_UMINUS:   SASSERT(num_args == 1); mk_uminus(args[0], result); break;
-    case OP_MUL:      
-        mk_mul(num_args, args, result); 
-        TRACE("arith_simplifier_plugin", tout << mk_pp(result, m_manager) << "\n";); 
-        break;
-    case OP_DIV:      SASSERT(num_args == 2); mk_div(args[0], args[1], result); break;
-    case OP_IDIV:     SASSERT(num_args == 2); mk_idiv(args[0], args[1], result); break;
-    case OP_REM:      SASSERT(num_args == 2); mk_rem(args[0], args[1], result); break;
-    case OP_MOD:      SASSERT(num_args == 2); mk_mod(args[0], args[1], result); break;
-    case OP_TO_REAL:  SASSERT(num_args == 1); mk_to_real(args[0], result); break;
-    case OP_TO_INT:   SASSERT(num_args == 1); mk_to_int(args[0], result); break;
-    case OP_IS_INT:   SASSERT(num_args == 1); mk_is_int(args[0], result); break;
-    case OP_POWER:                    return false;
-    case OP_ABS:      SASSERT(num_args == 1); mk_abs(args[0], result); break;
-    case OP_IRRATIONAL_ALGEBRAIC_NUM: return false;
-    default:
-        return false;
-    }
-    TRACE("arith_simplifier_plugin", tout << mk_pp(result.get(), m_manager) << "\n";);
-    return true;
-}
-
-void arith_simplifier_plugin::mk_abs(expr * arg, expr_ref & result) {
-    expr_ref c(m_manager);
-    expr_ref m_arg(m_manager);
-    mk_uminus(arg, m_arg);
-    mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg)), c);
-    m_bsimp.mk_ite(c, arg, m_arg, result);
-}
-
-bool arith_simplifier_plugin::is_arith_term(expr * n) const {
-    return n->get_kind() == AST_APP && to_app(n)->get_family_id() == m_fid;
-}
-
-bool arith_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
-    TRACE("reduce_eq_bug", tout << mk_ismt2_pp(lhs, m_manager) << "\n" << mk_ismt2_pp(rhs, m_manager) << "\n";);
-    set_reduce_invoked();
-    if (m_presimp) {
-        return false;
-    }
-    if (m_params.m_arith_expand_eqs) {
-        expr_ref le(m_manager), ge(m_manager);
-        mk_le_ge_eq_core<LE>(lhs, rhs, le);
-        mk_le_ge_eq_core<GE>(lhs, rhs, ge);
-        m_bsimp.mk_and(le, ge, result);
-        return true;
-    }
-
-    if (m_params.m_arith_process_all_eqs || is_arith_term(lhs) || is_arith_term(rhs)) {
-        mk_arith_eq(lhs, rhs, result); 
-        return true;
-    }
-    return false;
-}
-
-
-
diff --git a/src/ast/simplifier/arith_simplifier_plugin.h b/src/ast/simplifier/arith_simplifier_plugin.h
deleted file mode 100644
index 21ab8f6b4..000000000
--- a/src/ast/simplifier/arith_simplifier_plugin.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    arith_simplifier_plugin.h
-
-Abstract:
-
-    Simplifier for the arithmetic family.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-08
-    
---*/
-#ifndef ARITH_SIMPLIFIER_PLUGIN_H_
-#define ARITH_SIMPLIFIER_PLUGIN_H_
-
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/poly_simplifier_plugin.h"
-#include "ast/arith_decl_plugin.h"
-#include "ast/simplifier/arith_simplifier_params.h"
-
-/**
-   \brief Simplifier for the arith family.
-*/
-class arith_simplifier_plugin : public poly_simplifier_plugin {
-public:
-    enum op_kind {
-        LE, GE, EQ
-    };
-protected:
-    arith_simplifier_params & m_params;
-    arith_util                m_util;
-    basic_simplifier_plugin & m_bsimp;
-    expr_ref                  m_int_zero;
-    expr_ref                  m_real_zero;
-
-    bool is_neg_poly(expr * t) const;
-
-    template<op_kind k>
-    void mk_le_ge_eq_core(expr * arg1, expr * arg2, expr_ref & result);
-
-    void prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result);
-
-    void gcd_reduce_monomial(expr_ref_vector& monomials, numeral& k);
-
-    void div_monomial(expr_ref_vector& monomials, numeral const& g);
-    void get_monomial_gcd(expr_ref_vector& monomials, numeral& g);
-
-public:
-    arith_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, arith_simplifier_params & p);
-    ~arith_simplifier_plugin();
-    arith_util & get_arith_util() { return m_util; }
-    virtual numeral norm(const numeral & n) { return n; }
-    virtual bool is_numeral(expr * n, rational & val) const { bool f; return m_util.is_numeral(n, val, f); }
-    bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
-    virtual bool is_minus_one(expr * n) const { numeral tmp; return is_numeral(n, tmp) && tmp.is_minus_one(); }
-    virtual expr * get_zero(sort * s) const { return m_util.is_int(s) ? m_int_zero.get() : m_real_zero.get(); }
-
-    virtual app * mk_numeral(numeral const & n) { return m_util.mk_numeral(n, m_curr_sort->get_decl_kind() == INT_SORT); }
-    app * mk_numeral(numeral const & n, bool is_int) { return m_util.mk_numeral(n, is_int); }
-    bool is_int_sort(sort const * s) const { return m_util.is_int(s); }
-    bool is_real_sort(sort const * s) const { return m_util.is_real(s); }
-    bool is_arith_sort(sort const * s) const { return is_int_sort(s) || is_real_sort(s); }
-    bool is_int(expr const * n) const { return m_util.is_int(n); }
-    bool is_le(expr const * n) const { return m_util.is_le(n); }
-    bool is_ge(expr const * n) const { return m_util.is_ge(n); }
-
-    virtual bool is_le_ge(expr * n) const { return is_le(n) || is_ge(n); }
-    
-    void mk_le(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_ge(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_lt(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_gt(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_arith_eq(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_div(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_idiv(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_mod(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_rem(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_to_real(expr * arg, expr_ref & result);
-    void mk_to_int(expr * arg, expr_ref & result);
-    void mk_is_int(expr * arg, expr_ref & result);
-    void mk_abs(expr * arg, expr_ref & result);
-
-    virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
-    virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
-
-    bool is_arith_term(expr * n) const;
-
-    void gcd_normalize(numeral & coeff, expr_ref& term);
-
-};
-
-#endif /* ARITH_SIMPLIFIER_PLUGIN_H_ */
diff --git a/src/ast/simplifier/array_simplifier_params.cpp b/src/ast/simplifier/array_simplifier_params.cpp
deleted file mode 100644
index ff7dba25f..000000000
--- a/src/ast/simplifier/array_simplifier_params.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/*++
-Copyright (c) 2012 Microsoft Corporation
-
-Module Name:
-
-    array_simplifier_params.cpp
-
-Abstract:
-
-    This file was created during code reorg.
-
-Author:
-
-    Leonardo de Moura (leonardo) 2012-12-02.
-
-Revision History:
-
---*/
-#include "ast/simplifier/array_simplifier_params.h"
-#include "ast/simplifier/array_simplifier_params_helper.hpp"
-
-void array_simplifier_params::updt_params(params_ref const & _p) {
-    array_simplifier_params_helper p(_p);
-    m_array_canonize_simplify = p.array_canonize();
-    m_array_simplify          = p.array_simplify();
-}
diff --git a/src/ast/simplifier/array_simplifier_params.h b/src/ast/simplifier/array_simplifier_params.h
deleted file mode 100644
index 07ead5fd6..000000000
--- a/src/ast/simplifier/array_simplifier_params.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*++
-Copyright (c) 2012 Microsoft Corporation
-
-Module Name:
-
-    array_simplifier_params.h
-
-Abstract:
-
-    This file was created during code reorg.
-
-Author:
-
-    Leonardo de Moura (leonardo) 2012-12-02.
-
-Revision History:
-
---*/
-#ifndef ARRAY_SIMPLIFIER_PARAMS_H_
-#define ARRAY_SIMPLIFIER_PARAMS_H_
-
-#include "util/params.h"
-
-struct array_simplifier_params1 {
-
-    array_simplifier_params1(params_ref const & p = params_ref()) {
-        updt_params(p);
-    }
-
-    void updt_params(params_ref const & _p);
-};
-    
-#endif /* ARITH_SIMPLIFIER_PARAMS_H_ */
-
diff --git a/src/ast/simplifier/array_simplifier_params_helper.pyg b/src/ast/simplifier/array_simplifier_params_helper.pyg
deleted file mode 100644
index 93c184c23..000000000
--- a/src/ast/simplifier/array_simplifier_params_helper.pyg
+++ /dev/null
@@ -1,6 +0,0 @@
-def_module_params(class_name='array_simplifier_params_helper',
-                  module_name="old_simplify", # Parameters will be in the old_simplify module
-                  export=True,
-                  params=(
-                          ('array.canonize', BOOL, False, 'normalize array terms into normal form during simplification'),
-                          ('array.simplify', BOOL, True, 'enable/disable array simplifications')))
diff --git a/src/ast/simplifier/array_simplifier_plugin.cpp b/src/ast/simplifier/array_simplifier_plugin.cpp
deleted file mode 100644
index 754daab69..000000000
--- a/src/ast/simplifier/array_simplifier_plugin.cpp
+++ /dev/null
@@ -1,877 +0,0 @@
-/*++
-Copyright (c) 2008 Microsoft Corporation
-
-Module Name:
-
-    array_simplifier_plugin.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Nikolaj Bjorner (nbjorner) 2008-05-05
-
-Revision History:
-
-Notes TODO:
-
-    Examine quadratic cost of simplification vs. model-based procedure.
-
-    Parameterize cache replacement strategy.
-    Some parameters are hard-wired.
-
---*/
-
-#include "ast/simplifier/array_simplifier_plugin.h"
-#include "ast/ast_ll_pp.h"
-#include "ast/ast_pp.h"
-
-
-array_simplifier_plugin::array_simplifier_plugin(
-    ast_manager & m, 
-    basic_simplifier_plugin& s, 
-    simplifier& simp,
-    array_simplifier_params const& p) : 
-    simplifier_plugin(symbol("array"),m),
-    m_util(m),
-    m_simp(s),
-    m_simplifier(simp),
-    m_params(p),
-    m_store_cache_size(0)
-{}
-
-
-array_simplifier_plugin::~array_simplifier_plugin() {
-
-    select_cache::iterator it = m_select_cache.begin();
-    select_cache::iterator end = m_select_cache.end();
-    for ( ; it != end; ++it) {
-        m_manager.dec_array_ref(it->m_key->size(), it->m_key->c_ptr());
-        m_manager.dec_ref(it->m_value);
-        dealloc(it->m_key);
-    }
-
-    store_cache::iterator it2 = m_store_cache.begin();
-    store_cache::iterator end2 = m_store_cache.end();
-    for (; it2 != end2; ++it2) {
-        m_manager.dec_ref(it->m_value);
-        dealloc(it->m_key);
-    }
-}
-
-
-bool array_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
-    if (!m_params.m_array_simplify)
-        return false;
-    set_reduce_invoked();
-    if (m_presimp)
-        return false;
-#if Z3DEBUG
-    for (unsigned i = 0; i < num_args && i < f->get_arity(); ++i) {
-        SASSERT(m_manager.get_sort(args[i]) == f->get_domain(i));
-    }
-#endif
-    TRACE("array_simplifier", {
-            tout << mk_pp(f, m_manager) << " ";
-            for (unsigned i = 0; i < num_args; ++i) {
-                tout << mk_pp(args[i], m_manager) << " ";
-            }
-            tout << "\n";
-        }
-        );
-    SASSERT(f->get_family_id() == m_fid);
-    switch(f->get_decl_kind()) {
-    case OP_SELECT: 
-        mk_select(num_args, args, result);
-        break;
-    case OP_STORE:
-        mk_store(f, num_args, args, result);
-        break;
-    case OP_SET_UNION: {
-        sort* s = f->get_range();
-        expr_ref empty(m_manager);
-        mk_empty_set(s, empty);
-        switch(num_args) {
-        case 0:
-            result = empty;
-            break;
-        case 1:
-            result = args[0];
-            break;
-        default: {
-            result = args[0];
-            func_decl* f_or = m_manager.mk_or_decl();
-            for (unsigned i = 1; i < num_args; ++i) {
-                mk_map(f_or, result, args[i], result);
-            }
-            break;
-        }
-        }
-        break;
-    }
-    case OP_SET_INTERSECT: {
-        expr_ref full(m_manager);
-        mk_full_set(f->get_range(), full);
-        switch(num_args) {
-        case 0:
-            result = full;
-            break;
-        case 1:
-            result = args[0];
-            break;
-        default: {
-            result = args[0];
-            func_decl* f_and = m_manager.mk_and_decl();
-            for (unsigned i = 1; i < num_args; ++i) {
-                mk_map(f_and, result, args[i], result);
-            }
-            break;
-        }
-        }
-        TRACE("array_simplifier", tout << "sort " << mk_pp(result.get(), m_manager) << "\n";);
-        break;
-    }
-    case OP_SET_SUBSET: {
-        SASSERT(num_args == 2);
-        expr_ref diff(m_manager), emp(m_manager);
-        mk_set_difference(num_args, args, diff);
-        mk_empty_set(m_manager.get_sort(args[0]), emp);
-        m_simp.mk_eq(diff.get(), emp.get(), result);
-        break;
-    }
-    case OP_SET_COMPLEMENT: {
-        SASSERT(num_args == 1);
-        func_decl* f_not = m_manager.mk_not_decl();
-        mk_map(f_not, args[0], result);
-        break;
-    }
-    case OP_SET_DIFFERENCE: {
-        SASSERT(num_args == 2);
-        expr_ref r1(m_manager);
-        mk_map(m_manager.mk_not_decl(), args[1], r1);
-        mk_map(m_manager.mk_and_decl(), args[0], r1, result);
-        break;
-    }
-    case OP_ARRAY_MAP: {
-        SASSERT(f->get_num_parameters() == 1);
-        SASSERT(f->get_parameter(0).is_ast());
-        SASSERT(is_func_decl(f->get_parameter(0).get_ast()));
-        //
-        // map_d (store a j v) = (store (map_f a) v (d v)) 
-        //
-        if (num_args == 1 && is_store(args[0])) {
-            app* store_expr = to_app(args[0]);
-            unsigned num_args = store_expr->get_num_args();
-            SASSERT(num_args >= 3);
-            parameter p = f->get_parameter(0);
-            func_decl* d = to_func_decl(p.get_ast());
-            expr* a = store_expr->get_arg(0);
-            expr* v = store_expr->get_arg(num_args-1);
-            // expr*const* args = store_expr->get_args()+1;            
-            expr_ref r1(m_manager), r2(m_manager);
-            ptr_vector<expr> new_args;
-
-            reduce(f, 1, &a, r1);
-            m_simplifier.mk_app(d, 1, &v, r2);
-            new_args.push_back(r1);
-            for (unsigned i = 1; i + 1 < num_args; ++i) {
-                new_args.push_back(store_expr->get_arg(i));
-            }
-            new_args.push_back(r2);
-            mk_store(store_expr->get_decl(), num_args, new_args.c_ptr(), result);
-            break;
-        }
- 
-       //
-        // map_d (store a j v) (store b j w) = (store (map_f a b) j (d v w)) 
-        //
-        if (num_args > 1 && same_store(num_args, args)) {
-            app* store_expr1 = to_app(args[0]);
-            unsigned num_indices = store_expr1->get_num_args();
-            SASSERT(num_indices >= 3);
-            parameter p = f->get_parameter(0);
-            func_decl* d = to_func_decl(p.get_ast());
-            ptr_vector<expr> arrays;
-            ptr_vector<expr> values;
-            for (unsigned i = 0; i < num_args; ++i) {
-                arrays.push_back(to_app(args[i])->get_arg(0));
-                values.push_back(to_app(args[i])->get_arg(num_indices-1));
-            }
-            
-            expr_ref r1(m_manager), r2(m_manager);
-            reduce(f, arrays.size(), arrays.c_ptr(), r1);
-            m_simplifier.mk_app(d, values.size(), values.c_ptr(), r2);
-            ptr_vector<expr> new_args;
-            new_args.push_back(r1);
-            for (unsigned i = 1; i + 1 < num_indices; ++i) {
-                new_args.push_back(store_expr1->get_arg(i));
-            }
-            new_args.push_back(r2);
-            mk_store(store_expr1->get_decl(), new_args.size(), new_args.c_ptr(), result);
-            break;
-        }
-        //
-        // map_d (const v) = (const (d v))
-        //
-        if (num_args == 1 && is_const_array(args[0])) {
-            app* const_expr = to_app(args[0]);
-            SASSERT(const_expr->get_num_args() == 1);
-            parameter p = f->get_parameter(0);
-            func_decl* d = to_func_decl(p.get_ast());
-            expr* v = const_expr->get_arg(0);
-            expr_ref r1(m_manager);
-            
-            m_simplifier.mk_app(d, 1, &v, r1);
-            expr* arg = r1.get();
-            parameter param(f->get_range());
-            result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, &param, 1, &arg);
-            break;
-        }
-        //
-        // map_d (const v) (const w) = (const (d v w))
-        //
-        if (num_args > 1 && all_const_array(num_args, args)) {
-            parameter p = f->get_parameter(0);
-            func_decl* d = to_func_decl(p.get_ast());
-            ptr_vector<expr> values;
-            for (unsigned i = 0; i < num_args; ++i) {
-                values.push_back(to_app(args[i])->get_arg(0));
-            }
-            expr_ref r1(m_manager);
-            
-            m_simplifier.mk_app(d, values.size(), values.c_ptr(), r1);
-            expr* arg = r1.get();
-            parameter param(f->get_range());
-            result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, &param, 1, &arg);
-            break;
-        }
-        result = m_manager.mk_app(f, num_args, args);
-        
-        break;
-    }
-    default:
-        result = m_manager.mk_app(f, num_args, args);
-        break;
-    }
-    TRACE("array_simplifier", 
-          tout << mk_pp(result.get(), m_manager) << "\n";);
-
-    return true;
-}
-
-bool array_simplifier_plugin::same_store(unsigned num_args, expr* const* args) const {
-    if (num_args == 0) {
-        return true;
-    }
-    if (!is_store(args[0])) {
-        return false;
-    }
-    SASSERT(to_app(args[0])->get_num_args() >= 3);
-    unsigned num_indices = to_app(args[0])->get_num_args() - 2;
-    for (unsigned i = 1; i < num_args; ++i) {
-        if (!is_store(args[i])) {
-            return false;
-        }
-        for (unsigned j = 1; j < num_indices + 1; ++j) {
-            if (to_app(args[0])->get_arg(j) != to_app(args[i])->get_arg(j)) {
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-bool array_simplifier_plugin::all_const_array(unsigned num_args, expr* const* args) const {
-    bool is_const = true;
-    for (unsigned i = 0; is_const && i < num_args; ++i) {
-        is_const = is_const_array(args[i]);
-    }
-    return is_const;
-}
-
-bool array_simplifier_plugin::all_values(unsigned num_args, expr* const* args) const {
-    for (unsigned i = 0; i < num_args; ++i) {
-        if (!m_manager.is_unique_value(args[i])) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool array_simplifier_plugin::lex_lt(unsigned num_args, expr* const* args1, expr* const* args2) {
-    for (unsigned i = 0; i < num_args; ++i) {
-    TRACE("array_simplifier", 
-          tout << mk_pp(args1[i], m_manager) << "\n";
-          tout << mk_pp(args2[i], m_manager) << "\n";
-          tout << args1[i]->get_id() << " " << args2[i]->get_id() << "\n";
-          );
-
-        if (args1[i]->get_id() < args2[i]->get_id()) return true;
-        if (args1[i]->get_id() > args2[i]->get_id()) return false;
-    }
-    return false;
-}
-
-
-void array_simplifier_plugin::get_stores(expr* n, unsigned& arity, expr*& m, ptr_vector<expr*const>& stores) {
-    while (is_store(n)) {
-        app* a = to_app(n);
-        SASSERT(a->get_num_args() > 2);
-        arity = a->get_num_args()-2;
-        n = a->get_arg(0);
-        stores.push_back(a->get_args()+1);
-    }
-    m = n;
-}
-
-lbool array_simplifier_plugin::eq_default(expr* def, unsigned arity, unsigned num_st, expr*const* const* st) {
-    bool all_diseq = m_manager.is_unique_value(def) && num_st > 0;
-    bool all_eq = true;
-    for (unsigned i = 0; i < num_st; ++i) {
-        all_eq  &= (st[i][arity] == def);
-        all_diseq &= m_manager.is_unique_value(st[i][arity]) && (st[i][arity] != def);
-        TRACE("array_simplifier", tout << m_manager.is_unique_value(st[i][arity]) << " " << mk_pp(st[i][arity], m_manager) << "\n";);
-    }
-    if (all_eq) {
-        return l_true;
-    }
-    if (all_diseq) {
-        return l_false;
-    }   
-    return l_undef;
-}
-
-
-bool array_simplifier_plugin::insert_table(expr* def, unsigned arity, unsigned num_st, expr*const* const* st, arg_table& table) {
-    for (unsigned i = 0; i < num_st; ++i ) {
-        for (unsigned j = 0; j < arity; ++j) {
-            if (!m_manager.is_unique_value(st[i][j])) {
-                return false;
-            }
-        }
-        TRACE("array_simplifier", tout << "inserting: ";
-              for (unsigned j = 0; j < arity; ++j) {
-                  tout << mk_pp(st[i][j], m_manager) << " ";              
-              }
-            tout << " |-> " << mk_pp(def, m_manager) << "\n";
-            );
-        args_entry e(arity, st[i]);
-        table.insert_if_not_there(e);
-    }
-    return true;
-}
-
-
-lbool array_simplifier_plugin::eq_stores(expr* def, unsigned arity, unsigned num_st1, expr*const* const* st1, unsigned num_st2, expr*const* const* st2) {
-    if (num_st1 == 0) {
-        return eq_default(def, arity, num_st2, st2);
-    }
-    if (num_st2 == 0) {
-        return eq_default(def, arity, num_st1, st1);
-    }
-    arg_table table1, table2;
-    if (!insert_table(def, arity, num_st1, st1, table1)) {
-        return l_undef;
-    }
-    if (!insert_table(def, arity, num_st2, st2, table2)) {
-        return l_undef;
-    }
-
-    arg_table::iterator it = table1.begin();
-    arg_table::iterator end = table1.end();
-    for (; it != end; ++it) {
-        args_entry const & e1 = *it;
-        args_entry e2;
-        expr* v1 = e1.m_args[arity];
-        if (table2.find(e1, e2)) {
-            expr* v2 = e2.m_args[arity];
-            if (v1 == v2) {
-                table2.erase(e1);
-                continue;
-            }
-            if (m_manager.is_unique_value(v1) && m_manager.is_unique_value(v2)) {
-                return l_false;
-            }
-            return l_undef;
-        }
-        else if (m_manager.is_unique_value(v1) && m_manager.is_unique_value(def) && v1 != def) {
-            return l_false;
-        }
-    }
-    it = table2.begin();
-    end = table2.end();
-    for (; it != end; ++it) {
-        args_entry const & e = *it;
-        expr* v = e.m_args[arity];
-        if (m_manager.is_unique_value(v) && m_manager.is_unique_value(def) && v != def) {
-            return l_false;
-        }
-    }
-    if (!table2.empty() || !table1.empty()) {
-        return l_undef;
-    }
-    return l_true;    
-}
-
-
-bool array_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
-    set_reduce_invoked();
-    expr* c1, *c2;
-    ptr_vector<expr*const> st1, st2;
-    unsigned arity1 = 0;
-    unsigned arity2 = 0;
-    get_stores(lhs, arity1, c1, st1);
-    get_stores(rhs, arity2, c2, st2);
-    if (arity1 == arity2 && is_const_array(c1) && is_const_array(c2)) {
-        c1 = to_app(c1)->get_arg(0);
-        c2 = to_app(c2)->get_arg(0);
-        if (c1 == c2) {
-            lbool eq = eq_stores(c1, arity2, st1.size(), st1.c_ptr(), st2.size(), st2.c_ptr());
-            TRACE("array_simplifier", 
-                  tout << mk_pp(lhs, m_manager) << " = " 
-                  << mk_pp(rhs, m_manager) << " := " << eq << "\n";
-                  tout << "arity: " << arity1 << "\n";);
-            switch(eq) {
-            case l_false: 
-                result = m_manager.mk_false(); 
-                return true;
-            case l_true: 
-                result = m_manager.mk_true(); 
-                return true;
-            default: 
-                return false;
-            }
-        }
-        else if (m_manager.is_unique_value(c1) && m_manager.is_unique_value(c2)) {
-            result = m_manager.mk_false();
-            return true;
-        }
-    }
-    return false;
-}
-    
-bool array_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-    return false;
-}
-    
-
-array_simplifier_plugin::const_select_result
-array_simplifier_plugin::mk_select_const(expr* m, app* index, expr_ref& result) {
-    store_info* info = 0;
-    expr* r = 0, *a = 0;
-    if (!is_store(m)) {
-        return NOT_CACHED;
-    }
-    if (!m_store_cache.find(m, info)) {
-        return NOT_CACHED;
-    }       
-    if (info->m_map.find(index, r)) {
-        result = r;
-        return FOUND_VALUE;
-    }
-    a = info->m_default.get();
-
-    // 
-    // Unfold and cache the store while searching for value of index.
-    //     
-    while (is_store(a) && m_manager.is_unique_value(to_app(a)->get_arg(1))) {
-        app* b = to_app(a);
-        app* c = to_app(b->get_arg(1));
-        
-        if (!info->m_map.contains(c)) {
-            info->m_map.insert(c, b->get_arg(2));
-            m_manager.inc_ref(b->get_arg(2));
-            ++m_store_cache_size;
-        }
-        a = b->get_arg(0);
-        info->m_default = a;
-        
-        if (c == index) {
-            result = b->get_arg(2);
-            return FOUND_VALUE;
-        }
-    }
-    result = info->m_default.get();
-    return FOUND_DEFAULT;
-}
-
-void array_simplifier_plugin::cache_store(unsigned num_stores, expr* store_term) 
-{
-    if (num_stores <= m_const_store_threshold) {
-        return;
-    }
-    prune_store_cache();
-    if (!m_store_cache.contains(store_term)) {
-        store_info * info = alloc(store_info, m_manager, store_term);
-        m_manager.inc_ref(store_term);
-        m_store_cache.insert(store_term, info);
-        TRACE("cache_store", tout << m_store_cache.size() << "\n";);
-        ++m_store_cache_size;
-    }
-}
-
-void array_simplifier_plugin::cache_select(unsigned num_args, expr * const * args, expr * result) {
-    ptr_vector<expr> * entry = alloc(ptr_vector<expr>);
-    entry->append(num_args, const_cast<expr**>(args));
-    const select_cache::key_data & kd = m_select_cache.insert_if_not_there(entry, result);
-    if (kd.m_key != entry) {
-        dealloc(entry);
-        return;
-    }
-    m_manager.inc_array_ref(num_args, args);
-    m_manager.inc_ref(result);
-    TRACE("cache_select", tout << m_select_cache.size() << "\n";);
-}
-
-
-
-void array_simplifier_plugin::prune_select_cache() {
-    if (m_select_cache.size() > m_select_cache_max_size) {
-        flush_select_cache();
-    }
-}
-
-void array_simplifier_plugin::prune_store_cache() {
-    if (m_store_cache_size > m_store_cache_max_size) {
-        flush_store_cache();
-    }
-}
-
-void array_simplifier_plugin::flush_select_cache() {
-    select_cache::iterator it  = m_select_cache.begin();
-    select_cache::iterator end = m_select_cache.end();
-    for (; it != end; ++it) {
-        ptr_vector<expr> * e = (*it).m_key;
-        m_manager.dec_array_ref(e->size(), e->begin());
-        m_manager.dec_ref((*it).m_value);
-        dealloc(e);
-    }
-    m_select_cache.reset();
-}
-
-void array_simplifier_plugin::flush_store_cache() {
-    store_cache::iterator it = m_store_cache.begin();
-    store_cache::iterator end = m_store_cache.end();
-    for (; it != end; ++it) {
-        m_manager.dec_ref((*it).m_key);
-        const_map::iterator mit = (*it).m_value->m_map.begin();
-        const_map::iterator mend = (*it).m_value->m_map.end();
-        for (; mit != mend; ++mit) {
-            m_manager.dec_ref((*mit).m_value);
-        }
-        dealloc((*it).m_value);
-    }
-    m_store_cache.reset();
-    m_store_cache_size = 0;
-}
-
-
-void array_simplifier_plugin::flush_caches() {
-    flush_select_cache();
-    flush_store_cache();
-}
-
-void array_simplifier_plugin::mk_set_difference(unsigned num_args, expr * const * args, expr_ref & result) {
-    SASSERT(num_args == 2);
-    result = m_manager.mk_app(m_fid, OP_SET_DIFFERENCE, 0, 0, num_args, args);
-}
-
-void array_simplifier_plugin::mk_empty_set(sort* ty, expr_ref & result) {
-    parameter param(ty);
-    expr* args[1] = { m_manager.mk_false() };
-    result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, &param, 1, args);
-}
-
-void array_simplifier_plugin::mk_full_set(sort* ty, expr_ref & result) {
-    parameter param(ty);
-    expr* args[1] = { m_manager.mk_true() };
-    result = m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, &param, 1, args);
-}
-
-
-bool array_simplifier_plugin::same_args(unsigned num_args, expr * const * args1, expr * const * args2) {
-    for (unsigned i = 0; i < num_args; ++i) {
-        if (args1[i] != args2[i]) {
-            return false;
-        }
-    }
-    return true;
-}
-
-void array_simplifier_plugin::mk_store(func_decl* f, unsigned num_args, expr * const * args, expr_ref & result) {
-
-    SASSERT(num_args >= 3);
-
-    expr* arg0 = args[0];
-    expr* argn = args[num_args-1];
-
-    //
-    // store(store(a,i,v),i,w) = store(a,i,w)
-    // 
-    if (is_store(arg0) &&
-        same_args(num_args-2, args+1, to_app(arg0)->get_args()+1)) {
-        expr_ref_buffer new_args(m_manager);
-        new_args.push_back(to_app(arg0)->get_arg(0));
-        for (unsigned i = 1; i < num_args; ++i) {
-            new_args.push_back(args[i]);
-        }
-        reduce(f, num_args, new_args.c_ptr(), result);
-        TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";);
-        return;
-    }
-
-    //
-    // store(const(v),i,v) = const(v)
-    //
-    if (is_const_array(arg0) &&
-        to_app(arg0)->get_arg(0) == args[num_args-1]) {
-        result = arg0;
-        TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";);
-        return;
-    }
-
-    // 
-    // store(a, i, select(a, i)) = a
-    //
-    if (is_select(argn) && 
-        (to_app(argn)->get_num_args() == num_args - 1) &&
-        same_args(num_args-1, args, to_app(argn)->get_args())) {
-        TRACE("dummy_store", tout << "dummy store simplified mk_store(\n";
-              for (unsigned i = 0; i < num_args; i++) ast_ll_pp(tout, m_manager, args[i]);
-              tout << ") =====>\n";
-              ast_ll_pp(tout, m_manager, arg0););
-        result = arg0;
-        TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";);
-        return;
-    }
-
-    // 
-    // store(store(a,i,v),j,w) -> store(store(a,j,w),i,v)
-    // if i, j are values, i->get_id() < j->get_id()
-    // 
-    if (m_params.m_array_canonize_simplify &&
-        is_store(arg0) && 
-        all_values(num_args-2, args+1) &&
-        all_values(num_args-2, to_app(arg0)->get_args()+1) &&
-        lex_lt(num_args-2, args+1, to_app(arg0)->get_args()+1)) {
-        expr* const* args2 = to_app(arg0)->get_args();
-        expr_ref_buffer new_args(m_manager);
-        new_args.push_back(args2[0]);
-        for (unsigned i = 1; i < num_args; ++i) {
-            new_args.push_back(args[i]);
-        }
-        reduce(f, num_args, new_args.c_ptr(), result);
-        new_args.reset();
-        new_args.push_back(result);
-        for (unsigned i = 1; i < num_args; ++i) {
-            new_args.push_back(args2[i]);
-        }
-        result = m_manager.mk_app(m_fid, OP_STORE, num_args, new_args.c_ptr());
-        TRACE("array_simplifier", tout << mk_pp(result.get(), m_manager) << "\n";);
-        return;
-    }
-        
-
-    result = m_manager.mk_app(m_fid, OP_STORE, num_args, args);
-    TRACE("array_simplifier", tout << "default: " << mk_pp(result.get(), m_manager) << "\n";);
-
-}
-
-void array_simplifier_plugin::mk_select_as_array(unsigned num_args, expr * const * args, expr_ref & result) {
-    SASSERT(is_as_array(args[0]));
-    func_decl * f = get_as_array_func_decl(to_app(args[0]));
-    result = m_manager.mk_app(f, num_args - 1, args+1);
-}
-
-void array_simplifier_plugin::mk_select_as_array_tree(unsigned num_args, expr * const * args, expr_ref & result) {
-    SASSERT(is_as_array_tree(args[0]));
-    SASSERT(m_manager.is_ite(args[0]));
-    ptr_buffer<app, 32> todo;
-    obj_map<app, app *> cache;
-    app_ref_buffer trail(m_manager);
-    todo.push_back(to_app(args[0]));
-    while (!todo.empty()) {
-        app * curr = todo.back();
-        SASSERT(m_manager.is_ite(curr));
-        expr * branches[2] = {0, 0};
-        bool visited = true;
-        for (unsigned i = 0; i < 2; i++) {
-            expr * arg = curr->get_arg(i+1);
-            if (is_as_array(arg)) {
-                branches[i] = m_manager.mk_app(get_as_array_func_decl(to_app(arg)), num_args - 1, args+1);
-            }
-            else {
-                SASSERT(m_manager.is_ite(arg));
-                app * new_arg = 0;
-                if (!cache.find(to_app(arg), new_arg)) {
-                    todo.push_back(to_app(arg));
-                    visited = false;
-                }
-                else {
-                    branches[i] = new_arg;
-                }
-            }
-        }
-        if (visited) {
-            todo.pop_back();
-            app * new_curr = m_manager.mk_ite(curr->get_arg(0), branches[0], branches[1]);
-            trail.push_back(new_curr);
-            cache.insert(curr, new_curr);
-        }
-    }
-    SASSERT(cache.contains(to_app(args[0])));
-    app * r = 0;
-    cache.find(to_app(args[0]), r);
-    result = r;
-}
-
-void array_simplifier_plugin::mk_select(unsigned num_args, expr * const * args, expr_ref & result) {
-    expr * r = 0;
-    
-    if (is_as_array(args[0])) {
-        mk_select_as_array(num_args, args, result);
-        return;
-    }
-
-    if (is_as_array_tree(args[0])) {
-        mk_select_as_array_tree(num_args, args, result);
-        return;
-    }
-
-    bool is_const_select = num_args == 2 && m_manager.is_unique_value(args[1]);
-    app* const_index = is_const_select?to_app(args[1]):0;
-    unsigned num_const_stores = 0;
-    expr_ref tmp(m_manager);
-    expr* args2[2];
-    if (is_const_select) {
-        switch(mk_select_const(args[0], const_index, tmp)) {
-        case NOT_CACHED:
-            break;
-        case FOUND_VALUE:
-            TRACE("mk_select", tout << "found value\n"; ast_ll_pp(tout, m_manager, tmp.get()); );
-            result = tmp.get();
-            // value of select is stored under result.
-            return;
-        case FOUND_DEFAULT:
-            args2[0] = tmp.get();
-            args2[1] = args[1];
-            args = args2;
-            is_const_select = false;
-            break;
-        }
-    }
-
-    SASSERT(num_args > 0);
-    ptr_vector<expr> & entry = m_tmp2;
-    entry.reset();
-    entry.append(num_args, args);
-    expr * entry0 = entry[0];
-    SASSERT(m_todo.empty());
-    m_todo.push_back(entry0);
-    while (!m_todo.empty()) {
-        expr * m = m_todo.back();
-        TRACE("array_simplifier", tout << mk_bounded_pp(m, m_manager) << "\n";);
-        if (is_store(m)) {
-            expr * nested_array = to_app(m)->get_arg(0);
-            expr * else_branch  = 0;
-            entry[0] = nested_array;
-            if (is_const_select) {
-                if (m_manager.is_unique_value(to_app(m)->get_arg(1))) {
-                    app* const_index2 = to_app(to_app(m)->get_arg(1));
-                    //
-                    // we found the value, all other stores are different.
-                    // there is no need to recurse.
-                    //
-                    if (const_index == const_index2) {
-                        result = to_app(m)->get_arg(2);
-                        cache_store(num_const_stores, args[0]);
-                        m_todo.reset();
-                        return;
-                    }
-                    ++num_const_stores;
-                }
-                else {
-                    is_const_select = false;
-                }
-            }
-            if (m_select_cache.find(&entry, else_branch)) {
-                expr_ref_buffer eqs(m_manager);
-                for (unsigned i = 1; i < num_args ; ++i) {
-                    expr * a = args[i];
-                    expr * b = to_app(m)->get_arg(i);
-                    expr_ref eq(m_manager);
-                    m_simp.mk_eq(a, b, eq);
-                    eqs.push_back(eq.get());
-                }
-                expr_ref cond(m_manager);
-                m_simp.mk_and(eqs.size(), eqs.c_ptr(), cond);
-                expr * then_branch = to_app(m)->get_arg(num_args);
-                if (m_manager.is_true(cond.get())) {
-                    result = then_branch;
-                }
-                else if (m_manager.is_false(cond.get())) {
-                    result = else_branch;
-                }
-                else {
-                    m_simp.mk_ite(cond.get(), then_branch, else_branch, result);
-                }
-                entry[0] = m;
-                cache_select(entry.size(), entry.c_ptr(), result.get());
-                m_todo.pop_back();
-            }
-            else {
-                m_todo.push_back(nested_array);
-            }
-        }
-        else if (is_const_array(m)) {
-            entry[0] = m;
-            cache_select(entry.size(), entry.c_ptr(), to_app(m)->get_arg(0));
-            m_todo.pop_back();
-        }
-        else {
-            entry[0] = m;
-            TRACE("array_simplifier", {
-                    for (unsigned i = 0; i < entry.size(); ++i) {
-                        tout << mk_bounded_pp(entry[i], m_manager) << ": " 
-                             << mk_bounded_pp(m_manager.get_sort(entry[i]), m_manager) << "\n";
-                    }}
-                    );
-            r = m_manager.mk_app(m_fid, OP_SELECT, 0, 0, entry.size(), entry.c_ptr());
-            cache_select(entry.size(), entry.c_ptr(), r);
-            m_todo.pop_back();
-        }
-    }
-    cache_store(num_const_stores, args[0]);
-    entry[0] = entry0;
-#ifdef Z3DEBUG
-    bool f =
-#endif
-    m_select_cache.find(&entry, r);
-    SASSERT(f);
-    result = r;
-    prune_select_cache();
-    prune_store_cache();
-    TRACE("mk_select", 
-          for (unsigned i = 0; i < num_args; i++) { 
-              ast_ll_pp(tout, m_manager, args[i]); tout << "\n"; 
-          };
-          tout << "is_store: " << is_store(args[0]) << "\n";
-          ast_ll_pp(tout, m_manager, r););
-}
-
-
-void array_simplifier_plugin::mk_map(func_decl* f, expr* a, expr* b, expr_ref& result) {
-    expr* exprs[2] = { a, b };
-    parameter param(f);
-    result = m_manager.mk_app(m_fid, OP_ARRAY_MAP, 1, &param, 2, exprs );
-}
-
-void array_simplifier_plugin::mk_map(func_decl* f, expr* a, expr_ref& result) {
-    parameter param(f);
-    result = m_manager.mk_app(m_fid, OP_ARRAY_MAP, 1, &param, 1, &a );
-}
-
-
diff --git a/src/ast/simplifier/array_simplifier_plugin.h b/src/ast/simplifier/array_simplifier_plugin.h
deleted file mode 100644
index 62eb5e5ff..000000000
--- a/src/ast/simplifier/array_simplifier_plugin.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/*++
-Copyright (c) 2008 Microsoft Corporation
-
-Module Name:
-
-    array_simplifier_plugin.h
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Nikolaj Bjorner (nbjorner) 2008-05-05
-
-Revision History:
-
---*/
-#ifndef ARRAY_SIMPLIFIER_PLUGIN_H_
-#define ARRAY_SIMPLIFIER_PLUGIN_H_
-
-#include "ast/ast.h"
-#include "util/map.h"
-#include "ast/array_decl_plugin.h"
-#include "ast/simplifier/simplifier_plugin.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/array_simplifier_params.h"
-#include "ast/simplifier/simplifier.h"
-#include "util/obj_hashtable.h"
-#include "util/lbool.h"
-
-class array_simplifier_plugin : public simplifier_plugin {
-
-    typedef ptr_vector<expr> entry;
-
-    struct entry_hash_proc { 
-        unsigned operator()(ptr_vector<expr> * entry) const {
-            return get_exprs_hash(entry->size(), entry->begin(), 0xbeef1010);
-        }
-    };
-
-    struct entry_eq_proc {
-        bool operator()(ptr_vector<expr> * entry1, ptr_vector<expr> * entry2) const {
-            if (entry1->size() != entry2->size()) return false;
-            return compare_arrays(entry1->begin(), entry2->begin(), entry1->size());
-        }
-    };
-
-    typedef map<entry *, expr *, entry_hash_proc, entry_eq_proc> select_cache;
-
-    struct args_entry {
-        unsigned     m_arity;
-        expr* const* m_args;
-        args_entry(unsigned a, expr* const* args) : m_arity(a), m_args(args) {}
-        args_entry() : m_arity(0), m_args(0) {}
-    };
-
-    struct args_entry_hash_proc {
-        unsigned operator()(args_entry const& e) const {
-            return get_exprs_hash(e.m_arity, e.m_args, 0xbeef1010);
-        }
-    };
-    struct args_entry_eq_proc {
-        bool operator()(args_entry const& e1, args_entry const& e2) const {
-            if (e1.m_arity != e2.m_arity) return false;
-            return compare_arrays(e1.m_args, e2.m_args, e1.m_arity);
-        }
-    };
-    typedef hashtable<args_entry, args_entry_hash_proc, args_entry_eq_proc> arg_table;
-
-    array_util         m_util;
-    basic_simplifier_plugin& m_simp;
-    simplifier&              m_simplifier;
-    array_simplifier_params const&     m_params;
-    select_cache       m_select_cache;
-    ptr_vector<expr>   m_tmp;
-    ptr_vector<expr>   m_tmp2;
-    ptr_vector<expr>   m_todo;
-    static const unsigned m_select_cache_max_size = 100000; 
-    typedef obj_map<expr, expr*> const_map;
-    class store_info {
-        store_info();
-        store_info(store_info const&);
-    public:
-        const_map         m_map;
-        expr_ref          m_default;
-        store_info(ast_manager& m, expr* d): m_default(d, m) {}
-    };
-
-    typedef obj_map<expr, store_info*> store_cache;
-    store_cache          m_store_cache;
-    unsigned             m_store_cache_size;
-    static const unsigned m_store_cache_max_size = 10000; 
-    static const unsigned m_const_store_threshold = 5;
-    enum const_select_result {
-        NOT_CACHED,
-        FOUND_DEFAULT,
-        FOUND_VALUE        
-    };
-
-
-public:
-    array_simplifier_plugin(ast_manager & m, basic_simplifier_plugin& s, simplifier& simp, array_simplifier_params const& p);
-    virtual ~array_simplifier_plugin();
-
-    virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
-
-    virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
-    
-    virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result);    
-   
-    virtual void flush_caches();
-
-private:
-    bool is_select(expr* n) const { return m_util.is_select(n); }
-    bool is_store(expr * n) const { return m_util.is_store(n); }
-    bool is_const_array(expr * n) const { return m_util.is_const(n); }
-    bool is_as_array(expr * n) const { return m_util.is_as_array(n); }
-    bool is_as_array_tree(expr * n) { return m_util.is_as_array_tree(n); }
-    func_decl * get_as_array_func_decl(app * n) const { return m_util.get_as_array_func_decl(n); }
-    void mk_select_as_array(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_select_as_array_tree(unsigned num_args, expr * const * args, expr_ref & result);
-    bool is_enumerated(expr* n, expr_ref& c, ptr_vector<expr>& keys, ptr_vector<expr>& vals);
-    const_select_result mk_select_const(expr* m, app* index, expr_ref& result);
-    void cache_store(unsigned num_stores, expr* nested_store);
-    void cache_select(unsigned num_args, expr * const * args, expr * result);
-    void prune_select_cache();
-    void prune_store_cache();
-    void flush_select_cache();
-    void flush_store_cache();    
-    void mk_set_difference(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_empty_set(sort* ty, expr_ref & result);
-    void mk_full_set(sort* ty, expr_ref & result);
-    void mk_select(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_store(func_decl* f, unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_map(func_decl* f, expr* a, expr* b, expr_ref & result);
-    void mk_map(func_decl* f, expr* a, expr_ref & result);
-    bool same_args(unsigned num_args, expr * const * args1, expr * const * args2);
-
-    void get_stores(expr* n, unsigned& arity, expr*& m, ptr_vector<expr*const>& stores);
-    lbool eq_default(expr* def, unsigned arity, unsigned num_st, expr*const* const* st);
-    bool insert_table(expr* def, unsigned arity, unsigned num_st, expr*const* const* st, arg_table& table);
-    lbool eq_stores(expr* def, unsigned arity, unsigned num_st1, expr*const* const* st1, unsigned num_st2, expr*const* const* st2);
-
-    bool same_store(unsigned num_args, expr* const* args) const;    
-    bool all_const_array(unsigned num_args, expr* const* args) const;    
-    bool all_values(unsigned num_args, expr* const* args) const;
-    bool lex_lt(unsigned num_args, expr* const* args1, expr* const* args2);
-
-};
-
-
-#endif /* ARRAY_SIMPLIFIER_PLUGIN_H_ */
-
diff --git a/src/ast/simplifier/base_simplifier.h b/src/ast/simplifier/base_simplifier.h
deleted file mode 100644
index 73a04d605..000000000
--- a/src/ast/simplifier/base_simplifier.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    base_simplifier.h
-
-Abstract:
-
-    Base class for expression simplifier functors.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-11
-
-Notes:
-
---*/
-#ifndef BASE_SIMPLIFIER_H_
-#define BASE_SIMPLIFIER_H_
-
-#include "ast/expr_map.h"
-#include "ast/ast_pp.h"
-
-/**
-   \brief Implements basic functionality used by expression simplifiers.
-*/
-class base_simplifier {
-protected:
-    ast_manager &                  m;
-    expr_map                       m_cache;
-    ptr_vector<expr>               m_todo;
-
-    void cache_result(expr * n, expr * r, proof * p) { 
-        m_cache.insert(n, r, p); 
-        CTRACE("simplifier", !is_rewrite_proof(n, r, p),
-               tout << mk_pp(n, m) << "\n";
-               tout << mk_pp(r, m) << "\n";
-               tout << mk_pp(p, m) << "\n";);
-        SASSERT(is_rewrite_proof(n, r, p));
-    }
-    void reset_cache() { m_cache.reset(); }
-    void flush_cache() { m_cache.flush(); }
-    void get_cached(expr * n, expr * & r, proof * & p) const { m_cache.get(n, r, p); }
-
-    void reinitialize() { m_cache.set_store_proofs(m.fine_grain_proofs()); }
-
-
-    void visit(expr * n, bool & visited) {
-        if (!is_cached(n)) {
-            m_todo.push_back(n);
-            visited = false;
-        }
-    }
-
-public:
-    base_simplifier(ast_manager & m):
-        m(m),
-        m_cache(m, m.fine_grain_proofs()) {
-    }
-    bool is_cached(expr * n) const {  return m_cache.contains(n); }
-    ast_manager & get_manager() { return m; }
-
-    bool is_rewrite_proof(expr* n, expr* r, proof* p) {
-        if (p && 
-            !m.is_undef_proof(p) && 
-            !(m.has_fact(p) && 
-              (m.is_eq(m.get_fact(p)) || m.is_oeq(m.get_fact(p)) || m.is_iff(m.get_fact(p))) && 
-              to_app(m.get_fact(p))->get_arg(0) == n && 
-              to_app(m.get_fact(p))->get_arg(1) == r)) return false;
-        
-        return (!m.fine_grain_proofs() || p || (n == r));
-    }
-};
-
-#endif /* BASE_SIMPLIFIER_H_ */
diff --git a/src/ast/simplifier/basic_simplifier_plugin.cpp b/src/ast/simplifier/basic_simplifier_plugin.cpp
deleted file mode 100644
index be51bc291..000000000
--- a/src/ast/simplifier/basic_simplifier_plugin.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    basic_simplifier_plugin.cpp
-
-Abstract:
-
-    Simplifier for the basic family.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-07
-    
---*/
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/ast_ll_pp.h"
-#include "ast/rewriter/bool_rewriter.h"
-
-basic_simplifier_plugin::basic_simplifier_plugin(ast_manager & m):
-    simplifier_plugin(symbol("basic"), m), 
-    m_rewriter(alloc(bool_rewriter, m)) {
-}
-
-basic_simplifier_plugin::~basic_simplifier_plugin() {
-    dealloc(m_rewriter);
-}
-    
-bool basic_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-    SASSERT(f->get_family_id() == m_manager.get_basic_family_id());
-    basic_op_kind k = static_cast<basic_op_kind>(f->get_decl_kind());
-    switch (k) {
-    case OP_FALSE:     
-    case OP_TRUE:      
-        return false;
-    case OP_EQ:        
-        SASSERT(num_args == 2); 
-        mk_eq(args[0], args[1], result); 
-        return true;
-    case OP_DISTINCT:
-        mk_distinct(num_args, args, result);
-        return true;
-    case OP_ITE: 
-        SASSERT(num_args == 3);
-        mk_ite(args[0], args[1], args[2], result);
-        return true;
-    case OP_AND:       
-        mk_and(num_args, args, result); 
-        return true;
-    case OP_OR:        
-        mk_or(num_args, args, result); 
-        return true;
-    case OP_IMPLIES:
-        mk_implies(args[0], args[1], result); 
-        return true;
-    case OP_IFF:      
-        mk_iff(args[0], args[1], result); 
-        return true;
-    case OP_XOR: 
-        switch (num_args) {
-        case 0: result = m_manager.mk_true(); break;
-        case 1: result = args[0]; break;
-        case 2: mk_xor(args[0], args[1], result); break;
-        default: UNREACHABLE(); break;
-        }
-        return true;
-    case OP_NOT:      
-        SASSERT(num_args == 1);
-        mk_not(args[0], result); 
-        return true;
-    default:
-        UNREACHABLE();
-        return false;
-    }
-}
-
-/**
-   \brief Return true if \c rhs is of the form (ite c t1 t2) and are_distinct(lhs, t1) and are_distinct(lhs, t2).
-*/
-static bool is_lhs_diseq_rhs_ite_branches(ast_manager & m, expr * lhs, expr * rhs) {
-    return m.is_ite(rhs) && m.are_distinct(lhs, to_app(rhs)->get_arg(1)) && m.are_distinct(lhs, to_app(rhs)->get_arg(2));
-}
-
-/**
-   \brief Return true if \c rhs is of the form (ite c t1 t2) and lhs = t1 && are_distinct(lhs, t2)
-*/
-static bool is_lhs_eq_rhs_ite_then(ast_manager & m, expr * lhs, expr * rhs) {
-    return m.is_ite(rhs) && lhs == to_app(rhs)->get_arg(1) && m.are_distinct(lhs, to_app(rhs)->get_arg(2));
-}
-
-/**
-   \brief Return true if \c rhs is of the form (ite c t1 t2) and are_distinct(lhs,t1) && lhs = t2
-*/
-static bool is_lhs_eq_rhs_ite_else(ast_manager & m, expr * lhs, expr * rhs) {
-    return m.is_ite(rhs) && lhs == to_app(rhs)->get_arg(2) && m.are_distinct(lhs, to_app(rhs)->get_arg(1));
-}
-
-void basic_simplifier_plugin::mk_eq(expr * lhs, expr * rhs, expr_ref & result) {
-    // (= t1 (ite C t2 t3)) --> false if are_distinct(t1, t2) && are_distinct(t1, t3)
-    if (is_lhs_diseq_rhs_ite_branches(m_manager, lhs, rhs) || is_lhs_diseq_rhs_ite_branches(m_manager, rhs, lhs)) {
-        result = m_manager.mk_false();
-    }
-    // (= t1 (ite C t2 t3)) --> C if t1 = t2 && are_distinct(t1, t3)
-    else if (is_lhs_eq_rhs_ite_then(m_manager, lhs, rhs)) {
-        result = to_app(rhs)->get_arg(0);
-    }
-    // (= t1 (ite C t2 t3)) --> C if t1 = t2 && are_distinct(t1, t3)
-    else if (is_lhs_eq_rhs_ite_then(m_manager, rhs, lhs)) {
-        result = to_app(lhs)->get_arg(0);
-    }
-    // (= t1 (ite C t2 t3)) --> (not C)  if t1 = t3 && are_distinct(t1, t2)
-    else if (is_lhs_eq_rhs_ite_else(m_manager, lhs, rhs)) {
-        mk_not(to_app(rhs)->get_arg(0), result);
-    }
-    // (= t1 (ite C t2 t3)) --> (not C)  if t1 = t3 && are_distinct(t1, t2)
-    else if (is_lhs_eq_rhs_ite_else(m_manager, rhs, lhs)) {
-        mk_not(to_app(lhs)->get_arg(0), result);
-    }
-    else {
-        m_rewriter->mk_eq(lhs, rhs, result);
-    }
-}
-
-bool basic_simplifier_plugin::eliminate_and() const { return m_rewriter->elim_and(); }
-void basic_simplifier_plugin::set_eliminate_and(bool f) { m_rewriter->set_elim_and(f); }
-void basic_simplifier_plugin::mk_iff(expr * lhs, expr * rhs, expr_ref & result) { mk_eq(lhs, rhs, result); }
-void basic_simplifier_plugin::mk_xor(expr * lhs, expr * rhs, expr_ref & result) { m_rewriter->mk_xor(lhs, rhs, result); }
-void basic_simplifier_plugin::mk_implies(expr * lhs, expr * rhs, expr_ref & result) { m_rewriter->mk_implies(lhs, rhs, result); }
-void basic_simplifier_plugin::mk_ite(expr * c, expr * t, expr * e, expr_ref & result) { m_rewriter->mk_ite(c, t, e, result); }
-void basic_simplifier_plugin::mk_and(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_and(num_args, args, result); }
-void basic_simplifier_plugin::mk_or(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_or(num_args, args, result); }
-void basic_simplifier_plugin::mk_and(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_and(arg1, arg2, result); }
-void basic_simplifier_plugin::mk_or(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_or(arg1, arg2, result); }
-void basic_simplifier_plugin::mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { m_rewriter->mk_and(arg1, arg2, arg3, result); }
-void basic_simplifier_plugin::mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { m_rewriter->mk_or(arg1, arg2, arg3, result); }
-void basic_simplifier_plugin::mk_nand(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_nand(num_args, args, result); } 
-void basic_simplifier_plugin::mk_nor(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_nor(num_args, args, result); } 
-void basic_simplifier_plugin::mk_nand(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_nand(arg1, arg2, result); }
-void basic_simplifier_plugin::mk_nor(expr * arg1, expr * arg2, expr_ref & result) { m_rewriter->mk_nor(arg1, arg2, result); }
-void basic_simplifier_plugin::mk_distinct(unsigned num_args, expr * const * args, expr_ref & result) { m_rewriter->mk_distinct(num_args, args, result); }
-void basic_simplifier_plugin::mk_not(expr * n, expr_ref & result) { m_rewriter->mk_not(n, result); }
-
-void basic_simplifier_plugin::enable_ac_support(bool flag) {
-    m_rewriter->set_flat(flag);
-}
diff --git a/src/ast/simplifier/basic_simplifier_plugin.h b/src/ast/simplifier/basic_simplifier_plugin.h
deleted file mode 100644
index f28a19b56..000000000
--- a/src/ast/simplifier/basic_simplifier_plugin.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    basic_simplifier_plugin.h
-
-Abstract:
-
-    Simplifier for the basic family.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-07
-    
---*/
-#ifndef BASIC_SIMPLIFIER_PLUGIN_H_
-#define BASIC_SIMPLIFIER_PLUGIN_H_
-
-#include "ast/simplifier/simplifier_plugin.h"
-
-class bool_rewriter;
-
-/**
-   \brief Simplifier for the basic family.
-*/
-class basic_simplifier_plugin : public simplifier_plugin {
-    bool_rewriter * m_rewriter;
-public:
-    basic_simplifier_plugin(ast_manager & m);
-    virtual ~basic_simplifier_plugin();
-    bool_rewriter & get_rewriter() { return *m_rewriter; }
-    bool eliminate_and() const;
-    void set_eliminate_and(bool f);
-    void mk_eq(expr * lhs, expr * rhs, expr_ref & result);
-    void mk_iff(expr * lhs, expr * rhs, expr_ref & result);
-    void mk_xor(expr * lhs, expr * rhs, expr_ref & result);
-    void mk_implies(expr * lhs, expr * rhs, expr_ref & result);
-    void mk_ite(expr * c, expr * t, expr * e, expr_ref & result);
-    void mk_and(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_or(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_and(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_or(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
-    void mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result);
-    void mk_nand(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_nor(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_nand(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_nor(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_distinct(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_not(expr * n, expr_ref & result);
-    virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
-    virtual void enable_ac_support(bool flag);
-};
-
-/**
-   \brief Functor that compares expressions, but puts the expressions e and f(e) close to each other, where
-   f is in family m_fid, and has kind m_dkind;
-*/
-struct expr_lt_proc {
-    family_id  m_fid;
-    decl_kind  m_dkind;
-
-    expr_lt_proc(family_id fid = null_family_id, decl_kind k = null_decl_kind):m_fid(fid), m_dkind(k) {}
-
-    unsigned get_id(expr * n) const {
-        if (m_fid != null_family_id && is_app_of(n, m_fid, m_dkind))
-            return (to_app(n)->get_arg(0)->get_id() << 1) + 1;
-        else
-            return n->get_id() << 1;
-    }
-
-    bool operator()(expr * n1, expr * n2) const {
-        return get_id(n1) < get_id(n2);
-    }
-};
-
-#endif /* BASIC_SIMPLIFIER_PLUGIN_H_ */
diff --git a/src/ast/simplifier/bv_elim.cpp b/src/ast/simplifier/bv_elim.cpp
deleted file mode 100644
index 1c0048a07..000000000
--- a/src/ast/simplifier/bv_elim.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-
-/*++
-Copyright (c) 2015 Microsoft Corporation
-
---*/
-
-#include "ast/simplifier/bv_elim.h"
-#include "ast/bv_decl_plugin.h"
-#include "ast/rewriter/var_subst.h"
-#include <sstream>
-
-void bv_elim::elim(quantifier* q, quantifier_ref& r) {
-
-    svector<symbol>  names, _names;
-    sort_ref_buffer  sorts(m), _sorts(m);
-    expr_ref_buffer  pats(m);
-    expr_ref_buffer  no_pats(m);
-    expr_ref_buffer  subst_map(m), _subst_map(m);
-    var_subst        subst(m);
-    bv_util          bv(m);
-    expr_ref         new_body(m);
-    expr*            old_body = q->get_expr();
-    unsigned num_decls = q->get_num_decls();
-    family_id bfid = m.mk_family_id("bv");
-
-    //
-    // Traverse sequence of bound variables to eliminate
-    // bit-vecctor variables and replace them by 
-    // Booleans.
-    // 
-    unsigned var_idx = 0;
-    for (unsigned i = num_decls; i > 0; ) {
-        --i;
-        sort*  s  = q->get_decl_sort(i);
-        symbol nm = q->get_decl_name(i);
-
-        if (bv.is_bv_sort(s)) {
-            // convert n-bit bit-vector variable into sequence of n-Booleans.
-            unsigned num_bits = bv.get_bv_size(s);
-            expr_ref_buffer args(m);
-            expr_ref bv(m);
-            for (unsigned j = 0; j < num_bits; ++j) {
-                std::ostringstream new_name;
-                new_name << nm.str();
-                new_name << "_";
-                new_name << j;
-                var* v = m.mk_var(var_idx++, m.mk_bool_sort());                
-                args.push_back(v);
-                _sorts.push_back(m.mk_bool_sort());
-                _names.push_back(symbol(new_name.str().c_str()));
-            }
-            bv = m.mk_app(bfid, OP_MKBV, 0, 0, args.size(), args.c_ptr());
-            _subst_map.push_back(bv.get());
-        }
-        else {
-            _subst_map.push_back(m.mk_var(var_idx++, s));
-            _sorts.push_back(s);
-            _names.push_back(nm);
-        }
-    }
-    // 
-    // reverse the vectors.
-    // 
-    SASSERT(_names.size() == _sorts.size());
-    for (unsigned i = _names.size(); i > 0; ) {
-        --i;
-        names.push_back(_names[i]);
-        sorts.push_back(_sorts[i]);
-    }
-    for (unsigned i = _subst_map.size(); i > 0; ) {
-        --i;
-        subst_map.push_back(_subst_map[i]);
-    }
-
-    expr* const* sub  = subst_map.c_ptr();
-    unsigned sub_size = subst_map.size();
-
-    subst(old_body, sub_size, sub, new_body);
-
-    for (unsigned j = 0; j < q->get_num_patterns(); j++) {
-        expr_ref pat(m);        
-        subst(q->get_pattern(j), sub_size, sub, pat);
-        pats.push_back(pat);
-    }
-    for (unsigned j = 0; j < q->get_num_no_patterns(); j++) {
-        expr_ref nopat(m);
-        subst(q->get_no_pattern(j), sub_size, sub, nopat);
-        no_pats.push_back(nopat);
-    }
-
-    r = m.mk_quantifier(true, 
-                        names.size(),
-                        sorts.c_ptr(),
-                        names.c_ptr(),
-                        new_body.get(),
-                        q->get_weight(),
-                        q->get_qid(),
-                        q->get_skid(),
-                        pats.size(), pats.c_ptr(),
-                        no_pats.size(), no_pats.c_ptr());
-}
-
-bool bv_elim_star::visit_quantifier(quantifier* q) {
-    // behave like destructive resolution, do not recurse.
-    return true;
-}
-
-void bv_elim_star::reduce1_quantifier(quantifier* q) {
-    quantifier_ref r(m);
-    proof_ref pr(m);
-    m_bv_elim.elim(q, r);
-    if (m.fine_grain_proofs()) {
-        pr = m.mk_rewrite(q, r.get());
-    }
-    else {
-        pr = 0;
-    }
-    cache_result(q, r, pr);
-}
diff --git a/src/ast/simplifier/bv_elim.h b/src/ast/simplifier/bv_elim.h
deleted file mode 100644
index c027b1a9e..000000000
--- a/src/ast/simplifier/bv_elim.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    bv_elim.h
-
-Abstract:
-
-    Eliminate bit-vectors variables from clauses, by
-    replacing them by bound Boolean variables.
-
-Author:
-
-    Nikolaj Bjorner (nbjorner) 2008-12-16.
-
-Revision History:
-
---*/
-#ifndef BV_ELIM_H_
-#define BV_ELIM_H_
-
-#include "ast/ast.h"
-#include "ast/simplifier/simplifier.h"
-
-class bv_elim {
-    ast_manager& m;
-public:
-    bv_elim(ast_manager& m) : m(m) {};
-
-    void elim(quantifier* q, quantifier_ref& r);
-};
-
-class bv_elim_star : public simplifier {
-protected:
-    bv_elim m_bv_elim;
-    virtual bool visit_quantifier(quantifier* q);
-    virtual void reduce1_quantifier(quantifier* q);
-public:
-    bv_elim_star(ast_manager& m) : simplifier(m), m_bv_elim(m) { enable_ac_support(false); }
-    virtual ~bv_elim_star() {}
-};
-
-#endif /* BV_ELIM_H_ */
-
diff --git a/src/ast/simplifier/bv_simplifier_params.cpp b/src/ast/simplifier/bv_simplifier_params.cpp
deleted file mode 100644
index 07f854179..000000000
--- a/src/ast/simplifier/bv_simplifier_params.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    bv_simplifier_params.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2012-12-02.
-
-Revision History:
-
---*/
-#include "ast/simplifier/bv_simplifier_params.h"
-#include "ast/simplifier/bv_simplifier_params_helper.hpp"
-#include "ast/rewriter/bv_rewriter_params.hpp"
-
-void bv_simplifier_params::updt_params(params_ref const & _p) {
-    bv_simplifier_params_helper p(_p);
-    bv_rewriter_params rp(_p);
-    m_hi_div0 = rp.hi_div0();
-    m_bv2int_distribute = p.bv_bv2int_distribute();
-
-}
-
-#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl;
-
-void bv_simplifier_params::display(std::ostream & out) const {
-    DISPLAY_PARAM(m_hi_div0);
-    DISPLAY_PARAM(m_bv2int_distribute);
-}
diff --git a/src/ast/simplifier/bv_simplifier_params.h b/src/ast/simplifier/bv_simplifier_params.h
deleted file mode 100644
index 8c0792203..000000000
--- a/src/ast/simplifier/bv_simplifier_params.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    bv_simplifier_params.h
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-10-10.
-
-Revision History:
-
---*/
-#ifndef BV_SIMPLIFIER_PARAMS_H_
-#define BV_SIMPLIFIER_PARAMS_H_
-
-#include "util/params.h"
-
-struct bv_simplifier_params {
-    bool  m_hi_div0; //!< if true, uses the hardware interpretation for div0, mod0, ... if false, div0, mod0, ... are considered uninterpreted.
-    bool  m_bv2int_distribute; //!< if true allows downward propagation of bv2int.
-
-    bv_simplifier_params(params_ref const & p = params_ref()) {
-        updt_params(p);
-    }
-
-    void updt_params(params_ref const & _p);
-
-    void display(std::ostream & out) const;
-};
-
-#endif /* BV_SIMPLIFIER_PARAMS_H_ */
-
diff --git a/src/ast/simplifier/bv_simplifier_params_helper.pyg b/src/ast/simplifier/bv_simplifier_params_helper.pyg
deleted file mode 100644
index 6bcf83207..000000000
--- a/src/ast/simplifier/bv_simplifier_params_helper.pyg
+++ /dev/null
@@ -1,4 +0,0 @@
-def_module_params(class_name='bv_simplifier_params_helper',
-                  module_name="old_simplify", # Parameters will be in the old_simplify module
-                  export=True,
-                  params=(('bv.bv2int_distribute', BOOL, True, 'if true, then int2bv is distributed over arithmetical operators'),))
diff --git a/src/ast/simplifier/bv_simplifier_plugin.cpp b/src/ast/simplifier/bv_simplifier_plugin.cpp
deleted file mode 100644
index c23d2e748..000000000
--- a/src/ast/simplifier/bv_simplifier_plugin.cpp
+++ /dev/null
@@ -1,2261 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    bv_simplifier_plugin.cpp
-
-Abstract:
-
-    Simplifier for the bv family.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-08
-    Nikolaj Bjorner (nbjorner) 2008-01-05
-    
---*/
-#include "ast/simplifier/bv_simplifier_plugin.h"
-#include "ast/ast_ll_pp.h"
-#include "ast/ast_pp.h"
-#include "ast/arith_decl_plugin.h"
-#include "util/obj_hashtable.h"
-#include "ast/ast_util.h"
-
-bv_simplifier_plugin::~bv_simplifier_plugin() {
-    flush_caches();
-}
-
-bv_simplifier_plugin::bv_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, bv_simplifier_params & p):
-    poly_simplifier_plugin(symbol("bv"), m, OP_BADD, OP_BMUL, OP_BNEG, OP_BSUB, OP_BV_NUM),
-    m_manager(m),
-    m_util(m),
-    m_arith(m),
-    m_bsimp(b),
-    m_params(p),
-    m_zeros(m) {
-}
-
-rational bv_simplifier_plugin::norm(const numeral & n) {
-    unsigned bv_size = get_bv_size(m_curr_sort);
-    return norm(n, bv_size, false);
-}
-
-
-bool bv_simplifier_plugin::is_numeral(expr * n, rational & val) const { 
-    unsigned bv_size;
-    return m_util.is_numeral(n, val, bv_size); 
-}
-
-expr * bv_simplifier_plugin::get_zero(sort * s) const {
-    bv_simplifier_plugin * _this = const_cast<bv_simplifier_plugin*>(this);
-    unsigned bv_size = _this->get_bv_size(s);
-    if (bv_size >= m_zeros.size())
-        _this->m_zeros.resize(bv_size+1);
-    if (m_zeros.get(bv_size) == 0)
-        _this->m_zeros.set(bv_size, _this->m_util.mk_numeral(rational(0), s));
-    return m_zeros.get(bv_size);
-}
-
-bool bv_simplifier_plugin::are_numerals(unsigned num_args, expr * const* args, unsigned& bv_size) {
-    numeral r;
-    if (num_args == 0) {
-        return false;
-    }
-    for (unsigned i = 0; i < num_args; ++i) {
-        if (!m_util.is_numeral(args[i], r, bv_size)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-app * bv_simplifier_plugin::mk_numeral(numeral const & n) { 
-    unsigned bv_size = get_bv_size(m_curr_sort);
-    return mk_numeral(n, bv_size); 
-}
-
-app * bv_simplifier_plugin::mk_numeral(numeral const& n, unsigned bv_size) {
-    numeral r = mod(n, rational::power_of_two(bv_size));
-    SASSERT(!r.is_neg());
-    SASSERT(r < rational::power_of_two(bv_size));
-    return m_util.mk_numeral(r, bv_size);
-}
-
-bool bv_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-    
-    SASSERT(f->get_family_id() == m_fid);
-    
-    bv_op_kind k = static_cast<bv_op_kind>(f->get_decl_kind());
-    switch (k) {
-    case OP_BV_NUM:    SASSERT(num_args == 0); result = mk_numeral(f->get_parameter(0).get_rational(), f->get_parameter(1).get_int()); break;
-    case OP_BIT0:      SASSERT(num_args == 0); result = mk_numeral(0, 1); break;
-    case OP_BIT1:      SASSERT(num_args == 0); result = mk_numeral(1, 1); break;
-    case OP_BADD:      SASSERT(num_args > 0); 
-        mk_add(num_args, args, result); 
-        TRACE("bv_add_bug", 
-              for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m_manager, 10) << "\n";
-              tout << mk_bounded_pp(result, m_manager, 10) << "\n";); 
-        mk_add_concat(result);              
-        break;
-    case OP_BSUB:      SASSERT(num_args > 0); mk_sub(num_args, args, result);  break;
-    case OP_BNEG:      SASSERT(num_args == 1); mk_uminus(args[0], result);     break;
-    case OP_BMUL:      SASSERT(num_args > 0);  mk_mul(num_args, args, result); break;
-    case OP_ULEQ:      if (m_presimp) return false; SASSERT(num_args == 2); mk_ule(args[0], args[1], result); break;
-    case OP_UGEQ:      if (m_presimp) return false; SASSERT(num_args == 2); mk_ule(args[1], args[0], result); break;
-    case OP_ULT:       if (m_presimp) return false; SASSERT(num_args == 2); mk_ult(args[0], args[1], result); break;
-    case OP_UGT:       if (m_presimp) return false; SASSERT(num_args == 2); mk_ult(args[1], args[0], result); break;
-    case OP_SLEQ:      if (m_presimp) return false; SASSERT(num_args == 2); mk_sle(args[0], args[1], result); break;
-    case OP_SGEQ:      if (m_presimp) return false; SASSERT(num_args == 2); mk_sle(args[1], args[0], result); break;
-    case OP_SLT:       if (m_presimp) return false; SASSERT(num_args == 2); mk_slt(args[0], args[1], result); break;
-    case OP_SGT:       if (m_presimp) return false; SASSERT(num_args == 2); mk_slt(args[1], args[0], result); break;
-    case OP_BAND:      SASSERT(num_args > 0); mk_bv_and(num_args, args, result); break;
-    case OP_BOR:       SASSERT(num_args > 0); mk_bv_or(num_args, args, result); break;
-    case OP_BNOT:      SASSERT(num_args == 1); mk_bv_not(args[0], result); break;
-    case OP_BXOR:      SASSERT(num_args > 0); mk_bv_xor(num_args, args, result); break;
-    case OP_CONCAT:    SASSERT(num_args > 0); mk_concat(num_args, args, result); break;
-    case OP_ZERO_EXT:  
-        SASSERT(num_args == 1); SASSERT(f->get_num_parameters() == 1); 
-        mk_zeroext(f->get_parameter(0).get_int(), args[0], result);
-        break;
-    case OP_EXTRACT:
-        SASSERT(num_args == 1); SASSERT(f->get_num_parameters() == 2); 
-        mk_extract(f->get_parameter(0).get_int(), f->get_parameter(1).get_int(), args[0], result);
-        break;
-    case OP_REPEAT:
-        SASSERT(num_args == 1); SASSERT(f->get_num_parameters() == 1); 
-        mk_repeat(f->get_parameter(0).get_int(), args[0], result);
-        break;
-    case OP_BUREM:
-        SASSERT(num_args == 2);
-        mk_bv_urem(args[0], args[1], result);
-        break;
-    case OP_SIGN_EXT:  
-        SASSERT(num_args == 1); SASSERT(f->get_num_parameters() == 1);
-        mk_sign_extend(f->get_parameter(0).get_int(), args[0], result);
-        break;
-    case OP_BSHL:      SASSERT(num_args == 2); mk_bv_shl(args[0], args[1], result); break;
-    case OP_BLSHR:     SASSERT(num_args == 2); mk_bv_lshr(args[0], args[1], result); break;
-    case OP_INT2BV:    SASSERT(num_args == 1); mk_int2bv(args[0], f->get_range(), result); break;
-    case OP_BV2INT:    SASSERT(num_args == 1); mk_bv2int(args[0], f->get_range(), result); break;
-    case OP_BSDIV:     SASSERT(num_args == 2); mk_bv_sdiv(args[0], args[1], result);  break;
-    case OP_BUDIV:     SASSERT(num_args == 2); mk_bv_udiv(args[0], args[1], result);  break;
-    case OP_BSREM:     SASSERT(num_args == 2); mk_bv_srem(args[0], args[1], result);  break;
-    case OP_BSMOD:     SASSERT(num_args == 2); mk_bv_smod(args[0], args[1], result);  break;
-    case OP_BNAND:     SASSERT(num_args > 0); mk_bv_nand(num_args, args, result);  break;
-    case OP_BNOR:      SASSERT(num_args > 0); mk_bv_nor(num_args, args, result);  break;
-    case OP_BXNOR:     SASSERT(num_args > 0); mk_bv_xnor(num_args, args, result);  break;
-    case OP_ROTATE_LEFT:  SASSERT(num_args == 1); mk_bv_rotate_left(f, args[0], result); break;
-    case OP_ROTATE_RIGHT: SASSERT(num_args == 1); mk_bv_rotate_right(f, args[0], result); break;
-    case OP_EXT_ROTATE_LEFT: SASSERT(num_args == 2); mk_bv_ext_rotate_left(args[0], args[1], result); break;
-    case OP_EXT_ROTATE_RIGHT: SASSERT(num_args == 2); mk_bv_ext_rotate_right(args[0], args[1], result); break;
-    case OP_BREDOR:    SASSERT(num_args == 1); mk_bv_redor(args[0], result); break;
-    case OP_BREDAND:   SASSERT(num_args == 1); mk_bv_redand(args[0], result); break;
-    case OP_BCOMP:     SASSERT(num_args == 2); mk_bv_comp(args[0], args[1], result); break;
-    case OP_BASHR:     SASSERT(num_args == 2); mk_bv_ashr(args[0], args[1], result); break;
-    case OP_BSDIV_I:   SASSERT(num_args == 2); mk_bv_sdiv_i(args[0], args[1], result);  break;
-    case OP_BUDIV_I:   SASSERT(num_args == 2); mk_bv_udiv_i(args[0], args[1], result);  break;
-    case OP_BSREM_I:   SASSERT(num_args == 2); mk_bv_srem_i(args[0], args[1], result);  break;
-    case OP_BUREM_I:   SASSERT(num_args == 2); mk_bv_urem_i(args[0], args[1], result);  break;
-    case OP_BSMOD_I:   SASSERT(num_args == 2); mk_bv_smod_i(args[0], args[1], result);  break;
-    case OP_BSDIV0:
-    case OP_BUDIV0:
-    case OP_BSREM0:
-    case OP_BUREM0:
-    case OP_BSMOD0:
-    case OP_BIT2BOOL:
-    case OP_CARRY:
-    case OP_XOR3:
-    case OP_MKBV:
-    case OP_BUMUL_NO_OVFL:
-    case OP_BSMUL_NO_OVFL:
-    case OP_BSMUL_NO_UDFL:
-        result = m_manager.mk_app(f, num_args, args);
-        break;        
-    default:
-        UNREACHABLE();
-        break;
-    }
-    SASSERT(result.get());
-
-    TRACE("bv_simplifier", 
-          tout << mk_pp(f, m_manager) << "\n";
-          for (unsigned i = 0; i < num_args; ++i) {
-              tout << mk_pp(args[i], m_manager) << " ";
-          }
-          tout << "\n";
-          tout << mk_pp(result.get(), m_manager) << "\n";
-          );
-
-    return true;
-}
-
-bool bv_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {    
-    set_reduce_invoked();
-
-    if (m_presimp)
-        return false;
-    expr_ref tmp(m_manager);
-    tmp = m_manager.mk_eq(lhs,rhs);
-    mk_bv_eq(lhs, rhs, result);
-    return result.get() != tmp.get();
-}
-
-bool bv_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) { 
-    set_reduce_invoked();
-
-    return false;
-}
-
-void bv_simplifier_plugin::flush_caches() { 
-    TRACE("extract_cache", tout << "flushing extract cache...\n";);
-    extract_cache::iterator it  = m_extract_cache.begin();
-    extract_cache::iterator end = m_extract_cache.end();
-    for (; it != end; ++it) {
-        extract_entry & e = (*it).m_key;
-        m_manager.dec_ref(e.m_arg);
-        m_manager.dec_ref((*it).m_value);
-    }
-    m_extract_cache.reset();
-}
-
-
-inline uint64 bv_simplifier_plugin::to_uint64(const numeral & n, unsigned bv_size) {
-    SASSERT(bv_size <= 64);
-    numeral tmp = n;
-    if (tmp.is_neg()) {
-        tmp = mod(tmp, rational::power_of_two(bv_size));
-    }
-    SASSERT(tmp.is_nonneg());
-    SASSERT(tmp.is_uint64());
-    return tmp.get_uint64();
-}
-
-#define MK_BV_OP(_oper_,_binop_) \
-rational bv_simplifier_plugin::mk_bv_ ## _oper_(numeral const& a0, numeral const& b0, unsigned sz) { \
-    rational r(0), a(a0), b(b0); \
-    numeral p64 = rational::power_of_two(64); \
-    numeral mul(1); \
-    while (sz > 0) { \
-        numeral a1 = a % p64; \
-        numeral b1 = b % p64; \
-        uint64 u = a1.get_uint64() _binop_ b1.get_uint64(); \
-        if (sz < 64) { \
-            uint64 mask = shift_left(1ull,(uint64)sz) - 1ull; \
-            u = mask & u; \
-        } \
-        r += mul*rational(u,rational::ui64()); \
-        mul *= p64; \
-        a = div(a, p64); \
-        b = div(b, p64); \
-        sz -= (sz<64)?sz:64; \
-    } \
-    return r; \
-}
-
-MK_BV_OP(and,&)
-MK_BV_OP(or,|)
-MK_BV_OP(xor,^)
-
-rational bv_simplifier_plugin::mk_bv_not(numeral const& a0, unsigned sz) { 
-    rational r(0), a(a0), mul(1);
-    numeral p64 = rational::power_of_two(64); 
-    while (sz > 0) { 
-        numeral a1 = a % p64; 
-        uint64 u = ~a1.get_uint64();
-        if (sz < 64) { 
-            uint64 mask = shift_left(1ull,(uint64)sz) - 1ull; 
-            u = mask & u; 
-        } 
-        r += mul*rational(u,rational::ui64()); 
-        mul *= p64; 
-        a = div(a, p64); 
-        sz -= (64<sz)?64:sz; 
-    } 
-    return r; 
-}
-
-
-void bv_simplifier_plugin::mk_ule(expr* a, expr* b, expr_ref& result) {
-    mk_leq_core(false, a, b, result);
-}
-
-void bv_simplifier_plugin::mk_ult(expr* a, expr* b, expr_ref& result) {
-    expr_ref tmp(m_manager);
-    mk_leq_core(false, b, a, tmp);
-    m_bsimp.mk_not(tmp.get(), result);
-}
-
-void bv_simplifier_plugin::mk_sle(expr* a, expr* b, expr_ref& result) {
-    mk_leq_core(true, a, b, result);
-}
-
-void bv_simplifier_plugin::mk_slt(expr* a, expr* b, expr_ref& result) {
-    expr_ref tmp(m_manager);
-    mk_leq_core(true, b, a, tmp);
-    m_bsimp.mk_not(tmp.get(), result);
-}
-
-void bv_simplifier_plugin::mk_leq_core(bool is_signed, expr * arg1, expr * arg2, expr_ref & result) {
-    numeral r1, r2, r3;
-    bool is_num1     = is_numeral(arg1, r1);
-    bool is_num2     = is_numeral(arg2, r2);
-    decl_kind k      = is_signed ? OP_SLEQ : OP_ULEQ;
-    unsigned bv_size = get_bv_size(arg1);
-
-    if (is_num1) {
-        r1 = norm(r1, bv_size, is_signed);
-    }
-    if (is_num2) {
-        r2 = norm(r2, bv_size, is_signed);
-    }
-
-    if (is_num1 && is_num2) {
-        result = r1 <= r2 ? m_manager.mk_true() : m_manager.mk_false();
-        return;
-    }
-
-    numeral lower, upper;
-
-    if (is_num1 || is_num2) {
-        if (is_signed) {
-            lower = - rational::power_of_two(bv_size - 1);
-            upper =   rational::power_of_two(bv_size - 1) - numeral(1);
-        }
-        else {
-            lower = numeral(0);
-            upper = rational::power_of_two(bv_size) - numeral(1);
-        }
-    }
-
-    if (is_num2) {
-        if (r2 == lower) {
-            m_bsimp.mk_eq(arg1, arg2, result);
-            return;
-        }
-        if (r2 == upper) {
-            result = m_manager.mk_true();
-            return;
-        }
-    }
-
-    if (is_num1) {
-        // 0 <= arg2 is true
-        if (r1 == lower) {
-            result = m_manager.mk_true();
-            return;
-        }
-
-        // 2^n-1 <= arg2 is arg1 = arg2
-        if (r1 == upper) {
-            m_bsimp.mk_eq(arg1, arg2, result);
-            return;
-        }
-    }
-
-    //
-    // In general, we can use lexicographic extensions of concat.
-    // But this does not always turn into savings.
-    // Only apply the simplification if arg1 is a numeral.
-    // 
-    if (is_num1 && !is_signed && m_util.is_concat(arg2) && to_app(arg2)->get_num_args() == 2) {
-        // 
-        // c <=_u (concat 0 a) <=> c[u:l] = 0 && c[l-1:0] <=_u a
-        //
-        app* app = to_app(arg2);
-        expr * arg2_1 = app->get_arg(0);
-        expr * arg2_2 = app->get_arg(1);
-        if (m_util.is_zero(arg2_1)) {
-            unsigned sz1   = get_bv_size(arg2_1);
-            unsigned sz2   = get_bv_size(arg2_2);
-            
-            expr_ref tmp1(m_manager);
-            expr_ref tmp2(m_manager);
-            mk_extract(sz2 + sz1 - 1, sz2, arg1, tmp1);
-            mk_extract(sz2 - 1, 0, arg1, tmp2);
-            
-            expr_ref eq(m_manager);
-            expr_ref zero(m_manager);
-            zero = mk_bv0(sz1);
-            mk_bv_eq(tmp1.get(), zero, eq);
-            
-            expr_ref ineq(m_manager);
-            ineq = m_util.mk_ule(tmp2.get(), arg2_2);
-            
-            m_bsimp.mk_and(eq.get(), ineq.get(), result);
-            return;
-        }
-    }
-
-    //
-    // TODO: 
-    // Others:
-    //
-    // k <=_s (concat 0 a) <=> (k[u:l] = 0 && k[l-1:0] <=_u a) || k[u:u] = bv1
-    //
-    // (concat 0 a) <=_s k <=> k[u:u] = bv0 && (k[u:l] != 0 || a <=_u k[l-1:0])
-    //
-    // (concat 0 a) <=_u k <=> k[u:l] != 0 || a <=_u k[l-1:0]
-    //
-
-    result = m_manager.mk_app(m_fid, k, arg1, arg2);
-}
-
-void bv_simplifier_plugin::mk_extract(unsigned high, unsigned low, expr* arg, expr_ref& result) {
-    
-    unsigned arg_sz = get_bv_size(arg);
-    unsigned sz     = high - low + 1;
-    TRACE("bv_simplifier_plugin", tout << "mk_extract [" << high << ":" << low << "]\n";
-          tout << "arg_sz: " << arg_sz << " sz: " << sz << "\n";
-          tout << "arg:\n";
-          ast_ll_pp(tout, m_manager, arg););    
-
-    if (arg_sz == sz) {
-        result = arg;
-    }
-    else {
-        mk_extract_core(high, low, arg, result);
-    }
-    if (m_extract_cache.size() > (1 << 12)) {
-        flush_caches();
-    }
-
-    TRACE("bv_simplifier_plugin", tout << "mk_extract [" << high << ":" << low << "]\n";
-          tout << "arg_sz: " << arg_sz << " sz: " << sz << "\n";
-          tout << "arg:\n";
-          ast_ll_pp(tout, m_manager, arg);
-          tout << "=====================>\n";
-          ast_ll_pp(tout, m_manager, result.get()););
-}
-
-
-void bv_simplifier_plugin::cache_extract(unsigned h, unsigned l, expr * arg, expr * result) {
-    m_manager.inc_ref(arg); 
-    m_manager.inc_ref(result);
-    m_extract_cache.insert(extract_entry(h, l, arg), result);
-}
-
-expr* bv_simplifier_plugin::get_cached_extract(unsigned h, unsigned l, expr * arg) {
-    expr * result = 0;
-    if (m_extract_cache.find(extract_entry(h, l, arg), result)) {
-        return result;
-    }
-    return 0;
-}
-
-
-void bv_simplifier_plugin::mk_extract_core(unsigned high, unsigned low, expr * arg, expr_ref& result) {
-    
-    if (!lookup_mk_extract(high, low, arg, result)) {
-        while (!m_extract_args.empty()) {
-            unsigned low2 = m_lows.back();
-            unsigned high2 = m_highs.back();
-            expr* arg2 = m_extract_args.back();
-            if (try_mk_extract(high2, low2, arg2, result)) {
-                if (!m_extract_cache.contains(extract_entry(high2, low2, arg2))) {
-                    cache_extract(high2, low2, arg2, result.get());
-                }
-                m_lows.pop_back();
-                m_highs.pop_back();
-                m_extract_args.pop_back();
-            }
-        }
-        if (!lookup_mk_extract(high, low, arg, result)) {
-            UNREACHABLE();
-        }
-    }
-}
-
-
-bool bv_simplifier_plugin::lookup_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result) {
-    expr* cached_result = get_cached_extract(high, low, arg);
-    if (cached_result) {
-        result = cached_result;
-        return true;
-    }
-
-    m_extract_args.push_back(arg);
-    m_lows.push_back(low);
-    m_highs.push_back(high);
-    return false;
-}
-
-
-bool bv_simplifier_plugin::try_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result) {
-
-    SASSERT(low <= high);
-    unsigned arg_sz = get_bv_size(arg);
-    unsigned sz     = high - low + 1;
-    numeral r;
-    unsigned num_bits;
-
-    if (arg_sz == sz) {
-        result = arg;
-        return true;
-    }
-
-    expr* cached_result = get_cached_extract(high, low, arg);
-    if (cached_result) {
-        result = cached_result;
-        return true;
-    }    
-
-    if (!is_app(arg)) {
-        result = m_util.mk_extract(high, low, arg);
-        return true;
-    }
-    app* a = to_app(arg);
-
-    if (m_util.is_numeral(a, r, num_bits)) {
-        if (r.is_neg()) {
-            r = mod(r, rational::power_of_two(sz));
-        }
-        SASSERT(r.is_nonneg());
-        if (r.is_uint64()) {
-            uint64 u     = r.get_uint64();
-            uint64 e     = shift_right(u, low) & (shift_left(1ull, sz) - 1ull);
-            TRACE("mk_extract_bug", tout << u << "[" << high << ":" << low << "] " << e << " (u >> low): " << shift_right(u, low) << " (1ull << sz): " 
-                  << shift_left(1ull, sz) << " ((1ull << sz) - 1ull)" << (shift_left(1ull, sz) - 1ull) << "\n";);
-            result = mk_numeral(numeral(e, numeral::ui64()), sz);
-            return true;
-        }
-        result = mk_numeral(div(r, rational::power_of_two(low)), sz);
-        return true;
-    }
-    // (extract[high:low] (extract[high2:low2] x)) == (extract[high+low2 : low+low2] x)
-    else if (is_app_of(a, m_fid, OP_EXTRACT)) {
-        expr * x = a->get_arg(0);
-        unsigned low2 = a->get_decl()->get_parameter(1).get_int();
-        return lookup_mk_extract(high + low2, low + low2, x, result);
-    }
-    //
-    // (extract[hi:lo] (bvXshr A c:bv[n])) -> (extract[hi+c:lo+c] A) 
-    //   if c < n, c <= lo <= hi < n - c
-    //
-    else if ((is_app_of(a, m_fid, OP_BASHR) || is_app_of(a, m_fid, OP_BLSHR)) &&
-             is_numeral(a->get_arg(1), r) && r.is_unsigned()) {
-        unsigned c = r.get_unsigned();
-        unsigned bv_size = get_bv_size(a);
-        if (c < bv_size && c <= low && high < bv_size - c) {
-            return lookup_mk_extract(high+c, low+c, a->get_arg(0), result);
-        }
-    }
-    // (concat a_0 ... a_{n-1})
-    // Remark: the least significant bits are stored in a_{n-1}
-    else if (is_app_of(a, m_fid, OP_CONCAT)) {
-        expr_ref_buffer new_args(m_manager);
-        unsigned i = a->get_num_args();
-        // look for first argument
-        while (i > 0) {
-            --i;
-            expr * a_i     = a->get_arg(i);
-            unsigned a_sz = get_bv_size(a_i);
-            TRACE("extract_bug", tout << "FIRST a_sz: " << a_sz << " high: " << high << " low: " << low << "\n" <<
-                  mk_pp(a_i, m_manager) << "\n";);
-            if (a_sz <= low) {
-                low  -= a_sz;
-                high -= a_sz;
-            }
-            else {
-                // found first argument
-                if (a_sz <= high) {
-                    expr_ref new_arg(m_manager);
-                    if (!lookup_mk_extract(a_sz - 1, low, a_i, new_arg)) {
-                        return false;
-                    }
-                    new_args.push_back(new_arg.get());
-                    unsigned num_consumed_bytes = a_sz - low;
-                    // I have to apply extract[sz - num_consumed_bytes - 1, 0] on the rest of concat
-                    high = (sz - num_consumed_bytes - 1);
-                    break;
-                }
-                else {
-                    return lookup_mk_extract(high, low, a_i, result);
-                }
-            }
-        }
-        TRACE("extract_bug", tout << " high: " << high << " low: " << low << "\n";);
-        
-        // look for remaining arguments
-        while (i > 0) {
-            --i;
-            expr * a_i     = a->get_arg(i);
-            unsigned a_sz = get_bv_size(a_i);
-            TRACE("extract_bug", tout << "SECOND a_sz: " << a_sz << " high: " << high << " " << 
-                  mk_pp( a_i, m_manager) << "\n";);
-            if (a_sz <= high) {
-                high  -= a_sz;
-                new_args.push_back(a_i);
-            }
-            else {
-                // found last argument
-                expr_ref new_arg(m_manager);
-                if (!lookup_mk_extract(high, 0, a_i, new_arg)) {
-                    return false;
-                }
-                new_args.push_back(new_arg.get());
-                // The arguments in new_args are in reverse order.
-                ptr_buffer<expr> rev_new_args;
-                unsigned i = new_args.size();
-                while (i > 0) {
-                    --i;
-                    rev_new_args.push_back(new_args[i]);
-                }
-                mk_concat(rev_new_args.size(), rev_new_args.c_ptr(), result);
-                return true;
-            }
-        }
-        UNREACHABLE();
-    }
-    else if (is_app_of(a, m_fid, OP_SIGN_EXT)) {
-        SASSERT(a->get_num_args() == 1);
-        unsigned bv_size = get_bv_size(a->get_arg(0));
-        if (high < bv_size) {
-            return lookup_mk_extract(high, low, a->get_arg(0), result);
-        }
-    }
-    else if (is_app_of(a, m_fid, OP_BAND) ||
-             is_app_of(a, m_fid, OP_BOR) ||
-             is_app_of(a, m_fid, OP_BXOR) ||
-             is_app_of(a, m_fid, OP_BNOR) ||
-             is_app_of(a, m_fid, OP_BNAND) ||
-             is_app_of(a, m_fid, OP_BNOT) ||
-             (low == 0 && is_app_of(a, m_fid, OP_BADD)) ||
-             (low == 0 && is_app_of(a, m_fid, OP_BMUL)) ||
-             (low == 0 && is_app_of(a, m_fid, OP_BSUB))) {
-        expr_ref_buffer new_args(m_manager);
-        bool all_found = true;
-        for (unsigned i = 0; i < a->get_num_args(); ++i) {
-            expr_ref new_arg(m_manager);
-            if (!lookup_mk_extract(high, low, a->get_arg(i), new_arg)) {
-                all_found = false;
-            }
-            new_args.push_back(new_arg.get());
-        }
-        if (!all_found) {
-            return false;
-        }
-        // We should not use mk_app because it does not guarantee that the result would be in simplified form.
-        // result = m_manager.mk_app(m_fid, a->get_decl_kind(), new_args.size(), new_args.c_ptr());
-        if (is_app_of(a, m_fid, OP_BAND))
-            mk_bv_and(new_args.size(), new_args.c_ptr(), result);
-        else if (is_app_of(a, m_fid, OP_BOR))
-            mk_bv_or(new_args.size(), new_args.c_ptr(), result);
-        else if (is_app_of(a, m_fid, OP_BXOR))
-            mk_bv_xor(new_args.size(), new_args.c_ptr(), result);
-        else if (is_app_of(a, m_fid, OP_BNOR))
-            mk_bv_nor(new_args.size(), new_args.c_ptr(), result);
-        else if (is_app_of(a, m_fid, OP_BNAND))
-            mk_bv_nand(new_args.size(), new_args.c_ptr(), result);
-        else if (is_app_of(a, m_fid, OP_BNOT)) {
-            SASSERT(new_args.size() == 1);
-            mk_bv_not(new_args[0], result);
-        }
-        else if (is_app_of(a, m_fid, OP_BADD))
-            mk_add(new_args.size(), new_args.c_ptr(), result);
-        else if (is_app_of(a, m_fid, OP_BMUL))
-            mk_mul(new_args.size(), new_args.c_ptr(), result);
-        else if (is_app_of(a, m_fid, OP_BSUB))
-            mk_sub(new_args.size(), new_args.c_ptr(), result);
-        else {
-            UNREACHABLE();
-        }
-        return true;
-    }
-    else if (m_manager.is_ite(a)) {
-        expr_ref then_b(m_manager), else_b(m_manager);
-        bool ok = lookup_mk_extract(high, low, a->get_arg(1), then_b);
-        ok = lookup_mk_extract(high, low, a->get_arg(2), else_b) && ok;
-        if (ok) {
-            m_bsimp.mk_ite(a->get_arg(0), then_b.get(), else_b.get(), result);
-        }
-        return ok;
-    }
-    result = m_util.mk_extract(high, low, arg);
-    return true;
-}
-
-/**
-   \brief Let f be the operator fid:k. Then, this function
-   store in result the flat args of n. If n is not an f application, then store n in result.
-
-   Example: if n is (f (f a b) (f c (f d e))), then a b c d e are stored in result.
-*/
-template<typename T>
-void get_assoc_args(family_id fid, decl_kind k, expr * n, T & result) {
-    ptr_buffer<expr> todo;
-    todo.push_back(n);
-    while (!todo.empty()) {
-        expr * n = todo.back();
-        todo.pop_back();
-        if (is_app_of(n, fid, k)) {
-            app * app = to_app(n);
-            unsigned    i   = app->get_num_args();
-            while (i > 0) {
-                --i;
-                todo.push_back(app->get_arg(i));
-            }
-        }
-        else {
-            result.push_back(n);
-        }
-    }
-}
-
-/**
-   \brief Similar to get_assoc_args, but the arguments are stored in reverse
-   other in result.
-*/
-template<typename T>
-void get_inv_assoc_args(family_id fid, decl_kind k, expr * n, T & result) {
-    ptr_buffer<expr> todo;
-    todo.push_back(n);
-    while (!todo.empty()) {
-        expr * n = todo.back();
-        todo.pop_back();
-        if (is_app_of(n, fid, k)) {
-            app * app     = to_app(n);
-            unsigned  num = app->get_num_args();
-            for (unsigned i = 0; i < num; i++) 
-                todo.push_back(app->get_arg(i));
-        }
-        else {
-            result.push_back(n);
-        }
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_eq(expr* a1, expr* a2, expr_ref& result) {
-    
-    rational val1;
-    rational val2;
-    bool is_num1 = is_numeral(a1, val1);
-    bool is_num2 = is_numeral(a2, val2);
-    if (is_num1 && is_num2 && val1 != val2) {
-        result = m_manager.mk_false();
-        return;
-    }
-
-    if (!m_util.is_concat(a1) && !is_num1) {
-        mk_eq_core(a1, a2, result);
-        return;
-    }
-    if (!m_util.is_concat(a2) && !is_num2) {
-        mk_eq_core(a1, a2, result);
-        return;
-    }
-    
-    ptr_buffer<expr> args1, args2;
-    get_inv_assoc_args(m_fid, OP_CONCAT, a1, args1);
-    get_inv_assoc_args(m_fid, OP_CONCAT, a2, args2);
-    TRACE("mk_bv_eq_concat", tout << mk_ll_pp(a1, m_manager) << "\n" << mk_ll_pp(a2, m_manager) << "\n";
-          tout << "args1:\n";
-          for (unsigned i = 0; i < args1.size(); i++) tout << mk_ll_pp(args1[i], m_manager) << "\n";
-          tout << "args2:\n";
-          for (unsigned i = 0; i < args2.size(); i++) tout << mk_ll_pp(args2[i], m_manager) << "\n";);
-
-          
-
-    expr_ref lhs(m_manager), rhs(m_manager), eq(m_manager);
-    expr_ref_buffer eqs(m_manager);
-    unsigned low1 = 0, low2 = 0;
-    ptr_buffer<expr>::iterator it1  = args1.begin();
-    ptr_buffer<expr>::iterator end1 = args1.end();
-    ptr_buffer<expr>::iterator it2  = args2.begin();
-    ptr_buffer<expr>::iterator end2 = args2.end();
-
-    while (it1 != end1 && it2 != end2) {
-        SASSERT(it1 != end1);
-        SASSERT(it2 != end2);
-        expr * arg1    = *it1;
-        expr * arg2    = *it2;
-        TRACE("expr_bv_util", tout << "low1: " << low1 << " low2: " << low2 << "\n";
-              tout << mk_pp(arg1, m_manager) << "\n";
-              tout << mk_pp(arg2, m_manager) << "\n";);
-        unsigned sz1  = get_bv_size(arg1);
-        unsigned sz2  = get_bv_size(arg2);
-        SASSERT(low1 < sz1 && low2 < sz2);
-        unsigned rsz1 = sz1 - low1;
-        unsigned rsz2 = sz2 - low2;
-        TRACE("expr_bv_util", tout << "rsz1: " << rsz1 << " rsz2: " << rsz2 << "\n";
-              tout << mk_pp(arg1, m_manager) << "\n";
-              tout << mk_pp(arg2, m_manager) << "\n";);
-
-        if (rsz1 == rsz2) {
-            mk_extract(sz1 - 1, low1, arg1, lhs);
-            mk_extract(sz2 - 1, low2, arg2, rhs);
-            low1 = 0;
-            low2 = 0;
-            ++it1;
-            ++it2;
-        }
-        else if (rsz1 < rsz2) {
-            mk_extract(sz1  - 1, low1, arg1, lhs);
-            mk_extract(rsz1 + low2 - 1, low2, arg2, rhs);
-            low1  = 0;
-            low2 += rsz1; 
-            ++it1;
-        }
-        else {
-            mk_extract(rsz2 + low1 - 1, low1, arg1, lhs);
-            mk_extract(sz2  - 1, low2, arg2, rhs);
-            low1 += rsz2;
-            low2  = 0;
-            ++it2;
-        }
-        mk_eq_core(lhs.get(), rhs.get(), eq);
-        eqs.push_back(eq.get());
-    }
-    m_bsimp.mk_and(eqs.size(), eqs.c_ptr(), result);
-}
-
-void bv_simplifier_plugin::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
-    TRACE("mk_eq_core", ast_ll_pp(tout, m_manager, arg1 ); ast_ll_pp(tout, m_manager, arg2););
-    if (arg1 == arg2) {
-        result = m_manager.mk_true();
-        return;
-    }
-    if ((m_util.is_bv_and(arg1) && m_util.is_allone(arg2)) || (m_util.is_bv_or(arg1) && m_util.is_zero(arg2))) {
-        mk_args_eq_numeral(to_app(arg1), arg2, result);
-        return;
-    }
-    if ((m_util.is_bv_and(arg2) && m_util.is_allone(arg1)) || (m_util.is_bv_or(arg2) && m_util.is_zero(arg1))) {
-        mk_args_eq_numeral(to_app(arg2), arg1, result);
-        return;
-    }
-
-#if 1
-    rational r;
-    unsigned num_bits = 0;
-    if (m_util.is_numeral(arg2, r, num_bits)) {
-        std::swap(arg1, arg2);
-    }
-    
-    if (m_util.is_numeral(arg1, r, num_bits) &&
-        (m_util.is_bv_and(arg2) || m_util.is_bv_or(arg2) || m_util.is_bv_not(arg2))) {
-        rational two(2);
-        expr_ref tmp(m_manager);
-        expr_ref_vector tmps(m_manager);
-        for (unsigned i = 0; i < num_bits; ++i) {
-            bool is_neg = (r % two).is_zero();
-            bit2bool_simplify(i, arg2, tmp);
-            if (is_neg) {
-                expr_ref tmp2(m_manager);
-                m_bsimp.mk_not(tmp, tmp2);
-                tmp = tmp2;
-            }
-            tmps.push_back(tmp);
-            r = div(r, two);
-        }        
-        m_bsimp.mk_and(tmps.size(), tmps.c_ptr(), result);
-        TRACE("mk_eq_bb", 
-              tout << mk_pp(arg1, m_manager) << "\n";
-              tout << mk_pp(arg2, m_manager) << "\n";
-              tout << mk_pp(result, m_manager) << "\n";);
-        return;
-    }
-#endif
-
-    if (!m_util.is_bv_add(arg1) && !m_util.is_bv_add(arg2) && 
-        !m_util.is_bv_mul(arg1) && !m_util.is_bv_mul(arg2)) {
-        m_bsimp.mk_eq(arg1, arg2, result);
-        return;
-    }
-    
-    set_curr_sort(arg1);
-    expr_ref_vector args1(m_manager);
-    expr_ref_vector args2(m_manager);
-    get_assoc_args(m_fid, OP_BADD, arg1, args1);
-    get_assoc_args(m_fid, OP_BADD, arg2, args2);
-    TRACE("mk_eq_core",
-          tout << mk_pp(arg1, m_manager) << "\n" << mk_pp(arg2, m_manager) << "\n";
-          tout << args1.size() << " " << args2.size() << "\n";);
-
-    unsigned idx2 = 0;
-    while (idx2 < args2.size()) {
-        expr * m2 = args2.get(idx2);
-        unsigned sz1  = args1.size();
-        unsigned idx1 = 0;
-        for (; idx1 < sz1; ++idx1) {
-            expr * m1 = args1.get(idx1);
-            if (eq_monomials_modulo_k(m1, m2)) {
-                expr_ref tmp(m_manager);
-                if (merge_monomials(true, m1, m2, tmp)) {
-                    args1.set(idx1, tmp.get());
-                }
-                else {
-                    // the monomial cancelled each other.
-                    args1.erase(idx1);
-                }
-                break;
-            }
-        }
-        if (idx1 == sz1) {
-            ++idx2;
-        }
-        else {
-            args2.erase(idx2);
-        }
-    }
-
-    expr_ref lhs(m_manager);
-    expr_ref rhs(m_manager);
-    mk_sum_of_monomials(args1, lhs);
-    mk_sum_of_monomials(args2, rhs);
-    m_bsimp.mk_eq(lhs.get(), rhs.get(), result);
-}
-
-void bv_simplifier_plugin::mk_args_eq_numeral(app * app, expr * n, expr_ref & result) {
-    expr_ref_buffer eqs(m_manager);
-    expr_ref eq(m_manager);
-    unsigned num = app->get_num_args();
-    for (unsigned i = 0; i < num; i++) {
-        mk_bv_eq(app->get_arg(i), n, eq);
-        eqs.push_back(eq.get());
-    }
-    m_bsimp.mk_and(eqs.size(), eqs.c_ptr(), result);
-}
-
-void bv_simplifier_plugin::mk_concat(unsigned num_args, expr * const * args, expr_ref & result) {
-    TRACE("bv_simplifier_plugin", tout << "mk_concat:\n";
-          for (unsigned i = 0; i < num_args; i++) ast_ll_pp(tout, m_manager, args[i]););
-    unsigned shift = 0;
-    numeral  val(0), arg_val;
-    for (unsigned i = num_args; i > 0; ) {
-        --i;
-        expr * arg   = args[i];
-        if (is_numeral(arg, arg_val)) {
-            arg_val    *= rational::power_of_two(shift);
-            val        += arg_val;
-            shift      += get_bv_size(arg);
-            TRACE("bv_simplifier_plugin", 
-                  tout << "val: " << val << " arg_val: " << arg_val << " shift: " << shift << "\n";);
-        }
-        else {
-            // one of the arguments is not a number 
-            result = m_manager.mk_app(m_fid, OP_CONCAT, num_args, args);
-            return;
-        }
-    }
-    
-    // all arguments are numerals
-    result = mk_numeral(val, shift);
-}
-
-void bv_simplifier_plugin::mk_bv_and(unsigned num_args, expr * const* args, expr_ref & result) {
-    ptr_buffer<expr> flat_args;
-    for (unsigned i = 0; i < num_args; ++i) {
-        flat_args.push_back(args[i]);
-    }
-    // expr_lt_proc is a total order on expressions.
-    std::sort(flat_args.begin(), flat_args.end(), expr_lt_proc(m_fid, OP_BNOT));
-    SASSERT(num_args > 0);
-
-    unsigned bv_size = get_bv_size(args[0]);
-
-    numeral allone = mk_allone(bv_size);
-    numeral val;
-
-    uint64 unit = bv_size <= 64 ? to_uint64(numeral(-1), bv_size) : 0;
-    numeral n_unit(allone);
- 
-    expr * prev  = 0;
-    ptr_buffer<expr>::iterator it  = flat_args.begin();
-    ptr_buffer<expr>::iterator it2 = it;
-    ptr_buffer<expr>::iterator end = flat_args.end();
-    for (; it != end; ++it) {
-        expr* n = *it;
-        if (prev && 
-            ((is_app_of(n, m_fid, OP_BNOT)    && to_app(n)->get_arg(0) == prev) ||
-             (is_app_of(prev, m_fid, OP_BNOT) && to_app(prev)->get_arg(0) == n))) {
-            result = mk_bv0(bv_size);
-            return;
-        }
-        else if (bv_size <= 64 && is_numeral(n, val)) {
-            unit &= to_uint64(val, bv_size);
-            if (unit == 0) {
-                result = mk_bv0(bv_size);
-                return;
-            }
-        }
-        else if (bv_size > 64 && is_numeral(n, val)) {
-            n_unit = mk_bv_and(val, n_unit, bv_size);
-            if (n_unit.is_zero()) {
-                result = mk_bv0(bv_size);
-                return;
-            }
-        }
-        else if (!prev || prev != n) {
-            *it2 = n;
-            prev = *it2;
-            ++it2;
-        }
-    }
-
-    if (bv_size <= 64) {
-        n_unit = numeral(unit, numeral::ui64());
-    }
-
-    flat_args.set_end(it2);
-    if (n_unit != allone) {
-        flat_args.push_back(mk_numeral(n_unit, bv_size));
-    }
-
-    unsigned sz = flat_args.size();
-    switch(sz) {
-    case 0:
-        result = mk_numeral(n_unit, bv_size);
-        break;
-    case 1:
-        result = flat_args.back();
-        break;
-    default:
-        result = mk_list_assoc_app(m_manager, m_fid, OP_BAND, sz, flat_args.c_ptr());
-        break;
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_or(unsigned num_args, expr * const* args, expr_ref & result) {
-#if 0
-    // Transformations for SAGE
-    // (bvor (concat 0 x) (concat y 0)) ==> (concat y x)
-    // (bvor (concat x 0) (concat 0 y)) ==> (concat x y)
-    if (num_args == 2 && 
-        m_util.is_concat(args[0]) && 
-        m_util.is_concat(args[1]) &&
-        to_app(args[0])->get_num_args() == 2 && 
-        to_app(args[1])->get_num_args() == 2) {
-        expr * x1 = to_app(args[0])->get_arg(0);
-        expr * x2 = to_app(args[0])->get_arg(1);
-        expr * y1 = to_app(args[1])->get_arg(0);
-        expr * y2 = to_app(args[1])->get_arg(1);
-        if (get_bv_size(x1) == get_bv_size(y1) && 
-            get_bv_size(x2) == get_bv_size(y2)) {
-            if (m_util.is_zero(x1) && m_util.is_zero(y2)) {
-                // (bvor (concat 0 x) (concat y 0)) ==> (concat y x)
-                mk_concat(y1, x2, result);
-                return;
-            }
-            if (m_util.is_zero(x2) && m_util.is_zero(y1)) {
-                // (bvor (concat x 0) (concat 0 y)) ==> (concat x y)
-                mk_concat(x1, y2, result);
-                return;
-            }
-        }
-    }
-    // Investigate why it did not work.
-#endif        
-
-    ptr_buffer<expr> flat_args;
-    for (unsigned i = 0; i < num_args; ++i) {
-        flat_args.push_back(args[i]);
-    }
-    std::sort(flat_args.begin(), flat_args.end(), expr_lt_proc(m_fid, OP_BNOT));
-    SASSERT(num_args > 0);
-
-    unsigned bv_size = get_bv_size(args[0]), sz;
-    numeral allone = mk_allone(bv_size);
-    numeral val;
-
-    uint64 unit = 0;
-    numeral n_unit(0);
-
-    expr * prev  = 0;
-    ptr_buffer<expr>::iterator it  = flat_args.begin();
-    ptr_buffer<expr>::iterator it2 = it;
-    ptr_buffer<expr>::iterator end = flat_args.end();
-    for (; it != end; ++it) {
-        expr* n = *it;
-        if (prev && 
-            ((is_app_of(n, m_fid, OP_BNOT)  && to_app(n)->get_arg(0) == prev) ||
-             (is_app_of(prev, m_fid, OP_BNOT) && to_app(prev)->get_arg(0) == n))) {
-            result = mk_numeral(allone, bv_size);
-            return;
-        }
-        else if (bv_size <= 64 && is_numeral(n, val)) {
-            unit |= to_uint64(val, bv_size);
-        }
-        else if (bv_size > 64 && is_numeral(n, val)) {
-            n_unit = mk_bv_or(val, n_unit, bv_size);
-        }
-        else if (!prev || prev != n) {
-            *it2 = n;
-            prev = *it2;
-            ++it2;
-        }
-    }
-
-    if (bv_size <= 64) {
-        n_unit = numeral(unit, numeral::ui64());
-    }
-
-    if (allone == n_unit) {
-        result = mk_numeral(allone, bv_size);
-        return;
-    }
-
-    flat_args.set_end(it2);
-    if (!n_unit.is_zero()) {
-        flat_args.push_back(mk_numeral(n_unit, bv_size));
-    }
-
-    sz = flat_args.size();
-    switch(sz) {
-    case 0:
-        result = mk_numeral(n_unit, bv_size);
-        break;
-    case 1:
-        result = flat_args.back();
-        break;
-    default:
-        result = mk_list_assoc_app(m_manager, m_fid, OP_BOR, sz, flat_args.c_ptr());
-        break;
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_xor(unsigned num_args, expr * const * args, expr_ref & result) {
-    ptr_buffer<expr> flat_args;
-    for (unsigned i = 0; i < num_args; ++i) {
-        flat_args.push_back(args[i]);
-    }
-    std::sort(flat_args.begin(), flat_args.end(), expr_lt_proc());
-    SASSERT(num_args > 0);
-
-    unsigned bv_size = get_bv_size(args[0]);
-    numeral val;
-    
-    uint64 unit = 0;
-    numeral n_unit(0);
-
-    expr * prev = 0;
-    ptr_buffer<expr>::iterator it  = flat_args.begin();
-    ptr_buffer<expr>::iterator it2 = it;
-    ptr_buffer<expr>::iterator end = flat_args.end();
-    for (; it != end; ++it) {
-        if (bv_size <= 64 && is_numeral(*it, val)) {
-            uint64 u = to_uint64(val, bv_size);
-            unit = u ^ unit;
-        }
-        else if (bv_size > 64 && is_numeral(*it, val)) {
-            n_unit = mk_bv_xor(n_unit, val, bv_size);
-        }
-        else if (prev != 0 && prev == *it) {
-            --it2; // remove prev
-            prev = 0;
-        }
-        else {
-            *it2 = *it;
-            prev = *it2;
-            ++it2;
-        }
-    }
-    flat_args.set_end(it2);
-
-    if (bv_size <= 64) {
-        n_unit = numeral(numeral(unit,numeral::ui64()));
-    }
-
-    if (!n_unit.is_zero()) {
-        flat_args.push_back(mk_numeral(n_unit, bv_size));
-    }
-
-    unsigned sz = flat_args.size();
-    switch(sz) {
-    case 0:
-        result = mk_numeral(n_unit, bv_size);
-        break;
-    case 1:
-        result = flat_args.back();
-        break;
-    default:
-        result = mk_list_assoc_app(m_manager, m_fid, OP_BXOR, flat_args.size(), flat_args.c_ptr());
-        break;
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_not(expr * arg, expr_ref & result) {
-    numeral val;
-    unsigned bv_size;
-    if (m_util.is_numeral(arg, val, bv_size)) {
-        if (bv_size <= 64) {
-            uint64 l    = bv_size;
-            uint64 mask = shift_left(1ull,l) - 1ull;
-            uint64 u    = val.get_uint64();
-            u           = mask & (~u);
-            result = mk_numeral(numeral(u, numeral::ui64()), bv_size);
-            TRACE("bv_not_bug", 
-                  tout << l << " " << mask << " " << u << "\n";
-                  tout << mk_pp(arg, m_manager) << "\n" << mk_pp(result, m_manager) << "\n";);
-        }
-        else {
-            numeral r = mk_bv_not(val, bv_size);
-            result = mk_numeral(r, bv_size);
-            TRACE("bv_not_bug", 
-                  tout << mk_pp(arg, m_manager) << "\n" << mk_pp(result, m_manager) << "\n";);   
-        }
-    }
-    else if (is_app_of(arg, m_fid, OP_BNOT)) {
-        result = to_app(arg)->get_arg(0);
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BNOT, arg);
-    }
-}
-
-void bv_simplifier_plugin::mk_zeroext(unsigned n, expr * arg, expr_ref & result) {
-    if (n == 0) {
-        result = arg;
-    }
-    else {
-        expr_ref zero(m_manager);
-        zero = mk_bv0(n);
-        mk_concat(zero.get(), arg, result);
-    }
-}
-
-void bv_simplifier_plugin::mk_repeat(unsigned n, expr * arg, expr_ref & result) {
-    ptr_buffer<expr> args;
-    for (unsigned i = 0; i < n; i++) {
-        args.push_back(arg);
-    }
-    mk_concat(args.size(), args.c_ptr(), result);
-}
-
-bool bv_simplifier_plugin::is_minus_one_core(expr * arg) const {
-    numeral r;
-    unsigned bv_size;
-    if (m_util.is_numeral(arg, r, bv_size)) {
-        numeral minus_one(-1);
-        minus_one = mod(minus_one, rational::power_of_two(bv_size));
-        return r == minus_one;
-    }
-    return false;
-}
-
-bool bv_simplifier_plugin::is_x_minus_one(expr * arg, expr * & x) {
-    if (is_add(arg) && to_app(arg)->get_num_args() == 2) {
-        if (is_minus_one_core(to_app(arg)->get_arg(0))) {
-            x = to_app(arg)->get_arg(1);
-            return true;
-        }
-        if (is_minus_one_core(to_app(arg)->get_arg(1))) {
-            x = to_app(arg)->get_arg(0);
-            return true;
-        }
-    }
-    return false;
-}
-
-void bv_simplifier_plugin::mk_bv_urem(expr * arg1, expr * arg2, expr_ref & result) {
-    numeral r1, r2;
-    unsigned bv_size;
-    bool is_num1 = m_util.is_numeral(arg1, r1, bv_size);
-    bool is_num2 = m_util.is_numeral(arg2, r2, bv_size);
-    bv_size = get_bv_size(arg1);
-
-    if (is_num2 && r2.is_zero() && !m_params.m_hi_div0) {
-        result = m_manager.mk_app(m_fid, OP_BUREM0, arg1);
-        return;
-    }
-
-    if (is_num1 && is_num2 && !r2.is_zero()) {
-        SASSERT(r1.is_nonneg() && r2.is_pos());
-        r1 %= r2;
-        result = mk_numeral(r1, bv_size);
-        return;
-    }
-
-    if (!m_params.m_hi_div0) {
-        // TODO: implement the optimization in this branch for the case the hardware interpretation is used for (x urem 0)
-        // urem(0, x) ==> ite(x = 0, urem0(x), 0)
-        if (is_num1 && r1.is_zero()) {
-            expr * zero        = arg1;
-            expr_ref urem0(m_manager), eq0(m_manager);
-            urem0 = m_manager.mk_app(m_fid, OP_BUREM0, 1, &zero);
-            m_bsimp.mk_eq(arg2, zero, eq0);
-            m_bsimp.mk_ite(eq0.get(), urem0.get(), zero, result);
-            TRACE("urem", 
-                  tout << "urem:\n";
-                  ast_ll_pp(tout, m_manager, arg1); ast_ll_pp(tout, m_manager, arg2);
-                  tout << "result:\n"; ast_ll_pp(tout, m_manager, result.get()););
-            return;
-        }
-        
-        // urem(x - 1, x) ==> ite(x = 0, urem0(x-1), x - 1) ==> ite(x = 0, urem0(-1), x - 1)
-        expr * x;
-        if (is_x_minus_one(arg1, x) && x == arg2) {
-            expr * x_minus_1 = arg1;
-            expr_ref zero(m_manager);
-            zero = mk_bv0(bv_size);
-            expr_ref minus_one(m_manager), urem0(m_manager), eq0(m_manager);
-            minus_one = mk_numeral(numeral::minus_one(), bv_size);
-            expr * minus_1 = minus_one.get();
-            urem0 = m_manager.mk_app(m_fid, OP_BUREM0, 1, &minus_1);
-            m_bsimp.mk_eq(arg2, zero.get(), eq0);
-            m_bsimp.mk_ite(eq0.get(), urem0.get(), x_minus_1, result);
-            TRACE("urem", 
-                  tout << "urem:\n";
-                  ast_ll_pp(tout, m_manager, arg1); ast_ll_pp(tout, m_manager, arg2);
-                  tout << "result:\n"; ast_ll_pp(tout, m_manager, result.get()););
-            return;
-        }
-    }
-
-    if (is_num2 || m_params.m_hi_div0) {
-        result = m_manager.mk_app(m_fid, OP_BUREM_I, arg1, arg2);
-    }
-    else {
-        bv_size = get_bv_size(arg2);
-        result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)),
-                                  m_manager.mk_app(m_fid, OP_BUREM0, arg1),
-                                  m_manager.mk_app(m_fid, OP_BUREM_I, arg1, arg2));
-    }
-}
-
-void bv_simplifier_plugin::mk_sign_extend(unsigned n, expr * arg, expr_ref & result) {
-    numeral r;
-    unsigned bv_size;
-    if (m_util.is_numeral(arg, r, bv_size)) {
-        unsigned result_bv_size = bv_size + n;
-        r = norm(r, bv_size, true);
-        r = mod(r, rational::power_of_two(result_bv_size));
-        result = mk_numeral(r, result_bv_size);
-        TRACE("mk_sign_extend", tout << "n: " << n << "\n"; 
-              ast_ll_pp(tout, m_manager, arg); tout << "====>\n"; 
-              ast_ll_pp(tout, m_manager, result.get()););
-        return;
-    }
-    parameter param(n);
-    result = m_manager.mk_app(m_fid, OP_SIGN_EXT, 1, &param, 1, &arg);
-}
-
-/**
-   Implement the following reductions
-   
-   (bvashr (bvashr a n1) n2) ==> (bvashr a (+ n1 n2))
-   (bvlshr (bvlshr a n1) n2) ==> (bvlshr a (+ n1 n2))
-   (bvshl (bvshl a n1) n2)   ==> (bvshl a (+ n1 n2))
-   when n1 and n2 are numerals.
-   Remark if (+ n1 n2) is greater than bv_size, we set (+ n1 n2) to bv_size 
-   
-   Return true if the transformation was applied and the result stored in 'result'.
-   Return false otherwise.
-*/
-bool bv_simplifier_plugin::shift_shift(bv_op_kind k, expr* arg1, expr* arg2, expr_ref& result) {
-    SASSERT(k == OP_BASHR || k == OP_BSHL || k == OP_BLSHR);
-    if (!is_app_of(arg1, m_fid, k))
-        return false;
-    expr * a  = to_app(arg1)->get_arg(0);
-    expr * n1 = to_app(arg1)->get_arg(1);
-    expr * n2 = arg2;
-    numeral r1, r2;
-    unsigned bv_size = UINT_MAX;
-    bool is_num1 = m_util.is_numeral(n1, r1, bv_size);
-    bool is_num2 = m_util.is_numeral(n2, r2, bv_size);
-    if (!is_num1 || !is_num2)
-        return false;
-    SASSERT(bv_size != UINT_MAX);
-    numeral r = r1 + r2;
-    if (r > numeral(bv_size))
-        r = numeral(bv_size);
-    switch (k) {
-    case OP_BASHR:
-        mk_bv_ashr(a, m_util.mk_numeral(r, bv_size), result);
-        break;
-    case OP_BLSHR:
-        mk_bv_lshr(a, m_util.mk_numeral(r, bv_size), result);
-        break;
-    default:
-        SASSERT(k == OP_BSHL);
-        mk_bv_shl(a, m_util.mk_numeral(r, bv_size), result);
-        break;
-    }
-    return true;
-}
-
-void bv_simplifier_plugin::mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result) {
-    // x << 0  ==  x
-    numeral r1, r2;
-    unsigned bv_size = get_bv_size(arg1);        
-    bool is_num1 = is_numeral(arg1, r1);
-    bool is_num2 = is_numeral(arg2, r2);
-
-    if (is_num2 && r2.is_zero()) {
-        result = arg1;
-    }
-    else if (is_num2 && r2 >= rational(bv_size)) {
-        result = mk_numeral(0, bv_size);        
-    }
-    else if (is_num2 && is_num1 && bv_size <= 64) {
-        SASSERT(r1.is_uint64() && r2.is_uint64());
-        SASSERT(r2.get_uint64() < bv_size);
-
-        uint64 r = shift_left(r1.get_uint64(), r2.get_uint64());
-        result   = mk_numeral(r, bv_size);
-    }
-    else if (is_num1 && is_num2) {
-        SASSERT(r2 < rational(bv_size));
-        SASSERT(r2.is_unsigned());
-        result = mk_numeral(r1 * rational::power_of_two(r2.get_unsigned()), bv_size);
-    }
-
-    //
-    // (bvshl x k) -> (concat (extract [n-1-k:0] x) bv0:k)
-    //
-    else if (is_num2 && r2.is_pos() && r2 < numeral(bv_size)) {
-        SASSERT(r2.is_unsigned());
-        unsigned r = r2.get_unsigned();
-        expr_ref tmp1(m_manager);
-        mk_extract(bv_size - r - 1, 0, arg1, tmp1);
-        expr_ref zero(m_manager);
-        zero = mk_bv0(r);
-        expr* args[2] = { tmp1.get(), zero.get() };
-        mk_concat(2, args, result);
-    }
-    else if (shift_shift(OP_BSHL, arg1, arg2, result)) {
-        // done
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BSHL, arg1, arg2);
-    }
-    TRACE("mk_bv_shl", 
-          tout << mk_pp(arg1, m_manager) << " << " 
-          << mk_pp(arg2, m_manager) << " = " 
-          << mk_pp(result.get(), m_manager) << "\n";);
-}
- 
-void bv_simplifier_plugin::mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result) {
-    // x >> 0 == x
-    numeral r1, r2;
-    unsigned bv_size = get_bv_size(arg1);        
-    bool is_num1 = is_numeral(arg1, r1);
-    bool is_num2 = is_numeral(arg2, r2);
-
-    if (is_num2 && r2.is_zero()) {
-        result = arg1;
-    }
-    else if (is_num2 && r2 >= rational(bv_size)) {
-        result = mk_numeral(rational(0), bv_size);
-    }
-    else if (is_num1 && is_num2 && bv_size <= 64) {        
-        SASSERT(r1.is_uint64()); 
-        SASSERT(r2.is_uint64());
-        uint64 r = shift_right(r1.get_uint64(), r2.get_uint64());
-        result   = mk_numeral(r, bv_size);
-    }
-    else if (is_num1 && is_num2) {
-        SASSERT(r2.is_unsigned());
-        unsigned sh = r2.get_unsigned();
-        r1 = div(r1, rational::power_of_two(sh));
-        result = mk_numeral(r1, bv_size);
-    }
-    //
-    // (bvlshr x k) -> (concat bv0:k (extract [n-1:k] x))
-    //
-    else if (is_num2 && r2.is_pos() && r2 < numeral(bv_size)) {
-        SASSERT(r2.is_unsigned());
-        unsigned r = r2.get_unsigned();
-        expr_ref tmp1(m_manager);
-        mk_extract(bv_size - 1, r, arg1, tmp1);
-        expr_ref zero(m_manager);
-        zero = mk_bv0(r);
-        expr* args[2] = {  zero.get(), tmp1.get() };
-        mk_concat(2, args, result);
-    }
-    else if (shift_shift(OP_BLSHR, arg1, arg2, result)) {
-        // done
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BLSHR, arg1, arg2);
-    }
-    TRACE("mk_bv_lshr", tout << mk_pp(arg1, m_manager) << " >> " << 
-          mk_pp(arg2, m_manager) << " = " << mk_pp(result.get(), m_manager) << "\n";);
-
-}
-
-
-void bv_simplifier_plugin::mk_int2bv(expr * arg, sort* range, expr_ref & result) {
-    numeral val;
-    bool is_int;
-    unsigned bv_size = get_bv_size(range);
-    
-    if (m_arith.is_numeral(arg, val, is_int)) {
-        result = mk_numeral(val, bv_size);
-    }
-    // (int2bv (bv2int x)) == x
-    else if (is_app_of(arg, m_fid, OP_BV2INT) && bv_size == get_bv_size(to_app(arg)->get_arg(0))) {
-        result = to_app(arg)->get_arg(0);
-    }
-    else {
-        parameter parameter(bv_size);
-        result = m_manager.mk_app(m_fid, OP_INT2BV, 1, &parameter, 1, &arg);
-        SASSERT(result.get());
-    }
-}
-
-void bv_simplifier_plugin::mk_bv2int(expr * arg, sort* range, expr_ref & result) {
-    if (!m_params.m_bv2int_distribute) {
-        parameter parameter(range);
-        result = m_manager.mk_app(m_fid, OP_BV2INT, 1, &parameter, 1, &arg);
-        return;
-    }
-    numeral v;
-    if (is_numeral(arg, v)) {
-        result = m_arith.mk_numeral(v, true);
-    }
-    else if (is_mul_no_overflow(arg)) {
-        expr_ref tmp1(m_manager), tmp2(m_manager);
-        mk_bv2int(to_app(arg)->get_arg(0), range, tmp1);
-        mk_bv2int(to_app(arg)->get_arg(1), range, tmp2);
-        result = m_arith.mk_mul(tmp1, tmp2);        
-    }
-    else if (is_add_no_overflow(arg)) {
-        expr_ref tmp1(m_manager), tmp2(m_manager);
-        mk_bv2int(to_app(arg)->get_arg(0), range, tmp1);
-        mk_bv2int(to_app(arg)->get_arg(1), range, tmp2);
-        result = m_arith.mk_add(tmp1, tmp2);        
-    }
-    // commented out to reproduce bug in reduction of int2bv/bv2int
-    else if (m_util.is_concat(arg) && to_app(arg)->get_num_args() > 0) {
-        expr_ref_vector args(m_manager);        
-        unsigned num_args = to_app(arg)->get_num_args();
-        for (unsigned i = 0; i < num_args; ++i) {
-            expr_ref tmp(m_manager);
-            mk_bv2int(to_app(arg)->get_arg(i), range, tmp);
-            args.push_back(tmp);
-        }
-        unsigned sz = get_bv_size(to_app(arg)->get_arg(num_args-1));
-        for (unsigned i = num_args - 1; i > 0; ) {
-            expr_ref tmp(m_manager);
-            --i;
-            tmp = args[i].get();
-            tmp = m_arith.mk_mul(m_arith.mk_numeral(power(numeral(2), sz), true), tmp);
-            args[i] = tmp;
-            sz += get_bv_size(to_app(arg)->get_arg(i));
-        }
-        result = m_arith.mk_add(args.size(), args.c_ptr());
-    }
-    else {
-        parameter parameter(range);
-        result = m_manager.mk_app(m_fid, OP_BV2INT, 1, &parameter, 1, &arg);
-    }
-    SASSERT(m_arith.is_int(m_manager.get_sort(result.get())));
-}
-
-unsigned bv_simplifier_plugin::num_leading_zero_bits(expr* e) {
-    numeral v;
-    unsigned sz = get_bv_size(e);
-    if (is_numeral(e, v)) {
-        while (v.is_pos()) {
-            SASSERT(sz > 0);
-            --sz;
-            v = div(v, numeral(2));
-        }
-        return sz;
-    }
-    else if (m_util.is_concat(e)) {
-        app* a = to_app(e);
-        unsigned sz1 = get_bv_size(a->get_arg(0));
-        unsigned nb1 = num_leading_zero_bits(a->get_arg(0));
-        if (sz1 == nb1) {
-            nb1 += num_leading_zero_bits(a->get_arg(1));
-        }
-        return nb1;
-    }
-    return 0;
-}
-
-bool bv_simplifier_plugin::is_mul_no_overflow(expr* e) {
-    if (!is_mul(e)) {
-        return false;
-    }
-    expr* e1 = to_app(e)->get_arg(0);
-    expr* e2 = to_app(e)->get_arg(1);
-    unsigned sz = get_bv_size(e1);
-    unsigned nb1 = num_leading_zero_bits(e1);
-    unsigned nb2 = num_leading_zero_bits(e2);
-    return nb1 + nb2 >= sz;
-}
-
-bool bv_simplifier_plugin::is_add_no_overflow(expr* e) {
-    if (!is_add(e)) {
-        return false;
-    }
-    expr* e1 = to_app(e)->get_arg(0);
-    expr* e2 = to_app(e)->get_arg(1);
-    unsigned nb1 = num_leading_zero_bits(e1);
-    unsigned nb2 = num_leading_zero_bits(e2);
-    return nb1 > 0 && nb2 > 0;
-}
-
-
-
-// Macro for generating mk_bv_sdiv_i, mk_bv_udiv_i, mk_bv_srem_i, mk_bv_urem_i and mk_bv_smod_i.
-// These are essentially evaluators for the arg1 and arg2 are numerals.
-// Q: Why do we need them?
-// A: A constant may be eliminated using substitution. Its value is computed using the evaluator.
-//    Example: Suppose we have the top-level atom (= x (bvsrem_i a b)), and x is eliminated.
-#define MK_FIXED_DIV_I(NAME, OP)                                                                \
-void bv_simplifier_plugin::NAME##_i(expr * arg1, expr * arg2, expr_ref & result) {              \
-    numeral r1, r2;                                                                             \
-    unsigned bv_size;                                                                           \
-    bool is_num1 = m_util.is_numeral(arg1, r1, bv_size);                                        \
-    bool is_num2 = m_util.is_numeral(arg2, r2, bv_size);                                        \
-    if (is_num1 && is_num2 && !r2.is_zero()) {                                                  \
-        NAME(arg1, arg2, result);                                                               \
-    }                                                                                           \
-    else  {                                                                                     \
-        result = m_manager.mk_app(m_fid, OP, arg1, arg2);                                       \
-    }                                                                                           \
-}
-
-MK_FIXED_DIV_I(mk_bv_sdiv, OP_BSDIV_I)
-MK_FIXED_DIV_I(mk_bv_udiv, OP_BUDIV_I)
-MK_FIXED_DIV_I(mk_bv_srem, OP_BSREM_I)
-MK_FIXED_DIV_I(mk_bv_urem, OP_BUREM_I)
-MK_FIXED_DIV_I(mk_bv_smod, OP_BSMOD_I)
-
-void bv_simplifier_plugin::mk_bv_sdiv(expr* arg1, expr* arg2, expr_ref& result) {
-    numeral r1, r2;
-    unsigned bv_size;
-    bool is_num1 = m_util.is_numeral(arg1, r1, bv_size);
-    bool is_num2 = m_util.is_numeral(arg2, r2, bv_size);
-
-    if (is_num2 && r2.is_zero() && !m_params.m_hi_div0) {
-        result = m_manager.mk_app(m_fid, OP_BSDIV0, arg1);
-    }
-    else if (is_num1 && is_num2 && !r2.is_zero()) {
-        r1 = norm(r1, bv_size, true);
-        r2 = norm(r2, bv_size, true);
-        result = mk_numeral(machine_div(r1, r2), bv_size);
-    }
-    else if (is_num2 || m_params.m_hi_div0) {
-        result = m_manager.mk_app(m_fid, OP_BSDIV_I, arg1, arg2);
-    }
-    else {
-        bv_size = get_bv_size(arg2);
-        result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)),
-                                  m_manager.mk_app(m_fid, OP_BSDIV0, arg1),
-                                  m_manager.mk_app(m_fid, OP_BSDIV_I, arg1, arg2));
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_udiv(expr* arg1, expr* arg2, expr_ref& result) {
-    numeral r1, r2;
-    unsigned bv_size;
-    bool is_num1 = m_util.is_numeral(arg1, r1, bv_size);
-    bool is_num2 = m_util.is_numeral(arg2, r2, bv_size);
-
-    if (is_num2 && r2.is_zero() && !m_params.m_hi_div0) {
-        result = m_manager.mk_app(m_fid, OP_BUDIV0, arg1);
-    }
-    else if (is_num1 && is_num2 && !r2.is_zero()) {
-        SASSERT(r1.is_nonneg());
-        SASSERT(r2.is_nonneg());
-        result = mk_numeral(machine_div(r1, r2), bv_size);
-    }
-    else if (is_num2 || m_params.m_hi_div0) {
-        result = m_manager.mk_app(m_fid, OP_BUDIV_I, arg1, arg2);
-    }
-    else {
-        bv_size = get_bv_size(arg2);
-        result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)),
-                                  m_manager.mk_app(m_fid, OP_BUDIV0, arg1),
-                                  m_manager.mk_app(m_fid, OP_BUDIV_I, arg1, arg2));
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_srem(expr* arg1, expr* arg2, expr_ref& result) {
-    numeral r1, r2;
-    unsigned bv_size;
-    bool is_num1 = m_util.is_numeral(arg1, r1, bv_size);
-    bool is_num2 = m_util.is_numeral(arg2, r2, bv_size);
-
-    if (is_num2 && r2.is_zero() && !m_params.m_hi_div0) {
-        result = m_manager.mk_app(m_fid, OP_BSREM0, arg1);
-    }
-    else if (is_num1 && is_num2 && !r2.is_zero()) {
-        r1 = norm(r1, bv_size, true);
-        r2 = norm(r2, bv_size, true);
-        result = mk_numeral(r1 % r2, bv_size);
-    }
-    else if (is_num2 || m_params.m_hi_div0) {
-        result = m_manager.mk_app(m_fid, OP_BSREM_I, arg1, arg2);
-    }
-    else {
-        bv_size = get_bv_size(arg2);
-        result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)),
-                                  m_manager.mk_app(m_fid, OP_BSREM0, arg1),
-                                  m_manager.mk_app(m_fid, OP_BSREM_I, arg1, arg2));
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_smod(expr* arg1, expr* arg2, expr_ref& result) {
-    numeral r1, r2;
-    unsigned bv_size;
-    bool is_num1 = m_util.is_numeral(arg1, r1, bv_size);
-    bool is_num2 = m_util.is_numeral(arg2, r2, bv_size);
-
-    if (is_num1)
-        r1 = m_util.norm(r1, bv_size, true);
-    if (is_num2)
-        r2 = m_util.norm(r2, bv_size, true);
-    
-    TRACE("bv_simplifier", 
-          tout << mk_pp(arg1, m_manager) << " smod " << mk_pp(arg2, m_manager) << "\n";
-          );
-
-
-    if (is_num2 && r2.is_zero()) {
-        if (!m_params.m_hi_div0)
-            result = m_manager.mk_app(m_fid, OP_BSMOD0, arg1);
-        else
-            result = arg1;
-    }
-    else if (is_num1 && is_num2) {
-        SASSERT(!r2.is_zero());
-        numeral abs_r1 = m_util.norm(abs(r1), bv_size);
-        numeral abs_r2 = m_util.norm(abs(r2), bv_size);
-        numeral u      = m_util.norm(abs_r1 % abs_r2, bv_size);
-        numeral r;
-        if (u.is_zero())
-            r = u;
-        else if (r1.is_pos() && r2.is_pos())
-            r = u;
-        else if (r1.is_neg() && r2.is_pos())
-            r = m_util.norm(-u + r2, bv_size);
-        else if (r1.is_pos() && r2.is_neg())
-            r = m_util.norm(u + r2, bv_size);
-        else
-            r = m_util.norm(-u, bv_size);
-        result = mk_numeral(r, bv_size);
-    }
-    else if (is_num2 || m_params.m_hi_div0) {
-        result = m_manager.mk_app(m_fid, OP_BSMOD_I, arg1, arg2);
-    }
-    else {
-        bv_size = get_bv_size(arg2);
-        result = m_manager.mk_ite(m_manager.mk_eq(arg2, mk_numeral(0, bv_size)),
-                                  m_manager.mk_app(m_fid, OP_BSMOD0, arg1),
-                                  m_manager.mk_app(m_fid, OP_BSMOD_I, arg1, arg2));
-    }
-}
-
-uint64 bv_simplifier_plugin::n64(expr* e) {
-    numeral r;
-    unsigned bv_size;
-    if (m_util.is_numeral(e, r, bv_size) && bv_size <= 64) {
-        return r.get_uint64();
-    }
-    UNREACHABLE();
-    return 0;
-}
-
-rational bv_simplifier_plugin::num(expr* e) {
-    numeral r;
-    unsigned bv_size;
-    if (!m_util.is_numeral(e, r, bv_size)) {
-        UNREACHABLE();
-    }
-    return r;
-
-}
-
-void bv_simplifier_plugin::mk_bv_nand(unsigned num_args, expr* const* args, expr_ref& result) {
-    unsigned bv_size;
-    if (are_numerals(num_args, args, bv_size)) {
-        if (bv_size <= 64) {
-            uint64 r = n64(args[0]);
-            for (unsigned i = 1; i < num_args; i++) {
-                r &= n64(args[i]);
-            }
-            result = mk_numeral(~r, bv_size);
-        }
-        else {
-            numeral r = num(args[0]);
-            for (unsigned i = 1; i < num_args; i++) {
-                r = mk_bv_and(r, num(args[i]), bv_size);
-            }
-            result = mk_numeral(mk_bv_not(r, bv_size), bv_size);
-        }
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BNAND, num_args, args);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_nor(unsigned num_args, expr* const* args, expr_ref& result) {
-    unsigned bv_size;
-    if (are_numerals(num_args, args, bv_size)) {
-        if (bv_size <= 64) {
-            uint64 r = n64(args[0]);
-            for (unsigned i = 1; i < num_args; i++) {
-                r |= n64(args[i]);
-            }
-            result = mk_numeral(~r, bv_size);
-        }
-        else {
-            numeral r = num(args[0]);
-            for (unsigned i = 1; i < num_args; i++) {
-                r = mk_bv_or(r, num(args[i]), bv_size);
-            }
-            result = mk_numeral(mk_bv_not(r, bv_size), bv_size);
-        }
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BNOR, num_args, args);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_xnor(unsigned num_args, expr* const* args, expr_ref& result) {
-    unsigned bv_size;
-    if (are_numerals(num_args, args, bv_size)) {
-        if (bv_size <= 64) {
-            uint64 r = n64(args[0]);
-            for (unsigned i = 1; i < num_args; i++) {
-                r ^= n64(args[i]);
-            }
-            result = mk_numeral(~r, bv_size);
-        }
-        else {
-            numeral r = num(args[0]);
-            for (unsigned i = 1; i < num_args; i++) {
-                r = mk_bv_xor(r, num(args[i]), bv_size);
-            }
-            result = mk_numeral(mk_bv_not(r, bv_size), bv_size);
-        }
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BXNOR, num_args, args);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_rotate_left_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result) {
-    SASSERT(shift < bv_size);
-    if (bv_size <= 64) {
-        uint64 a       = r.get_uint64();
-        uint64 r       = shift_left(a, shift) | shift_right(a, bv_size - shift);
-        result = mk_numeral(r, bv_size);        
-    }
-    else {
-        rational r1 = div(r, rational::power_of_two(bv_size - shift)); // shift right
-        rational r2 = (r * rational::power_of_two(shift)) % rational::power_of_two(bv_size); // shift left
-        result = mk_numeral(r1 + r2, bv_size);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_rotate_left(func_decl* f, expr* arg, expr_ref& result) {
-    numeral r;
-    unsigned bv_size;
-    SASSERT(f->get_decl_kind() == OP_ROTATE_LEFT);
-    if (m_util.is_numeral(arg, r, bv_size)) {
-        unsigned shift   = f->get_parameter(0).get_int() % bv_size;
-        mk_bv_rotate_left_core(shift, r, bv_size, result);
-    }
-    else {
-        result = m_manager.mk_app(f, arg);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_rotate_right_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result) {
-    SASSERT(shift < bv_size);
-    if (bv_size <= 64) {
-        uint64 a       = r.get_uint64();
-        uint64 r       = shift_right(a, shift) | shift_left(a, bv_size - shift);
-        result = mk_numeral(r, bv_size);
-    }
-    else {
-        rational r1 = div(r, rational::power_of_two(shift)); // shift right
-        rational r2 = (r * rational::power_of_two(bv_size - shift)) % rational::power_of_two(bv_size); // shift left
-        result = mk_numeral(r1 + r2, bv_size);            
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_rotate_right(func_decl* f, expr* arg, expr_ref& result) {
-    numeral r;
-    unsigned bv_size;
-    SASSERT(f->get_decl_kind() == OP_ROTATE_RIGHT);
-    if (m_util.is_numeral(arg, r, bv_size)) {
-        unsigned shift   = f->get_parameter(0).get_int() % bv_size;
-        mk_bv_rotate_right_core(shift, r, bv_size, result);
-    }
-    else {
-        result = m_manager.mk_app(f, arg);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_redor(expr* arg, expr_ref& result) {
-    if (is_numeral(arg)) {
-        result = m_util.is_zero(arg)?mk_numeral(0, 1):mk_numeral(1,1);
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BREDOR, arg);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_redand(expr* arg, expr_ref& result) {
-    numeral r;
-    unsigned bv_size;
-    if (m_util.is_numeral(arg, r, bv_size)) {
-        numeral allone = mk_allone(bv_size);
-        result = mk_numeral((r == allone)?1:0, 1);
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BREDAND, arg);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_comp(expr* arg1, expr* arg2, expr_ref& result) {
-    numeral r1, r2;
-    if (arg1 == arg2) {
-        result = mk_numeral(1,1);
-    }
-    else if (is_numeral(arg1, r1) && is_numeral(arg2, r2)) {
-        result = mk_numeral((r1 == r2)?1:0, 1);
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BCOMP, arg1, arg2);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_ashr(expr* arg1, expr* arg2, expr_ref& result) {
-    numeral r1, r2;
-    unsigned bv_size = get_bv_size(arg1);
-    bool is_num1 = m_util.is_numeral(arg1, r1, bv_size);
-    bool is_num2 = m_util.is_numeral(arg2, r2, bv_size);
-
-    if (bv_size == 0) {
-        result = mk_numeral(rational(0), bv_size);
-    }
-    else if (is_num2 && r2.is_zero()) {
-        result = arg1;
-    }
-    else if (bv_size <= 64 && is_num1 && is_num2) {
-        uint64 n1      = n64(arg1);
-        uint64 n2_orig = n64(arg2);
-        uint64 n2             = n2_orig % bv_size;
-        SASSERT(n2 < bv_size);
-        uint64 r       = shift_right(n1, n2);
-        bool   sign    = (n1 & shift_left(1ull, bv_size - 1ull)) != 0;
-        if (n2_orig > n2) {
-            if (sign) {
-                r = shift_left(1ull, bv_size) - 1ull;
-            }
-            else {
-                r = 0;
-            }
-        }
-        else if (sign) {
-            uint64 allone  = shift_left(1ull, bv_size) - 1ull;
-            uint64 mask    = ~(shift_left(1ull, bv_size - n2) - 1ull);
-            mask          &= allone;
-            r |= mask;
-        }
-        result = mk_numeral(r, bv_size);
-        TRACE("bv", tout << mk_pp(arg1, m_manager) << " >> " 
-              << mk_pp(arg2, m_manager) << " = " 
-              << mk_pp(result.get(), m_manager) << "\n";
-              tout << n1 << " >> " << n2 << " = " << r << "\n";
-              );
-    }
-    else if (is_num1 && is_num2 && rational(bv_size) <= r2) {
-        if (has_sign_bit(r1, bv_size)) {
-            result = mk_numeral(mk_allone(bv_size), bv_size);
-        }
-        else {
-            result = mk_bv0(bv_size);
-        }
-    }
-    else if (is_num1 && is_num2) {
-        SASSERT(r2 < rational(bv_size));
-        bool   sign = has_sign_bit(r1, bv_size);
-        r1 = div(r1, rational::power_of_two(r2.get_unsigned()));
-        if (sign) {
-            // pad ones.
-            rational p(1);
-            for (unsigned i = 0; i < bv_size; ++i) {
-                if (r1 < p) {
-                    r1 += p;
-                }
-                p *= rational(2);
-            }
-        }
-        result = mk_numeral(r1, bv_size);
-        
-    }
-    else if (shift_shift(OP_BASHR, arg1, arg2, result)) {
-        // done
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_BASHR, arg1, arg2);
-    }
-}
-
-void bv_simplifier_plugin::mk_bv_ext_rotate_right(expr* arg1, expr* arg2, expr_ref& result) {
-    numeral r2;
-    unsigned bv_size;
-    if (m_util.is_numeral(arg2, r2, bv_size)) {
-        unsigned shift = static_cast<unsigned>((r2 % numeral(bv_size)).get_uint64() % static_cast<uint64>(bv_size));
-        numeral r1;
-        if (is_numeral(arg1, r1)) {
-            mk_bv_rotate_right_core(shift, r1, bv_size, result);
-        }
-        else {
-            parameter p(shift);
-            result = m_manager.mk_app(m_fid, OP_ROTATE_RIGHT, 1, &p, 1, &arg1);
-        }
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_EXT_ROTATE_RIGHT, arg1, arg2);
-    }
-}
-
-
-void bv_simplifier_plugin::mk_bv_ext_rotate_left(expr* arg1, expr* arg2, expr_ref& result) {
-    numeral r2;
-    unsigned bv_size;
-    if (m_util.is_numeral(arg2, r2, bv_size)) {
-        unsigned shift = static_cast<unsigned>((r2 % numeral(bv_size)).get_uint64() % static_cast<uint64>(bv_size));
-        numeral r1;
-        if (is_numeral(arg1, r1)) {
-            mk_bv_rotate_left_core(shift, r1, bv_size, result);
-        }
-        else {
-            parameter p(shift);
-            result = m_manager.mk_app(m_fid, OP_ROTATE_LEFT, 1, &p, 1, &arg1);
-        }
-    }
-    else {
-        result = m_manager.mk_app(m_fid, OP_EXT_ROTATE_LEFT, arg1, arg2);
-    }
-}
-
-void bv_simplifier_plugin::bit2bool_simplify(unsigned idx, expr* e, expr_ref& result) {
-
-    parameter p(idx);
-
-    ptr_vector<expr> todo;
-    expr_ref_vector pinned(m_manager);
-    ptr_vector<app> cache;
-    todo.push_back(e);
-    expr* e0 = e;
-    ptr_vector<expr> argv;
-    expr_ref tmp(m_manager);
-    while (!todo.empty()) {
-        e = todo.back();
-        unsigned e_id = e->get_id();
-        if (e_id >= cache.size()) {
-            cache.resize(e_id+1,0);
-        }
-        if (cache[e_id]) {
-            todo.pop_back();
-            continue;
-        }
-        if (!m_util.is_numeral(e) &&
-            !m_util.is_bv_and(e) &&
-            !m_util.is_bv_or(e) &&
-            !(is_app_of(e, m_fid, OP_BXOR) && to_app(e)->get_num_args() == 2) &&
-            !m_manager.is_ite(e) &&
-            !m_util.is_concat(e) &&
-            !m_util.is_bv_not(e)) {
-            expr_ref extr(m_manager);
-            extr = m_util.mk_extract(idx, idx, e);
-            cache[e_id] = m_manager.mk_eq(m_util.mk_numeral(1, 1), extr);
-            pinned.push_back(cache[e_id]);
-            todo.pop_back();
-            continue;
-        }
-        app* a = to_app(e);
-        unsigned sz = a->get_num_args();
-        if (m_util.is_concat(e)) {
-            // look for first argument
-            unsigned idx1 = idx;
-            while (sz > 0) {
-                --sz;
-                expr * a_i     = a->get_arg(sz);
-                unsigned a_sz = get_bv_size(a_i);
-                if (a_sz <= idx1) {
-                    idx1 -= a_sz;
-                }
-                else {
-                    // idx < a_sz;
-                    bit2bool_simplify(idx1, a_i, tmp);
-                    pinned.push_back(tmp);
-                    cache[e_id] = to_app(tmp);
-                    break;
-                }
-            }
-            todo.pop_back();
-            continue;
-        }
-        argv.reset();
-        for (unsigned i = 0; i < sz; ++i) {
-            expr* arg_i = a->get_arg(i);
-            if (i == 0 && m_manager.is_ite(e)) {
-                argv.push_back(arg_i);
-            }
-            else if (cache.size() > arg_i->get_id() && cache[arg_i->get_id()]) {
-                argv.push_back(cache[arg_i->get_id()]);
-            }
-            else {
-                todo.push_back(arg_i);
-            }
-        }
-        if (sz != argv.size()) {
-            continue;
-        }
-        todo.pop_back();
-        rational val;
-        unsigned num_bits;
-        if (m_util.is_numeral(e, val, num_bits)) {
-            rational two(2);
-            for (unsigned i = 0; i < idx; ++i) {
-                val = div(val, two);
-            }  
-            bool is_pos = !(val % two).is_zero();
-            tmp = is_pos?m_manager.mk_true():m_manager.mk_false();
-        }
-        else if (m_util.is_bv_and(e)) {
-            //tmp = m_manager.mk_and(sz, argv.c_ptr());
-            m_bsimp.mk_and(sz, argv.c_ptr(), tmp);
-            pinned.push_back(tmp);
-        }
-        else if (m_util.is_bv_or(e)) {
-            //tmp = m_manager.mk_or(sz, argv.c_ptr());
-            m_bsimp.mk_or(sz, argv.c_ptr(), tmp);
-            pinned.push_back(tmp);
-        }
-        else if (m_util.is_bv_not(e)) {
-            //tmp = m_manager.mk_not(argv[0]);
-            m_bsimp.mk_not(argv[0], tmp);
-            pinned.push_back(tmp);
-        }
-        else if (is_app_of(e, m_fid, OP_BXOR)) {
-            SASSERT(argv.size() == 2);
-            m_bsimp.mk_xor(argv[0], argv[1], tmp);
-            pinned.push_back(tmp);
-        }
-        else if (m_manager.is_ite(e)) {
-            //tmp = m_manager.mk_ite(argv[0], argv[1], argv[2]);
-            m_bsimp.mk_ite(argv[0], argv[1], argv[2], tmp);
-            pinned.push_back(tmp);
-        }
-        else {
-            UNREACHABLE();
-        }
-        cache[e_id] = to_app(tmp);
-    }
-    result = cache[e0->get_id()]; 
-}
-
-
-// replace addition by concatenation.
-void bv_simplifier_plugin::mk_add_concat(expr_ref& result) {
-    if (!m_util.is_bv_add(result)) {
-        return;
-    }
-    app* a = to_app(result);
-    if (a->get_num_args() != 2) {
-        return;
-    }
-    expr* x = a->get_arg(0);
-    expr* y = a->get_arg(1);
-    if (!m_util.is_concat(x)) {
-        std::swap(x, y);
-    }
-    if (!m_util.is_concat(x)) {
-        return;
-    }
-    unsigned sz = m_util.get_bv_size(x);
-
-#if 0
-    // optimzied version. Seems not worth it..
-#define UPDATE_CURR(_curr1, _idx1,_x,_is_num, _i)                       \
-    if (_idx1 >= m_util.get_bv_size(_curr1)) {                          \
-        _curr1 = _x;                                                    \
-        _idx1 = _i;                                                     \
-        _is_num = false;                                                \
-    }                                                                   \
-    while (m_util.is_concat(_curr1)) {                                  \
-        _is_num = false;                                                \
-        unsigned num_args = to_app(_curr1)->get_num_args();             \
-        while (true) {                                                  \
-            --num_args;                                                 \
-            expr* c1 = to_app(_curr1)->get_arg(num_args);               \
-            unsigned sz1 = m_util.get_bv_size(c1);                      \
-            if (sz1 < _idx1) {                                          \
-                _idx1 -= sz1;                                           \
-            }                                                           \
-            else {                                                      \
-                _curr1 = c1;                                            \
-                break;                                                  \
-            }                                                           \
-        }                                                               \
-    }
-    
-    unsigned idx1 = 0, idx2 = 0;
-    expr* curr1 = x, *curr2 = y;
-    bool is_num1 = false, is_num2 = false;
-    rational val1, val2;
-    rational two(2);
-    for (unsigned i = 0; i < sz; ++i, ++idx1, ++idx2) {
-        UPDATE_CURR(curr1, idx1, x, is_num1, i);
-        UPDATE_CURR(curr2, idx2, y, is_num2, i);
-        if (idx1 == 0 && m_util.is_numeral(curr1, val1, bv_size)) {
-            is_num1 = true;
-        }
-        if (idx2 == 0 && m_util.is_numeral(curr2, val2, bv_size)) {
-            is_num2 = true;
-        }
-        if ((is_num1 && (val1 % two).is_zero()) ||
-            (is_num2 && (val2 % two).is_zero())) {
-            val1 = div(val1, two);
-            val2 = div(val2, two);
-                continue;
-        }
-        return;
-    }
-    mk_bv_or(2, a->get_args(), result);
-#endif
-    
-    for (unsigned i = 0; i < sz; ++i) {
-        if (!is_zero_bit(x,i) && !is_zero_bit(y,i)) {
-            return;
-        }
-    }
-    mk_bv_or(2, a->get_args(), result);
-}
-
-bool bv_simplifier_plugin::is_zero_bit(expr* x, unsigned idx) {
-    rational val;
-    unsigned bv_size;
-    if (m_util.is_numeral(x, val, bv_size)) {
-        if (val.is_zero()) {
-            return true;
-        }
-        rational two(2);
-        while (idx > 0) {
-            val = div(val, two);
-            idx--;
-        }
-        return (val % two).is_zero();
-    }
-    if (m_util.is_concat(x)) {
-        unsigned num_args = to_app(x)->get_num_args();
-        while (num_args > 0) {
-            --num_args;
-            expr* y = to_app(x)->get_arg(num_args);
-            bv_size = m_util.get_bv_size(y);
-            if (bv_size <= idx) {
-                idx -= bv_size;
-            }
-            else {
-                return is_zero_bit(y, idx);
-            }
-        }
-        UNREACHABLE();
-    }
-    
-    return false;
-}
diff --git a/src/ast/simplifier/bv_simplifier_plugin.h b/src/ast/simplifier/bv_simplifier_plugin.h
deleted file mode 100644
index 7208b6dc8..000000000
--- a/src/ast/simplifier/bv_simplifier_plugin.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    bv_simplifier_plugin.h
-
-Abstract:
-
-    Simplifier for the bv family.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-08
-    
---*/
-#ifndef BV_SIMPLIFIER_PLUGIN_H_
-#define BV_SIMPLIFIER_PLUGIN_H_
-
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/poly_simplifier_plugin.h"
-#include "ast/bv_decl_plugin.h"
-#include "util/map.h"
-#include "ast/simplifier/bv_simplifier_params.h"
-#include "ast/arith_decl_plugin.h"
-
-/**
-   \brief Simplifier for the bv family.
-*/
-class bv_simplifier_plugin : public poly_simplifier_plugin {
-
-    typedef rational numeral;
-    struct extract_entry {
-        unsigned m_high;
-        unsigned m_low;
-        expr *    m_arg;
-        extract_entry():m_high(0), m_low(0), m_arg(0) {}
-        extract_entry(unsigned h, unsigned l, expr * n):m_high(h), m_low(l), m_arg(n) {}
-        unsigned hash() const {
-            unsigned a = m_high;
-            unsigned b = m_low;
-            unsigned c = m_arg->get_id();
-            mix(a,b,c);
-            return c;
-        }
-        bool operator==(const extract_entry & e) const {
-            return m_high == e.m_high && m_low == e.m_low && m_arg == e.m_arg;
-        }
-        struct hash_proc {
-            unsigned operator()(extract_entry const& e) const { return e.hash(); }
-        };
-        struct eq_proc {
-            bool operator()(extract_entry const& a, extract_entry const& b) const { return a == b; }
-        };
-    };
-    typedef map<extract_entry, expr *, extract_entry::hash_proc , extract_entry::eq_proc > extract_cache;
-
-protected:
-    ast_manager&              m_manager;
-    bv_util                   m_util;
-    arith_util                m_arith;
-    basic_simplifier_plugin & m_bsimp;
-    bv_simplifier_params &    m_params;
-    expr_ref_vector           m_zeros;
-    extract_cache             m_extract_cache;
-
-    unsigned_vector           m_lows, m_highs;
-    ptr_vector<expr>          m_extract_args;
-
-    rational mk_bv_and(numeral const& a0, numeral const& b0, unsigned sz);
-    rational mk_bv_or(numeral const& a0, numeral const& b0, unsigned sz);
-    rational mk_bv_xor(numeral const& a0, numeral const& b0, unsigned sz);
-    rational mk_bv_not(numeral const& a0, unsigned sz);
-    rational num(expr* e);
-    bool has_sign_bit(numeral const& n, unsigned bv_size) { return m_util.has_sign_bit(n, bv_size); }
-
-    bool shift_shift(bv_op_kind k, expr* arg1, expr* arg2, expr_ref& result);
-
-    void bit2bool_simplify(unsigned idx, expr* e, expr_ref& result);
-
-    void mk_add_concat(expr_ref& result);
-    bool is_zero_bit(expr* x, unsigned idx);
-
-    void mk_bv_rotate_left_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result);
-    void mk_bv_rotate_right_core(unsigned shift, numeral r, unsigned bv_size, expr_ref& result);
-
-public:
-    bv_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b, bv_simplifier_params & p);
-    virtual ~bv_simplifier_plugin();
-
-
-    // simplifier_plugin:
-    virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
-    virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
-    virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result);
-    virtual void flush_caches();
-
-    // poly_simplifier_plugin
-    virtual rational norm(const rational & n);
-    virtual bool is_numeral(expr * n, rational & val) const;
-    bool is_numeral(expr * n) const { return m_util.is_numeral(n); }
-    virtual bool is_minus_one(expr * n) const { return is_minus_one_core(n); }
-    virtual expr * get_zero(sort * s) const;
-    virtual app * mk_numeral(rational const & n);
-
-    bool is_bv(expr * n) const { return m_util.is_bv(n); }
-    bool is_bv_sort(sort * s) const { return m_util.is_bv_sort(s); }
-
-    bool is_le(expr * n) const { return m_util.is_bv_ule(n) || m_util.is_bv_sle(n); }
-    // REMARK: simplified bv expressions are never of the form a >= b.
-    virtual bool is_le_ge(expr * n) const { return is_le(n); }
-
-    uint64 to_uint64(const numeral & n, unsigned bv_size);
-    rational norm(rational const& n, unsigned bv_size, bool is_signed) { return m_util.norm(n, bv_size, is_signed); }
-    unsigned get_bv_size(expr const * n) { return get_bv_size(m_manager.get_sort(n)); }
-    unsigned get_bv_size(sort const * s) { return m_util.get_bv_size(s); }
-    void mk_leq_core(bool is_signed, expr * arg1, expr * arg2, expr_ref & result);
-    void mk_ule(expr* a, expr* b, expr_ref& result);
-    void mk_ult(expr* a, expr* b, expr_ref& result);
-    void mk_sle(expr* a, expr* b, expr_ref& result);
-    void mk_slt(expr* a, expr* b, expr_ref& result);
-    void mk_bv_and(unsigned num_args, expr * const* args, expr_ref & result);
-    void mk_bv_or(unsigned num_args, expr * const* args, expr_ref & result);
-    void mk_bv_xor(unsigned num_args, expr * const* args, expr_ref & result);
-    void mk_bv_not(expr * arg, expr_ref & result);
-    void mk_extract(unsigned hi,unsigned lo, expr* bv, expr_ref& result);
-    void mk_extract_core(unsigned high, unsigned low, expr * arg, expr_ref& result);
-    void cache_extract(unsigned h, unsigned l, expr * arg, expr * result);
-    expr* get_cached_extract(unsigned h, unsigned l, expr * arg);
-
-    bool lookup_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result);
-    bool try_mk_extract(unsigned high, unsigned low, expr * arg, expr_ref& result);
-
-    void mk_bv_eq(expr* a1, expr* a2, expr_ref& result);
-    void mk_eq_core(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_args_eq_numeral(app * app, expr * n, expr_ref & result);
-
-    void mk_concat(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_concat(expr * arg1, expr * arg2, expr_ref & result) { 
-        expr * args[2] = { arg1, arg2 }; 
-        mk_concat(2, args, result); 
-    }
-    void mk_zeroext(unsigned n, expr * arg, expr_ref & result);
-    void mk_repeat(unsigned n, expr * arg, expr_ref & result);
-    void mk_sign_extend(unsigned n, expr * arg, expr_ref & result);
-    void mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_bv_ashr(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_smod(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_urem(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_bv_srem(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_udiv(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_sdiv(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_smod_i(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_urem_i(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_bv_srem_i(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_udiv_i(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_sdiv_i(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_nand(unsigned num_args, expr* const* args, expr_ref& result);
-    void mk_bv_nor(unsigned num_args, expr* const* args, expr_ref& result);
-    void mk_bv_xnor(unsigned num_args, expr* const* args, expr_ref& result);
-    void mk_bv_rotate_right(func_decl* f, expr* arg, expr_ref& result);
-    void mk_bv_rotate_left(func_decl* f, expr* arg, expr_ref& result);
-    void mk_bv_ext_rotate_right(expr* arg1, expr* arg2, expr_ref& result);
-    void mk_bv_ext_rotate_left(expr* arg1, expr* arg2, expr_ref& result);
-
-    void mk_bv_redor(expr* arg, expr_ref& result);
-    void mk_bv_redand(expr* arg, expr_ref& result);
-    void mk_bv_comp(expr* arg1, expr* arg2, expr_ref& result);
-
-    bool are_numerals(unsigned num_args, expr * const* args, unsigned& bv_size);
-    app * mk_numeral(rational const & n, unsigned bv_size);
-    app * mk_numeral(uint64 n, unsigned bv_size) { return mk_numeral(numeral(n, numeral::ui64()), bv_size); }
-    app* mk_bv0(unsigned bv_size) { return m_util.mk_numeral(numeral(0), bv_size); }
-    rational mk_allone(unsigned bv_size) { return rational::power_of_two(bv_size) - numeral(1); }
-    bool is_minus_one_core(expr * arg) const;
-    bool is_x_minus_one(expr * arg, expr * & x);
-    void mk_int2bv(expr * arg, sort* range, expr_ref & result);
-    void mk_bv2int(expr * arg, sort* range, expr_ref & result);
-    uint64 n64(expr* e);
-    bool is_mul_no_overflow(expr* e);
-    bool is_add_no_overflow(expr* e);
-    unsigned num_leading_zero_bits(expr* e);
-
-};
-
-#endif /* BV_SIMPLIFIER_PLUGIN_H_ */
diff --git a/src/ast/simplifier/datatype_simplifier_plugin.cpp b/src/ast/simplifier/datatype_simplifier_plugin.cpp
deleted file mode 100644
index b665ed101..000000000
--- a/src/ast/simplifier/datatype_simplifier_plugin.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*++
-Copyright (c) 2008 Microsoft Corporation
-
-Module Name:
-
-    datatype_simplifier_plugin.cpp
-
-Abstract:
-
-    Simplifier for algebraic datatypes.
-
-Author:
-
-    nbjorner 2008-11-6
-    
---*/
-
-#include "ast/simplifier/datatype_simplifier_plugin.h"
-
-datatype_simplifier_plugin::datatype_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b):
-    simplifier_plugin(symbol("datatype"), m),
-    m_util(m),
-    m_bsimp(b) {
-}
-
-datatype_simplifier_plugin::~datatype_simplifier_plugin() {
-}
-
-bool datatype_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-
-    SASSERT(f->get_family_id() == get_family_id());
-    switch(f->get_decl_kind()) {
-    case OP_DT_CONSTRUCTOR: {
-        return false;
-    }
-    case OP_DT_RECOGNISER: {
-        //
-        // simplify is_cons(cons(x,y)) -> true
-        // simplify is_cons(nil) -> false
-        //
-        SASSERT(num_args == 1);
-        
-        if (!is_app_of(args[0], get_family_id(), OP_DT_CONSTRUCTOR)) {
-            return false;
-        }
-        app* a = to_app(args[0]);
-        func_decl* f1 = a->get_decl();
-        func_decl* f2 = m_util.get_recognizer_constructor(f);
-        if (f1 == f2) {
-            result = m_manager.mk_true();
-        }
-        else {
-            result = m_manager.mk_false();
-        }
-        return true;
-    }
-    case OP_DT_ACCESSOR: {
-        // 
-        // simplify head(cons(x,y)) -> x
-        // 
-        SASSERT(num_args == 1);
-        
-        if (!is_app_of(args[0], get_family_id(), OP_DT_CONSTRUCTOR)) {
-            return false;
-        }
-        app* a = to_app(args[0]);
-        func_decl* f1 = a->get_decl();
-        func_decl* f2 = m_util.get_accessor_constructor(f);
-        if (f1 != f2) {
-            return false;
-        }
-        ptr_vector<func_decl> const* acc = m_util.get_constructor_accessors(f1);
-        SASSERT(acc && acc->size() == a->get_num_args());
-        for (unsigned i = 0; i < acc->size(); ++i) {
-            if (f == (*acc)[i]) {
-                // found it.
-                result = a->get_arg(i);
-                return true;
-            }
-        }
-        UNREACHABLE();
-    }
-    case OP_DT_UPDATE_FIELD:
-        return false;
-    default:
-        UNREACHABLE();
-    }
-    
-    return false;
-}
-
-bool datatype_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
-    set_reduce_invoked();
-    if (is_app_of(lhs, get_family_id(), OP_DT_CONSTRUCTOR) &&
-        is_app_of(rhs, get_family_id(), OP_DT_CONSTRUCTOR)) {
-        app* a = to_app(lhs);
-        app* b = to_app(rhs);
-        if (a->get_decl() != b->get_decl()) {
-            result = m_manager.mk_false();
-            return true;
-        }
-        expr_ref_vector eqs(m_manager);
-        for (unsigned i = 0; i < a->get_num_args(); ++i) {            
-            m_bsimp.mk_eq(a->get_arg(i),b->get_arg(i), result);
-            eqs.push_back(result);                
-        }
-        m_bsimp.mk_and(eqs.size(), eqs.c_ptr(), result);
-        return true;
-    }
-    // TBD: occurs check, constructor check.
-
-    return false;
-}
-
diff --git a/src/ast/simplifier/datatype_simplifier_plugin.h b/src/ast/simplifier/datatype_simplifier_plugin.h
deleted file mode 100644
index e976beba7..000000000
--- a/src/ast/simplifier/datatype_simplifier_plugin.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*++
-Copyright (c) 2008 Microsoft Corporation
-
-Module Name:
-
-    datatype_simplifier_plugin.h
-
-Abstract:
-
-    Simplifier for algebraic datatypes.
-
-Author:
-
-    nbjorner 2008-11-6
-    
---*/
-#ifndef DATATYPE_SIMPLIFIER_PLUGIN_H_
-#define DATATYPE_SIMPLIFIER_PLUGIN_H_
-
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/datatype_decl_plugin.h"
-
-/**
-   \brief Simplifier for the arith family.
-*/
-class datatype_simplifier_plugin : public simplifier_plugin {
-    datatype_util             m_util;
-    basic_simplifier_plugin & m_bsimp;
-
-
-public:
-    datatype_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b);
-    ~datatype_simplifier_plugin();
-    
-
-    virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
-
-    virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
-
-};
-
-#endif /* DATATYPE_SIMPLIFIER_PLUGIN_H_ */
diff --git a/src/ast/simplifier/elim_bounds.cpp b/src/ast/simplifier/elim_bounds.cpp
deleted file mode 100644
index 738fc3012..000000000
--- a/src/ast/simplifier/elim_bounds.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    elim_bounds.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-06-28.
-
-Revision History:
-
---*/
-#include "ast/simplifier/elim_bounds.h"
-#include "ast/used_vars.h"
-#include "util/obj_hashtable.h"
-#include "ast/rewriter/var_subst.h"
-#include "ast/ast_pp.h"
-
-elim_bounds::elim_bounds(ast_manager & m):
-    m_manager(m),
-    m_util(m) {
-}
-
-/**
-   \brief Find bounds of the form
-
-   (<= x k)
-   (<= (+ x (* -1 y)) k)
-   (<= (+ x (* -1 t)) k)
-   (<= (+ t (* -1 x)) k)
-
-   x and y are a bound variables, t is a ground term and k is a numeral
-
-   It also detects >=, and the atom can be negated.
-*/
-bool elim_bounds::is_bound(expr * n, var * & lower, var * & upper) {
-    upper    = 0;
-    lower    = 0;
-    bool neg = false;
-    if (m_manager.is_not(n)) {
-        n   = to_app(n)->get_arg(0);
-        neg = true;
-    }
-
-    bool le  = false;
-    if (m_util.is_le(n)) {
-        SASSERT(m_util.is_numeral(to_app(n)->get_arg(1)));
-        n  = to_app(n)->get_arg(0);
-        le = true;
-    }
-    else if (m_util.is_ge(n)) {
-        SASSERT(m_util.is_numeral(to_app(n)->get_arg(1)));
-        n  = to_app(n)->get_arg(0);
-        le = false;
-    }
-    else {
-        return false;
-    }
-
-    if (neg)
-        le = !le;
-
-    if (is_var(n)) {
-        upper = to_var(n);
-    }
-    else if (m_util.is_add(n) && to_app(n)->get_num_args() == 2) {
-        expr * arg1 = to_app(n)->get_arg(0);
-        expr * arg2 = to_app(n)->get_arg(1);
-        if (is_var(arg1))
-            upper   = to_var(arg1);
-        else if (!is_ground(arg1))
-            return false;
-        rational k;
-        bool is_int;
-        if (m_util.is_mul(arg2) && m_util.is_numeral(to_app(arg2)->get_arg(0), k, is_int) && k.is_minus_one()) {
-            arg2    = to_app(arg2)->get_arg(1);
-            if (is_var(arg2))
-                lower = to_var(arg2);
-            else if (!is_ground(arg2))
-                return false; // not supported
-        }
-        else {
-            return false; // not supported
-        }
-    }
-    else {
-        return false;
-    }
-
-    if (!le)
-        std::swap(upper, lower);
-
-    return true;
-}
-
-bool elim_bounds::is_bound(expr * n) {
-    var * lower, * upper;
-    return is_bound(n, lower, upper);
-}
-
-void elim_bounds::operator()(quantifier * q, expr_ref & r) {
-    if (!q->is_forall()) {
-        r = q;
-        return;
-    }
-    expr * n = q->get_expr();
-    unsigned num_vars = q->get_num_decls();
-    ptr_buffer<expr> atoms;
-    if (m_manager.is_or(n))
-        atoms.append(to_app(n)->get_num_args(), to_app(n)->get_args());
-    else
-        atoms.push_back(n);
-    used_vars          m_used_vars;
-    // collect non-candidates
-    unsigned sz = atoms.size();
-    for (unsigned i = 0; i < sz; i++) {
-        expr * a = atoms[i];
-        if (!is_bound(a))
-            m_used_vars.process(a);
-    }
-    if (m_used_vars.uses_all_vars(q->get_num_decls())) {
-        r = q;
-        return;
-    }
-    // collect candidates
-    obj_hashtable<var> m_lowers;
-    obj_hashtable<var> m_uppers;
-    obj_hashtable<var> m_candidate_set;
-    ptr_buffer<var>    m_candidates;
-#define ADD_CANDIDATE(V) if (!m_lowers.contains(V) && !m_uppers.contains(V)) { m_candidate_set.insert(V); m_candidates.push_back(V); }
-    for (unsigned i = 0; i < sz; i++) {
-        expr * a    = atoms[i];
-        var * lower = 0;
-        var * upper = 0;
-        if (is_bound(a, lower, upper)) {
-            if (lower != 0 && !m_used_vars.contains(lower->get_idx()) && lower->get_idx() < num_vars) {
-                ADD_CANDIDATE(lower);
-                m_lowers.insert(lower);
-            }
-            if (upper != 0 && !m_used_vars.contains(upper->get_idx()) && upper->get_idx() < num_vars) {
-                ADD_CANDIDATE(upper);
-                m_uppers.insert(upper);
-            }
-        }
-    }
-    TRACE("elim_bounds", tout << "candidates:\n"; for (unsigned i = 0; i < m_candidates.size(); i++) tout << mk_pp(m_candidates[i], m_manager) << "\n";);
-    // remove candidates that have lower and upper bounds
-    for (unsigned i = 0; i < m_candidates.size(); i++) {
-        var * v = m_candidates[i];
-        if (m_lowers.contains(v) && m_uppers.contains(v))
-            m_candidate_set.erase(v);
-    }
-    TRACE("elim_bounds", tout << "candidates after filter:\n"; for (unsigned i = 0; i < m_candidates.size(); i++) tout << mk_pp(m_candidates[i], m_manager) << "\n";);
-    if (m_candidate_set.empty()) {
-        r = q;
-        return;
-    }
-    // remove bounds that contain variables in m_candidate_set
-    unsigned j = 0;
-    for (unsigned i = 0; i < sz; i++) {
-        expr * a    = atoms[i];
-        var * lower = 0;
-        var * upper = 0;
-        if (is_bound(a, lower, upper) && ((lower != 0 && m_candidate_set.contains(lower)) || (upper != 0 && m_candidate_set.contains(upper))))
-            continue;
-        atoms[j] = a;
-        j++;
-    }
-    atoms.resize(j);
-    expr * new_body = 0;
-    switch (atoms.size()) {
-    case 0:
-        r = m_manager.mk_false();
-        TRACE("elim_bounds", tout << mk_pp(q, m_manager) << "\n" << mk_pp(r, m_manager) << "\n";);
-        return;
-    case 1:
-        new_body = atoms[0];
-        break;
-    default:
-        new_body = m_manager.mk_or(atoms.size(), atoms.c_ptr());
-        break;
-    }
-    quantifier_ref new_q(m_manager);
-    new_q = m_manager.update_quantifier(q, new_body);
-    elim_unused_vars(m_manager, new_q, params_ref(), r);
-    TRACE("elim_bounds", tout << mk_pp(q, m_manager) << "\n" << mk_pp(r, m_manager) << "\n";);
-}
-
-bool elim_bounds_star::visit_quantifier(quantifier * q) {
-    if (!q->is_forall() || q->get_num_patterns() != 0)
-        return true;
-    bool visited         = true;
-    visit(q->get_expr(), visited);
-    return visited;
-}
-
-void elim_bounds_star::reduce1_quantifier(quantifier * q) {
-    if (!q->is_forall() || q->get_num_patterns() != 0) {
-        cache_result(q, q, 0);
-        return;
-    }
-    quantifier_ref new_q(m);
-    expr * new_body = 0;
-    proof * new_pr;
-    get_cached(q->get_expr(), new_body, new_pr);
-    new_q = m.update_quantifier(q, new_body);
-    expr_ref r(m);
-    m_elim(new_q, r);
-    if (q == r.get()) {
-        cache_result(q, q, 0);
-        return;
-    }
-    proof_ref pr(m);
-    if (m.fine_grain_proofs())
-        pr = m.mk_rewrite(q, r); // TODO: improve justification
-    cache_result(q, r, pr);
-}
-
diff --git a/src/ast/simplifier/elim_bounds.h b/src/ast/simplifier/elim_bounds.h
deleted file mode 100644
index d4da953a8..000000000
--- a/src/ast/simplifier/elim_bounds.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    elim_bounds.h
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-06-28.
-
-Revision History:
-
---*/
-#ifndef ELIM_BOUNDS_H_
-#define ELIM_BOUNDS_H_
-
-#include "ast/ast.h"
-#include "ast/arith_decl_plugin.h"
-#include "ast/simplifier/simplifier.h"
-
-/**
-   \brief Functor for eliminating irrelevant bounds in quantified formulas.
-   
-   Example:
-   (forall (x Int) (y Int) (or (not (>= y x) (not (>= x 0)) (= (select a x) 1))))
-
-   The bound (>= y x) is irrelevant and can be eliminated.
-
-   This can be easily proved by using Fourier-Motzkin elimination.
-
-   Limitations & Assumptions:
-   - It assumes the input formula was already simplified.
-   - It can only handle bounds in the diff-logic fragment.
-
-   \remark This operation is subsumed by Fourier-Motzkin elimination.
-*/
-class elim_bounds {
-    ast_manager &      m_manager;
-    arith_util         m_util;
-    bool is_bound(expr * n, var * & lower, var * & upper);
-    bool is_bound(expr * n);
-public:
-    elim_bounds(ast_manager & m);
-    void operator()(quantifier * q, expr_ref & r);
-};
-
-/**
-   \brief Functor for applying elim_bounds in all
-   universal quantifiers in an expression.
-
-   Assumption: the formula was already skolemized.
-*/
-class elim_bounds_star : public simplifier {
-protected:
-    elim_bounds  m_elim;
-    virtual bool visit_quantifier(quantifier * q);
-    virtual void reduce1_quantifier(quantifier * q);
-public:
-    elim_bounds_star(ast_manager & m):simplifier(m), m_elim(m) { enable_ac_support(false); }
-    virtual ~elim_bounds_star() {}
-};
-
-#endif /* ELIM_BOUNDS_H_ */
-
diff --git a/src/ast/simplifier/fpa_simplifier_plugin.cpp b/src/ast/simplifier/fpa_simplifier_plugin.cpp
deleted file mode 100644
index 2d333c872..000000000
--- a/src/ast/simplifier/fpa_simplifier_plugin.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*++
-Copyright (c) 2015 Microsoft Corporation
-
-Module Name:
-
-    fpa_simplifier_plugin.cpp
-
-Abstract:
-
-    Simplifier for the floating-point theory
-
-Author:
-
-    Christoph (cwinter) 2015-01-14
-
---*/
-#include "ast/simplifier/fpa_simplifier_plugin.h"
-
-fpa_simplifier_plugin::fpa_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b) :
-simplifier_plugin(symbol("fpa"), m),
-m_util(m),
-m_rw(m) {}
-
-fpa_simplifier_plugin::~fpa_simplifier_plugin() {}
-
-bool fpa_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-
-    SASSERT(f->get_family_id() == get_family_id());
-
-    return m_rw.mk_app_core(f, num_args, args, result) != BR_FAILED;
-}
-
-bool fpa_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
-    set_reduce_invoked();
-
-    return m_rw.mk_eq_core(lhs, rhs, result) != BR_FAILED;
-}
-
diff --git a/src/ast/simplifier/fpa_simplifier_plugin.h b/src/ast/simplifier/fpa_simplifier_plugin.h
deleted file mode 100644
index 8c9f8de4e..000000000
--- a/src/ast/simplifier/fpa_simplifier_plugin.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*++
-Copyright (c) 2015 Microsoft Corporation
-
-Module Name:
-
-    fpa_simplifier_plugin.h
-
-Abstract:
-
-    Simplifier for the floating-point theory
-
-Author:
-
-    Christoph (cwinter) 2015-01-14
-
---*/
-#ifndef FPA_SIMPLIFIER_PLUGIN_H_
-#define FPA_SIMPLIFIER_PLUGIN_H_
-
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/fpa_decl_plugin.h"
-#include "ast/rewriter/fpa_rewriter.h"
-
-class fpa_simplifier_plugin : public simplifier_plugin {
-    fpa_util     m_util;
-    fpa_rewriter m_rw;
-
-public:
-    fpa_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b);
-    ~fpa_simplifier_plugin();
-
-
-    virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
-
-    virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
-
-};
-
-#endif /* FPA_SIMPLIFIER_PLUGIN_H_ */
diff --git a/src/ast/simplifier/poly_simplifier_plugin.cpp b/src/ast/simplifier/poly_simplifier_plugin.cpp
deleted file mode 100644
index 88637d694..000000000
--- a/src/ast/simplifier/poly_simplifier_plugin.cpp
+++ /dev/null
@@ -1,835 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    poly_simplifier_plugin.cpp
-
-Abstract:
-
-    Abstract class for families that have polynomials.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-08
-    
---*/
-#include "ast/simplifier/poly_simplifier_plugin.h"
-#include "ast/ast_pp.h"
-#include "ast/ast_util.h"
-#include "ast/ast_smt2_pp.h"
-#include "ast/ast_ll_pp.h"
-
-poly_simplifier_plugin::poly_simplifier_plugin(symbol const & fname, ast_manager & m, decl_kind add, decl_kind mul, decl_kind uminus, decl_kind sub,
-                                               decl_kind num):
-    simplifier_plugin(fname, m), 
-    m_ADD(add), 
-    m_MUL(mul),
-    m_SUB(sub),
-    m_UMINUS(uminus),
-    m_NUM(num),
-    m_curr_sort(0),
-    m_curr_sort_zero(0) {
-}
-
-expr * poly_simplifier_plugin::mk_add(unsigned num_args, expr * const * args) { 
-    SASSERT(num_args > 0);
-#ifdef Z3DEBUG
-    // check for incorrect use of mk_add
-    for (unsigned i = 0; i < num_args; i++) {
-        SASSERT(!is_zero(args[i]));
-    }
-#endif    
-    if (num_args == 1)
-        return args[0];
-    else
-        return m_manager.mk_app(m_fid, m_ADD, num_args, args); 
-}
-
-expr * poly_simplifier_plugin::mk_mul(unsigned num_args, expr * const * args) { 
-    SASSERT(num_args > 0);
-#ifdef Z3DEBUG
-    // check for incorrect use of mk_mul
-    set_curr_sort(args[0]);
-    SASSERT(!is_zero(args[0]));
-    numeral k;
-    for (unsigned i = 0; i < num_args; i++) {
-        SASSERT(!is_numeral(args[i], k) || !k.is_one());
-        SASSERT(i == 0 || !is_numeral(args[i]));
-    }
-#endif
-    if (num_args == 1) 
-        return args[0];
-    else if (num_args == 2)
-        return m_manager.mk_app(m_fid, m_MUL, args[0], args[1]);
-    else if (is_numeral(args[0]))
-        return m_manager.mk_app(m_fid, m_MUL, args[0], m_manager.mk_app(m_fid, m_MUL, num_args - 1, args+1));
-    else
-        return m_manager.mk_app(m_fid, m_MUL, num_args, args); 
-}
-
-expr * poly_simplifier_plugin::mk_mul(numeral const & c, expr * body) {
-    numeral c_prime, d;
-    c_prime = norm(c);
-    if (c_prime.is_zero())
-        return 0;
-    if (body == 0)
-        return mk_numeral(c_prime);
-    if (c_prime.is_one())
-         return body;
-    if (is_numeral(body, d)) {
-        c_prime = norm(c_prime*d);
-        if (c_prime.is_zero())
-            return 0;
-        return mk_numeral(c_prime);
-    }
-    set_curr_sort(body);
-    expr * args[2] = { mk_numeral(c_prime), body };
-    return mk_mul(2, args);
-}
-
-/**
-   \brief Traverse args, and copy the non-numeral exprs to result, and accumulate the 
-   value of the numerals in k.
-*/
-void poly_simplifier_plugin::process_monomial(unsigned num_args, expr * const * args, numeral & k, ptr_buffer<expr> & result) {
-    rational v;
-    for (unsigned i = 0; i < num_args; i++) {
-        expr * arg = args[i];
-        if (is_numeral(arg, v))
-            k *= v;
-        else
-            result.push_back(arg);
-    }
-}
-
-#ifdef Z3DEBUG
-/**
-   \brief Return true if m is a wellformed monomial.
-*/
-bool poly_simplifier_plugin::wf_monomial(expr * m) const {
-    SASSERT(!is_add(m));
-    if (is_mul(m)) {
-        app * curr = to_app(m);
-        expr * pp  = 0;
-        if (is_numeral(curr->get_arg(0)))
-            pp = curr->get_arg(1);
-        else
-            pp = curr;
-        if (is_mul(pp)) {
-            for (unsigned i = 0; i < to_app(pp)->get_num_args(); i++) {
-                expr * arg = to_app(pp)->get_arg(i);
-                CTRACE("wf_monomial_bug", is_mul(arg), 
-                       tout << "m:  "  << mk_ismt2_pp(m, m_manager) << "\n";
-                       tout << "pp: "  << mk_ismt2_pp(pp, m_manager) << "\n";
-                       tout << "arg: " << mk_ismt2_pp(arg, m_manager) << "\n";
-                       tout << "i:  " << i << "\n";
-                       );
-                SASSERT(!is_mul(arg));
-                SASSERT(!is_numeral(arg));
-            }
-        }
-    }
-    return true;
-}
-
-/**
-   \brief Return true if m is a wellformed polynomial.
-*/
-bool poly_simplifier_plugin::wf_polynomial(expr * m) const {
-    if (is_add(m)) {
-        for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) {
-            expr * arg = to_app(m)->get_arg(i);
-            SASSERT(!is_add(arg));
-            SASSERT(wf_monomial(arg));
-        }
-    }
-    else if (is_mul(m)) {
-        SASSERT(wf_monomial(m));
-    }
-    return true;
-}
-#endif
-
-/**
-   \brief Functor used to sort the elements of a monomial.
-   Force numeric constants to be in the beginning.
-*/
-struct monomial_element_lt_proc {
-    poly_simplifier_plugin &  m_plugin;
-    monomial_element_lt_proc(poly_simplifier_plugin & p):m_plugin(p) {}
-    bool operator()(expr * m1, expr * m2) const {
-        SASSERT(!m_plugin.is_numeral(m1) || !m_plugin.is_numeral(m2));
-        if (m_plugin.is_numeral(m1))
-            return true;
-        if (m_plugin.is_numeral(m2))
-            return false;
-        return m1->get_id() < m2->get_id();
-    }
-};
-
-/**
-   \brief Create a monomial (* args). 
-*/
-void poly_simplifier_plugin::mk_monomial(unsigned num_args, expr * * args, expr_ref & result) {
-    switch(num_args) {
-    case 0:
-        result = mk_one();
-        break;
-    case 1:
-        result = args[0];
-        break;
-    default:
-        std::stable_sort(args, args + num_args, monomial_element_lt_proc(*this));
-        result = mk_mul(num_args, args);
-        SASSERT(wf_monomial(result));
-        break;
-    }
-}
-
-/**
-   \brief Return the body of the monomial. That is, the monomial without a coefficient.
-   Examples: (* 2 (* x y)) ==> (* x y)
-             (* x x) ==> (* x x)
-             x       ==> x
-             10      ==> 10
-*/
-expr * poly_simplifier_plugin::get_monomial_body(expr * m) {
-    TRACE("get_monomial_body_bug", tout << mk_pp(m, m_manager) << "\n";);
-    SASSERT(wf_monomial(m));
-    if (!is_mul(m))
-       return m;
-    if (is_numeral(to_app(m)->get_arg(0)))
-        return to_app(m)->get_arg(1);
-    return m;
-}
-
-inline bool is_essentially_var(expr * n, family_id fid) {
-    SASSERT(is_var(n) || is_app(n));
-    return is_var(n) || to_app(n)->get_family_id() != fid;
-}
-
-/**
-   \brief Hack for ordering monomials.
-   We want an order << where
-      - (* c1 m1) << (* c2 m2)    when  m1->get_id() < m2->get_id(), and c1 and c2 are numerals.
-      - c << m                    when  c is a numeral, and m is not.
-
-   So, this method returns -1 for numerals, and the id of the body of the monomial   
-*/
-int poly_simplifier_plugin::get_monomial_body_order(expr * m) {
-    if (is_essentially_var(m, m_fid)) {
-        return m->get_id();
-    }
-    else if (is_mul(m)) {
-        if (is_numeral(to_app(m)->get_arg(0)))
-            return to_app(m)->get_arg(1)->get_id();
-        else
-            return m->get_id();
-    }
-    else if (is_numeral(m)) {
-        return -1;
-    }
-    else {
-        return m->get_id();
-    }
-}
-
-void poly_simplifier_plugin::get_monomial_coeff(expr * m, numeral & result) {
-    SASSERT(!is_numeral(m));
-    SASSERT(wf_monomial(m));
-    if (!is_mul(m))
-        result = numeral::one();
-    else if (is_numeral(to_app(m)->get_arg(0), result))
-        return;
-    else
-        result = numeral::one();
-}
-
-/**
-   \brief Return true if n1 and n2 can be written as k1 * t and k2 * t, where k1 and
-   k2 are numerals, or n1 and n2 are both numerals.
-*/
-bool poly_simplifier_plugin::eq_monomials_modulo_k(expr * n1, expr * n2) {
-    bool is_num1 = is_numeral(n1);
-    bool is_num2 = is_numeral(n2);
-    if (is_num1 != is_num2)
-        return false;
-    if (is_num1 && is_num2)
-        return true;
-    return get_monomial_body(n1) == get_monomial_body(n2);
-}
-
-/**
-   \brief Return (k1 + k2) * t (or (k1 - k2) * t when inv = true), where n1 = k1 * t, and n2 = k2 * t
-   Return false if the monomials cancel each other.
-*/
-bool poly_simplifier_plugin::merge_monomials(bool inv, expr * n1, expr * n2, expr_ref & result) {
-    numeral k1;
-    numeral k2;
-    bool is_num1 = is_numeral(n1, k1);
-    bool is_num2 = is_numeral(n2, k2);
-    SASSERT(is_num1 == is_num2);
-    if (!is_num1 && !is_num2) {
-        get_monomial_coeff(n1, k1);
-        get_monomial_coeff(n2, k2);        
-        SASSERT(eq_monomials_modulo_k(n1, n2));
-    }
-    if (inv)
-        k1 -= k2;
-    else 
-        k1 += k2;
-    if (k1.is_zero())
-        return false;
-    if (is_num1 && is_num2) {
-        result = mk_numeral(k1);
-    }
-    else {
-        expr * b = get_monomial_body(n1);
-        if (k1.is_one())
-            result = b;
-        else
-            result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k1), b);
-    }
-    TRACE("merge_monomials", tout << mk_pp(n1, m_manager) << "\n" << mk_pp(n2, m_manager) << "\n" << mk_pp(result, m_manager) << "\n";);
-    return true;
-}
-
-/**
-   \brief Return a monomial equivalent to -n.
-*/
-void poly_simplifier_plugin::inv_monomial(expr * n, expr_ref & result) {
-    set_curr_sort(n);
-    SASSERT(wf_monomial(n));
-    rational v;
-    SASSERT(n != 0);
-    TRACE("inv_monomial_bug", tout << "n:\n" << mk_ismt2_pp(n, m_manager) << "\n";);
-    if (is_numeral(n, v)) {
-        TRACE("inv_monomial_bug", tout << "is numeral\n";);
-        v.neg();
-        result = mk_numeral(v);
-    }
-    else {
-        TRACE("inv_monomial_bug", tout << "is not numeral\n";);
-        numeral k;
-        get_monomial_coeff(n, k);
-        expr * b = get_monomial_body(n);
-        k.neg();
-        if (k.is_one())
-            result = b;
-        else
-            result = m_manager.mk_app(m_fid, m_MUL, mk_numeral(k), b);
-    }
-}
-
-/** 
-    \brief Add a monomial n to result. 
-*/
-template<bool Inv>
-void poly_simplifier_plugin::add_monomial_core(expr * n, expr_ref_vector & result) {
-    if (is_zero(n)) 
-        return;
-    if (Inv) {
-        expr_ref n_prime(m_manager);
-        inv_monomial(n, n_prime);
-        result.push_back(n_prime);
-    }
-    else { 
-        result.push_back(n);
-    }
-}
-
-void poly_simplifier_plugin::add_monomial(bool inv, expr * n, expr_ref_vector & result) {
-    if (inv)
-        add_monomial_core<true>(n, result);
-    else
-        add_monomial_core<false>(n, result);
-}
-
-/**
-   \brief Copy the monomials in n to result. The monomials are inverted if inv is true.
-   Equivalent monomials are merged. 
-*/
-template<bool Inv>
-void poly_simplifier_plugin::process_sum_of_monomials_core(expr * n, expr_ref_vector & result) {
-    SASSERT(wf_polynomial(n));
-    if (is_add(n)) {
-        for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) 
-            add_monomial_core<Inv>(to_app(n)->get_arg(i), result);
-    }
-    else {
-        add_monomial_core<Inv>(n, result);
-    }
-}
-
-void poly_simplifier_plugin::process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result) {
-    if (inv)
-        process_sum_of_monomials_core<true>(n, result);
-    else
-        process_sum_of_monomials_core<false>(n, result);
-}
-
-/**
-   \brief Copy the (non-numeral) monomials in n to result. The monomials are inverted if inv is true.
-   Equivalent monomials are merged. The constant (numeral) monomials are accumulated in k.
-*/
-void poly_simplifier_plugin::process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result, numeral & k) {
-    SASSERT(wf_polynomial(n));
-    numeral val;
-    if (is_add(n)) {
-        for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) {
-            expr * arg = to_app(n)->get_arg(i);
-            if (is_numeral(arg, val)) {
-                k += inv ? -val : val;
-            }
-            else {
-                add_monomial(inv, arg, result);
-            }
-        }
-    }
-    else if (is_numeral(n, val)) {
-        k += inv ? -val : val;
-    }
-    else {
-        add_monomial(inv, n, result);
-    }
-}
-
-/**
-   \brief Functor used to sort monomials.
-   Force numeric constants to be in the beginning of a polynomial.
-*/
-struct monomial_lt_proc {
-    poly_simplifier_plugin & m_plugin;
-    monomial_lt_proc(poly_simplifier_plugin & p):m_plugin(p) {}
-    bool operator()(expr * m1, expr * m2) const {
-        return m_plugin.get_monomial_body_order(m1) < m_plugin.get_monomial_body_order(m2);
-    }
-};
-
-void poly_simplifier_plugin::mk_sum_of_monomials_core(unsigned sz, expr ** ms, expr_ref & result) {
-    switch (sz) {
-    case 0:
-        result = mk_zero();
-        break;
-    case 1:
-        result = ms[0];
-        break;
-    default:
-        result = mk_add(sz, ms);
-        break;
-    }
-}
-
-/**
-   \brief Return true if m is essentially a variable, or is of the form (* c x),
-   where c is a numeral and x is essentially a variable.
-   Store the "variable" in x.
-*/
-bool poly_simplifier_plugin::is_simple_monomial(expr * m, expr * & x) {
-    if (is_essentially_var(m, m_fid)) {
-        x = m;
-        return true;
-    }
-    if (is_app(m) && to_app(m)->get_num_args() == 2) {
-        expr * arg1 = to_app(m)->get_arg(0);
-        expr * arg2 = to_app(m)->get_arg(1);
-        if (is_numeral(arg1) && is_essentially_var(arg2, m_fid)) {
-            x = arg2;
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
-   \brief Return true if all monomials are simple, and each "variable" occurs only once.
-   The method assumes the monomials were sorted using monomial_lt_proc.
-*/
-bool poly_simplifier_plugin::is_simple_sum_of_monomials(expr_ref_vector & monomials) {
-    expr * last_var = 0;
-    expr * curr_var = 0;
-    unsigned size = monomials.size();
-    for (unsigned i = 0; i < size; i++) {
-        expr * m = monomials.get(i);
-        if (!is_simple_monomial(m, curr_var))
-            return false;
-        if (curr_var == last_var)
-            return false;
-        last_var = curr_var;
-    }
-    return true;
-}
-
-/**
-   \brief Store in result the sum of the given monomials.
-*/
-void poly_simplifier_plugin::mk_sum_of_monomials(expr_ref_vector & monomials, expr_ref & result) {
-    switch (monomials.size()) {
-    case 0:
-        result = mk_zero();
-        break;
-    case 1:
-        result = monomials.get(0);
-        break;
-    default: {
-        TRACE("mk_sum_sort", tout << "before\n"; for (unsigned i = 0; i < monomials.size(); i++) tout << mk_ll_pp(monomials.get(i), m_manager) << "\n";);
-        std::stable_sort(monomials.c_ptr(), monomials.c_ptr() + monomials.size(), monomial_lt_proc(*this));
-        TRACE("mk_sum_sort", tout << "after\n";  for (unsigned i = 0; i < monomials.size(); i++) tout << mk_ll_pp(monomials.get(i), m_manager) << "\n";);
-        if (is_simple_sum_of_monomials(monomials)) {
-            mk_sum_of_monomials_core(monomials.size(), monomials.c_ptr(), result);
-            return;
-        }
-        ptr_buffer<expr> new_monomials;
-        expr * last_body = 0;
-        numeral last_coeff;
-        numeral coeff; 
-        unsigned sz = monomials.size();
-        for (unsigned i = 0; i < sz; i++) {
-            expr * m    = monomials.get(i);
-            expr * body = 0;
-            if (!is_numeral(m, coeff)) {
-                body = get_monomial_body(m);
-                get_monomial_coeff(m, coeff);
-            }
-            if (last_body == body) {
-                last_coeff += coeff;
-                continue;
-            }
-            expr * new_m = mk_mul(last_coeff, last_body);
-            if (new_m)
-                new_monomials.push_back(new_m);
-            last_body  = body;
-            last_coeff = coeff;
-        }
-        expr * new_m = mk_mul(last_coeff, last_body);
-        if (new_m)
-            new_monomials.push_back(new_m);
-        TRACE("mk_sum", for (unsigned i = 0; i < monomials.size(); i++) tout << mk_pp(monomials.get(i), m_manager) << "\n";
-              tout << "======>\n";
-              for (unsigned i = 0; i < new_monomials.size(); i++) tout << mk_pp(new_monomials.get(i), m_manager) << "\n";);
-        mk_sum_of_monomials_core(new_monomials.size(), new_monomials.c_ptr(), result);
-        break;
-    } }
-}
-
-/**
-   \brief Auxiliary template for mk_add_core
-*/
-template<bool Inv>
-void poly_simplifier_plugin::mk_add_core_core(unsigned num_args, expr * const * args, expr_ref & result) {
-    SASSERT(num_args >= 2);
-    expr_ref_vector monomials(m_manager);
-    process_sum_of_monomials_core<false>(args[0], monomials);
-    for (unsigned i = 1; i < num_args; i++) {
-        process_sum_of_monomials_core<Inv>(args[i], monomials);
-    }
-    TRACE("mk_add_core_bug", 
-          for (unsigned i = 0; i < monomials.size(); i++) { 
-              SASSERT(monomials.get(i) != 0); 
-              tout << mk_ismt2_pp(monomials.get(i), m_manager) << "\n"; 
-          });
-    mk_sum_of_monomials(monomials, result);
-}
-
-/**
-   \brief Return a sum of monomials. The method assume that each arg in args is a sum of monomials.
-   If inv is true, then all but the first argument in args are inverted.
-*/
-void poly_simplifier_plugin::mk_add_core(bool inv, unsigned num_args, expr * const * args, expr_ref & result) {
-    TRACE("mk_add_core_bug", 
-          for (unsigned i = 0; i < num_args; i++) { 
-              SASSERT(args[i] != 0);
-              tout << mk_ismt2_pp(args[i], m_manager) << "\n";
-          });
-    switch (num_args) {
-    case 0:
-        result = mk_zero();
-        break;
-    case 1:
-        result = args[0];
-        break;
-    default:
-        if (inv)
-            mk_add_core_core<true>(num_args, args, result);
-        else
-            mk_add_core_core<false>(num_args, args, result);
-        break;
-    }
-}
-
-void poly_simplifier_plugin::mk_add(unsigned num_args, expr * const * args, expr_ref & result) {
-    SASSERT(num_args > 0);
-    set_curr_sort(args[0]);
-    mk_add_core(false, num_args, args, result);
-}
-
-void poly_simplifier_plugin::mk_add(expr * arg1, expr * arg2, expr_ref & result) {
-    expr * args[2] = { arg1, arg2 };
-    mk_add(2, args, result);
-}
-
-void poly_simplifier_plugin::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) {
-    SASSERT(num_args > 0);
-    set_curr_sort(args[0]);
-    mk_add_core(true, num_args, args, result);
-}
-
-void poly_simplifier_plugin::mk_sub(expr * arg1, expr * arg2, expr_ref & result) {
-    expr * args[2] = { arg1, arg2 };
-    mk_sub(2, args, result);
-}
-
-void poly_simplifier_plugin::mk_uminus(expr * arg, expr_ref & result) {
-    set_curr_sort(arg);
-    rational v;
-    if (is_numeral(arg, v)) {
-        v.neg();
-        result = mk_numeral(v);
-    }
-    else {
-        expr_ref zero(mk_zero(), m_manager);
-        mk_sub(zero.get(), arg, result);
-    }
-}
-
-/**
-   \brief Add monomial n to result, the coeff of n is stored in k.
-*/
-void poly_simplifier_plugin::append_to_monomial(expr * n, numeral & k, ptr_buffer<expr> & result) {
-    SASSERT(wf_monomial(n));
-    rational val;
-    if (is_numeral(n, val)) {
-        k *= val;
-        return;
-    }
-    get_monomial_coeff(n, val);
-    k *= val;
-    n  = get_monomial_body(n);
-
-    unsigned hd = result.size();
-    result.push_back(n);
-    while (hd < result.size()) {
-        n = result[hd];
-        if (is_mul(n)) {
-            result[hd] = result.back();
-            result.pop_back();
-            for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) {
-                result.push_back(to_app(n)->get_arg(i));
-            }
-        }
-        else if (is_numeral(n, val)) {
-            k *= val;
-            result[hd] = result.back();
-            result.pop_back();
-        }
-        else {
-            ++hd;
-        }
-    }
-}
-
-/**
-   \brief Return a sum of monomials that is equivalent to (* args[0] ... args[num_args-1]).
-   This method assumes that each arg[i] is a sum of monomials.
-*/
-void poly_simplifier_plugin::mk_mul(unsigned num_args, expr * const * args, expr_ref & result) {
-    if (num_args == 1) {
-        result = args[0];
-        return;
-    }
-    rational val;
-    if (num_args == 2 && is_numeral(args[0], val) && is_essentially_var(args[1], m_fid)) {
-        if (val.is_one())
-            result = args[1];
-        else if (val.is_zero())
-            result = args[0];
-        else
-            result = mk_mul(num_args, args);
-        return;
-    }
-    if (num_args == 2 && is_essentially_var(args[0], m_fid) && is_numeral(args[1], val)) {
-        if (val.is_one())
-            result = args[0];
-        else if (val.is_zero())
-            result = args[1];
-        else {
-            expr * inv_args[2] = { args[1], args[0] };
-            result = mk_mul(2, inv_args);
-        }
-        return;
-    }
-
-    TRACE("mk_mul_bug", 
-          for (unsigned i = 0; i < num_args; i++) {
-              tout << mk_pp(args[i], m_manager) << "\n";
-          });
-    set_curr_sort(args[0]);
-    buffer<unsigned> szs;
-    buffer<unsigned> it;
-    vector<ptr_vector<expr> > sums;
-    for (unsigned i = 0; i < num_args; i ++) {
-        it.push_back(0);
-        expr * arg  = args[i];
-        SASSERT(wf_polynomial(arg));
-        sums.push_back(ptr_vector<expr>());
-        ptr_vector<expr> & v = sums.back();
-        if (is_add(arg)) {
-            v.append(to_app(arg)->get_num_args(), to_app(arg)->get_args());
-        }
-        else {
-            v.push_back(arg);
-        }
-        szs.push_back(v.size());
-    }
-    expr_ref_vector monomials(m_manager);
-    do {
-        rational k(1);
-        ptr_buffer<expr> m;
-        for (unsigned i = 0; i < num_args; i++) {
-            ptr_vector<expr> & v = sums[i];
-            expr * arg           = v[it[i]];
-            TRACE("mk_mul_bug", tout << "k: " << k << " arg: " << mk_pp(arg, m_manager) << "\n";);
-            append_to_monomial(arg, k, m);
-            TRACE("mk_mul_bug", tout << "after k: " << k << "\n";);
-        }
-        expr_ref num(m_manager);
-        if (!k.is_zero() && !k.is_one()) {
-            num = mk_numeral(k);
-            m.push_back(num);
-            // bit-vectors can normalize 
-            // to 1 during
-            // internalization.
-            if (is_numeral(num, k) && k.is_one()) {
-                m.pop_back();
-            }                
-        }
-        if (!k.is_zero()) {
-            expr_ref new_monomial(m_manager);
-            TRACE("mk_mul_bug", 
-                  for (unsigned i = 0; i < m.size(); i++) {
-                      tout << mk_pp(m[i], m_manager) << "\n";
-                  });
-            mk_monomial(m.size(), m.c_ptr(), new_monomial);
-            TRACE("mk_mul_bug", tout << "new_monomial:\n" << mk_pp(new_monomial, m_manager) << "\n";);
-            add_monomial_core<false>(new_monomial, monomials);
-        }
-    }
-    while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr()));
-    mk_sum_of_monomials(monomials, result);
-}
-
-void poly_simplifier_plugin::mk_mul(expr * arg1, expr * arg2, expr_ref & result) {
-    expr * args[2] = { arg1, arg2 };
-    mk_mul(2, args, result);
-}
-
-bool poly_simplifier_plugin::reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-    unsigned i = 0;
-    for (; i < num_args; i++)
-        if (!is_numeral(args[i]))
-            break;
-    if (i == num_args) {
-        // all arguments are numerals
-        // check if arguments are different...
-        ptr_buffer<expr> buffer;
-        buffer.append(num_args, args);
-        std::sort(buffer.begin(), buffer.end(), ast_lt_proc());
-        for (unsigned i = 0; i < num_args; i++) {
-            if (i > 0 && buffer[i] == buffer[i-1]) {
-                result = m_manager.mk_false();
-                return true;
-            }
-        }
-        result = m_manager.mk_true();
-        return true;
-    }
-    return false;
-}
-
-bool poly_simplifier_plugin::reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-    if (is_decl_of(f, m_fid, m_ADD)) {
-        SASSERT(num_args > 0);
-        set_curr_sort(args[0]);
-        expr_ref_buffer args1(m_manager);
-        for (unsigned i = 0; i < num_args; ++i) {
-            expr * arg = args[i];
-            rational m = norm(mults[i]);
-            if (m.is_zero()) {
-                // skip
-            }
-            else if (m.is_one()) {
-                args1.push_back(arg);
-            }
-            else {
-                expr_ref k(m_manager);
-                k = mk_numeral(m);
-                expr_ref new_arg(m_manager);
-                mk_mul(k, args[i], new_arg);
-                args1.push_back(new_arg);
-            }
-        }
-        if (args1.empty()) {
-            result = mk_zero();
-        }
-        else {
-            mk_add(args1.size(), args1.c_ptr(), result);
-        }
-        return true;
-    }
-    else {
-        return simplifier_plugin::reduce(f, num_args, mults, args, result);
-    }
-}
-
-/**
-   \brief Return true if n is can be put into the form (+ v t) or (+ (- v) t)
-   \c inv = true will contain true if (- v) is found, and false otherwise.
-*/
-bool poly_simplifier_plugin::is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) {
-    if (!is_add(n) || is_ground(n))
-        return false;
-    
-    ptr_buffer<expr> args;
-    v = 0;
-    expr * curr = to_app(n);
-    bool stop = false;
-    inv = false;
-    while (!stop) {
-        expr * arg;
-        expr * neg_arg;
-        if (is_add(curr)) {
-            arg  = to_app(curr)->get_arg(0);
-            curr = to_app(curr)->get_arg(1);
-        }
-        else {
-            arg  = curr;
-            stop = true;
-        }
-        if (is_ground(arg)) {
-            TRACE("model_checker_bug", tout << "pushing:\n" << mk_pp(arg, m_manager) << "\n";);
-            args.push_back(arg);
-        }
-        else if (is_var(arg)) {
-            if (v != 0)
-                return false; // already found variable
-            v = to_var(arg);
-        }
-        else if (is_times_minus_one(arg, neg_arg) && is_var(neg_arg)) {
-            if (v != 0)
-                return false; // already found variable
-            v = to_var(neg_arg);
-            inv = true;
-        }
-        else {
-            return false; // non ground term.
-        }
-    }
-    if (v == 0)
-        return false; // did not find variable
-    SASSERT(!args.empty());
-    mk_add(args.size(), args.c_ptr(), t);
-    return true;
-}
diff --git a/src/ast/simplifier/poly_simplifier_plugin.h b/src/ast/simplifier/poly_simplifier_plugin.h
deleted file mode 100644
index fe5572b20..000000000
--- a/src/ast/simplifier/poly_simplifier_plugin.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    poly_simplifier_plugin.h
-
-Abstract:
-
-    Abstract class for families that have polynomials.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-08
-    
---*/
-#ifndef POLY_SIMPLIFIER_PLUGIN_H_
-#define POLY_SIMPLIFIER_PLUGIN_H_
-
-#include "ast/simplifier/simplifier_plugin.h"
-
-/**
-   \brief Abstract class that provides simplification functions for polynomials.
-*/
-class poly_simplifier_plugin : public simplifier_plugin {
-protected:
-    typedef rational numeral;
-    decl_kind          m_ADD;
-    decl_kind          m_MUL;
-    decl_kind          m_SUB;
-    decl_kind          m_UMINUS;
-    decl_kind          m_NUM;
-    sort *             m_curr_sort;
-    expr *             m_curr_sort_zero;
-
-    expr * mk_add(unsigned num_args, expr * const * args);
-    expr * mk_add(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_add(2, args); }
-    expr * mk_mul(unsigned num_args, expr * const * args);
-    expr * mk_mul(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_mul(2, args); }
-    // expr * mk_sub(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_fid, m_SUB, num_args, args); }
-    expr * mk_uminus(expr * arg) { return m_manager.mk_app(m_fid, m_UMINUS, arg); }
-
-    void process_monomial(unsigned num_args, expr * const * args, numeral & k, ptr_buffer<expr> & result);
-    void mk_monomial(unsigned num_args, expr * * args, expr_ref & result);
-    bool eq_monomials_modulo_k(expr * n1, expr * n2);
-    bool merge_monomials(bool inv, expr * n1, expr * n2, expr_ref & result);
-    template<bool Inv>
-    void add_monomial_core(expr * n, expr_ref_vector & result);
-    void add_monomial(bool inv, expr * n, expr_ref_vector & result);
-    template<bool Inv>
-    void process_sum_of_monomials_core(expr * n, expr_ref_vector & result);
-    void process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result);
-    void process_sum_of_monomials(bool inv, expr * n, expr_ref_vector & result, numeral & k);
-    void mk_sum_of_monomials(expr_ref_vector & monomials, expr_ref & result);
-    template<bool Inv>
-    void mk_add_core_core(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_add_core(bool inv, unsigned num_args, expr * const * args, expr_ref & result);
-    void append_to_monomial(expr * n, numeral & k, ptr_buffer<expr> & result);
-    expr * mk_mul(numeral const & c, expr * body);
-    void mk_sum_of_monomials_core(unsigned sz, expr ** ms, expr_ref & result);
-    bool is_simple_sum_of_monomials(expr_ref_vector & monomials);
-    bool is_simple_monomial(expr * m, expr * & x);
-
-public:
-    poly_simplifier_plugin(symbol const & fname, ast_manager & m, decl_kind add, decl_kind mul, decl_kind uminus, decl_kind sub, decl_kind num);
-    virtual ~poly_simplifier_plugin() {}
-
-    /** 
-        \brief Return true if the given expression is a numeral, and store its value in \c val.
-    */
-    virtual bool is_numeral(expr * n, numeral & val) const = 0;
-    bool is_numeral(expr * n) const { return is_app_of(n, m_fid, m_NUM); }
-    bool is_zero(expr * n) const { 
-        SASSERT(m_curr_sort_zero != 0);
-        SASSERT(m_manager.get_sort(n) == m_manager.get_sort(m_curr_sort_zero)); 
-        return n == m_curr_sort_zero; 
-    }
-    bool is_zero_safe(expr * n) {
-        set_curr_sort(m_manager.get_sort(n));
-        return is_zero(n);
-    }
-    virtual bool is_minus_one(expr * n) const = 0;
-    virtual expr * get_zero(sort * s) const = 0;
-    
-
-    /**
-       \brief Return true if n is of the form (* -1 r)
-    */
-    bool is_times_minus_one(expr * n, expr * & r) const {
-        if (is_mul(n) && to_app(n)->get_num_args() == 2 && is_minus_one(to_app(n)->get_arg(0))) {
-            r = to_app(n)->get_arg(1);
-            return true;
-        }
-        return false;
-    }
-    
-    /**
-       \brief Return true if n is of the form: a <= b or a >= b.
-    */
-    virtual bool is_le_ge(expr * n) const = 0;
-    
-    /**
-       \brief Return a constant representing the giving numeral and sort m_curr_sort.
-    */
-    virtual app * mk_numeral(numeral const & n) = 0;
-    app * mk_zero() { return mk_numeral(numeral::zero()); }
-    app * mk_one() { return mk_numeral(numeral::one()); }
-    app * mk_minus_one() { return mk_numeral(numeral::minus_one()); }
-    
-    /**
-       \brief Normalize the given numeral with respect to m_curr_sort
-    */
-    virtual numeral norm(numeral const & n) = 0;
-
-    void set_curr_sort(sort * s) { 
-        if (s != m_curr_sort) { 
-            // avoid virtual function call
-            m_curr_sort = s; 
-            m_curr_sort_zero = get_zero(m_curr_sort); 
-        } 
-    }
-    void set_curr_sort(expr * n) { set_curr_sort(m_manager.get_sort(n)); }
-
-    bool is_add(expr const * n) const { return is_app_of(n, m_fid, m_ADD); }
-    bool is_mul(expr const * n) const { return is_app_of(n, m_fid, m_MUL); }
-    void mk_add(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_add(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_sub(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_sub(expr * arg1, expr * arg2, expr_ref & result);
-    void mk_uminus(expr * arg, expr_ref & result);
-    void mk_mul(unsigned num_args, expr * const * args, expr_ref & result);
-    void mk_mul(expr * arg1, expr * arg2, expr_ref & result);
-    
-    virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result);
-
-    virtual bool reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result);
-    virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
-        return simplifier_plugin::reduce(f, num_args, args, result);
-    }
-
-
-    expr * get_monomial_body(expr * m);
-    int get_monomial_body_order(expr * m);
-    void get_monomial_coeff(expr * m, numeral & result);
-    void inv_monomial(expr * n, expr_ref & result);
-
-    bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t);
-
-#ifdef Z3DEBUG
-    bool wf_monomial(expr * m) const;
-    bool wf_polynomial(expr * m) const;
-#endif
-};
-
-#endif /* POLY_SIMPLIFIER_PLUGIN_H_ */
diff --git a/src/ast/simplifier/seq_simplifier_plugin.cpp b/src/ast/simplifier/seq_simplifier_plugin.cpp
deleted file mode 100644
index 2125c4f4c..000000000
--- a/src/ast/simplifier/seq_simplifier_plugin.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*++
-Copyright (c) 2016 Microsoft Corporation
-
-Module Name:
-
-    seq_simplifier_plugin.cpp
-
-Abstract:
-
-    Simplifier for the theory of sequences
-
-Author:
-
-    Nikolaj Bjorner (nbjorner) 2016-02-05
-
---*/
-#include "ast/simplifier/seq_simplifier_plugin.h"
-
-seq_simplifier_plugin::seq_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b) :
-simplifier_plugin(symbol("seq"), m),
-m_util(m),
-m_rw(m) {}
-
-seq_simplifier_plugin::~seq_simplifier_plugin() {}
-
-bool seq_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-
-    SASSERT(f->get_family_id() == get_family_id());
-
-    return m_rw.mk_app_core(f, num_args, args, result) != BR_FAILED;
-}
-
-bool seq_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & result) {
-    set_reduce_invoked();
-
-    return m_rw.mk_eq_core(lhs, rhs, result) != BR_FAILED;
-}
-
diff --git a/src/ast/simplifier/seq_simplifier_plugin.h b/src/ast/simplifier/seq_simplifier_plugin.h
deleted file mode 100644
index a37a2209f..000000000
--- a/src/ast/simplifier/seq_simplifier_plugin.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*++
-Copyright (c) 2016 Microsoft Corporation
-
-Module Name:
-
-    seq_simplifier_plugin.h
-
-Abstract:
-
-    Simplifier for the sequence theory
-
-Author:
-
-    Nikolaj Bjorner (nbjorner) 2016-02-05
-
---*/
-#ifndef SEQ_SIMPLIFIER_PLUGIN_H_
-#define SEQ_SIMPLIFIER_PLUGIN_H_
-
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/seq_decl_plugin.h"
-#include "ast/rewriter/seq_rewriter.h"
-
-class seq_simplifier_plugin : public simplifier_plugin {
-    seq_util     m_util;
-    seq_rewriter m_rw;
-
-public:
-    seq_simplifier_plugin(ast_manager & m, basic_simplifier_plugin & b);
-    ~seq_simplifier_plugin();
-
-
-    virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
-
-    virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result);
-
-};
-
-#endif /* SEQ_SIMPLIFIER_PLUGIN_H_ */
diff --git a/src/ast/simplifier/simplifier.cpp b/src/ast/simplifier/simplifier.cpp
deleted file mode 100644
index c02753440..000000000
--- a/src/ast/simplifier/simplifier.cpp
+++ /dev/null
@@ -1,962 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    simplifier.cpp
-
-Abstract:
-
-    Expression simplifier.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-03
-
-Notes:
-
---*/
-#include "ast/simplifier/simplifier.h"
-#include "ast/rewriter/var_subst.h"
-#include "ast/ast_ll_pp.h"
-#include "ast/ast_pp.h"
-#include "ast/well_sorted.h"
-#include "ast/ast_smt_pp.h"
-
-simplifier::simplifier(ast_manager & m):
-    base_simplifier(m),
-    m_proofs(m),
-    m_subst_proofs(m),
-    m_need_reset(false),
-    m_use_oeq(false),
-    m_visited_quantifier(false),
-    m_ac_support(true) {
-}
-
-void simplifier::register_plugin(plugin * p) {
-    m_plugins.register_plugin(p);
-}
-
-simplifier::~simplifier() {
-    flush_cache();
-}
-
-void simplifier::enable_ac_support(bool flag) {
-    m_ac_support = flag;
-    ptr_vector<plugin>::const_iterator it  = m_plugins.begin();
-    ptr_vector<plugin>::const_iterator end = m_plugins.end();
-    for (; it != end; ++it) {
-        if (*it != 0)
-            (*it)->enable_ac_support(flag);
-    }
-}
-
-/**
-   \brief External interface for the simplifier.
-   A client will invoke operator()(s, r, p) to simplify s.
-   The result is stored in r.
-   When proof generation is enabled, a proof for the equivalence (or equisatisfiability)
-   of s and r is stored in p.
-   When proof generation is disabled, this method stores the "undefined proof" object in p.
-*/
-void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) {
-    m_need_reset = true;
-    reinitialize();
-    expr  * s_orig = s;
-    (void)s_orig;
-    expr  * old_s;
-    expr  * result;
-    proof * result_proof;
-    switch (m.proof_mode()) {
-    case PGM_DISABLED: // proof generation is disabled.
-        reduce_core(s);
-        // after executing reduce_core, the result of the simplification is in the cache
-        get_cached(s, result, result_proof);
-        r = result;
-        p = m.mk_undef_proof();
-        break;
-    case PGM_COARSE: // coarse proofs... in this case, we do not produce a step by step (fine grain) proof to show the equivalence (or equisatisfiability) of s an r.
-        m_subst_proofs.reset(); // m_subst_proofs is an auxiliary vector that is used to justify substitutions. See comment on method get_subst.
-        reduce_core(s);
-        get_cached(s, result, result_proof);
-        r = result;
-        if (result == s)
-            p = m.mk_reflexivity(s);
-        else {
-            remove_duplicates(m_subst_proofs);
-            p = m.mk_rewrite_star(s, result, m_subst_proofs.size(), m_subst_proofs.c_ptr());
-        }
-        break;
-    case PGM_FINE: // fine grain proofs... in this mode, every proof step (or most of them) is described.
-        m_proofs.reset();
-        old_s = 0;
-        // keep simplyfing until no further simplifications are possible.
-        while (s != old_s) {
-            TRACE("simplifier", tout << "simplification pass... " << s->get_id() << "\n";);
-            TRACE("simplifier_loop", tout << mk_ll_pp(s, m) << "\n";);
-            reduce_core(s);
-            get_cached(s, result, result_proof);
-            SASSERT(is_rewrite_proof(s, result, result_proof));
-            if (result_proof != 0) {
-                m_proofs.push_back(result_proof);
-            }
-            old_s = s;
-            s     = result;
-        }
-        SASSERT(s != 0);
-        r = s;
-        p = m_proofs.empty() ? m.mk_reflexivity(s) : m.mk_transitivity(m_proofs.size(), m_proofs.c_ptr());
-        SASSERT(is_rewrite_proof(s_orig, r, p));
-        break;
-    default:
-        UNREACHABLE();
-    }
-}
-
-void simplifier::flush_cache() {
-    m_cache.flush();
-    ptr_vector<plugin>::const_iterator it  = m_plugins.begin();
-    ptr_vector<plugin>::const_iterator end = m_plugins.end();
-    for (; it != end; ++it) {
-        if (*it != 0) {
-            (*it)->flush_caches();
-        }
-    }
-}
-
-bool simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) {
-    return false;
-}
-
-void simplifier::reduce_core(expr * n1) {
-    if (!is_cached(n1)) {
-        // We do not assume m_todo is empty... So, we store the current size of the todo-stack.
-        unsigned sz = m_todo.size();
-        m_todo.push_back(n1);
-        while (m_todo.size() != sz) {
-            expr * n = m_todo.back();
-            if (is_cached(n))
-                m_todo.pop_back();
-            else if (visit_children(n)) {
-                // if all children were already simplified, then remove n from the todo stack and apply a
-                // simplification step to it.
-                m_todo.pop_back();
-                reduce1(n);
-            }
-            if (m.canceled()) {
-                cache_result(n1, n1, 0);
-                break;
-            }
-        }
-    }
-}
-
-/**
-   \brief Return true if all children of n have been already simplified.
-*/
-bool simplifier::visit_children(expr * n) {
-    switch(n->get_kind()) {
-    case AST_VAR:
-        return true;
-    case AST_APP:
-        // The simplifier has support for flattening AC (associative-commutative) operators.
-        // The method ast_manager::mk_app is used to create the flat version of an AC operator.
-        // In Z3 1.x, we used multi-ary operators. This creates problems for the superposition engine.
-        // So, starting at Z3 2.x, only boolean operators can be multi-ary.
-        // Example:
-        //    (and (and a b) (and c d)) --> (and a b c d)
-        //    (+ (+ a b) (+ c d)) --> (+ a (+ b (+ c d)))
-        // Remark: The flattening is only applied if m_ac_support is true.
-        if (m_ac_support && to_app(n)->get_decl()->is_associative() && to_app(n)->get_decl()->is_commutative())
-            return visit_ac(to_app(n));
-        else {
-            bool visited = true;
-            unsigned j = to_app(n)->get_num_args();
-            while (j > 0) {
-                --j;
-                visit(to_app(n)->get_arg(j), visited);
-            }
-            return visited;
-        }
-    case AST_QUANTIFIER:
-        return visit_quantifier(to_quantifier(n));
-    default:
-        UNREACHABLE();
-        return true;
-    }
-}
-
-/**
-   \brief Visit the children of n assuming it is an AC (associative-commutative) operator.
-
-   For example, if n is of the form (+ (+ a b) (+ c d)), this method
-   will return true if the nodes a, b, c and d have been already simplified.
-   The nodes (+ a b) and (+ c d) are not really checked.
-*/
-bool simplifier::visit_ac(app * n) {
-    bool visited = true;
-    func_decl * decl = n->get_decl();
-    SASSERT(m_ac_support);
-    SASSERT(decl->is_associative());
-    SASSERT(decl->is_commutative());
-    m_ac_marked.reset();
-    ptr_buffer<app> todo;
-    todo.push_back(n);
-    while (!todo.empty()) {
-        app * n = todo.back();
-        todo.pop_back();
-        if (m_ac_mark.is_marked(n))
-            continue;
-        m_ac_mark.mark(n, true);
-        m_ac_marked.push_back(n);
-        SASSERT(n->get_decl() == decl);
-        unsigned i = n->get_num_args();
-        while (i > 0) {
-            --i;
-            expr * arg  = n->get_arg(i);
-            if (is_app_of(arg, decl))
-                todo.push_back(to_app(arg));
-            else
-                visit(arg, visited);
-        }
-    }
-    ptr_vector<expr>::const_iterator it  = m_ac_marked.begin();
-    ptr_vector<expr>::const_iterator end = m_ac_marked.end();
-    for (; it != end; ++it)
-        m_ac_mark.mark(*it, false);
-    return visited;
-}
-
-bool simplifier::visit_quantifier(quantifier * n) {
-    m_visited_quantifier = true;
-    bool visited         = true;
-    unsigned j = to_quantifier(n)->get_num_patterns();
-    while (j > 0) {
-        --j;
-        visit(to_quantifier(n)->get_pattern(j), visited);
-    }
-    j = to_quantifier(n)->get_num_no_patterns();
-    while (j > 0) {
-        --j;
-        visit(to_quantifier(n)->get_no_pattern(j), visited);
-    }
-    visit(to_quantifier(n)->get_expr(), visited);
-    return visited;
-}
-
-/**
-   \brief Simplify n and store the result in the cache.
-*/
-void simplifier::reduce1(expr * n) {
-    switch (n->get_kind()) {
-    case AST_VAR:
-        cache_result(n, n, 0);
-        break;
-    case AST_APP:
-        reduce1_app(to_app(n));
-        break;
-    case AST_QUANTIFIER:
-        reduce1_quantifier(to_quantifier(n));
-        break;
-    default:
-        UNREACHABLE();
-    }
-}
-
-/**
-   \brief Simplify the given application using the cached values,
-   associativity flattening, the given substitution, and family/theory
-   specific simplifications via plugins.
-*/
-void simplifier::reduce1_app(app * n) {
-    expr_ref r(m);
-    proof_ref p(m);
-    TRACE("reduce", tout << "reducing...\n" << mk_pp(n, m) << "\n";);
-    if (get_subst(n, r, p)) {
-        TRACE("reduce", tout << "applying substitution...\n";);
-        cache_result(n, r, p);
-        return;
-    }
-
-    func_decl * decl = n->get_decl();
-    if (m_ac_support && decl->is_associative() && decl->is_commutative())
-        reduce1_ac_app_core(n);
-    else
-        reduce1_app_core(n);
-}
-
-
-void simplifier::reduce1_app_core(app * n) {
-    m_args.reset();
-    func_decl * decl = n->get_decl();
-    proof_ref p1(m);
-    // Stores the new arguments of n in m_args.
-    // Let n be of the form
-    // (decl arg_0 ... arg_{n-1})
-    // then
-    // m_args contains [arg_0', ..., arg_{n-1}'],
-    // where arg_i' is the simplification of arg_i
-    // and
-    // p1 is a proof for
-    // (decl arg_0 ... arg_{n-1}) is equivalente/equisatisfiable to (decl arg_0' ... arg_{n-1}')
-    // p1 is built using the congruence proof step and the proofs for arg_0' ... arg_{n-1}'.
-    // Of course, p1 is 0 if proofs are not enabled or coarse grain proofs are used.
-    bool has_new_args = get_args(n, m_args, p1);
-    // The following if implements a simple trick.
-    //   If none of the arguments have been simplified, and n is not a theory symbol,
-    //   Then no simplification is possible, and we can cache the result of the simplification of n as n.
-    if (has_new_args || decl->get_family_id() != null_family_id) {
-        expr_ref r(m);
-        TRACE("reduce", tout << "reduce1_app\n"; for(unsigned i = 0; i < m_args.size(); i++) tout << mk_ll_pp(m_args[i], m););
-        // the method mk_app invokes get_subst and plugins to simplify
-        // (decl arg_0' ... arg_{n-1}')
-        mk_app(decl, m_args.size(), m_args.c_ptr(), r);
-        if (!m.fine_grain_proofs()) {
-            cache_result(n, r, 0);
-        }
-        else {
-            expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr());
-            proof * p;
-            if (n == r)
-                p = 0;
-            else if (r != s)
-                // we use a "theory rewrite generic proof" to justify the step
-                // s = (decl arg_0' ... arg_{n-1}') --> r
-                p = m.mk_transitivity(p1, m.mk_rewrite(s, r));
-            else
-                p = p1;
-            cache_result(n, r, p);
-        }
-    }
-    else {
-        cache_result(n, n, 0);
-    }
-}
-
-bool is_ac_list(app * n, ptr_vector<expr> & args) {
-    args.reset();
-    func_decl * f = n->get_decl();
-    app * curr    = n;
-    while (true) {
-        if (curr->get_num_args() != 2)
-            return false;
-        expr * arg1 = curr->get_arg(0);
-        if (is_app_of(arg1, f))
-            return false;
-        args.push_back(arg1);
-        expr * arg2 = curr->get_arg(1);
-        if (!is_app_of(arg2, f)) {
-            args.push_back(arg2);
-            return true;
-        }
-        curr = to_app(arg2);
-    }
-}
-
-bool is_ac_vector(app * n) {
-    func_decl * f     = n->get_decl();
-    unsigned num_args = n->get_num_args();
-    for (unsigned i = 0; i < num_args; i++) {
-        if (is_app_of(n->get_arg(i), f))
-            return false;
-    }
-    return true;
-}
-
-void simplifier::reduce1_ac_app_core(app * n) {
-    app_ref    n_c(m);
-    proof_ref  p1(m);
-    mk_ac_congruent_term(n, n_c, p1);
-    TRACE("ac", tout << "expr:\n" << mk_pp(n, m) << "\ncongruent term:\n" << mk_pp(n_c, m) << "\n";);
-    expr_ref r(m);
-    func_decl * decl = n->get_decl();
-    family_id fid    = decl->get_family_id();
-    plugin * p       = get_plugin(fid);
-    if (is_ac_vector(n_c)) {
-        if (p != 0 && p->reduce(decl, n_c->get_num_args(), n_c->get_args(), r)) {
-            // done...
-        }
-        else {
-            r = n_c;
-        }
-    }
-    else if (is_ac_list(n_c, m_args)) {
-        // m_args contains the arguments...
-        if (p != 0 && p->reduce(decl, m_args.size(), m_args.c_ptr(), r)) {
-            // done...
-        }
-        else {
-            r = m.mk_app(decl, m_args.size(), m_args.c_ptr());
-        }
-    }
-    else {
-        m_args.reset();
-        m_mults.reset();
-        get_ac_args(n_c, m_args, m_mults);
-        TRACE("ac", tout << "AC args:\n";
-              for (unsigned i = 0; i < m_args.size(); i++) {
-                  tout << mk_pp(m_args[i], m) << " * " << m_mults[i] << "\n";
-              });
-        if (p != 0 && p->reduce(decl, m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), r)) {
-            // done...
-        }
-        else {
-            ptr_buffer<expr> new_args;
-            expand_args(m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), new_args);
-            r = m.mk_app(decl, new_args.size(), new_args.c_ptr());
-        }
-    }
-    TRACE("ac", tout << "AC result:\n" << mk_pp(r, m) << "\n";);
-
-    if (!m.fine_grain_proofs()) {
-        cache_result(n, r, 0);
-    }
-    else {
-        proof * p;
-        if (n == r.get())
-            p = 0;
-        else if (r.get() != n_c.get())
-            p = m.mk_transitivity(p1, m.mk_rewrite(n_c, r));
-        else
-            p = p1;
-        cache_result(n, r, p);
-    }
-}
-
-static unsigned g_rewrite_lemma_id = 0;
-
-void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * const * args, expr* result) {
-    expr_ref arg(m);
-    arg = m.mk_app(decl, num_args, args);
-    if (arg.get() != result) {
-        char buffer[128];
-#ifdef _WINDOWS
-        sprintf_s(buffer, ARRAYSIZE(buffer), "lemma_%d.smt", g_rewrite_lemma_id);
-#else
-        sprintf(buffer, "rewrite_lemma_%d.smt", g_rewrite_lemma_id);
-#endif
-        ast_smt_pp pp(m);
-        pp.set_benchmark_name("rewrite_lemma");
-        pp.set_status("unsat");
-        expr_ref n(m);
-        n = m.mk_not(m.mk_eq(arg.get(), result));
-        std::ofstream out(buffer);
-        pp.display(out, n);
-        out.close();
-        ++g_rewrite_lemma_id;
-    }
-}
-
-/**
-   \brief Return in \c result an expression \c e equivalent to <tt>(f args[0] ... args[num_args - 1])</tt>, and
-   store in \c pr a proof for <tt>(= (f args[0] ... args[num_args - 1]) e)</tt>
-
-   If e is identical to (f args[0] ... args[num_args - 1]), then pr is set to 0.
-*/
-void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result) {
-    m_need_reset = true;
-    if (m.is_eq(decl)) {
-        sort * s = m.get_sort(args[0]);
-        plugin * p = get_plugin(s->get_family_id());
-        if (p != 0 && p->reduce_eq(args[0], args[1], result))
-            return;
-    }
-    else if (m.is_distinct(decl)) {
-        sort * s = m.get_sort(args[0]);
-        plugin * p = get_plugin(s->get_family_id());
-        if (p != 0 && p->reduce_distinct(num_args, args, result))
-            return;
-    }
-    family_id fid = decl->get_family_id();
-    plugin * p = get_plugin(fid);
-    if (p != 0 && p->reduce(decl, num_args, args, result)) {
-        //uncomment this line if you want to trace rewrites as lemmas:
-        //dump_rewrite_lemma(decl, num_args, args, result.get());
-        return;
-    }
-
-    result = m.mk_app(decl, num_args, args);
-}
-
-/**
-   \brief Create a term congruence to n (f a[0] ... a[num_args-1]) using the
-   cached values for the a[i]'s. Store the result in r, and the proof for (= n r) in p.
-   If n and r are identical, then set p to 0.
-*/
-void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) {
-    bool has_new_args  = false;
-    ptr_vector<expr>   args;
-    ptr_vector<proof>  proofs;
-    unsigned num       = n->get_num_args();
-    for (unsigned j = 0; j < num; j++) {
-        expr * arg     = n->get_arg(j);
-        expr * new_arg;
-        proof * arg_proof;
-        get_cached(arg, new_arg, arg_proof);
-
-        CTRACE("simplifier_bug", (arg != new_arg) != (arg_proof != 0),
-               tout << mk_ll_pp(arg, m) << "\n---->\n" << mk_ll_pp(new_arg, m) << "\n";
-               tout << "#" << arg->get_id() << " #" << new_arg->get_id() << "\n";
-               tout << arg << " " << new_arg << "\n";);
-
-
-        if (arg != new_arg) {
-            has_new_args = true;
-            proofs.push_back(arg_proof);
-            SASSERT(arg_proof);
-        }
-        else {
-            SASSERT(arg_proof == 0);
-        }
-        args.push_back(new_arg);
-    }
-    if (has_new_args) {
-        r = m.mk_app(n->get_decl(), args.size(), args.c_ptr());
-        if (m_use_oeq)
-            p = m.mk_oeq_congruence(n, r, proofs.size(), proofs.c_ptr());
-        else
-            p = m.mk_congruence(n, r, proofs.size(), proofs.c_ptr());
-    }
-    else {
-        r = n;
-        p = 0;
-    }
-}
-
-/**
-   \brief Store the new arguments of \c n in result. Store in p a proof for
-   (= n (f result[0] ... result[num_args - 1])), where f is the function symbol of n.
-
-   If there are no new arguments or fine grain proofs are disabled, then p is set to 0.
-
-   Return true there are new arguments.
-*/
-bool simplifier::get_args(app * n, ptr_vector<expr> & result, proof_ref & p) {
-    bool has_new_args  = false;
-    unsigned num       = n->get_num_args();
-    if (m.fine_grain_proofs()) {
-        app_ref r(m);
-        mk_congruent_term(n, r, p);
-        result.append(r->get_num_args(), r->get_args());
-        SASSERT(n->get_num_args() == result.size());
-        has_new_args = r != n;
-    }
-    else {
-        p = 0;
-        for (unsigned j = 0; j < num; j++) {
-            expr * arg     = n->get_arg(j);
-            expr * new_arg;
-            proof * arg_proof;
-            get_cached(arg, new_arg, arg_proof);
-            if (arg != new_arg) {
-                has_new_args = true;
-            }
-            result.push_back(new_arg);
-        }
-    }
-    return has_new_args;
-}
-
-/**
-   \brief Create a term congruence to n (where n is an expression such as: (f (f a_1 a_2) (f a_3 (f a_4 a_5))) using the
-   cached values for the a_i's. Store the result in r, and the proof for (= n r) in p.
-   If n and r are identical, then set p to 0.
-*/
-void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
-    SASSERT(m_ac_support);
-    func_decl * f = n->get_decl();
-
-    m_ac_cache.reset();
-    m_ac_pr_cache.reset();
-
-    ptr_buffer<app>   todo;
-    ptr_buffer<expr>  new_args;
-    ptr_buffer<proof> new_arg_prs;
-    todo.push_back(n);
-    while (!todo.empty()) {
-        app * curr    = todo.back();
-        if (m_ac_cache.contains(curr)) {
-            todo.pop_back();
-            continue;
-        }
-        bool visited     = true;
-        bool has_new_arg = false;
-        new_args.reset();
-        new_arg_prs.reset();
-        unsigned num_args = curr->get_num_args();
-        for (unsigned j = 0; j < num_args; j ++) {
-            expr * arg = curr->get_arg(j);
-            if (is_app_of(arg, f)) {
-                app * new_arg = 0;
-                if (m_ac_cache.find(to_app(arg), new_arg)) {
-                    SASSERT(new_arg != 0);
-                    new_args.push_back(new_arg);
-                    if (arg != new_arg)
-                        has_new_arg = true;
-                    if (m.fine_grain_proofs()) {
-                        proof * pr = 0;
-                        m_ac_pr_cache.find(to_app(arg), pr);
-                        if (pr != 0)
-                            new_arg_prs.push_back(pr);
-                    }
-                }
-                else {
-                    visited = false;
-                    todo.push_back(to_app(arg));
-                }
-            }
-            else {
-                expr * new_arg = 0;
-                proof * pr;
-                get_cached(arg, new_arg, pr);
-                new_args.push_back(new_arg);
-                if (arg != new_arg)
-                    has_new_arg = true;
-                if (m.fine_grain_proofs() && pr != 0)
-                    new_arg_prs.push_back(pr);
-            }
-        }
-        if (visited) {
-            SASSERT(new_args.size() == curr->get_num_args());
-            todo.pop_back();
-            if (!has_new_arg) {
-                m_ac_cache.insert(curr, curr);
-                if (m.fine_grain_proofs())
-                    m_ac_pr_cache.insert(curr, 0);
-            }
-            else {
-                app * new_curr = m.mk_app(f, new_args.size(), new_args.c_ptr());
-                m_ac_cache.insert(curr, new_curr);
-                if (m.fine_grain_proofs()) {
-                    proof * p = m.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr());
-                    m_ac_pr_cache.insert(curr, p);
-                }
-            }
-        }
-    }
-
-    SASSERT(m_ac_cache.contains(n));
-    app *   new_n = 0;
-    m_ac_cache.find(n, new_n);
-    r = new_n;
-    if (m.fine_grain_proofs()) {
-        proof * new_pr = 0;
-        m_ac_pr_cache.find(n, new_pr);
-        p = new_pr;
-    }
-}
-
-#define White 0
-#define Grey  1
-#define Black 2
-
-#ifdef Z3DEBUG
-static int get_color(obj_map<expr, int> & colors, expr * n) {
-    obj_map<expr, int>::obj_map_entry * entry = colors.insert_if_not_there2(n, White);
-    return entry->get_data().m_value;
-}
-#endif
-
-static bool visit_ac_children(func_decl * f, expr * n, obj_map<expr, int> & colors, ptr_buffer<expr> & todo, ptr_buffer<expr> & result) {
-    if (is_app_of(n, f)) {
-        unsigned num_args = to_app(n)->get_num_args();
-        bool visited      = true;
-        // Put the arguments in 'result' in reverse order.
-        // Reason: preserve the original order of the arguments in the final result.
-        // Remark: get_ac_args will traverse 'result' backwards.
-        for (unsigned i = 0; i < num_args; i++) {
-            expr * arg = to_app(n)->get_arg(i);
-            obj_map<expr, int>::obj_map_entry * entry = colors.insert_if_not_there2(arg, White);
-            if (entry->get_data().m_value == White) {
-                todo.push_back(arg);
-                visited = false;
-            }
-        }
-        return visited;
-    }
-    else {
-        return true;
-    }
-}
-
-void simplifier::ac_top_sort(app * n, ptr_buffer<expr> & result) {
-    ptr_buffer<expr> todo;
-    func_decl * f = n->get_decl();
-    obj_map<expr, int> & colors = m_colors;
-    colors.reset();
-    todo.push_back(n);
-    while (!todo.empty()) {
-        expr * curr = todo.back();
-        int color;
-        obj_map<expr, int>::obj_map_entry * entry = colors.insert_if_not_there2(curr, White);
-        SASSERT(entry);
-        color = entry->get_data().m_value;
-        switch (color) {
-        case White:
-            // Remark: entry becomes invalid if an element is inserted into the hashtable.
-            // So, I must set Grey before executing visit_ac_children.
-            entry->get_data().m_value = Grey;
-            SASSERT(get_color(colors, curr) == Grey);
-            if (visit_ac_children(f, curr, colors, todo, result)) {
-                // If visit_ac_children succeeded, then the hashtable was not modified,
-                // and entry is still valid.
-                SASSERT(todo.back() == curr);
-                entry->get_data().m_value = Black;
-                SASSERT(get_color(colors, curr) == Black);
-                result.push_back(curr);
-                todo.pop_back();
-            }
-            break;
-        case Grey:
-            SASSERT(visit_ac_children(f, curr, colors, todo, result));
-            SASSERT(entry);
-            entry->get_data().m_value = Black;
-            SASSERT(get_color(colors, curr) == Black);
-            result.push_back(curr);
-            SASSERT(todo.back() == curr);
-            todo.pop_back();
-            break;
-        case Black:
-            todo.pop_back();
-            break;
-        default:
-            UNREACHABLE();
-        }
-    }
-}
-
-void simplifier::get_ac_args(app * n, ptr_vector<expr> & args, vector<rational> & mults) {
-    SASSERT(m_ac_support);
-    ptr_buffer<expr> sorted_exprs;
-    ac_top_sort(n, sorted_exprs);
-    SASSERT(!sorted_exprs.empty());
-    SASSERT(sorted_exprs[sorted_exprs.size()-1] == n);
-
-    TRACE("ac", tout << mk_ll_pp(n, m, true, false) << "#" << n->get_id() << "\nsorted expressions...\n";
-          for (unsigned i = 0; i < sorted_exprs.size(); i++) {
-              tout << "#" << sorted_exprs[i]->get_id() << " ";
-          }
-          tout << "\n";);
-
-    m_ac_mults.reset();
-    m_ac_mults.insert(n, rational(1));
-    func_decl * decl = n->get_decl();
-    unsigned j = sorted_exprs.size();
-    while (j > 0) {
-        --j;
-        expr * curr = sorted_exprs[j];
-        rational mult;
-        m_ac_mults.find(curr, mult);
-        SASSERT(!mult.is_zero());
-        if (is_app_of(curr, decl)) {
-            unsigned num_args = to_app(curr)->get_num_args();
-            for (unsigned i = 0; i < num_args; i++) {
-                expr * arg = to_app(curr)->get_arg(i);
-                rational zero;
-                obj_map<expr, rational>::obj_map_entry * entry = m_ac_mults.insert_if_not_there2(arg, zero);
-                entry->get_data().m_value += mult;
-            }
-        }
-        else {
-            args.push_back(curr);
-            mults.push_back(mult);
-        }
-    }
-}
-
-void simplifier::reduce1_quantifier(quantifier * q) {
-    expr *  new_body;
-    proof * new_body_pr;
-    SASSERT(is_well_sorted(m, q));
-    get_cached(q->get_expr(), new_body, new_body_pr);
-
-    quantifier_ref q1(m);
-    proof * p1 = 0;
-
-    if (is_quantifier(new_body) &&
-        to_quantifier(new_body)->is_forall() == q->is_forall() &&
-        !to_quantifier(q)->has_patterns() &&
-        !to_quantifier(new_body)->has_patterns()) {
-
-        quantifier * nested_q = to_quantifier(new_body);
-
-        ptr_buffer<sort> sorts;
-        buffer<symbol>   names;
-        sorts.append(q->get_num_decls(), q->get_decl_sorts());
-        names.append(q->get_num_decls(), q->get_decl_names());
-        sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts());
-        names.append(nested_q->get_num_decls(), nested_q->get_decl_names());
-
-        q1 = m.mk_quantifier(q->is_forall(),
-                                     sorts.size(),
-                                     sorts.c_ptr(),
-                                     names.c_ptr(),
-                                     nested_q->get_expr(),
-                                     std::min(q->get_weight(), nested_q->get_weight()),
-                                     q->get_qid(),
-                                     q->get_skid(),
-                                     0, 0, 0, 0);
-        SASSERT(is_well_sorted(m, q1));
-
-        if (m.fine_grain_proofs()) {
-            quantifier * q0 = m.update_quantifier(q, new_body);
-            proof * p0 = q == q0 ? 0 : m.mk_quant_intro(q, q0, new_body_pr);
-            p1 = m.mk_pull_quant(q0, q1);
-            p1 = m.mk_transitivity(p0, p1);
-        }
-    }
-    else {
-        ptr_buffer<expr> new_patterns;
-        ptr_buffer<expr> new_no_patterns;
-        expr  * new_pattern;
-        proof * new_pattern_pr;
-
-        // Remark: we can ignore the proofs for the patterns.
-        unsigned num = q->get_num_patterns();
-        for (unsigned i = 0; i < num; i++) {
-            get_cached(q->get_pattern(i), new_pattern, new_pattern_pr);
-            if (m.is_pattern(new_pattern)) {
-                new_patterns.push_back(new_pattern);
-            }
-        }
-        num = q->get_num_no_patterns();
-        for (unsigned i = 0; i < num; i++) {
-            get_cached(q->get_no_pattern(i), new_pattern, new_pattern_pr);
-            new_no_patterns.push_back(new_pattern);
-        }
-
-        remove_duplicates(new_patterns);
-        remove_duplicates(new_no_patterns);
-
-        q1 = m.mk_quantifier(q->is_forall(),
-                                     q->get_num_decls(),
-                                     q->get_decl_sorts(),
-                                     q->get_decl_names(),
-                                     new_body,
-                                     q->get_weight(),
-                                     q->get_qid(),
-                                     q->get_skid(),
-                                     new_patterns.size(),
-                                     new_patterns.c_ptr(),
-                                     new_no_patterns.size(),
-                                     new_no_patterns.c_ptr());
-        SASSERT(is_well_sorted(m, q1));
-
-        TRACE("simplifier", tout << mk_pp(q, m) << "\n" << mk_pp(q1, m) << "\n";);
-        if (m.fine_grain_proofs()) {
-            if (q != q1 && !new_body_pr) {
-                new_body_pr = m.mk_rewrite(q->get_expr(), new_body);
-            }
-            p1 = q == q1 ? 0 : m.mk_quant_intro(q, q1, new_body_pr);
-        }
-    }
-
-    expr_ref r(m);
-    elim_unused_vars(m, q1, params_ref(), r);
-
-    proof * pr = 0;
-    if (m.fine_grain_proofs()) {
-        proof * p2 = 0;
-        if (q1.get() != r.get())
-            p2 = m.mk_elim_unused_vars(q1, r);
-        pr = m.mk_transitivity(p1, p2);
-    }
-
-    cache_result(q, r, pr);
-}
-
-/**
-   \see release_plugins
-*/
-void simplifier::borrow_plugins(simplifier const & s) {
-    ptr_vector<plugin>::const_iterator it  = s.begin_plugins();
-    ptr_vector<plugin>::const_iterator end = s.end_plugins();
-    for (; it != end; ++it)
-        register_plugin(*it);
-}
-
-/**
-   \brief Make the simplifier behave as a pre-simplifier: No AC, and plugins are marked in pre-simplification mode.
-*/
-void simplifier::enable_presimp() {
-    enable_ac_support(false);
-    ptr_vector<plugin>::const_iterator it  = begin_plugins();
-    ptr_vector<plugin>::const_iterator end = end_plugins();
-    for (; it != end; ++it)
-        (*it)->enable_presimp(true);
-}
-
-/**
-   \brief This method should be invoked if the plugins of this simplifier were borrowed from a different simplifier.
-*/
-void simplifier::release_plugins() {
-    m_plugins.release();
-}
-
-void subst_simplifier::set_subst_map(expr_map * s) {
-    flush_cache();
-    m_subst_map = s;
-}
-
-bool subst_simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) {
-    if (m_subst_map && m_subst_map->contains(n)) {
-        expr *  _r;
-        proof * _p = 0;
-        m_subst_map->get(n, _r, _p);
-        r = _r;
-        p = _p;
-        if (m.coarse_grain_proofs())
-            m_subst_proofs.push_back(p);
-        return true;
-    }
-    return false;
-}
-
-static void push_core(ast_manager & m, expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
-    SASSERT(pr == 0 || m.is_undef_proof(pr) || e == m.get_fact(pr));
-    TRACE("preprocessor",
-          tout << mk_pp(e, m) << "\n";
-          if (pr) tout << mk_ll_pp(pr, m) << "\n\n";);
-    if (m.is_true(e))
-        return;
-    result.push_back(e);
-    if (m.proofs_enabled())
-        result_prs.push_back(pr);
-}
-
-static void push_and(ast_manager & m, app * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
-    unsigned num = e->get_num_args();
-    TRACE("push_and", tout << mk_pp(e, m) << "\n";);
-    for (unsigned i = 0; i < num; i++)
-        push_assertion(m, e->get_arg(i), m.mk_and_elim(pr, i), result, result_prs);
-}
-
-static void push_not_or(ast_manager & m, app * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
-    unsigned num = e->get_num_args();
-    TRACE("push_not_or", tout << mk_pp(e, m) << "\n";);
-    for (unsigned i = 0; i < num; i++) {
-        expr * child = e->get_arg(i);
-        if (m.is_not(child)) {
-            expr * not_child = to_app(child)->get_arg(0);
-            push_assertion(m, not_child, m.mk_not_or_elim(pr, i), result, result_prs);
-        }
-        else {
-            expr_ref not_child(m);
-            not_child = m.mk_not(child);
-            push_assertion(m, not_child, m.mk_not_or_elim(pr, i), result, result_prs);
-        }
-    }
-}
-
-void push_assertion(ast_manager & m, expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs) {
-    CTRACE("push_assertion", !(pr == 0 || m.is_undef_proof(pr) || m.get_fact(pr) == e),
-           tout << mk_pp(e, m) << "\n" << mk_pp(m.get_fact(pr), m) << "\n";);
-    SASSERT(pr == 0 || m.is_undef_proof(pr) || m.get_fact(pr) == e);
-    if (m.is_and(e))
-        push_and(m, to_app(e), pr, result, result_prs);
-    else if (m.is_not(e) && m.is_or(to_app(e)->get_arg(0)))
-        push_not_or(m, to_app(to_app(e)->get_arg(0)), pr, result, result_prs);
-    else
-        push_core(m, e, pr, result, result_prs);
-}
-
diff --git a/src/ast/simplifier/simplifier.h b/src/ast/simplifier/simplifier.h
deleted file mode 100644
index 5148721f1..000000000
--- a/src/ast/simplifier/simplifier.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    simplifier.h
-
-Abstract:
-
-    Generic expression simplifier with support for theory specific "plugins".
-
-Author:
-
-    Leonardo (leonardo) 2008-01-03
-
-Notes:
-
---*/
-#ifndef SIMPLIFIER_H_
-#define SIMPLIFIER_H_
-
-#include "ast/simplifier/base_simplifier.h"
-#include "ast/simplifier/simplifier_plugin.h"
-#include "util/plugin_manager.h"
-#include "ast/ast_util.h"
-#include "util/obj_hashtable.h"
-
-/**
-   \brief Local simplifier.
-   Proof production can be enabled/disabled.
-   
-   The simplifier can also apply substitutions during the
-   simplification. A substitution is a mapping from expression
-   to expression+proof, where for each entry e_1->(e_2,p) p is 
-   a proof for (= e_1 e_2).
-   
-   The simplifier can also generate coarse grain proofs. In a coarse
-   proof, local rewrite steps are omitted, and only the substitutions
-   used are tracked.
-
-   Example: 
-
-   Consider the expression (+ a b), and the substitution b->(0, p)
-   When fine grain proofs are enabled, the simplifier will produce the
-   following proof
-
-   Assume the id of the proof object p is $0. Note that p is a proof for (= b 0).
-
-   $1: [reflexivity] |- (= a a)
-   $2: [congruence] $1 $0 |- (= (+ a b) (+ a 0))
-   $3: [plus-0] |- (= (+ a 0) a)
-   $4: [transitivity] $2 $3 |- (= (+ a b) a)
-
-   When coarse grain proofs are enabled, the simplifier produces the following
-   proof:
-
-   $1: [simplifier] $0 |- (= (+ a b) a)
-*/
-class simplifier : public base_simplifier {
-protected:
-    typedef simplifier_plugin plugin;
-    plugin_manager<plugin>         m_plugins;
-    ptr_vector<expr>               m_args;
-    vector<rational>               m_mults;
-    ptr_vector<expr>               m_args2;
-
-    proof_ref_vector               m_proofs; // auxiliary vector for implementing exhaustive simplification.
-    proof_ref_vector               m_subst_proofs; // in coarse grain proof generation mode, this vector tracks the justification for substitutions (see method get_subst).
-
-    bool                           m_need_reset;
-    bool                           m_use_oeq;
-
-    bool                           m_visited_quantifier; //!< true, if the simplifier found a quantifier
-
-    bool                           m_ac_support;
-
-    expr_mark                      m_ac_mark;
-    ptr_vector<expr>               m_ac_marked;
-    obj_map<app, app *>            m_ac_cache;    // temporary cache for ac
-    obj_map<app, proof *>          m_ac_pr_cache; // temporary cache for ac
-    obj_map<expr, int>             m_colors;      // temporary cache for topological sort.
-    obj_map<expr, rational>        m_ac_mults;
-
-    /*
-      Simplifier uses an idiom for rewriting ASTs without using recursive calls.
-
-      - It uses a cache (field m_cache in base_simplifier) and a todo-stack (field m_todo in base_simplifier).
-      
-      - The cache is a mapping from AST to (AST + Proof). An entry [n -> (n',pr)] is used to store the fact
-      that n and n' are equivalent and pr is a proof for that. If proofs are disabled, then pr is 0.
-      We say n' is the result of the simplification of n.
-      Note: Some simplifications do not preserve equivalence, but equisatisfiability.
-      For saving space, we use pr = 0 also to represent the reflexivity proof [n -> (n, 0)].
-
-     
-      - The simplifier can be extended using plugin (subclasses of the class simplifier_plugin).
-      Each theory has a family ID. All operators (func_decls) and sorts from a given theory have
-      the same family_id. Given an application (object of the class app), we use the method
-      get_family_id() to obtain the family id of the operator in this application. 
-      The simplifier uses plugin to apply theory specific simplifications. The basic idea is:
-      whenever an AST with family_id X is found, invoke the plugin for this family_id.
-      A simplifier_plugin implements the following API:
-         1) bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result)
-         This method is invoked when the simplifier is trying to reduce/simplify an application
-         of the form (f args[0] ... args[num_args - 1]), and f has a family_id associated with
-         the plugin. The plugin may return false to indicate it could not simplify this application.
-         If it returns true (success), the result should be stored in the argument result.
-
-         2) bool reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result);
-         This method is a similar to the previous one, and it is used to handle associative operators.
-         A plugin does not need to implement this method, the default implementation will use the previous one.
-         The arguments mults indicates the multiplicity of every argument in args. 
-         For example, suppose this reduce is invoked with the arguments (f, 2, [3, 2], [a, b], result).
-         This represents the application (f a a a b b). 
-         Some theory simplifiers may have efficient ways to encode this multiplicity. For example,
-         the arithmetic solver, if f is "+", the multiplicity can be encoded using "*". 
-         This optimization is used because some benchmarks can create term that are very huge when
-         flattened. One "real" example (that motivated this optimization) is:
-             let a1 = x1 + x1
-             let a2 = a1 + a1
-             ...
-             let an = a{n-1} + a{n-1}
-             an
-         In this example, n was 32, so after AC flattening, we had an application
-         (+ x1 ... x1) with 2^32 arguments. Using the simple reduce would produce a stack overflow.
-
-         This class uses a topological sort for computing the multiplicities efficiently.
-         So, the field m_colors is used to implement the topological sort.
-
-
-         3) bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result)
-         This method is invoked when the sort of lhs and rhs has a family_id associated with the plugin.
-         This method allows theory specific simplifications such as:
-         (= (+ a b) b)  --> (= a 0)
-         Assuming n1 is a reference to (+ a b) and n2 to b, the simplifier would invoke
-         reduce_eq(n1, n2, result)
-         Like reduce, false can be returned if a simplification could not be applied.
-         And if true is returned, then the result is stored in the argument result.
-
-         4) bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result)
-         It is similar to reduce_eq, but it used for theory specific simplifications for
-         (distinct args[0] ... args[num_args-1])
-         Example:
-         (distinct 0 1 ... n) --> true
-
-      - The idiom used in this class is implemented in the methdo reduce_core.
-      See reduce_core for more details. The basic idea is:
-
-         1) Get the next ast to be simplified from the todo-stack.
-         2) If it is already cached, then do nothing. That is, this expression was already simplified.
-         3) Otherwise, check whether all arguments already have been simplified (method visit_children).
-             3a) The arguments that have not been simplified are added to the todo-stack by visit_children.
-             In this case visit_children will return false.
-             3b) If all arguments have already been simplified, then invoke reduce1 to perform a reduction/simplification
-             step. The cache is updated with the result.
-
-      - After invoking reduce_core(n), the cache contains an entry [n -> (n', pr)].
-
-     */
-
-    void flush_cache();
-
-    /**
-       \brief This method can be redefined in subclasses of simplifier to implement substitutions.
-       It returns true if n should be substituted by r, where the substitution is justified by the
-       proof p. The field m_subst_proofs is used to store these justifications when coarse proofs are used (PGM_COARSE).
-       This method is redefined in the class subst_simplifier. It is used in asserted_formulas
-       for implementing constant elimination. For example, if asserted_formulas contains the atoms
-       (= a (+ b 1)) (p a c), then the constant "a" can be eliminated. This is achieved by set (+ b 1) as
-       a substitution for "a".
-    */
-    virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
-
-    void reduce_core(expr * n);
-    bool visit_children(expr * n);
-    bool visit_ac(app * n);
-    virtual bool visit_quantifier(quantifier * q);
-    void reduce1(expr * n);
-    void reduce1_app(app * n);
-    void reduce1_app_core(app * n);
-    void reduce1_ac_app_core(app * n);
-    void mk_congruent_term(app * n, app_ref & r, proof_ref & p);
-    void mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p);
-    bool get_args(app * n, ptr_vector<expr> & result, proof_ref & p);
-    void get_ac_args(app * n, ptr_vector<expr> & args, vector<rational> & mults);
-    virtual void reduce1_quantifier(quantifier * q);
-    void dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * const * args, expr* result);
-    void ac_top_sort(app * n, ptr_buffer<expr> & result);
-    
-public:
-    simplifier(ast_manager & manager);
-    virtual ~simplifier();
-
-    void enable_ac_support(bool flag);
-
-    /**
-       \brief Simplify the expression \c s. Store the result in \c r, and a proof that <tt>(= s r)</tt> in \c p.
-    */
-    void operator()(expr * s, expr_ref & r, proof_ref & p);
-    void reset() { if (m_need_reset) { flush_cache(); m_need_reset = false; } }
-
-    bool visited_quantifier() const { return m_visited_quantifier; }
-
-    void mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & r);
-    void cache_result(expr * n, expr * r, proof * p) { m_need_reset = true;  base_simplifier::cache_result(n, r, p); }
-
-    void register_plugin(plugin * p);
-    ptr_vector<plugin>::const_iterator begin_plugins() const { return m_plugins.begin(); }
-    ptr_vector<plugin>::const_iterator end_plugins() const { return m_plugins.end(); }
-
-    plugin * get_plugin(family_id fid) const { return m_plugins.get_plugin(fid); }
-
-    ast_manager & get_manager() { return m; }
-
-    void borrow_plugins(simplifier const & s);
-    void release_plugins();
-
-    void enable_presimp();
-};
-
-class subst_simplifier : public simplifier {
-protected:
-    expr_map *                     m_subst_map;
-    virtual bool get_subst(expr * n, expr_ref & r, proof_ref & p);
-public:
-    subst_simplifier(ast_manager & manager):simplifier(manager), m_subst_map(0) {}
-    void set_subst_map(expr_map * s);
-};
-
-void push_assertion(ast_manager & m, expr * e, proof * pr, expr_ref_vector & result, proof_ref_vector & result_prs);
-
-#endif 
diff --git a/src/ast/simplifier/simplifier_plugin.cpp b/src/ast/simplifier/simplifier_plugin.cpp
deleted file mode 100644
index a62b15131..000000000
--- a/src/ast/simplifier/simplifier_plugin.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*++
-Copyright (c) 2006 Microsoft Corporation
-
-Module Name:
-
-    simplifier_plugin.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Leonardo de Moura (leonardo) 2008-12-29.
-
-Revision History:
-
---*/
-#include "ast/simplifier/simplifier_plugin.h"
-
-/**
-   \brief Copy every args[i] mult[i] times to new_args.
-*/
-void expand_args(unsigned num_args, rational const * mults, expr * const * args, ptr_buffer<expr> & new_args) {
-    for (unsigned i = 0; i < num_args; i++) {
-        rational const & c = mults[i];
-        SASSERT(c.is_int());
-        rational j(0);
-        while (j < c) {
-            new_args.push_back(args[i]);
-            j++;
-        }
-    }
-}
-
-bool simplifier_plugin::reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result) {
-    set_reduce_invoked();
-    if (f->is_idempotent()) {
-        return reduce(f, num_args, args, result);
-    }
-    else {
-        ptr_buffer<expr> new_args;
-        expand_args(num_args, mults, args, new_args);
-        return reduce(f, new_args.size(), new_args.c_ptr(), result);
-    }
-}
diff --git a/src/ast/simplifier/simplifier_plugin.h b/src/ast/simplifier/simplifier_plugin.h
deleted file mode 100644
index 26b5bcd59..000000000
--- a/src/ast/simplifier/simplifier_plugin.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*++
-Copyright (c) 2007 Microsoft Corporation
-
-Module Name:
-
-    simplifier_plugin.h
-
-Abstract:
-
-    Expression simplifier plugin interface.
-
-Author:
-
-    Leonardo (leonardo) 2008-01-03
-
---*/
-
-#ifndef SIMPLIFIER_PLUGIN_H_
-#define SIMPLIFIER_PLUGIN_H_
-
-#include "ast/ast.h"
-
-class simplifier;
-
-void expand_args(unsigned num_args, rational const * mults, expr * const * args, ptr_buffer<expr> & new_args);
-
-/**
-   \brief Abstract simplifier for the operators in a given family.
-*/
-class simplifier_plugin {
-protected:
-    ast_manager &   m_manager;
-    family_id       m_fid;
-    bool            m_presimp; // true if simplifier is performing pre-simplification...
-    bool            m_reduce_invoked; // true if one of the reduce methods were invoked.
-
-    void set_reduce_invoked() { m_reduce_invoked = true; }
-
-public:
-    simplifier_plugin(symbol const & fname, ast_manager & m):m_manager(m), m_fid(m.mk_family_id(fname)), m_presimp(false), m_reduce_invoked(false) {}
-    
-    bool reduce_invoked() const { return m_reduce_invoked; }
-
-    virtual ~simplifier_plugin() {}
-    
-    virtual simplifier_plugin * mk_fresh() {
-        UNREACHABLE();
-        return 0;
-    }
-    
-    /**
-       \brief Return in \c result an expression \c e equivalent to <tt>(f args[0] ... args[num_args - 1])</tt>.
-
-       Return true if succeeded.
-    */
-    virtual bool reduce(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); return false; }
-
-    /**
-       \brief Return in \c result an expression \c e equivalent to <tt>(f args[0] ... args[0] ... args[num_args - 1])</tt>.
-       Where each args[i] occurs mults[i] times.
-
-       Return true if succeeded.
-    */
-    virtual bool reduce(func_decl * f, unsigned num_args, rational const * mults, expr * const * args, expr_ref & result);
-
-    /**
-       \brief Return in \c result an expression \c e equivalent to <tt>(= lhs rhs)</tt>.
-
-       Return true if succeeded.
-    */
-    virtual bool reduce_eq(expr * lhs, expr * rhs, expr_ref & result) { set_reduce_invoked(); return false; }
-    
-    /**
-       \brief Return in \c result an expression \c e equivalent to <tt>(distinct args[0] ... args[num_args-1])</tt>.
-
-       Return true if succeeded.
-    */
-    virtual bool reduce_distinct(unsigned num_args, expr * const * args, expr_ref & result) { set_reduce_invoked(); return false; }
-    
-    family_id get_family_id() const { return m_fid; }
-   
-    /**
-       \brief Simplifiers may maintain local caches. These caches must be flushed when this method is invoked.
-    */
-    virtual void flush_caches() { /* do nothing */ }
-
-    ast_manager & get_manager() { return m_manager; }
-
-    void enable_presimp(bool flag) { m_presimp = flag; }
-
-    virtual void enable_ac_support(bool flag) {}
-};
-
-#endif
diff --git a/src/muz/pdr/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp
index 42a57214a..19ffdeeec 100644
--- a/src/muz/pdr/pdr_util.cpp
+++ b/src/muz/pdr/pdr_util.cpp
@@ -22,29 +22,26 @@ Notes:
 --*/
 
 #include <sstream>
-#include "ast/simplifier/arith_simplifier_plugin.h"
+#include "util/util.h"
+#include "util/ref_vector.h"
 #include "ast/array_decl_plugin.h"
 #include "ast/ast_pp.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
-#include "ast/rewriter/bool_rewriter.h"
-#include "muz/base/dl_util.h"
 #include "ast/for_each_expr.h"
-#include "smt/params/smt_params.h"
-#include "model/model.h"
-#include "util/ref_vector.h"
-#include "ast/rewriter/rewriter.h"
-#include "ast/rewriter/rewriter_def.h"
-#include "util/util.h"
-#include "muz/pdr/pdr_manager.h"
-#include "muz/pdr/pdr_util.h"
+#include "ast/scoped_proof.h"
 #include "ast/arith_decl_plugin.h"
 #include "ast/rewriter/expr_replacer.h"
-#include "model/model_smt2_pp.h"
+#include "ast/rewriter/bool_rewriter.h"
 #include "ast/rewriter/poly_rewriter.h"
 #include "ast/rewriter/poly_rewriter_def.h"
 #include "ast/rewriter/arith_rewriter.h"
-#include "ast/scoped_proof.h"
+#include "ast/rewriter/rewriter.h"
+#include "ast/rewriter/rewriter_def.h"
+#include "smt/params/smt_params.h"
+#include "model/model.h"
+#include "muz/base/dl_util.h"
+#include "muz/pdr/pdr_manager.h"
+#include "muz/pdr/pdr_util.h"
+#include "model/model_smt2_pp.h"
 
 
 
diff --git a/src/muz/rel/dl_bound_relation.h b/src/muz/rel/dl_bound_relation.h
index 1678e23b8..3dec9d313 100644
--- a/src/muz/rel/dl_bound_relation.h
+++ b/src/muz/rel/dl_bound_relation.h
@@ -26,7 +26,6 @@ Revision History:
 #include "muz/rel/dl_vector_relation.h"
 #include "muz/rel/dl_interval_relation.h"
 #include "ast/arith_decl_plugin.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
 #include "ast/rewriter/bool_rewriter.h"
 
 namespace datalog {
diff --git a/src/muz/rel/dl_interval_relation.h b/src/muz/rel/dl_interval_relation.h
index 05334624f..a9cce9802 100644
--- a/src/muz/rel/dl_interval_relation.h
+++ b/src/muz/rel/dl_interval_relation.h
@@ -20,13 +20,12 @@ Revision History:
 #define DL_INTERVAL_RELATION_H_
 
 
+#include "ast/arith_decl_plugin.h"
+#include "smt/old_interval.h"
 #include "muz/base/dl_context.h"
 #include "muz/rel/dl_relation_manager.h"
 #include "muz/rel/dl_base.h"
-#include "smt/old_interval.h"
 #include "muz/rel/dl_vector_relation.h"
-#include "ast/arith_decl_plugin.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
 
 namespace datalog {
 
diff --git a/src/muz/spacer/spacer_legacy_mbp.cpp b/src/muz/spacer/spacer_legacy_mbp.cpp
index fc2fd1fdd..9f03e6d2f 100644
--- a/src/muz/spacer/spacer_legacy_mbp.cpp
+++ b/src/muz/spacer/spacer_legacy_mbp.cpp
@@ -20,9 +20,6 @@ Notes:
 
 #include "ast/array_decl_plugin.h"
 #include "ast/ast_pp.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
-#include "ast/simplifier/basic_simplifier_plugin.h"
-#include "ast/simplifier/bv_simplifier_plugin.h"
 #include "ast/rewriter/bool_rewriter.h"
 #include "muz/base/dl_util.h"
 #include "ast/for_each_expr.h"
diff --git a/src/smt/arith_eq_adapter.h b/src/smt/arith_eq_adapter.h
index f18b2999f..4a8a293e3 100644
--- a/src/smt/arith_eq_adapter.h
+++ b/src/smt/arith_eq_adapter.h
@@ -23,7 +23,6 @@ Revision History:
 #include "util/obj_pair_hashtable.h"
 #include "ast/arith_decl_plugin.h"
 #include "util/statistics.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
 
 namespace smt {
 
diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h
index 26b970d30..5f4e58a1b 100644
--- a/src/smt/theory_arith.h
+++ b/src/smt/theory_arith.h
@@ -34,7 +34,6 @@ Revision History:
 #include "util/obj_pair_hashtable.h"
 #include "smt/old_interval.h"
 #include "math/grobner/grobner.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
 #include "smt/arith_eq_solver.h"
 #include "smt/theory_opt.h"
 #include "util/uint_set.h"
diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h
index fbcc9c11a..4f15a6156 100644
--- a/src/smt/theory_arith_int.h
+++ b/src/smt/theory_arith_int.h
@@ -19,12 +19,11 @@ Revision History:
 #ifndef THEORY_ARITH_INT_H_
 #define THEORY_ARITH_INT_H_
 
-#include "ast/ast_ll_pp.h"
-#include "ast/simplifier/arith_simplifier_plugin.h"
-#include "ast/well_sorted.h"
-#include "math/euclid/euclidean_solver.h"
 #include "util/numeral_buffer.h"
+#include "ast/ast_ll_pp.h"
+#include "ast/well_sorted.h"
 #include "ast/ast_smt2_pp.h"
+#include "math/euclid/euclidean_solver.h"
 
 namespace smt {
 

From 4452ff9884f69e06b8a8343b20bec1dea3ebd2ee Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 29 Aug 2017 19:16:56 -0700
Subject: [PATCH 41/74] elaborate on dom simplifier

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/tactic/core/CMakeLists.txt          |   1 +
 src/tactic/core/dom_simplify_tactic.cpp | 415 ++++++++++++------------
 src/tactic/core/dom_simplify_tactic.h   | 132 ++++++++
 3 files changed, 336 insertions(+), 212 deletions(-)
 create mode 100644 src/tactic/core/dom_simplify_tactic.h

diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt
index f192b4fa6..006948315 100644
--- a/src/tactic/core/CMakeLists.txt
+++ b/src/tactic/core/CMakeLists.txt
@@ -7,6 +7,7 @@ z3_add_component(core_tactics
     ctx_simplify_tactic.cpp
     der_tactic.cpp
     distribute_forall_tactic.cpp
+    dom_simplify_tactic.cpp
     elim_term_ite_tactic.cpp
     elim_uncnstr_tactic.cpp
     injectivity_tactic.cpp
diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp
index 3b444ef1a..21686fe64 100644
--- a/src/tactic/core/dom_simplify_tactic.cpp
+++ b/src/tactic/core/dom_simplify_tactic.cpp
@@ -17,210 +17,165 @@ Notes:
 
 --*/
 
-#if 0
 
-#include "ast/ast.h"
+#include "ast/ast_util.h"
+#include "ast/ast_pp.h"
+#include "tactic/core/dom_simplify_tactic.h"
 
-class expr_dominators {
-public:
-    typedef obj_map<expr, ptr_vector<expr>> tree_t;
-private:
-    ast_manager&          m;
-    expr_ref              m_root;
-    obj_map<unsigned>     m_expr2post; // reverse post-order number
-    ptr_vector<expr>      m_post2expr;
-    tree_t                m_parents;
-    obj_map<expr, expr*>  m_doms;
-    tree_t                m_tree;
 
-    void add_edge(tree_t& tree, expr * src, expr* dst) {        
-        tree.insert_if_not_there(src, ptr_vector<expr>()).push_back(dst);        
-    }
-
-    /**
-       \brief compute a post-order traversal for e.
-       Also populate the set of parents
-     */
-    void compute_post_order() {
-        unsigned post_num = 0;
-        SASSERT(m_post2expr.empty());
-        SASSERT(m_expr2post.empty());
-        ast_mark mark;
-        ptr_vector<expr> todo;
-        todo.push_back(m_root);
-        while (!todo.empty()) {
-            expr* e = todo.back();
-            if (is_marked(e)) {
-                todo.pop_back();
-                continue;
+/**
+   \brief compute a post-order traversal for e.
+   Also populate the set of parents
+*/
+void expr_dominators::compute_post_order() {
+    unsigned post_num = 0;
+    SASSERT(m_post2expr.empty());
+    SASSERT(m_expr2post.empty());
+    ast_mark mark;
+    ptr_vector<expr> todo;
+    todo.push_back(m_root);
+    while (!todo.empty()) {
+        expr* e = todo.back();
+        if (mark.is_marked(e)) {
+            todo.pop_back();
+            continue;
+        }
+        if (is_app(e)) {
+            app* a = to_app(e);
+            bool done = true;
+            for (expr* arg : *a) {
+                if (!mark.is_marked(arg)) {
+                    todo.push_back(arg);
+                    done = false;
+                }
             }
-            if (is_app(e)) {
-                app* a = to_app(e);
-                bool done = true;
+            if (done) {
+                mark.mark(e, true);
+                m_expr2post.insert(e, post_num++);
+                m_post2expr.push_back(e);
+                todo.pop_back();
                 for (expr* arg : *a) {
-                    if (!is_marked(arg)) {
-                        todo.push_back(arg);
-                        done = false;
-                    }
-                }
-                if (done) {
-                    mark.mark(e);
-                    m_expr2post.insert(e, post_num++);
-                    m_post2expr.push_back(e);
-                    todo.pop_back();
-                    for (expr* arg : *a) {
-                        add_edge(m_parents, arg, a);
-                    }
-                }
-            }
-            else {
-                todo.pop_back();
-            }
-        }
-    }
-
-    expr* intersect(expr* x, expr * y) {
-        unsigned n1 = m_expr2post[x];
-        unsigned n2 = m_expr2post[y];
-        while (n1 != n2) {
-            if (n1 < n2) {
-                x = m_doms[x];
-                n1 = m_expr2post[x];
-            }
-            else if (n1 > n2) {
-                y = m_doms[y];
-                n2 = m_expr2post[y];
-            }
-        }
-        SASSERT(x == y);
-        return x;
-    }
-
-    void compute_dominators() {
-        expr * e = m_root;
-        SASSERT(m_doms.empty());
-        m_doms.insert(e, e);
-        bool change = true;
-        while (change) {
-            change = false;
-            SASSERT(m_post2expr.back() == e);
-            for (unsigned i = 0; i < m_post2expr.size() - 1; ++i) {
-                expr * child = m_post2expr[i];
-                ptr_vector<expr> const& p = m_parents[child];
-                SASSERT(!p.empty());
-                expr * new_idom = p[0], * idom2 = 0;
-                for (unsigned j = 1; j < p.size(); ++j) {
-                    if (m_doms.find(p[j], idom2)) {
-                        new_idom = intersect(new_idom, idom2);
-                    }
-                }
-                if (!m_doms.find(child, idom2) || idom2 != new_idom) {
-                    m_doms.insert(child, new_idom);
-                    change = true;
+                    add_edge(m_parents, arg, a);
                 }
             }
         }
-    }
-
-    void extract_tree() {
-        for (auto const& kv : m_doms) {
-            add_edge(m_tree, kv.m_value, kv.m_key);
+        else {
+            mark.mark(e, true);
+            todo.pop_back();
         }
-    }    
-
-    void reset() {
-        m_expr2post.reset();
-        m_post2expr.reset();
-        m_parents.reset();
-        m_doms.reset();
-        m_tree.reset();
-        m_root.reset();
     }
+}
 
-
-public:
-    expr_dominators(ast_manager& m): m(m), m_root(m) {}
-
-    void compile(expr * e) {
-        reset();
-        m_root = e;
-        compute_post_order();
-        compute_dominators();
-        extract_tree();
+expr* expr_dominators::intersect(expr* x, expr * y) {
+    unsigned n1 = m_expr2post[x];
+    unsigned n2 = m_expr2post[y];
+    while (n1 != n2) {
+        if (n1 < n2) {
+            x = m_doms[x];
+            n1 = m_expr2post[x];
+        }
+        else if (n1 > n2) {
+            y = m_doms[y];
+            n2 = m_expr2post[y];
+        }
     }
+    SASSERT(x == y);
+    return x;
+}
 
-    void compile(unsigned sz, expr * const* es) {
-        expr_ref e(m.mk_and(sz, es), m);
-        compile(e);
+void expr_dominators::compute_dominators() {
+    expr * e = m_root;
+    SASSERT(m_doms.empty());
+    m_doms.insert(e, e);
+    bool change = true;
+    while (change) {
+        change = false;
+        SASSERT(m_post2expr.back() == e);
+        for (unsigned i = 0; i < m_post2expr.size() - 1; ++i) {
+            expr * child = m_post2expr[i];
+            ptr_vector<expr> const& p = m_parents[child];
+            SASSERT(!p.empty());
+            expr * new_idom = p[0], * idom2 = 0;
+            for (unsigned j = 1; j < p.size(); ++j) {
+                if (m_doms.find(p[j], idom2)) {
+                    new_idom = intersect(new_idom, idom2);
+                }
+            }
+            if (!m_doms.find(child, idom2) || idom2 != new_idom) {
+                m_doms.insert(child, new_idom);
+                change = true;
+            }
+        }
     }
+}
+
+void expr_dominators::extract_tree() {
+    for (auto const& kv : m_doms) {
+        add_edge(m_tree, kv.m_value, kv.m_key);
+    }
+}    
+
+
+
+void expr_dominators::compile(expr * e) {
+    reset();
+    m_root = e;
+    compute_post_order();
+    compute_dominators();
+    extract_tree();
+}
+
+void expr_dominators::compile(unsigned sz, expr * const* es) {
+    expr_ref e(m.mk_and(sz, es), m);
+    compile(e);
+}
+
+
+void expr_dominators::reset() {
+    m_expr2post.reset();
+    m_post2expr.reset();
+    m_parents.reset();
+    m_doms.reset();
+    m_tree.reset();
+    m_root.reset();
+}
 
-    tree_t const& get_tree() { return m_tree; }
 
-};
 
 // goes to header file:
 
-class dom_simplify_tactic : public tactic {
-public:
-    class simplifier {
-    public:
-        virtual ~simplifier() {}
-        /**
-           \brief assert_expr performs an implicit push
-         */
-        virtual bool assert_expr(expr * t, bool sign) = 0;
 
-        /**
-           \brief apply simplification.
-        */
-        virtual void operator()(expr_ref& r) = 0;
-
-        /**
-           \brief pop scopes accumulated from assertions.
-         */
-        virtual void pop(unsigned num_scopes) = 0;
-    };
-private:
-    simplifier&          m_simplifier;
-    params_ref           m_params;
-    expr_ref_vector      m_trail;
-    obj_map<expr, expr*> m_result;
-    expr_dominators      m_dominators;
-
-    expr_ref simplify(expr* t);
-    expr_ref simplify_ite(app * ite);
-    expr_ref simplify_and(app * ite) { return simplify_and_or(true, ite); }
-    expr_ref simplify_or(app * ite) { return simplify_and_or(false, ite); }
-    expr_ref simplify_and_or(bool is_and app * ite);
-
-    expr_ref get_cache(expr* t) { if (!m_result.find(r, r)) r = t; return expr_ref(r, m); }
-    void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); }
-
-    void simplify_goal(goal_ref& g);
-
-public:
-    dom_simplify_tactic(ast_manager & m, simplifier& s, params_ref const & p = params_ref());
-
-    virtual tactic * translate(ast_manager & m);
-
-    virtual ~dom_simplify_tactic();
-
-    virtual void updt_params(params_ref const & p);
-    static  void get_param_descrs(param_descrs & r);
-    virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
-    
-    virtual void operator()(goal_ref const & in, 
-                            goal_ref_buffer & result, 
-                            model_converter_ref & mc, 
-                            proof_converter_ref & pc,
-                            expr_dependency_ref & core);
-
-    virtual void cleanup();
-};
 
 // implementation:
 
-expr_ref dom_simplifier_tactic::simplify_ite(app * ite) {
+tactic * dom_simplify_tactic::translate(ast_manager & m) {
+    return alloc(dom_simplify_tactic, m, m_simplifier->translate(m), m_params);
+}
+
+void dom_simplify_tactic::operator()(
+    goal_ref const & in, 
+    goal_ref_buffer & result, 
+    model_converter_ref & mc, 
+    proof_converter_ref & pc,
+    expr_dependency_ref & core) {
+    mc = 0; pc = 0; core = 0;
+
+    tactic_report report("dom-simplify", *in.get());
+    simplify_goal(*(in.get()));
+    in->inc_depth();
+    result.push_back(in.get());
+
+}
+
+void dom_simplify_tactic::cleanup() {
+    m_trail.reset(); 
+    m_args.reset(); 
+    m_args2.reset(); 
+    m_result.reset(); 
+    m_dominators.reset(); 
+}
+
+expr_ref dom_simplify_tactic::simplify_ite(app * ite) {
     expr_ref r(m);
     expr * c = 0, * t = 0, * e = 0;
     VERIFY(m.is_ite(ite, c, t, e));
@@ -233,7 +188,7 @@ expr_ref dom_simplifier_tactic::simplify_ite(app * ite) {
         r = simplify(e);
     }
     else {
-        expr_ref t = simplify(t);
+        expr_ref new_t = simplify(t);
         pop(scope_level() - old_lvl);
         if (!assert_expr(new_c, true)) {
             return new_t;
@@ -254,14 +209,18 @@ expr_ref dom_simplifier_tactic::simplify_ite(app * ite) {
     return r;
 }
 
-expr_ref dom_simplifier_tactic::simplify(expr * e0) {
+expr_ref dom_simplify_tactic::simplify(expr * e0) {
     expr_ref r(m);
     expr* e = 0;
     if (!m_result.find(e0, e)) {
         e = e0;
     }
-
-    if (m.is_ite(e)) {
+    
+    ++m_depth;
+    if (m_depth > m_max_depth) {
+        r = e;
+    }
+    else if (m.is_ite(e)) {
         r = simplify_ite(to_app(e));
     }
     else if (m.is_and(e)) {
@@ -271,7 +230,7 @@ expr_ref dom_simplifier_tactic::simplify(expr * e0) {
         r = simplify_or(to_app(e));
     }
     else {
-        tree_t const& t = m_dominators.get_tree();
+        expr_dominators::tree_t const& t = m_dominators.get_tree();
         if (t.contains(e)) {
             ptr_vector<expr> const& children = t[e];
             for (expr * child : children) {
@@ -281,7 +240,7 @@ expr_ref dom_simplifier_tactic::simplify(expr * e0) {
         if (is_app(e)) {
             m_args.reset();
             for (expr* arg : *to_app(e)) {
-                m_args.push_back(get_cached(arg));
+                m_args.push_back(get_cached(arg)); // TBD is cache really applied to all sub-terms?
             }
             r = m.mk_app(to_app(e)->get_decl(), m_args.size(), m_args.c_ptr());
         }
@@ -289,18 +248,20 @@ expr_ref dom_simplifier_tactic::simplify(expr * e0) {
             r = e;
         }
     }
-    m_simplifier(r);
+    (*m_simplifier)(r);
     cache(e0, r);
+    TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";);
+    --m_depth;
     return r;
 }
 
-expr_ref dom_simplifier_tactic::simplify_or_and(bool is_and, app * e) {
+expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) {
     expr_ref r(m);
     unsigned old_lvl = scope_level();
     m_args.reset();
     for (expr * arg : *e) {
         r = simplify(arg);
-        if (!assert_expr(r, is_and)) {
+        if (!assert_expr(r, !is_and)) {
             r = is_and ? m.mk_false() : m.mk_true();
         }
         m_args.push_back(r);
@@ -310,7 +271,7 @@ expr_ref dom_simplifier_tactic::simplify_or_and(bool is_and, app * e) {
     m_args2.reset();
     for (expr * arg : m_args) {
         r = simplify(arg);
-        if (!assert_expr(r, is_and)) {
+        if (!assert_expr(r, !is_and)) {
             r = is_and ? m.mk_false() : m.mk_true();
         }
         m_args2.push_back(r);
@@ -321,31 +282,61 @@ expr_ref dom_simplifier_tactic::simplify_or_and(bool is_and, app * e) {
     return r;
 }
 
-void dom_simplifier_tactic::simplify_goal(goal& g) {
+
+void dom_simplify_tactic::init(goal& g) {
     expr_ref_vector args(m);
-    expr_ref fml(m);
+    unsigned sz = g.size();
     for (unsigned i = 0; i < sz; ++i) args.push_back(g.form(i));
-    fml = mk_and(args);
-    expr_ref tmp(fml);
-    // TBD: deal with dependencies.
-    do {
-        m_result.reset();
-        m_trail.reset();
-        m_dominators.compile(fml);
-#if 0
-        for (unsigned i = 0; i < sz; ++i) {
-            r = simplify(g.form(i));
-            // TBD: simplfy goal as a conjuction ?
-            //
+    expr_ref fml = mk_and(args);
+    m_result.reset();
+    m_trail.reset();
+    m_dominators.compile(fml);
+}
+
+void dom_simplify_tactic::simplify_goal(goal& g) {
+
+    SASSERT(scope_level() == 0);
+    bool change = true;
+    m_depth = 0;
+    while (change) {
+        change = false;
+
+        // go forwards
+        init(g);
+        unsigned sz = g.size();
+        for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) {
+            expr_ref r = simplify(g.form(i));
+            if (i < sz - 1 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) {
+                r = m.mk_false();
+            }
+            change |= r != g.form(i);
+            proof* new_pr = 0;
+            if (g.proofs_enabled()) {
+                new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite_star(g.form(i), r, 0, 0)); 
+            }
+            g.update(i, r, new_pr, g.dep(i));
         }
-#endif
-        tmp = fml;
-        fml = simplify(fml);
+        pop(scope_level());
+        
+        // go backwards
+        init(g);
+        sz = g.size();
+        for (unsigned i = sz; !g.inconsistent() && i > 0; ) {
+            --i;
+            expr_ref r = simplify(g.form(i));
+            if (i > 0 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) {
+                r = m.mk_false();
+            }
+            change |= r != g.form(i);
+            proof* new_pr = 0;
+            if (g.proofs_enabled()) {
+                new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite_star(g.form(i), r, 0, 0)); 
+            }
+            g.update(i, r, new_pr, g.dep(i));
+        }
+        pop(scope_level());
     }
-    while (tmp != fml);
-    //g.reset();
-    //g.add(fml);
+    SASSERT(scope_level() == 0);
 }
 
 
-#endif
diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h
new file mode 100644
index 000000000..9325f95f5
--- /dev/null
+++ b/src/tactic/core/dom_simplify_tactic.h
@@ -0,0 +1,132 @@
+/*++
+Copyright (c) 2017 Microsoft Corporation
+
+Module Name:
+
+    dom_simplify_tactic.cpp
+
+Abstract:
+
+    Dominator-based context simplifer.
+
+Author:
+
+    Nikolaj and Nuno 
+
+Notes:
+
+--*/
+
+#ifndef DOM_SIMPLIFY_TACTIC_H_
+#define DOM_SIMPLIFY_TACTIC_H_
+
+#include "ast/ast.h"
+#include "tactic/tactic.h"
+
+
+class expr_dominators {
+public:
+    typedef obj_map<expr, ptr_vector<expr>> tree_t;
+private:
+    ast_manager&          m;
+    expr_ref              m_root;
+    obj_map<expr, unsigned>     m_expr2post; // reverse post-order number
+    ptr_vector<expr>      m_post2expr;
+    tree_t                m_parents;
+    obj_map<expr, expr*>  m_doms;
+    tree_t                m_tree;
+
+    void add_edge(tree_t& tree, expr * src, expr* dst) {        
+        tree.insert_if_not_there2(src, ptr_vector<expr>())->get_data().m_value.push_back(dst);        
+    }
+
+    void compute_post_order();
+    expr* intersect(expr* x, expr * y);
+    void compute_dominators();
+    void extract_tree();
+
+public:
+    expr_dominators(ast_manager& m): m(m), m_root(m) {}
+
+    void compile(expr * e);
+    void compile(unsigned sz, expr * const* es);
+    tree_t const& get_tree() { return m_tree; }
+    void reset();
+    
+};
+
+class dom_simplify_tactic : public tactic {
+public:
+    class simplifier {
+    public:
+        virtual ~simplifier() {}
+        /**
+           \brief assert_expr performs an implicit push
+         */
+        virtual bool assert_expr(expr * t, bool sign) = 0;
+
+        /**
+           \brief apply simplification.
+        */
+        virtual void operator()(expr_ref& r) = 0;
+
+        /**
+           \brief pop scopes accumulated from assertions.
+         */
+        virtual void pop(unsigned num_scopes) = 0;
+
+        virtual simplifier * translate(ast_manager & m);
+
+    };
+private:
+    ast_manager&         m;
+    simplifier*          m_simplifier;
+    params_ref           m_params;
+    expr_ref_vector      m_trail, m_args, m_args2;
+    obj_map<expr, expr*> m_result;
+    expr_dominators      m_dominators;
+    unsigned             m_scope_level;
+    unsigned             m_depth;
+    unsigned             m_max_depth;
+
+    expr_ref simplify(expr* t);
+    expr_ref simplify_ite(app * ite);
+    expr_ref simplify_and(app * ite) { return simplify_and_or(true, ite); }
+    expr_ref simplify_or(app * ite) { return simplify_and_or(false, ite); }
+    expr_ref simplify_and_or(bool is_and, app * ite);
+    void simplify_goal(goal& g);
+
+    expr_ref get_cached(expr* t) { expr* r = 0; if (!m_result.find(r, r)) r = t; return expr_ref(r, m); }
+    void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); }
+
+    unsigned scope_level() { return m_scope_level; }
+    void pop(unsigned n) { SASSERT(n <= m_scope_level); m_scope_level -= n; m_simplifier->pop(n); }
+    bool assert_expr(expr* f, bool sign) { m_scope_level++; return m_simplifier->assert_expr(f, sign); }
+
+    void init(goal& g);
+
+public:
+    dom_simplify_tactic(ast_manager & m, simplifier* s, params_ref const & p = params_ref()):
+        m(m), m_simplifier(s), m_params(p), 
+        m_trail(m), m_args(m), m_args2(m), 
+        m_dominators(m), 
+        m_scope_level(0), m_depth(0), m_max_depth(1024) {}
+
+
+    virtual ~dom_simplify_tactic() {}
+
+    virtual tactic * translate(ast_manager & m);
+    virtual void updt_params(params_ref const & p) {}
+    static  void get_param_descrs(param_descrs & r) {}
+    virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
+    
+    virtual void operator()(goal_ref const & in, 
+                            goal_ref_buffer & result, 
+                            model_converter_ref & mc, 
+                            proof_converter_ref & pc,
+                            expr_dependency_ref & core);
+
+    virtual void cleanup();
+};
+
+#endif

From 009e94d18821ed3723f1db681ca032aae086ca0d Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 30 Aug 2017 14:00:01 -0700
Subject: [PATCH 42/74] update to theory_seq following examples from PJLJ

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/expr_substitution.h             |   1 +
 src/ast/pattern/pattern_inference.cpp   | 637 +-----------------------
 src/ast/rewriter/seq_rewriter.cpp       |  18 +-
 src/math/automata/automaton.h           |  10 +
 src/smt/smt_quantifier.cpp              |  26 +-
 src/smt/theory_seq.cpp                  |  79 ++-
 src/smt/theory_seq.h                    |   2 +
 src/tactic/core/dom_simplify_tactic.cpp | 116 ++++-
 src/tactic/core/dom_simplify_tactic.h   |  31 ++
 9 files changed, 213 insertions(+), 707 deletions(-)

diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h
index 924481dd8..d360356eb 100644
--- a/src/ast/expr_substitution.h
+++ b/src/ast/expr_substitution.h
@@ -79,6 +79,7 @@ public:
         }
     }
     bool empty() const { return m_subst.empty(); }
+    expr* find(expr * e) { proof* pr; expr* d = 0; if (find(e, d, pr)) return d; else return e; }
     bool find(expr * s, expr * & def, proof * & def_pr) { return m_subst.find(s, def, def_pr); }
     bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep) { return m_subst.find(s, def, def_pr, def_dep); }
     bool contains(expr * s) { return m_subst.contains(s); }
diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp
index dfeb29ffe..6a91dd85e 100644
--- a/src/ast/pattern/pattern_inference.cpp
+++ b/src/ast/pattern/pattern_inference.cpp
@@ -97,641 +97,6 @@ static void dump_app_vector(std::ostream & out, ptr_vector<app> const & v, ast_m
 }
 #endif
 
-#if 0
-pattern_inference_old::pattern_inference_old(ast_manager & m, pattern_inference_params & params):
-    simplifier(m),
-    m_params(params),
-    m_bfid(m.get_basic_family_id()),
-    m_afid(m.mk_family_id("arith")),
-    m_le(m),
-    m_nested_arith_only(true),
-    m_block_loop_patterns(params.m_pi_block_loop_patterns),
-    m_candidates(m),
-    m_pattern_weight_lt(m_candidates_info),
-    m_collect(m, *this),
-    m_contains_subpattern(*this),
-    m_database(m) {
-    if (params.m_pi_arith == AP_NO)
-        register_forbidden_family(m_afid);
-    enable_ac_support(false);
-}
-
-void pattern_inference_old::collect::operator()(expr * n, unsigned num_bindings) {
-    SASSERT(m_info.empty());
-    SASSERT(m_todo.empty());
-    SASSERT(m_cache.empty());
-    m_num_bindings = num_bindings;
-    m_todo.push_back(entry(n, 0));
-    while (!m_todo.empty()) {
-        entry & e      = m_todo.back();
-        n              = e.m_node;
-        unsigned delta = e.m_delta;
-        TRACE("collect", tout << "processing: " << n->get_id() << " " << delta << " kind: " << n->get_kind() << "\n";);
-        TRACE("collect_info", tout << mk_pp(n, m) << "\n";);
-        if (visit_children(n, delta)) {
-            m_todo.pop_back();
-            save_candidate(n, delta);
-        }
-    }
-    reset();
-}
-
-inline void pattern_inference_old::collect::visit(expr * n, unsigned delta, bool & visited) {
-    entry e(n, delta);
-    if (!m_cache.contains(e)) {
-        m_todo.push_back(e);
-        visited = false;
-    }
-}
-
-bool pattern_inference_old::collect::visit_children(expr * n, unsigned delta) {
-    bool visited = true;
-    unsigned i;
-    switch (n->get_kind()) {
-    case AST_APP:
-        i = to_app(n)->get_num_args();
-        while (i > 0) {
-            --i;
-            visit(to_app(n)->get_arg(i), delta, visited);
-        }
-        break;
-    case AST_QUANTIFIER:
-        visit(to_quantifier(n)->get_expr(), delta + to_quantifier(n)->get_num_decls(), visited);
-        break;
-    default:
-        break;
-    }
-    return visited;
-}
-
-inline void pattern_inference_old::collect::save(expr * n, unsigned delta, info * i) {
-    m_cache.insert(entry(n, delta), i);
-    if (i != 0)
-        m_info.push_back(i);
-}
-
-void pattern_inference_old::collect::save_candidate(expr * n, unsigned delta) {
-    switch (n->get_kind()) {
-    case AST_VAR: {
-        unsigned idx = to_var(n)->get_idx();
-        if (idx >= delta) {
-            idx = idx - delta;
-            uint_set free_vars;
-            if (idx < m_num_bindings)
-                free_vars.insert(idx);
-            info * i = 0;
-            if (delta == 0)
-                i = alloc(info, m, n, free_vars, 1);
-            else
-                i = alloc(info, m, m.mk_var(idx, to_var(n)->get_sort()), free_vars, 1);
-            save(n, delta, i);
-        }
-        else {
-            save(n, delta, 0);
-        }
-        return;
-    }
-    case AST_APP: {
-        app *       c    = to_app(n);
-        func_decl * decl = c->get_decl();
-        if (m_owner.is_forbidden(c)) {
-            save(n, delta, 0);
-            return;
-        }
-
-        if (c->get_num_args() == 0) {
-            save(n, delta, alloc(info, m, n, uint_set(), 1));
-            return;
-        }
-
-        ptr_buffer<expr> buffer;
-        bool changed   = false; // false if none of the children is mapped to a node different from itself.
-        uint_set free_vars;
-        unsigned size  = 1;
-        unsigned num   = c->get_num_args();
-        for (unsigned i = 0; i < num; i++) {
-            expr * child      = c->get_arg(i);
-            info * child_info = 0;
-#ifdef Z3DEBUG
-            bool found =
-#endif
-            m_cache.find(entry(child, delta), child_info);
-            SASSERT(found);
-            if (child_info == 0) {
-                save(n, delta, 0);
-                return;
-            }
-            buffer.push_back(child_info->m_node.get());
-            free_vars |= child_info->m_free_vars;
-            size      += child_info->m_size;
-            if (child != child_info->m_node.get())
-                changed = true;
-        }
-
-        app * new_node = 0;
-        if (changed)
-            new_node = m.mk_app(decl, buffer.size(), buffer.c_ptr());
-        else
-            new_node = to_app(n);
-        save(n, delta, alloc(info, m, new_node, free_vars, size));
-        // Remark: arithmetic patterns are only used if they are nested inside other terms.
-        // That is, we never consider x + 1 as pattern. On the other hand, f(x+1) can be a pattern
-        // if arithmetic is not in the forbidden list.
-        //
-        // Remark: The rule above has an exception. The operators (div, idiv, mod) are allowed to be
-        // used as patterns even when they are not nested in other terms. The motivation is that
-        // Z3 currently doesn't implement them (i.e., they are uninterpreted). So, some users add axioms
-        // stating properties about these operators.
-        family_id fid = c->get_family_id();
-        decl_kind k   = c->get_decl_kind();
-        if (!free_vars.empty() &&
-            (fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) {
-            TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m) << "\n";);
-            m_owner.add_candidate(new_node, free_vars, size);
-        }
-        return;
-    }
-    default:
-        save(n, delta, 0);
-        return;
-    }
-}
-
-
-void pattern_inference_old::collect::reset() {
-    m_cache.reset();
-    std::for_each(m_info.begin(), m_info.end(), delete_proc<info>());
-    m_info.reset();
-    SASSERT(m_todo.empty());
-}
-
-void pattern_inference_old::add_candidate(app * n, uint_set const & free_vars, unsigned size) {
-    for (unsigned i = 0; i < m_num_no_patterns; i++) {
-        if (n == m_no_patterns[i])
-            return;
-    }
-
-    if (!m_candidates_info.contains(n)) {
-        m_candidates_info.insert(n, info(free_vars, size));
-        m_candidates.push_back(n);
-    }
-}
-
-
-/**
-   \brief Copy the non-looping patterns in m_candidates to result when m_params.m_pi_block_loop_patterns = true.
-   Otherwise, copy m_candidates to result.
-*/
-void pattern_inference_old::filter_looping_patterns(ptr_vector<app> & result) {
-    unsigned num = m_candidates.size();
-    for (unsigned i1 = 0; i1 < num; i1++) {
-        app * n1 = m_candidates.get(i1);
-        expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1);
-        SASSERT(e1);
-        uint_set const & s1 = e1->get_data().m_value.m_free_vars;
-        if (m_block_loop_patterns) {
-            bool smaller = false;
-            for (unsigned i2 = 0; i2 < num; i2++) {
-                if (i1 != i2) {
-                    app * n2 = m_candidates.get(i2);
-                    expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2);
-                    if (e2) {
-                        uint_set const & s2 = e2->get_data().m_value.m_free_vars;
-                        // Remark: the comparison operator only makes sense if both AST nodes
-                        // contain the same number of variables.
-                        // Example:
-                        // (f X Y) <: (f (g X Z W) Y)
-                        if (s1 == s2 && m_le(m_num_bindings, n1, n2) && !m_le(m_num_bindings, n2, n1)) {
-                            smaller = true;
-                            break;
-                        }
-                    }
-                }
-            }
-            if (!smaller)
-                result.push_back(n1);
-            else
-                m_candidates_info.erase(n1);
-        }
-        else {
-            result.push_back(n1);
-        }
-    }
-}
-
-
-
-inline void pattern_inference_old::contains_subpattern::save(expr * n) {
-    unsigned id = n->get_id();
-    m_already_processed.assure_domain(id);
-    if (!m_already_processed.contains(id)) {
-        m_todo.push_back(n);
-        m_already_processed.insert(id);
-    }
-}
-
-bool pattern_inference_old::contains_subpattern::operator()(expr * n) {
-    m_already_processed.reset();
-    m_todo.reset();
-    expr2info::obj_map_entry * _e = m_owner.m_candidates_info.find_core(n);
-    SASSERT(_e);
-    uint_set const & s1 = _e->get_data().m_value.m_free_vars;
-    save(n);
-    unsigned num;
-    while (!m_todo.empty()) {
-        expr * curr = m_todo.back();
-        m_todo.pop_back();
-        switch (curr->get_kind()) {
-        case AST_APP:
-            if (curr != n) {
-                expr2info::obj_map_entry * e = m_owner.m_candidates_info.find_core(curr);
-                if (e) {
-                    uint_set const & s2 = e->get_data().m_value.m_free_vars;
-                    SASSERT(s2.subset_of(s1));
-                    if (s1 == s2) {
-                        TRACE("pattern_inference", tout << mk_pp(n, m_owner.m) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m) << "\n";);
-                        return true;
-                    }
-                }
-            }
-            num = to_app(curr)->get_num_args();
-            for (unsigned i = 0; i < num; i++)
-                save(to_app(curr)->get_arg(i));
-            break;
-        case AST_VAR:
-            break;
-        default:
-            UNREACHABLE();
-        }
-    }
-    return false;
-}
-
-/**
-   Return true if n contains a direct/indirect child that is also a
-   pattern, and contains the same number of free variables.
-*/
-inline bool pattern_inference_old::contains_subpattern(expr * n) {
-    return m_contains_subpattern(n);
-}
-
-/**
-   \brief Copy a pattern p in patterns to result, if there is no
-   direct/indirect child of p in patterns which contains the same set
-   of variables.
-
-   Remark: Every pattern p in patterns is also a member of
-   m_pattern_map.
-*/
-void pattern_inference_old::filter_bigger_patterns(ptr_vector<app> const & patterns, ptr_vector<app> & result) {
-    for (app * curr : patterns) {
-        if (!contains_subpattern(curr))
-            result.push_back(curr);
-    }
-}
-
-
-bool pattern_inference_old::pattern_weight_lt::operator()(expr * n1, expr * n2) const {
-    expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1);
-    expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2);
-    SASSERT(e1 != 0);
-    SASSERT(e2 != 0);
-    info const & i1 = e1->get_data().m_value;
-    info const & i2 = e2->get_data().m_value;
-    unsigned num_free_vars1 = i1.m_free_vars.num_elems();
-    unsigned num_free_vars2 = i2.m_free_vars.num_elems();
-    return num_free_vars1 > num_free_vars2 || (num_free_vars1 == num_free_vars2 && i1.m_size < i2.m_size);
-}
-
-/**
-   \brief Create unary patterns (single expressions that contain all
-   bound variables).  If a candidate does not contain all bound
-   variables, then it is copied to remaining_candidate_patterns.  The
-   new patterns are stored in result.
-*/
-void pattern_inference_old::candidates2unary_patterns(ptr_vector<app> const & candidate_patterns,
-                                                  ptr_vector<app> & remaining_candidate_patterns,
-                                                  app_ref_buffer  & result) {
-    for (app * candidate : candidate_patterns) {
-        expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
-        info const & i = e->get_data().m_value;
-        if (i.m_free_vars.num_elems() == m_num_bindings) {
-            app * new_pattern = m.mk_pattern(candidate);
-            result.push_back(new_pattern);
-        }
-        else {
-            remaining_candidate_patterns.push_back(candidate);
-        }
-    }
-}
-
-// TODO: this code is too inefficient when the number of candidate
-// patterns is too big.
-// HACK: limit the number of case-splits:
-#define MAX_SPLITS 32
-
-void pattern_inference_old::candidates2multi_patterns(unsigned max_num_patterns,
-                                                  ptr_vector<app> const & candidate_patterns,
-                                                  app_ref_buffer & result) {
-    SASSERT(!candidate_patterns.empty());
-    m_pre_patterns.push_back(alloc(pre_pattern));
-    unsigned sz = candidate_patterns.size();
-    unsigned num_splits = 0;
-    for (unsigned j = 0; j < m_pre_patterns.size(); j++) {
-        pre_pattern * curr = m_pre_patterns[j];
-        if (curr->m_free_vars.num_elems() == m_num_bindings) {
-            app * new_pattern = m.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr());
-            result.push_back(new_pattern);
-            if (result.size() >= max_num_patterns)
-                return;
-        }
-        else if (curr->m_idx < sz) {
-            app * n                     = candidate_patterns[curr->m_idx];
-            expr2info::obj_map_entry * e = m_candidates_info.find_core(n);
-            uint_set const & s           = e->get_data().m_value.m_free_vars;
-            if (!s.subset_of(curr->m_free_vars)) {
-                pre_pattern * new_p = alloc(pre_pattern,*curr);
-                new_p->m_exprs.push_back(n);
-                new_p->m_free_vars |= s;
-                new_p->m_idx++;
-                m_pre_patterns.push_back(new_p);
-
-                if (num_splits < MAX_SPLITS) {
-                    m_pre_patterns[j] = 0;
-                    curr->m_idx++;
-                    m_pre_patterns.push_back(curr);
-                    num_splits++;
-                }
-            }
-            else {
-                m_pre_patterns[j] = 0;
-                curr->m_idx++;
-                m_pre_patterns.push_back(curr);
-            }
-        }
-        TRACE("pattern_inference", tout << "m_pre_patterns.size(): " << m_pre_patterns.size() <<
-              "\nnum_splits: " << num_splits << "\n";);
-    }
-}
-
-void pattern_inference_old::reset_pre_patterns() {
-    std::for_each(m_pre_patterns.begin(), m_pre_patterns.end(), delete_proc<pre_pattern>());
-    m_pre_patterns.reset();
-}
-
-
-bool pattern_inference_old::is_forbidden(app * n) const {
-    func_decl const * decl = n->get_decl();
-    if (is_ground(n))
-        return false;
-    // Remark: skolem constants should not be used in patterns, since they do not
-    // occur outside of the quantifier. That is, Z3 will never match this kind of
-    // pattern.
-    if (m_params.m_pi_avoid_skolems && decl->is_skolem()) {
-        CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m) << "\n";);
-        return true;
-    }
-    if (is_forbidden(decl))
-        return true;
-    return false;
-}
-
-bool pattern_inference_old::has_preferred_patterns(ptr_vector<app> & candidate_patterns, app_ref_buffer & result) {
-    if (m_preferred.empty())
-        return false;
-    bool found = false;
-    for (app * candidate : candidate_patterns) {
-        if (m_preferred.contains(to_app(candidate)->get_decl())) {
-            expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
-            info const & i = e->get_data().m_value;
-            if (i.m_free_vars.num_elems() == m_num_bindings) {
-                TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m) << "\n";);
-                app * p = m.mk_pattern(candidate);
-                result.push_back(p);
-                found = true;
-            }
-        }
-    }
-    return found;
-}
-
-void pattern_inference_old::mk_patterns(unsigned num_bindings,
-                                    expr *   n,
-                                    unsigned num_no_patterns,
-                                    expr * const * no_patterns,
-                                    app_ref_buffer & result) {
-    m_num_bindings    = num_bindings;
-    m_num_no_patterns = num_no_patterns;
-    m_no_patterns     = no_patterns;
-
-    m_collect(n, num_bindings);
-
-    TRACE("pattern_inference",
-          tout << mk_pp(n, m);
-          tout << "\ncandidates:\n";
-          unsigned num = m_candidates.size();
-          for (unsigned i = 0; i < num; i++) {
-              tout << mk_pp(m_candidates.get(i), m) << "\n";
-          });
-
-    if (!m_candidates.empty()) {
-        m_tmp1.reset();
-        filter_looping_patterns(m_tmp1);
-        TRACE("pattern_inference",
-              tout << "candidates after removing looping-patterns:\n";
-              dump_app_vector(tout, m_tmp1, m););
-        SASSERT(!m_tmp1.empty());
-        if (!has_preferred_patterns(m_tmp1, result)) {
-            // continue if there are no preferred patterns
-            m_tmp2.reset();
-            filter_bigger_patterns(m_tmp1, m_tmp2);
-            SASSERT(!m_tmp2.empty());
-            TRACE("pattern_inference",
-                  tout << "candidates after removing bigger patterns:\n";
-                  dump_app_vector(tout, m_tmp2, m););
-            m_tmp1.reset();
-            candidates2unary_patterns(m_tmp2, m_tmp1, result);
-            unsigned num_extra_multi_patterns = m_params.m_pi_max_multi_patterns;
-            if (result.empty())
-                num_extra_multi_patterns++;
-            if (num_extra_multi_patterns > 0 && !m_tmp1.empty()) {
-                // m_pattern_weight_lt is not a total order
-                std::stable_sort(m_tmp1.begin(), m_tmp1.end(), m_pattern_weight_lt);
-                TRACE("pattern_inference",
-                      tout << "candidates after sorting:\n";
-                      dump_app_vector(tout, m_tmp1, m););
-                candidates2multi_patterns(num_extra_multi_patterns, m_tmp1, result);
-            }
-        }
-    }
-
-    reset_pre_patterns();
-    m_candidates_info.reset();
-    m_candidates.reset();
-}
-
-
-void pattern_inference_old::reduce1_quantifier(quantifier * q) {
-    TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";);
-    if (!q->is_forall()) {
-        simplifier::reduce1_quantifier(q);
-        return;
-    }
-
-    int weight = q->get_weight();
-
-    if (m_params.m_pi_use_database) {
-        m_database.initialize(g_pattern_database);
-        app_ref_vector new_patterns(m);
-        unsigned new_weight;
-        if (m_database.match_quantifier(q, new_patterns, new_weight)) {
-#ifdef Z3DEBUG
-            for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m, new_patterns.get(i))); }
-#endif
-            quantifier_ref new_q(m);
-            if (q->get_num_patterns() > 0) {
-                // just update the weight...
-                TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m) << "\n";);
-                new_q = m.update_quantifier_weight(q, new_weight);
-            }
-            else {
-                quantifier_ref tmp(m);
-                tmp   = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr());
-                new_q = m.update_quantifier_weight(tmp, new_weight);
-                TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m) << "\n";);
-            }
-            proof * pr = 0;
-            if (m.fine_grain_proofs())
-                pr = m.mk_rewrite(q, new_q);
-            cache_result(q, new_q, pr);
-            return;
-        }
-    }
-
-    if (q->get_num_patterns() > 0) {
-        simplifier::reduce1_quantifier(q);
-        return;
-    }
-
-    if (m_params.m_pi_nopat_weight >= 0)
-        weight = m_params.m_pi_nopat_weight;
-
-    SASSERT(q->get_num_patterns() == 0);
-    expr *  new_body;
-    proof * new_body_pr;
-    get_cached(q->get_expr(), new_body, new_body_pr);
-
-    ptr_buffer<expr> new_no_patterns;
-    unsigned num_no_patterns = q->get_num_no_patterns();
-    for (unsigned i = 0; i < num_no_patterns; i++) {
-        expr  * new_pattern;
-        proof * new_pattern_pr;
-        get_cached(q->get_no_pattern(i), new_pattern, new_pattern_pr);
-        new_no_patterns.push_back(new_pattern);
-    }
-
-    app_ref_buffer new_patterns(m);
-
-    if (m_params.m_pi_arith == AP_CONSERVATIVE)
-        m_forbidden.push_back(m_afid);
-
-    mk_patterns(q->get_num_decls(), new_body, new_no_patterns.size(), new_no_patterns.c_ptr(), new_patterns);
-
-    if (new_patterns.empty() && !new_no_patterns.empty()) {
-        if (new_patterns.empty()) {
-            mk_patterns(q->get_num_decls(), new_body, 0, 0, new_patterns);
-            if (m_params.m_pi_warnings && !new_patterns.empty()) {
-                warning_msg("ignoring nopats annotation because Z3 couldn't find any other pattern (quantifier id: %s)", q->get_qid().str().c_str());
-            }
-        }
-    }
-
-    if (m_params.m_pi_arith == AP_CONSERVATIVE) {
-        m_forbidden.pop_back();
-        if (new_patterns.empty()) {
-            flet<bool> l1(m_block_loop_patterns, false); // allow looping patterns
-            mk_patterns(q->get_num_decls(), new_body, new_no_patterns.size(), new_no_patterns.c_ptr(), new_patterns);
-            if (!new_patterns.empty()) {
-                weight = std::max(weight, static_cast<int>(m_params.m_pi_arith_weight));
-                if (m_params.m_pi_warnings) {
-                    warning_msg("using arith. in pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_ARITH_WEIGHT=<val>).",
-                                q->get_qid().str().c_str(), weight);
-                }
-            }
-        }
-    }
-
-    if (m_params.m_pi_arith != AP_NO && new_patterns.empty()) {
-        if (new_patterns.empty()) {
-            flet<bool> l1(m_nested_arith_only, false); // try to find a non-nested arith pattern
-            flet<bool> l2(m_block_loop_patterns, false); // allow looping patterns
-            mk_patterns(q->get_num_decls(), new_body, new_no_patterns.size(), new_no_patterns.c_ptr(), new_patterns);
-            if (!new_patterns.empty()) {
-                weight = std::max(weight, static_cast<int>(m_params.m_pi_non_nested_arith_weight));
-                if (m_params.m_pi_warnings) {
-                    warning_msg("using non nested arith. pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_NON_NESTED_ARITH_WEIGHT=<val>).",
-                                q->get_qid().str().c_str(), weight);
-                }
-                // verbose_stream() << mk_pp(q, m) << "\n";
-            }
-        }
-    }
-
-    quantifier_ref new_q(m);
-    new_q = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body);
-    if (weight != q->get_weight())
-        new_q = m.update_quantifier_weight(new_q, weight);
-    proof_ref pr(m);
-    if (m.fine_grain_proofs()) {
-        if (new_body_pr == 0)
-            new_body_pr = m.mk_reflexivity(new_body);
-        pr = m.mk_quant_intro(q, new_q, new_body_pr);
-    }
-
-    if (new_patterns.empty() && m_params.m_pi_pull_quantifiers) {
-        pull_quant pull(m);
-        expr_ref   new_expr(m);
-        proof_ref  new_pr(m);
-        pull(new_q, new_expr, new_pr);
-        quantifier * new_new_q = to_quantifier(new_expr);
-        if (new_new_q != new_q) {
-            mk_patterns(new_new_q->get_num_decls(), new_new_q->get_expr(), 0, 0, new_patterns);
-            if (!new_patterns.empty()) {
-                if (m_params.m_pi_warnings) {
-                    warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str());
-                }
-                new_q = m.update_quantifier(new_new_q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_new_q->get_expr());
-                if (m.fine_grain_proofs()) {
-                    pr = m.mk_transitivity(pr, new_pr);
-                    pr = m.mk_transitivity(pr, m.mk_quant_intro(new_new_q, new_q, m.mk_reflexivity(new_q->get_expr())));
-                }
-                TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";);
-            }
-        }
-    }
-
-    if (new_patterns.empty()) {
-        if (m_params.m_pi_warnings) {
-            warning_msg("failed to find a pattern for quantifier (quantifier id: %s)", q->get_qid().str().c_str());
-        }
-        TRACE("pi_failed", tout << mk_pp(q, m) << "\n";);
-    }
-
-    if (new_patterns.empty() && new_body == q->get_expr()) {
-        cache_result(q, q, 0);
-        return;
-    }
-
-    IF_IVERBOSE(10,
-        verbose_stream() << "(smt.inferred-patterns :qid " << q->get_qid() << "\n";
-        for (unsigned i = 0; i < new_patterns.size(); i++)
-            verbose_stream() << "  " << mk_ismt2_pp(new_patterns[i], m, 2) << "\n";
-        verbose_stream() << ")\n"; );
-
-    cache_result(q, new_q, pr);
-}
-
-#endif
 
 #include "ast/pattern/database.h"
 
@@ -881,7 +246,7 @@ void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) {
         // stating properties about these operators.
         family_id fid = c->get_family_id();
         decl_kind k   = c->get_decl_kind();
-        if (!free_vars.empty() &&
+        if (!free_vars.empty() &&            
             (fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) {
             TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m) << "\n";);
             m_owner.add_candidate(new_node, free_vars, size);
diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp
index 70eddcea1..997d5671e 100644
--- a/src/ast/rewriter/seq_rewriter.cpp
+++ b/src/ast/rewriter/seq_rewriter.cpp
@@ -26,6 +26,7 @@ Notes:
 #include "math/automata/automaton.h"
 #include "ast/well_sorted.h"
 #include "ast/rewriter/var_subst.h"
+#include "ast/rewriter/bool_rewriter.h"
 #include "math/automata/symbolic_automata_def.h"
 
 
@@ -102,7 +103,6 @@ public:
                 return sym_expr::mk_pred(fml, x->get_sort());
             }
         }
-
         sort* s = x->get_sort();
         if (m.is_bool(s)) s = y->get_sort();
         var_ref v(m.mk_var(0, s), m);
@@ -112,7 +112,10 @@ public:
             return y;
         }
         if (m.is_true(fml2)) return x;
-        expr_ref fml(m.mk_and(fml1, fml2), m);
+        if (fml1 == fml2) return x;        
+        bool_rewriter br(m);
+        expr_ref fml(m);
+        br.mk_and(fml1, fml2, fml);
         return sym_expr::mk_pred(fml, x->get_sort());
     }
     virtual T mk_or(T x, T y) {
@@ -120,12 +123,15 @@ public:
             x->get_char() == y->get_char()) {
             return x;
         }
+        if (x == y) return x;
         var_ref v(m.mk_var(0, x->get_sort()), m);
         expr_ref fml1 = x->accept(v);
         expr_ref fml2 = y->accept(v);        
         if (m.is_false(fml1)) return y;
         if (m.is_false(fml2)) return x;
-        expr_ref fml(m.mk_or(fml1, fml2), m);
+        bool_rewriter br(m);
+        expr_ref fml(m);
+        br.mk_or(fml1, fml2, fml);
         return sym_expr::mk_pred(fml, x->get_sort());
     }
 
@@ -197,10 +203,10 @@ void re2automaton::set_solver(expr_solver* solver) {
 
 eautomaton* re2automaton::operator()(expr* e) { 
     eautomaton* r = re2aut(e); 
-    if (r) {
-        display_expr1 disp(m);
+    if (r) {        
         r->compress(); 
-        TRACE("seq", r->display(tout, disp););
+        bool_rewriter br(m);
+        TRACE("seq", display_expr1 disp(m); r->display(tout, disp););
     }
     return r;
 } 
diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h
index 2f55d5cc2..12aa120b3 100644
--- a/src/math/automata/automaton.h
+++ b/src/math/automata/automaton.h
@@ -300,6 +300,16 @@ public:
         }
     }
 
+    bool is_sink_state(unsigned s) const {
+        if (is_final_state(s)) return false;
+        moves mvs;
+        get_moves_from(s, mvs);
+        for (move const& m : mvs) {
+            if (s != m.dst()) return false;
+        }
+        return true;
+    }
+
     void add_init_to_final_states() {
         add_to_final_states(init());
     }
diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp
index 22004ee78..1d58e2bb3 100644
--- a/src/smt/smt_quantifier.cpp
+++ b/src/smt/smt_quantifier.cpp
@@ -208,10 +208,8 @@ namespace smt {
             IF_VERBOSE(10, verbose_stream() << "quick checking quantifiers (unsat)...\n";);
             quick_checker mc(m_context);
             bool result = true;
-            ptr_vector<quantifier>::const_iterator it  = m_quantifiers.begin();
-            ptr_vector<quantifier>::const_iterator end = m_quantifiers.end();
-            for (; it != end; ++it)
-                if (check_quantifier(*it) && mc.instantiate_unsat(*it))
+            for (quantifier* q : m_quantifiers) 
+                if (check_quantifier(q) && mc.instantiate_unsat(q))
                     result = false;
             if (m_params.m_qi_quick_checker == MC_UNSAT || !result) {
                 m_qi_queue.instantiate();
@@ -220,9 +218,8 @@ namespace smt {
             // MC_NO_SAT is too expensive (it creates too many irrelevant instances).
             // we should use MBQI=true instead.
             IF_VERBOSE(10, verbose_stream() << "quick checking quantifiers (not sat)...\n";);
-            it  = m_quantifiers.begin();
-            for (; it != end; ++it)
-                if (check_quantifier(*it) && mc.instantiate_not_sat(*it))
+            for (quantifier* q : m_quantifiers) 
+                if (check_quantifier(q) && mc.instantiate_not_sat(q))
                     result = false;
             m_qi_queue.instantiate();
             return result;
@@ -493,10 +490,11 @@ namespace smt {
 
         virtual void assign_eh(quantifier * q) {
             m_active = true;
+            ast_manager& m = m_context->get_manager();
             if (!m_fparams->m_ematching) {
                 return;
             }
-            if (false && m_context->get_manager().is_rec_fun_def(q) && mbqi_enabled(q)) {
+            if (false && m.is_rec_fun_def(q) && mbqi_enabled(q)) {
                 return;
             }
             bool has_unary_pattern = false;
@@ -513,16 +511,20 @@ namespace smt {
                 num_eager_multi_patterns++;
             for (unsigned i = 0, j = 0; i < num_patterns; i++) {
                 app * mp = to_app(q->get_pattern(i));
-                SASSERT(m_context->get_manager().is_pattern(mp));
+                SASSERT(m.is_pattern(mp));
                 bool unary = (mp->get_num_args() == 1);
-                if (!unary && j >= num_eager_multi_patterns) {
-                    TRACE("quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n"
+                if (m.is_rec_fun_def(q) && i > 0) {
+                    // add only the first pattern
+                    TRACE("quantifier", tout << "skip recursive function body " << mk_ismt2_pp(mp, m) << "\n";);
+                }
+                else if (!unary && j >= num_eager_multi_patterns) {
+                    TRACE("quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m) << "\n"
                           << "j: " << j << " unary: " << unary << " m_params.m_qi_max_eager_multipatterns: " << m_fparams->m_qi_max_eager_multipatterns
                           << " num_eager_multi_patterns: " << num_eager_multi_patterns << "\n";);
                     m_lazy_mam->add_pattern(q, mp);
                 }
                 else {
-                    TRACE("quantifier", tout << "adding:\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n";);
+                    TRACE("quantifier", tout << "adding:\n" << mk_ismt2_pp(mp, m) << "\n";);
                     m_mam->add_pattern(q, mp);
                 }
                 if (!unary)
diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp
index 461e9e8ee..44ce804e7 100644
--- a/src/smt/theory_seq.cpp
+++ b/src/smt/theory_seq.cpp
@@ -151,9 +151,8 @@ void theory_seq::solution_map::pop_scope(unsigned num_scopes) {
 }
 
 void theory_seq::solution_map::display(std::ostream& out) const {
-    eqdep_map_t::iterator it = m_map.begin(), end = m_map.end();
-    for (; it != end; ++it) {
-        out << mk_pp(it->m_key, m) << " |-> " << mk_pp(it->m_value.first, m) << "\n";
+    for (auto const& kv : m_map) {
+        out << mk_pp(kv.m_key, m) << " |-> " << mk_pp(kv.m_value.first, m) << "\n";
     }
 }
 
@@ -187,9 +186,8 @@ void theory_seq::exclusion_table::pop_scope(unsigned num_scopes) {
 }
 
 void theory_seq::exclusion_table::display(std::ostream& out) const {
-    table_t::iterator it = m_table.begin(), end = m_table.end();
-    for (; it != end; ++it) {
-        out << mk_pp(it->first, m) << " != " << mk_pp(it->second, m) << "\n";
+    for (auto const& kv : m_table) {
+        out << mk_pp(kv.first, m) << " != " << mk_pp(kv.second, m) << "\n";
     }
 }
 
@@ -214,6 +212,7 @@ theory_seq::theory_seq(ast_manager& m):
     m_trail_stack(*this),
     m_ls(m), m_rs(m),
     m_lhs(m), m_rhs(m),
+    m_res(m),
     m_atoms_qhead(0),
     m_new_solution(false),
     m_new_propagation(false),
@@ -937,18 +936,14 @@ bool theory_seq::check_length_coherence0(expr* e) {
 
 bool theory_seq::check_length_coherence() {
 
-    obj_hashtable<expr>::iterator it = m_length.begin(), end = m_length.end();
 #if 1
-    for (; it != end; ++it) {
-        expr* e = *it;
+    for (expr* e : m_length) {
         if (check_length_coherence0(e)) {
             return true;
         }
     }
-    it = m_length.begin();
 #endif
-    for (; it != end; ++it) {
-        expr* e = *it;
+    for (expr* e : m_length) {
         if (check_length_coherence(e)) {
             return true;
         }
@@ -957,10 +952,9 @@ bool theory_seq::check_length_coherence() {
 }
 
 bool theory_seq::fixed_length() {
-    obj_hashtable<expr>::iterator it = m_length.begin(), end = m_length.end();
     bool found = false;
-    for (; it != end; ++it) {
-        if (fixed_length(*it)) {
+    for (expr* e : m_length) {
+        if (fixed_length(e)) {
             found = true;
         }
     }
@@ -2502,12 +2496,11 @@ void theory_seq::display(std::ostream & out) const {
     }
     if (!m_re2aut.empty()) {
         out << "Regex\n";
-        obj_map<expr, eautomaton*>::iterator it = m_re2aut.begin(), end = m_re2aut.end();
-        for (; it != end; ++it) {
-            out << mk_pp(it->m_key, m) << "\n";
+        for (auto const& kv : m_re2aut) {
+            out << mk_pp(kv.m_key, m) << "\n";
             display_expr disp(m);
-            if (it->m_value) {
-                it->m_value->display(out, disp);
+            if (kv.m_value) {
+                kv.m_value->display(out, disp);
             }
         }
     }
@@ -2521,9 +2514,7 @@ void theory_seq::display(std::ostream & out) const {
     }
 
     if (!m_length.empty()) {
-        obj_hashtable<expr>::iterator it = m_length.begin(), end = m_length.end();
-        for (; it != end; ++it) {
-            expr* e = *it;
+        for (expr* e : m_length) {
             rational lo(-1), hi(-1);
             lower_bound(e, lo);
             upper_bound(e, hi);
@@ -2636,6 +2627,12 @@ void theory_seq::collect_statistics(::statistics & st) const {
     st.update("seq int.to.str", m_stats.m_int_string);
 }
 
+void theory_seq::init_search_eh() {
+    m_re2aut.reset();
+    m_res.reset();
+    m_automata.reset();
+}
+
 void theory_seq::init_model(expr_ref_vector const& es) {
     expr_ref new_s(m);
     for (expr* e : es) {
@@ -3397,7 +3394,6 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) {
     literal lit = ctx.get_literal(n);
     if (!is_true) {
         e3 = m_util.re.mk_complement(e2);
-        is_true = true;
         lit.neg();
     }
     eautomaton* a = get_automaton(e3);
@@ -3416,26 +3412,17 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) {
     unsigned_vector states;
     a->get_epsilon_closure(a->init(), states);
     literal_vector lits;
-    if (is_true) {
-        lits.push_back(~lit);
-    }
+    lits.push_back(~lit);
+    
     for (unsigned i = 0; i < states.size(); ++i) {
-        if (is_true) {
-            lits.push_back(mk_accept(e1, zero, e3, states[i]));
-        }
-        else {
-            literal nlit = ~lit;
-            propagate_lit(0, 1, &nlit, mk_reject(e1, zero, e3, states[i]));
-        }
+        lits.push_back(mk_accept(e1, zero, e3, states[i]));
     }
-    if (is_true) {
-        if (lits.size() == 2) {
-            propagate_lit(0, 1, &lit, lits[1]);
-        }
-        else {
-            TRACE("seq", ctx.display_literals_verbose(tout, lits); tout << "\n";);
-            ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
-        }
+    if (lits.size() == 2) {
+        propagate_lit(0, 1, &lit, lits[1]);
+    }
+    else {
+        TRACE("seq", ctx.display_literals_verbose(tout, lits); tout << "\n";);
+        ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
     }
 }
 
@@ -4180,10 +4167,8 @@ eautomaton* theory_seq::get_automaton(expr* re) {
         TRACE("seq", result->display(tout, disp););
     }
     m_automata.push_back(result);
-    m_trail_stack.push(push_back_vector<theory_seq, scoped_ptr_vector<eautomaton> >(m_automata));
-
     m_re2aut.insert(re, result);
-    m_trail_stack.push(insert_obj_map<theory_seq, expr, eautomaton*>(m_re2aut, re));
+    m_res.push_back(re);
     return result;
 }
 
@@ -4264,6 +4249,10 @@ void theory_seq::propagate_acc_rej_length(literal lit, expr* e) {
     if (m_util.str.is_length(idx)) return;
     SASSERT(m_autil.is_numeral(idx));
     SASSERT(get_context().get_assignment(lit) == l_true);
+    if (aut->is_sink_state(src)) {
+        propagate_lit(0, 1, &lit, false_literal);
+        return;
+    }
     bool is_final = aut->is_final_state(src);
     if (is_final == is_acc) {
         propagate_lit(0, 1, &lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), idx)));
diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h
index b5a53e8e4..1f97697c2 100644
--- a/src/smt/theory_seq.h
+++ b/src/smt/theory_seq.h
@@ -328,6 +328,7 @@ namespace smt {
         // maintain automata with regular expressions.
         scoped_ptr_vector<eautomaton>  m_automata;
         obj_map<expr, eautomaton*>     m_re2aut;
+        expr_ref_vector                m_res;
 
         // queue of asserted atoms
         ptr_vector<expr>               m_atoms;
@@ -361,6 +362,7 @@ namespace smt {
         virtual void collect_statistics(::statistics & st) const;
         virtual model_value_proc * mk_value(enode * n, model_generator & mg);
         virtual void init_model(model_generator & mg);
+        virtual void init_search_eh();
 
         void init_model(expr_ref_vector const& es);
         // final check 
diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp
index 21686fe64..595f8f7c6 100644
--- a/src/tactic/core/dom_simplify_tactic.cpp
+++ b/src/tactic/core/dom_simplify_tactic.cpp
@@ -115,8 +115,6 @@ void expr_dominators::extract_tree() {
     }
 }    
 
-
-
 void expr_dominators::compile(expr * e) {
     reset();
     m_root = e;
@@ -130,7 +128,6 @@ void expr_dominators::compile(unsigned sz, expr * const* es) {
     compile(e);
 }
 
-
 void expr_dominators::reset() {
     m_expr2post.reset();
     m_post2expr.reset();
@@ -142,11 +139,8 @@ void expr_dominators::reset() {
 
 
 
-// goes to header file:
-
-
-
-// implementation:
+// -----------------------
+// dom_simplify_tactic
 
 tactic * dom_simplify_tactic::translate(ast_manager & m) {
     return alloc(dom_simplify_tactic, m, m_simplifier->translate(m), m_params);
@@ -340,3 +334,109 @@ void dom_simplify_tactic::simplify_goal(goal& g) {
 }
 
 
+// ----------------------
+// expr_substitution_simplifier
+
+bool expr_substitution_simplifier::assert_expr(expr * t, bool sign) {
+    expr* tt;    
+    if (!sign) {
+        update_substitution(t, 0);
+    }
+    else if (m.is_not(t, tt)) {
+        update_substitution(tt, 0);
+    }
+    else {
+        expr_ref nt(m.mk_not(t), m);
+        update_substitution(nt, 0);
+    }
+    return true;
+}
+
+
+bool expr_substitution_simplifier::is_gt(expr* lhs, expr* rhs) {
+    if (lhs == rhs) {
+        return false;
+    }
+    if (m.is_value(rhs)) {
+        return true;
+    }
+    SASSERT(is_ground(lhs) && is_ground(rhs));
+    if (depth(lhs) > depth(rhs)) {
+        return true;
+    }
+    if (depth(lhs) == depth(rhs) && is_app(lhs) && is_app(rhs)) {
+        app* l = to_app(lhs);
+        app* r = to_app(rhs);
+        if (l->get_decl()->get_id() != r->get_decl()->get_id()) {
+            return l->get_decl()->get_id() > r->get_decl()->get_id();
+        }
+        if (l->get_num_args() != r->get_num_args()) {
+            return l->get_num_args() > r->get_num_args();
+        }
+        for (unsigned i = 0; i < l->get_num_args(); ++i) {
+            if (l->get_arg(i) != r->get_arg(i)) {
+                return is_gt(l->get_arg(i), r->get_arg(i));
+            }
+        }
+        UNREACHABLE();
+    }
+    
+    return false;
+}
+
+void expr_substitution_simplifier::update_substitution(expr* n, proof* pr) {
+    expr* lhs, *rhs, *n1;
+    if (is_ground(n) && (m.is_eq(n, lhs, rhs) || m.is_iff(n, lhs, rhs))) {
+        compute_depth(lhs);
+        compute_depth(rhs);
+        if (is_gt(lhs, rhs)) {
+            TRACE("propagate_values", tout << "insert " << mk_pp(lhs, m) << " -> " << mk_pp(rhs, m) << "\n";);
+            m_scoped_substitution.insert(lhs, rhs, pr);
+            return;
+        }
+        if (is_gt(rhs, lhs)) {
+            TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";);
+            m_scoped_substitution.insert(rhs, lhs, m.mk_symmetry(pr));
+            return;
+        }
+        TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";);
+    }
+    if (m.is_not(n, n1)) {
+        m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr)); 
+    }
+    else {
+        m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr)); 
+    }
+}
+
+void expr_substitution_simplifier::compute_depth(expr* e) {
+    ptr_vector<expr> todo;
+    todo.push_back(e);    
+    while (!todo.empty()) {
+        e = todo.back();
+        unsigned d = 0;
+        if (m_expr2depth.contains(e)) {
+            todo.pop_back();
+            continue;
+        }
+        if (is_app(e)) {
+            app* a = to_app(e);
+            bool visited = true;
+            for (expr* arg : *a) {
+                unsigned d1 = 0;
+                if (m_expr2depth.find(arg, d1)) {
+                    d = std::max(d, d1);
+                }
+                else {
+                    visited = false;
+                    todo.push_back(arg);
+                }
+            }
+            if (!visited) {
+                continue;
+            }
+        }
+        todo.pop_back();
+        m_expr2depth.insert(e, d + 1);
+    }
+}
diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h
index 9325f95f5..2fa79dd1d 100644
--- a/src/tactic/core/dom_simplify_tactic.h
+++ b/src/tactic/core/dom_simplify_tactic.h
@@ -21,6 +21,7 @@ Notes:
 #define DOM_SIMPLIFY_TACTIC_H_
 
 #include "ast/ast.h"
+#include "ast/expr_substitution.h"
 #include "tactic/tactic.h"
 
 
@@ -129,4 +130,34 @@ public:
     virtual void cleanup();
 };
 
+class expr_substitution_simplifier : public dom_simplify_tactic::simplifier {
+    ast_manager&             m;
+    expr_substitution        m_subst;
+    scoped_expr_substitution m_scoped_substitution;
+    obj_map<expr, unsigned>  m_expr2depth;
+
+    // move from asserted_formulas to here..
+    void compute_depth(expr* e);
+    bool is_gt(expr* lhs, expr* rhs);
+    unsigned depth(expr* e) { return m_expr2depth[e]; }
+
+public:
+    expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst) {}
+    virtual ~expr_substitution_simplifier() {}
+    virtual bool assert_expr(expr * t, bool sign);
+
+    void update_substitution(expr* n, proof* pr);
+    
+    virtual void operator()(expr_ref& r) { r = m_scoped_substitution.find(r); }
+    
+    virtual void pop(unsigned num_scopes) { m_scoped_substitution.pop(num_scopes); }
+    
+    virtual simplifier * translate(ast_manager & m) {
+        SASSERT(m_subst.empty());
+        return alloc(expr_substitution_simplifier, m);
+    }
+
+    
+};
+
 #endif

From 62f8cc1289f925adc416bef90eab3ef3657dcf55 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Thu, 31 Aug 2017 07:33:38 -0700
Subject: [PATCH 43/74] fix ordering for value propagation to ensure values are
 preferred

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/smt/asserted_formulas.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index 626588ab3..6ecf26bd8 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -514,9 +514,15 @@ bool asserted_formulas::is_gt(expr* lhs, expr* rhs) {
     if (lhs == rhs) {
         return false;
     }
-    if (m.is_value(rhs)) {
+    // values are always less in ordering than non-values.
+    bool v1 = m.is_value(lhs);
+    bool v2 = m.is_value(rhs);
+    if (!v1 && v2) {
         return true;
     }
+    if (v1 && !v2) {
+        return false;
+    }
     SASSERT(is_ground(lhs) && is_ground(rhs));
     if (depth(lhs) > depth(rhs)) {
         return true;

From fff54d5d08e610c856ba5651ab0a1276079445aa Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 03:56:10 -0700
Subject: [PATCH 44/74] fix perf regression with negative polynomial
 normalization, adding new datatype plugin

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/CMakeLists.txt                     |   1 +
 src/ast/ast.cpp                            |  43 +-
 src/ast/ast.h                              |  13 +-
 src/ast/datatype_decl_plugin.cpp           |   2 +-
 src/ast/datatype_decl_plugin.h             |   2 +-
 src/ast/datatype_decl_plugin2.cpp          | 819 +++++++++++++++++++++
 src/ast/datatype_decl_plugin2.h            | 257 +++++++
 src/ast/rewriter/arith_rewriter.cpp        |  66 +-
 src/ast/rewriter/arith_rewriter.h          |   3 +
 src/ast/rewriter/arith_rewriter_params.pyg |   1 +
 src/smt/asserted_formulas.cpp              |   2 +
 src/smt/params/theory_arith_params.cpp     |   3 +
 src/smt/theory_datatype.cpp                |  13 +-
 src/util/symbol_table.h                    |  21 +-
 14 files changed, 1207 insertions(+), 39 deletions(-)
 create mode 100644 src/ast/datatype_decl_plugin2.cpp
 create mode 100644 src/ast/datatype_decl_plugin2.h

diff --git a/src/ast/CMakeLists.txt b/src/ast/CMakeLists.txt
index 0a14d9473..47ed2def8 100644
--- a/src/ast/CMakeLists.txt
+++ b/src/ast/CMakeLists.txt
@@ -14,6 +14,7 @@ z3_add_component(ast
     ast_util.cpp
     bv_decl_plugin.cpp
     datatype_decl_plugin.cpp
+    datatype_decl_plugin2.cpp
     decl_collector.cpp
     dl_decl_plugin.cpp
     expr2polynomial.cpp
diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp
index c1ff04ada..1a35e710a 100644
--- a/src/ast/ast.cpp
+++ b/src/ast/ast.cpp
@@ -188,18 +188,14 @@ decl_info::decl_info(decl_info const& other) :
 
 
 void decl_info::init_eh(ast_manager & m) {
-    vector<parameter>::iterator it  = m_parameters.begin();
-    vector<parameter>::iterator end = m_parameters.end();
-    for (; it != end; ++it) {
-        it->init_eh(m);
+    for (parameter & p : m_parameters) {
+        p.init_eh(m);
     }
 }
 
 void decl_info::del_eh(ast_manager & m) {
-    vector<parameter>::iterator it  = m_parameters.begin();
-    vector<parameter>::iterator end = m_parameters.end();
-    for (; it != end; ++it) {
-        it->del_eh(m, m_family_id);
+    for (parameter & p : m_parameters) {
+        p.del_eh(m, m_family_id);
     }
 }
 
@@ -1935,6 +1931,35 @@ sort * ast_manager::mk_sort(symbol const & name, sort_info * info) {
     return register_node(new_node);
 }
 
+sort * ast_manager::substitute(sort* s, unsigned n, sort * const * src, sort * const * dst) {
+    for (unsigned i = 0; i < n; ++i) {
+        if (s == src[i]) return dst[i];
+    }
+
+    vector<parameter> ps;
+    bool change = false;
+    sort_ref_vector sorts(*this);
+    for (unsigned i = 0; i < s->get_num_parameters(); ++i) {
+        parameter const& p = s->get_parameter(i);
+        if (p.is_ast()) {
+            SASSERT(is_sort(p.get_ast()));
+            change = true;
+            sorts.push_back(substitute(to_sort(p.get_ast()), n, src, dst));
+            ps.push_back(parameter(sorts.back()));
+        }
+        else {
+            ps.push_back(p);
+        }
+    }
+    if (!change) {
+        return s;
+    }
+    decl_info dinfo(s->get_family_id(), s->get_decl_kind(), ps.size(), ps.c_ptr(), s->private_parameters());
+    sort_info sinfo(dinfo, s->get_num_elements());
+    return mk_sort(s->get_name(), &sinfo);
+}
+
+
 sort * ast_manager::mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters) {
     user_sort_plugin * plugin = get_user_sort_plugin();
     decl_kind kind = plugin->register_name(name);
@@ -2580,7 +2605,7 @@ expr * ast_manager::get_some_value(sort * s) {
     return mk_model_value(0, s);
 }
 
-bool ast_manager::is_fully_interp(sort const * s) const {
+bool ast_manager::is_fully_interp(sort * s) const {
     if (is_uninterp(s))
         return false;
     family_id fid = s->get_family_id();
diff --git a/src/ast/ast.h b/src/ast/ast.h
index 699268bd0..a1e31f46f 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -335,13 +335,17 @@ public:
               unsigned num_parameters = 0, parameter const * parameters = 0, bool private_parameters = false):
         decl_info(family_id, k, num_parameters, parameters, private_parameters), m_num_elements(num_elements) {
     }
-    sort_info(sort_info const& other) : decl_info(other), m_num_elements(other.m_num_elements) {
+    sort_info(sort_info const& other) : decl_info(other), m_num_elements(other.m_num_elements) {            
     }
+    sort_info(decl_info const& di, sort_size const& num_elements) : 
+        decl_info(di), m_num_elements(num_elements) {}
+
     ~sort_info() {}
 
     bool is_infinite() const { return m_num_elements.is_infinite(); }
     bool is_very_big() const { return m_num_elements.is_very_big(); }
     sort_size const & get_num_elements() const { return m_num_elements; }
+    void set_num_elements(sort_size const& s) { m_num_elements = s; }
 };
 
 std::ostream & operator<<(std::ostream & out, sort_info const & info);
@@ -567,6 +571,7 @@ public:
     bool is_very_big() const { return get_info() == 0 || get_info()->is_very_big(); }
     bool is_sort_of(family_id fid, decl_kind k) const { return get_family_id() == fid && get_decl_kind() == k; }
     sort_size const & get_num_elements() const { return get_info()->get_num_elements(); }
+    void set_num_elements(sort_size const& s) { get_info()->set_num_elements(s); }
     unsigned get_size() const { return get_obj_size(); }
 };
 
@@ -988,7 +993,7 @@ public:
 
     // Return true if the interpreted sort s does not depend on uninterpreted sorts.
     // This may be the case, for example, for array and datatype sorts.
-    virtual bool is_fully_interp(sort const * s) const { return true; }
+    virtual bool is_fully_interp(sort * s) const { return true; }
 
     // Event handlers for deleting/translating PARAM_EXTERNAL
     virtual void del(parameter const & p) {}
@@ -1655,6 +1660,8 @@ public:
 
     sort * mk_sort(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = 0);
 
+    sort * substitute(sort* s, unsigned n, sort * const * src, sort * const * dst);
+
     sort * mk_bool_sort() const { return m_bool_sort; }
 
     sort * mk_proof_sort() const { return m_proof_sort; }
@@ -1667,7 +1674,7 @@ public:
        \brief A sort is "fully" interpreted if it is interpreted,
        and doesn't depend on other uninterpreted sorts.
     */
-    bool is_fully_interp(sort const * s) const;
+    bool is_fully_interp(sort * s) const;
 
     func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
                              unsigned arity, sort * const * domain, sort * range = 0);
diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp
index b4f30767f..446992105 100644
--- a/src/ast/datatype_decl_plugin.cpp
+++ b/src/ast/datatype_decl_plugin.cpp
@@ -675,7 +675,7 @@ expr * datatype_decl_plugin::get_some_value(sort * s) {
     return m_manager->mk_app(c, args.size(), args.c_ptr());
 }
 
-bool datatype_decl_plugin::is_fully_interp(sort const * s) const {
+bool datatype_decl_plugin::is_fully_interp(sort * s) const {
     SASSERT(s->is_sort_of(m_family_id, DATATYPE_SORT));
     parameter const * parameters = s->get_parameters();
     unsigned num_types        = parameters[0].get_int();
diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h
index 3d008ad9c..dcd352471 100644
--- a/src/ast/datatype_decl_plugin.h
+++ b/src/ast/datatype_decl_plugin.h
@@ -150,7 +150,7 @@ public:
 
     virtual expr * get_some_value(sort * s);
 
-    virtual bool is_fully_interp(sort const * s) const;
+    virtual bool is_fully_interp(sort * s) const;
 
     virtual bool is_value(app* e) const;
 
diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
new file mode 100644
index 000000000..4e60abcce
--- /dev/null
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -0,0 +1,819 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    datatype_decl_plugin.cpp
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-01-10.
+
+Revision History:
+
+    // compute sort sizes and insert them.
+    // have a notion of pre-sort or just attach sort size after declaration.
+
+--*/
+#include "ast/datatype_decl_plugin2.h"
+#include "util/warning.h"
+#include "ast/ast_smt2_pp.h"
+
+namespace datatype {
+
+    func_decl_ref accessor::instantiate(sort_ref_vector const& ps) const {
+        unsigned n = ps.size();
+        SASSERT(n == get_def().params().size());
+        sort_ref range(m.substitute(m_range, n, get_def().params().c_ptr(), ps.c_ptr()), m);
+        sort_ref src(get_def().instantiate(ps));
+        sort* srcs[1] = { src.get() };
+        parameter pas[2] = { parameter(name()), parameter(get_constructor().name()) };
+        return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_ACCESSOR, 2, pas, 1, srcs, range), m);
+    }
+
+    func_decl_ref accessor::instantiate(sort* dt) const {
+        sort_ref_vector sorts = get_def().u().datatype_params(dt);
+        return instantiate(sorts);
+    }
+
+    def const& accessor::get_def() const { return m_constructor->get_def(); }
+    util& accessor::u() const { return m_constructor->u(); }
+
+    util& constructor::u() const { return m_def->u(); }
+
+    func_decl_ref constructor::instantiate(sort_ref_vector const& ps) const {
+        sort_ref_vector domain(m);
+        for (accessor const& a : accessors()) {
+            domain.push_back(a.instantiate(ps)->get_range());
+        }
+        sort_ref range = get_def().instantiate(ps);
+        parameter pas[1] = { parameter(name()) };
+        return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_CONSTRUCTOR, 1, pas, domain.size(), domain.c_ptr(), range), m);        
+    }
+
+    func_decl_ref constructor::instantiate(sort* dt) const {
+        sort_ref_vector sorts = get_def().u().datatype_params(dt);
+        return instantiate(sorts);
+    }
+
+    sort_ref def::instantiate(sort_ref_vector const& sorts) const {
+        sort_ref s(m);
+        if (!m_sort) {
+            vector<parameter> ps;
+            for (sort * s : m_params) ps.push_back(parameter(s));
+            m_sort = m.mk_sort(u().get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr());
+        }
+        if (sorts.empty()) {
+            return m_sort;
+        }
+        return sort_ref(m.substitute(m_sort, sorts.size(), sorts.c_ptr(), m_params.c_ptr()), m);
+    }
+
+    enum status {
+        GRAY,
+        BLACK
+    };
+
+    namespace decl {
+
+        plugin::~plugin() {
+            finalize();
+        }
+
+        void plugin::finalize() {
+            for (auto& kv : m_defs) {
+                dealloc(kv.m_value);
+            }
+            m_defs.reset();
+            m_util = 0; // force deletion
+        }
+
+        util & plugin::u() const {
+            SASSERT(m_manager);
+            if (m_util.get() == 0) {
+                m_util = alloc(util, *m_manager);
+            }
+            return *(m_util.get());
+        }
+
+        struct invalid_datatype {};
+
+        sort * plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
+            try {
+                if (k != DATATYPE_SORT) {
+                    throw invalid_datatype();
+                }
+                if (num_parameters < 1) {
+                    throw invalid_datatype();                    
+                }
+                parameter const & name = parameters[0];
+                if (!name.is_symbol()) {
+                    TRACE("datatype", tout << "expected symol parameter at position " << 0 << " got: " << name << "\n";);
+                    throw invalid_datatype();
+                }
+                for (unsigned i = 1; i < num_parameters; ++i) {
+                    parameter const& s = parameters[i];
+                    if (!s.is_ast() || !is_sort(s.get_ast())) {
+                        TRACE("datatype", tout << "expected sort parameter at position " << i << " got: " << s << "\n";);
+                        throw invalid_datatype();
+                    }
+                }
+                                
+                sort* s = m_manager->mk_sort(name.get_symbol(),
+                                             sort_info(m_family_id, k, num_parameters, parameters, true));
+                // compute datatype size
+                sort_size ts = u().get_datatype_size(s); 
+                s->set_num_elements(ts);
+                return s;
+            }
+            catch (invalid_datatype) {
+                m_manager->raise_exception("invalid datatype");
+                return 0;
+            }
+        }
+
+        func_decl * plugin::mk_update_field(
+            unsigned num_parameters, parameter const * parameters, 
+            unsigned arity, sort * const * domain, sort * range) {
+            decl_kind k = OP_DT_UPDATE_FIELD;
+            ast_manager& m = *m_manager;
+            
+            if (num_parameters != 1 || !parameters[0].is_ast()) {
+                m.raise_exception("invalid parameters for datatype field update");
+                return 0;
+            }
+            if (arity != 2) {
+                m.raise_exception("invalid number of arguments for datatype field update");
+                return 0;
+            }
+            func_decl* acc = 0;
+            if (is_func_decl(parameters[0].get_ast())) {
+                acc = to_func_decl(parameters[0].get_ast());
+            }
+            if (acc && !u().is_accessor(acc)) {
+                acc = 0;
+            }
+            if (!acc) {
+                m.raise_exception("datatype field update requires a datatype accessor as the second argument");
+                return 0;
+            }
+            sort* dom = acc->get_domain(0);
+            sort* rng = acc->get_range();
+            if (dom != domain[0]) {
+                m.raise_exception("first argument to field update should be a data-type");
+                return 0;
+            }
+            if (rng != domain[1]) {
+                std::ostringstream buffer;
+                buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m) 
+                       << " instead of " << mk_ismt2_pp(domain[1], m);
+                m.raise_exception(buffer.str().c_str());
+                return 0;
+            }
+            range = domain[0];
+            func_decl_info info(m_family_id, k, num_parameters, parameters);
+            return m.mk_func_decl(symbol("update-field"), arity, domain, range, info);
+        }
+
+        
+        func_decl * decl::plugin::mk_constructor(unsigned num_parameters, parameter const * parameters, 
+                                                 unsigned arity, sort * const * domain, sort * range) {
+            ast_manager& m = *m_manager;
+            SASSERT(num_parameters == 1 && parameters[0].is_symbol() && range && u().is_datatype(range));
+            if (num_parameters != 1 || !parameters[0].is_symbol() || !range || !u().is_datatype(range)) {
+                m_manager->raise_exception("invalid parameters for datatype constructor");
+            }
+            // we blindly trust other conditions are met, including domain types.
+            symbol name = parameters[0].get_symbol();
+            func_decl_info info(m_family_id, OP_DT_CONSTRUCTOR, num_parameters, parameters);
+            info.m_private_parameters = true;
+            return m.mk_func_decl(name, arity, domain, range, info);
+        }
+
+        func_decl * decl::plugin::mk_recognizer(unsigned num_parameters, parameter const * parameters, 
+                                                unsigned arity, sort * const * domain, sort *) {
+            ast_manager& m = *m_manager;
+            SASSERT(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
+            SASSERT(u().is_datatype(domain[0]));
+            // blindly trust that parameter is a constructor
+            sort* range = m_manager->mk_bool_sort();
+            func_decl_info info(m_family_id, OP_DT_RECOGNISER, num_parameters, parameters);
+            info.m_private_parameters = true;
+            symbol name = to_func_decl(parameters[0].get_ast())->get_name();
+            return m.mk_func_decl(name, arity, domain, range);
+        }
+
+        func_decl * decl::plugin::mk_accessor(unsigned num_parameters, parameter const * parameters, 
+                                              unsigned arity, sort * const * domain, sort * range) 
+        {            
+            ast_manager& m = *m_manager;
+            SASSERT(arity == 1 && num_parameters == 2 && parameters[0].is_symbol() && parameters[0].is_symbol());
+            SASSERT(u().is_datatype(domain[0]));
+            SASSERT(range);
+            func_decl_info info(m_family_id, OP_DT_ACCESSOR, num_parameters, parameters);
+            info.m_private_parameters = true;
+            symbol name = parameters[0].get_symbol();
+            return m.mk_func_decl(name, arity, domain, range);            
+        }
+
+        func_decl * decl::plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, 
+                                               unsigned arity, sort * const * domain, sort * range) {                        
+            switch (k) {
+            case OP_DT_CONSTRUCTOR:
+                return mk_constructor(num_parameters, parameters, arity, domain, range);
+            case OP_DT_RECOGNISER:
+                return mk_recognizer(num_parameters, parameters, arity, domain, range);                
+            case OP_DT_ACCESSOR:
+                return mk_accessor(num_parameters, parameters, arity, domain, range);                
+            case OP_DT_UPDATE_FIELD: 
+                return mk_update_field(num_parameters, parameters, arity, domain, range);
+            default:
+                m_manager->raise_exception("invalid datatype operator kind");
+                return 0;
+            }
+        }
+
+        def& plugin::add(symbol const& name, unsigned n, sort * const * params) {
+            ast_manager& m = *m_manager;
+            def* d = alloc(def, m, u(), name, m_class_id, n, params);
+            m_defs.insert(name, d);
+            m_def_block.push_back(name);
+            return *d;
+        }
+
+        void plugin::end_def_block() {
+            ast_manager& m = *m_manager;
+            sort_ref_vector sorts(m);
+            for (symbol const& s : m_def_block) {
+                def const& d = *m_defs[s];
+                sort_ref_vector ps(m);
+                sorts.push_back(d.instantiate(ps));
+            }
+            if (!u().is_well_founded(sorts.size(), sorts.c_ptr())) {
+                m_manager->raise_exception("datatype is not well-founded");
+            }
+        }
+
+        void plugin::del(symbol const& s) {
+            def* d = 0;
+            if (m_defs.find(s, d)) dealloc(d);
+            m_defs.remove(s);
+        }
+
+        bool plugin::is_value_visit(expr * arg, ptr_buffer<app> & todo) const {
+            if (!is_app(arg))
+                return false;
+            family_id fid = to_app(arg)->get_family_id();
+            if (fid == m_family_id) {
+                if (!u().is_constructor(to_app(arg)))
+                    return false;
+                if (to_app(arg)->get_num_args() == 0)
+                    return true;
+                todo.push_back(to_app(arg));
+                return true;
+            }
+            else {
+                return m_manager->is_value(arg);
+            }
+        }
+        
+        bool plugin::is_value(app * e) const {
+            TRACE("dt_is_value", tout << "checking\n" << mk_ismt2_pp(e, *m_manager) << "\n";);
+            if (!u().is_constructor(e))
+                return false;
+            if (e->get_num_args() == 0)
+                return true;
+            // REMARK: if the following check is too expensive, we should
+            // cache the values in the decl::plugin.
+            ptr_buffer<app> todo;
+            // potentially expensive check for common sub-expressions.
+            for (expr* arg : *e) {
+                if (!is_value_visit(arg, todo)) {
+                    TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";);
+                    return false;
+                }
+            }
+            while (!todo.empty()) {
+                app * curr = todo.back();
+                SASSERT(u().is_constructor(curr));
+                todo.pop_back();
+                for (expr* arg : *curr) {
+                    if (!is_value_visit(arg, todo)) {
+                        TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";);
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        
+        void plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
+            if (logic == symbol::null) {
+                op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD));
+            }
+        }
+
+        expr * plugin::get_some_value(sort * s) {
+            SASSERT(u().is_datatype(s));
+            func_decl * c = u().get_non_rec_constructor(s);
+            ptr_buffer<expr> args;
+            for (unsigned i = 0; i < c->get_arity(); i++) {
+                args.push_back(m_manager->get_some_value(c->get_domain(i)));
+            }
+            return m_manager->mk_app(c, args.size(), args.c_ptr());
+        }
+
+        bool plugin::is_fully_interp(sort * s) const {
+            return u().is_fully_interp(s);
+        }
+    }
+
+    sort_ref_vector util::datatype_params(sort * s) const {
+        SASSERT(is_datatype(s));
+        sort_ref_vector result(m);
+        for (unsigned i = 1; i < s->get_num_parameters(); ++i) {
+            result.push_back(to_sort(s->get_parameter(i).get_ast()));
+        }
+        return result;
+    }
+
+
+    bool util::is_fully_interp(sort * s) const {
+        SASSERT(is_datatype(s));
+        bool fi = true;
+        return fi;
+        if (m_is_fully_interp.find(s, fi)) {
+            return fi;
+        }
+        unsigned sz = m_fully_interp_trail.size();
+        m_is_fully_interp.insert(s, true);
+        def const& d = get_def(s);
+        bool is_interp = true;
+        m_fully_interp_trail.push_back(s);
+        for (constructor const& c : d) {
+            for (accessor const& a : c) {
+                func_decl_ref ac = a.instantiate(s);
+                sort* r = ac->get_range();
+                if (!m.is_fully_interp(r)) {
+                    is_interp = false;
+                    break;
+                }
+            }
+            if (!is_interp) break;
+        }
+        for (unsigned i = sz; i < m_fully_interp_trail.size(); ++i) {
+            m_is_fully_interp.remove(m_fully_interp_trail[i]);
+        }
+        m_fully_interp_trail.shrink(sz);
+        m_is_fully_interp.insert(s, is_interp);
+        m_asts.push_back(s);
+        return true;
+    }
+
+    /**
+       \brief Return true if the inductive datatype is recursive.
+    */
+    bool util::is_recursive_core(sort* s) const {
+        obj_map<sort, status> already_found;
+        ptr_vector<sort> todo, subsorts;
+        todo.push_back(s);
+        status st;
+        while (!todo.empty()) {
+            s = todo.back();
+            if (already_found.find(s, st) && st == BLACK) {
+                todo.pop_back();
+                continue;
+            }
+            already_found.insert(s, GRAY);
+            def const& d = get_def(s);
+            bool can_process       = true;
+            for (constructor const& c : d) {
+                for (accessor const& a : c) {
+                    sort* d = a.range();
+                    // check if d is a datatype sort
+                    subsorts.reset();
+                    get_subsorts(d, subsorts);
+                    for (sort * s2 : subsorts) {
+                        if (is_datatype(s2)) {
+                            if (already_found.find(s2, st)) {
+                                // type is recursive
+                                if (st == GRAY) return true;
+                            }
+                            else {
+                                todo.push_back(s2);
+                                can_process = false;
+                            }
+                        }
+                    }
+                }
+            }
+            if (can_process) {
+                already_found.insert(s, BLACK);
+                todo.pop_back();
+            }
+        }
+        return false;
+    }
+    
+    /**
+       \brief Return the size of the inductive datatype.
+       Pre-condition: The given argument constains the parameters of an inductive datatype.
+    */
+    sort_size util::get_datatype_size(sort* s0) {
+        obj_map<sort, status> already_found;
+        obj_map<sort, sort_size> szs;
+        ptr_vector<sort> todo;
+        todo.push_back(s0);
+        status st;
+        while (!todo.empty()) {
+            sort* s  = todo.back();
+            if (already_found.find(s, st) && st == BLACK) {
+                todo.pop_back();
+                continue;
+            }
+            already_found.insert(s, GRAY);
+            bool     is_very_big       = false;
+            bool     can_process       = true;
+            def const& d = get_def(s);
+            for (constructor const& c : d) {
+                for (accessor const& a : c) {
+                    func_decl_ref ac = a.instantiate(s);
+                    sort* r = ac->get_range();
+                    if (is_datatype(r)) {
+                        if (already_found.find(r, st)) {
+                            // type is infinite
+                            if (st == GRAY) return sort_size();
+                        }
+                        else {
+                            todo.push_back(r);
+                            can_process = false;
+                        }
+                    }
+                    else if (r->is_infinite()) {
+                        // type is infinite
+                        return sort_size();
+                    }
+                    else if (r->is_very_big()) {
+                        is_very_big = true;
+                    }
+                }
+            }
+
+            if (can_process) {
+                todo.pop_back();
+                already_found.insert(s, BLACK);
+                if (is_very_big) {
+                    szs.insert(s, sort_size::mk_very_big());
+                }
+                else {
+                    // the type is not infinite nor the number of elements is infinite...
+                    // computing the number of elements
+                    rational num;
+                    def const& d = get_def(s);
+                    for (constructor const& c : d) {
+                        rational c_num(1); 
+                        for (accessor const& a : c) {
+                            func_decl_ref ac = a.instantiate(s);
+                            sort* r = ac->get_range();
+                            if (szs.contains(r)) {
+                                c_num *= rational(szs[r].size(), rational::ui64());
+                            }
+                            else {
+                                SASSERT(!r->is_infinite() && !r->is_very_big());
+                                c_num *= rational(r->get_num_elements().size(), rational::ui64());
+                            }
+                        }
+                        num += c_num;
+                    }
+                    szs.insert(s, sort_size(num));
+                }
+            }
+        }
+        return szs[s0];
+    }
+
+
+    /**
+       \brief Return true if the inductive datatype is well-founded.
+       Pre-condition: The given argument constains the parameters of an inductive datatype.
+    */
+    bool util::is_well_founded(unsigned num_types, sort* const* sorts) {
+        buffer<bool> well_founded(num_types, false);
+        obj_map<sort, unsigned> sort2id;
+        for (unsigned i = 0; i < num_types; ++i) {
+            sort2id.insert(sorts[i], i);
+        }
+        unsigned num_well_founded = 0, id = 0;
+        bool changed;
+        do {
+            changed = false;
+            for (unsigned tid = 0; tid < num_types; tid++) {
+                if (well_founded[tid]) {
+                    continue;
+                }
+                sort* s = sorts[tid];
+                def const& d = get_def(s);
+                for (constructor const& c : d) {
+                    bool found_nonwf = false;
+                    for (accessor const& a : c) {
+                        if (sort2id.find(a.range(), id) && !well_founded[id]) {
+                            found_nonwf = true;
+                            break;
+                        }
+                    }
+                    if (!found_nonwf) {
+                        changed = true;
+                        well_founded[tid] = true;
+                        num_well_founded++;
+                        break;
+                    }
+                }
+            }
+        } 
+        while(changed && num_well_founded < num_types);
+        return num_well_founded == num_types;
+    }
+
+    def const& util::get_def(sort* s) const {
+        return m_plugin->get_def(s);
+    }
+
+    void util::get_subsorts(sort* s, ptr_vector<sort>& sorts) const {
+        sorts.push_back(s);
+        for (unsigned i = 0; i < s->get_num_parameters(); ++i) {
+            parameter const& p = s->get_parameter(i);
+            if (p.is_ast() && is_sort(p.get_ast())) {
+                get_subsorts(to_sort(p.get_ast()), sorts);
+            }
+        }
+    }
+
+
+    util::util(ast_manager & m):
+        m(m),
+        m_family_id(m.mk_family_id("datatype")),
+        m_asts(m),
+        m_start(0) {
+        m_plugin = dynamic_cast<decl::plugin*>(m.get_plugin(m_family_id));
+    }
+
+    util::~util() {
+        std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc<ptr_vector<func_decl> >());
+    }
+
+    ptr_vector<func_decl> const & util::get_datatype_constructors(sort * ty) {
+        SASSERT(is_datatype(ty));
+        ptr_vector<func_decl> * r = 0;
+        if (m_datatype2constructors.find(ty, r))
+            return *r;
+        r = alloc(ptr_vector<func_decl>);
+        m_asts.push_back(ty);
+        m_vectors.push_back(r);
+        m_datatype2constructors.insert(ty, r);
+        def const& d = get_def(ty);
+        for (constructor const& c : d) {
+            func_decl_ref f = c.instantiate(ty);
+            m_asts.push_back(f);
+            r->push_back(f);
+        }
+        return *r;
+    }
+
+    ptr_vector<func_decl> const & util::get_constructor_accessors(func_decl * con) {
+        SASSERT(is_constructor(con));
+        ptr_vector<func_decl> * res = 0;
+        if (m_constructor2accessors.find(con, res))
+            return *res;
+        res = alloc(ptr_vector<func_decl>);
+        m_asts.push_back(con);
+        m_vectors.push_back(res);
+        m_constructor2accessors.insert(con, res);
+        sort * datatype = con->get_range();
+        def const& d = get_def(datatype);
+        for (constructor const& c : d) {
+            if (c.name() == con->get_name()) {
+                for (accessor const& a : c) {
+                    res->push_back(a.instantiate(datatype));
+                }
+                break;
+            }
+        }
+        return *res;
+    }
+
+    func_decl * util::get_constructor_recognizer(func_decl * constructor) {
+        SASSERT(is_constructor(constructor));
+        func_decl * d = 0;
+        if (m_constructor2recognizer.find(constructor, d))
+            return d;
+        sort * datatype = constructor->get_range();
+        parameter ps[1] = { parameter(constructor) };
+        d  = m.mk_func_decl(m_family_id, OP_DT_RECOGNISER, 1, ps, 1, &datatype);
+        SASSERT(d);
+        m_asts.push_back(constructor);
+        m_asts.push_back(d);
+        m_constructor2recognizer.insert(constructor, d);
+        return d;
+    }
+
+    func_decl * util::get_recognizer_constructor(func_decl * recognizer) {
+        SASSERT(is_recognizer(recognizer));
+        return to_func_decl(recognizer->get_parameter(0).get_ast());
+    }
+
+    bool util::is_recursive(sort * ty) {
+        SASSERT(is_datatype(ty));
+        bool r = false;
+        if (!m_is_recursive.find(ty, r)) {
+            r = is_recursive_core(ty);
+            m_is_recursive.insert(ty, r);
+            m_asts.push_back(ty);
+        }
+        return r;
+    }
+
+    bool util::is_enum_sort(sort* s) {
+        if (!is_datatype(s)) {
+            return false;
+        }
+        bool r = false;
+        if (m_is_enum.find(s, r))
+            return r;
+        ptr_vector<func_decl> const& cnstrs = get_datatype_constructors(s);
+        r = true;
+        for (unsigned i = 0; r && i < cnstrs.size(); ++i) {
+            r = cnstrs[i]->get_arity() == 0;
+        }
+        m_is_enum.insert(s, r);
+        m_asts.push_back(s);
+        return r;
+    }
+
+    func_decl * util::get_accessor_constructor(func_decl * accessor) { 
+        SASSERT(is_accessor(accessor));
+        func_decl * r = 0;
+        if (m_accessor2constructor.find(accessor, r))
+            return r;
+        sort * datatype = accessor->get_domain(0);
+        symbol c_id   = accessor->get_parameter(1).get_symbol();
+        def const& d = get_def(datatype);
+        func_decl_ref fn(m);
+        for (constructor const& c : d) {
+            if (c.name() == c_id) {
+                fn = c.instantiate(datatype);
+                break;
+            }
+        }
+        r = fn;
+        m_accessor2constructor.insert(accessor, r);
+        m_asts.push_back(accessor);
+        m_asts.push_back(r);
+        return r;
+    }
+
+
+    void util::reset() {
+        m_datatype2constructors.reset();
+        m_datatype2nonrec_constructor.reset();
+        m_constructor2accessors.reset();
+        m_constructor2recognizer.reset();
+        m_recognizer2constructor.reset();
+        m_accessor2constructor.reset();
+        m_is_recursive.reset();
+        m_is_enum.reset();
+        std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc<ptr_vector<func_decl> >());
+        m_vectors.reset();
+        m_asts.reset();
+        ++m_start;
+    }
+
+
+    /**
+       \brief Return a constructor mk(T_1, ... T_n)
+       where each T_i is not a datatype or it is a datatype that contains 
+       a constructor that will not contain directly or indirectly an element of the given sort.
+    */
+    func_decl * util::get_non_rec_constructor(sort * ty) {
+        SASSERT(is_datatype(ty));
+        func_decl * r = 0;
+        if (m_datatype2nonrec_constructor.find(ty, r))
+            return r;
+        r = 0;
+        ptr_vector<sort> forbidden_set;
+        forbidden_set.push_back(ty);
+        r = get_non_rec_constructor_core(ty, forbidden_set);
+        SASSERT(forbidden_set.back() == ty);
+        SASSERT(r);
+        m_asts.push_back(ty);
+        m_asts.push_back(r);
+        m_datatype2nonrec_constructor.insert(ty, r);
+        return r;
+    }
+
+    /**
+       \brief Return a constructor mk(T_1, ..., T_n) where
+       each T_i is not a datatype or it is a datatype t not in forbidden_set,
+       and get_non_rec_constructor_core(T_i, forbidden_set union { T_i })
+    */
+    func_decl * util::get_non_rec_constructor_core(sort * ty, ptr_vector<sort> & forbidden_set) {
+        // We must select a constructor c(T_1, ..., T_n):T such that
+        //   1) T_i's are not recursive
+        // If there is no such constructor, then we select one that 
+        //   2) each type T_i is not recursive or contains a constructor that does not depend on T
+        ptr_vector<func_decl> const& constructors = get_datatype_constructors(ty);
+        // step 1)
+        unsigned sz = constructors.size();
+        ++m_start;
+        for (unsigned j = 0; j < sz; ++j) {        
+            func_decl * c = constructors[(j + m_start) % sz];
+            unsigned num_args = c->get_arity();
+            unsigned i = 0;
+            for (; i < num_args; i++) {
+                sort * T_i = c->get_domain(i);
+                if (is_datatype(T_i))
+                    break;
+            }
+            if (i == num_args)
+                return c;
+        }
+        // step 2)
+        for (unsigned j = 0; j < sz; ++j) {        
+            func_decl * c = constructors[(j + m_start) % sz];
+            TRACE("util_bug", tout << "non_rec_constructor c: " << c->get_name() << "\n";);
+            unsigned num_args = c->get_arity();
+            unsigned i = 0;
+            for (; i < num_args; i++) {
+                sort * T_i = c->get_domain(i);
+                TRACE("util_bug", tout << "c: " << c->get_name() << " i: " << i << " T_i: " << T_i->get_name() << "\n";);
+                if (!is_datatype(T_i)) {
+                    TRACE("util_bug", tout << "T_i is not a datatype\n";);
+                    continue;
+                }
+                if (std::find(forbidden_set.begin(), forbidden_set.end(), T_i) != forbidden_set.end()) {
+                    TRACE("util_bug", tout << "T_i is in forbidden_set\n";);
+                    break;
+                }
+                forbidden_set.push_back(T_i);
+                func_decl * nested_c = get_non_rec_constructor_core(T_i, forbidden_set);
+                SASSERT(forbidden_set.back() == T_i);
+                forbidden_set.pop_back();
+                TRACE("util_bug", tout << "nested_c: " << nested_c->get_name() << "\n";);
+                if (nested_c == 0)
+                    break;
+            }
+            if (i == num_args)
+                return c;
+        }
+        return 0;
+    }
+
+
+    /**
+       \brief Two datatype sorts s1 and s2 are siblings if they were
+       defined together in the same mutually recursive definition.
+    */
+    bool util::are_siblings(sort * s1, sort * s2) {
+        if (!is_datatype(s1) || !is_datatype(s2)) {
+            return s1 == s2;
+        }
+        else {
+            return get_def(s1).id() == get_def(s2).id();
+        }
+    }
+
+    void util::display_datatype(sort *s0, std::ostream& strm) {
+        ast_mark mark;
+        ptr_buffer<sort> todo;
+        SASSERT(is_datatype(s0));
+        strm << s0->get_name() << " where\n";
+        todo.push_back(s0);
+        mark.mark(s0, true);
+        while (!todo.empty()) {
+            sort* s = todo.back();
+            todo.pop_back();
+            strm << s->get_name() << " =\n";
+
+            ptr_vector<func_decl> const& cnstrs = get_datatype_constructors(s);
+            for (unsigned i = 0; i < cnstrs.size(); ++i) {
+                func_decl* cns = cnstrs[i];
+                func_decl* rec = get_constructor_recognizer(cns);
+                strm << "  " << cns->get_name() << " :: " << rec->get_name() << " :: ";
+                ptr_vector<func_decl> const & accs = get_constructor_accessors(cns);
+                for (unsigned j = 0; j < accs.size(); ++j) {
+                    func_decl* acc = accs[j];
+                    sort* s1 = acc->get_range();
+                    strm << "(" << acc->get_name() << ": " << s1->get_name() << ") "; 
+                    if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) {
+                        mark.mark(s1, true);
+                        todo.push_back(s1);
+                    }          
+                }
+                strm << "\n";
+            }
+        }
+    }
+}
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
new file mode 100644
index 000000000..fad7ff174
--- /dev/null
+++ b/src/ast/datatype_decl_plugin2.h
@@ -0,0 +1,257 @@
+/*++
+Copyright (c) 2006 Microsoft Corporation
+
+Module Name:
+
+    datatype_decl_plugin.h
+
+Abstract:
+
+    <abstract>
+
+Author:
+
+    Leonardo de Moura (leonardo) 2008-01-09.
+
+Revision History:
+
+--*/
+#ifndef DATATYPE_DECL_PLUGIN2_H_
+#define DATATYPE_DECL_PLUGIN2_H_
+
+#include "ast/ast.h"
+#include "util/buffer.h"
+#include "util/symbol_table.h"
+#include "util/obj_hashtable.h"
+
+namespace datatype {
+
+    class util;
+    class def;
+    class accessor;
+    class constructor;
+
+    enum sort_kind {
+        DATATYPE_SORT
+    };
+    
+    enum op_kind {
+        OP_DT_CONSTRUCTOR,
+        OP_DT_RECOGNISER,
+        OP_DT_ACCESSOR,
+        OP_DT_UPDATE_FIELD,
+        LAST_DT_OP
+    };
+
+    class accessor {
+        ast_manager& m;
+        symbol   m_name;
+        sort_ref m_domain;
+        sort_ref m_range;
+        constructor* m_constructor;
+    public:
+        accessor(ast_manager& m, symbol const& n):
+            m(m),
+            m_name(n),
+            m_domain(m),
+            m_range(m)
+        {}
+        sort* range() const { return m_range; }
+        symbol const& name() const { return m_name; }
+        func_decl_ref instantiate(sort_ref_vector const& ps) const;
+        func_decl_ref instantiate(sort* dt) const;
+        void attach(constructor* d) { m_constructor = d; }
+        constructor const& get_constructor() const { return *m_constructor; }
+        def const& get_def() const;
+        util& u() const;
+    };
+
+    class constructor {
+        ast_manager&     m;
+        symbol           m_name;
+        vector<accessor> m_accessors;
+        def*             m_def;
+    public:
+        constructor(ast_manager& m, symbol n): m(m), m_name(n) {}
+        void add(accessor& a) { m_accessors.push_back(a); a.attach(this); }
+        symbol const& name() const { return m_name; }
+        vector<accessor> const& accessors() const { return m_accessors; }
+        vector<accessor>::const_iterator begin() const { return m_accessors.begin(); }
+        vector<accessor>::const_iterator end() const { return m_accessors.end(); }
+        func_decl_ref instantiate(sort_ref_vector const& ps) const;
+        func_decl_ref instantiate(sort* dt) const;
+        void attach(def* d) { m_def = d; }
+        def const& get_def() const { return *m_def; }
+        util& u() const;
+    };
+
+    class def {
+        ast_manager&        m;
+        util&               m_util;
+        symbol              m_name;
+        unsigned            m_class_id;
+        sort_ref_vector     m_params;
+        mutable sort_ref    m_sort;
+        vector<constructor> m_constructors;
+    public:
+        def(ast_manager& m, util& u, symbol const& n, unsigned class_id, unsigned num_params, sort * const* params):
+            m(m),
+            m_util(u),
+            m_name(n),
+            m_class_id(class_id),            
+            m_params(m, num_params, params), 
+            m_sort(m)
+        {}
+        void add(constructor& c) {
+            m_constructors.push_back(c);
+            c.attach(this);
+        }
+        symbol const& name() const { return m_name; }
+        unsigned id() const { return m_class_id; }
+        sort_ref instantiate(sort_ref_vector const& ps) const;
+        vector<constructor> const& constructors() const { return m_constructors; }
+        vector<constructor>::const_iterator begin() const { return m_constructors.begin(); }
+        vector<constructor>::const_iterator end() const { return m_constructors.end(); }
+        sort_ref_vector const& params() const { return m_params; }
+        util& u() const { return m_util; }
+    };
+
+    namespace decl {
+
+        class plugin : public decl_plugin {
+            mutable scoped_ptr<util> m_util;
+            map<symbol, def*, symbol_hash_proc, symbol_eq_proc> m_defs; 
+            svector<symbol>          m_def_block;
+            unsigned                 m_class_id;
+            util & u() const;
+        public:
+            plugin(): m_class_id(0) {}
+            virtual ~plugin();
+
+            virtual void finalize();
+        
+            virtual decl_plugin * mk_fresh() { return alloc(plugin); }
+        
+            virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters);
+        
+            virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, 
+                                             unsigned arity, sort * const * domain, sort * range);
+                
+            virtual expr * get_some_value(sort * s);
+        
+            virtual bool is_fully_interp(sort * s) const;
+        
+            virtual bool is_value(app* e) const;
+        
+            virtual bool is_unique_value(app * e) const { return is_value(e); }
+        
+            virtual void get_op_names(svector<builtin_name> & op_names, symbol const & logic);
+                
+            void begin_def_block() { m_class_id++; m_def_block.reset(); }
+
+            void end_def_block();
+
+            def& add(symbol const& name, unsigned n, sort * const * params);
+
+            void del(symbol const& d);
+
+            def const& get_def(sort* s) const { def* d = 0; VERIFY(m_defs.find(datatype_name(s), d)); return *d; }
+
+        private:
+            bool is_value_visit(expr * arg, ptr_buffer<app> & todo) const;
+        
+            func_decl * mk_update_field(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
+            func_decl * mk_constructor(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
+            func_decl * mk_accessor(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
+            func_decl * mk_recognizer(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
+            symbol datatype_name(sort * s) const {
+                //SASSERT(u().is_datatype(s));
+                return s->get_parameter(0).get_symbol();
+            }
+            
+        };
+    }
+
+    class util {
+        ast_manager & m;
+        family_id     m_family_id;
+        mutable decl::plugin* m_plugin;
+
+        
+        func_decl * get_constructor(sort * ty, unsigned c_id) const;
+        
+        obj_map<sort, ptr_vector<func_decl> *>      m_datatype2constructors;
+        obj_map<sort, func_decl *>                  m_datatype2nonrec_constructor;
+        obj_map<func_decl, ptr_vector<func_decl> *> m_constructor2accessors;
+        obj_map<func_decl, func_decl *>             m_constructor2recognizer;
+        obj_map<func_decl, func_decl *>             m_recognizer2constructor;
+        obj_map<func_decl, func_decl *>             m_accessor2constructor;
+        obj_map<sort, bool>                         m_is_recursive;
+        obj_map<sort, bool>                         m_is_enum;
+        mutable obj_map<sort, bool>                         m_is_fully_interp;
+        mutable ast_ref_vector                      m_asts;
+        ptr_vector<ptr_vector<func_decl> >          m_vectors;
+        unsigned                                    m_start;
+        mutable ptr_vector<sort>                            m_fully_interp_trail;
+        
+        func_decl * get_non_rec_constructor_core(sort * ty, ptr_vector<sort> & forbidden_set);
+        func_decl * get_constructor(sort * ty, unsigned c_id);
+
+        friend class decl::plugin;
+
+        bool is_recursive_core(sort * s) const;
+        sort_size get_datatype_size(sort* s0);
+        bool is_well_founded(unsigned num_types, sort* const* sorts);
+        def const& get_def(sort* s) const;
+        void get_subsorts(sort* s, ptr_vector<sort>& sorts) const;        
+
+    public:
+        util(ast_manager & m);
+        ~util();
+        ast_manager & get_manager() const { return m; }
+        bool is_datatype(sort const* s) const { return is_sort_of(s, m_family_id, DATATYPE_SORT); }
+        bool is_enum_sort(sort* s);
+        bool is_recursive(sort * ty);
+        bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
+        bool is_recognizer(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); }
+        bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); }
+        bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
+        bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
+        bool is_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); }
+        bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
+        bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
+        ptr_vector<func_decl> const & get_datatype_constructors(sort * ty);
+        unsigned get_datatype_num_constructors(sort * ty);
+        unsigned get_datatype_num_parameter_sorts(sort * ty);
+        sort*  get_datatype_parameter_sort(sort * ty, unsigned idx);
+        func_decl * get_non_rec_constructor(sort * ty);
+        func_decl * get_constructor_recognizer(func_decl * constructor);
+        ptr_vector<func_decl> const & get_constructor_accessors(func_decl * constructor);
+        func_decl * get_accessor_constructor(func_decl * accessor);
+        func_decl * get_recognizer_constructor(func_decl * recognizer);
+        family_id get_family_id() const { return m_family_id; }
+        bool are_siblings(sort * s1, sort * s2);
+        bool is_func_decl(op_kind k, unsigned num_params, parameter const* params, func_decl* f);
+        bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f);
+        void reset();
+        void display_datatype(sort *s, std::ostream& strm);
+        bool is_fully_interp(sort * s) const;
+        sort_ref_vector datatype_params(sort * s) const;
+    };
+
+};
+
+#endif /* DATATYPE_DECL_PLUGIN_H_ */
+
diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp
index 4dfd69ddd..c1ef61938 100644
--- a/src/ast/rewriter/arith_rewriter.cpp
+++ b/src/ast/rewriter/arith_rewriter.cpp
@@ -35,6 +35,7 @@ void arith_rewriter::updt_local_params(params_ref const & _p) {
     m_mul2power       = p.mul_to_power();
     m_elim_rem        = p.elim_rem();
     m_expand_tan      = p.expand_tan();
+    m_expand_eqs      = p.expand_eqs();
     set_sort_sums(p.sort_sums());
 }
 
@@ -454,7 +455,20 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
             st = BR_DONE;
         }
     }
-    if (st == BR_DONE && arg1 == orig_arg1 && arg2 == orig_arg2) {
+    if (kind == EQ && m_expand_eqs) {
+        result = m().mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2));
+        return BR_REWRITE2;
+    }
+    else if (is_numeral(arg2, a2) && is_neg_poly(arg1, new_arg1)) {
+        a2.neg();
+        new_arg2 = m_util.mk_numeral(a2, m_util.is_int(new_arg1));
+        switch (kind) {
+        case LE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_DONE;
+        case GE: result = m_util.mk_le(new_arg1, new_arg2); return BR_DONE;
+        case EQ: result = m_util.mk_eq(new_arg1, new_arg2); return BR_DONE;
+        }
+    }
+    else if (st == BR_DONE && arg1 == orig_arg1 && arg2 == orig_arg2) {
         // Nothing new; return BR_FAILED to avoid rewriting loops.
         return BR_FAILED;
     }
@@ -494,6 +508,56 @@ br_status arith_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result
     return mk_le_ge_eq_core(arg1, arg2, EQ, result);
 }
 
+expr_ref arith_rewriter::neg_monomial(expr* e) const {
+    expr_ref_vector args(m());
+    rational a1;
+    if (is_app(e) & m_util.is_mul(e)) {
+        if (is_numeral(to_app(e)->get_arg(0), a1)) {
+            if (!a1.is_minus_one()) {
+                args.push_back(m_util.mk_numeral(-a1, m_util.is_int(e)));
+            }
+            args.append(to_app(e)->get_num_args() - 1, to_app(e)->get_args() + 1);
+        }
+        else {
+            args.push_back(m_util.mk_numeral(rational(-1), m_util.is_int(e)));
+            args.append(to_app(e)->get_num_args(), to_app(e)->get_args());
+        }
+    }
+    else {
+        args.push_back(m_util.mk_numeral(rational(-1), m_util.is_int(e)));
+        args.push_back(e);
+    }
+    if (args.size() == 1) {
+        return expr_ref(args.back(), m());
+    }
+    else {
+        return expr_ref(m_util.mk_mul(args.size(), args.c_ptr()), m());
+    }
+}
+
+bool arith_rewriter::is_neg_poly(expr* t, expr_ref& neg) const {
+    rational r;
+    if (m_util.is_mul(t) && is_numeral(to_app(t)->get_arg(0), r) && r.is_neg()) {
+        neg = neg_monomial(t);
+        return true;
+    }
+
+    if (!m_util.is_add(t)) {
+        return false;
+    }
+    expr * t2 = to_app(t)->get_arg(0);
+
+    if (m_util.is_mul(t2) && is_numeral(to_app(t2)->get_arg(0), r) && r.is_neg()) {
+        expr_ref_vector args1(m());
+        for (expr* e1 : *to_app(t)) {
+            args1.push_back(neg_monomial(e1));
+        }       
+        neg = m_util.mk_add(args1.size(), args1.c_ptr());      
+        return true;
+    }    
+    return false;
+}
+
 bool arith_rewriter::is_anum_simp_target(unsigned num_args, expr * const * args) {
     if (!m_anum_simp)
         return false;
diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h
index 5d9fb1d66..352cb5a6c 100644
--- a/src/ast/rewriter/arith_rewriter.h
+++ b/src/ast/rewriter/arith_rewriter.h
@@ -55,6 +55,7 @@ class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
     bool m_push_to_real;
     bool m_anum_simp;
     bool m_elim_rem;
+    bool m_expand_eqs;
     unsigned m_max_degree;
 
     void get_coeffs_gcd(expr * t, numeral & g, bool & first, unsigned & num_consts);
@@ -88,6 +89,8 @@ class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
     bool is_2_pi_integer_offset(expr * t, expr * & m);
     bool is_pi_integer(expr * t);
     bool is_pi_integer_offset(expr * t, expr * & m);
+    bool is_neg_poly(expr* e, expr_ref& neg) const;
+    expr_ref neg_monomial(expr * e) const;
     expr * mk_sin_value(rational const & k);
     app * mk_sqrt(rational const & k);
 
diff --git a/src/ast/rewriter/arith_rewriter_params.pyg b/src/ast/rewriter/arith_rewriter_params.pyg
index 8a41d838d..94ada1b6d 100644
--- a/src/ast/rewriter/arith_rewriter_params.pyg
+++ b/src/ast/rewriter/arith_rewriter_params.pyg
@@ -12,4 +12,5 @@ def_module_params(module_name='rewriter',
                           ("arith_lhs", BOOL, False, "all monomials are moved to the left-hand-side, and the right-hand-side is just a constant."),
                           ("elim_to_real", BOOL, False, "eliminate to_real from arithmetic predicates that contain only integers."),
                           ("push_to_real", BOOL, True, "distribute to_real over * and +."),
+                          ("expand_eqs", BOOL, False, "expand equalities into two inequalities"),
                           ("elim_rem", BOOL, False, "replace (rem x y) with (ite (>= y 0) (mod x y) (- (mod x y))).")))
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index 6ecf26bd8..fb2004c77 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -123,6 +123,8 @@ void asserted_formulas::set_eliminate_and(bool flag) {
     p.set_bool("arith_lhs", true);
     p.set_bool("sort_sums", true);
     p.set_bool("rewrite_patterns", true);
+    p.set_bool("expand_eqs", m_params.m_arith_expand_eqs);
+    p.set_bool("gcd_rounding", true);
     m_rewriter.updt_params(p);
     flush_cache();
 }
diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp
index ab80f0c67..9b8aa9b81 100644
--- a/src/smt/params/theory_arith_params.cpp
+++ b/src/smt/params/theory_arith_params.cpp
@@ -18,6 +18,7 @@ Revision History:
 --*/
 #include "smt/params/theory_arith_params.h"
 #include "smt/params/smt_params_helper.hpp"
+#include "ast/rewriter/arith_rewriter_params.hpp"
 
 void theory_arith_params::updt_params(params_ref const & _p) {
     smt_params_helper p(_p);
@@ -36,6 +37,8 @@ void theory_arith_params::updt_params(params_ref const & _p) {
     m_arith_bound_prop = static_cast<bound_prop_mode>(p.arith_propagation_mode());
     m_arith_dump_lemmas = p.arith_dump_lemmas();
     m_arith_reflect = p.arith_reflect();
+    arith_rewriter_params ap(_p);
+    m_arith_expand_eqs = ap.expand_eqs();
 }
 
 
diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp
index 806d6706b..beb1acf63 100644
--- a/src/smt/theory_datatype.cpp
+++ b/src/smt/theory_datatype.cpp
@@ -437,10 +437,7 @@ namespace smt {
             ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, 0, m_used_eqs.size(), m_used_eqs.c_ptr())));
             TRACE("occurs_check",
                   tout << "occurs_check: true\n";
-                  enode_pair_vector::const_iterator it  = m_used_eqs.begin();
-                  enode_pair_vector::const_iterator end = m_used_eqs.end();
-                  for(; it != end; ++it) {
-                      enode_pair const & p = *it;
+                  for (enode_pair const& p : m_used_eqs) {
                       tout << "eq: #" << p.first->get_owner_id() << " #" << p.second->get_owner_id() << "\n";
                       tout << mk_bounded_pp(p.first->get_owner(), get_manager()) << " " << mk_bounded_pp(p.second->get_owner(), get_manager()) << "\n";
                   });
@@ -613,11 +610,9 @@ namespace smt {
                 d1->m_constructor = d2->m_constructor;
             }
         }
-        ptr_vector<enode>::iterator it   = d2->m_recognizers.begin();
-        ptr_vector<enode>::iterator end  = d2->m_recognizers.end();
-        for (; it != end; ++it) 
-            if (*it)
-                add_recognizer(v1, *it);
+        for (enode* e : d2->m_recognizers) 
+            if (e)
+                add_recognizer(v1, e);
     }
 
     void theory_datatype::unmerge_eh(theory_var v1, theory_var v2) {
diff --git a/src/util/symbol_table.h b/src/util/symbol_table.h
index ea848d991..818cb7584 100644
--- a/src/util/symbol_table.h
+++ b/src/util/symbol_table.h
@@ -182,29 +182,20 @@ public:
     }
 
     void append(symbol_table<T> const& other) { 
-        typename sym_table::iterator it = other.m_sym_table.begin();
-        typename sym_table::iterator end = other.m_sym_table.end();
-
-        for (; it != end; ++it) {
-            insert((*it).m_key, (*it).m_data);                
+        for (auto const& kv : other.m_sym_table) {
+            insert(kv.m_key, kv.m_data);                
         }
     }
 
     void get_range(vector<T,false>& range) const {
-        typename sym_table::iterator it = m_sym_table.begin();
-        typename sym_table::iterator end = m_sym_table.end();
-
-        for (; it != end; ++it) {
-            range.push_back((*it).m_data);
+        for (auto kv : m_sym_table) {
+            range.push_back(kv.m_data);
         }
     }
 
     void get_dom(svector<symbol>& dom) const {
-        typename sym_table::iterator it = m_sym_table.begin();
-        typename sym_table::iterator end = m_sym_table.end();
-
-        for (; it != end; ++it) {
-            dom.push_back((*it).m_key);
+        for (auto kv : m_sym_table) {
+            dom.push_back(kv.m_key);
         }
     }
 };

From 7fbb93847414c166db8b4c694d98c4727382adcc Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 12:00:02 -0700
Subject: [PATCH 45/74] working on parametric datatype redo

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/datatype_decl_plugin2.cpp | 184 ++++++++++++++++++++----------
 src/ast/datatype_decl_plugin2.h   | 101 +++++++++++++++-
 src/cmd_context/pdecl.cpp         | 119 +++++++------------
 src/cmd_context/pdecl.h           |  14 ---
 4 files changed, 266 insertions(+), 152 deletions(-)

diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 4e60abcce..0030af609 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -15,14 +15,13 @@ Author:
 
 Revision History:
 
-    // compute sort sizes and insert them.
-    // have a notion of pre-sort or just attach sort size after declaration.
-
 --*/
-#include "ast/datatype_decl_plugin2.h"
 #include "util/warning.h"
+#include "ast/datatype_decl_plugin2.h"
+#include "ast/array_decl_plugin.h"
 #include "ast/ast_smt2_pp.h"
 
+
 namespace datatype {
 
     func_decl_ref accessor::instantiate(sort_ref_vector const& ps) const {
@@ -78,6 +77,32 @@ namespace datatype {
         BLACK
     };
 
+    namespace param_size {
+        size* size::mk_offset(sort_size const& s) { return alloc(offset, s); }
+        size* size::mk_param(sort_ref& p) { return alloc(sparam, p); }
+        size* size::mk_plus(size* a1, size* a2) { return alloc(plus, a1, a2); }
+        size* size::mk_times(size* a1, size* a2) { return alloc(times, a1, a2); }
+        size* size::mk_times(ptr_vector<size>& szs) {
+            if (szs.empty()) return mk_offset(sort_size(1));
+            if (szs.size() == 1) return szs[0];
+            size* r = szs[0];
+            for (unsigned i = 1; i < szs.size(); ++i) {
+                r = mk_times(r, szs[i]);
+            }
+            return r;
+        }
+        size* size::mk_plus(ptr_vector<size>& szs) {
+            if (szs.empty()) return mk_offset(sort_size(0));
+            if (szs.size() == 1) return szs[0];
+            size* r = szs[0];
+            for (unsigned i = 1; i < szs.size(); ++i) {
+                r = mk_plus(r, szs[i]);
+            }
+            return r;
+        }
+        size* size::mk_power(size* a1, size* a2) { return alloc(power, a1, a2); }
+    }
+
     namespace decl {
 
         plugin::~plugin() {
@@ -125,9 +150,16 @@ namespace datatype {
                                 
                 sort* s = m_manager->mk_sort(name.get_symbol(),
                                              sort_info(m_family_id, k, num_parameters, parameters, true));
-                // compute datatype size
-                sort_size ts = u().get_datatype_size(s); 
-                s->set_num_elements(ts);
+                def* d = 0;
+                if (m_defs.find(s->get_name(), d) && d->sort_size()) {
+                    obj_map<sort, sort_size> S;
+                    for (unsigned i = 1; i < num_parameters; ++i) {
+                        sort* r = to_sort(parameters[i].get_ast());
+                        S.insert(d->params()[i], r->get_num_elements()); 
+                    }
+                    sort_size ts = d->sort_size()->fold(S);
+                    s->set_num_elements(ts);
+                }
                 return s;
             }
             catch (invalid_datatype) {
@@ -256,6 +288,7 @@ namespace datatype {
             if (!u().is_well_founded(sorts.size(), sorts.c_ptr())) {
                 m_manager->raise_exception("datatype is not well-founded");
             }
+            u().compute_datatype_size_functions(m_def_block);
         }
 
         void plugin::del(symbol const& s) {
@@ -418,84 +451,111 @@ namespace datatype {
         }
         return false;
     }
+
+    unsigned util::get_datatype_num_parameter_sorts(sort * ty) {
+        SASSERT(ty->get_num_parameters() >= 1);
+        return ty->get_num_parameters() - 1;
+    }
+
+    sort* util::get_datatype_parameter_sort(sort * ty, unsigned idx) {
+        SASSERT(idx < get_datatype_num_parameter_sorts(ty));
+        return to_sort(ty->get_parameter(idx+1).get_ast());
+    }
+
+    param_size::size* util::get_sort_size(sort_ref_vector const& params, sort* s) {
+        if (params.empty()) {
+            return param_size::size::mk_offset(s->get_num_elements());
+        }
+        if (is_datatype(s)) {
+            param_size::size* sz;
+            obj_map<sort, param_size::size*> S;
+            unsigned n = get_datatype_num_parameter_sorts(s);
+            for (unsigned i = 0; i < n; ++i) {
+                sort* ps = get_datatype_parameter_sort(s, i);
+                sz = get_sort_size(params, ps);
+                sz->inc_ref();
+                S.insert(ps, sz); 
+            }
+            def & d = get_def(s->get_name());
+            sz = d.sort_size()->subst(S);
+            for (auto & kv : S) {
+                kv.m_value->dec_ref();
+            }
+            return sz;
+        }
+        array_util autil(m);
+        if (autil.is_array(s)) {
+            unsigned n = get_array_arity(s);
+            ptr_vector<param_size::size> szs;
+            for (unsigned i = 0; i < n; ++i) {
+                szs.push_back(get_sort_size(params, get_array_domain(s, i)));
+            }
+            param_size::size* sz1 = param_size::size::mk_times(szs);
+            param_size::size* sz2 = get_sort_size(params, get_array_range(s));
+            return param_size::size::mk_power(sz2, sz1);
+        }
+        for (sort* p : params) {           
+            if (s == p) return param_size::size::mk_param(sort_ref(s, m));
+        }
+        return param_size::size::mk_offset(s->get_num_elements());        
+    }
     
-    /**
-       \brief Return the size of the inductive datatype.
-       Pre-condition: The given argument constains the parameters of an inductive datatype.
-    */
-    sort_size util::get_datatype_size(sort* s0) {
-        obj_map<sort, status> already_found;
-        obj_map<sort, sort_size> szs;
-        ptr_vector<sort> todo;
-        todo.push_back(s0);
+    void util::compute_datatype_size_functions(svector<symbol> const& names) {
+        map<symbol, status, symbol_hash_proc, symbol_eq_proc> already_found;
+        map<symbol, param_size::size*, symbol_hash_proc, symbol_eq_proc> szs;
+
+        svector<symbol> todo(names);
         status st;
         while (!todo.empty()) {
-            sort* s  = todo.back();
+            symbol s = todo.back();
             if (already_found.find(s, st) && st == BLACK) {
                 todo.pop_back();
                 continue;
             }
             already_found.insert(s, GRAY);
-            bool     is_very_big       = false;
-            bool     can_process       = true;
-            def const& d = get_def(s);
+            bool is_infinite = false;
+            bool can_process = true;
+            def& d = get_def(s);
             for (constructor const& c : d) {
                 for (accessor const& a : c) {
-                    func_decl_ref ac = a.instantiate(s);
-                    sort* r = ac->get_range();
+                    sort* r = a.range();
                     if (is_datatype(r)) {
-                        if (already_found.find(r, st)) {
+                        symbol s2 = r->get_name();
+                        if (already_found.find(s2, st)) {
                             // type is infinite
-                            if (st == GRAY) return sort_size();
+                            if (st == GRAY) {
+                                is_infinite = true;
+                            }
                         }
-                        else {
-                            todo.push_back(r);
+                        else if (names.contains(s2)) {
+                            todo.push_back(s2);
                             can_process = false;
                         }
                     }
-                    else if (r->is_infinite()) {
-                        // type is infinite
-                        return sort_size();
-                    }
-                    else if (r->is_very_big()) {
-                        is_very_big = true;
-                    }
                 }
             }
+            if (!can_process) {
+                continue;
+            }
+            todo.pop_back();
+            already_found.insert(s, BLACK);
+            if (is_infinite) {
+                d.set_sort_size(param_size::size::mk_offset(sort_size::mk_infinite()));
+                continue;
+            }
 
-            if (can_process) {
-                todo.pop_back();
-                already_found.insert(s, BLACK);
-                if (is_very_big) {
-                    szs.insert(s, sort_size::mk_very_big());
-                }
-                else {
-                    // the type is not infinite nor the number of elements is infinite...
-                    // computing the number of elements
-                    rational num;
-                    def const& d = get_def(s);
-                    for (constructor const& c : d) {
-                        rational c_num(1); 
-                        for (accessor const& a : c) {
-                            func_decl_ref ac = a.instantiate(s);
-                            sort* r = ac->get_range();
-                            if (szs.contains(r)) {
-                                c_num *= rational(szs[r].size(), rational::ui64());
-                            }
-                            else {
-                                SASSERT(!r->is_infinite() && !r->is_very_big());
-                                c_num *= rational(r->get_num_elements().size(), rational::ui64());
-                            }
-                        }
-                        num += c_num;
-                    }
-                    szs.insert(s, sort_size(num));
+            ptr_vector<param_size::size> s_add;        
+            for (constructor const& c : d) {
+                ptr_vector<param_size::size> s_mul;
+                for (accessor const& a : c) {
+                    s_mul.push_back(get_sort_size(d.params(), a.range()));
                 }
+                s_add.push_back(param_size::size::mk_times(s_mul));
             }
+            d.set_sort_size(param_size::size::mk_plus(s_add));
         }
-        return szs[s0];
     }
-
+    
 
     /**
        \brief Return true if the inductive datatype is well-founded.
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index fad7ff174..a4a76f346 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -85,11 +85,100 @@ namespace datatype {
         util& u() const;
     };
 
+    namespace param_size {
+        class size {
+            unsigned m_ref;
+        public:
+            size(): m_ref(0) {}
+            virtual ~size() {}
+            void inc_ref() { ++m_ref; }
+            void dec_ref() { --m_ref; if (m_ref == 0) dealloc(this); }
+            static size* mk_offset(sort_size const& s); 
+            static size* mk_param(sort_ref& p); 
+            static size* mk_plus(size* a1, size* a2); 
+            static size* mk_times(size* a1, size* a2); 
+            static size* mk_plus(ptr_vector<size>& szs);
+            static size* mk_times(ptr_vector<size>& szs);
+            static size* mk_power(size* a1, size* a2);
+            
+            virtual size* subst(obj_map<sort, size*>& S) = 0;
+            virtual sort_size fold(obj_map<sort, sort_size> const& S) = 0;
+            
+        };
+        struct offset : public size {
+            sort_size m_offset;
+            offset(sort_size const& s): m_offset(s) {}
+            virtual ~offset() {}
+            virtual size* subst(obj_map<sort,size*>& S) { return this; }
+            virtual sort_size fold(obj_map<sort, sort_size> const& S) { return m_offset; }
+        };
+        struct plus : public size {
+            size* m_arg1, *m_arg2;
+            plus(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref();}
+            virtual ~plus() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
+            virtual size* subst(obj_map<sort,size*>& S) { return mk_plus(m_arg1->subst(S), m_arg2->subst(S)); }
+            virtual sort_size fold(obj_map<sort, sort_size> const& S) { 
+                sort_size s1 = m_arg1->fold(S);
+                sort_size s2 = m_arg2->fold(S);
+                if (s1.is_infinite()) return s1;
+                if (s2.is_infinite()) return s2;
+                if (s1.is_very_big()) return s1;
+                if (s2.is_very_big()) return s2;
+                rational r = rational(s1.size(), rational::ui64()) + rational(s2.size(), rational::ui64());
+                return sort_size(r);
+            }
+        };
+        struct times : public size {
+            size* m_arg1, *m_arg2;
+            times(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); }
+            virtual ~times() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
+            virtual size* subst(obj_map<sort,size*>& S) { return mk_times(m_arg1->subst(S), m_arg2->subst(S)); }
+            virtual sort_size fold(obj_map<sort, sort_size> const& S) { 
+                sort_size s1 = m_arg1->fold(S);
+                sort_size s2 = m_arg2->fold(S);
+                if (s1.is_infinite()) return s1;
+                if (s2.is_infinite()) return s2;
+                if (s1.is_very_big()) return s1;
+                if (s2.is_very_big()) return s2;
+                rational r = rational(s1.size(), rational::ui64()) * rational(s2.size(), rational::ui64());
+                return sort_size(r);
+            }
+        };
+        struct power : public size {
+            size* m_arg1, *m_arg2;
+            power(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); }
+            virtual ~power() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
+            virtual size* subst(obj_map<sort,size*>& S) { return mk_power(m_arg1->subst(S), m_arg2->subst(S)); }
+            virtual sort_size fold(obj_map<sort, sort_size> const& S) { 
+                sort_size s1 = m_arg1->fold(S);
+                sort_size s2 = m_arg2->fold(S);
+                // s1^s2
+                if (s1.is_infinite()) return s1;
+                if (s2.is_infinite()) return s2;
+                if (s1.is_very_big()) return s1;
+                if (s2.is_very_big()) return s2;
+                if (s1.size() == 1) return s1;
+                if (s2.size() == 1) return s1;
+                if (s1.size() > (2 << 20) || s2.size() > 10) return sort_size::mk_very_big();
+                rational r = ::power(rational(s1.size(), rational::ui64()), static_cast<unsigned>(s2.size()));
+                return sort_size(r);
+            }
+        };
+        struct sparam : public size {
+            sort_ref m_param;
+            sparam(sort_ref& p): m_param(p) {}
+            virtual ~sparam() {}
+            virtual size* subst(obj_map<sort,size*>& S) { return S[m_param]; }
+            virtual sort_size fold(obj_map<sort, sort_size> const& S) { return S[m_param]; }
+        };
+    };
+
     class def {
         ast_manager&        m;
         util&               m_util;
         symbol              m_name;
         unsigned            m_class_id;
+        param_size::size*   m_sort_size;
         sort_ref_vector     m_params;
         mutable sort_ref    m_sort;
         vector<constructor> m_constructors;
@@ -99,9 +188,13 @@ namespace datatype {
             m_util(u),
             m_name(n),
             m_class_id(class_id),            
+            m_sort_size(0),
             m_params(m, num_params, params), 
             m_sort(m)
         {}
+        ~def() {
+            if (m_sort_size) m_sort_size->dec_ref();
+        }
         void add(constructor& c) {
             m_constructors.push_back(c);
             c.attach(this);
@@ -114,6 +207,8 @@ namespace datatype {
         vector<constructor>::const_iterator end() const { return m_constructors.end(); }
         sort_ref_vector const& params() const { return m_params; }
         util& u() const { return m_util; }
+        param_size::size* sort_size() { return m_sort_size; }
+        void set_sort_size(param_size::size* p) { m_sort_size = p; p->inc_ref(); }
     };
 
     namespace decl {
@@ -155,7 +250,8 @@ namespace datatype {
 
             void del(symbol const& d);
 
-            def const& get_def(sort* s) const { def* d = 0; VERIFY(m_defs.find(datatype_name(s), d)); return *d; }
+            def const& get_def(sort* s) const { return *(m_defs[datatype_name(s)]); }
+            def& get_def(symbol const& s) { return *(m_defs[s]); }
 
         private:
             bool is_value_visit(expr * arg, ptr_buffer<app> & todo) const;
@@ -213,8 +309,11 @@ namespace datatype {
 
         bool is_recursive_core(sort * s) const;
         sort_size get_datatype_size(sort* s0);
+        void compute_datatype_size_functions(svector<symbol> const& names);
+        param_size::size* get_sort_size(sort_ref_vector const& params, sort* s);
         bool is_well_founded(unsigned num_types, sort* const* sorts);
         def const& get_def(sort* s) const;
+        def& get_def(symbol const& s) { return m_plugin->get_def(s); }
         void get_subsorts(sort* s, ptr_vector<sort>& sorts) const;        
 
     public:
diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp
index 983dbbc78..88ab9f8b8 100644
--- a/src/cmd_context/pdecl.cpp
+++ b/src/cmd_context/pdecl.cpp
@@ -39,15 +39,13 @@ public:
         }
         else {
             SASSERT(m_const == 0);
-            obj_map<sort, void *>::iterator it  = m_map.begin();
-            obj_map<sort, void *>::iterator end = m_map.end();
-            for (; it != end; ++it) {
-                m.m().dec_ref((*it).m_key);
+            for (auto kv : m_map) {
+                m.m().dec_ref(kv.m_key);
                 if (m_num_params == 1) {
-                    m.m().dec_ref(static_cast<sort*>((*it).m_value));
+                    m.m().dec_ref(static_cast<sort*>(kv.m_value));
                 }
                 else {
-                    psort_inst_cache * child = static_cast<psort_inst_cache*>((*it).m_value);
+                    psort_inst_cache * child = static_cast<psort_inst_cache*>(kv.m_value);
                     child->finalize(m);
                     child->~psort_inst_cache();
                     m.a().deallocate(sizeof(psort_inst_cache), child);
@@ -485,20 +483,16 @@ void pconstructor_decl::finalize(pdecl_manager & m) {
 }
 
 bool pconstructor_decl::has_missing_refs(symbol & missing) const {
-    ptr_vector<paccessor_decl>::const_iterator it  = m_accessors.begin();
-    ptr_vector<paccessor_decl>::const_iterator end = m_accessors.end();
-    for (; it != end; ++it) {
-        if ((*it)->has_missing_refs(missing))
+    for (paccessor_decl* a : m_accessors) {
+        if (a->has_missing_refs(missing))
             return true;
     }
     return false;
 }
 
 bool pconstructor_decl::fix_missing_refs(dictionary<int> const & symbol2idx, symbol & missing) {
-    ptr_vector<paccessor_decl>::iterator it  = m_accessors.begin();
-    ptr_vector<paccessor_decl>::iterator end = m_accessors.end();
-    for (; it != end; ++it) {
-        if (!(*it)->fix_missing_refs(symbol2idx, missing))
+    for (paccessor_decl* a : m_accessors) {
+        if (!a->fix_missing_refs(symbol2idx, missing))
             return false;
     }
     return true;
@@ -506,20 +500,16 @@ bool pconstructor_decl::fix_missing_refs(dictionary<int> const & symbol2idx, sym
 
 constructor_decl * pconstructor_decl::instantiate_decl(pdecl_manager & m, sort * const * s) {
     ptr_buffer<accessor_decl> as;
-    ptr_vector<paccessor_decl>::iterator it  = m_accessors.begin();
-    ptr_vector<paccessor_decl>::iterator end = m_accessors.end();
-    for (; it != end; ++it) 
-        as.push_back((*it)->instantiate_decl(m, s));
+    for (paccessor_decl* a : m_accessors) 
+        as.push_back(a->instantiate_decl(m, s));
     return mk_constructor_decl(m_name, m_recogniser_name, as.size(), as.c_ptr());
 }
 
 void pconstructor_decl::display(std::ostream & out, pdatatype_decl const * const * dts) const {
     out << "(" << m_name;
-    ptr_vector<paccessor_decl>::const_iterator it  = m_accessors.begin();
-    ptr_vector<paccessor_decl>::const_iterator end = m_accessors.end();
-    for (; it != end; ++it) {
+    for (paccessor_decl* a : m_accessors) {
         out << " ";
-        (*it)->display(out, dts);
+        a->display(out, dts);
     }
     out << ")";
 }
@@ -538,23 +528,17 @@ void pdatatype_decl::finalize(pdecl_manager & m) {
 }
 
 bool pdatatype_decl::has_missing_refs(symbol & missing) const {
-    ptr_vector<pconstructor_decl>::const_iterator it  = m_constructors.begin();
-    ptr_vector<pconstructor_decl>::const_iterator end = m_constructors.end();
-    for (; it != end; ++it) {
-        if ((*it)->has_missing_refs(missing))
+    for (auto c : m_constructors) 
+        if (c->has_missing_refs(missing))
             return true;
-    }
     return false;
 }
 
 bool pdatatype_decl::has_duplicate_accessors(symbol & duplicated) const {
     hashtable<symbol, symbol_hash_proc, symbol_eq_proc> names;
-    ptr_vector<pconstructor_decl>::const_iterator it  = m_constructors.begin();
-    ptr_vector<pconstructor_decl>::const_iterator end = m_constructors.end();
-    for (; it != end; ++it) {
-        ptr_vector<paccessor_decl> const& acc = (*it)->m_accessors;
-        for (unsigned i = 0; i < acc.size(); ++i) {
-            symbol const& name = acc[i]->get_name();
+    for (auto c : m_constructors) {
+        for (auto a : c->m_accessors) {
+            symbol const& name = a->get_name();
             if (names.contains(name)) {
                 duplicated = name;
                 return true;
@@ -567,10 +551,8 @@ bool pdatatype_decl::has_duplicate_accessors(symbol & duplicated) const {
 
 
 bool pdatatype_decl::fix_missing_refs(dictionary<int> const & symbol2idx, symbol & missing) {
-    ptr_vector<pconstructor_decl>::iterator it  = m_constructors.begin();
-    ptr_vector<pconstructor_decl>::iterator end = m_constructors.end();
-    for (; it != end; ++it) {
-        if (!(*it)->fix_missing_refs(symbol2idx, missing))
+    for (auto c : m_constructors) {
+        if (!c->fix_missing_refs(symbol2idx, missing))
             return false;
     }
     return true;
@@ -578,10 +560,8 @@ bool pdatatype_decl::fix_missing_refs(dictionary<int> const & symbol2idx, symbol
 
 datatype_decl * pdatatype_decl::instantiate_decl(pdecl_manager & m, sort * const * s) {
     ptr_buffer<constructor_decl> cs;
-    ptr_vector<pconstructor_decl>::iterator it  = m_constructors.begin();
-    ptr_vector<pconstructor_decl>::iterator end = m_constructors.end();
-    for (; it != end; ++it) {
-        cs.push_back((*it)->instantiate_decl(m, s));
+    for (auto c : m_constructors) {
+        cs.push_back(c->instantiate_decl(m, s));
     }
     return mk_datatype_decl(m_name, cs.size(), cs.c_ptr());
 }
@@ -624,17 +604,16 @@ sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const *
 void pdatatype_decl::display(std::ostream & out) const {
     out << "(declare-datatype " << m_name;
     display_sort_args(out, m_num_params);
-    ptr_vector<pconstructor_decl>::const_iterator it  = m_constructors.begin();
-    ptr_vector<pconstructor_decl>::const_iterator end = m_constructors.end();
-    for (bool first = true; it != end; ++it) {
+    bool first = true;
+    for (auto c : m_constructors) {
         if (!first)
             out << " ";
         if (m_parent) {
-            (*it)->display(out, m_parent->children());
+            c->display(out, m_parent->children());
         }
         else {
             pdatatype_decl const * dts[1] = { this };
-            (*it)->display(out, dts);
+            c->display(out, dts);
         }
         first = false;
     }
@@ -647,11 +626,9 @@ pdatatypes_decl::pdatatypes_decl(unsigned id, unsigned num_params, pdecl_manager
     m_datatypes(num_datatypes, dts) {
     m.inc_ref(num_datatypes, dts);
 
-    ptr_vector<pdatatype_decl>::iterator it  = m_datatypes.begin();
-    ptr_vector<pdatatype_decl>::iterator end = m_datatypes.end();
-    for (; it != end; ++it) {
-        SASSERT((*it)->m_parent == 0);
-        (*it)->m_parent = this;
+    for (auto d : m_datatypes) {
+        SASSERT(d->m_parent == 0);
+        d->m_parent = this;
     }
 }
 
@@ -662,36 +639,30 @@ void pdatatypes_decl::finalize(pdecl_manager & m) {
 bool pdatatypes_decl::fix_missing_refs(symbol & missing) {
     TRACE("fix_missing_refs", tout << "pdatatypes_decl::fix_missing_refs\n";);
     dictionary<int> symbol2idx;
-    ptr_vector<pdatatype_decl>::iterator it  = m_datatypes.begin();
-    ptr_vector<pdatatype_decl>::iterator end = m_datatypes.end();
-    for (unsigned idx = 0; it != end; ++it, ++idx)
-        symbol2idx.insert((*it)->get_name(), idx);
-
-    it  = m_datatypes.begin();
-    for (unsigned idx = 0; it != end; ++it, ++idx) {
-        if (!(*it)->fix_missing_refs(symbol2idx, missing))
+    int idx = 0;
+    for (pdatatype_decl* d : m_datatypes) 
+        symbol2idx.insert(d->get_name(), idx++);
+    for (pdatatype_decl* d : m_datatypes) 
+        if (!d->fix_missing_refs(symbol2idx, missing))
             return false;
-    }
     return true;
 }
 
 bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) {
     datatype_decl_buffer dts;
-    ptr_vector<pdatatype_decl>::iterator it  = m_datatypes.begin();
-    ptr_vector<pdatatype_decl>::iterator end = m_datatypes.end();
-    for (; it != end; ++it) {
-        dts.m_buffer.push_back((*it)->instantiate_decl(m, s));
+    for (auto d : m_datatypes) {
+        dts.m_buffer.push_back(d->instantiate_decl(m, s));
     }
     sort_ref_vector sorts(m.m());
     bool is_ok = m.get_dt_plugin()->mk_datatypes(dts.m_buffer.size(), dts.m_buffer.c_ptr(), m_num_params, s, sorts);
     if (!is_ok)
         return false;
-    it  = m_datatypes.begin();
-    for (unsigned i = 0; it != end; ++it, ++i) {
-        sort * new_dt = sorts.get(i);
-        (*it)->cache(m, s, new_dt);
-        m.save_info(new_dt, *it, m_num_params, s);
-        m.notify_new_dt(new_dt, *it);
+    unsigned i = 0;
+    for (auto d : m_datatypes) {
+        sort * new_dt = sorts.get(i++);
+        d->cache(m, s, new_dt);
+        m.save_info(new_dt, d, m_num_params, s);
+        m.notify_new_dt(new_dt, d);
     }
     return true;
 }
@@ -965,11 +936,9 @@ void pdecl_manager::save_info(sort * s, psort_decl * d, unsigned num_indices, un
 }
 
 void pdecl_manager::reset_sort_info() {
-    obj_map<sort, sort_info *>::iterator it  = m_sort2info.begin();
-    obj_map<sort, sort_info *>::iterator end = m_sort2info.end();
-    for (; it != end; ++it) {
-        sort * s         = (*it).m_key;
-        sort_info * info = (*it).m_value;
+    for (auto kv : m_sort2info) {
+        sort * s         = kv.m_key;
+        sort_info * info = kv.m_value;
         m().dec_ref(s);
         unsigned sz = info->obj_size();
         info->finalize(*this);
diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h
index 2dfbb93ae..1d7203197 100644
--- a/src/cmd_context/pdecl.h
+++ b/src/cmd_context/pdecl.h
@@ -134,20 +134,6 @@ public:
     virtual void display(std::ostream & out) const;
 };
 
-#if 0
-class psort_dt_decl : public psort_decl {
-protected:
-    friend class pdecl_manager;
-    psort_dt_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n);
-    virtual size_t obj_size() const { return sizeof(psort_dt_decl); }
-    virtual void finalize(pdecl_manager & m);
-    virtual ~psort_dt_decl() {}
-public:
-    virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s);
-    virtual void display(std::ostream & out) const;
-};
-#endif
-
 class datatype_decl_plugin;
 class datatype_decl;
 class constructor_decl;

From c6722859c271723760b20188bf75a6201ad33af7 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 14:36:03 -0700
Subject: [PATCH 46/74] update rewriting of equalities and monomials for
 regressions

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/justified_expr.h                   |  4 +-
 src/ast/rewriter/arith_rewriter.cpp        | 20 +++---
 src/ast/rewriter/arith_rewriter.h          |  4 +-
 src/ast/rewriter/arith_rewriter_params.pyg |  1 -
 src/ast/rewriter/bv_rewriter.cpp           |  1 +
 src/ast/rewriter/poly_rewriter.h           |  9 ++-
 src/ast/rewriter/poly_rewriter_def.h       | 71 ++++++++++++-------
 src/cmd_context/cmd_context.cpp            | 79 +++++++---------------
 src/smt/asserted_formulas.cpp              |  8 ++-
 9 files changed, 102 insertions(+), 95 deletions(-)

diff --git a/src/ast/justified_expr.h b/src/ast/justified_expr.h
index 49362940d..8aa961686 100644
--- a/src/ast/justified_expr.h
+++ b/src/ast/justified_expr.h
@@ -21,12 +21,12 @@ public:
     justified_expr& operator=(justified_expr const& other) {
         SASSERT(&m == &other.m);
         if (this != &other) {
+            m.inc_ref(other.get_fml());
+            m.inc_ref(other.get_proof());
             m.dec_ref(m_fml);
             m.dec_ref(m_proof);
             m_fml = other.get_fml();
             m_proof = other.get_proof();
-            m.inc_ref(m_fml);
-            m.inc_ref(m_proof);
         }
         return *this;
     }
diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp
index c1ef61938..b630cdff6 100644
--- a/src/ast/rewriter/arith_rewriter.cpp
+++ b/src/ast/rewriter/arith_rewriter.cpp
@@ -26,7 +26,6 @@ void arith_rewriter::updt_local_params(params_ref const & _p) {
     arith_rewriter_params p(_p);
     m_arith_lhs       = p.arith_lhs();
     m_gcd_rounding    = p.gcd_rounding();
-    m_eq2ineq         = p.eq2ineq();
     m_elim_to_real    = p.elim_to_real();
     m_push_to_real    = p.push_to_real();
     m_anum_simp       = p.algebraic_number_evaluator();
@@ -371,7 +370,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
     if ((is_zero(arg1) && is_reduce_power_target(arg2, kind == EQ)) ||
         (is_zero(arg2) && is_reduce_power_target(arg1, kind == EQ)))
         return reduce_power(arg1, arg2, kind, result);
-    br_status st = cancel_monomials(arg1, arg2, m_arith_lhs, new_arg1, new_arg2);
+    br_status st = cancel_monomials(arg1, arg2, true, new_arg1, new_arg2);
     TRACE("mk_le_bug", tout << "st: " << st << " " << new_arg1 << " " << new_arg2 << "\n";);
     if (st != BR_FAILED) {
         arg1 = new_arg1;
@@ -455,11 +454,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
             st = BR_DONE;
         }
     }
-    if (kind == EQ && m_expand_eqs) {
-        result = m().mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2));
-        return BR_REWRITE2;
-    }
-    else if (is_numeral(arg2, a2) && is_neg_poly(arg1, new_arg1)) {
+    if (is_numeral(arg2, a2) && is_neg_poly(arg1, new_arg1)) {
         a2.neg();
         new_arg2 = m_util.mk_numeral(a2, m_util.is_int(new_arg1));
         switch (kind) {
@@ -500,12 +495,19 @@ br_status arith_rewriter::mk_gt_core(expr * arg1, expr * arg2, expr_ref & result
     return BR_REWRITE2;
 }
 
+bool arith_rewriter::is_arith_term(expr * n) const {
+    return n->get_kind() == AST_APP && to_app(n)->get_family_id() == get_fid();
+}
+
 br_status arith_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
-    if (m_eq2ineq) {
+    if (m_expand_eqs) {
         result = m().mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2));
         return BR_REWRITE2;
     }
-    return mk_le_ge_eq_core(arg1, arg2, EQ, result);
+    if (m_arith_lhs || is_arith_term(arg1) || is_arith_term(arg2)) {
+        return mk_le_ge_eq_core(arg1, arg2, EQ, result);
+    }
+    return BR_FAILED;
 }
 
 expr_ref arith_rewriter::neg_monomial(expr* e) const {
diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h
index 352cb5a6c..f1e4c4396 100644
--- a/src/ast/rewriter/arith_rewriter.h
+++ b/src/ast/rewriter/arith_rewriter.h
@@ -50,12 +50,12 @@ public:
 class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
     bool m_arith_lhs;
     bool m_gcd_rounding;
-    bool m_eq2ineq;
     bool m_elim_to_real;
     bool m_push_to_real;
     bool m_anum_simp;
     bool m_elim_rem;
     bool m_expand_eqs;
+    bool m_process_all_eqs;
     unsigned m_max_degree;
 
     void get_coeffs_gcd(expr * t, numeral & g, bool & first, unsigned & num_consts);
@@ -83,6 +83,8 @@ class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
     expr * reduce_power(expr * arg, bool is_eq);
     br_status reduce_power(expr * arg1, expr * arg2, op_kind kind, expr_ref & result);
 
+    bool is_arith_term(expr * n) const;
+
     bool is_pi_multiple(expr * t, rational & k);
     bool is_pi_offset(expr * t, rational & k, expr * & m);
     bool is_2_pi_integer(expr * t);
diff --git a/src/ast/rewriter/arith_rewriter_params.pyg b/src/ast/rewriter/arith_rewriter_params.pyg
index 94ada1b6d..9a6c6c1c0 100644
--- a/src/ast/rewriter/arith_rewriter_params.pyg
+++ b/src/ast/rewriter/arith_rewriter_params.pyg
@@ -6,7 +6,6 @@ def_module_params(module_name='rewriter',
                           ("expand_power", BOOL, False, "expand (^ t k) into (* t ... t) if  1 < k <= max_degree."),
                           ("expand_tan", BOOL, False, "replace (tan x) with (/ (sin x) (cos x))."),
                           ("max_degree", UINT, 64, "max degree of algebraic numbers (and power operators) processed by simplifier."),
-                          ("eq2ineq", BOOL, False, "split arithmetic equalities into two inequalities."),
                           ("sort_sums", BOOL, False, "sort the arguments of + application."),
                           ("gcd_rounding", BOOL, False, "use gcd rounding on integer arithmetic atoms."),
                           ("arith_lhs", BOOL, False, "all monomials are moved to the left-hand-side, and the right-hand-side is just a constant."),
diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp
index 75f931c1d..ce35300ca 100644
--- a/src/ast/rewriter/bv_rewriter.cpp
+++ b/src/ast/rewriter/bv_rewriter.cpp
@@ -20,6 +20,7 @@ Notes:
 #include "ast/rewriter/bv_rewriter_params.hpp"
 #include "ast/rewriter/poly_rewriter_def.h"
 #include "ast/ast_smt2_pp.h"
+#include "ast/ast_lt.h"
 
 
 void bv_rewriter::updt_local_params(params_ref const & _p) {
diff --git a/src/ast/rewriter/poly_rewriter.h b/src/ast/rewriter/poly_rewriter.h
index 8cbc7f864..c00464383 100644
--- a/src/ast/rewriter/poly_rewriter.h
+++ b/src/ast/rewriter/poly_rewriter.h
@@ -48,7 +48,6 @@ protected:
     decl_kind power_decl_kind() const { return Config::power_decl_kind(); }
     bool is_power(expr * t) const { return is_app_of(t, get_fid(), power_decl_kind()); }
     expr * get_power_body(expr * t, rational & k);
-    struct mon_pw_lt; // functor used to sort monomial elements when use_power() == true
 
     expr * mk_mul_app(unsigned num_args, expr * const * args);
     expr * mk_mul_app(numeral const & c, expr * arg);
@@ -85,6 +84,14 @@ protected:
     bool is_mul(expr * t, numeral & c, expr * & pp);
     void hoist_cmul(expr_ref_buffer & args);
 
+    class mon_lt {
+        poly_rewriter& rw;
+        int ordinal(expr* e) const;
+    public:
+        mon_lt(poly_rewriter& rw): rw(rw) {}
+        bool operator()(expr* e1, expr * e2) const;
+    };
+
 public:
     poly_rewriter(ast_manager & m, params_ref const & p = params_ref()):
         Config(m),
diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h
index 85044af08..8fda040b6 100644
--- a/src/ast/rewriter/poly_rewriter_def.h
+++ b/src/ast/rewriter/poly_rewriter_def.h
@@ -18,7 +18,7 @@ Notes:
 --*/
 #include "ast/rewriter/poly_rewriter.h"
 #include "ast/rewriter/poly_rewriter_params.hpp"
-#include "ast/ast_lt.h"
+// include "ast/ast_lt.h"
 #include "ast/ast_ll_pp.h"
 #include "ast/ast_smt2_pp.h"
 
@@ -191,21 +191,9 @@ br_status poly_rewriter<Config>::mk_flat_mul_core(unsigned num_args, expr * cons
 }
 
 
-template<typename Config>
-struct poly_rewriter<Config>::mon_pw_lt {
-    poly_rewriter<Config> & m_owner;
-    mon_pw_lt(poly_rewriter<Config> & o):m_owner(o) {}
-    
-    bool operator()(expr * n1, expr * n2) const { 
-        rational k;
-        return lt(m_owner.get_power_body(n1, k), 
-                  m_owner.get_power_body(n2, k));
-    }
-};
-
-
 template<typename Config>
 br_status poly_rewriter<Config>::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) {
+    mon_lt lt(*this);
     SASSERT(num_args >= 2);
     // cheap case
     numeral a;
@@ -320,11 +308,8 @@ br_status poly_rewriter<Config>::mk_nflat_mul_core(unsigned num_args, expr * con
         if (ordered && num_coeffs == 0 && !use_power())
             return BR_FAILED;
         if (!ordered) {
-            if (use_power())
-                std::sort(new_args.begin(), new_args.end(), mon_pw_lt(*this));
-            else
-                std::sort(new_args.begin(), new_args.end(), ast_to_lt());
-            TRACE("poly_rewriter",
+            std::sort(new_args.begin(), new_args.end(), lt);
+        TRACE("poly_rewriter",
                   tout << "after sorting:\n";
                   for (unsigned i = 0; i < new_args.size(); i++) {
                       if (i > 0)
@@ -498,8 +483,43 @@ void poly_rewriter<Config>::hoist_cmul(expr_ref_buffer & args) {
     args.resize(j);
 }
 
+template<typename Config>
+bool poly_rewriter<Config>::mon_lt::operator()(expr* e1, expr * e2) const {
+    return ordinal(e1) < ordinal(e2);
+}
+
+inline bool is_essentially_var(expr * n, family_id fid) {
+    SASSERT(is_var(n) || is_app(n));
+    return is_var(n) || to_app(n)->get_family_id() != fid;
+}
+
+template<typename Config>
+int poly_rewriter<Config>::mon_lt::ordinal(expr* e) const {
+    rational k;
+    if (is_essentially_var(e, rw.get_fid())) {
+        return e->get_id();
+    }
+    else if (rw.is_mul(e)) {
+        if (rw.is_numeral(to_app(e)->get_arg(0)))
+            return to_app(e)->get_arg(1)->get_id();
+        else
+            return e->get_id();
+    }
+    else if (rw.is_numeral(e)) {
+        return -1;
+    }
+    else if (rw.use_power() && rw.is_power(e) && rw.is_numeral(to_app(e)->get_arg(1), k) && k > rational(1)) {
+        return to_app(e)->get_arg(0)->get_id();
+    }
+    else {
+        return e->get_id();
+    }
+}
+
+
 template<typename Config>
 br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) {
+    mon_lt lt(*this);
     SASSERT(num_args >= 2);
     numeral c;
     unsigned num_coeffs = 0;
@@ -591,9 +611,9 @@ br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * con
         else if (m_sort_sums) {
             TRACE("rewriter_bug", tout << "new_args.size(): " << new_args.size() << "\n";);
             if (c.is_zero())
-                std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt());
+                std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), mon_lt(*this));
             else
-                std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt());
+                std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), mon_lt(*this));
         }
         result = mk_add_app(new_args.size(), new_args.c_ptr());
         TRACE("rewriter", tout << result << "\n";);
@@ -624,10 +644,10 @@ br_status poly_rewriter<Config>::mk_nflat_add_core(unsigned num_args, expr * con
         }
         else if (!ordered) {
             if (c.is_zero())
-                std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), ast_to_lt());
+                std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), lt);
             else 
-                std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), ast_to_lt());
-        }
+                std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), lt);
+    }
         result = mk_add_app(new_args.size(), new_args.c_ptr());        
         if (hoist_multiplication(result)) {
             return BR_REWRITE_FULL;
@@ -681,6 +701,7 @@ br_status poly_rewriter<Config>::mk_sub(unsigned num_args, expr * const * args,
 template<typename Config>
 br_status poly_rewriter<Config>::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) {
     set_curr_sort(m().get_sort(lhs));
+    mon_lt lt(*this);
     unsigned lhs_sz;
     expr * const * lhs_monomials = get_monomials(lhs, lhs_sz);
     unsigned rhs_sz;
@@ -831,7 +852,7 @@ br_status poly_rewriter<Config>::cancel_monomials(expr * lhs, expr * rhs, bool m
     if (move) {
         if (m_sort_sums) { 
             // + 1 to skip coefficient
-            std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), ast_to_lt());
+            std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), lt);
         }
         c_at_rhs = true;
     }
diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp
index f172e5e93..d1b8f7f78 100644
--- a/src/cmd_context/cmd_context.cpp
+++ b/src/cmd_context/cmd_context.cpp
@@ -585,10 +585,8 @@ void cmd_context::register_builtin_sorts(decl_plugin * p) {
     svector<builtin_name> names;
     p->get_sort_names(names, m_logic);
     family_id fid = p->get_family_id();
-    svector<builtin_name>::const_iterator it  = names.begin();
-    svector<builtin_name>::const_iterator end = names.end();
-    for (; it != end; ++it) {
-        psort_decl * d = pm().mk_psort_builtin_decl((*it).m_name, fid, (*it).m_kind);
+    for (builtin_name const& n : names) {
+        psort_decl * d = pm().mk_psort_builtin_decl(n.m_name, fid, n.m_kind);
         insert(d);
     }
 }
@@ -597,17 +595,15 @@ void cmd_context::register_builtin_ops(decl_plugin * p) {
     svector<builtin_name> names;
     p->get_op_names(names, m_logic);
     family_id fid = p->get_family_id();
-    svector<builtin_name>::const_iterator it  = names.begin();
-    svector<builtin_name>::const_iterator end = names.end();
-    for (; it != end; ++it) {
-        if (m_builtin_decls.contains((*it).m_name)) {
-            builtin_decl & d = m_builtin_decls.find((*it).m_name);
-            builtin_decl * new_d = alloc(builtin_decl, fid, (*it).m_kind, d.m_next);
+    for (builtin_name const& n : names) {
+        if (m_builtin_decls.contains(n.m_name)) {
+            builtin_decl & d = m_builtin_decls.find(n.m_name);
+            builtin_decl * new_d = alloc(builtin_decl, fid, n.m_kind, d.m_next);
             d.m_next = new_d;
             m_extra_builtin_decls.push_back(new_d);
         }
         else {
-            m_builtin_decls.insert((*it).m_name, builtin_decl(fid, (*it).m_kind));
+            m_builtin_decls.insert(n.m_name, builtin_decl(fid, n.m_kind));
         }
     }
 }
@@ -693,10 +689,8 @@ void cmd_context::init_manager_core(bool new_manager) {
         load_plugin(symbol("seq"),      logic_has_seq(), fids);
         load_plugin(symbol("fpa"),      logic_has_fpa(), fids);
         load_plugin(symbol("pb"),       logic_has_pb(), fids);
-        svector<family_id>::iterator it  = fids.begin();
-        svector<family_id>::iterator end = fids.end();
-        for (; it != end; ++it) {
-            decl_plugin * p = m_manager->get_plugin(*it);
+        for (family_id fid : fids) {
+            decl_plugin * p = m_manager->get_plugin(fid);
             if (p) {
                 register_builtin_sorts(p);
                 register_builtin_ops(p);
@@ -1148,11 +1142,8 @@ void cmd_context::erase_object_ref(symbol const & s) {
 }
 
 void cmd_context::reset_func_decls() {
-    dictionary<func_decls>::iterator  it  = m_func_decls.begin();
-    dictionary<func_decls>::iterator  end = m_func_decls.end();
-    for (; it != end; ++it) {
-        func_decls fs = (*it).m_value;
-        fs.finalize(m());
+    for (auto & kv : m_func_decls) {
+        kv.m_value.finalize(m());
     }
     m_func_decls.reset();
     m_func_decls_stack.reset();
@@ -1160,10 +1151,8 @@ void cmd_context::reset_func_decls() {
 }
 
 void cmd_context::reset_psort_decls() {
-    dictionary<psort_decl*>::iterator  it  = m_psort_decls.begin();
-    dictionary<psort_decl*>::iterator  end = m_psort_decls.end();
-    for (; it != end; ++it) {
-        psort_decl * p = (*it).m_value;
+    for (auto & kv : m_psort_decls) {
+        psort_decl * p = kv.m_value;
         pm().dec_ref(p);
     }
     m_psort_decls.reset();
@@ -1179,19 +1168,14 @@ void cmd_context::reset_macros() {
 }
 
 void cmd_context::reset_cmds() {
-    dictionary<cmd*>::iterator  it  = m_cmds.begin();
-    dictionary<cmd*>::iterator  end = m_cmds.end();
-    for (; it != end; ++it) {
-        cmd * c = (*it).m_value;
-        c->reset(*this);
+    for (auto& kv : m_cmds) {
+        kv.m_value->reset(*this);
     }
 }
 
 void cmd_context::finalize_cmds() {
-    dictionary<cmd*>::iterator  it  = m_cmds.begin();
-    dictionary<cmd*>::iterator  end = m_cmds.end();
-    for (; it != end; ++it) {
-        cmd * c = (*it).m_value;
+    for (auto& kv : m_cmds) {
+        cmd * c = kv.m_value;
         c->finalize(*this);
         dealloc(c);
     }
@@ -1204,10 +1188,8 @@ void cmd_context::reset_user_tactics() {
 }
 
 void cmd_context::reset_object_refs() {
-    dictionary<object_ref*>::iterator it  = m_object_refs.begin();
-    dictionary<object_ref*>::iterator end = m_object_refs.end();
-    for (; it != end; ++it) {
-        object_ref * r = (*it).m_value;
+    for (auto& kv : m_object_refs) {
+        object_ref * r = kv.m_value;
         r->dec_ref(*this);
     }
     m_object_refs.reset();
@@ -1541,10 +1523,8 @@ void cmd_context::reset_assertions() {
         mk_solver();
     }
     restore_assertions(0);
-    svector<scope>::iterator it  = m_scopes.begin();
-    svector<scope>::iterator end = m_scopes.end();
-    for (; it != end; ++it) {
-        it->m_assertions_lim = 0;
+    for (scope& s : m_scopes) {
+        s.m_assertions_lim = 0;
         if (m_solver) m_solver->push();
     }
 }
@@ -1717,10 +1697,7 @@ void cmd_context::set_solver_factory(solver_factory * f) {
         mk_solver();
         // assert formulas and create scopes in the new solver.
         unsigned lim = 0;
-        svector<scope>::iterator it  = m_scopes.begin();
-        svector<scope>::iterator end = m_scopes.end();
-        for (; it != end; ++it) {
-            scope & s = *it;
+        for (scope& s : m_scopes) {
             for (unsigned i = lim; i < s.m_assertions_lim; i++) {
                 m_solver->assert_expr(m_assertions[i]);
             }
@@ -1757,11 +1734,9 @@ void cmd_context::display_statistics(bool show_total_time, double total_time) {
 void cmd_context::display_assertions() {
     if (!m_interactive_mode)
         throw cmd_exception("command is only available in interactive mode, use command (set-option :interactive-mode true)");
-    std::vector<std::string>::const_iterator it  = m_assertion_strings.begin();
-    std::vector<std::string>::const_iterator end = m_assertion_strings.end();
     regular_stream() << "(";
-    for (bool first = true; it != end; ++it) {
-        std::string const & s = *it;
+    bool first = true;
+    for (std::string const& s : m_assertion_strings) {
         if (first)
             first = false;
         else
@@ -1837,10 +1812,8 @@ void cmd_context::display(std::ostream & out, func_decl * d, unsigned indent) co
 }
 
 void cmd_context::dump_assertions(std::ostream & out) const {
-    ptr_vector<expr>::const_iterator it  = m_assertions.begin();
-    ptr_vector<expr>::const_iterator end = m_assertions.end();
-    for (; it != end; ++it) {
-        display(out, *it);
+    for (expr * e : m_assertions) {
+        display(out, e);
         out << std::endl;
     }
 }
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index fb2004c77..7dca13c07 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -120,7 +120,7 @@ void asserted_formulas::push_assertion(expr * e, proof * pr, vector<justified_ex
 void asserted_formulas::set_eliminate_and(bool flag) {
     params_ref p;
     p.set_bool("elim_and", flag);
-    p.set_bool("arith_lhs", true);
+//    p.set_bool("arith_lhs", true);
     p.set_bool("sort_sums", true);
     p.set_bool("rewrite_patterns", true);
     p.set_bool("expand_eqs", m_params.m_arith_expand_eqs);
@@ -429,7 +429,6 @@ void asserted_formulas::commit(unsigned new_qhead) {
 }
 
 void asserted_formulas::propagate_values() {
-    TRACE("propagate_values", tout << "before:\n"; display(tout););
     flush_cache();
 
     unsigned num_prop = 0;
@@ -466,7 +465,7 @@ void asserted_formulas::propagate_values() {
 }
 
 unsigned asserted_formulas::propagate_values(unsigned i) {
-    expr * n = m_formulas[i].get_fml();
+    expr_ref n(m_formulas[i].get_fml(), m);
     expr_ref new_n(m);                                                  
     proof_ref new_pr(m);                                                
     m_rewriter(n, new_n, new_pr);                                       
@@ -476,6 +475,9 @@ unsigned asserted_formulas::propagate_values(unsigned i) {
     }
     justified_expr j(m, new_n, new_pr);
     m_formulas[i] = j;
+    if (m_formulas[i].get_fml() != new_n) {
+        std::cout << "NOT updated\n";
+    }
     if (m.is_false(j.get_fml())) {
         m_inconsistent = true;
     }

From a887475e9fa1b576fd857b997c5542536bc17c69 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 15:01:54 -0700
Subject: [PATCH 47/74] remove dom-simplifier from build

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/tactic/core/CMakeLists.txt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt
index 006948315..f192b4fa6 100644
--- a/src/tactic/core/CMakeLists.txt
+++ b/src/tactic/core/CMakeLists.txt
@@ -7,7 +7,6 @@ z3_add_component(core_tactics
     ctx_simplify_tactic.cpp
     der_tactic.cpp
     distribute_forall_tactic.cpp
-    dom_simplify_tactic.cpp
     elim_term_ite_tactic.cpp
     elim_uncnstr_tactic.cpp
     injectivity_tactic.cpp

From ee4ae33ac444fdc7407545e06453029aeca45f56 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 15:19:55 -0700
Subject: [PATCH 48/74] build fixes

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/datatype_decl_plugin2.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 0030af609..44102156d 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -495,7 +495,10 @@ namespace datatype {
             return param_size::size::mk_power(sz2, sz1);
         }
         for (sort* p : params) {           
-            if (s == p) return param_size::size::mk_param(sort_ref(s, m));
+            if (s == p) {
+                sort_ref sr(s, m);
+                return param_size::size::mk_param(sr);
+            }
         }
         return param_size::size::mk_offset(s->get_num_elements());        
     }

From 10f734357ee2e50e88d29462c857e3757a69e238 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 15:32:57 -0700
Subject: [PATCH 49/74] build fixes

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/array_decl_plugin.cpp | 2 +-
 src/ast/array_decl_plugin.h   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp
index 6296d344a..cb016e263 100644
--- a/src/ast/array_decl_plugin.cpp
+++ b/src/ast/array_decl_plugin.cpp
@@ -546,7 +546,7 @@ expr * array_decl_plugin::get_some_value(sort * s) {
     return m_manager->mk_app(m_family_id, OP_CONST_ARRAY, 1, &p, 1, &v);
 }
 
-bool array_decl_plugin::is_fully_interp(sort const * s) const {
+bool array_decl_plugin::is_fully_interp(sort * s) const {
     SASSERT(s->is_sort_of(m_family_id, ARRAY_SORT));
     unsigned sz = get_array_arity(s);
     for (unsigned i = 0; i < sz; i++) {
diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h
index 0febb82a4..0704fe56a 100644
--- a/src/ast/array_decl_plugin.h
+++ b/src/ast/array_decl_plugin.h
@@ -127,7 +127,7 @@ class array_decl_plugin : public decl_plugin {
 
     virtual expr * get_some_value(sort * s);
 
-    virtual bool is_fully_interp(sort const * s) const;
+    virtual bool is_fully_interp(sort * s) const;
 };
 
 class array_recognizers {

From eb6b2813ff68501d308cc2696a57568aa9e6e835 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 16:14:22 -0700
Subject: [PATCH 50/74] build fixes

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/datatype_decl_plugin2.cpp          | 14 ++++++++++++++
 src/ast/datatype_decl_plugin2.h            | 19 +++++++++++++++----
 src/ast/rewriter/arith_rewriter.cpp        |  3 ++-
 src/ast/rewriter/arith_rewriter.h          |  1 +
 src/ast/rewriter/arith_rewriter_params.pyg |  1 +
 src/smt/asserted_formulas.cpp              |  2 +-
 6 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 44102156d..8969e02a9 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -24,6 +24,12 @@ Revision History:
 
 namespace datatype {
 
+    void accessor::fix_range(sort_ref_vector const& dts) {
+        if (!m_range) {
+            m_range = dts[m_index];
+        }
+    }
+
     func_decl_ref accessor::instantiate(sort_ref_vector const& ps) const {
         unsigned n = ps.size();
         SASSERT(n == get_def().params().size());
@@ -285,6 +291,14 @@ namespace datatype {
                 sort_ref_vector ps(m);
                 sorts.push_back(d.instantiate(ps));
             }
+            for (symbol const& s : m_def_block) {
+                def& d = *m_defs[s];
+                for (constructor& c : d) {
+                    for (accessor& a : c) {
+                        // a.fix_range(sorts);
+                    }
+                }
+            }
             if (!u().is_well_founded(sorts.size(), sorts.c_ptr())) {
                 m_manager->raise_exception("datatype is not well-founded");
             }
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index a4a76f346..13e85b47f 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -46,17 +46,24 @@ namespace datatype {
     class accessor {
         ast_manager& m;
         symbol   m_name;
-        sort_ref m_domain;
         sort_ref m_range;
+        unsigned m_index;    // reference to recursive data-type may only get resolved after all mutually recursive data-types are procssed.
         constructor* m_constructor;
     public:
-        accessor(ast_manager& m, symbol const& n):
+        accessor(ast_manager& m, symbol const& n, sort* range):
             m(m),
             m_name(n),
-            m_domain(m),
-            m_range(m)
+            m_range(range, m),
+            m_index(UINT_MAX)
+        {}
+        accessor(ast_manager& m, symbol const& n, unsigned index):
+            m(m),
+            m_name(n),
+            m_range(m),
+            m_index(index)
         {}
         sort* range() const { return m_range; }
+        void fix_range(sort_ref_vector const& dts);
         symbol const& name() const { return m_name; }
         func_decl_ref instantiate(sort_ref_vector const& ps) const;
         func_decl_ref instantiate(sort* dt) const;
@@ -78,6 +85,8 @@ namespace datatype {
         vector<accessor> const& accessors() const { return m_accessors; }
         vector<accessor>::const_iterator begin() const { return m_accessors.begin(); }
         vector<accessor>::const_iterator end() const { return m_accessors.end(); }
+        vector<accessor>::iterator begin() { return m_accessors.begin(); }
+        vector<accessor>::iterator end() { return m_accessors.end(); }
         func_decl_ref instantiate(sort_ref_vector const& ps) const;
         func_decl_ref instantiate(sort* dt) const;
         void attach(def* d) { m_def = d; }
@@ -205,6 +214,8 @@ namespace datatype {
         vector<constructor> const& constructors() const { return m_constructors; }
         vector<constructor>::const_iterator begin() const { return m_constructors.begin(); }
         vector<constructor>::const_iterator end() const { return m_constructors.end(); }
+        vector<constructor>::iterator begin() { return m_constructors.begin(); }
+        vector<constructor>::iterator end() { return m_constructors.end(); }
         sort_ref_vector const& params() const { return m_params; }
         util& u() const { return m_util; }
         param_size::size* sort_size() { return m_sort_size; }
diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp
index b630cdff6..8b29004ab 100644
--- a/src/ast/rewriter/arith_rewriter.cpp
+++ b/src/ast/rewriter/arith_rewriter.cpp
@@ -25,6 +25,7 @@ Notes:
 void arith_rewriter::updt_local_params(params_ref const & _p) {
     arith_rewriter_params p(_p);
     m_arith_lhs       = p.arith_lhs();
+    m_arith_ineq_lhs  = p.arith_ineq_lhs;
     m_gcd_rounding    = p.gcd_rounding();
     m_elim_to_real    = p.elim_to_real();
     m_push_to_real    = p.push_to_real();
@@ -370,7 +371,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
     if ((is_zero(arg1) && is_reduce_power_target(arg2, kind == EQ)) ||
         (is_zero(arg2) && is_reduce_power_target(arg1, kind == EQ)))
         return reduce_power(arg1, arg2, kind, result);
-    br_status st = cancel_monomials(arg1, arg2, true, new_arg1, new_arg2);
+    br_status st = cancel_monomials(arg1, arg2, m_arith_ineq_lhs || m_arith_lhs, new_arg1, new_arg2);
     TRACE("mk_le_bug", tout << "st: " << st << " " << new_arg1 << " " << new_arg2 << "\n";);
     if (st != BR_FAILED) {
         arg1 = new_arg1;
diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h
index f1e4c4396..af9d0e09d 100644
--- a/src/ast/rewriter/arith_rewriter.h
+++ b/src/ast/rewriter/arith_rewriter.h
@@ -49,6 +49,7 @@ public:
 
 class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
     bool m_arith_lhs;
+    bool m_arith_ineq_lhs;
     bool m_gcd_rounding;
     bool m_elim_to_real;
     bool m_push_to_real;
diff --git a/src/ast/rewriter/arith_rewriter_params.pyg b/src/ast/rewriter/arith_rewriter_params.pyg
index 9a6c6c1c0..d40f46917 100644
--- a/src/ast/rewriter/arith_rewriter_params.pyg
+++ b/src/ast/rewriter/arith_rewriter_params.pyg
@@ -9,6 +9,7 @@ def_module_params(module_name='rewriter',
                           ("sort_sums", BOOL, False, "sort the arguments of + application."),
                           ("gcd_rounding", BOOL, False, "use gcd rounding on integer arithmetic atoms."),
                           ("arith_lhs", BOOL, False, "all monomials are moved to the left-hand-side, and the right-hand-side is just a constant."),
+                          ("arith_ineq_lhs", BOOL, False, "rewrite inequalities so that right-hand-side is a constant."),
                           ("elim_to_real", BOOL, False, "eliminate to_real from arithmetic predicates that contain only integers."),
                           ("push_to_real", BOOL, True, "distribute to_real over * and +."),
                           ("expand_eqs", BOOL, False, "expand equalities into two inequalities"),
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index 7dca13c07..f37cabde8 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -120,7 +120,7 @@ void asserted_formulas::push_assertion(expr * e, proof * pr, vector<justified_ex
 void asserted_formulas::set_eliminate_and(bool flag) {
     params_ref p;
     p.set_bool("elim_and", flag);
-//    p.set_bool("arith_lhs", true);
+    p.set_bool("arith_ineq_lhs", true);
     p.set_bool("sort_sums", true);
     p.set_bool("rewrite_patterns", true);
     p.set_bool("expand_eqs", m_params.m_arith_expand_eqs);

From 7b9b7149798f1f2cfce059727845ac3f58a2c58e Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 16:14:58 -0700
Subject: [PATCH 51/74] build fixes

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/arith_rewriter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp
index 8b29004ab..ff1894a18 100644
--- a/src/ast/rewriter/arith_rewriter.cpp
+++ b/src/ast/rewriter/arith_rewriter.cpp
@@ -25,7 +25,7 @@ Notes:
 void arith_rewriter::updt_local_params(params_ref const & _p) {
     arith_rewriter_params p(_p);
     m_arith_lhs       = p.arith_lhs();
-    m_arith_ineq_lhs  = p.arith_ineq_lhs;
+    m_arith_ineq_lhs  = p.arith_ineq_lhs();
     m_gcd_rounding    = p.gcd_rounding();
     m_elim_to_real    = p.elim_to_real();
     m_push_to_real    = p.push_to_real();

From 09386e43e307a95cc9f49bfe3bbdd6f57477cce1 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 19:07:02 -0700
Subject: [PATCH 52/74] doctest fix

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/python/z3/z3.py           |  4 +--
 src/ast/datatype_decl_plugin2.cpp | 59 ++++++++++++++++---------------
 src/ast/datatype_decl_plugin2.h   | 44 +++++++++++------------
 3 files changed, 53 insertions(+), 54 deletions(-)

diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py
index 1452a037e..0b5f151be 100644
--- a/src/api/python/z3/z3.py
+++ b/src/api/python/z3/z3.py
@@ -3640,7 +3640,7 @@ def BitVecs(names, bv, ctx=None):
     >>> Product(x, y, z)
     1*x*y*z
     >>> simplify(Product(x, y, z))
-    x*y*z
+    z*x*y
     """
     ctx = _get_ctx(ctx)
     if isinstance(names, str):
@@ -7647,7 +7647,7 @@ def simplify(a, *arguments, **keywords):
     >>> simplify(x + 1 + y + x + 1)
     2 + 2*x + y
     >>> simplify((x + 1)*(y + 1), som=True)
-    1 + x + y + x*y
+    1 + x + y + y*x
     >>> simplify(Distinct(x, y, 1), blast_distinct=True)
     And(Not(x == y), Not(x == 1), Not(y == 1))
     >>> simplify(And(x == 0, y == 1), elim_and=True)
diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 8969e02a9..8233da7d9 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -31,6 +31,7 @@ namespace datatype {
     }
 
     func_decl_ref accessor::instantiate(sort_ref_vector const& ps) const {
+        ast_manager& m = ps.get_manager();
         unsigned n = ps.size();
         SASSERT(n == get_def().params().size());
         sort_ref range(m.substitute(m_range, n, get_def().params().c_ptr(), ps.c_ptr()), m);
@@ -52,8 +53,8 @@ namespace datatype {
 
     func_decl_ref constructor::instantiate(sort_ref_vector const& ps) const {
         sort_ref_vector domain(m);
-        for (accessor const& a : accessors()) {
-            domain.push_back(a.instantiate(ps)->get_range());
+        for (accessor const* a : accessors()) {
+            domain.push_back(a->instantiate(ps)->get_range());
         }
         sort_ref range = get_def().instantiate(ps);
         parameter pas[1] = { parameter(name()) };
@@ -293,9 +294,9 @@ namespace datatype {
             }
             for (symbol const& s : m_def_block) {
                 def& d = *m_defs[s];
-                for (constructor& c : d) {
-                    for (accessor& a : c) {
-                        // a.fix_range(sorts);
+                for (constructor* c : d) {
+                    for (accessor* a : *c) {
+                        a->fix_range(sorts);
                     }
                 }
             }
@@ -401,9 +402,9 @@ namespace datatype {
         def const& d = get_def(s);
         bool is_interp = true;
         m_fully_interp_trail.push_back(s);
-        for (constructor const& c : d) {
-            for (accessor const& a : c) {
-                func_decl_ref ac = a.instantiate(s);
+        for (constructor const* c : d) {
+            for (accessor const* a : *c) {
+                func_decl_ref ac = a->instantiate(s);
                 sort* r = ac->get_range();
                 if (!m.is_fully_interp(r)) {
                     is_interp = false;
@@ -438,9 +439,9 @@ namespace datatype {
             already_found.insert(s, GRAY);
             def const& d = get_def(s);
             bool can_process       = true;
-            for (constructor const& c : d) {
-                for (accessor const& a : c) {
-                    sort* d = a.range();
+            for (constructor const* c : d) {
+                for (accessor const* a : *c) {
+                    sort* d = a->range();
                     // check if d is a datatype sort
                     subsorts.reset();
                     get_subsorts(d, subsorts);
@@ -533,9 +534,9 @@ namespace datatype {
             bool is_infinite = false;
             bool can_process = true;
             def& d = get_def(s);
-            for (constructor const& c : d) {
-                for (accessor const& a : c) {
-                    sort* r = a.range();
+            for (constructor const* c : d) {
+                for (accessor const* a : *c) {
+                    sort* r = a->range();
                     if (is_datatype(r)) {
                         symbol s2 = r->get_name();
                         if (already_found.find(s2, st)) {
@@ -562,10 +563,10 @@ namespace datatype {
             }
 
             ptr_vector<param_size::size> s_add;        
-            for (constructor const& c : d) {
+            for (constructor const* c : d) {
                 ptr_vector<param_size::size> s_mul;
-                for (accessor const& a : c) {
-                    s_mul.push_back(get_sort_size(d.params(), a.range()));
+                for (accessor const* a : *c) {
+                    s_mul.push_back(get_sort_size(d.params(), a->range()));
                 }
                 s_add.push_back(param_size::size::mk_times(s_mul));
             }
@@ -594,10 +595,10 @@ namespace datatype {
                 }
                 sort* s = sorts[tid];
                 def const& d = get_def(s);
-                for (constructor const& c : d) {
+                for (constructor const* c : d) {
                     bool found_nonwf = false;
-                    for (accessor const& a : c) {
-                        if (sort2id.find(a.range(), id) && !well_founded[id]) {
+                    for (accessor const* a : *c) {
+                        if (sort2id.find(a->range(), id) && !well_founded[id]) {
                             found_nonwf = true;
                             break;
                         }
@@ -652,8 +653,8 @@ namespace datatype {
         m_vectors.push_back(r);
         m_datatype2constructors.insert(ty, r);
         def const& d = get_def(ty);
-        for (constructor const& c : d) {
-            func_decl_ref f = c.instantiate(ty);
+        for (constructor const* c : d) {
+            func_decl_ref f = c->instantiate(ty);
             m_asts.push_back(f);
             r->push_back(f);
         }
@@ -671,10 +672,10 @@ namespace datatype {
         m_constructor2accessors.insert(con, res);
         sort * datatype = con->get_range();
         def const& d = get_def(datatype);
-        for (constructor const& c : d) {
-            if (c.name() == con->get_name()) {
-                for (accessor const& a : c) {
-                    res->push_back(a.instantiate(datatype));
+        for (constructor const* c : d) {
+            if (c->name() == con->get_name()) {
+                for (accessor const* a : *c) {
+                    res->push_back(a->instantiate(datatype));
                 }
                 break;
             }
@@ -739,9 +740,9 @@ namespace datatype {
         symbol c_id   = accessor->get_parameter(1).get_symbol();
         def const& d = get_def(datatype);
         func_decl_ref fn(m);
-        for (constructor const& c : d) {
-            if (c.name() == c_id) {
-                fn = c.instantiate(datatype);
+        for (constructor const* c : d) {
+            if (c->name() == c_id) {
+                fn = c->instantiate(datatype);
                 break;
             }
         }
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index 13e85b47f..c540b7c1c 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -44,22 +44,19 @@ namespace datatype {
     };
 
     class accessor {
-        ast_manager& m;
         symbol   m_name;
-        sort_ref m_range;
+        sort*    m_range;
         unsigned m_index;    // reference to recursive data-type may only get resolved after all mutually recursive data-types are procssed.
         constructor* m_constructor;
     public:
-        accessor(ast_manager& m, symbol const& n, sort* range):
-            m(m),
+        accessor(symbol const& n, sort* range):
             m_name(n),
-            m_range(range, m),
+            m_range(range),
             m_index(UINT_MAX)
         {}
-        accessor(ast_manager& m, symbol const& n, unsigned index):
-            m(m),
+        accessor(symbol const& n, unsigned index):
             m_name(n),
-            m_range(m),
+            m_range(0),
             m_index(index)
         {}
         sort* range() const { return m_range; }
@@ -76,17 +73,18 @@ namespace datatype {
     class constructor {
         ast_manager&     m;
         symbol           m_name;
-        vector<accessor> m_accessors;
+        ptr_vector<accessor> m_accessors;
         def*             m_def;
     public:
         constructor(ast_manager& m, symbol n): m(m), m_name(n) {}
-        void add(accessor& a) { m_accessors.push_back(a); a.attach(this); }
+        ~constructor();
+        void add(accessor* a) { m_accessors.push_back(a); a->attach(this); }
         symbol const& name() const { return m_name; }
-        vector<accessor> const& accessors() const { return m_accessors; }
-        vector<accessor>::const_iterator begin() const { return m_accessors.begin(); }
-        vector<accessor>::const_iterator end() const { return m_accessors.end(); }
-        vector<accessor>::iterator begin() { return m_accessors.begin(); }
-        vector<accessor>::iterator end() { return m_accessors.end(); }
+        ptr_vector<accessor> const& accessors() const { return m_accessors; }
+        ptr_vector<accessor>::const_iterator begin() const { return m_accessors.begin(); }
+        ptr_vector<accessor>::const_iterator end() const { return m_accessors.end(); }
+        ptr_vector<accessor>::iterator begin() { return m_accessors.begin(); }
+        ptr_vector<accessor>::iterator end() { return m_accessors.end(); }
         func_decl_ref instantiate(sort_ref_vector const& ps) const;
         func_decl_ref instantiate(sort* dt) const;
         void attach(def* d) { m_def = d; }
@@ -190,7 +188,7 @@ namespace datatype {
         param_size::size*   m_sort_size;
         sort_ref_vector     m_params;
         mutable sort_ref    m_sort;
-        vector<constructor> m_constructors;
+        ptr_vector<constructor> m_constructors;
     public:
         def(ast_manager& m, util& u, symbol const& n, unsigned class_id, unsigned num_params, sort * const* params):
             m(m),
@@ -204,18 +202,18 @@ namespace datatype {
         ~def() {
             if (m_sort_size) m_sort_size->dec_ref();
         }
-        void add(constructor& c) {
+        void add(constructor* c) {
             m_constructors.push_back(c);
-            c.attach(this);
+            c->attach(this);
         }
         symbol const& name() const { return m_name; }
         unsigned id() const { return m_class_id; }
         sort_ref instantiate(sort_ref_vector const& ps) const;
-        vector<constructor> const& constructors() const { return m_constructors; }
-        vector<constructor>::const_iterator begin() const { return m_constructors.begin(); }
-        vector<constructor>::const_iterator end() const { return m_constructors.end(); }
-        vector<constructor>::iterator begin() { return m_constructors.begin(); }
-        vector<constructor>::iterator end() { return m_constructors.end(); }
+        ptr_vector<constructor> const& constructors() const { return m_constructors; }
+        ptr_vector<constructor>::const_iterator begin() const { return m_constructors.begin(); }
+        ptr_vector<constructor>::const_iterator end() const { return m_constructors.end(); }
+        ptr_vector<constructor>::iterator begin() { return m_constructors.begin(); }
+        ptr_vector<constructor>::iterator end() { return m_constructors.end(); }
         sort_ref_vector const& params() const { return m_params; }
         util& u() const { return m_util; }
         param_size::size* sort_size() { return m_sort_size; }

From a3dba5b2f97341269e205dbfb18d4c127e084f56 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 3 Sep 2017 20:01:59 -0700
Subject: [PATCH 53/74] hide new datatype plugin

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/api_datatype.cpp                 | 107 +++++++++--------------
 src/ast/ast_smt_pp.cpp                   |  17 ++--
 src/ast/datatype_decl_plugin.cpp         |  37 ++++----
 src/ast/datatype_decl_plugin.h           |  11 ++-
 src/ast/datatype_decl_plugin2.cpp        |  12 ++-
 src/ast/datatype_decl_plugin2.h          |  74 ++++++++++++----
 src/ast/decl_collector.cpp               |   4 +-
 src/ast/rewriter/datatype_rewriter.cpp   |  16 ++--
 src/ast/rewriter/enum2bv_rewriter.cpp    |   2 +-
 src/cmd_context/cmd_context.cpp          |  12 +--
 src/muz/base/dl_rule.h                   |   2 +-
 src/muz/base/rule_properties.cpp         |   2 +-
 src/muz/bmc/dl_bmc_engine.cpp            |  10 +--
 src/muz/pdr/pdr_prop_solver.cpp          |   2 +-
 src/muz/spacer/spacer_util.cpp           |   2 +-
 src/qe/qe_datatype_plugin.cpp            |  12 +--
 src/qe/qe_datatypes.cpp                  |   6 +-
 src/qe/qe_lite.cpp                       |   2 +-
 src/smt/proto_model/datatype_factory.cpp |  12 +--
 src/smt/smt_value_sort.cpp               |   2 +-
 src/smt/theory_datatype.cpp              |  37 ++++----
 src/tactic/core/elim_uncnstr_tactic.cpp  |  17 ++--
 src/tactic/portfolio/enum2bv_solver.cpp  |   2 +-
 src/test/get_consequences.cpp            |   2 +-
 24 files changed, 211 insertions(+), 191 deletions(-)

diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp
index 851d96a4e..39d147ed1 100644
--- a/src/api/api_datatype.cpp
+++ b/src/api/api_datatype.cpp
@@ -69,18 +69,13 @@ extern "C" {
         // create constructor
         SASSERT(dt_util.is_datatype(tuple));
         SASSERT(!dt_util.is_recursive(tuple));
-        ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(tuple);
-        func_decl* decl = (*decls)[0];
+        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(tuple);
+        func_decl* decl = (decls)[0];
         mk_c(c)->save_multiple_ast_trail(decl);
         *mk_tuple_decl = of_func_decl(decl);
 
         // Create projections
-        ptr_vector<func_decl> const * accs = dt_util.get_constructor_accessors(decl);
-        if (!accs) {
-            SET_ERROR_CODE(Z3_INVALID_ARG);
-            RETURN_Z3(0);
-        }
-        ptr_vector<func_decl> const & _accs = *accs;
+        ptr_vector<func_decl> const & _accs = dt_util.get_constructor_accessors(decl);
         SASSERT(_accs.size() == num_fields);
         for (unsigned i = 0; i < _accs.size(); i++) {
             mk_c(c)->save_multiple_ast_trail(_accs[i]);
@@ -136,10 +131,10 @@ extern "C" {
         // create constructor
         SASSERT(dt_util.is_datatype(e));
         SASSERT(!dt_util.is_recursive(e));
-        ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(e);
-        SASSERT(decls && decls->size() == n);
+        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(e);
+        SASSERT(decls.size() == n);
         for (unsigned i = 0; i < n; ++i) {
-            func_decl* decl = (*decls)[i];
+            func_decl* decl = (decls)[i];
             mk_c(c)->save_multiple_ast_trail(decl);
             enum_consts[i] = of_func_decl(decl);
             decl = dt_util.get_constructor_recognizer(decl);
@@ -191,7 +186,7 @@ extern "C" {
         sort * s = sorts.get(0);
 
         mk_c(c)->save_multiple_ast_trail(s);
-        ptr_vector<func_decl> const& cnstrs = *data_util.get_datatype_constructors(s);
+        ptr_vector<func_decl> const& cnstrs = data_util.get_datatype_constructors(s);
         SASSERT(cnstrs.size() == 2);
         func_decl* f;
         if (nil_decl) {
@@ -215,18 +210,16 @@ extern "C" {
             *is_cons_decl = of_func_decl(f);
         }
         if (head_decl) {
-            ptr_vector<func_decl> const* acc = data_util.get_constructor_accessors(cnstrs[1]);
-            SASSERT(acc);
-            SASSERT(acc->size() == 2);
-            f = (*acc)[0];
+            ptr_vector<func_decl> const& acc = data_util.get_constructor_accessors(cnstrs[1]);
+            SASSERT(acc.size() == 2);
+            f = (acc)[0];
             mk_c(c)->save_multiple_ast_trail(f);
             *head_decl = of_func_decl(f);
         }
         if (tail_decl) {
-            ptr_vector<func_decl> const* acc = data_util.get_constructor_accessors(cnstrs[1]);
-            SASSERT(acc);
-            SASSERT(acc->size() == 2);
-            f = (*acc)[1];
+            ptr_vector<func_decl> const& acc = data_util.get_constructor_accessors(cnstrs[1]);
+            SASSERT(acc.size() == 2);
+            f = (acc)[1];
             mk_c(c)->save_multiple_ast_trail(f);
             *tail_decl = of_func_decl(f);
         }
@@ -301,13 +294,9 @@ extern "C" {
             *tester = of_func_decl(f2);
         }
 
-        ptr_vector<func_decl> const* accs = data_util.get_constructor_accessors(f);
-        if (!accs && num_fields > 0) {
-            SET_ERROR_CODE(Z3_INVALID_ARG);
-            return;
-        }
+        ptr_vector<func_decl> const& accs = data_util.get_constructor_accessors(f);
         for (unsigned i = 0; i < num_fields; ++i) {
-            func_decl* f2 = (*accs)[i];
+            func_decl* f2 = (accs)[i];
             mk_c(c)->save_multiple_ast_trail(f2);
             accessors[i] = of_func_decl(f2);
         }
@@ -368,11 +357,11 @@ extern "C" {
         sort * s = sorts.get(0);
 
         mk_c(c)->save_ast_trail(s);
-        ptr_vector<func_decl> const* cnstrs = data_util.get_datatype_constructors(s);
+        ptr_vector<func_decl> const& cnstrs = data_util.get_datatype_constructors(s);
 
         for (unsigned i = 0; i < num_constructors; ++i) {
             constructor* cn = reinterpret_cast<constructor*>(constructors[i]);
-            cn->m_constructor = (*cnstrs)[i];
+            cn->m_constructor = cnstrs[i];
         }
         RETURN_Z3_mk_datatype(of_sort(s));
         Z3_CATCH_RETURN(0);
@@ -434,10 +423,10 @@ extern "C" {
             mk_c(c)->save_multiple_ast_trail(s);
             sorts[i] = of_sort(s);
             constructor_list* cl = reinterpret_cast<constructor_list*>(constructor_lists[i]);
-            ptr_vector<func_decl> const* cnstrs = data_util.get_datatype_constructors(s);
+            ptr_vector<func_decl> const& cnstrs = data_util.get_datatype_constructors(s);
             for (unsigned j = 0; j < cl->size(); ++j) {
                 constructor* cn = (*cl)[j];
-                cn->m_constructor = (*cnstrs)[j];
+                cn->m_constructor = cnstrs[j];
             }
         }
         RETURN_Z3_mk_datatypes;
@@ -456,12 +445,7 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(_t);
-        if (!decls) {
-            SET_ERROR_CODE(Z3_INVALID_ARG);
-            return 0;
-        }
-        return decls->size();
+        return dt_util.get_datatype_constructors(_t).size();
         Z3_CATCH_RETURN(0);
     }
 
@@ -474,12 +458,12 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(_t);
-        if (!decls || idx >= decls->size()) {
+        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(_t);
+        if (idx >= decls.size()) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        func_decl* decl = (*decls)[idx];
+        func_decl* decl = (decls)[idx];
         mk_c(c)->save_ast_trail(decl);
         return of_func_decl(decl);
     }
@@ -504,12 +488,12 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(_t);
-        if (!decls || idx >= decls->size()) {
+        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(_t);
+        if (idx >= decls.size()) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        func_decl* decl = (*decls)[idx];
+        func_decl* decl = (decls)[idx];
         decl = dt_util.get_constructor_recognizer(decl);
         mk_c(c)->save_ast_trail(decl);
         RETURN_Z3(of_func_decl(decl));
@@ -527,23 +511,23 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(_t);
-        if (!decls || idx_c >= decls->size()) {
+        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(_t);
+        if (idx_c >= decls.size()) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        func_decl* decl = (*decls)[idx_c];
+        func_decl* decl = (decls)[idx_c];
         if (decl->get_arity() <= idx_a) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const * accs = dt_util.get_constructor_accessors(decl);
-        SASSERT(accs && accs->size() == decl->get_arity());
-        if (!accs || accs->size() <= idx_a) {
+        ptr_vector<func_decl> const & accs = dt_util.get_constructor_accessors(decl);
+        SASSERT(accs.size() == decl->get_arity());
+        if (accs.size() <= idx_a) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        decl = (*accs)[idx_a];
+        decl = (accs)[idx_a];
         mk_c(c)->save_ast_trail(decl);
         RETURN_Z3(of_func_decl(decl));
         Z3_CATCH_RETURN(0);
@@ -574,16 +558,13 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(tuple);
-        if (!decls || decls->size() != 1) {
+        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(tuple);
+        if (decls.size() != 1) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        ptr_vector<func_decl> const * accs = dt_util.get_constructor_accessors((*decls)[0]);
-        if (!accs) {
-            return 0;
-        }
-        return accs->size();
+        ptr_vector<func_decl> const & accs = dt_util.get_constructor_accessors(decls[0]);
+        return accs.size();
         Z3_CATCH_RETURN(0);
     }
 
@@ -597,21 +578,17 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(tuple);
-        if (!decls || decls->size() != 1) {
+        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(tuple);
+        if (decls.size() != 1) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const * accs = dt_util.get_constructor_accessors((*decls)[0]);
-        if (!accs) {
-            SET_ERROR_CODE(Z3_INVALID_ARG);
-            RETURN_Z3(0);
-        }
-        if (accs->size() <= i) {
+        ptr_vector<func_decl> const & accs = dt_util.get_constructor_accessors((decls)[0]);
+        if (accs.size() <= i) {
             SET_ERROR_CODE(Z3_IOB);
             RETURN_Z3(0);
         }
-        func_decl* acc = (*accs)[i];
+        func_decl* acc = (accs)[i];
         mk_c(c)->save_ast_trail(acc);
         RETURN_Z3(of_func_decl(acc));
         Z3_CATCH_RETURN(0);
diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp
index 9ebfdfbef..9211c5899 100644
--- a/src/ast/ast_smt_pp.cpp
+++ b/src/ast/ast_smt_pp.cpp
@@ -907,7 +907,6 @@ public:
     void pp_dt(ast_mark& mark, sort* s) {
         SASSERT(s->is_sort_of(m_dt_fid, DATATYPE_SORT));
         datatype_util util(m_manager);
-        ptr_vector<func_decl> const* decls;
         ptr_vector<sort> rec_sorts;
 
         rec_sorts.push_back(s);
@@ -916,10 +915,10 @@ public:
         // collect siblings and sorts that have not already been printed.
         for (unsigned h = 0; h < rec_sorts.size(); ++h) {
             s = rec_sorts[h];
-            decls = util.get_datatype_constructors(s);
+            ptr_vector<func_decl> const& decls = util.get_datatype_constructors(s);
 
-            for (unsigned i = 0; i < decls->size(); ++i) {
-                func_decl* f = (*decls)[i];
+            for (unsigned i = 0; i < decls.size(); ++i) {
+                func_decl* f = decls[i];
                 for (unsigned j = 0; j < f->get_arity(); ++j) {
                     sort* s2 = f->get_domain(j);
                     if (!mark.is_marked(s2)) {
@@ -955,11 +954,11 @@ public:
             m_out << "(";
             m_out << m_renaming.get_symbol(s->get_name());
             m_out << " ";
-            decls = util.get_datatype_constructors(s);
+            ptr_vector<func_decl> const& decls = util.get_datatype_constructors(s);
 
-            for (unsigned i = 0; i < decls->size(); ++i) {
-                func_decl* f = (*decls)[i];
-                ptr_vector<func_decl> const& accs = *util.get_constructor_accessors(f);
+            for (unsigned i = 0; i < decls.size(); ++i) {
+                func_decl* f = decls[i];
+                ptr_vector<func_decl> const& accs = util.get_constructor_accessors(f);
                 if (m_is_smt2 || accs.size() > 0) {
                     m_out << "(";
                 }
@@ -976,7 +975,7 @@ public:
                 }
                 if (m_is_smt2 || accs.size() > 0) {
                     m_out << ")";
-                    if (i + 1 < decls->size()) {
+                    if (i + 1 < decls.size()) {
                         m_out << " ";
                     }
                 }
diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp
index 446992105..e2aa54236 100644
--- a/src/ast/datatype_decl_plugin.cpp
+++ b/src/ast/datatype_decl_plugin.cpp
@@ -20,6 +20,7 @@ Revision History:
 #include "util/warning.h"
 #include "ast/ast_smt2_pp.h"
 
+#ifndef DATATYPE_V2
 
 /**
    \brief Auxiliary class used to declare inductive datatypes.
@@ -802,11 +803,11 @@ func_decl * datatype_util::get_constructor(sort * ty, unsigned c_id) {
     return d;
 }
 
-ptr_vector<func_decl> const * datatype_util::get_datatype_constructors(sort * ty) {
+ptr_vector<func_decl> const & datatype_util::get_datatype_constructors(sort * ty) {
     SASSERT(is_datatype(ty));
     ptr_vector<func_decl> * r = 0;
     if (m_datatype2constructors.find(ty, r))
-        return r;
+        return *r;
     r = alloc(ptr_vector<func_decl>);
     m_asts.push_back(ty);
     m_vectors.push_back(r);
@@ -819,7 +820,7 @@ ptr_vector<func_decl> const * datatype_util::get_datatype_constructors(sort * ty
         m_asts.push_back(c);
         r->push_back(c);
     }
-    return r;
+    return *r;
 }
 
 /**
@@ -854,12 +855,12 @@ func_decl * datatype_util::get_non_rec_constructor_core(sort * ty, ptr_vector<so
     //   1) T_i's are not recursive
     // If there is no such constructor, then we select one that 
     //   2) each type T_i is not recursive or contains a constructor that does not depend on T
-    ptr_vector<func_decl> const * constructors = get_datatype_constructors(ty);
+    ptr_vector<func_decl> const & constructors = get_datatype_constructors(ty);
     // step 1)
-    unsigned sz = constructors->size();
+    unsigned sz = constructors.size();
     ++m_start;
     for (unsigned j = 0; j < sz; ++j) {        
-        func_decl * c = (*constructors)[(j + m_start) % sz];
+        func_decl * c = constructors[(j + m_start) % sz];
         unsigned num_args = c->get_arity();
         unsigned i = 0;
         for (; i < num_args; i++) {
@@ -872,7 +873,7 @@ func_decl * datatype_util::get_non_rec_constructor_core(sort * ty, ptr_vector<so
     }
     // step 2)
     for (unsigned j = 0; j < sz; ++j) {        
-        func_decl * c = (*constructors)[(j + m_start) % sz];
+        func_decl * c = constructors[(j + m_start) % sz];
         TRACE("datatype_util_bug", tout << "non_rec_constructor c: " << c->get_name() << "\n";);
         unsigned num_args = c->get_arity();
         unsigned i = 0;
@@ -915,11 +916,11 @@ func_decl * datatype_util::get_constructor_recognizer(func_decl * constructor) {
     return d;
 }
 
-ptr_vector<func_decl> const * datatype_util::get_constructor_accessors(func_decl * constructor) {
+ptr_vector<func_decl> const & datatype_util::get_constructor_accessors(func_decl * constructor) {
     SASSERT(is_constructor(constructor));
     ptr_vector<func_decl> * res = 0;
     if (m_constructor2accessors.find(constructor, res))
-        return res;
+        return *res;
     res = alloc(ptr_vector<func_decl>);
     m_asts.push_back(constructor);
     m_vectors.push_back(res);
@@ -938,7 +939,7 @@ ptr_vector<func_decl> const * datatype_util::get_constructor_accessors(func_decl
         m_asts.push_back(d);
         res->push_back(d);
     }
-    return res;
+    return *res;
 }
 
 func_decl * datatype_util::get_accessor_constructor(func_decl * accessor) { 
@@ -988,7 +989,7 @@ bool datatype_util::is_enum_sort(sort* s) {
     bool r = false;
     if (m_is_enum.find(s, r))
         return r;
-    ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
+    ptr_vector<func_decl> const& cnstrs = get_datatype_constructors(s);
     r = true;
     for (unsigned i = 0; r && i < cnstrs.size(); ++i) {
         r = cnstrs[i]->get_arity() == 0;
@@ -1048,14 +1049,14 @@ void datatype_util::display_datatype(sort *s0, std::ostream& strm) {
         todo.pop_back();
         strm << s->get_name() << " =\n";
 
-        ptr_vector<func_decl> const * cnstrs = get_datatype_constructors(s);
-        for (unsigned i = 0; i < cnstrs->size(); ++i) {
-            func_decl* cns = (*cnstrs)[i];
+        ptr_vector<func_decl> const & cnstrs = get_datatype_constructors(s);
+        for (unsigned i = 0; i < cnstrs.size(); ++i) {
+            func_decl* cns = cnstrs[i];
             func_decl* rec = get_constructor_recognizer(cns);
             strm << "  " << cns->get_name() << " :: " << rec->get_name() << " :: ";
-            ptr_vector<func_decl> const * accs = get_constructor_accessors(cns);
-            for (unsigned j = 0; j < accs->size(); ++j) {
-                func_decl* acc = (*accs)[j];
+            ptr_vector<func_decl> const & accs = get_constructor_accessors(cns);
+            for (unsigned j = 0; j < accs.size(); ++j) {
+                func_decl* acc = accs[j];
                 sort* s1 = acc->get_range();
                 strm << "(" << acc->get_name() << ": " << s1->get_name() << ") "; 
                 if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) {
@@ -1090,3 +1091,5 @@ bool datatype_util::is_constructor_of(unsigned num_params, parameter const* para
         params[1] == f->get_parameter(1);
 }
 
+
+#endif
diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h
index dcd352471..b9c2602ef 100644
--- a/src/ast/datatype_decl_plugin.h
+++ b/src/ast/datatype_decl_plugin.h
@@ -16,9 +16,15 @@ Author:
 Revision History:
 
 --*/
+// define DATATYPE_V2
+#ifdef DATATYPE_V2
+#include "ast/datatype_decl_plugin2.h"
+#else
+
 #ifndef DATATYPE_DECL_PLUGIN_H_
 #define DATATYPE_DECL_PLUGIN_H_
 
+
 #include "ast/ast.h"
 #include "util/tptr.h"
 #include "util/buffer.h"
@@ -210,7 +216,7 @@ public:
     bool is_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); }
     bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
     bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
-    ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
+    ptr_vector<func_decl> const & get_datatype_constructors(sort * ty);
     unsigned get_datatype_num_constructors(sort * ty) { 
         SASSERT(is_datatype(ty));
         unsigned tid = ty->get_parameter(1).get_int();
@@ -230,7 +236,7 @@ public:
     unsigned get_recognizer_constructor_idx(func_decl * f) const { SASSERT(is_recognizer(f)); return f->get_parameter(1).get_int(); }
     func_decl * get_non_rec_constructor(sort * ty);
     func_decl * get_constructor_recognizer(func_decl * constructor);
-    ptr_vector<func_decl> const * get_constructor_accessors(func_decl * constructor);
+    ptr_vector<func_decl> const & get_constructor_accessors(func_decl * constructor);
     func_decl * get_accessor_constructor(func_decl * accessor);
     func_decl * get_recognizer_constructor(func_decl * recognizer);
     family_id get_family_id() const { return m_family_id; }
@@ -245,3 +251,4 @@ public:
 
 #endif /* DATATYPE_DECL_PLUGIN_H_ */
 
+#endif /* DATATYPE_V2 */
diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 8233da7d9..2fc62af31 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -22,6 +22,7 @@ Revision History:
 #include "ast/ast_smt2_pp.h"
 
 
+#ifdef DATATYPE_V2
 namespace datatype {
 
     void accessor::fix_range(sort_ref_vector const& dts) {
@@ -49,6 +50,10 @@ namespace datatype {
     def const& accessor::get_def() const { return m_constructor->get_def(); }
     util& accessor::u() const { return m_constructor->u(); }
 
+    constructor::~constructor() {
+        for (accessor* a : m_accessors) dealloc(a);
+        m_accessors.reset();
+    }
     util& constructor::u() const { return m_def->u(); }
 
     func_decl_ref constructor::instantiate(sort_ref_vector const& ps) const {
@@ -164,7 +169,7 @@ namespace datatype {
                         sort* r = to_sort(parameters[i].get_ast());
                         S.insert(d->params()[i], r->get_num_elements()); 
                     }
-                    sort_size ts = d->sort_size()->fold(S);
+                    sort_size ts = d->sort_size()->eval(S);
                     s->set_num_elements(ts);
                 }
                 return s;
@@ -278,7 +283,9 @@ namespace datatype {
 
         def& plugin::add(symbol const& name, unsigned n, sort * const * params) {
             ast_manager& m = *m_manager;
-            def* d = alloc(def, m, u(), name, m_class_id, n, params);
+            def* d = 0;
+            if (m_defs.find(name, d)) dealloc(d);
+            d = alloc(def, m, u(), name, m_class_id, n, params);
             m_defs.insert(name, d);
             m_def_block.push_back(name);
             return *d;
@@ -895,3 +902,4 @@ namespace datatype {
         }
     }
 }
+#endif
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index c540b7c1c..c8aa042f1 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -24,17 +24,12 @@ Revision History:
 #include "util/symbol_table.h"
 #include "util/obj_hashtable.h"
 
-namespace datatype {
-
-    class util;
-    class def;
-    class accessor;
-    class constructor;
+#ifdef DATATYPE_V2
 
     enum sort_kind {
         DATATYPE_SORT
     };
-    
+   
     enum op_kind {
         OP_DT_CONSTRUCTOR,
         OP_DT_RECOGNISER,
@@ -43,6 +38,14 @@ namespace datatype {
         LAST_DT_OP
     };
 
+namespace datatype {
+
+    class util;
+    class def;
+    class accessor;
+    class constructor;
+ 
+
     class accessor {
         symbol   m_name;
         sort*    m_range;
@@ -109,7 +112,7 @@ namespace datatype {
             static size* mk_power(size* a1, size* a2);
             
             virtual size* subst(obj_map<sort, size*>& S) = 0;
-            virtual sort_size fold(obj_map<sort, sort_size> const& S) = 0;
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) = 0;
             
         };
         struct offset : public size {
@@ -117,16 +120,16 @@ namespace datatype {
             offset(sort_size const& s): m_offset(s) {}
             virtual ~offset() {}
             virtual size* subst(obj_map<sort,size*>& S) { return this; }
-            virtual sort_size fold(obj_map<sort, sort_size> const& S) { return m_offset; }
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { return m_offset; }
         };
         struct plus : public size {
             size* m_arg1, *m_arg2;
             plus(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref();}
             virtual ~plus() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
             virtual size* subst(obj_map<sort,size*>& S) { return mk_plus(m_arg1->subst(S), m_arg2->subst(S)); }
-            virtual sort_size fold(obj_map<sort, sort_size> const& S) { 
-                sort_size s1 = m_arg1->fold(S);
-                sort_size s2 = m_arg2->fold(S);
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { 
+                sort_size s1 = m_arg1->eval(S);
+                sort_size s2 = m_arg2->eval(S);
                 if (s1.is_infinite()) return s1;
                 if (s2.is_infinite()) return s2;
                 if (s1.is_very_big()) return s1;
@@ -140,9 +143,9 @@ namespace datatype {
             times(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); }
             virtual ~times() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
             virtual size* subst(obj_map<sort,size*>& S) { return mk_times(m_arg1->subst(S), m_arg2->subst(S)); }
-            virtual sort_size fold(obj_map<sort, sort_size> const& S) { 
-                sort_size s1 = m_arg1->fold(S);
-                sort_size s2 = m_arg2->fold(S);
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { 
+                sort_size s1 = m_arg1->eval(S);
+                sort_size s2 = m_arg2->eval(S);
                 if (s1.is_infinite()) return s1;
                 if (s2.is_infinite()) return s2;
                 if (s1.is_very_big()) return s1;
@@ -156,9 +159,9 @@ namespace datatype {
             power(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); }
             virtual ~power() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
             virtual size* subst(obj_map<sort,size*>& S) { return mk_power(m_arg1->subst(S), m_arg2->subst(S)); }
-            virtual sort_size fold(obj_map<sort, sort_size> const& S) { 
-                sort_size s1 = m_arg1->fold(S);
-                sort_size s2 = m_arg2->fold(S);
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { 
+                sort_size s1 = m_arg1->eval(S);
+                sort_size s2 = m_arg2->eval(S);
                 // s1^s2
                 if (s1.is_infinite()) return s1;
                 if (s2.is_infinite()) return s2;
@@ -176,7 +179,7 @@ namespace datatype {
             sparam(sort_ref& p): m_param(p) {}
             virtual ~sparam() {}
             virtual size* subst(obj_map<sort,size*>& S) { return S[m_param]; }
-            virtual sort_size fold(obj_map<sort, sort_size> const& S) { return S[m_param]; }
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { return S[m_param]; }
         };
     };
 
@@ -201,6 +204,8 @@ namespace datatype {
         {}
         ~def() {
             if (m_sort_size) m_sort_size->dec_ref();
+            for (constructor* c : m_constructors) dealloc(c);
+            m_constructors.reset();
         }
         void add(constructor* c) {
             m_constructors.push_back(c);
@@ -361,5 +366,36 @@ namespace datatype {
 
 };
 
+#ifdef DATATYPE_V2
+typedef datatype::accessor accessor_decl;
+typedef datatype::constructor constructor_decl;
+typedef datatype::def datatype_decl;
+typedef datatype::decl::plugin datatype_decl_plugin;
+typedef datatype::util datatype_util;
+
+class type_ref {
+    void * m_data;
+public:
+    type_ref():m_data(TAG(void *, static_cast<void*>(0), 1)) {}
+    type_ref(int idx):m_data(BOXINT(void *, idx)) {}
+    type_ref(sort * s):m_data(TAG(void *, s, 1)) {}
+    
+    bool is_idx() const { return GET_TAG(m_data) == 0; }
+    bool is_sort() const { return GET_TAG(m_data) == 1; }
+    sort * get_sort() const { return UNTAG(sort *, m_data); }
+    int get_idx() const { return UNBOXINT(m_data); }
+};
+
+inline accessor_decl * mk_accessor_decl(symbol const & n, type_ref const & t) {
+    if (t.is_idx()) {
+        return alloc(accessor_decl, n, t.get_idx());
+    }
+    else {
+        return alloc(accessor_decl, n, t.get_sort());
+    }
+}
+#endif
+
 #endif /* DATATYPE_DECL_PLUGIN_H_ */
 
+#endif DATATYPE_V2
diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp
index e000f43df..bf509aba5 100644
--- a/src/ast/decl_collector.cpp
+++ b/src/ast/decl_collector.cpp
@@ -28,9 +28,9 @@ void decl_collector::visit_sort(sort * n) {
 
         unsigned num_cnstr = m_dt_util.get_datatype_num_constructors(n);
         for (unsigned i = 0; i < num_cnstr; i++) {
-            func_decl * cnstr = m_dt_util.get_datatype_constructors(n)->get(i);
+            func_decl * cnstr = m_dt_util.get_datatype_constructors(n).get(i);
             m_decls.push_back(cnstr);
-            ptr_vector<func_decl> const & cnstr_acc = *m_dt_util.get_constructor_accessors(cnstr);
+            ptr_vector<func_decl> const & cnstr_acc = m_dt_util.get_constructor_accessors(cnstr);
             unsigned num_cas = cnstr_acc.size();
             for (unsigned j = 0; j < num_cas; j++) {
                 func_decl * accsr = cnstr_acc.get(j);
diff --git a/src/ast/rewriter/datatype_rewriter.cpp b/src/ast/rewriter/datatype_rewriter.cpp
index d38c9e476..8746fab86 100644
--- a/src/ast/rewriter/datatype_rewriter.cpp
+++ b/src/ast/rewriter/datatype_rewriter.cpp
@@ -47,11 +47,11 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr
         func_decl * c_decl = a->get_decl();
         if (c_decl != m_util.get_accessor_constructor(f))
             return BR_FAILED;
-        ptr_vector<func_decl> const * acc = m_util.get_constructor_accessors(c_decl);
-        SASSERT(acc && acc->size() == a->get_num_args());
-        unsigned num = acc->size();
+        ptr_vector<func_decl> const & acc = m_util.get_constructor_accessors(c_decl);
+        SASSERT(acc.size() == a->get_num_args());
+        unsigned num = acc.size();
         for (unsigned i = 0; i < num; ++i) {
-            if (f == (*acc)[i]) {
+            if (f == acc[i]) {
                 // found it.
                 result = a->get_arg(i);
                 return BR_DONE;
@@ -70,13 +70,13 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr
             result = a;
             return BR_DONE;
         }
-        ptr_vector<func_decl> const * acc = m_util.get_constructor_accessors(c_decl);
-        SASSERT(acc && acc->size() == a->get_num_args());
-        unsigned num = acc->size();
+        ptr_vector<func_decl> const & acc = m_util.get_constructor_accessors(c_decl);
+        SASSERT(acc.size() == a->get_num_args());
+        unsigned num = acc.size();
         ptr_buffer<expr> new_args;
         for (unsigned i = 0; i < num; ++i) {
             
-            if (f == (*acc)[i]) {
+            if (f == acc[i]) {
                 new_args.push_back(args[1]);
             }
             else {
diff --git a/src/ast/rewriter/enum2bv_rewriter.cpp b/src/ast/rewriter/enum2bv_rewriter.cpp
index eb6b195f0..b2ecbcc24 100644
--- a/src/ast/rewriter/enum2bv_rewriter.cpp
+++ b/src/ast/rewriter/enum2bv_rewriter.cpp
@@ -130,7 +130,7 @@ struct enum2bv_rewriter::imp {
                     m_imp.m_bounds.push_back(m_bv.mk_ule(result, m_bv.mk_numeral(nc-1, bv_size)));
                 }                
                 expr_ref f_def(m);
-                ptr_vector<func_decl> const& cs = *m_dt.get_datatype_constructors(s);
+                ptr_vector<func_decl> const& cs = m_dt.get_datatype_constructors(s);
                 f_def = m.mk_const(cs[nc-1]);
                 for (unsigned i = nc - 1; i > 0; ) {
                     --i;
diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp
index 8b1a89f3f..109fe1718 100644
--- a/src/cmd_context/cmd_context.cpp
+++ b/src/cmd_context/cmd_context.cpp
@@ -1954,19 +1954,19 @@ cmd_context::dt_eh::~dt_eh() {
 
 void cmd_context::dt_eh::operator()(sort * dt, pdecl* pd) {
     TRACE("new_dt_eh", tout << "new datatype: "; m_owner.pm().display(tout, dt); tout << "\n";);
-    ptr_vector<func_decl> const * constructors = m_dt_util.get_datatype_constructors(dt);
-    unsigned num_constructors = constructors->size();
+    ptr_vector<func_decl> const & constructors = m_dt_util.get_datatype_constructors(dt);
+    unsigned num_constructors = constructors.size();
     for (unsigned j = 0; j < num_constructors; j++) {
-        func_decl * c = constructors->get(j);
+        func_decl * c = constructors[j];
         m_owner.insert(c);
         TRACE("new_dt_eh", tout << "new constructor: " << c->get_name() << "\n";);
         func_decl * r = m_dt_util.get_constructor_recognizer(c);
         m_owner.insert(r);
         TRACE("new_dt_eh", tout << "new recognizer: " << r->get_name() << "\n";);
-        ptr_vector<func_decl> const * accessors = m_dt_util.get_constructor_accessors(c);
-        unsigned num_accessors = accessors->size();
+        ptr_vector<func_decl> const & accessors = m_dt_util.get_constructor_accessors(c);
+        unsigned num_accessors = accessors.size();
         for (unsigned k = 0; k < num_accessors; k++) {
-            func_decl * a = accessors->get(k);
+            func_decl * a = accessors[k];
             m_owner.insert(a);
             TRACE("new_dt_eh", tout << "new accessor: " << a->get_name() << "\n";);
         }
diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h
index ea0e64e4f..51d673ba5 100644
--- a/src/muz/base/dl_rule.h
+++ b/src/muz/base/dl_rule.h
@@ -65,7 +65,7 @@ namespace datalog {
             else if (m_dt.is_accessor(n)) {
                 sort* s = m.get_sort(n->get_arg(0));
                 SASSERT(m_dt.is_datatype(s));
-                if (m_dt.get_datatype_constructors(s)->size() > 1) {
+                if (m_dt.get_datatype_constructors(s).size() > 1) {
                     m_found = true;
                     m_func = n->get_decl();
                 }
diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp
index 21317a07c..75487bbfb 100644
--- a/src/muz/base/rule_properties.cpp
+++ b/src/muz/base/rule_properties.cpp
@@ -191,7 +191,7 @@ void rule_properties::operator()(app* n) {
     else if (m_dt.is_accessor(n)) {
         sort* s = m.get_sort(n->get_arg(0));
         SASSERT(m_dt.is_datatype(s));
-        if (m_dt.get_datatype_constructors(s)->size() > 1) {
+        if (m_dt.get_datatype_constructors(s).size() > 1) {
             m_uninterp_funs.insert(n->get_decl(), m_rule);
         }
     }
diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp
index 6d2f88019..b9dbe83ca 100644
--- a/src/muz/bmc/dl_bmc_engine.cpp
+++ b/src/muz/bmc/dl_bmc_engine.cpp
@@ -808,7 +808,7 @@ namespace datalog {
             datatype_util dtu(m);
             ptr_vector<sort> sorts;
             func_decl* p = r.get_decl();
-            ptr_vector<func_decl> const& succs  = *dtu.get_datatype_constructors(m.get_sort(path));
+            ptr_vector<func_decl> const& succs  = dtu.get_datatype_constructors(m.get_sort(path));
             // populate substitution of bound variables.
             r.get_vars(m, sorts);
             sub.reset();
@@ -871,8 +871,8 @@ namespace datalog {
                 path_var  = m.mk_var(0, m_path_sort);
                 trace_var = m.mk_var(1, pred_sort);
                 // sort* sorts[2] = { pred_sort, m_path_sort };
-                ptr_vector<func_decl> const& cnstrs = *dtu.get_datatype_constructors(pred_sort);
-                ptr_vector<func_decl> const& succs  = *dtu.get_datatype_constructors(m_path_sort);
+                ptr_vector<func_decl> const& cnstrs = dtu.get_datatype_constructors(pred_sort);
+                ptr_vector<func_decl> const& succs  = dtu.get_datatype_constructors(m_path_sort);
                 SASSERT(cnstrs.size() == rls.size());
                 pred = m.mk_app(mk_predicate(p), trace_var.get(), path_var.get());
                 for (unsigned i = 0; i < rls.size(); ++i) {
@@ -1039,8 +1039,8 @@ namespace datalog {
             sort* trace_sort = m.get_sort(trace);
             func_decl* p = m_sort2pred.find(trace_sort);
             datalog::rule_vector const& rules = b.m_rules.get_predicate_rules(p);
-            ptr_vector<func_decl> const& cnstrs = *dtu.get_datatype_constructors(trace_sort);
-            ptr_vector<func_decl> const& succs  = *dtu.get_datatype_constructors(m_path_sort);
+            ptr_vector<func_decl> const& cnstrs = dtu.get_datatype_constructors(trace_sort);
+            ptr_vector<func_decl> const& succs  = dtu.get_datatype_constructors(m_path_sort);
             for (unsigned i = 0; i < cnstrs.size(); ++i) {
                 if (trace->get_decl() == cnstrs[i]) {
                     svector<std::pair<unsigned, unsigned> > positions;
diff --git a/src/muz/pdr/pdr_prop_solver.cpp b/src/muz/pdr/pdr_prop_solver.cpp
index 3055985f4..1bca8e925 100644
--- a/src/muz/pdr/pdr_prop_solver.cpp
+++ b/src/muz/pdr/pdr_prop_solver.cpp
@@ -135,7 +135,7 @@ namespace pdr {
                     func_decl* f = to_app(val)->get_decl();
                     func_decl* r = dt.get_constructor_recognizer(f);
                     conjs[i] = m.mk_app(r, c);
-                    ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f);
+                    ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(f);
                     for (unsigned j = 0; j < acc.size(); ++j) {
                         conjs.push_back(m.mk_eq(apply_accessor(acc, j, f, c), to_app(val)->get_arg(j)));
                     }
diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp
index a277c9ed6..14d8899c5 100644
--- a/src/muz/spacer/spacer_util.cpp
+++ b/src/muz/spacer/spacer_util.cpp
@@ -711,7 +711,7 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs)
                 func_decl* f = to_app(val)->get_decl();
                 func_decl* r = dt.get_constructor_recognizer(f);
                 conjs[i] = m.mk_app(r, c);
-                ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f);
+                ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(f);
                 for (unsigned j = 0; j < acc.size(); ++j) {
                     conjs.push_back(m.mk_eq(apply_accessor(m, acc, j, f, c), to_app(val)->get_arg(j)));
                 }
diff --git a/src/qe/qe_datatype_plugin.cpp b/src/qe/qe_datatype_plugin.cpp
index 4178c3af3..eff230ffe 100644
--- a/src/qe/qe_datatype_plugin.cpp
+++ b/src/qe/qe_datatype_plugin.cpp
@@ -262,7 +262,7 @@ namespace qe {
             }
             func_decl* c = a->get_decl();
             func_decl* r = m_util.get_constructor_recognizer(c);
-            ptr_vector<func_decl> const & acc = *m_util.get_constructor_accessors(c);
+            ptr_vector<func_decl> const & acc = m_util.get_constructor_accessors(c);
             SASSERT(acc.size() == a->get_num_args());
             //
             // It suffices to solve just the first available equality.
@@ -379,7 +379,7 @@ namespace qe {
                 return false;
             }
             func_decl* c = l->get_decl();
-            ptr_vector<func_decl> const& acc = *m_util.get_constructor_accessors(c);
+            ptr_vector<func_decl> const& acc = m_util.get_constructor_accessors(c);
             func_decl* rec = m_util.get_constructor_recognizer(c);
             expr_ref_vector conj(m);
             conj.push_back(m.mk_app(rec, r));
@@ -626,7 +626,7 @@ namespace qe {
             // If 'x' does not yet have a recognizer, then branch according to recognizers.
             // 
             if (!has_recognizer(x, fml, r, c)) {
-                c = (*m_datatype_util.get_datatype_constructors(s))[vl.get_unsigned()];
+                c = m_datatype_util.get_datatype_constructors(s)[vl.get_unsigned()];
                 r = m_datatype_util.get_constructor_recognizer(c);
                 app* is_c = m.mk_app(r, x);                
                 // assert v => r(x)            
@@ -673,7 +673,7 @@ namespace qe {
             // Introduce auxiliary variable to eliminate.
             // 
             if (!has_recognizer(x, fml, r, c)) {
-                c = (*m_datatype_util.get_datatype_constructors(s))[vl.get_unsigned()];
+                c = m_datatype_util.get_datatype_constructors(s)[vl.get_unsigned()];
                 r = m_datatype_util.get_constructor_recognizer(c);
                 app* is_c = m.mk_app(r, x);                
                 fml = m.mk_and(is_c, fml);
@@ -774,7 +774,7 @@ namespace qe {
                 return;
             }
             
-            c = (*m_datatype_util.get_datatype_constructors(s))[vl.get_unsigned()];
+            c = m_datatype_util.get_datatype_constructors(s)[vl.get_unsigned()];
             r = m_datatype_util.get_constructor_recognizer(c);
             app* is_c = m.mk_app(r, x);
             
@@ -794,7 +794,7 @@ namespace qe {
             else {
                 SASSERT(vl.is_unsigned());
                 SASSERT(vl.get_unsigned() < m_datatype_util.get_datatype_num_constructors(s));
-                c = (*m_datatype_util.get_datatype_constructors(s))[vl.get_unsigned()];
+                c = m_datatype_util.get_datatype_constructors(s)[vl.get_unsigned()];
             }
             subst_constructor(x, c, fml, def);                
         }
diff --git a/src/qe/qe_datatypes.cpp b/src/qe/qe_datatypes.cpp
index db1e6ec85..f16bdda59 100644
--- a/src/qe/qe_datatypes.cpp
+++ b/src/qe/qe_datatypes.cpp
@@ -75,7 +75,7 @@ namespace qe {
             app_ref arg(m);
             SASSERT(dt.is_constructor(m_val));
             func_decl* f = m_val->get_decl();
-            ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f);
+            ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(f);
             for (unsigned i = 0; i < acc.size(); ++i) {
                 arg = m.mk_fresh_const(acc[i]->get_name().str().c_str(), acc[i]->get_range());
                 model.register_decl(arg->get_decl(), m_val->get_arg(i));
@@ -152,7 +152,7 @@ namespace qe {
             }
             func_decl* c = a->get_decl();
             func_decl* rec = dt.get_constructor_recognizer(c);
-            ptr_vector<func_decl> const & acc = *dt.get_constructor_accessors(c);
+            ptr_vector<func_decl> const & acc = dt.get_constructor_accessors(c);
             SASSERT(acc.size() == a->get_num_args());
             //
             // It suffices to solve just the first available equality.
@@ -230,7 +230,7 @@ namespace qe {
                 return false;
             }
             func_decl* c = to_app(l)->get_decl();
-            ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(c);
+            ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(c);
             if (!is_app_of(r, c)) {
                 lits.push_back(m.mk_app(dt.get_constructor_recognizer(c), r));
             }
diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp
index 257331161..10af3be25 100644
--- a/src/qe/qe_lite.cpp
+++ b/src/qe/qe_lite.cpp
@@ -671,7 +671,7 @@ namespace eq {
             else {
                 func_decl* rec = dt.get_constructor_recognizer(d);
                 conjs.push_back(m.mk_app(rec, r));
-                ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(d);
+                ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(d);
                 for (unsigned i = 0; i < acc.size(); ++i) {
                     conjs.push_back(m.mk_eq(c->get_arg(i), m.mk_app(acc[i], r)));
                 }
diff --git a/src/smt/proto_model/datatype_factory.cpp b/src/smt/proto_model/datatype_factory.cpp
index d738f2cbd..653ef034a 100644
--- a/src/smt/proto_model/datatype_factory.cpp
+++ b/src/smt/proto_model/datatype_factory.cpp
@@ -88,8 +88,8 @@ expr * datatype_factory::get_almost_fresh_value(sort * s) {
     // Traverse constructors, and try to invoke get_fresh_value of one of the arguments (if the argument is not a sibling datatype of s).
     // If the argumet is a sibling datatype of s, then
     // use get_last_fresh_value.
-    ptr_vector<func_decl> const * constructors = m_util.get_datatype_constructors(s);
-    for (func_decl * constructor : *constructors) {
+    ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(s);
+    for (func_decl * constructor : constructors) {
         expr_ref_vector args(m_manager);
         bool found_fresh_arg = false;
         bool recursive       = false;
@@ -151,8 +151,8 @@ expr * datatype_factory::get_fresh_value(sort * s) {
     // Traverse constructors, and try to invoke get_fresh_value of one of the 
     // arguments (if the argument is not a sibling datatype of s).
     // Two datatypes are siblings if they were defined together in the same mutually recursive definition.
-    ptr_vector<func_decl> const * constructors = m_util.get_datatype_constructors(s);
-    for (func_decl * constructor : *constructors) {
+    ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(s);
+    for (func_decl * constructor : constructors) {
         expr_ref_vector args(m_manager);
         bool found_fresh_arg = false;
         unsigned num            = constructor->get_arity();
@@ -189,8 +189,8 @@ expr * datatype_factory::get_fresh_value(sort * s) {
         while(true) {
             ++num_iterations;
             TRACE("datatype_factory", tout << mk_pp(get_last_fresh_value(s), m_manager) << "\n";);
-            ptr_vector<func_decl> const * constructors = m_util.get_datatype_constructors(s);
-            for (func_decl * constructor : *constructors) {
+            ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(s);
+            for (func_decl * constructor : constructors) {
                 expr_ref_vector args(m_manager);
                 bool found_sibling   = false;
                 unsigned num         = constructor->get_arity();
diff --git a/src/smt/smt_value_sort.cpp b/src/smt/smt_value_sort.cpp
index 56768b91a..3eeb3461d 100644
--- a/src/smt/smt_value_sort.cpp
+++ b/src/smt/smt_value_sort.cpp
@@ -52,7 +52,7 @@ namespace smt {
                 // simple
             }
             else if (data.is_datatype(s)) {
-                ptr_vector<func_decl> const& cs = *data.get_datatype_constructors(s);
+                ptr_vector<func_decl> const& cs = data.get_datatype_constructors(s);
                 for (unsigned i = 0; i < cs.size(); ++i) {
                     func_decl* f = cs[i];
                     for (unsigned j = 0; j < f->get_arity(); ++j) {
diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp
index beb1acf63..33b4b194d 100644
--- a/src/smt/theory_datatype.cpp
+++ b/src/smt/theory_datatype.cpp
@@ -97,12 +97,9 @@ namespace smt {
         SASSERT(m_util.is_datatype(get_manager().get_sort(n->get_owner())));
         ast_manager & m = get_manager();
         ptr_vector<expr> args;
-        ptr_vector<func_decl> const * accessors   = m_util.get_constructor_accessors(c);
-        SASSERT(c->get_arity() == accessors->size());
-        ptr_vector<func_decl>::const_iterator it  = accessors->begin();
-        ptr_vector<func_decl>::const_iterator end = accessors->end();
-        for (; it != end; ++it) {
-            func_decl * d = *it;
+        ptr_vector<func_decl> const & accessors   = m_util.get_constructor_accessors(c);
+        SASSERT(c->get_arity() == accessors.size());
+        for (func_decl * d : accessors) {
             SASSERT(d->get_arity() == 1);
             expr * acc    = m.mk_app(d, n->get_owner());
             args.push_back(acc);
@@ -123,15 +120,14 @@ namespace smt {
         SASSERT(is_constructor(n));
         ast_manager & m   = get_manager();
         func_decl * d     = n->get_decl();
-        ptr_vector<func_decl> const * accessors   = m_util.get_constructor_accessors(d);
-        SASSERT(n->get_num_args() == accessors->size());
-        ptr_vector<func_decl>::const_iterator it  = accessors->begin();
-        ptr_vector<func_decl>::const_iterator end = accessors->end();
-        for (unsigned i = 0; it != end; ++it, ++i) {
-            func_decl * acc   = *it;
+        ptr_vector<func_decl> const & accessors   = m_util.get_constructor_accessors(d);
+        SASSERT(n->get_num_args() == accessors.size());
+        unsigned i = 0;
+        for (func_decl * acc : accessors) {
             app * acc_app     = m.mk_app(acc, n->get_owner());
             enode * arg       = n->get_arg(i);
             assert_eq_axiom(arg, acc_app, null_literal);
+            ++i;
         }
     }
 
@@ -172,15 +168,12 @@ namespace smt {
         func_decl * acc  = to_func_decl(upd->get_parameter(0).get_ast());
         func_decl * con  = m_util.get_accessor_constructor(acc);
         func_decl * rec  = m_util.get_constructor_recognizer(con);
-        ptr_vector<func_decl> const * accessors   = m_util.get_constructor_accessors(con);
-        ptr_vector<func_decl>::const_iterator it  = accessors->begin();
-        ptr_vector<func_decl>::const_iterator end = accessors->end();
+        ptr_vector<func_decl> const & accessors   = m_util.get_constructor_accessors(con);
         app_ref rec_app(m.mk_app(rec, arg1), m);
         ctx.internalize(rec_app, false);
         literal is_con(ctx.get_bool_var(rec_app));
-        for (; it != end; ++it) {
+        for (func_decl* acc1 : accessors) {
             enode* arg;
-            func_decl * acc1   = *it;
             if (acc1 == acc) {
                 arg = n->get_arg(1);
             }
@@ -215,7 +208,7 @@ namespace smt {
             ast_manager & m = get_manager();
             sort * s      = m.get_sort(n->get_owner());
             if (m_util.get_datatype_num_constructors(s) == 1) {
-                func_decl * c = m_util.get_datatype_constructors(s)->get(0);
+                func_decl * c = m_util.get_datatype_constructors(s)[0];
                 assert_is_constructor_axiom(n, c, null_literal);
             }
             else {
@@ -716,8 +709,8 @@ namespace smt {
             enode * r = d->m_recognizers[unassigned_idx];
             literal consequent;
             if (!r) {
-                ptr_vector<func_decl> const * constructors = m_util.get_datatype_constructors(dt);
-                func_decl * rec = m_util.get_constructor_recognizer(constructors->get(unassigned_idx));
+                ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(dt);
+                func_decl * rec = m_util.get_constructor_recognizer(constructors[unassigned_idx]);
                 app * rec_app   = get_manager().mk_app(rec, n->get_owner());
                 ctx.internalize(rec_app, false);
                 consequent = literal(ctx.get_bool_var(rec_app));
@@ -781,9 +774,9 @@ namespace smt {
                 for (unsigned idx = 0; it != end; ++it, ++idx) {
                     enode * curr = *it;
                     if (curr == 0) {
-                        ptr_vector<func_decl> const * constructors = m_util.get_datatype_constructors(s);
+                        ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(s);
                         // found empty slot...
-                        r = m_util.get_constructor_recognizer(constructors->get(idx));
+                        r = m_util.get_constructor_recognizer(constructors[idx]);
                         break;
                     }
                     else if (!ctx.is_relevant(curr)) { 
diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp
index 73adabb6f..a13cd54d8 100644
--- a/src/tactic/core/elim_uncnstr_tactic.cpp
+++ b/src/tactic/core/elim_uncnstr_tactic.cpp
@@ -174,11 +174,8 @@ class elim_uncnstr_tactic : public tactic {
                 if (fid == m_dt_util.get_family_id()) {
                     // In the current implementation, I only handle the case where
                     // the datatype has a recursive constructor.
-                    ptr_vector<func_decl> const * constructors = m_dt_util.get_datatype_constructors(s);
-                    ptr_vector<func_decl>::const_iterator it   = constructors->begin();
-                    ptr_vector<func_decl>::const_iterator end  = constructors->end();
-                    for (; it != end; ++it) {
-                        func_decl * constructor = *it;
+                    ptr_vector<func_decl> const & constructors = m_dt_util.get_datatype_constructors(s);
+                    for (func_decl * constructor : constructors) {
                         unsigned num    = constructor->get_arity();
                         unsigned target = UINT_MAX;
                         for (unsigned i = 0; i < num; i++) {
@@ -707,10 +704,10 @@ class elim_uncnstr_tactic : public tactic {
                         app * u;
                         if (!mk_fresh_uncnstr_var_for(f, num, args, u))
                             return u;
-                        ptr_vector<func_decl> const * accs = m_dt_util.get_constructor_accessors(c);
+                        ptr_vector<func_decl> const & accs = m_dt_util.get_constructor_accessors(c);
                         ptr_buffer<expr> new_args;
-                        for (unsigned i = 0; i < accs->size(); i++) {
-                            if (accs->get(i) == f) 
+                        for (unsigned i = 0; i < accs.size(); i++) {
+                            if (accs[i] == f) 
                                 new_args.push_back(u);
                             else
                                 new_args.push_back(m().get_some_value(c->get_domain(i)));
@@ -726,9 +723,9 @@ class elim_uncnstr_tactic : public tactic {
                             return u;
                         if (!m_mc)
                             return u;
-                        ptr_vector<func_decl> const * accs = m_dt_util.get_constructor_accessors(f);
+                        ptr_vector<func_decl> const & accs = m_dt_util.get_constructor_accessors(f);
                         for (unsigned i = 0; i < num; i++) {
-                            add_def(args[i], m().mk_app(accs->get(i), u));
+                            add_def(args[i], m().mk_app(accs[i], u));
                         }
                         return u;
                     }
diff --git a/src/tactic/portfolio/enum2bv_solver.cpp b/src/tactic/portfolio/enum2bv_solver.cpp
index 36a178c41..db7cebd6e 100644
--- a/src/tactic/portfolio/enum2bv_solver.cpp
+++ b/src/tactic/portfolio/enum2bv_solver.cpp
@@ -136,7 +136,7 @@ public:
             if (m.is_eq(b, u, v) && is_uninterp_const(u) && m_rewriter.bv2enum().find(to_app(u)->get_decl(), f) && bv.is_numeral(v, num, bvsize)) {
                 SASSERT(num.is_unsigned());
                 expr_ref head(m);
-                ptr_vector<func_decl> const& enums = *dt.get_datatype_constructors(f->get_range());
+                ptr_vector<func_decl> const& enums = dt.get_datatype_constructors(f->get_range());
                 if (enums.size() > num.get_unsigned()) {
                     head = m.mk_eq(m.mk_const(f), m.mk_const(enums[num.get_unsigned()]));
                     consequences[i] = m.mk_implies(a, head);
diff --git a/src/test/get_consequences.cpp b/src/test/get_consequences.cpp
index 9337dcee3..e8868c661 100644
--- a/src/test/get_consequences.cpp
+++ b/src/test/get_consequences.cpp
@@ -71,7 +71,7 @@ void test2() {
     sort* rgb = new_sorts[0].get();
 
     expr_ref x = mk_const(m, "x", rgb), y = mk_const(m, "y", rgb), z = mk_const(m, "z", rgb);
-    ptr_vector<func_decl> const& enums = *dtutil.get_datatype_constructors(rgb);
+    ptr_vector<func_decl> const& enums = dtutil.get_datatype_constructors(rgb);
     expr_ref r = expr_ref(m.mk_const(enums[0]), m);
     expr_ref g = expr_ref(m.mk_const(enums[1]), m);
     expr_ref b = expr_ref(m.mk_const(enums[2]), m);

From f12a4f04fdf5bcd16a120ffe1c1d64a5b4f02359 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Mon, 4 Sep 2017 09:28:40 -0700
Subject: [PATCH 54/74] aligning simplifier and rewriter for regression tests

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/api_datatype.cpp             | 10 +++--
 src/ast/datatype_decl_plugin.cpp     |  2 +-
 src/ast/datatype_decl_plugin.h       | 12 +++---
 src/ast/datatype_decl_plugin2.cpp    | 64 ++++++++++++++++++++++++++--
 src/ast/datatype_decl_plugin2.h      | 45 +++++++++++++------
 src/ast/rewriter/arith_rewriter.cpp  |  4 +-
 src/ast/rewriter/poly_rewriter.h     |  1 +
 src/ast/rewriter/poly_rewriter_def.h |  7 ++-
 src/cmd_context/pdecl.cpp            |  7 ++-
 src/cmd_context/pdecl.h              |  9 ++--
 src/muz/bmc/dl_bmc_engine.cpp        |  4 +-
 src/test/get_consequences.cpp        |  3 +-
 12 files changed, 125 insertions(+), 43 deletions(-)

diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp
index 39d147ed1..d670888ed 100644
--- a/src/api/api_datatype.cpp
+++ b/src/api/api_datatype.cpp
@@ -51,7 +51,7 @@ extern "C" {
         constructor_decl* constrs[1] = { mk_constructor_decl(to_symbol(name), recognizer, acc.size(), acc.c_ptr()) };
 
         {
-            datatype_decl * dt = mk_datatype_decl(to_symbol(name), 1, constrs);
+            datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 1, constrs);
             bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, 0, tuples);
             del_datatype_decl(dt);
 
@@ -113,7 +113,7 @@ extern "C" {
 
 
         {
-            datatype_decl * dt = mk_datatype_decl(to_symbol(name), n, constrs.c_ptr());
+            datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), n, constrs.c_ptr());
             bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, 0, sorts);
             del_datatype_decl(dt);
 
@@ -160,6 +160,7 @@ extern "C" {
         LOG_Z3_mk_list_sort(c, name, elem_sort, nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl);
         RESET_ERROR_CODE();
         ast_manager& m = mk_c(c)->m();
+        datatype_util& dt_util = mk_c(c)->dtutil();
         mk_c(c)->reset_last_result();
         datatype_util data_util(m);
         accessor_decl* head_tail[2] = {
@@ -174,7 +175,7 @@ extern "C" {
 
         sort_ref_vector sorts(m);
         {
-            datatype_decl * decl = mk_datatype_decl(to_symbol(name), 2, constrs);
+            datatype_decl * decl = mk_datatype_decl(dt_util, to_symbol(name), 2, constrs);
             bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &decl, 0, 0, sorts);
             del_datatype_decl(decl);
 
@@ -316,6 +317,7 @@ extern "C" {
                                            Z3_symbol name,
                                            unsigned num_constructors,
                                            Z3_constructor constructors[]) {
+        datatype_util& dt_util = mk_c(c)->dtutil();
         ptr_vector<constructor_decl> constrs;
         for (unsigned i = 0; i < num_constructors; ++i) {
             constructor* cn = reinterpret_cast<constructor*>(constructors[i]);
@@ -330,7 +332,7 @@ extern "C" {
             }
             constrs.push_back(mk_constructor_decl(cn->m_name, cn->m_tester, acc.size(), acc.c_ptr()));
         }
-        return mk_datatype_decl(to_symbol(name), num_constructors, constrs.c_ptr());
+        return mk_datatype_decl(dt_util, to_symbol(name), num_constructors, constrs.c_ptr());
     }
 
     Z3_sort Z3_API Z3_mk_datatype(Z3_context c,
diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp
index e2aa54236..1c090bc33 100644
--- a/src/ast/datatype_decl_plugin.cpp
+++ b/src/ast/datatype_decl_plugin.cpp
@@ -95,7 +95,7 @@ public:
     ptr_vector<constructor_decl> const & get_constructors() const { return m_constructors; }
 };
 
-datatype_decl * mk_datatype_decl(symbol const & n, unsigned num_constructors, constructor_decl * const * cs) {
+datatype_decl * mk_datatype_decl(datatype_util&, symbol const & n, unsigned num_constructors, constructor_decl * const * cs) {
     return alloc(datatype_decl, n, num_constructors, cs);
 }
 
diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h
index b9c2602ef..e3e1b7b10 100644
--- a/src/ast/datatype_decl_plugin.h
+++ b/src/ast/datatype_decl_plugin.h
@@ -16,7 +16,7 @@ Author:
 Revision History:
 
 --*/
-// define DATATYPE_V2
+//define DATATYPE_V2
 #ifdef DATATYPE_V2
 #include "ast/datatype_decl_plugin2.h"
 #else
@@ -79,14 +79,14 @@ class datatype_decl;
 class datatype_util;
 
 accessor_decl * mk_accessor_decl(symbol const & n, type_ref const & t);
-void del_accessor_decl(accessor_decl * d);
-void del_accessor_decls(unsigned num, accessor_decl * const * as);
+//void del_accessor_decl(accessor_decl * d);
+//void del_accessor_decls(unsigned num, accessor_decl * const * as);
 // Remark: the constructor becomes the owner of the accessor_decls
 constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * const * acs);
-void del_constructor_decl(constructor_decl * d);
-void del_constructor_decls(unsigned num, constructor_decl * const * cs);
+//void del_constructor_decl(constructor_decl * d);
+//void del_constructor_decls(unsigned num, constructor_decl * const * cs);
 // Remark: the datatype becomes the owner of the constructor_decls
-datatype_decl * mk_datatype_decl(symbol const & n, unsigned num_constructors, constructor_decl * const * cs);
+datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_constructors, constructor_decl * const * cs);
 void del_datatype_decl(datatype_decl * d);
 void del_datatype_decls(unsigned num, datatype_decl * const * ds);
 
diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 2fc62af31..c059907d5 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -1,5 +1,5 @@
 /*++
-Copyright (c) 2006 Microsoft Corporation
+Copyright (c) 2017 Microsoft Corporation
 
 Module Name:
 
@@ -11,18 +11,21 @@ Abstract:
 
 Author:
 
-    Leonardo de Moura (leonardo) 2008-01-10.
+    Nikolaj Bjorner (nbjorner) 2017-9-1 
 
 Revision History:
 
 --*/
+
+#include "ast/datatype_decl_plugin.h"
+
+#ifdef DATATYPE_V2
 #include "util/warning.h"
 #include "ast/datatype_decl_plugin2.h"
 #include "ast/array_decl_plugin.h"
 #include "ast/ast_smt2_pp.h"
 
 
-#ifdef DATATYPE_V2
 namespace datatype {
 
     void accessor::fix_range(sort_ref_vector const& dts) {
@@ -57,6 +60,7 @@ namespace datatype {
     util& constructor::u() const { return m_def->u(); }
 
     func_decl_ref constructor::instantiate(sort_ref_vector const& ps) const {
+        ast_manager& m = ps.get_manager();
         sort_ref_vector domain(m);
         for (accessor const* a : accessors()) {
             domain.push_back(a->instantiate(ps)->get_range());
@@ -281,6 +285,11 @@ namespace datatype {
             }
         }
 
+        def* plugin::mk(symbol const& name, unsigned n, sort * const * params) {
+            ast_manager& m = *m_manager;
+            return alloc(def, m, u(), name, m_class_id, n, params);
+        }
+
         def& plugin::add(symbol const& name, unsigned n, sort * const * params) {
             ast_manager& m = *m_manager;
             def* d = 0;
@@ -313,6 +322,21 @@ namespace datatype {
             u().compute_datatype_size_functions(m_def_block);
         }
 
+        bool plugin::mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts) {
+            begin_def_block();
+            for (unsigned i = 0; i < num_datatypes; ++i) {
+                if (m_defs.find(datatypes[i]->name(), d)) dealloc(d);
+                m_defs.insert(datatypes[i]->name(), datatypes[i]);
+                m_def_block.push_back(datatypes[i]->name());
+            }
+            end_def_block();            
+            sort_ref_vector ps(*m_manager);
+            for (symbol const& s : m_def_block) {                
+                new_sorts.push_back(m_defs[s]->instantiate(ps));
+            }
+            return true;
+        }
+
         void plugin::del(symbol const& s) {
             def* d = 0;
             if (m_defs.find(s, d)) dealloc(d);
@@ -705,7 +729,7 @@ namespace datatype {
         return d;
     }
 
-    func_decl * util::get_recognizer_constructor(func_decl * recognizer) {
+    func_decl * util::get_recognizer_constructor(func_decl * recognizer) const {
         SASSERT(is_recognizer(recognizer));
         return to_func_decl(recognizer->get_parameter(0).get_ast());
     }
@@ -856,6 +880,22 @@ namespace datatype {
         return 0;
     }
 
+    unsigned util::get_constructor_idx(func_decl * f) const {
+        unsigned idx = 0;
+        def const& d = get_def(f->get_range());
+        for (constructor* c : d) {
+            if (c->name() == f->get_name()) {
+                return idx;
+            }
+            ++idx;
+        }
+        UNREACHABLE();
+        return 0;
+    }
+    unsigned util::get_recognizer_constructor_idx(func_decl * f) const {
+        return get_constructor_idx(get_recognizer_constructor(f));
+    }
+
 
     /**
        \brief Two datatype sorts s1 and s2 are siblings if they were
@@ -870,6 +910,12 @@ namespace datatype {
         }
     }
 
+    unsigned util::get_datatype_num_constructors(sort * ty) {
+        def const& d = get_def(ty->get_name());
+        return d.constructors().size();
+    }
+
+
     void util::display_datatype(sort *s0, std::ostream& strm) {
         ast_mark mark;
         ptr_buffer<sort> todo;
@@ -902,4 +948,14 @@ namespace datatype {
         }
     }
 }
+
+datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_constructors, constructor_decl * const * cs) {
+    datatype::decl::plugin* p = u.get_plugin();
+    datatype::def( d = p->mk(n, 0, 0);
+    for (unsigned i = 0; i < num_constructors; ++i) {
+        d->add(cs[i]);
+    }
+    return d;
+}
+
 #endif
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index c8aa042f1..c79939a5b 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -1,5 +1,5 @@
 /*++
-Copyright (c) 2006 Microsoft Corporation
+Copyright (c) 2017 Microsoft Corporation
 
 Module Name:
 
@@ -11,10 +11,13 @@ Abstract:
 
 Author:
 
-    Leonardo de Moura (leonardo) 2008-01-09.
+    Nikolaj Bjorner (nbjorner) 2017-9-1 
 
 Revision History:
 
+    rewritten to support SMTLIB-2.6 parameters from
+     Leonardo de Moura (leonardo) 2008-01-09.
+
 --*/
 #ifndef DATATYPE_DECL_PLUGIN2_H_
 #define DATATYPE_DECL_PLUGIN2_H_
@@ -74,12 +77,11 @@ namespace datatype {
     };
 
     class constructor {
-        ast_manager&     m;
         symbol           m_name;
         ptr_vector<accessor> m_accessors;
         def*             m_def;
     public:
-        constructor(ast_manager& m, symbol n): m(m), m_name(n) {}
+        constructor(symbol n): m_name(n) {}
         ~constructor();
         void add(accessor* a) { m_accessors.push_back(a); a->attach(this); }
         symbol const& name() const { return m_name; }
@@ -262,8 +264,12 @@ namespace datatype {
 
             def& add(symbol const& name, unsigned n, sort * const * params);
 
+            def* mk(symbol const& name, unsigned n, sort * const * params);
+
             void del(symbol const& d);
 
+            bool mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts);
+
             def const& get_def(sort* s) const { return *(m_defs[datatype_name(s)]); }
             def& get_def(symbol const& s) { return *(m_defs[s]); }
 
@@ -299,9 +305,7 @@ namespace datatype {
         family_id     m_family_id;
         mutable decl::plugin* m_plugin;
 
-        
-        func_decl * get_constructor(sort * ty, unsigned c_id) const;
-        
+                
         obj_map<sort, ptr_vector<func_decl> *>      m_datatype2constructors;
         obj_map<sort, func_decl *>                  m_datatype2nonrec_constructor;
         obj_map<func_decl, ptr_vector<func_decl> *> m_constructor2accessors;
@@ -310,14 +314,13 @@ namespace datatype {
         obj_map<func_decl, func_decl *>             m_accessor2constructor;
         obj_map<sort, bool>                         m_is_recursive;
         obj_map<sort, bool>                         m_is_enum;
-        mutable obj_map<sort, bool>                         m_is_fully_interp;
+        mutable obj_map<sort, bool>                 m_is_fully_interp;
         mutable ast_ref_vector                      m_asts;
         ptr_vector<ptr_vector<func_decl> >          m_vectors;
         unsigned                                    m_start;
-        mutable ptr_vector<sort>                            m_fully_interp_trail;
+        mutable ptr_vector<sort>                    m_fully_interp_trail;
         
         func_decl * get_non_rec_constructor_core(sort * ty, ptr_vector<sort> & forbidden_set);
-        func_decl * get_constructor(sort * ty, unsigned c_id);
 
         friend class decl::plugin;
 
@@ -353,7 +356,7 @@ namespace datatype {
         func_decl * get_constructor_recognizer(func_decl * constructor);
         ptr_vector<func_decl> const & get_constructor_accessors(func_decl * constructor);
         func_decl * get_accessor_constructor(func_decl * accessor);
-        func_decl * get_recognizer_constructor(func_decl * recognizer);
+        func_decl * get_recognizer_constructor(func_decl * recognizer) const;
         family_id get_family_id() const { return m_family_id; }
         bool are_siblings(sort * s1, sort * s2);
         bool is_func_decl(op_kind k, unsigned num_params, parameter const* params, func_decl* f);
@@ -362,11 +365,12 @@ namespace datatype {
         void display_datatype(sort *s, std::ostream& strm);
         bool is_fully_interp(sort * s) const;
         sort_ref_vector datatype_params(sort * s) const;
+        unsigned get_constructor_idx(func_decl * f) const;
+        unsigned get_recognizer_constructor_idx(func_decl * f) const;
     };
 
 };
 
-#ifdef DATATYPE_V2
 typedef datatype::accessor accessor_decl;
 typedef datatype::constructor constructor_decl;
 typedef datatype::def datatype_decl;
@@ -394,7 +398,22 @@ inline accessor_decl * mk_accessor_decl(symbol const & n, type_ref const & t) {
         return alloc(accessor_decl, n, t.get_sort());
     }
 }
-#endif
+
+inline constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * * acs) {
+    constructor_decl* c = alloc(constructor_decl, n);
+    for (unsigned i = 0; i < num_accessors; ++i) {
+        c->add(acs[i]);
+    }
+    return c;
+}
+
+
+
+// Remark: the datatype becomes the owner of the constructor_decls
+datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_constructors, constructor_decl * const * cs);
+inline void del_datatype_decl(datatype_decl * d) {}
+inline void del_datatype_decls(unsigned num, datatype_decl * const * ds) {}
+
 
 #endif /* DATATYPE_DECL_PLUGIN_H_ */
 
diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp
index ff1894a18..fbfcabd78 100644
--- a/src/ast/rewriter/arith_rewriter.cpp
+++ b/src/ast/rewriter/arith_rewriter.cpp
@@ -455,7 +455,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
             st = BR_DONE;
         }
     }
-    if (is_numeral(arg2, a2) && is_neg_poly(arg1, new_arg1)) {
+    if (m_arith_lhs && is_numeral(arg2, a2) && is_neg_poly(arg1, new_arg1)) {
         a2.neg();
         new_arg2 = m_util.mk_numeral(a2, m_util.is_int(new_arg1));
         switch (kind) {
@@ -523,7 +523,7 @@ expr_ref arith_rewriter::neg_monomial(expr* e) const {
         }
         else {
             args.push_back(m_util.mk_numeral(rational(-1), m_util.is_int(e)));
-            args.append(to_app(e)->get_num_args(), to_app(e)->get_args());
+            args.push_back(e);
         }
     }
     else {
diff --git a/src/ast/rewriter/poly_rewriter.h b/src/ast/rewriter/poly_rewriter.h
index c00464383..23743399e 100644
--- a/src/ast/rewriter/poly_rewriter.h
+++ b/src/ast/rewriter/poly_rewriter.h
@@ -36,6 +36,7 @@ protected:
     bool                    m_sort_sums;
     bool                    m_hoist_mul;
     bool                    m_hoist_cmul;
+    bool                    m_ast_order;
 
     bool is_numeral(expr * n) const { return Config::is_numeral(n); }
     bool is_numeral(expr * n, numeral & r) const { return Config::is_numeral(n, r); }
diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h
index 8fda040b6..b52440393 100644
--- a/src/ast/rewriter/poly_rewriter_def.h
+++ b/src/ast/rewriter/poly_rewriter_def.h
@@ -18,7 +18,8 @@ Notes:
 --*/
 #include "ast/rewriter/poly_rewriter.h"
 #include "ast/rewriter/poly_rewriter_params.hpp"
-// include "ast/ast_lt.h"
+#include "ast/rewriter/arith_rewriter_params.hpp"
+#include "ast/ast_lt.h"
 #include "ast/ast_ll_pp.h"
 #include "ast/ast_smt2_pp.h"
 
@@ -33,6 +34,8 @@ void poly_rewriter<Config>::updt_params(params_ref const & _p) {
     m_som_blowup = p.som_blowup();
     if (!m_flat) m_som = false;
     if (m_som) m_hoist_mul = false;
+    arith_rewriter_params ap(_p);
+    m_ast_order  = !ap.arith_ineq_lhs();
 }
 
 template<typename Config>
@@ -485,6 +488,8 @@ void poly_rewriter<Config>::hoist_cmul(expr_ref_buffer & args) {
 
 template<typename Config>
 bool poly_rewriter<Config>::mon_lt::operator()(expr* e1, expr * e2) const {
+    if (rw.m_ast_order) 
+        return lt(e1,e2);
     return ordinal(e1) < ordinal(e2);
 }
 
diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp
index 034b067c7..a9ce0b69c 100644
--- a/src/cmd_context/pdecl.cpp
+++ b/src/cmd_context/pdecl.cpp
@@ -566,7 +566,8 @@ datatype_decl * pdatatype_decl::instantiate_decl(pdecl_manager & m, sort * const
     for (auto c : m_constructors) {
         cs.push_back(c->instantiate_decl(m, s));
     }
-    return mk_datatype_decl(m_name, cs.size(), cs.c_ptr());
+    datatype_util util(m.m());
+    return mk_datatype_decl(util, m_name, cs.size(), cs.c_ptr());
 }
 
 struct datatype_decl_buffer {
@@ -679,9 +680,7 @@ struct pdecl_manager::sort_info {
     }
     virtual ~sort_info() {}
     virtual unsigned obj_size() const { return sizeof(sort_info); }
-    virtual void finalize(pdecl_manager & m) {
-        m.dec_ref(m_decl);
-    }
+    virtual void finalize(pdecl_manager & m) { m.dec_ref(m_decl); }
     virtual void display(std::ostream & out, pdecl_manager const & m) const = 0;
     virtual format * pp(pdecl_manager const & m) const = 0;
 };
diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h
index 66e9cff53..e7fae8dd5 100644
--- a/src/cmd_context/pdecl.h
+++ b/src/cmd_context/pdecl.h
@@ -23,6 +23,7 @@ Revision History:
 #include "util/obj_hashtable.h"
 #include "util/dictionary.h"
 #include "ast/format.h"
+#include "ast/datatype_decl_plugin.h"
 
 class pdecl_manager;
 
@@ -139,10 +140,10 @@ public:
     virtual void display(std::ostream & out) const;
 };
 
-class datatype_decl_plugin;
-class datatype_decl;
-class constructor_decl;
-class accessor_decl;
+//class datatype_decl_plugin;
+//class datatype_decl;
+//class constructor_decl;
+//class accessor_decl;
 
 class pdatatypes_decl;
 class pdatatype_decl;
diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp
index b9dbe83ca..9109f6b3c 100644
--- a/src/muz/bmc/dl_bmc_engine.cpp
+++ b/src/muz/bmc/dl_bmc_engine.cpp
@@ -979,7 +979,7 @@ namespace datalog {
                     symbol is_name(_name.str().c_str());
                     cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr()));
                 }
-                dts.push_back(mk_datatype_decl(pred->get_name(), cnstrs.size(), cnstrs.c_ptr()));
+                dts.push_back(mk_datatype_decl(dtu, pred->get_name(), cnstrs.size(), cnstrs.c_ptr()));
             }
 
 
@@ -1027,7 +1027,7 @@ namespace datalog {
                     accs.push_back(mk_accessor_decl(name, tr));
                     cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr()));
                 }
-                dts.push_back(mk_datatype_decl(symbol("Path"), cnstrs.size(), cnstrs.c_ptr()));
+                dts.push_back(mk_datatype_decl(dtu, symbol("Path"), cnstrs.size(), cnstrs.c_ptr()));
                 VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, 0, new_sorts));
                 m_path_sort = new_sorts[0].get();
             }
diff --git a/src/test/get_consequences.cpp b/src/test/get_consequences.cpp
index e8868c661..229cbe834 100644
--- a/src/test/get_consequences.cpp
+++ b/src/test/get_consequences.cpp
@@ -65,9 +65,8 @@ void test2() {
     constructor_decl* G = mk_constructor_decl(symbol("G"), symbol("is-G"), 0, 0);
     constructor_decl* B = mk_constructor_decl(symbol("B"), symbol("is-B"), 0, 0);
     constructor_decl* constrs[3] = { R, G, B };
-    datatype_decl * enum_sort = mk_datatype_decl(symbol("RGB"), 3, constrs);
+    datatype_decl * enum_sort = mk_datatype_decl(dtutil, symbol("RGB"), 3, constrs);
     VERIFY(dt.mk_datatypes(1, &enum_sort, 0, 0, new_sorts));    
-    del_constructor_decls(3, constrs);
     sort* rgb = new_sorts[0].get();
 
     expr_ref x = mk_const(m, "x", rgb), y = mk_const(m, "y", rgb), z = mk_const(m, "z", rgb);

From 93474c0263502e9c7575d9c53ade310fccb78bba Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Mon, 4 Sep 2017 09:43:25 -0700
Subject: [PATCH 55/74] aligning simplifier and rewriter for regression tests

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/python/z3/z3.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py
index 0b5f151be..1452a037e 100644
--- a/src/api/python/z3/z3.py
+++ b/src/api/python/z3/z3.py
@@ -3640,7 +3640,7 @@ def BitVecs(names, bv, ctx=None):
     >>> Product(x, y, z)
     1*x*y*z
     >>> simplify(Product(x, y, z))
-    z*x*y
+    x*y*z
     """
     ctx = _get_ctx(ctx)
     if isinstance(names, str):
@@ -7647,7 +7647,7 @@ def simplify(a, *arguments, **keywords):
     >>> simplify(x + 1 + y + x + 1)
     2 + 2*x + y
     >>> simplify((x + 1)*(y + 1), som=True)
-    1 + x + y + y*x
+    1 + x + y + x*y
     >>> simplify(Distinct(x, y, 1), blast_distinct=True)
     And(Not(x == y), Not(x == 1), Not(y == 1))
     >>> simplify(And(x == 0, y == 1), elim_and=True)

From 5492d0e1355e0979eda801a3110224c6a040fdfc Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Mon, 4 Sep 2017 11:03:57 -0700
Subject: [PATCH 56/74] re-introduce eq2ineq name for rewriting parameter

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/arith_rewriter.cpp        |  4 +--
 src/ast/rewriter/arith_rewriter.h          |  2 +-
 src/ast/rewriter/arith_rewriter_params.pyg |  2 +-
 src/cmd_context/pdecl.cpp                  | 25 -----------------
 src/muz/pdr/pdr_context.cpp                |  6 ++--
 src/smt/asserted_formulas.cpp              |  2 +-
 src/smt/params/theory_arith_params.cpp     |  4 +--
 src/smt/params/theory_arith_params.h       |  4 +--
 src/smt/smt_setup.cpp                      | 32 +++++++++++-----------
 9 files changed, 28 insertions(+), 53 deletions(-)

diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp
index fbfcabd78..275290665 100644
--- a/src/ast/rewriter/arith_rewriter.cpp
+++ b/src/ast/rewriter/arith_rewriter.cpp
@@ -35,7 +35,7 @@ void arith_rewriter::updt_local_params(params_ref const & _p) {
     m_mul2power       = p.mul_to_power();
     m_elim_rem        = p.elim_rem();
     m_expand_tan      = p.expand_tan();
-    m_expand_eqs      = p.expand_eqs();
+    m_eq2ineq         = p.eq2ineq();
     set_sort_sums(p.sort_sums());
 }
 
@@ -501,7 +501,7 @@ bool arith_rewriter::is_arith_term(expr * n) const {
 }
 
 br_status arith_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) {
-    if (m_expand_eqs) {
+    if (m_eq2ineq) {
         result = m().mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2));
         return BR_REWRITE2;
     }
diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h
index af9d0e09d..1bef9a964 100644
--- a/src/ast/rewriter/arith_rewriter.h
+++ b/src/ast/rewriter/arith_rewriter.h
@@ -55,7 +55,7 @@ class arith_rewriter : public poly_rewriter<arith_rewriter_core> {
     bool m_push_to_real;
     bool m_anum_simp;
     bool m_elim_rem;
-    bool m_expand_eqs;
+    bool m_eq2ineq;
     bool m_process_all_eqs;
     unsigned m_max_degree;
 
diff --git a/src/ast/rewriter/arith_rewriter_params.pyg b/src/ast/rewriter/arith_rewriter_params.pyg
index d40f46917..c7374105a 100644
--- a/src/ast/rewriter/arith_rewriter_params.pyg
+++ b/src/ast/rewriter/arith_rewriter_params.pyg
@@ -12,5 +12,5 @@ def_module_params(module_name='rewriter',
                           ("arith_ineq_lhs", BOOL, False, "rewrite inequalities so that right-hand-side is a constant."),
                           ("elim_to_real", BOOL, False, "eliminate to_real from arithmetic predicates that contain only integers."),
                           ("push_to_real", BOOL, True, "distribute to_real over * and +."),
-                          ("expand_eqs", BOOL, False, "expand equalities into two inequalities"),
+                          ("eq2ineq", BOOL, False, "expand equalities into two inequalities"),
                           ("elim_rem", BOOL, False, "replace (rem x y) with (ite (>= y 0) (mod x y) (- (mod x y))).")))
diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp
index a9ce0b69c..04456c076 100644
--- a/src/cmd_context/pdecl.cpp
+++ b/src/cmd_context/pdecl.cpp
@@ -292,27 +292,6 @@ sort * psort_decl::find(sort * const * s) {
     return m_inst_cache->find(s);
 }
 
-#if 0
-psort_dt_decl::psort_dt_decl(unsigned id, unsigned num_params, pdecl_manager& m, symbol const& n):
-    psort_decl(id, num_params, m, n) {
-}
-
-void psort_dt_decl::finalize(pdecl_manager& m) {
-    psort_decl::finalize(m);
-}
-
-
-sort * psort_dt_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
-    UNREACHABLE();
-    return 0;
-}
-
-void psort_dt_decl::display(std::ostream & out) const {
-    out << get_name() << " " << get_num_params();
-}
-#endif
-
-
 psort_user_decl::psort_user_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, psort * p) :
     psort_decl(id, num_params, m, n),
     m_def(p) {
@@ -859,10 +838,6 @@ psort_decl * pdecl_manager::mk_psort_user_decl(unsigned num_params, symbol const
 }
 
 
-//psort_decl * pdecl_manager::mk_psort_dt_decl(unsigned num_params, symbol const & n) {
-//    return new (a().allocate(sizeof(psort_dt_decl))) psort_dt_decl(m_id_gen.mk(), num_params, *this, n);
-//}
-
 psort_decl * pdecl_manager::mk_psort_builtin_decl(symbol const & n, family_id fid, decl_kind k) {
     return new (a().allocate(sizeof(psort_builtin_decl))) psort_builtin_decl(m_id_gen.mk(), *this, n, fid, k);
 }
diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp
index 0190044b1..fd734ea66 100644
--- a/src/muz/pdr/pdr_context.cpp
+++ b/src/muz/pdr/pdr_context.cpp
@@ -1835,16 +1835,16 @@ namespace pdr {
                 !m_params.pdr_use_convex_interior_generalizer()) {
                 if (classify.is_dl()) {
                     m_fparams.m_arith_mode = AS_DIFF_LOGIC;
-                    m_fparams.m_arith_expand_eqs = true;
+                    m_fparams.m_arith_eq2ineq = true;
                 }
                 else if (classify.is_utvpi()) {
                     IF_VERBOSE(1, verbose_stream() << "UTVPI\n";);
                     m_fparams.m_arith_mode = AS_UTVPI;
-                    m_fparams.m_arith_expand_eqs = true;
+                    m_fparams.m_arith_eq2ineq = true;
                 }
                 else {
                     m_fparams.m_arith_mode = AS_ARITH;
-                    m_fparams.m_arith_expand_eqs = false;
+                    m_fparams.m_arith_eq2ineq = false;
                 }
             }
         }
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index f37cabde8..1581e70bd 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -123,7 +123,7 @@ void asserted_formulas::set_eliminate_and(bool flag) {
     p.set_bool("arith_ineq_lhs", true);
     p.set_bool("sort_sums", true);
     p.set_bool("rewrite_patterns", true);
-    p.set_bool("expand_eqs", m_params.m_arith_expand_eqs);
+    p.set_bool("eq2ineq", m_params.m_arith_eq2ineq);
     p.set_bool("gcd_rounding", true);
     m_rewriter.updt_params(p);
     flush_cache();
diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp
index 9b8aa9b81..250848db4 100644
--- a/src/smt/params/theory_arith_params.cpp
+++ b/src/smt/params/theory_arith_params.cpp
@@ -38,14 +38,14 @@ void theory_arith_params::updt_params(params_ref const & _p) {
     m_arith_dump_lemmas = p.arith_dump_lemmas();
     m_arith_reflect = p.arith_reflect();
     arith_rewriter_params ap(_p);
-    m_arith_expand_eqs = ap.expand_eqs();
+    m_arith_eq2ineq = ap.eq2ineq();
 }
 
 
 #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl;
 
 void theory_arith_params::display(std::ostream & out) const {
-    DISPLAY_PARAM(m_arith_expand_eqs);
+    DISPLAY_PARAM(m_arith_eq2ineq);
     DISPLAY_PARAM(m_arith_process_all_eqs);
     DISPLAY_PARAM(m_arith_mode);
     DISPLAY_PARAM(m_arith_auto_config_simplex); //!< force simplex solver in auto_config
diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h
index 89c4fe46c..1fe7e1163 100644
--- a/src/smt/params/theory_arith_params.h
+++ b/src/smt/params/theory_arith_params.h
@@ -49,7 +49,7 @@ enum arith_pivot_strategy {
 };
 
 struct theory_arith_params {
-    bool                    m_arith_expand_eqs;
+    bool                    m_arith_eq2ineq;
     bool                    m_arith_process_all_eqs;
     arith_solver_id         m_arith_mode;
     bool                    m_arith_auto_config_simplex; //!< force simplex solver in auto_config
@@ -110,7 +110,7 @@ struct theory_arith_params {
 
 
     theory_arith_params(params_ref const & p = params_ref()):
-        m_arith_expand_eqs(false),
+        m_arith_eq2ineq(false),
         m_arith_process_all_eqs(false),
         m_arith_mode(AS_ARITH),
         m_arith_auto_config_simplex(false),
diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp
index f0c44a574..50b49f3b8 100644
--- a/src/smt/smt_setup.cpp
+++ b/src/smt/smt_setup.cpp
@@ -226,7 +226,7 @@ namespace smt {
 
     void setup::setup_QF_RDL() {
         m_params.m_relevancy_lvl       = 0;
-        m_params.m_arith_expand_eqs    = true;
+        m_params.m_arith_eq2ineq       = true;
         m_params.m_arith_reflect       = false;
         m_params.m_arith_propagate_eqs = false;
         m_params.m_nnf_cnf             = false;
@@ -266,7 +266,7 @@ namespace smt {
         TRACE("setup", tout << "setup_QF_RDL(st)\n";);
         check_no_uninterpreted_functions(st, "QF_RDL");
         m_params.m_relevancy_lvl       = 0;
-        m_params.m_arith_expand_eqs    = true;
+        m_params.m_arith_eq2ineq       = true;
         m_params.m_arith_reflect       = false;
         m_params.m_arith_propagate_eqs = false;
         m_params.m_nnf_cnf             = false;
@@ -318,7 +318,7 @@ namespace smt {
     void setup::setup_QF_IDL() {
         TRACE("setup", tout << "setup_QF_IDL()\n";);
         m_params.m_relevancy_lvl       = 0;
-        m_params.m_arith_expand_eqs    = true;
+        m_params.m_arith_eq2ineq       = true;
         m_params.m_arith_reflect       = false;
         m_params.m_arith_propagate_eqs = false;
         m_params.m_arith_small_lemma_size = 30;
@@ -336,7 +336,7 @@ namespace smt {
         TRACE("setup", tout << "setup_QF_IDL(st)\n";);
         check_no_uninterpreted_functions(st, "QF_IDL");
         m_params.m_relevancy_lvl       = 0;
-        m_params.m_arith_expand_eqs    = true;
+        m_params.m_arith_eq2ineq       = true;
         m_params.m_arith_reflect       = false;
         m_params.m_arith_propagate_eqs = false;
         m_params.m_arith_small_lemma_size = 30;
@@ -390,7 +390,7 @@ namespace smt {
         m_params.m_arith_reflect    = false;
         m_params.m_nnf_cnf          = false;
         m_params.m_arith_eq_bounds  = true;
-        m_params.m_arith_expand_eqs = true;
+        m_params.m_arith_eq2ineq    = true;
         m_params.m_phase_selection  = PS_ALWAYS_FALSE;
         m_params.m_restart_strategy = RS_GEOMETRIC;
         m_params.m_restart_factor   = 1.5;
@@ -406,8 +406,8 @@ namespace smt {
         m_params.m_arith_reflect    = false;
         m_params.m_nnf_cnf          = false;
         if (st.m_num_uninterpreted_functions == 0) {
-            m_params.m_arith_expand_eqs       = true;
-            m_params.m_arith_propagate_eqs    = false;
+            m_params.m_arith_eq2ineq        = true;
+            m_params.m_arith_propagate_eqs  = false;
             if (is_dense(st)) {
                 m_params.m_arith_small_lemma_size = 128;
                 m_params.m_lemma_gc_half          = true;
@@ -440,7 +440,7 @@ namespace smt {
     void setup::setup_QF_LRA() {
         TRACE("setup", tout << "setup_QF_LRA(st)\n";);
         m_params.m_relevancy_lvl       = 0;
-        m_params.m_arith_expand_eqs    = true;
+        m_params.m_arith_eq2ineq       = true;
         m_params.m_arith_reflect       = false;
         m_params.m_arith_propagate_eqs = false;
         m_params.m_eliminate_term_ite  = true;
@@ -451,7 +451,7 @@ namespace smt {
     void setup::setup_QF_LRA(static_features const & st) {
         check_no_uninterpreted_functions(st, "QF_LRA");
         m_params.m_relevancy_lvl       = 0;
-        m_params.m_arith_expand_eqs    = true;
+        m_params.m_arith_eq2ineq       = true;
         m_params.m_arith_reflect       = false;
         m_params.m_arith_propagate_eqs = false;
         m_params.m_eliminate_term_ite  = true;
@@ -480,7 +480,7 @@ namespace smt {
     void setup::setup_QF_LIA() {
         TRACE("setup", tout << "setup_QF_LIA(st)\n";);
         m_params.m_relevancy_lvl       = 0;
-        m_params.m_arith_expand_eqs    = true;
+        m_params.m_arith_eq2ineq       = true;
         m_params.m_arith_reflect       = false; 
         m_params.m_arith_propagate_eqs = false; 
         m_params.m_nnf_cnf             = false;
@@ -492,12 +492,12 @@ namespace smt {
         TRACE("setup", tout << "QF_LIA setup\n";);
 
         m_params.m_relevancy_lvl       = 0;
-        m_params.m_arith_expand_eqs    = true;
+        m_params.m_arith_eq2ineq       = true;
         m_params.m_arith_reflect       = false; 
         m_params.m_arith_propagate_eqs = false;
         m_params.m_nnf_cnf             = false;
         if (st.m_max_ite_tree_depth > 50) {
-            m_params.m_arith_expand_eqs     = false;
+            m_params.m_arith_eq2ineq        = false;
             m_params.m_pull_cheap_ite_trees = true;
             m_params.m_arith_propagate_eqs  = true;
             m_params.m_relevancy_lvl        = 2; 
@@ -507,7 +507,7 @@ namespace smt {
             m_params.m_arith_gcd_test         = false;
             m_params.m_arith_branch_cut_ratio = 4;
             m_params.m_relevancy_lvl          = 2; 
-            m_params.m_arith_expand_eqs       = true;
+            m_params.m_arith_eq2ineq          = true;
             m_params.m_eliminate_term_ite     = true;
             // if (st.m_num_exprs < 5000 && st.m_num_ite_terms < 50) { // safeguard to avoid high memory consumption
             // TODO: implement analsysis function to decide where lift ite is too expensive.
@@ -755,7 +755,7 @@ namespace smt {
             m_context.register_plugin(alloc(smt::theory_dummy, m_manager.mk_family_id("arith"), "no arithmetic"));
             break;
         case AS_DIFF_LOGIC:
-            m_params.m_arith_expand_eqs  = true;
+            m_params.m_arith_eq2ineq  = true;
             if (fixnum) {
                 if (int_only)
                     m_context.register_plugin(alloc(smt::theory_fidl, m_manager, m_params));
@@ -770,7 +770,7 @@ namespace smt {
             }
             break;
         case AS_DENSE_DIFF_LOGIC:
-            m_params.m_arith_expand_eqs  = true;
+            m_params.m_arith_eq2ineq  = true;
             if (fixnum) {
                 if (int_only)
                     m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params));
@@ -785,7 +785,7 @@ namespace smt {
             }
             break;
         case AS_UTVPI:
-            m_params.m_arith_expand_eqs  = true;
+            m_params.m_arith_eq2ineq  = true;
             if (int_only)
                 m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager));
             else

From 5d17e286672ee808702972cb6da60389bb80a720 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Mon, 4 Sep 2017 21:12:43 -0700
Subject: [PATCH 57/74] support for smtlib2.6 datatype parsing

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/api_datatype.cpp                 |  57 +++++-----
 src/ast/ast.cpp                          |  35 ++-----
 src/ast/ast_ll_pp.cpp                    |   2 +-
 src/ast/ast_smt2_pp.cpp                  |   2 +-
 src/ast/ast_smt_pp.cpp                   |  32 ++++--
 src/ast/datatype_decl_plugin.cpp         |  24 ++---
 src/ast/datatype_decl_plugin.h           |  14 +--
 src/ast/datatype_decl_plugin2.cpp        |  85 +++++++++------
 src/ast/datatype_decl_plugin2.h          |  31 +++---
 src/ast/decl_collector.cpp               |   4 +-
 src/ast/rewriter/datatype_rewriter.cpp   |   4 +-
 src/ast/rewriter/enum2bv_rewriter.cpp    |   2 +-
 src/cmd_context/cmd_context.cpp          |  21 ++--
 src/cmd_context/pdecl.cpp                | 127 +++++++++++++++++++++--
 src/cmd_context/pdecl.h                  |  29 ++++--
 src/muz/base/dl_rule.h                   |   2 +-
 src/muz/base/rule_properties.cpp         |   2 +-
 src/muz/bmc/dl_bmc_engine.cpp            |  18 ++--
 src/muz/pdr/pdr_prop_solver.cpp          |   2 +-
 src/muz/spacer/spacer_util.cpp           |   2 +-
 src/parsers/smt2/smt2parser.cpp          |  27 ++++-
 src/qe/qe_datatype_plugin.cpp            |  12 +--
 src/qe/qe_datatypes.cpp                  |   6 +-
 src/qe/qe_lite.cpp                       |   2 +-
 src/smt/proto_model/datatype_factory.cpp |   6 +-
 src/smt/smt_value_sort.cpp               |   2 +-
 src/smt/theory_datatype.cpp              |  22 ++--
 src/tactic/core/elim_uncnstr_tactic.cpp  |   6 +-
 src/tactic/portfolio/enum2bv_solver.cpp  |   2 +-
 29 files changed, 374 insertions(+), 206 deletions(-)

diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp
index d670888ed..d667a7428 100644
--- a/src/api/api_datatype.cpp
+++ b/src/api/api_datatype.cpp
@@ -45,13 +45,13 @@ extern "C" {
 
         ptr_vector<accessor_decl> acc;
         for (unsigned i = 0; i < num_fields; ++i) {
-            acc.push_back(mk_accessor_decl(to_symbol(field_names[i]), type_ref(to_sort(field_sorts[i]))));
+            acc.push_back(mk_accessor_decl(m, to_symbol(field_names[i]), type_ref(to_sort(field_sorts[i]))));
         }
 
         constructor_decl* constrs[1] = { mk_constructor_decl(to_symbol(name), recognizer, acc.size(), acc.c_ptr()) };
 
         {
-            datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 1, constrs);
+            datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, 1, constrs);
             bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, 0, tuples);
             del_datatype_decl(dt);
 
@@ -69,13 +69,13 @@ extern "C" {
         // create constructor
         SASSERT(dt_util.is_datatype(tuple));
         SASSERT(!dt_util.is_recursive(tuple));
-        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(tuple);
+        ptr_vector<func_decl> const & decls = *dt_util.get_datatype_constructors(tuple);
         func_decl* decl = (decls)[0];
         mk_c(c)->save_multiple_ast_trail(decl);
         *mk_tuple_decl = of_func_decl(decl);
 
         // Create projections
-        ptr_vector<func_decl> const & _accs = dt_util.get_constructor_accessors(decl);
+        ptr_vector<func_decl> const & _accs = *dt_util.get_constructor_accessors(decl);
         SASSERT(_accs.size() == num_fields);
         for (unsigned i = 0; i < _accs.size(); i++) {
             mk_c(c)->save_multiple_ast_trail(_accs[i]);
@@ -113,7 +113,7 @@ extern "C" {
 
 
         {
-            datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), n, constrs.c_ptr());
+            datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 0, 0, n, constrs.c_ptr());
             bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, 0, sorts);
             del_datatype_decl(dt);
 
@@ -131,7 +131,7 @@ extern "C" {
         // create constructor
         SASSERT(dt_util.is_datatype(e));
         SASSERT(!dt_util.is_recursive(e));
-        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(e);
+        ptr_vector<func_decl> const & decls = *dt_util.get_datatype_constructors(e);
         SASSERT(decls.size() == n);
         for (unsigned i = 0; i < n; ++i) {
             func_decl* decl = (decls)[i];
@@ -164,8 +164,8 @@ extern "C" {
         mk_c(c)->reset_last_result();
         datatype_util data_util(m);
         accessor_decl* head_tail[2] = {
-            mk_accessor_decl(symbol("head"), type_ref(to_sort(elem_sort))),
-            mk_accessor_decl(symbol("tail"), type_ref(0))
+            mk_accessor_decl(m, symbol("head"), type_ref(to_sort(elem_sort))),
+            mk_accessor_decl(m, symbol("tail"), type_ref(0))
         };
         constructor_decl* constrs[2] = {
             mk_constructor_decl(symbol("nil"), symbol("is_nil"), 0, 0),
@@ -175,7 +175,7 @@ extern "C" {
 
         sort_ref_vector sorts(m);
         {
-            datatype_decl * decl = mk_datatype_decl(dt_util, to_symbol(name), 2, constrs);
+            datatype_decl * decl = mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, 2, constrs);
             bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &decl, 0, 0, sorts);
             del_datatype_decl(decl);
 
@@ -187,7 +187,7 @@ extern "C" {
         sort * s = sorts.get(0);
 
         mk_c(c)->save_multiple_ast_trail(s);
-        ptr_vector<func_decl> const& cnstrs = data_util.get_datatype_constructors(s);
+        ptr_vector<func_decl> const& cnstrs = *data_util.get_datatype_constructors(s);
         SASSERT(cnstrs.size() == 2);
         func_decl* f;
         if (nil_decl) {
@@ -211,14 +211,14 @@ extern "C" {
             *is_cons_decl = of_func_decl(f);
         }
         if (head_decl) {
-            ptr_vector<func_decl> const& acc = data_util.get_constructor_accessors(cnstrs[1]);
+            ptr_vector<func_decl> const& acc = *data_util.get_constructor_accessors(cnstrs[1]);
             SASSERT(acc.size() == 2);
             f = (acc)[0];
             mk_c(c)->save_multiple_ast_trail(f);
             *head_decl = of_func_decl(f);
         }
         if (tail_decl) {
-            ptr_vector<func_decl> const& acc = data_util.get_constructor_accessors(cnstrs[1]);
+            ptr_vector<func_decl> const& acc = *data_util.get_constructor_accessors(cnstrs[1]);
             SASSERT(acc.size() == 2);
             f = (acc)[1];
             mk_c(c)->save_multiple_ast_trail(f);
@@ -295,7 +295,7 @@ extern "C" {
             *tester = of_func_decl(f2);
         }
 
-        ptr_vector<func_decl> const& accs = data_util.get_constructor_accessors(f);
+        ptr_vector<func_decl> const& accs = *data_util.get_constructor_accessors(f);
         for (unsigned i = 0; i < num_fields; ++i) {
             func_decl* f2 = (accs)[i];
             mk_c(c)->save_multiple_ast_trail(f2);
@@ -318,21 +318,22 @@ extern "C" {
                                            unsigned num_constructors,
                                            Z3_constructor constructors[]) {
         datatype_util& dt_util = mk_c(c)->dtutil();
+        ast_manager& m = mk_c(c)->m();
         ptr_vector<constructor_decl> constrs;
         for (unsigned i = 0; i < num_constructors; ++i) {
             constructor* cn = reinterpret_cast<constructor*>(constructors[i]);
             ptr_vector<accessor_decl> acc;
             for (unsigned j = 0; j < cn->m_sorts.size(); ++j) {
                 if (cn->m_sorts[j].get()) {
-                    acc.push_back(mk_accessor_decl(cn->m_field_names[j], type_ref(cn->m_sorts[j].get())));
+                    acc.push_back(mk_accessor_decl(m, cn->m_field_names[j], type_ref(cn->m_sorts[j].get())));
                 }
                 else {
-                    acc.push_back(mk_accessor_decl(cn->m_field_names[j], type_ref(cn->m_sort_refs[j])));
+                    acc.push_back(mk_accessor_decl(m, cn->m_field_names[j], type_ref(cn->m_sort_refs[j])));
                 }
             }
             constrs.push_back(mk_constructor_decl(cn->m_name, cn->m_tester, acc.size(), acc.c_ptr()));
         }
-        return mk_datatype_decl(dt_util, to_symbol(name), num_constructors, constrs.c_ptr());
+        return mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, num_constructors, constrs.c_ptr());
     }
 
     Z3_sort Z3_API Z3_mk_datatype(Z3_context c,
@@ -359,7 +360,7 @@ extern "C" {
         sort * s = sorts.get(0);
 
         mk_c(c)->save_ast_trail(s);
-        ptr_vector<func_decl> const& cnstrs = data_util.get_datatype_constructors(s);
+        ptr_vector<func_decl> const& cnstrs = *data_util.get_datatype_constructors(s);
 
         for (unsigned i = 0; i < num_constructors; ++i) {
             constructor* cn = reinterpret_cast<constructor*>(constructors[i]);
@@ -408,7 +409,7 @@ extern "C" {
         ptr_vector<datatype_decl> datas;
         for (unsigned i = 0; i < num_sorts; ++i) {
             constructor_list* cl = reinterpret_cast<constructor_list*>(constructor_lists[i]);
-            datas.push_back(mk_datatype_decl(c,sort_names[i], cl->size(), reinterpret_cast<Z3_constructor*>(cl->c_ptr())));
+            datas.push_back(mk_datatype_decl(c, sort_names[i], cl->size(), reinterpret_cast<Z3_constructor*>(cl->c_ptr())));
         }
         sort_ref_vector _sorts(m);
         bool ok = mk_c(c)->get_dt_plugin()->mk_datatypes(datas.size(), datas.c_ptr(), 0, 0, _sorts);
@@ -425,7 +426,7 @@ extern "C" {
             mk_c(c)->save_multiple_ast_trail(s);
             sorts[i] = of_sort(s);
             constructor_list* cl = reinterpret_cast<constructor_list*>(constructor_lists[i]);
-            ptr_vector<func_decl> const& cnstrs = data_util.get_datatype_constructors(s);
+            ptr_vector<func_decl> const& cnstrs = *data_util.get_datatype_constructors(s);
             for (unsigned j = 0; j < cl->size(); ++j) {
                 constructor* cn = (*cl)[j];
                 cn->m_constructor = cnstrs[j];
@@ -447,7 +448,7 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        return dt_util.get_datatype_constructors(_t).size();
+        return dt_util.get_datatype_constructors(_t)->size();
         Z3_CATCH_RETURN(0);
     }
 
@@ -460,7 +461,7 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(_t);
+        ptr_vector<func_decl> const & decls = *dt_util.get_datatype_constructors(_t);
         if (idx >= decls.size()) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
@@ -490,7 +491,7 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(_t);
+        ptr_vector<func_decl> const & decls = *dt_util.get_datatype_constructors(_t);
         if (idx >= decls.size()) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
@@ -513,7 +514,7 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(_t);
+        ptr_vector<func_decl> const & decls = *dt_util.get_datatype_constructors(_t);
         if (idx_c >= decls.size()) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
@@ -523,7 +524,7 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const & accs = dt_util.get_constructor_accessors(decl);
+        ptr_vector<func_decl> const & accs = *dt_util.get_constructor_accessors(decl);
         SASSERT(accs.size() == decl->get_arity());
         if (accs.size() <= idx_a) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
@@ -560,12 +561,12 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(tuple);
+        ptr_vector<func_decl> const & decls = *dt_util.get_datatype_constructors(tuple);
         if (decls.size() != 1) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             return 0;
         }
-        ptr_vector<func_decl> const & accs = dt_util.get_constructor_accessors(decls[0]);
+        ptr_vector<func_decl> const & accs = *dt_util.get_constructor_accessors(decls[0]);
         return accs.size();
         Z3_CATCH_RETURN(0);
     }
@@ -580,12 +581,12 @@ extern "C" {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const & decls = dt_util.get_datatype_constructors(tuple);
+        ptr_vector<func_decl> const & decls = *dt_util.get_datatype_constructors(tuple);
         if (decls.size() != 1) {
             SET_ERROR_CODE(Z3_INVALID_ARG);
             RETURN_Z3(0);
         }
-        ptr_vector<func_decl> const & accs = dt_util.get_constructor_accessors((decls)[0]);
+        ptr_vector<func_decl> const & accs = *dt_util.get_constructor_accessors((decls)[0]);
         if (accs.size() <= i) {
             SET_ERROR_CODE(Z3_IOB);
             RETURN_Z3(0);
diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp
index 1a35e710a..a905efa28 100644
--- a/src/ast/ast.cpp
+++ b/src/ast/ast.cpp
@@ -1287,10 +1287,8 @@ decl_kind user_sort_plugin::register_name(symbol s) {
 
 decl_plugin * user_sort_plugin::mk_fresh() {
     user_sort_plugin * p = alloc(user_sort_plugin);
-    svector<symbol>::iterator it  = m_sort_names.begin();
-    svector<symbol>::iterator end = m_sort_names.end();
-    for (; it != end; ++it)
-        p->register_name(*it);
+    for (symbol const& s : m_sort_names) 
+        p->register_name(s);
     return p;
 }
 
@@ -1410,26 +1408,20 @@ ast_manager::~ast_manager() {
     dec_ref(m_true);
     dec_ref(m_false);
     dec_ref(m_undef_proof);
-    ptr_vector<decl_plugin>::iterator it  = m_plugins.begin();
-    ptr_vector<decl_plugin>::iterator end = m_plugins.end();
-    for (; it != end; ++it) {
-        if (*it)
-            (*it)->finalize();
+    for (decl_plugin* p : m_plugins) {
+        if (p)
+            p->finalize();
     }
-    it = m_plugins.begin();
-    for (; it != end; ++it) {
-        if (*it) 
-            dealloc(*it);
+    for (decl_plugin* p : m_plugins) {
+        if (p) 
+            dealloc(p);
     }
     m_plugins.reset();
     while (!m_ast_table.empty()) {
         DEBUG_CODE(std::cout << "ast_manager LEAKED: " << m_ast_table.size() << std::endl;);
         ptr_vector<ast> roots;
         ast_mark mark;
-        ast_table::iterator it_a = m_ast_table.begin();
-        ast_table::iterator end_a = m_ast_table.end();
-        for (; it_a != end_a; ++it_a) {
-            ast* n = (*it_a);
+        for (ast * n : m_ast_table) {
             switch (n->get_kind()) {
             case AST_SORT: {
                 sort_info* info = to_sort(n)->get_info();
@@ -1462,9 +1454,7 @@ ast_manager::~ast_manager() {
                 break;
             }           
         }        
-        it_a = m_ast_table.begin();
-        for (; it_a != end_a; ++it_a) {
-            ast* n = *it_a;
+        for (ast * n : m_ast_table) {
             if (!mark.is_marked(n)) {
                 roots.push_back(n);
             }
@@ -1659,11 +1649,8 @@ bool ast_manager::is_bool(expr const * n) const {
 
 #ifdef Z3DEBUG
 bool ast_manager::slow_not_contains(ast const * n) {
-    ast_table::iterator it  = m_ast_table.begin();
-    ast_table::iterator end = m_ast_table.end();
     unsigned num = 0;
-    for (; it != end; ++it) {
-        ast * curr = *it;
+    for (ast * curr : m_ast_table) {
         if (compare_nodes(curr, n)) {
             TRACE("nondet_bug",
                   tout << "id1:   " << curr->get_id() << ", id2: " << n->get_id() << "\n";
diff --git a/src/ast/ast_ll_pp.cpp b/src/ast/ast_ll_pp.cpp
index c00053780..6b14b75a8 100644
--- a/src/ast/ast_ll_pp.cpp
+++ b/src/ast/ast_ll_pp.cpp
@@ -284,7 +284,7 @@ public:
         }
         unsigned num_args = to_app(n)->get_num_args();
         if (num_args > 0) 
-            m_out << "(";
+            m_out << "(_ ";
         display_name(to_app(n)->get_decl());
         display_params(to_app(n)->get_decl());
         for (unsigned i = 0; i < num_args; i++) {
diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp
index 51ccc1e7d..642983737 100644
--- a/src/ast/ast_smt2_pp.cpp
+++ b/src/ast/ast_smt2_pp.cpp
@@ -431,7 +431,7 @@ format_ns::format * smt2_pp_environment::pp_sort(sort * s) {
         fs.push_back(pp_sort(to_sort(s->get_parameter(0).get_ast())));
         return mk_seq1(m, fs.begin(), fs.end(), f2f(), get_sutil().is_seq(s)?"Seq":"RegEx");
     }
-#if 0
+#ifdef DATATYPE_V2
     if (get_dtutil().is_datatype(s)) {
         ptr_buffer<format> fs;
         unsigned sz = get_dtutil().get_datatype_num_parameter_sorts(s);
diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp
index 9211c5899..fdac6c7be 100644
--- a/src/ast/ast_smt_pp.cpp
+++ b/src/ast/ast_smt_pp.cpp
@@ -21,17 +21,17 @@ Revision History:
 
 #include<sstream>
 #include<iostream>
+#include "util/vector.h"
+#include "util/smt2_util.h"
 #include "ast/ast_smt_pp.h"
 #include "ast/arith_decl_plugin.h"
 #include "ast/bv_decl_plugin.h"
 #include "ast/array_decl_plugin.h"
 #include "ast/datatype_decl_plugin.h"
+#include "ast/seq_decl_plugin.h"
 #include "ast/fpa_decl_plugin.h"
-#include "util/vector.h"
 #include "ast/for_each_ast.h"
 #include "ast/decl_collector.h"
-#include "util/smt2_util.h"
-#include "ast/seq_decl_plugin.h"
 
 // ---------------------------------------
 // smt_renaming
@@ -210,7 +210,19 @@ class smt_printer {
     void pp_decl(func_decl* d) {
         symbol sym = m_renaming.get_symbol(d->get_name());
         if (d->get_family_id() == m_dt_fid) {
+#ifdef DATATYPE_V2
+            std::cout << "printing " << sym << "\n";
+            datatype_util util(m_manager);
+            if (util.is_recognizer(d)) {
+                std::cout << d->get_num_parameters() << "\n";
+                visit_params(false, sym, d->get_num_parameters(), d->get_parameters());
+            }
+            else {
+                m_out << sym;
+            }
+#else
             m_out << sym;
+#endif
         }
         else if (m_manager.is_ite(d)) {
             if (!m_is_smt2 && is_bool(d->get_range())) {
@@ -366,14 +378,15 @@ class smt_printer {
             return;
         }
         else if (s->is_sort_of(m_dt_fid, DATATYPE_SORT)) {
+#ifndef DATATYPE_V2
             m_out << m_renaming.get_symbol(s->get_name());            
-#if 0
+#else
             datatype_util util(m_manager);
             unsigned num_sorts = util.get_datatype_num_parameter_sorts(s);
             if (num_sorts > 0) {
                 m_out << "(";
             }
-
+            m_out << m_renaming.get_symbol(s->get_name());            
             if (num_sorts > 0) {
                 for (unsigned i = 0; i < num_sorts; ++i) {
                     m_out << " ";
@@ -533,7 +546,8 @@ class smt_printer {
             pp_arg(curr, n);
             m_out << ")";
 
-        } else if (m_manager.is_distinct(decl)) {
+        } 
+        else if (m_manager.is_distinct(decl)) {
             ptr_vector<expr> args(num_args, n->get_args());
             unsigned         idx = 0;
             m_out << "(and";
@@ -915,7 +929,7 @@ public:
         // collect siblings and sorts that have not already been printed.
         for (unsigned h = 0; h < rec_sorts.size(); ++h) {
             s = rec_sorts[h];
-            ptr_vector<func_decl> const& decls = util.get_datatype_constructors(s);
+            ptr_vector<func_decl> const& decls = *util.get_datatype_constructors(s);
 
             for (unsigned i = 0; i < decls.size(); ++i) {
                 func_decl* f = decls[i];
@@ -954,11 +968,11 @@ public:
             m_out << "(";
             m_out << m_renaming.get_symbol(s->get_name());
             m_out << " ";
-            ptr_vector<func_decl> const& decls = util.get_datatype_constructors(s);
+            ptr_vector<func_decl> const& decls = *util.get_datatype_constructors(s);
 
             for (unsigned i = 0; i < decls.size(); ++i) {
                 func_decl* f = decls[i];
-                ptr_vector<func_decl> const& accs = util.get_constructor_accessors(f);
+                ptr_vector<func_decl> const& accs = *util.get_constructor_accessors(f);
                 if (m_is_smt2 || accs.size() > 0) {
                     m_out << "(";
                 }
diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp
index 1c090bc33..7ad80336d 100644
--- a/src/ast/datatype_decl_plugin.cpp
+++ b/src/ast/datatype_decl_plugin.cpp
@@ -34,7 +34,7 @@ public:
     type_ref const & get_type() const { return m_type; }
 };
 
-accessor_decl * mk_accessor_decl(symbol const & n, type_ref const & t) {
+accessor_decl * mk_accessor_decl(ast_manager& m, symbol const & n, type_ref const & t) {
     return alloc(accessor_decl, n, t);
 }
 
@@ -95,7 +95,7 @@ public:
     ptr_vector<constructor_decl> const & get_constructors() const { return m_constructors; }
 };
 
-datatype_decl * mk_datatype_decl(datatype_util&, symbol const & n, unsigned num_constructors, constructor_decl * const * cs) {
+datatype_decl * mk_datatype_decl(datatype_util&, symbol const & n, unsigned num_params, sort * const* params, unsigned num_constructors, constructor_decl * const * cs) {
     return alloc(datatype_decl, n, num_constructors, cs);
 }
 
@@ -803,11 +803,11 @@ func_decl * datatype_util::get_constructor(sort * ty, unsigned c_id) {
     return d;
 }
 
-ptr_vector<func_decl> const & datatype_util::get_datatype_constructors(sort * ty) {
+ptr_vector<func_decl> const * datatype_util::get_datatype_constructors(sort * ty) {
     SASSERT(is_datatype(ty));
     ptr_vector<func_decl> * r = 0;
     if (m_datatype2constructors.find(ty, r))
-        return *r;
+        return r;
     r = alloc(ptr_vector<func_decl>);
     m_asts.push_back(ty);
     m_vectors.push_back(r);
@@ -820,7 +820,7 @@ ptr_vector<func_decl> const & datatype_util::get_datatype_constructors(sort * ty
         m_asts.push_back(c);
         r->push_back(c);
     }
-    return *r;
+    return r;
 }
 
 /**
@@ -855,7 +855,7 @@ func_decl * datatype_util::get_non_rec_constructor_core(sort * ty, ptr_vector<so
     //   1) T_i's are not recursive
     // If there is no such constructor, then we select one that 
     //   2) each type T_i is not recursive or contains a constructor that does not depend on T
-    ptr_vector<func_decl> const & constructors = get_datatype_constructors(ty);
+    ptr_vector<func_decl> const & constructors = *get_datatype_constructors(ty);
     // step 1)
     unsigned sz = constructors.size();
     ++m_start;
@@ -916,11 +916,11 @@ func_decl * datatype_util::get_constructor_recognizer(func_decl * constructor) {
     return d;
 }
 
-ptr_vector<func_decl> const & datatype_util::get_constructor_accessors(func_decl * constructor) {
+ptr_vector<func_decl> const * datatype_util::get_constructor_accessors(func_decl * constructor) {
     SASSERT(is_constructor(constructor));
     ptr_vector<func_decl> * res = 0;
     if (m_constructor2accessors.find(constructor, res))
-        return *res;
+        return res;
     res = alloc(ptr_vector<func_decl>);
     m_asts.push_back(constructor);
     m_vectors.push_back(res);
@@ -939,7 +939,7 @@ ptr_vector<func_decl> const & datatype_util::get_constructor_accessors(func_decl
         m_asts.push_back(d);
         res->push_back(d);
     }
-    return *res;
+    return res;
 }
 
 func_decl * datatype_util::get_accessor_constructor(func_decl * accessor) { 
@@ -989,7 +989,7 @@ bool datatype_util::is_enum_sort(sort* s) {
     bool r = false;
     if (m_is_enum.find(s, r))
         return r;
-    ptr_vector<func_decl> const& cnstrs = get_datatype_constructors(s);
+    ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
     r = true;
     for (unsigned i = 0; r && i < cnstrs.size(); ++i) {
         r = cnstrs[i]->get_arity() == 0;
@@ -1049,12 +1049,12 @@ void datatype_util::display_datatype(sort *s0, std::ostream& strm) {
         todo.pop_back();
         strm << s->get_name() << " =\n";
 
-        ptr_vector<func_decl> const & cnstrs = get_datatype_constructors(s);
+        ptr_vector<func_decl> const & cnstrs = *get_datatype_constructors(s);
         for (unsigned i = 0; i < cnstrs.size(); ++i) {
             func_decl* cns = cnstrs[i];
             func_decl* rec = get_constructor_recognizer(cns);
             strm << "  " << cns->get_name() << " :: " << rec->get_name() << " :: ";
-            ptr_vector<func_decl> const & accs = get_constructor_accessors(cns);
+            ptr_vector<func_decl> const & accs = *get_constructor_accessors(cns);
             for (unsigned j = 0; j < accs.size(); ++j) {
                 func_decl* acc = accs[j];
                 sort* s1 = acc->get_range();
diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h
index e3e1b7b10..3b4c8dd08 100644
--- a/src/ast/datatype_decl_plugin.h
+++ b/src/ast/datatype_decl_plugin.h
@@ -16,7 +16,7 @@ Author:
 Revision History:
 
 --*/
-//define DATATYPE_V2
+// define DATATYPE_V2
 #ifdef DATATYPE_V2
 #include "ast/datatype_decl_plugin2.h"
 #else
@@ -78,15 +78,11 @@ class constructor_decl;
 class datatype_decl;
 class datatype_util;
 
-accessor_decl * mk_accessor_decl(symbol const & n, type_ref const & t);
-//void del_accessor_decl(accessor_decl * d);
-//void del_accessor_decls(unsigned num, accessor_decl * const * as);
+accessor_decl * mk_accessor_decl(ast_manager& m, symbol const & n, type_ref const & t);
 // Remark: the constructor becomes the owner of the accessor_decls
 constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * const * acs);
-//void del_constructor_decl(constructor_decl * d);
-//void del_constructor_decls(unsigned num, constructor_decl * const * cs);
 // Remark: the datatype becomes the owner of the constructor_decls
-datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_constructors, constructor_decl * const * cs);
+datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort * const* params, unsigned num_constructors, constructor_decl * const * cs);
 void del_datatype_decl(datatype_decl * d);
 void del_datatype_decls(unsigned num, datatype_decl * const * ds);
 
@@ -216,7 +212,7 @@ public:
     bool is_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); }
     bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
     bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
-    ptr_vector<func_decl> const & get_datatype_constructors(sort * ty);
+    ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
     unsigned get_datatype_num_constructors(sort * ty) { 
         SASSERT(is_datatype(ty));
         unsigned tid = ty->get_parameter(1).get_int();
@@ -236,7 +232,7 @@ public:
     unsigned get_recognizer_constructor_idx(func_decl * f) const { SASSERT(is_recognizer(f)); return f->get_parameter(1).get_int(); }
     func_decl * get_non_rec_constructor(sort * ty);
     func_decl * get_constructor_recognizer(func_decl * constructor);
-    ptr_vector<func_decl> const & get_constructor_accessors(func_decl * constructor);
+    ptr_vector<func_decl> const * get_constructor_accessors(func_decl * constructor);
     func_decl * get_accessor_constructor(func_decl * accessor);
     func_decl * get_recognizer_constructor(func_decl * recognizer);
     family_id get_family_id() const { return m_family_id; }
diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index c059907d5..41ae2e839 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -37,6 +37,7 @@ namespace datatype {
     func_decl_ref accessor::instantiate(sort_ref_vector const& ps) const {
         ast_manager& m = ps.get_manager();
         unsigned n = ps.size();
+        SASSERT(m_range);
         SASSERT(n == get_def().params().size());
         sort_ref range(m.substitute(m_range, n, get_def().params().c_ptr(), ps.c_ptr()), m);
         sort_ref src(get_def().instantiate(ps));
@@ -79,13 +80,14 @@ namespace datatype {
         sort_ref s(m);
         if (!m_sort) {
             vector<parameter> ps;
+            ps.push_back(parameter(m_name));
             for (sort * s : m_params) ps.push_back(parameter(s));
             m_sort = m.mk_sort(u().get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr());
         }
         if (sorts.empty()) {
             return m_sort;
         }
-        return sort_ref(m.substitute(m_sort, sorts.size(), sorts.c_ptr(), m_params.c_ptr()), m);
+        return sort_ref(m.substitute(m_sort, sorts.size(), m_params.c_ptr(), sorts.c_ptr()), m);
     }
 
     enum status {
@@ -135,6 +137,7 @@ namespace datatype {
 
         util & plugin::u() const {
             SASSERT(m_manager);
+            SASSERT(m_family_id != null_family_id);
             if (m_util.get() == 0) {
                 m_util = alloc(util, *m_manager);
             }
@@ -146,9 +149,11 @@ namespace datatype {
         sort * plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
             try {
                 if (k != DATATYPE_SORT) {
+                    TRACE("datatype", tout << "invalid kind parameter to datatype\n";);
                     throw invalid_datatype();
                 }
                 if (num_parameters < 1) {
+                    TRACE("datatype", tout << "at least one parameter expected to datatype declaration\n";);
                     throw invalid_datatype();                    
                 }
                 parameter const & name = parameters[0];
@@ -169,13 +174,17 @@ namespace datatype {
                 def* d = 0;
                 if (m_defs.find(s->get_name(), d) && d->sort_size()) {
                     obj_map<sort, sort_size> S;
-                    for (unsigned i = 1; i < num_parameters; ++i) {
-                        sort* r = to_sort(parameters[i].get_ast());
+                    for (unsigned i = 0; i + 1 < num_parameters; ++i) {
+                        sort* r = to_sort(parameters[i + 1].get_ast());
                         S.insert(d->params()[i], r->get_num_elements()); 
                     }
                     sort_size ts = d->sort_size()->eval(S);
+                    TRACE("datatype", tout << name << " has size " << ts << "\n";);
                     s->set_num_elements(ts);
                 }
+                else {
+                    TRACE("datatype", tout << "not setting size for " << name << "\n";);
+                }
                 return s;
             }
             catch (invalid_datatype) {
@@ -227,14 +236,12 @@ namespace datatype {
             return m.mk_func_decl(symbol("update-field"), arity, domain, range, info);
         }
 
+#define VALIDATE_PARAM(_pred_) if (!(_pred_)) m_manager->raise_exception("invalid parameter to datatype function");
         
         func_decl * decl::plugin::mk_constructor(unsigned num_parameters, parameter const * parameters, 
                                                  unsigned arity, sort * const * domain, sort * range) {
             ast_manager& m = *m_manager;
-            SASSERT(num_parameters == 1 && parameters[0].is_symbol() && range && u().is_datatype(range));
-            if (num_parameters != 1 || !parameters[0].is_symbol() || !range || !u().is_datatype(range)) {
-                m_manager->raise_exception("invalid parameters for datatype constructor");
-            }
+            VALIDATE_PARAM(num_parameters == 1 && parameters[0].is_symbol() && range && u().is_datatype(range));
             // we blindly trust other conditions are met, including domain types.
             symbol name = parameters[0].get_symbol();
             func_decl_info info(m_family_id, OP_DT_CONSTRUCTOR, num_parameters, parameters);
@@ -245,27 +252,27 @@ namespace datatype {
         func_decl * decl::plugin::mk_recognizer(unsigned num_parameters, parameter const * parameters, 
                                                 unsigned arity, sort * const * domain, sort *) {
             ast_manager& m = *m_manager;
-            SASSERT(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
-            SASSERT(u().is_datatype(domain[0]));
+            VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
+            VALIDATE_PARAM(u().is_datatype(domain[0]));
             // blindly trust that parameter is a constructor
             sort* range = m_manager->mk_bool_sort();
+            func_decl* f = to_func_decl(parameters[0].get_ast());
             func_decl_info info(m_family_id, OP_DT_RECOGNISER, num_parameters, parameters);
             info.m_private_parameters = true;
-            symbol name = to_func_decl(parameters[0].get_ast())->get_name();
-            return m.mk_func_decl(name, arity, domain, range);
+            return m.mk_func_decl(symbol("is"), arity, domain, range, info);
         }
 
         func_decl * decl::plugin::mk_accessor(unsigned num_parameters, parameter const * parameters, 
                                               unsigned arity, sort * const * domain, sort * range) 
         {            
             ast_manager& m = *m_manager;
-            SASSERT(arity == 1 && num_parameters == 2 && parameters[0].is_symbol() && parameters[0].is_symbol());
-            SASSERT(u().is_datatype(domain[0]));
+            VALIDATE_PARAM(arity == 1 && num_parameters == 2 && parameters[0].is_symbol() && parameters[1].is_symbol());
+            VALIDATE_PARAM(u().is_datatype(domain[0]));
             SASSERT(range);
             func_decl_info info(m_family_id, OP_DT_ACCESSOR, num_parameters, parameters);
             info.m_private_parameters = true;
             symbol name = parameters[0].get_symbol();
-            return m.mk_func_decl(name, arity, domain, range);            
+            return m.mk_func_decl(name, arity, domain, range, info);           
         }
 
         func_decl * decl::plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, 
@@ -290,15 +297,20 @@ namespace datatype {
             return alloc(def, m, u(), name, m_class_id, n, params);
         }
 
+#if 0
         def& plugin::add(symbol const& name, unsigned n, sort * const * params) {
             ast_manager& m = *m_manager;
             def* d = 0;
-            if (m_defs.find(name, d)) dealloc(d);
+            if (m_defs.find(name, d)) {
+                TRACE("datatype", tout << "delete previous version for " << name << "\n";);
+                dealloc(d);
+            }
             d = alloc(def, m, u(), name, m_class_id, n, params);
             m_defs.insert(name, d);
             m_def_block.push_back(name);
             return *d;
         }
+#endif
 
         void plugin::end_def_block() {
             ast_manager& m = *m_manager;
@@ -325,7 +337,11 @@ namespace datatype {
         bool plugin::mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts) {
             begin_def_block();
             for (unsigned i = 0; i < num_datatypes; ++i) {
-                if (m_defs.find(datatypes[i]->name(), d)) dealloc(d);
+                def* d = 0;
+                if (m_defs.find(datatypes[i]->name(), d)) {
+                    TRACE("datatype", tout << "delete previous version for " << datatypes[i]->name() << "\n";);
+                    dealloc(d);
+                }
                 m_defs.insert(datatypes[i]->name(), datatypes[i]);
                 m_def_block.push_back(datatypes[i]->name());
             }
@@ -391,6 +407,7 @@ namespace datatype {
         }
         
         void plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
+            op_names.push_back(builtin_name("is", OP_DT_RECOGNISER));
             if (logic == symbol::null) {
                 op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD));
             }
@@ -548,6 +565,10 @@ namespace datatype {
         }
         return param_size::size::mk_offset(s->get_num_elements());        
     }
+
+    bool util::is_declared(sort* s) const {
+        return m_plugin->is_declared(s);
+    }
     
     void util::compute_datatype_size_functions(svector<symbol> const& names) {
         map<symbol, status, symbol_hash_proc, symbol_eq_proc> already_found;
@@ -668,17 +689,18 @@ namespace datatype {
         m_asts(m),
         m_start(0) {
         m_plugin = dynamic_cast<decl::plugin*>(m.get_plugin(m_family_id));
+        SASSERT(m_plugin);
     }
 
     util::~util() {
         std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc<ptr_vector<func_decl> >());
     }
 
-    ptr_vector<func_decl> const & util::get_datatype_constructors(sort * ty) {
+    ptr_vector<func_decl> const * util::get_datatype_constructors(sort * ty) {
         SASSERT(is_datatype(ty));
         ptr_vector<func_decl> * r = 0;
         if (m_datatype2constructors.find(ty, r))
-            return *r;
+            return r;
         r = alloc(ptr_vector<func_decl>);
         m_asts.push_back(ty);
         m_vectors.push_back(r);
@@ -689,14 +711,15 @@ namespace datatype {
             m_asts.push_back(f);
             r->push_back(f);
         }
-        return *r;
+        return r;
     }
 
-    ptr_vector<func_decl> const & util::get_constructor_accessors(func_decl * con) {
+    ptr_vector<func_decl> const * util::get_constructor_accessors(func_decl * con) {
         SASSERT(is_constructor(con));
         ptr_vector<func_decl> * res = 0;
-        if (m_constructor2accessors.find(con, res))
-            return *res;
+        if (m_constructor2accessors.find(con, res)) {
+            return res;
+        }
         res = alloc(ptr_vector<func_decl>);
         m_asts.push_back(con);
         m_vectors.push_back(res);
@@ -706,12 +729,14 @@ namespace datatype {
         for (constructor const* c : d) {
             if (c->name() == con->get_name()) {
                 for (accessor const* a : *c) {
-                    res->push_back(a->instantiate(datatype));
+                    func_decl_ref fn = a->instantiate(datatype);
+                    res->push_back(fn);
+                    m_asts.push_back(fn);
                 }
                 break;
             }
         }
-        return *res;
+        return res;
     }
 
     func_decl * util::get_constructor_recognizer(func_decl * constructor) {
@@ -752,7 +777,7 @@ namespace datatype {
         bool r = false;
         if (m_is_enum.find(s, r))
             return r;
-        ptr_vector<func_decl> const& cnstrs = get_datatype_constructors(s);
+        ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
         r = true;
         for (unsigned i = 0; r && i < cnstrs.size(); ++i) {
             r = cnstrs[i]->get_arity() == 0;
@@ -833,7 +858,7 @@ namespace datatype {
         //   1) T_i's are not recursive
         // If there is no such constructor, then we select one that 
         //   2) each type T_i is not recursive or contains a constructor that does not depend on T
-        ptr_vector<func_decl> const& constructors = get_datatype_constructors(ty);
+        ptr_vector<func_decl> const& constructors = *get_datatype_constructors(ty);
         // step 1)
         unsigned sz = constructors.size();
         ++m_start;
@@ -928,12 +953,12 @@ namespace datatype {
             todo.pop_back();
             strm << s->get_name() << " =\n";
 
-            ptr_vector<func_decl> const& cnstrs = get_datatype_constructors(s);
+            ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
             for (unsigned i = 0; i < cnstrs.size(); ++i) {
                 func_decl* cns = cnstrs[i];
                 func_decl* rec = get_constructor_recognizer(cns);
                 strm << "  " << cns->get_name() << " :: " << rec->get_name() << " :: ";
-                ptr_vector<func_decl> const & accs = get_constructor_accessors(cns);
+                ptr_vector<func_decl> const & accs = *get_constructor_accessors(cns);
                 for (unsigned j = 0; j < accs.size(); ++j) {
                     func_decl* acc = accs[j];
                     sort* s1 = acc->get_range();
@@ -949,9 +974,9 @@ namespace datatype {
     }
 }
 
-datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_constructors, constructor_decl * const * cs) {
+datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs) {
     datatype::decl::plugin* p = u.get_plugin();
-    datatype::def( d = p->mk(n, 0, 0);
+    datatype::def* d = p->mk(n, num_params, params);
     for (unsigned i = 0; i < num_constructors; ++i) {
         d->add(cs[i]);
     }
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index c79939a5b..d7173b24a 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -50,19 +50,19 @@ namespace datatype {
  
 
     class accessor {
-        symbol   m_name;
-        sort*    m_range;
+        symbol    m_name;
+        sort_ref  m_range;
         unsigned m_index;    // reference to recursive data-type may only get resolved after all mutually recursive data-types are procssed.
         constructor* m_constructor;
     public:
-        accessor(symbol const& n, sort* range):
+        accessor(ast_manager& m, symbol const& n, sort* range):
             m_name(n),
-            m_range(range),
+            m_range(range, m),
             m_index(UINT_MAX)
         {}
-        accessor(symbol const& n, unsigned index):
+        accessor(ast_manager& m, symbol const& n, unsigned index):
             m_name(n),
-            m_range(0),
+            m_range(m),
             m_index(index)
         {}
         sort* range() const { return m_range; }
@@ -262,8 +262,6 @@ namespace datatype {
 
             void end_def_block();
 
-            def& add(symbol const& name, unsigned n, sort * const * params);
-
             def* mk(symbol const& name, unsigned n, sort * const * params);
 
             void del(symbol const& d);
@@ -272,7 +270,7 @@ namespace datatype {
 
             def const& get_def(sort* s) const { return *(m_defs[datatype_name(s)]); }
             def& get_def(symbol const& s) { return *(m_defs[s]); }
-
+            bool is_declared(sort* s) const { return m_defs.contains(datatype_name(s)); }
         private:
             bool is_value_visit(expr * arg, ptr_buffer<app> & todo) const;
         
@@ -337,6 +335,7 @@ namespace datatype {
         util(ast_manager & m);
         ~util();
         ast_manager & get_manager() const { return m; }
+        // sort * mk_datatype_sort(symbol const& name, unsigned n, sort* const* params); 
         bool is_datatype(sort const* s) const { return is_sort_of(s, m_family_id, DATATYPE_SORT); }
         bool is_enum_sort(sort* s);
         bool is_recursive(sort * ty);
@@ -348,13 +347,13 @@ namespace datatype {
         bool is_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); }
         bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
         bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
-        ptr_vector<func_decl> const & get_datatype_constructors(sort * ty);
+        ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
         unsigned get_datatype_num_constructors(sort * ty);
         unsigned get_datatype_num_parameter_sorts(sort * ty);
         sort*  get_datatype_parameter_sort(sort * ty, unsigned idx);
         func_decl * get_non_rec_constructor(sort * ty);
         func_decl * get_constructor_recognizer(func_decl * constructor);
-        ptr_vector<func_decl> const & get_constructor_accessors(func_decl * constructor);
+        ptr_vector<func_decl> const * get_constructor_accessors(func_decl * constructor);
         func_decl * get_accessor_constructor(func_decl * accessor);
         func_decl * get_recognizer_constructor(func_decl * recognizer) const;
         family_id get_family_id() const { return m_family_id; }
@@ -362,11 +361,13 @@ namespace datatype {
         bool is_func_decl(op_kind k, unsigned num_params, parameter const* params, func_decl* f);
         bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f);
         void reset();
+        bool is_declared(sort* s) const;
         void display_datatype(sort *s, std::ostream& strm);
         bool is_fully_interp(sort * s) const;
         sort_ref_vector datatype_params(sort * s) const;
         unsigned get_constructor_idx(func_decl * f) const;
         unsigned get_recognizer_constructor_idx(func_decl * f) const;
+        decl::plugin* get_plugin() { return m_plugin; }
     };
 
 };
@@ -390,12 +391,12 @@ public:
     int get_idx() const { return UNBOXINT(m_data); }
 };
 
-inline accessor_decl * mk_accessor_decl(symbol const & n, type_ref const & t) {
+inline accessor_decl * mk_accessor_decl(ast_manager& m, symbol const & n, type_ref const & t) {
     if (t.is_idx()) {
-        return alloc(accessor_decl, n, t.get_idx());
+        return alloc(accessor_decl, m, n, t.get_idx());
     }
     else {
-        return alloc(accessor_decl, n, t.get_sort());
+        return alloc(accessor_decl, m, n, t.get_sort());
     }
 }
 
@@ -410,7 +411,7 @@ inline constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r
 
 
 // Remark: the datatype becomes the owner of the constructor_decls
-datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_constructors, constructor_decl * const * cs);
+datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs);
 inline void del_datatype_decl(datatype_decl * d) {}
 inline void del_datatype_decls(unsigned num, datatype_decl * const * ds) {}
 
diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp
index bf509aba5..e000f43df 100644
--- a/src/ast/decl_collector.cpp
+++ b/src/ast/decl_collector.cpp
@@ -28,9 +28,9 @@ void decl_collector::visit_sort(sort * n) {
 
         unsigned num_cnstr = m_dt_util.get_datatype_num_constructors(n);
         for (unsigned i = 0; i < num_cnstr; i++) {
-            func_decl * cnstr = m_dt_util.get_datatype_constructors(n).get(i);
+            func_decl * cnstr = m_dt_util.get_datatype_constructors(n)->get(i);
             m_decls.push_back(cnstr);
-            ptr_vector<func_decl> const & cnstr_acc = m_dt_util.get_constructor_accessors(cnstr);
+            ptr_vector<func_decl> const & cnstr_acc = *m_dt_util.get_constructor_accessors(cnstr);
             unsigned num_cas = cnstr_acc.size();
             for (unsigned j = 0; j < num_cas; j++) {
                 func_decl * accsr = cnstr_acc.get(j);
diff --git a/src/ast/rewriter/datatype_rewriter.cpp b/src/ast/rewriter/datatype_rewriter.cpp
index 8746fab86..9efa61f70 100644
--- a/src/ast/rewriter/datatype_rewriter.cpp
+++ b/src/ast/rewriter/datatype_rewriter.cpp
@@ -47,7 +47,7 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr
         func_decl * c_decl = a->get_decl();
         if (c_decl != m_util.get_accessor_constructor(f))
             return BR_FAILED;
-        ptr_vector<func_decl> const & acc = m_util.get_constructor_accessors(c_decl);
+        ptr_vector<func_decl> const & acc = *m_util.get_constructor_accessors(c_decl);
         SASSERT(acc.size() == a->get_num_args());
         unsigned num = acc.size();
         for (unsigned i = 0; i < num; ++i) {
@@ -70,7 +70,7 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr
             result = a;
             return BR_DONE;
         }
-        ptr_vector<func_decl> const & acc = m_util.get_constructor_accessors(c_decl);
+        ptr_vector<func_decl> const & acc = *m_util.get_constructor_accessors(c_decl);
         SASSERT(acc.size() == a->get_num_args());
         unsigned num = acc.size();
         ptr_buffer<expr> new_args;
diff --git a/src/ast/rewriter/enum2bv_rewriter.cpp b/src/ast/rewriter/enum2bv_rewriter.cpp
index b2ecbcc24..eb6b195f0 100644
--- a/src/ast/rewriter/enum2bv_rewriter.cpp
+++ b/src/ast/rewriter/enum2bv_rewriter.cpp
@@ -130,7 +130,7 @@ struct enum2bv_rewriter::imp {
                     m_imp.m_bounds.push_back(m_bv.mk_ule(result, m_bv.mk_numeral(nc-1, bv_size)));
                 }                
                 expr_ref f_def(m);
-                ptr_vector<func_decl> const& cs = m_dt.get_datatype_constructors(s);
+                ptr_vector<func_decl> const& cs = *m_dt.get_datatype_constructors(s);
                 f_def = m.mk_const(cs[nc-1]);
                 for (unsigned i = nc - 1; i > 0; ) {
                     --i;
diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp
index 109fe1718..21f1cfe27 100644
--- a/src/cmd_context/cmd_context.cpp
+++ b/src/cmd_context/cmd_context.cpp
@@ -682,8 +682,6 @@ bool cmd_context::logic_has_datatype() const {
 void cmd_context::init_manager_core(bool new_manager) {
     SASSERT(m_manager != 0);
     SASSERT(m_pmanager != 0);
-    m_dt_eh    = alloc(dt_eh, *this);
-    m_pmanager->set_new_datatype_eh(m_dt_eh.get());
     if (new_manager) {
         decl_plugin * basic = m_manager->get_plugin(m_manager->get_basic_family_id());
         register_builtin_sorts(basic);
@@ -719,6 +717,8 @@ void cmd_context::init_manager_core(bool new_manager) {
             }
         }
     }
+    m_dt_eh = alloc(dt_eh, *this);
+    m_pmanager->set_new_datatype_eh(m_dt_eh.get());
     if (!has_logic()) {
         // add list type only if the logic is not specified.
         // it prevents clashes with builtin types.
@@ -795,6 +795,7 @@ void cmd_context::insert(symbol const & s, func_decl * f) {
     dictionary<func_decls>::entry * e = m_func_decls.insert_if_not_there2(s, func_decls());
     func_decls & fs = e->get_data().m_value;
     if (!fs.insert(m(), f)) {
+        UNREACHABLE();
         std::string msg = "invalid declaration, ";
         msg += f->get_arity() == 0 ? "constant" : "function";
         msg += " '";
@@ -1954,21 +1955,17 @@ cmd_context::dt_eh::~dt_eh() {
 
 void cmd_context::dt_eh::operator()(sort * dt, pdecl* pd) {
     TRACE("new_dt_eh", tout << "new datatype: "; m_owner.pm().display(tout, dt); tout << "\n";);
-    ptr_vector<func_decl> const & constructors = m_dt_util.get_datatype_constructors(dt);
-    unsigned num_constructors = constructors.size();
-    for (unsigned j = 0; j < num_constructors; j++) {
-        func_decl * c = constructors[j];
-        m_owner.insert(c);
+    for (func_decl * c : *m_dt_util.get_datatype_constructors(dt)) {
         TRACE("new_dt_eh", tout << "new constructor: " << c->get_name() << "\n";);
+        m_owner.insert(c);        
+#ifndef DATATYPE_V2
         func_decl * r = m_dt_util.get_constructor_recognizer(c);
         m_owner.insert(r);
         TRACE("new_dt_eh", tout << "new recognizer: " << r->get_name() << "\n";);
-        ptr_vector<func_decl> const & accessors = m_dt_util.get_constructor_accessors(c);
-        unsigned num_accessors = accessors.size();
-        for (unsigned k = 0; k < num_accessors; k++) {
-            func_decl * a = accessors[k];
-            m_owner.insert(a);
+#endif
+        for (func_decl * a : *m_dt_util.get_constructor_accessors(c)) {
             TRACE("new_dt_eh", tout << "new accessor: " << a->get_name() << "\n";);
+            m_owner.insert(a);            
         }
     }
     if (m_owner.m_scopes.size() > 0) {
diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp
index 04456c076..4417061ec 100644
--- a/src/cmd_context/pdecl.cpp
+++ b/src/cmd_context/pdecl.cpp
@@ -170,9 +170,10 @@ public:
     virtual char const * hcons_kind() const { return "psort_var"; }
     virtual unsigned hcons_hash() const { return hash_u_u(m_num_params, m_idx); }
     virtual bool hcons_eq(psort const * other) const {
-        if (other->hcons_kind() != hcons_kind())
-            return false;
-        return get_num_params() == other->get_num_params() && m_idx == static_cast<psort_var const *>(other)->m_idx;
+        return 
+            other->hcons_kind() == hcons_kind() && 
+            get_num_params() == other->get_num_params() && 
+            m_idx == static_cast<psort_var const *>(other)->m_idx;
     }
     virtual void display(std::ostream & out) const {
         out << "s_" << m_idx;
@@ -344,6 +345,53 @@ void psort_user_decl::display(std::ostream & out) const {
     out << ")";
 }
 
+// -------------------
+// psort_dt_decl
+
+psort_dt_decl::psort_dt_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n) :
+    psort_decl(id, num_params, m, n) {
+    m_psort_kind = PSORT_DT;
+}
+
+
+sort * psort_dt_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
+#ifndef DATATYPE_V2
+    UNREACHABLE();
+    return 0;
+#else
+    SASSERT(n == m_num_params);
+    sort * r = find(s);
+    if (r)
+        return r;
+    buffer<parameter> ps;
+    ps.push_back(parameter(m_name));
+    for (unsigned i = 0; i < n; i++)
+        ps.push_back(parameter(s[i]));
+    datatype_util util(m.m());
+    r = m.m().mk_sort(util.get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr());
+    cache(m, s, r);
+    m.save_info(r, this, n, s);
+    if (m_num_params > 0 && util.is_declared(r)) {
+        bool has_typevar = false;
+        // crude check ..
+        for (unsigned i = 0; !has_typevar && i < n; ++i) {
+            has_typevar = s[i]->get_name().is_numerical();
+        }
+        if (!has_typevar) {
+            m.notify_new_dt(r, this);
+        }
+    }
+    return r;
+#endif
+}
+
+void psort_dt_decl::display(std::ostream & out) const {
+    out << "(datatype-sort " << m_name << ")";
+}
+
+// -------------------
+// psort_builtin_decl
+
 psort_builtin_decl::psort_builtin_decl(unsigned id, pdecl_manager & m, symbol const & n, family_id fid, decl_kind k):
     psort_decl(id, PSORT_DECL_VAR_PARAMS, m, n),
     m_fid(fid),
@@ -435,8 +483,8 @@ bool paccessor_decl::fix_missing_refs(dictionary<int> const & symbol2idx, symbol
 
 accessor_decl * paccessor_decl::instantiate_decl(pdecl_manager & m, sort * const * s) {
     switch (m_type.kind()) {
-    case PTR_REC_REF: return mk_accessor_decl(m_name, type_ref(m_type.get_idx()));
-    case PTR_PSORT:   return mk_accessor_decl(m_name, type_ref(m_type.get_psort()->instantiate(m, s)));
+    case PTR_REC_REF: return mk_accessor_decl(m.m(), m_name, type_ref(m_type.get_idx()));
+    case PTR_PSORT:   return mk_accessor_decl(m.m(), m_name, type_ref(m_type.get_psort()->instantiate(m, s)));
     default:
         // missing refs must have been eliminated.
         UNREACHABLE();
@@ -546,7 +594,7 @@ datatype_decl * pdatatype_decl::instantiate_decl(pdecl_manager & m, sort * const
         cs.push_back(c->instantiate_decl(m, s));
     }
     datatype_util util(m.m());
-    return mk_datatype_decl(util, m_name, cs.size(), cs.c_ptr());
+    return mk_datatype_decl(util, m_name, m_num_params, s, cs.size(), cs.c_ptr());
 }
 
 struct datatype_decl_buffer {
@@ -554,6 +602,12 @@ struct datatype_decl_buffer {
     ~datatype_decl_buffer() { del_datatype_decls(m_buffer.size(), m_buffer.c_ptr()); }
 };
 
+#ifdef DATATYPE_V2
+sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
+    UNREACHABLE();
+    return 0;
+}
+#else
 sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
     SASSERT(m_num_params == n);
     sort * r = find(s);
@@ -583,6 +637,7 @@ sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const *
     }
     return 0;
 }
+#endif
 
 void pdatatype_decl::display(std::ostream & out) const {
     out << "(declare-datatype " << m_name;
@@ -603,6 +658,27 @@ void pdatatype_decl::display(std::ostream & out) const {
     out << ")";
 }
 
+#ifdef DATATYPE_V2
+bool pdatatype_decl::commit(pdecl_manager& m) {
+    sort_ref_vector ps(m.m());
+    for (unsigned i = 0; i < m_num_params; ++i) {
+        ps.push_back(m.m().mk_uninterpreted_sort(symbol(i), 0, 0));
+    }
+    datatype_decl_buffer dts;
+    dts.m_buffer.push_back(instantiate_decl(m, ps.c_ptr()));
+    datatype_decl * d_ptr = dts.m_buffer[0];
+    sort_ref_vector sorts(m.m());
+    bool is_ok = m.get_dt_plugin()->mk_datatypes(1, &d_ptr, m_num_params, ps.c_ptr(), sorts);
+    if (is_ok) {
+        if (m_num_params == 0) {
+            m.notify_new_dt(sorts.get(0), this);        
+        }
+    }
+    return is_ok;
+}
+#endif
+
+
 pdatatypes_decl::pdatatypes_decl(unsigned id, unsigned num_params, pdecl_manager & m,
                                  unsigned num_datatypes, pdatatype_decl * const * dts):
     pdecl(id, num_params),
@@ -631,6 +707,12 @@ bool pdatatypes_decl::fix_missing_refs(symbol & missing) {
     return true;
 }
 
+#ifdef DATATYPE_V2
+bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) {
+    UNREACHABLE();
+    return false;
+}
+#else
 bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) {
     datatype_decl_buffer dts;
     for (auto d : m_datatypes) {
@@ -649,6 +731,31 @@ bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) {
     }
     return true;
 }
+#endif
+
+#ifdef DATATYPE_V2
+bool pdatatypes_decl::commit(pdecl_manager& m) {
+    datatype_decl_buffer dts;
+    for (pdatatype_decl* d : m_datatypes) {
+        sort_ref_vector ps(m.m());
+        for (unsigned i = 0; i < d->get_num_params(); ++i) {
+            ps.push_back(m.m().mk_uninterpreted_sort(symbol(i), 0, 0));
+        }        
+        dts.m_buffer.push_back(d->instantiate_decl(m, ps.c_ptr()));
+    }
+    sort_ref_vector sorts(m.m());
+    bool is_ok = m.get_dt_plugin()->mk_datatypes(m_datatypes.size(), dts.m_buffer.c_ptr(), 0, nullptr, sorts);
+    if (is_ok) {
+        for (unsigned i = 0; i < m_datatypes.size(); ++i) {
+            pdatatype_decl* d = m_datatypes[i];
+            if (d->get_num_params() == 0) {
+                m.notify_new_dt(sorts.get(i), this);        
+            }
+        }
+    }
+    return is_ok;
+}
+#endif
 
 struct pdecl_manager::sort_info {
     psort_decl * m_decl;
@@ -790,9 +897,8 @@ psort * pdecl_manager::register_psort(psort * n) {
     psort * r = m_table.insert_if_not_there(n);
     if (r != n) {
         del_decl_core(n);
-        return r;
     }
-    return n;
+    return r;
 }
 
 psort * pdecl_manager::mk_psort_var(unsigned num_params, unsigned vidx) {
@@ -837,6 +943,11 @@ psort_decl * pdecl_manager::mk_psort_user_decl(unsigned num_params, symbol const
     return new (a().allocate(sizeof(psort_user_decl))) psort_user_decl(m_id_gen.mk(), num_params, *this, n, def);
 }
 
+psort_decl * pdecl_manager::mk_psort_dt_decl(unsigned num_params, symbol const & n) {
+    // std::cout << "insert dt-psort: " << n << " " << num_params << "\n";
+    return new (a().allocate(sizeof(psort_dt_decl))) psort_dt_decl(m_id_gen.mk(), num_params, *this, n);    
+}
+
 
 psort_decl * pdecl_manager::mk_psort_builtin_decl(symbol const & n, family_id fid, decl_kind k) {
     return new (a().allocate(sizeof(psort_builtin_decl))) psort_builtin_decl(m_id_gen.mk(), *this, n, fid, k);
diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h
index e7fae8dd5..414415255 100644
--- a/src/cmd_context/pdecl.h
+++ b/src/cmd_context/pdecl.h
@@ -87,7 +87,7 @@ typedef ptr_hashtable<psort, psort_hash_proc, psort_eq_proc> psort_table;
 
 #define PSORT_DECL_VAR_PARAMS UINT_MAX
 
-typedef enum { PSORT_BASE = 0, PSORT_USER, PSORT_BUILTIN } psort_decl_kind;
+typedef enum { PSORT_BASE = 0, PSORT_USER, PSORT_BUILTIN, PSORT_DT } psort_decl_kind;
 
 class psort_decl : public pdecl {
 protected:
@@ -111,6 +111,7 @@ public:
     virtual void reset_cache(pdecl_manager& m);
     bool is_user_decl() const { return m_psort_kind == PSORT_USER; }
     bool is_builtin_decl() const { return m_psort_kind == PSORT_BUILTIN; }
+    bool is_dt_decl() const { return m_psort_kind == PSORT_DT; }
 };
 
 class psort_user_decl : public psort_decl {
@@ -125,7 +126,7 @@ public:
     virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s);
     virtual void display(std::ostream & out) const;
 };
-
+ 
 class psort_builtin_decl : public psort_decl {
 protected:
     friend class pdecl_manager;
@@ -140,10 +141,17 @@ public:
     virtual void display(std::ostream & out) const;
 };
 
-//class datatype_decl_plugin;
-//class datatype_decl;
-//class constructor_decl;
-//class accessor_decl;
+class psort_dt_decl : public psort_decl {
+protected:
+    friend class pdecl_manager;
+    psort_dt_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n);
+    virtual size_t obj_size() const { return sizeof(psort_dt_decl); }
+    virtual ~psort_dt_decl() {}
+public:
+    virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s);
+    virtual void display(std::ostream & out) const;
+};
+
 
 class pdatatypes_decl;
 class pdatatype_decl;
@@ -233,6 +241,9 @@ public:
     virtual void display(std::ostream & out) const;
     bool has_missing_refs(symbol & missing) const;
     bool has_duplicate_accessors(symbol & repeated) const;
+#ifdef DATATYPE_V2
+    bool commit(pdecl_manager& m);
+#endif
 };
 
 /**
@@ -250,6 +261,10 @@ class pdatatypes_decl : public pdecl {
     virtual ~pdatatypes_decl() {}
 public:
     pdatatype_decl const * const * children() const { return m_datatypes.c_ptr(); }
+#ifdef DATATYPE_V2
+    // commit declaration 
+    bool commit(pdecl_manager& m);
+#endif
 };
 
 class new_datatype_eh {
@@ -292,7 +307,7 @@ public:
     psort * mk_psort_var(unsigned num_params, unsigned vidx);
     psort * mk_psort_app(unsigned num_params, psort_decl * d, unsigned num_args, psort * const * args);
     psort * mk_psort_app(psort_decl * d);
-    // psort_decl * mk_psort_dt_decl(unsigned num_params, symbol const & n);
+    psort_decl * mk_psort_dt_decl(unsigned num_params, symbol const & n);
     psort_decl * mk_psort_user_decl(unsigned num_params, symbol const & n, psort * def);
     psort_decl * mk_psort_builtin_decl(symbol const & n, family_id fid, decl_kind k);
     paccessor_decl * mk_paccessor_decl(unsigned num_params, symbol const & s, ptype const & p);
diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h
index 51d673ba5..ea0e64e4f 100644
--- a/src/muz/base/dl_rule.h
+++ b/src/muz/base/dl_rule.h
@@ -65,7 +65,7 @@ namespace datalog {
             else if (m_dt.is_accessor(n)) {
                 sort* s = m.get_sort(n->get_arg(0));
                 SASSERT(m_dt.is_datatype(s));
-                if (m_dt.get_datatype_constructors(s).size() > 1) {
+                if (m_dt.get_datatype_constructors(s)->size() > 1) {
                     m_found = true;
                     m_func = n->get_decl();
                 }
diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp
index 75487bbfb..21317a07c 100644
--- a/src/muz/base/rule_properties.cpp
+++ b/src/muz/base/rule_properties.cpp
@@ -191,7 +191,7 @@ void rule_properties::operator()(app* n) {
     else if (m_dt.is_accessor(n)) {
         sort* s = m.get_sort(n->get_arg(0));
         SASSERT(m_dt.is_datatype(s));
-        if (m_dt.get_datatype_constructors(s).size() > 1) {
+        if (m_dt.get_datatype_constructors(s)->size() > 1) {
             m_uninterp_funs.insert(n->get_decl(), m_rule);
         }
     }
diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp
index 9109f6b3c..3368c7640 100644
--- a/src/muz/bmc/dl_bmc_engine.cpp
+++ b/src/muz/bmc/dl_bmc_engine.cpp
@@ -808,7 +808,7 @@ namespace datalog {
             datatype_util dtu(m);
             ptr_vector<sort> sorts;
             func_decl* p = r.get_decl();
-            ptr_vector<func_decl> const& succs  = dtu.get_datatype_constructors(m.get_sort(path));
+            ptr_vector<func_decl> const& succs  = *dtu.get_datatype_constructors(m.get_sort(path));
             // populate substitution of bound variables.
             r.get_vars(m, sorts);
             sub.reset();
@@ -871,8 +871,8 @@ namespace datalog {
                 path_var  = m.mk_var(0, m_path_sort);
                 trace_var = m.mk_var(1, pred_sort);
                 // sort* sorts[2] = { pred_sort, m_path_sort };
-                ptr_vector<func_decl> const& cnstrs = dtu.get_datatype_constructors(pred_sort);
-                ptr_vector<func_decl> const& succs  = dtu.get_datatype_constructors(m_path_sort);
+                ptr_vector<func_decl> const& cnstrs = *dtu.get_datatype_constructors(pred_sort);
+                ptr_vector<func_decl> const& succs  = *dtu.get_datatype_constructors(m_path_sort);
                 SASSERT(cnstrs.size() == rls.size());
                 pred = m.mk_app(mk_predicate(p), trace_var.get(), path_var.get());
                 for (unsigned i = 0; i < rls.size(); ++i) {
@@ -970,7 +970,7 @@ namespace datalog {
                         _name << pred->get_name() << "_" << q->get_name() << j;
                         symbol name(_name.str().c_str());
                         type_ref tr(idx);
-                        accs.push_back(mk_accessor_decl(name, tr));
+                        accs.push_back(mk_accessor_decl(m, name, tr));
                     }
                     std::stringstream _name;
                     _name << pred->get_name() << "_" << i;
@@ -979,7 +979,7 @@ namespace datalog {
                     symbol is_name(_name.str().c_str());
                     cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr()));
                 }
-                dts.push_back(mk_datatype_decl(dtu, pred->get_name(), cnstrs.size(), cnstrs.c_ptr()));
+                dts.push_back(mk_datatype_decl(dtu, pred->get_name(), 0, nullptr, cnstrs.size(), cnstrs.c_ptr()));
             }
 
 
@@ -1024,10 +1024,10 @@ namespace datalog {
                     _name2 << "get_succ#" << i;
                     ptr_vector<accessor_decl> accs;
                     type_ref tr(0);
-                    accs.push_back(mk_accessor_decl(name, tr));
+                    accs.push_back(mk_accessor_decl(m, name, tr));
                     cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr()));
                 }
-                dts.push_back(mk_datatype_decl(dtu, symbol("Path"), cnstrs.size(), cnstrs.c_ptr()));
+                dts.push_back(mk_datatype_decl(dtu, symbol("Path"), 0, nullptr, cnstrs.size(), cnstrs.c_ptr()));
                 VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, 0, new_sorts));
                 m_path_sort = new_sorts[0].get();
             }
@@ -1039,8 +1039,8 @@ namespace datalog {
             sort* trace_sort = m.get_sort(trace);
             func_decl* p = m_sort2pred.find(trace_sort);
             datalog::rule_vector const& rules = b.m_rules.get_predicate_rules(p);
-            ptr_vector<func_decl> const& cnstrs = dtu.get_datatype_constructors(trace_sort);
-            ptr_vector<func_decl> const& succs  = dtu.get_datatype_constructors(m_path_sort);
+            ptr_vector<func_decl> const& cnstrs = *dtu.get_datatype_constructors(trace_sort);
+            ptr_vector<func_decl> const& succs  = *dtu.get_datatype_constructors(m_path_sort);
             for (unsigned i = 0; i < cnstrs.size(); ++i) {
                 if (trace->get_decl() == cnstrs[i]) {
                     svector<std::pair<unsigned, unsigned> > positions;
diff --git a/src/muz/pdr/pdr_prop_solver.cpp b/src/muz/pdr/pdr_prop_solver.cpp
index 1bca8e925..3055985f4 100644
--- a/src/muz/pdr/pdr_prop_solver.cpp
+++ b/src/muz/pdr/pdr_prop_solver.cpp
@@ -135,7 +135,7 @@ namespace pdr {
                     func_decl* f = to_app(val)->get_decl();
                     func_decl* r = dt.get_constructor_recognizer(f);
                     conjs[i] = m.mk_app(r, c);
-                    ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(f);
+                    ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f);
                     for (unsigned j = 0; j < acc.size(); ++j) {
                         conjs.push_back(m.mk_eq(apply_accessor(acc, j, f, c), to_app(val)->get_arg(j)));
                     }
diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp
index 14d8899c5..a277c9ed6 100644
--- a/src/muz/spacer/spacer_util.cpp
+++ b/src/muz/spacer/spacer_util.cpp
@@ -711,7 +711,7 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs)
                 func_decl* f = to_app(val)->get_decl();
                 func_decl* r = dt.get_constructor_recognizer(f);
                 conjs[i] = m.mk_app(r, c);
-                ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(f);
+                ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f);
                 for (unsigned j = 0; j < acc.size(); ++j) {
                     conjs.push_back(m.mk_eq(apply_accessor(m, acc, j, f, c), to_app(val)->get_arg(j)));
                 }
diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp
index 3d895668b..9526429cb 100644
--- a/src/parsers/smt2/smt2parser.cpp
+++ b/src/parsers/smt2/smt2parser.cpp
@@ -16,8 +16,6 @@ Author:
 Revision History:
 
 --*/
-#include "parsers/smt2/smt2parser.h"
-#include "parsers/smt2/smt2scanner.h"
 #include "util/stack.h"
 #include "ast/datatype_decl_plugin.h"
 #include "ast/bv_decl_plugin.h"
@@ -25,10 +23,12 @@ Revision History:
 #include "ast/seq_decl_plugin.h"
 #include "ast/ast_pp.h"
 #include "ast/well_sorted.h"
-#include "parsers/util/pattern_validation.h"
 #include "ast/rewriter/rewriter.h"
 #include "ast/has_free_vars.h"
 #include "ast/ast_smt2_pp.h"
+#include "parsers/smt2/smt2parser.h"
+#include "parsers/smt2/smt2scanner.h"
+#include "parsers/util/pattern_validation.h"
 #include "parsers/util/parser_params.hpp"
 #include<sstream>
 
@@ -885,6 +885,9 @@ namespace smt2 {
             }
             else if (sz == 1) {
                 check_missing(new_dt_decls[0], line, pos);
+#ifdef DATATYPE_V2
+                new_dt_decls[0]->commit(pm());
+#endif
             }
             else {
                 SASSERT(sz > 1);
@@ -897,8 +900,13 @@ namespace smt2 {
                     err_msg += "'";
                     throw parser_exception(err_msg, line, pos);
                 }
+#ifndef DATATYPE_V2
                 m_ctx.insert_aux_pdecl(dts.get());
+#else
+                dts->commit(pm());
+#endif
             }
+#ifndef DATATYPE_V2
             for (unsigned i = 0; i < sz; i++) {
                 pdatatype_decl * d = new_dt_decls[i];
                 SASSERT(d != 0);
@@ -911,6 +919,13 @@ namespace smt2 {
                     s = d->instantiate(pm(), 0, 0);
                 }
             }
+#else
+            for (unsigned i = 0; i < sz; i++) {
+                pdatatype_decl * d = new_dt_decls[i];
+                symbol duplicated;
+                check_duplicate(d, line, pos);
+            }                
+#endif
             TRACE("declare_datatypes", tout << "i: " << i << " new_dt_decls.size(): " << sz << "\n";
                   for (unsigned i = 0; i < sz; i++) tout << new_dt_decls[i]->get_name() << "\n";);
             m_ctx.print_success();
@@ -940,12 +955,16 @@ namespace smt2 {
             check_missing(d, line, pos);
             check_duplicate(d, line, pos);
 
+#ifndef DATATYPE_V2
             m_ctx.insert(d);
             if (d->get_num_params() == 0) {
                 // if datatype is not parametric... then force instantiation to register accessor, recognizers and constructors...
                 sort_ref s(m());
                 s = d->instantiate(pm(), 0, 0);
             }
+#else
+            d->commit(pm());
+#endif
             check_rparen_next("invalid end of datatype declaration, ')' expected");
             m_ctx.print_success();
         }
@@ -1909,6 +1928,8 @@ namespace smt2 {
                     m_dt_name2idx.insert(dt_name, i);
                     m_dt_name2arity.insert(dt_name, u);
                     m_dt_names.push_back(dt_name);
+                    psort_decl * decl = pm().mk_psort_dt_decl(u, dt_name);
+                    m_ctx.insert(decl);
                     check_rparen("invalid sort declaration, ')' expected");
                 }
                 else {
diff --git a/src/qe/qe_datatype_plugin.cpp b/src/qe/qe_datatype_plugin.cpp
index eff230ffe..c3525aa33 100644
--- a/src/qe/qe_datatype_plugin.cpp
+++ b/src/qe/qe_datatype_plugin.cpp
@@ -262,7 +262,7 @@ namespace qe {
             }
             func_decl* c = a->get_decl();
             func_decl* r = m_util.get_constructor_recognizer(c);
-            ptr_vector<func_decl> const & acc = m_util.get_constructor_accessors(c);
+            ptr_vector<func_decl> const & acc = *m_util.get_constructor_accessors(c);
             SASSERT(acc.size() == a->get_num_args());
             //
             // It suffices to solve just the first available equality.
@@ -379,7 +379,7 @@ namespace qe {
                 return false;
             }
             func_decl* c = l->get_decl();
-            ptr_vector<func_decl> const& acc = m_util.get_constructor_accessors(c);
+            ptr_vector<func_decl> const& acc = *m_util.get_constructor_accessors(c);
             func_decl* rec = m_util.get_constructor_recognizer(c);
             expr_ref_vector conj(m);
             conj.push_back(m.mk_app(rec, r));
@@ -626,7 +626,7 @@ namespace qe {
             // If 'x' does not yet have a recognizer, then branch according to recognizers.
             // 
             if (!has_recognizer(x, fml, r, c)) {
-                c = m_datatype_util.get_datatype_constructors(s)[vl.get_unsigned()];
+                c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned());
                 r = m_datatype_util.get_constructor_recognizer(c);
                 app* is_c = m.mk_app(r, x);                
                 // assert v => r(x)            
@@ -673,7 +673,7 @@ namespace qe {
             // Introduce auxiliary variable to eliminate.
             // 
             if (!has_recognizer(x, fml, r, c)) {
-                c = m_datatype_util.get_datatype_constructors(s)[vl.get_unsigned()];
+                c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned());
                 r = m_datatype_util.get_constructor_recognizer(c);
                 app* is_c = m.mk_app(r, x);                
                 fml = m.mk_and(is_c, fml);
@@ -774,7 +774,7 @@ namespace qe {
                 return;
             }
             
-            c = m_datatype_util.get_datatype_constructors(s)[vl.get_unsigned()];
+            c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned());
             r = m_datatype_util.get_constructor_recognizer(c);
             app* is_c = m.mk_app(r, x);
             
@@ -794,7 +794,7 @@ namespace qe {
             else {
                 SASSERT(vl.is_unsigned());
                 SASSERT(vl.get_unsigned() < m_datatype_util.get_datatype_num_constructors(s));
-                c = m_datatype_util.get_datatype_constructors(s)[vl.get_unsigned()];
+                c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned());
             }
             subst_constructor(x, c, fml, def);                
         }
diff --git a/src/qe/qe_datatypes.cpp b/src/qe/qe_datatypes.cpp
index f16bdda59..db1e6ec85 100644
--- a/src/qe/qe_datatypes.cpp
+++ b/src/qe/qe_datatypes.cpp
@@ -75,7 +75,7 @@ namespace qe {
             app_ref arg(m);
             SASSERT(dt.is_constructor(m_val));
             func_decl* f = m_val->get_decl();
-            ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(f);
+            ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f);
             for (unsigned i = 0; i < acc.size(); ++i) {
                 arg = m.mk_fresh_const(acc[i]->get_name().str().c_str(), acc[i]->get_range());
                 model.register_decl(arg->get_decl(), m_val->get_arg(i));
@@ -152,7 +152,7 @@ namespace qe {
             }
             func_decl* c = a->get_decl();
             func_decl* rec = dt.get_constructor_recognizer(c);
-            ptr_vector<func_decl> const & acc = dt.get_constructor_accessors(c);
+            ptr_vector<func_decl> const & acc = *dt.get_constructor_accessors(c);
             SASSERT(acc.size() == a->get_num_args());
             //
             // It suffices to solve just the first available equality.
@@ -230,7 +230,7 @@ namespace qe {
                 return false;
             }
             func_decl* c = to_app(l)->get_decl();
-            ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(c);
+            ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(c);
             if (!is_app_of(r, c)) {
                 lits.push_back(m.mk_app(dt.get_constructor_recognizer(c), r));
             }
diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp
index 10af3be25..257331161 100644
--- a/src/qe/qe_lite.cpp
+++ b/src/qe/qe_lite.cpp
@@ -671,7 +671,7 @@ namespace eq {
             else {
                 func_decl* rec = dt.get_constructor_recognizer(d);
                 conjs.push_back(m.mk_app(rec, r));
-                ptr_vector<func_decl> const& acc = dt.get_constructor_accessors(d);
+                ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(d);
                 for (unsigned i = 0; i < acc.size(); ++i) {
                     conjs.push_back(m.mk_eq(c->get_arg(i), m.mk_app(acc[i], r)));
                 }
diff --git a/src/smt/proto_model/datatype_factory.cpp b/src/smt/proto_model/datatype_factory.cpp
index 653ef034a..03f008fe6 100644
--- a/src/smt/proto_model/datatype_factory.cpp
+++ b/src/smt/proto_model/datatype_factory.cpp
@@ -88,7 +88,7 @@ expr * datatype_factory::get_almost_fresh_value(sort * s) {
     // Traverse constructors, and try to invoke get_fresh_value of one of the arguments (if the argument is not a sibling datatype of s).
     // If the argumet is a sibling datatype of s, then
     // use get_last_fresh_value.
-    ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(s);
+    ptr_vector<func_decl> const & constructors = *m_util.get_datatype_constructors(s);
     for (func_decl * constructor : constructors) {
         expr_ref_vector args(m_manager);
         bool found_fresh_arg = false;
@@ -151,7 +151,7 @@ expr * datatype_factory::get_fresh_value(sort * s) {
     // Traverse constructors, and try to invoke get_fresh_value of one of the 
     // arguments (if the argument is not a sibling datatype of s).
     // Two datatypes are siblings if they were defined together in the same mutually recursive definition.
-    ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(s);
+    ptr_vector<func_decl> const & constructors = *m_util.get_datatype_constructors(s);
     for (func_decl * constructor : constructors) {
         expr_ref_vector args(m_manager);
         bool found_fresh_arg = false;
@@ -189,7 +189,7 @@ expr * datatype_factory::get_fresh_value(sort * s) {
         while(true) {
             ++num_iterations;
             TRACE("datatype_factory", tout << mk_pp(get_last_fresh_value(s), m_manager) << "\n";);
-            ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(s);
+            ptr_vector<func_decl> const & constructors = *m_util.get_datatype_constructors(s);
             for (func_decl * constructor : constructors) {
                 expr_ref_vector args(m_manager);
                 bool found_sibling   = false;
diff --git a/src/smt/smt_value_sort.cpp b/src/smt/smt_value_sort.cpp
index 3eeb3461d..56768b91a 100644
--- a/src/smt/smt_value_sort.cpp
+++ b/src/smt/smt_value_sort.cpp
@@ -52,7 +52,7 @@ namespace smt {
                 // simple
             }
             else if (data.is_datatype(s)) {
-                ptr_vector<func_decl> const& cs = data.get_datatype_constructors(s);
+                ptr_vector<func_decl> const& cs = *data.get_datatype_constructors(s);
                 for (unsigned i = 0; i < cs.size(); ++i) {
                     func_decl* f = cs[i];
                     for (unsigned j = 0; j < f->get_arity(); ++j) {
diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp
index 33b4b194d..616314117 100644
--- a/src/smt/theory_datatype.cpp
+++ b/src/smt/theory_datatype.cpp
@@ -17,13 +17,13 @@ Revision History:
 
 --*/
 
+#include "util/stats.h"
+#include "ast/ast_pp.h"
+#include "ast/ast_ll_pp.h"
+#include "ast/ast_smt2_pp.h"
 #include "smt/smt_context.h"
 #include "smt/theory_datatype.h"
 #include "smt/smt_model_generator.h"
-#include "ast/ast_pp.h"
-#include "ast/ast_ll_pp.h"
-#include "util/stats.h"
-#include "ast/ast_smt2_pp.h"
 
 namespace smt {
     
@@ -97,7 +97,7 @@ namespace smt {
         SASSERT(m_util.is_datatype(get_manager().get_sort(n->get_owner())));
         ast_manager & m = get_manager();
         ptr_vector<expr> args;
-        ptr_vector<func_decl> const & accessors   = m_util.get_constructor_accessors(c);
+        ptr_vector<func_decl> const & accessors   = *m_util.get_constructor_accessors(c);
         SASSERT(c->get_arity() == accessors.size());
         for (func_decl * d : accessors) {
             SASSERT(d->get_arity() == 1);
@@ -120,7 +120,7 @@ namespace smt {
         SASSERT(is_constructor(n));
         ast_manager & m   = get_manager();
         func_decl * d     = n->get_decl();
-        ptr_vector<func_decl> const & accessors   = m_util.get_constructor_accessors(d);
+        ptr_vector<func_decl> const & accessors   = *m_util.get_constructor_accessors(d);
         SASSERT(n->get_num_args() == accessors.size());
         unsigned i = 0;
         for (func_decl * acc : accessors) {
@@ -168,7 +168,7 @@ namespace smt {
         func_decl * acc  = to_func_decl(upd->get_parameter(0).get_ast());
         func_decl * con  = m_util.get_accessor_constructor(acc);
         func_decl * rec  = m_util.get_constructor_recognizer(con);
-        ptr_vector<func_decl> const & accessors   = m_util.get_constructor_accessors(con);
+        ptr_vector<func_decl> const & accessors = *m_util.get_constructor_accessors(con);
         app_ref rec_app(m.mk_app(rec, arg1), m);
         ctx.internalize(rec_app, false);
         literal is_con(ctx.get_bool_var(rec_app));
@@ -208,7 +208,7 @@ namespace smt {
             ast_manager & m = get_manager();
             sort * s      = m.get_sort(n->get_owner());
             if (m_util.get_datatype_num_constructors(s) == 1) {
-                func_decl * c = m_util.get_datatype_constructors(s)[0];
+                func_decl * c = m_util.get_datatype_constructors(s)->get(0);
                 assert_is_constructor_axiom(n, c, null_literal);
             }
             else {
@@ -709,7 +709,7 @@ namespace smt {
             enode * r = d->m_recognizers[unassigned_idx];
             literal consequent;
             if (!r) {
-                ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(dt);
+                ptr_vector<func_decl> const & constructors = *m_util.get_datatype_constructors(dt);
                 func_decl * rec = m_util.get_constructor_recognizer(constructors[unassigned_idx]);
                 app * rec_app   = get_manager().mk_app(rec, n->get_owner());
                 ctx.internalize(rec_app, false);
@@ -774,7 +774,7 @@ namespace smt {
                 for (unsigned idx = 0; it != end; ++it, ++idx) {
                     enode * curr = *it;
                     if (curr == 0) {
-                        ptr_vector<func_decl> const & constructors = m_util.get_datatype_constructors(s);
+                        ptr_vector<func_decl> const & constructors = *m_util.get_datatype_constructors(s);
                         // found empty slot...
                         r = m_util.get_constructor_recognizer(constructors[idx]);
                         break;
@@ -793,7 +793,7 @@ namespace smt {
         }
         SASSERT(r != 0);
         app * r_app     = m.mk_app(r, n->get_owner());
-        TRACE("datatype", tout << "creating split: " << mk_bounded_pp(r_app, m) << "\n";);
+        TRACE("datatype", tout << "creating split: " << mk_pp(r_app, m) << "\n";);
         ctx.internalize(r_app, false);
         bool_var bv     = ctx.get_bool_var(r_app);
         ctx.set_true_first_flag(bv);
diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp
index a13cd54d8..6a38f787e 100644
--- a/src/tactic/core/elim_uncnstr_tactic.cpp
+++ b/src/tactic/core/elim_uncnstr_tactic.cpp
@@ -174,7 +174,7 @@ class elim_uncnstr_tactic : public tactic {
                 if (fid == m_dt_util.get_family_id()) {
                     // In the current implementation, I only handle the case where
                     // the datatype has a recursive constructor.
-                    ptr_vector<func_decl> const & constructors = m_dt_util.get_datatype_constructors(s);
+                    ptr_vector<func_decl> const & constructors = *m_dt_util.get_datatype_constructors(s);
                     for (func_decl * constructor : constructors) {
                         unsigned num    = constructor->get_arity();
                         unsigned target = UINT_MAX;
@@ -704,7 +704,7 @@ class elim_uncnstr_tactic : public tactic {
                         app * u;
                         if (!mk_fresh_uncnstr_var_for(f, num, args, u))
                             return u;
-                        ptr_vector<func_decl> const & accs = m_dt_util.get_constructor_accessors(c);
+                        ptr_vector<func_decl> const & accs = *m_dt_util.get_constructor_accessors(c);
                         ptr_buffer<expr> new_args;
                         for (unsigned i = 0; i < accs.size(); i++) {
                             if (accs[i] == f) 
@@ -723,7 +723,7 @@ class elim_uncnstr_tactic : public tactic {
                             return u;
                         if (!m_mc)
                             return u;
-                        ptr_vector<func_decl> const & accs = m_dt_util.get_constructor_accessors(f);
+                        ptr_vector<func_decl> const & accs = *m_dt_util.get_constructor_accessors(f);
                         for (unsigned i = 0; i < num; i++) {
                             add_def(args[i], m().mk_app(accs[i], u));
                         }
diff --git a/src/tactic/portfolio/enum2bv_solver.cpp b/src/tactic/portfolio/enum2bv_solver.cpp
index db7cebd6e..36a178c41 100644
--- a/src/tactic/portfolio/enum2bv_solver.cpp
+++ b/src/tactic/portfolio/enum2bv_solver.cpp
@@ -136,7 +136,7 @@ public:
             if (m.is_eq(b, u, v) && is_uninterp_const(u) && m_rewriter.bv2enum().find(to_app(u)->get_decl(), f) && bv.is_numeral(v, num, bvsize)) {
                 SASSERT(num.is_unsigned());
                 expr_ref head(m);
-                ptr_vector<func_decl> const& enums = dt.get_datatype_constructors(f->get_range());
+                ptr_vector<func_decl> const& enums = *dt.get_datatype_constructors(f->get_range());
                 if (enums.size() > num.get_unsigned()) {
                     head = m.mk_eq(m.mk_const(f), m.mk_const(enums[num.get_unsigned()]));
                     consequences[i] = m.mk_implies(a, head);

From aac7773a525b71fa6aab771163718ae9266d4835 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Mon, 4 Sep 2017 21:15:44 -0700
Subject: [PATCH 58/74] support for smtlib2.6 datatype parsing

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/cmd_context/cmd_context.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp
index 21f1cfe27..e93e51186 100644
--- a/src/cmd_context/cmd_context.cpp
+++ b/src/cmd_context/cmd_context.cpp
@@ -795,7 +795,6 @@ void cmd_context::insert(symbol const & s, func_decl * f) {
     dictionary<func_decls>::entry * e = m_func_decls.insert_if_not_there2(s, func_decls());
     func_decls & fs = e->get_data().m_value;
     if (!fs.insert(m(), f)) {
-        UNREACHABLE();
         std::string msg = "invalid declaration, ";
         msg += f->get_arity() == 0 ? "constant" : "function";
         msg += " '";

From 06087c17be591ce889927d07397e334e07ce8d79 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 5 Sep 2017 10:28:11 -0700
Subject: [PATCH 59/74] support for legacy datatype test

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/datatype_decl_plugin.h         |  2 +-
 src/ast/datatype_decl_plugin2.cpp      | 60 +++++++++++++++-----------
 src/ast/datatype_decl_plugin2.h        | 21 ++++++---
 src/ast/rewriter/datatype_rewriter.cpp |  1 +
 src/ast/rewriter/poly_rewriter_def.h   |  4 --
 src/ast/static_features.cpp            | 13 +++---
 src/cmd_context/basic_cmds.cpp         | 42 ++++++++----------
 src/cmd_context/cmd_context.cpp        | 38 ++++++----------
 src/cmd_context/pdecl.cpp              |  2 +-
 src/cmd_context/tactic_cmds.cpp        |  6 +--
 src/smt/theory_dense_diff_logic_def.h  |  5 ++-
 src/test/get_consequences.cpp          |  4 +-
 12 files changed, 100 insertions(+), 98 deletions(-)

diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h
index 3b4c8dd08..840329dda 100644
--- a/src/ast/datatype_decl_plugin.h
+++ b/src/ast/datatype_decl_plugin.h
@@ -16,7 +16,7 @@ Author:
 Revision History:
 
 --*/
-// define DATATYPE_V2
+#define DATATYPE_V2
 #ifdef DATATYPE_V2
 #include "ast/datatype_decl_plugin2.h"
 #else
diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 41ae2e839..fa3c73bca 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -236,7 +236,7 @@ namespace datatype {
             return m.mk_func_decl(symbol("update-field"), arity, domain, range, info);
         }
 
-#define VALIDATE_PARAM(_pred_) if (!(_pred_)) m_manager->raise_exception("invalid parameter to datatype function");
+#define VALIDATE_PARAM(_pred_) if (!(_pred_)) m_manager->raise_exception("invalid parameter to datatype function "  #_pred_);
         
         func_decl * decl::plugin::mk_constructor(unsigned num_parameters, parameter const * parameters, 
                                                  unsigned arity, sort * const * domain, sort * range) {
@@ -252,13 +252,26 @@ namespace datatype {
         func_decl * decl::plugin::mk_recognizer(unsigned num_parameters, parameter const * parameters, 
                                                 unsigned arity, sort * const * domain, sort *) {
             ast_manager& m = *m_manager;
-            VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
+            VALIDATE_PARAM(arity == 1 && num_parameters == 2 && parameters[1].is_symbol() && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
             VALIDATE_PARAM(u().is_datatype(domain[0]));
             // blindly trust that parameter is a constructor
             sort* range = m_manager->mk_bool_sort();
             func_decl* f = to_func_decl(parameters[0].get_ast());
             func_decl_info info(m_family_id, OP_DT_RECOGNISER, num_parameters, parameters);
             info.m_private_parameters = true;
+            return m.mk_func_decl(symbol(parameters[1].get_symbol()), arity, domain, range, info);
+        }
+
+        func_decl * decl::plugin::mk_is(unsigned num_parameters, parameter const * parameters, 
+                                                unsigned arity, sort * const * domain, sort *) {
+            ast_manager& m = *m_manager;
+            VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
+            VALIDATE_PARAM(u().is_datatype(domain[0]));
+            // blindly trust that parameter is a constructor
+            sort* range = m_manager->mk_bool_sort();
+            func_decl* f = to_func_decl(parameters[0].get_ast());
+            func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters);
+            info.m_private_parameters = true;
             return m.mk_func_decl(symbol("is"), arity, domain, range, info);
         }
 
@@ -282,6 +295,8 @@ namespace datatype {
                 return mk_constructor(num_parameters, parameters, arity, domain, range);
             case OP_DT_RECOGNISER:
                 return mk_recognizer(num_parameters, parameters, arity, domain, range);                
+            case OP_DT_IS:
+                return mk_is(num_parameters, parameters, arity, domain, range);                
             case OP_DT_ACCESSOR:
                 return mk_accessor(num_parameters, parameters, arity, domain, range);                
             case OP_DT_UPDATE_FIELD: 
@@ -297,20 +312,6 @@ namespace datatype {
             return alloc(def, m, u(), name, m_class_id, n, params);
         }
 
-#if 0
-        def& plugin::add(symbol const& name, unsigned n, sort * const * params) {
-            ast_manager& m = *m_manager;
-            def* d = 0;
-            if (m_defs.find(name, d)) {
-                TRACE("datatype", tout << "delete previous version for " << name << "\n";);
-                dealloc(d);
-            }
-            d = alloc(def, m, u(), name, m_class_id, n, params);
-            m_defs.insert(name, d);
-            m_def_block.push_back(name);
-            return *d;
-        }
-#endif
 
         void plugin::end_def_block() {
             ast_manager& m = *m_manager;
@@ -407,7 +408,7 @@ namespace datatype {
         }
         
         void plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
-            op_names.push_back(builtin_name("is", OP_DT_RECOGNISER));
+            op_names.push_back(builtin_name("is", OP_DT_IS));
             if (logic == symbol::null) {
                 op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD));
             }
@@ -739,18 +740,25 @@ namespace datatype {
         return res;
     }
 
-    func_decl * util::get_constructor_recognizer(func_decl * constructor) {
-        SASSERT(is_constructor(constructor));
+    func_decl * util::get_constructor_recognizer(func_decl * con) {
+        SASSERT(is_constructor(con));
         func_decl * d = 0;
-        if (m_constructor2recognizer.find(constructor, d))
+        if (m_constructor2recognizer.find(con, d))
             return d;
-        sort * datatype = constructor->get_range();
-        parameter ps[1] = { parameter(constructor) };
-        d  = m.mk_func_decl(m_family_id, OP_DT_RECOGNISER, 1, ps, 1, &datatype);
+        sort * datatype = con->get_range();
+        def const& dd = get_def(datatype);
+        symbol r;
+        for (constructor const* c : dd) {
+            if (c->name() == con->get_name()) {
+                r = c->recognizer();
+            }
+        }
+        parameter ps[2] = { parameter(con), parameter(r) };
+        d  = m.mk_func_decl(m_family_id, OP_DT_RECOGNISER, 2, ps, 1, &datatype);
         SASSERT(d);
-        m_asts.push_back(constructor);
+        m_asts.push_back(con);
         m_asts.push_back(d);
-        m_constructor2recognizer.insert(constructor, d);
+        m_constructor2recognizer.insert(con, d);
         return d;
     }
 
@@ -917,11 +925,11 @@ namespace datatype {
         UNREACHABLE();
         return 0;
     }
+
     unsigned util::get_recognizer_constructor_idx(func_decl * f) const {
         return get_constructor_idx(get_recognizer_constructor(f));
     }
 
-
     /**
        \brief Two datatype sorts s1 and s2 are siblings if they were
        defined together in the same mutually recursive definition.
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index d7173b24a..f076bb43f 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -36,7 +36,8 @@ Revision History:
     enum op_kind {
         OP_DT_CONSTRUCTOR,
         OP_DT_RECOGNISER,
-        OP_DT_ACCESSOR,
+        OP_DT_IS,
+        OP_DT_ACCESSOR,        
         OP_DT_UPDATE_FIELD,
         LAST_DT_OP
     };
@@ -78,13 +79,15 @@ namespace datatype {
 
     class constructor {
         symbol           m_name;
+        symbol           m_recognizer;
         ptr_vector<accessor> m_accessors;
         def*             m_def;
     public:
-        constructor(symbol n): m_name(n) {}
+        constructor(symbol n, symbol const& r): m_name(n), m_recognizer(r) {}
         ~constructor();
         void add(accessor* a) { m_accessors.push_back(a); a->attach(this); }
         symbol const& name() const { return m_name; }
+        symbol const& recognizer() const { return m_recognizer; }
         ptr_vector<accessor> const& accessors() const { return m_accessors; }
         ptr_vector<accessor>::const_iterator begin() const { return m_accessors.begin(); }
         ptr_vector<accessor>::const_iterator end() const { return m_accessors.end(); }
@@ -290,6 +293,10 @@ namespace datatype {
                 unsigned num_parameters, parameter const * parameters, 
                 unsigned arity, sort * const * domain, sort * range);
 
+            func_decl * mk_is(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
             symbol datatype_name(sort * s) const {
                 //SASSERT(u().is_datatype(s));
                 return s->get_parameter(0).get_symbol();
@@ -340,11 +347,15 @@ namespace datatype {
         bool is_enum_sort(sort* s);
         bool is_recursive(sort * ty);
         bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
-        bool is_recognizer(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); }
+        bool is_recognizer(func_decl * f) const { return is_recognizer0(f) || is_is(f); }
+        bool is_recognizer0(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); }
+        bool is_is(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_IS); }
         bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); }
         bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
         bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
-        bool is_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); }
+        bool is_recognizer0(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER);} 
+        bool is_is(app * f) const { return is_app_of(f, m_family_id, OP_DT_IS);} 
+        bool is_recognizer(app * f) const { return is_recognizer0(f) || is_is(f); }
         bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
         bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
         ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
@@ -401,7 +412,7 @@ inline accessor_decl * mk_accessor_decl(ast_manager& m, symbol const & n, type_r
 }
 
 inline constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * * acs) {
-    constructor_decl* c = alloc(constructor_decl, n);
+    constructor_decl* c = alloc(constructor_decl, n, r);
     for (unsigned i = 0; i < num_accessors; ++i) {
         c->add(acs[i]);
     }
diff --git a/src/ast/rewriter/datatype_rewriter.cpp b/src/ast/rewriter/datatype_rewriter.cpp
index 9efa61f70..f0a95929b 100644
--- a/src/ast/rewriter/datatype_rewriter.cpp
+++ b/src/ast/rewriter/datatype_rewriter.cpp
@@ -23,6 +23,7 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr
     switch(f->get_decl_kind()) {
     case OP_DT_CONSTRUCTOR: return BR_FAILED;
     case OP_DT_RECOGNISER:
+    case OP_DT_IS:
         //
         // simplify is_cons(cons(x,y)) -> true
         // simplify is_cons(nil) -> false
diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h
index b52440393..39c4a1078 100644
--- a/src/ast/rewriter/poly_rewriter_def.h
+++ b/src/ast/rewriter/poly_rewriter_def.h
@@ -753,18 +753,14 @@ br_status poly_rewriter<Config>::cancel_monomials(expr * lhs, expr * rhs, bool m
 
     normalize(c);
 
-    TRACE("mk_le_bug", tout << c << "\n";);
-
     if (!has_multiple && num_coeffs <= 1) {
         if (move) {
             if (is_numeral(rhs)) {
-                TRACE("mk_le_bug", tout << "rhs is numeral\n";);
                 return BR_FAILED;
             }
         }
         else {
             if (num_coeffs == 0 || is_numeral(rhs)) {
-                TRACE("mk_le_bug", tout << "rhs is numeral or no coeffs\n";);
                 return BR_FAILED;
             }
         }
diff --git a/src/ast/static_features.cpp b/src/ast/static_features.cpp
index 3c8333d02..3db4d02a2 100644
--- a/src/ast/static_features.cpp
+++ b/src/ast/static_features.cpp
@@ -145,18 +145,19 @@ bool static_features::is_diff_atom(expr const * e) const {
         return true;    
     if (!is_numeral(rhs)) 
         return false;    
-    // lhs can be 'x' or '(+ x (* -1 y))'
+    // lhs can be 'x' or '(+ x (* -1 y))' or '(+ (* -1 x) y)'
     if (!is_arith_expr(lhs))
         return true;
     expr* arg1, *arg2;
     if (!m_autil.is_add(lhs, arg1, arg2)) 
         return false;    
-    // x
-    if (is_arith_expr(arg1))
-        return false;
-    // arg2: (* -1 y)
     expr* m1, *m2;
-    return m_autil.is_mul(arg2, m1, m2) &&  is_minus_one(m1) && !is_arith_expr(m2);
+    if (!is_arith_expr(arg1) && m_autil.is_mul(arg2, m1, m2) &&  is_minus_one(m1) && !is_arith_expr(m2))
+        return true;
+    if (!is_arith_expr(arg2) && m_autil.is_mul(arg1, m1, m2) &&  is_minus_one(m1) && !is_arith_expr(m2))
+        return true;
+    return false;
+    
 }
 
 bool static_features::is_gate(expr const * e) const {
diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp
index c81170ba4..21b66febd 100644
--- a/src/cmd_context/basic_cmds.cpp
+++ b/src/cmd_context/basic_cmds.cpp
@@ -15,21 +15,21 @@ Author:
 Notes:
 
 --*/
-#include "cmd_context/cmd_context.h"
+#include "util/gparams.h"
+#include "util/env_params.h"
 #include "util/version.h"
 #include "ast/ast_smt_pp.h"
 #include "ast/ast_smt2_pp.h"
 #include "ast/ast_pp.h"
-#include "model/model_smt2_pp.h"
 #include "ast/array_decl_plugin.h"
 #include "ast/pp.h"
+#include "ast/well_sorted.h"
+#include "ast/pp_params.hpp"
+#include "model/model_smt2_pp.h"
+#include "cmd_context/cmd_context.h"
 #include "cmd_context/cmd_util.h"
 #include "cmd_context/simplify_cmd.h"
 #include "cmd_context/eval_cmd.h"
-#include "util/gparams.h"
-#include "util/env_params.h"
-#include "ast/well_sorted.h"
-#include "ast/pp_params.hpp"
 
 class help_cmd : public cmd {
     svector<symbol> m_cmds;
@@ -79,19 +79,15 @@ public:
             }
             // named_cmd_lt is not a total order for commands, but this is irrelevant for Linux x Windows behavior
             std::sort(cmds.begin(), cmds.end(), named_cmd_lt());
-            vector<named_cmd>::const_iterator it2  = cmds.begin();
-            vector<named_cmd>::const_iterator end2 = cmds.end();
-            for (; it2 != end2; ++it2) {
-                display_cmd(ctx, it2->first, it2->second);
+            for (named_cmd const& nc : cmds) {
+                display_cmd(ctx, nc.first, nc.second);
             }
         }
         else {
-            svector<symbol>::const_iterator it  = m_cmds.begin();
-            svector<symbol>::const_iterator end = m_cmds.end();
-            for (; it != end; ++it) {
-                cmd * c = ctx.find_cmd(*it);
+            for (symbol const& s : m_cmds) {
+                cmd * c = ctx.find_cmd(s);
                 SASSERT(c);
-                display_cmd(ctx, *it, c);
+                display_cmd(ctx, s, c);
             }
         }
         ctx.regular_stream() << "\"\n";
@@ -136,11 +132,10 @@ ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", {
     ctx.get_check_sat_result()->get_model(m);
     ctx.regular_stream() << "(";
     dictionary<macro_decls> const & macros = ctx.get_macros();
-    dictionary<macro_decls>::iterator it  = macros.begin();
-    dictionary<macro_decls>::iterator end = macros.end();
-    for (bool first = true; it != end; ++it) {
-        symbol const & name = (*it).m_key;
-        macro_decls const & _m    = (*it).m_value;
+    bool first = true;
+    for (auto const& kv : macros) {
+        symbol const & name = kv.m_key;
+        macro_decls const & _m    = kv.m_value;
         for (auto md : _m) {
             if (md.m_domain.size() == 0 && ctx.m().is_bool(md.m_body)) {
                 expr_ref val(ctx.m());
@@ -211,14 +206,13 @@ static void print_core(cmd_context& ctx) {
     ptr_vector<expr> core;
     ctx.get_check_sat_result()->get_unsat_core(core);
     ctx.regular_stream() << "(";
-    ptr_vector<expr>::const_iterator it  = core.begin();
-    ptr_vector<expr>::const_iterator end = core.end();
-    for (bool first = true; it != end; ++it) {
+    bool first = true;
+    for (expr* e : core) {
     if (first)
         first = false;
     else
         ctx.regular_stream() << " ";
-    ctx.regular_stream() << mk_ismt2_pp(*it, ctx.m());
+    ctx.regular_stream() << mk_ismt2_pp(e, ctx.m());
     }
     ctx.regular_stream() << ")" << std::endl;
 }
diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp
index e93e51186..c0e1c9f9c 100644
--- a/src/cmd_context/cmd_context.cpp
+++ b/src/cmd_context/cmd_context.cpp
@@ -18,7 +18,10 @@ Notes:
 
 #include<signal.h>
 #include "util/tptr.h"
-#include "cmd_context/cmd_context.h"
+#include "util/cancel_eh.h"
+#include "util/scoped_ctrl_c.h"
+#include "util/dec_ref_util.h"
+#include "util/scoped_timer.h"
 #include "ast/func_decl_dependencies.h"
 #include "ast/arith_decl_plugin.h"
 #include "ast/bv_decl_plugin.h"
@@ -31,22 +34,19 @@ Notes:
 #include "ast/rewriter/var_subst.h"
 #include "ast/pp.h"
 #include "ast/ast_smt2_pp.h"
-#include "cmd_context/basic_cmds.h"
-#include "util/cancel_eh.h"
-#include "util/scoped_ctrl_c.h"
-#include "util/dec_ref_util.h"
 #include "ast/decl_collector.h"
 #include "ast/well_sorted.h"
-#include "model/model_evaluator.h"
 #include "ast/for_each_expr.h"
-#include "util/scoped_timer.h"
-#include "cmd_context/interpolant_cmds.h"
+#include "ast/rewriter/th_rewriter.h"
+#include "model/model_evaluator.h"
 #include "model/model_smt2_pp.h"
 #include "model/model_v2_pp.h"
 #include "model/model_params.hpp"
-#include "ast/rewriter/th_rewriter.h"
 #include "tactic/tactic_exception.h"
 #include "solver/smt_logics.h"
+#include "cmd_context/basic_cmds.h"
+#include "cmd_context/interpolant_cmds.h"
+#include "cmd_context/cmd_context.h"
 
 func_decls::func_decls(ast_manager & m, func_decl * f):
     m_decls(TAG(func_decl*, f, 0)) {
@@ -61,11 +61,9 @@ void func_decls::finalize(ast_manager & m) {
     else {
         TRACE("func_decls", tout << "finalize...\n";);
         func_decl_set * fs = UNTAG(func_decl_set *, m_decls);
-        func_decl_set::iterator it  = fs->begin();
-        func_decl_set::iterator end = fs->end();
-        for (; it != end; ++it) {
-            TRACE("func_decls", tout << "dec_ref of " << (*it)->get_name() << " ref_count: " << (*it)->get_ref_count() << "\n";);
-            m.dec_ref(*it);
+        for (func_decl * f : *fs) {
+            TRACE("func_decls", tout << "dec_ref of " << f->get_name() << " ref_count: " << f->get_ref_count() << "\n";);
+            m.dec_ref(f);
         }
         dealloc(fs);
     }
@@ -161,10 +159,7 @@ bool func_decls::clash(func_decl * f) const {
     if (GET_TAG(m_decls) == 0)
         return false;
     func_decl_set * fs = UNTAG(func_decl_set *, m_decls);
-    func_decl_set::iterator it  = fs->begin();
-    func_decl_set::iterator end = fs->end();
-    for (; it != end; ++it) {
-        func_decl * g = *it;
+    for (func_decl * g : *fs) {
         if (g == f)
             continue;
         if (g->get_arity() != f->get_arity())
@@ -201,10 +196,7 @@ func_decl * func_decls::find(unsigned arity, sort * const * domain, sort * range
     if (!more_than_one())
         return first();
     func_decl_set * fs = UNTAG(func_decl_set *, m_decls);
-    func_decl_set::iterator it  = fs->begin();
-    func_decl_set::iterator end = fs->end();
-    for (; it != end; it++) {
-        func_decl * f = *it;
+    for (func_decl * f : *fs) {
         if (range != 0 && f->get_range() != range)
             continue;
         if (f->get_arity() != arity)
@@ -1957,11 +1949,9 @@ void cmd_context::dt_eh::operator()(sort * dt, pdecl* pd) {
     for (func_decl * c : *m_dt_util.get_datatype_constructors(dt)) {
         TRACE("new_dt_eh", tout << "new constructor: " << c->get_name() << "\n";);
         m_owner.insert(c);        
-#ifndef DATATYPE_V2
         func_decl * r = m_dt_util.get_constructor_recognizer(c);
         m_owner.insert(r);
         TRACE("new_dt_eh", tout << "new recognizer: " << r->get_name() << "\n";);
-#endif
         for (func_decl * a : *m_dt_util.get_constructor_accessors(c)) {
             TRACE("new_dt_eh", tout << "new accessor: " << a->get_name() << "\n";);
             m_owner.insert(a);            
diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp
index 4417061ec..9d6c5065e 100644
--- a/src/cmd_context/pdecl.cpp
+++ b/src/cmd_context/pdecl.cpp
@@ -267,8 +267,8 @@ public:
 
 psort_decl::psort_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n):
     pdecl(id, num_params),
-    m_psort_kind(PSORT_BASE),
     m_name(n),
+    m_psort_kind(PSORT_BASE),
     m_inst_cache(0) {
 }
 
diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp
index b5ad0707d..3eafc6bbd 100644
--- a/src/cmd_context/tactic_cmds.cpp
+++ b/src/cmd_context/tactic_cmds.cpp
@@ -255,11 +255,9 @@ public:
             result->m_core.append(core_elems.size(), core_elems.c_ptr());
             if (p.get_bool("print_unsat_core", false)) {
                 ctx.regular_stream() << "(unsat-core";
-                ptr_vector<expr>::const_iterator it  = core_elems.begin();
-                ptr_vector<expr>::const_iterator end = core_elems.end();
-                for (; it != end; ++it) {
+                for (expr * e : core_elems) {
                     ctx.regular_stream() << " ";
-                    ctx.display(ctx.regular_stream(), *it);
+                    ctx.display(ctx.regular_stream(), e);
                 }
                 ctx.regular_stream() << ")" << std::endl;
             }
diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h
index 342766c04..ad7727a89 100644
--- a/src/smt/theory_dense_diff_logic_def.h
+++ b/src/smt/theory_dense_diff_logic_def.h
@@ -127,7 +127,7 @@ namespace smt {
         if (!m_non_diff_logic_exprs) {
             TRACE("non_diff_logic", tout << "found non diff logic expression:\n" << mk_pp(n, get_manager()) << "\n";);
             get_context().push_trail(value_trail<context, bool>(m_non_diff_logic_exprs));
-        IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, get_manager()) << ")\n";); 
+            IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, get_manager()) << ")\n";); 
             m_non_diff_logic_exprs = true;
         }
     }
@@ -154,6 +154,9 @@ namespace smt {
         if (m_autil.is_add(lhs) && to_app(lhs)->get_num_args() == 2 && is_times_minus_one(to_app(lhs)->get_arg(1), s)) {
             t = to_app(to_app(lhs)->get_arg(0));
         }
+        else if (m_autil.is_add(lhs) && to_app(lhs)->get_num_args() == 2 && is_times_minus_one(to_app(lhs)->get_arg(0), s)) {
+            t = to_app(to_app(lhs)->get_arg(1));
+        }
         else if (m_autil.is_mul(lhs) && to_app(lhs)->get_num_args() == 2 && m_autil.is_minus_one(to_app(lhs)->get_arg(0))) {
             s = to_app(to_app(lhs)->get_arg(1));
             t = mk_zero_for(s);
diff --git a/src/test/get_consequences.cpp b/src/test/get_consequences.cpp
index 229cbe834..b9a54245e 100644
--- a/src/test/get_consequences.cpp
+++ b/src/test/get_consequences.cpp
@@ -65,12 +65,12 @@ void test2() {
     constructor_decl* G = mk_constructor_decl(symbol("G"), symbol("is-G"), 0, 0);
     constructor_decl* B = mk_constructor_decl(symbol("B"), symbol("is-B"), 0, 0);
     constructor_decl* constrs[3] = { R, G, B };
-    datatype_decl * enum_sort = mk_datatype_decl(dtutil, symbol("RGB"), 3, constrs);
+    datatype_decl * enum_sort = mk_datatype_decl(dtutil, symbol("RGB"), 0, nullptr, 3, constrs);
     VERIFY(dt.mk_datatypes(1, &enum_sort, 0, 0, new_sorts));    
     sort* rgb = new_sorts[0].get();
 
     expr_ref x = mk_const(m, "x", rgb), y = mk_const(m, "y", rgb), z = mk_const(m, "z", rgb);
-    ptr_vector<func_decl> const& enums = dtutil.get_datatype_constructors(rgb);
+    ptr_vector<func_decl> const& enums = *dtutil.get_datatype_constructors(rgb);
     expr_ref r = expr_ref(m.mk_const(enums[0]), m);
     expr_ref g = expr_ref(m.mk_const(enums[1]), m);
     expr_ref b = expr_ref(m.mk_const(enums[2]), m);

From 1f551f19f5ca16761d664b58d507d7776a7efaa1 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 5 Sep 2017 16:37:07 -0700
Subject: [PATCH 60/74] remove extra token

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/datatype_decl_plugin2.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index f076bb43f..d9bfea4a5 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -429,4 +429,4 @@ inline void del_datatype_decls(unsigned num, datatype_decl * const * ds) {}
 
 #endif /* DATATYPE_DECL_PLUGIN_H_ */
 
-#endif DATATYPE_V2
+#endif /* DATATYPE_V2 */

From c708691a506bef18db13e1963b69fccbc420387d Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 5 Sep 2017 17:24:29 -0700
Subject: [PATCH 61/74] merge

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/datatype_decl_plugin2.cpp | 9 +++++++++
 src/ast/datatype_decl_plugin2.h   | 2 +-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index bd7b41a88..a7d41dcbd 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -78,6 +78,7 @@ namespace datatype {
 
     sort_ref def::instantiate(sort_ref_vector const& sorts) const {
         sort_ref s(m);
+        TRACE("datatype", tout << "instantiate " << m_name << "\n";);
         if (!m_sort) {
             vector<parameter> ps;
             ps.push_back(parameter(m_name));
@@ -315,6 +316,7 @@ namespace datatype {
 
         void plugin::end_def_block() {
             ast_manager& m = *m_manager;
+
             sort_ref_vector sorts(m);
             for (symbol const& s : m_def_block) {
                 def const& d = *m_defs[s];
@@ -332,7 +334,12 @@ namespace datatype {
             if (!u().is_well_founded(sorts.size(), sorts.c_ptr())) {
                 m_manager->raise_exception("datatype is not well-founded");
             }
+
             u().compute_datatype_size_functions(m_def_block);
+            for (symbol const& s : m_def_block) {
+                sort_ref_vector ps(m);
+                m_defs[s]->instantiate(ps);
+            }
         }
 
         bool plugin::mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts) {
@@ -579,6 +586,8 @@ namespace datatype {
         status st;
         while (!todo.empty()) {
             symbol s = todo.back();
+            TRACE("datatype", tout << "Sort size for " << s << "\n";);
+
             if (already_found.find(s, st) && st == BLACK) {
                 todo.pop_back();
                 continue;
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index d9bfea4a5..a5db0dbc6 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -227,7 +227,7 @@ namespace datatype {
         sort_ref_vector const& params() const { return m_params; }
         util& u() const { return m_util; }
         param_size::size* sort_size() { return m_sort_size; }
-        void set_sort_size(param_size::size* p) { m_sort_size = p; p->inc_ref(); }
+        void set_sort_size(param_size::size* p) { m_sort_size = p; p->inc_ref(); m_sort = 0; }
     };
 
     namespace decl {

From dabf88b95d9178ffcb7d88b9e04eec700fca7d02 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 5 Sep 2017 17:40:22 -0700
Subject: [PATCH 62/74] rename del to remove to avoid compiler error

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/datatype_decl_plugin2.cpp | 2 +-
 src/ast/datatype_decl_plugin2.h   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index a7d41dcbd..74750d2ca 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -361,7 +361,7 @@ namespace datatype {
             return true;
         }
 
-        void plugin::del(symbol const& s) {
+        void plugin::remove(symbol const& s) {
             def* d = 0;
             if (m_defs.find(s, d)) dealloc(d);
             m_defs.remove(s);
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index a5db0dbc6..3ea2ad1da 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -267,7 +267,7 @@ namespace datatype {
 
             def* mk(symbol const& name, unsigned n, sort * const * params);
 
-            void del(symbol const& d);
+            void remove(symbol const& d);
 
             bool mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts);
 

From 9f5bd2feda960dbdd10977026478f3fcae792eb9 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 5 Sep 2017 19:58:05 -0700
Subject: [PATCH 63/74] fix front-end for datatype

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/rewriter/arith_rewriter.cpp |  2 +-
 src/cmd_context/pdecl.cpp           |  8 ++++----
 src/parsers/smt2/smt2parser.cpp     | 12 +++---------
 src/test/get_consequences.cpp       |  2 +-
 4 files changed, 9 insertions(+), 15 deletions(-)

diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp
index 275290665..631e1d8f3 100644
--- a/src/ast/rewriter/arith_rewriter.cpp
+++ b/src/ast/rewriter/arith_rewriter.cpp
@@ -455,7 +455,7 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin
             st = BR_DONE;
         }
     }
-    if (m_arith_lhs && is_numeral(arg2, a2) && is_neg_poly(arg1, new_arg1)) {
+    if ((m_arith_lhs || m_arith_ineq_lhs) && is_numeral(arg2, a2) && is_neg_poly(arg1, new_arg1)) {
         a2.neg();
         new_arg2 = m_util.mk_numeral(a2, m_util.is_int(new_arg1));
         switch (kind) {
diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp
index 9d6c5065e..1887d677d 100644
--- a/src/cmd_context/pdecl.cpp
+++ b/src/cmd_context/pdecl.cpp
@@ -660,6 +660,7 @@ void pdatatype_decl::display(std::ostream & out) const {
 
 #ifdef DATATYPE_V2
 bool pdatatype_decl::commit(pdecl_manager& m) {
+    TRACE("datatype", tout << m_name << "\n";);
     sort_ref_vector ps(m.m());
     for (unsigned i = 0; i < m_num_params; ++i) {
         ps.push_back(m.m().mk_uninterpreted_sort(symbol(i), 0, 0));
@@ -669,10 +670,8 @@ bool pdatatype_decl::commit(pdecl_manager& m) {
     datatype_decl * d_ptr = dts.m_buffer[0];
     sort_ref_vector sorts(m.m());
     bool is_ok = m.get_dt_plugin()->mk_datatypes(1, &d_ptr, m_num_params, ps.c_ptr(), sorts);
-    if (is_ok) {
-        if (m_num_params == 0) {
-            m.notify_new_dt(sorts.get(0), this);        
-        }
+    if (is_ok && m_num_params == 0) {
+        m.notify_new_dt(sorts.get(0), this);        
     }
     return is_ok;
 }
@@ -917,6 +916,7 @@ pconstructor_decl * pdecl_manager::mk_pconstructor_decl(unsigned num_params,
 }
 
 pdatatype_decl * pdecl_manager::mk_pdatatype_decl(unsigned num_params, symbol const & s, unsigned num, pconstructor_decl * const * cs) {
+    TRACE("datatype", tout << s << " has " << num_params << " parameters\n";);
     return new (a().allocate(sizeof(pdatatype_decl))) pdatatype_decl(m_id_gen.mk(), num_params, *this,
                                                                      s, num, cs);
 }
diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp
index 9526429cb..8144ebc08 100644
--- a/src/parsers/smt2/smt2parser.cpp
+++ b/src/parsers/smt2/smt2parser.cpp
@@ -619,14 +619,7 @@ namespace smt2 {
             psort_decl * d = m_ctx.find_psort_decl(id);
             int idx = 0;
             if (d == 0) {
-                if (m_dt_name2idx.find(id, idx)) {
-                    throw parser_exception("smtlib 2.6 parametric datatype sorts are not supported");
-                    // unsigned num_params = m_dt_name2arity.find(id);
-                    // d = pm().mk_psort_dt_decl(num_params, id);
-                }
-                else {
-                    unknown_sort(id);                
-                }
+                unknown_sort(id);                                
             }
             next();
             void * mem      = m_stack.allocate(sizeof(psort_frame));
@@ -924,10 +917,11 @@ namespace smt2 {
                 pdatatype_decl * d = new_dt_decls[i];
                 symbol duplicated;
                 check_duplicate(d, line, pos);
+                m_ctx.insert(d);
             }                
 #endif
             TRACE("declare_datatypes", tout << "i: " << i << " new_dt_decls.size(): " << sz << "\n";
-                  for (unsigned i = 0; i < sz; i++) tout << new_dt_decls[i]->get_name() << "\n";);
+                  for (unsigned j = 0; j < new_dt_decls.size(); ++j) tout << new_dt_decls[j]->get_name() << "\n";);
             m_ctx.print_success();
             next();
         }
diff --git a/src/test/get_consequences.cpp b/src/test/get_consequences.cpp
index b9a54245e..6d600f594 100644
--- a/src/test/get_consequences.cpp
+++ b/src/test/get_consequences.cpp
@@ -66,7 +66,7 @@ void test2() {
     constructor_decl* B = mk_constructor_decl(symbol("B"), symbol("is-B"), 0, 0);
     constructor_decl* constrs[3] = { R, G, B };
     datatype_decl * enum_sort = mk_datatype_decl(dtutil, symbol("RGB"), 0, nullptr, 3, constrs);
-    VERIFY(dt.mk_datatypes(1, &enum_sort, 0, 0, new_sorts));    
+    VERIFY(dt.mk_datatypes(1, &enum_sort, 0, nullptr, new_sorts));    
     sort* rgb = new_sorts[0].get();
 
     expr_ref x = mk_const(m, "x", rgb), y = mk_const(m, "y", rgb), z = mk_const(m, "z", rgb);

From d05d3bac4f8e57fc9168af1a80d93221cfad9bfa Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Tue, 5 Sep 2017 20:12:48 -0700
Subject: [PATCH 64/74] fix instantiations

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/cmd_context/pdecl.cpp       | 26 ++++++++++++++++++++++++--
 src/parsers/smt2/smt2parser.cpp |  5 ++++-
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp
index 1887d677d..2293072a2 100644
--- a/src/cmd_context/pdecl.cpp
+++ b/src/cmd_context/pdecl.cpp
@@ -604,8 +604,30 @@ struct datatype_decl_buffer {
 
 #ifdef DATATYPE_V2
 sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
-    UNREACHABLE();
-    return 0;
+    // TBD: copied
+    SASSERT(n == m_num_params);
+    sort * r = find(s);
+    if (r)
+        return r;
+    buffer<parameter> ps;
+    ps.push_back(parameter(m_name));
+    for (unsigned i = 0; i < n; i++)
+        ps.push_back(parameter(s[i]));
+    datatype_util util(m.m());
+    r = m.m().mk_sort(util.get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr());
+    cache(m, s, r);
+    m.save_info(r, this, n, s);
+    if (m_num_params > 0 && util.is_declared(r)) {
+        bool has_typevar = false;
+        // crude check ..
+        for (unsigned i = 0; !has_typevar && i < n; ++i) {
+            has_typevar = s[i]->get_name().is_numerical();
+        }
+        if (!has_typevar) {
+            m.notify_new_dt(r, this);
+        }
+    }
+    return r;
 }
 #else
 sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp
index 8144ebc08..7bfc8bd99 100644
--- a/src/parsers/smt2/smt2parser.cpp
+++ b/src/parsers/smt2/smt2parser.cpp
@@ -917,7 +917,10 @@ namespace smt2 {
                 pdatatype_decl * d = new_dt_decls[i];
                 symbol duplicated;
                 check_duplicate(d, line, pos);
-                m_ctx.insert(d);
+                if (!is_smt2_6) {
+                    // datatypes are inserted up front in SMT2.6 mode, so no need to re-insert them.
+                    m_ctx.insert(d);
+                }
             }                
 #endif
             TRACE("declare_datatypes", tout << "i: " << i << " new_dt_decls.size(): " << sz << "\n";

From fe02a5f87be30c3dda5c1c1dcf879081ff68602f Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 6 Sep 2017 02:16:00 -0700
Subject: [PATCH 65/74] fix parse/print of ADTs

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/ast_smt_pp.cpp            | 369 ++++++++++--------------------
 src/ast/datatype_decl_plugin2.cpp |  33 ++-
 src/ast/datatype_decl_plugin2.h   |   1 +
 src/smt/asserted_formulas.cpp     |   3 +-
 4 files changed, 153 insertions(+), 253 deletions(-)

diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp
index fdac6c7be..1c3b4aeb2 100644
--- a/src/ast/ast_smt_pp.cpp
+++ b/src/ast/ast_smt_pp.cpp
@@ -174,7 +174,6 @@ class smt_printer {
     symbol           m_logic;
     symbol           m_AUFLIRA;
     bool             m_no_lets;
-    bool             m_is_smt2;
     bool             m_simplify_implies;
     expr*            m_top;
 
@@ -199,12 +198,7 @@ class smt_printer {
     }
 
     void pp_id(expr* n) {
-         if (m_is_smt2) {
-             m_out << (is_bool(n)?"$x":(is_proof(n)?"@x":"?x")) << n->get_id();
-         }
-         else {
-             m_out << (is_bool(n)?"$x":"?x") << n->get_id();
-         }
+        m_out << (is_bool(n)?"$x":(is_proof(n)?"@x":"?x")) << n->get_id();
     }
 
     void pp_decl(func_decl* d) {
@@ -225,23 +219,15 @@ class smt_printer {
 #endif
         }
         else if (m_manager.is_ite(d)) {
-            if (!m_is_smt2 && is_bool(d->get_range())) {
-                m_out << "if_then_else";
-            }
-            else {
-                m_out << "ite";
-            }
+            m_out << "ite";            
         }
-        else if (!m_is_smt2 && m_manager.is_implies(d)) {
-            m_out << "implies";
-        }
-        else if (m_is_smt2 && m_manager.is_iff(d)) {
+        else if (m_manager.is_iff(d)) {
             m_out << "=";
         }
-        else if (m_is_smt2 && m_manager.is_implies(d)) {
+        else if (m_manager.is_implies(d)) {
             m_out << "=>";
         }
-        else if (m_is_smt2 && is_decl_of(d, m_arith_fid, OP_UMINUS)) {
+        else if (is_decl_of(d, m_arith_fid, OP_UMINUS)) {
             m_out << "-";
         }
         else {
@@ -263,28 +249,23 @@ class smt_printer {
             return;
         }
 
-        if (m_is_smt2) {
-            if (is_sort_symbol && sym == symbol("String")) {
-                m_out << "String";
-                return;
-            }
-            if (is_sort_symbol &&
-                sym != symbol("BitVec") &&
-                sym != symbol("FloatingPoint") &&
-                sym != symbol("RoundingMode")) {
-                m_out << "(" << sym << " ";
-            }
-            else if (!is_sort_symbol && is_sort_param(num_params, params)) {
-                m_out << "(as " << sym << " ";
-            }
-            else {
-                m_out << "(_ " << sym << " ";
-            }
+        if (is_sort_symbol && sym == symbol("String")) {
+            m_out << "String";
+            return;
+        }
+        if (is_sort_symbol &&
+            sym != symbol("BitVec") &&
+            sym != symbol("FloatingPoint") &&
+            sym != symbol("RoundingMode")) {
+            m_out << "(" << sym << " ";
+        }
+        else if (!is_sort_symbol && is_sort_param(num_params, params)) {
+            m_out << "(as " << sym << " ";
         }
         else {
-            m_out << sym << "[";
+            m_out << "(_ " << sym << " ";
         }
-
+        
         for (unsigned i = 0; i < num_params; ++i) {
             parameter const& p = params[i];
             if (p.is_ast()) {
@@ -305,20 +286,10 @@ class smt_printer {
                 m_out << p;
             }
             if (i + 1 < num_params) {
-                if (m_is_smt2) {
-                    m_out << " ";
-                }
-                else {
-                    m_out << ": ";
-                }
+                m_out << " ";
             }
         }
-        if (m_is_smt2) {
-            m_out << ")";
-        }
-        else {
-            m_out << "]";
-        }
+        m_out << ")";
     }
 
     bool is_auflira() const {
@@ -327,9 +298,7 @@ class smt_printer {
 
     void visit_sort(sort* s, bool bool2int = false) {
         symbol sym;
-        if (bool2int && is_bool(s) && !m_is_smt2) {
-            sym = symbol("Int");
-        } else if (s->is_sort_of(m_bv_fid, BV_SORT)) {
+        if (s->is_sort_of(m_bv_fid, BV_SORT)) {
             sym = symbol("BitVec");
         }
         else if (s->is_sort_of(m_arith_fid, REAL_SORT)) {
@@ -341,42 +310,9 @@ class smt_printer {
         else if (s->is_sort_of(m_arith_fid, INT_SORT)) {
             sym = s->get_name();
         }
-        else if (s->is_sort_of(m_array_fid, ARRAY_SORT) && m_is_smt2) {
+        else if (s->is_sort_of(m_array_fid, ARRAY_SORT)) {
             sym = "Array";
         }
-        else if (s->is_sort_of(m_array_fid, ARRAY_SORT) && !m_is_smt2) {
-            unsigned num_params = s->get_num_parameters();
-            SASSERT(num_params >= 2);
-            if (is_auflira()) {
-                sort* rng = to_sort(s->get_parameter(1).get_ast());
-                if (rng->get_family_id() == m_array_fid) {
-                    m_out << "Array2";
-                }
-                else {
-                    m_out << "Array1";
-                }
-                return;
-            }
-            sort* s1 = to_sort(s->get_parameter(0).get_ast());
-            sort* s2 = to_sort(s->get_parameter(1).get_ast());
-            if (num_params == 2 &&
-                s1->is_sort_of(m_bv_fid, BV_SORT) &&
-                s2->is_sort_of(m_bv_fid, BV_SORT)) {
-                m_out << "Array";
-                m_out << "[" << s1->get_parameter(0).get_int();
-                m_out << ":" << s2->get_parameter(0).get_int() << "]";
-                return;
-            }
-            m_out << "(Array ";
-            for (unsigned i = 0; i < num_params; ++i) {
-                visit_sort(to_sort(s->get_parameter(i).get_ast()));
-                if (i + 1 < num_params) {
-                    m_out << " ";
-                }
-            }
-            m_out << ")";
-            return;
-        }
         else if (s->is_sort_of(m_dt_fid, DATATYPE_SORT)) {
 #ifndef DATATYPE_V2
             m_out << m_renaming.get_symbol(s->get_name());            
@@ -416,20 +352,7 @@ class smt_printer {
 
     void pp_arg(expr *arg, app *parent)
     {
-        if (!m_is_smt2 && is_bool(arg) && is_var(arg) && parent->get_family_id() == m_basic_fid) {
-            m_out << "(not (= ";
-            pp_marked_expr(arg);
-            m_out << " 0))";
-        } else if (!m_is_smt2 && is_bool(arg) && !is_var(arg) &&
-            parent->get_family_id() != m_basic_fid &&
-            parent->get_family_id() != m_dt_fid) {
-
-            m_out << "(ite ";
-            pp_marked_expr(arg);
-            m_out << " 1 0)";
-        } else {
-            pp_marked_expr(arg);
-        }
+        pp_marked_expr(arg);
     }
 
     void visit_app(app* n) {
@@ -444,12 +367,7 @@ class smt_printer {
         if (m_autil.is_numeral(n, val, is_int)) {
             if (val.is_neg()) {
                 val.neg();
-                if (m_is_smt2) {
-                    m_out << "(- ";
-                }
-                else {
-                    m_out << "(~ ";
-                }
+                m_out << "(- ";
                 display_rational(val, is_int);
                 m_out << ")";
             }
@@ -471,12 +389,7 @@ class smt_printer {
             m_out << "\"";
         }
         else if (m_bvutil.is_numeral(n, val, bv_size)) {
-            if (m_is_smt2) {
-                m_out << "(_ bv" << val << " " << bv_size << ")";
-            }
-            else {
-                m_out << "bv" << val << "[" << bv_size << "]";
-            }
+            m_out << "(_ bv" << val << " " << bv_size << ")";
         }
         else if (m_futil.is_numeral(n, float_val)) {
             m_out << "((_ to_fp " <<
@@ -486,37 +399,17 @@ class smt_printer {
         }
         else if (m_bvutil.is_bit2bool(n)) {
             unsigned bit = n->get_decl()->get_parameter(0).get_int();
-            if (m_is_smt2) {
-                m_out << "(= ((_ extract " << bit << " " << bit << ") ";
-                pp_marked_expr(n->get_arg(0));
-                m_out << ") (_ bv1 1))";
-            }
-            else {
-                m_out << "(= (extract[" << bit << ":" << bit << "] ";
-                pp_marked_expr(n->get_arg(0));
-                m_out << ") bv1[1])";
-            }
+            m_out << "(= ((_ extract " << bit << " " << bit << ") ";
+            pp_marked_expr(n->get_arg(0));
+            m_out << ") (_ bv1 1))";
         }
         else if (m_manager.is_label(n, pos, names) && names.size() >= 1) {
-            if (m_is_smt2) {
-                m_out << "(! ";
-                pp_marked_expr(n->get_arg(0));
-                m_out << (pos?":lblpos":":lblneg") << " " << m_renaming.get_symbol(names[0]) << ")";
-            }
-            else {
-                m_out << "(" << (pos?"lblpos":"lblneg") << " " << m_renaming.get_symbol(names[0]) << " ";
-                expr* ch = n->get_arg(0);
-                pp_marked_expr(ch);
-                m_out << ")";
-            }
+            m_out << "(! ";
+            pp_marked_expr(n->get_arg(0));
+            m_out << (pos?":lblpos":":lblneg") << " " << m_renaming.get_symbol(names[0]) << ")";            
         }
         else if (m_manager.is_label_lit(n, names) && names.size() >= 1) {
-            if (m_is_smt2) {
-                m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0]) << ")";
-            }
-            else {
-                m_out << "(lblpos " << m_renaming.get_symbol(names[0]) << " true )";
-            }
+            m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0]) << ")";
         }
         else if (num_args == 0) {
             if (decl->private_parameters()) {
@@ -595,14 +488,11 @@ class smt_printer {
 
     void print_no_lets(expr *e)
     {
-        smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, true, m_simplify_implies, m_is_smt2, m_indent, m_num_var_names, m_var_names);
+        smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, true, m_simplify_implies, true, m_indent, m_num_var_names, m_var_names);
         p(e);
     }
 
     void print_bound(symbol const& name) {
-        if (!m_is_smt2 && (name.is_numerical() || '?' != name.bare_str()[0])) {
-            m_out << "?";
-        }
         m_out << name;
     }
 
@@ -616,9 +506,7 @@ class smt_printer {
         else {
             m_out << "exists ";
         }
-        if (m_is_smt2) {
-            m_out << "(";
-        }
+        m_out << "(";
         for (unsigned i = 0; i < q->get_num_decls(); ++i) {
             sort* s = q->get_decl_sort(i);
             m_out << "(";
@@ -627,15 +515,13 @@ class smt_printer {
             visit_sort(s, true);
             m_out << ") ";
         }
-        if (m_is_smt2) {
-            m_out << ")";
-        }
+        m_out << ")";
 
-        if (m_is_smt2 && (q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) {
+        if ((q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) {
             m_out << "(! ";
         }
         {
-            smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, false, m_is_smt2, m_simplify_implies, m_indent, m_num_var_names, m_var_names);
+            smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, false, true, m_simplify_implies, m_indent, m_num_var_names, m_var_names);
             p(q->get_expr());
         }
 
@@ -654,28 +540,18 @@ class smt_printer {
                 }
             }
 
-            if (m_is_smt2) {
-                m_out << " :pattern ( ";
-            }
-            else {
-                m_out << " :pat { ";
-            }
+            m_out << " :pattern ( ";
             for (unsigned j = 0; j < pat->get_num_args(); ++j) {
                 print_no_lets(pat->get_arg(j));
                 m_out << " ";
             }
-            if (m_is_smt2) {
-                m_out << ")";
-            }
-            else {
-                m_out << "}";
-            }
+            m_out << ")";
         }
 
         if (q->get_qid() != symbol::null)
             m_out << " :qid " << q->get_qid();
 
-        if (m_is_smt2 && (q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) {
+        if ((q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) {
             m_out << ")";
         }
         m_out << ")";
@@ -739,21 +615,11 @@ class smt_printer {
     }
 
     void visit_expr(expr* n) {
-        if (m_is_smt2) {
-            m_out << "(let ((";
-        }
-        else if (is_bool(n)) {
-            m_out << "(flet (";
-        }
-        else {
-            m_out << "(let (";
-        }
+        m_out << "(let ((";
         pp_id(n);
         m_out << " ";
         pp_expr(n);
-        if (m_is_smt2) {
-            m_out << ")";
-        }
+        m_out << ")";        
         m_out << ")";
         newline();
     }
@@ -865,7 +731,6 @@ public:
         m_AUFLIRA("AUFLIRA"),
         // It's much easier to read those testcases with that.
         m_no_lets(no_lets),
-        m_is_smt2(is_smt2),
         m_simplify_implies(simplify_implies)
     {
         m_basic_fid = m.get_basic_family_id();
@@ -919,8 +784,63 @@ public:
     }
 
     void pp_dt(ast_mark& mark, sort* s) {
-        SASSERT(s->is_sort_of(m_dt_fid, DATATYPE_SORT));
         datatype_util util(m_manager);
+        SASSERT(util.is_datatype(s));
+
+#ifdef DATATYPE_V2
+
+        sort_ref_vector ps(m_manager);        
+        ptr_vector<datatype::def> defs;
+        util.get_defs(s, defs);
+
+        for (datatype::def* d : defs) {
+            sort_ref sr = d->instantiate(ps);
+            if (mark.is_marked(sr)) return; // already processed
+            mark.mark(sr, true);
+        }
+                
+        m_out << "(declare-datatypes (";
+        bool first_def = true;
+        for (datatype::def* d : defs) {
+            if (!first_def) m_out << "\n    "; else first_def = false;
+            m_out << "(" << d->name() << " " << d->params().size() << ")";
+        }
+        m_out << ") (";
+        bool first_sort = true;
+        for (datatype::def* d : defs) {
+            if (!first_sort) m_out << "\n   "; else first_sort = false; 
+            if (!d->params().empty()) {
+                m_out << "(par (";
+                bool first_param = true;
+                for (sort* s : d->params()) {
+                    if (!first_param) m_out << " "; else first_param = false;
+                    visit_sort(s);
+                }
+                m_out << ")";
+            }
+            m_out << "(";
+            m_out << m_renaming.get_symbol(d->name());
+            m_out << " ";
+            bool first_constr = true;
+            for (datatype::constructor* f : *d) {
+                if (!first_constr) m_out << " "; else first_constr = false;
+                m_out << "(";                
+                m_out << m_renaming.get_symbol(f->name());
+                for (datatype::accessor* a : *f) {
+                    m_out << " (" << m_renaming.get_symbol(a->name()) << " ";
+                    visit_sort(a->range());
+                    m_out << ")";
+                }
+                m_out << ")";
+            }
+            if (!d->params().empty()) {
+                m_out << ")";
+            }
+            m_out << ")";
+        }
+        m_out << "))";     
+#else
+
         ptr_vector<sort> rec_sorts;
 
         rec_sorts.push_back(s);
@@ -954,55 +874,30 @@ public:
             }
         }
 
-        if (m_is_smt2) {
-            // TBD: datatypes may be declared parametrically.
-            // get access to parametric generalization, or print
-            // monomorphic specialization with a tag that gets reused at use-point.
-            m_out << "(declare-datatypes () (";
-        }
-        else {
-            m_out << ":datatypes (";
-        }
-        for (unsigned si = 0; si < rec_sorts.size(); ++si) {
-            s = rec_sorts[si];
+        m_out << "(declare-datatypes () (";
+        bool first_sort = true;
+        for (sort * s : rec_sorts) {
+            if (!first_sort) m_out << " "; else first_sort = false;
+            
             m_out << "(";
             m_out << m_renaming.get_symbol(s->get_name());
             m_out << " ";
-            ptr_vector<func_decl> const& decls = *util.get_datatype_constructors(s);
-
-            for (unsigned i = 0; i < decls.size(); ++i) {
-                func_decl* f = decls[i];
-                ptr_vector<func_decl> const& accs = *util.get_constructor_accessors(f);
-                if (m_is_smt2 || accs.size() > 0) {
-                    m_out << "(";
-                }
+            bool first_constr = true;
+            for (func_decl* f : *util.get_datatype_constructors(s)) {
+                if (!first_constr) m_out << " "; else first_constr = false;
+                m_out << "(";                
                 m_out << m_renaming.get_symbol(f->get_name());
-                if (!accs.empty() || !m_is_smt2) {
-                    m_out << " ";
-                }
-                for (unsigned j = 0; j < accs.size(); ++j) {
-                    func_decl* a = accs[j];
-                    m_out << "(" << m_renaming.get_symbol(a->get_name()) << " ";
+                for (func_decl* a : *util.get_constructor_accessors(f)) {
+                    m_out << " (" << m_renaming.get_symbol(a->get_name()) << " ";
                     visit_sort(a->get_range());
                     m_out << ")";
-                    if (j + 1 < accs.size()) m_out << " ";
-                }
-                if (m_is_smt2 || accs.size() > 0) {
-                    m_out << ")";
-                    if (i + 1 < decls.size()) {
-                        m_out << " ";
-                    }
                 }
+                m_out << ")";
             }
             m_out << ")";
-            if (si + 1 < rec_sorts.size()) {
-                m_out << " ";
-            }
         }
-        if (m_is_smt2) {
-            m_out << ")";
-        }
-        m_out << ")";
+        m_out << "))";     
+#endif   
         newline();
     }
 
@@ -1015,12 +910,7 @@ public:
             pp_dt(mark, s);
         }
         else {
-            if (m_is_smt2) {
-                m_out << "(declare-sort ";
-            }
-            else {
-                m_out << ":extrasorts (";
-            }
+            m_out << "(declare-sort ";
             visit_sort(s);
             m_out << ")";
             newline();
@@ -1034,29 +924,16 @@ public:
     }
 
     void operator()(func_decl* d) {
-        if (m_is_smt2) {
-            m_out << "(declare-fun ";
-            pp_decl(d);
-            m_out << "(";
-            for (unsigned i = 0; i < d->get_arity(); ++i) {
-                if (i > 0) m_out << " ";
-                visit_sort(d->get_domain(i), true);
-            }
-            m_out << ") ";
-            visit_sort(d->get_range());
-            m_out << ")";
-        }
-        else {
-            m_out << "(";
-            pp_decl(d);
-            for (unsigned i = 0; i < d->get_arity(); ++i) {
-                m_out << " ";
-                visit_sort(d->get_domain(i), true);
-            }
-            m_out << " ";
-            visit_sort(d->get_range());
-            m_out << ")";
+        m_out << "(declare-fun ";
+        pp_decl(d);
+        m_out << "(";
+        for (unsigned i = 0; i < d->get_arity(); ++i) {
+            if (i > 0) m_out << " ";
+            visit_sort(d->get_domain(i), true);
         }
+        m_out << ") ";
+        visit_sort(d->get_range());
+        m_out << ")";       
     }
 
     void visit_pred(func_decl* d) {
diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 74750d2ca..b53a2743f 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -965,35 +965,56 @@ namespace datatype {
         return d.constructors().size();
     }
 
+    void util::get_defs(sort* s0, ptr_vector<def>& defs) {
+        svector<symbol> mark;
+        ptr_buffer<sort> todo;
+        todo.push_back(s0);
+        mark.push_back(s0->get_name());
+        while (!todo.empty()) {
+            sort* s = todo.back();
+            todo.pop_back();
+            defs.push_back(&m_plugin->get_def(s->get_name()));
+            def const& d = get_def(s);
+            for (constructor* c : d) {
+                for (accessor* a : *c) {
+                    sort* s = a->range();
+                    if (are_siblings(s0, s) && !mark.contains(s->get_name())) {
+                        mark.push_back(s->get_name());
+                        todo.push_back(s);
+                    }
+                }
+            }
+        }
+    }
 
-    void util::display_datatype(sort *s0, std::ostream& strm) {
+    void util::display_datatype(sort *s0, std::ostream& out) {
         ast_mark mark;
         ptr_buffer<sort> todo;
         SASSERT(is_datatype(s0));
-        strm << s0->get_name() << " where\n";
+        out << s0->get_name() << " where\n";
         todo.push_back(s0);
         mark.mark(s0, true);
         while (!todo.empty()) {
             sort* s = todo.back();
             todo.pop_back();
-            strm << s->get_name() << " =\n";
+            out << s->get_name() << " =\n";
 
             ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
             for (unsigned i = 0; i < cnstrs.size(); ++i) {
                 func_decl* cns = cnstrs[i];
                 func_decl* rec = get_constructor_recognizer(cns);
-                strm << "  " << cns->get_name() << " :: " << rec->get_name() << " :: ";
+                out << "  " << cns->get_name() << " :: " << rec->get_name() << " :: ";
                 ptr_vector<func_decl> const & accs = *get_constructor_accessors(cns);
                 for (unsigned j = 0; j < accs.size(); ++j) {
                     func_decl* acc = accs[j];
                     sort* s1 = acc->get_range();
-                    strm << "(" << acc->get_name() << ": " << s1->get_name() << ") "; 
+                    out << "(" << acc->get_name() << ": " << s1->get_name() << ") "; 
                     if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) {
                         mark.mark(s1, true);
                         todo.push_back(s1);
                     }          
                 }
-                strm << "\n";
+                out << "\n";
             }
         }
     }
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index 3ea2ad1da..d9a069de3 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -379,6 +379,7 @@ namespace datatype {
         unsigned get_constructor_idx(func_decl * f) const;
         unsigned get_recognizer_constructor_idx(func_decl * f) const;
         decl::plugin* get_plugin() { return m_plugin; }
+        void get_defs(sort* s, ptr_vector<def>& defs);
     };
 
 };
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index 1581e70bd..ebdda73a1 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -432,7 +432,8 @@ void asserted_formulas::propagate_values() {
     flush_cache();
 
     unsigned num_prop = 0;
-    while (!inconsistent()) {
+    unsigned num_iterations = 0;
+    while (!inconsistent() && ++num_iterations < 2) {
         m_expr2depth.reset();
         m_scoped_substitution.push();
         unsigned prop = num_prop;

From f40a66c095b97c7b4ec607e3e1b1f8e81b277821 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 6 Sep 2017 02:26:19 -0700
Subject: [PATCH 66/74] fixes

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/ast_smt2_pp.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp
index cdc771f12..63c7de1a9 100644
--- a/src/ast/ast_smt2_pp.cpp
+++ b/src/ast/ast_smt2_pp.cpp
@@ -43,6 +43,9 @@ format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len) co
         len = static_cast<unsigned>(str.length());
         return mk_string(m, str.c_str());
     }
+    else if (!s.bare_str()) {
+        return mk_string(m,"null");
+    }
     else {
         len = static_cast<unsigned>(strlen(s.bare_str()));
         return mk_string(m, s.bare_str());

From 7f127cdd5dfc2a21433e64e19a5e91ab344648c4 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 6 Sep 2017 09:48:10 -0700
Subject: [PATCH 67/74] adding declarations for regression tests

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/datatype_decl_plugin2.cpp |  1 +
 src/ast/datatype_decl_plugin2.h   |  2 +-
 src/cmd_context/pdecl.cpp         | 99 +++++++++++++++++--------------
 src/cmd_context/pdecl.h           |  3 +
 src/parsers/smt2/smt2parser.cpp   |  1 +
 5 files changed, 62 insertions(+), 44 deletions(-)

diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index b53a2743f..43164a436 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -346,6 +346,7 @@ namespace datatype {
             begin_def_block();
             for (unsigned i = 0; i < num_datatypes; ++i) {
                 def* d = 0;
+                TRACE("datatype", tout << "declaring " << datatypes[i]->name() << "\n";);
                 if (m_defs.find(datatypes[i]->name(), d)) {
                     TRACE("datatype", tout << "delete previous version for " << datatypes[i]->name() << "\n";);
                     dealloc(d);
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index d9a069de3..a88c7100e 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -334,7 +334,6 @@ namespace datatype {
         void compute_datatype_size_functions(svector<symbol> const& names);
         param_size::size* get_sort_size(sort_ref_vector const& params, sort* s);
         bool is_well_founded(unsigned num_types, sort* const* sorts);
-        def const& get_def(sort* s) const;
         def& get_def(symbol const& s) { return m_plugin->get_def(s); }
         void get_subsorts(sort* s, ptr_vector<sort>& sorts) const;        
 
@@ -380,6 +379,7 @@ namespace datatype {
         unsigned get_recognizer_constructor_idx(func_decl * f) const;
         decl::plugin* get_plugin() { return m_plugin; }
         void get_defs(sort* s, ptr_vector<def>& defs);
+        def const& get_def(sort* s) const;
     };
 
 };
diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp
index 2293072a2..95a6030f3 100644
--- a/src/cmd_context/pdecl.cpp
+++ b/src/cmd_context/pdecl.cpp
@@ -360,28 +360,7 @@ sort * psort_dt_decl::instantiate(pdecl_manager & m, unsigned n, sort * const *
     return 0;
 #else
     SASSERT(n == m_num_params);
-    sort * r = find(s);
-    if (r)
-        return r;
-    buffer<parameter> ps;
-    ps.push_back(parameter(m_name));
-    for (unsigned i = 0; i < n; i++)
-        ps.push_back(parameter(s[i]));
-    datatype_util util(m.m());
-    r = m.m().mk_sort(util.get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr());
-    cache(m, s, r);
-    m.save_info(r, this, n, s);
-    if (m_num_params > 0 && util.is_declared(r)) {
-        bool has_typevar = false;
-        // crude check ..
-        for (unsigned i = 0; !has_typevar && i < n; ++i) {
-            has_typevar = s[i]->get_name().is_numerical();
-        }
-        if (!has_typevar) {
-            m.notify_new_dt(r, this);
-        }
-    }
-    return r;
+    return m.instantiate_datatype(this, m_name, n, s);
 #endif
 }
 
@@ -603,32 +582,39 @@ struct datatype_decl_buffer {
 };
 
 #ifdef DATATYPE_V2
+
 sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
-    // TBD: copied
-    SASSERT(n == m_num_params);
-    sort * r = find(s);
-    if (r)
-        return r;
-    buffer<parameter> ps;
-    ps.push_back(parameter(m_name));
-    for (unsigned i = 0; i < n; i++)
-        ps.push_back(parameter(s[i]));
+    sort * r = m.instantiate_datatype(this, m_name, n, s);
     datatype_util util(m.m());
-    r = m.m().mk_sort(util.get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr());
-    cache(m, s, r);
-    m.save_info(r, this, n, s);
-    if (m_num_params > 0 && util.is_declared(r)) {
-        bool has_typevar = false;
-        // crude check ..
-        for (unsigned i = 0; !has_typevar && i < n; ++i) {
-            has_typevar = s[i]->get_name().is_numerical();
-        }
-        if (!has_typevar) {
-            m.notify_new_dt(r, this);
+    if (r && n > 0 && util.is_declared(r)) {
+        ast_mark mark;
+        datatype::def const& d = util.get_def(r);
+        mark.mark(r, true);
+        sort_ref_vector params(m.m(), n, s);
+        for (datatype::constructor* c : d) {
+            for (datatype::accessor* a : *c) {
+                sort* rng = a->range();
+                if (util.is_datatype(rng) && !mark.is_marked(rng) && m_parent) {
+                    mark.mark(rng, true);
+                    // TBD: search over more than just parents
+                    for (pdatatype_decl* p : *m_parent) {
+                        if (p->get_name() == rng->get_name()) {
+                            ptr_vector<sort> ps;
+                            func_decl_ref acc = a->instantiate(params);
+                            for (unsigned j = 0; j < util.get_datatype_num_parameter_sorts(rng); ++j) {
+                                ps.push_back(util.get_datatype_parameter_sort(acc->get_range(), j));
+                            }
+                            m.instantiate_datatype(p, p->get_name(), ps.size(), ps.c_ptr());
+                            break;
+                        }
+                    }
+                }
+            }
         }
     }
     return r;
 }
+
 #else
 sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
     SASSERT(m_num_params == n);
@@ -729,6 +715,33 @@ bool pdatatypes_decl::fix_missing_refs(symbol & missing) {
 }
 
 #ifdef DATATYPE_V2
+sort* pdecl_manager::instantiate_datatype(psort_decl* p, symbol const& name, unsigned n, sort * const* s) {
+    TRACE("datatype", tout << name << " "; for (unsigned i = 0; i < n; ++i) tout << s[i]->get_name() << " "; tout << "\n";);
+    pdecl_manager& m = *this;
+    sort * r = p->find(s);
+    if (r)
+        return r;
+    buffer<parameter> ps;
+    ps.push_back(parameter(name));
+    for (unsigned i = 0; i < n; i++)
+        ps.push_back(parameter(s[i]));
+    datatype_util util(m.m());
+    r = m.m().mk_sort(util.get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr());
+    p->cache(m, s, r);
+    m.save_info(r, p, n, s);
+    if (n > 0 && util.is_declared(r)) {
+        bool has_typevar = false;
+        // crude check ..
+        for (unsigned i = 0; !has_typevar && i < n; ++i) {
+            has_typevar = s[i]->get_name().is_numerical();
+        }
+        if (!has_typevar) {
+            m.notify_new_dt(r, p);
+        }
+    }
+    return r;
+}
+
 bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) {
     UNREACHABLE();
     return false;
@@ -887,6 +900,7 @@ void pdecl_manager::init_list() {
                                   mk_pconstructor_decl(1, symbol("insert"), symbol("is-insert"), 2, as) };
     m_list = mk_pdatatype_decl(1, symbol("List"), 2, cs);
     inc_ref(m_list);
+    m_list->commit(*this);
 }
 
 pdecl_manager::pdecl_manager(ast_manager & m):
@@ -966,7 +980,6 @@ psort_decl * pdecl_manager::mk_psort_user_decl(unsigned num_params, symbol const
 }
 
 psort_decl * pdecl_manager::mk_psort_dt_decl(unsigned num_params, symbol const & n) {
-    // std::cout << "insert dt-psort: " << n << " " << num_params << "\n";
     return new (a().allocate(sizeof(psort_dt_decl))) psort_dt_decl(m_id_gen.mk(), num_params, *this, n);    
 }
 
diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h
index 414415255..bef723380 100644
--- a/src/cmd_context/pdecl.h
+++ b/src/cmd_context/pdecl.h
@@ -261,6 +261,8 @@ class pdatatypes_decl : public pdecl {
     virtual ~pdatatypes_decl() {}
 public:
     pdatatype_decl const * const * children() const { return m_datatypes.c_ptr(); }
+    pdatatype_decl * const * begin() const { return m_datatypes.begin(); }
+    pdatatype_decl * const * end() const { return m_datatypes.end(); }
 #ifdef DATATYPE_V2
     // commit declaration 
     bool commit(pdecl_manager& m);
@@ -316,6 +318,7 @@ public:
     pdatatypes_decl * mk_pdatatypes_decl(unsigned num_params, unsigned num, pdatatype_decl * const * dts);
     pdatatype_decl * mk_plist_decl() { if (!m_list) init_list(); return m_list; }
     bool fix_missing_refs(pdatatypes_decl * s, symbol & missing) { return s->fix_missing_refs(missing); }
+    sort * instantiate_datatype(psort_decl* p, symbol const& name, unsigned n, sort * const* s);
     sort * instantiate(psort * s, unsigned num, sort * const * args);
 
     void lazy_dec_ref(pdecl * p) { p->dec_ref(); if (p->get_ref_count() == 0) m_to_delete.push_back(p); }
diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp
index 7bfc8bd99..ab2eecb36 100644
--- a/src/parsers/smt2/smt2parser.cpp
+++ b/src/parsers/smt2/smt2parser.cpp
@@ -897,6 +897,7 @@ namespace smt2 {
                 m_ctx.insert_aux_pdecl(dts.get());
 #else
                 dts->commit(pm());
+				m_ctx.insert_aux_pdecl(dts.get());
 #endif
             }
 #ifndef DATATYPE_V2

From 2ea9bfaa41652303b50ece8afc5a7af6908ddb08 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Wed, 6 Sep 2017 13:34:41 -0700
Subject: [PATCH 68/74] remove unstable sequence interpolant from doc test

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/api/python/z3/z3.py            |  6 +--
 src/ast/ast_ll_pp.cpp              |  2 +-
 src/ast/rewriter/bool_rewriter.cpp | 76 +++++++-----------------------
 src/ast/rewriter/rewriter_def.h    |  7 +--
 src/ast/rewriter/th_rewriter.cpp   |  1 +
 src/smt/asserted_formulas.cpp      |  4 ++
 src/smt/asserted_formulas.h        |  1 +
 src/smt/tactic/smt_tactic.cpp      |  1 +
 8 files changed, 31 insertions(+), 67 deletions(-)

diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py
index 1452a037e..39af03190 100644
--- a/src/api/python/z3/z3.py
+++ b/src/api/python/z3/z3.py
@@ -8185,9 +8185,9 @@ def sequence_interpolant(v,p=None,ctx=None):
     If parameters p are supplied, these are used in creating the
     solver that determines satisfiability.
 
-    >>> x = Int('x')
-    >>> y = Int('y')
-    >>> print(sequence_interpolant([x < 0, y == x , y > 2]))
+    x = Int('x')
+    y = Int('y')
+    print(sequence_interpolant([x < 0, y == x , y > 2]))
     [Not(x >= 0), Not(y >= 0)]
     """
     f = v[0]
diff --git a/src/ast/ast_ll_pp.cpp b/src/ast/ast_ll_pp.cpp
index 6b14b75a8..c00053780 100644
--- a/src/ast/ast_ll_pp.cpp
+++ b/src/ast/ast_ll_pp.cpp
@@ -284,7 +284,7 @@ public:
         }
         unsigned num_args = to_app(n)->get_num_args();
         if (num_args > 0) 
-            m_out << "(_ ";
+            m_out << "(";
         display_name(to_app(n)->get_decl());
         display_params(to_app(n)->get_decl());
         for (unsigned i = 0; i < num_args; i++) {
diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp
index 44fccb49e..6e99cb23e 100644
--- a/src/ast/rewriter/bool_rewriter.cpp
+++ b/src/ast/rewriter/bool_rewriter.cpp
@@ -629,61 +629,23 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result)
             return BR_REWRITE2;
         }
     }
-    expr* cond2, *t2, *e2;
-    if (m().is_ite(t, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) {
-        try_ite_value(to_app(t), val, result);
-        result = m().mk_ite(cond, result, m().mk_eq(e, val));
-        return BR_REWRITE2;
-    }
-    if (m().is_ite(e, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) {
-        try_ite_value(to_app(e), val, result);
-        result = m().mk_ite(cond, m().mk_eq(t, val), result);
-        return BR_REWRITE2;
+    {
+        expr* cond2, *t2, *e2;
+        if (m().is_ite(t, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) {
+            try_ite_value(to_app(t), val, result);
+            result = m().mk_ite(cond, result, m().mk_eq(e, val));
+            return BR_REWRITE2;
+        }
+        if (m().is_ite(e, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) {
+            try_ite_value(to_app(e), val, result);
+            result = m().mk_ite(cond, m().mk_eq(t, val), result);
+            return BR_REWRITE2;
+        }
     }
 
     return BR_FAILED;
 }
 
-#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 (m().are_equal(lhs, rhs)) {
@@ -697,26 +659,20 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
     }
 
     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";);
+               tout << mk_bounded_pp(lhs, m()) << "\n" << mk_bounded_pp(rhs, m()) << "\n--->\n" << mk_bounded_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";);
+               tout << mk_bounded_pp(lhs, m()) << "\n" << mk_bounded_pp(rhs, m()) << "\n--->\n" << mk_bounded_pp(result, m()) << "\n";);
     }
     if (r != BR_FAILED)
         return r;
+    
 
     if (m().is_bool(lhs)) {
         bool unfolded = false;
diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h
index c511f00d0..42f268379 100644
--- a/src/ast/rewriter/rewriter_def.h
+++ b/src/ast/rewriter/rewriter_def.h
@@ -18,6 +18,7 @@ Notes:
 --*/
 #include "ast/rewriter/rewriter.h"
 #include "ast/ast_smt2_pp.h"
+#include "ast/ast_ll_pp.h"
 
 template<typename Config>
 template<bool ProofGen>
@@ -259,10 +260,10 @@ void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
         }
         br_status st = m_cfg.reduce_app(f, new_num_args, new_args, m_r, m_pr2);
         SASSERT(st != BR_DONE || m().get_sort(m_r) == m().get_sort(t));
-        TRACE("reduce_app",
-              tout << mk_ismt2_pp(t, m()) << "\n";
+        CTRACE("reduce_app", st != BR_FAILED,
+              tout << mk_bounded_pp(t, m()) << "\n";
               tout << "st: " << st;
-              if (m_r) tout << " --->\n" << mk_ismt2_pp(m_r, m());
+              if (m_r) tout << " --->\n" << mk_bounded_pp(m_r, m());
               tout << "\n";);
         if (st != BR_FAILED) {
             result_stack().shrink(fr.m_spos);
diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp
index a2ca12b24..dd431bf85 100644
--- a/src/ast/rewriter/th_rewriter.cpp
+++ b/src/ast/rewriter/th_rewriter.cpp
@@ -736,6 +736,7 @@ ast_manager & th_rewriter::m() const {
 void th_rewriter::updt_params(params_ref const & p) {
     m_params = p;
     m_imp->cfg().updt_params(p);
+    IF_VERBOSE(10, verbose_stream() << p << "\n";);
 }
 
 void th_rewriter::get_param_descrs(param_descrs & r) {
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index ebdda73a1..c8670bd36 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -60,6 +60,7 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p):
 
     m_macro_finder = alloc(macro_finder, m, m_macro_manager);
 
+    m_elim_and = true;
     set_eliminate_and(false);
 
 }
@@ -118,7 +119,10 @@ void asserted_formulas::push_assertion(expr * e, proof * pr, vector<justified_ex
 }
 
 void asserted_formulas::set_eliminate_and(bool flag) {
+    if (flag == m_elim_and) return;
+    m_elim_and = flag;
     params_ref p;
+    p.set_bool("pull_cheap_ite", false);
     p.set_bool("elim_and", flag);
     p.set_bool("arith_ineq_lhs", true);
     p.set_bool("sort_sums", true);
diff --git a/src/smt/asserted_formulas.h b/src/smt/asserted_formulas.h
index d0db105d3..88c9e13a7 100644
--- a/src/smt/asserted_formulas.h
+++ b/src/smt/asserted_formulas.h
@@ -52,6 +52,7 @@ class asserted_formulas {
     static_features             m_static_features;
     vector<justified_expr>      m_formulas;
     unsigned                    m_qhead;
+    bool                        m_elim_and;
     macro_manager               m_macro_manager;
     scoped_ptr<macro_finder>    m_macro_finder;  
     maximize_bv_sharing_rw      m_bv_sharing;
diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp
index f2dc83cfe..57ac7b34b 100644
--- a/src/smt/tactic/smt_tactic.cpp
+++ b/src/smt/tactic/smt_tactic.cpp
@@ -150,6 +150,7 @@ public:
                             proof_converter_ref & pc,
                             expr_dependency_ref & core) {
         try {
+            IF_VERBOSE(10, verbose_stream() << "(smt.tactic start)\n";);
             mc = 0; pc = 0; core = 0;
             SASSERT(in->is_well_sorted());
             ast_manager & m = in->m();

From 19fa5f8cb360a1aa6a80e04402eeffd00dbeb1ac Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Thu, 7 Sep 2017 06:23:01 -0700
Subject: [PATCH 69/74] expand select/store in pre-processor

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/smt/asserted_formulas.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index c8670bd36..c52ad851c 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -129,6 +129,7 @@ void asserted_formulas::set_eliminate_and(bool flag) {
     p.set_bool("rewrite_patterns", true);
     p.set_bool("eq2ineq", m_params.m_arith_eq2ineq);
     p.set_bool("gcd_rounding", true);
+    p.set_bool("expand_select_store", true);
     m_rewriter.updt_params(p);
     flush_cache();
 }

From 0c9711aad7550f246d9596abf628e58089efa6eb Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Fri, 8 Sep 2017 21:20:54 +0300
Subject: [PATCH 70/74] copy declarations

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/ast.cpp                   |  6 ++++
 src/ast/ast.h                     |  4 +++
 src/ast/ast_translation.cpp       |  8 ++----
 src/ast/datatype_decl_plugin2.cpp | 47 +++++++++++++++++++++++++++++++
 src/ast/datatype_decl_plugin2.h   |  6 ++++
 src/smt/smt_solver.cpp            | 18 +++++-------
 6 files changed, 73 insertions(+), 16 deletions(-)

diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp
index a905efa28..bb81c1eba 100644
--- a/src/ast/ast.cpp
+++ b/src/ast/ast.cpp
@@ -1529,12 +1529,15 @@ void ast_manager::raise_exception(char const * msg) {
     throw ast_exception(msg);
 }
 
+#include "ast/ast_translation.h"
+
 void ast_manager::copy_families_plugins(ast_manager const & from) {
     TRACE("copy_families_plugins",
           tout << "target:\n";
           for (family_id fid = 0; m_family_manager.has_family(fid); fid++) {
               tout << "fid: " << fid << " fidname: " << get_family_name(fid) << "\n";
           });
+    ast_translation trans(const_cast<ast_manager&>(from), *this, false);
     for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) {
       SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid));
       SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid));
@@ -1555,6 +1558,9 @@ void ast_manager::copy_families_plugins(ast_manager const & from) {
           SASSERT(new_p->get_family_id() == fid);
           SASSERT(has_plugin(fid));
       }
+      if (from.has_plugin(fid)) {
+          get_plugin(fid)->inherit(from.get_plugin(fid), trans);
+      }
       SASSERT(from.m_family_manager.has_family(fid) == m_family_manager.has_family(fid));
       SASSERT(from.get_family_id(fid_name) == get_family_id(fid_name));
       SASSERT(!from.has_plugin(fid) || has_plugin(fid));
diff --git a/src/ast/ast.h b/src/ast/ast.h
index a1e31f46f..4eb43d30b 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -895,6 +895,8 @@ struct ast_eq_proc {
     }
 };
 
+class ast_translation;
+
 class ast_table : public chashtable<ast*, obj_ptr_hash<ast>, ast_eq_proc> {
 public:
     void erase(ast * n);
@@ -930,6 +932,8 @@ protected:
         m_family_id = id;
     }
 
+    virtual void inherit(decl_plugin* other_p, ast_translation& ) { }
+
     friend class ast_manager;
 
 public:
diff --git a/src/ast/ast_translation.cpp b/src/ast/ast_translation.cpp
index 0c56b6de6..1bce4bcbe 100644
--- a/src/ast/ast_translation.cpp
+++ b/src/ast/ast_translation.cpp
@@ -37,11 +37,9 @@ void ast_translation::cleanup() {
 }
 
 void ast_translation::reset_cache() {
-    obj_map<ast, ast*>::iterator it  = m_cache.begin();
-    obj_map<ast, ast*>::iterator end = m_cache.end();
-    for (; it != end; ++it) {
-        m_from_manager.dec_ref(it->m_key);
-        m_to_manager.dec_ref(it->m_value);
+    for (auto & kv : m_cache) {
+        m_from_manager.dec_ref(kv.m_key);
+        m_to_manager.dec_ref(kv.m_value);
     }
     m_cache.reset();
 }
diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 43164a436..0a002ed08 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -24,6 +24,7 @@ Revision History:
 #include "ast/datatype_decl_plugin2.h"
 #include "ast/array_decl_plugin.h"
 #include "ast/ast_smt2_pp.h"
+#include "ast/ast_translation.h"
 
 
 namespace datatype {
@@ -53,6 +54,9 @@ namespace datatype {
 
     def const& accessor::get_def() const { return m_constructor->get_def(); }
     util& accessor::u() const { return m_constructor->u(); }
+    accessor* accessor::translate(ast_translation& tr) {
+        return alloc(accessor, tr.to(), name(), to_sort(tr(m_range.get())));
+    }
 
     constructor::~constructor() {
         for (accessor* a : m_accessors) dealloc(a);
@@ -76,6 +80,15 @@ namespace datatype {
         return instantiate(sorts);
     }
 
+    constructor* constructor::translate(ast_translation& tr) {
+        constructor* result = alloc(constructor, m_name, m_recognizer);
+        for (accessor* a : *this) {
+            result->add(a->translate(tr));
+        }
+        return result;
+    }
+
+
     sort_ref def::instantiate(sort_ref_vector const& sorts) const {
         sort_ref s(m);
         TRACE("datatype", tout << "instantiate " << m_name << "\n";);
@@ -91,6 +104,19 @@ namespace datatype {
         return sort_ref(m.substitute(m_sort, sorts.size(), m_params.c_ptr(), sorts.c_ptr()), m);
     }
 
+    def* def::translate(ast_translation& tr, util& u) {
+        sort_ref_vector ps(tr.to());
+        for (sort* p : m_params) {
+            ps.push_back(to_sort(tr(p)));
+        }
+        def* result = alloc(def, tr.to(), u, m_name, m_class_id, ps.size(), ps.c_ptr());
+        for (constructor* c : *this) {
+            add(c->translate(tr));
+        }
+        if (m_sort) result->m_sort = to_sort(tr(m_sort.get()));
+        return result;               
+    }
+
     enum status {
         GRAY,
         BLACK
@@ -145,6 +171,27 @@ namespace datatype {
             return *(m_util.get());
         }
 
+        static unsigned stack_depth = 0;
+
+        void plugin::inherit(decl_plugin* other_p, ast_translation& tr) {
+            ++stack_depth;
+            SASSERT(stack_depth < 10);
+            plugin* p = dynamic_cast<plugin*>(other_p);
+            svector<symbol> names;
+            SASSERT(p);
+            for (auto& kv : p->m_defs) {
+                def* d = kv.m_value;
+                if (!m_defs.contains(kv.m_key)) {
+                    names.push_back(kv.m_key);
+                    m_defs.insert(kv.m_key, d->translate(tr, u()));
+                }
+            }
+            m_class_id = m_defs.size();
+            u().compute_datatype_size_functions(names);
+            --stack_depth;
+        }
+
+
         struct invalid_datatype {};
 
         sort * plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
index a88c7100e..364ba9350 100644
--- a/src/ast/datatype_decl_plugin2.h
+++ b/src/ast/datatype_decl_plugin2.h
@@ -75,6 +75,7 @@ namespace datatype {
         constructor const& get_constructor() const { return *m_constructor; }
         def const& get_def() const;
         util& u() const;
+        accessor* translate(ast_translation& tr);
     };
 
     class constructor {
@@ -98,6 +99,7 @@ namespace datatype {
         void attach(def* d) { m_def = d; }
         def const& get_def() const { return *m_def; }
         util& u() const;
+        constructor* translate(ast_translation& tr);
     };
 
     namespace param_size {
@@ -228,6 +230,7 @@ namespace datatype {
         util& u() const { return m_util; }
         param_size::size* sort_size() { return m_sort_size; }
         void set_sort_size(param_size::size* p) { m_sort_size = p; p->inc_ref(); m_sort = 0; }
+        def* translate(ast_translation& tr, util& u);
     };
 
     namespace decl {
@@ -238,6 +241,9 @@ namespace datatype {
             svector<symbol>          m_def_block;
             unsigned                 m_class_id;
             util & u() const;
+
+            virtual void inherit(decl_plugin* other_p, ast_translation& tr);
+
         public:
             plugin(): m_class_id(0) {}
             virtual ~plugin();
diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp
index 9d86436d9..36272a139 100644
--- a/src/smt/smt_solver.cpp
+++ b/src/smt/smt_solver.cpp
@@ -61,15 +61,14 @@ namespace smt {
         }
 
         virtual solver * translate(ast_manager & m, params_ref const & p) {
+            ast_translation translator(get_manager(), m);
+
             solver * result = alloc(solver, m, p, m_logic);
             smt::kernel::copy(m_context, result->m_context);
 
-            ast_translation translator(get_manager(), m);
-            obj_map<expr, expr*>::iterator it = m_name2assertion.begin();
-            obj_map<expr, expr*>::iterator end = m_name2assertion.end();
-            for (; it != end; it++)
-                result->m_name2assertion.insert(translator(it->m_key),
-                                                translator(it->m_value));
+            for (auto & kv : m_name2assertion) 
+                result->m_name2assertion.insert(translator(kv.m_key),
+                                                translator(kv.m_value));
 
             return result;
         }
@@ -264,7 +263,7 @@ namespace smt {
         }
 
         void compute_assrtn_fds(ptr_vector<expr> & core, vector<func_decl_set> & assrtn_fds) {
-            assrtn_fds.resize(m_name2assertion.size());
+            assrtn_fds.resize(m_name2assertion.size());            
             obj_map<expr, expr*>::iterator ait = m_name2assertion.begin();
             obj_map<expr, expr*>::iterator aend = m_name2assertion.end();
             for (unsigned i = 0; ait != aend; ait++, i++) {
@@ -277,10 +276,7 @@ namespace smt {
         }
 
         bool fds_intersect(func_decl_set & pattern_fds, func_decl_set & assrtn_fds) {
-            func_decl_set::iterator it = pattern_fds.begin();
-            func_decl_set::iterator end = pattern_fds.end();
-            for (; it != end; it++) {
-                func_decl * fd = *it;
+            for (func_decl * fd : pattern_fds) {
                 if (assrtn_fds.contains(fd))
                     return true;
             }

From ed6e23f153ec3b63efc75c0ef12a89d789b100c5 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 9 Sep 2017 05:40:12 +0300
Subject: [PATCH 71/74] iterator -> for

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/smt/smt_solver.cpp | 27 +++++++++++++--------------
 1 file changed, 13 insertions(+), 14 deletions(-)

diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp
index 36272a139..65ba1ee24 100644
--- a/src/smt/smt_solver.cpp
+++ b/src/smt/smt_solver.cpp
@@ -264,14 +264,14 @@ namespace smt {
 
         void compute_assrtn_fds(ptr_vector<expr> & core, vector<func_decl_set> & assrtn_fds) {
             assrtn_fds.resize(m_name2assertion.size());            
-            obj_map<expr, expr*>::iterator ait = m_name2assertion.begin();
-            obj_map<expr, expr*>::iterator aend = m_name2assertion.end();
-            for (unsigned i = 0; ait != aend; ait++, i++) {
-                if (core.contains(ait->m_key))
-                    continue;
-                collect_fds_proc p(m, assrtn_fds[i]);
-                expr_fast_mark1 visited;
-                quick_for_each_expr(p, visited, ait->m_value);
+            unsigned i = 0;
+            for (auto & kv : m_name2assertion) {
+                if (!core.contains(kv.m_key)) {
+                    collect_fds_proc p(m, assrtn_fds[i]);
+                    expr_fast_mark1 visited;
+                    quick_for_each_expr(p, visited, kv.m_value);
+                }
+                ++i;
             }
         }
 
@@ -293,9 +293,8 @@ namespace smt {
             for (unsigned d = 0; d < m_core_extend_patterns_max_distance; d++) {
                 new_core_literals.reset();
 
-                unsigned sz = core.size();
-                for (unsigned i = 0; i < sz; i++) {
-                    expr_ref name(core[i], m);
+                for (expr* c : core) {
+                    expr_ref name(c, m);
                     SASSERT(m_name2assertion.contains(name));
                     expr_ref assrtn(m_name2assertion.find(name), m);
                     collect_pattern_fds(assrtn, pattern_fds);
@@ -305,12 +304,12 @@ namespace smt {
                     if (assrtn_fds.empty())
                         compute_assrtn_fds(core, assrtn_fds);
 
-                    obj_map<expr, expr*>::iterator ait = m_name2assertion.begin();
-                    obj_map<expr, expr*>::iterator aend = m_name2assertion.end();
-                    for (unsigned i = 0; ait != aend; ait++, i++) {
+                    unsigned i = 0;
+                    for (auto & kv : m_name2assertion) {
                         if (!core.contains(ait->m_key) &&
                             fds_intersect(pattern_fds, assrtn_fds[i]))
                             new_core_literals.push_back(ait->m_key);
+                        ++i;
                     }
                 }
 

From 04e57e08ba93be87633f2dbf9bfe3a500ac51081 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sat, 9 Sep 2017 08:37:17 +0300
Subject: [PATCH 72/74] na

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/smt/smt_solver.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp
index 65ba1ee24..e78c6388e 100644
--- a/src/smt/smt_solver.cpp
+++ b/src/smt/smt_solver.cpp
@@ -306,9 +306,9 @@ namespace smt {
 
                     unsigned i = 0;
                     for (auto & kv : m_name2assertion) {
-                        if (!core.contains(ait->m_key) &&
+                        if (!core.contains(kv.m_key) &&
                             fds_intersect(pattern_fds, assrtn_fds[i]))
-                            new_core_literals.push_back(ait->m_key);
+                            new_core_literals.push_back(kv.m_key);
                         ++i;
                     }
                 }

From 4fe55cf8e5509f5ea409d2ef0dc3643e1db0d26c Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 10 Sep 2017 14:48:57 +0300
Subject: [PATCH 73/74] fix plugin translation

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/datatype_decl_plugin2.cpp | 15 ++++++---------
 src/parsers/smt2/smt2parser.cpp   |  1 -
 2 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
index 0a002ed08..743a08e98 100644
--- a/src/ast/datatype_decl_plugin2.cpp
+++ b/src/ast/datatype_decl_plugin2.cpp
@@ -105,13 +105,14 @@ namespace datatype {
     }
 
     def* def::translate(ast_translation& tr, util& u) {
+        SASSERT(&u.get_manager() == &tr.to());
         sort_ref_vector ps(tr.to());
         for (sort* p : m_params) {
             ps.push_back(to_sort(tr(p)));
         }
         def* result = alloc(def, tr.to(), u, m_name, m_class_id, ps.size(), ps.c_ptr());
         for (constructor* c : *this) {
-            add(c->translate(tr));
+            result->add(c->translate(tr));
         }
         if (m_sort) result->m_sort = to_sort(tr(m_sort.get()));
         return result;               
@@ -171,24 +172,22 @@ namespace datatype {
             return *(m_util.get());
         }
 
-        static unsigned stack_depth = 0;
-
         void plugin::inherit(decl_plugin* other_p, ast_translation& tr) {
-            ++stack_depth;
-            SASSERT(stack_depth < 10);
             plugin* p = dynamic_cast<plugin*>(other_p);
             svector<symbol> names;
+            ptr_vector<def> new_defs;            
             SASSERT(p);
             for (auto& kv : p->m_defs) {
                 def* d = kv.m_value;
                 if (!m_defs.contains(kv.m_key)) {
                     names.push_back(kv.m_key);
-                    m_defs.insert(kv.m_key, d->translate(tr, u()));
+                    new_defs.push_back(d->translate(tr, u()));
                 }
             }
+            for (def* d : new_defs) 
+                m_defs.insert(d->name(), d);
             m_class_id = m_defs.size();
             u().compute_datatype_size_functions(names);
-            --stack_depth;
         }
 
 
@@ -304,7 +303,6 @@ namespace datatype {
             VALIDATE_PARAM(u().is_datatype(domain[0]));
             // blindly trust that parameter is a constructor
             sort* range = m_manager->mk_bool_sort();
-            func_decl* f = to_func_decl(parameters[0].get_ast());
             func_decl_info info(m_family_id, OP_DT_RECOGNISER, num_parameters, parameters);
             info.m_private_parameters = true;
             return m.mk_func_decl(symbol(parameters[1].get_symbol()), arity, domain, range, info);
@@ -317,7 +315,6 @@ namespace datatype {
             VALIDATE_PARAM(u().is_datatype(domain[0]));
             // blindly trust that parameter is a constructor
             sort* range = m_manager->mk_bool_sort();
-            func_decl* f = to_func_decl(parameters[0].get_ast());
             func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters);
             info.m_private_parameters = true;
             return m.mk_func_decl(symbol("is"), arity, domain, range, info);
diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp
index ab2eecb36..a1304a848 100644
--- a/src/parsers/smt2/smt2parser.cpp
+++ b/src/parsers/smt2/smt2parser.cpp
@@ -617,7 +617,6 @@ namespace smt2 {
             SASSERT(curr_is_identifier());
             symbol id = curr_id();
             psort_decl * d = m_ctx.find_psort_decl(id);
-            int idx = 0;
             if (d == 0) {
                 unknown_sort(id);                                
             }

From 070c699ffcfd2c1ffe10ac40825de13cdb195cd9 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Sun, 10 Sep 2017 15:32:53 +0300
Subject: [PATCH 74/74] remove V2 reference

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/CMakeLists.txt            |    1 -
 src/ast/ast_smt2_pp.cpp           |    2 -
 src/ast/ast_smt_pp.cpp            |   71 -
 src/ast/datatype_decl_plugin.cpp  | 2003 ++++++++++++++---------------
 src/ast/datatype_decl_plugin.h    |  582 ++++++---
 src/ast/datatype_decl_plugin2.cpp | 1077 ----------------
 src/ast/datatype_decl_plugin2.h   |  439 -------
 src/cmd_context/pdecl.cpp         |   62 -
 src/cmd_context/pdecl.h           |    4 -
 src/parsers/smt2/smt2parser.cpp   |   32 +-
 10 files changed, 1377 insertions(+), 2896 deletions(-)
 delete mode 100644 src/ast/datatype_decl_plugin2.cpp
 delete mode 100644 src/ast/datatype_decl_plugin2.h

diff --git a/src/ast/CMakeLists.txt b/src/ast/CMakeLists.txt
index 47ed2def8..0a14d9473 100644
--- a/src/ast/CMakeLists.txt
+++ b/src/ast/CMakeLists.txt
@@ -14,7 +14,6 @@ z3_add_component(ast
     ast_util.cpp
     bv_decl_plugin.cpp
     datatype_decl_plugin.cpp
-    datatype_decl_plugin2.cpp
     decl_collector.cpp
     dl_decl_plugin.cpp
     expr2polynomial.cpp
diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp
index c69cafc71..abb4ae959 100644
--- a/src/ast/ast_smt2_pp.cpp
+++ b/src/ast/ast_smt2_pp.cpp
@@ -434,7 +434,6 @@ format_ns::format * smt2_pp_environment::pp_sort(sort * s) {
         fs.push_back(pp_sort(to_sort(s->get_parameter(0).get_ast())));
         return mk_seq1(m, fs.begin(), fs.end(), f2f(), get_sutil().is_seq(s)?"Seq":"RegEx");
     }
-#ifdef DATATYPE_V2
     if (get_dtutil().is_datatype(s)) {
         unsigned sz = get_dtutil().get_datatype_num_parameter_sorts(s);
         if (sz > 0) {
@@ -445,7 +444,6 @@ format_ns::format * smt2_pp_environment::pp_sort(sort * s) {
             return mk_seq1(m, fs.begin(), fs.end(), f2f(), s->get_name().str().c_str());        
         }
     }
-#endif
     return format_ns::mk_string(get_manager(), s->get_name().str().c_str());
 }
 
diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp
index 1c3b4aeb2..906fd054b 100644
--- a/src/ast/ast_smt_pp.cpp
+++ b/src/ast/ast_smt_pp.cpp
@@ -204,19 +204,13 @@ class smt_printer {
     void pp_decl(func_decl* d) {
         symbol sym = m_renaming.get_symbol(d->get_name());
         if (d->get_family_id() == m_dt_fid) {
-#ifdef DATATYPE_V2
-            std::cout << "printing " << sym << "\n";
             datatype_util util(m_manager);
             if (util.is_recognizer(d)) {
-                std::cout << d->get_num_parameters() << "\n";
                 visit_params(false, sym, d->get_num_parameters(), d->get_parameters());
             }
             else {
                 m_out << sym;
             }
-#else
-            m_out << sym;
-#endif
         }
         else if (m_manager.is_ite(d)) {
             m_out << "ite";            
@@ -314,9 +308,6 @@ class smt_printer {
             sym = "Array";
         }
         else if (s->is_sort_of(m_dt_fid, DATATYPE_SORT)) {
-#ifndef DATATYPE_V2
-            m_out << m_renaming.get_symbol(s->get_name());            
-#else
             datatype_util util(m_manager);
             unsigned num_sorts = util.get_datatype_num_parameter_sorts(s);
             if (num_sorts > 0) {
@@ -330,7 +321,6 @@ class smt_printer {
                 }
                 m_out << ")";
             }
-#endif
             return;
         }
         else {
@@ -787,8 +777,6 @@ public:
         datatype_util util(m_manager);
         SASSERT(util.is_datatype(s));
 
-#ifdef DATATYPE_V2
-
         sort_ref_vector ps(m_manager);        
         ptr_vector<datatype::def> defs;
         util.get_defs(s, defs);
@@ -839,65 +827,6 @@ public:
             m_out << ")";
         }
         m_out << "))";     
-#else
-
-        ptr_vector<sort> rec_sorts;
-
-        rec_sorts.push_back(s);
-        mark.mark(s, true);
-
-        // collect siblings and sorts that have not already been printed.
-        for (unsigned h = 0; h < rec_sorts.size(); ++h) {
-            s = rec_sorts[h];
-            ptr_vector<func_decl> const& decls = *util.get_datatype_constructors(s);
-
-            for (unsigned i = 0; i < decls.size(); ++i) {
-                func_decl* f = decls[i];
-                for (unsigned j = 0; j < f->get_arity(); ++j) {
-                    sort* s2 = f->get_domain(j);
-                    if (!mark.is_marked(s2)) {
-                        if (m_manager.is_uninterp(s2)) {
-                            pp_sort_decl(mark, s2);
-                        }
-                        else if (!util.is_datatype(s2)) {
-                            // skip
-                        }
-                        else if (util.are_siblings(s, s2)) {
-                            rec_sorts.push_back(s2);
-                            mark.mark(s2, true);
-                        }
-                        else {
-                            pp_sort_decl(mark, s2);
-                        }
-                    }
-                }
-            }
-        }
-
-        m_out << "(declare-datatypes () (";
-        bool first_sort = true;
-        for (sort * s : rec_sorts) {
-            if (!first_sort) m_out << " "; else first_sort = false;
-            
-            m_out << "(";
-            m_out << m_renaming.get_symbol(s->get_name());
-            m_out << " ";
-            bool first_constr = true;
-            for (func_decl* f : *util.get_datatype_constructors(s)) {
-                if (!first_constr) m_out << " "; else first_constr = false;
-                m_out << "(";                
-                m_out << m_renaming.get_symbol(f->get_name());
-                for (func_decl* a : *util.get_constructor_accessors(f)) {
-                    m_out << " (" << m_renaming.get_symbol(a->get_name()) << " ";
-                    visit_sort(a->get_range());
-                    m_out << ")";
-                }
-                m_out << ")";
-            }
-            m_out << ")";
-        }
-        m_out << "))";     
-#endif   
         newline();
     }
 
diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp
index 1090b3ff1..566487e60 100644
--- a/src/ast/datatype_decl_plugin.cpp
+++ b/src/ast/datatype_decl_plugin.cpp
@@ -1,5 +1,5 @@
 /*++
-Copyright (c) 2006 Microsoft Corporation
+Copyright (c) 2017 Microsoft Corporation
 
 Module Name:
 
@@ -11,321 +11,703 @@ Abstract:
 
 Author:
 
-    Leonardo de Moura (leonardo) 2008-01-10.
+    Nikolaj Bjorner (nbjorner) 2017-9-1 
 
 Revision History:
 
 --*/
-#include "ast/datatype_decl_plugin.h"
+
 #include "util/warning.h"
+#include "ast/array_decl_plugin.h"
+#include "ast/datatype_decl_plugin.h"
 #include "ast/ast_smt2_pp.h"
+#include "ast/ast_translation.h"
 
-#ifndef DATATYPE_V2
 
-/**
-   \brief Auxiliary class used to declare inductive datatypes.
-*/
-class accessor_decl {
-    symbol    m_name;
-    type_ref  m_type;
-public:
-    accessor_decl(const symbol & n, type_ref r):m_name(n), m_type(r) {}
-    symbol const & get_name() const { return m_name; }
-    type_ref const & get_type() const { return m_type; }
-};
+namespace datatype {
 
-accessor_decl * mk_accessor_decl(ast_manager& m, symbol const & n, type_ref const & t) {
-    return alloc(accessor_decl, n, t);
-}
-
-void del_accessor_decl(accessor_decl * d) {
-    dealloc(d);
-}
-
-void del_accessor_decls(unsigned num, accessor_decl * const * as) {
-    for (unsigned i = 0; i < num; i++)
-        del_accessor_decl(as[i]);
-}
-
-/**
-   \brief Auxiliary class used to declare inductive datatypes.
-*/
-class constructor_decl {
-    symbol                    m_name;
-    symbol                    m_recogniser_name;
-    ptr_vector<accessor_decl> m_accessors;
-public:
-    constructor_decl(const symbol & n, const symbol & r, unsigned num_accessors, accessor_decl * const * accessors):
-        m_name(n), m_recogniser_name(r), m_accessors(num_accessors, accessors) {}
-    ~constructor_decl() {
-        std::for_each(m_accessors.begin(), m_accessors.end(), delete_proc<accessor_decl>());
-    }
-    symbol const & get_name() const { return m_name; }
-    symbol const & get_recognizer_name() const { return m_recogniser_name; }
-    ptr_vector<accessor_decl> const & get_accessors() const { return m_accessors; }
-};
-
-constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * const * accessors) {
-    return alloc(constructor_decl, n, r, num_accessors, accessors);
-}
-
-void del_constructor_decl(constructor_decl * d) {
-    dealloc(d);
-}
-
-void del_constructor_decls(unsigned num, constructor_decl * const * cs) {
-    for (unsigned i = 0; i < num; i++)
-        del_constructor_decl(cs[i]);
-}
-
-/**
-   \brief Auxiliary class used to declare inductive datatypes.
-*/
-class datatype_decl {
-    symbol                       m_name;
-    ptr_vector<constructor_decl> m_constructors;
-public:
-    datatype_decl(const symbol & n, unsigned num_constructors, constructor_decl * const * constructors):
-        m_name(n), m_constructors(num_constructors, constructors) {
-    }
-    ~datatype_decl() {
-        std::for_each(m_constructors.begin(), m_constructors.end(), delete_proc<constructor_decl>());
-    }
-    symbol const & get_name() const { return m_name; }
-    ptr_vector<constructor_decl> const & get_constructors() const { return m_constructors; }
-};
-
-datatype_decl * mk_datatype_decl(datatype_util&, symbol const & n, unsigned num_params, sort * const* params, unsigned num_constructors, constructor_decl * const * cs) {
-    return alloc(datatype_decl, n, num_constructors, cs);
-}
-
-void del_datatype_decl(datatype_decl * d) {
-    dealloc(d);
-}
-
-void del_datatype_decls(unsigned num, datatype_decl * const * ds) {
-    for (unsigned i = 0; i < num; i++)
-        del_datatype_decl(ds[i]);
-}
-
-typedef buffer<bool, false, 256> bool_buffer;
-
-struct invalid_datatype {};
-
-static parameter const & read(unsigned num_parameters, parameter const * parameters, unsigned idx, bool_buffer & read_pos) {
-    if (idx >= num_parameters) {
-        throw invalid_datatype();
-    }
-    if (idx >= read_pos.size()) {
-        read_pos.resize(idx+1, false);
-    }
-    read_pos[idx] = true;
-    return parameters[idx];
-}
-
-static int read_int(unsigned num_parameters, parameter const * parameters, unsigned idx, bool_buffer & read_pos) {
-    const parameter & r = read(num_parameters, parameters, idx, read_pos);
-    if (!r.is_int()) {
-        TRACE("datatype", tout << "expected integer parameter at position " << idx << " got: " << r << "\n";);
-        throw invalid_datatype();
-    }
-    return r.get_int();
-}
-
-static symbol read_symbol(unsigned num_parameters, parameter const * parameters, unsigned idx, bool_buffer & read_pos) {
-    parameter const & r = read(num_parameters, parameters, idx, read_pos);
-    if (!r.is_symbol()) {
-        TRACE("datatype", tout << "expected symol parameter at position " << idx << " got: " << r << "\n";);
-        throw invalid_datatype();
-    }
-    return r.get_symbol();
-}
-
-static sort* read_sort(unsigned num_parameters, parameter const * parameters, unsigned idx, bool_buffer & read_pos) {
-    parameter const & r = read(num_parameters, parameters, idx, read_pos);
-    if (!r.is_ast()) {
-        TRACE("datatype", tout << "expected ast parameter at position " << idx << " got: " << r << "\n";);
-        throw invalid_datatype();
-    }
-    ast* a = r.get_ast();
-    if (!is_sort(a)) {
-        throw invalid_datatype();
-    }
-    return to_sort(a);
-}
-
-enum status {
-    WHITE,
-    GRAY,
-    BLACK
-};
-
-/**
-   \brief Return true if the inductive datatype is recursive.
-   Pre-condition: The given argument constains the parameters of an inductive datatype.
-*/
-static bool is_recursive_datatype(parameter const * parameters) {
-    unsigned num_types          = parameters[0].get_int();
-    unsigned top_tid            = parameters[1].get_int();
-    buffer<status>    already_found(num_types, WHITE);
-    buffer<unsigned>  todo;
-    todo.push_back(top_tid);
-    while (!todo.empty()) {
-        unsigned tid = todo.back();
-        if (already_found[tid] == BLACK) {
-            todo.pop_back();
-            continue;
+    void accessor::fix_range(sort_ref_vector const& dts) {
+        if (!m_range) {
+            m_range = dts[m_index];
         }
-        already_found[tid] = GRAY;
-        unsigned o                 = datatype_decl_plugin::constructor_offset(parameters, tid);  // constructor offset
-        unsigned num_constructors  = parameters[o].get_int();            
-        bool     can_process       = true;
-        for (unsigned s = 1; s <= num_constructors; s++) {
-            unsigned k_i           = parameters[o + s].get_int();
-            unsigned num_accessors = parameters[k_i + 2].get_int();
-            for (unsigned r = 0; r < num_accessors; r++) {
-                parameter const & a_type = parameters[k_i + 4 + 2*r];
-                if (a_type.is_int()) {
-                    unsigned tid_prime = a_type.get_int();
-                    switch (already_found[tid_prime]) {
-                    case WHITE:
-                        todo.push_back(tid_prime);
-                        can_process = false;
-                        break;
-                    case GRAY:
-                        // type is recursive
-                        return true;
-                    case BLACK:
-                        break;
+    }
+
+    func_decl_ref accessor::instantiate(sort_ref_vector const& ps) const {
+        ast_manager& m = ps.get_manager();
+        unsigned n = ps.size();
+        SASSERT(m_range);
+        SASSERT(n == get_def().params().size());
+        sort_ref range(m.substitute(m_range, n, get_def().params().c_ptr(), ps.c_ptr()), m);
+        sort_ref src(get_def().instantiate(ps));
+        sort* srcs[1] = { src.get() };
+        parameter pas[2] = { parameter(name()), parameter(get_constructor().name()) };
+        return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_ACCESSOR, 2, pas, 1, srcs, range), m);
+    }
+
+    func_decl_ref accessor::instantiate(sort* dt) const {
+        sort_ref_vector sorts = get_def().u().datatype_params(dt);
+        return instantiate(sorts);
+    }
+
+    def const& accessor::get_def() const { return m_constructor->get_def(); }
+    util& accessor::u() const { return m_constructor->u(); }
+    accessor* accessor::translate(ast_translation& tr) {
+        return alloc(accessor, tr.to(), name(), to_sort(tr(m_range.get())));
+    }
+
+    constructor::~constructor() {
+        for (accessor* a : m_accessors) dealloc(a);
+        m_accessors.reset();
+    }
+    util& constructor::u() const { return m_def->u(); }
+
+    func_decl_ref constructor::instantiate(sort_ref_vector const& ps) const {
+        ast_manager& m = ps.get_manager();
+        sort_ref_vector domain(m);
+        for (accessor const* a : accessors()) {
+            domain.push_back(a->instantiate(ps)->get_range());
+        }
+        sort_ref range = get_def().instantiate(ps);
+        parameter pas[1] = { parameter(name()) };
+        return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_CONSTRUCTOR, 1, pas, domain.size(), domain.c_ptr(), range), m);        
+    }
+
+    func_decl_ref constructor::instantiate(sort* dt) const {
+        sort_ref_vector sorts = get_def().u().datatype_params(dt);
+        return instantiate(sorts);
+    }
+
+    constructor* constructor::translate(ast_translation& tr) {
+        constructor* result = alloc(constructor, m_name, m_recognizer);
+        for (accessor* a : *this) {
+            result->add(a->translate(tr));
+        }
+        return result;
+    }
+
+
+    sort_ref def::instantiate(sort_ref_vector const& sorts) const {
+        sort_ref s(m);
+        TRACE("datatype", tout << "instantiate " << m_name << "\n";);
+        if (!m_sort) {
+            vector<parameter> ps;
+            ps.push_back(parameter(m_name));
+            for (sort * s : m_params) ps.push_back(parameter(s));
+            m_sort = m.mk_sort(u().get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr());
+        }
+        if (sorts.empty()) {
+            return m_sort;
+        }
+        return sort_ref(m.substitute(m_sort, sorts.size(), m_params.c_ptr(), sorts.c_ptr()), m);
+    }
+
+    def* def::translate(ast_translation& tr, util& u) {
+        SASSERT(&u.get_manager() == &tr.to());
+        sort_ref_vector ps(tr.to());
+        for (sort* p : m_params) {
+            ps.push_back(to_sort(tr(p)));
+        }
+        def* result = alloc(def, tr.to(), u, m_name, m_class_id, ps.size(), ps.c_ptr());
+        for (constructor* c : *this) {
+            result->add(c->translate(tr));
+        }
+        if (m_sort) result->m_sort = to_sort(tr(m_sort.get()));
+        return result;               
+    }
+
+    enum status {
+        GRAY,
+        BLACK
+    };
+
+    namespace param_size {
+        size* size::mk_offset(sort_size const& s) { return alloc(offset, s); }
+        size* size::mk_param(sort_ref& p) { return alloc(sparam, p); }
+        size* size::mk_plus(size* a1, size* a2) { return alloc(plus, a1, a2); }
+        size* size::mk_times(size* a1, size* a2) { return alloc(times, a1, a2); }
+        size* size::mk_times(ptr_vector<size>& szs) {
+            if (szs.empty()) return mk_offset(sort_size(1));
+            if (szs.size() == 1) return szs[0];
+            size* r = szs[0];
+            for (unsigned i = 1; i < szs.size(); ++i) {
+                r = mk_times(r, szs[i]);
+            }
+            return r;
+        }
+        size* size::mk_plus(ptr_vector<size>& szs) {
+            if (szs.empty()) return mk_offset(sort_size(0));
+            if (szs.size() == 1) return szs[0];
+            size* r = szs[0];
+            for (unsigned i = 1; i < szs.size(); ++i) {
+                r = mk_plus(r, szs[i]);
+            }
+            return r;
+        }
+        size* size::mk_power(size* a1, size* a2) { return alloc(power, a1, a2); }
+    }
+
+    namespace decl {
+
+        plugin::~plugin() {
+            finalize();
+        }
+
+        void plugin::finalize() {
+            for (auto& kv : m_defs) {
+                dealloc(kv.m_value);
+            }
+            m_defs.reset();
+            m_util = 0; // force deletion
+        }
+
+        util & plugin::u() const {
+            SASSERT(m_manager);
+            SASSERT(m_family_id != null_family_id);
+            if (m_util.get() == 0) {
+                m_util = alloc(util, *m_manager);
+            }
+            return *(m_util.get());
+        }
+
+        void plugin::inherit(decl_plugin* other_p, ast_translation& tr) {
+            plugin* p = dynamic_cast<plugin*>(other_p);
+            svector<symbol> names;
+            ptr_vector<def> new_defs;            
+            SASSERT(p);
+            for (auto& kv : p->m_defs) {
+                def* d = kv.m_value;
+                if (!m_defs.contains(kv.m_key)) {
+                    names.push_back(kv.m_key);
+                    new_defs.push_back(d->translate(tr, u()));
+                }
+            }
+            for (def* d : new_defs) 
+                m_defs.insert(d->name(), d);
+            m_class_id = m_defs.size();
+            u().compute_datatype_size_functions(names);
+        }
+
+
+        struct invalid_datatype {};
+
+        sort * plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
+            try {
+                if (k != DATATYPE_SORT) {
+                    TRACE("datatype", tout << "invalid kind parameter to datatype\n";);
+                    throw invalid_datatype();
+                }
+                if (num_parameters < 1) {
+                    TRACE("datatype", tout << "at least one parameter expected to datatype declaration\n";);
+                    throw invalid_datatype();                    
+                }
+                parameter const & name = parameters[0];
+                if (!name.is_symbol()) {
+                    TRACE("datatype", tout << "expected symol parameter at position " << 0 << " got: " << name << "\n";);
+                    throw invalid_datatype();
+                }
+                for (unsigned i = 1; i < num_parameters; ++i) {
+                    parameter const& s = parameters[i];
+                    if (!s.is_ast() || !is_sort(s.get_ast())) {
+                        TRACE("datatype", tout << "expected sort parameter at position " << i << " got: " << s << "\n";);
+                        throw invalid_datatype();
+                    }
+                }
+                                
+                sort* s = m_manager->mk_sort(name.get_symbol(),
+                                             sort_info(m_family_id, k, num_parameters, parameters, true));
+                def* d = 0;
+                if (m_defs.find(s->get_name(), d) && d->sort_size()) {
+                    obj_map<sort, sort_size> S;
+                    for (unsigned i = 0; i + 1 < num_parameters; ++i) {
+                        sort* r = to_sort(parameters[i + 1].get_ast());
+                        S.insert(d->params()[i], r->get_num_elements()); 
+                    }
+                    sort_size ts = d->sort_size()->eval(S);
+                    TRACE("datatype", tout << name << " has size " << ts << "\n";);
+                    s->set_num_elements(ts);
+                }
+                else {
+                    TRACE("datatype", tout << "not setting size for " << name << "\n";);
+                }
+                return s;
+            }
+            catch (invalid_datatype) {
+                m_manager->raise_exception("invalid datatype");
+                return 0;
+            }
+        }
+
+        func_decl * plugin::mk_update_field(
+            unsigned num_parameters, parameter const * parameters, 
+            unsigned arity, sort * const * domain, sort * range) {
+            decl_kind k = OP_DT_UPDATE_FIELD;
+            ast_manager& m = *m_manager;
+            
+            if (num_parameters != 1 || !parameters[0].is_ast()) {
+                m.raise_exception("invalid parameters for datatype field update");
+                return 0;
+            }
+            if (arity != 2) {
+                m.raise_exception("invalid number of arguments for datatype field update");
+                return 0;
+            }
+            func_decl* acc = 0;
+            if (is_func_decl(parameters[0].get_ast())) {
+                acc = to_func_decl(parameters[0].get_ast());
+            }
+            if (acc && !u().is_accessor(acc)) {
+                acc = 0;
+            }
+            if (!acc) {
+                m.raise_exception("datatype field update requires a datatype accessor as the second argument");
+                return 0;
+            }
+            sort* dom = acc->get_domain(0);
+            sort* rng = acc->get_range();
+            if (dom != domain[0]) {
+                m.raise_exception("first argument to field update should be a data-type");
+                return 0;
+            }
+            if (rng != domain[1]) {
+                std::ostringstream buffer;
+                buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m) 
+                       << " instead of " << mk_ismt2_pp(domain[1], m);
+                m.raise_exception(buffer.str().c_str());
+                return 0;
+            }
+            range = domain[0];
+            func_decl_info info(m_family_id, k, num_parameters, parameters);
+            return m.mk_func_decl(symbol("update-field"), arity, domain, range, info);
+        }
+
+#define VALIDATE_PARAM(_pred_) if (!(_pred_)) m_manager->raise_exception("invalid parameter to datatype function "  #_pred_);
+        
+        func_decl * decl::plugin::mk_constructor(unsigned num_parameters, parameter const * parameters, 
+                                                 unsigned arity, sort * const * domain, sort * range) {
+            ast_manager& m = *m_manager;
+            VALIDATE_PARAM(num_parameters == 1 && parameters[0].is_symbol() && range && u().is_datatype(range));
+            // we blindly trust other conditions are met, including domain types.
+            symbol name = parameters[0].get_symbol();
+            func_decl_info info(m_family_id, OP_DT_CONSTRUCTOR, num_parameters, parameters);
+            info.m_private_parameters = true;
+            return m.mk_func_decl(name, arity, domain, range, info);
+        }
+
+        func_decl * decl::plugin::mk_recognizer(unsigned num_parameters, parameter const * parameters, 
+                                                unsigned arity, sort * const * domain, sort *) {
+            ast_manager& m = *m_manager;
+            VALIDATE_PARAM(arity == 1 && num_parameters == 2 && parameters[1].is_symbol() && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
+            VALIDATE_PARAM(u().is_datatype(domain[0]));
+            // blindly trust that parameter is a constructor
+            sort* range = m_manager->mk_bool_sort();
+            func_decl_info info(m_family_id, OP_DT_RECOGNISER, num_parameters, parameters);
+            info.m_private_parameters = true;
+            return m.mk_func_decl(symbol(parameters[1].get_symbol()), arity, domain, range, info);
+        }
+
+        func_decl * decl::plugin::mk_is(unsigned num_parameters, parameter const * parameters, 
+                                                unsigned arity, sort * const * domain, sort *) {
+            ast_manager& m = *m_manager;
+            VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
+            VALIDATE_PARAM(u().is_datatype(domain[0]));
+            // blindly trust that parameter is a constructor
+            sort* range = m_manager->mk_bool_sort();
+            func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters);
+            info.m_private_parameters = true;
+            return m.mk_func_decl(symbol("is"), arity, domain, range, info);
+        }
+
+        func_decl * decl::plugin::mk_accessor(unsigned num_parameters, parameter const * parameters, 
+                                              unsigned arity, sort * const * domain, sort * range) 
+        {            
+            ast_manager& m = *m_manager;
+            VALIDATE_PARAM(arity == 1 && num_parameters == 2 && parameters[0].is_symbol() && parameters[1].is_symbol());
+            VALIDATE_PARAM(u().is_datatype(domain[0]));
+            SASSERT(range);
+            func_decl_info info(m_family_id, OP_DT_ACCESSOR, num_parameters, parameters);
+            info.m_private_parameters = true;
+            symbol name = parameters[0].get_symbol();
+            return m.mk_func_decl(name, arity, domain, range, info);           
+        }
+
+        func_decl * decl::plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, 
+                                               unsigned arity, sort * const * domain, sort * range) {                        
+            switch (k) {
+            case OP_DT_CONSTRUCTOR:
+                return mk_constructor(num_parameters, parameters, arity, domain, range);
+            case OP_DT_RECOGNISER:
+                return mk_recognizer(num_parameters, parameters, arity, domain, range);                
+            case OP_DT_IS:
+                return mk_is(num_parameters, parameters, arity, domain, range);                
+            case OP_DT_ACCESSOR:
+                return mk_accessor(num_parameters, parameters, arity, domain, range);                
+            case OP_DT_UPDATE_FIELD: 
+                return mk_update_field(num_parameters, parameters, arity, domain, range);
+            default:
+                m_manager->raise_exception("invalid datatype operator kind");
+                return 0;
+            }
+        }
+
+        def* plugin::mk(symbol const& name, unsigned n, sort * const * params) {
+            ast_manager& m = *m_manager;
+            return alloc(def, m, u(), name, m_class_id, n, params);
+        }
+
+
+        void plugin::end_def_block() {
+            ast_manager& m = *m_manager;
+
+            sort_ref_vector sorts(m);
+            for (symbol const& s : m_def_block) {
+                def const& d = *m_defs[s];
+                sort_ref_vector ps(m);
+                sorts.push_back(d.instantiate(ps));
+            }
+            for (symbol const& s : m_def_block) {
+                def& d = *m_defs[s];
+                for (constructor* c : d) {
+                    for (accessor* a : *c) {
+                        a->fix_range(sorts);
                     }
                 }
             }
-        }
-        if (can_process) {
-            already_found[tid] = BLACK;
-            todo.pop_back();
-        }
-    }
-    return false;
-}
+            if (!u().is_well_founded(sorts.size(), sorts.c_ptr())) {
+                m_manager->raise_exception("datatype is not well-founded");
+            }
 
-/**
-   \brief Return the size of the inductive datatype.
-   Pre-condition: The given argument constains the parameters of an inductive datatype.
-*/
-static sort_size get_datatype_size(parameter const * parameters) {
-    unsigned num_types          = parameters[0].get_int();
-    unsigned top_tid            = parameters[1].get_int();
-    buffer<sort_size> szs(num_types, sort_size());
-    buffer<status>    already_found(num_types, WHITE);
-    buffer<unsigned>  todo;
-    todo.push_back(top_tid);
-    while (!todo.empty()) {
-        unsigned tid  = todo.back();
-        if (already_found[tid] == BLACK) {
-            todo.pop_back();
-            continue;
-        }
-        already_found[tid] = GRAY;
-        unsigned o                 = datatype_decl_plugin::constructor_offset(parameters, tid);
-        unsigned num_constructors  = parameters[o].get_int();            
-        bool     is_very_big       = false;
-        bool     can_process       = true;
-        for (unsigned s = 1; s <= num_constructors; s++) {
-            unsigned k_i           = parameters[o+s].get_int();
-            unsigned num_accessors = parameters[k_i+2].get_int();
-            for (unsigned r = 0; r < num_accessors; r++) {
-                parameter const & a_type = parameters[k_i+4 + 2*r];
-                if (a_type.is_int()) {
-                    int tid_prime = a_type.get_int();
-                    switch (already_found[tid_prime]) {
-                    case WHITE: 
-                        todo.push_back(tid_prime);
-                        can_process = false;
-                        break;
-                    case GRAY:
-                        // type is recursive
-                        return sort_size();
-                    case BLACK:
-                        break;
-                    }
-                }
-                else { 
-                    SASSERT(a_type.is_ast());
-                    sort * ty = to_sort(a_type.get_ast());
-                    if (ty->is_infinite()) {
-                        // type is infinite
-                        return sort_size();
-                    }
-                    else if (ty->is_very_big()) {
-                        is_very_big = true;
-                    }
-                }
+            u().compute_datatype_size_functions(m_def_block);
+            for (symbol const& s : m_def_block) {
+                sort_ref_vector ps(m);
+                m_defs[s]->instantiate(ps);
             }
         }
-        if (can_process) {
-            todo.pop_back();
-            already_found[tid] = BLACK;
-            if (is_very_big) {
-                szs[tid] = sort_size::mk_very_big();
+
+        bool plugin::mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts) {
+            begin_def_block();
+            for (unsigned i = 0; i < num_datatypes; ++i) {
+                def* d = 0;
+                TRACE("datatype", tout << "declaring " << datatypes[i]->name() << "\n";);
+                if (m_defs.find(datatypes[i]->name(), d)) {
+                    TRACE("datatype", tout << "delete previous version for " << datatypes[i]->name() << "\n";);
+                    dealloc(d);
+                }
+                m_defs.insert(datatypes[i]->name(), datatypes[i]);
+                m_def_block.push_back(datatypes[i]->name());
+            }
+            end_def_block();            
+            sort_ref_vector ps(*m_manager);
+            for (symbol const& s : m_def_block) {                
+                new_sorts.push_back(m_defs[s]->instantiate(ps));
+            }
+            return true;
+        }
+
+        void plugin::remove(symbol const& s) {
+            def* d = 0;
+            if (m_defs.find(s, d)) dealloc(d);
+            m_defs.remove(s);
+        }
+
+        bool plugin::is_value_visit(expr * arg, ptr_buffer<app> & todo) const {
+            if (!is_app(arg))
+                return false;
+            family_id fid = to_app(arg)->get_family_id();
+            if (fid == m_family_id) {
+                if (!u().is_constructor(to_app(arg)))
+                    return false;
+                if (to_app(arg)->get_num_args() == 0)
+                    return true;
+                todo.push_back(to_app(arg));
+                return true;
             }
             else {
-                // the type is not infinite nor the number of elements is infinite...
-                // computing the number of elements
-                rational num;
-                for (unsigned s = 1; s <= num_constructors; s++) {
-                    unsigned k_i           = parameters[o+s].get_int();
-                    unsigned num_accessors = parameters[k_i+2].get_int();
-                    rational c_num(1); 
-                    for (unsigned r = 0; r < num_accessors; r++) {
-                        parameter const & a_type = parameters[k_i+4 + 2*r];
-                        if (a_type.is_int()) {
-                            int tid_prime = a_type.get_int();
-                            SASSERT(!szs[tid_prime].is_infinite() && !szs[tid_prime].is_very_big());
-                            c_num *= rational(szs[tid_prime].size(),rational::ui64());
-                        }
-                        else {
-                            SASSERT(a_type.is_ast());
-                            sort * ty = to_sort(a_type.get_ast());
-                            SASSERT(!ty->is_infinite() && !ty->is_very_big());
-                            c_num *= rational(ty->get_num_elements().size(), rational::ui64());
-                        }
-                    }
-                    num += c_num;
-                }
-                szs[tid] = sort_size(num);
+                return m_manager->is_value(arg);
+            }
+        }
+        
+        bool plugin::is_value(app * e) const {
+            TRACE("dt_is_value", tout << "checking\n" << mk_ismt2_pp(e, *m_manager) << "\n";);
+            if (!u().is_constructor(e))
+                return false;
+            if (e->get_num_args() == 0)
+                return true;
+            // REMARK: if the following check is too expensive, we should
+            // cache the values in the decl::plugin.
+            ptr_buffer<app> todo;
+            // potentially expensive check for common sub-expressions.
+            for (expr* arg : *e) {
+                if (!is_value_visit(arg, todo)) {
+                    TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";);
+                    return false;
+                }
+            }
+            while (!todo.empty()) {
+                app * curr = todo.back();
+                SASSERT(u().is_constructor(curr));
+                todo.pop_back();
+                for (expr* arg : *curr) {
+                    if (!is_value_visit(arg, todo)) {
+                        TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";);
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        
+        void plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
+            op_names.push_back(builtin_name("is", OP_DT_IS));
+            if (logic == symbol::null) {
+                op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD));
             }
         }
-    }
-    return szs[top_tid];
-}
 
-/**
-   \brief Return true if the inductive datatype is well-founded.
-   Pre-condition: The given argument constains the parameters of an inductive datatype.
-*/
-static bool is_well_founded(parameter const * parameters) {
-    unsigned num_types        = parameters[0].get_int();
-    buffer<bool> well_founded(num_types, false);
-    unsigned num_well_founded = 0;
-    bool changed;
-    do {
-        changed = false;
-        for (unsigned tid = 0; tid < num_types; tid++) {
-            if (!well_founded[tid]) {
-                unsigned o                 = datatype_decl_plugin::constructor_offset(parameters, tid); // constructor offset
-                unsigned num_constructors  = parameters[o].get_int();
-                for (unsigned s = 1; s <= num_constructors; s++) {
-                    unsigned k_i           = parameters[o + s].get_int();
-                    unsigned num_accessors = parameters[k_i + 2].get_int();
-                    unsigned r = 0;
-                    for (; r < num_accessors; r++) {
-                        parameter const & a_type = parameters[k_i + 4 + 2*r];
-                        if (a_type.is_int() && !well_founded[a_type.get_int()]) {
+        expr * plugin::get_some_value(sort * s) {
+            SASSERT(u().is_datatype(s));
+            func_decl * c = u().get_non_rec_constructor(s);
+            ptr_buffer<expr> args;
+            for (unsigned i = 0; i < c->get_arity(); i++) {
+                args.push_back(m_manager->get_some_value(c->get_domain(i)));
+            }
+            return m_manager->mk_app(c, args.size(), args.c_ptr());
+        }
+
+        bool plugin::is_fully_interp(sort * s) const {
+            return u().is_fully_interp(s);
+        }
+    }
+
+    sort_ref_vector util::datatype_params(sort * s) const {
+        SASSERT(is_datatype(s));
+        sort_ref_vector result(m);
+        for (unsigned i = 1; i < s->get_num_parameters(); ++i) {
+            result.push_back(to_sort(s->get_parameter(i).get_ast()));
+        }
+        return result;
+    }
+
+
+    bool util::is_fully_interp(sort * s) const {
+        SASSERT(is_datatype(s));
+        bool fi = true;
+        return fi;
+        if (m_is_fully_interp.find(s, fi)) {
+            return fi;
+        }
+        unsigned sz = m_fully_interp_trail.size();
+        m_is_fully_interp.insert(s, true);
+        def const& d = get_def(s);
+        bool is_interp = true;
+        m_fully_interp_trail.push_back(s);
+        for (constructor const* c : d) {
+            for (accessor const* a : *c) {
+                func_decl_ref ac = a->instantiate(s);
+                sort* r = ac->get_range();
+                if (!m.is_fully_interp(r)) {
+                    is_interp = false;
+                    break;
+                }
+            }
+            if (!is_interp) break;
+        }
+        for (unsigned i = sz; i < m_fully_interp_trail.size(); ++i) {
+            m_is_fully_interp.remove(m_fully_interp_trail[i]);
+        }
+        m_fully_interp_trail.shrink(sz);
+        m_is_fully_interp.insert(s, is_interp);
+        m_asts.push_back(s);
+        return true;
+    }
+
+    /**
+       \brief Return true if the inductive datatype is recursive.
+    */
+    bool util::is_recursive_core(sort* s) const {
+        obj_map<sort, status> already_found;
+        ptr_vector<sort> todo, subsorts;
+        todo.push_back(s);
+        status st;
+        while (!todo.empty()) {
+            s = todo.back();
+            if (already_found.find(s, st) && st == BLACK) {
+                todo.pop_back();
+                continue;
+            }
+            already_found.insert(s, GRAY);
+            def const& d = get_def(s);
+            bool can_process       = true;
+            for (constructor const* c : d) {
+                for (accessor const* a : *c) {
+                    sort* d = a->range();
+                    // check if d is a datatype sort
+                    subsorts.reset();
+                    get_subsorts(d, subsorts);
+                    for (sort * s2 : subsorts) {
+                        if (is_datatype(s2)) {
+                            if (already_found.find(s2, st)) {
+                                // type is recursive
+                                if (st == GRAY) return true;
+                            }
+                            else {
+                                todo.push_back(s2);
+                                can_process = false;
+                            }
+                        }
+                    }
+                }
+            }
+            if (can_process) {
+                already_found.insert(s, BLACK);
+                todo.pop_back();
+            }
+        }
+        return false;
+    }
+
+    unsigned util::get_datatype_num_parameter_sorts(sort * ty) {
+        SASSERT(ty->get_num_parameters() >= 1);
+        return ty->get_num_parameters() - 1;
+    }
+
+    sort* util::get_datatype_parameter_sort(sort * ty, unsigned idx) {
+        SASSERT(idx < get_datatype_num_parameter_sorts(ty));
+        return to_sort(ty->get_parameter(idx+1).get_ast());
+    }
+
+    param_size::size* util::get_sort_size(sort_ref_vector const& params, sort* s) {
+        if (params.empty()) {
+            return param_size::size::mk_offset(s->get_num_elements());
+        }
+        if (is_datatype(s)) {
+            param_size::size* sz;
+            obj_map<sort, param_size::size*> S;
+            unsigned n = get_datatype_num_parameter_sorts(s);
+            for (unsigned i = 0; i < n; ++i) {
+                sort* ps = get_datatype_parameter_sort(s, i);
+                sz = get_sort_size(params, ps);
+                sz->inc_ref();
+                S.insert(ps, sz); 
+            }
+            def & d = get_def(s->get_name());
+            sz = d.sort_size()->subst(S);
+            for (auto & kv : S) {
+                kv.m_value->dec_ref();
+            }
+            return sz;
+        }
+        array_util autil(m);
+        if (autil.is_array(s)) {
+            unsigned n = get_array_arity(s);
+            ptr_vector<param_size::size> szs;
+            for (unsigned i = 0; i < n; ++i) {
+                szs.push_back(get_sort_size(params, get_array_domain(s, i)));
+            }
+            param_size::size* sz1 = param_size::size::mk_times(szs);
+            param_size::size* sz2 = get_sort_size(params, get_array_range(s));
+            return param_size::size::mk_power(sz2, sz1);
+        }
+        for (sort* p : params) {           
+            if (s == p) {
+                sort_ref sr(s, m);
+                return param_size::size::mk_param(sr);
+            }
+        }
+        return param_size::size::mk_offset(s->get_num_elements());        
+    }
+
+    bool util::is_declared(sort* s) const {
+        return m_plugin->is_declared(s);
+    }
+    
+    void util::compute_datatype_size_functions(svector<symbol> const& names) {
+        map<symbol, status, symbol_hash_proc, symbol_eq_proc> already_found;
+        map<symbol, param_size::size*, symbol_hash_proc, symbol_eq_proc> szs;
+
+        svector<symbol> todo(names);
+        status st;
+        while (!todo.empty()) {
+            symbol s = todo.back();
+            TRACE("datatype", tout << "Sort size for " << s << "\n";);
+
+            if (already_found.find(s, st) && st == BLACK) {
+                todo.pop_back();
+                continue;
+            }
+            already_found.insert(s, GRAY);
+            bool is_infinite = false;
+            bool can_process = true;
+            def& d = get_def(s);
+            for (constructor const* c : d) {
+                for (accessor const* a : *c) {
+                    sort* r = a->range();
+                    if (is_datatype(r)) {
+                        symbol s2 = r->get_name();
+                        if (already_found.find(s2, st)) {
+                            // type is infinite
+                            if (st == GRAY) {
+                                is_infinite = true;
+                            }
+                        }
+                        else if (names.contains(s2)) {
+                            todo.push_back(s2);
+                            can_process = false;
+                        }
+                    }
+                }
+            }
+            if (!can_process) {
+                continue;
+            }
+            todo.pop_back();
+            already_found.insert(s, BLACK);
+            if (is_infinite) {
+                d.set_sort_size(param_size::size::mk_offset(sort_size::mk_infinite()));
+                continue;
+            }
+
+            ptr_vector<param_size::size> s_add;        
+            for (constructor const* c : d) {
+                ptr_vector<param_size::size> s_mul;
+                for (accessor const* a : *c) {
+                    s_mul.push_back(get_sort_size(d.params(), a->range()));
+                }
+                s_add.push_back(param_size::size::mk_times(s_mul));
+            }
+            d.set_sort_size(param_size::size::mk_plus(s_add));
+        }
+    }
+    
+
+    /**
+       \brief Return true if the inductive datatype is well-founded.
+       Pre-condition: The given argument constains the parameters of an inductive datatype.
+    */
+    bool util::is_well_founded(unsigned num_types, sort* const* sorts) {
+        buffer<bool> well_founded(num_types, false);
+        obj_map<sort, unsigned> sort2id;
+        for (unsigned i = 0; i < num_types; ++i) {
+            sort2id.insert(sorts[i], i);
+        }
+        unsigned num_well_founded = 0, id = 0;
+        bool changed;
+        do {
+            changed = false;
+            for (unsigned tid = 0; tid < num_types; tid++) {
+                if (well_founded[tid]) {
+                    continue;
+                }
+                sort* s = sorts[tid];
+                def const& d = get_def(s);
+                for (constructor const* c : d) {
+                    bool found_nonwf = false;
+                    for (accessor const* a : *c) {
+                        if (sort2id.find(a->range(), id) && !well_founded[id]) {
+                            found_nonwf = true;
                             break;
                         }
                     }
-                    if (r ==  num_accessors) {
+                    if (!found_nonwf) {
                         changed = true;
                         well_founded[tid] = true;
                         num_well_founded++;
@@ -333,759 +715,358 @@ static bool is_well_founded(parameter const * parameters) {
                     }
                 }
             }
-        }
-    } while(changed && num_well_founded < num_types);
-    unsigned tid = parameters[1].get_int();
-    return well_founded[tid];
-}
-
-datatype_decl_plugin::~datatype_decl_plugin() {
-    SASSERT(m_util.get() == 0);
-}
-
-void datatype_decl_plugin::finalize() {
-    m_util = 0; // force deletion
-}
-
-datatype_util & datatype_decl_plugin::get_util() const {
-    SASSERT(m_manager);
-    if (m_util.get() == 0) {
-        m_util = alloc(datatype_util, *m_manager);
+        } 
+        while(changed && num_well_founded < num_types);
+        return num_well_founded == num_types;
     }
-    return *(m_util.get());
-}
 
-        
-sort * datatype_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
-    try {
-        if (k != DATATYPE_SORT) {
-            throw invalid_datatype();
-        }
-        buffer<bool, false, 256> found;
-        unsigned num_types = read_int(num_parameters, parameters, 0, found);
-        if (num_types == 0) {
-            throw invalid_datatype();
-        }
-        unsigned tid       = read_int(num_parameters, parameters, 1, found);
-        unsigned num_sort_params = read_int(num_parameters, parameters, 2, found);
-        for (unsigned j = 0; j < num_sort_params; ++j) {
-            read_sort(num_parameters, parameters, 3 + j, found);
-        }
-        unsigned c_offset  = constructor_offset(parameters);
-        for (unsigned j = 0; j < num_types; j++) {
-            read_symbol(num_parameters, parameters, c_offset + 2*j, found); // type name
-            unsigned       o          = read_int(num_parameters, parameters, c_offset + 2*j + 1, found);
-            unsigned num_constructors = read_int(num_parameters, parameters, o, found);
-            if (num_constructors == 0) {
-                throw invalid_datatype();
+    def const& util::get_def(sort* s) const {
+        return m_plugin->get_def(s);
+    }
+
+    void util::get_subsorts(sort* s, ptr_vector<sort>& sorts) const {
+        sorts.push_back(s);
+        for (unsigned i = 0; i < s->get_num_parameters(); ++i) {
+            parameter const& p = s->get_parameter(i);
+            if (p.is_ast() && is_sort(p.get_ast())) {
+                get_subsorts(to_sort(p.get_ast()), sorts);
             }
-            for (unsigned s = 1; s <= num_constructors; s++) {
-                unsigned k_i            = read_int(num_parameters, parameters, o + s, found);
-                read_symbol(num_parameters, parameters, k_i, found);      // constructor name
-                read_symbol(num_parameters, parameters, k_i + 1, found);  // recognizer name
-                unsigned num_accessors  = read_int(num_parameters, parameters, k_i + 2, found); 
-                unsigned first_accessor = k_i+3;
-                for (unsigned r = 0; r < num_accessors; r++) {
-                    read_symbol(num_parameters, parameters, first_accessor + 2*r, found); // accessor name
-                    parameter const & a_type = read(num_parameters, parameters, first_accessor + 2*r + 1, found); // accessort type
-                    if (!a_type.is_int() && !a_type.is_ast()) {
-                        throw invalid_datatype();
-                    }
-                    if (a_type.is_ast() && !is_sort(a_type.get_ast())) {
-                        throw invalid_datatype();
-                    }
+        }
+    }
+
+
+    util::util(ast_manager & m):
+        m(m),
+        m_family_id(m.mk_family_id("datatype")),
+        m_asts(m),
+        m_start(0) {
+        m_plugin = dynamic_cast<decl::plugin*>(m.get_plugin(m_family_id));
+        SASSERT(m_plugin);
+    }
+
+    util::~util() {
+        std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc<ptr_vector<func_decl> >());
+    }
+
+    ptr_vector<func_decl> const * util::get_datatype_constructors(sort * ty) {
+        SASSERT(is_datatype(ty));
+        ptr_vector<func_decl> * r = 0;
+        if (m_datatype2constructors.find(ty, r))
+            return r;
+        r = alloc(ptr_vector<func_decl>);
+        m_asts.push_back(ty);
+        m_vectors.push_back(r);
+        m_datatype2constructors.insert(ty, r);
+        def const& d = get_def(ty);
+        for (constructor const* c : d) {
+            func_decl_ref f = c->instantiate(ty);
+            m_asts.push_back(f);
+            r->push_back(f);
+        }
+        return r;
+    }
+
+    ptr_vector<func_decl> const * util::get_constructor_accessors(func_decl * con) {
+        SASSERT(is_constructor(con));
+        ptr_vector<func_decl> * res = 0;
+        if (m_constructor2accessors.find(con, res)) {
+            return res;
+        }
+        res = alloc(ptr_vector<func_decl>);
+        m_asts.push_back(con);
+        m_vectors.push_back(res);
+        m_constructor2accessors.insert(con, res);
+        sort * datatype = con->get_range();
+        def const& d = get_def(datatype);
+        for (constructor const* c : d) {
+            if (c->name() == con->get_name()) {
+                for (accessor const* a : *c) {
+                    func_decl_ref fn = a->instantiate(datatype);
+                    res->push_back(fn);
+                    m_asts.push_back(fn);
                 }
+                break;
             }
         }
-        // check if there is no garbage
-        if (found.size() != num_parameters || std::find(found.begin(), found.end(), false) != found.end()) {
-            throw invalid_datatype();
-        }
+        return res;
+    }
 
-        if (!is_well_founded(parameters)) {
-            m_manager->raise_exception("datatype is not well-founded");
-            return 0;
-        }
-        
-        // compute datatype size
-        sort_size ts = get_datatype_size(parameters);
-        symbol const & tname = parameters[c_offset + 2*tid].get_symbol();
-        return m_manager->mk_sort(tname,
-                                  sort_info(m_family_id, k, ts, num_parameters, parameters, true));
-    }
-    catch (invalid_datatype) {
-        m_manager->raise_exception("invalid datatype");
-        return 0;
-    }
-}
-
-static sort * get_other_datatype(ast_manager & m, family_id datatype_fid, sort * source_datatype, unsigned tid) {
-    SASSERT(source_datatype->get_family_id() == datatype_fid);
-    SASSERT(source_datatype->get_decl_kind() == DATATYPE_SORT);
-    if (tid == static_cast<unsigned>(source_datatype->get_parameter(1).get_int())) {
-        return source_datatype;
-    }
-    buffer<parameter> p;
-    unsigned n = source_datatype->get_num_parameters();
-    for (unsigned i = 0; i < n; i++) {
-        p.push_back(source_datatype->get_parameter(i));
-    }
-    p[1] = parameter(tid);
-    return m.mk_sort(datatype_fid, DATATYPE_SORT, n, p.c_ptr());
-}
-
-static sort * get_type(ast_manager & m, family_id datatype_fid, sort * source_datatype, parameter const & p) {
-    SASSERT(p.is_ast() || p.is_int());
-    if (p.is_ast()) {
-        return to_sort(p.get_ast());
-    }
-    else {
-        return get_other_datatype(m, datatype_fid, source_datatype, p.get_int());
-    }
-}
-
-func_decl * datatype_decl_plugin::mk_update_field(
-    unsigned num_parameters, parameter const * parameters, 
-    unsigned arity, sort * const * domain, sort * range) {
-    decl_kind k = OP_DT_UPDATE_FIELD;
-    ast_manager& m = *m_manager;
-
-    if (num_parameters != 1 || !parameters[0].is_ast()) {
-        m.raise_exception("invalid parameters for datatype field update");
-        return 0;
-    }
-    if (arity != 2) {
-        m.raise_exception("invalid number of arguments for datatype field update");
-        return 0;
-    }
-    func_decl* acc = 0;
-    if (is_func_decl(parameters[0].get_ast())) {
-        acc = to_func_decl(parameters[0].get_ast());
-    }
-    if (acc && !get_util().is_accessor(acc)) {
-        acc = 0;
-    }
-    if (!acc) {
-        m.raise_exception("datatype field update requires a datatype accessor as the second argument");
-        return 0;
-    }
-    sort* dom = acc->get_domain(0);
-    sort* rng = acc->get_range();
-    if (dom != domain[0]) {
-        m.raise_exception("first argument to field update should be a data-type");
-        return 0;
-    }
-    if (rng != domain[1]) {
-        std::ostringstream buffer;
-        buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m) 
-               << " instead of " << mk_ismt2_pp(domain[1], m);
-        m.raise_exception(buffer.str().c_str());
-        return 0;
-    }
-    range = domain[0];
-    func_decl_info info(m_family_id, k, num_parameters, parameters);
-    return m.mk_func_decl(symbol("update-field"), arity, domain, range, info);
-}
-
-func_decl * datatype_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, 
-                                             unsigned arity, sort * const * domain, sort * range) {
-
-    if (k == OP_DT_UPDATE_FIELD) {
-        return mk_update_field(num_parameters, parameters, arity, domain, range);
-    }
-    if (num_parameters < 2 || !parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) {
-        m_manager->raise_exception("invalid parameters for datatype operator");
-        return 0;
-    }
-    sort * datatype = to_sort(parameters[0].get_ast());
-    if (datatype->get_family_id() != m_family_id ||
-        datatype->get_decl_kind() != DATATYPE_SORT) {
-        m_manager->raise_exception("invalid parameters for datatype operator");
-        return 0;
-    }
-    for (unsigned i = 1; i < num_parameters; i++) {
-        if (!parameters[i].is_int()) {
-            m_manager->raise_exception("invalid parameters for datatype operator");
-            return 0;
-        }
-    }
-    unsigned c_idx            = parameters[1].get_int();
-    unsigned tid              = datatype->get_parameter(1).get_int();
-    unsigned o                = datatype_decl_plugin::constructor_offset(datatype, tid);
-    unsigned num_constructors = datatype->get_parameter(o).get_int();
-    if (c_idx >= num_constructors) {
-        m_manager->raise_exception("invalid parameters for datatype operator");
-        return 0;
-    }
-    unsigned k_i              = datatype->get_parameter(o + 1 + c_idx).get_int();
-
-    switch (k) {
-    case OP_DT_CONSTRUCTOR:
-        if (num_parameters != 2) { 
-            m_manager->raise_exception("invalid parameters for datatype constructor");
-            return 0;
-        }
-        else {
-            symbol   c_name           = datatype->get_parameter(k_i).get_symbol();
-            unsigned num_accessors    = datatype->get_parameter(k_i + 2).get_int();
-            if (num_accessors != arity) {
-                m_manager->raise_exception("invalid domain size for datatype constructor");
-                return 0;
+    func_decl * util::get_constructor_recognizer(func_decl * con) {
+        SASSERT(is_constructor(con));
+        func_decl * d = 0;
+        if (m_constructor2recognizer.find(con, d))
+            return d;
+        sort * datatype = con->get_range();
+        def const& dd = get_def(datatype);
+        symbol r;
+        for (constructor const* c : dd) {
+            if (c->name() == con->get_name()) {
+                r = c->recognizer();
             }
+        }
+        parameter ps[2] = { parameter(con), parameter(r) };
+        d  = m.mk_func_decl(m_family_id, OP_DT_RECOGNISER, 2, ps, 1, &datatype);
+        SASSERT(d);
+        m_asts.push_back(con);
+        m_asts.push_back(d);
+        m_constructor2recognizer.insert(con, d);
+        return d;
+    }
 
-            //
-            // the reference count to domain could be 0.
-            // we need to ensure that creating a temporary
-            // copy of the same type causes a free.
-            // 
-            sort_ref_vector domain_check(*m_manager);
+    func_decl * util::get_recognizer_constructor(func_decl * recognizer) const {
+        SASSERT(is_recognizer(recognizer));
+        return to_func_decl(recognizer->get_parameter(0).get_ast());
+    }
 
-            for (unsigned r = 0; r < num_accessors; r++) {
-                sort_ref ty(*m_manager);
-                ty = get_type(*m_manager, m_family_id, datatype, datatype->get_parameter(k_i + 4 + 2*r));
-                domain_check.push_back(ty);
-                if (ty != domain[r]) {
-                    m_manager->raise_exception("invalid domain for datatype constructor");
-                    return 0;
+    bool util::is_recursive(sort * ty) {
+        SASSERT(is_datatype(ty));
+        bool r = false;
+        if (!m_is_recursive.find(ty, r)) {
+            r = is_recursive_core(ty);
+            m_is_recursive.insert(ty, r);
+            m_asts.push_back(ty);
+        }
+        return r;
+    }
+
+    bool util::is_enum_sort(sort* s) {
+        if (!is_datatype(s)) {
+            return false;
+        }
+        bool r = false;
+        if (m_is_enum.find(s, r))
+            return r;
+        ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
+        r = true;
+        for (unsigned i = 0; r && i < cnstrs.size(); ++i) {
+            r = cnstrs[i]->get_arity() == 0;
+        }
+        m_is_enum.insert(s, r);
+        m_asts.push_back(s);
+        return r;
+    }
+
+    func_decl * util::get_accessor_constructor(func_decl * accessor) { 
+        SASSERT(is_accessor(accessor));
+        func_decl * r = 0;
+        if (m_accessor2constructor.find(accessor, r))
+            return r;
+        sort * datatype = accessor->get_domain(0);
+        symbol c_id   = accessor->get_parameter(1).get_symbol();
+        def const& d = get_def(datatype);
+        func_decl_ref fn(m);
+        for (constructor const* c : d) {
+            if (c->name() == c_id) {
+                fn = c->instantiate(datatype);
+                break;
+            }
+        }
+        r = fn;
+        m_accessor2constructor.insert(accessor, r);
+        m_asts.push_back(accessor);
+        m_asts.push_back(r);
+        return r;
+    }
+
+
+    void util::reset() {
+        m_datatype2constructors.reset();
+        m_datatype2nonrec_constructor.reset();
+        m_constructor2accessors.reset();
+        m_constructor2recognizer.reset();
+        m_recognizer2constructor.reset();
+        m_accessor2constructor.reset();
+        m_is_recursive.reset();
+        m_is_enum.reset();
+        std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc<ptr_vector<func_decl> >());
+        m_vectors.reset();
+        m_asts.reset();
+        ++m_start;
+    }
+
+
+    /**
+       \brief Return a constructor mk(T_1, ... T_n)
+       where each T_i is not a datatype or it is a datatype that contains 
+       a constructor that will not contain directly or indirectly an element of the given sort.
+    */
+    func_decl * util::get_non_rec_constructor(sort * ty) {
+        SASSERT(is_datatype(ty));
+        func_decl * r = 0;
+        if (m_datatype2nonrec_constructor.find(ty, r))
+            return r;
+        r = 0;
+        ptr_vector<sort> forbidden_set;
+        forbidden_set.push_back(ty);
+        TRACE("util_bug", tout << "invoke get-non-rec: " << sort_ref(ty, m) << "\n";);
+        r = get_non_rec_constructor_core(ty, forbidden_set);
+        SASSERT(forbidden_set.back() == ty);
+        SASSERT(r);
+        m_asts.push_back(ty);
+        m_asts.push_back(r);
+        m_datatype2nonrec_constructor.insert(ty, r);
+        return r;
+    }
+
+    /**
+       \brief Return a constructor mk(T_1, ..., T_n) where
+       each T_i is not a datatype or it is a datatype t not in forbidden_set,
+       and get_non_rec_constructor_core(T_i, forbidden_set union { T_i })
+    */
+    func_decl * util::get_non_rec_constructor_core(sort * ty, ptr_vector<sort> & forbidden_set) {
+        // We must select a constructor c(T_1, ..., T_n):T such that
+        //   1) T_i's are not recursive
+        // If there is no such constructor, then we select one that 
+        //   2) each type T_i is not recursive or contains a constructor that does not depend on T
+
+        ptr_vector<func_decl> const& constructors = *get_datatype_constructors(ty);
+        unsigned sz = constructors.size();
+        TRACE("util_bug", tout << "get-non-rec constructor: " << sort_ref(ty, m) << "\n";
+              tout << "forbidden: ";
+              for (sort* s : forbidden_set) tout << sort_ref(s, m) << " ";
+              tout << "\n";
+              tout << "constructors: " << sz << "\n";
+              for (func_decl* f : constructors) tout << func_decl_ref(f, m) << "\n";
+              );
+        // step 1)
+        unsigned start = ++m_start;
+        for (unsigned j = 0; j < sz; ++j) {        
+            func_decl * c = constructors[(j + start) % sz];
+            TRACE("util_bug", tout << "checking " << sort_ref(ty, m) << ": " << func_decl_ref(c, m) << "\n";);
+            unsigned num_args = c->get_arity();
+            unsigned i = 0;
+            for (; i < num_args && !is_datatype(c->get_domain(i)); i++);
+            if (i == num_args) {
+                TRACE("util_bug", tout << "found non-rec " << func_decl_ref(c, m) << "\n";);
+                return c;
+            }
+        }
+        // step 2)
+        for (unsigned j = 0; j < sz; ++j) {        
+            func_decl * c = constructors[(j + start) % sz];
+            TRACE("util_bug", tout << "non_rec_constructor c: " << j << " " << func_decl_ref(c, m) << "\n";);
+            unsigned num_args = c->get_arity();
+            unsigned i = 0;
+            for (; i < num_args; i++) {
+                sort * T_i = c->get_domain(i);
+                TRACE("util_bug", tout << "c: " << i << " " << sort_ref(T_i, m) << "\n";);
+                if (!is_datatype(T_i)) {
+                    TRACE("util_bug", tout << sort_ref(T_i, m) << " is not a datatype\n";);
+                    continue;
                 }
+                if (std::find(forbidden_set.begin(), forbidden_set.end(), T_i) != forbidden_set.end()) {
+                    TRACE("util_bug", tout << sort_ref(T_i, m) << " is in forbidden_set\n";);
+                    break;
+                }
+                forbidden_set.push_back(T_i);
+                func_decl * nested_c = get_non_rec_constructor_core(T_i, forbidden_set);
+                SASSERT(forbidden_set.back() == T_i);
+                forbidden_set.pop_back();
+                if (nested_c == 0)
+                    break;
+                TRACE("util_bug", tout << "nested_c: " << nested_c->get_name() << "\n";);
+            }
+            if (i == num_args)
+                return c;
+        }
+        return 0;
+    }
 
+    unsigned util::get_constructor_idx(func_decl * f) const {
+        unsigned idx = 0;
+        def const& d = get_def(f->get_range());
+        for (constructor* c : d) {
+            if (c->name() == f->get_name()) {
+                return idx;
             }
-            func_decl_info info(m_family_id, k, num_parameters, parameters);
-            info.m_private_parameters = true;
-            SASSERT(info.private_parameters());
-            return m_manager->mk_func_decl(c_name, arity, domain, datatype, info);
+            ++idx;
         }
-    case OP_DT_RECOGNISER:
-        if (num_parameters != 2 || arity != 1 || domain[0] != datatype) { 
-            m_manager->raise_exception("invalid parameters for datatype recogniser");
-            return 0;
-        }
-        else {
-            symbol   r_name = datatype->get_parameter(k_i + 1).get_symbol();
-            sort *    b     = m_manager->mk_bool_sort();
-            func_decl_info info(m_family_id, k, num_parameters, parameters);
-            info.m_private_parameters = true;
-            SASSERT(info.private_parameters());
-            return m_manager->mk_func_decl(r_name, arity, domain, b, info);
-        }
-    case OP_DT_ACCESSOR:
-        if (num_parameters != 3 || arity != 1 || domain[0] != datatype) { 
-            m_manager->raise_exception("invalid parameters for datatype accessor");
-            return 0;
-        }
-        else {
-            unsigned a_idx            = parameters[2].get_int();
-            unsigned num_accessors    = datatype->get_parameter(k_i + 2).get_int();
-            if (a_idx >= num_accessors) {
-                m_manager->raise_exception("invalid datatype accessor");
-                return 0;
-            }
-            symbol a_name         = datatype->get_parameter(k_i + 3 + 2*a_idx).get_symbol();
-            sort * a_type         = get_type(*m_manager, m_family_id, datatype, datatype->get_parameter(k_i + 4 + 2*a_idx));
-            func_decl_info info(m_family_id, k, num_parameters, parameters);
-            info.m_private_parameters = true;
-            SASSERT(info.private_parameters());
-            return m_manager->mk_func_decl(a_name, arity, domain, a_type, info);
-        }
-        break;
-    case OP_DT_UPDATE_FIELD: 
         UNREACHABLE();
         return 0;
-    default:
-        m_manager->raise_exception("invalid datatype operator kind");
-        return 0;
     }
-}
 
-bool datatype_decl_plugin::mk_datatypes(unsigned num_datatypes, datatype_decl * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_types) {
-    buffer<parameter> p;
-    p.push_back(parameter(num_datatypes));
-    p.push_back(parameter(-1));
-    p.push_back(parameter(num_params));
-    for (unsigned i = 0; i < num_params; ++i) {
-        p.push_back(parameter(sort_params[i]));
+    unsigned util::get_recognizer_constructor_idx(func_decl * f) const {
+        return get_constructor_idx(get_recognizer_constructor(f));
     }
-    
-    unsigned c_offset = constructor_offset(p.c_ptr());
-    for (unsigned i = 0; i < num_datatypes; i++) {
-        p.push_back(parameter(datatypes[i]->get_name()));
-        p.push_back(parameter(-1)); // offset is unknown at this point
-    }
-    for (unsigned i = 0; i < num_datatypes; i++) {
-        p[c_offset + 1 + 2*i] = parameter(p.size()); // save offset to constructor table
-        ptr_vector<constructor_decl> const & constructors = datatypes[i]->get_constructors();
-        unsigned num_constructors = constructors.size();
-        p.push_back(parameter(num_constructors));
-        for (unsigned j = 0; j < num_constructors; j++) {
-            p.push_back(parameter(-1)); // offset is unknown at this point
+
+    /**
+       \brief Two datatype sorts s1 and s2 are siblings if they were
+       defined together in the same mutually recursive definition.
+    */
+    bool util::are_siblings(sort * s1, sort * s2) {
+        if (!is_datatype(s1) || !is_datatype(s2)) {
+            return s1 == s2;
+        }
+        else {
+            return get_def(s1).id() == get_def(s2).id();
         }
     }
-    for (unsigned i = 0; i < num_datatypes; i++) {
-        unsigned       o          = constructor_offset(p.c_ptr(), i);
-        ptr_vector<constructor_decl> const & constructors = datatypes[i]->get_constructors();
-        unsigned num_constructors = constructors.size();
-        for (unsigned j = 0; j < num_constructors; j++) {
-            p[o+1+j] = parameter(p.size()); // save offset to constructor definition
-            constructor_decl * c  = constructors[j];
-            p.push_back(parameter(c->get_name()));
-            p.push_back(parameter(c->get_recognizer_name()));
-            ptr_vector<accessor_decl> const & accessors = c->get_accessors();
-            unsigned num_accessors = accessors.size();
-            p.push_back(parameter(num_accessors));
-            for (unsigned k = 0; k < num_accessors; k++) {
-                accessor_decl * a  = accessors[k];
-                p.push_back(parameter(a->get_name()));
-                type_ref const & ty = a->get_type(); 
-                if (ty.is_idx()) {
-                    if (static_cast<unsigned>(ty.get_idx()) >= num_datatypes) {
-                        TRACE("datatype", tout << "Index out of bounds: " << ty.get_idx() << "\n";);
-                        return false;
+
+    unsigned util::get_datatype_num_constructors(sort * ty) {
+        def const& d = get_def(ty->get_name());
+        return d.constructors().size();
+    }
+
+    void util::get_defs(sort* s0, ptr_vector<def>& defs) {
+        svector<symbol> mark;
+        ptr_buffer<sort> todo;
+        todo.push_back(s0);
+        mark.push_back(s0->get_name());
+        while (!todo.empty()) {
+            sort* s = todo.back();
+            todo.pop_back();
+            defs.push_back(&m_plugin->get_def(s->get_name()));
+            def const& d = get_def(s);
+            for (constructor* c : d) {
+                for (accessor* a : *c) {
+                    sort* s = a->range();
+                    if (are_siblings(s0, s) && !mark.contains(s->get_name())) {
+                        mark.push_back(s->get_name());
+                        todo.push_back(s);
                     }
-                    p.push_back(parameter(ty.get_idx()));
-                }
-                else {
-                    p.push_back(parameter(ty.get_sort()));
                 }
             }
         }
     }
-    for (unsigned i = 0; i < num_datatypes; i++) {
-        p[1] = parameter(i);
-        TRACE("datatype", tout << "new datatype parameters:\n";
-              for (unsigned j = 0; j < p.size(); j++) {
-                  tout << "p[" << j << "] -> " << p[j] << "\n";
-              });
-        sort * ty = mk_sort(DATATYPE_SORT, p.size(), p.c_ptr());
-        if (ty == 0) {
-            TRACE("datatype", tout << "Failed to create datatype sort from parameters\n";);
-            return false;
-        }
-        new_types.push_back(ty);
-    }
-    return true;
-}
 
-expr * datatype_decl_plugin::get_some_value(sort * s) {
-    SASSERT(s->is_sort_of(m_family_id, DATATYPE_SORT));
-    datatype_util & util = get_util();
-    func_decl * c = util.get_non_rec_constructor(s);
-    ptr_buffer<expr> args;
-    for (unsigned i = 0; i < c->get_arity(); i++) {
-        args.push_back(m_manager->get_some_value(c->get_domain(i)));
-    }
-    return m_manager->mk_app(c, args.size(), args.c_ptr());
-}
+    void util::display_datatype(sort *s0, std::ostream& out) {
+        ast_mark mark;
+        ptr_buffer<sort> todo;
+        SASSERT(is_datatype(s0));
+        out << s0->get_name() << " where\n";
+        todo.push_back(s0);
+        mark.mark(s0, true);
+        while (!todo.empty()) {
+            sort* s = todo.back();
+            todo.pop_back();
+            out << s->get_name() << " =\n";
 
-bool datatype_decl_plugin::is_fully_interp(sort * s) const {
-    SASSERT(s->is_sort_of(m_family_id, DATATYPE_SORT));
-    parameter const * parameters = s->get_parameters();
-    unsigned num_types        = parameters[0].get_int();
-    for (unsigned tid = 0; tid < num_types; tid++) {
-        unsigned o                 = datatype_decl_plugin::constructor_offset(s, tid);
-        unsigned num_constructors  = parameters[o].get_int();
-        for (unsigned si = 1; si <= num_constructors; si++) {
-            unsigned k_i           = parameters[o + si].get_int();
-            unsigned num_accessors = parameters[k_i + 2].get_int();
-            unsigned r = 0;
-            for (; r < num_accessors; r++) {
-                parameter const & a_type = parameters[k_i + 4 + 2*r];
-                if (a_type.is_int())
-                    continue;
-                SASSERT(a_type.is_ast());
-                sort * arg_s = to_sort(a_type.get_ast());
-                if (!m_manager->is_fully_interp(arg_s))
-                    return false;
-            }
-        }
-    }
-    return true;
-}
-
-bool datatype_decl_plugin::is_value_visit(expr * arg, ptr_buffer<app> & todo) const {
-    if (!is_app(arg))
-        return false;
-    family_id fid = to_app(arg)->get_family_id();
-    if (fid == m_family_id) {
-        if (!get_util().is_constructor(to_app(arg)))
-            return false;
-        if (to_app(arg)->get_num_args() == 0)
-            return true;
-        todo.push_back(to_app(arg));
-        return true;
-    }
-    else {
-        return m_manager->is_value(arg);
-    }
-}
-
-unsigned datatype_decl_plugin::constructor_offset(sort const* s)  {
-    return constructor_offset(s->get_parameters());
-}
-
-unsigned datatype_decl_plugin::constructor_offset(parameter const& p) {
-    return 3 + p.get_int();
-}
-
-unsigned datatype_decl_plugin::constructor_offset(parameter const* ps) {
-    return constructor_offset(ps[2]);
-}
-
-unsigned datatype_decl_plugin::constructor_offset(sort const* s, unsigned tid)  {
-    unsigned c_offset = constructor_offset(s->get_parameters());
-    return s->get_parameter(c_offset + 1 + 2*tid).get_int();
-}
-
-unsigned datatype_decl_plugin::constructor_offset(parameter const* ps, unsigned tid) {
-    unsigned c_offset = constructor_offset(ps[2]);
-    return ps[c_offset + 1 + 2*tid].get_int();
-}
-
-bool datatype_decl_plugin::is_value(app * e) const {
-    TRACE("dt_is_value", tout << "checking\n" << mk_ismt2_pp(e, *m_manager) << "\n";);
-    if (!get_util().is_constructor(e))
-        return false;
-    if (e->get_num_args() == 0)
-        return true;
-    // REMARK: if the following check is too expensive, we should
-    // cache the values in the datatype_decl_plugin.
-    ptr_buffer<app> todo;
-    // potentially expensive check for common sub-expressions.
-    for (unsigned i = 0; i < e->get_num_args(); i++) {
-        if (!is_value_visit(e->get_arg(i), todo)) {
-            TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(e->get_arg(i), *m_manager) << "\n";);
-            return false;
-        }
-    }
-    while (!todo.empty()) {
-        app * curr = todo.back();
-        SASSERT(get_util().is_constructor(curr));
-        todo.pop_back();
-        for (unsigned i = 0; i < curr->get_num_args(); i++) {
-            if (!is_value_visit(curr->get_arg(i), todo)) {
-                TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(curr->get_arg(i), *m_manager) << "\n";);
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-void datatype_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
-    if (logic == symbol::null) {
-        op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD));
-    }
-}
-
-
-datatype_util::datatype_util(ast_manager & m):
-    m_manager(m),
-    m_family_id(m.mk_family_id("datatype")),
-    m_asts(m),
-    m_start(0) {
-}
-
-datatype_util::~datatype_util() {
-    std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc<ptr_vector<func_decl> >());
-}
-
-func_decl * datatype_util::get_constructor(sort * ty, unsigned c_id) {
-    unsigned tid           = ty->get_parameter(1).get_int();
-    unsigned o             = datatype_decl_plugin::constructor_offset(ty, tid);
-    unsigned k_i           = ty->get_parameter(o + c_id + 1).get_int();
-    unsigned num_accessors = ty->get_parameter(k_i + 2).get_int();
-    parameter p[2]         = { parameter(ty), parameter(c_id) };
-    ptr_buffer<sort> domain;
-    for (unsigned r = 0; r < num_accessors; r++) {
-        domain.push_back(get_type(m_manager, m_family_id, ty, ty->get_parameter(k_i + 4 + 2*r)));
-    }
-    func_decl * d = m_manager.mk_func_decl(m_family_id, OP_DT_CONSTRUCTOR, 2, p, domain.size(), domain.c_ptr());
-    SASSERT(d);
-    return d;
-}
-
-ptr_vector<func_decl> const * datatype_util::get_datatype_constructors(sort * ty) {
-    SASSERT(is_datatype(ty));
-    ptr_vector<func_decl> * r = 0;
-    if (m_datatype2constructors.find(ty, r))
-        return r;
-    r = alloc(ptr_vector<func_decl>);
-    m_asts.push_back(ty);
-    m_vectors.push_back(r);
-    m_datatype2constructors.insert(ty, r);
-    unsigned tid               = ty->get_parameter(1).get_int();
-    unsigned o                 = datatype_decl_plugin::constructor_offset(ty, tid);
-    unsigned num_constructors  = ty->get_parameter(o).get_int();
-    for (unsigned c_id = 0; c_id < num_constructors; c_id++) {
-        func_decl * c = get_constructor(ty, c_id);
-        m_asts.push_back(c);
-        r->push_back(c);
-    }
-    return r;
-}
-
-/**
-   \brief Return a constructor mk(T_1, ... T_n)
-   where each T_i is not a datatype or it is a datatype that contains 
-   a constructor that will not contain directly or indirectly an element of the given sort.
-*/
-func_decl * datatype_util::get_non_rec_constructor(sort * ty) {
-    SASSERT(is_datatype(ty));
-    func_decl * r = 0;
-    if (m_datatype2nonrec_constructor.find(ty, r))
-        return r;
-    r = 0;
-    ptr_vector<sort> forbidden_set;
-    forbidden_set.push_back(ty);
-    r = get_non_rec_constructor_core(ty, forbidden_set);
-    SASSERT(forbidden_set.back() == ty);
-    SASSERT(r);
-    m_asts.push_back(ty);
-    m_asts.push_back(r);
-    m_datatype2nonrec_constructor.insert(ty, r);
-    return r;
-}
-
-/**
-   \brief Return a constructor mk(T_1, ..., T_n) where
-   each T_i is not a datatype or it is a datatype t not in forbidden_set,
-   and get_non_rec_constructor_core(T_i, forbidden_set union { T_i })
-*/
-func_decl * datatype_util::get_non_rec_constructor_core(sort * ty, ptr_vector<sort> & forbidden_set) {
-    // We must select a constructor c(T_1, ..., T_n):T such that
-    //   1) T_i's are not recursive
-    // If there is no such constructor, then we select one that 
-    //   2) each type T_i is not recursive or contains a constructor that does not depend on T
-    ptr_vector<func_decl> const & constructors = *get_datatype_constructors(ty);
-    // step 1)
-    unsigned sz = constructors.size();
-    unsigned start = ++m_start;
-    for (unsigned j = 0; j < sz; ++j) {        
-        func_decl * c = constructors[(j + start) % sz];
-        unsigned num_args = c->get_arity();
-        unsigned i = 0;
-        for (; i < num_args && !is_datatype(c->get_domain(i)); i++) {};
-        if (i == num_args)
-            return c;
-    }
-    // step 2)
-    for (unsigned j = 0; j < sz; ++j) {        
-        func_decl * c = (*constructors)[(j + start) % sz];
-        TRACE("datatype_util_bug", tout << "non_rec_constructor c: " << c->get_name() << "\n";);
-        unsigned num_args = c->get_arity();
-        unsigned i = 0;
-        for (; i < num_args; i++) {
-            sort * T_i = c->get_domain(i);
-            TRACE("datatype_util_bug", tout << "c: " << c->get_name() << " i: " << i << " T_i: " << T_i->get_name() << "\n";);
-            if (!is_datatype(T_i)) {
-                TRACE("datatype_util_bug", tout << "T_i is not a datatype\n";);
-                continue;
-            }
-            if (std::find(forbidden_set.begin(), forbidden_set.end(), T_i) != forbidden_set.end()) {
-                TRACE("datatype_util_bug", tout << "T_i is in forbidden_set\n";);
-                break;
-            }
-            forbidden_set.push_back(T_i);
-            func_decl * nested_c = get_non_rec_constructor_core(T_i, forbidden_set);
-            SASSERT(forbidden_set.back() == T_i);
-            forbidden_set.pop_back();
-            TRACE("datatype_util_bug", tout << "nested_c: " << nested_c->get_name() << "\n";);
-            if (nested_c == 0)
-                break;
-        }
-        if (i == num_args)
-            return c;
-    }
-    return 0;
-}
-
-func_decl * datatype_util::get_constructor_recognizer(func_decl * constructor) {
-    SASSERT(is_constructor(constructor));
-    func_decl * d = 0;
-    if (m_constructor2recognizer.find(constructor, d))
-        return d;
-    sort * datatype = constructor->get_range();
-    d               = m_manager.mk_func_decl(m_family_id, OP_DT_RECOGNISER, 2, constructor->get_parameters(), 1, &datatype);
-    SASSERT(d);
-    m_asts.push_back(constructor);
-    m_asts.push_back(d);
-    m_constructor2recognizer.insert(constructor, d);
-    return d;
-}
-
-ptr_vector<func_decl> const * datatype_util::get_constructor_accessors(func_decl * constructor) {
-    SASSERT(is_constructor(constructor));
-    ptr_vector<func_decl> * res = 0;
-    if (m_constructor2accessors.find(constructor, res))
-        return res;
-    res = alloc(ptr_vector<func_decl>);
-    m_asts.push_back(constructor);
-    m_vectors.push_back(res);
-    m_constructor2accessors.insert(constructor, res);
-    unsigned c_id              = constructor->get_parameter(1).get_int();
-    sort * datatype            = constructor->get_range();
-    unsigned tid               = datatype->get_parameter(1).get_int();
-    unsigned o                 = datatype_decl_plugin::constructor_offset(datatype, tid);
-    unsigned k_i               = datatype->get_parameter(o + c_id + 1).get_int();
-    unsigned num_accessors     = datatype->get_parameter(k_i+2).get_int();
-    parameter p[3]             = { parameter(datatype), parameter(c_id), parameter(-1) };
-    for (unsigned r = 0; r < num_accessors; r++) {
-        p[2]           = parameter(r);
-        func_decl * d  = m_manager.mk_func_decl(m_family_id, OP_DT_ACCESSOR, 3, p, 1, &datatype);
-        SASSERT(d);
-        m_asts.push_back(d);
-        res->push_back(d);
-    }
-    return res;
-}
-
-func_decl * datatype_util::get_accessor_constructor(func_decl * accessor) { 
-    SASSERT(is_accessor(accessor));
-    func_decl * r = 0;
-    if (m_accessor2constructor.find(accessor, r))
-        return r;
-    sort * datatype = to_sort(accessor->get_parameter(0).get_ast());
-    unsigned c_id   = accessor->get_parameter(1).get_int();
-    r = get_constructor(datatype, c_id);
-    m_accessor2constructor.insert(accessor, r);
-    m_asts.push_back(accessor);
-    m_asts.push_back(r);
-    return r;
-}
-
-func_decl * datatype_util::get_recognizer_constructor(func_decl * recognizer) {
-    SASSERT(is_recognizer(recognizer));
-    func_decl * r = 0;
-    if (m_recognizer2constructor.find(recognizer, r))
-        return r;
-    sort * datatype = to_sort(recognizer->get_parameter(0).get_ast());
-    unsigned c_id            = recognizer->get_parameter(1).get_int();
-    r = get_constructor(datatype, c_id);
-    m_recognizer2constructor.insert(recognizer, r);
-    m_asts.push_back(recognizer);
-    m_asts.push_back(r);
-    return r;
-}
-
-bool datatype_util::is_recursive(sort * ty) {
-    SASSERT(is_datatype(ty));
-    bool r = false;
-    if (m_is_recursive.find(ty, r))
-        return r;
-    r = is_recursive_datatype(ty->get_parameters());
-    m_is_recursive.insert(ty, r);
-    m_asts.push_back(ty);
-    return r;
-}
-
-
-bool datatype_util::is_enum_sort(sort* s) {
-    if (!is_datatype(s)) {
-        return false;
-    }
-    bool r = false;
-    if (m_is_enum.find(s, r))
-        return r;
-    ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
-    r = true;
-    for (unsigned i = 0; r && i < cnstrs.size(); ++i) {
-        r = cnstrs[i]->get_arity() == 0;
-    }
-    m_is_enum.insert(s, r);
-    m_asts.push_back(s);
-    return r;
-}
-
-
-void datatype_util::reset() {
-    m_datatype2constructors.reset();
-    m_datatype2nonrec_constructor.reset();
-    m_constructor2accessors.reset();
-    m_constructor2recognizer.reset();
-    m_recognizer2constructor.reset();
-    m_accessor2constructor.reset();
-    m_is_recursive.reset();
-    m_is_enum.reset();
-    std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc<ptr_vector<func_decl> >());
-    m_vectors.reset();
-    m_asts.reset();
-    ++m_start;
-}
-
-/**
-   \brief Two datatype sorts s1 and s2 are siblings if they were
-   defined together in the same mutually recursive definition.
-*/
-bool datatype_util::are_siblings(sort * s1, sort * s2) {
-    SASSERT(is_datatype(s1));
-    SASSERT(is_datatype(s2));
-    if (s1 == s2)
-        return true;
-    if (s1->get_num_parameters() != s2->get_num_parameters())
-        return false;
-    unsigned num_params = s1->get_num_parameters();
-    if (s1->get_parameter(0) != s2->get_parameter(0))
-        return false;
-    // position 1 contains the IDX of the datatype in a mutually recursive definition.
-    for (unsigned i = 2; i < num_params; i++) {
-        if (s1->get_parameter(i) != s2->get_parameter(i))
-            return false;
-    }
-    return true;
-}
-
-void datatype_util::display_datatype(sort *s0, std::ostream& strm) {
-    ast_mark mark;
-    ptr_buffer<sort> todo;
-    SASSERT(is_datatype(s0));
-    strm << s0->get_name() << " where\n";
-    todo.push_back(s0);
-    mark.mark(s0, true);
-    while (!todo.empty()) {
-        sort* s = todo.back();
-        todo.pop_back();
-        strm << s->get_name() << " =\n";
-
-        ptr_vector<func_decl> const & cnstrs = *get_datatype_constructors(s);
-        for (unsigned i = 0; i < cnstrs.size(); ++i) {
-            func_decl* cns = cnstrs[i];
-            func_decl* rec = get_constructor_recognizer(cns);
-            strm << "  " << cns->get_name() << " :: " << rec->get_name() << " :: ";
-            ptr_vector<func_decl> const & accs = *get_constructor_accessors(cns);
-            for (unsigned j = 0; j < accs.size(); ++j) {
-                func_decl* acc = accs[j];
-                sort* s1 = acc->get_range();
-                strm << "(" << acc->get_name() << ": " << s1->get_name() << ") "; 
-                if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) {
+            ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
+            for (unsigned i = 0; i < cnstrs.size(); ++i) {
+                func_decl* cns = cnstrs[i];
+                func_decl* rec = get_constructor_recognizer(cns);
+                out << "  " << cns->get_name() << " :: " << rec->get_name() << " :: ";
+                ptr_vector<func_decl> const & accs = *get_constructor_accessors(cns);
+                for (unsigned j = 0; j < accs.size(); ++j) {
+                    func_decl* acc = accs[j];
+                    sort* s1 = acc->get_range();
+                    out << "(" << acc->get_name() << ": " << s1->get_name() << ") "; 
+                    if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) {
                         mark.mark(s1, true);
                         todo.push_back(s1);
-                }          
+                    }          
+                }
+                out << "\n";
             }
-            strm << "\n";
         }
     }
-
 }
 
-bool datatype_util::is_func_decl(datatype_op_kind k, unsigned num_params, parameter const* params, func_decl* f) {
-    bool eq = 
-        f->get_decl_kind() == k &&
-        f->get_family_id() == m_family_id &&
-        f->get_num_parameters() == num_params;
-    for (unsigned i = 0; eq && i < num_params; ++i) {
-        eq = params[i] == f->get_parameter(i);
+datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs) {
+    datatype::decl::plugin* p = u.get_plugin();
+    datatype::def* d = p->mk(n, num_params, params);
+    for (unsigned i = 0; i < num_constructors; ++i) {
+        d->add(cs[i]);
     }
-    return eq;
+    return d;
 }
-
-bool datatype_util::is_constructor_of(unsigned num_params, parameter const* params, func_decl* f) {
-    return 
-        num_params == 2 &&
-        m_family_id == f->get_family_id() &&
-        OP_DT_CONSTRUCTOR == f->get_decl_kind() &&
-        2 == f->get_num_parameters() &&
-        params[0] == f->get_parameter(0) &&
-        params[1] == f->get_parameter(1);
-}
-
-
-#endif
diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h
index 840329dda..515ca6e20 100644
--- a/src/ast/datatype_decl_plugin.h
+++ b/src/ast/datatype_decl_plugin.h
@@ -1,5 +1,5 @@
 /*++
-Copyright (c) 2006 Microsoft Corporation
+Copyright (c) 2017 Microsoft Corporation
 
 Module Name:
 
@@ -11,55 +11,390 @@ Abstract:
 
 Author:
 
-    Leonardo de Moura (leonardo) 2008-01-09.
+    Nikolaj Bjorner (nbjorner) 2017-9-1 
 
 Revision History:
 
---*/
-#define DATATYPE_V2
-#ifdef DATATYPE_V2
-#include "ast/datatype_decl_plugin2.h"
-#else
+    rewritten to support SMTLIB-2.6 parameters from
+     Leonardo de Moura (leonardo) 2008-01-09.
 
+--*/
 #ifndef DATATYPE_DECL_PLUGIN_H_
 #define DATATYPE_DECL_PLUGIN_H_
 
-
 #include "ast/ast.h"
-#include "util/tptr.h"
 #include "util/buffer.h"
+#include "util/symbol_table.h"
 #include "util/obj_hashtable.h"
 
-enum datatype_sort_kind {
+
+enum sort_kind {
     DATATYPE_SORT
 };
 
-enum datatype_op_kind {
+enum op_kind {
     OP_DT_CONSTRUCTOR,
     OP_DT_RECOGNISER,
-    OP_DT_ACCESSOR,
+    OP_DT_IS,
+    OP_DT_ACCESSOR,        
     OP_DT_UPDATE_FIELD,
     LAST_DT_OP
 };
 
-/**
-   \brief Auxiliary class used to declare inductive datatypes.
-   It may be a sort or an integer. If it is an integer,
-   then it represents a reference to a recursive type.
+namespace datatype {
 
-   For example, consider the datatypes
-   Datatype
-     Tree     = tree(value:Real, children:TreeList)
-     TreeList = cons_t(first_t:Tree, rest_t:Tree)
-              | nil_t
-   End
-  
-   The recursive occurrences of Tree and TreeList will have idx 0 and
-   1 respectively.
+    class util;
+    class def;
+    class accessor;
+    class constructor;
+ 
+
+    class accessor {
+        symbol    m_name;
+        sort_ref  m_range;
+        unsigned m_index;    // reference to recursive data-type may only get resolved after all mutually recursive data-types are procssed.
+        constructor* m_constructor;
+    public:
+        accessor(ast_manager& m, symbol const& n, sort* range):
+            m_name(n),
+            m_range(range, m),
+            m_index(UINT_MAX)
+        {}
+        accessor(ast_manager& m, symbol const& n, unsigned index):
+            m_name(n),
+            m_range(m),
+            m_index(index)
+        {}
+        sort* range() const { return m_range; }
+        void fix_range(sort_ref_vector const& dts);
+        symbol const& name() const { return m_name; }
+        func_decl_ref instantiate(sort_ref_vector const& ps) const;
+        func_decl_ref instantiate(sort* dt) const;
+        void attach(constructor* d) { m_constructor = d; }
+        constructor const& get_constructor() const { return *m_constructor; }
+        def const& get_def() const;
+        util& u() const;
+        accessor* translate(ast_translation& tr);
+    };
+
+    class constructor {
+        symbol           m_name;
+        symbol           m_recognizer;
+        ptr_vector<accessor> m_accessors;
+        def*             m_def;
+    public:
+        constructor(symbol n, symbol const& r): m_name(n), m_recognizer(r) {}
+        ~constructor();
+        void add(accessor* a) { m_accessors.push_back(a); a->attach(this); }
+        symbol const& name() const { return m_name; }
+        symbol const& recognizer() const { return m_recognizer; }
+        ptr_vector<accessor> const& accessors() const { return m_accessors; }
+        ptr_vector<accessor>::const_iterator begin() const { return m_accessors.begin(); }
+        ptr_vector<accessor>::const_iterator end() const { return m_accessors.end(); }
+        ptr_vector<accessor>::iterator begin() { return m_accessors.begin(); }
+        ptr_vector<accessor>::iterator end() { return m_accessors.end(); }
+        func_decl_ref instantiate(sort_ref_vector const& ps) const;
+        func_decl_ref instantiate(sort* dt) const;
+        void attach(def* d) { m_def = d; }
+        def const& get_def() const { return *m_def; }
+        util& u() const;
+        constructor* translate(ast_translation& tr);
+    };
+
+    namespace param_size {
+        class size {
+            unsigned m_ref;
+        public:
+            size(): m_ref(0) {}
+            virtual ~size() {}
+            void inc_ref() { ++m_ref; }
+            void dec_ref() { --m_ref; if (m_ref == 0) dealloc(this); }
+            static size* mk_offset(sort_size const& s); 
+            static size* mk_param(sort_ref& p); 
+            static size* mk_plus(size* a1, size* a2); 
+            static size* mk_times(size* a1, size* a2); 
+            static size* mk_plus(ptr_vector<size>& szs);
+            static size* mk_times(ptr_vector<size>& szs);
+            static size* mk_power(size* a1, size* a2);
+            
+            virtual size* subst(obj_map<sort, size*>& S) = 0;
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) = 0;
+            
+        };
+        struct offset : public size {
+            sort_size m_offset;
+            offset(sort_size const& s): m_offset(s) {}
+            virtual ~offset() {}
+            virtual size* subst(obj_map<sort,size*>& S) { return this; }
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { return m_offset; }
+        };
+        struct plus : public size {
+            size* m_arg1, *m_arg2;
+            plus(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref();}
+            virtual ~plus() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
+            virtual size* subst(obj_map<sort,size*>& S) { return mk_plus(m_arg1->subst(S), m_arg2->subst(S)); }
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { 
+                sort_size s1 = m_arg1->eval(S);
+                sort_size s2 = m_arg2->eval(S);
+                if (s1.is_infinite()) return s1;
+                if (s2.is_infinite()) return s2;
+                if (s1.is_very_big()) return s1;
+                if (s2.is_very_big()) return s2;
+                rational r = rational(s1.size(), rational::ui64()) + rational(s2.size(), rational::ui64());
+                return sort_size(r);
+            }
+        };
+        struct times : public size {
+            size* m_arg1, *m_arg2;
+            times(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); }
+            virtual ~times() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
+            virtual size* subst(obj_map<sort,size*>& S) { return mk_times(m_arg1->subst(S), m_arg2->subst(S)); }
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { 
+                sort_size s1 = m_arg1->eval(S);
+                sort_size s2 = m_arg2->eval(S);
+                if (s1.is_infinite()) return s1;
+                if (s2.is_infinite()) return s2;
+                if (s1.is_very_big()) return s1;
+                if (s2.is_very_big()) return s2;
+                rational r = rational(s1.size(), rational::ui64()) * rational(s2.size(), rational::ui64());
+                return sort_size(r);
+            }
+        };
+        struct power : public size {
+            size* m_arg1, *m_arg2;
+            power(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); }
+            virtual ~power() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
+            virtual size* subst(obj_map<sort,size*>& S) { return mk_power(m_arg1->subst(S), m_arg2->subst(S)); }
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { 
+                sort_size s1 = m_arg1->eval(S);
+                sort_size s2 = m_arg2->eval(S);
+                // s1^s2
+                if (s1.is_infinite()) return s1;
+                if (s2.is_infinite()) return s2;
+                if (s1.is_very_big()) return s1;
+                if (s2.is_very_big()) return s2;
+                if (s1.size() == 1) return s1;
+                if (s2.size() == 1) return s1;
+                if (s1.size() > (2 << 20) || s2.size() > 10) return sort_size::mk_very_big();
+                rational r = ::power(rational(s1.size(), rational::ui64()), static_cast<unsigned>(s2.size()));
+                return sort_size(r);
+            }
+        };
+        struct sparam : public size {
+            sort_ref m_param;
+            sparam(sort_ref& p): m_param(p) {}
+            virtual ~sparam() {}
+            virtual size* subst(obj_map<sort,size*>& S) { return S[m_param]; }
+            virtual sort_size eval(obj_map<sort, sort_size> const& S) { return S[m_param]; }
+        };
+    };
+
+    class def {
+        ast_manager&        m;
+        util&               m_util;
+        symbol              m_name;
+        unsigned            m_class_id;
+        param_size::size*   m_sort_size;
+        sort_ref_vector     m_params;
+        mutable sort_ref    m_sort;
+        ptr_vector<constructor> m_constructors;
+    public:
+        def(ast_manager& m, util& u, symbol const& n, unsigned class_id, unsigned num_params, sort * const* params):
+            m(m),
+            m_util(u),
+            m_name(n),
+            m_class_id(class_id),            
+            m_sort_size(0),
+            m_params(m, num_params, params), 
+            m_sort(m)
+        {}
+        ~def() {
+            if (m_sort_size) m_sort_size->dec_ref();
+            for (constructor* c : m_constructors) dealloc(c);
+            m_constructors.reset();
+        }
+        void add(constructor* c) {
+            m_constructors.push_back(c);
+            c->attach(this);
+        }
+        symbol const& name() const { return m_name; }
+        unsigned id() const { return m_class_id; }
+        sort_ref instantiate(sort_ref_vector const& ps) const;
+        ptr_vector<constructor> const& constructors() const { return m_constructors; }
+        ptr_vector<constructor>::const_iterator begin() const { return m_constructors.begin(); }
+        ptr_vector<constructor>::const_iterator end() const { return m_constructors.end(); }
+        ptr_vector<constructor>::iterator begin() { return m_constructors.begin(); }
+        ptr_vector<constructor>::iterator end() { return m_constructors.end(); }
+        sort_ref_vector const& params() const { return m_params; }
+        util& u() const { return m_util; }
+        param_size::size* sort_size() { return m_sort_size; }
+        void set_sort_size(param_size::size* p) { m_sort_size = p; p->inc_ref(); m_sort = 0; }
+        def* translate(ast_translation& tr, util& u);
+    };
+
+    namespace decl {
+
+        class plugin : public decl_plugin {
+            mutable scoped_ptr<util> m_util;
+            map<symbol, def*, symbol_hash_proc, symbol_eq_proc> m_defs; 
+            svector<symbol>          m_def_block;
+            unsigned                 m_class_id;
+            util & u() const;
+
+            virtual void inherit(decl_plugin* other_p, ast_translation& tr);
+
+        public:
+            plugin(): m_class_id(0) {}
+            virtual ~plugin();
+
+            virtual void finalize();
+        
+            virtual decl_plugin * mk_fresh() { return alloc(plugin); }
+        
+            virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters);
+        
+            virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, 
+                                             unsigned arity, sort * const * domain, sort * range);
+                
+            virtual expr * get_some_value(sort * s);
+        
+            virtual bool is_fully_interp(sort * s) const;
+        
+            virtual bool is_value(app* e) const;
+        
+            virtual bool is_unique_value(app * e) const { return is_value(e); }
+        
+            virtual void get_op_names(svector<builtin_name> & op_names, symbol const & logic);
+                
+            void begin_def_block() { m_class_id++; m_def_block.reset(); }
+
+            void end_def_block();
+
+            def* mk(symbol const& name, unsigned n, sort * const * params);
+
+            void remove(symbol const& d);
+
+            bool mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts);
+
+            def const& get_def(sort* s) const { return *(m_defs[datatype_name(s)]); }
+            def& get_def(symbol const& s) { return *(m_defs[s]); }
+            bool is_declared(sort* s) const { return m_defs.contains(datatype_name(s)); }
+        private:
+            bool is_value_visit(expr * arg, ptr_buffer<app> & todo) const;
+        
+            func_decl * mk_update_field(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
+            func_decl * mk_constructor(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
+            func_decl * mk_accessor(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
+            func_decl * mk_recognizer(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
+            func_decl * mk_is(
+                unsigned num_parameters, parameter const * parameters, 
+                unsigned arity, sort * const * domain, sort * range);
+
+            symbol datatype_name(sort * s) const {
+                //SASSERT(u().is_datatype(s));
+                return s->get_parameter(0).get_symbol();
+            }
+            
+        };
+    }
+
+    class util {
+        ast_manager & m;
+        family_id     m_family_id;
+        mutable decl::plugin* m_plugin;
+
+                
+        obj_map<sort, ptr_vector<func_decl> *>      m_datatype2constructors;
+        obj_map<sort, func_decl *>                  m_datatype2nonrec_constructor;
+        obj_map<func_decl, ptr_vector<func_decl> *> m_constructor2accessors;
+        obj_map<func_decl, func_decl *>             m_constructor2recognizer;
+        obj_map<func_decl, func_decl *>             m_recognizer2constructor;
+        obj_map<func_decl, func_decl *>             m_accessor2constructor;
+        obj_map<sort, bool>                         m_is_recursive;
+        obj_map<sort, bool>                         m_is_enum;
+        mutable obj_map<sort, bool>                 m_is_fully_interp;
+        mutable ast_ref_vector                      m_asts;
+        ptr_vector<ptr_vector<func_decl> >          m_vectors;
+        unsigned                                    m_start;
+        mutable ptr_vector<sort>                    m_fully_interp_trail;
+        
+        func_decl * get_non_rec_constructor_core(sort * ty, ptr_vector<sort> & forbidden_set);
+
+        friend class decl::plugin;
+
+        bool is_recursive_core(sort * s) const;
+        sort_size get_datatype_size(sort* s0);
+        void compute_datatype_size_functions(svector<symbol> const& names);
+        param_size::size* get_sort_size(sort_ref_vector const& params, sort* s);
+        bool is_well_founded(unsigned num_types, sort* const* sorts);
+        def& get_def(symbol const& s) { return m_plugin->get_def(s); }
+        void get_subsorts(sort* s, ptr_vector<sort>& sorts) const;        
+
+    public:
+        util(ast_manager & m);
+        ~util();
+        ast_manager & get_manager() const { return m; }
+        // sort * mk_datatype_sort(symbol const& name, unsigned n, sort* const* params); 
+        bool is_datatype(sort const* s) const { return is_sort_of(s, m_family_id, DATATYPE_SORT); }
+        bool is_enum_sort(sort* s);
+        bool is_recursive(sort * ty);
+        bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
+        bool is_recognizer(func_decl * f) const { return is_recognizer0(f) || is_is(f); }
+        bool is_recognizer0(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); }
+        bool is_is(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_IS); }
+        bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); }
+        bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
+        bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
+        bool is_recognizer0(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER);} 
+        bool is_is(app * f) const { return is_app_of(f, m_family_id, OP_DT_IS);} 
+        bool is_recognizer(app * f) const { return is_recognizer0(f) || is_is(f); }
+        bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
+        bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
+        ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
+        unsigned get_datatype_num_constructors(sort * ty);
+        unsigned get_datatype_num_parameter_sorts(sort * ty);
+        sort*  get_datatype_parameter_sort(sort * ty, unsigned idx);
+        func_decl * get_non_rec_constructor(sort * ty);
+        func_decl * get_constructor_recognizer(func_decl * constructor);
+        ptr_vector<func_decl> const * get_constructor_accessors(func_decl * constructor);
+        func_decl * get_accessor_constructor(func_decl * accessor);
+        func_decl * get_recognizer_constructor(func_decl * recognizer) const;
+        family_id get_family_id() const { return m_family_id; }
+        bool are_siblings(sort * s1, sort * s2);
+        bool is_func_decl(op_kind k, unsigned num_params, parameter const* params, func_decl* f);
+        bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f);
+        void reset();
+        bool is_declared(sort* s) const;
+        void display_datatype(sort *s, std::ostream& strm);
+        bool is_fully_interp(sort * s) const;
+        sort_ref_vector datatype_params(sort * s) const;
+        unsigned get_constructor_idx(func_decl * f) const;
+        unsigned get_recognizer_constructor_idx(func_decl * f) const;
+        decl::plugin* get_plugin() { return m_plugin; }
+        void get_defs(sort* s, ptr_vector<def>& defs);
+        def const& get_def(sort* s) const;
+    };
+
+};
+
+typedef datatype::accessor accessor_decl;
+typedef datatype::constructor constructor_decl;
+typedef datatype::def datatype_decl;
+typedef datatype::decl::plugin datatype_decl_plugin;
+typedef datatype::util datatype_util;
 
-   This is a transient value, it is only used to declare a set of
-   recursive datatypes.
-*/
 class type_ref {
     void * m_data;
 public:
@@ -73,178 +408,29 @@ public:
     int get_idx() const { return UNBOXINT(m_data); }
 };
 
-class accessor_decl;
-class constructor_decl;
-class datatype_decl;
-class datatype_util;
+inline accessor_decl * mk_accessor_decl(ast_manager& m, symbol const & n, type_ref const & t) {
+    if (t.is_idx()) {
+        return alloc(accessor_decl, m, n, t.get_idx());
+    }
+    else {
+        return alloc(accessor_decl, m, n, t.get_sort());
+    }
+}
+
+inline constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * * acs) {
+    constructor_decl* c = alloc(constructor_decl, n, r);
+    for (unsigned i = 0; i < num_accessors; ++i) {
+        c->add(acs[i]);
+    }
+    return c;
+}
+
+
 
-accessor_decl * mk_accessor_decl(ast_manager& m, symbol const & n, type_ref const & t);
-// Remark: the constructor becomes the owner of the accessor_decls
-constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * const * acs);
 // Remark: the datatype becomes the owner of the constructor_decls
-datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort * const* params, unsigned num_constructors, constructor_decl * const * cs);
-void del_datatype_decl(datatype_decl * d);
-void del_datatype_decls(unsigned num, datatype_decl * const * ds);
+datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs);
+inline void del_datatype_decl(datatype_decl * d) {}
+inline void del_datatype_decls(unsigned num, datatype_decl * const * ds) {}
 
-class datatype_decl_plugin : public decl_plugin {
-    mutable scoped_ptr<datatype_util> m_util;
-    datatype_util & get_util() const;
-public:
-    datatype_decl_plugin() {}
-
-    virtual ~datatype_decl_plugin();
-    virtual void finalize();
-
-    virtual decl_plugin * mk_fresh() { return alloc(datatype_decl_plugin); }
-
-    
-    /**
-       Contract for sort: 
-         parameters[0]            - (int) n - number of recursive types.
-         parameters[1]            - (int) i - index 0..n-1 of which type is defined.
-
-         parameters[2]            - (int) p - number of type parameters.
-         
-         for j = 0..p-1
-         parameters[3 + j]        - (sort) s - type parameter
-               
-         c_offset := 3 + p
-         for j in 0..n-1
-         parameters[c_offset + 2*j]      - (symbol) name of the type
-         parameters[c_offset + 2*j + 1]  - (int) o - offset where the constructors are defined.
-      
-         for each offset o at parameters[2 + 2*j + 1] for some j in 0..n-1
-         parameters[o]            - (int) m - number of constructors
-         parameters[o+1]          - (int) k_1 - offset for constructor definition
-         ...
-         parameters[o+m]          - (int) k_m - offset for constructor definition
-      
-         for each offset k_i at parameters[o+s] for some s in 0..m-1
-         parameters[k_i]          - (symbol) name of the constructor
-         parameters[k_i+1]        - (symbol) name of the recognizer
-         parameters[k_i+2]        - (int) m' - number of accessors
-         parameters[k_i+3+2*r]    - (symbol) name of the r accessor
-         parameters[k_i+3+2*r+1]  - (int or type_ast) type of the accessor. If integer, then the value must be in [0..n-1], and it 
-                                    represents an reference to the recursive type.
-       
-       The idea with the additional offsets is that
-       access to relevant constructors and types can be performed using
-       a few address calculations.
-    */
-    virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters);
-    
-    /**
-       Contract for constructors
-         parameters[0] - (ast) datatype ast.
-         parmaeters[1] - (int) constructor idx.
-       Contract for accessors
-         parameters[0] - (ast) datatype ast.
-         parameters[1] - (int) constructor idx.
-         parameters[2] - (int) accessor idx.
-       Contract for tester
-         parameters[0] - (ast) datatype ast.
-         parameters[1] - (int) constructor idx.
-    */
-    virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, 
-                                     unsigned arity, sort * const * domain, sort * range);
-    
-    bool mk_datatypes(unsigned num_datatypes, datatype_decl * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts);
-
-    virtual expr * get_some_value(sort * s);
-
-    virtual bool is_fully_interp(sort * s) const;
-
-    virtual bool is_value(app* e) const;
-
-    virtual bool is_unique_value(app * e) const { return is_value(e); }
-
-    virtual void get_op_names(svector<builtin_name> & op_names, symbol const & logic);
-
-    static unsigned constructor_offset(sort const* s);
-    static unsigned constructor_offset(parameter const& p);
-    static unsigned constructor_offset(parameter const* ps);
-
-    static unsigned constructor_offset(sort const* s, unsigned tid);
-    static unsigned constructor_offset(parameter const* ps, unsigned tid);
-
-private:
-    bool is_value_visit(expr * arg, ptr_buffer<app> & todo) const;
-
-    func_decl * mk_update_field(
-        unsigned num_parameters, parameter const * parameters, 
-        unsigned arity, sort * const * domain, sort * range);
-};
-
-class datatype_util {
-    ast_manager & m_manager;
-    family_id     m_family_id;
-
-    func_decl * get_constructor(sort * ty, unsigned c_id) const;
-
-    obj_map<sort, ptr_vector<func_decl> *>      m_datatype2constructors;
-    obj_map<sort, func_decl *>                  m_datatype2nonrec_constructor;
-    obj_map<func_decl, ptr_vector<func_decl> *> m_constructor2accessors;
-    obj_map<func_decl, func_decl *>             m_constructor2recognizer;
-    obj_map<func_decl, func_decl *>             m_recognizer2constructor;
-    obj_map<func_decl, func_decl *>             m_accessor2constructor;
-    obj_map<sort, bool>                         m_is_recursive;
-    obj_map<sort, bool>                         m_is_enum;
-    ast_ref_vector                              m_asts;
-    ptr_vector<ptr_vector<func_decl> >          m_vectors;
-    unsigned                                    m_start;
-
-    func_decl * get_non_rec_constructor_core(sort * ty, ptr_vector<sort> & forbidden_set);
-    func_decl * get_constructor(sort * ty, unsigned c_id);
-
-public:
-    datatype_util(ast_manager & m);
-    ~datatype_util();
-    ast_manager & get_manager() const { return m_manager; }
-    bool is_datatype(sort * s) const { return is_sort_of(s, m_family_id, DATATYPE_SORT); }
-    bool is_enum_sort(sort* s);
-
-    bool is_recursive(sort * ty);
-    bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
-    bool is_recognizer(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); }
-    bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); }
-    bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
-    bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
-    bool is_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); }
-    bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
-    bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
-    ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
-    unsigned get_datatype_num_constructors(sort * ty) { 
-        SASSERT(is_datatype(ty));
-        unsigned tid = ty->get_parameter(1).get_int();
-        unsigned o = datatype_decl_plugin::constructor_offset(ty, tid);
-        return ty->get_parameter(o).get_int(); 
-    }
-    unsigned get_datatype_num_parameter_sorts(sort * ty) {
-        SASSERT(is_datatype(ty));
-        return ty->get_parameter(2).get_int();
-    }
-    sort*  get_datatype_parameter_sort(sort * ty, unsigned idx) {
-        SASSERT(is_datatype(ty));
-        SASSERT(idx < get_datatype_num_parameter_sorts(ty));
-        return to_sort(ty->get_parameter(3 + idx).get_ast());
-    }
-    unsigned get_constructor_idx(func_decl * f) const { SASSERT(is_constructor(f)); return f->get_parameter(1).get_int(); }
-    unsigned get_recognizer_constructor_idx(func_decl * f) const { SASSERT(is_recognizer(f)); return f->get_parameter(1).get_int(); }
-    func_decl * get_non_rec_constructor(sort * ty);
-    func_decl * get_constructor_recognizer(func_decl * constructor);
-    ptr_vector<func_decl> const * get_constructor_accessors(func_decl * constructor);
-    func_decl * get_accessor_constructor(func_decl * accessor);
-    func_decl * get_recognizer_constructor(func_decl * recognizer);
-    family_id get_family_id() const { return m_family_id; }
-    bool are_siblings(sort * s1, sort * s2);
-    bool is_func_decl(datatype_op_kind k, unsigned num_params, parameter const* params, func_decl* f);
-    bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f);
-    void reset();
-    void display_datatype(sort *s, std::ostream& strm);
-
-
-};
 
 #endif /* DATATYPE_DECL_PLUGIN_H_ */
-
-#endif /* DATATYPE_V2 */
diff --git a/src/ast/datatype_decl_plugin2.cpp b/src/ast/datatype_decl_plugin2.cpp
deleted file mode 100644
index 743a08e98..000000000
--- a/src/ast/datatype_decl_plugin2.cpp
+++ /dev/null
@@ -1,1077 +0,0 @@
-/*++
-Copyright (c) 2017 Microsoft Corporation
-
-Module Name:
-
-    datatype_decl_plugin.cpp
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Nikolaj Bjorner (nbjorner) 2017-9-1 
-
-Revision History:
-
---*/
-
-#include "ast/datatype_decl_plugin.h"
-
-#ifdef DATATYPE_V2
-#include "util/warning.h"
-#include "ast/datatype_decl_plugin2.h"
-#include "ast/array_decl_plugin.h"
-#include "ast/ast_smt2_pp.h"
-#include "ast/ast_translation.h"
-
-
-namespace datatype {
-
-    void accessor::fix_range(sort_ref_vector const& dts) {
-        if (!m_range) {
-            m_range = dts[m_index];
-        }
-    }
-
-    func_decl_ref accessor::instantiate(sort_ref_vector const& ps) const {
-        ast_manager& m = ps.get_manager();
-        unsigned n = ps.size();
-        SASSERT(m_range);
-        SASSERT(n == get_def().params().size());
-        sort_ref range(m.substitute(m_range, n, get_def().params().c_ptr(), ps.c_ptr()), m);
-        sort_ref src(get_def().instantiate(ps));
-        sort* srcs[1] = { src.get() };
-        parameter pas[2] = { parameter(name()), parameter(get_constructor().name()) };
-        return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_ACCESSOR, 2, pas, 1, srcs, range), m);
-    }
-
-    func_decl_ref accessor::instantiate(sort* dt) const {
-        sort_ref_vector sorts = get_def().u().datatype_params(dt);
-        return instantiate(sorts);
-    }
-
-    def const& accessor::get_def() const { return m_constructor->get_def(); }
-    util& accessor::u() const { return m_constructor->u(); }
-    accessor* accessor::translate(ast_translation& tr) {
-        return alloc(accessor, tr.to(), name(), to_sort(tr(m_range.get())));
-    }
-
-    constructor::~constructor() {
-        for (accessor* a : m_accessors) dealloc(a);
-        m_accessors.reset();
-    }
-    util& constructor::u() const { return m_def->u(); }
-
-    func_decl_ref constructor::instantiate(sort_ref_vector const& ps) const {
-        ast_manager& m = ps.get_manager();
-        sort_ref_vector domain(m);
-        for (accessor const* a : accessors()) {
-            domain.push_back(a->instantiate(ps)->get_range());
-        }
-        sort_ref range = get_def().instantiate(ps);
-        parameter pas[1] = { parameter(name()) };
-        return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_CONSTRUCTOR, 1, pas, domain.size(), domain.c_ptr(), range), m);        
-    }
-
-    func_decl_ref constructor::instantiate(sort* dt) const {
-        sort_ref_vector sorts = get_def().u().datatype_params(dt);
-        return instantiate(sorts);
-    }
-
-    constructor* constructor::translate(ast_translation& tr) {
-        constructor* result = alloc(constructor, m_name, m_recognizer);
-        for (accessor* a : *this) {
-            result->add(a->translate(tr));
-        }
-        return result;
-    }
-
-
-    sort_ref def::instantiate(sort_ref_vector const& sorts) const {
-        sort_ref s(m);
-        TRACE("datatype", tout << "instantiate " << m_name << "\n";);
-        if (!m_sort) {
-            vector<parameter> ps;
-            ps.push_back(parameter(m_name));
-            for (sort * s : m_params) ps.push_back(parameter(s));
-            m_sort = m.mk_sort(u().get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr());
-        }
-        if (sorts.empty()) {
-            return m_sort;
-        }
-        return sort_ref(m.substitute(m_sort, sorts.size(), m_params.c_ptr(), sorts.c_ptr()), m);
-    }
-
-    def* def::translate(ast_translation& tr, util& u) {
-        SASSERT(&u.get_manager() == &tr.to());
-        sort_ref_vector ps(tr.to());
-        for (sort* p : m_params) {
-            ps.push_back(to_sort(tr(p)));
-        }
-        def* result = alloc(def, tr.to(), u, m_name, m_class_id, ps.size(), ps.c_ptr());
-        for (constructor* c : *this) {
-            result->add(c->translate(tr));
-        }
-        if (m_sort) result->m_sort = to_sort(tr(m_sort.get()));
-        return result;               
-    }
-
-    enum status {
-        GRAY,
-        BLACK
-    };
-
-    namespace param_size {
-        size* size::mk_offset(sort_size const& s) { return alloc(offset, s); }
-        size* size::mk_param(sort_ref& p) { return alloc(sparam, p); }
-        size* size::mk_plus(size* a1, size* a2) { return alloc(plus, a1, a2); }
-        size* size::mk_times(size* a1, size* a2) { return alloc(times, a1, a2); }
-        size* size::mk_times(ptr_vector<size>& szs) {
-            if (szs.empty()) return mk_offset(sort_size(1));
-            if (szs.size() == 1) return szs[0];
-            size* r = szs[0];
-            for (unsigned i = 1; i < szs.size(); ++i) {
-                r = mk_times(r, szs[i]);
-            }
-            return r;
-        }
-        size* size::mk_plus(ptr_vector<size>& szs) {
-            if (szs.empty()) return mk_offset(sort_size(0));
-            if (szs.size() == 1) return szs[0];
-            size* r = szs[0];
-            for (unsigned i = 1; i < szs.size(); ++i) {
-                r = mk_plus(r, szs[i]);
-            }
-            return r;
-        }
-        size* size::mk_power(size* a1, size* a2) { return alloc(power, a1, a2); }
-    }
-
-    namespace decl {
-
-        plugin::~plugin() {
-            finalize();
-        }
-
-        void plugin::finalize() {
-            for (auto& kv : m_defs) {
-                dealloc(kv.m_value);
-            }
-            m_defs.reset();
-            m_util = 0; // force deletion
-        }
-
-        util & plugin::u() const {
-            SASSERT(m_manager);
-            SASSERT(m_family_id != null_family_id);
-            if (m_util.get() == 0) {
-                m_util = alloc(util, *m_manager);
-            }
-            return *(m_util.get());
-        }
-
-        void plugin::inherit(decl_plugin* other_p, ast_translation& tr) {
-            plugin* p = dynamic_cast<plugin*>(other_p);
-            svector<symbol> names;
-            ptr_vector<def> new_defs;            
-            SASSERT(p);
-            for (auto& kv : p->m_defs) {
-                def* d = kv.m_value;
-                if (!m_defs.contains(kv.m_key)) {
-                    names.push_back(kv.m_key);
-                    new_defs.push_back(d->translate(tr, u()));
-                }
-            }
-            for (def* d : new_defs) 
-                m_defs.insert(d->name(), d);
-            m_class_id = m_defs.size();
-            u().compute_datatype_size_functions(names);
-        }
-
-
-        struct invalid_datatype {};
-
-        sort * plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
-            try {
-                if (k != DATATYPE_SORT) {
-                    TRACE("datatype", tout << "invalid kind parameter to datatype\n";);
-                    throw invalid_datatype();
-                }
-                if (num_parameters < 1) {
-                    TRACE("datatype", tout << "at least one parameter expected to datatype declaration\n";);
-                    throw invalid_datatype();                    
-                }
-                parameter const & name = parameters[0];
-                if (!name.is_symbol()) {
-                    TRACE("datatype", tout << "expected symol parameter at position " << 0 << " got: " << name << "\n";);
-                    throw invalid_datatype();
-                }
-                for (unsigned i = 1; i < num_parameters; ++i) {
-                    parameter const& s = parameters[i];
-                    if (!s.is_ast() || !is_sort(s.get_ast())) {
-                        TRACE("datatype", tout << "expected sort parameter at position " << i << " got: " << s << "\n";);
-                        throw invalid_datatype();
-                    }
-                }
-                                
-                sort* s = m_manager->mk_sort(name.get_symbol(),
-                                             sort_info(m_family_id, k, num_parameters, parameters, true));
-                def* d = 0;
-                if (m_defs.find(s->get_name(), d) && d->sort_size()) {
-                    obj_map<sort, sort_size> S;
-                    for (unsigned i = 0; i + 1 < num_parameters; ++i) {
-                        sort* r = to_sort(parameters[i + 1].get_ast());
-                        S.insert(d->params()[i], r->get_num_elements()); 
-                    }
-                    sort_size ts = d->sort_size()->eval(S);
-                    TRACE("datatype", tout << name << " has size " << ts << "\n";);
-                    s->set_num_elements(ts);
-                }
-                else {
-                    TRACE("datatype", tout << "not setting size for " << name << "\n";);
-                }
-                return s;
-            }
-            catch (invalid_datatype) {
-                m_manager->raise_exception("invalid datatype");
-                return 0;
-            }
-        }
-
-        func_decl * plugin::mk_update_field(
-            unsigned num_parameters, parameter const * parameters, 
-            unsigned arity, sort * const * domain, sort * range) {
-            decl_kind k = OP_DT_UPDATE_FIELD;
-            ast_manager& m = *m_manager;
-            
-            if (num_parameters != 1 || !parameters[0].is_ast()) {
-                m.raise_exception("invalid parameters for datatype field update");
-                return 0;
-            }
-            if (arity != 2) {
-                m.raise_exception("invalid number of arguments for datatype field update");
-                return 0;
-            }
-            func_decl* acc = 0;
-            if (is_func_decl(parameters[0].get_ast())) {
-                acc = to_func_decl(parameters[0].get_ast());
-            }
-            if (acc && !u().is_accessor(acc)) {
-                acc = 0;
-            }
-            if (!acc) {
-                m.raise_exception("datatype field update requires a datatype accessor as the second argument");
-                return 0;
-            }
-            sort* dom = acc->get_domain(0);
-            sort* rng = acc->get_range();
-            if (dom != domain[0]) {
-                m.raise_exception("first argument to field update should be a data-type");
-                return 0;
-            }
-            if (rng != domain[1]) {
-                std::ostringstream buffer;
-                buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m) 
-                       << " instead of " << mk_ismt2_pp(domain[1], m);
-                m.raise_exception(buffer.str().c_str());
-                return 0;
-            }
-            range = domain[0];
-            func_decl_info info(m_family_id, k, num_parameters, parameters);
-            return m.mk_func_decl(symbol("update-field"), arity, domain, range, info);
-        }
-
-#define VALIDATE_PARAM(_pred_) if (!(_pred_)) m_manager->raise_exception("invalid parameter to datatype function "  #_pred_);
-        
-        func_decl * decl::plugin::mk_constructor(unsigned num_parameters, parameter const * parameters, 
-                                                 unsigned arity, sort * const * domain, sort * range) {
-            ast_manager& m = *m_manager;
-            VALIDATE_PARAM(num_parameters == 1 && parameters[0].is_symbol() && range && u().is_datatype(range));
-            // we blindly trust other conditions are met, including domain types.
-            symbol name = parameters[0].get_symbol();
-            func_decl_info info(m_family_id, OP_DT_CONSTRUCTOR, num_parameters, parameters);
-            info.m_private_parameters = true;
-            return m.mk_func_decl(name, arity, domain, range, info);
-        }
-
-        func_decl * decl::plugin::mk_recognizer(unsigned num_parameters, parameter const * parameters, 
-                                                unsigned arity, sort * const * domain, sort *) {
-            ast_manager& m = *m_manager;
-            VALIDATE_PARAM(arity == 1 && num_parameters == 2 && parameters[1].is_symbol() && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
-            VALIDATE_PARAM(u().is_datatype(domain[0]));
-            // blindly trust that parameter is a constructor
-            sort* range = m_manager->mk_bool_sort();
-            func_decl_info info(m_family_id, OP_DT_RECOGNISER, num_parameters, parameters);
-            info.m_private_parameters = true;
-            return m.mk_func_decl(symbol(parameters[1].get_symbol()), arity, domain, range, info);
-        }
-
-        func_decl * decl::plugin::mk_is(unsigned num_parameters, parameter const * parameters, 
-                                                unsigned arity, sort * const * domain, sort *) {
-            ast_manager& m = *m_manager;
-            VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
-            VALIDATE_PARAM(u().is_datatype(domain[0]));
-            // blindly trust that parameter is a constructor
-            sort* range = m_manager->mk_bool_sort();
-            func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters);
-            info.m_private_parameters = true;
-            return m.mk_func_decl(symbol("is"), arity, domain, range, info);
-        }
-
-        func_decl * decl::plugin::mk_accessor(unsigned num_parameters, parameter const * parameters, 
-                                              unsigned arity, sort * const * domain, sort * range) 
-        {            
-            ast_manager& m = *m_manager;
-            VALIDATE_PARAM(arity == 1 && num_parameters == 2 && parameters[0].is_symbol() && parameters[1].is_symbol());
-            VALIDATE_PARAM(u().is_datatype(domain[0]));
-            SASSERT(range);
-            func_decl_info info(m_family_id, OP_DT_ACCESSOR, num_parameters, parameters);
-            info.m_private_parameters = true;
-            symbol name = parameters[0].get_symbol();
-            return m.mk_func_decl(name, arity, domain, range, info);           
-        }
-
-        func_decl * decl::plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, 
-                                               unsigned arity, sort * const * domain, sort * range) {                        
-            switch (k) {
-            case OP_DT_CONSTRUCTOR:
-                return mk_constructor(num_parameters, parameters, arity, domain, range);
-            case OP_DT_RECOGNISER:
-                return mk_recognizer(num_parameters, parameters, arity, domain, range);                
-            case OP_DT_IS:
-                return mk_is(num_parameters, parameters, arity, domain, range);                
-            case OP_DT_ACCESSOR:
-                return mk_accessor(num_parameters, parameters, arity, domain, range);                
-            case OP_DT_UPDATE_FIELD: 
-                return mk_update_field(num_parameters, parameters, arity, domain, range);
-            default:
-                m_manager->raise_exception("invalid datatype operator kind");
-                return 0;
-            }
-        }
-
-        def* plugin::mk(symbol const& name, unsigned n, sort * const * params) {
-            ast_manager& m = *m_manager;
-            return alloc(def, m, u(), name, m_class_id, n, params);
-        }
-
-
-        void plugin::end_def_block() {
-            ast_manager& m = *m_manager;
-
-            sort_ref_vector sorts(m);
-            for (symbol const& s : m_def_block) {
-                def const& d = *m_defs[s];
-                sort_ref_vector ps(m);
-                sorts.push_back(d.instantiate(ps));
-            }
-            for (symbol const& s : m_def_block) {
-                def& d = *m_defs[s];
-                for (constructor* c : d) {
-                    for (accessor* a : *c) {
-                        a->fix_range(sorts);
-                    }
-                }
-            }
-            if (!u().is_well_founded(sorts.size(), sorts.c_ptr())) {
-                m_manager->raise_exception("datatype is not well-founded");
-            }
-
-            u().compute_datatype_size_functions(m_def_block);
-            for (symbol const& s : m_def_block) {
-                sort_ref_vector ps(m);
-                m_defs[s]->instantiate(ps);
-            }
-        }
-
-        bool plugin::mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts) {
-            begin_def_block();
-            for (unsigned i = 0; i < num_datatypes; ++i) {
-                def* d = 0;
-                TRACE("datatype", tout << "declaring " << datatypes[i]->name() << "\n";);
-                if (m_defs.find(datatypes[i]->name(), d)) {
-                    TRACE("datatype", tout << "delete previous version for " << datatypes[i]->name() << "\n";);
-                    dealloc(d);
-                }
-                m_defs.insert(datatypes[i]->name(), datatypes[i]);
-                m_def_block.push_back(datatypes[i]->name());
-            }
-            end_def_block();            
-            sort_ref_vector ps(*m_manager);
-            for (symbol const& s : m_def_block) {                
-                new_sorts.push_back(m_defs[s]->instantiate(ps));
-            }
-            return true;
-        }
-
-        void plugin::remove(symbol const& s) {
-            def* d = 0;
-            if (m_defs.find(s, d)) dealloc(d);
-            m_defs.remove(s);
-        }
-
-        bool plugin::is_value_visit(expr * arg, ptr_buffer<app> & todo) const {
-            if (!is_app(arg))
-                return false;
-            family_id fid = to_app(arg)->get_family_id();
-            if (fid == m_family_id) {
-                if (!u().is_constructor(to_app(arg)))
-                    return false;
-                if (to_app(arg)->get_num_args() == 0)
-                    return true;
-                todo.push_back(to_app(arg));
-                return true;
-            }
-            else {
-                return m_manager->is_value(arg);
-            }
-        }
-        
-        bool plugin::is_value(app * e) const {
-            TRACE("dt_is_value", tout << "checking\n" << mk_ismt2_pp(e, *m_manager) << "\n";);
-            if (!u().is_constructor(e))
-                return false;
-            if (e->get_num_args() == 0)
-                return true;
-            // REMARK: if the following check is too expensive, we should
-            // cache the values in the decl::plugin.
-            ptr_buffer<app> todo;
-            // potentially expensive check for common sub-expressions.
-            for (expr* arg : *e) {
-                if (!is_value_visit(arg, todo)) {
-                    TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";);
-                    return false;
-                }
-            }
-            while (!todo.empty()) {
-                app * curr = todo.back();
-                SASSERT(u().is_constructor(curr));
-                todo.pop_back();
-                for (expr* arg : *curr) {
-                    if (!is_value_visit(arg, todo)) {
-                        TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";);
-                        return false;
-                    }
-                }
-            }
-            return true;
-        }
-        
-        void plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
-            op_names.push_back(builtin_name("is", OP_DT_IS));
-            if (logic == symbol::null) {
-                op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD));
-            }
-        }
-
-        expr * plugin::get_some_value(sort * s) {
-            SASSERT(u().is_datatype(s));
-            func_decl * c = u().get_non_rec_constructor(s);
-            ptr_buffer<expr> args;
-            for (unsigned i = 0; i < c->get_arity(); i++) {
-                args.push_back(m_manager->get_some_value(c->get_domain(i)));
-            }
-            return m_manager->mk_app(c, args.size(), args.c_ptr());
-        }
-
-        bool plugin::is_fully_interp(sort * s) const {
-            return u().is_fully_interp(s);
-        }
-    }
-
-    sort_ref_vector util::datatype_params(sort * s) const {
-        SASSERT(is_datatype(s));
-        sort_ref_vector result(m);
-        for (unsigned i = 1; i < s->get_num_parameters(); ++i) {
-            result.push_back(to_sort(s->get_parameter(i).get_ast()));
-        }
-        return result;
-    }
-
-
-    bool util::is_fully_interp(sort * s) const {
-        SASSERT(is_datatype(s));
-        bool fi = true;
-        return fi;
-        if (m_is_fully_interp.find(s, fi)) {
-            return fi;
-        }
-        unsigned sz = m_fully_interp_trail.size();
-        m_is_fully_interp.insert(s, true);
-        def const& d = get_def(s);
-        bool is_interp = true;
-        m_fully_interp_trail.push_back(s);
-        for (constructor const* c : d) {
-            for (accessor const* a : *c) {
-                func_decl_ref ac = a->instantiate(s);
-                sort* r = ac->get_range();
-                if (!m.is_fully_interp(r)) {
-                    is_interp = false;
-                    break;
-                }
-            }
-            if (!is_interp) break;
-        }
-        for (unsigned i = sz; i < m_fully_interp_trail.size(); ++i) {
-            m_is_fully_interp.remove(m_fully_interp_trail[i]);
-        }
-        m_fully_interp_trail.shrink(sz);
-        m_is_fully_interp.insert(s, is_interp);
-        m_asts.push_back(s);
-        return true;
-    }
-
-    /**
-       \brief Return true if the inductive datatype is recursive.
-    */
-    bool util::is_recursive_core(sort* s) const {
-        obj_map<sort, status> already_found;
-        ptr_vector<sort> todo, subsorts;
-        todo.push_back(s);
-        status st;
-        while (!todo.empty()) {
-            s = todo.back();
-            if (already_found.find(s, st) && st == BLACK) {
-                todo.pop_back();
-                continue;
-            }
-            already_found.insert(s, GRAY);
-            def const& d = get_def(s);
-            bool can_process       = true;
-            for (constructor const* c : d) {
-                for (accessor const* a : *c) {
-                    sort* d = a->range();
-                    // check if d is a datatype sort
-                    subsorts.reset();
-                    get_subsorts(d, subsorts);
-                    for (sort * s2 : subsorts) {
-                        if (is_datatype(s2)) {
-                            if (already_found.find(s2, st)) {
-                                // type is recursive
-                                if (st == GRAY) return true;
-                            }
-                            else {
-                                todo.push_back(s2);
-                                can_process = false;
-                            }
-                        }
-                    }
-                }
-            }
-            if (can_process) {
-                already_found.insert(s, BLACK);
-                todo.pop_back();
-            }
-        }
-        return false;
-    }
-
-    unsigned util::get_datatype_num_parameter_sorts(sort * ty) {
-        SASSERT(ty->get_num_parameters() >= 1);
-        return ty->get_num_parameters() - 1;
-    }
-
-    sort* util::get_datatype_parameter_sort(sort * ty, unsigned idx) {
-        SASSERT(idx < get_datatype_num_parameter_sorts(ty));
-        return to_sort(ty->get_parameter(idx+1).get_ast());
-    }
-
-    param_size::size* util::get_sort_size(sort_ref_vector const& params, sort* s) {
-        if (params.empty()) {
-            return param_size::size::mk_offset(s->get_num_elements());
-        }
-        if (is_datatype(s)) {
-            param_size::size* sz;
-            obj_map<sort, param_size::size*> S;
-            unsigned n = get_datatype_num_parameter_sorts(s);
-            for (unsigned i = 0; i < n; ++i) {
-                sort* ps = get_datatype_parameter_sort(s, i);
-                sz = get_sort_size(params, ps);
-                sz->inc_ref();
-                S.insert(ps, sz); 
-            }
-            def & d = get_def(s->get_name());
-            sz = d.sort_size()->subst(S);
-            for (auto & kv : S) {
-                kv.m_value->dec_ref();
-            }
-            return sz;
-        }
-        array_util autil(m);
-        if (autil.is_array(s)) {
-            unsigned n = get_array_arity(s);
-            ptr_vector<param_size::size> szs;
-            for (unsigned i = 0; i < n; ++i) {
-                szs.push_back(get_sort_size(params, get_array_domain(s, i)));
-            }
-            param_size::size* sz1 = param_size::size::mk_times(szs);
-            param_size::size* sz2 = get_sort_size(params, get_array_range(s));
-            return param_size::size::mk_power(sz2, sz1);
-        }
-        for (sort* p : params) {           
-            if (s == p) {
-                sort_ref sr(s, m);
-                return param_size::size::mk_param(sr);
-            }
-        }
-        return param_size::size::mk_offset(s->get_num_elements());        
-    }
-
-    bool util::is_declared(sort* s) const {
-        return m_plugin->is_declared(s);
-    }
-    
-    void util::compute_datatype_size_functions(svector<symbol> const& names) {
-        map<symbol, status, symbol_hash_proc, symbol_eq_proc> already_found;
-        map<symbol, param_size::size*, symbol_hash_proc, symbol_eq_proc> szs;
-
-        svector<symbol> todo(names);
-        status st;
-        while (!todo.empty()) {
-            symbol s = todo.back();
-            TRACE("datatype", tout << "Sort size for " << s << "\n";);
-
-            if (already_found.find(s, st) && st == BLACK) {
-                todo.pop_back();
-                continue;
-            }
-            already_found.insert(s, GRAY);
-            bool is_infinite = false;
-            bool can_process = true;
-            def& d = get_def(s);
-            for (constructor const* c : d) {
-                for (accessor const* a : *c) {
-                    sort* r = a->range();
-                    if (is_datatype(r)) {
-                        symbol s2 = r->get_name();
-                        if (already_found.find(s2, st)) {
-                            // type is infinite
-                            if (st == GRAY) {
-                                is_infinite = true;
-                            }
-                        }
-                        else if (names.contains(s2)) {
-                            todo.push_back(s2);
-                            can_process = false;
-                        }
-                    }
-                }
-            }
-            if (!can_process) {
-                continue;
-            }
-            todo.pop_back();
-            already_found.insert(s, BLACK);
-            if (is_infinite) {
-                d.set_sort_size(param_size::size::mk_offset(sort_size::mk_infinite()));
-                continue;
-            }
-
-            ptr_vector<param_size::size> s_add;        
-            for (constructor const* c : d) {
-                ptr_vector<param_size::size> s_mul;
-                for (accessor const* a : *c) {
-                    s_mul.push_back(get_sort_size(d.params(), a->range()));
-                }
-                s_add.push_back(param_size::size::mk_times(s_mul));
-            }
-            d.set_sort_size(param_size::size::mk_plus(s_add));
-        }
-    }
-    
-
-    /**
-       \brief Return true if the inductive datatype is well-founded.
-       Pre-condition: The given argument constains the parameters of an inductive datatype.
-    */
-    bool util::is_well_founded(unsigned num_types, sort* const* sorts) {
-        buffer<bool> well_founded(num_types, false);
-        obj_map<sort, unsigned> sort2id;
-        for (unsigned i = 0; i < num_types; ++i) {
-            sort2id.insert(sorts[i], i);
-        }
-        unsigned num_well_founded = 0, id = 0;
-        bool changed;
-        do {
-            changed = false;
-            for (unsigned tid = 0; tid < num_types; tid++) {
-                if (well_founded[tid]) {
-                    continue;
-                }
-                sort* s = sorts[tid];
-                def const& d = get_def(s);
-                for (constructor const* c : d) {
-                    bool found_nonwf = false;
-                    for (accessor const* a : *c) {
-                        if (sort2id.find(a->range(), id) && !well_founded[id]) {
-                            found_nonwf = true;
-                            break;
-                        }
-                    }
-                    if (!found_nonwf) {
-                        changed = true;
-                        well_founded[tid] = true;
-                        num_well_founded++;
-                        break;
-                    }
-                }
-            }
-        } 
-        while(changed && num_well_founded < num_types);
-        return num_well_founded == num_types;
-    }
-
-    def const& util::get_def(sort* s) const {
-        return m_plugin->get_def(s);
-    }
-
-    void util::get_subsorts(sort* s, ptr_vector<sort>& sorts) const {
-        sorts.push_back(s);
-        for (unsigned i = 0; i < s->get_num_parameters(); ++i) {
-            parameter const& p = s->get_parameter(i);
-            if (p.is_ast() && is_sort(p.get_ast())) {
-                get_subsorts(to_sort(p.get_ast()), sorts);
-            }
-        }
-    }
-
-
-    util::util(ast_manager & m):
-        m(m),
-        m_family_id(m.mk_family_id("datatype")),
-        m_asts(m),
-        m_start(0) {
-        m_plugin = dynamic_cast<decl::plugin*>(m.get_plugin(m_family_id));
-        SASSERT(m_plugin);
-    }
-
-    util::~util() {
-        std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc<ptr_vector<func_decl> >());
-    }
-
-    ptr_vector<func_decl> const * util::get_datatype_constructors(sort * ty) {
-        SASSERT(is_datatype(ty));
-        ptr_vector<func_decl> * r = 0;
-        if (m_datatype2constructors.find(ty, r))
-            return r;
-        r = alloc(ptr_vector<func_decl>);
-        m_asts.push_back(ty);
-        m_vectors.push_back(r);
-        m_datatype2constructors.insert(ty, r);
-        def const& d = get_def(ty);
-        for (constructor const* c : d) {
-            func_decl_ref f = c->instantiate(ty);
-            m_asts.push_back(f);
-            r->push_back(f);
-        }
-        return r;
-    }
-
-    ptr_vector<func_decl> const * util::get_constructor_accessors(func_decl * con) {
-        SASSERT(is_constructor(con));
-        ptr_vector<func_decl> * res = 0;
-        if (m_constructor2accessors.find(con, res)) {
-            return res;
-        }
-        res = alloc(ptr_vector<func_decl>);
-        m_asts.push_back(con);
-        m_vectors.push_back(res);
-        m_constructor2accessors.insert(con, res);
-        sort * datatype = con->get_range();
-        def const& d = get_def(datatype);
-        for (constructor const* c : d) {
-            if (c->name() == con->get_name()) {
-                for (accessor const* a : *c) {
-                    func_decl_ref fn = a->instantiate(datatype);
-                    res->push_back(fn);
-                    m_asts.push_back(fn);
-                }
-                break;
-            }
-        }
-        return res;
-    }
-
-    func_decl * util::get_constructor_recognizer(func_decl * con) {
-        SASSERT(is_constructor(con));
-        func_decl * d = 0;
-        if (m_constructor2recognizer.find(con, d))
-            return d;
-        sort * datatype = con->get_range();
-        def const& dd = get_def(datatype);
-        symbol r;
-        for (constructor const* c : dd) {
-            if (c->name() == con->get_name()) {
-                r = c->recognizer();
-            }
-        }
-        parameter ps[2] = { parameter(con), parameter(r) };
-        d  = m.mk_func_decl(m_family_id, OP_DT_RECOGNISER, 2, ps, 1, &datatype);
-        SASSERT(d);
-        m_asts.push_back(con);
-        m_asts.push_back(d);
-        m_constructor2recognizer.insert(con, d);
-        return d;
-    }
-
-    func_decl * util::get_recognizer_constructor(func_decl * recognizer) const {
-        SASSERT(is_recognizer(recognizer));
-        return to_func_decl(recognizer->get_parameter(0).get_ast());
-    }
-
-    bool util::is_recursive(sort * ty) {
-        SASSERT(is_datatype(ty));
-        bool r = false;
-        if (!m_is_recursive.find(ty, r)) {
-            r = is_recursive_core(ty);
-            m_is_recursive.insert(ty, r);
-            m_asts.push_back(ty);
-        }
-        return r;
-    }
-
-    bool util::is_enum_sort(sort* s) {
-        if (!is_datatype(s)) {
-            return false;
-        }
-        bool r = false;
-        if (m_is_enum.find(s, r))
-            return r;
-        ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
-        r = true;
-        for (unsigned i = 0; r && i < cnstrs.size(); ++i) {
-            r = cnstrs[i]->get_arity() == 0;
-        }
-        m_is_enum.insert(s, r);
-        m_asts.push_back(s);
-        return r;
-    }
-
-    func_decl * util::get_accessor_constructor(func_decl * accessor) { 
-        SASSERT(is_accessor(accessor));
-        func_decl * r = 0;
-        if (m_accessor2constructor.find(accessor, r))
-            return r;
-        sort * datatype = accessor->get_domain(0);
-        symbol c_id   = accessor->get_parameter(1).get_symbol();
-        def const& d = get_def(datatype);
-        func_decl_ref fn(m);
-        for (constructor const* c : d) {
-            if (c->name() == c_id) {
-                fn = c->instantiate(datatype);
-                break;
-            }
-        }
-        r = fn;
-        m_accessor2constructor.insert(accessor, r);
-        m_asts.push_back(accessor);
-        m_asts.push_back(r);
-        return r;
-    }
-
-
-    void util::reset() {
-        m_datatype2constructors.reset();
-        m_datatype2nonrec_constructor.reset();
-        m_constructor2accessors.reset();
-        m_constructor2recognizer.reset();
-        m_recognizer2constructor.reset();
-        m_accessor2constructor.reset();
-        m_is_recursive.reset();
-        m_is_enum.reset();
-        std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc<ptr_vector<func_decl> >());
-        m_vectors.reset();
-        m_asts.reset();
-        ++m_start;
-    }
-
-
-    /**
-       \brief Return a constructor mk(T_1, ... T_n)
-       where each T_i is not a datatype or it is a datatype that contains 
-       a constructor that will not contain directly or indirectly an element of the given sort.
-    */
-    func_decl * util::get_non_rec_constructor(sort * ty) {
-        SASSERT(is_datatype(ty));
-        func_decl * r = 0;
-        if (m_datatype2nonrec_constructor.find(ty, r))
-            return r;
-        r = 0;
-        ptr_vector<sort> forbidden_set;
-        forbidden_set.push_back(ty);
-        TRACE("util_bug", tout << "invoke get-non-rec: " << sort_ref(ty, m) << "\n";);
-        r = get_non_rec_constructor_core(ty, forbidden_set);
-        SASSERT(forbidden_set.back() == ty);
-        SASSERT(r);
-        m_asts.push_back(ty);
-        m_asts.push_back(r);
-        m_datatype2nonrec_constructor.insert(ty, r);
-        return r;
-    }
-
-    /**
-       \brief Return a constructor mk(T_1, ..., T_n) where
-       each T_i is not a datatype or it is a datatype t not in forbidden_set,
-       and get_non_rec_constructor_core(T_i, forbidden_set union { T_i })
-    */
-    func_decl * util::get_non_rec_constructor_core(sort * ty, ptr_vector<sort> & forbidden_set) {
-        // We must select a constructor c(T_1, ..., T_n):T such that
-        //   1) T_i's are not recursive
-        // If there is no such constructor, then we select one that 
-        //   2) each type T_i is not recursive or contains a constructor that does not depend on T
-
-        ptr_vector<func_decl> const& constructors = *get_datatype_constructors(ty);
-        unsigned sz = constructors.size();
-        TRACE("util_bug", tout << "get-non-rec constructor: " << sort_ref(ty, m) << "\n";
-              tout << "forbidden: ";
-              for (sort* s : forbidden_set) tout << sort_ref(s, m) << " ";
-              tout << "\n";
-              tout << "constructors: " << sz << "\n";
-              for (func_decl* f : constructors) tout << func_decl_ref(f, m) << "\n";
-              );
-        // step 1)
-        unsigned start = ++m_start;
-        for (unsigned j = 0; j < sz; ++j) {        
-            func_decl * c = constructors[(j + start) % sz];
-            TRACE("util_bug", tout << "checking " << sort_ref(ty, m) << ": " << func_decl_ref(c, m) << "\n";);
-            unsigned num_args = c->get_arity();
-            unsigned i = 0;
-            for (; i < num_args && !is_datatype(c->get_domain(i)); i++);
-            if (i == num_args) {
-                TRACE("util_bug", tout << "found non-rec " << func_decl_ref(c, m) << "\n";);
-                return c;
-            }
-        }
-        // step 2)
-        for (unsigned j = 0; j < sz; ++j) {        
-            func_decl * c = constructors[(j + start) % sz];
-            TRACE("util_bug", tout << "non_rec_constructor c: " << j << " " << func_decl_ref(c, m) << "\n";);
-            unsigned num_args = c->get_arity();
-            unsigned i = 0;
-            for (; i < num_args; i++) {
-                sort * T_i = c->get_domain(i);
-                TRACE("util_bug", tout << "c: " << i << " " << sort_ref(T_i, m) << "\n";);
-                if (!is_datatype(T_i)) {
-                    TRACE("util_bug", tout << sort_ref(T_i, m) << " is not a datatype\n";);
-                    continue;
-                }
-                if (std::find(forbidden_set.begin(), forbidden_set.end(), T_i) != forbidden_set.end()) {
-                    TRACE("util_bug", tout << sort_ref(T_i, m) << " is in forbidden_set\n";);
-                    break;
-                }
-                forbidden_set.push_back(T_i);
-                func_decl * nested_c = get_non_rec_constructor_core(T_i, forbidden_set);
-                SASSERT(forbidden_set.back() == T_i);
-                forbidden_set.pop_back();
-                if (nested_c == 0)
-                    break;
-                TRACE("util_bug", tout << "nested_c: " << nested_c->get_name() << "\n";);
-            }
-            if (i == num_args)
-                return c;
-        }
-        return 0;
-    }
-
-    unsigned util::get_constructor_idx(func_decl * f) const {
-        unsigned idx = 0;
-        def const& d = get_def(f->get_range());
-        for (constructor* c : d) {
-            if (c->name() == f->get_name()) {
-                return idx;
-            }
-            ++idx;
-        }
-        UNREACHABLE();
-        return 0;
-    }
-
-    unsigned util::get_recognizer_constructor_idx(func_decl * f) const {
-        return get_constructor_idx(get_recognizer_constructor(f));
-    }
-
-    /**
-       \brief Two datatype sorts s1 and s2 are siblings if they were
-       defined together in the same mutually recursive definition.
-    */
-    bool util::are_siblings(sort * s1, sort * s2) {
-        if (!is_datatype(s1) || !is_datatype(s2)) {
-            return s1 == s2;
-        }
-        else {
-            return get_def(s1).id() == get_def(s2).id();
-        }
-    }
-
-    unsigned util::get_datatype_num_constructors(sort * ty) {
-        def const& d = get_def(ty->get_name());
-        return d.constructors().size();
-    }
-
-    void util::get_defs(sort* s0, ptr_vector<def>& defs) {
-        svector<symbol> mark;
-        ptr_buffer<sort> todo;
-        todo.push_back(s0);
-        mark.push_back(s0->get_name());
-        while (!todo.empty()) {
-            sort* s = todo.back();
-            todo.pop_back();
-            defs.push_back(&m_plugin->get_def(s->get_name()));
-            def const& d = get_def(s);
-            for (constructor* c : d) {
-                for (accessor* a : *c) {
-                    sort* s = a->range();
-                    if (are_siblings(s0, s) && !mark.contains(s->get_name())) {
-                        mark.push_back(s->get_name());
-                        todo.push_back(s);
-                    }
-                }
-            }
-        }
-    }
-
-    void util::display_datatype(sort *s0, std::ostream& out) {
-        ast_mark mark;
-        ptr_buffer<sort> todo;
-        SASSERT(is_datatype(s0));
-        out << s0->get_name() << " where\n";
-        todo.push_back(s0);
-        mark.mark(s0, true);
-        while (!todo.empty()) {
-            sort* s = todo.back();
-            todo.pop_back();
-            out << s->get_name() << " =\n";
-
-            ptr_vector<func_decl> const& cnstrs = *get_datatype_constructors(s);
-            for (unsigned i = 0; i < cnstrs.size(); ++i) {
-                func_decl* cns = cnstrs[i];
-                func_decl* rec = get_constructor_recognizer(cns);
-                out << "  " << cns->get_name() << " :: " << rec->get_name() << " :: ";
-                ptr_vector<func_decl> const & accs = *get_constructor_accessors(cns);
-                for (unsigned j = 0; j < accs.size(); ++j) {
-                    func_decl* acc = accs[j];
-                    sort* s1 = acc->get_range();
-                    out << "(" << acc->get_name() << ": " << s1->get_name() << ") "; 
-                    if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) {
-                        mark.mark(s1, true);
-                        todo.push_back(s1);
-                    }          
-                }
-                out << "\n";
-            }
-        }
-    }
-}
-
-datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs) {
-    datatype::decl::plugin* p = u.get_plugin();
-    datatype::def* d = p->mk(n, num_params, params);
-    for (unsigned i = 0; i < num_constructors; ++i) {
-        d->add(cs[i]);
-    }
-    return d;
-}
-
-#endif
diff --git a/src/ast/datatype_decl_plugin2.h b/src/ast/datatype_decl_plugin2.h
deleted file mode 100644
index 364ba9350..000000000
--- a/src/ast/datatype_decl_plugin2.h
+++ /dev/null
@@ -1,439 +0,0 @@
-/*++
-Copyright (c) 2017 Microsoft Corporation
-
-Module Name:
-
-    datatype_decl_plugin.h
-
-Abstract:
-
-    <abstract>
-
-Author:
-
-    Nikolaj Bjorner (nbjorner) 2017-9-1 
-
-Revision History:
-
-    rewritten to support SMTLIB-2.6 parameters from
-     Leonardo de Moura (leonardo) 2008-01-09.
-
---*/
-#ifndef DATATYPE_DECL_PLUGIN2_H_
-#define DATATYPE_DECL_PLUGIN2_H_
-
-#include "ast/ast.h"
-#include "util/buffer.h"
-#include "util/symbol_table.h"
-#include "util/obj_hashtable.h"
-
-#ifdef DATATYPE_V2
-
-    enum sort_kind {
-        DATATYPE_SORT
-    };
-   
-    enum op_kind {
-        OP_DT_CONSTRUCTOR,
-        OP_DT_RECOGNISER,
-        OP_DT_IS,
-        OP_DT_ACCESSOR,        
-        OP_DT_UPDATE_FIELD,
-        LAST_DT_OP
-    };
-
-namespace datatype {
-
-    class util;
-    class def;
-    class accessor;
-    class constructor;
- 
-
-    class accessor {
-        symbol    m_name;
-        sort_ref  m_range;
-        unsigned m_index;    // reference to recursive data-type may only get resolved after all mutually recursive data-types are procssed.
-        constructor* m_constructor;
-    public:
-        accessor(ast_manager& m, symbol const& n, sort* range):
-            m_name(n),
-            m_range(range, m),
-            m_index(UINT_MAX)
-        {}
-        accessor(ast_manager& m, symbol const& n, unsigned index):
-            m_name(n),
-            m_range(m),
-            m_index(index)
-        {}
-        sort* range() const { return m_range; }
-        void fix_range(sort_ref_vector const& dts);
-        symbol const& name() const { return m_name; }
-        func_decl_ref instantiate(sort_ref_vector const& ps) const;
-        func_decl_ref instantiate(sort* dt) const;
-        void attach(constructor* d) { m_constructor = d; }
-        constructor const& get_constructor() const { return *m_constructor; }
-        def const& get_def() const;
-        util& u() const;
-        accessor* translate(ast_translation& tr);
-    };
-
-    class constructor {
-        symbol           m_name;
-        symbol           m_recognizer;
-        ptr_vector<accessor> m_accessors;
-        def*             m_def;
-    public:
-        constructor(symbol n, symbol const& r): m_name(n), m_recognizer(r) {}
-        ~constructor();
-        void add(accessor* a) { m_accessors.push_back(a); a->attach(this); }
-        symbol const& name() const { return m_name; }
-        symbol const& recognizer() const { return m_recognizer; }
-        ptr_vector<accessor> const& accessors() const { return m_accessors; }
-        ptr_vector<accessor>::const_iterator begin() const { return m_accessors.begin(); }
-        ptr_vector<accessor>::const_iterator end() const { return m_accessors.end(); }
-        ptr_vector<accessor>::iterator begin() { return m_accessors.begin(); }
-        ptr_vector<accessor>::iterator end() { return m_accessors.end(); }
-        func_decl_ref instantiate(sort_ref_vector const& ps) const;
-        func_decl_ref instantiate(sort* dt) const;
-        void attach(def* d) { m_def = d; }
-        def const& get_def() const { return *m_def; }
-        util& u() const;
-        constructor* translate(ast_translation& tr);
-    };
-
-    namespace param_size {
-        class size {
-            unsigned m_ref;
-        public:
-            size(): m_ref(0) {}
-            virtual ~size() {}
-            void inc_ref() { ++m_ref; }
-            void dec_ref() { --m_ref; if (m_ref == 0) dealloc(this); }
-            static size* mk_offset(sort_size const& s); 
-            static size* mk_param(sort_ref& p); 
-            static size* mk_plus(size* a1, size* a2); 
-            static size* mk_times(size* a1, size* a2); 
-            static size* mk_plus(ptr_vector<size>& szs);
-            static size* mk_times(ptr_vector<size>& szs);
-            static size* mk_power(size* a1, size* a2);
-            
-            virtual size* subst(obj_map<sort, size*>& S) = 0;
-            virtual sort_size eval(obj_map<sort, sort_size> const& S) = 0;
-            
-        };
-        struct offset : public size {
-            sort_size m_offset;
-            offset(sort_size const& s): m_offset(s) {}
-            virtual ~offset() {}
-            virtual size* subst(obj_map<sort,size*>& S) { return this; }
-            virtual sort_size eval(obj_map<sort, sort_size> const& S) { return m_offset; }
-        };
-        struct plus : public size {
-            size* m_arg1, *m_arg2;
-            plus(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref();}
-            virtual ~plus() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
-            virtual size* subst(obj_map<sort,size*>& S) { return mk_plus(m_arg1->subst(S), m_arg2->subst(S)); }
-            virtual sort_size eval(obj_map<sort, sort_size> const& S) { 
-                sort_size s1 = m_arg1->eval(S);
-                sort_size s2 = m_arg2->eval(S);
-                if (s1.is_infinite()) return s1;
-                if (s2.is_infinite()) return s2;
-                if (s1.is_very_big()) return s1;
-                if (s2.is_very_big()) return s2;
-                rational r = rational(s1.size(), rational::ui64()) + rational(s2.size(), rational::ui64());
-                return sort_size(r);
-            }
-        };
-        struct times : public size {
-            size* m_arg1, *m_arg2;
-            times(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); }
-            virtual ~times() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
-            virtual size* subst(obj_map<sort,size*>& S) { return mk_times(m_arg1->subst(S), m_arg2->subst(S)); }
-            virtual sort_size eval(obj_map<sort, sort_size> const& S) { 
-                sort_size s1 = m_arg1->eval(S);
-                sort_size s2 = m_arg2->eval(S);
-                if (s1.is_infinite()) return s1;
-                if (s2.is_infinite()) return s2;
-                if (s1.is_very_big()) return s1;
-                if (s2.is_very_big()) return s2;
-                rational r = rational(s1.size(), rational::ui64()) * rational(s2.size(), rational::ui64());
-                return sort_size(r);
-            }
-        };
-        struct power : public size {
-            size* m_arg1, *m_arg2;
-            power(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); }
-            virtual ~power() { m_arg1->dec_ref(); m_arg2->dec_ref(); }
-            virtual size* subst(obj_map<sort,size*>& S) { return mk_power(m_arg1->subst(S), m_arg2->subst(S)); }
-            virtual sort_size eval(obj_map<sort, sort_size> const& S) { 
-                sort_size s1 = m_arg1->eval(S);
-                sort_size s2 = m_arg2->eval(S);
-                // s1^s2
-                if (s1.is_infinite()) return s1;
-                if (s2.is_infinite()) return s2;
-                if (s1.is_very_big()) return s1;
-                if (s2.is_very_big()) return s2;
-                if (s1.size() == 1) return s1;
-                if (s2.size() == 1) return s1;
-                if (s1.size() > (2 << 20) || s2.size() > 10) return sort_size::mk_very_big();
-                rational r = ::power(rational(s1.size(), rational::ui64()), static_cast<unsigned>(s2.size()));
-                return sort_size(r);
-            }
-        };
-        struct sparam : public size {
-            sort_ref m_param;
-            sparam(sort_ref& p): m_param(p) {}
-            virtual ~sparam() {}
-            virtual size* subst(obj_map<sort,size*>& S) { return S[m_param]; }
-            virtual sort_size eval(obj_map<sort, sort_size> const& S) { return S[m_param]; }
-        };
-    };
-
-    class def {
-        ast_manager&        m;
-        util&               m_util;
-        symbol              m_name;
-        unsigned            m_class_id;
-        param_size::size*   m_sort_size;
-        sort_ref_vector     m_params;
-        mutable sort_ref    m_sort;
-        ptr_vector<constructor> m_constructors;
-    public:
-        def(ast_manager& m, util& u, symbol const& n, unsigned class_id, unsigned num_params, sort * const* params):
-            m(m),
-            m_util(u),
-            m_name(n),
-            m_class_id(class_id),            
-            m_sort_size(0),
-            m_params(m, num_params, params), 
-            m_sort(m)
-        {}
-        ~def() {
-            if (m_sort_size) m_sort_size->dec_ref();
-            for (constructor* c : m_constructors) dealloc(c);
-            m_constructors.reset();
-        }
-        void add(constructor* c) {
-            m_constructors.push_back(c);
-            c->attach(this);
-        }
-        symbol const& name() const { return m_name; }
-        unsigned id() const { return m_class_id; }
-        sort_ref instantiate(sort_ref_vector const& ps) const;
-        ptr_vector<constructor> const& constructors() const { return m_constructors; }
-        ptr_vector<constructor>::const_iterator begin() const { return m_constructors.begin(); }
-        ptr_vector<constructor>::const_iterator end() const { return m_constructors.end(); }
-        ptr_vector<constructor>::iterator begin() { return m_constructors.begin(); }
-        ptr_vector<constructor>::iterator end() { return m_constructors.end(); }
-        sort_ref_vector const& params() const { return m_params; }
-        util& u() const { return m_util; }
-        param_size::size* sort_size() { return m_sort_size; }
-        void set_sort_size(param_size::size* p) { m_sort_size = p; p->inc_ref(); m_sort = 0; }
-        def* translate(ast_translation& tr, util& u);
-    };
-
-    namespace decl {
-
-        class plugin : public decl_plugin {
-            mutable scoped_ptr<util> m_util;
-            map<symbol, def*, symbol_hash_proc, symbol_eq_proc> m_defs; 
-            svector<symbol>          m_def_block;
-            unsigned                 m_class_id;
-            util & u() const;
-
-            virtual void inherit(decl_plugin* other_p, ast_translation& tr);
-
-        public:
-            plugin(): m_class_id(0) {}
-            virtual ~plugin();
-
-            virtual void finalize();
-        
-            virtual decl_plugin * mk_fresh() { return alloc(plugin); }
-        
-            virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters);
-        
-            virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, 
-                                             unsigned arity, sort * const * domain, sort * range);
-                
-            virtual expr * get_some_value(sort * s);
-        
-            virtual bool is_fully_interp(sort * s) const;
-        
-            virtual bool is_value(app* e) const;
-        
-            virtual bool is_unique_value(app * e) const { return is_value(e); }
-        
-            virtual void get_op_names(svector<builtin_name> & op_names, symbol const & logic);
-                
-            void begin_def_block() { m_class_id++; m_def_block.reset(); }
-
-            void end_def_block();
-
-            def* mk(symbol const& name, unsigned n, sort * const * params);
-
-            void remove(symbol const& d);
-
-            bool mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts);
-
-            def const& get_def(sort* s) const { return *(m_defs[datatype_name(s)]); }
-            def& get_def(symbol const& s) { return *(m_defs[s]); }
-            bool is_declared(sort* s) const { return m_defs.contains(datatype_name(s)); }
-        private:
-            bool is_value_visit(expr * arg, ptr_buffer<app> & todo) const;
-        
-            func_decl * mk_update_field(
-                unsigned num_parameters, parameter const * parameters, 
-                unsigned arity, sort * const * domain, sort * range);
-
-            func_decl * mk_constructor(
-                unsigned num_parameters, parameter const * parameters, 
-                unsigned arity, sort * const * domain, sort * range);
-
-            func_decl * mk_accessor(
-                unsigned num_parameters, parameter const * parameters, 
-                unsigned arity, sort * const * domain, sort * range);
-
-            func_decl * mk_recognizer(
-                unsigned num_parameters, parameter const * parameters, 
-                unsigned arity, sort * const * domain, sort * range);
-
-            func_decl * mk_is(
-                unsigned num_parameters, parameter const * parameters, 
-                unsigned arity, sort * const * domain, sort * range);
-
-            symbol datatype_name(sort * s) const {
-                //SASSERT(u().is_datatype(s));
-                return s->get_parameter(0).get_symbol();
-            }
-            
-        };
-    }
-
-    class util {
-        ast_manager & m;
-        family_id     m_family_id;
-        mutable decl::plugin* m_plugin;
-
-                
-        obj_map<sort, ptr_vector<func_decl> *>      m_datatype2constructors;
-        obj_map<sort, func_decl *>                  m_datatype2nonrec_constructor;
-        obj_map<func_decl, ptr_vector<func_decl> *> m_constructor2accessors;
-        obj_map<func_decl, func_decl *>             m_constructor2recognizer;
-        obj_map<func_decl, func_decl *>             m_recognizer2constructor;
-        obj_map<func_decl, func_decl *>             m_accessor2constructor;
-        obj_map<sort, bool>                         m_is_recursive;
-        obj_map<sort, bool>                         m_is_enum;
-        mutable obj_map<sort, bool>                 m_is_fully_interp;
-        mutable ast_ref_vector                      m_asts;
-        ptr_vector<ptr_vector<func_decl> >          m_vectors;
-        unsigned                                    m_start;
-        mutable ptr_vector<sort>                    m_fully_interp_trail;
-        
-        func_decl * get_non_rec_constructor_core(sort * ty, ptr_vector<sort> & forbidden_set);
-
-        friend class decl::plugin;
-
-        bool is_recursive_core(sort * s) const;
-        sort_size get_datatype_size(sort* s0);
-        void compute_datatype_size_functions(svector<symbol> const& names);
-        param_size::size* get_sort_size(sort_ref_vector const& params, sort* s);
-        bool is_well_founded(unsigned num_types, sort* const* sorts);
-        def& get_def(symbol const& s) { return m_plugin->get_def(s); }
-        void get_subsorts(sort* s, ptr_vector<sort>& sorts) const;        
-
-    public:
-        util(ast_manager & m);
-        ~util();
-        ast_manager & get_manager() const { return m; }
-        // sort * mk_datatype_sort(symbol const& name, unsigned n, sort* const* params); 
-        bool is_datatype(sort const* s) const { return is_sort_of(s, m_family_id, DATATYPE_SORT); }
-        bool is_enum_sort(sort* s);
-        bool is_recursive(sort * ty);
-        bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
-        bool is_recognizer(func_decl * f) const { return is_recognizer0(f) || is_is(f); }
-        bool is_recognizer0(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); }
-        bool is_is(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_IS); }
-        bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); }
-        bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
-        bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
-        bool is_recognizer0(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER);} 
-        bool is_is(app * f) const { return is_app_of(f, m_family_id, OP_DT_IS);} 
-        bool is_recognizer(app * f) const { return is_recognizer0(f) || is_is(f); }
-        bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
-        bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
-        ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
-        unsigned get_datatype_num_constructors(sort * ty);
-        unsigned get_datatype_num_parameter_sorts(sort * ty);
-        sort*  get_datatype_parameter_sort(sort * ty, unsigned idx);
-        func_decl * get_non_rec_constructor(sort * ty);
-        func_decl * get_constructor_recognizer(func_decl * constructor);
-        ptr_vector<func_decl> const * get_constructor_accessors(func_decl * constructor);
-        func_decl * get_accessor_constructor(func_decl * accessor);
-        func_decl * get_recognizer_constructor(func_decl * recognizer) const;
-        family_id get_family_id() const { return m_family_id; }
-        bool are_siblings(sort * s1, sort * s2);
-        bool is_func_decl(op_kind k, unsigned num_params, parameter const* params, func_decl* f);
-        bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f);
-        void reset();
-        bool is_declared(sort* s) const;
-        void display_datatype(sort *s, std::ostream& strm);
-        bool is_fully_interp(sort * s) const;
-        sort_ref_vector datatype_params(sort * s) const;
-        unsigned get_constructor_idx(func_decl * f) const;
-        unsigned get_recognizer_constructor_idx(func_decl * f) const;
-        decl::plugin* get_plugin() { return m_plugin; }
-        void get_defs(sort* s, ptr_vector<def>& defs);
-        def const& get_def(sort* s) const;
-    };
-
-};
-
-typedef datatype::accessor accessor_decl;
-typedef datatype::constructor constructor_decl;
-typedef datatype::def datatype_decl;
-typedef datatype::decl::plugin datatype_decl_plugin;
-typedef datatype::util datatype_util;
-
-class type_ref {
-    void * m_data;
-public:
-    type_ref():m_data(TAG(void *, static_cast<void*>(0), 1)) {}
-    type_ref(int idx):m_data(BOXINT(void *, idx)) {}
-    type_ref(sort * s):m_data(TAG(void *, s, 1)) {}
-    
-    bool is_idx() const { return GET_TAG(m_data) == 0; }
-    bool is_sort() const { return GET_TAG(m_data) == 1; }
-    sort * get_sort() const { return UNTAG(sort *, m_data); }
-    int get_idx() const { return UNBOXINT(m_data); }
-};
-
-inline accessor_decl * mk_accessor_decl(ast_manager& m, symbol const & n, type_ref const & t) {
-    if (t.is_idx()) {
-        return alloc(accessor_decl, m, n, t.get_idx());
-    }
-    else {
-        return alloc(accessor_decl, m, n, t.get_sort());
-    }
-}
-
-inline constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * * acs) {
-    constructor_decl* c = alloc(constructor_decl, n, r);
-    for (unsigned i = 0; i < num_accessors; ++i) {
-        c->add(acs[i]);
-    }
-    return c;
-}
-
-
-
-// Remark: the datatype becomes the owner of the constructor_decls
-datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs);
-inline void del_datatype_decl(datatype_decl * d) {}
-inline void del_datatype_decls(unsigned num, datatype_decl * const * ds) {}
-
-
-#endif /* DATATYPE_DECL_PLUGIN_H_ */
-
-#endif /* DATATYPE_V2 */
diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp
index 95a6030f3..7eac1f347 100644
--- a/src/cmd_context/pdecl.cpp
+++ b/src/cmd_context/pdecl.cpp
@@ -355,13 +355,8 @@ psort_dt_decl::psort_dt_decl(unsigned id, unsigned num_params, pdecl_manager & m
 
 
 sort * psort_dt_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
-#ifndef DATATYPE_V2
-    UNREACHABLE();
-    return 0;
-#else
     SASSERT(n == m_num_params);
     return m.instantiate_datatype(this, m_name, n, s);
-#endif
 }
 
 void psort_dt_decl::display(std::ostream & out) const {
@@ -581,7 +576,6 @@ struct datatype_decl_buffer {
     ~datatype_decl_buffer() { del_datatype_decls(m_buffer.size(), m_buffer.c_ptr()); }
 };
 
-#ifdef DATATYPE_V2
 
 sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
     sort * r = m.instantiate_datatype(this, m_name, n, s);
@@ -615,37 +609,6 @@ sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const *
     return r;
 }
 
-#else
-sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) {
-    SASSERT(m_num_params == n);
-    sort * r = find(s);
-    if (r)
-        return r;
-    if (m_parent != 0) {
-        if (m_parent->instantiate(m, s)) {
-            r = find(s);
-            SASSERT(r);
-            return r;
-        }
-    }
-    else {
-        datatype_decl_buffer dts;
-        dts.m_buffer.push_back(instantiate_decl(m, s));
-        datatype_decl * d_ptr = dts.m_buffer[0];
-        sort_ref_vector sorts(m.m());
-        bool is_ok = m.get_dt_plugin()->mk_datatypes(1, &d_ptr, m_num_params, s, sorts);
-        TRACE("pdatatype_decl", tout << "instantiating " << m_name << " is_ok: " << is_ok << "\n";);
-        if (is_ok) {
-            r = sorts.get(0);
-            cache(m, s, r);
-            m.save_info(r, this, n, s);
-            m.notify_new_dt(r, this);
-            return r;
-        }
-    }
-    return 0;
-}
-#endif
 
 void pdatatype_decl::display(std::ostream & out) const {
     out << "(declare-datatype " << m_name;
@@ -666,7 +629,6 @@ void pdatatype_decl::display(std::ostream & out) const {
     out << ")";
 }
 
-#ifdef DATATYPE_V2
 bool pdatatype_decl::commit(pdecl_manager& m) {
     TRACE("datatype", tout << m_name << "\n";);
     sort_ref_vector ps(m.m());
@@ -683,7 +645,6 @@ bool pdatatype_decl::commit(pdecl_manager& m) {
     }
     return is_ok;
 }
-#endif
 
 
 pdatatypes_decl::pdatatypes_decl(unsigned id, unsigned num_params, pdecl_manager & m,
@@ -714,7 +675,6 @@ bool pdatatypes_decl::fix_missing_refs(symbol & missing) {
     return true;
 }
 
-#ifdef DATATYPE_V2
 sort* pdecl_manager::instantiate_datatype(psort_decl* p, symbol const& name, unsigned n, sort * const* s) {
     TRACE("datatype", tout << name << " "; for (unsigned i = 0; i < n; ++i) tout << s[i]->get_name() << " "; tout << "\n";);
     pdecl_manager& m = *this;
@@ -746,28 +706,7 @@ bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) {
     UNREACHABLE();
     return false;
 }
-#else
-bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) {
-    datatype_decl_buffer dts;
-    for (auto d : m_datatypes) {
-        dts.m_buffer.push_back(d->instantiate_decl(m, s));
-    }
-    sort_ref_vector sorts(m.m());
-    bool is_ok = m.get_dt_plugin()->mk_datatypes(dts.m_buffer.size(), dts.m_buffer.c_ptr(), m_num_params, s, sorts);
-    if (!is_ok)
-        return false;
-    unsigned i = 0;
-    for (auto d : m_datatypes) {
-        sort * new_dt = sorts.get(i++);
-        d->cache(m, s, new_dt);
-        m.save_info(new_dt, d, m_num_params, s);
-        m.notify_new_dt(new_dt, d);
-    }
-    return true;
-}
-#endif
 
-#ifdef DATATYPE_V2
 bool pdatatypes_decl::commit(pdecl_manager& m) {
     datatype_decl_buffer dts;
     for (pdatatype_decl* d : m_datatypes) {
@@ -789,7 +728,6 @@ bool pdatatypes_decl::commit(pdecl_manager& m) {
     }
     return is_ok;
 }
-#endif
 
 struct pdecl_manager::sort_info {
     psort_decl * m_decl;
diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h
index bef723380..c72020827 100644
--- a/src/cmd_context/pdecl.h
+++ b/src/cmd_context/pdecl.h
@@ -241,9 +241,7 @@ public:
     virtual void display(std::ostream & out) const;
     bool has_missing_refs(symbol & missing) const;
     bool has_duplicate_accessors(symbol & repeated) const;
-#ifdef DATATYPE_V2
     bool commit(pdecl_manager& m);
-#endif
 };
 
 /**
@@ -263,10 +261,8 @@ public:
     pdatatype_decl const * const * children() const { return m_datatypes.c_ptr(); }
     pdatatype_decl * const * begin() const { return m_datatypes.begin(); }
     pdatatype_decl * const * end() const { return m_datatypes.end(); }
-#ifdef DATATYPE_V2
     // commit declaration 
     bool commit(pdecl_manager& m);
-#endif
 };
 
 class new_datatype_eh {
diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp
index a1304a848..681c3d597 100644
--- a/src/parsers/smt2/smt2parser.cpp
+++ b/src/parsers/smt2/smt2parser.cpp
@@ -877,9 +877,7 @@ namespace smt2 {
             }
             else if (sz == 1) {
                 check_missing(new_dt_decls[0], line, pos);
-#ifdef DATATYPE_V2
                 new_dt_decls[0]->commit(pm());
-#endif
             }
             else {
                 SASSERT(sz > 1);
@@ -892,27 +890,9 @@ namespace smt2 {
                     err_msg += "'";
                     throw parser_exception(err_msg, line, pos);
                 }
-#ifndef DATATYPE_V2
-                m_ctx.insert_aux_pdecl(dts.get());
-#else
                 dts->commit(pm());
-				m_ctx.insert_aux_pdecl(dts.get());
-#endif
+                m_ctx.insert_aux_pdecl(dts.get());
             }
-#ifndef DATATYPE_V2
-            for (unsigned i = 0; i < sz; i++) {
-                pdatatype_decl * d = new_dt_decls[i];
-                SASSERT(d != 0);
-                symbol duplicated;
-                check_duplicate(d, line, pos);
-                m_ctx.insert(d);
-                if (d->get_num_params() == 0) {
-                    // if datatype is not parametric... then force instantiation to register accessor, recognizers and constructors...
-                    sort_ref s(m());
-                    s = d->instantiate(pm(), 0, 0);
-                }
-            }
-#else
             for (unsigned i = 0; i < sz; i++) {
                 pdatatype_decl * d = new_dt_decls[i];
                 symbol duplicated;
@@ -922,7 +902,6 @@ namespace smt2 {
                     m_ctx.insert(d);
                 }
             }                
-#endif
             TRACE("declare_datatypes", tout << "i: " << i << " new_dt_decls.size(): " << sz << "\n";
                   for (unsigned j = 0; j < new_dt_decls.size(); ++j) tout << new_dt_decls[j]->get_name() << "\n";);
             m_ctx.print_success();
@@ -952,16 +931,7 @@ namespace smt2 {
             check_missing(d, line, pos);
             check_duplicate(d, line, pos);
 
-#ifndef DATATYPE_V2
-            m_ctx.insert(d);
-            if (d->get_num_params() == 0) {
-                // if datatype is not parametric... then force instantiation to register accessor, recognizers and constructors...
-                sort_ref s(m());
-                s = d->instantiate(pm(), 0, 0);
-            }
-#else
             d->commit(pm());
-#endif
             check_rparen_next("invalid end of datatype declaration, ')' expected");
             m_ctx.print_success();
         }