summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndres Noetzli <andres.noetzli@gmail.com>2019-03-29 17:00:23 -0700
committerAndres Noetzli <andres.noetzli@gmail.com>2019-04-17 08:44:54 -0700
commitdfcfbaec28a42c693f3aa36aef38283e9722bd39 (patch)
tree936cfd482cb0fcdd9fe22c5d002d48ee937f9750 /src
parentc2b5995b59b47ae16d66c5ae77199f801bf11a17 (diff)
Add rewrite for splitting equalities
This commit adds an extended rewrite that splits a given string equality into multiple equalities if it can find prefixes of equal lengths. For example: `(= (str.++ x "AB" z) (str.++ "A" x y)) ---> (and (= (str.++ x "A") (str.++ "A" x)) (= (str.++ "B" z) y))`
Diffstat (limited to 'src')
-rw-r--r--src/theory/strings/theory_strings_rewriter.cpp100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/theory/strings/theory_strings_rewriter.cpp b/src/theory/strings/theory_strings_rewriter.cpp
index ec9a46e3a..94e43be36 100644
--- a/src/theory/strings/theory_strings_rewriter.cpp
+++ b/src/theory/strings/theory_strings_rewriter.cpp
@@ -592,6 +592,106 @@ Node TheoryStringsRewriter::rewriteStrEqualityExt(Node node)
}
}
+ if (node[0].getKind() == STRING_CONCAT && node[1].getKind() == STRING_CONCAT)
+ {
+ // (= (str.++ x_1 ... x_i x_{i + 1} ... x_n)
+ // (str.++ y_1 ... y_j y_{j + 1} ... y_m)) --->
+ // (and (= (str.++ x_1 ... x_i) (str.++ y_1 ... y_j))
+ // (= (str.++ x_{i + 1} ... x_n) (str.++ y_{j + 1} ... y_m)))
+ //
+ // if (str.len (str.++ x_1 ... x_i)) = (str.len (str.++ y_1 ... y_j))
+ //
+ // This rewrite performs length-based equality splitting: If we can show
+ // that two prefixes have the same length, we can split an equality into
+ // two equalities, one over the prefixes and another over the suffixes.
+ std::vector<Node> v0, v1;
+ getConcat(node[0], v0);
+ getConcat(node[1], v1);
+ size_t startRhs = 0;
+ for (size_t i = 0, size0 = v0.size(); i <= size0; i++)
+ {
+ std::vector<Node> pfxv0(v0.begin(), v0.begin() + i);
+ Node pfx0 = mkConcat(STRING_CONCAT, pfxv0);
+ for (size_t j = startRhs, size1 = v1.size(); j <= size1; j++)
+ {
+ if (!(i == 0 && j == 0) && !(i == v0.size() && j == v1.size()))
+ {
+ std::vector<Node> pfxv1(v1.begin(), v1.begin() + j);
+ Node pfx1 = mkConcat(STRING_CONCAT, pfxv1);
+ Node lenPfx0 = nm->mkNode(STRING_LENGTH, pfx0);
+ Node lenPfx1 = nm->mkNode(STRING_LENGTH, pfx1);
+
+ if (checkEntailArithEq(lenPfx0, lenPfx1))
+ {
+ std::vector<Node> sfxv0(v0.begin() + i, v0.end());
+ std::vector<Node> sfxv1(v1.begin() + j, v1.end());
+ Node ret = nm->mkNode(kind::AND,
+ pfx0.eqNode(pfx1),
+ mkConcat(STRING_CONCAT, sfxv0)
+ .eqNode(mkConcat(STRING_CONCAT, sfxv1)));
+ return returnRewrite(node, ret, "split-eq");
+ }
+ else if (checkEntailArith(lenPfx1, lenPfx0, true))
+ {
+ // The prefix on the right-hand side is strictly longer than the
+ // prefix on the left-hand side, so we try to strip the right-hand
+ // prefix by the length of the left-hand prefix
+ //
+ // Example:
+ // (= (str.++ "A" x y) (str.++ x "AB" z)) --->
+ // (and (= (str.++ "A" x) (str.++ x "A")) (= y (str.++ "B" z)))
+ std::vector<Node> rpfxv1;
+ if (stripSymbolicLength(pfxv1, rpfxv1, 1, lenPfx0))
+ {
+ std::vector<Node> sfxv0(v0.begin() + i, v0.end());
+ pfxv1.insert(pfxv1.end(), v1.begin() + j, v1.end());
+ Node ret =
+ nm->mkNode(kind::AND,
+ pfx0.eqNode(mkConcat(STRING_CONCAT, rpfxv1)),
+ mkConcat(STRING_CONCAT, sfxv0)
+ .eqNode(mkConcat(STRING_CONCAT, pfxv1)));
+ return returnRewrite(node, ret, "split-eq-strip-r");
+ }
+
+ // If the prefix of the right-hand side is (strictly) longer than
+ // the prefix of the left-hand side, we can advance the left-hand
+ // side (since the length of the right-hand side is only increasing
+ // in the inner loop)
+ break;
+ }
+ else if (checkEntailArith(lenPfx0, lenPfx1, true))
+ {
+ // The prefix on the left-hand side is strictly longer than the
+ // prefix on the right-hand side, so we try to strip the left-hand
+ // prefix by the length of the right-hand prefix
+ //
+ // Example:
+ // (= (str.++ x "AB" z) (str.++ "A" x y)) --->
+ // (and (= (str.++ x "A") (str.++ "A" x)) (= (str.++ "B" z) y))
+ std::vector<Node> rpfxv0;
+ if (stripSymbolicLength(pfxv0, rpfxv0, 1, lenPfx1))
+ {
+ pfxv0.insert(pfxv0.end(), v0.begin() + i, v0.end());
+ std::vector<Node> sfxv1(v1.begin() + j, v1.end());
+ Node ret =
+ nm->mkNode(kind::AND,
+ mkConcat(STRING_CONCAT, rpfxv0).eqNode(pfx1),
+ mkConcat(STRING_CONCAT, pfxv0)
+ .eqNode(mkConcat(STRING_CONCAT, sfxv1)));
+ return returnRewrite(node, ret, "split-eq-strip-l");
+ }
+
+ // If the prefix of the left-hand side is (strictly) longer than
+ // the prefix of the right-hand side, then we don't need to check
+ // that right-hand prefix for future left-hand prefixes anymore
+ // (since they are increasing in length)
+ startRhs = j + 1;
+ }
+ }
+ }
+ }
+ }
+
return node;
}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback