GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/preprocessing/passes/bv_gauss.cpp Lines: 311 342 90.9 %
Date: 2021-05-22 Branches: 676 1688 40.0 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Aina Niemetz, Mathias Preiner, Andrew Reynolds
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
 * Gaussian Elimination preprocessing pass.
14
 *
15
 * Simplify a given equation system modulo a (prime) number via Gaussian
16
 * Elimination if possible.
17
 */
18
19
#include "preprocessing/passes/bv_gauss.h"
20
21
#include <unordered_map>
22
#include <vector>
23
24
#include "expr/node.h"
25
#include "preprocessing/assertion_pipeline.h"
26
#include "preprocessing/preprocessing_pass_context.h"
27
#include "theory/bv/theory_bv_rewrite_rules_normalization.h"
28
#include "theory/bv/theory_bv_utils.h"
29
#include "theory/rewriter.h"
30
#include "util/bitvector.h"
31
32
using namespace cvc5;
33
using namespace cvc5::theory;
34
using namespace cvc5::theory::bv;
35
36
namespace cvc5 {
37
namespace preprocessing {
38
namespace passes {
39
40
namespace {
41
42
5802
bool is_bv_const(Node n)
43
{
44
5802
  if (n.isConst()) { return true; }
45
3992
  return Rewriter::rewrite(n).getKind() == kind::CONST_BITVECTOR;
46
}
47
48
902
Node get_bv_const(Node n)
49
{
50
902
  Assert(is_bv_const(n));
51
902
  return Rewriter::rewrite(n);
52
}
53
54
482
Integer get_bv_const_value(Node n)
55
{
56
482
  Assert(is_bv_const(n));
57
482
  return get_bv_const(n).getConst<BitVector>().getValue();
58
}
59
60
}  // namespace
61
62
/**
63
 * Determines if an overflow may occur in given 'expr'.
64
 *
65
 * Returns 0 if an overflow may occur, and the minimum required
66
 * bit-width such that no overflow occurs, otherwise.
67
 *
68
 * Note that it would suffice for this function to be Boolean.
69
 * However, it is handy to determine the minimum required bit-width for
70
 * debugging purposes.
71
 *
72
 * Note: getMinBwExpr assumes that 'expr' is rewritten.
73
 *
74
 * If not, all operators that are removed via rewriting (e.g., ror, rol, ...)
75
 * will be handled via the default case, which is not incorrect but also not
76
 * necessarily the minimum.
77
 */
78
202
unsigned BVGauss::getMinBwExpr(Node expr)
79
{
80
404
  std::vector<Node> visit;
81
  /* Maps visited nodes to the determined minimum bit-width required. */
82
404
  std::unordered_map<Node, unsigned> visited;
83
202
  std::unordered_map<Node, unsigned>::iterator it;
84
85
202
  visit.push_back(expr);
86
5634
  while (!visit.empty())
87
  {
88
5438
    Node n = visit.back();
89
2722
    visit.pop_back();
90
2722
    it = visited.find(n);
91
2722
    if (it == visited.end())
92
    {
93
1490
      if (is_bv_const(n))
94
      {
95
        /* Rewrite const expr, overflows in consts are irrelevant. */
96
420
        visited[n] = get_bv_const(n).getConst<BitVector>().getValue().length();
97
      }
98
      else
99
      {
100
1070
        visited[n] = 0;
101
1070
        visit.push_back(n);
102
1070
        for (const Node &nn : n) { visit.push_back(nn); }
103
      }
104
    }
105
1232
    else if (it->second == 0)
106
    {
107
1066
      Kind k = n.getKind();
108
1066
      Assert(k != kind::CONST_BITVECTOR);
109
1066
      Assert(!is_bv_const(n));
110
1066
      switch (k)
111
      {
112
56
        case kind::BITVECTOR_EXTRACT:
113
        {
114
56
          const unsigned size = bv::utils::getSize(n);
115
56
          const unsigned low = bv::utils::getExtractLow(n);
116
56
          const unsigned child_min_width = visited[n[0]];
117
56
          visited[n] = std::min(
118
112
              size, child_min_width >= low ? child_min_width - low : 0u);
119
56
          Assert(visited[n] <= visited[n[0]]);
120
56
          break;
121
        }
122
123
72
        case kind::BITVECTOR_ZERO_EXTEND:
124
        {
125
72
          visited[n] = visited[n[0]];
126
72
          break;
127
        }
128
129
138
        case kind::BITVECTOR_MULT:
130
        {
131
138
          Integer maxval = Integer(1);
132
426
          for (const Node& nn : n)
133
          {
134
288
            if (is_bv_const(nn))
135
            {
136
114
              maxval *= get_bv_const_value(nn);
137
            }
138
            else
139
            {
140
174
              maxval *= BitVector::mkOnes(visited[nn]).getValue();
141
            }
142
          }
143
138
          unsigned w = maxval.length();
144
138
          if (w > bv::utils::getSize(n)) { return 0; } /* overflow */
145
134
          visited[n] = w;
146
134
          break;
147
        }
148
149
328
        case kind::BITVECTOR_CONCAT:
150
        {
151
          unsigned i, wnz, nc;
152
650
          for (i = 0, wnz = 0, nc = n.getNumChildren() - 1; i < nc; ++i)
153
          {
154
378
            unsigned wni = bv::utils::getSize(n[i]);
155
378
            if (n[i] != bv::utils::mkZero(wni)) { break; }
156
            /* sum of all bit-widths of leading zero concats */
157
322
            wnz += wni;
158
          }
159
          /* Do not consider leading zero concats, i.e.,
160
           * min bw of current concat is determined as
161
           *   min bw of first non-zero term
162
           *   plus actual bw of all subsequent terms */
163
984
          visited[n] = bv::utils::getSize(n) + visited[n[i]]
164
656
                       - bv::utils::getSize(n[i]) - wnz;
165
328
          break;
166
        }
167
168
6
        case kind::BITVECTOR_UREM:
169
        case kind::BITVECTOR_LSHR:
170
        case kind::BITVECTOR_ASHR:
171
        {
172
6
          visited[n] = visited[n[0]];
173
6
          break;
174
        }
175
176
        case kind::BITVECTOR_OR:
177
        case kind::BITVECTOR_NOR:
178
        case kind::BITVECTOR_XOR:
179
        case kind::BITVECTOR_XNOR:
180
        case kind::BITVECTOR_AND:
181
        case kind::BITVECTOR_NAND:
182
        {
183
          unsigned wmax = 0;
184
          for (const Node &nn : n)
185
          {
186
            if (visited[nn] > wmax)
187
            {
188
              wmax = visited[nn];
189
            }
190
          }
191
          visited[n] = wmax;
192
          break;
193
        }
194
195
104
        case kind::BITVECTOR_ADD:
196
        {
197
104
          Integer maxval = Integer(0);
198
364
          for (const Node& nn : n)
199
          {
200
260
            if (is_bv_const(nn))
201
            {
202
              maxval += get_bv_const_value(nn);
203
            }
204
            else
205
            {
206
260
              maxval += BitVector::mkOnes(visited[nn]).getValue();
207
            }
208
          }
209
104
          unsigned w = maxval.length();
210
104
          if (w > bv::utils::getSize(n)) { return 0; } /* overflow */
211
102
          visited[n] = w;
212
102
          break;
213
        }
214
215
362
        default:
216
        {
217
          /* BITVECTOR_UDIV (since x / 0 = -1)
218
           * BITVECTOR_NOT
219
           * BITVECTOR_NEG
220
           * BITVECTOR_SHL */
221
362
          visited[n] = bv::utils::getSize(n);
222
        }
223
      }
224
    }
225
  }
226
196
  Assert(visited.find(expr) != visited.end());
227
196
  return visited[expr];
228
}
229
230
/**
231
 * Apply Gaussian Elimination modulo a (prime) number.
232
 * The given equation system is represented as a matrix of Integers.
233
 *
234
 * Note that given 'prime' does not have to be prime but can be any
235
 * arbitrary number. However, if 'prime' is indeed prime, GE is guaranteed
236
 * to succeed, which is not the case, otherwise.
237
 *
238
 * Returns INVALID if GE can not be applied, UNIQUE and PARTIAL if GE was
239
 * successful, and NONE, otherwise.
240
 *
241
 * Vectors 'rhs' and 'lhs' represent the right hand side and left hand side
242
 * of the given matrix, respectively. The resulting matrix (in row echelon
243
 * form) is stored in 'rhs' and 'lhs', i.e., the given matrix is overwritten
244
 * with the resulting matrix.
245
 */
246
140
BVGauss::Result BVGauss::gaussElim(Integer prime,
247
                                   std::vector<Integer>& rhs,
248
                                   std::vector<std::vector<Integer>>& lhs)
249
{
250
140
  Assert(prime > 0);
251
140
  Assert(lhs.size());
252
140
  Assert(lhs.size() == rhs.size());
253
140
  Assert(lhs.size() <= lhs[0].size());
254
255
  /* special case: zero ring */
256
140
  if (prime == 1)
257
  {
258
2
    rhs = std::vector<Integer>(rhs.size(), Integer(0));
259
6
    lhs = std::vector<std::vector<Integer>>(
260
4
        lhs.size(), std::vector<Integer>(lhs[0].size(), Integer(0)));
261
2
    return BVGauss::Result::UNIQUE;
262
  }
263
264
138
  size_t nrows = lhs.size();
265
138
  size_t ncols = lhs[0].size();
266
267
#ifdef CVC5_ASSERTIONS
268
138
  for (size_t i = 1; i < nrows; ++i) Assert(lhs[i].size() == ncols);
269
#endif
270
  /* (1) if element in pivot column is non-zero and != 1, divide row elements
271
   *     by element in pivot column modulo prime, i.e., multiply row with
272
   *     multiplicative inverse of element in pivot column modulo prime
273
   *
274
   * (2) subtract pivot row from all rows below pivot row
275
   *
276
   * (3) subtract (multiple of) current row from all rows above s.t. all
277
   *     elements in current pivot column above current row become equal to one
278
   *
279
   * Note: we do not normalize the given matrix to values modulo prime
280
   *       beforehand but on-the-fly. */
281
282
  /* pivot = lhs[pcol][pcol] */
283
468
  for (size_t pcol = 0, prow = 0; pcol < ncols && prow < nrows; ++pcol, ++prow)
284
  {
285
    /* lhs[j][pcol]: element in pivot column */
286
1020
    for (size_t j = prow; j < nrows; ++j)
287
    {
288
#ifdef CVC5_ASSERTIONS
289
1142
      for (size_t k = 0; k < pcol; ++k)
290
      {
291
452
        Assert(lhs[j][k] == 0);
292
      }
293
#endif
294
      /* normalize element in pivot column to modulo prime */
295
690
      lhs[j][pcol] = lhs[j][pcol].euclidianDivideRemainder(prime);
296
      /* exchange rows if pivot elem is 0 */
297
690
      if (j == prow)
298
      {
299
498
        while (lhs[j][pcol] == 0)
300
        {
301
172
          for (size_t k = prow + 1; k < nrows; ++k)
302
          {
303
106
            lhs[k][pcol] = lhs[k][pcol].euclidianDivideRemainder(prime);
304
106
            if (lhs[k][pcol] != 0)
305
            {
306
54
              std::swap(rhs[j], rhs[k]);
307
54
              std::swap(lhs[j], lhs[k]);
308
54
              break;
309
            }
310
          }
311
120
          if (pcol >= ncols - 1) break;
312
78
          if (lhs[j][pcol] == 0)
313
          {
314
34
            pcol += 1;
315
34
            if (lhs[j][pcol] != 0)
316
20
              lhs[j][pcol] = lhs[j][pcol].euclidianDivideRemainder(prime);
317
          }
318
        }
319
      }
320
321
690
      if (lhs[j][pcol] != 0)
322
      {
323
        /* (1) */
324
522
        if (lhs[j][pcol] != 1)
325
        {
326
740
          Integer inv = lhs[j][pcol].modInverse(prime);
327
376
          if (inv == -1)
328
          {
329
12
            return BVGauss::Result::INVALID; /* not coprime */
330
          }
331
1202
          for (size_t k = pcol; k < ncols; ++k)
332
          {
333
838
            lhs[j][k] = lhs[j][k].modMultiply(inv, prime);
334
838
            if (j <= prow) continue; /* pivot */
335
488
            lhs[j][k] = lhs[j][k].modAdd(-lhs[prow][k], prime);
336
          }
337
364
          rhs[j] = rhs[j].modMultiply(inv, prime);
338
364
          if (j > prow) { rhs[j] = rhs[j].modAdd(-rhs[prow], prime); }
339
        }
340
        /* (2) */
341
146
        else if (j != prow)
342
        {
343
78
          for (size_t k = pcol; k < ncols; ++k)
344
          {
345
58
            lhs[j][k] = lhs[j][k].modAdd(-lhs[prow][k], prime);
346
          }
347
20
          rhs[j] = rhs[j].modAdd(-rhs[prow], prime);
348
        }
349
      }
350
    }
351
    /* (3) */
352
610
    for (size_t j = 0; j < prow; ++j)
353
    {
354
560
      Integer mul = lhs[j][pcol];
355
280
      if (mul != 0)
356
      {
357
512
        for (size_t k = pcol; k < ncols; ++k)
358
        {
359
296
          lhs[j][k] = lhs[j][k].modAdd(-lhs[prow][k] * mul, prime);
360
        }
361
216
        rhs[j] = rhs[j].modAdd(-rhs[prow] * mul, prime);
362
      }
363
    }
364
  }
365
366
126
  bool ispart = false;
367
472
  for (size_t i = 0; i < nrows; ++i)
368
  {
369
354
    size_t pcol = i;
370
460
    while (pcol < ncols && lhs[i][pcol] == 0) ++pcol;
371
404
    if (pcol >= ncols)
372
    {
373
58
      rhs[i] = rhs[i].euclidianDivideRemainder(prime);
374
58
      if (rhs[i] != 0)
375
      {
376
        /* no solution */
377
8
        return BVGauss::Result::NONE;
378
      }
379
50
      continue;
380
    }
381
976
    for (size_t j = i; j < ncols; ++j)
382
    {
383
680
      if (lhs[i][j] >= prime || lhs[i][j] <= -prime)
384
      {
385
2
        lhs[i][j] = lhs[i][j].euclidianDivideRemainder(prime);
386
      }
387
680
      if (j > pcol && lhs[i][j] != 0)
388
      {
389
72
        ispart = true;
390
      }
391
    }
392
  }
393
394
118
  if (ispart)
395
  {
396
42
    return BVGauss::Result::PARTIAL;
397
  }
398
399
76
  return BVGauss::Result::UNIQUE;
400
}
401
402
/**
403
 * Apply Gaussian Elimination on a set of equations modulo some (prime)
404
 * number given as bit-vector equations.
405
 *
406
 * IMPORTANT: Applying GE modulo some number (rather than modulo 2^bw)
407
 * on a set of bit-vector equations is only sound if this set of equations
408
 * has a solution that does not produce overflows. Consequently, we only
409
 * apply GE if the given bit-width guarantees that no overflows can occur
410
 * in the given set of equations.
411
 *
412
 * Note that the given set of equations does not have to be modulo a prime
413
 * but can be modulo any arbitrary number. However, if it is indeed modulo
414
 * prime, GE is guaranteed to succeed, which is not the case, otherwise.
415
 *
416
 * Returns INVALID if GE can not be applied, UNIQUE and PARTIAL if GE was
417
 * successful, and NONE, otherwise.
418
 *
419
 * The resulting constraints are stored in 'res' as a mapping of unknown
420
 * to result (modulo prime). These mapped results are added as constraints
421
 * of the form 'unknown = mapped result' in applyInternal.
422
 */
423
36
BVGauss::Result BVGauss::gaussElimRewriteForUrem(
424
    const std::vector<Node>& equations, std::unordered_map<Node, Node>& res)
425
{
426
36
  Assert(res.empty());
427
428
72
  Node prime;
429
72
  Integer iprime;
430
72
  std::unordered_map<Node, std::vector<Integer>> vars;
431
36
  size_t neqs = equations.size();
432
72
  std::vector<Integer> rhs;
433
  std::vector<std::vector<Integer>> lhs =
434
72
      std::vector<std::vector<Integer>>(neqs, std::vector<Integer>());
435
436
36
  res = std::unordered_map<Node, Node>();
437
438
128
  for (size_t i = 0; i < neqs; ++i)
439
  {
440
184
    Node eq = equations[i];
441
92
    Assert(eq.getKind() == kind::EQUAL);
442
184
    Node urem, eqrhs;
443
444
92
    if (eq[0].getKind() == kind::BITVECTOR_UREM)
445
    {
446
92
      urem = eq[0];
447
92
      Assert(is_bv_const(eq[1]));
448
92
      eqrhs = eq[1];
449
    }
450
    else
451
    {
452
      Assert(eq[1].getKind() == kind::BITVECTOR_UREM);
453
      urem = eq[1];
454
      Assert(is_bv_const(eq[0]));
455
      eqrhs = eq[0];
456
    }
457
92
    if (getMinBwExpr(Rewriter::rewrite(urem[0])) == 0)
458
    {
459
      Trace("bv-gauss-elim")
460
          << "Minimum required bit-width exceeds given bit-width, "
461
             "will not apply Gaussian Elimination."
462
          << std::endl;
463
      return BVGauss::Result::INVALID;
464
    }
465
92
    rhs.push_back(get_bv_const_value(eqrhs));
466
467
92
    Assert(is_bv_const(urem[1]));
468
92
    Assert(i == 0 || get_bv_const_value(urem[1]) == iprime);
469
92
    if (i == 0)
470
    {
471
36
      prime = urem[1];
472
36
      iprime = get_bv_const_value(prime);
473
    }
474
475
184
    std::unordered_map<Node, Integer> tmp;
476
184
    std::vector<Node> stack;
477
92
    stack.push_back(urem[0]);
478
700
    while (!stack.empty())
479
    {
480
608
      Node n = stack.back();
481
304
      stack.pop_back();
482
483
      /* Subtract from rhs if const */
484
304
      if (is_bv_const(n))
485
      {
486
        Integer val = get_bv_const_value(n);
487
        if (val > 0) rhs.back() -= val;
488
        continue;
489
      }
490
491
      /* Split into matrix columns */
492
304
      Kind k = n.getKind();
493
304
      if (k == kind::BITVECTOR_ADD)
494
      {
495
106
        for (const Node& nn : n) { stack.push_back(nn); }
496
      }
497
198
      else if (k == kind::BITVECTOR_MULT)
498
      {
499
368
        Node n0, n1;
500
        /* Flatten mult expression. */
501
184
        n = RewriteRule<FlattenAssocCommut>::run<true>(n);
502
        /* Split operands into consts and non-consts */
503
368
        NodeBuilder nb_consts(NodeManager::currentNM(), k);
504
368
        NodeBuilder nb_nonconsts(NodeManager::currentNM(), k);
505
568
        for (const Node& nn : n)
506
        {
507
768
          Node nnrw = Rewriter::rewrite(nn);
508
384
          if (is_bv_const(nnrw))
509
          {
510
176
            nb_consts << nnrw;
511
          }
512
          else
513
          {
514
208
            nb_nonconsts << nnrw;
515
          }
516
        }
517
184
        Assert(nb_nonconsts.getNumChildren() > 0);
518
        /* n0 is const */
519
184
        unsigned nc = nb_consts.getNumChildren();
520
184
        if (nc > 1)
521
        {
522
          n0 = Rewriter::rewrite(nb_consts.constructNode());
523
        }
524
184
        else if (nc == 1)
525
        {
526
176
          n0 = nb_consts[0];
527
        }
528
        else
529
        {
530
8
          n0 = bv::utils::mkOne(bv::utils::getSize(n));
531
        }
532
        /* n1 is a mult with non-const operands */
533
184
        if (nb_nonconsts.getNumChildren() > 1)
534
        {
535
20
          n1 = Rewriter::rewrite(nb_nonconsts.constructNode());
536
        }
537
        else
538
        {
539
164
          n1 = nb_nonconsts[0];
540
        }
541
184
        Assert(is_bv_const(n0));
542
184
        Assert(!is_bv_const(n1));
543
184
        tmp[n1] += get_bv_const_value(n0);
544
      }
545
      else
546
      {
547
14
        tmp[n] += Integer(1);
548
      }
549
    }
550
551
    /* Note: "var" is not necessarily a VARIABLE but can be an arbitrary expr */
552
553
290
    for (const auto& p : tmp)
554
    {
555
396
      Node var = p.first;
556
396
      Integer val = p.second;
557
198
      if (i > 0 && vars.find(var) == vars.end())
558
      {
559
        /* Add column and fill column elements of rows above with 0. */
560
28
        vars[var].insert(vars[var].end(), i, Integer(0));
561
      }
562
198
      vars[var].push_back(val);
563
    }
564
565
332
    for (const auto& p : vars)
566
    {
567
240
      if (tmp.find(p.first) == tmp.end())
568
      {
569
42
        vars[p.first].push_back(Integer(0));
570
      }
571
    }
572
  }
573
574
36
  size_t nvars = vars.size();
575
36
  if (nvars == 0)
576
  {
577
    return BVGauss::Result::INVALID;
578
  }
579
36
  size_t nrows = vars.begin()->second.size();
580
#ifdef CVC5_ASSERTIONS
581
142
  for (const auto& p : vars)
582
  {
583
106
    Assert(p.second.size() == nrows);
584
  }
585
#endif
586
587
36
  if (nrows < 1)
588
  {
589
    return BVGauss::Result::INVALID;
590
  }
591
592
128
  for (size_t i = 0; i < nrows; ++i)
593
  {
594
364
    for (const auto& p : vars)
595
    {
596
272
      lhs[i].push_back(p.second[i]);
597
    }
598
  }
599
600
#ifdef CVC5_ASSERTIONS
601
128
  for (const auto& row : lhs)
602
  {
603
92
    Assert(row.size() == nvars);
604
  }
605
36
  Assert(lhs.size() == rhs.size());
606
#endif
607
608
36
  if (lhs.size() > lhs[0].size())
609
  {
610
2
    return BVGauss::Result::INVALID;
611
  }
612
613
34
  Trace("bv-gauss-elim") << "Applying Gaussian Elimination..." << std::endl;
614
34
  BVGauss::Result ret = gaussElim(iprime, rhs, lhs);
615
616
34
  if (ret != BVGauss::Result::NONE && ret != BVGauss::Result::INVALID)
617
  {
618
68
    std::vector<Node> vvars;
619
34
    for (const auto& p : vars) { vvars.push_back(p.first); }
620
34
    Assert(nvars == vvars.size());
621
34
    Assert(nrows == lhs.size());
622
34
    Assert(nrows == rhs.size());
623
34
    NodeManager *nm = NodeManager::currentNM();
624
34
    if (ret == BVGauss::Result::UNIQUE)
625
    {
626
54
      for (size_t i = 0; i < nvars; ++i)
627
      {
628
40
        res[vvars[i]] = nm->mkConst<BitVector>(
629
80
            BitVector(bv::utils::getSize(vvars[i]), rhs[i]));
630
      }
631
    }
632
    else
633
    {
634
20
      Assert(ret == BVGauss::Result::PARTIAL);
635
636
60
      for (size_t pcol = 0, prow = 0; pcol < nvars && prow < nrows;
637
           ++pcol, ++prow)
638
      {
639
44
        Assert(lhs[prow][pcol] == 0 || lhs[prow][pcol] == 1);
640
52
        while (pcol < nvars && lhs[prow][pcol] == 0) pcol += 1;
641
44
        if (pcol >= nvars)
642
        {
643
4
          Assert(rhs[prow] == 0);
644
4
          break;
645
        }
646
40
        if (lhs[prow][pcol] == 0)
647
        {
648
          Assert(rhs[prow] == 0);
649
          continue;
650
        }
651
40
        Assert(lhs[prow][pcol] == 1);
652
80
        std::vector<Node> stack;
653
100
        for (size_t i = pcol + 1; i < nvars; ++i)
654
        {
655
60
          if (lhs[prow][i] == 0) continue;
656
          /* Normalize (no negative numbers, hence no subtraction)
657
           * e.g., x = 4 - 2y  --> x = 4 + 9y (modulo 11) */
658
68
          Integer m = iprime - lhs[prow][i];
659
68
          Node bv = bv::utils::mkConst(bv::utils::getSize(vvars[i]), m);
660
68
          Node mult = nm->mkNode(kind::BITVECTOR_MULT, vvars[i], bv);
661
34
          stack.push_back(mult);
662
        }
663
664
40
        if (stack.empty())
665
        {
666
6
          res[vvars[pcol]] = nm->mkConst<BitVector>(
667
12
              BitVector(bv::utils::getSize(vvars[pcol]), rhs[prow]));
668
        }
669
        else
670
        {
671
68
          Node tmp = stack.size() == 1 ? stack[0]
672
102
                                       : nm->mkNode(kind::BITVECTOR_ADD, stack);
673
674
34
          if (rhs[prow] != 0)
675
          {
676
96
            tmp = nm->mkNode(
677
                kind::BITVECTOR_ADD,
678
64
                bv::utils::mkConst(bv::utils::getSize(vvars[pcol]), rhs[prow]),
679
                tmp);
680
          }
681
34
          Assert(!is_bv_const(tmp));
682
34
          res[vvars[pcol]] = nm->mkNode(kind::BITVECTOR_UREM, tmp, prime);
683
        }
684
      }
685
    }
686
  }
687
34
  return ret;
688
}
689
690
9465
BVGauss::BVGauss(PreprocessingPassContext* preprocContext,
691
9465
                 const std::string& name)
692
9465
    : PreprocessingPass(preprocContext, name)
693
{
694
9465
}
695
696
6
PreprocessingPassResult BVGauss::applyInternal(
697
    AssertionPipeline* assertionsToPreprocess)
698
{
699
12
  std::vector<Node> assertions(assertionsToPreprocess->ref());
700
12
  std::unordered_map<Node, std::vector<Node>> equations;
701
702
62
  while (!assertions.empty())
703
  {
704
56
    Node a = assertions.back();
705
28
    assertions.pop_back();
706
28
    cvc5::Kind k = a.getKind();
707
708
28
    if (k == kind::AND)
709
    {
710
24
      for (const Node& aa : a)
711
      {
712
16
        assertions.push_back(aa);
713
      }
714
    }
715
20
    else if (k == kind::EQUAL)
716
    {
717
40
      Node urem;
718
719
20
      if (is_bv_const(a[1]) && a[0].getKind() == kind::BITVECTOR_UREM)
720
      {
721
20
        urem = a[0];
722
      }
723
      else if (is_bv_const(a[0]) && a[1].getKind() == kind::BITVECTOR_UREM)
724
      {
725
        urem = a[1];
726
      }
727
      else
728
      {
729
        continue;
730
      }
731
732
20
      if (urem[0].getKind() == kind::BITVECTOR_ADD && is_bv_const(urem[1]))
733
      {
734
20
        equations[urem[1]].push_back(a);
735
      }
736
    }
737
  }
738
739
12
  std::unordered_map<Node, Node> subst;
740
741
6
  NodeManager* nm = NodeManager::currentNM();
742
14
  for (const auto& eq : equations)
743
  {
744
8
    if (eq.second.size() <= 1) { continue; }
745
746
16
    std::unordered_map<Node, Node> res;
747
8
    BVGauss::Result ret = gaussElimRewriteForUrem(eq.second, res);
748
16
    Trace("bv-gauss-elim") << "result: "
749
                           << (ret == BVGauss::Result::INVALID
750
24
                                   ? "INVALID"
751
                                   : (ret == BVGauss::Result::UNIQUE
752
10
                                          ? "UNIQUE"
753
                                          : (ret == BVGauss::Result::PARTIAL
754
2
                                                 ? "PARTIAL"
755
8
                                                 : "NONE")))
756
8
                           << std::endl;
757
8
    if (ret != BVGauss::Result::INVALID)
758
    {
759
8
      if (ret == BVGauss::Result::NONE)
760
      {
761
        assertionsToPreprocess->clear();
762
        Node n = nm->mkConst<bool>(false);
763
        assertionsToPreprocess->push_back(n);
764
        return PreprocessingPassResult::CONFLICT;
765
      }
766
      else
767
      {
768
28
        for (const Node& e : eq.second)
769
        {
770
20
          subst[e] = nm->mkConst<bool>(true);
771
        }
772
        /* add resulting constraints */
773
28
        for (const auto& p : res)
774
        {
775
40
          Node a = nm->mkNode(kind::EQUAL, p.first, p.second);
776
20
          Trace("bv-gauss-elim") << "added assertion: " << a << std::endl;
777
          // add new assertion
778
20
          assertionsToPreprocess->push_back(a);
779
        }
780
      }
781
    }
782
  }
783
784
6
  if (!subst.empty())
785
  {
786
    /* delete (= substitute with true) obsolete assertions */
787
6
    const std::vector<Node>& aref = assertionsToPreprocess->ref();
788
38
    for (size_t i = 0, asize = aref.size(); i < asize; ++i)
789
    {
790
64
      Node a = aref[i];
791
64
      Node as = a.substitute(subst.begin(), subst.end());
792
      // replace the assertion
793
32
      assertionsToPreprocess->replace(i, as);
794
    }
795
  }
796
6
  return PreprocessingPassResult::NO_CONFLICT;
797
}
798
799
800
}  // namespace passes
801
}  // namespace preprocessing
802
28191
}  // namespace cvc5