GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/core_solver.cpp Lines: 1114 1371 81.3 %
Date: 2021-08-17 Branches: 2557 6307 40.5 %

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