GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/core_solver.cpp Lines: 1118 1401 79.8 %
Date: 2021-09-29 Branches: 2567 6467 39.7 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Andres Noetzli, Tianyi Liang
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/core_solver.h"
17
18
#include "base/configuration.h"
19
#include "expr/sequence.h"
20
#include "options/strings_options.h"
21
#include "smt/logic_exception.h"
22
#include "theory/rewriter.h"
23
#include "theory/strings/sequences_rewriter.h"
24
#include "theory/strings/strings_entail.h"
25
#include "theory/strings/theory_strings_utils.h"
26
#include "theory/strings/word.h"
27
#include "util/rational.h"
28
#include "util/string.h"
29
30
using namespace std;
31
using namespace cvc5::context;
32
using namespace cvc5::kind;
33
34
namespace cvc5 {
35
namespace theory {
36
namespace strings {
37
38
15129
CoreInferInfo::CoreInferInfo(InferenceId id) : d_infer(id), d_index(0), d_rev(false) {}
39
40
6271
CoreSolver::CoreSolver(Env& env,
41
                       SolverState& s,
42
                       InferenceManager& im,
43
                       TermRegistry& tr,
44
6271
                       BaseSolver& bs)
45
    : EnvObj(env),
46
      d_state(s),
47
      d_im(im),
48
      d_termReg(tr),
49
      d_bsolver(bs),
50
      d_nfPairs(context()),
51
6271
      d_extDeq(userContext())
52
{
53
6271
  d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) );
54
6271
  d_one = NodeManager::currentNM()->mkConst( Rational( 1 ) );
55
6271
  d_neg_one = NodeManager::currentNM()->mkConst(Rational(-1));
56
6271
  d_true = NodeManager::currentNM()->mkConst( true );
57
6271
  d_false = NodeManager::currentNM()->mkConst( false );
58
6271
}
59
60
6268
CoreSolver::~CoreSolver() {
61
62
6268
}
63
64
void CoreSolver::debugPrintFlatForms( const char * tc ){
65
  for( unsigned k=0; k<d_strings_eqc.size(); k++ ){
66
    Node eqc = d_strings_eqc[k];
67
    if( d_eqc[eqc].size()>1 ){
68
      Trace( tc ) << "EQC [" << eqc << "]" << std::endl;
69
    }else{
70
      Trace( tc ) << "eqc [" << eqc << "]";
71
    }
72
    Node c = d_bsolver.getConstantEqc(eqc);
73
    if( !c.isNull() ){
74
      Trace( tc ) << "  C: " << c;
75
      if( d_eqc[eqc].size()>1 ){
76
        Trace( tc ) << std::endl;
77
      }
78
    }
79
    if( d_eqc[eqc].size()>1 ){
80
      for( unsigned i=0; i<d_eqc[eqc].size(); i++ ){
81
        Node n = d_eqc[eqc][i];
82
        Trace( tc ) << "    ";
83
        for( unsigned j=0; j<d_flat_form[n].size(); j++ ){
84
          Node fc = d_flat_form[n][j];
85
          Node fcc = d_bsolver.getConstantEqc(fc);
86
          Trace( tc ) << " ";
87
          if( !fcc.isNull() ){
88
            Trace( tc ) << fcc;
89
          }else{
90
            Trace( tc ) << fc;
91
          }
92
        }
93
        if( n!=eqc ){
94
          Trace( tc ) << ", from " << n;
95
        }
96
        Trace( tc ) << std::endl;
97
      }
98
    }else{
99
      Trace( tc ) << std::endl;
100
    }
101
  }
102
  Trace( tc ) << std::endl;
103
}
104
105
struct sortConstLength {
106
  std::map< Node, unsigned > d_const_length;
107
  bool operator() (Node i, Node j) {
108
    std::map< Node, unsigned >::iterator it_i = d_const_length.find( i );
109
    std::map< Node, unsigned >::iterator it_j = d_const_length.find( j );
110
    if( it_i==d_const_length.end() ){
111
      if( it_j==d_const_length.end() ){
112
        return i<j;
113
      }else{
114
        return false;
115
      }
116
    }else{
117
      if( it_j==d_const_length.end() ){
118
        return true;
119
      }else{
120
        return it_i->second<it_j->second;
121
      }
122
    }
123
  }
124
};
125
126
22707
void CoreSolver::checkCycles()
127
{
128
  // first check for cycles, while building ordering of equivalence classes
129
22707
  d_flat_form.clear();
130
22707
  d_flat_form_index.clear();
131
22707
  d_eqc.clear();
132
  // Rebuild strings eqc based on acyclic ordering, first copy the equivalence
133
  // classes from the base solver.
134
22707
  const std::vector<Node>& eqc = d_bsolver.getStringEqc();
135
22707
  d_strings_eqc.clear();
136
448585
  for (const Node& r : eqc)
137
  {
138
851757
    std::vector< Node > curr;
139
851757
    std::vector< Node > exp;
140
425879
    checkCycles(r, curr, exp);
141
425879
    if (d_im.hasProcessed())
142
    {
143
1
      return;
144
    }
145
  }
146
}
147
148
22706
void CoreSolver::checkFlatForms()
149
{
150
  // debug print flat forms
151
22706
  if (Trace.isOn("strings-ff"))
152
  {
153
    Trace("strings-ff") << "Flat forms : " << std::endl;
154
    debugPrintFlatForms("strings-ff");
155
  }
156
157
  // inferences without recursively expanding flat forms
158
159
  //(1) approximate equality by containment, infer conflicts
160
448515
  for (const Node& eqc : d_strings_eqc)
161
  {
162
851645
    Node c = d_bsolver.getConstantEqc(eqc);
163
425836
    if (!c.isNull())
164
    {
165
      // if equivalence class is constant, all component constants in flat forms
166
      // must be contained in it, in order
167
122262
      std::map<Node, std::vector<Node> >::iterator it = d_eqc.find(eqc);
168
122262
      if (it != d_eqc.end())
169
      {
170
43661
        for (const Node& n : it->second)
171
        {
172
          int firstc, lastc;
173
23239
          if (!StringsEntail::canConstantContainList(
174
23239
                  c, d_flat_form[n], firstc, lastc))
175
          {
176
54
            Trace("strings-ff-debug") << "Flat form for " << n
177
27
                                      << " cannot be contained in constant "
178
27
                                      << c << std::endl;
179
54
            Trace("strings-ff-debug") << "  indices = " << firstc << "/"
180
27
                                      << lastc << std::endl;
181
            // conflict, explanation is n = base ^ base = c ^ relevant portion
182
            // of ( n = f[n] )
183
54
            std::vector<Node> exp;
184
80
            for (int e = firstc; e <= lastc; e++)
185
            {
186
53
              if (d_flat_form[n][e].isConst())
187
              {
188
42
                Assert(e >= 0 && e < (int)d_flat_form_index[n].size());
189
42
                Assert(d_flat_form_index[n][e] >= 0
190
                       && d_flat_form_index[n][e] < (int)n.getNumChildren());
191
126
                d_im.addToExplanation(
192
84
                    n[d_flat_form_index[n][e]], d_flat_form[n][e], exp);
193
              }
194
            }
195
27
            d_bsolver.explainConstantEqc(n, eqc, exp);
196
54
            Node conc = d_false;
197
27
            d_im.sendInference(exp, conc, InferenceId::STRINGS_F_NCTN);
198
27
            return;
199
          }
200
        }
201
      }
202
    }
203
  }
204
205
  //(2) scan lists, unification to infer conflicts and equalities
206
445766
  for (const Node& eqc : d_strings_eqc)
207
  {
208
423317
    std::map<Node, std::vector<Node> >::iterator it = d_eqc.find(eqc);
209
423317
    if (it == d_eqc.end() || it->second.size() <= 1)
210
    {
211
392265
      continue;
212
    }
213
    // iterate over start index
214
125276
    for (unsigned start = 0; start < it->second.size() - 1; start++)
215
    {
216
283061
      for (unsigned r = 0; r < 2; r++)
217
      {
218
188837
        bool isRev = r == 1;
219
188837
        checkFlatForm(it->second, start, isRev);
220
188837
        if (d_state.isInConflict())
221
        {
222
460
          return;
223
        }
224
225
1724099
        for (const Node& n : it->second)
226
        {
227
1535492
          std::reverse(d_flat_form[n].begin(), d_flat_form[n].end());
228
1535492
          std::reverse(d_flat_form_index[n].begin(),
229
1535492
                       d_flat_form_index[n].end());
230
        }
231
      }
232
    }
233
  }
234
}
235
236
188837
void CoreSolver::checkFlatForm(std::vector<Node>& eqc,
237
                                  size_t start,
238
                                  bool isRev)
239
{
240
188837
  size_t count = 0;
241
  // We check for flat form inferences involving `eqc[start]` and terms past
242
  // `start`. If there was a flat form inference involving `eqc[start]` and a
243
  // term at a smaller index `i`, we would have found it with when `start` was
244
  // `i`. Thus, we mark the preceeding terms in the equivalence class as
245
  // ineligible.
246
377444
  std::vector<Node> inelig(eqc.begin(), eqc.begin() + start + 1);
247
377444
  Node a = eqc[start];
248
377674
  Trace("strings-ff-debug")
249
188837
      << "Check flat form for a = " << a << ", whose flat form is "
250
188837
      << d_flat_form[a] << ")" << std::endl;
251
377444
  Node b;
252
  // the length explanation
253
377444
  Node lant;
254
60590
  do
255
  {
256
488319
    std::vector<Node> exp;
257
488319
    Node conc;
258
249427
    InferenceId infType = InferenceId::UNKNOWN;
259
249427
    size_t eqc_size = eqc.size();
260
249427
    size_t asize = d_flat_form[a].size();
261
249427
    if (count == asize)
262
    {
263
      for (size_t i = start + 1; i < eqc_size; i++)
264
      {
265
        b = eqc[i];
266
        if (std::find(inelig.begin(), inelig.end(), b) != inelig.end())
267
        {
268
          continue;
269
        }
270
271
        size_t bsize = d_flat_form[b].size();
272
        if (count < bsize)
273
        {
274
          Trace("strings-ff-debug")
275
              << "Found endpoint (in a) with non-empty b = " << b
276
              << ", whose flat form is " << d_flat_form[b] << std::endl;
277
          // endpoint
278
          Node emp = Word::mkEmptyWord(a.getType());
279
          std::vector<Node> conc_c;
280
          for (unsigned j = count; j < bsize; j++)
281
          {
282
            conc_c.push_back(b[d_flat_form_index[b][j]].eqNode(emp));
283
          }
284
          Assert(!conc_c.empty());
285
          conc = utils::mkAnd(conc_c);
286
          infType = InferenceId::STRINGS_F_ENDPOINT_EMP;
287
          Assert(count > 0);
288
          // swap, will enforce is empty past current
289
          a = eqc[i];
290
          b = eqc[start];
291
          break;
292
        }
293
        inelig.push_back(eqc[i]);
294
      }
295
    }
296
    else
297
    {
298
498854
      Node curr = d_flat_form[a][count];
299
498854
      Node curr_c = d_bsolver.getConstantEqc(curr);
300
498854
      Node ac = a[d_flat_form_index[a][count]];
301
498854
      std::vector<Node> lexp;
302
498854
      Node lcurr = d_state.getLength(ac, lexp);
303
1365829
      for (size_t i = start + 1; i < eqc_size; i++)
304
      {
305
1126937
        b = eqc[i];
306
1126937
        if (std::find(inelig.begin(), inelig.end(), b) != inelig.end())
307
        {
308
285183
          continue;
309
        }
310
311
841754
        if (count == d_flat_form[b].size())
312
        {
313
          inelig.push_back(b);
314
          Trace("strings-ff-debug")
315
              << "Found endpoint in b = " << b << ", whose flat form is "
316
              << d_flat_form[b] << std::endl;
317
          // endpoint
318
          Node emp = Word::mkEmptyWord(a.getType());
319
          std::vector<Node> conc_c;
320
          for (size_t j = count; j < asize; j++)
321
          {
322
            conc_c.push_back(a[d_flat_form_index[a][j]].eqNode(emp));
323
          }
324
          Assert(!conc_c.empty());
325
          conc = utils::mkAnd(conc_c);
326
          infType = InferenceId::STRINGS_F_ENDPOINT_EMP;
327
          Assert(count > 0);
328
          break;
329
        }
330
        else
331
        {
332
1672973
          Node cc = d_flat_form[b][count];
333
841754
          if (cc != curr)
334
          {
335
1508389
            Node bc = b[d_flat_form_index[b][count]];
336
759462
            inelig.push_back(b);
337
759462
            Assert(!d_state.areEqual(curr, cc));
338
1508389
            Node cc_c = d_bsolver.getConstantEqc(cc);
339
759462
            if (!curr_c.isNull() && !cc_c.isNull())
340
            {
341
              // check for constant conflict
342
              size_t index;
343
59314
              Node s = Word::splitConstant(cc_c, curr_c, index, isRev);
344
29772
              if (s.isNull())
345
              {
346
230
                d_bsolver.explainConstantEqc(ac,curr,exp);
347
230
                d_bsolver.explainConstantEqc(bc,cc,exp);
348
230
                conc = d_false;
349
230
                infType = InferenceId::STRINGS_F_CONST;
350
230
                break;
351
              }
352
            }
353
1459380
            else if ((d_flat_form[a].size() - 1) == count
354
729690
                     && (d_flat_form[b].size() - 1) == count)
355
            {
356
4595
              conc = ac.eqNode(bc);
357
4595
              infType = InferenceId::STRINGS_F_ENDPOINT_EQ;
358
4595
              break;
359
            }
360
            else
361
            {
362
              // if lengths are the same, apply LengthEq
363
1444480
              std::vector<Node> lexp2;
364
1444480
              Node lcc = d_state.getLength(bc, lexp2);
365
725095
              if (d_state.areEqual(lcurr, lcc))
366
              {
367
5710
                if (Trace.isOn("strings-ff-debug"))
368
                {
369
                  Trace("strings-ff-debug")
370
                      << "Infer " << ac << " == " << bc << " since " << lcurr
371
                      << " == " << lcc << std::endl;
372
                  Trace("strings-ff-debug")
373
                      << "Explanation for " << lcurr << " is ";
374
                  for (size_t j = 0; j < lexp.size(); j++)
375
                  {
376
                    Trace("strings-ff-debug") << lexp[j] << std::endl;
377
                  }
378
                  Trace("strings-ff-debug")
379
                      << "Explanation for " << lcc << " is ";
380
                  for (size_t j = 0; j < lexp2.size(); j++)
381
                  {
382
                    Trace("strings-ff-debug") << lexp2[j] << std::endl;
383
                  }
384
                }
385
11420
                std::vector<Node> lexpc;
386
5710
                lexpc.insert(lexpc.end(), lexp.begin(), lexp.end());
387
5710
                lexpc.insert(lexpc.end(), lexp2.begin(), lexp2.end());
388
5710
                d_im.addToExplanation(lcurr, lcc, lexpc);
389
5710
                lant = utils::mkAnd(lexpc);
390
5710
                conc = ac.eqNode(bc);
391
5710
                infType = InferenceId::STRINGS_F_UNIFY;
392
5710
                break;
393
              }
394
            }
395
          }
396
        }
397
      }
398
    }
399
249427
    if (!conc.isNull())
400
    {
401
21070
      Trace("strings-ff-debug") << "Found inference (" << infType
402
10535
                                << "): " << conc << " based on equality " << a
403
10535
                                << " == " << b << ", " << isRev << std::endl;
404
      // explain why prefixes up to now were the same
405
15796
      for (size_t j = 0; j < count; j++)
406
      {
407
10522
        Trace("strings-ff-debug") << "Add at " << d_flat_form_index[a][j] << " "
408
5261
                                  << d_flat_form_index[b][j] << std::endl;
409
15783
        d_im.addToExplanation(
410
10522
            a[d_flat_form_index[a][j]], b[d_flat_form_index[b][j]], exp);
411
      }
412
      // explain why other components up to now are empty
413
31605
      for (unsigned t = 0; t < 2; t++)
414
      {
415
42140
        Node c = t == 0 ? a : b;
416
        ssize_t jj;
417
21070
        if (infType == InferenceId::STRINGS_F_ENDPOINT_EQ
418
11880
            || (t == 1 && infType == InferenceId::STRINGS_F_ENDPOINT_EMP))
419
        {
420
          // explain all the empty components for F_EndpointEq, all for
421
          // the short end for F_EndpointEmp
422
9190
          jj = isRev ? -1 : c.getNumChildren();
423
        }
424
        else
425
        {
426
17820
          jj = t == 0 ? d_flat_form_index[a][count]
427
5940
                      : d_flat_form_index[b][count];
428
        }
429
21070
        ssize_t startj = isRev ? jj + 1 : 0;
430
21070
        ssize_t endj = isRev ? c.getNumChildren() : jj;
431
42140
        Node emp = Word::mkEmptyWord(a.getType());
432
46234
        for (ssize_t j = startj; j < endj; j++)
433
        {
434
25164
          if (d_state.areEqual(c[j], emp))
435
          {
436
5452
            d_im.addToExplanation(c[j], emp, exp);
437
          }
438
        }
439
      }
440
10535
      d_im.addToExplanation(a, b, exp);
441
10535
      if (!lant.isNull())
442
      {
443
        // add the length explanation
444
5710
        exp.push_back(lant);
445
      }
446
      // Notice that F_EndpointEmp is not typically applied, since
447
      // strict prefix equality ( a.b = a ) where a,b non-empty
448
      // is conflicting by arithmetic len(a.b)=len(a)+len(b)!=len(a)
449
      // when len(b)!=0. Although if we do not infer this conflict eagerly,
450
      // it may be applied (see #3272).
451
10535
      d_im.sendInference(exp, conc, infType, isRev);
452
10535
      if (d_state.isInConflict())
453
      {
454
230
        return;
455
      }
456
10305
      break;
457
    }
458
238892
    count++;
459
238892
  } while (inelig.size() < eqc.size());
460
}
461
462
1203182
Node CoreSolver::checkCycles( Node eqc, std::vector< Node >& curr, std::vector< Node >& exp ){
463
1203182
  if( std::find( curr.begin(), curr.end(), eqc )!=curr.end() ){
464
    // a loop
465
    return eqc;
466
1203182
  }else if( std::find( d_strings_eqc.begin(), d_strings_eqc.end(), eqc )==d_strings_eqc.end() ){
467
425879
    curr.push_back( eqc );
468
851757
    Node emp = Word::mkEmptyWord(eqc.getType());
469
    //look at all terms in this equivalence class
470
425879
    eq::EqualityEngine* ee = d_state.getEqualityEngine();
471
425879
    eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, ee );
472
4427907
    while( !eqc_i.isFinished() ) {
473
4002029
      Node n = (*eqc_i);
474
2001015
      if( !d_bsolver.isCongruent(n) ){
475
1028668
        if( n.getKind() == kind::STRING_CONCAT ){
476
322565
          Trace("strings-cycle") << eqc << " check term : " << n << " in " << eqc << std::endl;
477
322565
          if (eqc != emp)
478
          {
479
313265
            d_eqc[eqc].push_back( n );
480
          }
481
1124678
          for( unsigned i=0; i<n.getNumChildren(); i++ ){
482
1604227
            Node nr = d_state.getRepresentative(n[i]);
483
802114
            if (eqc == emp)
484
            {
485
              //for empty eqc, ensure all components are empty
486
24811
              if (nr != emp)
487
              {
488
2
                std::vector<Node> exps;
489
1
                exps.push_back(n.eqNode(emp));
490
2
                d_im.sendInference(
491
2
                    exps, n[i].eqNode(emp), InferenceId::STRINGS_I_CYCLE_E);
492
1
                return Node::null();
493
              }
494
            }else{
495
777303
              if (nr != emp)
496
              {
497
745598
                d_flat_form[n].push_back( nr );
498
745598
                d_flat_form_index[n].push_back( i );
499
              }
500
              //for non-empty eqc, recurse and see if we find a loop
501
1554606
              Node ncy = checkCycles( nr, curr, exp );
502
777303
              if( !ncy.isNull() ){
503
                Trace("strings-cycle") << eqc << " cycle: " << ncy << " at " << n << "[" << i << "] : " << n[i] << std::endl;
504
                d_im.addToExplanation(n, eqc, exp);
505
                d_im.addToExplanation(nr, n[i], exp);
506
                if( ncy==eqc ){
507
                  //can infer all other components must be empty
508
                  for( unsigned j=0; j<n.getNumChildren(); j++ ){
509
                    //take first non-empty
510
                    if (j != i && !d_state.areEqual(n[j], emp))
511
                    {
512
                      d_im.sendInference(
513
                          exp, n[j].eqNode(emp), InferenceId::STRINGS_I_CYCLE);
514
                      return Node::null();
515
                    }
516
                  }
517
                  Trace("strings-error") << "Looping term should be congruent : " << n << " " << eqc << " " << ncy << std::endl;
518
                  //should find a non-empty component, otherwise would have been singular congruent (I_Norm_S)
519
                  Assert(false);
520
                }else{
521
                  return ncy;
522
                }
523
              }else{
524
777303
                if (d_im.hasProcessed())
525
                {
526
                  return Node::null();
527
                }
528
              }
529
            }
530
          }
531
        }
532
      }
533
2001014
      ++eqc_i;
534
    }
535
425878
    curr.pop_back();
536
425878
    Trace("strings-eqc") << "* add string eqc: " << eqc << std::endl;
537
    //now we can add it to the list of equivalence classes
538
425878
    d_strings_eqc.push_back( eqc );
539
  }else{
540
    //already processed
541
  }
542
1203181
  return Node::null();
543
}
544
545
15020
void CoreSolver::checkNormalFormsEq()
546
{
547
  // calculate normal forms for each equivalence class, possibly adding
548
  // splitting lemmas
549
15020
  d_normal_form.clear();
550
23849
  std::map<Node, Node> nf_to_eqc;
551
23849
  std::map<Node, Node> eqc_to_nf;
552
23849
  std::map<Node, Node> eqc_to_exp;
553
204044
  for (const Node& eqc : d_strings_eqc)
554
  {
555
384239
    TypeNode stype = eqc.getType();
556
390430
    Trace("strings-process-debug") << "- Verify normal forms are the same for "
557
195215
                                   << eqc << std::endl;
558
195215
    normalizeEquivalenceClass(eqc, stype);
559
195215
    Trace("strings-debug") << "Finished normalizing eqc..." << std::endl;
560
195215
    if (d_im.hasProcessed())
561
    {
562
5737
      return;
563
    }
564
189478
    NormalForm& nfe = getNormalForm(eqc);
565
378502
    Node nf_term = utils::mkNConcat(nfe.d_nf, stype);
566
189478
    std::map<Node, Node>::iterator itn = nf_to_eqc.find(nf_term);
567
189478
    if (itn != nf_to_eqc.end())
568
    {
569
454
      NormalForm& nfe_eq = getNormalForm(itn->second);
570
      // two equivalence classes have same normal form, merge
571
454
      std::vector<Node> nf_exp(nfe.d_exp.begin(), nfe.d_exp.end());
572
454
      Node eexp = eqc_to_exp[itn->second];
573
454
      if (eexp != d_true)
574
      {
575
430
        nf_exp.push_back(eexp);
576
      }
577
454
      Node eq = nfe.d_base.eqNode(nfe_eq.d_base);
578
454
      d_im.sendInference(nf_exp, eq, InferenceId::STRINGS_NORMAL_FORM);
579
454
      if (d_im.hasProcessed())
580
      {
581
454
        return;
582
      }
583
    }
584
    else
585
    {
586
189024
      nf_to_eqc[nf_term] = eqc;
587
189024
      eqc_to_nf[eqc] = nf_term;
588
189024
      eqc_to_exp[eqc] = utils::mkAnd(nfe.d_exp);
589
    }
590
378048
    Trace("strings-process-debug")
591
189024
        << "Done verifying normal forms are the same for " << eqc << std::endl;
592
  }
593
8829
  if (Trace.isOn("strings-nf"))
594
  {
595
    Trace("strings-nf") << "**** Normal forms are : " << std::endl;
596
    for (std::map<Node, Node>::iterator it = eqc_to_exp.begin();
597
         it != eqc_to_exp.end();
598
         ++it)
599
    {
600
      NormalForm& nf = getNormalForm(it->first);
601
      Trace("strings-nf") << "  N[" << it->first << "] (base " << nf.d_base
602
                          << ") = " << eqc_to_nf[it->first] << std::endl;
603
      Trace("strings-nf") << "     exp: " << it->second << std::endl;
604
    }
605
    Trace("strings-nf") << std::endl;
606
  }
607
}
608
609
//compute d_normal_forms_(base,exp,exp_depend)[eqc]
610
195215
void CoreSolver::normalizeEquivalenceClass(Node eqc, TypeNode stype)
611
{
612
195215
  Trace("strings-process-debug") << "Process equivalence class " << eqc << std::endl;
613
384693
  Node emp = Word::mkEmptyWord(stype);
614
195215
  if (d_state.areEqual(eqc, emp))
615
  {
616
#ifdef CVC5_ASSERTIONS
617
10177
    for( unsigned j=0; j<d_eqc[eqc].size(); j++ ){
618
      Node n = d_eqc[eqc][j];
619
      for( unsigned i=0; i<n.getNumChildren(); i++ ){
620
        Assert(d_state.areEqual(n[i], emp));
621
      }
622
    }
623
#endif
624
    //do nothing
625
10177
    Trace("strings-process-debug") << "Return process equivalence class " << eqc << " : empty." << std::endl;
626
10177
    d_normal_form[eqc].init(emp);
627
  }
628
  else
629
  {
630
    // should not have computed the normal form of this equivalence class yet
631
185038
    Assert(d_normal_form.find(eqc) == d_normal_form.end());
632
    // Normal forms for the relevant terms in the equivalence class of eqc
633
364339
    std::vector<NormalForm> normal_forms;
634
    // map each term to its index in the above vector
635
364339
    std::map<Node, unsigned> term_to_nf_index;
636
    // get the normal forms
637
185038
    getNormalForms(eqc, normal_forms, term_to_nf_index, stype);
638
185038
    if (d_im.hasProcessed())
639
    {
640
      return;
641
    }
642
    // process the normal forms
643
185038
    processNEqc(eqc, normal_forms, stype);
644
185038
    if (d_im.hasProcessed())
645
    {
646
5737
      return;
647
    }
648
649
    //construct the normal form
650
179301
    Assert(!normal_forms.empty());
651
179301
    unsigned nf_index = 0;
652
179301
    std::map<Node, unsigned>::iterator it = term_to_nf_index.find(eqc);
653
    // we prefer taking the normal form whose base is the equivalence
654
    // class representative, since this leads to shorter explanations in
655
    // some cases.
656
179301
    if (it != term_to_nf_index.end())
657
    {
658
115798
      nf_index = it->second;
659
    }
660
179301
    d_normal_form[eqc] = normal_forms[nf_index];
661
358602
    Trace("strings-process-debug")
662
179301
        << "Return process equivalence class " << eqc
663
179301
        << " : returned = " << d_normal_form[eqc].d_nf << std::endl;
664
  }
665
}
666
667
635441
NormalForm& CoreSolver::getNormalForm(Node n)
668
{
669
635441
  std::map<Node, NormalForm>::iterator itn = d_normal_form.find(n);
670
635441
  if (itn == d_normal_form.end())
671
  {
672
    Trace("strings-warn") << "WARNING: returning empty normal form for " << n
673
                          << std::endl;
674
    // Shouln't ask for normal forms of strings that weren't computed. This
675
    // likely means that n is not a representative or not a term in the current
676
    // context. We simply return a default normal form here in this case.
677
    Assert(false);
678
    return d_normal_form[n];
679
  }
680
635441
  return itn->second;
681
}
682
683
38397
Node CoreSolver::getNormalString(Node x, std::vector<Node>& nf_exp)
684
{
685
38397
  if (!x.isConst())
686
  {
687
31895
    Node xr = d_state.getRepresentative(x);
688
31895
    TypeNode stype = xr.getType();
689
31895
    std::map<Node, NormalForm>::iterator it = d_normal_form.find(xr);
690
31895
    if (it != d_normal_form.end())
691
    {
692
31895
      NormalForm& nf = it->second;
693
63790
      Node ret = utils::mkNConcat(nf.d_nf, stype);
694
31895
      nf_exp.insert(nf_exp.end(), nf.d_exp.begin(), nf.d_exp.end());
695
31895
      d_im.addToExplanation(x, nf.d_base, nf_exp);
696
63790
      Trace("strings-debug")
697
31895
          << "Term: " << x << " has a normal form " << ret << std::endl;
698
31895
      return ret;
699
    }
700
    // if x does not have a normal form, then it should not occur in the
701
    // equality engine and hence should be its own representative.
702
    Assert(xr == x);
703
    if (x.getKind() == kind::STRING_CONCAT)
704
    {
705
      std::vector<Node> vec_nodes;
706
      for (unsigned i = 0; i < x.getNumChildren(); i++)
707
      {
708
        Node nc = getNormalString(x[i], nf_exp);
709
        vec_nodes.push_back(nc);
710
      }
711
      return utils::mkNConcat(vec_nodes, stype);
712
    }
713
  }
714
6502
  return x;
715
}
716
717
8589
Node CoreSolver::getConclusion(Node x,
718
                               Node y,
719
                               PfRule rule,
720
                               bool isRev,
721
                               SkolemCache* skc,
722
                               std::vector<Node>& newSkolems)
