GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/theory_strings_preprocess.cpp Lines: 488 488 100.0 %
Date: 2021-05-22 Branches: 1440 2812 51.2 %

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
 * Strings Preprocess.
14
 */
15
16
#include "theory/strings/theory_strings_preprocess.h"
17
18
#include "expr/kind.h"
19
#include "expr/skolem_manager.h"
20
#include "options/smt_options.h"
21
#include "options/strings_options.h"
22
#include "smt/logic_exception.h"
23
#include "theory/quantifiers/quantifiers_attributes.h"
24
#include "theory/strings/arith_entail.h"
25
#include "theory/strings/sequences_rewriter.h"
26
#include "theory/strings/word.h"
27
#include "util/statistics_registry.h"
28
29
using namespace cvc5;
30
using namespace cvc5::kind;
31
32
namespace cvc5 {
33
namespace theory {
34
namespace strings {
35
36
/** Mapping to a dummy node for marking an attribute on internal quantified
37
 * formulas */
38
struct QInternalVarAttributeId
39
{
40
};
41
typedef expr::Attribute<QInternalVarAttributeId, Node> QInternalVarAttribute;
42
43
9496
StringsPreprocess::StringsPreprocess(SkolemCache* sc,
44
9496
                                     HistogramStat<Kind>* statReductions)
45
9496
    : d_sc(sc), d_statReductions(statReductions)
46
{
47
9496
}
48
49
9496
StringsPreprocess::~StringsPreprocess(){
50
51
9496
}
52
53
3310
Node StringsPreprocess::reduce(Node t,
54
                               std::vector<Node>& asserts,
55
                               SkolemCache* sc)
56
{
57
6620
  Trace("strings-preprocess-debug")
58
3310
      << "StringsPreprocess::reduce: " << t << std::endl;
59
3310
  Node retNode = t;
60
3310
  NodeManager* nm = NodeManager::currentNM();
61
3310
  SkolemManager* sm = nm->getSkolemManager();
62
6620
  Node zero = nm->mkConst(Rational(0));
63
6620
  Node one = nm->mkConst(Rational(1));
64
6620
  Node negOne = nm->mkConst(Rational(-1));
65
66
3310
  if( t.getKind() == kind::STRING_SUBSTR ) {
67
    // processing term:  substr( s, n, m )
68
2320
    Node s = t[0];
69
2320
    Node n = t[1];
70
2320
    Node m = t[2];
71
2320
    Node skt = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "sst");
72
2320
    Node t12 = nm->mkNode(PLUS, n, m);
73
2320
    Node lt0 = nm->mkNode(STRING_LENGTH, s);
74
    //start point is greater than or equal zero
75
2320
    Node c1 = nm->mkNode(GEQ, n, zero);
76
    //start point is less than end of string
77
2320
    Node c2 = nm->mkNode(GT, lt0, n);
78
    //length is positive
79
2320
    Node c3 = nm->mkNode(GT, m, zero);
80
2320
    Node cond = nm->mkNode(AND, c1, c2, c3);
81
82
2320
    Node emp = Word::mkEmptyWord(t.getType());
83
84
2320
    Node sk1 = sc->mkSkolemCached(s, n, SkolemCache::SK_PREFIX, "sspre");
85
2320
    Node sk2 = sc->mkSkolemCached(s, t12, SkolemCache::SK_SUFFIX_REM, "sssufr");
86
2320
    Node b11 = s.eqNode(nm->mkNode(STRING_CONCAT, sk1, skt, sk2));
87
    //length of first skolem is second argument
88
2320
    Node b12 = nm->mkNode(STRING_LENGTH, sk1).eqNode(n);
89
2320
    Node lsk2 = nm->mkNode(STRING_LENGTH, sk2);
90
    // Length of the suffix is either 0 (in the case where m + n > len(s)) or
91
    // len(s) - n -m
92
    Node b13 = nm->mkNode(
93
        OR,
94
2320
        nm->mkNode(EQUAL, lsk2, nm->mkNode(MINUS, lt0, nm->mkNode(PLUS, n, m))),
95
4640
        nm->mkNode(EQUAL, lsk2, zero));
96
    // Length of the result is at most m
97
2320
    Node b14 = nm->mkNode(LEQ, nm->mkNode(STRING_LENGTH, skt), m);
98
99
2320
    Node b1 = nm->mkNode(AND, {b11, b12, b13, b14});
100
2320
    Node b2 = skt.eqNode(emp);
101
2320
    Node lemma = nm->mkNode(ITE, cond, b1, b2);
102
103
    // assert:
104
    // IF    n >=0 AND len( s ) > n AND m > 0
105
    // THEN: s = sk1 ++ skt ++ sk2 AND
106
    //       len( sk1 ) = n AND
107
    //       ( len( sk2 ) = len( s )-(n+m) OR len( sk2 ) = 0 ) AND
108
    //       len( skt ) <= m
109
    // ELSE: skt = ""
110
    //
111
    // Note: The length of sk2 is either 0 (if the n + m is greater or equal to
112
    // the length of s) or len(s) - (n + m) otherwise. If n + m is greater or
113
    // equal to the length of s, then len(s) - (n + m) is negative and in
114
    // conflict with lengths always being positive, so only len(sk2) = 0 may be
115
    // satisfied. If n + m is less than the length of s, then len(sk2) = 0
116
    // cannot be satisfied because we have the constraint that len(skt) <= m,
117
    // so sk2 must be greater than 0.
118
1160
    asserts.push_back(lemma);
119
120
    // Thus, substr( s, n, m ) = skt
121
1160
    retNode = skt;
122
  }
123
2150
  else if (t.getKind() == kind::STRING_UPDATE)
124
  {
125
    // processing term:  update( s, n, r )
126
30
    Node s = t[0];
127
30
    Node n = t[1];
128
30
    Node r = t[2];
129
30
    Node skt = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "sst");
130
30
    Node ls = nm->mkNode(STRING_LENGTH, s);
131
    // start point is greater than or equal zero
132
30
    Node c1 = nm->mkNode(GEQ, n, zero);
133
    // start point is less than end of string
134
30
    Node c2 = nm->mkNode(GT, ls, n);
135
30
    Node cond = nm->mkNode(AND, c1, c2);
136
137
    // substr(r,0,|s|-n)
138
30
    Node lens = nm->mkNode(STRING_LENGTH, s);
139
30
    Node rs;
140
15
    if (r.isConst() && Word::getLength(r) == 1)
141
    {
142
      // optimization: don't need to take substring for single characters, due
143
      // to guard on where it is used in the reduction below.
144
2
      rs = r;
145
    }
146
    else
147
    {
148
13
      rs = nm->mkNode(STRING_SUBSTR, r, zero, nm->mkNode(MINUS, lens, n));
149
    }
150
30
    Node rslen = nm->mkNode(STRING_LENGTH, rs);
151
30
    Node nsuf = nm->mkNode(PLUS, n, rslen);
152
    // substr(s, n, len(substr(r,0,|s|-n))), which is used for formalizing the
153
    // purification variable sk3 below.
154
30
    Node ssubstr = nm->mkNode(STRING_SUBSTR, s, n, rslen);
155
156
30
    Node sk1 = sc->mkSkolemCached(s, n, SkolemCache::SK_PREFIX, "sspre");
157
    Node sk2 =
158
30
        sc->mkSkolemCached(s, nsuf, SkolemCache::SK_SUFFIX_REM, "sssufr");
159
30
    Node sk3 = sc->mkSkolemCached(ssubstr, SkolemCache::SK_PURIFY, "ssubstr");
160
30
    Node a1 = skt.eqNode(nm->mkNode(STRING_CONCAT, sk1, rs, sk2));
161
30
    Node a2 = s.eqNode(nm->mkNode(STRING_CONCAT, sk1, sk3, sk2));
162
    // length of first skolem is second argument
163
30
    Node a3 = nm->mkNode(STRING_LENGTH, sk1).eqNode(n);
164
165
30
    Node b1 = nm->mkNode(AND, a1, a2, a3);
166
30
    Node b2 = skt.eqNode(s);
167
30
    Node lemma = nm->mkNode(ITE, cond, b1, b2);
168
169
    // assert:
170
    // IF    n >=0 AND n < len( s )
171
    // THEN: skt = sk1 ++ substr(r,0,len(s)-n) ++ sk2 AND
172
    //       s = sk1 ++ sk3 ++ sk2 AND
173
    //       len( sk1 ) = n
174
    // ELSE: skt = s
175
    // We use an optimization where r is used instead of substr(r,0,len(s)-n)
176
    // if r is a constant of length one.
177
15
    asserts.push_back(lemma);
178
179
    // Thus, str.update( s, n, r ) = skt
180
15
    retNode = skt;
181
  }
182
2135
  else if (t.getKind() == kind::STRING_STRIDOF)
183
  {
184
    // processing term:  indexof( x, y, n )
185
268
    Node x = t[0];
186
268
    Node y = t[1];
187
268
    Node n = t[2];
188
    Node skk = sc->mkTypedSkolemCached(
189
268
        nm->integerType(), t, SkolemCache::SK_PURIFY, "iok");
190
191
268
    Node negone = nm->mkConst(Rational(-1));
192
193
    // substr( x, n, len( x ) - n )
194
    Node st = nm->mkNode(STRING_SUBSTR,
195
                         x,
196
                         n,
197
268
                         nm->mkNode(MINUS, nm->mkNode(STRING_LENGTH, x), n));
198
    Node io2 =
199
268
        sc->mkSkolemCached(st, y, SkolemCache::SK_FIRST_CTN_PRE, "iopre");
200
    Node io4 =
201
268
        sc->mkSkolemCached(st, y, SkolemCache::SK_FIRST_CTN_POST, "iopost");
202
203
    // ~contains( substr( x, n, len( x ) - n ), y )
204
268
    Node c11 = nm->mkNode(STRING_STRCTN, st, y).negate();
205
    // n > len( x )
206
268
    Node c12 = nm->mkNode(GT, n, nm->mkNode(STRING_LENGTH, x));
207
    // 0 > n
208
268
    Node c13 = nm->mkNode(GT, zero, n);
209
268
    Node cond1 = nm->mkNode(OR, c11, c12, c13);
210
    // skk = -1
211
268
    Node cc1 = skk.eqNode(negone);
212
213
    // y = ""
214
268
    Node emp = Word::mkEmptyWord(x.getType());
215
268
    Node cond2 = y.eqNode(emp);
216
    // skk = n
217
268
    Node cc2 = skk.eqNode(t[2]);
218
219
    // substr( x, n, len( x ) - n ) = str.++( io2, y, io4 )
220
268
    Node c31 = st.eqNode(nm->mkNode(STRING_CONCAT, io2, y, io4));
221
    // ~contains( str.++( io2, substr( y, 0, len( y ) - 1) ), y )
222
    Node c32 =
223
536
        nm->mkNode(
224
              STRING_STRCTN,
225
268
              nm->mkNode(
226
                  STRING_CONCAT,
227
                  io2,
228
268
                  nm->mkNode(
229
                      STRING_SUBSTR,
230
                      y,
231
                      zero,
232
268
                      nm->mkNode(MINUS, nm->mkNode(STRING_LENGTH, y), one))),
233
              y)
234
268
            .negate();
235
    // skk = n + len( io2 )
236
268
    Node c33 = skk.eqNode(nm->mkNode(PLUS, n, nm->mkNode(STRING_LENGTH, io2)));
237
268
    Node cc3 = nm->mkNode(AND, c31, c32, c33);
238
239
    // assert:
240
    // IF:   ~contains( substr( x, n, len( x ) - n ), y ) OR n > len(x) OR 0 > n
241
    // THEN: skk = -1
242
    // ELIF: y = ""
243
    // THEN: skk = n
244
    // ELSE: substr( x, n, len( x ) - n ) = str.++( io2, y, io4 ) ^
245
    //       ~contains( str.++( io2, substr( y, 0, len( y ) - 1) ), y ) ^
246
    //       skk = n + len( io2 )
247
    // for fresh io2, io4.
248
268
    Node rr = nm->mkNode(ITE, cond1, cc1, nm->mkNode(ITE, cond2, cc2, cc3));
249
134
    asserts.push_back(rr);
250
251
    // Thus, indexof( x, y, n ) = skk.
252
134
    retNode = skk;
253
  }
254
2001
  else if (t.getKind() == STRING_ITOS)
255
  {
256
    // processing term:  int.to.str( n )
257
54
    Node n = t[0];
258
54
    Node itost = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "itost");
259
54
    Node leni = nm->mkNode(STRING_LENGTH, itost);
260
261
54
    std::vector<Node> conc;
262
54
    std::vector< TypeNode > argTypes;
263
27
    argTypes.push_back(nm->integerType());
264
    Node u =
265
54
        sm->mkDummySkolem("U", nm->mkFunctionType(argTypes, nm->integerType()));
266
267
54
    Node lem = nm->mkNode(GEQ, leni, one);
268
27
    conc.push_back(lem);
269
270
27
    lem = n.eqNode(nm->mkNode(APPLY_UF, u, leni));
271
27
    conc.push_back(lem);
272
273
27
    lem = zero.eqNode(nm->mkNode(APPLY_UF, u, zero));
274
27
    conc.push_back(lem);
275
276
54
    Node x = SkolemCache::mkIndexVar(t);
277
54
    Node xPlusOne = nm->mkNode(PLUS, x, one);
278
54
    Node xbv = nm->mkNode(BOUND_VAR_LIST, x);
279
54
    Node g = nm->mkNode(AND, nm->mkNode(GEQ, x, zero), nm->mkNode(LT, x, leni));
280
54
    Node sx = nm->mkNode(STRING_SUBSTR, itost, x, one);
281
54
    Node ux = nm->mkNode(APPLY_UF, u, x);
282
54
    Node ux1 = nm->mkNode(APPLY_UF, u, xPlusOne);
283
54
    Node c0 = nm->mkNode(STRING_TO_CODE, nm->mkConst(String("0")));
284
54
    Node c = nm->mkNode(MINUS, nm->mkNode(STRING_TO_CODE, sx), c0);
285
286
54
    Node ten = nm->mkConst(Rational(10));
287
54
    Node eq = ux1.eqNode(nm->mkNode(PLUS, c, nm->mkNode(MULT, ten, ux)));
288
    Node leadingZeroPos =
289
54
        nm->mkNode(AND, x.eqNode(zero), nm->mkNode(GT, leni, one));
290
    Node cb = nm->mkNode(
291
        AND,
292
54
        nm->mkNode(GEQ, c, nm->mkNode(ITE, leadingZeroPos, one, zero)),
293
108
        nm->mkNode(LT, c, ten));
294
295
54
    Node ux1lem = nm->mkNode(GEQ, n, ux1);
296
297
27
    lem = nm->mkNode(OR, g.negate(), nm->mkNode(AND, eq, cb, ux1lem));
298
27
    lem = mkForallInternal(xbv, lem);
299
27
    conc.push_back(lem);
300
301
54
    Node nonneg = nm->mkNode(GEQ, n, zero);
302
303
54
    Node emp = Word::mkEmptyWord(t.getType());
304
27
    lem = nm->mkNode(ITE, nonneg, nm->mkNode(AND, conc), itost.eqNode(emp));
305
27
    asserts.push_back(lem);
306
    // assert:
307
    // IF n>=0
308
    // THEN:
309
    //   len( itost ) >= 1 ^
310
    //   n = U( len( itost ) ) ^ U( 0 ) = 0 ^
311
    //   forall x. (x>=0 ^ x < str.len(itost)) =>
312
    //     U( x+1 ) = (str.code( str.at(itost, x) )-48) + 10*U( x ) ^
313
    //     ite( x=0 AND str.len(itost)>1, 49, 48 ) <=
314
    //       str.code( str.at(itost, x) ) < 58 ^
315
    //     n >= U(x + 1)
316
    // ELSE
317
    //   itost = ""
318
    // thus:
319
    //   int.to.str( n ) = itost
320
    //
321
    // Note: The conjunct `n >= U(x + 1)` is not needed for correctness but is
322
    // just an optimization.
323
324
    // The function U is an accumulator, where U( x ) for x>0 is the value of
325
    // str.to.int( str.substr( int.to.str( n ), 0, x ) ). For example, for
326
    // n=345, we have that U(1), U(2), U(3) = 3, 34, 345.
327
    // Above, we use str.code to map characters to their integer value, where
328
    // note that str.code( "0" ) = 48. Further notice that
329
    //   ite( x=0 AND str.len(itost)>1, 49, 48 )
330
    // enforces that int.to.str( n ) has no leading zeroes.
331
27
    retNode = itost;
332
1974
  } else if( t.getKind() == kind::STRING_STOI ) {
333
50
    Node s = t[0];
334
    Node stoit = sc->mkTypedSkolemCached(
335
50
        nm->integerType(), t, SkolemCache::SK_PURIFY, "stoit");
336
50
    Node lens = nm->mkNode(STRING_LENGTH, s);
337
338
50
    std::vector<Node> conc1;
339
50
    Node lem = stoit.eqNode(negOne);
340
25
    conc1.push_back(lem);
341
342
50
    Node emp = Word::mkEmptyWord(s.getType());
343
50
    Node sEmpty = s.eqNode(emp);
344
50
    Node k = sm->mkDummySkolem("k", nm->integerType());
345
50
    Node kc1 = nm->mkNode(GEQ, k, zero);
346
50
    Node kc2 = nm->mkNode(LT, k, lens);
347
50
    Node c0 = nm->mkNode(STRING_TO_CODE, nm->mkConst(String("0")));
348
    Node codeSk = nm->mkNode(
349
        MINUS,
350
50
        nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, s, k, one)),
351
100
        c0);
