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