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

STEllAR-GROUP / hpx / #856

28 Dec 2022 02:00AM UTC coverage: 86.602% (+0.05%) from 86.55%
#856

push

StellarBot
Merge #6119

6119: Update CMakeLists.txt r=hkaiser a=khuck

updating the default APEX version


Co-authored-by: Kevin Huck <khuck@cs.uoregon.edu>

174566 of 201573 relevant lines covered (86.6%)

1876093.78 hits per line

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

89.08
/libs/core/algorithms/include/hpx/parallel/algorithms/reduce_by_key.hpp
1
//  Copyright (c) 2015 John Biddiscombe
2
//
3
//  SPDX-License-Identifier: BSL-1.0
4
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
5
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6

7
/// \file parallel/algorithms/reduce_by_key.hpp
8

9
#pragma once
10

11
#if defined(DOXYGEN)
12

13
namespace hpx { namespace experimental {
14
    // clang-format off
15
    /// Reduce by Key performs an inclusive scan reduction operation on elements
16
    /// supplied in key/value pairs. The algorithm produces a single output
17
    /// value for each set of equal consecutive keys in [key_first, key_last).
18
    /// the value being the
19
    /// GENERALIZED_NONCOMMUTATIVE_SUM(op, init, *first, ..., *(first + (i - result))).
20
    /// for the run of consecutive matching keys.
21
    /// The number of keys supplied must match the number of values.
22
    ///
23
    /// \note   Complexity: O(\a last - \a first) applications of the
24
    ///         predicate \a op.
25
    ///
26
    /// \tparam ExPolicy    The type of the execution policy to use (deduced).
27
    ///                     It describes the manner in which the execution
28
    ///                     of the algorithm may be parallelized and the manner
29
    ///                     in which it applies user-provided function objects.
30
    /// \tparam RanIter     The type of the key iterators used (deduced).
31
    ///                     This iterator type must meet the requirements of a
32
    ///                     random access iterator.
33
    /// \tparam RanIter2    The type of the value iterators used (deduced).
34
    ///                     This iterator type must meet the requirements of a
35
    ///                     random access iterator.
36
    /// \tparam FwdIter1    The type of the iterator representing the
37
    ///                     destination key range (deduced).
38
    ///                     This iterator type must meet the requirements of a
39
    ///                     forward iterator.
40
    /// \tparam FwdIter2    The type of the iterator representing the
41
    ///                     destination value range (deduced).
42
    ///                     This iterator type must meet the requirements of a
43
    ///                     forward iterator.
44
    /// \tparam Compare     The type of the optional function/function object to use
45
    ///                     to compare keys (deduced).
46
    ///                     Assumed to be std::equal_to otherwise.
47
    /// \tparam Func        The type of the function/function object to use
48
    ///                     (deduced). Unlike its sequential form, the parallel
49
    ///                     overload of \a reduce_by_key requires \a Func to meet the
50
    ///                     requirements of \a CopyConstructible.
51
    ///
52
    /// \param policy       The execution policy to use for the scheduling of
53
    ///                     the iterations.
54
    /// \param key_first    Refers to the beginning of the sequence of key elements
55
    ///                     the algorithm will be applied to.
56
    /// \param key_last     Refers to the end of the sequence of key elements the
57
    ///                     algorithm will be applied to.
58
    /// \param values_first Refers to the beginning of the sequence of value elements
59
    ///                     the algorithm will be applied to.
60
    /// \param keys_output  Refers to the start output location for the keys
61
    ///                     produced by the algorithm.
62
    /// \param values_output Refers to the start output location for the values
63
    ///                     produced by the algorithm.
64
    /// \param comp         comp is a callable object. The return value of the
65
    ///                     INVOKE operation applied to an object of type Comp,
66
    ///                     when contextually converted to bool, yields true if
67
    ///                     the first argument of the call is less than the
68
    ///                     second, and false otherwise. It is assumed that comp
69
    ///                     will not apply any non-constant function through the
70
    ///                     dereferenced iterator.
71
    /// \param func         Specifies the function (or function object) which
72
    ///                     will be invoked for each of the elements in the
73
    ///                     sequence specified by [first, last). This is a
74
    ///                     binary predicate. The signature of this predicate
75
    ///                     should be equivalent to:
76
    ///                     \code
77
    ///                     Ret fun(const Type1 &a, const Type1 &b);
78
    ///                     \endcode \n
79
    ///                     The signature does not need to have const&.
80
    ///                     The types \a Type1 \a Ret must be
81
    ///                     such that an object of type \a FwdIter can be
82
    ///                     dereferenced and then implicitly converted to any
83
    ///                     of those types.
84
    ///
85
    /// \a comp has to induce a strict weak ordering on the values.
86
    ///
87
    /// The application of function objects in parallel algorithm
88
    /// invoked with an execution policy object of type
89
    /// \a sequenced_policy execute in sequential order in the
90
    /// calling thread.
91
    ///
92
    /// The application of function objects in parallel algorithm
93
    /// invoked with an execution policy object of type
94
    /// \a parallel_policy or \a parallel_task_policy are
95
    /// permitted to execute in an unordered fashion in unspecified
96
    /// threads, and indeterminately sequenced within each thread.
97
    ///
98
    /// \returns  The \a reduce_by_key algorithm returns a
99
    ///           \a hpx::future<pair<Iter1,Iter2>> if the execution policy is of
100
    ///           type
101
    ///           \a sequenced_task_policy or
102
    ///           \a parallel_task_policy and returns \a pair<Iter1,Iter2>
103
    ///           otherwise.
104
    ///
105
    template <typename ExPolicy, typename RanIter, typename RanIter2,
106
        typename FwdIter1, typename FwdIter2,
107
        typename Compare =
108
            std::equal_to<typename std::iterator_traits<RanIter>::value_type>,
109
        typename Func =
110
            std::plus<typename std::iterator_traits<RanIter2>::value_type>>
111
    typename util::detail::algorithm_result<ExPolicy,
112
        util::in_out_result<FwdIter1, FwdIter2>>::type
113
    reduce_by_key(ExPolicy&& policy, RanIter key_first, RanIter key_last,
114
        RanIter2 values_first, FwdIter1 keys_output, FwdIter2 values_output,
115
        Compare&& comp = Compare(), Func&& func = Func());
116
    // clang-format on
117
}}    // namespace hpx::experimental
118