352
50
    Node ten = nm->mkConst(Rational(10));
353
    Node kc3 = nm->mkNode(
354
50
        OR, nm->mkNode(LT, codeSk, zero), nm->mkNode(GEQ, codeSk, ten));
355
25
    conc1.push_back(nm->mkNode(OR, sEmpty, nm->mkNode(AND, kc1, kc2, kc3)));
356
357
50
    std::vector<Node> conc2;
358
50
    std::vector< TypeNode > argTypes;
359
25
    argTypes.push_back(nm->integerType());
360
    Node u =
361
50
        sm->mkDummySkolem("U", nm->mkFunctionType(argTypes, nm->integerType()));
362
363
25
    lem = stoit.eqNode(nm->mkNode(APPLY_UF, u, lens));
364
25
    conc2.push_back(lem);
365
366
25
    lem = zero.eqNode(nm->mkNode(APPLY_UF, u, zero));
367
25
    conc2.push_back(lem);
368
369
25
    lem = nm->mkNode(GT, lens, zero);
370
25
    conc2.push_back(lem);
371
372
50
    Node x = SkolemCache::mkIndexVar(t);
373
50
    Node xbv = nm->mkNode(BOUND_VAR_LIST, x);
374
50
    Node g = nm->mkNode(AND, nm->mkNode(GEQ, x, zero), nm->mkNode(LT, x, lens));
