LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/strings - strings_entail.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 493 535 92.1 %
Date: 2026-05-04 10:34:19 Functions: 18 19 94.7 %
Branches: 436 621 70.2 %

           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 entailment tests involving strings.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "theory/strings/strings_entail.h"
      14                 :            : 
      15                 :            : #include "expr/node_builder.h"
      16                 :            : #include "theory/rewriter.h"
      17                 :            : #include "theory/strings/arith_entail.h"
      18                 :            : #include "theory/strings/sequences_rewriter.h"
      19                 :            : #include "theory/strings/theory_strings_utils.h"
      20                 :            : #include "theory/strings/word.h"
      21                 :            : #include "util/rational.h"
      22                 :            : #include "util/string.h"
      23                 :            : 
      24                 :            : using namespace cvc5::internal::kind;
      25                 :            : 
      26                 :            : namespace cvc5::internal {
      27                 :            : namespace theory {
      28                 :            : namespace strings {
      29                 :            : 
      30                 :      70235 : StringsEntail::StringsEntail(Rewriter* r, ArithEntail& aent)
      31                 :      70235 :     : d_rr(r), d_arithEntail(aent)
      32                 :            : {
      33                 :      70235 : }
      34                 :            : 
      35                 :          0 : bool StringsEntail::canConstantContainConcat(Node c,
      36                 :            :                                              Node n,
      37                 :            :                                              int& firstc,
      38                 :            :                                              int& lastc)
      39                 :            : {
      40                 :          0 :   Assert(c.isConst());
      41                 :          0 :   Assert(n.getKind() == Kind::STRING_CONCAT);
      42                 :            :   // must find constant components in order
      43                 :          0 :   size_t pos = 0;
      44                 :          0 :   firstc = -1;
      45                 :          0 :   lastc = -1;
      46         [ -  - ]:          0 :   for (unsigned i = 0; i < n.getNumChildren(); i++)
      47                 :            :   {
      48         [ -  - ]:          0 :     if (n[i].isConst())
      49                 :            :     {
      50         [ -  - ]:          0 :       firstc = firstc == -1 ? i : firstc;
      51                 :          0 :       lastc = i;
      52                 :          0 :       size_t new_pos = Word::find(c, n[i], pos);
      53         [ -  - ]:          0 :       if (new_pos == std::string::npos)
      54                 :            :       {
      55                 :          0 :         return false;
      56                 :            :       }
      57                 :            :       else
      58                 :            :       {
      59                 :          0 :         pos = new_pos + Word::getLength(n[i]);
      60                 :            :       }
      61                 :            :     }
      62 [ -  - ][ -  - ]:          0 :     else if (n[i].getKind() == Kind::STRING_ITOS
      63                 :          0 :              && d_arithEntail.check(n[i][0]))
      64                 :            :     {
      65                 :          0 :       Assert(c.getType().isString());  // string-only
      66                 :          0 :       const std::vector<unsigned>& tvec = c.getConst<String>().getVec();
      67                 :            :       // find the first occurrence of a digit starting at pos
      68 [ -  - ][ -  - ]:          0 :       while (pos < tvec.size() && !String::isDigit(tvec[pos]))
                 [ -  - ]
      69                 :            :       {
      70                 :          0 :         pos++;
      71                 :            :       }
      72         [ -  - ]:          0 :       if (pos == tvec.size())
      73                 :            :       {
      74                 :          0 :         return false;
      75                 :            :       }
      76                 :            :       // must consume at least one digit here
      77                 :          0 :       pos++;
      78                 :            :     }
      79                 :            :   }
      80                 :          0 :   return true;
      81                 :            : }
      82                 :            : 
      83                 :      42815 : bool StringsEntail::canConstantContainList(Node c,
      84                 :            :                                            std::vector<Node>& l,
      85                 :            :                                            int& firstc,
      86                 :            :                                            int& lastc)
      87                 :            : {
      88 [ -  + ][ -  + ]:      42815 :   Assert(c.isConst());
                 [ -  - ]
      89                 :            :   // must find constant components in order
      90                 :      42815 :   size_t pos = 0;
      91                 :      42815 :   firstc = -1;
      92                 :      42815 :   lastc = -1;
      93         [ +  + ]:     125841 :   for (unsigned i = 0; i < l.size(); i++)
      94                 :            :   {
      95         [ +  + ]:      84079 :     if (l[i].isConst())
      96                 :            :     {
      97         [ +  + ]:      70887 :       firstc = firstc == -1 ? i : firstc;
      98                 :      70887 :       lastc = i;
      99                 :      70887 :       size_t new_pos = Word::find(c, l[i], pos);
     100         [ +  + ]:      70887 :       if (new_pos == std::string::npos)
     101                 :            :       {
     102                 :       1053 :         return false;
     103                 :            :       }
     104                 :            :       else
     105                 :            :       {
     106                 :      69834 :         pos = new_pos + Word::getLength(l[i]);
     107                 :            :       }
     108                 :            :     }
     109                 :            :   }
     110                 :      41762 :   return true;
     111                 :            : }
     112                 :            : 
     113                 :     238632 : bool StringsEntail::stripSymbolicLength(std::vector<Node>& n1,
     114                 :            :                                         std::vector<Node>& nr,
     115                 :            :                                         int dir,
     116                 :            :                                         Node& curr,
     117                 :            :                                         bool strict)
     118                 :            : {
     119 [ +  + ][ +  - ]:     238632 :   Assert(dir == 1 || dir == -1);
         [ -  + ][ -  + ]
                 [ -  - ]
     120 [ -  + ][ -  + ]:     238632 :   Assert(nr.empty());
                 [ -  - ]
     121                 :     238632 :   NodeManager* nm = curr.getNodeManager();
     122                 :     238632 :   Node zero = nm->mkConstInt(cvc5::internal::Rational(0));
     123                 :     238632 :   bool ret = false;
     124                 :     238632 :   bool success = true;
     125                 :     238632 :   unsigned sindex = 0;
     126 [ +  + ][ +  + ]:     441989 :   while (success && curr != zero && sindex < n1.size())
         [ +  + ][ +  + ]
     127                 :            :   {
     128 [ -  + ][ -  + ]:     203357 :     Assert(!curr.isNull());
                 [ -  - ]
     129                 :     203357 :     success = false;
     130         [ +  + ]:     203357 :     unsigned sindex_use = dir == 1 ? sindex : ((n1.size() - 1) - sindex);
     131         [ +  + ]:     203357 :     if (n1[sindex_use].isConst())
     132                 :            :     {
     133                 :            :       // could strip part of a constant
     134                 :            :       Node lowerBound =
     135                 :      90134 :           d_arithEntail.getConstantBound(d_arithEntail.rewriteArith(curr));
     136         [ +  + ]:      45067 :       if (!lowerBound.isNull())
     137                 :            :       {
     138 [ -  + ][ -  + ]:       9454 :         Assert(lowerBound.isConst());
                 [ -  - ]
     139                 :       9454 :         Rational lbr = lowerBound.getConst<Rational>();
     140         [ +  + ]:       9454 :         if (lbr.sgn() > 0)
     141                 :            :         {
     142 [ -  + ][ -  + ]:       3882 :           Assert(d_arithEntail.check(curr, true));
                 [ -  - ]
     143                 :       3882 :           Node s = n1[sindex_use];
     144                 :       3882 :           size_t slen = Word::getLength(s);
     145                 :       3882 :           Node ncl = nm->mkConstInt(cvc5::internal::Rational(slen));
     146                 :       7764 :           Node next_s = nm->mkNode(Kind::SUB, lowerBound, ncl);
     147                 :       3882 :           next_s = d_arithEntail.rewriteArith(next_s);
     148 [ -  + ][ -  + ]:       3882 :           Assert(next_s.isConst());
                 [ -  - ]
     149                 :            :           // we can remove the entire constant
     150         [ +  + ]:       3882 :           if (next_s.getConst<Rational>().sgn() >= 0)
     151                 :            :           {
     152                 :       1807 :             curr = d_arithEntail.rewriteArith(nm->mkNode(Kind::SUB, curr, ncl));
     153                 :       1807 :             success = true;
     154                 :       1807 :             sindex++;
     155                 :            :           }
     156                 :            :           else
     157                 :            :           {
     158                 :            :             // we can remove part of the constant
     159                 :            :             // lower bound minus the length of a concrete string is negative,
     160                 :            :             // hence lowerBound cannot be larger than long max
     161 [ -  + ][ -  + ]:       2075 :             Assert(lbr < Rational(String::maxSize()));
                 [ -  - ]
     162                 :       4150 :             curr = d_arithEntail.rewriteArith(
     163                 :       6225 :                 nm->mkNode(Kind::SUB, curr, lowerBound));
     164                 :       2075 :             uint32_t lbsize = lbr.getNumerator().toUnsignedInt();
     165 [ -  + ][ -  + ]:       2075 :             Assert(lbsize < slen);
                 [ -  - ]
     166         [ +  + ]:       2075 :             if (dir == 1)
     167                 :            :             {
     168                 :            :               // strip partially from the front
     169                 :       1845 :               nr.push_back(Word::prefix(s, lbsize));
     170                 :       1845 :               n1[sindex_use] = Word::suffix(s, slen - lbsize);
     171                 :            :             }
     172                 :            :             else
     173                 :            :             {
     174                 :            :               // strip partially from the back
     175                 :        230 :               nr.push_back(Word::suffix(s, lbsize));
     176                 :        230 :               n1[sindex_use] = Word::prefix(s, slen - lbsize);
     177                 :            :             }
     178                 :       2075 :             ret = true;
     179                 :            :           }
     180 [ -  + ][ -  + ]:       3882 :           Assert(d_arithEntail.check(curr));
                 [ -  - ]
     181                 :       3882 :         }
     182                 :            :         else
     183                 :            :         {
     184                 :            :           // we cannot remove the constant
     185                 :            :         }
     186                 :       9454 :       }
     187                 :      45067 :     }
     188                 :            :     else
     189                 :            :     {
     190                 :            :       Node next_s = NodeManager::mkNode(
     191                 :            :           Kind::SUB,
     192                 :            :           curr,
     193                 :     316580 :           NodeManager::mkNode(Kind::STRING_LENGTH, n1[sindex_use]));
     194                 :     158290 :       next_s = d_arithEntail.rewriteArith(next_s);
     195         [ +  + ]:     158290 :       if (d_arithEntail.check(next_s))
     196                 :            :       {
     197                 :       1993 :         success = true;
     198                 :       1993 :         curr = next_s;
     199                 :       1993 :         sindex++;
     200                 :            :       }
     201                 :     158290 :     }
     202                 :            :   }
     203 [ +  + ][ +  + ]:     238632 :   if (strict && curr != zero)
                 [ +  + ]
     204                 :            :   {
     205                 :            :     // return false if we did not strip the entire length
     206                 :      10309 :     ret = false;
     207                 :            :   }
     208         [ +  + ]:     228323 :   else if (sindex > 0)
     209                 :            :   {
     210         [ +  + ]:       3378 :     if (dir == 1)
     211                 :            :     {
     212                 :       3046 :       nr.insert(nr.begin(), n1.begin(), n1.begin() + sindex);
     213                 :       3046 :       n1.erase(n1.begin(), n1.begin() + sindex);
     214                 :            :     }
     215                 :            :     else
     216                 :            :     {
     217                 :        332 :       nr.insert(nr.end(), n1.end() - sindex, n1.end());
     218                 :        332 :       n1.erase(n1.end() - sindex, n1.end());
     219                 :            :     }
     220                 :       3378 :     ret = true;
     221                 :            :   }
     222                 :     238632 :   return ret;
     223                 :     238632 : }
     224                 :            : 
     225                 :     222090 : int StringsEntail::componentContains(std::vector<Node>& n1,
     226                 :            :                                      std::vector<Node>& n2,
     227                 :            :                                      std::vector<Node>& nb,
     228                 :            :                                      std::vector<Node>& ne,
     229                 :            :                                      bool computeRemainder,
     230                 :            :                                      int remainderDir)
     231                 :            : {
     232                 :     222090 :   return componentContainsInternal(
     233                 :     222090 :       false, n1, n2, nb, ne, computeRemainder, remainderDir);
     234                 :            : }
     235                 :            : 
     236                 :       5099 : int StringsEntail::componentContainsExt(std::vector<Node>& n1,
     237                 :            :                                         std::vector<Node>& n2,
     238                 :            :                                         std::vector<Node>& nb,
     239                 :            :                                         std::vector<Node>& ne,
     240                 :            :                                         bool computeRemainder,
     241                 :            :                                         int remainderDir)
     242                 :            : {
     243                 :       5099 :   return componentContainsInternal(
     244                 :       5099 :       true, n1, n2, nb, ne, computeRemainder, remainderDir);
     245                 :            : }
     246                 :            : 
     247                 :     227189 : int StringsEntail::componentContainsInternal(bool isExt,
     248                 :            :                                              std::vector<Node>& n1,
     249                 :            :                                              std::vector<Node>& n2,
     250                 :            :                                              std::vector<Node>& nb,
     251                 :            :                                              std::vector<Node>& ne,
     252                 :            :                                              bool computeRemainder,
     253                 :            :                                              int remainderDir)
     254                 :            : {
     255 [ -  + ][ -  + ]:     227189 :   Assert(nb.empty());
                 [ -  - ]
     256 [ -  + ][ -  + ]:     227189 :   Assert(ne.empty());
                 [ -  - ]
     257         [ +  - ]:     227189 :   Trace("strings-entail") << "Component contains: " << std::endl;
     258         [ +  - ]:     227189 :   Trace("strings-entail") << "isExt = " << isExt << std::endl;
     259         [ +  - ]:     227189 :   Trace("strings-entail") << "n1 = " << n1 << std::endl;
     260         [ +  - ]:     227189 :   Trace("strings-entail") << "n2 = " << n2 << std::endl;
     261                 :            :   // if n2 is a singleton, we can do optimized version here
     262         [ +  + ]:     227189 :   if (n2.size() == 1)
     263                 :            :   {
     264         [ +  + ]:     462941 :     for (unsigned i = 0; i < n1.size(); i++)
     265                 :            :     {
     266                 :     273459 :       Node n1rb;
     267                 :     273459 :       Node n1re;
     268         [ +  + ]:     546918 :       if (componentContainsBase(
     269                 :     546918 :               isExt, n1[i], n2[0], n1rb, n1re, 0, computeRemainder))
     270                 :            :       {
     271         [ +  + ]:      20759 :         if (computeRemainder)
     272                 :            :         {
     273                 :       4057 :           n1[i] = n2[0];
     274         [ +  - ]:       4057 :           if (remainderDir != -1)
     275                 :            :           {
     276         [ +  + ]:       4057 :             if (!n1re.isNull())
     277                 :            :             {
     278                 :        281 :               ne.push_back(n1re);
     279                 :            :             }
     280                 :       4057 :             ne.insert(ne.end(), n1.begin() + i + 1, n1.end());
     281                 :       4057 :             n1.erase(n1.begin() + i + 1, n1.end());
     282                 :            :           }
     283         [ -  - ]:          0 :           else if (!n1re.isNull())
     284                 :            :           {
     285                 :          0 :             n1[i] = NodeManager::mkNode(Kind::STRING_CONCAT, n1[i], n1re);
     286                 :            :           }
     287         [ -  + ]:       4057 :           if (remainderDir != 1)
     288                 :            :           {
     289                 :          0 :             nb.insert(nb.end(), n1.begin(), n1.begin() + i);
     290                 :          0 :             n1.erase(n1.begin(), n1.begin() + i);
     291         [ -  - ]:          0 :             if (!n1rb.isNull())
     292                 :            :             {
     293                 :          0 :               nb.push_back(n1rb);
     294                 :            :             }
     295                 :            :           }
     296         [ +  + ]:       4057 :           else if (!n1rb.isNull())
     297                 :            :           {
     298                 :        459 :             n1[i] = NodeManager::mkNode(Kind::STRING_CONCAT, n1rb, n1[i]);
     299                 :            :           }
     300                 :            :         }
     301                 :      20759 :         return i;
     302                 :            :       }
     303 [ +  + ][ +  + ]:     294218 :     }
     304                 :            :   }
     305         [ +  + ]:      16948 :   else if (n1.size() >= n2.size())
     306                 :            :   {
     307                 :       7754 :     unsigned diff = n1.size() - n2.size();
     308         [ +  + ]:      19379 :     for (unsigned i = 0; i <= diff; i++)
     309                 :            :     {
     310                 :      13213 :       Node n1rb_first;
     311                 :      13213 :       Node n1re_first;
     312                 :            :       // first component of n2 must be a suffix
     313         [ +  + ]:      26426 :       if (componentContainsBase(isExt,
     314                 :      13213 :                                 n1[i],
     315                 :      13213 :                                 n2[0],
     316                 :            :                                 n1rb_first,
     317                 :            :                                 n1re_first,
     318                 :            :                                 1,
     319 [ +  + ][ +  + ]:      13213 :                                 computeRemainder && remainderDir != 1))
     320                 :            :       {
     321 [ -  + ][ -  + ]:       2878 :         Assert(n1re_first.isNull());
                 [ -  - ]
     322         [ +  - ]:       4522 :         for (unsigned j = 1; j < n2.size(); j++)
     323                 :            :         {
     324                 :            :           // are we in the last component?
     325         [ +  + ]:       4522 :           if (j + 1 == n2.size())
     326                 :            :           {
     327                 :       2368 :             Node n1rb_last;
     328                 :       2368 :             Node n1re_last;
     329                 :            :             // last component of n2 must be a prefix
     330         [ +  + ]:       4736 :             if (componentContainsBase(isExt,
     331                 :       2368 :                                       n1[i + j],
     332                 :       2368 :                                       n2[j],
     333                 :            :                                       n1rb_last,
     334                 :            :                                       n1re_last,
     335                 :            :                                       -1,
     336 [ +  + ][ +  - ]:       2368 :                                       computeRemainder && remainderDir != -1))
     337                 :            :             {
     338         [ +  - ]:       3176 :               Trace("strings-entail-debug")
     339                 :       1588 :                   << "Last remainder begin is " << n1rb_last << std::endl;
     340         [ +  - ]:       3176 :               Trace("strings-entail-debug")
     341                 :       1588 :                   << "Last remainder end is " << n1re_last << std::endl;
     342 [ -  + ][ -  + ]:       1588 :               Assert(n1rb_last.isNull());
                 [ -  - ]
     343         [ +  + ]:       1588 :               if (computeRemainder)
     344                 :            :               {
     345         [ +  - ]:        530 :                 if (remainderDir != -1)
     346                 :            :                 {
     347         [ +  + ]:        530 :                   if (!n1re_last.isNull())
     348                 :            :                   {
     349                 :          6 :                     ne.push_back(n1re_last);
     350                 :            :                   }
     351                 :        530 :                   ne.insert(ne.end(), n1.begin() + i + j + 1, n1.end());
     352                 :        530 :                   n1.erase(n1.begin() + i + j + 1, n1.end());
     353                 :        530 :                   n1[i + j] = n2[j];
     354                 :            :                 }
     355         [ +  + ]:        530 :                 if (remainderDir != 1)
     356                 :            :                 {
     357                 :          9 :                   n1[i] = n2[0];
     358                 :          9 :                   nb.insert(nb.end(), n1.begin(), n1.begin() + i);
     359                 :          9 :                   n1.erase(n1.begin(), n1.begin() + i);
     360         [ +  + ]:          9 :                   if (!n1rb_first.isNull())
     361                 :            :                   {
     362                 :          3 :                     nb.push_back(n1rb_first);
     363                 :            :                   }
     364                 :            :                 }
     365                 :            :               }
     366         [ +  - ]:       1588 :               Trace("strings-entail-debug") << "ne = " << ne << std::endl;
     367         [ +  - ]:       1588 :               Trace("strings-entail-debug") << "nb = " << nb << std::endl;
     368         [ +  - ]:       1588 :               Trace("strings-entail-debug") << "...return " << i << std::endl;
     369                 :       1588 :               return i;
     370                 :            :             }
     371                 :            :             else
     372                 :            :             {
     373                 :        780 :               break;
     374                 :            :             }
     375 [ +  + ][ +  + ]:       4736 :           }
     376         [ +  + ]:       2154 :           else if (n1[i + j] != n2[j])
     377                 :            :           {
     378                 :        510 :             break;
     379                 :            :           }
     380                 :            :         }
     381                 :            :       }
     382 [ +  + ][ +  + ]:      14801 :     }
     383                 :            :   }
     384                 :     204842 :   return -1;
     385                 :            : }
     386                 :            : 
     387                 :     289040 : bool StringsEntail::componentContainsBase(bool isExt,
     388                 :            :                                           Node n1,
     389                 :            :                                           Node n2,
     390                 :            :                                           Node& n1rb,
     391                 :            :                                           Node& n1re,
     392                 :            :                                           int dir,
     393                 :            :                                           bool computeRemainder)
     394                 :            : {
     395 [ -  + ][ -  + ]:     289040 :   Assert(n1rb.isNull());
                 [ -  - ]
     396 [ -  + ][ -  + ]:     289040 :   Assert(n1re.isNull());
                 [ -  - ]
     397                 :            : 
     398         [ +  + ]:     289040 :   if (n1 == n2)
     399                 :            :   {
     400                 :      21344 :     return true;
     401                 :            :   }
     402                 :            :   else
     403                 :            :   {
     404 [ +  + ][ +  + ]:     267696 :     if (n1.isConst() && n2.isConst())
                 [ +  + ]
     405                 :            :     {
     406                 :      12306 :       size_t len1 = Word::getLength(n1);
     407                 :      12306 :       size_t len2 = Word::getLength(n2);
     408         [ +  + ]:      12306 :       if (len2 < len1)
     409                 :            :       {
     410         [ +  + ]:       4900 :         if (dir == 1)
     411                 :            :         {
     412         [ +  + ]:        414 :           if (Word::suffix(n1, len2) == n2)
     413                 :            :           {
     414         [ +  + ]:        299 :             if (computeRemainder)
     415                 :            :             {
     416                 :          3 :               n1rb = Word::prefix(n1, len1 - len2);
     417                 :            :             }
     418                 :        299 :             return true;
     419                 :            :           }
     420                 :            :         }
     421         [ +  + ]:       4486 :         else if (dir == -1)
     422                 :            :         {
     423         [ +  - ]:        314 :           if (Word::prefix(n1, len2) == n2)
     424                 :            :           {
     425         [ +  + ]:        314 :             if (computeRemainder)
     426                 :            :             {
     427                 :          6 :               n1re = Word::suffix(n1, len1 - len2);
     428                 :            :             }
     429                 :        314 :             return true;
     430                 :            :           }
     431                 :            :         }
     432                 :            :         else
     433                 :            :         {
     434                 :       4172 :           size_t f = Word::find(n1, n2);
     435         [ +  + ]:       4172 :           if (f != std::string::npos)
     436                 :            :           {
     437         [ +  + ]:       3254 :             if (computeRemainder)
     438                 :            :             {
     439         [ +  + ]:        691 :               if (f > 0)
     440                 :            :               {
     441                 :        459 :                 n1rb = Word::prefix(n1, f);
     442                 :            :               }
     443         [ +  + ]:        691 :               if (len1 > f + len2)
     444                 :            :               {
     445                 :        281 :                 n1re = Word::suffix(n1, len1 - (f + len2));
     446                 :            :               }
     447                 :            :             }
     448                 :       3254 :             return true;
     449                 :            :           }
     450                 :            :         }
     451                 :            :       }
     452                 :            :     }
     453 [ +  + ][ -  + ]:     255390 :     else if (!isExt || computeRemainder)
     454                 :            :     {
     455                 :            :       // Note the cases below would require constructing new terms
     456                 :            :       // as part of the remainder components. Thus, this is only checked
     457                 :            :       // when computeRemainder is false.
     458                 :     250286 :       return false;
     459                 :            :     }
     460                 :            :     else
     461                 :            :     {
     462                 :            :       // cases for:
     463                 :            :       //   n1 = x   containing   n2 = substr( x, n2[1], n2[2] )
     464         [ +  + ]:       5104 :       if (n2.getKind() == Kind::STRING_SUBSTR)
     465                 :            :       {
     466         [ +  + ]:       4719 :         if (n2[0] == n1)
     467                 :            :         {
     468                 :         12 :           bool success = true;
     469                 :         12 :           Node start_pos = n2[1];
     470                 :         24 :           Node end_pos = NodeManager::mkNode(Kind::ADD, n2[1], n2[2]);
     471                 :         24 :           Node len_n2s = NodeManager::mkNode(Kind::STRING_LENGTH, n2[0]);
     472         [ +  + ]:         12 :           if (dir == 1)
     473                 :            :           {
     474                 :            :             // To be a suffix, start + length must be greater than
     475                 :            :             // or equal to the length of the string.
     476                 :          6 :             success = d_arithEntail.check(end_pos, len_n2s);
     477                 :            :           }
     478         [ +  - ]:          6 :           else if (dir == -1)
     479                 :            :           {
     480                 :            :             // To be a prefix, must literally start at 0, since
     481                 :            :             //   if we knew it started at <0, it should be rewritten to "",
     482                 :            :             //   if we knew it started at 0, then n2[1] should be rewritten to
     483                 :            :             //   0.
     484                 :         12 :             success = start_pos.isConst()
     485 [ +  - ][ +  - ]:          6 :                       && start_pos.getConst<Rational>().sgn() == 0;
     486                 :            :           }
     487         [ +  - ]:         12 :           if (success)
     488                 :            :           {
     489                 :         12 :             return true;
     490                 :            :           }
     491 [ -  + ][ -  + ]:         36 :         }
                 [ -  + ]
     492                 :            :       }
     493                 :            : 
     494         [ +  + ]:       5092 :       if (dir == 0)
     495                 :            :       {
     496         [ +  + ]:       5086 :         if (n1.getKind() == Kind::STRING_REPLACE)
     497                 :            :         {
     498                 :            :           // (str.contains (str.replace x y z) w) ---> true
     499                 :            :           // if (str.contains x w) --> true and (str.contains z w) ---> true
     500                 :         16 :           Node xCtnW = checkContains(n1[0], n2);
     501 [ +  + ][ +  + ]:          8 :           if (!xCtnW.isNull() && xCtnW.getConst<bool>())
                 [ +  + ]
     502                 :            :           {
     503                 :          6 :             Node zCtnW = checkContains(n1[2], n2);
     504 [ +  + ][ +  - ]:          3 :             if (!zCtnW.isNull() && zCtnW.getConst<bool>())
                 [ +  + ]
     505                 :            :             {
     506                 :          2 :               return true;
     507                 :            :             }
     508         [ +  + ]:          3 :           }
     509         [ +  + ]:          8 :         }
     510                 :            :       }
     511                 :            :     }
     512                 :            :   }
     513                 :      13529 :   return false;
     514                 :            : }
     515                 :            : 
     516                 :     277015 : bool StringsEntail::stripConstantEndpoints(std::vector<Node>& n1,
     517                 :            :                                            std::vector<Node>& n2,
     518                 :            :                                            std::vector<Node>& nb,
     519                 :            :                                            std::vector<Node>& ne,
     520                 :            :                                            int dir)
     521                 :            : {
     522 [ -  + ][ -  + ]:     277015 :   Assert(nb.empty());
                 [ -  - ]
     523 [ -  + ][ -  + ]:     277015 :   Assert(ne.empty());
                 [ -  - ]
     524                 :     277015 :   bool changed = false;
     525                 :            :   // for ( forwards, backwards ) direction
     526         [ +  + ]:     727079 :   for (unsigned r = 0; r < 2; r++)
     527                 :            :   {
     528 [ +  + ][ +  + ]:     502167 :     if (dir == 0 || (r == 0 && dir == 1) || (r == 1 && dir == -1))
         [ +  + ][ +  + ]
                 [ +  + ]
     529                 :            :     {
     530         [ +  + ]:     360353 :       unsigned index0 = r == 0 ? 0 : n1.size() - 1;
     531         [ +  + ]:     360353 :       unsigned index1 = r == 0 ? 0 : n2.size() - 1;
     532                 :     360353 :       bool removeComponent = false;
     533                 :     360353 :       Node n1cmp = n1[index0];
     534                 :            : 
     535 [ +  + ][ +  + ]:     360353 :       if (n1cmp.isConst() && Word::isEmpty(n1cmp))
         [ +  + ][ +  + ]
                 [ -  - ]
     536                 :            :       {
     537                 :      51993 :         return false;
     538                 :            :       }
     539                 :            : 
     540                 :     308360 :       std::vector<Node> sss;
     541                 :     308360 :       std::vector<Node> sls;
     542                 :     308360 :       n1cmp = utils::decomposeSubstrChain(n1cmp, sss, sls);
     543         [ +  - ]:     616720 :       Trace("strings-rewrite-debug2")
     544                 :          0 :           << "stripConstantEndpoints : Compare " << n1cmp << " " << n2[index1]
     545                 :     308360 :           << ", dir = " << r << ", sss/sls=" << sss << "/" << sls << std::endl;
     546         [ +  + ]:     308360 :       if (n1cmp.isConst())
     547                 :            :       {
     548                 :     110515 :         Node s = n1cmp;
     549                 :     110515 :         size_t slen = Word::getLength(s);
     550                 :            :         // overlap is an overapproximation of the number of characters
     551                 :            :         // n2[index1] can match in s
     552                 :     110515 :         unsigned overlap = Word::getLength(s);
     553         [ +  + ]:     110515 :         if (n2[index1].isConst())
     554                 :            :         {
     555                 :      23133 :           Node t = n2[index1];
     556                 :      23133 :           std::size_t ret = r == 0 ? Word::find(s, t) : Word::rfind(s, t);
     557         [ +  + ]:      23133 :           if (ret == std::string::npos)
     558                 :            :           {
     559         [ +  + ]:       6015 :             if (n1.size() == 1)
     560                 :            :             {
     561                 :            :               // can remove everything
     562                 :            :               //   e.g. str.contains( "abc", str.++( "ba", x ) ) -->
     563                 :            :               //   str.contains( "", str.++( "ba", x ) )
     564                 :            :               //   or std.contains( str.substr( "abc", x, y ), "d" ) --->
     565                 :            :               //   str.contains( "", "d" )
     566                 :         31 :               removeComponent = true;
     567                 :            :             }
     568         [ +  + ]:       5984 :             else if (sss.empty())  // only if not substr
     569                 :            :             {
     570                 :            :               // check how much overlap there is
     571                 :            :               // This is used to partially strip off the endpoint
     572                 :            :               // e.g. str.contains( str.++( "abc", x ), str.++( "cd", y ) ) -->
     573                 :            :               // str.contains( str.++( "c", x ), str.++( "cd", y ) )
     574                 :       5981 :               overlap = r == 0 ? Word::overlap(s, t) : Word::overlap(t, s);
     575                 :            :             }
     576                 :            :             // note that we cannot process substring here, since t may
     577                 :            :             // match only part of s. Consider:
     578                 :            :             // (str.++ "C" (str.substr "AB" x y)), "CB"
     579                 :            :             // where "AB" and "CB" have no overlap, but "C" is not part of what
     580                 :            :             // is matched with "AB".
     581                 :            :           }
     582         [ +  + ]:      17118 :           else if (sss.empty())  // only if not substr
     583                 :            :           {
     584 [ -  + ][ -  + ]:       6054 :             Assert(ret < slen);
                 [ -  - ]
     585                 :            :             // can strip off up to the find position, e.g.
     586                 :            :             // str.contains( str.++( "abc", x ), str.++( "b", y ) ) -->
     587                 :            :             // str.contains( str.++( "bc", x ), str.++( "b", y ) ),
     588                 :            :             // and
     589                 :            :             // str.contains( str.++( x, "abbd" ), str.++( y, "b" ) ) -->
     590                 :            :             // str.contains( str.++( x, "abb" ), str.++( y, "b" ) )
     591                 :       6054 :             overlap = slen - ret;
     592                 :            :           }
     593                 :      23133 :         }
     594                 :            :         else
     595                 :            :         {
     596                 :            :           // inconclusive
     597                 :            :         }
     598         [ +  - ]:     221030 :         Trace("strings-rewrite-debug2")
     599                 :          0 :             << "rem = " << removeComponent << ", overlap = " << overlap
     600                 :     110515 :             << std::endl;
     601                 :            :         // process the overlap
     602         [ +  + ]:     110515 :         if (overlap < slen)
     603                 :            :         {
     604                 :       4181 :           changed = true;
     605         [ +  + ]:       4181 :           if (overlap == 0)
     606                 :            :           {
     607                 :       3753 :             removeComponent = true;
     608                 :            :           }
     609                 :            :           else
     610                 :            :           {
     611                 :            :             // can drop the prefix (resp. suffix) from the first (resp. last)
     612                 :            :             // component
     613         [ +  + ]:        428 :             if (r == 0)
     614                 :            :             {
     615                 :        144 :               nb.push_back(Word::prefix(s, slen - overlap));
     616                 :        144 :               n1[index0] = Word::suffix(s, overlap);
     617                 :            :             }
     618                 :            :             else
     619                 :            :             {
     620                 :        284 :               ne.push_back(Word::suffix(s, slen - overlap));
     621                 :        284 :               n1[index0] = Word::prefix(s, overlap);
     622                 :            :             }
     623                 :            :           }
     624                 :            :         }
     625                 :     110515 :       }
     626         [ +  + ]:     197845 :       else if (n1cmp.getKind() == Kind::STRING_ITOS)
     627                 :            :       {
     628         [ +  + ]:       1543 :         if (n2[index1].isConst())
     629                 :            :         {
     630 [ -  + ][ -  + ]:        978 :           Assert(n2[index1].getType().isString());  // string-only
                 [ -  - ]
     631                 :        978 :           cvc5::internal::String t = n2[index1].getConst<String>();
     632         [ +  + ]:        978 :           if (n1.size() == 1)
     633                 :            :           {
     634                 :            :             // if n1.size()==1, then if n2[index1] is not a number, we can drop
     635                 :            :             // the entire component
     636                 :            :             //    e.g. str.contains( int.to.str(x), "123a45") --> false
     637         [ +  + ]:        858 :             if (!t.isNumber())
     638                 :            :             {
     639                 :         79 :               removeComponent = true;
     640                 :            :             }
     641                 :            :           }
     642                 :            :           else
     643                 :            :           {
     644                 :        120 :             const std::vector<unsigned>& tvec = t.getVec();
     645 [ -  + ][ -  + ]:        120 :             Assert(tvec.size() > 0);
                 [ -  - ]
     646                 :            : 
     647                 :            :             // if n1.size()>1, then if the first (resp. last) character of
     648                 :            :             // n2[index1]
     649                 :            :             //  is not a digit, we can drop the entire component, e.g.:
     650                 :            :             //    str.contains( str.++( int.to.str(x), y ), "a12") -->
     651                 :            :             //    str.contains( y, "a12" )
     652                 :            :             //    str.contains( str.++( y, int.to.str(x) ), "a0b") -->
     653                 :            :             //    str.contains( y, "a0b" )
     654         [ +  + ]:        120 :             unsigned i = r == 0 ? 0 : (tvec.size() - 1);
     655         [ +  + ]:        120 :             if (!String::isDigit(tvec[i]))
     656                 :            :             {
     657                 :         47 :               removeComponent = true;
     658                 :            :             }
     659                 :            :           }
     660                 :        978 :         }
     661                 :            :       }
     662         [ +  + ]:     308360 :       if (removeComponent)
     663                 :            :       {
     664         [ +  - ]:       3910 :         Trace("strings-rewrite-debug2") << "...remove component" << std::endl;
     665                 :            :         // can drop entire first (resp. last) component
     666         [ +  + ]:       3910 :         if (r == 0)
     667                 :            :         {
     668                 :       1432 :           nb.push_back(n1[index0]);
     669                 :       1432 :           n1.erase(n1.begin(), n1.begin() + 1);
     670                 :            :         }
     671                 :            :         else
     672                 :            :         {
     673                 :       2478 :           ne.push_back(n1[index0]);
     674                 :       2478 :           n1.pop_back();
     675                 :            :         }
     676         [ +  + ]:       3910 :         if (n1.empty())
     677                 :            :         {
     678                 :            :           // if we've removed everything, just return (we will rewrite to false)
     679                 :        110 :           return true;
     680                 :            :         }
     681                 :            :         else
     682                 :            :         {
     683                 :       3800 :           changed = true;
     684                 :            :         }
     685                 :            :       }
     686 [ +  + ][ +  + ]:     360573 :     }
                 [ +  + ]
     687                 :            :   }
     688                 :            :   // TODO (#1180) : computing the maximal overlap in this function may be
     689                 :            :   // important.
     690                 :            :   // str.contains( str.++( str.to.int(x), str.substr(y,0,3) ), "2aaaa" ) --->
     691                 :            :   // false
     692                 :            :   //   ...since str.to.int(x) can contain at most 1 character from "2aaaa",
     693                 :            :   //   leaving 4 characters
     694                 :            :   //      which is larger that the upper bound for length of str.substr(y,0,3),
     695                 :            :   //      which is 3.
     696                 :     224912 :   return changed;
     697                 :            : }
     698                 :            : 
     699                 :     102866 : Node StringsEntail::checkContains(Node a, Node b)
     700                 :            : {
     701                 :     205732 :   Node ctn = NodeManager::mkNode(Kind::STRING_CONTAINS, a, b);
     702                 :            : 
     703         [ +  - ]:     102866 :   if (d_rr != nullptr)
     704                 :            :   {
     705                 :     102866 :     ctn = d_rr->rewrite(ctn);
     706                 :            :   }
     707                 :            :   else
     708                 :            :   {
     709         [ -  - ]:          0 :     if (d_rewriter == nullptr)
     710                 :            :     {
     711                 :          0 :       return Node::null();
     712                 :            :     }
     713                 :          0 :     Node prev;
     714                 :            :     do
     715                 :            :     {
     716                 :          0 :       prev = ctn;
     717                 :          0 :       ctn = d_rewriter->rewriteContains(ctn);
     718                 :          0 :     } while (prev != ctn && ctn.getKind() == Kind::STRING_CONTAINS);
     719                 :          0 :   }
     720                 :            : 
     721 [ -  + ][ -  + ]:     102866 :   Assert(ctn.getType().isBoolean());
                 [ -  - ]
     722         [ +  + ]:     102866 :   return ctn.isConst() ? ctn : Node::null();
     723                 :     102866 : }
     724                 :            : 
     725                 :      64197 : bool StringsEntail::checkNonEmpty(Node a)
     726                 :            : {
     727         [ +  + ]:      64197 :   if (a.isConst())
     728                 :            :   {
     729                 :      25576 :     return Word::getLength(a) != 0;
     730                 :            :   }
     731                 :      38621 :   Node len = NodeManager::mkNode(Kind::STRING_LENGTH, a);
     732                 :      38621 :   len = d_arithEntail.rewriteArith(len);
     733                 :      38621 :   return d_arithEntail.check(len, true);
     734                 :      38621 : }
     735                 :            : 
     736                 :      78707 : bool StringsEntail::checkLengthOne(Node s, bool strict)
     737                 :            : {
     738         [ +  + ]:      78707 :   if (s.isConst())
     739                 :            :   {
     740                 :       7814 :     size_t len = Word::getLength(s);
     741         [ +  + ]:       7814 :     return strict ? (len == 1) : (len <= 1);
     742                 :            :   }
     743                 :      70893 :   NodeManager* nm = s.getNodeManager();
     744                 :      70893 :   Node one = nm->mkConstInt(Rational(1));
     745                 :      70893 :   Node len = nm->mkNode(Kind::STRING_LENGTH, s);
     746                 :      70893 :   len = d_arithEntail.rewriteArith(len);
     747 [ +  - ][ -  - ]:      70893 :   return d_arithEntail.check(one, len)
                 [ -  - ]
     748 [ +  + ][ +  + ]:      70893 :          && (!strict || d_arithEntail.check(len, true));
         [ +  + ][ +  + ]
         [ +  - ][ -  - ]
     749                 :      70893 : }
     750                 :            : 
     751                 :     200168 : bool StringsEntail::checkMultisetSubset(Node a, Node b)
     752                 :            : {
     753                 :     200168 :   std::vector<Node> avec;
     754                 :     200168 :   utils::getConcat(getMultisetApproximation(a), avec);
     755                 :     200168 :   std::vector<Node> bvec;
     756                 :     200168 :   utils::getConcat(b, bvec);
     757                 :            : 
     758         [ +  + ]:    1201008 :   std::map<Node, unsigned> num_nconst[2];
     759         [ +  + ]:    1201008 :   std::map<Node, unsigned> num_const[2];
     760         [ +  + ]:     600504 :   for (unsigned j = 0; j < 2; j++)
     761                 :            :   {
     762         [ +  + ]:     400336 :     std::vector<Node>& jvec = j == 0 ? avec : bvec;
     763         [ +  + ]:     933893 :     for (const Node& cc : jvec)
     764                 :            :     {
     765         [ +  + ]:     533557 :       if (cc.isConst())
     766                 :            :       {
     767                 :     235605 :         num_const[j][cc]++;
     768                 :            :       }
     769                 :            :       else
     770                 :            :       {
     771                 :     297952 :         num_nconst[j][cc]++;
     772                 :            :       }
     773                 :            :     }
     774                 :            :   }
     775                 :     200168 :   bool ms_success = true;
     776         [ +  + ]:     206103 :   for (std::pair<const Node, unsigned>& nncp : num_nconst[0])
     777                 :            :   {
     778         [ +  + ]:     104234 :     if (nncp.second > num_nconst[1][nncp.first])
     779                 :            :     {
     780                 :      98299 :       ms_success = false;
     781                 :      98299 :       break;
     782                 :            :     }
     783                 :            :   }
     784         [ +  + ]:     200168 :   if (ms_success)
     785                 :            :   {
     786                 :            :     // count the number of constant characters in the first argument
     787         [ +  + ]:     611214 :     std::map<Node, unsigned> count_const[2];
     788                 :     101869 :     std::vector<Node> chars;
     789         [ +  + ]:     305607 :     for (unsigned j = 0; j < 2; j++)
     790                 :            :     {
     791         [ +  + ]:     316964 :       for (std::pair<const Node, unsigned>& ncp : num_const[j])
     792                 :            :       {
     793                 :     113226 :         Node cn = ncp.first;
     794 [ -  + ][ -  + ]:     113226 :         Assert(cn.isConst());
                 [ -  - ]
     795                 :     113226 :         std::vector<Node> cnChars = Word::getChars(cn);
     796         [ +  + ]:     269950 :         for (const Node& ch : cnChars)
     797                 :            :         {
     798                 :     156724 :           count_const[j][ch] += ncp.second;
     799         [ +  + ]:     156724 :           if (std::find(chars.begin(), chars.end(), ch) == chars.end())
     800                 :            :           {
     801                 :     117231 :             chars.push_back(ch);
     802                 :            :           }
     803                 :            :         }
     804                 :     113226 :       }
     805                 :            :     }
     806         [ +  - ]:     203738 :     Trace("strings-entail-ms-ss")
     807                 :     101869 :         << "For " << a << " and " << b << " : " << std::endl;
     808         [ +  + ]:     217555 :     for (const Node& ch : chars)
     809                 :            :     {
     810         [ +  - ]:     116630 :       Trace("strings-entail-ms-ss") << "  # occurrences of substring ";
     811         [ +  - ]:     116630 :       Trace("strings-entail-ms-ss") << ch << " in arguments is ";
     812         [ +  - ]:     233260 :       Trace("strings-entail-ms-ss")
     813                 :     116630 :           << count_const[0][ch] << " / " << count_const[1][ch] << std::endl;
     814         [ +  + ]:     116630 :       if (count_const[0][ch] < count_const[1][ch])
     815                 :            :       {
     816                 :        944 :         return true;
     817                 :            :       }
     818                 :            :     }
     819                 :            : 
     820                 :            :     // TODO (#1180): count the number of 2,3,4,.. character substrings
     821                 :            :     // for example:
     822                 :            :     // str.contains( str.++( x, "cbabc" ), str.++( "cabbc", x ) ) ---> false
     823                 :            :     // since the second argument contains more occurrences of "bb".
     824                 :            :     // note this is orthogonal reasoning to inductive reasoning
     825                 :            :     // via regular membership reduction in Liang et al CAV 2015.
     826 [ +  + ][ +  + ]:     407476 :   }
                 [ -  - ]
     827                 :     199224 :   return false;
     828 [ +  + ][ +  + ]:    1401176 : }
         [ -  - ][ -  - ]
     829                 :            : 
     830                 :       6770 : Node StringsEntail::checkHomogeneousString(Node a)
     831                 :            : {
     832                 :       6770 :   std::vector<Node> avec;
     833                 :       6770 :   utils::getConcat(getMultisetApproximation(a), avec);
     834                 :            : 
     835                 :       6770 :   bool cValid = false;
     836                 :       6770 :   Node c;
     837         [ +  + ]:       8870 :   for (const Node& ac : avec)
     838                 :            :   {
     839         [ +  + ]:       6811 :     if (ac.isConst())
     840                 :            :     {
     841                 :       4416 :       std::vector<Node> acv = Word::getChars(ac);
     842         [ +  + ]:       9073 :       for (const Node& cc : acv)
     843                 :            :       {
     844         [ +  + ]:       6973 :         if (!cValid)
     845                 :            :         {
     846                 :       3881 :           cValid = true;
     847                 :       3881 :           c = cc;
     848                 :            :         }
     849         [ +  + ]:       3092 :         else if (c != cc)
     850                 :            :         {
     851                 :            :           // Found a different character
     852                 :       2316 :           return Node::null();
     853                 :            :         }
     854                 :            :       }
     855         [ +  + ]:       4416 :     }
     856                 :            :     else
     857                 :            :     {
     858                 :            :       // Could produce a different character
     859                 :       2395 :       return Node::null();
     860                 :            :     }
     861                 :            :   }
     862                 :            : 
     863         [ +  + ]:       2059 :   if (!cValid)
     864                 :            :   {
     865                 :       1056 :     return Word::mkEmptyWord(a.getType());
     866                 :            :   }
     867                 :            : 
     868                 :       1531 :   return c;
     869                 :       6770 : }
     870                 :            : 
     871                 :     418973 : Node StringsEntail::getMultisetApproximation(Node a)
     872                 :            : {
     873                 :     418973 :   NodeManager* nm = a.getNodeManager();
     874         [ +  + ]:     472853 :   while (a.getKind() == Kind::STRING_SUBSTR)
     875                 :            :   {
     876                 :      53880 :     a = a[0];
     877                 :            :   }
     878         [ +  + ]:     418973 :   if (a.getKind() == Kind::STRING_REPLACE)
     879                 :            :   {
     880                 :            :     return getMultisetApproximation(
     881                 :      47178 :         nm->mkNode(Kind::STRING_CONCAT, a[0], a[2]));
     882                 :            :   }
     883         [ +  + ]:     395384 :   else if (a.getKind() == Kind::STRING_CONCAT)
     884                 :            :   {
     885                 :      69510 :     NodeBuilder nb(nm, Kind::STRING_CONCAT);
     886         [ +  + ]:     257956 :     for (const Node& ac : a)
     887                 :            :     {
     888                 :     188446 :       nb << getMultisetApproximation(ac);
     889                 :     188446 :     }
     890                 :      69510 :     return nb.constructNode();
     891                 :      69510 :   }
     892                 :            :   else
     893                 :            :   {
     894                 :     325874 :     return a;
     895                 :            :   }
     896                 :            : }
     897                 :            : 
     898                 :       1364 : Node StringsEntail::getStringOrEmpty(Node n)
     899                 :            : {
     900                 :       1364 :   Node res;
     901         [ +  + ]:       2733 :   while (res.isNull())
     902                 :            :   {
     903    [ +  + ][ + ]:       1369 :     switch (n.getKind())
     904                 :            :     {
     905                 :         16 :       case Kind::STRING_REPLACE:
     906                 :            :       {
     907         [ +  + ]:         16 :         if (Word::isEmpty(n[0]))
     908                 :            :         {
     909                 :            :           // (str.replace "" x y) --> y
     910                 :          5 :           n = n[2];
     911                 :          5 :           break;
     912                 :            :         }
     913                 :         11 :         if (checkLengthOne(n[0]) && Word::isEmpty(n[2]))
     914                 :            :         {
     915                 :            :           // (str.replace "A" x "") --> "A"
     916                 :          7 :           res = n[0];
     917                 :          7 :           break;
     918                 :            :         }
     919                 :            : 
     920                 :          4 :         res = n;
     921                 :          4 :         break;
     922                 :            :       }
     923                 :         34 :       case Kind::STRING_SUBSTR:
     924                 :            :       {
     925         [ +  + ]:         34 :         if (checkLengthOne(n[0]))
     926                 :            :         {
     927                 :            :           // (str.substr "A" x y) --> "A"
     928                 :         12 :           res = n[0];
     929                 :         12 :           break;
     930                 :            :         }
     931                 :         22 :         res = n;
     932                 :         22 :         break;
     933                 :            :       }
     934                 :       1319 :       default:
     935                 :            :       {
     936                 :       1319 :         res = n;
     937                 :       1319 :         break;
     938                 :            :       }
     939                 :            :     }
     940                 :            :   }
     941                 :       1364 :   return res;
     942                 :          0 : }
     943                 :            : 
     944                 :       2098 : Node StringsEntail::inferEqsFromContains(Node x, Node y)
     945                 :            : {
     946                 :       2098 :   NodeManager* nm = x.getNodeManager();
     947                 :       2098 :   Node emp = Word::mkEmptyWord(x.getType());
     948 [ -  + ][ -  + ]:       6294 :   AssertEqual(x.getType(), y.getType());
                 [ -  - ]
     949                 :       2098 :   TypeNode stype = x.getType();
     950                 :            : 
     951                 :       2098 :   Node xLen = nm->mkNode(Kind::STRING_LENGTH, x);
     952                 :       2098 :   std::vector<Node> yLens;
     953         [ +  + ]:       2098 :   if (y.getKind() != Kind::STRING_CONCAT)
     954                 :            :   {
     955                 :         10 :     yLens.push_back(nm->mkNode(Kind::STRING_LENGTH, y));
     956                 :            :   }
     957                 :            :   else
     958                 :            :   {
     959         [ +  + ]:       7961 :     for (const Node& yi : y)
     960                 :            :     {
     961                 :       5873 :       yLens.push_back(nm->mkNode(Kind::STRING_LENGTH, yi));
     962                 :       5873 :     }
     963                 :            :   }
     964                 :            : 
     965                 :       2098 :   std::vector<Node> zeroLens;
     966         [ +  + ]:       2098 :   if (x == emp)
     967                 :            :   {
     968                 :            :     // If x is the empty string, then all ys must be empty, too, and we can
     969                 :            :     // skip the expensive checks. Note that this is just a performance
     970                 :            :     // optimization.
     971                 :        199 :     zeroLens.swap(yLens);
     972                 :            :   }
     973                 :            :   else
     974                 :            :   {
     975                 :            :     // Check if we can infer that str.len(x) <= str.len(y). If that is the
     976                 :            :     // case, try to minimize the sum in str.len(x) <= str.len(y1) + ... +
     977                 :            :     // str.len(yn) (where y = y1 ++ ... ++ yn) while keeping the inequality
     978                 :            :     // true. The terms that can have length zero without making the inequality
     979                 :            :     // false must be all be empty if (str.contains x y) is true.
     980         [ +  + ]:       1899 :     if (!d_arithEntail.inferZerosInSumGeq(xLen, yLens, zeroLens))
     981                 :            :     {
     982                 :            :       // We could not prove that the inequality holds
     983                 :       1707 :       return Node::null();
     984                 :            :     }
     985         [ +  + ]:        192 :     else if (yLens.size() == y.getNumChildren())
     986                 :            :     {
     987                 :            :       // We could only prove that the inequality holds but not that any of the
     988                 :            :       // ys must be empty
     989                 :        136 :       return nm->mkNode(Kind::EQUAL, x, y);
     990                 :            :     }
     991                 :            :   }
     992                 :            : 
     993         [ +  + ]:        255 :   if (y.getKind() != Kind::STRING_CONCAT)
     994                 :            :   {
     995         [ -  + ]:          3 :     if (zeroLens.size() == 1)
     996                 :            :     {
     997                 :            :       // y is not a concatenation and we found that it must be empty, so just
     998                 :            :       // return (= y "")
     999                 :          0 :       Assert(zeroLens[0][0] == y);
    1000                 :          0 :       return nm->mkNode(Kind::EQUAL, y, emp);
    1001                 :            :     }
    1002                 :            :     else
    1003                 :            :     {
    1004                 :          3 :       Assert(yLens.size() == 1 && yLens[0][0] == y);
    1005                 :          3 :       return nm->mkNode(Kind::EQUAL, x, y);
    1006                 :            :     }
    1007                 :            :   }
    1008                 :            : 
    1009                 :        252 :   std::vector<Node> cs;
    1010         [ +  + ]:        324 :   for (const Node& yiLen : yLens)
    1011                 :            :   {
    1012 [ -  + ][ -  + ]:         72 :     Assert(std::find(y.begin(), y.end(), yiLen[0]) != y.end());
                 [ -  - ]
    1013                 :         72 :     cs.push_back(yiLen[0]);
    1014                 :            :   }
    1015                 :            : 
    1016                 :            :   // (= x (str.++ y1' ... ym'))
    1017                 :        252 :   std::vector<Node> eqs;
    1018                 :        504 :   Node mainEq = nm->mkNode(Kind::EQUAL, x, utils::mkConcat(cs, stype));
    1019                 :        252 :   eqs.push_back(mainEq);
    1020                 :            :   // (= y1'' "") ... (= yk'' "")
    1021         [ +  + ]:        871 :   for (const Node& zeroLen : zeroLens)
    1022                 :            :   {
    1023 [ -  + ][ -  + ]:        619 :     Assert(std::find(y.begin(), y.end(), zeroLen[0]) != y.end());
                 [ -  - ]
    1024                 :        619 :     eqs.push_back(nm->mkNode(Kind::EQUAL, zeroLen[0], emp));
    1025                 :            :   }
    1026                 :            :   // (and (= x (str.++ y1' ... ym')) (= y1'' "") ... (= yk'' ""))
    1027                 :        252 :   return nm->mkAnd(eqs);
    1028                 :       2098 : }
    1029                 :            : 
    1030                 :     111827 : Node StringsEntail::rewriteViaMacroSubstrStripSymLength(const Node& node,
    1031                 :            :                                                         Rewrite& rule,
    1032                 :            :                                                         std::vector<Node>& ch1,
    1033                 :            :                                                         std::vector<Node>& ch2)
    1034                 :            : {
    1035 [ -  + ][ -  + ]:     111827 :   Assert(node.getKind() == Kind::STRING_SUBSTR);
                 [ -  - ]
    1036                 :     111827 :   NodeManager* nm = node.getNodeManager();
    1037                 :     111827 :   utils::getConcat(node[0], ch1);
    1038                 :     111827 :   TypeNode stype = node[0].getType();
    1039                 :     111827 :   Node zero = nm->mkConstInt(Rational(0));
    1040                 :            :   // definite inclusion
    1041         [ +  + ]:     111827 :   if (node[1] == zero)
    1042                 :            :   {
    1043                 :      49151 :     Node curr = node[2];
    1044         [ +  + ]:      49151 :     if (stripSymbolicLength(ch1, ch2, 1, curr))
    1045                 :            :     {
    1046                 :       1906 :       std::vector<Node> chr = ch2;
    1047 [ +  + ][ +  + ]:       1906 :       if (curr != zero && !ch1.empty())
                 [ +  + ]
    1048                 :            :       {
    1049                 :            :         // make the length explicitly, which helps proof reconstruction
    1050                 :        377 :         Node cpulled = utils::mkConcat(ch2, stype);
    1051                 :            :         Node resultLen = nm->mkNode(
    1052                 :        754 :             Kind::SUB, node[2], nm->mkNode(Kind::STRING_LENGTH, cpulled));
    1053                 :       1131 :         chr.push_back(nm->mkNode(Kind::STRING_SUBSTR,
    1054                 :        754 :                                  utils::mkConcat(ch1, stype),
    1055                 :            :                                  node[1],
    1056                 :            :                                  resultLen));
    1057                 :        377 :       }
    1058                 :       1906 :       Node ret = utils::mkConcat(chr, stype);
    1059                 :       1906 :       rule = Rewrite::SS_LEN_INCLUDE;
    1060                 :       1906 :       return ret;
    1061                 :       1906 :     }
    1062         [ +  + ]:      49151 :   }
    1063         [ +  + ]:     324424 :   for (unsigned r = 0; r < 2; r++)
    1064                 :            :   {
    1065                 :            :     // the amount of characters we can strip
    1066                 :     217398 :     Node curr;
    1067         [ +  + ]:     217398 :     if (r == 0)
    1068                 :            :     {
    1069         [ +  + ]:     109921 :       if (node[1] != zero)
    1070                 :            :       {
    1071                 :            :         // strip up to start point off the start of the string
    1072                 :      62676 :         curr = node[1];
    1073                 :            :       }
    1074                 :            :     }
    1075         [ +  - ]:     107477 :     else if (r == 1)
    1076                 :            :     {
    1077                 :     214954 :       Node tot_len = nm->mkNode(Kind::STRING_LENGTH, node[0]);
    1078         [ +  + ]:     107477 :       if (node[2] != tot_len)
    1079                 :            :       {
    1080                 :     210894 :         Node end_pt = nm->mkNode(Kind::ADD, node[1], node[2]);
    1081                 :            :         // strip up to ( str.len(node[0]) - end_pt ) off the end of the string
    1082                 :     105447 :         curr = nm->mkNode(Kind::SUB, tot_len, end_pt);
    1083                 :     105447 :         curr = d_arithEntail.rewriteArith(curr);
    1084                 :     105447 :       }
    1085                 :     107477 :     }
    1086         [ +  + ]:     217398 :     if (!curr.isNull())
    1087                 :            :     {
    1088                 :            :       // strip off components while quantity is entailed positive
    1089         [ +  + ]:     168123 :       int dir = r == 0 ? 1 : -1;
    1090                 :     168123 :       ch2.clear();
    1091         [ +  + ]:     168123 :       if (stripSymbolicLength(ch1, ch2, dir, curr))
    1092                 :            :       {
    1093         [ +  + ]:       2895 :         if (r == 0)
    1094                 :            :         {
    1095                 :            :           // make the length explicitly, which helps proof reconstruction
    1096                 :       2444 :           Node cskipped = utils::mkConcat(ch2, stype);
    1097                 :            :           Node resultStart = nm->mkNode(
    1098                 :       4888 :               Kind::SUB, node[1], nm->mkNode(Kind::STRING_LENGTH, cskipped));
    1099                 :            :           Node ret = nm->mkNode(Kind::STRING_SUBSTR,
    1100                 :       4888 :                                 utils::mkConcat(ch1, stype),
    1101                 :            :                                 resultStart,
    1102                 :       9776 :                                 node[2]);
    1103                 :       2444 :           rule = Rewrite::SS_STRIP_START_PT;
    1104                 :       2444 :           return ret;
    1105                 :       2444 :         }
    1106                 :            :         else
    1107                 :            :         {
    1108                 :            :           Node ret = nm->mkNode(Kind::STRING_SUBSTR,
    1109                 :        902 :                                 utils::mkConcat(ch1, stype),
    1110                 :            :                                 node[1],
    1111                 :       1804 :                                 node[2]);
    1112                 :        451 :           rule = Rewrite::SS_STRIP_END_PT;
    1113                 :        451 :           return ret;
    1114                 :        451 :         }
    1115                 :            :       }
    1116                 :            :     }
    1117         [ +  + ]:     217398 :   }
    1118                 :            : 
    1119                 :     107026 :   return Node::null();
    1120                 :     111827 : }
    1121                 :            : 
    1122                 :            : }  // namespace strings
    1123                 :            : }  // namespace theory
    1124                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14