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: 436 498 87.6 %
Date: 2026-07-31 10:35:08 Functions: 17 22 77.3 %
Branches: 389 554 70.2 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  * Implementation of 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                 :      28664 : 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                 :      28664 :                        SequencesStatistics& statistics)
      39                 :            :     : EnvObj(env),
      40                 :      28664 :       d_state(s),
      41                 :      28664 :       d_im(im),
      42                 :      28664 :       d_termReg(tr),
      43                 :      28664 :       d_rewriter(rewriter),
      44                 :      28664 :       d_bsolver(bs),
      45                 :      28664 :       d_csolver(cs),
      46                 :      28664 :       d_extt(et),
      47                 :      28664 :       d_statistics(statistics),
      48                 :      28664 :       d_preproc(env, d_termReg.getSkolemCache(), &statistics.d_reductions),
      49                 :      28664 :       d_hasExtf(context(), false),
      50                 :      28664 :       d_extfInferCache(context()),
      51                 :      85992 :       d_reduced(userContext())
      52                 :            : {
      53                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_SUBSTR);
      54                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_UPDATE);
      55                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_INDEXOF);
      56                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_INDEXOF_RE);
      57                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_ITOS);
      58                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_STOI);
      59                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_REPLACE);
      60                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_REPLACE_ALL);
      61                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_REPLACE_RE);
      62                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_REPLACE_RE_ALL);
      63                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_CONTAINS);
      64                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_IN_REGEXP);
      65                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_LEQ);
      66                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_TO_CODE);
      67                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_TO_LOWER);
      68                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_TO_UPPER);
      69                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_REV);
      70                 :      28664 :   d_extt.addFunctionKind(Kind::STRING_UNIT);
      71                 :      28664 :   d_extt.addFunctionKind(Kind::SEQ_UNIT);
      72                 :      28664 :   d_extt.addFunctionKind(Kind::SEQ_NTH);
      73                 :            : 
      74                 :      28664 :   d_true = nodeManager()->mkConst(true);
      75                 :      28664 :   d_false = nodeManager()->mkConst(false);
      76                 :      28664 : }
      77                 :            : 
      78                 :      28651 : ExtfSolver::~ExtfSolver() {}
      79                 :            : 
      80                 :     451475 : bool ExtfSolver::shouldDoReduction(int effort, Node n, int pol)
      81                 :            : {
      82         [ +  - ]:     902950 :   Trace("strings-extf-debug") << "shouldDoReduction " << n << ", pol " << pol
      83                 :     451475 :                               << ", effort " << effort << std::endl;
      84         [ +  + ]:     451475 :   if (!isActiveInModel(n))
      85                 :            :   {
      86                 :            :     // n is not active in the model, no need to reduce
      87         [ +  - ]:       2325 :     Trace("strings-extf-debug") << "...skip due to model active" << std::endl;
      88                 :       2325 :     return false;
      89                 :            :   }
      90                 :            :   // check with negation if requested (only applied to Boolean terms)
      91                 :     449150 :   Assert(n.getType().isBoolean() || pol != -1);
      92         [ +  + ]:     449150 :   Node nn = pol == -1 ? n.notNode() : n;
      93         [ +  + ]:     449150 :   if (d_reduced.find(nn) != d_reduced.end())
      94                 :            :   {
      95                 :            :     // already sent a reduction lemma
      96         [ +  - ]:     264247 :     Trace("strings-extf-debug") << "...skip due to reduced" << std::endl;
      97                 :     264247 :     return false;
      98                 :            :   }
      99                 :     184903 :   Kind k = n.getKind();
     100                 :            :   // determine if it is the right effort
     101 [ +  + ][ +  + ]:     184903 :   if (k == Kind::STRING_SUBSTR || (k == Kind::STRING_CONTAINS && pol == 1))
                 [ +  + ]
     102                 :            :   {
     103                 :            :     // we reduce these semi-eagerly, at effort 1
     104                 :       3261 :     return (effort == 1);
     105                 :            :   }
     106 [ +  + ][ +  - ]:     181642 :   else if (k == Kind::STRING_CONTAINS && pol == -1)
     107                 :            :   {
     108                 :            :     // negative contains reduces at level 2, or 3 if guessing model
     109         [ +  - ]:      27596 :     int reffort = options().strings.stringModelBasedReduction ? 3 : 2;
     110                 :      27596 :     return (effort == reffort);
     111                 :            :   }
     112         [ +  - ]:     149801 :   else if (k == Kind::SEQ_UNIT || k == Kind::STRING_UNIT
     113 [ +  + ][ +  + ]:     149801 :            || k == Kind::STRING_IN_REGEXP || k == Kind::STRING_TO_CODE
     114 [ +  + ][ +  + ]:     303847 :            || (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                 :     119211 :     return false;
     120                 :            :   }
     121         [ +  + ]:      34835 :   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                 :      27446 :   return (effort == 2);
     138                 :     449150 : }
     139                 :            : 
     140                 :       5193 : void ExtfSolver::doReduction(Node n, int pol)
     141                 :            : {
     142         [ +  - ]:      10386 :   Trace("strings-extf-debug")
     143                 :       5193 :       << "doReduction " << n << ", pol " << pol << std::endl;
     144                 :            :   // polarity : 1 true, -1 false, 0 neither
     145                 :       5193 :   Kind k = n.getKind();
     146 [ +  + ][ +  + ]:       5193 :   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         [ +  + ]:       5193 :   Node nn = pol == -1 ? n.notNode() : n;
     176         [ +  - ]:      10386 :   Trace("strings-process-debug")
     177                 :       5193 :       << "Process reduction for " << n << ", pol = " << pol << std::endl;
     178 [ +  + ][ +  + ]:       5193 :   if (k == Kind::STRING_CONTAINS && pol == 1)
     179                 :            :   {
     180                 :        496 :     Node x = n[0];
     181                 :        496 :     Node s = n[1];
     182                 :            :     // positive contains reduces to a equality
     183                 :        496 :     SkolemCache* skc = d_termReg.getSkolemCache();
     184                 :        496 :     Node eq = d_termReg.eagerReduce(n, skc, d_termReg.getAlphabetCardinality());
     185 [ -  + ][ -  + ]:        496 :     Assert(!eq.isNull());
                 [ -  - ]
     186                 :        496 :     Assert(eq.getKind() == Kind::ITE && eq[0] == n);
     187                 :        496 :     eq = eq[1];
     188                 :        496 :     std::vector<Node> expn;
     189                 :        496 :     expn.push_back(n);
     190                 :        496 :     d_im.sendInference(
     191                 :            :         expn, expn, eq, InferenceId::STRINGS_CTN_POS, false, true);
     192         [ +  - ]:        992 :     Trace("strings-extf-debug")
     193                 :          0 :         << "  resolve extf : " << n << " based on positive contain reduction."
     194                 :        496 :         << std::endl;
     195         [ +  - ]:        992 :     Trace("strings-red-lemma") << "Reduction (positive contains) lemma : " << n
     196                 :        496 :                                << " => " << eq << std::endl;
     197                 :            :     // reduced positively
     198 [ -  + ][ -  + ]:        496 :     Assert(nn == n);
                 [ -  - ]
     199                 :        496 :     d_reduced.insert(nn);
     200                 :        496 :   }
     201                 :            :   else
     202                 :            :   {
     203                 :       4697 :     NodeManager* nm = nodeManager();
     204 [ +  + ][ +  + ]:       4697 :     Assert(k == Kind::STRING_SUBSTR || k == Kind::STRING_UPDATE
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
         [ +  + ][ +  - ]
         [ -  + ][ -  + ]
                 [ -  - ]
     205                 :            :            || k == Kind::STRING_CONTAINS || k == Kind::STRING_INDEXOF
     206                 :            :            || k == Kind::STRING_INDEXOF_RE || k == Kind::STRING_ITOS
     207                 :            :            || k == Kind::STRING_STOI || k == Kind::STRING_REPLACE
     208                 :            :            || k == Kind::STRING_REPLACE_ALL || k == Kind::SEQ_NTH
     209                 :            :            || k == Kind::STRING_REPLACE_RE || k == Kind::STRING_REPLACE_RE_ALL
     210                 :            :            || k == Kind::STRING_LEQ || k == Kind::STRING_TO_LOWER
     211                 :            :            || k == Kind::STRING_TO_UPPER || k == Kind::STRING_REV)
     212                 :          0 :         << "Unknown reduction: " << k;
     213                 :       4697 :     std::vector<Node> new_nodes;
     214                 :       4697 :     Node res = d_preproc.simplify(n, new_nodes);
     215 [ -  + ][ -  + ]:       4697 :     Assert(res != n);
                 [ -  - ]
     216                 :            :     // If we reduced a Boolean extended function (e.g. str.<=), then n is
     217                 :            :     // replaced by a fresh purification skolem standing for a Boolean term.
     218                 :            :     // Register it as a Boolean term skolem, so that it is consistently treated
     219                 :            :     // as a theory atom (and not as a plain Boolean variable). This matters in
     220                 :            :     // incremental mode, where the skolem may be reused as a Boolean term in a
     221                 :            :     // term position (e.g. an array element) in a subsequent check-sat: its CNF
     222                 :            :     // classification is fixed when its literal is first created here, so it
     223                 :            :     // must be registered before that point.
     224 [ +  - ][ +  + ]:       4697 :     if (res.isVar() && res.getType().isBoolean())
         [ +  - ][ +  + ]
                 [ -  - ]
     225                 :            :     {
     226                 :        128 :       d_env.registerBooleanTermSkolem(res);
     227                 :            :     }
     228                 :       4697 :     new_nodes.push_back(n.eqNode(res));
     229                 :            :     Node nnlem =
     230         [ -  + ]:       4697 :         new_nodes.size() == 1 ? new_nodes[0] : nm->mkNode(Kind::AND, new_nodes);
     231                 :            :     // in rare case where it rewrites to true, just record it is reduced
     232         [ -  + ]:       4697 :     if (rewrite(nnlem) == d_true)
     233                 :            :     {
     234         [ -  - ]:          0 :       Trace("strings-extf-debug")
     235                 :          0 :           << "  resolve extf : " << n << " based on (trivial) reduction."
     236                 :          0 :           << std::endl;
     237                 :          0 :       d_reduced.insert(nn);
     238                 :            :     }
     239                 :            :     else
     240                 :            :     {
     241                 :       4697 :       InferInfo ii(InferenceId::STRINGS_REDUCTION);
     242                 :            :       // ensure that we are called to process the side effects
     243                 :       4697 :       ii.d_sim = this;
     244                 :       4697 :       ii.d_conc = nnlem;
     245                 :       4697 :       d_im.sendInference(ii, true);
     246         [ +  - ]:       9394 :       Trace("strings-extf-debug")
     247                 :       4697 :           << "  resolve extf : " << n << " based on reduction." << std::endl;
     248                 :       4697 :       d_reductionWaitingMap[nnlem] = nn;
     249                 :       4697 :     }
     250                 :       4697 :   }
     251                 :       5193 : }
     252                 :            : 
     253                 :      56510 : void ExtfSolver::checkExtfReductionsEager()
     254                 :            : {
     255                 :            :   // return value is ignored
     256                 :      56510 :   checkExtfReductionsInternal(1);
     257                 :      56510 : }
     258                 :            : 
     259                 :      27773 : void ExtfSolver::checkExtfReductions(Theory::Effort e)
     260                 :            : {
     261         [ +  + ]:      27773 :   int effort = e == Theory::EFFORT_LAST_CALL ? 3 : 2;
     262                 :            :   // return value is ignored
     263                 :      27773 :   checkExtfReductionsInternal(effort);
     264                 :      27773 : }
     265                 :            : 
     266                 :      84283 : bool ExtfSolver::checkExtfReductionsInternal(int effort)
     267                 :            : {
     268                 :            :   // Notice we don't make a standard call to ExtTheory::doReductions here,
     269                 :            :   // since certain optimizations like context-dependent reductions and
     270                 :            :   // stratifying effort levels are done in doReduction below.
     271                 :            :   // We only have to reduce extended functions that are both relevant and
     272                 :            :   // active (see getRelevantActive).
     273                 :      84283 :   std::vector<Node> extf = getRelevantActive();
     274         [ +  - ]:     168566 :   Trace("strings-process") << "  checking " << extf.size() << " active extf"
     275                 :      84283 :                            << std::endl;
     276         [ +  + ]:     530565 :   for (const Node& n : extf)
     277                 :            :   {
     278 [ -  + ][ -  + ]:     451475 :     Assert(!d_state.isInConflict());
                 [ -  - ]
     279         [ +  - ]:     902950 :     Trace("strings-extf-debug")
     280                 :          0 :         << "  check " << n
     281                 :     451475 :         << ", active in model=" << d_extfInfoTmp[n].d_modelActive << std::endl;
     282                 :            :     // polarity, 1: positive, -1: negative, 0: neither
     283                 :     451475 :     int pol = 0;
     284         [ +  + ]:     451475 :     if (n.getType().isBoolean())
     285                 :            :     {
     286                 :     139734 :       Node rep = d_state.getRepresentative(n);
     287         [ +  - ]:      69867 :       if (rep.isConst())
     288                 :            :       {
     289         [ +  + ]:      69867 :         pol = rep.getConst<bool>() ? 1 : -1;
     290                 :            :       }
     291                 :      69867 :     }
     292         [ +  + ]:     451475 :     if (shouldDoReduction(effort, n, pol))
     293                 :            :     {
     294                 :       5193 :       doReduction(n, pol);
     295                 :            :       // we do not mark as inactive, since we may want to evaluate
     296         [ +  - ]:       5193 :       if (d_im.hasProcessed())
     297                 :            :       {
     298                 :       5193 :         return true;
     299                 :            :       }
     300                 :            :     }
     301                 :            :   }
     302                 :      79090 :   return false;
     303                 :      84283 : }
     304                 :            : 
     305                 :     109280 : void ExtfSolver::checkExtfEval(int effort)
     306                 :            : {
     307         [ +  - ]:     218560 :   Trace("strings-extf-list")
     308                 :     109280 :       << "Active extended functions, effort=" << effort << " : " << std::endl;
     309                 :     109280 :   d_extfInfoTmp.clear();
     310                 :     109280 :   d_extfToOrig.clear();
     311                 :     109280 :   NodeManager* nm = nodeManager();
     312                 :     109280 :   bool has_nreduce = false;
     313                 :     109280 :   std::vector<Node> terms = d_extt.getActive();
     314                 :            :   // the set of terms we have done extf inferences for
     315                 :     109280 :   std::unordered_set<Node> inferProcessed;
     316         [ +  + ]:     978774 :   for (const Node& n : terms)
     317                 :            :   {
     318                 :            :     // Setup information about n, including if it is equal to a constant.
     319                 :     870359 :     ExtfInfoTmp& einfo = d_extfInfoTmp[n];
     320 [ -  + ][ -  + ]:     870359 :     Assert(einfo.d_exp.empty());
                 [ -  - ]
     321                 :    1740718 :     Node r = d_state.getRepresentative(n);
     322                 :     870359 :     einfo.d_const = d_bsolver.getConstantEqc(r);
     323                 :            :     // Get the current values of the children of n.
     324                 :            :     // Notice that we look up the value of the direct children of n, and not
     325                 :            :     // their free variables. In other words, given a term:
     326                 :            :     //   t = (str.replace "B" (str.replace x "A" "B") "C")
     327                 :            :     // we may build the explanation that:
     328                 :            :     //   ((str.replace x "A" "B") = "B") => t = (str.replace "B" "B" "C")
     329                 :            :     // instead of basing this on the free variable x:
     330                 :            :     //   (x = "A") => t = (str.replace "B" (str.replace "A" "A" "B") "C")
     331                 :            :     // Although both allow us to infer t = "C", it is important to use the
     332                 :            :     // first kind of inference since it ensures that its subterms have the
     333                 :            :     // expected values. Otherwise, we may in rare cases fail to realize that
     334                 :            :     // the subterm (str.replace x "A" "B") does not currently have the correct
     335                 :            :     // value, say in this example that (str.replace x "A" "B") != "B".
     336                 :     870359 :     std::vector<Node> exp;
     337                 :     870359 :     std::vector<Node> schildren;
     338                 :            :     // seq.unit is parameterized
     339         [ -  + ]:     870359 :     if (n.getMetaKind() == kind::metakind::PARAMETERIZED)
     340                 :            :     {
     341                 :          0 :       schildren.push_back(n.getOperator());
     342                 :            :     }
     343                 :     870359 :     bool schanged = false;
     344         [ +  + ]:    2862828 :     for (const Node& nc : n)
     345                 :            :     {
     346                 :    1992469 :       Node sc = getCurrentSubstitutionFor(effort, nc, exp);
     347                 :    1992469 :       schildren.push_back(sc);
     348 [ +  + ][ +  + ]:    1992469 :       schanged = schanged || sc != nc;
     349                 :    1992469 :     }
     350                 :            :     // If there is information involving the children, attempt to do an
     351                 :            :     // inference and/or mark n as reduced.
     352                 :     870359 :     bool reduced = false;
     353                 :     870359 :     Node to_reduce = n;
     354         [ +  + ]:     870359 :     if (schanged)
     355                 :            :     {
     356                 :     335761 :       Node sn = nm->mkNode(n.getKind(), schildren);
     357         [ +  - ]:     671522 :       Trace("strings-extf-debug")
     358                 :          0 :           << "Check extf " << n << " == " << sn
     359                 :          0 :           << ", constant = " << einfo.d_const << ", effort=" << effort
     360                 :     335761 :           << ", exp " << exp << std::endl;
     361                 :     335761 :       einfo.d_initExp.insert(einfo.d_initExp.end(), exp.begin(), exp.end());
     362                 :     335761 :       einfo.d_exp.insert(einfo.d_exp.end(), exp.begin(), exp.end());
     363                 :            :       // inference is rewriting the substituted node
     364                 :     335761 :       Node nrc = rewrite(sn);
     365                 :            :       // if rewrites to a constant, then do the inference and mark as reduced
     366         [ +  + ]:     335761 :       if (nrc.isConst())
     367                 :            :       {
     368                 :            :         // at effort=3, our substitution is from the model, and we don't do
     369                 :            :         // inferences based on the model, instead we check whether the
     370                 :            :         // cosntraint is already equal to its expected value below.
     371         [ +  + ]:     149733 :         if (effort < 3)
     372                 :            :         {
     373                 :     142883 :           d_extt.markInactive(n, ExtReducedId::STRINGS_SR_CONST);
     374         [ +  - ]:     285766 :           Trace("strings-extf-debug")
     375                 :     142883 :               << "  resolvable by evaluation..." << std::endl;
     376                 :     142883 :           std::vector<Node> exps;
     377                 :            :           // The following optimization gets the "symbolic definition" of
     378                 :            :           // an extended term. The symbolic definition of a term t is a term
     379                 :            :           // t' where constants are replaced by their corresponding proxy
     380                 :            :           // variables.
     381                 :            :           // For example, if lsym is a proxy variable for "", then
     382                 :            :           // str.replace( lsym, lsym, lsym ) is the symbolic definition for
     383                 :            :           // str.replace( "", "", "" ). It is generally better to use symbolic
     384                 :            :           // definitions when doing cd-rewriting for the purpose of minimizing
     385                 :            :           // clauses, e.g. we infer the unit equality:
     386                 :            :           //    str.replace( lsym, lsym, lsym ) == ""
     387                 :            :           // instead of making this inference multiple times:
     388                 :            :           //    x = "" => str.replace( x, x, x ) == ""
     389                 :            :           //    y = "" => str.replace( y, y, y ) == ""
     390         [ +  - ]:     285766 :           Trace("strings-extf-debug")
     391                 :     142883 :               << "  get symbolic definition..." << std::endl;
     392                 :     142883 :           Node nrs;
     393                 :            :           // only use symbolic definitions if option is set
     394         [ +  - ]:     142883 :           if (options().strings.stringInferSym)
     395                 :            :           {
     396                 :     142883 :             nrs = d_termReg.getSymbolicDefinition(sn, exps);
     397                 :            :           }
     398         [ +  + ]:     142883 :           if (!nrs.isNull())
     399                 :            :           {
     400         [ +  - ]:     206924 :             Trace("strings-extf-debug")
     401                 :     103462 :                 << "  rewrite " << nrs << "..." << std::endl;
     402                 :     103462 :             Node nrsr = rewrite(nrs);
     403                 :            :             // ensure the symbolic form is not rewritable
     404         [ +  + ]:     103462 :             if (nrsr != nrs)
     405                 :            :             {
     406                 :            :               // we cannot use the symbolic definition if it rewrites
     407         [ +  - ]:      13036 :               Trace("strings-extf-debug")
     408                 :       6518 :                   << "  symbolic definition is trivial..." << std::endl;
     409                 :       6518 :               nrs = Node::null();
     410                 :            :             }
     411                 :     103462 :           }
     412                 :            :           else
     413                 :            :           {
     414         [ +  - ]:      78842 :             Trace("strings-extf-debug")
     415                 :      39421 :                 << "  could not infer symbolic definition." << std::endl;
     416                 :            :           }
     417                 :     142883 :           Node conc;
     418         [ +  + ]:     142883 :           if (!nrs.isNull())
     419                 :            :           {
     420         [ +  - ]:     193888 :             Trace("strings-extf-debug")
     421                 :      96944 :                 << "  symbolic def : " << nrs << std::endl;
     422         [ +  + ]:      96944 :             if (!d_state.areEqual(nrs, nrc))
     423                 :            :             {
     424                 :            :               // infer symbolic unit
     425         [ +  + ]:       2870 :               if (n.getType().isBoolean())
     426                 :            :               {
     427         [ +  + ]:       1894 :                 conc = nrc == d_true ? nrs : nrs.negate();
     428                 :            :               }
     429                 :            :               else
     430                 :            :               {
     431                 :        976 :                 conc = nrs.eqNode(nrc);
     432                 :            :               }
     433                 :       2870 :               einfo.d_exp.clear();
     434                 :            :             }
     435                 :            :           }
     436                 :            :           else
     437                 :            :           {
     438         [ +  + ]:      45939 :             if (!d_state.areEqual(n, nrc))
     439                 :            :             {
     440         [ +  + ]:       5295 :               if (n.getType().isBoolean())
     441                 :            :               {
     442         [ +  + ]:       3163 :                 conc = nrc == d_true ? n : n.negate();
     443                 :            :               }
     444                 :            :               else
     445                 :            :               {
     446                 :       2132 :                 conc = n.eqNode(nrc);
     447                 :            :               }
     448                 :            :             }
     449                 :            :           }
     450         [ +  + ]:     142883 :           if (!conc.isNull())
     451                 :            :           {
     452         [ +  - ]:      16330 :             Trace("strings-extf")
     453                 :       8165 :                 << "  resolve extf : " << sn << " -> " << nrc << std::endl;
     454         [ +  + ]:       8165 :             InferenceId inf = effort == 0 ? InferenceId::STRINGS_EXTF
     455                 :            :                                           : InferenceId::STRINGS_EXTF_N;
     456                 :       8165 :             d_im.sendInference(einfo.d_exp, conc, inf, false, true);
     457                 :       8165 :             d_statistics.d_cdSimplifications << n.getKind();
     458                 :            :           }
     459                 :     142883 :         }
     460                 :            :         else
     461                 :            :         {
     462                 :            :           // check if it is already equal, if so, mark as reduced. Otherwise, do
     463                 :            :           // nothing.
     464         [ +  + ]:       6850 :           if (d_state.areEqual(n, nrc))
     465                 :            :           {
     466         [ +  - ]:       4704 :             Trace("strings-extf")
     467                 :          0 :                 << "  resolved extf, since satisfied by model: " << n
     468                 :       2352 :                 << std::endl;
     469                 :       2352 :             einfo.d_modelActive = false;
     470                 :            :           }
     471                 :            :         }
     472                 :     149733 :         reduced = true;
     473                 :            :       }
     474         [ +  + ]:     186028 :       else if (effort < 3)
     475                 :            :       {
     476                 :            :         // if this was a predicate which changed after substitution + rewriting
     477                 :            :         // We only do this before models are constructed (effort<3)
     478 [ +  + ][ +  + ]:     185578 :         if (!einfo.d_const.isNull() && nrc.getType().isBoolean() && nrc != n)
         [ +  + ][ +  + ]
         [ +  + ][ -  - ]
     479                 :            :         {
     480                 :      27879 :           bool pol = einfo.d_const == d_true;
     481         [ +  + ]:      27879 :           Node nrcAssert = pol ? nrc : nrc.negate();
     482         [ +  + ]:      27879 :           Node nAssert = pol ? n : n.negate();
     483                 :      27879 :           einfo.d_exp.push_back(nAssert);
     484         [ +  - ]:      27879 :           Trace("strings-extf-debug") << "  decomposable..." << std::endl;
     485         [ +  - ]:      55758 :           Trace("strings-extf") << "  resolve extf : " << sn << " -> " << nrc
     486                 :      27879 :                                 << ", const = " << einfo.d_const << std::endl;
     487                 :            :           // We send inferences internal here, which may help show unsat.
     488                 :            :           // However, we do not make a determination whether n can be marked
     489                 :            :           // reduced since this argument may be circular: we may infer than n
     490                 :            :           // can be reduced to something else, but that thing may argue that it
     491                 :            :           // can be reduced to n, in theory.
     492         [ +  + ]:      27879 :           InferenceId infer = effort == 0 ? InferenceId::STRINGS_EXTF_D
     493                 :            :                                           : InferenceId::STRINGS_EXTF_D_N;
     494                 :      27879 :           d_im.sendInternalInference(einfo.d_exp, nrcAssert, infer);
     495                 :      27879 :         }
     496                 :     185578 :         to_reduce = nrc;
     497                 :            :       }
     498                 :     335761 :     }
     499                 :            :     // We must use the original n here to avoid circular justifications for
     500                 :            :     // why extended functions are reduced. In particular, n should never be a
     501                 :            :     // duplicate of another term considered in the block of code for
     502                 :            :     // checkExtfInference below.
     503                 :            :     // if not reduced and not processed
     504         [ +  - ]:     720626 :     if (!reduced && !n.isNull()
     505 [ +  + ][ +  - ]:    1590985 :         && inferProcessed.find(n) == inferProcessed.end())
                 [ +  + ]
     506                 :            :     {
     507                 :     720626 :       inferProcessed.insert(n);
     508         [ +  + ]:     720626 :       if (effort == 1)
     509                 :            :       {
     510         [ +  - ]:     182804 :         Trace("strings-extf")
     511                 :      91402 :             << "  cannot rewrite extf : " << to_reduce << std::endl;
     512                 :            :       }
     513                 :            :       // we take to_reduce to be the (partially) reduced version of n, which
     514                 :            :       // is justified by the explanation in einfo. We only do this if we are
     515                 :            :       // not based on the model (effort<3).
     516         [ +  + ]:     720626 :       if (effort < 3)
     517                 :            :       {
     518                 :     720162 :         checkExtfInference(n, to_reduce, einfo);
     519                 :            :       }
     520         [ -  + ]:     720626 :       if (TraceIsOn("strings-extf-list"))
     521                 :            :       {
     522         [ -  - ]:          0 :         Trace("strings-extf-list") << "  * " << to_reduce;
     523         [ -  - ]:          0 :         if (!einfo.d_const.isNull())
     524                 :            :         {
     525         [ -  - ]:          0 :           Trace("strings-extf-list") << ", const = " << einfo.d_const;
     526                 :            :         }
     527         [ -  - ]:          0 :         if (n != to_reduce)
     528                 :            :         {
     529         [ -  - ]:          0 :           Trace("strings-extf-list") << ", from " << n;
     530                 :            :         }
     531         [ -  - ]:          0 :         Trace("strings-extf-list") << std::endl;
     532                 :            :       }
     533 [ +  + ][ +  - ]:     720626 :       if (d_extt.isActive(n) && einfo.d_modelActive)
         [ +  - ][ +  + ]
                 [ -  - ]
     534                 :            :       {
     535                 :     720493 :         has_nreduce = true;
     536                 :            :       }
     537                 :            :     }
     538         [ +  + ]:     870359 :     if (d_state.isInConflict())
     539                 :            :     {
     540         [ +  - ]:        865 :       Trace("strings-extf-debug") << "  conflict, return." << std::endl;
     541                 :        865 :       return;
     542                 :            :     }
     543 [ +  + ][ +  + ]:     872954 :   }
         [ +  + ][ +  + ]
     544                 :     108415 :   d_hasExtf = has_nreduce;
     545 [ +  + ][ +  + ]:     110145 : }
     546                 :            : 
     547                 :     720162 : void ExtfSolver::checkExtfInference(Node n, Node nr, ExtfInfoTmp& in)
     548                 :            : {
     549                 :            :   // see if any previous term rewrote to nr, if so, we can conclude that
     550                 :            :   // term is equal to n.
     551                 :     720162 :   std::map<Node, Node>::iterator ito = d_extfToOrig.find(nr);
     552         [ +  + ]:     720162 :   if (ito != d_extfToOrig.end())
     553                 :            :   {
     554                 :      17406 :     Node no = ito->second;
     555         [ +  + ]:      17406 :     if (!d_state.areEqual(n, no))
     556                 :            :     {
     557 [ -  + ][ -  + ]:        696 :       Assert(d_extfInfoTmp.find(no) != d_extfInfoTmp.end());
                 [ -  - ]
     558                 :        696 :       ExtfInfoTmp& eito = d_extfInfoTmp[no];
     559                 :        696 :       Node conc = n.eqNode(no);
     560         [ +  - ]:       1392 :       Trace("strings-extf-infer")
     561                 :        696 :           << "infer same rewrite: " << conc << std::endl;
     562                 :        696 :       std::vector<Node> exp;
     563                 :        696 :       exp.insert(exp.end(), in.d_initExp.begin(), in.d_initExp.end());
     564                 :        696 :       exp.insert(exp.end(), eito.d_initExp.begin(), eito.d_initExp.end());
     565         [ +  - ]:        696 :       Trace("strings-extf-infer") << "..explaination is " << exp << std::endl;
     566                 :        696 :       d_im.sendInference(exp, conc, InferenceId::STRINGS_EXTF_REW_SAME);
     567                 :        696 :     }
     568                 :      17406 :     return;
     569                 :      17406 :   }
     570                 :            :   // store that n rewrites to nr
     571                 :     702756 :   d_extfToOrig[nr] = n;
     572                 :            : 
     573         [ +  + ]:     702756 :   if (in.d_const.isNull())
     574                 :            :   {
     575                 :     484319 :     return;
     576                 :            :   }
     577                 :     218437 :   NodeManager* nm = nodeManager();
     578         [ +  - ]:     436874 :   Trace("strings-extf-infer")
     579                 :          0 :       << "checkExtfInference: " << n << " : " << nr << " == " << in.d_const
     580                 :     218437 :       << " with exp " << in.d_exp << std::endl;
     581                 :            : 
     582                 :            :   // add original to explanation
     583         [ +  + ]:     218437 :   if (n.getType().isBoolean())
     584                 :            :   {
     585                 :            :     // if Boolean, it's easy
     586         [ +  + ]:     111630 :     in.d_exp.push_back(in.d_const.getConst<bool>() ? n : n.negate());
     587                 :            :   }
     588                 :            :   else
     589                 :            :   {
     590                 :            :     // otherwise, must explain via base node
     591                 :     213614 :     Node r = d_state.getRepresentative(n);
     592                 :            :     // explain using the base solver
     593                 :     106807 :     d_bsolver.explainConstantEqc(n, r, in.d_exp);
     594                 :     106807 :   }
     595                 :            : 
     596                 :            :   // d_extfInferCache stores whether we have made the inferences associated
     597                 :            :   // with a node n,
     598                 :            :   // this may need to be generalized if multiple inferences apply
     599                 :            : 
     600         [ +  + ]:     218437 :   if (nr.getKind() == Kind::STRING_CONTAINS)
     601                 :            :   {
     602 [ -  + ][ -  + ]:      66754 :     Assert(in.d_const.isConst());
                 [ -  - ]
     603                 :      66754 :     bool pol = in.d_const.getConst<bool>();
     604 [ +  + ][ +  + ]:      92100 :     if ((pol && nr[1].getKind() == Kind::STRING_CONCAT)
                 [ -  - ]
     605 [ +  + ][ +  + ]:      92100 :         || (!pol && nr[0].getKind() == Kind::STRING_CONCAT))
         [ +  + ][ +  + ]
         [ +  + ][ -  - ]
     606                 :            :     {
     607                 :            :       // If str.contains( x, str.++( y1, ..., yn ) ),
     608                 :            :       //   we may infer str.contains( x, y1 ), ..., str.contains( x, yn )
     609                 :            :       // The following recognizes two situations related to the above reasoning:
     610                 :            :       // (1) If ~str.contains( x, yi ) holds for some i, we are in conflict,
     611                 :            :       // (2) If str.contains( x, yj ) already holds for some j, then the term
     612                 :            :       // str.contains( x, yj ) is irrelevant since it is satisfied by all models
     613                 :            :       // for str.contains( x, str.++( y1, ..., yn ) ).
     614                 :            : 
     615                 :            :       // Notice that the dual of the above reasoning also holds, i.e.
     616                 :            :       // If ~str.contains( str.++( x1, ..., xn ), y ),
     617                 :            :       //   we may infer ~str.contains( x1, y ), ..., ~str.contains( xn, y )
     618                 :            :       // This is also handled here.
     619         [ +  + ]:       5604 :       if (d_extfInferCache.find(nr) == d_extfInferCache.end())
     620                 :            :       {
     621                 :       3394 :         d_extfInferCache.insert(nr);
     622                 :            : 
     623         [ +  + ]:       3394 :         int index = pol ? 1 : 0;
     624                 :       3394 :         std::vector<Node> children;
     625                 :       3394 :         children.push_back(nr[0]);
     626                 :       3394 :         children.push_back(nr[1]);
     627         [ +  + ]:      10575 :         for (const Node& nrc : nr[index])
     628                 :            :         {
     629                 :       7202 :           children[index] = nrc;
     630                 :       7202 :           Node conc = nm->mkNode(Kind::STRING_CONTAINS, children);
     631         [ +  + ]:       7202 :           conc = rewrite(pol ? conc : conc.negate());
     632                 :            :           // check if it already (does not) hold
     633         [ +  + ]:       7202 :           if (d_state.hasTerm(conc))
     634                 :            :           {
     635         [ +  + ]:        640 :             if (d_state.areEqual(conc, d_false))
     636                 :            :             {
     637                 :            :               // we are in conflict
     638                 :         21 :               d_im.addToExplanation(conc, d_false, in.d_exp);
     639                 :         21 :               d_im.sendInference(
     640                 :         21 :                   in.d_exp, d_false, InferenceId::STRINGS_CTN_DECOMPOSE);
     641 [ -  + ][ -  + ]:         21 :               Assert(d_state.isInConflict());
                 [ -  - ]
     642                 :         21 :               return;
     643                 :            :             }
     644         [ +  + ]:        619 :             else if (d_extt.hasFunctionKind(conc.getKind()))
     645                 :            :             {
     646                 :            :               // can mark as reduced, since model for n implies model for conc
     647                 :        210 :               d_extt.markInactive(conc, ExtReducedId::STRINGS_CTN_DECOMPOSE);
     648                 :            :             }
     649                 :            :           }
     650 [ +  + ][ +  + ]:      10617 :         }
                 [ +  + ]
     651         [ +  + ]:       3394 :       }
     652                 :            :     }
     653                 :            :     else
     654                 :            :     {
     655                 :     183450 :       if (std::find(d_extfInfoTmp[nr[0]].d_ctn[pol].begin(),
     656                 :     122300 :                     d_extfInfoTmp[nr[0]].d_ctn[pol].end(),
     657                 :            :                     nr[1])
     658         [ +  - ]:     183450 :           == d_extfInfoTmp[nr[0]].d_ctn[pol].end())
     659                 :            :       {
     660 [ +  - ][ -  - ]:     122300 :         Trace("strings-extf-debug") << "  store contains info : " << nr[0]
     661 [ -  + ][ -  + ]:      61150 :                                     << " " << pol << " " << nr[1] << std::endl;
                 [ -  - ]
     662                 :            :         // Store s (does not) contains t, since nr = (~)contains( s, t ) holds.
     663                 :      61150 :         d_extfInfoTmp[nr[0]].d_ctn[pol].push_back(nr[1]);
     664                 :      61150 :         d_extfInfoTmp[nr[0]].d_ctnFrom[pol].push_back(n);
     665                 :            :         // Do transistive closure on contains, e.g.
     666                 :            :         // if contains( s, t ) and ~contains( s, r ), then ~contains( t, r ).
     667                 :            : 
     668                 :            :         // The following infers new (negative) contains based on the above
     669                 :            :         // reasoning, provided that ~contains( t, r ) does not
     670                 :            :         // already hold in the current context. We test this by checking that
     671                 :            :         // contains( t, r ) is not already asserted false in the current
     672                 :            :         // context. We also handle the case where contains( t, r ) is equivalent
     673                 :            :         // to t = r, in which case we check that t != r does not already hold
     674                 :            :         // in the current context.
     675                 :            : 
     676                 :            :         // Notice that form of the above inference is enough to find
     677                 :            :         // conflicts purely due to contains predicates. For example, if we
     678                 :            :         // have only positive occurrences of contains, then no conflicts due to
     679                 :            :         // contains predicates are possible and this schema does nothing. For
     680                 :            :         // example, note that contains( s, t ) and contains( t, r ) implies
     681                 :            :         // contains( s, r ), which we could but choose not to infer. Instead,
     682                 :            :         // we prefer being lazy: only if ~contains( s, r ) appears later do we
     683                 :            :         // infer ~contains( t, r ), which suffices to show a conflict.
     684                 :      61150 :         bool opol = !pol;
     685                 :      66173 :         for (unsigned i = 0, size = d_extfInfoTmp[nr[0]].d_ctn[opol].size();
     686         [ +  + ]:      66173 :              i < size;
     687                 :            :              i++)
     688                 :            :         {
     689                 :       5023 :           Node onr = d_extfInfoTmp[nr[0]].d_ctn[opol][i];
     690                 :            :           Node concOrig = nm->mkNode(
     691 [ +  + ][ +  + ]:      10046 :               Kind::STRING_CONTAINS, pol ? nr[1] : onr, pol ? onr : nr[1]);
     692                 :       5023 :           Node conc = rewrite(concOrig);
     693                 :            :           // For termination concerns, we only do the inference if the contains
     694                 :            :           // does not rewrite (and thus does not introduce new terms).
     695         [ +  + ]:       5023 :           if (conc == concOrig)
     696                 :            :           {
     697                 :        381 :             bool do_infer = false;
     698                 :        381 :             conc = conc.negate();
     699                 :        381 :             bool pol2 = conc.getKind() != Kind::NOT;
     700         [ -  + ]:        381 :             Node lit = pol2 ? conc : conc[0];
     701         [ -  + ]:        381 :             if (lit.getKind() == Kind::EQUAL)
     702                 :            :             {
     703                 :          0 :               do_infer = pol2 ? !d_state.areEqual(lit[0], lit[1])
     704                 :          0 :                               : !d_state.areDisequal(lit[0], lit[1]);
     705                 :            :             }
     706                 :            :             else
     707                 :            :             {
     708         [ -  + ]:        381 :               do_infer = !d_state.areEqual(lit, pol2 ? d_true : d_false);
     709                 :            :             }
     710         [ +  + ]:        381 :             if (do_infer)
     711                 :            :             {
     712                 :        173 :               std::vector<Node> exp_c;
     713                 :        173 :               exp_c.insert(exp_c.end(), in.d_exp.begin(), in.d_exp.end());
     714                 :        173 :               Node ofrom = d_extfInfoTmp[nr[0]].d_ctnFrom[opol][i];
     715 [ -  + ][ -  + ]:        173 :               Assert(d_extfInfoTmp.find(ofrom) != d_extfInfoTmp.end());
                 [ -  - ]
     716                 :        173 :               exp_c.insert(exp_c.end(),
     717                 :        173 :                            d_extfInfoTmp[ofrom].d_exp.begin(),
     718                 :        173 :                            d_extfInfoTmp[ofrom].d_exp.end());
     719                 :        173 :               d_im.sendInference(exp_c, conc, InferenceId::STRINGS_CTN_TRANS);
     720                 :        173 :             }
     721                 :        381 :           }
     722                 :       5023 :         }
     723                 :            :       }
     724                 :            :       else
     725                 :            :       {
     726                 :            :         // If we already know that s (does not) contain t, then n may be
     727                 :            :         // redundant. However, we do not mark n as reduced here, since strings
     728                 :            :         // reductions may require dependencies between extended functions.
     729                 :            :         // Marking reduced here could lead to incorrect models if an
     730                 :            :         // extended function is marked reduced based on an assignment to
     731                 :            :         // something that depends on n.
     732         [ -  - ]:          0 :         Trace("strings-extf-debug") << "  redundant." << std::endl;
     733                 :            :       }
     734                 :            :     }
     735                 :      66733 :     return;
     736                 :            :   }
     737                 :            : 
     738                 :            :   // If it's not a predicate, see if we can solve the equality n = c, where c
     739                 :            :   // is the constant that extended term n is equal to.
     740                 :     151683 :   Node inferEq = nr.eqNode(in.d_const);
     741                 :     151683 :   Node inferEqr = rewrite(inferEq);
     742                 :     151683 :   Node inferEqrr = inferEqr;
     743         [ +  + ]:     151683 :   if (inferEqr.getKind() == Kind::EQUAL)
     744                 :            :   {
     745                 :            :     // try to use the extended rewriter for equalities
     746                 :     107330 :     inferEqrr = d_rewriter.rewriteEqualityExt(inferEqr);
     747                 :            :   }
     748         [ +  + ]:     151683 :   if (inferEqrr != inferEqr)
     749                 :            :   {
     750                 :      10095 :     inferEqrr = rewrite(inferEqrr);
     751         [ +  - ]:      20190 :     Trace("strings-extf-infer")
     752                 :          0 :         << "checkExtfInference: " << inferEq << " ...reduces to " << inferEqrr
     753                 :      10095 :         << " with explanation " << in.d_exp << std::endl;
     754                 :      20190 :     d_im.sendInternalInference(
     755                 :      10095 :         in.d_exp, inferEqrr, InferenceId::STRINGS_EXTF_EQ_REW);
     756                 :            :   }
     757                 :     151683 : }
     758                 :            : 
     759                 :    1992469 : Node ExtfSolver::getCurrentSubstitutionFor(int effort,
     760                 :            :                                            Node n,
     761                 :            :                                            std::vector<Node>& exp)
     762                 :            : {
     763         [ +  + ]:    1992469 :   if (effort >= 3)
     764                 :            :   {
     765                 :            :     // model values
     766                 :      16236 :     Node mv = d_state.getModel()->getRepresentative(n);
     767         [ +  - ]:      16236 :     Trace("strings-subs") << "   model val : " << mv << std::endl;
     768                 :      16236 :     return mv;
     769                 :      16236 :   }
     770                 :    3952466 :   Node nr = d_state.getRepresentative(n);
     771                 :            :   // if the normal form is available, use it
     772 [ +  + ][ +  + ]:    1976233 :   if (effort >= 1 && n.getType().isStringLike())
         [ +  + ][ +  + ]
                 [ -  - ]
     773                 :            :   {
     774 [ -  + ][ -  + ]:     153937 :     Assert(effort < 3);
                 [ -  - ]
     775                 :            :     // Return self if the normal form has not been computed. This may happen
     776                 :            :     // for terms that are not relevant in the current context.
     777         [ +  + ]:     153937 :     if (!d_csolver.hasNormalForm(nr))
     778                 :            :     {
     779                 :          2 :       return n;
     780                 :            :     }
     781                 :     153935 :     NormalForm& nfnr = d_csolver.getNormalForm(nr);
     782                 :     153935 :     Node ns;
     783 [ +  + ][ +  + ]:     153935 :     if (n.getKind() == Kind::STRING_CONCAT && n != nfnr.d_base)
                 [ +  + ]
     784                 :            :     {
     785                 :            :       // if the normal base is a term (str.++ t1 t2), and we are a term
     786                 :            :       // (str.++ s1 s2), then we explain the normal form concatentation of
     787                 :            :       // s1 and s2, instead of explaining (= (str.++ s1 s2) (str.++ t1 t2)) and
     788                 :            :       // concatentating the normal form explanation of t1 and t2. This
     789                 :            :       // ensures the explanation when taking as a substitution does not have
     790                 :            :       // concatentation terms on the LHS of equalities, which can lead to
     791                 :            :       // cyclic proof dependencies.
     792                 :       3463 :       std::vector<Node> vec;
     793         [ +  + ]:      10595 :       for (const Node& nc : n)
     794                 :            :       {
     795                 :      14264 :         Node ncr = d_state.getRepresentative(nc);
     796 [ -  + ][ -  + ]:       7132 :         Assert(d_csolver.hasNormalForm(ncr));
                 [ -  - ]
     797                 :       7132 :         NormalForm& nfnrc = d_csolver.getNormalForm(ncr);
     798                 :       7132 :         Node nsc = d_csolver.getNormalString(nfnrc.d_base, exp);
     799                 :       7132 :         d_im.addToExplanation(nc, nfnrc.d_base, exp);
     800                 :       7132 :         vec.push_back(nsc);
     801                 :       7132 :       }
     802                 :       3463 :       TypeNode stype = n.getType();
     803                 :       3463 :       ns = d_termReg.mkNConcat(vec, stype);
     804                 :       3463 :     }
     805                 :            :     else
     806                 :            :     {
     807                 :     150472 :       ns = d_csolver.getNormalString(nfnr.d_base, exp);
     808         [ +  - ]:     300944 :       Trace("strings-subs") << "   normal eqc : " << ns << " " << nfnr.d_base
     809                 :     150472 :                             << " " << nr << std::endl;
     810         [ +  - ]:     150472 :       if (!nfnr.d_base.isNull())
     811                 :            :       {
     812                 :     150472 :         d_im.addToExplanation(n, nfnr.d_base, exp);
     813                 :            :       }
     814                 :            :     }
     815                 :     153935 :     return ns;
     816                 :     153935 :   }
     817                 :            :   // otherwise, we use the best content heuristic
     818                 :    1822296 :   std::vector<Node> cexp;
     819                 :    3644592 :   Node c = d_bsolver.explainBestContentEqc(n, nr, cexp);
     820 [ +  + ][ +  + ]:    1822296 :   if (!c.isNull() && n.getKind() == Kind::STRING_CONCAT)
                 [ +  + ]
     821                 :            :   {
     822                 :      19694 :     cexp.clear();
     823                 :            :     // Similar to above, if we are a string concatentation, we ask for the
     824                 :            :     // best content of each of our children and concatenate them together.
     825                 :            :     // We consider the substitution only if at least one child had a best
     826                 :            :     // content. This prevents substitutions with concatenation terms on the
     827                 :            :     // left hand side, which can lead to cycles in the algorithm that elaborates
     828                 :            :     // proofs in very rare cases.
     829                 :      19694 :     std::vector<Node> vec;
     830         [ +  + ]:      65668 :     for (const Node& nc : n)
     831                 :            :     {
     832                 :      91948 :       Node ncr = d_state.getRepresentative(nc);
     833                 :      91948 :       Node cc = d_bsolver.explainBestContentEqc(nc, ncr, cexp);
     834         [ +  + ]:      45974 :       if (!cc.isNull())
     835                 :            :       {
     836                 :      35660 :         vec.push_back(cc);
     837                 :            :       }
     838                 :            :       else
     839                 :            :       {
     840                 :            :         // otherwise keep the same
     841                 :      10314 :         vec.push_back(nc);
     842                 :            :       }
     843                 :      45974 :     }
     844                 :      19694 :     TypeNode stype = n.getType();
     845                 :      19694 :     c = d_termReg.mkNConcat(vec, stype);
     846                 :      19694 :   }
     847         [ +  + ]:    1822296 :   if (!c.isNull())
     848                 :            :   {
     849                 :     961100 :     exp.insert(exp.end(), cexp.begin(), cexp.end());
     850                 :     961100 :     return c;
     851                 :            :   }
     852                 :     861196 :   return n;
     853                 :    1976233 : }
     854                 :            : 
     855                 :          0 : const std::map<Node, ExtfInfoTmp>& ExtfSolver::getInfo() const
     856                 :            : {
     857                 :          0 :   return d_extfInfoTmp;
     858                 :            : }
     859                 :      16288 : bool ExtfSolver::hasExtendedFunctions() const { return d_hasExtf.get(); }
     860                 :            : 
     861                 :      26181 : std::vector<Node> ExtfSolver::getActive(Kind k) const
     862                 :            : {
     863                 :      26181 :   return d_extt.getActive(k);
     864                 :            : }
     865                 :            : 
     866                 :     451615 : bool ExtfSolver::isActiveInModel(Node n) const
     867                 :            : {
     868                 :     451615 :   std::map<Node, ExtfInfoTmp>::const_iterator it = d_extfInfoTmp.find(n);
     869         [ -  + ]:     451615 :   if (it == d_extfInfoTmp.end())
     870                 :            :   {
     871                 :          0 :     DebugUnhandled() << "isActiveInModel: Expected extf info for " << n;
     872                 :            :     return true;
     873                 :            :   }
     874                 :     451615 :   return it->second.d_modelActive;
     875                 :            : }
     876                 :            : 
     877                 :      84760 : std::vector<Node> ExtfSolver::getRelevantActive() const
     878                 :            : {
     879                 :            :   // get the relevant term set
     880                 :      84760 :   std::vector<Node> extf = d_extt.getActive();
     881                 :      84760 :   const std::set<Node>& relevantTerms = d_termReg.getRelevantTermSet();
     882                 :            : 
     883                 :      84760 :   std::vector<Node> res;
     884         [ +  + ]:     562632 :   for (const Node& n : extf)
     885                 :            :   {
     886         [ +  + ]:     477872 :     if (relevantTerms.find(n) == relevantTerms.end())
     887                 :            :     {
     888                 :            :       // not relevant
     889                 :       9384 :       continue;
     890                 :            :     }
     891                 :     468488 :     res.push_back(n);
     892                 :            :   }
     893                 :     169520 :   return res;
     894                 :      84760 : }
     895                 :            : 
     896                 :          0 : bool StringsExtfCallback::getCurrentSubstitution(
     897                 :            :     int effort,
     898                 :            :     const std::vector<Node>& vars,
     899                 :            :     std::vector<Node>& subs,
     900                 :            :     std::map<Node, std::vector<Node> >& exp)
     901                 :            : {
     902         [ -  - ]:          0 :   Trace("strings-subs") << "getCurrentSubstitution, effort = " << effort
     903                 :          0 :                         << std::endl;
     904         [ -  - ]:          0 :   for (const Node& v : vars)
     905                 :            :   {
     906         [ -  - ]:          0 :     Trace("strings-subs") << "  get subs for " << v << "..." << std::endl;
     907                 :          0 :     Node s = d_esolver->getCurrentSubstitutionFor(effort, v, exp[v]);
     908                 :          0 :     subs.push_back(s);
     909                 :          0 :   }
     910                 :          0 :   return true;
     911                 :            : }
     912                 :            : 
     913                 :          0 : void ExtfSolver::processFact(InferInfo& ii, ProofGenerator*& pg)
     914                 :            : {
     915                 :            :   // process it with the inference manager
     916                 :          0 :   d_im.processFact(ii, pg);
     917                 :          0 : }
     918                 :            : 
     919                 :       4697 : TrustNode ExtfSolver::processLemma(InferInfo& ii, LemmaProperty& p)
     920                 :            : {
     921                 :            :   // if this was the reduction lemma for a term, mark it reduced now
     922                 :       4697 :   std::map<Node, Node>::iterator it = d_reductionWaitingMap.find(ii.d_conc);
     923         [ +  - ]:       4697 :   if (it != d_reductionWaitingMap.end())
     924                 :            :   {
     925                 :       4697 :     d_reduced.insert(it->second);
     926                 :       4697 :     d_reductionWaitingMap.erase(it);
     927                 :            :   }
     928                 :            :   // now process it with the inference manager
     929                 :       9394 :   return d_im.processLemma(ii, p);
     930                 :            : }
     931                 :            : 
     932                 :          0 : std::string ExtfSolver::debugPrintModel()
     933                 :            : {
     934                 :          0 :   std::stringstream ss;
     935                 :          0 :   std::vector<Node> extf;
     936                 :          0 :   d_extt.getTerms(extf);
     937                 :            :   // each extended function should have at least one annotation below
     938         [ -  - ]:          0 :   for (const Node& n : extf)
     939                 :            :   {
     940                 :          0 :     ss << "- " << n;
     941                 :            :     ExtReducedId id;
     942         [ -  - ]:          0 :     if (!d_extt.isActive(n, id))
     943                 :            :     {
     944                 :          0 :       ss << " :extt-inactive " << id;
     945                 :            :     }
     946         [ -  - ]:          0 :     if (!d_extfInfoTmp[n].d_modelActive)
     947                 :            :     {
     948                 :          0 :       ss << " :model-inactive";
     949                 :            :     }
     950         [ -  - ]:          0 :     if (d_reduced.find(n) != d_reduced.end())
     951                 :            :     {
     952                 :          0 :       ss << " :reduced";
     953                 :            :     }
     954                 :          0 :     ss << std::endl;
     955                 :            :   }
     956                 :          0 :   return ss.str();
     957                 :          0 : }
     958                 :            : 
     959                 :       3688 : bool ExtfSolver::isReduced(const Node& n) const
     960                 :            : {
     961                 :       3688 :   return d_reduced.find(n) != d_reduced.end();
     962                 :            : }
     963                 :            : 
     964                 :        880 : void ExtfSolver::markReduced(const Node& n) { d_reduced.insert(n); }
     965                 :            : 
     966                 :            : }  // namespace strings
     967                 :            : }  // namespace theory
     968                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14