375
50
    Node sx = nm->mkNode(STRING_SUBSTR, s, x, one);
376
50
    Node ux = nm->mkNode(APPLY_UF, u, x);
377
50
    Node ux1 = nm->mkNode(APPLY_UF, u, nm->mkNode(PLUS, x, one));
378
50
    Node c = nm->mkNode(MINUS, nm->mkNode(STRING_TO_CODE, sx), c0);
379
380
50
    Node eq = ux1.eqNode(nm->mkNode(PLUS, c, nm->mkNode(MULT, ten, ux)));
381
50
    Node cb = nm->mkNode(AND, nm->mkNode(GEQ, c, zero), nm->mkNode(LT, c, ten));
382
383
50
    Node ux1lem = nm->mkNode(GEQ, stoit, ux1);
384
385
25
    lem = nm->mkNode(OR, g.negate(), nm->mkNode(AND, eq, cb, ux1lem));
386
25
    lem = mkForallInternal(xbv, lem);
387
25
    conc2.push_back(lem);
388
389
50
    Node sneg = nm->mkNode(LT, stoit, zero);
390
25
    lem = nm->mkNode(ITE, sneg, nm->mkNode(AND, conc1), nm->mkNode(AND, conc2));
391
25
    asserts.push_back(lem);
392
393
    // assert:
394
    // IF stoit < 0
395
    // THEN:
