/*++ Copyright (c) 2006 Microsoft Corporation Module Name: substitution.h Abstract: A substitution, that is, a mapping from (variable, offset) to (expr, offset). We use offsets in order to avoid creating variants of terms. Author: Leonardo de Moura (leonardo) 2008-02-01. Revision History: --*/ #ifndef _SUBSTITUTION_H_ #define _SUBSTITUTION_H_ #include"expr_offset_map.h" #include"var_offset_map.h" #include"ast_pp.h" /** \brief A mapping from (variable,offset) to expr_offset. */ class substitution { ast_manager & m_manager; var_offset_map m_subst; // field for backtracking typedef std::pair var_offset; svector m_vars; unsigned_vector m_scopes; // fields for applying substitutions svector m_todo; expr_offset_map m_apply_cache; expr_ref_vector m_new_exprs; // fields for checking for cycles enum color { White, Grey, Black }; expr_offset_map m_color; #ifdef Z3DEBUG unsigned m_max_offset_since_reset; #endif void apply_visit(expr_offset const & n, bool & visited); color get_color(expr_offset const & p) const; void set_color(expr_offset const & p, color c); void visit(expr_offset const & p, bool & visited); bool visit_children(expr_offset const & p); bool acyclic(expr_offset p); public: substitution(ast_manager & m); ast_manager & get_manager() const { return m_manager; } // ----------------------------------- // // Reserve memory for the given number of // offsets and variables. // // ----------------------------------- void reserve(unsigned num_offsets, unsigned num_vars) { m_subst.reserve(num_offsets, num_vars); } void reserve_offsets(unsigned num_offsets) { m_subst.reserve_offsets(num_offsets); } void reserve_vars(unsigned num_vars) { m_subst.reserve_vars(num_vars); } // ----------------------------------- // // Reset functions // // ----------------------------------- // reset everything void reset(); // reset only the mapping from variables to expressions void reset_subst(); // reset only the substitution application cache void reset_cache(); // ----------------------------------- // // Backtracking // // ----------------------------------- void push_scope() { m_scopes.push_back(m_vars.size()); } void pop_scope(unsigned num_scopes = 1); unsigned get_scope_lvl() { return m_scopes.size(); } bool top_scope_has_bindings() const { return m_scopes.empty() ? !m_vars.empty() : m_scopes.back() < m_vars.size(); } unsigned get_num_bindings() const { return m_vars.size(); } // ----------------------------------- // // Cycle detection // // ----------------------------------- bool acyclic(); // ----------------------------------- // // Insertion & Lookup // // get_binding supplies a way to inspect the substitution. // // ----------------------------------- void insert(unsigned v_idx, unsigned offset, expr_offset const & t) { TRACE("subst_insert", tout << "inserting: #" << v_idx << ":" << offset << " --> " << mk_pp(t.get_expr(), m_manager) << ":" << t.get_offset() << "\n";); m_vars.push_back(var_offset(v_idx, offset)); m_subst.insert(v_idx, offset, t); } void insert(var * v, unsigned offset, expr_offset const & t) { insert(v->get_idx(), offset, t); } void insert(expr_offset v, expr_offset const & t) { SASSERT(is_var(v.get_expr())); insert(to_var(v.get_expr()), v.get_offset(), t); } bool find(unsigned v_idx, unsigned offset, expr_offset & r) const { return m_subst.find(v_idx, offset, r); } bool find(var * v, unsigned offset, expr_offset & r) const { return find(v->get_idx(), offset, r); } bool find(expr_offset v, expr_offset & r) const { SASSERT(is_var(v.get_expr())); return find(to_var(v.get_expr()), v.get_offset(), r); } void get_binding(unsigned binding_num, var_offset& var, expr_offset& r) { var = m_vars[binding_num]; VERIFY(m_subst.find(var.first, var.second, r)); } bool contains(var * v, unsigned offset) { expr_offset r; return find(v, offset, r); } // ----------------------------------- // // Application // // ----------------------------------- /** \brief Apply the current substitution to the given expression+offset. The result is an expression. The argument num_actual_offsets is the maximum offset used in a insert method since the last reset. The argument deltas is an array of size num_actual_offsets. It contains the variable delta for each offset. A free variable x:i in an expression offset t:j is mapped to the variable x+delta[i]. */ void apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n, expr_ref & result) { apply(num_actual_offsets, deltas, n, expr_offset(0, 0), expr_offset(0, 0), result); } /** \brief Similar to the previous method, but occurrences of s in n are substituted by t. If s != expr_offset(0,0), then the cache is reset before and after the execution of this procedure. */ void apply(unsigned num_actual_offsets, unsigned const* deltas, expr_offset const & n, expr_offset const & s, expr_offset const & t, expr_ref & result); void apply(expr * n, expr_ref & result) { unsigned deltas[1] = { 0 }; apply(1, deltas, expr_offset(n, 0), result); } // ----------------------------------- // // Debugging // // ----------------------------------- /** \brief Dump the current substitution (for debugging purposes). */ void display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas); /** \brief Dump the current substitution without normalizing expressions (for debugging purposes). */ void display(std::ostream & out); // ----------------------------------- // // Compare terms modulo a substitution // // ----------------------------------- bool compare(expr_offset t1, expr_offset t2); }; #endif