723
{
724
17178
  Trace("strings-csolver") << "CoreSolver::getConclusion: " << x << " " << y
725
8589
                           << " " << rule << " " << isRev << std::endl;
726
8589
  NodeManager* nm = NodeManager::currentNM();
727
8589
  Node conc;
728
8589
  if (rule == PfRule::CONCAT_SPLIT || rule == PfRule::CONCAT_LPROP)
729
  {
730
7242
    Node sk1;
731
7242
    Node sk2;
732
3621
    if (options::stringUnifiedVSpt())
733
    {
734
      // must compare so that we are agnostic to order of x/y
735
7242
      Node ux = x < y ? x : y;
736
7242
      Node uy = x < y ? y : x;
737
      Node sk = skc->mkSkolemCached(ux,
738
                                    uy,
739
                                    isRev ? SkolemCache::SK_ID_V_UNIFIED_SPT_REV
740
                                          : SkolemCache::SK_ID_V_UNIFIED_SPT,
741
7242
                                    "v_spt");
742
3621
      newSkolems.push_back(sk);
743
3621
      sk1 = sk;
744
3621
      sk2 = sk;
745
    }
746
    else
747
    {
748
      sk1 = skc->mkSkolemCached(
749
          x,
750
          y,
751
          isRev ? SkolemCache::SK_ID_V_SPT_REV : SkolemCache::SK_ID_V_SPT,
752
          "v_spt1");
753
      sk2 = skc->mkSkolemCached(
754
          y,
755
          x,
756
          isRev ? SkolemCache::SK_ID_V_SPT_REV : SkolemCache::SK_ID_V_SPT,
757
          "v_spt2");
758
      newSkolems.push_back(sk1);
759
      newSkolems.push_back(sk2);
760
    }
761
7242
    Node eq1 = x.eqNode(isRev ? nm->mkNode(STRING_CONCAT, sk1, y)
762
7242
                              : nm->mkNode(STRING_CONCAT, y, sk1));
763
764
3621
    if (rule == PfRule::CONCAT_LPROP)
765
    {
766
1495
      conc = eq1;
767
    }
768
    else
769
    {
770
4252
      Node eq2 = y.eqNode(isRev ? nm->mkNode(STRING_CONCAT, sk2, x)
771
4252
                                : nm->mkNode(STRING_CONCAT, x, sk2));
772
      // make agnostic to x/y
773
2126
      conc = x < y ? nm->mkNode(OR, eq1, eq2) : nm->mkNode(OR, eq2, eq1);
774
    }
775
3621
    if (options::stringUnifiedVSpt())
776
    {
777
      // we can assume its length is greater than zero
778
7242
      Node emp = Word::mkEmptyWord(sk1.getType());
779
10863
      conc = nm->mkNode(
780
          AND,
781
          conc,
782
7242
          sk1.eqNode(emp).negate(),
783
14484
          nm->mkNode(
784
14484
              GT, nm->mkNode(STRING_LENGTH, sk1), nm->mkConst(Rational(0))));
785
3621
    }
786
  }
787
4968
  else if (rule == PfRule::CONCAT_CSPLIT)
788
  {
789
4072
    Assert(y.isConst());
790
4072
    size_t yLen = Word::getLength(y);
791
    Node firstChar =
792
8144
        yLen == 1 ? y : (isRev ? Word::suffix(y, 1) : Word::prefix(y, 1));
793
    Node sk = skc->mkSkolemCached(
794
        x,
795
        isRev ? SkolemCache::SK_ID_VC_SPT_REV : SkolemCache::SK_ID_VC_SPT,
796
8144
        "c_spt");
797
4072
    newSkolems.push_back(sk);
798
4072
    conc = x.eqNode(isRev ? nm->mkNode(STRING_CONCAT, sk, firstChar)
799
                          : nm->mkNode(STRING_CONCAT, firstChar, sk));
800
  }
801
896
  else if (rule == PfRule::CONCAT_CPROP)
802
  {
803
    // expect (str.++ z d) and c
804
896
    Assert(x.getKind() == STRING_CONCAT && x.getNumChildren() == 2);
805
1792
    Node z = x[isRev ? 1 : 0];
806
1792
    Node d = x[isRev ? 0 : 1];
807
896
    Assert(d.isConst());
808
1792
    Node c = y;
809
896
    Assert(c.isConst());
810
896
    size_t cLen = Word::getLength(c);
811
896
    size_t p = getSufficientNonEmptyOverlap(c, d, isRev);
812
    Node preC =
813
1792
        p == cLen ? c : (isRev ? Word::suffix(c, p) : Word::prefix(c, p));
814
    Node sk = skc->mkSkolemCached(
815
        z,
816
        preC,
817
        isRev ? SkolemCache::SK_ID_C_SPT_REV : SkolemCache::SK_ID_C_SPT,
818
1792
        "c_spt");
819
896
    newSkolems.push_back(sk);
820
896
    conc = z.eqNode(isRev ? nm->mkNode(STRING_CONCAT, sk, preC)
821
                          : nm->mkNode(STRING_CONCAT, preC, sk));
822
  }
823
824
8589
  return conc;
825
}
826
827
3355
size_t CoreSolver::getSufficientNonEmptyOverlap(Node c, Node d, bool isRev)
828
{
829
3355
  Assert(c.isConst() && c.getType().isStringLike());
830
3355
  Assert(d.isConst() && d.getType().isStringLike());
831
  size_t p;
832
  size_t p2;
833
3355
  size_t cLen = Word::getLength(c);
834
3355
  if (isRev)
835
  {
836
    // Since non-empty, we start with character 1
837
3534
    Node c1 = Word::prefix(c, cLen - 1);
838
1767
    p = cLen - Word::roverlap(c1, d);
839
1767
    p2 = Word::rfind(c1, d);
840
  }
841
  else
842
  {
843
3176
    Node c1 = Word::substr(c, 1);
844
1588
    p = cLen - Word::overlap(c1, d);
845
1588
    p2 = Word::find(c1, d);
846
  }
847
3355
  return p2 == std::string::npos ? p : (p > p2 + 1 ? p2 + 1 : p);
848
}
849
850
25
Node CoreSolver::getDecomposeConclusion(Node x,
851
                                        Node l,
852
                                        bool isRev,
853
                                        SkolemCache* skc,
854
                                        std::vector<Node>& newSkolems)