396
    //   stoit = -1 ^
397
    //   ( s = "" OR
398
    //     ( k>=0 ^ k<len( s ) ^ ( str.code( str.at(s, k) ) < 48 OR
399
    //                             str.code( str.at(s, k) ) >= 58 )))
400
    // ELSE:
401
    //   stoit = U( len( s ) ) ^ U( 0 ) = 0 ^
402
    //   str.len( s ) > 0 ^
403
    //   forall x. (x>=0 ^ x < str.len(s)) =>
404
    //     U( x+1 ) = ( str.code( str.at(s, x) ) - 48 ) + 10*U( x ) ^
405
    //     48 <= str.code( str.at(s, x) ) < 58 ^
406
    //     stoit >= U( x+1 )
407
    // Thus, str.to.int( s ) = stoit
408
    //
409
    // Note: The conjunct `stoit >= U( x+1 )` is not needed for correctness but
410
    // is just an optimization.
411
412
25
    retNode = stoit;
413
  }
414
1949
  else if (t.getKind() == kind::SEQ_NTH)
415
  {
416
    // processing term:  str.nth( s, n)
417
    // similar to substr.
418
76
    Node s = t[0];
419
76
    Node n = t[1];
420
76
    Node skt = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "sst");
421
76
    Node t12 = nm->mkNode(PLUS, n, one);
422
76
    Node lt0 = nm->mkNode(STRING_LENGTH, s);
423
    // start point is greater than or equal zero
424
76
    Node c1 = nm->mkNode(GEQ, n, zero);
425
    // start point is less than end of string
426
76
    Node c2 = nm->mkNode(GT, lt0, n);
427
    // check whether this application of seq.nth is defined.
428
76
    Node cond = nm->mkNode(AND, c1, c2);
429
430
    // nodes for the case where `seq.nth` is defined.
431
76
    Node sk1 = sc->mkSkolemCached(s, n, SkolemCache::SK_PREFIX, "sspre");
432
76
    Node sk2 = sc->mkSkolemCached(s, t12, SkolemCache::SK_SUFFIX_REM, "sssufr");
433
76
    Node unit = nm->mkNode(SEQ_UNIT, skt);
434
76
    Node b11 = s.eqNode(nm->mkNode(STRING_CONCAT, sk1, unit, sk2));
435
    // length of first skolem is second argument
436
76
    Node b12 = nm->mkNode(STRING_LENGTH, sk1).eqNode(n);
437
76
    Node lsk2 = nm->mkNode(STRING_LENGTH, sk2);
438
76
    Node b13 = nm->mkNode(EQUAL, lsk2, nm->mkNode(MINUS, lt0, t12));
439
76
    Node b1 = nm->mkNode(AND, b11, b12, b13);
440
441
    // nodes for the case where `seq.nth` is undefined.
442
76
    Node uf = SkolemCache::mkSkolemSeqNth(s.getType(), "Uf");
443
76
    Node b2 = nm->mkNode(EQUAL, skt, nm->mkNode(APPLY_UF, uf, s, n));
444
445
    // the full ite, split on definedness of `seq.nth`
446
76
    Node lemma = nm->mkNode(ITE, cond, b1, b2);
447
448
    // assert:
449
    // IF    n >=0 AND n < len( s )
450
    // THEN: s = sk1 ++ unit(skt) ++ sk2 AND
451
    //       len( sk1 ) = n AND
452
    //       ( len( sk2 ) = len( s )- (n+1)
453
    // ELSE: skt = Uf(s, n), where Uf is a cached skolem function.
454
38
    asserts.push_back(lemma);
455
38
    retNode = skt;
456
  }
457
1911
  else if (t.getKind() == kind::STRING_STRREPL)
458
  {
459
    // processing term: replace( x, y, z )
460
484
    Node x = t[0];
461
484
    Node y = t[1];
462
484
    Node z = t[2];
463
484
    TypeNode tn = t[0].getType();
464
    Node rp1 =
465
484
        sc->mkSkolemCached(x, y, SkolemCache::SK_FIRST_CTN_PRE, "rfcpre");
466
    Node rp2 =
467
484
        sc->mkSkolemCached(x, y, SkolemCache::SK_FIRST_CTN_POST, "rfcpost");
468
484
    Node rpw = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "rpw");
469
470
    // y = ""
471
484
    Node emp = Word::mkEmptyWord(tn);
472
484
    Node cond1 = y.eqNode(emp);
473
    // rpw = str.++( z, x )
474
484
    Node c1 = rpw.eqNode(nm->mkNode(kind::STRING_CONCAT, z, x));
475
476
    // contains( x, y )
477
484
    Node cond2 = nm->mkNode(kind::STRING_STRCTN, x, y);
478
    // x = str.++( rp1, y, rp2 )
479
484
    Node c21 = x.eqNode(nm->mkNode(kind::STRING_CONCAT, rp1, y, rp2));
480
    // rpw = str.++( rp1, z, rp2 )
481
484
    Node c22 = rpw.eqNode(nm->mkNode(kind::STRING_CONCAT, rp1, z, rp2));
482
    // ~contains( str.++( rp1, substr( y, 0, len(y)-1 ) ), y )
483
    Node c23 =
484
968
        nm->mkNode(kind::STRING_STRCTN,
485
484
                   nm->mkNode(
486
                       kind::STRING_CONCAT,
487
                       rp1,
488
484
                       nm->mkNode(kind::STRING_SUBSTR,
489
                                  y,
490
                                  zero,
491
968
                                  nm->mkNode(kind::MINUS,
492
484
                                             nm->mkNode(kind::STRING_LENGTH, y),
493
                                             one))),
494
                   y)
495
484
            .negate();
496
497
    // assert:
498
    //   IF    y=""
499
    //   THEN: rpw = str.++( z, x )
500
    //   ELIF: contains( x, y )
501
    //   THEN: x = str.++( rp1, y, rp2 ) ^
502
    //         rpw = str.++( rp1, z, rp2 ) ^
503
    //         ~contains( str.++( rp1, substr( y, 0, len(y)-1 ) ), y ),
504
    //   ELSE: rpw = x
505
    // for fresh rp1, rp2, rpw
506
    Node rr = nm->mkNode(kind::ITE,
507
                         cond1,
508
                         c1,
509
968
                         nm->mkNode(kind::ITE,
510
                                    cond2,
511
484
                                    nm->mkNode(kind::AND, c21, c22, c23),
512
968
                                    rpw.eqNode(x)));
513
242
    asserts.push_back(rr);
514
515
    // Thus, replace( x, y, z ) = rpw.
516
242
    retNode = rpw;
517
  }
518
1669
  else if (t.getKind() == kind::STRING_STRREPLALL)
519
  {
520
    // processing term: replaceall( x, y, z )
521
24
    Node x = t[0];
522
24
    Node y = t[1];
523
24
    Node z = t[2];
524
24
    Node rpaw = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "rpaw");