119
#else
120
//
121
#include <hpx/config.hpp>
122
#include <hpx/execution/executors/execution.hpp>
123
//
124
#include <hpx/datastructures/tuple.hpp>
125
#include <hpx/iterator_support/range.hpp>
126
#include <hpx/iterator_support/transform_iterator.hpp>
127
#include <hpx/parallel/algorithms/for_each.hpp>
128
#include <hpx/parallel/algorithms/inclusive_scan.hpp>
129
#include <hpx/parallel/algorithms/sort.hpp>
130
#include <hpx/parallel/container_algorithms/copy.hpp>
131
#include <hpx/parallel/util/result_types.hpp>
132
#include <hpx/parallel/util/zip_iterator.hpp>
133
//
134
#include <cstdint>
135
#include <functional>
136
#include <type_traits>
137
#include <utility>
138
#include <vector>
139
//
140
/// \cond NOINTERNAL
141
#ifdef EXTRA_DEBUG
142
#include <iostream>
143
#define debug_reduce_by_key(a) std::cout << a
144
#else
145
#define debug_reduce_by_key(a)
146
#endif
147
/// \endcond
148

149
namespace hpx { namespace parallel { inline namespace v1 { namespace detail {
150
    /// \cond NOINTERNAL
151

152
    // -------------------------------------------------------------------
153
    // simple iterator helper object for access to prev/next items
154
    // -------------------------------------------------------------------
155
    struct reduce_stencil_transformer
156
    {
157
        // declare result type as a template
158
        template <typename T>
159
        struct result;
160

161
        // specialize result for iterator type
162
        template <typename This, typename Iterator>
163
        struct result<This(Iterator)>
164
        {
165
            typedef
166
                typename std::iterator_traits<Iterator>::reference element_type;
167
            typedef hpx::tuple<element_type, element_type, element_type> type;
168
        };
169

170
        // call operator for stencil transform
171
        // it will dereference tuple(it-1, it, it+1)
172
        template <typename Iterator>
173
        typename result<reduce_stencil_transformer(Iterator)>::type operator()(
3,979,774✔
174
            Iterator const& it) const
175
        {
176
            typedef typename result<reduce_stencil_transformer(Iterator)>::type
177
                type;
178
            return type(*std::prev(it), *it, *std::next(it));
3,992,262✔
179
        }
180
    };
181

182
    // -------------------------------------------------------------------
183
    // transform iterator using reduce_stencil_transformer helper
184
    // -------------------------------------------------------------------
185
    template <typename Iterator,
186
        typename Transformer = detail::reduce_stencil_transformer>
187
    class reduce_stencil_iterator
188
      : public hpx::util::transform_iterator<Iterator, Transformer>
189
    {
190
    private:
191
        typedef hpx::util::transform_iterator<Iterator, Transformer> base_type;
192

193
    public:
194
        reduce_stencil_iterator() {}
195

196
        explicit reduce_stencil_iterator(Iterator const& it)
197
          : base_type(it, Transformer())
198
        {
199
        }
200

201
        reduce_stencil_iterator(Iterator const& it, Transformer const& t)
34✔
202
          : base_type(it, t)
34✔
203
        {
34✔
204
        }
34✔
205
    };
206

207
    template <typename Iterator, typename Transformer>
208
    inline reduce_stencil_iterator<Iterator, Transformer>
209
    make_reduce_stencil_iterator(Iterator const& it, Transformer const& t)
34✔
210
    {
211
        return reduce_stencil_iterator<Iterator, Transformer>(it, t);
34✔
212
    }
213

214
    // -------------------------------------------------------------------
215
    // state of a reduce by key step
216
    // -------------------------------------------------------------------
217
    struct reduce_key_series_states
218
    {
219
        bool start;    // START of a segment
220
        bool end;      // END of a segment
221
        reduce_key_series_states(bool s = false, bool e = false)
9,634,876✔
222
          : start(s)
9,657,735✔
223
          , end(e)
9,657,735✔
224
        {
225
        }
9,657,735✔
226
    };
227

228
    // -------------------------------------------------------------------
229
    // callable that actually computes the state using the stencil iterator
230
    // -------------------------------------------------------------------
231
    template <typename Transformer, typename StencilIterType,
232
        typename KeyStateIterType, typename Compare>
233
    struct reduce_stencil_generate
234
    {
235
        typedef typename Transformer::template result<Transformer(
236
            StencilIterType)>::element_type element_type;
237
        typedef typename Transformer::template result<Transformer(
238
            StencilIterType)>::type tuple_type;
239
        typedef typename std::iterator_traits<KeyStateIterType>::reference
240
            KeyStateType;
241

242
        reduce_stencil_generate() {}
17✔
243

244
        void operator()(const tuple_type& value, KeyStateType& kiter,
3,974,214✔
245
            const Compare& comp) const
246
        {
247
            // resolves to a tuple of values for *(it-1), *it, *(it+1)
248

249
            element_type left = hpx::get<0>(value);
4,003,070✔
250
            element_type mid = hpx::get<1>(value);
4,003,070✔
251
            element_type right = hpx::get<2>(value);
4,003,070✔
252

253
            // we need to determine which of three states this
254
            // index is. It can be:
255
            // 1. Middle of a set of equivalent keys.
256
            // 2. Start of a set of equivalent keys.
257
            // 3. End of a set of equivalent keys.
258
            // 4. Both the start and end of a set of keys
259

260
            {
261
                const bool leftMatches(comp(left, mid));
4,003,070✔
262
                const bool rightMatches(comp(mid, right));
4,003,070✔
263
                kiter = reduce_key_series_states(!leftMatches, !rightMatches);
4,003,070✔
264
            }
265
        }
4,003,070✔
266
    };
267

268
    // -------------------------------------------------------------------
269
    // helper that extracts the final output iterators from copy_if
270
    // -------------------------------------------------------------------
271
    // Zip iterator has 3 iterators inside
272
    // Iter1, key type : Iter2, value type : Iter3, state type
273
    template <typename ZIter, typename iKey, typename iVal>
274
    util::in_out_result<iKey, iVal> make_pair_result(
17✔
275
        ZIter zipiter, iKey key_start, iVal val_start)
276
    {
277
        // the iterator we want is 'second' part of pair type (from copy_if)
278
        auto t = zipiter.out.get_iterator_tuple();
17✔
279
        iKey key_end = hpx::get<0>(t);
17✔
280
        return util::in_out_result<iKey, iVal>{
51✔
281
            key_end, std::next(val_start, std::distance(key_start, key_end))};
34✔
282
    }
283

284
    // async version that returns future<pair> from future<zip_iterator<blah>>
285
    template <typename ZIter, typename iKey, typename iVal>
286
    hpx::future<util::in_out_result<iKey, iVal>> make_pair_result(
287
        hpx::future<ZIter>&& ziter, iKey key_start, iVal val_start)
288
    {
289
        typedef util::in_out_result<iKey, iVal> result_type;
290

291
        return hpx::make_future<result_type>(
292
            HPX_MOVE(ziter), [=](ZIter zipiter) {
293
                auto t = zipiter.second.get_iterator_tuple();
294
                iKey key_end = hpx::get<0>(t);
295
                return result_type{key_end,
296
                    std::next(val_start, std::distance(key_start, key_end))};
297
            });
298
    }
299

300
    // -------------------------------------------------------------------
301
    // The main algorithm is implemented here, it replaces any async
302
    // execution policy with a non async one so that no waits are
303
    // necessry on the internal algorithms. Async execution is handled
304
    // by the wrapper layer that calls this.
305
    // -------------------------------------------------------------------
306
    template <typename ExPolicy, typename RanIter, typename RanIter2,
307
        typename FwdIter1, typename FwdIter2, typename Compare, typename Func>
308
    static util::in_out_result<FwdIter1, FwdIter2> reduce_by_key_impl(
17✔
309
        ExPolicy&& policy, RanIter key_first, RanIter key_last,
310
        RanIter2 values_first, FwdIter1 keys_output, FwdIter2 values_output,
311
        Compare&& comp, Func&& func)
312
    {
313
        using namespace hpx::parallel::v1::detail;
314
        using namespace hpx::util;
315

316
        // we need to determine based on the keys what is the keystate for
317
        // each key. The states are start, middle, end of a series and the special
318
        // state start and end of the sequence
319
        std::vector<reduce_key_series_states> key_state;
17✔
320
        using keystate_iter_type =
321
            std::vector<reduce_key_series_states>::iterator;
322
        using reducebykey_iter = detail::reduce_stencil_iterator<RanIter,
323
            reduce_stencil_transformer>;
324
        using element_type = typename std::iterator_traits<RanIter>::reference;
325
        using zip_ref = typename zip_iterator<reducebykey_iter,
326
            keystate_iter_type>::reference;
327
        //
328
        const std::uint64_t number_of_keys = std::distance(key_first, key_last);
17✔
329
        //
330
        key_state.assign(number_of_keys, reduce_key_series_states());
17✔
331
        {
332
            reduce_stencil_transformer r_s_t;
333
            reducebykey_iter reduce_begin =
334
                make_reduce_stencil_iterator(key_first, r_s_t);
17✔
335
            reducebykey_iter reduce_end =
336
                make_reduce_stencil_iterator(key_last, r_s_t);
17✔
337

338
            // FIXME: handle cases number_of_keys == 0 and
339
            //        number_of_keys == 1
340

341
            if (number_of_keys == 2)
17✔
342
            {
343
                // for two entries, one is a start, the other an end,
344
                // if they are different, then they are both start/end
345
                element_type left = *key_first;
×
346
                element_type right = *std::next(key_first);
×
347
                key_state[0] =
×
348
                    reduce_key_series_states(true, !comp(left, right));
×
349
                key_state[1] =
×
350
                    reduce_key_series_states(!comp(left, right), true);
×
351
            }
×
352
            else
353
            {
354
                // do the first and last elements by hand to simplify the iterator
355
                // traversal as there is no prev/next for first/last
356
                element_type elem0 = *key_first;
17✔
357
                element_type elem1 = *std::next(key_first);
17✔
358
                key_state[0] = reduce_key_series_states(true, elem0 != elem1);
17✔
359
                // middle elements
360
                reduce_stencil_generate<reduce_stencil_transformer, RanIter,
361
                    keystate_iter_type, Compare>
362
                    kernel;
17✔
363
                hpx::for_each(policy(hpx::execution::non_task),
34✔
364
                    zip_iterator(reduce_begin + 1, key_state.begin() + 1),
17✔
365
                    zip_iterator(reduce_end - 1, key_state.end() - 1),
17✔
366
                    [&kernel, &comp](zip_ref ref) {
3,999,626✔
367
                        kernel(hpx::get<0>(ref), hpx::get<1>(ref), comp);
3,999,809✔
368
                    });
3,999,809✔
369
                // Last element
370
                element_type elemN = *std::prev(key_last);
17✔
371
                element_type elemn = *std::prev(std::prev(key_last));
17✔
372
                key_state.back() =
17✔
373
                    reduce_key_series_states(elemN != elemn, true);
17✔
374
            }
375
        }
376
        {
377
            using zip_iterator_in = zip_iterator<RanIter2,
378
                std::vector<reduce_key_series_states>::iterator>;
379
            using zip_type_in = typename zip_iterator_in::value_type;
380

381
            using zip_iterator_vout = zip_iterator<FwdIter2,
382
                std::vector<reduce_key_series_states>::iterator>;
383

384
            using value_type =
385
                typename std::iterator_traits<RanIter2>::value_type;
386

387
            zip_iterator_in states_begin =
388
                zip_iterator(values_first, hpx::util::begin(key_state));
17✔
389
            zip_iterator_in states_end = zip_iterator(
17✔
390
                values_first + number_of_keys, hpx::util::end(key_state));
17✔
391
            zip_iterator_vout states_out_begin =
392
                zip_iterator(values_output, hpx::util::begin(key_state));
17✔
393
            //
394

395
            zip_type_in initial;
17✔
396
            //
397
            using lambda_type =
398
                hpx::tuple<value_type, reduce_key_series_states>;
399
            hpx::inclusive_scan(
17✔
400
                policy(hpx::execution::non_task), states_begin, states_end,
17✔
401
                states_out_begin,
402
                // B is the current entry, A is the one passed in from 'previous'
403
                [&func](zip_type_in a, zip_type_in b) -> lambda_type {
6,568,535✔
404
                    value_type a_val = hpx::get<0>(a);
5,568,418✔
405
                    reduce_key_series_states a_state = hpx::get<1>(a);
5,568,418✔
406
                    value_type b_val = hpx::get<0>(b);
5,568,418✔
407
                    reduce_key_series_states b_state = hpx::get<1>(b);
5,568,418✔
408
                    debug_reduce_by_key("{ " << a_val << "+" << b_val << " },\t"
409
                                             << a_state << b_state);
410
                    // if carrying a start flag, then copy - don't add
411
                    if (b_state.start)
5,568,418✔
412
                    {
413
                        debug_reduce_by_key(" = " << b_val << std::endl);
414
                        return hpx::make_tuple(b_val,
2,250,484✔
415
                            reduce_key_series_states(
2,250,484✔
416
                                a_state.start || b_state.start, b_state.end));
1,709,622✔
417
                    }
418
                    // normal add of previous + this
419
                    else
420
                    {
421
                        debug_reduce_by_key(
422
                            " = " << func(a_val, b_val) << std::endl);
423
                        value_type temp = func(a_val, b_val);
3,828,759✔
424
                        return hpx::make_tuple(temp,
4,336,254✔
425
                            reduce_key_series_states(
4,336,254✔
426
                                a_state.start || b_state.start, b_state.end));
3,858,796✔
427
                    }
428
                },
6,579,155✔
429
                initial);
430

431
            // now copy the values and keys for each element that
432
            // is marked by an 'END' state to the final output
433
            using zip2_ref = typename hpx::util::zip_iterator<RanIter, FwdIter2,
434
                std::vector<reduce_key_series_states>::iterator>::reference;
435

436
            std::vector<value_type> temp(number_of_keys);
17✔
437
            hpx::copy(policy(hpx::execution::non_task), values_output,
17✔
438
                values_output + number_of_keys, temp.begin());
17✔
439

440
            return make_pair_result(
17✔
441
                hpx::ranges::copy_if(policy(hpx::execution::non_task),
17✔
442
                    zip_iterator(
17✔
443
                        key_first, temp.begin(), hpx::util::begin(key_state)),
17✔
444
                    zip_iterator(key_last, temp.begin() + number_of_keys,
34✔
445
                        hpx::util::end(key_state)),
17✔
446
                    zip_iterator(keys_output, values_output,
17✔
447
                        hpx::util::begin(key_state)),
17✔
448
                    // copies to dest only when 'end' state is true
449
                    [](zip2_ref it) { return hpx::get<2>(it).end; }),
3,851,617✔
450
                keys_output, values_output);
17✔
451
        }
17✔
452
    }
17✔
453

454
    ///////////////////////////////////////////////////////////////////////
455
    // reduce_by_key wrapper struct
456
    template <typename FwdIter1, typename FwdIter2>
457
    struct reduce_by_key
458
      : public detail::algorithm<reduce_by_key<FwdIter1, FwdIter2>,
459
            util::in_out_result<FwdIter1, FwdIter2>>
460
    {
461
        reduce_by_key()
17✔
462
          : reduce_by_key::algorithm("reduce_by_key")
17✔
463
        {
17✔
464
        }
17✔
465

466
        template <typename ExPolicy, typename RanIter, typename RanIter2,
467
            typename Compare, typename Func>
468
        static util::in_out_result<FwdIter1, FwdIter2> sequential(
8✔
469
            ExPolicy&& policy, RanIter key_first, RanIter key_last,
470
            RanIter2 values_first, FwdIter1 keys_output, FwdIter2 values_output,
471
            Compare&& comp, Func&& func)
472
        {
473
            return reduce_by_key_impl(HPX_FORWARD(ExPolicy, policy), key_first,
16✔
474
                key_last, values_first, keys_output, values_output,
8✔
475
                HPX_FORWARD(Compare, comp), HPX_FORWARD(Func, func));
8✔
476
        }
477

478
        template <typename ExPolicy, typename RanIter, typename RanIter2,
479
            typename Compare, typename Func>
480
        static typename util::detail::algorithm_result<ExPolicy,
481
            util::in_out_result<FwdIter1, FwdIter2>>::type
482
        parallel(ExPolicy&& policy, RanIter key_first, RanIter key_last,
9✔
483
            RanIter2 values_first, FwdIter1 keys_output, FwdIter2 values_output,
484
            Compare&& comp, Func&& func)
485
        {
486
            return util::detail::algorithm_result<ExPolicy,
9✔
487
                util::in_out_result<FwdIter1,
488
                    FwdIter2>>::get(execution::async_execute(policy.executor(),
18✔
489
                hpx::util::deferred_call(
9✔
490
                    &hpx::parallel::v1::detail::reduce_by_key_impl<ExPolicy&&,
9✔
491
                        RanIter, RanIter2, FwdIter1, FwdIter2, Compare&&,
492
                        Func&&>,
493
                    policy, key_first, key_last, values_first, keys_output,
9✔
494
                    values_output, HPX_FORWARD(Compare, comp),
9✔
495
                    HPX_FORWARD(Func, func))));
9✔
496
        }
×
497
    };
498
    /// \endcond
499
}}}}    // namespace hpx::parallel::v1::detail
500

