Ultra 0.9.4
Loading...
Searching...
No Matches
integer.h
Go to the documentation of this file.
1
12
13#if !defined(ULTRA_INT_PRIMITIVE_H)
14#define ULTRA_INT_PRIMITIVE_H
15
16#include "kernel/nullary.h"
17#include "kernel/random.h"
18#include "kernel/terminal.h"
19#include "kernel/gp/function.h"
20
21#include <climits>
22#include <limits>
23
33{
34
35namespace internal
36{
37
38[[nodiscard]] inline bool int_value_index(const function *f,
39 const function::params &pars)
40{
41 Ensures(f);
42
43 for (std::size_t i(0); i < f->arity(); ++i)
44 if (pars[i].index() != d_int)
45 return false;
46
47 return true;
48}
49
50} // namespace internal
51
58[[nodiscard]] inline D_INT base(const value_t &v)
59{
60 return std::get<D_INT>(v);
61}
62
66class number : public terminal
67{
68public:
69 explicit number(D_INT m = -128, D_INT s = 128,
71 : terminal("INT", c), min_(m), sup_(s)
72 {
73 Expects(m < s);
74 }
75
76 [[nodiscard]] D_INT min() const noexcept { return min_; }
77 [[nodiscard]] D_INT sup() const noexcept { return sup_; }
78
79 [[nodiscard]] value_t instance() const final
80 { return random::between(min_, sup_); }
81
82private:
83 const D_INT min_, sup_;
84};
85
86class literal : public terminal
87{
88public:
89 explicit literal(D_INT v, category_t c = symbol::default_category)
90 : terminal(std::to_string(v), c), val_(v)
91 {
92 }
93
94 [[nodiscard]] value_t instance() const noexcept final { return val_; }
95
96private:
97 const D_INT val_;
98};
99
101class add : public function
102{
103public:
104 explicit add(category_t c = symbol::default_category)
105 : function("ADD", c, {c, c}) {}
106
107 [[nodiscard]] std::string to_string(format) const final
108 {
109 return "({0}+{1})";
110 }
111
112 [[nodiscard]] value_t eval(const params &pars) const final
113 {
114 Expects(internal::int_value_index(this, pars));
115
116 const auto v0(base(pars[0]));
117 const auto v1(base(pars[1]));
118
119 if (v0 > 0 && v1 > 0 && (v0 > std::numeric_limits<D_INT>::max() - v1))
120 return std::numeric_limits<D_INT>::max();
121 if (v0 < 0 && v1 < 0 && (v0 < std::numeric_limits<D_INT>::min() - v1))
122 return std::numeric_limits<D_INT>::min();
123
124 return v0 + v1;
125 }
126};
127
129class div : public function
130{
131public:
132 explicit div(return_type r = symbol::default_category,
135 : function("DIV", r, pt)
136 {
137 Expects(pt.size() == 2);
138 }
139
140 [[nodiscard]] std::string to_string(format) const final
141 {
142 return "({0}/{1})";
143 }
144
145 [[nodiscard]] value_t eval(const params &pars) const final
146 {
147 Expects(internal::int_value_index(this, pars));
148
149 const auto v0(base(pars[0]));
150 const auto v1(base(pars[1]));
151
152 if (v1 == 0 || (v0 == std::numeric_limits<D_INT>::min() && (v1 == -1)))
153 return v0;
154
155 return v0 / v1;
156 }
157};
158
159class ife : public function
160{
161public:
162 explicit ife(return_type r = symbol::default_category,
167 : function("IFE", r, pt)
168 {
169 Expects(pt.size() == 4);
170 Expects(r == pt[2]);
171 Expects(pt[0] == pt[1]);
172 Expects(pt[2] == pt[3]);
173 }
174
175 [[nodiscard]] std::string to_string(format f) const final
176 {
177 switch (f)
178 {
179 case python_format:
180 return "({2} if {0}=={1} else {3})";
181 default:
182 return "({0}=={1} ? {2} : {3})";
183 }
184 }
185
186 [[nodiscard]] value_t eval(const params &pars) const final
187 {
188 Expects(internal::int_value_index(this, pars));
189
190 const auto v0(base(pars[0]));
191 const auto v1(base(pars[1]));
192
193 return v0 == v1 ? pars[2] : pars[3];
194 }
195};
196
197class ifl : public function
198{
199public:
200 explicit ifl(return_type r = symbol::default_category,
205 : function("IFL", r, pt)
206 {
207 Expects(pt.size() == 4);
208 Expects(r == pt[2]);
209 Expects(pt[0] == pt[1]);
210 Expects(pt[2] == pt[3]);
211 }
212
213 [[nodiscard]] std::string to_string(format f) const final
214 {
215 switch (f)
216 {
217 case python_format: return "({2} if {0}<{1} else {3})";
218 default: return "({0}<{1} ? {2} : {3})";
219 }
220 }
221
222 [[nodiscard]] value_t eval(const params &pars) const final
223 {
224 Expects(internal::int_value_index(this, pars));
225
226 const auto v0(base(pars[0]));
227 const auto v1(base(pars[1]));
228
229 return v0 < v1 ? pars[2] : pars[3];
230 }
231};
232
233class ifz : public function
234{
235public:
236 explicit ifz(return_type r = symbol::default_category,
240 : function("IFZ", r, pt)
241 {
242 Expects(pt.size() == 3);
243 Expects(r == pt[1]);
244 Expects(pt[1] == pt[2]);
245 }
246
247 [[nodiscard]] std::string to_string(format f) const final
248 {
249 switch (f)
250 {
251 case python_format:
252 return "({1} if {0} == 0 else {2})";
253 default:
254 return "({0} == 0 ? {1} : {2})";
255 }
256 }
257
258 [[nodiscard]] value_t eval(const params &pars) const final
259 {
260 Expects(internal::int_value_index(this, pars));
261
262 return base(pars[0]) == 0 ? pars[1] : pars[2];
263 }
264};
265
267class mod : public function
268{
269public:
270 explicit mod(return_type r = symbol::default_category,
273 : function("MOD", r, pt)
274 {
275 Expects(pt.size() == 2);
276 Expects(pt[0] == pt[1]);
277 }
278
279 [[nodiscard]] std::string to_string(format) const final
280 {
281 return "({0}%{1})";
282 }
283
284 [[nodiscard]] value_t eval(const params &pars) const final
285 {
286 Expects(internal::int_value_index(this, pars));
287
288 const auto v0(base(pars[0]));
289 const auto v1(base(pars[1]));
290
291 if (v1 == 0 || (v0 == std::numeric_limits<D_INT>::min() && (v1 == -1)))
292 return v0;
293
294 return v0 % v1;
295 }
296};
297
299class mul : public function
300{
301public:
302 explicit mul(return_type r = symbol::default_category,
305 : function("MUL", r, pt)
306 {
307 Expects(pt.size() == 2);
308 }
309
310 [[nodiscard]] std::string to_string(format) const final
311 {
312 return "({0}*{1})";
313 }
314
315 [[nodiscard]] value_t eval(const params &pars) const final
316 {
317 Expects(internal::int_value_index(this, pars));
318
319 if constexpr (sizeof(std::intmax_t) >= 2 * sizeof(D_INT))
320 {
321 static_assert(sizeof(std::intmax_t) >= 2 * sizeof(D_INT),
322 "Unable to detect overflow after multiplication");
323
324 const std::intmax_t v0(base(pars[0]));
325 const std::intmax_t v1(base(pars[1]));
326
327 const auto tmp(v0 * v1);
328 if (tmp > std::numeric_limits<D_INT>::max())
329 return std::numeric_limits<D_INT>::max();
330 if (tmp < std::numeric_limits<D_INT>::min())
331 return std::numeric_limits<D_INT>::min();
332
333 return static_cast<D_INT>(tmp);
334 }
335 else
336 {
337 const auto v0(base(pars[0]));
338 const auto v1(base(pars[1]));
339
340 // On systems where the above relationship does not hold, the following
341 // compliant solution may be used to ensure signed overflow does not
342 // occur.
343 if (v0 > 0)
344 if (v1 > 0)
345 {
346 assert(v0 > 0 && v1 > 0);
347 if (v0 > std::numeric_limits<D_INT>::max() / v1)
348 return std::numeric_limits<D_INT>::max();
349 }
350 else
351 {
352 assert(v0 > 0 && v1 <= 0);
353 if (v1 < std::numeric_limits<D_INT>::min() / v0)
354 return std::numeric_limits<D_INT>::min();
355 }
356 else // v0 is non-positive
357 if (v1 > 0)
358 {
359 assert(v0 <= 0 && v1 > 0);
360 if (v0 < std::numeric_limits<D_INT>::min() / v1)
361 return std::numeric_limits<D_INT>::min();
362 }
363 else
364 {
365 assert(v0 <= 0 && v1 <= 0);
366 if (v0 != 0 && v1 < std::numeric_limits<D_INT>::max() / v0)
367 return std::numeric_limits<D_INT>::max();
368 }
369
370 return v0 * v1;
371 }
372 }
373};
374
376class shl : public function
377{
378public:
379 explicit shl(category_t c = symbol::default_category)
380 : function("SHL", c, {c, c}) {}
381
382 [[nodiscard]] std::string to_string(format) const final
383 {
384 return "({0} << {1})";
385 }
386
387 [[nodiscard]] value_t eval(const params &pars) const final
388 {
389 Expects(internal::int_value_index(this, pars));
390
391 const auto v0(base(pars[0]));
392 const auto v1(base(pars[1]));
393
394 if (v0 < 0 || v1 < 0
395 || v1 >= static_cast<D_INT>(sizeof(D_INT) * CHAR_BIT)
396 || v0 > std::numeric_limits<D_INT>::max() >> v1)
397 return v0;
398
399 return v0 << v1;
400 }
401};
402
404class sub : public function
405{
406public:
407 explicit sub(category_t c = symbol::default_category)
408 : function("SUB", c, {c, c}) {}
409
410 [[nodiscard]] std::string to_string(format) const final
411 {
412 return "({0}-{1})";
413 }
414
415 [[nodiscard]] value_t eval(const params &pars) const final
416 {
417 Expects(internal::int_value_index(this, pars));
418
419 const auto v0(base(pars[0]));
420 const auto v1(base(pars[1]));
421
422 if (v0 < 0 && v1 > 0 && (v0 < std::numeric_limits<D_INT>::min() + v1))
423 return std::numeric_limits<D_INT>::min();
424 if (v0 > 0 && v1 < 0 && (v0 > std::numeric_limits<D_INT>::max() + v1))
425 return std::numeric_limits<D_INT>::max();
426
427 return v0 - v1;
428 }
429};
430
431} // namespace ultra::integer
432
433#endif // include guard
Definition function.h:108
Definition function.h:44
std::vector< category_t > param_data_types
Definition function.h:49
function(const std::string &, return_type, param_data_types)
Definition function.cc:27
category_t return_type
Type used to describe the return category of the function.
Definition function.h:52
std::size_t arity() const noexcept
Definition function.cc:55
value_t eval(const params &pars) const final
Definition integer.h:112
std::string to_string(format) const final
Definition integer.h:107
value_t eval(const params &pars) const final
Definition integer.h:145
std::string to_string(format) const final
Definition integer.h:140
value_t eval(const params &pars) const final
Definition integer.h:186
std::string to_string(format f) const final
Definition integer.h:175
std::string to_string(format f) const final
Definition integer.h:213
value_t eval(const params &pars) const final
Definition integer.h:222
std::string to_string(format f) const final
Definition integer.h:247
value_t eval(const params &pars) const final
Definition integer.h:258
value_t instance() const noexcept final
Definition integer.h:94
value_t eval(const params &pars) const final
Definition integer.h:284
std::string to_string(format) const final
Definition integer.h:279
std::string to_string(format) const final
Definition integer.h:310
value_t eval(const params &pars) const final
Definition integer.h:315
value_t instance() const final
Definition integer.h:79
value_t eval(const params &pars) const final
Definition integer.h:387
std::string to_string(format) const final
Definition integer.h:382
value_t eval(const params &pars) const final
Definition integer.h:415
std::string to_string(format) const final
Definition integer.h:410
format
Supported rendering formats for symbol stringification.
Definition symbol.h:72
unsigned category_t
Definition symbol.h:55
static constexpr category_t default_category
Default category assigned when typing is not used.
Definition symbol.h:58
Definition terminal.h:41
Definition integer.h:33
D_INT base(const value_t &v)
Definition integer.h:58
std::variant< D_VOID, D_INT, D_DOUBLE, D_STRING, const D_NULLARY *, D_ADDRESS, const D_VARIABLE *, D_IVECTOR > value_t
Definition value.h:54
T between(T min, T sup)
Definition random.h:57