// -*- C++ -*-

// Copyright (C) 2007, 2008, 2009 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/multiway_merge.h
*  @brief Implementation of sequential and parallel multiway merge.
*
*  Explanations on the high-speed merging routines in the appendix of
*
*  P. Sanders.
*  Fast priority queues for cached memory.
*  ACM Journal of Experimental Algorithmics, 5, 2000.
*
*  This file is a GNU parallel extension to the Standard C++ Library.
*/

// Written by Johannes Singler and Manuel Holtgrewe.

#ifndef _GLIBCXX_PARALLEL_MULTIWAY_MERGE_H
#define _GLIBCXX_PARALLEL_MULTIWAY_MERGE_H

#include <vector>

#include <bits/stl_algo.h>
#include <parallel/features.h>
#include <parallel/parallel.h>
#include <parallel/losertree.h>
#if _GLIBCXX_ASSERTIONS
#include <parallel/checkers.h>
#endif

/** @brief Length of a sequence described by a pair of iterators. */
#define _GLIBCXX_PARALLEL_LENGTH(s) ((s).second - (s).first)

namespace __gnu_parallel
{

// Announce guarded and unguarded iterator.

template<typename RandomAccessIterator, typename Comparator>
  class guarded_iterator;

// Making the arguments const references seems to dangerous,
// the user-defined comparator might not be const.
template<typename RandomAccessIterator, typename Comparator>
  inline bool
  operator<(guarded_iterator<RandomAccessIterator, Comparator>& bi1,
             guarded_iterator<RandomAccessIterator, Comparator>& bi2);

template<typename RandomAccessIterator, typename Comparator>
  inline bool
  operator<=(guarded_iterator<RandomAccessIterator, Comparator>& bi1,
              guarded_iterator<RandomAccessIterator, Comparator>& bi2);

/** @brief Iterator wrapper supporting an implicit supremum at the end
 *         of the sequence, dominating all comparisons.
 *
 * The implicit supremum comes with a performance cost.
 *
 * Deriving from RandomAccessIterator is not possible since
 * RandomAccessIterator need not be a class.
 */
template<typename RandomAccessIterator, typename Comparator>
  class guarded_iterator
  {
  private:
    /** @brief Current iterator position. */
    RandomAccessIterator current;

    /** @brief End iterator of the sequence. */
    RandomAccessIterator end;

    /** @brief Comparator. */
    Comparator& comp;

  public:
    /** @brief Constructor. Sets iterator to beginning of sequence.
    *  @param begin Begin iterator of sequence.
    *  @param end End iterator of sequence.
    *  @param comp Comparator provided for associated overloaded
    *  compare operators. */
    guarded_iterator(RandomAccessIterator begin,
                     RandomAccessIterator end, Comparator& comp)
    : current(begin), end(end), comp(comp)
    { }

    /** @brief Pre-increment operator.
    *  @return This. */
    guarded_iterator<RandomAccessIterator, Comparator>&
    operator++()
    {
      ++current;
      return *this;
    }

    /** @brief Dereference operator.
    *  @return Referenced element. */
    typename std::iterator_traits<RandomAccessIterator>::value_type&
    operator*()
    { return *current; }

    /** @brief Convert to wrapped iterator.
    *  @return Wrapped iterator. */
    operator RandomAccessIterator()
    { return current; }

    friend bool
    operator< <RandomAccessIterator, Comparator>(
      guarded_iterator<RandomAccessIterator, Comparator>& bi1,
      guarded_iterator<RandomAccessIterator, Comparator>& bi2);

    friend bool
    operator<= <RandomAccessIterator, Comparator>(
      guarded_iterator<RandomAccessIterator, Comparator>& bi1,
      guarded_iterator<RandomAccessIterator, Comparator>& bi2);
  };

/** @brief Compare two elements referenced by guarded iterators.
 *  @param bi1 First iterator.
 *  @param bi2 Second iterator.
 *  @return @c True if less. */
template<typename RandomAccessIterator, typename Comparator>
  inline bool
  operator<(guarded_iterator<RandomAccessIterator, Comparator>& bi1,
            guarded_iterator<RandomAccessIterator, Comparator>& bi2)
  {
    if (bi1.current == bi1.end)	//bi1 is sup
      return bi2.current == bi2.end;	//bi2 is not sup
    if (bi2.current == bi2.end)	//bi2 is sup
      return true;
    return (bi1.comp)(*bi1, *bi2);	//normal compare
  }

/** @brief Compare two elements referenced by guarded iterators.
 *  @param bi1 First iterator.
 *  @param bi2 Second iterator.
 *  @return @c True if less equal. */
template<typename RandomAccessIterator, typename Comparator>
  inline bool
  operator<=(guarded_iterator<RandomAccessIterator, Comparator>& bi1,
               guarded_iterator<RandomAccessIterator, Comparator>& bi2)
  {
    if (bi2.current == bi2.end)	//bi1 is sup
      return bi1.current != bi1.end;	//bi2 is not sup
    if (bi1.current == bi1.end)	//bi2 is sup
      return false;
    return !(bi1.comp)(*bi2, *bi1);	//normal compare
  }

template<typename RandomAccessIterator, typename Comparator>
  class unguarded_iterator;

template<typename RandomAccessIterator, typename Comparator>
  inline bool
  operator<(unguarded_iterator<RandomAccessIterator, Comparator>& bi1,
            unguarded_iterator<RandomAccessIterator, Comparator>& bi2);

template<typename RandomAccessIterator, typename Comparator>
  inline bool
  operator<=(unguarded_iterator<RandomAccessIterator, Comparator>& bi1,
             unguarded_iterator<RandomAccessIterator, Comparator>& bi2);

template<typename RandomAccessIterator, typename Comparator>
  class unguarded_iterator
  {
  private:
    /** @brief Current iterator position. */
    RandomAccessIterator current;
    /** @brief Comparator. */
    mutable Comparator& comp;

  public:
    /** @brief Constructor. Sets iterator to beginning of sequence.
    *  @param begin Begin iterator of sequence.
    *  @param end Unused, only for compatibility.
    *  @param comp Unused, only for compatibility. */
    unguarded_iterator(RandomAccessIterator begin,
                       RandomAccessIterator end, Comparator& comp)
    : current(begin), comp(comp)
    { }

    /** @brief Pre-increment operator.
    *  @return This. */
    unguarded_iterator<RandomAccessIterator, Comparator>&
    operator++()
    {
      ++current;
      return *this;
    }

    /** @brief Dereference operator.
    *  @return Referenced element. */
    typename std::iterator_traits<RandomAccessIterator>::value_type&
    operator*()
    { return *current; }

    /** @brief Convert to wrapped iterator.
    *  @return Wrapped iterator. */
    operator RandomAccessIterator()
    { return current; }

    friend bool
    operator< <RandomAccessIterator, Comparator>(
      unguarded_iterator<RandomAccessIterator, Comparator>& bi1,
      unguarded_iterator<RandomAccessIterator, Comparator>& bi2);

