GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/core_solver.cpp Lines: 1105 1371 80.6 %
Date: 2021-05-22 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
12547
CoreInferInfo::CoreInferInfo(InferenceId id) : d_infer(id), d_index(0), d_rev(false) {}
36
37
9460
CoreSolver::CoreSolver(SolverState& s,
38
                       InferenceManager& im,
39
                       TermRegistry& tr,
40
9460
                       BaseSolver& bs)
41
    : d_state(s),
42
      d_im(im),
43
      d_termReg(tr),
44
      d_bsolver(bs),
45
9460
      d_nfPairs(s.getSatContext())
46
{
47
9460
  d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) );
48
9460
  d_one = NodeManager::currentNM()->mkConst( Rational( 1 ) );
49
9460
  d_neg_one = NodeManager::currentNM()->mkConst(Rational(-1));
50
9460
  d_true = NodeManager::currentNM()->mkConst( true );
51
9460
  d_false = NodeManager::currentNM()->mkConst( false );
52
9460
}
53
54
9460
CoreSolver::~CoreSolver() {
55
56
9460
}
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
20839
void CoreSolver::checkCycles()
121
{
122
  // first check for cycles, while building ordering of equivalence classes
123
20839
  d_flat_form.clear();
124
20839
  d_flat_form_index.clear();
125
20839
  d_eqc.clear();
126
  // Rebuild strings eqc based on acyclic ordering, first copy the equivalence
127
  // classes from the base solver.
128
20839
  const std::vector<Node>& eqc = d_bsolver.getStringEqc();
129
20839
  d_strings_eqc.clear();
130
356321
  for (const Node& r : eqc)
131
  {
132
670964
    std::vector< Node > curr;
133
670964
    std::vector< Node > exp;
134
335482
    checkCycles(r, curr, exp);
135
335482
    if (d_im.hasProcessed())
136
    {
137
      return;
138
    }
139
  }
140
}
141
142
20839
void CoreSolver::checkFlatForms()
143
{
144
  // debug print flat forms
145
20839
  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
356201
  for (const Node& eqc : d_strings_eqc)
155
  {
156
670776
    Node c = d_bsolver.getConstantEqc(eqc);
157
335414
    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
115960
      std::map<Node, std::vector<Node> >::iterator it = d_eqc.find(eqc);
162
115960
      if (it != d_eqc.end())
163
      {
164
44198
        for (const Node& n : it->second)
165
        {
166
          int firstc, lastc;
167
23626
          if (!StringsEntail::canConstantContainList(
168
23626
                  c, d_flat_form[n], firstc, lastc))
169
          {
170
104
            Trace("strings-ff-debug") << "Flat form for " << n
171
52
                                      << " cannot be contained in constant "
172
52
                                      << c << std::endl;
173
104
            Trace("strings-ff-debug") << "  indices = " << firstc << "/"
174
52
                                      << lastc << std::endl;
175
            // conflict, explanation is n = base ^ base = c ^ relevant portion
176
            // of ( n = f[n] )
177
104
            std::vector<Node> exp;
178
122
            for (int e = firstc; e <= lastc; e++)
179
            {
180
70
              if (d_flat_form[n][e].isConst())
181
              {
182
64
                Assert(e >= 0 && e < (int)d_flat_form_index[n].size());
183
64
                Assert(d_flat_form_index[n][e] >= 0
184
                       && d_flat_form_index[n][e] < (int)n.getNumChildren());
185
192
                d_im.addToExplanation(
186
128
                    n[d_flat_form_index[n][e]], d_flat_form[n][e], exp);
187
              }
188
            }
189
52
            d_bsolver.explainConstantEqc(n, eqc, exp);
190
104
            Node conc = d_false;
191
52
            d_im.sendInference(exp, conc, InferenceId::STRINGS_F_NCTN);
192
52
            return;
193
          }
194
        }
195
      }
196
    }
197
  }
198
199
  //(2) scan lists, unification to infer conflicts and equalities
200
355112
  for (const Node& eqc : d_strings_eqc)
201
  {
202
334417
    std::map<Node, std::vector<Node> >::iterator it = d_eqc.find(eqc);
203
334417
    if (it == d_eqc.end() || it->second.size() <= 1)
204
    {
205
312787
      continue;
206
    }
207
    // iterate over start index
208
78916
    for (unsigned start = 0; start < it->second.size() - 1; start++)
209
    {
210
171994
      for (unsigned r = 0; r < 2; r++)
211
      {
212
114708
        bool isRev = r == 1;
213
114708
        checkFlatForm(it->second, start, isRev);
214
114708
        if (d_state.isInConflict())
215
        {
216
184
          return;
217
        }
218
219
750908
        for (const Node& n : it->second)
220
        {
221
636292
          std::reverse(d_flat_form[n].begin(), d_flat_form[n].end());
222
636292
          std::reverse(d_flat_form_index[n].begin(),
223
636292
                       d_flat_form_index[n].end());
224
        }
225
      }
226
    }
227
  }
228
}
229
230
114708
void CoreSolver::checkFlatForm(std::vector<Node>& eqc,
231
                                  size_t start,
232
                                  bool isRev)
233
{
234
114708
  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
229324
  std::vector<Node> inelig(eqc.begin(), eqc.begin() + start + 1);
241
229324
  Node a = eqc[start];
242
229416
  Trace("strings-ff-debug")
243
114708
      << "Check flat form for a = " << a << ", whose flat form is "
244
114708
      << d_flat_form[a] << ")" << std::endl;
245
229324
  Node b;
246
  // the length explanation
247
229324
  Node lant;
248
24206
  do
249
  {
250
272809
    std::vector<Node> exp;
251
272809
    Node conc;
252
138914
    InferenceId infType = InferenceId::UNKNOWN;
253
138914
    size_t eqc_size = eqc.size();
254
138914
    size_t asize = d_flat_form[a].size();
255
138914
    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
277828
      Node curr = d_flat_form[a][count];
293
277828
      Node curr_c = d_bsolver.getConstantEqc(curr);
294
277828
      Node ac = a[d_flat_form_index[a][count]];
295
277828
      std::vector<Node> lexp;
296
277828
      Node lcurr = d_state.getLength(ac, lexp);
297
509977
      for (size_t i = start + 1; i < eqc_size; i++)
298
      {
299
376082
        b = eqc[i];
300
376082
        if (std::find(inelig.begin(), inelig.end(), b) != inelig.end())
301
        {
302
33351
          continue;
303
        }
304
305
342731
        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
680443
          Node cc = d_flat_form[b][count];
327
342731
          if (cc != curr)
328
          {
329
629769
            Node bc = b[d_flat_form_index[b][count]];
330
317394
            inelig.push_back(b);
331
317394
            Assert(!d_state.areEqual(curr, cc));
332
629769
            Node cc_c = d_bsolver.getConstantEqc(cc);
333
317394
            if (!curr_c.isNull() && !cc_c.isNull())
334
            {
335
              // check for constant conflict
336
              size_t index;
337
45010
              Node s = Word::splitConstant(cc_c, curr_c, index, isRev);
338
22551
              if (s.isNull())
339
              {
340
92
                d_bsolver.explainConstantEqc(ac,curr,exp);
341
92
                d_bsolver.explainConstantEqc(bc,cc,exp);
342
92
                conc = d_false;
343
92
                infType = InferenceId::STRINGS_F_CONST;
344
92
                break;
345
              }
346
            }
347
589686
            else if ((d_flat_form[a].size() - 1) == count
348
294843
                     && (d_flat_form[b].size() - 1) == count)
349
            {
350
2289
              conc = ac.eqNode(bc);
351
2289
              infType = InferenceId::STRINGS_F_ENDPOINT_EQ;
352
2289
              break;
353
            }
354
            else
355
            {
356
              // if lengths are the same, apply LengthEq
357
582470
              std::vector<Node> lexp2;
358
582470
              Node lcc = d_state.getLength(bc, lexp2);
359
292554
              if (d_state.areEqual(lcurr, lcc))
360
              {
361
2638
                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
5276
                std::vector<Node> lexpc;
380
2638
                lexpc.insert(lexpc.end(), lexp.begin(), lexp.end());
381
2638
                lexpc.insert(lexpc.end(), lexp2.begin(), lexp2.end());
382
2638
                d_im.addToExplanation(lcurr, lcc, lexpc);
383
2638
                lant = utils::mkAnd(lexpc);
384
2638
                conc = ac.eqNode(bc);
385
2638
                infType = InferenceId::STRINGS_F_UNIFY;
386
2638
                break;
387
              }
388
            }
389
          }
390
        }
391
      }
392
    }
393
138914
    if (!conc.isNull())
394
    {
395
10038
      Trace("strings-ff-debug") << "Found inference (" << infType
396
5019
                                << "): " << conc << " based on equality " << a
397
5019
                                << " == " << b << ", " << isRev << std::endl;
398
      // explain why prefixes up to now were the same
399
7470
      for (size_t j = 0; j < count; j++)
400
      {
401
4902
        Trace("strings-ff-debug") << "Add at " << d_flat_form_index[a][j] << " "
402
2451
                                  << d_flat_form_index[b][j] << std::endl;
403
7353
        d_im.addToExplanation(
404
4902
            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
15057
      for (unsigned t = 0; t < 2; t++)
408
      {
409
20076
        Node c = t == 0 ? a : b;
410
        ssize_t jj;
411
10038
        if (infType == InferenceId::STRINGS_F_ENDPOINT_EQ
412
5460
            || (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
4578
          jj = isRev ? -1 : c.getNumChildren();
417
        }
418
        else
419
        {
420
8190
          jj = t == 0 ? d_flat_form_index[a][count]
421
2730
                      : d_flat_form_index[b][count];
422
        }
423
10038
        ssize_t startj = isRev ? jj + 1 : 0;
424
10038
        ssize_t endj = isRev ? c.getNumChildren() : jj;
425
20076
        Node emp = Word::mkEmptyWord(a.getType());
426
21510
        for (ssize_t j = startj; j < endj; j++)
427
        {
428
11472
          if (d_state.areEqual(c[j], emp))
429
          {
430
1992
            d_im.addToExplanation(c[j], emp, exp);
431
          }
432
        }
433
      }
434
5019
      d_im.addToExplanation(a, b, exp);
435
5019
      if (!lant.isNull())
436
      {
437
        // add the length explanation
438
2638
        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
5019
      d_im.sendInference(exp, conc, infType, isRev);
446
5019
      if (d_state.isInConflict())
447
      {
448
92
        return;
449
      }
450
4927
      break;
451
    }
452
133895
    count++;
453
133895
  } while (inelig.size() < eqc.size());
454
}
455
456
862290
Node CoreSolver::checkCycles( Node eqc, std::vector< Node >& curr, std::vector< Node >& exp ){
457
862290
  if( std::find( curr.begin(), curr.end(), eqc )!=curr.end() ){
458
    // a loop
459
    return eqc;
460
862290
  }else if( std::find( d_strings_eqc.begin(), d_strings_eqc.end(), eqc )==d_strings_eqc.end() ){
461
335482
    curr.push_back( eqc );
462
670964
    Node emp = Word::mkEmptyWord(eqc.getType());
463
    //look at all terms in this equivalence class
464
335482
    eq::EqualityEngine* ee = d_state.getEqualityEngine();
465
335482
    eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, ee );
466
3126288
    while( !eqc_i.isFinished() ) {
467
2790806
      Node n = (*eqc_i);
468
1395403
      if( !d_bsolver.isCongruent(n) ){
469
773652
        if( n.getKind() == kind::STRING_CONCAT ){
470
229156
          Trace("strings-cycle") << eqc << " check term : " << n << " in " << eqc << std::endl;
471
229156
          if (eqc != emp)
472
          {
473
223527
            d_eqc[eqc].push_back( n );
474
          }
475
769749
          for( unsigned i=0; i<n.getNumChildren(); i++ ){
476
1081186
            Node nr = d_state.getRepresentative(n[i]);
477
540593
            if (eqc == emp)
478
            {
479
              //for empty eqc, ensure all components are empty
480
13785
              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
526808
              if (nr != emp)
490
              {
491
508213
                d_flat_form[n].push_back( nr );
492
508213
                d_flat_form_index[n].push_back( i );
493
              }
494
              //for non-empty eqc, recurse and see if we find a loop
495
1053616
              Node ncy = checkCycles( nr, curr, exp );
496
526808
              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
526808
                if (d_im.hasProcessed())
519
                {
520
                  return Node::null();
521
                }
522
              }
523
            }
524
          }
525
        }
526
      }
527
1395403
      ++eqc_i;
528
    }
529
335482
    curr.pop_back();
530
335482
    Trace("strings-eqc") << "* add string eqc: " << eqc << std::endl;
531
    //now we can add it to the list of equivalence classes
532
335482
    d_strings_eqc.push_back( eqc );
533
  }else{
534
    //already processed
535
  }
536
862290
  return Node::null();
537
}
538
539
15405
void CoreSolver::checkNormalFormsEq()
540
{
541
  // calculate normal forms for each equivalence class, possibly adding
542
  // splitting lemmas
543
15405
  d_normal_form.clear();
544
25245
  std::map<Node, Node> nf_to_eqc;
545
25245
  std::map<Node, Node> eqc_to_nf;
546
25245
  std::map<Node, Node> eqc_to_exp;
547
183302
  for (const Node& eqc : d_strings_eqc)
548
  {
549
341359
    TypeNode stype = eqc.getType();
550
346924
    Trace("strings-process-debug") << "- Verify normal forms are the same for "
551
173462
                                   << eqc << std::endl;
552
173462
    normalizeEquivalenceClass(eqc, stype);
553
173462
    Trace("strings-debug") << "Finished normalizing eqc..." << std::endl;
554
173462
    if (d_im.hasProcessed())
555
    {
556
5329
      return;
557
    }
558
168133
    NormalForm& nfe = getNormalForm(eqc);
559
336030
    Node nf_term = utils::mkNConcat(nfe.d_nf, stype);
560
168133
    std::map<Node, Node>::iterator itn = nf_to_eqc.find(nf_term);
561
168133
    if (itn != nf_to_eqc.end())
562
    {
563
236
      NormalForm& nfe_eq = getNormalForm(itn->second);
564
      // two equivalence classes have same normal form, merge
565
236
      std::vector<Node> nf_exp(nfe.d_exp.begin(), nfe.d_exp.end());
566
236
      Node eexp = eqc_to_exp[itn->second];
567
236
      if (eexp != d_true)
568
      {
569
199
        nf_exp.push_back(eexp);
570
      }
571
236
      Node eq = nfe.d_base.eqNode(nfe_eq.d_base);
572
236
      d_im.sendInference(nf_exp, eq, InferenceId::STRINGS_NORMAL_FORM);
573
236
      if (d_im.hasProcessed())
574
      {
575
236
        return;
576
      }
577
    }
578
    else
579
    {
580
167897
      nf_to_eqc[nf_term] = eqc;
581
167897
      eqc_to_nf[eqc] = nf_term;
582
167897
      eqc_to_exp[eqc] = utils::mkAnd(nfe.d_exp);
583
    }
584
335794
    Trace("strings-process-debug")
585
167897
        << "Done verifying normal forms are the same for " << eqc << std::endl;
586
  }
587
9840
  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
173462
void CoreSolver::normalizeEquivalenceClass(Node eqc, TypeNode stype)
605
{
606
173462
  Trace("strings-process-debug") << "Process equivalence class " << eqc << std::endl;
607
341595
  Node emp = Word::mkEmptyWord(stype);
608
173462
  if (d_state.areEqual(eqc, emp))
609
  {
610
#ifdef CVC5_ASSERTIONS
611
9351
    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
9351
    Trace("strings-process-debug") << "Return process equivalence class " << eqc << " : empty." << std::endl;
620
9351
    d_normal_form[eqc].init(emp);
621
  }
622
  else
623
  {
624
    // should not have computed the normal form of this equivalence class yet
625
164111
    Assert(d_normal_form.find(eqc) == d_normal_form.end());
626
    // Normal forms for the relevant terms in the equivalence class of eqc
627
322893
    std::vector<NormalForm> normal_forms;
628
    // map each term to its index in the above vector
629
322893
    std::map<Node, unsigned> term_to_nf_index;
630
    // get the normal forms
631
164111
    getNormalForms(eqc, normal_forms, term_to_nf_index, stype);
632
164111
    if (d_im.hasProcessed())
633
    {
634
      return;
635
    }
636
    // process the normal forms
637
164111
    processNEqc(eqc, normal_forms, stype);
638
164111
    if (d_im.hasProcessed())
639
    {
640
5329
      return;
641
    }
642
643
    //construct the normal form
644
158782
    Assert(!normal_forms.empty());
645
158782
    unsigned nf_index = 0;
646
158782
    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
158782
    if (it != term_to_nf_index.end())
651
    {
652
110113
      nf_index = it->second;
653
    }
654
158782
    d_normal_form[eqc] = normal_forms[nf_index];
655
317564
    Trace("strings-process-debug")
656
158782
        << "Return process equivalence class " << eqc
657
158782
        << " : returned = " << d_normal_form[eqc].d_nf << std::endl;
658
  }
659
}
660
661
509108
NormalForm& CoreSolver::getNormalForm(Node n)
662
{
663
509108
  std::map<Node, NormalForm>::iterator itn = d_normal_form.find(n);
664
509108
  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
509108
  return itn->second;
675
}
676
677
17719
Node CoreSolver::getNormalString(Node x, std::vector<Node>& nf_exp)
678
{
679
17719
  if (!x.isConst())
680
  {
681
17719
    Node xr = d_state.getRepresentative(x);
682
17719
    TypeNode stype = xr.getType();
683
17719
    std::map<Node, NormalForm>::iterator it = d_normal_form.find(xr);
684
17719
    if (it != d_normal_form.end())
685
    {
686
17719
      NormalForm& nf = it->second;
687
35438
      Node ret = utils::mkNConcat(nf.d_nf, stype);
688
17719
      nf_exp.insert(nf_exp.end(), nf.d_exp.begin(), nf.d_exp.end());
689
17719
      d_im.addToExplanation(x, nf.d_base, nf_exp);
690
35438
      Trace("strings-debug")
691
17719
          << "Term: " << x << " has a normal form " << ret << std::endl;
692
17719
      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
7513
Node CoreSolver::getConclusion(Node x,
712
                               Node y,
713
                               PfRule rule,
714
                               bool isRev,
715
                               SkolemCache* skc,
716
                               std::vector<Node>& newSkolems)
717
{
718
15026
  Trace("strings-csolver") << "CoreSolver::getConclusion: " << x << " " << y
719
7513
                           << " " << rule << " " << isRev << std::endl;
720
7513
  NodeManager* nm = NodeManager::currentNM();
721
7513
  Node conc;
722
7513
  if (rule == PfRule::CONCAT_SPLIT || rule == PfRule::CONCAT_LPROP)
723
  {
724
5304
    Node sk1;
725
5304
    Node sk2;
726
7956
    if (options::stringUnifiedVSpt())
727
    {
728
      // must compare so that we are agnostic to order of x/y
729
5304
      Node ux = x < y ? x : y;
730
5304
      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
5304
                                    "v_spt");
736
2652
      newSkolems.push_back(sk);
737
2652
      sk1 = sk;
738
2652
      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
5304
    Node eq1 = x.eqNode(isRev ? nm->mkNode(STRING_CONCAT, sk1, y)
756
5304
                              : nm->mkNode(STRING_CONCAT, y, sk1));
757
758
2652
    if (rule == PfRule::CONCAT_LPROP)
759
    {
760
1020
      conc = eq1;
761
    }
762
    else
763
    {
764
3264
      Node eq2 = y.eqNode(isRev ? nm->mkNode(STRING_CONCAT, sk2, x)
765
3264
                                : nm->mkNode(STRING_CONCAT, x, sk2));
766
      // make agnostic to x/y
767
1632
      conc = x < y ? nm->mkNode(OR, eq1, eq2) : nm->mkNode(OR, eq2, eq1);
768
    }
769
2652
    if (options::stringUnifiedVSpt())
770
    {
771
      // we can assume its length is greater than zero
772
5304
      Node emp = Word::mkEmptyWord(sk1.getType());
773
7956
      conc = nm->mkNode(
774
          AND,
775
          conc,
776
5304
          sk1.eqNode(emp).negate(),
777
10608
          nm->mkNode(
778
10608
              GT, nm->mkNode(STRING_LENGTH, sk1), nm->mkConst(Rational(0))));
779
2652
    }
780
  }
781
4861
  else if (rule == PfRule::CONCAT_CSPLIT)
782
  {
783
4061
    Assert(y.isConst());
784
4061
    size_t yLen = Word::getLength(y);
785
    Node firstChar =
786
8122
        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
8122
        "c_spt");
791
4061
    newSkolems.push_back(sk);
792
4061
    conc = x.eqNode(isRev ? nm->mkNode(STRING_CONCAT, sk, firstChar)
793
                          : nm->mkNode(STRING_CONCAT, firstChar, sk));
794
  }
795
800
  else if (rule == PfRule::CONCAT_CPROP)
796
  {
797
    // expect (str.++ z d) and c
798
798
    Assert(x.getKind() == STRING_CONCAT && x.getNumChildren() == 2);
799
1596
    Node z = x[isRev ? 1 : 0];
800
1596
    Node d = x[isRev ? 0 : 1];
801
798
    Assert(d.isConst());
802
1596
    Node c = y;
803
798
    Assert(c.isConst());
804
798
    size_t cLen = Word::getLength(c);
805
798
    size_t p = getSufficientNonEmptyOverlap(c, d, isRev);
806
    Node preC =
807
1596
        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
1596
        "c_spt");
813
798
    newSkolems.push_back(sk);
814
798
    conc = z.eqNode(isRev ? nm->mkNode(STRING_CONCAT, sk, preC)
815
                          : nm->mkNode(STRING_CONCAT, preC, sk));
816
  }
817
818
7513
  return conc;
819
}
820
821
3357
size_t CoreSolver::getSufficientNonEmptyOverlap(Node c, Node d, bool isRev)
822
{
823
3357
  Assert(c.isConst() && c.getType().isStringLike());
824
3357
  Assert(d.isConst() && d.getType().isStringLike());
825
  size_t p;
826
  size_t p2;
827
3357
  size_t cLen = Word::getLength(c);
828
3357
  if (isRev)
829
  {
830
    // Since non-empty, we start with character 1
831
3774
    Node c1 = Word::prefix(c, cLen - 1);
832
1887
    p = cLen - Word::roverlap(c1, d);
833
1887
    p2 = Word::rfind(c1, d);
834
  }
835
  else
836
  {
837
2940
    Node c1 = Word::substr(c, 1);
838
1470
    p = cLen - Word::overlap(c1, d);
839
1470
    p2 = Word::find(c1, d);
840
  }
841
3357
  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
164111
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
328222
  Node emp = Word::mkEmptyWord(stype);
869
  //constant for equivalence class
870
328222
  Node eqc_non_c = eqc;
871
164111
  Trace("strings-process-debug") << "Get normal forms " << eqc << std::endl;
872
164111
  eq::EqualityEngine* ee = d_state.getEqualityEngine();
873
164111
  eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, ee );
874
1434681
  while( !eqc_i.isFinished() ){
875
1270570
    Node n = (*eqc_i);
876
635285
    if( !d_bsolver.isCongruent(n) ){
877
367095
      Kind nk = n.getKind();
878
367095
      bool isCLike = utils::isConstantLike(n);
879
367095
      if (isCLike || nk == STRING_CONCAT)
880
      {
881
162087
        Trace("strings-process-debug") << "Get Normal Form : Process term " << n << " in eqc " << eqc << std::endl;
882
324174
        NormalForm nf_curr;
883
162087
        if (isCLike)
884
        {
885
48788
          nf_curr.init(n);
886
        }
887
113299
        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
113299
          nf_curr.d_base = n;
892
379513
          for( unsigned i=0; i<n.getNumChildren(); i++ ) {
893
532428
            Node nr = ee->getRepresentative( n[i] );
894
            // get the normal form for the component
895
266214
            NormalForm& nfr = getNormalForm(nr);
896
266214
            std::vector<Node>& nfrv = nfr.d_nf;
897
532428
            Trace("strings-process-debug")
898
532428
                << "Normalizing subterm " << n[i] << " = " << nr
899
266214
                << ", which is " << nfrv << std::endl;
900
266214
            unsigned orig_size = nf_curr.d_nf.size();
901
266214
            unsigned add_size = nfrv.size();
902
            //if not the empty string, add to current normal form
903
266214
            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
258063
              if (Configuration::isAssertionBuild())
908
              {
909
731540
                for (const Node& nn : nfrv)
910
                {
911
473477
                  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
473477
                  Assert(nn.getKind() != kind::STRING_CONCAT);
926
                }
927
              }
928
258063
              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
428688
            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
324948
              nf_curr.addToExplanation(
939
                  exp,
940
324948
                  orig_size + nfr.d_expDep[exp][false],
941
324948
                  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
532428
            Node base = nfr.d_base;
946
266214
            if (base != n[i])
947
            {
948
281960
              Node eq = n[i].eqNode(base);
949
              // The equality is relevant for the entire current segment
950
140980
              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
113299
          int total_size = nf_curr.d_nf.size();
956
302256
          for (std::pair<const Node, std::map<bool, unsigned> >& ed :
957
113299
               nf_curr.d_expDep)
958
          {
959
302256
            ed.second[true] = total_size - ed.second[true];
960
302256
            Assert(ed.second[true] >= 0);
961
          }
962
        }
963
        //if not equal to self
964
162087
        std::vector<Node>& currv = nf_curr.d_nf;
965
324174
        if (currv.size() > 1
966
324174
            || (currv.size() == 1 && utils::isConstantLike(currv[0])))
967
        {
968
          // if in a build with assertions, check that normal form is acyclic
969
162087
          if (Configuration::isAssertionBuild())
970
          {
971
162087
            if (currv.size() > 1)
972
            {
973
586776
              for (unsigned i = 0; i < currv.size(); i++)
974
              {
975
473477
                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
473477
                Assert(!d_state.areEqual(currv[i], n));
982
              }
983
            }
984
          }
985
162087
          term_to_nf_index[n] = normal_forms.size();
986
162087
          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
162087
        }
992
      }else{
993
205008
        eqc_non_c = n;
994
      }
995
    }
996
    else
997
    {
998
536380
      Trace("strings-process-debug")
999
268190
          << "Get Normal Form : term " << n << " in eqc " << eqc
1000
268190
          << " is congruent" << std::endl;
1001
    }
1002
635285
    ++eqc_i;
1003
  }
1004
1005
164111
  if( normal_forms.empty() ) {
1006
39547
    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
39547
    Assert(eqc_non_c.getKind() != STRING_CONCAT);
1019
79094
    NormalForm nf_triv;
1020
39547
    nf_triv.init(eqc_non_c);
1021
39547
    normal_forms.push_back(nf_triv);
1022
  }else{
1023
124564
    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
124564
      Trace("strings-solve") << "--- Single normal form for equivalence class " << eqc << std::endl;
1063
    }
1064
  }
1065
164111
}
1066
1067
164111
void CoreSolver::processNEqc(Node eqc,
1068
                             std::vector<NormalForm>& normal_forms,
1069
                             TypeNode stype)
1070
{
1071
164111
  if (normal_forms.size() <= 1)
1072
  {
1073
307305
    return;
1074
  }
1075
  // if equivalence class is constant, approximate as containment, infer
1076
  // conflicts
1077
20917
  Node c = d_bsolver.getConstantEqc(eqc);
1078
  // the possible inferences
1079
20917
  std::vector<CoreInferInfo> pinfer;
1080
  // compute normal forms that are effectively unique
1081
20917
  std::unordered_map<Node, size_t> nfCache;
1082
20917
  std::vector<size_t> nfIndices;
1083
18962
  bool hasConstIndex = false;
1084
75048
  for (size_t i = 0, nnforms = normal_forms.size(); i < nnforms; i++)
1085
  {
1086
56293
    NormalForm& nfi = normal_forms[i];
1087
112379
    Node ni = utils::mkNConcat(nfi.d_nf, stype);
1088
56293
    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
27727
      if (!c.isNull())
1093
      {
1094
        int firstc, lastc;
1095
11811
        if (!StringsEntail::canConstantContainList(c, nfi.d_nf, firstc, lastc))
1096
        {
1097
414
          Node n = nfi.d_base;
1098
414
          std::vector<Node> exp(nfi.d_exp.begin(), nfi.d_exp.end());
1099
          //conflict
1100
207
          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
207
          d_bsolver.explainConstantEqc(n, eqc, exp);
1106
207
          d_im.sendInference(exp, d_false, InferenceId::STRINGS_N_NCTN);
1107
          // conflict, finished
1108
207
          return;
1109
        }
1110
      }
1111
1112
27520
      nfCache[ni] = i;
1113
27520
      if (ni.isConst())
1114
      {
1115
10572
        hasConstIndex = true;
1116
10572
        nfIndices.insert(nfIndices.begin(), i);
1117
      }
1118
      else
1119
      {
1120
16948
        nfIndices.push_back(i);
1121
      }
1122
    }
1123
  }
1124
18755
  size_t nnfs = nfIndices.size();
1125
37510
  Trace("strings-solve") << "CoreSolver::processNEqc: " << nnfs << "/"
1126
37510
                         << normal_forms.size() << " normal forms are unique."
1127
18755
                         << std::endl;
1128
1129
  // loop over all pairs
1130
21232
  for (unsigned i = 0; i < nnfs - 1; i++)
1131
  {
1132
    //unify each normalform[j] with normal_forms[i]
1133
11965
    for (unsigned j = i + 1; j < nnfs; j++)
1134
    {
1135
8983
      NormalForm& nfi = normal_forms[nfIndices[i]];
1136
8983
      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
8983
      Trace("strings-solve") << "Strings: Process normal form #" << i << " against #" << j << "..." << std::endl;
1139
8983
      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
8983
      unsigned rindex = 0;
1147
8983
      nfi.reverse();
1148
8983
      nfj.reverse();
1149
8983
      processSimpleNEq(nfi, nfj, rindex, true, 0, pinfer, stype);
1150
8983
      nfi.reverse();
1151
8983
      nfj.reverse();
1152
8983
      if (d_im.hasProcessed())
1153
      {
1154
5419
        break;
1155
      }
1156
      // AJR: for less aggressive endpoint inference
1157
      // rindex = 0;
1158
1159
6693
      unsigned index = 0;
1160
6693
      processSimpleNEq(nfi, nfj, index, false, rindex, pinfer, stype);
1161
6693
      if (d_im.hasProcessed())
1162
      {
1163
839
        break;
1164
      }
1165
    }
1166
6111
    if (hasConstIndex || d_im.hasProcessed())
1167
    {
1168
3634
      break;
1169
    }
1170
  }
1171
18755
  if (d_state.isInConflict())
1172
  {
1173
    // conflict, we are done
1174
75
    return;
1175
  }
1176
  // Go back and check for normal form equality conflicts. These take
1177
  // precedence over any facts and lemmas.
1178
37109
  for (const std::pair<const Node, size_t>& ni : nfCache)
1179
  {
1180
57778
    for (const std::pair<const Node, size_t>& nj : nfCache)
1181
    {
1182
36295
      if (ni.first >= nj.first)
1183
      {
1184
        // avoid duplicate comparisons
1185
29605
        continue;
1186
      }
1187
13342
      Node eq = ni.first.eqNode(nj.first);
1188
6690
      eq = Rewriter::rewrite(eq);
1189
6690
      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
21483
    if (d_im.hasProcessed())
1202
    {
1203
3054
      break;
1204
    }
1205
  }
1206
18642
  if (d_im.hasProcessed() || pinfer.empty())
1207
  {
1208
    // either already sent a lemma or fact, or there are no possible inferences
1209
16687
    return;
1210
  }
1211
  // now, determine which of the possible inferences we want to add
1212
1955
  unsigned use_index = 0;
1213
1955
  bool set_use_index = false;
1214
3910
  Trace("strings-solve") << "Possible inferences (" << pinfer.size()
1215
1955
                         << ") : " << std::endl;
1216
1955
  InferenceId min_id = InferenceId::UNKNOWN;
1217
1955
  unsigned max_index = 0;
1218
11037
  for (unsigned i = 0, psize = pinfer.size(); i < psize; i++)
1219
  {
1220
9082
    CoreInferInfo& ipii = pinfer[i];
1221
9082
    InferInfo& ii = ipii.d_infer;
1222
18164
    Trace("strings-solve") << "#" << i << ": From " << ipii.d_i << " / "
1223
9082
                           << ipii.d_j << " (rev=" << ipii.d_rev << ") : ";
1224
9082
    Trace("strings-solve") << ii.d_conc << " by " << ii.getId() << std::endl;
1225
25291
    if (!set_use_index || ii.getId() < min_id
1226
15440
        || (ii.getId() == min_id && ipii.d_index > max_index))
1227
    {
1228
3140
      min_id = ii.getId();
1229
3140
      max_index = ipii.d_index;
1230
3140
      use_index = i;
1231
3140
      set_use_index = true;
1232
    }
1233
  }
1234
1955
  Trace("strings-solve") << "...choose #" << use_index << std::endl;
1235
1955
  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
15676
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
15676
  NodeManager* nm = NodeManager::currentNM();
1251
31323
  Node emp = Word::mkEmptyWord(stype);
1252
1253
15676
  const std::vector<Node>& nfiv = nfi.d_nf;
1254
15676
  const std::vector<Node>& nfjv = nfj.d_nf;
1255
15676
  Assert(rproc <= nfiv.size() && rproc <= nfjv.size());
1256
  while (true)
1257
  {
1258
35795
    bool lhsDone = (index == (nfiv.size() - rproc));
1259
35795
    bool rhsDone = (index == (nfjv.size() - rproc));
1260
35795
    if (lhsDone && rhsDone)
1261
    {
1262
      // We are done with both normal forms
1263
      break;
1264
    }
1265
35795
    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
35795
    Node x = nfiv[index];
1290
35795
    Node y = nfjv[index];
1291
71590
    Trace("strings-solve-debug")
1292
35795
        << "Process " << x << " ... " << y << std::endl;
1293
1294
52198
    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
32806
      Trace("strings-solve-debug")
1304
16403
          << "Simple Case 1 : strings are equal" << std::endl;
1305
16403
      index++;
1306
16403
      continue;
1307
    }
1308
19392
    Assert(!d_state.areEqual(x, y));
1309
1310
19392
    std::vector<Node> lenExp;
1311
19392
    Node xLenTerm = d_state.getLength(x, lenExp);
1312
19392
    Node yLenTerm = d_state.getLength(y, lenExp);
1313
1314
19392
    if (d_state.areEqual(xLenTerm, yLenTerm))
1315
    {
1316
2253
      std::vector<Node> ant;
1317
2253
      NormalForm::getExplanationForPrefixEq(nfi, nfj, index, index, ant);
1318
2253
      if (x.isConst() && y.isConst())
1319
      {
1320
        // if both are constant, it's just a constant conflict
1321
29
        d_im.sendInference(ant, d_false, InferenceId::STRINGS_N_CONST, isRev, true);
1322
29
        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
4448
      Trace("strings-solve-debug")
1329
2224
          << "Simple Case 2 : string lengths are equal" << std::endl;
1330
4448
      Node eq = x.eqNode(y);
1331
4448
      Node leneq = xLenTerm.eqNode(yLenTerm);
1332
2224
      lenExp.push_back(leneq);
1333
      // set the explanation for length
1334
4448
      Node lant = utils::mkAnd(lenExp);
1335
2224
      ant.push_back(lant);
1336
2224
      d_im.sendInference(ant, eq, InferenceId::STRINGS_N_UNIFY, isRev);
1337
2224
      break;
1338
    }
1339
43472
    else if ((!x.isConst() && index == nfiv.size() - rproc - 1)
1340
33927
             || (!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
1660
      Trace("strings-solve-debug")
1348
830
          << "Simple Case 3 : at endpoint" << std::endl;
1349
1660
      Node eqn[2];
1350
2490
      for (unsigned r = 0; r < 2; r++)
1351
      {
1352
1660
        const NormalForm& nfk = r == 0 ? nfi : nfj;
1353
1660
        const std::vector<Node>& nfkv = nfk.d_nf;
1354
3320
        std::vector<Node> eqnc;
1355
4117
        for (unsigned i = index, size = (nfkv.size() - rproc); i < size; i++)
1356
        {
1357
2457
          if (isRev)
1358
          {
1359
1375
            eqnc.insert(eqnc.begin(), nfkv[i]);
1360
          }
1361
          else
1362
          {
1363
1082
            eqnc.push_back(nfkv[i]);
1364
          }
1365
        }
1366
1660
        eqn[r] = utils::mkNConcat(eqnc, stype);
1367
      }
1368
1660
      Trace("strings-solve-debug")
1369
830
          << "Endpoint eq check: " << eqn[0] << " " << eqn[1] << std::endl;
1370
830
      if (!d_state.areEqual(eqn[0], eqn[1]))
1371
      {
1372
1660
        std::vector<Node> antec;
1373
830
        NormalForm::getExplanationForPrefixEq(nfi, nfj, -1, -1, antec);
1374
1660
        d_im.sendInference(antec,
1375
1660
                           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
830
      break;
1386
    }
1387
16309
    else if (x.isConst() && y.isConst())
1388
    {
1389
      // Constants in both normal forms.
1390
      //
1391
      // E.g. "abc" ++ ... = "ab" ++ ...
1392
3762
      size_t lenX = Word::getLength(x);
1393
3762
      size_t lenY = Word::getLength(y);
1394
7524
      Trace("strings-solve-debug")
1395
3762
          << "Simple Case 4 : Const Split : " << x << " vs " << y
1396
3762
          << " at index " << index << ", isRev = " << isRev << std::endl;
1397
3762
      size_t minLen = std::min(lenX, lenY);
1398
      bool isSameFix =
1399
3762
          isRev ? Word::rstrncmp(x, y, minLen) : Word::strncmp(x, y, minLen);
1400
3762
      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
3716
        bool xShorter = lenX < lenY;
1409
3716
        NormalForm& nfl = xShorter ? nfj : nfi;
1410
7432
        Node cl = xShorter ? y : x;
1411
7432
        Node ns = xShorter ? x : y;
1412
1413
7432
        Node remainderStr;
1414
3716
        if (isRev)
1415
        {
1416
2516
          size_t newlen = std::max(lenX, lenY) - minLen;
1417
2516
          remainderStr = Word::substr(cl, 0, newlen);
1418
        }
1419
        else
1420
        {
1421
1200
          remainderStr = Word::substr(cl, minLen);
1422
        }
1423
7432
        Trace("strings-solve-debug-test")
1424
3716
            << "Break normal form of " << cl << " into " << ns << ", "
1425
3716
            << remainderStr << std::endl;
1426
3716
        nfl.splitConstant(index, ns, remainderStr);
1427
3716
        index++;
1428
3716
        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
92
        std::vector<Node> antec;
1437
46
        NormalForm::getExplanationForPrefixEq(nfi, nfj, index, index, antec);
1438
46
        d_im.sendInference(antec, d_false, InferenceId::STRINGS_N_CONST, isRev, true);
1439
46
        break;
1440
      }
1441
    }
1442
1443
    // The candidate inference "info"
1444
25094
    CoreInferInfo info(InferenceId::UNKNOWN);
1445
12547
    InferInfo& iinfo = info.d_infer;
1446
12547
    info.d_index = index;
1447
    // for debugging
1448
12547
    info.d_i = nfi.d_base;
1449
12547
    info.d_j = nfj.d_base;
1450
12547
    info.d_rev = isRev;
1451
12547
    Assert(index < nfiv.size() - rproc && index < nfjv.size() - rproc);
1452
58688
    if (!d_state.areDisequal(xLenTerm, yLenTerm) && !d_state.areEqual(xLenTerm, yLenTerm)
1453
8500
        && !x.isConst()
1454
55831
        && !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
9576
      Trace("strings-solve-debug")
1462
4788
          << "Non-simple Case 1 : string lengths neither equal nor disequal"
1463
4788
          << std::endl;
1464
9576
      Node lenEq = nm->mkNode(EQUAL, xLenTerm, yLenTerm);
1465
4788
      lenEq = Rewriter::rewrite(lenEq);
1466
4788
      iinfo.d_conc = nm->mkNode(OR, lenEq, lenEq.negate());
1467
4788
      iinfo.setId(InferenceId::STRINGS_LEN_SPLIT);
1468
4788
      info.d_pendingPhase[lenEq] = true;
1469
4788
      pinfer.push_back(info);
1470
4788
      break;
1471
    }
1472
1473
15518
    Trace("strings-solve-debug")
1474
7759
        << "Non-simple Case 2 : must compare strings" << std::endl;
1475
7759
    int lhsLoopIdx = -1;
1476
7759
    int rhsLoopIdx = -1;
1477
7759
    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
7337
    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
4705
      Assert(!x.isConst() || !y.isConst());
1515
4705
      NormalForm& nfc = x.isConst() ? nfi : nfj;
1516
4705
      const std::vector<Node>& nfcv = nfc.d_nf;
1517
4705
      NormalForm& nfnc = x.isConst() ? nfj : nfi;
1518
4705
      const std::vector<Node>& nfncv = nfnc.d_nf;
1519
9410
      Node nc = nfncv[index];
1520
4705
      Assert(!nc.isConst()) << "Other string is not constant.";
1521
4705
      Assert(nc.getKind() != STRING_CONCAT) << "Other string is not CONCAT.";
1522
      // explanation for why nc is non-empty
1523
9410
      Node expNonEmpty = d_state.explainNonEmpty(nc);
1524
4705
      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
4705
      size_t ncIndex = index + 1;
1563
9410
      Node nextConstStr = nfnc.collectConstantStringAt(ncIndex);
1564
4705
      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
2559
        size_t cIndex = index;
1571
4320
        Node stra = nfc.collectConstantStringAt(cIndex);
1572
2559
        Assert(!stra.isNull());
1573
4320
        Node strb = nextConstStr;
1574
1575
        // Since `nc` is non-empty, we use the non-empty overlap
1576
2559
        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
2559
        if (p > 1)
1582
        {
1583
798
          NormalForm::getExplanationForPrefixEq(
1584
              nfc, nfnc, cIndex, ncIndex, iinfo.d_premises);
1585
798
          iinfo.d_premises.push_back(expNonEmpty);
1586
          // make the conclusion
1587
798
          SkolemCache* skc = d_termReg.getSkolemCache();
1588
          Node xcv =
1589
1596
              nm->mkNode(STRING_CONCAT, isRev ? strb : nc, isRev ? nc : strb);
1590
1596
          std::vector<Node> newSkolems;
1591
1596
          iinfo.d_conc = getConclusion(
1592
798
              xcv, stra, PfRule::CONCAT_CPROP, isRev, skc, newSkolems);
1593
798
          Assert(newSkolems.size() == 1);
1594
798
          iinfo.d_skolems[LENGTH_SPLIT].push_back(newSkolems[0]);
1595
798
          iinfo.setId(InferenceId::STRINGS_SSPLIT_CST_PROP);
1596
798
          iinfo.d_idRev = isRev;
1597
798
          pinfer.push_back(info);
1598
798
          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
3907
      SkolemCache* skc = d_termReg.getSkolemCache();
1607
7814
      std::vector<Node> newSkolems;
1608
11721
      iinfo.d_conc = getConclusion(
1609
7814
          nc, nfcv[index], PfRule::CONCAT_CSPLIT, isRev, skc, newSkolems);
1610
11721
      NormalForm::getExplanationForPrefixEq(
1611
7814
          nfi, nfj, index, index, iinfo.d_premises);
1612
3907
      iinfo.d_premises.push_back(expNonEmpty);
1613
3907
      Assert(newSkolems.size() == 1);
1614
3907
      iinfo.d_skolems[LENGTH_SPLIT].push_back(newSkolems[0]);
1615
3907
      iinfo.setId(InferenceId::STRINGS_SSPLIT_CST);
1616
3907
      iinfo.d_idRev = isRev;
1617
3907
      pinfer.push_back(info);
1618
3907
      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
2632
    Assert(d_state.areDisequal(xLenTerm, yLenTerm));
1627
2632
    Assert(!x.isConst());
1628
2632
    Assert(!y.isConst());
1629
1630
2632
    int32_t lentTestSuccess = -1;
1631
5264
    Node lenConstraint;
1632
5264
    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
6618
      for (size_t e = 0; e < 2; e++)
1637
      {
1638
8975
        Node t = e == 0 ? x : y;
1639
        // do not infer constants are larger than variables
1640
4989
        if (!t.isConst())
1641
        {
1642
8975
          Node lt1 = e == 0 ? xLenTerm : yLenTerm;
1643
8975
          Node lt2 = e == 0 ? yLenTerm : xLenTerm;
1644
8975
          Node entLit = Rewriter::rewrite(nm->mkNode(GT, lt1, lt2));
1645
4989
          std::pair<bool, Node> et = d_state.entailmentCheck(
1646
8975
              options::TheoryOfMode::THEORY_OF_TYPE_BASED, entLit);
1647
4989
          if (et.first)
1648
          {
1649
2006
            Trace("strings-entail")
1650
1003
                << "Strings entailment : " << entLit
1651
1003
                << " is entailed in the current context." << std::endl;
1652
2006
            Trace("strings-entail")
1653
1003
                << "  explanation was : " << et.second << std::endl;
1654
1003
            lentTestSuccess = e;
1655
1003
            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
1003
            iinfo.d_noExplain.push_back(lenConstraint);
1664
1003
            break;
1665
          }
1666
        }
1667
      }
1668
    }
1669
    // lcVec stores the length constraint portion of the antecedant.
1670
5264
    std::vector<Node> lcVec;
1671
2632
    if (lenConstraint.isNull())
1672
    {
1673
      // will do split on length
1674
1629
      lenConstraint = nm->mkNode(EQUAL, xLenTerm, yLenTerm).negate();
1675
1629
      lcVec.push_back(lenConstraint);
1676
    }
1677
    else
1678
    {
1679
1003
      utils::flattenOp(AND, lenConstraint, lcVec);
1680
    }
1681
1682
2632
    NormalForm::getExplanationForPrefixEq(nfi, nfj, index, index, iinfo.d_premises);
1683
    // Add premises for x != "" ^ y != ""
1684
7896
    for (unsigned xory = 0; xory < 2; xory++)
1685
    {
1686
10528
      Node t = xory == 0 ? x : y;
1687
10528
      Node tnz = d_state.explainNonEmpty(t);
1688
5264
      if (!tnz.isNull())
1689
      {
1690
5264
        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
2632
    SkolemCache* skc = d_termReg.getSkolemCache();
1700
5264
    std::vector<Node> newSkolems;
1701
    // make the conclusion
1702
2632
    if (lentTestSuccess == -1)
1703
    {
1704
1629
      iinfo.setId(InferenceId::STRINGS_SSPLIT_VAR);
1705
1629
      iinfo.d_conc =
1706
3258
          getConclusion(x, y, PfRule::CONCAT_SPLIT, isRev, skc, newSkolems);
1707
    }
1708
1003
    else if (lentTestSuccess == 0)
1709
    {
1710
275
      iinfo.setId(InferenceId::STRINGS_SSPLIT_VAR_PROP);
1711
275
      iinfo.d_conc =
1712
550
          getConclusion(x, y, PfRule::CONCAT_LPROP, isRev, skc, newSkolems);
1713
    }
1714
    else
1715
    {
1716
728
      Assert(lentTestSuccess == 1);
1717
728
      iinfo.setId(InferenceId::STRINGS_SSPLIT_VAR_PROP);
1718
728
      iinfo.d_conc =
1719
1456
          getConclusion(y, x, PfRule::CONCAT_LPROP, isRev, skc, newSkolems);
1720
    }
1721
    // add the length constraint(s) as the last antecedant
1722
5264
    Node lc = utils::mkAnd(lcVec);
1723
2632
    iinfo.d_premises.push_back(lc);
1724
2632
    iinfo.d_idRev = isRev;
1725
2632
    pinfer.push_back(info);
1726
2632
    break;
1727
20119
  }
1728
}
1729
1730
7759
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
7759
  int has_loop[2] = { -1, -1 };
1738
23277
  for (unsigned r = 0; r < 2; r++)
1739
  {
1740
15518
    NormalForm& nf = r == 0 ? nfi : nfj;
1741
15518
    NormalForm& nfo = r == 0 ? nfj : nfi;
1742
15518
    std::vector<Node>& nfv = nf.d_nf;
1743
15518
    std::vector<Node>& nfov = nfo.d_nf;
1744
15518
    if (nfov[index].isConst())
1745
    {
1746
4761
      continue;
1747
    }
1748
38667
    for (unsigned lp = index + 1, lpEnd = nfv.size() - rproc; lp < lpEnd; lp++)
1749
    {
1750
28394
      if (nfv[lp] == nfov[index])
1751
      {
1752
484
        has_loop[r] = lp;
1753
484
        break;
1754
      }
1755
    }
1756
  }
1757
7759
  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
7275
    Trace("strings-solve-debug") << "No loops detected." << std::endl;
1763
7275
    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
4462
void CoreSolver::processDeq(Node ni, Node nj)
1980
{
1981
4462
  NodeManager* nm = NodeManager::currentNM();
1982
4462
  NormalForm& nfni = getNormalForm(ni);
1983
4462
  NormalForm& nfnj = getNormalForm(nj);
1984
1985
4462
  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
9102
    for (size_t i = 0; i < 2; i++)
1994
    {
1995
6076
      NormalForm& nfc = i == 0 ? nfni : nfnj;
1996
6076
      if (nfc.d_nf.size() == 0 || nfc.d_nf[0].getKind() != SEQ_UNIT)
1997
      {
1998
        // may need to look at the other side
1999
6062
        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
3036
    Trace("strings-solve-debug") << "...trivial" << std::endl;
2046
3036
    return;
2047
  }
2048
2049
2844
  std::vector<Node> nfi = nfni.d_nf;
2050
2844
  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
3880
  for (uint32_t i = 0; i < 2; i++)
2057
  {
2058
5198
    Node c = d_bsolver.getConstantEqc(i == 0 ? ni : nj);
2059
2740
    if (!c.isNull())
2060
    {
2061
      int findex, lindex;
2062
1020
      if (!StringsEntail::canConstantContainList(
2063
              c, i == 0 ? nfj : nfi, findex, lindex))
2064
      {
2065
564
        Trace("strings-solve-debug")
2066
282
            << "Disequality: constant cannot contain list" << std::endl;
2067
282
        return;
2068
      }
2069
    }
2070
  }
2071
2072
1140
  if (processReverseDeq(nfi, nfj, ni, nj))
2073
  {
2074
260
    Trace("strings-solve-debug") << "...processed reverse" << std::endl;
2075
260
    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
1140
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
1140
  std::reverse(nfi.begin(), nfi.end());
2276
1140
  std::reverse(nfj.begin(), nfj.end());
2277
2278
1140
  size_t index = 0;
2279
1140
  bool ret = processSimpleDeq(nfi, nfj, ni, nj, index, true);
2280
2281
  // reverse normal form of i, j
2282
1140
  std::reverse(nfi.begin(), nfi.end());
2283
1140
  std::reverse(nfj.begin(), nfj.end());
2284
2285
1140
  return ret;
2286
}
2287
2288
2020
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
4040
  TypeNode stype = ni.getType();
2296
4040
  Node emp = Word::mkEmptyWord(stype);
2297
2298
2020
  NormalForm& nfni = getNormalForm(ni);
2299
2020
  NormalForm& nfnj = getNormalForm(nj);
2300
2814
  while (index < nfi.size() || index < nfj.size())
2301
  {
2302
2417
    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
2535
    Node x = nfi[index];
2345
2535
    Node y = nfj[index];
2346
4830
    Trace("strings-solve-debug")
2347
2415
        << "...Processing(QED) " << x << " " << y << std::endl;
2348
2692
    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
277
      index++;
2355
277
      continue;
2356
    }
2357
2358
2138
    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
226
    Assert(x.isConst() && y.isConst());
2387
226
    size_t xLen = Word::getLength(x);
2388
226
    size_t yLen = Word::getLength(y);
2389
226
    size_t shortLen = std::min(xLen, yLen);
2390
    bool isSameFix =
2391
226
        isRev ? Word::rstrncmp(x, y, shortLen) : Word::strncmp(x, y, shortLen);
2392
226
    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
106
      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
12569
bool CoreSolver::isNormalFormPair( Node n1, Node n2 ) {
2465
12569
  if (n1>n2)
2466
  {
2467
3586
    return isNormalFormPair(n2,n1);
2468
  }
2469
  //Trace("strings-debug") << "is normal form pair. " << n1 << " " << n2 << std::endl;
2470
8983
  NodeIntMap::const_iterator it = d_nfPairs.find(n1);
2471
8983
  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
8983
  return false;
2482
}
2483
2484
9730
void CoreSolver::checkNormalFormsDeq()
2485
{
2486
9730
  eq::EqualityEngine* ee = d_state.getEqualityEngine();
2487
19102
  std::map< Node, std::map< Node, bool > > processed;
2488
2489
9730
  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
37855
  for (const Node& eq : deqs)
2493
  {
2494
56250
    Node n[2];
2495
84375
    for( unsigned i=0; i<2; i++ ){
2496
56250
      n[i] = ee->getRepresentative( eq[i] );
2497
    }
2498
28125
    if( processed[n[0]].find( n[1] )==processed[n[0]].end() ){
2499
24000
      processed[n[0]][n[1]] = true;
2500
48000
      Node lt[2];
2501
72000
      for( unsigned i=0; i<2; i++ ){
2502
48000
        EqcInfo* ei = d_state.getOrMakeEqcInfo(n[i], false);
2503
48000
        lt[i] = ei ? ei->d_lengthTerm : Node::null();
2504
48000
        if( lt[i].isNull() ){
2505
          lt[i] = eq[i];
2506
        }
2507
48000
        lt[i] = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, lt[i] );
2508
      }
2509
24000
      if (!d_state.areEqual(lt[0], lt[1]) && !d_state.areDisequal(lt[0], lt[1]))
2510
      {
2511
314
        d_im.sendSplit(lt[0], lt[1], InferenceId::STRINGS_DEQ_LENGTH_SP);
2512
      }
2513
    }
2514
  }
2515
2516
9730
  if (d_im.hasProcessed())
2517
  {
2518
    // added splitting lemma above
2519
202
    return;
2520
  }
2521
  // otherwise, look at pairs of equivalence classes with equal lengths
2522
18900
  std::map<TypeNode, std::vector<std::vector<Node> > > colsT;
2523
18900
  std::map<TypeNode, std::vector<Node> > ltsT;
2524
9528
  d_state.separateByLength(d_strings_eqc, colsT, ltsT);
2525
12848
  for (std::pair<const TypeNode, std::vector<std::vector<Node> > >& ct : colsT)
2526
  {
2527
3476
    std::vector<std::vector<Node> >& cols = ct.second;
2528
24372
    for( unsigned i=0; i<cols.size(); i++ ){
2529
21052
      if (cols[i].size() > 1 && !d_im.hasPendingLemma())
2530
      {
2531
4936
        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
22449
        for( unsigned j=0; j<cols[i].size(); j++ ){
2541
61785
          for( unsigned k=(j+1); k<cols[i].size(); k++ ){
2542
            //for strings that are disequal, but have the same length
2543
44272
            if (cols[i][j].isConst() && cols[i][k].isConst())
2544
            {
2545
              // if both are constants, they should be distinct, and its trivial
2546
30584
              Assert(cols[i][j] != cols[i][k]);
2547
            }
2548
13688
            else if (d_state.areDisequal(cols[i][j], cols[i][k]))
2549
            {
2550
4462
              Assert(!d_state.isInConflict());
2551
4462
              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
4462
              processDeq(cols[i][j], cols[i][k]);
2562
4462
              if (d_im.hasProcessed())
2563
              {
2564
312
                return;
2565
              }
2566
            }
2567
          }
2568
        }
2569
      }
2570
    }
2571
  }
2572
}
2573
2574
9036
void CoreSolver::checkLengthsEqc() {
2575
38593
  for (unsigned i = 0; i < d_strings_eqc.size(); i++)
2576
  {
2577
59114
    TypeNode stype = d_strings_eqc[i].getType();
2578
29557
    NormalForm& nfi = getNormalForm(d_strings_eqc[i]);
2579
59114
    Trace("strings-process-debug")
2580
29557
        << "Process length constraints for " << d_strings_eqc[i] << std::endl;
2581
    // check if there is a length term for this equivalence class
2582
29557
    EqcInfo* ei = d_state.getOrMakeEqcInfo(d_strings_eqc[i], false);
2583
59114
    Node lt = ei ? ei->d_lengthTerm : Node::null();
2584
29557
    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
59114
    Node llt = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, lt);
2591
    // now, check if length normalization has occurred
2592
29557
    if (ei->d_normalizedLength.get().isNull())
2593
    {
2594
53564
      Node nf = utils::mkNConcat(nfi.d_nf, stype);
2595
26782
      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
53564
      std::vector<Node> ant;
2609
26782
      ant.insert(ant.end(), nfi.d_exp.begin(), nfi.d_exp.end());
2610
26782
      ant.push_back(lt.eqNode(nfi.d_base));
2611
53564
      Node lc = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, nf);
2612
53564
      Node lcr = Rewriter::rewrite(lc);
2613
53564
      Trace("strings-process-debug")
2614
26782
          << "Rewrote length " << lc << " to " << lcr << std::endl;
2615
26782
      if (!d_state.areEqual(llt, lcr))
2616
      {
2617
10630
        Node eq = llt.eqNode(lc);
2618
5315
        ei->d_normalizedLength.set(eq);
2619
5315
        d_im.sendInference(ant, eq, InferenceId::STRINGS_LEN_NORM, false, true);
2620
      }
2621
    }
2622
  }
2623
9036
}
2624
2625
1955
bool CoreSolver::processInferInfo(CoreInferInfo& cii)
2626
{
2627
1955
  InferInfo& ii = cii.d_infer;
2628
  // rewrite the conclusion, ensure non-trivial
2629
3910
  Node concr = Rewriter::rewrite(ii.d_conc);
2630
2631
1955
  if (concr == d_true)
2632
  {
2633
    // conclusion rewrote to true
2634
    return false;
2635
  }
2636
  // process the state change to this solver
2637
1955
  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
2588
  for (const std::pair<const Node, bool>& pp : cii.d_pendingPhase)
2644
  {
2645
1266
    Node ppr = Rewriter::rewrite(pp.first);
2646
633
    d_im.addPendingPhaseRequirement(ppr, pp.second);
2647
  }
2648
2649
  // send the inference, which is a lemma
2650
1955
  d_im.sendInference(ii, true);
2651
2652
1955
  return true;
2653
}
2654
2655
}  // namespace strings
2656
}  // namespace theory
2657
28194
}  // namespace cvc5