HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
bound_integer.hpp
1// Copyright Take Vos 2021-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "utility/module.hpp"
8#include "stdint.hpp"
9#include "interval.hpp"
10
11namespace hi::inline v1 {
12
13constexpr auto bounds_test = interval<longreg_t>{1, 5};
14
15static_assert(
16 std::numeric_limits<signed long>::min() <= bounds_test.lower() and
17 bounds_test.upper() <= std::numeric_limits<signed long>::max());
18
22template<interval<longreg_t> Bounds>
25
26 static constexpr bound_type bounds = Bounds;
27
28 // clang-format off
29
32 using value_type =
33 std::conditional_t<bounds.type_contains_range<signed char>(), signed char,
34 std::conditional_t<bounds.type_contains_range<unsigned char>(), unsigned char,
35 std::conditional_t<bounds.type_contains_range<signed short>(), signed short,
36 std::conditional_t<bounds.type_contains_range<unsigned short>(), unsigned short,
37 std::conditional_t<bounds.type_contains_range<signed int>(), signed int,
38 std::conditional_t<bounds.type_contains_range<unsigned int>(), unsigned int,
39 std::conditional_t<bounds.type_contains_range<signed long>(), signed long,
40 std::conditional_t<bounds.type_contains_range<unsigned long>(), unsigned long,
41 std::conditional_t<bounds.type_contains_range<signed long long>(), signed long long,
42 std::conditional_t<bounds.type_contains_range<unsigned long long>(), unsigned long long,
43 longreg_t>>>>>>>>>>;
44
48 std::conditional_t<bounds.type_contains_range<signed char>(), signed char,
49 std::conditional_t<bounds.type_contains_range<signed short>(), signed short,
50 std::conditional_t<bounds.type_contains_range<signed int>(), signed int,
51 std::conditional_t<bounds.type_contains_range<signed long>(), signed long,
52 std::conditional_t<bounds.type_contains_range<signed long long>(), signed long long,
53 longreg_t>>>>>;
54
55 // clang-format on
56
60
61 constexpr bound_integer() noexcept : value(bounds.lower() <= 0 and 0 <= bounds.upper() ? 0 : bounds.lower()) {}
62 constexpr bound_integer(bound_integer const &) noexcept = default;
63 constexpr bound_integer(bound_integer &&) noexcept = default;
64 constexpr bound_integer &operator=(bound_integer const &) noexcept = default;
65 constexpr bound_integer &operator=(bound_integer &&) noexcept = default;
66
67 [[nodiscard]] static constexpr bound_integer make_without_check(numeric_limited auto other) noexcept
68 {
70 r.value = static_cast<value_type>(other);
71 hi_axiom(r.holds_invariant);
72 return r;
73 }
74
75 constexpr bound_integer(numeric_limited auto other) noexcept(bounds.range_contains_type<decltype(other)>()) :
76 value(static_cast<value_type>(other))
77 {
78 if constexpr (not bounds.range_contains_type<decltype(other)>()) {
79 if (other != bounds) {
80 throw std::overflow_error("bound_integer(std::integral)");
81 }
82 }
83 hi_axiom(holds_invariant());
84 }
85
86 constexpr bound_integer &operator=(numeric_limited auto other) noexcept(bounds.range_contains_type<decltype(other)>())
87 {
88 if constexpr (not bounds.range_contains_type<decltype(other)>()) {
89 if (other != bounds) {
90 throw std::overflow_error("bound_integer(std::integral)");
91 }
92 }
93 value = static_cast<value_type>(value);
94 hi_axiom(holds_invariant());
95 return *this;
96 }
97
98 template<bound_type OtherBound>
99 constexpr bound_integer(bound_integer<OtherBound> other) noexcept(OtherBound.is_fully_inside(bounds)) :
100 value(static_cast<value_type>(other.value))
101 {
102 if constexpr (not OtherBound.is_fully_inside(bounds)) {
103 if (other.value != bounds) {
104 throw std::overflow_error("bound_integer(bound_integer)");
105 }
106 }
107 hi_axiom(holds_invariant());
108 }
109
110 template<bound_type OtherBound>
111 constexpr bound_integer &operator=(bound_integer<OtherBound> other) noexcept(OtherBound.is_fully_inside(bounds))
112 {
113 if constexpr (not OtherBound.is_fully_inside(bounds)) {
114 if (other.value != bounds) {
115 throw std::overflow_error("bound_integer(bound_integer)");
116 }
117 }
118 value = static_cast<value_type>(other._value);
119 hi_axiom(holds_invariant());
120 return *this;
121 }
122
123 //template<numeric_integral T>
124 //explicit constexpr operator T() noexcept(values_between_bounds_fit_in_type_v<T>)
125 //{
126 // if constexpr (not values_between_bounds_fit_in_type_v<T>) {
127 // if (not(std::numeric_limits<T>::min() <= value and value <= std::numeric_limits<T>::max())) {
128 // throw std::overflow_error("operator T()");
129 // }
130 // }
131 //
132 // return static_cast<T>(value);
133 //}
134
135 explicit constexpr operator bool() noexcept
136 {
137 if constexpr (bounds.lower() > 0 or bounds.upper() < 0) {
138 return true;
139 } else if constexpr (bounds) {
140 return value != value_type{0};
141 } else {
142 return false;
143 }
144 }
145
146 [[nodiscard]] constexpr bool holds_invariant() noexcept
147 {
148 return value == bounds;
149 }
150
151 [[nodiscard]] auto operator-() const noexcept
152 {
153 using r_type = bound_integer<-bounds>;
154 return r_type::make_without_check(-static_cast<r_type::calculation_type>(value));
155 }
156
159 template<bound_type RHSBounds>
160 [[nodiscard]] constexpr bool operator==(bound_integer<RHSBounds> const &rhs) noexcept
161 {
162 if constexpr (bounds.upper() < RHSBounds.lower() or bounds.lower() > RHSBounds.upper) {
163 return false;
164 } else if (bounds.is_value() and RHSBounds.is_value() and bounds.lower() == RHSBounds.lower()) {
165 return true;
166 } else {
167 return value == rhs.value;
168 }
169 }
170
173 template<bound_type RHSBounds>
174 [[nodiscard]] constexpr std::strong_ordering operator<=>(bound_integer<RHSBounds> const &rhs) noexcept
175 {
176 if constexpr (bounds.upper() < RHSBounds.lower()) {
177 return std::strong_ordering::less;
178 } else if constexpr (bounds.lower() > RHSBounds.upper()) {
179 return std::strong_ordering::greater;
180 } else if constexpr (bounds.is_value() and RHSBounds.is_value() and bounds.lower() == RHSBounds.lower()) {
181 return std::strong_ordering::equal;
182 } else {
183 return value <=> rhs.value;
184 }
185 }
186
187 template<bound_type RHSBounds>
188 [[nodiscard]] constexpr auto operator+(bound_integer<RHSBounds> const &rhs) noexcept
189 {
190 static_assert(bounds.lower() >= (std::numeric_limits<longreg_t>::min() >> 1), "lhs lower bound overflow");
191 static_assert(bounds.upper() <= (std::numeric_limits<longreg_t>::max() >> 1), "lhs upper bound overflow");
192 static_assert(RHSBounds.lower() >= (std::numeric_limits<longreg_t>::min() >> 1), "rhs lower bound overflow");
193 static_assert(RHSBounds.upper() <= (std::numeric_limits<longreg_t>::max() >> 1), "rhs upper bound overflow");
194
196 return r_type::make_without_check(
197 static_cast<r_type::calculation_type>(value) + static_cast<r_type::calculation_type>(rhs.value));
198 }
199
200 template<bound_type RHSBounds>
201 [[nodiscard]] constexpr auto operator-(bound_integer<RHSBounds> const &rhs) noexcept
202 {
203 static_assert(bounds.lower() >= (std::numeric_limits<longreg_t>::min() >> 1), "lhs lower bound overflow");
204 static_assert(bounds.upper() <= (std::numeric_limits<longreg_t>::max() >> 1), "lhs upper bound overflow");
205 static_assert(RHSBounds.lower() >= (std::numeric_limits<longreg_t>::min() >> 1), "rhs lower bound overflow");
206 static_assert(RHSBounds.upper() <= (std::numeric_limits<longreg_t>::max() >> 1), "rhs upper bound overflow");
207
208 using r_type = bound_integer<bounds - RHSBounds>;
209 return r_type::make_without_check(
210 static_cast<r_type::calculation_type>(value) - static_cast<r_type::calculation_type>(rhs.value));
211 }
212
213 template<bound_type RHSBounds>
214 [[nodiscard]] constexpr auto operator*(bound_integer<RHSBounds> const &rhs) noexcept
215 {
216 static_assert(bounds.lower() >= (std::numeric_limits<intreg_t>::min()), "lhs lower bound overflow");
217 static_assert(bounds.upper() <= (std::numeric_limits<intreg_t>::max()), "lhs upper bound overflow");
218 static_assert(RHSBounds.lower() >= (std::numeric_limits<intreg_t>::min()), "rhs lower bound overflow");
219 static_assert(RHSBounds.upper() <= (std::numeric_limits<intreg_t>::max()), "rhs upper bound overflow");
220
221 using r_type = bound_integer<bounds * RHSBounds>;
222 return r_type::make_without_check(
223 static_cast<r_type::calculation_type>(value) * static_cast<r_type::calculation_type>(rhs.value));
224 }
225
226 template<bound_type RHSBounds>
227 [[nodiscard]] constexpr auto operator/(bound_integer<RHSBounds> const &rhs) noexcept(0 != RHSBounds)
228 {
229 static_assert(bounds.lower() >= ((std::numeric_limits<longreg_t>::min() + 1)), "lhs lower bound overflow");
230 static_assert(bounds.upper() <= (std::numeric_limits<longreg_t>::max()), "lhs upper bound overflow");
231 static_assert(RHSBounds, "divide by zero");
232
233 if constexpr (0 == RHSBounds) {
234 if (rhs.value == 0) {
235 throw std::domain_error("divide by zero");
236 }
237 }
238
239 using r_type = bound_integer<bounds / RHSBounds>;
240 return r_type::make_without_check(
241 static_cast<r_type::calculation_type>(value) / static_cast<r_type::calculation_type>(rhs.value));
242 }
243
244 template<bound_type RHSBounds>
245 [[nodiscard]] constexpr auto operator%(bound_integer<RHSBounds> const &rhs) noexcept(0 != RHSBounds)
246 {
247 static_assert(RHSBounds, "divide by zero");
248
249 if constexpr (0 == RHSBounds) {
250 if (rhs.value == 0) {
251 throw std::domain_error("divide by zero");
252 }
253 }
254
255 using r_type = bound_integer<bounds % RHSBounds>;
256 return r_type::make_without_check(value % rhs.value);
257 }
258
259 /*
260 template<longreg_t lower_bound, longreg_t upper_bound, longreg_t RL, longreg_t RU>
261 [[nodiscard]] constexpr auto
262 operator|(bound_integer<lower_bound, upper_bound> const &lhs, bound_integer<RL, RU> const &rhs) noexcept
263 {
264 constexpr auto a = lower_bound | RL;
265 constexpr auto b = lower_bound | RU;
266 constexpr auto c = upper_bound | RL;
267 constexpr auto d = upper_bound | RU;
268 constexpr auto r_lower_bound = std::min({a, b, c, d});
269 constexpr auto r_upper_bound = std::max({a, b, c, d});
270 using r_type = bound_integer<r_lower_bound, r_upper_bound>;
271 using t_type = typename r_type::value_type;
272
273 return rtype{static_cast<t_type>(lhs.value) | static_cast<t_type>(rhs.value)};
274 }
275
276 template<longreg_t lower_bound, longreg_t upper_bound, longreg_t RL, longreg_t RU>
277 [[nodiscard]] constexpr auto
278 operator&(bound_integer<lower_bound, upper_bound> const &lhs, bound_integer<RL, RU> const &rhs) noexcept
279 {
280 constexpr auto a = lower_bound & RL;
281 constexpr auto b = lower_bound & RU;
282 constexpr auto c = upper_bound & RL;
283 constexpr auto d = upper_bound & RU;
284 constexpr auto r_lower_bound = std::min({a, b, c, d});
285 constexpr auto r_upper_bound = std::max({a, b, c, d});
286 using r_type = bound_integer<r_lower_bound, r_upper_bound>;
287 using t_type = typename r_type::value_type;
288
289 return rtype{static_cast<t_type>(lhs.value) & static_cast<t_type>(rhs.value)};
290 }
291
292 template<longreg_t lower_bound, longreg_t upper_bound, longreg_t RL, longreg_t RU>
293 [[nodiscard]] constexpr auto
294 operator^(bound_integer<lower_bound, upper_bound> const &lhs, bound_integer<RL, RU> const &rhs) noexcept
295 {
296 // Use 'or' and 'and' for limits on 'xor'.
297 constexpr auto a = lower_bound | RL;
298 constexpr auto b = lower_bound | RU;
299 constexpr auto c = upper_bound | RL;
300 constexpr auto d = upper_bound | RU;
301 constexpr auto e = lower_bound & RL;
302 constexpr auto f = lower_bound & RU;
303 constexpr auto g = upper_bound & RL;
304 constexpr auto h = upper_bound & RU;
305 constexpr auto r_lower_bound = std::min({a, b, c, d, e, f, g, h});
306 constexpr auto r_upper_bound = std::max({a, b, c, d, e, f, g, h});
307 using r_type = bound_integer<r_lower_bound, r_upper_bound>;
308 using t_type = typename r_type::value_type;
309
310 return rtype{static_cast<t_type>(lhs.value) ^ static_cast<t_type>(rhs.value)};
311 }
312
313 template<longreg_t RL, longreg_t RU>
314 bound_integer &operator+=(bound_integer<RL, RU> const &rhs) noexcept(RL == 0 and RU == 0)
315 {
316 return *this = *this + rhs;
317 }
318
319 template<longreg_t RL, longreg_t RU>
320 bound_integer &operator-=(bound_integer<RL, RU> const &rhs) noexcept(RL == 0 and RU == 0)
321 {
322 return *this = *this - rhs;
323 }
324
325 // clang-format off
326 template<longreg_t RL, longreg_t RU>
327 bound_integer &operator*=(bound_integer<RL, RU> const &rhs)
328 noexcept(
329 (RL == 1 and RU == 1) or
330 (lower_bound <= 0 and upper_bound >= 0 and RL == 0 and RU == 0) or
331 (lower_bound <= 0 and upper_bound >= 0 and RL == 0 and RU == 1) or
332 (lower_bound == -upper_bound and RL == -1 and RU == -1) or
333 (lower_bound <= 0 and upper_bound >= 0 and lower_bound == -upper_bound and RL == -1 and RU == 0) or
334 (lower_bound <= 0 and upper_bound >= 0 and lower_bound == -upper_bound and RL == -1 and RU == 1) or
335 )
336 {
337 return *this = *this * rhs;
338 }
339 // clang-format on
340
341 template<longreg_t RL, longreg_t RU>
342 bound_integer &operator/=(bound_integer<RL, RU> const &rhs) noexcept(RL > 0 or (lower_bound == -upper_bound and RU < 0))
343 {
344 return *this = *this / rhs;
345 }
346
347 template<longreg_t RL, longreg_t RU>
348 bound_integer &operator%=(bound_integer<RL, RU> const &rhs) noexcept(RL > 0 or RU < 0)
349 {
350 return *this = *this % rhs;
351 }
352
353 template<longreg_t RL, longreg_t RU>
354 bound_integer &operator&=(bound_integer<RL, RU> const &rhs) noexcept
355 {
356 value &= static_cast<value_type>(rhs.value);
357 return *this;
358 }
359
360 template<longreg_t RL, longreg_t RU>
361 bound_integer &operator|=(bound_integer<RL, RU> const &rhs)
362 {
363 return *this = *this | rhs;
364 }
365
366 template<longreg_t RL, longreg_t RU>
367 bound_integer &operator^=(bound_integer<RL, RU> const &rhs)
368 {
369 return *this = *this ^ rhs;
370 }
371
372 [[nodiscard]] friend auto abs(bound_integer &rhs)
373 {
374 using std::abs;
375
376 constexpr auto r_lower_bound = upper_bound < 0 ? -upper_bound : (lower_bound < 0 ? 0 : lower_bound);
377 constexpr auto r_upper_bound = upper_bound >= 0 ? upper_bound : (lower_bound >= 0 ? lower_bound : 0);
378
379 using r_type = bound_integer<lower_bound + RL, upper_bound + RU>;
380 using t_type = typename r_type::value_type;
381
382 if constexpr (lower_bound >= 0) {
383 return r_type{static_cast<t_type>(rhs.value)};
384 } else {
385 if (rhs < 0) {
386 return r_type{-static_cast<t_type>(rhs.value)};
387 } else {
388 return r_type{static_cast<t_type>(rhs.value)};
389 }
390 }
391 }*/
392};
393
394// bound_integer(std::integral auto value)
395// -> bound_integer<std::numeric_limits<decltype(value)>::min(), std::numeric_limits<decltype(value)>::max()>;
396
397// template<char... Chars>
398// constexpr auto operator"" _I()
399//{
400// constexpr long long value = long_long_from_chars<Chars...>();
401// return bound_integer_underlying<value, value>{value};
402//}
403} // namespace hi::inline v1
Extra integer definitions.
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:238
@ other
The gui_event does not have associated data.
DOXYGEN BUG.
Definition algorithm.hpp:13
int64_t longreg_t
Signed integer twice the size of a standard CPU register.
Definition stdint.hpp:61
Bound integer.
Definition bound_integer.hpp:23
value_type value
The value of the integer.
Definition bound_integer.hpp:59
std::conditional_t< bounds.type_contains_range< signed char >(), signed char, std::conditional_t< bounds.type_contains_range< unsigned char >(), unsigned char, std::conditional_t< bounds.type_contains_range< signed short >(), signed short, std::conditional_t< bounds.type_contains_range< unsigned short >(), unsigned short, std::conditional_t< bounds.type_contains_range< signed int >(), signed int, std::conditional_t< bounds.type_contains_range< unsigned int >(), unsigned int, std::conditional_t< bounds.type_contains_range< signed long >(), signed long, std::conditional_t< bounds.type_contains_range< unsigned long >(), unsigned long, std::conditional_t< bounds.type_contains_range< signed long long >(), signed long long, std::conditional_t< bounds.type_contains_range< unsigned long long >(), unsigned long long, longreg_t > > > > > > > > > > value_type
The type that can hold the value of the bound integer.
Definition bound_integer.hpp:32
constexpr bool operator==(bound_integer< RHSBounds > const &rhs) noexcept
Compare equality of two integers.
Definition bound_integer.hpp:160
std::conditional_t< bounds.type_contains_range< signed char >(), signed char, std::conditional_t< bounds.type_contains_range< signed short >(), signed short, std::conditional_t< bounds.type_contains_range< signed int >(), signed int, std::conditional_t< bounds.type_contains_range< signed long >(), signed long, std::conditional_t< bounds.type_contains_range< signed long long >(), signed long long, longreg_t > > > > > calculation_type
The type that is used as a temporary during calculation.
Definition bound_integer.hpp:47
constexpr std::strong_ordering operator<=>(bound_integer< RHSBounds > const &rhs) noexcept
Compare two integers.
Definition bound_integer.hpp:174
Interval arithmetic.
Definition interval.hpp:27
constexpr value_type lower() const noexcept
Get the lower bound of the interval.
Definition interval.hpp:87
constexpr value_type upper() const noexcept
Get the upper bound of the interval.
Definition interval.hpp:96
constexpr bool type_contains_range() const noexcept
Check if a given type can hold all values in the interval.
Definition interval.hpp:131
constexpr bool range_contains_type() const noexcept
Check if all the values in a type is inside the interval.
Definition interval.hpp:139
Definition concepts.hpp:18
T max(T... args)
T min(T... args)