855
{
856
25
  Assert(l.getType().isInteger());
857
25
  NodeManager* nm = NodeManager::currentNM();
858
50
  Node n = isRev ? nm->mkNode(MINUS, nm->mkNode(STRING_LENGTH, x), l) : l;
859
50
  Node sk1 = skc->mkSkolemCached(x, n, SkolemCache::SK_PREFIX, "dc_spt1");
860
25
  newSkolems.push_back(sk1);
861
50
  Node sk2 = skc->mkSkolemCached(x, n, SkolemCache::SK_SUFFIX_REM, "dc_spt2");
862
25
  newSkolems.push_back(sk2);
863
50
  Node conc = x.eqNode(nm->mkNode(STRING_CONCAT, sk1, sk2));
864
  // add the length constraint to the conclusion
865
50
  Node lc = nm->mkNode(STRING_LENGTH, isRev ? sk2 : sk1).eqNode(l);
866
50
  return nm->mkNode(AND, conc, lc);
867
}
868
869
185038
void CoreSolver::getNormalForms(Node eqc,
870
                                std::vector<NormalForm>& normal_forms,
871
                                std::map<Node, unsigned>& term_to_nf_index,
872
                                TypeNode stype)
873
{
874
370076
  Node emp = Word::mkEmptyWord(stype);
875
  //constant for equivalence class
876
370076
  Node eqc_non_c = eqc;
877
185038
  Trace("strings-process-debug") << "Get normal forms " << eqc << std::endl;
878
185038
  eq::EqualityEngine* ee = d_state.getEqualityEngine();
879
185038
  eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, ee );
880
1759386
  while( !eqc_i.isFinished() ){
881
1574348
    Node n = (*eqc_i);
882
787174
    if( !d_bsolver.isCongruent(n) ){
883
435567
      Kind nk = n.getKind();
884
435567
      bool isCLike = utils::isConstantLike(n);
885
435567
      if (isCLike || nk == STRING_CONCAT)
886
      {
887
184819
        Trace("strings-process-debug") << "Get Normal Form : Process term " << n << " in eqc " << eqc << std::endl;
888
369638
        NormalForm nf_curr;
889
184819
        if (isCLike)
890
        {
891
45099
          nf_curr.init(n);
892
        }
893
139720
        else if (nk == STRING_CONCAT)
894
        {
895
          // set the base to n, we construct the other portions of nf_curr in
896
          // the following.
897
139720
          nf_curr.d_base = n;
898
476357
          for( unsigned i=0; i<n.getNumChildren(); i++ ) {
899
673274
            Node nr = ee->getRepresentative( n[i] );
900
            // get the normal form for the component
901
336637
            NormalForm& nfr = getNormalForm(nr);
902
336637
            std::vector<Node>& nfrv = nfr.d_nf;
903
673274
            Trace("strings-process-debug")
904
673274
                << "Normalizing subterm " << n[i] << " = " << nr
905
336637
                << ", which is " << nfrv << std::endl;
906
336637
            unsigned orig_size = nf_curr.d_nf.size();
907
336637
            unsigned add_size = nfrv.size();
908
            //if not the empty string, add to current normal form
909
336637
            if (!nfrv.empty())
910
            {
911
              // if in a build with assertions, we run the following block,
912
              // which checks that normal forms do not have concat terms.
913
326506
              if (Configuration::isAssertionBuild())
914
              {
915
939429
                for (const Node& nn : nfrv)
916
                {
917
612923
                  if (Trace.isOn("strings-error"))
918
                  {
919
                    if (nn.getKind() == STRING_CONCAT)
920
                    {
921
                      Trace("strings-error")
922
                          << "Strings::Error: From eqc = " << eqc << ", " << n
923
                          << " index " << i << ", bad normal form : ";
924
                      for (unsigned rr = 0; rr < nfrv.size(); rr++)
925
                      {
926
                        Trace("strings-error") << nfrv[rr] << " ";
927
                      }
928
                      Trace("strings-error") << std::endl;
929
                    }
930
                  }
931
612923
                  Assert(nn.getKind() != kind::STRING_CONCAT);
932
                }
933
              }
934
326506
              nf_curr.d_nf.insert(nf_curr.d_nf.end(), nfrv.begin(), nfrv.end());
935
            }
936
            // Track explanation for the normal form. This is in two parts.
937
            // First, we must carry the explanation of the normal form computed
938
            // for the representative nr.
939
576854
            for (const Node& exp : nfr.d_exp)
940
            {
941
              // The explanation is only relevant for the subsegment it was
942
              // previously relevant for, shifted now based on its relative
943
              // placement in the normal form of n.
944
480434
              nf_curr.addToExplanation(
945
                  exp,
946
480434
                  orig_size + nfr.d_expDep[exp][false],
947
480434
                  orig_size + (add_size - nfr.d_expDep[exp][true]));
948
            }
949
            // Second, must explain that the component n[i] is equal to the
950
            // base of the normal form for nr.
951
673274
            Node base = nfr.d_base;
952
336637
            if (base != n[i])
953
            {
954
377292
              Node eq = n[i].eqNode(base);
955
              // The equality is relevant for the entire current segment
956
188646
              nf_curr.addToExplanation(eq, orig_size, orig_size + add_size);
957
            }
958
          }
959
          // Now that we are finished with the loop, we convert forward indices
960
          // to reverse indices in the explanation dependency information
961
139720
          int total_size = nf_curr.d_nf.size();
962
426825
          for (std::pair<const Node, std::map<bool, unsigned> >& ed :
963
139720
               nf_curr.d_expDep)
964
          {
965
426825
            ed.second[true] = total_size - ed.second[true];
966
426825
            Assert(ed.second[true] >= 0);
967
          }
968
        }
969
        //if not equal to self
970
184819
        std::vector<Node>& currv = nf_curr.d_nf;
971
369638
        if (currv.size() > 1
972
369638
            || (currv.size() == 1 && utils::isConstantLike(currv[0])))
973
        {
974
          // if in a build with assertions, check that normal form is acyclic
975
184819
          if (Configuration::isAssertionBuild())
976
          {
977
184819
            if (currv.size() > 1)
978
            {
979
752643
              for (unsigned i = 0; i < currv.size(); i++)
980
              {
981
612923
                if (Trace.isOn("strings-error"))
982
                {
983
                  Trace("strings-error") << "Cycle for normal form ";
984
                  utils::printConcatTrace(currv, "strings-error");
985
                  Trace("strings-error") << "..." << currv[i] << std::endl;
986
                }
987
612923
                Assert(!d_state.areEqual(currv[i], n));
988
              }
989
            }
990
          }
991
184819
          term_to_nf_index[n] = normal_forms.size();
992
184819
          normal_forms.push_back(nf_curr);
993
        }else{
994
          //this was redundant: combination of self + empty string(s)
995
          Node nn = currv.size() == 0 ? emp : currv[0];
996
          Assert(d_state.areEqual(nn, eqc));
997
184819
        }
998
      }else{
999
250748
        eqc_non_c = n;
1000
      }
1001
    }
1002
    else
1003
    {
1004
703214
      Trace("strings-process-debug")
1005
351607
          << "Get Normal Form : term " << n << " in eqc " << eqc
1006
351607
          << " is congruent" << std::endl;
1007
    }
1008
787174
    ++eqc_i;
1009
  }
1010
1011
185038
  if( normal_forms.empty() ) {
1012
48655
    Trace("strings-solve-debug2") << "construct the normal form" << std::endl;
1013
    // This case happens when there are no non-trivial normal forms for this
1014
    // equivalence class. For example, given assertions:
1015
    //   { x = y ++ z, x = y, z = "" }
1016
    // The equivalence class of { x, y, y ++ z } is such that the normal form
1017
    // of all terms is a variable (either x or y) in the equivalence class
1018
    // itself. Thus, the normal form of this equivalence class can be assigned
1019
    // to one of these variables.
1020
    // We use a non-concatenation term among the terms in this equivalence
1021
    // class, which is stored in eqc_non_c. The reason is this does not require
1022
    // an explanation, whereas e.g. y ++ z would require the explanation z = ""
1023
    // to justify its normal form is y.
1024
48655
    Assert(eqc_non_c.getKind() != STRING_CONCAT);
1025
97310
    NormalForm nf_triv;
1026
48655
    nf_triv.init(eqc_non_c);
1027
48655
    normal_forms.push_back(nf_triv);
1028
  }else{
1029
136383
    if(Trace.isOn("strings-solve")) {
1030
      Trace("strings-solve") << "--- Normal forms for equivalance class " << eqc << " : " << std::endl;
1031
      for (unsigned i = 0, size = normal_forms.size(); i < size; i++)
1032
      {
1033
        NormalForm& nf = normal_forms[i];
1034
        Trace("strings-solve") << "#" << i << " (from " << nf.d_base << ") : ";
1035
        for (unsigned j = 0, sizej = nf.d_nf.size(); j < sizej; j++)
1036
        {
1037
          if(j>0) {
1038
            Trace("strings-solve") << ", ";
1039
          }
1040
          Trace("strings-solve") << nf.d_nf[j];
1041
        }
1042
        Trace("strings-solve") << std::endl;
1043
        Trace("strings-solve") << "   Explanation is : ";
1044
        if (nf.d_exp.size() == 0)
1045
        {
1046
          Trace("strings-solve") << "NONE";
1047
        } else {
1048
          for (unsigned j = 0, sizej = nf.d_exp.size(); j < sizej; j++)
1049
          {
1050
            if(j>0) {
1051
              Trace("strings-solve") << " AND ";
1052
            }
1053
            Trace("strings-solve") << nf.d_exp[j];
1054
          }
1055
          Trace("strings-solve") << std::endl;
1056
          Trace("strings-solve") << "WITH DEPENDENCIES : " << std::endl;
1057
          for (unsigned j = 0, sizej = nf.d_exp.size(); j < sizej; j++)
1058
          {
1059
            Node exp = nf.d_exp[j];
1060
            Trace("strings-solve") << "   " << exp << " -> ";
1061
            Trace("strings-solve") << nf.d_expDep[exp][false] << ",";
1062
            Trace("strings-solve") << nf.d_expDep[exp][true] << std::endl;
1063
          }
1064
        }
1065
        Trace("strings-solve") << std::endl;
1066
      }
1067
    } else {
1068
136383
      Trace("strings-solve") << "--- Single normal form for equivalence class " << eqc << std::endl;
1069
    }
1070
  }
1071
185038
}
1072
1073
185038
void CoreSolver::processNEqc(Node eqc,
1074
                             std::vector<NormalForm>& normal_forms,
1075
                             TypeNode stype)
