GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arrays/theory_arrays_rewriter.cpp Lines: 351 356 98.6 %
Date: 2021-09-10 Branches: 852 1613 52.8 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   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
 * [[ Add one-line brief description here ]]
14
 *
15
 * [[ Add lengthier description here ]]
16
 * \todo document this file
17
 */
18
19
#include "theory/arrays/theory_arrays_rewriter.h"
20
21
#include "expr/array_store_all.h"
22
#include "expr/attribute.h"
23
#include "proof/conv_proof_generator.h"
24
#include "proof/eager_proof_generator.h"
25
#include "theory/arrays/skolem_cache.h"
26
#include "util/cardinality.h"
27
28
namespace cvc5 {
29
namespace theory {
30
namespace arrays {
31
32
namespace attr {
33
  struct ArrayConstantMostFrequentValueTag { };
34
  struct ArrayConstantMostFrequentValueCountTag { };
35
  }  // namespace attr
36
37
typedef expr::Attribute<attr::ArrayConstantMostFrequentValueCountTag, uint64_t> ArrayConstantMostFrequentValueCountAttr;
38
typedef expr::Attribute<attr::ArrayConstantMostFrequentValueTag, Node> ArrayConstantMostFrequentValueAttr;
39
40
1146
Node getMostFrequentValue(TNode store) {
41
1146
  return store.getAttribute(ArrayConstantMostFrequentValueAttr());
42
}
43
1146
uint64_t getMostFrequentValueCount(TNode store) {
44
1146
  return store.getAttribute(ArrayConstantMostFrequentValueCountAttr());
45
}
46
47
573
void setMostFrequentValue(TNode store, TNode value) {
48
573
  return store.setAttribute(ArrayConstantMostFrequentValueAttr(), value);
49
}
50
573
void setMostFrequentValueCount(TNode store, uint64_t count) {
51
573
  return store.setAttribute(ArrayConstantMostFrequentValueCountAttr(), count);
52
}
53
54
9913
TheoryArraysRewriter::TheoryArraysRewriter(ProofNodeManager* pnm)
55
9913
    : d_epg(pnm ? new EagerProofGenerator(pnm) : nullptr)
56
{
57
9913
}
58
59
7953
Node TheoryArraysRewriter::normalizeConstant(TNode node)
60
{
61
7953
  return normalizeConstant(node, node[1].getType().getCardinality());
62
}
63
64
// this function is called by printers when using the option "--model-u-dt-enum"
65
7953
Node TheoryArraysRewriter::normalizeConstant(TNode node, Cardinality indexCard)
66
{
67
15906
  TNode store = node[0];
68
15906
  TNode index = node[1];
69
15906
  TNode value = node[2];
70
71
15906
  std::vector<TNode> indices;
72
15906
  std::vector<TNode> elements;
73
74
  // Normal form for nested stores is just ordering by index - but also need
75
  // to check if we are writing to default value
76
77
  // Go through nested stores looking for where to insert index
78
  // Also check whether we are replacing an existing store
79
15906
  TNode replacedValue;
80
7953
  uint32_t depth = 1;
81
7953
  uint32_t valCount = 1;
82
9343
  while (store.getKind() == kind::STORE)
83
  {
84
2713
    if (index == store[1])
85
    {
86
146
      replacedValue = store[2];
87
146
      store = store[0];
88
146
      break;
89
    }
90
2567
    else if (index >= store[1])
91
    {
92
1872
      break;
93
    }
94
695
    if (value == store[2])
95
    {
96
145
      valCount += 1;
97
    }
98
695
    depth += 1;
99
695
    indices.push_back(store[1]);
100
695
    elements.push_back(store[2]);
101
695
    store = store[0];
102
  }
103
15906
  Node n = store;
104
105
  // Get the default value at the bottom of the nested stores
106
17945
  while (store.getKind() == kind::STORE)
107
  {
108
4996
    if (value == store[2])
109
    {
110
634
      valCount += 1;
111
    }
112
4996
    depth += 1;
113
4996
    store = store[0];
114
  }
115
7953
  Assert(store.getKind() == kind::STORE_ALL);
116
15906
  ArrayStoreAll storeAll = store.getConst<ArrayStoreAll>();
117
15906
  Node defaultValue = storeAll.getValue();
118
7953
  NodeManager* nm = NodeManager::currentNM();
119
120
  // Check if we are writing to default value - if so the store
121
  // to index can be ignored
122
7953
  if (value == defaultValue)
123
  {
124
337
    if (replacedValue.isNull())
125
    {
126
      // Quick exit - if writing to default value and nothing was
127
      // replaced, we can just return node[0]
128
325
      return node[0];
129
    }
130
    // else rebuild the store without the replaced write and then exit
131
  }
132
  else
133
  {
134
7616
    n = nm->mkNode(kind::STORE, n, index, value);
135
  }
136
137
  // Build the rest of the store after inserting/deleting
138
8836
  while (!indices.empty())
139
  {
140
604
    n = nm->mkNode(kind::STORE, n, indices.back(), elements.back());
141
604
    indices.pop_back();
142
604
    elements.pop_back();
143
  }
144
145
  // Ready to exit if write was to the default value (see previous comment)
146
7628
  if (value == defaultValue)
147
  {
148
12
    return n;
149
  }
150
151
7616
  if (indexCard.isInfinite())
152
  {
153
6115
    return n;
154
  }
155
156
  // When index sort is finite, we have to check whether there is any value
157
  // that is written to more than the default value.  If so, it must become
158
  // the new default value
159
160
3002
  TNode mostFrequentValue;
161
1501
  uint32_t mostFrequentValueCount = 0;
162
1501
  store = node[0];
163
1501
  if (store.getKind() == kind::STORE)
164
  {
165
851
    mostFrequentValue = getMostFrequentValue(store);
166
851
    mostFrequentValueCount = getMostFrequentValueCount(store);
167
  }
168
169
  // Compute the most frequently written value for n
170
1501
  if (valCount > mostFrequentValueCount
171
1501
      || (valCount == mostFrequentValueCount && value < mostFrequentValue))
172
  {
173
991
    mostFrequentValue = value;
174
991
    mostFrequentValueCount = valCount;
175
  }
176
177
  // Need to make sure the default value count is larger, or the same and the
178
  // default value is expression-order-less-than nextValue
179
  Cardinality::CardinalityComparison compare =
180
1501
      indexCard.compare(mostFrequentValueCount + depth);
181
1501
  Assert(compare != Cardinality::UNKNOWN);
182
1501
  if (compare == Cardinality::GREATER
183
1501
      || (compare == Cardinality::EQUAL && (defaultValue < mostFrequentValue)))
184
  {
185
1416
    return n;
186
  }
187
188
  // Bad case: have to recompute value counts and/or possibly switch out
189
  // default value
190
85
  store = n;
191
170
  std::unordered_set<TNode> indexSet;
192
170
  std::unordered_map<TNode, uint32_t> elementsMap;
193
85
  std::unordered_map<TNode, uint32_t>::iterator it;
194
  uint32_t count;
195
85
  uint32_t max = 0;
196
170
  TNode maxValue;
197
367
  while (store.getKind() == kind::STORE)
198
  {
199
141
    indices.push_back(store[1]);
200
141
    indexSet.insert(store[1]);
201
141
    elements.push_back(store[2]);
202
141
    it = elementsMap.find(store[2]);
203
141
    if (it != elementsMap.end())
204
    {
205
39
      (*it).second = (*it).second + 1;
206
39
      count = (*it).second;
207
    }
208
    else
209
    {
210
102
      elementsMap[store[2]] = 1;
211
102
      count = 1;
212
    }
213
141
    if (count > max || (count == max && store[2] < maxValue))
214
    {
215
133
      max = count;
216
133
      maxValue = store[2];
217
    }
218
141
    store = store[0];
219
  }
220
221
85
  Assert(depth == indices.size());
222
85
  compare = indexCard.compare(max + depth);
223
85
  Assert(compare != Cardinality::UNKNOWN);
224
85
  if (compare == Cardinality::GREATER
225
85
      || (compare == Cardinality::EQUAL && (defaultValue < maxValue)))
226
  {
227
    Assert(!replacedValue.isNull() && mostFrequentValue == replacedValue);
228
    return n;
229
  }
230
231
  // Out of luck: have to swap out default value
232
233
  // Enumerate values from index type into newIndices and sort
234
170
  std::vector<Node> newIndices;
235
170
  TypeEnumerator te(index.getType());
236
85
  bool needToSort = false;
237
85
  uint32_t numTe = 0;
238
541
  while (!te.isFinished()
239
1167
         && (!indexCard.isFinite()
240
541
             || numTe < indexCard.getFiniteCardinality().toUnsignedInt()))
241
  {
242
228
    if (indexSet.find(*te) == indexSet.end())
243
    {
244
87
      if (!newIndices.empty() && (!(newIndices.back() < (*te))))
245
      {
246
6
        needToSort = true;
247
      }
248
87
      newIndices.push_back(*te);
249
    }
250
228
    ++numTe;
251
228
    ++te;
252
  }
253
85
  Assert(indexCard.compare(newIndices.size() + depth) == Cardinality::EQUAL);
254
85
  if (needToSort)
255
  {
256
4
    std::sort(newIndices.begin(), newIndices.end());
257
  }
258
259
85
  n = nm->mkConst(ArrayStoreAll(node.getType(), maxValue));
260
85
  std::vector<Node>::iterator itNew = newIndices.begin(),
261
85
                              it_end = newIndices.end();
262
541
  while (itNew != it_end || !indices.empty())
263
  {
264
228
    if (itNew != it_end && (indices.empty() || (*itNew) < indices.back()))
265
    {
266
87
      n = nm->mkNode(kind::STORE, n, (*itNew), defaultValue);
267
87
      ++itNew;
268
    }
269
141
    else if (itNew == it_end || indices.back() < (*itNew))
270
    {
271
141
      if (elements.back() != maxValue)
272
      {
273
17
        n = nm->mkNode(kind::STORE, n, indices.back(), elements.back());
274
      }
275
141
      indices.pop_back();
276
141
      elements.pop_back();
277
    }
278
  }
279
85
  return n;
280
}
281
282
23
Node TheoryArraysRewriter::expandEqRange(TNode node)
283
{
284
23
  Assert(node.getKind() == kind::EQ_RANGE);
285
286
23
  NodeManager* nm = NodeManager::currentNM();
287
46
  TNode a = node[0];
288
46
  TNode b = node[1];
289
46
  TNode i = node[2];
290
46
  TNode j = node[3];
291
46
  Node k = SkolemCache::getEqRangeVar(node);
292
46
  Node bvl = nm->mkNode(kind::BOUND_VAR_LIST, k);
293
46
  TypeNode type = k.getType();
294
295
  Kind kle;
296
46
  Node range;
297
23
  if (type.isBitVector())
298
  {
299
19
    kle = kind::BITVECTOR_ULE;
300
  }
301
4
  else if (type.isFloatingPoint())
302
  {
303
    kle = kind::FLOATINGPOINT_LEQ;
304
  }
305
4
  else if (type.isInteger() || type.isReal())
306
  {
307
4
    kle = kind::LEQ;
308
  }
309
  else
310
  {
311
    Unimplemented() << "Type " << type << " is not supported for predicate "
312
                    << node.getKind();
313
  }
314
315
23
  range = nm->mkNode(kind::AND, nm->mkNode(kle, i, k), nm->mkNode(kle, k, j));
316
317
  Node eq = nm->mkNode(kind::EQUAL,
318
46
                       nm->mkNode(kind::SELECT, a, k),
319
92
                       nm->mkNode(kind::SELECT, b, k));
320
46
  Node implies = nm->mkNode(kind::IMPLIES, range, eq);
321
46
  return nm->mkNode(kind::FORALL, bvl, implies);
322
}
323
324
57513
RewriteResponse TheoryArraysRewriter::postRewrite(TNode node)
325
{
326
115026
  Trace("arrays-postrewrite")
327
57513
      << "Arrays::postRewrite start " << node << std::endl;
328
57513
  switch (node.getKind())
329
  {
330
21646
    case kind::SELECT:
331
    {
332
21646
      TNode store = node[0];
333
21646
      TNode index = node[1];
334
21646
      Node n;
335
      bool val;
336
22894
      while (store.getKind() == kind::STORE)
337
      {
338
8143
        if (index == store[1])
339
        {
340
39
          val = true;
341
        }
342
8104
        else if (index.isConst() && store[1].isConst())
343
        {
344
294
          val = false;
345
        }
346
        else
347
        {
348
7810
          n = Rewriter::rewrite(mkEqNode(store[1], index));
349
7810
          if (n.getKind() != kind::CONST_BOOLEAN)
350
          {
351
7480
            break;
352
          }
353
330
          val = n.getConst<bool>();
354
        }
355
663
        if (val)
356
        {
357
          // select(store(a,i,v),j) = v if i = j
358
78
          Trace("arrays-postrewrite")
359
39
              << "Arrays::postRewrite returning " << store[2] << std::endl;
360
39
          return RewriteResponse(REWRITE_DONE, store[2]);
361
        }
362
        // select(store(a,i,v),j) = select(a,j) if i /= j
363
624
        store = store[0];
364
      }
365
21607
      if (store.getKind() == kind::STORE_ALL)
366
      {
367
        // select(store_all(v),i) = v
368
590
        ArrayStoreAll storeAll = store.getConst<ArrayStoreAll>();
369
295
        n = storeAll.getValue();
370
590
        Trace("arrays-postrewrite")
371
295
            << "Arrays::postRewrite returning " << n << std::endl;
372
295
        Assert(n.isConst());
373
295
        return RewriteResponse(REWRITE_DONE, n);
374
      }
375
21312
      else if (store != node[0])
376
      {
377
64
        n = NodeManager::currentNM()->mkNode(kind::SELECT, store, index);
378
128
        Trace("arrays-postrewrite")
379
64
            << "Arrays::postRewrite returning " << n << std::endl;
380
64
        return RewriteResponse(REWRITE_DONE, n);
381
      }
382
21248
      break;
383
    }
384
15581
    case kind::STORE:
385
    {
386
15581
      TNode store = node[0];
387
15581
      TNode value = node[2];
388
      // store(a,i,select(a,i)) = a
389
32142
      if (value.getKind() == kind::SELECT && value[0] == store
390
31408
          && value[1] == node[1])
391
      {
392
80
        Trace("arrays-postrewrite")
393
40
            << "Arrays::postRewrite returning " << store << std::endl;
394
40
        return RewriteResponse(REWRITE_DONE, store);
395
      }
396
15541
      TNode index = node[1];
397
15541
      if (store.isConst() && index.isConst() && value.isConst())
398
      {
399
        // normalize constant
400
15906
        Node n = normalizeConstant(node);
401
7953
        Assert(n.isConst());
402
15906
        Trace("arrays-postrewrite")
403
7953
            << "Arrays::postRewrite returning " << n << std::endl;
404
7953
        return RewriteResponse(REWRITE_DONE, n);
405
      }
406
7588
      if (store.getKind() == kind::STORE)
407
      {
408
        // store(store(a,i,v),j,w)
409
        bool val;
410
4500
        if (index == store[1])
411
        {
412
19
          val = true;
413
        }
414
4481
        else if (index.isConst() && store[1].isConst())
415
        {
416
438
          val = false;
417
        }
418
        else
419
        {
420
6245
          Node eqRewritten = Rewriter::rewrite(mkEqNode(store[1], index));
421
4043
          if (eqRewritten.getKind() != kind::CONST_BOOLEAN)
422
          {
423
3682
            Trace("arrays-postrewrite")
424
1841
                << "Arrays::postRewrite returning " << node << std::endl;
425
1841
            return RewriteResponse(REWRITE_DONE, node);
426
          }
427
2202
          val = eqRewritten.getConst<bool>();
428
        }
429
2659
        NodeManager* nm = NodeManager::currentNM();
430
2659
        if (val)
431
        {
432
          // store(store(a,i,v),i,w) = store(a,i,w)
433
38
          Node result = nm->mkNode(kind::STORE, store[0], index, value);
434
38
          Trace("arrays-postrewrite")
435
19
              << "Arrays::postRewrite returning " << result << std::endl;
436
19
          return RewriteResponse(REWRITE_AGAIN, result);
437
        }
438
2640
        else if (index < store[1])
439
        {
440
          // store(store(a,i,v),j,w) = store(store(a,j,w),i,v)
441
          //    IF i != j and j comes before i in the ordering
442
410
          std::vector<TNode> indices;
443
410
          std::vector<TNode> elements;
444
205
          indices.push_back(store[1]);
445
205
          elements.push_back(store[2]);
446
205
          store = store[0];
447
410
          Node n;
448
1671
          while (store.getKind() == kind::STORE)
449
          {
450
839
            if (index == store[1])
451
            {
452
8
              val = true;
453
            }
454
831
            else if (index.isConst() && store[1].isConst())
455
            {
456
20
              val = false;
457
            }
458
            else
459
            {
460
811
              n = Rewriter::rewrite(mkEqNode(store[1], index));
461
811
              if (n.getKind() != kind::CONST_BOOLEAN)
462
              {
463
28
                break;
464
              }
465
783
              val = n.getConst<bool>();
466
            }
467
811
            if (val)
468
            {
469
8
              store = store[0];
470
8
              break;
471
            }
472
803
            else if (!(index < store[1]))
473
            {
474
70
              break;
475
            }
476
733
            indices.push_back(store[1]);
477
733
            elements.push_back(store[2]);
478
733
            store = store[0];
479
          }
480
435
          if (value.getKind() == kind::SELECT && value[0] == store
481
424
              && value[1] == index)
482
          {
483
6
            n = store;
484
          }
485
          else
486
          {
487
199
            n = nm->mkNode(kind::STORE, store, index, value);
488
          }
489
2081
          while (!indices.empty())
490
          {
491
938
            n = nm->mkNode(kind::STORE, n, indices.back(), elements.back());
492
938
            indices.pop_back();
493
938
            elements.pop_back();
494
          }
495
205
          Assert(n != node);
496
410
          Trace("arrays-postrewrite")
497
205
              << "Arrays::postRewrite returning " << n << std::endl;
498
205
          return RewriteResponse(REWRITE_AGAIN, n);
499
        }
500
      }
501
5523
      break;
502
    }
503
18927
    case kind::EQUAL:
504
    {
505
18927
      if (node[0] == node[1])
506
      {
507
1452
        Trace("arrays-postrewrite")
508
726
            << "Arrays::postRewrite returning true" << std::endl;
509
        return RewriteResponse(REWRITE_DONE,
510
726
                               NodeManager::currentNM()->mkConst(true));
511
      }
512
18201
      else if (node[0].isConst() && node[1].isConst())
513
      {
514
522
        Trace("arrays-postrewrite")
515
261
            << "Arrays::postRewrite returning false" << std::endl;
516
        return RewriteResponse(REWRITE_DONE,
517
261
                               NodeManager::currentNM()->mkConst(false));
518
      }
519
17940
      if (node[0] > node[1])
520
      {
521
        Node newNode =
522
6612
            NodeManager::currentNM()->mkNode(node.getKind(), node[1], node[0]);
523
6612
        Trace("arrays-postrewrite")
524
3306
            << "Arrays::postRewrite returning " << newNode << std::endl;
525
3306
        return RewriteResponse(REWRITE_DONE, newNode);
526
      }
527
14634
      break;
528
    }
529
1359
    default: break;
530
  }
531
85528
  Trace("arrays-postrewrite")
532
42764
      << "Arrays::postRewrite returning " << node << std::endl;
533
42764
  return RewriteResponse(REWRITE_DONE, node);
534
}
535
536
32463
RewriteResponse TheoryArraysRewriter::preRewrite(TNode node)
537
{
538
64926
  Trace("arrays-prerewrite")
539
32463
      << "Arrays::preRewrite start " << node << std::endl;
540
32463
  switch (node.getKind())
541
  {
542
12874
    case kind::SELECT:
543
    {
544
12874
      TNode store = node[0];
545
12874
      TNode index = node[1];
546
12874
      Node n;
547
      bool val;
548
17590
      while (store.getKind() == kind::STORE)
549
      {
550
7091
        if (index == store[1])
551
        {
552
924
          val = true;
553
        }
554
6167
        else if (index.isConst() && store[1].isConst())
555
        {
556
1583
          val = false;
557
        }
558
        else
559
        {
560
4584
          n = Rewriter::rewrite(mkEqNode(store[1], index));
561
4584
          if (n.getKind() != kind::CONST_BOOLEAN)
562
          {
563
3756
            break;
564
          }
565
828
          val = n.getConst<bool>();
566
        }
567
3335
        if (val)
568
        {
569
          // select(store(a,i,v),j) = v if i = j
570
1954
          Trace("arrays-prerewrite")
571
977
              << "Arrays::preRewrite returning " << store[2] << std::endl;
572
977
          return RewriteResponse(REWRITE_AGAIN, store[2]);
573
        }
574
        // select(store(a,i,v),j) = select(a,j) if i /= j
575
2358
        store = store[0];
576
      }
577
11897
      if (store.getKind() == kind::STORE_ALL)
578
      {
579
        // select(store_all(v),i) = v
580
892
        ArrayStoreAll storeAll = store.getConst<ArrayStoreAll>();
581
446
        n = storeAll.getValue();
582
892
        Trace("arrays-prerewrite")
583
446
            << "Arrays::preRewrite returning " << n << std::endl;
584
446
        Assert(n.isConst());
585
446
        return RewriteResponse(REWRITE_DONE, n);
586
      }
587
11451
      else if (store != node[0])
588
      {
589
298
        n = NodeManager::currentNM()->mkNode(kind::SELECT, store, index);
590
596
        Trace("arrays-prerewrite")
591
298
            << "Arrays::preRewrite returning " << n << std::endl;
592
298
        return RewriteResponse(REWRITE_DONE, n);
593
      }
594
11153
      break;
595
    }
596
7934
    case kind::STORE:
597
    {
598
7934
      TNode store = node[0];
599
7934
      TNode value = node[2];
600
      // store(a,i,select(a,i)) = a
601
16348
      if (value.getKind() == kind::SELECT && value[0] == store
602
15994
          && value[1] == node[1])
603
      {
604
30
        Trace("arrays-prerewrite")
605
15
            << "Arrays::preRewrite returning " << store << std::endl;
606
15
        return RewriteResponse(REWRITE_AGAIN, store);
607
      }
608
7919
      if (store.getKind() == kind::STORE)
609
      {
610
        // store(store(a,i,v),j,w)
611
5347
        TNode index = node[1];
612
        bool val;
613
3186
        if (index == store[1])
614
        {
615
226
          val = true;
616
        }
617
2960
        else if (index.isConst() && store[1].isConst())
618
        {
619
1324
          val = false;
620
        }
621
        else
622
        {
623
2487
          Node eqRewritten = Rewriter::rewrite(mkEqNode(store[1], index));
624
1636
          if (eqRewritten.getKind() != kind::CONST_BOOLEAN)
625
          {
626
785
            break;
627
          }
628
851
          val = eqRewritten.getConst<bool>();
629
        }
630
2401
        NodeManager* nm = NodeManager::currentNM();
631
2401
        if (val)
632
        {
633
          // store(store(a,i,v),i,w) = store(a,i,w)
634
480
          Node newNode = nm->mkNode(kind::STORE, store[0], index, value);
635
480
          Trace("arrays-prerewrite")
636
240
              << "Arrays::preRewrite returning " << newNode << std::endl;
637
240
          return RewriteResponse(REWRITE_DONE, newNode);
638
        }
639
      }
640
6894
      break;
641
    }
642
11062
    case kind::EQUAL:
643
    {
644
11062
      if (node[0] == node[1])
645
      {
646
1960
        Trace("arrays-prerewrite")
647
980
            << "Arrays::preRewrite returning true" << std::endl;
648
        return RewriteResponse(REWRITE_DONE,
649
980
                               NodeManager::currentNM()->mkConst(true));
650
      }
651
10082
      break;
652
    }
653
593
    default: break;
654
  }
655
656
59014
  Trace("arrays-prerewrite")
657
29507
      << "Arrays::preRewrite returning " << node << std::endl;
658
29507
  return RewriteResponse(REWRITE_DONE, node);
659
}
660
661
22444
TrustNode TheoryArraysRewriter::expandDefinition(Node node)
662
{
663
22444
  Kind kind = node.getKind();
664
665
22444
  if (kind == kind::EQ_RANGE)
666
  {
667
40
    Node expandedEqRange = expandEqRange(node);
668
20
    if (d_epg)
669
    {
670
6
      TrustNode tn = d_epg->mkTrustNode(node.eqNode(expandedEqRange),
671
                                        PfRule::ARRAYS_EQ_RANGE_EXPAND,
672
                                        {},
673
12
                                        {node});
674
3
      return TrustNode::mkTrustRewrite(node, expandedEqRange, d_epg.get());
675
    }
676
17
    return TrustNode::mkTrustRewrite(node, expandedEqRange, nullptr);
677
  }
678
679
22424
  return TrustNode::null();
680
}
681
682
}  // namespace arrays
683
}  // namespace theory
684
29502
}  // namespace cvc5