3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-20 21:03:39 +00:00

perf improvements for #1979

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2018-12-04 10:13:55 -08:00
parent ea0d253308
commit 9e5aaf074e
3 changed files with 149 additions and 82 deletions

View file

@ -33,20 +33,23 @@ Notes:
expr_ref sym_expr::accept(expr* e) { expr_ref sym_expr::accept(expr* e) {
ast_manager& m = m_t.get_manager(); ast_manager& m = m_t.get_manager();
expr_ref result(m); expr_ref result(m);
var_subst subst(m);
seq_util u(m);
unsigned r1, r2, r3;
switch (m_ty) { switch (m_ty) {
case t_pred: { case t_pred:
var_subst subst(m);
result = subst(m_t, 1, &e); result = subst(m_t, 1, &e);
break;
case t_not:
result = m_expr->accept(e);
result = m.mk_not(result);
break; break;
}
case t_char: case t_char:
SASSERT(m.get_sort(e) == m.get_sort(m_t)); SASSERT(m.get_sort(e) == m.get_sort(m_t));
SASSERT(m.get_sort(e) == m_sort); SASSERT(m.get_sort(e) == m_sort);
result = m.mk_eq(e, m_t); result = m.mk_eq(e, m_t);
break; break;
case t_range: { case t_range:
seq_util u(m);
unsigned r1, r2, r3;
if (u.is_const_char(m_t, r1) && u.is_const_char(e, r2) && u.is_const_char(m_s, r3)) { if (u.is_const_char(m_t, r1) && u.is_const_char(e, r2) && u.is_const_char(m_s, r3)) {
result = m.mk_bool_val((r1 <= r2) && (r2 <= r3)); result = m.mk_bool_val((r1 <= r2) && (r2 <= r3));
} }
@ -55,7 +58,7 @@ expr_ref sym_expr::accept(expr* e) {
} }
break; break;
} }
}
return result; return result;
} }
@ -64,6 +67,7 @@ std::ostream& sym_expr::display(std::ostream& out) const {
case t_char: return out << m_t; case t_char: return out << m_t;
case t_range: return out << m_t << ":" << m_s; case t_range: return out << m_t << ":" << m_s;
case t_pred: return out << m_t; case t_pred: return out << m_t;
case t_not: return m_expr->display(out << "not ");
} }
return out << "expression type not recognized"; return out << "expression type not recognized";
} }
@ -79,10 +83,11 @@ struct display_expr1 {
class sym_expr_boolean_algebra : public boolean_algebra<sym_expr*> { class sym_expr_boolean_algebra : public boolean_algebra<sym_expr*> {
ast_manager& m; ast_manager& m;
expr_solver& m_solver; expr_solver& m_solver;
expr_ref m_var;
typedef sym_expr* T; typedef sym_expr* T;
public: public:
sym_expr_boolean_algebra(ast_manager& m, expr_solver& s): sym_expr_boolean_algebra(ast_manager& m, expr_solver& s):
m(m), m_solver(s) {} m(m), m_solver(s), m_var(m) {}
T mk_false() override { T mk_false() override {
expr_ref fml(m.mk_false(), m); expr_ref fml(m.mk_false(), m);
@ -93,6 +98,7 @@ public:
return sym_expr::mk_pred(fml, m.mk_bool_sort()); return sym_expr::mk_pred(fml, m.mk_bool_sort());
} }
T mk_and(T x, T y) override { T mk_and(T x, T y) override {
seq_util u(m);
if (x->is_char() && y->is_char()) { if (x->is_char() && y->is_char()) {
if (x->get_char() == y->get_char()) { if (x->get_char() == y->get_char()) {
return x; return x;
@ -102,6 +108,21 @@ public:
return sym_expr::mk_pred(fml, x->get_sort()); return sym_expr::mk_pred(fml, x->get_sort());
} }
} }
unsigned lo1, hi1, lo2, hi2;
if (x->is_range() && y->is_range() &&
u.is_const_char(x->get_lo(), lo1) && u.is_const_char(x->get_hi(), hi1) &&
u.is_const_char(y->get_lo(), lo2) && u.is_const_char(y->get_hi(), hi2)) {
lo1 = std::max(lo1, lo2);
hi1 = std::min(hi1, hi2);
if (lo1 > hi1) {
expr_ref fml(m.mk_false(), m);
return sym_expr::mk_pred(fml, x->get_sort());
}
expr_ref _start(u.mk_char(lo1), m);
expr_ref _stop(u.mk_char(hi1), m);
return sym_expr::mk_range(_start, _stop);
}
sort* s = x->get_sort(); sort* s = x->get_sort();
if (m.is_bool(s)) s = y->get_sort(); if (m.is_bool(s)) s = y->get_sort();
var_ref v(m.mk_var(0, s), m); var_ref v(m.mk_var(0, s), m);
@ -110,13 +131,29 @@ public:
if (m.is_true(fml1)) { if (m.is_true(fml1)) {
return y; return y;
} }
if (m.is_true(fml2)) return x; if (m.is_true(fml2)) {
if (fml1 == fml2) return x; return x;
}
if (fml1 == fml2) {
return x;
}
if (is_complement(fml1, fml2)) {
expr_ref ff(m.mk_false(), m);
return sym_expr::mk_pred(ff, x->get_sort());
}
bool_rewriter br(m); bool_rewriter br(m);
expr_ref fml(m); expr_ref fml(m);
br.mk_and(fml1, fml2, fml); br.mk_and(fml1, fml2, fml);
return sym_expr::mk_pred(fml, x->get_sort()); return sym_expr::mk_pred(fml, x->get_sort());
} }
bool is_complement(expr* f1, expr* f2) {
expr* f = nullptr;
return
(m.is_not(f1, f) && f == f2) ||
(m.is_not(f2, f) && f == f1);
}
T mk_or(T x, T y) override { T mk_or(T x, T y) override {
if (x->is_char() && y->is_char() && if (x->is_char() && y->is_char() &&
x->get_char() == y->get_char()) { x->get_char() == y->get_char()) {
@ -147,6 +184,7 @@ public:
} }
} }
} }
T mk_or(unsigned sz, T const* ts) override { T mk_or(unsigned sz, T const* ts) override {
switch (sz) { switch (sz) {
case 0: return mk_false(); case 0: return mk_false();
@ -160,15 +198,24 @@ public:
} }
} }
} }
lbool is_sat(T x) override { lbool is_sat(T x) override {
unsigned lo, hi;
seq_util u(m);
if (x->is_char()) { if (x->is_char()) {
return l_true; return l_true;
} }
if (x->is_range()) { if (x->is_range() && u.is_const_char(x->get_lo(), lo) && u.is_const_char(x->get_hi(), hi)) {
// TBD check lower is below upper. return (lo <= hi) ? l_true : l_false;
} }
expr_ref v(m.mk_fresh_const("x", x->get_sort()), m); if (x->is_not() && x->get_arg()->is_range() && u.is_const_char(x->get_arg()->get_lo(), lo) && 0 < lo) {
expr_ref fml = x->accept(v); return l_true;
}
if (!m_var || m.get_sort(m_var) != x->get_sort()) {
m_var = m.mk_fresh_const("x", x->get_sort());
}
expr_ref fml = x->accept(m_var);
if (m.is_true(fml)) { if (m.is_true(fml)) {
return l_true; return l_true;
} }
@ -177,16 +224,11 @@ public:
} }
return m_solver.check_sat(fml); return m_solver.check_sat(fml);
} }
T mk_not(T x) override { T mk_not(T x) override {
var_ref v(m.mk_var(0, x->get_sort()), m); return sym_expr::mk_not(m, x);
expr_ref fml(m.mk_not(x->accept(v)), m);
return sym_expr::mk_pred(fml, x->get_sort());
} }
/*virtual vector<std::pair<vector<bool>, T>> generate_min_terms(vector<T> constraints){
return 0;
}*/
}; };
re2automaton::re2automaton(ast_manager& m): m(m), u(m), m_ba(nullptr), m_sa(nullptr) {} re2automaton::re2automaton(ast_manager& m): m(m), u(m), m_ba(nullptr), m_sa(nullptr) {}
@ -1434,6 +1476,14 @@ br_status seq_rewriter::mk_re_inter(expr* a, expr* b, expr_ref& result) {
result = a; result = a;
return BR_DONE; return BR_DONE;
} }
expr* ac = nullptr, *bc = nullptr;
if ((m_util.re.is_complement(a, ac) && ac == b) ||
(m_util.re.is_complement(b, bc) && bc == a)) {
sort* seq_sort = nullptr;
VERIFY(m_util.is_re(a, seq_sort));
result = m_util.re.mk_empty(seq_sort);
return BR_DONE;
}
return BR_FAILED; return BR_FAILED;
} }