525
526
    Node numOcc = sc->mkTypedSkolemCached(
527
24
        nm->integerType(), x, y, SkolemCache::SK_NUM_OCCUR, "numOcc");
528
24
    std::vector<TypeNode> argTypes;
529
12
    argTypes.push_back(nm->integerType());
530
    Node us =
531
24
        sm->mkDummySkolem("Us", nm->mkFunctionType(argTypes, nm->stringType()));
532
24
    TypeNode ufType = nm->mkFunctionType(argTypes, nm->integerType());
533
    Node uf = sc->mkTypedSkolemCached(
534
24
        ufType, x, y, SkolemCache::SK_OCCUR_INDEX, "Uf");
535
536
24
    Node ufno = nm->mkNode(APPLY_UF, uf, numOcc);
537
24
    Node usno = nm->mkNode(APPLY_UF, us, numOcc);
538
24
    Node rem = nm->mkNode(STRING_SUBSTR, x, ufno, nm->mkNode(STRING_LENGTH, x));
539
540
24
    std::vector<Node> lem;
541
12
    lem.push_back(nm->mkNode(GEQ, numOcc, zero));
542
12
    lem.push_back(rpaw.eqNode(nm->mkNode(APPLY_UF, us, zero)));
543
12
    lem.push_back(usno.eqNode(rem));
544
12
    lem.push_back(nm->mkNode(APPLY_UF, uf, zero).eqNode(zero));
545
12
    lem.push_back(nm->mkNode(STRING_STRIDOF, x, y, ufno).eqNode(negOne));
546
547
24
    Node i = SkolemCache::mkIndexVar(t);
548
24
    Node bvli = nm->mkNode(BOUND_VAR_LIST, i);
549
    Node bound =
550
24
        nm->mkNode(AND, nm->mkNode(GEQ, i, zero), nm->mkNode(LT, i, numOcc));
551
24
    Node ufi = nm->mkNode(APPLY_UF, uf, i);
552
24
    Node ufip1 = nm->mkNode(APPLY_UF, uf, nm->mkNode(PLUS, i, one));
553
24
    Node ii = nm->mkNode(STRING_STRIDOF, x, y, ufi);
554
    Node cc = nm->mkNode(
555
        STRING_CONCAT,
556
24
        nm->mkNode(STRING_SUBSTR, x, ufi, nm->mkNode(MINUS, ii, ufi)),
557
        z,
558
48
        nm->mkNode(APPLY_UF, us, nm->mkNode(PLUS, i, one)));
559
560
24
    std::vector<Node> flem;
561
12
    flem.push_back(ii.eqNode(negOne).negate());
562
12
    flem.push_back(nm->mkNode(APPLY_UF, us, i).eqNode(cc));
563
12
    flem.push_back(
564
24
        ufip1.eqNode(nm->mkNode(PLUS, ii, nm->mkNode(STRING_LENGTH, y))));
565
566
24
    Node body = nm->mkNode(OR, bound.negate(), nm->mkNode(AND, flem));
567
24
    Node q = mkForallInternal(bvli, body);
568
12
    lem.push_back(q);
569
570
    // assert:
571
    //   IF y=""
572
    //   THEN: rpaw = x
573
    //   ELSE:
574
    //     numOcc >= 0 ^
575
    //     rpaw = Us(0) ^ Us(numOcc) = substr(x, Uf(numOcc), len(x)) ^
576
    //     Uf(0) = 0 ^ indexof( x, y, Uf(numOcc) ) = -1 ^
577
    //     forall i. 0 <= i < numOcc =>
578
    //        ii != -1 ^
579
    //        Us( i ) = str.substr( x, Uf(i), ii - Uf(i) ) ++ z ++ Us(i+1) ^
580
    //        Uf( i+1 ) = ii + len(y)
581
    //        where ii == indexof( x, y, Uf(i) )
582
583
    // Conceptually, numOcc is the number of occurrences of y in x, Uf( i ) is
584
    // the index to begin searching in x for y after the i^th occurrence of y in
585
    // x, and Us( i ) is the result of processing the remainder after processing
586
    // the i^th occurrence of y in x.
587
24
    Node emp = Word::mkEmptyWord(t.getType());
588
    Node assert =
589
24
        nm->mkNode(ITE, y.eqNode(emp), rpaw.eqNode(x), nm->mkNode(AND, lem));
590
12
    asserts.push_back(assert);
591
592
    // Thus, replaceall( x, y, z ) = rpaw
593
12
    retNode = rpaw;
594
  }
595
1657
  else if (t.getKind() == STRING_REPLACE_RE)
596
  {
597
32
    Node x = t[0];
598
32
    Node y = t[1];
599
32
    Node z = t[2];
600
32
    Node k = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "k");
601
602
32
    std::vector<Node> emptyVec;
603
    Node sigmaStar =
604
32
        nm->mkNode(REGEXP_STAR, nm->mkNode(REGEXP_SIGMA, emptyVec));
605
32
    Node re = nm->mkNode(REGEXP_CONCAT, sigmaStar, y, sigmaStar);
606
    // in_re(x, re.++(_*, y, _*))
607
32
    Node hasMatch = nm->mkNode(STRING_IN_REGEXP, x, re);
608
609
    // in_re("", y)
610
    Node matchesEmpty =
611
32
        nm->mkNode(STRING_IN_REGEXP, nm->mkConst(String("")), y);
612
    // k = z ++ x
613
32
    Node res1 = k.eqNode(nm->mkNode(STRING_CONCAT, z, x));
614
615
    Node k1 =
616
32
        sc->mkSkolemCached(x, y, SkolemCache::SK_FIRST_MATCH_PRE, "rre_pre");
617
    Node k2 =
618
32
        sc->mkSkolemCached(x, y, SkolemCache::SK_FIRST_MATCH, "rre_match");
619
    Node k3 =
620
32
        sc->mkSkolemCached(x, y, SkolemCache::SK_FIRST_MATCH_POST, "rre_post");
621
    // x = k1 ++ k2 ++ k3
622
32
    Node splitX = x.eqNode(nm->mkNode(STRING_CONCAT, k1, k2, k3));
623
    // ~in_re(k1 ++ str.substr(k2, 0, str.len(k2) - 1), re.++(_*, y, _*))
624
32
    Node k2len = nm->mkNode(STRING_LENGTH, k2);
625
    Node firstMatch =
626
64
        nm->mkNode(
627
              STRING_IN_REGEXP,
628
32
              nm->mkNode(
629
                  STRING_CONCAT,
630
                  k1,
631
32
                  nm->mkNode(
632
32
                      STRING_SUBSTR, k2, zero, nm->mkNode(MINUS, k2len, one))),
633
              re)
634
32
            .negate();
635
    // in_re(k2, y)
636
32
    Node k2Match = nm->mkNode(STRING_IN_REGEXP, k2, y);
637
    // k = k1 ++ z ++ k3
