• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #881

05 Feb 2023 11:26PM UTC coverage: 86.546% (+0.6%) from 85.952%
#881

push

StellarBot
Merge #6157

6157: Improve index_queue_spawning r=hkaiser a=hkaiser

- synchronous for_each_partitioner now binds tasks to cores
- add scheduling properties: get_first_core and with_first_core
- index_queue_spawning now supports first_core
- adapt rotate to use first_core for partitions


Co-authored-by: Hartmut Kaiser <hartmut.kaiser@gmail.com>

416 of 416 new or added lines in 18 files covered. (100.0%)

174866 of 202049 relevant lines covered (86.55%)

1824233.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.0
/libs/core/algorithms/include/hpx/parallel/algorithms/rotate.hpp
1
//  Copyright (c) 2007-2023 Hartmut Kaiser
2
//  Copyright (c) 2021-2022 Chuanqiu He
3
//
4
//  SPDX-License-Identifier: BSL-1.0
5
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
6
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7

8
/// \file hpx/parallel/algorithms/rotate.hpp
9

10
#pragma once
11

12
#if defined(DOXYGEN)
13
namespace hpx {
14
    /// Performs a left rotation on a range of elements. Specifically,
15
    /// \a rotate swaps the elements in the range [first, last) in such a way
16
    /// that the element new_first becomes the first element of the new range
17
    /// and new_first - 1 becomes the last element.
18
    ///
19
    /// \note   Complexity: Linear in the distance between \a first and \a last.
20
    ///
21
    /// \tparam FwdIter     The type of the source iterators used (deduced).
22
    ///                     This iterator type must meet the requirements of a
23
    ///                     forward iterator.
24
    ///
25
    /// \param first        Refers to the beginning of the sequence of elements
26
    ///                     the algorithm will be applied to.
27
    /// \param new_first    Refers to the element that should appear at the
28
    ///                     beginning of the rotated range.
29
    /// \param last         Refers to the end of the sequence of elements the
30
    ///                     algorithm will be applied to.
31
    ///
32
    /// The assignments in the parallel \a rotate algorithm
33
    /// execute in sequential order in the calling thread.
34
    ///
35
    /// \note The type of dereferenced \a FwdIter must meet the requirements
36
    ///       of \a MoveAssignable and \a MoveConstructible.
37
    ///
38
    /// \returns  The \a rotate algorithm returns a FwdIter.
39
    ///           The \a rotate algorithm returns the iterator to the new
40
    ///           location of the element pointed by first,equal to first +
41
    ///           (last - new_first).
42
    ///
43
    template <typename FwdIter>
44
    FwdIter rotate(FwdIter first, FwdIter new_first, FwdIter last);
45

46
    /// Performs a left rotation on a range of elements. Specifically,
47
    /// \a rotate swaps the elements in the range [first, last) in such a way
48
    /// that the element new_first becomes the first element of the new range
49
    /// and new_first - 1 becomes the last element. Executed according to the
50
    /// policy.
51
    ///
52
    /// \note   Complexity: Linear in the distance between \a first and \a last.
53
    ///
54
    /// \tparam ExPolicy    The type of the execution policy to use (deduced).
55
    ///                     It describes the manner in which the execution
56
    ///                     of the algorithm may be parallelized and the manner
57
    ///                     in which it executes the assignments.
58
    /// \tparam FwdIter     The type of the source iterators used (deduced).
59
    ///                     This iterator type must meet the requirements of a
60
    ///                     forward iterator.
61
    ///
62
    /// \param policy       The execution policy to use for the scheduling of
63
    ///                     the iterations.
64
    /// \param first        Refers to the beginning of the sequence of elements
65
    ///                     the algorithm will be applied to.
66
    /// \param new_first    Refers to the element that should appear at the
67
    ///                     beginning of the rotated range.
68
    /// \param last         Refers to the end of the sequence of elements the
69
    ///                     algorithm will be applied to.
70
    ///
71
    /// The assignments in the parallel \a rotate algorithm invoked
72
    /// with an execution policy object of type \a sequenced_policy
73
    /// execute in sequential order in the calling thread.
74
    ///
75
    /// The assignments in the parallel \a rotate algorithm invoked with
76
    /// an execution policy object of type \a parallel_policy or
77
    /// \a parallel_task_policy are permitted to execute in an unordered
78
    /// fashion in unspecified threads, and indeterminately sequenced
79
    /// within each thread.
80
    ///
81
    /// \note The type of dereferenced \a FwdIter must meet the requirements
82
    ///       of \a MoveAssignable and \a MoveConstructible.
83
    ///
84
    /// \returns  The \a rotate algorithm returns a \a hpx::future<FwdIter>
85
    ///           if the execution policy is of type
86
    ///           \a sequenced_task_policy or \a parallel_task_policy and
87
    ///           returns \a FwdIter otherwise.
88
    ///           The \a rotate algorithm returns the iterator equal to
89
    ///           first + (last - new_first).
90
    ///
91
    template <typename ExPolicy, typename FwdIter>
92
    typename parallel::util::detail::algorithm_result<ExPolicy, FwdIter>::type
93
    rotate(ExPolicy&& policy, FwdIter first, FwdIter new_first, FwdIter last);
94

95
    /// Copies the elements from the range [first, last), to another range
96
    /// beginning at \a dest_first in such a way, that the element
97
    /// \a new_first becomes the first element of the new range and
98
    /// \a new_first - 1 becomes the last element.
99
    ///
100
    /// \note   Complexity: Performs exactly \a last - \a first assignments.
101
    ///
102
    /// \tparam FwdIter     The type of the source iterators used (deduced).
103
    ///                     This iterator type must meet the requirements of
104
    ///                     a forward iterator.
105
    /// \tparam OutIter     The type of the source iterators used (deduced).
106
    ///                     This iterator type must meet the requirements of
107
    ///                     a output iterator.
108
    ///
109
    /// \param first        Refers to the beginning of the sequence of
110
    ///                     elements the algorithm will be applied to.
111
    /// \param new_first    Refers to the element that should appear at the
112
    ///                     beginning of the rotated range.
113
    /// \param last         Refers to the end of the sequence of elements
114
    ///                     the algorithm will be applied to.
115
    /// \param dest_first   Refers to the begin of the destination range.
116
    ///
117
    /// The assignments in the parallel \a rotate_copy algorithm
118
    /// execute in sequential order in the calling thread.
119
    ///
120
    /// \returns  The \a rotate_copy algorithm returns a output iterator,
121
    ///           The \a rotate_copy algorithm returns the output iterator
122
    ///           to the element past the last element copied.
123
    ///
124
    template <typename FwdIter, typename OutIter>
125
    OutIter rotate_copy(
126
        FwdIter first, FwdIter new_first, FwdIter last, OutIter dest_first);
127

128
    /// Copies the elements from the range [first, last), to another range
129
    /// beginning at \a dest_first in such a way, that the element
130
    /// \a new_first becomes the first element of the new range and
131
    /// \a new_first - 1 becomes the last element. Executed according to
132
    /// the policy.
133
    ///
134
    /// \note   Complexity: Performs exactly \a last - \a first assignments.
135
    ///
136
    /// \tparam ExPolicy    The type of the execution policy to use
137
    ///                     (deduced). It describes the manner in which the
138
    ///                     execution of the algorithm may be parallelized
139
    ///                     and the manner in which it executes the
140
    ///                     assignments.
141
    /// \tparam FwdIter1    The type of the source iterators used (deduced).
142
    ///                     This iterator type must meet the requirements of
143
    ///                     a forward iterator.
144
    /// \tparam FwdIter2    The type of the iterator representing the
145
    ///                     destination range (deduced).
146
    ///                     This iterator type must meet the requirements of
147
    ///                     a forward iterator.
148
    ///
149
    /// \param policy       The execution policy to use for the scheduling
150
    ///                     of the iterations.
151
    /// \param first        Refers to the beginning of the sequence of
152
    ///                     elements the algorithm will be applied to.
153
    /// \param new_first    Refers to the element that should appear at the
154
    ///                     beginning of the rotated range.
155
    /// \param last         Refers to the end of the sequence of elements
156
    ///                     the algorithm will be applied to.
157
    /// \param dest_first   Refers to the begin of the destination range.
158
    ///
159
    /// The assignments in the parallel \a rotate_copy algorithm
160
    /// execute in sequential order in the calling thread.
161
    ///
162
    /// The assignments in the parallel \a rotate_copy algorithm
163
    /// execute in an unordered
164
    /// fashion in unspecified threads, and indeterminately sequenced
165
    /// within each thread.
166
    ///
167
    /// \returns  The \a rotate_copy algorithm returns a
168
    ///           \a hpx::future<FwdIter2>
169
    ///           if the execution policy is of type
170
    ///           \a parallel_task_policy and
171
    ///           returns FwdIter2
172
    ///           otherwise.
173
    ///           The \a rotate_copy algorithm returns the output iterator
174
    ///           to the element past the last element copied.
175
    ///
176
    template <typename ExPolicy, typename FwdIter1, typename FwdIter2>
177
    typename parallel::util::detail::algorithm_result<ExPolicy, FwdIter2>::type
178
    rotate_copy(ExPolicy&& policy, FwdIter1 first, FwdIter1 new_first,
179
        FwdIter1 last, FwdIter2 dest_first);
180

181
}    // namespace hpx
182

183
#else    // DOXYGEN
184

185
#include <hpx/config.hpp>
186
#include <hpx/async_base/scheduling_properties.hpp>
187
#include <hpx/async_local/dataflow.hpp>
188
#include <hpx/concepts/concepts.hpp>
189
#include <hpx/execution/algorithms/when_all.hpp>
190
#include <hpx/execution/traits/is_execution_policy.hpp>
191
#include <hpx/futures/traits/is_future.hpp>
192
#include <hpx/iterator_support/traits/is_iterator.hpp>
193
#include <hpx/modules/execution.hpp>
194

195
#include <hpx/executors/execution_policy.hpp>
196
#include <hpx/executors/execution_policy_parameters.hpp>
197
#include <hpx/parallel/algorithms/copy.hpp>
198
#include <hpx/parallel/algorithms/detail/dispatch.hpp>
199
#include <hpx/parallel/algorithms/detail/rotate.hpp>
200
#include <hpx/parallel/algorithms/reverse.hpp>
201
#include <hpx/parallel/util/detail/algorithm_result.hpp>
202
#include <hpx/parallel/util/detail/sender_util.hpp>
203
#include <hpx/parallel/util/result_types.hpp>
204
#include <hpx/parallel/util/transfer.hpp>
205

206
#include <algorithm>
207
#include <cstddef>
208
#include <iterator>
209
#include <type_traits>
210
#include <utility>
211

