GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/core_solver.cpp Lines: 1117 1371 81.5 %
Date: 2021-09-07 Branches: 2562 6307 40.6 %

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