Commits

Nikolaj Bjorner committed 1c17e40

optmizing DL

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

Comments (0)

Files changed (6)

src/muz_qe/dl_rule.cpp

 #include"quant_hoist.h"
 #include"expr_replacer.h"
 #include"bool_rewriter.h"
+#include"qe_lite.h"
 
 namespace datalog {
 
         return r;
     }
 
+    void rule_manager::reduce_unbound_vars(rule_ref& r) {
+        unsigned ut_len = r->get_uninterpreted_tail_size();
+        unsigned t_len = r->get_tail_size();
+        ptr_vector<sort> vars;
+        uint_set index_set;
+        qe_lite qe(m);
+        expr_ref_vector conjs(m);
+
+        if (ut_len == t_len) {
+            return;
+        }
+
+        get_free_vars(r->get_head(), vars);
+        for (unsigned i = 0; i < ut_len; ++i) {
+            get_free_vars(r->get_tail(i), vars);
+        }
+        for (unsigned i = ut_len; i < t_len; ++i) {
+            conjs.push_back(r->get_tail(i));
+        }
+
+        for (unsigned i = 0; i < vars.size(); ++i) {
+            if (vars[i]) {
+                index_set.insert(i);
+            }
+        }
+        qe(index_set, false, conjs);
+        bool change = conjs.size() != t_len - ut_len;
+        for (unsigned i = 0; !change && i < conjs.size(); ++i) {
+            change = r->get_tail(ut_len+i) != conjs[i].get();
+        }
+        if (change) {
+            app_ref_vector tail(m);
+            svector<bool> tail_neg;
+            for (unsigned i = 0; i < ut_len; ++i) {
+                tail.push_back(r->get_tail(i));
+                tail_neg.push_back(r->is_neg_tail(i));
+            }
+            for (unsigned i = 0; i < conjs.size(); ++i) {
+                tail.push_back(ensure_app(conjs[i].get()));
+            }
+            tail_neg.resize(tail.size(), false);
+            IF_VERBOSE(1, r->display(m_ctx, verbose_stream() << "reducing rule\n"););
+            r = mk(r->get_head(), tail.size(), tail.c_ptr(), tail_neg.c_ptr());
+            IF_VERBOSE(1, r->display(m_ctx, verbose_stream() << "reduced rule\n"););
+            TRACE("dl", r->display(m_ctx, tout << "reduced rule\n"););
+        }
+    }
+
     void rule_manager::fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination) {
 
+        reduce_unbound_vars(r);
+
         if (!m_ctx.fix_unbound_vars()) {
             return;
         }
 
-        if (r->get_uninterpreted_tail_size() == r->get_tail_size()) {
-            //no interpreted tail to fix
+        unsigned ut_len = r->get_uninterpreted_tail_size();
+        unsigned t_len = r->get_tail_size();
+
+        if (ut_len == t_len) {
+            // no interpreted tail to fix
             return;
         }
 
         get_free_vars(r, free_rule_vars);
         vctr.count_vars(m, head);
 
-        unsigned ut_len = r->get_uninterpreted_tail_size();
+
         for (unsigned i = 0; i < ut_len; i++) {
             app * t = r->get_tail(i);
             vctr.count_vars(m, t);
         }
 
         ptr_vector<sort> interp_vars;
-        //var_idx_set interp_vars;
         var_idx_set unbound_vars;
         expr_ref_vector tails_with_unbound(m);
 
-        unsigned t_len = r->get_tail_size();
         for (unsigned i = ut_len; i < t_len; i++) {
             app * t = r->get_tail(i);
             interp_vars.reset();

src/muz_qe/dl_rule.h

 
         unsigned hoist_quantifier(bool is_forall, expr_ref& fml, svector<symbol>* names);
 
+        /**
+           \brief Perform cheap quantifier elimination to reduce the number of variables in the interpreted tail.
+         */
+        void reduce_unbound_vars(rule_ref& r);
+
     public:
 
         ast_manager& get_manager() const { return m; }
         void fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination);
 
 
+
         /**
            \brief apply substitution to variables of rule.
         */

src/muz_qe/pdr_context.cpp

             ++m_stats.m_num_nodes;
             m_search.add_leaf(*child); 
             IF_VERBOSE(2, verbose_stream() << "Predecessor: " << mk_pp(o_cube, m) << "\n";);
+            m_stats.m_max_depth = std::max(m_stats.m_max_depth, child->depth());
         }
         check_pre_closed(n);
         TRACE("pdr", m_search.display(tout););

src/muz_qe/pdr_context.h

         expr_ref               m_state;
         model_ref              m_model;
         ptr_vector<model_node> m_children;
-        unsigned               m_level;
+        unsigned               m_level;       
         unsigned               m_orig_level;
+        unsigned               m_depth;
         bool                   m_closed;
     public:
         model_node(model_node* parent, expr_ref& state, pred_transformer& pt, unsigned level):
-            m_parent(parent), m_pt(pt), m_state(state), m_model(0), m_level(level), m_orig_level(level), m_closed(false) {
+            m_parent(parent), m_pt(pt), m_state(state), m_model(0), 
+                m_level(level), m_orig_level(level), m_depth(0), m_closed(false) {
             if (m_parent) {
                 m_parent->m_children.push_back(this);
                 SASSERT(m_parent->m_level == level+1);
                 SASSERT(m_parent->m_level > 0);
+                m_depth = m_parent->m_depth+1;
             }
         }
         void set_model(model_ref& m) { m_model = m; }
         unsigned level() const { return m_level; }
         unsigned orig_level() const { return m_orig_level; }
+        unsigned depth() const { return m_depth; }
         void     increase_level() { ++m_level; }
         expr*    state() const { return m_state; }
         ptr_vector<model_node> const& children() { return m_children; }

src/muz_qe/qe_lite.cpp

 #include"tactical.h"
 #include"bool_rewriter.h"
 #include"var_subst.h"
+#include"uint_set.h"
+#include"dl_util.h"
+#include"th_rewriter.h"
 
-class der2 {
-    ast_manager &   m;
-    var_subst       m_subst;
-    expr_ref_buffer m_new_exprs;
-    
-    ptr_vector<expr> m_map;
-    int_vector       m_pos2var;
-    ptr_vector<var>  m_inx2var;
-    unsigned_vector  m_order;
-    expr_ref_vector  m_subst_map;
-    expr_ref_buffer  m_new_args;
-
-    /**
-       \brief Return true if e can be viewed as a variable disequality. 
-       Store the variable id in v and the definition in t.
-       For example:
-
-          if e is (not (= (VAR 1) T)), then v assigned to 1, and t to T.
-          if e is (iff (VAR 2) T), then v is assigned to 2, and t to (not T).
-              (not T) is used because this formula is equivalent to (not (iff (VAR 2) (not T))),
-              and can be viewed as a disequality.
-    */
-    bool is_var_diseq(expr * e, unsigned num_decls, var *& v, expr_ref & t);
-
-    /**
-       \brief Return true if e can be viewed as a variable equality.
-    */
-    bool is_var_eq(expr * e, unsigned num_decls, var *& v, expr_ref & t);
-
-    bool is_var_def(bool check_eq, expr* e, unsigned num_decls, var*& v, expr_ref& t);
-
-    void get_elimination_order();
-    void create_substitution(unsigned sz);
-    void apply_substitution(quantifier * q, expr_ref & r);
-    void reduce_quantifier1(quantifier * q, expr_ref & r, proof_ref & pr);
-    void elim_unused_vars(expr_ref& r, proof_ref &pr);
-
-public:
-    der2(ast_manager & m):m(m),m_subst(m),m_new_exprs(m),m_subst_map(m),m_new_args(m) {}
-    void operator()(quantifier * q, expr_ref & r, proof_ref & pr);
-    void reduce_quantifier(quantifier * q, expr_ref & r, proof_ref & pr);
-    ast_manager& get_manager() const { return m; }
-};
-
-static bool is_var(expr * e, unsigned num_decls) {
-    return is_var(e) && to_var(e)->get_idx() < num_decls;
-}
-
-static bool is_neg_var(ast_manager & m, expr * e, unsigned num_decls) {
-    expr* e1;
-    return m.is_not(e, e1) && is_var(e1, num_decls);
-}
-
-bool der2::is_var_def(bool check_eq, expr* e, unsigned num_decls, var*& v, expr_ref& t) {
-    if (check_eq) {
-        return is_var_eq(e, num_decls, v, t);
-    }
-    else {
-        return is_var_diseq(e, num_decls, v, t);
-    }    
-}
-
-bool der2::is_var_eq(expr * e, unsigned num_decls, var * & v, expr_ref & t) {
-    expr* lhs, *rhs;
-    
-    // (= VAR t), (iff VAR t), (iff (not VAR) t), (iff t (not VAR)) cases    
-    if (m.is_eq(e, lhs, rhs) || m.is_iff(e, lhs, rhs)) {
-        // (iff (not VAR) t) (iff t (not VAR)) cases
-        if (!is_var(lhs, num_decls) && !is_var(rhs, num_decls) && m.is_bool(lhs)) {
-            if (!is_neg_var(m, lhs, num_decls)) {
-                std::swap(lhs, rhs);
-            }
-            if (!is_neg_var(m, lhs, num_decls)) {
-                return false;
-            }
-            v = to_var(lhs);
-            t = m.mk_not(rhs);
-            m_new_exprs.push_back(t);
-            TRACE("der", tout << mk_pp(e, m) << "\n";);
-            return true;
-        }
-        if (!is_var(lhs, num_decls))
-            std::swap(lhs, rhs);
-        if (!is_var(lhs, num_decls))
-            return false;
-        v = to_var(lhs);
-        t = rhs;
-        TRACE("der", tout << mk_pp(e, m) << "\n";);
-        return true;
-    }
-
-    // (ite cond (= VAR t) (= VAR t2)) case
-    expr* cond, *e2, *e3;
-    if (m.is_ite(e, cond, e2, e3)) {
-        if (is_var_eq(e2, num_decls, v, t)) {
-            expr_ref t2(m);
-            var* v2;
-            if (is_var_eq(e3, num_decls, v2, t2) && v2 == v) {
-                t = m.mk_ite(cond, t, t2);
-                m_new_exprs.push_back(t);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // VAR = true case
-    if (is_var(e, num_decls)) {
-        t = m.mk_true();
-        v = to_var(e);
-        TRACE("der", tout << mk_pp(e, m) << "\n";);
-        return true;
-    }
-
-    // VAR = false case
-    if (is_neg_var(m, e, num_decls)) {
-        t = m.mk_false();
-        v = to_var(to_app(e)->get_arg(0));
-        TRACE("der", tout << mk_pp(e, m) << "\n";);
-        return true;
-    }
-
-    return false;
-}
-
-/**
-   \brief Return true if \c e is of the form (not (= VAR t)) or (not (iff VAR t)) or 
-                                (iff VAR t) or (iff (not VAR) t) or (VAR IDX) or (not (VAR IDX)).
-   The last case can be viewed 
-*/
-bool der2::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) {
-    expr* e1;
-    if (m.is_not(e, e1)) {
-        return is_var_eq(e, num_decls, v, t);
-    }
-    else if (is_var_eq(e, num_decls, v, t) && m.is_bool(v)) { 
-        bool_rewriter(m).mk_not(t, t);
-        m_new_exprs.push_back(t);
-        return true;
-    }
-    else {
-        return false;
-    }
-}
-
-void der2::elim_unused_vars(expr_ref& r, proof_ref& pr) {
-    if (is_quantifier(r)) {
-        quantifier * q = to_quantifier(r);
-        ::elim_unused_vars(m, q, r);
-        if (m.proofs_enabled()) {
-            proof * p1 = m.mk_elim_unused_vars(q, r);
-            pr = m.mk_transitivity(pr, p1);
-        }
-    }
-}
-
-/**
-   Reduce the set of definitions in quantifier.
-   Then eliminate variables that have become unused
-*/
-void der2::operator()(quantifier * q, expr_ref & r, proof_ref & pr) {
-    TRACE("der", tout << mk_pp(q, m) << "\n";);    
-    pr = 0;
-    r  = q;
-    reduce_quantifier(q, r, pr);    
-    if (r != q) {
-        elim_unused_vars(r, pr);
-    }
-}
-
-void der2::reduce_quantifier(quantifier * q, expr_ref & r, proof_ref & pr) {   
-    r = q;
-    // Keep applying reduce_quantifier1 until r doesn't change anymore
-    do {
-        proof_ref curr_pr(m);
-        q  = to_quantifier(r);
-        reduce_quantifier1(q, r, curr_pr);
-        if (m.proofs_enabled()) {
-            pr = m.mk_transitivity(pr, curr_pr);
-        }
-    } while (q != r && is_quantifier(r));
-
-    m_new_exprs.reset();
-}
-
-void der2::reduce_quantifier1(quantifier * q, expr_ref & r, proof_ref & pr) {    
-    expr * e = q->get_expr();
-    unsigned num_decls = q->get_num_decls();
-    var * v = 0;
-    expr_ref t(m);    
-    unsigned num_args = 1;
-    expr* const* args = &e;
-    if ((q->is_forall() && m.is_or(e)) ||
-        (q->is_exists() && m.is_and(e))) {
-        num_args = to_app(e)->get_num_args();
-        args     = to_app(e)->get_args();
-    }
-
-    unsigned def_count = 0;
-    unsigned largest_vinx = 0;
-    
-    m_map.reset();
-    m_pos2var.reset();
-    m_inx2var.reset();    
-    m_pos2var.reserve(num_args, -1);
-    
-    // Find all definitions
-    for (unsigned i = 0; i < num_args; i++) {
-        if (is_var_def(q->is_exists(), args[i], num_decls, v, t)) {
-            unsigned idx = v->get_idx();
-            if(m_map.get(idx, 0) == 0) {
-                m_map.reserve(idx + 1, 0);
-                m_inx2var.reserve(idx + 1, 0);                
-                m_map[idx] = t;
-                m_inx2var[idx] = v;
-                m_pos2var[i] = idx;
-                def_count++;
-                largest_vinx = std::max(idx, largest_vinx); 
-            }
-        }
-    }
-    
-    if (def_count > 0) {
-        get_elimination_order();
-        SASSERT(m_order.size() <= def_count); // some might be missing because of cycles
-        
-        if (!m_order.empty()) {            
-            create_substitution(largest_vinx + 1);
-            apply_substitution(q, r);
-        }
-        else {
-            r = q;
-        }
-    }
-    else {
-        TRACE("der_bug", tout << "Did not find any diseq\n" << mk_pp(q, m) << "\n";);
-        r = q;
-    }
- 
-    if (m.proofs_enabled()) {
-        pr = r == q ? 0 : m.mk_der(q, r);
-    }    
-}
 
 static void der_sort_vars(ptr_vector<var> & vars, ptr_vector<expr> & definitions, unsigned_vector & order) {
     order.reset();
     }
 }
 
-void der2::get_elimination_order() {
-    m_order.reset();
 
-    TRACE("top_sort",
-        tout << "DEFINITIONS: " << std::endl;
-        for(unsigned i = 0; i < m_map.size(); i++)
-            if(m_map[i]) tout << "VAR " << i << " = " << mk_pp(m_map[i], m) << std::endl;
-      );
+class der2 {
+    ast_manager &   m;
+    var_subst       m_subst;
+    expr_ref_buffer m_new_exprs;
+    
+    ptr_vector<expr> m_map;
+    int_vector       m_pos2var;
+    ptr_vector<var>  m_inx2var;
+    unsigned_vector  m_order;
+    expr_ref_vector  m_subst_map;
+    expr_ref_buffer  m_new_args;
+    th_rewriter      m_rewriter;
+    unsigned         m_num_decls;
+    uint_set         m_var_set;
+    enum is_var_kind { BY_VAR_SET, BY_VAR_SET_COMPLEMENT, BY_NUM_DECLS };
+    is_var_kind      m_var_kind;
+
+    bool is_variable(expr * e) const {
+        if (!is_var(e)) {
+            return false;
+        }
+        unsigned idx = to_var(e)->get_idx();
+        switch(m_var_kind) {
+        case BY_VAR_SET:
+            return m_var_set.contains(idx);
+        case BY_VAR_SET_COMPLEMENT:
+            return !m_var_set.contains(idx);
+        case BY_NUM_DECLS:
+            return idx < m_num_decls;
+        }
+        UNREACHABLE();
+        return false;
+    }
+    
+    bool is_neg_var(ast_manager & m, expr * e) {
+        expr* e1;
+        return m.is_not(e, e1) && is_variable(e1);
+    }
+
+
+    /**
+       \brief Return true if e can be viewed as a variable disequality. 
+       Store the variable id in v and the definition in t.
+       For example:
+
+          if e is (not (= (VAR 1) T)), then v assigned to 1, and t to T.
+          if e is (iff (VAR 2) T), then v is assigned to 2, and t to (not T).
+              (not T) is used because this formula is equivalent to (not (iff (VAR 2) (not T))),
+              and can be viewed as a disequality.
+    */
+    bool is_var_diseq(expr * e, var * & v, expr_ref & t) {
+        expr* e1;
+        if (m.is_not(e, e1)) {
+            return is_var_eq(e, v, t);
+        }
+        else if (is_var_eq(e, v, t) && m.is_bool(v)) { 
+            bool_rewriter(m).mk_not(t, t);
+            m_new_exprs.push_back(t);
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+
 
-    // der2::top_sort ts(m);
-    der_sort_vars(m_inx2var, m_map, m_order);
+    /**
+       \brief Return true if e can be viewed as a variable equality.
+    */
 
-    TRACE("der", 
-            tout << "Elimination m_order:" << std::endl;
-            for(unsigned i=0; i<m_order.size(); i++)
-            {
-                if (i != 0) tout << ",";
-                tout << m_order[i];
+    bool is_var_eq(expr * e, var * & v, expr_ref & t) {
+        expr* lhs, *rhs;
+        
+        // (= VAR t), (iff VAR t), (iff (not VAR) t), (iff t (not VAR)) cases    
+        if (m.is_eq(e, lhs, rhs) || m.is_iff(e, lhs, rhs)) {
+            // (iff (not VAR) t) (iff t (not VAR)) cases
+            if (!is_variable(lhs) && !is_variable(rhs) && m.is_bool(lhs)) {
+                if (!is_neg_var(m, lhs)) {
+                    std::swap(lhs, rhs);
+                }
+                if (!is_neg_var(m, lhs)) {
+                    return false;
+                }
+                v = to_var(lhs);
+                t = m.mk_not(rhs);
+                m_new_exprs.push_back(t);
+                TRACE("der", tout << mk_pp(e, m) << "\n";);
+                return true;
             }
-            tout << std::endl;            
-          );
-}
+            if (!is_variable(lhs))
+                std::swap(lhs, rhs);
+            if (!is_variable(lhs))
+                return false;
+            v = to_var(lhs);
+            t = rhs;
+            TRACE("der", tout << mk_pp(e, m) << "\n";);
+            return true;
+        }
+        
+        // (ite cond (= VAR t) (= VAR t2)) case
+        expr* cond, *e2, *e3;
+        if (m.is_ite(e, cond, e2, e3)) {
+            if (is_var_eq(e2, v, t)) {
+                expr_ref t2(m);
+                var* v2;
+                if (is_var_eq(e3, v2, t2) && v2 == v) {
+                    t = m.mk_ite(cond, t, t2);
+                    m_new_exprs.push_back(t);
+                    return true;
+                }
+            }
+            return false;
+        }
+        
+        // VAR = true case
+        if (is_variable(e)) {
+            t = m.mk_true();
+            v = to_var(e);
+            TRACE("der", tout << mk_pp(e, m) << "\n";);
+            return true;
+        }
+        
+        // VAR = false case
+        if (is_neg_var(m, e)) {
+            t = m.mk_false();
+            v = to_var(to_app(e)->get_arg(0));
+            TRACE("der", tout << mk_pp(e, m) << "\n";);
+            return true;
+        }
+        
+        return false;
+    }
 
-void der2::create_substitution(unsigned sz) {
-    m_subst_map.reset();
-    m_subst_map.resize(sz, 0);
 
-    for(unsigned i = 0; i < m_order.size(); i++) {
-        expr_ref cur(m_map[m_order[i]], m);
+    bool is_var_def(bool check_eq, expr* e, var*& v, expr_ref& t) {
+        if (check_eq) {
+            return is_var_eq(e, v, t);
+        }
+        else {
+            return is_var_diseq(e, v, t);
+        }    
+    }
+
+    void get_elimination_order() {
+        m_order.reset();
 
-        // do all the previous substitutions before inserting
-        expr_ref r(m);
-        m_subst(cur, m_subst_map.size(), m_subst_map.c_ptr(), r);
+        TRACE("top_sort",
+              tout << "DEFINITIONS: " << std::endl;
+              for(unsigned i = 0; i < m_map.size(); i++)
+                  if(m_map[i]) tout << "VAR " << i << " = " << mk_pp(m_map[i], m) << std::endl;
+              );
+        
+        der_sort_vars(m_inx2var, m_map, m_order);
+        
+        TRACE("der", 
+              tout << "Elimination m_order:" << std::endl;
+              for(unsigned i=0; i<m_order.size(); i++)
+                  {
+                      if (i != 0) tout << ",";
+                      tout << m_order[i];
+                  }
+              tout << std::endl;            
+              );
+    }    
 
-        unsigned inx = sz - m_order[i]- 1;
-        SASSERT(m_subst_map[inx]==0);
-        m_subst_map[inx] = r;
+    void create_substitution(unsigned sz) {
+        m_subst_map.reset();
+        m_subst_map.resize(sz, 0);        
+        for (unsigned i = 0; i < m_order.size(); i++) {
+            expr_ref cur(m_map[m_order[i]], m);            
+            // do all the previous substitutions before inserting
+            expr_ref r(m);
+            m_subst(cur, m_subst_map.size(), m_subst_map.c_ptr(), r);            
+            unsigned inx = sz - m_order[i]- 1;
+            SASSERT(m_subst_map[inx]==0);
+            m_subst_map[inx] = r;
+        }
     }
-}
 
-void der2::apply_substitution(quantifier * q, expr_ref & r) {
-    expr * e = q->get_expr();
-    unsigned num_args=to_app(e)->get_num_args(); 
-    bool_rewriter rw(m);
-    
-    // get a new expression
-    m_new_args.reset();
-    for(unsigned i = 0; i < num_args; i++) {
-        int x = m_pos2var[i];
-        if (x != -1 && m_map[x] != 0) 
-            continue; // this is a disequality with definition (vanishes)
-                
-        m_new_args.push_back(to_app(e)->get_arg(i));
+    void apply_substitution(quantifier * q, expr_ref & r) {
+        expr * e = q->get_expr();
+        unsigned num_args = to_app(e)->get_num_args(); 
+        bool_rewriter rw(m);
+        
+        // get a new expression
+        m_new_args.reset();
+        for(unsigned i = 0; i < num_args; i++) {
+            int x = m_pos2var[i];
+            if (x != -1 && m_map[x] != 0) 
+                continue; // this is a disequality/equality with definition (vanishes)
+            
+            m_new_args.push_back(to_app(e)->get_arg(i));
+        }
+        
+        expr_ref t(m);
+        if (q->is_forall()) {
+            rw.mk_or(m_new_args.size(), m_new_args.c_ptr(), t);
+        }
+        else {
+            rw.mk_and(m_new_args.size(), m_new_args.c_ptr(), t);
+        }
+        expr_ref new_e(m);    
+        m_subst(t, m_subst_map.size(), m_subst_map.c_ptr(), new_e);
+        
+        // don't forget to update the quantifier patterns
+        expr_ref_buffer  new_patterns(m);
+        expr_ref_buffer  new_no_patterns(m);
+        for (unsigned j = 0; j < q->get_num_patterns(); j++) {
+            expr_ref new_pat(m);
+            m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_pat);
+            new_patterns.push_back(new_pat);
+        }
+        
+        for (unsigned j = 0; j < q->get_num_no_patterns(); j++) {
+            expr_ref new_nopat(m);
+            m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_nopat);
+            new_no_patterns.push_back(new_nopat);
+        }
+        
+        r = m.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), 
+                                new_no_patterns.size(), new_no_patterns.c_ptr(), new_e);
     }
 
-    expr_ref t(m);
-    if (q->is_forall()) {
-        rw.mk_or(m_new_args.size(), m_new_args.c_ptr(), t);
+    void reduce_quantifier1(quantifier * q, expr_ref & r, proof_ref & pr) {
+        expr * e = q->get_expr();
+        m_num_decls = q->get_num_decls();
+        m_var_kind = BY_NUM_DECLS;
+        unsigned num_args = 1;
+        expr* const* args = &e;
+        if ((q->is_forall() && m.is_or(e)) ||
+            (q->is_exists() && m.is_and(e))) {
+            num_args = to_app(e)->get_num_args();
+            args     = to_app(e)->get_args();
+        }
+        
+        unsigned def_count = 0;
+        unsigned largest_vinx = 0;
+        
+        find_definitions(num_args, args, q->is_exists(), def_count, largest_vinx);
+        
+        if (def_count > 0) {
+            get_elimination_order();
+            SASSERT(m_order.size() <= def_count); // some might be missing because of cycles
+            
+            if (!m_order.empty()) {            
+                create_substitution(largest_vinx + 1);
+                apply_substitution(q, r);
+            }
+            else {
+                r = q;
+            }
+        }
+        else {
+            TRACE("der_bug", tout << "Did not find any diseq\n" << mk_pp(q, m) << "\n";);
+            r = q;
+        }
+        
+        if (m.proofs_enabled()) {
+            pr = r == q ? 0 : m.mk_der(q, r);
+        }    
+    }    
+
+    void elim_unused_vars(expr_ref& r, proof_ref &pr) {
+        if (is_quantifier(r)) {
+            quantifier * q = to_quantifier(r);
+            ::elim_unused_vars(m, q, r);
+            if (m.proofs_enabled()) {
+                proof * p1 = m.mk_elim_unused_vars(q, r);
+                pr = m.mk_transitivity(pr, p1);
+            }
+        }
     }
-    else {
-        rw.mk_and(m_new_args.size(), m_new_args.c_ptr(), t);
+
+    void find_definitions(unsigned num_args, expr* const* args, bool is_exists, unsigned& def_count, unsigned& largest_vinx) {
+        var * v = 0;
+        expr_ref t(m);    
+        def_count = 0;
+        largest_vinx = 0;
+        m_map.reset();
+        m_pos2var.reset();
+        m_inx2var.reset();    
+        m_pos2var.reserve(num_args, -1);
+        
+        // Find all definitions
+        for (unsigned i = 0; i < num_args; i++) {
+            if (is_var_def(is_exists, args[i], v, t)) {
+                unsigned idx = v->get_idx();
+                if(m_map.get(idx, 0) == 0) {
+                    m_map.reserve(idx + 1, 0);
+                    m_inx2var.reserve(idx + 1, 0);                
+                    m_map[idx] = t;
+                    m_inx2var[idx] = v;
+                    m_pos2var[i] = idx;
+                    def_count++;
+                    largest_vinx = std::max(idx, largest_vinx); 
+                }
+            }
+        }
     }
-    expr_ref new_e(m);    
-    m_subst(t, m_subst_map.size(), m_subst_map.c_ptr(), new_e);
-    
-    // don't forget to update the quantifier patterns
-    expr_ref_buffer  new_patterns(m);
-    expr_ref_buffer  new_no_patterns(m);
-    for (unsigned j = 0; j < q->get_num_patterns(); j++) {
-        expr_ref new_pat(m);
-        m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_pat);
-        new_patterns.push_back(new_pat);
+
+    void reduce_var_set(expr_ref& r) {
+        expr_ref_vector conjs(m);
+        unsigned def_count = 0;
+        unsigned largest_vinx = 0;
+        datalog::flatten_and(r, conjs);
+        
+        find_definitions(conjs.size(), conjs.c_ptr(), true, def_count, largest_vinx);
+        
+        if (def_count > 0) {
+            get_elimination_order();
+            SASSERT(m_order.size() <= def_count); // some might be missing because of cycles
+            
+            if (!m_order.empty()) {            
+                expr_ref new_r(m);
+                create_substitution(largest_vinx + 1);
+                m_subst(r, m_subst_map.size(), m_subst_map.c_ptr(), new_r);
+                m_rewriter(new_r);
+                r = new_r;
+            }
+        }
     }
 
-    for (unsigned j = 0; j < q->get_num_no_patterns(); j++) {
-        expr_ref new_nopat(m);
-        m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_nopat);
-        new_no_patterns.push_back(new_nopat);
+
+public:
+    der2(ast_manager & m): m(m), m_subst(m), m_new_exprs(m), m_subst_map(m), m_new_args(m), m_rewriter(m) {}
+
+    void operator()(quantifier * q, expr_ref & r, proof_ref & pr) {
+        TRACE("der", tout << mk_pp(q, m) << "\n";);    
+        pr = 0;
+        r  = q;
+        reduce_quantifier(q, r, pr);    
+        if (r != q) {
+            elim_unused_vars(r, pr);
+        }
     }
 
-    r = m.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), 
-                            new_no_patterns.size(), new_no_patterns.c_ptr(), new_e);
-}
+    void reduce_quantifier(quantifier * q, expr_ref & r, proof_ref & pr) {   
+        r = q;
+        // Keep applying reduce_quantifier1 until r doesn't change anymore
+        do {
+            proof_ref curr_pr(m);
+            q  = to_quantifier(r);
+            reduce_quantifier1(q, r, curr_pr);
+            if (m.proofs_enabled()) {
+                pr = m.mk_transitivity(pr, curr_pr);
+            }
+        } while (q != r && is_quantifier(r));
+        
+        m_new_exprs.reset();
+    }
 
+    // Keep applying reduce_var-set until r doesn't change anymore
+    void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& r) {
+        m_var_kind = index_of_bound?BY_VAR_SET:BY_VAR_SET_COMPLEMENT;
+        m_var_set = index_set;
+        expr_ref prev(m);
+        do {            
+            prev = r;
+            reduce_var_set(r);
+        } while (prev != r);        
+        m_new_exprs.reset();
+    }
 
+    void operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& r) {
+        expr_ref fml(m);
+        fml = m.mk_and(r.size(), r.c_ptr());
+        (*this)(index_set, index_of_bound, fml);
+        r.reset();
+        datalog::flatten_and(fml, r);
+    }
 
+    ast_manager& get_manager() const { return m; }
+};
 
+// ------------------------------------------------------------
+// fm_tactic adapted to eliminate designated de-Brujin indices.
 
-class qe_lite::impl {
-    ast_manager& m;
-    der2         m_der;
+typedef ptr_vector<app> clauses;
+//typedef unsigned        var;
+typedef int             bvar;
+typedef int             literal;
+typedef svector<var>    var_vector;
+
+// Encode the constraint
+// lits \/ ( as[0]*xs[0] + ... + as[num_vars-1]*xs[num_vars-1] <= c
+// if strict is true, then <= is <.
+struct constraint {
+    static unsigned get_obj_size(unsigned num_lits, unsigned num_vars) {
+        return sizeof(constraint) + num_lits*sizeof(literal) + num_vars*(sizeof(var) + sizeof(rational));
+    }
+    unsigned           m_id;
+    unsigned           m_num_lits:29;
+    unsigned           m_strict:1;
+    unsigned           m_dead:1;
+    unsigned           m_mark:1;
+    unsigned           m_num_vars;
+    literal *          m_lits;
+    var *              m_xs;
+    rational  *        m_as;
+    rational           m_c;
+    expr_dependency *  m_dep;
+    ~constraint() {
+        rational * it  = m_as;
+        rational * end = it + m_num_vars;
+        for (; it != end; ++it)
+            it->~rational();
+    }
+    
+    unsigned hash() const { return hash_u(m_id); }
+};
 
+typedef ptr_vector<constraint> constraints;
+
+class constraint_set {
+    unsigned_vector m_id2pos; 
+    constraints     m_set;
 public:
-    impl(ast_manager& m): m(m), m_der(m) {}
+    typedef constraints::const_iterator iterator;
     
-    void operator()(app_ref_vector& vars, expr_ref& fml) {
-        if (vars.empty()) {
+    bool contains(constraint const & c) const { 
+        if (c.m_id >= m_id2pos.size()) 
+            return false; 
+        return m_id2pos[c.m_id] != UINT_MAX; 
+    }
+    
+    bool empty() const { return m_set.empty(); }
+    unsigned size() const { return m_set.size(); }
+    
+    void insert(constraint & c) {
+        unsigned id  = c.m_id;
+        m_id2pos.reserve(id+1, UINT_MAX);
+        if (m_id2pos[id] != UINT_MAX)
+            return; // already in the set
+        unsigned pos = m_set.size();
+        m_id2pos[id] = pos;
+        m_set.push_back(&c);
+    }
+    
+    void erase(constraint & c) {
+        unsigned id = c.m_id;
+        if (id >= m_id2pos.size())
+            return;
+        unsigned pos = m_id2pos[id];
+        if (pos == UINT_MAX)
+            return;
+        m_id2pos[id] = UINT_MAX;
+        unsigned last_pos = m_set.size() - 1;
+        if (pos != last_pos) {
+            constraint * last_c = m_set[last_pos];
+            m_set[pos] = last_c; 
+            m_id2pos[last_c->m_id] = pos;
+        }
+        m_set.pop_back();
+    }
+        
+    constraint & erase() {
+        SASSERT(!empty());
+        constraint & c = *m_set.back(); 
+        m_id2pos[c.m_id] = UINT_MAX;
+        m_set.pop_back();
+        return c;
+    }
+    
+    void reset() { m_id2pos.reset(); m_set.reset(); }
+    void finalize() { m_id2pos.finalize(); m_set.finalize(); }
+    
+    iterator begin() const { return m_set.begin(); }
+    iterator end() const { return m_set.end(); }
+};
+
+
+#if 0
+
+
+    
+    struct imp {
+        ast_manager &            m;
+        small_object_allocator   m_allocator;
+        arith_util               m_util;
+        constraints              m_constraints;
+        expr_ref_vector          m_bvar2expr;
+        char_vector              m_bvar2sign;
+        obj_map<expr, bvar>      m_expr2bvar;
+        char_vector              m_is_int;
+        char_vector              m_forbidden;
+        expr_ref_vector          m_var2expr;
+        obj_map<expr, var>       m_expr2var;
+        unsigned_vector          m_var2pos;
+        vector<constraints>      m_lowers;
+        vector<constraints>      m_uppers;
+        obj_hashtable<func_decl> m_forbidden_set; // variables that cannot be eliminated because occur in non OCC ineq part
+        goal_ref                 m_new_goal;
+        ref<fm_model_converter>  m_mc;
+        volatile bool            m_cancel;
+        id_gen                   m_id_gen;
+        bool                     m_produce_models;
+        bool                     m_fm_real_only;
+        unsigned                 m_fm_limit;
+        unsigned                 m_fm_cutoff1;
+        unsigned                 m_fm_cutoff2;
+        unsigned                 m_fm_extra;
+        bool                     m_fm_occ;
+        unsigned long long       m_max_memory;
+        unsigned                 m_counter;
+        bool                     m_inconsistent;
+        expr_dependency_ref      m_inconsistent_core;
+        constraint_set           m_sub_todo;
+        
+        // ---------------------------
+        //
+        // OCC clause recognizer
+        //
+        // ---------------------------
+        
+        bool is_literal(expr * t) const {
+            expr * atom;
+            return is_uninterp_const(t) || (m.is_not(t, atom) && is_uninterp_const(atom));
+        }
+        
+        bool is_constraint(expr * t) const {
+            return !is_literal(t);
+        }
+        
+        bool is_var(expr * t, expr * & x) const {
+            if (is_uninterp_const(t)) {
+                x = t;
+                return true;
+            }
+            else if (m_util.is_to_real(t) && is_uninterp_const(to_app(t)->get_arg(0))) {
+                x = to_app(t)->get_arg(0);
+                return true;
+            }
+            return false;
+        }
+        
+        bool is_var(expr * t) const {
+            expr * x;
+            return is_var(t, x);
+        }
+        
+        bool is_linear_mon_core(expr * t, expr * & x) const {
+            expr * c;
+            if (m_util.is_mul(t, c, x) && m_util.is_numeral(c) && is_var(x, x))
+                return true;
+            return is_var(t, x);
+        }
+        
+        bool is_linear_mon(expr * t) const {
+            expr * x;
+            return is_linear_mon_core(t, x);
+        }
+        
+        bool is_linear_pol(expr * t) const {
+            unsigned       num_mons;
+            expr * const * mons;
+            if (m_util.is_add(t)) {
+                num_mons = to_app(t)->get_num_args();
+                mons     = to_app(t)->get_args();
+            }
+            else {
+                num_mons = 1;
+                mons     = &t;
+            }
+            
+            expr_fast_mark2 visited;
+            bool all_forbidden = true;
+            for (unsigned i = 0; i < num_mons; i++) {
+                expr * x;
+                if (!is_linear_mon_core(mons[i], x))
+                    return false;
+                if (visited.is_marked(x))
+                    return false; // duplicates are not supported... must simplify first
+                visited.mark(x);
+                if (!m_forbidden_set.contains(to_app(x)->get_decl()) && (!m_fm_real_only || !m_util.is_int(x)))
+                    all_forbidden = false;
+            }
+            return !all_forbidden;
+        }
+        
+        bool is_linear_ineq(expr * t) const {
+            m.is_not(t, t);
+            expr * lhs, * rhs;
+            TRACE("is_occ_bug", tout << mk_pp(t, m) << "\n";);
+            if (m_util.is_le(t, lhs, rhs) || m_util.is_ge(t, lhs, rhs)) {
+                if (!m_util.is_numeral(rhs))
+                    return false;
+                return is_linear_pol(lhs);
+            }
+            return false;
+        }
+        
+        bool is_occ(expr * t) {
+            if (m_fm_occ && m.is_or(t)) {
+                unsigned num = to_app(t)->get_num_args();
+                bool found = false;
+                for (unsigned i = 0; i < num; i++) {
+                    expr * l = to_app(t)->get_arg(i);
+                    if (is_literal(l)) {
+                        continue;
+                    }
+                    else if (is_linear_ineq(l)) {
+                        if (found)
+                            return false;
+                        found = true;
+                    }
+                    else {
+                        return false;
+                    }
+                }
+                return found;
+            }
+            return is_linear_ineq(t);
+        }
+        
+        // ---------------------------
+        //
+        // Memory mng
+        //
+        // ---------------------------
+        void del_constraint(constraint * c) {
+            m.dec_ref(c->m_dep);
+            m_sub_todo.erase(*c);
+            m_id_gen.recycle(c->m_id);
+            c->~constraint();
+            unsigned sz = constraint::get_obj_size(c->m_num_lits, c->m_num_vars);
+            m_allocator.deallocate(sz, c);
+        }
+
+        void del_constraints(unsigned sz, constraint * const * cs) {
+            for (unsigned i = 0; i < sz; i++)
+                del_constraint(cs[i]);
+        }
+        
+        void reset_constraints() {
+            del_constraints(m_constraints.size(), m_constraints.c_ptr());
+            m_constraints.reset();
+        }
+        
+        constraint * mk_constraint(unsigned num_lits, literal * lits, unsigned num_vars, var * xs, rational * as, rational & c, bool strict,
+                                   expr_dependency * dep) {
+            unsigned sz         = constraint::get_obj_size(num_lits, num_vars);
+            char * mem          = static_cast<char*>(m_allocator.allocate(sz));
+            char * mem_as       = mem + sizeof(constraint);
+            char * mem_lits     = mem_as + sizeof(rational)*num_vars;
+            char * mem_xs       = mem_lits + sizeof(literal)*num_lits;
+            constraint * cnstr  = new (mem) constraint();
+            cnstr->m_id         = m_id_gen.mk();
+            cnstr->m_num_lits   = num_lits;
+            cnstr->m_dead       = false;
+            cnstr->m_mark       = false;
+            cnstr->m_strict     = strict;
+            cnstr->m_num_vars   = num_vars;
+            cnstr->m_lits       = reinterpret_cast<literal*>(mem_lits);
+            for (unsigned i = 0; i < num_lits; i++)
+                cnstr->m_lits[i] = lits[i];
+            cnstr->m_xs         = reinterpret_cast<var*>(mem_xs);
+            cnstr->m_as         = reinterpret_cast<rational*>(mem_as);
+            for (unsigned i = 0; i < num_vars; i++) {
+                TRACE("mk_constraint_bug", tout << "xs[" << i << "]: " << xs[i] << "\n";);
+                cnstr->m_xs[i] = xs[i];
+                new (cnstr->m_as + i) rational(as[i]);
+            }
+            cnstr->m_c = c;
+            DEBUG_CODE({
+                for (unsigned i = 0; i < num_vars; i++) {
+                    SASSERT(cnstr->m_xs[i] == xs[i]);
+                    SASSERT(cnstr->m_as[i] == as[i]);
+                }
+            });
+            cnstr->m_dep = dep;
+            m.inc_ref(dep);
+            return cnstr;
+        }
+        
+        // ---------------------------
+        //
+        // Util
+        //
+        // ---------------------------
+        
+        unsigned num_vars() const { return m_is_int.size(); }
+        
+        // multiply as and c, by the lcm of their denominators
+        void mk_int(unsigned num, rational * as, rational & c) {
+            rational l = denominator(c);
+            for (unsigned i = 0; i < num; i++)
+                l = lcm(l, denominator(as[i]));
+            if (l.is_one())
+                return;
+            c *= l;
+            SASSERT(c.is_int());
+            for (unsigned i = 0; i < num; i++) {
+                as[i] *= l;
+                SASSERT(as[i].is_int());
+            }
+        }
+        
+        void normalize_coeffs(constraint & c) {
+            if (c.m_num_vars == 0)
+                return;
+            // compute gcd of all coefficients
+            rational g = c.m_c;
+            if (g.is_neg())
+                g.neg();
+            for (unsigned i = 0; i < c.m_num_vars; i++) {
+                if (g.is_one())
+                    break;
+                if (c.m_as[i].is_pos())
+                    g = gcd(c.m_as[i], g);
+                else
+                    g = gcd(-c.m_as[i], g);
+            }
+            if (g.is_one())
+                return;
+            c.m_c /= g;
+            for (unsigned i = 0; i < c.m_num_vars; i++)
+                c.m_as[i] /= g;
+        }
+        
+        void display(std::ostream & out, constraint const & c) const {
+            for (unsigned i = 0; i < c.m_num_lits; i++) {
+                literal l = c.m_lits[i];
+                if (sign(l))
+                    out << "~";
+                bvar p    = lit2bvar(l);
+                out << mk_ismt2_pp(m_bvar2expr[p], m);
+                out << " ";
+            }
+            out << "(";
+            if (c.m_num_vars == 0)
+                out << "0";
+            for (unsigned i = 0; i < c.m_num_vars; i++) {
+                if (i > 0)
+                    out << " + ";
+                if (!c.m_as[i].is_one())
+                    out << c.m_as[i] << "*";
+                out << mk_ismt2_pp(m_var2expr.get(c.m_xs[i]), m);
+            }
+            if (c.m_strict)
+                out << " < ";
+            else
+                out << " <= ";
+            out << c.m_c;
+            out << ")";
+        }
+        
+        /**
+           \brief Return true if c1 subsumes c2
+       
+           c1 subsumes c2 If
+           1) All literals of c1 are literals of c2
+           2) polynomial of c1 == polynomial of c2
+           3) c1.m_c <= c2.m_c
+        */
+        bool subsumes(constraint const & c1, constraint const & c2) {
+            if (&c1 == &c2)
+                return false;
+            // quick checks first
+            if (c1.m_num_lits > c2.m_num_lits)
+                return false;
+            if (c1.m_num_vars != c2.m_num_vars)
+                return false;
+            if (c1.m_c > c2.m_c)
+                return false;
+            if (!c1.m_strict && c2.m_strict && c1.m_c == c2.m_c)
+                return false;
+            
+            m_counter += c1.m_num_lits + c2.m_num_lits;
+            
+            for (unsigned i = 0; i < c1.m_num_vars; i++) {
+                m_var2pos[c1.m_xs[i]] = i;
+            }
+            
+            bool failed = false;
+            for (unsigned i = 0; i < c2.m_num_vars; i++) {
+                unsigned pos1 = m_var2pos[c2.m_xs[i]];
+                if (pos1 == UINT_MAX || c1.m_as[pos1] != c2.m_as[i]) {
+                    failed = true;
+                    break;
+                }
+            }
+            
+            for (unsigned i = 0; i < c1.m_num_vars; i++) {
+                m_var2pos[c1.m_xs[i]] = UINT_MAX;
+            }
+            
+            if (failed)
+                return false;
+            
+            for (unsigned i = 0; i < c2.m_num_lits; i++) {
+                literal l = c2.m_lits[i];
+                bvar b    = lit2bvar(l);
+                SASSERT(m_bvar2sign[b] == 0);
+                m_bvar2sign[b] = sign(l) ? -1 : 1;
+            }
+            
+            for (unsigned i = 0; i < c1.m_num_lits; i++) {
+                literal l = c1.m_lits[i];
+                bvar b    = lit2bvar(l);
+                char s    = sign(l) ? -1 : 1;
+                if (m_bvar2sign[b] != s) {
+                    failed = true;
+                    break;
+                }
+            }
+            
+            for (unsigned i = 0; i < c2.m_num_lits; i++) {
+                literal l = c2.m_lits[i];
+                bvar b    = lit2bvar(l);
+                m_bvar2sign[b] = 0;
+            }
+            
+            if (failed)
+                return false;
+            
+            return true;
+        }
+        
+        void backward_subsumption(constraint const & c) {
+            if (c.m_num_vars == 0)
+                return;
+            var      best       = UINT_MAX;
+            unsigned best_sz    = UINT_MAX;
+            bool     best_lower = false;
+            for (unsigned i = 0; i < c.m_num_vars; i++) {
+                var xi     = c.m_xs[i];
+                if (is_forbidden(xi))
+                    continue; // variable is not in the index
+                bool neg_a = c.m_as[i].is_neg();
+                constraints & cs = neg_a ? m_lowers[xi] : m_uppers[xi];
+                if (cs.size() < best_sz) {
+                    best       = xi;
+                    best_sz    = cs.size();
+                    best_lower = neg_a;
+                }
+            }
+            if (best_sz == 0)
+                return;
+            if (best == UINT_MAX)
+                return; // none of the c variables are in the index.
+            constraints & cs = best_lower ? m_lowers[best] : m_uppers[best];
+            m_counter += cs.size();
+            constraints::iterator it  = cs.begin();
+            constraints::iterator it2 = it;
+            constraints::iterator end = cs.end();
+            for (; it != end; ++it) {
+                constraint * c2 = *it;
+                if (c2->m_dead)
+                    continue;
+                if (subsumes(c, *c2)) {
+                    TRACE("fm_subsumption", display(tout, c); tout << "\nsubsumed:\n"; display(tout, *c2); tout << "\n";);
+                    c2->m_dead = true;
+                    continue;
+                }
+                *it2 = *it;
+                ++it2;
+            }
+            cs.set_end(it2);
+        }
+        
+        void subsume() {
+            while (!m_sub_todo.empty()) {
+                constraint & c = m_sub_todo.erase();
+                if (c.m_dead)
+                    continue;
+                backward_subsumption(c);
+            }
+        }
+        
+        // ---------------------------
+        //
+        // Initialization
+        //
+        // ---------------------------
+        
+        imp(ast_manager & _m, params_ref const & p):
+            m(_m),
+            m_allocator("fm-tactic"),
+            m_util(m),
+            m_bvar2expr(m),
+            m_var2expr(m),
+            m_inconsistent_core(m) {
+            updt_params(p);
+            m_cancel = false;
+        }
+        
+        ~imp() {
+            reset_constraints();
+        }
+        
+        void updt_params(params_ref const & p) {
+            m_max_memory     = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
+            m_fm_real_only   = p.get_bool(":fm-real-only", true);
+            m_fm_limit       = p.get_uint(":fm-limit", 5000000);
+            m_fm_cutoff1     = p.get_uint(":fm-cutoff1", 8);
+            m_fm_cutoff2     = p.get_uint(":fm-cutoff2", 256);
+            m_fm_extra       = p.get_uint(":fm-extra", 0);
+            m_fm_occ         = p.get_bool(":fm-occ", false);
+        }
+        
+        void set_cancel(bool f) {
+            m_cancel = f;
+        }
+        
+        struct forbidden_proc {
+            imp & m_owner;
+            forbidden_proc(imp & o):m_owner(o) {}
+            void operator()(::var * n) {}
+            void operator()(app * n) {
+                if (is_uninterp_const(n) && m_owner.m.get_sort(n)->get_family_id() == m_owner.m_util.get_family_id()) {
+                    m_owner.m_forbidden_set.insert(n->get_decl());
+                }
+            }
+            void operator()(quantifier * n) {}
+        };
+        
+        void init_forbidden_set(goal const & g) {
+            m_forbidden_set.reset();
+            expr_fast_mark1 visited;
+            forbidden_proc  proc(*this);
+            unsigned sz = g.size();
+            for (unsigned i = 0; i < sz; i++) {
+                expr * f = g.form(i);
+                if (is_occ(f))
+                    continue;
+                TRACE("is_occ_bug", tout << "not OCC:\n" << mk_ismt2_pp(f, m) << "\n";);
+                quick_for_each_expr(proc, visited, f);
+            }
+        }
+        
+        void init(goal const & g) {
+            m_sub_todo.reset();
+            m_id_gen.reset();
+            reset_constraints();
+            m_bvar2expr.reset();
+            m_bvar2sign.reset();
+            m_bvar2expr.push_back(0); // bvar 0 is not used
+            m_bvar2sign.push_back(0);
+            m_expr2var.reset();
+            m_is_int.reset();
+            m_var2pos.reset();
+            m_forbidden.reset();
+            m_var2expr.reset();
+            m_expr2var.reset();
+            m_lowers.reset();
+            m_uppers.reset();
+            m_new_goal = 0;
+            m_mc = 0;
+            m_counter = 0;
+            m_inconsistent = false;
+            m_inconsistent_core = 0;
+            init_forbidden_set(g);
+        }
+        
+        // ---------------------------
+        //
+        // Internal data-structures
+        //
+        // ---------------------------
+        
+        static bool sign(literal l) { return l < 0; }
+        static bvar lit2bvar(literal l) { return l < 0 ? -l : l; }
+        
+        bool is_int(var x) const { 
+            return m_is_int[x] != 0;
+        }
+        
+        bool is_forbidden(var x) const {
+            return m_forbidden[x] != 0;
+        }
+        
+        bool all_int(constraint const & c) const {
+            for (unsigned i = 0; i < c.m_num_vars; i++) {
+                if (!is_int(c.m_xs[i]))
+                    return false;
+            }
+            return true;
+        }
+        
+        app * to_expr(constraint const & c) {
+            expr * ineq;
+            if (c.m_num_vars == 0) {
+                // 0 <  k (for k > 0)  --> true
+                // 0 <= 0 -- > true
+                if (c.m_c.is_pos() || (!c.m_strict && c.m_c.is_zero()))
+                    return m.mk_true();
+                ineq = 0;
+            }
+            else {
+                bool int_cnstr = all_int(c);
+                ptr_buffer<expr> ms;
+                for (unsigned i = 0; i < c.m_num_vars; i++) {
+                    expr * x = m_var2expr.get(c.m_xs[i]);
+                    if (!int_cnstr && is_int(c.m_xs[i]))
+                        x = m_util.mk_to_real(x);
+                    if (c.m_as[i].is_one())
+                        ms.push_back(x);
+                    else
+                        ms.push_back(m_util.mk_mul(m_util.mk_numeral(c.m_as[i], int_cnstr), x));
+                }
+                expr * lhs;
+                if (c.m_num_vars == 1)
+                    lhs = ms[0];
+                else
+                    lhs = m_util.mk_add(ms.size(), ms.c_ptr());
+                expr * rhs = m_util.mk_numeral(c.m_c, int_cnstr);
+                if (c.m_strict) {
+                    ineq = m.mk_not(m_util.mk_ge(lhs, rhs));
+                }
+                else {
+                    ineq = m_util.mk_le(lhs, rhs);
+                }
+            }
+            
+            if (c.m_num_lits == 0) {
+                if (ineq)
+                    return to_app(ineq);
+                else
+                    return m.mk_false();
+            }
+            
+            ptr_buffer<expr> lits;
+            for (unsigned i = 0; i < c.m_num_lits; i++) {
+                literal l = c.m_lits[i];
+                if (sign(l))
+                    lits.push_back(m.mk_not(m_bvar2expr.get(lit2bvar(l))));
+                else 
+                    lits.push_back(m_bvar2expr.get(lit2bvar(l)));
+            }
+            if (ineq)
+                lits.push_back(ineq);
+            if (lits.size() == 1)
+                return to_app(lits[0]);
+            else
+                return m.mk_or(lits.size(), lits.c_ptr());
+        }
+        
+        var mk_var(expr * t) {
+            SASSERT(is_uninterp_const(t));
+            SASSERT(m_util.is_int(t) || m_util.is_real(t));
+            var x = m_var2expr.size();
+            m_var2expr.push_back(t);
+            bool is_int = m_util.is_int(t);
+            m_is_int.push_back(is_int);
+            m_var2pos.push_back(UINT_MAX);
+            m_expr2var.insert(t, x);
+            m_lowers.push_back(constraints());
+            m_uppers.push_back(constraints());
+            bool forbidden = m_forbidden_set.contains(to_app(t)->get_decl()) || (m_fm_real_only && is_int);
+            m_forbidden.push_back(forbidden);
+            SASSERT(m_var2expr.size()  == m_is_int.size());
+            SASSERT(m_lowers.size()    == m_is_int.size());
+            SASSERT(m_uppers.size()    == m_is_int.size());
+            SASSERT(m_forbidden.size() == m_is_int.size()); 
+            SASSERT(m_var2pos.size()   == m_is_int.size());
+            return x;
+        }
+        
+        bvar mk_bvar(expr * t) {
+            SASSERT(is_uninterp_const(t));
+            SASSERT(m.is_bool(t));
+            bvar p = m_bvar2expr.size();
+            m_bvar2expr.push_back(t);
+            m_bvar2sign.push_back(0);
+            SASSERT(m_bvar2expr.size() == m_bvar2sign.size());
+            m_expr2bvar.insert(t, p);
+            SASSERT(p > 0);
+            return p;
+        }
+        
+        var to_var(expr * t) {
+            var x;
+            if (!m_expr2var.find(t, x))
+                x = mk_var(t);
+            SASSERT(m_expr2var.contains(t));
+            SASSERT(m_var2expr.get(x) == t);
+            TRACE("to_var_bug", tout << mk_ismt2_pp(t, m) << " --> " << x << "\n";);
+            return x;
+        }
+        
+        bvar to_bvar(expr * t) {
+            bvar p;
+            if (m_expr2bvar.find(t, p))
+                return p;
+            return mk_bvar(t);
+        }
+        
+        literal to_literal(expr * t) {
+            if (m.is_not(t, t))
+                return -to_bvar(t); 
+            else
+                return to_bvar(t);
+        }
+        
+        
+        void add_constraint(expr * f, expr_dependency * dep) {
+            SASSERT(!m.is_or(f) || m_fm_occ);
+            sbuffer<literal> lits;
+            sbuffer<var>     xs;
+            buffer<rational> as;
+            rational         c;
+            bool             strict;
+            unsigned         num;
+            expr * const *   args;
+            if (m.is_or(f)) {
+                num  = to_app(f)->get_num_args();
+                args = to_app(f)->get_args();
+            }
+            else {
+                num  = 1;
+                args = &f;
+            }
+
+#if Z3DEBUG
+            bool found_ineq = false;
+#endif
+            for (unsigned i = 0; i < num; i++) {
+                expr * l = args[i];
+                if (is_literal(l)) {
+                    lits.push_back(to_literal(l));
+                }
+                else {
+                    // found inequality
+                    SASSERT(!found_ineq);
+                    DEBUG_CODE(found_ineq = true;);
+                    bool neg    = m.is_not(l, l);
+                    SASSERT(m_util.is_le(l) || m_util.is_ge(l));
+                    strict      = neg;
+                    if (m_util.is_ge(l))
+                        neg = !neg;
+                    expr * lhs = to_app(l)->get_arg(0);
+                    expr * rhs = to_app(l)->get_arg(1);
+                    m_util.is_numeral(rhs, c);
+                    if (neg)
+                        c.neg();
+                    unsigned num_mons;
+                    expr * const * mons;
+                    if (m_util.is_add(lhs)) {
+                        num_mons = to_app(lhs)->get_num_args();
+                        mons     = to_app(lhs)->get_args();
+                    }
+                    else {
+                        num_mons = 1;
+                        mons     = &lhs;
+                    }
+                    
+                    bool all_int = true;
+                    for (unsigned j = 0; j < num_mons; j++) {
+                        expr * monomial = mons[j];
+                        expr * a;
+                        rational a_val;
+                        expr * x;
+                        if (m_util.is_mul(monomial, a, x)) {
+                            VERIFY(m_util.is_numeral(a, a_val));
+                        }
+                        else {
+                            x     = monomial;
+                            a_val = rational(1);
+                        }
+                        if (neg)
+                            a_val.neg();
+                        VERIFY(is_var(x, x));
+                        xs.push_back(to_var(x));
+                        as.push_back(a_val);
+                        if (!is_int(xs.back()))
+                            all_int = false;
+                    }
+                    mk_int(as.size(), as.c_ptr(), c);
+                    if (all_int && strict) {
+                        strict = false;
+                        c--;
+                    }
+                }
+            }
+            
+            TRACE("to_var_bug", tout << "before mk_constraint: "; for (unsigned i = 0; i < xs.size(); i++) tout << " " << xs[i]; tout << "\n";);
+            
+            constraint * new_c = mk_constraint(lits.size(),
+                                               lits.c_ptr(),
+                                               xs.size(),
+                                               xs.c_ptr(),
+                                               as.c_ptr(),
+                                               c,
+                                               strict,
+                                               dep);
+            
+            TRACE("to_var_bug", tout << "add_constraint: "; display(tout, *new_c); tout << "\n";);
+            VERIFY(register_constraint(new_c));
+        }
+        
+        bool is_false(constraint const & c) const {
+            return c.m_num_lits == 0 && c.m_num_vars == 0 && (c.m_c.is_neg() || (c.m_strict && c.m_c.is_zero()));
+        }
+        
+        bool register_constraint(constraint * c) {
+            normalize_coeffs(*c);
+            if (is_false(*c)) {
+                del_constraint(c);
+                m_inconsistent = true;
+                TRACE("add_constraint_bug", tout << "is false "; display(tout, *c); tout << "\n";);
+                return false;
+            }
+            
+            bool r = false;
+            
+            for (unsigned i = 0; i < c->m_num_vars; i++) {
+                var x = c->m_xs[i];
+                if (!is_forbidden(x)) {
+                    r = true;
+                    if (c->m_as[i].is_neg()) 
+                        m_lowers[x].push_back(c);
+                    else
+                        m_uppers[x].push_back(c);
+                }
+            }
+            
+            if (r) {
+                m_sub_todo.insert(*c);
+                m_constraints.push_back(c);
+                return true;
+            }
+            else {
+                TRACE("add_constraint_bug", tout << "all variables are forbidden "; display(tout, *c); tout << "\n";);
+                m_new_goal->assert_expr(to_expr(*c), 0, c->m_dep);
+                del_constraint(c);
+                return false;
+            }
+        }
+        
+        void init_use_list(goal const & g) {
+            unsigned sz = g.size();
+            for (unsigned i = 0; i < sz; i++) {
+                if (m_inconsistent)
+                    return;
+                expr * f = g.form(i);
+                if (is_occ(f))
+                    add_constraint(f, g.dep(i));
+                else
+                    m_new_goal->assert_expr(f, 0, g.dep(i));
+            }
+        }
+
+        unsigned get_cost(var x) const {
+            unsigned long long r = static_cast<unsigned long long>(m_lowers[x].size()) * static_cast<unsigned long long>(m_uppers[x].size());
+            if (r > UINT_MAX)
+                return UINT_MAX;
+            return static_cast<unsigned>(r);
+        }
+        
+        typedef std::pair<var, unsigned> x_cost;
+    
+        struct x_cost_lt {
+            char_vector const m_is_int;
+            x_cost_lt(char_vector & is_int):m_is_int(is_int) {}
+            bool operator()(x_cost const & p1, x_cost const & p2) const { 
+                // Integer variables with cost 0 can be eliminated even if they depend on real variables.
+                // Cost 0 == no lower or no upper bound.
+                if (p1.second == 0) {
+                    if (p2.second > 0) return true;
+                    return p1.first < p2.first;
+                }
+                if (p2.second == 0) return false;
+                bool int1 = m_is_int[p1.first] != 0;
+                bool int2 = m_is_int[p2.first] != 0;
+                return (!int1 && int2) || (int1 == int2 && p1.second < p2.second); 
+            }
+        };
+
+        void sort_candidates(var_vector & xs) {
+            svector<x_cost> x_cost_vector;
+            unsigned num = num_vars();
+            for (var x = 0; x < num; x++) {
+                if (!is_forbidden(x)) {
+                    x_cost_vector.push_back(x_cost(x, get_cost(x)));
+                }
+            }
+            // x_cost_lt is not a total order on variables
+            std::stable_sort(x_cost_vector.begin(), x_cost_vector.end(), x_cost_lt(m_is_int));
+            TRACE("fm", 
+                  svector<x_cost>::iterator it2  = x_cost_vector.begin();
+                  svector<x_cost>::iterator end2 = x_cost_vector.end();
+                  for (; it2 != end2; ++it2) {
+                      tout << "(" << mk_ismt2_pp(m_var2expr.get(it2->first), m) << " " << it2->second << ") ";
+                  }
+                  tout << "\n";);
+            svector<x_cost>::iterator it2  = x_cost_vector.begin();
+            svector<x_cost>::iterator end2 = x_cost_vector.end();
+            for (; it2 != end2; ++it2) {
+                xs.push_back(it2->first);
+            }
+        }
+        
+        void cleanup_constraints(constraints & cs) {
+            unsigned j = 0;
+            unsigned sz = cs.size();
+            for (unsigned i = 0; i < sz; i++) {
+                constraint * c = cs[i];
+                if (c->m_dead)
+                    continue;
+                cs[j] = c;
+                j++;
+            }
+            cs.shrink(j);
+        }
+    
+        // Set all_int = true if all variables in c are int.
+        // Set unit_coeff = true if the coefficient of x in c is 1 or -1.
+        // If all_int = false, then unit_coeff may not be set.
+        void analyze(constraint const & c, var x, bool & all_int, bool & unit_coeff) const {
+            all_int    = true;
+            unit_coeff = true;
+            for (unsigned i = 0; i < c.m_num_vars; i++) {
+                if (!is_int(c.m_xs[i])) {
+                    all_int = false;
+                    return;
+                }
+                if (c.m_xs[i] == x) {
+                    unit_coeff = (c.m_as[i].is_one() || c.m_as[i].is_minus_one());
+                }
+            }
+        }
+
+        void analyze(constraints const & cs, var x, bool & all_int, bool & unit_coeff) const {
+            all_int    = true;
+            unit_coeff = true;
+            constraints::const_iterator it  = cs.begin();
+            constraints::const_iterator end = cs.end();
+            for (; it != end; ++it) {
+                bool curr_unit_coeff;
+                analyze(*(*it), x, all_int, curr_unit_coeff);
+                if (!all_int)
+                    return;
+                if (!curr_unit_coeff)
+                    unit_coeff = false;
+            }
+        }
+        
+        // An integer variable x may be eliminated, if 
+        //   1- All variables in the contraints it occur are integer.
+        //   2- The coefficient of x in all lower bounds (or all upper bounds) is unit.
+        bool can_eliminate(var x) const {
+            if (!is_int(x))
+                return true;
+            bool all_int;
+            bool l_unit, u_unit;
+            analyze(m_lowers[x], x, all_int, l_unit);
+            if (!all_int)
+                return false;
+            analyze(m_uppers[x], x, all_int, u_unit);
+            return all_int && (l_unit || u_unit);
+        }
+        
+        void copy_constraints(constraints const & s, clauses & t) {
+            constraints::const_iterator it  = s.begin();
+            constraints::const_iterator end = s.end();
+            for (; it != end; ++it) {
+                app * c = to_expr(*(*it));
+                t.push_back(c);
+            }
+        }
+        
+        clauses tmp_clauses;
+        void save_constraints(var x) {
+            if (m_produce_models) {
+                tmp_clauses.reset();
+                copy_constraints(m_lowers[x], tmp_clauses);
+                copy_constraints(m_uppers[x], tmp_clauses);
+                m_mc->insert(to_app(m_var2expr.get(x))->get_decl(), tmp_clauses);
+            }
+        }
+        
+        void mark_constraints_dead(constraints const & cs) {
+            constraints::const_iterator it  = cs.begin();
+            constraints::const_iterator end = cs.end();
+            for (; it != end; ++it)
+                (*it)->m_dead = true;
+        }
+        
+        void mark_constraints_dead(var x) {
+            save_constraints(x);
+            mark_constraints_dead(m_lowers[x]);
+            mark_constraints_dead(m_uppers[x]);
+        }
+        
+        void get_coeff(constraint const & c, var x, rational & a) {
+            for (unsigned i = 0; i < c.m_num_vars; i++) {
+                if (c.m_xs[i] == x) {
+                    a = c.m_as[i];
+                    return;
+                }
+            }
+            UNREACHABLE();
+        }
+        
+        var_vector       new_xs;
+        vector<rational> new_as;
+        svector<literal> new_lits;
+        
+        constraint * resolve(constraint const & l, constraint const & u, var x) {
+            m_counter += l.m_num_vars + u.m_num_vars + l.m_num_lits + u.m_num_lits;
+            rational a, b;
+            get_coeff(l, x, a);
+            get_coeff(u, x, b);
+            SASSERT(a.is_neg());
+            SASSERT(b.is_pos());
+            a.neg();
+            
+            SASSERT(!is_int(x) || a.is_one() || b.is_one());
+            
+            new_xs.reset();
+            new_as.reset();
+            rational         new_c = l.m_c*b + u.m_c*a;
+            bool             new_strict = l.m_strict || u.m_strict;
+            
+            for (unsigned i = 0; i < l.m_num_vars; i++) {
+                var xi = l.m_xs[i];
+                if (xi == x)
+                    continue;
+                unsigned pos = new_xs.size();
+                new_xs.push_back(xi);
+                SASSERT(m_var2pos[xi] == UINT_MAX);
+                m_var2pos[xi] = pos;
+                new_as.push_back(l.m_as[i] * b);
+                SASSERT(new_xs[m_var2pos[xi]] == xi);
+                SASSERT(new_xs.size() == new_as.size());
+            }
+            
+            for (unsigned i = 0; i < u.m_num_vars; i++) {
+                var xi = u.m_xs[i];
+                if (xi == x)
+                    continue;
+                unsigned pos = m_var2pos[xi];
+                if (pos == UINT_MAX) {
+                    new_xs.push_back(xi);
+                    new_as.push_back(u.m_as[i] * a);
+                }
+                else {
+                    new_as[pos] += u.m_as[i] * a;
+                }
+            }
+            
+            // remove zeros and check whether all variables are int
+            bool all_int = true;
+            unsigned sz = new_xs.size();
+            unsigned j  = 0;
+            for (unsigned i = 0; i < sz; i++) {
+                if (new_as[i].is_zero())
+                    continue;
+                if (!is_int(new_xs[i]))
+                    all_int = false;
+                if (i != j) {
+                    new_xs[j] = new_xs[i];
+                    new_as[j] = new_as[i];
+                }
+                j++;
+            }
+            new_xs.shrink(j);
+            new_as.shrink(j);
+            
+            if (all_int && new_strict) {
+                new_strict = false;
+                new_c --;
+            }
+            
+            // reset m_var2pos
+            for (unsigned i = 0; i < l.m_num_vars; i++) {
+                m_var2pos[l.m_xs[i]] = UINT_MAX;
+            }
+            
+            if (new_xs.empty() && (new_c.is_pos() || (!new_strict && new_c.is_zero()))) {
+                // literal is true
+                TRACE("fm", tout << "resolution " << x << " consequent literal is always true: \n";
+                      display(tout, l);
+                      tout << "\n";
+                      display(tout, u); tout << "\n";);
+                return 0; // no constraint needs to be created.
+            }
+            
+            new_lits.reset();
+            for (unsigned i = 0; i < l.m_num_lits; i++) {
+                literal lit = l.m_lits[i];
+                bvar    p   = lit2bvar(lit);
+                m_bvar2sign[p] = sign(lit) ? -1 : 1;
+                new_lits.push_back(lit);
+            }
+            
+            bool tautology = false;
+            for (unsigned i = 0; i < u.m_num_lits && !tautology; i++) {
+                literal lit = u.m_lits[i];
+                bvar    p   = lit2bvar(lit);
+                switch (m_bvar2sign[p]) {
+                case 0:
+                    new_lits.push_back(lit);
+                break;
+                case -1:
+                    if (!sign(lit))
+                        tautology = true;
+                    break;
+                case 1:
+                    if (sign(lit))
+                        tautology = true;
+                    break;
+                default:
+                    UNREACHABLE();
+                }
+            }
+            
+            // reset m_bvar2sign
+            for (unsigned i = 0; i < l.m_num_lits; i++) {
+                literal lit = l.m_lits[i];
+                bvar    p   = lit2bvar(lit);
+                m_bvar2sign[p] = 0;
+            }
+            
+            if (tautology) {
+                TRACE("fm", tout << "resolution " << x << " tautology: \n";
+                      display(tout, l);
+                      tout << "\n";
+                      display(tout, u); tout << "\n";);
+                return 0;
+            }
+
+            expr_dependency * new_dep = m.mk_join(l.m_dep, u.m_dep);
+            
+            if (new_lits.empty() && new_xs.empty() && (new_c.is_neg() || (new_strict && new_c.is_zero()))) {
+                TRACE("fm", tout << "resolution " << x << " inconsistent: \n";
+                      display(tout, l);
+                      tout << "\n";
+                      display(tout, u); tout << "\n";);
+                m_inconsistent      = true;
+                m_inconsistent_core = new_dep;
+                return 0;
+            }
+            
+            constraint * new_cnstr = mk_constraint(new_lits.size(),
+                                                   new_lits.c_ptr(),
+                                                   new_xs.size(),
+                                                   new_xs.c_ptr(),
+                                                   new_as.c_ptr(),
+                                                   new_c,
+                                                   new_strict,
+                                                   new_dep);
+
+            TRACE("fm", tout << "resolution " << x << "\n";
+                  display(tout, l);
+                  tout << "\n";
+                  display(tout, u);
+                  tout << "\n---->\n";
+                  display(tout, *new_cnstr); 
+                  tout << "\n";
+                  tout << "new_dep: " << new_dep << "\n";);
+            
+            return new_cnstr;
+        }
+        
+        ptr_vector<constraint> new_constraints;
+        
+        bool try_eliminate(var x) {
+            constraints & l = m_lowers[x];
+            constraints & u = m_uppers[x];
+            cleanup_constraints(l);
+            cleanup_constraints(u);
+            
+            if (l.empty() || u.empty()) {
+                // easy case
+                mark_constraints_dead(x);
+                TRACE("fm", tout << "variables was eliminated (trivial case)\n";);
+                return true;
+            }
+            
+            unsigned num_lowers = l.size();
+            unsigned num_uppers = u.size();
+            
+            if (num_lowers > m_fm_cutoff1 && num_uppers > m_fm_cutoff1)
+                return false;
+            
+            if (num_lowers * num_uppers > m_fm_cutoff2)
+                return false;
+            
+            if (!can_eliminate(x))
+                return false;
+            
+            m_counter += num_lowers * num_uppers;
+            
+            TRACE("fm_bug", tout << "eliminating " << mk_ismt2_pp(m_var2expr.get(x), m) << "\nlowers:\n";
+                  display_constraints(tout, l); tout << "uppers:\n"; display_constraints(tout, u););
+            
+            unsigned num_old_cnstrs = num_uppers + num_lowers;
+            unsigned limit          = num_old_cnstrs + m_fm_extra;
+            unsigned num_new_cnstrs = 0;
+            new_constraints.reset();
+            for (unsigned i = 0; i < num_lowers; i++) {
+                for (unsigned j = 0; j < num_uppers; j++) {
+                    if (m_inconsistent || num_new_cnstrs > limit) {
+                        TRACE("fm", tout << "too many new constraints: " << num_new_cnstrs << "\n";);
+                        del_constraints(new_constraints.size(), new_constraints.c_ptr());
+                        return false;
+                    }
+                    constraint const & l_c = *(l[i]);
+                    constraint const & u_c = *(u[j]);
+                    constraint * new_c = resolve(l_c, u_c, x);
+                    if (new_c != 0) {
+                        num_new_cnstrs++;
+                        new_constraints.push_back(new_c);
+                    }
+                }
+            }
+            
+            mark_constraints_dead(x);
+            
+            unsigned sz = new_constraints.size();
+            
+            m_counter += sz;
+            
+            for (unsigned i = 0; i < sz; i++) {
+                constraint * c = new_constraints[i];
+                backward_subsumption(*c);
+                register_constraint(c);
+            }
+            TRACE("fm", tout << "variables was eliminated old: " << num_old_cnstrs << " new_constraints: " << sz << "\n";);
+            return true;
+        }
+        
+        void copy_remaining(vector<constraints> & v2cs) {
+            vector<constraints>::iterator it  = v2cs.begin();
+            vector<constraints>::iterator end = v2cs.end();
+            for (; it != end; ++it) {
+                constraints & cs = *it;
+                constraints::iterator it2  = cs.begin();
+                constraints::iterator end2 = cs.end();
+                for (; it2 != end2; ++it2) {
+                    constraint * c = *it2;
+                    if (!c->m_dead) {
+                        c->m_dead = true;
+                        expr * new_f = to_expr(*c);
+                        TRACE("fm_bug", tout << "asserting...\n" << mk_ismt2_pp(new_f, m) << "\nnew_dep: " << c->m_dep << "\n";);
+                        m_new_goal->assert_expr(new_f, 0, c->m_dep);
+                    }
+                }
+            }
+            v2cs.finalize();
+        }
+        
+        // Copy remaining clauses to m_new_goal
+        void copy_remaining() {
+            copy_remaining(m_uppers);
+            copy_remaining(m_lowers);
+        }
+        
+        void checkpoint() {
+            cooperate("fm");
+            if (m_cancel)
+                throw tactic_exception(TACTIC_CANCELED_MSG);