212
namespace hpx::parallel {
213

214
    //
215
    inline namespace v1 {
216

217
        ///////////////////////////////////////////////////////////////////////////
218
        // rotate
219
        namespace detail {
220

221
            /// \cond NOINTERNAL
222
            template <typename ExPolicy, typename FwdIter, typename Sent>
223
            decltype(auto) rotate_helper(
27✔
224
                ExPolicy policy, FwdIter first, FwdIter new_first, Sent last)
225
            {
226
                std::ptrdiff_t const size_left =
27✔
227
                    detail::distance(first, new_first);
27✔
228
                std::ptrdiff_t size_right = detail::distance(new_first, last);
27✔
229

230
                // get number of cores currently used
231
                std::size_t const cores =
27✔
232
                    parallel::execution::processing_units_count(
27✔
233
                        policy.parameters(), policy.executor(),
27✔
234
                        hpx::chrono::null_duration, size_left + size_right);
27✔
235

236
                // get currently used first core
237
                std::size_t first_core =
27✔
238
                    hpx::execution::experimental::get_first_core(policy);
27✔
239

240
                // calculate number of cores to be used for left and right section
241
                // proportional to the ratio of their sizes
242
                std::size_t cores_left = 1;
27✔
243
                if (size_right > 0)
27✔
244
                {
245
                    double const partition_size_ratio =
27✔
246
                        static_cast<double>(size_left) /
54✔
247
                        static_cast<double>(size_left + size_right);
27✔
248

249
                    // avoid cores_left == 0 after integer rounding
250
                    cores_left = (std::max)(static_cast<std::size_t>(1),
54✔
251
                        static_cast<std::size_t>(
27✔
252
                            partition_size_ratio * static_cast<double>(cores)));
27✔
253
                }
27✔
254

255
                // cores_right should be at least 1.
256
                std::size_t cores_right =
27✔
257
                    (std::max)(static_cast<std::size_t>(1), cores - cores_left);
27✔
258

259
                // invoke the reverse operations on the left and right sections
260
                // concurrently
261
                auto p = policy(hpx::execution::task);
27✔
262

263
                auto left_policy =
264
                    execution::with_processing_units_count(p, cores_left);
27✔
265
                auto right_policy = execution::with_processing_units_count(
27✔
266
                    hpx::execution::experimental::with_first_core(
27✔
267
                        p, cores == 1 ? first_core : first_core + cores_left),
27✔
268
                    cores_right);
269

270
                detail::reverse<FwdIter> r;
27✔
271

272
                return hpx::dataflow(
27✔
273
                    hpx::launch::sync,
274
                    [=](auto&& f1, auto&& f2) mutable {
126✔
275
                        // propagate exceptions, if appropriate
276
                        static constexpr bool handle_futures =
277
                            hpx::traits::is_future_v<decltype((f1))> &&
278
                            hpx::traits::is_future_v<decltype((f2))>;
279

280
                        if constexpr (handle_futures)
281
                        {
282
                            f1.get();
14✔
283
                            f2.get();
14✔
284
                        }
285

286
                        r.call(p(hpx::execution::non_task), first, last);
19✔
287

288
                        std::advance(first, size_right);
19✔
289
                        return util::in_out_result<FwdIter, Sent>{first, last};
27✔
290
                    },
×
291
                    r.call(left_policy, first, new_first),
27✔
292
                    r.call(right_policy, new_first, last));
27✔
293
            }
×
294

295
            template <typename IterPair>
296
            struct rotate : algorithm<rotate<IterPair>, IterPair>
297
            {
298
                constexpr rotate() noexcept
98✔
299
                  : algorithm<rotate, IterPair>("rotate")
98✔
300
                {
98✔
301
                }
98✔
302

303
                template <typename ExPolicy, typename InIter, typename Sent>
304
                static constexpr IterPair sequential(
71✔
305
                    ExPolicy, InIter first, InIter new_first, Sent last)
306
                {
307
                    return detail::sequential_rotate(first, new_first, last);
71✔
308
                }
32✔
309

310
                template <typename ExPolicy, typename FwdIter, typename Sent>
311
                static decltype(auto) parallel(ExPolicy&& policy, FwdIter first,
27✔
312
                    FwdIter new_first, Sent last)
313
                {
314
                    return util::detail::algorithm_result<ExPolicy,
27✔
315
                        IterPair>::get(rotate_helper(HPX_FORWARD(ExPolicy,
54✔
316
                                                         policy),
317
                        first, new_first, last));
27✔
318
                }
4✔
319
            };
320
            /// \endcond
321
        }    // namespace detail
322

323
        // clang-format off
324
        template <typename ExPolicy, typename FwdIter,
325
            HPX_CONCEPT_REQUIRES_(
326
                hpx::is_execution_policy_v<ExPolicy> &&
327
                hpx::traits::is_iterator_v<FwdIter>
328
            )>
329
        // clang-format on
330
        HPX_DEPRECATED_V(1, 8,
331
            "hpx::parallel::rotate is deprecated, use hpx::rotate instead ")
332
            typename util::detail::algorithm_result<ExPolicy,
333
                util::in_out_result<FwdIter, FwdIter>>::type
334
            rotate(ExPolicy&& policy, FwdIter first, FwdIter new_first,
335
                FwdIter last)
336
        {
337
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter>),
338
                "Requires at least forward iterator.");
339

340
            using is_seq = std::integral_constant<bool,
341
                hpx::is_sequenced_execution_policy_v<ExPolicy> ||
342
                    !hpx::traits::is_bidirectional_iterator_v<FwdIter>>;
343

344
            return detail::rotate<util::in_out_result<FwdIter, FwdIter>>()
345
                .call2(HPX_FORWARD(ExPolicy, policy), is_seq(), first,
346
                    new_first, last);
347
        }
