HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
algorithm_misc.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/utility.hpp"
8#include "../macros.hpp"
9#include <algorithm>
10#include <tuple>
11#include <cmath>
12#include <iterator>
13#include <bit>
14#include <span>
15#include <vector>
16#include <cstddef>
17
18hi_export_module(hikogui.algorithm.algorithm_misc);
19
20hi_export namespace hi::inline v1 {
21
27template<typename T, typename U, typename F>
28inline T transform(const U& input, F operation)
29{
30 T result = {};
31 result.reserve(input.size());
32 std::transform(input.begin(), input.end(), std::back_inserter(result), operation);
33 return result;
34}
35
41template<typename T, std::size_t N, typename F>
42constexpr std::array<T, N> generate_array(F operation)
43{
45
46 for (std::size_t i = 0; i < N; i++) {
47 a.at(i) = operation(i);
48 }
49
50 return a;
51}
52
62template<typename It>
63constexpr It unordered_remove(It first, It last, It element)
64{
65 hi_axiom(first != last);
66
67 using std::swap;
68
69 auto new_last = last - 1;
70 swap(*element, *new_last);
71 return new_last;
72}
73
74template<typename It, typename UnaryPredicate>
75constexpr It rfind_if(It const first, It const last, UnaryPredicate predicate)
76{
77 auto i = last;
78 do {
79 i--;
80 if (predicate(*i)) {
81 return i;
82 }
83 } while (i != first);
84 return last;
85}
86
87template<typename It, typename UnaryPredicate>
88constexpr It rfind_if_not(It const first, It const last, UnaryPredicate predicate)
89{
90 return rfind_if(first, last, [&](auto const& x) {
91 return !predicate(x);
92 });
93}
94
95template<typename It, typename T>
96constexpr It rfind(It const first, It const last, T const& value)
97{
98 return rfind_if(first, last, [&](auto const& x) {
99 return x == value;
100 });
101}
102
110template<typename It, typename ItAny>
111[[nodiscard]] constexpr It find_any(It data_first, It data_last, ItAny value_first, ItAny value_last) noexcept
112{
113 return std::find_if(data_first, data_last, [value_first, value_last](auto const& data) {
114 return std::any_of(value_first, value_last, [&data](auto const& value) {
115 return data == value;
116 });
117 });
118}
119
126template<typename ConstIt, typename It, typename UnaryPredicate>
127constexpr It find_cluster(ConstIt last, It start, UnaryPredicate predicate)
128{
129 auto const cluster_id = predicate(*start);
130
131 for (auto i = start + 1; i != last; ++i) {
132 if (predicate(*i) != cluster_id) {
133 return i;
134 }
135 }
136 return last;
137}
138
145template<typename ConstIt, typename It, typename UnaryPredicate>
146constexpr It rfind_cluster(ConstIt first, It start, UnaryPredicate predicate)
147{
148 auto const cluster_id = predicate(*start);
149
150 if (start == first) {
151 return first;
152 }
153
154 auto i = start - 1;
155 while (true) {
156 if (predicate(*i) != cluster_id) {
157 return (i + 1);
158 }
159
160 if (i == first) {
161 return i;
162 }
163 --i;
164 }
165 std::unreachable();
166}
167
175template<typename ConstIt, typename It, typename UnaryPredicate>
176constexpr std::pair<It, It> bifind_cluster(ConstIt first, ConstIt last, It start, UnaryPredicate predicate)
177{
178 return {rfind_cluster(first, start, predicate), find_cluster(last, start, predicate)};
179}
180
186template<typename It, typename S, typename F>
187inline void for_each_cluster(It first, It last, S IsClusterSeperator, F Function)
188{
189 if (first == last) {
190 return;
191 }
192
193 // If the first item is a cluster separator skip over it.
194 if (IsClusterSeperator(*first)) {
195 first++;
196 }
197
198 for (auto i = first; i != last;) {
199 auto j = std::find_if(i, last, IsClusterSeperator);
200 Function(i, j);
201
202 auto skipOverSeperator = (j == last) ? 0 : 1;
203 i = j + skipOverSeperator;
204 }
205}
206
207template<typename InputIt1, typename InputIt2, typename BinaryPredicate>
209rmismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate predicate) noexcept
210{
211 auto i1 = last1;
212 auto i2 = last2;
213
214 while (true) {
215 if (i1 == first1 && i2 == first2) {
216 return {last1, last2};
217 } else if (i1 == first1) {
218 return {last1, --i2};
219 } else if (i2 == first2) {
220 return {--i1, last2};
221 }
222
223 if (!predicate(*(--i1), *(--i2))) {
224 return {i1, i2};
225 }
226 }
227}
228
229template<typename InputIt1, typename InputIt2>
230inline std::pair<InputIt1, InputIt2> rmismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) noexcept
231{
232 return rmismatch(first1, last1, first2, last2, [&](auto a, auto b) {
233 return a == b;
234 });
235}
236
237template<typename T>
238T smoothstep(T x) noexcept
239{
240 x = std::clamp(x, T{0.0}, T{1.0});
241 return x * x * (3 - 2 * x);
242}
243
244template<typename T>
245T inverse_smoothstep(T x)
246{
247 return T{0.5} - std::sin(std::asin(T{1.0} - T{2.0} * x) / T{3.0});
248}
249
265auto shuffle_by_index(auto first, auto last, auto indices_first, auto indices_last, auto index_op) noexcept
266{
267 std::size_t src_size = std::distance(first, last);
268
269 // Keep track of index locations during shuffling of items.
270 auto src_indices = std::vector<std::size_t>{};
271 src_indices.reserve(src_size);
272 for (std::size_t i = 0; i != src_size; ++i) {
273 src_indices.push_back(i);
274 }
275
276 std::size_t dst = 0;
277 for (auto it = indices_first; it != indices_last; ++it, ++dst) {
278 auto const index = index_op(*it);
279 hi_assert_bounds(index, src_indices);
280
281 auto src = [&src_indices, index]() {
282 auto src = index;
283 do {
284 src = src_indices[src];
285 } while (src_indices[src] != index);
286 return src;
287 }();
288
289 if (src != dst) {
290 std::iter_swap(first + src, first + dst);
291 std::iter_swap(begin(src_indices) + src, begin(src_indices) + dst);
292 }
293 }
294
295 return first + dst;
296}
297
311auto shuffle_by_index(auto first, auto last, auto indices_first, auto indices_last) noexcept
312{
313 return shuffle_by_index(first, last, indices_first, indices_last, [](auto const& x) {
314 return narrow_cast<std::size_t>(x);
315 });
316}
317
326template<typename DataIt, typename ValueIt>
327DataIt front_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
328{
329 for (auto it = data_first; it != data_last; ++it) {
330 if (std::find(value_first, value_last, *it) == value_last) {
331 // Return the iterator pointing to the data that is not part of the value set.
332 return it;
333 }
334 }
335
336 return data_last;
337}
338
347template<typename DataIt, typename ValueIt>
348DataIt back_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
349{
350 auto it = data_last;
351 while (it != data_first) {
352 if (std::find(value_first, value_last, *(--it)) == value_last) {
353 // Return an iterator one beyond the data that is not part of the value set.
354 return it + 1;
355 }
356 }
357
358 return data_first;
359}
360
370template<std::endian Endian = std::endian::native, typename T, std::unsigned_integral Key>
371[[nodiscard]] constexpr T *fast_lower_bound(std::span<T> table, Key const& key) noexcept
372{
373 auto base = table.data();
374 auto len = table.size();
375
376 // A faster lower-bound search with less branches that are more predictable.
377 while (len > 1) {
378 hi_axiom_not_null(base);
379
380 auto const half = len / 2;
381
382 if (load<Key, Endian>(base + half - 1) < key) {
383 base += half;
384 }
385 len -= half;
386 }
387 if (load<Key, Endian>(base) < key) {
388 return nullptr;
389 }
390 return base;
391}
392
395template<std::endian Endian = std::endian::native, typename T, std::unsigned_integral Key>
396[[nodiscard]] constexpr T *fast_binary_search_eq(std::span<T> table, Key const& key) noexcept
397{
398 auto const ptr = fast_lower_bound<Endian>(table, key);
399 if (ptr == nullptr) {
400 return nullptr;
401 }
402
403 if (load<Key, Endian>(ptr) != key) {
404 return nullptr;
405 }
406
407 return ptr;
408}
409
410} // namespace hi::inline v1
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
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_misc.hpp:111
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_misc.hpp:265
DataIt front_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
Strip data from the front side.
Definition algorithm_misc.hpp:327
constexpr It unordered_remove(It first, It last, It element)
Remove element from a container.
Definition algorithm_misc.hpp:63
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_misc.hpp:176
constexpr T * fast_lower_bound(std::span< T > table, Key const &key) noexcept
The fast lower bound algorithm.
Definition algorithm_misc.hpp:371
constexpr It rfind_cluster(ConstIt first, It start, UnaryPredicate predicate)
Find the start of the current cluster.
Definition algorithm_misc.hpp:146
constexpr It find_cluster(ConstIt last, It start, UnaryPredicate predicate)
Find the start of the current cluster.
Definition algorithm_misc.hpp:127
DataIt back_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
Strip data from the back side.
Definition algorithm_misc.hpp:348
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_misc.hpp:396
constexpr std::array< T, N > generate_array(F operation)
Generate data in an array.
Definition algorithm_misc.hpp:42
void for_each_cluster(It first, It last, S IsClusterSeperator, F Function)
Definition algorithm_misc.hpp:187
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)