diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 877263e79..ee3d3aa71 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -833,18 +833,11 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { unsigned len = 0; unsigned j = 0; for (expr* e : m_es) { - if (str().is_string(e, b)) { - len += b.length(); - } - else if (str().is_unit(e)) { - len += 1; - } - else if (str().is_empty(e)) { - // skip - } - else { - m_es[j++] = e; - } + auto [bounded, len_e] = min_length(e); + if (bounded) + len += len_e; + else + m_es[j++] = e; } if (j == 0) { result = m_autil.mk_int(len); @@ -1091,15 +1084,14 @@ expr_ref seq_rewriter::mk_len(rational const& p, expr_ref_vector const& xs) { } bool seq_rewriter::extract_pop_suffix(expr_ref_vector const& as, expr* b, expr* c, expr_ref& result) { - unsigned len_a1 = 0, len_a2 = 0; - min_length(as, len_a1); + auto len_a1 = min_length(as).second; rational pos, len; if (!as.empty() && m_autil.is_numeral(b, pos) && m_autil.is_numeral(c, len) && len_a1 >= pos + len && pos >= 0 && len >= 0) { unsigned i = 0; len_a1 = 0; for ( ; i < as.size() && len_a1 < pos + len; ++i) { - min_length(as.get(i), len_a2); + auto len_a2 = min_length(as.get(i)).second; len_a1 += len_a2; } if (i < as.size()) { @@ -1449,10 +1441,9 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { return BR_DONE; } - unsigned lenA = 0, lenB = 0; - bool lA = min_length(as, lenA); + auto [lA, lenA] = min_length(as); if (lA) { - min_length(bs, lenB); + auto lenB = min_length(bs).second; if (lenB > lenA) { result = m().mk_false(); return BR_DONE; @@ -1642,6 +1633,16 @@ br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) { } } + auto [bounded_a, len_a] = min_length(a); + + if (bounded_a && m_autil.is_numeral(b, pos1)) { + if (0 <= pos1 && pos1 < len_a) + result = str().mk_nth_i(a, b); + else + result = str().mk_nth_u(a, b); + return BR_REWRITE_FULL; + } + expr* la = str().mk_length(a); result = m().mk_ite(m().mk_and(m_autil.mk_ge(b, zero()), m().mk_not(m_autil.mk_le(la, b))), str().mk_nth_i(a, b), @@ -1656,10 +1657,10 @@ br_status seq_rewriter::mk_seq_nth_i(expr* a, expr* b, expr_ref& result) { if (!m_autil.is_numeral(b, r) || !r.is_unsigned()) { return BR_FAILED; } - unsigned len = r.get_unsigned(); + unsigned offset = r.get_unsigned(); expr* a2, *i2; - if (len == 0 && str().is_at(a, a2, i2) && m_autil.is_numeral(i2, r) && r.is_zero()) { + if (offset == 0 && str().is_at(a, a2, i2) && m_autil.is_numeral(i2, r) && r.is_zero()) { result = str().mk_nth_i(a2, i2); return BR_REWRITE1; } @@ -1674,18 +1675,35 @@ br_status seq_rewriter::mk_seq_nth_i(expr* a, expr* b, expr_ref& result) { expr_ref_vector as(m()); str().get_concat_units(a, as); + expr* cond = nullptr, *el = nullptr, *th = nullptr; for (unsigned i = 0; i < as.size(); ++i) { expr* a = as.get(i), *u = nullptr; if (str().is_unit(a, u)) { - if (len == i) { + if (offset == i) { result = u; return BR_DONE; - } + } + continue; } - else { - return BR_FAILED; + else if (m().is_ite(a, cond, th, el)) { + auto [bounded, len1] = min_length(a); + if (!bounded) + break; + if (i + len1 < offset) { + offset -= len1; + continue; + } + expr_ref idx(m()); + idx = m_autil.mk_int(offset - i); + th = str().mk_nth_i(th, idx); + el = str().mk_nth_i(el, idx); + result = m().mk_ite(cond, th, el); + return BR_REWRITE2; } + else + break; } + return BR_FAILED; } @@ -1915,9 +1933,8 @@ br_status seq_rewriter::mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& resu // a = "", |b| > 0 -> replace("",b,c) = "" if (m_lhs.empty()) { - unsigned len = 0; str().get_concat(b, m_lhs); - min_length(m_lhs, len); + unsigned len = min_length(m_lhs).second; if (len > 0) { result = a; return BR_DONE; @@ -2301,10 +2318,9 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { } - unsigned len_a; - rational len_b; - if (max_length(b, len_b)) { - min_length(a, len_a); + auto [bounded_b, len_b] = max_length(b); + if (bounded_b) { + auto [bounded_a, len_a] = min_length(a); if (len_b <= len_a) { result = m().mk_eq(a, b); return BR_REWRITE1; @@ -2379,10 +2395,9 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { result = str().mk_suffix(a1, b); return BR_DONE; } - unsigned len_a; - rational len_b; - if (max_length(b, len_b)) { - min_length(a, len_a); + auto [bounded_b, len_b] = max_length(b); + if (bounded_b) { + auto [bounded_a, len_a] = min_length(a); if (len_b <= len_a) { result = m().mk_eq(a, b); return BR_REWRITE1; @@ -5011,21 +5026,20 @@ br_status seq_rewriter::mk_re_star(expr* a, expr_ref& result) { br_status seq_rewriter::mk_re_range(expr* lo, expr* hi, expr_ref& result) { zstring s; unsigned len = 0; - rational rlen; bool is_empty = false; if (str().is_string(lo, s) && s.length() != 1) is_empty = true; if (str().is_string(hi, s) && s.length() != 1) is_empty = true; - min_length(lo, len); + len = min_length(lo).second; if (len > 1) is_empty = true; - min_length(hi, len); + len = min_length(hi).second; if (len > 1) is_empty = true; - if (max_length(lo, rlen) && rlen == 0) + if (max_length(lo) == std::make_pair(true, rational(0))) is_empty = true; - if (max_length(hi, rlen) && rlen == 0) + if (max_length(hi) == std::make_pair(true, rational(0))) is_empty = true; if (is_empty) { sort* srt = re().mk_re(lo->get_sort()); @@ -5631,10 +5645,13 @@ bool seq_rewriter::set_empty(unsigned sz, expr* const* es, bool all, expr_ref_pa } lbool seq_rewriter::eq_length(expr* x, expr* y) { - unsigned xl = 0, yl = 0; - if (min_length(x, xl) && min_length(y, yl)) - return xl == yl ? l_true : l_false; - return l_undef; + auto [bounded_x, xl] = min_length(x); + if (!bounded_x) + return l_undef; + auto [bounded_y, yl] = min_length(y); + if (!bounded_y) + return l_undef; + return xl == yl ? l_true : l_false; } /*** @@ -5643,13 +5660,66 @@ lbool seq_rewriter::eq_length(expr* x, expr* y) { maximal length (the sequence is bounded). */ -bool seq_rewriter::min_length(unsigned sz, expr* const* ss, unsigned& len) { - ptr_buffer es; +std::pair seq_rewriter::min_length(unsigned sz, expr* const* ss) { + ptr_buffer es, sub; for (unsigned i = 0; i < sz; ++i) es.push_back(ss[i]); + obj_map> cache; + zstring s; - len = 0; + unsigned len = 0; bool bounded = true; + expr* c, *th, *el; + auto visit = [&](expr* e) { + if (cache.contains(e)) + return true; + if (str().is_unit(e)) { + cache.insert(e, { true, 1u }); + return true; + } + else if (str().is_empty(e)) { + cache.insert(e, { true, 0u }); + return true; + } + else if (str().is_string(e, s)) { + cache.insert(e, { true, s.length() }); + return true; + } + else if (str().is_concat(e)) { + bool visited = true; + std::pair result(true, 0u), r; + for (expr* arg : *to_app(e)) { + if (cache.find(arg, r)) { + result.first &= r.first; + result.second += r.second; + } + else { + sub.push_back(arg); + visited = false; + } + } + if (visited) + cache.insert(e, result); + return visited; + } + else if (m().is_ite(e, c, th, el)) { + unsigned sz = sub.size(); + std::pair r1, r2; + if (!cache.find(th, r1)) + sub.push_back(th); + if (!cache.find(el, r2)) + sub.push_back(el); + if (sz != sub.size()) + return false; + + cache.insert(e, { r1.first && r2.first && r1.second == r2.second, std::min(r1.second, r2.second)}); + return true; + } + else { + cache.insert(e, { false, 0u }); + return true; + } + }; while (!es.empty()) { expr* e = es.back(); es.pop_back(); @@ -5662,24 +5732,38 @@ bool seq_rewriter::min_length(unsigned sz, expr* const* ss, unsigned& len) { else if (str().is_concat(e)) for (expr* arg : *to_app(e)) es.push_back(arg); + else if (m().is_ite(e, c, th, el)) { + sub.push_back(th); + sub.push_back(el); + while (!sub.empty()) { + e = sub.back(); + if (visit(e)) + sub.pop_back(); + } + auto [bounded1, len1] = cache[th]; + auto [bounded2, len2] = cache[el]; + if (!bounded1 || !bounded2 || len1 != len2) + bounded = false; + len += std::min(len1, len2); + } else bounded = false; } - return bounded; + return { bounded, len }; } -bool seq_rewriter::min_length(expr* e, unsigned& len) { - return min_length(1, &e, len); +std::pair seq_rewriter::min_length(expr* e) { + return min_length(1, &e); } -bool seq_rewriter::min_length(expr_ref_vector const& es, unsigned& len) { - return min_length(es.size(), es.data(), len); +std::pair seq_rewriter::min_length(expr_ref_vector const& es) { + return min_length(es.size(), es.data()); } -bool seq_rewriter::max_length(expr* e, rational& len) { +std::pair seq_rewriter::max_length(expr* e) { ptr_buffer es; es.push_back(e); - len = 0; + rational len(0); zstring s; expr* s1 = nullptr, *i = nullptr, *l = nullptr; rational n; @@ -5702,9 +5786,9 @@ bool seq_rewriter::max_length(expr* e, rational& len) { es.push_back(arg); } else - return false; + return std::make_pair(false, len); } - return true; + return std::make_pair(true, len); } @@ -5806,9 +5890,8 @@ bool seq_rewriter::reduce_by_length(expr_ref_vector& ls, expr_ref_vector& rs, if (ls.empty() && rs.empty()) return true; - unsigned len1 = 0, len2 = 0; - bool bounded1 = min_length(ls, len1); - bool bounded2 = min_length(rs, len2); + auto [bounded1, len1] = min_length(ls); + auto [bounded2, len2] = min_length(rs); if (bounded1 && len1 < len2) return false; if (bounded2 && len2 < len1) diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 500972b1f..eecf032b6 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -324,10 +324,11 @@ class seq_rewriter { bool reduce_by_length(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs); bool reduce_itos(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs); bool reduce_eq_empty(expr* l, expr* r, expr_ref& result); - bool min_length(expr_ref_vector const& es, unsigned& len); - bool min_length(expr* e, unsigned& len); - bool min_length(unsigned sz, expr* const* es, unsigned& len); - bool max_length(expr* e, rational& len); + std::pair min_length(expr_ref_vector const& es); + std::pair min_length(expr* e); + std::pair min_length(unsigned sz, expr* const* es); + std::pair max_length(expr* e); + bool max_length(expr* e, rational& len) { auto ml = max_length(e); len = ml.second; return ml.first; } lbool eq_length(expr* x, expr* y); expr* concat_non_empty(expr_ref_vector& es); bool reduce_by_char(expr_ref& r, expr* ch, unsigned depth); diff --git a/src/opt/opt_preprocess.cpp b/src/opt/opt_preprocess.cpp index 268aa8986..d3475fcb0 100644 --- a/src/opt/opt_preprocess.cpp +++ b/src/opt/opt_preprocess.cpp @@ -78,6 +78,8 @@ namespace opt { new_soft.remove(f); continue; } + if (!m.inc()) + return false; expr_ref_vector mux(m); for (expr* g : trail) { @@ -160,9 +162,7 @@ namespace opt { for (auto& mux : mutexes) process_mutex(mux, new_soft, lower); - - if (mutexes.empty()) - { + if (mutexes.empty()) { obj_map dual_soft = dualize(new_soft, fmls); mutexes.reset(); lbool is_sat = s.find_mutexes(fmls, mutexes);