GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/core_solver.cpp Lines: 1105 1371 80.6 %
Date: 2021-05-24 Branches: 2537 6323 40.1 %

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