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