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: 561 606 92.6 %
Date: 2026-03-03 11:42:59 Functions: 20 21 95.2 %
Branches: 400 553 72.3 %

           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                 :     380167 : ArithEntail::ArithEntail(NodeManager* nm, Rewriter* r, bool recApprox)
      35                 :     380167 :     : d_rr(r), d_recApprox(recApprox)
      36                 :            : {
      37                 :     380167 :   d_one = nm->mkConstInt(Rational(1));
      38                 :     380167 :   d_zero = nm->mkConstInt(Rational(0));
      39                 :     380167 : }
      40                 :            : 
      41                 :     106541 : Node ArithEntail::rewritePredViaEntailment(const Node& n, bool isSimple)
      42                 :            : {
      43                 :     106541 :   Node exp;
      44                 :     213082 :   return rewritePredViaEntailment(n, exp, isSimple);
      45                 :     106541 : }
      46                 :            : 
      47                 :     107273 : Node ArithEntail::rewritePredViaEntailment(const Node& n,
      48                 :            :                                            Node& exp,
      49                 :            :                                            bool isSimple)
      50                 :            : {
      51                 :     107273 :   NodeManager* nm = n.getNodeManager();
      52                 :     107273 :   if (n.getKind() == Kind::EQUAL && n[0].getType().isInteger())
      53                 :            :   {
      54                 :      28329 :     exp = nm->mkNode(Kind::SUB, nm->mkNode(Kind::SUB, n[0], n[1]), d_one);
      55         [ +  + ]:      28329 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      56                 :            :     {
      57                 :       4458 :       return nm->mkConst(false);
      58                 :            :     }
      59                 :      26100 :     exp = nm->mkNode(Kind::SUB, nm->mkNode(Kind::SUB, n[1], n[0]), d_one);
      60         [ +  + ]:      26100 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      61                 :            :     {
      62                 :       2820 :       return nm->mkConst(false);
      63                 :            :     }
      64                 :      24690 :     exp = Node::null();
      65         [ +  + ]:      24690 :     if (checkEq(n[0], n[1]))
      66                 :            :     {
      67                 :            :       // explanation is null
      68                 :        644 :       return nm->mkConst(true);
      69                 :            :     }
      70                 :            :   }
      71         [ +  - ]:      78944 :   else if (n.getKind() == Kind::GEQ)
      72                 :            :   {
      73                 :      78944 :     exp = nm->mkNode(Kind::SUB, n[0], n[1]);
      74         [ +  + ]:      78944 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      75                 :            :     {
      76                 :       4298 :       return nm->mkConst(true);
      77                 :            :     }
      78                 :      76795 :     exp = nm->mkNode(Kind::SUB, nm->mkNode(Kind::SUB, n[1], n[0]), d_one);
      79         [ +  + ]:      76795 :     if (!findApprox(rewriteArith(exp), isSimple).isNull())
      80                 :            :     {
      81                 :      12838 :       return nm->mkConst(false);
      82                 :            :     }
      83                 :      70376 :     exp = Node::null();
      84                 :            :   }
      85                 :      94744 :   return Node::null();
      86                 :            : }
      87                 :            : 
      88                 :    1727953 : Node ArithEntail::rewriteArith(Node a)
      89                 :            : {
      90 [ -  + ][ -  - ]:    3455906 :   AlwaysAssert(a.getType().isInteger())
      91                 :    1727953 :       << "Bad term: " << a << " " << a.getType();
      92         [ +  + ]:    1727953 :   if (d_rr != nullptr)
      93                 :            :   {
      94                 :     243186 :     return d_rr->rewrite(a);
      95                 :            :   }
      96                 :            :   else
      97                 :            :   {
      98                 :    1484767 :     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                 :    1484767 :   Node an = arith::PolyNorm::getPolyNorm(a);
     104                 :    1484767 :   return an;
     105                 :    1484767 : }
     106                 :            : 
     107                 :      62049 : Node ArithEntail::normalizeGeq(const Node& n) const
     108                 :            : {
     109                 :      62049 :   NodeManager* nm = n.getNodeManager();
     110                 :     124098 :   if (n.getNumChildren() != 2 || !n[0].getType().isInteger()
     111                 :     124098 :       || !n[1].getType().isInteger())
     112                 :            :   {
     113                 :         62 :     return Node::null();
     114                 :            :   }
     115 [ +  + ][ +  + ]:      61987 :   switch (n.getKind())
                    [ - ]
     116                 :            :   {
     117                 :      39841 :     case Kind::GEQ: return n;
     118                 :       7248 :     case Kind::LEQ: return nm->mkNode(Kind::GEQ, n[1], n[0]);
     119                 :       6256 :     case Kind::LT:
     120                 :            :       return nm->mkNode(
     121                 :            :           Kind::GEQ,
     122                 :            :           n[1],
     123                 :       6256 :           nm->mkNode(Kind::ADD, n[0], nm->mkConstInt(Rational(1))));
     124                 :       8642 :     case Kind::GT:
     125                 :            :       return nm->mkNode(
     126                 :            :           Kind::GEQ,
     127                 :            :           n[0],
     128                 :       8642 :           nm->mkNode(Kind::ADD, n[1], nm->mkConstInt(Rational(1))));
     129                 :          0 :     default: break;
     130                 :            :   }
     131                 :          0 :   return Node::null();
     132                 :            : }
     133                 :            : 
     134                 :    1561856 : Node ArithEntail::rewriteLengthIntro(const Node& n,
     135                 :            :                                      TConvProofGenerator* pg) const
     136                 :            : {
     137                 :    1561856 :   NodeManager* nm = n.getNodeManager();
     138                 :    1561856 :   std::unordered_map<TNode, Node> visited;
     139                 :    1561856 :   std::unordered_map<TNode, Node>::iterator it;
     140                 :    1561856 :   std::vector<TNode> visit;
     141                 :    1561856 :   TNode cur;
     142                 :    1561856 :   visit.push_back(n);
     143                 :            :   do
     144                 :            :   {
     145                 :   27097163 :     cur = visit.back();
     146                 :   27097163 :     it = visited.find(cur);
     147         [ +  + ]:   27097163 :     if (it == visited.end())
     148                 :            :     {
     149         [ +  + ]:   14704901 :       if (cur.getNumChildren() == 0)
     150                 :            :       {
     151                 :    5995445 :         visit.pop_back();
     152                 :    5995445 :         visited[cur] = cur;
     153                 :    5995445 :         continue;
     154                 :            :       }
     155                 :    8709456 :       visited.emplace(cur, Node::null());
     156                 :    8709456 :       visit.insert(visit.end(), cur.begin(), cur.end());
     157                 :    8709456 :       continue;
     158                 :            :     }
     159                 :   12392262 :     visit.pop_back();
     160         [ +  + ]:   12392262 :     if (it->second.isNull())
     161                 :            :     {
     162                 :    8709456 :       Kind k = cur.getKind();
     163                 :    8709456 :       bool childChanged = false;
     164                 :    8709456 :       std::vector<Node> children;
     165         [ +  + ]:   25535307 :       for (const Node& cn : cur)
     166                 :            :       {
     167                 :   16825851 :         it = visited.find(cn);
     168 [ -  + ][ -  + ]:   16825851 :         Assert(it != visited.end());
                 [ -  - ]
     169 [ -  + ][ -  + ]:   16825851 :         Assert(!it->second.isNull());
                 [ -  - ]
     170                 :   16825851 :         children.push_back(it->second);
     171 [ +  + ][ +  + ]:   16825851 :         childChanged = childChanged || it->second != cn;
     172                 :   16825851 :       }
     173                 :    8709456 :       Node ret = cur;
     174         [ +  + ]:    8709456 :       if (childChanged)
     175                 :            :       {
     176                 :     581805 :         ret = nm->mkNode(k, children);
     177                 :            :       }
     178                 :    8709456 :       if (k == Kind::STRING_LENGTH
     179                 :    8709456 :           && (ret[0].getKind() == Kind::STRING_CONCAT || ret[0].isConst()))
     180                 :            :       {
     181                 :     434065 :         Node arg = ret[0];
     182                 :            :         // First ensure ACI norm, which ensures that we fully flatten
     183                 :            :         // e.g. (len (str.++ (str.++ a b) c)) ---> (len (str.++ a b c)) --->
     184                 :            :         // (+ (len a) (len b) (len c)) below.
     185         [ +  + ]:     434065 :         if (arg.getKind() == Kind::STRING_CONCAT)
     186                 :            :         {
     187                 :     135430 :           arg = expr::getACINormalForm(arg);
     188         [ +  + ]:     135430 :           if (arg != ret[0])
     189                 :            :           {
     190                 :         78 :             Node ret2 = nm->mkNode(k, {arg});
     191         [ +  + ]:         39 :             if (pg != nullptr)
     192                 :            :             {
     193                 :          6 :               pg->addRewriteStep(ret,
     194                 :            :                                  ret2,
     195                 :            :                                  nullptr,
     196                 :            :                                  false,
     197                 :            :                                  TrustId::MACRO_THEORY_REWRITE_RCONS_SIMPLE);
     198                 :            :             }
     199                 :         39 :             ret = ret2;
     200                 :         39 :           }
     201                 :            :         }
     202                 :     434065 :         std::vector<Node> cc;
     203                 :     434065 :         utils::getConcat(arg, cc);
     204                 :     434065 :         std::vector<Node> sum;
     205         [ +  + ]:    1156103 :         for (const Node& c : cc)
     206                 :            :         {
     207         [ +  + ]:     722038 :           if (c.isConst())
     208                 :            :           {
     209                 :     424449 :             sum.push_back(nm->mkConstInt(Rational(Word::getLength(c))));
     210                 :            :           }
     211                 :            :           else
     212                 :            :           {
     213                 :     297589 :             sum.push_back(nm->mkNode(Kind::STRING_LENGTH, c));
     214                 :            :           }
     215                 :            :         }
     216 [ -  + ][ -  + ]:     434065 :         Assert(!sum.empty());
                 [ -  - ]
     217         [ +  + ]:     434065 :         Node rret = sum.size() == 1 ? sum[0] : nm->mkNode(Kind::ADD, sum);
     218         [ +  + ]:     434065 :         if (pg != nullptr)
     219                 :            :         {
     220                 :        359 :           pg->addRewriteStep(ret,
     221                 :            :                              rret,
     222                 :            :                              nullptr,
     223                 :            :                              false,
     224                 :            :                              TrustId::MACRO_THEORY_REWRITE_RCONS_SIMPLE);
     225                 :            :         }
     226                 :     434065 :         ret = rret;
     227                 :     434065 :       }
     228                 :    8709456 :       visited[cur] = ret;
     229                 :    8709456 :     }
     230         [ +  + ]:   27097163 :   } while (!visit.empty());
     231 [ -  + ][ -  + ]:    1561856 :   Assert(visited.find(n) != visited.end());
                 [ -  - ]
     232 [ -  + ][ -  + ]:    1561856 :   Assert(!visited.find(n)->second.isNull());
                 [ -  - ]
     233                 :    3123712 :   return visited[n];
     234                 :    1561856 : }
     235                 :            : 
     236                 :      34676 : bool ArithEntail::checkEq(Node a, Node b)
     237                 :            : {
     238         [ +  + ]:      34676 :   if (a == b)
     239                 :            :   {
     240                 :        319 :     return true;
     241                 :            :   }
     242                 :      34357 :   Node ar = rewriteArith(a);
     243                 :      34357 :   Node br = rewriteArith(b);
     244                 :      34357 :   return ar == br;
     245                 :      34357 : }
     246                 :            : 
     247                 :     898384 : bool ArithEntail::check(Node a, Node b, bool strict, bool isSimple)
     248                 :            : {
     249         [ +  + ]:     898384 :   if (a == b)
     250                 :            :   {
     251                 :      55566 :     return !strict;
     252                 :            :   }
     253                 :    1685636 :   Node diff = NodeManager::mkNode(Kind::SUB, a, b);
     254                 :     842818 :   return check(diff, strict, isSimple);
     255                 :     842818 : }
     256                 :            : 
     257                 :    1372073 : bool ArithEntail::check(Node a, bool strict, bool isSimple)
     258                 :            : {
     259         [ +  + ]:    1372073 :   if (a.isConst())
     260                 :            :   {
     261         [ +  + ]:     149687 :     return a.getConst<Rational>().sgn() >= (strict ? 1 : 0);
     262                 :            :   }
     263                 :    1669412 :   Node ar = strict ? NodeManager::mkNode(Kind::SUB, a, d_one) : a;
     264         [ +  + ]:    1222386 :   if (isSimple)
     265                 :            :   {
     266                 :     206653 :     ar = arith::PolyNorm::getPolyNorm(ar);
     267                 :            :     // if simple, just call the checkSimple routine.
     268                 :     206653 :     return checkSimple(ar);
     269                 :            :   }
     270                 :            :   else
     271                 :            :   {
     272                 :            :     // otherwise rewrite arith and find approximation
     273                 :    1015733 :     ar = rewriteArith(ar);
     274                 :            :   }
     275                 :    1015733 :   Node ara = findApprox(ar, isSimple);
     276                 :    1015733 :   return !ara.isNull();
     277                 :    1222386 : }
     278                 :            : 
     279                 :    1226553 : Node ArithEntail::findApprox(Node ar, bool isSimple)
     280                 :            : {
     281         [ +  + ]:    1226553 :   std::map<Node, Node>& cache = isSimple ? d_approxCacheSimple : d_approxCache;
     282                 :    1226553 :   std::map<Node, Node>::iterator it = cache.find(ar);
     283         [ +  + ]:    1226553 :   if (it != cache.end())
     284                 :            :   {
     285                 :     824405 :     return it->second;
     286                 :            :   }
     287                 :     402148 :   Node ret;
     288         [ +  + ]:     402148 :   if (checkSimple(ar))
     289                 :            :   {
     290                 :            :     // didn't need approximation
     291                 :      25094 :     ret = ar;
     292                 :            :   }
     293                 :            :   else
     294                 :            :   {
     295                 :     377054 :     ret = findApproxInternal(ar, isSimple);
     296                 :            :   }
     297                 :     402148 :   cache[ar] = ret;
     298                 :     402148 :   return ret;
     299                 :     402148 : }
     300                 :            : 
     301                 :     377054 : Node ArithEntail::findApproxInternal(Node ar, bool isSimple)
     302                 :            : {
     303                 :            :   // if not using recursive approximations, we always set isSimple to true
     304         [ +  + ]:     377054 :   if (!d_recApprox)
     305                 :            :   {
     306                 :     324405 :     isSimple = true;
     307                 :            :   }
     308                 :     377054 :   NodeManager* nm = ar.getNodeManager();
     309                 :     377054 :   std::map<Node, Node> msum;
     310         [ +  - ]:     754108 :   Trace("strings-ent-approx-debug")
     311                 :     377054 :       << "Setup arithmetic approximations for " << ar << std::endl;
     312         [ -  + ]:     377054 :   if (!ArithMSum::getMonomialSum(ar, msum))
     313                 :            :   {
     314         [ -  - ]:          0 :     Trace("strings-ent-approx-debug")
     315                 :          0 :         << "...failed to get monomial sum!" << std::endl;
     316                 :          0 :     return Node::null();
     317                 :            :   }
     318                 :            :   // for each monomial v*c, mApprox[v] a list of
     319                 :            :   // possibilities for how the term can be soundly approximated, that is,
     320                 :            :   // if mApprox[v] contains av, then v*c > av*c. Notice that if c
     321                 :            :   // is positive, then v > av, otherwise if c is negative, then v < av.
     322                 :            :   // In other words, av is an under-approximation if c is positive, and an
     323                 :            :   // over-approximation if c is negative.
     324                 :     377054 :   bool changed = false;
     325                 :     377054 :   std::map<Node, std::vector<Node> > mApprox;
     326                 :            :   // map from approximations to their monomial sums
     327                 :     377054 :   std::map<Node, std::map<Node, Node> > approxMsums;
     328                 :            :   // aarSum stores each monomial that does not have multiple approximations
     329                 :     377054 :   std::vector<Node> aarSum;
     330                 :            :   // stores the witness
     331                 :     377054 :   arith::ArithSubs approxMap;
     332         [ +  + ]:    1196328 :   for (std::pair<const Node, Node>& m : msum)
     333                 :            :   {
     334                 :     819274 :     Node v = m.first;
     335                 :     819274 :     Node c = m.second;
     336         [ +  - ]:    1638548 :     Trace("strings-ent-approx-debug")
     337                 :     819274 :         << "Get approximations " << v << "..." << std::endl;
     338         [ +  + ]:     819274 :     if (v.isNull())
     339                 :            :     {
     340 [ -  + ][ -  + ]:     270848 :       Node mn = c.isNull() ? nm->mkConstInt(Rational(1)) : c;
                 [ -  - ]
     341                 :     270848 :       aarSum.push_back(mn);
     342                 :     270848 :     }
     343                 :            :     else
     344                 :            :     {
     345                 :            :       // c.isNull() means c = 1
     346 [ +  + ][ +  + ]:     548426 :       bool isOverApprox = !c.isNull() && c.getConst<Rational>().sgn() == -1;
     347                 :     548426 :       std::vector<Node>& approx = mApprox[v];
     348                 :     548426 :       std::unordered_set<Node> visited;
     349                 :     548426 :       std::vector<Node> toProcess;
     350                 :     548426 :       toProcess.push_back(v);
     351                 :            :       do
     352                 :            :       {
     353                 :     569235 :         Node curr = toProcess.back();
     354         [ +  - ]:     569235 :         Trace("strings-ent-approx-debug") << "  process " << curr << std::endl;
     355                 :     569235 :         curr = arith::PolyNorm::getPolyNorm(curr);
     356                 :     569235 :         toProcess.pop_back();
     357         [ +  + ]:     569235 :         if (visited.find(curr) == visited.end())
     358                 :            :         {
     359                 :     568292 :           visited.insert(curr);
     360                 :     568292 :           std::vector<Node> currApprox;
     361                 :     568292 :           getArithApproximations(curr, currApprox, isOverApprox, isSimple);
     362         [ +  + ]:     568292 :           if (currApprox.empty())
     363                 :            :           {
     364         [ +  - ]:     949158 :             Trace("strings-ent-approx-debug")
     365                 :     474579 :                 << "...approximation: " << curr << std::endl;
     366                 :            :             // no approximations, thus curr is a possibility
     367                 :     474579 :             approx.push_back(curr);
     368                 :            :           }
     369         [ +  + ]:      93713 :           else if (isSimple)
     370                 :            :           {
     371                 :            :             // don't rewrite or re-approximate
     372                 :      81655 :             approx = currApprox;
     373                 :            :           }
     374                 :            :           else
     375                 :            :           {
     376                 :      24116 :             toProcess.insert(
     377                 :      24116 :                 toProcess.end(), currApprox.begin(), currApprox.end());
     378                 :            :           }
     379                 :     568292 :         }
     380         [ +  + ]:     569235 :       } while (!toProcess.empty());
     381 [ -  + ][ -  + ]:     548426 :       Assert(!approx.empty());
                 [ -  - ]
     382                 :            :       // if we have only one approximation, move it to final
     383         [ +  + ]:     548426 :       if (approx.size() == 1)
     384                 :            :       {
     385         [ +  + ]:     520230 :         if (v != approx[0])
     386                 :            :         {
     387                 :      66059 :           changed = true;
     388         [ +  - ]:     132118 :           Trace("strings-ent-approx")
     389                 :      66059 :               << "- Propagate (" << (d_rr == nullptr) << ", " << isSimple
     390                 :      66059 :               << ") " << v << " = " << approx[0] << std::endl;
     391                 :      66059 :           approxMap.add(v, approx[0]);
     392                 :            :         }
     393                 :    1040460 :         Node mn = ArithMSum::mkCoeffTerm(c, approx[0]);
     394                 :     520230 :         aarSum.push_back(mn);
     395                 :     520230 :         mApprox.erase(v);
     396                 :     520230 :       }
     397                 :            :       else
     398                 :            :       {
     399                 :            :         // compute monomial sum form for each approximation, used below
     400         [ +  + ]:      84592 :         for (const Node& aa : approx)
     401                 :            :         {
     402         [ +  + ]:      56396 :           if (approxMsums.find(aa) == approxMsums.end())
     403                 :            :           {
     404                 :            :             // ensure rewritten, which makes a difference if isSimple is true
     405                 :      54058 :             Node aar = arith::PolyNorm::getPolyNorm(aa);
     406                 :            :             CVC5_UNUSED bool ret =
     407                 :      54058 :                 ArithMSum::getMonomialSum(aar, approxMsums[aa]);
     408                 :      54058 :             Assert(ret) << "Could not find sum " << aa;
     409                 :      54058 :           }
     410                 :            :         }
     411                 :      28196 :         changed = true;
     412                 :            :       }
     413                 :     548426 :     }
     414                 :     819274 :   }
     415         [ +  + ]:     377054 :   if (!changed)
     416                 :            :   {
     417                 :            :     // approximations had no effect, return
     418         [ +  - ]:     294368 :     Trace("strings-ent-approx-debug") << "...no approximations" << std::endl;
     419                 :     294368 :     return Node::null();
     420                 :            :   }
     421                 :            :   // get the current "fixed" sum for the abstraction of ar
     422                 :            :   Node aar =
     423                 :      82686 :       aarSum.empty()
     424                 :       5073 :           ? d_zero
     425 [ +  + ][ +  + ]:      82686 :           : (aarSum.size() == 1 ? aarSum[0] : nm->mkNode(Kind::ADD, aarSum));
     426                 :      82686 :   aar = arith::PolyNorm::getPolyNorm(aar);
     427         [ +  - ]:     165372 :   Trace("strings-ent-approx-debug")
     428                 :      82686 :       << "...processed fixed sum " << aar << " with " << mApprox.size()
     429                 :      82686 :       << " approximated monomials." << std::endl;
     430                 :            :   // if we have a choice of how to approximate
     431         [ +  + ]:      82686 :   if (!mApprox.empty())
     432                 :            :   {
     433                 :            :     // convert aar back to monomial sum
     434                 :      27880 :     std::map<Node, Node> msumAar;
     435         [ -  + ]:      27880 :     if (!ArithMSum::getMonomialSum(aar, msumAar))
     436                 :            :     {
     437                 :          0 :       return Node::null();
     438                 :            :     }
     439         [ -  + ]:      27880 :     if (TraceIsOn("strings-ent-approx"))
     440                 :            :     {
     441         [ -  - ]:          0 :       Trace("strings-ent-approx")
     442                 :          0 :           << "---- Check arithmetic entailment by under-approximation " << ar
     443                 :          0 :           << " >= 0" << std::endl;
     444         [ -  - ]:          0 :       Trace("strings-ent-approx") << "FIXED:" << std::endl;
     445                 :          0 :       ArithMSum::debugPrintMonomialSum(msumAar, "strings-ent-approx");
     446         [ -  - ]:          0 :       Trace("strings-ent-approx") << "APPROX:" << std::endl;
     447         [ -  - ]:          0 :       for (std::pair<const Node, std::vector<Node> >& a : mApprox)
     448                 :            :       {
     449                 :          0 :         Node c = msum[a.first];
     450         [ -  - ]:          0 :         Trace("strings-ent-approx") << "  ";
     451         [ -  - ]:          0 :         if (!c.isNull())
     452                 :            :         {
     453         [ -  - ]:          0 :           Trace("strings-ent-approx") << c << " * ";
     454                 :            :         }
     455         [ -  - ]:          0 :         Trace("strings-ent-approx")
     456                 :          0 :             << a.second << " ...from " << a.first << std::endl;
     457                 :          0 :       }
     458         [ -  - ]:          0 :       Trace("strings-ent-approx") << std::endl;
     459                 :            :     }
     460                 :      27880 :     Rational one(1);
     461                 :            :     // incorporate monomials one at a time that have a choice of approximations
     462         [ +  + ]:      56076 :     while (!mApprox.empty())
     463                 :            :     {
     464                 :      28196 :       Node v;
     465                 :      28196 :       Node vapprox;
     466                 :      28196 :       int maxScore = -1;
     467                 :            :       // Look at each approximation, take the one with the best score.
     468                 :            :       // Notice that we are in the process of trying to prove
     469                 :            :       // ( c1*t1 + .. + cn*tn ) + ( approx_1 | ... | approx_m ) >= 0,
     470                 :            :       // where c1*t1 + .. + cn*tn is the "fixed" component of our sum (aar)
     471                 :            :       // and approx_1 ... approx_m are possible approximations. The
     472                 :            :       // intution here is that we want coefficients c1...cn to be positive.
     473                 :            :       // This is because arithmetic string terms t1...tn (which may be
     474                 :            :       // applications of len, indexof, str.to.int) are never entailed to be
     475                 :            :       // negative. Hence, we add the approx_i that contributes the "most"
     476                 :            :       // towards making all constants c1...cn positive and cancelling negative
     477                 :            :       // monomials in approx_i itself.
     478         [ +  - ]:      28196 :       for (std::pair<const Node, std::vector<Node> >& nam : mApprox)
     479                 :            :       {
     480                 :      28196 :         Node cr = msum[nam.first];
     481         [ +  + ]:      84592 :         for (const Node& aa : nam.second)
     482                 :            :         {
     483                 :      56396 :           unsigned helpsCancelCount = 0;
     484                 :      56396 :           unsigned addsObligationCount = 0;
     485                 :      56396 :           std::map<Node, Node>::iterator it;
     486                 :            :           // we are processing an approximation cr*( c1*t1 + ... + cn*tn )
     487         [ +  + ]:     125307 :           for (std::pair<const Node, Node>& aam : approxMsums[aa])
     488                 :            :           {
     489                 :            :             // Say aar is of the form t + c*ti, and aam is the monomial ci*ti
     490                 :            :             // where ci != 0. We say aam:
     491                 :            :             // (1) helps cancel if c != 0 and c>0 != ci>0
     492                 :            :             // (2) adds obligation if c>=0 and c+ci<0
     493                 :      68911 :             Node ti = aam.first;
     494                 :      68911 :             Node ci = aam.second;
     495         [ +  + ]:      68911 :             if (!cr.isNull())
     496                 :            :             {
     497         [ +  + ]:     142146 :               ci = ci.isNull() ? cr
     498                 :      46624 :                                : nm->mkConstInt(cr.getConst<Rational>()
     499 [ +  + ][ -  - ]:     142146 :                                                 * ci.getConst<Rational>());
     500                 :            :             }
     501         [ +  - ]:      68911 :             Trace("strings-ent-approx-debug") << ci << "*" << ti << " ";
     502         [ +  + ]:      68911 :             int ciSgn = ci.isNull() ? 1 : ci.getConst<Rational>().sgn();
     503                 :      68911 :             it = msumAar.find(ti);
     504         [ +  + ]:      68911 :             if (it != msumAar.end())
     505                 :            :             {
     506                 :      28266 :               Node c = it->second;
     507         [ +  + ]:      28266 :               int cSgn = c.isNull() ? 1 : c.getConst<Rational>().sgn();
     508         [ +  + ]:      28266 :               if (cSgn == 0)
     509                 :            :               {
     510         [ +  - ]:       4444 :                 addsObligationCount += (ciSgn == -1 ? 1 : 0);
     511                 :            :               }
     512         [ +  + ]:      23822 :               else if (cSgn != ciSgn)
     513                 :            :               {
     514                 :      16180 :                 helpsCancelCount++;
     515         [ +  + ]:      16180 :                 Rational r1 = c.isNull() ? one : c.getConst<Rational>();
     516         [ +  + ]:      16180 :                 Rational r2 = ci.isNull() ? one : ci.getConst<Rational>();
     517                 :      16180 :                 Rational r12 = r1 + r2;
     518         [ +  + ]:      16180 :                 if (r12.sgn() == -1)
     519                 :            :                 {
     520                 :       6309 :                   addsObligationCount++;
     521                 :            :                 }
     522                 :      16180 :               }
     523                 :      28266 :             }
     524                 :            :             else
     525                 :            :             {
     526         [ +  + ]:      40645 :               addsObligationCount += (ciSgn == -1 ? 1 : 0);
     527                 :            :             }
     528                 :      68911 :           }
     529         [ +  - ]:     112792 :           Trace("strings-ent-approx-debug")
     530                 :          0 :               << "counts=" << helpsCancelCount << "," << addsObligationCount
     531                 :      56396 :               << " for " << aa << " into " << aar << std::endl;
     532         [ +  + ]:      56396 :           int score = (addsObligationCount > 0 ? 0 : 2)
     533         [ +  + ]:      56396 :                       + (helpsCancelCount > 0 ? 1 : 0);
     534                 :            :           // if its the best, update v and vapprox
     535 [ +  + ][ +  + ]:      56396 :           if (v.isNull() || score > maxScore)
                 [ +  + ]
     536                 :            :           {
     537                 :      34582 :             v = nam.first;
     538                 :      34582 :             vapprox = aa;
     539                 :      34582 :             maxScore = score;
     540                 :            :           }
     541                 :            :         }
     542         [ +  - ]:      28196 :         if (!v.isNull())
     543                 :            :         {
     544                 :      28196 :           break;
     545                 :            :         }
     546         [ -  + ]:      28196 :       }
     547         [ +  - ]:      56392 :       Trace("strings-ent-approx") << "- Decide (" << (d_rr == nullptr) << ") "
     548                 :      28196 :                                   << v << " = " << vapprox << std::endl;
     549                 :            :       // we incorporate v approximated by vapprox into the overall approximation
     550                 :            :       // for ar
     551 [ +  - ][ +  - ]:      28196 :       Assert(!v.isNull() && !vapprox.isNull());
         [ -  + ][ -  + ]
                 [ -  - ]
     552 [ -  + ][ -  + ]:      28196 :       Assert(msum.find(v) != msum.end());
                 [ -  - ]
     553                 :      56392 :       Node mn = ArithMSum::mkCoeffTerm(msum[v], vapprox);
     554                 :      28196 :       aar = nm->mkNode(Kind::ADD, aar, mn);
     555                 :      28196 :       approxMap.add(v, vapprox);
     556                 :            :       // update the msumAar map
     557                 :      28196 :       aar = arith::PolyNorm::getPolyNorm(aar);
     558                 :      28196 :       msumAar.clear();
     559         [ -  + ]:      28196 :       if (!ArithMSum::getMonomialSum(aar, msumAar))
     560                 :            :       {
     561                 :          0 :         DebugUnhandled();
     562                 :            :         Trace("strings-ent-approx")
     563                 :            :             << "...failed to get monomial sum!" << std::endl;
     564                 :            :         return Node::null();
     565                 :            :       }
     566                 :            :       // we have processed the approximation for v
     567                 :      28196 :       mApprox.erase(v);
     568 [ +  - ][ +  - ]:      28196 :     }
                 [ +  - ]
     569         [ +  - ]:      27880 :     Trace("strings-ent-approx") << "-----------------" << std::endl;
     570 [ +  - ][ +  - ]:      27880 :   }
     571         [ -  + ]:      82686 :   if (aar == ar)
     572                 :            :   {
     573         [ -  - ]:          0 :     Trace("strings-ent-approx-debug")
     574                 :          0 :         << "...approximation had no effect" << std::endl;
     575                 :            :     // this should never happen, but we avoid the infinite loop for sanity here
     576                 :          0 :     DebugUnhandled();
     577                 :            :     return Node::null();
     578                 :            :   }
     579                 :            :   // Check entailment on the approximation of ar.
     580                 :            :   // Notice that this may trigger further reasoning by approximation. For
     581                 :            :   // example, len( replace( x ++ y, substr( x, 0, n ), z ) ) may be
     582                 :            :   // under-approximated as len( x ) + len( y ) - len( substr( x, 0, n ) ) on
     583                 :            :   // this call, where in the recursive call we may over-approximate
     584                 :            :   // len( substr( x, 0, n ) ) as len( x ). In this example, we can infer
     585                 :            :   // that len( replace( x ++ y, substr( x, 0, n ), z ) ) >= len( y ) in two
     586                 :            :   // steps.
     587         [ +  + ]:      82686 :   if (check(aar, false, isSimple))
     588                 :            :   {
     589         [ +  - ]:      11394 :     Trace("strings-ent-approx")
     590                 :          0 :         << "*** StrArithApprox: showed " << ar
     591                 :       5697 :         << " >= 0 using under-approximation!" << std::endl;
     592         [ +  - ]:      11394 :     Trace("strings-ent-approx")
     593                 :       5697 :         << "*** StrArithApprox: rewritten was " << aar << std::endl;
     594                 :            :     // Apply arithmetic substitution, which ensures we only replace terms
     595                 :            :     // in the top-level arithmetic skeleton of ar.
     596                 :       5697 :     Node approx = approxMap.applyArith(ar);
     597         [ +  - ]:      11394 :     Trace("strings-ent-approx")
     598                 :          0 :         << "*** StrArithApprox: under-approximation was " << approx
     599                 :       5697 :         << std::endl;
     600                 :       5697 :     return approx;
     601                 :       5697 :   }
     602                 :      76989 :   return Node::null();
     603                 :     377054 : }
     604                 :            : 
     605                 :     568298 : void ArithEntail::getArithApproximations(Node a,
     606                 :            :                                          std::vector<Node>& approx,
     607                 :            :                                          bool isOverApprox,
     608                 :            :                                          bool isSimple)
     609                 :            : {
     610                 :     568298 :   NodeManager* nm = a.getNodeManager();
     611                 :            :   // We do not handle ADD here since this leads to exponential behavior.
     612                 :            :   // Instead, this is managed, e.g. during checkApprox, where
     613                 :            :   // ADD terms are expanded "on-demand" during the reasoning.
     614         [ +  - ]:    1136596 :   Trace("strings-ent-approx-debug")
     615                 :     568298 :       << "Get arith approximations " << a << std::endl;
     616                 :     568298 :   Kind ak = a.getKind();
     617         [ +  + ]:     568298 :   if (ak == Kind::MULT)
     618                 :            :   {
     619                 :       1871 :     Node c;
     620                 :       1871 :     Node v;
     621         [ +  + ]:       1871 :     if (ArithMSum::getMonomial(a, c, v))
     622                 :            :     {
     623                 :          6 :       bool isNeg = c.getConst<Rational>().sgn() == -1;
     624         [ -  + ]:         12 :       getArithApproximations(
     625                 :          0 :           v, approx, isNeg ? !isOverApprox : isOverApprox, isSimple);
     626         [ -  + ]:          6 :       for (unsigned i = 0, size = approx.size(); i < size; i++)
     627                 :            :       {
     628                 :          0 :         approx[i] = nm->mkNode(Kind::MULT, c, approx[i]);
     629                 :            :       }
     630                 :            :     }
     631                 :       1871 :   }
     632         [ +  + ]:     566427 :   else if (ak == Kind::STRING_LENGTH)
     633                 :            :   {
     634                 :     410772 :     Kind aak = a[0].getKind();
     635         [ +  + ]:     410772 :     if (aak == Kind::STRING_SUBSTR)
     636                 :            :     {
     637                 :            :       // over,under-approximations for len( substr( x, n, m ) )
     638                 :     168350 :       Node lenx = nm->mkNode(Kind::STRING_LENGTH, a[0][0]);
     639         [ +  + ]:      84175 :       if (isOverApprox)
     640                 :            :       {
     641                 :            :         // m >= 0 implies
     642                 :            :         //  m >= len( substr( x, n, m ) )
     643         [ +  + ]:      46401 :         if (check(a[0][2], false, isSimple))
     644                 :            :         {
     645                 :      26835 :           approx.push_back(a[0][2]);
     646                 :            :         }
     647         [ +  + ]:      46401 :         if (check(lenx, a[0][1], false, isSimple))
     648                 :            :         {
     649                 :            :           // n <= len( x ) implies
     650                 :            :           //   len( x ) - n >= len( substr( x, n, m ) )
     651                 :      20013 :           approx.push_back(nm->mkNode(Kind::SUB, lenx, a[0][1]));
     652                 :            :         }
     653                 :            :         else
     654                 :            :         {
     655                 :            :           // len( x ) >= len( substr( x, n, m ) )
     656                 :      26388 :           approx.push_back(lenx);
     657                 :            :         }
     658                 :            :       }
     659                 :            :       else
     660                 :            :       {
     661                 :            :         // 0 <= n and n+m <= len( x ) implies
     662                 :            :         //   m <= len( substr( x, n, m ) )
     663                 :      75548 :         Node npm = nm->mkNode(Kind::ADD, a[0][1], a[0][2]);
     664                 :      75548 :         if (check(a[0][1], false, isSimple)
     665                 :      75548 :             && check(lenx, npm, false, isSimple))
     666                 :            :         {
     667                 :       4222 :           approx.push_back(a[0][2]);
     668                 :            :         }
     669                 :            :         // 0 <= n and n+m >= len( x ) implies
     670                 :            :         //   len(x)-n <= len( substr( x, n, m ) )
     671                 :      75548 :         if (check(a[0][1], false, isSimple)
     672                 :      75548 :             && check(npm, lenx, false, isSimple))
     673                 :            :         {
     674                 :       3285 :           approx.push_back(nm->mkNode(Kind::SUB, lenx, a[0][1]));
     675                 :            :         }
     676                 :      37774 :       }
     677                 :      84175 :     }
     678         [ +  + ]:     326597 :     else if (aak == Kind::STRING_REPLACE)
     679                 :            :     {
     680                 :            :       // over,under-approximations for len( replace( x, y, z ) )
     681                 :            :       // notice this is either len( x ) or ( len( x ) + len( z ) - len( y ) )
     682                 :      34546 :       Node lenx = nm->mkNode(Kind::STRING_LENGTH, a[0][0]);
     683                 :      34546 :       Node leny = nm->mkNode(Kind::STRING_LENGTH, a[0][1]);
     684                 :      34546 :       Node lenz = nm->mkNode(Kind::STRING_LENGTH, a[0][2]);
     685         [ +  + ]:      17273 :       if (isOverApprox)
     686                 :            :       {
     687         [ +  + ]:       8633 :         if (check(leny, lenz, false, isSimple))
     688                 :            :         {
     689                 :            :           // len( y ) >= len( z ) implies
     690                 :            :           //   len( x ) >= len( replace( x, y, z ) )
     691                 :          1 :           approx.push_back(lenx);
     692                 :            :         }
     693                 :            :         else
     694                 :            :         {
     695                 :            :           // len( x ) + len( z ) >= len( replace( x, y, z ) )
     696                 :       8632 :           approx.push_back(nm->mkNode(Kind::ADD, lenx, lenz));
     697                 :            :         }
     698                 :            :       }
     699                 :            :       else
     700                 :            :       {
     701 [ +  - ][ +  + ]:       8640 :         if (check(lenz, leny, false, isSimple)
         [ -  - ][ -  - ]
     702                 :       8640 :             || check(lenz, lenx, false, isSimple))
     703                 :            :         {
     704                 :            :           // len( y ) <= len( z ) or len( x ) <= len( z ) implies
     705                 :            :           //   len( x ) <= len( replace( x, y, z ) )
     706                 :       2802 :           approx.push_back(lenx);
     707                 :            :         }
     708                 :            :         else
     709                 :            :         {
     710                 :            :           // len( x ) - len( y ) <= len( replace( x, y, z ) )
     711                 :       5838 :           approx.push_back(nm->mkNode(Kind::SUB, lenx, leny));
     712                 :            :         }
     713                 :            :       }
     714                 :      17273 :     }
     715         [ +  + ]:     309324 :     else if (aak == Kind::STRING_ITOS)
     716                 :            :     {
     717                 :            :       // over,under-approximations for len( int.to.str( x ) )
     718         [ +  + ]:       2097 :       if (isOverApprox)
     719                 :            :       {
     720         [ +  + ]:       1072 :         if (check(a[0][0], false, isSimple))
     721                 :            :         {
     722         [ +  + ]:        100 :           if (check(a[0][0], true, isSimple))
     723                 :            :           {
     724                 :            :             // x > 0 implies
     725                 :            :             //   x >= len( int.to.str( x ) )
     726                 :         14 :             approx.push_back(a[0][0]);
     727                 :            :           }
     728                 :            :           else
     729                 :            :           {
     730                 :            :             // x >= 0 implies
     731                 :            :             //   x+1 >= len( int.to.str( x ) )
     732                 :         86 :             approx.push_back(
     733                 :        172 :                 nm->mkNode(Kind::ADD, nm->mkConstInt(Rational(1)), a[0][0]));
     734                 :            :           }
     735                 :            :         }
     736                 :            :       }
     737                 :            :       else
     738                 :            :       {
     739         [ +  + ]:       1025 :         if (check(a[0][0], false, isSimple))
     740                 :            :         {
     741                 :            :           // x >= 0 implies
     742                 :            :           //   len( int.to.str( x ) ) >= 1
     743                 :         48 :           approx.push_back(nm->mkConstInt(Rational(1)));
     744                 :            :         }
     745                 :            :         // other crazy things are possible here, e.g.
     746                 :            :         // len( int.to.str( len( y ) + 10 ) ) >= 2
     747                 :            :       }
     748                 :            :     }
     749                 :            :   }
     750         [ +  + ]:     155655 :   else if (ak == Kind::STRING_INDEXOF)
     751                 :            :   {
     752                 :            :     // over,under-approximations for indexof( x, y, n )
     753         [ +  + ]:      24516 :     if (isOverApprox)
     754                 :            :     {
     755                 :      23614 :       Node lenx = nm->mkNode(Kind::STRING_LENGTH, a[0]);
     756                 :      23614 :       Node leny = nm->mkNode(Kind::STRING_LENGTH, a[1]);
     757         [ +  + ]:      11807 :       if (check(lenx, leny, false, isSimple))
     758                 :            :       {
     759                 :            :         // len( x ) >= len( y ) implies
     760                 :            :         //   len( x ) - len( y ) >= indexof( x, y, n )
     761                 :       1201 :         approx.push_back(nm->mkNode(Kind::SUB, lenx, leny));
     762                 :            :       }
     763                 :            :       else
     764                 :            :       {
     765                 :            :         // len( x ) >= indexof( x, y, n )
     766                 :      10606 :         approx.push_back(lenx);
     767                 :            :       }
     768                 :      11807 :     }
     769                 :            :     else
     770                 :            :     {
     771                 :            :       // TODO?:
     772                 :            :       // contains( substr( x, n, len( x ) ), y ) implies
     773                 :            :       //   n <= indexof( x, y, n )
     774                 :            :       // ...hard to test, runs risk of non-termination
     775                 :            : 
     776                 :            :       // -1 <= indexof( x, y, n )
     777                 :      12709 :       approx.push_back(nm->mkConstInt(Rational(-1)));
     778                 :            :     }
     779                 :            :   }
     780         [ +  + ]:     131139 :   else if (ak == Kind::STRING_STOI)
     781                 :            :   {
     782                 :            :     // over,under-approximations for str.to.int( x )
     783         [ +  + ]:        346 :     if (isOverApprox)
     784                 :            :     {
     785                 :            :       // TODO?:
     786                 :            :       // y >= 0 implies
     787                 :            :       //   y >= str.to.int( int.to.str( y ) )
     788                 :            :     }
     789                 :            :     else
     790                 :            :     {
     791                 :            :       // -1 <= str.to.int( x )
     792                 :        176 :       approx.push_back(nm->mkConstInt(Rational(-1)));
     793                 :            :     }
     794                 :            :   }
     795         [ +  - ]:    1136596 :   Trace("strings-ent-approx-debug")
     796                 :     568298 :       << "Return " << approx.size() << " approximations" << std::endl;
     797                 :     568304 : }
     798                 :            : 
     799                 :       3825 : bool ArithEntail::checkWithEqAssumption(Node assumption, Node a, bool strict)
     800                 :            : {
     801 [ -  + ][ -  + ]:       3825 :   Assert(assumption.getKind() == Kind::EQUAL);
                 [ -  - ]
     802         [ +  - ]:       7650 :   Trace("strings-entail") << "checkWithEqAssumption: " << assumption << " " << a
     803                 :       3825 :                           << ", strict=" << strict << std::endl;
     804                 :            : 
     805                 :            :   // Find candidates variables to compute substitutions for
     806                 :       3825 :   std::unordered_set<Node> candVars;
     807                 :      11475 :   std::vector<Node> toVisit = {assumption};
     808         [ +  + ]:      26080 :   while (!toVisit.empty())
     809                 :            :   {
     810                 :      22255 :     Node curr = toVisit.back();
     811                 :      22255 :     toVisit.pop_back();
     812                 :            : 
     813         [ +  + ]:      40496 :     if (curr.getKind() == Kind::ADD || curr.getKind() == Kind::MULT
     814 [ +  + ][ +  + ]:      40496 :         || curr.getKind() == Kind::SUB || curr.getKind() == Kind::EQUAL)
         [ +  + ][ +  + ]
     815                 :            :     {
     816         [ +  + ]:      27645 :       for (const auto& currChild : curr)
     817                 :            :       {
     818                 :      18430 :         toVisit.push_back(currChild);
     819                 :      18430 :       }
     820                 :            :     }
     821 [ +  + ][ +  - ]:      13040 :     else if (curr.isVar() && Theory::theoryOf(curr) == THEORY_ARITH)
         [ +  + ][ +  + ]
                 [ -  - ]
     822                 :            :     {
     823                 :       3017 :       candVars.insert(curr);
     824                 :            :     }
     825         [ +  + ]:      10023 :     else if (curr.getKind() == Kind::STRING_LENGTH)
     826                 :            :     {
     827                 :       4498 :       candVars.insert(curr);
     828                 :            :     }
     829                 :      22255 :   }
     830                 :            : 
     831                 :            :   // Check if any of the candidate variables are in n
     832                 :       3825 :   Node v;
     833 [ -  + ][ -  + ]:       3825 :   Assert(toVisit.empty());
                 [ -  - ]
     834                 :       3825 :   toVisit.push_back(a);
     835         [ +  + ]:      16505 :   while (!toVisit.empty())
     836                 :            :   {
     837                 :      12840 :     Node curr = toVisit.back();
     838                 :      12840 :     toVisit.pop_back();
     839                 :            : 
     840         [ +  + ]:      22221 :     for (const auto& currChild : curr)
     841                 :            :     {
     842                 :       9381 :       toVisit.push_back(currChild);
     843                 :       9381 :     }
     844                 :            : 
     845         [ +  + ]:      12840 :     if (candVars.find(curr) != candVars.end())
     846                 :            :     {
     847                 :        160 :       v = curr;
     848                 :        160 :       break;
     849                 :            :     }
     850         [ +  + ]:      12840 :   }
     851                 :            : 
     852         [ +  + ]:       3825 :   if (v.isNull())
     853                 :            :   {
     854                 :            :     // No suitable candidate found
     855                 :       3665 :     return false;
     856                 :            :   }
     857                 :            : 
     858                 :        320 :   Node solution = ArithMSum::solveEqualityFor(assumption, v);
     859         [ +  + ]:        160 :   if (solution.isNull())
     860                 :            :   {
     861                 :            :     // Could not solve for v
     862                 :          9 :     return false;
     863                 :            :   }
     864         [ +  - ]:        302 :   Trace("strings-entail") << "checkWithEqAssumption: subs " << v << " -> "
     865                 :        151 :                           << solution << std::endl;
     866                 :            : 
     867                 :        151 :   TNode tv = v;
     868                 :        151 :   TNode tsolution = solution;
     869                 :        151 :   a = a.substitute(tv, tsolution);
     870                 :        151 :   return check(a, strict);
     871                 :       3825 : }
     872                 :            : 
     873                 :       7016 : bool ArithEntail::checkWithAssumption(Node assumption,
     874                 :            :                                       Node a,
     875                 :            :                                       Node b,
     876                 :            :                                       bool strict)
     877                 :            : {
     878                 :       7016 :   NodeManager* nm = assumption.getNodeManager();
     879                 :            : 
     880 [ +  + ][ +  + ]:       7016 :   if (!assumption.isConst() && assumption.getKind() != Kind::EQUAL)
                 [ +  + ]
     881                 :            :   {
     882                 :            :     // We rewrite inequality assumptions from x <= y to x + (str.len s) = y
     883                 :            :     // where s is some fresh string variable. We use (str.len s) because
     884                 :            :     // (str.len s) must be non-negative for the equation to hold.
     885                 :       3821 :     Node x, y;
     886         [ +  + ]:       3821 :     if (assumption.getKind() == Kind::GEQ)
     887                 :            :     {
     888                 :       2641 :       x = assumption[0];
     889                 :       2641 :       y = assumption[1];
     890                 :            :     }
     891                 :            :     else
     892                 :            :     {
     893                 :            :       // (not (>= s t)) --> (>= (t - 1) s)
     894                 :       1180 :       Assert(assumption.getKind() == Kind::NOT
     895                 :            :              && assumption[0].getKind() == Kind::GEQ);
     896                 :       1180 :       x = nm->mkNode(Kind::SUB, assumption[0][1], nm->mkConstInt(Rational(1)));
     897                 :       1180 :       y = assumption[0][0];
     898                 :            :     }
     899                 :            : 
     900                 :       7642 :     Node s = NodeManager::mkBoundVar("slackVal", nm->stringType());
     901                 :       3821 :     Node slen = nm->mkNode(Kind::STRING_LENGTH, s);
     902                 :       7642 :     Node sleny = nm->mkNode(Kind::ADD, y, slen);
     903                 :       7642 :     Node rr = rewriteArith(nm->mkNode(Kind::SUB, x, sleny));
     904         [ -  + ]:       3821 :     if (rr.isConst())
     905                 :            :     {
     906                 :          0 :       assumption = nm->mkConst(rr.getConst<Rational>().sgn() == 0);
     907                 :            :     }
     908                 :            :     else
     909                 :            :     {
     910                 :       3821 :       assumption = nm->mkNode(Kind::EQUAL, x, sleny);
     911                 :            :     }
     912                 :       3821 :   }
     913                 :            : 
     914                 :      14032 :   Node diff = nm->mkNode(Kind::SUB, a, b);
     915                 :       7016 :   bool res = false;
     916         [ +  + ]:       7016 :   if (assumption.isConst())
     917                 :            :   {
     918                 :       3191 :     bool assumptionBool = assumption.getConst<bool>();
     919         [ +  - ]:       3191 :     if (assumptionBool)
     920                 :            :     {
     921                 :       3191 :       res = check(diff, strict);
     922                 :            :     }
     923                 :            :     else
     924                 :            :     {
     925                 :          0 :       res = true;
     926                 :            :     }
     927                 :            :   }
     928                 :            :   else
     929                 :            :   {
     930                 :       3825 :     res = checkWithEqAssumption(assumption, diff, strict);
     931                 :            :   }
     932                 :       7016 :   return res;
     933                 :       7016 : }
     934                 :            : 
     935                 :          0 : bool ArithEntail::checkWithAssumptions(std::vector<Node> assumptions,
     936                 :            :                                        Node a,
     937                 :            :                                        Node b,
     938                 :            :                                        bool strict)
     939                 :            : {
     940                 :            :   // TODO: We currently try to show the entailment with each assumption
     941                 :            :   // independently. In the future, we should make better use of multiple
     942                 :            :   // assumptions.
     943                 :          0 :   bool res = false;
     944         [ -  - ]:          0 :   for (const auto& assumption : assumptions)
     945                 :            :   {
     946         [ -  - ]:          0 :     if (checkWithAssumption(assumption, a, b, strict))
     947                 :            :     {
     948                 :          0 :       res = true;
     949                 :          0 :       break;
     950                 :            :     }
     951                 :            :   }
     952                 :          0 :   return res;
     953                 :            : }
     954                 :            : 
     955                 :            : struct ArithEntailConstantBoundLowerId
     956                 :            : {
     957                 :            : };
     958                 :            : typedef expr::Attribute<ArithEntailConstantBoundLowerId, Node>
     959                 :            :     ArithEntailConstantBoundLower;
     960                 :            : 
     961                 :            : struct ArithEntailConstantBoundUpperId
     962                 :            : {
     963                 :            : };
     964                 :            : typedef expr::Attribute<ArithEntailConstantBoundUpperId, Node>
     965                 :            :     ArithEntailConstantBoundUpper;
     966                 :            : 
     967                 :      84854 : void ArithEntail::setConstantBoundCache(TNode n, Node ret, bool isLower)
     968                 :            : {
     969         [ +  + ]:      84854 :   if (isLower)
     970                 :            :   {
     971                 :            :     ArithEntailConstantBoundLower acbl;
     972                 :      46478 :     n.setAttribute(acbl, ret);
     973                 :            :   }
     974                 :            :   else
     975                 :            :   {
     976                 :            :     ArithEntailConstantBoundUpper acbu;
     977                 :      38376 :     n.setAttribute(acbu, ret);
     978                 :            :   }
     979                 :      84854 : }
     980                 :            : 
     981                 :    1280077 : bool ArithEntail::getConstantBoundCache(TNode n, bool isLower, Node& c)
     982                 :            : {
     983         [ +  + ]:    1280077 :   if (isLower)
     984                 :            :   {
     985                 :            :     ArithEntailConstantBoundLower acbl;
     986         [ +  + ]:    1180793 :     if (n.hasAttribute(acbl))
     987                 :            :     {
     988                 :    1134315 :       c = n.getAttribute(acbl);
     989                 :    1134315 :       return true;
     990                 :            :     }
     991                 :            :   }
     992                 :            :   else
     993                 :            :   {
     994                 :            :     ArithEntailConstantBoundUpper acbu;
     995         [ +  + ]:      99284 :     if (n.hasAttribute(acbu))
     996                 :            :     {
     997                 :      60908 :       c = n.getAttribute(acbu);
     998                 :      60908 :       return true;
     999                 :            :     }
    1000                 :            :   }
    1001                 :      84854 :   return false;
    1002                 :            : }
    1003                 :            : 
    1004                 :      43832 : Node ArithEntail::getConstantBound(TNode a, bool isLower)
    1005                 :            : {
    1006 [ -  + ][ -  + ]:      43832 :   Assert(rewriteArith(a) == a);
                 [ -  - ]
    1007                 :      43832 :   Node ret;
    1008         [ +  + ]:      43832 :   if (getConstantBoundCache(a, isLower, ret))
    1009                 :            :   {
    1010                 :      35730 :     return ret;
    1011                 :            :   }
    1012         [ +  + ]:       8102 :   if (a.isConst())
    1013                 :            :   {
    1014                 :       1812 :     ret = a;
    1015                 :            :   }
    1016         [ +  + ]:       6290 :   else if (a.getKind() == Kind::STRING_LENGTH)
    1017                 :            :   {
    1018         [ +  - ]:       1029 :     if (isLower)
    1019                 :            :     {
    1020                 :       1029 :       ret = d_zero;
    1021                 :            :     }
    1022                 :            :   }
    1023 [ +  + ][ +  + ]:       5261 :   else if (a.getKind() == Kind::ADD || a.getKind() == Kind::MULT)
                 [ +  + ]
    1024                 :            :   {
    1025                 :       4389 :     std::vector<Node> children;
    1026                 :       4389 :     bool success = true;
    1027         [ +  + ]:       8229 :     for (unsigned i = 0; i < a.getNumChildren(); i++)
    1028                 :            :     {
    1029                 :       7410 :       Node ac = getConstantBound(a[i], isLower);
    1030         [ +  + ]:       7410 :       if (ac.isNull())
    1031                 :            :       {
    1032                 :       2402 :         success = false;
    1033                 :       2402 :         break;
    1034                 :            :       }
    1035                 :            :       else
    1036                 :            :       {
    1037         [ +  + ]:       5008 :         if (ac.getConst<Rational>().sgn() == 0)
    1038                 :            :         {
    1039         [ +  + ]:       1487 :           if (a.getKind() == Kind::MULT)
    1040                 :            :           {
    1041                 :         26 :             success = false;
    1042                 :         26 :             break;
    1043                 :            :           }
    1044                 :            :         }
    1045                 :            :         else
    1046                 :            :         {
    1047         [ +  + ]:       3521 :           if (a.getKind() == Kind::MULT)
    1048                 :            :           {
    1049         [ +  + ]:       1170 :             if ((ac.getConst<Rational>().sgn() > 0) != isLower)
    1050                 :            :             {
    1051                 :       1142 :               success = false;
    1052                 :       1142 :               break;
    1053                 :            :             }
    1054                 :            :           }
    1055                 :       2379 :           children.push_back(ac);
    1056                 :            :         }
    1057                 :            :       }
    1058         [ +  + ]:       7410 :     }
    1059         [ +  + ]:       4389 :     if (success)
    1060                 :            :     {
    1061         [ +  + ]:        819 :       if (children.empty())
    1062                 :            :       {
    1063                 :        286 :         ret = d_zero;
    1064                 :            :       }
    1065         [ +  - ]:        533 :       else if (children.size() == 1)
    1066                 :            :       {
    1067                 :        533 :         ret = children[0];
    1068                 :            :       }
    1069                 :            :       else
    1070                 :            :       {
    1071                 :          0 :         ret = a.getNodeManager()->mkNode(a.getKind(), children);
    1072                 :          0 :         ret = rewriteArith(ret);
    1073                 :            :       }
    1074                 :            :     }
    1075                 :       4389 :   }
    1076         [ +  - ]:      16204 :   Trace("strings-rewrite-cbound")
    1077         [ -  - ]:          0 :       << "Constant " << (isLower ? "lower" : "upper") << " bound for " << a
    1078                 :       8102 :       << " is " << ret << std::endl;
    1079 [ +  + ][ +  - ]:       8102 :   Assert(ret.isNull() || ret.isConst());
         [ -  + ][ -  + ]
                 [ -  - ]
    1080                 :            :   // entailment check should be at least as powerful as computing a lower bound
    1081                 :       8102 :   Assert(!isLower || ret.isNull() || ret.getConst<Rational>().sgn() < 0
    1082                 :            :          || check(a, false));
    1083                 :       8102 :   Assert(!isLower || ret.isNull() || ret.getConst<Rational>().sgn() <= 0
    1084                 :            :          || check(a, true));
    1085                 :            :   // cache
    1086                 :       8102 :   setConstantBoundCache(a, ret, isLower);
    1087                 :       8102 :   return ret;
    1088                 :          0 : }
    1089                 :            : 
    1090                 :    1236245 : Node ArithEntail::getConstantBoundLength(TNode s, bool isLower) const
    1091                 :            : {
    1092 [ -  + ][ -  + ]:    1236245 :   Assert(s.getType().isStringLike());
                 [ -  - ]
    1093                 :    1236245 :   Node ret;
    1094         [ +  + ]:    1236245 :   if (getConstantBoundCache(s, isLower, ret))
    1095                 :            :   {
    1096                 :    1159493 :     return ret;
    1097                 :            :   }
    1098                 :      76752 :   NodeManager* nm = s.getNodeManager();
    1099                 :      76752 :   Kind sk = s.getKind();
    1100         [ +  + ]:      76752 :   if (s.isConst())
    1101                 :            :   {
    1102                 :      13056 :     size_t len = Word::getLength(s);
    1103                 :      13056 :     ret = nm->mkConstInt(Rational(len));
    1104                 :            :   }
    1105 [ +  + ][ -  + ]:      63696 :   else if (sk == Kind::SEQ_UNIT || sk == Kind::STRING_UNIT)
    1106                 :            :   {
    1107                 :       1174 :     ret = nm->mkConstInt(1);
    1108                 :            :   }
    1109         [ +  + ]:      62522 :   else if (sk == Kind::STRING_CONCAT)
    1110                 :            :   {
    1111                 :      24886 :     Rational sum(0);
    1112                 :      24886 :     bool success = true;
    1113         [ +  + ]:      57764 :     for (const Node& sc : s)
    1114                 :            :     {
    1115                 :      45305 :       Node b = getConstantBoundLength(sc, isLower);
    1116         [ +  + ]:      45305 :       if (b.isNull())
    1117                 :            :       {
    1118         [ -  + ]:      12427 :         if (isLower)
    1119                 :            :         {
    1120                 :            :           // assume zero and continue
    1121                 :          0 :           continue;
    1122                 :            :         }
    1123                 :      12427 :         success = false;
    1124                 :      12427 :         break;
    1125                 :            :       }
    1126 [ -  + ][ -  + ]:      32878 :       Assert(b.isConst());
                 [ -  - ]
    1127                 :      32878 :       sum = sum + b.getConst<Rational>();
    1128 [ +  - ][ +  + ]:      57732 :     }
                 [ -  + ]
    1129 [ +  + ][ +  + ]:      24886 :     if (success && (!isLower || sum.sgn() != 0))
         [ +  + ][ +  + ]
    1130                 :            :     {
    1131                 :       5717 :       ret = nm->mkConstInt(sum);
    1132                 :            :     }
    1133                 :      24886 :   }
    1134 [ +  + ][ +  + ]:      76752 :   if (ret.isNull() && isLower)
                 [ +  + ]
    1135                 :            :   {
    1136                 :      25560 :     ret = d_zero;
    1137                 :            :   }
    1138                 :            :   // cache
    1139                 :      76752 :   setConstantBoundCache(s, ret, isLower);
    1140                 :      76752 :   return ret;
    1141                 :          0 : }
    1142                 :            : 
    1143                 :    1515036 : bool ArithEntail::checkSimple(Node a)
    1144                 :            : {
    1145                 :            :   // check whether a >= 0
    1146         [ +  + ]:    1515036 :   if (a.isConst())
    1147                 :            :   {
    1148                 :     603404 :     return a.getConst<Rational>().sgn() >= 0;
    1149                 :            :   }
    1150         [ +  + ]:     911632 :   else if (a.getKind() == Kind::STRING_LENGTH)
    1151                 :            :   {
    1152                 :            :     // str.len( t ) >= 0
    1153                 :     146437 :     return true;
    1154                 :            :   }
    1155 [ +  + ][ +  + ]:     765195 :   else if (a.getKind() == Kind::ADD || a.getKind() == Kind::MULT)
                 [ +  + ]
    1156                 :            :   {
    1157         [ +  + ]:     920149 :     for (unsigned i = 0; i < a.getNumChildren(); i++)
    1158                 :            :     {
    1159         [ +  + ]:     906101 :       if (!checkSimple(a[i]))
    1160                 :            :       {
    1161                 :     697631 :         return false;
    1162                 :            :       }
    1163                 :            :     }
    1164                 :            :     // t1 >= 0 ^ ... ^ tn >= 0 => t1 op ... op tn >= 0
    1165                 :      14048 :     return true;
    1166                 :            :   }
    1167                 :            : 
    1168                 :      53516 :   return false;
    1169                 :            : }
    1170                 :            : 
    1171                 :       1867 : bool ArithEntail::inferZerosInSumGeq(Node x,
    1172                 :            :                                      std::vector<Node>& ys,
    1173                 :            :                                      std::vector<Node>& zeroYs)
    1174                 :            : {
    1175 [ -  + ][ -  + ]:       1867 :   Assert(zeroYs.empty());
                 [ -  - ]
    1176                 :            : 
    1177                 :       1867 :   NodeManager* nm = x.getNodeManager();
    1178                 :            : 
    1179                 :            :   // Check if we can show that y1 + ... + yn >= x
    1180         [ +  + ]:       1867 :   Node sum = (ys.size() > 1) ? nm->mkNode(Kind::ADD, ys) : ys[0];
    1181         [ +  + ]:       1867 :   if (!check(sum, x))
    1182                 :            :   {
    1183                 :       1681 :     return false;
    1184                 :            :   }
    1185                 :            : 
    1186                 :            :   // Try to remove yi one-by-one and check if we can still show:
    1187                 :            :   //
    1188                 :            :   // y1 + ... + yi-1 +  yi+1 + ... + yn >= x
    1189                 :            :   //
    1190                 :            :   // If that's the case, we know that yi can be zero and the inequality still
    1191                 :            :   // holds.
    1192                 :        186 :   size_t i = 0;
    1193         [ +  + ]:        761 :   while (i < ys.size())
    1194                 :            :   {
    1195                 :        575 :     Node yi = ys[i];
    1196                 :        575 :     std::vector<Node>::iterator pos = ys.erase(ys.begin() + i);
    1197         [ +  + ]:        575 :     if (ys.size() > 1)
    1198                 :            :     {
    1199                 :        298 :       sum = nm->mkNode(Kind::ADD, ys);
    1200                 :            :     }
    1201                 :            :     else
    1202                 :            :     {
    1203         [ +  + ]:        277 :       sum = ys.size() == 1 ? ys[0] : d_zero;
    1204                 :            :     }
    1205                 :            : 
    1206         [ +  + ]:        575 :     if (check(sum, x))
    1207                 :            :     {
    1208                 :         98 :       zeroYs.push_back(yi);
    1209                 :            :     }
    1210                 :            :     else
    1211                 :            :     {
    1212                 :        477 :       ys.insert(pos, yi);
    1213                 :        477 :       i++;
    1214                 :            :     }
    1215                 :        575 :   }
    1216                 :        186 :   return true;
    1217                 :       1867 : }
    1218                 :            : 
    1219                 :            : }  // namespace strings
    1220                 :            : }  // namespace theory
    1221                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14