HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
interval.hpp
1// Copyright Take Vos 2020-2021.
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 "required.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 tt {
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 tt_axiom(holds_invariant());
55 }
56
62 [[nodiscard]] constexpr interval(value_type lower, value_type upper) noexcept :
63 v(lower, -upper)
64 {
65 tt_axiom(holds_invariant());
66 }
67
73 [[nodiscard]] static constexpr interval raw(bound_type bounds) noexcept
74 {
75 interval r;
76 r.v = bounds;
77 tt_axiom(r.holds_invariant());
78 return r;
79 }
80
81 [[nodiscard]] constexpr bool holds_invariant() const noexcept
82 {
83 return v[0] <= -v[1];
84 }
85
90 [[nodiscard]] constexpr value_type lower() const noexcept
91 {
92 return v[0];
93 }
94
99 [[nodiscard]] constexpr value_type upper() const noexcept
100 {
101 return -v[1];
102 }
103
108 [[nodiscard]] constexpr value_type delta() const noexcept
109 {
110 return upper() - lower();
111 }
112
117 [[nodiscard]] constexpr bool is_value() const noexcept
118 {
119 return delta() == 0;
120 }
121
126 [[nodiscard]] constexpr bool is_range() const noexcept
127 {
128 return delta() > 0;
129 }
130
133 template<numeric_limited T>
134 [[nodiscard]] constexpr bool type_contains_range() const noexcept
135 {
137 }
138
141 template<numeric_limited T>
142 [[nodiscard]] constexpr bool range_contains_type() const noexcept
143 {
145 }
146
151 operator bool () const noexcept
152 {
153 return v[0] != 0 or v[1] != 0;
154 }
155
156 [[nodiscard]] constexpr interval operator-() const noexcept
157 {
158 return interval::raw(v.yx());
159 }
160
161 [[nodiscard]] constexpr interval operator+(interval const &rhs) const noexcept
162 {
163 return interval::raw(v + rhs.v);
164 }
165
166 [[nodiscard]] constexpr interval operator-(interval const &rhs) const noexcept
167 {
168 return *this + (-rhs);
169 }
170
171 [[nodiscard]] constexpr interval operator*(interval const &rhs) const noexcept
172 {
173 ttlet ge_zero = ge(v, bound_type{});
174 ttlet lt_zero = ~ge_zero;
175
176 ttlet ac = (ge_zero & rhs.v.xx()) | (lt_zero & -rhs.v.yy());
177 ttlet db = (ge_zero & rhs.v.yy()) | (lt_zero & -rhs.v.xx());
178
179 ttlet ac_mul = ac * v;
180 ttlet db_mul = db * v;
181
182 return raw(min(ac_mul, db_mul.yx()));
183 }
184
187 [[nodiscard]] constexpr interval positive_mul(interval const &rhs) const noexcept
188 {
189 tt_axiom(v[0] >= 0 and rhs.v[0] >= 0);
190
191 return raw(v * neg<0b10>(rhs.v));
192 }
193
194 [[nodiscard]] constexpr interval operator/(interval const &rhs) const noexcept
195 {
196 if (rhs.v[0] <= 0 and 0 <= rhs.v[1]) {
197 // Return an infinite interval when it is possible to divide by zero.
198 return interval{};
199 }
200
201 ttlet rhs_ge_zero = ge(rhs.v, bound_type{});
202 ttlet rhs_lt_zero = ~rhs_ge_zero;
203
204 ttlet b_ma = (rhs_ge_zero & v.yy()) | (rhs_lt_zero & -v.xx());
205 ttlet a_mb = -b_ma.yx();
206
207 ttlet a_mb_mul = a_mb / rhs.v;
208 ttlet b_ma_mul = b_ma / rhs.v;
209
210 return raw(min(a_mb, b_ma));
211 }
212
213 [[nodiscard]] constexpr interval operator%(interval const &rhs) const noexcept
214 {
215 if (rhs.v[0] <= 0 and 0 <= rhs.v[1]) {
216 // Return an infinite interval when it is possible to divide by zero.
217 return interval{};
218 }
219
220 // In C++ the sign of the modulo operator's result is the same as the left operand.
221 ttlet rhs_abs = abs(rhs);
222 if (v[0] > 0) {
223 return rhs_abs;
224 } else if (v[1] > 0) {
225 return -rhs_abs;
226 } else {
227 return raw(rhs_abs.yy());
228 }
229 }
230
231 [[nodiscard]] friend constexpr interval reciprocal(interval const &rhs) noexcept
232 {
233 if (rhs.v[0] <= 0 and 0 <= rhs.v[1]) {
234 // Return an infinite interval when it is possible to divide by zero.
235 return interval{};
236 }
237
238 return raw(bound_type::broadcast(value_type{-1}) / rhs.yx());
239 }
240
241 [[nodiscard]] friend constexpr interval abs(interval const &rhs) noexcept
242 {
243 bound_type r;
244 r[0] = std::max({value_type{0}, rhs.v[0], rhs.v[1]});
245 r[1] = std::min(rhs.v[0], rhs.v[1]);
246 return raw(r);
247 }
248
249 [[nodiscard]] friend constexpr interval square(interval const &rhs) noexcept
250 {
251 ttlet abs_rhs = abs(rhs);
252 return abs_rhs.positive_mul(abs_rhs);
253 }
254
255 interval &operator+=(interval const &rhs) noexcept
256 {
257 return *this = *this + rhs;
258 }
259
260 interval &operator-=(interval const &rhs) noexcept
261 {
262 return *this = *this - rhs;
263 }
264
265 interval &operator*=(interval const &rhs) noexcept
266 {
267 return *this = *this * rhs;
268 }
269
270 [[nodiscard]] friend constexpr bool operator==(value_type const &lhs, interval const &rhs) noexcept
271 {
272 return rhs.lower() <= lhs and lhs <= rhs.upper();
273 }
274
275 [[nodiscard]] friend constexpr auto operator<=>(value_type const &lhs, interval const &rhs) noexcept
276 {
277 if constexpr (std::is_floating_point_v<value_type>) {
278 if (lhs < rhs.lower()) {
279 return std::partial_ordering::less;
280 } else if (lhs > rhs.upper()) {
281 return std::partial_ordering::greater;
282 } else if (lhs >= rhs.lower() and lhs <= rhs.upper()) {
283 return std::partial_ordering::equivalent;
284 } else {
285 return std::partial_ordering::unordered;
286 }
287 } else {
288 if (lhs < rhs.lower()) {
289 return std::weak_ordering::less;
290 } else if (lhs > rhs.upper()) {
291 return std::weak_ordering::greater;
292 } else {
293 return std::weak_ordering::equivalent;
294 }
295 }
296 }
297
303 [[nodiscard]] constexpr bool is_fully_inside(interval const &other) const noexcept
304 {
305 return ge(v, other.v) == 0b11;
306 }
307
308};
309
310using finterval = interval<float>;
311using dinterval = interval<double>;
312
313} // namespace tt
Interval arithmetic.
Definition interval.hpp:29
constexpr interval(value_type lower, value_type upper) noexcept
Construct an interval from a lower and upper bounds.
Definition interval.hpp:62
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:117
constexpr bool range_contains_type() const noexcept
Check if all the values in a type is inside the interval.
Definition interval.hpp:142
constexpr interval positive_mul(interval const &rhs) const noexcept
Multiply two positive intervals.
Definition interval.hpp:187
constexpr value_type delta() const noexcept
The distance between lower and upper bound.
Definition interval.hpp:108
constexpr bool type_contains_range() const noexcept
Check if a given type can hold all values in the interval.
Definition interval.hpp:134
constexpr value_type upper() const noexcept
Get the upper bound of the interval.
Definition interval.hpp:99
constexpr bool is_range() const noexcept
Check if the interval is a range of values.
Definition interval.hpp:126
static constexpr interval raw(bound_type bounds) noexcept
Construct an interval from a bound_type value.
Definition interval.hpp:73
constexpr value_type lower() const noexcept
Get the lower bound of the interval.
Definition interval.hpp:90
constexpr bool is_fully_inside(interval const &other) const noexcept
Check if the current interval is fully inside the other interval.
Definition interval.hpp:303
T max(T... args)
T min(T... args)