348

349
        ///////////////////////////////////////////////////////////////////////////
350
        // rotate_copy
351
        namespace detail {
352
            /// \cond NOINTERNAL
353

354
            // sequential rotate_copy
355
            template <typename InIter, typename Sent, typename OutIter>
356
            inline util::in_out_result<InIter, OutIter> sequential_rotate_copy(
42✔
357
                InIter first, InIter new_first, Sent last, OutIter dest_first)
358
            {
359
                util::in_out_result<InIter, OutIter> p1 =
360
                    util::copy(new_first, last, dest_first);
70✔
361
                util::in_out_result<InIter, OutIter> p2 =
362
                    util::copy(first, new_first, HPX_MOVE(p1.out));
42✔
363
                return util::in_out_result<InIter, OutIter>{
42✔
364
                    HPX_MOVE(p1.in), HPX_MOVE(p2.out)};
28✔
365
            }
56✔
366

367
            template <typename ExPolicy, typename FwdIter1, typename Sent,
368
                typename FwdIter2>
369
            hpx::future<util::in_out_result<FwdIter1, FwdIter2>>
370
            rotate_copy_helper(ExPolicy policy, FwdIter1 first,
28✔
371
                FwdIter1 new_first, Sent last, FwdIter2 dest_first)
372
            {
373
                using non_seq = std::false_type;
374

375
                auto p = hpx::execution::parallel_task_policy()
56✔
376
                             .on(policy.executor())
28✔
377
                             .with(policy.parameters());
28✔
378

379
                using copy_return_type =
380
                    util::in_out_result<FwdIter1, FwdIter2>;
381

382
                hpx::future<copy_return_type> f =
383
                    detail::copy<copy_return_type>().call2(
28✔
384
                        p, non_seq(), new_first, last, dest_first);
385

386
                return f.then([=](hpx::future<copy_return_type>&& result)
92✔
387
                                  -> hpx::future<copy_return_type> {
388
                    copy_return_type p1 = result.get();
28✔
389
                    return detail::copy<copy_return_type>().call2(
44✔
390
                        p, non_seq(), first, new_first, p1.out);
28✔
391
                });
12✔
392
            }
28✔
393

394
            template <typename IterPair>
395
            struct rotate_copy : algorithm<rotate_copy<IterPair>, IterPair>
396
            {
397
                constexpr rotate_copy() noexcept
70✔
398
                  : algorithm<rotate_copy, IterPair>("rotate_copy")
70✔
399
                {
70✔
400
                }
70✔
401

402
                template <typename ExPolicy, typename InIter, typename Sent,
403
                    typename OutIter>
404
                static util::in_out_result<InIter, OutIter> sequential(ExPolicy,
42✔
405
                    InIter first, InIter new_first, Sent last,
406
                    OutIter dest_first)
407
                {
408
                    return sequential_rotate_copy(
42✔
409
                        first, new_first, last, dest_first);
42✔
410
                }
28✔
411

412
                template <typename ExPolicy, typename FwdIter1, typename Sent,
413
                    typename FwdIter2>
414
                static typename util::detail::algorithm_result<ExPolicy,
415
                    util::in_out_result<FwdIter1, FwdIter2>>::type
416
                parallel(ExPolicy&& policy, FwdIter1 first, FwdIter1 new_first,
28✔
417
                    Sent last, FwdIter2 dest_first)
418
                {
419
                    return util::detail::algorithm_result<ExPolicy,
28✔
420
                        IterPair>::get(rotate_copy_helper(HPX_FORWARD(ExPolicy,
56✔
421
                                                              policy),
422
                        first, new_first, last, dest_first));
28✔
423
                }
6✔
424
            };
425
            /// \endcond
426
        }    // namespace detail
