Lattice Land Core Library
Loading...
Searching...
No Matches
arith_bound.hpp
Go to the documentation of this file.
1// Copyright 2022 Pierre Talbot
2
3#ifndef LALA_CORE_ARITH_BOUND_HPP
4#define LALA_CORE_ARITH_BOUND_HPP
5
6#include <type_traits>
7#include <utility>
8#include <cmath>
9#include <iostream>
10#include "../logic/logic.hpp"
11#include "pre_flb.hpp"
12#include "pre_fub.hpp"
13#include "pre_zlb.hpp"
14#include "pre_zub.hpp"
15#include "../b.hpp"
16#include "battery/memory.hpp"
17
18/** A pre-abstract universe is a lattice (with usual operations join, order, ...) equipped with a simple logical interpretation function and a next/prev functions.
19 We consider totally ordered pre-abstract universes with a downset semantics.
20 For any lattice \f$ L \f$, we consider an element \f$ a \in L \f$ to represent all the concrete elements equal to or below it.
21 This set is called the downset of \f$ a \f$ and is denoted \f$ \mathord{\downarrow}{a} \f$.
22 The concretization function \f$ \gamma \f$ formalizes this idea: \f$ \gamma(a) = \{x \mapsto b \;|\; b \in \mathord{\downarrow}{a} \cap U \} \f$ where \f$ U \f$ is the universe of discourse.
23 The intersection with \f$ U \f$ is necessary to remove potential elements in the abstract universe that are not in the concrete universe of discourse (e.g., \f$ -\infty, \infty \f$ below).
24
25 The downset semantics associates each element of a lattice to its concrete downset.
26 It is possible to decide that each element is associated to the concrete upset instead.
27 Doing so will reverse our usage of the lattice-theoretic operations (join instead of meet, <= instead of >=, etc.).
28 Instead of considering the upset semantics, it is more convenient to consider the downset semantics of the dual lattice.
29
30 Example:
31 * The lattice of increasing integer \f$ \mathit{ZUB} = \langle \{-\infty, \ldots, -2, -1, 0, 1, 2, \ldots, \infty\}, \leq \rangle \f$ is ordered by the natural arithmetic comparison operator, it represents an upper bound on the set of integers represented.
32 Using the downset semantics, we can represent simple constraints such as \f$ x \leq 3 \f$, in which case the downset \f$ \mathord{\downarrow}{3} = \{\ldots, 1, 2, 3\} \f$ represents all the values of \f$ x \f$ satisfying the constraints \f$ x \leq 3 \f$, that is, the solutions of the constraints.
33 * By taking the upset semantics of \f$ \mathit{ZUB} \f$, we can represent constraints such as \f$ x \geq 3 \f$.
34 * Alternatively, we can take the dual lattice of decreasing integers \f$ \mathit{ZLB} = \langle \{\infty, \ldots, 2, 1, 0, -1, -2, \ldots, -\infty\}, \geq \rangle \f$.
35 The downset semantics of \f$ \mathit{ZLB} \f$ corresponds to the upset semantics of \f$ \mathit{ZUB} \f$.
36
37 From a pre-abstract universe, we obtain an abstract universe using the `Universe` class below.
38 We also define various aliases to abstract universes such as `ZLB`, `ZUB`, etc.
39*/
40
41namespace lala {
42
43template<class PreUniverse, class Mem>
44class FlatUniverse;
45
46template<class PreUniverse, class Mem>
47class ArithBound;
48
49/** Lattice of integer lower bounds. */
50template<class VT, class Mem>
52
53/** Lattice of integer upper bounds. */
54template<class VT, class Mem>
56
57/** Lattice of floating-point lower bounds. */
58template<class VT, class Mem>
60
61/** Lattice of floating-point upper bounds. */
62template<class VT, class Mem>
64
65/** Aliases for lattice allocated on the stack (as local variable) and accessed by only one thread.
66 * To make things simpler, the underlying type is also chosen (when required). */
67namespace local {
72}
73
74namespace impl {
75 template<class T>
76 struct is_arith_bound {
77 static constexpr bool value = false;
78 };
79
80 template<class PreUniverse, class Mem>
81 struct is_arith_bound<ArithBound<PreUniverse, Mem>> {
82 static constexpr bool value = true;
83 };
84
85 template <class T>
86 inline constexpr bool is_arith_bound_v = is_arith_bound<T>::value;
87}
88
89
90template <class A, class R = A>
91R project_fun(Sig fun, const A& a, const A& b) {
92 R r{};
93 r.project(fun, a, b);
94 return r;
95}
96
97template <class A, class R = A>
98R project_fun(Sig fun, const A& a) {
99 R r{};
100 r.project(fun, a);
101 return r;
102}
103
104/** This function is useful when we need to convert a value to its dual.
105 The dual is the upset of the current element, therefore, if we have \f$ x <= 10 \f$, the dual is given by the formula \f$ x >= 10 \f$ interpreted in the dual lattice.
106 In that case, it just changes the type of the lattice without changing the value.
107 A difference occurs on the bottom and top element.
108 Indeed, by our representation of bot and top, the bottom value in a lattice L equals the top value in its dual, but we need them to remain the same, so the dual of `L::bot()` is `LDual::bot()`.*/
109template <class LDual, class L>
110CUDA constexpr LDual dual(const L& x) {
111 if(x.is_bot()) return LDual::bot();
112 if(x.is_top()) return LDual::top();
113 return LDual(x.value());
114}
115
116template<class PreUniverse, class Mem>
118{
119 using U = PreUniverse;
120public:
121 using pre_universe = PreUniverse;
122 using value_type = typename pre_universe::value_type;
123 using memory_type = Mem;
126
127 template<class M>
129
131
132 template<class M>
135
136 constexpr static const bool is_abstract_universe = true;
137 constexpr static const bool sequential = Mem::sequential;
138 constexpr static const bool is_totally_ordered = pre_universe::is_totally_ordered;
139 constexpr static const bool preserve_bot = pre_universe::preserve_bot;
140 constexpr static const bool preserve_top = pre_universe::preserve_top;
141 constexpr static const bool preserve_join = pre_universe::preserve_join;
142 constexpr static const bool preserve_meet = pre_universe::preserve_meet;
143 constexpr static const bool injective_concretization = pre_universe::injective_concretization;
144 constexpr static const bool preserve_concrete_covers = pre_universe::preserve_concrete_covers;
145 constexpr static const bool is_lower_bound = pre_universe::is_lower_bound;
146 constexpr static const bool is_upper_bound = pre_universe::is_upper_bound;
147 constexpr static const char* name = pre_universe::name;
148
149 constexpr static const bool is_arithmetic = pre_universe::is_arithmetic;
150
151 static_assert(is_totally_ordered, "The underlying pre-universe must be totally ordered.");
152 static_assert(is_arithmetic, "The underlying pre-universe must be arithmetic (e.g. integers, floating-point numbers).");
153
154 /** A pre-interpreted formula `x >= value` ready to use.
155 * This is mainly for optimization purpose to avoid calling `interpret` each time we need it. */
156 CUDA static constexpr this_type geq_k(value_type k) {
157 if constexpr(is_lower_bound) {
158 return this_type(k);
159 }
160 else {
161 static_assert(is_lower_bound,
162 "The pre-interpreted formula x >= k is only available over abstract universe modelling lower bounds.");
163 }
164 }
165
166 CUDA static constexpr this_type leq_k(value_type k) {
167 if constexpr(is_upper_bound) {
168 return this_type(k);
169 }
170 else {
171 static_assert(is_upper_bound,
172 "The pre-interpreted formula x <= k is only available over abstract universe modelling upper bounds.");
173 }
174 }
175
176 using atomic_type = memory_type::template atomic_type<value_type>;
177private:
178 atomic_type val;
179
180public:
181 /** Similar to \f$[\![\mathit{false}]\!]\f$ if `preserve_bot` is true. */
182 CUDA static constexpr local_type bot() { return local_type(U::bot()); }
183
184 /** Similar to \f$[\![\mathit{true}]\!]\f$ if `preserve_top` is true. */
185 CUDA static constexpr local_type top() { return local_type(U::top()); }
186 /** Initialize an upset universe to top. */
187 CUDA constexpr ArithBound(): val(U::top()) {}
188 /** Similar to \f$[\![x \leq_A i]\!]\f$ for any name `x` where \f$ \leq_A \f$ is the lattice order. */
189 CUDA constexpr ArithBound(value_type x): val(x) {}
190 CUDA constexpr ArithBound(const this_type& other): ArithBound(other.value()) {}
191 constexpr ArithBound(this_type&& other) = default;
192
193 template <class M>
194 CUDA constexpr ArithBound(const this_type2<M>& other): ArithBound(other.value()) {}
195
196 /** The assignment operator can only be used in a sequential context.
197 * It is monotone but not extensive. */
198 template <class M>
199 CUDA constexpr this_type& operator=(const this_type2<M>& other) {
200 memory_type::store(val, other.value());
201 return *this;
202 }
203
204 CUDA constexpr this_type& operator=(const this_type& other) {
205 memory_type::store(val, other.value());
206 return *this;
207 }
208
209 CUDA constexpr value_type value() const { return memory_type::load(val); }
210
211 CUDA constexpr atomic_type& atomic() { return val; }
212
213 // This is dangerous because a conversion to `value_type` can be done implicitly, and overloaded operators <, >, ... can be used on the underlying value_type instead of the abstract universe.
214 CUDA constexpr operator value_type() const { return value(); }
215
216 /** \return `true` whenever \f$ a = \top \f$, `false` otherwise.
217 * @parallel @order-preserving @increasing
218 */
219 CUDA constexpr local::B is_top() const {
220 return value() == U::top();
221 }
222
223 /** \return `true` whenever \f$ a = \bot \f$, `false` otherwise.
224 * @parallel @order-preserving @decreasing
225 */
226 CUDA constexpr local::B is_bot() const {
227 return value() == U::bot();
228 }
229
230 CUDA constexpr void join_top() {
231 memory_type::store(val, U::top());
232 }
233
234 template<class M1>
235 CUDA constexpr bool join(const this_type2<M1>& other) {
236 value_type r1 = value();
237 value_type r2 = other.value();
238 if(U::strict_order(r1, r2)) {
239 memory_type::store(val, r2);
240 return true;
241 }
242 return false;
243 }
244
245 CUDA constexpr void meet_bot() {
246 memory_type::store(val, U::bot());
247 }
248
249 template<class M1>
250 CUDA constexpr bool meet(const this_type2<M1>& other) {
251 value_type r1 = value();
252 value_type r2 = other.value();
253 if(U::strict_order(r2, r1)) {
254 memory_type::store(val, r2);
255 return true;
256 }
257 return false;
258 }
259
260 /** \return \f$ x <op> i \f$ where `x` is a variable's name, `i` the current value and `<op>` depends on the underlying universe.
261 If `U` preserves top, `true` is returned whenever \f$ a = \top \f$, if it preserves bottom `false` is returned whenever \f$ a = \bot \f$.
262 We always return an exact approximation, hence for any formula \f$ \llbracket \varphi \rrbracket = a \f$, we must have \f$ a = \llbracket \rrbracket a \llbracket \rrbracket \f$ where \f$ \rrbracket a \llbracket \f$ is the deinterpretation function.
263 */
264 template<class Env, class Allocator = typename Env::allocator_type>
265 CUDA NI TFormula<Allocator> deinterpret(AVar avar, const Env& env, const Allocator& allocator = Allocator()) const {
266 using F = TFormula<Allocator>;
267 if(preserve_top && is_top()) {
268 return F::make_true();
269 }
270 else if(preserve_bot && is_bot()) {
271 return F::make_false();
272 }
273 return F::make_binary(
274 F::make_avar(avar),
275 U::sig_order(),
277 UNTYPED, allocator);
278 }
279
280 /** Deinterpret the current value to a logical constant. */
281 template<class F>
282 CUDA NI F deinterpret() const {
283 return pre_universe::template deinterpret<F>(value());
284 }
285
286 /** Under-approximates the current element \f$ a \f$ w.r.t. \f$ \rrbracket a \llbracket \f$ into `ua`.
287 * For this abstract universe, it always returns `true` since the current element \f$ a \f$ is an exact representation of \f$ \rrbracket a \llbracket \f$. */
288 CUDA constexpr bool extract(local_type& ua) const {
289 ua.val = value();
290 return true;
291 }
292
293 /** Print the current element. */
294 CUDA NI void print() const {
295 if(is_bot()) {
296 printf("\u22A5");
297 }
298 else if(is_top()) {
299 printf("\u22A4");
300 }
301 else {
303 }
304 }
305
306private:
307 /** Interpret a formula of the form `x <sig> k`. */
308 template<bool diagnose = false, class F, class M2>
309 CUDA NI static bool interpret_tell_x_op_k(const F& f, this_type2<M2>& tell, IDiagnostics& diagnostics) {
310 value_type value = pre_universe::top();
311 bool res = pre_universe::template interpret_tell<diagnose>(f.seq(1), value, diagnostics);
312 if(res) {
313 if(f.sig() == EQ || f.sig() == U::sig_order()) { // e.g., x <= 4 or x >= 4.24
314 tell.meet(local_type(value));
315 }
316 else if(f.sig() == U::sig_strict_order()) { // e.g., x < 4 or x > 4.24
317 if constexpr(preserve_concrete_covers) {
318 tell.meet(local_type(pre_universe::prev(value)));
319 }
320 else {
321 tell.meet(local_type(value));
322 }
323 }
324 else {
325 RETURN_INTERPRETATION_ERROR("The symbol `" + LVar<typename F::allocator_type>(string_of_sig(f.sig())) + "` is not supported in the tell language of this universe.");
326 }
327 }
328 return res;
329 }
330
331 /** Interpret a formula of the form `x <sig> k`. */
332 template<bool diagnose = false, class F, class M2>
333 CUDA NI static bool interpret_ask_x_op_k(const F& f, this_type2<M2>& tell, IDiagnostics& diagnostics) {
334 value_type value = pre_universe::top();
335 bool res = pre_universe::template interpret_ask<diagnose>(f.seq(1), value, diagnostics);
336 if(res) {
337 if(f.sig() == U::sig_order()) {
338 tell.meet(local_type(value));
339 }
340 else if(f.sig() == NEQ || f.sig() == U::sig_strict_order()) {
341 // We could actually do a little bit better in the case of FLB/FUB.
342 // If the real number `k` is approximated by `[f, g]`, it actually means `]f, g[` so we could safely choose `r` since it already under-approximates `k`.
343 tell.meet(local_type(pre_universe::prev(value)));
344 }
345 else {
346 RETURN_INTERPRETATION_ERROR("The symbol `" + LVar<typename F::allocator_type>(string_of_sig(f.sig())) + "` is not supported in the ask language of this universe.");
347 }
348 }
349 return res;
350 }
351
352 template<bool diagnose = false, class F, class M2>
353 CUDA NI static bool interpret_tell_set(const F& f, this_type2<M2>& tell, IDiagnostics& diagnostics) {
354 if(!f.seq(1).is(F::S)) {
355 RETURN_INTERPRETATION_ERROR("The constant `S` in a constraint `x in S` must be a set.");
356 }
357 const auto& set = f.seq(1).s();
358 if(set.size() == 0) {
359 tell.meet_bot();
360 return true;
361 }
362 value_type join_s = pre_universe::bot();
363 constexpr int bound_index = is_lower_bound ? 0 : 1;
364 // We interpret each component of the set and take the meet of all the results.
365 for(int i = 0; i < set.size(); ++i) {
366 auto bound = battery::get<bound_index>(set[i]);
367 value_type set_element = pre_universe::top();
368 bool res = pre_universe::template interpret_tell<diagnose>(bound, set_element, diagnostics);
369 if(!res) {
370 return false;
371 }
372 join_s = pre_universe::join(join_s, set_element);
373 }
374 tell.meet(local_type(join_s));
375 return true;
376 }
377
378public:
379 /** Expects a predicate of the form `x <op> k` where `x` is any variable's name, and `k` a constant.
380 * The symbol `<op>` is expected to be `U::sig_order()`, `U::sig_strict_order()`, `=` or `in`.
381 * Existential formula \f$ \exists{x:T} \f$ can also be interpreted (only to top) depending on the underlying pre-universe.
382 */
383 template<bool diagnose = false, class F, class Env, class M2>
384 CUDA NI static bool interpret_tell(const F& f, const Env&, this_type2<M2>& tell, IDiagnostics& diagnostics) {
385 if(f.is(F::E)) {
386 typename U::value_type val;
387 bool res = pre_universe::template interpret_type<diagnose>(f, val, diagnostics);
388 if(res) {
389 tell.meet(local_type(val));
390 }
391 return res;
392 }
393 else {
394 if(f.is_binary() && f.seq(0).is_variable() && f.seq(1).is_constant()) {
395 // `x in k` is equivalent to `x >= meet k` where `>=` is the lattice order `U::sig_order()`.
396 if(f.sig() == IN) {
397 return interpret_tell_set<diagnose>(f, tell, diagnostics);
398 }
399 else {
400 return interpret_tell_x_op_k<diagnose>(f, tell, diagnostics);
401 }
402 }
403 else {
404 RETURN_INTERPRETATION_ERROR("Only binary formulas of the form `x <sig> k` where if x is a variable and k is a constant are supported.");
405 }
406 }
407 }
408
409 /** Expects a predicate of the form `x <op> k` where `x` is any variable's name, and `k` a constant.
410 * The symbol `<op>` is expected to be `U::sig_order()`, `U::sig_strict_order()` or `!=`.
411 */
412 template<bool diagnose = false, class F, class Env, class M2>
413 CUDA NI static bool interpret_ask(const F& f, const Env&, this_type2<M2>& ask, IDiagnostics& diagnostics) {
414 if(f.is_binary() && f.seq(0).is_variable() && f.seq(1).is_constant()) {
415 return interpret_ask_x_op_k<diagnose>(f, ask, diagnostics);
416 }
417 else {
418 RETURN_INTERPRETATION_ERROR("Only binary formulas of the form `x <sig> k` where if x is a variable and k is a constant are supported.");
419 }
420 }
421
422 template<IKind kind, bool diagnose = false, class F, class Env, class M2>
423 CUDA NI static bool interpret(const F& f, const Env& env, this_type2<M2>& value, IDiagnostics& diagnostics) {
424 if constexpr(kind == IKind::TELL) {
425 return interpret_tell<diagnose>(f, env, value, diagnostics);
426 }
427 else {
428 return interpret_ask<diagnose>(f, env, value, diagnostics);
429 }
430 }
431
432public:
433 CUDA static constexpr local_type next(const this_type2<Mem>& a) {
434 return local_type(pre_universe::next(a.value()));
435 }
436
437 CUDA static constexpr local_type prev(const this_type2<Mem>& a) {
438 return local_type(pre_universe::prev(a.value()));
439 }
440
441 /** Unary function of type `fun: FlatUniverse -> ArithBound`.
442 * If `a` is `bot`, we meet with bottom in-place.
443 * Otherwise, we apply the function `fun` to `a` and meet the result.
444 * \remark The result of the function is always over-approximated (or exact when possible).
445 */
446 CUDA constexpr void project(Sig fun, const local_flat_type& a) {
447 if(a.is_bot()) { meet_bot(); }
448 else if(!a.is_top()) {
449 meet(local_type(pre_universe::project(fun, a)));
450 }
451 }
452
453 /** Binary functions of type `project: FlatUniverse x FlatUniverse -> ArithBound`.
454 * If `a` or `b` is `bot`, we meet bottom in-place.
455 * Otherwise, we meet `fun(a,b)` in-place.
456 * \remark The result of the function is always over-approximated (or exact when possible).
457 * \remark If the function `fun` is partial (e.g. division), we expect the arguments `a` and `b` to be in the domain of `fun` (e.g. not equal to 0).
458 */
459 CUDA constexpr void project(Sig fun, const local_flat_type& a, const local_flat_type& b) {
460 if(a.is_bot() || b.is_bot()) { meet_bot(); }
461 else if(!a.is_top() && !b.is_top()) {
462 meet(local_type(pre_universe::project(fun, a.value(), b.value())));
463 }
464 }
465
466 /** In this universe, the non-trivial order-preserving functions are {min, max, +}. */
467 CUDA static constexpr bool is_trivial_fun(Sig fun) {
468 return fun != MIN && fun != MAX && fun != ADD && (is_upper_bound || fun != ABS);
469 }
470
471 /** The functions that are order-preserving on the natural order of the universe of discourse, and its dual. */
472 CUDA static constexpr bool is_order_preserving_fun(Sig fun) {
473 return fun == ADD || fun == MIN || fun == MAX || (is_lower_bound && fun == ABS);
474 }
475
476 CUDA constexpr void project(Sig fun, const local_type &a, const local_type &b) {
477 if (a.is_bot() || b.is_bot()) { meet_bot(); return; }
478 if(fun == MIN || fun == MAX || fun == ADD) {
479 meet(local_type(pre_universe::project(fun, a.value(), b.value())));
480 }
481 }
482
483 CUDA constexpr void project(Sig fun, const local_type &a) {
484 if (a.is_bot()) { meet_bot(); return; }
485 if (!a.is_top()) {
486 meet(project(fun, a.value()));
487 }
488 }
489
490 template<class Pre2, class Mem2>
491 friend class ArithBound;
492};
493
494// Lattice operators
495
496template<class Pre, class M1, class M2>
498 return Pre::join(a.value(), b.value());
499}
500
501template<class Pre, class M1, class M2>
503 return Pre::meet(a.value(), b.value());
504}
505
506template<class Pre, class M1, class M2>
507CUDA constexpr bool operator<=(const ArithBound<Pre, M1>& a, const ArithBound<Pre, M2>& b) {
508 return Pre::order(a.value(), b.value());
509}
510
511template<class Pre, class M1, class M2>
512CUDA constexpr bool operator<(const ArithBound<Pre, M1>& a, const ArithBound<Pre, M2>& b) {
513 return Pre::strict_order(a.value(), b.value());
514}
515
516template<class Pre, class M1, class M2>
517CUDA constexpr bool operator>=(const ArithBound<Pre, M1>& a, const ArithBound<Pre, M2>& b) {
518 return Pre::order(b.value(), a.value());
519}
520
521template<class Pre, class M1, class M2>
522CUDA constexpr bool operator>(const ArithBound<Pre, M1>& a, const ArithBound<Pre, M2>& b) {
523 return Pre::strict_order(b.value(), a.value());
524}
525
526template<class Pre, class M1, class M2>
527CUDA constexpr bool operator==(const ArithBound<Pre, M1>& a, const ArithBound<Pre, M2>& b) {
528 return a.value() == b.value();
529}
530
531template<class Pre, class M1, class M2>
532CUDA constexpr bool operator!=(const ArithBound<Pre, M1>& a, const ArithBound<Pre, M2>& b) {
533 return a.value() != b.value();
534}
535
536template<class Pre, class M>
537std::ostream& operator<<(std::ostream &s, const ArithBound<Pre, M> &a) {
538 if(a.is_bot()) {
539 s << "\u22A5";
540 }
541 else if(a.is_top()) {
542 s << "\u22A4";
543 }
544 else {
545 s << a.value();
546 }
547 return s;
548}
549
550} // namespace lala
551
552#endif
Definition ast.hpp:38
Definition arith_bound.hpp:118
CUDA NI F deinterpret() const
Definition arith_bound.hpp:282
static CUDA constexpr local_type next(const this_type2< Mem > &a)
Definition arith_bound.hpp:433
static CUDA constexpr bool is_order_preserving_fun(Sig fun)
Definition arith_bound.hpp:472
CUDA constexpr bool join(const this_type2< M1 > &other)
Definition arith_bound.hpp:235
CUDA constexpr local::B is_top() const
Definition arith_bound.hpp:219
static constexpr const bool is_totally_ordered
Definition arith_bound.hpp:138
this_type2< battery::local_memory > local_type
Definition arith_bound.hpp:130
static CUDA constexpr bool is_trivial_fun(Sig fun)
Definition arith_bound.hpp:467
CUDA constexpr bool meet(const this_type2< M1 > &other)
Definition arith_bound.hpp:250
static constexpr const bool preserve_top
Definition arith_bound.hpp:140
CUDA constexpr ArithBound(const this_type2< M > &other)
Definition arith_bound.hpp:194
CUDA constexpr bool extract(local_type &ua) const
Definition arith_bound.hpp:288
Mem memory_type
Definition arith_bound.hpp:123
static CUDA constexpr this_type leq_k(value_type k)
Definition arith_bound.hpp:166
static constexpr const bool is_abstract_universe
Definition arith_bound.hpp:136
static constexpr const char * name
Definition arith_bound.hpp:147
memory_type::template atomic_type< value_type > atomic_type
Definition arith_bound.hpp:176
CUDA constexpr value_type value() const
Definition arith_bound.hpp:209
static constexpr const bool is_lower_bound
Definition arith_bound.hpp:145
CUDA constexpr ArithBound(value_type x)
Definition arith_bound.hpp:189
static constexpr const bool preserve_bot
Definition arith_bound.hpp:139
CUDA static NI bool interpret_tell(const F &f, const Env &, this_type2< M2 > &tell, IDiagnostics &diagnostics)
Definition arith_bound.hpp:384
static constexpr const bool preserve_concrete_covers
Definition arith_bound.hpp:144
CUDA NI TFormula< Allocator > deinterpret(AVar avar, const Env &env, const Allocator &allocator=Allocator()) const
Definition arith_bound.hpp:265
CUDA constexpr this_type & operator=(const this_type &other)
Definition arith_bound.hpp:204
constexpr ArithBound(this_type &&other)=default
static CUDA constexpr local_type bot()
Definition arith_bound.hpp:182
static constexpr const bool sequential
Definition arith_bound.hpp:137
static constexpr const bool injective_concretization
Definition arith_bound.hpp:143
ArithBound< pre_universe, memory_type > this_type
Definition arith_bound.hpp:124
CUDA constexpr void project(Sig fun, const local_flat_type &a)
Definition arith_bound.hpp:446
CUDA constexpr atomic_type & atomic()
Definition arith_bound.hpp:211
CUDA constexpr ArithBound()
Definition arith_bound.hpp:187
static CUDA constexpr local_type prev(const this_type2< Mem > &a)
Definition arith_bound.hpp:437
PreUniverse pre_universe
Definition arith_bound.hpp:121
CUDA constexpr void join_top()
Definition arith_bound.hpp:230
CUDA static NI bool interpret_ask(const F &f, const Env &, this_type2< M2 > &ask, IDiagnostics &diagnostics)
Definition arith_bound.hpp:413
CUDA NI void print() const
Definition arith_bound.hpp:294
static constexpr const bool preserve_join
Definition arith_bound.hpp:141
ArithBound< pre_universe, M > this_type2
Definition arith_bound.hpp:128
CUDA static NI bool interpret(const F &f, const Env &env, this_type2< M2 > &value, IDiagnostics &diagnostics)
Definition arith_bound.hpp:423
static CUDA constexpr this_type geq_k(value_type k)
Definition arith_bound.hpp:156
static constexpr const bool preserve_meet
Definition arith_bound.hpp:142
CUDA constexpr void project(Sig fun, const local_type &a)
Definition arith_bound.hpp:483
CUDA constexpr void project(Sig fun, const local_type &a, const local_type &b)
Definition arith_bound.hpp:476
CUDA constexpr void project(Sig fun, const local_flat_type &a, const local_flat_type &b)
Definition arith_bound.hpp:459
CUDA constexpr void meet_bot()
Definition arith_bound.hpp:245
typename pre_universe::value_type value_type
Definition arith_bound.hpp:122
CUDA constexpr local::B is_bot() const
Definition arith_bound.hpp:226
static CUDA constexpr local_type top()
Definition arith_bound.hpp:185
CUDA constexpr this_type & operator=(const this_type2< M > &other)
Definition arith_bound.hpp:199
CUDA constexpr ArithBound(const this_type &other)
Definition arith_bound.hpp:190
static constexpr const bool is_arithmetic
Definition arith_bound.hpp:149
static constexpr const bool is_upper_bound
Definition arith_bound.hpp:146
Definition b.hpp:10
Definition flat_universe.hpp:30
CUDA constexpr local::B is_top() const
Definition flat_universe.hpp:115
CUDA constexpr value_type value() const
Definition flat_universe.hpp:108
CUDA constexpr local::B is_bot() const
Definition flat_universe.hpp:122
Definition diagnostics.hpp:19
Definition ast.hpp:234
#define RETURN_INTERPRETATION_ERROR(MSG)
Definition diagnostics.hpp:155
CUDA NI void print(const lala::Sig &sig)
Definition ast.hpp:212
Definition abstract_deps.hpp:14
std::ostream & operator<<(std::ostream &s, const CartesianProduct< A, As... > &cp)
Definition cartesian_product.hpp:530
CUDA constexpr LDual dual(const L &x)
Definition arith_bound.hpp:110
CUDA constexpr auto fjoin(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:463
CUDA constexpr bool operator==(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:503
R project_fun(Sig fun, const A &a, const A &b)
Definition arith_bound.hpp:91
Sig
Definition ast.hpp:94
@ NEQ
Equality relations.
Definition ast.hpp:112
@ EQ
Unary arithmetic function symbols.
Definition ast.hpp:112
@ ADD
Unary arithmetic function symbols.
Definition ast.hpp:97
@ IN
Set membership predicate.
Definition ast.hpp:108
@ MIN
Unary arithmetic function symbols.
Definition ast.hpp:97
@ MAX
Binary arithmetic function symbols.
Definition ast.hpp:97
@ ABS
Unary arithmetic function symbols.
Definition ast.hpp:96
battery::string< Allocator > LVar
Definition ast.hpp:25
CUDA constexpr bool operator<(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:484
CUDA NI const char * string_of_sig(Sig sig)
Definition ast.hpp:121
CUDA constexpr auto fmeet(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:470
CUDA constexpr bool operator>(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:497
#define UNTYPED
Definition sort.hpp:21