638
32
    Node res2 = k.eqNode(nm->mkNode(STRING_CONCAT, k1, z, k3));
639
640
    // IF in_re(x, re.++(_*, y, _*))
641
    // THEN:
642
    //   IF in_re("", y)
643
    //   THEN: k = z ++ x
644
    //   ELSE:
645
    //     x = k1 ++ k2 ++ k3 ^
646
    //     ~in_re(k1 ++ substr(k2, 0, str.len(k2) - 1), re.++(_*, y, _*)) ^
647
    //     in_re(k2, y) ^ k = k1 ++ z ++ k3
648
    // ELSE: k = x
649
48
    asserts.push_back(nm->mkNode(
650
        ITE,
651
        hasMatch,
652
32
        nm->mkNode(ITE,
653
                   matchesEmpty,
654
                   res1,
655
32
                   nm->mkNode(AND, {splitX, firstMatch, k2Match, res2})),
656
32
        k.eqNode(x)));
657
16
    retNode = k;
658
  }
659
1641
  else if (t.getKind() == STRING_REPLACE_RE_ALL)
660
  {
661
8
    Node x = t[0];
662
8
    Node y = t[1];
663
8
    Node z = t[2];
664
8
    Node k = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "k");
665
666
    Node numOcc = sc->mkTypedSkolemCached(
667
8
        nm->integerType(), x, y, SkolemCache::SK_NUM_OCCUR, "numOcc");
668
8
    std::vector<TypeNode> argTypes;
669
4
    argTypes.push_back(nm->integerType());
670
    Node us =
671
8
        sm->mkDummySkolem("Us", nm->mkFunctionType(argTypes, t.getType()));
672
8
    TypeNode ufType = nm->mkFunctionType(argTypes, nm->integerType());
673
    Node uf = sc->mkTypedSkolemCached(
674
8
        ufType, x, y, SkolemCache::SK_OCCUR_INDEX, "Uf");
675
    Node ul =
676
8
        sc->mkTypedSkolemCached(ufType, x, y, SkolemCache::SK_OCCUR_LEN, "Ul");
677
678
8
    Node emp = Word::mkEmptyWord(t.getType());
679
680
8
    std::vector<Node> emptyVec;
681
    Node sigmaStar =
682
8
        nm->mkNode(REGEXP_STAR, nm->mkNode(REGEXP_SIGMA, emptyVec));
683
8
    Node yp = nm->mkNode(REGEXP_DIFF, y, nm->mkNode(STRING_TO_REGEXP, emp));
684
8
    Node re = nm->mkNode(REGEXP_CONCAT, sigmaStar, yp, sigmaStar);
685
    // in_re(x, _* ++ y' ++ _*)
686
8
    Node hasMatch = nm->mkNode(STRING_IN_REGEXP, x, re);
687
688
8
    Node ufno = nm->mkNode(APPLY_UF, uf, numOcc);
689
8
    Node usno = nm->mkNode(APPLY_UF, us, numOcc);
690
8
    Node rem = nm->mkNode(STRING_SUBSTR, x, ufno, nm->mkNode(STRING_LENGTH, x));
691
692
8
    std::vector<Node> lemmas;
693
    // numOcc > 0
694
4
    lemmas.push_back(nm->mkNode(GT, numOcc, zero));
695
    // k = Us(0)
696
4
    lemmas.push_back(k.eqNode(nm->mkNode(APPLY_UF, us, zero)));
697
    // Us(numOcc) = substr(x, Uf(numOcc))
698
4
    lemmas.push_back(usno.eqNode(rem));
699
    // Uf(0) = 0
700
4
    lemmas.push_back(nm->mkNode(APPLY_UF, uf, zero).eqNode(zero));
701
    // not(in_re(substr(x, Uf(numOcc)), re.++(_*, y', _*)))
702
4
    lemmas.push_back(nm->mkNode(STRING_IN_REGEXP, rem, re).negate());
703
704
8
    Node i = SkolemCache::mkIndexVar(t);
705
8
    Node bvli = nm->mkNode(BOUND_VAR_LIST, i);
706
    Node bound =
707
8
        nm->mkNode(AND, nm->mkNode(GEQ, i, zero), nm->mkNode(LT, i, numOcc));
708
8
    Node ip1 = nm->mkNode(PLUS, i, one);
709
8
    Node ufi = nm->mkNode(APPLY_UF, uf, i);
710
8
    Node uli = nm->mkNode(APPLY_UF, ul, i);
711
8
    Node ufip1 = nm->mkNode(APPLY_UF, uf, ip1);
712
8
    Node ii = nm->mkNode(MINUS, ufip1, uli);
713
8
    Node match = nm->mkNode(STRING_SUBSTR, x, ii, uli);
714
    Node pfxMatch =
715
8
        nm->mkNode(STRING_SUBSTR, x, ufi, nm->mkNode(MINUS, ii, ufi));
716
    Node nonMatch =
717
        nm->mkNode(STRING_SUBSTR,
718
                   x,
719
                   ufi,
720
8
                   nm->mkNode(MINUS, nm->mkNode(MINUS, ufip1, one), ufi));
721
722
8
    std::vector<Node> flem;
723
    // Ul(i) > 0
724
4
    flem.push_back(nm->mkNode(GT, uli, zero));
725
    // Uf(i + 1) >= Uf(i) + Ul(i)
726
4
    flem.push_back(nm->mkNode(GEQ, ufip1, nm->mkNode(PLUS, ufi, uli)));
727
    // in_re(substr(x, ii, Ul(i)), y')
728
4
    flem.push_back(nm->mkNode(STRING_IN_REGEXP, match, yp));
729
    // ~in_re(substr(x, Uf(i), Uf(i + 1) - 1 - Uf(i)), re.++(_*, y', _*))
730
4
    flem.push_back(nm->mkNode(STRING_IN_REGEXP, nonMatch, re).negate());
731
    // Us(i) = substr(x, Uf(i), ii - Uf(i)) ++ z ++ Us(i + 1)
732
4
    flem.push_back(
733
8
        nm->mkNode(APPLY_UF, us, i)
734
8
            .eqNode(nm->mkNode(
735
8
                STRING_CONCAT, pfxMatch, z, nm->mkNode(APPLY_UF, us, ip1))));
736
737
8
    Node body = nm->mkNode(OR, bound.negate(), nm->mkNode(AND, flem));
738
8
    Node forall = mkForallInternal(bvli, body);
739
4
    lemmas.push_back(forall);
740
741
    // IF in_re(x, re.++(_*, y', _*))
742
    // THEN:
743
    //   numOcc > 0 ^
744
    //   k = Us(0) ^ Us(numOcc) = substr(x, Uf(numOcc)) ^
745
    //   Uf(0) = 0 ^ not(in_re(substr(x, Uf(numOcc)), re.++(_*, y', _*)))
746
    //   forall i. 0 <= i < nummOcc =>