427

428
        // clang-format off
429
        template <typename ExPolicy, typename FwdIter1, typename FwdIter2,
430
            HPX_CONCEPT_REQUIRES_(
431
                hpx::traits::is_iterator_v<FwdIter1> &&
432
                hpx::is_execution_policy_v<ExPolicy> &&
433
                hpx::traits::is_iterator_v<FwdIter2>
434
            )>
435
        // clang-format on
436
        HPX_DEPRECATED_V(1, 8,
437
            "hpx::parallel::rotate_copy is deprecated, use hpx::rotate_copy "
438
            "instead ") typename util::detail::algorithm_result<ExPolicy,
439
            util::in_out_result<FwdIter1, FwdIter2>>::type
440
            rotate_copy(ExPolicy&& policy, FwdIter1 first, FwdIter1 new_first,
441
                FwdIter1 last, FwdIter2 dest_first)
442
        {
443
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter1>),
444
                "Requires at least forward iterator.");
445
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter2>),
446
                "Requires at least forward iterator.");
447

448
            using is_seq = std::integral_constant<bool,
449
                hpx::is_sequenced_execution_policy_v<ExPolicy> ||
450
                    !hpx::traits::is_bidirectional_iterator_v<FwdIter1>>;
451

452
            return detail::rotate_copy<
