LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/strings - regexp_entail.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 521 577 90.3 %
Date: 2026-03-03 11:42:59 Functions: 13 13 100.0 %
Branches: 465 606 76.7 %

           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 regular expressions.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "theory/strings/regexp_entail.h"
      14                 :            : 
      15                 :            : #include "theory/rewriter.h"
      16                 :            : #include "theory/strings/regexp_eval.h"
      17                 :            : #include "theory/strings/theory_strings_utils.h"
      18                 :            : #include "theory/strings/word.h"
      19                 :            : #include "util/rational.h"
      20                 :            : #include "util/string.h"
      21                 :            : 
      22                 :            : using namespace std;
      23                 :            : using namespace cvc5::internal::kind;
      24                 :            : 
      25                 :            : namespace cvc5::internal {
      26                 :            : namespace theory {
      27                 :            : namespace strings {
      28                 :            : 
      29                 :     103886 : RegExpEntail::RegExpEntail(NodeManager* nm, Rewriter* r) : d_aent(nm, r)
      30                 :            : {
      31                 :     103886 :   d_zero = nm->mkConstInt(Rational(0));
      32                 :     103886 :   d_one = nm->mkConstInt(Rational(1));
      33                 :     103886 : }
      34                 :            : 
      35                 :      32768 : Node RegExpEntail::simpleRegexpConsume(NodeManager* nm,
      36                 :            :                                        std::vector<Node>& mchildren,
      37                 :            :                                        std::vector<Node>& children,
      38                 :            :                                        int dir)
      39                 :            : {
      40         [ +  - ]:      65536 :   Trace("regexp-ext-rewrite-debug")
      41                 :      32768 :       << "Simple reg exp consume, dir=" << dir << ":" << std::endl;
      42                 :      32768 :   unsigned tmin = dir < 0 ? 0 : dir;
      43         [ +  + ]:      32768 :   unsigned tmax = dir < 0 ? 1 : dir;
      44                 :            :   // try to remove off front and back
      45         [ +  + ]:      89187 :   for (unsigned t = 0; t < 2; t++)
      46                 :            :   {
      47 [ +  + ][ +  + ]:      62415 :     if (tmin <= t && t <= tmax)
      48                 :            :     {
      49         [ +  - ]:      82540 :       Trace("regexp-ext-rewrite-debug")
      50                 :      41270 :           << "Run consume, direction is " << t << " with:" << std::endl;
      51         [ +  - ]:      82540 :       Trace("regexp-ext-rewrite-debug")
      52                 :      41270 :           << "  mchildren : " << mchildren << std::endl;
      53         [ +  - ]:      82540 :       Trace("regexp-ext-rewrite-debug")
      54                 :      41270 :           << "  children : " << children << std::endl;
      55                 :      41270 :       bool do_next = true;
      56 [ +  + ][ +  + ]:      80109 :       while (!children.empty() && !mchildren.empty() && do_next)
         [ +  + ][ +  + ]
      57                 :            :       {
      58                 :      44843 :         do_next = false;
      59                 :      44843 :         Node xc = mchildren[mchildren.size() - 1];
      60                 :      44843 :         Node rc = children[children.size() - 1];
      61         [ +  - ]:      89686 :         Trace("regexp-ext-rewrite-debug")
      62                 :      44843 :             << "* " << xc << " in " << rc << std::endl;
      63 [ -  + ][ -  + ]:      44843 :         Assert(rc.getKind() != Kind::REGEXP_CONCAT);
                 [ -  - ]
      64 [ -  + ][ -  + ]:      44843 :         Assert(xc.getKind() != Kind::STRING_CONCAT);
                 [ -  - ]
      65         [ +  + ]:      44843 :         if (rc.getKind() == Kind::STRING_TO_REGEXP)
      66                 :            :         {
      67                 :      19176 :           std::vector<Node> childrenc;
      68                 :      19176 :           utils::getConcat(rc[0], childrenc);
      69         [ +  + ]:      19176 :           size_t cindex = t == 1 ? 0 : childrenc.size() - 1;
      70                 :      19176 :           Node rcc = childrenc[cindex];
      71                 :      19176 :           Node remStr;
      72         [ +  + ]:      19176 :           if (xc == rcc)
      73                 :            :           {
      74                 :       1970 :             mchildren.pop_back();
      75                 :       1970 :             do_next = true;
      76         [ +  - ]:       1970 :             Trace("regexp-ext-rewrite-debug") << "- strip equal" << std::endl;
      77                 :            :           }
      78 [ +  + ][ +  + ]:      17206 :           else if (rcc.isConst() && Word::isEmpty(rcc))
         [ +  + ][ +  + ]
                 [ -  - ]
      79                 :            :           {
      80         [ +  - ]:        816 :             Trace("regexp-ext-rewrite-debug")
      81                 :        408 :                 << "- ignore empty RE" << std::endl;
      82                 :            :             // ignore and continue
      83                 :        408 :             do_next = true;
      84                 :            :           }
      85 [ +  + ][ +  + ]:      16798 :           else if (xc.isConst() && rcc.isConst())
                 [ +  + ]
      86                 :            :           {
      87                 :            :             // split the constant
      88                 :            :             size_t index;
      89                 :       6461 :             remStr = Word::splitConstant(xc, rcc, index, t == 0);
      90         [ +  - ]:      12922 :             Trace("regexp-ext-rewrite-debug")
      91                 :          0 :                 << "- CRE: Regexp const split : " << xc << " " << rcc << " -> "
      92                 :       6461 :                 << remStr << " " << index << " " << t << std::endl;
      93         [ +  + ]:       6461 :             if (remStr.isNull())
      94                 :            :             {
      95         [ +  - ]:      10182 :               Trace("regexp-ext-rewrite-debug")
      96                 :       5091 :                   << "...return false" << std::endl;
      97                 :      10182 :               return nm->mkConst(false);
      98                 :            :             }
      99                 :            :             else
     100                 :            :             {
     101         [ +  - ]:       2740 :               Trace("regexp-ext-rewrite-debug")
     102                 :       1370 :                   << "- strip equal const" << std::endl;
     103                 :       1370 :               mchildren.pop_back();
     104         [ +  + ]:       1370 :               if (index == 0)
     105                 :            :               {
     106                 :       1142 :                 mchildren.push_back(remStr);
     107                 :            :                 // we've processed the remainder as leftover for the LHS
     108                 :            :                 // string, clear it now
     109                 :       1142 :                 remStr = Node::null();
     110                 :            :               }
     111                 :            :               // otherwise remStr is processed below
     112                 :            :             }
     113         [ +  - ]:       1370 :             Trace("regexp-ext-rewrite-debug") << "- split const" << std::endl;
     114                 :       1370 :             do_next = true;
     115                 :            :           }
     116         [ +  + ]:      14085 :           if (do_next)
     117                 :            :           {
     118         [ +  + ]:       3748 :             if (remStr.isNull())
     119                 :            :             {
     120                 :            :               // we have fully processed the component
     121                 :       3520 :               childrenc.erase(childrenc.begin() + cindex);
     122                 :            :             }
     123                 :            :             else
     124                 :            :             {
     125                 :            :               // we have a remainder
     126                 :        228 :               childrenc[cindex] = remStr;
     127                 :            :             }
     128         [ +  + ]:       3748 :             if (childrenc.empty())
     129                 :            :             {
     130                 :            :               // if childrenc is empty, we are done with the current str.to_re
     131                 :       3449 :               children.pop_back();
     132                 :            :             }
     133                 :            :             else
     134                 :            :             {
     135                 :            :               // otherwise we reconstruct it
     136                 :        299 :               TypeNode stype = nm->stringType();
     137                 :        598 :               children[children.size() - 1] = nm->mkNode(
     138                 :        897 :                   Kind::STRING_TO_REGEXP, utils::mkConcat(childrenc, stype));
     139                 :        299 :             }
     140                 :            :           }
     141 [ +  + ][ +  + ]:      29358 :         }
                 [ +  + ]
     142         [ +  + ]:      25667 :         else if (xc.isConst())
     143                 :            :         {
     144                 :            :           // check for constants
     145                 :       8683 :           cvc5::internal::String s = xc.getConst<String>();
     146         [ +  + ]:       8683 :           if (Word::isEmpty(xc))
     147                 :            :           {
     148         [ +  - ]:        454 :             Trace("regexp-ext-rewrite-debug") << "- ignore empty" << std::endl;
     149                 :            :             // ignore and continue
     150                 :        454 :             mchildren.pop_back();
     151                 :        454 :             do_next = true;
     152                 :            :           }
     153                 :       8229 :           else if (rc.getKind() == Kind::REGEXP_RANGE
     154 [ +  + ][ +  + ]:       8229 :                    || rc.getKind() == Kind::REGEXP_ALLCHAR)
                 [ +  + ]
     155                 :            :           {
     156         [ +  + ]:       1440 :             if (!isConstRegExp(rc))
     157                 :            :             {
     158                 :            :               // if a non-standard re.range term, abort
     159                 :          8 :               break;
     160                 :            :             }
     161                 :       1432 :             std::vector<unsigned> ssVec;
     162         [ +  + ]:       1432 :             ssVec.push_back(t == 0 ? s.back() : s.front());
     163                 :       1432 :             cvc5::internal::String ss(ssVec);
     164         [ +  + ]:       1432 :             if (testConstStringInRegExp(ss, rc))
     165                 :            :             {
     166                 :            :               // strip off one character
     167                 :        968 :               mchildren.pop_back();
     168         [ +  + ]:        968 :               if (s.size() > 1)
     169                 :            :               {
     170         [ +  + ]:        721 :                 if (t == 0)
     171                 :            :                 {
     172                 :        472 :                   mchildren.push_back(nm->mkConst(s.substr(0, s.size() - 1)));
     173                 :            :                 }
     174                 :            :                 else
     175                 :            :                 {
     176                 :        249 :                   mchildren.push_back(nm->mkConst(s.substr(1)));
     177                 :            :                 }
     178                 :            :               }
     179                 :        968 :               children.pop_back();
     180                 :        968 :               do_next = true;
     181                 :            :             }
     182                 :            :             else
     183                 :            :             {
     184         [ +  - ]:        928 :               Trace("regexp-ext-rewrite-debug")
     185                 :        464 :                   << "...return false" << std::endl;
     186                 :        928 :               return nm->mkConst(false);
     187                 :            :             }
     188 [ +  + ][ +  + ]:       1896 :           }
     189                 :       6789 :           else if (rc.getKind() == Kind::REGEXP_INTER
     190 [ +  + ][ +  + ]:       6789 :                    || rc.getKind() == Kind::REGEXP_UNION)
                 [ +  + ]
     191                 :            :           {
     192                 :            :             // see if any/each child does not work
     193                 :       2416 :             bool result_valid = true;
     194                 :       2416 :             Node result;
     195                 :       2416 :             Node emp_s = nm->mkConst(String(""));
     196         [ +  + ]:       8007 :             for (unsigned i = 0; i < rc.getNumChildren(); i++)
     197                 :            :             {
     198                 :       5591 :               std::vector<Node> mchildren_s;
     199                 :       5591 :               std::vector<Node> children_s;
     200                 :       5591 :               mchildren_s.push_back(xc);
     201                 :       5591 :               utils::getConcat(rc[i], children_s);
     202         [ +  - ]:       5591 :               Trace("regexp-ext-rewrite-debug") << push;
     203                 :       5591 :               Node ret = simpleRegexpConsume(nm, mchildren_s, children_s, t);
     204         [ +  - ]:       5591 :               Trace("regexp-ext-rewrite-debug") << pop;
     205         [ +  + ]:       5591 :               if (!ret.isNull())
     206                 :            :               {
     207                 :            :                 // one conjunct cannot be satisfied, return false
     208         [ -  + ]:       2991 :                 if (rc.getKind() == Kind::REGEXP_INTER)
     209                 :            :                 {
     210         [ -  - ]:          0 :                   Trace("regexp-ext-rewrite-debug")
     211                 :          0 :                       << "...return " << ret << std::endl;
     212                 :          0 :                   return ret;
     213                 :            :                 }
     214                 :            :               }
     215                 :            :               else
     216                 :            :               {
     217         [ +  + ]:       2600 :                 if (children_s.empty())
     218                 :            :                 {
     219                 :            :                   // if we were able to fully consume, store the result
     220 [ -  + ][ -  + ]:       1706 :                   Assert(mchildren_s.size() <= 1);
                 [ -  - ]
     221         [ +  + ]:       1706 :                   if (mchildren_s.empty())
     222                 :            :                   {
     223                 :        306 :                     mchildren_s.push_back(emp_s);
     224                 :            :                   }
     225         [ +  + ]:       1706 :                   if (result.isNull())
     226                 :            :                   {
     227                 :       1404 :                     result = mchildren_s[0];
     228                 :            :                   }
     229         [ +  + ]:        302 :                   else if (result != mchildren_s[0])
     230                 :            :                   {
     231                 :         22 :                     result_valid = false;
     232                 :            :                   }
     233                 :            :                 }
     234                 :            :                 else
     235                 :            :                 {
     236                 :        894 :                   result_valid = false;
     237                 :            :                 }
     238                 :            :               }
     239 [ +  - ][ +  - ]:       5591 :             }
                 [ +  - ]
     240         [ +  + ]:       2416 :             if (result_valid)
     241                 :            :             {
     242         [ +  + ]:       1699 :               if (result.isNull())
     243                 :            :               {
     244                 :            :                 // all disjuncts cannot be satisfied, return false
     245 [ -  + ][ -  + ]:        441 :                 Assert(rc.getKind() == Kind::REGEXP_UNION);
                 [ -  - ]
     246         [ +  - ]:        882 :                 Trace("regexp-ext-rewrite-debug")
     247                 :        441 :                     << "...return false" << std::endl;
     248                 :        882 :                 return nm->mkConst(false);
     249                 :            :               }
     250                 :            :               else
     251                 :            :               {
     252         [ +  - ]:       2516 :                 Trace("regexp-ext-rewrite-debug")
     253                 :          0 :                     << "- same result, try again, children now " << children
     254                 :       1258 :                     << std::endl;
     255                 :            :                 // all branches led to the same result
     256                 :       1258 :                 children.pop_back();
     257                 :       1258 :                 mchildren.pop_back();
     258         [ +  + ]:       1258 :                 if (result != emp_s)
     259                 :            :                 {
     260                 :        978 :                   mchildren.push_back(result);
     261                 :            :                 }
     262                 :       1258 :                 do_next = true;
     263                 :            :               }
     264                 :            :             }
     265 [ +  + ][ +  + ]:       2857 :           }
     266         [ +  + ]:       4373 :           else if (rc.getKind() == Kind::REGEXP_STAR)
     267                 :            :           {
     268                 :            :             // check if there is no way that this star can be unrolled even once
     269                 :       4173 :             std::vector<Node> mchildren_s;
     270                 :       8346 :             mchildren_s.insert(
     271                 :       4173 :                 mchildren_s.end(), mchildren.begin(), mchildren.end());
     272         [ +  + ]:       4173 :             if (t == 1)
     273                 :            :             {
     274                 :       1744 :               std::reverse(mchildren_s.begin(), mchildren_s.end());
     275                 :            :             }
     276                 :       4173 :             std::vector<Node> children_s;
     277                 :       4173 :             utils::getConcat(rc[0], children_s);
     278         [ +  - ]:       8346 :             Trace("regexp-ext-rewrite-debug")
     279                 :       4173 :                 << "- recursive call on body of star" << std::endl;
     280         [ +  - ]:       4173 :             Trace("regexp-ext-rewrite-debug") << push;
     281                 :       4173 :             Node ret = simpleRegexpConsume(nm, mchildren_s, children_s, t);
     282         [ +  - ]:       4173 :             Trace("regexp-ext-rewrite-debug") << pop;
     283         [ +  + ]:       4173 :             if (!ret.isNull())
     284                 :            :             {
     285         [ +  - ]:       3732 :               Trace("regexp-ext-rewrite-debug")
     286                 :          0 :                   << "- CRE : regexp star infeasable " << xc << " " << rc
     287                 :       1866 :                   << std::endl;
     288                 :       1866 :               children.pop_back();
     289         [ +  + ]:       1866 :               if (!children.empty())
     290                 :            :               {
     291         [ +  - ]:       1219 :                 Trace("regexp-ext-rewrite-debug") << "- continue" << std::endl;
     292                 :       1219 :                 do_next = true;
     293                 :            :               }
     294                 :            :             }
     295                 :            :             else
     296                 :            :             {
     297         [ +  + ]:       2307 :               if (children_s.empty())
     298                 :            :               {
     299                 :            :                 // Check if beyond this, we hit a conflict. In this case, we
     300                 :            :                 // must repeat.  Notice that we do not treat the case where
     301                 :            :                 // there are no more strings to consume as a failure, since
     302                 :            :                 // we may be within a recursive call, see issue #5510.
     303                 :       1800 :                 bool can_skip = true;
     304         [ +  + ]:       1800 :                 if (children.size() > 1)
     305                 :            :                 {
     306                 :       1426 :                   std::vector<Node> mchildren_ss;
     307                 :       2852 :                   mchildren_ss.insert(
     308                 :       1426 :                       mchildren_ss.end(), mchildren.begin(), mchildren.end());
     309                 :       1426 :                   std::vector<Node> children_ss;
     310                 :       2852 :                   children_ss.insert(
     311                 :       2852 :                       children_ss.end(), children.begin(), children.end() - 1);
     312         [ +  + ]:       1426 :                   if (t == 1)
     313                 :            :                   {
     314                 :        561 :                     std::reverse(mchildren_ss.begin(), mchildren_ss.end());
     315                 :        561 :                     std::reverse(children_ss.begin(), children_ss.end());
     316                 :            :                   }
     317         [ +  - ]:       2852 :                   Trace("regexp-ext-rewrite-debug")
     318                 :       1426 :                       << "- recursive call required repeat star" << std::endl;
     319         [ +  - ]:       1426 :                   Trace("regexp-ext-rewrite-debug") << push;
     320                 :            :                   Node rets =
     321                 :       1426 :                       simpleRegexpConsume(nm, mchildren_ss, children_ss, t);
     322         [ +  - ]:       1426 :                   Trace("regexp-ext-rewrite-debug") << pop;
     323         [ +  + ]:       1426 :                   if (!rets.isNull())
     324                 :            :                   {
     325                 :        702 :                     can_skip = false;
     326                 :            :                   }
     327                 :       1426 :                 }
     328         [ +  + ]:       1800 :                 if (!can_skip)
     329                 :            :                 {
     330                 :        702 :                   TypeNode stype = nm->stringType();
     331                 :        702 :                   Node prev = utils::mkConcat(mchildren, stype);
     332         [ +  - ]:       1404 :                   Trace("regexp-ext-rewrite-debug")
     333                 :        702 :                       << "- can't skip" << std::endl;
     334                 :            :                   // take the result of fully consuming once
     335         [ +  + ]:        702 :                   if (t == 1)
     336                 :            :                   {
     337                 :        353 :                     std::reverse(mchildren_s.begin(), mchildren_s.end());
     338                 :            :                   }
     339                 :        702 :                   mchildren.clear();
     340                 :       1404 :                   mchildren.insert(
     341                 :        702 :                       mchildren.end(), mchildren_s.begin(), mchildren_s.end());
     342                 :        702 :                   Node curr = utils::mkConcat(mchildren, stype);
     343                 :        702 :                   do_next = (prev != curr);
     344         [ +  - ]:       1404 :                   Trace("regexp-ext-rewrite-debug")
     345                 :        702 :                       << "- do_next = " << do_next << std::endl;
     346                 :        702 :                 }
     347                 :            :                 else
     348                 :            :                 {
     349         [ +  - ]:       2196 :                   Trace("regexp-ext-rewrite-debug")
     350                 :       1098 :                       << "- can skip " << rc << " from " << xc << std::endl;
     351                 :            :                 }
     352                 :            :               }
     353                 :            :             }
     354                 :       4173 :           }
     355    [ +  + ][ + ]:       8683 :         }
     356         [ +  + ]:      38839 :         if (!do_next)
     357                 :            :         {
     358         [ +  - ]:      60980 :           Trace("regexp-ext-rewrite")
     359                 :      30490 :               << "- cannot consume : " << xc << " " << rc << std::endl;
     360                 :            :         }
     361 [ +  + ][ +  + ]:      50847 :       }
                 [ +  + ]
     362                 :            :     }
     363         [ +  + ]:      56419 :     if (dir != 0)
     364                 :            :     {
     365                 :      38011 :       std::reverse(children.begin(), children.end());
     366                 :      38011 :       std::reverse(mchildren.begin(), mchildren.end());
     367                 :            :     }
     368                 :            :   }
     369         [ +  - ]:      26772 :   Trace("regexp-ext-rewrite-debug") << "...finished, return null" << std::endl;
     370                 :      26772 :   return Node::null();
     371                 :            : }
     372                 :            : 
     373                 :      24156 : bool RegExpEntail::isConstRegExp(TNode t)
     374                 :            : {
     375                 :      24156 :   std::unordered_set<TNode> visited;
     376                 :      24156 :   std::vector<TNode> visit;
     377                 :      24156 :   TNode cur;
     378                 :      24156 :   visit.push_back(t);
     379                 :            :   do
     380                 :            :   {
     381                 :      74455 :     cur = visit.back();
     382                 :      74455 :     visit.pop_back();
     383         [ +  + ]:      74455 :     if (visited.find(cur) == visited.end())
     384                 :            :     {
     385                 :      61226 :       visited.insert(cur);
     386                 :      61226 :       Kind ck = cur.getKind();
     387         [ +  + ]:      61226 :       if (ck == Kind::STRING_TO_REGEXP)
     388                 :            :       {
     389         [ +  + ]:      23592 :         if (!cur[0].isConst())
     390                 :            :         {
     391                 :       2710 :           return false;
     392                 :            :         }
     393                 :            :       }
     394         [ -  + ]:      37634 :       else if (ck == Kind::REGEXP_RV)
     395                 :            :       {
     396                 :          0 :         return false;
     397                 :            :       }
     398         [ +  + ]:      37634 :       else if (ck == Kind::REGEXP_RANGE)
     399                 :            :       {
     400         [ +  + ]:       4593 :         if (!utils::isCharacterRange(cur))
     401                 :            :         {
     402                 :         16 :           return false;
     403                 :            :         }
     404                 :            :       }
     405         [ +  + ]:      33041 :       else if (!utils::isRegExpKind(ck))
     406                 :            :       {
     407                 :        326 :         return false;
     408                 :            :       }
     409                 :            :       else
     410                 :            :       {
     411         [ +  + ]:      85079 :         for (const Node& cn : cur)
     412                 :            :         {
     413                 :      52364 :           visit.push_back(cn);
     414                 :      52364 :         }
     415                 :            :       }
     416                 :            :     }
     417         [ +  + ]:      71403 :   } while (!visit.empty());
     418                 :      21104 :   return true;
     419                 :      24156 : }
     420                 :            : 
     421                 :      19814 : bool RegExpEntail::testConstStringInRegExp(String& s, TNode r)
     422                 :            : {
     423                 :      19814 :   Kind k = r.getKind();
     424 [ +  + ][ +  + ]:      19814 :   if (k == Kind::REGEXP_CONCAT || k == Kind::REGEXP_STAR
     425         [ +  + ]:       7833 :       || k == Kind::REGEXP_UNION)
     426                 :            :   {
     427                 :            :     // If we can evaluate it via NFA construction, do so. We only do this
     428                 :            :     // for compound regular expressions (re.++, re.*, re.union) which may
     429                 :            :     // have non-trivial NFA constructions, otherwise the check below will
     430                 :            :     // be simpler.
     431         [ +  + ]:      12724 :     if (RegExpEval::canEvaluate(r))
     432                 :            :     {
     433                 :      11631 :       return RegExpEval::evaluate(s, r);
     434                 :            :     }
     435                 :            :   }
     436                 :       8183 :   return testConstStringInRegExpInternal(s, 0, r);
     437                 :            : }
     438                 :            : 
     439                 :      47970 : bool RegExpEntail::testConstStringInRegExpInternal(String& s,
     440                 :            :                                                    unsigned index_start,
     441                 :            :                                                    TNode r)
     442                 :            : {
     443 [ -  + ][ -  + ]:      47970 :   Assert(index_start <= s.size());
                 [ -  - ]
     444         [ +  - ]:      95940 :   Trace("regexp-debug") << "Checking " << s << " in " << r << ", starting at "
     445                 :      47970 :                         << index_start << std::endl;
     446 [ -  + ][ -  + ]:      47970 :   Assert(!r.isVar());
                 [ -  - ]
     447                 :      47970 :   Kind k = r.getKind();
     448 [ +  + ][ +  + ]:      47970 :   switch (k)
         [ +  + ][ +  + ]
            [ -  + ][ - ]
     449                 :            :   {
     450                 :      19229 :     case Kind::STRING_TO_REGEXP:
     451                 :            :     {
     452                 :      19229 :       String s2 = s.substr(index_start, s.size() - index_start);
     453         [ +  - ]:      19229 :       if (r[0].isConst())
     454                 :            :       {
     455                 :      19229 :         return (s2 == r[0].getConst<String>());
     456                 :            :       }
     457                 :          0 :       DebugUnhandled() << "RegExp contains variables";
     458                 :            :       return false;
     459                 :      19229 :     }
     460                 :       3427 :     case Kind::REGEXP_CONCAT:
     461                 :            :     {
     462         [ +  + ]:       3427 :       if (s.size() != index_start)
     463                 :            :       {
     464                 :       2711 :         std::vector<int> vec_k(r.getNumChildren(), -1);
     465                 :       2711 :         int start = 0;
     466                 :       2711 :         int left = (int)s.size() - index_start;
     467                 :       2711 :         int i = 0;
     468         [ +  - ]:       8663 :         while (i < (int)r.getNumChildren())
     469                 :            :         {
     470                 :       8663 :           bool flag = true;
     471         [ +  + ]:       8663 :           if (i == (int)r.getNumChildren() - 1)
     472                 :            :           {
     473         [ +  + ]:       1043 :             if (testConstStringInRegExpInternal(s, index_start + start, r[i]))
     474                 :            :             {
     475                 :        818 :               return true;
     476                 :            :             }
     477                 :            :           }
     478         [ +  + ]:       7620 :           else if (i == -1)
     479                 :            :           {
     480                 :       1893 :             return false;
     481                 :            :           }
     482                 :            :           else
     483                 :            :           {
     484         [ +  + ]:      20020 :             for (vec_k[i] = vec_k[i] + 1; vec_k[i] <= left; ++vec_k[i])
     485                 :            :             {
     486                 :      16766 :               cvc5::internal::String t = s.substr(index_start + start, vec_k[i]);
     487         [ +  + ]:      16766 :               if (testConstStringInRegExpInternal(t, 0, r[i]))
     488                 :            :               {
     489                 :       2473 :                 start += vec_k[i];
     490                 :       2473 :                 left -= vec_k[i];
     491                 :       2473 :                 flag = false;
     492                 :       2473 :                 ++i;
     493                 :       2473 :                 vec_k[i] = -1;
     494                 :       2473 :                 break;
     495                 :            :               }
     496         [ +  + ]:      16766 :             }
     497                 :            :           }
     498                 :            : 
     499         [ +  + ]:       5952 :           if (flag)
     500                 :            :           {
     501                 :       3479 :             --i;
     502         [ +  + ]:       3479 :             if (i >= 0)
     503                 :            :             {
     504                 :       1586 :               start -= vec_k[i];
     505                 :       1586 :               left += vec_k[i];
     506                 :            :             }
     507                 :            :           }
     508                 :            :         }
     509                 :          0 :         return false;
     510                 :       2711 :       }
     511                 :            :       else
     512                 :            :       {
     513         [ +  - ]:        998 :         for (unsigned i = 0; i < r.getNumChildren(); ++i)
     514                 :            :         {
     515         [ +  + ]:        998 :           if (!testConstStringInRegExpInternal(s, index_start, r[i]))
     516                 :            :           {
     517                 :        716 :             return false;
     518                 :            :           }
     519                 :            :         }
     520                 :          0 :         return true;
     521                 :            :       }
     522                 :            :     }
     523                 :         12 :     case Kind::REGEXP_UNION:
     524                 :            :     {
     525         [ +  + ]:         33 :       for (unsigned i = 0; i < r.getNumChildren(); ++i)
     526                 :            :       {
     527         [ +  + ]:         24 :         if (testConstStringInRegExpInternal(s, index_start, r[i]))
     528                 :            :         {
     529                 :          3 :           return true;
     530                 :            :         }
     531                 :            :       }
     532                 :          9 :       return false;
     533                 :            :     }
     534                 :       4858 :     case Kind::REGEXP_INTER:
     535                 :            :     {
     536         [ +  + ]:       6786 :       for (unsigned i = 0; i < r.getNumChildren(); ++i)
     537                 :            :       {
     538         [ +  + ]:       6047 :         if (!testConstStringInRegExpInternal(s, index_start, r[i]))
     539                 :            :         {
     540                 :       4119 :           return false;
     541                 :            :         }
     542                 :            :       }
     543                 :        739 :       return true;
     544                 :            :     }
     545                 :       5663 :     case Kind::REGEXP_STAR:
     546                 :            :     {
     547         [ +  + ]:       5663 :       if (s.size() != index_start)
     548                 :            :       {
     549         [ +  + ]:      10896 :         for (unsigned i = s.size() - index_start; i > 0; --i)
     550                 :            :         {
     551                 :      10821 :           cvc5::internal::String t = s.substr(index_start, i);
     552         [ +  + ]:      10821 :           if (testConstStringInRegExpInternal(t, 0, r[0]))
     553                 :            :           {
     554                 :       8274 :             if (index_start + i == s.size()
     555 [ +  + ][ +  - ]:       4137 :                 || testConstStringInRegExpInternal(s, index_start + i, r))
         [ +  + ][ +  - ]
                 [ -  - ]
     556                 :            :             {
     557                 :       4137 :               return true;
     558                 :            :             }
     559                 :            :           }
     560         [ +  + ]:      10821 :         }
     561                 :         75 :         return false;
     562                 :            :       }
     563                 :            :       else
     564                 :            :       {
     565                 :       1451 :         return true;
     566                 :            :       }
     567                 :            :     }
     568                 :         59 :     case Kind::REGEXP_NONE:
     569                 :            :     {
     570                 :         59 :       return false;
     571                 :            :     }
     572                 :      12312 :     case Kind::REGEXP_ALLCHAR:
     573                 :            :     {
     574         [ +  + ]:      12312 :       if (s.size() == index_start + 1)
     575                 :            :       {
     576                 :       5223 :         return true;
     577                 :            :       }
     578                 :            :       else
     579                 :            :       {
     580                 :       7089 :         return false;
     581                 :            :       }
     582                 :            :     }
     583                 :        924 :     case Kind::REGEXP_RANGE:
     584                 :            :     {
     585         [ +  + ]:        924 :       if (s.size() == index_start + 1)
     586                 :            :       {
     587                 :        818 :         unsigned a = r[0].getConst<String>().front();
     588                 :        818 :         unsigned b = r[1].getConst<String>().front();
     589                 :        818 :         unsigned c = s.back();
     590 [ +  + ][ +  + ]:        818 :         return (a <= c && c <= b);
     591                 :            :       }
     592                 :            :       else
     593                 :            :       {
     594                 :        106 :         return false;
     595                 :            :       }
     596                 :            :     }
     597                 :          0 :     case Kind::REGEXP_LOOP:
     598                 :            :     {
     599                 :          0 :       NodeManager* nm = r.getNodeManager();
     600                 :          0 :       uint32_t l = r[1].getConst<Rational>().getNumerator().toUnsignedInt();
     601         [ -  - ]:          0 :       if (s.size() == index_start)
     602                 :            :       {
     603                 :          0 :         return l == 0 || testConstStringInRegExpInternal(s, index_start, r[0]);
     604                 :            :       }
     605                 :          0 :       else if (l == 0 && r[1] == r[2])
     606                 :            :       {
     607                 :          0 :         return false;
     608                 :            :       }
     609                 :            :       else
     610                 :            :       {
     611                 :          0 :         Assert(r.getNumChildren() == 3)
     612                 :          0 :             << "String rewriter error: LOOP has 2 children";
     613         [ -  - ]:          0 :         if (l == 0)
     614                 :            :         {
     615                 :            :           // R{0,u}
     616                 :          0 :           uint32_t u = r[2].getConst<Rational>().getNumerator().toUnsignedInt();
     617         [ -  - ]:          0 :           for (unsigned len = s.size() - index_start; len >= 1; len--)
     618                 :            :           {
     619                 :          0 :             cvc5::internal::String t = s.substr(index_start, len);
     620         [ -  - ]:          0 :             if (testConstStringInRegExpInternal(t, 0, r[0]))
     621                 :            :             {
     622         [ -  - ]:          0 :               if (len + index_start == s.size())
     623                 :            :               {
     624                 :          0 :                 return true;
     625                 :            :               }
     626                 :            :               else
     627                 :            :               {
     628                 :          0 :                 Node num2 = nm->mkConstInt(cvc5::internal::Rational(u - 1));
     629                 :          0 :                 Node r2 = nm->mkNode(Kind::REGEXP_LOOP, r[0], r[1], num2);
     630         [ -  - ]:          0 :                 if (testConstStringInRegExpInternal(s, index_start + len, r2))
     631                 :            :                 {
     632                 :          0 :                   return true;
     633                 :            :                 }
     634 [ -  - ][ -  - ]:          0 :               }
     635                 :            :             }
     636         [ -  - ]:          0 :           }
     637                 :          0 :           return false;
     638                 :            :         }
     639                 :            :         else
     640                 :            :         {
     641                 :            :           // R{l,l}
     642                 :          0 :           Assert(r[1] == r[2])
     643                 :          0 :               << "String rewriter error: LOOP nums are not equal";
     644         [ -  - ]:          0 :           if (l > s.size() - index_start)
     645                 :            :           {
     646         [ -  - ]:          0 :             if (testConstStringInRegExpInternal(s, s.size(), r[0]))
     647                 :            :             {
     648                 :          0 :               l = s.size() - index_start;
     649                 :            :             }
     650                 :            :             else
     651                 :            :             {
     652                 :          0 :               return false;
     653                 :            :             }
     654                 :            :           }
     655         [ -  - ]:          0 :           for (unsigned len = 1; len <= s.size() - index_start; len++)
     656                 :            :           {
     657                 :          0 :             cvc5::internal::String t = s.substr(index_start, len);
     658         [ -  - ]:          0 :             if (testConstStringInRegExpInternal(t, 0, r[0]))
     659                 :            :             {
     660                 :          0 :               Node num2 = nm->mkConstInt(cvc5::internal::Rational(l - 1));
     661                 :          0 :               Node r2 = nm->mkNode(Kind::REGEXP_LOOP, r[0], num2, num2);
     662         [ -  - ]:          0 :               if (testConstStringInRegExpInternal(s, index_start + len, r2))
     663                 :            :               {
     664                 :          0 :                 return true;
     665                 :            :               }
     666 [ -  - ][ -  - ]:          0 :             }
     667         [ -  - ]:          0 :           }
     668                 :          0 :           return false;
     669                 :            :         }
     670                 :            :       }
     671                 :            :     }
     672                 :       1486 :     case Kind::REGEXP_COMPLEMENT:
     673                 :            :     {
     674                 :       1486 :       return !testConstStringInRegExpInternal(s, index_start, r[0]);
     675                 :            :       break;
     676                 :            :     }
     677                 :          0 :     default:
     678                 :            :     {
     679                 :          0 :       Assert(!utils::isRegExpKind(k));
     680                 :          0 :       return false;
     681                 :            :     }
     682                 :            :   }
     683                 :            : }
     684                 :            : 
     685                 :        623 : bool RegExpEntail::hasEpsilonNode(TNode node)
     686                 :            : {
     687         [ +  + ]:       2055 :   for (const Node& nc : node)
     688                 :            :   {
     689                 :       1457 :     if (nc.getKind() == Kind::STRING_TO_REGEXP && Word::isEmpty(nc[0]))
     690                 :            :     {
     691                 :         25 :       return true;
     692                 :            :     }
     693         [ +  + ]:       1457 :   }
     694                 :        598 :   return false;
     695                 :            : }
     696                 :            : 
     697                 :      11970 : Node RegExpEntail::getFixedLengthForRegexp(TNode n)
     698                 :            : {
     699                 :      11970 :   NodeManager* nm = n.getNodeManager();
     700                 :      11970 :   Kind k = n.getKind();
     701         [ +  + ]:      11970 :   if (k == Kind::STRING_TO_REGEXP)
     702                 :            :   {
     703         [ +  + ]:       3562 :     if (n[0].isConst())
     704                 :            :     {
     705                 :       6856 :       return nm->mkConstInt(Rational(Word::getLength(n[0])));
     706                 :            :     }
     707                 :            :   }
     708 [ +  + ][ +  + ]:       8408 :   else if (k == Kind::REGEXP_ALLCHAR || k == Kind::REGEXP_RANGE)
     709                 :            :   {
     710                 :       3638 :     return nm->mkConstInt(Rational(1));
     711                 :            :   }
     712 [ +  + ][ +  + ]:       6589 :   else if (k == Kind::REGEXP_UNION || k == Kind::REGEXP_INTER)
     713                 :            :   {
     714                 :       1089 :     Node ret;
     715         [ +  + ]:       2855 :     for (const Node& nc : n)
     716                 :            :     {
     717                 :       2362 :       Node flc = getFixedLengthForRegexp(nc);
     718 [ +  + ][ +  + ]:       2362 :       if (flc.isNull() || (!ret.isNull() && ret != flc))
         [ +  + ][ +  + ]
     719                 :            :       {
     720                 :        596 :         return Node::null();
     721                 :            :       }
     722         [ +  + ]:       1766 :       else if (ret.isNull())
     723                 :            :       {
     724                 :            :         // first time
     725                 :        960 :         ret = flc;
     726                 :            :       }
     727 [ +  + ][ +  + ]:       2958 :     }
     728                 :        493 :     return ret;
     729                 :       1089 :   }
     730         [ +  + ]:       5500 :   else if (k == Kind::REGEXP_CONCAT)
     731                 :            :   {
     732                 :       2223 :     Rational sum(0);
     733         [ +  + ]:       4696 :     for (const Node& nc : n)
     734                 :            :     {
     735                 :       4122 :       Node flc = getFixedLengthForRegexp(nc);
     736         [ +  + ]:       4122 :       if (flc.isNull())
     737                 :            :       {
     738                 :       1649 :         return flc;
     739                 :            :       }
     740                 :       2473 :       Assert(flc.isConst() && flc.getType().isInteger());
     741                 :       2473 :       sum += flc.getConst<Rational>();
     742 [ +  + ][ +  + ]:       5771 :     }
     743                 :        574 :     return nm->mkConstInt(sum);
     744                 :       2223 :   }
     745                 :       3411 :   return Node::null();
     746                 :            : }
     747                 :            : 
     748                 :         83 : Node RegExpEntail::getConstantBoundLengthForRegexp(TNode n, bool isLower) const
     749                 :            : {
     750 [ -  + ][ -  + ]:         83 :   Assert(n.getType().isRegExp());
                 [ -  - ]
     751                 :         83 :   Node ret;
     752         [ +  + ]:         83 :   if (getConstantBoundCache(n, isLower, ret))
     753                 :            :   {
     754                 :         27 :     return ret;
     755                 :            :   }
     756                 :         56 :   Kind k = n.getKind();
     757                 :         56 :   NodeManager* nm = n.getNodeManager();
     758         [ +  + ]:         56 :   if (k == Kind::STRING_TO_REGEXP)
     759                 :            :   {
     760                 :         24 :     ret = d_aent.getConstantBoundLength(n[0], isLower);
     761                 :            :   }
     762 [ +  - ][ -  + ]:         32 :   else if (k == Kind::REGEXP_ALLCHAR || k == Kind::REGEXP_RANGE)
     763                 :            :   {
     764                 :          0 :     ret = d_one;
     765                 :            :   }
     766 [ +  + ][ +  - ]:         32 :   else if (k == Kind::REGEXP_UNION || k == Kind::REGEXP_INTER
     767         [ +  + ]:         28 :            || k == Kind::REGEXP_CONCAT)
     768                 :            :   {
     769                 :         20 :     bool success = true;
     770                 :         20 :     bool firstTime = true;
     771                 :         20 :     Rational rr(0);
     772         [ +  + ]:         54 :     for (const Node& nc : n)
     773                 :            :     {
     774                 :         40 :       Node bc = getConstantBoundLengthForRegexp(nc, isLower);
     775         [ +  + ]:         40 :       if (bc.isNull())
     776                 :            :       {
     777 [ +  - ][ +  - ]:          6 :         if (k == Kind::REGEXP_UNION || (k == Kind::REGEXP_CONCAT && !isLower))
                 [ +  - ]
     778                 :            :         {
     779                 :            :           // since the bound could not be determined on the component, the
     780                 :            :           // overall bound is undetermined.
     781                 :          6 :           success = false;
     782                 :          6 :           break;
     783                 :            :         }
     784                 :            :         else
     785                 :            :         {
     786                 :            :           // if intersection, or we are computing lower bound for concat
     787                 :            :           // and the component cannot be determined, ignore it
     788                 :          0 :           continue;
     789                 :            :         }
     790                 :            :       }
     791                 :         34 :       Assert(bc.isConst() && bc.getType().isInteger());
     792                 :         34 :       Rational r = bc.getConst<Rational>();
     793         [ +  + ]:         34 :       if (k == Kind::REGEXP_CONCAT)
     794                 :            :       {
     795                 :         26 :         rr += r;
     796                 :            :       }
     797         [ +  + ]:          8 :       else if (firstTime)
     798                 :            :       {
     799                 :          4 :         rr = r;
     800                 :            :       }
     801         [ +  + ]:          4 :       else if ((k == Kind::REGEXP_UNION) == isLower)
     802                 :            :       {
     803                 :          2 :         rr = std::min(r, rr);
     804                 :            :       }
     805                 :            :       else
     806                 :            :       {
     807                 :          2 :         rr = std::max(r, rr);
     808                 :            :       }
     809                 :         34 :       firstTime = false;
     810 [ +  + ][ -  + ]:         46 :     }
                 [ +  - ]
     811                 :            :     // if we were successful and didn't ignore all components
     812 [ +  + ][ +  - ]:         20 :     if (success && !firstTime)
     813                 :            :     {
     814                 :         14 :       ret = nm->mkConstInt(rr);
     815                 :            :     }
     816                 :         20 :   }
     817 [ +  + ][ +  + ]:         56 :   if (ret.isNull() && isLower)
                 [ +  + ]
     818                 :            :   {
     819                 :          6 :     ret = d_zero;
     820                 :            :   }
     821         [ +  - ]:        112 :   Trace("strings-rentail") << "getConstantBoundLengthForRegexp: " << n
     822                 :          0 :                            << ", isLower=" << isLower << " returns " << ret
     823                 :         56 :                            << std::endl;
     824                 :         56 :   setConstantBoundCache(n, ret, isLower);
     825                 :         56 :   return ret;
     826                 :          0 : }
     827                 :            : 
     828                 :      23617 : bool RegExpEntail::regExpIncludes(Node r1,
     829                 :            :                                   Node r2,
     830                 :            :                                   std::map<std::pair<Node, Node>, bool>& cache)
     831                 :            : {
     832         [ +  + ]:      23617 :   if (r1 == r2)
     833                 :            :   {
     834                 :       3260 :     return true;
     835                 :            :   }
     836                 :      20357 :   std::pair<Node, Node> key = std::make_pair(r1, r2);
     837                 :      20357 :   const auto& it = cache.find(key);
     838         [ +  + ]:      20357 :   if (it != cache.end())
     839                 :            :   {
     840                 :       5479 :     return (*it).second;
     841                 :            :   }
     842                 :            :   // first, check some basic inclusions
     843                 :      14878 :   bool ret = false;
     844                 :      14878 :   Kind k2 = r2.getKind();
     845                 :            :   // if the right hand side is a constant string, this is a membership test
     846         [ +  + ]:      14878 :   if (k2 == Kind::STRING_TO_REGEXP)
     847                 :            :   {
     848                 :            :     // only check if r1 is a constant regular expression
     849                 :       4393 :     if (r2[0].isConst() && isConstRegExp(r1))
     850                 :            :     {
     851                 :       3681 :       String s = r2[0].getConst<String>();
     852                 :       3681 :       ret = testConstStringInRegExp(s, r1);
     853                 :       3681 :     }
     854                 :       4393 :     cache[key] = ret;
     855                 :       4393 :     return ret;
     856                 :            :   }
     857                 :      10485 :   Kind k1 = r1.getKind();
     858                 :      10485 :   bool retSet = false;
     859         [ +  + ]:      10485 :   if (k1 == Kind::REGEXP_UNION)
     860                 :            :   {
     861                 :       1035 :     retSet = true;
     862                 :            :     // if any component of r1 includes r2, return true
     863         [ +  + ]:       3615 :     for (const Node& r : r1)
     864                 :            :     {
     865         [ +  + ]:       2621 :       if (regExpIncludes(r, r2, cache))
     866                 :            :       {
     867                 :         41 :         ret = true;
     868                 :         41 :         break;
     869                 :            :       }
     870         [ +  + ]:       2621 :     }
     871                 :            :   }
     872 [ +  + ][ +  - ]:      10485 :   if (k2 == Kind::REGEXP_INTER && !ret)
     873                 :            :   {
     874                 :         20 :     retSet = true;
     875                 :            :     // if r1 includes any component of r2, return true
     876         [ +  + ]:         56 :     for (const Node& r : r2)
     877                 :            :     {
     878         [ +  + ]:         38 :       if (regExpIncludes(r1, r, cache))
     879                 :            :       {
     880                 :          2 :         ret = true;
     881                 :          2 :         break;
     882                 :            :       }
     883         [ +  + ]:         38 :     }
     884                 :            :   }
     885         [ +  + ]:      10485 :   if (k1 == Kind::REGEXP_STAR)
     886                 :            :   {
     887                 :       2194 :     retSet = true;
     888                 :            :     // inclusion if r1 is (re.* re.allchar), or if the body of r1 includes r2
     889                 :            :     // (or the body of r2 if it is also a star).
     890         [ +  + ]:       2194 :     if (r1[0].getKind() == Kind::REGEXP_ALLCHAR)
     891                 :            :     {
     892                 :        218 :       ret = true;
     893                 :            :     }
     894                 :            :     else
     895                 :            :     {
     896         [ +  + ]:       1976 :       ret = regExpIncludes(r1[0], k2 == Kind::REGEXP_STAR ? r2[0] : r2, cache);
     897                 :            :     }
     898                 :            :   }
     899         [ +  + ]:       8291 :   else if (k1 == Kind::STRING_TO_REGEXP)
     900                 :            :   {
     901                 :            :     // only way to include is if equal, which was already checked
     902                 :       3557 :     retSet = true;
     903                 :            :   }
     904 [ +  + ][ +  - ]:       4734 :   else if (k1 == Kind::REGEXP_RANGE && utils::isCharacterRange(r1))
         [ +  + ][ +  + ]
                 [ -  - ]
     905                 :            :   {
     906                 :        253 :     retSet = true;
     907                 :            :     // if comparing subranges, we check inclusion of interval
     908 [ +  + ][ +  - ]:        253 :     if (k2 == Kind::REGEXP_RANGE && utils::isCharacterRange(r2))
         [ +  + ][ +  + ]
                 [ -  - ]
     909                 :            :     {
     910                 :         37 :       unsigned l1 = r1[0].getConst<String>().front();
     911                 :         37 :       unsigned u1 = r1[1].getConst<String>().front();
     912                 :         37 :       unsigned l2 = r2[0].getConst<String>().front();
     913                 :         37 :       unsigned u2 = r2[1].getConst<String>().front();
     914 [ +  - ][ +  - ]:         37 :       ret = l1 <= l2 && l2 <= u1 && l1 <= u2 && u2 <= u1;
         [ +  - ][ +  + ]
     915                 :            :     }
     916                 :            :   }
     917         [ +  + ]:      10485 :   if (retSet)
     918                 :            :   {
     919                 :       7039 :     cache[key] = ret;
     920                 :       7039 :     return ret;
     921                 :            :   }
     922                 :            :   // avoid infinite loop
     923                 :       3446 :   cache[key] = false;
     924                 :       3446 :   NodeManager* nm = r1.getNodeManager();
     925                 :       3446 :   Node sigma = nm->mkNode(Kind::REGEXP_ALLCHAR, std::vector<Node>{});
     926                 :       3446 :   Node sigmaStar = nm->mkNode(Kind::REGEXP_STAR, sigma);
     927                 :            : 
     928                 :       3446 :   std::vector<Node> v1, v2;
     929                 :       3446 :   utils::getRegexpComponents(r1, v1);
     930                 :       3446 :   utils::getRegexpComponents(r2, v2);
     931                 :            : 
     932                 :            :   // In the following, we iterate over `r2` (the "includee") and try to
     933                 :            :   // match it with `r1`. `idxs`/`newIdxs` keep track of all the possible
     934                 :            :   // positions in `r1` that we could currently be at.
     935                 :       3446 :   std::unordered_set<size_t> newIdxs = {0};
     936                 :       3446 :   std::unordered_set<size_t> idxs;
     937         [ +  + ]:       7953 :   for (const Node& n2 : v2)
     938                 :            :   {
     939                 :            :     // Transfer elements from `newIdxs` to `idxs`. Out-of-bound indices are
     940                 :            :     // removed and for (re.* re.allchar), we additionally include the option of
     941                 :            :     // skipping it. Indices must be smaller than the size of `v1`.
     942                 :       7297 :     idxs.clear();
     943         [ +  + ]:      21039 :     for (size_t idx : newIdxs)
     944                 :            :     {
     945         [ +  + ]:      13742 :       if (idx < v1.size())
     946                 :            :       {
     947                 :      13537 :         idxs.insert(idx);
     948 [ +  + ][ +  + ]:      13537 :         if (idx + 1 < v1.size() && v1[idx] == sigmaStar)
                 [ +  + ]
     949                 :            :         {
     950 [ +  - ][ +  - ]:       3808 :           Assert(idx + 1 == v1.size() || v1[idx + 1] != sigmaStar);
         [ -  + ][ -  + ]
                 [ -  - ]
     951                 :       3808 :           idxs.insert(idx + 1);
     952                 :            :         }
     953                 :            :       }
     954                 :            :     }
     955                 :       7297 :     newIdxs.clear();
     956                 :            : 
     957         [ +  + ]:      21563 :     for (size_t idx : idxs)
     958                 :            :     {
     959         [ +  + ]:      14266 :       if (regExpIncludes(v1[idx], n2, cache))
     960                 :            :       {
     961                 :            :         // If this component includes n2, then we can consume it.
     962                 :       7765 :         newIdxs.insert(idx + 1);
     963                 :            :       }
     964         [ +  + ]:      14266 :       if (v1[idx] == sigmaStar)
     965                 :            :       {
     966                 :            :         // (re.* re.allchar) can match an arbitrary amount of `r2`
     967                 :       4049 :         newIdxs.insert(idx);
     968                 :            :       }
     969         [ +  + ]:      10217 :       else if (utils::isUnboundedWildcard(v1, idx))
     970                 :            :       {
     971                 :            :         // If a series of re.allchar is followed by (re.* re.allchar), we
     972                 :            :         // can decide not to "waste" the re.allchar because the order of
     973                 :            :         // the two wildcards is not observable (i.e. it does not change
     974                 :            :         // the sequences matched by the regular expression)
     975                 :       1922 :         newIdxs.insert(idx);
     976                 :            :       }
     977                 :            :     }
     978                 :            : 
     979         [ +  + ]:       7297 :     if (newIdxs.empty())
     980                 :            :     {
     981                 :            :       // If there are no candidates, we can't match the remainder of r2
     982                 :       2790 :       break;
     983                 :            :     }
     984                 :            :   }
     985                 :            : 
     986                 :            :   // We have processed all of `r2`. We are now looking if there was also a
     987                 :            :   // path to the end in `r1`. This makes sure that we don't have leftover
     988                 :            :   // bits in `r1` that don't match anything in `r2`.
     989                 :       3446 :   bool result = false;
     990         [ +  + ]:       5403 :   for (size_t idx : newIdxs)
     991                 :            :   {
     992 [ +  + ][ +  + ]:       2234 :     if (idx == v1.size() || (idx == v1.size() - 1 && v1[idx] == sigmaStar))
         [ +  + ][ +  + ]
     993                 :            :     {
     994                 :        277 :       result = true;
     995                 :        277 :       break;
     996                 :            :     }
     997                 :            :   }
     998                 :       3446 :   cache[key] = result;
     999                 :       3446 :   return result;
    1000                 :      20357 : }
    1001                 :            : 
    1002                 :       2981 : bool RegExpEntail::regExpIncludes(Node r1, Node r2)
    1003                 :            : {
    1004                 :       2981 :   std::map<std::pair<Node, Node>, bool> cache;
    1005                 :       5962 :   return regExpIncludes(r1, r2, cache);
    1006                 :       2981 : }
    1007                 :            : 
    1008                 :      53933 : Node RegExpEntail::getGeneralizedConstRegExp(const Node& n)
    1009                 :            : {
    1010 [ -  + ][ -  + ]:      53933 :   Assert(n.getType().isString());
                 [ -  - ]
    1011                 :      53933 :   NodeManager* nm = n.getNodeManager();
    1012                 :      53933 :   std::vector<Node> ncs;
    1013         [ +  + ]:      53933 :   if (n.getKind() == Kind::STRING_CONCAT)
    1014                 :            :   {
    1015                 :       6660 :     ncs.insert(ncs.end(), n.begin(), n.end());
    1016                 :            :   }
    1017                 :            :   else
    1018                 :            :   {
    1019                 :      47273 :     ncs.push_back(n);
    1020                 :            :   }
    1021                 :      53933 :   bool nonTrivial = false;
    1022                 :            :   Node sigmaStar =
    1023                 :     107866 :       nm->mkNode(Kind::REGEXP_STAR, nm->mkNode(Kind::REGEXP_ALLCHAR));
    1024                 :      53933 :   std::vector<Node> rs;
    1025         [ +  + ]:     123361 :   for (const Node& nc : ncs)
    1026                 :            :   {
    1027                 :      69428 :     Node re = sigmaStar;
    1028         [ +  + ]:      69428 :     if (nc.isConst())
    1029                 :            :     {
    1030                 :       8290 :       nonTrivial = true;
    1031                 :       8290 :       re = nm->mkNode(Kind::STRING_TO_REGEXP, nc);
    1032                 :            :     }
    1033         [ +  + ]:      61138 :     else if (nc.getKind() == Kind::STRING_ITOS)
    1034                 :            :     {
    1035                 :        999 :       nonTrivial = true;
    1036                 :            :       Node digRange = nm->mkNode(Kind::REGEXP_RANGE,
    1037                 :       1998 :                                  nm->mkConst(String("0")),
    1038                 :       3996 :                                  nm->mkConst(String("9")));
    1039                 :        999 :       re = nm->mkNode(Kind::REGEXP_STAR, digRange);
    1040                 :            :       // maybe non-empty digit range?
    1041                 :            :       // relies on RARE rule str-in-re-from-int-dig-range to prove
    1042         [ +  + ]:        999 :       if (d_aent.check(nc[0]))
    1043                 :            :       {
    1044                 :        227 :         re = nm->mkNode(Kind::REGEXP_CONCAT, digRange, re);
    1045                 :            :       }
    1046                 :        999 :     }
    1047                 :      69428 :     rs.push_back(re);
    1048                 :      69428 :   }
    1049         [ +  + ]:      53933 :   if (nonTrivial)
    1050                 :            :   {
    1051         [ +  + ]:       6821 :     return rs.size() == 1 ? rs[0] : nm->mkNode(Kind::REGEXP_CONCAT, rs);
    1052                 :            :   }
    1053                 :      47112 :   return Node::null();
    1054                 :      53933 : }
    1055                 :            : 
    1056                 :            : struct RegExpEntailConstantBoundLowerId
    1057                 :            : {
    1058                 :            : };
    1059                 :            : typedef expr::Attribute<RegExpEntailConstantBoundLowerId, Node>
    1060                 :            :     RegExpEntailConstantBoundLower;
    1061                 :            : 
    1062                 :            : struct RegExpEntailConstantBoundUpperId
    1063                 :            : {
    1064                 :            : };
    1065                 :            : typedef expr::Attribute<RegExpEntailConstantBoundUpperId, Node>
    1066                 :            :     RegExpEntailConstantBoundUpper;
    1067                 :            : 
    1068                 :         56 : void RegExpEntail::setConstantBoundCache(TNode n, Node ret, bool isLower)
    1069                 :            : {
    1070         [ +  + ]:         56 :   if (isLower)
    1071                 :            :   {
    1072                 :            :     RegExpEntailConstantBoundLower rcbl;
    1073                 :         28 :     n.setAttribute(rcbl, ret);
    1074                 :            :   }
    1075                 :            :   else
    1076                 :            :   {
    1077                 :            :     RegExpEntailConstantBoundUpper rcbu;
    1078                 :         28 :     n.setAttribute(rcbu, ret);
    1079                 :            :   }
    1080                 :         56 : }
    1081                 :            : 
    1082                 :         83 : bool RegExpEntail::getConstantBoundCache(TNode n, bool isLower, Node& c)
    1083                 :            : {
    1084         [ +  + ]:         83 :   if (isLower)
    1085                 :            :   {
    1086                 :            :     RegExpEntailConstantBoundLower rcbl;
    1087         [ +  + ]:         52 :     if (n.hasAttribute(rcbl))
    1088                 :            :     {
    1089                 :         24 :       c = n.getAttribute(rcbl);
    1090                 :         24 :       return true;
    1091                 :            :     }
    1092                 :            :   }
    1093                 :            :   else
    1094                 :            :   {
    1095                 :            :     RegExpEntailConstantBoundUpper rcbu;
    1096         [ +  + ]:         31 :     if (n.hasAttribute(rcbu))
    1097                 :            :     {
    1098                 :          3 :       c = n.getAttribute(rcbu);
    1099                 :          3 :       return true;
    1100                 :            :     }
    1101                 :            :   }
    1102                 :         56 :   return false;
    1103                 :            : }
    1104                 :            : 
    1105                 :            : }  // namespace strings
    1106                 :            : }  // namespace theory
    1107                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14