GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/strings/term_registry.cpp Lines: 285 310 91.9 %
Date: 2021-09-15 Branches: 688 1422 48.4 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Andres Noetzli, Morgan Deters
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 term registry for the theory of strings.
14
 */
15
16
#include "theory/strings/term_registry.h"
17
18
#include "expr/attribute.h"
19
#include "options/smt_options.h"
20
#include "options/strings_options.h"
21
#include "smt/logic_exception.h"
22
#include "theory/rewriter.h"
23
#include "theory/strings/inference_manager.h"
24
#include "theory/strings/theory_strings_utils.h"
25
#include "theory/strings/word.h"
26
#include "util/rational.h"
27
#include "util/string.h"
28
29
using namespace std;
30
using namespace cvc5::context;
31
using namespace cvc5::kind;
32
33
namespace cvc5 {
34
namespace theory {
35
namespace strings {
36
37
struct StringsProxyVarAttributeId
38
{
39
};
40
typedef expr::Attribute<StringsProxyVarAttributeId, bool>
41
    StringsProxyVarAttribute;
42
43
9942
TermRegistry::TermRegistry(Env& env,
44
                           SolverState& s,
45
                           SequencesStatistics& statistics,
46
9942
                           ProofNodeManager* pnm)
47
    : EnvObj(env),
48
      d_state(s),
49
      d_im(nullptr),
50
      d_statistics(statistics),
51
      d_hasStrCode(false),
52
      d_functionsTerms(context()),
53
9942
      d_inputVars(userContext()),
54
      d_preregisteredTerms(context()),
55
9942
      d_registeredTerms(userContext()),
56
9942
      d_registeredTypes(userContext()),
57
9942
      d_proxyVar(userContext()),
58
9942
      d_lengthLemmaTermsCache(userContext()),
59
      d_epg(
60
          pnm ? new EagerProofGenerator(
61
2486
              pnm, userContext(), "strings::TermRegistry::EagerProofGenerator")
62
62138
              : nullptr)
63
{
64
9942
  NodeManager* nm = NodeManager::currentNM();
65
9942
  d_zero = nm->mkConst(Rational(0));
66
9942
  d_one = nm->mkConst(Rational(1));
67
9942
  d_negOne = NodeManager::currentNM()->mkConst(Rational(-1));
68
9942
  d_cardSize = utils::getAlphabetCardinality();
69
9942
}
70
71
9939
TermRegistry::~TermRegistry() {}
72
73
9942
void TermRegistry::finishInit(InferenceManager* im) { d_im = im; }
74
75
30558
Node TermRegistry::eagerReduce(Node t, SkolemCache* sc)
76
{
77
30558
  NodeManager* nm = NodeManager::currentNM();
78
30558
  Node lemma;
79
30558
  Kind tk = t.getKind();
80
30558
  if (tk == STRING_TO_CODE)
81
  {
82
    // ite( str.len(s)==1, 0 <= str.code(s) < |A|, str.code(s)=-1 )
83
3322
    Node code_len = utils::mkNLength(t[0]).eqNode(nm->mkConst(Rational(1)));
84
3322
    Node code_eq_neg1 = t.eqNode(nm->mkConst(Rational(-1)));
85
    Node code_range = nm->mkNode(
86
        AND,
87
3322
        nm->mkNode(GEQ, t, nm->mkConst(Rational(0))),
88
3322
        nm->mkNode(
89
9966
            LT, t, nm->mkConst(Rational(utils::getAlphabetCardinality()))));
90
1661
    lemma = nm->mkNode(ITE, code_len, code_range, code_eq_neg1);
91
  }
92
28897
  else if (tk == STRING_INDEXOF || tk == STRING_INDEXOF_RE)
93
  {
94
    // (and
95
    //   (or (= (f x y n) (- 1)) (>= (f x y n) n))
96
    //   (<= (f x y n) (str.len x)))
97
    //
98
    // where f in { str.indexof, str.indexof_re }
99
628
    Node l = nm->mkNode(STRING_LENGTH, t[0]);
100
942
    lemma = nm->mkNode(
101
        AND,
102
1256
        nm->mkNode(
103
1256
            OR, nm->mkConst(Rational(-1)).eqNode(t), nm->mkNode(GEQ, t, t[2])),
104
942
        nm->mkNode(LEQ, t, l));
105
  }
106
28583
  else if (tk == STRING_STOI)
107
  {
108
    // (>= (str.to_int x) (- 1))
109
91
    lemma = nm->mkNode(GEQ, t, nm->mkConst(Rational(-1)));
110
  }
111
28492
  else if (tk == STRING_CONTAINS)
112
  {
113
    // ite( (str.contains s r), (= s (str.++ sk1 r sk2)), (not (= s r)))
114
    Node sk1 =
115
8268
        sc->mkSkolemCached(t[0], t[1], SkolemCache::SK_FIRST_CTN_PRE, "sc1");
116
    Node sk2 =
117
8268
        sc->mkSkolemCached(t[0], t[1], SkolemCache::SK_FIRST_CTN_POST, "sc2");
118
4134
    lemma = t[0].eqNode(utils::mkNConcat(sk1, t[1], sk2));
119
4134
    lemma = nm->mkNode(ITE, t, lemma, t[0].eqNode(t[1]).notNode());
120
  }
121
30558
  return lemma;
122
}
123
124
11263
Node TermRegistry::lengthPositive(Node t)
125
{
126
11263
  NodeManager* nm = NodeManager::currentNM();
127
22526
  Node zero = nm->mkConst(Rational(0));
128
22526
  Node emp = Word::mkEmptyWord(t.getType());
129
22526
  Node tlen = nm->mkNode(STRING_LENGTH, t);
130
22526
  Node tlenEqZero = tlen.eqNode(zero);
131
22526
  Node tEqEmp = t.eqNode(emp);
132
22526
  Node caseEmpty = nm->mkNode(AND, tlenEqZero, tEqEmp);
133
22526
  Node caseNEmpty = nm->mkNode(GT, tlen, zero);
134
  // (or (and (= (str.len t) 0) (= t "")) (> (str.len t) 0))
135
22526
  return nm->mkNode(OR, caseEmpty, caseNEmpty);
136
}
137
138
158451
void TermRegistry::preRegisterTerm(TNode n)
139
{
140
158451
  if (d_preregisteredTerms.find(n) != d_preregisteredTerms.end())
141
  {
142
65046
    return;
143
  }
144
157519
  eq::EqualityEngine* ee = d_state.getEqualityEngine();
145
157519
  d_preregisteredTerms.insert(n);
146
315038
  Trace("strings-preregister")
147
157519
      << "TheoryString::preregister : " << n << std::endl;
148
  // check for logic exceptions
149
157519
  Kind k = n.getKind();
150
157519
  if (!options::stringExp())
151
  {
152
4103
    if (k == STRING_INDEXOF || k == STRING_INDEXOF_RE || k == STRING_ITOS
153
4103
        || k == STRING_STOI || k == STRING_REPLACE || k == STRING_SUBSTR
154
4103
        || k == STRING_REPLACE_ALL || k == SEQ_NTH || k == STRING_REPLACE_RE
155
4103
        || k == STRING_REPLACE_RE_ALL || k == STRING_CONTAINS || k == STRING_LEQ
156
4103
        || k == STRING_TOLOWER || k == STRING_TOUPPER || k == STRING_REV
157
4103
        || k == STRING_UPDATE)
158
    {
159
      std::stringstream ss;
160
      ss << "Term of kind " << k
161
         << " not supported in default mode, try --strings-exp";
162
      throw LogicException(ss.str());
163
    }
164
  }
165
157519
  if (k == EQUAL)
166
  {
167
60017
    if (n[0].getType().isRegExp())
168
    {
169
      std::stringstream ss;
170
      ss << "Equality between regular expressions is not supported";
171
      throw LogicException(ss.str());
172
    }
173
60017
    ee->addTriggerPredicate(n);
174
60017
    return;
175
  }
176
97502
  else if (k == STRING_IN_REGEXP)
177
  {
178
3165
    d_im->requirePhase(n, true);
179
3165
    ee->addTriggerPredicate(n);
180
3165
    ee->addTerm(n[0]);
181
3165
    ee->addTerm(n[1]);
182
3165
    return;
183
  }
184
94337
  else if (k == STRING_TO_CODE)
185
  {
186
2953
    d_hasStrCode = true;
187
  }
188
91384
  else if (k == REGEXP_RANGE)
189
  {
190
255
    for (const Node& nc : n)
191
    {
192
170
      if (!nc.isConst())
193
      {
194
        throw LogicException(
195
            "expecting a constant string term in regexp range");
196
      }
197
170
      if (nc.getConst<String>().size() != 1)
198
      {
199
        throw LogicException(
200
            "expecting a single constant string term in regexp range");
201
      }
202
    }
203
  }
204
94337
  registerTerm(n, 0);
205
188674
  TypeNode tn = n.getType();
206
94337
  if (tn.isRegExp() && n.isVar())
207
  {
208
    std::stringstream ss;
209
    ss << "Regular expression variables are not supported.";
210
    throw LogicException(ss.str());
211
  }
212
94337
  if (tn.isString())  // string-only
213
  {
214
    // all characters of constants should fall in the alphabet
215
47818
    if (n.isConst())
216
    {
217
8158
      std::vector<unsigned> vec = n.getConst<String>().getVec();
218
17746
      for (unsigned u : vec)
219
      {
220
13667
        if (u >= d_cardSize)
221
        {
222
          std::stringstream ss;
223
          ss << "Characters in string \"" << n
224
             << "\" are outside of the given alphabet.";
225
          throw LogicException(ss.str());
226
        }
227
      }
228
    }
229
47818
    ee->addTerm(n);
230
  }
231
46519
  else if (tn.isBoolean())
232
  {
233
    // All kinds that we do congruence over that may return a Boolean go here
234
1883
    if (k == STRING_CONTAINS || k == STRING_LEQ || k == SEQ_NTH)
235
    {
236
      // Get triggered for both equal and dis-equal
237
1877
      ee->addTriggerPredicate(n);
238
    }
239
  }
240
  else
241
  {
242
    // Function applications/predicates
243
44636
    ee->addTerm(n);
244
  }
245
  // Set d_functionsTerms stores all function applications that are
246
  // relevant to theory combination. Notice that this is a subset of
247
  // the applications whose kinds are function kinds in the equality
248
  // engine. This means it does not include applications of operators
249
  // like re.++, which is not a function kind in the equality engine.
250
  // Concatenation terms do not need to be considered here because
251
  // their arguments have string type and do not introduce any shared
252
  // terms.
253
246808
  if (n.hasOperator() && ee->isFunctionKind(k)
254
148869
      && kindToTheoryId(k) == THEORY_STRINGS && k != STRING_CONCAT)
255
  {
256
43606
    d_functionsTerms.push_back(n);
257
  }
258
94337
  if (options::stringFMF())
259
  {
260
15158
    if (tn.isStringLike())
261
    {
262
      // Our decision strategy will minimize the length of this term if it is a
263
      // variable but not an internally generated Skolem, or a term that does
264
      // not belong to this theory.
265
11096
      if (n.isVar() ? !d_skCache.isSkolem(n)
266
3230
                    : kindToTheoryId(k) != THEORY_STRINGS)
267
      {
268
518
        d_inputVars.insert(n);
269
518
        Trace("strings-preregister") << "input variable: " << n << std::endl;
270
      }
271
    }
272
  }
273
}
274
275
206767
void TermRegistry::registerTerm(Node n, int effort)
276
{
277
413534
  Trace("strings-register") << "TheoryStrings::registerTerm() " << n
278
206767
                            << ", effort = " << effort << std::endl;
279
206767
  if (d_registeredTerms.find(n) != d_registeredTerms.end())
280
  {
281
149560
    Trace("strings-register") << "...already registered" << std::endl;
282
299120
    return;
283
  }
284
57207
  bool do_register = true;
285
114414
  TypeNode tn = n.getType();
286
57207
  if (!tn.isStringLike())
287
  {
288
27437
    if (options::stringEagerLen())
289
    {
290
27437
      do_register = effort == 0;
291
    }
292
    else
293
    {
294
      do_register = effort > 0 || n.getKind() != STRING_CONCAT;
295
    }
296
  }
297
57207
  if (!do_register)
298
  {
299
    Trace("strings-register") << "...do not register" << std::endl;
300
    return;
301
  }
302
57207
  Trace("strings-register") << "...register" << std::endl;
303
57207
  d_registeredTerms.insert(n);
304
  // ensure the type is registered
305
57207
  registerType(tn);
306
114414
  TrustNode regTermLem;
307
57207
  if (tn.isStringLike())
308
  {
309
    // register length information:
310
    //  for variables, split on empty vs positive length
311
    //  for concat/const/replace, introduce proxy var and state length relation
312
29770
    regTermLem = getRegisterTermLemma(n);
313
  }
314
27437
  else if (n.getKind() != STRING_CONTAINS)
315
  {
316
    // we don't send out eager reduction lemma for str.contains currently
317
52742
    Node eagerRedLemma = eagerReduce(n, &d_skCache);
318
26371
    if (!eagerRedLemma.isNull())
319
    {
320
      // if there was an eager reduction, we make the trust node for it
321
2013
      if (d_epg != nullptr)
322
      {
323
624
        regTermLem = d_epg->mkTrustNode(
324
312
            eagerRedLemma, PfRule::STRING_EAGER_REDUCTION, {}, {n});
325
      }
326
      else
327
      {
328
1701
        regTermLem = TrustNode::mkTrustLemma(eagerRedLemma, nullptr);
329
      }
330
    }
331
  }
332
57207
  if (!regTermLem.isNull())
333
  {
334
22894
    Trace("strings-lemma") << "Strings::Lemma REG-TERM : " << regTermLem
335
11447
                           << std::endl;
336
22894
    Trace("strings-assert")
337
11447
        << "(assert " << regTermLem.getNode() << ")" << std::endl;
338
11447
    d_im->trustedLemma(regTermLem, InferenceId::STRINGS_REGISTER_TERM);
339
  }
340
}
341
342
57207
void TermRegistry::registerType(TypeNode tn)
343
{
344
57207
  if (d_registeredTypes.find(tn) != d_registeredTypes.end())
345
  {
346
54855
    return;
347
  }
348
2352
  d_registeredTypes.insert(tn);
349
2352
  if (tn.isStringLike())
350
  {
351
    // preregister the empty word for the type
352
1864
    Node emp = Word::mkEmptyWord(tn);
353
932
    if (!d_state.hasTerm(emp))
354
    {
355
932
      preRegisterTerm(emp);
356
    }
357
  }
358
}
359
360
29770
TrustNode TermRegistry::getRegisterTermLemma(Node n)
361
{
362
29770
  Assert(n.getType().isStringLike());
363
29770
  NodeManager* nm = NodeManager::currentNM();
364
  // register length information:
365
  //  for variables, split on empty vs positive length
366
  //  for concat/const/replace, introduce proxy var and state length relation
367
59540
  Node lsum;
368
29770
  if (n.getKind() != STRING_CONCAT && !n.isConst())
369
  {
370
20648
    Node lsumb = nm->mkNode(STRING_LENGTH, n);
371
20492
    lsum = Rewriter::rewrite(lsumb);
372
    // can register length term if it does not rewrite
373
20492
    if (lsum == lsumb)
374
    {
375
20336
      registerTermAtomic(n, LENGTH_SPLIT);
376
20336
      return TrustNode::null();
377
    }
378
  }
379
18868
  Node sk = d_skCache.mkSkolemCached(n, SkolemCache::SK_PURIFY, "lsym");
380
  StringsProxyVarAttribute spva;
381
9434
  sk.setAttribute(spva, true);
382
18868
  Node eq = Rewriter::rewrite(sk.eqNode(n));
383
9434
  d_proxyVar[n] = sk;
384
  // If we are introducing a proxy for a constant or concat term, we do not
385
  // need to send lemmas about its length, since its length is already
386
  // implied.
387
9434
  if (n.isConst() || n.getKind() == STRING_CONCAT)
388
  {
389
    // do not send length lemma for sk.
390
9278
    registerTermAtomic(sk, LENGTH_IGNORE);
391
  }
392
18868
  Node skl = nm->mkNode(STRING_LENGTH, sk);
393
9434
  if (n.getKind() == STRING_CONCAT)
394
  {
395
11720
    std::vector<Node> nodeVec;
396
19952
    for (const Node& nc : n)
397
    {
398
14092
      if (nc.getAttribute(StringsProxyVarAttribute()))
399
      {
400
60
        Assert(d_proxyVarToLength.find(nc) != d_proxyVarToLength.end());
401
60
        nodeVec.push_back(d_proxyVarToLength[nc]);
402
      }
403
      else
404
      {
405
28064
        Node lni = nm->mkNode(STRING_LENGTH, nc);
406
14032
        nodeVec.push_back(lni);
407
      }
408
    }
409
5860
    lsum = nm->mkNode(PLUS, nodeVec);
410
5860
    lsum = Rewriter::rewrite(lsum);
411
  }
412
3574
  else if (n.isConst())
413
  {
414
3418
    lsum = nm->mkConst(Rational(Word::getLength(n)));
415
  }
416
9434
  Assert(!lsum.isNull());
417
9434
  d_proxyVarToLength[sk] = lsum;
418
18868
  Node ceq = Rewriter::rewrite(skl.eqNode(lsum));
419
420
18868
  Node ret = nm->mkNode(AND, eq, ceq);
421
422
  // it is a simple rewrite to justify this
423
9434
  if (d_epg != nullptr)
424
  {
425
1280
    return d_epg->mkTrustNode(ret, PfRule::MACRO_SR_PRED_INTRO, {}, {ret});
426
  }
427
8154
  return TrustNode::mkTrustLemma(ret, nullptr);
428
}
429
430
31037
void TermRegistry::registerTermAtomic(Node n, LengthStatus s)
431
{
432
31037
  if (d_lengthLemmaTermsCache.find(n) != d_lengthLemmaTermsCache.end())
433
  {
434
30656
    return;
435
  }
436
20340
  d_lengthLemmaTermsCache.insert(n);
437
438
20340
  if (s == LENGTH_IGNORE)
439
  {
440
    // ignore it
441
9262
    return;
442
  }
443
22156
  std::map<Node, bool> reqPhase;
444
22156
  TrustNode lenLem = getRegisterTermAtomicLemma(n, s, reqPhase);
445
11078
  if (!lenLem.isNull())
446
  {
447
22148
    Trace("strings-lemma") << "Strings::Lemma REGISTER-TERM-ATOMIC : " << lenLem
448
11074
                           << std::endl;
449
22148
    Trace("strings-assert")
450
11074
        << "(assert " << lenLem.getNode() << ")" << std::endl;
451
11074
    d_im->trustedLemma(lenLem, InferenceId::STRINGS_REGISTER_TERM_ATOMIC);
452
  }
453
33194
  for (const std::pair<const Node, bool>& rp : reqPhase)
454
  {
455
22116
    d_im->requirePhase(rp.first, rp.second);
456
  }
457
}
458
459
35386
SkolemCache* TermRegistry::getSkolemCache() { return &d_skCache; }
460
461
6957
const context::CDList<TNode>& TermRegistry::getFunctionTerms() const
462
{
463
6957
  return d_functionsTerms;
464
}
465
466
78
const context::CDHashSet<Node>& TermRegistry::getInputVars() const
467
{
468
78
  return d_inputVars;
469
}
470
471
11894
bool TermRegistry::hasStringCode() const { return d_hasStrCode; }
472
473
11078
TrustNode TermRegistry::getRegisterTermAtomicLemma(
474
    Node n, LengthStatus s, std::map<Node, bool>& reqPhase)
475
{
476
11078
  if (n.isConst())
477
  {
478
    // No need to send length for constant terms. This case may be triggered
479
    // for cases where the skolem cache automatically replaces a skolem by
480
    // a constant.
481
4
    return TrustNode::null();
482
  }
483
11074
  Assert(n.getType().isStringLike());
484
11074
  NodeManager* nm = NodeManager::currentNM();
485
22148
  Node n_len = nm->mkNode(kind::STRING_LENGTH, n);
486
22148
  Node emp = Word::mkEmptyWord(n.getType());
487
11074
  if (s == LENGTH_GEQ_ONE)
488
  {
489
32
    Node neq_empty = n.eqNode(emp).negate();
490
32
    Node len_n_gt_z = nm->mkNode(GT, n_len, d_zero);
491
32
    Node len_geq_one = nm->mkNode(AND, neq_empty, len_n_gt_z);
492
32
    Trace("strings-lemma") << "Strings::Lemma SK-GEQ-ONE : " << len_geq_one
493
16
                           << std::endl;
494
16
    Trace("strings-assert") << "(assert " << len_geq_one << ")" << std::endl;
495
16
    return TrustNode::mkTrustLemma(len_geq_one, nullptr);
496
  }
497
498
11058
  if (s == LENGTH_ONE)
499
  {
500
    Node len_one = n_len.eqNode(d_one);
501
    Trace("strings-lemma") << "Strings::Lemma SK-ONE : " << len_one
502
                           << std::endl;
503
    Trace("strings-assert") << "(assert " << len_one << ")" << std::endl;
504
    return TrustNode::mkTrustLemma(len_one, nullptr);
505
  }
506
11058
  Assert(s == LENGTH_SPLIT);
507
508
  // get the positive length lemma
509
22116
  Node lenLemma = lengthPositive(n);
510
  // split whether the string is empty
511
22116
  Node n_len_eq_z = n_len.eqNode(d_zero);
512
22116
  Node n_len_eq_z_2 = n.eqNode(emp);
513
22116
  Node case_empty = nm->mkNode(AND, n_len_eq_z, n_len_eq_z_2);
514
22116
  Node case_emptyr = Rewriter::rewrite(case_empty);
515
11058
  if (!case_emptyr.isConst())
516
  {
517
    // prefer trying the empty case first
518
    // notice that requirePhase must only be called on rewritten literals that
519
    // occur in the CNF stream.
520
11058
    n_len_eq_z = Rewriter::rewrite(n_len_eq_z);
521
11058
    Assert(!n_len_eq_z.isConst());
522
11058
    reqPhase[n_len_eq_z] = true;
523
11058
    n_len_eq_z_2 = Rewriter::rewrite(n_len_eq_z_2);
524
11058
    Assert(!n_len_eq_z_2.isConst());
525
11058
    reqPhase[n_len_eq_z_2] = true;
526
  }
527
  else
528
  {
529
    // If n = "" ---> true or len( n ) = 0 ----> true, then we expect that
530
    // n ---> "". Since this method is only called on non-constants n, it must
531
    // be that n = "" ^ len( n ) = 0 does not rewrite to true.
532
    Assert(!case_emptyr.getConst<bool>());
533
  }
534
535
11058
  if (d_epg != nullptr)
536
  {
537
1470
    return d_epg->mkTrustNode(lenLemma, PfRule::STRING_LENGTH_POS, {}, {n});
538
  }
539
9588
  return TrustNode::mkTrustLemma(lenLemma, nullptr);
540
}
541
542
411757
Node TermRegistry::getSymbolicDefinition(Node n, std::vector<Node>& exp) const
543
{
544
411757
  if (n.getNumChildren() == 0)
545
  {
546
366772
    Node pn = getProxyVariableFor(n);
547
183386
    if (pn.isNull())
548
    {
549
20057
      return Node::null();
550
    }
551
326658
    Node eq = n.eqNode(pn);
552
163329
    eq = Rewriter::rewrite(eq);
553
163329
    if (std::find(exp.begin(), exp.end(), eq) == exp.end())
554
    {
555
159993
      exp.push_back(eq);
556
    }
557
163329
    return pn;
558
  }
559
456742
  std::vector<Node> children;
560
228371
  if (n.getMetaKind() == metakind::PARAMETERIZED)
561
  {
562
42
    children.push_back(n.getOperator());
563
  }
564
604949
  for (const Node& nc : n)
565
  {
566
404483
    if (n.getType().isRegExp())
567
    {
568
142814
      children.push_back(nc);
569
    }
570
    else
571
    {
572
495433
      Node ns = getSymbolicDefinition(nc, exp);
573
261669
      if (ns.isNull())
574
      {
575
27905
        return Node::null();
576
      }
577
      else
578
      {
579
233764
        children.push_back(ns);
580
      }
581
    }
582
  }
583
200466
  return NodeManager::currentNM()->mkNode(n.getKind(), children);
584
}
585
586
191264
Node TermRegistry::getProxyVariableFor(Node n) const
587
{
588
191264
  NodeNodeMap::const_iterator it = d_proxyVar.find(n);
589
191264
  if (it != d_proxyVar.end())
590
  {
591
170897
    return (*it).second;
592
  }
593
20367
  return Node::null();
594
}
595
596
7552
Node TermRegistry::ensureProxyVariableFor(Node n)
597
{
598
7552
  Node proxy = getProxyVariableFor(n);
599
7552
  if (proxy.isNull())
600
  {
601
294
    registerTerm(n, 0);
602
294
    proxy = getProxyVariableFor(n);
603
  }
604
7552
  Assert(!proxy.isNull());
605
7552
  return proxy;
606
}
607
608
89982
void TermRegistry::removeProxyEqs(Node n, std::vector<Node>& unproc) const
609
{
610
89982
  if (n.getKind() == AND)
611
  {
612
2821
    for (const Node& nc : n)
613
    {
614
2232
      removeProxyEqs(nc, unproc);
615
    }
616
1194
    return;
617
  }
618
89393
  Trace("strings-subs-proxy") << "Input : " << n << std::endl;
619
178770
  Node ns = Rewriter::rewrite(n);
620
89393
  if (ns.getKind() == EQUAL)
621
  {
622
267455
    for (size_t i = 0; i < 2; i++)
623
    {
624
      // determine whether this side has a proxy variable
625
178314
      if (ns[i].getAttribute(StringsProxyVarAttribute()))
626
      {
627
32
        if (getProxyVariableFor(ns[1 - i]) == ns[i])
628
        {
629
32
          Trace("strings-subs-proxy")
630
16
              << "...trivial definition via " << ns[i] << std::endl;
631
          // it is a trivial equality, e.g. between a proxy variable
632
          // and its definition
633
16
          return;
634
        }
635
      }
636
    }
637
  }
638
89377
  if (!n.isConst() || !n.getConst<bool>())
639
  {
640
89367
    Trace("strings-subs-proxy") << "...unprocessed" << std::endl;
641
89367
    unproc.push_back(n);
642
  }
643
}
644
645
}  // namespace strings
646
}  // namespace theory
647
29577
}  // namespace cvc5