LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/strings - array_solver.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 206 218 94.5 %
Date: 2026-08-16 10:37:17 Functions: 9 10 90.0 %
Branches: 123 168 73.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                 :            :  * Sequences solver for seq.nth/seq.update.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "theory/strings/array_solver.h"
      14                 :            : 
      15                 :            : #include "expr/sequence.h"
      16                 :            : #include "theory/strings/arith_entail.h"
      17                 :            : #include "theory/strings/theory_strings_utils.h"
      18                 :            : #include "theory/strings/word.h"
      19                 :            : #include "util/rational.h"
      20                 :            : #include "util/string.h"
      21                 :            : 
      22                 :            : using namespace cvc5::context;
      23                 :            : using namespace cvc5::internal::kind;
      24                 :            : 
      25                 :            : namespace cvc5::internal {
      26                 :            : namespace theory {
      27                 :            : namespace strings {
      28                 :            : 
      29                 :      28738 : ArraySolver::ArraySolver(Env& env,
      30                 :            :                          SolverState& s,
      31                 :            :                          InferenceManager& im,
      32                 :            :                          TermRegistry& tr,
      33                 :            :                          BaseSolver& bs,
      34                 :            :                          CoreSolver& cs,
      35                 :            :                          ExtfSolver& es,
      36                 :      28738 :                          ExtTheory& extt)
      37                 :            :     : EnvObj(env),
      38                 :      28738 :       d_state(s),
      39                 :      28738 :       d_im(im),
      40                 :      28738 :       d_termReg(tr),
      41                 :      28738 :       d_bsolver(bs),
      42                 :      28738 :       d_csolver(cs),
      43                 :      28738 :       d_esolver(es),
      44                 :      28738 :       d_coreSolver(env, s, im, tr, es, extt),
      45                 :      57476 :       d_eqProc(context())
      46                 :            : {
      47                 :      28738 :   NodeManager* nm = nodeManager();
      48                 :      28738 :   d_zero = nm->mkConstInt(Rational(0));
      49                 :      28738 : }
      50                 :            : 
      51                 :      28725 : ArraySolver::~ArraySolver() {}
      52                 :            : 
      53                 :        389 : void ArraySolver::checkArrayConcat()
      54                 :            : {
      55         [ +  + ]:        389 :   if (!d_termReg.hasSeqUpdate())
      56                 :            :   {
      57         [ +  - ]:        164 :     Trace("seq-array") << "No seq.update/seq.nth terms, skipping check..."
      58                 :         82 :                        << std::endl;
      59                 :         82 :     return;
      60                 :            :   }
      61                 :        307 :   d_currTerms.clear();
      62         [ +  - ]:        307 :   Trace("seq-array") << "ArraySolver::checkArrayConcat..." << std::endl;
      63                 :            :   // Get the set of relevant terms. The core array solver requires knowing this
      64                 :            :   // set to ensure its write model is only over relevant terms.
      65                 :        307 :   std::vector<Node> terms = d_esolver.getRelevantActive();
      66                 :        307 :   checkTerms(terms);
      67                 :        307 : }
      68                 :            : 
      69                 :        245 : void ArraySolver::checkArray()
      70                 :            : {
      71         [ +  + ]:        245 :   if (!d_termReg.hasSeqUpdate())
      72                 :            :   {
      73         [ +  - ]:        164 :     Trace("seq-array") << "No seq.update/seq.nth terms, skipping check..."
      74                 :         82 :                        << std::endl;
      75                 :         82 :     return;
      76                 :            :   }
      77         [ +  - ]:        163 :   Trace("seq-array") << "ArraySolver::checkArray..." << std::endl;
      78                 :        163 :   d_coreSolver.check(d_currTerms[Kind::SEQ_NTH],
      79                 :        326 :                      d_currTerms[Kind::STRING_UPDATE]);
      80                 :            : }
      81                 :            : 
      82                 :        170 : void ArraySolver::checkArrayEager()
      83                 :            : {
      84         [ -  + ]:        170 :   if (!d_termReg.hasSeqUpdate())
      85                 :            :   {
      86         [ -  - ]:          0 :     Trace("seq-array") << "No seq.update/seq.nth terms, skipping check..."
      87                 :          0 :                        << std::endl;
      88                 :          0 :     return;
      89                 :            :   }
      90         [ +  - ]:        170 :   Trace("seq-array") << "ArraySolver::checkArray..." << std::endl;
      91                 :            :   // get the set of relevant terms, for reasons described above
      92                 :        170 :   std::vector<Node> terms = d_esolver.getRelevantActive();
      93                 :        170 :   std::vector<Node> nthTerms;
      94                 :        170 :   std::vector<Node> updateTerms;
      95         [ +  + ]:       1019 :   for (const Node& n : terms)
      96                 :            :   {
      97                 :        849 :     Kind k = n.getKind();
      98         [ +  + ]:        849 :     if (k == Kind::STRING_UPDATE)
      99                 :            :     {
     100                 :        149 :       updateTerms.push_back(n);
     101                 :            :     }
     102         [ +  + ]:        700 :     else if (k == Kind::SEQ_NTH)
     103                 :            :     {
     104                 :        458 :       nthTerms.push_back(n);
     105                 :            :     }
     106                 :            :   }
     107                 :        170 :   d_coreSolver.check(nthTerms, updateTerms);
     108                 :        170 : }
     109                 :            : 
     110                 :        307 : void ArraySolver::checkTerms(const std::vector<Node>& terms)
     111                 :            : {
     112                 :            :   // get all the active update terms that have not been reduced in the
     113                 :            :   // current context by context-dependent simplification
     114                 :        307 :   std::unordered_set<Node> processed;
     115         [ +  + ]:       4269 :   for (const Node& t : terms)
     116                 :            :   {
     117                 :       3962 :     bool checkInv = false;
     118                 :       3962 :     Kind k = t.getKind();
     119         [ +  - ]:       3962 :     Trace("seq-array-debug") << "check term " << t << "..." << std::endl;
     120         [ +  + ]:       3962 :     if (k == Kind::STRING_UPDATE)
     121                 :            :     {
     122         [ +  + ]:        598 :       if (!d_termReg.isHandledUpdateOrSubstr(t))
     123                 :            :       {
     124                 :            :         // not handled by procedure
     125         [ +  - ]:         98 :         Trace("seq-array-debug") << "...unhandled" << std::endl;
     126                 :         98 :         continue;
     127                 :            :       }
     128                 :            :       // for update terms, also check the inverse inference
     129                 :        500 :       checkInv = true;
     130                 :            :     }
     131         [ +  + ]:       3364 :     else if (k != Kind::SEQ_NTH)
     132                 :            :     {
     133                 :        446 :       continue;
     134                 :            :     }
     135                 :            : 
     136         [ +  + ]:       3418 :     if (d_bsolver.isCongruent(t))
     137                 :            :     {
     138                 :        890 :       continue;
     139                 :            :     }
     140                 :            : 
     141                 :            :     // check the normal inference
     142                 :       2528 :     checkTerm(t, false);
     143         [ +  + ]:       2528 :     if (checkInv)
     144                 :            :     {
     145                 :        424 :       checkTerm(t, true);
     146                 :            :     }
     147                 :            :   }
     148                 :        307 : }
     149                 :            : 
     150                 :       2952 : void ArraySolver::checkTerm(Node t, bool checkInv)
     151                 :            : {
     152                 :       2952 :   NodeManager* nm = nodeManager();
     153                 :       2952 :   Kind k = t.getKind();
     154                 :       5904 :   Node r = d_state.getRepresentative(t[0]);
     155                 :       2952 :   Node rself;
     156                 :       2952 :   NormalForm& nf = d_csolver.getNormalForm(r);
     157         [ +  - ]:       2952 :   Trace("seq-array-debug") << "...normal form " << nf.d_nf << std::endl;
     158                 :       2952 :   std::vector<Node> nfChildren;
     159                 :            : 
     160         [ +  + ]:       2952 :   if (k == Kind::SEQ_NTH)
     161                 :            :   {
     162                 :            :     // The core solver must process all `nth` terms
     163                 :       2104 :     d_currTerms[Kind::SEQ_NTH].push_back(t);
     164                 :            :   }
     165                 :            : 
     166         [ +  + ]:       2952 :   if (checkInv)
     167                 :            :   {
     168         [ -  + ]:        424 :     if (k != Kind::STRING_UPDATE)
     169                 :            :     {
     170                 :          0 :       return;
     171                 :            :     }
     172                 :            :     // If the term we are updating is atomic, but the update itself
     173                 :            :     // not atomic, then we will apply the inverse version of the update
     174                 :            :     // concat rule, based on the normal form of the term itself.
     175                 :        424 :     rself = d_state.getRepresentative(t);
     176                 :        424 :     NormalForm& nfSelf = d_csolver.getNormalForm(rself);
     177         [ +  + ]:        424 :     if (nfSelf.d_nf.size() > 1)
     178                 :            :     {
     179                 :         90 :       nfChildren.insert(
     180                 :         90 :           nfChildren.end(), nfSelf.d_nf.begin(), nfSelf.d_nf.end());
     181                 :            :     }
     182                 :            :     else
     183                 :            :     {
     184                 :        379 :       return;
     185                 :            :     }
     186                 :            :   }
     187                 :            :   else
     188                 :            :   {
     189         [ +  + ]:       2528 :     if (nf.d_nf.empty())
     190                 :            :     {
     191                 :            :       // updates should have been reduced (UPD_EMPTYSTR)
     192 [ -  + ][ -  + ]:          4 :       Assert(k != Kind::STRING_UPDATE);
                 [ -  - ]
     193         [ +  - ]:          4 :       Trace("seq-array-debug") << "...empty" << std::endl;
     194                 :          4 :       return;
     195                 :            :     }
     196         [ +  + ]:       2524 :     else if (nf.d_nf.size() == 1)
     197                 :            :     {
     198         [ +  - ]:       2091 :       Trace("seq-array-debug") << "...norm form size 1" << std::endl;
     199                 :            :       // NOTE: could split on n=0 if needed, do not introduce ITE
     200                 :       2091 :       Kind ck = nf.d_nf[0].getKind();
     201                 :       2091 :       bool cIsConst = nf.d_nf[0].isConst();
     202                 :            :       // Note that (seq.unit c) is rewritten to CONST_SEQUENCE{c}, hence we
     203                 :            :       // check two cases here. It is important for completeness of this schema
     204                 :            :       // to handle this differently from STRINGS_ARRAY_UPDATE_CONCAT /
     205                 :            :       // STRINGS_ARRAY_NTH_CONCAT. Otherwise we would conclude a trivial
     206                 :            :       // equality when update/nth is applied to a constant of length one.
     207         [ +  - ]:       1672 :       if (ck == Kind::SEQ_UNIT || ck == Kind::STRING_UNIT
     208 [ +  + ][ +  + ]:       3763 :           || (cIsConst && Word::getLength(nf.d_nf[0]) == 1))
         [ +  - ][ +  + ]
         [ +  + ][ -  - ]
     209                 :            :       {
     210         [ +  - ]:        446 :         Trace("seq-array-debug") << "...unit case" << std::endl;
     211                 :            :         // do we know whether n = 0 ?
     212                 :            :         // x = (seq.unit m) => (seq.update x n z) = ite(n=0, z, (seq.unit m))
     213                 :            :         // x = (seq.unit m) ^ n=0 => (seq.nth x n) = m
     214                 :            :         InferenceId iid;
     215                 :        446 :         Node eq;
     216                 :        446 :         std::vector<Node> exp;
     217                 :        446 :         std::vector<Node> nexp;
     218                 :        446 :         d_im.addToExplanation(t[0], nf.d_nf[0], exp);
     219                 :        446 :         d_im.addToExplanation(r, t[0], exp);
     220         [ +  + ]:        446 :         if (k == Kind::STRING_UPDATE)
     221                 :            :         {
     222                 :          7 :           iid = InferenceId::STRINGS_ARRAY_UPDATE_UNIT;
     223 [ +  + ][ -  - ]:         49 :           eq = nm->mkNode(
     224                 :            :               Kind::ITE,
     225                 :         35 :               {t[1].eqNode(d_zero), t.eqNode(t[2]), t.eqNode(nf.d_nf[0])});
     226                 :            :         }
     227                 :            :         else
     228                 :            :         {
     229         [ +  + ]:        439 :           if (d_state.areDisequal(t[1], d_zero))
     230                 :            :           {
     231                 :            :             // n is known to be disequal from zero, skip
     232                 :         56 :             return;
     233                 :            :           }
     234 [ -  + ][ -  + ]:        383 :           Assert(k == Kind::SEQ_NTH);
                 [ -  - ]
     235                 :        383 :           Node val;
     236         [ +  + ]:        383 :           if (cIsConst)
     237                 :            :           {
     238                 :         20 :             val = Word::getNth(nf.d_nf[0], 0);
     239                 :            :           }
     240                 :            :           else
     241                 :            :           {
     242                 :        363 :             val = nf.d_nf[0][0];
     243                 :            :           }
     244                 :        383 :           iid = InferenceId::STRINGS_ARRAY_NTH_UNIT;
     245                 :        383 :           eq = t.eqNode(val);
     246         [ +  + ]:        383 :           if (t[1] != d_zero)
     247                 :            :           {
     248                 :        292 :             exp.push_back(t[1].eqNode(d_zero));
     249                 :        292 :             nexp.push_back(t[1].eqNode(d_zero));
     250                 :            :           }
     251                 :        383 :         }
     252         [ +  + ]:        390 :         if (d_eqProc.find(eq) == d_eqProc.end())
     253                 :            :         {
     254                 :        271 :           d_eqProc.insert(eq);
     255                 :        271 :           d_im.sendInference(exp, nexp, eq, iid);
     256                 :            :         }
     257                 :        390 :         return;
     258                 :        446 :       }
     259         [ +  - ]:       1645 :       else if (!cIsConst)
     260                 :            :       {
     261         [ +  + ]:       1645 :         if (k == Kind::STRING_UPDATE)
     262                 :            :         {
     263                 :            :           // If the term we are updating is atomic, but the update itself
     264                 :            :           // not atomic, then we will apply the inverse version of the update
     265                 :            :           // concat rule, based on the normal form of the term itself.
     266                 :        374 :           rself = d_state.getRepresentative(t);
     267                 :        374 :           NormalForm& nfSelf = d_csolver.getNormalForm(rself);
     268         [ +  + ]:        374 :           if (nfSelf.d_nf.size() == 1)
     269                 :            :           {
     270                 :            :             // otherwise, if the normal form is not a constant word, and we
     271                 :            :             // are an atomic update term, then this term will be given to the
     272                 :            :             // core array solver.
     273                 :        372 :             d_currTerms[k].push_back(t);
     274                 :            :           }
     275                 :            :         }
     276                 :       1645 :         return;
     277                 :            :       }
     278                 :            :       else
     279                 :            :       {
     280                 :            :         // if the normal form is a constant word, it is treated as a
     281                 :            :         // concatenation. We split per character and case split on whether the
     282                 :            :         // nth/update falls on each character below, which must have a size
     283                 :            :         // greater than one.
     284                 :          0 :         std::vector<Node> chars = Word::getChars(nf.d_nf[0]);
     285                 :          0 :         Assert(chars.size() > 1);
     286                 :          0 :         nfChildren.insert(nfChildren.end(), chars.begin(), chars.end());
     287                 :          0 :       }
     288                 :            :     }
     289                 :            :     else
     290                 :            :     {
     291                 :        433 :       nfChildren.insert(nfChildren.end(), nf.d_nf.begin(), nf.d_nf.end());
     292                 :            :     }
     293                 :            :   }
     294                 :            :   // otherwise, we are the concatenation of the components
     295                 :            :   // NOTE: for nth, split on index vs component lengths, do not introduce ITE
     296                 :        478 :   std::vector<Node> cond;
     297                 :        478 :   std::vector<Node> cchildren;
     298                 :        478 :   std::vector<Node> lacc;
     299                 :        478 :   SkolemCache* skc = d_termReg.getSkolemCache();
     300         [ +  + ]:       1574 :   for (const Node& c : nfChildren)
     301                 :            :   {
     302         [ +  - ]:       1096 :     Trace("seq-array-debug") << "...process " << c << std::endl;
     303                 :       1096 :     Node clen = nm->mkNode(Kind::STRING_LENGTH, c);
     304                 :       1096 :     Node currIndex = t[1];
     305                 :       1096 :     Node currSum = d_zero;
     306         [ +  + ]:       1096 :     if (!lacc.empty())
     307                 :            :     {
     308         [ +  + ]:        618 :       currSum = lacc.size() == 1 ? lacc[0] : nm->mkNode(Kind::ADD, lacc);
     309                 :        618 :       currIndex = nm->mkNode(Kind::SUB, currIndex, currSum);
     310                 :            :     }
     311                 :       1096 :     Node cc;
     312 [ +  + ][ +  + ]:       1096 :     if (k == Kind::STRING_UPDATE && checkInv)
     313                 :            :     {
     314                 :            :       // component for the reverse form of the update inference is a fresh
     315                 :            :       // variable, in particular, the purification variable for the substring
     316                 :            :       // of the term we are updating.
     317                 :        180 :       Node sstr = nm->mkNode(Kind::STRING_SUBSTR, t[0], currSum, clen);
     318                 :         90 :       cc = skc->mkSkolemCached(sstr, SkolemCache::SK_PURIFY, "z");
     319                 :         90 :     }
     320                 :            :     // If it is a constant of length one, then the update/nth is determined
     321                 :            :     // in this interval. Notice this is done here as
     322                 :            :     // an optimization to short cut introducing terms like
     323                 :            :     // (seq.nth (seq.unit c) i), which by construction is only relevant in
     324                 :            :     // the context where i = 0, hence we replace by c here.
     325         [ -  + ]:       1006 :     else if (c.isConst())
     326                 :            :     {
     327         [ -  - ]:          0 :       if (Word::getLength(c) == 1)
     328                 :            :       {
     329         [ -  - ]:          0 :         if (k == Kind::STRING_UPDATE)
     330                 :            :         {
     331                 :          0 :           cc = nm->mkNode(Kind::ITE, t[1].eqNode(d_zero), t[2], c);
     332                 :            :         }
     333                 :            :         else
     334                 :            :         {
     335                 :          0 :           cc = Word::getNth(c, 0);
     336                 :            :         }
     337                 :            :       }
     338                 :            :     }
     339                 :            :     // if we did not process as a constant of length one
     340         [ +  + ]:       1096 :     if (cc.isNull())
     341                 :            :     {
     342         [ +  + ]:       1006 :       if (k == Kind::STRING_UPDATE)
     343                 :            :       {
     344                 :         86 :         cc = nm->mkNode(Kind::STRING_UPDATE, c, currIndex, t[2]);
     345                 :            :       }
     346                 :            :       else
     347                 :            :       {
     348 [ -  + ][ -  + ]:        920 :         Assert(k == Kind::SEQ_NTH);
                 [ -  - ]
     349                 :        920 :         cc = nm->mkNode(Kind::SEQ_NTH, c, currIndex);
     350                 :            :       }
     351                 :            :     }
     352         [ +  - ]:       1096 :     Trace("seq-array-debug") << "......component " << cc << std::endl;
     353                 :       1096 :     cchildren.push_back(cc);
     354                 :       1096 :     lacc.push_back(clen);
     355         [ +  + ]:       1096 :     if (k == Kind::SEQ_NTH)
     356                 :            :     {
     357                 :            :       Node currSumPost =
     358         [ +  + ]:        920 :           lacc.size() == 1 ? lacc[0] : nm->mkNode(Kind::ADD, lacc);
     359                 :       1840 :       Node cf = nm->mkNode(Kind::LT, t[1], currSumPost);
     360         [ +  - ]:        920 :       Trace("seq-array-debug") << "......condition " << cf << std::endl;
     361                 :        920 :       cond.push_back(cf);
     362                 :        920 :     }
     363 [ +  - ][ +  + ]:        176 :     else if (k == Kind::STRING_UPDATE && checkInv)
     364                 :            :     {
     365                 :        180 :       Node ccu = nm->mkNode(Kind::STRING_UPDATE, cc, currIndex, t[2]);
     366                 :         90 :       Node eq = c.eqNode(ccu);
     367         [ +  - ]:         90 :       Trace("seq-array-debug") << "......condition " << eq << std::endl;
     368                 :         90 :       cond.push_back(eq);
     369                 :         90 :     }
     370                 :       1096 :   }
     371                 :            :   // z = (seq.++ x y) =>
     372                 :            :   // (seq.update z n l) =
     373                 :            :   //   (seq.++ (seq.update x n 1) (seq.update y (- n len(x)) 1))
     374                 :            :   // z = (seq.++ x y) ^ (>= n 0) ^ (< n (+ (str.len x) (str.len y)))) =>
     375                 :            :   // (seq.nth z n) =
     376                 :            :   //    (ite (< n (str.len x)) (seq.nth x n)
     377                 :            :   //      (seq.nth y (- n (str.len x))))
     378                 :            :   InferenceId iid;
     379                 :        478 :   std::vector<Node> exp;
     380                 :        478 :   std::vector<Node> nexp;
     381                 :        478 :   Node eq;
     382         [ +  + ]:        478 :   if (k == Kind::STRING_UPDATE)
     383                 :            :   {
     384                 :         88 :     Node finalc = utils::mkConcat(cchildren, t.getType());
     385         [ +  + ]:         88 :     if (checkInv)
     386                 :            :     {
     387                 :         45 :       eq = t[0].eqNode(finalc);
     388                 :         45 :       cond.push_back(eq);
     389                 :         45 :       eq = nm->mkAnd(cond);
     390                 :            :     }
     391                 :            :     else
     392                 :            :     {
     393                 :         43 :       eq = t.eqNode(finalc);
     394                 :            :     }
     395                 :            :     // Must rewrite the equality to ensure terms are in rewritten form. This
     396                 :            :     // is important since this inference may be processed as a fact.
     397                 :         88 :     eq = rewrite(eq);
     398         [ +  + ]:         88 :     iid = checkInv ? InferenceId::STRINGS_ARRAY_UPDATE_CONCAT_INVERSE
     399                 :            :                    : InferenceId::STRINGS_ARRAY_UPDATE_CONCAT;
     400                 :         88 :   }
     401                 :            :   else
     402                 :            :   {
     403                 :        390 :     std::reverse(cchildren.begin(), cchildren.end());
     404                 :        390 :     std::reverse(cond.begin(), cond.end());
     405                 :        390 :     eq = t.eqNode(cchildren[0]);
     406         [ +  + ]:        920 :     for (size_t i = 1, ncond = cond.size(); i < ncond; i++)
     407                 :            :     {
     408                 :        530 :       eq = nm->mkNode(Kind::ITE, cond[i], t.eqNode(cchildren[i]), eq);
     409                 :            :     }
     410                 :            :     Node inBoundsCond =
     411                 :        780 :         nm->mkNode(Kind::AND, nm->mkNode(Kind::GEQ, t[1], d_zero), cond[0]);
     412                 :        390 :     exp.push_back(inBoundsCond);
     413                 :        390 :     nexp.push_back(inBoundsCond);
     414                 :        390 :     iid = InferenceId::STRINGS_ARRAY_NTH_CONCAT;
     415                 :        390 :   }
     416         [ +  + ]:        478 :   if (checkInv)
     417                 :            :   {
     418                 :         45 :     NormalForm& nfSelf = d_csolver.getNormalForm(rself);
     419                 :         45 :     exp.insert(exp.end(), nfSelf.d_exp.begin(), nfSelf.d_exp.end());
     420                 :         45 :     d_im.addToExplanation(t, nfSelf.d_base, exp);
     421                 :            :   }
     422                 :            :   else
     423                 :            :   {
     424                 :        433 :     exp.insert(exp.end(), nf.d_exp.begin(), nf.d_exp.end());
     425                 :        433 :     d_im.addToExplanation(t[0], nf.d_base, exp);
     426                 :            :   }
     427         [ +  + ]:        478 :   if (d_eqProc.find(eq) == d_eqProc.end())
     428                 :            :   {
     429                 :        349 :     d_eqProc.insert(eq);
     430         [ +  - ]:        349 :     Trace("seq-array") << "- send lemma - " << eq << std::endl;
     431                 :        349 :     d_im.sendInference(exp, nexp, eq, iid);
     432                 :            :   }
     433 [ +  + ][ +  + ]:       7900 : }
                 [ +  + ]
     434                 :            : 
     435                 :        100 : const std::map<Node, Node>& ArraySolver::getWriteModel(Node eqc)
     436                 :            : {
     437                 :        100 :   return d_coreSolver.getWriteModel(eqc);
     438                 :            : }
     439                 :            : 
     440                 :         62 : const std::map<Node, Node>& ArraySolver::getConnectedSequences()
     441                 :            : {
     442                 :         62 :   return d_coreSolver.getConnectedSequences();
     443                 :            : }
     444                 :            : 
     445                 :            : }  // namespace strings
     446                 :            : }  // namespace theory
     447                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14