LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/strings - arith_entail.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 563 608 92.6 %
Date: 2026-09-03 09:47:21 Functions: 20 21 95.2 %
Branches: 402 555 72.4 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  * Implementation of arithmetic entailment computation for string terms.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "theory/strings/arith_entail.h"
      14                 :            : 
      15                 :            : #include "expr/aci_norm.h"
      16                 :            : #include "expr/attribute.h"
      17                 :            : #include "expr/node_algorithm.h"
      18                 :            : #include "proof/conv_proof_generator.h"
      19                 :            : #include "theory/arith/arith_msum.h"
      20                 :            : #include "theory/arith/arith_poly_norm.h"
      21                 :            : #include "theory/arith/arith_subs.h"
      22                 :            : #include "theory/rewriter.h"
      23                 :            : #include "theory/strings/theory_strings_utils.h"
      24                 :            : #include "theory/strings/word.h"
      25                 :            : #include "theory/theory.h"
      26                 :            : #include "util/rational.h"
      27                 :            : 
      28                 :            : using namespace cvc5::internal::kind;
      29                 :            : 
      30                 :            : namespace cvc5::internal {
      31                 :            : namespace theory {
      32                 :            : namespace strings {
      33                 :            : 
      34                 :     343181 : ArithEntail::ArithEntail(NodeManager* nm, Rewriter* r, bool recApprox)
      35                 :     343181 :     : d_rr(r), d_recApprox(recApprox)
      36                 :            : {
      37                 :     343181 :   d_one = nm->mkConstInt(Rational(1));
      38                 :     343181 :   d_zero = nm->mkConstInt(Rational(0));
      39                 :     343181 : }
      40                 :            : 
      41                 :     147259 : Node ArithEntail::rewritePredViaEntailment(const Node& n, bool isSimple)
      42                 :            : {
      43                 :     147259 :   Node exp;
      44                 :     294518 :   return rewritePredViaEntailment(n, exp, isSimple);
      45                 :     147259 : }
      46                 :            : 
      47                 :     148354 : Node ArithEntail::rewritePredViaEntailment(const Node& n,
      48                 :            :                                            Node& exp,
      49                 :            :                                            bool isSimple)
      50                 :            : {
      51                 :     148354 :   NodeManager* nm = n.getNodeManager();
      52                 :     148354 :   if (n.getKind() == Kind::EQUAL && n[0].getType().isInteger())
      53                 :            :   {
      54                 :      40060 :     exp = nm->mkNode(Kind::SUB, nm->mkNode(Kind::SUB, n[0], n[1]), d_one);
      55         [ +  + ]:      40060 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      56                 :            :     {
      57                 :       6650 :       return nm->mkConst(false);
      58                 :            :     }
      59                 :      36735 :     exp = nm->mkNode(Kind::SUB, nm->mkNode(Kind::SUB, n[1], n[0]), d_one);
      60         [ +  + ]:      36735 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      61                 :            :     {
      62                 :       4254 :       return nm->mkConst(false);
      63                 :            :     }
      64                 :      34608 :     exp = Node::null();
      65         [ +  + ]:      34608 :     if (checkEq(n[0], n[1]))
      66                 :            :     {
      67                 :            :       // explanation is null
      68                 :        842 :       return nm->mkConst(true);
      69                 :            :     }
      70                 :            :   }
      71         [ +  - ]:     108294 :   else if (n.getKind() == Kind::GEQ)
      72                 :            :   {
      73                 :     108294 :     exp = nm->mkNode(Kind::SUB, n[0], n[1]);
      74         [ +  + ]:     108294 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      75                 :            :     {
      76                 :       6714 :       return nm->mkConst(true);
      77                 :            :     }
      78                 :     104937 :     exp = nm->mkNode(Kind::SUB, nm->mkNode(Kind::SUB, n[1], n[0]), d_one);
      79         [ +  + ]:     104937 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      80                 :            :     {
      81                 :      19694 :       return nm->mkConst(false);
      82                 :            :     }
      83                 :      95090 :     exp = Node::null();
      84                 :            :   }
      85                 :     129277 :   return Node::null();
      86                 :            : }
      87                 :            : 
      88                 :    2155019 : Node ArithEntail::rewriteArith(Node a)
      89                 :            : {
      90 [ -  + ][ -  - ]:    4310038 :   AlwaysAssert(a.getType().isInteger())
      91                 :    2155019 :       << "Bad term: " << a << " " << a.getType();
      92         [ +  + ]:    2155019 :   if (d_rr != nullptr)
      93                 :            :   {
      94                 :     294985 :     return d_rr->rewrite(a);
      95                 :            :   }
      96                 :            :   else
      97                 :            :   {
      98                 :    1860034 :     a = rewriteLengthIntro(a);
      99                 :            :   }
     100                 :            :   // Otherwise, use the poly norm utility. This is important since the rewrite
     101                 :            :   // must be justified by ARITH_POLY_NORM when in proof mode (when d_rr is
     102                 :            :   // null).
     103                 :    1860034 :   Node an = arith::PolyNorm::getPolyNorm(a);
     104                 :    1860034 :   return an;
     105                 :    1860034 : }
     106                 :            : 
     107                 :      94280 : Node ArithEntail::normalizeGeq(const Node& n) const
     108                 :            : {
     109                 :      94280 :   NodeManager* nm = n.getNodeManager();
     110                 :     188560 :   if (n.getNumChildren() != 2 || !n[0].getType().isInteger()
     111                 :     188560 :       || !n[1].getType().isInteger())
     112                 :            :   {
     113                 :         99 :     return Node::null();
     114                 :            :   }
     115 [ +  + ][ +  + ]:      94181 :   switch (n.getKind())
                    [ - ]
     116                 :            :   {
     117                 :      60807 :     case Kind::GEQ: return n;
     118                 :      11074 :     case Kind::LEQ: return nm->mkNode(Kind::GEQ, n[1], n[0]);
     119                 :       9152 :     case Kind::LT:
     120                 :            :       return nm->mkNode(
     121                 :            :           Kind::GEQ,
     122                 :            :           n[1],
     123                 :       9152 :           nm->mkNode(Kind::ADD, n[0], nm->mkConstInt(Rational(1))));
     124                 :      13148 :     case Kind::GT:
     125                 :            :       return nm->mkNode(
     126                 :            :           Kind::GEQ,
     127                 :            :           n[0],
     128                 :      13148 :           nm->mkNode(Kind::ADD, n[1], nm->mkConstInt(Rational(1))));
     129                 :          0 :     default: break;
     130                 :            :   }
     131                 :          0 :   return Node::null();
     132                 :            : }
     133                 :            : 
     134                 :    1973355 : Node ArithEntail::rewriteLengthIntro(const Node& n,
     135                 :            :                                      TConvProofGenerator* pg) const
     136                 :            : {
     137                 :    1973355 :   NodeManager* nm = n.getNodeManager();
     138                 :    1973355 :   std::unordered_map<TNode, Node> visited;
     139                 :    1973355 :   std::unordered_map<TNode, Node>::iterator it;
     140                 :    1973355 :   std::vector<TNode> visit;
     141                 :    1973355 :   TNode cur;
     142                 :    1973355 :   visit.push_back(n);
     143                 :            :   do
     144                 :            :   {
     145                 :   33709761 :     cur = visit.back();
     146                 :   33709761 :     it = visited.find(cur);
     147         [ +  + ]:   33709761 :     if (it == visited.end())
     148                 :            :     {
     149         [ +  + ]:   18387179 :       if (cur.getNumChildren() == 0)
     150                 :            :       {
     151                 :    7555162 :         visit.pop_back();
     152                 :    7555162 :         visited[cur] = cur;
     153                 :    7555162 :         continue;
     154                 :            :       }
     155                 :   10832017 :       visited.emplace(cur, Node::null());
     156                 :   10832017 :       visit.insert(visit.end(), cur.begin(), cur.end());
     157                 :   10832017 :       continue;
     158                 :            :     }
     159                 :   15322582 :     visit.pop_back();
     160         [ +  + ]:   15322582 :     if (it->second.isNull())
     161                 :            :     {
     162                 :   10832017 :       Kind k = cur.getKind();
     163                 :   10832017 :       bool childChanged = false;
     164                 :   10832017 :       std::vector<Node> children;
     165         [ +  + ]:   10832017 :       if (cur.getMetaKind() == kind::metakind::PARAMETERIZED)
     166                 :            :       {
     167                 :     185159 :         children.push_back(cur.getOperator());
     168                 :            :       }
     169         [ +  + ]:   31736406 :       for (const Node& cn : cur)
     170                 :            :       {
     171                 :   20904389 :         it = visited.find(cn);
     172 [ -  + ][ -  + ]:   20904389 :         Assert(it != visited.end());
                 [ -  - ]
     173 [ -  + ][ -  + ]:   20904389 :         Assert(!it->second.isNull());
                 [ -  - ]
     174                 :   20904389 :         children.push_back(it->second);
     175 [ +  + ][ +  + ]:   20904389 :         childChanged = childChanged || it->second != cn;
     176                 :   20904389 :       }
     177                 :   10832017 :       Node ret = cur;
     178         [ +  + ]:   10832017 :       if (childChanged)
     179                 :            :       {
     180                 :     740449 :         ret = nm->mkNode(k, children);
     181                 :            :       }
     182                 :   10832017 :       if (k == Kind::STRING_LENGTH
     183                 :   10832017 :           && (ret[0].getKind() == Kind::STRING_CONCAT || ret[0].isConst()))
     184                 :            :       {
     185                 :     545426 :         Node arg = ret[0];
     186                 :            :         // First ensure ACI norm, which ensures that we fully flatten
     187                 :            :         // e.g. (len (str.++ (str.++ a b) c)) ---> (len (str.++ a b c)) --->
     188                 :            :         // (+ (len a) (len b) (len c)) below.
     189         [ +  + ]:     545426 :         if (arg.getKind() == Kind::STRING_CONCAT)
     190                 :            :         {
     191                 :     170170 :           arg = expr::getACINormalForm(arg);
     192         [ +  + ]:     170170 :           if (arg != ret[0])
     193                 :            :           {
     194                 :         58 :             Node ret2 = nm->mkNode(k, {arg});
     195         [ +  + ]:         58 :             if (pg != nullptr)
     196                 :            :             {
     197                 :          9 :               pg->addRewriteStep(ret,
     198                 :            :                                  ret2,
     199                 :            :                                  nullptr,
     200                 :            :                                  false,
     201                 :            :                                  TrustId::MACRO_THEORY_REWRITE_RCONS_SIMPLE);
     202                 :            :             }
     203                 :         58 :             ret = ret2;
     204                 :         58 :           }
     205                 :            :         }
     206                 :     545426 :         std::vector<Node> cc;
     207                 :     545426 :         utils::getConcat(arg, cc);
     208                 :     545426 :         std::vector<Node> sum;
     209         [ +  + ]:    1471515 :         for (const Node& c : cc)
     210                 :            :         {
     211         [ +  + ]:     926089 :           if (c.isConst())
     212                 :            :           {
     213                 :     536816 :             sum.push_back(nm->mkConstInt(Rational(Word::getLength(c))));
     214                 :            :           }
     215                 :            :           else
     216                 :            :           {
     217                 :     389273 :             sum.push_back(nm->mkNode(Kind::STRING_LENGTH, c));
     218                 :            :           }
     219                 :            :         }
     220 [ -  + ][ -  + ]:     545426 :         Assert(!sum.empty());
                 [ -  - ]
     221         [ +  + ]:     545426 :         Node rret = sum.size() == 1 ? sum[0] : nm->mkNode(Kind::ADD, sum);
     222         [ +  + ]:     545426 :         if (pg != nullptr)
     223                 :            :         {
     224                 :        511 :           pg->addRewriteStep(ret,
     225                 :            :                              rret,
     226                 :            :                              nullptr,
     227                 :            :                              false,
     228                 :            :                              TrustId::MACRO_THEORY_REWRITE_RCONS_SIMPLE);
     229                 :            :         }
     230                 :     545426 :         ret = rret;
     231                 :     545426 :       }
     232                 :   10832017 :       visited[cur] = ret;
     233                 :   10832017 :     }
     234         [ +  + ]:   33709761 :   } while (!visit.empty());
     235 [ -  + ][ -  + ]:    1973355 :   Assert(visited.find(n) != visited.end());
                 [ -  - ]
     236 [ -  + ][ -  + ]:    1973355 :   Assert(!visited.find(n)->second.isNull());
                 [ -  - ]
     237                 :    3946710 :   return visited[n];
     238                 :    1973355 : }
     239                 :            : 
     240                 :      47800 : bool ArithEntail::checkEq(Node a, Node b)
     241                 :            : {
     242         [ +  + ]:      47800 :   if (a == b)
     243                 :            :   {
     244                 :        392 :     return true;
     245                 :            :   }
     246                 :      47408 :   Node ar = rewriteArith(a);
     247                 :      47408 :   Node br = rewriteArith(b);
     248                 :      47408 :   return ar == br;
     249                 :      47408 : }
     250                 :            : 
     251                 :    1081685 : bool ArithEntail::check(Node a, Node b, bool strict, bool isSimple)
     252                 :            : {
     253         [ +  + ]:    1081685 :   if (a == b)
     254                 :            :   {
     255                 :      65057 :     return !strict;
     256                 :            :   }
     257                 :    2033256 :   Node diff = NodeManager::mkNode(Kind::SUB, a, b);
     258                 :    1016628 :   return check(diff, strict, isSimple);
     259                 :    1016628 : }
     260                 :            : 
     261                 :    1674339 : bool ArithEntail::check(Node a, bool strict, bool isSimple)
     262                 :            : {
     263         [ +  + ]:    1674339 :   if (a.isConst())
     264                 :            :   {
     265         [ +  + ]:     176125 :     return a.getConst<Rational>().sgn() >= (strict ? 1 : 0);
     266                 :            :   }
     267                 :    2058223 :   Node ar = strict ? NodeManager::mkNode(Kind::SUB, a, d_one) : a;
     268         [ +  + ]:    1498214 :   if (isSimple)
     269                 :            :   {
     270                 :     255185 :     ar = arith::PolyNorm::getPolyNorm(ar);
     271                 :            :     // if simple, just call the checkSimple routine.
     272                 :     255185 :     return checkSimple(ar);
     273                 :            :   }
     274                 :            :   else
     275                 :            :   {
     276                 :            :     // otherwise rewrite arith and find approximation
     277                 :    1243029 :     ar = rewriteArith(ar);
     278                 :            :   }
     279                 :    1243029 :   Node ara = findApprox(ar, isSimple);
     280                 :    1243029 :   return !ara.isNull();
     281                 :    1498214 : }
     282                 :            : 
     283                 :    1534023 : Node ArithEntail::findApprox(Node ar, bool isSimple)
     284                 :            : {
     285         [ +  + ]:    1534023 :   std::map<Node, Node>& cache = isSimple ? d_approxCacheSimple : d_approxCache;
     286                 :    1534023 :   std::map<Node, Node>::iterator it = cache.find(ar);
     287         [ +  + ]:    1534023 :   if (it != cache.end())
     288                 :            :   {
     289                 :    1036330 :     return it->second;
     290                 :            :   }
     291                 :     497693 :   Node ret;
     292         [ +  + ]:     497693 :   if (checkSimple(ar))
     293                 :            :   {
     294                 :            :     // didn't need approximation
     295                 :      32266 :     ret = ar;
     296                 :            :   }
     297                 :            :   else
     298                 :            :   {
     299                 :     465427 :     ret = findApproxInternal(ar, isSimple);
     300                 :            :   }
     301                 :     497693 :   cache[ar] = ret;
     302                 :     497693 :   return ret;
     303                 :     497693 : }
     304                 :            : 
     305                 :     465427 : Node ArithEntail::findApproxInternal(Node ar, bool isSimple)
     306                 :            : {
     307                 :            :   // if not using recursive approximations, we always set isSimple to true
     308         [ +  + ]:     465427 :   if (!d_recApprox)
     309                 :            :   {
     310                 :     411988 :     isSimple = true;
     311                 :            :   }
     312                 :     465427 :   NodeManager* nm = ar.getNodeManager();
     313                 :     465427 :   std::map<Node, Node> msum;
     314         [ +  - ]:     930854 :   Trace("strings-ent-approx-debug")
     315                 :     465427 :       << "Setup arithmetic approximations for " << ar << std::endl;
     316         [ -  + ]:     465427 :   if (!ArithMSum::getMonomialSum(ar, msum))
     317                 :            :   {
     318         [ -  - ]:          0 :     Trace("strings-ent-approx-debug")
     319                 :          0 :         << "...failed to get monomial sum!" << std::endl;
     320                 :          0 :     return Node::null();
     321                 :            :   }
     322                 :            :   // for each monomial v*c, mApprox[v] a list of
     323                 :            :   // possibilities for how the term can be soundly approximated, that is,
     324                 :            :   // if mApprox[v] contains av, then v*c > av*c. Notice that if c
     325                 :            :   // is positive, then v > av, otherwise if c is negative, then v < av.
     326                 :            :   // In other words, av is an under-approximation if c is positive, and an
     327                 :            :   // over-approximation if c is negative.
     328                 :     465427 :   bool changed = false;
     329                 :     465427 :   std::map<Node, std::vector<Node> > mApprox;
     330                 :            :   // map from approximations to their monomial sums
     331                 :     465427 :   std::map<Node, std::map<Node, Node> > approxMsums;
     332                 :            :   // aarSum stores each monomial that does not have multiple approximations
     333                 :     465427 :   std::vector<Node> aarSum;
     334                 :            :   // stores the witness
     335                 :     465427 :   arith::ArithSubs approxMap;
     336         [ +  + ]:    1472382 :   for (std::pair<const Node, Node>& m : msum)
     337                 :            :   {
     338                 :    1006955 :     Node v = m.first;
     339                 :    1006955 :     Node c = m.second;
     340         [ +  - ]:    2013910 :     Trace("strings-ent-approx-debug")
     341                 :    1006955 :         << "Get approximations " << v << "..." << std::endl;
     342         [ +  + ]:    1006955 :     if (v.isNull())
     343                 :            :     {
     344 [ -  + ][ -  + ]:     332661 :       Node mn = c.isNull() ? nm->mkConstInt(Rational(1)) : c;
                 [ -  - ]
     345                 :     332661 :       aarSum.push_back(mn);
     346                 :     332661 :     }
     347                 :            :     else
     348                 :            :     {
     349                 :            :       // c.isNull() means c = 1
     350 [ +  + ][ +  + ]:     674294 :       bool isOverApprox = !c.isNull() && c.getConst<Rational>().sgn() == -1;
     351                 :     674294 :       std::vector<Node>& approx = mApprox[v];
     352                 :     674294 :       std::unordered_set<Node> visited;
     353                 :     674294 :       std::vector<Node> toProcess;
     354                 :     674294 :       toProcess.push_back(v);
     355                 :            :       do
     356                 :            :       {
     357                 :     696464 :         Node curr = toProcess.back();
     358         [ +  - ]:     696464 :         Trace("strings-ent-approx-debug") << "  process " << curr << std::endl;
     359                 :     696464 :         curr = arith::PolyNorm::getPolyNorm(curr);
     360                 :     696464 :         toProcess.pop_back();
     361         [ +  + ]:     696464 :         if (visited.find(curr) == visited.end())
     362                 :            :         {
     363                 :     695348 :           visited.insert(curr);
     364                 :     695348 :           std::vector<Node> currApprox;
     365                 :     695348 :           getArithApproximations(curr, currApprox, isOverApprox, isSimple);
     366         [ +  + ]:     695348 :           if (currApprox.empty())
     367                 :            :           {
     368         [ +  - ]:    1155896 :             Trace("strings-ent-approx-debug")
     369                 :     577948 :                 << "...approximation: " << curr << std::endl;
     370                 :            :             // no approximations, thus curr is a possibility
     371                 :     577948 :             approx.push_back(curr);
     372                 :            :           }
     373         [ +  + ]:     117400 :           else if (isSimple)
     374                 :            :           {
     375                 :            :             // don't rewrite or re-approximate
     376                 :     104487 :             approx = currApprox;
     377                 :            :           }
     378                 :            :           else
     379                 :            :           {
     380                 :      25826 :             toProcess.insert(
     381                 :      25826 :                 toProcess.end(), currApprox.begin(), currApprox.end());
     382                 :            :           }
     383                 :     695348 :         }
     384         [ +  + ]:     696464 :       } while (!toProcess.empty());
     385 [ -  + ][ -  + ]:     674294 :       Assert(!approx.empty());
                 [ -  - ]
     386                 :            :       // if we have only one approximation, move it to final
     387         [ +  + ]:     674294 :       if (approx.size() == 1)
     388                 :            :       {
     389         [ +  + ]:     640487 :         if (v != approx[0])
     390                 :            :         {
     391                 :      83066 :           changed = true;
     392         [ +  - ]:     166132 :           Trace("strings-ent-approx")
     393                 :      83066 :               << "- Propagate (" << (d_rr == nullptr) << ", " << isSimple
     394                 :      83066 :               << ") " << v << " = " << approx[0] << std::endl;
     395                 :      83066 :           approxMap.add(v, approx[0]);
     396                 :            :         }
     397                 :    1280974 :         Node mn = ArithMSum::mkCoeffTerm(c, approx[0]);
     398                 :     640487 :         aarSum.push_back(mn);
     399                 :     640487 :         mApprox.erase(v);
     400                 :     640487 :       }
     401                 :            :       else
     402                 :            :       {
     403                 :            :         // compute monomial sum form for each approximation, used below
     404         [ +  + ]:     101427 :         for (const Node& aa : approx)
     405                 :            :         {
     406         [ +  + ]:      67620 :           if (approxMsums.find(aa) == approxMsums.end())
     407                 :            :           {
     408                 :            :             // ensure rewritten, which makes a difference if isSimple is true
     409                 :      63972 :             Node aar = arith::PolyNorm::getPolyNorm(aa);
     410                 :            :             CVC5_UNUSED bool ret =
     411                 :      63972 :                 ArithMSum::getMonomialSum(aar, approxMsums[aa]);
     412                 :      63972 :             Assert(ret) << "Could not find sum " << aa;
     413                 :      63972 :           }
     414                 :            :         }
     415                 :      33807 :         changed = true;
     416                 :            :       }
     417                 :     674294 :     }
     418                 :    1006955 :   }
     419         [ +  + ]:     465427 :   if (!changed)
     420                 :            :   {
     421                 :            :     // approximations had no effect, return
     422         [ +  - ]:     365020 :     Trace("strings-ent-approx-debug") << "...no approximations" << std::endl;
     423                 :     365020 :     return Node::null();
     424                 :            :   }
     425                 :            :   // get the current "fixed" sum for the abstraction of ar
     426                 :            :   Node aar =
     427                 :     100407 :       aarSum.empty()
     428                 :       5609 :           ? d_zero
     429 [ +  + ][ +  + ]:     100407 :           : (aarSum.size() == 1 ? aarSum[0] : nm->mkNode(Kind::ADD, aarSum));
     430                 :     100407 :   aar = arith::PolyNorm::getPolyNorm(aar);
     431         [ +  - ]:     200814 :   Trace("strings-ent-approx-debug")
     432                 :     100407 :       << "...processed fixed sum " << aar << " with " << mApprox.size()
     433                 :     100407 :       << " approximated monomials." << std::endl;
     434                 :            :   // if we have a choice of how to approximate
     435         [ +  + ]:     100407 :   if (!mApprox.empty())
     436                 :            :   {
     437                 :            :     // convert aar back to monomial sum
     438                 :      32706 :     std::map<Node, Node> msumAar;
     439         [ -  + ]:      32706 :     if (!ArithMSum::getMonomialSum(aar, msumAar))
     440                 :            :     {
     441                 :          0 :       return Node::null();
     442                 :            :     }
     443         [ -  + ]:      32706 :     if (TraceIsOn("strings-ent-approx"))
     444                 :            :     {
     445         [ -  - ]:          0 :       Trace("strings-ent-approx")
     446                 :          0 :           << "---- Check arithmetic entailment by under-approximation " << ar
     447                 :          0 :           << " >= 0" << std::endl;
     448         [ -  - ]:          0 :       Trace("strings-ent-approx") << "FIXED:" << std::endl;
     449                 :          0 :       ArithMSum::debugPrintMonomialSum(msumAar, "strings-ent-approx");
     450         [ -  - ]:          0 :       Trace("strings-ent-approx") << "APPROX:" << std::endl;
     451         [ -  - ]:          0 :       for (std::pair<const Node, std::vector<Node> >& a : mApprox)
     452                 :            :       {
     453                 :          0 :         Node c = msum[a.first];
     454         [ -  - ]:          0 :         Trace("strings-ent-approx") << "  ";
     455         [ -  - ]:          0 :         if (!c.isNull())
     456                 :            :         {
     457         [ -  - ]:          0 :           Trace("strings-ent-approx") << c << " * ";
     458                 :            :         }
     459         [ -  - ]:          0 :         Trace("strings-ent-approx")
     460                 :          0 :             << a.second << " ...from " << a.first << std::endl;
     461                 :          0 :       }
     462         [ -  - ]:          0 :       Trace("strings-ent-approx") << std::endl;
     463                 :            :     }
     464                 :      32706 :     Rational one(1);
     465                 :            :     // incorporate monomials one at a time that have a choice of approximations
     466         [ +  + ]:      66513 :     while (!mApprox.empty())
     467                 :            :     {
     468                 :      33807 :       Node v;
     469                 :      33807 :       Node vapprox;
     470                 :      33807 :       int maxScore = -1;
     471                 :            :       // Look at each approximation, take the one with the best score.
     472                 :            :       // Notice that we are in the process of trying to prove
     473                 :            :       // ( c1*t1 + .. + cn*tn ) + ( approx_1 | ... | approx_m ) >= 0,
     474                 :            :       // where c1*t1 + .. + cn*tn is the "fixed" component of our sum (aar)
     475                 :            :       // and approx_1 ... approx_m are possible approximations. The
     476                 :            :       // intution here is that we want coefficients c1...cn to be positive.
     477                 :            :       // This is because arithmetic string terms t1...tn (which may be
     478                 :            :       // applications of len, indexof, str.to.int) are never entailed to be
     479                 :            :       // negative. Hence, we add the approx_i that contributes the "most"
     480                 :            :       // towards making all constants c1...cn positive and cancelling negative
     481                 :            :       // monomials in approx_i itself.
     482         [ +  - ]:      33807 :       for (std::pair<const Node, std::vector<Node> >& nam : mApprox)
     483                 :            :       {
     484                 :      33807 :         Node cr = msum[nam.first];
     485         [ +  + ]:     101427 :         for (const Node& aa : nam.second)
     486                 :            :         {
     487                 :      67620 :           unsigned helpsCancelCount = 0;
     488                 :      67620 :           unsigned addsObligationCount = 0;
     489                 :      67620 :           std::map<Node, Node>::iterator it;
     490                 :            :           // we are processing an approximation cr*( c1*t1 + ... + cn*tn )
     491         [ +  + ]:     148736 :           for (std::pair<const Node, Node>& aam : approxMsums[aa])
     492                 :            :           {
     493                 :            :             // Say aar is of the form t + c*ti, and aam is the monomial ci*ti
     494                 :            :             // where ci != 0. We say aam:
     495                 :            :             // (1) helps cancel if c != 0 and c>0 != ci>0
     496                 :            :             // (2) adds obligation if c>=0 and c+ci<0
     497                 :      81116 :             Node ti = aam.first;
     498                 :      81116 :             Node ci = aam.second;
     499         [ +  + ]:      81116 :             if (!cr.isNull())
     500                 :            :             {
     501         [ +  + ]:     167534 :               ci = ci.isNull() ? cr
     502                 :      54980 :                                : nm->mkConstInt(cr.getConst<Rational>()
     503 [ +  + ][ -  - ]:     167534 :                                                 * ci.getConst<Rational>());
     504                 :            :             }
     505         [ +  - ]:      81116 :             Trace("strings-ent-approx-debug") << ci << "*" << ti << " ";
     506         [ +  + ]:      81116 :             int ciSgn = ci.isNull() ? 1 : ci.getConst<Rational>().sgn();
     507                 :      81116 :             it = msumAar.find(ti);
     508         [ +  + ]:      81116 :             if (it != msumAar.end())
     509                 :            :             {
     510                 :      32875 :               Node c = it->second;
     511         [ +  + ]:      32875 :               int cSgn = c.isNull() ? 1 : c.getConst<Rational>().sgn();
     512         [ +  + ]:      32875 :               if (cSgn == 0)
     513                 :            :               {
     514         [ +  - ]:       4847 :                 addsObligationCount += (ciSgn == -1 ? 1 : 0);
     515                 :            :               }
     516         [ +  + ]:      28028 :               else if (cSgn != ciSgn)
     517                 :            :               {
     518                 :      17683 :                 helpsCancelCount++;
     519         [ +  + ]:      17683 :                 Rational r1 = c.isNull() ? one : c.getConst<Rational>();
     520         [ +  + ]:      17683 :                 Rational r2 = ci.isNull() ? one : ci.getConst<Rational>();
     521                 :      17683 :                 Rational r12 = r1 + r2;
     522         [ +  + ]:      17683 :                 if (r12.sgn() == -1)
     523                 :            :                 {
     524                 :       6574 :                   addsObligationCount++;
     525                 :            :                 }
     526                 :      17683 :               }
     527                 :      32875 :             }
     528                 :            :             else
     529                 :            :             {
     530         [ +  + ]:      48241 :               addsObligationCount += (ciSgn == -1 ? 1 : 0);
     531                 :            :             }
     532                 :      81116 :           }
     533         [ +  - ]:     135240 :           Trace("strings-ent-approx-debug")
     534                 :          0 :               << "counts=" << helpsCancelCount << "," << addsObligationCount
     535                 :      67620 :               << " for " << aa << " into " << aar << std::endl;
     536         [ +  + ]:      67620 :           int score = (addsObligationCount > 0 ? 0 : 2)
     537         [ +  + ]:      67620 :                       + (helpsCancelCount > 0 ? 1 : 0);
     538                 :            :           // if its the best, update v and vapprox
     539 [ +  + ][ +  + ]:      67620 :           if (v.isNull() || score > maxScore)
                 [ +  + ]
     540                 :            :           {
     541                 :      40504 :             v = nam.first;
     542                 :      40504 :             vapprox = aa;
     543                 :      40504 :             maxScore = score;
     544                 :            :           }
     545                 :            :         }
     546         [ +  - ]:      33807 :         if (!v.isNull())
     547                 :            :         {
     548                 :      33807 :           break;
     549                 :            :         }
     550         [ -  + ]:      33807 :       }
     551         [ +  - ]:      67614 :       Trace("strings-ent-approx") << "- Decide (" << (d_rr == nullptr) << ") "
     552                 :      33807 :                                   << v << " = " << vapprox << std::endl;
     553                 :            :       // we incorporate v approximated by vapprox into the overall approximation
     554                 :            :       // for ar
     555 [ +  - ][ +  - ]:      33807 :       Assert(!v.isNull() && !vapprox.isNull());
         [ -  + ][ -  + ]
                 [ -  - ]
     556 [ -  + ][ -  + ]:      33807 :       Assert(msum.find(v) != msum.end());
                 [ -  - ]
     557                 :      67614 :       Node mn = ArithMSum::mkCoeffTerm(msum[v], vapprox);
     558                 :      33807 :       aar = nm->mkNode(Kind::ADD, aar, mn);
     559                 :      33807 :       approxMap.add(v, vapprox);
     560                 :            :       // update the msumAar map
     561                 :      33807 :       aar = arith::PolyNorm::getPolyNorm(aar);
     562                 :      33807 :       msumAar.clear();
     563         [ -  + ]:      33807 :       if (!ArithMSum::getMonomialSum(aar, msumAar))
     564                 :            :       {
     565                 :          0 :         DebugUnhandled();
     566                 :            :         Trace("strings-ent-approx")
     567                 :            :             << "...failed to get monomial sum!" << std::endl;
     568                 :            :         return Node::null();
     569                 :            :       }
     570                 :            :       // we have processed the approximation for v
     571                 :      33807 :       mApprox.erase(v);
     572 [ +  - ][ +  - ]:      33807 :     }
                 [ +  - ]
     573         [ +  - ]:      32706 :     Trace("strings-ent-approx") << "-----------------" << std::endl;
     574 [ +  - ][ +  - ]:      32706 :   }
     575         [ -  + ]:     100407 :   if (aar == ar)
     576                 :            :   {
     577         [ -  - ]:          0 :     Trace("strings-ent-approx-debug")
     578                 :          0 :         << "...approximation had no effect" << std::endl;
     579                 :            :     // this should never happen, but we avoid the infinite loop for sanity here
     580                 :          0 :     DebugUnhandled();
     581                 :            :     return Node::null();
     582                 :            :   }
     583                 :            :   // Check entailment on the approximation of ar.
     584                 :            :   // Notice that this may trigger further reasoning by approximation. For
     585                 :            :   // example, len( replace( x ++ y, substr( x, 0, n ), z ) ) may be
     586                 :            :   // under-approximated as len( x ) + len( y ) - len( substr( x, 0, n ) ) on
     587                 :            :   // this call, where in the recursive call we may over-approximate
     588                 :            :   // len( substr( x, 0, n ) ) as len( x ). In this example, we can infer
     589                 :            :   // that len( replace( x ++ y, substr( x, 0, n ), z ) ) >= len( y ) in two
     590                 :            :   // steps.
     591         [ +  + ]:     100407 :   if (check(aar, false, isSimple))
     592                 :            :   {
     593         [ +  - ]:      13634 :     Trace("strings-ent-approx")
     594                 :          0 :         << "*** StrArithApprox: showed " << ar
     595                 :       6817 :         << " >= 0 using under-approximation!" << std::endl;
     596         [ +  - ]:      13634 :     Trace("strings-ent-approx")
     597                 :       6817 :         << "*** StrArithApprox: rewritten was " << aar << std::endl;
     598                 :            :     // Apply arithmetic substitution, which ensures we only replace terms
     599                 :            :     // in the top-level arithmetic skeleton of ar.
     600                 :       6817 :     Node approx = approxMap.applyArith(ar);
     601         [ +  - ]:      13634 :     Trace("strings-ent-approx")
     602                 :          0 :         << "*** StrArithApprox: under-approximation was " << approx
     603                 :       6817 :         << std::endl;
     604                 :       6817 :     return approx;
     605                 :       6817 :   }
     606                 :      93590 :   return Node::null();
     607                 :     465427 : }
     608                 :            : 
     609                 :     695354 : void ArithEntail::getArithApproximations(Node a,
     610                 :            :                                          std::vector<Node>& approx,
     611                 :            :                                          bool isOverApprox,
     612                 :            :                                          bool isSimple)
     613                 :            : {
     614                 :     695354 :   NodeManager* nm = a.getNodeManager();
     615                 :            :   // We do not handle ADD here since this leads to exponential behavior.
     616                 :            :   // Instead, this is managed, e.g. during checkApprox, where
     617                 :            :   // ADD terms are expanded "on-demand" during the reasoning.
     618         [ +  - ]:    1390708 :   Trace("strings-ent-approx-debug")
     619                 :     695354 :       << "Get arith approximations " << a << std::endl;
     620                 :     695354 :   Kind ak = a.getKind();
     621         [ +  + ]:     695354 :   if (ak == Kind::MULT)
     622                 :            :   {
     623                 :       1009 :     Node c;
     624                 :       1009 :     Node v;
     625         [ +  + ]:       1009 :     if (ArithMSum::getMonomial(a, c, v))
     626                 :            :     {
     627                 :          6 :       bool isNeg = c.getConst<Rational>().sgn() == -1;
     628         [ -  + ]:         12 :       getArithApproximations(
     629                 :          0 :           v, approx, isNeg ? !isOverApprox : isOverApprox, isSimple);
     630         [ -  + ]:          6 :       for (unsigned i = 0, size = approx.size(); i < size; i++)
     631                 :            :       {
     632                 :          0 :         approx[i] = nm->mkNode(Kind::MULT, c, approx[i]);
     633                 :            :       }
     634                 :            :     }
     635                 :       1009 :   }
     636         [ +  + ]:     694345 :   else if (ak == Kind::STRING_LENGTH)
     637                 :            :   {
     638                 :     507229 :     Kind aak = a[0].getKind();
     639         [ +  + ]:     507229 :     if (aak == Kind::STRING_SUBSTR)
     640                 :            :     {
     641                 :            :       // over,under-approximations for len( substr( x, n, m ) )
     642                 :     197352 :       Node lenx = nm->mkNode(Kind::STRING_LENGTH, a[0][0]);
     643         [ +  + ]:      98676 :       if (isOverApprox)
     644                 :            :       {
     645                 :            :         // m >= 0 implies
     646                 :            :         //  m >= len( substr( x, n, m ) )
     647         [ +  + ]:      54866 :         if (check(a[0][2], false, isSimple))
     648                 :            :         {
     649                 :      32105 :           approx.push_back(a[0][2]);
     650                 :            :         }
     651         [ +  + ]:      54866 :         if (check(lenx, a[0][1], false, isSimple))
     652                 :            :         {
     653                 :            :           // n <= len( x ) implies
     654                 :            :           //   len( x ) - n >= len( substr( x, n, m ) )
     655                 :      24613 :           approx.push_back(nm->mkNode(Kind::SUB, lenx, a[0][1]));
     656                 :            :         }
     657                 :            :         else
     658                 :            :         {
     659                 :            :           // len( x ) >= len( substr( x, n, m ) )
     660                 :      30253 :           approx.push_back(lenx);
     661                 :            :         }
     662                 :            :       }
     663                 :            :       else
     664                 :            :       {
     665                 :            :         // 0 <= n and n+m <= len( x ) implies
     666                 :            :         //   m <= len( substr( x, n, m ) )
     667                 :      87620 :         Node npm = nm->mkNode(Kind::ADD, a[0][1], a[0][2]);
     668                 :      87620 :         if (check(a[0][1], false, isSimple)
     669                 :      87620 :             && check(lenx, npm, false, isSimple))
     670                 :            :         {
     671                 :       5278 :           approx.push_back(a[0][2]);
     672                 :            :         }
     673                 :            :         // 0 <= n and n+m >= len( x ) implies
     674                 :            :         //   len(x)-n <= len( substr( x, n, m ) )
     675                 :      87620 :         if (check(a[0][1], false, isSimple)
     676                 :      87620 :             && check(npm, lenx, false, isSimple))
     677                 :            :         {
     678                 :       4147 :           approx.push_back(nm->mkNode(Kind::SUB, lenx, a[0][1]));
     679                 :            :         }
     680                 :      43810 :       }
     681                 :      98676 :     }
     682         [ +  + ]:     408553 :     else if (aak == Kind::STRING_REPLACE)
     683                 :            :     {
     684                 :            :       // over,under-approximations for len( replace( x, y, z ) )
     685                 :            :       // notice this is either len( x ) or ( len( x ) + len( z ) - len( y ) )
     686                 :      39494 :       Node lenx = nm->mkNode(Kind::STRING_LENGTH, a[0][0]);
     687                 :      39494 :       Node leny = nm->mkNode(Kind::STRING_LENGTH, a[0][1]);
     688                 :      39494 :       Node lenz = nm->mkNode(Kind::STRING_LENGTH, a[0][2]);
     689         [ +  + ]:      19747 :       if (isOverApprox)
     690                 :            :       {
     691         [ +  + ]:       9844 :         if (check(leny, lenz, false, isSimple))
     692                 :            :         {
     693                 :            :           // len( y ) >= len( z ) implies
     694                 :            :           //   len( x ) >= len( replace( x, y, z ) )
     695                 :          5 :           approx.push_back(lenx);
     696                 :            :         }
     697                 :            :         else
     698                 :            :         {
     699                 :            :           // len( x ) + len( z ) >= len( replace( x, y, z ) )
     700                 :       9839 :           approx.push_back(nm->mkNode(Kind::ADD, lenx, lenz));
     701                 :            :         }
     702                 :            :       }
     703                 :            :       else
     704                 :            :       {
     705 [ +  - ][ +  + ]:       9903 :         if (check(lenz, leny, false, isSimple)
         [ -  - ][ -  - ]
     706                 :       9903 :             || check(lenz, lenx, false, isSimple))
     707                 :            :         {
     708                 :            :           // len( y ) <= len( z ) or len( x ) <= len( z ) implies
     709                 :            :           //   len( x ) <= len( replace( x, y, z ) )
     710                 :       2566 :           approx.push_back(lenx);
     711                 :            :         }
     712                 :            :         else
     713                 :            :         {
     714                 :            :           // len( x ) - len( y ) <= len( replace( x, y, z ) )
     715                 :       7337 :           approx.push_back(nm->mkNode(Kind::SUB, lenx, leny));
     716                 :            :         }
     717                 :            :       }
     718                 :      19747 :     }
     719         [ +  + ]:     388806 :     else if (aak == Kind::STRING_ITOS)
     720                 :            :     {
     721                 :            :       // over,under-approximations for len( int.to.str( x ) )
     722         [ +  + ]:       5215 :       if (isOverApprox)
     723                 :            :       {
     724         [ +  + ]:       2610 :         if (check(a[0][0], false, isSimple))
     725                 :            :         {
     726         [ +  + ]:       1336 :           if (check(a[0][0], true, isSimple))
     727                 :            :           {
     728                 :            :             // x > 0 implies
     729                 :            :             //   x >= len( int.to.str( x ) )
     730                 :         14 :             approx.push_back(a[0][0]);
     731                 :            :           }
     732                 :            :           else
     733                 :            :           {
     734                 :            :             // x >= 0 implies
     735                 :            :             //   x+1 >= len( int.to.str( x ) )
     736                 :       1322 :             approx.push_back(
     737                 :       2644 :                 nm->mkNode(Kind::ADD, nm->mkConstInt(Rational(1)), a[0][0]));
     738                 :            :           }
     739                 :            :         }
     740                 :            :       }
     741                 :            :       else
     742                 :            :       {
     743         [ +  + ]:       2605 :         if (check(a[0][0], false, isSimple))
     744                 :            :         {
     745                 :            :           // x >= 0 implies
     746                 :            :           //   len( int.to.str( x ) ) >= 1
     747                 :       1454 :           approx.push_back(nm->mkConstInt(Rational(1)));
     748                 :            :         }
     749                 :            :         // other crazy things are possible here, e.g.
     750                 :            :         // len( int.to.str( len( y ) + 10 ) ) >= 2
     751                 :            :       }
     752                 :            :     }
     753                 :            :   }
     754         [ +  + ]:     187116 :   else if (ak == Kind::STRING_INDEXOF)
     755                 :            :   {
     756                 :            :     // over,under-approximations for indexof( x, y, n )
     757         [ +  + ]:      33155 :     if (isOverApprox)
     758                 :            :     {
     759                 :      32386 :       Node lenx = nm->mkNode(Kind::STRING_LENGTH, a[0]);
     760                 :      32386 :       Node leny = nm->mkNode(Kind::STRING_LENGTH, a[1]);
     761         [ +  + ]:      16193 :       if (check(lenx, leny, false, isSimple))
     762                 :            :       {
     763                 :            :         // len( x ) >= len( y ) implies
     764                 :            :         //   len( x ) - len( y ) >= indexof( x, y, n )
     765                 :       1744 :         approx.push_back(nm->mkNode(Kind::SUB, lenx, leny));
     766                 :            :       }
     767                 :            :       else
     768                 :            :       {
     769                 :            :         // len( x ) >= indexof( x, y, n )
     770                 :      14449 :         approx.push_back(lenx);
     771                 :            :       }
     772                 :      16193 :     }
     773                 :            :     else
     774                 :            :     {
     775                 :            :       // TODO?:
     776                 :            :       // contains( substr( x, n, len( x ) ), y ) implies
     777                 :            :       //   n <= indexof( x, y, n )
     778                 :            :       // ...hard to test, runs risk of non-termination
     779                 :            : 
     780                 :            :       // -1 <= indexof( x, y, n )
     781                 :      16962 :       approx.push_back(nm->mkConstInt(Rational(-1)));
     782                 :            :     }
     783                 :            :   }
     784         [ +  + ]:     153961 :   else if (ak == Kind::STRING_STOI)
     785                 :            :   {
     786                 :            :     // over,under-approximations for str.to.int( x )
     787         [ +  + ]:        476 :     if (isOverApprox)
     788                 :            :     {
     789                 :            :       // TODO?:
     790                 :            :       // y >= 0 implies
     791                 :            :       //   y >= str.to.int( int.to.str( y ) )
     792                 :            :     }
     793                 :            :     else
     794                 :            :     {
     795                 :            :       // -1 <= str.to.int( x )
     796                 :        241 :       approx.push_back(nm->mkConstInt(Rational(-1)));
     797                 :            :     }
     798                 :            :   }
     799         [ +  - ]:    1390708 :   Trace("strings-ent-approx-debug")
     800                 :     695354 :       << "Return " << approx.size() << " approximations" << std::endl;
     801                 :     695360 : }
     802                 :            : 
     803                 :       3845 : bool ArithEntail::checkWithEqAssumption(Node assumption, Node a, bool strict)
     804                 :            : {
     805 [ -  + ][ -  + ]:       3845 :   Assert(assumption.getKind() == Kind::EQUAL);
                 [ -  - ]
     806         [ +  - ]:       7690 :   Trace("strings-entail") << "checkWithEqAssumption: " << assumption << " " << a
     807                 :       3845 :                           << ", strict=" << strict << std::endl;
     808                 :            : 
     809                 :            :   // Find candidates variables to compute substitutions for
     810                 :       3845 :   std::unordered_set<Node> candVars;
     811                 :      11535 :   std::vector<Node> toVisit = {assumption};
     812         [ +  + ]:      26203 :   while (!toVisit.empty())
     813                 :            :   {
     814                 :      22358 :     Node curr = toVisit.back();
     815                 :      22358 :     toVisit.pop_back();
     816                 :            : 
     817         [ +  + ]:      40683 :     if (curr.getKind() == Kind::ADD || curr.getKind() == Kind::MULT
     818 [ +  + ][ +  + ]:      40683 :         || curr.getKind() == Kind::SUB || curr.getKind() == Kind::EQUAL)
         [ +  + ][ +  + ]
     819                 :            :     {
     820         [ +  + ]:      27769 :       for (const auto& currChild : curr)
     821                 :            :       {
     822                 :      18513 :         toVisit.push_back(currChild);
     823                 :      18513 :       }
     824                 :            :     }
     825 [ +  + ][ +  - ]:      13102 :     else if (curr.isVar() && Theory::theoryOf(curr) == THEORY_ARITH)
         [ +  + ][ +  + ]
                 [ -  - ]
     826                 :            :     {
     827                 :       3017 :       candVars.insert(curr);
     828                 :            :     }
     829         [ +  + ]:      10085 :     else if (curr.getKind() == Kind::STRING_LENGTH)
     830                 :            :     {
     831                 :       4522 :       candVars.insert(curr);
     832                 :            :     }
     833                 :      22358 :   }
     834                 :            : 
     835                 :            :   // Check if any of the candidate variables are in n
     836                 :       3845 :   Node v;
     837 [ -  + ][ -  + ]:       3845 :   Assert(toVisit.empty());
                 [ -  - ]
     838                 :       3845 :   toVisit.push_back(a);
     839         [ +  + ]:      16615 :   while (!toVisit.empty())
     840                 :            :   {
     841                 :      12926 :     Node curr = toVisit.back();
     842                 :      12926 :     toVisit.pop_back();
     843                 :            : 
     844         [ +  + ]:      22353 :     for (const auto& currChild : curr)
     845                 :            :     {
     846                 :       9427 :       toVisit.push_back(currChild);
     847                 :       9427 :     }
     848                 :            : 
     849         [ +  + ]:      12926 :     if (candVars.find(curr) != candVars.end())
     850                 :            :     {
     851                 :        156 :       v = curr;
     852                 :        156 :       break;
     853                 :            :     }
     854         [ +  + ]:      12926 :   }
     855                 :            : 
     856         [ +  + ]:       3845 :   if (v.isNull())
     857                 :            :   {
     858                 :            :     // No suitable candidate found
     859                 :       3689 :     return false;
     860                 :            :   }
     861                 :            : 
     862                 :        312 :   Node solution = ArithMSum::solveEqualityFor(assumption, v);
     863         [ +  + ]:        156 :   if (solution.isNull())
     864                 :            :   {
     865                 :            :     // Could not solve for v
     866                 :          9 :     return false;
     867                 :            :   }
     868         [ +  - ]:        294 :   Trace("strings-entail") << "checkWithEqAssumption: subs " << v << " -> "
     869                 :        147 :                           << solution << std::endl;
     870                 :            : 
     871                 :        147 :   TNode tv = v;
     872                 :        147 :   TNode tsolution = solution;
     873                 :        147 :   a = a.substitute(tv, tsolution);
     874                 :        147 :   return check(a, strict);
     875                 :       3845 : }
     876                 :            : 
     877                 :       7070 : bool ArithEntail::checkWithAssumption(Node assumption,
     878                 :            :                                       Node a,
     879                 :            :                                       Node b,
     880                 :            :                                       bool strict)
     881                 :            : {
     882                 :       7070 :   NodeManager* nm = assumption.getNodeManager();
     883                 :            : 
     884 [ +  + ][ +  + ]:       7070 :   if (!assumption.isConst() && assumption.getKind() != Kind::EQUAL)
                 [ +  + ]
     885                 :            :   {
     886                 :            :     // We rewrite inequality assumptions from x <= y to x + (str.len s) = y
     887                 :            :     // where s is some fresh string variable. We use (str.len s) because
     888                 :            :     // (str.len s) must be non-negative for the equation to hold.
     889                 :       3841 :     Node x, y;
     890         [ +  + ]:       3841 :     if (assumption.getKind() == Kind::GEQ)
     891                 :            :     {
     892                 :       2651 :       x = assumption[0];
     893                 :       2651 :       y = assumption[1];
     894                 :            :     }
     895                 :            :     else
     896                 :            :     {
     897                 :            :       // (not (>= s t)) --> (>= (t - 1) s)
     898                 :       1190 :       Assert(assumption.getKind() == Kind::NOT
     899                 :            :              && assumption[0].getKind() == Kind::GEQ);
     900                 :       1190 :       x = nm->mkNode(Kind::SUB, assumption[0][1], nm->mkConstInt(Rational(1)));
     901                 :       1190 :       y = assumption[0][0];
     902                 :            :     }
     903                 :            : 
     904                 :       7682 :     Node s = NodeManager::mkBoundVar("slackVal", nm->stringType());
     905                 :       3841 :     Node slen = nm->mkNode(Kind::STRING_LENGTH, s);
     906                 :       7682 :     Node sleny = nm->mkNode(Kind::ADD, y, slen);
     907                 :       7682 :     Node rr = rewriteArith(nm->mkNode(Kind::SUB, x, sleny));
     908         [ -  + ]:       3841 :     if (rr.isConst())
     909                 :            :     {
     910                 :          0 :       assumption = nm->mkConst(rr.getConst<Rational>().sgn() == 0);
     911                 :            :     }
     912                 :            :     else
     913                 :            :     {
     914                 :       3841 :       assumption = nm->mkNode(Kind::EQUAL, x, sleny);
     915                 :            :     }
     916                 :       3841 :   }
     917                 :            : 
     918                 :      14140 :   Node diff = nm->mkNode(Kind::SUB, a, b);
     919                 :       7070 :   bool res = false;
     920         [ +  + ]:       7070 :   if (assumption.isConst())
     921                 :            :   {
     922                 :       3225 :     bool assumptionBool = assumption.getConst<bool>();
     923         [ +  - ]:       3225 :     if (assumptionBool)
     924                 :            :     {
     925                 :       3225 :       res = check(diff, strict);
     926                 :            :     }
     927                 :            :     else
     928                 :            :     {
     929                 :          0 :       res = true;
     930                 :            :     }
     931                 :            :   }
     932                 :            :   else
     933                 :            :   {
     934                 :       3845 :     res = checkWithEqAssumption(assumption, diff, strict);
     935                 :            :   }
     936                 :       7070 :   return res;
     937                 :       7070 : }
     938                 :            : 
     939                 :          0 : bool ArithEntail::checkWithAssumptions(std::vector<Node> assumptions,
     940                 :            :                                        Node a,
     941                 :            :                                        Node b,
     942                 :            :                                        bool strict)
     943                 :            : {
     944                 :            :   // TODO: We currently try to show the entailment with each assumption
     945                 :            :   // independently. In the future, we should make better use of multiple
     946                 :            :   // assumptions.
     947                 :          0 :   bool res = false;
     948         [ -  - ]:          0 :   for (const auto& assumption : assumptions)
     949                 :            :   {
     950         [ -  - ]:          0 :     if (checkWithAssumption(assumption, a, b, strict))
     951                 :            :     {
     952                 :          0 :       res = true;
     953                 :          0 :       break;
     954                 :            :     }
     955                 :            :   }
     956                 :          0 :   return res;
     957                 :            : }
     958                 :            : 
     959                 :            : struct ArithEntailConstantBoundLowerId
     960                 :            : {
     961                 :            : };
     962                 :            : typedef expr::Attribute<ArithEntailConstantBoundLowerId, Node>
     963                 :            :     ArithEntailConstantBoundLower;
     964                 :            : 
     965                 :            : struct ArithEntailConstantBoundUpperId
     966                 :            : {
     967                 :            : };
     968                 :            : typedef expr::Attribute<ArithEntailConstantBoundUpperId, Node>
     969                 :            :     ArithEntailConstantBoundUpper;
     970                 :            : 
     971                 :      89766 : void ArithEntail::setConstantBoundCache(TNode n, Node ret, bool isLower)
     972                 :            : {
     973         [ +  + ]:      89766 :   if (isLower)
     974                 :            :   {
     975                 :            :     ArithEntailConstantBoundLower acbl;
     976                 :      49203 :     n.setAttribute(acbl, ret);
     977                 :            :   }
     978                 :            :   else
     979                 :            :   {
     980                 :            :     ArithEntailConstantBoundUpper acbu;
     981                 :      40563 :     n.setAttribute(acbu, ret);
     982                 :            :   }
     983                 :      89766 : }
     984                 :            : 
     985                 :    1183721 : bool ArithEntail::getConstantBoundCache(TNode n, bool isLower, Node& c)
     986                 :            : {
     987         [ +  + ]:    1183721 :   if (isLower)
     988                 :            :   {
     989                 :            :     ArithEntailConstantBoundLower acbl;
     990         [ +  + ]:    1078686 :     if (n.hasAttribute(acbl))
     991                 :            :     {
     992                 :    1029483 :       c = n.getAttribute(acbl);
     993                 :    1029483 :       return true;
     994                 :            :     }
     995                 :            :   }
     996                 :            :   else
     997                 :            :   {
     998                 :            :     ArithEntailConstantBoundUpper acbu;
     999         [ +  + ]:     105035 :     if (n.hasAttribute(acbu))
    1000                 :            :     {
    1001                 :      64472 :       c = n.getAttribute(acbu);
    1002                 :      64472 :       return true;
    1003                 :            :     }
    1004                 :            :   }
    1005                 :      89766 :   return false;
    1006                 :            : }
    1007                 :            : 
    1008                 :      53847 : Node ArithEntail::getConstantBound(TNode a, bool isLower)
    1009                 :            : {
    1010 [ -  + ][ -  + ]:      53847 :   Assert(rewriteArith(a) == a);
                 [ -  - ]
    1011                 :      53847 :   Node ret;
    1012         [ +  + ]:      53847 :   if (getConstantBoundCache(a, isLower, ret))
    1013                 :            :   {
    1014                 :      45207 :     return ret;
    1015                 :            :   }
    1016         [ +  + ]:       8640 :   if (a.isConst())
    1017                 :            :   {
    1018                 :       1892 :     ret = a;
    1019                 :            :   }
    1020         [ +  + ]:       6748 :   else if (a.getKind() == Kind::STRING_LENGTH)
    1021                 :            :   {
    1022         [ +  - ]:       1145 :     if (isLower)
    1023                 :            :     {
    1024                 :       1145 :       ret = d_zero;
    1025                 :            :     }
    1026                 :            :   }
    1027 [ +  + ][ +  + ]:       5603 :   else if (a.getKind() == Kind::ADD || a.getKind() == Kind::MULT)
                 [ +  + ]
    1028                 :            :   {
    1029                 :       4628 :     std::vector<Node> children;
    1030                 :       4628 :     bool success = true;
    1031         [ +  + ]:       8793 :     for (unsigned i = 0; i < a.getNumChildren(); i++)
    1032                 :            :     {
    1033                 :       7889 :       Node ac = getConstantBound(a[i], isLower);
    1034         [ +  + ]:       7889 :       if (ac.isNull())
    1035                 :            :       {
    1036                 :       2473 :         success = false;
    1037                 :       2473 :         break;
    1038                 :            :       }
    1039                 :            :       else
    1040                 :            :       {
    1041         [ +  + ]:       5416 :         if (ac.getConst<Rational>().sgn() == 0)
    1042                 :            :         {
    1043         [ +  + ]:       1675 :           if (a.getKind() == Kind::MULT)
    1044                 :            :           {
    1045                 :         27 :             success = false;
    1046                 :         27 :             break;
    1047                 :            :           }
    1048                 :            :         }
    1049                 :            :         else
    1050                 :            :         {
    1051         [ +  + ]:       3741 :           if (a.getKind() == Kind::MULT)
    1052                 :            :           {
    1053         [ +  + ]:       1253 :             if ((ac.getConst<Rational>().sgn() > 0) != isLower)
    1054                 :            :             {
    1055                 :       1224 :               success = false;
    1056                 :       1224 :               break;
    1057                 :            :             }
    1058                 :            :           }
    1059                 :       2517 :           children.push_back(ac);
    1060                 :            :         }
    1061                 :            :       }
    1062         [ +  + ]:       7889 :     }
    1063         [ +  + ]:       4628 :     if (success)
    1064                 :            :     {
    1065         [ +  + ]:        904 :       if (children.empty())
    1066                 :            :       {
    1067                 :        310 :         ret = d_zero;
    1068                 :            :       }
    1069         [ +  - ]:        594 :       else if (children.size() == 1)
    1070                 :            :       {
    1071                 :        594 :         ret = children[0];
    1072                 :            :       }
    1073                 :            :       else
    1074                 :            :       {
    1075                 :          0 :         ret = a.getNodeManager()->mkNode(a.getKind(), children);
    1076                 :          0 :         ret = rewriteArith(ret);
    1077                 :            :       }
    1078                 :            :     }
    1079                 :       4628 :   }
    1080         [ +  - ]:      17280 :   Trace("strings-rewrite-cbound")
    1081         [ -  - ]:          0 :       << "Constant " << (isLower ? "lower" : "upper") << " bound for " << a
    1082                 :       8640 :       << " is " << ret << std::endl;
    1083 [ +  + ][ +  - ]:       8640 :   Assert(ret.isNull() || ret.isConst());
         [ -  + ][ -  + ]
                 [ -  - ]
    1084                 :            :   // entailment check should be at least as powerful as computing a lower bound
    1085                 :       8640 :   Assert(!isLower || ret.isNull() || ret.getConst<Rational>().sgn() < 0
    1086                 :            :          || check(a, false));
    1087                 :       8640 :   Assert(!isLower || ret.isNull() || ret.getConst<Rational>().sgn() <= 0
    1088                 :            :          || check(a, true));
    1089                 :            :   // cache
    1090                 :       8640 :   setConstantBoundCache(a, ret, isLower);
    1091                 :       8640 :   return ret;
    1092                 :          0 : }
    1093                 :            : 
    1094                 :    1129874 : Node ArithEntail::getConstantBoundLength(TNode s, bool isLower) const
    1095                 :            : {
    1096 [ -  + ][ -  + ]:    1129874 :   Assert(s.getType().isStringLike());
                 [ -  - ]
    1097                 :    1129874 :   Node ret;
    1098         [ +  + ]:    1129874 :   if (getConstantBoundCache(s, isLower, ret))
    1099                 :            :   {
    1100                 :    1048748 :     return ret;
    1101                 :            :   }
    1102                 :      81126 :   NodeManager* nm = s.getNodeManager();
    1103                 :      81126 :   Kind sk = s.getKind();
    1104         [ +  + ]:      81126 :   if (s.isConst())
    1105                 :            :   {
    1106                 :      13752 :     size_t len = Word::getLength(s);
    1107                 :      13752 :     ret = nm->mkConstInt(Rational(len));
    1108                 :            :   }
    1109 [ +  + ][ -  + ]:      67374 :   else if (sk == Kind::SEQ_UNIT || sk == Kind::STRING_UNIT)
    1110                 :            :   {
    1111                 :       1174 :     ret = nm->mkConstInt(1);
    1112                 :            :   }
    1113         [ +  + ]:      66200 :   else if (sk == Kind::STRING_CONCAT)
    1114                 :            :   {
    1115                 :      26104 :     Rational sum(0);
    1116                 :      26104 :     bool success = true;
    1117         [ +  + ]:      60361 :     for (const Node& sc : s)
    1118                 :            :     {
    1119                 :      47293 :       Node b = getConstantBoundLength(sc, isLower);
    1120         [ +  + ]:      47293 :       if (b.isNull())
    1121                 :            :       {
    1122         [ -  + ]:      13036 :         if (isLower)
    1123                 :            :         {
    1124                 :            :           // assume zero and continue
    1125                 :          0 :           continue;
    1126                 :            :         }
    1127                 :      13036 :         success = false;
    1128                 :      13036 :         break;
    1129                 :            :       }
    1130 [ -  + ][ -  + ]:      34257 :       Assert(b.isConst());
                 [ -  - ]
    1131                 :      34257 :       sum = sum + b.getConst<Rational>();
    1132 [ +  - ][ +  + ]:      60329 :     }
                 [ -  + ]
    1133 [ +  + ][ +  + ]:      26104 :     if (success && (!isLower || sum.sgn() != 0))
         [ +  + ][ +  + ]
    1134                 :            :     {
    1135                 :       5658 :       ret = nm->mkConstInt(sum);
    1136                 :            :     }
    1137                 :      26104 :   }
    1138 [ +  + ][ +  + ]:      81126 :   if (ret.isNull() && isLower)
                 [ +  + ]
    1139                 :            :   {
    1140                 :      27458 :     ret = d_zero;
    1141                 :            :   }
    1142                 :            :   // cache
    1143                 :      81126 :   setConstantBoundCache(s, ret, isLower);
    1144                 :      81126 :   return ret;
    1145                 :          0 : }
    1146                 :            : 
    1147                 :    1865471 : bool ArithEntail::checkSimple(Node a)
    1148                 :            : {
    1149                 :            :   // check whether a >= 0
    1150         [ +  + ]:    1865471 :   if (a.isConst())
    1151                 :            :   {
    1152                 :     741422 :     return a.getConst<Rational>().sgn() >= 0;
    1153                 :            :   }
    1154         [ +  + ]:    1124049 :   else if (a.getKind() == Kind::STRING_LENGTH)
    1155                 :            :   {
    1156                 :            :     // str.len( t ) >= 0
    1157                 :     185845 :     return true;
    1158                 :            :   }
    1159 [ +  + ][ +  + ]:     938204 :   else if (a.getKind() == Kind::ADD || a.getKind() == Kind::MULT)
                 [ +  + ]
    1160                 :            :   {
    1161         [ +  + ]:    1130063 :     for (unsigned i = 0; i < a.getNumChildren(); i++)
    1162                 :            :     {
    1163         [ +  + ]:    1112451 :       if (!checkSimple(a[i]))
    1164                 :            :       {
    1165                 :     854550 :         return false;
    1166                 :            :       }
    1167                 :            :     }
    1168                 :            :     // t1 >= 0 ^ ... ^ tn >= 0 => t1 op ... op tn >= 0
    1169                 :      17612 :     return true;
    1170                 :            :   }
    1171                 :            : 
    1172                 :      66042 :   return false;
    1173                 :            : }
    1174                 :            : 
    1175                 :       1892 : bool ArithEntail::inferZerosInSumGeq(Node x,
    1176                 :            :                                      std::vector<Node>& ys,
    1177                 :            :                                      std::vector<Node>& zeroYs)
    1178                 :            : {
    1179 [ -  + ][ -  + ]:       1892 :   Assert(zeroYs.empty());
                 [ -  - ]
    1180                 :            : 
    1181                 :       1892 :   NodeManager* nm = x.getNodeManager();
    1182                 :            : 
    1183                 :            :   // Check if we can show that y1 + ... + yn >= x
    1184         [ +  + ]:       1892 :   Node sum = (ys.size() > 1) ? nm->mkNode(Kind::ADD, ys) : ys[0];
    1185         [ +  + ]:       1892 :   if (!check(sum, x))
    1186                 :            :   {
    1187                 :       1696 :     return false;
    1188                 :            :   }
    1189                 :            : 
    1190                 :            :   // Try to remove yi one-by-one and check if we can still show:
    1191                 :            :   //
    1192                 :            :   // y1 + ... + yi-1 +  yi+1 + ... + yn >= x
    1193                 :            :   //
    1194                 :            :   // If that's the case, we know that yi can be zero and the inequality still
    1195                 :            :   // holds.
    1196                 :        196 :   size_t i = 0;
    1197         [ +  + ]:        803 :   while (i < ys.size())
    1198                 :            :   {
    1199                 :        607 :     Node yi = ys[i];
    1200                 :        607 :     std::vector<Node>::iterator pos = ys.erase(ys.begin() + i);
    1201         [ +  + ]:        607 :     if (ys.size() > 1)
    1202                 :            :     {
    1203                 :        313 :       sum = nm->mkNode(Kind::ADD, ys);
    1204                 :            :     }
    1205                 :            :     else
    1206                 :            :     {
    1207         [ +  + ]:        294 :       sum = ys.size() == 1 ? ys[0] : d_zero;
    1208                 :            :     }
    1209                 :            : 
    1210         [ +  + ]:        607 :     if (check(sum, x))
    1211                 :            :     {
    1212                 :        117 :       zeroYs.push_back(yi);
    1213                 :            :     }
    1214                 :            :     else
    1215                 :            :     {
    1216                 :        490 :       ys.insert(pos, yi);
    1217                 :        490 :       i++;
    1218                 :            :     }
    1219                 :        607 :   }
    1220                 :        196 :   return true;
    1221                 :       1892 : }
    1222                 :            : 
    1223                 :            : }  // namespace strings
    1224                 :            : }  // namespace theory
    1225                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14