mimic++ v9.2.1
Loading...
Searching...
No Matches
Algorithm.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2024 - 2025.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_UTILITIES_ALGORITHM_HPP
7#define MIMICPP_UTILITIES_ALGORITHM_HPP
8
9#pragma once
10
13
14#ifndef MIMICPP_DETAIL_IS_MODULE
15 #include <algorithm>
16 #include <array>
17 #include <concepts>
18 #include <functional>
19 #include <iterator>
20 #include <ranges>
21 #include <string_view>
22 #include <tuple>
23 #include <type_traits>
24 #include <utility>
25#endif
26
28{
51 template <
52 std::ranges::random_access_range Target,
53 std::ranges::random_access_range Control,
54 typename Projection = std::identity,
55 std::indirect_unary_predicate<
56 std::projected<std::ranges::iterator_t<Control>, Projection>> Predicate>
57 requires std::ranges::sized_range<Target>
58 && std::ranges::sized_range<Control>
59 /*&& std::permutable<std::ranges::iterator_t<Target>>
60 && std::permutable<std::ranges::iterator_t<Control>>*/
61 [[nodiscard]]
62 constexpr std::ranges::borrowed_subrange_t<Target> partition_by(
63 Target && targetRange,
64 Control && controlRange,
65 Predicate predicate,
66 Projection projection = {})
67 {
68 MIMICPP_ASSERT(std::ranges::size(targetRange) == std::ranges::size(controlRange), "Size mismatch.");
69
70 auto const targetBegin = std::ranges::begin(targetRange);
71 auto const controlBegin = std::ranges::begin(controlRange);
72 auto midpoint = std::ranges::end(targetRange);
73 auto end = std::ranges::end(controlRange);
74 for (auto iter = std::ranges::find_if_not(controlRange, std::ref(predicate), std::ref(projection));
75 iter != end;
76 iter = std::ranges::find_if_not(iter, end, std::ref(predicate), std::ref(projection)))
77 {
78 auto const index = std::ranges::distance(controlBegin, iter);
79 std::ranges::iter_swap(iter, --end);
80 std::ranges::iter_swap(std::ranges::next(targetBegin, index), --midpoint);
81 }
82
83 return {std::move(midpoint), std::ranges::end(targetRange)};
84 }
85
98 template <std::ranges::borrowed_range Range>
99 [[nodiscard]]
100 constexpr std::ranges::borrowed_iterator_t<Range> find_closing_token(
101 Range && str,
102 std::ranges::range_value_t<Range> const& openingToken,
103 std::ranges::range_value_t<Range> const& closingToken)
104 {
105 auto const begin = std::ranges::begin(str);
106 auto const end = std::ranges::end(str);
107 auto closingIter = std::ranges::find(str, closingToken);
108 if (closingIter == end)
109 {
110 return closingIter;
111 }
112
113 for (auto openingIter = std::ranges::find(begin, closingIter, openingToken);
114 openingIter != closingIter
115 && closingIter != end;
116 openingIter = std::ranges::find(openingIter + 1, closingIter, openingToken))
117 {
118 closingIter = std::ranges::find(closingIter + 1, end, closingToken);
119 }
120
121 return closingIter;
122 }
123
138 template <std::ranges::borrowed_range Range>
139 [[nodiscard]]
140 constexpr std::ranges::borrowed_subrange_t<Range> find_next_unwrapped_token(
141 Range && str,
142 std::string_view const token,
143 std::ranges::forward_range auto&& opening,
144 std::ranges::forward_range auto&& closing)
145 {
146 using count_t = std::ranges::range_difference_t<Range>;
147 constexpr auto countAllOf = [](auto const& source, auto const& collection) {
148 count_t count{};
149 for (auto const c : collection)
150 {
151 count += std::ranges::count(source, c);
152 }
153
154 return count;
155 };
156
157 count_t openScopes{};
158 std::ranges::borrowed_subrange_t<Range> pending{str};
159 while (auto const match = std::ranges::search(pending, token))
160 {
161 // It's important to count the matched part, because the token may also be part of either the opening-
162 // or closing-collection. But count the match part with the opening collection after the test.
163 openScopes += countAllOf(std::ranges::subrange{pending.begin(), match.begin()}, opening);
164 openScopes -= countAllOf(std::ranges::subrange{pending.begin(), match.end()}, closing);
165 MIMICPP_ASSERT(0 <= openScopes, "More scopes closed than opened.");
166 if (0 == openScopes)
167 {
168 return match;
169 }
170
171 openScopes += countAllOf(match, opening);
172
173 pending = {match.end(), pending.end()};
174 }
175
176 return {std::ranges::end(str), std::ranges::end(str)};
177 }
178
189 template <std::ranges::forward_range Range, std::ranges::forward_range Prefix>
190 requires std::totally_ordered_with<std::ranges::range_value_t<Range>, Prefix>
191 [[nodiscard]]
192 constexpr std::ranges::borrowed_subrange_t<Range> prefix_range(Range && range, Prefix && prefix)
193 {
194 auto const lower = std::ranges::lower_bound(range, prefix);
195 auto const end = std::ranges::lower_bound(
196 lower,
197 std::ranges::end(range),
198 prefix,
199 [](auto const& element, auto const& p) {
200 auto const iter = std::ranges::mismatch(element, p).in2;
201 return iter == std::ranges::end(p);
202 });
203
204 return {lower, end};
205 }
206
216 template <std::copyable T, std::size_t firstN, std::size_t secondN>
217 [[nodiscard]]
218 constexpr std::array<T, firstN + secondN> concat_arrays(std::array<T, firstN> const& first, std::array<T, secondN> const& second)
219 {
220 return std::invoke(
221 [&]<std::size_t... firstIs, std::size_t... secondIs>(
222 [[maybe_unused]] std::index_sequence<firstIs...> const,
223 [[maybe_unused]] std::index_sequence<secondIs...> const) {
224 return std::array<T, firstN + secondN>{
225 std::get<firstIs>(first)...,
226 std::get<secondIs>(second)...};
227 },
228 std::make_index_sequence<firstN>{},
229 std::make_index_sequence<secondN>{});
230 }
231
241 template <std::copyable T, std::size_t firstN, typename... Others>
242 requires(... && std::same_as<T, std::ranges::range_value_t<Others>>)
243 // Not how I would like to formulate that constraint, but msvc does not accept it otherwise.
244 && (... && (0u <= std::tuple_size_v<Others>))
245 [[nodiscard]]
246 constexpr auto concat_arrays(std::array<T, firstN> const& first, Others const&... others)
247 {
248 if constexpr (0u == sizeof...(Others))
249 {
250 return first;
251 }
252 else
253 {
254 return concat_arrays(
255 first,
256 concat_arrays(others...));
257 }
258 }
259}
260
261namespace mimicpp::util::detail
262{
263 struct binary_find_fn
264 {
265 template <
266 std::forward_iterator Iterator,
267 std::sentinel_for<Iterator> Sentinel,
268 typename Projection = std::identity,
270 std::indirect_strict_weak_order<
271 T const*,
272 std::projected<Iterator, Projection>> Comparator = std::ranges::less>
273 [[nodiscard]]
274 constexpr Iterator operator()(
275 Iterator const first,
276 Sentinel const last,
277 T const& value,
278 Comparator compare = {},
279 Projection projection = {}) const
280 {
281 if (auto const iter = std::ranges::lower_bound(first, last, value, compare, projection);
282 iter != last
283 && !std::invoke(compare, value, std::invoke(projection, *iter)))
284 {
285 return iter;
286 }
287
288 return last;
289 }
290
291 template <
292 std::ranges::forward_range Range,
293 typename Projection = std::identity,
295 std::indirect_strict_weak_order<
296 T const*,
297 std::projected<std::ranges::iterator_t<Range>, Projection>> Comparator = std::ranges::less>
298 [[nodiscard]]
299 constexpr std::ranges::borrowed_iterator_t<Range> operator()(
300 Range&& range,
301 T const& value,
302 Comparator compare = {},
303 Projection projection = {}) const
304 {
305 return std::invoke(
306 *this,
307 std::ranges::begin(range),
308 std::ranges::end(range),
309 value,
310 std::move(compare),
311 std::move(projection));
312 }
313 };
314
315 struct contains_fn
316 {
317 template <
318 std::input_iterator Iterator,
319 std::sentinel_for<Iterator> Sentinel,
320 typename Projection = std::identity,
322 requires std::indirect_binary_predicate<
323 std::ranges::equal_to,
324 std::projected<Iterator, Projection>,
325 T const*>
326 [[nodiscard]]
327 constexpr bool operator()(Iterator first, Sentinel last, T const& value, Projection projection = {}) const
328 {
329 auto const iter = std::ranges::find(std::move(first), last, value, std::move(projection));
330 return iter != last;
331 }
332
333 template <
334 std::ranges::input_range Range,
335 typename Projection = std::identity,
337 requires std::indirect_binary_predicate<
338 std::ranges::equal_to,
339 std::projected<std::ranges::iterator_t<Range>, Projection>,
340 T const*>
341 [[nodiscard]]
342 constexpr bool operator()(Range&& r, T const& value, Projection projection = {}) const
343 {
344 return std::invoke(
345 *this,
346 std::ranges::begin(r),
347 std::ranges::end(r),
348 value,
349 std::move(projection));
350 }
351 };
352}
353
355{
361 inline constexpr detail::binary_find_fn binary_find{};
362
370 inline constexpr detail::contains_fn contains{};
371}
372
373#endif
#define MIMICPP_ASSERT(condition, msg)
Definition Config.hpp:51
#define MIMICPP_DETAIL_MODULE_EXPORT
Definition Config.hpp:19
Definition Fwd.hpp:445
std::remove_cvref_t< std::invoke_result_t< Projection &, std::iter_value_t< I > & > > projected_value_t
The alias template projected_value_t obtains the value type by stripping any reference and its topmos...
Definition C++26Backports.hpp:31
constexpr std::ranges::borrowed_subrange_t< Target > partition_by(Target &&targetRange, Control &&controlRange, Predicate predicate, Projection projection={})
Partitions the target range by the predicate results on the control range.
Definition Algorithm.hpp:62
constexpr std::ranges::borrowed_subrange_t< Range > prefix_range(Range &&range, Prefix &&prefix)
Returns a view containing all elements, which start with the given prefix.
Definition Algorithm.hpp:192
constexpr std::array< T, firstN+secondN > concat_arrays(std::array< T, firstN > const &first, std::array< T, secondN > const &second)
Concatenates the given arrays by copying all elements into a new array.
Definition Algorithm.hpp:218
constexpr detail::contains_fn contains
Determines, whether the specified value is contained in the given range.
Definition Algorithm.hpp:370
constexpr std::ranges::borrowed_iterator_t< Range > find_closing_token(Range &&str, std::ranges::range_value_t< Range > const &openingToken, std::ranges::range_value_t< Range > const &closingToken)
Finds the next closing token in the given string.
Definition Algorithm.hpp:100
constexpr std::ranges::borrowed_subrange_t< Range > find_next_unwrapped_token(Range &&str, std::string_view const token, std::ranges::forward_range auto &&opening, std::ranges::forward_range auto &&closing)
Finds the next unwrapped token in the given string.
Definition Algorithm.hpp:140
constexpr detail::binary_find_fn binary_find
Finds the specified value within the container and returns an iterator pointing to it....
Definition Algorithm.hpp:361