#include "options/strings_options.h"
#include "smt/logic_exception.h"
+#include "theory/arith/arith_msum.h"
+#include "theory/theory.h"
using namespace std;
using namespace CVC4;
Node TheoryStringsRewriter::rewriteSubstr(Node node)
{
Assert(node.getKind() == kind::STRING_SUBSTR);
+
+ NodeManager* nm = NodeManager::currentNM();
if (node[0].isConst())
{
if (node[0].getConst<String>().size() == 0)
{
// start beyond the maximum size of strings
// thus, it must be beyond the end point of this string
- Node ret = NodeManager::currentNM()->mkConst(::CVC4::String(""));
+ Node ret = nm->mkConst(::CVC4::String(""));
return returnRewrite(node, ret, "ss-const-start-max-oob");
}
else if (node[1].getConst<Rational>().sgn() < 0)
{
// start before the beginning of the string
- Node ret = NodeManager::currentNM()->mkConst(::CVC4::String(""));
+ Node ret = nm->mkConst(::CVC4::String(""));
return returnRewrite(node, ret, "ss-const-start-neg");
}
else
if (start >= s.size())
{
// start beyond the end of the string
- Node ret = NodeManager::currentNM()->mkConst(::CVC4::String(""));
+ Node ret = nm->mkConst(::CVC4::String(""));
return returnRewrite(node, ret, "ss-const-start-oob");
}
}
if (node[2].getConst<Rational>() > RMAXINT)
{
// take up to the end of the string
- Node ret = NodeManager::currentNM()->mkConst(
- ::CVC4::String(s.suffix(s.size() - start)));
+ Node ret = nm->mkConst(::CVC4::String(s.suffix(s.size() - start)));
return returnRewrite(node, ret, "ss-const-len-max-oob");
}
else if (node[2].getConst<Rational>().sgn() <= 0)
{
- Node ret = NodeManager::currentNM()->mkConst(::CVC4::String(""));
+ Node ret = nm->mkConst(::CVC4::String(""));
return returnRewrite(node, ret, "ss-const-len-non-pos");
}
else
if (start + len > s.size())
{
// take up to the end of the string
- Node ret = NodeManager::currentNM()->mkConst(
- ::CVC4::String(s.suffix(s.size() - start)));
+ Node ret = nm->mkConst(::CVC4::String(s.suffix(s.size() - start)));
return returnRewrite(node, ret, "ss-const-end-oob");
}
else
{
// compute the substr using the constant string
- Node ret = NodeManager::currentNM()->mkConst(
- ::CVC4::String(s.substr(start, len)));
+ Node ret = nm->mkConst(::CVC4::String(s.substr(start, len)));
return returnRewrite(node, ret, "ss-const-ss");
}
}
}
}
- Node zero = NodeManager::currentNM()->mkConst(CVC4::Rational(0));
+ Node zero = nm->mkConst(CVC4::Rational(0));
// if entailed non-positive length or negative start point
if (checkEntailArith(zero, node[1], true))
{
- Node ret = NodeManager::currentNM()->mkConst(::CVC4::String(""));
+ Node ret = nm->mkConst(::CVC4::String(""));
return returnRewrite(node, ret, "ss-start-neg");
}
else if (checkEntailArith(zero, node[2]))
{
- Node ret = NodeManager::currentNM()->mkConst(::CVC4::String(""));
+ Node ret = nm->mkConst(::CVC4::String(""));
return returnRewrite(node, ret, "ss-len-non-pos");
}
{
if (curr != zero && !n1.empty())
{
- childrenr.push_back(
- NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR,
- mkConcat(kind::STRING_CONCAT, n1),
- node[1],
- curr));
+ childrenr.push_back(nm->mkNode(kind::STRING_SUBSTR,
+ mkConcat(kind::STRING_CONCAT, n1),
+ node[1],
+ curr));
}
Node ret = mkConcat(kind::STRING_CONCAT, childrenr);
return returnRewrite(node, ret, "ss-len-include");
}
else if (r == 1)
{
- Node tot_len = Rewriter::rewrite(
- NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, node[0]));
- Node end_pt = Rewriter::rewrite(
- NodeManager::currentNM()->mkNode(kind::PLUS, node[1], node[2]));
+ Node tot_len =
+ Rewriter::rewrite(nm->mkNode(kind::STRING_LENGTH, node[0]));
+ Node end_pt = Rewriter::rewrite(nm->mkNode(kind::PLUS, node[1], node[2]));
if (node[2] != tot_len)
{
if (checkEntailArith(node[2], tot_len))
{
// end point beyond end point of string, map to tot_len
- Node ret = NodeManager::currentNM()->mkNode(
- kind::STRING_SUBSTR, node[0], node[1], tot_len);
+ Node ret = nm->mkNode(kind::STRING_SUBSTR, node[0], node[1], tot_len);
return returnRewrite(node, ret, "ss-end-pt-norm");
}
else
{
// strip up to ( str.len(node[0]) - end_pt ) off the end of the string
- curr = Rewriter::rewrite(
- NodeManager::currentNM()->mkNode(kind::MINUS, tot_len, end_pt));
+ curr = Rewriter::rewrite(nm->mkNode(kind::MINUS, tot_len, end_pt));
+ }
+ }
+
+ if (tot_len == nm->mkConst(Rational(1)))
+ {
+ Node n1_eq_zero =
+ Rewriter::rewrite(nm->mkNode(kind::EQUAL, node[1], zero));
+ if (checkEntailArithWithAssumption(n1_eq_zero, zero, node[2], false))
+ {
+ Node ret = nm->mkConst(::CVC4::String(""));
+ return returnRewrite(node, ret, "ss-len-one-unsat");
}
}
}
{
if (r == 0)
{
- Node ret = NodeManager::currentNM()->mkNode(
- kind::STRING_SUBSTR,
- mkConcat(kind::STRING_CONCAT, n1),
- curr,
- node[2]);
+ Node ret = nm->mkNode(kind::STRING_SUBSTR,
+ mkConcat(kind::STRING_CONCAT, n1),
+ curr,
+ node[2]);
return returnRewrite(node, ret, "ss-strip-start-pt");
}
else
{
- Node ret = NodeManager::currentNM()->mkNode(
- kind::STRING_SUBSTR,
- mkConcat(kind::STRING_CONCAT, n1),
- node[1],
- node[2]);
+ Node ret = nm->mkNode(kind::STRING_SUBSTR,
+ mkConcat(kind::STRING_CONCAT, n1),
+ node[1],
+ node[2]);
return returnRewrite(node, ret, "ss-strip-end-pt");
}
}
// the length of a string from the inner substr subtracts the start point
// of the outer substr
- Node len_from_inner = Rewriter::rewrite(NodeManager::currentNM()->mkNode(
- kind::MINUS, node[0][2], start_outer));
+ Node len_from_inner =
+ Rewriter::rewrite(nm->mkNode(kind::MINUS, node[0][2], start_outer));
Node len_from_outer = node[2];
Node new_len;
// take quantity that is for sure smaller than the other
}
if (!new_len.isNull())
{
- Node new_start = NodeManager::currentNM()->mkNode(
- kind::PLUS, start_inner, start_outer);
- Node ret = NodeManager::currentNM()->mkNode(
- kind::STRING_SUBSTR, node[0][0], new_start, new_len);
+ Node new_start = nm->mkNode(kind::PLUS, start_inner, start_outer);
+ Node ret =
+ nm->mkNode(kind::STRING_SUBSTR, node[0][0], new_start, new_len);
return returnRewrite(node, ret, "ss-combine");
}
}
if (n1[sindex_use].isConst())
{
// could strip part of a constant
- Node lowerBound = getConstantArithBound(curr);
+ Node lowerBound = getConstantArithBound(Rewriter::rewrite(curr));
if (!lowerBound.isNull())
{
Assert(lowerBound.isConst());
}
}
+bool TheoryStringsRewriter::checkEntailArithWithEqAssumption(Node assumption,
+ Node a,
+ bool strict)
+{
+ Assert(assumption.getKind() == kind::EQUAL);
+ Assert(Rewriter::rewrite(assumption) == assumption);
+
+ // Find candidates variables to compute substitutions for
+ std::unordered_set<Node, NodeHashFunction> candVars;
+ std::vector<Node> toVisit = {assumption};
+ while (!toVisit.empty())
+ {
+ Node curr = toVisit.back();
+ toVisit.pop_back();
+
+ if (curr.getKind() == kind::PLUS || curr.getKind() == kind::MULT
+ || curr.getKind() == kind::MINUS || curr.getKind() == kind::EQUAL)
+ {
+ for (const auto& currChild : curr)
+ {
+ toVisit.push_back(currChild);
+ }
+ }
+ else if (curr.getKind() == kind::VARIABLE
+ && Theory::theoryOf(curr) == THEORY_ARITH)
+ {
+ candVars.insert(curr);
+ }
+ }
+
+ // Check if any of the candidate variables are in n
+ Node v;
+ Assert(toVisit.empty());
+ toVisit.push_back(a);
+ while (!toVisit.empty())
+ {
+ Node curr = toVisit.back();
+ toVisit.pop_back();
+
+ for (const auto& currChild : curr)
+ {
+ toVisit.push_back(currChild);
+ }
+
+ if (curr.getKind() == kind::VARIABLE
+ && Theory::theoryOf(curr) == THEORY_ARITH
+ && candVars.find(curr) != candVars.end())
+ {
+ v = curr;
+ break;
+ }
+ }
+
+ if (v.isNull())
+ {
+ // No suitable candidate found
+ return false;
+ }
+
+ Node solution = ArithMSum::solveEqualityFor(assumption, v);
+ if (solution.isNull())
+ {
+ // Could not solve for v
+ return false;
+ }
+
+ a = a.substitute(TNode(v), TNode(solution));
+ return checkEntailArith(a, strict);
+}
+
+bool TheoryStringsRewriter::checkEntailArithWithAssumption(Node assumption,
+ Node a,
+ Node b,
+ bool strict)
+{
+ // TODO: Add support for inequality assumptions.
+ Assert(assumption.getKind() == kind::EQUAL);
+ Assert(Rewriter::rewrite(assumption) == assumption);
+
+ NodeManager* nm = NodeManager::currentNM();
+
+ Node diff = nm->mkNode(kind::MINUS, a, b);
+ return checkEntailArithWithEqAssumption(assumption, diff, strict);
+}
+
+bool TheoryStringsRewriter::checkEntailArithWithAssumptions(
+ std::vector<Node> assumptions, Node a, Node b, bool strict)
+{
+ // TODO: We currently try to show the entailment with each assumption
+ // independently. In the future, we should make better use of multiple
+ // assumptions.
+ bool res = false;
+ for (const auto& assumption : assumptions)
+ {
+ Assert(assumption.getKind() == kind::EQUAL);
+ Assert(Rewriter::rewrite(assumption) == assumption);
+
+ if (checkEntailArithWithAssumption(assumption, a, b, strict))
+ {
+ res = true;
+ break;
+ }
+ }
+ return res;
+}
+
Node TheoryStringsRewriter::getConstantArithBound(Node a, bool isLower)
{
Assert(Rewriter::rewrite(a) == a);
--- /dev/null
+/********************* */
+/*! \file theory_strings_rewriter_white.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andres Noetzli
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Unit tests for the strings rewriter
+ **
+ ** Unit tests for the strings rewriter.
+ **/
+
+#include "expr/node.h"
+#include "expr/node_manager.h"
+#include "smt/smt_engine.h"
+#include "smt/smt_engine_scope.h"
+#include "theory/rewriter.h"
+#include "theory/strings/theory_strings_rewriter.h"
+
+#include <cxxtest/TestSuite.h>
+#include <vector>
+
+using namespace CVC4;
+using namespace CVC4::smt;
+using namespace CVC4::theory;
+using namespace CVC4::theory::strings;
+
+class TheoryStringsRewriterWhite : public CxxTest::TestSuite
+{
+ ExprManager *d_em;
+ NodeManager *d_nm;
+ SmtEngine *d_smt;
+ SmtScope *d_scope;
+
+ public:
+ TheoryStringsRewriterWhite() {}
+
+ void setUp()
+ {
+ Options opts;
+ opts.setOutputLanguage(language::output::LANG_SMTLIB_V2);
+ d_em = new ExprManager(opts);
+ d_nm = NodeManager::fromExprManager(d_em);
+ d_smt = new SmtEngine(d_em);
+ d_scope = new SmtScope(d_smt);
+ }
+
+ void tearDown()
+ {
+ delete d_scope;
+ delete d_smt;
+ delete d_em;
+ }
+
+ void testCheckEntailArithWithAssumption()
+ {
+ TypeNode intType = d_nm->integerType();
+ TypeNode strType = d_nm->stringType();
+
+ Node x = d_nm->mkVar("x", intType);
+ Node y = d_nm->mkVar("y", strType);
+ Node z = d_nm->mkVar("z", intType);
+
+ Node zero = d_nm->mkConst(Rational(0));
+
+ Node slen_y = d_nm->mkNode(kind::STRING_LENGTH, y);
+ Node x_plus_slen_y = d_nm->mkNode(kind::PLUS, x, slen_y);
+ Node x_plus_slen_y_eq_zero =
+ Rewriter::rewrite(d_nm->mkNode(kind::EQUAL, x_plus_slen_y, zero));
+
+ // x + (str.len y) = 0 /\ 0 >= x --> true
+ TS_ASSERT(TheoryStringsRewriter::checkEntailArithWithAssumption(
+ x_plus_slen_y_eq_zero, zero, x, false));
+
+ // x + (str.len y) = 0 /\ 0 > x --> false
+ TS_ASSERT(!TheoryStringsRewriter::checkEntailArithWithAssumption(
+ x_plus_slen_y_eq_zero, zero, x, true));
+
+ Node x_plus_slen_y_plus_z_eq_zero = Rewriter::rewrite(d_nm->mkNode(
+ kind::EQUAL, d_nm->mkNode(kind::PLUS, x_plus_slen_y, z), zero));
+
+ // x + (str.len y) + z = 0 /\ 0 > x --> false
+ TS_ASSERT(!TheoryStringsRewriter::checkEntailArithWithAssumption(
+ x_plus_slen_y_plus_z_eq_zero, zero, x, true));
+
+ Node x_plus_slen_y_plus_slen_y_eq_zero = Rewriter::rewrite(d_nm->mkNode(
+ kind::EQUAL, d_nm->mkNode(kind::PLUS, x_plus_slen_y, slen_y), zero));
+
+ // x + (str.len y) + (str.len y) = 0 /\ 0 >= x --> true
+ TS_ASSERT(TheoryStringsRewriter::checkEntailArithWithAssumption(
+ x_plus_slen_y_plus_slen_y_eq_zero, zero, x, false));
+ }
+
+ void testRewriteSubstr()
+ {
+ TypeNode intType = d_nm->integerType();
+ TypeNode strType = d_nm->stringType();
+
+ Node empty = d_nm->mkConst(::CVC4::String(""));
+ Node a = d_nm->mkConst(::CVC4::String("A"));
+ Node s = d_nm->mkVar("s", strType);
+ Node x = d_nm->mkVar("x", intType);
+ Node y = d_nm->mkVar("y", intType);
+
+ // (str.substr "A" x x) --> ""
+ Node n = d_nm->mkNode(kind::STRING_SUBSTR, a, x, x);
+ Node res = TheoryStringsRewriter::rewriteSubstr(n);
+ TS_ASSERT_EQUALS(res, empty);
+
+ // (str.substr "A" (+ x 1) x) -> ""
+ n = d_nm->mkNode(kind::STRING_SUBSTR,
+ a,
+ d_nm->mkNode(kind::PLUS, x, d_nm->mkConst(Rational(1))),
+ x);
+ res = TheoryStringsRewriter::rewriteSubstr(n);
+ TS_ASSERT_EQUALS(res, empty);
+
+ // (str.substr "A" (+ x (str.len s2)) x) -> ""
+ n = d_nm->mkNode(
+ kind::STRING_SUBSTR,
+ a,
+ d_nm->mkNode(kind::PLUS, x, d_nm->mkNode(kind::STRING_LENGTH, s)),
+ x);
+ res = TheoryStringsRewriter::rewriteSubstr(n);
+ TS_ASSERT_EQUALS(res, empty);
+
+ // (str.substr "A" x y) -> (str.substr "A" x y)
+ n = d_nm->mkNode(kind::STRING_SUBSTR, a, x, y);
+ res = TheoryStringsRewriter::rewriteSubstr(n);
+ TS_ASSERT_EQUALS(res, n);
+ }
+};