mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-03 21:09:11 +00:00 
			
		
		
		
	move eq solver functionality to common place, fixes to goal2sat
This commit is contained in:
		
							parent
							
								
									cf3002c293
								
							
						
					
					
						commit
						e398959732
					
				
					 6 changed files with 468 additions and 370 deletions
				
			
		| 
						 | 
				
			
			@ -19,27 +19,44 @@ Author:
 | 
			
		|||
 | 
			
		||||
namespace seq {
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::reduce(expr* s, expr* t, eq_ptr& r) {
 | 
			
		||||
        expr_ref_vector ls(m), rs(m);
 | 
			
		||||
        ls.push_back(s);
 | 
			
		||||
        rs.push_back(t);
 | 
			
		||||
        eqr e(ls, rs);
 | 
			
		||||
        return reduce(e, r);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::solve(eq const& e, eq_ptr& r) {
 | 
			
		||||
        if (solve_itos1(e, r))
 | 
			
		||||
    bool eq_solver::reduce(eqr const& e, eq_ptr& r) {
 | 
			
		||||
        r = nullptr;
 | 
			
		||||
        if (reduce_unit(e, r))
 | 
			
		||||
            return true;
 | 
			
		||||
        if (solve_itos2(e, r))
 | 
			
		||||
        if (reduce_itos1(e, r))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        if (reduce_itos2(e, r))
 | 
			
		||||
            return true;
 | 
			
		||||
        if (reduce_binary_eq(e, r))
 | 
			
		||||
            return true;
 | 
			
		||||
        
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void eq_solver::set_conflict() {
 | 
			
		||||
        m_clause.reset();
 | 
			
		||||
        ctx.add_consequence(true, m_clause);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void eq_solver::add_consequence(expr_ref const& a) {
 | 
			
		||||
        m_clause.reset();
 | 
			
		||||
        m_clause.push_back(a);
 | 
			
		||||
        m_add_consequence(true, m_clause);
 | 
			
		||||
        ctx.add_consequence(true, m_clause);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void eq_solver::add_consequence(expr_ref const& a, expr_ref const& b) {
 | 
			
		||||
        m_clause.reset();
 | 
			
		||||
        m_clause.push_back(a);
 | 
			
		||||
        m_clause.push_back(b);
 | 
			
		||||
        m_add_consequence(true, m_clause);
 | 
			
		||||
        ctx.add_consequence(true, m_clause);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -48,17 +65,16 @@ namespace seq {
 | 
			
		|||
     * s = t or (s < 0 and t < 0)
 | 
			
		||||
     */
 | 
			
		||||
        
 | 
			
		||||
    bool eq_solver::match_itos1(eq const& e, expr*& a, expr*& b) {
 | 
			
		||||
    bool eq_solver::match_itos1(eqr const& e, expr*& a, expr*& b) {
 | 
			
		||||
        return 
 | 
			
		||||
            e.ls.size() == 1 && e.rs.size() == 1 && 
 | 
			
		||||
            seq.str.is_itos(e.ls[0], a) && seq.str.is_itos(e.rs[0], b);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::solve_itos1(eq const& e, eq_ptr& r) {
 | 
			
		||||
    bool eq_solver::reduce_itos1(eqr const& e, eq_ptr& r) {
 | 
			
		||||
        expr* s = nullptr, *t = nullptr;
 | 
			
		||||
        if (!match_itos1(e, s, t))
 | 
			
		||||
            return false;
 | 
			
		||||
        r = nullptr;
 | 
			
		||||
        expr_ref eq = mk_eq(s, t);
 | 
			
		||||
        add_consequence(eq, mk_le(s, -1));
 | 
			
		||||
        add_consequence(eq, mk_le(t, -1));
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +87,7 @@ namespace seq {
 | 
			
		|||
     *       s < 0
 | 
			
		||||
     */
 | 
			
		||||
    
 | 
			
		||||
    bool eq_solver::match_itos2(eq const& e, expr*& s) {
 | 
			
		||||
    bool eq_solver::match_itos2(eqr const& e, expr*& s) {
 | 
			
		||||
        if (e.ls.size() == 1 && e.rs.empty() && seq.str.is_itos(e.ls[0], s))
 | 
			
		||||
            return true;
 | 
			
		||||
        if (e.rs.size() == 1 && e.ls.empty() && seq.str.is_itos(e.rs[0], s))
 | 
			
		||||
| 
						 | 
				
			
			@ -79,14 +95,308 @@ namespace seq {
 | 
			
		|||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::solve_itos2(eq const& e, eq_ptr& r) {
 | 
			
		||||
    bool eq_solver::reduce_itos2(eqr const& e, eq_ptr& r) {
 | 
			
		||||
        expr* s = nullptr;
 | 
			
		||||
        if (!match_itos2(e, s))
 | 
			
		||||
            return false;
 | 
			
		||||
        r = nullptr;
 | 
			
		||||
        add_consequence(mk_le(s, -1));
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *    x = t, where x does not occur in t.
 | 
			
		||||
     */
 | 
			
		||||
    bool eq_solver::reduce_unit(eqr const& e, eq_ptr& r) {
 | 
			
		||||
        if (e.ls == e.rs) 
 | 
			
		||||
            return true;
 | 
			
		||||
        if (e.ls.size() == 1 && is_var(e.ls[0]) && !occurs(e.ls[0], e.rs)) {
 | 
			
		||||
            expr_ref y(seq.str.mk_concat(e.rs, e.ls[0]->get_sort()), m);
 | 
			
		||||
            ctx.add_solution(e.ls[0], y);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (e.rs.size() == 1 && is_var(e.rs[0]) && !occurs(e.rs[0], e.ls)) {
 | 
			
		||||
            expr_ref y(seq.str.mk_concat(e.ls, e.rs[0]->get_sort()), m);
 | 
			
		||||
            ctx.add_solution(e.rs[0], y);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Equation is of the form x ++ xs = ys ++ x
 | 
			
		||||
     * where |xs| = |ys| are units of same length
 | 
			
		||||
     * then xs is a wrap-around of ys
 | 
			
		||||
     * x ++ ab = ba ++ x
 | 
			
		||||
     *
 | 
			
		||||
     * When it is of the form x ++ a = b ++ x
 | 
			
		||||
     * Infer that a = b
 | 
			
		||||
     * It is also the case that x is a repetition of a's
 | 
			
		||||
     * But this information is not exposed with this inference.
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    bool eq_solver::reduce_binary_eq(eqr const& e, eq_ptr& r) {
 | 
			
		||||
        ptr_vector<expr> xs, ys;
 | 
			
		||||
        expr_ref x(m), y(m);
 | 
			
		||||
        if (!match_binary_eq(e, x, xs, ys, y))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (xs.size() != ys.size()) {
 | 
			
		||||
            set_conflict();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (xs.empty()) 
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        if (xs.size() != 1)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (ctx.expr2rep(xs[0]) == ctx.expr2rep(ys[0]))
 | 
			
		||||
            return false;
 | 
			
		||||
        expr_ref eq(m.mk_eq(xs[0], ys[0]), m);
 | 
			
		||||
        expr* veq = ctx.expr2rep(eq);
 | 
			
		||||
        if (m.is_true(veq)) 
 | 
			
		||||
            return false;
 | 
			
		||||
        add_consequence(eq);
 | 
			
		||||
        return m.is_false(veq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::is_var(expr* a) const {
 | 
			
		||||
        return
 | 
			
		||||
            seq.is_seq(a) &&
 | 
			
		||||
            !seq.str.is_concat(a) &&
 | 
			
		||||
            !seq.str.is_empty(a)  &&
 | 
			
		||||
            !seq.str.is_string(a) &&
 | 
			
		||||
            !seq.str.is_unit(a) &&
 | 
			
		||||
            !seq.str.is_itos(a) &&
 | 
			
		||||
            !seq.str.is_nth_i(a) && 
 | 
			
		||||
            !m.is_ite(a);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::occurs(expr* a, expr_ref_vector const& b) {
 | 
			
		||||
        for (auto const& elem : b) 
 | 
			
		||||
            if (a == elem || m.is_ite(elem)) 
 | 
			
		||||
                return true;        
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::occurs(expr* a, expr* b) {
 | 
			
		||||
        // true if a occurs under an interpreted function or under left/right selector.
 | 
			
		||||
        SASSERT(is_var(a));
 | 
			
		||||
        SASSERT(m_todo.empty());
 | 
			
		||||
        expr* e1 = nullptr, *e2 = nullptr;
 | 
			
		||||
        m_todo.push_back(b);
 | 
			
		||||
        while (!m_todo.empty()) {
 | 
			
		||||
            b = m_todo.back();
 | 
			
		||||
            if (a == b || m.is_ite(b)) {
 | 
			
		||||
                m_todo.reset();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            m_todo.pop_back();
 | 
			
		||||
            if (seq.str.is_concat(b, e1, e2)) {
 | 
			
		||||
                m_todo.push_back(e1);
 | 
			
		||||
                m_todo.push_back(e2);
 | 
			
		||||
            }
 | 
			
		||||
            else if (seq.str.is_unit(b, e1)) {
 | 
			
		||||
                m_todo.push_back(e1);
 | 
			
		||||
            }
 | 
			
		||||
            else if (seq.str.is_nth_i(b, e1, e2)) {
 | 
			
		||||
                m_todo.push_back(e1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void eq_solver::set_prefix(expr_ref& x, expr_ref_vector const& xs, unsigned sz) const {
 | 
			
		||||
        SASSERT(0 < xs.size() && sz <= xs.size());
 | 
			
		||||
        x = seq.str.mk_concat(sz, xs.c_ptr(), xs[0]->get_sort());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void eq_solver::set_suffix(expr_ref& x, expr_ref_vector const& xs, unsigned sz) const {
 | 
			
		||||
        SASSERT(0 < xs.size() && sz <= xs.size());
 | 
			
		||||
        x = seq.str.mk_concat(sz, xs.c_ptr() + xs.size() - sz, xs[0]->get_sort());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unsigned eq_solver::count_units_l2r(expr_ref_vector const& es, unsigned offset) const {
 | 
			
		||||
        unsigned i = offset, sz = es.size();
 | 
			
		||||
        for (; i < sz && seq.str.is_unit(es[i]); ++i) ;
 | 
			
		||||
        return i - offset;                        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unsigned eq_solver::count_units_r2l(expr_ref_vector const& es, unsigned offset) const {
 | 
			
		||||
        SASSERT(offset < es.size());
 | 
			
		||||
        unsigned i = offset, count = 0;
 | 
			
		||||
        do {
 | 
			
		||||
            if (!seq.str.is_unit(es[i]))
 | 
			
		||||
                break;
 | 
			
		||||
            ++count;
 | 
			
		||||
        }
 | 
			
		||||
        while (i-- > 0);
 | 
			
		||||
        return count;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unsigned eq_solver::count_non_units_l2r(expr_ref_vector const& es, unsigned offset) const {
 | 
			
		||||
        unsigned i = offset, sz = es.size();
 | 
			
		||||
        for (; i < sz && !seq.str.is_unit(es[i]); ++i) ;
 | 
			
		||||
        return i - offset;                        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unsigned eq_solver::count_non_units_r2l(expr_ref_vector const& es, unsigned offset) const {
 | 
			
		||||
        SASSERT(offset < es.size());
 | 
			
		||||
        unsigned i = offset, count = 0;
 | 
			
		||||
        do {
 | 
			
		||||
            if (seq.str.is_unit(es[i]))
 | 
			
		||||
                break;
 | 
			
		||||
            ++count;
 | 
			
		||||
        }
 | 
			
		||||
        while (i-- > 0);
 | 
			
		||||
        return count;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * match: .. X abc  = Y .. Z def U
 | 
			
		||||
     * where U is a variable or concatenation of variables
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::match_ternary_eq_r(expr_ref_vector const& ls, expr_ref_vector const& rs, 
 | 
			
		||||
                                       expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
 | 
			
		||||
        if (ls.size() > 1 && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {            
 | 
			
		||||
            unsigned num_ls_units = count_units_r2l(ls, ls.size() - 1);
 | 
			
		||||
            if (num_ls_units == 0 || num_ls_units == ls.size())
 | 
			
		||||
                return false;
 | 
			
		||||
            unsigned ls_units_offset = ls.size() - num_ls_units;
 | 
			
		||||
            unsigned num_rs_non_units = count_non_units_r2l(rs, rs.size() - 1);
 | 
			
		||||
            if (num_rs_non_units == rs.size())
 | 
			
		||||
                return false;
 | 
			
		||||
            SASSERT(num_rs_non_units > 0);
 | 
			
		||||
            unsigned num_rs_units = count_units_r2l(rs, rs.size() - 1 - num_rs_non_units);
 | 
			
		||||
            if (num_rs_units == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
            set_prefix(x,   ls, ls.size() - num_ls_units);
 | 
			
		||||
            set_suffix(xs,  ls, num_ls_units);
 | 
			
		||||
            unsigned offset = rs.size() - num_rs_non_units - num_rs_units;
 | 
			
		||||
            set_prefix(y1,  rs, offset);
 | 
			
		||||
            set_extract(ys, rs, offset, num_rs_units);
 | 
			
		||||
            set_suffix(y2,  rs, num_rs_non_units);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::match_ternary_eq_rhs(expr_ref_vector const& ls, expr_ref_vector const& rs, 
 | 
			
		||||
                                         expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
 | 
			
		||||
        if (match_ternary_eq_r(ls, rs, x, xs, y1, ys, y2))
 | 
			
		||||
            return true;
 | 
			
		||||
        if (match_ternary_eq_r(rs, ls, x, xs, y1, ys, y2))
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /*
 | 
			
		||||
      match: abc X ..  = Y def Z ..
 | 
			
		||||
         where Y is a variable or concatenation of variables
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::match_ternary_eq_l(expr_ref_vector const& ls, expr_ref_vector const& rs, 
 | 
			
		||||
                                       expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
 | 
			
		||||
        if (ls.size() > 1 && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {
 | 
			
		||||
            unsigned num_ls_units = count_units_l2r(ls, 0);
 | 
			
		||||
            if (num_ls_units == 0 || num_ls_units == ls.size())
 | 
			
		||||
                return false;
 | 
			
		||||
            unsigned num_rs_non_units = count_non_units_l2r(rs, 0);
 | 
			
		||||
            if (num_rs_non_units == rs.size() || num_rs_non_units == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
            unsigned num_rs_units = count_units_l2r(rs, num_rs_non_units);
 | 
			
		||||
            if (num_rs_units == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
            set_prefix(xs,  ls, num_ls_units);
 | 
			
		||||
            set_suffix(x,   ls, ls.size() - num_ls_units);
 | 
			
		||||
            set_prefix(y1,  rs, num_rs_non_units);
 | 
			
		||||
            set_extract(ys, rs, num_rs_non_units, num_rs_units);
 | 
			
		||||
            set_suffix(y2,  rs, rs.size() - num_rs_non_units - num_rs_units);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::match_ternary_eq_lhs(expr_ref_vector const& ls, expr_ref_vector const& rs, 
 | 
			
		||||
                                         expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
 | 
			
		||||
        if (match_ternary_eq_l(ls, rs, xs, x, y1, ys, y2))
 | 
			
		||||
            return true;
 | 
			
		||||
        if (match_ternary_eq_l(rs, ls, xs, x, y1, ys, y2))
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::all_units(expr_ref_vector const& es, unsigned start, unsigned end) const {
 | 
			
		||||
        for (unsigned i = start; i < end; ++i) 
 | 
			
		||||
            if (!seq.str.is_unit(es[i])) 
 | 
			
		||||
                return false;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       match X abc = defg Y, for abc, defg non-empty
 | 
			
		||||
    */
 | 
			
		||||
    
 | 
			
		||||
    bool eq_solver::match_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, 
 | 
			
		||||
                                    expr_ref& x, ptr_vector<expr>& xs, ptr_vector<expr>& ys, expr_ref& y) {
 | 
			
		||||
        if (ls.size() > 1 && is_var(ls[0]) &&
 | 
			
		||||
            rs.size() > 1 && is_var(rs.back()) &&
 | 
			
		||||
            all_units(ls, 1, ls.size()) &&
 | 
			
		||||
            all_units(rs, 0, rs.size() - 1)) {
 | 
			
		||||
            x = ls[0];
 | 
			
		||||
            y = rs.back();
 | 
			
		||||
            set_suffix(xs, ls, ls.size() - 1);
 | 
			
		||||
            set_prefix(ys, rs, rs.size() - 1);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::match_binary_eq(eqr const& e, expr_ref& x, ptr_vector<expr>& xs, ptr_vector<expr>& ys, expr_ref& y) {
 | 
			
		||||
        if (match_binary_eq(e.ls, e.rs, x, xs, ys, y) && x == y)
 | 
			
		||||
            return true;
 | 
			
		||||
        if (match_binary_eq(e.rs, e.ls, x, xs, ys, y) && x == y)
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * match: X abc Y..Y' = Z def T..T', where X,Z are variables or concatenation of variables
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    bool eq_solver::match_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs,
 | 
			
		||||
                                  expr_ref& x1, expr_ref_vector& xs, expr_ref& x2,
 | 
			
		||||
                                  expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
 | 
			
		||||
        if (ls.size() > 1 && is_var(ls[0]) && is_var(ls.back()) &&
 | 
			
		||||
            rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {
 | 
			
		||||
            unsigned ls_non_unit = count_non_units_l2r(ls, 0);
 | 
			
		||||
            unsigned rs_non_unit = count_non_units_l2r(rs, 0);
 | 
			
		||||
            if (ls_non_unit == ls.size())
 | 
			
		||||
                return false;
 | 
			
		||||
            if (rs_non_unit == rs.size())
 | 
			
		||||
                return false;
 | 
			
		||||
            unsigned ls_unit = count_units_l2r(ls, ls_non_unit);
 | 
			
		||||
            unsigned rs_unit = count_units_l2r(rs, rs_non_unit);
 | 
			
		||||
            if (ls_unit == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
            if (rs_unit == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
            set_prefix(x1,  ls, ls_non_unit);
 | 
			
		||||
            set_extract(xs, ls, ls_non_unit, ls_unit);
 | 
			
		||||
            set_suffix(x2,  ls, ls.size() - ls_non_unit - ls_unit);
 | 
			
		||||
            set_prefix(y1,  rs, rs_non_unit);
 | 
			
		||||
            set_extract(ys, rs, rs_non_unit, rs_unit);
 | 
			
		||||
            set_suffix(y2,  rs, rs.size() - rs_non_unit - rs_unit);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ Module Name:
 | 
			
		|||
 | 
			
		||||
Abstract:
 | 
			
		||||
 | 
			
		||||
    Solver-agnostic equality solving routines for sequences
 | 
			
		||||
    Solver core-agnostic equality inference routines for sequences
 | 
			
		||||
    
 | 
			
		||||
Author:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,23 +27,73 @@ namespace seq {
 | 
			
		|||
            ls(l), rs(r) {}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct eqr {
 | 
			
		||||
        expr_ref_vector const& ls;
 | 
			
		||||
        expr_ref_vector const& rs;
 | 
			
		||||
        eqr(expr_ref_vector const& l, expr_ref_vector const& r):
 | 
			
		||||
            ls(l), rs(r) {}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    typedef scoped_ptr<eq> eq_ptr;
 | 
			
		||||
 | 
			
		||||
    class eq_solver_context {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual void  add_consequence(bool uses_dep, expr_ref_vector const& clause) = 0;
 | 
			
		||||
        virtual void  add_solution(expr* var, expr* term) = 0;
 | 
			
		||||
        virtual expr* expr2rep(expr* e) = 0;
 | 
			
		||||
        virtual bool  get_length(expr* e, rational& r) = 0;
 | 
			
		||||
    };
 | 
			
		||||
        
 | 
			
		||||
    class eq_solver {
 | 
			
		||||
        ast_manager& m;
 | 
			
		||||
        axioms&      m_ax;
 | 
			
		||||
        seq_util     seq;
 | 
			
		||||
        expr_ref_vector                        m_clause;
 | 
			
		||||
        std::function<expr*(expr*)>            m_value;
 | 
			
		||||
        std::function<bool(expr*, rational&)>  m_int_value;
 | 
			
		||||
        std::function<void(bool, expr_ref_vector const&)>  m_add_consequence;
 | 
			
		||||
        ast_manager&       m;
 | 
			
		||||
        eq_solver_context& ctx;
 | 
			
		||||
        axioms&            m_ax;
 | 
			
		||||
        seq_util           seq;
 | 
			
		||||
        expr_ref_vector    m_clause;
 | 
			
		||||
 | 
			
		||||
        bool match_itos1(eq const& e, expr*& s, expr*& t);
 | 
			
		||||
        bool solve_itos1(eq const& e, eq_ptr& r);
 | 
			
		||||
        bool reduce_unit(eqr const& e, eq_ptr& r);
 | 
			
		||||
 | 
			
		||||
        bool match_itos2(eq const& e, expr*& s);
 | 
			
		||||
        bool solve_itos2(eq const& e, eq_ptr& r);
 | 
			
		||||
        bool match_itos1(eqr const& e, expr*& s, expr*& t);
 | 
			
		||||
        bool reduce_itos1(eqr const& e, eq_ptr& r);
 | 
			
		||||
 | 
			
		||||
        bool match_itos2(eqr const& e, expr*& s);
 | 
			
		||||
        bool reduce_itos2(eqr const& e, eq_ptr& r);
 | 
			
		||||
 | 
			
		||||
        bool match_binary_eq(eqr const& e, expr_ref& x, ptr_vector<expr>& xs, ptr_vector<expr>& ys, expr_ref& y);
 | 
			
		||||
        bool reduce_binary_eq(eqr const& e, eq_ptr& r);
 | 
			
		||||
 | 
			
		||||
        bool match_ternary_eq_r(expr_ref_vector const& ls, expr_ref_vector const& rs,
 | 
			
		||||
                                expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2);
 | 
			
		||||
 | 
			
		||||
        bool match_ternary_eq_l(expr_ref_vector const& ls, expr_ref_vector const& rs,
 | 
			
		||||
                                expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * count unit or non-unit entries left-to-right or right-to-left starting at, and including offset.
 | 
			
		||||
         */
 | 
			
		||||
        unsigned count_units_l2r(expr_ref_vector const& es, unsigned offset) const;
 | 
			
		||||
        unsigned count_units_r2l(expr_ref_vector const& es, unsigned offset) const;
 | 
			
		||||
        unsigned count_non_units_l2r(expr_ref_vector const& es, unsigned offset) const;
 | 
			
		||||
        unsigned count_non_units_r2l(expr_ref_vector const& es, unsigned offset) const;
 | 
			
		||||
 | 
			
		||||
        void set_prefix(expr_ref& x, expr_ref_vector const& xs, unsigned sz) const;
 | 
			
		||||
        void set_suffix(expr_ref& x, expr_ref_vector const& xs, unsigned sz) const;
 | 
			
		||||
 | 
			
		||||
        template<typename V>
 | 
			
		||||
        void set_prefix(V& dst, expr_ref_vector const& xs, unsigned sz) const { set_extract(dst, xs, 0, sz); }
 | 
			
		||||
 | 
			
		||||
        template<typename V>
 | 
			
		||||
        void set_suffix(V& dst, expr_ref_vector const& xs, unsigned sz) const { set_extract(dst, xs, xs.size() - sz, sz); }
 | 
			
		||||
 | 
			
		||||
        template<typename V> 
 | 
			
		||||
        void set_extract(V& dst, expr_ref_vector const& xs, unsigned offset, unsigned sz) const {
 | 
			
		||||
            SASSERT(offset + sz <= xs.size());
 | 
			
		||||
            dst.reset();
 | 
			
		||||
            dst.append(sz, xs.c_ptr() + offset);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void set_conflict();
 | 
			
		||||
        void add_consequence(expr_ref const& a);
 | 
			
		||||
        void add_consequence(expr_ref const& a, expr_ref const& b);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -53,21 +103,40 @@ namespace seq {
 | 
			
		|||
        expr_ref mk_ge(expr* x, rational const& n) { return m_ax.mk_ge(x, n); }
 | 
			
		||||
        expr_ref mk_le(expr* x, rational const& n) { return m_ax.mk_le(x, n); }
 | 
			
		||||
 | 
			
		||||
        ptr_vector<expr> m_todo;
 | 
			
		||||
        bool occurs(expr* a, expr_ref_vector const& b);
 | 
			
		||||
        bool occurs(expr* a, expr* b);
 | 
			
		||||
 | 
			
		||||
        bool all_units(expr_ref_vector const& es, unsigned start, unsigned end) const;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        
 | 
			
		||||
        eq_solver(ast_manager& m, axioms& ax):
 | 
			
		||||
        eq_solver(ast_manager& m, eq_solver_context& ctx, axioms& ax):
 | 
			
		||||
            m(m),
 | 
			
		||||
            ctx(ctx),
 | 
			
		||||
            m_ax(ax),
 | 
			
		||||
            seq(m),
 | 
			
		||||
            m_clause(m)
 | 
			
		||||
        {}
 | 
			
		||||
 | 
			
		||||
        void set_value(std::function<expr*(expr*)>& v) { m_value = v; }
 | 
			
		||||
        void set_int_value(std::function<bool(expr*,rational&)>& iv) { m_int_value = iv; }
 | 
			
		||||
        void set_add_consequence(std::function<void(bool, expr_ref_vector const&)>& ac) { m_add_consequence = ac; }
 | 
			
		||||
        bool reduce(eqr const& e, eq_ptr& r);
 | 
			
		||||
 | 
			
		||||
        bool solve(eq const& e, eq_ptr& r);
 | 
			
		||||
        bool reduce(expr* s, expr* t, eq_ptr& r);
 | 
			
		||||
 | 
			
		||||
        bool is_var(expr* a) const;
 | 
			
		||||
 | 
			
		||||
        bool match_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, 
 | 
			
		||||
                             expr_ref& x, ptr_vector<expr>& xs, ptr_vector<expr>& ys, expr_ref& y);
 | 
			
		||||
 | 
			
		||||
        bool match_ternary_eq_rhs(expr_ref_vector const& ls, expr_ref_vector const& rs,
 | 
			
		||||
                                  expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2);
 | 
			
		||||
 | 
			
		||||
        bool match_ternary_eq_lhs(expr_ref_vector const& ls, expr_ref_vector const& rs,
 | 
			
		||||
                                  expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2);
 | 
			
		||||
 | 
			
		||||
        bool match_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs,
 | 
			
		||||
                           expr_ref& x1, expr_ref_vector& xs, expr_ref& x2,
 | 
			
		||||
                           expr_ref& y1, expr_ref_vector& ys, expr_ref& y2);
 | 
			
		||||
        
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -257,8 +257,8 @@ struct goal2sat::imp : public sat::sat_internalizer {
 | 
			
		|||
        m_num_scopes = 0;
 | 
			
		||||
        m_map.pop(n);
 | 
			
		||||
        unsigned k = m_cache_lim[m_cache_lim.size() - n];
 | 
			
		||||
        for (; k-- > m_cache_trail.size(); ) {
 | 
			
		||||
            app* t = m_cache_trail[k];
 | 
			
		||||
        for (unsigned i = m_cache_trail.size(); i-- > k; ) {
 | 
			
		||||
            app* t = m_cache_trail[i];
 | 
			
		||||
            sat::literal lit;
 | 
			
		||||
            if (m_app2lit.find(t, lit)) {
 | 
			
		||||
                m_app2lit.remove(t);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,56 +60,41 @@ bool theory_seq::solve_eq(unsigned idx) {
 | 
			
		|||
        TRACE("seq", tout << "simplified\n";);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!ctx.inconsistent() && lift_ite(ls, rs, deps)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    seq::eq_ptr r;
 | 
			
		||||
    m_eq_deps = deps;
 | 
			
		||||
    seq::eqr er(ls, rs);
 | 
			
		||||
    if (!ctx.inconsistent() && m_eq.reduce(er, r)) {
 | 
			
		||||
        if (!r) 
 | 
			
		||||
            return true;
 | 
			
		||||
        m_eqs.set(idx, depeq(m_eq_id++, r->ls, r->rs, deps));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TRACE("seq_verbose", tout << ls << " = " << rs << "\n";);
 | 
			
		||||
    if (ls.empty() && rs.empty()) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (!ctx.inconsistent() && solve_unit_eq(ls, rs, deps)) {
 | 
			
		||||
        TRACE("seq", tout << "unit\n";);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (!ctx.inconsistent() && solve_binary_eq(ls, rs, deps)) {
 | 
			
		||||
        TRACE("seq", tout << "binary\n";);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (!ctx.inconsistent() && solve_nth_eq1(ls, rs, deps)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (!ctx.inconsistent() && solve_nth_eq1(rs, ls, deps)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    seq::eq_ptr r;
 | 
			
		||||
    m_eq_deps = deps;
 | 
			
		||||
    if (!ctx.inconsistent() && m_eq.solve(e, r)) {
 | 
			
		||||
        if (!r) 
 | 
			
		||||
            return true;
 | 
			
		||||
        m_eqs.set(idx, depeq(m_eq_id++, r->ls, r->rs, deps));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (!ctx.inconsistent() && solve_itos(rs, ls, deps)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (!ctx.inconsistent() && change) {
 | 
			
		||||
        // The propagation step from arithmetic state (e.g. length offset) to length constraints
 | 
			
		||||
        TRACE("seq", tout << "inserting equality\n";);
 | 
			
		||||
        expr_ref_vector new_ls(m);
 | 
			
		||||
        if (!m_offset_eq.empty() && find_better_rep(ls, rs, idx, deps, new_ls)) {
 | 
			
		||||
            // Find a better equivalent term for lhs of an equation based on length constraints            
 | 
			
		||||
            m_eqs.push_back(depeq(m_eq_id++, new_ls, rs, deps));
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            m_eqs.set(idx, depeq(m_eq_id++, ls, rs, deps));
 | 
			
		||||
        }
 | 
			
		||||
        m_eqs.set(idx, depeq(m_eq_id++, ls, rs, deps));        
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void theory_seq::add_consequence(bool uses_eq, expr_ref_vector const& clause) {
 | 
			
		||||
    dependency* dep = uses_eq ? m_eq_deps : nullptr;
 | 
			
		||||
    m_new_propagation = true;
 | 
			
		||||
    if (clause.size() == 1) {
 | 
			
		||||
        propagate_lit(dep, 0, nullptr, mk_literal(clause[0]));
 | 
			
		||||
        return;
 | 
			
		||||
| 
						 | 
				
			
			@ -126,118 +111,24 @@ void theory_seq::add_consequence(bool uses_eq, expr_ref_vector const& clause) {
 | 
			
		|||
    add_axiom(lits);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) {
 | 
			
		||||
    if (l == r) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (is_var(l) && !occurs(l, r) && add_solution(l, r, deps)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (is_var(r) && !occurs(r, l) && add_solution(r, l, deps)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool theory_seq::solve_unit_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* deps) {
 | 
			
		||||
    if (l.size() == 1 && is_var(l[0]) && !occurs(l[0], r) && add_solution(l[0], mk_concat(r, l[0]->get_sort()), deps)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (r.size() == 1 && is_var(r[0]) && !occurs(r[0], l) && add_solution(r[0], mk_concat(l, r[0]->get_sort()), deps)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool theory_seq::solve_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep) {
 | 
			
		||||
    ptr_vector<expr> xs, ys;
 | 
			
		||||
    expr_ref x(m), y(m);
 | 
			
		||||
    if (!is_binary_eq(ls, rs, x, xs, ys, y) &&
 | 
			
		||||
        !is_binary_eq(rs, ls, x, xs, ys, y)) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    // Equation is of the form x ++ xs = ys ++ y
 | 
			
		||||
    // where xs, ys are units.
 | 
			
		||||
    if (x != y) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (xs.size() != ys.size()) {
 | 
			
		||||
        TRACE("seq", tout << "binary conflict\n";);
 | 
			
		||||
        set_conflict(dep);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (xs.empty()) {
 | 
			
		||||
        // this should have been solved already
 | 
			
		||||
        UNREACHABLE();
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Equation is of the form x ++ xs = ys ++ x
 | 
			
		||||
    // where |xs| = |ys| are units of same length
 | 
			
		||||
    // then xs is a wrap-around of ys
 | 
			
		||||
    // x ++ ab = ba ++ x
 | 
			
		||||
    // 
 | 
			
		||||
    if (xs.size() == 1) {
 | 
			
		||||
        enode* n1 = ensure_enode(xs[0]);
 | 
			
		||||
        enode* n2 = ensure_enode(ys[0]);
 | 
			
		||||
        if (n1->get_root() == n2->get_root()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        literal eq = mk_eq(xs[0], ys[0], false);
 | 
			
		||||
        switch (ctx.get_assignment(eq)) {
 | 
			
		||||
        case l_false: {
 | 
			
		||||
            literal_vector conflict;
 | 
			
		||||
            conflict.push_back(~eq);
 | 
			
		||||
            TRACE("seq", tout << conflict << "\n";);
 | 
			
		||||
            set_conflict(dep, conflict);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
expr* theory_seq::expr2rep(expr* e) { 
 | 
			
		||||
    if (m.is_bool(e) && ctx.b_internalized(e)) {
 | 
			
		||||
        bool_var b = ctx.get_bool_var(e);
 | 
			
		||||
        switch (ctx.get_assignment(b)) {
 | 
			
		||||
        case l_true:
 | 
			
		||||
            break;
 | 
			
		||||
        case l_undef: 
 | 
			
		||||
            propagate_lit(dep, 0, nullptr, eq);
 | 
			
		||||
            m_new_propagation = true;
 | 
			
		||||
            return m.mk_true();
 | 
			
		||||
        case l_false:
 | 
			
		||||
            return m.mk_false();
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
    }        
 | 
			
		||||
    if (!ctx.e_internalized(e))
 | 
			
		||||
        return e;
 | 
			
		||||
    return ctx.get_enode(e)->get_root()->get_expr();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool theory_seq::occurs(expr* a, expr_ref_vector const& b) {
 | 
			
		||||
    for (auto const& elem : b) {
 | 
			
		||||
        if (a == elem || m.is_ite(elem)) return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool theory_seq::occurs(expr* a, expr* b) {
 | 
			
		||||
     // true if a occurs under an interpreted function or under left/right selector.
 | 
			
		||||
    SASSERT(is_var(a));
 | 
			
		||||
    SASSERT(m_todo.empty());
 | 
			
		||||
    expr* e1 = nullptr, *e2 = nullptr;
 | 
			
		||||
    m_todo.push_back(b);
 | 
			
		||||
    while (!m_todo.empty()) {
 | 
			
		||||
        b = m_todo.back();
 | 
			
		||||
        if (a == b || m.is_ite(b)) {
 | 
			
		||||
            m_todo.reset();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        m_todo.pop_back();
 | 
			
		||||
        if (m_util.str.is_concat(b, e1, e2)) {
 | 
			
		||||
            m_todo.push_back(e1);
 | 
			
		||||
            m_todo.push_back(e2);
 | 
			
		||||
        }
 | 
			
		||||
        else if (m_util.str.is_unit(b, e1)) {
 | 
			
		||||
            m_todo.push_back(e1);
 | 
			
		||||
        }
 | 
			
		||||
        else if (m_util.str.is_nth_i(b, e1, e2)) {
 | 
			
		||||
            m_todo.push_back(e1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   \brief
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -270,6 +161,7 @@ bool theory_seq::occurs(expr* a, expr* b) {
 | 
			
		|||
   TODO: propagate length offsets for last vars
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
#if 0
 | 
			
		||||
bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned idx,
 | 
			
		||||
                                 dependency*& deps, expr_ref_vector & res) {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -346,6 +238,7 @@ bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector cons
 | 
			
		|||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool theory_seq::has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & offset) {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -380,29 +273,9 @@ bool theory_seq::has_len_offset(expr_ref_vector const& ls, expr_ref_vector const
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool theory_seq::len_based_split() {
 | 
			
		||||
 | 
			
		||||
    if (false && m_offset_eq.propagate() && !m_offset_eq.empty()) {
 | 
			
		||||
        // NB: disabled until find_better_rep is handled.
 | 
			
		||||
#if 0
 | 
			
		||||
        for (unsigned i = m_eqs.size(); i-- > 0; ) {
 | 
			
		||||
            eq const& e = m_eqs[i];
 | 
			
		||||
            expr_ref_vector new_ls(m);
 | 
			
		||||
            dependency *deps = e.dep();
 | 
			
		||||
            if (find_better_rep(e.ls, e.rs, i, deps, new_ls)) {
 | 
			
		||||
                expr_ref_vector rs(m);
 | 
			
		||||
                rs.append(e.rs);
 | 
			
		||||
                m_eqs.set(i, eq(m_eq_id++, new_ls, rs, deps));
 | 
			
		||||
                TRACE("seq", display_equation(tout, m_eqs[i]););
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (auto const& e : m_eqs) {
 | 
			
		||||
        if (len_based_split(e)) {
 | 
			
		||||
    for (auto const& e : m_eqs) 
 | 
			
		||||
        if (len_based_split(e)) 
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -670,8 +543,8 @@ bool theory_seq::branch_binary_variable(depeq const& e) {
 | 
			
		|||
    }
 | 
			
		||||
    ptr_vector<expr> xs, ys;
 | 
			
		||||
    expr_ref x(m), y(m);
 | 
			
		||||
    if (!is_binary_eq(e.ls, e.rs, x, xs, ys, y) &&
 | 
			
		||||
        !is_binary_eq(e.rs, e.ls, x, xs, ys, y))
 | 
			
		||||
    if (!m_eq.match_binary_eq(e.ls, e.rs, x, xs, ys, y) &&
 | 
			
		||||
        !m_eq.match_binary_eq(e.rs, e.ls, x, xs, ys, y))
 | 
			
		||||
        return false;
 | 
			
		||||
    if (x == y) {
 | 
			
		||||
        return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -889,10 +762,8 @@ bool theory_seq::can_align_from_rhs(expr_ref_vector const& ls, expr_ref_vector c
 | 
			
		|||
bool theory_seq::branch_ternary_variable_rhs(depeq const& e) {
 | 
			
		||||
    expr_ref_vector xs(m), ys(m);
 | 
			
		||||
    expr_ref x(m), y1(m), y2(m);
 | 
			
		||||
    if (!is_ternary_eq_rhs(e.ls, e.rs, x, xs, y1, ys, y2) &&
 | 
			
		||||
        !is_ternary_eq_rhs(e.rs, e.ls, x, xs, y1, ys, y2)) {
 | 
			
		||||
    if (!m_eq.match_ternary_eq_rhs(e.ls, e.rs, x, xs, y1, ys, y2))
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (m_sk.is_align_l(y1) || m_sk.is_align_r(y1))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -933,8 +804,7 @@ bool theory_seq::branch_ternary_variable_rhs(depeq const& e) {
 | 
			
		|||
bool theory_seq::branch_ternary_variable_lhs(depeq const& e) {
 | 
			
		||||
    expr_ref_vector xs(m), ys(m);
 | 
			
		||||
    expr_ref x(m), y1(m), y2(m);
 | 
			
		||||
    if (!is_ternary_eq_lhs(e.ls, e.rs, xs, x, y1, ys, y2) &&
 | 
			
		||||
        !is_ternary_eq_lhs(e.rs, e.ls, xs, x, y1, ys, y2))
 | 
			
		||||
    if (!m_eq.match_ternary_eq_lhs(e.ls, e.rs, xs, x, y1, ys, y2))
 | 
			
		||||
        return false;
 | 
			
		||||
    if (m_sk.is_align_l(y1) || m_sk.is_align_r(y1))
 | 
			
		||||
        return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -998,7 +868,7 @@ literal theory_seq::mk_alignment(expr* e1, expr* e2) {
 | 
			
		|||
bool theory_seq::branch_quat_variable(depeq const& e) {
 | 
			
		||||
    expr_ref_vector xs(m), ys(m);
 | 
			
		||||
    expr_ref x1(m), x2(m), y1(m), y2(m);
 | 
			
		||||
    if (!is_quat_eq(e.ls, e.rs, x1, xs, x2, y1, ys, y2))
 | 
			
		||||
    if (!m_eq.match_quat_eq(e.ls, e.rs, x1, xs, x2, y1, ys, y2))
 | 
			
		||||
        return false;
 | 
			
		||||
    dependency* dep = e.dep();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1478,150 +1348,7 @@ bool theory_seq::is_unit_eq(expr_ref_vector const& ls, expr_ref_vector const& rs
 | 
			
		|||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   match X abc = defg Y, for abc, defg non-empty
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
bool theory_seq::is_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref& x, ptr_vector<expr>& xs, ptr_vector<expr>& ys, expr_ref& y) {
 | 
			
		||||
    if (ls.size() > 1 && is_var(ls[0]) &&
 | 
			
		||||
        rs.size() > 1 && is_var(rs.back())) {
 | 
			
		||||
        xs.reset();
 | 
			
		||||
        ys.reset();
 | 
			
		||||
        x = ls[0];
 | 
			
		||||
        y = rs.back();
 | 
			
		||||
        for (unsigned i = 1; i < ls.size(); ++i) {
 | 
			
		||||
            if (!m_util.str.is_unit(ls[i])) return false;
 | 
			
		||||
        }
 | 
			
		||||
        for (unsigned i = 0; i < rs.size()-1; ++i) {
 | 
			
		||||
            if (!m_util.str.is_unit(rs[i])) return false;
 | 
			
		||||
        }
 | 
			
		||||
        xs.append(ls.size()-1, ls.c_ptr() + 1);
 | 
			
		||||
        ys.append(rs.size()-1, rs.c_ptr());
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  match: X abc Y..Y' = Z def T..T', where X,Z are variables or concatenation of variables
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
bool theory_seq::is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs,
 | 
			
		||||
                            expr_ref& x1, expr_ref_vector& xs, expr_ref& x2,
 | 
			
		||||
                            expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
 | 
			
		||||
    if (ls.size() > 1 && is_var(ls[0]) && is_var(ls.back()) &&
 | 
			
		||||
        rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {
 | 
			
		||||
        unsigned l_start = 1;
 | 
			
		||||
        sort* srt = ls[0]->get_sort();
 | 
			
		||||
        for (; l_start < ls.size()-1; ++l_start) {
 | 
			
		||||
            if (m_util.str.is_unit(ls[l_start])) break;
 | 
			
		||||
        }
 | 
			
		||||
        if (l_start == ls.size()-1) return false;
 | 
			
		||||
        unsigned l_end = l_start;
 | 
			
		||||
        for (; l_end < ls.size()-1; ++l_end) {
 | 
			
		||||
            if (!m_util.str.is_unit(ls[l_end])) break;
 | 
			
		||||
        }
 | 
			
		||||
        --l_end;
 | 
			
		||||
        if (l_end < l_start) return false;
 | 
			
		||||
        unsigned r_start = 1;
 | 
			
		||||
        for (; r_start < rs.size()-1; ++r_start) {
 | 
			
		||||
            if (m_util.str.is_unit(rs[r_start])) break;
 | 
			
		||||
        }
 | 
			
		||||
        if (r_start == rs.size()-1) return false;
 | 
			
		||||
        unsigned r_end = r_start;
 | 
			
		||||
        for (; r_end < rs.size()-1; ++r_end) {
 | 
			
		||||
            if (!m_util.str.is_unit(rs[r_end])) break;
 | 
			
		||||
        }
 | 
			
		||||
        --r_end;
 | 
			
		||||
        if (r_end < r_start) return false;
 | 
			
		||||
        xs.reset();
 | 
			
		||||
        xs.append(l_end-l_start+1, ls.c_ptr()+l_start);
 | 
			
		||||
        x1 = mk_concat(l_start, ls.c_ptr(), srt);
 | 
			
		||||
        x2 = mk_concat(ls.size()-l_end-1, ls.c_ptr()+l_end+1, srt);
 | 
			
		||||
        ys.reset();
 | 
			
		||||
        ys.append(r_end-r_start+1, rs.c_ptr()+r_start);
 | 
			
		||||
        y1 = mk_concat(r_start, rs.c_ptr(), srt);
 | 
			
		||||
        y2 = mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1, srt);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  match: .. X abc  = .. Y def Z
 | 
			
		||||
         where Z is a variable or concatenation of variables
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
bool theory_seq::is_ternary_eq_rhs(expr_ref_vector const& ls, expr_ref_vector const& rs, 
 | 
			
		||||
                               expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
 | 
			
		||||
    if (ls.size() > 1 && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {
 | 
			
		||||
        sort* srt = ls[0]->get_sort();
 | 
			
		||||
        unsigned l_start = ls.size()-1;
 | 
			
		||||
        for (; l_start > 0; --l_start) {
 | 
			
		||||
            if (!m_util.str.is_unit(ls[l_start])) break;
 | 
			
		||||
        }
 | 
			
		||||
        if (l_start == ls.size()-1) return false;
 | 
			
		||||
        ++l_start;
 | 
			
		||||
        unsigned r_end = rs.size()-2;
 | 
			
		||||
        for (; r_end > 0; --r_end) {
 | 
			
		||||
            if (m_util.str.is_unit(rs[r_end])) break;
 | 
			
		||||
        }
 | 
			
		||||
        if (r_end == 0) return false;
 | 
			
		||||
        unsigned r_start = r_end;
 | 
			
		||||
        for (; r_start > 0; --r_start) {
 | 
			
		||||
            if (!m_util.str.is_unit(rs[r_start])) break;
 | 
			
		||||
        }
 | 
			
		||||
        ++r_start;
 | 
			
		||||
 | 
			
		||||
        xs.reset();
 | 
			
		||||
        xs.append(ls.size()-l_start, ls.c_ptr()+l_start);
 | 
			
		||||
        x = mk_concat(l_start, ls.c_ptr(), srt);
 | 
			
		||||
        ys.reset();
 | 
			
		||||
        ys.append(r_end-r_start+1, rs.c_ptr()+r_start);
 | 
			
		||||
        y1 = mk_concat(r_start, rs.c_ptr(), srt);
 | 
			
		||||
        y2 = mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1, srt);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  match: abc X ..  = Y def Z ..
 | 
			
		||||
         where Y is a variable or concatenation of variables
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
bool theory_seq::is_ternary_eq_lhs(expr_ref_vector const& ls, expr_ref_vector const& rs, 
 | 
			
		||||
                                expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
 | 
			
		||||
    if (ls.size() > 1 && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {
 | 
			
		||||
        sort* srt = ls[0]->get_sort();
 | 
			
		||||
        unsigned l_start = 0;
 | 
			
		||||
        for (; l_start < ls.size()-1; ++l_start) {
 | 
			
		||||
            if (!m_util.str.is_unit(ls[l_start])) break;
 | 
			
		||||
        }
 | 
			
		||||
        if (l_start == 0) return false;
 | 
			
		||||
        unsigned r_start = 1;
 | 
			
		||||
        for (; r_start < rs.size()-1; ++r_start) {
 | 
			
		||||
            if (m_util.str.is_unit(rs[r_start])) break;
 | 
			
		||||
        }
 | 
			
		||||
        if (r_start == rs.size()-1) return false;
 | 
			
		||||
        unsigned r_end = r_start;
 | 
			
		||||
        for (; r_end < rs.size()-1; ++r_end) {
 | 
			
		||||
            if (!m_util.str.is_unit(rs[r_end])) break;
 | 
			
		||||
        }
 | 
			
		||||
        --r_end;
 | 
			
		||||
 | 
			
		||||
        xs.reset();
 | 
			
		||||
        xs.append(l_start, ls.c_ptr());
 | 
			
		||||
        x = mk_concat(ls.size()-l_start, ls.c_ptr()+l_start, srt);
 | 
			
		||||
        ys.reset();
 | 
			
		||||
        ys.append(r_end-r_start+1, rs.c_ptr()+r_start);
 | 
			
		||||
        y1 = mk_concat(r_start, rs.c_ptr(), srt);
 | 
			
		||||
        y2 = mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1, srt);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
struct remove_obj_pair_map : public trail {
 | 
			
		||||
    obj_pair_hashtable<expr, expr> & m_map;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -272,7 +272,7 @@ theory_seq::theory_seq(context& ctx):
 | 
			
		|||
    m_autil(m),
 | 
			
		||||
    m_sk(m, m_rewrite),
 | 
			
		||||
    m_ax(*this, m_rewrite),
 | 
			
		||||
    m_eq(m, m_ax.ax()),
 | 
			
		||||
    m_eq(m, *this, m_ax.ax()),
 | 
			
		||||
    m_regex(*this),
 | 
			
		||||
    m_arith_value(m),
 | 
			
		||||
    m_trail_stack(*this),
 | 
			
		||||
| 
						 | 
				
			
			@ -285,13 +285,6 @@ theory_seq::theory_seq(context& ctx):
 | 
			
		|||
    m_has_seq(m_util.has_seq()),
 | 
			
		||||
    m_new_solution(false),
 | 
			
		||||
    m_new_propagation(false) {
 | 
			
		||||
 | 
			
		||||
    std::function<void(bool, expr_ref_vector const&)> _add_consequence = 
 | 
			
		||||
        [&](bool uses_eq, expr_ref_vector const& clause) { 
 | 
			
		||||
        add_consequence(uses_eq, clause);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    m_eq.set_add_consequence(_add_consequence);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
theory_seq::~theory_seq() {
 | 
			
		||||
| 
						 | 
				
			
			@ -913,8 +906,12 @@ bool theory_seq::simplify_eq(expr_ref_vector& ls, expr_ref_vector& rs, dependenc
 | 
			
		|||
            break;
 | 
			
		||||
        expr_ref li(p.first, m);
 | 
			
		||||
        expr_ref ri(p.second, m);
 | 
			
		||||
        if (solve_unit_eq(li, ri, deps)) {
 | 
			
		||||
            // no-op
 | 
			
		||||
        seq::eq_ptr r;
 | 
			
		||||
        m_eq_deps = deps;
 | 
			
		||||
        if (m_eq.reduce(li, ri, r)) {
 | 
			
		||||
            if (r) {
 | 
			
		||||
                m_eqs.push_back(mk_eqdep(r->ls, r->rs, deps));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (m_util.is_seq(li) || m_util.is_re(li)) {
 | 
			
		||||
            TRACE("seq_verbose", tout << "inserting " << li << " = " << ri << "\n";);
 | 
			
		||||
| 
						 | 
				
			
			@ -1000,15 +997,7 @@ bool theory_seq::reduce_length(expr* l, expr* r, literal_vector& lits) {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
bool theory_seq::is_var(expr* a) const {
 | 
			
		||||
    return
 | 
			
		||||
        m_util.is_seq(a) &&
 | 
			
		||||
        !m_util.str.is_concat(a) &&
 | 
			
		||||
        !m_util.str.is_empty(a)  &&
 | 
			
		||||
        !m_util.str.is_string(a) &&
 | 
			
		||||
        !m_util.str.is_unit(a) &&
 | 
			
		||||
        !m_util.str.is_itos(a) &&
 | 
			
		||||
        !m_util.str.is_nth_i(a) && 
 | 
			
		||||
        !m.is_ite(a);
 | 
			
		||||
    return m_eq.is_var(a);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ Revision History:
 | 
			
		|||
 | 
			
		||||
namespace smt {
 | 
			
		||||
 | 
			
		||||
    class theory_seq : public theory {
 | 
			
		||||
    class theory_seq : public theory, public seq::eq_solver_context {
 | 
			
		||||
        friend class seq_regex;
 | 
			
		||||
 | 
			
		||||
        struct assumption {
 | 
			
		||||
| 
						 | 
				
			
			@ -164,6 +164,15 @@ namespace smt {
 | 
			
		|||
            return depeq(m_eq_id++, ls, rs, dep);
 | 
			
		||||
        }        
 | 
			
		||||
 | 
			
		||||
        depeq mk_eqdep(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep) {
 | 
			
		||||
            expr_ref_vector ls(m), rs(m);
 | 
			
		||||
            for (expr* e : l)
 | 
			
		||||
                m_util.str.get_concat_units(e, ls);
 | 
			
		||||
            for (expr* e : r)
 | 
			
		||||
                m_util.str.get_concat_units(e, rs);
 | 
			
		||||
            return depeq(m_eq_id++, ls, rs, dep);
 | 
			
		||||
        }        
 | 
			
		||||
 | 
			
		||||
        // equalities that are decomposed by conacatenations
 | 
			
		||||
        typedef std::pair<expr_ref_vector, expr_ref_vector> decomposed_eq;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -409,7 +418,6 @@ namespace smt {
 | 
			
		|||
        int find_fst_non_empty_idx(expr_ref_vector const& x);
 | 
			
		||||
        expr* find_fst_non_empty_var(expr_ref_vector const& x);
 | 
			
		||||
        bool has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & diff);
 | 
			
		||||
        bool find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned idx, dependency*& deps, expr_ref_vector & res);
 | 
			
		||||
        
 | 
			
		||||
        // final check 
 | 
			
		||||
        bool simplify_and_solve_eqs();   // solve unitary equalities
 | 
			
		||||
| 
						 | 
				
			
			@ -456,17 +464,11 @@ namespace smt {
 | 
			
		|||
        bool solve_eq(unsigned idx);
 | 
			
		||||
        bool simplify_eq(expr_ref_vector& l, expr_ref_vector& r, dependency* dep);
 | 
			
		||||
        bool lift_ite(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep);
 | 
			
		||||
        bool solve_unit_eq(expr* l, expr* r, dependency* dep);
 | 
			
		||||
        bool solve_unit_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep);
 | 
			
		||||
        bool solve_nth_eq1(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep);
 | 
			
		||||
        obj_pair_hashtable<expr, expr> m_nth_eq2_cache;
 | 
			
		||||
        bool solve_nth_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep);
 | 
			
		||||
        bool solve_itos(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep);
 | 
			
		||||
        bool solve_itos(expr* n, expr_ref_vector const& rs, dependency* dep);
 | 
			
		||||
        bool is_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, expr_ref& x, ptr_vector<expr>& xunits, ptr_vector<expr>& yunits, expr_ref& y);
 | 
			
		||||
        bool is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref& x1, expr_ref_vector& xs, expr_ref& x2, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2);
 | 
			
		||||
        bool is_ternary_eq_rhs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2);
 | 
			
		||||
        bool is_ternary_eq_lhs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2);
 | 
			
		||||
 | 
			
		||||
        bool solve_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep);
 | 
			
		||||
        bool propagate_max_length(expr* l, expr* r, dependency* dep);
 | 
			
		||||
| 
						 | 
				
			
			@ -538,8 +540,6 @@ namespace smt {
 | 
			
		|||
        bool assume_equality(expr* l, expr* r);
 | 
			
		||||
 | 
			
		||||
        // variable solving utilities
 | 
			
		||||
        bool occurs(expr* a, expr* b);
 | 
			
		||||
        bool occurs(expr* a, expr_ref_vector const& b);
 | 
			
		||||
        bool is_var(expr* b) const;
 | 
			
		||||
        bool add_solution(expr* l, expr* r, dependency* dep);
 | 
			
		||||
        bool is_unit_nth(expr* a) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -558,7 +558,6 @@ namespace smt {
 | 
			
		|||
        void deque_axiom(expr* e);
 | 
			
		||||
        void add_axiom(literal l1, literal l2 = null_literal, literal l3 = null_literal, literal l4 = null_literal, literal l5 = null_literal);        
 | 
			
		||||
        void add_axiom(literal_vector& lits);
 | 
			
		||||
        void add_consequence(bool uses_eq, expr_ref_vector const& clause);
 | 
			
		||||
        
 | 
			
		||||
        bool has_length(expr *e) const { return m_has_length.contains(e); }
 | 
			
		||||
        void add_length(expr* e, expr* l);
 | 
			
		||||
| 
						 | 
				
			
			@ -591,7 +590,6 @@ namespace smt {
 | 
			
		|||
        bool lower_bound(expr* s, rational& lo) const;
 | 
			
		||||
        bool lower_bound2(expr* s, rational& lo);
 | 
			
		||||
        bool upper_bound(expr* s, rational& hi) const;
 | 
			
		||||
        bool get_length(expr* s, rational& val);
 | 
			
		||||
 | 
			
		||||
        void mk_decompose(expr* e, expr_ref& head, expr_ref& tail);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -633,6 +631,11 @@ namespace smt {
 | 
			
		|||
        void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { }
 | 
			
		||||
        void unmerge_eh(theory_var v1, theory_var v2) {}
 | 
			
		||||
 | 
			
		||||
        // eq_solver callbacks
 | 
			
		||||
        void add_consequence(bool uses_eq, expr_ref_vector const& clause) override;
 | 
			
		||||
        void  add_solution(expr* var, expr* term) override { SASSERT(var != term); add_solution(var, term, m_eq_deps); }
 | 
			
		||||
        expr* expr2rep(expr* e) override;
 | 
			
		||||
        bool  get_length(expr* e, rational& r) override;
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue