GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/theory_strings.cpp Lines: 481 586 82.1 %
Date: 2021-11-07 Branches: 1018 2445 41.6 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Tianyi Liang, Andres Noetzli
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * Implementation of the theory of strings.
14
 */
15
16
#include "theory/strings/theory_strings.h"
17
18
#include "expr/attribute.h"
19
#include "expr/bound_var_manager.h"
20
#include "expr/kind.h"
21
#include "expr/sequence.h"
22
#include "options/smt_options.h"
23
#include "options/strings_options.h"
24
#include "options/theory_options.h"
25
#include "smt/logic_exception.h"
26
#include "theory/decision_manager.h"
27
#include "theory/ext_theory.h"
28
#include "theory/rewriter.h"
29
#include "theory/strings/theory_strings_utils.h"
30
#include "theory/strings/type_enumerator.h"
31
#include "theory/strings/word.h"
32
#include "theory/theory_model.h"
33
#include "theory/valuation.h"
34
35
using namespace std;
36
using namespace cvc5::context;
37
using namespace cvc5::kind;
38
39
namespace cvc5 {
40
namespace theory {
41
namespace strings {
42
43
/**
44
 * Attribute used for making unique (bound variables) which correspond to
45
 * unique element values used in sequence models. See use in collectModelValues
46
 * below.
47
 */
48
struct SeqModelVarAttributeId
49
{
50
};
51
using SeqModelVarAttribute = expr::Attribute<SeqModelVarAttributeId, Node>;
52
53
15273
TheoryStrings::TheoryStrings(Env& env, OutputChannel& out, Valuation valuation)
54
    : Theory(THEORY_STRINGS, env, out, valuation),
55
      d_notify(*this),
56
      d_statistics(),
57
      d_state(env, d_valuation),
58
      d_termReg(env, d_state, d_statistics, d_pnm),
59
      d_rewriter(env.getRewriter(),
60
                 &d_statistics.d_rewrites,
61
                 d_termReg.getAlphabetCardinality()),
62
      d_eagerSolver(env, d_state, d_termReg, d_rewriter.getArithEntail()),
63
      d_extTheoryCb(),
64
      d_im(env, *this, d_state, d_termReg, d_extTheory, d_statistics),
65
      d_extTheory(env, d_extTheoryCb, d_im),
66
      // the checker depends on the cardinality of the alphabet
67
      d_checker(d_termReg.getAlphabetCardinality()),
68
      d_bsolver(env, d_state, d_im),
69
      d_csolver(env, d_state, d_im, d_termReg, d_bsolver),
70
      d_esolver(env,
71
                d_state,
72
                d_im,
73
                d_termReg,
74
                d_rewriter,
75
                d_bsolver,
76
                d_csolver,
77
                d_extTheory,
78
                d_statistics),
79
      d_rsolver(
80
          env, d_state, d_im, d_termReg, d_csolver, d_esolver, d_statistics),
81
30546
      d_regexp_elim(options::regExpElimAgg(), d_pnm, userContext()),
82
45819
      d_stringsFmf(env, valuation, d_termReg)
83
{
84
15273
  d_termReg.finishInit(&d_im);
85
86
15273
  d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) );
87
15273
  d_one = NodeManager::currentNM()->mkConst( Rational( 1 ) );
88
15273
  d_neg_one = NodeManager::currentNM()->mkConst(Rational(-1));
89
15273
  d_true = NodeManager::currentNM()->mkConst( true );
90
15273
  d_false = NodeManager::currentNM()->mkConst( false );
91
92
  // set up the extended function callback
93
15273
  d_extTheoryCb.d_esolver = &d_esolver;
94
95
  // use the state object as the official theory state
96
15273
  d_theoryState = &d_state;
97
  // use the inference manager as the official inference manager
98
15273
  d_inferManager = &d_im;
99
15273
}
100
101
30536
TheoryStrings::~TheoryStrings() {
102
103
30536
}
104
105
15273
TheoryRewriter* TheoryStrings::getTheoryRewriter() { return &d_rewriter; }
106
107
7989
ProofRuleChecker* TheoryStrings::getProofChecker() { return &d_checker; }
108
109
15273
bool TheoryStrings::needsEqualityEngine(EeSetupInfo& esi)
110
{
111
15273
  esi.d_notify = &d_notify;
112
15273
  esi.d_name = "theory::strings::ee";
113
15273
  esi.d_notifyNewClass = true;
114
15273
  esi.d_notifyMerge = true;
115
15273
  esi.d_notifyDisequal = true;
116
15273
  return true;
117
}
118
119
15273
void TheoryStrings::finishInit()
120
{
121
15273
  Assert(d_equalityEngine != nullptr);
122
123
  // witness is used to eliminate str.from_code
124
15273
  d_valuation.setUnevaluatedKind(WITNESS);
125
126
15273
  bool eagerEval = options::stringEagerEval();
127
  // The kinds we are treating as function application in congruence
128
15273
  d_equalityEngine->addFunctionKind(kind::STRING_LENGTH, eagerEval);
129
15273
  d_equalityEngine->addFunctionKind(kind::STRING_CONCAT, eagerEval);
130
15273
  d_equalityEngine->addFunctionKind(kind::STRING_IN_REGEXP, eagerEval);
131
15273
  d_equalityEngine->addFunctionKind(kind::STRING_TO_CODE, eagerEval);
132
15273
  d_equalityEngine->addFunctionKind(kind::SEQ_UNIT, eagerEval);
133
  // `seq.nth` is not always defined, and so we do not evaluate it eagerly.
134
15273
  d_equalityEngine->addFunctionKind(kind::SEQ_NTH, false);
135
  // extended functions
136
15273
  d_equalityEngine->addFunctionKind(kind::STRING_CONTAINS, eagerEval);
137
15273
  d_equalityEngine->addFunctionKind(kind::STRING_LEQ, eagerEval);
138
15273
  d_equalityEngine->addFunctionKind(kind::STRING_SUBSTR, eagerEval);
139
15273
  d_equalityEngine->addFunctionKind(kind::STRING_UPDATE, eagerEval);
140
15273
  d_equalityEngine->addFunctionKind(kind::STRING_ITOS, eagerEval);
141
15273
  d_equalityEngine->addFunctionKind(kind::STRING_STOI, eagerEval);
142
15273
  d_equalityEngine->addFunctionKind(kind::STRING_INDEXOF, eagerEval);
143
15273
  d_equalityEngine->addFunctionKind(kind::STRING_INDEXOF_RE, eagerEval);
144
15273
  d_equalityEngine->addFunctionKind(kind::STRING_REPLACE, eagerEval);
145
15273
  d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_ALL, eagerEval);
146
15273
  d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_RE, eagerEval);
147
15273
  d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_RE_ALL, eagerEval);
148
15273
  d_equalityEngine->addFunctionKind(kind::STRING_REPLACE_ALL, eagerEval);
149
15273
  d_equalityEngine->addFunctionKind(kind::STRING_TOLOWER, eagerEval);
150
15273
  d_equalityEngine->addFunctionKind(kind::STRING_TOUPPER, eagerEval);
151
15273
  d_equalityEngine->addFunctionKind(kind::STRING_REV, eagerEval);
152
15273
}
153
154
std::string TheoryStrings::identify() const
155
{
156
  return std::string("TheoryStrings");
157
}
158
159
7327
bool TheoryStrings::areCareDisequal( TNode x, TNode y ) {
160
7327
  Assert(d_equalityEngine->hasTerm(x));
161
7327
  Assert(d_equalityEngine->hasTerm(y));
162
21981
  if (d_equalityEngine->isTriggerTerm(x, THEORY_STRINGS)
163
21981
      && d_equalityEngine->isTriggerTerm(y, THEORY_STRINGS))
164
  {
165
    TNode x_shared =
166
8855
        d_equalityEngine->getTriggerTermRepresentative(x, THEORY_STRINGS);
167
    TNode y_shared =
168
8855
        d_equalityEngine->getTriggerTermRepresentative(y, THEORY_STRINGS);
169
5857
    EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared);
170
5857
    if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){
171
2859
      return true;
172
    }
173
  }
174
4468
  return false;
175
}
176
177
1076084
bool TheoryStrings::propagateLit(TNode literal)
178
{
179
1076084
  return d_im.propagateLit(literal);
180
}
181
182
46627
TrustNode TheoryStrings::explain(TNode literal)
183
{
184
46627
  Debug("strings-explain") << "explain called on " << literal << std::endl;
185
46627
  return d_im.explainLit(literal);
186
}
187
188
20560
void TheoryStrings::presolve() {
189
20560
  Debug("strings-presolve") << "TheoryStrings::Presolving : get fmf options " << (options::stringFMF() ? "true" : "false") << std::endl;
190
20560
  d_strat.initializeStrategy();
191
192
  // if strings fmf is enabled, register the strategy
193
20560
  if (options::stringFMF())
194
  {
195
78
    d_stringsFmf.presolve();
196
    // This strategy is local to a check-sat call, since we refresh the strategy
197
    // on every call to presolve.
198
78
    d_im.getDecisionManager()->registerStrategy(
199
        DecisionManager::STRAT_STRINGS_SUM_LENGTHS,
200
        d_stringsFmf.getDecisionStrategy(),
201
        DecisionManager::STRAT_SCOPE_LOCAL_SOLVE);
202
  }
203
20560
  Debug("strings-presolve") << "Finished presolve" << std::endl;
204
20560
}
205
206
207
/////////////////////////////////////////////////////////////////////////////
208
// MODEL GENERATION
209
/////////////////////////////////////////////////////////////////////////////
210
211
7594
bool TheoryStrings::collectModelValues(TheoryModel* m,
212
                                       const std::set<Node>& termSet)
213
{
214
7594
  if (Trace.isOn("strings-debug-model"))
215
  {
216
    Trace("strings-debug-model")
217
        << "TheoryStrings::collectModelValues" << std::endl;
218
    Trace("strings-debug-model") << "Equivalence classes are:" << std::endl;
219
    Trace("strings-debug-model") << debugPrintStringsEqc() << std::endl;
220
    Trace("strings-debug-model") << "Extended functions are:" << std::endl;
221
    Trace("strings-debug-model") << d_esolver.debugPrintModel() << std::endl;
222
  }
223
7594
  Trace("strings-model") << "TheoryStrings::collectModelValues" << std::endl;
224
  // Collects representatives by types and orders sequence types by how nested
225
  // they are
226
15188
  std::map<TypeNode, std::unordered_set<Node> > repSet;
227
15188
  std::unordered_set<TypeNode> toProcess;
228
  // Generate model
229
  // get the relevant string equivalence classes
230
105758
  for (const Node& s : termSet)
231
  {
232
196328
    TypeNode tn = s.getType();
233
98164
    if (tn.isStringLike())
234
    {
235
96266
      Node r = d_state.getRepresentative(s);
236
48133
      repSet[tn].insert(r);
237
48133
      toProcess.insert(tn);
238
    }
239
  }
240
241
9852
  while (!toProcess.empty())
242
  {
243
    // Pick one of the remaining types to collect model values for
244
2260
    TypeNode tn = *toProcess.begin();
245
1131
    if (!collectModelInfoType(tn, toProcess, repSet, m))
246
    {
247
      return false;
248
    }
249
  }
250
251
7593
  return true;
252
}
253
254
1130
bool TheoryStrings::collectModelInfoType(
255
    TypeNode tn,
256
    std::unordered_set<TypeNode>& toProcess,
257
    const std::map<TypeNode, std::unordered_set<Node> >& repSet,
258
    TheoryModel* m)
259
{
260
  // Make sure that the model values for the element type of sequences are
261
  // computed first
262
1130
  if (tn.isSequence() && tn.getSequenceElementType().isSequence())
263
  {
264
6
    TypeNode tnElem = tn.getSequenceElementType();
265
6
    if (toProcess.find(tnElem) != toProcess.end()
266
6
        && !collectModelInfoType(tnElem, toProcess, repSet, m))
267
    {
268
      return false;
269
    }
270
  }
271
1130
  toProcess.erase(tn);
272
273
  // get partition of strings of equal lengths for the representatives of the
274
  // current type
275
2260
  std::map<TypeNode, std::vector<std::vector<Node> > > colT;
276
2260
  std::map<TypeNode, std::vector<Node> > ltsT;
277
2260
  const std::vector<Node> repVec(repSet.at(tn).begin(), repSet.at(tn).end());
278
1130
  d_state.separateByLength(repVec, colT, ltsT);
279
1130
  const std::vector<std::vector<Node> >& col = colT[tn];
280
1130
  const std::vector<Node>& lts = ltsT[tn];
281
282
1130
  NodeManager* nm = NodeManager::currentNM();
283
2260
  std::map< Node, Node > processed;
284
  //step 1 : get all values for known lengths
285
2260
  std::vector< Node > lts_values;
286
2260
  std::map<std::size_t, Node> values_used;
287
2260
  std::vector<Node> len_splits;
288
7390
  for( unsigned i=0; i<col.size(); i++ ) {
289
6261
    Trace("strings-model") << "Checking length for {";
290
16447
    for( unsigned j=0; j<col[i].size(); j++ ) {
291
10186
      if( j>0 ) {
292
3925
        Trace("strings-model") << ", ";
293
      }
294
10186
      Trace("strings-model") << col[i][j];
295
    }
296
6261
    Trace("strings-model") << " } (length is " << lts[i] << ")" << std::endl;
297
12522
    Node len_value;
298
6261
    if( lts[i].isConst() ) {
299
4671
      len_value = lts[i];
300
    }
301
1590
    else if (!lts[i].isNull())
302
    {
303
      // get the model value for lts[i]
304
1590
      len_value = d_valuation.getModelValue(lts[i]);
305
    }
306
6261
    if (len_value.isNull())
307
    {
308
      lts_values.push_back(Node::null());
309
    }
310
    else
311
    {
312
      // must throw logic exception if we cannot construct the string
313
6261
      if (len_value.getConst<Rational>() > String::maxSize())
314
      {
315
2
        std::stringstream ss;
316
2
        ss << "The model was computed to have strings of length " << len_value
317
1
           << ". We only allow strings up to length " << String::maxSize();
318
1
        throw LogicException(ss.str());
319
      }
320
      std::size_t lvalue =
321
6260
          len_value.getConst<Rational>().getNumerator().toUnsignedInt();
322
6260
      auto itvu = values_used.find(lvalue);
323
6260
      if (itvu == values_used.end())
324
      {
325
5128
        values_used[lvalue] = lts[i];
326
      }
327
      else
328
      {
329
1132
        len_splits.push_back(lts[i].eqNode(itvu->second));
330
      }
331
6260
      lts_values.push_back(len_value);
332
    }
333
  }
334
  ////step 2 : assign arbitrary values for unknown lengths?
335
  // confirmed by calculus invariant, see paper
336
1129
  Trace("strings-model") << "Assign to equivalence classes..." << std::endl;
337
2258
  std::map<Node, Node> pure_eq_assign;
338
  //step 3 : assign values to equivalence classes that are pure variables
339
7388
  for( unsigned i=0; i<col.size(); i++ ){
340
12518
    std::vector< Node > pure_eq;
341
12518
    Trace("strings-model") << "The (" << col[i].size()
342
6259
                           << ") equivalence classes ";
343
16443
    for (const Node& eqc : col[i])
344
    {
345
10184
      Trace("strings-model") << eqc << " ";
346
      //check if col[i][j] has only variables
347
10184
      if (!eqc.isConst())
348
      {
349
3137
        NormalForm& nfe = d_csolver.getNormalForm(eqc);
350
3137
        if (nfe.d_nf.size() == 1)
351
        {
352
          // is it an equivalence class with a seq.unit term?
353
1011
          if (nfe.d_nf[0].getKind() == SEQ_UNIT)
354
          {
355
30
            Node argVal;
356
15
            if (nfe.d_nf[0][0].getType().isStringLike())
357
            {
358
              // By this point, we should have assigned model values for the
359
              // elements of this sequence type because of the check in the
360
              // beginning of this method
361
3
              argVal = m->getRepresentative(nfe.d_nf[0][0]);
362
            }
363
            else
364
            {
365
              // Otherwise, we use the term itself. We cannot get the model
366
              // value of this term, since it might not be available yet, as
367
              // it may belong to a theory that has not built its model yet.
368
              // Hence, we assign a (non-constant) skeleton (seq.unit argVal).
369
12
              argVal = nfe.d_nf[0][0];
370
            }
371
15
            Assert(!argVal.isNull()) << "No value for " << nfe.d_nf[0][0];
372
30
            Node c = rewrite(nm->mkNode(SEQ_UNIT, argVal));
373
15
            pure_eq_assign[eqc] = c;
374
15
            Trace("strings-model") << "(unit: " << nfe.d_nf[0] << ") ";
375
15
            m->getEqualityEngine()->addTerm(c);
376
          }
377
          // does it have a code and the length of these equivalence classes are
378
          // one?
379
996
          else if (d_termReg.hasStringCode() && lts_values[i] == d_one)
380
          {
381
325
            EqcInfo* eip = d_state.getOrMakeEqcInfo(eqc, false);
382
325
            if (eip && !eip->d_codeTerm.get().isNull())
383
            {
384
              // its value must be equal to its code
385
372
              Node ct = nm->mkNode(kind::STRING_TO_CODE, eip->d_codeTerm.get());
386
372
              Node ctv = d_valuation.getModelValue(ct);
387
              unsigned cvalue =
388
186
                  ctv.getConst<Rational>().getNumerator().toUnsignedInt();
389
186
              Trace("strings-model") << "(code: " << cvalue << ") ";
390
372
              std::vector<unsigned> vec;
391
186
              vec.push_back(cvalue);
392
372
              Node mv = nm->mkConst(String(vec));
393
186
              pure_eq_assign[eqc] = mv;
394
186
              m->getEqualityEngine()->addTerm(mv);
395
            }
396
          }
397
1011
          pure_eq.push_back(eqc);
398
        }
399
      }
400
      else
401
      {
402
7047
        processed[eqc] = eqc;
403
        // Make sure that constants are asserted to the theory model that we
404
        // are building. It is possible that new constants were introduced by
405
        // the eager evaluation in the equality engine. These terms are missing
406
        // in the term set and, as a result, are skipped when the equality
407
        // engine is asserted to the theory model.
408
7047
        m->getEqualityEngine()->addTerm(eqc);
409
      }
410
    }
411
6259
    Trace("strings-model") << "have length " << lts_values[i] << std::endl;
412
413
    //assign a new length if necessary
414
6259
    if( !pure_eq.empty() ){
415
712
      if( lts_values[i].isNull() ){
416
        // start with length two (other lengths have special precendence)
417
        std::size_t lvalue = 2;
418
        while( values_used.find( lvalue )!=values_used.end() ){
419
          lvalue++;
420
        }
421
        Trace("strings-model") << "*** Decide to make length of " << lvalue << std::endl;
422
        lts_values[i] = nm->mkConst(Rational(lvalue));
423
        values_used[lvalue] = Node::null();
424
      }
425
712
      Trace("strings-model") << "Need to assign values of length " << lts_values[i] << " to equivalence classes ";
426
1723
      for( unsigned j=0; j<pure_eq.size(); j++ ){
427
1011
        Trace("strings-model") << pure_eq[j] << " ";
428
      }
429
712
      Trace("strings-model") << std::endl;
430
431
      //use type enumerator
432
712
      Assert(lts_values[i].getConst<Rational>() <= Rational(String::maxSize()))
433
          << "Exceeded UINT32_MAX in string model";
434
      uint32_t currLen =
435
712
          lts_values[i].getConst<Rational>().getNumerator().toUnsignedInt();
436
1424
      std::unique_ptr<SEnumLen> sel;
437
1424
      Trace("strings-model") << "Cardinality of alphabet is "
438
712
                             << d_termReg.getAlphabetCardinality() << std::endl;
439
712
      if (tn.isString())  // string-only
440
      {
441
691
        sel.reset(new StringEnumLen(
442
691
            currLen, currLen, d_termReg.getAlphabetCardinality()));
443
      }
444
      else
445
      {
446
21
        sel.reset(new SeqEnumLen(tn, nullptr, currLen, currLen));
447
      }
448
1723
      for (const Node& eqc : pure_eq)
449
      {
450
2022
        Node c;
451
1011
        std::map<Node, Node>::iterator itp = pure_eq_assign.find(eqc);
452
1011
        if (itp == pure_eq_assign.end())
453
        {
454
704
          do
455
          {
456
1514
            if (sel->isFinished())
457
            {
458
              // We are in a case where model construction is impossible due to
459
              // an insufficient number of constants of a given length.
460
461
              // Consider an integer equivalence class E whose value is assigned
462
              // n in the model. Let { S_1, ..., S_m } be the set of string
463
              // equivalence classes such that len( x ) is a member of E for
464
              // some member x of each class S1, ...,Sm. Since our calculus is
465
              // saturated with respect to cardinality inference (see Liang
466
              // et al, Figure 6, CAV 2014), we have that m <= A^n, where A is
467
              // the cardinality of our alphabet.
468
469
              // Now, consider the case where there exists two integer
470
              // equivalence classes E1 and E2 that are assigned n, and moreover
471
              // we did not received notification from arithmetic that E1 = E2.
472
              // This typically should never happen, but assume in the following
473
              // that it does.
474
475
              // Now, it may be the case that there are string equivalence
476
              // classes { S_1, ..., S_m1 } whose lengths are in E1,
477
              // and classes { S'_1, ..., S'_m2 } whose lengths are in E2, where
478
              // m1 + m2 > A^n. In this case, we have insufficient strings to
479
              // assign to { S_1, ..., S_m1, S'_1, ..., S'_m2 }. If this
480
              // happens, we add a split on len( u1 ) = len( u2 ) for some
481
              // len( u1 ) in E1, len( u2 ) in E2. We do this for each pair of
482
              // integer equivalence classes that are assigned to the same value
483
              // in the model.
484
              AlwaysAssert(!len_splits.empty());
485
              for (const Node& sl : len_splits)
486
              {
487
                Node spl = nm->mkNode(OR, sl, sl.negate());
488
                d_im.lemma(spl, InferenceId::STRINGS_CMI_SPLIT);
489
                Trace("strings-lemma")
490
                    << "Strings::CollectModelInfoSplit: " << spl << std::endl;
491
              }
492
              // we added a lemma, so can return here
493
              return false;
494
            }
495
1514
            c = sel->getCurrent();
496
            // if we are a sequence with infinite element type
497
3028
            if (tn.isSequence()
498
3028
                && !d_env.isFiniteType(tn.getSequenceElementType()))
499
            {
500
              // Make a skeleton instead. In particular, this means that
501
              // a value:
502
              //   (seq.++ (seq.unit 0) (seq.unit 1) (seq.unit 2))
503
              // becomes:
504
              //   (seq.++ (seq.unit k_0) (seq.unit k_1) (seq.unit k_2))
505
              // where k_0, k_1, k_2 are fresh integer variables. These
506
              // variables will be assigned values in the standard way by the
507
              // model. This construction is necessary since the strings solver
508
              // must constrain the length of the model of an equivalence class
509
              // (e.g. in this case to length 3); moreover we cannot assign a
510
              // concrete value since it may conflict with other skeletons we
511
              // have assigned, e.g. for the case of (seq.unit argVal) above.
512
12
              SkolemManager* sm = nm->getSkolemManager();
513
12
              BoundVarManager* bvm = nm->getBoundVarManager();
514
12
              Assert(c.getKind() == CONST_SEQUENCE);
515
12
              const Sequence& sn = c.getConst<Sequence>();
516
12
              const std::vector<Node>& snvec = sn.getVec();
517
24
              std::vector<Node> skChildren;
518
39
              for (const Node& snv : snvec)
519
              {
520
54
                TypeNode etn = snv.getType();
521
54
                Node v = bvm->mkBoundVar<SeqModelVarAttribute>(snv, etn);
522
                // use a skolem, not a bound variable
523
54
                Node kv = sm->mkPurifySkolem(v, "smv");
524
27
                skChildren.push_back(nm->mkNode(SEQ_UNIT, kv));
525
              }
526
12
              c = utils::mkConcat(skChildren, tn);
527
            }
528
            // increment
529
1514
            sel->increment();
530
1514
          } while (m->hasTerm(c));
531
        }
532
        else
533
        {
534
201
          c = itp->second;
535
        }
536
2022
        Trace("strings-model") << "*** Assigned constant " << c << " for "
537
1011
                               << eqc << std::endl;
538
1011
        processed[eqc] = c;
539
1011
        if (!m->assertEquality(eqc, c, true))
540
        {
541
          // this should never happen due to the model soundness argument
542
          // for strings
543
          Unreachable()
544
              << "TheoryStrings::collectModelInfoType: Inconsistent equality"
545
              << std::endl;
546
          return false;
547
        }
548
      }
549
    }
550
  }
551
1129
  Trace("strings-model") << "String Model : Pure Assigned." << std::endl;
552
  //step 4 : assign constants to all other equivalence classes
553
11313
  for (const Node& rn : repVec)
554
  {
555
10184
    if (processed.find(rn) == processed.end())
556
    {
557
2126
      NormalForm& nf = d_csolver.getNormalForm(rn);
558
2126
      if (Trace.isOn("strings-model"))
559
      {
560
        Trace("strings-model")
561
            << "Construct model for " << rn << " based on normal form ";
562
        for (unsigned j = 0, size = nf.d_nf.size(); j < size; j++)
563
        {
564
          Node n = nf.d_nf[j];
565
          if (j > 0)
566
          {
567
            Trace("strings-model") << " ++ ";
568
          }
569
          Trace("strings-model") << n;
570
          Node r = d_state.getRepresentative(n);
571
          if (!r.isConst() && processed.find(r) == processed.end())
572
          {
573
            Trace("strings-model") << "(UNPROCESSED)";
574
          }
575
        }
576
      }
577
2126
      Trace("strings-model") << std::endl;
578
4252
      std::vector< Node > nc;
579
10007
      for (const Node& n : nf.d_nf)
580
      {
581
15762
        Node r = d_state.getRepresentative(n);
582
7881
        Assert(r.isConst() || processed.find(r) != processed.end());
583
7881
        nc.push_back(r.isConst() ? r : processed[r]);
584
      }
585
4252
      Node cc = utils::mkNConcat(nc, tn);
586
4252
      Trace("strings-model")
587
2126
          << "*** Determined constant " << cc << " for " << rn << std::endl;
588
2126
      processed[rn] = cc;
589
2126
      if (!m->assertEquality(rn, cc, true))
590
      {
591
        // this should never happen due to the model soundness argument
592
        // for strings
593
        Unreachable() << "TheoryStrings::collectModelInfoType: "
594
                         "Inconsistent equality (unprocessed eqc)"
595
                      << std::endl;
596
        return false;
597
      }
598
2126
      else if (!cc.isConst())
599
      {
600
        // the value may be specified by seq.unit components, ensure this
601
        // is marked as the skeleton for constructing values in this class.
602
4
        m->assertSkeleton(cc);
603
      }
604
    }
605
  }
606
  //Trace("strings-model") << "String Model : Assigned." << std::endl;
607
1129
  Trace("strings-model") << "String Model : Finished." << std::endl;
608
1129
  return true;
609
}
610
611
/////////////////////////////////////////////////////////////////////////////
612
// MAIN SOLVER
613
/////////////////////////////////////////////////////////////////////////////
614
615
188342
void TheoryStrings::preRegisterTerm(TNode n)
616
{
617
376684
  Trace("strings-preregister")
618
188342
      << "TheoryStrings::preRegisterTerm: " << n << std::endl;
619
188342
  d_termReg.preRegisterTerm(n);
620
  // Register the term with the extended theory. Notice we do not recurse on
621
  // this term here since preRegisterTerm is already called recursively on all
622
  // subterms in preregistered literals.
623
188342
  d_extTheory.registerTerm(n);
624
188342
}
625
626
1074645
bool TheoryStrings::preNotifyFact(
627
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
628
{
629
  // this is only required for internal facts, others are already registered
630
1074645
  if (isInternal && atom.getKind() == EQUAL)
631
  {
632
    // We must ensure these terms are registered. We register eagerly here for
633
    // performance reasons. Alternatively, terms could be registered at full
634
    // effort in e.g. BaseSolver::init.
635
111789
    for (const Node& t : atom)
636
    {
637
74526
      d_termReg.registerTerm(t, 0);
638
    }
639
  }
640
1074645
  return false;
641
}
642
643
1074645
void TheoryStrings::notifyFact(TNode atom,
644
                               bool polarity,
645
                               TNode fact,
646
                               bool isInternal)
647
{
648
1074645
  d_eagerSolver.notifyFact(atom, polarity, fact, isInternal);
649
  // process pending conflicts due to reasoning about endpoints
650
1074645
  if (!d_state.isInConflict() && d_state.hasPendingConflict())
651
  {
652
1234
    InferInfo iiPendingConf(InferenceId::UNKNOWN);
653
617
    d_state.getPendingConflict(iiPendingConf);
654
1234
    Trace("strings-pending")
655
617
        << "Process pending conflict " << iiPendingConf.d_premises << std::endl;
656
1234
    Trace("strings-conflict")
657
617
        << "CONFLICT: Eager : " << iiPendingConf.d_premises << std::endl;
658
617
    ++(d_statistics.d_conflictsEager);
659
    // call the inference manager to send the conflict
660
617
    d_im.processConflict(iiPendingConf);
661
617
    return;
662
  }
663
1074028
  Trace("strings-pending-debug") << "  Now collect terms" << std::endl;
664
1074028
  Trace("strings-pending-debug") << "  Finished collect terms" << std::endl;
665
}
666
667
444521
void TheoryStrings::postCheck(Effort e)
668
{
669
444521
  d_im.doPendingFacts();
670
671
444521
  Assert(d_strat.isStrategyInit());
672
1331159
  if (!d_state.isInConflict() && !d_valuation.needCheck()
673
553020
      && d_strat.hasStrategyEffort(e))
674
  {
675
57246
    Trace("strings-check-debug")
676
28623
        << "Theory of strings " << e << " effort check " << std::endl;
677
28623
    if (Trace.isOn("strings-eqc"))
678
    {
679
      Trace("strings-eqc") << debugPrintStringsEqc() << std::endl;
680
    }
681
28623
    ++(d_statistics.d_checkRuns);
682
28623
    bool sentLemma = false;
683
28623
    bool hadPending = false;
684
28623
    Trace("strings-check") << "Full effort check..." << std::endl;
685
15500
    do{
686
44123
      d_im.reset();
687
44123
      ++(d_statistics.d_strategyRuns);
688
44123
      Trace("strings-check") << "  * Run strategy..." << std::endl;
689
44123
      runStrategy(e);
690
      // remember if we had pending facts or lemmas
691
44123
      hadPending = d_im.hasPending();
692
      // Send the facts *and* the lemmas. We send lemmas regardless of whether
693
      // we send facts since some lemmas cannot be dropped. Other lemmas are
694
      // otherwise avoided by aborting the strategy when a fact is ready.
695
44123
      d_im.doPending();
696
      // Did we successfully send a lemma? Notice that if hasPending = true
697
      // and sentLemma = false, then the above call may have:
698
      // (1) had no pending lemmas, but successfully processed pending facts,
699
      // (2) unsuccessfully processed pending lemmas.
700
      // In either case, we repeat the strategy if we are not in conflict.
701
44123
      sentLemma = d_im.hasSentLemma();
702
44123
      if (Trace.isOn("strings-check"))
703
      {
704
        Trace("strings-check") << "  ...finish run strategy: ";
705
        Trace("strings-check") << (hadPending ? "hadPending " : "");
706
        Trace("strings-check") << (sentLemma ? "sentLemma " : "");
707
        Trace("strings-check") << (d_state.isInConflict() ? "conflict " : "");
708
        if (!hadPending && !sentLemma && !d_state.isInConflict())
709
        {
710
          Trace("strings-check") << "(none)";
711
        }
712
        Trace("strings-check") << std::endl;
713
      }
714
      // repeat if we did not add a lemma or conflict, and we had pending
715
      // facts or lemmas.
716
44123
    } while (!d_state.isInConflict() && !sentLemma && hadPending);
717
  }
718
444521
  Trace("strings-check") << "Theory of strings, done check : " << e << std::endl;
719
444521
  Assert(!d_im.hasPendingFact());
720
444521
  Assert(!d_im.hasPendingLemma());
721
444521
}
722
723
8768
bool TheoryStrings::needsCheckLastEffort() {
724
8768
  if( options::stringGuessModel() ){
725
    return d_esolver.hasExtendedFunctions();
726
  }
727
8768
  return false;
728
}
729
730
/** Conflict when merging two constants */
731
1143
void TheoryStrings::conflict(TNode a, TNode b){
732
1143
  if (d_state.isInConflict())
733
  {
734
    // already in conflict
735
    return;
736
  }
737
1143
  d_im.conflictEqConstantMerge(a, b);
738
1143
  Trace("strings-conflict") << "CONFLICT: Eq engine conflict" << std::endl;
739
1143
  ++(d_statistics.d_conflictsEqEngine);
740
}
741
742
130768
void TheoryStrings::eqNotifyNewClass(TNode t){
743
130768
  Kind k = t.getKind();
744
130768
  if (k == STRING_LENGTH || k == STRING_TO_CODE)
745
  {
746
38458
    Trace("strings-debug") << "New length eqc : " << t << std::endl;
747
    //we care about the length of this string
748
38458
    d_termReg.registerTerm(t[0], 1);
749
  }
750
130768
  d_eagerSolver.eqNotifyNewClass(t);
751
130768
}
752
753
14004
void TheoryStrings::addCarePairs(TNodeTrie* t1,
754
                                 TNodeTrie* t2,
755
                                 unsigned arity,
756
                                 unsigned depth)
757
{
758
14004
  if( depth==arity ){
759
1300
    if( t2!=NULL ){
760
2600
      Node f1 = t1->getData();
761
2600
      Node f2 = t2->getData();
762
1300
      if (!d_equalityEngine->areEqual(f1, f2))
763
      {
764
550
        Trace("strings-cg-debug") << "TheoryStrings::computeCareGraph(): checking function " << f1 << " and " << f2 << std::endl;
765
1100
        vector< pair<TNode, TNode> > currentPairs;
766
1678
        for (unsigned k = 0; k < f1.getNumChildren(); ++ k) {
767
2256
          TNode x = f1[k];
768
2256
          TNode y = f2[k];
769
1128
          Assert(d_equalityEngine->hasTerm(x));
770
1128
          Assert(d_equalityEngine->hasTerm(y));
771
1128
          Assert(!d_equalityEngine->areDisequal(x, y, false));
772
1128
          Assert(!areCareDisequal(x, y));
773
1128
          if (!d_equalityEngine->areEqual(x, y))
774
          {
775
1704
            if (d_equalityEngine->isTriggerTerm(x, THEORY_STRINGS)
776
1704
                && d_equalityEngine->isTriggerTerm(y, THEORY_STRINGS))
777
            {
778
222
              TNode x_shared = d_equalityEngine->getTriggerTermRepresentative(
779
444
                  x, THEORY_STRINGS);
780
222
              TNode y_shared = d_equalityEngine->getTriggerTermRepresentative(
781
444
                  y, THEORY_STRINGS);
782
222
              currentPairs.push_back(make_pair(x_shared, y_shared));
783
            }
784
          }
785
        }
786
772
        for (unsigned c = 0; c < currentPairs.size(); ++ c) {
787
222
          Trace("strings-cg-pair") << "TheoryStrings::computeCareGraph(): pair : " << currentPairs[c].first << " " << currentPairs[c].second << std::endl;
788
222
          addCarePair(currentPairs[c].first, currentPairs[c].second);
789
        }
790
      }
791
    }
792
  }else{
793
12704
    if( t2==NULL ){
794
10664
      if( depth<(arity-1) ){
795
        //add care pairs internal to each child
796
10935
        for (std::pair<const TNode, TNodeTrie>& tt : t1->d_data)
797
        {
798
7167
          addCarePairs(&tt.second, nullptr, arity, depth + 1);
799
        }
800
      }
801
      //add care pairs based on each pair of non-disequal arguments
802
35125
      for (std::map<TNode, TNodeTrie>::iterator it = t1->d_data.begin();
803
35125
           it != t1->d_data.end();
804
           ++it)
805
      {
806
24461
        std::map<TNode, TNodeTrie>::iterator it2 = it;
807
24461
        ++it2;
808
208567
        for( ; it2 != t1->d_data.end(); ++it2 ){
809
92053
          if (!d_equalityEngine->areDisequal(it->first, it2->first, false))
810
          {
811
2959
            if( !areCareDisequal(it->first, it2->first) ){
812
1658
              addCarePairs( &it->second, &it2->second, arity, depth+1 );
813
            }
814
          }
815
        }
816
      }
817
    }else{
818
      //add care pairs based on product of indices, non-disequal arguments
819
5166
      for (std::pair<const TNode, TNodeTrie>& tt1 : t1->d_data)
820
      {
821
8922
        for (std::pair<const TNode, TNodeTrie>& tt2 : t2->d_data)
822
        {
823
5796
          if (!d_equalityEngine->areDisequal(tt1.first, tt2.first, false))
824
          {
825
3240
            if (!areCareDisequal(tt1.first, tt2.first))
826
            {
827
1682
              addCarePairs(&tt1.second, &tt2.second, arity, depth + 1);
828
            }
829
          }
830
        }
831
      }
832
    }
833
  }
834
14004
}
835
836
9395
void TheoryStrings::computeCareGraph(){
837
  //computing the care graph here is probably still necessary, due to operators that take non-string arguments  TODO: verify
838
9395
  Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Build term indices..." << std::endl;
839
  // Term index for each (type, operator) pair. We require the operator here
840
  // since operators are polymorphic, taking strings/sequences.
841
18790
  std::map<std::pair<TypeNode, Node>, TNodeTrie> index;
842
18790
  std::map< Node, unsigned > arity;
843
9395
  const context::CDList<TNode>& fterms = d_termReg.getFunctionTerms();
844
9395
  size_t functionTerms = fterms.size();
845
60372
  for (unsigned i = 0; i < functionTerms; ++ i) {
846
101954
    TNode f1 = fterms[i];
847
50977
    Trace("strings-cg") << "...build for " << f1 << std::endl;
848
101954
    Node op = f1.getOperator();
849
101954
    std::vector< TNode > reps;
850
50977
    bool has_trigger_arg = false;
851
116687
    for( unsigned j=0; j<f1.getNumChildren(); j++ ){
852
65710
      reps.push_back(d_equalityEngine->getRepresentative(f1[j]));
853
65710
      if (d_equalityEngine->isTriggerTerm(f1[j], THEORY_STRINGS))
854
      {
855
48904
        has_trigger_arg = true;
856
      }
857
    }
858
50977
    if( has_trigger_arg ){
859
76604
      TypeNode ft = utils::getOwnerStringType(f1);
860
76604
      std::pair<TypeNode, Node> ikey = std::pair<TypeNode, Node>(ft, op);
861
38302
      index[ikey].addTerm(f1, reps);
862
38302
      arity[op] = reps.size();
863
    }
864
  }
865
  //for each index
866
12892
  for (std::pair<const std::pair<TypeNode, Node>, TNodeTrie>& ti : index)
867
  {
868
6994
    Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Process index "
869
3497
                        << ti.first << "..." << std::endl;
870
6994
    Node op = ti.first.second;
871
3497
    addCarePairs(&ti.second, nullptr, arity[op], 0);
872
  }
873
9395
}
874
875
void TheoryStrings::checkRegisterTermsPreNormalForm()
876
{
877
  const std::vector<Node>& seqc = d_bsolver.getStringEqc();
878
  for (const Node& eqc : seqc)
879
  {
880
    eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, d_equalityEngine);
881
    while (!eqc_i.isFinished())
882
    {
883
      Node n = (*eqc_i);
884
      if (!d_bsolver.isCongruent(n))
885
      {
886
        d_termReg.registerTerm(n, 2);
887
      }
888
      ++eqc_i;
889
    }
890
  }
891
}
892
893
13446
void TheoryStrings::checkCodes()
894
{
895
  // ensure that lemmas regarding str.code been added for each constant string
896
  // of length one
897
13446
  if (d_termReg.hasStringCode())
898
  {
899
1636
    NodeManager* nm = NodeManager::currentNM();
900
    // str.code applied to the code term for each equivalence class that has a
901
    // code term but is not a constant
902
3056
    std::vector<Node> nconst_codes;
903
    // str.code applied to the proxy variables for each equivalence classes that
904
    // are constants of size one
905
3056
    std::vector<Node> const_codes;
906
1636
    const std::vector<Node>& seqc = d_bsolver.getStringEqc();
907
22650
    for (const Node& eqc : seqc)
908
    {
909
21014
      NormalForm& nfe = d_csolver.getNormalForm(eqc);
910
21014
      if (nfe.d_nf.size() == 1 && nfe.d_nf[0].isConst())
911
      {
912
15136
        Node c = nfe.d_nf[0];
913
15136
        Trace("strings-code-debug") << "Get proxy variable for " << c
914
7568
                                    << std::endl;
915
15136
        Node cc = nm->mkNode(kind::STRING_TO_CODE, c);
916
7568
        cc = rewrite(cc);
917
7568
        Assert(cc.isConst());
918
15136
        Node cp = d_termReg.ensureProxyVariableFor(c);
919
15136
        Node vc = nm->mkNode(STRING_TO_CODE, cp);
920
7568
        if (!d_state.areEqual(cc, vc))
921
        {
922
1328
          std::vector<Node> emptyVec;
923
664
          d_im.sendInference(emptyVec, cc.eqNode(vc), InferenceId::STRINGS_CODE_PROXY);
924
        }
925
7568
        const_codes.push_back(vc);
926
      }
927
      else
928
      {
929
13446
        EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false);
930
13446
        if (ei && !ei->d_codeTerm.get().isNull())
931
        {
932
7144
          Node vc = nm->mkNode(kind::STRING_TO_CODE, ei->d_codeTerm.get());
933
3572
          nconst_codes.push_back(vc);
934
        }
935
      }
936
    }
937
1636
    if (d_im.hasProcessed())
938
    {
939
216
      return;
940
    }
941
    // now, ensure that str.code is injective
942
2840
    std::vector<Node> cmps;
943
1420
    cmps.insert(cmps.end(), const_codes.rbegin(), const_codes.rend());
944
1420
    cmps.insert(cmps.end(), nconst_codes.rbegin(), nconst_codes.rend());
945
4598
    for (unsigned i = 0, num_ncc = nconst_codes.size(); i < num_ncc; i++)
946
    {
947
6356
      Node c1 = nconst_codes[i];
948
3178
      cmps.pop_back();
949
20035
      for (const Node& c2 : cmps)
950
      {
951
33714
        Trace("strings-code-debug")
952
16857
            << "Compare codes : " << c1 << " " << c2 << std::endl;
953
16857
        if (!d_state.areDisequal(c1, c2) && !d_state.areEqual(c1, d_neg_one))
954
        {
955
2532
          Node eq_no = c1.eqNode(d_neg_one);
956
2532
          Node deq = c1.eqNode(c2).negate();
957
2532
          Node eqn = c1[0].eqNode(c2[0]);
958
          // str.code(x)==-1 V str.code(x)!=str.code(y) V x==y
959
2532
          Node inj_lem = nm->mkNode(kind::OR, eq_no, deq, eqn);
960
1266
          deq = rewrite(deq);
961
1266
          d_im.addPendingPhaseRequirement(deq, false);
962
2532
          std::vector<Node> emptyVec;
963
1266
          d_im.sendInference(emptyVec, inj_lem, InferenceId::STRINGS_CODE_INJ);
964
        }
965
      }
966
    }
967
  }
968
}
969
970
void TheoryStrings::checkRegisterTermsNormalForms()
971
{
972
  const std::vector<Node>& seqc = d_bsolver.getStringEqc();
973
  for (const Node& eqc : seqc)
974
  {
975
    NormalForm& nfi = d_csolver.getNormalForm(eqc);
976
    // check if there is a length term for this equivalence class
977
    EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false);
978
    Node lt = ei ? ei->d_lengthTerm : Node::null();
979
    if (lt.isNull())
980
    {
981
      Node c = utils::mkNConcat(nfi.d_nf, eqc.getType());
982
      d_termReg.registerTerm(c, 3);
983
    }
984
  }
985
}
986
987
126799
TrustNode TheoryStrings::ppRewrite(TNode atom, std::vector<SkolemLemma>& lems)
988
{
989
126799
  Trace("strings-ppr") << "TheoryStrings::ppRewrite " << atom << std::endl;
990
126799
  if (atom.getKind() == EQUAL)
991
  {
992
    // always apply aggressive equality rewrites here
993
53791
    Node ret = d_rewriter.rewriteEqualityExt(atom);
994
26964
    if (ret != atom)
995
    {
996
137
      return TrustNode::mkTrustRewrite(atom, ret, nullptr);
997
    }
998
  }
999
126662
  if (atom.getKind() == STRING_FROM_CODE)
1000
  {
1001
    // str.from_code(t) ---> ite(0 <= t < |A|, t = str.to_code(k), k = "")
1002
72
    NodeManager* nm = NodeManager::currentNM();
1003
72
    SkolemCache* sc = d_termReg.getSkolemCache();
1004
144
    Node k = sc->mkSkolemCached(atom, SkolemCache::SK_PURIFY, "kFromCode");
1005
144
    Node t = atom[0];
1006
144
    Node card = nm->mkConst(Rational(d_termReg.getAlphabetCardinality()));
1007
    Node cond =
1008
144
        nm->mkNode(AND, nm->mkNode(LEQ, d_zero, t), nm->mkNode(LT, t, card));
1009
144
    Node emp = Word::mkEmptyWord(atom.getType());
1010
    Node pred = nm->mkNode(
1011
144
        ITE, cond, t.eqNode(nm->mkNode(STRING_TO_CODE, k)), k.eqNode(emp));
1012
144
    TrustNode tnk = TrustNode::mkTrustLemma(pred);
1013
72
    lems.push_back(SkolemLemma(tnk, k));
1014
72
    return TrustNode::mkTrustRewrite(atom, k, nullptr);
1015
  }
1016
253180
  TrustNode ret;
1017
253180
  Node atomRet = atom;
1018
126590
  if (options::regExpElim() && atom.getKind() == STRING_IN_REGEXP)
1019
  {
1020
    // aggressive elimination of regular expression membership
1021
152
    ret = d_regexp_elim.eliminateTrusted(atomRet);
1022
152
    if (!ret.isNull())
1023
    {
1024
220
      Trace("strings-ppr") << "  rewrote " << atom << " -> " << ret.getNode()
1025
110
                           << " via regular expression elimination."
1026
110
                           << std::endl;
1027
110
      atomRet = ret.getNode();
1028
    }
1029
  }
1030
126590
  return ret;
1031
}
1032
1033
/** run the given inference step */
1034
320556
void TheoryStrings::runInferStep(InferStep s, int effort)
1035
{
1036
320556
  Trace("strings-process") << "Run " << s;
1037
320556
  if (effort > 0)
1038
  {
1039
46481
    Trace("strings-process") << ", effort = " << effort;
1040
  }
1041
320556
  Trace("strings-process") << "..." << std::endl;
1042
320556
  switch (s)
1043
  {
1044
44123
    case CHECK_INIT: d_bsolver.checkInit(); break;
1045
37370
    case CHECK_CONST_EQC: d_bsolver.checkConstantEquivalenceClasses(); break;
1046
51831
    case CHECK_EXTF_EVAL: d_esolver.checkExtfEval(effort); break;
1047
35232
    case CHECK_CYCLES: d_csolver.checkCycles(); break;
1048
35224
    case CHECK_FLAT_FORMS: d_csolver.checkFlatForms(); break;
1049
    case CHECK_REGISTER_TERMS_PRE_NF: checkRegisterTermsPreNormalForm(); break;
1050
23514
    case CHECK_NORMAL_FORMS_EQ: d_csolver.checkNormalFormsEq(); break;
1051
13934
    case CHECK_NORMAL_FORMS_DEQ: d_csolver.checkNormalFormsDeq(); break;
1052
13446
    case CHECK_CODES: checkCodes(); break;
1053
13020
    case CHECK_LENGTH_EQC: d_csolver.checkLengthsEqc(); break;
1054
    case CHECK_REGISTER_TERMS_NF: checkRegisterTermsNormalForms(); break;
1055
32020
    case CHECK_EXTF_REDUCTION: d_esolver.checkExtfReductions(effort); break;
1056
10637
    case CHECK_MEMBERSHIP: d_rsolver.checkMemberships(); break;
1057
10205
    case CHECK_CARDINALITY: d_bsolver.checkCardinality(); break;
1058
    default: Unreachable(); break;
1059
  }
1060
641112
  Trace("strings-process") << "Done " << s
1061
641112
                           << ", addedFact = " << d_im.hasPendingFact()
1062
641112
                           << ", addedLemma = " << d_im.hasPendingLemma()
1063
641112
                           << ", conflict = " << d_state.isInConflict()
1064
320556
                           << std::endl;
1065
320556
}
1066
1067
44123
void TheoryStrings::runStrategy(Theory::Effort e)
1068
{
1069
44123
  std::vector<std::pair<InferStep, int> >::iterator it = d_strat.stepBegin(e);
1070
  std::vector<std::pair<InferStep, int> >::iterator stepEnd =
1071
44123
      d_strat.stepEnd(e);
1072
1073
44123
  Trace("strings-process") << "----check, next round---" << std::endl;
1074
1235431
  while (it != stepEnd)
1075
  {
1076
629560
    InferStep curr = it->first;
1077
629560
    if (curr == BREAK)
1078
    {
1079
309004
      if (d_im.hasProcessed())
1080
      {
1081
32571
        break;
1082
      }
1083
    }
1084
    else
1085
    {
1086
320556
      runInferStep(curr, it->second);
1087
320556
      if (d_state.isInConflict())
1088
      {
1089
1335
        break;
1090
      }
1091
    }
1092
595654
    ++it;
1093
  }
1094
44123
  Trace("strings-process") << "----finished round---" << std::endl;
1095
44123
}
1096
1097
std::string TheoryStrings::debugPrintStringsEqc()
1098
{
1099
  std::stringstream ss;
1100
  for (unsigned t = 0; t < 2; t++)
1101
  {
1102
    eq::EqClassesIterator eqcs2_i = eq::EqClassesIterator(d_equalityEngine);
1103
    ss << (t == 0 ? "STRINGS:" : "OTHER:") << std::endl;
1104
    while (!eqcs2_i.isFinished())
1105
    {
1106
      Node eqc = (*eqcs2_i);
1107
      bool print = (t == 0 && eqc.getType().isStringLike())
1108
                   || (t == 1 && !eqc.getType().isStringLike());
1109
      if (print)
1110
      {
1111
        eq::EqClassIterator eqc2_i = eq::EqClassIterator(eqc, d_equalityEngine);
1112
        ss << "Eqc( " << eqc << " ) : { ";
1113
        while (!eqc2_i.isFinished())
1114
        {
1115
          if ((*eqc2_i) != eqc && (*eqc2_i).getKind() != kind::EQUAL)
1116
          {
1117
            ss << (*eqc2_i) << " ";
1118
          }
1119
          ++eqc2_i;
1120
        }
1121
        ss << " } " << std::endl;
1122
        EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false);
1123
        if (ei)
1124
        {
1125
          Trace("strings-eqc-debug")
1126
              << "* Length term : " << ei->d_lengthTerm.get() << std::endl;
1127
          Trace("strings-eqc-debug")
1128
              << "* Cardinality lemma k : " << ei->d_cardinalityLemK.get()
1129
              << std::endl;
1130
          Trace("strings-eqc-debug")
1131
              << "* Normalization length lemma : "
1132
              << ei->d_normalizedLength.get() << std::endl;
1133
        }
1134
      }
1135
      ++eqcs2_i;
1136
    }
1137
    ss << std::endl;
1138
  }
1139
  ss << std::endl;
1140
  return ss.str();
1141
}
1142
1143
}  // namespace strings
1144
}  // namespace theory
1145
31137
}  // namespace cvc5