#include "util/uint_set.h"
#include "ast/rewriter/seq_rewriter.h"
#include "ast/rewriter/seq_regex_bisim.h"
#include "ast/rewriter/seq_range_collapse.h"
#include "ast/rewriter/seq_regex_witness.h"
#include "ast/arith_decl_plugin.h"
#include "ast/array_decl_plugin.h"
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/ast_util.h"
#include "ast/well_sorted.h"
#include "ast/rewriter/var_subst.h"
#include "ast/rewriter/expr_safe_replace.h"
#include "params/seq_rewriter_params.hpp"
void seq_rewriter::updt_params(params_ref const & p) {
seq_rewriter_params sp(p);
m_coalesce_chars = sp.coalesce_chars();
m_max_power_expansion = sp.max_power_expansion();
}
void seq_rewriter::get_param_descrs(param_descrs & r) {
seq_rewriter_params::collect_param_descrs(r);
}
br_status seq_rewriter::mk_bool_app(func_decl* f, unsigned n, expr* const* args, expr_ref& result) {
switch (f->get_decl_kind()) {
case OP_AND:
return mk_bool_app_helper(true, n, args, result);
case OP_OR:
return mk_bool_app_helper(false, n, args, result);
case OP_EQ:
SASSERT(n == 2);
Z3_fallthrough;
default:
return BR_FAILED;
}
}
br_status seq_rewriter::mk_bool_app_helper(bool is_and, unsigned n, expr* const* args, expr_ref& result) {
bool found = false;
expr* arg = nullptr;
for (unsigned i = 0; i < n && !found; ++i) {
found = str().is_in_re(args[i]) || (m().is_not(args[i], arg) && str().is_in_re(arg));
}
if (!found) return BR_FAILED;
obj_map<expr, expr*> in_re, not_in_re;
bool found_pair = false;
ptr_buffer<expr> new_args;
for (unsigned i = 0; i < n; ++i) {
expr* args_i = args[i];
expr* x = nullptr, *y = nullptr, *z = nullptr;
if (str().is_in_re(args_i, x, y) && !str().is_empty(x)) {
if (in_re.find(x, z)) {
in_re[x] = is_and ? re().mk_inter(z, y) : re().mk_union(z, y);
found_pair = true;
}
else {
in_re.insert(x, y);
found_pair |= not_in_re.contains(x);
}
}
else if (m().is_not(args_i, arg) && str().is_in_re(arg, x, y) && !str().is_empty(x)) {
if (not_in_re.find(x, z)) {
not_in_re[x] = is_and ? re().mk_union(z, y) : re().mk_inter(z, y);
found_pair = true;
}
else {
not_in_re.insert(x, y);
found_pair |= in_re.contains(x);
}
}
else
new_args.push_back(args_i);
}
if (!found_pair) {
return BR_FAILED;
}
for (auto const & kv : in_re) {
expr* x = kv.m_key;
expr* y = kv.m_value;
expr* z = nullptr;
if (not_in_re.find(x, z)) {
expr* z_c = re().mk_complement(z);
expr* w = is_and ? re().mk_inter(y, z_c) : re().mk_union(y, z_c);
new_args.push_back(re().mk_in_re(x, w));
}
else {
new_args.push_back(re().mk_in_re(x, y));
}
}
for (auto const& kv : not_in_re) {
expr* x = kv.m_key;
expr* y = kv.m_value;
if (!in_re.contains(x)) {
new_args.push_back(re().mk_in_re(x, re().mk_complement(y)));
}
}
result = is_and ? m().mk_and(new_args) : m().mk_or(new_args);
return BR_REWRITE_FULL;
}
br_status seq_rewriter::mk_eq_helper(expr* a, expr* b, expr_ref& result) {
expr* sa = nullptr, *ra = nullptr, *sb = nullptr, *rb = nullptr;
if (str().is_in_re(b))
std::swap(a, b);
if (!str().is_in_re(a, sa, ra))
return BR_FAILED;
bool is_not = m().is_not(b, b);
if (!str().is_in_re(b, sb, rb))
return BR_FAILED;
if (sa != sb)
return BR_FAILED;
if (is_not)
rb = re().mk_complement(rb);
auto a_ = re().mk_inter(ra, rb);
auto b_ = re().mk_complement(ra);
expr* r = re().mk_union(a_, re().mk_inter(b_, re().mk_complement(rb)));
result = re().mk_in_re(sa, r);
return BR_REWRITE_FULL;
}
br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
SASSERT(f->get_family_id() == get_fid());
br_status st = BR_FAILED;
switch(f->get_decl_kind()) {
case OP_SEQ_UNIT:
SASSERT(num_args == 1);
st = mk_seq_unit(args[0], result);
break;
case OP_SEQ_EMPTY:
return BR_FAILED;
case OP_RE_PLUS:
SASSERT(num_args == 1);
st = mk_re_plus(args[0], result);
break;
case OP_RE_STAR:
SASSERT(num_args == 1);
st = mk_re_star(args[0], result);
break;
case OP_RE_OPTION:
SASSERT(num_args == 1);
st = mk_re_opt(args[0], result);
break;
case OP_RE_REVERSE:
SASSERT(num_args == 1);
st = mk_re_reverse(args[0], result);
break;
case OP_RE_DERIVATIVE:
SASSERT(num_args == 2);
st = mk_re_derivative(args[0], args[1], result);
break;
case OP_RE_CONCAT:
if (num_args == 1) {
result = args[0];
st = BR_DONE;
}
else {
SASSERT(num_args == 2);
st = mk_re_concat(args[0], args[1], result);
}
break;
case OP_RE_UNION:
if (num_args == 1) {
result = args[0];
st = BR_DONE;
}
else {
SASSERT(num_args == 2);
st = mk_re_union(args[0], args[1], result);
}
break;
case OP_RE_RANGE:
SASSERT(num_args == 2);
st = mk_re_range(args[0], args[1], result);
break;
case OP_RE_DIFF:
if (num_args == 2)
st = mk_re_diff(args[0], args[1], result);
else if (num_args == 1) {
result = args[0];
st = BR_DONE;
}
break;
case OP_RE_XOR:
if (num_args == 2)
st = mk_re_xor(args[0], args[1], result);
else if (num_args == 1) {
result = args[0];
st = BR_DONE;
}
break;
case OP_RE_INTERSECT:
if (num_args == 1) {
result = args[0];
st = BR_DONE;
}
else {
SASSERT(num_args == 2);
st = mk_re_inter(args[0], args[1], result);
}
break;
case OP_RE_COMPLEMENT:
SASSERT(num_args == 1);
st = mk_re_complement(args[0], result);
break;
case OP_RE_LOOP:
st = mk_re_loop(f, num_args, args, result);
break;
case OP_RE_POWER:
st = mk_re_power(f, args[0], result);
break;
case OP_RE_EMPTY_SET:
return BR_FAILED;
case OP_RE_FULL_SEQ_SET:
return BR_FAILED;
case OP_RE_FULL_CHAR_SET:
return BR_FAILED;
case OP_RE_OF_PRED:
return BR_FAILED;
case _OP_SEQ_SKOLEM:
return BR_FAILED;
case OP_SEQ_CONCAT:
if (num_args == 1) {
result = args[0];
st = BR_DONE;
}
else {
SASSERT(num_args == 2);
st = mk_seq_concat(args[0], args[1], result);
}
break;
case OP_SEQ_LENGTH:
SASSERT(num_args == 1);
st = mk_seq_length(args[0], result);
break;
case OP_SEQ_POWER:
SASSERT(num_args == 2);
st = mk_seq_power(args[0], args[1], result);
break;
case OP_SEQ_EXTRACT:
SASSERT(num_args == 3);
st = mk_seq_extract(args[0], args[1], args[2], result);
break;
case OP_SEQ_CONTAINS:
SASSERT(num_args == 2);
st = mk_seq_contains(args[0], args[1], result);
break;
case OP_SEQ_AT:
SASSERT(num_args == 2);
st = mk_seq_at(args[0], args[1], result);
break;
case OP_SEQ_NTH:
SASSERT(num_args == 2);
return mk_seq_nth(args[0], args[1], result);
case OP_SEQ_NTH_I:
SASSERT(num_args == 2);
return mk_seq_nth_i(args[0], args[1], result);
case OP_SEQ_PREFIX:
SASSERT(num_args == 2);
st = mk_seq_prefix(args[0], args[1], result);
break;
case OP_SEQ_SUFFIX:
SASSERT(num_args == 2);
st = mk_seq_suffix(args[0], args[1], result);
break;
case OP_SEQ_INDEX:
if (num_args == 2) {
expr_ref arg3(zero(), m());
result = str().mk_index(args[0], args[1], arg3);
st = BR_REWRITE1;
}
else {
SASSERT(num_args == 3);
st = mk_seq_index(args[0], args[1], args[2], result);
}
break;
case OP_SEQ_LAST_INDEX:
SASSERT(num_args == 2);
st = mk_seq_last_index(args[0], args[1], result);
break;
case OP_SEQ_REPLACE:
SASSERT(num_args == 3);
st = mk_seq_replace(args[0], args[1], args[2], result);
break;
case OP_SEQ_REPLACE_ALL:
SASSERT(num_args == 3);
st = mk_seq_replace_all(args[0], args[1], args[2], result);
break;
case OP_SEQ_MAP:
SASSERT(num_args == 2);
st = mk_seq_map(args[0], args[1], result);
break;
case OP_SEQ_MAPI:
SASSERT(num_args == 3);
st = mk_seq_mapi(args[0], args[1], args[2], result);
break;
case OP_SEQ_FOLDL:
SASSERT(num_args == 3);
st = mk_seq_foldl(args[0], args[1], args[2], result);
break;
case OP_SEQ_FOLDLI:
SASSERT(num_args == 4);
st = mk_seq_foldli(args[0], args[1], args[2], args[3], result);
break;
case OP_SEQ_REPLACE_RE:
SASSERT(num_args == 3);
st = mk_seq_replace_re(args[0], args[1], args[2], result);
break;
case OP_SEQ_REPLACE_RE_ALL:
SASSERT(num_args == 3);
st = mk_seq_replace_re_all(args[0], args[1], args[2], result);
break;
case OP_SEQ_TO_RE:
SASSERT(num_args == 1);
st = mk_str_to_regexp(args[0], result);
break;
case OP_SEQ_IN_RE:
SASSERT(num_args == 2);
st = mk_str_in_regexp(args[0], args[1], result);
break;
case OP_STRING_LE:
SASSERT(num_args == 2);
st = mk_str_le(args[0], args[1], result);
break;
case OP_STRING_LT:
SASSERT(num_args == 2);
st = mk_str_lt(args[0], args[1], result);
break;
case OP_STRING_FROM_CODE:
SASSERT(num_args == 1);
st = mk_str_from_code(args[0], result);
break;
case OP_STRING_TO_CODE:
SASSERT(num_args == 1);
st = mk_str_to_code(args[0], result);
break;
case OP_STRING_IS_DIGIT:
SASSERT(num_args == 1);
st = mk_str_is_digit(args[0], result);
break;
case OP_STRING_CONST:
st = BR_FAILED;
if (!m_coalesce_chars) {
st = mk_str_units(f, result);
}
break;
case OP_STRING_ITOS:
SASSERT(num_args == 1);
st = mk_str_itos(args[0], result);
break;
case OP_STRING_STOI:
SASSERT(num_args == 1);
st = mk_str_stoi(args[0], result);
break;
case OP_STRING_UBVTOS:
SASSERT(num_args == 1);
st = mk_str_ubv2s(args[0], result);
break;
case OP_STRING_SBVTOS:
SASSERT(num_args == 1);
st = mk_str_sbv2s(args[0], result);
break;
case _OP_STRING_CONCAT:
case _OP_STRING_PREFIX:
case _OP_STRING_SUFFIX:
case _OP_STRING_STRCTN:
case _OP_STRING_LENGTH:
case _OP_STRING_CHARAT:
case _OP_STRING_IN_REGEXP:
case _OP_STRING_TO_REGEXP:
case _OP_STRING_SUBSTR:
case _OP_STRING_STRREPL:
case _OP_STRING_STRIDOF:
UNREACHABLE();
}
if (st == BR_FAILED) {
st = lift_ites_throttled(f, num_args, args, result);
}
CTRACE(seq_verbose, st != BR_FAILED, tout << expr_ref(m().mk_app(f, num_args, args), m()) << " -> " << result << "\n";);
SASSERT(st == BR_FAILED || result->get_sort() == f->get_range());
return st;
}
br_status seq_rewriter::mk_seq_unit(expr* e, expr_ref& result) {
unsigned ch;
if (m_util.is_const_char(e, ch) && m_coalesce_chars) {
zstring s(ch);
TRACE(seq_verbose, tout << "rewrite seq.unit of 8-bit value " << ch << " to string constant \"" << s<< "\"" << std::endl;);
result = str().mk_string(s);
return BR_DONE;
}
return BR_FAILED;
}
expr_ref seq_rewriter::mk_seq_concat(expr* a, expr* b) {
expr_ref result(m());
if (BR_FAILED == mk_seq_concat(a, b, result))
result = str().mk_concat(a, b);
return result;
}
br_status seq_rewriter::mk_seq_concat(expr* a, expr* b, expr_ref& result) {
zstring s1, s2;
expr* c, *d;
bool isc1 = str().is_string(a, s1) && m_coalesce_chars;
bool isc2 = str().is_string(b, s2) && m_coalesce_chars;
if (!(isc1 && isc2) &&
!str().is_concat(a) &&
!str().is_empty(a) &&
!str().is_empty(b) &&
!(isc1 && str().is_concat(b, c, d) && str().is_string(c, s2)))
return BR_FAILED;
if (isc1 && isc2) {
result = str().mk_string(s1 + s2);
return BR_DONE;
}
ptr_vector<expr> es, todo;
todo.push_back(b);
todo.push_back(a);
while (!todo.empty()) {
expr* e = todo.back();
todo.pop_back();
expr* e1 = nullptr, *e2 = nullptr;
if (str().is_concat(e, e1, e2)) {
todo.push_back(e2);
todo.push_back(e1);
continue;
}
if (str().is_empty(e))
continue;
es.push_back(e);
}
if (es.empty()) {
result = str().mk_empty(a->get_sort());
return BR_DONE;
}
unsigned j = 0;
for (unsigned i = 0; i < es.size(); ++i) {
zstring t1, t2;
if (j > 0 && m_coalesce_chars && str().is_string(es.get(j - 1), t1) && str().is_string(es.get(i), t2))
es[j - 1] = str().mk_string(t1 + t2);
else
es[j++] = es.get(i);
}
es.shrink(j);
expr_ref cur(es.back(), m());
for (unsigned i = es.size() - 1; i-- > 0; )
cur = str().mk_concat(es.get(i), cur);
result = cur;
return BR_DONE;
}
bool seq_rewriter::mk_seq_reverse(expr* s, expr_ref& result) {
ptr_vector<expr> elems, todo;
todo.push_back(s);
while (!todo.empty()) {
expr* e = todo.back();
todo.pop_back();
if (str().is_concat(e)) { app* a = to_app(e); for (expr* arg : *a) todo.push_back(arg);
continue;
}
if (str().is_empty(e))
continue;
elems.push_back(e);
}
zstring zs;
expr_ref_vector es(m());
for (expr* e : elems) {
if (str().is_unit(e))
es.push_back(e); else if (str().is_string(e, zs))
es.push_back(str().mk_string(zs.reverse()));
else
return false; } result = str().mk_concat(es, s->get_sort());
return true;
}
br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
zstring b;
rational r;
m_es.reset();
str().get_concat(a, m_es);
unsigned len = 0;
unsigned j = 0;
for (expr* e : m_es) {
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);
return BR_DONE;
}
if (j != m_es.size() || j != 1) {
expr_ref_vector es(m());
for (unsigned i = 0; i < j; ++i) {
es.push_back(str().mk_length(m_es.get(i)));
}
if (len != 0) {
es.push_back(m_autil.mk_int(len));
}
result = m_autil.mk_add(es.size(), es.data());
return BR_REWRITE2;
}
expr* x = nullptr, *y = nullptr, *z = nullptr;
if (str().is_replace(a, x, y, z) && l_true == eq_length(y, z)) {
result = str().mk_length(x);
return BR_REWRITE1;
}
if (str().is_map(a, x, y)) {
result = str().mk_length(y);
return BR_REWRITE1;
}
if (str().is_mapi(a, x, y, z)) {
result = str().mk_length(z);
return BR_REWRITE1;
}
if (str().is_power(a, x, y) && m_autil.is_numeral(y, r) && r >= 0) {
result = m_autil.mk_mul(y, str().mk_length(x));
return BR_REWRITE2;
}
if (str().is_extract(a, x, y, z) &&
m_autil.is_numeral(y, r) && r.is_zero() &&
m_autil.is_numeral(z, r) && r >= 0) {
expr* len_x = str().mk_length(x);
result = m().mk_ite(m_autil.mk_le(len_x, z), len_x, z);
return BR_REWRITE_FULL;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_power(expr* a, expr* b, expr_ref& result) {
expr* s = nullptr, *k = nullptr;
rational n, kv;
if (str().is_empty(a)) {
result = a;
return BR_DONE;
}
bool is_num = m_autil.is_numeral(b, n);
if (is_num && !n.is_pos()) {
result = str().mk_empty(a->get_sort());
return BR_DONE;
}
if (is_num && n.is_one()) {
result = a;
return BR_DONE;
}
if (str().is_power(a, s, k) &&
(is_num || (m_autil.is_numeral(k, kv) && kv.is_pos()))) {
result = str().mk_power(s, m_autil.mk_mul(k, b));
return BR_REWRITE_FULL;
}
if (!is_num || n > rational(m_max_power_expansion))
return BR_FAILED;
unsigned v = n.get_unsigned();
zstring t;
if (str().is_string(a, t)) {
if (!(n * rational(t.length())).is_unsigned())
return BR_FAILED;
zstring r = t;
for (unsigned i = 1; i < v; ++i)
r += t;
result = str().mk_string(r);
return BR_DONE;
}
expr_ref_vector es(m());
for (unsigned i = 0; i < v; ++i)
es.push_back(a);
result = str().mk_concat(es, a->get_sort());
return BR_REWRITE_FULL;
}
expr_ref seq_rewriter::mk_seq_first(expr* t) {
expr_ref result(m());
expr* s, * j, * k;
if (str().is_extract(t, s, j, k))
result = str().mk_nth_i(s, j);
else
result = str().mk_nth_c(t, 0);
return result;
}
expr_ref seq_rewriter::mk_sub(expr* a, rational const& n) {
expr* a1, *a2;
SASSERT(n.is_int());
rational k;
if (m_autil.is_sub(a, a1, a2) && m_autil.is_numeral(a2, k))
return expr_ref(m_autil.mk_sub(a1, m_autil.mk_int(k + n)), m());
if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a2, k))
return expr_ref(m_autil.mk_add(a1, m_autil.mk_int(k - n)), m());
if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a1, k))
return expr_ref(m_autil.mk_add(a2, m_autil.mk_int(k - n)), m());
return expr_ref(m_autil.mk_sub(a, m_autil.mk_int(n)), m());
}
expr_ref seq_rewriter::mk_seq_rest(expr* t) {
expr_ref result(m());
expr* s, * j, * k;
rational jv;
if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, jv) && jv >= 0) {
auto a = m_autil.mk_int(jv + 1);
result = str().mk_substr(s, a, mk_sub(k, 1));
}
else {
auto a = one();
result = str().mk_substr(t, a, mk_sub(str().mk_length(t), 1));
}
return result;
}
expr_ref seq_rewriter::mk_seq_last(expr* t) {
expr_ref result(m());
expr* s, * j, * k, * s_, * len_s;
rational jv, i;
if (str().is_extract(t, s, j, k) &&
m_autil.is_numeral(j, jv) && jv >= 0 &&
str().is_len_sub(k, len_s, s_, i) &&
s == s_ && jv == i) {
expr_ref lastpos = mk_sub(len_s, 1);
result = str().mk_nth_i(s, lastpos);
}
else {
expr_ref lhs(str().mk_length(t), m());
auto rhs = one();
result = str().mk_nth_i(t, m_autil.mk_sub(lhs, rhs));
}
return result;
}
expr_ref seq_rewriter::mk_seq_butlast(expr* t) {
auto b = zero();
auto c = str().mk_length(t);
auto a = str().mk_substr(t, b, m_autil.mk_sub(c, one()));
return expr_ref(a, m());
}
br_status seq_rewriter::lift_ites_throttled(func_decl* f, unsigned n, expr* const* args, expr_ref& result) {
expr* c = nullptr, * t = nullptr, * e = nullptr;
for (unsigned i = 0; i < n; ++i)
if (m().is_ite(args[i], c, t, e) &&
lift_ites_filter(f, args[i]) &&
(get_depth(t) <= 2 || t->get_ref_count() == 1 ||
get_depth(e) <= 2 || e->get_ref_count() == 1)) {
ptr_buffer<expr> new_args;
for (unsigned j = 0; j < n; ++j) new_args.push_back(args[j]);
new_args[i] = t;
expr_ref arg1(m().mk_app(f, new_args), m());
new_args[i] = e;
expr_ref arg2(m().mk_app(f, new_args), m());
result = m().mk_ite(c, arg1, arg2);
TRACE(seq_verbose, tout << "lifting ite: " << mk_pp(result, m()) << std::endl;);
return BR_REWRITE2;
}
return BR_FAILED;
}
bool seq_rewriter::lift_ites_filter(func_decl* f, expr* ite) {
if (u().is_re(f->get_range()) && u().is_seq(ite->get_sort()))
return false;
return true;
}
bool seq_rewriter::is_suffix(expr* s, expr* offset, expr* len) {
expr_ref_vector lens(m());
rational a, b;
return
get_lengths(len, lens, a) &&
(a.neg(), m_autil.is_numeral(offset, b) &&
b.is_pos() &&
a == b &&
lens.contains(s));
}
bool seq_rewriter::is_prefix(expr* s, expr* offset, expr* len) {
expr_ref_vector lens(m());
rational a, b;
return
get_lengths(len, lens, a) &&
a < 0 &&
m_autil.is_numeral(offset, b) &&
b == 0 &&
lens.size() == 1 &&
lens.contains(s);
}
bool seq_rewriter::sign_is_determined(expr* e, sign& s) {
s = sign_zero;
if (m_autil.is_add(e)) {
for (expr* arg : *to_app(e)) {
sign s1;
if (!sign_is_determined(arg, s1))
return false;
if (s == sign_zero)
s = s1;
else if (s1 == sign_zero)
continue;
else if (s1 != s)
return false;
}
return true;
}
if (m_autil.is_mul(e)) {
for (expr* arg : *to_app(e)) {
sign s1;
if (!sign_is_determined(arg, s1))
return false;
if (s1 == sign_zero) {
s = sign_zero;
return true;
}
if (s == sign_zero)
s = s1;
else if (s != s1)
s = sign_neg;
else
s = sign_pos;
}
return true;
}
if (str().is_length(e)) {
s = sign_pos;
return true;
}
rational r;
if (m_autil.is_numeral(e, r)) {
if (r.is_pos())
s = sign_pos;
else if (r.is_neg())
s = sign_neg;
return true;
}
return false;
}
expr_ref seq_rewriter::mk_len(rational const& p, expr_ref_vector const& xs) {
expr_ref r(m_autil.mk_int(p), m());
for (expr* e : xs)
r = m_autil.mk_add(r, str().mk_length(e));
return r;
}
bool seq_rewriter::extract_pop_suffix(expr_ref_vector const& as, expr* b, expr* c, expr_ref& result) {
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) {
auto len_a2 = min_length(as.get(i)).second;
len_a1 += len_a2;
}
if (i < as.size()) {
expr* a = str().mk_concat(i, as.data(), as[0]->get_sort());
result = str().mk_substr(a, b, c);
return true;
}
}
return false;
}
bool seq_rewriter::extract_push_offset(expr_ref_vector const& as, expr* b, expr* c, expr_ref& result) {
expr_ref_vector lens(m());
rational pos1;
if (get_lengths(b, lens, pos1) && pos1 >= 0) {
unsigned i = 0;
for (; i < as.size(); ++i) {
expr* lhs = as.get(i);
if (lens.contains(lhs)) {
lens.erase(lhs);
}
else if (str().is_unit(lhs) && pos1.is_pos()) {
pos1 -= rational(1);
}
else {
break;
}
}
if (i != 0) {
expr_ref t1(m());
t1 = str().mk_concat(as.size() - i, as.data() + i, as[0]->get_sort());
expr_ref t2 = mk_len(pos1, lens);
result = str().mk_substr(t1, t2, c);
TRACE(seq, tout << result << "\n";);
return true;
}
}
return false;
}
bool seq_rewriter::extract_push_length(expr_ref_vector& as, expr* b, expr* c, expr_ref& result) {
rational pos;
expr_ref_vector lens(m());
if (!as.empty() &&
m_autil.is_numeral(b, pos) && pos.is_zero() &&
get_lengths(c, lens, pos) && !pos.is_neg()) {
unsigned i = 0;
for (; i < as.size(); ++i) {
expr* lhs = as.get(i);
if (lens.contains(lhs)) {
lens.erase(lhs);
}
else if (str().is_unit(lhs) && pos.is_pos()) {
pos -= rational(1);
}
else {
break;
}
}
if (i == as.size()) {
result = str().mk_concat(as.size(), as.data(), as[0]->get_sort());
return true;
}
else if (i != 0) {
expr_ref t1(m()), t2(m());
t1 = str().mk_concat(as.size() - i, as.data() + i, as[0]->get_sort());
t2 = mk_len(pos, lens);
result = str().mk_substr(t1, b, t2);
as[i] = result;
result = str().mk_concat(i + 1, as.data(), as[0]->get_sort());
TRACE(seq, tout << result << "\n";);
return true;
}
}
return false;
}
br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& result) {
zstring s;
rational pos, len;
TRACE(seq_verbose, tout << mk_pp(a, m()) << " " << mk_pp(b, m()) << " " << mk_pp(c, m()) << "\n";);
bool constantBase = str().is_string(a, s);
bool constantPos = m_autil.is_numeral(b, pos);
bool constantLen = m_autil.is_numeral(c, len);
sort* a_sort = a->get_sort();
sign sg;
if (sign_is_determined(c, sg) && sg == sign_neg) {
result = str().mk_empty(a_sort);
return BR_DONE;
}
if ( (constantPos && pos.is_neg()) || (constantLen && !len.is_pos()) ) {
result = str().mk_empty(a_sort);
return BR_DONE;
}
if (constantPos && constantBase && pos >= s.length()) {
result = str().mk_empty(a_sort);
return BR_DONE;
}
rational len_a;
if (constantPos && max_length(a, len_a) && len_a <= pos) {
result = str().mk_empty(a_sort);
return BR_DONE;
}
if (m_autil.is_mul(b)) {
bool has_base_length = false, all_other_factors_ge_one = true;
expr* base = nullptr;
rational factor;
ptr_buffer<expr> todo;
obj_hashtable<expr> visited;
todo.push_back(b);
while (!todo.empty()) {
expr* arg = todo.back();
todo.pop_back();
if (visited.contains(arg))
continue;
visited.insert(arg);
if (m_autil.is_mul(arg)) {
for (expr* e : *to_app(arg))
todo.push_back(e);
}
else if (str().is_length(arg, base) && base == a)
has_base_length = true;
else if (!m_autil.is_numeral(arg, factor) || factor < 1) {
all_other_factors_ge_one = false;
break;
}
}
if (has_base_length && all_other_factors_ge_one) {
result = str().mk_empty(a_sort);
return BR_DONE;
}
}
constantPos &= pos.is_unsigned();
constantLen &= len.is_unsigned();
if (constantLen && len == 1) {
result = str().mk_at(a, b);
return BR_REWRITE1;
}
if (constantPos && constantLen && constantBase) {
unsigned _pos = pos.get_unsigned();
unsigned _len = len.get_unsigned();
if (pos + len >= s.length())
result = str().mk_string(s.extract(_pos, s.length()));
else
result = str().mk_string(s.extract(_pos, _len));
return BR_DONE;
}
expr_ref_vector as(m());
str().get_concat_units(a, as);
if (as.empty()) {
result = str().mk_empty(a->get_sort());
return BR_DONE;
}
if (extract_pop_suffix(as, b, c, result))
return BR_REWRITE1;
if (extract_push_offset(as, b, c, result))
return BR_REWRITE3;
if (extract_push_length(as, b, c, result))
return BR_REWRITE3;
expr* a1 = nullptr, *b1 = nullptr, *c1 = nullptr;
if (str().is_extract(a, a1, b1, c1) &&
is_suffix(a1, b1, c1) && is_suffix(a, b, c)) {
{
auto _seq939_0 = m_autil.mk_add(b1, b);
auto _seq939_1 = m_autil.mk_sub(c1, b);
result = str().mk_substr(a1, _seq939_0, _seq939_1);
}
return BR_REWRITE3;
}
rational r1, r2;
if (str().is_extract(a, a1, b1, c1) &&
m_autil.is_numeral(b1, r1) && r1.is_unsigned() &&
m_autil.is_numeral(c1, r2) && r2.is_unsigned() &&
constantPos && constantLen) {
if (r1 == 0 && r2 >= pos + len) {
result = str().mk_substr(a1, b, c);
return BR_REWRITE1;
}
if (r1 >= 0 && pos <= r2) {
r2 = std::min(r2 - pos, len);
r1 += pos;
{
auto _seq960_0 = m_autil.mk_numeral(r1, true);
auto _seq960_1 = m_autil.mk_numeral(r2, true);
result = str().mk_substr(a1, _seq960_0, _seq960_1);
}
return BR_REWRITE1;
}
}
if (str().is_extract(a, a1, b1, c1) &&
is_prefix(a1, b1, c1) && is_prefix(a, b, c)) {
result = str().mk_substr(a1, b, m_autil.mk_sub(c1, m_autil.mk_sub(str().mk_length(a), c)));
return BR_REWRITE3;
}
if (str().is_extract(a, a1, b1, c1) &&
is_prefix(a, b, c) && is_suffix(a1, b1, c1)) {
expr_ref q(m_autil.mk_sub(c, str().mk_length(a)), m());
expr_ref suffix_length(m_autil.mk_sub(str().mk_length(a1), b1), m());
result = str().mk_substr(a1, b1, m_autil.mk_add(suffix_length, q));
return BR_REWRITE3;
}
if (str().is_extract(a, a1, b1, c1) && constantPos && pos == 0 && str().is_length(c, b1) && a1 == b1) {
result = a;
return BR_DONE;
}
if (str().is_extract(a, a1, b1, c1) && constantPos && pos == 0 && c == c1) {
result = a;
return BR_DONE;
}
if (str().is_extract(a, a1, b1, c1) && is_suffix(a, b, c) &&
m_autil.is_numeral(c1) && m_autil.is_numeral(b1)) {
{
auto _seq993_0 = m_autil.mk_add(b, b1);
auto _seq993_1 = m_autil.mk_sub(c1, b);
result = str().mk_substr(a1, _seq993_0, _seq993_1);
}
return BR_REWRITE2;
}
if (!constantPos)
return BR_FAILED;
unsigned offset = 0;
for (; offset < as.size() && str().is_unit(as.get(offset)) && offset < pos; ++offset) {};
if (offset == 0 && pos > 0) {
return BR_FAILED;
}
std::function<bool(expr*)> is_unit = [&](expr *e) { return str().is_unit(e); };
if (pos == 0 && as.forall(is_unit)) {
result = str().mk_empty(a->get_sort());
for (unsigned i = 1; i <= as.size(); ++i) {
auto _seq1011_0 = m_autil.mk_ge(c, m_autil.mk_int(i));
auto _seq1011_1 = str().mk_concat(i, as.data(), a->get_sort());
result = m().mk_ite(_seq1011_0, _seq1011_1, result);
}
return BR_REWRITE_FULL;
}
if (pos == 0 && !constantLen) {
return BR_FAILED;
}
if (offset == as.size()) {
result = str().mk_empty(a->get_sort());
return BR_DONE;
}
SASSERT(offset != 0 || pos == 0);
if (constantLen && pos == offset) {
unsigned _len = len.get_unsigned();
unsigned i = offset;
for (; i < as.size() && str().is_unit(as.get(i)) && i - offset < _len; ++i);
if (i - offset == _len) {
result = str().mk_concat(_len, as.data() + offset, a->get_sort());
return BR_DONE;
}
if (i == as.size()) {
result = str().mk_concat(as.size() - offset, as.data() + offset, as[0]->get_sort());
return BR_DONE;
}
}
if (offset == 0) {
return BR_FAILED;
}
expr_ref position(m());
position = m_autil.mk_sub(b, m_autil.mk_int(offset));
result = str().mk_concat(as.size() - offset, as.data() + offset, as[0]->get_sort());
result = str().mk_substr(result, position, c);
return BR_REWRITE3;
}
bool seq_rewriter::get_lengths(expr* e, expr_ref_vector& lens, rational& pos) {
expr* arg = nullptr, *e1 = nullptr, *e2 = nullptr;
rational pos1;
if (m_autil.is_add(e)) {
for (expr* arg1 : *to_app(e)) {
if (!get_lengths(arg1, lens, pos))
return false;
}
}
else if (str().is_length(e, arg)) {
lens.push_back(arg);
}
else if (m_autil.is_mul(e, e1, e2) && m_autil.is_numeral(e1, pos1) && str().is_length(e2, arg) && 0 <= pos1 && pos1 <= 10) {
while (pos1 > 0) {
lens.push_back(arg);
pos1 -= rational(1);
}
}
else if (m_autil.is_numeral(e, pos1)) {
pos += pos1;
}
else {
return false;
}
return true;
}
bool seq_rewriter::cannot_contain_suffix(expr* a, expr* b) {
if (str().is_unit(a) && str().is_unit(b) && m().are_distinct(a, b)) {
return true;
}
zstring A, B;
if (str().is_string(a, A) && str().is_string(b, B)) {
bool found = false;
for (unsigned i = 1; !found && i <= A.length(); ++i) {
found = A.extract(0, i).suffixof(B);
}
return !found;
}
return false;
}
bool seq_rewriter::cannot_contain_prefix(expr* a, expr* b) {
if (str().is_unit(a) && str().is_unit(b) && m().are_distinct(a, b)) {
return true;
}
zstring A, B;
if (str().is_string(a, A) && str().is_string(b, B)) {
bool found = false;
for (unsigned i = 0; !found && i < A.length(); ++i) {
found = A.extract(i, A.length()-i).suffixof(B);
}
return !found;
}
return false;
}
br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) {
zstring c, d;
if (str().is_string(a, c) && str().is_string(b, d)) {
result = m().mk_bool_val(c.contains(d));
return BR_DONE;
}
expr* x = nullptr, *y, *z;
if (str().is_extract(b, x, y, z) && x == a) {
result = m().mk_true();
return BR_DONE;
}
expr_ref_vector as(m()), bs(m());
str().get_concat_units(a, as);
str().get_concat_units(b, bs);
TRACE(seq, tout << mk_pp(a, m()) << " contains " << mk_pp(b, m()) << "\n";);
if (bs.empty()) {
result = m().mk_true();
return BR_DONE;
}
if (as.empty()) {
result = str().mk_is_empty(b);
return BR_REWRITE2;
}
auto [bounded_a, max_a] = max_length(a);
if (bounded_a && max_a <= 1) {
result = m().mk_or(str().mk_is_empty(b), m().mk_eq(a, b));
return BR_REWRITE2;
}
for (unsigned i = 0; bs.size() + i <= as.size(); ++i) {
unsigned j = 0;
for (; j < bs.size() && as.get(j+i) == bs.get(j); ++j) {};
if (j == bs.size()) {
result = m().mk_true();
return BR_DONE;
}
}
std::function<bool(expr*)> is_value = [&](expr* e) { return m().is_value(e); };
if (bs.forall(is_value) && as.forall(is_value)) {
result = m().mk_false();
return BR_DONE;
}
auto [lA, lenA] = min_length(as);
if (lA) {
auto lenB = min_length(bs).second;
if (lenB > lenA) {
result = m().mk_false();
return BR_DONE;
}
}
unsigned offs = 0;
unsigned sz = as.size();
expr* b0 = bs.get(0);
expr* bL = bs.get(bs.size()-1);
for (; offs < as.size() && cannot_contain_prefix(as[offs].get(), b0); ++offs) {}
for (; sz > offs && cannot_contain_suffix(as.get(sz-1), bL); --sz) {}
if (offs == sz) {
result = str().mk_is_empty(b);
return BR_REWRITE2;
}
if (offs > 0 || sz < as.size()) {
SASSERT(sz > offs);
result = str().mk_contains(str().mk_concat(sz-offs, as.data()+offs, a->get_sort()), b);
return BR_REWRITE2;
}
std::function<bool(expr*)> is_unit = [&](expr *e) { return str().is_unit(e); };
if (bs.forall(is_unit) && as.forall(is_unit)) {
expr_ref_vector ors(m());
for (unsigned i = 0; i + bs.size() <= as.size(); ++i) {
expr_ref_vector ands(m());
for (unsigned j = 0; j < bs.size(); ++j) {
ands.push_back(m().mk_eq(as.get(i + j), bs.get(j)));
}
ors.push_back(::mk_and(ands));
}
result = ::mk_or(ors);
return BR_REWRITE_FULL;
}
if (bs.size() == 1 && bs.forall(is_unit) && as.size() > 1) {
expr_ref_vector ors(m());
for (expr* ai : as) {
ors.push_back(str().mk_contains(ai, bs.get(0)));
}
result = ::mk_or(ors);
return BR_REWRITE_FULL;
}
expr_ref ra(a, m());
if (is_unit(b) && m().is_value(b) &&
reduce_by_char(ra, b, 4)) {
result = str().mk_contains(ra, b);
return BR_REWRITE1;
}
return BR_FAILED;
}
bool seq_rewriter::reduce_by_char(expr_ref& r, expr* ch, unsigned depth) {
expr* x = nullptr, *y = nullptr, *z = nullptr;
if (str().is_replace(r, x, y, z) &&
str().is_unit(y) && m().is_value(y) &&
str().is_unit(z) && m().is_value(z) &&
ch != y && ch != z) {
r = x;
if (depth > 0)
reduce_by_char(r, ch, depth - 1);
return true;
}
if (depth > 0 && str().is_concat(r)) {
bool reduced = false;
expr_ref_vector args(m());
for (expr* e : *to_app(r)) {
expr_ref tmp(e, m());
if (reduce_by_char(tmp, ch, depth - 1))
reduced = true;
args.push_back(tmp);
}
if (reduced)
r = str().mk_concat(args, args.get(0)->get_sort());
return reduced;
}
if (depth > 0 && str().is_extract(r, x, y, z)) {
expr_ref tmp(x, m());
if (reduce_by_char(tmp, ch, depth - 1)) {
r = str().mk_substr(tmp, y, z);
return true;
}
}
return false;
}
br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) {
zstring c;
rational r, offset_r, len_r;
expr* offset, *a1, *len;
expr_ref_vector lens(m());
sort* sort_a = a->get_sort();
if (str().is_extract(a, a1, offset, len) &&
m_autil.is_numeral(offset, offset_r) && offset_r.is_zero() &&
m_autil.is_numeral(len, len_r) && m_autil.is_numeral(b, r) &&
r < len_r) {
result = str().mk_at(a1, b);
return BR_REWRITE1;
}
if (!get_lengths(b, lens, r)) {
return BR_FAILED;
}
if (lens.empty() && r.is_neg()) {
result = str().mk_empty(sort_a);
return BR_DONE;
}
expr* a2 = nullptr, *i2 = nullptr;
if (lens.empty() && str().is_at(a, a2, i2)) {
if (r.is_pos()) {
result = str().mk_empty(sort_a);
}
else {
result = a;
}
return BR_DONE;
}
m_lhs.reset();
str().get_concat_units(a, m_lhs);
if (m_lhs.empty()) {
result = str().mk_empty(a->get_sort());
return BR_DONE;
}
unsigned i = 0;
for (; i < m_lhs.size(); ++i) {
expr* lhs = m_lhs.get(i);
if (lens.contains(lhs) && !r.is_neg()) {
lens.erase(lhs);
}
else if (str().is_unit(lhs) && r.is_zero() && lens.empty()) {
result = lhs;
return BR_REWRITE1;
}
else if (str().is_unit(lhs) && r.is_pos()) {
r -= rational(1);
}
else {
break;
}
}
if (i == 0) {
return BR_FAILED;
}
if (m_lhs.size() == i) {
result = str().mk_empty(sort_a);
return BR_DONE;
}
expr_ref pos(m_autil.mk_int(r), m());
for (expr* rhs : lens) {
pos = m_autil.mk_add(pos, str().mk_length(rhs));
}
result = str().mk_concat(m_lhs.size() - i , m_lhs.data() + i, sort_a);
result = str().mk_at(result, pos);
return BR_REWRITE2;
}
br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) {
rational pos1, pos2;
expr* s = nullptr, *p = nullptr, *len = nullptr;
if (str().is_unit(a, s) && m_autil.is_numeral(b, pos1) && pos1.is_zero()) {
result = s;
return BR_DONE;
}
if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1) && pos1 > 0) {
expr_ref_vector lens(m());
rational pos2;
if (get_lengths(len, lens, pos2) && (pos1 == -pos2) && (lens.size() == 1) && (lens.get(0) == s)) {
expr_ref k(m_autil.mk_int(pos1), m());
expr_ref case1(str().mk_nth_i(s, m_autil.mk_add(b, k)), m());
expr_ref case2(str().mk_nth_u(str().mk_empty(s->get_sort()), b), m());
expr_ref case3(str().mk_nth_u(a, b), m());
result = case3;
auto _seq0 = m_autil.mk_add(k, b);
auto _seq1 = str().mk_length(s);
result = m().mk_ite(m_autil.mk_lt(_seq0, _seq1), case1, result);
result = m().mk_ite(m_autil.mk_ge(k, str().mk_length(s)), case2, result);
result = m().mk_ite(m_autil.mk_lt(b, zero()), case3, result);
return BR_REWRITE_FULL;
}
}
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);
{
auto ge0 = m_autil.mk_ge(b, zero());
auto le_la = m_autil.mk_le(la, b);
auto not_le = m().mk_not(le_la);
auto guard = m().mk_and(ge0, not_le);
auto t1 = str().mk_nth_i(a, b);
auto e1 = str().mk_nth_u(a, b);
result = m().mk_ite(guard, t1, e1);
}
return BR_REWRITE_FULL;
}
br_status seq_rewriter::mk_seq_nth_i(expr* a, expr* b, expr_ref& result) {
zstring c;
rational r;
if (!m_autil.is_numeral(b, r) || !r.is_unsigned()) {
return BR_FAILED;
}
unsigned offset = r.get_unsigned();
expr* a2, *i2;
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;
}
expr* f, *s;
if (str().is_map(a, f, s)) {
expr* args[2] = { f, str().mk_nth_i(s, b) };
result = array_util(m()).mk_select(2, args);
return BR_REWRITE1;
}
expr_ref_vector as(m());
str().get_concat_units(a, as);
expr* cond = nullptr, *el = nullptr, *th = nullptr;
unsigned pos = 0;
for (unsigned i = 0; i < as.size(); ++i) {
expr* a = as.get(i), *u = nullptr;
if (str().is_unit(a, u)) {
if (offset == pos) {
result = u;
return BR_DONE;
}
++pos;
continue;
}
else if (m().is_ite(a, cond, th, el)) {
auto [bounded, len1] = min_length(a);
if (!bounded)
break;
if (pos + len1 <= offset) {
pos += len1;
continue;
}
expr_ref idx(m());
idx = m_autil.mk_int(offset - pos);
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;
}
br_status seq_rewriter::mk_seq_last_index(expr* a, expr* b, expr_ref& result) {
zstring s1, s2;
bool isc1 = str().is_string(a, s1);
bool isc2 = str().is_string(b, s2);
if (isc1 && isc2) {
int idx = s1.last_indexof(s2);
result = m_autil.mk_numeral(rational(idx), true);
return BR_DONE;
}
if (a == b) {
result = m_autil.mk_int(0);
return BR_DONE;
}
if (str().is_empty(b)) {
result = str().mk_length(a);
return BR_DONE;
}
expr_ref_vector as(m()), bs(m());
str().get_concat_units(a, as);
str().get_concat_units(b, bs);
auto is_suffix = [&](expr_ref_vector const& as, expr_ref_vector const& bs) {
if (as.size() < bs.size())
return l_undef;
for (unsigned j = 0; j < bs.size(); ++j) {
auto a = as.get(as.size() - j - 1);
auto b = bs.get(bs.size() - j - 1);
if (m().are_equal(a, b))
continue;
if (m().are_distinct(a, b))
return l_false;
return l_undef;
}
return l_true;
};
switch (compare_lengths(as, bs)) {
case shorter_c:
result = minus_one();
return BR_DONE;
case same_length_c: {
expr_ref eq(m().mk_eq(a, b), m());
auto found = zero();
auto not_found = minus_one();
result = m().mk_ite(eq, found, not_found);
return BR_REWRITE_FULL;
}
case longer_c: {
unsigned i = as.size();
while (i >= bs.size()) {
switch (is_suffix(as, bs)) {
case l_undef:
return BR_FAILED;
case l_true: {
auto _seq0 = str().mk_length(a);
auto _seq1 = m_autil.mk_int(bs.size() - i);
result = m_autil.mk_sub(_seq0, _seq1);
return BR_REWRITE3;
}
case l_false:
as.pop_back();
--i;
break;
}
}
break;
}
default:
break;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result) {
zstring s1, s2;
rational r;
bool isc1 = str().is_string(a, s1);
bool isc2 = str().is_string(b, s2);
sort* sort_a = a->get_sort();
if (isc1 && isc2 && m_autil.is_numeral(c, r) && r.is_unsigned()) {
int idx = s1.indexofu(s2, r.get_unsigned());
result = m_autil.mk_int(idx);
return BR_DONE;
}
if (m_autil.is_numeral(c, r) && r.is_neg()) {
result = minus_one();
return BR_DONE;
}
if (str().is_empty(b) && m_autil.is_numeral(c, r) && r.is_zero()) {
result = c;
return BR_DONE;
}
if (str().is_empty(b)) {
auto a1 = m_autil.mk_le(zero(), c);
auto b1 = m_autil.mk_le(c, str().mk_length(a));
auto cond = m().mk_and(a1, b1);
result = m().mk_ite(cond, c, minus_one());
return BR_REWRITE2;
}
if (str().is_empty(a)) {
expr* emp = str().mk_is_empty(b);
auto a1 = m().mk_eq(c, zero());
auto cond = m().mk_and(a1, emp);
{
auto then_value = zero();
auto else_value = minus_one();
result = m().mk_ite(cond, then_value, else_value);
}
return BR_REWRITE2;
}
if (a == b) {
if (m_autil.is_numeral(c, r)) {
result = r.is_zero() ? zero() : minus_one();
return BR_DONE;
}
else {
auto found = zero();
expr_ref at_zero(m().mk_eq(found, c), m());
auto not_found = minus_one();
result = m().mk_ite(at_zero, found, not_found);
return BR_REWRITE2;
}
}
expr* x = nullptr, *y = nullptr, *len1 = nullptr;
rational r1, r2;
if (str().is_extract(a, x, y, len1) && m_autil.is_numeral(len1, r1) &&
m_autil.is_numeral(c, r2) && r2 > r1) {
result = minus_one();
return BR_DONE;
}
expr_ref_vector as(m()), bs(m());
str().get_concat_units(a, as);
unsigned i = 0;
if (m_autil.is_numeral(c, r)) {
i = 0;
while (r.is_pos() && i < as.size() && str().is_unit(as.get(i))) {
r -= rational(1);
++i;
}
if (i > 0) {
expr_ref a1(m());
a1 = str().mk_concat(as.size() - i, as.data() + i, sort_a);
result = str().mk_index(a1, b, m_autil.mk_int(r));
{
auto _seq1599_0 = m_autil.mk_ge(result, zero());
auto _seq1599_1 = m_autil.mk_add(m_autil.mk_int(i), result);
result = m().mk_ite(_seq1599_0, _seq1599_1, minus_one());
}
return BR_REWRITE_FULL;
}
}
bool is_zero = m_autil.is_numeral(c, r) && r.is_zero();
str().get_concat_units(b, bs);
i = 0;
while (is_zero && i < as.size() &&
0 < bs.size() &&
str().is_unit(as.get(i)) &&
str().is_unit(bs.get(0)) &&
m().are_distinct(as.get(i), bs.get(0))) {
++i;
}
if (i > 0) {
result = str().mk_index(
str().mk_concat(as.size() - i, as.data() + i, sort_a), b, c);
{
auto _seq1616_0 = m_autil.mk_ge(result, zero());
auto _seq1616_1 = m_autil.mk_add(m_autil.mk_int(i), result);
result = m().mk_ite(_seq1616_0, _seq1616_1, minus_one());
}
return BR_REWRITE_FULL;
}
switch (compare_lengths(as, bs)) {
case shorter_c:
if (is_zero) {
result = minus_one();
return BR_DONE;
}
break;
case same_length_c: {
auto _seqa = m_autil.mk_le(c, minus_one());
auto _seqb = m().mk_eq(c, zero());
expr_ref condition(m().mk_eq(a, b), m());
auto then_value = zero();
auto else_value = minus_one();
auto _seqc = m().mk_ite(condition, then_value, else_value);
{
auto then_value = minus_one();
expr_ref else_value(m().mk_ite(_seqb, _seqc, minus_one()), m());
result = m().mk_ite(_seqa, then_value, else_value);
}
}
return BR_REWRITE_FULL;
default:
break;
}
if (is_zero && !as.empty() && str().is_unit(as.get(0))) {
expr_ref a1(str().mk_concat(as.size() - 1, as.data() + 1, as[0]->get_sort()), m());
expr_ref b1(str().mk_index(a1, b, c), m());
{
auto _seq1639_0 = str().mk_prefix(b, a);
auto _seq1663_0 = m_autil.mk_ge(b1, zero());
auto _seq1663_1 = m_autil.mk_add(one(), b1);
auto _seq1639_1 = m().mk_ite(_seq1663_0, _seq1663_1, minus_one());
result = m().mk_ite(_seq1639_0, zero(), _seq1639_1);
}
return BR_REWRITE3;
}
expr_ref ra(a, m());
if (str().is_unit(b) && m().is_value(b) &&
reduce_by_char(ra, b, 4)) {
result = str().mk_index(ra, b, c);
return BR_REWRITE1;
}
return BR_FAILED;
}
seq_rewriter::length_comparison seq_rewriter::compare_lengths(unsigned sza, expr* const* as, unsigned szb, expr* const* bs) {
unsigned units_a = 0, units_b = 0, k = 0;
obj_map<expr, unsigned> mults;
bool b_has_foreign = false;
for (unsigned i = 0; i < sza; ++i) {
if (str().is_unit(as[i]))
units_a++;
else
mults.insert_if_not_there(as[i], 0)++;
}
for (unsigned i = 0; i < szb; ++i) {
if (str().is_unit(bs[i]))
units_b++;
else if (mults.find(bs[i], k)) {
--k;
if (k == 0) {
mults.erase(bs[i]);
}
else {
mults.insert(bs[i], k);
}
}
else {
b_has_foreign = true;
}
}
if (units_a > units_b && !b_has_foreign) {
return longer_c;
}
if (units_a == units_b && !b_has_foreign && mults.empty()) {
return same_length_c;
}
if (units_b > units_a && mults.empty()) {
return shorter_c;
}
return unknown_c;
}
br_status seq_rewriter::mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& result) {
zstring s1, s2, s3;
sort* sort_a = a->get_sort();
if (str().is_string(a, s1) && str().is_string(b, s2) &&
str().is_string(c, s3)) {
result = str().mk_string(s1.replace(s2, s3));
return BR_DONE;
}
if (b == c) {
result = a;
return BR_DONE;
}
if (a == b) {
result = c;
return BR_DONE;
}
if (str().is_empty(b)) {
result = str().mk_concat(c, a);
return BR_REWRITE1;
}
if (str().is_empty(a) && str().is_empty(c)) {
result = a;
return BR_DONE;
}
m_lhs.reset();
str().get_concat(a, m_lhs);
if (m_lhs.empty()) {
str().get_concat(b, m_lhs);
unsigned len = min_length(m_lhs).second;
if (len > 0) {
result = a;
return BR_DONE;
}
return BR_FAILED;
}
if (m_lhs.get(0) == b) {
m_lhs[0] = c;
result = str().mk_concat(m_lhs.size(), m_lhs.data(), sort_a);
return BR_REWRITE1;
}
if (str().is_string(b, s2) && str().is_string(c, s3) &&
str().is_string(m_lhs.get(0), s1) && s1.contains(s2) ) {
m_lhs[0] = str().mk_string(s1.replace(s2, s3));
result = str().mk_concat(m_lhs.size(), m_lhs.data(), sort_a);
return BR_REWRITE1;
}
m_lhs.reset();
m_rhs.reset();
str().get_concat_units(a, m_lhs);
str().get_concat_units(b, m_rhs);
if (m_rhs.empty()) {
result = str().mk_concat(c, a);
return BR_REWRITE1;
}
auto compare_at_i = [&](unsigned i) {
for (unsigned j = 0; j < m_rhs.size() && i + j < m_lhs.size(); ++j) {
expr* b0 = m_rhs.get(j);
expr* a0 = m_lhs.get(i + j);
if (m().are_equal(a0, b0))
continue;
if (!str().is_unit(b0) || !str().is_unit(a0))
return l_undef;
if (m().are_distinct(a0, b0))
return l_false;
return l_undef;
}
return l_true;
};
unsigned i = 0;
for (; i < m_lhs.size(); ++i) {
lbool cmp = compare_at_i(i);
if (cmp == l_false && str().is_unit(m_lhs.get(i)))
continue;
if (cmp == l_true && m_lhs.size() < i + m_rhs.size()) {
expr_ref a1(str().mk_concat(i, m_lhs.data(), sort_a), m());
expr_ref a2(str().mk_concat(m_lhs.size()-i, m_lhs.data()+i, sort_a), m());
{
auto _seq1783_0 = m().mk_eq(a2, b);
auto _seq1783_1 = str().mk_concat(a1, c);
result = m().mk_ite(_seq1783_0, _seq1783_1, a);
}
return BR_REWRITE_FULL;
}
if (cmp == l_true) {
expr_ref_vector es(m());
es.append(i, m_lhs.data());
es.push_back(c);
es.append(m_lhs.size()-i-m_rhs.size(), m_lhs.data()+i+m_rhs.size());
result = str().mk_concat(es, sort_a);
return BR_REWRITE_FULL;
}
break;
}
if (i > 0) {
expr_ref a1(str().mk_concat(i, m_lhs.data(), sort_a), m());
expr_ref a2(str().mk_concat(m_lhs.size()-i, m_lhs.data()+i, sort_a), m());
result = str().mk_concat(a1, str().mk_replace(a2, b, c));
return BR_REWRITE_FULL;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_replace_all(expr* a, expr* b, expr* c, expr_ref& result) {
if (str().is_empty(b) || b == c) {
result = a;
return BR_DONE;
}
if (a == b) {
auto _seq1814_0 = str().mk_is_empty(b);
auto _seq1814_1 = str().mk_empty(a->get_sort());
result = m().mk_ite(_seq1814_0, _seq1814_1, c);
return BR_REWRITE2;
}
if (str().is_empty(a) && str().is_empty(c)) {
result = a;
return BR_DONE;
}
zstring s1, s2;
expr_ref_vector strs(m());
if (str().is_string(a, s1) && str().is_string(b, s2)) {
SASSERT(s2.length() > 0);
if (s1.length() < s2.length()) {
result = a;
return BR_DONE;
}
for (unsigned i = 0; i < s1.length(); ++i) {
if (s1.length() >= s2.length() + i &&
s2 == s1.extract(i, s2.length())) {
strs.push_back(c);
i += s2.length() - 1;
}
else
strs.push_back(str().mk_unit(str().mk_char(s1, i)));
}
result = str().mk_concat(strs, a->get_sort());
return BR_REWRITE_FULL;
}
expr_ref_vector a_vals(m());
expr_ref_vector b_vals(m());
if (try_get_unit_values(a, a_vals) && try_get_unit_values(b, b_vals)) {
replace_all_subvectors(a_vals, b_vals, c, strs);
result = str().mk_concat(strs, a->get_sort());
return BR_REWRITE_FULL;
}
return BR_FAILED;
}
expr_ref seq_rewriter::re_replace_char(expr *r, unsigned a_ch, unsigned b_ch, expr *a_str, expr *b_str) {
expr *r1 = nullptr, *r2 = nullptr, *s = nullptr;
zstring str_val;
sort *seq_sort = nullptr;
if (re().is_to_re(r, s) && str().is_string(s, str_val)) {
seq_sort = s->get_sort();
expr_ref_vector parts(m());
for (unsigned i = 0; i < str_val.length(); ++i) {
if (str_val[i] == a_ch) {
return expr_ref(re().mk_empty(re().mk_re(seq_sort)), m());
}
else if (str_val[i] == b_ch) {
auto a_re = re().mk_to_re(a_str);
auto b_re = re().mk_to_re(b_str);
parts.push_back(re().mk_union(a_re, b_re));
}
else {
zstring ch(str_val[i]);
parts.push_back(re().mk_to_re(str().mk_string(ch)));
}
}
if (parts.empty())
return expr_ref(re().mk_epsilon(seq_sort), m());
expr_ref result(parts.back(), m());
for (int i = parts.size() - 1; i-- > 0;)
result = re().mk_concat(parts.get(i), result);
return result;
}
if (re().is_range(r, r1, r2)) {
zstring lo_s, hi_s;
if (str().is_string(r1, lo_s) && str().is_string(r2, hi_s) && lo_s.length() == 1 && hi_s.length() == 1) {
unsigned lo = lo_s[0], hi = hi_s[0];
expr_ref_vector parts(m());
if (lo <= hi) {
unsigned prev = lo;
for (unsigned ch = lo; ch <= hi; ++ch) {
if (ch == a_ch || ch == b_ch) {
if (prev < ch) {
zstring prev_z(prev), pred_z(ch - 1);
{
auto _seq1911_0 = str().mk_string(prev_z);
auto _seq1911_1 = str().mk_string(pred_z);
parts.push_back(re().mk_range(_seq1911_0, _seq1911_1));
}
}
if (ch == b_ch) {
auto _seq1914_0 = re().mk_to_re(a_str);
auto _seq1914_1 = re().mk_to_re(b_str);
parts.push_back(re().mk_union(_seq1914_0, _seq1914_1));
}
prev = ch + 1;
}
}
if (prev <= hi) {
zstring prev_z(prev), hi_z(hi);
{
auto _seq1922_0 = str().mk_string(prev_z);
auto _seq1922_1 = str().mk_string(hi_z);
parts.push_back(re().mk_range(_seq1922_0, _seq1922_1));
}
}
}
if (parts.empty()) {
sort *re_sort = r->get_sort();
return expr_ref(re().mk_empty(re_sort), m());
}
expr_ref result(parts[0].get(), m());
for (unsigned i = 1; i < parts.size(); ++i)
result = re().mk_union(result, parts[i].get());
return result;
}
return expr_ref(r, m());
}
if (re().is_union(r, r1, r2)) {
auto lhs = re_replace_char(r1, a_ch, b_ch, a_str, b_str);
auto rhs = re_replace_char(r2, a_ch, b_ch, a_str, b_str);
return expr_ref(re().mk_union(lhs, rhs), m());
}
if (re().is_intersection(r, r1, r2)) {
auto lhs1 = re_replace_char(r1, a_ch, b_ch, a_str, b_str);
auto rhs1 = re_replace_char(r2, a_ch, b_ch, a_str, b_str);
return expr_ref(re().mk_inter(lhs1, rhs1), m());
}
if (re().is_concat(r, r1, r2)) {
auto lhs = re_replace_char(r1, a_ch, b_ch, a_str, b_str);
auto rhs = re_replace_char(r2, a_ch, b_ch, a_str, b_str);
return expr_ref(re().mk_concat(lhs, rhs), m());
}
if (re().is_star(r, r1)) {
return expr_ref(re().mk_star(re_replace_char(r1, a_ch, b_ch, a_str, b_str)), m());
}
if (re().is_plus(r, r1)) {
return expr_ref(re().mk_plus(re_replace_char(r1, a_ch, b_ch, a_str, b_str)), m());
}
if (re().is_opt(r, r1)) {
return expr_ref(re().mk_opt(re_replace_char(r1, a_ch, b_ch, a_str, b_str)), m());
}
unsigned lo, hi;
if (re().is_loop(r, r1, lo, hi)) {
return expr_ref(re().mk_loop(re_replace_char(r1, a_ch, b_ch, a_str, b_str), lo, hi), m());
}
if (re().is_loop(r, r1, lo)) {
return expr_ref(re().mk_loop(re_replace_char(r1, a_ch, b_ch, a_str, b_str), lo), m());
}
if (re().is_complement(r)) {
UNREACHABLE();
}
return expr_ref(r, m());
}
br_status seq_rewriter::mk_seq_map(expr* f, expr* seqA, expr_ref& result) {
if (str().is_empty(seqA)) {
result = str().mk_empty(str().mk_seq(get_array_range(f->get_sort())));
return BR_DONE;
}
expr* a, *s1, *s2;
if (str().is_unit(seqA, a)) {
array_util array(m());
expr* args[2] = { f, a };
result = str().mk_unit(array.mk_select(2, args));
return BR_REWRITE2;
}
if (str().is_concat(seqA, s1, s2)) {
auto m1 = str().mk_map(f, s1);
auto m2 = str().mk_map(f, s2);
result = str().mk_concat(m1, m2);
return BR_REWRITE2;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_mapi(expr* f, expr* i, expr* seqA, expr_ref& result) {
if (str().is_empty(seqA)) {
result = str().mk_empty(str().mk_seq(get_array_range(f->get_sort())));
return BR_DONE;
}
expr* a, *s1, *s2;
if (str().is_unit(seqA, a)) {
array_util array(m());
expr* args[3] = { f, i, a };
result = str().mk_unit(array.mk_select(3, args));
return BR_REWRITE2;
}
if (str().is_concat(seqA, s1, s2)) {
expr_ref j(m_autil.mk_add(i, str().mk_length(s1)), m());
auto left = str().mk_mapi(f, i, s1);
auto right = str().mk_mapi(f, j, s2);
result = str().mk_concat(left, right);
return BR_REWRITE2;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_foldl(expr* f, expr* b, expr* seqA, expr_ref& result) {
if (str().is_empty(seqA)) {
result = b;
return BR_DONE;
}
expr* a, *s1, *s2;
if (str().is_unit(seqA, a)) {
array_util array(m());
expr* args[3] = { f, b, a };
result = array.mk_select(3, args);
return BR_REWRITE1;
}
if (str().is_concat(seqA, s1, s2)) {
result = str().mk_foldl(f, b, s1);
result = str().mk_foldl(f, result, s2);
return BR_REWRITE3;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_foldli(expr* f, expr* i, expr* b, expr* seqA, expr_ref& result) {
if (str().is_empty(seqA)) {
result = b;
return BR_DONE;
}
expr* a, *s1, *s2;
if (str().is_unit(seqA, a)) {
array_util array(m());
expr* args[4] = { f, i, b, a };
result = array.mk_select(4, args);
return BR_REWRITE1;
}
if (str().is_concat(seqA, s1, s2)) {
expr_ref j(m_autil.mk_add(i, str().mk_length(s1)), m());
result = str().mk_foldli(f, i, b, s1);
result = str().mk_foldli(f, j, result, s2);
return BR_REWRITE3;
}
return BR_FAILED;
}
bool seq_rewriter::try_get_unit_values(expr* s, expr_ref_vector& vals) {
expr* h, * t, * v;
t = s;
while (str().is_concat(t, h, t))
if (str().is_unit(h, v) && m().is_value(v))
vals.push_back(h);
else
return false;
if (str().is_unit(t, v) && m().is_value(v))
vals.push_back(t);
else
return false;
return true;
}
void seq_rewriter::replace_all_subvectors(expr_ref_vector const& a, expr_ref_vector const& b, expr* c, expr_ref_vector& result) {
unsigned int i = 0;
unsigned int k = b.size();
while (i + k <= a.size()) {
unsigned j = 0;
while (j < k && b[j] == a[i + j])
++j;
if (j < k) result.push_back(a[i++]);
else { result.push_back(c);
i += k;
}
}
while (i < a.size())
result.push_back(a[i++]);
}
br_status seq_rewriter::mk_seq_replace_re_all(expr* a, expr* b, expr* c, expr_ref& result) {
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_replace_re(expr* a, expr* b, expr* c, expr_ref& result) {
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) {
TRACE(seq, tout << mk_pp(a, m()) << " " << mk_pp(b, m()) << "\n";);
zstring s1, s2;
bool isc1 = str().is_string(a, s1);
bool isc2 = str().is_string(b, s2);
sort* sort_a = a->get_sort();
if (isc1 && isc2) {
result = m().mk_bool_val(s1.prefixof(s2));
TRACE(seq, tout << result << "\n";);
return BR_DONE;
}
if (str().is_empty(a)) {
result = m().mk_true();
return BR_DONE;
}
expr* a1 = str().get_leftmost_concat(a);
expr* b1 = str().get_leftmost_concat(b);
isc1 = str().is_string(a1, s1);
isc2 = str().is_string(b1, s2);
expr_ref_vector as(m()), bs(m());
if (a1 != b1 && isc1 && isc2) {
if (s1.length() <= s2.length()) {
if (s1.prefixof(s2)) {
if (a == a1) {
result = m().mk_true();
TRACE(seq, tout << s1 << " " << s2 << " " << result << "\n";);
return BR_DONE;
}
str().get_concat(a, as);
str().get_concat(b, bs);
SASSERT(as.size() > 1);
s2 = s2.extract(s1.length(), s2.length()-s1.length());
bs[0] = str().mk_string(s2);
{
auto _seq2156_0 = str().mk_concat(as.size() - 1, as.data() + 1, sort_a);
auto _seq2156_1 = str().mk_concat(bs.size(), bs.data(), sort_a);
result = str().mk_prefix(_seq2156_0, _seq2156_1);
}
TRACE(seq, tout << s1 << " " << s2 << " " << result << "\n";);
return BR_REWRITE_FULL;
}
else {
result = m().mk_false();
TRACE(seq, tout << s1 << " " << s2 << " " << result << "\n";);
return BR_DONE;
}
}
else {
if (s2.prefixof(s1)) {
if (b == b1) {
result = m().mk_false();
TRACE(seq, tout << s1 << " " << s2 << " " << result << "\n";);
return BR_DONE;
}
str().get_concat(a, as);
str().get_concat(b, bs);
SASSERT(bs.size() > 1);
s1 = s1.extract(s2.length(), s1.length() - s2.length());
as[0] = str().mk_string(s1);
auto a = str().mk_concat(as.size(), as.data(), sort_a);
result = str().mk_prefix(a, str().mk_concat(bs.size()-1, bs.data()+1, sort_a));
TRACE(seq, tout << s1 << " " << s2 << " " << result << "\n";);
return BR_REWRITE_FULL;
}
else {
result = m().mk_false();
TRACE(seq, tout << s1 << " " << s2 << " " << result << "\n";);
return BR_DONE;
}
}
}
str().get_concat_units(a, as);
str().get_concat_units(b, bs);
unsigned i = 0;
expr_ref_vector eqs(m());
for (; i < as.size() && i < bs.size(); ++i) {
expr* ai = as.get(i), *bi = bs.get(i);
if (m().are_equal(ai, bi)) {
continue;
}
if (m().are_distinct(ai, bi)) {
result = m().mk_false();
return BR_DONE;
}
if (str().is_unit(ai) && str().is_unit(bi)) {
eqs.push_back(m().mk_eq(ai, bi));
continue;
}
break;
}
if (i == as.size()) {
result = mk_and(eqs);
TRACE(seq, tout << result << "\n";);
return BR_REWRITE3;
}
SASSERT(i < as.size());
if (i == bs.size()) {
for (unsigned j = i; j < as.size(); ++j) {
eqs.push_back(str().mk_is_empty(as.get(j)));
}
result = mk_and(eqs);
TRACE(seq, tout << result << "\n";);
return BR_REWRITE3;
}
if (i > 0) {
SASSERT(i < as.size() && i < bs.size());
a = str().mk_concat(as.size() - i, as.data() + i, sort_a);
b = str().mk_concat(bs.size() - i, bs.data() + i, sort_a);
eqs.push_back(str().mk_prefix(a, b));
result = mk_and(eqs);
TRACE(seq, tout << result << "\n";);
return BR_REWRITE3;
}
expr* a2 = nullptr, *a3 = nullptr;
if (str().is_replace(a, a1, a2, a3) && a1 == a3 && a2 == b) {
result = str().mk_prefix(a1, b);
return BR_DONE;
}
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;
}
}
return BR_FAILED;
}
br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) {
if (a == b) {
result = m().mk_true();
return BR_DONE;
}
sort* sort_a = a->get_sort();
if (str().is_empty(a)) {
result = m().mk_true();
return BR_DONE;
}
if (str().is_empty(b)) {
result = str().mk_is_empty(a);
return BR_REWRITE3;
}
expr_ref_vector as(m()), bs(m()), eqs(m());
str().get_concat_units(a, as);
str().get_concat_units(b, bs);
unsigned i = 1, sza = as.size(), szb = bs.size();
for (; i <= sza && i <= szb; ++i) {
expr* ai = as.get(sza-i), *bi = bs.get(szb-i);
if (m().are_equal(ai, bi)) {
continue;
}
if (m().are_distinct(ai, bi)) {
result = m().mk_false();
return BR_DONE;
}
if (str().is_unit(ai) && str().is_unit(bi)) {
eqs.push_back(m().mk_eq(ai, bi));
continue;
}
break;
}
if (i > sza) {
result = mk_and(eqs);
TRACE(seq, tout << result << "\n";);
return BR_REWRITE3;
}
if (i > szb) {
for (unsigned j = i; j <= sza; ++j) {
expr* aj = as.get(sza-j);
eqs.push_back(str().mk_is_empty(aj));
}
result = mk_and(eqs);
TRACE(seq, tout << result << "\n";);
return BR_REWRITE3;
}
if (i > 1) {
SASSERT(i <= sza && i <= szb);
a = str().mk_concat(sza - i + 1, as.data(), sort_a);
b = str().mk_concat(szb - i + 1, bs.data(), sort_a);
eqs.push_back(str().mk_suffix(a, b));
result = mk_and(eqs);
TRACE(seq, tout << result << "\n";);
return BR_REWRITE3;
}
expr* a1 = nullptr, *a2 = nullptr, *a3 = nullptr;
if (str().is_replace(a, a1, a2, a3) && a1 == a3 && a2 == b) {
result = str().mk_suffix(a1, b);
return BR_DONE;
}
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;
}
}
return BR_FAILED;
}
br_status seq_rewriter::mk_str_units(func_decl* f, expr_ref& result) {
zstring s;
VERIFY(str().is_string(f, s));
expr_ref_vector es(m());
unsigned sz = s.length();
for (unsigned j = 0; j < sz; ++j) {
es.push_back(str().mk_unit(str().mk_char(s, j)));
}
result = str().mk_concat(es, f->get_range());
return BR_DONE;
}
br_status seq_rewriter::mk_str_le(expr* a, expr* b, expr_ref& result) {
result = m().mk_not(str().mk_lex_lt(b, a));
return BR_REWRITE2;
}
br_status seq_rewriter::mk_str_lt(expr* a, expr* b, expr_ref& result) {
zstring as, bs;
if (str().is_empty(b)) {
result = m().mk_false();
return BR_DONE;
}
if (str().is_empty(a)) {
result = m().mk_not(m().mk_eq(a, b));
return BR_REWRITE1;
}
if (str().is_string(a, as) && str().is_string(b, bs)) {
unsigned sz = std::min(as.length(), bs.length());
for (unsigned i = 0; i < sz; ++i) {
if (as[i] < bs[i]) {
result = m().mk_true();
return BR_DONE;
}
if (as[i] > bs[i]) {
result = m().mk_false();
return BR_DONE;
}
}
result = m().mk_bool_val(as.length() < bs.length());
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_str_from_code(expr* a, expr_ref& result) {
rational r;
if (m_autil.is_numeral(a, r)) {
if (r.is_neg() || r > u().max_char()) {
result = str().mk_string(zstring());
}
else {
unsigned num = r.get_unsigned();
zstring s(1, &num);
result = str().mk_string(s);
}
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_str_to_code(expr* a, expr_ref& result) {
zstring s;
if (str().is_string(a, s)) {
if (s.length() == 1)
result = m_autil.mk_int(s[0]);
else
result = minus_one();
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_str_is_digit(expr* a, expr_ref& result) {
zstring s;
if (str().is_string(a, s)) {
if (s.length() == 1 && '0' <= s[0] && s[0] <= '9')
result = m().mk_true();
else
result = m().mk_false();
return BR_DONE;
}
if (str().is_empty(a)) {
result = m().mk_false();
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_str_ubv2s(expr* a, expr_ref& result) {
bv_util bv(m());
rational val;
if (bv.is_numeral(a, val)) {
result = str().mk_string(zstring(val));
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_str_sbv2s(expr *a, expr_ref &result) {
bv_util bv(m());
rational val;
unsigned bv_size = 0;
if (bv.is_numeral(a, val, bv_size)) {
rational r = mod(val, rational::power_of_two(bv_size));
SASSERT(!r.is_neg());
if (r >= rational::power_of_two(bv_size - 1)) {
r -= rational::power_of_two(bv_size);
}
result = str().mk_string(zstring(r));
return BR_DONE;
}
bv_size = bv.get_bv_size(a);
{
auto _seq2449_0 = bv.mk_slt(a, bv.mk_numeral(0, bv_size));
auto _seq2499_0 = str().mk_string(zstring("-"));
auto _seq2499_1 = str().mk_ubv2s(bv.mk_bv_neg(a));
auto _seq2449_1 = str().mk_concat(_seq2499_0, _seq2499_1);
auto _seq2449_2 = str().mk_ubv2s(a);
result = m().mk_ite(_seq2449_0, _seq2449_1, _seq2449_2);
}
return BR_REWRITE_FULL;
}
br_status seq_rewriter::mk_str_itos(expr* a, expr_ref& result) {
rational r;
if (m_autil.is_numeral(a, r)) {
if (r.is_int() && !r.is_neg()) {
result = str().mk_string(zstring(r));
}
else {
result = str().mk_string(zstring());
}
return BR_DONE;
}
expr* b = nullptr;
if (str().is_stoi(a, b) && max_length(b, r) && r <= 1) {
expr_ref_vector eqs(m());
for (unsigned i = 0; i < 10; ++i) {
zstring s('0' + i);
eqs.push_back(m().mk_eq(b, str().mk_string(s)));
}
result = m().mk_or(eqs);
result = m().mk_ite(result, b, str().mk_string(zstring()));
return BR_REWRITE2;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
zstring s;
if (str().is_string(a, s)) {
std::string s1 = s.encode();
if (s1.length() == 0) {
result = minus_one();
return BR_DONE;
}
for (unsigned i = 0; i < s1.length(); ++i) {
if (!('0' <= s1[i] && s1[i] <= '9')) {
result = minus_one();
return BR_DONE;
}
}
rational r(s1.c_str());
result = m_autil.mk_numeral(r, true);
return BR_DONE;
}
expr* b;
if (str().is_itos(a, b)) {
auto a = m_autil.mk_ge(b, zero());
result = m().mk_ite(a, b, minus_one());
return BR_DONE;
}
if (str().is_ubv2s(a, b)) {
bv_util bv(m());
result = bv.mk_ubv2int(b);
return BR_DONE;
}
expr* c = nullptr, *t = nullptr, *e = nullptr;
if (m().is_ite(a, c, t, e)) {
auto a = str().mk_stoi(t);
result = m().mk_ite(c, a, str().mk_stoi(e));
return BR_REWRITE_FULL;
}
expr* u = nullptr;
unsigned ch = 0;
if (str().is_unit(a, u) && m_util.is_const_char(u, ch)) {
if ('0' <= ch && ch <= '9') {
result = m_autil.mk_int(ch - '0');
}
else {
result = minus_one();
}
return BR_DONE;
}
expr_ref_vector as(m());
str().get_concat_units(a, as);
if (as.empty()) {
result = minus_one();
return BR_DONE;
}
if (str().is_unit(as.back())) {
expr_ref tail(str().mk_stoi(as.back()), m());
expr_ref head(str().mk_concat(as.size() - 1, as.data(), a->get_sort()), m());
expr_ref stoi_head(str().mk_stoi(head), m());
{
auto _seq2559_0 = m_autil.mk_ge(stoi_head, zero());
auto _seq2559_1 = m_autil.mk_add(m_autil.mk_mul(m_autil.mk_int(10), stoi_head), tail);
result = m().mk_ite(_seq2559_0, _seq2559_1, minus_one());
}
result = m().mk_ite(m_autil.mk_ge(tail, zero()),
result,
tail);
result = m().mk_ite(str().mk_is_empty(head),
tail,
result);
return BR_REWRITE_FULL;
}
if (str().is_unit(as.get(0), u) && m_util.is_const_char(u, ch) && '0' == ch) {
result = str().mk_concat(as.size() - 1, as.data() + 1, as[0]->get_sort());
{
auto _seq2573_0 = str().mk_is_empty(result);
auto _seq2573_1 = str().mk_stoi(result);
result = m().mk_ite(_seq2573_0, zero(), _seq2573_1);
}
return BR_REWRITE_FULL;
}
return BR_FAILED;
}
void seq_rewriter::add_next(u_map<expr*>& next, expr_ref_vector& trail, unsigned idx, expr* cond) {
expr* acc;
if (!m().is_true(cond) && next.find(idx, acc)) {
expr* args[2] = { cond, acc };
cond = mk_or(m(), 2, args);
}
trail.push_back(cond);
next.insert(idx, cond);
}
bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) {
seq.reset();
zstring s;
ptr_vector<expr> todo;
expr *e1, *e2;
todo.push_back(e);
while (!todo.empty()) {
e = todo.back();
todo.pop_back();
if (str().is_string(e, s)) {
for (unsigned i = 0; i < s.length(); ++i) {
seq.push_back(str().mk_char(s, i));
}
}
else if (str().is_empty(e)) {
continue;
}
else if (str().is_unit(e, e1)) {
seq.push_back(e1);
}
else if (str().is_concat(e, e1, e2)) {
todo.push_back(e2);
todo.push_back(e1);
}
else {
return false;
}
}
return true;
}
bool seq_rewriter::get_head_tail(expr* s, expr_ref& head, expr_ref& tail) {
expr* h = nullptr, *t = nullptr;
zstring s1;
if (str().is_unit(s, h)) {
head = h;
tail = str().mk_empty(s->get_sort());
return true;
}
if (str().is_string(s, s1) && s1.length() > 0) {
head = m_util.mk_char(s1[0]);
tail = str().mk_string(s1.extract(1, s1.length()));
return true;
}
if (str().is_concat(s, h, t) && get_head_tail(h, head, tail)) {
tail = mk_seq_concat(tail, t);
return true;
}
return false;
}
bool seq_rewriter::get_head_tail_reversed(expr* s, expr_ref& head, expr_ref& tail) {
expr* h = nullptr, *t = nullptr;
zstring s1;
if (str().is_unit(s, t)) {
head = str().mk_empty(s->get_sort());
tail = t;
return true;
}
if (str().is_string(s, s1) && s1.length() > 0) {
head = str().mk_string(s1.extract(0, s1.length() - 1));
tail = m_util.mk_char(s1[s1.length() - 1]);
return true;
}
if (str().is_concat(s, h, t) && get_head_tail_reversed(t, head, tail)) {
head = mk_seq_concat(h, head);
return true;
}
return false;
}
bool seq_rewriter::get_re_head_tail(expr* r, expr_ref& head, expr_ref& tail) {
expr* r1 = nullptr, *r2 = nullptr;
if (re().is_concat(r, r1, r2)) {
head = r1;
tail = r2;
return re().min_length(r1) != UINT_MAX && re().max_length(r1) == re().min_length(r1);
}
return false;
}
bool seq_rewriter::get_re_head_tail_reversed(expr* r, expr_ref& head, expr_ref& tail) {
expr* r1 = nullptr, *r2 = nullptr;
if (re().is_concat(r, r1, r2)) {
unsigned len = re().min_length(r2);
if (len != UINT_MAX && re().max_length(r2) == len) {
if (get_re_head_tail_reversed(r1, head, tail))
tail = mk_re_append(tail, r2);
else {
head = r1;
tail = r2;
}
return true;
}
if (get_re_head_tail_reversed(r2, head, tail)) {
head = mk_re_append(r1, head);
return true;
}
}
return false;
}
expr_ref seq_rewriter::re_and(expr* cond, expr* r) {
expr_ref _cond(cond, m()), _r(r, m());
if (m().is_true(cond))
return expr_ref(r, m());
expr* re_empty = re().mk_empty(r->get_sort());
if (m().is_false(cond))
return expr_ref(re_empty, m());
return expr_ref(m().mk_ite(cond, r, re_empty), m());
}
expr_ref seq_rewriter::re_predicate(expr* cond, sort* seq_sort) {
expr_ref re_with_empty(re().mk_to_re(str().mk_empty(seq_sort)), m());
return re_and(cond, re_with_empty);
}
expr_ref seq_rewriter::is_nullable(expr* r) {
STRACE(seq_verbose, tout << "is_nullable: "
<< mk_pp(r, m()) << std::endl;);
expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr, nullptr), m());
if (!result) {
result = m_derive.nullable(r);
m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, nullptr, result);
}
STRACE(seq_verbose, tout << "is_nullable result: "
<< result << std::endl;);
return result;
}
br_status seq_rewriter::mk_re_reverse(expr* r, expr_ref& result) {
sort* seq_sort = nullptr;
VERIFY(m_util.is_re(r, seq_sort));
expr *r1 = nullptr, *r2 = nullptr, *p = nullptr, *s = nullptr;
expr *s1 = nullptr, *s2 = nullptr;
zstring zs;
unsigned lo = 0, hi = 0;
if (re().is_concat(r, r1, r2)) {
auto a_rev = mk_reverse(r2);
auto b_rev = mk_reverse(r1);
result = mk_re_append(a_rev, b_rev);
return BR_DONE;
}
else if (re().is_star(r, r1)) {
result = mk_star(mk_reverse(r1));
return BR_DONE;
}
else if (re().is_plus(r, r1)) {
result = mk_plus(mk_reverse(r1));
return BR_DONE;
}
else if (re().is_union(r, r1, r2)) {
auto a = mk_reverse(r1);
auto b = mk_reverse(r2);
result = mk_union(a, b);
return BR_DONE;
}
else if (re().is_intersection(r, r1, r2)) {
auto a = mk_reverse(r1);
auto b = mk_reverse(r2);
result = mk_inter(a, b);
return BR_DONE;
}
else if (re().is_diff(r, r1, r2)) {
auto a = mk_reverse(r1);
auto b = mk_reverse(r2);
result = mk_diff(a, b);
return BR_DONE;
}
else if (re().is_xor(r, r1, r2)) {
auto a = mk_reverse(r1);
auto b = mk_reverse(r2);
result = mk_re_xor_simplified(a, b);
return BR_DONE;
}
else if (m().is_ite(r, p, r1, r2)) {
auto a = mk_reverse(r1);
auto b = mk_reverse(r2);
m_br.mk_ite(p, a, b, result);
return BR_DONE;
}
else if (re().is_opt(r, r1)) {
result = mk_opt(mk_reverse(r1));
return BR_DONE;
}
else if (re().is_complement(r, r1)) {
result = mk_complement(mk_reverse(r1));
return BR_DONE;
}
else if (re().is_loop(r, r1, lo)) {
result = mk_loop(mk_reverse(r1), lo);
return BR_DONE;
}
else if (re().is_loop(r, r1, lo, hi)) {
result = mk_loop(mk_reverse(r1), lo, hi);
return BR_DONE;
}
else if (re().is_reverse(r, r1)) {
result = r1;
return BR_DONE;
}
else if (re().is_full_seq(r) ||
re().is_empty(r) ||
re().is_range(r) ||
re().is_full_char(r) ||
re().is_of_pred(r)) {
result = r;
return BR_DONE;
}
else if (re().is_to_re(r, s) && str().is_string(s, zs)) {
result = mk_to_re(str().mk_string(zs.reverse()));
return BR_DONE;
}
else if (re().is_to_re(r, s) && str().is_unit(s)) {
result = r;
return BR_DONE;
}
else if (re().is_to_re(r, s) && str().is_concat(s, s1, s2)) {
auto a_rev = mk_reverse(mk_to_re(s2));
auto b_rev = mk_reverse(mk_to_re(s1));
result = mk_re_append(a_rev, b_rev);
return BR_DONE;
}
else {
return BR_FAILED;
}
}
br_status seq_rewriter::mk_re_derivative(expr* ele, expr* r, expr_ref& result) {
result = mk_derivative(ele, r);
return BR_DONE;
}
expr_ref seq_rewriter::mk_derivative(expr* r) {
auto result = m_derive(seq::derivative_kind::antimirov_t, r);
TRACE(seq, tout << "Derivative of " << mk_pp(r, m()) << "\nis\n" << result << std::endl;);
return result;
}
expr_ref seq_rewriter::mk_derivative(expr* ele, expr* r) {
auto result = m_derive(seq::derivative_kind::antimirov_t, ele, r);
TRACE(seq,
tout << "Derivative of " << mk_pp(r, m()) << " w.r.t. " << mk_pp(ele, m()) << "\nis\n" << result << std::endl;);
return result;
}
expr_ref seq_rewriter::mk_regex_union_normalize(expr* r1, expr* r2) {
expr_ref _r1(r1, m()), _r2(r2, m());
expr *a1, *b1, *a2, *b2;
SASSERT(m_util.is_re(r1));
SASSERT(m_util.is_re(r2));
expr_ref result(m());
std::function<bool(expr*, expr*&, expr*&)> test = [&](expr* t, expr*& a, expr*& b) { return re().is_union(t, a, b); };
std::function<expr* (expr*, expr*)> compose = [&](expr* r1, expr* r2) { return (is_subset(r1, r2) ? r2 : (is_subset(r2, r1) ? r1 : re().mk_union(r1, r2))); };
std::function<bool(expr *, expr *)> is_complement = [&](expr *a, expr *b) {
expr *s;
if (re().is_complement(a, s) && s == b)
return true;
if (re().is_complement(b, s) && s == a)
return true;
return false;
};
if (r1 == r2 || re().is_empty(r2) || re().is_full_seq(r1))
result = r1;
else if (re().is_empty(r1) || re().is_full_seq(r2))
result = r2;
else if (re().is_dot_plus(r1) && re().get_info(r2).min_length > 0)
result = r1;
else if (re().is_dot_plus(r2) && re().get_info(r1).min_length > 0)
result = r2;
else if (false && re().is_intersection(r1, a1, a2) && re().is_intersection(r2, b1, b2) &&
is_complement(a1, b2) && is_complement(a2, b1)) {
result = re().mk_xor(a1, re().mk_complement(a2));
}
else if (false && re().is_intersection(r1, a1, a2) && re().is_intersection(r2, b1, b2) &&
is_complement(a1, b1) && is_complement(a2, b2)) {
result = re().mk_xor(a1, re().mk_complement(a2));
}
else {
unsigned lo1_v = 0, hi1_v = 0, lo2_v = 0, hi2_v = 0;
if (re().is_range(r1, lo1_v, hi1_v) && re().is_range(r2, lo2_v, hi2_v) &&
lo2_v <= hi1_v + 1 && lo1_v <= hi2_v + 1) {
unsigned new_lo = std::min(lo1_v, lo2_v);
unsigned new_hi = std::max(hi1_v, hi2_v);
result = re().mk_range(r1->get_sort(), new_lo, new_hi);
}
else
result = merge_regex_sets(r1, r2, re().mk_full_seq(r1->get_sort()), test, compose);
}
return result;
}
expr_ref seq_rewriter::mk_regex_inter_normalize(expr* r1, expr* r2) {
expr_ref _r1(r1, m()), _r2(r2, m());
SASSERT(m_util.is_re(r1));
SASSERT(m_util.is_re(r2));
expr_ref result(m());
if (re().is_epsilon(r2))
std::swap(r1, r2);
std::function<bool(expr*, expr*&, expr*&)> test = [&](expr* t, expr*& a, expr*& b) { return re().is_intersection(t, a, b); };
std::function<expr* (expr*, expr*)> compose = [&](expr* r1, expr* r2) { return (is_subset(r1, r2) ? r1 : (is_subset(r2, r1) ? r2 : re().mk_inter(r1, r2))); };
if (r1 == r2 || re().is_empty(r1) || re().is_full_seq(r2))
result = r1;
else if (re().is_empty(r2) || re().is_full_seq(r1))
result = r2;
else if (re().is_epsilon(r1)) {
if (re().get_info(r2).nullable == l_true)
result = r1;
else if (re().get_info(r2).nullable == l_false)
result = re().mk_empty(r1->get_sort());
else
result = merge_regex_sets(r1, r2, re().mk_empty(r1->get_sort()), test, compose);
}
else if (re().is_dot_plus(r1) && re().get_info(r2).min_length > 0)
result = r2;
else if (re().is_dot_plus(r2) && re().get_info(r1).min_length > 0)
result = r1;
else {
unsigned lo1_v = 0, hi1_v = 0, lo2_v = 0, hi2_v = 0;
if (re().is_range(r1, lo1_v, hi1_v) && re().is_range(r2, lo2_v, hi2_v)) {
unsigned new_lo = std::max(lo1_v, lo2_v);
unsigned new_hi = std::min(hi1_v, hi2_v);
result = re().mk_range(r1->get_sort(), new_lo, new_hi);
}
else
result = merge_regex_sets(r1, r2, re().mk_empty(r1->get_sort()), test, compose);
}
return result;
}
expr_ref seq_rewriter::merge_regex_sets(expr* r1, expr* r2, expr* unit,
std::function<bool(expr*, expr*&, expr*&)>& test,
std::function<expr* (expr*, expr*)>& compose) {
sort* seq_sort;
expr_ref result(unit, m());
expr_ref_vector prefix(m());
VERIFY(m_util.is_re(r1, seq_sort));
SASSERT(m_util.is_re(r2));
SASSERT(r2->get_sort() == r1->get_sort());
auto compare = [&](expr* x, expr* y) {
expr* z = nullptr;
if (x == y)
return 0;
unsigned xid = (re().is_complement(x, z) ? z->get_id() : x->get_id());
unsigned yid = (re().is_complement(y, z) ? z->get_id() : y->get_id());
SASSERT(xid != yid);
return (xid < yid ? -1 : 1);
};
auto composeresult = [&](expr* suffix) {
result = suffix;
while (!prefix.empty()) {
result = compose(prefix.back(), result);
prefix.pop_back();
}
return result;
};
expr* ar = r1;
expr* br = r2;
while (true) {
if (ar == br)
return composeresult(ar);
if (are_complements(ar, br))
return expr_ref(unit, m());
expr* a, * ar1, * b, * br1;
if (test(br, b, br1) && !test(ar, a, ar1))
std::swap(ar, br);
if (test(br, b, br1)) {
VERIFY(test(ar, a, ar1));
if (are_complements(a, b))
return expr_ref(unit, m());
switch (compare(a, b)) {
case 0:
prefix.push_back(a);
ar = ar1;
br = br1;
break;
case -1:
prefix.push_back(a);
ar = ar1;
break;
case 1:
prefix.push_back(b);
br = br1;
break;
default:
UNREACHABLE();
}
continue;
}
if (test(ar, a, ar1)) {
if (are_complements(a, br))
return expr_ref(unit, m());
switch (compare(a, br)) {
case 0:
return composeresult(ar);
case -1:
prefix.push_back(a);
ar = ar1;
break;
case 1:
prefix.push_back(br);
return composeresult(ar);
default:
UNREACHABLE();
}
continue;
}
if (compare(ar, br) == -1)
std::swap(ar, br);
prefix.push_back(br);
return composeresult(ar);
}
}
bool seq_rewriter::lt_char(expr* ch1, expr* ch2) {
unsigned u1, u2;
return u().is_const_char(ch1, u1) &&
u().is_const_char(ch2, u2) && (u1 < u2);
}
bool seq_rewriter::eq_char(expr* ch1, expr* ch2) {
return ch1 == ch2;
}
bool seq_rewriter::neq_char(expr* ch1, expr* ch2) {
unsigned u1, u2;
return u().is_const_char(ch1, u1) &&
u().is_const_char(ch2, u2) && (u1 != u2);
}
bool seq_rewriter::le_char(expr* ch1, expr* ch2) {
return eq_char(ch1, ch2) || lt_char(ch1, ch2);
}
bool seq_rewriter::is_re_contains_pattern(expr* r, vector<expr_ref_vector>& patterns) {
expr* r1 = nullptr, *r2 = nullptr, *s = nullptr;
if (re().is_concat(r, r1, r2) && re().is_full_seq(r1)) {
r = r2;
patterns.push_back(expr_ref_vector(m()));
}
else {
return false;
}
while (re().is_concat(r, r1, r2)) {
if (re().is_to_re(r1, s))
patterns.back().push_back(s);
else if (re().is_full_seq(r1))
patterns.push_back(expr_ref_vector(m()));
else
return false;
r = r2;
}
return re().is_full_seq(r);
}
bool seq_rewriter::non_overlap(zstring const& s1, zstring const& s2) const {
unsigned sz1 = s1.length(), sz2 = s2.length();
if (sz1 > sz2)
return non_overlap(s2, s1);
auto can_overlap = [&](unsigned start1, unsigned end1, unsigned start2) {
for (unsigned i = start1; i < end1; ++i) {
if (s1[i] != s2[start2 + i])
return false;
}
return true;
};
for (unsigned i = 1; i < sz1; ++i)
if (can_overlap(i, sz1, 0))
return false;
for (unsigned j = 0; j + sz1 < sz2; ++j)
if (can_overlap(0, sz1, j))
return false;
for (unsigned j = sz2 - sz1; j < sz2; ++j)
if (can_overlap(0, sz2 - j, j))
return false;
return true;
}
bool seq_rewriter::non_overlap(expr_ref_vector const& p1, expr_ref_vector const& p2) const {
unsigned sz1 = p1.size(), sz2 = p2.size();
if (sz1 > sz2)
return non_overlap(p2, p1);
if (sz1 == 0 || sz2 == 0)
return false;
zstring s1, s2;
if (sz1 == 1 && sz2 == 1 && str().is_string(p1[0], s1) && str().is_string(p2[0], s2))
return non_overlap(s1, s2);
for (expr* e : p1)
if (!str().is_unit(e))
return false;
for (expr* e : p2)
if (!str().is_unit(e))
return false;
auto can_overlap = [&](unsigned start1, unsigned end1, unsigned start2) {
for (unsigned i = start1; i < end1; ++i) {
if (m().are_distinct(p1[i], p2[start2 + i]))
return false;
if (!m().are_equal(p1[i], p2[start2 + i]))
return true;
}
return true;
};
for (unsigned i = 1; i < sz1; ++i)
if (can_overlap(i, sz1, 0))
return false;
for (unsigned j = 0; j + sz1 < sz2; ++j)
if (can_overlap(0, sz1, j))
return false;
for (unsigned j = sz2 - sz1; j < sz2; ++j)
if (can_overlap(0, sz2 - j, j))
return false;
return true;
}
bool seq_rewriter::rewrite_contains_pattern(expr* a, expr* b, expr_ref& result) {
vector<expr_ref_vector> patterns;
expr* x = nullptr, *y = nullptr, *z = nullptr, *u = nullptr;
if (!str().is_concat(a, x, y))
return false;
if (!is_re_contains_pattern(b, patterns))
return false;
m_lhs.reset();
u = y;
while (str().is_concat(u, z, u) && (str().is_unit(z) || str().is_string(z))) {
m_lhs.push_back(z);
}
for (auto const& p : patterns)
if (!non_overlap(p, m_lhs))
return false;
expr_ref_vector fmls(m());
sort* rs = b->get_sort();
expr_ref full(re().mk_full_seq(rs), m()), prefix(m()), suffix(m());
fmls.push_back(re().mk_in_re(y, b));
prefix = full;
for (unsigned i = 0; i < patterns.size(); ++i) {
for (expr* e : patterns[i])
prefix = re().mk_concat(prefix, re().mk_to_re(e));
prefix = re().mk_concat(prefix, full);
suffix = full;
for (unsigned j = i + 1; j < patterns.size(); ++j) {
for (expr* e : patterns[j])
suffix = re().mk_concat(suffix, re().mk_to_re(e));
suffix = re().mk_concat(suffix, full);
}
{
auto _seq3217_0 = re().mk_in_re(x, prefix);
auto _seq3217_1 = re().mk_in_re(y, suffix);
fmls.push_back(m().mk_and(_seq3217_0, _seq3217_1));
}
}
result = mk_or(fmls);
return true;
}
br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
STRACE(seq_verbose, tout << "mk_str_in_regexp: " << mk_pp(a, m())
<< ", " << mk_pp(b, m()) << std::endl;);
if (re().is_empty(b)) {
result = m().mk_false();
return BR_DONE;
}
if (re().is_full_seq(b)) {
result = m().mk_true();
return BR_DONE;
}
{
expr* rlo = nullptr, *rhi = nullptr;
if (re().is_range(b, rlo, rhi)) {
auto concrete_char = [&](expr* e) {
zstring s;
expr* ch = nullptr;
unsigned uc = 0;
return (str().is_string(e, s) && s.length() == 1) ||
(str().is_unit(e, ch) && m_util.is_const_char(ch, uc));
};
if (!concrete_char(rlo) || !concrete_char(rhi)) {
expr_ref_vector conj(m());
expr_ref lo_length(str().mk_length(rlo), m());
conj.push_back(m().mk_eq(lo_length, one()));
{
expr_ref lhs(str().mk_length(rhi), m());
auto rhs = one();
conj.push_back(m().mk_eq(lhs, rhs));
}
{
expr_ref lhs(str().mk_length(a), m());
auto rhs = one();
conj.push_back(m().mk_eq(lhs, rhs));
}
conj.push_back(str().mk_lex_le(rlo, a));
conj.push_back(str().mk_lex_le(a, rhi));
result = m().mk_and(conj);
return BR_REWRITE_FULL;
}
}
}
zstring s;
if (str().is_string(a, s) && re().is_ground(b)) {
expr_ref r(b, m());
for (unsigned i = 0; i < s.length(); ++i) {
if (re().is_empty(r)) {
result = m().mk_false();
return BR_DONE;
}
unsigned ch = s[i];
expr_ref new_r = mk_derivative(m_util.mk_char(ch), r);
r = new_r;
}
switch (re().get_info(r).nullable) {
case l_true:
result = m().mk_true();
return BR_DONE;
case l_false:
result = m().mk_false();
return BR_DONE;
default:
break;
}
}
expr *ra_x = nullptr, *ra_a = nullptr, *ra_b = nullptr;
zstring sa_val, sb_val;
if (str().is_replace_all(a, ra_x, ra_a, ra_b) && ra_a == ra_b) {
result = ra_x;
return BR_DONE;
}
if (str().is_replace_all(a, ra_x, ra_a, ra_b) && str().is_string(ra_a, sa_val) && sa_val.length() == 1 &&
str().is_string(ra_b, sb_val) && sb_val.length() == 1 && sa_val[0] != sb_val[0] && re().is_ground(b) &&
re().get_info(b).classical) {
expr_ref new_re = re_replace_char(b, sa_val[0], sb_val[0], ra_a, ra_b);
result = re().mk_in_re(ra_x, new_re);
return BR_REWRITE_FULL;
}
expr_ref b_s(m());
if (lift_str_from_to_re(b, b_s)) {
result = m_br.mk_eq_rw(a, b_s);
return BR_REWRITE_FULL;
}
expr* c = nullptr, *d = nullptr, *e = nullptr;
if (re().is_concat(b, c, d) && re().is_to_re(c, e) && re().is_full_seq(d)) {
result = str().mk_prefix(e, a);
return BR_REWRITE1;
}
if (re().is_concat(b, c, d) && re().is_to_re(d, e) && re().is_full_seq(c)) {
result = str().mk_suffix(e, a);
return BR_REWRITE1;
}
expr* b1 = nullptr;
expr* eps = nullptr;
if (re().is_opt(b, b1) ||
(re().is_union(b, b1, eps) && re().is_epsilon(eps)) ||
(re().is_union(b, eps, b1) && re().is_epsilon(eps)))
{
auto len_a = str().mk_length(a);
auto is_empty = m().mk_eq(len_a, zero());
auto in_b1 = re().mk_in_re(a, b1);
result = m().mk_ite(is_empty, m().mk_true(), in_b1);
return BR_REWRITE_FULL;
}
if (str().is_empty(a)) {
result = is_nullable(b);
if (str().is_in_re(result))
return BR_DONE;
else
return BR_REWRITE_FULL;
}
if (!u().can_be_member(a, b)) {
result = m().mk_false();
return BR_DONE;
}
return BR_FAILED;
}
bool seq_rewriter::has_fixed_length_constraint(expr* a, unsigned& len) {
unsigned minl = re().min_length(a), maxl = re().max_length(a);
len = minl;
return minl == maxl;
}
bool seq_rewriter::lift_str_from_to_re_ite(expr* r, expr_ref& result) {
expr* cond = nullptr, * then_r = nullptr, * else_r = nullptr;
expr_ref then_s(m());
expr_ref else_s(m());
if (m().is_ite(r, cond, then_r, else_r) &&
lift_str_from_to_re(then_r, then_s) &&
lift_str_from_to_re(else_r, else_s)) {
result = m().mk_ite(cond, then_s, else_s);
return true;
}
return false;
}
bool seq_rewriter::lift_str_from_to_re(expr* r, expr_ref& result)
{
expr* s = nullptr;
if (re().is_to_re(r, s)) {
result = s;
return true;
}
return lift_str_from_to_re_ite(r, result);
}
br_status seq_rewriter::mk_str_to_regexp(expr* a, expr_ref& result) {
expr* s = nullptr, *i = nullptr;
if (str().is_at(a, s, i)) {
expr_ref non_negative(m_autil.mk_ge(i, zero()), m());
expr_ref below_length(m_autil.mk_lt(i, str().mk_length(s)), m());
expr_ref valid(m().mk_and(non_negative, below_length), m());
expr_ref nth(str().mk_unit(str().mk_nth_i(s, i)), m());
expr_ref non_empty(re().mk_to_re(nth), m());
expr_ref empty(re().mk_to_re(str().mk_empty(a->get_sort())), m());
result = m().mk_ite(valid, non_empty, empty);
return BR_REWRITE_FULL;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_re_concat(expr* a, expr* b, expr_ref& result) {
auto accepts_empty_word = [&](expr* r) {
auto info = re().get_info(r);
return info.interpreted && info.nullable == l_true && info.min_length == 0;
};
auto starts_with_full_seq = [&](expr* r) {
expr* r1 = nullptr, *r2 = nullptr;
return re().is_full_seq(r) || (re().is_concat(r, r1, r2) && re().is_full_seq(r1));
};
auto ends_with_full_seq = [&](expr* r) {
expr* r1 = nullptr, *r2 = nullptr;
while (re().is_concat(r, r1, r2))
r = r2;
return re().is_full_seq(r);
};
auto all_inter_arms_end_with_full_seq = [&](expr* r) {
ptr_buffer<expr> todo;
todo.push_back(r);
while (!todo.empty()) {
expr* r1 = nullptr, *r2 = nullptr;
expr* t = todo.back();
todo.pop_back();
if (re().is_intersection(t, r1, r2)) {
todo.push_back(r1);
todo.push_back(r2);
}
else if (!ends_with_full_seq(t)) {
return false;
}
}
return true;
};
if (re().is_full_seq(a) && re().is_full_seq(b)) {
result = a;
return BR_DONE;
}
if (re().is_full_seq(a) && accepts_empty_word(b)) {
result = a;
return BR_DONE;
}
if (re().is_full_seq(b) && accepts_empty_word(a)) {
result = b;
return BR_DONE;
}
if (re().is_full_seq(b) && ends_with_full_seq(a)) {
result = a;
return BR_DONE;
}
if (re().is_full_seq(a) && starts_with_full_seq(b)) {
result = b;
return BR_DONE;
}
expr* u1 = nullptr, *u2 = nullptr;
if (re().is_full_seq(a) && re().is_union(b, u1, u2) &&
(starts_with_full_seq(u1) || starts_with_full_seq(u2))) {
{
auto _seq3554_0 = mk_regex_concat(a, u1);
auto _seq3554_1 = mk_regex_concat(a, u2);
result = mk_regex_union_normalize(_seq3554_0, _seq3554_1);
}
return BR_REWRITE2;
}
if (re().is_intersection(a, u1, u2) && re().is_full_seq(b) &&
all_inter_arms_end_with_full_seq(a)) {
result = a;
return BR_DONE;
}
if (re().is_empty(a)) {
result = a;
return BR_DONE;
}
if (re().is_empty(b)) {
result = b;
return BR_DONE;
}
if (re().is_epsilon(a)) {
result = b;
return BR_DONE;
}
if (re().is_epsilon(b)) {
result = a;
return BR_DONE;
}
if (re().is_full_char(a) && re().is_full_seq(b)) {
result = mk_plus(a);
return BR_DONE;
}
if (re().is_full_char(b) && re().is_full_seq(a)) {
result = mk_plus(b);
return BR_DONE;
}
expr_ref a_str(m());
expr_ref b_str(m());
if (lift_str_from_to_re(a, a_str) && lift_str_from_to_re(b, b_str)) {
result = mk_to_re(mk_seq_concat(a_str, b_str));
return BR_DONE;
}
expr *a1 = nullptr, *a2 = nullptr;
expr* b1 = nullptr;
if (re().is_to_re(a, a1) && re().is_to_re(b, b1)) {
result = mk_to_re(mk_seq_concat(a1, b1));
return BR_DONE;
}
if (re().is_star(a, a1) && re().is_star(b, b1) && a1 == b1) {
result = a;
return BR_DONE;
}
expr* b2 = nullptr, *b3 = nullptr;
if (re().is_star(a, a1) && re().is_concat(b, b1, b2) && re().is_star(b1, b3) && a1 == b3) {
result = b;
return BR_DONE;
}
if (re().is_star(a, a1) && a1 == b) {
result = mk_re_append(b, a);
return BR_DONE;
}
unsigned lo1, hi1, lo2, hi2;
if (re().is_loop(a, a1, lo1, hi1) && lo1 <= hi1 &&
re().is_loop(b, b1, lo2, hi2) && lo2 <= hi2 && a1 == b1 &&
hi1 <= UINT_MAX - hi2) {
result = mk_loop(a1, lo1 + lo2, hi1 + hi2);
return BR_DONE;
}
if (re().is_loop(a, a1, lo1) && re().is_loop(b, b1, lo2) && a1 == b1 &&
lo1 <= UINT_MAX - lo2) {
result = mk_loop(a1, lo1 + lo2);
return BR_DONE;
}
for (unsigned i = 0; i < 2; ++i) {
if (re().is_loop(a, a1, lo1) && re().is_loop(b, b1, lo2, hi2) &&
lo2 <= hi2 && a1 == b1 && lo1 <= UINT_MAX - lo2) {
result = mk_loop(a1, lo1 + lo2);
return BR_DONE;
}
if (re().is_loop(a, a1, lo1, hi1) && lo1 <= hi1 && re().is_star(b, b1) && a1 == b1) {
result = mk_loop(a1, lo1);
return BR_DONE;
}
if (re().is_loop(a, a1, lo1) && re().is_star(b, b1) && a1 == b1) {
result = a;
return BR_DONE;
}
if (re().is_loop(a, a1, lo1, hi1) && lo1 <= hi1 && hi1 < UINT_MAX && a1 == b) {
result = mk_loop(a1, lo1 + 1, hi1 + 1);
return BR_DONE;
}
std::swap(a, b);
}
expr* c = nullptr;
if (m().is_ite(a, c, a1, b1)) {
auto then_r = mk_re_append(a1, b);
auto else_r = mk_re_append(b1, b);
m_br.mk_ite(c, then_r, else_r, result);
return BR_DONE;
}
if (m().is_ite(b, c, a1, b1)) {
auto then_r = mk_re_append(a, a1);
auto else_r = mk_re_append(a, b1);
m_br.mk_ite(c, then_r, else_r, result);
return BR_DONE;
}
if (re().is_concat(a, a1, a2)) {
result = mk_re_append(a1, mk_re_append(a2, b));
return BR_DONE;
}
return BR_FAILED;
}
expr_ref seq_rewriter::mk_regex_concat(expr *r, expr *s) {
return mk_re_append(r, s);
}
bool seq_rewriter::are_complements(expr* r1, expr* r2) const {
expr* r = nullptr;
if (re().is_complement(r1, r) && r == r2)
return true;
if (re().is_complement(r2, r) && r == r1)
return true;
return false;
}
bool seq_rewriter::is_subset(expr* r1, expr* r2) const {
return m_subset.is_subset(r1, r2);
}
bool seq_rewriter::try_collapse_re_union(expr* a, expr* b, expr_ref& result) {
sort* seq_sort = nullptr;
if (!u().is_re(a->get_sort(), seq_sort))
return false;
seq::range_predicate pa(u().max_char()), pb(u().max_char());
if (!seq::regex_to_range_predicate(u(), a, pa))
return false;
if (!seq::regex_to_range_predicate(u(), b, pb))
return false;
result = seq::range_predicate_to_regex(u(), pa | pb, seq_sort);
return true;
}
bool seq_rewriter::try_collapse_re_inter(expr* a, expr* b, expr_ref& result) {
sort* seq_sort = nullptr;
if (!u().is_re(a->get_sort(), seq_sort))
return false;
seq::range_predicate pa(u().max_char()), pb(u().max_char());
if (!seq::regex_to_range_predicate(u(), a, pa))
return false;
if (!seq::regex_to_range_predicate(u(), b, pb))
return false;
result = seq::range_predicate_to_regex(u(), pa & pb, seq_sort);
return true;
}
br_status seq_rewriter::mk_re_union0(expr* a, expr* b, expr_ref& result) {
if (a == b) {
result = a;
return BR_DONE;
}
if (re().is_empty(a)) {
result = b;
return BR_DONE;
}
if (re().is_empty(b)) {
result = a;
return BR_DONE;
}
if (re().is_full_seq(a)) {
result = a;
return BR_DONE;
}
if (re().is_full_seq(b)) {
result = b;
return BR_DONE;
}
if (re().is_star(a) && re().is_epsilon(b)) {
result = a;
return BR_DONE;
}
if (re().is_star(b) && re().is_epsilon(a)) {
result = b;
return BR_DONE;
}
if (are_complements(a, b)) {
result = re().mk_full_seq(a->get_sort());
return BR_DONE;
}
expr *c = nullptr, *r1 = nullptr, *r2 = nullptr;
if (m().is_ite(a, c, r1, r2)) {
{
auto _seq3786_0 = re().mk_union(r1, b);
auto _seq3786_1 = re().mk_union(r2, b);
result = m().mk_ite(c, _seq3786_0, _seq3786_1);
}
return BR_REWRITE3;
}
if (m().is_ite(b, c, r1, r2)) {
{
auto _seq3790_0 = re().mk_union(a, r1);
auto _seq3790_1 = re().mk_union(a, r2);
result = m().mk_ite(c, _seq3790_0, _seq3790_1);
}
return BR_REWRITE3;
}
if (try_collapse_re_union(a, b, result))
return BR_DONE;
return BR_FAILED;
}
br_status seq_rewriter::mk_re_union(expr* a, expr* b, expr_ref& result) {
if (try_collapse_re_union(a, b, result))
return BR_DONE;
result = mk_regex_union_normalize(a, b);
return BR_DONE;
}
br_status seq_rewriter::mk_re_complement(expr* a, expr_ref& result) {
expr *e1 = nullptr, *e2 = nullptr;
if (re().is_intersection(a, e1, e2)) {
auto a1 = mk_complement(e1);
auto b1 = mk_complement(e2);
result = mk_union(a1, b1);
return BR_DONE;
}
if (re().is_union(a, e1, e2)) {
auto a1 = mk_complement(e1);
auto b1 = mk_complement(e2);
result = mk_inter(a1, b1);
return BR_DONE;
}
if (re().is_empty(a)) {
result = re().mk_full_seq(a->get_sort());
return BR_DONE;
}
if (re().is_full_seq(a)) {
result = re().mk_empty(a->get_sort());
return BR_DONE;
}
if (re().is_complement(a, e1)) {
result = e1;
return BR_DONE;
}
if (re().is_to_re(a, e1) && str().is_empty(e1)) {
result = mk_plus(re().mk_full_char(a->get_sort()));
return BR_DONE;
}
expr* c = nullptr;
if (m().is_ite(a, c, e1, e2)) {
auto then_r = mk_complement(e1);
auto else_r = mk_complement(e2);
m_br.mk_ite(c, then_r, else_r, result);
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_re_inter0(expr* a, expr* b, expr_ref& result) {
if (a == b) {
result = a;
return BR_DONE;
}
if (re().is_empty(a)) {
result = a;
return BR_DONE;
}
if (re().is_empty(b)) {
result = b;
return BR_DONE;
}
if (re().is_full_seq(a)) {
result = b;
return BR_DONE;
}
if (re().is_full_seq(b)) {
result = a;
return BR_DONE;
}
if (are_complements(a, b)) {
result = re().mk_empty(a->get_sort());
return BR_DONE;
}
expr *c = nullptr, *r1 = nullptr, *r2 = nullptr;
if (m().is_ite(a, c, r1, r2)) {
{
auto _seq3878_0 = re().mk_inter(r1, b);
auto _seq3878_1 = re().mk_inter(r2, b);
result = m().mk_ite(c, _seq3878_0, _seq3878_1);
}
return BR_REWRITE3;
}
if (m().is_ite(b, c, r1, r2)) {
{
auto _seq3882_0 = re().mk_inter(a, r1);
auto _seq3882_1 = re().mk_inter(a, r2);
result = m().mk_ite(c, _seq3882_0, _seq3882_1);
}
return BR_REWRITE3;
}
if (try_collapse_re_inter(a, b, result))
return BR_DONE;
return BR_FAILED;
}
br_status seq_rewriter::mk_re_inter(expr* a, expr* b, expr_ref& result) {
if (try_collapse_re_inter(a, b, result))
return BR_DONE;
result = mk_regex_inter_normalize(a, b);
return BR_DONE;
}
br_status seq_rewriter::mk_re_diff(expr* a, expr* b, expr_ref& result) {
seq::range_predicate pa(u().max_char()), pb(u().max_char());
sort* seq_sort = nullptr;
if (u().is_re(a->get_sort(), seq_sort)
&& seq::regex_to_range_predicate(u(), a, pa)
&& seq::regex_to_range_predicate(u(), b, pb)) {
result = seq::range_predicate_to_regex(u(), pa - pb, seq_sort);
return BR_DONE;
}
result = mk_inter(a, mk_complement(b));
return BR_DONE;
}
br_status seq_rewriter::mk_re_xor0(expr* a, expr* b, expr_ref& result) {
if (a == b) {
result = re().mk_empty(a->get_sort());
return BR_DONE;
}
if (re().is_empty(a)) {
result = b;
return BR_DONE;
}
if (re().is_empty(b)) {
result = a;
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_re_xor(expr* a, expr* b, expr_ref& result) {
if (a == b) {
result = re().mk_empty(a->get_sort());
return BR_DONE;
}
if (re().is_empty(a)) {
result = b;
return BR_DONE;
}
if (re().is_empty(b)) {
result = a;
return BR_DONE;
}
if (re().is_full_seq(a)) {
result = mk_complement(b);
return BR_DONE;
}
if (re().is_full_seq(b)) {
result = mk_complement(a);
return BR_DONE;
}
expr* ra = nullptr, * rb = nullptr;
bool ca = re().is_complement(a, ra);
bool cb = re().is_complement(b, rb);
if (ca && cb) {
result = mk_re_xor_simplified(ra, rb);
return BR_DONE;
}
if (ca) {
result = mk_complement(mk_re_xor_simplified(ra, b));
return BR_DONE;
}
if (cb) {
result = mk_complement(mk_re_xor_simplified(a, rb));
return BR_DONE;
}
if (a->get_id() > b->get_id()) {
result = re().mk_xor(b, a);
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_re_loop(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) {
rational n1, n2;
unsigned lo, hi, lo2, hi2, np;
expr* a = nullptr;
switch (num_args) {
case 1:
np = f->get_num_parameters();
lo2 = np > 0 ? f->get_parameter(0).get_int() : 0;
hi2 = np > 1 ? f->get_parameter(1).get_int() : lo2;
if (np == 2 && (lo2 > hi2 || hi2 < 0)) {
result = re().mk_empty(args[0]->get_sort());
return BR_DONE;
}
if (np == 1 && lo2 < 0) {
result = re().mk_empty(args[0]->get_sort());
return BR_DONE;
}
if (np == 2 && lo2 == 0 && hi2 == 0) {
result = mk_to_re(str().mk_empty(re().to_seq(args[0]->get_sort())));
return BR_DONE;
}
if (np == 1 && lo2 == 0) {
result = mk_star(args[0]);
return BR_DONE;
}
if (np == 2 && lo2 == 1 && hi2 == 1) {
result = args[0];
return BR_DONE;
}
if (re().is_loop(args[0], a, lo) && np == 1 &&
(lo == 0 || lo2 <= UINT_MAX / lo)) {
result = mk_loop(a, lo2 * lo);
return BR_DONE;
}
if (re().is_loop(args[0], a, lo, hi) && np == 2 && lo == hi && lo2 == hi2 &&
(lo == 0 || lo2 <= UINT_MAX / lo)) {
result = mk_loop(a, lo2 * lo, hi2 * hi);
return BR_DONE;
}
break;
case 2:
if (m_autil.is_numeral(args[1], n1) && n1.is_unsigned()) {
result = mk_loop(args[0], n1.get_unsigned());
return BR_DONE;
}
if (m_autil.is_numeral(args[1], n1) && n1 < 0) {
result = re().mk_empty(args[0]->get_sort());
return BR_DONE;
}
break;
case 3:
if (m_autil.is_numeral(args[1], n1) && n1.is_unsigned() &&
m_autil.is_numeral(args[2], n2) && n2.is_unsigned()) {
result = mk_loop(args[0], n1.get_unsigned(), n2.get_unsigned());
return BR_DONE;
}
break;
default:
break;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_re_power(func_decl* f, expr* a, expr_ref& result) {
unsigned p = f->get_parameter(0).get_int();
result = mk_loop(a, p, p);
return BR_DONE;
}
br_status seq_rewriter::mk_re_star(expr* a, expr_ref& result) {
expr* b, *c, *b1, *c1;
if (re().is_star(a) || re().is_full_seq(a)) {
result = a;
return BR_DONE;
}
if (re().is_full_char(a)) {
result = re().mk_full_seq(a->get_sort());
return BR_DONE;
}
if (re().is_empty(a)) {
sort* seq_sort = nullptr;
VERIFY(m_util.is_re(a, seq_sort));
result = mk_to_re(str().mk_empty(seq_sort));
return BR_DONE;
}
if (re().is_to_re(a, b) && str().is_empty(b)) {
result = a;
return BR_DONE;
}
if (re().is_plus(a, b)) {
if (re().is_full_char(b))
result = re().mk_full_seq(a->get_sort());
else
result = mk_star(b);
return BR_DONE;
}
if (re().is_union(a, b, c)) {
if (re().is_star(b, b1)) {
result = mk_star(mk_union(b1, c));
return BR_DONE;
}
if (re().is_star(c, c1)) {
result = mk_star(mk_union(b, c1));
return BR_DONE;
}
if (re().is_epsilon(b)) {
result = mk_star(c);
return BR_DONE;
}
if (re().is_epsilon(c)) {
result = mk_star(b);
return BR_DONE;
}
}
if (re().is_concat(a, b, c) &&
re().is_star(b, b1) && re().is_star(c, c1)) {
result = mk_star(mk_union(b1, c1));
return BR_DONE;
}
if (re().is_concat(a, b, c) && re().is_full_seq(b)) {
sort* seq_sort = nullptr;
VERIFY(m_util.is_re(a, seq_sort));
result = mk_union(re().mk_epsilon(seq_sort), a);
return BR_DONE;
}
if (m().is_ite(a, c, b1, c1)) {
if ((re().is_full_char(b1) || re().is_full_seq(b1)) &&
(re().is_full_char(c1) || re().is_full_seq(c1))) {
result = re().mk_full_seq(b1->get_sort());
return BR_REWRITE2;
}
auto then_r = mk_star(b1);
auto else_r = mk_star(c1);
m_br.mk_ite(c, then_r, else_r, result);
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_re_range(expr* lo, expr* hi, expr_ref& result) {
zstring slo, shi;
unsigned clo = 0, chi = 0;
expr *lo1, *hi1;
unsigned len = 0;
bool is_empty = false;
len = min_length(lo).second;
if (len > 1)
is_empty = true;
len = min_length(hi).second;
if (len > 1)
is_empty = true;
if (max_length(lo) == std::make_pair(true, rational(0)))
is_empty = true;
if (max_length(hi) == std::make_pair(true, rational(0)))
is_empty = true;
if (is_empty) {
sort* srt = re().mk_re(lo->get_sort());
result = re().mk_empty(srt);
return BR_DONE;
}
bool has_clo = false, has_chi = false;
if (str().is_string(lo, slo) && slo.length() == 1) {
clo = slo[0];
has_clo = true;
}
else if (str().is_unit(lo, lo1) && m_util.is_const_char(lo1, clo))
has_clo = true;
if (str().is_string(hi, shi) && shi.length() == 1) {
chi = shi[0];
has_chi = true;
}
else if (str().is_unit(hi, hi1) && m_util.is_const_char(hi1, chi))
has_chi = true;
if (!has_clo || !has_chi)
return BR_FAILED;
if (clo > chi) {
sort* srt = re().mk_re(lo->get_sort());
result = re().mk_empty(srt);
return BR_DONE;
}
if (clo == chi) {
result = re().mk_to_re(str().mk_string(zstring(clo)));
return BR_DONE;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_re_plus(expr* a, expr_ref& result) {
if (re().is_full_char(a))
return BR_FAILED;
if (re().is_empty(a)) {
result = a;
return BR_DONE;
}
if (re().is_full_seq(a)) {
result = a;
return BR_DONE;
}
if (re().is_epsilon(a)) {
result = a;
return BR_DONE;
}
if (re().is_plus(a)) {
result = a;
return BR_DONE;
}
if (re().is_star(a)) {
result = a;
return BR_DONE;
}
result = mk_re_append(a, mk_star(a));
return BR_DONE;
}
br_status seq_rewriter::mk_re_opt(expr* a, expr_ref& result) {
sort* s = nullptr;
VERIFY(m_util.is_re(a, s));
result = mk_union(mk_to_re(str().mk_empty(s)), a);
return BR_DONE;
}
void seq_rewriter::intersect(unsigned lo, unsigned hi, svector<std::pair<unsigned, unsigned>>& ranges) {
unsigned j = 0;
for (unsigned i = 0; i < ranges.size(); ++i) {
auto [lo1, hi1] = ranges[i];
if (hi < lo1)
break;
if (hi1 >= lo)
ranges[j++] = std::make_pair(std::max(lo1, lo), std::min(hi1, hi));
}
ranges.shrink(j);
}
void seq_rewriter::elim_condition(expr* elem, expr_ref& cond) {
expr_ref_vector conds(m());
expr_ref_vector conds_range(m());
flatten_and(cond, conds);
expr* lhs = nullptr, *rhs = nullptr, *e1 = nullptr;
bool all_ranges = false;
if (u().is_char(elem)) {
all_ranges = true;
unsigned ch = 0, ch2 = 0;
svector<std::pair<unsigned, unsigned>> ranges, ranges1;
ranges.push_back(std::make_pair(0, u().max_char()));
auto exclude_range = [&](unsigned lower, unsigned upper) {
SASSERT(lower <= upper);
if (lower == 0) {
if (upper == u().max_char())
ranges.reset();
else
intersect(upper + 1, u().max_char(), ranges);
}
else if (upper == u().max_char())
intersect(0, lower - 1, ranges);
else {
ranges1.reset();
ranges1.append(ranges);
intersect(0, lower - 1, ranges);
intersect(upper + 1, u().max_char(), ranges1);
ranges.append(ranges1);
}
};
bool negated = false;
for (expr* e : conds) {
if (u().is_char_const_range(elem, e, ch, ch2, negated)) {
if (ch > ch2) {
if (negated)
continue;
else
ranges.reset();
}
else if (negated)
exclude_range(ch, ch2);
else
intersect(ch, ch2, ranges);
conds_range.push_back(e);
}
else if (m().is_true(e) || (m().is_eq(e, lhs, rhs) && lhs == rhs))
continue;
else if (m().is_not(e, e1) && m().is_eq(e1, lhs, rhs) && u().is_const_char(lhs, ch) && u().is_const_char(rhs, ch2) && ch != ch2)
continue;
else if (u().is_char_le(e, lhs, rhs) && u().is_const_char(lhs, ch) && u().is_const_char(rhs, ch2) && ch <= ch2)
continue;
else if (m().is_not(e, e1) && u().is_char_le(e1, lhs, rhs) && u().is_const_char(lhs, ch) && u().is_const_char(rhs, ch2) && ch > ch2)
continue;
else if (m().is_false(e) || (m().is_not(e, e1) && m().is_eq(e1, lhs, rhs) && lhs == rhs))
ranges.reset();
else if (u().is_char_le(e, lhs, rhs) && u().is_const_char(lhs, ch) && u().is_const_char(rhs, ch2) && ch > ch2)
ranges.reset();
else if (m().is_not(e, e1) && u().is_char_le(e1, lhs, rhs) && u().is_const_char(lhs, ch) && u().is_const_char(rhs, ch2) && ch <= ch2)
ranges.reset();
else {
all_ranges = false;
break;
}
if (ranges.empty())
break;
}
if (all_ranges) {
if (ranges.empty()) {
cond = m().mk_false();
return;
}
if (is_uninterp_const(elem)) {
cond = m().mk_true();
return;
}
conds.set(conds_range);
}
}
expr* solution = nullptr;
for (expr* e : conds) {
if (!m().is_eq(e, lhs, rhs))
continue;
if (rhs == elem)
std::swap(lhs, rhs);
if (lhs != elem)
continue;
solution = rhs;
break;
}
if (solution) {
expr_safe_replace rep(m());
rep.insert(elem, solution);
rep(cond);
if (!is_uninterp_const(elem)) {
auto eq_sol = m().mk_eq(elem, solution);
cond = m().mk_and(eq_sol, cond);
}
}
else if (all_ranges) {
if (conds.empty())
cond = m().mk_true();
else if (conds.size() == 1)
cond = conds.get(0);
else
cond = m().mk_and(conds);
}
}
br_status seq_rewriter::reduce_re_is_empty(expr* r, expr_ref& result) {
expr* r1, *r2, *r3, *r4;
zstring s1, s2;
unsigned lo, hi;
auto eq_empty = [&](expr* r) { return m().mk_eq(r, re().mk_empty(r->get_sort())); };
if (re().is_union(r, r1, r2)) {
expr_ref lhs(eq_empty(r1), m());
expr_ref rhs(eq_empty(r2), m());
result = m().mk_and(lhs, rhs);
return BR_REWRITE2;
}
if (re().is_star(r) ||
re().is_to_re(r) ||
re().is_full_char(r) ||
re().is_full_seq(r)) {
result = m().mk_false();
return BR_DONE;
}
if (re().is_concat(r, r1, r2)) {
expr_ref lhs(eq_empty(r1), m());
expr_ref rhs(eq_empty(r2), m());
result = m().mk_or(lhs, rhs);
return BR_REWRITE2;
}
else if (re().is_range(r, r1, r2) &&
str().is_string(r1, s1) && str().is_string(r2, s2) &&
s1.length() == 1 && s2.length() == 1) {
result = m().mk_bool_val(s1[0] > s2[0]);
return BR_DONE;
}
else if (re().is_range(r, r1, r2) &&
str().is_string(r1, s1) && s1.length() != 1) {
result = m().mk_true();
return BR_DONE;
}
else if (re().is_range(r, r1, r2) &&
str().is_string(r2, s2) && s2.length() != 1) {
result = m().mk_true();
return BR_DONE;
}
else if ((re().is_loop(r, r1, lo) ||
re().is_loop(r, r1, lo, hi)) && lo == 0) {
result = m().mk_false();
return BR_DONE;
}
else if (re().is_loop(r, r1, lo) ||
(re().is_loop(r, r1, lo, hi) && lo <= hi)) {
result = eq_empty(r1);
return BR_REWRITE1;
}
else if (re().is_intersection(r, r1, r2) && re().is_union(r1, r3, r4)) {
auto a1 = re().mk_inter(r3, r2);
auto b1 = re().mk_inter(r4, r2);
result = eq_empty(re().mk_union(a1, b1));
return BR_REWRITE3;
}
else if (re().is_intersection(r, r1, r2) && re().is_union(r2, r3, r4)) {
auto a1 = re().mk_inter(r3, r1);
auto b1 = re().mk_inter(r4, r1);
result = eq_empty(re().mk_union(a1, b1));
return BR_REWRITE3;
}
return BR_FAILED;
}
br_status seq_rewriter::reduce_re_eq(expr* l, expr* r, expr_ref& result) {
if (re().is_empty(l)) {
std::swap(l, r);
}
if (re().is_empty(r)) {
return reduce_re_is_empty(l, result);
}
if (l == r) {
result = m().mk_true();
return BR_DONE;
}
if (!m_in_bisim && re().is_ground(l) && re().is_ground(r)) {
flet<bool> _block(m_in_bisim, true);
seq::regex_bisim bisim(*this);
switch (bisim.are_equivalent(l, r)) {
case l_true:
result = m().mk_true();
return BR_DONE;
case l_false:
result = m().mk_false();
return BR_DONE;
case l_undef:
break;
}
}
return BR_FAILED;
}
br_status seq_rewriter::mk_le_core(expr * l, expr * r, expr_ref & result) {
return BR_FAILED;
rational n;
if (str().is_length(r) && m_autil.is_numeral(l, n) && n <= 0) {
result = m().mk_true();
return BR_DONE;
}
expr* e = nullptr;
if (str().is_length(l, e) && m_autil.is_numeral(r, n)) {
if (n == 0)
result = str().mk_is_empty(e);
else if (n < 0)
result = m().mk_false();
else
return BR_FAILED;
return BR_REWRITE1;
}
return BR_FAILED;
}
br_status seq_rewriter::mk_eq_core(expr * l, expr * r, expr_ref & result) {
TRACE(seq, tout << mk_pp(l, m()) << " == " << mk_pp(r, m()) << "\n");
expr_ref_vector res(m());
expr_ref_pair_vector new_eqs(m());
if (m_util.is_re(l)) {
return reduce_re_eq(l, r, result);
}
bool changed = false;
if (reduce_eq_empty(l, r, result))
return BR_REWRITE_FULL;
expr* s1 = nullptr, *n1 = nullptr, *s2 = nullptr, *n2 = nullptr;
if (str().is_power(l, s1, n1) && str().is_power(r, s2, n2) && s1 == s2) {
expr_ref emp(str().mk_empty(s1->get_sort()), m());
expr_ref_vector fmls(m());
fmls.push_back(m().mk_eq(s1, emp));
expr_ref n1_nonpos(m_autil.mk_le(n1, zero()), m());
expr_ref n2_nonpos(m_autil.mk_le(n2, zero()), m());
fmls.push_back(m().mk_and(n1_nonpos, n2_nonpos));
expr_ref n1_pos(m_autil.mk_lt(zero(), n1), m());
expr_ref n2_pos(m_autil.mk_lt(zero(), n2), m());
expr_ref eq(m().mk_eq(n1, n2), m());
fmls.push_back(m().mk_and(n1_pos, n2_pos, eq));
result = m().mk_or(fmls);
return BR_REWRITE_FULL;
}
{
expr *ra_x = nullptr, *ra_a = nullptr, *ra_b = nullptr;
zstring sa_val, sb_val, s_val;
expr *str_side = nullptr, *ra_side = nullptr;
if (str().is_replace_all(l))
ra_side = l, str_side = r;
else if (str().is_replace_all(r))
ra_side = r, str_side = l;
if (ra_side && str_side &&
str().is_replace_all(ra_side, ra_x, ra_a, ra_b) && str().is_string(ra_a, sa_val) &&
sa_val.length() == 1 &&
str().is_string(ra_b, sb_val) && sb_val.length() == 1 &&
str().is_string(str_side, s_val)) {
result = re().mk_in_re(ra_side, re().mk_to_re(str_side));
return BR_REWRITE_FULL;
}
}
#if 0#endif
if (!reduce_eq(l, r, new_eqs, changed)) {
result = m().mk_false();
TRACE(seq_verbose, tout << result << "\n";);
return BR_DONE;
}
if (!changed) {
return BR_FAILED;
}
for (auto const& [lhs, rhs] : new_eqs) {
res.push_back(m().mk_eq(lhs, rhs));
}
result = mk_and(res);
TRACE(seq_verbose, tout << result << "\n";);
return BR_REWRITE3;
}
void seq_rewriter::remove_empty_and_concats(expr_ref_vector& es) {
unsigned j = 0;
bool has_concat = false;
for (expr* e : es) {
has_concat |= str().is_concat(e);
if (!str().is_empty(e))
es[j++] = e;
}
es.shrink(j);
if (has_concat) {
expr_ref_vector fs(m());
for (expr* e : es)
str().get_concat(e, fs);
es.swap(fs);
}
}
void seq_rewriter::remove_leading(unsigned n, expr_ref_vector& es) {
SASSERT(n <= es.size());
if (n == 0)
return;
for (unsigned i = n; i < es.size(); ++i) {
es[i-n] = es.get(i);
}
es.shrink(es.size() - n);
}
bool seq_rewriter::reduce_back(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs) {
expr* a, *b;
zstring s, s1, s2;
while (true) {
if (ls.empty() || rs.empty()) {
break;
}
expr* l = ls.back();
expr* r = rs.back();
if (str().is_unit(r) && str().is_string(l)) {
std::swap(l, r);
ls.swap(rs);
}
if (l == r) {
ls.pop_back();
rs.pop_back();
}
else if(str().is_unit(l, a) &&
str().is_unit(r, b)) {
if (m().are_distinct(a, b)) {
return false;
}
new_eqs.push_back(a, b);
ls.pop_back();
rs.pop_back();
}
else if (str().is_unit(l, a) && str().is_string(r, s)) {
SASSERT(s.length() > 0);
app_ref ch(str().mk_char(s, s.length()-1), m());
SASSERT(ch->get_sort() == a->get_sort());
new_eqs.push_back(ch, a);
ls.pop_back();
if (s.length() == 1) {
rs.pop_back();
}
else {
expr_ref s2(str().mk_string(s.extract(0, s.length()-1)), m());
rs[rs.size()-1] = s2;
}
}
else if (str().is_string(l, s1) && str().is_string(r, s2)) {
unsigned min_l = std::min(s1.length(), s2.length());
for (unsigned i = 0; i < min_l; ++i) {
if (s1[s1.length()-i-1] != s2[s2.length()-i-1]) {
return false;
}
}
ls.pop_back();
rs.pop_back();
if (min_l < s1.length()) {
ls.push_back(str().mk_string(s1.extract(0, s1.length()-min_l)));
}
if (min_l < s2.length()) {
rs.push_back(str().mk_string(s2.extract(0, s2.length()-min_l)));
}
}
else {
break;
}
}
return true;
}
bool seq_rewriter::reduce_front(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs) {
expr* a, *b;
zstring s, s1, s2;
unsigned head1 = 0, head2 = 0;
while (true) {
if (head1 == ls.size() || head2 == rs.size()) {
break;
}
SASSERT(head1 < ls.size() && head2 < rs.size());
expr* l = ls.get(head1);
expr* r = rs.get(head2);
if (str().is_unit(r) && str().is_string(l)) {
std::swap(l, r);
ls.swap(rs);
std::swap(head1, head2);
}
if (l == r) {
++head1;
++head2;
}
else if(str().is_unit(l, a) &&
str().is_unit(r, b)) {
if (m().are_distinct(a, b)) {
return false;
}
new_eqs.push_back(a, b);
++head1;
++head2;
}
else if (str().is_unit(l, a) && str().is_string(r, s)) {
SASSERT(s.length() > 0);
app* ch = str().mk_char(s, 0);
SASSERT(ch->get_sort() == a->get_sort());
new_eqs.push_back(ch, a);
++head1;
if (s.length() == 1) {
++head2;
}
else {
expr_ref s2(str().mk_string(s.extract(1, s.length()-1)), m());
rs[head2] = s2;
}
}
else if (str().is_string(l, s1) &&
str().is_string(r, s2)) {
TRACE(seq, tout << s1 << " - " << s2 << " " << s1.length() << " " << s2.length() << "\n";);
unsigned min_l = std::min(s1.length(), s2.length());
for (unsigned i = 0; i < min_l; ++i) {
if (s1[i] != s2[i]) {
TRACE(seq, tout << "different at position " << i << " " << s1[i] << " " << s2[i] << "\n";);
return false;
}
}
if (min_l == s1.length()) {
++head1;
}
else {
ls[head1] = str().mk_string(s1.extract(min_l, s1.length()-min_l));
}
if (min_l == s2.length()) {
++head2;
}
else {
rs[head2] = str().mk_string(s2.extract(min_l, s2.length()-min_l));
}
}
else {
break;
}
}
remove_leading(head1, ls);
remove_leading(head2, rs);
return true;
}
bool seq_rewriter::split_bag(expr_ref_vector &ls, expr_ref_vector &rs, expr_ref_pair_vector &new_eqs) {
auto eq = [](auto &a, auto &b) {
if (a.size() != b.size())
return false;
for (auto [k, v] : a)
if (!b.contains(k) || b[k] != v)
return false;
return true;
};
obj_map<expr, unsigned> ls_bag, rs_bag;
for (unsigned i = ls.size(), j = rs.size(); i-- > 0 && j-- > 0 && (i > 0 || j > 0);) {
ls_bag.insert_if_not_there(ls.get(i), 0)++;
rs_bag.insert_if_not_there(rs.get(j), 0)++;
if (eq(ls_bag, rs_bag)) {
auto l = str().mk_concat(ls.size() - i, ls.data() + i, ls.get(i)->get_sort());
auto r = str().mk_concat(rs.size() - j, rs.data() + j, rs.get(j)->get_sort());
new_eqs.push_back(l, r);
ls_bag.reset();
rs_bag.reset();
ls.shrink(i);
rs.shrink(j);
}
}
ls_bag.reset();
rs_bag.reset();
unsigned start = 0;
for (unsigned i = 0; i + 1 < ls.size() && i + 1 < rs.size(); ++i) {
ls_bag.insert_if_not_there(ls.get(i), 0)++;
rs_bag.insert_if_not_there(rs.get(i), 0)++;
if (eq(ls_bag, rs_bag)) {
auto l = str().mk_concat(i + 1 - start, ls.data() + start, ls.get(i)->get_sort());
auto r = str().mk_concat(i + 1 - start, rs.data() + start, rs.get(i)->get_sort());
new_eqs.push_back(l, r);
ls_bag.reset();
rs_bag.reset();
start = i + 1;
}
}
if (start > 0) {
remove_leading(start, ls);
remove_leading(start, rs);
}
return true;
}
bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs, bool& change) {
TRACE(seq_verbose, tout << ls << "\n"; tout << rs << "\n";);
unsigned hash_l = ls.hash();
unsigned hash_r = rs.hash();
unsigned sz_eqs = eqs.size();
remove_empty_and_concats(ls);
remove_empty_and_concats(rs);
return
reduce_back(ls, rs, eqs) &&
reduce_front(ls, rs, eqs) &&
reduce_itos(ls, rs, eqs) &&
reduce_itos(rs, ls, eqs) &&
reduce_value_clash(ls, rs, eqs) &&
reduce_by_length(ls, rs, eqs) &&
reduce_subsequence(ls, rs, eqs) &&
reduce_non_overlap(ls, rs, eqs) &&
reduce_non_overlap(rs, ls, eqs) &&
split_bag(ls, rs, eqs) &&
(change = (hash_l != ls.hash() || hash_r != rs.hash() || eqs.size() != sz_eqs),
true);
}
bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_pair_vector& new_eqs, bool& changed) {
m_lhs.reset();
m_rhs.reset();
str().get_concat(l, m_lhs);
str().get_concat(r, m_rhs);
bool change = false;
if (reduce_eq(m_lhs, m_rhs, new_eqs, change)) {
if (!change) {
new_eqs.push_back(l, r);
}
else {
add_seqs(m_lhs, m_rhs, new_eqs);
}
changed |= change;
return true;
}
else {
TRACE(seq, tout << mk_bounded_pp(l, m()) << " != " << mk_bounded_pp(r, m()) << "\n";);
return false;
}
}
bool seq_rewriter::reduce_arith_eq(expr* l, expr* r, expr_ref_vector& res) {
expr* s = nullptr, *sub = nullptr, *idx = nullptr;
rational i, n;
if (str().is_index(l, s, sub, idx) && m_autil.is_numeral(idx, i) && m_autil.is_numeral(r, n)) {
if (n == 0 && i == 0) {
res.push_back(str().mk_prefix(sub, s));
return true;
}
}
return false;
}
void seq_rewriter::add_seqs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_pair_vector& eqs) {
if (!ls.empty() || !rs.empty()) {
sort * s = (ls.empty() ? rs[0] : ls[0])->get_sort();
expr_ref lhs(str().mk_concat(ls, s), m());
expr_ref rhs(str().mk_concat(rs, s), m());
eqs.push_back(lhs, rhs);
}
}
bool seq_rewriter::reduce_contains(expr* a, expr* b, expr_ref_vector& disj) {
m_lhs.reset();
str().get_concat(a, m_lhs);
TRACE(seq, tout << expr_ref(a, m()) << " " << expr_ref(b, m()) << "\n";);
sort* sort_a = a->get_sort();
zstring s;
for (unsigned i = 0; i < m_lhs.size(); ++i) {
expr* e = m_lhs.get(i);
if (str().is_empty(e)) {
continue;
}
if (str().is_string(e, s)) {
unsigned sz = s.length();
expr_ref_vector es(m());
for (unsigned j = 0; j < sz; ++j) {
es.push_back(str().mk_unit(str().mk_char(s, j)));
}
es.append(m_lhs.size() - i, m_lhs.data() + i);
for (unsigned j = 0; j < sz; ++j) {
disj.push_back(str().mk_prefix(b, str().mk_concat(es.size() - j, es.data() + j, sort_a)));
}
continue;
}
if (str().is_unit(e)) {
disj.push_back(str().mk_prefix(b, str().mk_concat(m_lhs.size() - i, m_lhs.data() + i, sort_a)));
continue;
}
if (str().is_string(b, s)) {
expr* all = re().mk_full_seq(re().mk_re(b->get_sort()));
{
auto _seq4840_0 = str().mk_concat(m_lhs.size() - i, m_lhs.data() + i, sort_a);
auto _seq4840_1 = re().mk_concat(all, re().mk_concat(re().mk_to_re(b), all));
disj.push_back(re().mk_in_re(_seq4840_0, _seq4840_1));
}
return true;
}
if (i == 0) {
return false;
}
disj.push_back(str().mk_contains(str().mk_concat(m_lhs.size() - i, m_lhs.data() + i, sort_a), b));
return true;
}
disj.push_back(str().mk_is_empty(b));
return true;
}
expr* seq_rewriter::concat_non_empty(expr_ref_vector& es) {
sort* s = es[0]->get_sort();
unsigned j = 0;
for (expr* e : es) {
if (str().is_unit(e) || str().is_string(e) || m().is_ite(e))
es[j++] = e;
}
es.shrink(j);
return str().mk_concat(es, s);
}
bool seq_rewriter::set_empty(unsigned sz, expr* const* es, bool all, expr_ref_pair_vector& eqs) {
zstring s;
expr* emp = nullptr;
for (unsigned i = 0; i < sz; ++i) {
auto [bounded, len] = min_length(es[i]);
if (len > 0) {
if (all)
return false;
continue;
}
if (bounded && len == 0)
continue;
emp = emp?emp:str().mk_empty(es[i]->get_sort());
eqs.push_back(emp, es[i]);
}
return true;
}
lbool seq_rewriter::eq_length(expr* x, expr* y) {
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;
}
std::pair<bool, unsigned> seq_rewriter::min_length(unsigned sz, expr* const* ss) {
ptr_buffer<expr> es, sub;
for (unsigned i = 0; i < sz; ++i)
es.push_back(ss[i]);
obj_map<expr, std::pair<bool, unsigned>> cache;
zstring s;
unsigned len = 0;
bool bounded = true;
if (sz == 0)
return { bounded, len };
auto visit = [&](expr* e) {
expr* c, *th, *el;
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;
bool is_valid = true;
unsigned count = 0u;
for (expr* arg : *to_app(e)) {
std::pair<bool, unsigned> r;
if (cache.find(arg, r)) {
auto [r_valid, r_count] = r;
is_valid &= r_valid;
count += r_count;
}
else {
sub.push_back(arg);
visited = false;
}
}
if (visited)
cache.insert(e, { is_valid, count });
return visited;
}
else if (m().is_ite(e, c, th, el)) {
unsigned subsz = sub.size();
std::pair<bool, unsigned> r1, r2;
if (!cache.find(th, r1))
sub.push_back(th);
if (!cache.find(el, r2))
sub.push_back(el);
if (subsz != sub.size())
return false;
auto [r1_valid, r1_count] = r1;
auto [r2_valid, r2_count] = r2;
cache.insert(e, { r1_valid && r2_valid && r1_count == r2_count, std::min(r1_count, r2_count)});
return true;
}
else {
cache.insert(e, { false, 0u });
return true;
}
};
while (!es.empty()) {
expr* c, *th, *el;
expr* e = es.back();
es.pop_back();
if (str().is_unit(e))
len += 1;
else if (str().is_empty(e))
continue;
else if (str().is_string(e, s))
len += s.length();
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, len };
}
std::pair<bool, unsigned> seq_rewriter::min_length(expr* e) {
return min_length(1, &e);
}
std::pair<bool, unsigned> seq_rewriter::min_length(expr_ref_vector const& es) {
return min_length(es.size(), es.data());
}
std::pair<bool, rational> seq_rewriter::max_length(expr* e) {
ptr_buffer<expr> es;
es.push_back(e);
rational len(0);
zstring s;
expr* s1 = nullptr, *i = nullptr, *l = nullptr;
rational n;
while (!es.empty()) {
e = es.back();
es.pop_back();
if (str().is_unit(e))
len += 1;
else if (str().is_at(e))
len += 1;
else if (str().is_string(e, s))
len += rational(s.length());
else if (str().is_extract(e, s1, i, l) && m_autil.is_numeral(l, n) && !n.is_neg())
len += n;
else if (str().is_empty(e))
continue;
else if (str().is_concat(e)) {
for (expr* arg : *to_app(e))
es.push_back(arg);
}
else
return std::make_pair(false, len);
}
return std::make_pair(true, len);
}
bool seq_rewriter::is_string(unsigned n, expr* const* es, zstring& s) const {
zstring s1;
expr* e;
unsigned ch;
for (unsigned i = 0; i < n; ++i) {
if (str().is_string(es[i], s1)) {
s = s + s1;
}
else if (str().is_unit(es[i], e) && m_util.is_const_char(e, ch)) {
s = s + zstring(ch);
}
else {
return false;
}
}
return true;
}
expr_ref seq_rewriter::mk_length(expr* s) {
expr_ref result(m());
if (BR_FAILED == mk_seq_length(s, result))
result = str().mk_length(s);
return result;
}
bool seq_rewriter::reduce_itos(expr_ref_vector& ls, expr_ref_vector& rs,
expr_ref_pair_vector& eqs) {
expr* n = nullptr;
zstring s;
if (ls.size() == 1 &&
str().is_itos(ls.get(0), n) &&
is_string(rs.size(), rs.data(), s)) {
std::string s1 = s.encode();
for (auto c : s1) {
if (!('0' <= c && c <= '9'))
return false;
}
if (s1.size() > 1 && s1[0] == '0')
return false;
try {
rational r(s1.c_str());
if (s1 == r.to_string()) {
eqs.push_back(n, m_autil.mk_numeral(r, true));
ls.reset();
rs.reset();
return true;
}
}
catch (...)
{ }
}
return true;
}
bool seq_rewriter::reduce_eq_empty(expr* l, expr* r, expr_ref& result) {
if (str().is_empty(r))
std::swap(l, r);
if (str().is_length(r))
std::swap(l, r);
#if 0#endif
if (!str().is_empty(l))
return false;
expr* s = nullptr, *offset = nullptr, *len = nullptr;
if (str().is_extract(r, s, offset, len)) {
expr_ref len_s(str().mk_length(s), m());
expr_ref_vector fmls(m());
fmls.push_back(m_autil.mk_lt(offset, zero()));
fmls.push_back(m().mk_eq(s, l));
fmls.push_back(m_autil.mk_le(len, zero()));
fmls.push_back(m_autil.mk_le(len_s, offset));
result = m().mk_or(fmls);
return true;
}
if (str().is_itos(r, s)) {
result = m_autil.mk_lt(s, zero());
return true;
}
expr* base = nullptr, *n = nullptr;
if (str().is_power(r, base, n)) {
expr_ref_vector fmls(m());
fmls.push_back(m_autil.mk_le(n, zero()));
fmls.push_back(m().mk_eq(base, l));
result = m().mk_or(fmls);
return true;
}
if (str().is_at(r, s, offset)) {
expr_ref len_s(str().mk_length(s), m());
{
auto _seq5138_0 = m_autil.mk_le(len_s, offset);
auto _seq5138_1 = m_autil.mk_lt(offset, zero());
result = m().mk_or(_seq5138_0, _seq5138_1);
}
return true;
}
return false;
}
bool seq_rewriter::has_var(expr_ref_vector const& es) {
for (expr* e : es) {
auto [bounded, len] = min_length(e);
if (len == 0)
return true;
}
return false;
}
bool seq_rewriter::reduce_by_length(expr_ref_vector& ls, expr_ref_vector& rs,
expr_ref_pair_vector& eqs) {
if (ls.empty() && rs.empty())
return true;
auto [bounded1, len1] = min_length(ls);
auto [bounded2, len2] = min_length(rs);
if (bounded1 && len1 < len2)
return false;
if (bounded2 && len2 < len1)
return false;
if (bounded1 && len1 == len2 && len1 > 0 && has_var(rs)) {
if (!set_empty(rs.size(), rs.data(), false, eqs))
return false;
expr_ref lhs(concat_non_empty(ls), m());
expr_ref rhs(concat_non_empty(rs), m());
eqs.push_back(lhs, rhs);
ls.reset();
rs.reset();
}
else if (bounded2 && len1 == len2 && len1 > 0 && has_var(ls)) {
if (!set_empty(ls.size(), ls.data(), false, eqs))
return false;
expr_ref lhs(concat_non_empty(ls), m());
expr_ref rhs(concat_non_empty(rs), m());
eqs.push_back(lhs, rhs);
ls.reset();
rs.reset();
}
return true;
}
bool seq_rewriter::reduce_non_overlap(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) {
for (expr* u : rs)
if (!str().is_unit(u))
return true;
expr_ref_vector pattern(m());
for (expr* x : ls) {
if (str().is_unit(x))
pattern.push_back(x);
else if (!pattern.empty()) {
if (non_overlap(pattern, rs))
return false;
pattern.reset();
}
}
if (!pattern.empty() && non_overlap(pattern, rs))
return false;
return true;
}
bool seq_rewriter::reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) {
ptr_buffer<expr> es;
if (ls.empty() || rs.empty())
return true;
es.append(ls.size(), ls.data());
auto remove = [&](expr* r) {
for (unsigned i = 0; i < es.size(); ++i) {
if (r == es[i]) {
es[i] = es.back();
es.pop_back();
return true;
}
}
return false;
};
auto is_unit_value = [&](expr* r) {
return m().is_value(r) && str().is_unit(r);
};
for (expr* r : rs) {
if (remove(r))
continue;
if (!is_unit_value(r))
return true;
}
if (es.empty())
return true;
for (expr* e : es)
if (!is_unit_value(e))
return true;
return false;
}
bool seq_rewriter::reduce_extract(expr* l, expr* r, expr_ref_vector& res) {
expr* sub = nullptr, *p = nullptr, *ln = nullptr;
m_es.reset();
str().get_concat(r, m_es);
rational pos, len;
if (str().is_extract(l, sub, p, ln) &&
m_autil.is_numeral(p, pos) && m_autil.is_numeral(ln, len) &&
0 <= pos &&
0 <= len &&
all_of(m_es, [&](expr* e) { return str().is_unit(e); })) {
if (len == m_es.size()) {
expr_ref_vector result(m());
for (unsigned i = 0; i < pos.get_unsigned(); ++i)
result.push_back(str().mk_unit(str().mk_nth_i(sub, m_autil.mk_int(i))));
result.append(m_es);
res.push_back(str().mk_prefix(str().mk_concat(result, sub->get_sort()), sub));
return true;
}
}
return false;
}
bool seq_rewriter::reduce_subsequence(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) {
if (ls.size() > rs.size())
ls.swap(rs);
if (ls.size() == rs.size())
return true;
if (ls.empty() && rs.size() == 1)
return true;
uint_set rpos;
for (expr* x : ls) {
unsigned j = 0;
bool is_unit = str().is_unit(x);
for (expr* y : rs) {
if (!rpos.contains(j) && (x == y || (is_unit && str().is_unit(y)))) {
rpos.insert(j);
break;
}
++j;
}
if (j == rs.size())
return true;
}
unsigned i = 0, j = 0;
for (expr* y : rs) {
if (rpos.contains(i)) {
rs[j++] = y;
}
else if (!set_empty(1, &y, true, eqs)) {
return false;
}
++i;
}
if (j == rs.size()) {
return true;
}
rs.shrink(j);
SASSERT(ls.size() == rs.size());
if (!ls.empty()) {
sort* srt = ls[0]->get_sort();
expr_ref lhs(str().mk_concat(ls, srt), m());
expr_ref rhs(str().mk_concat(rs, srt), m());
eqs.push_back(lhs, rhs);
ls.reset();
rs.reset();
TRACE(seq, tout << "subsequence " << eqs << "\n";);
}
return true;
}
seq_rewriter::op_cache::op_cache(ast_manager& m):
m_trail(m)
{}
expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b, expr* c) {
op_entry e(op, a, b, c, nullptr);
m_table.find(e, e);
return e.r;
}
void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* c, expr* r) {
cleanup();
if (a) m_trail.push_back(a);
if (b) m_trail.push_back(b);
if (c) m_trail.push_back(c);
if (r) m_trail.push_back(r);
m_table.insert(op_entry(op, a, b, c, r));
}
void seq_rewriter::op_cache::cleanup() {
if (m_table.size() >= m_max_cache_size) {
m_trail.reset();
m_table.reset();
STRACE(seq_regex, tout << "Op cache reset!" << std::endl;);
STRACE(seq_regex_brief, tout << "(OP CACHE RESET) ";);
STRACE(seq_verbose, tout << "Derivative op cache reset" << std::endl;);
}
}
lbool seq_rewriter::some_string_in_re(expr* r, zstring& s) {
seq::regex_witness rw(*this);
return rw.get_witness(r, s);
}