453
                util::in_out_result<FwdIter1, FwdIter2>>()
454
                .call2(HPX_FORWARD(ExPolicy, policy), is_seq(), first,
455
                    new_first, last, dest_first);
456
        }
457
    }    // namespace v1
458
}    // namespace hpx::parallel
459

460
// create new APIs, tag_fallback_invoke overloads.
461
namespace hpx {
462

463
    ///////////////////////////////////////////////////////////////////////////
464
    // CPO for hpx::rotate
465
    inline constexpr struct rotate_t final
466
      : hpx::detail::tag_parallel_algorithm<rotate_t>
467
    {
468
        // clang-format off
469
        template <typename FwdIter,
470
            HPX_CONCEPT_REQUIRES_(
471
                hpx::traits::is_iterator_v<FwdIter>
472
            )>
473
        // clang-format on
474
        friend FwdIter tag_fallback_invoke(
6✔
475
            hpx::rotate_t, FwdIter first, FwdIter new_first, FwdIter last)
476
        {
477
            static_assert(hpx::traits::is_forward_iterator_v<FwdIter>,
478
                "Requires at least forward iterator.");
479

480
            return parallel::util::get_second_element(
6✔
481
                hpx::parallel::v1::detail::rotate<
2✔
482
                    hpx::parallel::util::in_out_result<FwdIter, FwdIter>>()
483
                    .call(hpx::execution::seq, first, new_first, last));
2✔
484
        }
×
485

486
        // clang-format off
487
        template <typename ExPolicy, typename FwdIter,
488
            HPX_CONCEPT_REQUIRES_(
489
                hpx::is_execution_policy_v<ExPolicy> &&
490
                hpx::traits::is_iterator_v<FwdIter>
491
            )>
492
        // clang-format on
493
        friend decltype(auto) tag_fallback_invoke(hpx::rotate_t,
52✔
494
            ExPolicy&& policy, FwdIter first, FwdIter new_first, FwdIter last)
495
        {
496
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter>),
497
                "Requires at least forward iterator.");
498

499
            using is_seq = std::integral_constant<bool,
500
                hpx::is_sequenced_execution_policy_v<ExPolicy> ||
501
                    !hpx::traits::is_bidirectional_iterator_v<FwdIter>>;
502

503
            return parallel::util::get_second_element(
52✔
504
                hpx::parallel::v1::detail::rotate<
59✔
505
                    hpx::parallel::util::in_out_result<FwdIter, FwdIter>>()
506
                    .call2(HPX_FORWARD(ExPolicy, policy), is_seq(), first,
44✔
507
                        new_first, last));
508
        }
