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