1076
{
1077
185038
  if (normal_forms.size() <= 1)
1078
  {
1079
347018
    return;
1080
  }
1081
  // if equivalence class is constant, approximate as containment, infer
1082
  // conflicts
1083
23058
  Node c = d_bsolver.getConstantEqc(eqc);
1084
  // the possible inferences
1085
23058
  std::vector<CoreInferInfo> pinfer;
1086
  // compute normal forms that are effectively unique
1087
23058
  std::unordered_map<Node, size_t> nfCache;
1088
23058
  std::vector<size_t> nfIndices;
1089
20937
  bool hasConstIndex = false;
1090
90200
  for (size_t i = 0, nnforms = normal_forms.size(); i < nnforms; i++)
1091
  {
1092
69325
    NormalForm& nfi = normal_forms[i];
1093
138588
    Node ni = utils::mkNConcat(nfi.d_nf, stype);
1094
69325
    if (nfCache.find(ni) == nfCache.end())
1095
    {
1096
      // If the equivalence class is entailed to be constant, check
1097
      // if the normal form cannot be contained in that constant.
1098
31786
      if (!c.isNull())
1099
      {
1100
        int firstc, lastc;
1101
9949
        if (!StringsEntail::canConstantContainList(c, nfi.d_nf, firstc, lastc))
1102
        {
1103
124
          Node n = nfi.d_base;
1104
124
          std::vector<Node> exp(nfi.d_exp.begin(), nfi.d_exp.end());
1105
          //conflict
1106
62
          Trace("strings-solve") << "Normal form for " << n << " cannot be contained in constant " << c << std::endl;
1107
          // conflict, explanation is:
1108
          //  n = base ^ base = c ^ relevant porition of ( n = N[n] )
1109
          // Notice although not implemented, this can be minimized based on
1110
          // firstc/lastc, normal_forms_exp_depend.
1111
62
          d_bsolver.explainConstantEqc(n, eqc, exp);
1112
62
          d_im.sendInference(exp, d_false, InferenceId::STRINGS_N_NCTN);
1113
          // conflict, finished
1114
62
          return;
1115
        }
1116
      }
1117
1118
31724
      nfCache[ni] = i;
1119
31724
      if (ni.isConst())
1120
      {
1121
9046
        hasConstIndex = true;
1122
9046
        nfIndices.insert(nfIndices.begin(), i);
1123
      }
1124
      else
1125
      {
1126
22678
        nfIndices.push_back(i);
1127
      }
1128
    }
1129
  }
1130
20875
  size_t nnfs = nfIndices.size();
1131
41750
  Trace("strings-solve") << "CoreSolver::processNEqc: " << nnfs << "/"
1132
41750
                         << normal_forms.size() << " normal forms are unique."
1133
20875
                         << std::endl;
1134
1135
  // loop over all pairs
1136
23713
  for (unsigned i = 0; i < nnfs - 1; i++)
1137
  {
1138
    //unify each normalform[j] with normal_forms[i]
1139
13929
    for (unsigned j = i + 1; j < nnfs; j++)
1140
    {
1141
10669
      NormalForm& nfi = normal_forms[nfIndices[i]];
1142
10669
      NormalForm& nfj = normal_forms[nfIndices[j]];
1143
      //ensure that normal_forms[i] and normal_forms[j] are the same modulo equality, add to pinfer if not
1144
10669
      Trace("strings-solve") << "Strings: Process normal form #" << i << " against #" << j << "..." << std::endl;
1145
10669
      if (isNormalFormPair(nfi.d_base, nfj.d_base))
1146
      {
1147
        Trace("strings-solve") << "Strings: Already cached." << std::endl;
1148
        continue;
1149
      }
1150
      // process the reverse direction first (check for easy conflicts and
1151
      // inferences)
1152
10669
      unsigned rindex = 0;
1153
10669
      nfi.reverse();
1154
10669
      nfj.reverse();
1155
10669
      processSimpleNEq(nfi, nfj, rindex, true, 0, pinfer, stype);
1156
10669
      nfi.reverse();
1157
10669
      nfj.reverse();
1158
10669
      if (d_im.hasProcessed())
1159
      {
1160
6209
        break;
1161
      }
1162
      // AJR: for less aggressive endpoint inference
1163
      // rindex = 0;
1164
1165
8014
      unsigned index = 0;
1166
8014
      processSimpleNEq(nfi, nfj, index, false, rindex, pinfer, stype);
1167
8014
      if (d_im.hasProcessed())
1168
      {
1169
899
        break;
1170
      }
1171
    }
1172
6814
    if (hasConstIndex || d_im.hasProcessed())
1173
    {
1174
3976
      break;
1175
    }
1176
  }
1177
20875
  if (d_state.isInConflict())
1178
  {
1179
    // conflict, we are done
1180
61
    return;
1181
  }
1182
  // Go back and check for normal form equality conflicts. These take
1183
  // precedence over any facts and lemmas.
1184
41246
  for (const std::pair<const Node, size_t>& ni : nfCache)
1185
  {
1186
65711
    for (const std::pair<const Node, size_t>& nj : nfCache)
1187
    {
1188
41786
      if (ni.first >= nj.first)
1189
      {
1190
        // avoid duplicate comparisons
1191
33486
        continue;
1192
      }
1193
16600
      Node eq = ni.first.eqNode(nj.first);
1194
8300
      eq = Rewriter::rewrite(eq);
1195
8300
      if (eq == d_false)
1196
      {
1197
        std::vector<Node> exp;
1198
        NormalForm& nfi = normal_forms[ni.second];
1199
        NormalForm& nfj = normal_forms[nj.second];
1200
        exp.insert(exp.end(), nfi.d_exp.begin(), nfi.d_exp.end());
1201
        exp.insert(exp.end(), nfj.d_exp.begin(), nfj.d_exp.end());
1202
        exp.push_back(nfi.d_base.eqNode(nfj.d_base));
1203
        d_im.sendInference(exp, d_false, InferenceId::STRINGS_N_EQ_CONF);
1204
        return;
1205
      }
1206
    }
1207
23925
    if (d_im.hasProcessed())
1208
    {
1209
3493
      break;
1210
    }
1211
  }
1212
20814
  if (d_im.hasProcessed() || pinfer.empty())
1213
  {
1214
    // either already sent a lemma or fact, or there are no possible inferences
1215
18693
    return;
1216
  }
1217
  // now, determine which of the possible inferences we want to add
1218
2121
  unsigned use_index = 0;
1219
2121
  bool set_use_index = false;
1220
4242
  Trace("strings-solve") << "Possible inferences (" << pinfer.size()
1221
2121
                         << ") : " << std::endl;
1222
2121
  InferenceId min_id = InferenceId::UNKNOWN;
1223
2121
  unsigned max_index = 0;
1224
12313
  for (unsigned i = 0, psize = pinfer.size(); i < psize; i++)
1225
  {
1226
10192
    CoreInferInfo& ipii = pinfer[i];
1227
10192
    InferInfo& ii = ipii.d_infer;
1228
20384
    Trace("strings-solve") << "#" << i << ": From " << ipii.d_i << " / "
1229
10192
                           << ipii.d_j << " (rev=" << ipii.d_rev << ") : ";
1230
10192
    Trace("strings-solve") << ii.d_conc << " by " << ii.getId() << std::endl;
1231
28455
    if (!set_use_index || ii.getId() < min_id
1232
17426
        || (ii.getId() == min_id && ipii.d_index > max_index))
1233
    {
1234
3399
      min_id = ii.getId();
1235
3399
      max_index = ipii.d_index;
1236
3399
      use_index = i;
1237
3399
      set_use_index = true;
1238
    }
1239
  }
1240
2121
  Trace("strings-solve") << "...choose #" << use_index << std::endl;
1241
2121
  if (!processInferInfo(pinfer[use_index]))
1242
  {
1243
    Unhandled() << "Failed to process infer info " << pinfer[use_index].d_infer
1244
                << std::endl;
1245
  }
1246
}
1247
1248
18683
void CoreSolver::processSimpleNEq(NormalForm& nfi,
1249
                                  NormalForm& nfj,
1250
                                  unsigned& index,
1251
                                  bool isRev,
1252
                                  unsigned rproc,
1253
                                  std::vector<CoreInferInfo>& pinfer,
1254
                                  TypeNode stype)
