Lattice Land Core Library
Loading...
Searching...
No Matches
algorithm.hpp
Go to the documentation of this file.
1// Copyright 2022 Pierre Talbot
2
3#ifndef LALA_CORE_ALGORITHM_HPP
4#define LALA_CORE_ALGORITHM_HPP
5
6#include "ast.hpp"
7
8namespace lala {
9
10/** \return `true` if the formula `f` has the shape `variable <sig> constant`. */
11template<class Allocator, class ExtendedSig>
14 return f.is(F::Seq)
15 && f.sig() == sig
16 && (f.seq(0).is(F::LV) || f.seq(0).is(F::V))
17 && f.seq(1).is_constant();
18}
19
20/** \return `true` if the formula `f` has the shape `variable op integer constant`, e.g., `x < 4`. */
21template<class Allocator, class ExtendedSig>
22CUDA NI bool is_v_op_z(const TFormula<Allocator, ExtendedSig>& f, Sig sig) {
24 return f.is(F::Seq)
25 && f.sig() == sig
26 && (f.seq(0).is(F::LV) || f.seq(0).is(F::V))
27 && f.seq(1).is(F::Z);
28}
29
30/** \return `true` if the formula `f` has the shape `variable = variable` or `variable <=> variable`. */
31template<class Allocator, class ExtendedSig>
34 return f.is(F::Seq)
35 && (f.sig() == EQ || f.sig() == EQUIV)
36 && (f.seq(0).is(F::LV) || f.seq(0).is(F::V))
37 && (f.seq(1).is(F::LV) || f.seq(1).is(F::V));
38}
39
40template<class Allocator>
41CUDA NI TFormula<Allocator> make_v_op_z(LVar<Allocator> v, Sig sig, logic_int z, AType aty = UNTYPED, const Allocator& allocator = Allocator()) {
42 using F = TFormula<Allocator>;
43 return F::make_binary(F::make_lvar(UNTYPED, std::move(v)), sig, F::make_z(z), aty, allocator);
44}
45
46template<class Allocator>
47CUDA NI TFormula<Allocator> make_v_op_z(AVar v, Sig sig, logic_int z, AType aty = UNTYPED, const Allocator& allocator = Allocator()) {
48 using F = TFormula<Allocator>;
49 return F::make_binary(F::make_avar(v), sig, F::make_z(z), aty, allocator);
50}
51
52template <class Allocator>
54 using F = TFormula<Allocator>;
55 assert(f.is(F::S) || f.is(F::Z) || f.is(F::R));
56 return f.is(F::S) ? SUPSETEQ : GEQ;
57}
58
59template <class Allocator>
61 using F = TFormula<Allocator>;
62 assert(f.is(F::S) || f.is(F::Z) || f.is(F::R));
63 return f.is(F::S) ? SUBSETEQ : LEQ;
64}
65
66namespace impl {
67 template<class Allocator, class ExtendedSig>
68 CUDA NI const TFormula<Allocator, ExtendedSig>& var_in_impl(const TFormula<Allocator, ExtendedSig>& f, bool& found);
69
70 template<size_t n, class Allocator, class ExtendedSig>
71 CUDA NI const TFormula<Allocator, ExtendedSig>& find_var_in_seq(const TFormula<Allocator, ExtendedSig>& f, bool& found) {
72 const auto& children = battery::get<1>(battery::get<n>(f.data()));
73 for(int i = 0; i < children.size(); ++i) {
74 const auto& subformula = var_in_impl(children[i], found);
75 if(found) {
76 return subformula;
77 }
78 }
79 return f;
80 }
81
82 template<class Allocator, class ExtendedSig>
83 CUDA NI const TFormula<Allocator, ExtendedSig>& var_in_impl(const TFormula<Allocator, ExtendedSig>& f, bool& found)
84 {
85 using F = TFormula<Allocator, ExtendedSig>;
86 switch(f.index()) {
87 case F::Z:
88 case F::R:
89 case F::S:
90 return f;
91 case F::V:
92 case F::E:
93 case F::LV:
94 found = true;
95 return f;
96 case F::Seq: return find_var_in_seq<F::Seq>(f, found);
97 case F::ESeq: return find_var_in_seq<F::ESeq>(f, found);
98 default: printf("var_in: formula not handled.\n"); assert(false); return f;
99 }
100 }
101
102 template<size_t n, class F>
103 CUDA NI int num_vars_in_seq(const F& f) {
104 const auto& children = battery::get<1>(battery::get<n>(f.data()));
105 int total = 0;
106 for(int i = 0; i < children.size(); ++i) {
107 total += num_vars(children[i]);
108 }
109 return total;
110 }
111
112 template<class F>
113 CUDA NI int num_qf_vars(const F& f, bool type_filter, AType aty);
114
115 template<size_t n, class F>
116 CUDA NI int num_qf_vars_in_seq(const F& f, bool type_filter, AType aty) {
117 const auto& children = battery::get<1>(battery::get<n>(f.data()));
118 int total = 0;
119 for(int i = 0; i < children.size(); ++i) {
120 total += num_qf_vars(children[i], type_filter, aty);
121 }
122 return total;
123 }
124
125 template<class F>
126 CUDA NI int num_qf_vars(const F& f, bool type_filter, AType aty) {
127 switch(f.index()) {
128 case F::E:
129 if(type_filter) {
130 return f.type() == aty ? 1 : 0;
131 }
132 else {
133 return 1;
134 }
135 case F::Seq: return impl::num_qf_vars_in_seq<F::Seq>(f, type_filter, aty);
136 case F::ESeq: return impl::num_qf_vars_in_seq<F::ESeq>(f, type_filter, aty);
137 default: return 0;
138 }
139 }
140}
141
142/** \return The first variable occurring in the formula, or any other subformula if the formula does not contain a variable.
143 It returns either a logical variable, an abstract variable or a quantifier. */
144template<typename Allocator, typename ExtendedSig>
146 bool found = false;
147 return impl::var_in_impl(f, found);
148}
149
150/** \return The number of variables occurring in the formula `F` including existential quantifier, logical variables and abstract variables.
151 * Each occurrence of a variable is added up (duplicates are counted). */
152template<class F>
153CUDA NI int num_vars(const F& f)
154{
155 switch(f.index()) {
156 case F::V:
157 case F::E:
158 case F::LV:
159 return 1;
160 case F::Seq: return impl::num_vars_in_seq<F::Seq>(f);
161 case F::ESeq: return impl::num_vars_in_seq<F::ESeq>(f);
162 default: return 0;
163 }
164}
165
166/** \return The number of existential quantifiers. */
167template<class F>
168CUDA int num_quantified_vars(const F& f) {
169 return impl::num_qf_vars(f, false, UNTYPED);
170}
171
172/** \return The number of variables occurring in an existential quantifier that have type `aty`. */
173template<class F>
174CUDA int num_quantified_vars(const F& f, AType aty) {
175 return impl::num_qf_vars(f, true, aty);
176}
177
178template<class F>
179CUDA NI AType type_of_conjunction(const typename F::Sequence& seq) {
180 AType ty = UNTYPED;
181 for(int i = 0; i < seq.size(); ++i) {
182 if(seq[i].type() == UNTYPED) {
183 return UNTYPED;
184 }
185 else if(ty == UNTYPED) {
186 ty = seq[i].type();
187 }
188 else if(ty != seq[i].type()) {
189 return UNTYPED;
190 }
191 }
192 return ty;
193}
194
195/** Given a conjunctive formula `f` of the form \f$ c_1 \land ... \land c_n \f$, it returns a pair \f$ \langle c_i \land .. \land c_j, c_k \land ... \land c_l \rangle \f$ such that the first component contains all formulas with the type `ty`, and the second component, all other formulas. */
196template<class F>
197CUDA NI battery::tuple<F,F> extract_ty(const F& f, AType ty) {
198 assert(f.is(F::Seq));
199 const auto& seq = f.seq();
200 typename F::Sequence fty;
201 typename F::Sequence other;
202 for(int i = 0; i < seq.size(); ++i) {
203 // In case of nested conjunction.
204 if(seq[i].is(F::Seq) && seq[i].sig() == AND) {
205 auto r = extract_ty(seq[i], ty);
206 auto& fty_ = battery::get<0>(r).seq();
207 auto& other_ = battery::get<1>(r).seq();
208 for(int i = 0; i < fty_.size(); ++i) {
209 fty.push_back(std::move(fty_[i]));
210 }
211 for(int i = 0; i < other_.size(); ++i) {
212 other.push_back(std::move(other_[i]));
213 }
214 }
215 else if(seq[i].type() == ty) {
216 fty.push_back(seq[i]);
217 }
218 else {
219 other.push_back(seq[i]);
220 }
221 }
222 AType other_ty = type_of_conjunction<F>(other);
223 return battery::make_tuple(
224 F::make_nary(AND, std::move(fty), ty),
225 F::make_nary(AND, std::move(other), other_ty));
226}
227
228template <class F>
229CUDA NI thrust::optional<F> negate(const F& f);
230
231/** not(f1 \/ ... \/ fn) --> not(f1) /\ ... /\ not(fn)
232 not(f1 /\ ... /\ fn) --> not(f1) \/ ... \/ not(fn) */
233template <class F>
234CUDA NI thrust::optional<F> de_morgan_law(Sig sig_neg, const F& f) {
235 auto seq = f.seq();
236 typename F::Sequence neg_seq(seq.size());
237 for(int i = 0; i < f.seq().size(); ++i) {
238 auto neg_i = negate(seq[i]);
239 if(neg_i.has_value()) {
240 neg_seq[i] = *neg_i;
241 }
242 else {
243 return {};
244 }
245 }
246 return F::make_nary(sig_neg, neg_seq, f.type());
247}
248
249template <class F>
250CUDA NI thrust::optional<F> negate_eq(const F& f) {
251 assert(f.is_binary() && f.sig() == EQ);
252 if(f.seq(0).is(F::B)) {
253 auto b = f.seq(0);
254 b.b() = !b.b();
255 return F::make_binary(b, EQ, f.seq(1), f.type(), f.seq().get_allocator());
256 }
257 else if(f.seq(1).is(F::B)) {
258 auto b = f.seq(1);
259 b.b() = !b.b();
260 return F::make_binary(f.seq(0), EQ, b, f.type(), f.seq().get_allocator());
261 }
262 return F::make_nary(NEQ, f.seq(), f.type());
263}
264
265template <class F>
266CUDA NI thrust::optional<F> negate(const F& f) {
267 if(f.is_true()) {
268 return F::make_false();
269 }
270 else if(f.is_false()) {
271 return F::make_true();
272 }
273 else if(f.is_variable()) {
274 return F::make_unary(NOT, f, f.type());
275 }
276 else if(f.is(F::Seq)) {
277 Sig neg_sig;
278 switch(f.sig()) {
279 case NOT: return f.seq(0);
280 case EQ: return negate_eq(f);
281 // Total order predicates can be reversed.
282 case LEQ: neg_sig = GT; break;
283 case GEQ: neg_sig = LT; break;
284 case NEQ: neg_sig = EQ; break;
285 case LT: neg_sig = GEQ; break;
286 case GT: neg_sig = LEQ; break;
287 // Partial order predicates cannot simply be reversed.
288 case SUBSET:
289 case SUBSETEQ:
290 case SUPSET:
291 case SUPSETEQ:
292 case IN:
293 return F::make_unary(NOT, f, f.type());
294 case AND:
295 return de_morgan_law(OR, f);
296 case OR:
297 return de_morgan_law(AND, f);
298 default:
299 return {};
300 }
301 return F::make_nary(neg_sig, f.seq(), f.type());
302 }
303 return {};
304}
305
307 switch(sig) {
308 case EQ: return NEQ;
309 case NEQ: return EQ;
310 case LEQ: return GT;
311 case GEQ: return LT;
312 case LT: return GEQ;
313 case GT: return LEQ;
314 default: assert(0); return sig;
315 }
316}
317
318/** True for the operators <=, <, >, >=, =, != */
319template <class F>
320CUDA NI bool is_arithmetic_comparison(const F& f) {
321 if(f.is(F::Seq)) {
322 switch(f.sig()) {
323 case LEQ:
324 case GEQ:
325 case LT:
326 case GT:
327 case EQ:
328 case NEQ:
329 return true;
330 default: break;
331 }
332 }
333 return false;
334}
335
336/** True for the operators =, !=, subset, subseteq, supset, supseteq */
337template <class F>
338CUDA NI bool is_set_comparison(const F& f) {
339 if(f.is(F::Seq)) {
340 switch(f.sig()) {
341 case EQ:
342 case NEQ:
343 case SUBSET:
344 case SUBSETEQ:
345 case SUPSET:
346 case SUPSETEQ:
347 return true;
348 default: break;
349 }
350 }
351 return false;
352}
353
354/** True for the operators <=, <, >, >=, =, !=, subset, subseteq, supset, supseteq */
355template <class F>
356CUDA NI bool is_comparison(const F& f) {
357 if(f.is(F::Seq)) {
358 switch(f.sig()) {
359 case LEQ:
360 case GEQ:
361 case LT:
362 case GT:
363 case EQ:
364 case NEQ:
365 case SUBSET:
366 case SUBSETEQ:
367 case SUPSET:
368 case SUPSETEQ:
369 return true;
370 default: break;
371 }
372 }
373 return false;
374}
375
376/** Returns the converse of a comparison operator (see `is_comparison`). */
377CUDA NI inline Sig converse_comparison(Sig sig) {
378 switch(sig) {
379 case LEQ: return GEQ;
380 case GEQ: return LEQ;
381 case GT: return LT;
382 case LT: return GT;
383 case EQ: return sig;
384 case NEQ: return sig;
385 case SUBSET: return SUPSET;
386 case SUBSETEQ: return SUPSETEQ;
387 case SUPSET: return SUBSET;
388 case SUPSETEQ: return SUBSETEQ;
389 default:
390 assert(false); // converse not supported for all operators.
391 break;
392 }
393 return sig;
394}
395
396/** Given a predicate of the form `t <op> u` (e.g., `x + y <= z + 4`), it transforms it into an equivalent predicate of the form `s <op> k` where `k` is a constant (e.g., `x + y - (z + 4) <= 0`).
397If the formula is not a predicate, it is returned unchanged. */
398template <class F>
399CUDA NI F move_constants_on_rhs(const F& f) {
400 if(is_comparison(f) && !f.seq(1).is_constant()) {
401 AType aty = f.type();
402 if(f.seq(0).is_constant()) {
403 return F::make_binary(f.seq(1), converse_comparison(f.sig()), f.seq(0), aty);
404 }
405 else {
406 return F::make_binary(
407 F::make_binary(f.seq(0), SUB, f.seq(1), aty),
408 f.sig(),
409 F::make_z(0),
410 aty);
411 }
412 }
413 return f;
414}
415
416/** Given a formula `f`, we transform all occurrences of `AVar` into logical variables. */
417template <class F, class Env>
418CUDA NI void map_avar_to_lvar(F& f, const Env& env, bool erase_type = false) {
419 switch(f.index()) {
420 case F::V:
421 f = F::make_lvar(erase_type ? UNTYPED : f.v().aty(), env.name_of(f.v()));
422 break;
423 case F::Seq:
424 for(int i = 0; i < f.seq().size(); ++i) {
425 map_avar_to_lvar(f.seq(i), env, erase_type);
426 }
427 break;
428 case F::ESeq:
429 for(int i = 0; i < f.eseq().size(); ++i) {
430 map_avar_to_lvar(f.eseq(i), env, erase_type);
431 }
432 break;
433 default: break;
434 }
435}
436
437namespace impl {
438 template <class F>
439 CUDA bool is_bz(const F& f) {
440 return f.is(F::Z) || f.is(F::B);
441 }
442
443 template <class F>
444 CUDA bool is_binary_bz(const typename F::Sequence& seq) {
445 return seq.size() == 2 && is_bz(seq[0]) && is_bz(seq[1]);
446 }
447
448 template <class F>
449 CUDA bool is_binary_z(const typename F::Sequence& seq) {
450 return seq.size() == 2 && seq[0].is(F::Z) && seq[1].is(F::Z);
451 }
452
453 /** Evaluate the function or predicate `sig` on the sequence of constants `seq`.
454 * It only works on Boolean and integers constants for now.
455 */
456 template <class F>
457 CUDA F eval_seq(Sig sig, const typename F::Sequence& seq, AType atype) {
458 switch(sig) {
459 case AND: {
460 typename F::Sequence residual;
461 for(int i = 0; i < seq.size(); ++i) {
462 if(seq[i].is_false()) {
463 return F::make_false();
464 }
465 else if(!seq[i].is_true()) {
466 residual.push_back(seq[i]);
467 }
468 }
469 if(residual.size() == 0) {
470 return F::make_true();
471 }
472 else if(residual.size() == 1) {
473 return residual[0];
474 }
475 else {
476 return F::make_nary(AND, std::move(residual), atype);
477 }
478 }
479 case OR: {
480 typename F::Sequence residual;
481 for(int i = 0; i < seq.size(); ++i) {
482 if(seq[i].is_true()) {
483 return F::make_true();
484 }
485 else if(!seq[i].is_false()) {
486 residual.push_back(seq[i]);
487 }
488 }
489 if(residual.size() == 0) {
490 return F::make_false();
491 }
492 else if(residual.size() == 1) {
493 return residual[0];
494 }
495 else {
496 return F::make_nary(OR, std::move(residual), atype);
497 }
498 }
499 case IMPLY: {
500 if(is_binary_bz<F>(seq)) {
501 return seq[0].is_false() || seq[1].is_true() ? F::make_true() : F::make_false();
502 }
503 else if(seq.size() == 2) {
504 if(seq[0].is_true()) { return seq[1]; }
505 else if(seq[0].is_false()) { return F::make_true(); }
506 else if(seq[1].is_true()) { return F::make_true(); }
507 else if(seq[1].is_false()) {
508 auto r = negate(seq[0]);
509 if(r.has_value()) {
510 return r.value();
511 }
512 }
513 }
514 break;
515 }
516 case EQUIV: {
517 if(is_binary_bz<F>(seq)) {
518 return (seq[0].is_true() == seq[1].is_true()) ? F::make_true() : F::make_false();
519 }
520 else if(seq.size() == 2) {
521 if(seq[0].is_true()) { return seq[1]; }
522 else if(seq[0].is_false()) { return F::make_unary(NOT, seq[1], atype); }
523 else if(seq[1].is_true()) { return seq[0]; }
524 else if(seq[1].is_false()) { return F::make_unary(NOT, seq[0], atype); }
525 }
526 break;
527 }
528 case NOT: {
529 if(seq.size() == 1 && is_bz(seq[0])) {
530 return seq[0].is_true() ? F::make_false() : F::make_true();
531 }
532 if(seq.size() == 1 && seq[0].is_unary() && seq[0].sig() == NOT) {
533 return seq[0].seq(0);
534 }
535 break;
536 }
537 case XOR: {
538 if(is_binary_bz<F>(seq)) {
539 return (seq[0].is_true() != seq[1].is_true()) ? F::make_true() : F::make_false();
540 }
541 break;
542 }
543 case ITE: {
544 if(seq.size() == 3 && is_bz(seq[0])) {
545 return seq[0].is_true() ? seq[1] : seq[2];
546 }
547 break;
548 }
549 case EQ: {
550 if(seq.size() == 2 && seq[0].is_constant() && seq[1].is_constant()) {
551 return seq[0] == seq[1] ? F::make_true() : F::make_false();
552 }
553 // We detect a common pattern for equalities: x + (-y) == 0
554 if(seq.size() == 2 && seq[0].is_binary() &&
555 seq[1].is(F::Z) && seq[1].z() == 0 &&
556 seq[0].sig() == ADD &&
557 seq[0].seq(1).is_unary() && seq[0].seq(1).sig() == NEG && seq[0].seq(1).seq(0).is_variable() &&
558 seq[0].seq(0).is_variable())
559 {
560 return F::make_binary(seq[0].seq(0), EQ, seq[0].seq(1).seq(0), atype);
561 }
562 break;
563 }
564 case NEQ: {
565 if(seq.size() == 2 && seq[0].is_constant() && seq[1].is_constant()) {
566 return seq[0] != seq[1] ? F::make_true() : F::make_false();
567 }
568 // We detect a common pattern for disequalities: x + (-y) != 0
569 if(seq.size() == 2 && seq[0].is_binary() &&
570 seq[1].is(F::Z) && seq[1].z() == 0 &&
571 seq[0].sig() == ADD &&
572 seq[0].seq(1).is_unary() && seq[0].seq(1).sig() == NEG && seq[0].seq(1).seq(0).is_variable() &&
573 seq[0].seq(0).is_variable())
574 {
575 return F::make_binary(seq[0].seq(0), NEQ, seq[0].seq(1).seq(0), atype);
576 }
577 // Detect `x + k != k2` where `k` and `k2` are constants. It should be generalized.
578 if(seq.size() == 2 && seq[0].is_binary() &&
579 is_bz(seq[1]) &&
580 seq[0].sig() == ADD &&
581 is_bz(seq[0].seq(1)))
582 {
583 return F::make_binary(seq[0].seq(0), NEQ, F::make_z(seq[1].to_z() - seq[0].seq(1).to_z()), atype);
584 }
585 // k != -x --> -k != x
586 if(seq.size() == 2 && is_bz(seq[0]) && seq[1].is_unary() && seq[1].sig() == NEG && seq[1].seq(0).is_variable()) {
587 return F::make_binary(F::make_z(-seq[0].to_z()), NEQ, seq[1].seq(0), atype);
588 }
589 // -x != k --> x != -k
590 if(seq.size() == 2 && is_bz(seq[1]) && seq[0].is_unary() && seq[0].sig() == NEG && seq[0].seq(0).is_variable()) {
591 return F::make_binary(seq[0].seq(0), NEQ, F::make_z(-seq[1].to_z()), atype);
592 }
593 break;
594 }
595 case LT: {
596 if(is_binary_z<F>(seq)) {
597 return seq[0].z() < seq[1].z() ? F::make_true() : F::make_false();
598 }
599 break;
600 }
601 case LEQ: {
602 if(is_binary_z<F>(seq)) {
603 return seq[0].z() <= seq[1].z() ? F::make_true() : F::make_false();
604 }
605 break;
606 }
607 case GT: {
608 if(is_binary_z<F>(seq)) {
609 return seq[0].z() > seq[1].z() ? F::make_true() : F::make_false();
610 }
611 break;
612 }
613 case GEQ: {
614 if(is_binary_z<F>(seq)) {
615 return seq[0].z() >= seq[1].z() ? F::make_true() : F::make_false();
616 }
617 break;
618 }
619 case MIN: {
620 if(is_binary_z<F>(seq)) {
621 return F::make_z(battery::min(seq[0].z(), seq[1].z()));
622 }
623 break;
624 }
625 case MAX: {
626 if(is_binary_z<F>(seq)) {
627 return F::make_z(battery::max(seq[0].z(), seq[1].z()));
628 }
629 break;
630 }
631 case NEG: {
632 if(seq.size() == 1 && is_bz(seq[0])) {
633 return F::make_z(-seq[0].to_z());
634 }
635 if(seq.size() == 1 && seq[0].is_unary() && seq[0].sig() == NEG) {
636 return seq[0].seq(0);
637 }
638 break;
639 }
640 case ABS: {
641 if(seq.size() == 1 && is_bz(seq[0])) {
642 return F::make_z(abs(seq[0].to_z()));
643 }
644 break;
645 }
646 case ADD: {
647 typename F::Sequence residual;
648 logic_int sum = 0;
649 for(int i = 0; i < seq.size(); ++i) {
650 if(is_bz(seq[i])) {
651 sum += seq[i].to_z();
652 }
653 else {
654 residual.push_back(seq[i]);
655 }
656 }
657 if(residual.size() == 0) {
658 return F::make_z(sum);
659 }
660 else {
661 if(sum != 0) {
662 residual.push_back(F::make_z(sum));
663 }
664 if(residual.size() == 1) {
665 return residual[0];
666 }
667 else {
668 return F::make_nary(ADD, std::move(residual), atype);
669 }
670 }
671 }
672 case MUL: {
673 typename F::Sequence residual;
674 logic_int prod = 1;
675 for(int i = 0; i < seq.size(); ++i) {
676 if(is_bz(seq[i])) {
677 prod *= seq[i].to_z();
678 }
679 else {
680 residual.push_back(seq[i]);
681 }
682 }
683 if(residual.size() == 0) {
684 return F::make_z(prod);
685 }
686 else {
687 if(prod == 0) {
688 return F::make_z(0);
689 }
690 else if(prod == -1 && residual.size() > 0) {
691 residual[0] = F::make_unary(NEG, std::move(residual[0]), atype);
692 }
693 else if(prod != 1) {
694 residual.push_back(F::make_z(prod));
695 }
696 if(residual.size() == 1) {
697 return residual[0];
698 }
699 else {
700 return F::make_nary(MUL, std::move(residual), atype);
701 }
702 }
703 }
704 case SUB: {
705 if(is_binary_bz<F>(seq)) {
706 return F::make_z(seq[0].to_z() - seq[1].to_z());
707 }
708 else if(seq.size() == 2 && is_bz(seq[1]) && seq[1].to_z() == 0) {
709 return seq[0];
710 }
711 else if(seq.size() == 2 && is_bz(seq[0]) && seq[0].to_z() == 0) {
712 return F::make_unary(NEG, seq[0], atype);
713 }
714 break;
715 }
716 case TDIV:
717 case FDIV:
718 case CDIV:
719 case EDIV: {
720 if(is_binary_z<F>(seq)) {
721 switch(sig) {
722 case TDIV: return F::make_z(battery::tdiv(seq[0].z(), seq[1].z()));
723 case FDIV: return F::make_z(battery::fdiv(seq[0].z(), seq[1].z()));
724 case CDIV: return F::make_z(battery::cdiv(seq[0].z(), seq[1].z()));
725 case EDIV: return F::make_z(battery::ediv(seq[0].z(), seq[1].z()));
726 default: assert(0); break;
727 }
728 }
729 else if(seq.size() == 2 && is_bz(seq[0]) && seq[0].to_z() == 0) {
730 return F::make_z(0);
731 }
732 else if(seq.size() == 2 && is_bz(seq[1]) && seq[1].to_z() == 1) {
733 return seq[0];
734 }
735 break;
736 }
737 case TMOD: {
738 if(is_binary_z<F>(seq)) {
739 return F::make_z(battery::tmod(seq[0].z(), seq[1].z()));
740 }
741 break;
742 }
743 case FMOD: {
744 if(is_binary_z<F>(seq)) {
745 return F::make_z(battery::fmod(seq[0].z(), seq[1].z()));
746 }
747 break;
748 }
749 case CMOD: {
750 if(is_binary_z<F>(seq)) {
751 return F::make_z(battery::cmod(seq[0].z(), seq[1].z()));
752 }
753 break;
754 }
755 case EMOD: {
756 if(is_binary_z<F>(seq)) {
757 return F::make_z(battery::emod(seq[0].z(), seq[1].z()));
758 }
759 break;
760 }
761 case POW: {
762 if(is_binary_bz<F>(seq)) {
763 return F::make_z(battery::ipow(seq[0].to_z(), seq[1].to_z()));
764 }
765 else if(seq.size() == 2 && is_bz(seq[1]) && seq[1].to_z() == 0) {
766 return F::make_z(1);
767 }
768 break;
769 }
770 case IN: {
771 if(seq.size() == 2 && is_bz(seq[0]) && seq[1].is(F::S)) {
772 const auto& set = seq[1].s();
773 logic_int left = seq[0].to_z();
774 for(int i = 0; i < set.size(); ++i) {
775 const auto& lb = battery::get<0>(set[i]);
776 const auto& ub = battery::get<1>(set[i]);
777 if(is_bz(lb) && is_bz(ub) && left >= lb.to_z() && left <= ub.to_z()) {
778 return F::make_true();
779 }
780 }
781 return F::make_false();
782 }
783 else if(seq.size() == 2 && seq[1].is(F::S) && seq[1].s().size() == 0) {
784 return F::make_false();
785 }
786 break;
787 }
788 }
789 return F::make_nary(sig, seq, atype);
790 }
791}
792
793template <class F>
794CUDA NI F eval(const F& f) {
795 switch(f.index()) {
796 case F::Z: return f;
797 case F::R: return f;
798 case F::S: return f;
799 case F::B: return f;
800 case F::V: return f;
801 case F::E: return f;
802 case F::LV: return f;
803 case F::Seq: {
804 const auto& seq = f.seq();
805 typename F::Sequence evaluated_seq;
806 for(int i = 0; i < seq.size(); ++i) {
807 evaluated_seq.push_back(eval(seq[i]));
808 }
809 return impl::eval_seq<F>(f.sig(), evaluated_seq, f.type());
810 }
811 case F::ESeq: {
812 auto eseq = f.eseq();
813 typename F::Sequence evaluated_eseq;
814 for(int i = 0; i < eseq.size(); ++i) {
815 evaluated_eseq.push_back(eval(eseq[i]));
816 }
817 return F::make_nary(f.esig(), std::move(evaluated_eseq), f.type());
818 }
819 default: assert(false); return f;
820 }
821}
822
823}
824
825#endif
Definition ast.hpp:38
Definition ast.hpp:234
CUDA Sig sig() const
Definition ast.hpp:520
CUDA const Sequence & seq() const
Definition ast.hpp:537
CUDA bool is(size_t kind) const
Definition ast.hpp:459
Definition abstract_deps.hpp:14
CUDA NI bool is_v_op_constant(const TFormula< Allocator, ExtendedSig > &f, Sig sig)
Definition algorithm.hpp:12
CUDA NI F move_constants_on_rhs(const F &f)
Definition algorithm.hpp:399
CUDA NI void map_avar_to_lvar(F &f, const Env &env, bool erase_type=false)
Definition algorithm.hpp:418
int AType
Definition sort.hpp:18
CUDA NI Sig converse_comparison(Sig sig)
Definition algorithm.hpp:377
CUDA NI bool is_arithmetic_comparison(const F &f)
Definition algorithm.hpp:320
Sig
Definition ast.hpp:94
@ XOR
Logical connector.
Definition ast.hpp:114
@ ITE
If-then-else.
Definition ast.hpp:115
@ NEQ
Equality relations.
Definition ast.hpp:112
@ LEQ
Unary arithmetic function symbols.
Definition ast.hpp:113
@ NOT
Unary arithmetic function symbols.
Definition ast.hpp:114
@ IMPLY
Unary arithmetic function symbols.
Definition ast.hpp:114
@ TMOD
Truncated division, present in most programming languages, is defined as , i.e., it rounds towards ze...
Definition ast.hpp:102
@ SUPSETEQ
Set inclusion predicates.
Definition ast.hpp:107
@ OR
Unary arithmetic function symbols.
Definition ast.hpp:114
@ EQ
Unary arithmetic function symbols.
Definition ast.hpp:112
@ GEQ
Unary arithmetic function symbols.
Definition ast.hpp:113
@ ADD
Unary arithmetic function symbols.
Definition ast.hpp:97
@ IN
Set membership predicate.
Definition ast.hpp:108
@ POW
Unary arithmetic function symbols.
Definition ast.hpp:97
@ MIN
Unary arithmetic function symbols.
Definition ast.hpp:97
@ EDIV
Unary arithmetic function symbols.
Definition ast.hpp:105
@ GT
Arithmetic comparison predicates. When applied to set, it corresponds to the lexicographic ordering o...
Definition ast.hpp:113
@ FMOD
Floor division (Knuth D. (1972). The Art of Computer Programming, Vol 1, Fundamental Algorithms),...
Definition ast.hpp:103
@ MAX
Binary arithmetic function symbols.
Definition ast.hpp:97
@ LT
Unary arithmetic function symbols.
Definition ast.hpp:113
@ TDIV
Unary arithmetic function symbols.
Definition ast.hpp:102
@ FDIV
Unary arithmetic function symbols.
Definition ast.hpp:103
@ SUBSETEQ
Unary arithmetic function symbols.
Definition ast.hpp:107
@ AND
Unary arithmetic function symbols.
Definition ast.hpp:114
@ SUBSET
Unary arithmetic function symbols.
Definition ast.hpp:107
@ EQUIV
Unary arithmetic function symbols.
Definition ast.hpp:114
@ ABS
Unary arithmetic function symbols.
Definition ast.hpp:96
@ SUB
Unary arithmetic function symbols.
Definition ast.hpp:97
@ SUPSET
Unary arithmetic function symbols.
Definition ast.hpp:107
@ MUL
Unary arithmetic function symbols.
Definition ast.hpp:97
@ CMOD
Ceil division is defined as . Modulus is defined as .
Definition ast.hpp:104
@ CDIV
Unary arithmetic function symbols.
Definition ast.hpp:104
@ NEG
Unary arithmetic function symbols.
Definition ast.hpp:96
@ EMOD
Euclidean division (Boute T. R. (1992). The Euclidean definition of the functions div and mod)....
Definition ast.hpp:105
CUDA NI thrust::optional< F > de_morgan_law(Sig sig_neg, const F &f)
Definition algorithm.hpp:234
long long int logic_int
Definition sort.hpp:114
CUDA NI bool is_var_equality(const TFormula< Allocator, ExtendedSig > &f)
Definition algorithm.hpp:32
CUDA NI TFormula< Allocator > make_v_op_z(LVar< Allocator > v, Sig sig, logic_int z, AType aty=UNTYPED, const Allocator &allocator=Allocator())
Definition algorithm.hpp:41
CUDA int num_quantified_vars(const F &f)
Definition algorithm.hpp:168
CUDA NI battery::tuple< F, F > extract_ty(const F &f, AType ty)
Definition algorithm.hpp:197
CUDA NI bool is_v_op_z(const TFormula< Allocator, ExtendedSig > &f, Sig sig)
Definition algorithm.hpp:22
battery::string< Allocator > LVar
Definition ast.hpp:25
CUDA NI AType type_of_conjunction(const typename F::Sequence &seq)
Definition algorithm.hpp:179
CUDA Sig leq_of_constant(const TFormula< Allocator > &f)
Definition algorithm.hpp:60
CUDA NI bool is_comparison(const F &f)
Definition algorithm.hpp:356
CUDA NI thrust::optional< F > negate_eq(const F &f)
Definition algorithm.hpp:250
CUDA NI thrust::optional< F > negate(const F &f)
Definition algorithm.hpp:266
CUDA Sig geq_of_constant(const TFormula< Allocator > &f)
Definition algorithm.hpp:53
CUDA NI int num_vars(const F &f)
Definition algorithm.hpp:153
CUDA Sig negate_arithmetic_comparison(Sig sig)
Definition algorithm.hpp:306
CUDA const TFormula< Allocator, ExtendedSig > & var_in(const TFormula< Allocator, ExtendedSig > &f)
Definition algorithm.hpp:145
CUDA NI F eval(const F &f)
Definition algorithm.hpp:794
CUDA NI bool is_set_comparison(const F &f)
Definition algorithm.hpp:338
#define UNTYPED
Definition sort.hpp:21