HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
algorithm.hpp
1// Copyright Take Vos 2019-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 <algorithm>
9#include <tuple>
10#include <cmath>
11#include <iterator>
12
13namespace hi::inline v1 {
14
20template<typename T, typename U, typename F>
21inline T transform(const U& input, F operation)
22{
23 T result = {};
24 result.reserve(input.size());
25 std::transform(input.begin(), input.end(), std::back_inserter(result), operation);
26 return result;
27}
28
34template<typename T, std::size_t N, typename F>
35constexpr std::array<T, N> generate_array(F operation)
36{
38
39 for (std::size_t i = 0; i < N; i++) {
40 a.at(i) = operation(i);
41 }
42
43 return a;
44}
45
55template<typename It>
56constexpr It unordered_remove(It first, It last, It element)
57{
58 hi_axiom(first != last);
59
60 using std::swap;
61
62 auto new_last = last - 1;
63 swap(*element, *new_last);
64 return new_last;
65}
66
67template<typename It, typename UnaryPredicate>
68constexpr It rfind_if(It const first, It const last, UnaryPredicate predicate)
69{
70 auto i = last;
71 do {
72 i--;
73 if (predicate(*i)) {
74 return i;
75 }
76 } while (i != first);
77 return last;
78}
79
80template<typename It, typename UnaryPredicate>
81constexpr It rfind_if_not(It const first, It const last, UnaryPredicate predicate)
82{
83 return rfind_if(first, last, [&](hilet& x) {
84 return !predicate(x);
85 });
86}
87
88template<typename It, typename T>
89constexpr It rfind(It const first, It const last, T const& value)
90{
91 return rfind_if(first, last, [&](hilet& x) {
92 return x == value;
93 });
94}
95
103template<typename It, typename ItAny>
104[[nodiscard]] constexpr It find_any(It data_first, It data_last, ItAny value_first, ItAny value_last) noexcept
105{
106 return std::find_if(data_first, data_last, [value_first, value_last](hilet& data) {
107 return std::any_of(value_first, value_last, [&data](hilet& value) {
108 return data == value;
109 });
110 });
111}
112
119template<typename ConstIt, typename It, typename UnaryPredicate>
120constexpr It find_cluster(ConstIt last, It start, UnaryPredicate predicate)
121{
122 hilet cluster_id = predicate(*start);
123
124 for (auto i = start + 1; i != last; ++i) {
125 if (predicate(*i) != cluster_id) {
126 return i;
127 }
128 }
129 return last;
130}
131
138template<typename ConstIt, typename It, typename UnaryPredicate>
139constexpr It rfind_cluster(ConstIt first, It start, UnaryPredicate predicate)
140{
141 hilet cluster_id = predicate(*start);
142
143 if (start == first) {
144 return first;
145 }
146
147 auto i = start - 1;
148 while (true) {
149 if (predicate(*i) != cluster_id) {
150 return (i + 1);
151 }
152
153 if (i == first) {
154 return i;
155 }
156 --i;
157 }
158 hi_unreachable();
159}
160
168template<typename ConstIt, typename It, typename UnaryPredicate>
169constexpr std::pair<It, It> bifind_cluster(ConstIt first, ConstIt last, It start, UnaryPredicate predicate)
170{
171 return {rfind_cluster(first, start, predicate), find_cluster(last, start, predicate)};
172}
173
179template<typename It, typename S, typename F>
180inline void for_each_cluster(It first, It last, S IsClusterSeperator, F Function)
181{
182 if (first == last) {
183 return;
184 }
185
186 // If the first item is a cluster separator skip over it.
187 if (IsClusterSeperator(*first)) {
188 first++;
189 }
190
191 for (auto i = first; i != last;) {
192 auto j = std::find_if(i, last, IsClusterSeperator);
193 Function(i, j);
194
195 auto skipOverSeperator = (j == last) ? 0 : 1;
196 i = j + skipOverSeperator;
197 }
198}
199
200template<typename InputIt1, typename InputIt2, typename BinaryPredicate>
202rmismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate predicate) noexcept
203{
204 auto i1 = last1;
205 auto i2 = last2;
206
207 while (true) {
208 if (i1 == first1 && i2 == first2) {
209 return {last1, last2};
210 } else if (i1 == first1) {
211 return {last1, --i2};
212 } else if (i2 == first2) {
213 return {--i1, last2};
214 }
215
216 if (!predicate(*(--i1), *(--i2))) {
217 return {i1, i2};
218 }
219 }
220}
221
222template<typename InputIt1, typename InputIt2>
223inline std::pair<InputIt1, InputIt2> rmismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) noexcept
224{
225 return rmismatch(first1, last1, first2, last2, [&](auto a, auto b) {
226 return a == b;
227 });
228}
229
230template<typename T>
231T smoothstep(T x) noexcept
232{
233 x = std::clamp(x, T{0.0}, T{1.0});
234 return x * x * (3 - 2 * x);
235}
236
237template<typename T>
238T inverse_smoothstep(T x)
239{
240 return T{0.5} - std::sin(std::asin(T{1.0} - T{2.0} * x) / T{3.0});
241}
242
258auto shuffle_by_index(auto first, auto last, auto indices_first, auto indices_last, auto index_op) noexcept
259{
260 std::size_t src_size = std::distance(first, last);
261
262 // Keep track of index locations during shuffling of items.
263 auto src_indices = std::vector<std::size_t>{};
264 src_indices.reserve(src_size);
265 for (std::size_t i = 0; i != src_size; ++i) {
266 src_indices.push_back(i);
267 }
268
269 std::size_t dst = 0;
270 for (auto it = indices_first; it != indices_last; ++it, ++dst) {
271 hilet index = index_op(*it);
272 hi_assert_bounds(index, src_indices);
273
274 auto src = [&src_indices, index]() {
275 auto src = index;
276 do {
277 src = src_indices[src];
278 } while (src_indices[src] != index);
279 return src;
280 }();
281
282 if (src != dst) {
283 std::iter_swap(first + src, first + dst);
284 std::iter_swap(begin(src_indices) + src, begin(src_indices) + dst);
285 }
286 }
287
288 return first + dst;
289}
290
304auto shuffle_by_index(auto first, auto last, auto indices_first, auto indices_last) noexcept
305{
306 return shuffle_by_index(first, last, indices_first, indices_last, [](hilet& x) {
307 return narrow_cast<std::size_t>(x);
308 });
309}
310
319template<typename DataIt, typename ValueIt>
320DataIt front_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
321{
322 for (auto it = data_first; it != data_last; ++it) {
323 if (std::find(value_first, value_last, *it) == value_last) {
324 // Return the iterator pointing to the data that is not part of the value set.
325 return it;
326 }
327 }
328
329 return data_last;
330}
331
340template<typename DataIt, typename ValueIt>
341DataIt back_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
342{
343 auto it = data_last;
344 while (it != data_first) {
345 if (std::find(value_first, value_last, *(--it)) == value_last) {
346 // Return an iterator one beyond the data that is not part of the value set.
347 return it + 1;
348 }
349 }
350
351 return data_first;
352}
353
363template<std::endian Endian = std::endian::native, typename T, std::unsigned_integral Key>
364[[nodiscard]] constexpr T *fast_lower_bound(std::span<T> table, Key const& key) noexcept
365{
366 auto base = table.data();
367 auto len = table.size();
368
369 // A faster lower-bound search with less branches that are more predictable.
370 while (len > 1) {
371 hi_axiom_not_null(base);
372
373 hilet half = len / 2;
374
375 if (load<Key, Endian>(base + half - 1) < key) {
376 base += half;
377 }
378 len -= half;
379 }
380 if (load<Key, Endian>(base) < key) {
381 return nullptr;
382 }
383 return base;
384}
385
388template<std::endian Endian = std::endian::native, typename T, std::unsigned_integral Key>
389[[nodiscard]] constexpr T *fast_binary_search_eq(std::span<T> table, Key const& key) noexcept
390{
391 hilet ptr = fast_lower_bound<Endian>(table, key);
392 if (ptr == nullptr) {
393 return nullptr;
394 }
395
396 if (load<Key, Endian>(ptr) != key) {
397 return nullptr;
398 }
399
400 return ptr;
401}
402
403} // namespace hi::inline v1
#define hi_assert_bounds(x,...)
Assert if a value is within bounds.
Definition assert.hpp:210
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:238
#define hi_axiom_not_null(expression,...)
Assert if an expression is not nullptr.
Definition assert.hpp:257
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:13
constexpr It find_any(It data_first, It data_last, ItAny value_first, ItAny value_last) noexcept
Find the first occurrence of an value in a data.
Definition algorithm.hpp:104
auto shuffle_by_index(auto first, auto last, auto indices_first, auto indices_last, auto index_op) noexcept
Shuffle a container based on a list of indices.
Definition algorithm.hpp:258
DataIt front_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
Strip data from the front side.
Definition algorithm.hpp:320
constexpr It unordered_remove(It first, It last, It element)
Remove element from a container.
Definition algorithm.hpp:56
constexpr std::pair< It, It > bifind_cluster(ConstIt first, ConstIt last, It start, UnaryPredicate predicate)
Find the begin and end of the current cluster.
Definition algorithm.hpp:169
constexpr T * fast_lower_bound(std::span< T > table, Key const &key) noexcept
The fast lower bound algorithm.
Definition algorithm.hpp:364
constexpr It rfind_cluster(ConstIt first, It start, UnaryPredicate predicate)
Find the start of the current cluster.
Definition algorithm.hpp:139
constexpr It find_cluster(ConstIt last, It start, UnaryPredicate predicate)
Find the start of the current cluster.
Definition algorithm.hpp:120
DataIt back_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
Strip data from the back side.
Definition algorithm.hpp:341
constexpr T * fast_binary_search_eq(std::span< T > table, Key const &key) noexcept
Search for the item that is equal to the key.
Definition algorithm.hpp:389
constexpr std::array< T, N > generate_array(F operation)
Generate data in an array.
Definition algorithm.hpp:35
void for_each_cluster(It first, It last, S IsClusterSeperator, F Function)
Definition algorithm.hpp:180
T any_of(T... args)
T asin(T... args)
T at(T... args)
T back_inserter(T... args)
T distance(T... args)
T find_if(T... args)
T iter_swap(T... args)
T reserve(T... args)
T sin(T... args)
T swap(T... args)
T transform(T... args)