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: 513 551 93.1 %
Date: 2024-09-23 10:48:02 Functions: 20 21 95.2 %
Branches: 390 533 73.2 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Andres Noetzli, Aina Niemetz
       4                 :            :  *
       5                 :            :  * This file is part of the cvc5 project.
       6                 :            :  *
       7                 :            :  * Copyright (c) 2009-2024 by the authors listed in the file AUTHORS
       8                 :            :  * in the top-level source directory and their institutional affiliations.
       9                 :            :  * All rights reserved.  See the file COPYING in the top-level source
      10                 :            :  * directory for licensing information.
      11                 :            :  * ****************************************************************************
      12                 :            :  *
      13                 :            :  * Implementation of arithmetic entailment computation for string terms.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "theory/strings/arith_entail.h"
      17                 :            : 
      18                 :            : #include "expr/attribute.h"
      19                 :            : #include "expr/node_algorithm.h"
      20                 :            : #include "proof/conv_proof_generator.h"
      21                 :            : #include "theory/arith/arith_msum.h"
      22                 :            : #include "theory/arith/arith_poly_norm.h"
      23                 :            : #include "theory/arith/arith_subs.h"
      24                 :            : #include "theory/rewriter.h"
      25                 :            : #include "theory/strings/theory_strings_utils.h"
      26                 :            : #include "theory/strings/word.h"
      27                 :            : #include "theory/theory.h"
      28                 :            : #include "util/rational.h"
      29                 :            : 
      30                 :            : using namespace cvc5::internal::kind;
      31                 :            : 
      32                 :            : namespace cvc5::internal {
      33                 :            : namespace theory {
      34                 :            : namespace strings {
      35                 :            : 
      36                 :     320145 : ArithEntail::ArithEntail(Rewriter* r) : d_rr(r)
      37                 :            : {
      38                 :     320145 :   d_one = NodeManager::currentNM()->mkConstInt(Rational(1));
      39                 :     320145 :   d_zero = NodeManager::currentNM()->mkConstInt(Rational(0));
      40                 :     320145 : }
      41                 :            : 
      42                 :     103127 : Node ArithEntail::rewritePredViaEntailment(const Node& n, bool isSimple)
      43                 :            : {
      44                 :     206254 :   Node exp;
      45                 :     206254 :   return rewritePredViaEntailment(n, exp, isSimple);
      46                 :            : }
      47                 :            : 
      48                 :     104480 : Node ArithEntail::rewritePredViaEntailment(const Node& n,
      49                 :            :                                            Node& exp,
      50                 :            :                                            bool isSimple)
      51                 :            : {
      52                 :     104480 :   NodeManager* nm = NodeManager::currentNM();
      53                 :     104480 :   if (n.getKind() == Kind::EQUAL && n[0].getType().isInteger())
      54                 :            :   {
      55                 :      40095 :     exp = nm->mkNode(Kind::SUB, nm->mkNode(Kind::SUB, n[0], n[1]), d_one);
      56         [ +  + ]:      40095 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      57                 :            :     {
      58                 :       3988 :       return nm->mkConst(false);
      59                 :            :     }
      60                 :      38101 :     exp = nm->mkNode(Kind::SUB, nm->mkNode(Kind::SUB, n[1], n[0]), d_one);
      61         [ +  + ]:      38101 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      62                 :            :     {
      63                 :       2264 :       return nm->mkConst(false);
      64                 :            :     }
      65                 :      36969 :     exp = Node::null();
      66         [ +  + ]:      36969 :     if (checkEq(n[0], n[1]))
      67                 :            :     {
      68                 :            :       // explanation is null
      69                 :       5392 :       return nm->mkConst(true);
      70                 :            :     }
      71                 :            :   }
      72         [ +  - ]:      64385 :   else if (n.getKind() == Kind::GEQ)
      73                 :            :   {
      74                 :      64385 :     exp = nm->mkNode(Kind::SUB, n[0], n[1]);
      75         [ +  + ]:      64385 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      76                 :            :     {
      77                 :       2998 :       return nm->mkConst(true);
      78                 :            :     }
      79                 :      62886 :     exp = nm->mkNode(Kind::SUB, nm->mkNode(Kind::SUB, n[1], n[0]), d_one);
      80         [ +  + ]:      62886 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      81                 :            :     {
      82                 :      13026 :       return nm->mkConst(false);
      83                 :            :     }
      84                 :      56373 :     exp = Node::null();
      85                 :            :   }
      86                 :      90646 :   return Node::null();
      87                 :            : }
      88                 :            : 
      89                 :    2547010 : Node ArithEntail::rewriteArith(Node a)
      90                 :            : {
      91 [ -  + ][ -  - ]:    5094030 :   AlwaysAssert(a.getType().isInteger())
      92                 :    2547010 :       << "Bad term: " << a << " " << a.getType();
      93         [ +  + ]:    2547010 :   if (d_rr != nullptr)
      94                 :            :   {
      95                 :    2121300 :     return d_rr->rewrite(a);
      96                 :            :   }
      97                 :            :   // Otherwise, use the poly norm utility. This is important since the rewrite
      98                 :            :   // must be justified by ARITH_POLY_NORM when in proof mode (when d_rr is
      99                 :            :   // null).
     100                 :     851438 :   Node an = arith::PolyNorm::getPolyNorm(a);
     101                 :     425719 :   return an;
     102                 :            : }
     103                 :            : 
     104                 :      51003 : Node ArithEntail::normalizeGeq(const Node& n) const
     105                 :            : {
     106                 :      51003 :   NodeManager* nm = NodeManager::currentNM();
     107                 :      97834 :   if (n.getNumChildren() != 2 || !n[0].getType().isInteger()
     108                 :      97834 :       || !n[1].getType().isInteger())
     109                 :            :   {
     110                 :       4228 :     return Node::null();
     111                 :            :   }
     112 [ +  + ][ +  + ]:      46775 :   switch (n.getKind())
                    [ + ]
     113                 :            :   {
     114                 :      16281 :     case Kind::GEQ: return n;
     115                 :      11156 :     case Kind::LEQ: return nm->mkNode(Kind::GEQ, n[1], n[0]);
     116                 :       4324 :     case Kind::LT:
     117                 :            :       return nm->mkNode(
     118                 :            :           Kind::GEQ,
     119                 :            :           n[1],
     120                 :       4324 :           nm->mkNode(Kind::ADD, n[0], nm->mkConstInt(Rational(1))));
     121                 :       6106 :     case Kind::GT:
     122                 :            :       return nm->mkNode(
     123                 :            :           Kind::GEQ,
     124                 :            :           n[0],
     125                 :       6106 :           nm->mkNode(Kind::ADD, n[1], nm->mkConstInt(Rational(1))));
     126                 :       8908 :     default: break;
     127                 :            :   }
     128                 :       8908 :   return Node::null();
     129                 :            : }
     130                 :            : 
     131                 :      55670 : Node ArithEntail::rewriteLengthIntro(const Node& n,
     132                 :            :                                      TConvProofGenerator* pg) const
     133                 :            : {
     134                 :      55670 :   NodeManager* nm = NodeManager::currentNM();
     135                 :     111340 :   std::unordered_map<TNode, Node> visited;
     136                 :      55670 :   std::unordered_map<TNode, Node>::iterator it;
     137                 :     111340 :   std::vector<TNode> visit;
     138                 :      55670 :   TNode cur;
     139                 :      55670 :   visit.push_back(n);
     140                 :    1025140 :   do
     141                 :            :   {
     142                 :    1080810 :     cur = visit.back();
     143                 :    1080810 :     it = visited.find(cur);
     144         [ +  + ]:    1080810 :     if (it == visited.end())
     145                 :            :     {
     146         [ +  + ]:     580678 :       if (cur.getNumChildren() == 0)
     147                 :            :       {
     148                 :     221389 :         visit.pop_back();
     149                 :     221389 :         visited[cur] = cur;
     150                 :     221389 :         continue;
     151                 :            :       }
     152                 :     359289 :       visited.emplace(cur, Node::null());
     153                 :     359289 :       visit.insert(visit.end(), cur.begin(), cur.end());
     154                 :     359289 :       continue;
     155                 :            :     }
     156                 :     500132 :     visit.pop_back();
     157         [ +  + ]:     500132 :     if (it->second.isNull())
     158                 :            :     {
     159                 :     359289 :       Kind k = cur.getKind();
     160                 :     359289 :       bool childChanged = false;
     161                 :     718578 :       std::vector<Node> children;
     162         [ +  + ]:    1025140 :       for (const Node& cn : cur)
     163                 :            :       {
     164                 :     665851 :         it = visited.find(cn);
     165 [ -  + ][ -  + ]:     665851 :         Assert(it != visited.end());
                 [ -  - ]
     166 [ -  + ][ -  + ]:     665851 :         Assert(!it->second.isNull());
                 [ -  - ]
     167                 :     665851 :         children.push_back(it->second);
     168 [ +  + ][ +  + ]:     665851 :         childChanged = childChanged || it->second != cn;
     169                 :            :       }
     170                 :     718578 :       Node ret = cur;
     171         [ +  + ]:     359289 :       if (childChanged)
     172                 :            :       {
     173                 :      38078 :         ret = nm->mkNode(k, children);
     174                 :            :       }
     175         [ +  + ]:     359289 :       if (k == Kind::STRING_LENGTH)
     176                 :            :       {
     177                 :     205380 :         std::vector<Node> cc;
     178         [ +  + ]:     205380 :         for (const Node& c : children)
     179                 :            :         {
     180                 :     102690 :           utils::getConcat(c, cc);
     181                 :            :         }
     182                 :     205380 :         std::vector<Node> sum;
     183         [ +  + ]:     242962 :         for (const Node& c : cc)
     184                 :            :         {
     185 [ +  + ][ +  + ]:     140272 :           if (c.isConst() && c.getType().isString())
         [ +  + ][ +  + ]
                 [ -  - ]
     186                 :            :           {
     187                 :      42020 :             sum.push_back(nm->mkConstInt(Rational(Word::getLength(c))));
     188                 :            :           }
     189                 :            :           else
     190                 :            :           {
     191                 :      98252 :             sum.push_back(nm->mkNode(Kind::STRING_LENGTH, c));
     192                 :            :           }
     193                 :            :         }
     194 [ -  + ][ -  + ]:     102690 :         Assert(!sum.empty());
                 [ -  - ]
     195         [ +  + ]:     205380 :         Node rret = sum.size() == 1 ? sum[0] : nm->mkNode(Kind::ADD, sum);
     196         [ +  + ]:     102690 :         if (pg != nullptr)
     197                 :            :         {
     198                 :       3468 :           pg->addRewriteStep(ret,
     199                 :            :                              rret,
     200                 :            :                              nullptr,
     201                 :            :                              false,
     202                 :            :                              TrustId::MACRO_THEORY_REWRITE_RCONS_SIMPLE);
     203                 :            :         }
     204                 :     102690 :         ret = rret;
     205                 :            :       }
     206                 :     359289 :       visited[cur] = ret;
     207                 :            :     }
     208         [ +  + ]:    1080810 :   } while (!visit.empty());
     209 [ -  + ][ -  + ]:      55670 :   Assert(visited.find(n) != visited.end());
                 [ -  - ]
     210 [ -  + ][ -  + ]:      55670 :   Assert(!visited.find(n)->second.isNull());
                 [ -  - ]
     211                 :     111340 :   return visited[n];
     212                 :            : }
     213                 :            : 
     214                 :      45802 : bool ArithEntail::checkEq(Node a, Node b)
     215                 :            : {
     216         [ +  + ]:      45802 :   if (a == b)
     217                 :            :   {
     218                 :       3396 :     return true;
     219                 :            :   }
     220                 :      84812 :   Node ar = rewriteArith(a);
     221                 :      84812 :   Node br = rewriteArith(b);
     222                 :      42406 :   return ar == br;
     223                 :            : }
     224                 :            : 
     225                 :     708499 : bool ArithEntail::check(Node a, Node b, bool strict, bool isSimple)
     226                 :            : {
     227         [ +  + ]:     708499 :   if (a == b)
     228                 :            :   {
     229                 :      53834 :     return !strict;
     230                 :            :   }
     231                 :    1309330 :   Node diff = NodeManager::currentNM()->mkNode(Kind::SUB, a, b);
     232                 :     654665 :   return check(diff, strict, isSimple);
     233                 :            : }
     234                 :            : 
     235                 :    1071290 : bool ArithEntail::check(Node a, bool strict, bool isSimple)
     236                 :            : {
     237         [ +  + ]:    1071290 :   if (a.isConst())
     238                 :            :   {
     239         [ +  + ]:     116278 :     return a.getConst<Rational>().sgn() >= (strict ? 1 : 0);
     240                 :            :   }
     241                 :    2160300 :   Node ar = strict ? NodeManager::currentNM()->mkNode(Kind::SUB, a, d_one) : a;
     242                 :     955009 :   ar = rewriteArith(ar);
     243                 :            :   // if simple, just call the checkSimple routine.
     244         [ +  + ]:     955009 :   if (isSimple)
     245                 :            :   {
     246                 :      45537 :     return checkSimple(ar);
     247                 :            :   }
     248                 :     909472 :   Node ara = findApprox(ar, isSimple);
     249                 :     909472 :   return !ara.isNull();
     250                 :            : }
     251                 :            : 
     252                 :    1115310 : Node ArithEntail::findApprox(Node ar, bool isSimple)
     253                 :            : {
     254         [ +  + ]:    1115310 :   std::map<Node, Node>& cache = isSimple ? d_approxCacheSimple : d_approxCache;
     255                 :    1115310 :   std::map<Node, Node>::iterator it = cache.find(ar);
     256         [ +  + ]:    1115310 :   if (it != cache.end())
     257                 :            :   {
     258                 :     784555 :     return it->second;
     259                 :            :   }
     260                 :     661506 :   Node ret;
     261         [ +  + ]:     330753 :   if (checkSimple(ar))
     262                 :            :   {
     263                 :            :     // didn't need approximation
     264                 :      21794 :     ret = ar;
     265                 :            :   }
     266                 :            :   else
     267                 :            :   {
     268                 :     308959 :     ret = findApproxInternal(ar, isSimple);
     269                 :            :   }
     270                 :     330753 :   cache[ar] = ret;
     271                 :     330753 :   return ret;
     272                 :            : }
     273                 :            : 
     274                 :     308959 : Node ArithEntail::findApproxInternal(Node ar, bool isSimple)
     275                 :            : {
     276                 :     617918 :   Assert(rewriteArith(ar) == ar)
     277                 :     308959 :       << "Not rewritten " << ar << ", got " << rewriteArith(ar);
     278                 :     308959 :   NodeManager* nm = NodeManager::currentNM();
     279                 :     617918 :   std::map<Node, Node> msum;
     280         [ +  - ]:     617918 :   Trace("strings-ent-approx-debug")
     281                 :     308959 :       << "Setup arithmetic approximations for " << ar << std::endl;
     282         [ -  + ]:     308959 :   if (!ArithMSum::getMonomialSum(ar, msum))
     283                 :            :   {
     284         [ -  - ]:          0 :     Trace("strings-ent-approx-debug")
     285                 :          0 :         << "...failed to get monomial sum!" << std::endl;
     286                 :          0 :     return Node::null();
     287                 :            :   }
     288                 :            :   // for each monomial v*c, mApprox[v] a list of
     289                 :            :   // possibilities for how the term can be soundly approximated, that is,
     290                 :            :   // if mApprox[v] contains av, then v*c > av*c. Notice that if c
     291                 :            :   // is positive, then v > av, otherwise if c is negative, then v < av.
     292                 :            :   // In other words, av is an under-approximation if c is positive, and an
     293                 :            :   // over-approximation if c is negative.
     294                 :     308959 :   bool changed = false;
     295                 :     617918 :   std::map<Node, std::vector<Node> > mApprox;
     296                 :            :   // map from approximations to their monomial sums
     297                 :     617918 :   std::map<Node, std::map<Node, Node> > approxMsums;
     298                 :            :   // aarSum stores each monomial that does not have multiple approximations
     299                 :     617918 :   std::vector<Node> aarSum;
     300                 :            :   // stores the witness
     301                 :     617918 :   arith::ArithSubs approxMap;
     302         [ +  + ]:    1007500 :   for (std::pair<const Node, Node>& m : msum)
     303                 :            :   {
     304                 :    1397090 :     Node v = m.first;
     305                 :    1397090 :     Node c = m.second;
     306         [ +  - ]:    1397090 :     Trace("strings-ent-approx-debug")
     307                 :     698545 :         << "Get approximations " << v << "..." << std::endl;
     308         [ +  + ]:     698545 :     if (v.isNull())
     309                 :            :     {
     310 [ -  + ][ -  + ]:     453470 :       Node mn = c.isNull() ? nm->mkConstInt(Rational(1)) : c;
                 [ -  - ]
     311                 :     226735 :       aarSum.push_back(mn);
     312                 :            :     }
     313                 :            :     else
     314                 :            :     {
     315                 :            :       // c.isNull() means c = 1
     316 [ +  + ][ +  + ]:     471810 :       bool isOverApprox = !c.isNull() && c.getConst<Rational>().sgn() == -1;
     317                 :     471810 :       std::vector<Node>& approx = mApprox[v];
     318                 :     943620 :       std::unordered_set<Node> visited;
     319                 :     943620 :       std::vector<Node> toProcess;
     320                 :     471810 :       toProcess.push_back(v);
     321                 :      67540 :       do
     322                 :            :       {
     323                 :    1078700 :         Node curr = toProcess.back();
     324         [ +  - ]:     539350 :         Trace("strings-ent-approx-debug") << "  process " << curr << std::endl;
     325                 :     539350 :         curr = rewriteArith(curr);
     326                 :     539350 :         toProcess.pop_back();
     327         [ +  + ]:     539350 :         if (visited.find(curr) == visited.end())
     328                 :            :         {
     329                 :     532888 :           visited.insert(curr);
     330                 :    1065780 :           std::vector<Node> currApprox;
     331                 :     532888 :           getArithApproximations(curr, currApprox, isOverApprox, isSimple);
     332         [ +  + ]:     532888 :           if (currApprox.empty())
     333                 :            :           {
     334         [ +  - ]:     931176 :             Trace("strings-ent-approx-debug")
     335                 :     465588 :                 << "...approximation: " << curr << std::endl;
     336                 :            :             // no approximations, thus curr is a possibility
     337                 :     465588 :             approx.push_back(curr);
     338                 :            :           }
     339         [ +  + ]:      67300 :           else if (isSimple)
     340                 :            :           {
     341                 :            :             // don't rewrite or re-approximate
     342                 :      17748 :             approx = currApprox;
     343                 :            :           }
     344                 :            :           else
     345                 :            :           {
     346                 :            :             toProcess.insert(
     347                 :      49552 :                 toProcess.end(), currApprox.begin(), currApprox.end());
     348                 :            :           }
     349                 :            :         }
     350         [ +  + ]:     539350 :       } while (!toProcess.empty());
     351 [ -  + ][ -  + ]:     471810 :       Assert(!approx.empty());
                 [ -  - ]
     352                 :            :       // if we have only one approximation, move it to final
     353         [ +  + ]:     471810 :       if (approx.size() == 1)
     354                 :            :       {
     355         [ +  + ]:     457662 :         if (v != approx[0])
     356                 :            :         {
     357                 :      46680 :           changed = true;
     358         [ +  - ]:      93360 :           Trace("strings-ent-approx")
     359                 :      46680 :               << "- Propagate (" << (d_rr == nullptr) << ", " << isSimple
     360                 :      46680 :               << ") " << v << " = " << approx[0] << std::endl;
     361                 :      46680 :           approxMap.add(v, approx[0]);
     362                 :            :         }
     363                 :    1372990 :         Node mn = ArithMSum::mkCoeffTerm(c, approx[0]);
     364                 :     457662 :         aarSum.push_back(mn);
     365                 :     457662 :         mApprox.erase(v);
     366                 :            :       }
     367                 :            :       else
     368                 :            :       {
     369                 :            :         // compute monomial sum form for each approximation, used below
     370         [ +  + ]:      42570 :         for (const Node& aa : approx)
     371                 :            :         {
     372         [ +  + ]:      28422 :           if (approxMsums.find(aa) == approxMsums.end())
     373                 :            :           {
     374                 :            :             CVC5_UNUSED bool ret =
     375                 :      27704 :                 ArithMSum::getMonomialSum(aa, approxMsums[aa]);
     376 [ -  + ][ -  + ]:      27704 :             Assert(ret);
                 [ -  - ]
     377                 :            :           }
     378                 :            :         }
     379                 :      14148 :         changed = true;
     380                 :            :       }
     381                 :            :     }
     382                 :            :   }
     383         [ +  + ]:     308959 :   if (!changed)
     384                 :            :   {
     385                 :            :     // approximations had no effect, return
     386         [ +  - ]:     256578 :     Trace("strings-ent-approx-debug") << "...no approximations" << std::endl;
     387                 :     256578 :     return Node::null();
     388                 :            :   }
     389                 :            :   // get the current "fixed" sum for the abstraction of ar
     390                 :            :   Node aar =
     391                 :      52381 :       aarSum.empty()
     392                 :       2879 :           ? d_zero
     393 [ +  + ][ +  + ]:     104762 :           : (aarSum.size() == 1 ? aarSum[0] : nm->mkNode(Kind::ADD, aarSum));
     394                 :      52381 :   aar = rewriteArith(aar);
     395         [ +  - ]:     104762 :   Trace("strings-ent-approx-debug")
     396                 :      52381 :       << "...processed fixed sum " << aar << " with " << mApprox.size()
     397                 :      52381 :       << " approximated monomials." << std::endl;
     398                 :            :   // if we have a choice of how to approximate
     399         [ +  + ]:      52381 :   if (!mApprox.empty())
     400                 :            :   {
     401                 :            :     // convert aar back to monomial sum
     402                 :      13878 :     std::map<Node, Node> msumAar;
     403         [ -  + ]:      13878 :     if (!ArithMSum::getMonomialSum(aar, msumAar))
     404                 :            :     {
     405                 :          0 :       return Node::null();
     406                 :            :     }
     407         [ -  + ]:      13878 :     if (TraceIsOn("strings-ent-approx"))
     408                 :            :     {
     409         [ -  - ]:          0 :       Trace("strings-ent-approx")
     410                 :          0 :           << "---- Check arithmetic entailment by under-approximation " << ar
     411                 :          0 :           << " >= 0" << std::endl;
     412         [ -  - ]:          0 :       Trace("strings-ent-approx") << "FIXED:" << std::endl;
     413                 :          0 :       ArithMSum::debugPrintMonomialSum(msumAar, "strings-ent-approx");
     414         [ -  - ]:          0 :       Trace("strings-ent-approx") << "APPROX:" << std::endl;
     415         [ -  - ]:          0 :       for (std::pair<const Node, std::vector<Node> >& a : mApprox)
     416                 :            :       {
     417                 :          0 :         Node c = msum[a.first];
     418         [ -  - ]:          0 :         Trace("strings-ent-approx") << "  ";
     419         [ -  - ]:          0 :         if (!c.isNull())
     420                 :            :         {
     421         [ -  - ]:          0 :           Trace("strings-ent-approx") << c << " * ";
     422                 :            :         }
     423         [ -  - ]:          0 :         Trace("strings-ent-approx")
     424                 :          0 :             << a.second << " ...from " << a.first << std::endl;
     425                 :            :       }
     426         [ -  - ]:          0 :       Trace("strings-ent-approx") << std::endl;
     427                 :            :     }
     428                 :      13878 :     Rational one(1);
     429                 :            :     // incorporate monomials one at a time that have a choice of approximations
     430         [ +  + ]:      28026 :     while (!mApprox.empty())
     431                 :            :     {
     432                 :      14148 :       Node v;
     433                 :      14148 :       Node vapprox;
     434                 :      14148 :       int maxScore = -1;
     435                 :            :       // Look at each approximation, take the one with the best score.
     436                 :            :       // Notice that we are in the process of trying to prove
     437                 :            :       // ( c1*t1 + .. + cn*tn ) + ( approx_1 | ... | approx_m ) >= 0,
     438                 :            :       // where c1*t1 + .. + cn*tn is the "fixed" component of our sum (aar)
     439                 :            :       // and approx_1 ... approx_m are possible approximations. The
     440                 :            :       // intution here is that we want coefficients c1...cn to be positive.
     441                 :            :       // This is because arithmetic string terms t1...tn (which may be
     442                 :            :       // applications of len, indexof, str.to.int) are never entailed to be
     443                 :            :       // negative. Hence, we add the approx_i that contributes the "most"
     444                 :            :       // towards making all constants c1...cn positive and cancelling negative
     445                 :            :       // monomials in approx_i itself.
     446         [ +  - ]:      14148 :       for (std::pair<const Node, std::vector<Node> >& nam : mApprox)
     447                 :            :       {
     448                 :      14148 :         Node cr = msum[nam.first];
     449         [ +  + ]:      42570 :         for (const Node& aa : nam.second)
     450                 :            :         {
     451                 :      28422 :           unsigned helpsCancelCount = 0;
     452                 :      28422 :           unsigned addsObligationCount = 0;
     453                 :      28422 :           std::map<Node, Node>::iterator it;
     454                 :            :           // we are processing an approximation cr*( c1*t1 + ... + cn*tn )
     455         [ +  + ]:      62435 :           for (std::pair<const Node, Node>& aam : approxMsums[aa])
     456                 :            :           {
     457                 :            :             // Say aar is of the form t + c*ti, and aam is the monomial ci*ti
     458                 :            :             // where ci != 0. We say aam:
     459                 :            :             // (1) helps cancel if c != 0 and c>0 != ci>0
     460                 :            :             // (2) adds obligation if c>=0 and c+ci<0
     461                 :      68026 :             Node ti = aam.first;
     462                 :      68026 :             Node ci = aam.second;
     463         [ +  + ]:      34013 :             if (!cr.isNull())
     464                 :            :             {
     465         [ +  + ]:      80821 :               ci = ci.isNull() ? cr
     466                 :      80821 :                                : rewriteArith(nm->mkNode(Kind::MULT, ci, cr));
     467                 :            :             }
     468         [ +  - ]:      34013 :             Trace("strings-ent-approx-debug") << ci << "*" << ti << " ";
     469         [ +  + ]:      34013 :             int ciSgn = ci.isNull() ? 1 : ci.getConst<Rational>().sgn();
     470                 :      34013 :             it = msumAar.find(ti);
     471         [ +  + ]:      34013 :             if (it != msumAar.end())
     472                 :            :             {
     473                 :      32838 :               Node c = it->second;
     474         [ +  + ]:      16419 :               int cSgn = c.isNull() ? 1 : c.getConst<Rational>().sgn();
     475         [ +  + ]:      16419 :               if (cSgn == 0)
     476                 :            :               {
     477         [ +  + ]:       4022 :                 addsObligationCount += (ciSgn == -1 ? 1 : 0);
     478                 :            :               }
     479         [ +  + ]:      12397 :               else if (cSgn != ciSgn)
     480                 :            :               {
     481                 :       8925 :                 helpsCancelCount++;
     482         [ +  + ]:      17850 :                 Rational r1 = c.isNull() ? one : c.getConst<Rational>();
     483         [ +  + ]:      17850 :                 Rational r2 = ci.isNull() ? one : ci.getConst<Rational>();
     484                 :      17850 :                 Rational r12 = r1 + r2;
     485         [ +  + ]:       8925 :                 if (r12.sgn() == -1)
     486                 :            :                 {
     487                 :       4144 :                   addsObligationCount++;
     488                 :            :                 }
     489                 :            :               }
     490                 :            :             }
     491                 :            :             else
     492                 :            :             {
     493         [ +  + ]:      17594 :               addsObligationCount += (ciSgn == -1 ? 1 : 0);
     494                 :            :             }
     495                 :            :           }
     496         [ +  - ]:      56844 :           Trace("strings-ent-approx-debug")
     497                 :          0 :               << "counts=" << helpsCancelCount << "," << addsObligationCount
     498                 :      28422 :               << " for " << aa << " into " << aar << std::endl;
     499         [ +  + ]:      28422 :           int score = (addsObligationCount > 0 ? 0 : 2)
     500         [ +  + ]:      28422 :                       + (helpsCancelCount > 0 ? 1 : 0);
     501                 :            :           // if its the best, update v and vapprox
     502 [ +  + ][ +  + ]:      28422 :           if (v.isNull() || score > maxScore)
                 [ +  + ]
     503                 :            :           {
     504                 :      19055 :             v = nam.first;
     505                 :      19055 :             vapprox = aa;
     506                 :      19055 :             maxScore = score;
     507                 :            :           }
     508                 :            :         }
     509         [ +  - ]:      14148 :         if (!v.isNull())
     510                 :            :         {
     511                 :      14148 :           break;
     512                 :            :         }
     513                 :            :       }
     514         [ +  - ]:      28296 :       Trace("strings-ent-approx") << "- Decide (" << (d_rr == nullptr) << ") "
     515                 :      14148 :                                   << v << " = " << vapprox << std::endl;
     516                 :            :       // we incorporate v approximated by vapprox into the overall approximation
     517                 :            :       // for ar
     518 [ +  - ][ +  - ]:      28296 :       Assert(!v.isNull() && !vapprox.isNull());
         [ -  + ][ -  - ]
     519 [ -  + ][ -  + ]:      14148 :       Assert(msum.find(v) != msum.end());
                 [ -  - ]
     520                 :      28296 :       Node mn = ArithMSum::mkCoeffTerm(msum[v], vapprox);
     521                 :      14148 :       aar = nm->mkNode(Kind::ADD, aar, mn);
     522                 :      14148 :       approxMap.add(v, vapprox);
     523                 :            :       // update the msumAar map
     524                 :      14148 :       aar = rewriteArith(aar);
     525                 :      14148 :       msumAar.clear();
     526         [ -  + ]:      14148 :       if (!ArithMSum::getMonomialSum(aar, msumAar))
     527                 :            :       {
     528                 :          0 :         Assert(false);
     529                 :            :         Trace("strings-ent-approx")
     530                 :            :             << "...failed to get monomial sum!" << std::endl;
     531                 :            :         return Node::null();
     532                 :            :       }
     533                 :            :       // we have processed the approximation for v
     534                 :      14148 :       mApprox.erase(v);
     535                 :            :     }
     536         [ +  - ]:      13878 :     Trace("strings-ent-approx") << "-----------------" << std::endl;
     537                 :            :   }
     538         [ -  + ]:      52381 :   if (aar == ar)
     539                 :            :   {
     540         [ -  - ]:          0 :     Trace("strings-ent-approx-debug")
     541                 :          0 :         << "...approximation had no effect" << std::endl;
     542                 :            :     // this should never happen, but we avoid the infinite loop for sanity here
     543                 :          0 :     Assert(false);
     544                 :            :     return Node::null();
     545                 :            :   }
     546                 :            :   // Check entailment on the approximation of ar.
     547                 :            :   // Notice that this may trigger further reasoning by approximation. For
     548                 :            :   // example, len( replace( x ++ y, substr( x, 0, n ), z ) ) may be
     549                 :            :   // under-approximated as len( x ) + len( y ) - len( substr( x, 0, n ) ) on
     550                 :            :   // this call, where in the recursive call we may over-approximate
     551                 :            :   // len( substr( x, 0, n ) ) as len( x ). In this example, we can infer
     552                 :            :   // that len( replace( x ++ y, substr( x, 0, n ), z ) ) >= len( y ) in two
     553                 :            :   // steps.
     554         [ +  + ]:      52381 :   if (check(aar, false, isSimple))
     555                 :            :   {
     556         [ +  - ]:       9716 :     Trace("strings-ent-approx")
     557                 :          0 :         << "*** StrArithApprox: showed " << ar
     558                 :       4858 :         << " >= 0 using under-approximation!" << std::endl;
     559         [ +  - ]:       9716 :     Trace("strings-ent-approx")
     560                 :       4858 :         << "*** StrArithApprox: rewritten was " << aar << std::endl;
     561                 :            :     // Apply arithmetic substitution, which ensures we only replace terms
     562                 :            :     // in the top-level arithmetic skeleton of ar.
     563                 :       9716 :     Node approx = approxMap.applyArith(ar);
     564         [ +  - ]:       9716 :     Trace("strings-ent-approx")
     565                 :          0 :         << "*** StrArithApprox: under-approximation was " << approx
     566                 :       4858 :         << std::endl;
     567                 :       4858 :     return approx;
     568                 :            :   }
     569                 :      47523 :   return Node::null();
     570                 :            : }
     571                 :            : 
     572                 :     533747 : void ArithEntail::getArithApproximations(Node a,
     573                 :            :                                          std::vector<Node>& approx,
     574                 :            :                                          bool isOverApprox,
     575                 :            :                                          bool isSimple)
     576                 :            : {
     577                 :     533747 :   NodeManager* nm = NodeManager::currentNM();
     578                 :            :   // We do not handle ADD here since this leads to exponential behavior.
     579                 :            :   // Instead, this is managed, e.g. during checkApprox, where
     580                 :            :   // ADD terms are expanded "on-demand" during the reasoning.
     581         [ +  - ]:    1067490 :   Trace("strings-ent-approx-debug")
     582                 :     533747 :       << "Get arith approximations " << a << std::endl;
     583                 :     533747 :   Kind ak = a.getKind();
     584         [ +  + ]:     533747 :   if (ak == Kind::MULT)
     585                 :            :   {
     586                 :       1718 :     Node c;
     587                 :       1718 :     Node v;
     588         [ +  - ]:        859 :     if (ArithMSum::getMonomial(a, c, v))
     589                 :            :     {
     590                 :        859 :       bool isNeg = c.getConst<Rational>().sgn() == -1;
     591         [ +  + ]:       1718 :       getArithApproximations(
     592                 :         96 :           v, approx, isNeg ? !isOverApprox : isOverApprox, isSimple);
     593         [ +  + ]:        863 :       for (unsigned i = 0, size = approx.size(); i < size; i++)
     594                 :            :       {
     595                 :          4 :         approx[i] = nm->mkNode(Kind::MULT, c, approx[i]);
     596                 :            :       }
     597                 :            :     }
     598                 :            :   }
     599         [ +  + ]:     532888 :   else if (ak == Kind::STRING_LENGTH)
     600                 :            :   {
     601                 :     301787 :     Kind aak = a[0].getKind();
     602         [ +  + ]:     301787 :     if (aak == Kind::STRING_SUBSTR)
     603                 :            :     {
     604                 :            :       // over,under-approximations for len( substr( x, n, m ) )
     605                 :     168750 :       Node lenx = nm->mkNode(Kind::STRING_LENGTH, a[0][0]);
     606         [ +  + ]:      56250 :       if (isOverApprox)
     607                 :            :       {
     608                 :            :         // m >= 0 implies
     609                 :            :         //  m >= len( substr( x, n, m ) )
     610         [ +  + ]:      33134 :         if (check(a[0][2], false, isSimple))
     611                 :            :         {
     612                 :      18380 :           approx.push_back(a[0][2]);
     613                 :            :         }
     614         [ +  + ]:      33134 :         if (check(lenx, a[0][1], false, isSimple))
     615                 :            :         {
     616                 :            :           // n <= len( x ) implies
     617                 :            :           //   len( x ) - n >= len( substr( x, n, m ) )
     618                 :      14428 :           approx.push_back(nm->mkNode(Kind::SUB, lenx, a[0][1]));
     619                 :            :         }
     620                 :            :         else
     621                 :            :         {
     622                 :            :           // len( x ) >= len( substr( x, n, m ) )
     623                 :      18706 :           approx.push_back(lenx);
     624                 :            :         }
     625                 :            :       }
     626                 :            :       else
     627                 :            :       {
     628                 :            :         // 0 <= n and n+m <= len( x ) implies
     629                 :            :         //   m <= len( substr( x, n, m ) )
     630                 :      69348 :         Node npm = nm->mkNode(Kind::ADD, a[0][1], a[0][2]);
     631                 :      46232 :         if (check(a[0][1], false, isSimple)
     632                 :      46232 :             && check(lenx, npm, false, isSimple))
     633                 :            :         {
     634                 :       3920 :           approx.push_back(a[0][2]);
     635                 :            :         }
     636                 :            :         // 0 <= n and n+m >= len( x ) implies
     637                 :            :         //   len(x)-n <= len( substr( x, n, m ) )
     638                 :      46232 :         if (check(a[0][1], false, isSimple)
     639                 :      46232 :             && check(npm, lenx, false, isSimple))
     640                 :            :         {
     641                 :       2969 :           approx.push_back(nm->mkNode(Kind::SUB, lenx, a[0][1]));
     642                 :            :         }
     643                 :            :       }
     644                 :            :     }
     645         [ +  + ]:     245537 :     else if (aak == Kind::STRING_REPLACE)
     646                 :            :     {
     647                 :            :       // over,under-approximations for len( replace( x, y, z ) )
     648                 :            :       // notice this is either len( x ) or ( len( x ) + len( z ) - len( y ) )
     649                 :      45507 :       Node lenx = nm->mkNode(Kind::STRING_LENGTH, a[0][0]);
     650                 :      45507 :       Node leny = nm->mkNode(Kind::STRING_LENGTH, a[0][1]);
     651                 :      45507 :       Node lenz = nm->mkNode(Kind::STRING_LENGTH, a[0][2]);
     652         [ +  + ]:      15169 :       if (isOverApprox)
     653                 :            :       {
     654         [ +  + ]:       8363 :         if (check(leny, lenz, false, isSimple))
     655                 :            :         {
     656                 :            :           // len( y ) >= len( z ) implies
     657                 :            :           //   len( x ) >= len( replace( x, y, z ) )
     658                 :       1480 :           approx.push_back(lenx);
     659                 :            :         }
     660                 :            :         else
     661                 :            :         {
     662                 :            :           // len( x ) + len( z ) >= len( replace( x, y, z ) )
     663                 :       6883 :           approx.push_back(nm->mkNode(Kind::ADD, lenx, lenz));
     664                 :            :         }
     665                 :            :       }
     666                 :            :       else
     667                 :            :       {
     668 [ +  - ][ +  + ]:       6806 :         if (check(lenz, leny, false, isSimple)
         [ -  - ][ -  - ]
     669                 :       6806 :             || check(lenz, lenx, false, isSimple))
     670                 :            :         {
     671                 :            :           // len( y ) <= len( z ) or len( x ) <= len( z ) implies
     672                 :            :           //   len( x ) <= len( replace( x, y, z ) )
     673                 :       2851 :           approx.push_back(lenx);
     674                 :            :         }
     675                 :            :         else
     676                 :            :         {
     677                 :            :           // len( x ) - len( y ) <= len( replace( x, y, z ) )
     678                 :       3955 :           approx.push_back(nm->mkNode(Kind::SUB, lenx, leny));
     679                 :            :         }
     680                 :            :       }
     681                 :            :     }
     682         [ +  + ]:     230368 :     else if (aak == Kind::STRING_ITOS)
     683                 :            :     {
     684                 :            :       // over,under-approximations for len( int.to.str( x ) )
     685         [ +  + ]:       5102 :       if (isOverApprox)
     686                 :            :       {
     687         [ +  + ]:       2806 :         if (check(a[0][0], false, isSimple))
     688                 :            :         {
     689         [ +  + ]:       1433 :           if (check(a[0][0], true, isSimple))
     690                 :            :           {
     691                 :            :             // x > 0 implies
     692                 :            :             //   x >= len( int.to.str( x ) )
     693                 :        425 :             approx.push_back(a[0][0]);
     694                 :            :           }
     695                 :            :           else
     696                 :            :           {
     697                 :            :             // x >= 0 implies
     698                 :            :             //   x+1 >= len( int.to.str( x ) )
     699                 :       1008 :             approx.push_back(
     700                 :       2016 :                 nm->mkNode(Kind::ADD, nm->mkConstInt(Rational(1)), a[0][0]));
     701                 :            :           }
     702                 :            :         }
     703                 :            :       }
     704                 :            :       else
     705                 :            :       {
     706         [ +  + ]:       2296 :         if (check(a[0][0], false, isSimple))
     707                 :            :         {
     708                 :            :           // x >= 0 implies
     709                 :            :           //   len( int.to.str( x ) ) >= 1
     710                 :       1430 :           approx.push_back(nm->mkConstInt(Rational(1)));
     711                 :            :         }
     712                 :            :         // other crazy things are possible here, e.g.
     713                 :            :         // len( int.to.str( len( y ) + 10 ) ) >= 2
     714                 :            :       }
     715                 :            :     }
     716                 :            :   }
     717         [ +  + ]:     231101 :   else if (ak == Kind::STRING_INDEXOF)
     718                 :            :   {
     719                 :            :     // over,under-approximations for indexof( x, y, n )
     720         [ +  + ]:      11569 :     if (isOverApprox)
     721                 :            :     {
     722                 :      17715 :       Node lenx = nm->mkNode(Kind::STRING_LENGTH, a[0]);
     723                 :      17715 :       Node leny = nm->mkNode(Kind::STRING_LENGTH, a[1]);
     724         [ +  + ]:       5905 :       if (check(lenx, leny, false, isSimple))
     725                 :            :       {
     726                 :            :         // len( x ) >= len( y ) implies
     727                 :            :         //   len( x ) - len( y ) >= indexof( x, y, n )
     728                 :        263 :         approx.push_back(nm->mkNode(Kind::SUB, lenx, leny));
     729                 :            :       }
     730                 :            :       else
     731                 :            :       {
     732                 :            :         // len( x ) >= indexof( x, y, n )
     733                 :       5642 :         approx.push_back(lenx);
     734                 :            :       }
     735                 :            :     }
     736                 :            :     else
     737                 :            :     {
     738                 :            :       // TODO?:
     739                 :            :       // contains( substr( x, n, len( x ) ), y ) implies
     740                 :            :       //   n <= indexof( x, y, n )
     741                 :            :       // ...hard to test, runs risk of non-termination
     742                 :            : 
     743                 :            :       // -1 <= indexof( x, y, n )
     744                 :       5664 :       approx.push_back(nm->mkConstInt(Rational(-1)));
     745                 :            :     }
     746                 :            :   }
     747         [ +  + ]:     219532 :   else if (ak == Kind::STRING_STOI)
     748                 :            :   {
     749                 :            :     // over,under-approximations for str.to.int( x )
     750         [ +  + ]:         62 :     if (isOverApprox)
     751                 :            :     {
     752                 :            :       // TODO?:
     753                 :            :       // y >= 0 implies
     754                 :            :       //   y >= str.to.int( int.to.str( y ) )
     755                 :            :     }
     756                 :            :     else
     757                 :            :     {
     758                 :            :       // -1 <= str.to.int( x )
     759                 :         32 :       approx.push_back(nm->mkConstInt(Rational(-1)));
     760                 :            :     }
     761                 :            :   }
     762         [ +  - ]:    1067490 :   Trace("strings-ent-approx-debug")
     763                 :     533747 :       << "Return " << approx.size() << " approximations" << std::endl;
     764                 :     534510 : }
     765                 :            : 
     766                 :       4161 : bool ArithEntail::checkWithEqAssumption(Node assumption, Node a, bool strict)
     767                 :            : {
     768 [ -  + ][ -  + ]:       4161 :   Assert(assumption.getKind() == Kind::EQUAL);
                 [ -  - ]
     769         [ +  - ]:       8322 :   Trace("strings-entail") << "checkWithEqAssumption: " << assumption << " " << a
     770                 :       4161 :                           << ", strict=" << strict << std::endl;
     771                 :            : 
     772                 :            :   // Find candidates variables to compute substitutions for
     773                 :       8322 :   std::unordered_set<Node> candVars;
     774                 :      20805 :   std::vector<Node> toVisit = {assumption};
     775         [ +  + ]:      28791 :   while (!toVisit.empty())
     776                 :            :   {
     777                 :      49260 :     Node curr = toVisit.back();
     778                 :      24630 :     toVisit.pop_back();
     779                 :            : 
     780         [ +  + ]:      44807 :     if (curr.getKind() == Kind::ADD || curr.getKind() == Kind::MULT
     781 [ +  + ][ +  + ]:      44807 :         || curr.getKind() == Kind::SUB || curr.getKind() == Kind::EQUAL)
         [ +  + ][ +  + ]
     782                 :            :     {
     783         [ +  + ]:      30702 :       for (const auto& currChild : curr)
     784                 :            :       {
     785                 :      20469 :         toVisit.push_back(currChild);
     786                 :            :       }
     787                 :            :     }
     788 [ +  + ][ +  - ]:      14397 :     else if (curr.isVar() && Theory::theoryOf(curr) == THEORY_ARITH)
         [ +  + ][ +  + ]
                 [ -  - ]
     789                 :            :     {
     790                 :       3235 :       candVars.insert(curr);
     791                 :            :     }
     792         [ +  + ]:      11162 :     else if (curr.getKind() == Kind::STRING_LENGTH)
     793                 :            :     {
     794                 :       5080 :       candVars.insert(curr);
     795                 :            :     }
     796                 :            :   }
     797                 :            : 
     798                 :            :   // Check if any of the candidate variables are in n
     799                 :       8322 :   Node v;
     800 [ -  + ][ -  + ]:       4161 :   Assert(toVisit.empty());
                 [ -  - ]
     801                 :       4161 :   toVisit.push_back(a);
     802         [ +  + ]:      18040 :   while (!toVisit.empty())
     803                 :            :   {
     804                 :      14111 :     Node curr = toVisit.back();
     805                 :      14111 :     toVisit.pop_back();
     806                 :            : 
     807         [ +  + ]:      24645 :     for (const auto& currChild : curr)
     808                 :            :     {
     809                 :      10534 :       toVisit.push_back(currChild);
     810                 :            :     }
     811                 :            : 
     812         [ +  + ]:      14111 :     if (candVars.find(curr) != candVars.end())
     813                 :            :     {
     814                 :        232 :       v = curr;
     815                 :        232 :       break;
     816                 :            :     }
     817                 :            :   }
     818                 :            : 
     819         [ +  + ]:       4161 :   if (v.isNull())
     820                 :            :   {
     821                 :            :     // No suitable candidate found
     822                 :       3929 :     return false;
     823                 :            :   }
     824                 :            : 
     825                 :        696 :   Node solution = ArithMSum::solveEqualityFor(assumption, v);
     826         [ +  + ]:        232 :   if (solution.isNull())
     827                 :            :   {
     828                 :            :     // Could not solve for v
     829                 :         35 :     return false;
     830                 :            :   }
     831         [ +  - ]:        394 :   Trace("strings-entail") << "checkWithEqAssumption: subs " << v << " -> "
     832                 :        197 :                           << solution << std::endl;
     833                 :            : 
     834                 :        394 :   TNode tv = v;
     835                 :        197 :   TNode tsolution = solution;
     836                 :        197 :   a = a.substitute(tv, tsolution);
     837                 :        197 :   return check(a, strict);
     838                 :            : }
     839                 :            : 
     840                 :       7605 : bool ArithEntail::checkWithAssumption(Node assumption,
     841                 :            :                                       Node a,
     842                 :            :                                       Node b,
     843                 :            :                                       bool strict)
     844                 :            : {
     845                 :       7605 :   NodeManager* nm = NodeManager::currentNM();
     846                 :            : 
     847 [ +  + ][ +  + ]:       7605 :   if (!assumption.isConst() && assumption.getKind() != Kind::EQUAL)
                 [ +  + ]
     848                 :            :   {
     849                 :            :     // We rewrite inequality assumptions from x <= y to x + (str.len s) = y
     850                 :            :     // where s is some fresh string variable. We use (str.len s) because
     851                 :            :     // (str.len s) must be non-negative for the equation to hold.
     852                 :       8314 :     Node x, y;
     853         [ +  + ]:       4157 :     if (assumption.getKind() == Kind::GEQ)
     854                 :            :     {
     855                 :       2858 :       x = assumption[0];
     856                 :       2858 :       y = assumption[1];
     857                 :            :     }
     858                 :            :     else
     859                 :            :     {
     860                 :            :       // (not (>= s t)) --> (>= (t - 1) s)
     861                 :       2598 :       Assert(assumption.getKind() == Kind::NOT
     862                 :            :              && assumption[0].getKind() == Kind::GEQ);
     863                 :       1299 :       x = nm->mkNode(Kind::SUB, assumption[0][1], nm->mkConstInt(Rational(1)));
     864                 :       1299 :       y = assumption[0][0];
     865                 :            :     }
     866                 :            : 
     867                 :      12471 :     Node s = nm->mkBoundVar("slackVal", nm->stringType());
     868                 :       8314 :     Node slen = nm->mkNode(Kind::STRING_LENGTH, s);
     869                 :      12471 :     Node sleny = nm->mkNode(Kind::ADD, y, slen);
     870                 :      12471 :     Node rr = rewriteArith(nm->mkNode(Kind::SUB, x, sleny));
     871         [ -  + ]:       4157 :     if (rr.isConst())
     872                 :            :     {
     873                 :          0 :       assumption = nm->mkConst(rr.getConst<Rational>().sgn() == 0);
     874                 :            :     }
     875                 :            :     else
     876                 :            :     {
     877                 :       4157 :       assumption = nm->mkNode(Kind::EQUAL, x, sleny);
     878                 :            :     }
     879                 :            :   }
     880                 :            : 
     881                 :      15210 :   Node diff = nm->mkNode(Kind::SUB, a, b);
     882                 :       7605 :   bool res = false;
     883         [ +  + ]:       7605 :   if (assumption.isConst())
     884                 :            :   {
     885                 :       3444 :     bool assumptionBool = assumption.getConst<bool>();
     886         [ +  - ]:       3444 :     if (assumptionBool)
     887                 :            :     {
     888                 :       3444 :       res = check(diff, strict);
     889                 :            :     }
     890                 :            :     else
     891                 :            :     {
     892                 :          0 :       res = true;
     893                 :            :     }
     894                 :            :   }
     895                 :            :   else
     896                 :            :   {
     897                 :       4161 :     res = checkWithEqAssumption(assumption, diff, strict);
     898                 :            :   }
     899                 :      15210 :   return res;
     900                 :            : }
     901                 :            : 
     902                 :          0 : bool ArithEntail::checkWithAssumptions(std::vector<Node> assumptions,
     903                 :            :                                        Node a,
     904                 :            :                                        Node b,
     905                 :            :                                        bool strict)
     906                 :            : {
     907                 :            :   // TODO: We currently try to show the entailment with each assumption
     908                 :            :   // independently. In the future, we should make better use of multiple
     909                 :            :   // assumptions.
     910                 :          0 :   bool res = false;
     911         [ -  - ]:          0 :   for (const auto& assumption : assumptions)
     912                 :            :   {
     913         [ -  - ]:          0 :     if (checkWithAssumption(assumption, a, b, strict))
     914                 :            :     {
     915                 :          0 :       res = true;
     916                 :          0 :       break;
     917                 :            :     }
     918                 :            :   }
     919                 :          0 :   return res;
     920                 :            : }
     921                 :            : 
     922                 :            : struct ArithEntailConstantBoundLowerId
     923                 :            : {
     924                 :            : };
     925                 :            : typedef expr::Attribute<ArithEntailConstantBoundLowerId, Node>
     926                 :            :     ArithEntailConstantBoundLower;
     927                 :            : 
     928                 :            : struct ArithEntailConstantBoundUpperId
     929                 :            : {
     930                 :            : };
     931                 :            : typedef expr::Attribute<ArithEntailConstantBoundUpperId, Node>
     932                 :            :     ArithEntailConstantBoundUpper;
     933                 :            : 
     934                 :      74523 : void ArithEntail::setConstantBoundCache(TNode n, Node ret, bool isLower)
     935                 :            : {
     936         [ +  + ]:      74523 :   if (isLower)
     937                 :            :   {
     938                 :            :     ArithEntailConstantBoundLower acbl;
     939                 :      40656 :     n.setAttribute(acbl, ret);
     940                 :            :   }
     941                 :            :   else
     942                 :            :   {
     943                 :            :     ArithEntailConstantBoundUpper acbu;
     944                 :      33867 :     n.setAttribute(acbu, ret);
     945                 :            :   }
     946                 :      74523 : }
     947                 :            : 
     948                 :     915981 : bool ArithEntail::getConstantBoundCache(TNode n, bool isLower, Node& c)
     949                 :            : {
     950         [ +  + ]:     915981 :   if (isLower)
     951                 :            :   {
     952                 :            :     ArithEntailConstantBoundLower acbl;
     953         [ +  + ]:     830417 :     if (n.hasAttribute(acbl))
     954                 :            :     {
     955                 :     789761 :       c = n.getAttribute(acbl);
     956                 :     789761 :       return true;
     957                 :            :     }
     958                 :            :   }
     959                 :            :   else
     960                 :            :   {
     961                 :            :     ArithEntailConstantBoundUpper acbu;
     962         [ +  + ]:      85564 :     if (n.hasAttribute(acbu))
     963                 :            :     {
     964                 :      51697 :       c = n.getAttribute(acbu);
     965                 :      51697 :       return true;
     966                 :            :     }
     967                 :            :   }
     968                 :      74523 :   return false;
     969                 :            : }
     970                 :            : 
     971                 :      36309 : Node ArithEntail::getConstantBound(TNode a, bool isLower)
     972                 :            : {
     973 [ -  + ][ -  + ]:      36309 :   Assert(rewriteArith(a) == a);
                 [ -  - ]
     974                 :      36309 :   Node ret;
     975         [ +  + ]:      36309 :   if (getConstantBoundCache(a, isLower, ret))
     976                 :            :   {
     977                 :      29520 :     return ret;
     978                 :            :   }
     979         [ +  + ]:       6789 :   if (a.isConst())
     980                 :            :   {
     981                 :       1788 :     ret = a;
     982                 :            :   }
     983         [ +  + ]:       5001 :   else if (a.getKind() == Kind::STRING_LENGTH)
     984                 :            :   {
     985         [ +  - ]:        774 :     if (isLower)
     986                 :            :     {
     987                 :        774 :       ret = d_zero;
     988                 :            :     }
     989                 :            :   }
     990 [ +  + ][ +  + ]:       4227 :   else if (a.getKind() == Kind::ADD || a.getKind() == Kind::MULT)
                 [ +  + ]
     991                 :            :   {
     992                 :       6840 :     std::vector<Node> children;
     993                 :       3420 :     bool success = true;
     994         [ +  + ]:       6686 :     for (unsigned i = 0; i < a.getNumChildren(); i++)
     995                 :            :     {
     996                 :       5967 :       Node ac = getConstantBound(a[i], isLower);
     997         [ +  + ]:       5967 :       if (ac.isNull())
     998                 :            :       {
     999                 :       1740 :         ret = ac;
    1000                 :       1740 :         success = false;
    1001                 :       1740 :         break;
    1002                 :            :       }
    1003                 :            :       else
    1004                 :            :       {
    1005         [ +  + ]:       4227 :         if (ac.getConst<Rational>().sgn() == 0)
    1006                 :            :         {
    1007         [ +  + ]:       1139 :           if (a.getKind() == Kind::MULT)
    1008                 :            :           {
    1009                 :         16 :             ret = ac;
    1010                 :         16 :             success = false;
    1011                 :         16 :             break;
    1012                 :            :           }
    1013                 :            :         }
    1014                 :            :         else
    1015                 :            :         {
    1016         [ +  + ]:       3088 :           if (a.getKind() == Kind::MULT)
    1017                 :            :           {
    1018         [ +  + ]:        963 :             if ((ac.getConst<Rational>().sgn() > 0) != isLower)
    1019                 :            :             {
    1020                 :        945 :               ret = Node::null();
    1021                 :        945 :               success = false;
    1022                 :        945 :               break;
    1023                 :            :             }
    1024                 :            :           }
    1025                 :       2143 :           children.push_back(ac);
    1026                 :            :         }
    1027                 :            :       }
    1028                 :            :     }
    1029         [ +  + ]:       3420 :     if (success)
    1030                 :            :     {
    1031         [ +  + ]:        719 :       if (children.empty())
    1032                 :            :       {
    1033                 :        208 :         ret = d_zero;
    1034                 :            :       }
    1035         [ +  - ]:        511 :       else if (children.size() == 1)
    1036                 :            :       {
    1037                 :        511 :         ret = children[0];
    1038                 :            :       }
    1039                 :            :       else
    1040                 :            :       {
    1041                 :          0 :         ret = NodeManager::currentNM()->mkNode(a.getKind(), children);
    1042                 :          0 :         ret = rewriteArith(ret);
    1043                 :            :       }
    1044                 :            :     }
    1045                 :            :   }
    1046         [ +  - ]:      13578 :   Trace("strings-rewrite-cbound")
    1047         [ -  - ]:          0 :       << "Constant " << (isLower ? "lower" : "upper") << " bound for " << a
    1048                 :       6789 :       << " is " << ret << std::endl;
    1049 [ +  + ][ -  + ]:       6789 :   Assert(ret.isNull() || ret.isConst());
         [ -  + ][ -  - ]
    1050                 :            :   // entailment check should be at least as powerful as computing a lower bound
    1051                 :       6789 :   Assert(!isLower || ret.isNull() || ret.getConst<Rational>().sgn() < 0
    1052                 :            :          || check(a, false));
    1053                 :       6789 :   Assert(!isLower || ret.isNull() || ret.getConst<Rational>().sgn() <= 0
    1054                 :            :          || check(a, true));
    1055                 :            :   // cache
    1056                 :       6789 :   setConstantBoundCache(a, ret, isLower);
    1057                 :       6789 :   return ret;
    1058                 :            : }
    1059                 :            : 
    1060                 :     879672 : Node ArithEntail::getConstantBoundLength(TNode s, bool isLower) const
    1061                 :            : {
    1062 [ -  + ][ -  + ]:     879672 :   Assert(s.getType().isStringLike());
                 [ -  - ]
    1063                 :     879672 :   Node ret;
    1064         [ +  + ]:     879672 :   if (getConstantBoundCache(s, isLower, ret))
    1065                 :            :   {
    1066                 :     811938 :     return ret;
    1067                 :            :   }
    1068                 :      67734 :   NodeManager* nm = NodeManager::currentNM();
    1069                 :      67734 :   Kind sk = s.getKind();
    1070         [ +  + ]:      67734 :   if (s.isConst())
    1071                 :            :   {
    1072                 :      12138 :     size_t len = Word::getLength(s);
    1073                 :      12138 :     ret = nm->mkConstInt(Rational(len));
    1074                 :            :   }
    1075 [ +  + ][ +  + ]:      55596 :   else if (sk == Kind::SEQ_UNIT || sk == Kind::STRING_UNIT)
    1076                 :            :   {
    1077                 :       1290 :     ret = nm->mkConstInt(1);
    1078                 :            :   }
    1079         [ +  + ]:      54306 :   else if (sk == Kind::STRING_CONCAT)
    1080                 :            :   {
    1081                 :      39912 :     Rational sum(0);
    1082                 :      19956 :     bool success = true;
    1083         [ +  + ]:      45493 :     for (const Node& sc : s)
    1084                 :            :     {
    1085                 :      35511 :       Node b = getConstantBoundLength(sc, isLower);
    1086         [ +  + ]:      35511 :       if (b.isNull())
    1087                 :            :       {
    1088         [ -  + ]:       9974 :         if (isLower)
    1089                 :            :         {
    1090                 :            :           // assume zero and continue
    1091                 :          0 :           continue;
    1092                 :            :         }
    1093                 :       9974 :         success = false;
    1094                 :       9974 :         break;
    1095                 :            :       }
    1096 [ -  + ][ -  + ]:      25537 :       Assert(b.isConst());
                 [ -  - ]
    1097                 :      25537 :       sum = sum + b.getConst<Rational>();
    1098                 :            :     }
    1099 [ +  + ][ +  + ]:      19956 :     if (success && (!isLower || sum.sgn() != 0))
         [ +  + ][ +  + ]
    1100                 :            :     {
    1101                 :       4483 :       ret = nm->mkConstInt(sum);
    1102                 :            :     }
    1103                 :            :   }
    1104 [ +  + ][ +  + ]:      67734 :   if (ret.isNull() && isLower)
                 [ +  + ]
    1105                 :            :   {
    1106                 :      22674 :     ret = d_zero;
    1107                 :            :   }
    1108                 :            :   // cache
    1109                 :      67734 :   setConstantBoundCache(s, ret, isLower);
    1110                 :      67734 :   return ret;
    1111                 :            : }
    1112                 :            : 
    1113                 :     927823 : bool ArithEntail::checkSimple(Node a)
    1114                 :            : {
    1115                 :            :   // check whether a >= 0
    1116         [ +  + ]:     927823 :   if (a.isConst())
    1117                 :            :   {
    1118                 :     389173 :     return a.getConst<Rational>().sgn() >= 0;
    1119                 :            :   }
    1120         [ +  + ]:     538650 :   else if (a.getKind() == Kind::STRING_LENGTH)
    1121                 :            :   {
    1122                 :            :     // str.len( t ) >= 0
    1123                 :      68526 :     return true;
    1124                 :            :   }
    1125 [ +  + ][ +  + ]:     470124 :   else if (a.getKind() == Kind::ADD || a.getKind() == Kind::MULT)
                 [ +  + ]
    1126                 :            :   {
    1127         [ +  + ]:     561198 :     for (unsigned i = 0; i < a.getNumChildren(); i++)
    1128                 :            :     {
    1129         [ +  + ]:     551461 :       if (!checkSimple(a[i]))
    1130                 :            :       {
    1131                 :     429516 :         return false;
    1132                 :            :       }
    1133                 :            :     }
    1134                 :            :     // t1 >= 0 ^ ... ^ tn >= 0 => t1 op ... op tn >= 0
    1135                 :       9737 :     return true;
    1136                 :            :   }
    1137                 :            : 
    1138                 :      30871 :   return false;
    1139                 :            : }
    1140                 :            : 
    1141                 :       2463 : bool ArithEntail::inferZerosInSumGeq(Node x,
    1142                 :            :                                      std::vector<Node>& ys,
    1143                 :            :                                      std::vector<Node>& zeroYs)
    1144                 :            : {
    1145 [ -  + ][ -  + ]:       2463 :   Assert(zeroYs.empty());
                 [ -  - ]
    1146                 :            : 
    1147                 :       2463 :   NodeManager* nm = NodeManager::currentNM();
    1148                 :            : 
    1149                 :            :   // Check if we can show that y1 + ... + yn >= x
    1150         [ +  + ]:       4926 :   Node sum = (ys.size() > 1) ? nm->mkNode(Kind::ADD, ys) : ys[0];
    1151         [ +  + ]:       2463 :   if (!check(sum, x))
    1152                 :            :   {
    1153                 :       2198 :     return false;
    1154                 :            :   }
    1155                 :            : 
    1156                 :            :   // Try to remove yi one-by-one and check if we can still show:
    1157                 :            :   //
    1158                 :            :   // y1 + ... + yi-1 +  yi+1 + ... + yn >= x
    1159                 :            :   //
    1160                 :            :   // If that's the case, we know that yi can be zero and the inequality still
    1161                 :            :   // holds.
    1162                 :        265 :   size_t i = 0;
    1163         [ +  + ]:        782 :   while (i < ys.size())
    1164                 :            :   {
    1165                 :       1034 :     Node yi = ys[i];
    1166                 :        517 :     std::vector<Node>::iterator pos = ys.erase(ys.begin() + i);
    1167         [ +  + ]:        517 :     if (ys.size() > 1)
    1168                 :            :     {
    1169                 :        130 :       sum = nm->mkNode(Kind::ADD, ys);
    1170                 :            :     }
    1171                 :            :     else
    1172                 :            :     {
    1173         [ +  + ]:        387 :       sum = ys.size() == 1 ? ys[0] : d_zero;
    1174                 :            :     }
    1175                 :            : 
    1176         [ +  + ]:        517 :     if (check(sum, x))
    1177                 :            :     {
    1178                 :         95 :       zeroYs.push_back(yi);
    1179                 :            :     }
    1180                 :            :     else
    1181                 :            :     {
    1182                 :        422 :       ys.insert(pos, yi);
    1183                 :        422 :       i++;
    1184                 :            :     }
    1185                 :            :   }
    1186                 :        265 :   return true;
    1187                 :            : }
    1188                 :            : 
    1189                 :            : }  // namespace strings
    1190                 :            : }  // namespace theory
    1191                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14