747
    //     Ul(i) > 0 ^
748
    //     Uf(i + 1) >= Uf(i) + Ul(i) ^
749
    //     in_re(substr(x, ii, Ul(i)), y') ^
750
    //     ~in_re(substr(x, Uf(i), Uf(i + 1) - 1 - Uf(i)), re.++(_*, y', _*)) ^
751
    //     Us(i) = substr(x, Uf(i), ii - Uf(i)) ++ z ++ Us(i + 1)
752
    //     where ii = Uf(i + 1) - Ul(i)
753
    // ELSE: k = x
754
    // where y' = re.diff(y, "")
755
    //
756
    // Conceptually, y' is the regex y but excluding the empty string (because
757
    // we do not want to match empty strings), numOcc is the number of shortest
758
    // matches of y' in x, Uf(i) is the end position of the i-th match, Ul(i)
759
    // is the length of the i^th match, and Us(i) is the result of processing
760
    // the remainder after processing the i^th occurrence of y in x.
761
4
    asserts.push_back(
762
8
        nm->mkNode(ITE, hasMatch, nm->mkNode(AND, lemmas), k.eqNode(x)));
763
4
    retNode = k;
764
  }
765
1637
  else if (t.getKind() == STRING_TOLOWER || t.getKind() == STRING_TOUPPER)
766
  {
767
12
    Node x = t[0];
768
12
    Node r = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "r");
769
770
12
    Node lenx = nm->mkNode(STRING_LENGTH, x);
771
12
    Node lenr = nm->mkNode(STRING_LENGTH, r);
772
12
    Node eqLenA = lenx.eqNode(lenr);
773
774
12
    Node i = SkolemCache::mkIndexVar(t);
775
12
    Node bvi = nm->mkNode(BOUND_VAR_LIST, i);
776
777
12
    Node ci = nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, x, i, one));
778
12
    Node ri = nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, r, i, one));
779
780
12
    Node lb = nm->mkConst(Rational(t.getKind() == STRING_TOUPPER ? 97 : 65));
781
12
    Node ub = nm->mkConst(Rational(t.getKind() == STRING_TOUPPER ? 122 : 90));
782
    Node offset =
783
12
        nm->mkConst(Rational(t.getKind() == STRING_TOUPPER ? -32 : 32));
784
785
    Node res = nm->mkNode(
786
        ITE,
787
12
        nm->mkNode(AND, nm->mkNode(LEQ, lb, ci), nm->mkNode(LEQ, ci, ub)),
788
12
        nm->mkNode(PLUS, ci, offset),
789
36
        ci);
790
791
    Node bound =
792
12
        nm->mkNode(AND, nm->mkNode(LEQ, zero, i), nm->mkNode(LT, i, lenr));
793
12
    Node body = nm->mkNode(OR, bound.negate(), ri.eqNode(res));
794
12
    Node rangeA = mkForallInternal(bvi, body);
795
796
    // upper 65 ... 90
797
    // lower 97 ... 122
798
    // assert:
799
    //   len(r) = len(x) ^
800
    //   forall i. 0 <= i < len(r) =>
801
    //     str.code( str.substr(r,i,1) ) = ite( 97 <= ci <= 122, ci-32, ci)
802
    // where ci = str.code( str.substr(x,i,1) )
803
12
    Node assert = nm->mkNode(AND, eqLenA, rangeA);
804
6
    asserts.push_back(assert);
805
806
    // Thus, toLower( x ) = r
807
6
    retNode = r;
808
  }
809
1631
  else if (t.getKind() == STRING_REV)
810
  {
811
36
    Node x = t[0];
812
36
    Node r = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "r");
813
814
36
    Node lenx = nm->mkNode(STRING_LENGTH, x);
815
36
    Node lenr = nm->mkNode(STRING_LENGTH, r);
816
36
    Node eqLenA = lenx.eqNode(lenr);
817
818
36
    Node i = SkolemCache::mkIndexVar(t);
819
36
    Node bvi = nm->mkNode(BOUND_VAR_LIST, i);
820
821
    Node revi = nm->mkNode(
822
36
        MINUS, nm->mkNode(STRING_LENGTH, x), nm->mkNode(PLUS, i, one));
823
36
    Node ssr = nm->mkNode(STRING_SUBSTR, r, i, one);
824
36
    Node ssx = nm->mkNode(STRING_SUBSTR, x, revi, one);
825
826
    Node bound =
827
36
        nm->mkNode(AND, nm->mkNode(LEQ, zero, i), nm->mkNode(LT, i, lenr));
828
36
    Node body = nm->mkNode(OR, bound.negate(), ssr.eqNode(ssx));
829
36
    Node rangeA = mkForallInternal(bvi, body);
830
    // assert:
831
    //   len(r) = len(x) ^
832
    //   forall i. 0 <= i < len(r) =>
833
    //     substr(r,i,1) = substr(x,len(x)-(i+1),1)
834
36
    Node assert = nm->mkNode(AND, eqLenA, rangeA);
835
18
    asserts.push_back(assert);
836
837
    // Thus, (str.rev x) = r
838
18
    retNode = r;
839
  }
840
1613
  else if (t.getKind() == kind::STRING_STRCTN)
841
  {
842
258
    Node x = t[0];
843
258
    Node s = t[1];
844
    //negative contains reduces to existential
845
258
    Node lenx = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, x);
846
258
    Node lens = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, s);
847
258
    Node b1 = SkolemCache::mkIndexVar(t);
848
258
    Node b1v = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, b1);
849
    Node body = NodeManager::currentNM()->mkNode(
850
        kind::AND,
851
258
        NodeManager::currentNM()->mkNode(kind::LEQ, zero, b1),
852
387
        NodeManager::currentNM()->mkNode(
853
            kind::LEQ,
854
            b1,
855
258
            NodeManager::currentNM()->mkNode(kind::MINUS, lenx, lens)),
856
516
        NodeManager::currentNM()->mkNode(
857
            kind::EQUAL,
858
258
            NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR, x, b1, lens),
859
903
            s));
860
129
    retNode = NodeManager::currentNM()->mkNode( kind::EXISTS, b1v, body );
861
  }
862
1484
  else if (t.getKind() == kind::STRING_LEQ)
863
  {
864
    Node ltp = sc->mkTypedSkolemCached(
865
64
        nm->booleanType(), t, SkolemCache::SK_PURIFY, "ltp");
866
64
    Node k = SkolemCache::mkIndexVar(t);
867
868
64
    std::vector<Node> conj;
869
32
    conj.push_back(nm->mkNode(GEQ, k, zero));
870
64
    Node substr[2];
871
64
    Node code[2];
872
96
    for (unsigned r = 0; r < 2; r++)
873
    {
874
128
      Node ta = t[r];
875
128
      Node tb = t[1 - r];
876
64
      substr[r] = nm->mkNode(STRING_SUBSTR, ta, zero, k);
877
64
      code[r] =
878
128
          nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, ta, k, one));