501
namespace hpx { namespace experimental {
502

503
#ifdef EXTRA_DEBUG
504
    std::ostream& operator<<(
505
        std::ostream& os, const detail::reduce_key_series_states& rs)
506
    {
507
        os << "{ start=" << rs.start << ",end=" << rs.end << "} ";
508
        return os;
509
    }
510
#endif
511

512
    template <typename ExPolicy, typename RanIter, typename RanIter2,
513
        typename FwdIter1, typename FwdIter2,
514
        typename Compare =
515
            std::equal_to<typename std::iterator_traits<RanIter>::value_type>,
516
        typename Func =
517
            std::plus<typename std::iterator_traits<RanIter2>::value_type>,
518
        HPX_CONCEPT_REQUIRES_(hpx::is_execution_policy<ExPolicy>::value&&
519
                hpx::traits::is_iterator<RanIter>::value&&
520
                    hpx::traits::is_iterator<RanIter2>::value&&
521
                        hpx::traits::is_iterator<FwdIter1>::value&&
522
                            hpx::traits::is_iterator<FwdIter2>::value)>
523
    typename hpx::parallel::util::detail::algorithm_result<ExPolicy,
524
        hpx::parallel::util::in_out_result<FwdIter1, FwdIter2>>::type
525
    reduce_by_key(ExPolicy&& policy, RanIter key_first, RanIter key_last,
17✔
526
        RanIter2 values_first, FwdIter1 keys_output, FwdIter2 values_output,
527
        Compare&& comp = Compare(), Func&& func = Func())
528
    {
529
        typedef hpx::parallel::util::detail::algorithm_result<ExPolicy,
530
            hpx::parallel::util::in_out_result<FwdIter1, FwdIter2>>
531
            result;
532

533
        static_assert(
534
            (hpx::traits::is_random_access_iterator<RanIter>::value) &&
535
                (hpx::traits::is_random_access_iterator<RanIter2>::value) &&
536
                (hpx::traits::is_forward_iterator<FwdIter1>::value) &&
537
                (hpx::traits::is_forward_iterator<FwdIter2>::value),
538
            "iterators : Random_access for inputs and forward for outputs.");
539

540
        const std::uint64_t number_of_keys = std::distance(key_first, key_last);
17✔
541

542
        if (number_of_keys <= 1)
17✔
543
        {    // we only have a single key/value so that is our output
544
            *keys_output = *key_first;
×
545
            *values_output = *values_first;
×
546
            return result::get(
×
547
                hpx::parallel::util::in_out_result<FwdIter1, FwdIter2>{
×
548
                    keys_output, values_output});
×
549
        }
550

551
        return hpx::parallel::v1::detail::reduce_by_key<FwdIter1, FwdIter2>()
34✔
552
            .call(HPX_FORWARD(ExPolicy, policy), key_first, key_last,
34✔
553
                values_first, keys_output, values_output,
554
                HPX_FORWARD(Compare, comp), HPX_FORWARD(Func, func));
17✔
555
    }
17✔
556
}}    // namespace hpx::experimental
557

558
namespace hpx { namespace parallel { inline namespace v1 {
559

560
    template <typename ExPolicy, typename RanIter, typename RanIter2,
561
        typename FwdIter1, typename FwdIter2,
562
        typename Compare =
563
            std::equal_to<typename std::iterator_traits<RanIter>::value_type>,
564
        typename Func =
565
            std::plus<typename std::iterator_traits<RanIter2>::value_type>>
566
    HPX_DEPRECATED_V(1, 9,
567
        "hpx::parallel::reduce_by_key is deprecated. Please use "
568
        "hpx::experimental::reduce_by_key instead.")
569
    constexpr decltype(auto)
570
        reduce_by_key(ExPolicy&& policy, RanIter key_first, RanIter key_last,
571
            RanIter2 values_first, FwdIter1 keys_output, FwdIter2 values_output,
572
            Compare&& comp = Compare(), Func&& func = Func())
573
    {
574
        return hpx::experimental::reduce_by_key(policy, key_first, key_last,
575
            values_first, keys_output, values_output, comp, func);
576
    }
577

578
}}}    // namespace hpx::parallel::v1
579

580
#endif
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