Non-contributing find replace rewrite (#2652)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Thu, 18 Oct 2018 17:51:42 +0000 (12:51 -0500)
committerGitHub <noreply@github.com>
Thu, 18 Oct 2018 17:51:42 +0000 (12:51 -0500)
src/theory/strings/theory_strings_rewriter.cpp
test/regress/CMakeLists.txt
test/regress/regress0/strings/ncontrib-rewrites.smt2 [new file with mode: 0644]

index c38a5e838f4a9539f445e68c21a1a375e58b35ac..7c008dc14e8eaea549fdab48c96fc6300710b2f0 100644 (file)
@@ -2845,6 +2845,53 @@ Node TheoryStringsRewriter::rewriteReplace( Node node ) {
       }
     }
   }
+  // miniscope based on components that do not contribute to contains
+  // for example,
+  //   str.replace( x ++ y ++ x ++ y, "A", z ) -->
+  //   str.replace( x ++ y, "A", z ) ++ x ++ y
+  // since if "A" occurs in x ++ y ++ x ++ y, then it must occur in x ++ y.
+  if (node[1].isConst() && node[1].getConst<String>().size() == 1)
+  {
+    Node lastLhs;
+    unsigned lastCheckIndex = 0;
+    for (unsigned i = 1, iend = children0.size(); i < iend; i++)
+    {
+      unsigned checkIndex = children0.size() - i;
+      std::vector<Node> checkLhs;
+      checkLhs.insert(
+          checkLhs.end(), children0.begin(), children0.begin() + checkIndex);
+      Node lhs = mkConcat(STRING_CONCAT, checkLhs);
+      Node rhs = children0[checkIndex];
+      Node ctn = nm->mkNode(STRING_STRCTN, lhs, rhs);
+      ctn = Rewriter::rewrite(ctn);
+      if (ctn.isConst() && ctn.getConst<bool>())
+      {
+        lastLhs = lhs;
+        lastCheckIndex = checkIndex;
+      }
+      else
+      {
+        break;
+      }
+    }
+    if (!lastLhs.isNull())
+    {
+      std::vector<Node> remc(children0.begin() + lastCheckIndex,
+                             children0.end());
+      Node rem = mkConcat(STRING_CONCAT, remc);
+      Node ret =
+          nm->mkNode(STRING_CONCAT,
+                     nm->mkNode(STRING_STRREPL, lastLhs, node[1], node[2]),
+                     rem);
+      // for example:
+      //   str.replace( x ++ x, "A", y ) ---> str.replace( x, "A", y ) ++ x
+      // Since we know that the first occurrence of "A" cannot be in the
+      // second occurrence of x. Notice this is specific to single characters
+      // due to complications with finds that span multiple components for
+      // non-characters.
+      return returnRewrite(node, ret, "repl-char-ncontrib-find");
+    }
+  }
 
   // TODO (#1180) incorporate these?
   // contains( t, s ) =>
index 59edb69862ef6030a97f09c63daa17eefbc85c13..a41108a71a3bd003219375eb8152d78ba83bb9ce 100644 (file)
@@ -807,6 +807,7 @@ set(regress_0_tests
   regress0/strings/leadingzero001.smt2
   regress0/strings/loop001.smt2
   regress0/strings/model001.smt2
+  regress0/strings/ncontrib-rewrites.smt2
   regress0/strings/norn-31.smt2
   regress0/strings/norn-simp-rew.smt2
   regress0/strings/repl-rewrites2.smt2
diff --git a/test/regress/regress0/strings/ncontrib-rewrites.smt2 b/test/regress/regress0/strings/ncontrib-rewrites.smt2
new file mode 100644 (file)
index 0000000..6b2e687
--- /dev/null
@@ -0,0 +1,15 @@
+(set-logic ALL)
+(set-info :status unsat)
+(declare-fun x () String)
+(declare-fun y () String)
+(declare-fun z () String)
+
+(assert (or
+
+(not (= (str.replace (str.++ x x) "A" "B") (str.replace x "" (str.replace x "A" "B"))))
+
+(not (= (str.replace (str.++ x y "B" x y) "A" z) (str.++ (str.replace (str.++ x y) "A" z) "B" x y)))
+
+))
+
+(check-sat)