1 /******************************************************************************
2 * Top contributors (to current version):
3 * Mudathir Mohamed, Gereon Kremer, Aina Niemetz
5 * This file is part of the cvc5 project.
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 * ****************************************************************************
13 * Bags theory rewriter.
16 #include "theory/bags/bags_rewriter.h"
18 #include "expr/emptybag.h"
19 #include "theory/bags/normal_form.h"
20 #include "util/rational.h"
21 #include "util/statistics_registry.h"
23 using namespace cvc5::kind
;
29 BagsRewriteResponse::BagsRewriteResponse()
30 : d_node(Node::null()), d_rewrite(Rewrite::NONE
)
34 BagsRewriteResponse::BagsRewriteResponse(Node n
, Rewrite rewrite
)
35 : d_node(n
), d_rewrite(rewrite
)
39 BagsRewriteResponse::BagsRewriteResponse(const BagsRewriteResponse
& r
)
40 : d_node(r
.d_node
), d_rewrite(r
.d_rewrite
)
44 BagsRewriter::BagsRewriter(HistogramStat
<Rewrite
>* statistics
)
45 : d_statistics(statistics
)
47 d_nm
= NodeManager::currentNM();
48 d_zero
= d_nm
->mkConst(CONST_RATIONAL
, Rational(0));
49 d_one
= d_nm
->mkConst(CONST_RATIONAL
, Rational(1));
52 RewriteResponse
BagsRewriter::postRewrite(TNode n
)
54 BagsRewriteResponse response
;
57 // no need to rewrite n if it is already in a normal form
58 response
= BagsRewriteResponse(n
, Rewrite::NONE
);
60 else if (n
.getKind() == EQUAL
)
62 response
= postRewriteEqual(n
);
64 else if (n
.getKind() == BAG_CHOOSE
)
66 response
= rewriteChoose(n
);
68 else if (NormalForm::areChildrenConstants(n
))
70 Node value
= NormalForm::evaluate(n
);
71 response
= BagsRewriteResponse(value
, Rewrite::CONSTANT_EVALUATION
);
78 case BAG_MAKE
: response
= rewriteMakeBag(n
); break;
79 case BAG_COUNT
: response
= rewriteBagCount(n
); break;
80 case BAG_DUPLICATE_REMOVAL
: response
= rewriteDuplicateRemoval(n
); break;
81 case BAG_UNION_MAX
: response
= rewriteUnionMax(n
); break;
82 case BAG_UNION_DISJOINT
: response
= rewriteUnionDisjoint(n
); break;
83 case BAG_INTER_MIN
: response
= rewriteIntersectionMin(n
); break;
84 case BAG_DIFFERENCE_SUBTRACT
:
85 response
= rewriteDifferenceSubtract(n
);
87 case BAG_DIFFERENCE_REMOVE
: response
= rewriteDifferenceRemove(n
); break;
88 case BAG_CARD
: response
= rewriteCard(n
); break;
89 case BAG_IS_SINGLETON
: response
= rewriteIsSingleton(n
); break;
90 case BAG_FROM_SET
: response
= rewriteFromSet(n
); break;
91 case BAG_TO_SET
: response
= rewriteToSet(n
); break;
92 case BAG_MAP
: response
= postRewriteMap(n
); break;
93 default: response
= BagsRewriteResponse(n
, Rewrite::NONE
); break;
97 Trace("bags-rewrite") << "postRewrite " << n
<< " to " << response
.d_node
98 << " by " << response
.d_rewrite
<< "." << std::endl
;
100 if (d_statistics
!= nullptr)
102 (*d_statistics
) << response
.d_rewrite
;
104 if (response
.d_node
!= n
)
106 return RewriteResponse(RewriteStatus::REWRITE_AGAIN_FULL
, response
.d_node
);
108 return RewriteResponse(RewriteStatus::REWRITE_DONE
, n
);
111 RewriteResponse
BagsRewriter::preRewrite(TNode n
)
113 BagsRewriteResponse response
;
114 Kind k
= n
.getKind();
117 case EQUAL
: response
= preRewriteEqual(n
); break;
118 case BAG_SUBBAG
: response
= rewriteSubBag(n
); break;
119 default: response
= BagsRewriteResponse(n
, Rewrite::NONE
);
122 Trace("bags-rewrite") << "preRewrite " << n
<< " to " << response
.d_node
123 << " by " << response
.d_rewrite
<< "." << std::endl
;
125 if (d_statistics
!= nullptr)
127 (*d_statistics
) << response
.d_rewrite
;
129 if (response
.d_node
!= n
)
131 return RewriteResponse(RewriteStatus::REWRITE_AGAIN_FULL
, response
.d_node
);
133 return RewriteResponse(RewriteStatus::REWRITE_DONE
, n
);
136 BagsRewriteResponse
BagsRewriter::preRewriteEqual(const TNode
& n
) const
138 Assert(n
.getKind() == EQUAL
);
141 // (= A A) = true where A is a bag
142 return BagsRewriteResponse(d_nm
->mkConst(true), Rewrite::IDENTICAL_NODES
);
144 return BagsRewriteResponse(n
, Rewrite::NONE
);
147 BagsRewriteResponse
BagsRewriter::rewriteSubBag(const TNode
& n
) const
149 Assert(n
.getKind() == BAG_SUBBAG
);
151 // (bag.subbag A B) = ((bag.difference_subtract A B) == bag.empty)
152 Node emptybag
= d_nm
->mkConst(EmptyBag(n
[0].getType()));
153 Node subtract
= d_nm
->mkNode(BAG_DIFFERENCE_SUBTRACT
, n
[0], n
[1]);
154 Node equal
= subtract
.eqNode(emptybag
);
155 return BagsRewriteResponse(equal
, Rewrite::SUB_BAG
);
158 BagsRewriteResponse
BagsRewriter::rewriteMakeBag(const TNode
& n
) const
160 Assert(n
.getKind() == BAG_MAKE
);
161 // return bag.empty for negative or zero multiplicity
162 if (n
[1].isConst() && n
[1].getConst
<Rational
>().sgn() != 1)
164 // (bag x c) = bag.empty where c <= 0
165 Node emptybag
= d_nm
->mkConst(EmptyBag(n
.getType()));
166 return BagsRewriteResponse(emptybag
, Rewrite::BAG_MAKE_COUNT_NEGATIVE
);
168 return BagsRewriteResponse(n
, Rewrite::NONE
);
171 BagsRewriteResponse
BagsRewriter::rewriteBagCount(const TNode
& n
) const
173 Assert(n
.getKind() == BAG_COUNT
);
174 if (n
[1].isConst() && n
[1].getKind() == BAG_EMPTY
)
176 // (bag.count x bag.empty) = 0
177 return BagsRewriteResponse(d_zero
, Rewrite::COUNT_EMPTY
);
179 if (n
[1].getKind() == BAG_MAKE
&& n
[0] == n
[1][0])
181 // (bag.count x (bag x c)) = (ite (>= c 1) c 0)
183 Node geq
= d_nm
->mkNode(GEQ
, c
, d_one
);
184 Node ite
= d_nm
->mkNode(ITE
, geq
, c
, d_zero
);
185 return BagsRewriteResponse(ite
, Rewrite::COUNT_BAG_MAKE
);
187 return BagsRewriteResponse(n
, Rewrite::NONE
);
190 BagsRewriteResponse
BagsRewriter::rewriteDuplicateRemoval(const TNode
& n
) const
192 Assert(n
.getKind() == BAG_DUPLICATE_REMOVAL
);
193 if (n
[0].getKind() == BAG_MAKE
&& n
[0][1].isConst()
194 && n
[0][1].getConst
<Rational
>().sgn() == 1)
196 // (bag.duplicate_removal (bag x n)) = (bag x 1)
197 // where n is a positive constant
198 Node bag
= d_nm
->mkBag(n
[0][0].getType(), n
[0][0], d_one
);
199 return BagsRewriteResponse(bag
, Rewrite::DUPLICATE_REMOVAL_BAG_MAKE
);
201 return BagsRewriteResponse(n
, Rewrite::NONE
);
204 BagsRewriteResponse
BagsRewriter::rewriteUnionMax(const TNode
& n
) const
206 Assert(n
.getKind() == BAG_UNION_MAX
);
207 if (n
[1].getKind() == BAG_EMPTY
|| n
[0] == n
[1])
209 // (bag.union_max A A) = A
210 // (bag.union_max A bag.empty) = A
211 return BagsRewriteResponse(n
[0], Rewrite::UNION_MAX_SAME_OR_EMPTY
);
213 if (n
[0].getKind() == BAG_EMPTY
)
215 // (bag.union_max bag.empty A) = A
216 return BagsRewriteResponse(n
[1], Rewrite::UNION_MAX_EMPTY
);
219 if ((n
[1].getKind() == BAG_UNION_MAX
|| n
[1].getKind() == BAG_UNION_DISJOINT
)
220 && (n
[0] == n
[1][0] || n
[0] == n
[1][1]))
222 // (bag.union_max A (bag.union_max A B)) = (bag.union_max A B)
223 // (bag.union_max A (bag.union_max B A)) = (bag.union_max B A)
224 // (bag.union_max A (bag.union_disjoint A B)) = (bag.union_disjoint A B)
225 // (bag.union_max A (bag.union_disjoint B A)) = (bag.union_disjoint B A)
226 return BagsRewriteResponse(n
[1], Rewrite::UNION_MAX_UNION_LEFT
);
229 if ((n
[0].getKind() == BAG_UNION_MAX
|| n
[0].getKind() == BAG_UNION_DISJOINT
)
230 && (n
[0][0] == n
[1] || n
[0][1] == n
[1]))
232 // (bag.union_max (bag.union_max A B) A)) = (bag.union_max A B)
233 // (bag.union_max (bag.union_max B A) A)) = (bag.union_max B A)
234 // (bag.union_max (bag.union_disjoint A B) A)) = (bag.union_disjoint A B)
235 // (bag.union_max (bag.union_disjoint B A) A)) = (bag.union_disjoint B A)
236 return BagsRewriteResponse(n
[0], Rewrite::UNION_MAX_UNION_RIGHT
);
238 return BagsRewriteResponse(n
, Rewrite::NONE
);
241 BagsRewriteResponse
BagsRewriter::rewriteUnionDisjoint(const TNode
& n
) const
243 Assert(n
.getKind() == BAG_UNION_DISJOINT
);
244 if (n
[1].getKind() == BAG_EMPTY
)
246 // (bag.union_disjoint A bag.empty) = A
247 return BagsRewriteResponse(n
[0], Rewrite::UNION_DISJOINT_EMPTY_RIGHT
);
249 if (n
[0].getKind() == BAG_EMPTY
)
251 // (bag.union_disjoint bag.empty A) = A
252 return BagsRewriteResponse(n
[1], Rewrite::UNION_DISJOINT_EMPTY_LEFT
);
254 if ((n
[0].getKind() == BAG_UNION_MAX
&& n
[1].getKind() == BAG_INTER_MIN
)
255 || (n
[1].getKind() == BAG_UNION_MAX
&& n
[0].getKind() == BAG_INTER_MIN
))
258 // (bag.union_disjoint (bag.union_max A B) (bag.inter_min A B)) =
259 // (bag.union_disjoint A B) // sum(a,b) = max(a,b) + min(a,b)
260 // check if the operands of bag.union_max and bag.inter_min are the
262 std::set
<Node
> left(n
[0].begin(), n
[0].end());
263 std::set
<Node
> right(n
[1].begin(), n
[1].end());
266 Node rewritten
= d_nm
->mkNode(BAG_UNION_DISJOINT
, n
[0][0], n
[0][1]);
267 return BagsRewriteResponse(rewritten
, Rewrite::UNION_DISJOINT_MAX_MIN
);
270 return BagsRewriteResponse(n
, Rewrite::NONE
);
273 BagsRewriteResponse
BagsRewriter::rewriteIntersectionMin(const TNode
& n
) const
275 Assert(n
.getKind() == BAG_INTER_MIN
);
276 if (n
[0].getKind() == BAG_EMPTY
)
278 // (bag.inter_min bag.empty A) = bag.empty
279 return BagsRewriteResponse(n
[0], Rewrite::INTERSECTION_EMPTY_LEFT
);
281 if (n
[1].getKind() == BAG_EMPTY
)
283 // (bag.inter_min A bag.empty) = bag.empty
284 return BagsRewriteResponse(n
[1], Rewrite::INTERSECTION_EMPTY_RIGHT
);
288 // (bag.inter_min A A) = A
289 return BagsRewriteResponse(n
[0], Rewrite::INTERSECTION_SAME
);
291 if (n
[1].getKind() == BAG_UNION_DISJOINT
|| n
[1].getKind() == BAG_UNION_MAX
)
293 if (n
[0] == n
[1][0] || n
[0] == n
[1][1])
295 // (bag.inter_min A (bag.union_disjoint A B)) = A
296 // (bag.inter_min A (bag.union_disjoint B A)) = A
297 // (bag.inter_min A (bag.union_max A B)) = A
298 // (bag.inter_min A (bag.union_max B A)) = A
299 return BagsRewriteResponse(n
[0], Rewrite::INTERSECTION_SHARED_LEFT
);
303 if (n
[0].getKind() == BAG_UNION_DISJOINT
|| n
[0].getKind() == BAG_UNION_MAX
)
305 if (n
[1] == n
[0][0] || n
[1] == n
[0][1])
307 // (bag.inter_min (bag.union_disjoint A B) A) = A
308 // (bag.inter_min (bag.union_disjoint B A) A) = A
309 // (bag.inter_min (bag.union_max A B) A) = A
310 // (bag.inter_min (bag.union_max B A) A) = A
311 return BagsRewriteResponse(n
[1], Rewrite::INTERSECTION_SHARED_RIGHT
);
315 return BagsRewriteResponse(n
, Rewrite::NONE
);
318 BagsRewriteResponse
BagsRewriter::rewriteDifferenceSubtract(
319 const TNode
& n
) const
321 Assert(n
.getKind() == BAG_DIFFERENCE_SUBTRACT
);
322 if (n
[0].getKind() == BAG_EMPTY
|| n
[1].getKind() == BAG_EMPTY
)
324 // (bag.difference_subtract A bag.empty) = A
325 // (bag.difference_subtract bag.empty A) = bag.empty
326 return BagsRewriteResponse(n
[0], Rewrite::SUBTRACT_RETURN_LEFT
);
330 // (bag.difference_subtract A A) = bag.empty
331 Node emptyBag
= d_nm
->mkConst(EmptyBag(n
.getType()));
332 return BagsRewriteResponse(emptyBag
, Rewrite::SUBTRACT_SAME
);
335 if (n
[0].getKind() == BAG_UNION_DISJOINT
)
339 // (bag.difference_subtract (bag.union_disjoint A B) A) = B
340 return BagsRewriteResponse(n
[0][1],
341 Rewrite::SUBTRACT_DISJOINT_SHARED_LEFT
);
345 // (bag.difference_subtract (bag.union_disjoint B A) A) = B
346 return BagsRewriteResponse(n
[0][0],
347 Rewrite::SUBTRACT_DISJOINT_SHARED_RIGHT
);
351 if (n
[1].getKind() == BAG_UNION_DISJOINT
|| n
[1].getKind() == BAG_UNION_MAX
)
353 if (n
[0] == n
[1][0] || n
[0] == n
[1][1])
355 // (bag.difference_subtract A (bag.union_disjoint A B)) = bag.empty
356 // (bag.difference_subtract A (bag.union_disjoint B A)) = bag.empty
357 // (bag.difference_subtract A (bag.union_max A B)) = bag.empty
358 // (bag.difference_subtract A (bag.union_max B A)) = bag.empty
359 Node emptyBag
= d_nm
->mkConst(EmptyBag(n
.getType()));
360 return BagsRewriteResponse(emptyBag
, Rewrite::SUBTRACT_FROM_UNION
);
364 if (n
[0].getKind() == BAG_INTER_MIN
)
366 if (n
[1] == n
[0][0] || n
[1] == n
[0][1])
368 // (bag.difference_subtract (bag.inter_min A B) A) = bag.empty
369 // (bag.difference_subtract (bag.inter_min B A) A) = bag.empty
370 Node emptyBag
= d_nm
->mkConst(EmptyBag(n
.getType()));
371 return BagsRewriteResponse(emptyBag
, Rewrite::SUBTRACT_MIN
);
375 return BagsRewriteResponse(n
, Rewrite::NONE
);
378 BagsRewriteResponse
BagsRewriter::rewriteDifferenceRemove(const TNode
& n
) const
380 Assert(n
.getKind() == BAG_DIFFERENCE_REMOVE
);
382 if (n
[0].getKind() == BAG_EMPTY
|| n
[1].getKind() == BAG_EMPTY
)
384 // (bag.difference_remove A bag.empty) = A
385 // (bag.difference_remove bag.empty B) = bag.empty
386 return BagsRewriteResponse(n
[0], Rewrite::REMOVE_RETURN_LEFT
);
391 // (bag.difference_remove A A) = bag.empty
392 Node emptyBag
= d_nm
->mkConst(EmptyBag(n
.getType()));
393 return BagsRewriteResponse(emptyBag
, Rewrite::REMOVE_SAME
);
396 if (n
[1].getKind() == BAG_UNION_DISJOINT
|| n
[1].getKind() == BAG_UNION_MAX
)
398 if (n
[0] == n
[1][0] || n
[0] == n
[1][1])
400 // (bag.difference_remove A (bag.union_disjoint A B)) = bag.empty
401 // (bag.difference_remove A (bag.union_disjoint B A)) = bag.empty
402 // (bag.difference_remove A (bag.union_max A B)) = bag.empty
403 // (bag.difference_remove A (bag.union_max B A)) = bag.empty
404 Node emptyBag
= d_nm
->mkConst(EmptyBag(n
.getType()));
405 return BagsRewriteResponse(emptyBag
, Rewrite::REMOVE_FROM_UNION
);
409 if (n
[0].getKind() == BAG_INTER_MIN
)
411 if (n
[1] == n
[0][0] || n
[1] == n
[0][1])
413 // (bag.difference_remove (bag.inter_min A B) A) = bag.empty
414 // (bag.difference_remove (bag.inter_min B A) A) = bag.empty
415 Node emptyBag
= d_nm
->mkConst(EmptyBag(n
.getType()));
416 return BagsRewriteResponse(emptyBag
, Rewrite::REMOVE_MIN
);
420 return BagsRewriteResponse(n
, Rewrite::NONE
);
423 BagsRewriteResponse
BagsRewriter::rewriteChoose(const TNode
& n
) const
425 Assert(n
.getKind() == BAG_CHOOSE
);
426 if (n
[0].getKind() == BAG_MAKE
&& n
[0][1].isConst()
427 && n
[0][1].getConst
<Rational
>() > 0)
429 // (bag.choose (bag x c)) = x where c is a constant > 0
430 return BagsRewriteResponse(n
[0][0], Rewrite::CHOOSE_BAG_MAKE
);
432 return BagsRewriteResponse(n
, Rewrite::NONE
);
435 BagsRewriteResponse
BagsRewriter::rewriteCard(const TNode
& n
) const
437 Assert(n
.getKind() == BAG_CARD
);
438 if (n
[0].getKind() == BAG_MAKE
&& n
[0][1].isConst())
440 // (bag.card (bag x c)) = c where c is a constant > 0
441 return BagsRewriteResponse(n
[0][1], Rewrite::CARD_BAG_MAKE
);
444 if (n
[0].getKind() == BAG_UNION_DISJOINT
)
446 // (bag.card (bag.union-disjoint A B)) = (+ (bag.card A) (bag.card B))
447 Node A
= d_nm
->mkNode(BAG_CARD
, n
[0][0]);
448 Node B
= d_nm
->mkNode(BAG_CARD
, n
[0][1]);
449 Node plus
= d_nm
->mkNode(PLUS
, A
, B
);
450 return BagsRewriteResponse(plus
, Rewrite::CARD_DISJOINT
);
452 return BagsRewriteResponse(n
, Rewrite::NONE
);
455 BagsRewriteResponse
BagsRewriter::rewriteIsSingleton(const TNode
& n
) const
457 Assert(n
.getKind() == BAG_IS_SINGLETON
);
458 if (n
[0].getKind() == BAG_MAKE
)
460 // (bag.is_singleton (bag x c)) = (c == 1)
461 Node equal
= n
[0][1].eqNode(d_one
);
462 return BagsRewriteResponse(equal
, Rewrite::IS_SINGLETON_BAG_MAKE
);
464 return BagsRewriteResponse(n
, Rewrite::NONE
);
467 BagsRewriteResponse
BagsRewriter::rewriteFromSet(const TNode
& n
) const
469 Assert(n
.getKind() == BAG_FROM_SET
);
470 if (n
[0].getKind() == SET_SINGLETON
)
472 // (bag.from_set (set.singleton (SetSingletonOp Int) x)) = (bag x 1)
473 TypeNode type
= n
[0].getType().getSetElementType();
474 Node bag
= d_nm
->mkBag(type
, n
[0][0], d_one
);
475 return BagsRewriteResponse(bag
, Rewrite::FROM_SINGLETON
);
477 return BagsRewriteResponse(n
, Rewrite::NONE
);
480 BagsRewriteResponse
BagsRewriter::rewriteToSet(const TNode
& n
) const
482 Assert(n
.getKind() == BAG_TO_SET
);
483 if (n
[0].getKind() == BAG_MAKE
&& n
[0][1].isConst()
484 && n
[0][1].getConst
<Rational
>().sgn() == 1)
486 // (bag.to_set (bag x n)) = (set.singleton (SetSingletonOp T) x)
487 // where n is a positive constant and T is the type of the bag's elements
488 Node set
= d_nm
->mkSingleton(n
[0][0].getType(), n
[0][0]);
489 return BagsRewriteResponse(set
, Rewrite::TO_SINGLETON
);
491 return BagsRewriteResponse(n
, Rewrite::NONE
);
494 BagsRewriteResponse
BagsRewriter::postRewriteEqual(const TNode
& n
) const
496 Assert(n
.getKind() == kind::EQUAL
);
499 Node ret
= d_nm
->mkConst(true);
500 return BagsRewriteResponse(ret
, Rewrite::EQ_REFL
);
503 if (n
[0].isConst() && n
[1].isConst())
505 Node ret
= d_nm
->mkConst(false);
506 return BagsRewriteResponse(ret
, Rewrite::EQ_CONST_FALSE
);
512 Node ret
= d_nm
->mkNode(kind::EQUAL
, n
[1], n
[0]);
513 return BagsRewriteResponse(ret
, Rewrite::EQ_SYM
);
515 return BagsRewriteResponse(n
, Rewrite::NONE
);
518 BagsRewriteResponse
BagsRewriter::postRewriteMap(const TNode
& n
) const
520 Assert(n
.getKind() == kind::BAG_MAP
);
523 // (bag.map f (as bag.empty (Bag T1)) = (as bag.empty (Bag T2))
524 // (bag.map f (bag "a" 3)) = (bag (f "a") 3)
525 std::map
<Node
, Rational
> elements
= NormalForm::getBagElements(n
[1]);
526 std::map
<Node
, Rational
> mappedElements
;
527 std::map
<Node
, Rational
>::iterator it
= elements
.begin();
528 while (it
!= elements
.end())
530 Node mappedElement
= d_nm
->mkNode(APPLY_UF
, n
[0], it
->first
);
531 mappedElements
[mappedElement
] = it
->second
;
534 TypeNode t
= d_nm
->mkBagType(n
[0].getType().getRangeType());
535 Node ret
= NormalForm::constructConstantBagFromElements(t
, mappedElements
);
536 return BagsRewriteResponse(ret
, Rewrite::MAP_CONST
);
538 Kind k
= n
[1].getKind();
543 // (bag.map f (bag x y)) = (bag (apply f x) y)
544 Node mappedElement
= d_nm
->mkNode(APPLY_UF
, n
[0], n
[1][0]);
546 d_nm
->mkBag(n
[0].getType().getRangeType(), mappedElement
, n
[1][1]);
547 return BagsRewriteResponse(ret
, Rewrite::MAP_BAG_MAKE
);
550 case BAG_UNION_DISJOINT
:
552 // (bag.map f (bag.union_disjoint A B)) =
553 // (bag.union_disjoint (bag.map f A) (bag.map f B))
554 Node a
= d_nm
->mkNode(BAG_MAP
, n
[0], n
[1][0]);
555 Node b
= d_nm
->mkNode(BAG_MAP
, n
[0], n
[1][1]);
556 Node ret
= d_nm
->mkNode(BAG_UNION_DISJOINT
, a
, b
);
557 return BagsRewriteResponse(ret
, Rewrite::MAP_UNION_DISJOINT
);
560 default: return BagsRewriteResponse(n
, Rewrite::NONE
);
564 } // namespace theory