LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/strings - sequences_rewriter.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2244 2306 97.3 %
Date: 2026-08-12 10:35:20 Functions: 63 63 100.0 %
Branches: 1759 2237 78.6 %

           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 the theory of strings.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "theory/strings/sequences_rewriter.h"
      14                 :            : 
      15                 :            : #include "expr/attribute.h"
      16                 :            : #include "expr/node_builder.h"
      17                 :            : #include "expr/sequence.h"
      18                 :            : #include "theory/rewriter.h"
      19                 :            : #include "theory/strings/arith_entail.h"
      20                 :            : #include "theory/strings/regexp_entail.h"
      21                 :            : #include "theory/strings/skolem_cache.h"
      22                 :            : #include "theory/strings/strings_rewriter.h"
      23                 :            : #include "theory/strings/theory_strings_utils.h"
      24                 :            : #include "theory/strings/word.h"
      25                 :            : #include "util/rational.h"
      26                 :            : #include "util/regexp.h"
      27                 :            : #include "util/statistics_registry.h"
      28                 :            : #include "util/string.h"
      29                 :            : 
      30                 :            : using namespace std;
      31                 :            : using namespace cvc5::internal::kind;
      32                 :            : 
      33                 :            : namespace cvc5::internal {
      34                 :            : namespace theory {
      35                 :            : namespace strings {
      36                 :            : 
      37                 :      47367 : SequencesRewriter::SequencesRewriter(NodeManager* nm,
      38                 :            :                                      ArithEntail& ae,
      39                 :            :                                      StringsEntail& se,
      40                 :      47367 :                                      HistogramStat<Rewrite>* statistics)
      41                 :            :     : TheoryRewriter(nm),
      42                 :      47367 :       d_statistics(statistics),
      43                 :      47367 :       d_arithEntail(ae),
      44                 :      47367 :       d_stringsEntail(se)
      45                 :            : {
      46                 :      47367 :   d_sigmaStar = nm->mkNode(Kind::REGEXP_STAR, nm->mkNode(Kind::REGEXP_ALLCHAR));
      47                 :      47367 :   d_true = nm->mkConst(true);
      48                 :      47367 :   d_false = nm->mkConst(false);
      49                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::RE_LOOP_ELIM,
      50                 :            :                            TheoryRewriteCtx::PRE_DSL);
      51                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::RE_EQ_ELIM,
      52                 :            :                            TheoryRewriteCtx::PRE_DSL);
      53                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_RE_INTER_UNION_INCLUSION,
      54                 :            :                            TheoryRewriteCtx::PRE_DSL);
      55                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::STR_IN_RE_EVAL,
      56                 :            :                            TheoryRewriteCtx::DSL_SUBCALL);
      57                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::STR_IN_RE_CONSUME,
      58                 :            :                            TheoryRewriteCtx::PRE_DSL);
      59                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::STR_IN_RE_CONCAT_STAR_CHAR,
      60                 :            :                            TheoryRewriteCtx::PRE_DSL);
      61                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::STR_IN_RE_SIGMA,
      62                 :            :                            TheoryRewriteCtx::PRE_DSL);
      63                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::STR_IN_RE_SIGMA_STAR,
      64                 :            :                            TheoryRewriteCtx::PRE_DSL);
      65                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_SUBSTR_STRIP_SYM_LENGTH,
      66                 :            :                            TheoryRewriteCtx::POST_DSL);
      67                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::STR_CTN_MULTISET_SUBSET,
      68                 :            :                            TheoryRewriteCtx::DSL_SUBCALL);
      69                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_STR_EQ_LEN_UNIFY_PREFIX,
      70                 :            :                            TheoryRewriteCtx::POST_DSL);
      71                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_STR_EQ_LEN_UNIFY,
      72                 :            :                            TheoryRewriteCtx::POST_DSL);
      73                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::STR_INDEXOF_RE_EVAL,
      74                 :            :                            TheoryRewriteCtx::POST_DSL);
      75                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::STR_REPLACE_RE_EVAL,
      76                 :            :                            TheoryRewriteCtx::POST_DSL);
      77                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::STR_REPLACE_RE_ALL_EVAL,
      78                 :            :                            TheoryRewriteCtx::POST_DSL);
      79                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_STR_CONST_NCTN_CONCAT,
      80                 :            :                            TheoryRewriteCtx::DSL_SUBCALL);
      81                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_STR_IN_RE_INCLUSION,
      82                 :            :                            TheoryRewriteCtx::POST_DSL);
      83                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_STR_STRIP_ENDPOINTS,
      84                 :            :                            TheoryRewriteCtx::POST_DSL);
      85                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_STR_SPLIT_CTN,
      86                 :            :                            TheoryRewriteCtx::POST_DSL);
      87                 :            :   // MACRO_RE_INTER_UNION_CONST_ELIM should always be called at post-dsl
      88                 :            :   // as it is partly subsumed by RARE rewrites for intersection.
      89                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_RE_INTER_UNION_CONST_ELIM,
      90                 :            :                            TheoryRewriteCtx::POST_DSL);
      91                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::MACRO_STR_COMPONENT_CTN,
      92                 :            :                            TheoryRewriteCtx::POST_DSL);
      93                 :      47367 :   registerProofRewriteRule(ProofRewriteRule::SEQ_EVAL_OP,
      94                 :            :                            TheoryRewriteCtx::DSL_SUBCALL);
      95                 :            :   // make back pointer to this (for rewriting contains)
      96                 :      47367 :   se.d_rewriter = this;
      97                 :      47367 : }
      98                 :            : 
      99                 :     942390 : Node SequencesRewriter::rewriteViaRule(ProofRewriteRule id, const Node& n)
     100                 :            : {
     101 [ +  + ][ +  + ]:     942390 :   switch (id)
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
                    [ - ]
     102                 :            :   {
     103                 :      19869 :     case ProofRewriteRule::RE_LOOP_ELIM: return rewriteViaReLoopElim(n);
     104                 :      19820 :     case ProofRewriteRule::RE_EQ_ELIM: return rewriteViaReEqElim(n);
     105                 :      19857 :     case ProofRewriteRule::MACRO_RE_INTER_UNION_INCLUSION:
     106                 :      19857 :       return rewriteViaMacroReInterUnionInclusion(n);
     107                 :         83 :     case ProofRewriteRule::RE_INTER_INCLUSION:
     108                 :            :     case ProofRewriteRule::RE_UNION_INCLUSION:
     109                 :         83 :       return rewriteViaReInterUnionInclusion(id, n);
     110                 :      83332 :     case ProofRewriteRule::STR_IN_RE_EVAL: return rewriteViaStrInReEval(n);
     111                 :      19711 :     case ProofRewriteRule::STR_IN_RE_CONSUME:
     112                 :      19711 :       return rewriteViaStrInReConsume(n);
     113                 :      19520 :     case ProofRewriteRule::STR_IN_RE_CONCAT_STAR_CHAR:
     114                 :      19520 :       return rewriteViaStrInReConcatStarChar(n);
     115                 :      19409 :     case ProofRewriteRule::STR_IN_RE_SIGMA: return rewriteViaStrInReSigma(n);
     116                 :      19399 :     case ProofRewriteRule::STR_IN_RE_SIGMA_STAR:
     117                 :      19399 :       return rewriteViaStrInReSigmaStar(n);
     118                 :        397 :     case ProofRewriteRule::MACRO_SUBSTR_STRIP_SYM_LENGTH:
     119                 :            :     {
     120                 :            :       // Rewrite without using the rewriter as a subutility, which ensures
     121                 :            :       // that we can reconstruct the reasoning in a proof.
     122                 :            :       Rewrite rule;
     123                 :        397 :       ArithEntail ae(nodeManager(), nullptr);
     124                 :        397 :       StringsEntail sent(nullptr, ae);
     125                 :        397 :       return rewriteViaMacroSubstrStripSymLength(n, rule, sent);
     126                 :        397 :     }
     127                 :      82870 :     case ProofRewriteRule::STR_CTN_MULTISET_SUBSET:
     128                 :            :     {
     129                 :            :       // don't use this just for evaluation
     130                 :     248610 :       if (n.getKind() == Kind::STRING_CONTAINS
     131                 :      82870 :           && (!n[0].isConst() || !n[1].isConst()))
     132                 :            :       {
     133         [ +  + ]:       8649 :         if (d_stringsEntail.checkMultisetSubset(n[0], n[1]))
     134                 :            :         {
     135                 :       1670 :           return d_nm->mkConst(false);
     136                 :            :         }
     137                 :            :       }
     138                 :            :     }
     139                 :      82035 :     break;
     140                 :        367 :     case ProofRewriteRule::MACRO_STR_EQ_LEN_UNIFY_PREFIX:
     141                 :            :     {
     142         [ +  + ]:        367 :       if (n.getKind() == Kind::EQUAL)
     143                 :            :       {
     144                 :         66 :         return rewriteViaStrEqLenUnifyPrefix(n);
     145                 :            :       }
     146                 :            :     }
     147                 :        301 :     break;
     148                 :        337 :     case ProofRewriteRule::MACRO_STR_EQ_LEN_UNIFY:
     149                 :            :     {
     150         [ +  + ]:        337 :       if (n.getKind() == Kind::EQUAL)
     151                 :            :       {
     152                 :            :         Rewrite rule;
     153                 :         36 :         return rewriteViaStrEqLenUnify(n, rule);
     154                 :            :       }
     155                 :            :     }
     156                 :        301 :     break;
     157                 :        347 :     case ProofRewriteRule::STR_INDEXOF_RE_EVAL:
     158                 :            :     {
     159                 :        347 :       return rewriteViaStrIndexofReEval(n);
     160                 :            :     }
     161                 :            :     break;
     162                 :        274 :     case ProofRewriteRule::STR_REPLACE_RE_EVAL:
     163                 :            :     {
     164                 :        274 :       return rewriteViaStrReplaceReEval(n);
     165                 :            :     }
     166                 :            :     break;
     167                 :        288 :     case ProofRewriteRule::STR_REPLACE_RE_ALL_EVAL:
     168                 :            :     {
     169                 :        288 :       return rewriteViaStrReplaceReAllEval(n);
     170                 :            :     }
     171                 :            :     break;
     172                 :     125429 :     case ProofRewriteRule::MACRO_STR_CONST_NCTN_CONCAT:
     173                 :            :     {
     174                 :     376287 :       if (n.getKind() == Kind::STRING_CONTAINS
     175 [ +  + ][ +  + ]:     125429 :           && n[0].getKind() == Kind::CONST_STRING)
         [ +  + ][ +  + ]
                 [ -  - ]
     176                 :            :       {
     177                 :      43824 :         NodeManager* nm = nodeManager();
     178                 :      43824 :         RegExpEntail re(nm, nullptr);
     179                 :      43824 :         Node re2 = re.getGeneralizedConstRegExp(n[1]);
     180         [ +  + ]:      43824 :         if (!re2.isNull())
     181                 :            :         {
     182                 :            :           Node re2s =
     183                 :      10486 :               nm->mkNode(Kind::REGEXP_CONCAT, d_sigmaStar, re2, d_sigmaStar);
     184                 :       5243 :           String s = n[0].getConst<String>();
     185         [ +  + ]:       5243 :           if (!RegExpEntail::testConstStringInRegExp(s, re2s))
     186                 :            :           {
     187                 :       5448 :             return nm->mkConst(false);
     188                 :            :           }
     189 [ +  + ][ +  + ]:       7967 :         }
     190 [ +  + ][ +  + ]:      46548 :       }
     191                 :            :     }
     192                 :     122705 :     break;
     193                 :        255 :     case ProofRewriteRule::MACRO_STR_IN_RE_INCLUSION:
     194                 :        255 :       return rewriteViaMacroStrInReInclusion(n);
     195                 :        162 :     case ProofRewriteRule::MACRO_STR_SPLIT_CTN:
     196                 :        162 :       return rewriteViaMacroStrSplitCtn(n);
     197                 :     210578 :     case ProofRewriteRule::MACRO_STR_STRIP_ENDPOINTS:
     198                 :            :     {
     199                 :     210578 :       std::vector<Node> nb, nrem, ne;
     200                 :     210578 :       return rewriteViaMacroStrStripEndpoints(n, nb, nrem, ne);
     201                 :     210578 :     }
     202                 :        162 :     case ProofRewriteRule::MACRO_RE_INTER_UNION_CONST_ELIM:
     203                 :            :     {
     204                 :        162 :       Node conflict;
     205                 :        162 :       return rewriteViaMacroReInterUnionConstElim(n, conflict);
     206                 :        162 :     }
     207                 :     217399 :     case ProofRewriteRule::MACRO_STR_COMPONENT_CTN:
     208                 :            :     {
     209         [ +  + ]:     217399 :       if (n.getKind() == Kind::STRING_CONTAINS)
     210                 :            :       {
     211                 :     217270 :         std::vector<Node> nc1;
     212                 :     217270 :         utils::getConcat(n[0], nc1);
     213                 :     217270 :         std::vector<Node> nc2;
     214                 :     217270 :         utils::getConcat(n[1], nc2);
     215                 :            :         // component-wise containment, note we do not use the extended version
     216                 :     217270 :         std::vector<Node> nc1rb;
     217                 :     217270 :         std::vector<Node> nc1re;
     218         [ +  + ]:     217270 :         if (d_stringsEntail.componentContains(nc1, nc2, nc1rb, nc1re) != -1)
     219                 :            :         {
     220                 :      35594 :           return nodeManager()->mkConst(true);
     221                 :            :         }
     222 [ +  + ][ +  + ]:     270661 :       }
         [ +  + ][ +  + ]
     223                 :            :     }
     224                 :     199602 :     break;
     225                 :      82409 :     case ProofRewriteRule::SEQ_EVAL_OP:
     226                 :            :     {
     227                 :            :       // this is a catchall rule for evaluation of operations on constant
     228                 :            :       // sequences
     229                 :      82409 :       TypeNode tn = utils::getOwnerStringType(n);
     230         [ +  + ]:      82409 :       if (tn.isSequence())
     231                 :            :       {
     232         [ +  + ]:       4288 :         for (const Node& nc : n)
     233                 :            :         {
     234         [ +  + ]:       3513 :           if (!nc.isConst())
     235                 :            :           {
     236                 :       2019 :             return Node::null();
     237                 :            :           }
     238         [ +  + ]:       3513 :         }
     239                 :        775 :         RewriteResponse response = postRewrite(n);
     240                 :        775 :         Node ret = response.d_node;
     241         [ +  - ]:        775 :         if (ret.isConst())
     242                 :            :         {
     243                 :        775 :           return ret;
     244                 :            :         }
     245 [ -  + ][ -  + ]:       1550 :       }
     246         [ +  + ]:      82409 :     }
     247                 :      79615 :     break;
     248                 :        116 :     case ProofRewriteRule::STR_OVERLAP_SPLIT_CTN:
     249                 :            :     case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_CTN:
     250                 :            :     case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_INDEXOF:
     251                 :            :     case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_REPLACE:
     252                 :        116 :       return rewriteViaOverlap(id, n);
     253                 :          0 :     default: break;
     254                 :            :   }
     255                 :     484559 :   return Node::null();
     256                 :            : }
     257                 :            : 
     258                 :          2 : ArithEntail& SequencesRewriter::getArithEntail() { return d_arithEntail; }
     259                 :            : 
     260                 :          3 : StringsEntail& SequencesRewriter::getStringsEntail() { return d_stringsEntail; }
     261                 :            : 
     262                 :     631382 : Node SequencesRewriter::rewriteEquality(Node node)
     263                 :            : {
     264 [ -  + ][ -  + ]:     631382 :   Assert(node.getKind() == Kind::EQUAL);
                 [ -  - ]
     265         [ +  + ]:     631382 :   if (node[0] == node[1])
     266                 :            :   {
     267                 :      34315 :     return returnRewrite(node, d_true, Rewrite::EQ_REFL);
     268                 :            :   }
     269                 :     597067 :   else if (node[0].isConst() && node[1].isConst())
     270                 :            :   {
     271                 :       5685 :     return returnRewrite(node, d_false, Rewrite::EQ_CONST_FALSE);
     272                 :            :   }
     273                 :            :   // standard ordering
     274         [ +  + ]:     591382 :   if (node[0] > node[1])
     275                 :            :   {
     276                 :     177292 :     Node ret = nodeManager()->mkNode(Kind::EQUAL, node[1], node[0]);
     277                 :      88646 :     return returnRewrite(node, ret, Rewrite::EQ_SYM);
     278                 :      88646 :   }
     279                 :     502736 :   return node;
     280                 :            : }
     281                 :            : 
     282                 :     120837 : Node SequencesRewriter::rewriteEqualityExt(Node node)
     283                 :            : {
     284 [ -  + ][ -  + ]:     120837 :   Assert(node.getKind() == Kind::EQUAL);
                 [ -  - ]
     285                 :     120837 :   TypeNode tn = node[0].getType();
     286         [ +  + ]:     120837 :   if (tn.isInteger())
     287                 :            :   {
     288                 :      28076 :     return rewriteArithEqualityExt(node);
     289                 :            :   }
     290         [ +  + ]:      92761 :   if (tn.isStringLike())
     291                 :            :   {
     292                 :      92739 :     return rewriteStrEqualityExt(node);
     293                 :            :   }
     294                 :         22 :   return node;
     295                 :     120837 : }
     296                 :            : 
     297                 :      92739 : Node SequencesRewriter::rewriteStrEqualityExt(Node node)
     298                 :            : {
     299                 :      92739 :   Assert(node.getKind() == Kind::EQUAL && node[0].getType().isStringLike());
     300                 :      92739 :   TypeNode stype = node[0].getType();
     301                 :            : 
     302                 :      92739 :   bool hasStrTerm = false;
     303         [ +  + ]:     183871 :   for (size_t r = 0; r < 2; r++)
     304                 :            :   {
     305 [ +  + ][ -  - ]:     179095 :     if (!node[r].isConst()
     306 [ +  + ][ +  + ]:     179095 :         && kindToTheoryId(node[r].getKind()) == THEORY_STRINGS)
         [ +  + ][ +  - ]
                 [ -  - ]
     307                 :            :     {
     308                 :      87963 :       hasStrTerm = true;
     309                 :      87963 :       break;
     310                 :            :     }
     311                 :            :   }
     312         [ +  + ]:      92739 :   if (!hasStrTerm)
     313                 :            :   {
     314                 :            :     // equality between variables and constants, no rewrites apply
     315                 :       4776 :     return node;
     316                 :            :   }
     317                 :            : 
     318                 :      87963 :   NodeManager* nm = nodeManager();
     319                 :            :   // ( ~contains( s, t ) V ~contains( t, s ) ) => ( s == t ---> false )
     320         [ +  + ]:     257087 :   for (unsigned r = 0; r < 2; r++)
     321                 :            :   {
     322                 :            :     // must call rewrite contains directly to avoid infinite loop
     323                 :     345634 :     Node ctn = nm->mkNode(Kind::STRING_CONTAINS, node[r], node[1 - r]);
     324                 :     172817 :     Node prev = ctn;
     325                 :     172817 :     ctn = rewriteContains(ctn);
     326 [ -  + ][ -  + ]:     172817 :     Assert(!ctn.isNull());
                 [ -  - ]
     327 [ +  + ][ +  + ]:     172817 :     if (ctn != prev && ctn.getKind() == Kind::STRING_CONTAINS)
                 [ +  + ]
     328                 :            :     {
     329                 :       1345 :       prev = ctn;
     330                 :       1345 :       ctn = rewriteContains(ctn);
     331 [ -  + ][ -  + ]:       1345 :       Assert(!ctn.isNull());
                 [ -  - ]
     332                 :            :     }
     333         [ +  + ]:     172817 :     if (ctn.isConst())
     334                 :            :     {
     335         [ +  + ]:      54065 :       if (!ctn.getConst<bool>())
     336                 :            :       {
     337                 :       3693 :         return returnRewrite(node, ctn, Rewrite::EQ_NCTN);
     338                 :            :       }
     339                 :            :       else
     340                 :            :       {
     341                 :            :         // definitely contains but not syntactically equal
     342                 :            :         // We may be able to simplify, e.g.
     343                 :            :         //  str.++( x, "a" ) == "a"  ----> x = ""
     344                 :            :       }
     345                 :            :     }
     346 [ +  + ][ +  + ]:     176510 :   }
     347                 :            : 
     348         [ +  + ]:     505620 :   std::vector<Node> c[2];
     349         [ +  + ]:     252810 :   for (unsigned i = 0; i < 2; i++)
     350                 :            :   {
     351                 :     168540 :     utils::getConcat(node[i], c[i]);
     352                 :            :   }
     353                 :            : 
     354                 :            :   // check if the prefix, suffix mismatches
     355                 :            :   //   For example, str.++( x, "a", y ) == str.++( x, "bc", z ) ---> false
     356                 :      84270 :   unsigned minsize = std::min(c[0].size(), c[1].size());
     357         [ +  + ]:     252561 :   for (unsigned r = 0; r < 2; r++)
     358                 :            :   {
     359         [ +  + ]:     169442 :     for (unsigned i = 0; i < minsize; i++)
     360                 :            :     {
     361         [ +  + ]:     169180 :       unsigned index1 = r == 0 ? i : (c[0].size() - 1) - i;
     362         [ +  + ]:     169180 :       unsigned index2 = r == 0 ? i : (c[1].size() - 1) - i;
     363                 :     169180 :       Node s = c[0][index1];
     364                 :     169180 :       Node t = c[1][index2];
     365 [ +  + ][ +  + ]:     169180 :       if (s.isConst() && t.isConst())
                 [ +  + ]
     366                 :            :       {
     367                 :       1245 :         size_t lenS = Word::getLength(s);
     368                 :       1245 :         size_t lenT = Word::getLength(t);
     369         [ +  + ]:       1245 :         size_t lenShort = lenS <= lenT ? lenS : lenT;
     370                 :       2490 :         bool isSameFix = r == 1 ? Word::rstrncmp(s, t, lenShort)
     371                 :       1245 :                                 : Word::strncmp(s, t, lenShort);
     372         [ +  + ]:       1245 :         if (!isSameFix)
     373                 :            :         {
     374                 :        242 :           Node ret = nodeManager()->mkConst(false);
     375                 :        242 :           return returnRewrite(node, ret, Rewrite::EQ_NFIX);
     376                 :        242 :         }
     377                 :            :       }
     378         [ +  + ]:     168938 :       if (s != t)
     379                 :            :       {
     380                 :     168029 :         break;
     381                 :            :       }
     382 [ +  + ][ +  + ]:     337451 :     }
                 [ +  + ]
     383                 :            :   }
     384                 :            : 
     385                 :      84028 :   Node new_ret;
     386                 :            :   // ------- equality unification
     387                 :      84028 :   bool changed = false;
     388         [ +  + ]:     252084 :   for (unsigned i = 0; i < 2; i++)
     389                 :            :   {
     390 [ +  + ][ +  - ]:     168937 :     while (!c[0].empty() && !c[1].empty() && c[0].back() == c[1].back())
         [ +  + ][ +  + ]
     391                 :            :     {
     392                 :        881 :       c[0].pop_back();
     393                 :        881 :       c[1].pop_back();
     394                 :        881 :       changed = true;
     395                 :            :     }
     396                 :            :     // splice constants
     397 [ +  - ][ +  + ]:     335689 :     if (!c[0].empty() && !c[1].empty() && c[0].back().isConst()
     398 [ +  + ][ +  + ]:     335689 :         && c[1].back().isConst())
                 [ +  + ]
     399                 :            :     {
     400         [ +  + ]:       2580 :       Node cs[2];
     401                 :            :       size_t csl[2];
     402         [ +  + ]:       1290 :       for (unsigned j = 0; j < 2; j++)
     403                 :            :       {
     404                 :        860 :         cs[j] = c[j].back();
     405                 :        860 :         csl[j] = Word::getLength(cs[j]);
     406                 :            :       }
     407                 :        430 :       size_t larger = csl[0] > csl[1] ? 0 : 1;
     408                 :        430 :       size_t smallerSize = csl[1 - larger];
     409                 :        860 :       if (cs[1 - larger]
     410                 :        675 :           == (i == 0 ? Word::suffix(cs[larger], smallerSize)
     411                 :        245 :                      : Word::prefix(cs[larger], smallerSize)))
     412                 :            :       {
     413                 :        430 :         size_t sizeDiff = csl[larger] - smallerSize;
     414                 :        430 :         c[larger][c[larger].size() - 1] =
     415                 :       1105 :             i == 0 ? Word::prefix(cs[larger], sizeDiff)
     416                 :        675 :                    : Word::suffix(cs[larger], sizeDiff);
     417                 :        430 :         c[1 - larger].pop_back();
     418                 :        430 :         changed = true;
     419                 :            :       }
     420 [ +  + ][ -  - ]:       1290 :     }
     421         [ +  + ]:     504168 :     for (unsigned j = 0; j < 2; j++)
     422                 :            :     {
     423                 :     336112 :       std::reverse(c[j].begin(), c[j].end());
     424                 :            :     }
     425                 :            :   }
     426         [ +  + ]:      84028 :   if (changed)
     427                 :            :   {
     428                 :            :     // e.g. x++y = x++z ---> y = z, "AB" ++ x = "A" ++ y --> "B" ++ x = y
     429                 :       1060 :     Node s1 = utils::mkConcat(c[0], stype);
     430                 :       1060 :     Node s2 = utils::mkConcat(c[1], stype);
     431                 :       1060 :     if (s1 != node[0] || s2 != node[1])
     432                 :            :     {
     433                 :       1060 :       new_ret = s1.eqNode(s2);
     434                 :            :       // We generally don't apply the extended equality rewriter if the
     435                 :            :       // original node was an equality but we may be able to do additional
     436                 :            :       // rewriting here, e.g.,
     437                 :            :       // x++y = "" --> x = "" and y = ""
     438                 :       1060 :       return returnRewrite(node, new_ret, Rewrite::STR_EQ_UNIFY);
     439                 :            :     }
     440 [ -  + ][ -  + ]:       2120 :   }
     441                 :            : 
     442                 :            :   // ------- rewrites for (= "" _)
     443                 :      82968 :   Node empty = Word::mkEmptyWord(stype);
     444         [ +  + ]:     230245 :   for (size_t i = 0; i < 2; i++)
     445                 :            :   {
     446         [ +  + ]:     156753 :     if (node[i] == empty)
     447                 :            :     {
     448                 :      46944 :       Node ne = node[1 - i];
     449         [ +  + ]:      46944 :       if (ne.getKind() == Kind::STRING_REPLACE)
     450                 :            :       {
     451                 :            :         // (= "" (str.replace x y x)) ---> (= x "")
     452         [ +  + ]:       5620 :         if (ne[0] == ne[2])
     453                 :            :         {
     454                 :        362 :           Node ret = nm->mkNode(Kind::EQUAL, ne[0], empty);
     455                 :        181 :           return returnRewrite(node, ret, Rewrite::STR_EMP_REPL_X_Y_X);
     456                 :        181 :         }
     457                 :            : 
     458                 :            :         // (= "" (str.replace x y "A")) ---> (and (= x "") (not (= y "")))
     459         [ +  + ]:       5439 :         if (d_stringsEntail.checkNonEmpty(ne[2]))
     460                 :            :         {
     461                 :        651 :           Node ret = nm->mkNode(
     462                 :            :               Kind::AND,
     463                 :        434 :               {nm->mkNode(Kind::EQUAL, ne[0], empty),
     464                 :        434 :                nm->mkNode(Kind::NOT, nm->mkNode(Kind::EQUAL, ne[1], empty))});
     465                 :        217 :           return returnRewrite(node, ret, Rewrite::STR_EMP_REPL_EMP);
     466                 :        217 :         }
     467                 :            : 
     468                 :            :         // (= "" (str.replace x "A" "")) ---> (str.prefix x "A")
     469                 :       5222 :         if (d_stringsEntail.checkLengthOne(ne[1], true) && ne[2] == empty)
     470                 :            :         {
     471                 :       1126 :           Node ret = nm->mkNode(Kind::STRING_PREFIX, ne[0], ne[1]);
     472                 :        563 :           return returnRewrite(node, ret, Rewrite::STR_EMP_REPL_EMP);
     473                 :        563 :         }
     474                 :            :       }
     475         [ +  + ]:      41324 :       else if (ne.getKind() == Kind::STRING_SUBSTR)
     476                 :            :       {
     477                 :      36806 :         Node zero = nm->mkConstInt(Rational(0));
     478                 :            : 
     479 [ +  + ][ -  - ]:      36806 :         if (d_arithEntail.check(ne[1], false)
     480 [ +  + ][ +  + ]:      36806 :             && d_arithEntail.check(ne[2], true))
         [ +  + ][ +  - ]
                 [ -  - ]
     481                 :            :         {
     482                 :            :           // (= "" (str.substr x 0 m)) ---> (= "" x) if m > 0
     483         [ +  + ]:       1402 :           if (ne[1] == zero)
     484                 :            :           {
     485                 :       1248 :             Node ret = nm->mkNode(Kind::EQUAL, ne[0], empty);
     486                 :        624 :             return returnRewrite(node, ret, Rewrite::STR_EMP_SUBSTR_LEQ_LEN);
     487                 :        624 :           }
     488                 :            : 
     489                 :            :           // (= "" (str.substr x n m)) ---> (<= (str.len x) n)
     490                 :            :           // if n >= 0 and m > 0
     491                 :            :           Node ret = nm->mkNode(
     492                 :       1556 :               Kind::LEQ, nm->mkNode(Kind::STRING_LENGTH, ne[0]), ne[1]);
     493                 :        778 :           return returnRewrite(node, ret, Rewrite::STR_EMP_SUBSTR_LEQ_LEN);
     494                 :        778 :         }
     495                 :            : 
     496                 :            :         // (= "" (str.substr "A" 0 z)) ---> (<= z 0)
     497                 :      35404 :         if (d_stringsEntail.checkNonEmpty(ne[0]) && ne[1] == zero)
     498                 :            :         {
     499                 :      14226 :           Node ret = nm->mkNode(Kind::LEQ, ne[2], zero);
     500                 :       7113 :           return returnRewrite(node, ret, Rewrite::STR_EMP_SUBSTR_LEQ_Z);
     501                 :       7113 :         }
     502         [ +  + ]:      36806 :       }
     503         [ +  + ]:      46944 :     }
     504                 :            :   }
     505                 :            : 
     506                 :            :   // ------- rewrites for (= (str.replace _ _ _) _)
     507         [ +  + ]:     220455 :   for (size_t i = 0; i < 2; i++)
     508                 :            :   {
     509         [ +  + ]:     146984 :     if (node[i].getKind() == Kind::STRING_REPLACE)
     510                 :            :     {
     511                 :      12451 :       Node repl = node[i];
     512                 :      12451 :       Node x = node[1 - i];
     513                 :            : 
     514                 :            :       // (= "A" (str.replace "" x y)) ---> (and (= x "") (= y "A"))
     515                 :      12451 :       if (d_stringsEntail.checkNonEmpty(x) && repl[0] == empty)
     516                 :            :       {
     517                 :            :         Node ret =
     518                 :         32 :             nm->mkNode(Kind::AND, {repl[1].eqNode(empty), repl[2].eqNode(x)});
     519                 :          8 :         return returnRewrite(node, ret, Rewrite::STR_EQ_REPL_EMP);
     520                 :          8 :       }
     521                 :            : 
     522                 :            :       // (= x (str.replace y x y)) ---> (= x y)
     523                 :      12443 :       if (repl[0] == repl[2] && x == repl[1])
     524                 :            :       {
     525                 :          2 :         Node ret = nm->mkNode(Kind::EQUAL, x, repl[0]);
     526                 :          1 :         return returnRewrite(node, ret, Rewrite::STR_EQ_REPL_TO_EQ);
     527                 :          1 :       }
     528                 :            : 
     529                 :            :       // (= x (str.replace x "A" "B")) ---> (not (str.contains x "A"))
     530         [ +  + ]:      12442 :       if (x == repl[0])
     531                 :            :       {
     532                 :       8724 :         Node eq = rewriteEquality(nm->mkNode(Kind::EQUAL, repl[1], repl[2]));
     533 [ +  + ][ +  - ]:       4362 :         if (eq.isConst() && !eq.getConst<bool>())
                 [ +  + ]
     534                 :            :         {
     535                 :            :           Node ret = nm->mkNode(Kind::NOT,
     536                 :         20 :                                 nm->mkNode(Kind::STRING_CONTAINS, x, repl[1]));
     537                 :         10 :           return returnRewrite(node, ret, Rewrite::STR_EQ_REPL_NOT_CTN);
     538                 :         10 :         }
     539         [ +  + ]:       4362 :       }
     540                 :            : 
     541                 :            :       // (= (str.replace x y z) z) --> (or (= x y) (= x z))
     542                 :            :       // if (str.len y) = (str.len z)
     543         [ +  + ]:      12432 :       if (repl[2] == x)
     544                 :            :       {
     545                 :       6038 :         Node lenY = nm->mkNode(Kind::STRING_LENGTH, repl[1]);
     546                 :       6038 :         Node lenZ = nm->mkNode(Kind::STRING_LENGTH, repl[2]);
     547         [ +  + ]:       3019 :         if (d_arithEntail.checkEq(lenY, lenZ))
     548                 :            :         {
     549                 :          6 :           Node ret = nm->mkNode(Kind::OR,
     550                 :          4 :                                 {nm->mkNode(Kind::EQUAL, repl[0], repl[1]),
     551                 :          4 :                                  nm->mkNode(Kind::EQUAL, repl[0], repl[2])});
     552                 :          2 :           return returnRewrite(node, ret, Rewrite::STR_EQ_REPL_TO_DIS);
     553                 :          2 :         }
     554 [ +  + ][ +  + ]:       3021 :       }
     555 [ +  + ][ +  + ]:      12472 :     }
     556                 :            :   }
     557                 :            : 
     558                 :            :   // Try to rewrite (= x y) into a conjunction of equalities based on length
     559                 :            :   // entailment.
     560                 :            :   //
     561                 :            :   // (<= (str.len x) (str.++ y1 ... yn)) AND (= x (str.++ y1 ... yn)) --->
     562                 :            :   //  (and (= x (str.++ y1' ... ym')) (= y1'' "") ... (= yk'' ""))
     563                 :            :   //
     564                 :            :   // where yi' and yi'' correspond to some yj and
     565                 :            :   //   (<= (str.len x) (str.++ y1' ... ym'))
     566                 :      73471 :   new_ret = rewriteViaStrEqLenUnifyPrefix(node);
     567         [ +  + ]:      73471 :   if (!new_ret.isNull())
     568                 :            :   {
     569                 :        166 :     return returnRewrite(node, new_ret, Rewrite::STR_EQ_CONJ_LEN_ENTAIL);
     570                 :            :   }
     571                 :            : 
     572                 :            :   // (= (str.++ x_1 ... x_i x_{i + 1} ... x_n)
     573                 :            :   //    (str.++ y_1 ... y_j y_{j + 1} ... y_m)) --->
     574                 :            :   //  (and (= (str.++ x_1 ... x_i) (str.++ y_1 ... y_j))
     575                 :            :   //       (= (str.++ x_{i + 1} ... x_n) (str.++ y_{j + 1} ... y_m)))
     576                 :            :   //
     577                 :            :   // if (str.len (str.++ x_1 ... x_i)) = (str.len (str.++ y_1 ... y_j))
     578                 :            :   //
     579                 :            :   // This rewrite performs length-based equality splitting: If we can show
     580                 :            :   // that two prefixes have the same length, we can split an equality into
     581                 :            :   // two equalities, one over the prefixes and another over the suffixes.
     582                 :            :   Rewrite rule;
     583                 :      73305 :   new_ret = rewriteViaStrEqLenUnify(node, rule);
     584         [ +  + ]:      73305 :   if (!new_ret.isNull())
     585                 :            :   {
     586                 :         49 :     return returnRewrite(node, new_ret, rule);
     587                 :            :   }
     588                 :            : 
     589                 :      73256 :   return node;
     590 [ +  + ][ -  - ]:     429577 : }
     591                 :            : 
     592                 :      28076 : Node SequencesRewriter::rewriteArithEqualityExt(Node node)
     593                 :            : {
     594                 :      28076 :   Assert(node.getKind() == Kind::EQUAL && node[0].getType().isInteger());
     595                 :            : 
     596                 :            :   // cases where we can solve the equality
     597                 :            : 
     598                 :            :   // notice we cannot rewrite str.to.int(x)=n to x="n" due to leading zeroes.
     599                 :            : 
     600                 :      28076 :   return node;
     601                 :            : }
     602                 :            : 
     603                 :     198772 : Node SequencesRewriter::rewriteLength(Node node)
     604                 :            : {
     605 [ -  + ][ -  + ]:     198772 :   Assert(node.getKind() == Kind::STRING_LENGTH);
                 [ -  - ]
     606                 :     198772 :   NodeManager* nm = nodeManager();
     607                 :     198772 :   Kind nk0 = node[0].getKind();
     608         [ +  + ]:     198772 :   if (node[0].isConst())
     609                 :            :   {
     610                 :      30838 :     Node retNode = nm->mkConstInt(Rational(Word::getLength(node[0])));
     611                 :      15419 :     return returnRewrite(node, retNode, Rewrite::LEN_EVAL);
     612                 :      15419 :   }
     613         [ +  + ]:     183353 :   else if (nk0 == Kind::STRING_CONCAT)
     614                 :            :   {
     615                 :      15664 :     Node tmpNode = node[0];
     616         [ +  - ]:      15664 :     if (tmpNode.getKind() == Kind::STRING_CONCAT)
     617                 :            :     {
     618                 :      15664 :       std::vector<Node> node_vec;
     619         [ +  + ]:      59778 :       for (unsigned int i = 0; i < tmpNode.getNumChildren(); ++i)
     620                 :            :       {
     621         [ +  + ]:      44114 :         if (tmpNode[i].isConst())
     622                 :            :         {
     623                 :      10569 :           node_vec.push_back(
     624                 :      21138 :               nm->mkConstInt(Rational(Word::getLength(tmpNode[i]))));
     625                 :            :         }
     626                 :            :         else
     627                 :            :         {
     628                 :      33545 :           node_vec.push_back(
     629                 :      33545 :               nodeManager()->mkNode(Kind::STRING_LENGTH, tmpNode[i]));
     630                 :            :         }
     631                 :            :       }
     632                 :      15664 :       Node retNode = nodeManager()->mkNode(Kind::ADD, node_vec);
     633                 :      15664 :       return returnRewrite(node, retNode, Rewrite::LEN_CONCAT);
     634                 :      15664 :     }
     635         [ -  + ]:      15664 :   }
     636 [ +  + ][ +  + ]:     167689 :   else if (nk0 == Kind::STRING_REPLACE || nk0 == Kind::STRING_REPLACE_ALL)
     637                 :            :   {
     638                 :       7822 :     Node len1 = nm->mkNode(Kind::STRING_LENGTH, node[0][1]);
     639                 :       7822 :     Node len2 = nm->mkNode(Kind::STRING_LENGTH, node[0][2]);
     640         [ +  + ]:       3911 :     if (d_arithEntail.checkEq(len1, len2))
     641                 :            :     {
     642                 :            :       // len( y ) == len( z ) => len( str.replace( x, y, z ) ) ---> len( x )
     643                 :        554 :       Node retNode = nm->mkNode(Kind::STRING_LENGTH, node[0][0]);
     644                 :        277 :       return returnRewrite(node, retNode, Rewrite::LEN_REPL_INV);
     645                 :        277 :     }
     646 [ +  + ][ +  + ]:       7822 :   }
     647 [ +  + ][ +  + ]:     163778 :   else if (nk0 == Kind::STRING_TO_LOWER || nk0 == Kind::STRING_TO_UPPER
     648 [ +  + ][ +  + ]:     163705 :            || nk0 == Kind::STRING_REV || nk0 == Kind::STRING_UPDATE)
     649                 :            :   {
     650                 :            :     // len( f( x ) ) == len( x ) where f is to_lower, to_upper, or rev.
     651                 :            :     // len( update( x, n, y ) ) = len( x )
     652                 :       1238 :     Node retNode = nm->mkNode(Kind::STRING_LENGTH, node[0][0]);
     653                 :        619 :     return returnRewrite(node, retNode, Rewrite::LEN_CONV_INV);
     654                 :        619 :   }
     655 [ +  + ][ -  + ]:     163159 :   else if (nk0 == Kind::SEQ_UNIT || nk0 == Kind::STRING_UNIT)
     656                 :            :   {
     657                 :        818 :     Node retNode = nm->mkConstInt(Rational(1));
     658                 :        818 :     return returnRewrite(node, retNode, Rewrite::LEN_SEQ_UNIT);
     659                 :        818 :   }
     660                 :     165975 :   return node;
     661                 :            : }
     662                 :            : 
     663                 :            : // TODO (#1180) add rewrite
     664                 :            : //  str.++( str.substr( x, n1, n2 ), str.substr( x, n1+n2, n3 ) ) --->
     665                 :            : //  str.substr( x, n1, n2+n3 )
     666                 :     253257 : Node SequencesRewriter::rewriteConcat(Node node)
     667                 :            : {
     668 [ -  + ][ -  + ]:     253257 :   Assert(node.getKind() == Kind::STRING_CONCAT);
                 [ -  - ]
     669         [ +  - ]:     506514 :   Trace("strings-rewrite-debug")
     670                 :     253257 :       << "Strings::rewriteConcat start " << node << std::endl;
     671                 :     253257 :   std::vector<Node> node_vec;
     672                 :     253257 :   Node preNode = Node::null();
     673         [ +  + ]:    1099399 :   for (Node tmpNode : node)
     674                 :            :   {
     675         [ +  + ]:     846142 :     if (tmpNode.getKind() == Kind::STRING_CONCAT)
     676                 :            :     {
     677                 :      11474 :       unsigned j = 0;
     678                 :            :       // combine the first term with the previous constant if applicable
     679         [ +  + ]:      11474 :       if (!preNode.isNull())
     680                 :            :       {
     681         [ +  + ]:       2767 :         if (tmpNode[0].isConst())
     682                 :            :         {
     683                 :        607 :           std::vector<Node> wvec;
     684                 :        607 :           wvec.push_back(preNode);
     685                 :        607 :           wvec.push_back(tmpNode[0]);
     686                 :        607 :           preNode = Word::mkWordFlatten(wvec);
     687                 :        607 :           node_vec.push_back(preNode);
     688                 :        607 :         }
     689                 :            :         else
     690                 :            :         {
     691                 :       2160 :           node_vec.push_back(preNode);
     692                 :       2160 :           node_vec.push_back(tmpNode[0]);
     693                 :            :         }
     694                 :       2767 :         preNode = Node::null();
     695                 :       2767 :         ++j;
     696                 :            :       }
     697                 :            :       // insert the middle terms to node_vec
     698         [ +  - ]:      11474 :       if (j <= tmpNode.getNumChildren() - 1)
     699                 :            :       {
     700                 :      11474 :         node_vec.insert(node_vec.end(), tmpNode.begin() + j, tmpNode.end() - 1);
     701                 :            :       }
     702                 :            :       // take the last term as the current
     703                 :      11474 :       tmpNode = tmpNode[tmpNode.getNumChildren() - 1];
     704                 :            :     }
     705         [ +  + ]:     846142 :     if (!tmpNode.isConst())
     706                 :            :     {
     707         [ +  + ]:     536896 :       if (!preNode.isNull())
     708                 :            :       {
     709 [ +  - ][ +  + ]:     139683 :         if (preNode.isConst() && !Word::isEmpty(preNode))
         [ +  - ][ +  + ]
                 [ -  - ]
     710                 :            :         {
     711                 :     134667 :           node_vec.push_back(preNode);
     712                 :            :         }
     713                 :     139683 :         preNode = Node::null();
     714                 :            :       }
     715                 :     536896 :       node_vec.push_back(tmpNode);
     716                 :            :     }
     717                 :            :     else
     718                 :            :     {
     719         [ +  + ]:     309246 :       if (preNode.isNull())
     720                 :            :       {
     721                 :     242584 :         preNode = tmpNode;
     722                 :            :       }
     723                 :            :       else
     724                 :            :       {
     725                 :      66662 :         std::vector<Node> vec;
     726                 :      66662 :         vec.push_back(preNode);
     727                 :      66662 :         vec.push_back(tmpNode);
     728                 :      66662 :         preNode = Word::mkWordFlatten(vec);
     729                 :      66662 :       }
     730                 :            :     }
     731                 :     846142 :   }
     732 [ +  + ][ +  - ]:     253257 :   if (!preNode.isNull() && (!preNode.isConst() || !Word::isEmpty(preNode)))
         [ +  + ][ +  + ]
         [ +  + ][ -  - ]
     733                 :            :   {
     734                 :      95577 :     node_vec.push_back(preNode);
     735                 :            :   }
     736                 :            : 
     737                 :     253257 :   TypeNode tn = node.getType();
     738                 :     253257 :   Node retNode = utils::mkConcat(node_vec, tn);
     739         [ +  - ]:     506514 :   Trace("strings-rewrite-debug")
     740                 :     253257 :       << "Strings::rewriteConcat end " << retNode << std::endl;
     741         [ +  + ]:     253257 :   if (retNode != node)
     742                 :            :   {
     743                 :      41852 :     return returnRewrite(node, retNode, Rewrite::CONCAT_NORM);
     744                 :            :   }
     745                 :     211405 :   return node;
     746                 :     253257 : }
     747                 :            : 
     748                 :        116 : Node SequencesRewriter::rewriteAllRegExp(TNode node)
     749                 :            : {
     750 [ -  + ][ -  + ]:        116 :   Assert(node.getKind() == Kind::REGEXP_ALL);
                 [ -  - ]
     751                 :        116 :   NodeManager* nm = nodeManager();
     752                 :            :   // re.all ----> (re.* re.allchar)
     753                 :        232 :   Node ret = nm->mkNode(Kind::REGEXP_STAR, nm->mkNode(Kind::REGEXP_ALLCHAR));
     754                 :        232 :   return returnRewrite(node, ret, Rewrite::RE_ALL_ELIM);
     755                 :        116 : }
     756                 :            : 
     757                 :       8981 : Node SequencesRewriter::rewriteConcatRegExp(TNode node)
     758                 :            : {
     759 [ -  + ][ -  + ]:       8981 :   Assert(node.getKind() == Kind::REGEXP_CONCAT);
                 [ -  - ]
     760                 :       8981 :   NodeManager* nm = nodeManager();
     761         [ +  - ]:      17962 :   Trace("strings-rewrite-debug")
     762                 :       8981 :       << "Strings::rewriteConcatRegExp flatten " << node << std::endl;
     763                 :       8981 :   Node retNode = node;
     764                 :       8981 :   std::vector<Node> vec;
     765                 :       8981 :   bool changed = false;
     766                 :       8981 :   Node emptyRe;
     767                 :            : 
     768                 :            :   // get the string type that are members of this regular expression
     769                 :       8981 :   TypeNode rtype = node.getType();
     770                 :       8981 :   TypeNode stype;
     771         [ +  - ]:       8981 :   if (rtype.isRegExp())
     772                 :            :   {
     773                 :            :     // standard regular expressions are for strings
     774                 :       8981 :     stype = nm->stringType();
     775                 :            :   }
     776                 :            :   else
     777                 :            :   {
     778                 :          0 :     Unimplemented();
     779                 :            :   }
     780                 :            : 
     781         [ +  + ]:      38160 :   for (const Node& c : node)
     782                 :            :   {
     783         [ +  + ]:      29214 :     if (c.getKind() == Kind::REGEXP_CONCAT)
     784                 :            :     {
     785                 :        428 :       changed = true;
     786         [ +  + ]:       1584 :       for (const Node& cc : c)
     787                 :            :       {
     788                 :       1156 :         vec.push_back(cc);
     789                 :       1156 :       }
     790                 :            :     }
     791 [ +  + ][ +  + ]:      37600 :     else if (c.getKind() == Kind::STRING_TO_REGEXP && c[0].isConst()
                 [ -  - ]
     792                 :      37600 :              && Word::isEmpty(c[0]))
     793                 :            :     {
     794                 :        104 :       changed = true;
     795                 :        104 :       emptyRe = c;
     796                 :            :     }
     797         [ +  + ]:      28682 :     else if (c.getKind() == Kind::REGEXP_NONE)
     798                 :            :     {
     799                 :            :       // re.++( ..., empty, ... ) ---> empty
     800                 :         35 :       Node ret = nm->mkNode(Kind::REGEXP_NONE);
     801                 :         35 :       return returnRewrite(node, ret, Rewrite::RE_CONCAT_EMPTY);
     802                 :         35 :     }
     803                 :            :     else
     804                 :            :     {
     805                 :      28647 :       vec.push_back(c);
     806                 :            :     }
     807         [ +  + ]:      29214 :   }
     808         [ +  + ]:       8946 :   if (changed)
     809                 :            :   {
     810                 :            :     // flatten
     811                 :            :     // this handles nested re.++ and elimination or str.to.re(""), e.g.:
     812                 :            :     // re.++( re.++( R1, R2 ), str.to.re(""), R3 ) ---> re.++( R1, R2, R3 )
     813         [ +  + ]:        508 :     if (vec.empty())
     814                 :            :     {
     815 [ -  + ][ -  + ]:          4 :       Assert(!emptyRe.isNull());
                 [ -  - ]
     816                 :          4 :       retNode = emptyRe;
     817                 :            :     }
     818                 :            :     else
     819                 :            :     {
     820         [ +  + ]:        504 :       retNode = vec.size() == 1 ? vec[0] : nm->mkNode(Kind::REGEXP_CONCAT, vec);
     821                 :            :     }
     822                 :        508 :     return returnRewrite(node, retNode, Rewrite::RE_CONCAT_FLATTEN);
     823                 :            :   }
     824         [ +  - ]:      16876 :   Trace("strings-rewrite-debug")
     825                 :       8438 :       << "Strings::rewriteConcatRegExp start " << node << std::endl;
     826                 :       8438 :   std::vector<Node> cvec;
     827                 :            :   // the current accumulation of constant strings
     828                 :       8438 :   std::vector<Node> preReStr;
     829                 :            :   // whether the last component was (_)*
     830                 :       8438 :   bool lastAllStar = false;
     831                 :       8438 :   String emptyStr = String("");
     832                 :            :   // this loop checks to see if components can be combined or dropped
     833         [ +  + ]:      44812 :   for (unsigned i = 0, size = vec.size(); i <= size; i++)
     834                 :            :   {
     835                 :      36374 :     Node curr;
     836         [ +  + ]:      36374 :     if (i < size)
     837                 :            :     {
     838                 :      27936 :       curr = vec[i];
     839 [ -  + ][ -  + ]:      27936 :       Assert(curr.getKind() != Kind::REGEXP_CONCAT);
                 [ -  - ]
     840                 :            :     }
     841                 :            :     // update preReStr
     842 [ +  + ][ +  + ]:      36374 :     if (!curr.isNull() && curr.getKind() == Kind::STRING_TO_REGEXP)
                 [ +  + ]
     843                 :            :     {
     844                 :       8415 :       lastAllStar = false;
     845                 :       8415 :       preReStr.push_back(curr[0]);
     846                 :       8415 :       curr = Node::null();
     847                 :            :     }
     848         [ +  + ]:      27959 :     else if (!preReStr.empty())
     849                 :            :     {
     850 [ -  + ][ -  + ]:       7890 :       Assert(!lastAllStar);
                 [ -  - ]
     851                 :            :       // this groups consecutive strings a++b ---> ab
     852                 :            :       Node acc =
     853                 :      15780 :           nm->mkNode(Kind::STRING_TO_REGEXP, utils::mkConcat(preReStr, stype));
     854                 :       7890 :       cvec.push_back(acc);
     855                 :       7890 :       preReStr.clear();
     856                 :       7890 :     }
     857 [ +  + ][ +  + ]:      20069 :     else if (!curr.isNull() && lastAllStar)
                 [ +  + ]
     858                 :            :     {
     859                 :            :       // if empty, drop it
     860                 :            :       // e.g. this ensures we rewrite (_)* ++ (a)* ---> (_)*
     861 [ +  + ][ -  - ]:        401 :       if (RegExpEntail::isConstRegExp(curr)
     862 [ +  + ][ +  + ]:        401 :           && RegExpEntail::testConstStringInRegExp(emptyStr, curr))
         [ +  + ][ +  - ]
                 [ -  - ]
     863                 :            :       {
     864                 :        194 :         curr = Node::null();
     865                 :            :       }
     866                 :            :     }
     867         [ +  + ]:      36374 :     if (!curr.isNull())
     868                 :            :     {
     869                 :      19327 :       lastAllStar = false;
     870         [ +  + ]:      19327 :       if (curr.getKind() == Kind::REGEXP_STAR)
     871                 :            :       {
     872                 :            :         // we can group stars (a)* ++ (a)* ---> (a)*
     873 [ +  + ][ +  + ]:       8418 :         if (!cvec.empty() && cvec.back() == curr)
                 [ +  + ]
     874                 :            :         {
     875                 :         41 :           curr = Node::null();
     876                 :            :         }
     877         [ +  + ]:       8377 :         else if (curr[0].getKind() == Kind::REGEXP_ALLCHAR)
     878                 :            :         {
     879 [ -  + ][ -  + ]:       3329 :           Assert(!lastAllStar);
                 [ -  - ]
     880                 :       3329 :           lastAllStar = true;
     881                 :            :           // go back and remove empty ones from back of cvec
     882                 :            :           // e.g. this ensures we rewrite (a)* ++ (_)* ---> (_)*
     883                 :       3329 :           while (
     884 [ +  + ][ -  - ]:       5050 :               !cvec.empty() && RegExpEntail::isConstRegExp(cvec.back())
     885 [ +  + ][ +  + ]:       8441 :               && RegExpEntail::testConstStringInRegExp(emptyStr, cvec.back()))
         [ +  + ][ +  + ]
         [ +  + ][ -  - ]
     886                 :            :           {
     887                 :         62 :             cvec.pop_back();
     888                 :            :           }
     889                 :            :         }
     890                 :            :       }
     891                 :            :     }
     892         [ +  + ]:      36374 :     if (!curr.isNull())
     893                 :            :     {
     894                 :      19286 :       cvec.push_back(curr);
     895                 :            :     }
     896                 :      36374 :   }
     897 [ -  + ][ -  + ]:       8438 :   Assert(!cvec.empty());
                 [ -  - ]
     898                 :       8438 :   retNode = utils::mkConcat(cvec, rtype);
     899         [ +  + ]:       8438 :   if (retNode != node)
     900                 :            :   {
     901                 :            :     // handles all cases where consecutive re constants are combined or
     902                 :            :     // dropped as described in the loop above.
     903                 :        375 :     return returnRewrite(node, retNode, Rewrite::RE_CONCAT);
     904                 :            :   }
     905                 :            : 
     906                 :            :   // flipping adjacent star arguments
     907                 :       8063 :   changed = false;
     908         [ +  + ]:      26145 :   for (size_t i = 0, size = cvec.size() - 1; i < size; i++)
     909                 :            :   {
     910 [ +  + ][ +  + ]:      18082 :     if (cvec[i].getKind() == Kind::REGEXP_STAR && cvec[i][0] == cvec[i + 1])
         [ +  + ][ +  + ]
                 [ -  - ]
     911                 :            :     {
     912                 :            :       // by convention, flip the order (a*)++a ---> a++(a*)
     913                 :        195 :       std::swap(cvec[i], cvec[i + 1]);
     914                 :        195 :       changed = true;
     915                 :            :     }
     916                 :            :   }
     917         [ +  + ]:       8063 :   if (changed)
     918                 :            :   {
     919                 :        104 :     retNode = utils::mkConcat(cvec, rtype);
     920                 :        104 :     return returnRewrite(node, retNode, Rewrite::RE_CONCAT_OPT);
     921                 :            :   }
     922                 :       7959 :   return node;
     923                 :       8981 : }
     924                 :            : 
     925                 :       4827 : Node SequencesRewriter::rewriteStarRegExp(TNode node)
     926                 :            : {
     927 [ -  + ][ -  + ]:       4827 :   Assert(node.getKind() == Kind::REGEXP_STAR);
                 [ -  - ]
     928                 :       4827 :   NodeManager* nm = nodeManager();
     929                 :       4827 :   Node retNode = node;
     930         [ +  + ]:       4827 :   if (node[0].getKind() == Kind::REGEXP_STAR)
     931                 :            :   {
     932                 :            :     // ((R)*)* ---> R*
     933                 :         32 :     return returnRewrite(node, node[0], Rewrite::RE_STAR_NESTED_STAR);
     934                 :            :   }
     935                 :       6385 :   else if (node[0].getKind() == Kind::STRING_TO_REGEXP && node[0][0].isConst()
     936                 :       6385 :            && Word::isEmpty(node[0][0]))
     937                 :            :   {
     938                 :            :     // ("")* ---> ""
     939                 :         18 :     return returnRewrite(node, node[0], Rewrite::RE_STAR_EMPTY_STRING);
     940                 :            :   }
     941         [ +  + ]:       4777 :   else if (node[0].getKind() == Kind::REGEXP_NONE)
     942                 :            :   {
     943                 :            :     // (empty)* ---> ""
     944                 :         28 :     retNode = nm->mkNode(Kind::STRING_TO_REGEXP, nm->mkConst(String("")));
     945                 :         28 :     return returnRewrite(node, retNode, Rewrite::RE_STAR_EMPTY);
     946                 :            :   }
     947         [ +  + ]:       4749 :   else if (node[0].getKind() == Kind::REGEXP_UNION)
     948                 :            :   {
     949         [ +  + ]:       2304 :     for (const Node& nc : node[0])
     950                 :            :     {
     951         [ +  + ]:       1628 :       if (nc.getKind() == Kind::REGEXP_ALLCHAR)
     952                 :            :       {
     953                 :            :         // (re.* (re.union ... re.allchar ...)) ---> (re.* re.allchar)
     954                 :         12 :         retNode = nm->mkNode(Kind::REGEXP_STAR, nc);
     955                 :         12 :         return returnRewrite(node, retNode, Rewrite::RE_STAR_UNION_CHAR);
     956                 :            :       }
     957 [ +  + ][ +  + ]:       2316 :     }
     958                 :            :     // simplification of unions under star
     959         [ +  + ]:        676 :     if (RegExpEntail::hasEpsilonNode(node[0]))
     960                 :            :     {
     961                 :         26 :       bool changed = false;
     962                 :         26 :       std::vector<Node> node_vec;
     963         [ +  + ]:         78 :       for (const Node& nc : node[0])
     964                 :            :       {
     965                 :        103 :         if (nc.getKind() == Kind::STRING_TO_REGEXP && nc[0].isConst()
     966                 :        103 :             && Word::isEmpty(nc[0]))
     967                 :            :         {
     968                 :            :           // can be removed
     969                 :         26 :           changed = true;
     970                 :            :         }
     971                 :            :         else
     972                 :            :         {
     973                 :         26 :           node_vec.push_back(nc);
     974                 :            :         }
     975                 :         78 :       }
     976         [ +  - ]:         26 :       if (changed)
     977                 :            :       {
     978                 :         26 :         retNode = node_vec.size() == 1
     979         [ +  - ]:         52 :                       ? node_vec[0]
     980                 :         26 :                       : nm->mkNode(Kind::REGEXP_UNION, node_vec);
     981                 :         26 :         retNode = nm->mkNode(Kind::REGEXP_STAR, retNode);
     982                 :            :         // simplification of union beneath star based on loop above
     983                 :            :         // for example, ( "" | "a" )* ---> ("a")*
     984                 :         26 :         return returnRewrite(node, retNode, Rewrite::RE_STAR_UNION);
     985                 :            :       }
     986         [ -  + ]:         26 :     }
     987                 :            :   }
     988                 :       4711 :   return node;
     989                 :       4827 : }
     990                 :            : 
     991                 :      23913 : Node SequencesRewriter::rewriteViaMacroReInterUnionInclusion(const Node& node)
     992                 :            : {
     993                 :      23913 :   Kind nk = node.getKind();
     994 [ +  + ][ +  + ]:      23913 :   if (nk != Kind::REGEXP_UNION && nk != Kind::REGEXP_INTER)
     995                 :            :   {
     996                 :      19644 :     return Node::null();
     997                 :            :   }
     998         [ +  + ]:      25614 :   std::vector<Node> polRegExp[2];
     999         [ +  + ]:      14550 :   for (const Node& ni : node)
    1000                 :            :   {
    1001                 :      10281 :     Kind nik = ni.getKind();
    1002         [ +  + ]:      10281 :     uint32_t pindex = nik == Kind::REGEXP_COMPLEMENT ? 1 : 0;
    1003         [ +  + ]:      10281 :     Node nia = pindex == 1 ? ni[0] : ni;
    1004                 :      10281 :     polRegExp[pindex].push_back(nia);
    1005                 :      10281 :   }
    1006         [ +  + ]:       5796 :   for (const Node& negMem : polRegExp[1])
    1007                 :            :   {
    1008         [ +  + ]:       2956 :     for (const Node& posMem : polRegExp[0])
    1009                 :            :     {
    1010         [ +  + ]:       1429 :       Node m1 = nk == Kind::REGEXP_INTER ? negMem : posMem;
    1011         [ +  + ]:       1429 :       Node m2 = nk == Kind::REGEXP_INTER ? posMem : negMem;
    1012                 :            :       // inclusion test for conflicting case m1 contains m2
    1013                 :            :       // (re.inter (re.comp R1) R2) --> re.none where R1 includes R2
    1014                 :            :       // (re.union R1 (re.comp R2)) --> (re.* re.allchar) where R1 includes R2
    1015         [ +  + ]:       1429 :       if (RegExpEntail::regExpIncludes(m1, m2))
    1016                 :            :       {
    1017                 :        266 :         NodeManager* nm = nodeManager();
    1018                 :            :         Node retNode = nk == Kind::REGEXP_INTER
    1019                 :            :                            ? nm->mkNode(Kind::REGEXP_NONE)
    1020                 :            :                            : nm->mkNode(Kind::REGEXP_STAR,
    1021                 :        317 :                                         nm->mkNode(Kind::REGEXP_ALLCHAR));
    1022                 :        266 :         std::vector<Node> newChildren;
    1023                 :        266 :         newChildren.push_back(retNode);
    1024                 :            :         // Now go back and include all the remaining children that were
    1025                 :            :         // not involved. This simplifies proof checking, since we can isolate
    1026                 :            :         // which children led to the conflict.
    1027                 :            :         // In particular, if Ri includes Rj, then we rewrite
    1028                 :            :         //    (re.inter R1 ... (re.comp Ri) ... Rj ... Rn)
    1029                 :            :         // to
    1030                 :            :         //    (re.inter re.none R1...R{i-1} R{i+1} ... R{j-1} R{j+1} .. Rn)
    1031                 :            :         // where the latter will be rewritten to re.none.
    1032                 :        266 :         bool foundPos = false;
    1033                 :        266 :         bool foundNeg = false;
    1034         [ +  + ]:        849 :         for (const Node& nc : node)
    1035                 :            :         {
    1036 [ +  + ][ +  + ]:        583 :           if (!foundPos && nc == posMem)
                 [ +  + ]
    1037                 :            :           {
    1038                 :        266 :             foundPos = true;
    1039                 :        266 :             continue;
    1040                 :            :           }
    1041         [ +  + ]:        282 :           if (!foundNeg && nc.getKind() == Kind::REGEXP_COMPLEMENT
    1042 [ +  + ][ +  - ]:        599 :               && nc[0] == negMem)
         [ +  + ][ +  + ]
                 [ -  - ]
    1043                 :            :           {
    1044                 :        266 :             foundNeg = true;
    1045                 :        266 :             continue;
    1046                 :            :           }
    1047                 :         51 :           newChildren.push_back(nc);
    1048         [ +  + ]:        583 :         }
    1049         [ +  + ]:        266 :         if (newChildren.size() > 1)
    1050                 :            :         {
    1051                 :         51 :           retNode = nm->mkNode(nk, newChildren);
    1052                 :            :         }
    1053                 :        266 :         return retNode;
    1054                 :        266 :       }
    1055 [ +  + ][ +  + ]:       1695 :     }
    1056                 :            :   }
    1057                 :       4003 :   return Node::null();
    1058 [ +  + ][ -  - ]:      12807 : }
    1059                 :            : 
    1060                 :         83 : Node SequencesRewriter::rewriteViaReInterUnionInclusion(ProofRewriteRule id,
    1061                 :            :                                                         const Node& n)
    1062                 :            : {
    1063 [ +  - ][ -  + ]:         83 :   if (n.getNumChildren() != 2 || n[1].getKind() != Kind::REGEXP_COMPLEMENT)
         [ +  - ][ -  + ]
                 [ -  - ]
    1064                 :            :   {
    1065                 :          0 :     return Node::null();
    1066                 :            :   }
    1067                 :         83 :   Kind k = n.getKind();
    1068         [ +  + ]:         83 :   if (id == ProofRewriteRule::RE_INTER_INCLUSION)
    1069                 :            :   {
    1070                 :         71 :     if (k == Kind::REGEXP_INTER && RegExpEntail::regExpIncludes(n[1][0], n[0]))
    1071                 :            :     {
    1072                 :         71 :       return nodeManager()->mkNode(Kind::REGEXP_NONE);
    1073                 :            :     }
    1074                 :            :   }
    1075                 :            :   else
    1076                 :            :   {
    1077 [ -  + ][ -  + ]:         12 :     Assert(id == ProofRewriteRule::RE_UNION_INCLUSION);
                 [ -  - ]
    1078                 :         12 :     if (k == Kind::REGEXP_UNION && RegExpEntail::regExpIncludes(n[0], n[1][0]))
    1079                 :            :     {
    1080                 :         12 :       NodeManager* nm = nodeManager();
    1081                 :         12 :       return nm->mkNode(Kind::REGEXP_STAR, nm->mkNode(Kind::REGEXP_ALLCHAR));
    1082                 :            :     }
    1083                 :            :   }
    1084                 :          0 :   return Node::null();
    1085                 :            : }
    1086                 :            : 
    1087                 :       4515 : Node SequencesRewriter::rewriteAndOrRegExp(TNode node)
    1088                 :            : {
    1089                 :       4515 :   Kind nk = node.getKind();
    1090 [ +  + ][ +  - ]:       4515 :   Assert(nk == Kind::REGEXP_UNION || nk == Kind::REGEXP_INTER);
         [ -  + ][ -  + ]
                 [ -  - ]
    1091         [ +  - ]:       9030 :   Trace("strings-rewrite-debug")
    1092                 :       4515 :       << "Strings::rewriteAndOrRegExp start " << node << std::endl;
    1093                 :       4515 :   NodeManager* nm = nodeManager();
    1094                 :       4515 :   std::vector<Node> node_vec;
    1095                 :            :   // list of constant string regular expressions (str.to_re c)
    1096                 :       4515 :   std::vector<Node> constStrRe;
    1097                 :       4515 :   bool changed = false;
    1098         [ +  + ]:      15206 :   for (const Node& ni : node)
    1099                 :            :   {
    1100                 :      10772 :     Kind nik = ni.getKind();
    1101         [ +  + ]:      10772 :     if (nik == nk)
    1102                 :            :     {
    1103         [ +  + ]:        232 :       for (const Node& nic : ni)
    1104                 :            :       {
    1105         [ +  + ]:        162 :         if (std::find(node_vec.begin(), node_vec.end(), nic) == node_vec.end())
    1106                 :            :         {
    1107                 :        160 :           node_vec.push_back(nic);
    1108                 :            :         }
    1109                 :        162 :       }
    1110                 :         70 :       changed = true;
    1111                 :            :     }
    1112         [ +  + ]:      10702 :     else if (nik == Kind::REGEXP_NONE)
    1113                 :            :     {
    1114         [ +  + ]:         52 :       if (nk == Kind::REGEXP_INTER)
    1115                 :            :       {
    1116                 :         22 :         return returnRewrite(node, ni, Rewrite::RE_AND_EMPTY);
    1117                 :            :       }
    1118                 :            :       // otherwise, can ignore
    1119                 :         30 :       changed = true;
    1120                 :            :     }
    1121                 :      10650 :     else if (nik == Kind::REGEXP_STAR
    1122 [ +  + ][ +  + ]:      10650 :              && ni[0].getKind() == Kind::REGEXP_ALLCHAR)
         [ +  + ][ +  + ]
                 [ -  - ]
    1123                 :            :     {
    1124         [ +  + ]:         68 :       if (nk == Kind::REGEXP_UNION)
    1125                 :            :       {
    1126                 :         46 :         return returnRewrite(node, ni, Rewrite::RE_OR_ALL);
    1127                 :            :       }
    1128                 :            :       // otherwise, can ignore
    1129                 :         22 :       changed = true;
    1130                 :            :     }
    1131         [ +  + ]:      10582 :     else if (std::find(node_vec.begin(), node_vec.end(), ni) == node_vec.end())
    1132                 :            :     {
    1133 [ +  + ][ +  + ]:      10552 :       if (nik == Kind::STRING_TO_REGEXP && ni[0].isConst())
         [ +  + ][ +  + ]
                 [ -  - ]
    1134                 :            :       {
    1135         [ +  + ]:       3744 :         if (nk == Kind::REGEXP_INTER)
    1136                 :            :         {
    1137         [ +  + ]:        353 :           if (!constStrRe.empty())
    1138                 :            :           {
    1139 [ -  + ][ -  + ]:         13 :             Assert(constStrRe[0][0] != ni[0]);
                 [ -  - ]
    1140                 :            :             // (re.inter .. (str.to_re c1) .. (str.to_re c2) ..) ---> re.none
    1141                 :            :             // for distinct constant strings c1, c2.
    1142                 :         13 :             Node ret = nm->mkNode(Kind::REGEXP_NONE);
    1143                 :            :             return returnRewrite(
    1144                 :         13 :                 node, ret, Rewrite::RE_INTER_CONST_CONST_CONFLICT);
    1145                 :         13 :           }
    1146                 :            :         }
    1147                 :            :         else
    1148                 :            :         {
    1149 [ -  + ][ -  + ]:       3391 :           Assert(nk == Kind::REGEXP_UNION);
                 [ -  - ]
    1150                 :            :         }
    1151                 :       3731 :         constStrRe.push_back(ni);
    1152                 :            :       }
    1153                 :      10539 :       node_vec.push_back(ni);
    1154                 :            :     }
    1155                 :            :     else
    1156                 :            :     {
    1157                 :         30 :       changed = true;
    1158                 :            :     }
    1159         [ +  + ]:      10772 :   }
    1160                 :            :   // if we already changed due to flattening, return already
    1161         [ +  + ]:       4434 :   if (changed)
    1162                 :            :   {
    1163                 :        147 :     Node retNode = node;
    1164         [ -  + ]:        147 :     if (node_vec.empty())
    1165                 :            :     {
    1166         [ -  - ]:          0 :       if (nk == Kind::REGEXP_INTER)
    1167                 :            :       {
    1168                 :            :         retNode =
    1169                 :          0 :             nm->mkNode(Kind::REGEXP_STAR, nm->mkNode(Kind::REGEXP_ALLCHAR));
    1170                 :            :       }
    1171                 :            :       else
    1172                 :            :       {
    1173                 :          0 :         retNode = nm->mkNode(Kind::REGEXP_NONE);
    1174                 :            :       }
    1175                 :            :     }
    1176                 :            :     else
    1177                 :            :     {
    1178         [ +  + ]:        147 :       retNode = node_vec.size() == 1 ? node_vec[0] : nm->mkNode(nk, node_vec);
    1179                 :            :     }
    1180         [ +  - ]:        147 :     if (retNode != node)
    1181                 :            :     {
    1182                 :            :       // flattening and removing children, based on loop above
    1183                 :        147 :       return returnRewrite(node, retNode, Rewrite::RE_ANDOR_FLATTEN);
    1184                 :            :     }
    1185         [ -  + ]:        147 :   }
    1186                 :            :   // try to eliminate components via constant membership tests
    1187                 :       4287 :   Node conflict;
    1188                 :       4287 :   Node retNode = rewriteViaMacroReInterUnionConstElim(node, conflict);
    1189         [ +  + ]:       4287 :   if (!retNode.isNull())
    1190                 :            :   {
    1191                 :        231 :     return returnRewrite(node, retNode, Rewrite::RE_ANDOR_CONST_REMOVE);
    1192                 :            :   }
    1193                 :            : 
    1194                 :            :   // use inclusion tests
    1195                 :       4056 :   retNode = rewriteViaMacroReInterUnionInclusion(node);
    1196         [ +  + ]:       4056 :   if (!retNode.isNull())
    1197                 :            :   {
    1198                 :        152 :     return returnRewrite(node, retNode, Rewrite::RE_ANDOR_INC_CONFLICT);
    1199                 :            :   }
    1200                 :            : 
    1201                 :            :   // otherwise there is no change
    1202                 :       3904 :   return node;
    1203                 :       4515 : }
    1204                 :            : 
    1205                 :        155 : Node SequencesRewriter::rewriteLoopRegExp(TNode node)
    1206                 :            : {
    1207 [ -  + ][ -  + ]:        155 :   Assert(node.getKind() == Kind::REGEXP_LOOP);
                 [ -  - ]
    1208                 :        155 :   uint32_t l = utils::getLoopMinOccurrences(node);
    1209                 :        155 :   uint32_t u = utils::getLoopMaxOccurrences(node);
    1210                 :        155 :   Node r = node[0];
    1211                 :        155 :   Node retNode = node;
    1212                 :            : 
    1213                 :        155 :   NodeManager* nm = nodeManager();
    1214         [ +  + ]:        155 :   if (u < l)
    1215                 :            :   {
    1216                 :            :     // ((_ re.loop l u) r) --> re.none if u < l
    1217                 :         23 :     std::vector<Node> nvec;
    1218                 :         23 :     retNode = nm->mkNode(Kind::REGEXP_NONE, nvec);
    1219                 :         23 :     return returnRewrite(node, retNode, Rewrite::RE_LOOP_NONE);
    1220                 :         23 :   }
    1221         [ +  + ]:        132 :   else if (u == 0)
    1222                 :            :   {
    1223                 :          2 :     retNode = nm->mkNode(Kind::STRING_TO_REGEXP, nm->mkConst(String("")));
    1224                 :          2 :     return returnRewrite(node, retNode, Rewrite::RE_LOOP_ZERO);
    1225                 :            :   }
    1226         [ +  + ]:        130 :   else if (r.getKind() == Kind::REGEXP_STAR)
    1227                 :            :   {
    1228                 :         10 :     return returnRewrite(node, r, Rewrite::RE_LOOP_STAR);
    1229                 :            :   }
    1230                 :        120 :   retNode = rewriteViaReLoopElim(node);
    1231 [ +  - ][ +  - ]:        120 :   Assert(!retNode.isNull() && retNode != node);
         [ -  + ][ -  + ]
                 [ -  - ]
    1232                 :        120 :   return returnRewrite(node, retNode, Rewrite::RE_LOOP);
    1233                 :        155 : }
    1234                 :            : 
    1235                 :      73537 : Node SequencesRewriter::rewriteViaStrEqLenUnifyPrefix(const Node& node)
    1236                 :            : {
    1237                 :      73537 :   Node newRet;
    1238         [ +  + ]:     220233 :   for (unsigned i = 0; i < 2; i++)
    1239                 :            :   {
    1240         [ +  + ]:     146910 :     if (node[1 - i].getKind() == Kind::STRING_CONCAT)
    1241                 :            :     {
    1242                 :       2047 :       newRet = d_stringsEntail.inferEqsFromContains(node[i], node[1 - i]);
    1243                 :            :       // don't rewrite if just returning a (flipped) equality
    1244 [ +  + ][ +  + ]:       2047 :       if (!newRet.isNull() && newRet.getKind() == Kind::AND)
                 [ +  + ]
    1245                 :            :       {
    1246         [ +  + ]:        214 :         if (i == 1)
    1247                 :            :         {
    1248                 :            :           // flip the first equality back
    1249                 :         50 :           std::vector<Node> nc(newRet.begin(), newRet.end());
    1250                 :         50 :           nc[0] = nc[0][1].eqNode(nc[0][0]);
    1251                 :         50 :           newRet = nodeManager()->mkNode(Kind::AND, nc);
    1252                 :         50 :         }
    1253                 :        214 :         return newRet;
    1254                 :            :       }
    1255                 :            :     }
    1256                 :            :   }
    1257                 :      73323 :   return Node::null();
    1258                 :      73537 : }
    1259                 :            : 
    1260                 :      73341 : Node SequencesRewriter::rewriteViaStrEqLenUnify(const Node& node, Rewrite& rule)
    1261                 :            : {
    1262 [ +  + ][ -  - ]:      73341 :   if (node[0].getKind() == Kind::STRING_CONCAT
    1263 [ +  + ][ +  + ]:      73341 :       && node[1].getKind() == Kind::STRING_CONCAT)
         [ +  + ][ +  - ]
                 [ -  - ]
    1264                 :            :   {
    1265                 :        243 :     std::vector<Node> v0, v1;
    1266                 :        243 :     utils::getConcat(node[0], v0);
    1267                 :        243 :     utils::getConcat(node[1], v1);
    1268                 :        243 :     size_t startRhs = 0;
    1269                 :        243 :     TypeNode stype = node[0].getType();
    1270         [ +  + ]:       1052 :     for (size_t i = 0, size0 = v0.size(); i <= size0; i++)
    1271                 :            :     {
    1272                 :        894 :       const std::vector<Node> pfxv0(v0.begin(), v0.begin() + i);
    1273                 :        894 :       Node pfx0 = utils::mkConcat(pfxv0, stype);
    1274         [ +  + ]:       3613 :       for (size_t j = startRhs, size1 = v1.size(); j <= size1; j++)
    1275                 :            :       {
    1276 [ +  + ][ +  + ]:       3137 :         if (!(i == 0 && j == 0) && !(i == v0.size() && j == v1.size()))
         [ +  + ][ +  + ]
                 [ +  + ]
    1277                 :            :         {
    1278                 :       2736 :           std::vector<Node> pfxv1(v1.begin(), v1.begin() + j);
    1279                 :       2736 :           Node pfx1 = utils::mkConcat(pfxv1, stype);
    1280                 :       2736 :           Node lenPfx0 = d_nm->mkNode(Kind::STRING_LENGTH, pfx0);
    1281                 :       2736 :           Node lenPfx1 = d_nm->mkNode(Kind::STRING_LENGTH, pfx1);
    1282                 :            : 
    1283         [ +  + ]:       2736 :           if (d_arithEntail.checkEq(lenPfx0, lenPfx1))
    1284                 :            :           {
    1285                 :         83 :             std::vector<Node> sfxv0(v0.begin() + i, v0.end());
    1286                 :         83 :             std::vector<Node> sfxv1(v1.begin() + j, v1.end());
    1287                 :            :             Node ret =
    1288                 :        249 :                 d_nm->mkNode(Kind::AND,
    1289                 :         83 :                              {pfx0.eqNode(pfx1),
    1290                 :        166 :                               utils::mkConcat(sfxv0, stype)
    1291                 :        249 :                                   .eqNode(utils::mkConcat(sfxv1, stype))});
    1292                 :         83 :             rule = Rewrite::SPLIT_EQ;
    1293                 :         83 :             return ret;
    1294                 :         83 :           }
    1295         [ +  + ]:       2653 :           else if (d_arithEntail.check(lenPfx1, lenPfx0, true))
    1296                 :            :           {
    1297                 :            :             // The prefix on the right-hand side is strictly longer than the
    1298                 :            :             // prefix on the left-hand side, so we try to strip the right-hand
    1299                 :            :             // prefix by the length of the left-hand prefix
    1300                 :            :             //
    1301                 :            :             // Example:
    1302                 :            :             // (= (str.++ "A" x y) (str.++ x "AB" z)) --->
    1303                 :            :             //   (and (= (str.++ "A" x) (str.++ x "A")) (= y (str.++ "B" z)))
    1304                 :        333 :             std::vector<Node> rpfxv1;
    1305         [ -  + ]:        333 :             if (d_stringsEntail.stripSymbolicLength(
    1306                 :            :                     pfxv1, rpfxv1, 1, lenPfx0, true))
    1307                 :            :             {
    1308                 :            :               // The rewrite requires the full left-hand prefix length to be
    1309                 :            :               // stripped (otherwise we would have to keep parts of the
    1310                 :            :               // left-hand prefix).
    1311                 :          0 :               if (lenPfx0.isConst() && lenPfx0.getConst<Rational>().isZero())
    1312                 :            :               {
    1313                 :          0 :                 std::vector<Node> sfxv0(v0.begin() + i, v0.end());
    1314                 :          0 :                 pfxv1.insert(pfxv1.end(), v1.begin() + j, v1.end());
    1315                 :            :                 Node ret =
    1316                 :          0 :                     d_nm->mkNode(Kind::AND,
    1317                 :          0 :                                  {pfx0.eqNode(utils::mkConcat(rpfxv1, stype)),
    1318                 :          0 :                                   utils::mkConcat(sfxv0, stype)
    1319                 :          0 :                                       .eqNode(utils::mkConcat(pfxv1, stype))});
    1320                 :          0 :                 rule = Rewrite::SPLIT_EQ_STRIP_R;
    1321                 :          0 :                 return ret;
    1322                 :          0 :               }
    1323                 :            :             }
    1324                 :            : 
    1325                 :            :             // If the prefix of the right-hand side is (strictly) longer than
    1326                 :            :             // the prefix of the left-hand side, we can advance the left-hand
    1327                 :            :             // side (since the length of the right-hand side is only increasing
    1328                 :            :             // in the inner loop)
    1329                 :        333 :             break;
    1330         [ -  + ]:        333 :           }
    1331         [ +  + ]:       2320 :           else if (d_arithEntail.check(lenPfx0, lenPfx1, true))
    1332                 :            :           {
    1333                 :            :             // The prefix on the left-hand side is strictly longer than the
    1334                 :            :             // prefix on the right-hand side, so we try to strip the left-hand
    1335                 :            :             // prefix by the length of the right-hand prefix
    1336                 :            :             //
    1337                 :            :             // Example:
    1338                 :            :             // (= (str.++ x "AB" z) (str.++ "A" x y)) --->
    1339                 :            :             //   (and (= (str.++ x "A") (str.++ "A" x)) (= (str.++ "B" z) y))
    1340                 :        311 :             std::vector<Node> sfxv0 = pfxv0;
    1341                 :        311 :             std::vector<Node> rpfxv0;
    1342         [ +  + ]:        311 :             if (d_stringsEntail.stripSymbolicLength(
    1343                 :            :                     sfxv0, rpfxv0, 1, lenPfx1, true))
    1344                 :            :             {
    1345                 :            :               // The rewrite requires the full right-hand prefix length to be
    1346                 :            :               // stripped (otherwise we would have to keep parts of the
    1347                 :            :               // right-hand prefix).
    1348 [ +  - ][ +  - ]:          2 :               if (lenPfx1.isConst() && lenPfx1.getConst<Rational>().isZero())
                 [ +  - ]
    1349                 :            :               {
    1350                 :          2 :                 sfxv0.insert(sfxv0.end(), v0.begin() + i, v0.end());
    1351                 :          2 :                 std::vector<Node> sfxv1(v1.begin() + j, v1.end());
    1352                 :            :                 Node ret =
    1353                 :          6 :                     d_nm->mkNode(Kind::AND,
    1354                 :          4 :                                  {utils::mkConcat(rpfxv0, stype).eqNode(pfx1),
    1355                 :          4 :                                   utils::mkConcat(sfxv0, stype)
    1356                 :          6 :                                       .eqNode(utils::mkConcat(sfxv1, stype))});
    1357                 :          2 :                 rule = Rewrite::SPLIT_EQ_STRIP_L;
    1358                 :          2 :                 return ret;
    1359                 :          2 :               }
    1360                 :            :             }
    1361                 :            : 
    1362                 :            :             // If the prefix of the left-hand side is (strictly) longer than
    1363                 :            :             // the prefix of the right-hand side, then we don't need to check
    1364                 :            :             // that right-hand prefix for future left-hand prefixes anymore
    1365                 :            :             // (since they are increasing in length)
    1366                 :        309 :             startRhs = j + 1;
    1367 [ +  + ][ +  + ]:        313 :           }
    1368 [ +  + ][ +  + ]:       3990 :         }
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
    1369                 :            :       }
    1370 [ +  + ][ +  + ]:        979 :     }
    1371 [ +  + ][ +  + ]:        413 :   }
                 [ +  + ]
    1372                 :      73256 :   return Node::null();
    1373                 :            : }
    1374                 :            : 
    1375                 :      19989 : Node SequencesRewriter::rewriteViaReLoopElim(const Node& node)
    1376                 :            : {
    1377         [ +  + ]:      19989 :   if (node.getKind() != Kind::REGEXP_LOOP)
    1378                 :            :   {
    1379                 :      19797 :     return Node::null();
    1380                 :            :   }
    1381                 :        192 :   uint32_t l = utils::getLoopMinOccurrences(node);
    1382                 :        192 :   uint32_t u = utils::getLoopMaxOccurrences(node);
    1383         [ +  + ]:        192 :   if (u < l)
    1384                 :            :   {
    1385                 :          6 :     return Node::null();
    1386                 :            :   }
    1387                 :        186 :   Node r = node[0];
    1388                 :        186 :   std::vector<Node> vec_nodes;
    1389         [ +  + ]:        801 :   for (unsigned i = 0; i < l; i++)
    1390                 :            :   {
    1391                 :        615 :     vec_nodes.push_back(r);
    1392                 :            :   }
    1393                 :        186 :   NodeManager* nm = nodeManager();
    1394                 :        186 :   Node n = vec_nodes.size() == 0
    1395                 :        220 :                ? nm->mkNode(Kind::STRING_TO_REGEXP, nm->mkConst(String("")))
    1396                 :        152 :            : vec_nodes.size() == 1 ? r
    1397 [ +  + ][ +  + ]:        406 :                                    : nm->mkNode(Kind::REGEXP_CONCAT, vec_nodes);
         [ +  + ][ +  + ]
                 [ -  - ]
    1398                 :        186 :   Node retNode;
    1399         [ +  + ]:        186 :   if (u == l)
    1400                 :            :   {
    1401                 :        119 :     retNode = n;
    1402                 :            :   }
    1403                 :            :   else
    1404                 :            :   {
    1405                 :         67 :     std::vector<Node> vec2;
    1406                 :         67 :     vec2.push_back(n);
    1407                 :         67 :     TypeNode rtype = nm->regExpType();
    1408         [ +  + ]:        448 :     for (uint32_t j = l; j < u; j++)
    1409                 :            :     {
    1410                 :        381 :       vec_nodes.push_back(r);
    1411                 :        381 :       n = utils::mkConcat(vec_nodes, rtype);
    1412                 :        381 :       vec2.push_back(n);
    1413                 :            :     }
    1414                 :         67 :     retNode = nm->mkNode(Kind::REGEXP_UNION, vec2);
    1415                 :         67 :   }
    1416         [ +  - ]:        372 :   Trace("strings-lp") << "Strings::lp " << node << " => " << retNode
    1417                 :        186 :                       << std::endl;
    1418 [ -  + ][ -  + ]:        186 :   Assert(retNode != node);
                 [ -  - ]
    1419                 :        186 :   return retNode;
    1420                 :        186 : }
    1421                 :            : 
    1422                 :      19820 : Node SequencesRewriter::rewriteViaReEqElim(const Node& n)
    1423                 :            : {
    1424                 :      19820 :   if (n.getKind() != Kind::EQUAL || !n[0].getType().isRegExp())
    1425                 :            :   {
    1426                 :      19802 :     return Node::null();
    1427                 :            :   }
    1428                 :         18 :   NodeManager* nm = nodeManager();
    1429                 :         18 :   Node v = SkolemCache::mkRegExpEqVar(nm, n);
    1430                 :         36 :   Node mem1 = nm->mkNode(Kind::STRING_IN_REGEXP, v, n[0]);
    1431                 :         36 :   Node mem2 = nm->mkNode(Kind::STRING_IN_REGEXP, v, n[1]);
    1432                 :         54 :   return nm->mkNode(Kind::FORALL,
    1433 [ +  + ][ -  - ]:         72 :                     {nm->mkNode(Kind::BOUND_VAR_LIST, v), mem1.eqNode(mem2)});
    1434                 :         18 : }
    1435                 :            : 
    1436                 :     110236 : Node SequencesRewriter::rewriteViaStrInReEval(const Node& node)
    1437                 :            : {
    1438 [ +  + ][ +  + ]:     140522 :   if (node.getKind() != Kind::STRING_IN_REGEXP || !node[0].isConst()
                 [ -  - ]
    1439                 :     140522 :       || !RegExpEntail::isConstRegExp(node[1]))
    1440                 :            :   {
    1441                 :     106928 :     return Node::null();
    1442                 :            :   }
    1443                 :            :   // test whether x in node[1]
    1444                 :       3308 :   String s = node[0].getConst<String>();
    1445                 :       3308 :   bool test = RegExpEntail::testConstStringInRegExp(s, node[1]);
    1446                 :       3308 :   return nodeManager()->mkConst(test);
    1447                 :       3308 : }
    1448                 :            : 
    1449                 :      19520 : Node SequencesRewriter::rewriteViaStrInReConcatStarChar(const Node& n)
    1450                 :            : {
    1451                 :      58560 :   if (n.getKind() != Kind::STRING_IN_REGEXP
    1452 [ +  + ][ +  + ]:      20869 :       || n[0].getKind() != Kind::STRING_CONCAT
                 [ -  - ]
    1453 [ +  + ][ +  + ]:      20869 :       || n[1].getKind() != Kind::REGEXP_STAR)
         [ +  + ][ +  + ]
                 [ -  - ]
    1454                 :            :   {
    1455                 :      19401 :     return Node::null();
    1456                 :            :   }
    1457                 :        238 :   Node len = RegExpEntail::getFixedLengthForRegexp(n[1][0]);
    1458         [ -  + ]:        119 :   if (len.isNull())
    1459                 :            :   {
    1460                 :          0 :     return Node::null();
    1461                 :            :   }
    1462 [ +  - ][ -  + ]:        119 :   if (!len.isConst() || len.getConst<Rational>() != Rational(1))
         [ +  - ][ -  + ]
                 [ -  - ]
    1463                 :            :   {
    1464                 :          0 :     return Node::null();
    1465                 :            :   }
    1466                 :        119 :   NodeManager* nm = nodeManager();
    1467                 :        119 :   std::vector<Node> cc;
    1468                 :        119 :   utils::getConcat(n[0], cc);
    1469                 :        119 :   std::vector<Node> conj;
    1470         [ +  + ]:        462 :   for (const Node& c : cc)
    1471                 :            :   {
    1472                 :        343 :     conj.push_back(nm->mkNode(Kind::STRING_IN_REGEXP, c, n[1]));
    1473                 :            :   }
    1474                 :        119 :   return nm->mkAnd(conj);
    1475                 :        119 : }
    1476                 :            : 
    1477                 :      19409 : Node SequencesRewriter::rewriteViaStrInReSigma(const Node& n)
    1478                 :            : {
    1479                 :      58227 :   if (n.getKind() != Kind::STRING_IN_REGEXP
    1480 [ +  + ][ +  + ]:      19409 :       || n[1].getKind() != Kind::REGEXP_CONCAT)
         [ +  + ][ +  + ]
                 [ -  - ]
    1481                 :            :   {
    1482                 :      19283 :     return Node::null();
    1483                 :            :   }
    1484                 :        126 :   const Node& r = n[1];
    1485                 :        126 :   bool allSigmaStrict = true;
    1486                 :        126 :   size_t allSigmaMinSize = 0;
    1487         [ +  + ]:        217 :   for (const Node& rc : r)
    1488                 :            :   {
    1489         [ +  + ]:        203 :     if (rc.getKind() == Kind::REGEXP_ALLCHAR)
    1490                 :            :     {
    1491                 :         45 :       allSigmaMinSize++;
    1492                 :            :     }
    1493                 :        474 :     else if (rc.getKind() == Kind::REGEXP_STAR
    1494 [ +  + ][ +  + ]:        158 :              && rc[0].getKind() == Kind::REGEXP_ALLCHAR)
         [ +  + ][ +  + ]
                 [ -  - ]
    1495                 :            :     {
    1496                 :         46 :       allSigmaStrict = false;
    1497                 :            :     }
    1498                 :            :     else
    1499                 :            :     {
    1500                 :        112 :       return Node::null();
    1501                 :            :     }
    1502         [ +  + ]:        203 :   }
    1503                 :            :   // x in re.++(_*, _, _) ---> str.len(x) >= 2
    1504                 :         14 :   NodeManager* nm = nodeManager();
    1505                 :         14 :   Node num = nm->mkConstInt(Rational(allSigmaMinSize));
    1506                 :         28 :   Node lenx = nm->mkNode(Kind::STRING_LENGTH, n[0]);
    1507         [ +  + ]:         14 :   return nm->mkNode(allSigmaStrict ? Kind::EQUAL : Kind::GEQ, lenx, num);
    1508                 :        126 : }
    1509                 :      19399 : Node SequencesRewriter::rewriteViaStrInReSigmaStar(const Node& n)
    1510                 :            : {
    1511                 :      58197 :   if (n.getKind() != Kind::STRING_IN_REGEXP
    1512 [ +  + ][ +  + ]:      20627 :       || n[1].getKind() != Kind::REGEXP_STAR
                 [ -  - ]
    1513                 :      20627 :       || n[1][0].getKind() != Kind::REGEXP_CONCAT)
    1514                 :            :   {
    1515                 :      19392 :     return Node::null();
    1516                 :            :   }
    1517                 :          7 :   const Node& r = n[1][0];
    1518         [ +  + ]:         28 :   for (const Node& rc : r)
    1519                 :            :   {
    1520         [ -  + ]:         21 :     if (rc.getKind() != Kind::REGEXP_ALLCHAR)
    1521                 :            :     {
    1522                 :          0 :       return Node::null();
    1523                 :            :     }
    1524         [ +  - ]:         21 :   }
    1525                 :          7 :   NodeManager* nm = nodeManager();
    1526                 :          7 :   Node zero = nm->mkConstInt(Rational(0));
    1527                 :          7 :   Node num = nm->mkConstInt(Rational(r.getNumChildren()));
    1528                 :         14 :   Node lenx = nm->mkNode(Kind::STRING_LENGTH, n[0]);
    1529                 :         14 :   Node t = nm->mkNode(Kind::INTS_MODULUS, lenx, num);
    1530                 :          7 :   return nm->mkNode(Kind::EQUAL, t, zero);
    1531                 :          7 : }
    1532                 :            : 
    1533                 :     112190 : Node SequencesRewriter::rewriteViaMacroSubstrStripSymLength(const Node& node,
    1534                 :            :                                                             Rewrite& rule,
    1535                 :            :                                                             StringsEntail& sent)
    1536                 :            : {
    1537         [ +  + ]:     112190 :   if (node.getKind() != Kind::STRING_SUBSTR)
    1538                 :            :   {
    1539                 :        331 :     return Node::null();
    1540                 :            :   }
    1541                 :     111859 :   std::vector<Node> ch1;
    1542                 :     111859 :   std::vector<Node> ch2;
    1543                 :     111859 :   return sent.rewriteViaMacroSubstrStripSymLength(node, rule, ch1, ch2);
    1544                 :     111859 : }
    1545                 :            : 
    1546                 :      15841 : Node SequencesRewriter::rewriteViaMacroStrInReInclusion(const Node& n)
    1547                 :            : {
    1548         [ +  + ]:      15841 :   if (n.getKind() != Kind::STRING_IN_REGEXP)
    1549                 :            :   {
    1550                 :        165 :     return Node::null();
    1551                 :            :   }
    1552                 :            :   // check regular expression inclusion
    1553                 :            :   // This makes a regular expression that contains all possible model values
    1554                 :            :   // for x, and checks whether r includes this regular expression. If so,
    1555                 :            :   // the membership rewrites to true.
    1556                 :      15676 :   RegExpEntail re(nodeManager(), nullptr);
    1557                 :      15676 :   Node reForX = re.getGeneralizedConstRegExp(n[0]);
    1558                 :            :   // only chance of success is if there was at least one constant
    1559         [ +  + ]:      15676 :   if (!reForX.isNull())
    1560                 :            :   {
    1561         [ +  + ]:       2848 :     if (RegExpEntail::regExpIncludes(n[1], reForX))
    1562                 :            :     {
    1563                 :        152 :       return d_true;
    1564                 :            :     }
    1565                 :            :   }
    1566                 :      15524 :   return Node::null();
    1567                 :      15676 : }
    1568                 :            : 
    1569                 :      26142 : Node SequencesRewriter::rewriteViaMacroStrSplitCtn(const Node& node)
    1570                 :            : {
    1571                 :      78426 :   if (node.getKind() != Kind::STRING_CONTAINS
    1572                 :      52143 :       || node[0].getKind() != Kind::STRING_CONCAT || !node[1].isConst()
    1573                 :      52143 :       || Word::getLength(node[1]) == 0)  // don't bother if empty string
    1574                 :            :   {
    1575                 :      21186 :     return Node::null();
    1576                 :            :   }
    1577                 :       4956 :   Node t = node[1];
    1578                 :            :   // Below, we are looking for a constant component of node[0]
    1579                 :            :   // has no overlap with node[1], which means we can split.
    1580                 :            :   // Notice that if the first or last components had no
    1581                 :            :   // overlap, these would have been removed by strip
    1582                 :            :   // constant endpoints. Hence, we consider only the inner children.
    1583         [ +  + ]:       8657 :   for (size_t i = 1, iend = (node[0].getNumChildren() - 1); i < iend; i++)
    1584                 :            :   {
    1585                 :            :     // constant contains
    1586         [ +  + ]:       4564 :     if (node[0][i].isConst())
    1587                 :            :     {
    1588                 :            :       // if no overlap, we can split into disjunction
    1589                 :       5541 :       if (!Word::hasOverlap(node[0][i], node[1], false)
    1590                 :       5541 :           && !Word::hasOverlap(node[1], node[0][i], false))
    1591                 :            :       {
    1592                 :        863 :         std::vector<Node> nc0;
    1593                 :        863 :         utils::getConcat(node[0], nc0);
    1594         [ +  + ]:       5178 :         std::vector<Node> spl[2];
    1595                 :        863 :         spl[0].insert(spl[0].end(), nc0.begin(), nc0.begin() + i);
    1596 [ -  + ][ -  + ]:        863 :         Assert(i < nc0.size() - 1);
                 [ -  - ]
    1597                 :        863 :         spl[1].insert(spl[1].end(), nc0.begin() + i + 1, nc0.end());
    1598                 :        863 :         TypeNode stype = node[0].getType();
    1599                 :       3452 :         Node ret = nodeManager()->mkNode(
    1600                 :            :             Kind::OR,
    1601                 :        863 :             {nodeManager()->mkNode(Kind::STRING_CONTAINS,
    1602                 :       1726 :                                    utils::mkConcat(spl[0], stype),
    1603                 :            :                                    node[1]),
    1604                 :        863 :              nodeManager()->mkNode(Kind::STRING_CONTAINS,
    1605                 :       1726 :                                    utils::mkConcat(spl[1], stype),
    1606                 :       6041 :                                    node[1])});
    1607                 :        863 :         return ret;
    1608 [ +  + ][ -  - ]:       4315 :       }
    1609                 :            :     }
    1610                 :            :   }
    1611                 :       4093 :   return Node::null();
    1612                 :       4956 : }
    1613                 :            : 
    1614                 :     210659 : Node SequencesRewriter::rewriteViaMacroStrStripEndpoints(
    1615                 :            :     const Node& n,
    1616                 :            :     std::vector<Node>& nb,
    1617                 :            :     std::vector<Node>& nrem,
    1618                 :            :     std::vector<Node>& ne)
    1619                 :            : {
    1620                 :     210659 :   Kind k = n.getKind();
    1621                 :     210659 :   std::vector<int> dirs;
    1622         [ +  + ]:     210659 :   if (k == Kind::STRING_INDEXOF)
    1623                 :            :   {
    1624                 :            :     // must start at zero
    1625                 :       7854 :     if (!n[2].isConst() || n[2].getConst<Rational>().sgn() != 0)
    1626                 :            :     {
    1627                 :          0 :       return Node::null();
    1628                 :            :     }
    1629                 :            :     // only strip off the end
    1630                 :       7854 :     dirs.push_back(-1);
    1631                 :            :   }
    1632 [ +  + ][ +  + ]:     202805 :   else if (k == Kind::STRING_CONTAINS || k == Kind::STRING_REPLACE)
    1633                 :            :   {
    1634         [ +  + ]:     202664 :     if (n[1].isConst())
    1635                 :            :     {
    1636                 :            :       // if constant, we strip from one direction at a time, to ease proof
    1637                 :            :       // reconstruction
    1638                 :      67708 :       dirs.push_back(-1);
    1639                 :      67708 :       dirs.push_back(1);
    1640                 :            :     }
    1641                 :            :     else
    1642                 :            :     {
    1643                 :     134956 :       dirs.push_back(0);
    1644                 :            :     }
    1645                 :            :   }
    1646                 :            :   else
    1647                 :            :   {
    1648                 :        141 :     return Node::null();
    1649                 :            :   }
    1650                 :            : 
    1651                 :     210518 :   std::vector<Node> nc2;
    1652                 :     210518 :   utils::getConcat(n[1], nc2);
    1653         [ -  + ]:     210518 :   if (nc2.empty())
    1654                 :            :   {
    1655                 :          0 :     return Node::null();
    1656                 :            :   }
    1657                 :            :   // strip endpoints
    1658                 :     210518 :   bool success = false;
    1659         [ +  + ]:     482816 :   for (int dir : dirs)
    1660                 :            :   {
    1661                 :     276531 :     nrem.clear();
    1662                 :     276531 :     utils::getConcat(n[0], nrem);
    1663                 :     276531 :     nb.clear();
    1664                 :     276531 :     ne.clear();
    1665         [ +  + ]:     276531 :     if (d_stringsEntail.stripConstantEndpoints(nrem, nc2, nb, ne, dir))
    1666                 :            :     {
    1667                 :       4233 :       success = true;
    1668                 :       4233 :       break;
    1669                 :            :     }
    1670                 :            :   }
    1671         [ +  + ]:     210518 :   if (success)
    1672                 :            :   {
    1673                 :       4233 :     NodeManager* nm = nodeManager();
    1674                 :       4233 :     TypeNode stype = n[0].getType();
    1675                 :       4233 :     Node rem = utils::mkConcat(nrem, stype);
    1676 [ +  + ][ +  - ]:       4233 :     switch (k)
    1677                 :            :     {
    1678                 :       3709 :       case Kind::STRING_CONTAINS:
    1679                 :            :       {
    1680                 :       3709 :         return nm->mkNode(Kind::STRING_CONTAINS, rem, n[1]);
    1681                 :            :       }
    1682                 :        424 :       case Kind::STRING_REPLACE:
    1683                 :            :       {
    1684                 :        424 :         std::vector<Node> cc;
    1685                 :        424 :         cc.insert(cc.end(), nb.begin(), nb.end());
    1686                 :        424 :         cc.push_back(nm->mkNode(Kind::STRING_REPLACE, rem, n[1], n[2]));
    1687                 :        424 :         cc.insert(cc.end(), ne.begin(), ne.end());
    1688                 :        424 :         return utils::mkConcat(cc, stype);
    1689                 :        424 :       }
    1690                 :        100 :       case Kind::STRING_INDEXOF:
    1691                 :            :       {
    1692                 :        100 :         return nm->mkNode(Kind::STRING_INDEXOF, rem, n[1], n[2]);
    1693                 :            :       }
    1694                 :          0 :       default: break;
    1695                 :            :     }
    1696 [ -  + ][ -  + ]:       8466 :   }
    1697                 :     206285 :   return Node::null();
    1698                 :     210659 : }
    1699                 :            : 
    1700                 :       4455 : Node SequencesRewriter::rewriteViaMacroReInterUnionConstElim(const Node& n,
    1701                 :            :                                                              Node& conflict)
    1702                 :            : {
    1703                 :       4455 :   Kind k = n.getKind();
    1704 [ +  + ][ +  + ]:       4455 :   if (k != Kind::REGEXP_INTER && k != Kind::REGEXP_UNION)
    1705                 :            :   {
    1706                 :        138 :     return Node::null();
    1707                 :            :   }
    1708                 :       4317 :   std::vector<Node> constStrRe;
    1709                 :       4317 :   std::vector<Node> otherRe;
    1710         [ +  + ]:      14699 :   for (const Node& nc : n)
    1711                 :            :   {
    1712         [ +  + ]:      10382 :     if (!RegExpEntail::isConstRegExp(nc))
    1713                 :            :     {
    1714                 :       1042 :       continue;
    1715                 :            :     }
    1716         [ +  + ]:       9340 :     if (nc.getKind() == Kind::STRING_TO_REGEXP)
    1717                 :            :     {
    1718 [ -  + ][ -  + ]:       3708 :       Assert(nc[0].isConst());
                 [ -  - ]
    1719                 :       3708 :       constStrRe.push_back(nc);
    1720                 :            :     }
    1721                 :            :     else
    1722                 :            :     {
    1723                 :       5632 :       otherRe.push_back(nc);
    1724                 :            :     }
    1725         [ +  + ]:      10382 :   }
    1726                 :            : 
    1727 [ +  + ][ +  + ]:       4317 :   if (constStrRe.empty() || otherRe.empty())
                 [ +  + ]
    1728                 :            :   {
    1729                 :       3308 :     return Node::null();
    1730                 :            :   }
    1731                 :            :   // go back and process constant strings against the others
    1732                 :       1009 :   std::unordered_set<Node> toRemove;
    1733         [ +  + ]:       2158 :   for (const Node& c : constStrRe)
    1734                 :            :   {
    1735                 :       1171 :     Assert(c.getKind() == Kind::STRING_TO_REGEXP && c[0].isConst());
    1736                 :       1171 :     String s = c[0].getConst<String>();
    1737         [ +  + ]:       3158 :     for (const Node& r : otherRe)
    1738                 :            :     {
    1739 [ -  + ][ -  + ]:       2094 :       Assert(RegExpEntail::isConstRegExp(r));
                 [ -  - ]
    1740         [ +  - ]:       4188 :       Trace("strings-rewrite-debug")
    1741                 :       2094 :           << "Check " << c << " vs " << r << std::endl;
    1742                 :            :       // skip if already removing, or not constant
    1743         [ -  + ]:       2094 :       if (toRemove.find(r) != toRemove.end())
    1744                 :            :       {
    1745         [ -  - ]:          0 :         Trace("strings-rewrite-debug") << "...skip" << std::endl;
    1746                 :          0 :         continue;
    1747                 :            :       }
    1748                 :            :       // test whether c from (str.to_re c) is in r
    1749         [ +  + ]:       2094 :       if (RegExpEntail::testConstStringInRegExp(s, r))
    1750                 :            :       {
    1751         [ +  - ]:        286 :         Trace("strings-rewrite-debug") << "...included" << std::endl;
    1752         [ +  + ]:        286 :         if (k == Kind::REGEXP_INTER)
    1753                 :            :         {
    1754                 :            :           // (re.inter .. (str.to_re c) .. R ..) --->
    1755                 :            :           // (re.inter .. (str.to_re c) .. ..) when c in R
    1756                 :        201 :           toRemove.insert(r);
    1757                 :            :         }
    1758                 :            :         else
    1759                 :            :         {
    1760                 :            :           // (re.union .. (str.to_re c) .. R ..) --->
    1761                 :            :           // (re.union .. .. R ..) when c in R
    1762                 :         85 :           toRemove.insert(c);
    1763                 :         85 :           break;
    1764                 :            :         }
    1765                 :            :       }
    1766                 :            :       else
    1767                 :            :       {
    1768         [ +  - ]:       1808 :         Trace("strings-rewrite-debug") << "...not included" << std::endl;
    1769         [ +  + ]:       1808 :         if (k == Kind::REGEXP_INTER)
    1770                 :            :         {
    1771                 :         22 :           conflict = c;
    1772                 :            :           // (re.inter .. (str.to_re c) .. R ..) ---> re.none
    1773                 :            :           // if c is not a member of R.
    1774                 :         22 :           return nodeManager()->mkNode(Kind::REGEXP_NONE);
    1775                 :            :         }
    1776                 :            :       }
    1777                 :            :     }
    1778         [ +  + ]:       1171 :   }
    1779                 :            : 
    1780         [ +  + ]:        987 :   if (!toRemove.empty())
    1781                 :            :   {
    1782                 :        239 :     std::vector<Node> vec;
    1783         [ +  + ]:        764 :     for (const Node& nc : n)
    1784                 :            :     {
    1785         [ +  + ]:        525 :       if (toRemove.find(nc) == toRemove.end())
    1786                 :            :       {
    1787                 :        239 :         vec.push_back(nc);
    1788                 :            :       }
    1789                 :        525 :     }
    1790 [ -  + ][ -  + ]:        239 :     Assert(!vec.empty());
                 [ -  - ]
    1791         [ +  - ]:        239 :     return vec.size() == 1 ? vec[0] : nodeManager()->mkNode(k, vec);
    1792                 :        239 :   }
    1793                 :            : 
    1794                 :        748 :   return Node::null();
    1795                 :       4317 : }
    1796                 :            : 
    1797                 :       1396 : Node SequencesRewriter::rewriteViaStrIndexofReEval(const Node& n)
    1798                 :            : {
    1799                 :       2524 :   if (n.getKind() == Kind::STRING_INDEXOF_RE && n[0].isConst() && n[2].isConst()
    1800                 :       2524 :       && RegExpEntail::isConstRegExp(n[1]))
    1801                 :            :   {
    1802                 :        336 :     NodeManager* nm = nodeManager();
    1803                 :        336 :     Rational nrat = n[2].getConst<Rational>();
    1804                 :        336 :     String s = n[0].getConst<String>();
    1805                 :        336 :     Rational rsize(s.size());
    1806 [ +  + ][ +  + ]:        336 :     if (nrat > rsize || nrat.sgn() < 0)
                 [ +  + ]
    1807                 :            :     {
    1808                 :         21 :       Node negone = nm->mkConstInt(Rational(-1));
    1809                 :         21 :       return negone;
    1810                 :         21 :     }
    1811                 :        315 :     uint32_t start = nrat.getNumerator().toUnsignedInt();
    1812                 :        315 :     Node rem = nm->mkConst(s.substr(start));
    1813                 :        315 :     std::pair<size_t, size_t> match = firstMatch(rem, n[1]);
    1814                 :            :     Node ret = nm->mkConstInt(
    1815                 :        315 :         Rational(match.first == string::npos
    1816                 :            :                      ? -1
    1817         [ +  + ]:        630 :                      : static_cast<int64_t>(start + match.first)));
    1818                 :        315 :     return ret;
    1819                 :        336 :   }
    1820                 :       1060 :   return Node::null();
    1821                 :            : }
    1822                 :            : 
    1823                 :        883 : Node SequencesRewriter::rewriteViaStrReplaceReEval(const Node& n)
    1824                 :            : {
    1825 [ +  + ][ +  + ]:       1502 :   if (n.getKind() == Kind::STRING_REPLACE_RE && n[0].isConst()
                 [ -  - ]
    1826                 :       1502 :       && RegExpEntail::isConstRegExp(n[1]))
    1827                 :            :   {
    1828                 :        369 :     NodeManager* nm = nodeManager();
    1829                 :            :     // str.replace_re("ZABCZ", re.++("A", _*, "C"), y) ---> "Z" ++ y ++ "Z"
    1830                 :        369 :     std::pair<size_t, size_t> match = firstMatch(n[0], n[1]);
    1831         [ +  + ]:        369 :     if (match.first != string::npos)
    1832                 :            :     {
    1833                 :        225 :       String s = n[0].getConst<String>();
    1834                 :        900 :       Node ret = nm->mkNode(Kind::STRING_CONCAT,
    1835                 :        450 :                             {nm->mkConst(s.substr(0, match.first)),
    1836                 :            :                              n[2],
    1837                 :        450 :                              nm->mkConst(s.substr(match.second))});
    1838                 :        225 :       return ret;
    1839                 :        225 :     }
    1840                 :        144 :     return n[0];
    1841                 :            :   }
    1842                 :        514 :   return Node::null();
    1843                 :            : }
    1844                 :            : 
    1845                 :        924 : Node SequencesRewriter::rewriteViaStrReplaceReAllEval(const Node& n)
    1846                 :            : {
    1847 [ +  + ][ +  + ]:       1602 :   if (n.getKind() == Kind::STRING_REPLACE_RE_ALL && n[0].isConst()
                 [ -  - ]
    1848                 :       1602 :       && RegExpEntail::isConstRegExp(n[1]))
    1849                 :            :   {
    1850                 :        482 :     NodeManager* nm = nodeManager();
    1851                 :            :     // str.replace_re_all("ZABCZAB", re.++("A", _*, "C"), y) --->
    1852                 :            :     //   "Z" ++ y ++ "Z" ++ y
    1853                 :        482 :     TypeNode t = n[0].getType();
    1854 [ -  + ][ -  + ]:        482 :     Assert(t.isString());
                 [ -  - ]
    1855                 :        482 :     Node emp = Word::mkEmptyWord(t);
    1856                 :            :     Node yp = nm->mkNode(Kind::REGEXP_INTER,
    1857                 :            :                          n[1],
    1858                 :        964 :                          nm->mkNode(Kind::REGEXP_COMPLEMENT,
    1859                 :       1928 :                                     nm->mkNode(Kind::STRING_TO_REGEXP, emp)));
    1860                 :        482 :     std::vector<Node> res;
    1861                 :        482 :     String rem = n[0].getConst<String>();
    1862                 :        482 :     std::pair<size_t, size_t> match(0, 0);
    1863         [ +  + ]:        871 :     while (rem.size() != 0)
    1864                 :            :     {
    1865                 :        621 :       match = firstMatch(nm->mkConst(rem), yp);
    1866         [ +  + ]:        621 :       if (match.first == string::npos)
    1867                 :            :       {
    1868                 :        232 :         break;
    1869                 :            :       }
    1870                 :        389 :       res.push_back(nm->mkConst(rem.substr(0, match.first)));
    1871                 :        389 :       res.push_back(n[2]);
    1872                 :        389 :       rem = rem.substr(match.second);
    1873                 :            :     }
    1874                 :            :     // only concatenate remainder if non-empty
    1875         [ +  + ]:        482 :     if (rem.size() != 0)
    1876                 :            :     {
    1877                 :        232 :       res.push_back(nm->mkConst(rem));
    1878                 :            :     }
    1879                 :        482 :     Node ret = utils::mkConcat(res, t);
    1880                 :        482 :     return ret;
    1881                 :        482 :   }
    1882                 :        442 :   return Node::null();
    1883                 :            : }
    1884                 :            : 
    1885                 :        116 : Node SequencesRewriter::rewriteViaOverlap(ProofRewriteRule id, const Node& n)
    1886                 :            : {
    1887         [ -  + ]:        116 :   if (n.getNumChildren() < 2)
    1888                 :            :   {
    1889                 :          0 :     return Node::null();
    1890                 :            :   }
    1891                 :            :   // get the list of overlaps to check, based on the rule, which will be passed
    1892                 :            :   // to Word::hasOverlap below.
    1893                 :        116 :   std::vector<std::tuple<Node, Node, int>> overlap;
    1894                 :        116 :   Kind k = n.getKind();
    1895 [ +  + ][ +  + ]:        116 :   switch (id)
                    [ - ]
    1896                 :            :   {
    1897                 :          8 :     case ProofRewriteRule::STR_OVERLAP_SPLIT_CTN:
    1898                 :            :     {
    1899 [ +  - ][ -  + ]:          8 :       if (k != Kind::STRING_CONTAINS || n[0].getNumChildren() != 3)
         [ +  - ][ -  + ]
                 [ -  - ]
    1900                 :            :       {
    1901                 :          0 :         return Node::null();
    1902                 :            :       }
    1903                 :          8 :       overlap.emplace_back(n[0][1], n[1], false);
    1904                 :          8 :       overlap.emplace_back(n[1], n[0][1], false);
    1905                 :            :     }
    1906                 :          8 :     break;
    1907                 :         64 :     case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_CTN:
    1908                 :            :     {
    1909 [ +  - ][ -  + ]:        128 :       if (k != Kind::STRING_CONTAINS || n[0].getNumChildren() != 3
                 [ -  - ]
    1910 [ +  - ][ -  + ]:        128 :           || n[1].getNumChildren() != 3)
         [ +  - ][ +  - ]
                 [ -  - ]
    1911                 :            :       {
    1912                 :          0 :         return Node::null();
    1913                 :            :       }
    1914                 :         64 :       overlap.emplace_back(n[0][0], n[1][0], false);
    1915                 :         64 :       overlap.emplace_back(n[0][2], n[1][2], true);
    1916                 :            :     }
    1917                 :         64 :     break;
    1918                 :         12 :     case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_INDEXOF:
    1919                 :            :     {
    1920 [ +  - ][ -  + ]:         24 :       if (k != Kind::STRING_INDEXOF || n[0].getNumChildren() != 2
                 [ -  - ]
    1921                 :         24 :           || n[1].getNumChildren() != 2 || !n[2].isConst()
    1922 [ +  - ][ -  + ]:         24 :           || n[2].getConst<Rational>().sgn() != 0)
         [ +  - ][ +  - ]
                 [ -  - ]
    1923                 :            :       {
    1924                 :          0 :         return Node::null();
    1925                 :            :       }
    1926                 :         12 :       overlap.emplace_back(n[0][1], n[1][1], true);
    1927                 :            :     }
    1928                 :         12 :     break;
    1929                 :         32 :     case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_REPLACE:
    1930                 :            :     {
    1931 [ +  - ][ -  + ]:         64 :       if (k != Kind::STRING_REPLACE || n[0].getNumChildren() != 3
                 [ -  - ]
    1932 [ +  - ][ -  + ]:         64 :           || n[1].getNumChildren() != 3)
         [ +  - ][ +  - ]
                 [ -  - ]
    1933                 :            :       {
    1934                 :          0 :         return Node::null();
    1935                 :            :       }
    1936                 :         32 :       overlap.emplace_back(n[0][0], n[1][0], false);
    1937                 :         32 :       overlap.emplace_back(n[0][2], n[1][2], true);
    1938                 :            :     }
    1939                 :         32 :     break;
    1940                 :          0 :     default: return Node::null();
    1941                 :            :   }
    1942         [ +  + ]:        336 :   for (const std::tuple<Node, Node, int>& f : overlap)
    1943                 :            :   {
    1944                 :        220 :     const Node& c1 = std::get<0>(f);
    1945                 :        220 :     const Node& c2 = std::get<1>(f);
    1946                 :            :     // ensure it is a constant
    1947 [ +  - ][ -  + ]:        220 :     if (!c1.isConst() || !c2.isConst())
                 [ -  + ]
    1948                 :            :     {
    1949                 :          0 :       return Node::null();
    1950                 :            :     }
    1951                 :            :     // if it has an overlap
    1952         [ -  + ]:        220 :     if (Word::hasOverlap(c1, c2, std::get<2>(f)))
    1953                 :            :     {
    1954                 :          0 :       return Node::null();
    1955                 :            :     }
    1956                 :            :   }
    1957                 :            : 
    1958                 :            :   // checks succeeded, make the appropriate rewritten term
    1959                 :        116 :   NodeManager* nm = nodeManager();
    1960 [ +  + ][ +  + ]:        116 :   switch (id)
                    [ - ]
    1961                 :            :   {
    1962                 :          8 :     case ProofRewriteRule::STR_OVERLAP_SPLIT_CTN:
    1963                 :         24 :       return nm->mkNode(
    1964                 :            :           Kind::OR,
    1965 [ +  + ][ -  - ]:         32 :           {nm->mkNode(k, n[0][0], n[1]), nm->mkNode(k, n[0][2], n[1])});
    1966                 :         64 :     case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_CTN:
    1967                 :         64 :       return nm->mkNode(k, n[0][1], n[1]);
    1968                 :         12 :     case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_INDEXOF:
    1969                 :         12 :       return nm->mkNode(k, n[0][0], n[1], n[2]);
    1970                 :         32 :     case ProofRewriteRule::STR_OVERLAP_ENDPOINTS_REPLACE:
    1971                 :            :       return nm->mkNode(Kind::STRING_CONCAT,
    1972                 :            :                         n[0][0],
    1973                 :         64 :                         nm->mkNode(k, n[0][1], n[1], n[2]),
    1974                 :         96 :                         n[0][2]);
    1975                 :          0 :     default: break;
    1976                 :            :   }
    1977                 :          0 :   return Node::null();
    1978                 :        116 : }
    1979                 :            : 
    1980                 :         28 : Node SequencesRewriter::rewriteRepeatRegExp(TNode node)
    1981                 :            : {
    1982 [ -  + ][ -  + ]:         28 :   Assert(node.getKind() == Kind::REGEXP_REPEAT);
                 [ -  - ]
    1983                 :         28 :   NodeManager* nm = nodeManager();
    1984                 :            :   // ((_ re.^ n) R) --> ((_ re.loop n n) R)
    1985                 :         28 :   unsigned r = utils::getRepeatAmount(node);
    1986                 :         28 :   Node lop = nm->mkConst(RegExpLoop(r, r));
    1987                 :         56 :   Node retNode = nm->mkNode(Kind::REGEXP_LOOP, lop, node[0]);
    1988                 :         56 :   return returnRewrite(node, retNode, Rewrite::RE_REPEAT_ELIM);
    1989                 :         28 : }
    1990                 :            : 
    1991                 :        114 : Node SequencesRewriter::rewriteOptionRegExp(TNode node)
    1992                 :            : {
    1993 [ -  + ][ -  + ]:        114 :   Assert(node.getKind() == Kind::REGEXP_OPT);
                 [ -  - ]
    1994                 :        114 :   NodeManager* nm = nodeManager();
    1995                 :            :   Node retNode =
    1996                 :            :       nm->mkNode(Kind::REGEXP_UNION,
    1997                 :        228 :                  nm->mkNode(Kind::STRING_TO_REGEXP, nm->mkConst(String(""))),
    1998                 :        342 :                  node[0]);
    1999                 :        228 :   return returnRewrite(node, retNode, Rewrite::RE_OPT_ELIM);
    2000                 :        114 : }
    2001                 :            : 
    2002                 :        239 : Node SequencesRewriter::rewritePlusRegExp(TNode node)
    2003                 :            : {
    2004 [ -  + ][ -  + ]:        239 :   Assert(node.getKind() == Kind::REGEXP_PLUS);
                 [ -  - ]
    2005                 :        239 :   NodeManager* nm = nodeManager();
    2006                 :            :   Node retNode = nm->mkNode(
    2007                 :        478 :       Kind::REGEXP_CONCAT, node[0], nm->mkNode(Kind::REGEXP_STAR, node[0]));
    2008                 :        478 :   return returnRewrite(node, retNode, Rewrite::RE_PLUS_ELIM);
    2009                 :        239 : }
    2010                 :            : 
    2011                 :        479 : Node SequencesRewriter::rewriteDifferenceRegExp(TNode node)
    2012                 :            : {
    2013 [ -  + ][ -  + ]:        479 :   Assert(node.getKind() == Kind::REGEXP_DIFF);
                 [ -  - ]
    2014                 :        479 :   NodeManager* nm = nodeManager();
    2015                 :            :   Node retNode = nm->mkNode(Kind::REGEXP_INTER,
    2016                 :            :                             node[0],
    2017                 :        958 :                             nm->mkNode(Kind::REGEXP_COMPLEMENT, node[1]));
    2018                 :        958 :   return returnRewrite(node, retNode, Rewrite::RE_DIFF_ELIM);
    2019                 :        479 : }
    2020                 :            : 
    2021                 :       1340 : Node SequencesRewriter::rewriteRangeRegExp(TNode node)
    2022                 :            : {
    2023 [ -  + ][ -  + ]:       1340 :   Assert(node.getKind() == Kind::REGEXP_RANGE);
                 [ -  - ]
    2024                 :       1340 :   NodeManager* nm = nodeManager();
    2025                 :            :   unsigned ch[2];
    2026                 :       1340 :   bool hasNonConst = false;
    2027         [ +  + ]:       3950 :   for (size_t i = 0; i < 2; ++i)
    2028                 :            :   {
    2029         [ +  + ]:       2650 :     if (!node[i].isConst())
    2030                 :            :     {
    2031                 :          4 :       hasNonConst = true;
    2032                 :          4 :       continue;
    2033                 :            :     }
    2034         [ +  + ]:       2646 :     else if (node[i].getConst<String>().size() != 1)
    2035                 :            :     {
    2036                 :            :       // non-singleton means empty
    2037                 :         40 :       Node retNode = nm->mkNode(Kind::REGEXP_NONE);
    2038                 :         40 :       return returnRewrite(node, retNode, Rewrite::RE_RANGE_NON_SINGLETON);
    2039                 :         40 :     }
    2040                 :       2606 :     ch[i] = node[i].getConst<String>().front();
    2041                 :            :   }
    2042         [ +  + ]:       1300 :   if (hasNonConst)
    2043                 :            :   {
    2044                 :            :     // not applied to characters, it is not handled
    2045                 :          2 :     return node;
    2046                 :            :   }
    2047                 :            : 
    2048         [ +  + ]:       1298 :   if (node[0] == node[1])
    2049                 :            :   {
    2050                 :         10 :     Node retNode = nm->mkNode(Kind::STRING_TO_REGEXP, node[0]);
    2051                 :            :     // re.range( "A", "A" ) ---> str.to_re( "A" )
    2052                 :         10 :     return returnRewrite(node, retNode, Rewrite::RE_RANGE_SINGLE);
    2053                 :         10 :   }
    2054                 :            : 
    2055         [ +  + ]:       1288 :   if (ch[0] > ch[1])
    2056                 :            :   {
    2057                 :            :     // re.range( "B", "A" ) ---> re.none
    2058                 :         12 :     Node retNode = nm->mkNode(Kind::REGEXP_NONE);
    2059                 :         12 :     return returnRewrite(node, retNode, Rewrite::RE_RANGE_EMPTY);
    2060                 :         12 :   }
    2061                 :       1276 :   return node;
    2062                 :            : }
    2063                 :            : 
    2064                 :      36696 : Node SequencesRewriter::rewriteViaStrInReConsume(const Node& node)
    2065                 :            : {
    2066         [ +  + ]:      36696 :   if (node.getKind() != Kind::STRING_IN_REGEXP)
    2067                 :            :   {
    2068                 :      18171 :     return Node::null();
    2069                 :            :   }
    2070                 :            :   // if star, we consider the body of the star
    2071                 :      18525 :   bool isStar = (node[1].getKind() == Kind::REGEXP_STAR);
    2072         [ +  + ]:      18525 :   size_t numIter = isStar ? 2 : 1;
    2073         [ +  + ]:      43540 :   for (size_t i = 0; i < numIter; i++)
    2074                 :            :   {
    2075         [ +  + ]:      26712 :     int dir = isStar ? (i == 0 ? 0 : 1) : -1;
    2076 [ +  + ][ +  + ]:      26712 :     Node r = isStar ? node[1][0] : node[1];
                 [ -  - ]
    2077                 :      26712 :     std::vector<Node> children;
    2078                 :      26712 :     utils::getConcat(r, children);
    2079                 :      26712 :     std::vector<Node> mchildren;
    2080                 :      26712 :     utils::getConcat(node[0], mchildren);
    2081                 :            :     Node scn =
    2082                 :      26712 :         RegExpEntail::simpleRegexpConsume(d_nm, mchildren, children, dir);
    2083         [ +  + ]:      26712 :     if (!scn.isNull())
    2084                 :            :     {
    2085                 :        495 :       return scn;
    2086                 :            :     }
    2087 [ +  + ][ +  + ]:      26217 :     else if (!isStar || children.empty())
                 [ +  + ]
    2088                 :            :     {
    2089                 :            :       // Given a membership (str.++ x1 ... xn) in (re.++ r1 ... rm),
    2090                 :            :       // above, we strip components to construct an equivalent membership:
    2091                 :            :       // (str.++ xi .. xj) in (re.++ rk ... rl).
    2092                 :      20352 :       Node xn = utils::mkConcat(mchildren, node[0].getType());
    2093                 :            :       // if we considered the body of the star, we revert to the original RE
    2094                 :      20080 :       Node rn = isStar ? node[1] : utils::mkConcat(children, node[1].getType());
    2095                 :            :       // construct the updated regular expression
    2096                 :      20352 :       Node newMem = nodeManager()->mkNode(Kind::STRING_IN_REGEXP, xn, rn);
    2097         [ +  + ]:      10176 :       if (newMem != node)
    2098                 :            :       {
    2099                 :       1202 :         return newMem;
    2100                 :            :       }
    2101 [ +  + ][ +  + ]:      12580 :     }
                 [ +  + ]
    2102 [ +  + ][ +  + ]:      31803 :   }
         [ +  + ][ +  + ]
    2103                 :      16828 :   return Node::null();
    2104                 :            : }
    2105                 :            : 
    2106                 :      27196 : Node SequencesRewriter::rewriteMembership(TNode node)
    2107                 :            : {
    2108 [ -  + ][ -  + ]:      27196 :   Assert(node.getKind() == Kind::STRING_IN_REGEXP);
                 [ -  - ]
    2109                 :      27196 :   NodeManager* nm = nodeManager();
    2110                 :      27196 :   Node x = node[0];
    2111                 :      27196 :   Node r = node[1];
    2112                 :            : 
    2113                 :      27196 :   TypeNode stype = x.getType();
    2114                 :      27196 :   TypeNode rtype = r.getType();
    2115                 :            : 
    2116         [ +  + ]:      27196 :   if (r.getKind() == Kind::REGEXP_NONE)
    2117                 :            :   {
    2118                 :        292 :     Node retNode = nodeManager()->mkConst(false);
    2119                 :        292 :     return returnRewrite(node, retNode, Rewrite::RE_IN_EMPTY);
    2120                 :        292 :   }
    2121                 :            :   // test for constant evaluation
    2122                 :      26904 :   Node eval = rewriteViaStrInReEval(node);
    2123         [ +  + ]:      26904 :   if (!eval.isNull())
    2124                 :            :   {
    2125                 :       2677 :     return returnRewrite(node, eval, Rewrite::RE_IN_EVAL);
    2126                 :            :   }
    2127         [ +  + ]:      24227 :   else if (r.getKind() == Kind::REGEXP_ALLCHAR)
    2128                 :            :   {
    2129                 :        140 :     Node one = nm->mkConstInt(Rational(1));
    2130                 :        280 :     Node retNode = one.eqNode(nm->mkNode(Kind::STRING_LENGTH, x));
    2131                 :        140 :     return returnRewrite(node, retNode, Rewrite::RE_IN_SIGMA);
    2132                 :        140 :   }
    2133         [ +  + ]:      24087 :   else if (r.getKind() == Kind::REGEXP_STAR)
    2134                 :            :   {
    2135         [ +  + ]:       9211 :     if (x.isConst())
    2136                 :            :     {
    2137                 :         76 :       size_t xlen = Word::getLength(x);
    2138         [ +  + ]:         76 :       if (xlen == 0)
    2139                 :            :       {
    2140                 :            :         // e.g. (str.in.re "" (re.* (str.to.re x))) ----> true
    2141                 :         28 :         return returnRewrite(node, d_true, Rewrite::RE_EMPTY_IN_STR_STAR);
    2142                 :            :       }
    2143         [ +  + ]:         48 :       else if (xlen == 1)
    2144                 :            :       {
    2145         [ +  + ]:         36 :         if (r[0].getKind() == Kind::STRING_TO_REGEXP)
    2146                 :            :         {
    2147                 :         16 :           Node retNode = r[0][0].eqNode(x);
    2148                 :            :           // e.g. (str.in.re "A" (re.* (str.to.re x))) ----> "A" = x
    2149                 :          8 :           return returnRewrite(node, retNode, Rewrite::RE_CHAR_IN_STR_STAR);
    2150                 :          8 :         }
    2151                 :            :       }
    2152                 :            :     }
    2153         [ +  + ]:       9135 :     else if (x.getKind() == Kind::STRING_CONCAT)
    2154                 :            :     {
    2155                 :            :       // (str.in.re (str.++ x1 ... xn) (re.* R)) -->
    2156                 :            :       //   (str.in.re x1 (re.* R)) AND ... AND (str.in.re xn (re.* R))
    2157                 :            :       //     if the length of all strings in R is one.
    2158                 :       3644 :       Node flr = RegExpEntail::getFixedLengthForRegexp(r[0]);
    2159         [ +  + ]:       1822 :       if (!flr.isNull())
    2160                 :            :       {
    2161                 :        980 :         Node one = nm->mkConstInt(Rational(1));
    2162         [ +  + ]:        980 :         if (flr == one)
    2163                 :            :         {
    2164                 :        746 :           NodeBuilder nb(nodeManager(), Kind::AND);
    2165         [ +  + ]:       2899 :           for (const Node& xc : x)
    2166                 :            :           {
    2167                 :       2153 :             nb << nm->mkNode(Kind::STRING_IN_REGEXP, xc, r);
    2168                 :       2153 :           }
    2169                 :            :           return returnRewrite(
    2170                 :        746 :               node, nb.constructNode(), Rewrite::RE_IN_DIST_CHAR_STAR);
    2171                 :        746 :         }
    2172         [ +  + ]:        980 :       }
    2173         [ +  + ]:       1822 :     }
    2174         [ +  + ]:       8429 :     if (r[0].getKind() == Kind::REGEXP_ALLCHAR)
    2175                 :            :     {
    2176                 :        446 :       return returnRewrite(node, d_true, Rewrite::RE_IN_SIGMA_STAR);
    2177                 :            :     }
    2178         [ +  + ]:       7983 :     else if (r[0].getKind() == Kind::REGEXP_CONCAT)
    2179                 :            :     {
    2180                 :        993 :       bool isAllchar = true;
    2181         [ +  + ]:       1069 :       for (const Node& rc : r[0])
    2182                 :            :       {
    2183         [ +  + ]:       1056 :         if (rc.getKind() != Kind::REGEXP_ALLCHAR)
    2184                 :            :         {
    2185                 :        980 :           isAllchar = false;
    2186                 :        980 :           break;
    2187                 :            :         }
    2188         [ +  + ]:       2049 :       }
    2189         [ +  + ]:        993 :       if (isAllchar)
    2190                 :            :       {
    2191                 :            :         // For example:
    2192                 :            :         // (str.in_re x (re.* re.allchar re.allchar)) --->
    2193                 :            :         // (= (mod (str.len x) 2) 0)
    2194                 :         13 :         Node zero = nm->mkConstInt(Rational(0));
    2195                 :         26 :         Node factor = nm->mkConstInt(Rational(r[0].getNumChildren()));
    2196                 :            :         Node t = nm->mkNode(
    2197                 :         26 :             Kind::INTS_MODULUS, nm->mkNode(Kind::STRING_LENGTH, x), factor);
    2198                 :         13 :         Node retNode = t.eqNode(zero);
    2199                 :         13 :         return returnRewrite(node, retNode, Rewrite::RE_IN_CHAR_MODULUS_STAR);
    2200                 :         13 :       }
    2201                 :            :     }
    2202                 :            :   }
    2203         [ +  + ]:      14876 :   else if (r.getKind() == Kind::REGEXP_CONCAT)
    2204                 :            :   {
    2205                 :       9123 :     bool allSigma = true;
    2206                 :       9123 :     bool allSigmaStrict = true;
    2207                 :       9123 :     unsigned allSigmaMinSize = 0;
    2208                 :       9123 :     Node constStr;
    2209                 :       9123 :     size_t constIdx = 0;
    2210                 :       9123 :     size_t nchildren = r.getNumChildren();
    2211         [ +  + ]:      17524 :     for (size_t i = 0; i < nchildren; i++)
    2212                 :            :     {
    2213                 :      16448 :       Node rc = r[i];
    2214 [ -  + ][ -  + ]:      16448 :       Assert(rc.getKind() != Kind::REGEXP_NONE);
                 [ -  - ]
    2215         [ +  + ]:      16448 :       if (rc.getKind() == Kind::REGEXP_ALLCHAR)
    2216                 :            :       {
    2217                 :       1181 :         allSigmaMinSize++;
    2218                 :            :       }
    2219                 :      45801 :       else if (rc.getKind() == Kind::REGEXP_STAR
    2220 [ +  + ][ +  + ]:      15267 :                && rc[0].getKind() == Kind::REGEXP_ALLCHAR)
         [ +  + ][ +  + ]
                 [ -  - ]
    2221                 :            :       {
    2222                 :       2694 :         allSigmaStrict = false;
    2223                 :            :       }
    2224         [ +  + ]:      12573 :       else if (rc.getKind() == Kind::STRING_TO_REGEXP)
    2225                 :            :       {
    2226         [ +  + ]:       5652 :         if (constStr.isNull())
    2227                 :            :         {
    2228                 :       4526 :           constStr = rc[0];
    2229                 :       4526 :           constIdx = i;
    2230                 :            :         }
    2231                 :            :         else
    2232                 :            :         {
    2233                 :       1126 :           allSigma = false;
    2234                 :       1126 :           break;
    2235                 :            :         }
    2236                 :            :       }
    2237                 :            :       else
    2238                 :            :       {
    2239                 :       6921 :         allSigma = false;
    2240                 :       6921 :         break;
    2241                 :            :       }
    2242         [ +  + ]:      16448 :     }
    2243         [ +  + ]:       9123 :     if (allSigma)
    2244                 :            :     {
    2245         [ +  + ]:       1076 :       if (constStr.isNull())
    2246                 :            :       {
    2247                 :            :         // x in re.++(_*, _, _) ---> str.len(x) >= 2
    2248                 :         26 :         Node num = nm->mkConstInt(Rational(allSigmaMinSize));
    2249                 :         26 :         Node lenx = nm->mkNode(Kind::STRING_LENGTH, x);
    2250                 :            :         Node retNode =
    2251         [ +  + ]:         52 :             nm->mkNode(allSigmaStrict ? Kind::EQUAL : Kind::GEQ, lenx, num);
    2252                 :         26 :         return returnRewrite(node, retNode, Rewrite::RE_CONCAT_PURE_ALLCHAR);
    2253                 :         26 :       }
    2254 [ +  + ][ +  + ]:       1050 :       else if (allSigmaMinSize == 0 && nchildren >= 3 && constIdx != 0
                 [ +  - ]
    2255         [ +  - ]:        148 :                && constIdx != nchildren - 1)
    2256                 :            :       {
    2257                 :            :         // x in re.++(_*, "abc", _*) ---> str.contains(x, "abc")
    2258                 :        296 :         Node retNode = nm->mkNode(Kind::STRING_CONTAINS, x, constStr);
    2259                 :        148 :         return returnRewrite(node, retNode, Rewrite::RE_CONCAT_TO_CONTAINS);
    2260                 :        148 :       }
    2261                 :            :     }
    2262         [ +  + ]:       9123 :   }
    2263                 :       5753 :   else if (r.getKind() == Kind::REGEXP_INTER
    2264 [ +  + ][ +  + ]:       5753 :            || r.getKind() == Kind::REGEXP_UNION)
                 [ +  + ]
    2265                 :            :   {
    2266                 :       1372 :     std::vector<Node> mvec;
    2267         [ +  + ]:       4275 :     for (unsigned i = 0; i < r.getNumChildren(); i++)
    2268                 :            :     {
    2269                 :       2903 :       mvec.push_back(nodeManager()->mkNode(Kind::STRING_IN_REGEXP, x, r[i]));
    2270                 :            :     }
    2271                 :            :     Node retNode = nodeManager()->mkNode(
    2272         [ +  + ]:       1372 :         r.getKind() == Kind::REGEXP_INTER ? Kind::AND : Kind::OR, mvec);
    2273                 :       1372 :     return returnRewrite(node, retNode, Rewrite::RE_IN_ANDOR);
    2274                 :       1372 :   }
    2275         [ +  + ]:       4381 :   else if (r.getKind() == Kind::STRING_TO_REGEXP)
    2276                 :            :   {
    2277                 :       2577 :     Node retNode = x.eqNode(r[0]);
    2278                 :       2577 :     return returnRewrite(node, retNode, Rewrite::RE_IN_CSTRING);
    2279                 :       2577 :   }
    2280         [ +  + ]:       1804 :   else if (r.getKind() == Kind::REGEXP_RANGE)
    2281                 :            :   {
    2282                 :            :     // x in re.range( char_i, char_j ) ---> i <= str.code(x) <= j
    2283                 :            :     // we do not do this if the arguments are not constant
    2284         [ +  - ]:        871 :     if (RegExpEntail::isConstRegExp(r))
    2285                 :            :     {
    2286                 :        871 :       Node xcode = nm->mkNode(Kind::STRING_TO_CODE, x);
    2287                 :       2613 :       Node retNode = nm->mkNode(
    2288                 :            :           Kind::AND,
    2289                 :       1742 :           {nm->mkNode(Kind::LEQ, nm->mkNode(Kind::STRING_TO_CODE, r[0]), xcode),
    2290                 :       1742 :            nm->mkNode(
    2291                 :       3484 :                Kind::LEQ, xcode, nm->mkNode(Kind::STRING_TO_CODE, r[1]))});
    2292                 :        871 :       return returnRewrite(node, retNode, Rewrite::RE_IN_RANGE);
    2293                 :        871 :     }
    2294                 :            :   }
    2295         [ +  + ]:        933 :   else if (r.getKind() == Kind::REGEXP_COMPLEMENT)
    2296                 :            :   {
    2297                 :       1734 :     Node retNode = nm->mkNode(Kind::STRING_IN_REGEXP, x, r[0]).negate();
    2298                 :        867 :     return returnRewrite(node, retNode, Rewrite::RE_IN_COMPLEMENT);
    2299                 :        867 :   }
    2300                 :            : 
    2301                 :            :   // do simple consumes
    2302                 :      16985 :   Node retNode = rewriteViaStrInReConsume(node);
    2303         [ +  + ]:      16985 :   if (!retNode.isNull())
    2304                 :            :   {
    2305         [ +  - ]:       2798 :     Trace("regexp-ext-rewrite")
    2306                 :       1399 :         << "Regexp : rewrite : " << node << " -> " << retNode << std::endl;
    2307                 :       1399 :     return returnRewrite(node, retNode, Rewrite::RE_SIMPLE_CONSUME);
    2308                 :            :   }
    2309                 :            :   // check regular expression inclusion
    2310                 :            :   // This makes a regular expression that contains all possible model values
    2311                 :            :   // for x, and checks whether r includes this regular expression. If so,
    2312                 :            :   // the membership rewrites to true.
    2313                 :      15586 :   Node ret = rewriteViaMacroStrInReInclusion(node);
    2314         [ +  + ]:      15586 :   if (!ret.isNull())
    2315                 :            :   {
    2316                 :        134 :     return returnRewrite(node, ret, Rewrite::RE_IN_INCLUSION);
    2317                 :            :   }
    2318                 :      15452 :   return node;
    2319                 :      27196 : }
    2320                 :            : 
    2321                 :    1597271 : RewriteResponse SequencesRewriter::postRewrite(TNode node)
    2322                 :            : {
    2323         [ +  - ]:    3194542 :   Trace("sequences-postrewrite")
    2324                 :    1597271 :       << "Strings::SequencesRewriter::postRewrite start " << node << std::endl;
    2325                 :    1597271 :   Node retNode = node;
    2326                 :    1597271 :   Kind nk = node.getKind();
    2327         [ +  + ]:    1597271 :   if (nk == Kind::STRING_CONCAT)
    2328                 :            :   {
    2329                 :     253257 :     retNode = rewriteConcat(node);
    2330                 :            :   }
    2331         [ +  + ]:    1344014 :   else if (nk == Kind::EQUAL)
    2332                 :            :   {
    2333                 :     627020 :     retNode = rewriteEquality(node);
    2334                 :            :   }
    2335         [ +  + ]:     716994 :   else if (nk == Kind::STRING_LENGTH)
    2336                 :            :   {
    2337                 :     198772 :     retNode = rewriteLength(node);
    2338                 :            :   }
    2339         [ +  + ]:     518222 :   else if (nk == Kind::STRING_CHARAT)
    2340                 :            :   {
    2341                 :        401 :     retNode = rewriteCharAt(node);
    2342                 :            :   }
    2343         [ +  + ]:     517821 :   else if (nk == Kind::STRING_SUBSTR)
    2344                 :            :   {
    2345                 :     128632 :     retNode = rewriteSubstr(node);
    2346                 :            :   }
    2347         [ +  + ]:     389189 :   else if (nk == Kind::STRING_UPDATE)
    2348                 :            :   {
    2349                 :       2556 :     retNode = rewriteUpdate(node);
    2350                 :            :   }
    2351         [ +  + ]:     386633 :   else if (nk == Kind::STRING_CONTAINS)
    2352                 :            :   {
    2353                 :     131350 :     retNode = rewriteContains(node);
    2354                 :            :   }
    2355         [ +  + ]:     255283 :   else if (nk == Kind::STRING_INDEXOF)
    2356                 :            :   {
    2357                 :      13868 :     retNode = rewriteIndexof(node);
    2358                 :            :   }
    2359         [ +  + ]:     241415 :   else if (nk == Kind::STRING_INDEXOF_RE)
    2360                 :            :   {
    2361                 :       1610 :     retNode = rewriteIndexofRe(node);
    2362                 :            :   }
    2363         [ +  + ]:     239805 :   else if (nk == Kind::STRING_REPLACE)
    2364                 :            :   {
    2365                 :      17910 :     retNode = rewriteReplace(node);
    2366                 :            :   }
    2367         [ +  + ]:     221895 :   else if (nk == Kind::STRING_REPLACE_ALL)
    2368                 :            :   {
    2369                 :       3389 :     retNode = rewriteReplaceAll(node);
    2370                 :            :   }
    2371         [ +  + ]:     218506 :   else if (nk == Kind::STRING_REPLACE_RE)
    2372                 :            :   {
    2373                 :        785 :     retNode = rewriteReplaceRe(node);
    2374                 :            :   }
    2375         [ +  + ]:     217721 :   else if (nk == Kind::STRING_REPLACE_RE_ALL)
    2376                 :            :   {
    2377                 :        766 :     retNode = rewriteReplaceReAll(node);
    2378                 :            :   }
    2379         [ +  + ]:     216955 :   else if (nk == Kind::STRING_REV)
    2380                 :            :   {
    2381                 :        647 :     retNode = rewriteStrReverse(node);
    2382                 :            :   }
    2383 [ +  + ][ +  + ]:     216308 :   else if (nk == Kind::STRING_PREFIX || nk == Kind::STRING_SUFFIX)
    2384                 :            :   {
    2385                 :        998 :     retNode = rewritePrefixSuffix(node);
    2386                 :            :   }
    2387         [ +  + ]:     215310 :   else if (nk == Kind::STRING_IN_REGEXP)
    2388                 :            :   {
    2389                 :      27196 :     retNode = rewriteMembership(node);
    2390                 :            :   }
    2391         [ +  + ]:     188114 :   else if (nk == Kind::REGEXP_ALL)
    2392                 :            :   {
    2393                 :        116 :     retNode = rewriteAllRegExp(node);
    2394                 :            :   }
    2395         [ +  + ]:     187998 :   else if (nk == Kind::REGEXP_CONCAT)
    2396                 :            :   {
    2397                 :       8981 :     retNode = rewriteConcatRegExp(node);
    2398                 :            :   }
    2399 [ +  + ][ +  + ]:     179017 :   else if (nk == Kind::REGEXP_UNION || nk == Kind::REGEXP_INTER)
    2400                 :            :   {
    2401                 :       4515 :     retNode = rewriteAndOrRegExp(node);
    2402                 :            :   }
    2403         [ +  + ]:     174502 :   else if (nk == Kind::REGEXP_DIFF)
    2404                 :            :   {
    2405                 :        479 :     retNode = rewriteDifferenceRegExp(node);
    2406                 :            :   }
    2407         [ +  + ]:     174023 :   else if (nk == Kind::REGEXP_STAR)
    2408                 :            :   {
    2409                 :       4827 :     retNode = rewriteStarRegExp(node);
    2410                 :            :   }
    2411         [ +  + ]:     169196 :   else if (nk == Kind::REGEXP_PLUS)
    2412                 :            :   {
    2413                 :        239 :     retNode = rewritePlusRegExp(node);
    2414                 :            :   }
    2415         [ +  + ]:     168957 :   else if (nk == Kind::REGEXP_OPT)
    2416                 :            :   {
    2417                 :        114 :     retNode = rewriteOptionRegExp(node);
    2418                 :            :   }
    2419         [ +  + ]:     168843 :   else if (nk == Kind::REGEXP_RANGE)
    2420                 :            :   {
    2421                 :       1340 :     retNode = rewriteRangeRegExp(node);
    2422                 :            :   }
    2423         [ +  + ]:     167503 :   else if (nk == Kind::REGEXP_LOOP)
    2424                 :            :   {
    2425                 :        155 :     retNode = rewriteLoopRegExp(node);
    2426                 :            :   }
    2427         [ +  + ]:     167348 :   else if (nk == Kind::REGEXP_REPEAT)
    2428                 :            :   {
    2429                 :         28 :     retNode = rewriteRepeatRegExp(node);
    2430                 :            :   }
    2431         [ +  + ]:     167320 :   else if (nk == Kind::SEQ_UNIT)
    2432                 :            :   {
    2433                 :       3693 :     retNode = rewriteSeqUnit(node);
    2434                 :            :   }
    2435         [ +  + ]:     163627 :   else if (nk == Kind::SEQ_NTH)
    2436                 :            :   {
    2437                 :       9429 :     retNode = rewriteSeqNth(node);
    2438                 :            :   }
    2439                 :            : 
    2440         [ +  - ]:    3194542 :   Trace("sequences-postrewrite")
    2441                 :          0 :       << "Strings::SequencesRewriter::postRewrite returning " << retNode
    2442                 :    1597271 :       << std::endl;
    2443         [ +  + ]:    1597271 :   if (node != retNode)
    2444                 :            :   {
    2445         [ +  - ]:     615094 :     Trace("strings-rewrite-debug") << "Strings::SequencesRewriter::postRewrite "
    2446                 :     307547 :                                    << node << " to " << retNode << std::endl;
    2447                 :     307547 :     return RewriteResponse(REWRITE_AGAIN_FULL, retNode);
    2448                 :            :   }
    2449         [ +  - ]:    1289724 :   Trace("strings-rewrite-nf") << "No rewrites for : " << node << std::endl;
    2450                 :    1289724 :   return RewriteResponse(REWRITE_DONE, retNode);
    2451                 :    1597271 : }
    2452                 :            : 
    2453                 :    1560690 : RewriteResponse SequencesRewriter::preRewrite(TNode node)
    2454                 :            : {
    2455                 :    1560690 :   return RewriteResponse(REWRITE_DONE, node);
    2456                 :            : }
    2457                 :            : 
    2458                 :       9429 : Node SequencesRewriter::rewriteSeqNth(Node node)
    2459                 :            : {
    2460 [ -  + ][ -  + ]:       9429 :   Assert(node.getKind() == Kind::SEQ_NTH);
                 [ -  - ]
    2461                 :       9429 :   Node s = node[0];
    2462                 :       9429 :   Node i = node[1];
    2463 [ +  + ][ +  + ]:       9429 :   if (s.isConst() && i.isConst())
                 [ +  + ]
    2464                 :            :   {
    2465                 :        452 :     size_t len = Word::getLength(s);
    2466         [ +  + ]:        452 :     if (i.getConst<Rational>().sgn() != -1)
    2467                 :            :     {
    2468                 :        412 :       Integer posInt = i.getConst<Rational>().getNumerator();
    2469 [ +  + ][ +  + ]:        412 :       if (posInt.fitsUnsignedInt() && posInt < Integer(len))
         [ +  + ][ +  + ]
                 [ -  - ]
    2470                 :            :       {
    2471                 :        259 :         size_t pos = posInt.toUnsignedInt();
    2472                 :        259 :         Node ret = Word::getNth(s, pos);
    2473                 :        259 :         return returnRewrite(node, ret, Rewrite::SEQ_NTH_EVAL);
    2474                 :        259 :       }
    2475         [ +  + ]:        412 :     }
    2476                 :            :   }
    2477                 :            : 
    2478                 :       9170 :   std::vector<Node> prefix, suffix;
    2479                 :       9170 :   utils::getConcat(s, suffix);
    2480         [ +  + ]:      12374 :   if ((i.isConst() && i.getConst<Rational>().isZero())
    2481 [ +  + ][ +  + ]:      12374 :       || d_stringsEntail.stripSymbolicLength(suffix, prefix, 1, i, true))
                 [ +  + ]
    2482                 :            :   {
    2483         [ +  + ]:       1713 :     if (suffix.size() > 0)
    2484                 :            :     {
    2485         [ +  + ]:       1677 :       if (suffix[0].getKind() == Kind::SEQ_UNIT)
    2486                 :            :       {
    2487                 :            :         // (seq.nth (seq.++ prefix (seq.unit x) suffix) n) ---> x
    2488                 :            :         // if len(prefix) = n
    2489                 :        341 :         Node ret = suffix[0][0];
    2490                 :        341 :         return returnRewrite(node, ret, Rewrite::SEQ_NTH_EVAL_SYM);
    2491                 :        341 :       }
    2492                 :            :       // TODO: STRING_UNIT?
    2493                 :            :     }
    2494                 :            :   }
    2495                 :            : 
    2496                 :       8829 :   return node;
    2497                 :       9429 : }
    2498                 :            : 
    2499                 :        401 : Node SequencesRewriter::rewriteCharAt(Node node)
    2500                 :            : {
    2501 [ -  + ][ -  + ]:        401 :   Assert(node.getKind() == Kind::STRING_CHARAT);
                 [ -  - ]
    2502                 :        401 :   NodeManager* nm = nodeManager();
    2503                 :        401 :   Node one = nm->mkConstInt(Rational(1));
    2504                 :        802 :   Node retNode = nm->mkNode(Kind::STRING_SUBSTR, node[0], node[1], one);
    2505                 :        802 :   return returnRewrite(node, retNode, Rewrite::CHARAT_ELIM);
    2506                 :        401 : }
    2507                 :            : 
    2508                 :     128635 : Node SequencesRewriter::rewriteSubstr(Node node)
    2509                 :            : {
    2510 [ -  + ][ -  + ]:     128635 :   Assert(node.getKind() == Kind::STRING_SUBSTR);
                 [ -  - ]
    2511                 :            : 
    2512                 :     128635 :   NodeManager* nm = nodeManager();
    2513         [ +  + ]:     128635 :   if (node[0].isConst())
    2514                 :            :   {
    2515         [ +  + ]:      30965 :     if (Word::isEmpty(node[0]))
    2516                 :            :     {
    2517                 :       2594 :       Node ret = node[0];
    2518                 :       2594 :       return returnRewrite(node, ret, Rewrite::SS_EMPTYSTR);
    2519                 :       2594 :     }
    2520                 :            :     // rewriting for constant arguments
    2521                 :      28371 :     if (node[1].isConst() && node[2].isConst())
    2522                 :            :     {
    2523                 :      10792 :       Node s = node[0];
    2524                 :      10792 :       cvc5::internal::Rational rMaxInt(String::maxSize());
    2525                 :            :       uint32_t start;
    2526         [ +  + ]:      10792 :       if (node[1].getConst<Rational>() > rMaxInt)
    2527                 :            :       {
    2528                 :            :         // start beyond the maximum size of strings
    2529                 :            :         // thus, it must be beyond the end point of this string
    2530                 :         10 :         Node ret = Word::mkEmptyWord(node.getType());
    2531                 :         10 :         return returnRewrite(node, ret, Rewrite::SS_CONST_START_MAX_OOB);
    2532                 :         10 :       }
    2533         [ +  + ]:      10782 :       else if (node[1].getConst<Rational>().sgn() < 0)
    2534                 :            :       {
    2535                 :            :         // start before the beginning of the string
    2536                 :        930 :         Node ret = Word::mkEmptyWord(node.getType());
    2537                 :        930 :         return returnRewrite(node, ret, Rewrite::SS_CONST_START_NEG);
    2538                 :        930 :       }
    2539                 :            :       else
    2540                 :            :       {
    2541                 :       9852 :         start = node[1].getConst<Rational>().getNumerator().toUnsignedInt();
    2542         [ +  + ]:       9852 :         if (start >= Word::getLength(node[0]))
    2543                 :            :         {
    2544                 :            :           // start beyond the end of the string
    2545                 :        511 :           Node ret = Word::mkEmptyWord(node.getType());
    2546                 :        511 :           return returnRewrite(node, ret, Rewrite::SS_CONST_START_OOB);
    2547                 :        511 :         }
    2548                 :            :       }
    2549                 :      18682 :       Rational endPt(node[1].getConst<Rational>()
    2550                 :      18682 :                      + node[2].getConst<Rational>());
    2551         [ +  + ]:       9341 :       if (endPt > rMaxInt)
    2552                 :            :       {
    2553                 :            :         // take up to the end of the string
    2554                 :          2 :         size_t lenS = Word::getLength(s);
    2555                 :          2 :         Node ret = Word::suffix(s, lenS - start);
    2556                 :          2 :         return returnRewrite(node, ret, Rewrite::SS_CONST_LEN_MAX_OOB);
    2557                 :          2 :       }
    2558         [ +  + ]:       9339 :       else if (node[2].getConst<Rational>().sgn() <= 0)
    2559                 :            :       {
    2560                 :        860 :         Node ret = Word::mkEmptyWord(node.getType());
    2561                 :        860 :         return returnRewrite(node, ret, Rewrite::SS_CONST_LEN_NON_POS);
    2562                 :        860 :       }
    2563                 :            :       else
    2564                 :            :       {
    2565                 :            :         uint32_t len =
    2566                 :       8479 :             node[2].getConst<Rational>().getNumerator().toUnsignedInt();
    2567                 :            :         // should not overflow due to checks above
    2568 [ +  + ][ +  - ]:       8479 :         Assert(start == 0 || start + len > len);
         [ -  + ][ -  + ]
                 [ -  - ]
    2569         [ +  + ]:       8479 :         if (start + len > Word::getLength(node[0]))
    2570                 :            :         {
    2571                 :            :           // take up to the end of the string
    2572                 :        604 :           size_t lenS = Word::getLength(s);
    2573                 :        604 :           Node ret = Word::suffix(s, lenS - start);
    2574                 :        604 :           return returnRewrite(node, ret, Rewrite::SS_CONST_END_OOB);
    2575                 :        604 :         }
    2576                 :            :         else
    2577                 :            :         {
    2578                 :            :           // compute the substr using the constant string
    2579                 :       7875 :           Node ret = Word::substr(s, start, len);
    2580                 :       7875 :           return returnRewrite(node, ret, Rewrite::SS_CONST_SS);
    2581                 :       7875 :         }
    2582                 :            :       }
    2583                 :      10792 :     }
    2584                 :            :   }
    2585                 :     115249 :   Node zero = nm->mkConstInt(cvc5::internal::Rational(0));
    2586                 :            : 
    2587                 :            :   // if entailed non-positive length or negative start point
    2588         [ +  + ]:     115249 :   if (d_arithEntail.check(zero, node[1], true))
    2589                 :            :   {
    2590                 :        163 :     Node ret = Word::mkEmptyWord(node.getType());
    2591                 :        163 :     return returnRewrite(node, ret, Rewrite::SS_START_NEG);
    2592                 :        163 :   }
    2593         [ +  + ]:     115086 :   else if (d_arithEntail.check(zero, node[2]))
    2594                 :            :   {
    2595                 :       2928 :     Node ret = Word::mkEmptyWord(node.getType());
    2596                 :       2928 :     return returnRewrite(node, ret, Rewrite::SS_LEN_NON_POS);
    2597                 :       2928 :   }
    2598                 :            : 
    2599         [ +  + ]:     112158 :   if (node[0].getKind() == Kind::STRING_SUBSTR)
    2600                 :            :   {
    2601                 :            :     // (str.substr (str.substr x a b) c d) ---> "" if c >= b
    2602                 :            :     //
    2603                 :            :     // Note that this rewrite can be generalized to:
    2604                 :            :     //
    2605                 :            :     // (str.substr x a b) ---> "" if a >= (str.len x)
    2606                 :            :     //
    2607                 :            :     // This can be done when we generalize our entailment methods to
    2608                 :            :     // accept an optional context. Then we could conjecture that
    2609                 :            :     // (str.substr x a b) rewrites to "" and do a case analysis:
    2610                 :            :     //
    2611                 :            :     // - a < 0 or b < 0 (the result is trivially empty in these cases)
    2612                 :            :     // - a >= (str.len x) assuming that { a >= 0, b >= 0 }
    2613                 :            :     //
    2614                 :            :     // For example, for (str.substr (str.substr x a a) a a), we could
    2615                 :            :     // then deduce that under those assumptions, "a" is an
    2616                 :            :     // over-approximation of the length of (str.substr x a a), which
    2617                 :            :     // then allows us to reason that the result of the whole term must
    2618                 :            :     // be empty.
    2619         [ +  + ]:      11557 :     if (d_arithEntail.check(node[1], node[0][2]))
    2620                 :            :     {
    2621                 :         68 :       Node ret = Word::mkEmptyWord(node.getType());
    2622                 :         68 :       return returnRewrite(node, ret, Rewrite::SS_START_GEQ_LEN);
    2623                 :         68 :     }
    2624                 :            :   }
    2625         [ +  + ]:     100601 :   else if (node[0].getKind() == Kind::STRING_REPLACE)
    2626                 :            :   {
    2627                 :            :     // (str.substr (str.replace x y z) 0 n)
    2628                 :            :     //   ---> (str.replace (str.substr x 0 n) y z)
    2629                 :            :     // if (str.len y) = 1 and (str.len z) = 1
    2630         [ +  + ]:       3698 :     if (node[1] == zero)
    2631                 :            :     {
    2632                 :       3180 :       if (d_stringsEntail.checkLengthOne(node[0][1], true)
    2633                 :       3180 :           && d_stringsEntail.checkLengthOne(node[0][2], true))
    2634                 :            :       {
    2635                 :            :         Node ret = nm->mkNode(
    2636                 :            :             Kind::STRING_REPLACE,
    2637                 :        122 :             nm->mkNode(Kind::STRING_SUBSTR, node[0][0], node[1], node[2]),
    2638                 :            :             node[0][1],
    2639                 :        244 :             node[0][2]);
    2640                 :         61 :         return returnRewrite(node, ret, Rewrite::SUBSTR_REPL_SWAP);
    2641                 :         61 :       }
    2642                 :            :     }
    2643                 :            :   }
    2644                 :            : 
    2645                 :     112029 :   TypeNode stype = node.getType();
    2646                 :            : 
    2647                 :            :   // (str.substr s x x) ---> "" if (str.len s) <= 1
    2648                 :     112029 :   if (node[1] == node[2] && d_stringsEntail.checkLengthOne(node[0]))
    2649                 :            :   {
    2650                 :          6 :     Node ret = Word::mkEmptyWord(node.getType());
    2651                 :          6 :     return returnRewrite(node, ret, Rewrite::SS_LEN_ONE_Z_Z);
    2652                 :          6 :   }
    2653                 :            : 
    2654                 :     224046 :   Node slenRew = nm->mkNode(Kind::STRING_LENGTH, node[0]);
    2655                 :            :   // make strict to avoid infinite loops
    2656         [ +  + ]:     112023 :   if (d_arithEntail.check(node[2], slenRew, true))
    2657                 :            :   {
    2658                 :            :     // end point beyond end point of string, map to slenRew
    2659                 :        460 :     Node ret = nm->mkNode(Kind::STRING_SUBSTR, node[0], node[1], slenRew);
    2660                 :        230 :     return returnRewrite(node, ret, Rewrite::SS_END_PT_NORM);
    2661                 :        230 :   }
    2662                 :            : 
    2663                 :            :   // Rewrite based on symbolic length analysis, using the strings entailment
    2664                 :            :   // utility that is owned by this rewriter. This handles three rewrite rules
    2665                 :            :   // that are also included as part of the macro proof rewrite rule
    2666                 :            :   // MACRO_SUBSTR_STRIP_SYM_LENGTH.
    2667                 :            :   Rewrite ruleSymLen;
    2668                 :            :   Node retSymLen =
    2669                 :     111793 :       rewriteViaMacroSubstrStripSymLength(node, ruleSymLen, d_stringsEntail);
    2670         [ +  + ]:     111793 :   if (!retSymLen.isNull())
    2671                 :            :   {
    2672                 :       4727 :     return returnRewrite(node, retSymLen, ruleSymLen);
    2673                 :            :   }
    2674                 :            :   // combine substr
    2675         [ +  + ]:     107066 :   if (node[0].getKind() == Kind::STRING_SUBSTR)
    2676                 :            :   {
    2677                 :      11167 :     Node start_inner = node[0][1];
    2678                 :      11167 :     Node start_outer = node[1];
    2679                 :      11167 :     if (d_arithEntail.check(start_outer) && d_arithEntail.check(start_inner))
    2680                 :            :     {
    2681                 :            :       // both are positive
    2682                 :            :       // thus, start point is definitely start_inner+start_outer.
    2683                 :            :       // We can rewrite if it for certain what the length is
    2684                 :            : 
    2685                 :            :       // the length of a string from the inner substr subtracts the start point
    2686                 :            :       // of the outer substr
    2687                 :      15614 :       Node len_from_inner = nm->mkNode(Kind::SUB, node[0][2], start_outer);
    2688                 :       7807 :       Node len_from_outer = node[2];
    2689                 :       7807 :       Node new_len;
    2690                 :       7807 :       Rewrite rule = Rewrite::NONE;
    2691                 :            :       // take quantity that is for sure smaller than the other
    2692         [ -  + ]:       7807 :       if (len_from_inner == len_from_outer)
    2693                 :            :       {
    2694                 :          0 :         new_len = len_from_inner;
    2695                 :          0 :         rule = Rewrite::SS_COMBINE_EQ;
    2696                 :            :       }
    2697         [ +  + ]:       7807 :       else if (d_arithEntail.check(len_from_inner, len_from_outer))
    2698                 :            :       {
    2699                 :        590 :         new_len = len_from_outer;
    2700                 :        590 :         rule = Rewrite::SS_COMBINE_GEQ_INNER;
    2701                 :            :       }
    2702         [ +  + ]:       7217 :       else if (d_arithEntail.check(len_from_outer, len_from_inner))
    2703                 :            :       {
    2704                 :        283 :         new_len = len_from_inner;
    2705                 :        283 :         rule = Rewrite::SS_COMBINE_GEQ_OUTER;
    2706                 :            :       }
    2707         [ +  + ]:       7807 :       if (!new_len.isNull())
    2708                 :            :       {
    2709                 :       1746 :         Node new_start = nm->mkNode(Kind::ADD, start_inner, start_outer);
    2710                 :            :         Node ret =
    2711                 :       1746 :             nm->mkNode(Kind::STRING_SUBSTR, node[0][0], new_start, new_len);
    2712                 :        873 :         return returnRewrite(node, ret, rule);
    2713                 :        873 :       }
    2714 [ +  + ][ +  + ]:       9553 :     }
                 [ +  + ]
    2715 [ +  + ][ +  + ]:      12040 :   }
    2716                 :     106193 :   return node;
    2717                 :     115249 : }
    2718                 :            : 
    2719                 :       2556 : Node SequencesRewriter::rewriteUpdate(Node node)
    2720                 :            : {
    2721 [ -  + ][ -  + ]:       2556 :   Assert(node.getKind() == Kind::STRING_UPDATE);
                 [ -  - ]
    2722                 :       2556 :   Node s = node[0];
    2723                 :       2556 :   Node i = node[1];
    2724                 :       2556 :   Node x = node[2];
    2725         [ +  + ]:       2556 :   if (s.isConst())
    2726                 :            :   {
    2727         [ +  + ]:        383 :     if (Word::isEmpty(s))
    2728                 :            :     {
    2729                 :         14 :       return returnRewrite(node, s, Rewrite::UPD_EMPTYSTR);
    2730                 :            :     }
    2731                 :            :     // rewriting for constant arguments
    2732         [ +  + ]:        369 :     if (node[1].isConst())
    2733                 :            :     {
    2734                 :        325 :       cvc5::internal::Rational rMaxInt(String::maxSize());
    2735         [ -  + ]:        325 :       if (node[1].getConst<Rational>() > rMaxInt)
    2736                 :            :       {
    2737                 :            :         // start beyond the maximum size of strings
    2738                 :            :         // thus, it must be beyond the end point of this string
    2739                 :          0 :         return returnRewrite(node, s, Rewrite::UPD_CONST_INDEX_MAX_OOB);
    2740                 :            :       }
    2741         [ +  + ]:        325 :       else if (node[1].getConst<Rational>().sgn() < 0)
    2742                 :            :       {
    2743                 :            :         // start before the beginning of the string
    2744                 :         16 :         return returnRewrite(node, s, Rewrite::UPD_CONST_INDEX_NEG);
    2745                 :            :       }
    2746                 :            :       uint32_t start =
    2747                 :        309 :           node[1].getConst<Rational>().getNumerator().toUnsignedInt();
    2748                 :        309 :       size_t len = Word::getLength(s);
    2749         [ +  + ]:        309 :       if (start >= len)
    2750                 :            :       {
    2751                 :            :         // start beyond the end of the string
    2752                 :         34 :         return returnRewrite(node, s, Rewrite::UPD_CONST_INDEX_OOB);
    2753                 :            :       }
    2754         [ +  + ]:        275 :       if (node[2].isConst())
    2755                 :            :       {
    2756                 :        394 :         Node ret = Word::update(s, start, node[2]);
    2757                 :        197 :         return returnRewrite(node, ret, Rewrite::UPD_EVAL);
    2758                 :        197 :       }
    2759         [ +  + ]:        325 :     }
    2760                 :            :   }
    2761                 :            : 
    2762                 :       2295 :   NodeManager* nm = nodeManager();
    2763                 :       2295 :   Node zero = nm->mkConstInt(0);
    2764                 :       2295 :   Node sLen = nm->mkNode(Kind::STRING_LENGTH, s);
    2765                 :       2295 :   if (d_arithEntail.check(zero, i, true) || d_arithEntail.check(i, sLen))
    2766                 :            :   {
    2767                 :            :     // (seq.update s i x) ---> s if x < 0 or x >= len(s)
    2768                 :         22 :     Node ret = s;
    2769                 :         22 :     return returnRewrite(node, ret, Rewrite::UPD_OOB);
    2770                 :         22 :   }
    2771                 :            : 
    2772                 :       2273 :   std::vector<Node> prefix, suffix;
    2773                 :       2273 :   utils::getConcat(s, suffix);
    2774         [ +  + ]:       4052 :   if ((i.isConst() && i.getConst<Rational>().isZero())
    2775 [ +  + ][ +  + ]:       4052 :       || d_stringsEntail.stripSymbolicLength(suffix, prefix, 1, i, true))
                 [ +  + ]
    2776                 :            :   {
    2777                 :       1319 :     Node updateLen = nm->mkNode(Kind::STRING_LENGTH, x);
    2778                 :       1319 :     std::vector<Node> replaced;
    2779         [ +  + ]:       1319 :     if (d_stringsEntail.stripSymbolicLength(
    2780                 :            :             suffix, replaced, 1, updateLen, true))
    2781                 :            :     {
    2782                 :            :       // (seq.update (seq.++ p r s) i x) ---> (seq.++ p x s)
    2783                 :            :       // if len(p) = i and len(r) = len(x)
    2784                 :         63 :       prefix.emplace_back(x);
    2785                 :         63 :       prefix.insert(prefix.end(), suffix.begin(), suffix.end());
    2786                 :         63 :       Node ret = utils::mkConcat(prefix, node.getType());
    2787                 :         63 :       return returnRewrite(node, ret, Rewrite::UPD_EVAL_SYM);
    2788                 :         63 :     }
    2789 [ +  + ][ +  + ]:       1382 :   }
    2790                 :            : 
    2791 [ +  + ][ -  + ]:       2210 :   if (s.getKind() == Kind::STRING_REV && d_stringsEntail.checkLengthOne(x))
         [ +  + ][ -  + ]
                 [ -  - ]
    2792                 :            :   {
    2793                 :            :     // str.update(str.rev(s), n, t) --->
    2794                 :            :     //   str.rev(str.update(s, len(s) - (n + 1), t))
    2795                 :            :     Node idx =
    2796                 :          0 :         nm->mkNode(Kind::SUB,
    2797                 :          0 :                    {nm->mkNode(Kind::STRING_LENGTH, s),
    2798                 :          0 :                     nm->mkNode(Kind::ADD, i, nm->mkConstInt(Rational(1)))});
    2799                 :            :     Node ret = nm->mkNode(Kind::STRING_REV,
    2800                 :          0 :                           nm->mkNode(Kind::STRING_UPDATE, s[0], idx, x));
    2801                 :          0 :     return returnRewrite(node, ret, Rewrite::UPD_REV);
    2802                 :          0 :   }
    2803                 :            : 
    2804                 :       2210 :   return node;
    2805                 :       2556 : }
    2806                 :            : 
    2807                 :     305512 : Node SequencesRewriter::rewriteContains(Node node)
    2808                 :            : {
    2809 [ -  + ][ -  + ]:     305512 :   Assert(node.getKind() == Kind::STRING_CONTAINS);
                 [ -  - ]
    2810                 :     305512 :   NodeManager* nm = nodeManager();
    2811                 :            : 
    2812         [ +  + ]:     305512 :   if (node[0] == node[1])
    2813                 :            :   {
    2814                 :       6726 :     return returnRewrite(node, d_true, Rewrite::CTN_EQ);
    2815                 :            :   }
    2816         [ +  + ]:     298786 :   if (node[0].isConst())
    2817                 :            :   {
    2818         [ +  + ]:     122617 :     if (node[1].isConst())
    2819                 :            :     {
    2820                 :      10493 :       Node ret = nm->mkConst(Word::find(node[0], node[1]) != std::string::npos);
    2821                 :      10493 :       return returnRewrite(node, ret, Rewrite::CTN_CONST);
    2822                 :      10493 :     }
    2823                 :            :     else
    2824                 :            :     {
    2825                 :     112124 :       Node t = node[1];
    2826         [ +  + ]:     112124 :       if (Word::isEmpty(node[0]))
    2827                 :            :       {
    2828                 :     106422 :         Node len1 = nodeManager()->mkNode(Kind::STRING_LENGTH, node[1]);
    2829         [ +  + ]:      53211 :         if (d_arithEntail.check(len1, true))
    2830                 :            :         {
    2831                 :            :           // we handle the false case here since the rewrite for equality
    2832                 :            :           // uses this function, hence we want to conclude false if possible.
    2833                 :            :           // len(x)>0 => contains( "", x ) ---> false
    2834                 :       1878 :           Node ret = nodeManager()->mkConst(false);
    2835                 :       1878 :           return returnRewrite(node, ret, Rewrite::CTN_LHS_EMPTYSTR);
    2836                 :       1878 :         }
    2837         [ +  + ]:      53211 :       }
    2838         [ +  + ]:      58913 :       else if (d_stringsEntail.checkLengthOne(t))
    2839                 :            :       {
    2840                 :      31080 :         std::vector<Node> vec = Word::getChars(node[0]);
    2841                 :      15540 :         Node emp = Word::mkEmptyWord(t.getType());
    2842                 :      15540 :         NodeBuilder nb(nodeManager(), Kind::OR);
    2843                 :      15540 :         nb << emp.eqNode(t);
    2844         [ +  + ]:      33371 :         for (const Node& c : vec)
    2845                 :            :         {
    2846 [ -  + ][ -  + ]:      53493 :           AssertEqual(c.getType(), t.getType());
                 [ -  - ]
    2847                 :      17831 :           nb << c.eqNode(t);
    2848                 :            :         }
    2849                 :            : 
    2850                 :            :         // str.contains("ABCabc", t) --->
    2851                 :            :         // t = "" v t = "A" v t = "B" v t = "C" v t = "a" v t = "b" v t = "c"
    2852                 :            :         // if len(t) <= 1
    2853                 :      15540 :         Node ret = nb;
    2854                 :      15540 :         return returnRewrite(node, ret, Rewrite::CTN_SPLIT);
    2855                 :      15540 :       }
    2856                 :            :       else
    2857                 :            :       {
    2858                 :            :         Node ret =
    2859                 :      43373 :             rewriteViaRule(ProofRewriteRule::MACRO_STR_CONST_NCTN_CONCAT, node);
    2860         [ +  + ]:      43373 :         if (!ret.isNull())
    2861                 :            :         {
    2862                 :       2626 :           return returnRewrite(node, ret, Rewrite::CTN_NCONST_CTN_CONCAT);
    2863                 :            :         }
    2864         [ +  + ]:      43373 :       }
    2865         [ +  + ]:     112124 :     }
    2866                 :            :   }
    2867                 :     268249 :   Node maybeRew;
    2868                 :     268249 :   Rewrite maybeRule = Rewrite::NONE;
    2869         [ +  + ]:     268249 :   if (node[1].isConst())
    2870                 :            :   {
    2871                 :     121401 :     size_t len = Word::getLength(node[1]);
    2872         [ +  + ]:     121401 :     if (len == 0)
    2873                 :            :     {
    2874                 :            :       // contains( x, "" ) ---> true
    2875                 :      50719 :       Node ret = nodeManager()->mkConst(true);
    2876                 :      50719 :       return returnRewrite(node, ret, Rewrite::CTN_RHS_EMPTYSTR);
    2877                 :      50719 :     }
    2878         [ +  + ]:      70682 :     else if (len == 1)
    2879                 :            :     {
    2880                 :            :       // The following rewrites are specific to a single character second
    2881                 :            :       // argument of contains, where we can reason that this character is
    2882                 :            :       // not split over multiple components in the first argument.
    2883         [ +  + ]:      51757 :       if (node[0].getKind() == Kind::STRING_CONCAT)
    2884                 :            :       {
    2885                 :       9973 :         std::vector<Node> nc1;
    2886                 :       9973 :         utils::getConcat(node[0], nc1);
    2887                 :       9973 :         NodeBuilder nb(nodeManager(), Kind::OR);
    2888         [ +  + ]:      42819 :         for (const Node& ncc : nc1)
    2889                 :            :         {
    2890                 :      32846 :           nb << nm->mkNode(Kind::STRING_CONTAINS, ncc, node[1]);
    2891                 :            :         }
    2892                 :       9973 :         Node ret = nb.constructNode();
    2893                 :            :         // str.contains( x ++ y, "A" ) --->
    2894                 :            :         //   str.contains( x, "A" ) OR str.contains( y, "A" )
    2895                 :            :         // Remember the rewrite, we might find a better rule in following.
    2896                 :       9973 :         maybeRew = ret;
    2897                 :       9973 :         maybeRule = Rewrite::CTN_CONCAT_CHAR;
    2898                 :       9973 :       }
    2899         [ +  + ]:      41784 :       else if (node[0].getKind() == Kind::STRING_REPLACE)
    2900                 :            :       {
    2901                 :      14148 :         Node rplDomain = d_stringsEntail.checkContains(node[0][1], node[1]);
    2902 [ +  + ][ +  + ]:       7074 :         if (!rplDomain.isNull() && !rplDomain.getConst<bool>())
                 [ +  + ]
    2903                 :            :         {
    2904                 :        556 :           Node d1 = nm->mkNode(Kind::STRING_CONTAINS, node[0][0], node[1]);
    2905                 :        834 :           Node d2 = nm->mkNode(
    2906                 :            :               Kind::AND,
    2907                 :        556 :               {nm->mkNode(Kind::STRING_CONTAINS, node[0][0], node[0][1]),
    2908                 :        556 :                nm->mkNode(Kind::STRING_CONTAINS, node[0][2], node[1])});
    2909                 :        556 :           Node ret = nm->mkNode(Kind::OR, d1, d2);
    2910                 :            :           // If str.contains( y, "A" ) ---> false, then:
    2911                 :            :           // str.contains( str.replace( x, y, z ), "A" ) --->
    2912                 :            :           //   str.contains( x, "A" ) OR
    2913                 :            :           //   ( str.contains( x, y ) AND str.contains( z, "A" ) )
    2914                 :        278 :           return returnRewrite(node, ret, Rewrite::CTN_REPL_CHAR);
    2915                 :        278 :         }
    2916         [ +  + ]:       7074 :       }
    2917                 :            :     }
    2918                 :            :   }
    2919                 :     217252 :   std::vector<Node> nc1;
    2920                 :     217252 :   utils::getConcat(node[0], nc1);
    2921                 :     217252 :   std::vector<Node> nc2;
    2922                 :     217252 :   utils::getConcat(node[1], nc2);
    2923                 :            : 
    2924                 :            :   // component-wise containment
    2925                 :     217252 :   Node cret = rewriteViaRule(ProofRewriteRule::MACRO_STR_COMPONENT_CTN, node);
    2926         [ +  + ]:     217252 :   if (!cret.isNull())
    2927                 :            :   {
    2928                 :      17779 :     return returnRewrite(node, cret, Rewrite::CTN_COMPONENT);
    2929                 :            :   }
    2930                 :     199473 :   TypeNode stype = node[0].getType();
    2931                 :            : 
    2932                 :            :   // (str.contains (str.++ ... s ...) (str.substr s n m)) ---> true
    2933         [ +  + ]:     199473 :   if (node[1].getKind() == Kind::STRING_SUBSTR)
    2934                 :            :   {
    2935         [ +  + ]:      64273 :     if (std::find(nc1.begin(), nc1.end(), node[1][0]) != nc1.end())
    2936                 :            :     {
    2937                 :       2712 :       Node res = nm->mkConst(true);
    2938                 :       2712 :       return returnRewrite(node, res, Rewrite::CTN_CONCAT_CTN_SUBSTR);
    2939                 :       2712 :     }
    2940                 :            :   }
    2941                 :            : 
    2942                 :            :   // strip endpoints
    2943                 :            :   Node retStr =
    2944                 :     196761 :       rewriteViaRule(ProofRewriteRule::MACRO_STR_STRIP_ENDPOINTS, node);
    2945         [ +  + ]:     196761 :   if (!retStr.isNull())
    2946                 :            :   {
    2947                 :       3565 :     return returnRewrite(node, retStr, Rewrite::CTN_STRIP_ENDPT);
    2948                 :            :   }
    2949                 :            : 
    2950         [ +  + ]:     412966 :   for (const Node& n : nc2)
    2951                 :            :   {
    2952         [ +  + ]:     220315 :     if (nc2.size() > 1)
    2953                 :            :     {
    2954                 :      82628 :       Node ctnConst = d_stringsEntail.checkContains(node[0], n);
    2955 [ +  + ][ +  + ]:      41314 :       if (!ctnConst.isNull() && !ctnConst.getConst<bool>())
                 [ +  + ]
    2956                 :            :       {
    2957                 :        105 :         Node res = nm->mkConst(false);
    2958                 :        105 :         return returnRewrite(node, res, Rewrite::CTN_CONCAT_COM_NON_CTN);
    2959                 :        105 :       }
    2960         [ +  + ]:      41314 :     }
    2961         [ +  + ]:     220210 :     if (n.getKind() == Kind::STRING_REPLACE)
    2962                 :            :     {
    2963                 :            :       // (str.contains x (str.replace y z w)) --> false
    2964                 :            :       // if (str.contains x y) = false and (str.contains x w) = false
    2965                 :            :       //
    2966                 :            :       // Reasoning: (str.contains x y) checks that x does not contain y if the
    2967                 :            :       // replacement does not change y. (str.contains x w) checks that if the
    2968                 :            :       // replacement changes anything in y, the w makes it impossible for it to
    2969                 :            :       // occur in x.
    2970                 :      36162 :       Node ctnConst = d_stringsEntail.checkContains(node[0], n[0]);
    2971 [ +  + ][ +  + ]:      18081 :       if (!ctnConst.isNull() && !ctnConst.getConst<bool>())
                 [ +  + ]
    2972                 :            :       {
    2973                 :      11764 :         Node ctnConst2 = d_stringsEntail.checkContains(node[0], n[2]);
    2974 [ +  + ][ +  + ]:       5882 :         if (!ctnConst2.isNull() && !ctnConst2.getConst<bool>())
                 [ +  + ]
    2975                 :            :         {
    2976                 :        440 :           Node res = nm->mkConst(false);
    2977                 :        440 :           return returnRewrite(node, res, Rewrite::CTN_RPL_NON_CTN);
    2978                 :        440 :         }
    2979         [ +  + ]:       5882 :       }
    2980         [ +  + ]:      18081 :     }
    2981                 :            :   }
    2982                 :            : 
    2983                 :            :   // length entailment
    2984                 :     385302 :   Node len_n1 = nodeManager()->mkNode(Kind::STRING_LENGTH, node[0]);
    2985                 :     385302 :   Node len_n2 = nodeManager()->mkNode(Kind::STRING_LENGTH, node[1]);
    2986         [ +  + ]:     192651 :   if (d_arithEntail.check(len_n2, len_n1, true))
    2987                 :            :   {
    2988                 :            :     // len( n2 ) > len( n1 ) => contains( n1, n2 ) ---> false
    2989                 :       1021 :     Node ret = nodeManager()->mkConst(false);
    2990                 :       1021 :     return returnRewrite(node, ret, Rewrite::CTN_LEN_INEQ);
    2991                 :       1021 :   }
    2992                 :            : 
    2993                 :            :   // multi-set reasoning
    2994                 :            :   //   For example, contains( str.++( x, "b" ), str.++( "a", x ) ) ---> false
    2995                 :            :   //   since the number of a's in the second argument is greater than the number
    2996                 :            :   //   of a's in the first argument
    2997         [ +  + ]:     191630 :   if (d_stringsEntail.checkMultisetSubset(node[0], node[1]))
    2998                 :            :   {
    2999                 :        104 :     Node ret = nm->mkConst(false);
    3000                 :        104 :     return returnRewrite(node, ret, Rewrite::CTN_MSET_NSS);
    3001                 :        104 :   }
    3002                 :            : 
    3003         [ +  + ]:     191526 :   if (d_arithEntail.check(len_n2, len_n1, false))
    3004                 :            :   {
    3005                 :            :     // len( n2 ) >= len( n1 ) => contains( n1, n2 ) ---> n1 = n2
    3006                 :     137534 :     Node ret = node[0].eqNode(node[1]);
    3007                 :      68767 :     return returnRewrite(node, ret, Rewrite::CTN_LEN_INEQ_NSTRICT);
    3008                 :      68767 :   }
    3009                 :            : 
    3010                 :            :   // splitting
    3011         [ +  + ]:     122759 :   if (node[0].getKind() == Kind::STRING_CONCAT)
    3012                 :            :   {
    3013                 :            :     // e.g. (str.contains (str.++ x "AB" y) "C") -->
    3014                 :            :     // (or (str.contains x "C") (str.contains y "C")
    3015                 :      25980 :     Node ret = rewriteViaMacroStrSplitCtn(node);
    3016         [ +  + ]:      25980 :     if (!ret.isNull())
    3017                 :            :     {
    3018                 :        851 :       return returnRewrite(node, ret, Rewrite::CTN_SPLIT);
    3019                 :            :     }
    3020         [ +  + ]:      25980 :   }
    3021         [ +  + ]:      96779 :   else if (node[0].getKind() == Kind::STRING_SUBSTR)
    3022                 :            :   {
    3023                 :            :     // (str.contains (str.substr x n (str.len y)) y) --->
    3024                 :            :     //   (= (str.substr x n (str.len y)) y)
    3025                 :            :     //
    3026                 :            :     // TODO: Remove with under-/over-approximation
    3027         [ +  + ]:      17390 :     if (node[0][2] == nm->mkNode(Kind::STRING_LENGTH, node[1]))
    3028                 :            :     {
    3029                 :         38 :       Node ret = nm->mkNode(Kind::EQUAL, node[0], node[1]);
    3030                 :         19 :       return returnRewrite(node, ret, Rewrite::CTN_SUBSTR);
    3031                 :         19 :     }
    3032                 :            :   }
    3033         [ +  + ]:      79389 :   else if (node[0].getKind() == Kind::STRING_REPLACE)
    3034                 :            :   {
    3035         [ +  + ]:       9562 :     if (node[0][0] == node[0][2])
    3036                 :            :     {
    3037                 :            :       // (str.contains (str.replace x y x) y) ---> (str.contains x y)
    3038         [ +  + ]:       1216 :       if (node[0][1] == node[1])
    3039                 :            :       {
    3040                 :        110 :         Node ret = nm->mkNode(Kind::STRING_CONTAINS, node[0][0], node[1]);
    3041                 :         55 :         return returnRewrite(node, ret, Rewrite::CTN_REPL_TO_CTN);
    3042                 :         55 :       }
    3043                 :            : 
    3044                 :            :       // (str.contains (str.replace x y x) z) ---> (str.contains x z)
    3045                 :            :       // if (str.len z) <= 1
    3046         [ +  + ]:       1161 :       if (d_stringsEntail.checkLengthOne(node[1]))
    3047                 :            :       {
    3048                 :       1664 :         Node ret = nm->mkNode(Kind::STRING_CONTAINS, node[0][0], node[1]);
    3049                 :        832 :         return returnRewrite(node, ret, Rewrite::CTN_REPL_LEN_ONE_TO_CTN);
    3050                 :        832 :       }
    3051                 :            :     }
    3052                 :            : 
    3053                 :            :     // (str.contains (str.replace x y z) z) --->
    3054                 :            :     //   (or (str.contains x y) (str.contains x z))
    3055         [ +  + ]:       8675 :     if (node[0][2] == node[1])
    3056                 :            :     {
    3057                 :       4086 :       Node ret = nm->mkNode(
    3058                 :            :           Kind::OR,
    3059                 :       2724 :           {nm->mkNode(Kind::STRING_CONTAINS, node[0][0], node[0][1]),
    3060                 :       2724 :            nm->mkNode(Kind::STRING_CONTAINS, node[0][0], node[0][2])});
    3061                 :       1362 :       return returnRewrite(node, ret, Rewrite::CTN_REPL_TO_CTN_DISJ);
    3062                 :       1362 :     }
    3063                 :            :   }
    3064                 :      69827 :   else if (node[0].getKind() == Kind::STRING_ITOS && node[1].isConst())
    3065                 :            :   {
    3066                 :        726 :     String s = node[1].getConst<String>();
    3067         [ -  + ]:        726 :     if (!s.isNumber())
    3068                 :            :     {
    3069                 :          0 :       Node ret = nm->mkConst(false);
    3070                 :          0 :       return returnRewrite(node, ret, Rewrite::CTN_ITOS_NON_DIGIT);
    3071                 :          0 :     }
    3072         [ +  - ]:        726 :   }
    3073                 :            : 
    3074         [ +  + ]:     119640 :   if (node[1].getKind() == Kind::STRING_REPLACE)
    3075                 :            :   {
    3076                 :            :     // (str.contains x (str.replace y x y)) --->
    3077                 :            :     //   (str.contains x y)
    3078                 :      10667 :     if (node[0] == node[1][1] && node[1][0] == node[1][2])
    3079                 :            :     {
    3080                 :         40 :       Node ret = nm->mkNode(Kind::STRING_CONTAINS, node[0], node[1][0]);
    3081                 :         20 :       return returnRewrite(node, ret, Rewrite::CTN_REPL);
    3082                 :         20 :     }
    3083                 :            : 
    3084                 :            :     // (str.contains x (str.replace "" x y)) --->
    3085                 :            :     //   (= "" (str.replace "" x y))
    3086                 :            :     //
    3087                 :            :     // Note: Length-based reasoning is not sufficient to get this rewrite. We
    3088                 :            :     // can neither show that str.len(str.replace("", x, y)) - str.len(x) >= 0
    3089                 :            :     // nor str.len(x) - str.len(str.replace("", x, y)) >= 0
    3090                 :      10647 :     Node emp = Word::mkEmptyWord(stype);
    3091                 :      10647 :     if (node[0] == node[1][1] && node[1][0] == emp)
    3092                 :            :     {
    3093                 :          2 :       Node ret = nm->mkNode(Kind::EQUAL, emp, node[1]);
    3094                 :          1 :       return returnRewrite(node, ret, Rewrite::CTN_REPL_EMPTY);
    3095                 :          1 :     }
    3096         [ +  + ]:      10647 :   }
    3097                 :            :   // If we marked a rewrite but did not yet return it.
    3098         [ +  + ]:     119619 :   if (!maybeRew.isNull())
    3099                 :            :   {
    3100                 :       1523 :     return returnRewrite(node, maybeRew, maybeRule);
    3101                 :            :   }
    3102                 :            : 
    3103                 :     118096 :   return node;
    3104                 :     268249 : }
    3105                 :            : 
    3106                 :      13868 : Node SequencesRewriter::rewriteIndexof(Node node)
    3107                 :            : {
    3108 [ -  + ][ -  + ]:      13868 :   Assert(node.getKind() == Kind::STRING_INDEXOF);
                 [ -  - ]
    3109                 :      13868 :   NodeManager* nm = nodeManager();
    3110                 :            : 
    3111                 :      13868 :   if (node[2].isConst() && node[2].getConst<Rational>().sgn() < 0)
    3112                 :            :   {
    3113                 :            :     // z<0  implies  str.indexof( x, y, z ) --> -1
    3114                 :        185 :     Node negone = nm->mkConstInt(Rational(-1));
    3115                 :        185 :     return returnRewrite(node, negone, Rewrite::IDOF_NEG);
    3116                 :        185 :   }
    3117                 :            : 
    3118                 :            :   // the string type
    3119                 :      13683 :   TypeNode stype = node[0].getType();
    3120                 :            : 
    3121                 :            :   // evaluation and simple cases
    3122                 :      13683 :   std::vector<Node> children0;
    3123                 :      13683 :   utils::getConcat(node[0], children0);
    3124                 :      13683 :   if (children0[0].isConst() && node[1].isConst() && node[2].isConst())
    3125                 :            :   {
    3126                 :       1776 :     cvc5::internal::Rational rMaxInt(cvc5::internal::String::maxSize());
    3127         [ +  + ]:       1776 :     if (node[2].getConst<Rational>() > rMaxInt)
    3128                 :            :     {
    3129         [ -  + ]:         10 :       if (node[0].isConst())
    3130                 :            :       {
    3131                 :            :         // We know that, due to limitations on the size of string constants
    3132                 :            :         // in our implementation, that accessing a position greater than
    3133                 :            :         // rMaxInt is guaranteed to be out of bounds.
    3134                 :          0 :         Node negone = nm->mkConstInt(Rational(-1));
    3135                 :          0 :         return returnRewrite(node, negone, Rewrite::IDOF_MAX);
    3136                 :          0 :       }
    3137                 :            :     }
    3138                 :            :     else
    3139                 :            :     {
    3140 [ -  + ][ -  + ]:       1766 :       Assert(node[2].getConst<Rational>().sgn() >= 0);
                 [ -  - ]
    3141                 :       1766 :       Node s = children0[0];
    3142                 :       1766 :       Node t = node[1];
    3143                 :            :       uint32_t start =
    3144                 :       1766 :           node[2].getConst<Rational>().getNumerator().toUnsignedInt();
    3145                 :       1766 :       std::size_t ret = Word::find(s, t, start);
    3146         [ +  + ]:       1766 :       if (ret != std::string::npos)
    3147                 :            :       {
    3148                 :        719 :         Node retv = nm->mkConstInt(Rational(static_cast<unsigned>(ret)));
    3149                 :        719 :         return returnRewrite(node, retv, Rewrite::IDOF_FIND);
    3150                 :        719 :       }
    3151         [ +  + ]:       1047 :       else if (children0.size() == 1)
    3152                 :            :       {
    3153                 :        537 :         Node negone = nm->mkConstInt(Rational(-1));
    3154                 :        537 :         return returnRewrite(node, negone, Rewrite::IDOF_NFIND);
    3155                 :        537 :       }
    3156 [ +  + ][ +  + ]:       3022 :     }
    3157         [ +  + ]:       1776 :   }
    3158                 :            : 
    3159         [ +  + ]:      12427 :   if (node[0] == node[1])
    3160                 :            :   {
    3161         [ +  + ]:        970 :     if (node[2].isConst())
    3162                 :            :     {
    3163         [ +  - ]:         31 :       if (node[2].getConst<Rational>().sgn() == 0)
    3164                 :            :       {
    3165                 :            :         // indexof( x, x, 0 ) --> 0
    3166                 :         31 :         Node zero = nm->mkConstInt(Rational(0));
    3167                 :         31 :         return returnRewrite(node, zero, Rewrite::IDOF_EQ_CST_START);
    3168                 :         31 :       }
    3169                 :            :     }
    3170         [ -  + ]:        939 :     if (d_arithEntail.check(node[2], true))
    3171                 :            :     {
    3172                 :            :       // y>0  implies  indexof( x, x, y ) --> -1
    3173                 :          0 :       Node negone = nm->mkConstInt(Rational(-1));
    3174                 :          0 :       return returnRewrite(node, negone, Rewrite::IDOF_EQ_NSTART);
    3175                 :          0 :     }
    3176                 :        939 :     Node emp = Word::mkEmptyWord(stype);
    3177         [ +  + ]:        939 :     if (node[0] != emp)
    3178                 :            :     {
    3179                 :            :       // indexof( x, x, z ) ---> indexof( "", "", z )
    3180                 :        266 :       Node ret = nm->mkNode(Kind::STRING_INDEXOF, emp, emp, node[2]);
    3181                 :        133 :       return returnRewrite(node, ret, Rewrite::IDOF_EQ_NORM);
    3182                 :        133 :     }
    3183         [ +  + ]:        939 :   }
    3184                 :            : 
    3185                 :      24526 :   Node len0 = nm->mkNode(Kind::STRING_LENGTH, node[0]);
    3186                 :      24526 :   Node len1 = nm->mkNode(Kind::STRING_LENGTH, node[1]);
    3187                 :      24526 :   Node len0m2 = nm->mkNode(Kind::SUB, len0, node[2]);
    3188                 :            : 
    3189         [ +  + ]:      12263 :   if (node[1].isConst())
    3190                 :            :   {
    3191         [ +  + ]:       8608 :     if (Word::isEmpty(node[1]))
    3192                 :            :     {
    3193                 :       1060 :       if (d_arithEntail.check(len0, node[2]) && d_arithEntail.check(node[2]))
    3194                 :            :       {
    3195                 :            :         // len(x)>=z ^ z >=0 implies indexof( x, "", z ) ---> z
    3196                 :         98 :         return returnRewrite(node, node[2], Rewrite::IDOF_EMP_IDOF);
    3197                 :            :       }
    3198                 :            :     }
    3199                 :            :   }
    3200                 :            : 
    3201         [ +  + ]:      12165 :   if (d_arithEntail.check(len1, len0m2, true))
    3202                 :            :   {
    3203                 :            :     // len(x)-z < len(y)  implies  indexof( x, y, z ) ----> -1
    3204                 :         15 :     Node negone = nm->mkConstInt(Rational(-1));
    3205                 :         15 :     return returnRewrite(node, negone, Rewrite::IDOF_LEN);
    3206                 :         15 :   }
    3207                 :            : 
    3208                 :      12150 :   Node fstr = node[0];
    3209                 :      12150 :   if (!node[2].isConst() || node[2].getConst<Rational>().sgn() != 0)
    3210                 :            :   {
    3211                 :       4106 :     fstr = nm->mkNode(Kind::STRING_SUBSTR, node[0], node[2], len0);
    3212                 :            :   }
    3213                 :            : 
    3214                 :      24300 :   Node cmp_conr = d_stringsEntail.checkContains(fstr, node[1]);
    3215         [ +  - ]:      24300 :   Trace("strings-rewrite-debug") << "For " << node << ", check contains("
    3216 [ -  + ][ -  - ]:      12150 :                                  << fstr << ", " << node[1] << ")" << std::endl;
    3217         [ +  - ]:      12150 :   Trace("strings-rewrite-debug") << "...got " << cmp_conr << std::endl;
    3218                 :      12150 :   std::vector<Node> children1;
    3219                 :      12150 :   utils::getConcat(node[1], children1);
    3220         [ +  + ]:      12150 :   if (!cmp_conr.isNull())
    3221                 :            :   {
    3222         [ +  + ]:       2145 :     if (cmp_conr.getConst<bool>())
    3223                 :            :     {
    3224                 :       2103 :       if (node[2].isConst() && node[2].getConst<Rational>().sgn() == 0)
    3225                 :            :       {
    3226                 :            :         // past the first position in node[0] that contains node[1], we can drop
    3227                 :       1103 :         std::vector<Node> nb;
    3228                 :       1103 :         std::vector<Node> ne;
    3229                 :       1103 :         int cc = d_stringsEntail.componentContains(
    3230                 :            :             children0, children1, nb, ne, true, 1);
    3231 [ +  + ][ +  + ]:       1103 :         if (cc != -1 && !ne.empty())
                 [ +  + ]
    3232                 :            :         {
    3233                 :            :           // For example:
    3234                 :            :           // str.indexof(str.++(x,y,z),y,0) ---> str.indexof(str.++(x,y),y,0)
    3235                 :        173 :           Node nn = utils::mkConcat(children0, stype);
    3236                 :        346 :           Node ret = nm->mkNode(Kind::STRING_INDEXOF, nn, node[1], node[2]);
    3237                 :        173 :           return returnRewrite(node, ret, Rewrite::IDOF_DEF_CTN);
    3238                 :        173 :         }
    3239                 :            : 
    3240                 :            :         // Strip components from the beginning that are guaranteed not to match
    3241         [ +  + ]:        930 :         if (d_stringsEntail.stripConstantEndpoints(
    3242                 :            :                 children0, children1, nb, ne, 1))
    3243                 :            :         {
    3244                 :            :           // str.indexof(str.++("AB", x, "C"), "C", 0) --->
    3245                 :            :           // 2 + str.indexof(str.++(x, "C"), "C", 0)
    3246                 :         66 :           Node ret = nm->mkNode(
    3247                 :            :               Kind::ADD,
    3248                 :         44 :               {nm->mkNode(Kind::STRING_LENGTH, utils::mkConcat(nb, stype)),
    3249                 :         88 :                nm->mkNode(Kind::STRING_INDEXOF,
    3250                 :         44 :                           utils::mkConcat(children0, stype),
    3251                 :            :                           node[1],
    3252                 :         66 :                           node[2])});
    3253                 :         22 :           return returnRewrite(node, ret, Rewrite::IDOF_STRIP_CNST_ENDPTS);
    3254                 :         22 :         }
    3255 [ +  + ][ +  + ]:       1298 :       }
    3256                 :            :       // To show that the first argument definitely contains the second, the
    3257                 :            :       // index must be a valid index in the first argument. This ensures that
    3258                 :            :       // (str.indexof t "" n) is not rewritten to something other than -1 when n
    3259                 :            :       // is beyond the length of t. This is not required for the above rewrites,
    3260                 :            :       // which only apply when n=0.
    3261                 :       1908 :       if (d_arithEntail.check(node[2]) && d_arithEntail.check(len0, node[2]))
    3262                 :            :       {
    3263                 :            :         // strip symbolic length
    3264                 :        946 :         Node new_len = node[2];
    3265                 :        946 :         std::vector<Node> nr;
    3266         [ +  + ]:        946 :         if (d_stringsEntail.stripSymbolicLength(children0, nr, 1, new_len))
    3267                 :            :         {
    3268                 :            :           // For example:
    3269                 :            :           // z>=0 and z>str.len( x1 ) and str.contains( x2, y )-->true
    3270                 :            :           // implies
    3271                 :            :           // str.indexof( str.++( x1, x2 ), y, z ) --->
    3272                 :            :           // str.len( x1 ) + str.indexof( x2, y, z-str.len(x1) )
    3273                 :         38 :           Node nn = utils::mkConcat(children0, stype);
    3274                 :        114 :           Node ret = nm->mkNode(
    3275                 :            :               Kind::ADD,
    3276                 :         76 :               {nm->mkNode(Kind::SUB, node[2], new_len),
    3277                 :         76 :                nm->mkNode(Kind::STRING_INDEXOF, nn, node[1], new_len)});
    3278                 :         38 :           return returnRewrite(node, ret, Rewrite::IDOF_STRIP_SYM_LEN);
    3279                 :         38 :         }
    3280 [ +  + ][ +  + ]:        984 :       }
    3281                 :            :     }
    3282                 :            :     else
    3283                 :            :     {
    3284                 :            :       // str.contains( x, y ) --> false  implies  str.indexof(x,y,z) --> -1
    3285                 :         42 :       Node negone = nm->mkConstInt(Rational(-1));
    3286                 :         42 :       return returnRewrite(node, negone, Rewrite::IDOF_NCTN);
    3287                 :         42 :     }
    3288                 :            :   }
    3289                 :            :   else
    3290                 :            :   {
    3291                 :      10005 :     Node new_len = node[2];
    3292                 :      10005 :     std::vector<Node> nr;
    3293         [ +  + ]:      10005 :     if (d_stringsEntail.stripSymbolicLength(children0, nr, 1, new_len))
    3294                 :            :     {
    3295                 :            :       // Normalize the string before the start index.
    3296                 :            :       //
    3297                 :            :       // For example:
    3298                 :            :       // str.indexof(str.++("ABCD", x), y, 3) --->
    3299                 :            :       // str.indexof(str.++("AAAD", x), y, 3)
    3300                 :        260 :       Node nodeNr = utils::mkConcat(nr, stype);
    3301                 :        260 :       Node normNr = lengthPreserveRewrite(nodeNr);
    3302         [ +  + ]:        260 :       if (normNr != nodeNr)
    3303                 :            :       {
    3304                 :         43 :         std::vector<Node> normNrChildren;
    3305                 :         43 :         utils::getConcat(normNr, normNrChildren);
    3306                 :         43 :         std::vector<Node> children(normNrChildren);
    3307                 :         43 :         children.insert(children.end(), children0.begin(), children0.end());
    3308                 :         43 :         Node nn = utils::mkConcat(children, stype);
    3309                 :         86 :         Node res = nm->mkNode(Kind::STRING_INDEXOF, nn, node[1], node[2]);
    3310                 :         43 :         return returnRewrite(node, res, Rewrite::IDOF_NORM_PREFIX);
    3311                 :         43 :       }
    3312 [ +  + ][ +  + ]:        303 :     }
    3313 [ +  + ][ +  + ]:      10048 :   }
    3314                 :            : 
    3315                 :      11832 :   if (node[2].isConst() && node[2].getConst<Rational>().sgn() == 0)
    3316                 :            :   {
    3317                 :            :     Node retStr =
    3318                 :       7827 :         rewriteViaRule(ProofRewriteRule::MACRO_STR_STRIP_ENDPOINTS, node);
    3319         [ +  + ]:       7827 :     if (!retStr.isNull())
    3320                 :            :     {
    3321                 :            :       // For example:
    3322                 :            :       // str.indexof( str.++( x, "A" ), "B", 0 ) ---> str.indexof( x, "B", 0 )
    3323                 :         73 :       return returnRewrite(node, retStr, Rewrite::RPL_PULL_ENDPT);
    3324                 :            :     }
    3325         [ +  + ]:       7827 :   }
    3326                 :            : 
    3327                 :      11759 :   return node;
    3328                 :      13683 : }
    3329                 :            : 
    3330                 :       1610 : Node SequencesRewriter::rewriteIndexofRe(Node node)
    3331                 :            : {
    3332 [ -  + ][ -  + ]:       1610 :   Assert(node.getKind() == Kind::STRING_INDEXOF_RE);
                 [ -  - ]
    3333                 :       1610 :   NodeManager* nm = nodeManager();
    3334                 :       1610 :   Node s = node[0];
    3335                 :       1610 :   Node r = node[1];
    3336                 :       1610 :   Node n = node[2];
    3337                 :       1610 :   Node zero = nm->mkConstInt(Rational(0));
    3338                 :       1610 :   Node slen = nm->mkNode(Kind::STRING_LENGTH, s);
    3339                 :            : 
    3340                 :       1610 :   if (d_arithEntail.check(zero, n, true) || d_arithEntail.check(n, slen, true))
    3341                 :            :   {
    3342                 :         55 :     Node ret = nm->mkConstInt(Rational(-1));
    3343                 :         55 :     return returnRewrite(node, ret, Rewrite::INDEXOF_RE_INVALID_INDEX);
    3344                 :         55 :   }
    3345                 :            : 
    3346         [ +  + ]:       1555 :   if (RegExpEntail::isConstRegExp(r))
    3347                 :            :   {
    3348                 :       1049 :     Node neval = rewriteViaStrIndexofReEval(node);
    3349         [ +  + ]:       1049 :     if (!neval.isNull())
    3350                 :            :     {
    3351                 :        257 :       return returnRewrite(node, neval, Rewrite::INDEXOF_RE_EVAL);
    3352                 :            :     }
    3353                 :        792 :     if (d_arithEntail.check(n, zero) && d_arithEntail.check(slen, n))
    3354                 :            :     {
    3355                 :        516 :       String emptyStr("");
    3356         [ +  + ]:        516 :       if (RegExpEntail::testConstStringInRegExp(emptyStr, r))
    3357                 :            :       {
    3358                 :         14 :         return returnRewrite(node, n, Rewrite::INDEXOF_RE_EMP_RE);
    3359                 :            :       }
    3360         [ +  + ]:        516 :     }
    3361         [ +  + ]:        778 :     if (r.getKind() == Kind::REGEXP_NONE)
    3362                 :            :     {
    3363                 :         12 :       Node ret = nm->mkConstInt(Rational(-1));
    3364                 :         12 :       return returnRewrite(node, ret, Rewrite::INDEXOF_RE_NONE);
    3365                 :         12 :     }
    3366         [ +  + ]:       1049 :   }
    3367                 :       1272 :   return node;
    3368                 :       1610 : }
    3369                 :            : 
    3370                 :      17910 : Node SequencesRewriter::rewriteReplace(Node node)
    3371                 :            : {
    3372 [ -  + ][ -  + ]:      17910 :   Assert(node.getKind() == Kind::STRING_REPLACE);
                 [ -  - ]
    3373                 :      17910 :   NodeManager* nm = nodeManager();
    3374                 :            : 
    3375                 :            :   // the string type
    3376                 :      17910 :   TypeNode stype = node.getType();
    3377                 :            : 
    3378                 :      17910 :   std::vector<Node> children0;
    3379                 :      17910 :   utils::getConcat(node[0], children0);
    3380                 :            : 
    3381 [ +  + ][ +  + ]:      17910 :   if (node[1].isConst() && children0[0].isConst())
         [ +  - ][ +  + ]
                 [ -  - ]
    3382                 :            :   {
    3383                 :       4728 :     Node s = children0[0];
    3384                 :       4728 :     Node t = node[1];
    3385                 :       4728 :     std::size_t p = Word::find(s, t);
    3386         [ +  + ]:       4728 :     if (p == std::string::npos)
    3387                 :            :     {
    3388         [ +  + ]:       1344 :       if (children0.size() == 1)
    3389                 :            :       {
    3390                 :        966 :         return returnRewrite(node, node[0], Rewrite::RPL_CONST_NFIND);
    3391                 :            :       }
    3392                 :            :     }
    3393                 :            :     else
    3394                 :            :     {
    3395                 :       3384 :       Node s1 = Word::substr(s, 0, p);
    3396                 :       6768 :       Node s3 = Word::substr(s, p + Word::getLength(t));
    3397                 :       3384 :       std::vector<Node> children;
    3398         [ +  + ]:       3384 :       if (!Word::isEmpty(s1))
    3399                 :            :       {
    3400                 :        229 :         children.push_back(s1);
    3401                 :            :       }
    3402                 :       3384 :       children.push_back(node[2]);
    3403         [ +  + ]:       3384 :       if (!Word::isEmpty(s3))
    3404                 :            :       {
    3405                 :       1431 :         children.push_back(s3);
    3406                 :            :       }
    3407                 :       3384 :       children.insert(children.end(), children0.begin() + 1, children0.end());
    3408                 :       3384 :       Node ret;
    3409 [ +  + ][ +  + ]:       3384 :       if (children0.size() == 1 && node[2].isConst())
         [ +  + ][ +  + ]
                 [ -  - ]
    3410                 :            :       {
    3411                 :            :         // evaluate the constant, this ensures that we always immediately
    3412                 :            :         // evaluate constants immediately, which is important for proof
    3413                 :            :         // reconstruction.
    3414                 :       1621 :         ret = Word::mkWordFlatten(children);
    3415                 :            :       }
    3416                 :            :       else
    3417                 :            :       {
    3418                 :       1763 :         ret = utils::mkConcat(children, stype);
    3419                 :            :       }
    3420                 :       3384 :       return returnRewrite(node, ret, Rewrite::RPL_CONST_FIND);
    3421                 :       3384 :     }
    3422 [ +  + ][ +  + ]:       9078 :   }
    3423                 :            : 
    3424                 :      13560 :   if (node[1].isConst() && Word::isEmpty(node[1]))
    3425                 :            :   {
    3426                 :        548 :     Node ret = nm->mkNode(Kind::STRING_CONCAT, node[2], node[0]);
    3427                 :        274 :     return returnRewrite(node, ret, Rewrite::RPL_RPL_EMPTY);
    3428                 :        274 :   }
    3429                 :            : 
    3430                 :            :   // rewrites that apply to both replace and replaceall
    3431                 :      13286 :   Node rri = rewriteReplaceInternal(node);
    3432         [ +  + ]:      13286 :   if (!rri.isNull())
    3433                 :            :   {
    3434                 :            :     // printing of the rewrite managed by the call above
    3435                 :        759 :     return rri;
    3436                 :            :   }
    3437                 :            : 
    3438         [ +  + ]:      12527 :   if (node[0] == node[2])
    3439                 :            :   {
    3440                 :            :     // ( len( y )>=len(x) ) => str.replace( x, y, x ) ---> x
    3441                 :       4580 :     Node l0 = nodeManager()->mkNode(Kind::STRING_LENGTH, node[0]);
    3442                 :       4580 :     Node l1 = nodeManager()->mkNode(Kind::STRING_LENGTH, node[1]);
    3443         [ +  + ]:       2290 :     if (d_arithEntail.check(l1, l0))
    3444                 :            :     {
    3445                 :        872 :       return returnRewrite(node, node[0], Rewrite::RPL_RPL_LEN_ID);
    3446                 :            :     }
    3447 [ +  + ][ +  + ]:       3162 :   }
    3448                 :            : 
    3449                 :      11655 :   std::vector<Node> children1;
    3450                 :      11655 :   utils::getConcat(node[1], children1);
    3451                 :            : 
    3452                 :            :   // check if contains definitely does (or does not) hold
    3453                 :      23310 :   Node cmp_conr = d_stringsEntail.checkContains(node[0], node[1]);
    3454         [ +  + ]:      11655 :   if (!cmp_conr.isNull())
    3455                 :            :   {
    3456 [ -  + ][ -  + ]:       4660 :     Assert(cmp_conr.isConst());
                 [ -  - ]
    3457         [ +  + ]:       4660 :     if (cmp_conr.getConst<bool>())
    3458                 :            :     {
    3459                 :            :       // component-wise containment
    3460                 :       3838 :       std::vector<Node> cb;
    3461                 :       3838 :       std::vector<Node> ce;
    3462                 :       3838 :       int cc = d_stringsEntail.componentContains(
    3463                 :            :           children0, children1, cb, ce, true, 1);
    3464         [ +  + ]:       3838 :       if (cc != -1)
    3465                 :            :       {
    3466 [ +  + ][ +  + ]:       3647 :         if (cc == 0 && children0[0] == children1[0])
                 [ +  + ]
    3467                 :            :         {
    3468                 :            :           // definitely a prefix, can do the replace
    3469                 :            :           // for example,
    3470                 :            :           //   str.replace( str.++( x, "ab" ), str.++( x, "a" ), y )  --->
    3471                 :            :           //   str.++( y, "b" )
    3472                 :        178 :           std::vector<Node> cres;
    3473                 :        178 :           cres.push_back(node[2]);
    3474                 :        178 :           cres.insert(cres.end(), ce.begin(), ce.end());
    3475                 :        178 :           Node ret = utils::mkConcat(cres, stype);
    3476                 :        178 :           return returnRewrite(node, ret, Rewrite::RPL_CCTN_RPL);
    3477                 :        178 :         }
    3478         [ +  + ]:       3469 :         else if (!ce.empty())
    3479                 :            :         {
    3480                 :            :           // we can pull remainder past first definite containment
    3481                 :            :           // for example,
    3482                 :            :           //   str.replace( str.++( x, "ab" ), "a", y ) --->
    3483                 :            :           //   str.++( str.replace( str.++( x, "a" ), "a", y ), "b" )
    3484                 :            :           // this is independent of whether the second argument may be empty
    3485                 :        971 :           std::vector<Node> scc;
    3486                 :       2913 :           scc.push_back(nodeManager()->mkNode(Kind::STRING_REPLACE,
    3487                 :       1942 :                                               utils::mkConcat(children0, stype),
    3488                 :            :                                               node[1],
    3489                 :            :                                               node[2]));
    3490                 :        971 :           scc.insert(scc.end(), ce.begin(), ce.end());
    3491                 :        971 :           Node ret = utils::mkConcat(scc, stype);
    3492                 :        971 :           return returnRewrite(node, ret, Rewrite::RPL_CCTN);
    3493                 :        971 :         }
    3494                 :            :       }
    3495 [ +  + ][ +  + ]:       4987 :     }
    3496                 :            :     else
    3497                 :            :     {
    3498                 :            :       // ~contains( t, s ) => ( replace( t, s, r ) ----> t )
    3499                 :        822 :       return returnRewrite(node, node[0], Rewrite::RPL_NCTN);
    3500                 :            :     }
    3501                 :            :   }
    3502                 :            : 
    3503         [ +  + ]:       9684 :   if (d_stringsEntail.checkNonEmpty(node[1]))
    3504                 :            :   {
    3505                 :            :     // pull endpoints that can be stripped
    3506                 :            :     // for example,
    3507                 :            :     //   str.replace( str.++( "b", x, "b" ), "a", y ) --->
    3508                 :            :     //   str.++( "b", str.replace( x, "a", y ), "b" )
    3509                 :            :     Node retStr =
    3510                 :       5672 :         rewriteViaRule(ProofRewriteRule::MACRO_STR_STRIP_ENDPOINTS, node);
    3511         [ +  + ]:       5672 :     if (!retStr.isNull())
    3512                 :            :     {
    3513                 :        352 :       return returnRewrite(node, retStr, Rewrite::RPL_PULL_ENDPT);
    3514                 :            :     }
    3515         [ +  + ]:       5672 :   }
    3516                 :            : 
    3517                 :       9332 :   children1.clear();
    3518                 :       9332 :   utils::getConcat(node[1], children1);
    3519                 :       9332 :   Node lastChild1 = children1[children1.size() - 1];
    3520         [ +  + ]:       9332 :   if (lastChild1.getKind() == Kind::STRING_SUBSTR)
    3521                 :            :   {
    3522                 :            :     // (str.replace x (str.++ t (str.substr y i j)) z) --->
    3523                 :            :     // (str.replace x (str.++ t
    3524                 :            :     //                  (str.substr y i (+ (str.len x) 1 (- (str.len t))))) z)
    3525                 :            :     // if j > len(x)
    3526                 :            :     //
    3527                 :            :     // Reasoning: If the string to be replaced is longer than x, then it does
    3528                 :            :     // not matter how much longer it is, the result is always x. Thus, it is
    3529                 :            :     // fine to only look at the prefix of length len(x) + 1 - len(t).
    3530                 :            : 
    3531                 :        269 :     children1.pop_back();
    3532                 :            :     // Length of the non-substr components in the second argument
    3533                 :            :     Node partLen1 =
    3534                 :        538 :         nm->mkNode(Kind::STRING_LENGTH, utils::mkConcat(children1, stype));
    3535                 :        538 :     Node maxLen1 = nm->mkNode(Kind::ADD, partLen1, lastChild1[2]);
    3536                 :            : 
    3537                 :        269 :     Node zero = nm->mkConstInt(Rational(0));
    3538                 :        269 :     Node one = nm->mkConstInt(Rational(1));
    3539                 :        538 :     Node len0 = nm->mkNode(Kind::STRING_LENGTH, node[0]);
    3540                 :        538 :     Node len0_1 = nm->mkNode(Kind::ADD, len0, one);
    3541                 :            :     // Check len(t) + j > len(x) + 1
    3542         [ +  + ]:        269 :     if (d_arithEntail.check(maxLen1, len0_1, true))
    3543                 :            :     {
    3544                 :          4 :       children1.push_back(nm->mkNode(
    3545                 :            :           Kind::STRING_SUBSTR,
    3546                 :            :           lastChild1[0],
    3547                 :            :           lastChild1[1],
    3548                 :          8 :           nm->mkNode(Kind::ADD, len0, one, nm->mkNode(Kind::NEG, partLen1))));
    3549                 :            :       Node res = nm->mkNode(Kind::STRING_REPLACE,
    3550                 :            :                             node[0],
    3551                 :          8 :                             utils::mkConcat(children1, stype),
    3552                 :         16 :                             node[2]);
    3553                 :          4 :       return returnRewrite(node, res, Rewrite::REPL_SUBST_IDX);
    3554                 :          4 :     }
    3555 [ +  + ][ +  + ]:        289 :   }
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
    3556                 :            : 
    3557         [ +  + ]:       9328 :   if (node[0].getKind() == Kind::STRING_REPLACE)
    3558                 :            :   {
    3559                 :        882 :     Node x = node[0];
    3560                 :        882 :     Node y = node[1];
    3561                 :        882 :     Node z = node[2];
    3562                 :        882 :     if (x[0] == x[2] && x[0] == y)
    3563                 :            :     {
    3564                 :            :       // (str.replace (str.replace y w y) y z) -->
    3565                 :            :       //   (str.replace (str.replace y w z) y z)
    3566                 :            :       // if (str.len w) >= (str.len z) and w != z
    3567                 :            :       //
    3568                 :            :       // Reasoning: There are two cases: (1) w does not appear in y and (2) w
    3569                 :            :       // does appear in y.
    3570                 :            :       //
    3571                 :            :       // Case (1): In this case, the reasoning is trivial. The
    3572                 :            :       // inner replace does not do anything, so we can just replace its third
    3573                 :            :       // argument with any string.
    3574                 :            :       //
    3575                 :            :       // Case (2): After the inner replace, we are guaranteed to have a string
    3576                 :            :       // that contains y at the index of w in the original string y. The outer
    3577                 :            :       // replace then replaces that y with z, so we can short-circuit that
    3578                 :            :       // replace by directly replacing w with z in the inner replace. We can
    3579                 :            :       // only do that if the result of the new inner replace does not contain
    3580                 :            :       // y, otherwise we end up doing two replaces that are different from the
    3581                 :            :       // original expression. We enforce that by requiring that the length of w
    3582                 :            :       // has to be greater or equal to the length of z and that w and z have to
    3583                 :            :       // be different. This makes sure that an inner replace changes a string
    3584                 :            :       // to a string that is shorter than y, making it impossible for the outer
    3585                 :            :       // replace to match.
    3586                 :         71 :       Node w = x[1];
    3587                 :            : 
    3588                 :            :       // (str.len w) >= (str.len z)
    3589                 :         71 :       Node wlen = nm->mkNode(Kind::STRING_LENGTH, w);
    3590                 :         71 :       Node zlen = nm->mkNode(Kind::STRING_LENGTH, z);
    3591         [ +  + ]:         71 :       if (d_arithEntail.check(wlen, zlen))
    3592                 :            :       {
    3593                 :            :         // w != z
    3594 [ +  + ][ +  + ]:         39 :         if (w != z && w.isConst() && z.isConst())
         [ +  - ][ +  + ]
    3595                 :            :         {
    3596                 :            :           Node ret = nm->mkNode(Kind::STRING_REPLACE,
    3597                 :         22 :                                 nm->mkNode(Kind::STRING_REPLACE, y, w, z),
    3598                 :            :                                 y,
    3599                 :         44 :                                 z);
    3600                 :         11 :           return returnRewrite(node, ret, Rewrite::REPL_REPL_SHORT_CIRCUIT);
    3601                 :         11 :         }
    3602                 :            :       }
    3603 [ +  + ][ +  + ]:         93 :     }
                 [ +  + ]
    3604 [ +  + ][ +  + ]:        904 :   }
                 [ +  + ]
    3605                 :            : 
    3606         [ +  + ]:       9317 :   if (node[1].getKind() == Kind::STRING_REPLACE)
    3607                 :            :   {
    3608         [ +  + ]:       1046 :     if (node[1][0] == node[0])
    3609                 :            :     {
    3610                 :        304 :       if (node[1][0] == node[1][2] && node[1][0] == node[2])
    3611                 :            :       {
    3612                 :            :         // str.replace( x, str.replace( x, y, x ), x ) ---> x
    3613                 :         10 :         return returnRewrite(node, node[0], Rewrite::REPL_REPL2_INV_ID);
    3614                 :            :       }
    3615                 :        294 :       bool dualReplIteSuccess = false;
    3616                 :        588 :       Node cmp_con2 = d_stringsEntail.checkContains(node[1][0], node[1][2]);
    3617 [ +  + ][ -  + ]:        294 :       if (!cmp_con2.isNull() && !cmp_con2.getConst<bool>())
                 [ -  + ]
    3618                 :            :       {
    3619                 :            :         // str.contains( x, z ) ---> false
    3620                 :            :         //   implies
    3621                 :            :         // str.replace( x, str.replace( x, y, z ), w ) --->
    3622                 :            :         // ite( str.contains( x, y ), x, w )
    3623                 :          0 :         dualReplIteSuccess = true;
    3624                 :            :       }
    3625                 :            :       else
    3626                 :            :       {
    3627                 :            :         // str.contains( y, z ) ---> false and str.contains( z, y ) ---> false
    3628                 :            :         //   implies
    3629                 :            :         // str.replace( x, str.replace( x, y, z ), w ) --->
    3630                 :            :         // ite( str.contains( x, y ), x, w )
    3631                 :        294 :         cmp_con2 = d_stringsEntail.checkContains(node[1][1], node[1][2]);
    3632 [ +  + ][ +  + ]:        294 :         if (!cmp_con2.isNull() && !cmp_con2.getConst<bool>())
                 [ +  + ]
    3633                 :            :         {
    3634                 :         96 :           cmp_con2 = d_stringsEntail.checkContains(node[1][2], node[1][1]);
    3635 [ +  - ][ +  + ]:         96 :           if (!cmp_con2.isNull() && !cmp_con2.getConst<bool>())
                 [ +  + ]
    3636                 :            :           {
    3637                 :         40 :             dualReplIteSuccess = true;
    3638                 :            :           }
    3639                 :            :         }
    3640                 :            :       }
    3641         [ +  + ]:        294 :       if (dualReplIteSuccess)
    3642                 :            :       {
    3643                 :            :         Node res =
    3644                 :            :             nm->mkNode(Kind::ITE,
    3645                 :         80 :                        nm->mkNode(Kind::STRING_CONTAINS, node[0], node[1][1]),
    3646                 :            :                        node[0],
    3647                 :        160 :                        node[2]);
    3648                 :         40 :         return returnRewrite(node, res, Rewrite::REPL_DUAL_REPL_ITE);
    3649                 :         40 :       }
    3650         [ +  + ]:        294 :     }
    3651                 :            : 
    3652                 :        996 :     bool invSuccess = false;
    3653         [ +  + ]:        996 :     if (node[1][1] == node[0])
    3654                 :            :     {
    3655         [ +  + ]:        122 :       if (node[1][0] == node[1][2])
    3656                 :            :       {
    3657                 :            :         // str.replace(x, str.replace(y, x, y), w) ---> str.replace(x, y, w)
    3658                 :          6 :         invSuccess = true;
    3659                 :            :       }
    3660                 :        116 :       else if (node[1][1] == node[2] || node[1][0] == node[2])
    3661                 :            :       {
    3662                 :            :         // str.contains(y, z) ----> false and ( y == w or x == w ) implies
    3663                 :            :         //   implies
    3664                 :            :         // str.replace(x, str.replace(y, x, z), w) ---> str.replace(x, y, w)
    3665                 :        112 :         Node cmp_con2 = d_stringsEntail.checkContains(node[1][0], node[1][2]);
    3666 [ +  + ][ -  + ]:         56 :         invSuccess = !cmp_con2.isNull() && !cmp_con2.getConst<bool>();
    3667                 :         56 :       }
    3668                 :            :     }
    3669                 :            :     else
    3670                 :            :     {
    3671                 :            :       // str.contains(x, z) ----> false and str.contains(x, w) ----> false
    3672                 :            :       //   implies
    3673                 :            :       // str.replace(x, str.replace(y, z, w), u) ---> str.replace(x, y, u)
    3674                 :       1748 :       Node cmp_con2 = d_stringsEntail.checkContains(node[0], node[1][1]);
    3675 [ +  + ][ +  + ]:        874 :       if (!cmp_con2.isNull() && !cmp_con2.getConst<bool>())
                 [ +  + ]
    3676                 :            :       {
    3677                 :         80 :         cmp_con2 = d_stringsEntail.checkContains(node[0], node[1][2]);
    3678 [ +  + ][ -  + ]:         80 :         invSuccess = !cmp_con2.isNull() && !cmp_con2.getConst<bool>();
    3679                 :            :       }
    3680                 :        874 :     }
    3681         [ +  + ]:        996 :     if (invSuccess)
    3682                 :            :     {
    3683                 :         12 :       Node res = nm->mkNode(Kind::STRING_REPLACE, node[0], node[1][0], node[2]);
    3684                 :          6 :       return returnRewrite(node, res, Rewrite::REPL_REPL2_INV);
    3685                 :          6 :     }
    3686                 :            :   }
    3687         [ +  + ]:       9261 :   if (node[2].getKind() == Kind::STRING_REPLACE)
    3688                 :            :   {
    3689         [ +  + ]:        548 :     if (node[2][1] == node[0])
    3690                 :            :     {
    3691                 :            :       // str.contains( z, w ) ----> false implies
    3692                 :            :       // str.replace( x, w, str.replace( z, x, y ) ) ---> str.replace( x, w, z )
    3693                 :         48 :       Node cmp_con2 = d_stringsEntail.checkContains(node[2][0], node[1]);
    3694 [ +  + ][ -  + ]:         24 :       if (!cmp_con2.isNull() && !cmp_con2.getConst<bool>())
                 [ -  + ]
    3695                 :            :       {
    3696                 :            :         Node res =
    3697                 :          0 :             nm->mkNode(Kind::STRING_REPLACE, node[0], node[1], node[2][0]);
    3698                 :          0 :         return returnRewrite(node, res, Rewrite::REPL_REPL3_INV);
    3699                 :          0 :       }
    3700         [ +  - ]:         24 :     }
    3701         [ +  + ]:        548 :     if (node[2][0] == node[1])
    3702                 :            :     {
    3703                 :          4 :       bool success = false;
    3704                 :          4 :       if (node[2][0] == node[2][2] && node[2][1] == node[0])
    3705                 :            :       {
    3706                 :            :         // str.replace( x, y, str.replace( y, x, y ) ) ---> x
    3707                 :          0 :         success = true;
    3708                 :            :       }
    3709                 :            :       else
    3710                 :            :       {
    3711                 :            :         // str.contains( x, z ) ----> false implies
    3712                 :            :         // str.replace( x, y, str.replace( y, z, w ) ) ---> x
    3713                 :          4 :         cmp_conr = d_stringsEntail.checkContains(node[0], node[2][1]);
    3714 [ -  + ][ -  - ]:          4 :         success = !cmp_conr.isNull() && !cmp_conr.getConst<bool>();
    3715                 :            :       }
    3716         [ -  + ]:          4 :       if (success)
    3717                 :            :       {
    3718                 :          0 :         return returnRewrite(node, node[0], Rewrite::REPL_REPL3_INV_ID);
    3719                 :            :       }
    3720                 :            :     }
    3721                 :            :   }
    3722                 :            :   // miniscope based on components that do not contribute to contains
    3723                 :            :   // for example,
    3724                 :            :   //   str.replace( x ++ y ++ x ++ y, "A", z ) -->
    3725                 :            :   //   str.replace( x ++ y, "A", z ) ++ x ++ y
    3726                 :            :   // since if "A" occurs in x ++ y ++ x ++ y, then it must occur in x ++ y.
    3727         [ +  + ]:       9261 :   if (d_stringsEntail.checkLengthOne(node[1]))
    3728                 :            :   {
    3729                 :       4667 :     Node lastLhs;
    3730                 :       4667 :     unsigned lastCheckIndex = 0;
    3731         [ +  + ]:       4721 :     for (unsigned i = 1, iend = children0.size(); i < iend; i++)
    3732                 :            :     {
    3733                 :       1963 :       unsigned checkIndex = children0.size() - i;
    3734                 :       1963 :       std::vector<Node> checkLhs;
    3735                 :       3926 :       checkLhs.insert(
    3736                 :       3926 :           checkLhs.end(), children0.begin(), children0.begin() + checkIndex);
    3737                 :       1963 :       Node lhs = utils::mkConcat(checkLhs, stype);
    3738                 :       1963 :       Node rhs = children0[checkIndex];
    3739                 :       3926 :       Node ctn = d_stringsEntail.checkContains(lhs, rhs);
    3740 [ +  + ][ +  - ]:       1963 :       if (!ctn.isNull() && ctn.getConst<bool>())
                 [ +  + ]
    3741                 :            :       {
    3742                 :         54 :         lastLhs = lhs;
    3743                 :         54 :         lastCheckIndex = checkIndex;
    3744                 :            :       }
    3745                 :            :       else
    3746                 :            :       {
    3747                 :       1909 :         break;
    3748                 :            :       }
    3749 [ +  + ][ +  + ]:       7690 :     }
         [ +  + ][ +  + ]
    3750         [ +  + ]:       4667 :     if (!lastLhs.isNull())
    3751                 :            :     {
    3752                 :         44 :       std::vector<Node> remc(children0.begin() + lastCheckIndex,
    3753                 :         44 :                              children0.end());
    3754                 :         44 :       Node rem = utils::mkConcat(remc, stype);
    3755                 :         44 :       std::vector<Node> rchildren;
    3756                 :         44 :       rchildren.push_back(
    3757                 :         88 :           nm->mkNode(Kind::STRING_REPLACE, lastLhs, node[1], node[2]));
    3758                 :            :       // "inline" the components of concatenation, which makes RARE
    3759                 :            :       // reconstruction easier.
    3760                 :         44 :       utils::getConcat(rem, rchildren);
    3761                 :         44 :       Node ret = utils::mkConcat(rchildren, lastLhs.getType());
    3762                 :            :       // for example:
    3763                 :            :       //   str.replace( x ++ x, "A", y ) ---> str.replace( x, "A", y ) ++ x
    3764                 :            :       // Since we know that the first occurrence of "A" cannot be in the
    3765                 :            :       // second occurrence of x. Notice this is specific to single characters
    3766                 :            :       // due to complications with finds that span multiple components for
    3767                 :            :       // non-characters.
    3768                 :         44 :       return returnRewrite(node, ret, Rewrite::REPL_CHAR_NCONTRIB_FIND);
    3769                 :         44 :     }
    3770         [ +  + ]:       4667 :   }
    3771                 :            : 
    3772                 :            :   // TODO (#1180) incorporate these?
    3773                 :            :   // contains( t, s ) =>
    3774                 :            :   //   replace( replace( x, t, s ), s, r ) ----> replace( x, t, r )
    3775                 :            :   // contains( t, s ) =>
    3776                 :            :   //   contains( replace( t, s, r ), r ) ----> true
    3777                 :            : 
    3778                 :       9217 :   return node;
    3779                 :      17910 : }
    3780                 :            : 
    3781                 :       3389 : Node SequencesRewriter::rewriteReplaceAll(Node node)
    3782                 :            : {
    3783 [ -  + ][ -  + ]:       3389 :   Assert(node.getKind() == Kind::STRING_REPLACE_ALL);
                 [ -  - ]
    3784                 :            : 
    3785                 :       3389 :   TypeNode stype = node.getType();
    3786                 :            : 
    3787                 :       3389 :   if (node[0].isConst() && node[1].isConst())
    3788                 :            :   {
    3789                 :        793 :     std::vector<Node> children;
    3790                 :        793 :     Node s = node[0];
    3791                 :        793 :     Node t = node[1];
    3792                 :        793 :     if (Word::isEmpty(s) || Word::isEmpty(t))
    3793                 :            :     {
    3794                 :        471 :       return returnRewrite(node, node[0], Rewrite::REPLALL_EMPTY_FIND);
    3795                 :            :     }
    3796                 :        322 :     std::size_t sizeS = Word::getLength(s);
    3797                 :        322 :     std::size_t sizeT = Word::getLength(t);
    3798                 :        322 :     std::size_t index = 0;
    3799                 :        322 :     std::size_t curr = 0;
    3800                 :            :     do
    3801                 :            :     {
    3802                 :        563 :       curr = Word::find(s, t, index);
    3803         [ +  + ]:        563 :       if (curr != std::string::npos)
    3804                 :            :       {
    3805         [ +  + ]:        241 :         if (curr > index)
    3806                 :            :         {
    3807                 :         44 :           children.push_back(Word::substr(s, index, curr - index));
    3808                 :            :         }
    3809                 :        241 :         children.push_back(node[2]);
    3810                 :        241 :         index = curr + sizeT;
    3811                 :            :       }
    3812                 :            :       else
    3813                 :            :       {
    3814                 :        322 :         children.push_back(Word::substr(s, index, sizeS - index));
    3815                 :            :       }
    3816 [ +  + ][ +  - ]:        563 :     } while (curr != std::string::npos && curr < sizeS);
    3817 [ -  + ][ -  + ]:        322 :     Assert(!children.empty());
                 [ -  - ]
    3818                 :            :     // constant evaluation, construct the concatenation and flatten it.
    3819                 :        322 :     Node res;
    3820         [ +  + ]:        322 :     if (node[2].isConst())
    3821                 :            :     {
    3822                 :        319 :       res = Word::mkWordFlatten(children);
    3823                 :            :     }
    3824                 :            :     else
    3825                 :            :     {
    3826                 :          3 :       res = utils::mkConcat(children, stype);
    3827                 :            :     }
    3828                 :        322 :     return returnRewrite(node, res, Rewrite::REPLALL_CONST);
    3829                 :        793 :   }
    3830                 :            : 
    3831                 :            :   // rewrites that apply to both replace and replaceall
    3832                 :       2596 :   Node rri = rewriteReplaceInternal(node);
    3833         [ +  + ]:       2596 :   if (!rri.isNull())
    3834                 :            :   {
    3835                 :            :     // printing of the rewrite managed by the call above
    3836                 :        272 :     return rri;
    3837                 :            :   }
    3838                 :            : 
    3839                 :       4648 :   Node cmp_conr = d_stringsEntail.checkContains(node[0], node[1]);
    3840 [ +  + ][ +  + ]:       2324 :   if (!cmp_conr.isNull() && !cmp_conr.getConst<bool>())
                 [ +  + ]
    3841                 :            :   {
    3842                 :            :     // ~contains( t, s ) => ( replace_all( t, s, r ) ----> t )
    3843                 :        160 :     return returnRewrite(node, node[0], Rewrite::RPL_NCTN);
    3844                 :            :   }
    3845                 :            : 
    3846                 :       2164 :   return node;
    3847                 :       3389 : }
    3848                 :            : 
    3849                 :      15882 : Node SequencesRewriter::rewriteReplaceInternal(Node node)
    3850                 :            : {
    3851                 :      15882 :   Kind nk = node.getKind();
    3852 [ +  + ][ +  - ]:      15882 :   Assert(nk == Kind::STRING_REPLACE || nk == Kind::STRING_REPLACE_ALL);
         [ -  + ][ -  + ]
                 [ -  - ]
    3853                 :            : 
    3854         [ +  + ]:      15882 :   if (node[1] == node[2])
    3855                 :            :   {
    3856                 :        574 :     return returnRewrite(node, node[0], Rewrite::RPL_ID);
    3857                 :            :   }
    3858                 :            : 
    3859         [ +  + ]:      15308 :   if (node[0] == node[1])
    3860                 :            :   {
    3861                 :            :     // only holds for replaceall if non-empty
    3862 [ +  + ][ +  + ]:        789 :     if (nk == Kind::STRING_REPLACE || d_stringsEntail.checkNonEmpty(node[1]))
         [ +  + ][ +  + ]
                 [ -  - ]
    3863                 :            :     {
    3864                 :        457 :       return returnRewrite(node, node[2], Rewrite::RPL_REPLACE);
    3865                 :            :     }
    3866                 :            :   }
    3867                 :            : 
    3868                 :      14851 :   return Node::null();
    3869                 :            : }
    3870                 :            : 
    3871                 :        785 : Node SequencesRewriter::rewriteReplaceRe(Node node)
    3872                 :            : {
    3873 [ -  + ][ -  + ]:        785 :   Assert(node.getKind() == Kind::STRING_REPLACE_RE);
                 [ -  - ]
    3874                 :        785 :   NodeManager* nm = nodeManager();
    3875                 :        785 :   Node x = node[0];
    3876                 :        785 :   Node y = node[1];
    3877                 :        785 :   Node z = node[2];
    3878                 :            : 
    3879         [ +  + ]:        785 :   if (RegExpEntail::isConstRegExp(y))
    3880                 :            :   {
    3881                 :        609 :     Node neval = rewriteViaStrReplaceReEval(node);
    3882         [ +  + ]:        609 :     if (!neval.isNull())
    3883                 :            :     {
    3884                 :        359 :       return returnRewrite(node, neval, Rewrite::REPLACE_RE_EVAL);
    3885                 :            :     }
    3886                 :            :     // str.replace_re( x, y, z ) ---> z ++ x if "" in y ---> true
    3887                 :        250 :     String emptyStr("");
    3888         [ +  + ]:        250 :     if (RegExpEntail::testConstStringInRegExp(emptyStr, y))
    3889                 :            :     {
    3890                 :          8 :       Node ret = nm->mkNode(Kind::STRING_CONCAT, z, x);
    3891                 :          4 :       return returnRewrite(node, ret, Rewrite::REPLACE_RE_EMP_RE);
    3892                 :          4 :     }
    3893         [ +  + ]:        246 :     if (y.getKind() == Kind::REGEXP_NONE)
    3894                 :            :     {
    3895                 :         27 :       return returnRewrite(node, x, Rewrite::REPLACE_RE_NONE);
    3896                 :            :     }
    3897 [ +  + ][ +  + ]:        640 :   }
    3898                 :        395 :   return node;
    3899                 :        785 : }
    3900                 :            : 
    3901                 :        766 : Node SequencesRewriter::rewriteReplaceReAll(Node node)
    3902                 :            : {
    3903 [ -  + ][ -  + ]:        766 :   Assert(node.getKind() == Kind::STRING_REPLACE_RE_ALL);
                 [ -  - ]
    3904                 :        766 :   Node x = node[0];
    3905                 :        766 :   Node y = node[1];
    3906                 :        766 :   Node z = node[2];
    3907                 :            : 
    3908         [ +  + ]:        766 :   if (RegExpEntail::isConstRegExp(y))
    3909                 :            :   {
    3910                 :        636 :     Node neval = rewriteViaStrReplaceReAllEval(node);
    3911         [ +  + ]:        636 :     if (!neval.isNull())
    3912                 :            :     {
    3913                 :        440 :       return returnRewrite(node, neval, Rewrite::REPLACE_RE_ALL_EVAL);
    3914                 :            :     }
    3915         [ +  + ]:        196 :     if (y.getKind() == Kind::REGEXP_NONE)
    3916                 :            :     {
    3917                 :         10 :       return returnRewrite(node, x, Rewrite::REPLACE_RE_ALL_NONE);
    3918                 :            :     }
    3919         [ +  + ]:        636 :   }
    3920                 :            : 
    3921                 :        316 :   return node;
    3922                 :        766 : }
    3923                 :            : 
    3924                 :       1305 : std::pair<size_t, size_t> SequencesRewriter::firstMatch(Node n, Node r)
    3925                 :            : {
    3926                 :       1305 :   Assert(n.isConst() && n.getType().isStringLike());
    3927 [ -  + ][ -  + ]:       1305 :   Assert(r.getType().isRegExp());
                 [ -  - ]
    3928                 :       1305 :   NodeManager* nm = nodeManager();
    3929                 :            : 
    3930                 :       2610 :   Node re = nm->mkNode(Kind::REGEXP_CONCAT, r, d_sigmaStar);
    3931                 :       1305 :   String s = n.getConst<String>();
    3932                 :            : 
    3933         [ +  + ]:       1305 :   if (s.size() == 0)
    3934                 :            :   {
    3935         [ +  + ]:        154 :     if (RegExpEntail::testConstStringInRegExp(s, r))
    3936                 :            :     {
    3937                 :         39 :       return std::make_pair(0, 0);
    3938                 :            :     }
    3939                 :            :     else
    3940                 :            :     {
    3941                 :        115 :       return std::make_pair(string::npos, string::npos);
    3942                 :            :     }
    3943                 :            :   }
    3944                 :            : 
    3945         [ +  + ]:       2269 :   for (size_t i = 0, size = s.size(); i < size; i++)
    3946                 :            :   {
    3947                 :       1912 :     String ss = s.substr(i);
    3948         [ +  + ]:       1912 :     if (RegExpEntail::testConstStringInRegExp(ss, re))
    3949                 :            :     {
    3950         [ +  - ]:       1766 :       for (size_t j = i; j <= size; j++)
    3951                 :            :       {
    3952                 :       1766 :         String substr = s.substr(i, j - i);
    3953         [ +  + ]:       1766 :         if (RegExpEntail::testConstStringInRegExp(substr, r))
    3954                 :            :         {
    3955                 :        794 :           return std::make_pair(i, j);
    3956                 :            :         }
    3957         [ +  + ]:       1766 :       }
    3958                 :            :     }
    3959         [ +  + ]:       1912 :   }
    3960                 :            : 
    3961                 :        357 :   return std::make_pair(string::npos, string::npos);
    3962                 :       1305 : }
    3963                 :            : 
    3964                 :        647 : Node SequencesRewriter::rewriteStrReverse(Node node)
    3965                 :            : {
    3966 [ -  + ][ -  + ]:        647 :   Assert(node.getKind() == Kind::STRING_REV);
                 [ -  - ]
    3967                 :        647 :   NodeManager* nm = nodeManager();
    3968                 :        647 :   Node x = node[0];
    3969         [ +  + ]:        647 :   if (x.isConst())
    3970                 :            :   {
    3971                 :            :     // reverse the characters in the constant
    3972                 :        159 :     Node retNode = Word::reverse(x);
    3973                 :        159 :     return returnRewrite(node, retNode, Rewrite::STR_CONV_CONST);
    3974                 :        159 :   }
    3975         [ +  + ]:        488 :   else if (x.getKind() == Kind::STRING_CONCAT)
    3976                 :            :   {
    3977                 :         92 :     std::vector<Node> children;
    3978         [ +  + ]:        278 :     for (const Node& nc : x)
    3979                 :            :     {
    3980                 :        186 :       children.push_back(nm->mkNode(Kind::STRING_REV, nc));
    3981                 :        186 :     }
    3982                 :         92 :     std::reverse(children.begin(), children.end());
    3983                 :            :     // rev( x1 ++ x2 ) --> rev( x2 ) ++ rev( x1 )
    3984                 :         92 :     Node retNode = nm->mkNode(Kind::STRING_CONCAT, children);
    3985                 :         92 :     return returnRewrite(node, retNode, Rewrite::STR_REV_MINSCOPE_CONCAT);
    3986                 :         92 :   }
    3987         [ +  + ]:        396 :   else if (x.getKind() == Kind::STRING_REV)
    3988                 :            :   {
    3989                 :            :     // rev( rev( x ) ) --> x
    3990                 :         12 :     Node retNode = x[0];
    3991                 :         12 :     return returnRewrite(node, retNode, Rewrite::STR_REV_IDEM);
    3992                 :         12 :   }
    3993 [ +  - ][ +  + ]:        384 :   else if (x.getKind() == Kind::STRING_UNIT || x.getKind() == Kind::SEQ_UNIT)
                 [ +  + ]
    3994                 :            :   {
    3995                 :            :     // rev( str.unit( x ) ) --> str.unit( x )
    3996                 :         14 :     return returnRewrite(node, x, Rewrite::STR_REV_UNIT);
    3997                 :            :   }
    3998                 :        370 :   return node;
    3999                 :        647 : }
    4000                 :            : 
    4001                 :        998 : Node SequencesRewriter::rewritePrefixSuffix(Node n)
    4002                 :            : {
    4003 [ +  + ][ +  - ]:        998 :   Assert(n.getKind() == Kind::STRING_PREFIX
         [ -  + ][ -  + ]
                 [ -  - ]
    4004                 :            :          || n.getKind() == Kind::STRING_SUFFIX);
    4005                 :        998 :   bool isPrefix = n.getKind() == Kind::STRING_PREFIX;
    4006         [ +  + ]:        998 :   if (n[0] == n[1])
    4007                 :            :   {
    4008                 :         33 :     Node ret = nodeManager()->mkConst(true);
    4009                 :         33 :     return returnRewrite(n, ret, Rewrite::SUF_PREFIX_EQ);
    4010                 :         33 :   }
    4011         [ +  + ]:        965 :   if (n[0].isConst())
    4012                 :            :   {
    4013         [ +  + ]:        565 :     if (Word::isEmpty(n[0]))
    4014                 :            :     {
    4015                 :         29 :       Node ret = nodeManager()->mkConst(true);
    4016                 :         29 :       return returnRewrite(n, ret, Rewrite::SUF_PREFIX_EMPTY_CONST);
    4017                 :         29 :     }
    4018                 :            :   }
    4019         [ +  + ]:        936 :   if (n[1].isConst())
    4020                 :            :   {
    4021                 :        331 :     Node s = n[1];
    4022                 :        331 :     size_t lenS = Word::getLength(s);
    4023         [ +  + ]:        331 :     if (n[0].isConst())
    4024                 :            :     {
    4025                 :        109 :       Node ret = nodeManager()->mkConst(false);
    4026                 :        109 :       Node t = n[0];
    4027                 :        109 :       size_t lenT = Word::getLength(t);
    4028         [ +  + ]:        109 :       if (lenS >= lenT)
    4029                 :            :       {
    4030                 :         68 :         if ((isPrefix && t == Word::prefix(s, lenT))
    4031                 :         68 :             || (!isPrefix && t == Word::suffix(s, lenT)))
    4032                 :            :         {
    4033                 :         32 :           ret = nodeManager()->mkConst(true);
    4034                 :            :         }
    4035                 :            :       }
    4036                 :        109 :       return returnRewrite(n, ret, Rewrite::SUF_PREFIX_CONST);
    4037                 :        109 :     }
    4038         [ +  + ]:        222 :     else if (lenS == 0)
    4039                 :            :     {
    4040                 :          8 :       Node ret = n[0].eqNode(n[1]);
    4041                 :          4 :       return returnRewrite(n, ret, Rewrite::SUF_PREFIX_EMPTY);
    4042                 :          4 :     }
    4043         [ +  - ]:        218 :     else if (lenS == 1)
    4044                 :            :     {
    4045                 :            :       // (str.prefix x "A") and (str.suffix x "A") are equivalent to
    4046                 :            :       // (str.contains "A" x )
    4047                 :        436 :       Node ret = nodeManager()->mkNode(Kind::STRING_CONTAINS, n[1], n[0]);
    4048                 :        218 :       return returnRewrite(n, ret, Rewrite::SUF_PREFIX_CTN);
    4049                 :        218 :     }
    4050         [ -  + ]:        331 :   }
    4051                 :       1210 :   Node lens = nodeManager()->mkNode(Kind::STRING_LENGTH, n[0]);
    4052                 :       1210 :   Node lent = nodeManager()->mkNode(Kind::STRING_LENGTH, n[1]);
    4053                 :            : 
    4054                 :            :   // Check if we can turn the prefix/suffix into an equality by showing that the
    4055                 :            :   // prefix/suffix is at least as long as the string
    4056         [ +  + ]:        605 :   if (d_arithEntail.check(lens, lent))
    4057                 :            :   {
    4058                 :         94 :     Node retNode = n[0].eqNode(n[1]);
    4059                 :         47 :     return returnRewrite(n, retNode, Rewrite::SUF_PREFIX_TO_EQS);
    4060                 :         47 :   }
    4061                 :            : 
    4062                 :        558 :   Node val;
    4063         [ +  + ]:        558 :   if (isPrefix)
    4064                 :            :   {
    4065                 :        343 :     val = nodeManager()->mkConstInt(cvc5::internal::Rational(0));
    4066                 :            :   }
    4067                 :            :   else
    4068                 :            :   {
    4069                 :        215 :     val = nodeManager()->mkNode(Kind::SUB, lent, lens);
    4070                 :            :   }
    4071                 :            : 
    4072                 :            :   // general reduction to equality + substr
    4073                 :            :   Node retNode =
    4074                 :       1116 :       n[0].eqNode(nodeManager()->mkNode(Kind::STRING_SUBSTR, n[1], val, lens));
    4075                 :            : 
    4076                 :        558 :   return returnRewrite(n, retNode, Rewrite::SUF_PREFIX_ELIM);
    4077                 :        605 : }
    4078                 :            : 
    4079                 :        262 : Node SequencesRewriter::lengthPreserveRewrite(Node n)
    4080                 :            : {
    4081                 :        262 :   NodeManager* nm = nodeManager();
    4082                 :            :   Node len =
    4083                 :        524 :       d_arithEntail.rewriteLengthIntro(nm->mkNode(Kind::STRING_LENGTH, n));
    4084                 :        262 :   len = d_arithEntail.rewriteArith(len);
    4085                 :        524 :   Node res = canonicalStrForSymbolicLength(len, n.getType());
    4086         [ -  + ]:        524 :   return res.isNull() ? n : res;
    4087                 :        262 : }
    4088                 :            : 
    4089                 :        268 : Node SequencesRewriter::canonicalStrForSymbolicLength(Node len,
    4090                 :            :                                                       TypeNode stype) const
    4091                 :            : {
    4092                 :        268 :   NodeManager* nm = nodeManager();
    4093                 :            : 
    4094                 :        268 :   Node res;
    4095         [ +  + ]:        268 :   if (len.isConst())
    4096                 :            :   {
    4097                 :            :     // c -> "A" repeated c times
    4098                 :        238 :     Rational ratLen = len.getConst<Rational>();
    4099 [ -  + ][ -  + ]:        238 :     Assert(ratLen.getDenominator() == 1);
                 [ -  - ]
    4100                 :        238 :     Integer intLen = ratLen.getNumerator();
    4101                 :        238 :     uint32_t u = intLen.getUnsignedInt();
    4102         [ +  - ]:        238 :     if (stype.isString())  // string-only
    4103                 :            :     {
    4104                 :        238 :       res = nm->mkConst(String(std::string(u, 'A')));
    4105                 :            :     }
    4106                 :            :     // we could do this for sequences, but we need to be careful: some
    4107                 :            :     // sorts do not permit values that the solver can handle (e.g. uninterpreted
    4108                 :            :     // sorts and arrays).
    4109                 :        238 :   }
    4110         [ +  + ]:         30 :   else if (len.getKind() == Kind::ADD)
    4111                 :            :   {
    4112                 :            :     // x + y -> norm(x) + norm(y)
    4113                 :          2 :     NodeBuilder concatBuilder(nodeManager(), Kind::STRING_CONCAT);
    4114         [ +  + ]:          6 :     for (const auto& n : len)
    4115                 :            :     {
    4116                 :          8 :       Node sn = canonicalStrForSymbolicLength(n, stype);
    4117         [ -  + ]:          4 :       if (sn.isNull())
    4118                 :            :       {
    4119                 :          0 :         return Node::null();
    4120                 :            :       }
    4121                 :          4 :       std::vector<Node> snChildren;
    4122                 :          4 :       utils::getConcat(sn, snChildren);
    4123                 :          4 :       concatBuilder.append(snChildren);
    4124 [ +  - ][ +  - ]:          4 :     }
    4125                 :          2 :     res = concatBuilder.constructNode();
    4126         [ +  - ]:          2 :   }
    4127         [ +  - ]:          2 :   else if (len.getKind() == Kind::MULT && len.getNumChildren() == 2
    4128 [ +  + ][ +  - ]:         30 :            && len[0].isConst())
         [ +  + ][ +  + ]
                 [ -  - ]
    4129                 :            :   {
    4130                 :            :     // c * x -> norm(x) repeated c times
    4131                 :          2 :     Rational ratReps = len[0].getConst<Rational>();
    4132 [ -  + ][ -  + ]:          2 :     Assert(ratReps.getDenominator() == 1);
                 [ -  - ]
    4133                 :          2 :     Integer intReps = ratReps.getNumerator();
    4134                 :            : 
    4135                 :          4 :     Node nRep = canonicalStrForSymbolicLength(len[1], stype);
    4136         [ -  + ]:          2 :     if (nRep.isNull())
    4137                 :            :     {
    4138                 :          0 :       return Node::null();
    4139                 :            :     }
    4140                 :          2 :     std::vector<Node> nRepChildren;
    4141                 :          2 :     utils::getConcat(nRep, nRepChildren);
    4142                 :          2 :     NodeBuilder concatBuilder(nodeManager(), Kind::STRING_CONCAT);
    4143         [ +  + ]:          6 :     for (size_t i = 0, reps = intReps.getUnsignedInt(); i < reps; i++)
    4144                 :            :     {
    4145                 :          4 :       concatBuilder.append(nRepChildren);
    4146                 :            :     }
    4147                 :          2 :     res = concatBuilder.constructNode();
    4148 [ +  - ][ +  - ]:          2 :   }
                 [ +  - ]
    4149         [ +  - ]:         26 :   else if (len.getKind() == Kind::STRING_LENGTH)
    4150                 :            :   {
    4151                 :            :     // len(x) -> x
    4152                 :         26 :     res = len[0];
    4153                 :            :   }
    4154                 :        268 :   return res;
    4155                 :        268 : }
    4156                 :            : 
    4157                 :       3693 : Node SequencesRewriter::rewriteSeqUnit(Node node)
    4158                 :            : {
    4159 [ -  + ][ -  + ]:       3693 :   Assert(node.getKind() == Kind::SEQ_UNIT);
                 [ -  - ]
    4160                 :       3693 :   NodeManager* nm = nodeManager();
    4161         [ +  + ]:       3693 :   if (node[0].isConst())
    4162                 :            :   {
    4163                 :       1145 :     std::vector<Node> seq;
    4164                 :       1145 :     seq.push_back(node[0]);
    4165                 :            :     // important to take the type according to the operator here, not the
    4166                 :            :     // type of the argument
    4167                 :       1145 :     TypeNode stype = node.getType().getSequenceElementType();
    4168                 :       1145 :     Node ret = nm->mkConst(Sequence(stype, seq));
    4169                 :       1145 :     return returnRewrite(node, ret, Rewrite::SEQ_UNIT_EVAL);
    4170                 :       1145 :   }
    4171                 :       2548 :   return node;
    4172                 :            : }
    4173                 :            : 
    4174                 :     463071 : Node SequencesRewriter::returnRewrite(Node node, Node ret, Rewrite r)
    4175                 :            : {
    4176         [ +  - ]:     926142 :   Trace("strings-rewrite") << "Rewrite " << node << " to " << ret << " by " << r
    4177                 :     463071 :                            << "." << std::endl;
    4178         [ +  + ]:     463071 :   if (d_statistics != nullptr)
    4179                 :            :   {
    4180                 :     458544 :     (*d_statistics) << r;
    4181                 :            :   }
    4182                 :     463071 :   return ret;
    4183                 :            : }
    4184                 :            : 
    4185                 :            : }  // namespace strings
    4186                 :            : }  // namespace theory
    4187                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14