/*++
Copyright (c) 2011 Microsoft Corporation

Module Name:

    qe_tactic.cpp

Abstract:

    Quantifier elimination front-end for tactic framework.

Author:

    Leonardo de Moura (leonardo) 2011-12-28.

Revision History:

--*/
#include "tactic/tactical.h"
#include "qe/qe.h"

class qe_tactic : public tactic {
    statistics               m_st;
    struct     imp {
        ast_manager &            m;
        smt_params               m_fparams;
        qe::expr_quant_elim      m_qe;

        imp(ast_manager & _m, params_ref const & p):
            m(_m),
            m_qe(m, m_fparams) {
            updt_params(p);
        }

        void updt_params(params_ref const & p) {
            m_fparams.updt_params(p);
            m_fparams.m_nlquant_elim = p.get_bool("qe_nonlinear", false);
            m_qe.updt_params(p);
        }

        void collect_param_descrs(param_descrs & r) {
            m_qe.collect_param_descrs(r);
        }

        void checkpoint() {
            tactic::checkpoint(m);
        }

        void operator()(goal_ref const & g, 
                        goal_ref_buffer & result) {
            tactic_report report("qe", *g);
            m_fparams.m_model = g->models_enabled();
            proof_ref new_pr(m);
            expr_ref new_f(m);
            bool produce_proofs = g->proofs_enabled();

            unsigned sz = g->size();
            for (unsigned i = 0; i < sz; i++) {
                checkpoint();
                if (g->inconsistent())
                    break;
                expr * f = g->form(i);
                if (!has_quantifiers(f))
                    continue;
                m_qe(m.mk_true(), f, new_f);
                new_pr = nullptr;
                if (produce_proofs) {
                    new_pr = m.mk_rewrite(f, new_f);
                    new_pr = m.mk_modus_ponens(g->pr(i), new_pr);
                }
                g->update(i, new_f, new_pr, g->dep(i));                
            }
            g->inc_depth();
            g->elim_true();
            result.push_back(g.get());
        }

        void collect_statistics(statistics & st) const {
            m_qe.collect_statistics(st);
        }

        void reset_statistics() {            
        }

    };
    
    imp *      m_imp;
    params_ref m_params;
public:
    qe_tactic(ast_manager & m, params_ref const & p):
        m_params(p) {
        m_imp = alloc(imp, m, p);
    }

    tactic * translate(ast_manager & m) override {
        return alloc(qe_tactic, m, m_params);
    }
        
    ~qe_tactic() override {
        dealloc(m_imp);
    }

    void updt_params(params_ref const & p) override {
        m_params = p;
        m_imp->updt_params(p);
    }

   
    void collect_param_descrs(param_descrs & r) override {
        r.insert("qe_nonlinear", CPK_BOOL, "(default: false) enable virtual term substitution.");
        m_imp->collect_param_descrs(r);
    }
    
    void operator()(goal_ref const & in, 
                    goal_ref_buffer & result) override {
        (*m_imp)(in, result);
        m_st.reset();
        m_imp->collect_statistics(m_st);
        
    }

    void collect_statistics(statistics & st) const override {
        st.copy(m_st);
    }

    void reset_statistics() override {
        m_st.reset();
    }

    
    void cleanup() override {
        ast_manager & m = m_imp->m;
        dealloc(m_imp);
        m_imp = alloc(imp, m, m_params);
    }
    
};

tactic * mk_qe_tactic(ast_manager & m, params_ref const & p) {
    return clean(alloc(qe_tactic, m, p));
}