    friend bool
    operator<= <RandomAccessIterator, Comparator>(
      unguarded_iterator<RandomAccessIterator, Comparator>& bi1,
      unguarded_iterator<RandomAccessIterator, Comparator>& bi2);
  };

/** @brief Compare two elements referenced by unguarded iterators.
 *  @param bi1 First iterator.
 *  @param bi2 Second iterator.
 *  @return @c True if less. */
template<typename RandomAccessIterator, typename Comparator>
  inline bool
  operator<(unguarded_iterator<RandomAccessIterator, Comparator>& bi1,
            unguarded_iterator<RandomAccessIterator, Comparator>& bi2)
  {
    // Normal compare.
    return (bi1.comp)(*bi1, *bi2);
  }

/** @brief Compare two elements referenced by unguarded iterators.
 *  @param bi1 First iterator.
 *  @param bi2 Second iterator.
 *  @return @c True if less equal. */
template<typename RandomAccessIterator, typename Comparator>
  inline bool
  operator<=(unguarded_iterator<RandomAccessIterator, Comparator>& bi1,
            unguarded_iterator<RandomAccessIterator, Comparator>& bi2)
  {
    // Normal compare.
    return !(bi1.comp)(*bi2, *bi1);
  }

/** @brief Highly efficient 3-way merging procedure.
 *
 * Merging is done with the algorithm implementation described by Peter
 * Sanders.  Basically, the idea is to minimize the number of necessary
 * comparison after merging out an element.  The implementation trick
 * that makes this fast is that the order of the sequences is stored
 * in the instruction pointer (translated into labels in C++).
 *
 * This works well for merging up to 4 sequences.
 *
 * Note that making the merging stable does <em>not</em> come at a
 * performance hit.
 *
 * Whether the merging is done guarded or unguarded is selected by the
 * used iterator class.
 *
 * @param seqs_begin Begin iterator of iterator pair input sequence.
 * @param seqs_end End iterator of iterator pair input sequence.
 * @param target Begin iterator out output sequence.
 * @param comp Comparator.
 * @param length Maximum length to merge, less equal than the
 * total number of elements available.
 *
 * @return End iterator of output sequence.
 */
template<template<typename RAI, typename C> class iterator,
	 typename RandomAccessIteratorIterator,
	 typename RandomAccessIterator3,
	 typename _DifferenceTp,
	 typename Comparator>
  RandomAccessIterator3
  multiway_merge_3_variant(
      RandomAccessIteratorIterator seqs_begin,
      RandomAccessIteratorIterator seqs_end,
      RandomAccessIterator3 target,
      _DifferenceTp length, Comparator comp)
  {
    _GLIBCXX_CALL(length);

    typedef _DifferenceTp difference_type;

    typedef typename std::iterator_traits<RandomAccessIteratorIterator>
      ::value_type::first_type
      RandomAccessIterator1;
    typedef typename std::iterator_traits<RandomAccessIterator1>::value_type
      value_type;

    if (length == 0)
      return target;

#if _GLIBCXX_ASSERTIONS
    _DifferenceTp orig_length = length;
#endif

    iterator<RandomAccessIterator1, Comparator>
      seq0(seqs_begin[0].first, seqs_begin[0].second, comp),
      seq1(seqs_begin[1].first, seqs_begin[1].second, comp),
      seq2(seqs_begin[2].first, seqs_begin[2].second, comp);

    if (seq0 <= seq1)
      {
        if (seq1 <= seq2)
          goto s012;
        else
          if (seq2 <  seq0)
            goto s201;
          else
            goto s021;
      }
    else
      {
        if (seq1 <= seq2)
          {
            if (seq0 <= seq2)
              goto s102;
            else
              goto s120;
          }
        else
          goto s210;
      }
#define _GLIBCXX_PARALLEL_MERGE_3_CASE(a,b,c,c0,c1)     \
    s ## a ## b ## c :                                  \
      *target = *seq ## a;                              \
      ++target;                                         \
      --length;                                         \
      ++seq ## a;                                       \
      if (length == 0) goto finish;                     \
      if (seq ## a c0 seq ## b) goto s ## a ## b ## c;  \
      if (seq ## a c1 seq ## c) goto s ## b ## a ## c;  \
      goto s ## b ## c ## a;

    _GLIBCXX_PARALLEL_MERGE_3_CASE(0, 1, 2, <=, <=);
    _GLIBCXX_PARALLEL_MERGE_3_CASE(1, 2, 0, <=, < );
    _GLIBCXX_PARALLEL_MERGE_3_CASE(2, 0, 1, < , < );
    _GLIBCXX_PARALLEL_MERGE_3_CASE(1, 0, 2, < , <=);
    _GLIBCXX_PARALLEL_MERGE_3_CASE(0, 2, 1, <=, <=);
    _GLIBCXX_PARALLEL_MERGE_3_CASE(2, 1, 0, < , < );

#undef _GLIBCXX_PARALLEL_MERGE_3_CASE

  finish:
    ;

#if _GLIBCXX_ASSERTIONS
  _GLIBCXX_PARALLEL_ASSERT(
      ((RandomAccessIterator1)seq0 - seqs_begin[0].first) +
      ((RandomAccessIterator1)seq1 - seqs_begin[1].first) +
      ((RandomAccessIterator1)seq2 - seqs_begin[2].first)
      == orig_length);
#endif

    seqs_begin[0].first = seq0;
    seqs_begin[1].first = seq1;
    seqs_begin[2].first = seq2;

    return target;
  }

/**
 * @brief Highly efficient 4-way merging procedure.
 *
 * Merging is done with the algorithm implementation described by Peter
 * Sanders. Basically, the idea is to minimize the number of necessary
 * comparison after merging out an element.  The implementation trick
 * that makes this fast is that the order of the sequences is stored
 * in the instruction pointer (translated into goto labels in C++).
 *
 * This works well for merging up to 4 sequences.
 *
 * Note that making the merging stable does <em>not</em> come at a
 * performance hit.
 *
 * Whether the merging is done guarded or unguarded is selected by the
 * used iterator class.
 *
 * @param seqs_begin Begin iterator of iterator pair input sequence.
 * @param seqs_end End iterator of iterator pair input sequence.
 * @param target Begin iterator out output sequence.
 * @param comp Comparator.
 * @param length Maximum length to merge, less equal than the
 * total number of elements available.
 *
 * @return End iterator of output sequence.
 */
template<template<typename RAI, typename C> class iterator,
	 typename RandomAccessIteratorIterator,
	 typename RandomAccessIterator3,
	 typename _DifferenceTp,
	 typename Comparator>
  RandomAccessIterator3
  multiway_merge_4_variant(RandomAccessIteratorIterator seqs_begin,
                           RandomAccessIteratorIterator seqs_end,
                           RandomAccessIterator3 target,
                           _DifferenceTp length, Comparator comp)
  {
    _GLIBCXX_CALL(length);
    typedef _DifferenceTp difference_type;

    typedef typename std::iterator_traits<RandomAccessIteratorIterator>
      ::value_type::first_type
      RandomAccessIterator1;
    typedef typename std::iterator_traits<RandomAccessIterator1>::value_type
      value_type;

    iterator<RandomAccessIterator1, Comparator>
      seq0(seqs_begin[0].first, seqs_begin[0].second, comp),
      seq1(seqs_begin[1].first, seqs_begin[1].second, comp),
      seq2(seqs_begin[2].first, seqs_begin[2].second, comp),
      seq3(seqs_begin[3].first, seqs_begin[3].second, comp);

#define _GLIBCXX_PARALLEL_DECISION(a,b,c,d) {                   \
      if (seq ## d < seq ## a) goto s ## d ## a ## b ## c;	\
      if (seq ## d < seq ## b) goto s ## a ## d ## b ## c;	\
      if (seq ## d < seq ## c) goto s ## a ## b ## d ## c;	\
      goto s ## a ## b ## c ## d;  }

    if (seq0 <= seq1)
      {
        if (seq1 <= seq2)
          _GLIBCXX_PARALLEL_DECISION(0,1,2,3)
          else
            if (seq2 < seq0)
              _GLIBCXX_PARALLEL_DECISION(2,0,1,3)
              else
                _GLIBCXX_PARALLEL_DECISION(0,2,1,3)
                  }
    else
      {
        if (seq1 <= seq2)
          {
            if (seq0 <= seq2)
              _GLIBCXX_PARALLEL_DECISION(1,0,2,3)
              else
                _GLIBCXX_PARALLEL_DECISION(1,2,0,3)
                  }
        else
          _GLIBCXX_PARALLEL_DECISION(2,1,0,3)
            }

#define _GLIBCXX_PARALLEL_MERGE_4_CASE(a,b,c,d,c0,c1,c2)        \
    s ## a ## b ## c ## d:                                      \
      if (length == 0) goto finish;                             \
    *target = *seq ## a;                                        \
    ++target;                                                   \
    --length;                                                   \
    ++seq ## a;                                                 \
    if (seq ## a c0 seq ## b) goto s ## a ## b ## c ## d;       \
    if (seq ## a c1 seq ## c) goto s ## b ## a ## c ## d;       \
    if (seq ## a c2 seq ## d) goto s ## b ## c ## a ## d;       \
    goto s ## b ## c ## d ## a;

    _GLIBCXX_PARALLEL_MERGE_4_CASE(0, 1, 2, 3, <=, <=, <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(0, 1, 3, 2, <=, <=, <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(0, 2, 1, 3, <=, <=, <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(0, 2, 3, 1, <=, <=, <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(0, 3, 1, 2, <=, <=, <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(0, 3, 2, 1, <=, <=, <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(1, 0, 2, 3, < , <=, <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(1, 0, 3, 2, < , <=, <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(1, 2, 0, 3, <=, < , <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(1, 2, 3, 0, <=, <=, < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(1, 3, 0, 2, <=, < , <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(1, 3, 2, 0, <=, <=, < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(2, 0, 1, 3, < , < , <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(2, 0, 3, 1, < , <=, < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(2, 1, 0, 3, < , < , <=);
    _GLIBCXX_PARALLEL_MERGE_4_CASE(2, 1, 3, 0, < , <=, < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(2, 3, 0, 1, <=, < , < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(2, 3, 1, 0, <=, < , < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(3, 0, 1, 2, < , < , < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(3, 0, 2, 1, < , < , < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(3, 1, 0, 2, < , < , < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(3, 1, 2, 0, < , < , < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(3, 2, 0, 1, < , < , < );
    _GLIBCXX_PARALLEL_MERGE_4_CASE(3, 2, 1, 0, < , < , < );

#undef _GLIBCXX_PARALLEL_MERGE_4_CASE
#undef _GLIBCXX_PARALLEL_DECISION

  finish:
    ;

    seqs_begin[0].first = seq0;
    seqs_begin[1].first = seq1;
    seqs_begin[2].first = seq2;
    seqs_begin[3].first = seq3;

    return target;
  }

/** @brief Multi-way merging procedure for a high branching factor,
 *         guarded case.
 *
 * This merging variant uses a LoserTree class as selected by <tt>LT</tt>.
 *
 * Stability is selected through the used LoserTree class <tt>LT</tt>.
 *
 * At least one non-empty sequence is required.
 *
 * @param seqs_begin Begin iterator of iterator pair input sequence.
 * @param seqs_end End iterator of iterator pair input sequence.
 * @param target Begin iterator out output sequence.
 * @param comp Comparator.
 * @param length Maximum length to merge, less equal than the
 * total number of elements available.
 *
 * @return End iterator of output sequence.
 */
template<typename LT,
	 typename RandomAccessIteratorIterator,
	 typename RandomAccessIterator3,
	 typename _DifferenceTp,
	 typename Comparator>
  RandomAccessIterator3
  multiway_merge_loser_tree(RandomAccessIteratorIterator seqs_begin,
                            RandomAccessIteratorIterator seqs_end,
                            RandomAccessIterator3 target,
                            _DifferenceTp length, Comparator comp)
  {
    _GLIBCXX_CALL(length)

    typedef _DifferenceTp difference_type;
    typedef typename std::iterator_traits<RandomAccessIteratorIterator>
      ::value_type::first_type
      RandomAccessIterator1;
    typedef typename std::iterator_traits<RandomAccessIterator1>::value_type
      value_type;

    int k = static_cast<int>(seqs_end - seqs_begin);

    LT lt(k, comp);

    // Default value for potentially non-default-constructible types.
    value_type* arbitrary_element = NULL;

    for (int t = 0; t < k; ++t)
      {
        if(arbitrary_element == NULL
            && _GLIBCXX_PARALLEL_LENGTH(seqs_begin[t]) > 0)
          arbitrary_element = &(*seqs_begin[t].first);
      }

    for (int t = 0; t < k; ++t)
      {
        if (seqs_begin[t].first == seqs_begin[t].second)
          lt.insert_start(*arbitrary_element, t, true);
        else
          lt.insert_start(*seqs_begin[t].first, t, false);
      }

    lt.init();

    int source;

    for (difference_type i = 0; i < length; ++i)
      {
        //take out
        source = lt.get_min_source();

        *(target++) = *(seqs_begin[source].first++);

        // Feed.
        if (seqs_begin[source].first == seqs_begin[source].second)
          lt.delete_min_insert(*arbitrary_element, true);
        else
          // Replace from same source.
          lt.delete_min_insert(*seqs_begin[source].first, false);
      }

    return target;
  }

/** @brief Multi-way merging procedure for a high branching factor,
 *         unguarded case.
 *
 * Merging is done using the LoserTree class <tt>LT</tt>.
 *
 * Stability is selected by the used LoserTrees.
 *
 * @pre No input will run out of elements during the merge.
 *
 * @param seqs_begin Begin iterator of iterator pair input sequence.
 * @param seqs_end End iterator of iterator pair input sequence.
 * @param target Begin iterator out output sequence.
 * @param comp Comparator.
 * @param length Maximum length to merge, less equal than the
 * total number of elements available.
 *
 * @return End iterator of output sequence.
 */
template<typename LT,
    typename RandomAccessIteratorIterator,
    typename RandomAccessIterator3,
    typename _DifferenceTp, typename Comparator>
  RandomAccessIterator3
  multiway_merge_loser_tree_unguarded(
    RandomAccessIteratorIterator seqs_begin,
    RandomAccessIteratorIterator seqs_end,
    RandomAccessIterator3 target,
    const typename std::iterator_traits<typename std::iterator_traits<
      RandomAccessIteratorIterator>::value_type::first_type>::value_type&
        sentinel,
    _DifferenceTp length,
    Comparator comp)
  {
    _GLIBCXX_CALL(length)
    typedef _DifferenceTp difference_type;

    typedef typename std::iterator_traits<RandomAccessIteratorIterator>
      ::value_type::first_type
      RandomAccessIterator1;
    typedef typename std::iterator_traits<RandomAccessIterator1>::value_type
      value_type;

    int k = seqs_end - seqs_begin;

    LT lt(k, sentinel, comp);

    for (int t = 0; t < k; ++t)
      {
#if _GLIBCXX_ASSERTIONS
        _GLIBCXX_PARALLEL_ASSERT(seqs_begin[t].first != seqs_begin[t].second);
#endif
        lt.insert_start(*seqs_begin[t].first, t, false);
      }

    lt.init();

    int source;

#if _GLIBCXX_ASSERTIONS
    difference_type i = 0;
#endif

    RandomAccessIterator3 target_end = target + length;
    while (target < target_end)
      {
        // Take out.
        source = lt.get_min_source();

#if _GLIBCXX_ASSERTIONS
        _GLIBCXX_PARALLEL_ASSERT(0 <= source && source < k);
        _GLIBCXX_PARALLEL_ASSERT(i == 0
            || !comp(*(seqs_begin[source].first), *(target - 1)));
#endif

        // Feed.
        *(target++) = *(seqs_begin[source].first++);

#if _GLIBCXX_ASSERTIONS
        ++i;
#endif
        // Replace from same source.
        lt.delete_min_insert(*seqs_begin[source].first, false);
      }

    return target;
  }


/** @brief Multi-way merging procedure for a high branching factor,
 *         requiring sentinels to exist.
 *
 * @param stable The value must the same as for the used LoserTrees.
 * @param UnguardedLoserTree Loser Tree variant to use for the unguarded
 *   merging.
 * @param GuardedLoserTree Loser Tree variant to use for the guarded
 *   merging.
 *
 * @param seqs_begin Begin iterator of iterator pair input sequence.
 * @param seqs_end End iterator of iterator pair input sequence.
 * @param target Begin iterator out output sequence.
 * @param comp Comparator.
 * @param length Maximum length to merge, less equal than the
 * total number of elements available.
 *
 * @return End iterator of output sequence.
 */
template<
    typename UnguardedLoserTree,
    typename RandomAccessIteratorIterator,
    typename RandomAccessIterator3,
    typename _DifferenceTp,
    typename Comparator>
  RandomAccessIterator3
  multiway_merge_loser_tree_sentinel(
    RandomAccessIteratorIterator seqs_begin,
    RandomAccessIteratorIterator seqs_end,
    RandomAccessIterator3 target,
    const typename std::iterator_traits<typename std::iterator_traits<
      RandomAccessIteratorIterator>::value_type::first_type>::value_type&
        sentinel,
    _DifferenceTp length,
    Comparator comp)
  {
    _GLIBCXX_CALL(length)

    typedef _DifferenceTp difference_type;
    typedef std::iterator_traits<RandomAccessIteratorIterator> traits_type;
    typedef typename std::iterator_traits<RandomAccessIteratorIterator>
      ::value_type::first_type
      RandomAccessIterator1;
    typedef typename std::iterator_traits<RandomAccessIterator1>::value_type
      value_type;

    RandomAccessIterator3 target_end;

    for (RandomAccessIteratorIterator s = seqs_begin; s != seqs_end; ++s)
      // Move the sequends end behind the sentinel spots.  This has the
      // effect that the sentinel appears to be within the sequence. Then,
      // we can use the unguarded variant if we merge out as many
      // non-sentinel elements as we have.
      ++((*s).second);

    target_end = multiway_merge_loser_tree_unguarded
        <UnguardedLoserTree>
      (seqs_begin, seqs_end, target, sentinel, length, comp);

#if _GLIBCXX_ASSERTIONS
    _GLIBCXX_PARALLEL_ASSERT(target_end == target + length);
    _GLIBCXX_PARALLEL_ASSERT(is_sorted(target, target_end, comp));
#endif

    // Restore the sequence ends so the sentinels are not contained in the
    // sequence any more (see comment in loop above).
    for (RandomAccessIteratorIterator s = seqs_begin; s != seqs_end; ++s)
      --((*s).second);

    return target_end;
  }

/**
 * @brief Traits for determining whether the loser tree should
 *   use pointers or copies.
 *
 * The field "use_pointer" is used to determine whether to use pointers in
 * the loser trees or whether to copy the values into the loser tree.
 *
 * The default behavior is to use pointers if the data type is 4 times as
 * big as the pointer to it.
 *
 * Specialize for your data type to customize the behavior.
 *
 * Example:
 *
 *   template<>
 *   struct loser_tree_traits<int>
 *   { static const bool use_pointer = false; };
 *
 *   template<>
 *   struct loser_tree_traits<heavyweight_type>
 *   { static const bool use_pointer = true; };
 *
 * @param T type to give the loser tree traits for.
 */
template <typename T>
struct loser_tree_traits
{
  /**
   * @brief True iff to use pointers instead of values in loser trees.
   *
   * The default behavior is to use pointers if the data type is four
   * times as big as the pointer to it.
   */
  static const bool use_pointer = (sizeof(T) > 4 * sizeof(T*));
};

/**
 * @brief Switch for 3-way merging with sentinels turned off.
 *
 * Note that 3-way merging is always stable!
 */
template<
  bool sentinels /*default == false*/,
  typename RandomAccessIteratorIterator,
  typename RandomAccessIterator3,
  typename _DifferenceTp,
  typename Comparator>
struct multiway_merge_3_variant_sentinel_switch
{
  RandomAccessIterator3 operator()(
      RandomAccessIteratorIterator seqs_begin,
      RandomAccessIteratorIterator seqs_end,
      RandomAccessIterator3 target,
      _DifferenceTp length, Comparator comp)
  {
    return multiway_merge_3_variant<guarded_iterator>(
        seqs_begin, seqs_end, target, length, comp);
  }
};

/**
 * @brief Switch for 3-way merging with sentinels turned on.
 *
 * Note that 3-way merging is always stable!
 */
template<
  typename RandomAccessIteratorIterator,
  typename RandomAccessIterator3,
  typename _DifferenceTp,
  typename Comparator>
struct multiway_merge_3_variant_sentinel_switch
    <true, RandomAccessIteratorIterator, RandomAccessIterator3,
     _DifferenceTp, Comparator>
{
  RandomAccessIterator3 operator()(
      RandomAccessIteratorIterator seqs_begin,
      RandomAccessIteratorIterator seqs_end,
      RandomAccessIterator3 target,
      _DifferenceTp length, Comparator comp)
  {
    return multiway_merge_3_variant<unguarded_iterator>(
        seqs_begin, seqs_end, target, length, comp);
  }
};

/**
 * @brief Switch for 4-way merging with sentinels turned off.
 *
 * Note that 4-way merging is always stable!
 */
template<
  bool sentinels /*default == false*/,
  typename RandomAccessIteratorIterator,
  typename RandomAccessIterator3,
  typename _DifferenceTp,
  typename Comparator>
struct multiway_merge_4_variant_sentinel_switch
{
  RandomAccessIterator3 operator()(
      RandomAccessIteratorIterator seqs_begin,
      RandomAccessIteratorIterator seqs_end,
      RandomAccessIterator3 target,
      _DifferenceTp length, Comparator comp)
  {
    return multiway_merge_4_variant<guarded_iterator>(
        seqs_begin, seqs_end, target, length, comp);
  }
};

/**
 * @brief Switch for 4-way merging with sentinels turned on.
 *
 * Note that 4-way merging is always stable!
 */
template<
  typename RandomAccessIteratorIterator,
  typename RandomAccessIterator3,
  typename _DifferenceTp,
  typename Comparator>
struct multiway_merge_4_variant_sentinel_switch
    <true, RandomAccessIteratorIterator, RandomAccessIterator3,
     _DifferenceTp, Comparator>
{
  RandomAccessIterator3 operator()(
      RandomAccessIteratorIterator seqs_begin,
      RandomAccessIteratorIterator seqs_end,
      RandomAccessIterator3 target,
      _DifferenceTp length, Comparator comp)
  {
    return multiway_merge_4_variant<unguarded_iterator>(
        seqs_begin, seqs_end, target, length, comp);
  }
};

/**
 * @brief Switch for k-way merging with sentinels turned on.
 */
template<
  bool sentinels,
  bool stable,
  typename RandomAccessIteratorIterator,
  typename RandomAccessIterator3,
  typename _DifferenceTp,
  typename Comparator>
struct multiway_merge_k_variant_sentinel_switch
{
  RandomAccessIterator3 operator()(
      RandomAccessIteratorIterator seqs_begin,
      RandomAccessIteratorIterator seqs_end,
      RandomAccessIterator3 target,
      const typename std::iterator_traits<typename std::iterator_traits<
        RandomAccessIteratorIterator>::value_type::first_type>::value_type&
          sentinel,
      _DifferenceTp length, Comparator comp)
  {
    typedef typename std::iterator_traits<RandomAccessIteratorIterator>
      ::value_type::first_type
      RandomAccessIterator1;
    typedef typename std::iterator_traits<RandomAccessIterator1>::value_type
      value_type;

    return multiway_merge_loser_tree_sentinel<
        typename __gnu_cxx::__conditional_type<
            loser_tree_traits<value_type>::use_pointer
          , LoserTreePointerUnguarded<stable, value_type, Comparator>
          , LoserTreeUnguarded<stable, value_type, Comparator>
        >::__type>(seqs_begin, seqs_end, target, sentinel, length, comp);
  }
};

/**
 * @brief Switch for k-way merging with sentinels turned off.
 */
template<
  bool stable,
  typename RandomAccessIteratorIterator,
  typename RandomAccessIterator3,
  typename _DifferenceTp,
  typename Comparator>
struct multiway_merge_k_variant_sentinel_switch
    <false, stable, RandomAccessIteratorIterator, RandomAccessIterator3,
     _DifferenceTp, Comparator>
{
  RandomAccessIterator3 operator()(
      RandomAccessIteratorIterator seqs_begin,
      RandomAccessIteratorIterator seqs_end,
      RandomAccessIterator3 target,
      const typename std::iterator_traits<typename std::iterator_traits<
        RandomAccessIteratorIterator>::value_type::first_type>::value_type&
          sentinel,
      _DifferenceTp length, Comparator comp)
  {
    typedef typename std::iterator_traits<RandomAccessIteratorIterator>
      ::value_type::first_type
      RandomAccessIterator1;
    typedef typename std::iterator_traits<RandomAccessIterator1>::value_type
      value_type;

    return multiway_merge_loser_tree<
        typename __gnu_cxx::__conditional_type<
            loser_tree_traits<value_type>::use_pointer
          , LoserTreePointer<stable, value_type, Comparator>
          , LoserTree<stable, value_type, Comparator>
        >::__type >(seqs_begin, seqs_end, target, length, comp);
  }
};

/** @brief Sequential multi-way merging switch.
 *
 *  The _GLIBCXX_PARALLEL_DECISION is based on the branching factor and
 *  runtime settings.
 *  @param seqs_begin Begin iterator of iterator pair input sequence.
 *  @param seqs_end End iterator of iterator pair input sequence.
 *  @param target Begin iterator out output sequence.
 *  @param comp Comparator.
 *  @param length Maximum length to merge, possibly larger than the
 *  number of elements available.
 *  @param stable Stable merging incurs a performance penalty.
 *  @param sentinel The sequences have a sentinel element.
 *  @return End iterator of output sequence. */
template<
    bool stable,
    bool sentinels,
    typename RandomAccessIteratorIterator,
    typename RandomAccessIterator3,
    typename _DifferenceTp,
    typename Comparator>
  RandomAccessIterator3
  sequential_multiway_merge(
    RandomAccessIteratorIterator seqs_begin,
    RandomAccessIteratorIterator seqs_end,
    RandomAccessIterator3 target,
    const typename std::iterator_traits<typename std::iterator_traits<
      RandomAccessIteratorIterator>::value_type::first_type>::value_type&
        sentinel,
    _DifferenceTp length, Comparator comp)
  {
    _GLIBCXX_CALL(length)

    typedef _DifferenceTp difference_type;
    typedef typename std::iterator_traits<RandomAccessIteratorIterator>
      ::value_type::first_type
      RandomAccessIterator1;
    typedef typename std::iterator_traits<RandomAccessIterator1>::value_type
      value_type;

#if _GLIBCXX_ASSERTIONS
    for (RandomAccessIteratorIterator s = seqs_begin; s != seqs_end; ++s)
      {
        _GLIBCXX_PARALLEL_ASSERT(is_sorted((*s).first, (*s).second, comp));
      }
#endif

    _DifferenceTp total_length = 0;
    for (RandomAccessIteratorIterator s = seqs_begin; s != seqs_end; ++s)
      total_length += _GLIBCXX_PARALLEL_LENGTH(*s);

    length = std::min<_DifferenceTp>(length, total_length);

    if(length == 0)
      return target;

    RandomAccessIterator3 return_target = target;
    int k = static_cast<int>(seqs_end - seqs_begin);

    switch (k)
      {
      case 0:
        break;
      case 1:
        return_target = std::copy(seqs_begin[0].first,
                                  seqs_begin[0].first + length,
                                  target);
        seqs_begin[0].first += length;
        break;
      case 2:
        return_target = merge_advance(seqs_begin[0].first,
                                      seqs_begin[0].second,
                                      seqs_begin[1].first,
                                      seqs_begin[1].second,
                                      target, length, comp);
        break;
      case 3:
        return_target = multiway_merge_3_variant_sentinel_switch<
            sentinels
          , RandomAccessIteratorIterator
          , RandomAccessIterator3
          , _DifferenceTp
          , Comparator>()(seqs_begin, seqs_end, target, length, comp);
        break;
      case 4:
        return_target = multiway_merge_4_variant_sentinel_switch<
            sentinels
          , RandomAccessIteratorIterator
          , RandomAccessIterator3
          , _DifferenceTp
          , Comparator>()(seqs_begin, seqs_end, target, length, comp);
        break;
      default:
          return_target = multiway_merge_k_variant_sentinel_switch<
              sentinels
            , stable
            , RandomAccessIteratorIterator
            , RandomAccessIterator3
            , _DifferenceTp
            , Comparator>()(seqs_begin, seqs_end, target, sentinel, length, comp);
          break;
      }
#if _GLIBCXX_ASSERTIONS
    _GLIBCXX_PARALLEL_ASSERT(is_sorted(target, target + length, comp));
#endif

    return return_target;
  }

/**
 * @brief Stable sorting functor.
 *
 * Used to reduce code instanciation in multiway_merge_sampling_splitting.
 */
template<bool stable, class RandomAccessIterator, class StrictWeakOrdering>
struct sampling_sorter
{
  void operator()(RandomAccessIterator first, RandomAccessIterator last,
                  StrictWeakOrdering comp)
  { __gnu_sequential::stable_sort(first, last, comp); }
};

/**
 * @brief Non-stable sorting functor.
 *
 * Used to reduce code instantiation in multiway_merge_sampling_splitting.
 */
template<class RandomAccessIterator, class StrictWeakOrdering>
struct sampling_sorter<false, RandomAccessIterator, StrictWeakOrdering>
{
  void operator()(RandomAccessIterator first, RandomAccessIterator last,
                  StrictWeakOrdering comp)
  { __gnu_sequential::sort(first, last, comp); }
};

/**
 * @brief Sampling based splitting for parallel multiway-merge routine.
 */
template<
    bool stable
  , typename RandomAccessIteratorIterator
  , typename Comparator
  , typename difference_type>
void multiway_merge_sampling_splitting(
    RandomAccessIteratorIterator seqs_begin,
    RandomAccessIteratorIterator seqs_end,
    difference_type length, difference_type total_length, Comparator comp,
    std::vector<std::pair<difference_type, difference_type> > *pieces)
{
  typedef typename std::iterator_traits<RandomAccessIteratorIterator>
    ::value_type::first_type
    RandomAccessIterator1;
  typedef typename std::iterator_traits<RandomAccessIterator1>::value_type
    value_type;

  // k sequences.
  int k = static_cast<int>(seqs_end - seqs_begin);

  int num_threads = omp_get_num_threads();

  difference_type num_samples =
      __gnu_parallel::_Settings::get().merge_oversampling * num_threads;

  value_type* samples = static_cast<value_type*>(
    ::operator new(sizeof(value_type) * k * num_samples));
  // Sample.
  for (int s = 0; s < k; ++s)
    for (difference_type i = 0; i < num_samples; ++i)
      {
        difference_type sample_index =
            static_cast<difference_type>(
                _GLIBCXX_PARALLEL_LENGTH(seqs_begin[s])
                    * (double(i + 1) / (num_samples + 1))
                    * (double(length) / total_length));
        new(&(samples[s * num_samples + i]))
            value_type(seqs_begin[s].first[sample_index]);
      }

  // Sort stable or non-stable, depending on value of template parameter
  // "stable".
  sampling_sorter<stable, value_type*, Comparator>()(
      samples, samples + (num_samples * k), comp);

  for (int slab = 0; slab < num_threads; ++slab)
    // For each slab / processor.
    for (int seq = 0; seq < k; ++seq)
      {
        // For each sequence.
        if (slab > 0)
          pieces[slab][seq].first =
              std::upper_bound(
                seqs_begin[seq].first,
                seqs_begin[seq].second,
                samples[num_samples * k * slab / num_threads],
                  comp)
              - seqs_begin[seq].first;
        else
          // Absolute beginning.
          pieces[slab][seq].first = 0;
        if ((slab + 1) < num_threads)
          pieces[slab][seq].second =
              std::upper_bound(
                  seqs_begin[seq].first,
                  seqs_begin[seq].second,
                  samples[num_samples * k * (slab + 1) /
                      num_threads], comp)
              - seqs_begin[seq].first;
        else
            // Absolute end.
          pieces[slab][seq].second = _GLIBCXX_PARALLEL_LENGTH(seqs_begin[seq]);
      }
    ::operator delete(samples);
}

/**
 * @brief Exact splitting for parallel multiway-merge routine.
 *
 * None of the passed sequences may be empty.
 */
template<
    bool stable
  , typename RandomAccessIteratorIterator
  , typename Comparator
  , typename difference_type>
void multiway_merge_exact_splitting(
    RandomAccessIteratorIterator seqs_begin,
    RandomAccessIteratorIterator seqs_end,
    difference_type length, difference_type total_length, Comparator comp,
    std::vector<std::pair<difference_type, difference_type> > *pieces)
{
  typedef typename std::iterator_traits<RandomAccessIteratorIterator>
    ::value_type::first_type
    RandomAccessIterator1;

  const bool tight = (total_length == length);

  // k sequences.
  const int k = static_cast<int>(seqs_end - seqs_begin);

  const int num_threads = omp_get_num_threads();

  // (Settings::multiway_merge_splitting == __gnu_parallel::_Settings::EXACT).
  std::vector<RandomAccessIterator1>* offsets =
      new std::vector<RandomAccessIterator1>[num_threads];
  std::vector<
      std::pair<RandomAccessIterator1, RandomAccessIterator1>
      > se(k);

  copy(seqs_begin, seqs_end, se.begin());

  difference_type* borders =
      new difference_type[num_threads + 1];
  equally_split(length, num_threads, borders);

  for (int s = 0; s < (num_threads - 1); ++s)
    {
      offsets[s].resize(k);
      multiseq_partition(
          se.begin(), se.end(), borders[s + 1],
          offsets[s].begin(), comp);

      // Last one also needed and available.
      if (!tight)
        {
          offsets[num_threads - 1].resize(k);
          multiseq_partition(se.begin(), se.end(),
                difference_type(length),
                offsets[num_threads - 1].begin(),  comp);
        }
    }


  for (int slab = 0; slab < num_threads; ++slab)
    {
      // For each slab / processor.
      for (int seq = 0; seq < k; ++seq)
        {
          // For each sequence.
          if (slab == 0)
            {
              // Absolute beginning.
              pieces[slab][seq].first = 0;
            }
          else
            pieces[slab][seq].first =
                pieces[slab - 1][seq].second;
          if (!tight || slab < (num_threads - 1))
            pieces[slab][seq].second =
                offsets[slab][seq] - seqs_begin[seq].first;
          else
            {
              // slab == num_threads - 1
              pieces[slab][seq].second =
                  _GLIBCXX_PARALLEL_LENGTH(seqs_begin[seq]);
            }
        }
    }
  delete[] offsets;
}

/** @brief Parallel multi-way merge routine.
 *
 * The _GLIBCXX_PARALLEL_DECISION is based on the branching factor
 * and runtime settings.
 *
 * Must not be called if the number of sequences is 1.
 *
 * @param Splitter functor to split input (either exact or sampling based)
 *
 * @param seqs_begin Begin iterator of iterator pair input sequence.
 * @param seqs_end End iterator of iterator pair input sequence.
 * @param target Begin iterator out output sequence.
 * @param comp Comparator.
 * @param length Maximum length to merge, possibly larger than the
 * number of elements available.
 * @param stable Stable merging incurs a performance penalty.
 * @param sentinel Ignored.
 * @return End iterator of output sequence.
 */
template<
    bool stable,
    bool sentinels,
    typename RandomAccessIteratorIterator,
    typename RandomAccessIterator3,
    typename _DifferenceTp,
    typename Splitter,
    typename Comparator
    >
  RandomAccessIterator3
  parallel_multiway_merge(RandomAccessIteratorIterator seqs_begin,
                          RandomAccessIteratorIterator seqs_end,
                          RandomAccessIterator3 target,
                          Splitter splitter,
                          _DifferenceTp length,
                          Comparator comp,
                          thread_index_t num_threads)
    {
#if _GLIBCXX_ASSERTIONS
      _GLIBCXX_PARALLEL_ASSERT(seqs_end - seqs_begin > 1);
#endif

      _GLIBCXX_CALL(length)

      typedef _DifferenceTp difference_type;
      typedef typename std::iterator_traits<RandomAccessIteratorIterator>
        ::value_type::first_type
        RandomAccessIterator1;
      typedef typename
        std::iterator_traits<RandomAccessIterator1>::value_type value_type;

      // Leave only non-empty sequences.
      std::pair<RandomAccessIterator1, RandomAccessIterator1>* ne_seqs =
        static_cast<std::pair<RandomAccessIterator1, RandomAccessIterator1>*>(
        ::operator new(
            sizeof(std::pair<RandomAccessIterator1, RandomAccessIterator1>)
              * (seqs_end - seqs_begin)));
      int k = 0;
      difference_type total_length = 0;
      for (RandomAccessIteratorIterator raii = seqs_begin;
           raii != seqs_end; ++raii)
        {
          _DifferenceTp seq_length = _GLIBCXX_PARALLEL_LENGTH(*raii);
          if(seq_length > 0)
            {
              total_length += seq_length;
              //ne_seqs[k] = *raii;
              new(&(ne_seqs[k++]))
                std::pair<RandomAccessIterator1, RandomAccessIterator1>(*raii);
            }
        }

      _GLIBCXX_CALL(total_length)

      length = std::min<_DifferenceTp>(length, total_length);

      if (total_length == 0 || k == 0)
      {
        ::operator delete(ne_seqs);
        return target;
      }

      std::vector<std::pair<difference_type, difference_type> >* pieces;

      num_threads = static_cast<thread_index_t>
        (std::min<difference_type>(num_threads, total_length));

#     pragma omp parallel num_threads (num_threads)
        {
#         pragma omp single
            {
              num_threads = omp_get_num_threads();
              // Thread t will have to merge pieces[iam][0..k - 1]
              pieces = new std::vector<
                  std::pair<difference_type, difference_type> >[num_threads];
              for (int s = 0; s < num_threads; ++s)
                pieces[s].resize(k);

              difference_type num_samples =
                  __gnu_parallel::_Settings::get().merge_oversampling *
                    num_threads;

              splitter(ne_seqs, ne_seqs + k, length, total_length,
                       comp, pieces);
            } //single

          thread_index_t iam = omp_get_thread_num();

          difference_type target_position = 0;

          for (int c = 0; c < k; ++c)
            target_position += pieces[iam][c].first;

          std::pair<RandomAccessIterator1, RandomAccessIterator1>* chunks
            = new std::pair<RandomAccessIterator1, RandomAccessIterator1>[k];

          for (int s = 0; s < k; ++s)
            {
              chunks[s] = std::make_pair(
                ne_seqs[s].first + pieces[iam][s].first,
                ne_seqs[s].first + pieces[iam][s].second);
            }

          if(length > target_position)
            sequential_multiway_merge<stable, sentinels>(
              chunks, chunks + k, target + target_position,
               *(seqs_begin->second), length - target_position, comp);

          delete[] chunks;
        } // parallel

#if _GLIBCXX_ASSERTIONS
      _GLIBCXX_PARALLEL_ASSERT(is_sorted(target, target + length, comp));
#endif

      k = 0;
      // Update ends of sequences.
      for (RandomAccessIteratorIterator raii = seqs_begin;
           raii != seqs_end; ++raii)
        {
          _DifferenceTp length = _GLIBCXX_PARALLEL_LENGTH(*raii);
          if(length > 0)
            (*raii).first += pieces[num_threads - 1][k++].second;
        }

      delete[] pieces;

      return target + length;
    }

/**
 * @brief Multiway Merge Frontend.
 *
 * Merge the sequences specified by seqs_begin and seqs_end into
 * target.  seqs_begin and seqs_end must point to a sequence of
 * pairs.  These pairs must contain an iterator to the beginning
 * of a sequence in their first entry and an iterator the end of
 * the same sequence in their second entry.
 *
 * Ties are broken arbitrarily.  See stable_multiway_merge for a variant
 * that breaks ties by sequence number but is slower.
 *
 * The first entries of the pairs (i.e. the begin iterators) will be moved
 * forward.
 *
 * The output sequence has to provide enough space for all elements
 * that are written to it.
 *
 * This function will merge the input sequences:
 *
 * - not stable
 * - parallel, depending on the input size and Settings
 * - using sampling for splitting
 * - not using sentinels
 *
 * Example:
 *
 * <pre>
 *   int sequences[10][10];
 *   for (int i = 0; i < 10; ++i)
 *     for (int j = 0; i < 10; ++j)
 *       sequences[i][j] = j;
 *
 *   int out[33];
 *   std::vector<std::pair<int*> > seqs;
 *   for (int i = 0; i < 10; ++i)
 *     { seqs.push(std::make_pair<int*>(sequences[i], sequences[i] + 10)) }
 *
 *   multiway_merge(seqs.begin(), seqs.end(), target, std::less<int>(), 33);
 * </pre>
 *
 * @see stable_multiway_merge
 *
 * @pre All input sequences must be sorted.
 * @pre Target must provide enough space to merge out length elements or
 *    the number of elements in all sequences, whichever is smaller.
 *
 * @post [target, return value) contains merged elements from the
 *    input sequences.
 * @post return value - target = min(length, number of elements in all
 *    sequences).
 *
 * @param RandomAccessIteratorPairIterator iterator over sequence
 *    of pairs of iterators
 * @param RandomAccessIteratorOut iterator over target sequence
 * @param _DifferenceTp difference type for the sequence
 * @param Comparator strict weak ordering type to compare elements
 *    in sequences
 *
 * @param seqs_begin  begin of sequence sequence
 * @param seqs_end    end of sequence sequence
 * @param target      target sequence to merge to.
 * @param comp        strict weak ordering to use for element comparison.
 * @param length Maximum length to merge, possibly larger than the
 * number of elements available.
 *
 * @return end iterator of output sequence
 */
// multiway_merge
// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , __gnu_parallel::sequential_tag)
{
  typedef _DifferenceTp difference_type;
  _GLIBCXX_CALL(seqs_end - seqs_begin)

  // catch special case: no sequences
  if (seqs_begin == seqs_end)
    return target;

  // Execute multiway merge *sequentially*.
  return sequential_multiway_merge
    </* stable = */ false, /* sentinels = */ false>
      (seqs_begin, seqs_end, target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , __gnu_parallel::exact_tag tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute merge; maybe parallel, depending on the number of merged
    // elements and the number of sequences and global thresholds in
    // Settings.
    if ((seqs_end - seqs_begin > 1) &&
          _GLIBCXX_PARALLEL_CONDITION(
          ((seqs_end - seqs_begin) >=
             __gnu_parallel::_Settings::get().multiway_merge_minimal_k)
          && ((sequence_index_t)length >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_n)))
      return parallel_multiway_merge
                    </* stable = */ false, /* sentinels = */ false>(
          seqs_begin, seqs_end, target,
          multiway_merge_exact_splitting</* stable = */ false,
            typename std::iterator_traits<RandomAccessIteratorPairIterator>
              ::value_type*, Comparator, _DifferenceTp>,
          static_cast<difference_type>(length), comp, tag.get_num_threads());
    else
      return sequential_multiway_merge
                      </* stable = */ false, /* sentinels = */ false>(
          seqs_begin, seqs_end, target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , __gnu_parallel::sampling_tag tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute merge; maybe parallel, depending on the number of merged
    // elements and the number of sequences and global thresholds in
    // Settings.
    if ((seqs_end - seqs_begin > 1) &&
          _GLIBCXX_PARALLEL_CONDITION(
          ((seqs_end - seqs_begin) >=
             __gnu_parallel::_Settings::get().multiway_merge_minimal_k)
          && ((sequence_index_t)length >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_n)))
      return parallel_multiway_merge
                    </* stable = */ false, /* sentinels = */ false>(
          seqs_begin, seqs_end,
          target,
          multiway_merge_exact_splitting</* stable = */ false,
            typename std::iterator_traits<RandomAccessIteratorPairIterator>
              ::value_type*, Comparator, _DifferenceTp>,
          static_cast<difference_type>(length), comp, tag.get_num_threads());
    else
      return sequential_multiway_merge
                      </* stable = */ false, /* sentinels = */ false>(
          seqs_begin, seqs_end,
          target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , parallel_tag tag = parallel_tag(0))
{
  return multiway_merge(seqs_begin, seqs_end, target, length, comp,
                         exact_tag(tag.get_num_threads()));
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , default_parallel_tag tag)
{
  return multiway_merge(seqs_begin, seqs_end, target, length, comp,
                         exact_tag(tag.get_num_threads()));
}

// stable_multiway_merge
// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , __gnu_parallel::sequential_tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute multiway merge *sequentially*.
    return sequential_multiway_merge
      </* stable = */ true, /* sentinels = */ false>
        (seqs_begin, seqs_end, target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , __gnu_parallel::exact_tag tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute merge; maybe parallel, depending on the number of merged
    // elements and the number of sequences and global thresholds in
    // Settings.
    if ((seqs_end - seqs_begin > 1) &&
          _GLIBCXX_PARALLEL_CONDITION(
          ((seqs_end - seqs_begin) >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_k)
          && ((sequence_index_t)length >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_n)))
      return parallel_multiway_merge
        </* stable = */ true, /* sentinels = */ false>(
          seqs_begin, seqs_end,
          target,
          multiway_merge_exact_splitting</* stable = */ true,
            typename std::iterator_traits<RandomAccessIteratorPairIterator>
              ::value_type*, Comparator, _DifferenceTp>,
          static_cast<difference_type>(length), comp, tag.get_num_threads());
    else
      return sequential_multiway_merge</* stable = */ true,
        /* sentinels = */ false>(
          seqs_begin, seqs_end,
          target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , sampling_tag tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute merge; maybe parallel, depending on the number of merged
    // elements and the number of sequences and global thresholds in
    // Settings.
    if ((seqs_end - seqs_begin > 1) &&
          _GLIBCXX_PARALLEL_CONDITION(
          ((seqs_end - seqs_begin) >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_k)
          && ((sequence_index_t)length >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_n)))
      return parallel_multiway_merge
        </* stable = */ true, /* sentinels = */ false>(
          seqs_begin, seqs_end,
          target,
          multiway_merge_sampling_splitting</* stable = */ true,
            typename std::iterator_traits<RandomAccessIteratorPairIterator>
              ::value_type*, Comparator, _DifferenceTp>,
          static_cast<difference_type>(length), comp, tag.get_num_threads());
    else
      return sequential_multiway_merge
        </* stable = */ true, /* sentinels = */ false>(
          seqs_begin, seqs_end,
          target, *(seqs_begin->second), length, comp);
}


// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , parallel_tag tag = parallel_tag(0))
{
  return stable_multiway_merge(seqs_begin, seqs_end, target, length, comp,
                         exact_tag(tag.get_num_threads()));
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , default_parallel_tag tag)
{
  return stable_multiway_merge(seqs_begin, seqs_end, target, length, comp,
                         exact_tag(tag.get_num_threads()));
}

/**
 * @brief Multiway Merge Frontend.
 *
 * Merge the sequences specified by seqs_begin and seqs_end into
 * target.  seqs_begin and seqs_end must point to a sequence of
 * pairs.  These pairs must contain an iterator to the beginning
 * of a sequence in their first entry and an iterator the end of
 * the same sequence in their second entry.
 *
 * Ties are broken arbitrarily.  See stable_multiway_merge for a variant
 * that breaks ties by sequence number but is slower.
 *
 * The first entries of the pairs (i.e. the begin iterators) will be moved
 * forward accordingly.
 *
 * The output sequence has to provide enough space for all elements
 * that are written to it.
 *
 * This function will merge the input sequences:
 *
 * - not stable
 * - parallel, depending on the input size and Settings
 * - using sampling for splitting
 * - using sentinels
 *
 * You have to take care that the element the end iterator points to is
 * readable and contains a value that is greater than any other non-sentinel
 * value in all sequences.
 *
 * Example:
 *
 * <pre>
 *   int sequences[10][11];
 *   for (int i = 0; i < 10; ++i)
 *     for (int j = 0; i < 11; ++j)
 *       sequences[i][j] = j; // last one is sentinel!
 *
 *   int out[33];
 *   std::vector<std::pair<int*> > seqs;
 *   for (int i = 0; i < 10; ++i)
 *     { seqs.push(std::make_pair<int*>(sequences[i], sequences[i] + 10)) }
 *
 *   multiway_merge(seqs.begin(), seqs.end(), target, std::less<int>(), 33);
 * </pre>
 *
 * @pre All input sequences must be sorted.
 * @pre Target must provide enough space to merge out length elements or
 *    the number of elements in all sequences, whichever is smaller.
 * @pre For each @c i, @c seqs_begin[i].second must be the end
 *    marker of the sequence, but also reference the one more sentinel
 *    element.
 *
 * @post [target, return value) contains merged elements from the
 *    input sequences.
 * @post return value - target = min(length, number of elements in all
 *    sequences).
 *
 * @see stable_multiway_merge_sentinels
 *
 * @param RandomAccessIteratorPairIterator iterator over sequence
 *    of pairs of iterators
 * @param RandomAccessIteratorOut iterator over target sequence
 * @param _DifferenceTp difference type for the sequence
 * @param Comparator strict weak ordering type to compare elements
 *    in sequences
 *
 * @param seqs_begin  begin of sequence sequence
 * @param seqs_end    end of sequence sequence
 * @param target      target sequence to merge to.
 * @param comp        strict weak ordering to use for element comparison.
 * @param length Maximum length to merge, possibly larger than the
 * number of elements available.
 *
 * @return end iterator of output sequence
 */
// multiway_merge_sentinels
// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , __gnu_parallel::sequential_tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute multiway merge *sequentially*.
    return sequential_multiway_merge
      </* stable = */ false, /* sentinels = */ true>
        (seqs_begin, seqs_end,
         target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , __gnu_parallel::exact_tag tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute merge; maybe parallel, depending on the number of merged
    // elements and the number of sequences and global thresholds in
    // Settings.
    if ((seqs_end - seqs_begin > 1) &&
          _GLIBCXX_PARALLEL_CONDITION(
          ((seqs_end - seqs_begin) >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_k)
          && ((sequence_index_t)length >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_n)))
      return parallel_multiway_merge
        </* stable = */ false, /* sentinels = */ true>(
          seqs_begin, seqs_end,
          target,
          multiway_merge_exact_splitting</* stable = */ false,
            typename std::iterator_traits<RandomAccessIteratorPairIterator>
              ::value_type*, Comparator, _DifferenceTp>,
          static_cast<difference_type>(length), comp, tag.get_num_threads());
    else
      return sequential_multiway_merge
        </* stable = */ false, /* sentinels = */ true>(
          seqs_begin, seqs_end,
          target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , sampling_tag tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute merge; maybe parallel, depending on the number of merged
    // elements and the number of sequences and global thresholds in
    // Settings.
    if ((seqs_end - seqs_begin > 1) &&
          _GLIBCXX_PARALLEL_CONDITION(
          ((seqs_end - seqs_begin) >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_k)
          && ((sequence_index_t)length >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_n)))
      return parallel_multiway_merge
        </* stable = */ false, /* sentinels = */ true>
          (seqs_begin, seqs_end, target,
          multiway_merge_sampling_splitting</* stable = */ false,
            typename std::iterator_traits<RandomAccessIteratorPairIterator>
              ::value_type*, Comparator, _DifferenceTp>,
          static_cast<difference_type>(length), comp, tag.get_num_threads());
    else
      return sequential_multiway_merge
        </* stable = */false, /* sentinels = */ true>(
          seqs_begin, seqs_end,
          target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , parallel_tag tag = parallel_tag(0))
{
  return multiway_merge_sentinels(seqs_begin, seqs_end, target, length, comp,
                         exact_tag(tag.get_num_threads()));
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , default_parallel_tag tag)
{
  return multiway_merge_sentinels(seqs_begin, seqs_end, target, length, comp,
                         exact_tag(tag.get_num_threads()));
}

// stable_multiway_merge_sentinels
// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , __gnu_parallel::sequential_tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute multiway merge *sequentially*.
    return sequential_multiway_merge
      </* stable = */ true, /* sentinels = */ true>
        (seqs_begin, seqs_end, target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , __gnu_parallel::exact_tag tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute merge; maybe parallel, depending on the number of merged
    // elements and the number of sequences and global thresholds in
    // Settings.
    if ((seqs_end - seqs_begin > 1) &&
          _GLIBCXX_PARALLEL_CONDITION(
          ((seqs_end - seqs_begin) >=
          __gnu_parallel::_Settings::get().multiway_merge_minimal_k)
          && ((sequence_index_t)length >=
          __gnu_parallel::_Settings::get().multiway_merge_minimal_n)))
      return parallel_multiway_merge
        </* stable = */ true, /* sentinels = */ true>(
          seqs_begin, seqs_end,
          target,
          multiway_merge_exact_splitting</* stable = */ true,
            typename std::iterator_traits<RandomAccessIteratorPairIterator>
              ::value_type*, Comparator, _DifferenceTp>,
          static_cast<difference_type>(length), comp, tag.get_num_threads());
    else
      return sequential_multiway_merge
        </* stable = */ true, /* sentinels = */ true>(
          seqs_begin, seqs_end, target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , sampling_tag tag)
{
    typedef _DifferenceTp difference_type;
    _GLIBCXX_CALL(seqs_end - seqs_begin)

    // catch special case: no sequences
    if (seqs_begin == seqs_end)
      return target;

    // Execute merge; maybe parallel, depending on the number of merged
    // elements and the number of sequences and global thresholds in
    // Settings.
    if ((seqs_end - seqs_begin > 1) &&
          _GLIBCXX_PARALLEL_CONDITION(
          ((seqs_end - seqs_begin) >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_k)
          && ((sequence_index_t)length >=
            __gnu_parallel::_Settings::get().multiway_merge_minimal_n)))
      return parallel_multiway_merge
        </* stable = */ true, /* sentinels = */ true>(
          seqs_begin, seqs_end,
          target,
          multiway_merge_sampling_splitting</* stable = */ true,
            typename std::iterator_traits<RandomAccessIteratorPairIterator>
              ::value_type*, Comparator, _DifferenceTp>,
          static_cast<difference_type>(length), comp, tag.get_num_threads());
    else
      return sequential_multiway_merge
        </* stable = */ true, /* sentinels = */ true>(
          seqs_begin, seqs_end,
          target, *(seqs_begin->second), length, comp);
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , parallel_tag tag = parallel_tag(0))
{
  return stable_multiway_merge_sentinels(seqs_begin, seqs_end, target, length, comp,
                         exact_tag(tag.get_num_threads()));
}

// public interface
template<
    typename RandomAccessIteratorPairIterator
  , typename RandomAccessIteratorOut
  , typename _DifferenceTp
  , typename Comparator>
RandomAccessIteratorOut
stable_multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin
    , RandomAccessIteratorPairIterator seqs_end
    , RandomAccessIteratorOut target
    , _DifferenceTp length, Comparator comp
    , default_parallel_tag tag)
{
  return stable_multiway_merge_sentinels(seqs_begin, seqs_end, target, length, comp,
                         exact_tag(tag.get_num_threads()));
}

}; // namespace __gnu_parallel

#endif /* _GLIBCXX_PARALLEL_MULTIWAY_MERGE_H */
