1 /*********************                                                        */
2 /*! \file unconstrained_simplifier.cpp
3  ** \verbatim
4  ** Top contributors (to current version):
5  **   Clark Barrett, Andres Noetzli, Tim King
6  ** This file is part of the CVC4 project.
7  ** Copyright (c) 2009-2019 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.\endverbatim
11  **
12  ** \brief Simplifications based on unconstrained variables
13  **
14  ** This module implements a preprocessing phase which replaces certain
15  ** "unconstrained" expressions by variables.  Based on Roberto
16  ** Bruttomesso's PhD thesis.
17  **/
18 
19 #include "preprocessing/passes/unconstrained_simplifier.h"
20 
21 #include "smt/smt_statistics_registry.h"
22 #include "theory/logic_info.h"
23 #include "theory/rewriter.h"
24 
25 namespace CVC4 {
26 namespace preprocessing {
27 namespace passes {
28 
29 using namespace CVC4::theory;
30 
UnconstrainedSimplifier(PreprocessingPassContext * preprocContext)31 UnconstrainedSimplifier::UnconstrainedSimplifier(
32     PreprocessingPassContext* preprocContext)
33     : PreprocessingPass(preprocContext, "unconstrained-simplifier"),
34       d_numUnconstrainedElim("preprocessor::number of unconstrained elims", 0),
35       d_context(preprocContext->getDecisionContext()),
36       d_substitutions(preprocContext->getDecisionContext()),
37       d_logicInfo(preprocContext->getLogicInfo())
38 {
39   smtStatisticsRegistry()->registerStat(&d_numUnconstrainedElim);
40 }
41 
~UnconstrainedSimplifier()42 UnconstrainedSimplifier::~UnconstrainedSimplifier()
43 {
44   smtStatisticsRegistry()->unregisterStat(&d_numUnconstrainedElim);
45 }
46 
47 struct unc_preprocess_stack_element
48 {
49   TNode node;
50   TNode parent;
unc_preprocess_stack_elementCVC4::preprocessing::passes::unc_preprocess_stack_element51   unc_preprocess_stack_element(TNode n) : node(n) {}
unc_preprocess_stack_elementCVC4::preprocessing::passes::unc_preprocess_stack_element52   unc_preprocess_stack_element(TNode n, TNode p) : node(n), parent(p) {}
53 }; /* struct unc_preprocess_stack_element */
54 
visitAll(TNode assertion)55 void UnconstrainedSimplifier::visitAll(TNode assertion)
56 {
57   // Do a topological sort of the subexpressions and substitute them
58   vector<unc_preprocess_stack_element> toVisit;
59   toVisit.push_back(assertion);
60 
61   while (!toVisit.empty())
62   {
63     // The current node we are processing
64     TNode current = toVisit.back().node;
65     TNode parent = toVisit.back().parent;
66     toVisit.pop_back();
67 
68     TNodeCountMap::iterator find = d_visited.find(current);
69     if (find != d_visited.end())
70     {
71       if (find->second == 1)
72       {
73         d_visitedOnce.erase(current);
74         if (current.isVar())
75         {
76           d_unconstrained.erase(current);
77         }
78       }
79       ++find->second;
80       continue;
81     }
82 
83     d_visited[current] = 1;
84     d_visitedOnce[current] = parent;
85 
86     if (current.getNumChildren() == 0)
87     {
88       if (current.getKind() == kind::VARIABLE
89           || current.getKind() == kind::SKOLEM)
90       {
91         d_unconstrained.insert(current);
92       }
93     }
94     else
95     {
96       for (TNode childNode : current)
97       {
98         toVisit.push_back(unc_preprocess_stack_element(childNode, current));
99       }
100     }
101   }
102 }
103 
newUnconstrainedVar(TypeNode t,TNode var)104 Node UnconstrainedSimplifier::newUnconstrainedVar(TypeNode t, TNode var)
105 {
106   Node n = NodeManager::currentNM()->mkSkolem(
107       "unconstrained",
108       t,
109       "a new var introduced because of unconstrained variable "
110           + var.toString());
111   return n;
112 }
113 
processUnconstrained()114 void UnconstrainedSimplifier::processUnconstrained()
115 {
116   NodeManager* nm = NodeManager::currentNM();
117 
118   vector<TNode> workList(d_unconstrained.begin(), d_unconstrained.end());
119   Node currentSub;
120   TNode parent;
121   bool swap;
122   bool isSigned;
123   bool strict;
124   vector<TNode> delayQueueLeft;
125   vector<Node> delayQueueRight;
126 
127   TNode current = workList.back();
128   workList.pop_back();
129   for (;;)
130   {
131     Assert(d_visitedOnce.find(current) != d_visitedOnce.end());
132     parent = d_visitedOnce[current];
133     if (!parent.isNull())
134     {
135       swap = isSigned = strict = false;
136       bool checkParent = false;
137       switch (parent.getKind())
138       {
139         // If-then-else operator - any two unconstrained children makes the
140         // parent unconstrained
141         case kind::ITE:
142         {
143           Assert(parent[0] == current || parent[1] == current
144                  || parent[2] == current);
145           bool uCond =
146               parent[0] == current
147               || d_unconstrained.find(parent[0]) != d_unconstrained.end();
148           bool uThen =
149               parent[1] == current
150               || d_unconstrained.find(parent[1]) != d_unconstrained.end();
151           bool uElse =
152               parent[2] == current
153               || d_unconstrained.find(parent[2]) != d_unconstrained.end();
154           if ((uCond && uThen) || (uCond && uElse) || (uThen && uElse))
155           {
156             if (d_unconstrained.find(parent) == d_unconstrained.end()
157                 && !d_substitutions.hasSubstitution(parent))
158             {
159               ++d_numUnconstrainedElim;
160               if (uThen)
161               {
162                 if (parent[1] != current)
163                 {
164                   if (parent[1].isVar())
165                   {
166                     currentSub = parent[1];
167                   }
168                   else
169                   {
170                     Assert(d_substitutions.hasSubstitution(parent[1]));
171                     currentSub = d_substitutions.apply(parent[1]);
172                   }
173                 }
174                 else if (currentSub.isNull())
175                 {
176                   currentSub = current;
177                 }
178               }
179               else if (parent[2] != current)
180               {
181                 if (parent[2].isVar())
182                 {
183                   currentSub = parent[2];
184                 }
185                 else
186                 {
187                   Assert(d_substitutions.hasSubstitution(parent[2]));
188                   currentSub = d_substitutions.apply(parent[2]);
189                 }
190               }
191               else if (currentSub.isNull())
192               {
193                 currentSub = current;
194               }
195               current = parent;
196             }
197             else
198             {
199               currentSub = Node();
200             }
201           }
202           else if (uCond)
203           {
204             Cardinality card = parent.getType().getCardinality();
205             if (card.isFinite() && !card.isLargeFinite()
206                 && card.getFiniteCardinality() == 2)
207             {
208               // Special case: condition is unconstrained, then and else are
209               // different, and total cardinality of the type is 2, then the
210               // result is unconstrained
211               Node test = Rewriter::rewrite(parent[1].eqNode(parent[2]));
212               if (test == nm->mkConst<bool>(false))
213               {
214                 ++d_numUnconstrainedElim;
215                 if (currentSub.isNull())
216                 {
217                   currentSub = current;
218                 }
219                 currentSub = newUnconstrainedVar(parent.getType(), currentSub);
220                 current = parent;
221               }
222             }
223           }
224           break;
225         }
226 
227         // Comparisons that return a different type - assuming domains are
228         // larger than 1, any unconstrained child makes parent unconstrained as
229         // well
230         case kind::EQUAL:
231           if (parent[0].getType() != parent[1].getType())
232           {
233             TNode other = (parent[0] == current) ? parent[1] : parent[0];
234             if (current.getType().isSubtypeOf(other.getType()))
235             {
236               break;
237             }
238           }
239           if (parent[0].getType().isDatatype())
240           {
241             TypeNode tn = parent[0].getType();
242             const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
243             if (dt.isRecursiveSingleton(tn.toType()))
244             {
245               // domain size may be 1
246               break;
247             }
248           }
249           if (parent[0].getType().isBoolean())
250           {
251             checkParent = true;
252             break;
253           }
254         case kind::BITVECTOR_COMP:
255         case kind::LT:
256         case kind::LEQ:
257         case kind::GT:
258         case kind::GEQ:
259         {
260           if (d_unconstrained.find(parent) == d_unconstrained.end()
261               && !d_substitutions.hasSubstitution(parent))
262           {
263             ++d_numUnconstrainedElim;
264             Assert(parent[0] != parent[1]
265                    && (parent[0] == current || parent[1] == current));
266             if (currentSub.isNull())
267             {
268               currentSub = current;
269             }
270             currentSub = newUnconstrainedVar(parent.getType(), currentSub);
271             current = parent;
272           }
273           else
274           {
275             currentSub = Node();
276           }
277           break;
278         }
279 
280         // Unary operators that propagate unconstrainedness
281         case kind::NOT:
282         case kind::BITVECTOR_NOT:
283         case kind::BITVECTOR_NEG:
284         case kind::UMINUS:
285           ++d_numUnconstrainedElim;
286           Assert(parent[0] == current);
287           if (currentSub.isNull())
288           {
289             currentSub = current;
290           }
291           current = parent;
292           break;
293 
294         // Unary operators that propagate unconstrainedness and return a
295         // different type
296         case kind::BITVECTOR_EXTRACT:
297           ++d_numUnconstrainedElim;
298           Assert(parent[0] == current);
299           if (currentSub.isNull())
300           {
301             currentSub = current;
302           }
303           currentSub = newUnconstrainedVar(parent.getType(), currentSub);
304           current = parent;
305           break;
306 
307         // Operators returning same type requiring all children to be
308         // unconstrained
309         case kind::AND:
310         case kind::OR:
311         case kind::IMPLIES:
312         case kind::BITVECTOR_AND:
313         case kind::BITVECTOR_OR:
314         case kind::BITVECTOR_NAND:
315         case kind::BITVECTOR_NOR:
316         {
317           bool allUnconstrained = true;
318           for (TNode child : parent)
319           {
320             if (d_unconstrained.find(child) == d_unconstrained.end())
321             {
322               allUnconstrained = false;
323               break;
324             }
325           }
326           if (allUnconstrained)
327           {
328             checkParent = true;
329           }
330         }
331         break;
332 
333         // Require all children to be unconstrained and different
334         case kind::BITVECTOR_SHL:
335         case kind::BITVECTOR_LSHR:
336         case kind::BITVECTOR_ASHR:
337         case kind::BITVECTOR_UDIV_TOTAL:
338         case kind::BITVECTOR_UREM_TOTAL:
339         case kind::BITVECTOR_SDIV:
340         case kind::BITVECTOR_SREM:
341         case kind::BITVECTOR_SMOD:
342         {
343           bool allUnconstrained = true;
344           bool allDifferent = true;
345           for (TNode::iterator child_it = parent.begin();
346                child_it != parent.end();
347                ++child_it)
348           {
349             if (d_unconstrained.find(*child_it) == d_unconstrained.end())
350             {
351               allUnconstrained = false;
352               break;
353             }
354             for (TNode::iterator child_it2 = child_it + 1;
355                  child_it2 != parent.end();
356                  ++child_it2)
357             {
358               if (*child_it == *child_it2)
359               {
360                 allDifferent = false;
361                 break;
362               }
363             }
364           }
365           if (allUnconstrained && allDifferent)
366           {
367             checkParent = true;
368           }
369           break;
370         }
371 
372         // Requires all children to be unconstrained and different, and returns
373         // a different type
374         case kind::BITVECTOR_CONCAT:
375         {
376           bool allUnconstrained = true;
377           bool allDifferent = true;
378           for (TNode::iterator child_it = parent.begin();
379                child_it != parent.end();
380                ++child_it)
381           {
382             if (d_unconstrained.find(*child_it) == d_unconstrained.end())
383             {
384               allUnconstrained = false;
385               break;
386             }
387             for (TNode::iterator child_it2 = child_it + 1;
388                  child_it2 != parent.end();
389                  ++child_it2)
390             {
391               if (*child_it == *child_it2)
392               {
393                 allDifferent = false;
394                 break;
395               }
396             }
397           }
398           if (allUnconstrained && allDifferent)
399           {
400             if (d_unconstrained.find(parent) == d_unconstrained.end()
401                 && !d_substitutions.hasSubstitution(parent))
402             {
403               ++d_numUnconstrainedElim;
404               if (currentSub.isNull())
405               {
406                 currentSub = current;
407               }
408               currentSub = newUnconstrainedVar(parent.getType(), currentSub);
409               current = parent;
410             }
411             else
412             {
413               currentSub = Node();
414             }
415           }
416         }
417         break;
418 
419         // N-ary operators returning same type requiring at least one child to
420         // be unconstrained
421         case kind::PLUS:
422         case kind::MINUS:
423           if (current.getType().isInteger() && !parent.getType().isInteger())
424           {
425             break;
426           }
427         case kind::XOR:
428         case kind::BITVECTOR_XOR:
429         case kind::BITVECTOR_XNOR:
430         case kind::BITVECTOR_PLUS:
431         case kind::BITVECTOR_SUB: checkParent = true; break;
432 
433         // Multiplication/division: must be non-integer and other operand must
434         // be non-zero
435         case kind::MULT:
436         case kind::DIVISION:
437         {
438           Assert(parent.getNumChildren() == 2);
439           TNode other;
440           if (parent[0] == current)
441           {
442             other = parent[1];
443           }
444           else
445           {
446             Assert(parent[1] == current);
447             other = parent[0];
448           }
449           if (d_unconstrained.find(other) != d_unconstrained.end())
450           {
451             if (d_unconstrained.find(parent) == d_unconstrained.end()
452                 && !d_substitutions.hasSubstitution(parent))
453             {
454               if (current.getType().isInteger() && other.getType().isInteger())
455               {
456                 Assert(parent.getKind() == kind::DIVISION
457                        || parent.getType().isInteger());
458                 if (parent.getKind() == kind::DIVISION)
459                 {
460                   break;
461                 }
462               }
463               ++d_numUnconstrainedElim;
464               if (currentSub.isNull())
465               {
466                 currentSub = current;
467               }
468               current = parent;
469             }
470             else
471             {
472               currentSub = Node();
473             }
474           }
475           else
476           {
477             // if only the denominator of a division is unconstrained, can't
478             // set it to 0 so the result is not unconstrained
479             if (parent.getKind() == kind::DIVISION && current == parent[1])
480             {
481               break;
482             }
483             // if we are an integer, the only way we are unconstrained is if
484             // we are a MULT by -1
485             if (current.getType().isInteger())
486             {
487               // div/mult by 1 should have been simplified
488               Assert(other != nm->mkConst<Rational>(1));
489               // div by -1 should have been simplified
490               if (other != nm->mkConst<Rational>(-1))
491               {
492                 break;
493               }
494               else
495               {
496                 Assert(parent.getKind() == kind::MULT);
497                 Assert(parent.getType().isInteger());
498               }
499             }
500             else
501             {
502               // TODO(#2377): could build ITE here
503               Node test = other.eqNode(nm->mkConst<Rational>(0));
504               if (Rewriter::rewrite(test) != nm->mkConst<bool>(false))
505               {
506                 break;
507               }
508             }
509             ++d_numUnconstrainedElim;
510             if (currentSub.isNull())
511             {
512               currentSub = current;
513             }
514             current = parent;
515           }
516           break;
517         }
518 
519         // Bitvector MULT - current must only appear once in the children:
520         // all other children must be unconstrained or odd
521         case kind::BITVECTOR_MULT:
522         {
523           bool found = false;
524           bool done = false;
525 
526           for (TNode child : parent)
527           {
528             if (child == current)
529             {
530               if (found)
531               {
532                 done = true;
533                 break;
534               }
535               found = true;
536               continue;
537             }
538             else if (d_unconstrained.find(child) == d_unconstrained.end())
539             {
540               Node extractOp =
541                   nm->mkConst<BitVectorExtract>(BitVectorExtract(0, 0));
542               vector<Node> children;
543               children.push_back(child);
544               Node test = nm->mkNode(extractOp, children);
545               BitVector one(1, unsigned(1));
546               test = test.eqNode(nm->mkConst<BitVector>(one));
547               if (Rewriter::rewrite(test) != nm->mkConst<bool>(true))
548               {
549                 done = true;
550                 break;
551               }
552             }
553           }
554           if (done)
555           {
556             break;
557           }
558           checkParent = true;
559           break;
560         }
561 
562         // Uninterpreted function - if domain is infinite, no quantifiers are
563         // used, and any child is unconstrained, result is unconstrained
564         case kind::APPLY_UF:
565           if (d_logicInfo.isQuantified()
566               || !current.getType().getCardinality().isInfinite())
567           {
568             break;
569           }
570           if (d_unconstrained.find(parent) == d_unconstrained.end()
571               && !d_substitutions.hasSubstitution(parent))
572           {
573             ++d_numUnconstrainedElim;
574             if (currentSub.isNull())
575             {
576               currentSub = current;
577             }
578             if (parent.getType() != current.getType())
579             {
580               currentSub = newUnconstrainedVar(parent.getType(), currentSub);
581             }
582             current = parent;
583           }
584           else
585           {
586             currentSub = Node();
587           }
588           break;
589 
590         // Array select - if array is unconstrained, so is result
591         case kind::SELECT:
592           if (parent[0] == current)
593           {
594             ++d_numUnconstrainedElim;
595             Assert(current.getType().isArray());
596             if (currentSub.isNull())
597             {
598               currentSub = current;
599             }
600             currentSub = newUnconstrainedVar(
601                 current.getType().getArrayConstituentType(), currentSub);
602             current = parent;
603           }
604           break;
605 
606         // Array store - if both store and value are unconstrained, so is
607         // resulting store
608         case kind::STORE:
609           if (((parent[0] == current
610                 && d_unconstrained.find(parent[2]) != d_unconstrained.end())
611                || (parent[2] == current
612                    && d_unconstrained.find(parent[0])
613                           != d_unconstrained.end())))
614           {
615             if (d_unconstrained.find(parent) == d_unconstrained.end()
616                 && !d_substitutions.hasSubstitution(parent))
617             {
618               ++d_numUnconstrainedElim;
619               if (parent[0] != current)
620               {
621                 if (parent[0].isVar())
622                 {
623                   currentSub = parent[0];
624                 }
625                 else
626                 {
627                   Assert(d_substitutions.hasSubstitution(parent[0]));
628                   currentSub = d_substitutions.apply(parent[0]);
629                 }
630               }
631               else if (currentSub.isNull())
632               {
633                 currentSub = current;
634               }
635               current = parent;
636             }
637             else
638             {
639               currentSub = Node();
640             }
641           }
642           break;
643 
644         // Bit-vector comparisons: replace with new Boolean variable, but have
645         // to also conjoin with a side condition as there is always one case
646         // when the comparison is forced to be false
647         case kind::BITVECTOR_ULT:
648         case kind::BITVECTOR_UGE:
649         case kind::BITVECTOR_UGT:
650         case kind::BITVECTOR_ULE:
651         case kind::BITVECTOR_SLT:
652         case kind::BITVECTOR_SGE:
653         case kind::BITVECTOR_SGT:
654         case kind::BITVECTOR_SLE:
655         {
656           // Tuples over (signed, swap, strict).
657           switch (parent.getKind())
658           {
659             case kind::BITVECTOR_UGE: break;
660             case kind::BITVECTOR_ULT: strict = true; break;
661             case kind::BITVECTOR_ULE: swap = true; break;
662             case kind::BITVECTOR_UGT:
663               swap = true;
664               strict = true;
665               break;
666             case kind::BITVECTOR_SGE: isSigned = true; break;
667             case kind::BITVECTOR_SLT:
668               isSigned = true;
669               strict = true;
670               break;
671             case kind::BITVECTOR_SLE:
672               isSigned = true;
673               swap = true;
674               break;
675             case kind::BITVECTOR_SGT:
676               isSigned = true;
677               swap = true;
678               strict = true;
679               break;
680             default: Unreachable();
681           }
682           TNode other;
683           bool left = false;
684           if (parent[0] == current)
685           {
686             other = parent[1];
687             left = true;
688           }
689           else
690           {
691             Assert(parent[1] == current);
692             other = parent[0];
693           }
694           if (d_unconstrained.find(other) != d_unconstrained.end())
695           {
696             if (d_unconstrained.find(parent) == d_unconstrained.end()
697                 && !d_substitutions.hasSubstitution(parent))
698             {
699               ++d_numUnconstrainedElim;
700               if (currentSub.isNull())
701               {
702                 currentSub = current;
703               }
704               currentSub = newUnconstrainedVar(parent.getType(), currentSub);
705               current = parent;
706             }
707             else
708             {
709               currentSub = Node();
710             }
711           }
712           else
713           {
714             unsigned size = current.getType().getBitVectorSize();
715             BitVector bv =
716                 isSigned ? BitVector(size, Integer(1).multiplyByPow2(size - 1))
717                          : BitVector(size, unsigned(0));
718             if (swap == left)
719             {
720               bv = ~bv;
721             }
722             if (currentSub.isNull())
723             {
724               currentSub = current;
725             }
726             currentSub = newUnconstrainedVar(parent.getType(), currentSub);
727             current = parent;
728             Node test =
729                 Rewriter::rewrite(other.eqNode(nm->mkConst<BitVector>(bv)));
730             if (test == nm->mkConst<bool>(false))
731             {
732               break;
733             }
734             currentSub = strict ? currentSub.andNode(test.notNode())
735                                 : currentSub.orNode(test);
736             // Delay adding this substitution - see comment at end of function
737             delayQueueLeft.push_back(current);
738             delayQueueRight.push_back(currentSub);
739             currentSub = Node();
740             parent = TNode();
741           }
742           break;
743         }
744 
745         // Do nothing
746         case kind::BITVECTOR_SIGN_EXTEND:
747         case kind::BITVECTOR_ZERO_EXTEND:
748         case kind::BITVECTOR_REPEAT:
749         case kind::BITVECTOR_ROTATE_LEFT:
750         case kind::BITVECTOR_ROTATE_RIGHT:
751 
752         default: break;
753       }
754       if (checkParent)
755       {
756         // run for various cases from above
757         if (d_unconstrained.find(parent) == d_unconstrained.end()
758             && !d_substitutions.hasSubstitution(parent))
759         {
760           ++d_numUnconstrainedElim;
761           if (currentSub.isNull())
762           {
763             currentSub = current;
764           }
765           current = parent;
766         }
767         else
768         {
769           currentSub = Node();
770         }
771       }
772       if (current == parent && d_visited[parent] == 1)
773       {
774         d_unconstrained.insert(parent);
775         continue;
776       }
777     }
778     if (!currentSub.isNull())
779     {
780       Assert(currentSub.isVar());
781       d_substitutions.addSubstitution(current, currentSub, false);
782     }
783     if (workList.empty())
784     {
785       break;
786     }
787     current = workList.back();
788     currentSub = Node();
789     workList.pop_back();
790   }
791   TNode left;
792   Node right;
793   // All substitutions except those arising from bitvector comparisons are
794   // substitutions t -> x where x is a variable.  This allows us to build the
795   // substitution very quickly (never invalidating the substitution cache).
796   // Bitvector comparisons are more complicated and may require
797   // back-substitution and cache-invalidation.  So we do these last.
798   while (!delayQueueLeft.empty())
799   {
800     left = delayQueueLeft.back();
801     if (!d_substitutions.hasSubstitution(left))
802     {
803       right = d_substitutions.apply(delayQueueRight.back());
804       d_substitutions.addSubstitution(delayQueueLeft.back(), right);
805     }
806     delayQueueLeft.pop_back();
807     delayQueueRight.pop_back();
808   }
809 }
810 
applyInternal(AssertionPipeline * assertionsToPreprocess)811 PreprocessingPassResult UnconstrainedSimplifier::applyInternal(
812     AssertionPipeline* assertionsToPreprocess)
813 {
814   d_preprocContext->spendResource(options::preprocessStep());
815 
816   std::vector<Node>& assertions = assertionsToPreprocess->ref();
817 
818   d_context->push();
819 
820   for (const Node& assertion : assertions)
821   {
822     visitAll(assertion);
823   }
824 
825   if (!d_unconstrained.empty())
826   {
827     processUnconstrained();
828     //    d_substitutions.print(Message.getStream());
829     for (Node& assertion : assertions)
830     {
831       assertion = Rewriter::rewrite(d_substitutions.apply(assertion));
832     }
833   }
834 
835   // to clear substitutions map
836   d_context->pop();
837 
838   d_visited.clear();
839   d_visitedOnce.clear();
840   d_unconstrained.clear();
841 
842   return PreprocessingPassResult::NO_CONFLICT;
843 }
844 
845 
846 }  // namespace passes
847 }  // namespace preprocessing
848 }  // namespace CVC4
849