View file

@ -31,31 +31,38 @@ class sym_expr {
enum ty { enum ty {
t_char, t_char,
t_pred, t_pred,
t_not,
t_range t_range
}; };
ty m_ty; ty m_ty;
sort* m_sort; sort* m_sort;
expr_ref m_t; sym_expr* m_expr;
expr_ref m_s; expr_ref m_t;
unsigned m_ref; expr_ref m_s;
sym_expr(ty ty, expr_ref& t, expr_ref& s, sort* srt) : m_ty(ty), m_sort(srt), m_t(t), m_s(s), m_ref(0) {} unsigned m_ref;
sym_expr(ty ty, expr_ref& t, expr_ref& s, sort* srt, sym_expr* e) :
m_ty(ty), m_sort(srt), m_expr(e), m_t(t), m_s(s), m_ref(0) {}
public: public:
~sym_expr() { if (m_expr) m_expr->dec_ref(); }
expr_ref accept(expr* e); expr_ref accept(expr* e);
static sym_expr* mk_char(expr_ref& t) { return alloc(sym_expr, t_char, t, t, t.get_manager().get_sort(t)); } static sym_expr* mk_char(expr_ref& t) { return alloc(sym_expr, t_char, t, t, t.get_manager().get_sort(t), nullptr); }
static sym_expr* mk_char(ast_manager& m, expr* t) { expr_ref tr(t, m); return mk_char(tr); } static sym_expr* mk_char(ast_manager& m, expr* t) { expr_ref tr(t, m); return mk_char(tr); }
static sym_expr* mk_pred(expr_ref& t, sort* s) { return alloc(sym_expr, t_pred, t, t, s); } static sym_expr* mk_pred(expr_ref& t, sort* s) { return alloc(sym_expr, t_pred, t, t, s, nullptr); }
static sym_expr* mk_range(expr_ref& lo, expr_ref& hi) { return alloc(sym_expr, t_range, lo, hi, lo.get_manager().get_sort(hi)); } static sym_expr* mk_range(expr_ref& lo, expr_ref& hi) { return alloc(sym_expr, t_range, lo, hi, lo.get_manager().get_sort(hi), nullptr); }
static sym_expr* mk_not(ast_manager& m, sym_expr* e) { expr_ref f(m); e->inc_ref(); return alloc(sym_expr, t_not, f, f, e->get_sort(), e); }
void inc_ref() { ++m_ref; } void inc_ref() { ++m_ref; }
void dec_ref() { --m_ref; if (m_ref == 0) dealloc(this); } void dec_ref() { --m_ref; if (m_ref == 0) dealloc(this); }
std::ostream& display(std::ostream& out) const; std::ostream& display(std::ostream& out) const;
bool is_char() const { return m_ty == t_char; } bool is_char() const { return m_ty == t_char; }
bool is_pred() const { return !is_char(); } bool is_pred() const { return !is_char(); }
bool is_range() const { return m_ty == t_range; } bool is_range() const { return m_ty == t_range; }
bool is_not() const { return m_ty == t_not; }
sort* get_sort() const { return m_sort; } sort* get_sort() const { return m_sort; }
expr* get_char() const { SASSERT(is_char()); return m_t; } expr* get_char() const { SASSERT(is_char()); return m_t; }
expr* get_pred() const { SASSERT(is_pred()); return m_t; } expr* get_pred() const { SASSERT(is_pred()); return m_t; }
expr* get_lo() const { SASSERT(is_range()); return m_t; } expr* get_lo() const { SASSERT(is_range()); return m_t; }
expr* get_hi() const { SASSERT(is_range()); return m_s; } expr* get_hi() const { SASSERT(is_range()); return m_s; }
sym_expr* get_arg() const { SASSERT(is_not()); return m_expr; }
}; };
class sym_expr_manager { class sym_expr_manager {

View file

@ -51,6 +51,7 @@ public:
m_kernel.assert_expr(e); m_kernel.assert_expr(e);
lbool r = m_kernel.check(); lbool r = m_kernel.check();
m_kernel.pop(1); m_kernel.pop(1);
IF_VERBOSE(11, verbose_stream() << "is " << r << " " << mk_pp(e, m_kernel.m()) << "\n");
return r; return r;
} }
}; };
@ -250,6 +251,9 @@ void theory_seq::init(context* ctx) {
m_arith_value.init(ctx); m_arith_value.init(ctx);
} }
#define TRACEFIN(s) { TRACE("seq", tout << ">>" << s << "\n";); IF_VERBOSE(10, verbose_stream() << s << "\n"); }
final_check_status theory_seq::final_check_eh() { final_check_status theory_seq::final_check_eh() {
if (m_reset_cache) { if (m_reset_cache) {
m_rep.reset_cache(); m_rep.reset_cache();
@ -260,79 +264,79 @@ final_check_status theory_seq::final_check_eh() {
TRACE("seq_verbose", get_context().display(tout);); TRACE("seq_verbose", get_context().display(tout););
if (simplify_and_solve_eqs()) { if (simplify_and_solve_eqs()) {
++m_stats.m_solve_eqs; ++m_stats.m_solve_eqs;
TRACE("seq", tout << ">>solve_eqs\n";); TRACEFIN("solve_eqs");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (check_contains()) { if (check_contains()) {
++m_stats.m_propagate_contains; ++m_stats.m_propagate_contains;
TRACE("seq", tout << ">>propagate_contains\n";); TRACEFIN("propagate_contains");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (solve_nqs(0)) { if (solve_nqs(0)) {
++m_stats.m_solve_nqs; ++m_stats.m_solve_nqs;
TRACE("seq", tout << ">>solve_nqs\n";); TRACEFIN("solve_nqs");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (fixed_length(true)) { if (fixed_length(true)) {
++m_stats.m_fixed_length; ++m_stats.m_fixed_length;
TRACE("seq", tout << ">>zero_length\n";); TRACEFIN("zero_length");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (m_params.m_split_w_len && len_based_split()) { if (m_params.m_split_w_len && len_based_split()) {
++m_stats.m_branch_variable; ++m_stats.m_branch_variable;
TRACE("seq", tout << ">>split_based_on_length\n";); TRACEFIN("split_based_on_length");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (fixed_length()) { if (fixed_length()) {
++m_stats.m_fixed_length; ++m_stats.m_fixed_length;
TRACE("seq", tout << ">>fixed_length\n";); TRACEFIN("fixed_length");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (check_int_string()) { if (check_int_string()) {
++m_stats.m_int_string; ++m_stats.m_int_string;
TRACE("seq", tout << ">>int_string\n";); TRACEFIN("int_string");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (reduce_length_eq()) { if (reduce_length_eq()) {
++m_stats.m_branch_variable; ++m_stats.m_branch_variable;
TRACE("seq", tout << ">>reduce length\n";); TRACEFIN("reduce_length");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (branch_unit_variable()) { if (branch_unit_variable()) {
++m_stats.m_branch_variable; ++m_stats.m_branch_variable;
TRACE("seq", tout << ">>branch_unit_variable\n";); TRACEFIN("ranch_unit_variable");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (branch_binary_variable()) { if (branch_binary_variable()) {
++m_stats.m_branch_variable; ++m_stats.m_branch_variable;
TRACE("seq", tout << ">>branch_binary_variable\n";); TRACEFIN("branch_binary_variable");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (branch_ternary_variable1() || branch_ternary_variable2() || branch_quat_variable()) { if (branch_ternary_variable1() || branch_ternary_variable2() || branch_quat_variable()) {
++m_stats.m_branch_variable; ++m_stats.m_branch_variable;
TRACE("seq", tout << ">>split_based_on_alignment\n";); TRACEFIN("split_based_on_alignment");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (branch_variable_mb() || branch_variable()) { if (branch_variable_mb() || branch_variable()) {
++m_stats.m_branch_variable; ++m_stats.m_branch_variable;
TRACE("seq", tout << ">>branch_variable\n";); TRACEFIN("branch_variable");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (check_length_coherence()) { if (check_length_coherence()) {
++m_stats.m_check_length_coherence; ++m_stats.m_check_length_coherence;
TRACE("seq", tout << ">>check_length_coherence\n";); TRACEFIN("check_length_coherence");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (!check_extensionality()) { if (!check_extensionality()) {
++m_stats.m_extensionality; ++m_stats.m_extensionality;
TRACE("seq", tout << ">>extensionality\n";); TRACEFIN("extensionality");
return FC_CONTINUE; return FC_CONTINUE;
} }
if (is_solved()) { if (is_solved()) {
TRACE("seq", tout << ">>is_solved\n";); TRACEFIN("is_solved");
return FC_DONE; return FC_DONE;
} }
TRACE("seq", tout << ">>give_up\n";); TRACEFIN("give_up");
return FC_GIVEUP; return FC_GIVEUP;
} }
@ -4518,8 +4522,6 @@ void theory_seq::add_itos_length_axiom(expr* len) {
void theory_seq::propagate_in_re(expr* n, bool is_true) { void theory_seq::propagate_in_re(expr* n, bool is_true) {
TRACE("seq", tout << mk_pp(n, m) << " <- " << (is_true?"true":"false") << "\n";); TRACE("seq", tout << mk_pp(n, m) << " <- " << (is_true?"true":"false") << "\n";);
expr* s = nullptr, *re = nullptr;
VERIFY(m_util.str.is_in_re(n, s, re));
expr_ref tmp(n, m); expr_ref tmp(n, m);
m_rewrite(tmp); m_rewrite(tmp);
@ -4540,43 +4542,39 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) {
return; return;
} }
expr_ref e3(re, m); expr* s = nullptr, *_re = nullptr;
VERIFY(m_util.str.is_in_re(n, s, _re));
expr_ref re(_re, m);
context& ctx = get_context(); context& ctx = get_context();
literal lit = ctx.get_literal(n); literal lit = ctx.get_literal(n);
if (!is_true) { if (!is_true) {
e3 = m_util.re.mk_complement(re); re = m_util.re.mk_complement(re);
lit.neg(); lit.neg();
} }
literal_vector lits; literal_vector lits;
enode_pair_vector eqs;
for (unsigned i = 0; i < m_s_in_re.size(); ++i) { for (unsigned i = 0; i < m_s_in_re.size(); ++i) {
auto const& entry = m_s_in_re[i]; auto const& entry = m_s_in_re[i];
if (entry.m_active && get_root(entry.m_s) == get_root(s)) { if (entry.m_active && get_root(entry.m_s) == get_root(s) && entry.m_re != re) {
m_trail_stack.push(vector_value_trail<theory_seq, s_in_re, true>(m_s_in_re, i)); m_trail_stack.push(vector_value_trail<theory_seq, s_in_re, true>(m_s_in_re, i));
m_s_in_re[i].m_active = false; m_s_in_re[i].m_active = false;
e3 = m_util.re.mk_inter(entry.m_re, e3); IF_VERBOSE(11, verbose_stream() << "intersect " << re << " " << mk_pp(entry.m_re, m) << " " << mk_pp(s, m) << " " << mk_pp(entry.m_s, m) << "\n";);
re = m_util.re.mk_inter(entry.m_re, re);
m_rewrite(re);
lits.push_back(entry.m_lit); lits.push_back(entry.m_lit);
eqs.push_back(enode_pair(ensure_enode(entry.m_s), ensure_enode(s))); enode* n1 = ensure_enode(entry.m_s);
enode* n2 = ensure_enode(s);
if (n1 != n2) {
lits.push_back(mk_eq(n1->get_owner(), n2->get_owner(), false));
}
} }
} }
if (!lits.empty()) {
TRACE("seq", tout << "creating intersection " << e3 << "\n";);
lits.push_back(lit);
literal inter = mk_literal(m_util.re.mk_in_re(s, e3));
justification* js =
ctx.mk_justification(
ext_theory_propagation_justification(
get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), inter));
ctx.assign(inter, js);
return;
}
eautomaton* a = get_automaton(e3); IF_VERBOSE(11, verbose_stream() << mk_pp(s, m) << " in " << re << "\n");
eautomaton* a = get_automaton(re);
if (!a) return; if (!a) return;
m_s_in_re.push_back(s_in_re(lit, s, e3, a)); m_s_in_re.push_back(s_in_re(lit, s, re, a));
m_trail_stack.push(push_back_vector<theory_seq, vector<s_in_re>>(m_s_in_re)); m_trail_stack.push(push_back_vector<theory_seq, vector<s_in_re>>(m_s_in_re));
expr_ref len = mk_len(s); expr_ref len = mk_len(s);
@ -4587,7 +4585,7 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) {
lits.push_back(~lit); lits.push_back(~lit);
for (unsigned st : states) { for (unsigned st : states) {
lits.push_back(mk_accept(s, zero, e3, st)); lits.push_back(mk_accept(s, zero, re, st));
} }
if (lits.size() == 2) { if (lits.size() == 2) {
propagate_lit(nullptr, 1, &lit, lits[1]); propagate_lit(nullptr, 1, &lit, lits[1]);
@ -4906,24 +4904,36 @@ void theory_seq::add_extract_suffix_axiom(expr* e, expr* s, expr* i) {
void theory_seq::add_at_axiom(expr* e) { void theory_seq::add_at_axiom(expr* e) {
expr* s = nullptr, *i = nullptr; expr* s = nullptr, *i = nullptr;
VERIFY(m_util.str.is_at(e, s, i)); VERIFY(m_util.str.is_at(e, s, i));
expr_ref len_e = mk_len(e);
expr_ref len_s = mk_len(s);
expr_ref zero(m_autil.mk_int(0), m); expr_ref zero(m_autil.mk_int(0), m);
expr_ref one(m_autil.mk_int(1), m); expr_ref one(m_autil.mk_int(1), m);
expr_ref x = mk_skolem(m_pre, s, i);
//expr_ref y = mk_skolem(m_post, s, mk_sub(mk_sub(len_s, i), one));
expr_ref y = mk_skolem(m_tail, s, i);
expr_ref xey = mk_concat(x, e, y);
expr_ref len_x = mk_len(x);
expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m);
expr_ref len_s = mk_len(s);
literal i_ge_0 = mk_simplified_literal(m_autil.mk_ge(i, zero)); literal i_ge_0 = mk_simplified_literal(m_autil.mk_ge(i, zero));
literal i_ge_len_s = mk_simplified_literal(m_autil.mk_ge(mk_sub(i, mk_len(s)), zero)); literal i_ge_len_s = mk_simplified_literal(m_autil.mk_ge(mk_sub(i, mk_len(s)), zero));
rational iv;
add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, xey)); if (m_autil.is_numeral(i, iv) && iv.is_int() && !iv.is_neg()) {
add_axiom(~i_ge_0, i_ge_len_s, mk_eq(one, len_e, false)); expr_ref_vector es(m);
add_axiom(~i_ge_0, i_ge_len_s, mk_eq(i, len_x, false)); expr_ref nth(m);
unsigned k = iv.get_unsigned();
for (unsigned j = 0; j <= k; ++j) {
es.push_back(m_util.str.mk_unit(mk_nth(s, m_autil.mk_int(j))));
}
nth = es.back();
es.push_back(mk_skolem(m_tail, s, m_autil.mk_int(k)));
add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, m_util.str.mk_concat(es)));
add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(nth, e));
}
else {
expr_ref len_e = mk_len(e);
expr_ref x = mk_skolem(m_pre, s, i);
expr_ref y = mk_skolem(m_tail, s, i);
expr_ref xey = mk_concat(x, e, y);
expr_ref len_x = mk_len(x);
add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, xey));
add_axiom(~i_ge_0, i_ge_len_s, mk_eq(one, len_e, false));
add_axiom(~i_ge_0, i_ge_len_s, mk_eq(i, len_x, false));
}
add_axiom(i_ge_0, mk_eq(e, emp, false)); add_axiom(i_ge_0, mk_eq(e, emp, false));
add_axiom(~i_ge_len_s, mk_eq(e, emp, false)); add_axiom(~i_ge_len_s, mk_eq(e, emp, false));