multiseq_selection.h
Go to the documentation of this file.
// -*- C++ -*-
// Copyright (C) 2007-2025 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the terms
// of the GNU General Public License as published by the Free Software
// Foundation; either version 3, or (at your option) any later
// version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file parallel/multiseq_selection.h
* @brief Functions to find elements of a certain global __rank in
* multiple sorted sequences. Also serves for splitting such
* sequence sets.
*
* The algorithm description can be found in
*
* P. J. Varman, S. D. Scheufler, B. R. Iyer, and G. R. Ricard.
* Merging Multiple Lists on Hierarchical-Memory Multiprocessors.
* Journal of Parallel and Distributed Computing, 12(2):171-177, 1991.
*
* This file is a GNU parallel extension to the Standard C++ Library.
*/
// Written by Johannes Singler.
#ifndef _GLIBCXX_PARALLEL_MULTISEQ_SELECTION_H
#define _GLIBCXX_PARALLEL_MULTISEQ_SELECTION_H 1
#include <vector>
#include <queue>
#include <bits/stl_algo.h>
namespace __gnu_parallel
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // *nary_function
/** @brief Compare __a pair of types lexicographically, ascending. */
template<typename _T1, typename _T2, typename _Compare>
class _Lexicographic
: public std::binary_function<std::pair<_T1, _T2>,
std::pair<_T1, _T2>, bool>
{
private:
_Compare& _M_comp;
public:
_Lexicographic(_Compare& __comp) : _M_comp(__comp) { }
bool
operator()(const std::pair<_T1, _T2>& __p1,
const std::pair<_T1, _T2>& __p2) const
{
if (_M_comp(__p1.first, __p2.first))
return true;
if (_M_comp(__p2.first, __p1.first))
return false;
// Firsts are equal.
return __p1.second < __p2.second;
}
};
/** @brief Compare __a pair of types lexicographically, descending. */
template<typename _T1, typename _T2, typename _Compare>
class _LexicographicReverse : public std::binary_function<_T1, _T2, bool>
{
private:
_Compare& _M_comp;
public:
_LexicographicReverse(_Compare& __comp) : _M_comp(__comp) { }
bool
operator()(const std::pair<_T1, _T2>& __p1,
const std::pair<_T1, _T2>& __p2) const
{
if (_M_comp(__p2.first, __p1.first))
return true;
if (_M_comp(__p1.first, __p2.first))
return false;
// Firsts are equal.
return __p2.second < __p1.second;
}
};
#pragma GCC diagnostic pop // -Wdeprecated-declarations
/**
* @brief Splits several sorted sequences at a certain global __rank,
* resulting in a splitting point for each sequence.
* The sequences are passed via a sequence of random-access
* iterator pairs, none of the sequences may be empty. If there
* are several equal elements across the split, the ones on the
* __left side will be chosen from sequences with smaller number.
* @param __begin_seqs Begin of the sequence of iterator pairs.
* @param __end_seqs End of the sequence of iterator pairs.
* @param __rank The global rank to partition at.
* @param __begin_offsets A random-access __sequence __begin where the
* __result will be stored in. Each element of the sequence is an
* iterator that points to the first element on the greater part of
* the respective __sequence.
* @param __comp The ordering functor, defaults to std::less<_Tp>.
*/
template<typename _RanSeqs, typename _RankType, typename _RankIterator,
typename _Compare>
void
multiseq_partition(_RanSeqs __begin_seqs, _RanSeqs __end_seqs,
_RankType __rank,
_RankIterator __begin_offsets,
_Compare __comp = std::less<
typename std::iterator_traits<typename
std::iterator_traits<_RanSeqs>::value_type::
first_type>::value_type>()) // std::less<_Tp>
{
_GLIBCXX_CALL(__end_seqs - __begin_seqs)
typedef typename std::iterator_traits<_RanSeqs>::value_type::first_type
_It;
typedef typename std::iterator_traits<_RanSeqs>::difference_type
_SeqNumber;
typedef typename std::iterator_traits<_It>::difference_type
_DifferenceType;
typedef typename std::iterator_traits<_It>::value_type _ValueType;
_Lexicographic<_ValueType, _SeqNumber, _Compare> __lcomp(__comp);
_LexicographicReverse<_ValueType, _SeqNumber, _Compare> __lrcomp(__comp);
// Number of sequences, number of elements in total (possibly
// including padding).
_DifferenceType __m = std::distance(__begin_seqs, __end_seqs), __nn = 0,
__nmax, __n, __r;
for (_SeqNumber __i = 0; __i < __m; __i++)
{
__nn += std::distance(__begin_seqs[__i].first,
__begin_seqs[__i].second);
_GLIBCXX_PARALLEL_ASSERT(
std::distance(__begin_seqs[__i].first,
__begin_seqs[__i].second) > 0);
}
if (__rank == __nn)
{
for (_SeqNumber __i = 0; __i < __m; __i++)
__begin_offsets[__i] = __begin_seqs[__i].second; // Very end.
// Return __m - 1;
return;
}
_GLIBCXX_PARALLEL_ASSERT(__m != 0);
_GLIBCXX_PARALLEL_ASSERT(__nn != 0);
_GLIBCXX_PARALLEL_ASSERT(__rank >= 0);
_GLIBCXX_PARALLEL_ASSERT(__rank < __nn);
_DifferenceType* __ns = new _DifferenceType[__m];
_DifferenceType* __a = new _DifferenceType[__m];
_DifferenceType* __b = new _DifferenceType[__m];
_DifferenceType __l;
__ns[0] = std::distance(__begin_seqs[0].first, __begin_seqs[0].second);
__nmax = __ns[0];
for (_SeqNumber __i = 0; __i < __m; __i++)
{
__ns[__i] = std::distance(__begin_seqs[__i].first,
__begin_seqs[__i].second);
__nmax = std::max(__nmax, __ns[__i]);
}
__r = __rd_log2(__nmax) + 1;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlong-long" // LL literal
// Pad all lists to this length, at least as long as any ns[__i],
// equality iff __nmax = 2^__k - 1.
__l = (1ULL << __r) - 1;
#pragma GCC diagnostic pop
for (_SeqNumber __i = 0; __i < __m; __i++)
{
__a[__i] = 0;
__b[__i] = __l;
}
__n = __l / 2;
// Invariants:
// 0 <= __a[__i] <= __ns[__i], 0 <= __b[__i] <= __l
#define __S(__i) (__begin_seqs[__i].first)
// Initial partition.
std::vector<std::pair<_ValueType, _SeqNumber> > __sample;
for (_SeqNumber __i = 0; __i < __m; __i++)
if (__n < __ns[__i]) //__sequence long enough
__sample.push_back(std::make_pair(__S(__i)[__n], __i));
__gnu_sequential::sort(__sample.begin(), __sample.end(), __lcomp);
for (_SeqNumber __i = 0; __i < __m; __i++) //conceptual infinity
if (__n >= __ns[__i]) //__sequence too short, conceptual infinity
__sample.push_back(
std::make_pair(__S(__i)[0] /*__dummy element*/, __i));
_DifferenceType __localrank = __rank / __l;
_SeqNumber __j;
for (__j = 0;
__j < __localrank && ((__n + 1) <= __ns[__sample[__j].second]);
++__j)
__a[__sample[__j].second] += __n + 1;
for (; __j < __m; __j++)
__b[__sample[__j].second] -= __n + 1;
// Further refinement.
while (__n > 0)
{
__n /= 2;
_SeqNumber __lmax_seq = -1; // to avoid warning
const _ValueType* __lmax = 0; // impossible to avoid the warning?
for (_SeqNumber __i = 0; __i < __m; __i++)
{
if (__a[__i] > 0)
{
if (!__lmax)
{
__lmax = &(__S(__i)[__a[__i] - 1]);
__lmax_seq = __i;
}
else
{
// Max, favor rear sequences.
if (!__comp(__S(__i)[__a[__i] - 1], *__lmax))
{
__lmax = &(__S(__i)[__a[__i] - 1]);
__lmax_seq = __i;
}
}
}
}
_SeqNumber __i;
for (__i = 0; __i < __m; __i++)
{
_DifferenceType __middle = (__b[__i] + __a[__i]) / 2;
if (__lmax && __middle < __ns[__i] &&
__lcomp(std::make_pair(__S(__i)[__middle], __i),
std::make_pair(*__lmax, __lmax_seq)))
__a[__i] = std::min(__a[__i] + __n + 1, __ns[__i]);
else
__b[__i] -= __n + 1;
}
_DifferenceType __leftsize = 0;
for (_SeqNumber __i = 0; __i < __m; __i++)
__leftsize += __a[__i] / (__n + 1);
_DifferenceType __skew = __rank / (__n + 1) - __leftsize;
if (__skew > 0)
{
// Move to the left, find smallest.
std::priority_queue<std::pair<_ValueType, _SeqNumber>,
std::vector<std::pair<_ValueType, _SeqNumber> >,
_LexicographicReverse<_ValueType, _SeqNumber, _Compare> >
__pq(__lrcomp);
for (_SeqNumber __i = 0; __i < __m; __i++)
if (__b[__i] < __ns[__i])
__pq.push(std::make_pair(__S(__i)[__b[__i]], __i));
for (; __skew != 0 && !__pq.empty(); --__skew)
{
_SeqNumber __source = __pq.top().second;
__pq.pop();
__a[__source]
= std::min(__a[__source] + __n + 1, __ns[__source]);
__b[__source] += __n + 1;
if (__b[__source] < __ns[__source])
__pq.push(
std::make_pair(__S(__source)[__b[__source]], __source));
}
}
else if (__skew < 0)
{
// Move to the right, find greatest.
std::priority_queue<std::pair<_ValueType, _SeqNumber>,
std::vector<std::pair<_ValueType, _SeqNumber> >,
_Lexicographic<_ValueType, _SeqNumber, _Compare> >
__pq(__lcomp);
for (_SeqNumber __i = 0; __i < __m; __i++)
if (__a[__i] > 0)
__pq.push(std::make_pair(__S(__i)[__a[__i] - 1], __i));
for (; __skew != 0; ++__skew)
{
_SeqNumber __source = __pq.top().second;
__pq.pop();
__a[__source] -= __n + 1;
__b[__source] -= __n + 1;
if (__a[__source] > 0)
__pq.push(std::make_pair(
__S(__source)[__a[__source] - 1], __source));
}
}
}
// Postconditions:
// __a[__i] == __b[__i] in most cases, except when __a[__i] has been
// clamped because of having reached the boundary
// Now return the result, calculate the offset.
// Compare the keys on both edges of the border.
// Maximum of left edge, minimum of right edge.
_ValueType* __maxleft = 0;
_ValueType* __minright = 0;
for (_SeqNumber __i = 0; __i < __m; __i++)
{
if (__a[__i] > 0)
{
if (!__maxleft)
__maxleft = &(__S(__i)[__a[__i] - 1]);
else
{
// Max, favor rear sequences.
if (!__comp(__S(__i)[__a[__i] - 1], *__maxleft))
__maxleft = &(__S(__i)[__a[__i] - 1]);
}
}
if (__b[__i] < __ns[__i])
{
if (!__minright)
__minright = &(__S(__i)[__b[__i]]);
else
{
// Min, favor fore sequences.
if (__comp(__S(__i)[__b[__i]], *__minright))
__minright = &(__S(__i)[__b[__i]]);
}
}
}
_SeqNumber __seq = 0;
for (_SeqNumber __i = 0; __i < __m; __i++)
__begin_offsets[__i] = __S(__i) + __a[__i];
delete[] __ns;
delete[] __a;
delete[] __b;
}
/**
* @brief Selects the element at a certain global __rank from several
* sorted sequences.
*
* The sequences are passed via a sequence of random-access
* iterator pairs, none of the sequences may be empty.
* @param __begin_seqs Begin of the sequence of iterator pairs.
* @param __end_seqs End of the sequence of iterator pairs.
* @param __rank The global rank to partition at.
* @param __offset The rank of the selected element in the global
* subsequence of elements equal to the selected element. If the
* selected element is unique, this number is 0.
* @param __comp The ordering functor, defaults to std::less.
*/
template<typename _Tp, typename _RanSeqs, typename _RankType,
typename _Compare>
_Tp
multiseq_selection(_RanSeqs __begin_seqs, _RanSeqs __end_seqs,
_RankType __rank,
_RankType& __offset, _Compare __comp = std::less<_Tp>())
{
_GLIBCXX_CALL(__end_seqs - __begin_seqs)
typedef typename std::iterator_traits<_RanSeqs>::value_type::first_type
_It;
typedef typename std::iterator_traits<_RanSeqs>::difference_type
_SeqNumber;
typedef typename std::iterator_traits<_It>::difference_type
_DifferenceType;
_Lexicographic<_Tp, _SeqNumber, _Compare> __lcomp(__comp);
_LexicographicReverse<_Tp, _SeqNumber, _Compare> __lrcomp(__comp);
// Number of sequences, number of elements in total (possibly
// including padding).
_DifferenceType __m = std::distance(__begin_seqs, __end_seqs);
_DifferenceType __nn = 0;
_DifferenceType __nmax, __n, __r;
for (_SeqNumber __i = 0; __i < __m; __i++)
__nn += std::distance(__begin_seqs[__i].first,
__begin_seqs[__i].second);
if (__m == 0 || __nn == 0 || __rank < 0 || __rank >= __nn)
{
// result undefined if there is no data or __rank is outside bounds
throw std::exception();
}
_DifferenceType* __ns = new _DifferenceType[__m];
_DifferenceType* __a = new _DifferenceType[__m];
_DifferenceType* __b = new _DifferenceType[__m];
_DifferenceType __l;
__ns[0] = std::distance(__begin_seqs[0].first, __begin_seqs[0].second);
__nmax = __ns[0];
for (_SeqNumber __i = 0; __i < __m; ++__i)
{
__ns[__i] = std::distance(__begin_seqs[__i].first,
__begin_seqs[__i].second);
__nmax = std::max(__nmax, __ns[__i]);
}
__r = __rd_log2(__nmax) + 1;
// Pad all lists to this length, at least as long as any ns[__i],
// equality iff __nmax = 2^__k - 1
__l = __round_up_to_pow2(__r) - 1;
for (_SeqNumber __i = 0; __i < __m; ++__i)
{
__a[__i] = 0;
__b[__i] = __l;
}
__n = __l / 2;
// Invariants:
// 0 <= __a[__i] <= __ns[__i], 0 <= __b[__i] <= __l
#define __S(__i) (__begin_seqs[__i].first)
// Initial partition.
std::vector<std::pair<_Tp, _SeqNumber> > __sample;
for (_SeqNumber __i = 0; __i < __m; __i++)
if (__n < __ns[__i])
__sample.push_back(std::make_pair(__S(__i)[__n], __i));
__gnu_sequential::sort(__sample.begin(), __sample.end(),
__lcomp, sequential_tag());
// Conceptual infinity.
for (_SeqNumber __i = 0; __i < __m; __i++)
if (__n >= __ns[__i])
__sample.push_back(
std::make_pair(__S(__i)[0] /*__dummy element*/, __i));
_DifferenceType __localrank = __rank / __l;
_SeqNumber __j;
for (__j = 0;
__j < __localrank && ((__n + 1) <= __ns[__sample[__j].second]);
++__j)
__a[__sample[__j].second] += __n + 1;
for (; __j < __m; ++__j)
__b[__sample[__j].second] -= __n + 1;
// Further refinement.
while (__n > 0)
{
__n /= 2;
const _Tp* __lmax = 0;
for (_SeqNumber __i = 0; __i < __m; ++__i)
{
if (__a[__i] > 0)
{
if (!__lmax)
__lmax = &(__S(__i)[__a[__i] - 1]);
else
{
if (__comp(*__lmax, __S(__i)[__a[__i] - 1])) //max
__lmax = &(__S(__i)[__a[__i] - 1]);
}
}
}
_SeqNumber __i;
for (__i = 0; __i < __m; __i++)
{
_DifferenceType __middle = (__b[__i] + __a[__i]) / 2;
if (__lmax && __middle < __ns[__i]
&& __comp(__S(__i)[__middle], *__lmax))
__a[__i] = std::min(__a[__i] + __n + 1, __ns[__i]);
else
__b[__i] -= __n + 1;
}
_DifferenceType __leftsize = 0;
for (_SeqNumber __i = 0; __i < __m; ++__i)
__leftsize += __a[__i] / (__n + 1);
_DifferenceType __skew = __rank / (__n + 1) - __leftsize;
if (__skew > 0)
{
// Move to the left, find smallest.
std::priority_queue<std::pair<_Tp, _SeqNumber>,
std::vector<std::pair<_Tp, _SeqNumber> >,
_LexicographicReverse<_Tp, _SeqNumber, _Compare> >
__pq(__lrcomp);
for (_SeqNumber __i = 0; __i < __m; ++__i)
if (__b[__i] < __ns[__i])
__pq.push(std::make_pair(__S(__i)[__b[__i]], __i));
for (; __skew != 0 && !__pq.empty(); --__skew)
{
_SeqNumber __source = __pq.top().second;
__pq.pop();
__a[__source]
= std::min(__a[__source] + __n + 1, __ns[__source]);
__b[__source] += __n + 1;
if (__b[__source] < __ns[__source])
__pq.push(
std::make_pair(__S(__source)[__b[__source]], __source));
}
}
else if (__skew < 0)
{
// Move to the right, find greatest.
std::priority_queue<std::pair<_Tp, _SeqNumber>,
std::vector<std::pair<_Tp, _SeqNumber> >,
_Lexicographic<_Tp, _SeqNumber, _Compare> > __pq(__lcomp);
for (_SeqNumber __i = 0; __i < __m; ++__i)
if (__a[__i] > 0)
__pq.push(std::make_pair(__S(__i)[__a[__i] - 1], __i));
for (; __skew != 0; ++__skew)
{
_SeqNumber __source = __pq.top().second;
__pq.pop();
__a[__source] -= __n + 1;
__b[__source] -= __n + 1;
if (__a[__source] > 0)
__pq.push(std::make_pair(
__S(__source)[__a[__source] - 1], __source));
}
}
}
// Postconditions:
// __a[__i] == __b[__i] in most cases, except when __a[__i] has been
// clamped because of having reached the boundary
// Now return the result, calculate the offset.
// Compare the keys on both edges of the border.
// Maximum of left edge, minimum of right edge.
bool __maxleftset = false, __minrightset = false;
// Impossible to avoid the warning?
_Tp __maxleft, __minright;
for (_SeqNumber __i = 0; __i < __m; ++__i)
{
if (__a[__i] > 0)
{
if (!__maxleftset)
{
__maxleft = __S(__i)[__a[__i] - 1];
__maxleftset = true;
}
else
{
// Max.
if (__comp(__maxleft, __S(__i)[__a[__i] - 1]))
__maxleft = __S(__i)[__a[__i] - 1];
}
}
if (__b[__i] < __ns[__i])
{
if (!__minrightset)
{
__minright = __S(__i)[__b[__i]];
__minrightset = true;
}
else
{
// Min.
if (__comp(__S(__i)[__b[__i]], __minright))
__minright = __S(__i)[__b[__i]];
}
}
}
// Minright is the __splitter, in any case.
if (!__maxleftset || __comp(__minright, __maxleft))
{
// Good luck, everything is split unambiguously.
__offset = 0;
}
else
{
// We have to calculate an offset.
__offset = 0;
for (_SeqNumber __i = 0; __i < __m; ++__i)
{
_DifferenceType lb
= std::lower_bound(__S(__i), __S(__i) + __ns[__i],
__minright,
__comp) - __S(__i);
__offset += __a[__i] - lb;
}
}
delete[] __ns;
delete[] __a;
delete[] __b;
return __minright;
}
}
#undef __S
#endif /* _GLIBCXX_PARALLEL_MULTISEQ_SELECTION_H */
queue
vector
stl_algo.h
_GLIBCXX_CALL
#define _GLIBCXX_CALL(__n)
Macro to produce log message when entering a function.
Definition compiletime_settings.h:44
std::make_pair
constexpr pair< typename __decay_and_strip< _T1 >::__type, typename __decay_and_strip< _T2 >::__type > make_pair(_T1 &&__x, _T2 &&__y)
A convenience wrapper for creating a pair from two objects.
Definition stl_pair.h:1164
std::max
constexpr const _Tp & max(const _Tp &, const _Tp &)
This does what you think it does.
Definition stl_algobase.h:258
std::min
constexpr const _Tp & min(const _Tp &, const _Tp &)
This does what you think it does.
Definition stl_algobase.h:234
std::distance
constexpr iterator_traits< _InputIterator >::difference_type distance(_InputIterator __first, _InputIterator __last)
A generalization of pointer arithmetic.
Definition stl_iterator_base_funcs.h:150
__gnu_parallel
GNU parallel code for public use.
Definition balanced_quicksort.h:62
__gnu_parallel::multiseq_selection
_Tp multiseq_selection(_RanSeqs __begin_seqs, _RanSeqs __end_seqs, _RankType __rank, _RankType &__offset, _Compare __comp=std::less< _Tp >())
Selects the element at a certain global __rank from several sorted sequences.
Definition multiseq_selection.h:397
__gnu_parallel::__round_up_to_pow2
_Tp __round_up_to_pow2(_Tp __x)
Round up to the next greater power of 2.
Definition random_shuffle.h:248
__gnu_parallel::multiseq_partition
void multiseq_partition(_RanSeqs __begin_seqs, _RanSeqs __end_seqs, _RankType __rank, _RankIterator __begin_offsets, _Compare __comp=std::less< typename std::iterator_traits< typename std::iterator_traits< _RanSeqs >::value_type::first_type >::value_type >())
Splits several sorted sequences at a certain global __rank, resulting in a splitting point for each s...
Definition multiseq_selection.h:128
__gnu_parallel::__rd_log2
_Size __rd_log2(_Size __n)
Calculates the rounded-down logarithm of __n for base 2.
Definition base.h:102
std::exception
Base class for all library exceptions.
Definition exception.h:62
std::binary_function
Definition stl_function.h:135
std::less
One of the comparison functors.
Definition stl_function.h:404
std::pair
Struct holding two objects of arbitrary type.
Definition stl_pair.h:304
std::pair::first
_T1 first
The first member.
Definition stl_pair.h:308
std::pair::second
_T2 second
The second member.
Definition stl_pair.h:309
std::iterator_traits
Traits class for iterators.
Definition stl_iterator_base_types.h:180
std::priority_queue
A standard container automatically sorting its contents.
Definition stl_queue.h:553
std::priority_queue::empty
bool empty() const
Definition stl_queue.h:808
std::priority_queue::pop
void pop()
Removes first element.
Definition stl_queue.h:886
std::priority_queue::top
const_reference top() const
Definition stl_queue.h:823
std::priority_queue::push
void push(const value_type &__x)
Add data to the queue.
Definition stl_queue.h:838
std::vector
A standard container which offers fixed time access to individual elements in any order.
Definition stl_vector.h:459
__gnu_parallel::_Lexicographic
Compare __a pair of types lexicographically, ascending.
Definition multiseq_selection.h:60
__gnu_parallel::_LexicographicReverse
Compare __a pair of types lexicographically, descending.
Definition multiseq_selection.h:85
__gnu_parallel::sequential_tag
Forces sequential execution at compile time.
Definition tags.h:42