diff --git a/src/ast/euf/euf_seq_plugin.cpp b/src/ast/euf/euf_seq_plugin.cpp index 618b1699a..0a025c5c7 100644 --- a/src/ast/euf/euf_seq_plugin.cpp +++ b/src/ast/euf/euf_seq_plugin.cpp @@ -212,11 +212,16 @@ namespace euf { if (same_star_body(a, b)) push_merge(n, a); - // Rule 1 extended: concat(v*, concat(v*, c)) = concat(v*, c) + // Rule 1 extended (right): concat(v*, concat(v*, c)) = concat(v*, c) enode* b1, *b2; if (is_concat(b, b1, b2) && same_star_body(a, b1)) push_merge(n, b); + // Rule 1 extended (left): concat(concat(c, v*), v*) = concat(c, v*) + enode* a1, *a2; + if (is_concat(a, a1, a2) && same_star_body(a2, b)) + push_merge(n, a); + // Rule 2: Nullable absorption by .* // concat(.*, v) = .* when v is nullable if (is_full_seq(a) && is_nullable(b)) diff --git a/src/ast/euf/euf_sgraph.cpp b/src/ast/euf/euf_sgraph.cpp index eff926004..b332cd2a4 100644 --- a/src/ast/euf/euf_sgraph.cpp +++ b/src/ast/euf/euf_sgraph.cpp @@ -200,13 +200,18 @@ namespace euf { n->m_length = 1; break; - case snode_kind::s_loop: + case snode_kind::s_loop: { + bool body_nullable = n->num_args() > 0 && n->arg(0)->is_nullable(); + unsigned lo = 0, hi = 0; + expr* body = nullptr; + bool lo_zero = n->get_expr() && m_seq.re.is_loop(n->get_expr(), body, lo, hi) && lo == 0; n->m_ground = n->num_args() > 0 ? n->arg(0)->is_ground() : true; n->m_regex_free = false; - n->m_nullable = false; // depends on lower bound + n->m_nullable = lo_zero || body_nullable; n->m_level = 1; n->m_length = 1; break; + } case snode_kind::s_union: SASSERT(n->num_args() == 2); @@ -461,13 +466,17 @@ namespace euf { } snode* sgraph::drop_left(snode* n, unsigned count) { - for (unsigned i = 0; i < count && !n->is_empty(); ++i) + if (count == 0 || n->is_empty()) return n; + if (count >= n->length()) return mk_empty(); + for (unsigned i = 0; i < count; ++i) n = drop_first(n); return n; } snode* sgraph::drop_right(snode* n, unsigned count) { - for (unsigned i = 0; i < count && !n->is_empty(); ++i) + if (count == 0 || n->is_empty()) return n; + if (count >= n->length()) return mk_empty(); + for (unsigned i = 0; i < count; ++i) n = drop_last(n); return n; }