1#ifndef TATOOINE_FOR_LOOP_H
2#define TATOOINE_FOR_LOOP_H
4#include <tatooine/available_libraries.h>
12#include <boost/range/algorithm/transform.hpp>
14#if TATOOINE_OPENMP_AVAILABLE
16#define TATOOINE_PARALLEL_FOR_LOOPS_AVAILABLE 1
18#define TATOOINE_PARALLEL_FOR_LOOPS_AVAILABLE 0
23#if TATOOINE_PARALLEL_FOR_LOOPS_AVAILABLE
29#if TATOOINE_PARALLEL_FOR_LOOPS_AVAILABLE
31 auto n = std::size_t{};
34 if (omp_get_thread_num()) {
35 n =
static_cast<std::size_t
>(omp_get_num_threads());
48namespace detail::for_loop {
56template <
typename Int, std::
size_t N, std::
size_t I, std::
size_t ParallelIndex>
71 template <std::size_t... IndexSequence,
72 invocable<
decltype(((void)IndexSequence, Int{}))...> Iteration>
73 constexpr auto loop(Iteration&& iteration,
74 std::index_sequence<IndexSequence...> )
const {
77 std::invoke_result_t<Iteration,
78 decltype(((void)IndexSequence, Int{}))...>;
79 constexpr bool returns_void = is_same<return_type, void>;
80 constexpr bool returns_bool = is_same<return_type, bool>;
81 static_assert(returns_void || returns_bool);
84 if constexpr (returns_void) {
93 m_ends}(std::forward<Iteration>(iteration))) {
100 if constexpr (returns_bool) {
107 template <
typename Iteration>
109 return loop(std::forward<Iteration>(iteration),
110 std::make_index_sequence<N>{});
117template <
typename Int, std::
size_t N, std::
size_t ParallelIndex>
131 template <std::size_t... IndexSequence,
132 invocable<
decltype(((void)IndexSequence, Int{}))...> Iteration>
133 constexpr auto loop(Iteration&& iteration,
134 std::index_sequence<IndexSequence...> )
const {
137 std::invoke_result_t<Iteration,
138 decltype(((void)IndexSequence, Int{}))...>;
139 constexpr auto returns_void = is_same<return_type, void>;
140 constexpr auto returns_bool = is_same<return_type, bool>;
141 static_assert(returns_void || returns_bool);
143 m_status[0] = m_begins[0];
144 for (; m_status[0] < m_ends[0]; ++m_status[0]) {
145 if constexpr (returns_void) {
147 iteration(m_status[IndexSequence]...);
151 if (!iteration(m_status[IndexSequence]...)) {
156 if constexpr (returns_bool) {
163 template <
typename Iteration>
165 return loop(std::forward<Iteration>(iteration),
166 std::make_index_sequence<N>{});
169#if TATOOINE_PARALLEL_FOR_LOOPS_AVAILABLE
175template <
typename Int, std::
size_t N, std::
size_t I>
190 template <std::size_t... IndexSequence,
191 invocable<
decltype(((void)IndexSequence, Int{}))...> Iteration>
192 auto loop(Iteration&& iteration,
193 std::index_sequence<IndexSequence...> )
const {
196 std::invoke_result_t<Iteration,
197 decltype(((void)IndexSequence, Int{}))...>;
198 static constexpr auto returns_void = is_same<return_type, void>;
199 static constexpr auto returns_bool = is_same<return_type, bool>;
200 static_assert(returns_void || returns_bool);
202#pragma omp parallel for
203 for (Int i = m_begins[I - 1]; i < m_ends[I - 1]; ++i) {
204 auto status_copy = m_status;
205 status_copy[I - 1] = i;
206 if constexpr (returns_void) {
209 status_copy, m_begins, m_ends}(std::forward<Iteration>(iteration));
214 status_copy, m_begins, m_ends}(std::forward<Iteration>(iteration));
215 assert(cont &&
"cannot break in parallel loop");
218 if constexpr (returns_bool) {
225 template <
typename Iteration>
227 return loop(std::forward<Iteration>(iteration),
228 std::make_index_sequence<N>{});
236template <
typename Int, std::
size_t N>
250 template <std::size_t... IndexSequence,
251 invocable<
decltype(((void)IndexSequence, Int{}))...> Iteration>
252 auto loop(Iteration&& iteration,
253 std::index_sequence<IndexSequence...> )
const {
256 std::invoke_result_t<Iteration,
257 decltype(((void)IndexSequence, Int{}))...>;
258 constexpr bool returns_void = is_same<return_type, void>;
259 constexpr bool returns_bool = is_same<return_type, bool>;
260 static_assert(returns_void || returns_bool);
262#pragma omp parallel for
263 for (Int i = m_begins[0]; i < m_ends[0]; ++i) {
264 auto status_copy = m_status;
266 if constexpr (returns_void) {
268 iteration(status_copy[IndexSequence]...);
272 auto const cont = iteration(status_copy[IndexSequence]...);
273 assert(cont &&
"cannot break in parallel loop");
276 if constexpr (returns_bool) {
283 template <
typename Iteration>
285 return loop(std::forward<Iteration>(iteration),
286 std::make_index_sequence<N>{});
291template <std::size_t ParallelIndex,
typename Int, Int... IndexSequence,
293 invocable<
decltype(((void)IndexSequence, Int{}))...> Iteration>
295 std::integer_sequence<Int, IndexSequence...>,
296 std::pair<Ranges, Ranges>
const&... ranges) {
299 std::invoke_result_t<Iteration,
300 decltype(((void)IndexSequence, Int{}))...>;
301 constexpr bool returns_void = is_same<return_type, void>;
302 constexpr bool returns_bool = is_same<return_type, bool>;
303 static_assert(returns_void || returns_bool);
306 std::array{((void)IndexSequence,
static_cast<Int
>(ranges.first))...};
308 std::array{((void)IndexSequence,
static_cast<Int
>(ranges.first))...};
309 auto const ends = std::array{
static_cast<Int
>(ranges.second)...};
310 return for_loop_impl<Int,
sizeof...(ranges),
sizeof...(ranges),
312 status, begins, ends}(std::forward<Iteration>(iteration));
317template <
typename Iteration,
typename Range>
319 std::invocable<Iteration, std::ranges::iterator_t<Range>> &&
323template <
typename Iteration,
typename IntRange>
325 std::invocable<Iteration, IntRange> &&
335template <
typename Int = std::size_t,
typename Iteration,
integral... Ranges>
337 Ranges (&&... ranges)[2]) ->
void {
339 std::forward<Iteration>(iteration),
340 std::make_integer_sequence<Int,
sizeof...(ranges)>{},
341 std::pair{ranges[0], ranges[1]}...);
351template <
typename Int = std::size_t,
typename Iteration, integral... Ranges>
353 std::pair<Ranges, Ranges>
const&... ranges) ->
void {
355 std::forward<Iteration>(iteration),
356 std::make_integer_sequence<Int,
sizeof...(ranges)>{}, ranges...);
366template <
typename Int = std::size_t,
typename Iteration, integral... Ends>
368 Ends
const... ends) ->
void {
370 std::forward<Iteration>(iteration),
371 std::make_integer_sequence<Int,
sizeof...(ends)>{},
372 std::pair{Ends(0), ends}...);
382template <
typename Int = std::size_t,
typename Iteration, integral... Ranges>
383constexpr auto for_loop([[maybe_unused]] Iteration&& iteration,
385 [[maybe_unused]] Ranges (&&... ranges)[2]) ->
void
388#if TATOOINE_PARALLEL_FOR_LOOPS_AVAILABLE
390 std::forward<Iteration>(iteration),
391 std::make_integer_sequence<Int,
sizeof...(ranges)>{},
392 std::pair{ranges[0], ranges[1]}...);
403template <
typename Int = std::size_t,
typename Iteration, integral... Ranges>
405 [[maybe_unused]] Iteration&& iteration,
407 [[maybe_unused]] std::pair<Ranges, Ranges>
const&... ranges) ->
void
410#if TATOOINE_PARALLEL_FOR_LOOPS_AVAILABLE
412 std::forward<Iteration>(iteration),
413 std::make_integer_sequence<Int,
sizeof...(ranges)>{}, ranges...);
424template <
typename Int = std::size_t,
typename Iteration, integral... Ends>
425constexpr auto for_loop([[maybe_unused]] Iteration&& iteration,
427 [[maybe_unused]] Ends
const... ends) ->
void
430#if TATOOINE_PARALLEL_FOR_LOOPS_AVAILABLE
432 std::forward<Iteration>(iteration),
433 std::make_integer_sequence<Int,
sizeof...(ends)>{},
434 std::pair{Ends(0), ends}...);
445template <
typename Int = std::size_t,
typename Iteration, integral... Ranges>
446constexpr auto for_loop(Iteration&& iteration, Ranges (&&... ranges)[2])
449 std::pair{ranges[0], ranges[1]}...);
459template <
typename Int = std::size_t,
typename Iteration, integral... Ranges>
461 std::pair<Ranges, Ranges>
const&... ranges) ->
void {
473template <
typename Int = std::size_t,
typename Iteration, integral... Ends>
474constexpr auto for_loop(Iteration&& iteration, Ends
const... ends) ->
void {
479template <
typename Iteration,
integral Int, std::
size_t N>
481 std::array<Int, N>
const& sizes) {
483 [&](
auto const... is) {
484 for_loop(std::forward<Iteration>(iteration), policy, is...);
489template <
typename Iteration,
integral Int, std::
size_t N>
495template <
typename Int = std::size_t, integral... Ends>
497 invocable<
decltype(((
void)std::declval<Ends>(),
498 std::declval<Int>()))...>
auto&& iteration,
500 Ends
const... ends) ->
void {
502 [&](
auto const... chunk_is) {
504 [&](
auto const... inner_is) {
505 iteration((chunk_is * chunk_size + inner_is)...);
507 policy, std::min<Int>(chunk_size, chunk_is * chunk_size - ends)...);
509 ends / chunk_size...);
512template <
typename Int = std::size_t,
typename Iteration, integral... Ends>
513requires invocable<Iteration,
514 decltype((std::declval<Ends>(), std::declval<Int>()))...>
517 Ends
const... ends) ->
void {
524template <integral_pair_range IntPairRange,
525 for_loop_nested_index_iteration<std::vector<
common_type<
526 typename std::ranges::range_value_t<IntPairRange>::first_type,
527 typename std::ranges::range_value_t<IntPairRange>::second_type>>>
529auto for_loop(Iteration&& iteration, IntPairRange
const& ranges,
531 using integral_pair_t = std::ranges::range_value_t<IntPairRange>;
532 using int_t =
common_type<
typename integral_pair_t::first_type,
533 typename integral_pair_t::second_type>;
534 using iteration_invoke_result_type =
535 std::invoke_result_t<Iteration, std::vector<int_t>>;
536 auto cur_indices = std::vector<int_t>(
size(ranges));
539 [](
auto const&
range) {
return static_cast<int_t
>(
range.first); });
540 auto finished =
false;
543 auto const can_continue = iteration(cur_indices);
548 iteration(cur_indices);
550 ++cur_indices.front();
551 for (std::size_t i = 0; i <
size(ranges) - 1; ++i) {
552 if (cur_indices[i] ==
static_cast<int_t
>(ranges[i].second)) {
553 cur_indices[i] =
static_cast<int_t
>(ranges[i].first);
554 ++cur_indices[i + 1];
555 if (i ==
size(ranges) - 2 &&
556 cur_indices[i + 1] ==
static_cast<int_t
>(ranges[i + 1].second)) {
568template <integral_pair_range IntPairRange,
569 for_loop_nested_index_iteration<std::vector<
common_type<
570 typename std::ranges::range_value_t<IntPairRange>::first_type,
571 typename std::ranges::range_value_t<IntPairRange>::second_type>>>
573auto for_loop(Iteration&& iteration, IntPairRange
const& ranges,
575 for_loop(std::forward<Iteration>(iteration), ranges, policy);
580template <integral_pair_range IntPairRange,
581 for_loop_nested_index_iteration<std::vector<
common_type<
582 typename std::ranges::range_value_t<IntPairRange>::first_type,
583 typename std::ranges::range_value_t<IntPairRange>::second_type>>>
585auto for_loop(Iteration&& iteration, IntPairRange
const& ranges) {
586 for_loop(std::forward<Iteration>(iteration), ranges,
592template <integral_range IntRange,
593 for_loop_nested_index_iteration<IntRange> Iteration>
598 auto const nesting_depth =
size(ends);
599 using int_t = std::ranges::range_value_t<IntRange>;
600 using iteration_invoke_result_type =
601 std::invoke_result_t<Iteration, std::vector<int_t>>;
602 auto cur_indices =
begin;
603 auto finished =
false;
606 auto const can_continue = iteration(cur_indices);
611 iteration(cur_indices);
613 ++cur_indices.front();
614 for (std::size_t i = 0; i < nesting_depth - 1; ++i) {
615 if (cur_indices[i] == ends[i]) {
616 cur_indices[i] =
begin[i];
617 ++cur_indices[i + 1];
618 if (i == nesting_depth - 2 && cur_indices[i + 1] == ends[i + 1]) {
630template <integral_range IntRange,
631 for_loop_nested_index_iteration<IntRange> Iteration>
633 IntRange
const& ends) {
634 return for_loop(std::forward<Iteration>(iteration),
begin, ends,
640template <integral_range IntRange,
641 for_loop_nested_index_iteration<IntRange> Iteration>
642auto for_loop(Iteration&& iteration, IntRange
const& ends,
644 using int_t = std::ranges::range_value_t<IntRange>;
645 for_loop<int_t>(std::forward<Iteration>(iteration),
646 std::vector<int_t>(
size(ends), 0), ends, policy);
651template <integral_range IntRange,
652 for_loop_nested_index_iteration<IntRange> Iteration>
653auto for_loop(Iteration&& iteration, IntRange
const& ends) {
654 using int_t = std::ranges::range_value_t<IntRange>;
655 for_loop(std::forward<Iteration>(iteration),
656 std::vector<int_t>(
size(ends), 0), ends,
661template <range Range, for_loop_range_iteration<Range> Iteration>
662requires(!integral<std::ranges::range_value_t<Range>>) &&
663 (!integral_pair<std::ranges::range_value_t<Range>>)
664 auto for_loop([[maybe_unused]] Iteration&& iteration,
665 [[maybe_unused]] Range&& r,
669#if TATOOINE_PARALLEL_FOR_LOOPS_AVAILABLE
670#pragma omp parallel for
671 for (
auto it = std::ranges::begin(r); it != std::ranges::end(r); ++it) {
678template <range Range, for_loop_range_iteration<Range> Iteration>
679requires(!integral<std::ranges::range_value_t<Range>>) &&
680 (!integral_pair<std::ranges::range_value_t<Range>>)
683 using iteration_invoke_result_type =
684 std::invoke_result_t<Iteration, std::ranges::iterator_t<Range>>;
685 for (
auto it = std::ranges::begin(r); it != std::ranges::end(r); ++it) {
687 auto const can_continue = iteration(it);
698template <range Range, for_loop_range_iteration<Range> Iteration>
699requires(!integral<std::ranges::range_value_t<Range>>) &&
700 (!integral_pair<std::ranges::range_value_t<Range>>)
701auto for_loop(Iteration&& iteration, Range
const& r) {
Definition: concepts.h:18
Definition: for_loop.h:324
Definition: for_loop.h:318
Definition: concepts.h:21
Definition: concepts.h:121
Definition: concepts.h:84
Definition: concepts.h:15
constexpr auto for_loop(Iteration &&iteration, std::integer_sequence< Int, IndexSequence... >, std::pair< Ranges, Ranges > const &... ranges)
Definition: for_loop.h:294
static constexpr sequential_t sequential
Definition: tags.h:63
Definition: algorithm.h:6
typename common_type_impl< Ts... >::type common_type
Definition: common_type.h:23
auto begin(Range &&range)
Definition: iterator_facade.h:318
static constexpr bool parallel_for_loop_support
Definition: for_loop.h:24
auto end(Range &&range)
Definition: iterator_facade.h:322
constexpr auto chunked_for_loop(invocable< decltype(((void) std::declval< Ends >(), std::declval< Int >()))... > auto &&iteration, execution_policy_tag auto policy, integral auto const chunk_size, Ends const ... ends) -> void
Definition: for_loop.h:496
auto for_loop_unpacked(Iteration &&iteration, execution_policy_tag auto policy, std::array< Int, N > const &sizes)
Definition: for_loop.h:480
auto size(vec< ValueType, N > const &v)
Definition: vec.h:148
constexpr decltype(auto) invoke_unpacked(F &&f)
All arguments are bound -> just call f.
Definition: invoke_unpacked.h:15
auto for_loop_num_parallel_threads()
Definition: for_loop.h:30
constexpr auto for_loop(Iteration &&iteration, execution_policy::sequential_t, Ranges(&&... ranges)[2]) -> void
Use this function for creating a sequential nested loop.
Definition: for_loop.h:336
auto create_aligned_data_for_parallel()
Definition: for_loop.h:42
std::array< Int, N > & m_status
Definition: for_loop.h:242
std::array< Int, N > const & m_begins
Definition: for_loop.h:243
auto loop(Iteration &&iteration, std::index_sequence< IndexSequence... >) const
Definition: for_loop.h:252
std::array< Int, N > const & m_ends
Definition: for_loop.h:244
auto operator()(Iteration &&iteration) const
Definition: for_loop.h:284
constexpr auto loop(Iteration &&iteration, std::index_sequence< IndexSequence... >) const
Definition: for_loop.h:133
std::array< Int, N > & m_status
Definition: for_loop.h:123
std::array< Int, N > const & m_ends
Definition: for_loop.h:125
constexpr auto operator()(Iteration &&iteration) const
Definition: for_loop.h:164
std::array< Int, N > const & m_begins
Definition: for_loop.h:124
std::array< Int, N > const & m_ends
Definition: for_loop.h:183
std::array< Int, N > & m_status
Definition: for_loop.h:181
auto operator()(Iteration &&iteration) const
Definition: for_loop.h:226
std::array< Int, N > const & m_begins
Definition: for_loop.h:182
auto loop(Iteration &&iteration, std::index_sequence< IndexSequence... >) const
recursively creates loops
Definition: for_loop.h:192
Definition: for_loop.h:57
std::array< Int, N > const & m_begins
Definition: for_loop.h:63
constexpr auto operator()(Iteration &&iteration) const
Definition: for_loop.h:108
constexpr auto loop(Iteration &&iteration, std::index_sequence< IndexSequence... >) const
recursively creates loops
Definition: for_loop.h:73
std::array< Int, N > const & m_ends
Definition: for_loop.h:64
std::array< Int, N > & m_status
Definition: for_loop.h:62
Definition: invoke_unpacked.h:11