879
64
      conj.push_back(nm->mkNode(LEQ, k, nm->mkNode(STRING_LENGTH, ta)));
880
    }
881
32
    conj.push_back(substr[0].eqNode(substr[1]));
882
64
    std::vector<Node> ite_ch;
883
32
    ite_ch.push_back(ltp);
884
96
    for (unsigned r = 0; r < 2; r++)
885
    {
886
64
      ite_ch.push_back(nm->mkNode(LT, code[r], code[1 - r]));
887
    }
888
32
    conj.push_back(nm->mkNode(ITE, ite_ch));
889
890
    Node conjn = nm->mkNode(
891
64
        EXISTS, nm->mkNode(BOUND_VAR_LIST, k), nm->mkNode(AND, conj));
892
    // Intuitively, the reduction says either x and y are equal, or they have
893
    // some (maximal) common prefix after which their characters at position k
894
    // are distinct, and the comparison of their code matches the return value
895
    // of the overall term.
896
    // Notice the below reduction relies on the semantics of str.code being -1
897
    // for the empty string. In particular, say x = "a" and y = "ab", then the
898
    // reduction below is satisfied for k = 1, since
899
    //   str.code(substr( "a", 1, 1 )) = str.code( "" ) = -1 <
900
    //   str.code(substr( "ab", 1, 1 )) = str.code( "b" ) = 66
901
902
    // assert:
903
    //  IF x=y
904
    //  THEN: ltp
905
    //  ELSE: exists k.
906
    //        k >= 0 AND k <= len( x ) AND k <= len( y ) AND
907
    //        substr( x, 0, k ) = substr( y, 0, k ) AND
908
    //        IF    ltp
909
    //        THEN: str.code(substr( x, k, 1 )) < str.code(substr( y, k, 1 ))
910
    //        ELSE: str.code(substr( x, k, 1 )) > str.code(substr( y, k, 1 ))
911
64
    Node assert = nm->mkNode(ITE, t[0].eqNode(t[1]), ltp, conjn);
912
32
    asserts.push_back(assert);
913
914
    // Thus, str.<=( x, y ) = ltp
915
32
    retNode = ltp;
916
  }
917
6620
  return retNode;
918
}
919
920
2963
Node StringsPreprocess::simplify(Node t, std::vector<Node>& asserts)
921
{
922
2963
  size_t prev_asserts = asserts.size();
923
  // call the static reduce routine
924
2963
  Node retNode = reduce(t, asserts, d_sc);
925
2963
  if( t!=retNode ){
926
1511
    Trace("strings-preprocess") << "StringsPreprocess::simplify: " << t << " -> " << retNode << std::endl;
927
1511
    if (!asserts.empty())
928
    {
929
2814
      Trace("strings-preprocess")
930
2814
          << " ... new nodes (" << (asserts.size() - prev_asserts)
931
1407
          << "):" << std::endl;
932
2795
      for (size_t i = prev_asserts; i < asserts.size(); ++i)
933
      {
934
1388
        Trace("strings-preprocess") << "   " << asserts[i] << std::endl;
935
      }
936
    }
937
1511
    if (d_statReductions != nullptr)
938
    {
939
1425
      (*d_statReductions) << t.getKind();
940
    }
941
  }
942
  else
943
  {
944
2904
    Trace("strings-preprocess-debug")
945
1452
        << "Return " << retNode << " unchanged" << std::endl;
946
  }
947
2963
  return retNode;
948
}
949
950
2582
Node StringsPreprocess::simplifyRec(Node t, std::vector<Node>& asserts)
951
{
952
2582
  std::map<Node, Node>::iterator it = d_visited.find(t);
953
2582
  if (it != d_visited.end())
954
  {
955
1036
    return it->second;
956
  }else{
957
3092
    Node retNode = t;
958
1546
    if( t.getNumChildren()==0 ){
959
386
      retNode = simplify(t, asserts);
960
    }
961
1160
    else if (!t.isClosure())
962
    {
963
1152
      bool changed = false;
964
2304
      std::vector< Node > cc;
965
1152
      if( t.getMetaKind() == kind::metakind::PARAMETERIZED ){
966
12
        cc.push_back( t.getOperator() );
967
      }
968
3587
      for(unsigned i=0; i<t.getNumChildren(); i++) {
969
4870
        Node s = simplifyRec(t[i], asserts);
970
2435
        cc.push_back( s );
971
2435
        if( s!=t[i] ) {
972
211
          changed = true;
973
        }
974
      }
975
2304
      Node tmp = t;
976
1152
      if( changed ){
977
184
        tmp = NodeManager::currentNM()->mkNode( t.getKind(), cc );
978
      }
979
1152
      retNode = simplify(tmp, asserts);
980
    }
981
1546
    d_visited[t] = retNode;
982
1546
    return retNode;
983
  }
984
}
985
986
91
Node StringsPreprocess::processAssertion(Node n, std::vector<Node>& asserts)
987
{
988
182
  std::vector<Node> asserts_curr;
989
91
  Node ret = simplifyRec(n, asserts_curr);
990
203
  while (!asserts_curr.empty())
991
  {
992
112
    Node curr = asserts_curr.back();
993
56
    asserts_curr.pop_back();
994
112
    std::vector<Node> asserts_tmp;
995
56
    curr = simplifyRec(curr, asserts_tmp);
996
56
    asserts_curr.insert(
997
112
        asserts_curr.end(), asserts_tmp.begin(), asserts_tmp.end());
998
56
    asserts.push_back(curr);
999
  }
1000
182
  return ret;
1001
}
1002
1003
92
Node StringsPreprocess::mkForallInternal(Node bvl, Node body)
1004
{
1005
92
  NodeManager* nm = NodeManager::currentNM();
1006
  QInternalVarAttribute qiva;
1007
184
  Node qvar;
1008
92
  if (bvl.hasAttribute(qiva))
1009
  {
1010
14
    qvar = bvl.getAttribute(qiva);
1011
  }
1012
  else
1013
  {
1014
78
    SkolemManager* sm = nm->getSkolemManager();
1015
78
    qvar = sm->mkDummySkolem("qinternal", nm->booleanType());
1016
    // this dummy variable marks that the quantified formula is internal
1017
78
    qvar.setAttribute(InternalQuantAttribute(), true);
1018
    // remember the dummy variable
1019
78
    bvl.setAttribute(qiva, qvar);
1020
  }
1021
  // make the internal attribute, and put it in a singleton list
1022
184
  Node ip = nm->mkNode(INST_ATTRIBUTE, qvar);
1023
184
  Node ipl = nm->mkNode(INST_PATTERN_LIST, ip);
1024
  // make the overall formula
1025
184
  return nm->mkNode(FORALL, bvl, body, ipl);
1026
}
1027
1028
}  // namespace strings
1029
}  // namespace theory
1030
28194
}  // namespace cvc5