LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/strings - extf_solver.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 419 481 87.1 %
Date: 2026-03-04 11:41:08 Functions: 17 22 77.3 %
Branches: 373 534 69.9 %

           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 solver for extended functions of theory of strings.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "theory/strings/extf_solver.h"
      14                 :            : 
      15                 :            : #include "options/strings_options.h"
      16                 :            : #include "theory/strings/array_solver.h"
      17                 :            : #include "theory/strings/sequences_rewriter.h"
      18                 :            : #include "theory/strings/theory_strings_preprocess.h"
      19                 :            : #include "theory/strings/theory_strings_utils.h"
      20                 :            : #include "util/statistics_registry.h"
      21                 :            : 
      22                 :            : using namespace std;
      23                 :            : using namespace cvc5::context;
      24                 :            : using namespace cvc5::internal::kind;
      25                 :            : 
      26                 :            : namespace cvc5::internal {
      27                 :            : namespace theory {
      28                 :            : namespace strings {
      29                 :            : 
      30                 :      49981 : ExtfSolver::ExtfSolver(Env& env,
      31                 :            :                        SolverState& s,
      32                 :            :                        InferenceManager& im,
      33                 :            :                        TermRegistry& tr,
      34                 :            :                        StringsRewriter& rewriter,
      35                 :            :                        BaseSolver& bs,
      36                 :            :                        CoreSolver& cs,
      37                 :            :                        ExtTheory& et,
      38                 :      49981 :                        SequencesStatistics& statistics)
      39                 :            :     : EnvObj(env),
      40                 :      49981 :       d_state(s),
      41                 :      49981 :       d_im(im),
      42                 :      49981 :       d_termReg(tr),
      43                 :      49981 :       d_rewriter(rewriter),
      44                 :      49981 :       d_bsolver(bs),
      45                 :      49981 :       d_csolver(cs),
      46                 :      49981 :       d_extt(et),
      47                 :      49981 :       d_statistics(statistics),
      48                 :      49981 :       d_preproc(env, d_termReg.getSkolemCache(), &statistics.d_reductions),
      49                 :      49981 :       d_hasExtf(context(), false),
      50                 :      49981 :       d_extfInferCache(context()),
      51                 :     149943 :       d_reduced(userContext())
      52                 :            : {
      53                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_SUBSTR);
      54                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_UPDATE);
      55                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_INDEXOF);
      56                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_INDEXOF_RE);
      57                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_ITOS);
      58                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_STOI);
      59                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_REPLACE);
      60                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_REPLACE_ALL);
      61                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_REPLACE_RE);
      62                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_REPLACE_RE_ALL);
      63                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_CONTAINS);
      64                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_IN_REGEXP);
      65                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_LEQ);
      66                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_TO_CODE);
      67                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_TO_LOWER);
      68                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_TO_UPPER);
      69                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_REV);
      70                 :      49981 :   d_extt.addFunctionKind(Kind::STRING_UNIT);
      71                 :      49981 :   d_extt.addFunctionKind(Kind::SEQ_UNIT);
      72                 :      49981 :   d_extt.addFunctionKind(Kind::SEQ_NTH);
      73                 :            : 
      74                 :      49981 :   d_true = nodeManager()->mkConst(true);
      75                 :      49981 :   d_false = nodeManager()->mkConst(false);
      76                 :      49981 : }
      77                 :            : 
      78                 :      49636 : ExtfSolver::~ExtfSolver() {}
      79                 :            : 
      80                 :     446582 : bool ExtfSolver::shouldDoReduction(int effort, Node n, int pol)
      81                 :            : {
      82         [ +  - ]:     893164 :   Trace("strings-extf-debug") << "shouldDoReduction " << n << ", pol " << pol
      83                 :     446582 :                               << ", effort " << effort << std::endl;
      84         [ +  + ]:     446582 :   if (!isActiveInModel(n))
      85                 :            :   {
      86                 :            :     // n is not active in the model, no need to reduce
      87         [ +  - ]:       2197 :     Trace("strings-extf-debug") << "...skip due to model active" << std::endl;
      88                 :       2197 :     return false;
      89                 :            :   }
      90                 :            :   // check with negation if requested (only applied to Boolean terms)
      91                 :     444385 :   Assert(n.getType().isBoolean() || pol != -1);
      92         [ +  + ]:     444385 :   Node nn = pol == -1 ? n.notNode() : n;
      93         [ +  + ]:     444385 :   if (d_reduced.find(nn) != d_reduced.end())
      94                 :            :   {
      95                 :            :     // already sent a reduction lemma
      96         [ +  - ]:     260908 :     Trace("strings-extf-debug") << "...skip due to reduced" << std::endl;
      97                 :     260908 :     return false;
      98                 :            :   }
      99                 :     183477 :   Kind k = n.getKind();
     100                 :            :   // determine if it is the right effort
     101 [ +  + ][ +  + ]:     183477 :   if (k == Kind::STRING_SUBSTR || (k == Kind::STRING_CONTAINS && pol == 1))
                 [ +  + ]
     102                 :            :   {
     103                 :            :     // we reduce these semi-eagerly, at effort 1
     104                 :       3157 :     return (effort == 1);
     105                 :            :   }
     106 [ +  + ][ +  - ]:     180320 :   else if (k == Kind::STRING_CONTAINS && pol == -1)
     107                 :            :   {
     108                 :            :     // negative contains reduces at level 2, or 3 if guessing model
     109         [ +  - ]:      27047 :     int reffort = options().strings.stringModelBasedReduction ? 3 : 2;
     110                 :      27047 :     return (effort == reffort);
     111                 :            :   }
     112         [ +  - ]:     149098 :   else if (k == Kind::SEQ_UNIT || k == Kind::STRING_UNIT
     113 [ +  + ][ +  + ]:     149098 :            || k == Kind::STRING_IN_REGEXP || k == Kind::STRING_TO_CODE
     114 [ +  + ][ +  + ]:     302371 :            || (n.getType().isBoolean() && pol == 0))
         [ -  + ][ +  + ]
         [ +  + ][ -  - ]
     115                 :            :   {
     116                 :            :     // never necessary to reduce seq.unit. str.to_code or str.in_re here.
     117                 :            :     // also, we do not reduce str.contains that are preregistered but not
     118                 :            :     // asserted (pol=0).
     119                 :     119587 :     return false;
     120                 :            :   }
     121         [ +  + ]:      33686 :   else if (options().strings.seqArray != options::SeqArrayMode::NONE)
     122                 :            :   {
     123         [ +  + ]:       7730 :     if (k == Kind::SEQ_NTH)
     124                 :            :     {
     125                 :            :       // don't need to reduce seq.nth when sequence update solver is used
     126                 :       6520 :       return false;
     127                 :            :     }
     128         [ -  + ]:        194 :     else if ((k == Kind::STRING_UPDATE || k == Kind::STRING_SUBSTR)
     129 [ +  + ][ +  + ]:       1404 :              && d_termReg.isHandledUpdateOrSubstr(n))
         [ +  + ][ +  + ]
                 [ -  - ]
     130                 :            :     {
     131                 :            :       // don't need to reduce certain seq.update
     132                 :            :       // don't need to reduce certain seq.extract with length 1
     133                 :        869 :       return false;
     134                 :            :     }
     135                 :            :   }
     136                 :            :   // all other operators reduce at level 2
     137                 :      26297 :   return (effort == 2);
     138                 :     444385 : }
     139                 :            : 
     140                 :       4973 : void ExtfSolver::doReduction(Node n, int pol)
     141                 :            : {
     142         [ +  - ]:       9946 :   Trace("strings-extf-debug")
     143                 :       4973 :       << "doReduction " << n << ", pol " << pol << std::endl;
     144                 :            :   // polarity : 1 true, -1 false, 0 neither
     145                 :       4973 :   Kind k = n.getKind();
     146 [ +  + ][ +  + ]:       4973 :   if (k == Kind::STRING_CONTAINS && pol == -1)
     147                 :            :   {
     148                 :         17 :     Node x = n[0];
     149                 :         17 :     Node s = n[1];
     150                 :         17 :     std::vector<Node> lexp;
     151                 :         17 :     Node lenx = d_state.getLength(x, lexp);
     152                 :         17 :     Node lens = d_state.getLength(s, lexp);
     153                 :            :     // we use an optimized reduction for negative string contains if the
     154                 :            :     // lengths are equal
     155         [ -  + ]:         17 :     if (d_state.areEqual(lenx, lens))
     156                 :            :     {
     157         [ -  - ]:          0 :       Trace("strings-extf-debug")
     158                 :          0 :           << "  resolve extf : " << n << " based on equal lengths disequality."
     159                 :          0 :           << std::endl;
     160                 :            :       // We can reduce negative contains to a disequality when lengths are
     161                 :            :       // equal. In other words, len( x ) = len( s ) implies
     162                 :            :       //   ~contains( x, s ) reduces to x != s.
     163                 :            :       // len( x ) = len( s ) ^ ~contains( x, s ) => x != s
     164                 :          0 :       lexp.push_back(lenx.eqNode(lens));
     165                 :          0 :       lexp.push_back(n.negate());
     166                 :          0 :       Node xneqs = x.eqNode(s).negate();
     167                 :          0 :       d_im.sendInference(
     168                 :            :           lexp, xneqs, InferenceId::STRINGS_CTN_NEG_EQUAL, false, true);
     169                 :            :       // this depends on the current assertions, so this
     170                 :            :       // inference is context-dependent
     171                 :          0 :       d_extt.markInactive(n, ExtReducedId::STRINGS_NEG_CTN_DEQ, true);
     172                 :          0 :       return;
     173                 :          0 :     }
     174 [ +  - ][ +  - ]:         17 :   }
         [ +  - ][ +  - ]
                 [ +  - ]
     175         [ +  + ]:       4973 :   Node nn = pol == -1 ? n.notNode() : n;
     176         [ +  - ]:       9946 :   Trace("strings-process-debug")
     177                 :       4973 :       << "Process reduction for " << n << ", pol = " << pol << std::endl;
     178 [ +  + ][ +  + ]:       4973 :   if (k == Kind::STRING_CONTAINS && pol == 1)
     179                 :            :   {
     180                 :        475 :     Node x = n[0];
     181                 :        475 :     Node s = n[1];
     182                 :            :     // positive contains reduces to a equality
     183                 :        475 :     SkolemCache* skc = d_termReg.getSkolemCache();
     184                 :        475 :     Node eq = d_termReg.eagerReduce(n, skc, d_termReg.getAlphabetCardinality());
     185 [ -  + ][ -  + ]:        475 :     Assert(!eq.isNull());
                 [ -  - ]
     186                 :        475 :     Assert(eq.getKind() == Kind::ITE && eq[0] == n);
     187                 :        475 :     eq = eq[1];
     188                 :        475 :     std::vector<Node> expn;
     189                 :        475 :     expn.push_back(n);
     190                 :        475 :     d_im.sendInference(expn, expn, eq, InferenceId::STRINGS_CTN_POS, false, true);
     191         [ +  - ]:        950 :     Trace("strings-extf-debug")
     192                 :          0 :         << "  resolve extf : " << n << " based on positive contain reduction."
     193                 :        475 :         << std::endl;
     194         [ +  - ]:        950 :     Trace("strings-red-lemma") << "Reduction (positive contains) lemma : " << n
     195                 :        475 :                                << " => " << eq << std::endl;
     196                 :            :     // reduced positively
     197 [ -  + ][ -  + ]:        475 :     Assert(nn == n);
                 [ -  - ]
     198                 :        475 :     d_reduced.insert(nn);
     199                 :        475 :   }
     200                 :            :   else
     201                 :            :   {
     202                 :       4498 :     NodeManager* nm = nodeManager();
     203 [ +  + ][ +  + ]:       4498 :     Assert(k == Kind::STRING_SUBSTR || k == Kind::STRING_UPDATE
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  - ]
         [ -  + ][ -  + ]
                 [ -  - ]
     204                 :            :            || k == Kind::STRING_CONTAINS || k == Kind::STRING_INDEXOF
     205                 :            :            || k == Kind::STRING_INDEXOF_RE || k == Kind::STRING_ITOS
     206                 :            :            || k == Kind::STRING_STOI || k == Kind::STRING_REPLACE
     207                 :            :            || k == Kind::STRING_REPLACE_ALL || k == Kind::SEQ_NTH
     208                 :            :            || k == Kind::STRING_REPLACE_RE || k == Kind::STRING_REPLACE_RE_ALL
     209                 :            :            || k == Kind::STRING_LEQ || k == Kind::STRING_TO_LOWER
     210                 :            :            || k == Kind::STRING_TO_UPPER || k == Kind::STRING_REV)
     211                 :          0 :         << "Unknown reduction: " << k;
     212                 :       4498 :     std::vector<Node> new_nodes;
     213                 :       4498 :     Node res = d_preproc.simplify(n, new_nodes);
     214 [ -  + ][ -  + ]:       4498 :     Assert(res != n);
                 [ -  - ]
     215                 :       4498 :     new_nodes.push_back(n.eqNode(res));
     216                 :            :     Node nnlem =
     217         [ -  + ]:       4498 :         new_nodes.size() == 1 ? new_nodes[0] : nm->mkNode(Kind::AND, new_nodes);
     218                 :            :     // in rare case where it rewrites to true, just record it is reduced
     219         [ -  + ]:       4498 :     if (rewrite(nnlem) == d_true)
     220                 :            :     {
     221         [ -  - ]:          0 :       Trace("strings-extf-debug")
     222                 :          0 :           << "  resolve extf : " << n << " based on (trivial) reduction."
     223                 :          0 :           << std::endl;
     224                 :          0 :       d_reduced.insert(nn);
     225                 :            :     }
     226                 :            :     else
     227                 :            :     {
     228                 :       4498 :       InferInfo ii(InferenceId::STRINGS_REDUCTION);
     229                 :            :       // ensure that we are called to process the side effects
     230                 :       4498 :       ii.d_sim = this;
     231                 :       4498 :       ii.d_conc = nnlem;
     232                 :       4498 :       d_im.sendInference(ii, true);
     233         [ +  - ]:       8996 :       Trace("strings-extf-debug")
     234                 :       4498 :           << "  resolve extf : " << n << " based on reduction." << std::endl;
     235                 :       4498 :       d_reductionWaitingMap[nnlem] = nn;
     236                 :       4498 :     }
     237                 :       4498 :   }
     238                 :       4973 : }
     239                 :            : 
     240                 :      66296 : void ExtfSolver::checkExtfReductionsEager()
     241                 :            : {
     242                 :            :   // return value is ignored
     243                 :      66296 :   checkExtfReductionsInternal(1);
     244                 :      66296 : }
     245                 :            : 
     246                 :      37816 : void ExtfSolver::checkExtfReductions(Theory::Effort e)
     247                 :            : {
     248         [ +  + ]:      37816 :   int effort = e == Theory::EFFORT_LAST_CALL ? 3 : 2;
     249                 :            :   // return value is ignored
     250                 :      37816 :   checkExtfReductionsInternal(effort);
     251                 :      37816 : }
     252                 :            : 
     253                 :     104112 : bool ExtfSolver::checkExtfReductionsInternal(int effort)
     254                 :            : {
     255                 :            :   // Notice we don't make a standard call to ExtTheory::doReductions here,
     256                 :            :   // since certain optimizations like context-dependent reductions and
     257                 :            :   // stratifying effort levels are done in doReduction below.
     258                 :            :   // We only have to reduce extended functions that are both relevant and
     259                 :            :   // active (see getRelevantActive).
     260                 :     104112 :   std::vector<Node> extf = getRelevantActive();
     261         [ +  - ]:     208224 :   Trace("strings-process") << "  checking " << extf.size() << " active extf"
     262                 :     104112 :                            << std::endl;
     263         [ +  + ]:     545721 :   for (const Node& n : extf)
     264                 :            :   {
     265 [ -  + ][ -  + ]:     446582 :     Assert(!d_state.isInConflict());
                 [ -  - ]
     266         [ +  - ]:     893164 :     Trace("strings-extf-debug")
     267                 :          0 :         << "  check " << n
     268                 :     446582 :         << ", active in model=" << d_extfInfoTmp[n].d_modelActive << std::endl;
     269                 :            :     // polarity, 1: positive, -1: negative, 0: neither
     270                 :     446582 :     int pol = 0;
     271         [ +  + ]:     446582 :     if (n.getType().isBoolean())
     272                 :            :     {
     273                 :     138910 :       Node rep = d_state.getRepresentative(n);
     274         [ +  - ]:      69455 :       if (rep.isConst())
     275                 :            :       {
     276         [ +  + ]:      69455 :         pol = rep.getConst<bool>() ? 1 : -1;
     277                 :            :       }
     278                 :      69455 :     }
     279         [ +  + ]:     446582 :     if (shouldDoReduction(effort, n, pol))
     280                 :            :     {
     281                 :       4973 :       doReduction(n, pol);
     282                 :            :       // we do not mark as inactive, since we may want to evaluate
     283         [ +  - ]:       4973 :       if (d_im.hasProcessed())
     284                 :            :       {
     285                 :       4973 :         return true;
     286                 :            :       }
     287                 :            :     }
     288                 :            :   }
     289                 :      99139 :   return false;
     290                 :     104112 : }
     291                 :            : 
     292                 :     128413 : void ExtfSolver::checkExtfEval(int effort)
     293                 :            : {
     294         [ +  - ]:     256826 :   Trace("strings-extf-list")
     295                 :     128413 :       << "Active extended functions, effort=" << effort << " : " << std::endl;
     296                 :     128413 :   d_extfInfoTmp.clear();
     297                 :     128413 :   d_extfToOrig.clear();
     298                 :     128413 :   NodeManager* nm = nodeManager();
     299                 :     128413 :   bool has_nreduce = false;
     300                 :     128413 :   std::vector<Node> terms = d_extt.getActive();
     301                 :            :   // the set of terms we have done extf inferences for
     302                 :     128413 :   std::unordered_set<Node> inferProcessed;
     303         [ +  + ]:     988827 :   for (const Node& n : terms)
     304                 :            :   {
     305                 :            :     // Setup information about n, including if it is equal to a constant.
     306                 :     861182 :     ExtfInfoTmp& einfo = d_extfInfoTmp[n];
     307 [ -  + ][ -  + ]:     861182 :     Assert(einfo.d_exp.empty());
                 [ -  - ]
     308                 :    1722364 :     Node r = d_state.getRepresentative(n);
     309                 :     861182 :     einfo.d_const = d_bsolver.getConstantEqc(r);
     310                 :            :     // Get the current values of the children of n.
     311                 :            :     // Notice that we look up the value of the direct children of n, and not
     312                 :            :     // their free variables. In other words, given a term:
     313                 :            :     //   t = (str.replace "B" (str.replace x "A" "B") "C")
     314                 :            :     // we may build the explanation that:
     315                 :            :     //   ((str.replace x "A" "B") = "B") => t = (str.replace "B" "B" "C")
     316                 :            :     // instead of basing this on the free variable x:
     317                 :            :     //   (x = "A") => t = (str.replace "B" (str.replace "A" "A" "B") "C")
     318                 :            :     // Although both allow us to infer t = "C", it is important to use the
     319                 :            :     // first kind of inference since it ensures that its subterms have the
     320                 :            :     // expected values. Otherwise, we may in rare cases fail to realize that
     321                 :            :     // the subterm (str.replace x "A" "B") does not currently have the correct
     322                 :            :     // value, say in this example that (str.replace x "A" "B") != "B".
     323                 :     861182 :     std::vector<Node> exp;
     324                 :     861182 :     std::vector<Node> schildren;
     325                 :            :     // seq.unit is parameterized
     326         [ -  + ]:     861182 :     if (n.getMetaKind() == kind::metakind::PARAMETERIZED)
     327                 :            :     {
     328                 :          0 :       schildren.push_back(n.getOperator());
     329                 :            :     }
     330                 :     861182 :     bool schanged = false;
     331         [ +  + ]:    2829875 :     for (const Node& nc : n)
     332                 :            :     {
     333                 :    1968693 :       Node sc = getCurrentSubstitutionFor(effort, nc, exp);
     334                 :    1968693 :       schildren.push_back(sc);
     335 [ +  + ][ +  + ]:    1968693 :       schanged = schanged || sc != nc;
     336                 :    1968693 :     }
     337                 :            :     // If there is information involving the children, attempt to do an
     338                 :            :     // inference and/or mark n as reduced.
     339                 :     861182 :     bool reduced = false;
     340                 :     861182 :     Node to_reduce = n;
     341         [ +  + ]:     861182 :     if (schanged)
     342                 :            :     {
     343                 :     332737 :       Node sn = nm->mkNode(n.getKind(), schildren);
     344         [ +  - ]:     665474 :       Trace("strings-extf-debug")
     345                 :          0 :           << "Check extf " << n << " == " << sn
     346                 :          0 :           << ", constant = " << einfo.d_const << ", effort=" << effort
     347                 :     332737 :           << ", exp " << exp << std::endl;
     348                 :     332737 :       einfo.d_initExp.insert(einfo.d_initExp.end(), exp.begin(), exp.end());
     349                 :     332737 :       einfo.d_exp.insert(einfo.d_exp.end(), exp.begin(), exp.end());
     350                 :            :       // inference is rewriting the substituted node
     351                 :     332737 :       Node nrc = rewrite(sn);
     352                 :            :       // if rewrites to a constant, then do the inference and mark as reduced
     353         [ +  + ]:     332737 :       if (nrc.isConst())
     354                 :            :       {
     355                 :            :         // at effort=3, our substitution is from the model, and we don't do
     356                 :            :         // inferences based on the model, instead we check whether the
     357                 :            :         // cosntraint is already equal to its expected value below.
     358         [ +  + ]:     149443 :         if (effort < 3)
     359                 :            :         {
     360                 :     142789 :           d_extt.markInactive(n, ExtReducedId::STRINGS_SR_CONST);
     361         [ +  - ]:     285578 :           Trace("strings-extf-debug")
     362                 :     142789 :               << "  resolvable by evaluation..." << std::endl;
     363                 :     142789 :           std::vector<Node> exps;
     364                 :            :           // The following optimization gets the "symbolic definition" of
     365                 :            :           // an extended term. The symbolic definition of a term t is a term
     366                 :            :           // t' where constants are replaced by their corresponding proxy
     367                 :            :           // variables.
     368                 :            :           // For example, if lsym is a proxy variable for "", then
     369                 :            :           // str.replace( lsym, lsym, lsym ) is the symbolic definition for
     370                 :            :           // str.replace( "", "", "" ). It is generally better to use symbolic
     371                 :            :           // definitions when doing cd-rewriting for the purpose of minimizing
     372                 :            :           // clauses, e.g. we infer the unit equality:
     373                 :            :           //    str.replace( lsym, lsym, lsym ) == ""
     374                 :            :           // instead of making this inference multiple times:
     375                 :            :           //    x = "" => str.replace( x, x, x ) == ""
     376                 :            :           //    y = "" => str.replace( y, y, y ) == ""
     377         [ +  - ]:     285578 :           Trace("strings-extf-debug")
     378                 :     142789 :               << "  get symbolic definition..." << std::endl;
     379                 :     142789 :           Node nrs;
     380                 :            :           // only use symbolic definitions if option is set
     381         [ +  - ]:     142789 :           if (options().strings.stringInferSym)
     382                 :            :           {
     383                 :     142789 :             nrs = d_termReg.getSymbolicDefinition(sn, exps);
     384                 :            :           }
     385         [ +  + ]:     142789 :           if (!nrs.isNull())
     386                 :            :           {
     387         [ +  - ]:     206824 :             Trace("strings-extf-debug")
     388                 :     103412 :                 << "  rewrite " << nrs << "..." << std::endl;
     389                 :     103412 :             Node nrsr = rewrite(nrs);
     390                 :            :             // ensure the symbolic form is not rewritable
     391         [ +  + ]:     103412 :             if (nrsr != nrs)
     392                 :            :             {
     393                 :            :               // we cannot use the symbolic definition if it rewrites
     394         [ +  - ]:      12918 :               Trace("strings-extf-debug")
     395                 :       6459 :                   << "  symbolic definition is trivial..." << std::endl;
     396                 :       6459 :               nrs = Node::null();
     397                 :            :             }
     398                 :     103412 :           }
     399                 :            :           else
     400                 :            :           {
     401         [ +  - ]:      78754 :             Trace("strings-extf-debug")
     402                 :      39377 :                 << "  could not infer symbolic definition." << std::endl;
     403                 :            :           }
     404                 :     142789 :           Node conc;
     405         [ +  + ]:     142789 :           if (!nrs.isNull())
     406                 :            :           {
     407         [ +  - ]:     193906 :             Trace("strings-extf-debug")
     408                 :      96953 :                 << "  symbolic def : " << nrs << std::endl;
     409         [ +  + ]:      96953 :             if (!d_state.areEqual(nrs, nrc))
     410                 :            :             {
     411                 :            :               // infer symbolic unit
     412         [ +  + ]:       2851 :               if (n.getType().isBoolean())
     413                 :            :               {
     414         [ +  + ]:       1879 :                 conc = nrc == d_true ? nrs : nrs.negate();
     415                 :            :               }
     416                 :            :               else
     417                 :            :               {
     418                 :        972 :                 conc = nrs.eqNode(nrc);
     419                 :            :               }
     420                 :       2851 :               einfo.d_exp.clear();
     421                 :            :             }
     422                 :            :           }
     423                 :            :           else
     424                 :            :           {
     425         [ +  + ]:      45836 :             if (!d_state.areEqual(n, nrc))
     426                 :            :             {
     427         [ +  + ]:       5226 :               if (n.getType().isBoolean())
     428                 :            :               {
     429         [ +  + ]:       3122 :                 conc = nrc == d_true ? n : n.negate();
     430                 :            :               }
     431                 :            :               else
     432                 :            :               {
     433                 :       2104 :                 conc = n.eqNode(nrc);
     434                 :            :               }
     435                 :            :             }
     436                 :            :           }
     437         [ +  + ]:     142789 :           if (!conc.isNull())
     438                 :            :           {
     439         [ +  - ]:      16154 :             Trace("strings-extf")
     440                 :       8077 :                 << "  resolve extf : " << sn << " -> " << nrc << std::endl;
     441         [ +  + ]:       8077 :             InferenceId inf = effort == 0 ? InferenceId::STRINGS_EXTF : InferenceId::STRINGS_EXTF_N;
     442                 :       8077 :             d_im.sendInference(einfo.d_exp, conc, inf, false, true);
     443                 :       8077 :             d_statistics.d_cdSimplifications << n.getKind();
     444                 :            :           }
     445                 :     142789 :         }
     446                 :            :         else
     447                 :            :         {
     448                 :            :           // check if it is already equal, if so, mark as reduced. Otherwise, do
     449                 :            :           // nothing.
     450         [ +  + ]:       6654 :           if (d_state.areEqual(n, nrc))
     451                 :            :           {
     452         [ +  - ]:       4448 :             Trace("strings-extf")
     453                 :          0 :                 << "  resolved extf, since satisfied by model: " << n
     454                 :       2224 :                 << std::endl;
     455                 :       2224 :             einfo.d_modelActive = false;
     456                 :            :           }
     457                 :            :         }
     458                 :     149443 :         reduced = true;
     459                 :            :       }
     460         [ +  + ]:     183294 :       else if (effort < 3)
     461                 :            :       {
     462                 :            :         // if this was a predicate which changed after substitution + rewriting
     463                 :            :         // We only do this before models are constructed (effort<3)
     464 [ +  + ][ +  + ]:     182848 :         if (!einfo.d_const.isNull() && nrc.getType().isBoolean() && nrc != n)
         [ +  + ][ +  + ]
         [ +  + ][ -  - ]
     465                 :            :         {
     466                 :      27456 :           bool pol = einfo.d_const == d_true;
     467         [ +  + ]:      27456 :           Node nrcAssert = pol ? nrc : nrc.negate();
     468         [ +  + ]:      27456 :           Node nAssert = pol ? n : n.negate();
     469                 :      27456 :           einfo.d_exp.push_back(nAssert);
     470         [ +  - ]:      27456 :           Trace("strings-extf-debug") << "  decomposable..." << std::endl;
     471         [ +  - ]:      54912 :           Trace("strings-extf") << "  resolve extf : " << sn << " -> " << nrc
     472                 :      27456 :                                 << ", const = " << einfo.d_const << std::endl;
     473                 :            :           // We send inferences internal here, which may help show unsat.
     474                 :            :           // However, we do not make a determination whether n can be marked
     475                 :            :           // reduced since this argument may be circular: we may infer than n
     476                 :            :           // can be reduced to something else, but that thing may argue that it
     477                 :            :           // can be reduced to n, in theory.
     478                 :      27456 :           InferenceId infer =
     479         [ +  + ]:      27456 :               effort == 0 ? InferenceId::STRINGS_EXTF_D : InferenceId::STRINGS_EXTF_D_N;
     480                 :      27456 :           d_im.sendInternalInference(einfo.d_exp, nrcAssert, infer);
     481                 :      27456 :         }
     482                 :     182848 :         to_reduce = nrc;
     483                 :            :       }
     484                 :     332737 :     }
     485                 :            :     // We must use the original n here to avoid circular justifications for
     486                 :            :     // why extended functions are reduced. In particular, n should never be a
     487                 :            :     // duplicate of another term considered in the block of code for
     488                 :            :     // checkExtfInference below.
     489                 :            :     // if not reduced and not processed
     490         [ +  - ]:     711739 :     if (!reduced && !n.isNull()
     491 [ +  + ][ +  - ]:    1572921 :         && inferProcessed.find(n) == inferProcessed.end())
                 [ +  + ]
     492                 :            :     {
     493                 :     711739 :       inferProcessed.insert(n);
     494         [ +  + ]:     711739 :       if (effort == 1)
     495                 :            :       {
     496         [ +  - ]:     176408 :         Trace("strings-extf")
     497                 :      88204 :             << "  cannot rewrite extf : " << to_reduce << std::endl;
     498                 :            :       }
     499                 :            :       // we take to_reduce to be the (partially) reduced version of n, which
     500                 :            :       // is justified by the explanation in einfo. We only do this if we are
     501                 :            :       // not based on the model (effort<3).
     502         [ +  + ]:     711739 :       if (effort < 3)
     503                 :            :       {
     504                 :     711279 :         checkExtfInference(n, to_reduce, einfo);
     505                 :            :       }
     506         [ -  + ]:     711739 :       if (TraceIsOn("strings-extf-list"))
     507                 :            :       {
     508         [ -  - ]:          0 :         Trace("strings-extf-list") << "  * " << to_reduce;
     509         [ -  - ]:          0 :         if (!einfo.d_const.isNull())
     510                 :            :         {
     511         [ -  - ]:          0 :           Trace("strings-extf-list") << ", const = " << einfo.d_const;
     512                 :            :         }
     513         [ -  - ]:          0 :         if (n != to_reduce)
     514                 :            :         {
     515         [ -  - ]:          0 :           Trace("strings-extf-list") << ", from " << n;
     516                 :            :         }
     517         [ -  - ]:          0 :         Trace("strings-extf-list") << std::endl;
     518                 :            :       }
     519 [ +  + ][ +  - ]:     711739 :       if (d_extt.isActive(n) && einfo.d_modelActive)
         [ +  - ][ +  + ]
                 [ -  - ]
     520                 :            :       {
     521                 :     711606 :         has_nreduce = true;
     522                 :            :       }
     523                 :            :     }
     524         [ +  + ]:     861182 :     if (d_state.isInConflict())
     525                 :            :     {
     526         [ +  - ]:        768 :       Trace("strings-extf-debug") << "  conflict, return." << std::endl;
     527                 :        768 :       return;
     528                 :            :     }
     529 [ +  + ][ +  + ]:     863486 :   }
         [ +  + ][ +  + ]
     530                 :     127645 :   d_hasExtf = has_nreduce;
     531 [ +  + ][ +  + ]:     129181 : }
     532                 :            : 
     533                 :     711279 : void ExtfSolver::checkExtfInference(Node n, Node nr, ExtfInfoTmp& in)
     534                 :            : {
     535                 :            :   // see if any previous term rewrote to nr, if so, we can conclude that
     536                 :            :   // term is equal to n.
     537                 :     711279 :   std::map<Node, Node>::iterator ito = d_extfToOrig.find(nr);
     538         [ +  + ]:     711279 :   if (ito != d_extfToOrig.end())
     539                 :            :   {
     540                 :      18005 :     Node no = ito->second;
     541         [ +  + ]:      18005 :     if (!d_state.areEqual(n, no))
     542                 :            :     {
     543 [ -  + ][ -  + ]:        578 :       Assert(d_extfInfoTmp.find(no) != d_extfInfoTmp.end());
                 [ -  - ]
     544                 :        578 :       ExtfInfoTmp& eito = d_extfInfoTmp[no];
     545                 :        578 :       Node conc = n.eqNode(no);
     546         [ +  - ]:       1156 :       Trace("strings-extf-infer")
     547                 :        578 :           << "infer same rewrite: " << conc << std::endl;
     548                 :        578 :       std::vector<Node> exp;
     549                 :        578 :       exp.insert(exp.end(), in.d_initExp.begin(), in.d_initExp.end());
     550                 :        578 :       exp.insert(exp.end(), eito.d_initExp.begin(), eito.d_initExp.end());
     551         [ +  - ]:        578 :       Trace("strings-extf-infer") << "..explaination is " << exp << std::endl;
     552                 :        578 :       d_im.sendInference(exp, conc, InferenceId::STRINGS_EXTF_REW_SAME);
     553                 :        578 :     }
     554                 :      18005 :     return;
     555                 :      18005 :   }
     556                 :            :   // store that n rewrites to nr
     557                 :     693274 :   d_extfToOrig[nr] = n;
     558                 :            : 
     559         [ +  + ]:     693274 :   if (in.d_const.isNull())
     560                 :            :   {
     561                 :     482400 :     return;
     562                 :            :   }
     563                 :     210874 :   NodeManager* nm = nodeManager();
     564         [ +  - ]:     421748 :   Trace("strings-extf-infer")
     565                 :          0 :       << "checkExtfInference: " << n << " : " << nr << " == " << in.d_const
     566                 :     210874 :       << " with exp " << in.d_exp << std::endl;
     567                 :            : 
     568                 :            :   // add original to explanation
     569         [ +  + ]:     210874 :   if (n.getType().isBoolean())
     570                 :            :   {
     571                 :            :     // if Boolean, it's easy
     572         [ +  + ]:     108710 :     in.d_exp.push_back(in.d_const.getConst<bool>() ? n : n.negate());
     573                 :            :   }
     574                 :            :   else
     575                 :            :   {
     576                 :            :     // otherwise, must explain via base node
     577                 :     204328 :     Node r = d_state.getRepresentative(n);
     578                 :            :     // explain using the base solver
     579                 :     102164 :     d_bsolver.explainConstantEqc(n, r, in.d_exp);
     580                 :     102164 :   }
     581                 :            : 
     582                 :            :   // d_extfInferCache stores whether we have made the inferences associated
     583                 :            :   // with a node n,
     584                 :            :   // this may need to be generalized if multiple inferences apply
     585                 :            : 
     586         [ +  + ]:     210874 :   if (nr.getKind() == Kind::STRING_CONTAINS)
     587                 :            :   {
     588 [ -  + ][ -  + ]:      65242 :     Assert(in.d_const.isConst());
                 [ -  - ]
     589                 :      65242 :     bool pol = in.d_const.getConst<bool>();
     590 [ +  + ][ +  + ]:      90219 :     if ((pol && nr[1].getKind() == Kind::STRING_CONCAT)
                 [ -  - ]
     591 [ +  + ][ +  + ]:      90219 :         || (!pol && nr[0].getKind() == Kind::STRING_CONCAT))
         [ +  + ][ +  + ]
         [ +  + ][ -  - ]
     592                 :            :     {
     593                 :            :       // If str.contains( x, str.++( y1, ..., yn ) ),
     594                 :            :       //   we may infer str.contains( x, y1 ), ..., str.contains( x, yn )
     595                 :            :       // The following recognizes two situations related to the above reasoning:
     596                 :            :       // (1) If ~str.contains( x, yi ) holds for some i, we are in conflict,
     597                 :            :       // (2) If str.contains( x, yj ) already holds for some j, then the term
     598                 :            :       // str.contains( x, yj ) is irrelevant since it is satisfied by all models
     599                 :            :       // for str.contains( x, str.++( y1, ..., yn ) ).
     600                 :            : 
     601                 :            :       // Notice that the dual of the above reasoning also holds, i.e.
     602                 :            :       // If ~str.contains( str.++( x1, ..., xn ), y ),
     603                 :            :       //   we may infer ~str.contains( x1, y ), ..., ~str.contains( xn, y )
     604                 :            :       // This is also handled here.
     605         [ +  + ]:       5159 :       if (d_extfInferCache.find(nr) == d_extfInferCache.end())
     606                 :            :       {
     607                 :       3140 :         d_extfInferCache.insert(nr);
     608                 :            : 
     609         [ +  + ]:       3140 :         int index = pol ? 1 : 0;
     610                 :       3140 :         std::vector<Node> children;
     611                 :       3140 :         children.push_back(nr[0]);
     612                 :       3140 :         children.push_back(nr[1]);
     613         [ +  + ]:       9806 :         for (const Node& nrc : nr[index])
     614                 :            :         {
     615                 :       6687 :           children[index] = nrc;
     616                 :       6687 :           Node conc = nm->mkNode(Kind::STRING_CONTAINS, children);
     617         [ +  + ]:       6687 :           conc = rewrite(pol ? conc : conc.negate());
     618                 :            :           // check if it already (does not) hold
     619         [ +  + ]:       6687 :           if (d_state.hasTerm(conc))
     620                 :            :           {
     621         [ +  + ]:        536 :             if (d_state.areEqual(conc, d_false))
     622                 :            :             {
     623                 :            :               // we are in conflict
     624                 :         21 :               d_im.addToExplanation(conc, d_false, in.d_exp);
     625                 :         21 :               d_im.sendInference(
     626                 :         21 :                   in.d_exp, d_false, InferenceId::STRINGS_CTN_DECOMPOSE);
     627 [ -  + ][ -  + ]:         21 :               Assert(d_state.isInConflict());
                 [ -  - ]
     628                 :         21 :               return;
     629                 :            :             }
     630         [ +  + ]:        515 :             else if (d_extt.hasFunctionKind(conc.getKind()))
     631                 :            :             {
     632                 :            :               // can mark as reduced, since model for n implies model for conc
     633                 :        210 :               d_extt.markInactive(conc, ExtReducedId::STRINGS_CTN_DECOMPOSE);
     634                 :            :             }
     635                 :            :           }
     636 [ +  + ][ +  + ]:       9848 :         }
                 [ +  + ]
     637         [ +  + ]:       3140 :       }
     638                 :            :     }
     639                 :            :     else
     640                 :            :     {
     641                 :     180249 :       if (std::find(d_extfInfoTmp[nr[0]].d_ctn[pol].begin(),
     642                 :     120166 :                     d_extfInfoTmp[nr[0]].d_ctn[pol].end(),
     643                 :            :                     nr[1])
     644         [ +  - ]:     180249 :           == d_extfInfoTmp[nr[0]].d_ctn[pol].end())
     645                 :            :       {
     646 [ +  - ][ -  - ]:     120166 :         Trace("strings-extf-debug") << "  store contains info : " << nr[0]
     647 [ -  + ][ -  + ]:      60083 :                                     << " " << pol << " " << nr[1] << std::endl;
                 [ -  - ]
     648                 :            :         // Store s (does not) contains t, since nr = (~)contains( s, t ) holds.
     649                 :      60083 :         d_extfInfoTmp[nr[0]].d_ctn[pol].push_back(nr[1]);
     650                 :      60083 :         d_extfInfoTmp[nr[0]].d_ctnFrom[pol].push_back(n);
     651                 :            :         // Do transistive closure on contains, e.g.
     652                 :            :         // if contains( s, t ) and ~contains( s, r ), then ~contains( t, r ).
     653                 :            : 
     654                 :            :         // The following infers new (negative) contains based on the above
     655                 :            :         // reasoning, provided that ~contains( t, r ) does not
     656                 :            :         // already hold in the current context. We test this by checking that
     657                 :            :         // contains( t, r ) is not already asserted false in the current
     658                 :            :         // context. We also handle the case where contains( t, r ) is equivalent
     659                 :            :         // to t = r, in which case we check that t != r does not already hold
     660                 :            :         // in the current context.
     661                 :            : 
     662                 :            :         // Notice that form of the above inference is enough to find
     663                 :            :         // conflicts purely due to contains predicates. For example, if we
     664                 :            :         // have only positive occurrences of contains, then no conflicts due to
     665                 :            :         // contains predicates are possible and this schema does nothing. For
     666                 :            :         // example, note that contains( s, t ) and contains( t, r ) implies
     667                 :            :         // contains( s, r ), which we could but choose not to infer. Instead,
     668                 :            :         // we prefer being lazy: only if ~contains( s, r ) appears later do we
     669                 :            :         // infer ~contains( t, r ), which suffices to show a conflict.
     670                 :      60083 :         bool opol = !pol;
     671                 :      65106 :         for (unsigned i = 0, size = d_extfInfoTmp[nr[0]].d_ctn[opol].size();
     672         [ +  + ]:      65106 :              i < size;
     673                 :            :              i++)
     674                 :            :         {
     675                 :       5023 :           Node onr = d_extfInfoTmp[nr[0]].d_ctn[opol][i];
     676                 :            :           Node concOrig = nm->mkNode(
     677 [ +  + ][ +  + ]:      10046 :               Kind::STRING_CONTAINS, pol ? nr[1] : onr, pol ? onr : nr[1]);
     678                 :       5023 :           Node conc = rewrite(concOrig);
     679                 :            :           // For termination concerns, we only do the inference if the contains
     680                 :            :           // does not rewrite (and thus does not introduce new terms).
     681         [ +  + ]:       5023 :           if (conc == concOrig)
     682                 :            :           {
     683                 :        381 :             bool do_infer = false;
     684                 :        381 :             conc = conc.negate();
     685                 :        381 :             bool pol2 = conc.getKind() != Kind::NOT;
     686         [ -  + ]:        381 :             Node lit = pol2 ? conc : conc[0];
     687         [ -  + ]:        381 :             if (lit.getKind() == Kind::EQUAL)
     688                 :            :             {
     689                 :          0 :               do_infer = pol2 ? !d_state.areEqual(lit[0], lit[1])
     690                 :          0 :                               : !d_state.areDisequal(lit[0], lit[1]);
     691                 :            :             }
     692                 :            :             else
     693                 :            :             {
     694         [ -  + ]:        381 :               do_infer = !d_state.areEqual(lit, pol2 ? d_true : d_false);
     695                 :            :             }
     696         [ +  + ]:        381 :             if (do_infer)
     697                 :            :             {
     698                 :        173 :               std::vector<Node> exp_c;
     699                 :        173 :               exp_c.insert(exp_c.end(), in.d_exp.begin(), in.d_exp.end());
     700                 :        173 :               Node ofrom = d_extfInfoTmp[nr[0]].d_ctnFrom[opol][i];
     701 [ -  + ][ -  + ]:        173 :               Assert(d_extfInfoTmp.find(ofrom) != d_extfInfoTmp.end());
                 [ -  - ]
     702                 :        173 :               exp_c.insert(exp_c.end(),
     703                 :        173 :                            d_extfInfoTmp[ofrom].d_exp.begin(),
     704                 :        173 :                            d_extfInfoTmp[ofrom].d_exp.end());
     705                 :        173 :               d_im.sendInference(exp_c, conc, InferenceId::STRINGS_CTN_TRANS);
     706                 :        173 :             }
     707                 :        381 :           }
     708                 :       5023 :         }
     709                 :            :       }
     710                 :            :       else
     711                 :            :       {
     712                 :            :         // If we already know that s (does not) contain t, then n may be
     713                 :            :         // redundant. However, we do not mark n as reduced here, since strings
     714                 :            :         // reductions may require dependencies between extended functions.
     715                 :            :         // Marking reduced here could lead to incorrect models if an
     716                 :            :         // extended function is marked reduced based on an assignment to
     717                 :            :         // something that depends on n.
     718         [ -  - ]:          0 :         Trace("strings-extf-debug") << "  redundant." << std::endl;
     719                 :            :       }
     720                 :            :     }
     721                 :      65221 :     return;
     722                 :            :   }
     723                 :            : 
     724                 :            :   // If it's not a predicate, see if we can solve the equality n = c, where c
     725                 :            :   // is the constant that extended term n is equal to.
     726                 :     145632 :   Node inferEq = nr.eqNode(in.d_const);
     727                 :     145632 :   Node inferEqr = rewrite(inferEq);
     728                 :     145632 :   Node inferEqrr = inferEqr;
     729         [ +  + ]:     145632 :   if (inferEqr.getKind() == Kind::EQUAL)
     730                 :            :   {
     731                 :            :     // try to use the extended rewriter for equalities
     732                 :     102681 :     inferEqrr = d_rewriter.rewriteEqualityExt(inferEqr);
     733                 :            :   }
     734         [ +  + ]:     145632 :   if (inferEqrr != inferEqr)
     735                 :            :   {
     736                 :       9915 :     inferEqrr = rewrite(inferEqrr);
     737         [ +  - ]:      19830 :     Trace("strings-extf-infer")
     738                 :          0 :         << "checkExtfInference: " << inferEq << " ...reduces to " << inferEqrr
     739                 :       9915 :         << " with explanation " << in.d_exp << std::endl;
     740                 :       9915 :     d_im.sendInternalInference(in.d_exp, inferEqrr, InferenceId::STRINGS_EXTF_EQ_REW);
     741                 :            :   }
     742                 :     145632 : }
     743                 :            : 
     744                 :    1968693 : Node ExtfSolver::getCurrentSubstitutionFor(int effort,
     745                 :            :                                            Node n,
     746                 :            :                                            std::vector<Node>& exp)
     747                 :            : {
     748         [ +  + ]:    1968693 :   if (effort >= 3)
     749                 :            :   {
     750                 :            :     // model values
     751                 :      15696 :     Node mv = d_state.getModel()->getRepresentative(n);
     752         [ +  - ]:      15696 :     Trace("strings-subs") << "   model val : " << mv << std::endl;
     753                 :      15696 :     return mv;
     754                 :      15696 :   }
     755                 :    3905994 :   Node nr = d_state.getRepresentative(n);
     756                 :            :   // if the normal form is available, use it
     757 [ +  + ][ +  + ]:    1952997 :   if (effort >= 1 && n.getType().isStringLike())
         [ +  + ][ +  + ]
                 [ -  - ]
     758                 :            :   {
     759 [ -  + ][ -  + ]:     147638 :     Assert(effort < 3);
                 [ -  - ]
     760                 :            :     // Return self if the normal form has not been computed. This may happen
     761                 :            :     // for terms that are not relevant in the current context.
     762         [ +  + ]:     147638 :     if (!d_csolver.hasNormalForm(nr))
     763                 :            :     {
     764                 :          2 :       return n;
     765                 :            :     }
     766                 :     147636 :     NormalForm& nfnr = d_csolver.getNormalForm(nr);
     767                 :     147636 :     Node ns;
     768 [ +  + ][ +  + ]:     147636 :     if (n.getKind() == Kind::STRING_CONCAT && n != nfnr.d_base)
                 [ +  + ]
     769                 :            :     {
     770                 :            :       // if the normal base is a term (str.++ t1 t2), and we are a term
     771                 :            :       // (str.++ s1 s2), then we explain the normal form concatentation of
     772                 :            :       // s1 and s2, instead of explaining (= (str.++ s1 s2) (str.++ t1 t2)) and
     773                 :            :       // concatentating the normal form explanation of t1 and t2. This
     774                 :            :       // ensures the explanation when taking as a substitution does not have
     775                 :            :       // concatentation terms on the LHS of equalities, which can lead to
     776                 :            :       // cyclic proof dependencies.
     777                 :       3149 :       std::vector<Node> vec;
     778         [ +  + ]:       9619 :       for (const Node& nc : n)
     779                 :            :       {
     780                 :      12940 :         Node ncr = d_state.getRepresentative(nc);
     781 [ -  + ][ -  + ]:       6470 :         Assert(d_csolver.hasNormalForm(ncr));
                 [ -  - ]
     782                 :       6470 :         NormalForm& nfnrc = d_csolver.getNormalForm(ncr);
     783                 :       6470 :         Node nsc = d_csolver.getNormalString(nfnrc.d_base, exp);
     784                 :       6470 :         d_im.addToExplanation(nc, nfnrc.d_base, exp);
     785                 :       6470 :         vec.push_back(nsc);
     786                 :       6470 :       }
     787                 :       3149 :       TypeNode stype = n.getType();
     788                 :       3149 :       ns = d_termReg.mkNConcat(vec, stype);
     789                 :       3149 :     }
     790                 :            :     else
     791                 :            :     {
     792                 :     144487 :       ns = d_csolver.getNormalString(nfnr.d_base, exp);
     793         [ +  - ]:     288974 :       Trace("strings-subs") << "   normal eqc : " << ns << " " << nfnr.d_base
     794                 :     144487 :                             << " " << nr << std::endl;
     795         [ +  - ]:     144487 :       if (!nfnr.d_base.isNull())
     796                 :            :       {
     797                 :     144487 :         d_im.addToExplanation(n, nfnr.d_base, exp);
     798                 :            :       }
     799                 :            :     }
     800                 :     147636 :     return ns;
     801                 :     147636 :   }
     802                 :            :   // otherwise, we use the best content heuristic
     803                 :    3610718 :   Node c = d_bsolver.explainBestContentEqc(n, nr, exp);
     804         [ +  + ]:    1805359 :   if (!c.isNull())
     805                 :            :   {
     806                 :     953968 :     return c;
     807                 :            :   }
     808                 :     851391 :   return n;
     809                 :    1952997 : }
     810                 :            : 
     811                 :          0 : const std::map<Node, ExtfInfoTmp>& ExtfSolver::getInfo() const
     812                 :            : {
     813                 :          0 :   return d_extfInfoTmp;
     814                 :            : }
     815                 :      24766 : bool ExtfSolver::hasExtendedFunctions() const { return d_hasExtf.get(); }
     816                 :            : 
     817                 :      36340 : std::vector<Node> ExtfSolver::getActive(Kind k) const
     818                 :            : {
     819                 :      36340 :   return d_extt.getActive(k);
     820                 :            : }
     821                 :            : 
     822                 :     446722 : bool ExtfSolver::isActiveInModel(Node n) const
     823                 :            : {
     824                 :     446722 :   std::map<Node, ExtfInfoTmp>::const_iterator it = d_extfInfoTmp.find(n);
     825         [ -  + ]:     446722 :   if (it == d_extfInfoTmp.end())
     826                 :            :   {
     827                 :          0 :     DebugUnhandled() << "isActiveInModel: Expected extf info for " << n;
     828                 :            :     return true;
     829                 :            :   }
     830                 :     446722 :   return it->second.d_modelActive;
     831                 :            : }
     832                 :            : 
     833                 :     104589 : std::vector<Node> ExtfSolver::getRelevantActive() const
     834                 :            : {
     835                 :            :   // get the relevant term set
     836                 :     104589 :   std::vector<Node> extf = d_extt.getActive();
     837                 :     104589 :   const std::set<Node>& relevantTerms = d_termReg.getRelevantTermSet();
     838                 :            : 
     839                 :     104589 :   std::vector<Node> res;
     840         [ +  + ]:     576945 :   for (const Node& n : extf)
     841                 :            :   {
     842         [ +  + ]:     472356 :     if (relevantTerms.find(n) == relevantTerms.end())
     843                 :            :     {
     844                 :            :       // not relevant
     845                 :       9130 :       continue;
     846                 :            :     }
     847                 :     463226 :     res.push_back(n);
     848                 :            :   }
     849                 :     209178 :   return res;
     850                 :     104589 : }
     851                 :            : 
     852                 :          0 : bool StringsExtfCallback::getCurrentSubstitution(
     853                 :            :     int effort,
     854                 :            :     const std::vector<Node>& vars,
     855                 :            :     std::vector<Node>& subs,
     856                 :            :     std::map<Node, std::vector<Node> >& exp)
     857                 :            : {
     858         [ -  - ]:          0 :   Trace("strings-subs") << "getCurrentSubstitution, effort = " << effort
     859                 :          0 :                         << std::endl;
     860         [ -  - ]:          0 :   for (const Node& v : vars)
     861                 :            :   {
     862         [ -  - ]:          0 :     Trace("strings-subs") << "  get subs for " << v << "..." << std::endl;
     863                 :          0 :     Node s = d_esolver->getCurrentSubstitutionFor(effort, v, exp[v]);
     864                 :          0 :     subs.push_back(s);
     865                 :          0 :   }
     866                 :          0 :   return true;
     867                 :            : }
     868                 :            : 
     869                 :          0 : void ExtfSolver::processFact(InferInfo& ii, ProofGenerator*& pg)
     870                 :            : {
     871                 :            :   // process it with the inference manager
     872                 :          0 :   d_im.processFact(ii, pg);
     873                 :          0 : }
     874                 :            : 
     875                 :       4498 : TrustNode ExtfSolver::processLemma(InferInfo& ii, LemmaProperty& p)
     876                 :            : {
     877                 :            :   // if this was the reduction lemma for a term, mark it reduced now
     878                 :       4498 :   std::map<Node, Node>::iterator it = d_reductionWaitingMap.find(ii.d_conc);
     879         [ +  - ]:       4498 :   if (it != d_reductionWaitingMap.end())
     880                 :            :   {
     881                 :       4498 :     d_reduced.insert(it->second);
     882                 :       4498 :     d_reductionWaitingMap.erase(it);
     883                 :            :   }
     884                 :            :   // now process it with the inference manager
     885                 :       8996 :   return d_im.processLemma(ii, p);
     886                 :            : }
     887                 :            : 
     888                 :          0 : std::string ExtfSolver::debugPrintModel()
     889                 :            : {
     890                 :          0 :   std::stringstream ss;
     891                 :          0 :   std::vector<Node> extf;
     892                 :          0 :   d_extt.getTerms(extf);
     893                 :            :   // each extended function should have at least one annotation below
     894         [ -  - ]:          0 :   for (const Node& n : extf)
     895                 :            :   {
     896                 :          0 :     ss << "- " << n;
     897                 :            :     ExtReducedId id;
     898         [ -  - ]:          0 :     if (!d_extt.isActive(n, id))
     899                 :            :     {
     900                 :          0 :       ss << " :extt-inactive " << id;
     901                 :            :     }
     902         [ -  - ]:          0 :     if (!d_extfInfoTmp[n].d_modelActive)
     903                 :            :     {
     904                 :          0 :       ss << " :model-inactive";
     905                 :            :     }
     906         [ -  - ]:          0 :     if (d_reduced.find(n) != d_reduced.end())
     907                 :            :     {
     908                 :          0 :       ss << " :reduced";
     909                 :            :     }
     910                 :          0 :     ss << std::endl;
     911                 :            :   }
     912                 :          0 :   return ss.str();
     913                 :          0 : }
     914                 :            : 
     915                 :       3706 : bool ExtfSolver::isReduced(const Node& n) const
     916                 :            : {
     917                 :       3706 :   return d_reduced.find(n) != d_reduced.end();
     918                 :            : }
     919                 :            : 
     920                 :        880 : void ExtfSolver::markReduced(const Node& n) { d_reduced.insert(n); }
     921                 :            : 
     922                 :            : }  // namespace strings
     923                 :            : }  // namespace theory
     924                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14