HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
interval.hpp
1// Copyright Take Vos 2020-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.hpp"
8#include "assert.hpp"
9#include "concepts.hpp"
10#include "rapid/numeric_array.hpp"
11#include <type_traits>
12#include <limits>
13#include <concepts>
14#include <algorithm>
15#include <compare>
16
17namespace hi::inline v1 {
18
28template<numeric_limited T>
29struct interval {
30public:
31 using value_type = T;
33
34 bound_type v;
35
36 constexpr interval(interval const &rhs) noexcept = default;
37 constexpr interval(interval &&rhs) noexcept = default;
38 constexpr interval &operator=(interval const &rhs) noexcept = default;
39 constexpr interval &operator=(interval &&rhs) noexcept = default;
40
45 constexpr interval() noexcept
46 {
47 if constexpr (std::is_floating_point_v<value_type>) {
50 } else {
53 }
54 hi_axiom(holds_invariant());
55 }
56
62 [[nodiscard]] constexpr interval(value_type lower, value_type upper) noexcept : v(lower, -upper)
63 {
64 hi_axiom(holds_invariant());
65 }
66
72 [[nodiscard]] static constexpr interval raw(bound_type bounds) noexcept
73 {
74 interval r;
75 r.v = bounds;
76 hi_axiom(r.holds_invariant());
77 return r;
78 }
79
80 [[nodiscard]] constexpr bool holds_invariant() const noexcept
81 {
82 return v[0] <= -v[1];
83 }
84
89 [[nodiscard]] constexpr value_type lower() const noexcept
90 {
91 return v[0];
92 }
93
98 [[nodiscard]] constexpr value_type upper() const noexcept
99 {
100 return -v[1];
101 }
102
107 [[nodiscard]] constexpr value_type delta() const noexcept
108 {
109 return upper() - lower();
110 }
111
116 [[nodiscard]] constexpr bool is_value() const noexcept
117 {
118 return delta() == 0;
119 }
120
125 [[nodiscard]] constexpr bool is_range() const noexcept
126 {
127 return delta() > 0;
128 }
129
132 template<numeric_limited T>
133 [[nodiscard]] constexpr bool type_contains_range() const noexcept
134 {
135 return std::numeric_limits<T>::lower() <= lower() and upper() <= std::numeric_limits<T>::max();
136 }
137
140 template<numeric_limited T>
141 [[nodiscard]] constexpr bool range_contains_type() const noexcept
142 {
143 return lower() <= std::numeric_limits<T>::lower() and std::numeric_limits<T>::max() <= upper();
144 }
145
150 operator bool() const noexcept
151 {
152 return v[0] != 0 or v[1] != 0;
153 }
154
155 [[nodiscard]] constexpr interval operator-() const noexcept
156 {
157 return interval::raw(v.yx());
158 }
159
160 [[nodiscard]] constexpr interval operator+(interval const &rhs) const noexcept
161 {
162 return interval::raw(v + rhs.v);
163 }
164
165 [[nodiscard]] constexpr interval operator-(interval const &rhs) const noexcept
166 {
167 return *this + (-rhs);
168 }
169
170 [[nodiscard]] constexpr interval operator*(interval const &rhs) const noexcept
171 {
172 hilet ge_zero = ge(v, bound_type{});
173 hilet lt_zero = ~ge_zero;
174
175 hilet ac = (ge_zero & rhs.v.xx()) | (lt_zero & -rhs.v.yy());
176 hilet db = (ge_zero & rhs.v.yy()) | (lt_zero & -rhs.v.xx());
177
178 hilet ac_mul = ac * v;
179 hilet db_mul = db * v;
180
181 return raw(min(ac_mul, db_mul.yx()));
182 }
183
186 [[nodiscard]] constexpr interval positive_mul(interval const &rhs) const noexcept
187 {
188 hi_axiom(v[0] >= 0 and rhs.v[0] >= 0);
189
190 return raw(v * neg<0b10>(rhs.v));
191 }
192
193 [[nodiscard]] constexpr interval operator/(interval const &rhs) const noexcept
194 {
195 if (rhs.v[0] <= 0 and 0 <= rhs.v[1]) {
196 // Return an infinite interval when it is possible to divide by zero.
197 return interval{};
198 }
199
200 hilet rhs_ge_zero = ge(rhs.v, bound_type{});
201 hilet rhs_lt_zero = ~rhs_ge_zero;
202
203 hilet b_ma = (rhs_ge_zero & v.yy()) | (rhs_lt_zero & -v.xx());
204 hilet a_mb = -b_ma.yx();
205
206 hilet a_mb_mul = a_mb / rhs.v;
207 hilet b_ma_mul = b_ma / rhs.v;
208
209 return raw(min(a_mb, b_ma));
210 }
211
212 [[nodiscard]] constexpr interval operator%(interval const &rhs) const noexcept
213 {
214 if (rhs.v[0] <= 0 and 0 <= rhs.v[1]) {
215 // Return an infinite interval when it is possible to divide by zero.
216 return interval{};
217 }
218
219 // In C++ the sign of the modulo operator's result is the same as the left operand.
220 hilet rhs_abs = abs(rhs);
221 if (v[0] > 0) {
222 return rhs_abs;
223 } else if (v[1] > 0) {
224 return -rhs_abs;
225 } else {
226 return raw(rhs_abs.yy());
227 }
228 }
229
230 [[nodiscard]] friend constexpr interval reciprocal(interval const &rhs) noexcept
231 {
232 if (rhs.v[0] <= 0 and 0 <= rhs.v[1]) {
233 // Return an infinite interval when it is possible to divide by zero.
234 return interval{};
235 }
236
237 return raw(bound_type::broadcast(value_type{-1}) / rhs.yx());
238 }
239
240 [[nodiscard]] friend constexpr interval abs(interval const &rhs) noexcept
241 {
242 bound_type r;
243 r[0] = std::max({value_type{0}, rhs.v[0], rhs.v[1]});
244 r[1] = std::min(rhs.v[0], rhs.v[1]);
245 return raw(r);
246 }
247
248 [[nodiscard]] friend constexpr interval square(interval const &rhs) noexcept
249 {
250 hilet abs_rhs = abs(rhs);
251 return abs_rhs.positive_mul(abs_rhs);
252 }
253
254 interval &operator+=(interval const &rhs) noexcept
255 {
256 return *this = *this + rhs;
257 }
258
259 interval &operator-=(interval const &rhs) noexcept
260 {
261 return *this = *this - rhs;
262 }
263
264 interval &operator*=(interval const &rhs) noexcept
265 {
266 return *this = *this * rhs;
267 }
268
269 [[nodiscard]] friend constexpr bool operator==(value_type const &lhs, interval const &rhs) noexcept
270 {
271 return rhs.lower() <= lhs and lhs <= rhs.upper();
272 }
273
274 [[nodiscard]] friend constexpr auto operator<=>(value_type const &lhs, interval const &rhs) noexcept
275 {
276 if constexpr (std::is_floating_point_v<value_type>) {
277 if (lhs < rhs.lower()) {
278 return std::partial_ordering::less;
279 } else if (lhs > rhs.upper()) {
280 return std::partial_ordering::greater;
281 } else if (lhs >= rhs.lower() and lhs <= rhs.upper()) {
282 return std::partial_ordering::equivalent;
283 } else {
284 return std::partial_ordering::unordered;
285 }
286 } else {
287 if (lhs < rhs.lower()) {
288 return std::weak_ordering::less;
289 } else if (lhs > rhs.upper()) {
290 return std::weak_ordering::greater;
291 } else {
292 return std::weak_ordering::equivalent;
293 }
294 }
295 }
296
302 [[nodiscard]] constexpr bool is_fully_inside(interval const &other) const noexcept
303 {
304 return ge(v, other.v) == 0b11;
305 }
306};
307
308using finterval = interval<float>;
309using dinterval = interval<double>;
310
311} // namespace hi::inline v1
Utilities to assert and bound check.
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
@ square
<square> asian compatibility forms.
Interval arithmetic.
Definition interval.hpp:29
constexpr bool is_range() const noexcept
Check if the interval is a range of values.
Definition interval.hpp:125
constexpr interval(value_type lower, value_type upper) noexcept
Construct an interval from a lower and upper bounds.
Definition interval.hpp:62
constexpr interval positive_mul(interval const &rhs) const noexcept
Multiply two positive intervals.
Definition interval.hpp:186
constexpr value_type delta() const noexcept
The distance between lower and upper bound.
Definition interval.hpp:107
constexpr value_type lower() const noexcept
Get the lower bound of the interval.
Definition interval.hpp:89
static constexpr interval raw(bound_type bounds) noexcept
Construct an interval from a bound_type value.
Definition interval.hpp:72
constexpr value_type upper() const noexcept
Get the upper bound of the interval.
Definition interval.hpp:98
constexpr bool is_fully_inside(interval const &other) const noexcept
Check if the current interval is fully inside the other interval.
Definition interval.hpp:302
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
constexpr interval() noexcept
Default construct an interval.
Definition interval.hpp:45
constexpr bool is_value() const noexcept
Check if the interval is one value.
Definition interval.hpp:116
T max(T... args)
T min(T... args)