×
509

510
    } rotate{};
511

512
    ///////////////////////////////////////////////////////////////////////////
513
    // CPO for hpx::rotate_copy
514
    inline constexpr struct rotate_copy_t final
515
      : hpx::detail::tag_parallel_algorithm<rotate_copy_t>
516
    {
517
        // clang-format off
518
        template <typename FwdIter, typename OutIter,
519
            HPX_CONCEPT_REQUIRES_(
520
                hpx::traits::is_iterator_v<FwdIter> &&
521
                hpx::traits::is_iterator_v<OutIter>
522
            )>
523
        // clang-format on
524
        friend OutIter tag_fallback_invoke(hpx::rotate_copy_t, FwdIter first,
6✔
525
            FwdIter new_first, FwdIter last, OutIter dest_first)
526
        {
527
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter>),
528
                "Requires at least forward iterator.");
529
            static_assert((hpx::traits::is_output_iterator_v<OutIter>),
530
                "Requires at least output iterator.");
531

532
            return parallel::util::get_second_element(
6✔
533
                hpx::parallel::v1::detail::rotate_copy<
2✔
534
                    hpx::parallel::util::in_out_result<FwdIter, OutIter>>()
535
                    .call(hpx::execution::seq, first, new_first, last,
2✔
536
                        dest_first));
537
        }
×
538

539
        // clang-format off
540
        template <typename ExPolicy, typename FwdIter1, typename FwdIter2,
541
            HPX_CONCEPT_REQUIRES_(
542
                hpx::traits::is_iterator_v<FwdIter1> &&
543
                hpx::is_execution_policy_v<ExPolicy> &&
544
                hpx::traits::is_iterator_v<FwdIter2>
545
            )>
546
        // clang-format on
547
        friend typename parallel::util::detail::algorithm_result<ExPolicy,
548
            FwdIter2>::type
549
        tag_fallback_invoke(hpx::rotate_copy_t, ExPolicy&& policy,
26✔
550
            FwdIter1 first, FwdIter1 new_first, FwdIter1 last,
551
            FwdIter2 dest_first)
552
        {
553
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter1>),
554
                "Requires at least forward iterator.");
555
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter2>),
556
                "Requires at least forward iterator.");
557

558
            using is_seq = std::integral_constant<bool,
559
                hpx::is_sequenced_execution_policy_v<ExPolicy> ||
560
                    !hpx::traits::is_forward_iterator_v<FwdIter1>>;
561

562
            return parallel::util::get_second_element(
26✔
563
                hpx::parallel::v1::detail::rotate_copy<
36✔
564
                    hpx::parallel::util::in_out_result<FwdIter1, FwdIter2>>()
565
                    .call2(HPX_FORWARD(ExPolicy, policy), is_seq(), first,
18✔
566
                        new_first, last, dest_first));
567
        }
×
568
    } rotate_copy{};
569
}    // namespace hpx
570

571
#endif    // DOXYGEN
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc