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

STEllAR-GROUP / hpx / #852

17 Dec 2022 04:43PM UTC coverage: 85.912% (-0.7%) from 86.568%
#852

push

StellarBot
Merge #6106

6106: Modernizing modules of levels 0 to 5 r=hkaiser a=hkaiser

- flyby: HPX_FORWARD/HPX_MOVE now expand to std::forward and std::move if those are implemented as builtin functions

working towards #5497

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

87 of 87 new or added lines in 24 files covered. (100.0%)

173152 of 201546 relevant lines covered (85.91%)

1910264.28 hits per line

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

91.79
/libs/core/algorithms/include/hpx/parallel/algorithms/partition.hpp
1
//  Copyright (c) 2014-2017 Hartmut Kaiser
2
//  Copyright (c)      2017 Taeguk Kwon
3
//  Copyright (c)      2021 Akhil J Nair
4
//
5
//  SPDX-License-Identifier: BSL-1.0
6
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
7
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8

9
#pragma once
10

11
#if defined(DOXYGEN)
12
namespace hpx {
13
    // clang-format off
14

15
    ///////////////////////////////////////////////////////////////////////////
16
    /// Reorders the elements in the range [first, last) in such a way that
17
    /// all elements for which the predicate \a pred returns true precede
18
    /// the elements for which the predicate \a pred returns false.
19
    /// Relative order of the elements is not preserved.
20
    ///
21
    /// \note   Complexity: At most 2 * (last - first) swaps.
22
    ///         Exactly \a last - \a first applications of the predicate and
23
    ///         projection.
24
    ///
25
    /// \tparam FwdIter     The type of the source iterators used (deduced).
26
    ///                     This iterator type must meet the requirements of a
27
    ///                     forward iterator.
28
    /// \tparam Pred        The type of the function/function object to use
29
    ///                     (deduced). Unlike its sequential form, the parallel
30
    ///                     overload of \a partition requires \a Pred to meet
31
    ///                     the requirements of \a CopyConstructible.
32
    /// \tparam Proj        The type of an optional projection function. This
33
    ///                     defaults to \a util::projection_identity
34
    ///
35
    /// \param first        Refers to the beginning of the sequence of elements
36
    ///                     the algorithm will be applied to.
37
    /// \param last         Refers to the end of the sequence of elements the
38
    ///                     algorithm will be applied to.
39
    /// \param pred         Specifies the function (or function object) which
40
    ///                     will be invoked for each of the elements in the
41
    ///                     sequence specified by [first, last). This is an
42
    ///                     unary predicate for partitioning the source
43
    ///                     iterators. The signature of
44
    ///                     this predicate should be equivalent to:
45
    ///                     \code
46
    ///                     bool pred(const Type &a);
47
    ///                     \endcode \n
48
    ///                     The signature does not need to have const&, but
49
    ///                     the function must not modify the objects passed to
50
    ///                     it. The type \a Type must be such that an object of
51
    ///                     type \a FwdIter can be dereferenced and then
52
    ///                     implicitly converted to Type.
53
    /// \param proj         Specifies the function (or function object) which
54
    ///                     will be invoked for each of the elements as a
55
    ///                     projection operation before the actual predicate
56
    ///                     \a is invoked.
57
    ///
58
    /// The assignments in the parallel \a partition algorithm invoked without
59
    /// an execution policy object execute in sequential order in the calling
60
    /// thread.
61
    ///
62
    /// \returns  The \a partition algorithm returns returns \a FwdIter.
63
    ///           The \a partition algorithm returns the iterator to
64
    ///           the first element of the second group.
65
    ///
66
    template <typename FwdIter, typename Pred,
67
        typename Proj = parallel::util::projection_identity>
68
    FwdIter partition(FwdIter first, FwdIter last,
69
        Pred&& pred, Proj&& proj = Proj());
70

71
    ///////////////////////////////////////////////////////////////////////////
72
    /// Reorders the elements in the range [first, last) in such a way that
73
    /// all elements for which the predicate \a pred returns true precede
74
    /// the elements for which the predicate \a pred returns false.
75
    /// Relative order of the elements is not preserved.
76
    ///
77
    /// \note   Complexity: At most 2 * (last - first) swaps.
78
    ///         Exactly \a last - \a first applications of the predicate and
79
    ///         projection.
80
    ///
81
    /// \tparam ExPolicy    The type of the execution policy to use (deduced).
82
    ///                     It describes the manner in which the execution
83
    ///                     of the algorithm may be parallelized and the manner
84
    ///                     in which it executes the assignments.
85
    /// \tparam FwdIter     The type of the source iterators used (deduced).
86
    ///                     This iterator type must meet the requirements of an
87
    ///                     forward iterator.
88
    /// \tparam Pred        The type of the function/function object to use
89
    ///                     (deduced). Unlike its sequential form, the parallel
90
    ///                     overload of \a partition requires \a Pred to meet
91
    ///                     the requirements of \a CopyConstructible.
92
    /// \tparam Proj        The type of an optional projection function. This
93
    ///                     defaults to \a util::projection_identity
94
    ///
95
    /// \param policy       The execution policy to use for the scheduling of
96
    ///                     the iterations.
97
    /// \param first        Refers to the beginning of the sequence of elements
98
    ///                     the algorithm will be applied to.
99
    /// \param last         Refers to the end of the sequence of elements the
100
    ///                     algorithm will be applied to.
101
    /// \param pred         Specifies the function (or function object) which
102
    ///                     will be invoked for each of the elements in the
103
    ///                     sequence specified by [first, last). This is an
104
    ///                     unary predicate for partitioning the source
105
    ///                     iterators. The signature of
106
    ///                     this predicate should be equivalent to:
107
    ///                     \code
108
    ///                     bool pred(const Type &a);
109
    ///                     \endcode \n
110
    ///                     The signature does not need to have const&, but
111
    ///                     the function must not modify the objects passed to
112
    ///                     it. The type \a Type must be such that an object of
113
    ///                     type \a FwdIter can be dereferenced and then
114
    ///                     implicitly converted to Type.
115
    /// \param proj         Specifies the function (or function object) which
116
    ///                     will be invoked for each of the elements as a
117
    ///                     projection operation before the actual predicate
118
    ///                     \a is invoked.
119
    ///
120
    /// The assignments in the parallel \a partition algorithm invoked with
121
    /// an execution policy object of type \a sequenced_policy
122
    /// execute in sequential order in the calling thread.
123
    ///
124
    /// The assignments in the parallel \a partition algorithm invoked with
125
    /// an execution policy object of type \a parallel_policy or
126
    /// \a parallel_task_policy are permitted to execute in an unordered
127
    /// fashion in unspecified threads, and indeterminately sequenced
128
    /// within each thread.
129
    ///
130
    /// \returns  The \a partition algorithm returns a \a hpx::future<FwdIter>
131
    ///           if the execution policy is of type \a parallel_task_policy
132
    ///           and returns \a FwdIter otherwise.
133
    ///           The \a partition algorithm returns the iterator to
134
    ///           the first element of the second group.
135
    ///
136
    template <typename ExPolicy, typename FwdIter, typename Pred,
137
        typename Proj = parallel::util::projection_identity>
138
    parallel::util::detail::algorithm_result_t<ExPolicy, FwdIter>
139
    partition(ExPolicy&& policy, FwdIter first, FwdIter last, Pred&& pred,
140
        Proj&& proj = Proj());
141

142
    ///////////////////////////////////////////////////////////////////////////
143
    /// Permutes the elements in the range [first, last) such that there exists
144
    /// an iterator i such that for every iterator j in the range [first, i)
145
    /// INVOKE(f, INVOKE (proj, *j)) != false, and for every iterator k in the
146
    /// range [i, last), INVOKE(f, INVOKE (proj, *k)) == false
147
    ///
148
    /// \note   Complexity: At most (last - first) * log(last - first) swaps,
149
    ///         but only linear number of swaps if there is enough extra memory.
150
    ///         Exactly \a last - \a first applications of the predicate and
151
    ///         projection.
152
    ///
153
    /// \tparam BidirIter   The type of the source iterators used (deduced).
154
    ///                     This iterator type must meet the requirements of a
155
    ///                     bidirectional iterator.
156
    /// \tparam F           The type of the function/function object to use
157
    ///                     (deduced). Unlike its sequential form, the parallel
158
    ///                     overload of \a transform requires \a F to meet the
159
    ///                     requirements of \a CopyConstructible.
160
    /// \tparam Proj        The type of an optional projection function. This
161
    ///                     defaults to \a util::projection_identity
162
    ///
163
    /// \param first        Refers to the beginning of the sequence of elements
164
    ///                     the algorithm will be applied to.
165
    /// \param last         Refers to the end of the sequence of elements the
166
    ///                     algorithm will be applied to.
167
    /// \param f            Unary predicate which returns true if the element
168
    ///                     should be ordered before other elements.
169
    ///                     Specifies the function (or function object) which
170
    ///                     will be invoked for each of the elements in the
171
    ///                     sequence specified by [first, last). The signature
172
    ///                     of this predicate should be equivalent to:
173
    ///                     \code
174
    ///                     bool fun(const Type &a);
175
    ///                     \endcode \n
176
    ///                     The signature does not need to have const&.
177
    ///                     The type \a Type must be such that an object of
178
    ///                     type \a BidirIter can be dereferenced and then
179
    ///                     implicitly converted to \a Type.
180
    /// \param proj         Specifies the function (or function object) which
181
    ///                     will be invoked for each of the elements as a
182
    ///                     projection operation before the actual predicate
183
    ///                     \a f is invoked.
184
    ///
185
    /// The invocations of \a f in the parallel \a stable_partition algorithm
186
    /// invoked without an execution policy object executes in sequential order
187
    /// in the calling thread.
188
    ///
189
    /// \returns  The \a stable_partition algorithm returns an iterator i such
190
    ///           that for every iterator j in the range [first, i), f(*j) !=
191
    ///           false INVOKE(f, INVOKE(proj, *j)) != false, and for every
192
    ///           iterator k in the range [i, last), f(*k) == false
193
    ///           INVOKE(f, INVOKE (proj, *k)) == false. The relative order of
194
    ///           the elements in both groups is preserved.
195
    ///
196
    template <typename BidirIter, typename F,
197
        typename Proj = parallel::util::projection_identity>
198
    BidirIter stable_partition(BidirIter first, BidirIter last, F&& f,
199
        Proj&& proj = Proj());
200

201
    ///////////////////////////////////////////////////////////////////////////
202
    /// Permutes the elements in the range [first, last) such that there exists
203
    /// an iterator i such that for every iterator j in the range [first, i)
204
    /// INVOKE(f, INVOKE (proj, *j)) != false, and for every iterator k in the
205
    /// range [i, last), INVOKE(f, INVOKE (proj, *k)) == false
206
    ///
207
    /// \note   Complexity: At most (last - first) * log(last - first) swaps,
208
    ///         but only linear number of swaps if there is enough extra memory.
209
    ///         Exactly \a last - \a first applications of the predicate and
210
    ///         projection.
211
    ///
212
    /// \tparam ExPolicy    The type of the execution policy to use (deduced).
213
    ///                     It describes the manner in which the execution
214
    ///                     of the algorithm may be parallelized and the manner
215
    ///                     in which it executes the invocations of \a f.
216
    /// \tparam BidirIter   The type of the source iterators used (deduced).
217
    ///                     This iterator type must meet the requirements of a
218
    ///                     bidirectional iterator.
219
    /// \tparam F           The type of the function/function object to use
220
    ///                     (deduced). Unlike its sequential form, the parallel
221
    ///                     overload of \a transform requires \a F to meet the
222
    ///                     requirements of \a CopyConstructible.
223
    /// \tparam Proj        The type of an optional projection function. This
224
    ///                     defaults to \a util::projection_identity
225
    ///
226
    /// \param policy       The execution policy to use for the scheduling of
227
    ///                     the iterations.
228
    /// \param first        Refers to the beginning of the sequence of elements
229
    ///                     the algorithm will be applied to.
230
    /// \param last         Refers to the end of the sequence of elements the
231
    ///                     algorithm will be applied to.
232
    /// \param f            Unary predicate which returns true if the element
233
    ///                     should be ordered before other elements.
234
    ///                     Specifies the function (or function object) which
235
    ///                     will be invoked for each of the elements in the
236
    ///                     sequence specified by [first, last). The signature
237
    ///                     of this predicate should be equivalent to:
238
    ///                     \code
239
    ///                     bool fun(const Type &a);
240
    ///                     \endcode \n
241
    ///                     The signature does not need to have const&.
242
    ///                     The type \a Type must be such that an object of
243
    ///                     type \a BidirIter can be dereferenced and then
244
    ///                     implicitly converted to \a Type.
245
    /// \param proj         Specifies the function (or function object) which
246
    ///                     will be invoked for each of the elements as a
247
    ///                     projection operation before the actual predicate
248
    ///                     \a f is invoked.
249
    ///
250
    /// The invocations of \a f in the parallel \a stable_partition algorithm
251
    /// invoked with an execution policy object of type
252
    /// \a sequenced_policy executes in sequential order in the
253
    /// calling thread.
254
    ///
255
    /// The invocations of \a f in the parallel \a stable_partition algorithm
256
    /// invoked with an execution policy object of type \a parallel_policy
257
    /// or \a parallel_task_policy are permitted to execute in an
258
    /// unordered fashion in unspecified threads, and indeterminately sequenced
259
    /// within each thread.
260
    ///
261
    /// \returns  The \a stable_partition algorithm returns an iterator i such
262
    ///           that for every iterator j in the range [first, i), f(*j) !=
263
    ///           false INVOKE(f, INVOKE(proj, *j)) != false, and for every
264
    ///           iterator k in the range [i, last), f(*k) == false
265
    ///           INVOKE(f, INVOKE (proj, *k)) == false. The relative order of
266
    ///           the elements in both groups is preserved.
267
    ///           If the execution policy is of type \a parallel_task_policy
268
    ///           the algorithm returns a \a future<> referring to this iterator.
269
    ///
270
    template <typename ExPolicy, typename BidirIter, typename F,
271
        typename Proj = parallel::util::projection_identity>
272
    parallel::util::detail::algorithm_result_t<ExPolicy, BidirIter>
273
    stable_partition(ExPolicy&& policy, BidirIter first, BidirIter last,
274
        F&& f, Proj&& proj = Proj());
275

276
    ///////////////////////////////////////////////////////////////////////////
277
    /// Copies the elements in the range, defined by [first, last),
278
    /// to two different ranges depending on the value returned by
279
    /// the predicate \a pred. The elements, that satisfy the predicate \a pred
280
    /// are copied to the range beginning at \a dest_true. The rest of
281
    /// the elements are copied to the range beginning at \a dest_false.
282
    /// The order of the elements is preserved.
283
    ///
284
    /// \note   Complexity: Performs not more than \a last - \a first
285
    ///         assignments, exactly \a last - \a first applications of the
286
    ///         predicate \a pred.
287
    ///
288
    /// \tparam FwdIter1    The type of the source iterators used (deduced).
289
    ///                     This iterator type must meet the requirements of an
290
    ///                     forward iterator.
291
    /// \tparam FwdIter2    The type of the iterator representing the
292
    ///                     destination range for the elements that satisfy
293
    ///                     the predicate \a pred (deduced).
294
    ///                     This iterator type must meet the requirements of an
295
    ///                     forward iterator.
296
    /// \tparam FwdIter3    The type of the iterator representing the
297
    ///                     destination range for the elements that don't
298
    ///                     satisfy the predicate \a pred (deduced).
299
    ///                     This iterator type must meet the requirements of an
300
    ///                     forward iterator.
301
    /// \tparam Pred        The type of the function/function object to use
302
    ///                     (deduced). Unlike its sequential form, the parallel
303
    ///                     overload of \a partition_copy requires \a Pred to
304
    ///                     meet the requirements of \a CopyConstructible.
305
    /// \tparam Proj        The type of an optional projection function. This
306
    ///                     defaults to \a util::projection_identity
307
    ///
308
    /// \param first        Refers to the beginning of the sequence of elements
309
    ///                     the algorithm will be applied to.
310
    /// \param last         Refers to the end of the sequence of elements the
311
    ///                     algorithm will be applied to.
312
    /// \param dest_true    Refers to the beginning of the destination range
313
    ///                     for the elements that satisfy the predicate \a pred
314
    /// \param dest_false   Refers to the beginning of the destination range
315
    ///                     for the elements that don't satisfy the predicate
316
    ///                     \a pred.
317
    /// \param pred         Specifies the function (or function object) which
318
    ///                     will be invoked for each of the elements in the
319
    ///                     sequence specified by [first, last). This is an
320
    ///                     unary predicate for partitioning the source
321
    ///                     iterators. The signature of
322
    ///                     this predicate should be equivalent to:
323
    ///                     \code
324
    ///                     bool pred(const Type &a);
325
    ///                     \endcode \n
326
    ///                     The signature does not need to have const&, but
327
    ///                     the function must not modify the objects passed to
328
    ///                     it. The type \a Type must be such that an object of
329
    ///                     type \a FwdIter1 can be dereferenced and then
330
    ///                     implicitly converted to Type.
331
    /// \param proj         Specifies the function (or function object) which
332
    ///                     will be invoked for each of the elements as a
333
    ///                     projection operation before the actual predicate
334
    ///                     \a is invoked.
335
    ///
336
    /// The assignments in the parallel \a partition_copy algorithm invoked
337
    /// without an execution policy object execute in sequential order in the
338
    /// calling thread.
339
    ///
340
    /// \returns  The \a partition_copy algorithm returns
341
    ///           \a std::pair<OutIter1, OutIter2>.
342
    ///           The \a partition_copy algorithm returns the pair of
343
    ///           the destination iterator to the end of the \a dest_true
344
    ///           range, and the destination iterator to the end of the \a
345
    ///           dest_false range.
346
    ///
347
    template <typename FwdIter1, typename FwdIter2, typename FwdIter3,
348
        typename Pred, typename Proj = parallel::util::projection_identity>
349
    std::pair<FwdIter2 ,FwdIter3>
350
    partition_copy(FwdIter1 first, FwdIter1 last,
351
        FwdIter2 dest_true, FwdIter3 dest_false, Pred&& pred,
352
        Proj&& proj = Proj());
353

354
    ///////////////////////////////////////////////////////////////////////////
355
    /// Copies the elements in the range, defined by [first, last),
356
    /// to two different ranges depending on the value returned by
357
    /// the predicate \a pred. The elements, that satisfy the predicate \a pred,
358
    /// are copied to the range beginning at \a dest_true. The rest of
359
    /// the elements are copied to the range beginning at \a dest_false.
360
    /// The order of the elements is preserved.
361
    ///
362
    /// \note   Complexity: Performs not more than \a last - \a first
363
    ///         assignments, exactly \a last - \a first applications of the
364
    ///         predicate \a pred.
365
    ///
366
    /// \tparam ExPolicy    The type of the execution policy to use (deduced).
367
    ///                     It describes the manner in which the execution
368
    ///                     of the algorithm may be parallelized and the manner
369
    ///                     in which it executes the assignments.
370
    /// \tparam FwdIter1    The type of the source iterators used (deduced).
371
    ///                     This iterator type must meet the requirements of an
372
    ///                     forward iterator.
373
    /// \tparam FwdIter2    The type of the iterator representing the
374
    ///                     destination range for the elements that satisfy
375
    ///                     the predicate \a pred (deduced).
376
    ///                     This iterator type must meet the requirements of an
377
    ///                     forward iterator.
378
    /// \tparam FwdIter3    The type of the iterator representing the
379
    ///                     destination range for the elements that don't
380
    ///                     satisfy the predicate \a pred (deduced).
381
    ///                     This iterator type must meet the requirements of an
382
    ///                     forward iterator.
383
    /// \tparam Pred        The type of the function/function object to use
384
    ///                     (deduced). Unlike its sequential form, the parallel
385
    ///                     overload of \a partition_copy requires \a Pred to
386
    ///                     meet the requirements of \a CopyConstructible.
387
    /// \tparam Proj        The type of an optional projection function. This
388
    ///                     defaults to \a util::projection_identity
389
    ///
390
    /// \param policy       The execution policy to use for the scheduling of
391
    ///                     the iterations.
392
    /// \param first        Refers to the beginning of the sequence of elements
393
    ///                     the algorithm will be applied to.
394
    /// \param last         Refers to the end of the sequence of elements the
395
    ///                     algorithm will be applied to.
396
    /// \param dest_true    Refers to the beginning of the destination range
397
    ///                     for the elements that satisfy the predicate \a pred
398
    /// \param dest_false   Refers to the beginning of the destination range
399
    ///                     for the elements that don't satisfy the predicate
400
    ///                     \a pred.
401
    /// \param pred         Specifies the function (or function object) which
402
    ///                     will be invoked for each of the elements in the
403
    ///                     sequence specified by [first, last). This is an
404
    ///                     unary predicate for partitioning the source
405
    ///                     iterators. The signature of
406
    ///                     this predicate should be equivalent to:
407
    ///                     \code
408
    ///                     bool pred(const Type &a);
409
    ///                     \endcode \n
410
    ///                     The signature does not need to have const&, but
411
    ///                     the function must not modify the objects passed to
412
    ///                     it. The type \a Type must be such that an object of
413
    ///                     type \a FwdIter1 can be dereferenced and then
414
    ///                     implicitly converted to Type.
415
    /// \param proj         Specifies the function (or function object) which
416
    ///                     will be invoked for each of the elements as a
417
    ///                     projection operation before the actual predicate
418
    ///                     \a is invoked.
419
    ///
420
    /// The assignments in the parallel \a partition_copy algorithm invoked
421
    /// with an execution policy object of type \a sequenced_policy
422
    /// execute in sequential order in the calling thread.
423
    ///
424
    /// The assignments in the parallel \a partition_copy algorithm invoked
425
    /// with an execution policy object of type \a parallel_policy or
426
    /// \a parallel_task_policy are permitted to execute in an unordered
427
    /// fashion in unspecified threads, and indeterminately sequenced
428
    /// within each thread.
429
    ///
430
    /// \returns  The \a partition_copy algorithm returns a
431
    ///           \a hpx::future<std::pair<OutIter1, OutIter2>>
432
    ///           if the execution policy is of type \a parallel_task_policy
433
    ///           and returns
434
    ///           \a std::pair<OutIter1, OutIter2> otherwise.
435
    ///           The \a partition_copy algorithm returns the pair of
436
    ///           the destination iterator to the end of the \a dest_true
437
    ///           range, and the destination iterator to the end of the \a
438
    ///           dest_false range.
439
    ///
440
    template <typename ExPolicy, typename FwdIter1, typename FwdIter2,
441
        typename FwdIter3, typename Pred,
442
        typename Proj = parallel::util::projection_identity>
443
    parallel::util::detail::algorithm_result_t
444
        <ExPolicy, std::pair<FwdIter2, FwdIter3>>
445
    partition_copy(ExPolicy&& policy, FwdIter1 first, FwdIter1 last,
446
        FwdIter2 dest_true, FwdIter3 dest_false, Pred&& pred, Proj&& proj = Proj());
447

448
    // clang-format on
449
}    // namespace hpx
450

451
#else    // DOXYGEN
452

453
#include <hpx/config.hpp>
454
#include <hpx/assert.hpp>
455
#include <hpx/concepts/concepts.hpp>
456
#include <hpx/functional/invoke.hpp>
457
#include <hpx/functional/traits/is_invocable.hpp>
458
#include <hpx/futures/future.hpp>
459
#include <hpx/iterator_support/traits/is_iterator.hpp>
460
#include <hpx/modules/async_local.hpp>
461
#include <hpx/synchronization/spinlock.hpp>
462
#include <hpx/type_support/unused.hpp>
463

464
#include <hpx/algorithms/traits/projected.hpp>
465
#include <hpx/execution/executors/execution.hpp>
466
#include <hpx/execution/executors/execution_information.hpp>
467
#include <hpx/execution/executors/execution_parameters.hpp>
468
#include <hpx/executors/exception_list.hpp>
469
#include <hpx/executors/execution_policy.hpp>
470
#include <hpx/parallel/algorithms/detail/advance_and_get_distance.hpp>
471
#include <hpx/parallel/algorithms/detail/advance_to_sentinel.hpp>
472
#include <hpx/parallel/algorithms/detail/dispatch.hpp>
473
#include <hpx/parallel/algorithms/detail/distance.hpp>
474
#include <hpx/parallel/util/detail/algorithm_result.hpp>
475
#include <hpx/parallel/util/detail/chunk_size.hpp>
476
#include <hpx/parallel/util/detail/handle_local_exceptions.hpp>
477
#include <hpx/parallel/util/detail/sender_util.hpp>
478
#include <hpx/parallel/util/invoke_projected.hpp>
479
#include <hpx/parallel/util/loop.hpp>
480
#include <hpx/parallel/util/projection_identity.hpp>
481
#include <hpx/parallel/util/scan_partitioner.hpp>
482
#include <hpx/parallel/util/transfer.hpp>
483
#include <hpx/parallel/util/zip_iterator.hpp>
484

485
#if !defined(HPX_HAVE_CXX17_SHARED_PTR_ARRAY)
486
#include <boost/shared_array.hpp>
487
#else
488
#include <memory>
489
#endif
490

491
#include <algorithm>
492
#include <cstddef>
493
#include <cstdint>
494
#include <exception>
495
#include <iterator>
496
#include <list>
497
#include <type_traits>
498
#include <utility>
499
#include <vector>
500

501
namespace hpx { namespace parallel { inline namespace v1 {
502

503
    template <typename Tuple>
504
    constexpr HPX_FORCEINLINE
505
        std::pair<typename hpx::tuple_element<1, Tuple>::type,
506
            typename hpx::tuple_element<2, Tuple>::type>
507
        tuple_to_pair(Tuple&& t)
72✔
508
    {
509
        return std::make_pair(hpx::get<1>(t), hpx::get<2>(t));
72✔
510
    }
511

512
    template <typename Tuple>
513
    hpx::future<std::pair<typename hpx::tuple_element<1, Tuple>::type,
514
        typename hpx::tuple_element<2, Tuple>::type>>
515
    tuple_to_pair(hpx::future<Tuple>&& f)
24✔
516
    {
517
        using result_type =
518
            std::pair<typename hpx::tuple_element<1, Tuple>::type,
519
                typename hpx::tuple_element<2, Tuple>::type>;
520

521
        return hpx::make_future<result_type>(
24✔
522
            HPX_MOVE(f), [](Tuple&& t) -> result_type {
36✔
523
                return tuple_to_pair(HPX_MOVE(t));
12✔
524
            });
525
    }
526

527
    ///////////////////////////////////////////////////////////////////////////
528
    // stable_partition
529
    namespace detail {
530
        /// \cond NOINTERNAL
531
        struct stable_partition_helper
532
        {
533
            template <typename ExPolicy, typename RandIter, typename F,
534
                typename Proj>
535
            hpx::future<RandIter> operator()(ExPolicy&& policy, RandIter first,
195✔
536
                RandIter last, std::size_t size, F&& f, Proj&& proj,
537
                std::size_t chunks)
538
            {
539
                if (chunks < 2)
195✔
540
                {
541
                    return execution::async_execute(policy.executor(),
208✔
542
                        [first, last, f = HPX_FORWARD(F, f),
208✔
543
                            proj = HPX_FORWARD(Proj, proj)]() -> RandIter {
104✔
544
                            return std::stable_partition(first, last,
208✔
545
                                util::invoke_projected<F, Proj>(f, proj));
104✔
546
                        });
547
                }
548

549
                std::size_t mid_point = size / 2;
91✔
550
                chunks /= 2;
91✔
551

552
                RandIter mid = first;
91✔
553
                std::advance(mid, mid_point);
91✔
554

555
                hpx::future<RandIter> left =
556
                    execution::async_execute(policy.executor(), *this, policy,
182✔
557
                        first, mid, mid_point, f, proj, chunks);
91✔
558
                hpx::future<RandIter> right =
559
                    execution::async_execute(policy.executor(), *this, policy,
182✔
560
                        mid, last, size - mid_point, f, proj, chunks);
91✔
561

562
                return dataflow(
91✔
563
                    policy.executor(),
91✔
564
                    [mid](hpx::future<RandIter>&& left,
182✔
565
                        hpx::future<RandIter>&& right) -> RandIter {
566
                        if (left.has_exception() || right.has_exception())
91✔
567
                        {
568
                            std::list<std::exception_ptr> errors;
28✔
569
                            if (left.has_exception())
28✔
570
                            {
571
                                hpx::parallel::util::detail::
28✔
572
                                    handle_local_exceptions<ExPolicy>::call(
573
                                        left.get_exception_ptr(), errors);
28✔
574
                            }
14✔
575
                            if (right.has_exception())
14✔
576
                            {
577
                                hpx::parallel::util::detail::
14✔
578
                                    handle_local_exceptions<ExPolicy>::call(
579
                                        right.get_exception_ptr(), errors);
14✔
580
                            }
14✔
581

582
                            if (!errors.empty())
14✔
583
                            {
584
                                throw exception_list(HPX_MOVE(errors));
14✔
585
                            }
586
                        }
28✔
587
                        RandIter first = left.get();
63✔
588
                        RandIter last = right.get();
63✔
589

590
                        std::rotate(first, mid, last);
63✔
591

592
                        // for some library implementations std::rotate
593
                        // does not return the new middle point
594
                        std::advance(first, std::distance(mid, last));
63✔
595
                        return first;
63✔
596
                    },
28✔
597
                    HPX_MOVE(left), HPX_MOVE(right));
598
            }
195✔
599
        };
600

601
        template <typename BidirIter, typename Sent, typename F, typename Proj>
602
        static BidirIter stable_partition_seq(
25✔
603
            BidirIter first, Sent last, F&& f, Proj&& proj)
604
        {
605
            using value_type =
606
                typename std::iterator_traits<BidirIter>::value_type;
607
            std::vector<value_type> falseValues;
25✔
608

609
            BidirIter next = first;
25✔
610
            while (first != last)
130,115✔
611
            {
612
                if (HPX_INVOKE(f, HPX_INVOKE(proj, *first)))
130,102✔
613
                {
614
                    *next = HPX_MOVE(*first);
40,027✔
615
                    ++next;
40,027✔
616
                }
40,027✔
617
                else
618
                {
619
                    falseValues.emplace_back(HPX_MOVE(*first));
90,063✔
620
                }
621

622
                ++first;
130,090✔
623
            }
624

625
            util::move(std::begin(falseValues), std::end(falseValues), next);
13✔
626
            return next;
627
        }
25✔
628

629
        template <typename Iter>
630
        struct stable_partition
631
          : public detail::algorithm<stable_partition<Iter>, Iter>
632
        {
633
            stable_partition()
38✔
634
              : stable_partition::algorithm("stable_partition")
38✔
635
            {
38✔
636
            }
38✔
637

638
            template <typename ExPolicy, typename BidirIter, typename Sent,
639
                typename F, typename Proj>
640
            static BidirIter sequential(
25✔
641
                ExPolicy&&, BidirIter first, Sent last, F&& f, Proj&& proj)
642
            {
643
                return stable_partition_seq(
25✔
644
                    first, last, HPX_FORWARD(F, f), HPX_FORWARD(Proj, proj));
25✔
645
            }
646

647
            template <typename ExPolicy, typename RandIter, typename Sent,
648
                typename F, typename Proj>
649
            static typename util::detail::algorithm_result<ExPolicy,
650
                RandIter>::type
651
            parallel(ExPolicy&& policy, RandIter first, Sent last, F&& f,
13✔
652
                Proj&& proj)
653
            {
654
                using algorithm_result =
655
                    util::detail::algorithm_result<ExPolicy, RandIter>;
656
                using difference_type =
657
                    typename std::iterator_traits<RandIter>::difference_type;
658

659
                future<RandIter> result;
13✔
660
                auto last_iter = first;
13✔
661

662
                try
663
                {
664
                    // advances last_iter to last and gets distance
665
                    difference_type size =
13✔
666
                        detail::advance_and_get_distance(last_iter, last);
13✔
667

668
                    if (size == 0)
13✔
669
                    {
670
                        result = hpx::make_ready_future(HPX_MOVE(last_iter));
×
671
                    }
×
672
                    else
673
                    {
674
                        std::size_t const cores =
13✔
675
                            execution::processing_units_count(
13✔
676
                                policy.parameters(), policy.executor(),
13✔
677
                                hpx::chrono::null_duration, size);
678

679
                        std::size_t chunk_size = execution::get_chunk_size(
13✔
680
                            policy.parameters(), policy.executor(),
13✔
681
                            hpx::chrono::null_duration, cores, size);
682

683
                        std::size_t max_chunks =
13✔
684
                            execution::maximal_number_of_chunks(
13✔
685
                                policy.parameters(), policy.executor(), cores,
13✔
686
                                size);
687

688
                        util::detail::adjust_chunk_size_and_max_chunks(
13✔
689
                            cores, size, chunk_size, max_chunks);
13✔
690

691
                        result = stable_partition_helper()(
13✔
692
                            HPX_FORWARD(ExPolicy, policy), first, last_iter,
13✔
693
                            size, HPX_FORWARD(F, f), HPX_FORWARD(Proj, proj),
13✔
694
                            max_chunks);
13✔
695
                    }
696
                }
13✔
697
                catch (...)
698
                {
699
                    result = hpx::make_exceptional_future<RandIter>(
×
700
                        std::current_exception());
×
701
                }
×
702

703
                if (result.has_exception())
13✔
704
                {
705
                    return algorithm_result::get(
×
706
                        detail::handle_exception<ExPolicy, RandIter>::call(
×
707
                            HPX_MOVE(result)));
×
708
                }
709

710
                return algorithm_result::get(HPX_MOVE(result));
13✔
711
            }
13✔
712
        };
713
        /// \endcond
714
    }    // namespace detail
715

716
    // clang-format off
717
    template <typename ExPolicy, typename BidirIter, typename F,
718
        typename Proj = util::projection_identity,
719
        HPX_CONCEPT_REQUIRES_(
720
            hpx::is_execution_policy_v<ExPolicy> &&
721
            hpx::traits::is_iterator_v<BidirIter> &&
722
            traits::is_projected_v<Proj, BidirIter> &&
723
            traits::is_indirect_callable_v<ExPolicy, F,
724
                            traits::projected<Proj, BidirIter>>
725
        )>
726
    // clang-format on
727
    HPX_DEPRECATED_V(1, 8,
728
        "hpx::parallel::stable_partition is deprecated, use "
729
        "hpx::stable_partition instead")
730
        util::detail::algorithm_result_t<ExPolicy, BidirIter> stable_partition(
731
            ExPolicy&& policy, BidirIter first, BidirIter last, F&& f,
732
            Proj&& proj = Proj())
733
    {
734
        static_assert((hpx::traits::is_bidirectional_iterator_v<BidirIter>),
735
            "Requires at least bidirectional iterator.");
736

737
#if defined(HPX_GCC_VERSION) && HPX_GCC_VERSION >= 100000
738
#pragma GCC diagnostic push
739
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
740
#endif
741
        using is_seq = std::integral_constant<bool,
742
            hpx::is_sequenced_execution_policy_v<ExPolicy> ||
743
                !hpx::traits::is_random_access_iterator_v<BidirIter>>;
744

745
        return detail::stable_partition<BidirIter>().call2(
746
            HPX_FORWARD(ExPolicy, policy), is_seq(), first, last,
747
            HPX_FORWARD(F, f), HPX_FORWARD(Proj, proj));
748
#if defined(HPX_GCC_VERSION) && HPX_GCC_VERSION >= 100000
749
#pragma GCC diagnostic pop
750
#endif
751
    }
752

753
    /////////////////////////////////////////////////////////////////////////////
754
    // partition
755
    namespace detail {
756
        /// \cond NOINTERNAL
757

758
        // sequential partition with projection function for bidirectional iterator.
759
        template <typename BidirIter, typename Pred, typename Proj,
760
            HPX_CONCEPT_REQUIRES_(
761
                hpx::traits::is_bidirectional_iterator_v<BidirIter>)>
762
        BidirIter sequential_partition(
130,124✔
763
            BidirIter first, BidirIter last, Pred&& pred, Proj&& proj)
764
        {
765
            while (true)
53,703,806✔
766
            {
767
                while (
150,992,613✔
768
                    first != last && HPX_INVOKE(pred, HPX_INVOKE(proj, *first)))
97,288,815✔
769
                    ++first;
43,585,009✔
770
                if (first == last)
53,703,798✔
771
                    break;
63,039✔
772

773
                while (first != --last &&
191,734,553✔
774
                    !HPX_INVOKE(pred, HPX_INVOKE(proj, *last)))
138,026,717✔
775
                    ;
776
                if (first == last)
53,640,759✔
777
                    break;
67,077✔
778

779
#if defined(HPX_HAVE_CXX20_STD_RANGES_ITER_SWAP)
780
                std::ranges::iter_swap(first++, last);
781
#else
782
                std::iter_swap(first++, last);
53,573,682✔
783
#endif
784
            }
785

786
            return first;
130,116✔
787
        }
×
788

789
        // sequential partition with projection function for forward iterator.
790
        template <typename FwdIter, typename Pred, typename Proj,
791
            HPX_CONCEPT_REQUIRES_(hpx::traits::is_forward_iterator_v<FwdIter> &&
792
                !hpx::traits::is_bidirectional_iterator_v<FwdIter>)>
793
        FwdIter sequential_partition(
18✔
794
            FwdIter first, FwdIter last, Pred&& pred, Proj&& proj)
795
        {
796
            while (first != last && HPX_INVOKE(pred, HPX_INVOKE(proj, *first)))
10,030✔
797
                ++first;
10,012✔
798

799
            if (first == last)
14✔
800
                return first;
1✔
801

802
            for (FwdIter it = std::next(first); it != last; ++it)
259,776✔
803
            {
804
                if (HPX_INVOKE(pred, HPX_INVOKE(proj, *it)))
259,763✔
805
                {
806
#if defined(HPX_HAVE_CXX20_STD_RANGES_ITER_SWAP)
807
                    std::ranges::iter_swap(first++, it);
808
#else
809
                    std::iter_swap(first++, it);
119,854✔
810
#endif
811
                }
119,854✔
812
            }
259,763✔
813

814
            return first;
13✔
815
        }
14✔
816

817
        struct partition_helper
818
        {
819
            template <typename FwdIter>
820
            struct block
×
821
            {
822
                FwdIter first;
823
                FwdIter last;
824
                // Maybe 'std::int64_t' is enough to avoid overflow.
825
                std::int64_t block_no;
826

827
                block()
391,761✔
828
                  : first()
391,761✔
829
                  , last()
391,761✔
830
                  , block_no(-1)
391,761✔
831
                {
832
                }
391,761✔
833

834
                block(FwdIter first, FwdIter last, std::int64_t block_no = -1)
555,427✔
835
                  : first(first)
555,427✔
836
                  , last(last)
555,427✔
837
                  , block_no(block_no)
555,427✔
838
                {
839
                }
555,427✔
840

841
                bool empty() const
780,769,396✔
842
                {
843
                    return first == last;
781,000,182✔
844
                }
845

846
                // The blocks are sorted by their positions.
847
                // The block_no implies block's position.
848
                // If the block is on leftside of boundary, block_no is negative number.
849
                // Otherwise, block_no is positive number.
850
                // The farther from the boundary, the larger its absolute value.
851
                // The example of sorted blocks below (the number means block_no):
852
                //     -3  -2  -1  1  2  3  4
853
                bool operator<(block<FwdIter> const& other) const
54✔
854
                {
855
                    if ((this->block_no < 0 && other.block_no < 0) ||
82✔
856
                        (this->block_no > 0 && other.block_no > 0))
42✔
857
                        return this->block_no > other.block_no;
25✔
858
                    else
859
                        return this->block_no < other.block_no;
29✔
860
                }
54✔
861
            };
862

863
            template <typename Iter, typename Enable = void>
864
            class block_manager;
24✔
865

866
            // block manager for random access iterator.
867
            template <typename RandIter>
868
            class block_manager<RandIter,
869
                std::enable_if_t<
870
                    hpx::traits::is_random_access_iterator_v<RandIter>>>
871
            {
872
            public:
873
                block_manager(
130,080✔
874
                    RandIter first, RandIter last, std::size_t block_size)
875
                  : first_(first)
130,080✔
876
                  , left_(0)
130,080✔
877
                  , right_(std::distance(first, last))
130,080✔
878
                  , block_size_(block_size)
130,080✔
879
                {
880
                }
130,080✔
881
                block_manager(const block_manager&) = delete;
882
                block_manager& operator=(const block_manager&) = delete;
883

884
                // Get block from the end of leftside of boundary.
885
                block<RandIter> get_left_block()
154,557✔
886
                {
887
                    std::lock_guard<decltype(mutex_)> lk(mutex_);
154,557✔
888

889
                    if (left_ >= right_)
154,557✔
890
                        return {first_, first_};
5,797✔
891

892
                    std::size_t begin_index = left_;
148,760✔
893
                    std::size_t end_index =
148,760✔
894
                        (std::min)(left_ + block_size_, right_);
148,760✔
895

896
                    left_ += end_index - begin_index;
148,760✔
897

898
                    RandIter begin_iter = std::next(first_, begin_index);
148,760✔
899
                    RandIter end_iter = std::next(first_, end_index);
148,760✔
900

901
                    boundary_ = end_iter;
148,760✔
902

903
                    return {begin_iter, end_iter, left_block_no_--};
148,760✔
904
                }
154,557✔
905

906
                // Get block from the end of rightside of boundary.
907
                block<RandIter> get_right_block()
270,426✔
908
                {
909
                    std::lock_guard<decltype(mutex_)> lk(mutex_);
270,426✔
910

911
                    if (left_ >= right_)
270,426✔
912
                        return {first_, first_};
260,577✔
913

914
                    std::size_t begin_index =
9,849✔
915
                        (std::max)(right_ - block_size_, left_);
9,849✔
916
                    std::size_t end_index = right_;
9,849✔
917

918
                    right_ -= end_index - begin_index;
9,849✔
919

920
                    RandIter begin_iter = std::next(first_, begin_index);
9,849✔
921
                    RandIter end_iter = std::next(first_, end_index);
9,849✔
922

923
                    boundary_ = begin_iter;
9,849✔
924

925
                    return {begin_iter, end_iter, right_block_no_++};
9,849✔
926
                }
270,426✔
927

928
                RandIter boundary()
130,076✔
929
                {
930
                    return boundary_;
130,076✔
931
                }
932

933
            private:
934
                RandIter first_, boundary_;
935
                std::size_t left_, right_;
936
                std::size_t block_size_;
937
                std::int64_t left_block_no_{-1}, right_block_no_{1};
130,080✔
938
                hpx::spinlock mutex_;
939
            };
940

941
            // block manager for forward access iterator.
942
            template <typename FwdIter>
943
            class block_manager<FwdIter,
944
                std::enable_if_t<hpx::traits::is_forward_iterator_v<FwdIter> &&
945
                    !hpx::traits::is_random_access_iterator_v<FwdIter>>>
946
            {
947
            public:
948
                // In constructor, prepare all blocks for fast acquirements of blocks.
949
                block_manager(
48✔
950
                    FwdIter first, FwdIter last, std::size_t block_size)
951
                  : boundary_(first)
24✔
952
                  , blocks_((std::distance(first, last) + block_size - 1) /
48✔
953
                        block_size)
24✔
954
                {
955
                    left_ = 0;
24✔
956
                    right_ = blocks_.size();
24✔
957

958
                    if (blocks_.size() == 1)
24✔
959
                    {
960
                        blocks_.front() = {first, last};
×
961
                        return;
×
962
                    }
963

964
                    FwdIter next = std::next(first, block_size);
24✔
965

966
                    blocks_.front() = {first, next};
24✔
967

968
                    for (std::size_t i = 1; i < blocks_.size() - 1; ++i)
24✔
969
                    {
970
                        first = next;
×
971
                        next = std::next(first, block_size);
×
972
                        blocks_[i] = {first, next};
×
973
                    }
×
974

975
                    blocks_.back() = {next, last};
24✔
976
                }
24✔
977

978
                block_manager(const block_manager&) = delete;
979
                block_manager& operator=(const block_manager&) = delete;
980

981
                // Get block from the end of leftside of boundary.
982
                block<FwdIter> get_left_block()
170✔
983
                {
984
                    std::lock_guard<decltype(mutex_)> lk(mutex_);
170✔
985

986
                    if (left_ >= right_)
170✔
987
                        return {boundary_, boundary_};
146✔
988

989
                    boundary_ = blocks_[left_].last;
24✔
990
                    blocks_[left_].block_no = left_block_no_--;
24✔
991

992
                    // NOLINTNEXTLINE(bugprone-macro-repeated-side-effects)
993
                    return HPX_MOVE(blocks_[left_++]);
24✔
994
                }
170✔
995

996
                // // Get block from the end of rightside of boundary.
997
                block<FwdIter> get_right_block()
182✔
998
                {
999
                    std::lock_guard<decltype(mutex_)> lk(mutex_);
182✔
1000

1001
                    if (left_ >= right_)
182✔
1002
                        return {boundary_, boundary_};
158✔
1003

1004
                    boundary_ = blocks_[--right_].first;
24✔
1005
                    blocks_[right_].block_no = right_block_no_++;
24✔
1006

1007
                    return HPX_MOVE(blocks_[right_]);
24✔
1008
                }
182✔
1009

1010
                FwdIter boundary()
16✔
1011
                {
1012
                    return boundary_;
16✔
1013
                }
1014

1015
            private:
1016
                FwdIter boundary_;
1017
                std::vector<block<FwdIter>> blocks_;
1018
                std::size_t left_, right_;
1019
                std::int64_t left_block_no_{-1}, right_block_no_{1};
24✔
1020
                hpx::spinlock mutex_;
1021
            };
1022

1023
            // std::swap_ranges doesn't support overlapped ranges in standard.
1024
            // But, actually general implementations of std::swap_ranges are useful
1025
            //     in specific cases.
1026
            // The problem is that standard doesn't guarantee that implementation.
1027
            // The swap_ranges_forward is the general implementation of
1028
            //     std::swap_ranges for guaranteeing utilizations in specific cases.
1029
            // If dest is previous to first, the range [first, last) can be
1030
            //     successfully moved to the range [dest, dest+distance(first, last)).
1031
            template <class FwdIter1, class FwdIter2>
1032
            static FwdIter2 swap_ranges_forward(
117✔
1033
                FwdIter1 first, FwdIter1 last, FwdIter2 dest)
1034
            {
1035
                while (first != last)
1,112,723✔
1036
                {
1037
#if defined(HPX_HAVE_CXX20_STD_RANGES_ITER_SWAP)
1038
                    std::ranges::iter_swap(first++, dest++);
1039
#else
1040
                    std::iter_swap(first++, dest++);
1,112,606✔
1041
#endif
1042
                }
1043

1044
                return dest;
117✔
1045
            }
×
1046

1047
            // The function which performs sub-partitioning.
1048
            template <typename FwdIter, typename Pred, typename Proj>
1049
            static block<FwdIter> partition_thread(
130,587✔
1050
                block_manager<FwdIter>& block_manager, Pred pred, Proj proj)
1051
            {
1052
                block<FwdIter> left_block, right_block;
130,587✔
1053

1054
                left_block = block_manager.get_left_block();
130,587✔
1055
                right_block = block_manager.get_right_block();
130,587✔
1056

1057
                while (true)
104,628,214✔
1058
                {
1059
                    while ((!left_block.empty() ||
481,530,413✔
1060
                               !(left_block = block_manager.get_left_block())
24,140✔
1061
                                    .empty()) &&
24,140✔
1062
                        HPX_INVOKE(pred, HPX_INVOKE(proj, *left_block.first)))
376,923,373✔
1063
                    {
1064
                        ++left_block.first;
272,319,611✔
1065
                    }
1066

1067
                    while ((!right_block.empty() ||
299,463,159✔
1068
                               !(right_block = block_manager.get_right_block())
140,021✔
1069
                                    .empty()) &&
140,021✔
1070
                        !HPX_INVOKE(pred, HPX_INVOKE(proj, *right_block.first)))
194,699,230✔
1071
                    {
1072
                        ++right_block.first;
90,256,832✔
1073
                    }
1074

1075
                    if (left_block.empty())
104,630,334✔
1076
                        return right_block;
5,487✔
1077
                    if (right_block.empty())
104,622,715✔
1078
                        return left_block;
125,088✔
1079

1080
#if defined(HPX_HAVE_CXX20_STD_RANGES_ITER_SWAP)
1081
                    std::ranges::iter_swap(
1082
                        left_block.first++, right_block.first++);
1083
#else
1084
                    std::iter_swap(left_block.first++, right_block.first++);
104,470,966✔
1085
#endif
1086
                }
1087
            }
130,575✔
1088

1089
            // The function which collapses remaining blocks.
1090
            // Performs sequential sub-partitioning to remaining blocks for
1091
            //     reducing the number and size of remaining blocks.
1092
            template <typename FwdIter, typename Pred, typename Proj>
1093
            static void collapse_remaining_blocks(
130,092✔
1094
                std::vector<block<FwdIter>>& remaining_blocks, Pred& pred,
1095
                Proj& proj)
1096
            {
1097
                if (remaining_blocks.empty())
130,092✔
1098
                    return;
4,899✔
1099

1100
                auto left_iter = std::begin(remaining_blocks);
125,193✔
1101
                auto right_iter = std::end(remaining_blocks) - 1;
125,193✔
1102

1103
                if (left_iter->block_no > 0 || right_iter->block_no < 0)
125,193✔
1104
                    return;
125,183✔
1105

1106
                while (true)
2,195✔
1107
                {
1108
                    while (true)
4,307✔
1109
                    {
1110
                        if (left_iter->empty())
4,307✔
1111
                        {
1112
                            ++left_iter;
13✔
1113
                            if (left_iter == std::end(remaining_blocks) ||
13✔
1114
                                left_iter->block_no > 0)
13✔
1115
                                break;
6✔
1116
                        }
7✔
1117
                        if (!HPX_INVOKE(
4,301✔
1118
                                pred, HPX_INVOKE(proj, *left_iter->first)))
1119
                            break;
2,189✔
1120
                        ++left_iter->first;
2,112✔
1121
                    }
1122

1123
                    while (true)
4,459✔
1124
                    {
1125
                        if (right_iter->empty())
4,459✔
1126
                        {
1127
                            if (right_iter == std::begin(remaining_blocks) ||
12✔
1128
                                (--right_iter)->block_no < 0)
12✔
1129
                                break;
4✔
1130
                        }
8✔
1131
                        if (HPX_INVOKE(
4,455✔
1132
                                pred, HPX_INVOKE(proj, *right_iter->first)))
1133
                            break;
2,191✔
1134
                        ++right_iter->first;
2,264✔
1135
                    }
1136

1137
                    if (left_iter == std::end(remaining_blocks) ||
2,195✔
1138
                        left_iter->block_no > 0)
2,195✔
1139
                        break;
6✔
1140
                    if (right_iter->empty() || right_iter->block_no < 0)
2,189✔
1141
                        break;
4✔
1142

1143
#if defined(HPX_HAVE_CXX20_STD_RANGES_ITER_SWAP)
1144
                    std::ranges::iter_swap(
1145
                        left_iter->first++, right_iter->first++);
1146
#else
1147
                    std::iter_swap(left_iter->first++, right_iter->first++);
2,185✔
1148
#endif
1149
                }
1150

1151
                if (left_iter < right_iter ||
18✔
1152
                    (!right_iter->empty() && left_iter == right_iter))
8✔
1153
                {
1154
                    remaining_blocks.erase(
20✔
1155
                        right_iter->empty() ? right_iter : right_iter + 1,
10✔
1156
                        std::end(remaining_blocks));
10✔
1157

1158
                    remaining_blocks.erase(
20✔
1159
                        std::begin(remaining_blocks), left_iter);
10✔
1160
                }
10✔
1161
                else
1162
                {
1163
                    remaining_blocks.clear();
×
1164
                }
1165
            }
130,092✔
1166

1167
            // The function which merges remaining blocks that are placed
1168
            //     leftside of boundary. Requires bidirectional iterator.
1169
            // Move remaining blocks to the adjacent left of the boundary.
1170
            // In the end, all remaining blocks are merged into one block which
1171
            //     is adjacent to the left of boundary.
1172
            // clang-format off
1173
            template <typename BidirIter,
1174
                HPX_CONCEPT_REQUIRES_(
1175
                    hpx::traits::is_bidirectional_iterator_v<BidirIter>
1176
                )>
1177
            // clang-format on
1178
            static block<BidirIter> merge_leftside_remaining_blocks(
125,068✔
1179
                std::vector<block<BidirIter>>& remaining_blocks,
1180
                BidirIter boundary, BidirIter first)
1181
            {
1182
                HPX_ASSERT(!remaining_blocks.empty());
125,068✔
1183
                HPX_UNUSED(first);
125,068✔
1184

1185
                auto boundary_rbegin =
1186
                    std::reverse_iterator<BidirIter>(boundary);
125,068✔
1187
                for (auto it = remaining_blocks.rbegin();
250,136✔
1188
                     it != remaining_blocks.rend(); ++it)
250,136✔
1189
                {
1190
                    auto rbegin = std::reverse_iterator<BidirIter>(it->last);
125,068✔
1191
                    auto rend = std::reverse_iterator<BidirIter>(it->first);
125,068✔
1192

1193
                    if (boundary_rbegin == rbegin)
125,068✔
1194
                    {
1195
                        boundary_rbegin = rend;
125,068✔
1196
                        continue;
125,068✔
1197
                    }
1198

1199
                    boundary_rbegin =
×
1200
                        swap_ranges_forward(rbegin, rend, boundary_rbegin);
×
1201
                }
×
1202

1203
                return {boundary_rbegin.base(), boundary};
125,068✔
1204
            }
×
1205

1206
            // The function which merges remaining blocks that are placed
1207
            //     leftside of boundary. Requires forward iterator.
1208
            // Move remaining blocks to the adjacent left of the boundary.
1209
            // In the end, all remaining blocks are merged into one block which
1210
            //     is adjacent to the left of boundary.
1211
            // clang-format off
1212
            template <typename FwdIter,
1213
                HPX_CONCEPT_REQUIRES_(
1214
                    hpx::traits::is_forward_iterator_v<FwdIter> &&
1215
                    !hpx::traits::is_bidirectional_iterator_v<FwdIter>
1216
                )>
1217
            // clang-format on
1218
            static block<FwdIter> merge_leftside_remaining_blocks(
7✔
1219
                std::vector<block<FwdIter>>& remaining_blocks, FwdIter boundary,
1220
                FwdIter first)
1221
            {
1222
                HPX_ASSERT(!remaining_blocks.empty());
7✔
1223

1224
                std::vector<FwdIter> dest_iters(remaining_blocks.size());
7✔
1225
                std::vector<std::size_t> dest_iter_indexes(
7✔
1226
                    remaining_blocks.size());
7✔
1227
                std::vector<std::size_t> remaining_block_indexes(
7✔
1228
                    remaining_blocks.size());
7✔
1229
                std::vector<std::size_t> counts(remaining_blocks.size());
7✔
1230
                std::size_t count_sum = 0u;
7✔
1231

1232
                for (std::size_t i = 0; i < counts.size(); ++i)
14✔
1233
                {
1234
                    counts[i] = std::distance(
7✔
1235
                        remaining_blocks[i].first, remaining_blocks[i].last);
7✔
1236
                    count_sum += counts[i];
7✔
1237
                }
7✔
1238

1239
                remaining_block_indexes[0] =
7✔
1240
                    std::distance(first, remaining_blocks[0].first);
7✔
1241
                for (std::size_t i = 1; i < remaining_block_indexes.size(); ++i)
7✔
1242
                {
1243
                    remaining_block_indexes[i] =
×
1244
                        remaining_block_indexes[i - 1] + counts[i - 1] +
×
1245
                        std::distance(remaining_blocks[i - 1].last,
×
1246
                            remaining_blocks[i].first);
×
1247
                }
×
1248

1249
                std::size_t boundary_end_index = std::distance(first, boundary);
7✔
1250
                std::size_t boundary_begin_index =
7✔
1251
                    boundary_end_index - count_sum;
7✔
1252

1253
                dest_iters[0] = std::next(first, boundary_begin_index);
7✔
1254
                dest_iter_indexes[0] = boundary_begin_index;
7✔
1255

1256
                for (std::size_t i = 0; i < dest_iters.size() - 1; ++i)
7✔
1257
                {
1258
                    dest_iters[i + 1] = std::next(dest_iters[i], counts[i]);
×
1259
                    dest_iter_indexes[i + 1] = dest_iter_indexes[i] + counts[i];
×
1260
                }
×
1261

1262
                for (std::int64_t i = std::int64_t(dest_iters.size() - 1);
14✔
1263
                     i >= 0; --i)
14✔
1264
                {
1265
                    if (remaining_blocks[i].first == dest_iters[i])
7✔
1266
                        continue;
7✔
1267

1268
                    if (remaining_block_indexes[i] + counts[i] <=
×
1269
                        dest_iter_indexes[i])
×
1270
                    {
1271
                        // when the ranges are not overlapped each other.
1272
                        swap_ranges_forward(remaining_blocks[i].first,
×
1273
                            remaining_blocks[i].last, dest_iters[i]);
×
1274
                    }
×
1275
                    else
1276
                    {
1277
                        // when the ranges are overlapped each other.
1278
                        swap_ranges_forward(remaining_blocks[i].first,
×
1279
                            dest_iters[i], remaining_blocks[i].last);
×
1280
                    }
1281
                }
×
1282

1283
                return {dest_iters[0], boundary};
7✔
1284
            }
7✔
1285

1286
            // The function which merges remaining blocks into
1287
            //     one block which is adjacent to boundary.
1288
            template <typename FwdIter>
1289
            static block<FwdIter> merge_remaining_blocks(
130,092✔
1290
                std::vector<block<FwdIter>>& remaining_blocks, FwdIter boundary,
1291
                FwdIter first)
1292
            {
1293
                if (remaining_blocks.empty())
130,092✔
1294
                    return {boundary, boundary};
4,899✔
1295

1296
                if (remaining_blocks.front().block_no < 0)
125,193✔
1297
                {
1298
                    // when blocks are placed in left side of boundary.
1299
                    return merge_leftside_remaining_blocks(
125,075✔
1300
                        remaining_blocks, boundary, first);
125,075✔
1301
                }
1302
                else
1303
                {
1304
                    // when blocks are placed in right side of boundary.
1305
                    FwdIter boundary_end = boundary;
118✔
1306
                    for (auto& block : remaining_blocks)
238✔
1307
                    {
1308
                        if (block.first == boundary_end)
120✔
1309
                        {
1310
                            boundary_end = block.last;
3✔
1311
                            continue;
3✔
1312
                        }
1313

1314
                        boundary_end = swap_ranges_forward(
117✔
1315
                            block.first, block.last, boundary_end);
117✔
1316
                    }
1317

1318
                    return {boundary, boundary_end};
118✔
1319
                }
×
1320
            }
130,092✔
1321

1322
            template <typename ExPolicy, typename FwdIter, typename Pred,
1323
                typename Proj>
1324
            static FwdIter call(ExPolicy&& policy, FwdIter first, FwdIter last,
133,467✔
1325
                Pred&& pred, Proj&& proj)
1326
            {
1327
                if (first == last)
133,467✔
1328
                    return first;
3,363✔
1329

1330
                std::size_t const cores = execution::processing_units_count(
130,104✔
1331
                    policy.parameters(), policy.executor(),
130,104✔
1332
                    hpx::chrono::null_duration, std::distance(first, last));
130,104✔
1333

1334
                // TODO: Find more better block size.
1335
                const std::size_t block_size = std::size_t(20000);
130,104✔
1336
                block_manager<FwdIter> block_manager(first, last, block_size);
130,104✔
1337

1338
                std::vector<hpx::future<block<FwdIter>>>
1339
                    remaining_block_futures(cores);
130,108✔
1340

1341
                // Main parallel phase: perform sub-partitioning in each thread.
1342
                for (std::size_t i = 0; i < remaining_block_futures.size(); ++i)
260,691✔
1343
                {
1344
                    remaining_block_futures[i] = execution::async_execute(
130,587✔
1345
                        policy.executor(), [&block_manager, pred, proj]() {
261,174✔
1346
                            return partition_thread(block_manager, pred, proj);
130,587✔
1347
                        });
1348
                }
130,587✔
1349

1350
                // Wait sub-partitioning to be all finished.
1351
                hpx::wait_all_nothrow(remaining_block_futures);
130,104✔
1352

1353
                // Handle exceptions in parallel phrase.
1354
                std::list<std::exception_ptr> errors;
130,104✔
1355

1356
                util::detail::handle_local_exceptions<ExPolicy>::call(
130,104✔
1357
                    remaining_block_futures, errors);
1358

1359
                std::vector<block<FwdIter>> remaining_blocks(
130,092✔
1360
                    remaining_block_futures.size());
130,092✔
1361

1362
                // Get remaining blocks from the result of sub-partitioning.
1363
                for (std::size_t i = 0; i < remaining_block_futures.size(); ++i)
260,631✔
1364
                    remaining_blocks[i] = remaining_block_futures[i].get();
130,539✔
1365

1366
                // Remove blocks that are empty.
1367
                FwdIter boundary = block_manager.boundary();
130,092✔
1368
                remaining_blocks.erase(
130,092✔
1369
                    std::remove_if(std::begin(remaining_blocks),
130,092✔
1370
                        std::end(remaining_blocks),
130,092✔
1371
                        [](block<FwdIter> const& block) -> bool {
130,539✔
1372
                            return block.empty();
130,539✔
1373
                        }),
1374
                    std::end(remaining_blocks));
130,092✔
1375

1376
                // Sort remaining blocks to be listed from left to right.
1377
                std::sort(
130,092✔
1378
                    std::begin(remaining_blocks), std::end(remaining_blocks));
130,092✔
1379

1380
                // Collapse remaining blocks each other.
1381
                collapse_remaining_blocks(remaining_blocks, pred, proj);
130,092✔
1382

1383
                // Merge remaining blocks into one block
1384
                //     which is adjacent to boundary.
1385
                block<FwdIter> unpartitioned_block =
1386
                    merge_remaining_blocks(remaining_blocks, boundary, first);
130,092✔
1387

1388
                // Perform sequential partition to unpartitioned range.
1389
                FwdIter real_boundary =
1390
                    sequential_partition(unpartitioned_block.first,
260,184✔
1391
                        unpartitioned_block.last, pred, proj);
130,092✔
1392

1393
                return real_boundary;
130,092✔
1394
            }
133,467✔
1395
        };
1396

1397
        template <typename ExPolicy, typename FwdIter, typename Pred,
1398
            typename Proj>
1399
        hpx::future<FwdIter> parallel_partition(ExPolicy&& policy,
133,467✔
1400
            FwdIter first, FwdIter last, Pred&& pred, Proj&& proj)
1401
        {
1402
            return execution::async_execute(
133,467✔
1403
                policy.executor(), [=]() mutable -> FwdIter {
266,934✔
1404
                    try
1405
                    {
1406
                        return partition_helper::call(
133,467✔
1407
                            policy, first, last, pred, proj);
133,467✔
1408
                    }
12✔
1409
                    catch (...)
1410
                    {
1411
                        util::detail::handle_local_exceptions<ExPolicy>::call(
12✔
1412
                            std::current_exception());
12✔
1413
                    }
12✔
1414
                });
24✔
1415
        }
×
1416

1417
        template <typename FwdIter>
1418
        struct partition : public detail::algorithm<partition<FwdIter>, FwdIter>
1419
        {
1420
            partition()
133,517✔
1421
              : partition::algorithm("partition")
133,517✔
1422
            {
133,517✔
1423
            }
133,517✔
1424

1425
            template <typename ExPolicy, typename Sent, typename Pred,
1426
                typename Proj = util::projection_identity>
1427
            static FwdIter sequential(
50✔
1428
                ExPolicy, FwdIter first, Sent last, Pred&& pred, Proj&& proj)
1429
            {
1430
                auto last_iter = detail::advance_to_sentinel(first, last);
50✔
1431
                return sequential_partition(first, last_iter,
100✔
1432
                    HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj));
50✔
1433
            }
1434

1435
            template <typename ExPolicy, typename Sent, typename Pred,
1436
                typename Proj = util::projection_identity>
1437
            static
1438
                typename util::detail::algorithm_result<ExPolicy, FwdIter>::type
1439
                parallel(ExPolicy&& policy, FwdIter first, Sent last,
133,467✔
1440
                    Pred&& pred, Proj&& proj)
1441
            {
1442
                using algorithm_result =
1443
                    util::detail::algorithm_result<ExPolicy, FwdIter>;
1444
                auto last_iter = detail::advance_to_sentinel(first, last);
133,467✔
1445

1446
                try
1447
                {
1448
                    return algorithm_result::get(parallel_partition(
133,467✔
1449
                        HPX_FORWARD(ExPolicy, policy), first, last_iter,
133,467✔
1450
                        HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj)));
133,467✔
1451
                }
6✔
1452
                catch (...)
1453
                {
1454
                    return algorithm_result::get(
6✔
1455
                        detail::handle_exception<ExPolicy, FwdIter>::call(
6✔
1456
                            std::current_exception()));
6✔
1457
                }
6✔
1458
            }
133,473✔
1459
        };
1460
        /// \endcond
1461
    }    // namespace detail
1462

1463
    // clang-format off
1464
    template <typename ExPolicy, typename FwdIter, typename Pred,
1465
        typename Proj = util::projection_identity,
1466
        HPX_CONCEPT_REQUIRES_(
1467
            hpx::is_execution_policy_v<ExPolicy> &&
1468
            hpx::traits::is_iterator_v<FwdIter> &&
1469
            traits::is_projected_v<Proj, FwdIter> &&
1470
            traits::is_indirect_callable_v<ExPolicy,
1471
                    Pred, traits::projected<Proj, FwdIter>>
1472
        )>
1473
    // clang-format on
1474
    HPX_DEPRECATED_V(1, 8,
1475
        "hpx::parallel::partition is deprecated, use "
1476
        "hpx::partition instead")
1477
        util::detail::algorithm_result_t<ExPolicy, FwdIter> partition(
1478
            ExPolicy&& policy, FwdIter first, FwdIter last, Pred&& pred,
1479
            Proj&& proj = Proj())
1480
    {
1481
        static_assert((hpx::traits::is_forward_iterator_v<FwdIter>),
1482
            "Required at least forward iterator.");
1483

1484
#if defined(HPX_GCC_VERSION) && HPX_GCC_VERSION >= 100000
1485
#pragma GCC diagnostic push
1486
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1487
#endif
1488
        return detail::partition<FwdIter>().call(HPX_FORWARD(ExPolicy, policy),
1489
            first, last, HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj));
1490
#if defined(HPX_GCC_VERSION) && HPX_GCC_VERSION >= 100000
1491
#pragma GCC diagnostic pop
1492
#endif
1493
    }
1494

1495
    /////////////////////////////////////////////////////////////////////////////
1496
    // partition_copy
1497
    namespace detail {
1498
        /// \cond NOINTERNAL
1499

1500
        // sequential partition_copy with projection function
1501
        template <typename InIter, typename OutIter1, typename OutIter2,
1502
            typename Pred, typename Proj>
1503
        hpx::tuple<InIter, OutIter1, OutIter2> sequential_partition_copy(
50✔
1504
            InIter first, InIter last, OutIter1 dest_true, OutIter2 dest_false,
1505
            Pred&& pred, Proj&& proj)
1506
        {
1507
            while (first != last)
10,280,242✔
1508
            {
1509
                if (HPX_INVOKE(pred, HPX_INVOKE(proj, *first)))
10,280,192✔
1510
                    *dest_true++ = *first;
7,886,733✔
1511
                else
1512
                    *dest_false++ = *first;
2,393,459✔
1513
                first++;
10,280,192✔
1514
            }
1515
            return hpx::make_tuple(
38✔
1516
                HPX_MOVE(last), HPX_MOVE(dest_true), HPX_MOVE(dest_false));
1517
        }
1518

1519
        template <typename IterTuple>
1520
        struct partition_copy
1521
          : public detail::algorithm<partition_copy<IterTuple>, IterTuple>
1522
        {
1523
            partition_copy()
116✔
1524
              : partition_copy::algorithm("partition_copy")
116✔
1525
            {
116✔
1526
            }
116✔
1527

1528
            template <typename ExPolicy, typename InIter, typename Sent,
1529
                typename OutIter1, typename OutIter2, typename Pred,
1530
                typename Proj = util::projection_identity>
1531
            static hpx::tuple<InIter, OutIter1, OutIter2> sequential(ExPolicy,
50✔
1532
                InIter first, Sent last, OutIter1 dest_true,
1533
                OutIter2 dest_false, Pred&& pred, Proj&& proj)
1534
            {
1535
                auto last_iter = detail::advance_to_sentinel(first, last);
50✔
1536
                return sequential_partition_copy(first, last_iter, dest_true,
100✔
1537
                    dest_false, HPX_FORWARD(Pred, pred),
50✔
1538
                    HPX_FORWARD(Proj, proj));
50✔
1539
            }
1540

1541
            template <typename ExPolicy, typename FwdIter1, typename Sent,
1542
                typename FwdIter2, typename FwdIter3, typename Pred,
1543
                typename Proj = util::projection_identity>
1544
            static typename util::detail::algorithm_result<ExPolicy,
1545
                hpx::tuple<FwdIter1, FwdIter2, FwdIter3>>::type
1546
            parallel(ExPolicy&& policy, FwdIter1 first, Sent last,
66✔
1547
                FwdIter2 dest_true, FwdIter3 dest_false, Pred&& pred,
1548
                Proj&& proj)
1549
            {
1550
                using zip_iterator = hpx::util::zip_iterator<FwdIter1, bool*>;
1551
                using result = util::detail::algorithm_result<ExPolicy,
1552
                    hpx::tuple<FwdIter1, FwdIter2, FwdIter3>>;
1553
                using difference_type =
1554
                    typename std::iterator_traits<FwdIter1>::difference_type;
1555
                using output_iterator_offset =
1556
                    std::pair<std::size_t, std::size_t>;
1557

1558
                if (first == last)
66✔
1559
                    return result::get(
×
1560
                        hpx::make_tuple(first, dest_true, dest_false));
×
1561

1562
                auto last_iter = first;
66✔
1563
                difference_type count =
66✔
1564
                    detail::advance_and_get_distance(last_iter, last);
66✔
1565

1566
#if defined(HPX_HAVE_CXX17_SHARED_PTR_ARRAY)
1567
                std::shared_ptr<bool[]> flags(new bool[count]);
66✔
1568
#else
1569
                boost::shared_array<bool> flags(new bool[count]);
1570
#endif
1571
                output_iterator_offset init = {0, 0};
66✔
1572

1573
                using hpx::get;
1574
                using scan_partitioner_type = util::scan_partitioner<ExPolicy,
1575
                    hpx::tuple<FwdIter1, FwdIter2, FwdIter3>,
1576
                    output_iterator_offset>;
1577

1578
                // Note: replacing the invoke() with HPX_INVOKE()
1579
                // below makes gcc generate errors
1580
                auto f1 = [pred = HPX_FORWARD(Pred, pred),
546✔
1581
                              proj = HPX_FORWARD(Proj, proj)](
66✔
1582
                              zip_iterator part_begin,
1583
                              std::size_t part_size) -> output_iterator_offset {
1584
                    std::size_t true_count = 0;
480✔
1585

1586
                    // MSVC complains if pred or proj is captured by ref below
1587
                    util::loop_n<std::decay_t<ExPolicy>>(part_begin, part_size,
480✔
1588
                        [pred, proj, &true_count](
20,336,546✔
1589
                            zip_iterator it) mutable -> void {
1590
                            bool f = hpx::invoke(
20,337,680✔
1591
                                pred, hpx::invoke(proj, get<0>(*it)));
20,337,680✔
1592

1593
                            if ((get<1>(*it) = f))
20,337,680✔
1594
                                ++true_count;
15,662,735✔
1595
                        });
20,335,692✔
1596

1597
                    return output_iterator_offset(
480✔
1598
                        true_count, part_size - true_count);
480✔
1599
                };
1600

1601
                auto f2 = [](output_iterator_offset const& prev_sum,
360✔
1602
                              output_iterator_offset const& curr)
1603
                    -> output_iterator_offset {
1604
                    return output_iterator_offset(
360✔
1605
                        get<0>(prev_sum) + get<0>(curr),
360✔
1606
                        get<1>(prev_sum) + get<1>(curr));
360✔
1607
                };
1608
                auto f3 = [dest_true, dest_false, flags](
2,016✔
1609
                              zip_iterator part_begin, std::size_t part_size,
1610
                              output_iterator_offset val) mutable -> void {
1611
                    HPX_UNUSED(flags);
360✔
1612
                    output_iterator_offset offset = val;
360✔
1613
                    std::size_t count_true = get<0>(offset);
360✔
1614
                    std::size_t count_false = get<1>(offset);
360✔
1615
                    std::advance(dest_true, count_true);
360✔
1616
                    std::advance(dest_false, count_false);
360✔
1617

1618
                    util::loop_n<std::decay_t<ExPolicy>>(part_begin, part_size,
360✔
1619
                        [&dest_true, &dest_false](zip_iterator it) mutable {
20,333,563✔
1620
                            if (get<1>(*it))
20,337,174✔
1621
                                *dest_true++ = get<0>(*it);
15,662,165✔
1622
                            else
1623
                                *dest_false++ = get<0>(*it);
4,674,956✔
1624
                        });
20,334,525✔
1625
                };
360✔
1626

1627
                auto f4 = [last_iter, dest_true, dest_false, flags](
270✔
1628
                              std::vector<output_iterator_offset>&& items,
1629
                              std::vector<hpx::future<void>>&&) mutable
1630
                    -> hpx::tuple<FwdIter1, FwdIter2, FwdIter3> {
1631
                    HPX_UNUSED(flags);
54✔
1632

1633
                    output_iterator_offset count_pair = items.back();
54✔
1634
                    std::size_t count_true = get<0>(count_pair);
54✔
1635
                    std::size_t count_false = get<1>(count_pair);
54✔
1636
                    std::advance(dest_true, count_true);
54✔
1637
                    std::advance(dest_false, count_false);
54✔
1638

1639
                    return hpx::make_tuple(last_iter, dest_true, dest_false);
54✔
1640
                };
1641

1642
                return scan_partitioner_type::call(
66✔
1643
                    HPX_FORWARD(ExPolicy, policy),
66✔
1644
                    zip_iterator(first, flags.get()), count, init,
66✔
1645
                    // step 1 performs first part of scan algorithm
1646
                    HPX_MOVE(f1),
1647
                    // step 2 propagates the partition results from left
1648
                    // to right
1649
                    HPX_MOVE(f2),
1650
                    // step 3 runs final accumulation on each partition
1651
                    HPX_MOVE(f3),
1652
                    // step 4 use this return value
1653
                    HPX_MOVE(f4));
1654
            }
66✔
1655
        };
1656
        /// \endcond
1657
    }    // namespace detail
1658

1659
    // clang-format off
1660
    template <typename ExPolicy, typename FwdIter1, typename FwdIter2,
1661
        typename FwdIter3, typename Pred,
1662
        typename Proj = util::projection_identity,
1663
        HPX_CONCEPT_REQUIRES_(
1664
            hpx::is_execution_policy_v<ExPolicy> &&
1665
            hpx::traits::is_iterator_v<FwdIter1> &&
1666
            hpx::traits::is_iterator_v<FwdIter2> &&
1667
            hpx::traits::is_iterator_v<FwdIter3> &&
1668
            traits::is_projected_v<Proj, FwdIter1> &&
1669
            traits::is_indirect_callable_v<ExPolicy, Pred,
1670
                traits::projected<Proj, FwdIter1>>
1671
        )>
1672
    // clang-format on
1673
    util::detail::algorithm_result_t<ExPolicy, std::pair<FwdIter2, FwdIter3>>
1674
    partition_copy(ExPolicy&& policy, FwdIter1 first, FwdIter1 last,
30✔
1675
        FwdIter2 dest_true, FwdIter3 dest_false, Pred&& pred,
1676
        Proj&& proj = Proj())
1677
    {
1678
        static_assert((hpx::traits::is_forward_iterator_v<FwdIter1>),
1679
            "Required at least forward iterator.");
1680
        static_assert((hpx::traits::is_forward_iterator_v<FwdIter2>),
1681
            "Requires at least forward iterator.");
1682
        static_assert((hpx::traits::is_forward_iterator_v<FwdIter3>),
1683
            "Requires at least forward iterator.");
1684

1685
#if defined(HPX_GCC_VERSION) && HPX_GCC_VERSION >= 100000
1686
#pragma GCC diagnostic push
1687
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1688
#endif
1689
        using result_type = hpx::tuple<FwdIter1, FwdIter2, FwdIter3>;
1690

1691
        return parallel::v1::tuple_to_pair(
30✔
1692
            detail::partition_copy<result_type>().call(
60✔
1693
                HPX_FORWARD(ExPolicy, policy), first, last, dest_true,
30✔
1694
                dest_false, HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj)));
30✔
1695
#if defined(HPX_GCC_VERSION) && HPX_GCC_VERSION >= 100000
1696
#pragma GCC diagnostic pop
1697
#endif
1698
    }
1699
}}}    // namespace hpx::parallel::v1
1700

1701
namespace hpx {
1702
    ///////////////////////////////////////////////////////////////////////////
1703
    // CPO for hpx::stable_partition
1704
    inline constexpr struct stable_partition_t final
1705
      : hpx::detail::tag_parallel_algorithm<stable_partition_t>
1706
    {
1707
        // clang-format off
1708
        template <typename BidirIter, typename F,
1709
            typename Proj = parallel::util::projection_identity,
1710
            HPX_CONCEPT_REQUIRES_(
1711
                hpx::traits::is_iterator_v<BidirIter> &&
1712
                parallel::traits::is_projected_v<Proj, BidirIter> &&
1713
                parallel::traits::is_indirect_callable_v<
1714
                    hpx::execution::sequenced_policy, F,
1715
                    parallel::traits::projected<Proj, BidirIter>>
1716
        )>
1717
        // clang-format on
1718
        friend BidirIter tag_fallback_invoke(hpx::stable_partition_t,
2✔
1719
            BidirIter first, BidirIter last, F&& f, Proj&& proj = Proj())
1720
        {
1721
            static_assert(hpx::traits::is_bidirectional_iterator_v<BidirIter>,
1722
                "Requires at least bidirectional iterator.");
1723

1724
            return hpx::parallel::v1::detail::stable_partition<BidirIter>()
4✔
1725
                .call2(hpx::execution::seq, std::true_type{}, first, last,
2✔
1726
                    HPX_FORWARD(F, f), HPX_FORWARD(Proj, proj));
2✔
1727
        }
1728

1729
        // clang-format off
1730
        template <typename ExPolicy, typename BidirIter, typename F,
1731
            typename Proj = parallel::util::projection_identity,
1732
            HPX_CONCEPT_REQUIRES_(
1733
                hpx::is_execution_policy_v<ExPolicy> &&
1734
                hpx::traits::is_iterator_v<BidirIter> &&
1735
                parallel::traits::is_projected_v<Proj, BidirIter> &&
1736
                parallel::traits::is_indirect_callable_v<ExPolicy, F,
1737
                    parallel::traits::projected<Proj, BidirIter>>
1738
        )>
1739
        // clang-format on
1740
        friend typename parallel::util::detail::algorithm_result_t<ExPolicy,
1741
            BidirIter>
1742
        tag_fallback_invoke(hpx::stable_partition_t, ExPolicy&& policy,
26✔
1743
            BidirIter first, BidirIter last, F&& f, Proj&& proj = Proj())
1744
        {
1745
            static_assert(hpx::traits::is_bidirectional_iterator_v<BidirIter>,
1746
                "Requires at least bidirectional iterator.");
1747

1748
            using is_seq = std::integral_constant<bool,
1749
                hpx::is_sequenced_execution_policy_v<ExPolicy> ||
1750
                    !hpx::traits::is_random_access_iterator_v<BidirIter>>;
1751

1752
            return hpx::parallel::v1::detail::stable_partition<BidirIter>()
52✔
1753
                .call2(HPX_FORWARD(ExPolicy, policy), is_seq(), first, last,
45✔
1754
                    HPX_FORWARD(F, f), HPX_FORWARD(Proj, proj));
26✔
1755
        }
1756
    } stable_partition{};
1757

1758
    ///////////////////////////////////////////////////////////////////////////
1759
    // CPO for hpx::partition
1760
    inline constexpr struct partition_t final
1761
      : hpx::detail::tag_parallel_algorithm<partition_t>
1762
    {
1763
        // clang-format off
1764
        template <typename FwdIter, typename Pred,
1765
            typename Proj = parallel::util::projection_identity,
1766
            HPX_CONCEPT_REQUIRES_(
1767
                hpx::traits::is_iterator_v<FwdIter> &&
1768
                parallel::traits::is_projected_v<Proj, FwdIter> &&
1769
                parallel::traits::is_indirect_callable_v<
1770
                    hpx::execution::sequenced_policy,
1771
                    Pred, parallel::traits::projected<Proj, FwdIter>>
1772
        )>
1773
        // clang-format on
1774
        friend FwdIter tag_fallback_invoke(hpx::partition_t, FwdIter first,
6✔
1775
            FwdIter last, Pred&& pred, Proj&& proj = Proj())
1776
        {
1777
            static_assert(hpx::traits::is_forward_iterator_v<FwdIter>,
1778
                "Required at least forward iterator.");
1779

1780
            return hpx::parallel::v1::detail::partition<FwdIter>().call(
12✔
1781
                hpx::execution::seq, first, last, HPX_FORWARD(Pred, pred),
6✔
1782
                HPX_FORWARD(Proj, proj));
6✔
1783
        }
1784

1785
        // clang-format off
1786
        template <typename ExPolicy, typename FwdIter, typename Pred,
1787
            typename Proj = parallel::util::projection_identity,
1788
            HPX_CONCEPT_REQUIRES_(
1789
                hpx::is_execution_policy_v<ExPolicy> &&
1790
                hpx::traits::is_iterator_v<FwdIter> &&
1791
                parallel::traits::is_projected_v<Proj, FwdIter> &&
1792
                parallel::traits::is_indirect_callable_v<ExPolicy,
1793
                    Pred, parallel::traits::projected<Proj, FwdIter>>
1794
        )>
1795
        // clang-format on
1796
        friend typename parallel::util::detail::algorithm_result_t<ExPolicy,
1797
            FwdIter>
1798
        tag_fallback_invoke(hpx::partition_t, ExPolicy&& policy, FwdIter first,
90✔
1799
            FwdIter last, Pred&& pred, Proj&& proj = Proj())
1800
        {
1801
            static_assert(hpx::traits::is_forward_iterator_v<FwdIter>,
1802
                "Required at least forward iterator.");
1803

1804
            return hpx::parallel::v1::detail::partition<FwdIter>().call(
180✔
1805
                HPX_FORWARD(ExPolicy, policy), first, last,
90✔
1806
                HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj));
90✔
1807
        }
1808
    } partition{};
1809

1810
    ///////////////////////////////////////////////////////////////////////////
1811
    // CPO for hpx::partition_copy
1812
    inline constexpr struct partition_copy_t final
1813
      : hpx::detail::tag_parallel_algorithm<partition_copy_t>
1814
    {
1815
        // clang-format off
1816
        template <typename FwdIter1, typename FwdIter2,
1817
            typename FwdIter3, typename Pred,
1818
            typename Proj = parallel::util::projection_identity,
1819
            HPX_CONCEPT_REQUIRES_(
1820
                hpx::traits::is_iterator_v<FwdIter1> &&
1821
                hpx::traits::is_iterator_v<FwdIter2> &&
1822
                hpx::traits::is_iterator_v<FwdIter3> &&
1823
                parallel::traits::is_projected_v<Proj, FwdIter1> &&
1824
                parallel::traits::is_indirect_callable_v<
1825
                    hpx::execution::sequenced_policy, Pred,
1826
                    parallel::traits::projected<Proj, FwdIter1>>
1827
            )>
1828
        // clang-format on
1829
        friend std::pair<FwdIter2, FwdIter3> tag_fallback_invoke(
6✔
1830
            hpx::partition_copy_t, FwdIter1 first, FwdIter1 last,
1831
            FwdIter2 dest_true, FwdIter3 dest_false, Pred&& pred,
1832
            Proj&& proj = Proj())
1833
        {
1834
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter1>),
1835
                "Required at least forward iterator.");
