/*++
Copyright (c) 2019 Microsoft Corporation

Module Name:

    func_decl_replace.cpp

Abstract:

    Replace functions in expressions.

Author:

    Nikolaj Bjorner (nbjorner) 2019-03-28

Revision History:


--*/


#include "ast/rewriter/func_decl_replace.h"

expr_ref func_decl_replace::operator()(expr* e) {
    m_todo.push_back(e);
    m_refs.push_back(e);

    while (!m_todo.empty()) {
        expr* a = m_todo.back(), *b;
        if (m_cache.contains(a)) {
            m_todo.pop_back();
        }
        else if (is_var(a)) {
            m_cache.insert(a, a);
            m_todo.pop_back();
        }
        else if (is_app(a)) {
            app* c = to_app(a);
            unsigned n = c->get_num_args();
            m_args.reset();            
            bool arg_differs = false;
            for (unsigned i = 0; i < n; ++i) {
                expr* d = nullptr, *arg = c->get_arg(i);
                if (m_cache.find(arg, d)) {
                    m_args.push_back(d);
                    arg_differs |= arg != d;
                    SASSERT(arg->get_sort() == d->get_sort());
                }
                else {
                    m_todo.push_back(arg);
                }
            }
            if (m_args.size() == n) {
                if (arg_differs) {
                    b = m.mk_app(c->get_decl(), m_args.size(), m_args.data());
                    m_refs.push_back(b);
                    SASSERT(a->get_sort() == b->get_sort());
                } else {
                    b = a;
                }
                func_decl* f = nullptr;
                if (m_subst.find(c->get_decl(), f)) {
                    b = m.mk_app(f, m_args.size(), m_args.data());
                    m_refs.push_back(b);
                }
                m_cache.insert(a, b);
                m_todo.pop_back();
            }
        }
        else {
            quantifier* q = to_quantifier(a);
            SASSERT(is_quantifier(a));
            expr* body = q->get_expr(), *new_body;
            if (m_cache.find(body, new_body)) {
                if (new_body == body) {
                    b = a;
                }
                else {
                    b = m.update_quantifier(q, new_body);
                    m_refs.push_back(b);
                }
                m_cache.insert(a, b);
                m_todo.pop_back();
            }
            else {
                m_todo.push_back(body);
            }
        }        
    }
    return expr_ref(m_cache.find(e), m);
}

void func_decl_replace::reset() {
    m_cache.reset();
    m_subst.reset();
    m_refs.reset();
    m_funs.reset();
}