1255
{
1256
18683
  NodeManager* nm = NodeManager::currentNM();
1257
37339
  Node emp = Word::mkEmptyWord(stype);
1258
1259
18683
  const std::vector<Node>& nfiv = nfi.d_nf;
1260
18683
  const std::vector<Node>& nfjv = nfj.d_nf;
1261
18683
  Assert(rproc <= nfiv.size() && rproc <= nfjv.size());
1262
  while (true)
1263
  {
1264
41683
    bool lhsDone = (index == (nfiv.size() - rproc));
1265
41683
    bool rhsDone = (index == (nfjv.size() - rproc));
1266
41683
    if (lhsDone && rhsDone)
1267
    {
1268
      // We are done with both normal forms
1269
      break;
1270
    }
1271
41683
    else if (lhsDone || rhsDone)
1272
    {
1273
      // Only one side is done so the remainder of the other side must be empty
1274
      NormalForm& nfk = index == (nfiv.size() - rproc) ? nfj : nfi;
1275
      std::vector<Node>& nfkv = nfk.d_nf;
1276
      unsigned index_k = index;
1277
      std::vector<Node> curr_exp;
1278
      NormalForm::getExplanationForPrefixEq(nfi, nfj, -1, -1, curr_exp);
1279
      while (!d_state.isInConflict() && index_k < (nfkv.size() - rproc))
1280
      {
1281
        // can infer that this string must be empty
1282
        Node eq = nfkv[index_k].eqNode(emp);
1283
        Assert(!d_state.areEqual(emp, nfkv[index_k]));
1284
        d_im.sendInference(curr_exp, eq, InferenceId::STRINGS_N_ENDPOINT_EMP, isRev);
1285
        index_k++;
1286
      }
1287
      break;
1288
    }
1289
1290
    // We have inferred that the normal forms up to position `index` are
1291
    // equivalent. Below, we refer to the components at the current position of
1292
    // the normal form as `x` and `y`.
1293
    //
1294
    // E.g. x ++ ... = y ++ ...
1295
41683
    Node x = nfiv[index];
1296
41683
    Node y = nfjv[index];
1297
83366
    Trace("strings-solve-debug")
1298
41683
        << "Process " << x << " ... " << y << std::endl;
1299
1300
60152
    if (x == y)
1301
    {
1302
      // The normal forms have the same term at the current position. We just
1303
      // continue with the next index. By construction of the normal forms, we
1304
      // end up in this case if the two components are equal according to the
1305
      // equality engine (i.e. we cannot have different x and y terms that are
1306
      // equal in the equality engine).
1307
      //
1308
      // E.g. x ++ x' ++ ... = x ++ y' ++ ... ---> x' ++ ... = y' ++ ...
1309
36938
      Trace("strings-solve-debug")
1310
18469
          << "Simple Case 1 : strings are equal" << std::endl;
1311
18469
      index++;
1312
18469
      continue;
1313
    }
1314
23214
    Assert(!d_state.areEqual(x, y));
1315
1316
23214
    std::vector<Node> lenExp;
1317
23214
    Node xLenTerm = d_state.getLength(x, lenExp);
1318
23214
    Node yLenTerm = d_state.getLength(y, lenExp);
1319
1320
23214
    if (d_state.areEqual(xLenTerm, yLenTerm))
1321
    {
1322
2582
      std::vector<Node> ant;
1323
2582
      NormalForm::getExplanationForPrefixEq(nfi, nfj, index, index, ant);
1324
2582
      if (x.isConst() && y.isConst())
1325
      {
1326
        // if both are constant, it's just a constant conflict
1327
27
        d_im.sendInference(ant, d_false, InferenceId::STRINGS_N_CONST, isRev, true);
1328
27
        return;
1329
      }
1330
      // `x` and `y` have the same length. We infer that the two components
1331
      // have to be the same.
1332
      //
1333
      // E.g. x ++ ... = y ++ ... ^ len(x) = len(y) ---> x = y
1334
5110
      Trace("strings-solve-debug")
1335
2555
          << "Simple Case 2 : string lengths are equal" << std::endl;
1336
5110
      Node eq = x.eqNode(y);
1337
5110
      Node leneq = xLenTerm.eqNode(yLenTerm);
1338
2555
      lenExp.push_back(leneq);
1339
      // set the explanation for length
1340
5110
      Node lant = utils::mkAnd(lenExp);
1341
2555
      ant.push_back(lant);
1342
2555
      d_im.sendInference(ant, eq, InferenceId::STRINGS_N_UNIFY, isRev);
1343
2555
      break;
1344
    }
1345
53091
    else if ((!x.isConst() && index == nfiv.size() - rproc - 1)
1346
40962
             || (!y.isConst() && index == nfjv.size() - rproc - 1))
1347
    {
1348
      // We have reached the last component in one of the normal forms and it
1349
      // is not a constant. Thus, the last component must be equal to the
1350
      // remainder of the other normal form.
1351
      //
1352
      // E.g. x = y ++ y' ---> x = y ++ y'
1353
1876
      Trace("strings-solve-debug")
1354
938
          << "Simple Case 3 : at endpoint" << std::endl;
1355
1876
      Node eqn[2];
1356
2814
      for (unsigned r = 0; r < 2; r++)
1357
      {
1358
1876
        const NormalForm& nfk = r == 0 ? nfi : nfj;
1359
1876
        const std::vector<Node>& nfkv = nfk.d_nf;
1360
3752
        std::vector<Node> eqnc;
1361
4735
        for (unsigned i = index, size = (nfkv.size() - rproc); i < size; i++)
1362
        {
1363
2859
          if (isRev)
1364
          {
1365
1291
            eqnc.insert(eqnc.begin(), nfkv[i]);
1366
          }
1367
          else
1368
          {
1369
1568
            eqnc.push_back(nfkv[i]);
1370
          }
1371
        }
1372
1876
        eqn[r] = utils::mkNConcat(eqnc, stype);
1373
      }
1374
1876
      Trace("strings-solve-debug")
1375
938
          << "Endpoint eq check: " << eqn[0] << " " << eqn[1] << std::endl;
1376
938
      if (!d_state.areEqual(eqn[0], eqn[1]))
1377
      {
1378
1876
        std::vector<Node> antec;
1379
938
        NormalForm::getExplanationForPrefixEq(nfi, nfj, -1, -1, antec);
1380
1876
        d_im.sendInference(antec,
1381
1876
                           eqn[0].eqNode(eqn[1]),
1382
                           InferenceId::STRINGS_N_ENDPOINT_EQ,
1383
                           isRev,
1384
                           true);
1385
      }
1386
      else
1387
      {
1388
        Assert(nfiv.size() == nfjv.size());
1389
        index = nfiv.size() - rproc;
1390
      }
1391
938
      break;
1392
    }
1393
19694
    else if (x.isConst() && y.isConst())
1394
    {
1395
      // Constants in both normal forms.
1396
      //
1397
      // E.g. "abc" ++ ... = "ab" ++ ...
1398
4565
      size_t lenX = Word::getLength(x);
1399
4565
      size_t lenY = Word::getLength(y);
1400
9130
      Trace("strings-solve-debug")
1401
4565
          << "Simple Case 4 : Const Split : " << x << " vs " << y
1402
4565
          << " at index " << index << ", isRev = " << isRev << std::endl;
1403
4565
      size_t minLen = std::min(lenX, lenY);
1404
      bool isSameFix =
1405
4565
          isRev ? Word::rstrncmp(x, y, minLen) : Word::strncmp(x, y, minLen);
1406
4565
      if (isSameFix)
1407
      {
1408
        // The shorter constant is a prefix/suffix of the longer constant. We
1409
        // split the longer constant into the shared part and the remainder and
1410
        // continue from there.
1411
        //
1412
        // E.g. "abc" ++ x' ++ ... = "ab" ++ y' ++ ... --->
1413
        //      "c" ++ x' ++ ... = y' ++ ...
1414
4531
        bool xShorter = lenX < lenY;
1415
4531
        NormalForm& nfl = xShorter ? nfj : nfi;
1416
9062
        Node cl = xShorter ? y : x;
1417
9062
        Node ns = xShorter ? x : y;
1418
1419
9062
        Node remainderStr;
1420
4531
        if (isRev)
1421
        {
1422
3670
          size_t newlen = std::max(lenX, lenY) - minLen;
1423
3670
          remainderStr = Word::substr(cl, 0, newlen);
1424
        }
1425
        else
1426
        {
1427
861
          remainderStr = Word::substr(cl, minLen);
1428
        }
1429
9062
        Trace("strings-solve-debug-test")
1430
4531
            << "Break normal form of " << cl << " into " << ns << ", "
1431
4531
            << remainderStr << std::endl;
1432
4531
        nfl.splitConstant(index, ns, remainderStr);
1433
4531
        index++;
1434
4531
        continue;
1435
      }
1436
      else
1437
      {
1438
        // Conflict because the shorter constant is not a prefix/suffix of the
1439
        // other.
1440
        //
1441
        // E.g. "abc" ++ ... = "bc" ++ ... ---> conflict
1442
68
        std::vector<Node> antec;
1443
34
        NormalForm::getExplanationForPrefixEq(nfi, nfj, index, index, antec);
1444
34
        d_im.sendInference(antec, d_false, InferenceId::STRINGS_N_CONST, isRev, true);
1445
34
        break;
1446
      }
1447
    }
1448
1449
    // The candidate inference "info"
1450
30258
    CoreInferInfo info(InferenceId::UNKNOWN);
1451
15129
    InferInfo& iinfo = info.d_infer;
1452
15129
    info.d_index = index;
1453
    // for debugging
1454
15129
    info.d_i = nfi.d_base;
1455
15129
    info.d_j = nfj.d_base;
1456
15129
    info.d_rev = isRev;
1457
15129
    Assert(index < nfiv.size() - rproc && index < nfjv.size() - rproc);
1458
70662
    if (!d_state.areDisequal(xLenTerm, yLenTerm) && !d_state.areEqual(xLenTerm, yLenTerm)
1459
10146
        && !x.isConst()
1460
67592
        && !y.isConst())  // AJR: remove the latter 2 conditions?
1461
    {
1462
      // We don't know whether `x` and `y` have the same length or not. We
1463
      // split on whether they are equal or not (note that splitting on
1464
      // equality between strings is worse since it is harder to process).
1465
      //
1466
      // E.g. x ++ ... = y ++ ... ---> (len(x) = len(y)) v (len(x) != len(y))
1467
12236
      Trace("strings-solve-debug")
1468
6118
          << "Non-simple Case 1 : string lengths neither equal nor disequal"
1469
6118
          << std::endl;
1470
12236
      Node lenEq = nm->mkNode(EQUAL, xLenTerm, yLenTerm);
1471
6118
      lenEq = Rewriter::rewrite(lenEq);
1472
6118
      iinfo.d_conc = nm->mkNode(OR, lenEq, lenEq.negate());
1473
6118
      iinfo.setId(InferenceId::STRINGS_LEN_SPLIT);
1474
6118
      info.d_pendingPhase[lenEq] = true;
1475
6118
      pinfer.push_back(info);
1476
6118
      break;
1477
    }
1478
1479
18022
    Trace("strings-solve-debug")
1480
9011
        << "Non-simple Case 2 : must compare strings" << std::endl;
1481
9011
    int lhsLoopIdx = -1;
1482
9011
    int rhsLoopIdx = -1;
1483
9011
    if (detectLoop(nfi, nfj, index, lhsLoopIdx, rhsLoopIdx, rproc))
1484
    {
1485
      // We are dealing with a looping word equation.
1486
      // Note we could make this code also run in the reverse direction, but
1487
      // this is not implemented currently.
1488
472
      if (!isRev)
1489
      {
1490
        // add temporarily to the antecedant of iinfo.
1491
417
        NormalForm::getExplanationForPrefixEq(nfi, nfj, -1, -1, iinfo.d_premises);
1492
        ProcessLoopResult plr =
1493
1251
            processLoop(lhsLoopIdx != -1 ? nfi : nfj,
1494
417
                        lhsLoopIdx != -1 ? nfj : nfi,
1495
417
                        lhsLoopIdx != -1 ? lhsLoopIdx : rhsLoopIdx,
1496
417
                        index,
1497
417
                        info);
1498
417
        if (plr == ProcessLoopResult::INFERENCE)
1499
        {
1500
417
          pinfer.push_back(info);
1501
417
          break;
1502
        }
1503
        else if (plr == ProcessLoopResult::CONFLICT)
1504
        {
1505
          break;
1506
        }
1507
        Assert(plr == ProcessLoopResult::SKIPPED);
1508
        // not processing an inference here, undo changes to ant
1509
        iinfo.d_premises.clear();
1510
      }
1511
    }
1512
1513
    // AJR: length entailment here?
1514
8594
    if (x.isConst() || y.isConst())
1515
    {
1516
      // Below, we deal with the case where `x` or `y` is a constant string. We
1517
      // refer to the non-constant component as `nc` below.
1518
      //
1519
      // E.g. "abc" ++ ... = nc ++ ...
1520
4973
      Assert(!x.isConst() || !y.isConst());
1521
4973
      NormalForm& nfc = x.isConst() ? nfi : nfj;
1522
4973
      const std::vector<Node>& nfcv = nfc.d_nf;
1523
4973
      NormalForm& nfnc = x.isConst() ? nfj : nfi;
1524
4973
      const std::vector<Node>& nfncv = nfnc.d_nf;
1525
9946
      Node nc = nfncv[index];
1526
4973
      Assert(!nc.isConst()) << "Other string is not constant.";
1527
4973
      Assert(nc.getKind() != STRING_CONCAT) << "Other string is not CONCAT.";
1528
      // explanation for why nc is non-empty
1529
9946
      Node expNonEmpty = d_state.explainNonEmpty(nc);
1530
4973
      if (expNonEmpty.isNull())
1531
      {
1532
        // The non-constant side may be equal to the empty string. Split on
1533
        // whether it is.
1534
        //
1535
        // E.g. "abc" ++ ... = nc ++ ... ---> (nc = "") v (nc != "")
1536
10
        Node eq = nc.eqNode(emp);
1537
5
        eq = Rewriter::rewrite(eq);
1538
5
        if (eq.isConst())
1539
        {
1540
          // If the equality rewrites to a constant, we must use the
1541
          // purify variable for this string to communicate that
1542
          // we have inferred whether it is empty.
1543
          SkolemCache* skc = d_termReg.getSkolemCache();
1544
          Node p = skc->mkSkolemCached(nc, SkolemCache::SK_PURIFY, "lsym");
1545
          Node pEq = p.eqNode(emp);
1546
          // should not be constant
1547
          Assert(!Rewriter::rewrite(pEq).isConst());
1548
          // infer the purification equality, and the (dis)equality
1549
          // with the empty string in the direction that the rewriter
1550
          // inferred
1551
          iinfo.d_conc = nm->mkNode(
1552
              AND, p.eqNode(nc), !eq.getConst<bool>() ? pEq.negate() : pEq);
1553
          iinfo.setId(InferenceId::STRINGS_INFER_EMP);
1554
        }
1555
        else
1556
        {
1557
5
          iinfo.d_conc = nm->mkNode(OR, eq, eq.negate());
1558
5
          iinfo.setId(InferenceId::STRINGS_LEN_SPLIT_EMP);
1559
        }
1560
5
        pinfer.push_back(info);
1561
5
        break;
1562
      }
1563
1564
      // At this point, we know that `nc` is non-empty, so we add expNonEmpty
1565
      // to our explanation below. We do this after adding other parts of the
1566
      // explanation for consistency with other inferences.
1567
1568
4968
      size_t ncIndex = index + 1;
1569
9936
      Node nextConstStr = nfnc.collectConstantStringAt(ncIndex);
1570
4968
      if (!nextConstStr.isNull())
1571
      {
1572
        // We are in the case where we have a constant after `nc`, so we
1573
        // split `nc`.
1574
        //
1575
        // E.g. "abc" ++ ... = nc ++ "b" ++ ... ---> nc = "a" ++ k
1576
2459
        size_t cIndex = index;
1577
4022
        Node stra = nfc.collectConstantStringAt(cIndex);
1578
2459
        Assert(!stra.isNull());
1579
4022
        Node strb = nextConstStr;
1580
1581
        // Since `nc` is non-empty, we use the non-empty overlap
1582
2459
        size_t p = getSufficientNonEmptyOverlap(stra, strb, isRev);
1583
1584
        // If we can't split off more than a single character from the
1585
        // constant, we might as well do regular constant/non-constant
1586
        // inference (see below).
1587
2459
        if (p > 1)
1588
        {
1589
896
          NormalForm::getExplanationForPrefixEq(
1590
              nfc, nfnc, cIndex, ncIndex, iinfo.d_premises);
1591
896
          iinfo.d_premises.push_back(expNonEmpty);
1592
          // make the conclusion
1593
896
          SkolemCache* skc = d_termReg.getSkolemCache();
1594
          Node xcv =
1595
1792
              nm->mkNode(STRING_CONCAT, isRev ? strb : nc, isRev ? nc : strb);
1596
1792
          std::vector<Node> newSkolems;
1597
1792
          iinfo.d_conc = getConclusion(
1598
896
              xcv, stra, PfRule::CONCAT_CPROP, isRev, skc, newSkolems);
1599
896
          Assert(newSkolems.size() == 1);
1600
896
          iinfo.d_skolems[LENGTH_SPLIT].push_back(newSkolems[0]);
1601
896
          iinfo.setId(InferenceId::STRINGS_SSPLIT_CST_PROP);
1602
896
          iinfo.d_idRev = isRev;
1603
896
          pinfer.push_back(info);
1604
896
          break;
1605
        }
1606
      }
1607
1608
      // Since none of the other inferences apply, we just infer that `nc` has
1609
      // to start with the first character of the constant.
1610
      //
1611
      // E.g. "abc" ++ ... = nc ++ ... ---> nc = "a" ++ k
1612
4072
      SkolemCache* skc = d_termReg.getSkolemCache();
1613
8144
      std::vector<Node> newSkolems;
1614
12216
      iinfo.d_conc = getConclusion(
1615
8144
          nc, nfcv[index], PfRule::CONCAT_CSPLIT, isRev, skc, newSkolems);
1616
12216
      NormalForm::getExplanationForPrefixEq(
1617
8144
          nfi, nfj, index, index, iinfo.d_premises);
1618
4072
      iinfo.d_premises.push_back(expNonEmpty);
1619
4072
      Assert(newSkolems.size() == 1);
1620
4072
      iinfo.d_skolems[LENGTH_SPLIT].push_back(newSkolems[0]);
1621
4072
      iinfo.setId(InferenceId::STRINGS_SSPLIT_CST);
1622
4072
      iinfo.d_idRev = isRev;
1623
4072
      pinfer.push_back(info);
1624
4072
      break;
1625
    }
1626
1627
    // Below, we deal with the case where `x` and `y` are two non-constant
1628
    // terms of different lengths. In this case, we have to split on which term
1629
    // is a prefix/suffix of the other.
1630
    //
1631
    // E.g. x ++ ... = y ++ ... ---> (x = y ++ k) v (y = x ++ k)
1632
3621
    Assert(d_state.areDisequal(xLenTerm, yLenTerm));
1633
3621
    Assert(!x.isConst());
1634
3621
    Assert(!y.isConst());
1635
1636
3621
    int32_t lentTestSuccess = -1;
1637
7242
    Node lenConstraint;
1638
3621
    if (options::stringCheckEntailLen())
1639
    {
1640
      // If length entailment checks are enabled, we can save the case split by
1641
      // inferring that `x` has to be longer than `y` or vice-versa.
1642
8836
      for (size_t e = 0; e < 2; e++)
1643
      {
1644
11925
        Node t = e == 0 ? x : y;
1645
        // do not infer constants are larger than variables
1646
6710
        if (!t.isConst())
1647
        {
1648
11925
          Node lt1 = e == 0 ? xLenTerm : yLenTerm;
1649
11925
          Node lt2 = e == 0 ? yLenTerm : xLenTerm;
1650
11925
          Node entLit = Rewriter::rewrite(nm->mkNode(GT, lt1, lt2));
1651
6710
          std::pair<bool, Node> et = d_state.entailmentCheck(
1652
11925
              options::TheoryOfMode::THEORY_OF_TYPE_BASED, entLit);
1653
6710
          if (et.first)
1654
          {
1655
2990
            Trace("strings-entail")
1656
1495
                << "Strings entailment : " << entLit
1657
1495
                << " is entailed in the current context." << std::endl;
1658
2990
            Trace("strings-entail")
1659
1495
                << "  explanation was : " << et.second << std::endl;
1660
1495
            lentTestSuccess = e;
1661
1495
            lenConstraint = entLit;
1662
            // Its not explained by the equality engine of this class, so its
1663
            // marked as not being explained. The length constraint is
1664
            // additionally being saved and added to the length constraint
1665
            // vector lcVec below, which is added to iinfo.d_ant below. Length
1666
            // constraints are being added as the last antecedant for the sake
1667
            // of proof reconstruction, which expect length constraints to come
1668
            // last.
1669
1495
            iinfo.d_noExplain.push_back(lenConstraint);
1670
1495
            break;
1671
          }
1672
        }
1673
      }
1674
    }
1675
    // lcVec stores the length constraint portion of the antecedant.
1676
7242
    std::vector<Node> lcVec;
1677
3621
    if (lenConstraint.isNull())
1678
    {
1679
      // will do split on length
1680
2126
      lenConstraint = nm->mkNode(EQUAL, xLenTerm, yLenTerm).negate();
1681
2126
      lcVec.push_back(lenConstraint);
1682
    }
1683
    else
1684
    {
1685
1495
      utils::flattenOp(AND, lenConstraint, lcVec);
1686
    }
1687
1688
3621
    NormalForm::getExplanationForPrefixEq(nfi, nfj, index, index, iinfo.d_premises);
1689
    // Add premises for x != "" ^ y != ""
1690
10863
    for (unsigned xory = 0; xory < 2; xory++)
1691
    {
1692
14484
      Node t = xory == 0 ? x : y;
1693
14484
      Node tnz = d_state.explainNonEmpty(t);
1694
7242
      if (!tnz.isNull())
1695
      {
1696
7242
        lcVec.push_back(tnz);
1697
      }
1698
      else
1699
      {
1700
        tnz = x.eqNode(emp).negate();
1701
        lcVec.push_back(tnz);
1702
        iinfo.d_noExplain.push_back(tnz);
1703
      }
1704
    }
1705
3621
    SkolemCache* skc = d_termReg.getSkolemCache();
1706
7242
    std::vector<Node> newSkolems;
1707
    // make the conclusion
1708
3621
    if (lentTestSuccess == -1)
1709
    {
1710
2126
      iinfo.setId(InferenceId::STRINGS_SSPLIT_VAR);
1711
2126
      iinfo.d_conc =
1712
4252
          getConclusion(x, y, PfRule::CONCAT_SPLIT, isRev, skc, newSkolems);
1713
    }
1714
1495
    else if (lentTestSuccess == 0)
1715
    {
1716
532
      iinfo.setId(InferenceId::STRINGS_SSPLIT_VAR_PROP);
1717
532
      iinfo.d_conc =
1718
1064
          getConclusion(x, y, PfRule::CONCAT_LPROP, isRev, skc, newSkolems);
1719
    }
1720
    else
1721
    {
1722
963
      Assert(lentTestSuccess == 1);
1723
963
      iinfo.setId(InferenceId::STRINGS_SSPLIT_VAR_PROP);
1724
963
      iinfo.d_conc =
1725
1926
          getConclusion(y, x, PfRule::CONCAT_LPROP, isRev, skc, newSkolems);
1726
    }
1727
    // add the length constraint(s) as the last antecedant
1728
7242
    Node lc = utils::mkAnd(lcVec);
1729
3621
    iinfo.d_premises.push_back(lc);
1730
3621
    iinfo.d_idRev = isRev;
1731
3621
    pinfer.push_back(info);
1732
3621
    break;
1733
23000
  }
1734
}
1735
1736
9011
bool CoreSolver::detectLoop(NormalForm& nfi,
1737
                               NormalForm& nfj,
1738
                               int index,
1739
                               int& loop_in_i,
1740
                               int& loop_in_j,
1741
                               unsigned rproc)
1742
{
1743
9011
  int has_loop[2] = { -1, -1 };
1744
27033
  for (unsigned r = 0; r < 2; r++)
1745
  {
1746
18022
    NormalForm& nf = r == 0 ? nfi : nfj;
1747
18022
    NormalForm& nfo = r == 0 ? nfj : nfi;
1748
18022
    std::vector<Node>& nfv = nf.d_nf;
1749
18022
    std::vector<Node>& nfov = nfo.d_nf;
1750
18022
    if (nfov[index].isConst())
1751
    {
1752
5019
      continue;
1753
    }
1754
47668
    for (unsigned lp = index + 1, lpEnd = nfv.size() - rproc; lp < lpEnd; lp++)
1755
    {
1756
35137
      if (nfv[lp] == nfov[index])
1757
      {
1758
472
        has_loop[r] = lp;
1759
472
        break;
1760
      }
1761
    }
1762
  }
1763
9011
  if( has_loop[0]!=-1 || has_loop[1]!=-1 ) {
1764
472
    loop_in_i = has_loop[0];
1765
472
    loop_in_j = has_loop[1];
1766
472
    return true;
1767
  } else {
1768
8539
    Trace("strings-solve-debug") << "No loops detected." << std::endl;
1769
8539
    return false;
1770
  }
1771
}
1772
1773
//xs(zy)=t(yz)xr
1774
417
CoreSolver::ProcessLoopResult CoreSolver::processLoop(NormalForm& nfi,
1775
                                                      NormalForm& nfj,
1776
                                                      int loop_index,
1777
                                                      int index,
1778
                                                      CoreInferInfo& info)
1779
{
1780
417
  NodeManager* nm = NodeManager::currentNM();
1781
834
  Node conc;
1782
417
  const std::vector<Node>& veci = nfi.d_nf;
1783
417
  const std::vector<Node>& vecoi = nfj.d_nf;
1784
1785
834
  TypeNode stype = veci[loop_index].getType();
1786
1787
417
  if (options::stringProcessLoopMode() == options::ProcessLoopMode::ABORT)
1788
  {
1789
    throw LogicException("Looping word equation encountered.");
1790
  }
1791
834
  else if (options::stringProcessLoopMode() == options::ProcessLoopMode::NONE
1792
417
           || stype.isSequence())
1793
  {
1794
    // note we cannot convert looping word equations into regular expressions if
1795
    // we are handling sequences, since there is no analog for regular
1796
    // expressions over sequences currently
1797
    d_im.setIncomplete(IncompleteId::STRINGS_LOOP_SKIP);
1798
    return ProcessLoopResult::SKIPPED;
1799
  }
1800
1801
834
  Trace("strings-loop") << "Detected possible loop for " << veci[loop_index]
1802
417
                        << std::endl;
1803
417
  Trace("strings-loop") << " ... (X)= " << vecoi[index] << std::endl;
1804
417
  Trace("strings-loop") << " ... T(Y.Z)= ";
1805
834
  std::vector<Node> vec_t(veci.begin() + index, veci.begin() + loop_index);
1806
834
  Node t_yz = utils::mkNConcat(vec_t, stype);
1807
417
  Trace("strings-loop") << " (" << t_yz << ")" << std::endl;
1808
417
  Trace("strings-loop") << " ... S(Z.Y)= ";
1809
834
  std::vector<Node> vec_s(vecoi.begin() + index + 1, vecoi.end());
1810
834
  Node s_zy = utils::mkNConcat(vec_s, stype);
1811
417
  Trace("strings-loop") << s_zy << std::endl;
1812
417
  Trace("strings-loop") << " ... R= ";
1813
834
  std::vector<Node> vec_r(veci.begin() + loop_index + 1, veci.end());
1814
834
  Node r = utils::mkNConcat(vec_r, stype);
1815
417
  Trace("strings-loop") << r << std::endl;
1816
1817
834
  Node emp = Word::mkEmptyWord(stype);
1818
1819
417
  InferInfo& iinfo = info.d_infer;
1820
417
  if (s_zy.isConst() && r.isConst() && r != emp)
1821
  {
1822
    int c;
1823
18
    bool flag = true;
1824
18
    if (s_zy.getConst<String>().tailcmp(r.getConst<String>(), c))
1825
    {
1826
18
      if (c >= 0)
1827
      {
1828
18
        s_zy = Word::substr(s_zy, 0, c);
1829
18
        r = emp;
1830
18
        vec_r.clear();
1831
36
        Trace("strings-loop") << "Strings::Loop: Refactor S(Z.Y)= " << s_zy
1832
18
                              << ", c=" << c << std::endl;
1833
18
        flag = false;
1834
      }
1835
    }
1836
18
    if (flag)
1837
    {
1838
      Trace("strings-loop") << "Strings::Loop: tails are different."
1839
                            << std::endl;
1840
      d_im.sendInference(
1841
          iinfo.d_premises, conc, InferenceId::STRINGS_FLOOP_CONFLICT, false, true);
1842
      return ProcessLoopResult::CONFLICT;
1843
    }
1844
  }
1845
1846
834
  Node split_eq;
1847
1249
  for (unsigned i = 0; i < 2; i++)
1848
  {
1849
1666
    Node t = i == 0 ? veci[loop_index] : t_yz;
1850
834
    split_eq = t.eqNode(emp);
1851
1666
    Node split_eqr = Rewriter::rewrite(split_eq);
1852
    // the equality could rewrite to false
1853
834
    if (!split_eqr.isConst())
1854
    {
1855
1586
      Node expNonEmpty = d_state.explainNonEmpty(t);
1856
794
      if (expNonEmpty.isNull())
1857
      {
1858
        // no antecedants necessary
1859
2
        iinfo.d_premises.clear();
1860
        // try to make t equal to empty to avoid loop
1861
2
        iinfo.d_conc = nm->mkNode(kind::OR, split_eq, split_eq.negate());
1862
2
        iinfo.setId(InferenceId::STRINGS_LEN_SPLIT_EMP);
1863
2
        return ProcessLoopResult::INFERENCE;
1864
      }
1865
      else
1866
      {
1867
792
        iinfo.d_premises.push_back(expNonEmpty);
1868
      }
1869
    }
1870
    else
1871
    {
1872
40
      Assert(!split_eqr.getConst<bool>());
1873
    }
1874
  }
1875
1876
830
  Node str_in_re;
1877
850
  if (s_zy == t_yz && r == emp && s_zy.isConst()
1878
435
      && s_zy.getConst<String>().isRepeated())
1879
  {
1880
28
    Node rep_c = Word::substr(s_zy, 0, 1);
1881
28
    Trace("strings-loop") << "Special case (X)=" << vecoi[index] << " "
1882
14
                          << std::endl;
1883
14
    Trace("strings-loop") << "... (C)=" << rep_c << " " << std::endl;
1884
    // special case
1885
28
    str_in_re = nm->mkNode(
1886
        STRING_IN_REGEXP,
1887
14
        vecoi[index],
1888
28
        nm->mkNode(REGEXP_STAR, nm->mkNode(STRING_TO_REGEXP, rep_c)));
1889
14
    conc = str_in_re;
1890
  }
1891
401
  else if (t_yz.isConst())
1892
  {
1893
52
    Trace("strings-loop") << "Strings::Loop: Const Normal Breaking."
1894
26
                          << std::endl;
1895
26
    unsigned size = Word::getLength(t_yz);
1896
52
    std::vector<Node> vconc;
1897
98
    for (unsigned len = 1; len <= size; len++)
1898
    {
1899
124
      Node y = Word::substr(t_yz, 0, len);
1900
124
      Node z = Word::substr(t_yz, len, size - len);
1901
124
      Node restr = s_zy;
1902
124
      Node cc;
1903
72
      if (r != emp)
1904
      {
1905
64
        std::vector<Node> v2(vec_r);
1906
32
        v2.insert(v2.begin(), y);
1907
32
        v2.insert(v2.begin(), z);
1908
32
        restr = utils::mkNConcat(z, y);
1909
32
        cc = Rewriter::rewrite(s_zy.eqNode(utils::mkNConcat(v2, stype)));
1910
      }
1911
      else
1912
      {
1913
40
        cc = Rewriter::rewrite(s_zy.eqNode(utils::mkNConcat(z, y)));
1914
      }
1915
72
      if (cc == d_false)
1916
      {
1917
20
        continue;
1918
      }
1919
      Node conc2 = nm->mkNode(
1920
          STRING_IN_REGEXP,
1921
52
          vecoi[index],
1922
208
          nm->mkNode(
1923
              REGEXP_CONCAT,
1924
104
              nm->mkNode(STRING_TO_REGEXP, y),
1925
260
              nm->mkNode(REGEXP_STAR, nm->mkNode(STRING_TO_REGEXP, restr))));
1926
52
      cc = cc == d_true ? conc2 : nm->mkNode(kind::AND, cc, conc2);
1927
52
      vconc.push_back(cc);
1928
    }
1929
42
    conc = vconc.size() == 0 ? Node::null() : vconc.size() == 1
1930
16
                                                  ? vconc[0]
1931
                                                  : nm->mkNode(kind::OR, vconc);
1932
  }
1933
  else
1934
  {
1935
750
    if (options::stringProcessLoopMode()
1936
375
        == options::ProcessLoopMode::SIMPLE_ABORT)
1937
    {
1938
      throw LogicException("Normal looping word equation encountered.");
1939
    }
1940
750
    else if (options::stringProcessLoopMode()
1941
375
             == options::ProcessLoopMode::SIMPLE)
1942
    {
1943
      d_im.setIncomplete(IncompleteId::STRINGS_LOOP_SKIP);
1944
      return ProcessLoopResult::SKIPPED;
1945
    }
1946
1947
750
    Trace("strings-loop") << "Strings::Loop: Normal Loop Breaking."
1948
375
                          << std::endl;
1949
    // right
1950
375
    SkolemCache* skc = d_termReg.getSkolemCache();
1951
750
    Node sk_w = skc->mkSkolem("w_loop");
1952
750
    Node sk_y = skc->mkSkolem("y_loop");
1953
375
    iinfo.d_skolems[LENGTH_GEQ_ONE].push_back(sk_y);
1954
750
    Node sk_z = skc->mkSkolem("z_loop");
1955
    // t1 * ... * tn = y * z
1956
750
    Node conc1 = t_yz.eqNode(utils::mkNConcat(sk_y, sk_z));
1957
    // s1 * ... * sk = z * y * r
1958
375
    vec_r.insert(vec_r.begin(), sk_y);
1959
375
    vec_r.insert(vec_r.begin(), sk_z);
1960
750
    Node conc2 = s_zy.eqNode(utils::mkNConcat(vec_r, stype));
1961
750
    Node conc3 = vecoi[index].eqNode(utils::mkNConcat(sk_y, sk_w));
1962
750
    Node restr = r == emp ? s_zy : utils::mkNConcat(sk_z, sk_y);
1963
375
    str_in_re =
1964
750
        nm->mkNode(kind::STRING_IN_REGEXP,
1965
                   sk_w,
1966
750
                   nm->mkNode(kind::REGEXP_STAR,
1967
750
                              nm->mkNode(kind::STRING_TO_REGEXP, restr)));
1968
1969
750
    std::vector<Node> vec_conc;
1970
375
    vec_conc.push_back(conc1);
1971
375
    vec_conc.push_back(conc2);
1972
375
    vec_conc.push_back(conc3);
1973
375
    vec_conc.push_back(str_in_re);
1974
375
    conc = nm->mkNode(kind::AND, vec_conc);
1975
  }  // normal case
1976
1977
  // we will be done
1978
415
  iinfo.d_conc = conc;
1979
415
  iinfo.setId(InferenceId::STRINGS_FLOOP);
1980
415
  info.d_nfPair[0] = nfi.d_base;
1981
415
  info.d_nfPair[1] = nfj.d_base;
1982
415
  return ProcessLoopResult::INFERENCE;
1983
}
1984
1985
5818
void CoreSolver::processDeq(Node ni, Node nj)
1986
{
1987
5818
  NodeManager* nm = NodeManager::currentNM();
1988
5818
  NormalForm& nfni = getNormalForm(ni);
1989
5818
  NormalForm& nfnj = getNormalForm(nj);
1990
1991
5818
  if (nfni.d_nf.size() <= 1 && nfnj.d_nf.size() <= 1)
1992
  {
1993
    // If normal forms have size <=1, then we are comparing either:
1994
    // (1) variable to variable,
1995
    // (2) variable to constant-like (empty, constant string/seq or SEQ_UNIT),
1996
    // (3) SEQ_UNIT to constant-like.
1997
    // We only have to process (3) here, since disequalities of the form of (1)
1998
    // and (2) are satisfied by construction.
1999
12646
    for (size_t i = 0; i < 2; i++)
2000
    {
2001
8440
      NormalForm& nfc = i == 0 ? nfni : nfnj;
2002
8440
      if (nfc.d_nf.size() == 0 || nfc.d_nf[0].getKind() != SEQ_UNIT)
2003
      {
2004
        // may need to look at the other side
2005
8424
        continue;
2006
      }
2007
16
      Node u = nfc.d_nf[0];
2008
      // if the other side is constant like
2009
16
      NormalForm& nfo = i == 0 ? nfnj : nfni;
2010
16
      if (nfo.d_nf.size() == 0 || !utils::isConstantLike(nfo.d_nf[0]))
2011
      {
2012
6
        break;
2013
      }
2014
      // v is the other side (a possibly constant seq.unit term)
2015
10
      Node v = nfo.d_nf[0];
2016
10
      Node vc;
2017
10
      if (v.isConst())
2018
      {
2019
        // get the list of characters (strings of length 1)
2020
20
        std::vector<Node> vchars = Word::getChars(v);
2021
10
        if (vchars.size() != 1)
2022
        {
2023
          // constant of size != 1, disequality is satisfied
2024
          break;
2025
        }
2026
        // get the element of the character
2027
10
        vc = vchars[0].getConst<Sequence>().getVec()[0];
2028
      }
2029
      else
2030
      {
2031
        Assert(v.getKind() == SEQ_UNIT);
2032
        vc = v[0];
2033
      }
2034
10
      Assert(u[0].getType() == vc.getType());
2035
      // if already disequal, we are done
2036
10
      if (d_state.areDisequal(u[0], vc))
2037
      {
2038
6
        break;
2039
      }
2040
      // seq.unit(x) != seq.unit(y) => x != y
2041
      // Notice this is a special case, since the code below would justify this
2042
      // disequality by reasoning that a component is disequal. However, the
2043
      // disequality components are the entire disequality.
2044
8
      Node deq = u.eqNode(v).notNode();
2045
8
      std::vector<Node> premises;
2046
4
      premises.push_back(deq);
2047
8
      Node conc = u[0].eqNode(vc).notNode();
2048
4
      d_im.sendInference(premises, conc, InferenceId::STRINGS_UNIT_INJ_DEQ, false, true);
2049
4
      return;
2050
    }
2051
4218
    Trace("strings-solve-debug") << "...trivial" << std::endl;
2052
4218
    return;
2053
  }
2054
2055
3192
  std::vector<Node> nfi = nfni.d_nf;
2056
3192
  std::vector<Node> nfj = nfnj.d_nf;
2057
2058
  // See if one side is constant, if so, the disequality ni != nj is satisfied
2059
  // if it cannot contain the other side.
2060
  //
2061
  // E.g. "abc" != x ++ "d" ++ y
2062
4514
  for (uint32_t i = 0; i < 2; i++)
2063
  {
2064
6066
    Node c = d_bsolver.getConstantEqc(i == 0 ? ni : nj);
2065
3148
    if (!c.isNull())
2066
    {
2067
      int findex, lindex;
2068
1135
      if (!StringsEntail::canConstantContainList(
2069
              c, i == 0 ? nfj : nfi, findex, lindex))
2070
      {
2071
460
        Trace("strings-solve-debug")
2072
230
            << "Disequality: constant cannot contain list" << std::endl;
2073
230
        return;
2074
      }
2075
    }
2076
  }
2077
2078
1366
  if (processReverseDeq(nfi, nfj, ni, nj))
2079
  {
2080
363
    Trace("strings-solve-debug") << "...processed reverse" << std::endl;
2081
363
    return;
2082
  }
2083
2084
1003
  if (options::stringsDeqExt())
2085
  {
2086
    processDeqExtensionality(ni, nj);
2087
    return;
2088
  }
2089
2090
1003
  nfi = nfni.d_nf;
2091
1003
  nfj = nfnj.d_nf;
2092
2093
1003
  size_t index = 0;
2094
1003
  while (index < nfi.size() || index < nfj.size())
2095
  {
2096
1003
    if (processSimpleDeq(nfi, nfj, ni, nj, index, false))
2097
    {
2098
140
      Trace("strings-solve-debug") << "...processed simple" << std::endl;
2099
1143
      return;
2100
    }
2101
2102
    // We have inferred that the normal forms up to position `index` are
2103
    // equivalent. Below, we refer to the components at the current position of
2104
    // the normal form as `x` and `y`.
2105
    //
2106
    // E.g. x ++ ... = y ++ ...
2107
863
    Assert(index < nfi.size() && index < nfj.size());
2108
863
    Node x = nfi[index];
2109
863
    Node y = nfj[index];
2110
1726
    Trace("strings-solve-debug")
2111
863
        << "...Processing(DEQ) " << x << " " << y << std::endl;
2112
863
    if (d_state.areEqual(x, y))
2113
    {
2114
      // The normal forms have an equivalent term at `index` in the current
2115
      // context. We move to the next `index`.
2116
      //
2117
      // E.g. x ++ x' ++ ... != z ++ y' ++ ... ^ x = z
2118
      index++;
2119
      continue;
2120
    }
2121
2122
863
    Assert(!x.isConst() || !y.isConst());
2123
863
    std::vector<Node> lenExp;
2124
863
    Node xLenTerm = d_state.getLength(x, lenExp);
2125
863
    Node yLenTerm = d_state.getLength(y, lenExp);
2126
863
    if (d_state.areDisequal(xLenTerm, yLenTerm))
2127
    {
2128
      // Below, we deal with the cases where the components at the current
2129
      // index are of different lengths in the current context.
2130
      //
2131
      // E.g. x ++ x' ++ ... != y ++ y' ++ ... ^ len(x) != len(y)
2132
794
      if (x.isConst() || y.isConst())
2133
      {
2134
792
        Node ck = x.isConst() ? x : y;
2135
792
        Node nck = x.isConst() ? y : x;
2136
792
        Node nckLenTerm = x.isConst() ? yLenTerm : xLenTerm;
2137
792
        Node expNonEmpty = d_state.explainNonEmpty(nck);
2138
792
        if (expNonEmpty.isNull())
2139
        {
2140
          // Either `x` or `y` is a constant and the other may be equal to the
2141
          // empty string in the current context. We split on whether it
2142
          // actually is empty in that case.
2143
          //
2144
          // E.g. x ++ x' ++ ... != "abc" ++ y' ++ ... ^ len(x) != len(y) --->
2145
          //      x = "" v x != ""
2146
          Node emp = Word::mkEmptyWord(nck.getType());
2147
          d_im.sendSplit(nck, emp, InferenceId::STRINGS_DEQ_DISL_EMP_SPLIT);
2148
          return;
2149
        }
2150
2151
792
        Node firstChar = Word::getLength(ck) == 1 ? ck : Word::prefix(ck, 1);
2152
792
        if (d_state.areEqual(nckLenTerm, d_one))
2153
        {
2154
771
          if (d_state.areDisequal(firstChar, nck))
2155
          {
2156
            // Either `x` or `y` is a constant and the other is a string of
2157
            // length one. If the non-constant is disequal to the first
2158
            // character of the constant in the current context, we satisfy the
2159
            // disequality and there is nothing else to do.
2160
            //
2161
            // E.g. "d" ++ x' ++ ... != "abc" ++ y' ++ ... ^ len(x) = 1
2162
729
            return;
2163
          }
2164
42
          else if (!d_state.areEqual(firstChar, nck))
2165
          {
2166
            // Either `x` or `y` is a constant and the other is a string of
2167
            // length one. If we do not know whether the non-constant is equal
2168
            // or disequal to the first character in the constant, we split on
2169
            // whether it is.
2170
            //
2171
            // E.g. x ++ x' ++ ... != "abc" ++ y' ++ ... ^ len(x) = 1 --->
2172
            //      x = "a" v x != "a"
2173
42
            if (d_im.sendSplit(firstChar,
2174
                               nck,
2175
                               InferenceId::STRINGS_DEQ_DISL_FIRST_CHAR_EQ_SPLIT,
2176
                               false))
2177
            {
2178
42
              return;
2179
            }
2180
          }
2181
        }
2182
        else
2183
        {
2184
          // Either `x` or `y` is a constant and it is not know whether the
2185
          // non-empty non-constant is of length one. We split the non-constant
2186
          // into a string of length one and the remainder.
2187
          //
2188
          // len(x)>=1 => x = k1 ++ k2 ^ len(k1) = 1
2189
21
          SkolemCache* skc = d_termReg.getSkolemCache();
2190
42
          std::vector<Node> newSkolems;
2191
          Node conc = getDecomposeConclusion(
2192
42
              nck, d_one, false, skc, newSkolems);
2193
21
          Assert(newSkolems.size() == 2);
2194
42
          std::vector<Node> antecLen;
2195
21
          antecLen.push_back(nm->mkNode(GEQ, nckLenTerm, d_one));
2196
21
          d_im.sendInference(antecLen,
2197
                             antecLen,
2198
                             conc,
2199
                             InferenceId::STRINGS_DEQ_DISL_FIRST_CHAR_STRING_SPLIT,
2200
                             false,
2201
                             true);
2202
21
          return;
2203
        }
2204
      }
2205
      else
2206
      {
2207
        // `x` and `y` have different lengths in the current context and they
2208
        // are both non-constants. We split them into parts that have the same
2209
        // lengths.
2210
        //
2211
        // len(x) > len(y) => x = k1 ++ k2 ^ len(k1) = len(y)
2212
        // len(y) > len(x) => y = k3 ++ k4 ^ len(k3) = len(x)
2213
4
        Trace("strings-solve")
2214
2
            << "Non-Simple Case 1 : add lemmas " << std::endl;
2215
2
        SkolemCache* skc = d_termReg.getSkolemCache();
2216
2217
6
        for (unsigned r = 0; r < 2; r++)
2218
        {
2219
8
          Node ux = r == 0 ? x : y;
2220
8
          Node uy = r == 0 ? y : x;
2221
8
          Node uxLen = nm->mkNode(STRING_LENGTH, ux);
2222
8
          Node uyLen = nm->mkNode(STRING_LENGTH, uy);
2223
          // We always add the length constraint in the conclusion here
2224
          // because the skolem needs to have length `uyLen`. If we only assert
2225
          // that the skolem's length is greater or equal to one, we can end up
2226
          // in a loop:
2227
          //
2228
          // 1. Split: x = k1 ++ k2 ^ len(k1) >= 1
2229
          // 2. Assume: k2 = ""
2230
          // 3. Deduce: x = k1
2231
          //
2232
          // After step 3, `k1` is marked congruent because `x` is the older
2233
          // variable. So we get `x` in the normal form again.
2234
8
          std::vector<Node> newSkolems;
2235
          Node conc =
2236
8
              getDecomposeConclusion(ux, uyLen, false, skc, newSkolems);
2237
4
          Assert(newSkolems.size() == 2);
2238
4
          d_termReg.registerTermAtomic(newSkolems[1], LENGTH_GEQ_ONE);
2239
8
          std::vector<Node> antecLen;
2240
4
          antecLen.push_back(nm->mkNode(GT, uxLen, uyLen));
2241
4
          d_im.sendInference(antecLen,
2242
                             antecLen,
2243
                             conc,
2244
                             InferenceId::STRINGS_DEQ_DISL_STRINGS_SPLIT,
2245
                             false,
2246
                             true);
2247
        }
2248
2249
2
        return;
2250
      }
2251
    }
2252
69
    else if (d_state.areEqual(xLenTerm, yLenTerm))
2253
    {
2254
      // `x` and `y` have the same length in the current context. We split on
2255
      // whether they are actually equal.
2256
      //
2257
      // E.g. x ++ x' ++ ... != y ++ y' ++ ... ^ len(x) = len(y) --->
2258
      //      x = y v x != y
2259
38
      Assert(!d_state.areDisequal(x, y));
2260
38
      if (d_im.sendSplit(x, y, InferenceId::STRINGS_DEQ_STRINGS_EQ, false))
2261
      {
2262
38
        return;
2263
      }
2264
    }
2265
    else
2266
    {
2267
      // It is not known whether `x` and `y` have the same length in the
2268
      // current context. We split on whether they do.
2269
      //
2270
      // E.g. x ++ x' ++ ... != y ++ y' ++ ... --->
2271
      //      len(x) = len(y) v len(x) != len(y)
2272
31
      if (d_im.sendSplit(xLenTerm, yLenTerm, InferenceId::STRINGS_DEQ_LENS_EQ))
2273
      {
2274
31
        return;
2275
      }
2276
    }
2277
  }
2278
  Unreachable();
2279
}
2280
2281
1366
bool CoreSolver::processReverseDeq(std::vector<Node>& nfi,
2282
                                   std::vector<Node>& nfj,
2283
                                   Node ni,
2284
                                   Node nj)
2285
{
2286
  // reverse normal form of i, j
2287
1366
  std::reverse(nfi.begin(), nfi.end());
2288
1366
  std::reverse(nfj.begin(), nfj.end());
2289
2290
1366
  size_t index = 0;
2291
1366
  bool ret = processSimpleDeq(nfi, nfj, ni, nj, index, true);
2292
2293
  // reverse normal form of i, j
2294
1366
  std::reverse(nfi.begin(), nfi.end());
2295
1366
  std::reverse(nfj.begin(), nfj.end());
2296
2297
1366
  return ret;
2298
}
2299
2300
2369
bool CoreSolver::processSimpleDeq(std::vector<Node>& nfi,
2301
                                  std::vector<Node>& nfj,
2302
                                  Node ni,
2303
                                  Node nj,
2304
                                  size_t& index,
2305
                                  bool isRev)
2306
{
2307
4738
  TypeNode stype = ni.getType();
2308
4738
  Node emp = Word::mkEmptyWord(stype);
2309
2310
2369
  NormalForm& nfni = getNormalForm(ni);
2311
2369
  NormalForm& nfnj = getNormalForm(nj);
2312
3049
  while (index < nfi.size() || index < nfj.size())
2313
  {
2314
2709
    if (index >= nfi.size() || index >= nfj.size())
2315
    {
2316
      // We have reached the end of one of the normal forms. Note that this
2317
      // function only deals with two normal forms that have the same length,
2318
      // so the remainder of the longer normal form needs to be empty. This
2319
      // will lead to a conflict.
2320
      //
2321
      // E.g. px ++ x ++ ... != py ^
2322
      //        len(px ++ x ++ ...) = len(py) --->
2323
      //      x = "" ^ ...
2324
4
      Trace("strings-solve-debug")
2325
2
          << "Disequality normalize empty" << std::endl;
2326
      // the antecedant
2327
4
      std::vector<Node> ant;
2328
      // the antecedant that is not explainable in this context
2329
4
      std::vector<Node> antn;
2330
4
      Node niLenTerm = d_state.getLengthExp(ni, ant, nfni.d_base);
2331
4
      Node njLenTerm = d_state.getLengthExp(nj, ant, nfnj.d_base);
2332
      // length is not guaranteed to hold
2333
4
      Node leq = niLenTerm.eqNode(njLenTerm);
2334
2
      ant.push_back(leq);
2335
2
      antn.push_back(leq);
2336
2
      ant.insert(ant.end(), nfni.d_exp.begin(), nfni.d_exp.end());
2337
2
      ant.insert(ant.end(), nfnj.d_exp.begin(), nfnj.d_exp.end());
2338
4
      std::vector<Node> cc;
2339
2
      std::vector<Node>& nfk = index >= nfi.size() ? nfj : nfi;
2340
4
      for (size_t k = index; k < nfk.size(); k++)
2341
      {
2342
2
        cc.push_back(nfk[k].eqNode(emp));
2343
      }
2344
2
      Node conc = cc.size() == 1
2345
2
                      ? cc[0]
2346
6
                      : NodeManager::currentNM()->mkNode(kind::AND, cc);
2347
2
      d_im.sendInference(ant, antn, conc, InferenceId::STRINGS_DEQ_NORM_EMP, isRev, true);
2348
2
      return true;
2349
    }
2350
2351
    // We have inferred that the normal forms up to position `index` are
2352
    // equivalent. Below, we refer to the components at the current position of
2353
    // the normal form as `x` and `y`.
2354
    //
2355
    // E.g. x ++ ... = y ++ ...
2356
2871
    Node x = nfi[index];
2357
2871
    Node y = nfj[index];
2358
5414
    Trace("strings-solve-debug")
2359
2707
        << "...Processing(QED) " << x << " " << y << std::endl;
2360
2883
    if (d_state.areEqual(x, y))
2361
    {
2362
      // The normal forms have an equivalent term at `index` in the current
2363
      // context. We move to the next `index`.
2364
      //
2365
      // E.g. x ++ x' ++ ... != z ++ y' ++ ... ^ x = z
2366
176
      index++;
2367
176
      continue;
2368
    }
2369
2370
2531
    if (!x.isConst() || !y.isConst())
2371
    {
2372
4502
      std::vector<Node> lenExp;
2373
4502
      Node xLenTerm = d_state.getLength(x, lenExp);
2374
4502
      Node yLenTerm = d_state.getLength(y, lenExp);
2375
2251
      if (d_state.areEqual(xLenTerm, yLenTerm) && d_state.areDisequal(x, y))
2376
      {
2377
        // Either `x` or `y` is non-constant, the lengths are equal, and `x`
2378
        // and `y` are disequal in the current context. The disequality is
2379
        // satisfied and there is nothing else to do.
2380
        //
2381
        // E.g. x ++ x' ++ ... != y ++ y' ++ ... ^ len(x) = len(y) ^ x != y
2382
770
        Trace("strings-solve")
2383
385
            << "Simple Case 2 : found equal length disequal sub strings " << x
2384
385
            << " " << y << std::endl;
2385
385
        return true;
2386
      }
2387
      else
2388
      {
2389
        // Either `x` or `y` is non-constant but it is not known whether they
2390
        // have the same length or are disequal. We bail out.
2391
        //
2392
        // E.g. x ++ x' ++ ... != y ++ y' ++ ...
2393
1866
        return false;
2394
      }
2395
    }
2396
2397
    // Below, we deal with the cases where both `x` and `y` are constant.
2398
280
    Assert(x.isConst() && y.isConst());
2399
280
    size_t xLen = Word::getLength(x);
2400
280
    size_t yLen = Word::getLength(y);
2401
280
    size_t shortLen = std::min(xLen, yLen);
2402
    bool isSameFix =
2403
280
        isRev ? Word::rstrncmp(x, y, shortLen) : Word::strncmp(x, y, shortLen);
2404
280
    if (!isSameFix)
2405
    {
2406
      // `x` and `y` are constants but do not share a prefix/suffix, thus
2407
      // satisfying the disequality. There is nothing else to do.
2408
      //
2409
      // E.g. "abc" ++ x' ++ ... != "d" ++ y' ++ ...
2410
116
      return true;
2411
    }
2412
2413
    // `x` and `y` are constants and share a prefix/suffix. We move the common
2414
    // prefix/suffix to a separate component in the normal form.
2415
    //
2416
    // E.g. "abc" ++ x' ++ ... != "ab" ++ y' ++ ... --->
2417
    //      "ab" ++ "c" ++ x' ++ ... != "ab" ++ y' ++ ...
2418
328
    Node nk = xLen < yLen ? x : y;
2419
328
    Node nl = xLen < yLen ? y : x;
2420
328
    Node remainderStr;
2421
164
    if (isRev)
2422
    {
2423
50
      remainderStr = Word::substr(nl, 0, Word::getLength(nl) - shortLen);
2424
100
      Trace("strings-solve-debug-test")
2425
50
          << "Rev. Break normal form of " << nl << " into " << nk << ", "
2426
50
          << remainderStr << std::endl;
2427
    }
2428
    else
2429
    {
2430
114
      remainderStr = Word::substr(nl, shortLen);
2431
228
      Trace("strings-solve-debug-test")
2432
114
          << "Break normal form of " << nl << " into " << nk << ", "
2433
114
          << remainderStr << std::endl;
2434
    }
2435
164
    if (xLen < yLen)
2436
    {
2437
72
      nfj.insert(nfj.begin() + index + 1, remainderStr);
2438
72
      nfj[index] = nfi[index];
2439
    }
2440
    else
2441
    {
2442
92
      nfi.insert(nfi.begin() + index + 1, remainderStr);
2443
92
      nfi[index] = nfj[index];
2444
    }
2445
2446
164
    index++;
2447
  }
2448
  return false;
2449
}
2450
2451
void CoreSolver::processDeqExtensionality(Node n1, Node n2)
2452
{
2453
  // hash based on equality
2454
  Node eq = n1 < n2 ? n1.eqNode(n2) : n2.eqNode(n1);
2455
  NodeSet::const_iterator it = d_extDeq.find(eq);
2456
  if (it != d_extDeq.end())
2457
  {
2458
    // already processed
2459
    return;
2460
  }
2461
  d_extDeq.insert(eq);
2462
2463
  NodeManager* nm = NodeManager::currentNM();
2464
  SkolemCache* sc = d_termReg.getSkolemCache();
2465
  TypeNode intType = nm->integerType();
2466
  Node k = sc->mkTypedSkolemCached(
2467
      intType, n1, n2, SkolemCache::SK_DEQ_DIFF, "diff");
2468
  Node deq = eq.negate();
2469
  Node ss1, ss2;
2470
  if (n1.getType().isString())
2471
  {
2472
    // substring of length 1
2473
    ss1 = nm->mkNode(STRING_SUBSTR, n1, k, d_one);
2474
    ss2 = nm->mkNode(STRING_SUBSTR, n2, k, d_one);
2475
  }
2476
  else
2477
  {
2478
    // as an optimization, for sequences, use seq.nth
2479
    ss1 = nm->mkNode(SEQ_NTH, n1, k);
2480
    ss2 = nm->mkNode(SEQ_NTH, n2, k);
2481
  }
2482
  // disequality between nth/substr
2483
  Node conc1 = ss1.eqNode(ss2).negate();
2484
2485
  // The skolem k is in the bounds of at least
2486
  // one string/sequence
2487
  Node len1 = nm->mkNode(STRING_LENGTH, n1);
2488
  Node len2 = nm->mkNode(STRING_LENGTH, n2);
2489
  Node conc2 = nm->mkNode(LEQ, d_zero, k);
2490
  Node conc3 = nm->mkNode(LT, k, len1);
2491
  Node lenDeq = nm->mkNode(EQUAL, len1, len2).negate();
2492
2493
  std::vector<Node> concs = {conc1, conc2, conc3};
2494
  Node conc = nm->mkNode(OR, lenDeq, nm->mkAnd(concs));
2495
  // A != B => ( seq.len(A) != seq.len(B) or
2496
  //             ( seq.nth(A, d) != seq.nth(B, d) ^ 0 <= d < seq.len(A) ) )
2497
  d_im.sendInference(
2498
      {deq}, conc, InferenceId::STRINGS_DEQ_EXTENSIONALITY, false, true);
2499
}
2500
2501
void CoreSolver::addNormalFormPair( Node n1, Node n2 ){
2502
  if (n1>n2)
2503
  {
2504
    addNormalFormPair(n2,n1);
2505
    return;
2506
  }
2507
  if( !isNormalFormPair( n1, n2 ) ){
2508
    int index = 0;
2509
    NodeIntMap::const_iterator it = d_nfPairs.find(n1);
2510
    if (it != d_nfPairs.end())
2511
    {
2512
      index = (*it).second;
2513
    }
2514
    d_nfPairs[n1] = index + 1;
2515
    if( index<(int)d_nf_pairs_data[n1].size() ){
2516
      d_nf_pairs_data[n1][index] = n2;
2517
    }else{
2518
      d_nf_pairs_data[n1].push_back( n2 );
2519
    }
2520
    Assert(isNormalFormPair(n1, n2));
2521
  } else {
2522
    Trace("strings-nf-debug") << "Already a normal form pair " << n1 << " " << n2 << std::endl;
2523
  }
2524
}
2525
2526
14603
bool CoreSolver::isNormalFormPair( Node n1, Node n2 ) {
2527
14603
  if (n1>n2)
2528
  {
2529
3934
    return isNormalFormPair(n2,n1);
2530
  }
2531
  //Trace("strings-debug") << "is normal form pair. " << n1 << " " << n2 << std::endl;
2532
10669
  NodeIntMap::const_iterator it = d_nfPairs.find(n1);
2533
10669
  if (it != d_nfPairs.end())
2534
  {
2535
    Assert(d_nf_pairs_data.find(n1) != d_nf_pairs_data.end());
2536
    for( int i=0; i<(*it).second; i++ ){
2537
      Assert(i < (int)d_nf_pairs_data[n1].size());
2538
      if( d_nf_pairs_data[n1][i]==n2 ){
2539
        return true;
2540
      }
2541
    }
2542
  }
2543
10669
  return false;
2544
}
2545
2546
8634
void CoreSolver::checkNormalFormsDeq()
2547
{
2548
8634
  eq::EqualityEngine* ee = d_state.getEqualityEngine();
2549
16947
  std::map< Node, std::map< Node, bool > > processed;
2550
2551
8634
  const context::CDList<Node>& deqs = d_state.getDisequalityList();
2552
2553
  //for each pair of disequal strings, must determine whether their lengths are equal or disequal
2554
43108
  for (const Node& eq : deqs)
2555
  {
2556
68948
    Node n[2];
2557
103422
    for( unsigned i=0; i<2; i++ ){
2558
68948
      n[i] = ee->getRepresentative( eq[i] );
2559
    }
2560
34474
    if( processed[n[0]].find( n[1] )==processed[n[0]].end() ){
2561
28053
      processed[n[0]][n[1]] = true;
2562
56106
      Node lt[2];
2563
84159
      for( unsigned i=0; i<2; i++ ){
2564
56106
        EqcInfo* ei = d_state.getOrMakeEqcInfo(n[i], false);
2565
56106
        lt[i] = ei ? ei->d_lengthTerm : Node::null();
2566
56106
        if( lt[i].isNull() ){
2567
          lt[i] = eq[i];
2568
        }
2569
56106
        lt[i] = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, lt[i] );
2570
      }
2571
28053
      if (!d_state.areEqual(lt[0], lt[1]) && !d_state.areDisequal(lt[0], lt[1]))
2572
      {
2573
284
        d_im.sendSplit(lt[0], lt[1], InferenceId::STRINGS_DEQ_LENGTH_SP);
2574
      }
2575
    }
2576
  }
2577
2578
8634
  if (d_im.hasProcessed())
2579
  {
2580
    // added splitting lemma above
2581
181
    return;
2582
  }
2583
  // otherwise, look at pairs of equivalence classes with equal lengths
2584
16766
  std::map<TypeNode, std::vector<std::vector<Node> > > colsT;
2585
16766
  std::map<TypeNode, std::vector<Node> > ltsT;
2586
8453
  d_state.separateByLength(d_strings_eqc, colsT, ltsT);
2587
11919
  for (std::pair<const TypeNode, std::vector<std::vector<Node> > >& ct : colsT)
2588
  {
2589
3606
    std::vector<std::vector<Node> >& cols = ct.second;
2590
27916
    for( unsigned i=0; i<cols.size(); i++ ){
2591
24450
      if (cols[i].size() > 1 && !d_im.hasPendingLemma())
2592
      {
2593
5023
        if (Trace.isOn("strings-solve"))
2594
        {
2595
          Trace("strings-solve") << "- Verify disequalities are processed for "
2596
                                 << cols[i][0] << ", normal form : ";
2597
          utils::printConcatTrace(getNormalForm(cols[i][0]).d_nf, "strings-solve");
2598
          Trace("strings-solve")
2599
              << "... #eql = " << cols[i].size() << std::endl;
2600
        }
2601
        //must ensure that normal forms are disequal
2602
23553
        for( unsigned j=0; j<cols[i].size(); j++ ){
2603
67517
          for( unsigned k=(j+1); k<cols[i].size(); k++ ){
2604
            //for strings that are disequal, but have the same length
2605
48987
            if (cols[i][j].isConst() && cols[i][k].isConst())
2606
            {
2607
              // if both are constants, they should be distinct, and its trivial
2608
30715
              Assert(cols[i][j] != cols[i][k]);
2609
            }
2610
18272
            else if (d_state.areDisequal(cols[i][j], cols[i][k]))
2611
            {
2612
5818
              Assert(!d_state.isInConflict());
2613
5818
              if (Trace.isOn("strings-solve"))
2614
              {
2615
                Trace("strings-solve") << "- Compare " << cols[i][j] << ", nf ";
2616
                utils::printConcatTrace(getNormalForm(cols[i][j]).d_nf,
2617
                                        "strings-solve");
2618
                Trace("strings-solve") << " against " << cols[i][k] << ", nf ";
2619
                utils::printConcatTrace(getNormalForm(cols[i][k]).d_nf,
2620
                                        "strings-solve");
2621
                Trace("strings-solve") << "..." << std::endl;
2622
              }
2623
5818
              processDeq(cols[i][j], cols[i][k]);
2624
5818
              if (d_im.hasProcessed())
2625
              {
2626
280
                return;
2627
              }
2628
            }
2629
          }
2630
        }
2631
      }
2632
    }
2633
  }
2634
}
2635
2636
8012
void CoreSolver::checkLengthsEqc() {
2637
41428
  for (unsigned i = 0; i < d_strings_eqc.size(); i++)
2638
  {
2639
66832
    TypeNode stype = d_strings_eqc[i].getType();
2640
33416
    NormalForm& nfi = getNormalForm(d_strings_eqc[i]);
2641
66832
    Trace("strings-process-debug")
2642
33416
        << "Process length constraints for " << d_strings_eqc[i] << std::endl;
2643
    // check if there is a length term for this equivalence class
2644
33416
    EqcInfo* ei = d_state.getOrMakeEqcInfo(d_strings_eqc[i], false);
2645
66832
    Node lt = ei ? ei->d_lengthTerm : Node::null();
2646
33416
    if (lt.isNull())
2647
    {
2648
      Trace("strings-process-debug")
2649
          << "No length term for eqc " << d_strings_eqc[i] << std::endl;
2650
      continue;
2651
    }
2652
66832
    Node llt = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, lt);
2653
    // now, check if length normalization has occurred
2654
33416
    if (ei->d_normalizedLength.get().isNull())
2655
    {
2656
58114
      Node nf = utils::mkNConcat(nfi.d_nf, stype);
2657
29057
      if (Trace.isOn("strings-process-debug"))
2658
      {
2659
        Trace("strings-process-debug")
2660
            << "  normal form is " << nf << " from base " << nfi.d_base
2661
            << std::endl;
2662
        Trace("strings-process-debug") << "  normal form exp is: " << std::endl;
2663
        for (const Node& exp : nfi.d_exp)
2664
        {
2665
          Trace("strings-process-debug") << "   " << exp << std::endl;
2666
        }
2667
      }
2668
2669
      // if not, add the lemma
2670
58114
      std::vector<Node> ant;
2671
29057
      ant.insert(ant.end(), nfi.d_exp.begin(), nfi.d_exp.end());
2672
29057
      ant.push_back(lt.eqNode(nfi.d_base));
2673
58114
      Node lc = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, nf);
2674
58114
      Node lcr = Rewriter::rewrite(lc);
2675
58114
      Trace("strings-process-debug")
2676
29057
          << "Rewrote length " << lc << " to " << lcr << std::endl;
2677
29057
      if (!d_state.areEqual(llt, lcr))
2678
      {
2679
14002
        Node eq = llt.eqNode(lc);
2680
7001
        ei->d_normalizedLength.set(eq);
2681
7001
        d_im.sendInference(ant, eq, InferenceId::STRINGS_LEN_NORM, false, true);
2682
      }
2683
    }
2684
  }
2685
8012
}
2686
2687
2121
bool CoreSolver::processInferInfo(CoreInferInfo& cii)
2688
{
2689
2121
  InferInfo& ii = cii.d_infer;
2690
  // rewrite the conclusion, ensure non-trivial
2691
4242
  Node concr = Rewriter::rewrite(ii.d_conc);
2692
2693
2121
  if (concr == d_true)
2694
  {
2695
    // conclusion rewrote to true
2696
    return false;
2697
  }
2698
  // process the state change to this solver
2699
2121
  if (!cii.d_nfPair[0].isNull())
2700
  {
2701
    Assert(!cii.d_nfPair[1].isNull());
2702
    addNormalFormPair(cii.d_nfPair[0], cii.d_nfPair[1]);
2703
  }
2704
  // send phase requirements
2705
2841
  for (const std::pair<const Node, bool>& pp : cii.d_pendingPhase)
2706
  {
2707
1440
    Node ppr = Rewriter::rewrite(pp.first);
2708
720
    d_im.addPendingPhaseRequirement(ppr, pp.second);
2709
  }
2710
2711
  // send the inference, which is a lemma
2712
2121
  d_im.sendInference(ii, true);
2713
2714
2121
  return true;
2715
}
2716
2717
}  // namespace strings
2718
}  // namespace theory
2719
22746
}  // namespace cvc5