From be627007e1991ec125d4d1097cd12508134d771b Mon Sep 17 00:00:00 2001 From: CEisenhofer Date: Thu, 11 Jun 2026 15:34:25 +0200 Subject: [PATCH] Use lookahead for regex decomposition Make snode const --- src/ast/euf/euf_seq_plugin.cpp | 6 +- src/ast/euf/euf_sgraph.cpp | 121 +-- src/ast/euf/euf_sgraph.h | 38 +- src/ast/euf/euf_snode.h | 46 +- src/ast/rewriter/seq_split.cpp | 2 +- src/smt/seq/seq_nielsen.cpp | 666 +++++++------- src/smt/seq/seq_nielsen.h | 321 ++----- src/smt/seq/seq_nielsen_pp.cpp | 24 +- src/smt/seq/seq_regex.cpp | 160 ++-- src/smt/seq/seq_regex.h | 92 +- src/smt/seq/seq_state.h | 6 +- src/smt/seq_model.cpp | 53 +- src/smt/seq_model.h | 14 +- src/smt/theory_nseq.cpp | 108 +-- src/smt/theory_nseq.h | 4 +- src/test/euf_seq_plugin.cpp | 11 +- src/test/euf_sgraph.cpp | 246 ++--- src/test/nseq_basic.cpp | 78 +- src/test/nseq_zipt.cpp | 38 +- src/test/seq_nielsen.cpp | 1574 ++++++++++++++++---------------- src/test/seq_parikh.cpp | 28 +- src/test/seq_regex.cpp | 298 +++--- 22 files changed, 1868 insertions(+), 2066 deletions(-) diff --git a/src/ast/euf/euf_seq_plugin.cpp b/src/ast/euf/euf_seq_plugin.cpp index 63c238c36..bac403e49 100644 --- a/src/ast/euf/euf_seq_plugin.cpp +++ b/src/ast/euf/euf_seq_plugin.cpp @@ -52,7 +52,7 @@ namespace euf { } unsigned enode_concat_hash::operator()(enode* n) const { - snode* sn = sg.find(n->get_expr()); + snode const* sn = sg.find(n->get_expr()); if (sn && sn->has_cached_hash()) return sn->assoc_hash(); if (!is_any_concat(n, seq)) @@ -70,8 +70,8 @@ namespace euf { if (!is_any_concat(a, seq) || !is_any_concat(b, seq)) return false; // fast-path: snode length check (O(1), avoids leaf allocation) - snode* sa = sg.find(a->get_expr()); - snode* sb = sg.find(b->get_expr()); + snode const* sa = sg.find(a->get_expr()); + snode const* sb = sg.find(b->get_expr()); if (sa && sb && sa->length() != sb->length()) return false; // fast-path: cached associativity hash (O(1)) diff --git a/src/ast/euf/euf_sgraph.cpp b/src/ast/euf/euf_sgraph.cpp index 92588afae..cba3ca7bb 100644 --- a/src/ast/euf/euf_sgraph.cpp +++ b/src/ast/euf/euf_sgraph.cpp @@ -154,8 +154,8 @@ namespace euf { case snode_kind::s_concat: { SASSERT(n->num_args() == 2); - snode* l = n->arg(0); - snode* r = n->arg(1); + const snode* l = n->arg(0); + const snode* r = n->arg(1); n->m_ground = l->is_ground() && r->is_ground(); n->m_regex_free = l->is_regex_free() && r->is_regex_free(); n->m_is_classical = l->is_classical() && r->is_classical(); @@ -171,7 +171,7 @@ namespace euf { // NSB review: SASSERT(n->num_args() == 2); and simplify code // NSB review: is this the correct definition of ground what about the exponent? SASSERT(n->num_args() >= 1); - snode* base = n->arg(0); + snode const* base = n->arg(0); n->m_ground = base->is_ground(); n->m_regex_free = base->is_regex_free(); n->m_is_classical = base->is_classical(); @@ -336,8 +336,8 @@ namespace euf { n->m_hash_matrix[1][1] = 1; } else if (n->is_concat()) { - snode* l = n->arg(0); - snode* r = n->arg(1); + snode const* l = n->arg(0); + snode const* r = n->arg(1); if (l->has_cached_hash() && r->has_cached_hash()) { // 2x2 matrix multiplication: M(L) * M(R) n->m_hash_matrix[0][0] = l->m_hash_matrix[0][0] * r->m_hash_matrix[0][0] + l->m_hash_matrix[0][1] * r->m_hash_matrix[1][0]; @@ -357,9 +357,9 @@ namespace euf { } } - snode *sgraph::mk_snode(expr *e, snode_kind k, unsigned num_args, snode *const *args) { + snode* sgraph::mk_snode(expr *e, const snode_kind k, const unsigned num_args, snode const** args) { SASSERT(e); - unsigned id = m_nodes.size(); + const unsigned id = m_nodes.size(); snode *n = snode::mk(m_region, e, k, id, num_args, args); compute_metadata(n); compute_hash_matrix(n); @@ -379,10 +379,10 @@ namespace euf { return n; } - snode* sgraph::mk(expr* e) { + snode const* sgraph::mk(expr* e) { SASSERT(e); expr_ref _e(e, m); // pin locally to not clash with character creation, never needed if we use mk_enode early. - snode* n = find(e); + snode const* n = find(e); if (n) return n; @@ -390,7 +390,7 @@ namespace euf { // so that Nielsen graph can do prefix matching on them zstring s; if (m_seq.str.is_string(e, s) && !s.empty()) { - snode* result = mk_char(s[s.length() - 1]); + snode const* result = mk_char(s[s.length() - 1]); for (unsigned i = s.length() - 1; i-- > 0; ) result = mk_concat(mk_char(s[i]), result); // register the original string expression as an alias @@ -402,13 +402,13 @@ namespace euf { return result; } - snode_kind k = classify(e); + const snode_kind k = classify(e); if (!is_app(e)) return mk_snode(e, k, 0, nullptr); app* a = to_app(e); - unsigned arity = a->get_num_args(); + const unsigned arity = a->get_num_args(); // recursively register children // for seq/re children, create classified snodes @@ -416,14 +416,14 @@ namespace euf { snode_vector child_nodes; for (unsigned i = 0; i < arity; ++i) { expr* ch = a->get_arg(i); - snode* cn = mk(ch); + snode const* cn = mk(ch); child_nodes.push_back(cn); } return mk_snode(e, k, child_nodes.size(), child_nodes.data()); } - snode* sgraph::find(expr* e) const { + snode const* sgraph::find(expr* e) const { if (!e) return nullptr; unsigned eid = e->get_id(); @@ -456,13 +456,13 @@ namespace euf { if (num_scopes == 0) return; SASSERT(num_scopes <= m_num_scopes); - unsigned new_lvl = m_num_scopes - num_scopes; - unsigned old_sz = m_scopes[new_lvl]; + const unsigned new_lvl = m_num_scopes - num_scopes; + const unsigned old_sz = m_scopes[new_lvl]; for (unsigned i = m_nodes.size(); i-- > old_sz; ) { - snode* n = m_nodes[i]; + snode const* n = m_nodes[i]; if (n->get_expr()) { - unsigned eid = n->get_expr()->get_id(); + const unsigned eid = n->get_expr()->get_id(); if (eid < m_expr2snode.size()) m_expr2snode[eid] = nullptr; } @@ -470,9 +470,9 @@ namespace euf { m_nodes.shrink(old_sz); m_scopes.shrink(new_lvl); // undo alias entries (string constant decompositions) - unsigned alias_old = m_alias_trail_lim[new_lvl]; + const unsigned alias_old = m_alias_trail_lim[new_lvl]; for (unsigned i = m_alias_trail.size(); i-- > alias_old; ) { - unsigned eid = m_alias_trail[i]; + const unsigned eid = m_alias_trail[i]; if (eid < m_expr2snode.size()) m_expr2snode[eid] = nullptr; } @@ -482,23 +482,23 @@ namespace euf { m_egraph.pop(num_scopes); } - snode* sgraph::mk_var(symbol const& name, sort* s) { - expr_ref e(m.mk_const(name, s), m); + snode const* sgraph::mk_var(symbol const& name, sort* s) { + const expr_ref e(m.mk_const(name, s), m); return mk(e); } - snode* sgraph::mk_char(unsigned ch) { - expr_ref c(m_seq.str.mk_char(ch), m); - expr_ref u(m_seq.str.mk_unit(c), m); + snode const* sgraph::mk_char(unsigned ch) { + const expr_ref c(m_seq.str.mk_char(ch), m); + const expr_ref u(m_seq.str.mk_unit(c), m); return mk(u); } - snode* sgraph::mk_empty_seq(sort* s) { - expr_ref e(m_seq.str.mk_empty(s), m); + snode const* sgraph::mk_empty_seq(sort* s) { + const expr_ref e(m_seq.str.mk_empty(s), m); return mk(e); } - snode* sgraph::mk_concat(snode* a, snode* b) { + snode const* sgraph::mk_concat(snode const* a, snode const* b) { if (a->is_empty()) return b; if (b->is_empty()) return a; if (m_seq.is_re(a->get_expr())) @@ -506,29 +506,29 @@ namespace euf { return mk(expr_ref(m_seq.str.mk_concat(a->get_expr(), b->get_expr()), m)); } - snode* sgraph::drop_first(snode* n) { + snode const* sgraph::drop_first(snode const* n) { if (n->is_empty() || n->is_token()) return mk_empty_seq(n->get_sort()); SASSERT(n->is_concat()); - snode* l = n->arg(0); - snode* r = n->arg(1); + snode const* l = n->arg(0); + snode const* r = n->arg(1); if (l->is_token() || l->is_empty()) return r; return mk_concat(drop_first(l), r); } - snode* sgraph::drop_last(snode* n) { + snode const* sgraph::drop_last(snode const* n) { if (n->is_empty() || n->is_token()) return mk_empty_seq(n->get_sort()); SASSERT(n->is_concat()); - snode* l = n->arg(0); - snode* r = n->arg(1); + snode const* l = n->arg(0); + snode const* r = n->arg(1); if (r->is_token() || r->is_empty()) return l; return mk_concat(l, drop_last(r)); } - snode* sgraph::drop_left(snode* n, unsigned count) { + snode const* sgraph::drop_left(snode const* n, unsigned count) { if (count == 0 || n->is_empty()) return n; if (count >= n->length()) return mk_empty_seq(n->get_sort()); SASSERT(n->is_concat()); @@ -538,7 +538,7 @@ namespace euf { return drop_left(n->arg(1), count - left_len); } - snode* sgraph::drop_right(snode* n, unsigned count) { + snode const* sgraph::drop_right(snode const * n, unsigned count) { if (count == 0 || n->is_empty()) return n; if (count >= n->length()) return mk_empty_seq(n->get_sort()); SASSERT(n->is_concat()); @@ -548,7 +548,7 @@ namespace euf { return drop_right(n->arg(0), count - right_len); } - snode* sgraph::subst(snode* n, snode* var, snode* replacement) { + snode const* sgraph::subst(snode const* n, snode const* var, snode const* replacement) { if (n == var) return replacement; if (n->is_empty() || n->is_char()) @@ -653,7 +653,7 @@ namespace euf { return l_false; } - lbool sgraph::re_nullable(snode* re) { + lbool sgraph::re_nullable(snode const* re) { if (!re) return l_undef; // Projection-free regexes: defer to the standard regex info. @@ -693,7 +693,7 @@ namespace euf { } } - snode* sgraph::deriv_proj(snode* re, expr* ch) { + snode const* sgraph::deriv_proj(snode const* re, expr* ch) { SASSERT(re && re->get_expr()); expr* re_expr = re->get_expr(); sort* re_sort = re_expr->get_sort(); @@ -787,7 +787,7 @@ namespace euf { // else ⊥. The gate is on the *current* state (paper §3.3). if (!m_proj_oracle || !m_proj_oracle->projection_state_in_Q(state, nu)) return mk(m_seq.re.mk_empty(re_sort)); - snode* dstate = deriv_proj(re->arg(0), ch); // arg(0) ≡ state + snode const* dstate = deriv_proj(re->arg(0), ch); // arg(0) ≡ state if (!dstate || dstate->is_fail() || m_seq.re.is_empty(dstate->get_expr())) return mk(m_seq.re.mk_empty(re_sort)); // δ(state) may be concrete (one state) or an ite-term (symbolic @@ -797,42 +797,42 @@ namespace euf { case snode_kind::s_ite: { // ite-structured residual (from a symbolic-character derivative): // δ_a(ite(c, th, el)) = ite(c, δ_a(th), δ_a(el)). - snode* dth = deriv_proj(re->arg(1), ch); - snode* del = deriv_proj(re->arg(2), ch); + snode const* dth = deriv_proj(re->arg(1), ch); + snode const* del = deriv_proj(re->arg(2), ch); return mk(expr_ref(m.mk_ite(re->arg(0)->get_expr(), dth->get_expr(), del->get_expr()), m)); } case snode_kind::s_complement: { - snode* d = deriv_proj(re->arg(0), ch); + snode const* d = deriv_proj(re->arg(0), ch); return mk(expr_ref(mk_compl(d->get_expr()), m)); } case snode_kind::s_intersect: { - snode* d0 = deriv_proj(re->arg(0), ch); - snode* d1 = deriv_proj(re->arg(1), ch); + snode const* d0 = deriv_proj(re->arg(0), ch); + snode const* d1 = deriv_proj(re->arg(1), ch); return mk(expr_ref(mk_inter(d0->get_expr(), d1->get_expr()), m)); } case snode_kind::s_union: { - snode* d0 = deriv_proj(re->arg(0), ch); - snode* d1 = deriv_proj(re->arg(1), ch); + snode const* d0 = deriv_proj(re->arg(0), ch); + snode const* d1 = deriv_proj(re->arg(1), ch); return mk(expr_ref(mk_union(d0->get_expr(), d1->get_expr()), m)); } case snode_kind::s_concat: { // δ_a(R·S) = δ_a(R)·S ⊔ (nullable(R) ? δ_a(S) : ∅) - snode* d0 = deriv_proj(re->arg(0), ch); + snode const* d0 = deriv_proj(re->arg(0), ch); expr* head = mk_concat(d0->get_expr(), re->arg(1)->get_expr()); if (re_nullable(re->arg(0)) == l_true) { - snode* d1 = deriv_proj(re->arg(1), ch); + snode const* d1 = deriv_proj(re->arg(1), ch); head = mk_union(head, d1->get_expr()); } return mk(expr_ref(head, m)); } case snode_kind::s_star: { // δ_a(R*) = δ_a(R)·R* - snode* d = deriv_proj(re->arg(0), ch); + snode const* d = deriv_proj(re->arg(0), ch); return mk(expr_ref(mk_concat(d->get_expr(), re_expr), m)); } case snode_kind::s_plus: { // δ_a(R+) = δ_a(R)·R* - snode* d = deriv_proj(re->arg(0), ch); + snode const* d = deriv_proj(re->arg(0), ch); expr_ref star(m_seq.re.mk_star(re->arg(0)->get_expr()), m); return mk(expr_ref(mk_concat(d->get_expr(), star), m)); } @@ -843,7 +843,7 @@ namespace euf { } } - snode* sgraph::brzozowski_deriv(snode* re, snode* elem) { + snode const* sgraph::brzozowski_deriv(snode const* re, snode const* elem) { expr* re_expr = re->get_expr(); expr* elem_expr = elem->get_expr(); SASSERT(re_expr); @@ -900,23 +900,24 @@ namespace euf { // derivative states get distinct snode ids and BFS emptiness checks // fail to deduplicate, exploring an exploded state space. if (re->has_projection()) { - snode* d = deriv_proj(re, elem_expr); + snode const* d = deriv_proj(re, elem_expr); expr_ref e(d->get_expr(), m); th_rewriter trw(m); trw(e); return mk(e); } - expr_ref result = m_rewriter.mk_derivative(elem_expr, re_expr); + std::cout << "Derivative of " << mk_pp(re_expr, m) << "\nwith respect to " << mk_pp(elem_expr, m) << std::endl; + const expr_ref result = m_rewriter.mk_derivative(elem_expr, re_expr); SASSERT(result); return mk(result); } - bool sgraph::are_unit_distinct(snode* a, snode* b) const { + bool sgraph::are_unit_distinct(snode const* a, snode const* b) const { return a->is_char_or_unit() && b->is_char_or_unit() && m.are_distinct(a->get_expr(), b->get_expr()); } - void sgraph::collect_re_predicates(snode* re, expr_ref_vector& preds) { + void sgraph::collect_re_predicates(snode const* re, expr_ref_vector& preds) { if (!re) return; expr* e = re->get_expr(); @@ -983,14 +984,14 @@ namespace euf { } } - void sgraph::compute_minterms(snode* re, snode_vector& minterms) { + void sgraph::compute_minterms(snode const* re, snode_vector& minterms) { expr_ref_vector preds(m); collect_re_predicates(re, preds); - unsigned max_c = m_seq.max_char(); + const unsigned max_c = m_seq.max_char(); if (preds.empty()) { - expr_ref fc(m_seq.re.mk_full_char(m_str_sort), m); + const expr_ref fc(m_seq.re.mk_full_char(m_str_sort), m); minterms.push_back(mk(fc)); return; } @@ -1106,7 +1107,7 @@ namespace euf { } return "?"; }; - for (snode* n : m_nodes) { + for (snode const* n : m_nodes) { out << "snode[" << n->id() << "] " << kind_str(n->kind()) << " level=" << n->level() diff --git a/src/ast/euf/euf_sgraph.h b/src/ast/euf/euf_sgraph.h index fb7b3df29..3d2a43a21 100644 --- a/src/ast/euf/euf_sgraph.h +++ b/src/ast/euf/euf_sgraph.h @@ -115,7 +115,7 @@ namespace euf { expr_ref_vector m_pin; // maps expression id to snode - ptr_vector m_expr2snode; + ptr_vector m_expr2snode; // trail of alias entries (string constant → decomposed snode) for pop unsigned_vector m_alias_trail; // expression ids @@ -124,11 +124,11 @@ namespace euf { // Oracle answering "state ∈ Q_nu" for projection derivatives. Not owned. projection_oracle* m_proj_oracle = nullptr; - snode* mk_snode(expr* e, snode_kind k, unsigned num_args, snode* const* args); + snode* mk_snode(expr* e, snode_kind k, unsigned num_args, snode const** args); snode_kind classify(expr* e) const; void compute_metadata(snode* n); void compute_hash_matrix(snode* n); - void collect_re_predicates(snode* re, expr_ref_vector& preds); + void collect_re_predicates(snode const* re, expr_ref_vector& preds); public: sgraph(ast_manager& m, egraph& eg, bool add_plugin = true); @@ -140,10 +140,10 @@ namespace euf { egraph const& get_egraph() const { return m_egraph; } // register an expression and return its snode - snode* mk(expr* e); + snode const* mk(expr* e); // lookup an already-registered expression - snode* find(expr* e) const; + snode const* find(expr* e) const; // register expression in both sgraph and egraph enode* mk_enode(expr* e); @@ -151,27 +151,27 @@ namespace euf { sort* get_str_sort() const { return m_str_sort; } // return true if a, b are of the same length and distinct - bool are_unit_distinct(snode *a, snode *b) const; + bool are_unit_distinct(snode const* a, snode const* b) const; // factory methods for creating snodes with corresponding expressions - snode* mk_var(symbol const& name, sort* s); - snode* mk_char(unsigned ch); - snode *mk_empty_seq(sort *s); - snode* mk_concat(snode* a, snode* b); + snode const* mk_var(symbol const& name, sort* s); + snode const* mk_char(unsigned ch); + snode const *mk_empty_seq(sort *s); + snode const* mk_concat(snode const* a, snode const* b); // drop operations: remove tokens from the front/back of a concat tree - snode* drop_first(snode* n); - snode* drop_last(snode* n); - snode* drop_left(snode* n, unsigned count); - snode* drop_right(snode* n, unsigned count); + snode const* drop_first(snode const* n); + snode const* drop_last(snode const* n); + snode const* drop_left(snode const* n, unsigned count); + snode const* drop_right(const snode* n, unsigned count); // substitution: replace all occurrences of var in n by replacement - snode* subst(snode* n, snode* var, snode* replacement); + snode const* subst(snode const* n, snode const* var, snode const* replacement); // Brzozowski derivative of regex re with respect to element elem. // allowed_range can explicitly provide a concrete character or range to use // for deriving symbolic variables. - snode* brzozowski_deriv(snode* re, snode* elem); + snode const* brzozowski_deriv(snode const* re, snode const* elem); // Register the oracle consulted when deriving projection operators. // Passing nullptr unregisters. Not owned. @@ -190,19 +190,19 @@ namespace euf { expr_ref wrap_proj(expr* e, expr* root, unsigned nu); // Projection-aware Brzozowski derivative w.r.t. a character expr // (concrete or symbolic). - snode* deriv_proj(snode* re, expr* ch); + snode const* deriv_proj(snode const* re, expr* ch); // Projection-aware nullability: lifts re.get_info().nullable to regexes // that may contain projection operators. Returns l_true / l_false // (l_undef only if an underlying projection-free subterm is undef). - lbool re_nullable(snode* re); + lbool re_nullable(snode const* re); // Decode a character expression that may be represented as a const-char, // a unit string containing a const-char, or a one-character string literal. bool decode_re_char(expr* ex, unsigned& out) const; // compute minterms (character class partition) from a regex - void compute_minterms(snode* re, snode_vector& minterms); + void compute_minterms(snode const* re, snode_vector& minterms); // scope management for backtracking void push(); diff --git a/src/ast/euf/euf_snode.h b/src/ast/euf/euf_snode.h index 346eb87c8..4412a341f 100644 --- a/src/ast/euf/euf_snode.h +++ b/src/ast/euf/euf_snode.h @@ -36,7 +36,7 @@ namespace euf { class sgraph; class snode; - typedef ptr_vector snode_vector; + typedef ptr_vector snode_vector; enum class snode_kind { s_empty, // empty string (OP_SEQ_EMPTY or empty string constant) @@ -85,7 +85,7 @@ namespace euf { // all zeros means not cached, non-zero means cached unsigned m_hash_matrix[2][2] = {{0, 0}, {0, 0}}; - snode *m_args[0]; // variable-length array, allocated via get_snode_size(num_args) + snode const* m_args[0]; // variable-length array, allocated via get_snode_size(num_args) friend class sgraph; @@ -93,9 +93,9 @@ namespace euf { return sizeof(snode) + num_args * sizeof(snode *); } - static snode *mk(region &r, expr *e, snode_kind k, unsigned id, unsigned num_args, snode *const *args) { + static snode *mk(region &r, expr *e, snode_kind k, unsigned id, unsigned num_args, snode const** args) { void *mem = r.allocate(get_snode_size(num_args)); - snode *n = new (mem) snode(); + snode * n = new (mem) snode(); n->m_expr = e; n->m_kind = k; n->m_id = id; @@ -107,7 +107,7 @@ namespace euf { } public: - expr *get_expr() const { + expr* get_expr() const { return m_expr; // assumed to be non-null } snode_kind kind() const { @@ -121,12 +121,12 @@ namespace euf { return m_num_args; } - snode* arg(const unsigned i) const { + snode const* arg(const unsigned i) const { SASSERT(i < m_num_args); return m_args[i]; } - snode* arg0() const { + snode const* arg0() const { return arg(0); } @@ -137,11 +137,11 @@ namespace euf { // O(1) amortized per token, O(tree height) auxiliary memory. class token_iterator { snode_vector m_stack; // pending subtrees, top == back() - snode *m_current = nullptr; // current token, nullptr == end + snode const* m_current = nullptr; // current token, nullptr == end void advance() { while (!m_stack.empty()) { - snode *n = m_stack.back(); + snode const* n = m_stack.back(); m_stack.pop_back(); if (n->is_concat()) { m_stack.push_back(n->arg(1)); @@ -168,7 +168,7 @@ namespace euf { advance(); } - snode *operator*() const { return m_current; } + snode const* operator*() const { return m_current; } token_iterator &operator++() { advance(); return *this; } token_iterator operator++(int) { token_iterator t = *this; advance(); return t; } bool operator==(token_iterator const &o) const { return m_current == o.m_current; } @@ -286,7 +286,7 @@ namespace euf { if (!is_concat()) return false; str.reset(); - for (const snode* c : *this) { + for (snode const* c : *this) { unsigned val; if (!c->is_char()) return false; @@ -309,34 +309,34 @@ namespace euf { } } - sort *get_sort() const { + sort* get_sort() const { return m_expr ? m_expr->get_sort() : nullptr; } // analogous to ZIPT's Str.First / Str.Last snode const *first() const { - snode const *s = this; + snode const* s = this; while (s->is_concat()) s = s->arg(0); return s; } snode const *last() const { - snode const *s = this; + snode const* s = this; while (s->is_concat()) s = s->arg(1); return s; } - snode *first() { - snode *s = this; + snode const* first() { + snode const* s = this; while (s->is_concat()) s = s->arg(0); return s; } - snode *last() { - snode *s = this; + snode const* last() { + snode const* s = this; while (s->is_concat()) s = s->arg(1); return s; @@ -349,7 +349,7 @@ namespace euf { arg(1)->collect_tokens(tokens); } else if (!is_empty()) - tokens.push_back(const_cast(this)); + tokens.push_back(this); } snode_vector collect_tokens() const { @@ -360,7 +360,7 @@ namespace euf { // access the i-th token (0-based, left-to-right order) // returns nullptr if i >= length() - snode *at(unsigned i) const { + snode const* at(const unsigned i) const { if (is_concat()) { unsigned left_len = arg(0)->length(); if (i < left_len) @@ -369,16 +369,16 @@ namespace euf { } if (is_empty()) return nullptr; - return i == 0 ? const_cast(this) : nullptr; + return i == 0 ? this : nullptr; } }; } struct spp { - euf::snode *n; + euf::snode const* n; ast_manager &m; - spp(euf::snode *n, ast_manager &m) : n(n), m(m) {} + spp(euf::snode const* n, ast_manager &m) : n(n), m(m) {} }; inline std::ostream &operator<<(std::ostream &out, spp const&p) { diff --git a/src/ast/rewriter/seq_split.cpp b/src/ast/rewriter/seq_split.cpp index 63cb68f4c..3aaf75968 100644 --- a/src/ast/rewriter/seq_split.cpp +++ b/src/ast/rewriter/seq_split.cpp @@ -39,7 +39,7 @@ void seq_split::push(split_set& out, split_oracle const& oracle, expr* d, expr* // Pairs where any component is bottom (the empty regex) are dropped. bool seq_split::intersect(split_set const& s1, split_set const& s2, split_set& result, unsigned threshold, split_oracle const& oracle) { - seq_util::rex& r = re(); + const seq_util::rex& r = re(); for (auto const& p1 : s1) { for (auto const& p2 : s2) { if (r.is_empty(p1.m_d) || r.is_empty(p2.m_d) || diff --git a/src/smt/seq/seq_nielsen.cpp b/src/smt/seq/seq_nielsen.cpp index f17ec9164..1ba3b91be 100644 --- a/src/smt/seq/seq_nielsen.cpp +++ b/src/smt/seq/seq_nielsen.cpp @@ -70,22 +70,92 @@ namespace seq { return result; } + std::pair split_membership(euf::snode const *str, euf::snode const *regex, euf::sgraph& sg, unsigned threshold, split_set& result) { + seq_util& seq = sg.get_seq_util(); + ast_manager& m = sg.get_manager(); + euf::snode const* first = str->first(); + SASSERT(first); + SASSERT(!first->is_char()); // constants are consumed earlier + + // Choose the factorization boundary so the tail starts with the + // LONGEST run of concrete characters c — this gives the split-engine + // lookahead oracle the most pruning information. head = u' (tokens + // before the run), tail = c · u''' (tokens from the run onward). + euf::snode_vector toks; + str->collect_tokens(toks); + const unsigned total = toks.size(); + unsigned run_start = 0, run_len = 0; + for (unsigned i = 0; i < total; ) { + if (!toks[i]->is_char()) { ++i; continue; } + unsigned j = i; + while (j < total && toks[j]->is_char()) ++j; + if (j - i > run_len) { run_len = j - i; run_start = i; } + i = j; + } + // No constant run → fall back to splitting off the first token. + const unsigned p = run_len == 0 ? 1 : run_start; + SASSERT(p >= 1); + euf::snode const* head = p == 1 ? first : sg.drop_right(str, total - p); + euf::snode const* tail = sg.drop_left(str, p); + SASSERT(head && tail); + + // Build the constant lookahead c and (if non-empty) an oracle that + // prunes splits whose ∇ cannot match c. + zstring c; + for (unsigned i = 0; i < run_len; ++i) { + expr* ch; unsigned cv; + VERIFY(seq.str.is_unit(toks[run_start + i]->get_expr(), ch)); + VERIFY(seq.is_const_char(ch, cv)); + c = c + zstring(cv); + } + split_oracle oracle; + if (!c.empty()) + oracle = [&sg, c](expr*, expr* n) { return split_lookahead_viable(n, sg, c); }; + + // Decompose the regex into a split-set via the shared seq_split engine + // (sigma from the paper): head ∈ Δ ∧ tail ∈ ∇ for each ⟨Δ,∇⟩, with the + // lookahead oracle pruning non-viable ∇ during generation. + seq_rewriter rw(m); + if (!rw.split(regex->get_expr(), result, threshold, split_mode::strong, oracle)) { + result.clear(); + return { nullptr, nullptr }; + } + + rw.simplify_split(result); + + return { head, tail }; + } + + bool split_lookahead_viable(expr* n_regex, euf::sgraph& sg, zstring const& c) { + euf::snode const* cur = sg.mk(n_regex); + SASSERT(cur); + for (unsigned i = 0; i < c.length(); i++) { + if (sg.re_nullable(cur) == l_true) + return true; // N accepts the prefix c[0..i) → a suffix completes it + cur = sg.brzozowski_deriv(cur, sg.mk_char(c[i])); + SASSERT(cur); + if (cur->is_fail()) + return false; // N went (syntactically) dead before reaching c + } + return !cur->is_fail(); + } + // Directional helpers mirroring ZIPT's fwd flag: // fwd=true -> left-to-right (prefix/head) // fwd=false -> right-to-left (suffix/tail) - static euf::snode *dir_token(euf::snode *s, const bool fwd) { + static euf::snode const* dir_token(euf::snode const* s, const bool fwd) { if (!s) return nullptr; return fwd ? s->first() : s->last(); } - static euf::snode *dir_drop(euf::sgraph &sg, euf::snode *s, const unsigned count, const bool fwd) { + static euf::snode const* dir_drop(euf::sgraph &sg, euf::snode const* s, const unsigned count, const bool fwd) { if (!s || count == 0) return s; return fwd ? sg.drop_left(s, count) : sg.drop_right(s, count); } - static euf::snode *dir_concat(euf::sgraph &sg, euf::snode *a, euf::snode *b, const bool fwd) { + static euf::snode const* dir_concat(euf::sgraph &sg, euf::snode const* a, euf::snode const* b, const bool fwd) { if (!a) return b; if (!b) @@ -93,7 +163,7 @@ namespace seq { return fwd ? sg.mk_concat(a, b) : sg.mk_concat(b, a); } - static void collect_tokens_dir(euf::snode *s, const bool fwd, euf::snode_vector &toks) { + static void collect_tokens_dir(euf::snode const* s, const bool fwd, euf::snode_vector &toks) { toks.reset(); if (!s) return; @@ -104,7 +174,7 @@ namespace seq { // Right-derivative helper used by backward str_mem simplification: // dR(re, c) = reverse( derivative(c, reverse(re)) ). - static euf::snode *reverse_brzozowski_deriv(euf::sgraph &sg, euf::snode *re, euf::snode *elem) { + static euf::snode const* reverse_brzozowski_deriv(euf::sgraph &sg, euf::snode const* re, euf::snode const* elem) { if (!re || !elem || !re->get_expr() || !elem->get_expr()) return nullptr; ast_manager &m = sg.get_manager(); @@ -141,14 +211,14 @@ namespace seq { return m_lhs == m_rhs || (m_lhs && m_rhs && m_lhs->is_empty() && m_rhs->is_empty()); } - bool str_eq::contains_var(euf::snode *var) const { + bool str_eq::contains_var(euf::snode const* var) const { if (!var) return false; // check if var appears in the token list of lhs or rhs if (m_lhs) { euf::snode_vector tokens; m_lhs->collect_tokens(tokens); - for (const euf::snode *t : tokens) { + for (euf::snode const* t : tokens) { if (t == var) return true; } @@ -156,7 +226,7 @@ namespace seq { if (m_rhs) { euf::snode_vector tokens; m_rhs->collect_tokens(tokens); - for (const euf::snode *t : tokens) { + for (euf::snode const* t : tokens) { if (t == var) return true; } @@ -185,7 +255,7 @@ namespace seq { return n->graph().sg().re_nullable(m_regex) == l_false; } - bool str_mem::contains_var(euf::snode* var) const { + bool str_mem::contains_var(euf::snode const* var) const { SASSERT(var); if (m_str) { euf::snode_vector tokens; @@ -392,7 +462,7 @@ namespace seq { } } - void nielsen_node::add_char_range(euf::snode* sym_char, char_set const& range, dep_tracker dep) { + void nielsen_node::add_char_range(euf::snode const* sym_char, char_set const& range, dep_tracker dep) { if (sym_char->is_char()) { // for a concrete character just check if it matches const expr * val = sym_char->get_expr(); @@ -506,7 +576,7 @@ namespace seq { return e; } - void nielsen_graph::add_str_eq(euf::snode *lhs, euf::snode *rhs, smt::enode *l, smt::enode *r) const { + void nielsen_graph::add_str_eq(euf::snode const* lhs, euf::snode const* rhs, smt::enode *l, smt::enode *r) const { const dep_tracker dep = m_dep_mgr.mk_leaf(enode_pair(l, r)); str_eq eq(lhs, rhs, dep); eq.sort(); @@ -518,7 +588,7 @@ namespace seq { m_root->add_str_eq(eq); } - void nielsen_graph::add_str_deq(euf::snode *lhs, euf::snode *rhs, sat::literal l) const { + void nielsen_graph::add_str_deq(euf::snode const* lhs, euf::snode const* rhs, sat::literal l) const { const dep_tracker dep = m_dep_mgr.mk_leaf(l); str_deq deq(lhs, rhs, dep); // check if root node contains this equation already @@ -529,7 +599,7 @@ namespace seq { m_root->add_str_deq(deq); } - void nielsen_graph::add_str_mem(euf::snode *str, euf::snode *regex, sat::literal l) const { + void nielsen_graph::add_str_mem(euf::snode const* str, euf::snode const* regex, sat::literal l) const { // check if root node contains this membership constraint already if (std::ranges::any_of(m_root->str_mems(), [&](const str_mem &e) { return e.m_regex == regex && e.m_str == str; })) @@ -540,20 +610,20 @@ namespace seq { } // test-friendly overloads (no external dependency tracking) - void nielsen_graph::add_str_eq(euf::snode *lhs, euf::snode *rhs) { + void nielsen_graph::add_str_eq(euf::snode const* lhs, euf::snode const* rhs) { const dep_tracker dep = m_dep_mgr.mk_leaf(enode_pair(nullptr, nullptr)); str_eq eq(lhs, rhs, dep); eq.sort(); m_root->add_str_eq(eq); } - void nielsen_graph::add_str_deq(euf::snode *lhs, euf::snode *rhs) { + void nielsen_graph::add_str_deq(euf::snode const* lhs, euf::snode const* rhs) { const dep_tracker dep = m_dep_mgr.mk_leaf(enode_pair(nullptr, nullptr)); str_deq deq(lhs, rhs, dep); m_root->add_str_deq(deq); } - void nielsen_graph::add_str_mem(euf::snode *str, euf::snode *regex) { + void nielsen_graph::add_str_mem(euf::snode const* str, euf::snode const* regex) { const dep_tracker dep = nullptr; m_root->add_str_mem(str_mem(str, regex, dep)); } @@ -600,7 +670,7 @@ namespace seq { n->add_constraint(constraint(le, dep, m)); } - euf::snode *nielsen_graph::mk_projection_term(euf::snode *root_re, unsigned nu) { + euf::snode const* nielsen_graph::mk_projection_term(euf::snode const* root_re, unsigned nu) { SASSERT(root_re && root_re->get_expr()); // π_{Q_nu, {root}}(root): current state == accepting state == root. expr_ref proj = m_sg.mk_re_proj(root_re->get_expr(), root_re->get_expr(), nu); @@ -611,12 +681,12 @@ namespace seq { // nielsen_node: simplify_and_init // ----------------------------------------------------------------------- - bool nielsen_node::check_empty_side_conflict(euf::sgraph& sg, euf::snode* non_empty_side, + bool nielsen_node::check_empty_side_conflict(euf::sgraph& sg, euf::snode const* non_empty_side, dep_tracker const& dep) { euf::snode_vector tokens; non_empty_side->collect_tokens(tokens); - const bool has_char = std::ranges::any_of(tokens, [](euf::snode* t){ return t->is_char(); }); - const bool all_eliminable = std::ranges::all_of(tokens, [](euf::snode* t){ + const bool has_char = std::ranges::any_of(tokens, [](euf::snode const* t){ return t->is_char(); }); + const bool all_eliminable = std::ranges::all_of(tokens, [](euf::snode const* t){ return t->is_var() || t->is_power(); }); if (has_char || !all_eliminable) { @@ -658,7 +728,7 @@ namespace seq { } // Get the base expression of a power snode. - static expr* get_power_base_expr(euf::snode* power, seq_util& seq) { + static expr* get_power_base_expr(euf::snode const* power, seq_util& seq) { if (!power || !power->is_power()) return nullptr; const expr * e = power->get_expr(); expr* base = nullptr, *exp = nullptr; @@ -666,7 +736,7 @@ namespace seq { } // Get the exponent expression of a power snode. - static expr* get_power_exp_expr(euf::snode* power, seq_util& seq) { + static expr* get_power_exp_expr(euf::snode const* power, seq_util& seq) { if (!power->is_power()) return nullptr; const expr * e = power->get_expr(); expr* base = nullptr, *exp = nullptr; @@ -678,7 +748,7 @@ namespace seq { // power(c^e) · char(c) → power(c^(e+1)), // power(c^e1) · power(c^e2) → power(c^(e1+e2)). // Returns new snode if merging happened, nullptr otherwise. - static euf::snode* merge_adjacent_powers(euf::sgraph& sg, euf::snode* side) { + static euf::snode const* merge_adjacent_powers(euf::sgraph& sg, euf::snode const* side) { if (!side || side->is_empty() || side->is_token()) return nullptr; @@ -696,7 +766,7 @@ namespace seq { unsigned i = 0; while (i < tokens.size()) { - euf::snode* tok = tokens[i]; + euf::snode const* tok = tokens[i]; // Case 1: current is a power token — absorb following same-base tokens. // Skip at leading position (i == 0) to keep exponents small: CommPower @@ -710,7 +780,7 @@ namespace seq { bool local_merged = false; unsigned j = i + 1; while (j < tokens.size()) { - euf::snode* next = tokens[j]; + euf::snode const* next = tokens[j]; if (next->is_power()) { const expr * nb = get_power_base_expr(next, seq); if (nb == base_e) { @@ -740,7 +810,7 @@ namespace seq { // Skip at leading position (i == 0) to avoid undoing power unwinding: // unwind produces u · u^(n-1); merging it back to u^n creates an infinite cycle. if (i > 0 && tok->is_char() && tok->get_expr() && i + 1 < tokens.size()) { - euf::snode* next = tokens[i + 1]; + euf::snode const* next = tokens[i + 1]; if (next->is_power() && get_power_base_expr(next, seq) == tok->get_expr()) { expr* base_e = tok->get_expr(); // Use same arg order as Case 1: add(exp, 1), not add(1, exp), @@ -749,7 +819,7 @@ namespace seq { expr* exp_acc = arith.mk_add(get_power_exp_expr(next, seq), arith.mk_int(1)); unsigned j = i + 2; while (j < tokens.size()) { - euf::snode* further = tokens[j]; + euf::snode const* further = tokens[j]; if (further->is_power() && get_power_base_expr(further, seq) == base_e) { exp_acc = arith.mk_add(exp_acc, get_power_exp_expr(further, seq)); ++j; continue; @@ -776,7 +846,7 @@ namespace seq { if (!merged) return nullptr; - euf::snode* rebuilt = nullptr; + euf::snode const* rebuilt = nullptr; for (const auto tok : result) rebuilt = rebuilt ? sg.mk_concat(rebuilt, tok) : tok; if (!rebuilt) @@ -786,7 +856,7 @@ namespace seq { // Simplify constant-exponent powers: base^0 → ε, base^1 → base. // Returns new snode if any simplification happened, nullptr otherwise. - static euf::snode* simplify_const_powers(nielsen_node* node, euf::sgraph& sg, euf::snode* side, dep_tracker& dep) { + static euf::snode const* simplify_const_powers(nielsen_node* node, euf::sgraph& sg, euf::snode const* side, dep_tracker& dep) { dep = nullptr; SASSERT(side); if (side->is_empty()) @@ -800,7 +870,7 @@ namespace seq { bool simplified = false; euf::snode_vector result; - for (euf::snode* tok : tokens) { + for (euf::snode const* tok : tokens) { if (tok->is_power()) { expr* exp_e = get_power_exp_expr(tok, seq); rational ub; @@ -814,7 +884,7 @@ namespace seq { } if (ub.is_one()) { // base^1 → base - euf::snode* base_sn = tok->arg0(); + euf::snode const* base_sn = tok->arg0(); if (base_sn) { dep = node->graph().dep_mgr().mk_join(dep, ub_dep); result.push_back(base_sn); @@ -830,8 +900,8 @@ namespace seq { if (!simplified) return nullptr; - euf::snode* rebuilt = nullptr; - for (euf::snode* tok : result) { + euf::snode const* rebuilt = nullptr; + for (euf::snode const* tok : result) { rebuilt = rebuilt ? sg.mk_concat(rebuilt, tok) : tok; } if (!rebuilt) @@ -845,7 +915,7 @@ namespace seq { // Returns (count_expr, num_tokens_consumed). count_expr is nullptr // when no complete base-pattern match is found. static std::pair comm_power( - euf::snode* base_sn, euf::snode* side, ast_manager& m, seq_util& seq, const bool fwd) { + euf::snode const* base_sn, euf::snode const* side, ast_manager& m, seq_util& seq, const bool fwd) { arith_util arith(m); euf::snode_vector base_tokens, side_tokens; collect_tokens_dir(base_sn, fwd, base_tokens); @@ -860,7 +930,7 @@ namespace seq { unsigned i = 0; for (; i < side_tokens.size(); i++) { - euf::snode* t = side_tokens[i]; + euf::snode const* t = side_tokens[i]; if (pos == 0) { last_stable_idx = i; last_stable_sum = sum; @@ -879,7 +949,7 @@ namespace seq { // Skip at leading position (i == 0) to avoid undoing power unwinding: // unwind produces u · u^(n-1); merging it back to u^n creates an infinite cycle. if (i > 0 && t->is_power()) { - euf::snode* pow_base = t->arg0(); + euf::snode const* pow_base = t->arg0(); if (pow_base) { euf::snode_vector pb_tokens; collect_tokens_dir(pow_base, fwd, pb_tokens); @@ -907,7 +977,7 @@ namespace seq { return {expr_ref(last_stable_sum, m), last_stable_idx}; } - euf::snode* nielsen_graph::to_partial_label_regex(euf::snode* label) const { + euf::snode const* nielsen_graph::to_partial_label_regex(euf::snode const* label) const { SASSERT(label && label->get_expr()); expr* e = label->get_expr(); @@ -925,7 +995,7 @@ namespace seq { return nullptr; } - void nielsen_graph::record_partial_derivative_edge(euf::snode* src_re, euf::snode* label, euf::snode* dst_re) { + void nielsen_graph::record_partial_derivative_edge(euf::snode const* src_re, euf::snode const* label, euf::snode const* dst_re) { SASSERT(src_re && dst_re); if (!src_re->is_ground() || !dst_re->is_ground()) return; @@ -942,7 +1012,7 @@ namespace seq { if (src_re->has_projection() || dst_re->has_projection()) return; - //euf::snode* label_re = to_partial_label_regex(label); + //euf::snode const* label_re = to_partial_label_regex(label); //SASSERT(label_re); //if (!m_seq.is_re(label_re->get_expr()) || !label_re->is_ground()) // return; @@ -985,7 +1055,7 @@ namespace seq { m_partial_dfa_in[dst_e->get_id()].push_back(edge_idx); } - bool nielsen_graph::collect_scc_for_projection(euf::snode* root_re, uint_set& scc) const { + bool nielsen_graph::collect_scc_for_projection(euf::snode const* root_re, uint_set& scc) const { scc.reset(); if (!root_re || !root_re->get_expr()) return false; @@ -1109,7 +1179,7 @@ namespace seq { return newly_marked; } - bool nielsen_graph::try_extract_partial_projection(euf::snode* root_re, euf::snode*& projection_re) { + bool nielsen_graph::try_extract_partial_projection(euf::snode const* root_re, euf::snode const*& projection_re) { SASSERT(root_re && root_re->get_expr()); projection_re = nullptr; if (!root_re->is_ground()) @@ -1137,7 +1207,7 @@ namespace seq { return true; } - euf::snode* nielsen_graph::get_slice(euf::snode* v, expr* left, expr* right) { + euf::snode const* nielsen_graph::get_slice(euf::snode const* v, expr* left, expr* right) { SASSERT(v && v->get_expr() && left && right); SASSERT(v->is_var()); @@ -1157,13 +1227,13 @@ namespace seq { } - euf::snode* nielsen_graph::get_tail(euf::snode* v, expr* cnt, const bool fwd) { + euf::snode const* nielsen_graph::get_tail(euf::snode const* v, expr* cnt, const bool fwd) { if (fwd) return get_slice(v, cnt, a.mk_int(0)); return get_slice(v, a.mk_int(0), cnt); } - euf::snode* nielsen_graph::get_tail(euf::snode* v, const unsigned cnt, const bool fwd) { + euf::snode const* nielsen_graph::get_tail(euf::snode const* v, const unsigned cnt, const bool fwd) { return get_tail(v, a.mk_int(cnt), fwd); } @@ -1212,11 +1282,11 @@ namespace seq { changed = true; } - auto cannot_be_empty = [&](euf::snode* side) { + auto cannot_be_empty = [&](euf::snode const* side) { euf::snode_vector tokens; side->collect_tokens(tokens); - const bool has_char = std::ranges::any_of(tokens, [](euf::snode* t){ return t->is_char(); }); - const bool all_eliminable = std::ranges::all_of(tokens, [](euf::snode* t){ + const bool has_char = std::ranges::any_of(tokens, [](euf::snode const* t){ return t->is_char(); }); + const bool all_eliminable = std::ranges::all_of(tokens, [](euf::snode const* t){ return t->is_var() || t->is_power(); }); return has_char || !all_eliminable; @@ -1250,13 +1320,13 @@ namespace seq { // simplify constant-exponent powers dep_tracker lhs_pow_dep = nullptr; - if (euf::snode* s = simplify_const_powers(this, sg, deq.m_lhs, lhs_pow_dep)) { + if (euf::snode const* s = simplify_const_powers(this, sg, deq.m_lhs, lhs_pow_dep)) { deq.m_lhs = s; deq.m_dep = m_graph.dep_mgr().mk_join(deq.m_dep, lhs_pow_dep); changed = true; } dep_tracker rhs_pow_dep = nullptr; - if (euf::snode* s = simplify_const_powers(this, sg, deq.m_rhs, rhs_pow_dep)) { + if (euf::snode const* s = simplify_const_powers(this, sg, deq.m_rhs, rhs_pow_dep)) { deq.m_rhs = s; deq.m_dep = m_graph.dep_mgr().mk_join(deq.m_dep, rhs_pow_dep); changed = true; @@ -1270,8 +1340,8 @@ namespace seq { unsigned prefix = 0; while (prefix < lhs_toks.size() && prefix < rhs_toks.size()) { - euf::snode* lt = lhs_toks[prefix]; - euf::snode* rt = rhs_toks[prefix]; + euf::snode const* lt = lhs_toks[prefix]; + euf::snode const* rt = rhs_toks[prefix]; if (m.are_equal(lt->get_expr(), rt->get_expr())) { ++prefix; } @@ -1289,8 +1359,8 @@ namespace seq { unsigned lsz = lhs_toks.size(), rsz = rhs_toks.size(); unsigned suffix = 0; while (suffix < lsz - prefix && suffix < rsz - prefix) { - euf::snode* lt = lhs_toks[lsz - 1 - suffix]; - euf::snode* rt = rhs_toks[rsz - 1 - suffix]; + euf::snode const* lt = lhs_toks[lsz - 1 - suffix]; + euf::snode const* rt = rhs_toks[rsz - 1 - suffix]; if (m.are_equal(lt->get_expr(), rt->get_expr())) { ++suffix; } else if (sg.are_unit_distinct(lt, rt)) { @@ -1352,8 +1422,8 @@ namespace seq { // --- prefix --- unsigned prefix = 0; while (prefix < lhs_toks.size() && prefix < rhs_toks.size()) { - euf::snode* lt = lhs_toks[prefix]; - euf::snode* rt = rhs_toks[prefix]; + euf::snode const* lt = lhs_toks[prefix]; + euf::snode const* rt = rhs_toks[prefix]; if (m.are_equal(lt->get_expr(), rt->get_expr())) { ++prefix; } @@ -1370,8 +1440,8 @@ namespace seq { unsigned lsz = lhs_toks.size(), rsz = rhs_toks.size(); unsigned suffix = 0; while (suffix < lsz - prefix && suffix < rsz - prefix) { - euf::snode* lt = lhs_toks[lsz - 1 - suffix]; - euf::snode* rt = rhs_toks[rsz - 1 - suffix]; + euf::snode const* lt = lhs_toks[lsz - 1 - suffix]; + euf::snode const* rt = rhs_toks[rsz - 1 - suffix]; if (m.are_equal(lt->get_expr(), rt->get_expr())) { ++suffix; } else if (sg.are_unit_distinct(lt, rt)) { @@ -1400,22 +1470,22 @@ namespace seq { // 3a: simplify constant-exponent powers (base^0 → ε, base^1 → base) dep_tracker lhs_pow_dep = nullptr; - if (euf::snode* s = simplify_const_powers(this, sg, eq.m_lhs, lhs_pow_dep)) { + if (euf::snode const* s = simplify_const_powers(this, sg, eq.m_lhs, lhs_pow_dep)) { eq.m_lhs = s; eq.m_dep = m_graph.dep_mgr().mk_join(eq.m_dep, lhs_pow_dep); changed = true; } dep_tracker rhs_pow_dep = nullptr; - if (euf::snode* s = simplify_const_powers(this, sg, eq.m_rhs, rhs_pow_dep)) { + if (euf::snode const* s = simplify_const_powers(this, sg, eq.m_rhs, rhs_pow_dep)) { eq.m_rhs = s; eq.m_dep = m_graph.dep_mgr().mk_join(eq.m_dep, rhs_pow_dep); changed = true; } // 3b: merge adjacent same-base tokens into combined powers - if (euf::snode* s = merge_adjacent_powers(sg, eq.m_lhs)) + if (euf::snode const* s = merge_adjacent_powers(sg, eq.m_lhs)) { eq.m_lhs = s; changed = true; } - if (euf::snode* s = merge_adjacent_powers(sg, eq.m_rhs)) + if (euf::snode const* s = merge_adjacent_powers(sg, eq.m_rhs)) { eq.m_rhs = s; changed = true; } // 3c: CommPower-based power elimination — when one side starts @@ -1433,16 +1503,16 @@ namespace seq { SASSERT(eq.well_formed()); bool comm_changed = false; for (int side = 0; side < 2 && !comm_changed; ++side) { - euf::snode*& pow_side = side == 0 ? eq.m_lhs : eq.m_rhs; - euf::snode*& other_side = side == 0 ? eq.m_rhs : eq.m_lhs; + euf::snode const*& pow_side = side == 0 ? eq.m_lhs : eq.m_rhs; + euf::snode const*& other_side = side == 0 ? eq.m_rhs : eq.m_lhs; if (!pow_side || !other_side) continue; for (unsigned od = 0; od < 2 && !comm_changed; ++od) { bool fwd = od == 0; - euf::snode* end_tok = dir_token(pow_side, fwd); + euf::snode const* end_tok = dir_token(pow_side, fwd); if (!end_tok || !end_tok->is_power()) continue; - euf::snode* base_sn = end_tok->arg0(); + euf::snode const* base_sn = end_tok->arg0(); expr* pow_exp = get_power_exp_expr(end_tok, seq); if (!base_sn || !pow_exp) continue; @@ -1509,8 +1579,8 @@ namespace seq { SASSERT(eq.well_formed()); for (unsigned od = 0; od < 2 && !changed; ++od) { bool fwd = (od == 0); - euf::snode* lh = dir_token(eq.m_lhs, fwd); - euf::snode* rh = dir_token(eq.m_rhs, fwd); + euf::snode const* lh = dir_token(eq.m_lhs, fwd); + euf::snode const* rh = dir_token(eq.m_rhs, fwd); if (!(lh && rh && lh->is_power() && rh->is_power())) continue; expr* lb = get_power_base_expr(lh, seq); @@ -1563,7 +1633,7 @@ namespace seq { expr_ref zero(m_graph.a.mk_int(0), m); add_constraint(m_graph.mk_constraint(m_graph.a.mk_ge(d, zero), eq.m_dep)); expr_ref pw(seq.str.mk_power(lb, d), m); - euf::snode*& larger_side = lp_le_rp ? eq.m_rhs : eq.m_lhs; + euf::snode const*& larger_side = lp_le_rp ? eq.m_rhs : eq.m_lhs; larger_side = dir_concat(sg, sg.mk(pw), larger_side, fwd); } changed = true; @@ -1582,11 +1652,11 @@ namespace seq { for (unsigned od = 0; od < 2; ++od) { bool fwd = od == 0; while (!mem.m_str->is_empty()) { - euf::snode* tok = dir_token(mem.m_str, fwd); + euf::snode const* tok = dir_token(mem.m_str, fwd); if (!tok || !tok->is_char_or_unit()) break; - euf::snode* src_re = mem.m_regex; - euf::snode* deriv = fwd + euf::snode const* src_re = mem.m_regex; + euf::snode const* deriv = fwd ? sg.brzozowski_deriv(mem.m_regex, tok) : reverse_brzozowski_deriv(sg, mem.m_regex, tok); TRACE(seq, tout << mem_pp(mem, m) << " d: " << spp(deriv, m) << "\n"); @@ -1607,8 +1677,8 @@ namespace seq { // per-minterm var_split behaviour). euf::snode_vector mts; sg.compute_minterms(src_re, mts); - for (euf::snode* mt : mts) { - euf::snode* mt_deriv = sg.brzozowski_deriv(src_re, mt); + for (euf::snode const* mt : mts) { + euf::snode const* mt_deriv = sg.brzozowski_deriv(src_re, mt); if (mt_deriv && !mt_deriv->is_fail() && mt_deriv->is_ground()) m_graph.record_partial_derivative_edge(src_re, mt, mt_deriv); } @@ -1628,13 +1698,13 @@ namespace seq { while (mem.m_str && !mem.m_str->is_empty()) { // TODO: generalize this to work for reverse derivative as well. - euf::snode *tok = mem.m_str->first(); + euf::snode const* tok = mem.m_str->first(); if (!tok || !tok->is_char_or_unit()) break; - euf::snode* src_re = mem.m_regex; + euf::snode const* src_re = mem.m_regex; - euf::snode* next = nullptr; + euf::snode const* next = nullptr; if (src_re->has_projection()) { // The generic symbolic derivative cannot see the projection // operator; route through the projection-aware derivative, @@ -1661,8 +1731,8 @@ namespace seq { if (src_re->is_ground()) { euf::snode_vector mts; sg.compute_minterms(src_re, mts); - for (euf::snode* mt : mts) { - euf::snode* mt_deriv = sg.brzozowski_deriv(src_re, mt); + for (euf::snode const* mt : mts) { + euf::snode const* mt_deriv = sg.brzozowski_deriv(src_re, mt); if (mt_deriv && !mt_deriv->is_fail()) m_graph.record_partial_derivative_edge(src_re, mt, mt_deriv); } @@ -1761,7 +1831,7 @@ namespace seq { return true; } - euf::snode* nielsen_graph::mk_rewrite(expr* e) const { + euf::snode const* nielsen_graph::mk_rewrite(expr* e) const { expr_ref er(e, m); th_rewriter rw(m); rw(er); @@ -2107,7 +2177,7 @@ namespace seq { // 0. empty side propagation if (l->is_empty() || r->is_empty()) { - euf::snode* non_empty_side = l->is_empty() ? r : l; + euf::snode const* non_empty_side = l->is_empty() ? r : l; nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "det", true); euf::snode_vector tokens; @@ -2117,7 +2187,7 @@ namespace seq { eqs[eq_idx] = eqs.back(); eqs.pop_back(); - for (euf::snode* t : tokens) { + for (euf::snode const* t : tokens) { if (t->is_var()) { nielsen_subst s(t, m_sg.mk_empty_seq(t->get_sort()), eq.m_dep); e->add_subst(s); @@ -2144,8 +2214,8 @@ namespace seq { // --- prefix --- unsigned prefix = 0; while (prefix < lhs_toks.size() && prefix < rhs_toks.size()) { - euf::snode* lt = lhs_toks[prefix]; - euf::snode* rt = rhs_toks[prefix]; + euf::snode const* lt = lhs_toks[prefix]; + euf::snode const* rt = rhs_toks[prefix]; if (m.are_equal(lt->get_expr(), rt->get_expr())) ++prefix; else if (m_sg.are_unit_distinct(lt, rt)) @@ -2160,8 +2230,8 @@ namespace seq { } SASSERT(lt->is_unit()); - euf::snode* lhs_rest = m_sg.drop_left(l, prefix + 1); - euf::snode* rhs_rest = m_sg.drop_left(r, prefix + 1); + euf::snode const* lhs_rest = m_sg.drop_left(l, prefix + 1); + euf::snode const* rhs_rest = m_sg.drop_left(r, prefix + 1); auto& eqs = child->str_eqs(); eqs[eq_idx] = eqs.back(); @@ -2185,8 +2255,8 @@ namespace seq { unsigned lsz = lhs_toks.size(), rsz = rhs_toks.size(); unsigned suffix = 0; while (suffix < lsz - prefix && suffix < rsz - prefix) { - euf::snode* lt = lhs_toks[lsz - 1 - suffix]; - euf::snode* rt = rhs_toks[rsz - 1 - suffix]; + euf::snode const* lt = lhs_toks[lsz - 1 - suffix]; + euf::snode const* rt = rhs_toks[rsz - 1 - suffix]; if (m.are_equal(lt->get_expr(), rt->get_expr())) ++suffix; else if (m_sg.are_unit_distinct(lt, rt)) @@ -2195,8 +2265,8 @@ namespace seq { nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "det", true); - euf::snode* lhs_rest = m_sg.drop_right(l, suffix + 1); - euf::snode* rhs_rest = m_sg.drop_right(r, suffix + 1); + euf::snode const* lhs_rest = m_sg.drop_right(l, suffix + 1); + euf::snode const* rhs_rest = m_sg.drop_right(r, suffix + 1); auto& eqs = child->str_eqs(); eqs[eq_idx] = eqs.back(); @@ -2220,16 +2290,16 @@ namespace seq { // 2. power-character directional inconsistency for (unsigned od = 0; od < 2; ++od) { bool fwd = (od == 0); - euf::snode* lh = dir_token(l, fwd); - euf::snode* rh = dir_token(r, fwd); + euf::snode const* lh = dir_token(l, fwd); + euf::snode const* rh = dir_token(r, fwd); for (int side = 0; side < 2; ++side) { - euf::snode* pow_head = (side == 0) ? lh : rh; - euf::snode* other_head = (side == 0) ? rh : lh; + euf::snode const* pow_head = (side == 0) ? lh : rh; + euf::snode const* other_head = (side == 0) ? rh : lh; if (!pow_head || !pow_head->is_power() || !other_head || !other_head->is_char()) continue; - euf::snode* base_sn = pow_head->arg0(); + euf::snode const* base_sn = pow_head->arg0(); if (!base_sn) continue; - euf::snode* base_head = dir_token(base_sn, fwd); + euf::snode const* base_head = dir_token(base_sn, fwd); if (!base_head || !base_head->is_char()) continue; if (m.are_equal(base_head->get_expr(), other_head->get_expr())) continue; // Directional base/head mismatch -> force exponent 0 and power -> ε. @@ -2251,10 +2321,10 @@ namespace seq { for (unsigned od = 0; od < 2; ++od) { bool fwd = od == 0; - euf::snode* var_side = nullptr; - euf::snode* char_side = nullptr; - euf::snode* lhead = dir_token(l, fwd); - euf::snode* rhead = dir_token(r, fwd); + euf::snode const* var_side = nullptr; + euf::snode const* char_side = nullptr; + euf::snode const* lhead = dir_token(l, fwd); + euf::snode const* rhead = dir_token(r, fwd); if (!lhead || !rhead) continue; if (lhead->is_var() && rhead->is_char()) { @@ -2274,7 +2344,7 @@ namespace seq { if (var_toks.size() <= 1 || char_toks.empty()) continue; - euf::snode* var_node = var_toks[0]; + euf::snode const* var_node = var_toks[0]; SASSERT(var_node->is_var()); unsigned i = 0; @@ -2284,8 +2354,8 @@ namespace seq { bool failed = false; while (j1 < var_toks.size() && j2 < char_toks.size()) { - euf::snode* st1 = var_toks[j1]; - euf::snode* st2 = char_toks[j2]; + euf::snode const* st1 = var_toks[j1]; + euf::snode const* st2 = char_toks[j2]; if (!st2->is_char()) break; if (st1->is_char()) { @@ -2319,9 +2389,9 @@ namespace seq { if (i == 0) continue; bool skip_dir = false; - euf::snode* next_var = nullptr; + euf::snode const* next_var = nullptr; for (unsigned k = i; k < char_toks.size(); ++k) { - euf::snode* t = char_toks[k]; + euf::snode const* t = char_toks[k]; if (t->is_power()) { skip_dir = true; break; @@ -2338,11 +2408,11 @@ namespace seq { for (str_eq const& other_eq : node->str_eqs()) { if (other_eq.is_trivial() || !other_eq.m_lhs || !other_eq.m_rhs) continue; - euf::snode* lh2 = dir_token(other_eq.m_lhs, fwd); - euf::snode* rh2 = dir_token(other_eq.m_rhs, fwd); + euf::snode const* lh2 = dir_token(other_eq.m_lhs, fwd); + euf::snode const* rh2 = dir_token(other_eq.m_rhs, fwd); if (!lh2 || !rh2) continue; - auto record_dep = [&](euf::snode* head_var, euf::snode* other_side) { + auto record_dep = [&](euf::snode const* head_var, euf::snode const* other_side) { euf::snode_vector other_toks; collect_tokens_dir(other_side, fwd, other_toks); for (unsigned idx = 0; idx < other_toks.size(); ++idx) { @@ -2383,12 +2453,12 @@ namespace seq { if (cycle_found) continue; } - euf::snode* prefix_sn = char_toks[0]; + euf::snode const* prefix_sn = char_toks[0]; for (unsigned j = 1; j < i; ++j) { prefix_sn = dir_concat(m_sg, prefix_sn, char_toks[j], fwd); } - euf::snode* tail = get_tail(var_node, compute_length_expr(prefix_sn).get(), fwd); - euf::snode* replacement = dir_concat(m_sg, prefix_sn, tail, fwd); + euf::snode const* tail = get_tail(var_node, compute_length_expr(prefix_sn).get(), fwd); + euf::snode const* replacement = dir_concat(m_sg, prefix_sn, tail, fwd); nielsen_subst s(var_node, replacement, eq.m_dep); nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "det", true); @@ -2400,8 +2470,8 @@ namespace seq { // variable definition: x = t where x is a single var and x ∉ vars(t) // → deterministically substitute x → t throughout the node - euf::snode* var = nullptr; - euf::snode* def; + euf::snode const* var = nullptr; + euf::snode const* def; if (l->is_var() && !snode_contains_var(r, l)) { var = l; @@ -2439,14 +2509,14 @@ namespace seq { SASSERT(eq.well_formed()); for (unsigned od = 0; od < 2; ++od) { const bool fwd = (od == 0); - euf::snode* lhead = dir_token(eq.m_lhs, fwd); - euf::snode* rhead = dir_token(eq.m_rhs, fwd); + euf::snode const* lhead = dir_token(eq.m_lhs, fwd); + euf::snode const* rhead = dir_token(eq.m_rhs, fwd); if (!lhead || !rhead) continue; // char vs var: branch 1: var -> ε, branch 2: var -> char·var (depending on direction) - euf::snode* char_head = lhead->is_char_or_unit() ? lhead : (rhead->is_char_or_unit() ? rhead : nullptr); - euf::snode* var_head = lhead->is_var() ? lhead : (rhead->is_var() ? rhead : nullptr); + euf::snode const* char_head = lhead->is_char_or_unit() ? lhead : (rhead->is_char_or_unit() ? rhead : nullptr); + euf::snode const* var_head = lhead->is_var() ? lhead : (rhead->is_var() ? rhead : nullptr); if (char_head && var_head) { nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "nielsen const 0", true); @@ -2455,8 +2525,8 @@ namespace seq { child->apply_subst(m_sg, s1); - euf::snode* tail = get_tail(var_head, a.mk_int(1), fwd); - euf::snode* replacement = dir_concat(m_sg, char_head, tail, fwd); + euf::snode const* tail = get_tail(var_head, a.mk_int(1), fwd); + euf::snode const* replacement = dir_concat(m_sg, char_head, tail, fwd); child = mk_child(node); e = mk_edge(node, child, "nielsen const >", false); e->add_side_constraint(mk_constraint(a.mk_ge(compute_length_expr(tail), a.mk_int(0)), eq.m_dep)); @@ -2477,8 +2547,8 @@ namespace seq { SASSERT(eq.well_formed()); for (unsigned od = 0; od < 2; ++od) { const bool fwd = od == 0; - euf::snode* lhead = dir_token(eq.m_lhs, fwd); - euf::snode* rhead = dir_token(eq.m_rhs, fwd); + euf::snode const* lhead = dir_token(eq.m_lhs, fwd); + euf::snode const* rhead = dir_token(eq.m_rhs, fwd); SASSERT(lhead && rhead); SASSERT(lhead->id() != rhead->id()); if (!lhead->is_var() || !rhead->is_var()) @@ -2503,7 +2573,7 @@ namespace seq { } // child 3: x → y·x (no progress) { - euf::snode* replacement = dir_concat(m_sg, rhead, get_tail(lhead, compute_length_expr(rhead).get(), fwd), fwd); + euf::snode const* replacement = dir_concat(m_sg, rhead, get_tail(lhead, compute_length_expr(rhead).get(), fwd), fwd); nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "nielsen var >", false); const nielsen_subst s(lhead, replacement, eq.m_dep); @@ -2512,7 +2582,7 @@ namespace seq { } // child 4: y → x·y (no progress) { - euf::snode* replacement = dir_concat(m_sg, lhead, get_tail(rhead, compute_length_expr(lhead).get(), fwd), fwd); + euf::snode const* replacement = dir_concat(m_sg, lhead, get_tail(rhead, compute_length_expr(lhead).get(), fwd), fwd); nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "nielsen var <", false); const nielsen_subst s(rhead, replacement, eq.m_dep); @@ -2529,7 +2599,7 @@ namespace seq { // EqSplit helpers: token length classification // ----------------------------------------------------------------------- - bool nielsen_graph::token_has_variable_length(euf::snode* tok) const { + bool nielsen_graph::token_has_variable_length(euf::snode const* tok) const { // Chars and units have known constant length 1. if (tok->is_char_or_unit()) return false; @@ -2546,7 +2616,7 @@ namespace seq { return true; } - unsigned nielsen_graph::token_const_length(euf::snode* tok) const { + unsigned nielsen_graph::token_const_length(euf::snode const* tok) const { if (tok->is_char_or_unit()) return 1; if (tok->get_expr()) { @@ -2638,7 +2708,7 @@ namespace seq { if (consume_lhs) { if (li >= lhs_len) break; - euf::snode* tok = lhs_toks[li++]; + euf::snode const* tok = lhs_toks[li++]; const bool is_var = token_has_variable_length(tok); if (is_var) { // Confirm any pending split point. @@ -2658,7 +2728,7 @@ namespace seq { else { if (ri >= rhs_len) break; - euf::snode* tok = rhs_toks[ri++]; + euf::snode const* tok = rhs_toks[ri++]; const bool is_var = token_has_variable_length(tok); if (is_var) { if (has_pending && (!has_best || std::abs(pending_padding) < std::abs(best_padding))) { @@ -2725,20 +2795,20 @@ namespace seq { continue; // Split the equation sides into prefix / suffix. - euf::snode* lhs_prefix = m_sg.drop_right(eq.m_lhs, lhs_toks.size() - split_lhs); - euf::snode* lhs_suffix = m_sg.drop_left(eq.m_lhs, split_lhs); - euf::snode* rhs_prefix = m_sg.drop_right(eq.m_rhs, rhs_toks.size() - split_rhs); - euf::snode* rhs_suffix = m_sg.drop_left(eq.m_rhs, split_rhs); + euf::snode const* lhs_prefix = m_sg.drop_right(eq.m_lhs, lhs_toks.size() - split_lhs); + euf::snode const* lhs_suffix = m_sg.drop_left(eq.m_lhs, split_lhs); + euf::snode const* rhs_prefix = m_sg.drop_right(eq.m_rhs, rhs_toks.size() - split_rhs); + euf::snode const* rhs_suffix = m_sg.drop_left(eq.m_rhs, split_rhs); // Build the two new equations, incorporating a padding variable if needed. - euf::snode* eq1_lhs = lhs_prefix; - euf::snode* eq1_rhs = rhs_prefix; - euf::snode* eq2_lhs = lhs_suffix; - euf::snode* eq2_rhs = rhs_suffix; + euf::snode const* eq1_lhs = lhs_prefix; + euf::snode const* eq1_rhs = rhs_prefix; + euf::snode const* eq2_lhs = lhs_suffix; + euf::snode const* eq2_rhs = rhs_suffix; th_rewriter rw(m); - euf::snode* pad = nullptr; + euf::snode const* pad = nullptr; if (padding != 0) { // NSB review: can we represent pad_var using a string function? expr *pad_var = m_sk.mk("eq-split", a.mk_int(padding), eq.m_lhs->get_expr(), @@ -2795,7 +2865,7 @@ namespace seq { // nielsen_graph: mk_fresh_var // ----------------------------------------------------------------------- - euf::snode* nielsen_graph::mk_fresh_var(sort* s) { + euf::snode const* nielsen_graph::mk_fresh_var(sort* s) { ++m_stats.m_num_fresh_vars; const std::string name = "v!" + std::to_string(m_fresh_cnt++); return m_sg.mk_var(symbol(name.c_str()), s); @@ -2891,20 +2961,20 @@ namespace seq { // Helper: find a power token in any str_eq // ----------------------------------------------------------------------- - euf::snode* nielsen_graph::find_power_token(nielsen_node* node) { + euf::snode const* nielsen_graph::find_power_token(nielsen_node* node) { for (str_eq const& eq : node->str_eqs()) { if (eq.is_trivial()) continue; SASSERT(eq.well_formed()); euf::snode_vector toks; eq.m_lhs->collect_tokens(toks); - for (euf::snode* t : toks) { + for (euf::snode const* t : toks) { if (t->is_power()) return t; } toks.reset(); eq.m_rhs->collect_tokens(toks); - for (euf::snode* t : toks) { + for (euf::snode const* t : toks) { if (t->is_power()) return t; } @@ -2918,8 +2988,8 @@ namespace seq { // ----------------------------------------------------------------------- bool nielsen_graph::find_power_vs_non_var(nielsen_node* node, - euf::snode*& power, - euf::snode*& other_head, + euf::snode const*& power, + euf::snode const*& other_head, str_eq const*& eq_out, bool& fwd) { for (str_eq const& eq : node->str_eqs()) { @@ -2927,8 +2997,8 @@ namespace seq { continue; for (unsigned od = 0; od < 2; ++od) { const bool local_fwd = (od == 0); - euf::snode* lhead = dir_token(eq.m_lhs, local_fwd); - euf::snode* rhead = dir_token(eq.m_rhs, local_fwd); + euf::snode const* lhead = dir_token(eq.m_lhs, local_fwd); + euf::snode const* rhead = dir_token(eq.m_rhs, local_fwd); // Match power vs any non-variable, non-empty token (char, unit, // power with different base, etc.). // Same-base power vs power is handled by NumCmp (priority 3). @@ -2958,8 +3028,8 @@ namespace seq { // ----------------------------------------------------------------------- bool nielsen_graph::find_power_vs_var(nielsen_node* node, - euf::snode*& power, - euf::snode*& var_head, + euf::snode const*& power, + euf::snode const*& var_head, str_eq const*& eq_out, bool& fwd) { for (str_eq const& eq : node->str_eqs()) { @@ -2967,8 +3037,8 @@ namespace seq { for (unsigned od = 0; od < 2; ++od) { const bool local_fwd = (od == 0); - euf::snode* lhead = dir_token(eq.m_lhs, local_fwd); - euf::snode* rhead = dir_token(eq.m_rhs, local_fwd); + euf::snode const* lhead = dir_token(eq.m_lhs, local_fwd); + euf::snode const* rhead = dir_token(eq.m_rhs, local_fwd); if (lhead && lhead->is_power() && rhead && rhead->is_var()) { power = lhead; var_head = rhead; @@ -2989,7 +3059,7 @@ namespace seq { } bool nielsen_graph::find_power_vs_var(nielsen_node* node, - euf::snode*& power, + euf::snode const*& power, str_mem const*& mem_out, bool& fwd) { for (str_mem const& mem : node->str_mems()) { @@ -3000,7 +3070,7 @@ namespace seq { for (unsigned od = 0; od < 2; ++od) { const bool local_fwd = (od == 0); - euf::snode* lhead = dir_token(mem.m_str, local_fwd); + euf::snode const* lhead = dir_token(mem.m_str, local_fwd); if (lhead && lhead->is_power()) { power = lhead; mem_out = &mem; @@ -3028,7 +3098,7 @@ namespace seq { // ExtendDir. When both sides are non-empty and a power faces a // constant, ConstNumUnwinding (priority 4) handles it with both // n=0 and n≥1 branches. - euf::snode* power = nullptr; + euf::snode const* power = nullptr; dep_tracker dep = m_dep_mgr.mk_empty(); for (str_eq const& eq : node->str_eqs()) { if (eq.is_trivial()) @@ -3049,7 +3119,7 @@ namespace seq { return false; SASSERT(power->is_power() && power->num_args() >= 1); - euf::snode *base = power->arg0(); + euf::snode const* base = power->arg0(); nielsen_node* child; nielsen_edge* e; @@ -3091,8 +3161,8 @@ namespace seq { continue; for (unsigned od = 0; od < 2; ++od) { const bool fwd = (od == 0); - euf::snode* lhead = dir_token(eq.m_lhs, fwd); - euf::snode* rhead = dir_token(eq.m_rhs, fwd); + euf::snode const* lhead = dir_token(eq.m_lhs, fwd); + euf::snode const* rhead = dir_token(eq.m_rhs, fwd); if (!lhead || !rhead) continue; if (!lhead->is_power() || !rhead->is_power()) @@ -3149,18 +3219,18 @@ namespace seq { continue; for (int side = 0; side < 2; ++side) { - euf::snode* pow_side = (side == 0) ? eq.m_lhs : eq.m_rhs; - euf::snode* other_side = (side == 0) ? eq.m_rhs : eq.m_lhs; + euf::snode const* pow_side = (side == 0) ? eq.m_lhs : eq.m_rhs; + euf::snode const* other_side = (side == 0) ? eq.m_rhs : eq.m_lhs; // NB: Shuvendu - this test is always false if (!pow_side || !other_side) continue; for (unsigned od = 0; od < 2; ++od) { const bool fwd = od == 0; - euf::snode* end_tok = dir_token(pow_side, fwd); + euf::snode const* end_tok = dir_token(pow_side, fwd); if (!end_tok || !end_tok->is_power()) continue; - euf::snode* base_sn = end_tok->arg0(); + euf::snode const* base_sn = end_tok->arg0(); expr* pow_exp = get_power_exp_expr(end_tok, m_seq); // NB: Shuvendu - this test is also redundant if (!base_sn || !pow_exp) @@ -3206,15 +3276,15 @@ namespace seq { bool nielsen_graph::apply_const_num_unwinding(nielsen_node* node) { - euf::snode *power = nullptr; - euf::snode *other_head = nullptr; + euf::snode const* power = nullptr; + euf::snode const* other_head = nullptr; str_eq const *eq = nullptr; bool fwd = true; if (!find_power_vs_non_var(node, power, other_head, eq, fwd)) return false; SASSERT(power->is_power() && power->num_args() >= 1); - euf::snode *base = power->arg0(); + euf::snode const* base = power->arg0(); expr *exp_n = get_power_exponent(power); expr *zero = a.mk_int(0); expr *one = a.mk_int(1); @@ -3242,9 +3312,9 @@ namespace seq { expr *base_expr = to_app(power_e)->get_arg(0); const expr_ref n_minus_1 = normalize_arith(m, a.mk_sub(exp_n, one)); const expr_ref nested_pow(seq.str.mk_power(base_expr, n_minus_1), m); - euf::snode* nested_power_snode = m_sg.mk(nested_pow); + euf::snode const* nested_power_snode = m_sg.mk(nested_pow); - euf::snode* replacement = dir_concat(m_sg, base, nested_power_snode, fwd); + euf::snode const* replacement = dir_concat(m_sg, base, nested_power_snode, fwd); child = mk_child(node); e = mk_edge(node, child, "unwinding >", true); const nielsen_subst s2(power, replacement, eq->m_dep); @@ -3265,12 +3335,12 @@ namespace seq { // ite-derivative would otherwise delay SCC detection by one more level. // ----------------------------------------------------------------------- - void nielsen_graph::precompute_partial_dfa(euf::snode* root_re, const unsigned depth) { + void nielsen_graph::precompute_partial_dfa(euf::snode const* root_re, const unsigned depth) { SASSERT(root_re); if (!root_re->is_ground()) return; - struct work_item { euf::snode* re; unsigned d; }; + struct work_item { euf::snode const* re; unsigned d; }; svector queue; queue.push_back({root_re, depth}); uint_set visited; @@ -3281,9 +3351,9 @@ namespace seq { queue.pop_back(); euf::snode_vector mts; m_sg.compute_minterms(re, mts); - for (euf::snode* mt : mts) { + for (euf::snode const* mt : mts) { // std::cout << "minterm: " << mk_pp(mt->get_expr(), m) << std::endl; - euf::snode* deriv = m_sg.brzozowski_deriv(re, mt); + euf::snode const* deriv = m_sg.brzozowski_deriv(re, mt); if (!deriv || deriv->is_fail()) continue; record_partial_derivative_edge(re, mt, deriv); @@ -3306,7 +3376,7 @@ namespace seq { // as symbolic chars are consumed (mirroring the old concrete-char approach). // ----------------------------------------------------------------------- - void nielsen_graph::record_dfa_edges_from_ite(euf::snode* src_re, expr* ite_deriv) { + void nielsen_graph::record_dfa_edges_from_ite(euf::snode const* src_re, expr* ite_deriv) { if (!src_re || !ite_deriv) return; expr *c, *th, *el; @@ -3315,9 +3385,9 @@ namespace seq { expr *char_ex, *minterm_re; if (m_seq.str.is_in_re(c, char_ex, minterm_re)) { if (!m_seq.re.is_empty(th)) { - euf::snode* dst = m_sg.mk(th); + euf::snode const* dst = m_sg.mk(th); if (dst && !dst->is_fail() && dst->is_ground()) { - euf::snode* label = m_sg.mk(minterm_re); + euf::snode const* label = m_sg.mk(minterm_re); record_partial_derivative_edge(src_re, label, dst); } } @@ -3332,7 +3402,7 @@ namespace seq { // no SCC exists yet or no edges have been marked. // ----------------------------------------------------------------------- - euf::snode* nielsen_graph::get_current_stabilizer(euf::snode* root_re) { + euf::snode const* nielsen_graph::get_current_stabilizer(euf::snode const* root_re) { if (!root_re || !root_re->is_ground() || m_projection_extract_idx == 0) return nullptr; uint_set scc; @@ -3357,19 +3427,19 @@ namespace seq { SASSERT(mem.well_formed()); if (mem.is_primitive()) continue; - euf::snode* first = mem.m_str->first(); + euf::snode const* first = mem.m_str->first(); SASSERT(first); if (!first->is_var()) continue; // Get the current stabilizer for this regex (no novelty guard). - euf::snode* stabilizer = get_current_stabilizer(mem.m_regex); + euf::snode const* stabilizer = get_current_stabilizer(mem.m_regex); if (!stabilizer || m_seq.re.is_epsilon(stabilizer->get_expr())) continue; // Collect primitive regex constraints on `first`. dep_tracker x_dep = nullptr; - euf::snode* x_regex = m_seq_regex->collect_primitive_regex_intersection( + euf::snode const* x_regex = m_seq_regex->collect_primitive_regex_intersection( first, *node, m_dep_mgr, x_dep); if (!x_regex) continue; @@ -3379,7 +3449,7 @@ namespace seq { continue; // Subsume: replace x·rest ∈ r with rest ∈ r. - euf::snode* tail = m_sg.drop_first(mem.m_str); + euf::snode const* tail = m_sg.drop_first(mem.m_str); SASSERT(tail); nielsen_node* child = mk_child(node); @@ -3428,13 +3498,13 @@ namespace seq { SASSERT(mem.well_formed()); if (mem.is_primitive()) continue; - euf::snode* first = mem.m_str->first(); + euf::snode const* first = mem.m_str->first(); SASSERT(first); if (!first->is_var()) continue; - euf::snode* x = first; - euf::snode* stabilizer_re = nullptr; + euf::snode const* x = first; + euf::snode const* stabilizer_re = nullptr; // Eagerly precompute partial DFA edges from this regex so that // collect_scc_for_projection can detect cycles without waiting @@ -3456,9 +3526,9 @@ namespace seq { VERIFY(m_seq.is_re(stabilizer_re->get_expr(), seq_sort)); // Construct the replacement x = x' x'' - euf::snode* xp = m_sg.mk(m_sk.mk("cycle", x->get_expr(), stabilizer_re->get_expr(), seq_sort)); - euf::snode* xpp = get_tail(x, compute_length_expr(xp).get()); - euf::snode* xp_xpp = m_sg.mk_concat(xp, xpp); + euf::snode const* xp = m_sg.mk(m_sk.mk("cycle", x->get_expr(), stabilizer_re->get_expr(), seq_sort)); + euf::snode const* xpp = get_tail(x, compute_length_expr(xp).get()); + euf::snode const* xp_xpp = m_sg.mk_concat(xp, xpp); nielsen_node* child = mk_child(node); SASSERT(child->m_str_mem[mi] == mem); @@ -3488,7 +3558,7 @@ namespace seq { const expr_ref sigma_star(m_seq.re.mk_full_seq(re_sort), m); const expr_ref s_ne_sigma_star(m_seq.re.mk_concat(s_ne, sigma_star), m); const expr_ref xpp_re(m_seq.re.mk_complement(s_ne_sigma_star), m); - euf::snode* xpp_snode = m_sg.mk(xpp_re); + euf::snode const* xpp_snode = m_sg.mk(xpp_re); child->add_str_mem(str_mem(xpp, xpp_snode, mem.m_dep)); TRACE(seq, tout << "cycle_decomp: x=" << mk_pp(x->get_expr(), m) @@ -3522,8 +3592,8 @@ namespace seq { // Try both directions (ZIPT's ExtendDir(fwd=true/false)). for (unsigned od = 0; od < 2; ++od) { const bool fwd = (od == 0); - euf::snode* lhead = dir_token(eq.m_lhs, fwd); - euf::snode* rhead = dir_token(eq.m_rhs, fwd); + euf::snode const* lhead = dir_token(eq.m_lhs, fwd); + euf::snode const* rhead = dir_token(eq.m_rhs, fwd); if (!lhead || !rhead) continue; @@ -3533,7 +3603,7 @@ namespace seq { euf::snode_vector toks; collect_tokens_dir(eq.m_lhs, fwd, toks); euf::snode_vector ground_prefix; - const euf::snode* target_var = nullptr; + euf::snode const* target_var = nullptr; for (unsigned i = 0; i < toks.size(); ++i) { if (toks[i]->is_var()) { target_var = toks[i]; @@ -3552,7 +3622,7 @@ namespace seq { euf::snode_vector toks; collect_tokens_dir(eq.m_rhs, fwd, toks); euf::snode_vector ground_prefix; - const euf::snode* target_var = nullptr; + euf::snode const* target_var = nullptr; for (unsigned i = 0; i < toks.size(); ++i) { if (toks[i]->is_var()) { target_var = toks[i]; @@ -3576,37 +3646,13 @@ namespace seq { // Modifier: apply_regex_factorization (Boolean Closure) // ----------------------------------------------------------------------- - // Lookahead oracle for the split engine: is the split's right component - // `n_regex` prefix-compatible with the constant character sequence `c`? - // The factorization picks a boundary so the tail starts with c, hence the - // tail-regex ∇ must be able to match c as a prefix. We use a *prefix* test - // (not strict "starts-with"): we accept as soon as N accepts a prefix of c - // (a suffix appended downstream can complete it). This is sound to apply - // during split generation — it never drops a viable split. - bool nielsen_graph::split_lookahead_viable(expr* n_regex, zstring const& c) { - euf::snode* cur = m_sg.mk(n_regex); - if (!cur) - return true; // conservative: keep - for (unsigned i = 0; i < c.length(); ++i) { - if (m_sg.re_nullable(cur) == l_true) - return true; // N accepts the prefix c[0..i) → a suffix completes it - cur = m_sg.brzozowski_deriv(cur, m_sg.mk_char(c[i])); - if (!cur || cur->is_fail()) - return false; // N went (syntactically) dead before reaching c - } - // Consumed all of c without matching a prefix: keep iff δ_c(N) is non-empty - // (one BFS emptiness check rather than per-char; an empty-language state is - // never nullable, so the loop above can't wrongly early-accept it). - return !m_seq_regex->is_empty_regex(cur); - } - bool nielsen_graph::apply_regex_factorization(nielsen_node* node) { if (m_regex_factorization_threshold == 0) return false; struct rf_split { - euf::snode* m_p; - euf::snode* m_q; + euf::snode const* m_p; + euf::snode const* m_q; dep_tracker m_dep; }; @@ -3625,67 +3671,27 @@ namespace seq { if (mem.m_regex->has_projection()) continue; - euf::snode* first = mem.m_str->first(); - SASSERT(first); - SASSERT(!first->is_char()); // constants are consumed earlier - - // Choose the factorization boundary so the tail starts with the - // LONGEST run of concrete characters c — this gives the split-engine - // lookahead oracle the most pruning information. head = u' (tokens - // before the run), tail = c · u''' (tokens from the run onward). - euf::snode_vector toks; - mem.m_str->collect_tokens(toks); - const unsigned total = toks.size(); - unsigned run_start = 0, run_len = 0; - for (unsigned i = 0; i < total; ) { - if (!toks[i]->is_char()) { ++i; continue; } - unsigned j = i; - while (j < total && toks[j]->is_char()) ++j; - if (j - i > run_len) { run_len = j - i; run_start = i; } - i = j; - } - // No constant run → fall back to splitting off the first token. - const unsigned p = (run_len == 0) ? 1 : run_start; - SASSERT(p >= 1); - euf::snode* head = (p == 1) ? first : m_sg.drop_right(mem.m_str, total - p); - euf::snode* tail = m_sg.drop_left(mem.m_str, p); - SASSERT(head && tail); - - // Build the constant lookahead c and (if non-empty) an oracle that - // prunes splits whose ∇ cannot match c. - zstring c; - for (unsigned i = 0; i < run_len; ++i) { - expr* ch; unsigned cv; - VERIFY(m_seq.str.is_unit(toks[run_start + i]->get_expr(), ch)); - VERIFY(m_seq.is_const_char(ch, cv)); - c = c + zstring(cv); - } - split_oracle oracle; - if (!c.empty()) - oracle = [this, c](expr*, expr* n) { return split_lookahead_viable(n, c); }; - - // Decompose the regex into a split-set via the shared seq_split engine - // (sigma from the paper): head ∈ Δ ∧ tail ∈ ∇ for each ⟨Δ,∇⟩, with the - // lookahead oracle pruning non-viable ∇ during generation. split_set pairs; - seq_rewriter rw(m); - if (!rw.split(mem.m_regex->get_expr(), pairs, m_regex_factorization_threshold, - split_mode::strong, oracle)) + auto [head, tail] = split_membership(mem.m_str, mem.m_regex, sg(), m_regex_factorization_threshold, pairs); + if (!head) { + SASSERT(!tail); continue; + } + SASSERT(tail); - rw.simplify_split(pairs); + euf::snode const* const first = mem.m_str->first(); vector feasible; dep_tracker eliminated_dep = mem.m_dep; for (auto const &[tp, tq] : pairs) { - euf::snode* sn_p = m_sg.mk(tp); - euf::snode* sn_q = m_sg.mk(tq); + euf::snode const* sn_p = m_sg.mk(tp); + euf::snode const* sn_q = m_sg.mk(tq); // Also check intersection with other primitive constraints on `head`. // Only valid when head is the single token `first`; for a multi-token // head Δ constrains the whole prefix, so we only check Δ ≠ ∅. - ptr_vector regexes_p; + euf::snode_vector regexes_p; regexes_p.push_back(sn_p); dep_tracker first_filter_dep = nullptr; if (head == first) { @@ -3739,7 +3745,7 @@ namespace seq { bool nielsen_graph::fire_gpower_intro( nielsen_node* node, str_eq const& eq, - euf::snode* var, euf::snode_vector const& ground_prefix_orig, const bool fwd) { + euf::snode const* var, euf::snode_vector const& ground_prefix_orig, const bool fwd) { // Compress repeated patterns in the ground prefix (mirrors ZIPT's LcpCompressionFull). // E.g., [a,b,a,b] has minimal period 2 → use [a,b] as the power base. @@ -3771,7 +3777,7 @@ namespace seq { if (ground_prefix.size() == 1 && ground_prefix[0]->is_power()) { expr* base_e = get_power_base_expr(ground_prefix[0], m_seq); if (base_e) { - euf::snode* base_sn = m_sg.mk(base_e); + euf::snode const* base_sn = m_sg.mk(base_e); if (base_sn) { euf::snode_vector base_toks; collect_tokens_dir(base_sn, fwd, base_toks); @@ -3802,7 +3808,7 @@ namespace seq { // Create fresh exponent variable and power expression: base^n const expr_ref fresh_n = get_or_create_gpower_n_var(var); const expr_ref power_expr(m_seq.str.mk_power(base_str, fresh_n), m); - euf::snode* power_snode = m_sg.mk(power_expr); + euf::snode const* power_snode = m_sg.mk(power_expr); if (!power_snode) return false; @@ -3813,7 +3819,7 @@ namespace seq { // For char tokens P(c) = {ε}, for power tokens P(u^m) = {u^m', 0 ≤ m' ≤ m}. // Child at position i substitutes var → base^n · t0·...·t_{i-1} · P(t_i). for (unsigned i = 0; i < base_len; ++i) { - euf::snode* tok = ground_prefix[i]; + euf::snode const* tok = ground_prefix[i]; // Skip char position when preceding token is a power: // The power case at i-1 with 0 ≤ m' ≤ exp already covers m' = exp, @@ -3823,11 +3829,11 @@ namespace seq { continue; // Build full-token prefix: ground_prefix[0..i-1] - euf::snode* prefix_sn = nullptr; + euf::snode const* prefix_sn = nullptr; for (unsigned j = 0; j < i; ++j) prefix_sn = (j == 0) ? ground_prefix[0] : dir_concat(m_sg, prefix_sn, ground_prefix[j], fwd); - euf::snode* replacement; + euf::snode const* replacement; expr_ref fresh_m(m); if (tok->is_power()) { @@ -3837,13 +3843,13 @@ namespace seq { if (inner_exp && inner_base) { fresh_m = get_or_create_gpower_m_var(var); expr_ref partial_pow(m_seq.str.mk_power(inner_base, fresh_m), m); - euf::snode* partial_sn = m_sg.mk(partial_pow); - euf::snode* suffix_sn = prefix_sn ? dir_concat(m_sg, prefix_sn, partial_sn, fwd) : partial_sn; + euf::snode const* partial_sn = m_sg.mk(partial_pow); + euf::snode const* suffix_sn = prefix_sn ? dir_concat(m_sg, prefix_sn, partial_sn, fwd) : partial_sn; replacement = dir_concat(m_sg, power_snode, suffix_sn, fwd); } else { // Fallback: use full token (shouldn't normally happen) - euf::snode* suffix_sn = prefix_sn ? dir_concat(m_sg, prefix_sn, tok, fwd) : tok; + euf::snode const* suffix_sn = prefix_sn ? dir_concat(m_sg, prefix_sn, tok, fwd) : tok; replacement = dir_concat(m_sg, power_snode, suffix_sn, fwd); } } @@ -3993,16 +3999,16 @@ namespace seq { continue; // Decompose u = u1·u2 and v = v1·v2 at signature indices. - euf::snode* u1 = m_sg.drop_right(eq.m_lhs, lhs_toks.size() - i); - euf::snode* u2 = m_sg.drop_left(eq.m_lhs, i); - euf::snode* v1 = m_sg.drop_right(eq.m_rhs, rhs_toks.size() - j); - euf::snode* v2 = m_sg.drop_left(eq.m_rhs, j); + euf::snode const* u1 = m_sg.drop_right(eq.m_lhs, lhs_toks.size() - i); + euf::snode const* u2 = m_sg.drop_left(eq.m_lhs, i); + euf::snode const* v1 = m_sg.drop_right(eq.m_rhs, rhs_toks.size() - j); + euf::snode const* v2 = m_sg.drop_left(eq.m_rhs, j); // NSB review: if we keep this skolem function it should include arguments // to not clash with other values of i, j // Why not use // x := str.substr(u2, 0, str.len(u2) - str.len(v1)), const auto x_e = m_sk.mk("signature-split", eq.m_lhs->get_expr(), eq.m_rhs->get_expr(), eq.m_lhs->get_sort()); - euf::snode *x = m_sg.mk(x_e); + euf::snode const* x = m_sg.mk(x_e); for (unsigned branch = 0; branch < 2; ++branch) { nielsen_node* child = mk_child(node); @@ -4056,7 +4062,7 @@ namespace seq { expr_ref c2(m), th2(m), el2(m); if (!bool_rewriter(m).decompose_ite(r, c2, th2, el2)) { // No ite remaining: leaf → create child node with regex updated to r - euf::snode *new_regex_snode = m_sg.mk(r); + euf::snode const* new_regex_snode = m_sg.mk(r); nielsen_node *child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "regex if", true); for (const auto f : cs) { @@ -4118,7 +4124,7 @@ namespace seq { SASSERT(mem.well_formed()); if (mem.is_primitive()) continue; - euf::snode* first = mem.m_str->first(); + euf::snode const* first = mem.m_str->first(); SASSERT(first); // std::cout << "Considering regex: " << spp(mem.m_regex, m) << std::endl; @@ -4140,10 +4146,10 @@ namespace seq { // then the suffix where the current x is located is at str.len(x) - len(x) // (seq.nth x (- (str.len x) len(x)) // - euf::snode* fresh_char = m_sg.mk(get_or_create_char_var(first)); + euf::snode const* fresh_char = m_sg.mk(get_or_create_char_var(first)); - euf::snode* tail = get_tail(first, 1, true); - euf::snode* replacement = m_sg.mk_concat(fresh_char, tail); + euf::snode const* tail = get_tail(first, 1, true); + euf::snode const* replacement = m_sg.mk_concat(fresh_char, tail); nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "regex var split", false); @@ -4167,15 +4173,15 @@ namespace seq { bool nielsen_graph::apply_power_split(nielsen_node* node) { - euf::snode* power = nullptr; - euf::snode* var_head = nullptr; + euf::snode const* power = nullptr; + euf::snode const* var_head = nullptr; str_eq const* eq = nullptr; bool fwd = true; if (!find_power_vs_var(node, power, var_head, eq, fwd)) return false; SASSERT(power->is_power() && power->num_args() >= 1); - euf::snode* base = power->arg0(); + euf::snode const* base = power->arg0(); const expr_ref zero(a.mk_int(0), m); // Branch 1: enumerate all decompositions of the base. @@ -4191,12 +4197,12 @@ namespace seq { const expr_ref fresh_m = get_or_create_gpower_n_var(var_head); const expr_ref power_m_expr(m_seq.str.mk_power(base_expr, fresh_m), m); - euf::snode* power_m_sn = m_sg.mk(power_m_expr); + euf::snode const* power_m_sn = m_sg.mk(power_m_expr); if (!power_m_sn) return false; for (unsigned i = 0; i < base_len; ++i) { - euf::snode* tok = base_toks[i]; + euf::snode const* tok = base_toks[i]; // Skip char position when preceding token is a power: // the power case at i-1 with 0 <= m' <= exp already covers m' = exp. @@ -4204,11 +4210,11 @@ namespace seq { continue; // Build full-token prefix: base_toks[0..i-1] - euf::snode* prefix_sn = nullptr; + euf::snode const* prefix_sn = nullptr; for (unsigned j = 0; j < i; ++j) prefix_sn = (j == 0) ? base_toks[0] : dir_concat(m_sg, prefix_sn, base_toks[j], fwd); - euf::snode* replacement; + euf::snode const* replacement; expr_ref fresh_inner_m(m); if (tok->is_power()) { @@ -4218,12 +4224,12 @@ namespace seq { if (inner_exp && inner_base) { fresh_inner_m = get_or_create_gpower_m_var(var_head); expr_ref partial_pow(m_seq.str.mk_power(inner_base, fresh_inner_m), m); - euf::snode* partial_sn = m_sg.mk(partial_pow); - euf::snode* suffix_sn = prefix_sn ? dir_concat(m_sg, prefix_sn, partial_sn, fwd) : partial_sn; + euf::snode const* partial_sn = m_sg.mk(partial_pow); + euf::snode const* suffix_sn = prefix_sn ? dir_concat(m_sg, prefix_sn, partial_sn, fwd) : partial_sn; replacement = dir_concat(m_sg, power_m_sn, suffix_sn, fwd); } else { - euf::snode* suffix_sn = prefix_sn ? dir_concat(m_sg, prefix_sn, tok, fwd) : tok; + euf::snode const* suffix_sn = prefix_sn ? dir_concat(m_sg, prefix_sn, tok, fwd) : tok; replacement = dir_concat(m_sg, power_m_sn, suffix_sn, fwd); } } @@ -4254,7 +4260,7 @@ namespace seq { // Branch 2: x = u^n · x' (variable extends past full power, non-progress) // so replace x -> u^n · x { - euf::snode* replacement = dir_concat(m_sg, power, var_head, fwd); + euf::snode const* replacement = dir_concat(m_sg, power, var_head, fwd); nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "power split", false); const nielsen_subst s(var_head, replacement, eq->m_dep); @@ -4276,15 +4282,15 @@ namespace seq { bool nielsen_graph::apply_var_num_unwinding_eq(nielsen_node* node) { - euf::snode* power = nullptr; - euf::snode* var_head = nullptr; + euf::snode const* power = nullptr; + euf::snode const* var_head = nullptr; str_eq const* eq = nullptr; bool fwd = true; if (!find_power_vs_var(node, power, var_head, eq, fwd)) return false; SASSERT(power->is_power() && power->num_args() >= 1); - euf::snode* base = power->arg0(); + euf::snode const* base = power->arg0(); expr* exp_n = get_power_exponent(power); SASSERT(exp_n); const expr_ref zero(a.mk_int(0), m); @@ -4306,8 +4312,8 @@ namespace seq { // Side constraint: n >= 1 { const expr_ref power_expr(m_seq.str.mk_power(base->get_expr(), a.mk_sub(exp_n, a.mk_int(1))), m); - euf::snode* power_snode = m_sg.mk(power_expr); - euf::snode* replacement = dir_concat(m_sg, base, power_snode, fwd); + euf::snode const* power_snode = m_sg.mk(power_expr); + euf::snode const* replacement = dir_concat(m_sg, base, power_snode, fwd); nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "unwinding eq >", false); const nielsen_subst s(power, replacement, eq->m_dep); // TODO review - ensure var does not occur in replacement. @@ -4321,14 +4327,14 @@ namespace seq { bool nielsen_graph::apply_var_num_unwinding_mem(nielsen_node* node) { - euf::snode* power = nullptr; + euf::snode const* power = nullptr; str_mem const* mem = nullptr; bool fwd = true; if (!find_power_vs_var(node, power, mem, fwd)) return false; SASSERT(power->is_power() && power->num_args() >= 1); - euf::snode* base = power->arg0(); + euf::snode const* base = power->arg0(); expr* exp_n = get_power_exponent(power); SASSERT(exp_n); const expr_ref zero(a.mk_int(0), m); @@ -4349,9 +4355,9 @@ namespace seq { // Side constraint: n >= 1 { const expr_ref power_expr(m_seq.str.mk_power(base->get_expr(), a.mk_sub(exp_n, a.mk_int(1))), m); - euf::snode* power_snode = m_sg.mk(power_expr); + euf::snode const* power_snode = m_sg.mk(power_expr); - euf::snode* replacement = dir_concat(m_sg, base, power_snode, fwd); + euf::snode const* replacement = dir_concat(m_sg, base, power_snode, fwd); nielsen_node* child = mk_child(node); nielsen_edge* e = mk_edge(node, child, "unwinding mem >", false); const nielsen_subst s(power, replacement, mem->m_dep); // TODO review - ensure var does not occur in replacement. @@ -4372,8 +4378,8 @@ namespace seq { return false; const str_deq& first = node->m_str_deq.back(); - euf::snode* u = first.m_lhs; - euf::snode* v = first.m_rhs; + euf::snode const* u = first.m_lhs; + euf::snode const* v = first.m_rhs; const expr_ref u_len(compute_length_expr(u), m); const expr_ref v_len(compute_length_expr(v), m); @@ -4381,15 +4387,15 @@ namespace seq { str_eq eq_uv(u, v, first.m_dep); sort *char_sort = nullptr; VERIFY(seq().is_seq(u->get_sort(), char_sort)); - euf::snode* a = m_sg.mk(seq().str.mk_unit(m_sk.mk("diseq.a", u->get_expr(), v->get_expr(), char_sort).get())); - euf::snode* b = m_sg.mk(seq().str.mk_unit(m_sk.mk("diseq.b", u->get_expr(), v->get_expr(), char_sort).get())); - euf::snode* w = m_sg.mk(m_sk.mk("diseq.w", u->get_expr(), v->get_expr()).get()); - euf::snode* up = m_sg.mk(m_sk.mk("diseq.u'", u->get_expr(), v->get_expr()).get()); - euf::snode* vp = m_sg.mk(m_sk.mk("diseq.v'", u->get_expr(), v->get_expr()).get()); + euf::snode const* a = m_sg.mk(seq().str.mk_unit(m_sk.mk("diseq.a", u->get_expr(), v->get_expr(), char_sort).get())); + euf::snode const* b = m_sg.mk(seq().str.mk_unit(m_sk.mk("diseq.b", u->get_expr(), v->get_expr(), char_sort).get())); + euf::snode const* w = m_sg.mk(m_sk.mk("diseq.w", u->get_expr(), v->get_expr()).get()); + euf::snode const* up = m_sg.mk(m_sk.mk("diseq.u'", u->get_expr(), v->get_expr()).get()); + euf::snode const* vp = m_sg.mk(m_sk.mk("diseq.v'", u->get_expr(), v->get_expr()).get()); const expr_ref up_len(compute_length_expr(up), m); const expr_ref vp_len(compute_length_expr(vp), m); - euf::snode* wau = dir_concat(m_sg, dir_concat(m_sg, w, a, true), up, true); - euf::snode* wbv = dir_concat(m_sg, dir_concat(m_sg, w, b, true), vp, true); + euf::snode const* wau = dir_concat(m_sg, dir_concat(m_sg, w, a, true), up, true); + euf::snode const* wbv = dir_concat(m_sg, dir_concat(m_sg, w, b, true), vp, true); const str_eq u_eq(u, wau, first.m_dep); const str_eq v_eq(v, wbv, first.m_dep); @@ -4480,7 +4486,7 @@ namespace seq { // nielsen_graph: length constraint generation // ----------------------------------------------------------------------- - expr_ref nielsen_graph::compute_length_expr(euf::snode* n) { + expr_ref nielsen_graph::compute_length_expr(euf::snode const* n) { if (n->is_empty()) return expr_ref(a.mk_int(0), m); @@ -4493,7 +4499,7 @@ namespace seq { return expr_ref(a.mk_add(left, right), m); } - //euf::snode *length_term = nullptr; + //euf::snode const* length_term = nullptr; //if (m_length_info.find(n->id(), length_term) && length_term) // return expr_ref(length_term->get_expr(), m); @@ -4524,7 +4530,7 @@ namespace seq { euf::snode_vector tokens; eq.m_lhs->collect_tokens(tokens); eq.m_rhs->collect_tokens(tokens); - for (const euf::snode* tok : tokens) { + for (euf::snode const* tok : tokens) { if (tok->is_var() && !seen_vars.contains(tok->id())) { seen_vars.insert(tok->id()); expr_ref len_var(seq.str.mk_length(tok->get_expr()), m); @@ -4561,7 +4567,7 @@ namespace seq { } } - void nielsen_graph::compute_regex_length_interval(euf::snode* regex, unsigned& min_len, unsigned& max_len) const { + void nielsen_graph::compute_regex_length_interval(euf::snode const* regex, unsigned& min_len, unsigned& max_len) const { const seq_util & seq = m_sg.get_seq_util(); expr* e = regex->get_expr(); SASSERT(e && seq.is_re(e)); @@ -4594,14 +4600,14 @@ namespace seq { // + NielsenNode constructor length assertion logic // ----------------------------------------------------------------------- - expr_ref nielsen_graph::get_or_create_char_var(euf::snode* var) { + expr_ref nielsen_graph::get_or_create_char_var(euf::snode const* var) { SASSERT(var && var->is_var()); const expr_ref idx(a.mk_sub(seq().str.mk_length(var->get_expr()), compute_length_expr(var)), m); const auto e = seq().str.mk_nth_u(var->get_expr(), idx); return expr_ref(m_seq.str.mk_unit(expr_ref(e, m)), m); } - expr_ref nielsen_graph::get_or_create_gpower_n_var(euf::snode* var) { + expr_ref nielsen_graph::get_or_create_gpower_n_var(euf::snode const* var) { SASSERT(var && var->is_var()); //unsigned mc = 0; //m_mod_cnt.find(var->id(), mc); @@ -4609,7 +4615,7 @@ namespace seq { return m_sk.mk("gpn!", var->get_expr()/*, a.mk_int(mc)*/, a.mk_int()); } - expr_ref nielsen_graph::get_or_create_gpower_m_var(euf::snode* var) { + expr_ref nielsen_graph::get_or_create_gpower_m_var(euf::snode const* var) { SASSERT(var && var->is_var()); //unsigned mc = 0; //m_mod_cnt.find(var->id(), mc); @@ -4686,7 +4692,7 @@ namespace seq { euf::snode_vector tokens; eq.m_lhs->collect_tokens(tokens); eq.m_rhs->collect_tokens(tokens); - for (euf::snode* tok : tokens) { + for (euf::snode const* tok : tokens) { if (tok->is_var() && !seen_vars.contains(tok->id())) { seen_vars.insert(tok->id()); expr_ref len_var = compute_length_expr(tok); @@ -4779,12 +4785,12 @@ namespace seq { return constraint(fml, dep, m); } - expr* nielsen_graph::get_power_exponent(euf::snode* power) { + expr* nielsen_graph::get_power_exponent(euf::snode const* power) { SASSERT(power); if (!power->is_power()) return nullptr; SASSERT(power->num_args() == 2); - const euf::snode* exp_snode = power->arg(1); + euf::snode const* exp_snode = power->arg(1); return exp_snode ? exp_snode->get_expr() : nullptr; } @@ -4813,9 +4819,9 @@ namespace seq { SASSERT(dep); - const euf::snode* approx = nullptr; - for (euf::snode* tok : tokens) { - euf::snode* tok_re = nullptr; + euf::snode const* approx = nullptr; + for (euf::snode const* tok : tokens) { + euf::snode const* tok_re = nullptr; if (tok->is_char()) { // Concrete character → to_re(unit(c)) @@ -4825,7 +4831,7 @@ namespace seq { } else if (tok->is_var()) { // Variable → intersection of primitive regex constraints, or Σ* - euf::snode* x_range = m_seq_regex->collect_primitive_regex_intersection(tok, node, m_dep_mgr, dep); + euf::snode const* x_range = m_seq_regex->collect_primitive_regex_intersection(tok, node, m_dep_mgr, dep); if (x_range) tok_re = x_range; else { @@ -4843,12 +4849,12 @@ namespace seq { auto& cs = node.char_ranges()[tok->id()]; if (!cs.first.is_empty()) { // Build union of re.range for each interval - euf::snode* range_re = nullptr; + euf::snode const* range_re = nullptr; for (auto const& r : cs.first.ranges()) { expr_ref rng(m_seq.re.mk_range( m_seq.str.mk_string(zstring(r.m_lo)), m_seq.str.mk_string(zstring(r.m_hi - 1))), m); - euf::snode* rng_sn = m_sg.mk(rng); + euf::snode const* rng_sn = m_sg.mk(rng); if (!range_re) range_re = rng_sn; else { @@ -4895,7 +4901,7 @@ namespace seq { expr* re = regex->get_expr(); SASSERT(ae && re); const expr_ref inter(m_seq.re.mk_inter(ae, re), m); - euf::snode* inter_sn = m_sg.mk(inter); + euf::snode const* inter_sn = m_sg.mk(inter); SASSERT(inter_sn); // TODO: Minimize the conflict here const lbool result = m_seq_regex->is_empty_bfs(inter_sn, 5000); @@ -4914,15 +4920,15 @@ namespace seq { SASSERT(m_seq_regex); // Group str_mem constraints by variable (primitive constraints only) - u_map, dep_tracker>> var_regexes; + u_map> var_regexes; for (auto const& mem : node.str_mems()) { SASSERT(mem.is_primitive()); - const euf::snode* const first = mem.m_str->first(); + euf::snode const* const first = mem.m_str->first(); SASSERT(first && first->is_var()); - auto& list = var_regexes.insert_if_not_there(first->id(), std::pair, dep_tracker>()); - list.first.push_back(mem.m_regex); - list.second = dep_mgr().mk_join(list.second, mem.m_dep); + auto &[fst, snd] = var_regexes.insert_if_not_there(first->id(), std::pair()); + fst.push_back(mem.m_regex); + snd = dep_mgr().mk_join(snd, mem.m_dep); } // check intersection non-emptiness (also for single occurrences; it could be empty) diff --git a/src/smt/seq/seq_nielsen.h b/src/smt/seq/seq_nielsen.h index 24026264c..958c13405 100644 --- a/src/smt/seq/seq_nielsen.h +++ b/src/smt/seq/seq_nielsen.h @@ -27,202 +27,6 @@ Abstract: -- nielsen_node: graph node with constraint set and outgoing edges -- nielsen_graph: the overall Nielsen transformation graph - ----------------------------------------------------------------------- - ZIPT PORT COMPARISON SUMMARY - ----------------------------------------------------------------------- - - The ZIPT reference is organized as follows (all under ZIPT/Constraints/): - NielsenGraph.cs -- the graph manager class - NielsenNode.cs -- node class + BacktrackReasons enum - NielsenEdge.cs -- edge class with string and character substitutions - ConstraintElement/ - Constraint.cs -- abstract base for all constraints - StrEqBase.cs -- abstract base for StrEq and StrMem - StrEq.cs -- string equality with full simplification/splitting - StrMem.cs -- regex membership with Brzozowski derivatives - IntEq.cs -- integer equality over length polynomials - IntLe.cs -- integer inequality over length polynomials - Modifier/ -- ~15 modifier types driving graph expansion - - A. PORTED FAITHFULLY - -------------------- - 1. backtrack_reason enum (BacktrackReasons): all eleven values (Unevaluated, - Extended, SymbolClash, ParikhImage, Subsumption, Arithmetic, Regex, - RegexWidening, CharacterRange, SMT, ChildrenFailed) are present with - identical semantics. - - 2. simplify_result enum (SimplifyResult): all five values (Proceed, Conflict, - Satisfied, Restart, RestartAndSatisfied) are present with identical semantics. - Note: RestartAndSatisfied is declared but not yet exercised in this port. - - 3. nielsen_node status fields and accessors: m_is_general_conflict, - m_is_extended, m_reason, m_eval_idx map directly to IsGeneralConflict, - IsExtended, CurrentReason, evalIdx. The is_currently_conflict() predicate - faithfully mirrors IsCurrentlyConflict (GeneralConflict || (reason != - Unevaluated && IsExtended)). - - 4. nielsen_node::reset_counter() mirrors NielsenNode.ResetCounter() exactly. - - 5. nielsen_node::clone_from() mirrors the copy constructor - NielsenNode(graph, parent) for str_eq and str_mem constraints. - - 6. nielsen_edge identity (operator==) mirrors NielsenEdge.Equals(): both - compare by source and target node pointer identity. - - 7. nielsen_graph::inc_run_idx() mirrors the RunIdx increment in NielsenGraph. - Check(), including the UINT_MAX overflow guard that calls reset_counter() - on all nodes. - - 8. str_eq::sort() mirrors StrEqBase.SortStr(): swaps lhs/rhs when lhs > rhs. - (Z3 compares by snode id; ZIPT compares Str lexicographically.) - - 9. str_eq::is_trivial() mirrors the trivially-satisfied check when both sides - are empty. - - 10. str_mem fields (m_str, m_regex, m_history, m_id, m_dep) mirror StrMem - fields (Str, Regex, History, Id, Reason) faithfully, including the unique - identifier used for cycle tracking. - - 11. str_mem::is_primitive() mirrors StrMem.IsPrimitiveRegex(): single variable - on the left side of the membership constraint. - - 12. nielsen_subst::is_eliminating() mirrors the logic behind - NielsenEdge.BumpedModCount: a substitution is non-eliminating (bumps the - modification counter) when the substituted variable appears in the - replacement. - - 13. nielsen_graph::mk_edge() faithfully mirrors NielsenEdge construction: it - links src to tgt and registers the outgoing edge. - - B. PORTED WITH ALGORITHMIC CHANGES - ------------------------------------ - 1. dep_tracker (DependencyTracker): ZIPT's DependencyTracker is a .NET - class using a BitArray-like structure for tracking constraint origins. - Z3 uses scoped_dependency_manager (an arena-based binary - join tree from util/dependency.h) where each leaf carries a dep_source - value identifying the originating eq or mem constraint by kind and index. - - 2. Substitution application (nielsen_node::apply_subst): ZIPT uses an - immutable, functional style -- Apply() returns a new constraint if - changed, using C# reference equality to detect no-ops. Z3 uses - in-place mutation via sgraph::subst(), modifying the constraint vectors - directly. The functional change also propagates the substitution's - dependency to the merged constraint. - - 3. Node constraint containers: ZIPT's NielsenNode stores str_eq constraints - in NList (a sorted list for O(log n) subsumption lookup) and str_mem - constraints in Dictionary (keyed by id for O(1) cycle lookup). - Z3 uses plain vector and vector, which is simpler. - - 4. nielsen_edge substitution list: ZIPT's NielsenEdge carries two substitution - lists -- Subst (string-level, mapping string variables to strings) and - SubstC (character-level, mapping symbolic character variables to concrete - characters). Z3's nielsen_edge carries a single vector, - covering only string-level substitutions; character substitutions are not - represented. - - 5. nielsen_graph node registry: ZIPT keeps nodes in a HashSet plus - a Dictionary, List> for subsumption candidate - lookup. Z3 uses a ptr_vector, simplifying memory management. - - 6. nielsen_graph::display() vs NielsenGraph.ToDot(): ZIPT outputs a DOT-format - graph with color highlighting for the current satisfying path. Z3 outputs - plain human-readable text with node/edge details but no DOT syntax or path - highlighting. - - 7. str_eq::contains_var() / str_mem::contains_var(): ZIPT performs occurrence - checks through StrManager.Subst() (which uses hash-consing and reference - equality). Z3 walks the snode tree via collect_tokens(), which is correct - but re-traverses the DAG on every call. - - C. NOT PORTED - ------------- - The following ZIPT components are absent from this implementation. - They represent the algorithmic core of the search procedure and - are expected to be ported in subsequent work. - - Constraint simplification and propagation: - - Constraint.SimplifyAndPropagate() / SimplifyAndPropagateInternal(): the - main constraint-driven simplification loop is not ported. str_eq and - str_mem have no Simplify methods. - - StrEq.SimplifyDir() / SimplifyFinal() / AddDefinition(): forward/backward - simplification passes, including Makanin-style prefix cancellation, power - token handling, and variable definition propagation. - - StrEq.GetNielsenDep() / SplitEq(): the Nielsen dependency analysis and - equation-splitting heuristic used to choose the best split point. - - StrMem.SimplifyCharRegex() / SimplifyDir(): Brzozowski derivative-based - simplification consuming ground prefixes/suffixes of the string. - - StrMem.TrySubsume(): stabilizer-based subsumption (not ported, not needed). - - StrMem.ExtractCycle() / StabilizerFromCycle(): cycle detection over the - search path and extraction of a Kleene-star stabilizer to generalize the - cycle. This is the key termination argument for regex membership. - - StrMem.Extend(): the splitting driver that produces the next modifier - (RegexVarSplitModifier, RegexCharSplitModifier, StarIntrModifier, etc.). - - Integer constraints: - - IntEq / IntLe: integer equality and inequality constraints over Presburger - arithmetic polynomials (PDD) are entirely absent. The Z3 port - has no ConstraintsIntEq or ConstraintsIntLe in nielsen_node. - - IntBounds / VarBoundWatcher: ZIPT-style cached interval maps and eager - watcher propagation are not stored in nielsen_node; bounds are queried - from the arithmetic subsolver on demand. - - AddLowerIntBound() / AddHigherIntBound(): incremental interval tightening - — PORTED as the above add_lower/upper_int_bound methods. - - Character-level handling: - - CharSubst: character-level variable substitution (symbolic char -> concrete - char) is absent. ZIPT uses this to handle symbolic character tokens - (SymCharToken) that represent a single unknown character. - - SymCharToken / CharacterSet: symbolic character tokens with associated - character range constraints (CharRanges) are not ported. - - DisEqualities: per-node character disequality constraints used for conflict - detection during character substitution are not ported. - - Modifier hierarchy (Constraints/Modifier/): - - 13 Modifier subclasses driving graph expansion are ported as - apply_* methods in generate_extensions, matching ZIPT's TypeOrder - priority: DetModifier(1), PowerEpsilonModifier(2), NumCmpModifier(3), - ConstNumUnwindingModifier(4), EqSplitModifier(5), StarIntrModifier(6), - GPowerIntrModifier(7), ConstNielsenModifier(8), RegexCharSplitModifier(9), - RegexVarSplitModifier(10), PowerSplitModifier(11), VarNielsenModifier(12), - VarNumUnwindingModifier(13). - - NOT PORTED: DirectedNielsenModifier, DecomposeModifier, CombinedModifier. - - NumCmp, ConstNumUnwinding, VarNumUnwinding are approximated (no PDD - integer polynomial infrastructure; power tokens are replaced with ε - or peeled with fresh variables instead of exact exponent arithmetic). - - Search procedure: - - NielsenGraph.Check() / NielsenNode.GraphExpansion(): ported as - nielsen_graph::solve() (iterative deepening, starting at depth 3, - incrementing by 1 per failure, bounded by smt.nseq.max_depth) and - search_dfs() (depth-bounded DFS with eval_idx cycle detection and - node status tracking). - - NielsenNode.SimplifyAndInit(): ported as - nielsen_node::simplify_and_init() with prefix matching, symbol clash, - empty propagation, and Brzozowski derivative consumption. - - NielsenGraph.FindExisting() / subsumption cache lookup: not ported, - not needed. - - Auxiliary infrastructure: - - LocalInfo: thread-local search bookkeeping (current path, modification - counts, regex occurrence cache for cycle detection, current node pointer) - is not ported. - - NielsenGraph.SubSolver / InnerStringPropagator: the auxiliary Z3 solver - for arithmetic lemma generation and the inner string propagator for - model-based refinement are not ported. - - PowerToken: word-repetition tokens of the form u^n (distinct from regex - Kleene star) are not represented in Z3's snode. - - GetSignature(): the constraint-pair signature used for subsumption - candidate matching is not ported. - - Constraint.Shared: the flag indicating whether a constraint should be - forwarded to the outer solver — PORTED as - nielsen_graph::assert_root_constraints_to_solver(), called at the start - of solve() to make all root-level length/Parikh constraints immediately - visible to m_solver. - - Interpretation: the model-extraction class mapping string and integer - variables to concrete values is not ported. - ----------------------------------------------------------------------- - Author: Clemens Eisenhofer 2026-03-02 @@ -316,8 +120,6 @@ namespace seq { using enode_pair_vector = svector; using dep_source = std::variant; - - // Arena-based dependency manager: builds an immutable tree of dep_source // leaves joined by binary join nodes. Memory is managed via a region; // call dep_manager::reset() to release all allocations at once. @@ -375,14 +177,26 @@ namespace seq { // and arithmetic <= dependencies. void deps_to_lits(dep_manager &dep_mgr, dep_tracker deps, svector &eqs, svector &lits); + // decompose a membership constraint into a set of pairs of regex splits + std::pair split_membership(euf::snode const* str, euf::snode const* regex, euf::sgraph& sg, unsigned threshold, split_set& result); + + // Lookahead oracle for the split engine: is the split's right component + // `n_regex` prefix-compatible with the constant character sequence `c`? + // The factorization picks a boundary so the tail starts with c, hence the + // tail-regex ∇ must be able to match c as a prefix. We use a *prefix* test + // (not strict "starts-with"): we accept as soon as N accepts a prefix of c + // (a suffix appended downstream can complete it). This is sound to apply + // during split generation — it never drops a viable split. + bool split_lookahead_viable(expr* n_regex, euf::sgraph& sg, zstring const& c); + // string equality constraint: lhs = rhs // mirrors ZIPT's StrEq (both sides are regex-free snode trees) struct str_eq { - euf::snode* m_lhs; // assumed to be non-null - euf::snode* m_rhs; // assumed to be non-null + euf::snode const* m_lhs; // assumed to be non-null + euf::snode const* m_rhs; // assumed to be non-null dep_tracker m_dep; - str_eq(euf::snode* lhs, euf::snode* rhs, dep_tracker const& dep): + str_eq(euf::snode const* lhs, euf::snode const* rhs, dep_tracker const& dep): m_lhs(lhs), m_rhs(rhs), m_dep(dep) { SASSERT(well_formed()); } @@ -398,7 +212,7 @@ namespace seq { bool is_trivial() const; // check if the constraint contains a given variable - bool contains_var(euf::snode* var) const; + bool contains_var(euf::snode const* var) const; bool well_formed() const { // assumed to be always true @@ -420,11 +234,11 @@ namespace seq { // string disequality constraint: lhs != rhs struct str_deq { - euf::snode* m_lhs; // assumed to be non-null - euf::snode* m_rhs; // assumed to be non-null + euf::snode const* m_lhs; // assumed to be non-null + euf::snode const* m_rhs; // assumed to be non-null dep_tracker m_dep; - str_deq(euf::snode* lhs, euf::snode* rhs, dep_tracker const& dep): + str_deq(euf::snode const* lhs, euf::snode const* rhs, dep_tracker const& dep): m_lhs(lhs), m_rhs(rhs), m_dep(dep) { SASSERT(well_formed()); } @@ -439,7 +253,7 @@ namespace seq { } } - bool contains_var(euf::snode* var) const { + bool contains_var(euf::snode const* var) const { return m_lhs->collect_tokens().contains(var) || m_rhs->collect_tokens().contains(var); } @@ -464,11 +278,11 @@ namespace seq { // regex membership constraint: str in regex // mirrors ZIPT's StrMem struct str_mem { - euf::snode* m_str; // assumed to be non-null - euf::snode* m_regex; // assumed to be non-null + euf::snode const* m_str; // assumed to be non-null + euf::snode const* m_regex; // assumed to be non-null dep_tracker m_dep; - str_mem(euf::snode* str, euf::snode* regex, dep_tracker const& dep): + str_mem(euf::snode const* str, euf::snode const* regex, dep_tracker const& dep): m_str(str), m_regex(regex), m_dep(dep) {} bool operator==(str_mem const& other) const { @@ -484,7 +298,7 @@ namespace seq { bool is_contradiction(nielsen_node const* n) const; // check if the constraint contains a given variable - bool contains_var(euf::snode* var) const; + bool contains_var(euf::snode const* var) const; bool well_formed() const { // assumed to be always true @@ -508,12 +322,12 @@ namespace seq { // (can be used as well to substitute arbitrary nodes - like powers) // mirrors ZIPT's Subst struct nielsen_subst { - euf::snode* m_var; - euf::snode* m_replacement; + euf::snode const* m_var; + euf::snode const* m_replacement; dep_tracker m_dep; nielsen_subst(): m_var(nullptr), m_replacement(nullptr), m_dep(nullptr) {} - nielsen_subst(euf::snode* var, euf::snode* repl, dep_tracker const& dep): + nielsen_subst(euf::snode const* var, euf::snode const* repl, dep_tracker const& dep): m_var(var), m_replacement(repl), m_dep(dep) { SASSERT(var != nullptr); SASSERT(repl != nullptr); @@ -718,7 +532,7 @@ namespace seq { // add a character range constraint for a symbolic char. // intersects with existing range; sets conflict if result is empty. - void add_char_range(euf::snode* sym_char, char_set const& range, dep_tracker dep); + void add_char_range(euf::snode const* sym_char, char_set const& range, dep_tracker dep); // edge access ptr_vector const& outgoing() const { return m_outgoing; } @@ -817,7 +631,7 @@ namespace seq { // Collects tokens from non_empty_side; if any token causes a conflict // (is a concrete character or an unexpected kind), sets conflict flags // and returns true. Otherwise returns false. - bool check_empty_side_conflict(euf::sgraph& sg, euf::snode* non_empty_side, + bool check_empty_side_conflict(euf::sgraph& sg, euf::snode const* non_empty_side, dep_tracker const& dep); // Length bounds are queried from the arithmetic subsolver when needed. @@ -868,7 +682,7 @@ namespace seq { friend class nielsen_node; friend class nielsen_edge; - // Edge endpoints are stored as expr* (not snode*) because the cache + // Edge endpoints are stored as expr* (not snode const*) because the cache // must survive sgraph pops. snodes are allocated in a region that is // never freed, but their m_expr field is owned by the egraph trail and // becomes dangling on pop. We pin the referenced expressions via @@ -1036,14 +850,14 @@ namespace seq { ptr_vector const& sat_path() const { return m_sat_path; } // add constraints to the root node from external solver - void add_str_eq(euf::snode* lhs, euf::snode* rhs, smt::enode* l, smt::enode* r) const; - void add_str_deq(euf::snode* lhs, euf::snode* rhs, sat::literal l) const; - void add_str_mem(euf::snode* str, euf::snode* regex, sat::literal l) const; + void add_str_eq(euf::snode const* lhs, euf::snode const* rhs, smt::enode* l, smt::enode* r) const; + void add_str_deq(euf::snode const* lhs, euf::snode const* rhs, sat::literal l) const; + void add_str_mem(euf::snode const* str, euf::snode const* regex, sat::literal l) const; // test-friendly overloads (no external dependency tracking) - void add_str_eq(euf::snode* lhs, euf::snode* rhs); - void add_str_deq(euf::snode* lhs, euf::snode* rhs); - void add_str_mem(euf::snode* str, euf::snode* regex); + void add_str_eq(euf::snode const* lhs, euf::snode const* rhs); + void add_str_deq(euf::snode const* lhs, euf::snode const* rhs); + void add_str_mem(euf::snode const* str, euf::snode const* regex); // access all nodes ptr_vector const& nodes() const { return m_nodes; } @@ -1075,9 +889,9 @@ namespace seq { std::string to_dot() const; - std::ostream& partial_dfa_to_dot(std::ostream& out, euf::snode* start_state, bool keep_names) const; + std::ostream& partial_dfa_to_dot(std::ostream& out, euf::snode const* start_state, bool keep_names) const; - std::string partial_dfa_to_dot(euf::snode* start_state, bool keep_names) const; + std::string partial_dfa_to_dot(euf::snode const* start_state, bool keep_names) const; // reset all nodes and state void reset(); @@ -1118,12 +932,12 @@ namespace seq { // build an arithmetic expression representing the length of an snode tree. // concatenations are expanded to sums, chars to 1, empty to 0, // variables to (str.len var_expr). - expr_ref compute_length_expr(euf::snode* n); + expr_ref compute_length_expr(euf::snode const* n); // compute Parikh length interval [min_len, max_len] for a regex snode. // uses seq_util::rex min_length/max_length on the underlying expression. // max_len == UINT_MAX means unbounded. - void compute_regex_length_interval(euf::snode* regex, unsigned& min_len, unsigned& max_len) const; + void compute_regex_length_interval(euf::snode const* regex, unsigned& min_len, unsigned& max_len) const; // accessor for the seq_regex module seq_regex* seq_regex_module() const { return m_seq_regex; } @@ -1175,15 +989,15 @@ namespace seq { // Record a discovered derivative edge in the global partial DFA. // The `label` may be a concrete string token (converted to to_re) // or an already-regular-expression minterm. - void record_partial_derivative_edge(euf::snode* src_re, euf::snode* label, euf::snode* dst_re); + void record_partial_derivative_edge(euf::snode const* src_re, euf::snode const* label, euf::snode const* dst_re); // Convert a transition label (string token or regex minterm) into a // one-character regex snode used by the partial DFA. - euf::snode* to_partial_label_regex(euf::snode* label) const; + euf::snode const* to_partial_label_regex(euf::snode const* label) const; // Collect the SCC containing root_re in the current partial DFA. // Returns false if no cyclic SCC containing root_re exists. - bool collect_scc_for_projection(euf::snode* root_re, uint_set& scc) const; + bool collect_scc_for_projection(euf::snode const* root_re, uint_set& scc) const; // Mark SCC edges with a monotone extraction index and return the // currently covered edge count for this extraction. @@ -1194,17 +1008,17 @@ namespace seq { // snapshot index nu. This is the stabilizer of root_re kept symbolically // (the projection's derivative/nullability are evaluated lazily by the // sgraph consulting projection_state_in_Q). - euf::snode* mk_projection_term(euf::snode* root_re, unsigned nu); + euf::snode const* mk_projection_term(euf::snode const* root_re, unsigned nu); // Try to extract a stronger projection for root_re. Returns true and // stores it in projection_re iff SCC coverage has grown. - bool try_extract_partial_projection(euf::snode* root_re, euf::snode*& projection_re); + bool try_extract_partial_projection(euf::snode const* root_re, euf::snode const*& projection_re); - euf::snode* get_slice(euf::snode* v, expr* left, expr* right); + euf::snode const* get_slice(euf::snode const* v, expr* left, expr* right); - euf::snode* get_tail(euf::snode* v, expr* cnt, bool fwd = true); + euf::snode const* get_tail(euf::snode const* v, expr* cnt, bool fwd = true); - euf::snode* get_tail(euf::snode* v, unsigned cnt, bool fwd = true); + euf::snode const* get_tail(euf::snode const* v, unsigned cnt, bool fwd = true); // Apply the Parikh image filter to a node: generate modular length // constraints from regex memberships and append them to the node's @@ -1217,10 +1031,10 @@ namespace seq { void apply_parikh_to_node(nielsen_node& node) const; // simplify expression and create a node from simplified expression. - euf::snode *mk_rewrite(expr *e) const; + euf::snode const* mk_rewrite(expr *e) const; // create a fresh variable with a unique name and the given sequence sort - euf::snode* mk_fresh_var(sort* s); + euf::snode const* mk_fresh_var(sort* s); // deterministic modifier: var = ε, same-head cancel bool apply_det_modifier(nielsen_node* node); @@ -1238,10 +1052,10 @@ namespace seq { // helper: classify whether a token has variable (symbolic) length // returns true for variables, powers, etc.; false for chars, units, string literals - bool token_has_variable_length(euf::snode* tok) const; + bool token_has_variable_length(euf::snode const* tok) const; // helper: get the constant length of a token (only valid when !token_has_variable_length) - unsigned token_const_length(euf::snode* tok) const; + unsigned token_const_length(euf::snode const* tok) const; // helper: find a split point in a regex-free equation. // ports ZIPT's StrEq.SplitEq algorithm. @@ -1293,19 +1107,19 @@ namespace seq { // Return the current stabilizer s* for root_re from the partial DFA // (bypasses the novelty guard used by try_extract_partial_projection). - euf::snode* get_current_stabilizer(euf::snode* root_re); + euf::snode const* get_current_stabilizer(euf::snode const* root_re); // BFS of Brzozowski derivatives from root_re up to `depth` steps, // eagerly recording concrete minterm edges in the partial DFA so that // collect_scc_for_projection can find cycles without first waiting for // concrete children to record them one level at a time. - void precompute_partial_dfa(euf::snode* root_re, unsigned depth); + void precompute_partial_dfa(euf::snode const* root_re, unsigned depth); // Walk an ite-structured symbolic derivative expression and record // concrete DFA edges for each non-fail branch. // Called from simplify_and_init when a symbolic character is consumed, // so that cycle_decomp can detect SCCs lazily (as with concrete chars). - void record_dfa_edges_from_ite(euf::snode* src_re, expr* ite_deriv); + void record_dfa_edges_from_ite(euf::snode const* src_re, expr* ite_deriv); // generalized power introduction: for an equation where one head is // a variable v and the other side has ground prefix + a variable x @@ -1316,18 +1130,11 @@ namespace seq { // generalized regex factorization (Boolean closure derivation rule) bool apply_regex_factorization(nielsen_node* node); - // Lookahead oracle for apply_regex_factorization's split() call: returns - // true iff the split's right component `n_regex` is prefix-compatible with - // the constant character sequence `c` (the tail of the factorization starts - // with c). Prunes splits whose tail-regex can never match c. Sound to - // apply during split generation (prefix-, not strict-, match). - bool split_lookahead_viable(expr* n_regex, zstring const& c); - // helper for apply_gpower_intr: fires the substitution. // `fwd=true` uses left-to-right decomposition; `fwd=false` mirrors ZIPT's // backward (right-to-left) direction. bool fire_gpower_intro(nielsen_node* node, str_eq const& eq, - euf::snode* var, euf::snode_vector const& ground_prefix_orig, bool fwd); + euf::snode const* var, euf::snode_vector const& ground_prefix_orig, bool fwd); // heuristic string equation splitting. Left to right scanning for shortest prefix with matching variables. bool apply_signature_split(nielsen_node* node); @@ -1353,17 +1160,17 @@ namespace seq { bool axiomatize_diseq(nielsen_node* node); // find the first power token in any str_eq at this node - static euf::snode* find_power_token(nielsen_node* node); + static euf::snode const* find_power_token(nielsen_node* node); // find a power token facing a constant (char/non-var) token at either end // of an equation; returns orientation via `fwd` (true=head, false=tail). - static bool find_power_vs_non_var(nielsen_node* node, euf::snode*& power, euf::snode*& other_head, str_eq const*& eq_out, bool& fwd); + static bool find_power_vs_non_var(nielsen_node* node, euf::snode const*& power, euf::snode const*& other_head, str_eq const*& eq_out, bool& fwd); // find a power token facing a variable token at either end of an // equation; returns orientation via `fwd` (true=head, false=tail). - static bool find_power_vs_var(nielsen_node* node, euf::snode*& power, euf::snode*& var_head, str_eq const*& eq_out, bool& fwd); + static bool find_power_vs_var(nielsen_node* node, euf::snode const*& power, euf::snode const*& var_head, str_eq const*& eq_out, bool& fwd); - static bool find_power_vs_var(nielsen_node* node, euf::snode*& power, str_mem const*& mem_out, bool& fwd); + static bool find_power_vs_var(nielsen_node* node, euf::snode const*& power, str_mem const*& mem_out, bool& fwd); // ----------------------------------------------- // Integer feasibility subsolver methods @@ -1404,7 +1211,7 @@ namespace seq { constraint mk_constraint(expr *fml, dep_tracker const &dep) const; // get the exponent expression from a power snode (arg(1)) - static expr * get_power_exponent(euf::snode* power); + static expr * get_power_exponent(euf::snode const* power); // ----------------------------------------------- // Modification counter methods for substitution length tracking. @@ -1413,13 +1220,13 @@ namespace seq { // ----------------------------------------------- // Get or create a fresh symbolic character variable for the given variable - expr_ref get_or_create_char_var(euf::snode* var); + expr_ref get_or_create_char_var(euf::snode const* var); // Get or create a fresh integer variable for gpower n (full exponent) for the given variable - expr_ref get_or_create_gpower_n_var(euf::snode* var); + expr_ref get_or_create_gpower_n_var(euf::snode const* var); // Get or create a fresh integer variable for gpower m (partial exponent) for the given variable - expr_ref get_or_create_gpower_m_var(euf::snode* var); + expr_ref get_or_create_gpower_m_var(euf::snode const* var); // Compute and add |x| = |u| length constraints to an edge for all // its non-eliminating substitutions. Uses current m_mod_cnt. diff --git a/src/smt/seq/seq_nielsen_pp.cpp b/src/smt/seq/seq_nielsen_pp.cpp index a1e614255..444877405 100644 --- a/src/smt/seq/seq_nielsen_pp.cpp +++ b/src/smt/seq/seq_nielsen_pp.cpp @@ -627,12 +627,12 @@ namespace seq { return; // collect the original variables present in the root node's constraints - ptr_vector vars; + euf::snode_vector vars; uint_set seen; - auto add_vars = [&](euf::snode* s) { + auto add_vars = [&](euf::snode const* s) { if (!s) return; - for (euf::snode* t : s->collect_tokens()) + for (euf::snode const* t : s->collect_tokens()) if (t->is_var() && !seen.contains(t->id())) { seen.insert(t->id()); vars.push_back(t); @@ -657,12 +657,14 @@ namespace seq { } bool any = false; - for (euf::snode* var : vars) { - euf::snode* val = var; + for (euf::snode const* var : vars) { + euf::snode const* val = var; // apply substitutions in root-to-node order (path is node-to-root) - for (unsigned i = path.size(); i-- > 0; ) - for (nielsen_subst const& s : path[i]->subst()) + for (unsigned i = path.size(); i-- > 0; ) { + for (nielsen_subst const& s : path[i]->subst()) { val = sg.subst(val, s.m_var, s.m_replacement); + } + } if (val == var) continue; // unchanged: variable is still free at this node if (!any) { out << "
Subst:
"; any = true; } @@ -763,7 +765,7 @@ namespace seq { return ss.str(); } - std::ostream& nielsen_graph::partial_dfa_to_dot(std::ostream& out, euf::snode* start_state, bool keep_names) const { + std::ostream& nielsen_graph::partial_dfa_to_dot(std::ostream& out, euf::snode const* start_state, bool keep_names) const { out << "digraph G {\n"; out << " node [shape=box];\n"; @@ -833,8 +835,8 @@ namespace seq { bool accepting = false; if (node_expr) { - euf::snode* sn = m_sg.mk(node_expr); - accepting = (const_cast(m_sg).re_nullable(sn) == l_true); + euf::snode const* sn = m_sg.mk(node_expr); + accepting = m_sg.re_nullable(sn) == l_true; } out << " N" << node_id << " ["; @@ -873,7 +875,7 @@ namespace seq { return out; } - std::string nielsen_graph::partial_dfa_to_dot(euf::snode* start_state, bool keep_names) const { + std::string nielsen_graph::partial_dfa_to_dot(euf::snode const* start_state, bool keep_names) const { std::stringstream ss; partial_dfa_to_dot(ss, start_state, keep_names); return ss.str(); diff --git a/src/smt/seq/seq_regex.cpp b/src/smt/seq/seq_regex.cpp index ac271db4c..330813690 100644 --- a/src/smt/seq/seq_regex.cpp +++ b/src/smt/seq/seq_regex.cpp @@ -87,22 +87,22 @@ namespace seq { m_self_stabilizing.reset(); } - void seq_regex::add_stabilizer(euf::snode* regex, euf::snode* stabilizer) { + void seq_regex::add_stabilizer(euf::snode const* regex, euf::snode const* stabilizer) { if (!regex || !stabilizer) return; - unsigned id = regex->id(); - auto& stabs = m_stabilizers.insert_if_not_there(id, ptr_vector()); + const unsigned id = regex->id(); + auto& stabs = m_stabilizers.insert_if_not_there(id, euf::snode_vector()); // De-duplicate by pointer equality (mirrors ZIPT Environment.AddStabilizer // which checks reference equality before adding). - for (euf::snode* s : stabs) + for (euf::snode const* s : stabs) if (s == stabilizer) return; stabs.push_back(stabilizer); } - euf::snode* seq_regex::get_stabilizer_union(euf::snode* regex) { + euf::snode const* seq_regex::get_stabilizer_union(euf::snode const* regex) { if (!regex) return nullptr; @@ -119,7 +119,7 @@ namespace seq { // Multiple stabilizers: build re.union chain. // union(s1, union(s2, ... union(sN-1, sN)...)) - euf::snode* result = stabs[stabs.size() - 1]; + euf::snode const* result = stabs[stabs.size() - 1]; for (unsigned i = stabs.size() - 1; i-- > 0; ) { expr* lhs = stabs[i]->get_expr(); expr* rhs = result->get_expr(); @@ -128,7 +128,7 @@ namespace seq { return result; } - bool seq_regex::has_stabilizers(euf::snode* regex) const { + bool seq_regex::has_stabilizers(euf::snode const* regex) const { if (!regex) return false; if (!m_stabilizers.contains(regex->id())) @@ -136,7 +136,7 @@ namespace seq { return !m_stabilizers[regex->id()].empty(); } - ptr_vector const* seq_regex::get_stabilizers(euf::snode* regex) const { + euf::snode_vector const* seq_regex::get_stabilizers(euf::snode const* regex) const { if (!regex) return nullptr; if (!m_stabilizers.contains(regex->id())) @@ -144,12 +144,12 @@ namespace seq { return &m_stabilizers[regex->id()]; } - void seq_regex::set_self_stabilizing(euf::snode* regex) { + void seq_regex::set_self_stabilizing(euf::snode const* regex) { if (regex) m_self_stabilizing.insert(regex->id()); } - bool seq_regex::is_self_stabilizing(euf::snode* regex) const { + bool seq_regex::is_self_stabilizing(euf::snode const* regex) const { return regex && m_self_stabilizing.contains(regex->id()); } @@ -157,7 +157,7 @@ namespace seq { // Self-stabilizing auto-detection // ----------------------------------------------------------------------- - bool seq_regex::compute_self_stabilizing(euf::snode* regex) const { + bool seq_regex::compute_self_stabilizing(euf::snode const* regex) const { return false; } @@ -165,7 +165,7 @@ namespace seq { // Self-stabilizing propagation through derivatives // ----------------------------------------------------------------------- - void seq_regex::propagate_self_stabilizing(euf::snode* parent, euf::snode* deriv) { + void seq_regex::propagate_self_stabilizing(euf::snode const* parent, euf::snode const* deriv) { if (!parent || !deriv) return; @@ -205,7 +205,7 @@ namespace seq { // If S is self-stabilizing, the D(c,R)·S branch inherits it. // If the whole parent R·S is self-stabilizing, the derivative is too. if (parent->is_concat() && parent->num_args() == 2) { - euf::snode* tail = parent->arg(1); + euf::snode const* tail = parent->arg(1); bool tail_ss = is_self_stabilizing(tail) || compute_self_stabilizing(tail); if (tail_ss || parent_ss) { set_self_stabilizing(deriv); @@ -217,8 +217,8 @@ namespace seq { // D(c, R|S) = D(c,R) | D(c,S). // Self-stabilizing if both children are self-stabilizing. if (parent->is_union() && parent->num_args() == 2) { - euf::snode* lhs = parent->arg(0); - euf::snode* rhs = parent->arg(1); + euf::snode const* lhs = parent->arg(0); + euf::snode const* rhs = parent->arg(1); bool lhs_ss = is_self_stabilizing(lhs) || compute_self_stabilizing(lhs); bool rhs_ss = is_self_stabilizing(rhs) || compute_self_stabilizing(rhs); if (lhs_ss && rhs_ss) { @@ -231,8 +231,8 @@ namespace seq { // D(c, R∩S) = D(c,R) ∩ D(c,S). // Self-stabilizing if both children are self-stabilizing. if (parent->is_intersect() && parent->num_args() == 2) { - euf::snode* lhs = parent->arg(0); - euf::snode* rhs = parent->arg(1); + euf::snode const* lhs = parent->arg(0); + euf::snode const* rhs = parent->arg(1); bool lhs_ss = is_self_stabilizing(lhs) || compute_self_stabilizing(lhs); bool rhs_ss = is_self_stabilizing(rhs) || compute_self_stabilizing(rhs); if (lhs_ss && rhs_ss) { @@ -245,7 +245,7 @@ namespace seq { // D(c, ~R) = ~D(c, R). // Preserves self-stabilizing from R. if (parent->is_complement() && parent->num_args() == 1) { - euf::snode* inner = parent->arg(0); + euf::snode const* inner = parent->arg(0); bool inner_ss = is_self_stabilizing(inner) || compute_self_stabilizing(inner); if (inner_ss) { set_self_stabilizing(deriv); @@ -266,10 +266,10 @@ namespace seq { // Derivative with propagation // ----------------------------------------------------------------------- - euf::snode* seq_regex::derivative_with_propagation(euf::snode* re, euf::snode* elem) { + euf::snode const* seq_regex::derivative_with_propagation(euf::snode const* re, euf::snode const* elem) { if (!re || !elem) return nullptr; - euf::snode* deriv = derivative(re, elem); + euf::snode const* deriv = derivative(re, elem); if (deriv) propagate_self_stabilizing(re, deriv); return deriv; @@ -279,7 +279,7 @@ namespace seq { // Uniform derivative (symbolic character consumption) // ----------------------------------------------------------------------- - euf::snode* seq_regex::try_uniform_derivative(euf::snode* regex) { + euf::snode const* seq_regex::try_uniform_derivative(euf::snode const* regex) const { if (!regex) return nullptr; @@ -303,11 +303,11 @@ namespace seq { // Compute the derivative for each non-empty minterm. If all produce // the same result, the derivative is independent of the character // value and we can consume a symbolic character deterministically. - euf::snode* uniform = nullptr; - for (euf::snode* mt : minterms) { + euf::snode const* uniform = nullptr; + for (euf::snode const* mt : minterms) { if (!mt || mt->is_fail()) continue; // empty character class — no character belongs to it - euf::snode* deriv = m_sg.brzozowski_deriv(regex, mt); + euf::snode const* deriv = m_sg.brzozowski_deriv(regex, mt); if (!deriv) return nullptr; // derivative computation failed if (!uniform) { @@ -323,7 +323,7 @@ namespace seq { // Ground prefix consumption // ----------------------------------------------------------------------- - bool seq_regex::is_empty_regex(euf::snode* re) const { + bool seq_regex::is_empty_regex(euf::snode const* re) const { SASSERT(re); // direct empty language constant if (re->is_fail()) @@ -369,7 +369,7 @@ namespace seq { // BFS regex emptiness check — helper: collect character boundaries // This is faster than computing the actual minterms but probably not minimal // ----------------------------------------------------------------------- - void seq_regex::collect_char_boundaries(euf::snode* re, unsigned_vector& bounds) const { + void seq_regex::collect_char_boundaries(euf::snode const* re, unsigned_vector& bounds) const { SASSERT(re && re->get_expr()); expr* e = re->get_expr(); @@ -425,7 +425,7 @@ namespace seq { // BFS regex emptiness check — helper: alphabet representatives // Faster alternative of computing all min-terms and taking representatives of them // ----------------------------------------------------------------------- - bool seq_regex::get_alphabet_representatives(euf::snode* re, euf::snode_vector& reps) { + bool seq_regex::get_alphabet_representatives(euf::snode const* re, euf::snode_vector& reps) { if (!re || !re->get_expr()) return false; @@ -466,7 +466,7 @@ namespace seq { // NSB review: we have similar functionality in seq_rewriter::some_seq_in_re // currently both these versions only relly work for strings not general sequences - lbool seq_regex::is_empty_bfs(euf::snode* re, unsigned max_states) { + lbool seq_regex::is_empty_bfs(euf::snode const* re, unsigned max_states) { SASSERT(re); const expr* e = re->get_expr(); SASSERT(e); @@ -517,7 +517,7 @@ namespace seq { if (states_explored >= max_states) return l_undef; // also don't cache - euf::snode* current = worklist.back(); + euf::snode const* current = worklist.back(); worklist.pop_back(); ++states_explored; @@ -533,11 +533,11 @@ namespace seq { // Nothing found = dead-end continue; - for (euf::snode* ch : reps) { + for (euf::snode const* ch : reps) { if (!m.inc()) return l_undef; // don't cache // std::cout << "Deriving by " << snode_label_html(ch, sg().get_manager()) << std::endl; - euf::snode* deriv = m_sg.brzozowski_deriv(current, ch); + euf::snode const* deriv = m_sg.brzozowski_deriv(current, ch); SASSERT(deriv); if (is_nullable(deriv)) return cache_and_return(l_false); // found an accepting state @@ -560,7 +560,7 @@ namespace seq { // Mirrors ZIPT NielsenNode.CheckEmptiness (NielsenNode.cs:1429-1469) // ----------------------------------------------------------------------- - lbool seq_regex::check_intersection_emptiness(ptr_vector const& regexes, unsigned max_states) { + lbool seq_regex::check_intersection_emptiness(euf::snode_vector const& regexes, unsigned max_states) { if (regexes.empty()) return l_false; // empty intersection = full language (vacuously non-empty) @@ -569,7 +569,7 @@ namespace seq { if (regexes.size() == 1) return is_empty_bfs(regexes[0], max_states); - euf::snode* result = regexes[0]; + euf::snode const* result = regexes[0]; for (unsigned i = 1; i < regexes.size(); ++i) { expr* r1 = result->get_expr(); expr* r2 = regexes[i]->get_expr(); @@ -587,7 +587,7 @@ namespace seq { // Mirrors ZIPT NielsenNode.IsLanguageSubset (NielsenNode.cs:1382-1385) // ----------------------------------------------------------------------- - lbool seq_regex::is_language_subset(euf::snode* subset_re, euf::snode* superset_re) { + lbool seq_regex::is_language_subset(euf::snode const* subset_re, euf::snode const* superset_re) { if (!subset_re || !superset_re) return l_undef; @@ -601,13 +601,13 @@ namespace seq { // Build complement(superset) expr* sup_expr = superset_re->get_expr(); - euf::snode *comp_sn = m_sg.mk(seq.re.mk_complement(sup_expr)); + euf::snode const* comp_sn = m_sg.mk(seq.re.mk_complement(sup_expr)); // Build intersection and check emptiness // subset ∩ complement(superset) should be empty for subset relation expr* sub_expr = subset_re->get_expr(); auto inter = seq.re.mk_inter(sub_expr, comp_sn->get_expr()); - euf::snode* inter_sn = m_sg.mk(inter); + euf::snode const* inter_sn = m_sg.mk(inter); return is_empty_bfs(inter_sn); } @@ -615,17 +615,17 @@ namespace seq { // Collect primitive regex intersection for a variable // ----------------------------------------------------------------------- - euf::snode* seq_regex::collect_primitive_regex_intersection( - euf::snode* var, nielsen_node const& node, dep_manager& dep_mgr, dep_tracker& dep) const { + euf::snode const* seq_regex::collect_primitive_regex_intersection( + euf::snode const* var, nielsen_node const& node, dep_manager& dep_mgr, dep_tracker& dep) const { SASSERT(var); - euf::snode* result = nullptr; + euf::snode const* result = nullptr; for (auto const& mem : node.str_mems()) { // Primitive constraint: str is a single variable if (!mem.is_primitive()) continue; - euf::snode *first = mem.m_str->first(); + euf::snode const* first = mem.m_str->first(); // NSB review: why is this "first" and not mem.m_str? SASSERT(first); if (first != var) @@ -666,11 +666,11 @@ namespace seq { return simplify_status::ok; while (mem.m_str && !mem.m_str->is_empty()) { - euf::snode* first = mem.m_str->first(); + euf::snode const* first = mem.m_str->first(); if (!first || !first->is_char()) break; - euf::snode* parent_re = mem.m_regex; - euf::snode* deriv = m_sg.brzozowski_deriv(parent_re, first); + euf::snode const* parent_re = mem.m_regex; + euf::snode const* deriv = m_sg.brzozowski_deriv(parent_re, first); if (!deriv) break; if (deriv->is_fail()) @@ -732,7 +732,7 @@ namespace seq { // Minterm computation with filtering // ----------------------------------------------------------------------- - void seq_regex::get_minterms(euf::snode* regex, euf::snode_vector& minterms) { + void seq_regex::get_minterms(euf::snode const* regex, euf::snode_vector& minterms) { if (!regex) return; @@ -744,7 +744,7 @@ namespace seq { // note: minterms are regex character-class expressions, not concrete // characters, so we cannot compute Brzozowski derivatives with them. // callers should compute derivatives using concrete or fresh chars. - for (euf::snode* mt : raw) { + for (euf::snode const* mt : raw) { if (!mt || mt->is_fail()) continue; minterms.push_back(mt); @@ -763,8 +763,8 @@ namespace seq { return is_nullable(mem.m_regex); // consume ground prefix: derive regex by each leading concrete char - seq::str_mem working = mem; - simplify_status st = simplify_ground_prefix(working); + str_mem working = mem; + const simplify_status st = simplify_ground_prefix(working); if (st == simplify_status::conflict) return false; if (st == simplify_status::satisfied) @@ -773,9 +773,9 @@ namespace seq { // after ground prefix consumption, if the front is still a concrete // character we can take one more step (shouldn't happen after // simplify_ground_prefix, but guard defensively) - euf::snode* first = working.m_str->first(); + euf::snode const* first = working.m_str->first(); if (first && first->is_char()) { - seq::str_mem derived = derive(working, first); + const str_mem derived = derive(working, first); if (is_empty_regex(derived.m_regex)) return false; out_mems.push_back(derived); @@ -793,7 +793,7 @@ namespace seq { // History recording // ----------------------------------------------------------------------- - seq::str_mem seq_regex::record_history(seq::str_mem const& mem, euf::snode* history_re) { + seq::str_mem seq_regex::record_history(seq::str_mem const& mem, euf::snode const* history_re) { return str_mem(mem.m_str, mem.m_regex, mem.m_dep); } @@ -802,15 +802,15 @@ namespace seq { // Cycle detection // ----------------------------------------------------------------------- - euf::snode* seq_regex::extract_cycle(seq::str_mem const& mem) const { + euf::snode const* seq_regex::extract_cycle(seq::str_mem const& mem) const { #if 0 // Walk the history chain looking for a repeated regex. // A cycle exists when the current regex matches a regex in the history. if (!mem.m_regex || !mem.m_history) return nullptr; - euf::snode* current = mem.m_regex; - euf::snode* hist = mem.m_history; + euf::snode const* current = mem.m_regex; + euf::snode const* hist = mem.m_history; // Walk the history chain up to a bounded depth. // The history is structured as a chain of regex snapshots connected @@ -818,8 +818,8 @@ namespace seq { // and arg(1) is the tail. A leaf (non-concat) is a terminal entry. unsigned bound = 1000; while (hist && bound-- > 0) { - euf::snode* entry = hist; - euf::snode* tail = nullptr; + euf::snode const* entry = hist; + euf::snode const* tail = nullptr; // If the history node is a regex concat, decompose it: // arg(0) is the regex snapshot, arg(1) is the rest of the chain @@ -842,8 +842,8 @@ namespace seq { // Stabilizer from cycle // ----------------------------------------------------------------------- - euf::snode* seq_regex::stabilizer_from_cycle(euf::snode* cycle_regex, - euf::snode* current_regex) { + euf::snode const* seq_regex::stabilizer_from_cycle(euf::snode const* cycle_regex, + euf::snode const* current_regex) { if (!cycle_regex || !current_regex) return nullptr; @@ -856,7 +856,7 @@ namespace seq { // Extract cycle history tokens // ----------------------------------------------------------------------- - euf::snode* seq_regex::extract_cycle_history(seq::str_mem const& current, + euf::snode const* seq_regex::extract_cycle_history(seq::str_mem const& current, seq::str_mem const& ancestor) { // The history is built by simplify_and_init as a left-associative // string concat chain: concat(concat(concat(nil, c1), c2), c3). @@ -869,21 +869,21 @@ namespace seq { // Mirrors ZIPT StrMem.GetFilteredStabilizerStar (StrMem.cs:228-243) // ----------------------------------------------------------------------- - euf::snode* seq_regex::get_filtered_stabilizer_star(euf::snode* re, - euf::snode* excluded_char) { + euf::snode const* seq_regex::get_filtered_stabilizer_star(euf::snode const* re, + euf::snode const* excluded_char) const { if (!re) return nullptr; - ptr_vector const* stabs = get_stabilizers(re); + euf::snode_vector const* stabs = get_stabilizers(re); if (!stabs || stabs->empty()) return nullptr; - euf::snode* filtered_union = nullptr; + euf::snode const* filtered_union = nullptr; - for (euf::snode* s : *stabs) { + for (euf::snode const* s : *stabs) { if (!s) continue; // Keep only stabilizers whose language cannot start with excluded_char - euf::snode* d = m_sg.brzozowski_deriv(s, excluded_char); + euf::snode const* d = m_sg.brzozowski_deriv(s, excluded_char); if (d && d->is_fail()) { if (!filtered_union) { filtered_union = s; @@ -913,8 +913,8 @@ namespace seq { // Mirrors ZIPT StrMem.StabilizerFromCycle (StrMem.cs:163-225) // ----------------------------------------------------------------------- - euf::snode* seq_regex::strengthened_stabilizer(euf::snode* cycle_regex, - euf::snode* cycle_history) { + euf::snode const* seq_regex::strengthened_stabilizer(euf::snode const* cycle_regex, + euf::snode const* cycle_history) { if (!cycle_regex || !cycle_history) return nullptr; @@ -929,14 +929,14 @@ namespace seq { // A sub-cycle is detected when the derivative returns to cycle_regex. svector> sub_cycles; unsigned cycle_start = 0; - euf::snode* current_re = cycle_regex; + euf::snode const* current_re = cycle_regex; for (unsigned i = 0; i < tokens.size(); ++i) { - euf::snode* tok = tokens[i]; + euf::snode const* tok = tokens[i]; if (!tok) return nullptr; - euf::snode* deriv = m_sg.brzozowski_deriv(current_re, tok); + euf::snode const* deriv = m_sg.brzozowski_deriv(current_re, tok); if (!deriv) return nullptr; @@ -961,7 +961,7 @@ namespace seq { // Build a stabilizer body for each sub-cycle. // body = to_re(t0) · [filteredStar(R1, t1)] · to_re(t1) · ... · to_re(t_{n-1}) - euf::snode* overall_union = nullptr; + euf::snode const* overall_union = nullptr; for (auto const& sc : sub_cycles) { unsigned start = sc.first; @@ -969,17 +969,17 @@ namespace seq { if (start >= end) continue; - euf::snode* re_state = cycle_regex; - euf::snode* body = nullptr; + euf::snode const* re_state = cycle_regex; + euf::snode const* body = nullptr; for (unsigned i = start; i < end; ++i) { - euf::snode* tok = tokens[i]; + euf::snode const* tok = tokens[i]; if (!tok) break; // Insert filtered stabilizer star before each token after the first if (i > start) { - euf::snode* filtered = get_filtered_stabilizer_star(re_state, tok); + euf::snode const* filtered = get_filtered_stabilizer_star(re_state, tok); if (filtered) { expr* fe = filtered->get_expr(); if (fe) { @@ -998,7 +998,7 @@ namespace seq { expr_ref unit_str(seq.str.mk_unit(tok_expr), m); expr_ref tok_re(seq.re.mk_to_re(unit_str), m); - euf::snode* tok_re_sn = m_sg.mk(tok_re); + euf::snode const* tok_re_sn = m_sg.mk(tok_re); if (!body) { body = tok_re_sn; @@ -1012,7 +1012,7 @@ namespace seq { } // Advance the regex state - euf::snode* deriv = m_sg.brzozowski_deriv(re_state, tok); + euf::snode const* deriv = m_sg.brzozowski_deriv(re_state, tok); if (!deriv) break; re_state = deriv; @@ -1046,7 +1046,7 @@ namespace seq { SASSERT(mem.m_str && mem.m_regex); // 1. Leading token must be a variable - euf::snode* first = mem.m_str->first(); + euf::snode const* first = mem.m_str->first(); if (!first || !first->is_var()) return false; @@ -1055,16 +1055,16 @@ namespace seq { return false; // 3. Build stabStar = star(union(all stabilizers for this regex)) - euf::snode* stab_union = get_stabilizer_union(mem.m_regex); + euf::snode const* stab_union = get_stabilizer_union(mem.m_regex); if (!stab_union) return false; expr* su_expr = stab_union->get_expr(); expr_ref stab_star(seq.re.mk_star(su_expr), m); - euf::snode* stab_star_sn = m_sg.mk(stab_star); + euf::snode const* stab_star_sn = m_sg.mk(stab_star); // 4. Collect all primitive regex constraints on variable `first` - euf::snode* x_range = collect_primitive_regex_intersection(first, node, dep); + euf::snode const* x_range = collect_primitive_regex_intersection(first, node, dep); if (!x_range) return false; diff --git a/src/smt/seq/seq_regex.h b/src/smt/seq/seq_regex.h index ca7826a1e..7728481e3 100644 --- a/src/smt/seq/seq_regex.h +++ b/src/smt/seq/seq_regex.h @@ -54,7 +54,7 @@ namespace seq { // Maps regex snode id → list of stabilizer snodes. // Each regex may accumulate multiple stabilizers from different // cycle detections. The list is deduplicated by pointer equality. - u_map> m_stabilizers; + u_map m_stabilizers; // Set of regex snode ids that are self-stabilizing, i.e., the // stabilizer for the regex is the regex itself (e.g., r*). @@ -69,12 +69,12 @@ namespace seq { // to_re string literals in a regex. Boundaries partition the // alphabet into equivalence classes where all characters in // the same class produce identical derivatives. - void collect_char_boundaries(euf::snode* re, unsigned_vector& bounds) const; + void collect_char_boundaries(euf::snode const* re, unsigned_vector& bounds) const; // Build a set of representative character snodes, one per // alphabet equivalence class, derived from the boundary points // of the given regex. - bool get_alphabet_representatives(euf::snode* re, euf::snode_vector& reps); + bool get_alphabet_representatives(euf::snode const* re, euf::snode_vector& reps); public: @@ -114,28 +114,28 @@ namespace seq { // Add a stabilizer for a regex. De-duplicates by pointer equality. // Mirrors ZIPT Environment.AddStabilizer (Environment.cs:114-123). - void add_stabilizer(euf::snode* regex, euf::snode* stabilizer); + void add_stabilizer(euf::snode const* regex, euf::snode const* stabilizer); // Get the union of all stabilizers registered for a regex. // Returns a single re.union snode combining all stabilizers, // or nullptr if no stabilizers exist for the regex. // Mirrors ZIPT Environment.GetStabilizerUnion (Environment.cs:125-128). - euf::snode* get_stabilizer_union(euf::snode* regex); + euf::snode const* get_stabilizer_union(euf::snode const* regex); // Check if any stabilizers have been registered for a regex. - bool has_stabilizers(euf::snode* regex) const; + bool has_stabilizers(euf::snode const* regex) const; // Get raw stabilizer list for a regex (read-only). // Returns nullptr if no stabilizers exist. - ptr_vector const* get_stabilizers(euf::snode* regex) const; + euf::snode_vector const* get_stabilizers(euf::snode const* regex) const; // Mark a regex as self-stabilizing (stabilizer == regex itself). // Mirrors ZIPT Environment.SetSelfStabilizing (Environment.cs:143-146). - void set_self_stabilizing(euf::snode* regex); + void set_self_stabilizing(euf::snode const* regex); // Check if a regex is marked as self-stabilizing. // Mirrors ZIPT Environment.IsSelfStabilizing (Environment.cs:134-141). - bool is_self_stabilizing(euf::snode* regex) const; + bool is_self_stabilizing(euf::snode const* regex) const; // ----------------------------------------------------------------- // Self-stabilizing auto-detection and propagation through derivatives @@ -149,7 +149,7 @@ namespace seq { // - ∅ (fail/empty language): no live derivatives, trivially stable. // - Complement of full_seq (~Σ* = ∅): also trivially stable. // Does NOT mark the snode; call set_self_stabilizing to persist. - bool compute_self_stabilizing(euf::snode* regex) const; + bool compute_self_stabilizing(euf::snode const* regex) const; // After computing a derivative of parent, propagate the self- // stabilizing flag to the derivative result if warranted. @@ -162,12 +162,12 @@ namespace seq { // - If parent is R∩S and both are self-stabilizing → derivative is. // - If parent is ~R and R is self-stabilizing → derivative is. // Updates the internal self-stabilizing set for the derivative. - void propagate_self_stabilizing(euf::snode* parent, euf::snode* deriv); + void propagate_self_stabilizing(euf::snode const* parent, euf::snode const* deriv); // Convenience: compute derivative and propagate self-stabilizing flags. // Equivalent to calling derivative() followed by // propagate_self_stabilizing(). - euf::snode* derivative_with_propagation(euf::snode* re, euf::snode* elem); + euf::snode const* derivative_with_propagation(euf::snode const* re, euf::snode const* elem); // ----------------------------------------------------------------- // Basic regex predicates @@ -176,7 +176,7 @@ namespace seq { // check if regex is the empty language (∅ / re.empty). // performs structural analysis beyond is_fail() to detect // derived emptiness (e.g., union of empties, concat with empty). - bool is_empty_regex(euf::snode* re) const; + bool is_empty_regex(euf::snode const* re) const; // BFS emptiness check over the Brzozowski derivative automaton. // Explores reachable derivative states using representative @@ -185,7 +185,7 @@ namespace seq { // l_false — regex is definitely non-empty (found a nullable state) // l_undef — inconclusive (hit exploration bound or failed derivative) // max_states caps the number of explored states to prevent blowup. - lbool is_empty_bfs(euf::snode* re, unsigned max_states = 10000); + lbool is_empty_bfs(euf::snode const* re, unsigned max_states = 10000); // Check emptiness of the intersection of multiple regexes. // Uses BFS over the product of Brzozowski derivative automata. @@ -193,33 +193,33 @@ namespace seq { // l_false — intersection is definitely non-empty // l_undef — inconclusive (hit exploration bound) // Mirrors ZIPT NielsenNode.CheckEmptiness (NielsenNode.cs:1429-1469) - lbool check_intersection_emptiness(ptr_vector const& regexes, unsigned max_states = UINT_MAX); + lbool check_intersection_emptiness(euf::snode_vector const& regexes, unsigned max_states = UINT_MAX); // Check if L(subset_re) ⊆ L(superset_re). // Computed as: subset_re ∩ complement(superset_re) = ∅. // Mirrors ZIPT NielsenNode.IsLanguageSubset (NielsenNode.cs:1382-1385) - lbool is_language_subset(euf::snode* subset_re, euf::snode* superset_re); + lbool is_language_subset(euf::snode const* subset_re, euf::snode const* superset_re); // Collect all primitive regex constraints on variable `var` from // the node's str_mem list and return their intersection as a // single regex snode (using re.inter). // Returns nullptr if no primitive constraints found. - euf::snode* collect_primitive_regex_intersection( - euf::snode* var, nielsen_node const& node, dep_manager& dep_mgr, dep_tracker& dep) const; + euf::snode const* collect_primitive_regex_intersection( + euf::snode const* var, nielsen_node const& node, dep_manager& dep_mgr, dep_tracker& dep) const; // check if regex is the full language (Σ* / re.all) - bool is_full_regex(euf::snode* re) const { + static bool is_full_regex(euf::snode const* re) { return re && re->is_full_seq(); } // check if regex accepts the empty string // (projection-aware: re may contain re.proj operators) - bool is_nullable(euf::snode* re) const { + bool is_nullable(euf::snode const* re) const { return re && m_sg.re_nullable(re) == l_true; } // check if regex is ground (no string variables) - bool is_ground(euf::snode* re) const { + bool is_ground(euf::snode const* re) const { return re && re->is_ground(); } @@ -229,7 +229,7 @@ namespace seq { // compute Brzozowski derivative of regex w.r.t. character element. // returns nullptr on failure. - euf::snode* derivative(euf::snode* re, euf::snode* elem) { + euf::snode const* derivative(euf::snode const* re, euf::snode const* elem) { return m_sg.brzozowski_deriv(re, elem); } @@ -240,17 +240,17 @@ namespace seq { // of symbolic (variable) characters without branching. // Returns the uniform derivative if found, nullptr otherwise. // Mirrors ZIPT's SimplifyCharRegex uniform-derivative fast path. - euf::snode* try_uniform_derivative(euf::snode* regex); + euf::snode const* try_uniform_derivative(euf::snode const* regex) const; // compute derivative of a str_mem constraint: advance past one character. // the string side is shortened by drop_first and the regex is derived. // Propagates self-stabilizing flags from the parent regex to the derivative. - str_mem derive(str_mem const& mem, euf::snode* elem) { - euf::snode* parent_re = mem.m_regex; - euf::snode* deriv = m_sg.brzozowski_deriv(parent_re, elem); + str_mem derive(str_mem const& mem, euf::snode const* elem) { + euf::snode const* parent_re = mem.m_regex; + euf::snode const* deriv = m_sg.brzozowski_deriv(parent_re, elem); if (deriv) propagate_self_stabilizing(parent_re, deriv); - euf::snode* new_str = m_sg.drop_first(mem.m_str); + euf::snode const* new_str = m_sg.drop_first(mem.m_str); return str_mem(new_str, deriv, mem.m_dep); } @@ -268,13 +268,13 @@ namespace seq { // - the string becomes empty and regex is nullable (satisfied) // - the string becomes empty and regex is not nullable (conflict) // modifies mem in-place. - simplify_status simplify_ground_prefix(seq::str_mem& mem); + simplify_status simplify_ground_prefix(str_mem& mem); // consume ground characters from the back of mem.m_str by computing // reverse derivatives. modifies mem in-place. // (reverse derivatives require regex reversal; this is a best-effort // simplification that handles the common case of trailing constants.) - simplify_status simplify_ground_suffix(seq::str_mem& mem); + simplify_status simplify_ground_suffix(str_mem& mem); // ----------------------------------------------------------------- // Trivial checks @@ -284,14 +284,14 @@ namespace seq { // returns 1 if satisfied (empty string in nullable regex, or full regex) // returns -1 if conflicting (empty string in non-nullable, or ∅ regex) // returns 0 if undetermined - int check_trivial(seq::str_mem const& mem) const; + int check_trivial(str_mem const& mem) const; // ----------------------------------------------------------------- // Minterm and character computation // ----------------------------------------------------------------- // compute minterms (character class partition) from regex - void compute_minterms(euf::snode* re, euf::snode_vector& minterms) { + void compute_minterms(euf::snode const* re, euf::snode_vector& minterms) { m_sg.compute_minterms(re, minterms); } @@ -299,7 +299,7 @@ namespace seq { // (fail) minterms. Minterms are regex character-class expressions // forming a partition of the alphabet; callers use them to drive // fresh-variable creation in character-split modifiers. - void get_minterms(euf::snode* regex, euf::snode_vector& minterms); + void get_minterms(euf::snode const* regex, euf::snode_vector& minterms); // collect concrete first-position characters from a regex. // extracts characters reachable from to_re leaves and simple ranges. @@ -314,8 +314,8 @@ namespace seq { // for the Nielsen graph to expand via character-split modifiers. // returns false if the constraint is immediately conflicting // (empty string in non-nullable regex, or derivative yields ∅). - bool process_str_mem(seq::str_mem const& mem, - vector& out_mems); + bool process_str_mem(str_mem const& mem, + vector& out_mems); // ----------------------------------------------------------------- // Cycle detection and stabilizers @@ -324,24 +324,24 @@ namespace seq { // record current regex in the derivation history of a str_mem. // the history tracks a chain of (regex, id) pairs for cycle detection. // returns the updated str_mem. - seq::str_mem record_history(seq::str_mem const& mem, euf::snode* history_re); + str_mem record_history(str_mem const& mem, euf::snode const* history_re); // check if the derivation history of mem contains a cycle, i.e., // the same regex id appears twice in the history chain. // if found, returns the cycle entry point regex; nullptr otherwise. - euf::snode* extract_cycle(seq::str_mem const& mem) const; + euf::snode const* extract_cycle(str_mem const& mem) const; // check if the derivation history exhibits a cycle. // returns true when the current regex matches a previously seen regex // in the history chain. used to trigger stabilizer introduction. - bool detect_cycle(seq::str_mem const& mem) const; + bool detect_cycle(str_mem const& mem) const; // compute a Kleene star stabilizer from a cycle. // given the regex at the cycle point and the current regex, // builds r* that over-approximates any number of cycle iterations. // returns nullptr if no stabilizer can be computed. - euf::snode* stabilizer_from_cycle(euf::snode* cycle_regex, - euf::snode* current_regex); + euf::snode const* stabilizer_from_cycle(euf::snode const* cycle_regex, + euf::snode const* current_regex); // Strengthened stabilizer construction with sub-cycle detection. // Replays the consumed character tokens from cycle_history on the @@ -352,8 +352,8 @@ namespace seq { // Returns a union of all sub-cycle stabilizer bodies, or nullptr // if no non-trivial stabilizer can be built. // Mirrors ZIPT StrMem.StabilizerFromCycle (StrMem.cs:163-225). - euf::snode* strengthened_stabilizer(euf::snode* cycle_regex, - euf::snode* cycle_history); + euf::snode const* strengthened_stabilizer(euf::snode const* cycle_regex, + euf::snode const* cycle_history); // Get filtered stabilizer star: for regex state re, retrieve // existing stabilizers, filter out those whose language can @@ -361,15 +361,15 @@ namespace seq { // remaining in star(union(...)). // Returns nullptr (or empty-equivalent) if no valid stabilizers. // Mirrors ZIPT StrMem.GetFilteredStabilizerStar (StrMem.cs:228-243). - euf::snode* get_filtered_stabilizer_star(euf::snode* re, - euf::snode* excluded_char); + euf::snode const* get_filtered_stabilizer_star(euf::snode const* re, + euf::snode const* excluded_char) const; // Extract the cycle portion of a str_mem's history by comparing // the current history with an ancestor's history length. // Returns the sub-sequence of tokens consumed since the ancestor, // or nullptr if the history did not advance. - euf::snode* extract_cycle_history(seq::str_mem const& current, - seq::str_mem const& ancestor); + euf::snode const* extract_cycle_history(str_mem const& current, + str_mem const& ancestor); // try to subsume a str_mem constraint using stabilizer-based // reasoning. Enhanced version: checks if the leading variable's @@ -378,7 +378,7 @@ namespace seq { // Falls back to cycle-based pointer equality check. // returns true if the constraint can be dropped. // Mirrors ZIPT StrMem.TrySubsume (StrMem.cs:354-386). - bool try_subsume(seq::str_mem const& mem, seq::nielsen_node const& node); + bool try_subsume(str_mem const& mem, nielsen_node const& node); }; } diff --git a/src/smt/seq/seq_state.h b/src/smt/seq/seq_state.h index 6831e1591..21962e608 100644 --- a/src/smt/seq/seq_state.h +++ b/src/smt/seq/seq_state.h @@ -29,19 +29,19 @@ namespace smt { struct tracked_str_eq : seq::str_eq { enode *m_l, *m_r; - tracked_str_eq(euf::snode *lhs, euf::snode *rhs, enode *l, enode *r, seq::dep_tracker const &dep) + tracked_str_eq(euf::snode const* lhs, euf::snode const* rhs, enode* l, enode* r, seq::dep_tracker const &dep) : str_eq(lhs, rhs, dep), m_l(l), m_r(r) {} }; struct tracked_str_deq : seq::str_deq { sat::literal lit; - tracked_str_deq(euf::snode *lhs, euf::snode *rhs, const sat::literal lit, seq::dep_tracker const &dep) + tracked_str_deq(euf::snode const* lhs, euf::snode const* rhs, const sat::literal lit, seq::dep_tracker const &dep) : str_deq(lhs, rhs, dep), lit(lit) {} }; struct tracked_str_mem : seq::str_mem { sat::literal lit; - tracked_str_mem(euf::snode *str, euf::snode *regex, const sat::literal lit, seq::dep_tracker const &dep) + tracked_str_mem(euf::snode const* str, euf::snode const* regex, const sat::literal lit, seq::dep_tracker const &dep) : str_mem(str, regex, dep), lit(lit) {} }; diff --git a/src/smt/seq_model.cpp b/src/smt/seq_model.cpp index e2636856d..bbcc914da 100644 --- a/src/smt/seq_model.cpp +++ b/src/smt/seq_model.cpp @@ -51,11 +51,11 @@ namespace smt { class seq_snode_value_proc : public model_value_proc { seq_model& m_owner; enode* m_node; - euf::snode* m_snode; - ptr_vector m_dependencies; + euf::snode const* m_snode; + enode_vector m_dependencies; public: - seq_snode_value_proc(seq_model& owner, enode* node, euf::snode* snode) + seq_snode_value_proc(seq_model& owner, enode* node, euf::snode const* snode) : m_owner(owner), m_node(node), m_snode(snode) { m_owner.collect_dependencies(m_snode, m_dependencies); } @@ -84,7 +84,7 @@ namespace smt { // internalization and one re-created during the Nielsen search), so snode // ids are NOT a reliable key. Expressions are perfectly shared, so their // id is stable across all snodes that denote the same term. - static unsigned var_key(euf::snode* n) { + static unsigned var_key(euf::snode const* n) { return n->first()->get_expr()->get_id(); } @@ -159,7 +159,7 @@ namespace smt { } // look up snode for this expression - euf::snode* sn = m_sg.find(e); + euf::snode const* sn = m_sg.find(e); IF_VERBOSE(2, { verbose_stream() << "nseq mk_value: expr=" << mk_bounded_pp(e, m, 2); if (sn) verbose_stream() << " snode[" << sn->id() << "] kind=" << (int)sn->kind(); @@ -197,7 +197,7 @@ namespace smt { // When a new substitution (s.m_var -> s.m_replacement) is applied, // substitute s.m_var in all existing values, then record the new binding. - vector> bindings; + vector> bindings; for (seq::nielsen_edge* e : sat_path) { for (seq::nielsen_subst const& s : e->subst()) { SASSERT(s.m_var); @@ -226,9 +226,9 @@ namespace smt { } } - void seq_model::collect_dependencies(euf::snode *n, ptr_vector &deps) const { + void seq_model::collect_dependencies(euf::snode const* n, enode_vector &deps) const { uint_set seen; - buffer todo; + buffer todo; todo.push_back(n); while (!todo.empty()) { auto curr = todo.back(); @@ -259,7 +259,7 @@ namespace smt { // when using the dependencies to build a value for n we should // map the values that are passed in to the sub-terms that are listed as dependencies. // sub-terms are under concat, power and unit - euf::snode *replacement = nullptr; + euf::snode const* replacement = nullptr; if (m_var_replacement.find(var_key(curr), replacement)) todo.push_back(replacement); } @@ -278,7 +278,7 @@ namespace smt { // verbose_stream() << "collect " << mk_pp(n->get_expr(), m) << " " << deps.size() << "\n"; } - expr_ref seq_model::snode_to_value(euf::snode *n, ptr_vector const &deps, expr_ref_vector const &values) { + expr_ref seq_model::snode_to_value(euf::snode const* n, enode_vector const &deps, expr_ref_vector const &values) { // var2value: leaf deps keyed by expression ID (populated from `deps`/`values`). // node2value: computed nodes keyed by (snode_id * 2 + is_recursive). // The recursion flag is part of the key because the SAME variable snode @@ -288,12 +288,12 @@ namespace smt { u_map var2value; u_map node2value; // resolve: check leaf deps by expression ID, computed nodes by (snode,recursive) key. - auto resolve = [&](euf::snode* s, expr*& out) -> bool { + auto resolve = [&](euf::snode const* s, expr*& out) -> bool { if (var2value.find(s->get_expr()->get_id(), out)) return true; return node2value.find(s->id(), out); }; - buffer todo; + buffer todo; for (unsigned i = 0; i < deps.size(); ++i) { var2value.insert(deps[i]->get_expr_id(), values[i]); } @@ -355,7 +355,7 @@ namespace smt { continue; // not all arguments processed yet, will retry after children } else if (curr->is_var()) { - euf::snode *replacement = nullptr; + euf::snode const* replacement = nullptr; if (m_var_replacement.find(var_key(curr), replacement)) { // outer variable: its value is the value of its replacement. expr* rv = nullptr; @@ -386,7 +386,7 @@ namespace smt { return expr_ref(result, m); } - expr* seq_model::get_var_value(euf::snode* var) { + expr* seq_model::get_var_value(euf::snode const* var) { SASSERT(var); const unsigned key = var_key(var); expr* val = nullptr; @@ -434,13 +434,13 @@ namespace smt { return val; } - expr* seq_model::mk_fresh_value(euf::snode* var) { + expr* seq_model::mk_fresh_value(euf::snode const* var) { SASSERT(var->get_expr()); SASSERT(m_seq.is_seq(var->get_expr())); auto srt = var->get_expr()->get_sort(); // check if this variable has regex constraints - euf::snode* re = nullptr; + euf::snode const* re = nullptr; unsigned key = var_key(var); if (m_var_regex.find(key, re) && re) { expr* re_expr = re->get_expr(); @@ -517,9 +517,8 @@ namespace smt { return m_seq.str.mk_empty(srt); } - lbool seq_model::projection_witness(euf::snode* re0, expr_ref& witness) { - if (!re0 || !re0->get_expr()) - return l_undef; + lbool seq_model::projection_witness(euf::snode const* re0, expr_ref& witness) const { + SASSERT(re0 && re0->get_expr()); sort* seq_sort = nullptr; if (!m_seq.is_re(re0->get_expr(), seq_sort)) return l_undef; @@ -529,7 +528,7 @@ namespace smt { // accepting when re_nullable (projection-aware) reports nullable. // The regex carries the length intersection (∩ Σ^n), so an accepting // run has exactly the requested length and the search is finite. - vector> work; + vector> work; work.push_back({re0, zstring()}); uint_set visited; visited.insert(re0->id()); @@ -537,7 +536,7 @@ namespace smt { unsigned head = 0; const unsigned MAX_STATES = 100000; while (head < work.size() && head < MAX_STATES) { - euf::snode* st = work[head].first; + euf::snode const* st = work[head].first; zstring w = work[head].second; ++head; @@ -550,8 +549,8 @@ namespace smt { euf::snode_vector mts; m_sg.compute_minterms(st, mts); - for (euf::snode* mt : mts) { - euf::snode* d = m_sg.brzozowski_deriv(st, mt); + for (euf::snode const* mt : mts) { + euf::snode const* d = m_sg.brzozowski_deriv(st, mt); if (!d || d->is_fail()) continue; if (visited.contains(d->id())) @@ -584,7 +583,7 @@ namespace smt { continue; // empty string in nullable regex: already satisfied, no variable to constrain VERIFY(mem.is_primitive()); // everything else should have been eliminated already unsigned id = var_key(mem.m_str); - euf::snode* existing = nullptr; + euf::snode const* existing = nullptr; if (m_var_regex.find(id, existing) && existing) { // intersect with existing constraint: // build re.inter(existing, new_regex) @@ -592,7 +591,7 @@ namespace smt { expr* e2 = mem.m_regex->get_expr(); if (e1 && e2) { expr_ref inter(m_seq.re.mk_inter(e1, e2), m); - euf::snode* inter_sn = m_sg.mk(inter); + euf::snode const* inter_sn = m_sg.mk(inter); SASSERT(inter_sn); m_var_regex.insert(id, inter_sn); } @@ -623,7 +622,7 @@ namespace smt { #if 0 // retained in case we want to reconstruct small power unfoldings. - expr_ref seq_model::snode_to_value(euf::snode* n, expr_ref_vector const& values) { + expr_ref seq_model::snode_to_value(euf::snode const* n, expr_ref_vector const& values) { SASSERT(n); if (n->is_empty()) { sort* srt = n->get_sort(); @@ -659,7 +658,7 @@ namespace smt { } if (n->is_var()) { - euf::snode *replacement = nullptr; + euf::snode const* replacement = nullptr; if (!m_var_replacement.find(n->id(), replacement)) return expr_ref(get_var_value(n), m); return mk_value_with_dependencies(replacement, values); diff --git a/src/smt/seq_model.h b/src/smt/seq_model.h index 8a8e1b5f8..5c028464b 100644 --- a/src/smt/seq_model.h +++ b/src/smt/seq_model.h @@ -61,14 +61,14 @@ namespace smt { // variable assignments extracted from the satisfying Nielsen node. // maps snode id -> expr* (concrete value) u_map m_var_values; - u_map m_var_replacement; + u_map m_var_replacement; // trail for GC protection of generated expressions expr_ref_vector m_trail; // per-variable regex constraints: maps snode id -> intersected regex snode. // collected during init() from the state's str_mem list. - u_map m_var_regex; + u_map m_var_regex; public: seq_model(ast_manager& m, context& ctx, seq_util& seq, @@ -101,21 +101,21 @@ namespace smt { // Returns a concrete Z3 expression. // Optionally uses pre-evaluated model values for // enode dependencies (provided by model_generator). - expr_ref snode_to_value(euf::snode *n, ptr_vector const &nodes, expr_ref_vector const &values); + expr_ref snode_to_value(euf::snode const* n, enode_vector const &nodes, expr_ref_vector const &values); // Collect enode dependencies required to evaluate an snode value. - void collect_dependencies(euf::snode* n, ptr_vector& deps) const; + void collect_dependencies(euf::snode const* n, enode_vector& deps) const; // look up or compute the value for an snode variable. // If no assignment exists, delegates to mk_fresh_value. - expr* get_var_value(euf::snode* var); + expr* get_var_value(euf::snode const* var); // generate a fresh value for a variable, respecting regex // membership constraints. If the variable has associated // regex constraints (collected during init), generates a // witness satisfying the intersection; otherwise falls back // to a plain fresh value from the factory. - expr* mk_fresh_value(euf::snode* var); + expr* mk_fresh_value(euf::snode const* var); // Witness extraction for regexes that contain a projection operator // (re.proj), which the standard seq_rewriter::some_seq_in_re cannot @@ -123,7 +123,7 @@ namespace smt { // derivative automaton (m_sg) for a nullable (accepting) state, // building the accepting word. Returns l_true and sets `witness` // on success. - lbool projection_witness(euf::snode* re, expr_ref& witness); + lbool projection_witness(euf::snode const* re, expr_ref& witness) const; // collect per-variable regex constraints from the state. // For each positive str_mem, records the regex (or intersects diff --git a/src/smt/theory_nseq.cpp b/src/smt/theory_nseq.cpp index c1d0fb6f7..5c82a3df3 100644 --- a/src/smt/theory_nseq.cpp +++ b/src/smt/theory_nseq.cpp @@ -34,13 +34,13 @@ namespace smt { m_rewriter(m), m_arith_value(m), m_egraph(m), - m_sgraph(m, m_egraph), + m_sg(m, m_egraph), m_length_solver(m), m_context_solver(ctx, [this](expr* e1, expr* e2) { m_axioms.diseq_axiom(e1, e2); }), - m_nielsen(m_sgraph, m_length_solver, m_context_solver), + m_nielsen(m_sg, m_length_solver, m_context_solver), m_axioms(m_th_rewriter), - m_regex(m_sgraph), - m_model(m, ctx, m_seq, m_rewriter, m_sgraph), + m_regex(m_sg), + m_model(m, ctx, m_seq, m_rewriter, m_sg), m_relevant_lengths(m) { std::function add_clause = @@ -220,8 +220,8 @@ namespace smt { } if (!m_seq.is_seq(e1)) return; - euf::snode *s1 = get_snode(e1); - euf::snode *s2 = get_snode(e2); + euf::snode const* s1 = get_snode(e1); + euf::snode const* s2 = get_snode(e2); seq::dep_tracker dep = nullptr; ctx.push_trail(restore_vector(m_prop_queue)); m_prop_queue.push_back(eq_item(s1, s2, get_enode(v1), get_enode(v2), dep)); @@ -266,8 +266,8 @@ namespace smt { if (get_fparams().m_nseq_axiomatize_diseq) m_axioms.diseq_axiom(e1, e2); else { - euf::snode *s1 = get_snode(e1); - euf::snode *s2 = get_snode(e2); + euf::snode const* s1 = get_snode(e1); + euf::snode const* s2 = get_snode(e2); const seq::dep_tracker dep = nullptr; ctx.push_trail(restore_vector(m_prop_queue)); const expr_ref eq_expr(m.mk_eq(e1, e2), m); @@ -284,7 +284,7 @@ namespace smt { // Boolean assignment notification // ----------------------------------------------------------------------- - void theory_nseq::assign_eh(bool_var v, bool is_true) { + void theory_nseq::assign_eh(const bool_var v, const bool is_true) { try { expr* e = ctx.bool_var2expr(v); const literal lit(v, !is_true); @@ -292,8 +292,8 @@ namespace smt { expr *s = nullptr, *re = nullptr, *a = nullptr, *b = nullptr; TRACE(seq, tout << (is_true ? "" : "¬") << mk_bounded_pp(e, m, 3) << "\n";); if (m_seq.str.is_in_re(e, s, re)) { - euf::snode* sn_str = get_snode(s); - euf::snode* sn_re = get_snode(re); + euf::snode const* sn_str = get_snode(s); + euf::snode const* sn_re = get_snode(re); const seq::dep_tracker dep = nullptr; if (is_true) { ctx.push_trail(restore_vector(m_prop_queue)); @@ -306,7 +306,7 @@ namespace smt { // so the Nielsen graph sees it uniformly; the original negative literal // is kept in mem_source for conflict reporting. const expr_ref re_compl(m_seq.re.mk_complement(re), m); - euf::snode* sn_re_compl = get_snode(re_compl.get()); + euf::snode const* sn_re_compl = get_snode(re_compl.get()); ctx.push_trail(restore_vector(m_prop_queue)); m_prop_queue.push_back(mem_item(sn_str, sn_re_compl, lit, dep)); m_last_constraint_added = ctx.get_scope_level(); @@ -385,13 +385,13 @@ namespace smt { if (n1->get_root() != n2->get_root()) { const auto v1 = mk_var(n1); const auto v2 = mk_var(n2); - const literal lit(v, false); + const literal l(v, false); ctx.mark_as_relevant(n1); ctx.mark_as_relevant(n2); TRACE(seq, tout << "is-eq " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n"); justification* js = ctx.mk_justification( ext_theory_eq_propagation_justification( - get_id(), ctx, 1, &lit, 0, nullptr, n1, n2)); + get_id(), ctx, 1, &l, 0, nullptr, n1, n2)); ctx.assign_eq(n1, n2, eq_justification(js)); new_eq_eh(v1, v2); } @@ -421,14 +421,14 @@ namespace smt { void theory_nseq::push_scope_eh() { theory::push_scope_eh(); - m_sgraph.push(); + m_sg.push(); } void theory_nseq::pop_scope_eh(unsigned num_scopes) { try { theory::pop_scope_eh(num_scopes); - m_sgraph.pop(num_scopes); + m_sg.pop(num_scopes); // A pop may remove constraints and/or unassign forced Nielsen // literals; conservatively invalidate the cached SAT path. if (m_can_hot_restart && ctx.get_scope_level() - num_scopes < m_last_constraint_added) @@ -506,15 +506,15 @@ namespace smt { if (!mem.m_str->is_empty()) { if (mem.m_str->first()->is_char()) { - euf::snode* re_node = mem.m_regex; - euf::snode* str_node = mem.m_str; + euf::snode const* re_node = mem.m_regex; + euf::snode const* str_node = mem.m_str; do { // eliminate leading character by derivatives; derive by the // CURRENT leading char (str_node->first()), not the original // mem.m_str->first() — otherwise a multi-char prefix is derived // by its first char repeatedly (unsound). - re_node = m_sgraph.brzozowski_deriv(re_node, str_node->first()); - str_node = m_sgraph.drop_first(str_node); + re_node = m_sg.brzozowski_deriv(re_node, str_node->first()); + str_node = m_sg.drop_first(str_node); } while (!str_node->is_empty() && str_node->first()->is_char()); if (re_node->is_fail()) { @@ -532,7 +532,7 @@ namespace smt { } else { // check nullability - if (m_sgraph.re_nullable(mem.m_regex) == l_true) { + if (m_sg.re_nullable(mem.m_regex) == l_true) { // empty string in nullable regex → trivially satisfied m_ignored_mem.insert(mem.lit); ctx.push_trail(insert_map(m_ignored_mem, mem.lit)); @@ -570,7 +570,7 @@ namespace smt { } // empty string in non-nullable regex → conflict - if (mem.m_str->is_empty() && m_sgraph.re_nullable(mem.m_regex) == l_false) { + if (mem.m_str->is_empty() && m_sg.re_nullable(mem.m_regex) == l_false) { literal_vector lits; lits.push_back(mem.lit); set_conflict(lits); @@ -607,25 +607,11 @@ namespace smt { if (get_fparams().m_nseq_regex_factorization_eager && get_fparams().m_nseq_regex_factorization_threshold > 0 && mem.m_str->is_concat()) { - const app* const a = to_app(s); - const unsigned na = a->get_num_args(); - SASSERT(na >= 2); - - const expr_ref head(a->get_arg(0), m); - const expr_ref tail(m_seq.str.mk_concat(na - 1, a->get_args() + 1, s->get_sort()), m); const unsigned threshold = get_fparams().m_nseq_regex_factorization_threshold; + split_set pairs; - if (!m_rewriter.split(mem.m_regex->get_expr(), pairs, threshold)) - // we give up - return; - - //std::cout << "Pairs:\n"; - //for (auto& pair: pairs) { - // std::cout << mk_pp(pair.m_d, m) << " ; " << mk_pp(pair.m_n, m) << std::endl; - //} - - m_rewriter.simplify_split(pairs); + auto [head, tail] = seq::split_membership(mem.m_str, mem.m_regex, m_sg, threshold, pairs); if (pairs.empty()) { // no viable splits @@ -638,24 +624,24 @@ namespace smt { TRACE(seq, tout << "eager regex fact: " << mk_pp(s, m) << " in " << mk_pp(re, m) << " -> " << pairs.size() << " splits\n";); - if (!ctx.e_internalized(head)) - ctx.internalize(head, false); - if (!ctx.e_internalized(tail)) - ctx.internalize(tail, false); + if (!ctx.e_internalized(head->get_expr())) + ctx.internalize(head->get_expr(), false); + if (!ctx.e_internalized(tail->get_expr())) + ctx.internalize(tail->get_expr(), false); // forward direction; mk_literal Tseitin-encodes each conjunction literal_vector lits; lits.push_back(~mem.lit); - //std::cout << "Decomposing into:\n"; + std::cout << "Decomposing into:\n"; for (auto const& sp : pairs) { - expr_ref mem_head(m_seq.re.mk_in_re(head, sp.m_d), m); - expr_ref mem_tail(m_seq.re.mk_in_re(tail, sp.m_n), m); + expr_ref mem_head(m_seq.re.mk_in_re(head->get_expr(), sp.m_d), m); + expr_ref mem_tail(m_seq.re.mk_in_re(tail->get_expr(), sp.m_n), m); expr_ref conj(m.mk_and(mem_head, mem_tail), m); lits.push_back(mk_literal(conj)); - //seq::dep_tracker dep = nullptr; - //std::cout << seq::mem_pp(seq::str_mem(m_sgraph.mk(head), m_sgraph.mk(sp.m_d), dep), m) << " && " << seq::mem_pp(seq::str_mem(m_sgraph.mk(tail), m_sgraph.mk(sp.m_n), dep), m) << "\n"; + seq::dep_tracker dep = nullptr; + std::cout << seq::mem_pp(seq::str_mem(head, m_sg.mk(sp.m_d), dep), m) << " && " << seq::mem_pp(seq::str_mem(tail, m_sg.mk(sp.m_n), dep), m) << "\n"; } - //std::cout << std::endl; + std::cout << std::endl; ctx.mk_th_axiom(get_id(), lits.size(), lits.data()); m_ignored_mem.insert(mem.lit); ctx.push_trail(insert_map(m_ignored_mem, mem.lit)); @@ -1439,8 +1425,8 @@ namespace smt { // Helpers // ----------------------------------------------------------------------- - euf::snode* theory_nseq::get_snode(expr* e) { - return m_sgraph.mk(e); + euf::snode const* theory_nseq::get_snode(expr* e) { + return m_sg.mk(e); } // ----------------------------------------------------------------------- @@ -1619,9 +1605,9 @@ namespace smt { // Check intersection emptiness for each variable. for (auto &[var_id, mem_indices] : var_to_mems) { - ptr_vector regexes; - for (unsigned i : mem_indices) { - euf::snode* re = mems[i]->m_regex; + euf::snode_vector regexes; + for (const unsigned i : mem_indices) { + euf::snode const* re = mems[i]->m_regex; SASSERT(re); regexes.push_back(re); } @@ -1828,11 +1814,11 @@ namespace smt { SASSERT(!var_to_mems.empty()); - for (expr *len_expr : m_relevant_lengths) { - expr *s = nullptr; + for (expr* len_expr : m_relevant_lengths) { + expr* s = nullptr; VERIFY(m_seq.str.is_length(len_expr, s)); - euf::snode *s_node = m_sgraph.find(s); + euf::snode const* s_node = m_sg.find(s); SASSERT(s_node); unsigned var_id = s_node->id(); @@ -1850,7 +1836,7 @@ namespace smt { const unsigned l = val_l.get_unsigned(); unsigned_vector const &mem_indices = var_to_mems[var_id]; - ptr_vector regexes; + euf::snode_vector regexes; bool has_projection = false; for (auto i : mem_indices) { SASSERT(mems[i].well_formed()); @@ -1873,18 +1859,18 @@ namespace smt { SASSERT(!regexes.empty()); sort *ele_sort; - VERIFY(m_seq.is_seq(m_sgraph.get_str_sort(), ele_sort)); + VERIFY(m_seq.is_seq(m_sg.get_str_sort(), ele_sort)); unsigned g = 1; if (m_gradient_cache.contains(s)) g = m_gradient_cache[s]; else m_gradient_cache.insert(s, 1); - expr_ref allchar(m_seq.re.mk_full_char(m_seq.re.mk_re(m_sgraph.get_str_sort())), m); + expr_ref allchar(m_seq.re.mk_full_char(m_seq.re.mk_re(m_sg.get_str_sort())), m); expr_ref l_expr(m_autil.mk_int(l), m); expr_ref loop_l(m_seq.re.mk_loop_proper(allchar.get(), l, l), m); - euf::snode *sigmal_node = get_snode(loop_l.get()); + euf::snode const* sigmal_node = get_snode(loop_l.get()); regexes.push_back(sigmal_node); SASSERT(regexes.size() > 1); @@ -1899,7 +1885,7 @@ namespace smt { expr_ref star_g(m_seq.re.mk_star(loop_g.get()), m); expr_ref sigmal_g_expr(m_seq.re.mk_concat(loop_l.get(), star_g.get()), m); - euf::snode *sigmal_g_node = get_snode(sigmal_g_expr.get()); + euf::snode const* sigmal_g_node = get_snode(sigmal_g_expr.get()); regexes.push_back(sigmal_g_node); lbool result_g = m_regex.check_intersection_emptiness(regexes); diff --git a/src/smt/theory_nseq.h b/src/smt/theory_nseq.h index 5102711e5..d52d9fe6d 100644 --- a/src/smt/theory_nseq.h +++ b/src/smt/theory_nseq.h @@ -41,7 +41,7 @@ namespace smt { seq_rewriter m_rewriter; arith_value m_arith_value; euf::egraph m_egraph; // private egraph (not shared with smt context) - euf::sgraph m_sgraph; // private sgraph + euf::sgraph m_sg; // private sgraph // m_context_solver must be declared before m_nielsen: its address is passed // to the m_nielsen constructor and must remain stable for the object's lifetime. sub_solver m_length_solver; @@ -147,7 +147,7 @@ namespace smt { } void set_propagate(enode_pair_vector const &eqs, literal_vector const &lits, literal p); bool add_nielsen_assumptions(); - euf::snode* get_snode(expr* e); + euf::snode const* get_snode(expr* e); // propagation dispatch helpers void propagate_eq(tracked_str_eq const& eq) const; diff --git a/src/test/euf_seq_plugin.cpp b/src/test/euf_seq_plugin.cpp index 0c71beba9..1a6ff1292 100644 --- a/src/test/euf_seq_plugin.cpp +++ b/src/test/euf_seq_plugin.cpp @@ -19,8 +19,9 @@ static euf::enode* get_node(euf::egraph& g, seq_util& seq, expr* e) { if (n) return n; euf::enode_vector args; if (is_app(e)) - for (expr* arg : *to_app(e)) + for (expr* arg : *to_app(e)) { args.push_back(get_node(g, seq, arg)); + } n = g.mk(e, 0, args.size(), args.data()); if (seq.is_seq(e) || seq.is_re(e)) g.add_th_var(n, ++s_var, seq.get_family_id()); @@ -42,20 +43,20 @@ static void test_sgraph_basic() { expr_ref empty(seq.str.mk_empty(str_sort), m); expr_ref xy(seq.str.mk_concat(x, y), m); - euf::snode* sx = sg.mk(x); + euf::snode const* sx = sg.mk(x); SASSERT(sx); SASSERT(sx->is_var()); SASSERT(!sx->is_ground()); SASSERT(sx->is_regex_free()); SASSERT(sx->length() == 1); - euf::snode* se = sg.mk(empty); + euf::snode const* se = sg.mk(empty); SASSERT(se); SASSERT(se->is_empty()); SASSERT(se->is_ground()); SASSERT(se->length() == 0); - euf::snode* sxy = sg.mk(xy); + euf::snode const* sxy = sg.mk(xy); SASSERT(sxy); SASSERT(sxy->is_concat()); SASSERT(!sxy->is_ground()); @@ -169,7 +170,7 @@ static void test_seq_plugin_star_merge() { // register in sgraph sg.mk(star_star); - euf::snode* s = sg.find(star_x); + euf::snode const* s = sg.find(star_x); SASSERT(s && s->is_star()); std::cout << g << "\n"; diff --git a/src/test/euf_sgraph.cpp b/src/test/euf_sgraph.cpp index 9a2dbf056..39e02d313 100644 --- a/src/test/euf_sgraph.cpp +++ b/src/test/euf_sgraph.cpp @@ -29,12 +29,12 @@ static void test_sgraph_classify() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); - sort_ref str_sort(seq.str.mk_string_sort(), m); + const seq_util seq(m); + const sort_ref str_sort(seq.str.mk_string_sort(), m); // string variable - expr_ref x(m.mk_const("x", str_sort), m); - euf::snode* sx = sg.mk(x); + const expr_ref x(m.mk_const("x", str_sort), m); + euf::snode const* sx = sg.mk(x); SASSERT(sx && sx->is_var()); SASSERT(!sx->is_ground()); SASSERT(sx->is_regex_free()); @@ -43,8 +43,8 @@ static void test_sgraph_classify() { SASSERT(sx->is_token()); // empty string - expr_ref empty(seq.str.mk_empty(str_sort), m); - euf::snode* se = sg.mk(empty); + const expr_ref empty(seq.str.mk_empty(str_sort), m); + euf::snode const* se = sg.mk(empty); SASSERT(se && se->is_empty()); SASSERT(se->is_ground()); SASSERT(se->level() == 0); @@ -52,9 +52,9 @@ static void test_sgraph_classify() { SASSERT(!se->is_token()); // character unit with literal char - expr_ref ch(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch), m); - euf::snode* sca = sg.mk(unit_a); + const expr_ref ch(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch), m); + euf::snode const* sca = sg.mk(unit_a); SASSERT(sca && sca->is_char()); SASSERT(sca->is_ground()); SASSERT(sca->level() == 1); @@ -62,9 +62,9 @@ static void test_sgraph_classify() { SASSERT(sca->is_token()); // concat of two variables - expr_ref y(m.mk_const("y", str_sort), m); - expr_ref xy(seq.str.mk_concat(x, y), m); - euf::snode* sxy = sg.mk(xy); + const expr_ref y(m.mk_const("y", str_sort), m); + const expr_ref xy(seq.str.mk_concat(x, y), m); + euf::snode const* sxy = sg.mk(xy); SASSERT(sxy && sxy->is_concat()); SASSERT(!sxy->is_ground()); SASSERT(sxy->is_regex_free()); @@ -85,60 +85,60 @@ static void test_sgraph_regex() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - sort_ref str_sort(seq.str.mk_string_sort(), m); + const sort_ref str_sort(seq.str.mk_string_sort(), m); - expr_ref x(m.mk_const("x", str_sort), m); + const expr_ref x(m.mk_const("x", str_sort), m); // to_re - expr_ref to_re_x(seq.re.mk_to_re(x), m); - euf::snode* str = sg.mk(to_re_x); + const expr_ref to_re_x(seq.re.mk_to_re(x), m); + euf::snode const* str = sg.mk(to_re_x); SASSERT(str && str->is_to_re()); SASSERT(!str->is_regex_free()); SASSERT(str->num_args() == 1); // star - expr_ref star_x(seq.re.mk_star(to_re_x), m); - euf::snode* ss = sg.mk(star_x); + const expr_ref star_x(seq.re.mk_star(to_re_x), m); + euf::snode const* ss = sg.mk(star_x); SASSERT(ss && ss->is_star()); SASSERT(!ss->is_regex_free()); SASSERT(ss->num_args() == 1); // full_seq (.*) - expr_ref full_seq(seq.re.mk_full_seq(str_sort), m); - euf::snode* sfs = sg.mk(full_seq); + const expr_ref full_seq(seq.re.mk_full_seq(str_sort), m); + euf::snode const* sfs = sg.mk(full_seq); SASSERT(sfs && sfs->is_full_seq()); SASSERT(sfs->is_ground()); // full_char (.) - expr_ref full_char(seq.re.mk_full_char(str_sort), m); - euf::snode* sfc = sg.mk(full_char); + const expr_ref full_char(seq.re.mk_full_char(str_sort), m); + euf::snode const* sfc = sg.mk(full_char); SASSERT(sfc && sfc->is_full_char()); SASSERT(sfc->is_ground()); // empty set, fail - sort_ref re_sort(seq.re.mk_re(str_sort), m); - expr_ref empty_set(seq.re.mk_empty(re_sort), m); - euf::snode* sfail = sg.mk(empty_set); + const sort_ref re_sort(seq.re.mk_re(str_sort), m); + const expr_ref empty_set(seq.re.mk_empty(re_sort), m); + euf::snode const* sfail = sg.mk(empty_set); SASSERT(sfail && sfail->is_fail()); // union: to_re(x) | star(to_re(x)), nullable because star is - expr_ref re_union(seq.re.mk_union(to_re_x, star_x), m); - euf::snode* su = sg.mk(re_union); + const expr_ref re_union(seq.re.mk_union(to_re_x, star_x), m); + euf::snode const* su = sg.mk(re_union); SASSERT(su && su->is_union()); // intersection: to_re(x) & star(to_re(x)), nullable only if both are - expr_ref re_inter(seq.re.mk_inter(to_re_x, star_x), m); - euf::snode* si = sg.mk(re_inter); + const expr_ref re_inter(seq.re.mk_inter(to_re_x, star_x), m); + euf::snode const* si = sg.mk(re_inter); SASSERT(si && si->is_intersect()); // complement of to_re(x): nullable because to_re(x) is not nullable - expr_ref re_comp(seq.re.mk_complement(to_re_x), m); - euf::snode* sc = sg.mk(re_comp); + const expr_ref re_comp(seq.re.mk_complement(to_re_x), m); + euf::snode const* sc = sg.mk(re_comp); SASSERT(sc && sc->is_complement()); // in_re - expr_ref in_re(seq.re.mk_in_re(x, star_x), m); - euf::snode* sir = sg.mk(in_re); + const expr_ref in_re(seq.re.mk_in_re(x, star_x), m); + euf::snode const* sir = sg.mk(in_re); SASSERT(sir && sir->is_in_re()); SASSERT(!sir->is_regex_free()); @@ -160,7 +160,7 @@ static void test_sgraph_power() { expr_ref n(arith.mk_int(3), m); expr_ref xn(seq.str.mk_power(x, n), m); - euf::snode* sp = sg.mk(xn); + euf::snode const* sp = sg.mk(xn); SASSERT(sp && sp->is_power()); SASSERT(!sp->is_ground()); // base x is not ground SASSERT(sp->is_regex_free()); @@ -258,8 +258,8 @@ static void test_sgraph_find_idempotent() { sort_ref str_sort(seq.str.mk_string_sort(), m); expr_ref x(m.mk_const("x", str_sort), m); - euf::snode* s1 = sg.mk(x); - euf::snode* s2 = sg.mk(x); // calling mk again returns same node + euf::snode const* s1 = sg.mk(x); + euf::snode const* s2 = sg.mk(x); // calling mk again returns same node SASSERT(s1 == s2); SASSERT(s1 == sg.find(x)); } @@ -278,9 +278,9 @@ static void test_sgraph_mk_concat() { expr_ref y(m.mk_const("y", str_sort), m); expr_ref empty(seq.str.mk_empty(str_sort), m); - euf::snode* sx = sg.mk(x); - euf::snode* sy = sg.mk(y); - euf::snode* se = sg.mk(empty); + euf::snode const* sx = sg.mk(x); + euf::snode const* sy = sg.mk(y); + euf::snode const* se = sg.mk(empty); // concat with empty yields the non-empty side at sgraph level // (empty absorption is a property of the expression, checked via mk) @@ -288,14 +288,14 @@ static void test_sgraph_mk_concat() { // normal concat via expression expr_ref xy(seq.str.mk_concat(x, y), m); - euf::snode* sxy = sg.mk(xy); + euf::snode const* sxy = sg.mk(xy); SASSERT(sxy && sxy->is_concat()); SASSERT(sxy->num_args() == 2); SASSERT(sxy->arg(0) == sx); SASSERT(sxy->arg(1) == sy); // calling mk again with same expr returns same node - euf::snode* sxy2 = sg.mk(xy); + euf::snode const* sxy2 = sg.mk(xy); SASSERT(sxy == sxy2); } @@ -314,14 +314,14 @@ static void test_sgraph_mk_power() { expr_ref n(arith.mk_int(5), m); expr_ref xn(seq.str.mk_power(x, n), m); - euf::snode* sx = sg.mk(x); - euf::snode* sp = sg.mk(xn); + euf::snode const* sx = sg.mk(x); + euf::snode const* sp = sg.mk(xn); SASSERT(sp && sp->is_power()); SASSERT(sp->num_args() == 2); SASSERT(sp->arg(0) == sx); // calling mk again returns same node - euf::snode* sp2 = sg.mk(xn); + euf::snode const* sp2 = sg.mk(xn); SASSERT(sp == sp2); } @@ -339,21 +339,21 @@ static void test_sgraph_first_last() { expr_ref b(m.mk_const("b", str_sort), m); expr_ref c(m.mk_const("c", str_sort), m); - euf::snode* sa = sg.mk(a); - euf::snode* sb = sg.mk(b); - euf::snode* sc = sg.mk(c); + euf::snode const* sa = sg.mk(a); + euf::snode const* sb = sg.mk(b); + euf::snode const* sc = sg.mk(c); // concat(concat(a,b),c): first=a, last=c expr_ref ab(seq.str.mk_concat(a, b), m); expr_ref ab_c(seq.str.mk_concat(ab, c), m); - euf::snode* sab_c = sg.mk(ab_c); + euf::snode const* sab_c = sg.mk(ab_c); SASSERT(sab_c->first() == sa); SASSERT(sab_c->last() == sc); // concat(a,concat(b,c)): first=a, last=c expr_ref bc(seq.str.mk_concat(b, c), m); expr_ref a_bc(seq.str.mk_concat(a, bc), m); - euf::snode* sa_bc = sg.mk(a_bc); + euf::snode const* sa_bc = sg.mk(a_bc); SASSERT(sa_bc->first() == sa); SASSERT(sa_bc->last() == sc); @@ -378,13 +378,13 @@ static void test_sgraph_concat_metadata() { expr_ref ch(seq.str.mk_char('Z'), m); expr_ref unit_z(seq.str.mk_unit(ch), m); - euf::snode* sx = sg.mk(x); - euf::snode* se = sg.mk(empty); - euf::snode* sz = sg.mk(unit_z); + euf::snode const* sx = sg.mk(x); + euf::snode const* se = sg.mk(empty); + euf::snode const* sz = sg.mk(unit_z); // concat(x, unit('Z')): not ground (x is variable), regex_free, not nullable expr_ref xz(seq.str.mk_concat(x, unit_z), m); - euf::snode* sxz = sg.mk(xz); + euf::snode const* sxz = sg.mk(xz); SASSERT(!sxz->is_ground()); SASSERT(sxz->is_regex_free()); SASSERT(sxz->length() == 2); @@ -392,14 +392,14 @@ static void test_sgraph_concat_metadata() { // concat(empty, empty): nullable (both empty) expr_ref empty2(seq.str.mk_concat(empty, empty), m); - euf::snode* see = sg.mk(empty2); + euf::snode const* see = sg.mk(empty2); SASSERT(see->is_ground()); SASSERT(see->length() == 0); // deep chain: concat(concat(x,x),concat(x,x)) has level 3, length 4 expr_ref xx(seq.str.mk_concat(x, x), m); expr_ref xxxx(seq.str.mk_concat(xx, xx), m); - euf::snode* sxxxx = sg.mk(xxxx); + euf::snode const* sxxxx = sg.mk(xxxx); SASSERT(sxxxx->level() == 3); SASSERT(sxxxx->length() == 4); } @@ -437,40 +437,40 @@ static void test_sgraph_factory() { seq_util seq(m); // mk_var - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); SASSERT(x && x->is_var()); SASSERT(!x->is_ground()); SASSERT(x->length() == 1); // mk_char - euf::snode* a = sg.mk_char('A'); + euf::snode const* a = sg.mk_char('A'); SASSERT(a && a->is_char()); SASSERT(a->is_ground()); SASSERT(a->length() == 1); // mk_empty - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); SASSERT(e && e->is_empty()); SASSERT(e->length() == 0); // mk_concat with empty absorption - euf::snode* xe = sg.mk_concat(x, e); + euf::snode const* xe = sg.mk_concat(x, e); SASSERT(xe == x); - euf::snode* ex = sg.mk_concat(e, x); + euf::snode const* ex = sg.mk_concat(e, x); SASSERT(ex == x); // mk_concat of two variables - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* xy = sg.mk_concat(x, y); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* xy = sg.mk_concat(x, y); SASSERT(xy && xy->is_concat()); SASSERT(xy->length() == 2); SASSERT(xy->arg(0) == x); SASSERT(xy->arg(1) == y); // mk_concat of multiple characters - euf::snode* b = sg.mk_char('B'); - euf::snode* c = sg.mk_char('C'); - euf::snode* abc = sg.mk_concat(sg.mk_concat(a, b), c); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* c = sg.mk_char('C'); + euf::snode const* abc = sg.mk_concat(sg.mk_concat(a, b), c); SASSERT(abc->length() == 3); SASSERT(abc->is_ground()); SASSERT(abc->first() == a); @@ -486,15 +486,15 @@ static void test_sgraph_indexing() { euf::sgraph sg(m, eg); seq_util seq(m); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* c = sg.mk_char('C'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* c = sg.mk_char('C'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // build concat(concat(a, b), concat(c, x)) => [A, B, C, x] - euf::snode* ab = sg.mk_concat(a, b); - euf::snode* cx = sg.mk_concat(c, x); - euf::snode* abcx = sg.mk_concat(ab, cx); + euf::snode const* ab = sg.mk_concat(a, b); + euf::snode const* cx = sg.mk_concat(c, x); + euf::snode const* abcx = sg.mk_concat(ab, cx); SASSERT(abcx->length() == 4); @@ -519,7 +519,7 @@ static void test_sgraph_indexing() { SASSERT(a->at(1) == nullptr); // empty: at(0) is nullptr - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); SASSERT(e->at(0) == nullptr); euf::snode_vector empty_tokens; e->collect_tokens(empty_tokens); @@ -535,62 +535,62 @@ static void test_sgraph_drop() { euf::sgraph sg(m, eg); seq_util seq(m); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* c = sg.mk_char('C'); - euf::snode* d = sg.mk_char('D'); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* c = sg.mk_char('C'); + euf::snode const* d = sg.mk_char('D'); // build concat(concat(a, b), concat(c, d)) => [A, B, C, D] - euf::snode* ab = sg.mk_concat(a, b); - euf::snode* cd = sg.mk_concat(c, d); - euf::snode* abcd = sg.mk_concat(ab, cd); + euf::snode const* ab = sg.mk_concat(a, b); + euf::snode const* cd = sg.mk_concat(c, d); + euf::snode const* abcd = sg.mk_concat(ab, cd); SASSERT(abcd->length() == 4); // drop_first: [A, B, C, D] => [B, C, D] - euf::snode* bcd = sg.drop_first(abcd); + euf::snode const* bcd = sg.drop_first(abcd); SASSERT(bcd->length() == 3); SASSERT(bcd->first() == b); SASSERT(bcd->last() == d); // drop_last: [A, B, C, D] => [A, B, C] - euf::snode* abc = sg.drop_last(abcd); + euf::snode const* abc = sg.drop_last(abcd); SASSERT(abc->length() == 3); SASSERT(abc->first() == a); SASSERT(abc->last() == c); // drop_left(2): [A, B, C, D] => [C, D] - euf::snode* cd2 = sg.drop_left(abcd, 2); + euf::snode const* cd2 = sg.drop_left(abcd, 2); SASSERT(cd2->length() == 2); SASSERT(cd2->first() == c); // drop_left(1): [A, B, C, D] => [B, C, D] - euf::snode* bcd2 = sg.drop_left(abcd, 1); + euf::snode const* bcd2 = sg.drop_left(abcd, 1); SASSERT(bcd2->length() == 3); SASSERT(bcd2->first() == b); SASSERT(bcd2->last() == d); // drop_right(2): [A, B, C, D] => [A, B] - euf::snode* ab2 = sg.drop_right(abcd, 2); + euf::snode const* ab2 = sg.drop_right(abcd, 2); SASSERT(ab2->length() == 2); SASSERT(ab2->last() == b); // drop_right(1): [A, B, C, D] => [A, B, C] - euf::snode* abc2 = sg.drop_right(abcd, 1); + euf::snode const* abc2 = sg.drop_right(abcd, 1); SASSERT(abc2->length() == 3); SASSERT(abc2->first() == a); SASSERT(abc2->last() == c); // drop all: [A, B, C, D] => empty - euf::snode* empty = sg.drop_left(abcd, 4); + euf::snode const* empty = sg.drop_left(abcd, 4); SASSERT(empty->is_empty()); // drop from single token: [A] => empty - euf::snode* e = sg.drop_first(a); + euf::snode const* e = sg.drop_first(a); SASSERT(e->is_empty()); // drop from empty: no change - euf::snode* ee = sg.drop_first(sg.mk_empty_seq(seq.str.mk_string_sort())); + euf::snode const* ee = sg.drop_first(sg.mk_empty_seq(seq.str.mk_string_sort())); SASSERT(ee->is_empty()); } @@ -603,29 +603,29 @@ static void test_sgraph_subst() { euf::sgraph sg(m, eg); seq_util seq(m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // concat(x, concat(a, x)) with x -> b gives concat(b, concat(a, b)) - euf::snode* ax = sg.mk_concat(a, x); - euf::snode* xax = sg.mk_concat(x, ax); + euf::snode const* ax = sg.mk_concat(a, x); + euf::snode const* xax = sg.mk_concat(x, ax); SASSERT(xax->length() == 3); - euf::snode* result = sg.subst(xax, x, b); + euf::snode const* result = sg.subst(xax, x, b); SASSERT(result->length() == 3); SASSERT(result->first() == b); SASSERT(result->last() == b); SASSERT(result->at(1) == a); // middle is still 'A' // substitution of non-occurring variable is identity - euf::snode* same = sg.subst(xax, y, b); + euf::snode const* same = sg.subst(xax, y, b); SASSERT(same == xax); // substitution of variable with empty - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - euf::snode* collapsed = sg.subst(xax, x, e); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* collapsed = sg.subst(xax, x, e); SASSERT(collapsed->length() == 1); // just 'a' remains SASSERT(collapsed == a); } @@ -639,15 +639,15 @@ static void test_sgraph_complex_concat() { euf::sgraph sg(m, eg); // build a string "HELLO" = concat(H, concat(E, concat(L, concat(L, O)))) - euf::snode* h = sg.mk_char('H'); - euf::snode* e = sg.mk_char('E'); - euf::snode* l = sg.mk_char('L'); - euf::snode* o = sg.mk_char('O'); + euf::snode const* h = sg.mk_char('H'); + euf::snode const* e = sg.mk_char('E'); + euf::snode const* l = sg.mk_char('L'); + euf::snode const* o = sg.mk_char('O'); - euf::snode* lo = sg.mk_concat(l, o); - euf::snode* llo = sg.mk_concat(l, lo); - euf::snode* ello = sg.mk_concat(e, llo); - euf::snode* hello = sg.mk_concat(h, ello); + euf::snode const* lo = sg.mk_concat(l, o); + euf::snode const* llo = sg.mk_concat(l, lo); + euf::snode const* ello = sg.mk_concat(e, llo); + euf::snode const* hello = sg.mk_concat(h, ello); SASSERT(hello->length() == 5); SASSERT(hello->is_ground()); @@ -662,24 +662,24 @@ static void test_sgraph_complex_concat() { SASSERT(hello->at(4) == o); // drop first 2 from "HELLO" => "LLO" - euf::snode* llo2 = sg.drop_left(hello, 2); + euf::snode const* llo2 = sg.drop_left(hello, 2); SASSERT(llo2->length() == 3); SASSERT(llo2->first() == l); // drop last 3 from "HELLO" => "HE" - euf::snode* he = sg.drop_right(hello, 3); + euf::snode const* he = sg.drop_right(hello, 3); SASSERT(he->length() == 2); SASSERT(he->first() == h); SASSERT(he->last() == e); // mixed variables and characters: concat(x, "AB", y) - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* ab = sg.mk_concat(a, b); - euf::snode* xab = sg.mk_concat(x, ab); - euf::snode* xaby = sg.mk_concat(xab, y); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* ab = sg.mk_concat(a, b); + euf::snode const* xab = sg.mk_concat(x, ab); + euf::snode const* xaby = sg.mk_concat(xab, y); SASSERT(xaby->length() == 4); SASSERT(!xaby->is_ground()); @@ -706,18 +706,18 @@ static void test_sgraph_brzozowski() { expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); expr_ref star_a(seq.re.mk_star(to_re_a), m); - euf::snode* s_star_a = sg.mk(star_a); - euf::snode* s_unit_a = sg.mk(unit_a); + euf::snode const* s_star_a = sg.mk(star_a); + euf::snode const* s_unit_a = sg.mk(unit_a); - euf::snode* deriv = sg.brzozowski_deriv(s_star_a, s_unit_a); + euf::snode const* deriv = sg.brzozowski_deriv(s_star_a, s_unit_a); SASSERT(deriv != nullptr); std::cout << " d/da(a*) kind: " << (int)deriv->kind() << "\n"; // derivative of re.empty w.r.t. 'a' should be re.empty sort_ref re_sort(seq.re.mk_re(str_sort), m); expr_ref re_empty(seq.re.mk_empty(re_sort), m); - euf::snode* s_empty = sg.mk(re_empty); - euf::snode* deriv_empty = sg.brzozowski_deriv(s_empty, s_unit_a); + euf::snode const* s_empty = sg.mk(re_empty); + euf::snode const* deriv_empty = sg.brzozowski_deriv(s_empty, s_unit_a); SASSERT(deriv_empty != nullptr); SASSERT(deriv_empty->is_fail()); // derivative of empty set is empty set std::cout << " d/da(empty) kind: " << (int)deriv_empty->kind() << "\n"; @@ -737,7 +737,7 @@ static void test_sgraph_minterms() { // simple regex with no character predicates: re.all (.*) expr_ref re_all(seq.re.mk_full_seq(str_sort), m); - euf::snode* s_re_all = sg.mk(re_all); + euf::snode const* s_re_all = sg.mk(re_all); euf::snode_vector minterms; sg.compute_minterms(s_re_all, minterms); @@ -749,7 +749,7 @@ static void test_sgraph_minterms() { expr_ref evil(seq.re.mk_to_re(seq.str.mk_string(zstring("evil"))), m); expr_ref slash_evil(seq.re.mk_to_re(seq.str.mk_string(zstring("/evil"))), m); expr_ref union_re(seq.re.mk_union(evil, slash_evil), m); - euf::snode* s_union_re = sg.mk(union_re); + euf::snode const* s_union_re = sg.mk(union_re); euf::snode_vector union_minterms; sg.compute_minterms(s_union_re, union_minterms); diff --git a/src/test/nseq_basic.cpp b/src/test/nseq_basic.cpp index ee3d8167c..7ffae340b 100644 --- a/src/test/nseq_basic.cpp +++ b/src/test/nseq_basic.cpp @@ -41,7 +41,7 @@ static void test_nseq_instantiation() { euf::sgraph sg(m, eg); nseq_basic_dummy_solver solver; seq::context_solver_i context_solver; - seq::nielsen_graph ng(sg, solver, context_solver); + const seq::nielsen_graph ng(sg, solver, context_solver); SASSERT(ng.root() == nullptr); SASSERT(ng.num_nodes() == 0); std::cout << " ok\n"; @@ -50,7 +50,7 @@ static void test_nseq_instantiation() { // Test 2: parameter validation accepts "nseq" static void test_nseq_param_validation() { std::cout << "test_nseq_param_validation\n"; - smt_params p; + const smt_params p; // Should not throw try { p.validate_string_solver(symbol("nseq")); @@ -72,9 +72,9 @@ static void test_nseq_param_validation() { // Test 2b: parameter validation rejects invalid variants of "nseq" static void test_nseq_param_validation_rejects_invalid() { std::cout << "test_nseq_param_validation_rejects_invalid\n"; - smt_params p; + const smt_params p; static const char* invalid_variants[] = { "nseq2", "NSEQ", "nseqq", "nse", "Nseq", "nseq ", "" }; - for (auto s : invalid_variants) { + for (const auto s : invalid_variants) { bool threw = false; try { p.validate_string_solver(symbol(s)); @@ -94,7 +94,7 @@ static void test_nseq_simplification() { std::cout << "test_nseq_simplification\n"; ast_manager m; reg_decl_plugins(m); - seq_util su(m); + const seq_util su(m); euf::egraph eg(m); euf::sgraph sg(m, eg); nseq_basic_dummy_solver solver; @@ -102,12 +102,12 @@ static void test_nseq_simplification() { seq::nielsen_graph ng(sg, solver, context_solver); // Add a trivial equality: empty = empty - euf::snode* empty1 = sg.mk_empty_seq(su.str.mk_string_sort()); - euf::snode* empty2 = sg.mk_empty_seq(su.str.mk_string_sort()); + euf::snode const* empty1 = sg.mk_empty_seq(su.str.mk_string_sort()); + euf::snode const* empty2 = sg.mk_empty_seq(su.str.mk_string_sort()); ng.add_str_eq(empty1, empty2); - seq::nielsen_graph::search_result r = ng.solve(); + const seq::nielsen_graph::search_result r = ng.solve(); // empty = empty is trivially satisfied SASSERT(r == seq::nielsen_graph::search_result::sat); std::cout << " ok: trivial equality solved as sat\n"; @@ -118,7 +118,7 @@ static void test_nseq_node_satisfied() { std::cout << "test_nseq_node_satisfied\n"; ast_manager m; reg_decl_plugins(m); - seq_util su(m); + const seq_util su(m); euf::egraph eg(m); euf::sgraph sg(m, eg); nseq_basic_dummy_solver solver; @@ -130,15 +130,15 @@ static void test_nseq_node_satisfied() { SASSERT(node->is_satisfied()); // add a trivial equality - euf::snode *empty = sg.mk_empty_seq(su.str.mk_string_sort()); - seq::dep_tracker dep = nullptr; - seq::str_eq eq(empty, empty, dep); + const euf::snode *empty = sg.mk_empty_seq(su.str.mk_string_sort()); + const seq::dep_tracker dep = nullptr; + const seq::str_eq eq(empty, empty, dep); node->add_str_eq(eq); SASSERT(node->str_eqs().size() == 1); SASSERT(!node->str_eqs()[0].is_trivial() || node->str_eqs()[0].m_lhs == node->str_eqs()[0].m_rhs); // After simplification, trivial equalities should be removed - ptr_vector cur_path; - seq::simplify_result sr = node->simplify_and_init(cur_path); + const ptr_vector cur_path; + const seq::simplify_result sr = node->simplify_and_init(cur_path); VERIFY(sr == seq::simplify_result::satisfied || sr == seq::simplify_result::proceed); std::cout << " ok\n"; @@ -155,11 +155,11 @@ static void test_nseq_symbol_clash() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('a'); - euf::snode* b = sg.mk_char('b'); + euf::snode const* a = sg.mk_char('a'); + euf::snode const* b = sg.mk_char('b'); ng.add_str_eq(a, b); - auto r = ng.solve(); + const auto r = ng.solve(); SASSERT(r == seq::nielsen_graph::search_result::unsat); // verify conflict explanation returns the equality index @@ -183,10 +183,10 @@ static void test_nseq_var_eq_self() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); ng.add_str_eq(x, x); - auto r = ng.solve(); + const auto r = ng.solve(); SASSERT(r == seq::nielsen_graph::search_result::sat); std::cout << " ok: x = x solved as sat\n"; } @@ -202,14 +202,14 @@ static void test_nseq_prefix_clash() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('a'); - euf::snode* b = sg.mk_char('b'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* xb = sg.mk_concat(x, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('a'); + euf::snode const* b = sg.mk_char('b'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* xb = sg.mk_concat(x, b); ng.add_str_eq(xa, xb); - auto r = ng.solve(); + const auto r = ng.solve(); SASSERT(r == seq::nielsen_graph::search_result::unsat); std::cout << " ok: x·a = x·b detected as unsat\n"; } @@ -225,14 +225,14 @@ static void test_nseq_const_nielsen_solvable() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('a'); - euf::snode* ax = sg.mk_concat(a, x); - euf::snode* ay = sg.mk_concat(a, y); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('a'); + euf::snode const* ax = sg.mk_concat(a, x); + euf::snode const* ay = sg.mk_concat(a, y); ng.add_str_eq(ax, ay); - auto r = ng.solve(); + const auto r = ng.solve(); // a·x = a·y simplifies to x = y which is satisfiable (x = y = ε) SASSERT(r == seq::nielsen_graph::search_result::sat); std::cout << " ok: a·x = a·y solved as sat\n"; @@ -248,12 +248,12 @@ static void test_nseq_length_mismatch() { nseq_basic_dummy_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('a'); - euf::snode* b = sg.mk_char('b'); - euf::snode* ab = sg.mk_concat(a, b); + euf::snode const* a = sg.mk_char('a'); + euf::snode const* b = sg.mk_char('b'); + euf::snode const* ab = sg.mk_concat(a, b); ng.add_str_eq(ab, a); - auto r = ng.solve(); + const auto r = ng.solve(); SASSERT(r == seq::nielsen_graph::search_result::unsat); std::cout << " ok: ab = a detected as unsat\n"; } @@ -270,15 +270,15 @@ static void test_setup_seq_str_dispatches_nseq() { smt::context ctx(m, params); // Assert a string equality to trigger string theory setup during check() - seq_util su(m); + const seq_util su(m); sort* str_sort = su.str.mk_string_sort(); - app_ref x(m.mk_const(symbol("x_setup_test"), str_sort), m); - app_ref eq(m.mk_eq(x.get(), x.get()), m); + const app_ref x(m.mk_const(symbol("x_setup_test"), str_sort), m); + const app_ref eq(m.mk_eq(x.get(), x.get()), m); ctx.assert_expr(eq); ctx.check(); // Verify that theory_nseq (not theory_seq) was registered for the "seq" family - family_id seq_fid = m.mk_family_id("seq"); + const family_id seq_fid = m.mk_family_id("seq"); SASSERT(ctx.get_theory(seq_fid) != nullptr); SASSERT(dynamic_cast(ctx.get_theory(seq_fid)) != nullptr); std::cout << " ok: setup_seq_str dispatched to setup_nseq for 'nseq'\n"; diff --git a/src/test/nseq_zipt.cpp b/src/test/nseq_zipt.cpp index c802122ca..ba4094fba 100644 --- a/src/test/nseq_zipt.cpp +++ b/src/test/nseq_zipt.cpp @@ -60,23 +60,23 @@ public: struct str_builder { euf::sgraph& sg; seq_util& su; - euf::snode* vars[26] = {}; // vars['A'-'A'] .. vars['Z'-'A'] + euf::snode const* vars[26] = {}; // vars['A'-'A'] .. vars['Z'-'A'] str_builder(euf::sgraph& sg, seq_util& su) : sg(sg), su(su) {} - euf::snode* var(char c) { - int idx = c - 'A'; + euf::snode const* var(const char c) { + const int idx = c - 'A'; if (!vars[idx]) vars[idx] = sg.mk_var(symbol(std::string(1, c).c_str()), su.str.mk_string_sort()); return vars[idx]; } - euf::snode* parse(const char* s) { - euf::snode* result = nullptr; + euf::snode const* parse(const char* s) { + euf::snode const* result = nullptr; for (const char* p = s; *p; ++p) { - euf::snode* tok = (*p >= 'A' && *p <= 'Z') + euf::snode const* tok = (*p >= 'A' && *p <= 'Z') ? var(*p) - : sg.mk_char((unsigned)(unsigned char)*p); + : sg.mk_char((unsigned char)*p); result = result ? sg.mk_concat(result, tok) : tok; } return result ? result : sg.mk_empty_seq(su.str.mk_string_sort()); @@ -99,20 +99,20 @@ struct regex_builder { re_sort = su.re.mk_re(su.str.mk_string_sort()); } - euf::snode* parse(const char* s) { + euf::snode const* parse(const char* s) { int pos = 0; - expr_ref e = parse_union(s, pos, (int)strlen(s)); + const expr_ref e = parse_union(s, pos, (int)strlen(s)); SASSERT(pos == (int)strlen(s)); return sg.mk(e.get()); } private: - expr_ref mk_char_re(char c) { - zstring zs(std::string(1, c).c_str()); + expr_ref mk_char_re(const char c) const { + const zstring zs(std::string(1, c).c_str()); return expr_ref(su.re.mk_to_re(su.str.mk_string(zs)), m); } - expr_ref parse_union(const char* s, int& pos, int len) { + expr_ref parse_union(const char* s, int& pos, const int len) { expr_ref left = parse_inter(s, pos, len); while (pos < len && s[pos] == '|') { ++pos; @@ -122,7 +122,7 @@ private: return left; } - expr_ref parse_inter(const char* s, int& pos, int len) { + expr_ref parse_inter(const char* s, int& pos, const int len) { expr_ref left = parse_concat(s, pos, len); while (pos < len && s[pos] == '&') { ++pos; @@ -132,7 +132,7 @@ private: return left; } - expr_ref parse_concat(const char* s, int& pos, int len) { + expr_ref parse_concat(const char* s, int& pos, const int len) { expr_ref acc(m); while (pos < len && s[pos] != ')' && s[pos] != '|' && s[pos] != '&') { expr_ref tok = parse_repeat(s, pos, len); @@ -141,7 +141,7 @@ private: return acc ? acc : expr_ref(su.re.mk_to_re(su.str.mk_string(zstring(""))), m); } - expr_ref parse_repeat(const char* s, int& pos, int len) { + expr_ref parse_repeat(const char* s, int& pos, const int len) { // collect leading tildes (complement) int tildes = 0; while (pos < len && s[pos] == '~') { ++tildes; ++pos; } @@ -160,10 +160,10 @@ private: return base; } - expr_ref parse_primary(const char* s, int& pos, int len) { + expr_ref parse_primary(const char* s, int& pos, const int len) { if (pos >= len) return expr_ref(su.re.mk_to_re(su.str.mk_string(zstring(""))), m); - char c = s[pos]; + const char c = s[pos]; if (c == '(') { ++pos; expr_ref inner = parse_union(s, pos, len); @@ -197,8 +197,8 @@ struct nseq_fixture { : eg(init(m)), sg(m, eg), dummy_solver(), context_solver(), ng(sg, dummy_solver, context_solver), su(m), sb(sg, su), rb(m, su, sg) {} - euf::snode* S(const char* s) { return sb.parse(s); } - euf::snode* R(const char* s) { return rb.parse(s); } + euf::snode const* S(const char* s) { return sb.parse(s); } + euf::snode const* R(const char* s) { return rb.parse(s); } }; static constexpr int TEST_TIMEOUT_SEC = 2; diff --git a/src/test/seq_nielsen.cpp b/src/test/seq_nielsen.cpp index e00db29cb..6b82bd0d9 100644 --- a/src/test/seq_nielsen.cpp +++ b/src/test/seq_nielsen.cpp @@ -42,26 +42,26 @@ static void test_dep_tracker() { seq::dep_manager dm; // empty tracker - seq::dep_tracker d0 = dm.mk_empty(); + const seq::dep_tracker d0 = dm.mk_empty(); SASSERT(d0 == nullptr); // tracker with one leaf (using sat::literal) - seq::dep_tracker d1 = dm.mk_leaf(sat::literal(3)); + const seq::dep_tracker d1 = dm.mk_leaf(sat::literal(3)); SASSERT(d1 != nullptr); // tracker with another leaf (using sat::literal) - seq::dep_tracker d2 = dm.mk_leaf(sat::literal(5)); + const seq::dep_tracker d2 = dm.mk_leaf(sat::literal(5)); SASSERT(d2 != nullptr); // merge - seq::dep_tracker d3 = dm.mk_join(d1, d2); + const seq::dep_tracker d3 = dm.mk_join(d1, d2); SASSERT(d3 != nullptr); SASSERT(dm.contains(d3, sat::literal(3))); SASSERT(dm.contains(d3, sat::literal(5))); SASSERT(!dm.contains(d1, sat::literal(5))); // another leaf with same value as d1 - seq::dep_tracker d4 = dm.mk_leaf(sat::literal(3)); + const seq::dep_tracker d4 = dm.mk_leaf(sat::literal(3)); SASSERT(dm.contains(d4, sat::literal(3))); SASSERT(!dm.contains(d4, sat::literal(5))); } @@ -73,27 +73,27 @@ static void test_str_eq() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; // basic equality - seq::str_eq eq1(x, y, dep); + const seq::str_eq eq1(x, y, dep); SASSERT(eq1.contains_var(x)); SASSERT(eq1.contains_var(y)); SASSERT(!eq1.contains_var(a)); // trivial equality: same node - seq::str_eq eq2(x, x, dep); + const seq::str_eq eq2(x, x, dep); SASSERT(eq2.is_trivial()); // trivial equality: both empty - seq::str_eq eq3(e, e, dep); + const seq::str_eq eq3(e, e, dep); SASSERT(eq3.is_trivial()); // sorting: lower id first @@ -102,8 +102,8 @@ static void test_str_eq() { SASSERT(eq4.m_lhs->id() <= eq4.m_rhs->id()); // contains_var with concat - euf::snode* xa = sg.mk_concat(x, a); - seq::str_eq eq5(xa, y, dep); + euf::snode const* xa = sg.mk_concat(x, a); + const seq::str_eq eq5(xa, y, dep); SASSERT(eq5.contains_var(x)); SASSERT(eq5.contains_var(y)); SASSERT(!eq5.contains_var(e)); @@ -117,26 +117,26 @@ static void test_str_mem() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - sort_ref str_sort(seq.str.mk_string_sort(), m); + const sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); // create a regex: re.all (.*) - expr_ref star_fc(seq.re.mk_full_seq(str_sort), m); - euf::snode* regex = sg.mk(star_fc); + const expr_ref star_fc(seq.re.mk_full_seq(str_sort), m); + euf::snode const* regex = sg.mk(star_fc); - seq::dep_tracker dep = nullptr; - seq::str_mem mem(x, regex, dep); + const seq::dep_tracker dep = nullptr; + const seq::str_mem mem(x, regex, dep); // x in regex is primitive (x is a single variable) SASSERT(mem.is_primitive()); SASSERT(mem.contains_var(x)); // concatenation is not primitive - euf::snode* a = sg.mk_char('A'); - euf::snode* xa = sg.mk_concat(x, a); - seq::str_mem mem2(xa, regex, dep); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* xa = sg.mk_concat(x, a); + const seq::str_mem mem2(xa, regex, dep); SASSERT(!mem2.is_primitive()); SASSERT(mem2.contains_var(x)); } @@ -148,28 +148,28 @@ static void test_nielsen_subst() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - seq::dep_tracker dep = nullptr; - seq::nielsen_subst s1(x, a, dep); + const seq::dep_tracker dep = nullptr; + const seq::nielsen_subst s1(x, a, dep); SASSERT(s1.is_eliminating()); // eliminating substitution: x -> empty - seq::nielsen_subst s2(x, e, dep); + const seq::nielsen_subst s2(x, e, dep); SASSERT(s2.is_eliminating()); // non-eliminating substitution: x -> concat(A, x) - euf::snode* ax = sg.mk_concat(a, x); - seq::nielsen_subst s3(x, ax, dep); + euf::snode const* ax = sg.mk_concat(a, x); + const seq::nielsen_subst s3(x, ax, dep); SASSERT(!s3.is_eliminating()); // eliminating substitution: x -> y (x not in y) - seq::nielsen_subst s4(x, y, dep); + const seq::nielsen_subst s4(x, y, dep); SASSERT(s4.is_eliminating()); } @@ -181,15 +181,15 @@ static void test_nielsen_node() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - sort_ref str_sort(seq.str.mk_string_sort(), m); + const sort_ref str_sort(seq.str.mk_string_sort(), m); dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); seq::nielsen_node* root = ng.mk_node(); SASSERT(root->id() == 0); @@ -199,15 +199,15 @@ static void test_nielsen_node() { SASSERT(root->reason() == seq::backtrack_reason::unevaluated); // add constraints - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; root->add_str_eq(seq::str_eq(x, y, dep)); root->add_str_eq(seq::str_eq(sg.mk_concat(x, a), sg.mk_concat(a, y), dep)); SASSERT(root->str_eqs().size() == 2); // regex membership - expr_ref re_all(seq.re.mk_full_seq(str_sort), m); - euf::snode* regex = sg.mk(re_all); - euf::snode* empty = sg.mk_empty_seq(seq.str.mk_string_sort()); + const expr_ref re_all(seq.re.mk_full_seq(str_sort), m); + euf::snode const* regex = sg.mk(re_all); + euf::snode const* empty = sg.mk_empty_seq(seq.str.mk_string_sort()); root->add_str_mem(seq::str_mem(x, regex, dep)); SASSERT(root->str_mems().size() == 1); @@ -231,13 +231,13 @@ static void test_nielsen_edge() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); // create parent and child nodes seq::nielsen_node* parent = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; parent->add_str_eq(seq::str_eq(x, y, dep)); seq::nielsen_node* child = ng.mk_child(parent); @@ -262,15 +262,15 @@ static void test_nielsen_graph_populate() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - sort_ref str_sort(seq.str.mk_string_sort(), m); + const sort_ref str_sort(seq.str.mk_string_sort(), m); dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); // add string equality: x = y ng.add_str_eq(x, y); @@ -279,14 +279,14 @@ static void test_nielsen_graph_populate() { SASSERT(ng.num_nodes() == 1); // add regex membership: x in .* - expr_ref re_all(seq.re.mk_full_seq(str_sort), m); - euf::snode* regex = sg.mk(re_all); + const expr_ref re_all(seq.re.mk_full_seq(str_sort), m); + euf::snode const* regex = sg.mk(re_all); ng.add_str_mem(x, regex); SASSERT(ng.root()->str_mems().size() == 1); // add another equality: concat(x, A) = concat(A, y) - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* ay = sg.mk_concat(a, y); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* ay = sg.mk_concat(a, y); ng.add_str_eq(xa, ay); SASSERT(ng.root()->str_eqs().size() == 2); @@ -301,28 +301,28 @@ static void test_nielsen_subst_apply() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); sort_ref str_sort(seq.str.mk_string_sort(), m); dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); // create node with constraint: concat(x, A) = concat(B, y) seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* by = sg.mk_concat(b, y); + const seq::dep_tracker dep = nullptr; + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* by = sg.mk_concat(b, y); node->add_str_eq(seq::str_eq(xa, by, dep)); // apply substitution x -> empty - seq::nielsen_subst s(x, e, dep); + const seq::nielsen_subst s(x, e, dep); node->apply_subst(sg, s); // after x -> empty: lhs should be just A, rhs still concat(B, y) @@ -344,8 +344,8 @@ static void test_nielsen_graph_reset() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); ng.add_str_eq(x, y); SASSERT(ng.num_nodes() == 1); @@ -364,36 +364,36 @@ static void test_nielsen_expansion() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* ay = sg.mk_concat(a, y); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* ay = sg.mk_concat(a, y); // root: x = Ay ng.add_str_eq(x, ay); seq::nielsen_node* root = ng.root(); SASSERT(root->str_eqs().size() == 1); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; // branch 1: x -> eps (eliminating, progress) - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); seq::nielsen_node* child1 = ng.mk_child(root); - seq::nielsen_subst s1(x, e, dep); + const seq::nielsen_subst s1(x, e, dep); child1->apply_subst(sg, s1); seq::nielsen_edge* edge1 = ng.mk_edge(root, child1, "test", true); edge1->add_subst(s1); // branch 2: x -> Ax (non-eliminating, non-progress) - euf::snode* ax = sg.mk_concat(a, x); + euf::snode const* ax = sg.mk_concat(a, x); seq::nielsen_node* child2 = ng.mk_child(root); - seq::nielsen_subst s2(x, ax, dep); + const seq::nielsen_subst s2(x, ax, dep); child2->apply_subst(sg, s2); seq::nielsen_edge* edge2 = ng.mk_edge(root, child2, "test", false); edge2->add_subst(s2); @@ -417,28 +417,28 @@ static void test_multiple_memberships() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - sort_ref str_sort(seq.str.mk_string_sort(), m); + const sort_ref str_sort(seq.str.mk_string_sort(), m); dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // x in .* - expr_ref re_all(seq.re.mk_full_seq(str_sort), m); - euf::snode* regex1 = sg.mk(re_all); + const expr_ref re_all(seq.re.mk_full_seq(str_sort), m); + euf::snode const* regex1 = sg.mk(re_all); ng.add_str_mem(x, regex1); // x in re.union(to_re("A"), to_re("B")) - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - expr_ref to_re_b(seq.re.mk_to_re(unit_b), m); - expr_ref re_union(seq.re.mk_union(to_re_a, to_re_b), m); - euf::snode* regex2 = sg.mk(re_union); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + const expr_ref to_re_b(seq.re.mk_to_re(unit_b), m); + const expr_ref re_union(seq.re.mk_union(to_re_a, to_re_b), m); + euf::snode const* regex2 = sg.mk(re_union); ng.add_str_mem(x, regex2); SASSERT(ng.root() != nullptr); @@ -459,13 +459,13 @@ static void test_backedge() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); ng.add_str_eq(x, y); seq::nielsen_node* root = ng.root(); seq::nielsen_node* child = ng.mk_child(root); - + // set backedge from child to root (cycle) // child->set_backedge(root); // SASSERT(child->backedge() == root); @@ -484,19 +484,19 @@ static void test_eq_split_basic() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* yb = sg.mk_concat(y, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* yb = sg.mk_concat(y, b); // x·A = y·B — eq_split returns false (no valid split point), // falls through to var_nielsen (priority 12) → 3 progress children ng.add_str_eq(xa, yb); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(root->outgoing().size() == 3); @@ -516,15 +516,15 @@ static void test_eq_split_solve_sat() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* z = sg.mk_var(symbol("z"), sg.get_str_sort()); - euf::snode* w = sg.mk_var(symbol("w"), sg.get_str_sort()); - euf::snode* xy = sg.mk_concat(x, y); - euf::snode* zw = sg.mk_concat(z, w); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* z = sg.mk_var(symbol("z"), sg.get_str_sort()); + euf::snode const* w = sg.mk_var(symbol("w"), sg.get_str_sort()); + euf::snode const* xy = sg.mk_concat(x, y); + euf::snode const* zw = sg.mk_concat(z, w); ng.add_str_eq(xy, zw); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -536,19 +536,19 @@ static void test_eq_split_solve_unsat() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* yb = sg.mk_concat(y, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* yb = sg.mk_concat(y, b); ng.add_str_eq(xa, yb); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); } @@ -564,15 +564,15 @@ static void test_eq_split_same_var_det() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* xb = sg.mk_concat(x, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* xb = sg.mk_concat(x, b); // x·A = x·B → same-head cancel → A = B → clash → unsat ng.add_str_eq(xa, xb); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); } @@ -588,14 +588,14 @@ static void test_eq_split_commutation_sat() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* xya = sg.mk_concat(x, sg.mk_concat(y, a)); - euf::snode* yxa = sg.mk_concat(y, sg.mk_concat(x, a)); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* xya = sg.mk_concat(x, sg.mk_concat(y, a)); + euf::snode const* yxa = sg.mk_concat(y, sg.mk_concat(x, a)); ng.add_str_eq(xya, yxa); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -612,13 +612,13 @@ static void test_const_nielsen_char_var() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // A = y (single var definition → det modifier fires) ng.add_str_eq(a, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // det modifier: y → A (1 progress child) SASSERT(root->outgoing().size() == 1); @@ -637,15 +637,15 @@ static void test_const_nielsen_var_char() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* b = sg.mk_char('B'); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* by = sg.mk_concat(b, y); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* by = sg.mk_concat(b, y); // x = B·y (single var definition → det modifier fires) ng.add_str_eq(x, by); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // det modifier: x → B·y (1 progress child) SASSERT(root->outgoing().size() == 1); @@ -664,14 +664,14 @@ static void test_const_nielsen_solve_sat() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* ax = sg.mk_concat(a, x); - euf::snode* ab = sg.mk_concat(a, b); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* ax = sg.mk_concat(a, x); + euf::snode const* ab = sg.mk_concat(a, b); ng.add_str_eq(ax, ab); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -687,15 +687,15 @@ static void test_const_nielsen_solve_unsat() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* ax = sg.mk_concat(a, x); - euf::snode* by = sg.mk_concat(b, y); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* ax = sg.mk_concat(a, x); + euf::snode const* by = sg.mk_concat(b, y); ng.add_str_eq(ax, by); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); } @@ -711,18 +711,18 @@ static void test_const_nielsen_priority_over_eq_split() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* ax = sg.mk_concat(a, x); - euf::snode* yb = sg.mk_concat(y, b); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* ax = sg.mk_concat(a, x); + euf::snode const* yb = sg.mk_concat(y, b); // A·x = y·B → lhs starts with char, rhs starts with var → const_nielsen ng.add_str_eq(ax, yb); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // const_nielsen produces 2 children, not var_nielsen's 3 SASSERT(root->outgoing().size() == 2); @@ -742,23 +742,23 @@ static void test_const_nielsen_tail_char_var() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* w = sg.mk_var(symbol("w"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* lhs = sg.mk_concat(x, a); // x·A - euf::snode* rhs = sg.mk_concat(w, y); // w·y + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* w = sg.mk_var(symbol("w"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* lhs = sg.mk_concat(x, a); // x·A + euf::snode const* rhs = sg.mk_concat(w, y); // w·y ng.add_str_eq(lhs, rhs); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(root->outgoing().size() == 2); bool saw_empty = false; bool saw_tail = false; - for (seq::nielsen_edge* e : root->outgoing()) { + for (const seq::nielsen_edge* e : root->outgoing()) { SASSERT(e->subst().size() == 1); seq::nielsen_subst const& s = e->subst()[0]; SASSERT(s.m_var == y); @@ -791,18 +791,18 @@ static void test_const_nielsen_not_applicable_both_vars() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* yb = sg.mk_concat(y, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* yb = sg.mk_concat(y, b); // x·A = y·B → both heads are vars → var_nielsen fires (priority 12) ng.add_str_eq(xa, yb); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(root->outgoing().size() == 3); } @@ -818,15 +818,15 @@ static void test_const_nielsen_multi_char_solve() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* c = sg.mk_char('C'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* abx = sg.mk_concat(a, sg.mk_concat(b, x)); - euf::snode* abc = sg.mk_concat(a, sg.mk_concat(b, c)); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* c = sg.mk_char('C'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* abx = sg.mk_concat(a, sg.mk_concat(b, x)); + euf::snode const* abc = sg.mk_concat(a, sg.mk_concat(b, c)); ng.add_str_eq(abx, abc); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -848,23 +848,23 @@ static void test_regex_char_split_basic() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); - expr_ref to_re_ab(seq.re.mk_to_re(ab), m); - euf::snode* regex = sg.mk(to_re_ab); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); + const expr_ref to_re_ab(seq.re.mk_to_re(ab), m); + euf::snode const* regex = sg.mk(to_re_ab); ng.add_str_mem(x, regex); SASSERT(ng.root() != nullptr); - auto sr = ng.root()->simplify_and_init({}); + const auto sr = ng.root()->simplify_and_init({}); SASSERT(sr != seq::simplify_result::conflict); - bool extended = ng.generate_extensions(ng.root()); + const bool extended = ng.generate_extensions(ng.root()); SASSERT(extended); // should have at least 2 children: x→'A'·z and x→ε SASSERT(ng.root()->outgoing().size() >= 2); @@ -884,15 +884,15 @@ static void test_regex_char_split_solve_sat() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* regex = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* regex = sg.mk(to_re_a); ng.add_str_mem(x, regex); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -909,18 +909,18 @@ static void test_regex_char_split_solve_multi_char() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); - expr_ref to_re_ab(seq.re.mk_to_re(ab), m); - euf::snode* regex = sg.mk(to_re_ab); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); + const expr_ref to_re_ab(seq.re.mk_to_re(ab), m); + euf::snode const* regex = sg.mk(to_re_ab); ng.add_str_mem(x, regex); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -937,19 +937,19 @@ static void test_regex_char_split_union() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - expr_ref to_re_b(seq.re.mk_to_re(unit_b), m); - expr_ref re_union(seq.re.mk_union(to_re_a, to_re_b), m); - euf::snode* regex = sg.mk(re_union); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + const expr_ref to_re_b(seq.re.mk_to_re(unit_b), m); + const expr_ref re_union(seq.re.mk_union(to_re_a, to_re_b), m); + euf::snode const* regex = sg.mk(re_union); ng.add_str_mem(x, regex); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -966,16 +966,16 @@ static void test_regex_char_split_star_sat() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - expr_ref re_star(seq.re.mk_star(to_re_a), m); - euf::snode* regex = sg.mk(re_star); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + const expr_ref re_star(seq.re.mk_star(to_re_a), m); + euf::snode const* regex = sg.mk(re_star); ng.add_str_mem(x, regex); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -992,20 +992,20 @@ static void test_regex_char_split_concat_str() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* xy = sg.mk_concat(x, y); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* xy = sg.mk_concat(x, y); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); - expr_ref to_re_ab(seq.re.mk_to_re(ab), m); - euf::snode* regex = sg.mk(to_re_ab); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); + const expr_ref to_re_ab(seq.re.mk_to_re(ab), m); + euf::snode const* regex = sg.mk(to_re_ab); ng.add_str_mem(xy, regex); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -1023,18 +1023,18 @@ static void test_regex_char_split_with_eq() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); ng.add_str_eq(x, y); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* regex = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* regex = sg.mk(to_re_a); ng.add_str_mem(x, regex); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -1052,16 +1052,16 @@ static void test_regex_char_split_ground_skip() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); + euf::snode const* a = sg.mk_char('A'); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* regex = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* regex = sg.mk(to_re_a); // "A" in to_re("A") → simplification consumes the char prefix via derivative ng.add_str_mem(a, regex); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -1082,14 +1082,14 @@ static void test_var_nielsen_basic() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // x = y → det: x → y (single var definition) ng.add_str_eq(x, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(root->outgoing().size() == 1); SASSERT(root->outgoing()[0]->is_progress()); @@ -1107,15 +1107,15 @@ static void test_var_nielsen_same_var_det() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* xb = sg.mk_concat(x, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* xb = sg.mk_concat(x, b); // x·A = x·B → same-head cancel → A = B → clash → unsat ng.add_str_eq(xa, xb); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); } @@ -1131,14 +1131,14 @@ static void test_var_nielsen_not_applicable_char() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // A = y → det: y → A (variable definition, 1 child) ng.add_str_eq(a, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(root->outgoing().size() == 1); } @@ -1155,15 +1155,15 @@ static void test_var_nielsen_solve_sat() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* z = sg.mk_var(symbol("z"), sg.get_str_sort()); - euf::snode* w = sg.mk_var(symbol("w"), sg.get_str_sort()); - euf::snode* xy = sg.mk_concat(x, y); - euf::snode* zw = sg.mk_concat(z, w); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* z = sg.mk_var(symbol("z"), sg.get_str_sort()); + euf::snode const* w = sg.mk_var(symbol("w"), sg.get_str_sort()); + euf::snode const* xy = sg.mk_concat(x, y); + euf::snode const* zw = sg.mk_concat(z, w); ng.add_str_eq(xy, zw); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -1179,15 +1179,15 @@ static void test_var_nielsen_solve_unsat() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* yb = sg.mk_concat(y, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* yb = sg.mk_concat(y, b); ng.add_str_eq(xa, yb); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); } @@ -1202,14 +1202,14 @@ static void test_var_nielsen_commutation_sat() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* xya = sg.mk_concat(x, sg.mk_concat(y, a)); - euf::snode* yxa = sg.mk_concat(y, sg.mk_concat(x, a)); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* xya = sg.mk_concat(x, sg.mk_concat(y, a)); + euf::snode const* yxa = sg.mk_concat(y, sg.mk_concat(x, a)); ng.add_str_eq(xya, yxa); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -1222,16 +1222,16 @@ static void test_var_nielsen_priority() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); ng.add_str_eq(x, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // det modifier: x → y (1 progress child) SASSERT(root->outgoing().size() == 1); @@ -1248,19 +1248,19 @@ static void test_generate_extensions_det_priority() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; - seq::nielsen_graph ng(sg, solver, context_solver); + seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* xy = sg.mk_concat(x, y); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* xy = sg.mk_concat(x, y); // x·A = x·y → after simplify, becomes A = y → det: y → A ng.add_str_eq(xa, xy); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -1274,19 +1274,19 @@ static void test_generate_extensions_no_applicable() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // A = B → no variables involved → no modifier applies ng.add_str_eq(a, b); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(!extended); SASSERT(root->outgoing().empty()); } @@ -1301,15 +1301,15 @@ static void test_generate_extensions_regex_only() { euf::sgraph sg(m, eg); seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // Build regex to_re("A") - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* re_node = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* re_node = sg.mk(to_re_a); // x ∈ to_re("A") → only regex_char_split can fire (no str_eq) ng.add_str_mem(x, re_node); @@ -1317,7 +1317,7 @@ static void test_generate_extensions_regex_only() { root->simplify_and_init({}); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // at least 1 child (epsilon branch) + possibly char branches SASSERT(root->outgoing().size() >= 1); @@ -1332,27 +1332,27 @@ static void test_generate_extensions_mixed_det_first() { euf::sgraph sg(m, eg); seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* xb = sg.mk_concat(x, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* xb = sg.mk_concat(x, b); // Build a regex for the mem constraint - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* re_node = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* re_node = sg.mk(to_re_a); // x·A = x·B → simplify cancels x → A = B → clash → unsat ng.add_str_eq(xa, xb); ng.add_str_mem(y, re_node); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); } @@ -1368,11 +1368,11 @@ static void test_solve_empty_graph() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); SASSERT(!ng.root()); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -1384,12 +1384,12 @@ static void test_solve_trivially_satisfied() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); ng.add_str_eq(x, x); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -1401,18 +1401,18 @@ static void test_solve_node_status_unsat() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // A = B is an immediate conflict ng.add_str_eq(a, b); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); // root should be marked as general conflict - seq::nielsen_node* root = ng.root(); + const seq::nielsen_node* root = ng.root(); SASSERT(root->is_general_conflict()); SASSERT(root->is_currently_conflict()); } @@ -1428,14 +1428,14 @@ static void test_solve_conflict_deps() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // Add two constraints: A = B (unsat) and a dummy x = x ng.add_str_eq(a, b); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); ng.add_str_eq(x, x); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); // conflict_sources should be non-empty since there's a conflict @@ -1449,13 +1449,13 @@ static void test_dep_tracker_get_set_bits() { seq::dep_manager dm; // empty tracker has no leaves - seq::dep_tracker d0 = dm.mk_empty(); + const seq::dep_tracker d0 = dm.mk_empty(); vector bits0; dm.linearize(d0, bits0); SASSERT(bits0.empty()); // single leaf with sat::literal(5) - seq::dep_tracker d1 = dm.mk_leaf(sat::literal(5)); + const seq::dep_tracker d1 = dm.mk_leaf(sat::literal(5)); vector bits1; dm.linearize(d1, bits1); SASSERT(bits1.size() == 1); @@ -1463,7 +1463,7 @@ static void test_dep_tracker_get_set_bits() { SASSERT(std::get(bits1[0]).index() == 5); // two leaves merged: sat::literal(3) and sat::literal(11) - seq::dep_tracker d2 = dm.mk_join( + const seq::dep_tracker d2 = dm.mk_join( dm.mk_leaf(sat::literal(3)), dm.mk_leaf(sat::literal(11))); vector bits2; @@ -1472,7 +1472,7 @@ static void test_dep_tracker_get_set_bits() { bool has_3 = false, has_11 = false; for (auto const& d : bits2) { if (std::holds_alternative(d)) { - unsigned idx = std::get(d).index(); + const unsigned idx = std::get(d).index(); if (idx == 3) has_3 = true; if (idx == 11) has_11 = true; } @@ -1481,7 +1481,7 @@ static void test_dep_tracker_get_set_bits() { SASSERT(has_11); // join with additional leaves - seq::dep_tracker d3 = dm.mk_join( + const seq::dep_tracker d3 = dm.mk_join( dm.mk_leaf(sat::literal(31)), dm.mk_leaf(sat::literal(32))); vector bits3; @@ -1490,7 +1490,7 @@ static void test_dep_tracker_get_set_bits() { bool has31 = false, has32 = false; for (auto const& d : bits3) { if (std::holds_alternative(d)) { - unsigned idx = std::get(d).index(); + const unsigned idx = std::get(d).index(); if (idx == 31) has31 = true; if (idx == 32) has32 = true; } @@ -1507,15 +1507,15 @@ static void test_explain_conflict_single_eq() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // eq[0]: A = B (conflict) ng.add_str_eq(a, b); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); // test-friendly overloads use null deps, so explain_conflict won't return anything @@ -1538,16 +1538,16 @@ static void test_explain_conflict_multi_eq() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // eq[0]: x = x (trivially sat) ng.add_str_eq(x, x); // eq[1]: A = B (conflict) ng.add_str_eq(a, b); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); // with test-friendly overload (null deps), explain_conflict won't return deps @@ -1565,23 +1565,23 @@ static void test_solve_node_extended_flag() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* xy = sg.mk_concat(x, y); - euf::snode* z = sg.mk_var(symbol("z"), sg.get_str_sort()); - euf::snode* w = sg.mk_var(symbol("w"), sg.get_str_sort()); - euf::snode* zw = sg.mk_concat(z, w); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* xy = sg.mk_concat(x, y); + euf::snode const* z = sg.mk_var(symbol("z"), sg.get_str_sort()); + euf::snode const* w = sg.mk_var(symbol("w"), sg.get_str_sort()); + euf::snode const* zw = sg.mk_concat(z, w); // x·y = z·w — requires extension generation ng.add_str_eq(xy, zw); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); // root should be marked as extended - seq::nielsen_node* root = ng.root(); + const seq::nielsen_node* root = ng.root(); SASSERT(root->is_extended()); } @@ -1594,26 +1594,26 @@ static void test_solve_mixed_eq_mem_sat() { euf::sgraph sg(m, eg); seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* ya = sg.mk_concat(y, a); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* ya = sg.mk_concat(y, a); // x·A = y·A (satisfiable: x=y=ε, or x=y=anything) ng.add_str_eq(xa, ya); // y ∈ to_re("A") (y must be "A") - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* re_a = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* re_a = sg.mk(to_re_a); ng.add_str_mem(y, re_a); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -1625,19 +1625,19 @@ static void test_solve_children_failed_reason() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* yb = sg.mk_concat(y, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* yb = sg.mk_concat(y, b); // x·A = y·B is unsat (last char mismatch propagates up) ng.add_str_eq(xa, yb); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); } @@ -1653,23 +1653,23 @@ static void test_simplify_prefix_cancel() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // A·B·x = A·B·y → prefix cancel A,B → x = y (proceed) - euf::snode* abx = sg.mk_concat(a, sg.mk_concat(b, x)); - euf::snode* aby = sg.mk_concat(a, sg.mk_concat(b, y)); + euf::snode const* abx = sg.mk_concat(a, sg.mk_concat(b, x)); + euf::snode const* aby = sg.mk_concat(a, sg.mk_concat(b, y)); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(abx, aby, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::proceed); SASSERT(node->str_eqs().size() == 1); // after prefix cancel: remaining eq has variable-only sides @@ -1688,19 +1688,19 @@ static void test_simplify_suffix_cancel_rtl() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // x·A = y·A → suffix cancel A (RTL) → x = y - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* ya = sg.mk_concat(y, a); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* ya = sg.mk_concat(y, a); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(xa, ya, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::proceed); SASSERT(node->str_eqs().size() == 1); SASSERT(node->str_eqs()[0].m_lhs->is_var()); @@ -1715,23 +1715,23 @@ static void test_simplify_symbol_clash() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // A·x = B·y → symbol clash on first char - euf::snode* ax = sg.mk_concat(a, x); - euf::snode* by = sg.mk_concat(b, y); + euf::snode const* ax = sg.mk_concat(a, x); + euf::snode const* by = sg.mk_concat(b, y); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(ax, by, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::conflict); SASSERT(node->is_general_conflict()); SASSERT(node->reason() == seq::backtrack_reason::symbol_clash); @@ -1744,22 +1744,22 @@ static void test_simplify_empty_propagation() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* xy = sg.mk_concat(x, y); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* xy = sg.mk_concat(x, y); // ε = x·y → forces x=ε, y=ε → all trivial → satisfied seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(e, xy, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::satisfied); } @@ -1770,20 +1770,20 @@ static void test_simplify_empty_vs_char() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - euf::snode* a = sg.mk_char('A'); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* a = sg.mk_char('A'); // ε = A → rhs has non-variable token → conflict seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(e, a, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::conflict); SASSERT(node->reason() == seq::backtrack_reason::symbol_clash); } @@ -1796,22 +1796,22 @@ static void test_simplify_multi_pass_clash() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* c = sg.mk_char('C'); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* c = sg.mk_char('C'); // A·B = A·C → cancel A → B vs C → clash - euf::snode* ab = sg.mk_concat(a, b); - euf::snode* ac = sg.mk_concat(a, c); + euf::snode const* ab = sg.mk_concat(a, b); + euf::snode const* ac = sg.mk_concat(a, c); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(ab, ac, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::conflict); SASSERT(node->reason() == seq::backtrack_reason::symbol_clash); } @@ -1823,21 +1823,21 @@ static void test_simplify_trivial_removal() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(e, e, dep)); // trivial node->add_str_eq(seq::str_eq(x, y, dep)); // non-trivial - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::proceed); SASSERT(node->str_eqs().size() == 1); } @@ -1849,20 +1849,20 @@ static void test_simplify_all_trivial_satisfied() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(x, x, dep)); // trivial: same pointer node->add_str_eq(seq::str_eq(e, e, dep)); // trivial: both empty - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::satisfied); } @@ -1875,22 +1875,22 @@ static void test_simplify_regex_infeasible() { euf::sgraph sg(m, eg); seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* regex = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* regex = sg.mk(to_re_a); // ε ∈ to_re("A") → non-nullable → conflict seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_mem(seq::str_mem(e, regex, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::conflict); SASSERT(node->reason() == seq::backtrack_reason::regex); } @@ -1904,23 +1904,23 @@ static void test_simplify_nullable_removal() { euf::sgraph sg(m, eg); seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - expr_ref re_star(seq.re.mk_star(to_re_a), m); - euf::snode* regex = sg.mk(re_star); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + const expr_ref re_star(seq.re.mk_star(to_re_a), m); + euf::snode const* regex = sg.mk(re_star); // ε ∈ star(to_re("A")) → nullable → satisfied, mem removed seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_mem(seq::str_mem(e, regex, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::satisfied); SASSERT(node->str_mems().empty()); } @@ -1934,23 +1934,23 @@ static void test_simplify_brzozowski_sat() { euf::sgraph sg(m, eg); seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* regex = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* regex = sg.mk(to_re_a); // "A" ∈ to_re("A") → derivative consumes 'A' → ε ∈ ε-regex → satisfied seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_mem(seq::str_mem(a, regex, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::satisfied); } @@ -1963,34 +1963,34 @@ static void test_simplify_brzozowski_rtl_suffix() { euf::sgraph sg(m, eg); seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ba(seq.str.mk_concat(unit_b, unit_a), m); - expr_ref to_re_ba(seq.re.mk_to_re(ba), m); - euf::snode* regex = sg.mk(to_re_ba); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ba(seq.str.mk_concat(unit_b, unit_a), m); + const expr_ref to_re_ba(seq.re.mk_to_re(ba), m); + euf::snode const* regex = sg.mk(to_re_ba); // x·"A" ∈ to_re("BA") → RTL consume trailing 'A' → x ∈ to_re("B") seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_mem(seq::str_mem(xa, regex, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::proceed); SASSERT(node->str_mems().size() == 1); SASSERT(node->str_mems()[0].m_str->is_var()); SASSERT(node->str_mems()[0].m_str->id() == x->id()); - euf::snode* deriv_b = sg.brzozowski_deriv(node->str_mems()[0].m_regex, sg.mk_char('B')); + euf::snode const* deriv_b = sg.brzozowski_deriv(node->str_mems()[0].m_regex, sg.mk_char('B')); SASSERT(deriv_b); } @@ -2001,31 +2001,31 @@ static void test_simplify_multiple_eqs() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* z = sg.mk_var(symbol("z"), sg.get_str_sort()); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* z = sg.mk_var(symbol("z"), sg.get_str_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; // eq1: ε = ε (trivial → removed) node->add_str_eq(seq::str_eq(e, e, dep)); // eq2: A·x = A·y (prefix cancel → x = y) - euf::snode* ax = sg.mk_concat(a, x); - euf::snode* ay = sg.mk_concat(a, y); + euf::snode const* ax = sg.mk_concat(a, x); + euf::snode const* ay = sg.mk_concat(a, y); node->add_str_eq(seq::str_eq(ax, ay, dep)); // eq3: x = z (non-trivial, kept) node->add_str_eq(seq::str_eq(x, z, dep)); SASSERT(node->str_eqs().size() == 3); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::proceed); // eq1 removed, eq2 simplified to x=y, eq3 kept → 2 eqs remain SASSERT(node->str_eqs().size() == 2); @@ -2046,15 +2046,15 @@ static void test_det_cancel_child_eq() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* xb = sg.mk_concat(x, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* xb = sg.mk_concat(x, b); // x·A = x·B → simplify cancels x → A = B → clash → unsat ng.add_str_eq(xa, xb); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); } @@ -2070,18 +2070,18 @@ static void test_const_nielsen_child_substitutions() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* ax = sg.mk_concat(a, x); - euf::snode* yb = sg.mk_concat(y, b); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* ax = sg.mk_concat(a, x); + euf::snode const* yb = sg.mk_concat(y, b); // A·x = y·B → const_nielsen: 2 children, both substitute y ng.add_str_eq(ax, yb); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(root->outgoing().size() == 2); @@ -2105,17 +2105,17 @@ static void test_var_nielsen_substitution_types() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // x = y → det: x → y (single var definition, 1 child) ng.add_str_eq(x, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(root->outgoing().size() == 1); @@ -2140,16 +2140,16 @@ static void test_explain_conflict_mem_only() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* regex = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* regex = sg.mk(to_re_a); // ε ∈ to_re("A") → conflict (non-nullable) ng.add_str_mem(e, regex); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); // with test-friendly overload (null deps), explain_conflict won't return deps @@ -2167,25 +2167,25 @@ static void test_explain_conflict_mixed_eq_mem() { euf::sgraph sg(m, eg); seq_util seq(m); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); // eq[0]: A = B (conflict) ng.add_str_eq(a, b); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* regex = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* regex = sg.mk(to_re_a); // mem[0]: ε ∈ to_re("A") ng.add_str_mem(e, regex); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); // with test-friendly overload (null deps), explain_conflict won't return deps @@ -2203,16 +2203,16 @@ static void test_subsumption_pruning_unsat() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // A = B is an immediate conflict (symbol clash). // Any branch that inherits this equation should be pruned. ng.add_str_eq(a, b); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); // root should have conflict set @@ -2227,27 +2227,27 @@ static void test_subsumption_reason_set() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // x·A = y·B: after Nielsen splitting, children will have A=B // which is unsat. The subsumption pruning may fire on sibling // branches that inherit the same conflict. - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* yb = sg.mk_concat(y, b); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* yb = sg.mk_concat(y, b); ng.add_str_eq(xa, yb); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::unsat); // check that at least one node has subsumption reason bool found_subsumption = false; - for (seq::nielsen_node* nd : ng.nodes()) { + for (const seq::nielsen_node* nd : ng.nodes()) { if (nd->reason() == seq::backtrack_reason::subsumption) { found_subsumption = true; SASSERT(nd->is_general_conflict()); @@ -2270,16 +2270,16 @@ static void test_length_constraints_basic() { arith_util arith(m); seq_util seq(m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // equation: x . y = A . B - euf::snode* lhs = sg.mk_concat(x, y); - euf::snode* rhs = sg.mk_concat(a, b); + euf::snode const* lhs = sg.mk_concat(x, y); + euf::snode const* rhs = sg.mk_concat(a, b); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); ng.add_str_eq(lhs, rhs); @@ -2313,7 +2313,7 @@ static void test_length_constraints_trivial_skip() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // trivial equation: x = x (same snode) dummy_simple_solver solver; @@ -2337,7 +2337,7 @@ static void test_length_constraints_empty() { euf::egraph eg(m); euf::sgraph sg(m, eg); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); vector constraints; @@ -2356,18 +2356,18 @@ static void test_length_constraints_concat_chain() { euf::sgraph sg(m, eg); arith_util arith(m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* z = sg.mk_var(symbol("z"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* c = sg.mk_char('C'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* z = sg.mk_var(symbol("z"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* c = sg.mk_char('C'); // equation: x . y . z = A . B . C - euf::snode* lhs = sg.mk_concat(sg.mk_concat(x, y), z); - euf::snode* rhs = sg.mk_concat(sg.mk_concat(a, b), c); + euf::snode const* lhs = sg.mk_concat(sg.mk_concat(x, y), z); + euf::snode const* rhs = sg.mk_concat(sg.mk_concat(a, b), c); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); ng.add_str_eq(lhs, rhs); @@ -2392,10 +2392,10 @@ static void test_length_constraints_multi_eq() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -2424,15 +2424,15 @@ static void test_length_constraints_shared_var() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // equation: x . A = A . x (x appears on both sides) - euf::snode* lhs = sg.mk_concat(x, a); - euf::snode* rhs = sg.mk_concat(a, x); + euf::snode const* lhs = sg.mk_concat(x, a); + euf::snode const* rhs = sg.mk_concat(a, x); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); ng.add_str_eq(lhs, rhs); @@ -2456,10 +2456,10 @@ static void test_length_constraints_deps() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); - dummy_simple_solver solver; + dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); ng.add_str_eq(x, a); // eq index 0 @@ -2481,10 +2481,10 @@ static void test_length_constraints_empty_side() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* e = sg.mk_empty_seq(seq.str.mk_string_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* e = sg.mk_empty_seq(seq.str.mk_string_sort()); // x = ε dummy_simple_solver solver; @@ -2518,9 +2518,9 @@ static void test_length_kind_tagging() { arith_util arith(m); sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); // equation: x = a (one eq + one nonneg) dummy_simple_solver solver; @@ -2529,13 +2529,13 @@ static void test_length_kind_tagging() { ng.add_str_eq(x, a); // membership: y in to_re("AB") (bounds + nonneg) - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); - expr_ref to_re_ab(seq.re.mk_to_re(ab), m); - euf::snode* regex = sg.mk(to_re_ab); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); + const expr_ref to_re_ab(seq.re.mk_to_re(ab), m); + euf::snode const* regex = sg.mk(to_re_ab); ng.add_str_mem(y, regex); vector constraints; @@ -2583,15 +2583,15 @@ static void test_power_epsilon_no_power() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); // x = A: no power tokens, power_epsilon should not fire ng.add_str_eq(x, a); seq::nielsen_node* root = ng.root(); // det fires (x is single var, A doesn't contain x → x → A) - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // det: x → A (variable definition, 1 child) SASSERT(root->outgoing().size() == 1); @@ -2609,14 +2609,14 @@ static void test_num_cmp_no_power() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // x = y: no power tokens, num_cmp should not fire ng.add_str_eq(x, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // det fires (x → y, variable definition): 1 child SASSERT(root->outgoing().size() == 1); @@ -2636,11 +2636,11 @@ static void test_star_intr_no_backedge() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* regex = sg.mk(to_re_a); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* regex = sg.mk(to_re_a); // x ∈ to_re("A"): no backedge, star_intr should not fire ng.add_str_mem(x, regex); @@ -2648,10 +2648,10 @@ static void test_star_intr_no_backedge() { seq::nielsen_node* root = ng.root(); // SASSERT(root->backedge() == nullptr); - auto sr = root->simplify_and_init({}); + const auto sr = root->simplify_and_init({}); SASSERT(sr != seq::simplify_result::conflict); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // regex_char_split fires (priority 9): at least 2 children (x→A·z, x→ε) SASSERT(root->outgoing().size() >= 2); @@ -2671,12 +2671,12 @@ static void test_star_intr_with_backedge() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - expr_ref re_star(seq.re.mk_star(to_re_a), m); - euf::snode* regex = sg.mk(re_star); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + const expr_ref re_star(seq.re.mk_star(to_re_a), m); + euf::snode const* regex = sg.mk(re_star); // x ∈ star(to_re("A")): set backedge to simulate cycle detection ng.add_str_mem(x, regex); @@ -2684,7 +2684,7 @@ static void test_star_intr_with_backedge() { seq::nielsen_node* root = ng.root(); // root->set_backedge(root); // simulate backedge - auto sr = root->simplify_and_init({}); + const auto sr = root->simplify_and_init({}); // star(to_re("A")) is nullable, so empty string satisfies it // simplify may remove the membership or proceed if (sr == seq::simplify_result::satisfied) { @@ -2692,7 +2692,7 @@ static void test_star_intr_with_backedge() { return; // OK, the regex is nullable so it was removed } - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); if (extended) { // star_intr should have generated at least 1 child SASSERT(root->outgoing().size() >= 1); @@ -2712,18 +2712,18 @@ static void test_gpower_intr_self_cycle() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a1 = sg.mk_char('A'); - euf::snode* a2 = sg.mk_char('A'); - euf::snode* lhs = sg.mk_concat(a1, x); // Ax - euf::snode* rhs = sg.mk_concat(x, a2); // xA + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a1 = sg.mk_char('A'); + euf::snode const* a2 = sg.mk_char('A'); + euf::snode const* lhs = sg.mk_concat(a1, x); // Ax + euf::snode const* rhs = sg.mk_concat(x, a2); // xA // Ax = xA → variable x appears on both sides with ground prefix 'A' // GPowerIntr detects self-cycle and introduces x = A^n · suffix ng.add_str_eq(lhs, rhs); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(ng.stats().m_mod_gpower_intr == 1); SASSERT(root->outgoing().size() == 1); @@ -2742,19 +2742,19 @@ static void test_gpower_intr_no_cycle() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); - euf::snode* lhs = sg.mk_concat(a, x); // Ax - euf::snode* rhs = sg.mk_concat(y, b); // Yb + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); + euf::snode const* lhs = sg.mk_concat(a, x); // Ax + euf::snode const* rhs = sg.mk_concat(y, b); // Yb // Ax = Yb → Y is head of RHS, scan LHS: prefix=[A], target=x, but x ≠ y → no cycle // GPowerIntr does NOT fire; ConstNielsen (priority 8) fires instead ng.add_str_eq(lhs, rhs); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(ng.stats().m_mod_gpower_intr == 0); std::cout << " gpower_intr did not fire (no cycle)\n"; @@ -2774,25 +2774,25 @@ static void test_regex_var_split_basic() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // Build a regex: re.union(to_re("A"), to_re("B")) - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - expr_ref to_re_b(seq.re.mk_to_re(unit_b), m); - expr_ref re_union(seq.re.mk_union(to_re_a, to_re_b), m); - euf::snode* regex = sg.mk(re_union); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + const expr_ref to_re_b(seq.re.mk_to_re(unit_b), m); + const expr_ref re_union(seq.re.mk_union(to_re_a, to_re_b), m); + euf::snode const* regex = sg.mk(re_union); ng.add_str_mem(x, regex); seq::nielsen_node* root = ng.root(); - auto sr = root->simplify_and_init({}); + const auto sr = root->simplify_and_init({}); SASSERT(sr != seq::simplify_result::conflict); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // Should produce children via regex_char_split or regex_var_split SASSERT(root->outgoing().size() >= 2); @@ -2811,17 +2811,17 @@ static void test_power_split_no_power() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* xa = sg.mk_concat(x, a); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* xa = sg.mk_concat(x, a); // x·A = y: no power tokens, power_split should not fire // det fires (y is single var, y ∉ vars(x·A) → y → x·A) ng.add_str_eq(xa, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // det fires: 1 child (y → x·A) SASSERT(root->outgoing().size() == 1); @@ -2839,14 +2839,14 @@ static void test_var_num_unwinding_no_power() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // x = y: no power tokens, var_num_unwinding should not fire ng.add_str_eq(x, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); // det fires: 1 child (x → y) SASSERT(root->outgoing().size() == 1); @@ -2863,15 +2863,15 @@ static void test_const_num_unwinding_no_power() { dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* b = sg.mk_char('B'); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* b = sg.mk_char('B'); // A = B: no power tokens, clash during simplification ng.add_str_eq(a, b); seq::nielsen_node* root = ng.root(); // Should detect clash during simplify - auto sr = root->simplify_and_init({}); + const auto sr = root->simplify_and_init({}); SASSERT(sr == seq::simplify_result::conflict); } @@ -2892,14 +2892,14 @@ static void test_priority_chain_order() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); - euf::snode* xa = sg.mk_concat(x, a); - euf::snode* xy = sg.mk_concat(x, y); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* xa = sg.mk_concat(x, a); + euf::snode const* xy = sg.mk_concat(x, y); ng.add_str_eq(xa, xy); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -2911,12 +2911,12 @@ static void test_priority_chain_order() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); ng.add_str_eq(x, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(root->outgoing().size() == 1); // Det: variable definition, 1 child } @@ -2929,12 +2929,12 @@ static void test_priority_chain_order() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* a = sg.mk_char('A'); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); ng.add_str_eq(a, y); seq::nielsen_node* root = ng.root(); - bool extended = ng.generate_extensions(root); + const bool extended = ng.generate_extensions(root); SASSERT(extended); SASSERT(root->outgoing().size() == 1); // Det: variable definition, 1 child } @@ -2952,14 +2952,14 @@ static void test_gpower_intr_solve_sat() { seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a1 = sg.mk_char('A'); - euf::snode* a2 = sg.mk_char('A'); - euf::snode* a3 = sg.mk_char('A'); - euf::snode* aaa = sg.mk_concat(a1, sg.mk_concat(a2, a3)); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a1 = sg.mk_char('A'); + euf::snode const* a2 = sg.mk_char('A'); + euf::snode const* a3 = sg.mk_char('A'); + euf::snode const* aaa = sg.mk_concat(a1, sg.mk_concat(a2, a3)); ng.add_str_eq(x, aaa); - auto result = ng.solve(); + const auto result = ng.solve(); SASSERT(result == seq::nielsen_graph::search_result::sat); } @@ -2975,18 +2975,18 @@ static void test_parikh_exact_length() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - arith_util arith(m); + const arith_util arith(m); sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); - expr_ref to_re_ab(seq.re.mk_to_re(ab), m); - euf::snode* regex = sg.mk(to_re_ab); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref ab(seq.str.mk_concat(unit_a, unit_b), m); + const expr_ref to_re_ab(seq.re.mk_to_re(ab), m); + euf::snode const* regex = sg.mk(to_re_ab); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3026,16 +3026,16 @@ static void test_parikh_star_unbounded() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - arith_util arith(m); + const arith_util arith(m); sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - expr_ref re_star(seq.re.mk_star(to_re_a), m); - euf::snode* regex = sg.mk(re_star); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + const expr_ref re_star(seq.re.mk_star(to_re_a), m); + euf::snode const* regex = sg.mk(re_star); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3071,7 +3071,7 @@ static void test_parikh_union_interval() { arith_util arith(m); sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // "AB" expr_ref ch_a(seq.str.mk_char('A'), m); @@ -3092,7 +3092,7 @@ static void test_parikh_union_interval() { expr_ref to_re_cde(seq.re.mk_to_re(cde), m); expr_ref re_union(seq.re.mk_union(to_re_ab, to_re_cde), m); - euf::snode* regex = sg.mk(re_union); + euf::snode const* regex = sg.mk(re_union); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3124,16 +3124,16 @@ static void test_parikh_loop_bounded() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - arith_util arith(m); + const arith_util arith(m); sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - expr_ref re_loop(seq.re.mk_loop(to_re_a, 3, 5), m); - euf::snode* regex = sg.mk(re_loop); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + const expr_ref re_loop(seq.re.mk_loop(to_re_a, 3, 5), m); + euf::snode const* regex = sg.mk(re_loop); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3165,13 +3165,13 @@ static void test_parikh_empty_regex() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - arith_util arith(m); - sort_ref str_sort(seq.str.mk_string_sort(), m); + const arith_util arith(m); + const sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref re_empty(seq.re.mk_empty(seq.re.mk_re(str_sort)), m); - euf::snode* regex = sg.mk(re_empty); + const expr_ref re_empty(seq.re.mk_empty(seq.re.mk_re(str_sort)), m); + euf::snode const* regex = sg.mk(re_empty); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3201,18 +3201,18 @@ static void test_parikh_full_char() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - arith_util arith(m); + const arith_util arith(m); sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // re.range("A", "Z") matches single characters in [A-Z] - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref ch_z(seq.str.mk_char('Z'), m); - expr_ref unit_z(seq.str.mk_unit(ch_z), m); - expr_ref re_range(seq.re.mk_range(unit_a, unit_z), m); - euf::snode* regex = sg.mk(re_range); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref ch_z(seq.str.mk_char('Z'), m); + const expr_ref unit_z(seq.str.mk_unit(ch_z), m); + const expr_ref re_range(seq.re.mk_range(unit_a, unit_z), m); + euf::snode const* regex = sg.mk(re_range); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3244,12 +3244,12 @@ static void test_parikh_mixed_eq_mem() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - arith_util arith(m); + const arith_util arith(m); sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('A'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('A'); // equation: x = A dummy_simple_solver solver; @@ -3258,13 +3258,13 @@ static void test_parikh_mixed_eq_mem() { ng.add_str_eq(x, a); // membership: y in to_re("BC") - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref ch_c(seq.str.mk_char('C'), m); - expr_ref unit_c(seq.str.mk_unit(ch_c), m); - expr_ref bc(seq.str.mk_concat(unit_b, unit_c), m); - expr_ref to_re_bc(seq.re.mk_to_re(bc), m); - euf::snode* regex = sg.mk(to_re_bc); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref ch_c(seq.str.mk_char('C'), m); + const expr_ref unit_c(seq.str.mk_unit(ch_c), m); + const expr_ref bc(seq.str.mk_concat(unit_b, unit_c), m); + const expr_ref to_re_bc(seq.re.mk_to_re(bc), m); + euf::snode const* regex = sg.mk(to_re_bc); ng.add_str_mem(y, regex); vector constraints; @@ -3294,13 +3294,13 @@ static void test_parikh_full_seq_no_bounds() { euf::egraph eg(m); euf::sgraph sg(m, eg); seq_util seq(m); - arith_util arith(m); - sort_ref str_sort(seq.str.mk_string_sort(), m); + const arith_util arith(m); + const sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref re_all(seq.re.mk_full_seq(str_sort), m); - euf::snode* regex = sg.mk(re_all); + const expr_ref re_all(seq.re.mk_full_seq(str_sort), m); + euf::snode const* regex = sg.mk(re_all); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3334,12 +3334,12 @@ static void test_parikh_dep_tracking() { arith_util arith(m); sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); - euf::snode* regex = sg.mk(to_re_a); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref to_re_a(seq.re.mk_to_re(unit_a), m); + euf::snode const* regex = sg.mk(to_re_a); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3384,21 +3384,21 @@ public: } }; -static void add_len_ge(seq::nielsen_graph& ng, seq::nielsen_node* node, euf::snode* var, unsigned lb, seq::dep_tracker dep) { +static void add_len_ge(seq::nielsen_graph& ng, seq::nielsen_node* node, euf::snode const* var, unsigned lb, seq::dep_tracker dep) { ast_manager& m = ng.get_manager(); arith_util arith(m); - expr_ref len(ng.seq().str.mk_length(var->get_expr()), m); + const expr_ref len(ng.seq().str.mk_length(var->get_expr()), m); node->add_constraint(seq::constraint(arith.mk_ge(len, arith.mk_int(lb)), dep, m)); } -static void add_len_le(seq::nielsen_graph& ng, seq::nielsen_node* node, euf::snode* var, unsigned ub, seq::dep_tracker dep) { +static void add_len_le(seq::nielsen_graph& ng, seq::nielsen_node* node, euf::snode const* var, unsigned ub, seq::dep_tracker dep) { ast_manager& m = ng.get_manager(); arith_util arith(m); - expr_ref len(ng.seq().str.mk_length(var->get_expr()), m); + const expr_ref len(ng.seq().str.mk_length(var->get_expr()), m); node->add_constraint(seq::constraint(arith.mk_le(len, arith.mk_int(ub)), dep, m)); } -static unsigned queried_lb(seq::nielsen_node* node, euf::snode* var) { +static unsigned queried_lb(seq::nielsen_node* node, euf::snode const* var) { rational lb; seq::dep_tracker d = nullptr; if (!node->lower_bound(var->get_expr(), lb, d)) @@ -3407,7 +3407,7 @@ static unsigned queried_lb(seq::nielsen_node* node, euf::snode* var) { return lb.is_unsigned() ? lb.get_unsigned() : 0; } -static unsigned queried_ub(seq::nielsen_node* node, euf::snode* var) { +static unsigned queried_ub(seq::nielsen_node* node, euf::snode const* var) { rational ub; seq::dep_tracker d = nullptr; if (!node->upper_bound(var->get_expr(), ub, d)) @@ -3425,7 +3425,7 @@ static void test_add_lower_int_bound_basic() { euf::sgraph sg(m, eg); seq_util seq(m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3433,7 +3433,7 @@ static void test_add_lower_int_bound_basic() { ng.add_str_eq(x, x); // create root node seq::nielsen_node* node = ng.root(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; // initially no bounds SASSERT(queried_lb(node, x) == 0); @@ -3465,7 +3465,7 @@ static void test_add_upper_int_bound_basic() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3473,7 +3473,7 @@ static void test_add_upper_int_bound_basic() { ng.add_str_eq(x, x); seq::nielsen_node* node = ng.root(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; SASSERT(queried_ub(node, x) == UINT_MAX); @@ -3502,7 +3502,7 @@ static void test_add_bound_lb_gt_ub_conflict() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3510,7 +3510,7 @@ static void test_add_bound_lb_gt_ub_conflict() { ng.add_str_eq(x, x); seq::nielsen_node* node = ng.root(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; add_len_le(ng, node, x, 3, dep); add_len_ge(ng, node, x, 5, dep); @@ -3527,8 +3527,8 @@ static void test_bounds_cloned() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3536,7 +3536,7 @@ static void test_bounds_cloned() { ng.add_str_eq(x, y); seq::nielsen_node* parent = ng.root(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; // set bounds on parent add_len_ge(ng, parent, x, 2, dep); @@ -3566,9 +3566,9 @@ static void test_subst_does_not_propagate_bounds_single_var() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('a'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('a'); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3576,15 +3576,15 @@ static void test_subst_does_not_propagate_bounds_single_var() { ng.add_str_eq(x, y); seq::nielsen_node* node = ng.root(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; // set bounds: 3 <= len(x) <= 7 add_len_ge(ng, node, x, 3, dep); add_len_le(ng, node, x, 7, dep); // apply substitution x → a·y - euf::snode* ay = sg.mk_concat(a, y); - seq::nielsen_subst s(x, ay, dep); + euf::snode const* ay = sg.mk_concat(a, y); + const seq::nielsen_subst s(x, ay, dep); node->apply_subst(sg, s); // No local propagation anymore: y keeps conservative defaults. @@ -3602,9 +3602,9 @@ static void test_subst_no_immediate_bound_conflict() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('a'); - euf::snode* b = sg.mk_char('b'); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('a'); + euf::snode const* b = sg.mk_char('b'); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3612,14 +3612,14 @@ static void test_subst_no_immediate_bound_conflict() { ng.add_str_eq(x, a); seq::nielsen_node* node = ng.root(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; // set bounds: 3 <= len(x) (so x must have at least 3 chars) add_len_ge(ng, node, x, 3, dep); // apply substitution x → a·b (no eager bound check at this stage) - euf::snode* ab = sg.mk_concat(a, b); - seq::nielsen_subst s(x, ab, dep); + euf::snode const* ab = sg.mk_concat(a, b); + const seq::nielsen_subst s(x, ab, dep); node->apply_subst(sg, s); SASSERT(!node->is_general_conflict()); @@ -3636,15 +3636,15 @@ static void test_simplify_does_not_add_local_parikh_bounds() { euf::sgraph sg(m, eg); seq_util seq(m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // create regex: to_re("AB") — exactly 2 chars - expr_ref ch_a(seq.str.mk_char('A'), m); - expr_ref ch_b(seq.str.mk_char('B'), m); - expr_ref unit_a(seq.str.mk_unit(ch_a), m); - expr_ref unit_b(seq.str.mk_unit(ch_b), m); - expr_ref re_ab(seq.re.mk_concat(seq.re.mk_to_re(unit_a), seq.re.mk_to_re(unit_b)), m); - euf::snode* regex = sg.mk(re_ab); + const expr_ref ch_a(seq.str.mk_char('A'), m); + const expr_ref ch_b(seq.str.mk_char('B'), m); + const expr_ref unit_a(seq.str.mk_unit(ch_a), m); + const expr_ref unit_b(seq.str.mk_unit(ch_b), m); + const expr_ref re_ab(seq.re.mk_concat(seq.re.mk_to_re(unit_a), seq.re.mk_to_re(unit_b)), m); + euf::snode const* regex = sg.mk(re_ab); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3654,7 +3654,7 @@ static void test_simplify_does_not_add_local_parikh_bounds() { seq::nielsen_node* node = ng.root(); // simplify_and_init no longer materializes local IntBounds. - seq::simplify_result sr = node->simplify_and_init({}); + const seq::simplify_result sr = node->simplify_and_init({}); (void)sr; SASSERT(queried_lb(node, x) == 0); @@ -3672,10 +3672,10 @@ static void test_assert_root_constraints_to_solver() { euf::sgraph sg(m, eg); seq_util seq(m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* a = sg.mk_char('a'); - euf::snode* b = sg.mk_char('b'); - euf::snode* ab = sg.mk_concat(a, b); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* a = sg.mk_char('a'); + euf::snode const* b = sg.mk_char('b'); + euf::snode const* ab = sg.mk_concat(a, b); tracking_solver ts(m); seq::context_solver_i context_solver; @@ -3704,8 +3704,8 @@ static void test_assert_root_constraints_once() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); tracking_solver ts(m); seq::context_solver_i context_solver; @@ -3714,7 +3714,7 @@ static void test_assert_root_constraints_once() { // solve is called (iterative deepening runs multiple iterations) ng.solve(); - unsigned count_first = ts.asserted.size(); + const unsigned count_first = ts.asserted.size(); // after reset, assert count should be 0 then non-zero again // (reset clears m_root_constraints_asserted) @@ -3735,9 +3735,9 @@ static void test_subst_does_not_propagate_bounds_multi_var() { euf::egraph eg(m); euf::sgraph sg(m, eg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); - euf::snode* z = sg.mk_var(symbol("z"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* z = sg.mk_var(symbol("z"), sg.get_str_sort()); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3745,14 +3745,14 @@ static void test_subst_does_not_propagate_bounds_multi_var() { ng.add_str_eq(x, y); seq::nielsen_node* node = ng.root(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; // set upper bound: len(x) <= 5 add_len_le(ng, node, x, 5, dep); // apply substitution x → y·z (two vars, no constants) - euf::snode* yz = sg.mk_concat(y, z); - seq::nielsen_subst s(x, yz, dep); + euf::snode const* yz = sg.mk_concat(y, z); + const seq::nielsen_subst s(x, yz, dep); node->apply_subst(sg, s); SASSERT(queried_ub(node, y) == UINT_MAX); @@ -3769,7 +3769,7 @@ static void test_simplify_unit_prefix_split() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); dummy_simple_solver solver; seq::context_solver_i context_solver; @@ -3777,27 +3777,27 @@ static void test_simplify_unit_prefix_split() { // create symbolic char variables a, b (non-concrete -> s_unit) sort* char_sort = seq.mk_char_sort(); - expr_ref sym_a(m.mk_const(symbol("a"), char_sort), m); - expr_ref sym_b(m.mk_const(symbol("b"), char_sort), m); - expr_ref unit_a_expr(seq.str.mk_unit(sym_a), m); - expr_ref unit_b_expr(seq.str.mk_unit(sym_b), m); - euf::snode* ua = sg.mk(unit_a_expr); - euf::snode* ub = sg.mk(unit_b_expr); + const expr_ref sym_a(m.mk_const(symbol("a"), char_sort), m); + const expr_ref sym_b(m.mk_const(symbol("b"), char_sort), m); + const expr_ref unit_a_expr(seq.str.mk_unit(sym_a), m); + const expr_ref unit_b_expr(seq.str.mk_unit(sym_b), m); + euf::snode const* ua = sg.mk(unit_a_expr); + euf::snode const* ub = sg.mk(unit_b_expr); SASSERT(ua->is_unit()); SASSERT(ub->is_unit()); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // ua ++ x = ub ++ y - euf::snode* lhs = sg.mk_concat(ua, x); - euf::snode* rhs = sg.mk_concat(ub, y); + euf::snode const* lhs = sg.mk_concat(ua, x); + euf::snode const* rhs = sg.mk_concat(ub, y); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(lhs, rhs, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::proceed); // original eq stripped to x==y, plus a new unit(a)==unit(b) eq SASSERT(node->str_eqs().size() == 2); @@ -3820,30 +3820,30 @@ static void test_simplify_unit_prefix_split_empty_rest() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); sort* char_sort = seq.mk_char_sort(); - expr_ref sym_a(m.mk_const(symbol("a"), char_sort), m); - expr_ref sym_b(m.mk_const(symbol("b"), char_sort), m); - expr_ref unit_a_expr(seq.str.mk_unit(sym_a), m); - expr_ref unit_b_expr(seq.str.mk_unit(sym_b), m); - euf::snode* ua = sg.mk(unit_a_expr); - euf::snode* ub = sg.mk(unit_b_expr); + const expr_ref sym_a(m.mk_const(symbol("a"), char_sort), m); + const expr_ref sym_b(m.mk_const(symbol("b"), char_sort), m); + const expr_ref unit_a_expr(seq.str.mk_unit(sym_a), m); + const expr_ref unit_b_expr(seq.str.mk_unit(sym_b), m); + euf::snode const* ua = sg.mk(unit_a_expr); + euf::snode const* ub = sg.mk(unit_b_expr); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // ua ++ x = ub (rhs has no rest after unit) - euf::snode* lhs = sg.mk_concat(ua, x); + euf::snode const* lhs = sg.mk_concat(ua, x); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(lhs, ub, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); // unit(a)==unit(b) and x==empty are produced; x==empty forces x->epsilon and satisfied SASSERT(sr == seq::simplify_result::satisfied || sr == seq::simplify_result::proceed); std::cout << " ok\n"; @@ -3857,34 +3857,34 @@ static void test_simplify_unit_suffix_split() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq_util seq(m); + const seq_util seq(m); dummy_simple_solver solver; seq::context_solver_i context_solver; seq::nielsen_graph ng(sg, solver, context_solver); sort* char_sort = seq.mk_char_sort(); - expr_ref sym_a(m.mk_const(symbol("a"), char_sort), m); - expr_ref sym_b(m.mk_const(symbol("b"), char_sort), m); - expr_ref unit_a_expr(seq.str.mk_unit(sym_a), m); - expr_ref unit_b_expr(seq.str.mk_unit(sym_b), m); - euf::snode* ua = sg.mk(unit_a_expr); - euf::snode* ub = sg.mk(unit_b_expr); + const expr_ref sym_a(m.mk_const(symbol("a"), char_sort), m); + const expr_ref sym_b(m.mk_const(symbol("b"), char_sort), m); + const expr_ref unit_a_expr(seq.str.mk_unit(sym_a), m); + const expr_ref unit_b_expr(seq.str.mk_unit(sym_b), m); + euf::snode const* ua = sg.mk(unit_a_expr); + euf::snode const* ub = sg.mk(unit_b_expr); SASSERT(ua->is_unit()); SASSERT(ub->is_unit()); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); - euf::snode* y = sg.mk_var(symbol("y"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* y = sg.mk_var(symbol("y"), sg.get_str_sort()); // x ++ ua = y ++ ub - euf::snode* lhs = sg.mk_concat(x, ua); - euf::snode* rhs = sg.mk_concat(y, ub); + euf::snode const* lhs = sg.mk_concat(x, ua); + euf::snode const* rhs = sg.mk_concat(y, ub); seq::nielsen_node* node = ng.mk_node(); - seq::dep_tracker dep = nullptr; + const seq::dep_tracker dep = nullptr; node->add_str_eq(seq::str_eq(lhs, rhs, dep)); - auto sr = node->simplify_and_init({}); + const auto sr = node->simplify_and_init({}); SASSERT(sr == seq::simplify_result::proceed); // original eq stripped to x==y, plus a new unit(a)==unit(b) eq SASSERT(node->str_eqs().size() == 2); diff --git a/src/test/seq_parikh.cpp b/src/test/seq_parikh.cpp index 50adb23da..c2b98fba9 100644 --- a/src/test/seq_parikh.cpp +++ b/src/test/seq_parikh.cpp @@ -318,9 +318,9 @@ static void test_generate_constraints_ab_star() { arith_util arith(m); seq::seq_parikh parikh(sg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); expr_ref re = mk_ab_star(m, seq); - euf::snode* regex = sg.mk(re); + euf::snode const* regex = sg.mk(re); seq::dep_manager dm; sat::literal lit = sat::null_literal; // dummy literal for dependency tracking seq::dep_tracker dep = dm.mk_leaf(lit); @@ -364,11 +364,11 @@ static void test_generate_constraints_bounded_loop() { arith_util arith(m); seq::seq_parikh parikh(sg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // loop("ab", 1, 3): min_len=2, max_len=6, stride=2 expr_ref ab = mk_to_re_ab(m, seq); expr_ref re(seq.re.mk_loop(ab, 1, 3), m); - euf::snode* regex = sg.mk(re); + euf::snode const* regex = sg.mk(re); seq::dep_manager dm; seq::dep_tracker dep = dm.mk_leaf(sat::null_literal); seq::str_mem mem(x, regex, dep); @@ -402,10 +402,10 @@ static void test_generate_constraints_stride_one() { seq::seq_parikh parikh(sg); sort_ref str_sort(seq.str.mk_string_sort(), m); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); // full_seq: stride=1 → no modular constraint expr_ref re(seq.re.mk_full_seq(str_sort), m); - euf::snode* regex = sg.mk(re); + euf::snode const* regex = sg.mk(re); seq::dep_manager dm; seq::dep_tracker dep = dm.mk_leaf(sat::null_literal); seq::str_mem mem(x, regex, dep); @@ -426,9 +426,9 @@ static void test_generate_constraints_fixed_length() { seq_util seq(m); seq::seq_parikh parikh(sg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); expr_ref re = mk_to_re_ab(m, seq); // fixed len 2 - euf::snode* regex = sg.mk(re); + euf::snode const* regex = sg.mk(re); seq::dep_manager dm; seq::dep_tracker dep = dm.mk_leaf(sat::null_literal); seq::str_mem mem(x, regex, dep); @@ -449,9 +449,9 @@ static void test_generate_constraints_dep_propagated() { seq_util seq(m); seq::seq_parikh parikh(sg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); expr_ref re = mk_ab_star(m, seq); - euf::snode* regex = sg.mk(re); + euf::snode const* regex = sg.mk(re); seq::dep_manager dm; sat::literal lit(7); seq::dep_tracker dep = dm.mk_leaf(lit); @@ -490,9 +490,9 @@ static void test_apply_to_node_adds_constraints() { seq::nielsen_graph ng(sg, solver, context_solver); seq::seq_parikh parikh(sg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); expr_ref re = mk_ab_star(m, seq); // stride 2 → generates constraints - euf::snode* regex = sg.mk(re); + euf::snode const* regex = sg.mk(re); ng.add_str_mem(x, regex); // root node should have no int_constraints initially @@ -523,9 +523,9 @@ static void test_apply_to_node_stride_one_no_constraints() { seq::nielsen_graph ng(sg, solver, context_solver); seq::seq_parikh parikh(sg); - euf::snode* x = sg.mk_var(symbol("x"), sg.get_str_sort()); + euf::snode const* x = sg.mk_var(symbol("x"), sg.get_str_sort()); expr_ref re(seq.re.mk_full_seq(str_sort), m); // stride 1 → no constraints - euf::snode* regex = sg.mk(re); + euf::snode const* regex = sg.mk(re); ng.add_str_mem(x, regex); unsigned before = ng.root()->constraints().size(); diff --git a/src/test/seq_regex.cpp b/src/test/seq_regex.cpp index e2ec36c5e..ba4e37aeb 100644 --- a/src/test/seq_regex.cpp +++ b/src/test/seq_regex.cpp @@ -42,13 +42,13 @@ static void test_seq_regex_is_empty() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq::seq_regex nr(sg); + const seq::seq_regex nr(sg); seq_util su(m); sort* str_sort = su.str.mk_string_sort(); // re.none is the empty language - expr_ref none_e(su.re.mk_empty(su.re.mk_re(str_sort)), m); - euf::snode* none_n = sg.mk(none_e.get()); + const expr_ref none_e(su.re.mk_empty(su.re.mk_re(str_sort)), m); + euf::snode const* none_n = sg.mk(none_e.get()); SASSERT(nr.is_empty_regex(none_n)); std::cout << " ok: re.none recognized as empty\n"; } @@ -60,13 +60,13 @@ static void test_seq_regex_is_full() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq::seq_regex nr(sg); + const seq::seq_regex nr(sg); seq_util su(m); sort* str_sort = su.str.mk_string_sort(); // re.all (full sequence regex) is not empty - expr_ref full_e(su.re.mk_full_seq(su.re.mk_re(str_sort)), m); - euf::snode* full_n = sg.mk(full_e.get()); + const expr_ref full_e(su.re.mk_full_seq(su.re.mk_re(str_sort)), m); + euf::snode const* full_n = sg.mk(full_e.get()); SASSERT(!nr.is_empty_regex(full_n)); std::cout << " ok: re.all not recognized as empty\n"; } @@ -85,8 +85,8 @@ static void test_strengthened_stabilizer_null() { seq_util su(m); sort* str_sort = su.str.mk_string_sort(); sort* re_sort = su.re.mk_re(str_sort); - expr_ref full_e(su.re.mk_full_seq(re_sort), m); - euf::snode* full_re = sg.mk(full_e); + const expr_ref full_e(su.re.mk_full_seq(re_sort), m); + euf::snode const* full_re = sg.mk(full_e); SASSERT(nr.strengthened_stabilizer(full_re, nullptr) == nullptr); SASSERT(nr.strengthened_stabilizer(nullptr, full_re) == nullptr); @@ -106,14 +106,14 @@ static void test_strengthened_stabilizer_single_char() { seq_util su(m); // Build a* - expr_ref star_a(su.re.mk_star(su.re.mk_to_re(su.str.mk_string("a"))), m); - euf::snode* re_star_a = sg.mk(star_a); + const expr_ref star_a(su.re.mk_star(su.re.mk_to_re(su.str.mk_string("a"))), m); + euf::snode const* re_star_a = sg.mk(star_a); // Build history = char 'a' (single token, no concat needed) - euf::snode* tok_a = sg.mk_char('a'); - euf::snode* history = tok_a; + euf::snode const* tok_a = sg.mk_char('a'); + euf::snode const* history = tok_a; - euf::snode* result = nr.strengthened_stabilizer(re_star_a, history); + euf::snode const* result = nr.strengthened_stabilizer(re_star_a, history); // Should produce a non-null stabilizer body (to_re("a")) SASSERT(result != nullptr); std::cout << " ok: a* with history 'a' -> non-null stabilizer\n"; @@ -132,16 +132,16 @@ static void test_strengthened_stabilizer_two_char() { seq_util su(m); // Build (ab)* - expr_ref ab(su.re.mk_to_re(su.str.mk_string("ab")), m); - expr_ref star_ab(su.re.mk_star(ab), m); - euf::snode* re_star_ab = sg.mk(star_ab); + const expr_ref ab(su.re.mk_to_re(su.str.mk_string("ab")), m); + const expr_ref star_ab(su.re.mk_star(ab), m); + euf::snode const* re_star_ab = sg.mk(star_ab); // Build history: concat(char_a, char_b) using string concat - euf::snode* tok_a = sg.mk_char('a'); - euf::snode* tok_b = sg.mk_char('b'); - euf::snode* history = sg.mk_concat(tok_a, tok_b); + euf::snode const* tok_a = sg.mk_char('a'); + euf::snode const* tok_b = sg.mk_char('b'); + euf::snode const* history = sg.mk_concat(tok_a, tok_b); - euf::snode* result = nr.strengthened_stabilizer(re_star_ab, history); + euf::snode const* result = nr.strengthened_stabilizer(re_star_ab, history); // Should produce a non-null stabilizer body SASSERT(result != nullptr); std::cout << " ok: (ab)* with history 'ab' -> non-null stabilizer\n"; @@ -154,16 +154,16 @@ static void test_filtered_stabilizer_star_empty() { reg_decl_plugins(m); euf::egraph eg(m); euf::sgraph sg(m, eg); - seq::seq_regex nr(sg); + const seq::seq_regex nr(sg); seq_util su(m); sort* str_sort = su.str.mk_string_sort(); sort* re_sort = su.re.mk_re(str_sort); - expr_ref full_e(su.re.mk_full_seq(re_sort), m); - euf::snode* full_re = sg.mk(full_e); - euf::snode* tok_a = sg.mk_char('a'); + const expr_ref full_e(su.re.mk_full_seq(re_sort), m); + euf::snode const* full_re = sg.mk(full_e); + euf::snode const* tok_a = sg.mk_char('a'); - euf::snode* result = nr.get_filtered_stabilizer_star(full_re, tok_a); + euf::snode const* result = nr.get_filtered_stabilizer_star(full_re, tok_a); SASSERT(result == nullptr); std::cout << " ok: no stabilizers -> nullptr\n"; } @@ -179,17 +179,17 @@ static void test_filtered_stabilizer_star_with_stab() { seq_util su(m); // Build a* as the regex state - expr_ref star_a(su.re.mk_star(su.re.mk_to_re(su.str.mk_string("a"))), m); - euf::snode* re_star_a = sg.mk(star_a); + const expr_ref star_a(su.re.mk_star(su.re.mk_to_re(su.str.mk_string("a"))), m); + euf::snode const* re_star_a = sg.mk(star_a); // Register a stabilizer: to_re("b") — only accepts "b" - expr_ref stab_b(su.re.mk_to_re(su.str.mk_string("b")), m); - euf::snode* stab_b_sn = sg.mk(stab_b); + const expr_ref stab_b(su.re.mk_to_re(su.str.mk_string("b")), m); + euf::snode const* stab_b_sn = sg.mk(stab_b); nr.add_stabilizer(re_star_a, stab_b_sn); // Exclude char 'a': D('a', to_re("b")) should be fail - euf::snode* tok_a = sg.mk_char('a'); - euf::snode* result = nr.get_filtered_stabilizer_star(re_star_a, tok_a); + euf::snode const* tok_a = sg.mk_char('a'); + euf::snode const* result = nr.get_filtered_stabilizer_star(re_star_a, tok_a); // to_re("b") should pass the filter → result is star(to_re("b")) SASSERT(result != nullptr); SASSERT(result->is_star()); @@ -207,17 +207,17 @@ static void test_filtered_stabilizer_star_filtered() { seq_util su(m); // Build a* as the regex state - expr_ref star_a(su.re.mk_star(su.re.mk_to_re(su.str.mk_string("a"))), m); - euf::snode* re_star_a = sg.mk(star_a); + const expr_ref star_a(su.re.mk_star(su.re.mk_to_re(su.str.mk_string("a"))), m); + euf::snode const* re_star_a = sg.mk(star_a); // Register a stabilizer: to_re("a") — accepts "a" - expr_ref stab_a(su.re.mk_to_re(su.str.mk_string("a")), m); - euf::snode* stab_a_sn = sg.mk(stab_a); + const expr_ref stab_a(su.re.mk_to_re(su.str.mk_string("a")), m); + euf::snode const* stab_a_sn = sg.mk(stab_a); nr.add_stabilizer(re_star_a, stab_a_sn); // Exclude char 'a': D('a', to_re("a")) is NOT fail → filtered out - euf::snode* tok_a = sg.mk_char('a'); - euf::snode* result = nr.get_filtered_stabilizer_star(re_star_a, tok_a); + euf::snode const* tok_a = sg.mk_char('a'); + euf::snode const* result = nr.get_filtered_stabilizer_star(re_star_a, tok_a); SASSERT(result == nullptr); std::cout << " ok: filter removes to_re('a') when excluding 'a'\n"; } @@ -234,26 +234,26 @@ static void test_extract_cycle_history_basic() { sort* str_sort = su.str.mk_string_sort(); sort* re_sort = su.re.mk_re(str_sort); - expr_ref full_e(su.re.mk_full_seq(re_sort), m); - euf::snode* full_re = sg.mk(full_e); + const expr_ref full_e(su.re.mk_full_seq(re_sort), m); + euf::snode const* full_re = sg.mk(full_e); - euf::snode* tok_a = sg.mk_char('a'); - euf::snode* tok_b = sg.mk_char('b'); - euf::snode* tok_c = sg.mk_char('c'); + euf::snode const* tok_a = sg.mk_char('a'); + euf::snode const* tok_b = sg.mk_char('b'); + euf::snode const* tok_c = sg.mk_char('c'); // Ancestor history: just 'a' (length 1) - euf::snode* anc_hist = tok_a; + euf::snode const* anc_hist = tok_a; // Current history: concat(concat(a, b), c) = a,b,c (length 3) - euf::snode* cur_hist = sg.mk_concat(sg.mk_concat(tok_a, tok_b), tok_c); + euf::snode const* cur_hist = sg.mk_concat(sg.mk_concat(tok_a, tok_b), tok_c); - euf::snode* empty_str = sg.mk_empty_seq(str_sort); - seq::dep_tracker empty_dep = nullptr; + euf::snode const* empty_str = sg.mk_empty_seq(str_sort); + const seq::dep_tracker empty_dep = nullptr; - seq::str_mem ancestor(empty_str, full_re, empty_dep); - seq::str_mem current(empty_str, full_re, empty_dep); + const seq::str_mem ancestor(empty_str, full_re, empty_dep); + const seq::str_mem current(empty_str, full_re, empty_dep); - euf::snode* cycle = nr.extract_cycle_history(current, ancestor); + euf::snode const* cycle = nr.extract_cycle_history(current, ancestor); // Should return the last 2 tokens (b, c) SASSERT(cycle != nullptr); SASSERT(cycle->length() == 2); @@ -272,20 +272,20 @@ static void test_extract_cycle_history_null_ancestor() { sort* str_sort = su.str.mk_string_sort(); sort* re_sort = su.re.mk_re(str_sort); - expr_ref full_e(su.re.mk_full_seq(re_sort), m); - euf::snode* full_re = sg.mk(full_e); + const expr_ref full_e(su.re.mk_full_seq(re_sort), m); + euf::snode const* full_re = sg.mk(full_e); - euf::snode* tok_a = sg.mk_char('a'); - euf::snode* tok_b = sg.mk_char('b'); - euf::snode* cur_hist = sg.mk_concat(tok_a, tok_b); - euf::snode* empty_str = sg.mk_empty_seq(str_sort); - seq::dep_tracker empty_dep = nullptr; + euf::snode const* tok_a = sg.mk_char('a'); + euf::snode const* tok_b = sg.mk_char('b'); + euf::snode const* cur_hist = sg.mk_concat(tok_a, tok_b); + euf::snode const* empty_str = sg.mk_empty_seq(str_sort); + const seq::dep_tracker empty_dep = nullptr; // Ancestor has no history (nullptr) - seq::str_mem ancestor(empty_str, full_re, empty_dep); - seq::str_mem current(empty_str, full_re, empty_dep); + const seq::str_mem ancestor(empty_str, full_re, empty_dep); + const seq::str_mem current(empty_str, full_re, empty_dep); - euf::snode* cycle = nr.extract_cycle_history(current, ancestor); + euf::snode const* cycle = nr.extract_cycle_history(current, ancestor); // With null ancestor history, entire current history is the cycle SASSERT(cycle != nullptr); SASSERT(cycle->length() == 2); @@ -304,9 +304,9 @@ static void test_bfs_empty_none() { sort* str_sort = su.str.mk_string_sort(); sort* re_sort = su.re.mk_re(str_sort); - expr_ref none_e(su.re.mk_empty(re_sort), m); - euf::snode* none_re = sg.mk(none_e); - lbool result = nr.is_empty_bfs(none_re); + const expr_ref none_e(su.re.mk_empty(re_sort), m); + euf::snode const* none_re = sg.mk(none_e); + const lbool result = nr.is_empty_bfs(none_re); SASSERT(result == l_true); std::cout << " ok: re.none -> l_true (empty)\n"; } @@ -323,9 +323,9 @@ static void test_bfs_nonempty_full() { sort* str_sort = su.str.mk_string_sort(); sort* re_sort = su.re.mk_re(str_sort); - expr_ref full_e(su.re.mk_full_seq(re_sort), m); - euf::snode* full_re = sg.mk(full_e); - lbool result = nr.is_empty_bfs(full_re); + const expr_ref full_e(su.re.mk_full_seq(re_sort), m); + euf::snode const* full_re = sg.mk(full_e); + const lbool result = nr.is_empty_bfs(full_re); SASSERT(result == l_false); std::cout << " ok: full_seq -> l_false (non-empty)\n"; } @@ -340,9 +340,9 @@ static void test_bfs_nonempty_to_re() { seq::seq_regex nr(sg); seq_util su(m); - expr_ref to_re_abc(su.re.mk_to_re(su.str.mk_string("abc")), m); - euf::snode* re_abc = sg.mk(to_re_abc); - lbool result = nr.is_empty_bfs(re_abc); + const expr_ref to_re_abc(su.re.mk_to_re(su.str.mk_string("abc")), m); + euf::snode const* re_abc = sg.mk(to_re_abc); + const lbool result = nr.is_empty_bfs(re_abc); SASSERT(result == l_false); std::cout << " ok: to_re(\"abc\") -> l_false (non-empty)\n"; } @@ -357,9 +357,9 @@ static void test_bfs_nonempty_star() { seq::seq_regex nr(sg); seq_util su(m); - expr_ref star_a(su.re.mk_star(su.re.mk_to_re(su.str.mk_string("a"))), m); - euf::snode* re_star_a = sg.mk(star_a); - lbool result = nr.is_empty_bfs(re_star_a); + const expr_ref star_a(su.re.mk_star(su.re.mk_to_re(su.str.mk_string("a"))), m); + euf::snode const* re_star_a = sg.mk(star_a); + const lbool result = nr.is_empty_bfs(re_star_a); SASSERT(result == l_false); std::cout << " ok: a* -> l_false (non-empty, accepts epsilon)\n"; } @@ -376,11 +376,11 @@ static void test_bfs_empty_union_of_empties() { sort* str_sort = su.str.mk_string_sort(); sort* re_sort = su.re.mk_re(str_sort); - expr_ref none1(su.re.mk_empty(re_sort), m); - expr_ref none2(su.re.mk_empty(re_sort), m); - expr_ref union_e(su.re.mk_union(none1, none2), m); - euf::snode* re_union = sg.mk(union_e); - lbool result = nr.is_empty_bfs(re_union); + const expr_ref none1(su.re.mk_empty(re_sort), m); + const expr_ref none2(su.re.mk_empty(re_sort), m); + const expr_ref union_e(su.re.mk_union(none1, none2), m); + euf::snode const* re_union = sg.mk(union_e); + const lbool result = nr.is_empty_bfs(re_union); SASSERT(result == l_true); std::cout << " ok: union(none, none) -> l_true (empty)\n"; } @@ -396,11 +396,11 @@ static void test_bfs_nonempty_range() { seq_util su(m); sort* str_sort = su.str.mk_string_sort(); - expr_ref lo(su.mk_char('a'), m); - expr_ref hi(su.mk_char('z'), m); - expr_ref range_e(su.re.mk_range(su.str.mk_unit(lo), su.str.mk_unit(hi)), m); - euf::snode* re_range = sg.mk(range_e); - lbool result = nr.is_empty_bfs(re_range); + const expr_ref lo(su.mk_char('a'), m); + const expr_ref hi(su.mk_char('z'), m); + const expr_ref range_e(su.re.mk_range(su.str.mk_unit(lo), su.str.mk_unit(hi)), m); + euf::snode const* re_range = sg.mk(range_e); + const lbool result = nr.is_empty_bfs(re_range); SASSERT(result == l_false); std::cout << " ok: range('a','z') -> l_false (non-empty)\n"; } @@ -417,9 +417,9 @@ static void test_bfs_empty_complement_full() { sort* str_sort = su.str.mk_string_sort(); sort* re_sort = su.re.mk_re(str_sort); - expr_ref comp_full(su.re.mk_complement(su.re.mk_full_seq(re_sort)), m); - euf::snode* re_comp = sg.mk(comp_full); - lbool result = nr.is_empty_bfs(re_comp); + const expr_ref comp_full(su.re.mk_complement(su.re.mk_full_seq(re_sort)), m); + euf::snode const* re_comp = sg.mk(comp_full); + const lbool result = nr.is_empty_bfs(re_comp); SASSERT(result == l_true); std::cout << " ok: ~full_seq -> l_true (empty)\n"; } @@ -433,7 +433,7 @@ static void test_bfs_null_safety() { euf::sgraph sg(m, eg); seq::seq_regex nr(sg); - lbool result = nr.is_empty_bfs(nullptr); + const lbool result = nr.is_empty_bfs(nullptr); SASSERT(result == l_undef); std::cout << " ok: nullptr -> l_undef\n"; } @@ -449,13 +449,13 @@ static void test_bfs_bounded() { seq_util su(m); // (a|b)+ requires at least one char; with max_states=1 should bail - expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); - expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); - expr_ref ab_union(su.re.mk_union(a_re, b_re), m); - expr_ref ab_plus(su.re.mk_plus(ab_union), m); - euf::snode* re_plus = sg.mk(ab_plus); + const expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); + const expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); + const expr_ref ab_union(su.re.mk_union(a_re, b_re), m); + const expr_ref ab_plus(su.re.mk_plus(ab_union), m); + euf::snode const* re_plus = sg.mk(ab_plus); - lbool result = nr.is_empty_bfs(re_plus, 1); + const lbool result = nr.is_empty_bfs(re_plus, 1); SASSERT(result == l_undef); std::cout << " ok: (a|b)+ with max_states=1 -> l_undef (bounded)\n"; } @@ -469,13 +469,13 @@ static void test_char_set_is_subset() { std::cout << "test_char_set_is_subset\n"; // {a} ⊆ {a,b,c} = [97,100) - char_set cs1(char_range('a', 'b')); // {a} - char_set cs2(char_range('a', 'd')); // {a,b,c} + const char_set cs1(char_range('a', 'b')); // {a} + const char_set cs2(char_range('a', 'd')); // {a,b,c} SASSERT(cs1.is_subset(cs2)); SASSERT(!cs2.is_subset(cs1)); // empty ⊆ anything - char_set empty; + const char_set empty; SASSERT(empty.is_subset(cs1)); SASSERT(empty.is_subset(cs2)); @@ -484,7 +484,7 @@ static void test_char_set_is_subset() { SASSERT(cs2.is_subset(cs2)); // disjoint: {x} not ⊆ {a} - char_set cs3(char_range('x', 'y')); + const char_set cs3(char_range('x', 'y')); SASSERT(!cs3.is_subset(cs1)); std::cout << " ok\n"; @@ -500,10 +500,10 @@ static void test_stabilizer_store_basic() { seq::seq_regex nr(sg); seq_util su(m); - expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); - expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); - euf::snode* a_sn = sg.mk(a_re); - euf::snode* b_sn = sg.mk(b_re); + const expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); + const expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); + euf::snode const* a_sn = sg.mk(a_re); + euf::snode const* b_sn = sg.mk(b_re); SASSERT(!nr.has_stabilizers(a_sn)); nr.add_stabilizer(a_sn, b_sn); @@ -532,16 +532,16 @@ static void test_self_stabilizing() { seq::seq_regex nr(sg); seq_util su(m); - expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); - euf::snode* a_sn = sg.mk(a_re); + const expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); + euf::snode const* a_sn = sg.mk(a_re); SASSERT(!nr.is_self_stabilizing(a_sn)); nr.set_self_stabilizing(a_sn); SASSERT(nr.is_self_stabilizing(a_sn)); // star should be detected as self-stabilizing - expr_ref star_a(su.re.mk_star(a_re), m); - euf::snode* star_sn = sg.mk(star_a); + const expr_ref star_a(su.re.mk_star(a_re), m); + euf::snode const* star_sn = sg.mk(star_a); SASSERT(nr.compute_self_stabilizing(star_sn)); std::cout << " ok\n"; @@ -558,19 +558,19 @@ static void test_check_intersection_sat() { seq_util su(m); // a* ∩ (a|b)* should be non-empty (both accept "a") - expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); - expr_ref star_a(su.re.mk_star(a_re), m); - expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); - expr_ref ab_union(su.re.mk_union(a_re, b_re), m); - expr_ref star_ab(su.re.mk_star(ab_union), m); + const expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); + const expr_ref star_a(su.re.mk_star(a_re), m); + const expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); + const expr_ref ab_union(su.re.mk_union(a_re, b_re), m); + const expr_ref star_ab(su.re.mk_star(ab_union), m); - euf::snode* s1 = sg.mk(star_a); - euf::snode* s2 = sg.mk(star_ab); - ptr_vector regexes; + euf::snode const* s1 = sg.mk(star_a); + euf::snode const* s2 = sg.mk(star_ab); + euf::snode_vector regexes; regexes.push_back(s1); regexes.push_back(s2); - lbool result = nr.check_intersection_emptiness(regexes, UINT_MAX); + const lbool result = nr.check_intersection_emptiness(regexes, UINT_MAX); SASSERT(result == l_false); // non-empty std::cout << " ok: a* ∩ (a|b)* is non-empty\n"; } @@ -587,15 +587,15 @@ static void test_check_intersection_unsat() { sort* str_sort = su.str.mk_string_sort(); // to_re("a") ∩ to_re("b") should be empty - expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); - expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); - euf::snode* s1 = sg.mk(a_re); - euf::snode* s2 = sg.mk(b_re); - ptr_vector regexes; + const expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); + const expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); + euf::snode const* s1 = sg.mk(a_re); + euf::snode const* s2 = sg.mk(b_re); + euf::snode_vector regexes; regexes.push_back(s1); regexes.push_back(s2); - lbool result = nr.check_intersection_emptiness(regexes, UINT_MAX); + const lbool result = nr.check_intersection_emptiness(regexes, UINT_MAX); SASSERT(result == l_true); // empty std::cout << " ok: to_re(a) ∩ to_re(b) is empty\n"; } @@ -611,16 +611,16 @@ static void test_is_language_subset_true() { seq_util su(m); // a* ⊆ (a|b)* should be true - expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); - expr_ref star_a(su.re.mk_star(a_re), m); - expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); - expr_ref ab_union(su.re.mk_union(a_re, b_re), m); - expr_ref star_ab(su.re.mk_star(ab_union), m); + const expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); + const expr_ref star_a(su.re.mk_star(a_re), m); + const expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); + const expr_ref ab_union(su.re.mk_union(a_re, b_re), m); + const expr_ref star_ab(su.re.mk_star(ab_union), m); - euf::snode* subset = sg.mk(star_a); - euf::snode* superset = sg.mk(star_ab); + euf::snode const* subset = sg.mk(star_a); + euf::snode const* superset = sg.mk(star_ab); - lbool result = nr.is_language_subset(subset, superset); + const lbool result = nr.is_language_subset(subset, superset); SASSERT(result == l_true); std::cout << " ok: a* ⊆ (a|b)*\n"; } @@ -636,16 +636,16 @@ static void test_is_language_subset_false() { seq_util su(m); // (a|b)* ⊄ a* should be false (b ∈ (a|b)* but b ∉ a*) - expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); - expr_ref star_a(su.re.mk_star(a_re), m); - expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); - expr_ref ab_union(su.re.mk_union(a_re, b_re), m); - expr_ref star_ab(su.re.mk_star(ab_union), m); + const expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); + const expr_ref star_a(su.re.mk_star(a_re), m); + const expr_ref b_re(su.re.mk_to_re(su.str.mk_string("b")), m); + const expr_ref ab_union(su.re.mk_union(a_re, b_re), m); + const expr_ref star_ab(su.re.mk_star(ab_union), m); - euf::snode* subset = sg.mk(star_ab); - euf::snode* superset = sg.mk(star_a); + euf::snode const* subset = sg.mk(star_ab); + euf::snode const* superset = sg.mk(star_a); - lbool result = nr.is_language_subset(subset, superset); + const lbool result = nr.is_language_subset(subset, superset); SASSERT(result == l_false); std::cout << " ok: (a|b)* ⊄ a*\n"; } @@ -662,15 +662,15 @@ static void test_is_language_subset_trivial() { sort* str_sort = su.str.mk_string_sort(); // ∅ ⊆ anything = true - expr_ref none(su.re.mk_empty(su.re.mk_re(str_sort)), m); - expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); - euf::snode* empty_sn = sg.mk(none); - euf::snode* a_sn = sg.mk(a_re); + const expr_ref none(su.re.mk_empty(su.re.mk_re(str_sort)), m); + const expr_ref a_re(su.re.mk_to_re(su.str.mk_string("a")), m); + euf::snode const* empty_sn = sg.mk(none); + euf::snode const* a_sn = sg.mk(a_re); SASSERT(nr.is_language_subset(empty_sn, a_sn) == l_true); // anything ⊆ Σ* = true - expr_ref full(su.re.mk_full_seq(su.re.mk_re(str_sort)), m); - euf::snode* full_sn = sg.mk(full); + const expr_ref full(su.re.mk_full_seq(su.re.mk_re(str_sort)), m); + euf::snode const* full_sn = sg.mk(full); SASSERT(nr.is_language_subset(a_sn, full_sn) == l_true); // L ⊆ L = true (same pointer) @@ -689,14 +689,14 @@ static void test_some_seq_in_re_excluded_low_regression() { seq_rewriter rw(m); th_rewriter tr(m); - expr_ref low(su.mk_char('A'), m); - expr_ref high(su.mk_char('Z'), m); - expr_ref range_az(su.re.mk_range(su.str.mk_unit(low), su.str.mk_unit(high)), m); - expr_ref not_a(su.re.mk_complement(su.re.mk_to_re(su.str.mk_string("A"))), m); - expr_ref re_expr(su.re.mk_inter(not_a, range_az), m); + const expr_ref low(su.mk_char('A'), m); + const expr_ref high(su.mk_char('Z'), m); + const expr_ref range_az(su.re.mk_range(su.str.mk_unit(low), su.str.mk_unit(high)), m); + const expr_ref not_a(su.re.mk_complement(su.re.mk_to_re(su.str.mk_string("A"))), m); + const expr_ref re_expr(su.re.mk_inter(not_a, range_az), m); expr_ref witness(m); - lbool wr = rw.some_seq_in_re(re_expr, witness); + const lbool wr = rw.some_seq_in_re(re_expr, witness); SASSERT(wr == l_true); SASSERT(witness); @@ -704,7 +704,7 @@ static void test_some_seq_in_re_excluded_low_regression() { SASSERT(su.str.is_string(witness, ws)); SASSERT(ws != zstring("A")); - expr_ref in_re(su.re.mk_in_re(witness, re_expr), m); + const expr_ref in_re(su.re.mk_in_re(witness, re_expr), m); expr_ref in_re_simpl(m); tr(in_re, in_re_simpl); SASSERT(m.is_true(in_re_simpl)); @@ -730,8 +730,8 @@ static void test_some_seq_in_re_inter_loop_regression() { return expr_ref(su.re.mk_to_re(su.str.mk_string(s)), m); }; auto mk_range = [&](const char* lo, const char* hi) -> expr_ref { - expr_ref l(su.mk_char(lo[0]), m); - expr_ref h(su.mk_char(hi[0]), m); + const expr_ref l(su.mk_char(lo[0]), m); + const expr_ref h(su.mk_char(hi[0]), m); return expr_ref(su.re.mk_range(su.str.mk_unit(l), su.str.mk_unit(h)), m); }; auto cat = [&](expr* a, expr* b) -> expr_ref {