1836
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter2>),
1837
                "Requires at least forward iterator.");
1838
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter3>),
1839
                "Requires at least forward iterator.");
1840

1841
            using result_type = hpx::tuple<FwdIter1, FwdIter2, FwdIter3>;
1842

1843
            return parallel::v1::tuple_to_pair(
6✔
1844
                parallel::v1::detail::partition_copy<result_type>().call(
12✔
1845
                    hpx::execution::seq, first, last, dest_true, dest_false,
1846
                    HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj)));
6✔
1847
        }
1848

1849
        // clang-format off
1850
        template <typename ExPolicy, typename FwdIter1, typename FwdIter2,
1851
            typename FwdIter3, typename Pred,
1852
            typename Proj = parallel::util::projection_identity,
1853
            HPX_CONCEPT_REQUIRES_(
1854
                hpx::is_execution_policy_v<ExPolicy> &&
1855
                hpx::traits::is_iterator_v<FwdIter1> &&
1856
                hpx::traits::is_iterator_v<FwdIter2> &&
1857
                hpx::traits::is_iterator_v<FwdIter3> &&
1858
                parallel::traits::is_projected_v<Proj, FwdIter1> &&
1859
                parallel::traits::is_indirect_callable_v<ExPolicy, Pred,
1860
                    parallel::traits::projected<Proj, FwdIter1>>
1861
            )>
1862
        // clang-format on
1863
        friend parallel::util::detail::algorithm_result_t<ExPolicy,
1864
            std::pair<FwdIter2, FwdIter3>>
1865
        tag_fallback_invoke(hpx::partition_copy_t, ExPolicy&& policy,
60✔
1866
            FwdIter1 first, FwdIter1 last, FwdIter2 dest_true,
1867
            FwdIter3 dest_false, Pred&& pred, Proj&& proj = Proj())
1868
        {
1869
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter1>),
1870
                "Required at least forward iterator.");
1871
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter2>),
1872
                "Requires at least forward iterator.");
1873
            static_assert((hpx::traits::is_forward_iterator_v<FwdIter3>),
1874
                "Requires at least forward iterator.");
1875

1876
            using result_type = hpx::tuple<FwdIter1, FwdIter2, FwdIter3>;
1877

1878
            return parallel::v1::tuple_to_pair(
60✔
1879
                parallel::v1::detail::partition_copy<result_type>().call(
120✔
1880
                    HPX_FORWARD(ExPolicy, policy), first, last, dest_true,
60✔
1881
                    dest_false, HPX_FORWARD(Pred, pred),
60✔
1882
                    HPX_FORWARD(Proj, proj)));
60✔
1883
        }
×
1884
    } partition_copy{};
1885
}    // namespace hpx
1886

1887
#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