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

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