1904bef10a
* docs/html/parallel_mode.html: Added reference to MCSTL. More documentation on compile-time settings and tuning. * include/parallel/multiway_merge.h: Added reference to paper. * include/parallel/multiseq_selection.h: Added reference to paper. * include/parallel/workstealing.h: Added reference to paper. * include/parallel/balanced_quicksort.h: Added reference to paper. * include/parallel/tree.h: Added reference to paper. From-SVN: r129129
3631 lines
129 KiB
C++
3631 lines
129 KiB
C++
// -*- C++ -*-
|
|
|
|
// Copyright (C) 2007 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 2, 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.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this library; see the file COPYING. If not, write to
|
|
// the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
|
|
// MA 02111-1307, USA.
|
|
|
|
// As a special exception, you may use this file as part of a free
|
|
// software library without restriction. Specifically, if other files
|
|
// instantiate templates or use macros or inline functions from this
|
|
// file, or you compile this file and link it with other files to
|
|
// produce an executable, this file does not by itself cause the
|
|
// resulting executable to be covered by the GNU General Public
|
|
// License. This exception does not however invalidate any other
|
|
// reasons why the executable file might be covered by the GNU General
|
|
// Public License.
|
|
|
|
/** @file parallel/tree.h
|
|
* @brief Parallel red-black tree operations.
|
|
*
|
|
* This implementation is described in
|
|
*
|
|
* Leonor Frias, Johannes Singler.
|
|
* Parallelization of Bulk Operations for STL Dictionaries.
|
|
* Workshop on Highly Parallel Processing on a Chip (HPPC) 2007.
|
|
*
|
|
* This file is a GNU parallel extension to the Standard C++ Library.
|
|
*/
|
|
|
|
// Written by Leonor Frias Moya, Johannes Singler.
|
|
|
|
#ifndef _GLIBCXX_PARALLEL_TREE_H
|
|
#define _GLIBCXX_PARALLEL_TREE_H 1
|
|
|
|
#include <parallel/parallel.h>
|
|
#include <functional>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <functional>
|
|
#include <iostream>
|
|
//#include <ext/malloc_allocator.h>
|
|
#include <bits/stl_tree.h>
|
|
|
|
#include <parallel/list_partition.h>
|
|
|
|
//#define _GLIBCXX_TIMING
|
|
#ifdef _GLIBCXX_TIMING
|
|
#define _timing_tag parallel_tag
|
|
#else
|
|
#define _timing_tag sequential_tag
|
|
#endif
|
|
|
|
namespace std
|
|
{
|
|
// XXX Declaration should go to stl_tree.h.
|
|
void
|
|
_Rb_tree_rotate_left(_Rb_tree_node_base* const __x,
|
|
_Rb_tree_node_base*& __root);
|
|
|
|
void
|
|
_Rb_tree_rotate_right(_Rb_tree_node_base* const __x,
|
|
_Rb_tree_node_base*& __root);
|
|
}
|
|
|
|
|
|
namespace __gnu_parallel
|
|
{
|
|
// XXX move into parallel/type_traits.h if <type_traits> doesn't work.
|
|
/** @brief Helper class: remove the const modifier from the first
|
|
component, if present. Set kind component.
|
|
* @param T Simple type, nothing to unconst */
|
|
template<typename T>
|
|
struct unconst_first_component
|
|
{
|
|
/** @brief New type after removing the const */
|
|
typedef T type;
|
|
};
|
|
|
|
/** @brief Helper class: remove the const modifier from the first
|
|
component, if present. Map kind component
|
|
* @param Key First component, from which to remove the const modifier
|
|
* @param Load Second component
|
|
* @sa unconst_first_component */
|
|
template<typename Key, typename Load>
|
|
struct unconst_first_component<std::pair<const Key, Load> >
|
|
{
|
|
/** @brief New type after removing the const */
|
|
typedef std::pair<Key, Load> type;
|
|
};
|
|
|
|
/** @brief Helper class: set the appropriate comparator to deal with
|
|
* repetitions. Comparator for unique dictionaries.
|
|
*
|
|
* StrictlyLess and LessEqual are part of a mechanism to deal with
|
|
* repetitions transparently whatever the actual policy is.
|
|
* @param _Key Keys to compare
|
|
* @param _Compare Comparator equal to conceptual < */
|
|
template<typename _Key, typename _Compare>
|
|
struct StrictlyLess : public std::binary_function<_Key, _Key, bool>
|
|
{
|
|
/** @brief Comparator equal to conceptual < */
|
|
_Compare c;
|
|
|
|
/** @brief Constructor given a Comparator */
|
|
StrictlyLess(const _Compare& _c) : c(_c) { }
|
|
|
|
/** @brief Copy constructor */
|
|
StrictlyLess(const StrictlyLess<_Key, _Compare>& strictly_less)
|
|
: c(strictly_less.c) { }
|
|
|
|
/** @brief Operator() */
|
|
bool operator()(const _Key& k1, const _Key& k2) const
|
|
{
|
|
return c(k1, k2);
|
|
}
|
|
};
|
|
|
|
/** @brief Helper class: set the appropriate comparator to deal with
|
|
* repetitions. Comparator for non-unique dictionaries.
|
|
*
|
|
* StrictlyLess and LessEqual are part of a mechanism to deal with
|
|
* repetitions transparently whatever the actual policy is.
|
|
* @param _Key Keys to compare
|
|
* @param _Compare Comparator equal to conceptual <= */
|
|
template<typename _Key, typename _Compare>
|
|
struct LessEqual : public std::binary_function<_Key, _Key, bool>
|
|
{
|
|
/** @brief Comparator equal to conceptual < */
|
|
_Compare c;
|
|
|
|
/** @brief Constructor given a Comparator */
|
|
LessEqual(const _Compare& _c) : c(_c) { }
|
|
|
|
/** @brief Copy constructor */
|
|
LessEqual(const LessEqual<_Key, _Compare>& less_equal)
|
|
: c(less_equal.c) { }
|
|
|
|
/** @brief Operator() */
|
|
bool operator()(const _Key& k1, const _Key& k2) const
|
|
{ return !c(k2, k1); }
|
|
};
|
|
|
|
|
|
/** @brief Parallel red-black tree.
|
|
*
|
|
* Extension of the sequential red-black tree. Specifically,
|
|
* parallel bulk insertion operations are provided.
|
|
* @param _Key Keys to compare
|
|
* @param _Val Elements to store in the tree
|
|
* @param _KeyOfValue Obtains the key from an element <
|
|
* @param _Compare Comparator equal to conceptual <
|
|
* @param _Alloc Allocator for the elements */
|
|
template<typename _Key, typename _Val, typename _KeyOfValue,
|
|
typename _Compare, typename _Alloc = std::allocator<_Val> >
|
|
class _Rb_tree
|
|
: public std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>
|
|
{
|
|
private:
|
|
/** @brief Sequential tree */
|
|
typedef std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc> base_type;
|
|
|
|
/** @brief Renaming of base node type */
|
|
typedef typename std::_Rb_tree_node<_Val> _Rb_tree_node;
|
|
|
|
/** @brief Renaming of libstdc++ node type */
|
|
typedef typename std::_Rb_tree_node_base _Rb_tree_node_base;
|
|
|
|
/** @brief Renaming of base key_type */
|
|
typedef typename base_type::key_type key_type;
|
|
|
|
/** @brief Renaming of base value_type */
|
|
typedef typename base_type::value_type value_type;
|
|
|
|
/** @brief Helper class to unconst the first component of
|
|
* value_type if exists.
|
|
*
|
|
* This helper class is needed for map, but may discard qualifiers
|
|
* for set; however, a set with a const element type is not useful
|
|
* and should fail in some other place anyway.
|
|
*/
|
|
typedef typename unconst_first_component<value_type>::type nc_value_type;
|
|
|
|
/** @brief Pointer to a node */
|
|
typedef _Rb_tree_node* _Rb_tree_node_ptr;
|
|
|
|
/** @brief Wrapper comparator class to deal with repetitions
|
|
transparently according to dictionary type with key _Key and
|
|
comparator _Compare. Unique dictionaries object
|
|
*/
|
|
StrictlyLess<_Key, _Compare> strictly_less;
|
|
|
|
/** @brief Wrapper comparator class to deal with repetitions
|
|
transparently according to dictionary type with key _Key and
|
|
comparator _Compare. Non-unique dictionaries object
|
|
*/
|
|
LessEqual<_Key, _Compare> less_equal;
|
|
|
|
public:
|
|
/** @brief Renaming of base size_type */
|
|
typedef typename base_type::size_type size_type;
|
|
|
|
/** @brief Constructor with a given comparator and allocator.
|
|
*
|
|
* Delegates the basic initialization to the sequential class and
|
|
* initializes the helper comparators of the parallel class
|
|
* @param c Comparator object with which to initialize the class
|
|
* comparator and the helper comparators
|
|
* @param a Allocator object with which to initialize the class comparator
|
|
*/
|
|
_Rb_tree(const _Compare& c, const _Alloc& a)
|
|
: base_type(c, a), strictly_less(base_type::_M_impl._M_key_compare),
|
|
less_equal(base_type::_M_impl._M_key_compare)
|
|
{ }
|
|
|
|
/** @brief Copy constructor.
|
|
*
|
|
* Delegates the basic initialization to the sequential class and
|
|
* initializes the helper comparators of the parallel class
|
|
* @param __x Parallel red-black instance to copy
|
|
*/
|
|
_Rb_tree(const _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>& __x)
|
|
: base_type(__x), strictly_less(base_type::_M_impl._M_key_compare),
|
|
less_equal(base_type::_M_impl._M_key_compare)
|
|
{ }
|
|
|
|
/** @brief Parallel replacement of the sequential
|
|
* std::_Rb_tree::_M_insert_unique()
|
|
*
|
|
* Parallel bulk insertion and construction. If the container is
|
|
* empty, bulk construction is performed. Otherwise, bulk
|
|
* insertion is performed
|
|
* @param __first First element of the input
|
|
* @param __last Last element of the input
|
|
*/
|
|
template<typename _InputIterator>
|
|
void
|
|
_M_insert_unique(_InputIterator __first, _InputIterator __last)
|
|
{
|
|
if (__first==__last) return;
|
|
if (_GLIBCXX_PARALLEL_CONDITION(true))
|
|
if (base_type::_M_impl._M_node_count == 0)
|
|
{
|
|
_M_bulk_insertion_construction(__first, __last, true,
|
|
strictly_less);
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify());
|
|
}
|
|
else
|
|
{
|
|
_M_bulk_insertion_construction(__first, __last, false,
|
|
strictly_less);
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify());
|
|
}
|
|
else
|
|
{
|
|
base_type::_M_insert_unique(__first, __last);
|
|
}
|
|
}
|
|
|
|
/** @brief Parallel replacement of the sequential
|
|
* std::_Rb_tree::_M_insert_equal()
|
|
*
|
|
* Parallel bulk insertion and construction. If the container is
|
|
* empty, bulk construction is performed. Otherwise, bulk
|
|
* insertion is performed
|
|
* @param __first First element of the input
|
|
* @param __last Last element of the input */
|
|
template<typename _InputIterator>
|
|
void
|
|
_M_insert_equal(_InputIterator __first, _InputIterator __last)
|
|
{
|
|
if (__first==__last) return;
|
|
if (_GLIBCXX_PARALLEL_CONDITION(true))
|
|
if (base_type::_M_impl._M_node_count == 0)
|
|
_M_bulk_insertion_construction(__first, __last, true, less_equal);
|
|
else
|
|
_M_bulk_insertion_construction(__first, __last, false, less_equal);
|
|
else
|
|
base_type::_M_insert_equal(__first, __last);
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify());
|
|
}
|
|
|
|
private:
|
|
|
|
/** @brief Helper class of _Rb_tree: node linking.
|
|
*
|
|
* Nodes linking forming an almost complete tree. The last level
|
|
* is coloured red, the rest are black
|
|
* @param ranker Calculates the position of a node in an array of nodes
|
|
*/
|
|
template<typename ranker>
|
|
class nodes_initializer
|
|
{
|
|
/** @brief Renaming of tree size_type */
|
|
|
|
typedef _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc> tree_type;
|
|
typedef typename tree_type::size_type size_type;
|
|
public:
|
|
|
|
/** @brief mask[%i]= 0..01..1, where the number of 1s is %i+1 */
|
|
size_type mask[sizeof(size_type)*8];
|
|
|
|
/** @brief Array of nodes (initial address) */
|
|
const _Rb_tree_node_ptr* r_init;
|
|
|
|
/** @brief Total number of (used) nodes */
|
|
size_type n;
|
|
|
|
/** @brief Rank of the last tree node that can be calculated
|
|
taking into account a complete tree
|
|
*/
|
|
size_type splitting_point;
|
|
|
|
/** @brief Rank of the tree root */
|
|
size_type rank_root;
|
|
|
|
/** @brief Height of the tree */
|
|
int height;
|
|
|
|
/** @brief Number of threads into which divide the work */
|
|
const thread_index_t num_threads;
|
|
|
|
/** @brief Helper object to mind potential gaps in r_init */
|
|
const ranker& rank;
|
|
|
|
/** @brief Constructor
|
|
* @param r Array of nodes
|
|
* @param _n Total number of (used) nodes
|
|
* @param _num_threads Number of threads into which divide the work
|
|
* @param _rank Helper object to mind potential gaps in @c r_init */
|
|
nodes_initializer(const _Rb_tree_node_ptr* r, const size_type _n,
|
|
const thread_index_t _num_threads, const ranker& _rank):
|
|
r_init(r),
|
|
n(_n),
|
|
num_threads(_num_threads),
|
|
rank(_rank)
|
|
{
|
|
height = log2(n);
|
|
splitting_point = 2 * (n - ((1 << height) - 1)) -1;
|
|
|
|
// Rank root.
|
|
size_type max = 1 << (height + 1);
|
|
rank_root= (max-2) >> 1;
|
|
if (rank_root > splitting_point)
|
|
rank_root = complete_to_original(rank_root);
|
|
|
|
mask[0] = 0x1;
|
|
for (unsigned int i = 1; i < sizeof(size_type)*8; ++i)
|
|
{
|
|
mask[i] = (mask[i-1] << 1) + 1;
|
|
}
|
|
}
|
|
|
|
/** @brief Query for tree height
|
|
* @return Tree height */
|
|
int
|
|
get_height() const
|
|
{ return height; }
|
|
|
|
/** @brief Query for the splitting point
|
|
* @return Splitting point */
|
|
size_type
|
|
get_shifted_splitting_point() const
|
|
{ return rank.get_shifted_rank(splitting_point, 0); }
|
|
|
|
/** @brief Query for the tree root node
|
|
* @return Tree root node */
|
|
_Rb_tree_node_ptr
|
|
get_root() const
|
|
{ return r_init[rank.get_shifted_rank(rank_root,num_threads/2)]; }
|
|
|
|
/** @brief Calculation of the parent position in the array of nodes
|
|
* @hideinitializer */
|
|
#define CALCULATE_PARENT \
|
|
if (p_s> splitting_point) \
|
|
p_s = complete_to_original(p_s); \
|
|
int s_r = rank.get_shifted_rank(p_s,iam); \
|
|
r->_M_parent = r_init[s_r]; \
|
|
\
|
|
/** @brief Link a node with its parent and children taking into
|
|
account that its rank (without gaps) is different to that in
|
|
a complete tree
|
|
* @param r Pointer to the node
|
|
* @param iam Partition of the array in which the node is, where
|
|
* iam is in [0..num_threads)
|
|
* @sa link_complete */
|
|
void
|
|
link_incomplete(const _Rb_tree_node_ptr& r, const int iam) const
|
|
{
|
|
size_type real_pos = rank.get_real_rank(&r-r_init, iam);
|
|
size_type l_s, r_s, p_s;
|
|
int mod_pos= original_to_complete(real_pos);
|
|
int zero= first_0_right(mod_pos);
|
|
|
|
// 1. Convert n to n', where n' will be its rank if the tree
|
|
// was complete
|
|
// 2. Calculate neighbours for n'
|
|
// 3. Convert the neighbors n1', n2' and n3' to their
|
|
// appropriate values n1, n2, n3. Note that it must be
|
|
// checked that these neighbors actually exist.
|
|
calculate_shifts_pos_level(mod_pos, zero, l_s, r_s, p_s);
|
|
if (l_s > splitting_point)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(r_s > splitting_point);
|
|
if (zero == 1)
|
|
{
|
|
r->_M_left = 0;
|
|
r->_M_right = 0;
|
|
}
|
|
else
|
|
{
|
|
r->_M_left= r_init[rank.get_shifted_rank(complete_to_original(l_s),iam)];
|
|
r->_M_right= r_init[rank.get_shifted_rank(complete_to_original(r_s),iam)];
|
|
}
|
|
|
|
}
|
|
else{
|
|
r->_M_left= r_init[rank.get_shifted_rank(l_s,iam)];
|
|
if (zero != 1)
|
|
{
|
|
r->_M_right= r_init[rank.get_shifted_rank(complete_to_original(r_s),iam)];
|
|
}
|
|
else
|
|
{
|
|
r->_M_right = 0;
|
|
}
|
|
}
|
|
r->_M_color = std::_S_black;
|
|
CALCULATE_PARENT;
|
|
}
|
|
|
|
/** @brief Link a node with its parent and children taking into
|
|
account that its rank (without gaps) is the same as that in
|
|
a complete tree
|
|
* @param r Pointer to the node
|
|
* @param iam Partition of the array in which the node is, where
|
|
* iam is in [0..@c num_threads)
|
|
* @sa link_incomplete
|
|
*/
|
|
void
|
|
link_complete(const _Rb_tree_node_ptr& r, const int iam) const
|
|
{
|
|
size_type real_pos = rank.get_real_rank(&r-r_init, iam);
|
|
size_type p_s;
|
|
|
|
// Test if it is a leaf on the last not necessarily full level
|
|
if ((real_pos & mask[0]) == 0)
|
|
{
|
|
if ((real_pos & 0x2) == 0)
|
|
p_s = real_pos + 1;
|
|
else
|
|
p_s = real_pos - 1;
|
|
r->_M_color = std::_S_red;
|
|
r->_M_left = 0;
|
|
r->_M_right = 0;
|
|
}
|
|
else
|
|
{
|
|
size_type l_s, r_s;
|
|
int zero = first_0_right(real_pos);
|
|
calculate_shifts_pos_level(real_pos, zero, l_s, r_s, p_s);
|
|
r->_M_color = std::_S_black;
|
|
|
|
r->_M_left = r_init[rank.get_shifted_rank(l_s,iam)];
|
|
if (r_s > splitting_point)
|
|
r_s = complete_to_original(r_s);
|
|
r->_M_right = r_init[rank.get_shifted_rank(r_s,iam)];
|
|
}
|
|
CALCULATE_PARENT;
|
|
}
|
|
|
|
#undef CALCULATE_PARENT
|
|
|
|
private:
|
|
/** @brief Change of "base": Convert the rank in the actual tree
|
|
into the corresponding rank if the tree was complete
|
|
* @param pos Rank in the actual incomplete tree
|
|
* @return Rank in the corresponding complete tree
|
|
* @sa complete_to_original */
|
|
int
|
|
original_to_complete(const int pos) const
|
|
{ return (pos << 1) - splitting_point; }
|
|
|
|
/** @brief Change of "base": Convert the rank if the tree was
|
|
complete into the corresponding rank in the actual tree
|
|
* @param pos Rank in the complete tree
|
|
* @return Rank in the actual incomplete tree
|
|
* @sa original_to_complete */
|
|
int
|
|
complete_to_original(const int pos) const
|
|
{ return (pos + splitting_point) >> 1; }
|
|
|
|
|
|
/** @brief Calculate the rank in the complete tree of the parent
|
|
and children of a node
|
|
* @param pos Rank in the complete tree of the node whose parent
|
|
* and children rank must be calculated
|
|
* @param level Tree level in which the node at pos is in
|
|
* (starting to count at leaves). @pre @c level > 1
|
|
* @param left_shift Rank in the complete tree of the left child
|
|
* of pos (out parameter)
|
|
* @param right_shift Rank in the complete tree of the right
|
|
* child of pos (out parameter)
|
|
* @param parent_shift Rank in the complete tree of the parent
|
|
* of pos (out parameter)
|
|
*/
|
|
void
|
|
calculate_shifts_pos_level(const size_type pos, const int level,
|
|
size_type& left_shift, size_type& right_shift,
|
|
size_type& parent_shift) const
|
|
{
|
|
int stride = 1 << (level -1);
|
|
left_shift = pos - stride;
|
|
right_shift = pos + stride;
|
|
if (((pos >> (level + 1)) & 0x1) == 0)
|
|
parent_shift = pos + 2*stride;
|
|
else
|
|
parent_shift = pos - 2*stride;
|
|
}
|
|
|
|
/** @brief Search for the first 0 bit (growing the weight)
|
|
* @param x Binary number (corresponding to a rank in the tree)
|
|
* whose first 0 bit must be calculated
|
|
* @return Position of the first 0 bit in @c x (starting to
|
|
* count with 1)
|
|
*/
|
|
int
|
|
first_0_right(const size_type x) const
|
|
{
|
|
if ((x & 0x2) == 0)
|
|
return 1;
|
|
else
|
|
return first_0_right_bs(x);
|
|
}
|
|
|
|
/** @brief Search for the first 0 bit (growing the weight) using
|
|
* binary search
|
|
*
|
|
* Binary search can be used instead of a naive loop using the
|
|
* masks in mask array
|
|
* @param x Binary number (corresponding to a rank in the tree)
|
|
* whose first 0 bit must be calculated
|
|
* @param k_beg Position in which to start searching. By default is 2.
|
|
* @return Position of the first 0 bit in x (starting to count with 1) */
|
|
int
|
|
first_0_right_bs(const size_type x, int k_beg=2) const
|
|
{
|
|
int k_end = sizeof(size_type)*8;
|
|
size_type not_x = x ^ mask[k_end-1];
|
|
while ((k_end-k_beg) > 1)
|
|
{
|
|
int k = k_beg + (k_end-k_beg)/2;
|
|
if ((not_x & mask[k-1]) != 0)
|
|
k_end = k;
|
|
else
|
|
k_beg = k;
|
|
}
|
|
return k_beg;
|
|
}
|
|
};
|
|
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
/** @brief Helper class of nodes_initializer: mind the gaps of an
|
|
array of nodes.
|
|
*
|
|
* Get absolute positions in an array of nodes taking into account
|
|
* the gaps in it @sa ranker_no_gaps
|
|
*/
|
|
class ranker_gaps
|
|
{
|
|
/** @brief Renaming of tree's size_type */
|
|
typedef _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc> tree_type;
|
|
typedef typename tree_type::size_type size_type;
|
|
|
|
/** @brief Array containing the beginning ranks of all the
|
|
num_threads partitions just considering the valid nodes, not
|
|
the gaps */
|
|
size_type* beg_partition;
|
|
|
|
/** @brief Array containing the beginning ranks of all the
|
|
num_threads partitions considering the valid nodes and the
|
|
gaps */
|
|
const size_type* beg_shift_partition;
|
|
|
|
/** @brief Array containing the number of accumulated gaps at
|
|
the beginning of each partition */
|
|
const size_type* rank_shift;
|
|
|
|
/** @brief Number of partitions (and threads that work on it) */
|
|
const thread_index_t num_threads;
|
|
|
|
public:
|
|
/** @brief Constructor
|
|
* @param size_p Pointer to the array containing the beginning
|
|
* ranks of all the @c _num_threads partitions considering the
|
|
* valid nodes and the gaps
|
|
* @param shift_r Array containing the number of accumulated
|
|
* gaps at the beginning of each partition
|
|
* @param _num_threads Number of partitions (and threads that
|
|
* work on it) */
|
|
ranker_gaps(const size_type* size_p, const size_type* shift_r,
|
|
const thread_index_t _num_threads) :
|
|
beg_shift_partition(size_p),
|
|
rank_shift(shift_r),
|
|
num_threads(_num_threads)
|
|
{
|
|
beg_partition = new size_type[num_threads+1];
|
|
beg_partition[0] = 0;
|
|
for (int i = 1; i <= num_threads; ++i)
|
|
{
|
|
beg_partition[i] = beg_partition[i-1] + (beg_shift_partition[i] - beg_shift_partition[i-1]) - (rank_shift[i] - rank_shift[i-1]);
|
|
|
|
}
|
|
|
|
// Ghost element, strictly larger than any index requested.
|
|
++beg_partition[num_threads];
|
|
}
|
|
|
|
/** @brief Destructor
|
|
* Needs to be defined to deallocate the dynamic memory that has
|
|
* been allocated for beg_partition array
|
|
*/
|
|
~ranker_gaps()
|
|
{ delete[] beg_partition; }
|
|
|
|
/** @brief Convert a rank in the array of nodes considering
|
|
valid nodes and gaps, to the corresponding considering only
|
|
the valid nodes
|
|
* @param pos Rank in the array of nodes considering valid nodes and gaps
|
|
* @param index Partition which the rank belongs to
|
|
* @return Rank in the array of nodes considering only the valid nodes
|
|
* @sa get_shifted_rank
|
|
*/
|
|
size_type
|
|
get_real_rank(const size_type pos, const int index) const
|
|
{ return pos - rank_shift[index]; }
|
|
|
|
/** @brief Inverse of get_real_rank: Convert a rank in the array
|
|
of nodes considering only valid nodes, to the corresponding
|
|
considering valid nodes and gaps
|
|
* @param pos Rank in the array of nodes considering only valid nodes
|
|
* @param index Partition which the rank is most likely to
|
|
* belong to (i. e. the corresponding if there were no gaps)
|
|
* @pre 0 <= @c pos <= number_of_distinct_elements
|
|
* @return Rank in the array of nodes considering valid nodes and gaps
|
|
* @post 0 <= @c return <= number_of_elements
|
|
* @sa get_real_rank()
|
|
*/
|
|
size_type
|
|
get_shifted_rank(const size_type pos, const int index) const
|
|
{
|
|
// Heuristic.
|
|
if (beg_partition[index] <= pos and pos < beg_partition[index+1])
|
|
return pos + rank_shift[index];
|
|
else
|
|
// Called rarely, do not hinder inlining.
|
|
return get_shifted_rank_loop(pos,index);
|
|
}
|
|
|
|
/** @brief Helper method of get_shifted_rank: in case the given
|
|
index in get_shifted_rank is not correct, look for it and
|
|
then calculate the rank
|
|
* @param pos Rank in the array of nodes considering only valid nodes
|
|
* @param index Partition which the rank should have belong to
|
|
* if there were no gaps
|
|
* @return Rank in the array of nodes considering valid nodes and gaps
|
|
*/
|
|
size_type
|
|
get_shifted_rank_loop(const size_type pos, int index) const
|
|
{
|
|
while (pos >= beg_partition[index+1])
|
|
++index;
|
|
while (pos < beg_partition[index])
|
|
--index;
|
|
_GLIBCXX_PARALLEL_ASSERT(0 <= index && index < num_threads);
|
|
return pos + rank_shift[index];
|
|
}
|
|
};
|
|
|
|
/** @brief Helper class of nodes_initializer: access an array of
|
|
* nodes with no gaps
|
|
*
|
|
* Get absolute positions in an array of nodes taking into account
|
|
* that there are no gaps in it. @sa ranker_gaps */
|
|
class ranker_no_gaps
|
|
{
|
|
/** @brief Renaming of tree's size_type */
|
|
typedef _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc> tree_type;
|
|
typedef typename tree_type::size_type size_type;
|
|
|
|
public:
|
|
/** @brief Convert a rank in the array of nodes considering
|
|
* valid nodes and gaps, to the corresponding considering only
|
|
* the valid nodes
|
|
*
|
|
* As there are no gaps in this case, get_shifted_rank() and
|
|
* get_real_rank() are synonyms and make no change on pos
|
|
* @param pos Rank in the array of nodes considering valid nodes and gaps
|
|
* @param index Partition which the rank belongs to, unused here
|
|
* @return Rank in the array of nodes considering only the valid nodes */
|
|
size_type
|
|
get_real_rank(const size_type pos, const int index) const
|
|
{ return pos; }
|
|
|
|
/** @brief Inverse of get_real_rank: Convert a rank in the array
|
|
* of nodes considering only valid nodes, to the corresponding
|
|
* considering valid nodes and gaps
|
|
*
|
|
* As there are no gaps in this case, get_shifted_rank() and
|
|
* get_real_rank() are synonyms and make no change on pos
|
|
* @param pos Rank in the array of nodes considering only valid nodes
|
|
* @param index Partition which the rank belongs to, unused here
|
|
* @return Rank in the array of nodes considering valid nodes and gaps
|
|
*/
|
|
size_type
|
|
get_shifted_rank(const size_type pos, const int index) const
|
|
{ return pos; }
|
|
};
|
|
|
|
|
|
/** @brief Helper comparator class: Invert a binary comparator
|
|
* @param _Comp Comparator to invert
|
|
* @param _Iterator Iterator to the elements to compare */
|
|
template<typename _Comp, typename _Iterator>
|
|
class gr_or_eq
|
|
{
|
|
/** @brief Renaming value_type of _Iterator */
|
|
typedef typename std::iterator_traits<_Iterator>::value_type value_type;
|
|
|
|
/** @brief Comparator to be inverted */
|
|
const _Comp comp;
|
|
|
|
public:
|
|
/** @brief Constructor
|
|
* @param c Comparator */
|
|
gr_or_eq(const _Comp& c) : comp(c) { }
|
|
|
|
/** @brief Operator()
|
|
* @param a First value to compare
|
|
* @param b Second value to compare */
|
|
bool operator()(const value_type& a, const value_type& b) const
|
|
{
|
|
if (not (comp(_KeyOfValue()(a), _KeyOfValue()(b))))
|
|
return true;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/** @brief Helper comparator class: Passed as a parameter of
|
|
list_partition to check that a sequence is sorted
|
|
* @param _InputIterator Iterator to the elements to compare
|
|
* @param _CompIsSorted Comparator to check for sortednesss */
|
|
template<typename _InputIterator, typename _CompIsSorted>
|
|
class is_sorted_functor
|
|
{
|
|
/** @brief Element to compare with (first parameter of comp) */
|
|
_InputIterator prev;
|
|
|
|
/** @brief Comparator to check for sortednesss */
|
|
const _CompIsSorted comp;
|
|
|
|
/** @brief Sum up the history of the operator() of this
|
|
* comparator class Its value is true if all calls to comp from
|
|
* this class have returned true. It is false otherwise */
|
|
bool sorted;
|
|
|
|
public:
|
|
/** @brief Constructor
|
|
*
|
|
* Sorted is set to true
|
|
* @param first Element to compare with the first time the
|
|
* operator() is called
|
|
* @param c Comparator to check for sortedness */
|
|
is_sorted_functor(const _InputIterator first, const _CompIsSorted c)
|
|
: prev(first), comp(c), sorted(true) { }
|
|
|
|
/** @brief Operator() with only one explicit parameter. Updates
|
|
the class member @c prev and sorted.
|
|
* @param it Iterator to the element which must be compared to
|
|
* the element pointed by the the class member @c prev */
|
|
void operator()(const _InputIterator it)
|
|
{
|
|
if (sorted and it != prev and comp(_KeyOfValue()(*it),
|
|
_KeyOfValue()(*prev)))
|
|
sorted = false;
|
|
prev = it;
|
|
}
|
|
|
|
/** @brief Query method for sorted
|
|
* @return Current value of sorted */
|
|
bool is_sorted() const
|
|
{
|
|
return sorted;
|
|
}
|
|
};
|
|
|
|
/** @brief Helper functor: sort the input based upon elements
|
|
instead of keys
|
|
* @param KeyComparator Comparator for the key of values */
|
|
template<typename KeyComparator>
|
|
class ValueCompare
|
|
: public std::binary_function<value_type, value_type, bool>
|
|
{
|
|
/** @brief Comparator for the key of values */
|
|
const KeyComparator comp;
|
|
|
|
public:
|
|
/** @brief Constructor
|
|
* @param c Comparator for the key of values */
|
|
ValueCompare(const KeyComparator& c): comp(c) { }
|
|
|
|
/** @brief Operator(): Analogous to comp but for values and not keys
|
|
* @param v1 First value to compare
|
|
* @param v2 Second value to compare
|
|
* @return Result of the comparison */
|
|
bool operator()(const value_type& v1, const value_type& v2) const
|
|
{ return comp(_KeyOfValue()(v1),_KeyOfValue()(v2)); }
|
|
};
|
|
|
|
/** @brief Helper comparator: compare a key with the key in a node
|
|
* @param _Comparator Comparator for keys */
|
|
template<typename _Comparator>
|
|
struct compare_node_key
|
|
{
|
|
/** @brief Comparator for keys */
|
|
const _Comparator& c;
|
|
|
|
/** @brief Constructor
|
|
* @param _c Comparator for keys */
|
|
compare_node_key(const _Comparator& _c) : c(_c) { }
|
|
|
|
/** @brief Operator() with the first parameter being a node
|
|
* @param r Node whose key is to be compared
|
|
* @param k Key to be compared
|
|
* @return Result of the comparison */
|
|
bool operator()(const _Rb_tree_node_ptr r, const key_type& k) const
|
|
{ return c(base_type::_S_key(r),k); }
|
|
|
|
/** @brief Operator() with the second parameter being a node
|
|
* @param k Key to be compared
|
|
* @param r Node whose key is to be compared
|
|
* @return Result of the comparison */
|
|
bool operator()(const key_type& k, const _Rb_tree_node_ptr r) const
|
|
{ return c(k, base_type::_S_key(r)); }
|
|
};
|
|
|
|
/** @brief Helper comparator: compare a key with the key of a
|
|
value pointed by an iterator
|
|
* @param _Comparator Comparator for keys
|
|
*/
|
|
template<typename _Iterator, typename _Comparator>
|
|
struct compare_value_key
|
|
{
|
|
/** @brief Comparator for keys */
|
|
const _Comparator& c;
|
|
|
|
/** @brief Constructor
|
|
* @param _c Comparator for keys */
|
|
compare_value_key(const _Comparator& _c) : c(_c){ }
|
|
|
|
/** @brief Operator() with the first parameter being an iterator
|
|
* @param v Iterator to the value whose key is to be compared
|
|
* @param k Key to be compared
|
|
* @return Result of the comparison */
|
|
bool operator()(const _Iterator& v, const key_type& k) const
|
|
{ return c(_KeyOfValue()(*v),k); }
|
|
|
|
/** @brief Operator() with the second parameter being an iterator
|
|
* @param k Key to be compared
|
|
* @param v Iterator to the value whose key is to be compared
|
|
* @return Result of the comparison */
|
|
bool operator()(const key_type& k, const _Iterator& v) const
|
|
{ return c(k, _KeyOfValue()(*v)); }
|
|
};
|
|
|
|
/** @brief Helper class of _Rb_tree to avoid some symmetric code
|
|
in tree operations */
|
|
struct LeftRight
|
|
{
|
|
/** @brief Obtain the conceptual left child of a node
|
|
* @param parent Node whose child must be obtained
|
|
* @return Reference to the child node */
|
|
static _Rb_tree_node_base*& left(_Rb_tree_node_base* parent)
|
|
{ return parent->_M_left; }
|
|
|
|
/** @brief Obtain the conceptual right child of a node
|
|
* @param parent Node whose child must be obtained
|
|
* @return Reference to the child node */
|
|
static _Rb_tree_node_base*& right(_Rb_tree_node_base* parent)
|
|
{ return parent->_M_right; }
|
|
};
|
|
|
|
/** @brief Helper class of _Rb_tree to avoid some symmetric code
|
|
in tree operations: inverse the symmetry
|
|
* @param S Symmetry to inverse
|
|
* @sa LeftRight */
|
|
template<typename S>
|
|
struct Opposite
|
|
{
|
|
/** @brief Obtain the conceptual left child of a node, inverting
|
|
the symmetry
|
|
* @param parent Node whose child must be obtained
|
|
* @return Reference to the child node */
|
|
static _Rb_tree_node_base*& left(_Rb_tree_node_base* parent)
|
|
{ return S::right(parent);}
|
|
|
|
/** @brief Obtain the conceptual right child of a node,
|
|
inverting the symmetry
|
|
* @param parent Node whose child must be obtained
|
|
* @return Reference to the child node */
|
|
static _Rb_tree_node_base*& right(_Rb_tree_node_base* parent)
|
|
{ return S::left(parent);}
|
|
};
|
|
|
|
/** @brief Inverse symmetry of LeftRight */
|
|
typedef Opposite<LeftRight> RightLeft;
|
|
|
|
/** @brief Helper comparator to compare value pointers, so that
|
|
the value is taken
|
|
* @param Comparator Comparator for values
|
|
* @param _ValuePtr Pointer to values */
|
|
template<typename Comparator, typename _ValuePtr>
|
|
class PtrComparator
|
|
: public std::binary_function<_ValuePtr, _ValuePtr, bool>
|
|
{
|
|
/** @brief Comparator for values */
|
|
Comparator comp;
|
|
|
|
public:
|
|
/** @brief Constructor
|
|
* @param comp Comparator for values */
|
|
PtrComparator(Comparator comp) : comp(comp) { }
|
|
|
|
/** @brief Operator(): compare the values instead of the pointers
|
|
* @param v1 Pointer to the first element to compare
|
|
* @param v2 Pointer to the second element to compare */
|
|
bool operator()(const _ValuePtr& v1, const _ValuePtr& v2) const
|
|
{ return comp(*v1,*v2); }
|
|
};
|
|
|
|
/** @brief Iterator whose elements are pointers
|
|
* @param value_type Type pointed by the pointers */
|
|
template<typename _ValueTp>
|
|
class PtrIterator
|
|
{
|
|
public:
|
|
/** @brief The iterator category is random access iterator */
|
|
typedef typename std::random_access_iterator_tag iterator_category;
|
|
typedef _ValueTp value_type;
|
|
typedef size_t difference_type;
|
|
typedef value_type* ValuePtr;
|
|
typedef ValuePtr& reference;
|
|
typedef value_type** pointer;
|
|
|
|
/** @brief Element accessed by the iterator */
|
|
value_type** ptr;
|
|
|
|
/** @brief Trivial constructor */
|
|
PtrIterator() { }
|
|
|
|
/** @brief Constructor from an element */
|
|
PtrIterator(const ValuePtr& __i) : ptr(&__i) { }
|
|
|
|
/** @brief Constructor from a pointer */
|
|
PtrIterator(const pointer& __i) : ptr(__i) { }
|
|
|
|
/** @brief Copy constructor */
|
|
PtrIterator(const PtrIterator<value_type>& __i) : ptr(__i.ptr) { }
|
|
|
|
reference
|
|
operator*() const
|
|
{ return **ptr; }
|
|
|
|
ValuePtr
|
|
operator->() const
|
|
{ return *ptr; }
|
|
|
|
/** @brief Bidirectional iterator requirement */
|
|
PtrIterator&
|
|
operator++()
|
|
{
|
|
++ptr;
|
|
return *this;
|
|
}
|
|
|
|
/** @brief Bidirectional iterator requirement */
|
|
PtrIterator
|
|
operator++(int)
|
|
{ return PtrIterator(ptr++); }
|
|
|
|
/** @brief Bidirectional iterator requirement */
|
|
PtrIterator&
|
|
operator--()
|
|
{
|
|
--ptr;
|
|
return *this;
|
|
}
|
|
|
|
/** @brief Bidirectional iterator requirement */
|
|
PtrIterator
|
|
operator--(int)
|
|
{ return PtrIterator(ptr--); }
|
|
|
|
/** @brief Random access iterator requirement */
|
|
reference
|
|
operator[](const difference_type& __n) const
|
|
{ return *ptr[__n]; }
|
|
|
|
/** @brief Random access iterator requirement */
|
|
PtrIterator&
|
|
operator+=(const difference_type& __n)
|
|
{
|
|
ptr += __n;
|
|
return *this;
|
|
}
|
|
|
|
/** @brief Random access iterator requirement */
|
|
PtrIterator
|
|
operator+(const difference_type& __n) const
|
|
{ return PtrIterator(ptr + __n); }
|
|
|
|
/** @brief Random access iterator requirement */
|
|
PtrIterator&
|
|
operator-=(const difference_type& __n)
|
|
{
|
|
ptr -= __n;
|
|
return *this;
|
|
}
|
|
|
|
/** @brief Random access iterator requirement */
|
|
PtrIterator
|
|
operator-(const difference_type& __n) const
|
|
{ return PtrIterator(ptr - __n); }
|
|
|
|
/** @brief Random access iterator requirement */
|
|
difference_type
|
|
operator-(const PtrIterator<value_type>& iter) const
|
|
{ return ptr - iter.ptr; }
|
|
|
|
/** @brief Random access iterator requirement */
|
|
difference_type
|
|
operator+(const PtrIterator<value_type>& iter) const
|
|
{ return ptr + iter.ptr; }
|
|
|
|
/** @brief Allow assignment of an element ValuePtr to the iterator */
|
|
PtrIterator<value_type>& operator=(const ValuePtr sptr)
|
|
{
|
|
ptr = &sptr;
|
|
return *this;
|
|
}
|
|
|
|
PtrIterator<value_type>& operator=(const PtrIterator<value_type>& piter)
|
|
{
|
|
ptr = piter.ptr;
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const PtrIterator<value_type>& piter)
|
|
{ return ptr == piter.ptr; }
|
|
|
|
bool operator!=(const PtrIterator<value_type>& piter)
|
|
{ return ptr != piter.ptr; }
|
|
|
|
};
|
|
|
|
|
|
/** @brief Bulk insertion helper: synchronization and construction
|
|
of the tree bottom up */
|
|
struct concat_problem
|
|
{
|
|
/** @brief Root of a tree.
|
|
*
|
|
* Input: Middle node to concatenate two subtrees. Out: Root of
|
|
* the resulting concatenated tree. */
|
|
_Rb_tree_node_ptr t;
|
|
|
|
/** @brief Black height of @c t */
|
|
int black_h;
|
|
|
|
/** @brief Synchronization variable.
|
|
*
|
|
* \li READY_YES: the root of the tree can be concatenated with
|
|
* the result of the children concatenation problems (both of
|
|
* them have finished).
|
|
* \li READY_NOT: at least one of the children
|
|
* concatenation_problem have not finished */
|
|
int is_ready;
|
|
|
|
/** @brief Parent concatenation problem to solve when @c
|
|
is_ready = READY_YES */
|
|
concat_problem* par_problem;
|
|
|
|
/** @brief Left concatenation problem */
|
|
concat_problem* left_problem;
|
|
|
|
/** @brief Right concatenation problem */
|
|
concat_problem* right_problem;
|
|
|
|
/** @brief Value NO for the synchronization variable. */
|
|
static const int READY_NO = 0;
|
|
|
|
/** @brief Value YES for the synchronization variable. */
|
|
static const int READY_YES = 1;
|
|
|
|
/** @brief Trivial constructor.
|
|
*
|
|
* Initialize the synchronization variable to not ready. */
|
|
concat_problem(): is_ready(READY_NO) { }
|
|
|
|
/** @brief Constructor.
|
|
*
|
|
* Initialize the synchronization variable to not ready.
|
|
* @param _t Root of a tree.
|
|
* @param _black_h Black height of @c _t
|
|
* @param _par_problem Parent concatenation problem to solve
|
|
* when @c is_ready = READY_YES
|
|
*/
|
|
concat_problem(const _Rb_tree_node_ptr _t, const int _black_h,
|
|
concat_problem* _par_problem)
|
|
: t(_t), black_h(_black_h), is_ready(READY_NO), par_problem(_par_problem)
|
|
{
|
|
// The root of an insertion problem must be black.
|
|
if (t != NULL and t->_M_color == std::_S_red)
|
|
{
|
|
t->_M_color = std::_S_black;
|
|
++black_h;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/** @brief Bulk insertion helper: insertion of a sequence of
|
|
elements in a subtree
|
|
@invariant t, pos_beg and pos_end will not change after initialization
|
|
*/
|
|
struct insertion_problem
|
|
{
|
|
/** @brief Renaming of _Rb_tree @c size_type */
|
|
typedef _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc> tree_type;
|
|
typedef typename tree_type::size_type size_type;
|
|
|
|
/** @brief Root of the tree where the elements are to be inserted */
|
|
_Rb_tree_node_ptr t;
|
|
|
|
/** @brief Position of the first node in the array of nodes to
|
|
be inserted into @c t */
|
|
size_type pos_beg;
|
|
|
|
/** @brief Position of the first node in the array of nodes
|
|
that won't be inserted into @c t */
|
|
size_type pos_end;
|
|
|
|
/** @brief Partition in the array of nodes of @c pos_beg and @c
|
|
pos_end (must be the same for both, and so gaps are
|
|
avoided) */
|
|
int array_partition;
|
|
|
|
/** @brief Concatenation problem to solve once the insertion
|
|
problem is finished */
|
|
concat_problem* conc;
|
|
|
|
/** @brief Trivial constructor. */
|
|
insertion_problem()
|
|
{ }
|
|
|
|
/** @brief Constructor.
|
|
* @param b Position of the first node in the array of nodes to
|
|
* be inserted into @c _conc->t
|
|
* @param e Position of the first node in the array of nodes
|
|
* that won't be inserted into @c _conc->t
|
|
* @param array_p Partition in the array of nodes of @c b and @c e
|
|
* @param _conc Concatenation problem to solve once the
|
|
* insertion problem is finished
|
|
*/
|
|
insertion_problem(const size_type b, const size_type e,
|
|
const int array_p, concat_problem* _conc)
|
|
: t(_conc->t), pos_beg(b), pos_end(e), array_partition(array_p),
|
|
conc(_conc)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(pos_beg <= pos_end);
|
|
|
|
//The root of an insertion problem must be black!!
|
|
_GLIBCXX_PARALLEL_ASSERT(t == NULL or t->_M_color != std::_S_red);
|
|
}
|
|
};
|
|
|
|
|
|
/** @brief Main bulk construction and insertion helper method
|
|
* @param __first First element in a sequence to be added into the tree
|
|
* @param __last End of the sequence of elements to be added into the tree
|
|
* @param is_construction If true, the tree was empty and so, this
|
|
* is constructed. Otherwise, the elements are added to an
|
|
* existing tree.
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
* The input sequence is preprocessed so that the bulk
|
|
* construction or insertion can be performed
|
|
* efficiently. Essentially, the sequence is checked for
|
|
* sortednesss and iterators to the middle of the structure are
|
|
* saved so that afterwards the sequence can be processed
|
|
* effectively in parallel. */
|
|
template<typename _InputIterator, typename StrictlyLessOrLessEqual>
|
|
void
|
|
_M_bulk_insertion_construction(const _InputIterator __first, const _InputIterator __last, const bool is_construction, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
Timing<_timing_tag> t;
|
|
|
|
t.tic();
|
|
|
|
thread_index_t num_threads = get_max_threads();
|
|
size_type n;
|
|
size_type beg_partition[num_threads+1];
|
|
_InputIterator access[num_threads+1];
|
|
beg_partition[0] = 0;
|
|
bool is_sorted= is_sorted_distance_accessors(__first, __last, access, beg_partition,n, num_threads, std::__iterator_category(__first));
|
|
|
|
t.tic("is_sorted");
|
|
|
|
if (not is_sorted)
|
|
{
|
|
_M_not_sorted_bulk_insertion_construction(access, beg_partition, n, num_threads, is_construction, strictly_less_or_less_equal);
|
|
}
|
|
else
|
|
{
|
|
// The vector must be moved... all ranges must have at least
|
|
// one element, or make just sequential???
|
|
if (static_cast<size_type>(num_threads) > n)
|
|
{
|
|
int j = 1;
|
|
for (int i = 1; i <= num_threads; ++i)
|
|
{
|
|
if (beg_partition[j-1] != beg_partition[i])
|
|
{
|
|
beg_partition[j] = beg_partition[i];
|
|
access[j] = access[i];
|
|
++j;
|
|
}
|
|
}
|
|
num_threads = static_cast<thread_index_t>(n);
|
|
}
|
|
|
|
if (is_construction)
|
|
_M_sorted_bulk_construction(access, beg_partition, n, num_threads,
|
|
strictly_less_or_less_equal);
|
|
else
|
|
_M_sorted_bulk_insertion(access, beg_partition, n, num_threads,
|
|
strictly_less_or_less_equal);
|
|
}
|
|
|
|
t.tic("main work");
|
|
|
|
t.print();
|
|
}
|
|
|
|
/** @brief Bulk construction and insertion helper method on an
|
|
* input sequence which is not sorted
|
|
*
|
|
* The elements are copied, according to the copy policy, in order
|
|
* to be sorted. Then the
|
|
* _M_not_sorted_bulk_insertion_construction() method is called
|
|
* appropriately
|
|
* @param access Array of iterators of size @c num_threads +
|
|
* 1. Each position contains the first element in each subsequence
|
|
* to be added into the tree.
|
|
* @param beg_partition Array of positions of size @c num_threads
|
|
* + 1. Each position contains the rank of the first element in
|
|
* each subsequence to be added into the tree.
|
|
* @param n Size of the sequence to be inserted
|
|
* @param num_threads Number of threads and corresponding
|
|
* subsequences in which the insertion work is going to be shared
|
|
* @param is_construction If true, the tree was empty and so, this
|
|
* is constructed. Otherwise, the elements are added to an
|
|
* existing tree.
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
*/
|
|
template<typename _InputIterator, typename StrictlyLessOrLessEqual>
|
|
void
|
|
_M_not_sorted_bulk_insertion_construction(_InputIterator* access,
|
|
size_type* beg_partition,
|
|
const size_type n,
|
|
const thread_index_t num_threads,
|
|
const bool is_construction,
|
|
StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
// Copy entire elements. In the case of a map, we would be
|
|
// copying the pair. Therefore, the copy should be reconsidered
|
|
// when objects are big. Essentially two cases:
|
|
// - The key is small: make that the pair, is a pointer to data
|
|
// instead of a copy to it
|
|
// - The key is big: we simply have a pointer to the iterator
|
|
#if _GLIBCXX_TREE_FULL_COPY
|
|
nc_value_type* v = static_cast<nc_value_type*> (::operator new(sizeof(nc_value_type)*(n+1)));
|
|
|
|
uninitialized_copy_from_accessors(access, beg_partition, v, num_threads);
|
|
|
|
_M_not_sorted_bulk_insertion_construction<nc_value_type, nc_value_type*, ValueCompare<_Compare> >
|
|
(beg_partition, v, ValueCompare<_Compare>(base_type::_M_impl._M_key_compare), n, num_threads, is_construction, strictly_less_or_less_equal);
|
|
#else
|
|
// For sorting, we cannot use the new PtrIterator because we
|
|
// want the pointers to be exchanged and not the elements.
|
|
typedef PtrComparator<ValueCompare<_Compare>, nc_value_type*> this_ptr_comparator;
|
|
nc_value_type** v = static_cast<nc_value_type**> (::operator new(sizeof(nc_value_type*)*(n+1)));
|
|
|
|
uninitialized_ptr_copy_from_accessors(access, beg_partition, v, num_threads);
|
|
|
|
_M_not_sorted_bulk_insertion_construction<nc_value_type*, PtrIterator<nc_value_type>, this_ptr_comparator>
|
|
(beg_partition, v, this_ptr_comparator(ValueCompare<_Compare>(base_type::_M_impl._M_key_compare)), n, num_threads, is_construction, strictly_less_or_less_equal);
|
|
#endif
|
|
}
|
|
|
|
/** @brief Bulk construction and insertion helper method on an
|
|
* input sequence which is not sorted
|
|
*
|
|
* The elements are sorted and its accessors calculated. Then,
|
|
* _M_sorted_bulk_construction() or _M_sorted_bulk_insertion() is
|
|
* called.
|
|
* @param beg_partition Array of positions of size @c num_threads
|
|
* + 1. Each position contains the rank of the first element in
|
|
* each subsequence to be added into the tree.
|
|
* @param v Array of elements to be sorted (copy of the original sequence).
|
|
* @param comp Comparator to be used for sorting the elements
|
|
* @param n Size of the sequence to be inserted
|
|
* @param num_threads Number of threads and corresponding
|
|
* subsequences in which the insertion work is going to be shared
|
|
* @param is_construction If true, _M_sorted_bulk_construction()
|
|
* is called. Otherwise, _M_sorted_bulk_insertion() is called.
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
*/
|
|
template<typename ElementsToSort, typename IteratorSortedElements, typename Comparator, typename StrictlyLessOrLessEqual>
|
|
void
|
|
_M_not_sorted_bulk_insertion_construction(size_type* beg_partition, ElementsToSort* v, Comparator comp, const size_type n, thread_index_t num_threads, const bool is_construction, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
// The accessors have been calculated for the non sorted.
|
|
Timing<_timing_tag> t;
|
|
|
|
t.tic();
|
|
|
|
num_threads = static_cast<thread_index_t>(std::min<size_type>(num_threads, n));
|
|
|
|
std::stable_sort(v, v+n, comp);
|
|
|
|
t.tic("sort");
|
|
|
|
IteratorSortedElements sorted_access[num_threads+1];
|
|
range_accessors(IteratorSortedElements(v), IteratorSortedElements(v+n), sorted_access, beg_partition, n, num_threads, std::__iterator_category(v));
|
|
|
|
t.tic("range_accessors");
|
|
|
|
// Partial template specialization not available.
|
|
if (is_construction)
|
|
_M_sorted_bulk_construction(sorted_access, beg_partition, n, num_threads, strictly_less_or_less_equal);
|
|
else
|
|
_M_sorted_bulk_insertion(sorted_access, beg_partition, n, num_threads, strictly_less_or_less_equal);
|
|
delete v;
|
|
|
|
t.tic("actual construction or insertion");
|
|
|
|
t.print();
|
|
}
|
|
|
|
/** @brief Construct a tree sequentially using the parallel routine
|
|
* @param r_array Array of nodes from which to take the nodes to
|
|
* build the tree
|
|
* @param pos_beg Position of the first node in the array of nodes
|
|
* to be part of the tree
|
|
* @param pos_end Position of the first node in the array of nodes
|
|
* that will not be part of the tree
|
|
* @param black_h Black height of the resulting tree (out)
|
|
*/
|
|
static _Rb_tree_node_ptr
|
|
simple_tree_construct(_Rb_tree_node_ptr* r_array, const size_type pos_beg,
|
|
const size_type pos_end, int& black_h)
|
|
{
|
|
if (pos_beg == pos_end)
|
|
{
|
|
black_h = 0;
|
|
return NULL;
|
|
}
|
|
if (pos_beg+1 == pos_end)
|
|
{
|
|
// It is needed, not only for efficiency but because the
|
|
// last level in our tree construction is red.
|
|
make_leaf(r_array[pos_beg], black_h);
|
|
return r_array[pos_beg];
|
|
}
|
|
|
|
// Dummy b_p
|
|
size_type b_p[2];
|
|
b_p[0] = 0;
|
|
b_p[1] = pos_end - pos_beg;
|
|
_Rb_tree_node_ptr* r= r_array + pos_beg;
|
|
size_type length = pos_end - pos_beg;
|
|
|
|
ranker_no_gaps rank;
|
|
nodes_initializer<ranker_no_gaps> nodes_init(r, length, 1, rank);
|
|
|
|
black_h = nodes_init.get_height();
|
|
|
|
size_type split = nodes_init.get_shifted_splitting_point();
|
|
for (size_type i = 0; i < split; ++i)
|
|
nodes_init.link_complete(r[i],0);
|
|
|
|
for (size_type i = split; i < length; ++i)
|
|
nodes_init.link_incomplete(r[i],0);
|
|
|
|
_Rb_tree_node_ptr t = nodes_init.get_root();
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify_tree(t));
|
|
_GLIBCXX_PARALLEL_ASSERT(t->_M_color == std::_S_black);
|
|
return t;
|
|
}
|
|
|
|
|
|
/** @brief Allocation of an array of nodes and initialization of
|
|
their value fields from an input sequence. Done in parallel.
|
|
* @param access Array of iterators of size @c num_threads +
|
|
* 1. Each position contains the first value in the subsequence to
|
|
* be copied into the corresponding tree node.
|
|
* @param beg_partition Array of positions of size @c num_threads
|
|
* + 1. Each position contains the rank of the first element in
|
|
* the subsequence from which to copy the data to initialize the
|
|
* nodes.
|
|
* @param n Size of the sequence and the array of nodes to be allocated.
|
|
* @param num_threads Number of threads and corresponding
|
|
* subsequences in which the allocation and initialization work is
|
|
* going to be shared
|
|
*/
|
|
template<typename _Iterator>
|
|
_Rb_tree_node_ptr*
|
|
_M_unsorted_bulk_allocation_and_initialization(const _Iterator* access, const size_type* beg_partition, const size_type n, const thread_index_t num_threads)
|
|
{
|
|
_Rb_tree_node_ptr* r = static_cast<_Rb_tree_node_ptr*> (::operator new (sizeof(_Rb_tree_node_ptr)*(n+1)));
|
|
|
|
// Allocate and initialize the nodes (don't check for uniqueness
|
|
// because the sequence is not necessarily sorted.
|
|
#pragma omp parallel num_threads(num_threads)
|
|
{
|
|
#if USE_PAPI
|
|
PAPI_register_thread();
|
|
#endif
|
|
|
|
int iam = omp_get_thread_num();
|
|
_Iterator it = access[iam];
|
|
size_type i = beg_partition[iam];
|
|
while (it!= access[iam+1])
|
|
{
|
|
r[i] = base_type::_M_create_node(*it);
|
|
++i;
|
|
++it;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
/** @brief Allocation of an array of nodes and initialization of
|
|
* their value fields from an input sequence. Done in
|
|
* parallel. Besides, the sequence is checked for uniqueness while
|
|
* copying the elements, and if there are repetitions, gaps within
|
|
* the partitions are created.
|
|
*
|
|
* An extra ghost node pointer is reserved in the array to ease
|
|
* comparisons later while linking the nodes
|
|
* @pre The sequence is sorted.
|
|
* @param access Array of iterators of size @c num_threads +
|
|
* 1. Each position contains the first value in the subsequence to
|
|
* be copied into the corresponding tree node.
|
|
* @param beg_partition Array of positions of size @c num_threads
|
|
* + 1. Each position contains the rank of the first element in
|
|
* the subsequence from which to copy the data to initialize the
|
|
* nodes.
|
|
* @param rank_shift Array of size @c num_threads + 1 containing
|
|
* the number of accumulated gaps at the beginning of each
|
|
* partition
|
|
* @param n Size of the sequence and the array of nodes (-1) to be
|
|
* allocated.
|
|
* @param num_threads Number of threads and corresponding
|
|
* subsequences in which the allocation and initialization work is
|
|
* going to be shared
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
*/
|
|
template<typename _Iterator, typename StrictlyLessOrLessEqual>
|
|
_Rb_tree_node_ptr*
|
|
_M_sorted_bulk_allocation_and_initialization(_Iterator* access, size_type* beg_partition, size_type* rank_shift, const size_type n, thread_index_t& num_threads, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
// Ghost node at the end to avoid extra comparisons in nodes_initializer.
|
|
_Rb_tree_node_ptr* r = static_cast<_Rb_tree_node_ptr*> (::operator new (sizeof(_Rb_tree_node_ptr)*(n+1)));
|
|
r[n] = NULL;
|
|
|
|
// Dealing with repetitions (EFFICIENCY ISSUE).
|
|
_Iterator access_copy[num_threads+1];
|
|
for (int i = 0; i <= num_threads; ++i)
|
|
access_copy[i] = access[i];
|
|
// Allocate and initialize the nodes
|
|
#pragma omp parallel num_threads(num_threads)
|
|
{
|
|
#if USE_PAPI
|
|
PAPI_register_thread();
|
|
#endif
|
|
thread_index_t iam = omp_get_thread_num();
|
|
_Iterator prev = access[iam];
|
|
size_type i = beg_partition[iam];
|
|
_Iterator it = prev;
|
|
if (iam != 0)
|
|
{
|
|
--prev;
|
|
// Dealing with repetitions (CORRECTNESS ISSUE).
|
|
while (it!= access_copy[iam+1] and not strictly_less_or_less_equal(_KeyOfValue()(*prev), _KeyOfValue()(*it)))
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(not base_type::_M_impl._M_key_compare(_KeyOfValue()(*it),_KeyOfValue()(*prev)));
|
|
++it;
|
|
}
|
|
access[iam] = it;
|
|
if (it != access_copy[iam+1]){
|
|
r[i] = base_type::_M_create_node(*it);
|
|
++i;
|
|
prev=it;
|
|
++it;
|
|
}
|
|
//}
|
|
}
|
|
else
|
|
{
|
|
r[i] = base_type::_M_create_node(*prev);
|
|
++i;
|
|
++it;
|
|
}
|
|
while (it!= access_copy[iam+1])
|
|
{
|
|
/***** Dealing with repetitions (CORRECTNESS ISSUE) *****/
|
|
if (strictly_less_or_less_equal(_KeyOfValue()(*prev),_KeyOfValue()(*it)))
|
|
{
|
|
r[i] = base_type::_M_create_node(*it);
|
|
++i;
|
|
prev=it;
|
|
}
|
|
else{
|
|
_GLIBCXX_PARALLEL_ASSERT(not base_type::_M_impl._M_key_compare(_KeyOfValue()(*it),_KeyOfValue()(*prev)));
|
|
}
|
|
++it;
|
|
}
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
rank_shift[iam+1] = beg_partition[iam+1] - i;
|
|
}
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
rank_shift[0] = 0;
|
|
/* Guarantee that there are no empty intervals.
|
|
- If an empty interval is found, is joined with the previous one
|
|
(the rank_shift of the previous is augmented with all the new
|
|
repetitions)
|
|
*/
|
|
thread_index_t i = 1;
|
|
while (i <= num_threads and rank_shift[i] != (beg_partition[i] - beg_partition[i-1]))
|
|
{
|
|
rank_shift[i] += rank_shift[i-1];
|
|
++i;
|
|
}
|
|
if (i <= num_threads)
|
|
{
|
|
thread_index_t j = i - 1;
|
|
while (true)
|
|
{
|
|
do
|
|
{
|
|
rank_shift[j] += rank_shift[i];
|
|
++i;
|
|
} while (i <= num_threads and rank_shift[i] == (beg_partition[i] - beg_partition[i-1]));
|
|
|
|
beg_partition[j] = beg_partition[i-1];
|
|
access[j] = access[i-1];
|
|
if (i > num_threads) break;
|
|
++j;
|
|
|
|
// Initialize with the previous.
|
|
rank_shift[j] = rank_shift[j-1];
|
|
}
|
|
num_threads = j;
|
|
}
|
|
return r;
|
|
|
|
}
|
|
|
|
/** @brief Allocation of an array of nodes and initialization of
|
|
* their value fields from an input sequence.
|
|
*
|
|
* The allocation and initialization is done in parallel. Besides,
|
|
* the sequence is checked for uniqueness while copying the
|
|
* elements. However, in contrast to
|
|
* _M_sorted_bulk_allocation_and_initialization(), if there are
|
|
* repetitions, no gaps within the partitions are created. To do
|
|
* so efficiently, some extra memory is needed to compute a prefix
|
|
* sum.
|
|
* @pre The sequence is sorted.
|
|
* @param access Array of iterators of size @c num_threads +
|
|
* 1. Each position contains the first value in the subsequence to
|
|
* be copied into the corresponding tree node.
|
|
* @param beg_partition Array of positions of size @c num_threads
|
|
* + 1. Each position contains the rank of the first element in
|
|
* the subsequence from which to copy the data to initialize the
|
|
* nodes.
|
|
* @param n Size of the sequence and the array of nodes (-1) to be
|
|
* allocated.
|
|
* @param num_threads Number of threads and corresponding
|
|
* subsequences in which the allocation and initialization work is
|
|
* going to be shared
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
*/
|
|
template<typename _Iterator, typename StrictlyLessOrLessEqual>
|
|
_Rb_tree_node_ptr*
|
|
_M_sorted_no_gapped_bulk_allocation_and_initialization(_Iterator* access, size_type* beg_partition, size_type& n, const thread_index_t num_threads, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
size_type* sums = static_cast<size_type*> (::operator new (sizeof(size_type)*n));
|
|
// Allocate and initialize the nodes
|
|
/* try
|
|
{*/
|
|
#pragma omp parallel num_threads(num_threads)
|
|
{
|
|
#if USE_PAPI
|
|
PAPI_register_thread();
|
|
#endif
|
|
int iam = omp_get_thread_num();
|
|
_Iterator prev = access[iam];
|
|
size_type i = beg_partition[iam];
|
|
_Iterator it = prev;
|
|
if (iam !=0)
|
|
{
|
|
--prev;
|
|
|
|
// First iteration here, to update accessor in case was
|
|
// equal to the last element of the previous range
|
|
|
|
// Dealing with repetitions (CORRECTNESS ISSUE).
|
|
if (strictly_less_or_less_equal(_KeyOfValue()(*prev),_KeyOfValue()(*it)))
|
|
{
|
|
sums[i] = 0;
|
|
prev=it;
|
|
}
|
|
else
|
|
{
|
|
sums[i] = 1;
|
|
}
|
|
++i;
|
|
++it;
|
|
}
|
|
else
|
|
{
|
|
sums[i] = 0;
|
|
++i;
|
|
++it;
|
|
}
|
|
while (it!= access[iam+1])
|
|
{
|
|
// Dealing with repetitions (CORRECTNESS ISSUE).
|
|
if (strictly_less_or_less_equal(_KeyOfValue()(*prev),_KeyOfValue()(*it)))
|
|
{
|
|
sums[i] = 0;
|
|
prev=it;
|
|
}
|
|
else
|
|
sums[i] = 1;
|
|
++i;
|
|
++it;
|
|
}
|
|
}
|
|
// Should be done in parallel.
|
|
partial_sum(sums,sums + n, sums);
|
|
|
|
n -= sums[n-1];
|
|
_Rb_tree_node_ptr* r = static_cast<_Rb_tree_node_ptr*> (::operator new (sizeof(_Rb_tree_node_ptr)*(n+1)));
|
|
r[n]=0;
|
|
|
|
#pragma omp parallel num_threads(num_threads)
|
|
{
|
|
#if USE_PAPI
|
|
PAPI_register_thread();
|
|
#endif
|
|
int iam = omp_get_thread_num();
|
|
_Iterator it = access[iam];
|
|
size_type i = beg_partition[iam];
|
|
size_type j = i;
|
|
size_type before = 0;
|
|
if (iam > 0)
|
|
{
|
|
before = sums[i-1];
|
|
j -= sums[i-1];
|
|
}
|
|
beg_partition[iam] = j;
|
|
while (it!= access[iam+1])
|
|
{
|
|
while (it!= access[iam+1] and sums[i]!=before)
|
|
{
|
|
before = sums[i];
|
|
++i;
|
|
++it;
|
|
}
|
|
if (it!= access[iam+1])
|
|
{
|
|
r[j] = base_type::_M_create_node(*it);
|
|
++j;
|
|
++i;
|
|
++it;
|
|
}
|
|
}
|
|
|
|
}
|
|
beg_partition[num_threads] = n;
|
|
|
|
// Update beginning of partitions.
|
|
::operator delete(sums);
|
|
return r;
|
|
}
|
|
|
|
/** @brief Main bulk construction method: perform the actual
|
|
initialization, allocation and finally node linking once the
|
|
input sequence has already been preprocessed.
|
|
* @param access Array of iterators of size @c num_threads +
|
|
* 1. Each position contains the first value in the subsequence to
|
|
* be copied into the corresponding tree node.
|
|
* @param beg_partition Array of positions of size @c num_threads
|
|
* + 1. Each position contains the rank of the first element in
|
|
* the subsequence from which to copy the data to initialize the
|
|
* nodes.
|
|
* @param n Size of the sequence and the array of nodes (-1) to be
|
|
* allocated.
|
|
* @param num_threads Number of threads and corresponding
|
|
* subsequences in which the work is going to be shared
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
*/
|
|
template<typename _Iterator, typename StrictlyLessOrLessEqual>
|
|
void
|
|
_M_sorted_bulk_construction(_Iterator* access, size_type* beg_partition, const size_type n, thread_index_t num_threads, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
Timing<_timing_tag> t;
|
|
|
|
// Dealing with repetitions (EFFICIENCY ISSUE).
|
|
size_type rank_shift[num_threads+1];
|
|
|
|
t.tic();
|
|
|
|
_Rb_tree_node_ptr* r = _M_sorted_bulk_allocation_and_initialization(access, beg_partition, rank_shift, n, num_threads, strictly_less_or_less_equal);
|
|
|
|
t.tic("bulk allocation and initialization");
|
|
|
|
// Link the tree appropriately.
|
|
// Dealing with repetitions (EFFICIENCY ISSUE).
|
|
ranker_gaps rank(beg_partition, rank_shift, num_threads);
|
|
nodes_initializer<ranker_gaps> nodes_init(r, n - rank_shift[num_threads], num_threads, rank);
|
|
size_type split = nodes_init.get_shifted_splitting_point();
|
|
|
|
#pragma omp parallel num_threads(num_threads)
|
|
{
|
|
#if USE_PAPI
|
|
PAPI_register_thread();
|
|
#endif
|
|
int iam = omp_get_thread_num();
|
|
size_type beg = beg_partition[iam];
|
|
// Dealing with repetitions (EFFICIENCY ISSUE).
|
|
size_type end = beg_partition[iam+1] - (rank_shift[iam+1] - rank_shift[iam]);
|
|
if (split >= end)
|
|
{
|
|
for (size_type i = beg; i < end; ++i)
|
|
{
|
|
nodes_init.link_complete(r[i],iam);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (split <= beg)
|
|
{
|
|
for (size_type i = beg; i < end; ++i)
|
|
nodes_init.link_incomplete(r[i],iam);
|
|
}
|
|
else
|
|
{
|
|
for (size_type i = beg; i < split; ++i)
|
|
nodes_init.link_complete(r[i],iam);
|
|
for (size_type i = split; i < end; ++i)
|
|
nodes_init.link_incomplete(r[i],iam);
|
|
}
|
|
}
|
|
}
|
|
// If the execution reaches this point, there has been no
|
|
// exception, and so the structure can be initialized.
|
|
|
|
// Join the tree laid on the array of ptrs with the header node.
|
|
// Dealing with repetitions (EFFICIENCY ISSUE).
|
|
base_type::_M_impl._M_node_count = n - rank_shift[num_threads];
|
|
base_type::_M_impl._M_header._M_left = r[0];
|
|
thread_index_t with_element = num_threads;
|
|
while ((beg_partition[with_element] - beg_partition[with_element-1]) == (rank_shift[with_element] - rank_shift[with_element-1]))
|
|
{
|
|
--with_element;
|
|
}
|
|
base_type::_M_impl._M_header._M_right = r[beg_partition[with_element] - (rank_shift[with_element] - rank_shift[with_element-1]) - 1];
|
|
base_type::_M_impl._M_header._M_parent = nodes_init.get_root();
|
|
nodes_init.get_root()->_M_parent= &base_type::_M_impl._M_header;
|
|
|
|
t.tic("linking nodes");
|
|
::operator delete(r);
|
|
|
|
t.tic("delete array of pointers");
|
|
t.print();
|
|
}
|
|
|
|
|
|
/** @brief Main bulk insertion method: perform the actual
|
|
initialization, allocation and finally insertion once the
|
|
input sequence has already been preprocessed.
|
|
* @param access Array of iterators of size @c num_threads +
|
|
* 1. Each position contains the first value in the subsequence to
|
|
* be copied into the corresponding tree node.
|
|
* @param beg_partition Array of positions of size @c num_threads
|
|
* + 1. Each position contains the rank of the first element in
|
|
* the subsequence from which to copy the data to initialize the
|
|
* nodes.
|
|
* @param k Size of the sequence to be inserted (including the
|
|
* possible repeated elements among the sequence itself and
|
|
* against those elements already in the tree)
|
|
* @param num_threads Number of threads and corresponding
|
|
* subsequences in which the work is going to be shared
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
*/
|
|
template<typename _Iterator, typename StrictlyLessOrLessEqual>
|
|
void
|
|
_M_sorted_bulk_insertion(_Iterator* access, size_type* beg_partition, size_type k, thread_index_t num_threads, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT((size_type)num_threads <= k);
|
|
Timing<_timing_tag> t;
|
|
|
|
t.tic();
|
|
|
|
// num_thr-1 problems in the upper part of the tree
|
|
// num_thr problems to further parallelize
|
|
std::vector<size_type> existing(num_threads,0);
|
|
#if _GLIBCXX_TREE_INITIAL_SPLITTING
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
size_type rank_shift[num_threads+1];
|
|
|
|
// Need to create them dynamically because they are so erased
|
|
concat_problem* conc[2*num_threads-1];
|
|
#endif
|
|
_Rb_tree_node_ptr* r;
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
if (not strictly_less_or_less_equal(base_type::_S_key(base_type::_M_root()),base_type::_S_key(base_type::_M_root()) ))
|
|
{
|
|
// Unique container
|
|
// Set 1 and 2 could be done in parallel ...
|
|
// 1. Construct the nodes with their corresponding data
|
|
#if _GLIBCXX_TREE_INITIAL_SPLITTING
|
|
r = _M_sorted_bulk_allocation_and_initialization(access, beg_partition, rank_shift, k, num_threads, strictly_less_or_less_equal);
|
|
t.tic("bulk allocation and initialization");
|
|
#else
|
|
r = _M_sorted_no_gapped_bulk_allocation_and_initialization(access, beg_partition, k, num_threads, strictly_less_or_less_equal);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// Not unique container.
|
|
r = _M_unsorted_bulk_allocation_and_initialization(access, beg_partition, k, num_threads);
|
|
#if _GLIBCXX_TREE_INITIAL_SPLITTING
|
|
// Trivial initialization of rank_shift.
|
|
for (int i=0; i <= num_threads; ++i)
|
|
rank_shift[i] = 0;
|
|
#endif
|
|
}
|
|
#if _GLIBCXX_TREE_INITIAL_SPLITTING
|
|
// Calculate position of last element to be inserted: must be
|
|
// done now, or otherwise becomes messy.
|
|
|
|
/***** Dealing with
|
|
repetitions (EFFICIENCY ISSUE) *****/
|
|
size_type last = beg_partition[num_threads] - (rank_shift[num_threads] - rank_shift[num_threads - 1]);
|
|
|
|
t.tic("last element to be inserted");
|
|
|
|
//2. Split the tree according to access in num_threads parts
|
|
//Initialize upper concat_problems
|
|
//Allocate them dynamically because they are afterwards so erased
|
|
for (int i=0; i < (2*num_threads-1); ++i)
|
|
{
|
|
conc[i] = new concat_problem ();
|
|
}
|
|
concat_problem* root_problem = _M_bulk_insertion_initialize_upper_problems(conc, 0, num_threads, NULL);
|
|
|
|
// The first position of access and the last are ignored, so we
|
|
// have exactly num_threads subtrees.
|
|
bool before = omp_get_nested();
|
|
omp_set_nested(true);
|
|
_M_bulk_insertion_split_tree_by_pivot(static_cast<_Rb_tree_node_ptr>(base_type::_M_root()), r, access, beg_partition, rank_shift, 0, num_threads-1, conc, num_threads, strictly_less_or_less_equal);
|
|
omp_set_nested(before);
|
|
|
|
// Construct upper tree with the first elements of ranges if
|
|
// they are NULL We cannot do this by default because they could
|
|
// be repeated and would not be checked.
|
|
size_type r_s = 0;
|
|
for (int pos = 1; pos < num_threads; ++pos)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(conc[(pos-1)*2]->t == NULL or conc[pos*2-1]->t == NULL or strictly_less_or_less_equal(base_type::_S_key(base_type::_S_maximum(conc[(pos-1)*2]->t)), base_type::_S_key(conc[pos*2-1]->t)));
|
|
_GLIBCXX_PARALLEL_ASSERT(conc[pos*2]->t == NULL or conc[pos*2-1]->t == NULL or strictly_less_or_less_equal( base_type::_S_key(conc[pos*2-1]->t), base_type::_S_key(base_type::_S_minimum(conc[pos*2]->t))));
|
|
/***** Dealing with repetitions (CORRECTNESS ISSUE) *****/
|
|
|
|
// The first element of the range is the root.
|
|
if (conc[pos*2-1]->t == NULL or (not(strictly_less_or_less_equal(base_type::_S_key(static_cast<_Rb_tree_node_ptr>(conc[pos*2-1]->t)), _KeyOfValue()(*access[pos])))))
|
|
{
|
|
// There was not a candidate element
|
|
// or
|
|
// Exists an initialized position in the array which
|
|
// corresponds to conc[pos*2-1]->t */
|
|
if (conc[pos*2-1]->t == NULL)
|
|
{
|
|
size_t np = beg_partition[pos];
|
|
_GLIBCXX_PARALLEL_ASSERT(conc[(pos-1)*2]->t == NULL or strictly_less_or_less_equal(base_type::_S_key(base_type::_S_maximum(conc[(pos-1)*2]->t)), base_type::_S_key(r[np])));
|
|
_GLIBCXX_PARALLEL_ASSERT(conc[pos*2]->t == NULL or strictly_less_or_less_equal( base_type::_S_key(r[np]), base_type::_S_key(base_type::_S_minimum(conc[pos*2]->t))));
|
|
conc[pos*2-1]->t = r[np];
|
|
r[np]->_M_color = std::_S_black;
|
|
++base_type::_M_impl._M_node_count;
|
|
}
|
|
else
|
|
{
|
|
base_type::_M_destroy_node(r[beg_partition[pos]]);
|
|
}
|
|
++(access[pos]);
|
|
++(beg_partition[pos]);
|
|
++r_s;
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(conc[(pos-1)*2]->t == NULL or conc[(pos-1)*2]->t->_M_color == std::_S_black);
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
rank_shift[pos] += r_s;
|
|
}
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
rank_shift[num_threads] += r_s;
|
|
#else
|
|
concat_problem root_problem_on_stack(static_cast<_Rb_tree_node_ptr>(base_type::_M_root()), black_height(static_cast<_Rb_tree_node_ptr>(base_type::_M_root())), NULL);
|
|
concat_problem * root_problem = &root_problem_on_stack;
|
|
size_type last = k;
|
|
#endif
|
|
|
|
t.tic("sorted_no_gapped...");
|
|
|
|
// 3. Split the range according to tree and create
|
|
// 3. insertion/concatenation problems to be solved in parallel
|
|
#if _GLIBCXX_TREE_DYNAMIC_BALANCING
|
|
size_type min_problem = (k/num_threads) / (log2(k/num_threads + 1)+1);
|
|
#else
|
|
size_type min_problem = base_type::size() + k;
|
|
#endif
|
|
|
|
RestrictedBoundedConcurrentQueue<insertion_problem>* ins_problems[num_threads];
|
|
|
|
#pragma omp parallel num_threads(num_threads)
|
|
{
|
|
int num_thread = omp_get_thread_num();
|
|
ins_problems[num_thread] = new RestrictedBoundedConcurrentQueue<insertion_problem>(2*(log2(base_type::size())+1));
|
|
#if _GLIBCXX_TREE_INITIAL_SPLITTING
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
size_type end_k_thread = beg_partition[num_thread+1] - (rank_shift[num_thread+1] - rank_shift[num_thread]);
|
|
ins_problems[num_thread]->push_front(insertion_problem(beg_partition[num_thread], end_k_thread, num_thread, conc[num_thread*2]));
|
|
#else
|
|
// size_type end_k_thread = beg_partition[num_thread+1];
|
|
#endif
|
|
insertion_problem ip_to_solve;
|
|
bool change;
|
|
|
|
#if _GLIBCXX_TREE_INITIAL_SPLITTING
|
|
#pragma omp barrier
|
|
#else
|
|
#pragma omp single
|
|
ins_problems[num_thread]->push_front(insertion_problem(0, k, num_thread, root_problem));
|
|
#endif
|
|
|
|
do
|
|
{
|
|
// First do own work.
|
|
while (ins_problems[num_thread]->pop_front(ip_to_solve))
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(ip_to_solve.pos_beg <= ip_to_solve.pos_end);
|
|
_M_bulk_insertion_split_sequence(r, ins_problems[num_thread], ip_to_solve, existing[num_thread], min_problem, strictly_less_or_less_equal);
|
|
|
|
}
|
|
yield();
|
|
change = false;
|
|
|
|
//Then, try to steal from others (and become own).
|
|
for (int i=1; i<num_threads; ++i)
|
|
{
|
|
if (ins_problems[(num_thread+i)%num_threads]->pop_back(ip_to_solve))
|
|
{
|
|
change = true;
|
|
_M_bulk_insertion_split_sequence(r, ins_problems[num_thread], ip_to_solve, existing[num_thread], min_problem, strictly_less_or_less_equal);
|
|
break;
|
|
}
|
|
}
|
|
} while (change);
|
|
}
|
|
|
|
t.tic("merging");
|
|
|
|
// Update root and sizes.
|
|
base_type::_M_root() = root_problem->t;
|
|
root_problem->t->_M_parent = &(base_type::_M_impl._M_header);
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
|
|
// Add the k elements that wanted to be inserted, minus the ones
|
|
// that were repeated.
|
|
#if _GLIBCXX_TREE_INITIAL_SPLITTING
|
|
base_type::_M_impl._M_node_count += (k - (rank_shift[num_threads]));
|
|
#else
|
|
base_type::_M_impl._M_node_count += k;
|
|
#endif
|
|
// Also then, take out the ones that were already existing in the tree.
|
|
for (int i = 0; i< num_threads; ++i)
|
|
{
|
|
base_type::_M_impl._M_node_count -= existing[i];
|
|
}
|
|
// Update leftmost and rightmost.
|
|
/***** Dealing with repetitions (EFFICIENCY ISSUE) *****/
|
|
if (not strictly_less_or_less_equal(base_type::_S_key(base_type::_M_root()), base_type::_S_key(base_type::_M_root()))){
|
|
// Unique container.
|
|
if (base_type::_M_impl._M_key_compare(_KeyOfValue()(*(access[0])), base_type::_S_key(base_type::_M_leftmost())))
|
|
base_type::_M_leftmost() = r[0];
|
|
if (base_type::_M_impl._M_key_compare(base_type::_S_key(base_type::_M_rightmost()), _KeyOfValue()(*(--access[num_threads]))))
|
|
base_type::_M_rightmost() = r[last - 1];
|
|
}
|
|
else{
|
|
if (strictly_less_or_less_equal(_KeyOfValue()(*(access[0])), base_type::_S_key(base_type::_M_leftmost())))
|
|
base_type::_M_leftmost() = base_type::_S_minimum(base_type::_M_root());
|
|
if (strictly_less_or_less_equal(base_type::_S_key(base_type::_M_rightmost()), _KeyOfValue()(*(--access[num_threads]))))
|
|
base_type::_M_rightmost() = base_type::_S_maximum(base_type::_M_root());
|
|
}
|
|
|
|
|
|
|
|
|
|
#if _GLIBCXX_TREE_INITIAL_SPLITTING
|
|
// Delete root problem
|
|
delete root_problem;
|
|
#endif
|
|
|
|
// Delete queues
|
|
for (int pos = 0; pos < num_threads; ++pos)
|
|
{
|
|
delete ins_problems[pos];
|
|
}
|
|
|
|
// Delete array of pointers
|
|
::operator delete(r);
|
|
|
|
t.tic();
|
|
t.print();
|
|
}
|
|
|
|
|
|
/** @brief Divide a tree according to the splitter elements of a
|
|
* given sequence.
|
|
*
|
|
* The tree of the initial recursive call is divided in exactly
|
|
* num_threads partitions, some of which may be empty. Besides,
|
|
* some nodes may be extracted from it to afterwards concatenate
|
|
* the subtrees resulting from inserting the elements into it.
|
|
* This is done sequentially. It could be done in parallel but the
|
|
* performance is much worse.
|
|
* @param t Root of the tree to be split
|
|
* @param r Array of nodes to be inserted into the tree (here only
|
|
* used to look up its elements)
|
|
* @param access Array of iterators of size @c num_threads +
|
|
* 1. Each position contains the first value in the subsequence
|
|
* that has been copied into the corresponding tree node.
|
|
* @param beg_partition Array of positions of size @c num_threads
|
|
* + 1. Each position contains the rank of the first element in
|
|
* the array of nodes to be inserted.
|
|
* @param rank_shift Array of size @c num_threads + 1 containing
|
|
* the number of accumulated gaps at the beginning of each
|
|
* partition
|
|
* @param pos_beg First position in the access array to be
|
|
* considered to split @c t
|
|
* @param pos_end Last position (included) in the access array to
|
|
* be considered to split @c t
|
|
* @param conc Array of concatenation problems to be initialized
|
|
* @param num_threads Number of threads and corresponding
|
|
* subsequences in which the original sequence has been
|
|
* partitioned
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
*/
|
|
template<typename _Iterator, typename StrictlyLessOrLessEqual>
|
|
void
|
|
_M_bulk_insertion_split_tree_by_pivot(_Rb_tree_node_ptr t, _Rb_tree_node_ptr* r, _Iterator* access, size_type* beg_partition, size_type* rank_shift, const size_type pos_beg, const size_type pos_end, concat_problem** conc, const thread_index_t num_threads, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
if (pos_beg == pos_end)
|
|
{
|
|
//Elements are in [pos_beg, pos_end]
|
|
conc[pos_beg*2]->t = t;
|
|
conc[pos_beg*2]->black_h = black_height(t);
|
|
force_black_root (conc[pos_beg*2]->t, conc[pos_beg*2]->black_h);
|
|
return;
|
|
}
|
|
if (t == 0)
|
|
{
|
|
for (size_type i = pos_beg; i < pos_end; ++i)
|
|
{
|
|
conc[i*2]->t = NULL;
|
|
conc[i*2]->black_h = 0;
|
|
conc[i*2+1]->t = NULL;
|
|
}
|
|
conc[pos_end*2]->t = NULL;
|
|
conc[pos_end*2]->black_h = 0;
|
|
return;
|
|
}
|
|
|
|
// Return the last pos, in which key >= (pos-1).
|
|
// Search in the range [pos_beg, pos_end]
|
|
size_type pos = std::upper_bound(access + pos_beg, access + pos_end + 1, base_type::_S_key(t), compare_value_key<_Iterator, _Compare>(base_type::_M_impl._M_key_compare)) - access;
|
|
if (pos != pos_beg)
|
|
{
|
|
--pos;
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(pos == 0 or not base_type::_M_impl._M_key_compare(base_type::_S_key(t), _KeyOfValue()(*access[pos])));
|
|
|
|
|
|
_Rb_tree_node_ptr ll, lr;
|
|
int black_h_ll, black_h_lr;
|
|
_Rb_tree_node_ptr rl, rr;
|
|
int black_h_rl, black_h_rr;
|
|
|
|
if (pos != pos_beg)
|
|
{
|
|
_Rb_tree_node_ptr prev = r[beg_partition[pos] - 1 - (rank_shift[pos] - rank_shift[pos - 1])];
|
|
|
|
_GLIBCXX_PARALLEL_ASSERT(strictly_less_or_less_equal(base_type::_S_key(prev), _KeyOfValue()(*access[pos])));
|
|
|
|
split(static_cast<_Rb_tree_node_ptr>(t->_M_left),
|
|
static_cast<const key_type&>(_KeyOfValue()(*access[pos])),
|
|
static_cast<const key_type&>(base_type::_S_key(prev)),
|
|
conc[pos*2-1]->t, ll, lr, black_h_ll, black_h_lr,
|
|
strictly_less_or_less_equal);
|
|
|
|
_M_bulk_insertion_split_tree_by_pivot(ll, r, access, beg_partition, rank_shift, pos_beg, pos-1, conc,num_threads, strictly_less_or_less_equal);
|
|
}
|
|
else
|
|
{
|
|
lr = static_cast<_Rb_tree_node_ptr>(t->_M_left);
|
|
black_h_lr = black_height (lr);
|
|
force_black_root (lr, black_h_lr);
|
|
}
|
|
|
|
if (pos != pos_end)
|
|
{
|
|
_Rb_tree_node_ptr prev = r[beg_partition[pos+1] - 1 - (rank_shift[pos+1] - rank_shift[pos])];
|
|
|
|
_GLIBCXX_PARALLEL_ASSERT(not base_type::_M_impl._M_key_compare(_KeyOfValue()(*access[pos+1]), base_type::_S_key(prev)));
|
|
_GLIBCXX_PARALLEL_ASSERT(strictly_less_or_less_equal(base_type::_S_key(prev), _KeyOfValue()(*access[pos+1])));
|
|
|
|
split(static_cast<_Rb_tree_node_ptr>(t->_M_right),
|
|
static_cast<const key_type&>(_KeyOfValue()(*access[pos+1])),
|
|
static_cast<const key_type&>(base_type::_S_key(prev)),
|
|
conc[pos*2+1]->t, rl, rr, black_h_rl, black_h_rr,
|
|
strictly_less_or_less_equal);
|
|
|
|
_M_bulk_insertion_split_tree_by_pivot(rr, r, access, beg_partition, rank_shift, pos+1, pos_end, conc,num_threads, strictly_less_or_less_equal);
|
|
}
|
|
else
|
|
{
|
|
rl = static_cast<_Rb_tree_node_ptr>(t->_M_right);
|
|
black_h_rl = black_height (rl);
|
|
force_black_root (rl, black_h_rl);
|
|
}
|
|
|
|
// When key(t) is equal to key(access[pos]) and no other key in
|
|
// the left tree satisfies the criteria to be conc[pos*2-1]->t,
|
|
// key(t) must be assigned to it to avoid repetitions.
|
|
// Therefore, we do not have a root parameter for the
|
|
// concatenate function and a new concatenate function must be
|
|
// provided.
|
|
if (pos != pos_beg and conc[pos*2-1]->t == NULL and not strictly_less_or_less_equal(_KeyOfValue()(*access[pos]), base_type::_S_key(t)))
|
|
{
|
|
conc[pos*2-1]->t = t;
|
|
t = NULL;
|
|
}
|
|
concatenate(t, lr, rl, black_h_lr, black_h_rl, conc[pos*2]->t, conc[pos*2]->black_h);
|
|
}
|
|
|
|
/** @brief Divide the insertion problem until a leaf is reached or
|
|
* the problem is small.
|
|
*
|
|
* During the recursion, the right subproblem is queued, so that
|
|
* it can be handled by any thread. The left subproblem is
|
|
* divided recursively, and finally, solved right away
|
|
* sequentially.
|
|
* @param r Array of nodes containing the nodes to added into the tree
|
|
* @param ins_problems Pointer to a queue of insertion
|
|
* problems. The calling thread owns this queue, i. e. it is the
|
|
* only one to push elements, but other threads could pop elements
|
|
* from it in other methods.
|
|
* @param ip Current insertion problem to be solved
|
|
* @param existing Number of existing elements found when solving
|
|
* the insertion problem (out)
|
|
* @param min_problem Threshold size on the size of the insertion
|
|
* problem in which to stop recursion
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
*/
|
|
template<typename StrictlyLessOrLessEqual>
|
|
void
|
|
_M_bulk_insertion_split_sequence(_Rb_tree_node_ptr* r, RestrictedBoundedConcurrentQueue<insertion_problem>* ins_problems, insertion_problem& ip, size_type& existing, const size_type min_problem, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(ip.t == ip.conc->t);
|
|
if (ip.t == NULL or (ip.pos_end- ip.pos_beg) <= min_problem)
|
|
{
|
|
// SOLVE PROBLEM SEQUENTIALLY
|
|
// Start solving the problem.
|
|
_GLIBCXX_PARALLEL_ASSERT(ip.pos_beg <= ip.pos_end);
|
|
_M_bulk_insertion_merge_concatenate(r, ip, existing, strictly_less_or_less_equal);
|
|
return;
|
|
}
|
|
|
|
size_type pos_beg_right;
|
|
size_type pos_end_left = divide(r, ip.pos_beg, ip.pos_end, base_type::_S_key(ip.t), pos_beg_right, existing, strictly_less_or_less_equal);
|
|
|
|
int black_h_l, black_h_r;
|
|
if (ip.t->_M_color == std::_S_black)
|
|
{
|
|
black_h_l = black_h_r = ip.conc->black_h - 1;
|
|
}
|
|
else
|
|
{
|
|
black_h_l = black_h_r = ip.conc->black_h;
|
|
}
|
|
|
|
// Right problem into the queue.
|
|
ip.conc->right_problem = new concat_problem(static_cast<_Rb_tree_node_ptr>(ip.t->_M_right), black_h_r, ip.conc);
|
|
ip.conc->left_problem = new concat_problem(static_cast<_Rb_tree_node_ptr>(ip.t->_M_left), black_h_l, ip.conc);
|
|
|
|
ins_problems->push_front(insertion_problem(pos_beg_right, ip.pos_end, ip.array_partition, ip.conc->right_problem));
|
|
|
|
// Solve left problem.
|
|
insertion_problem ip_left(ip.pos_beg, pos_end_left, ip.array_partition, ip.conc->left_problem);
|
|
_M_bulk_insertion_split_sequence(r, ins_problems, ip_left, existing, min_problem, strictly_less_or_less_equal);
|
|
}
|
|
|
|
|
|
/** @brief Insert a sequence of elements into a tree using a
|
|
* divide-and-conquer scheme.
|
|
*
|
|
* The problem is solved recursively and sequentially dividing the
|
|
* sequence to be inserted according to the root of the tree. This
|
|
* is done until a leaf is reached or the proportion of elements
|
|
* to be inserted is small. Finally, the two resulting trees are
|
|
* concatenated.
|
|
* @param r_array Array of nodes containing the nodes to be added
|
|
* into the tree (among others)
|
|
* @param t Root of the tree
|
|
* @param pos_beg Position of the first node in the array of
|
|
* nodes to be inserted into the tree
|
|
* @param pos_end Position of the first node in the array of
|
|
* nodes that will not be inserted into the tree
|
|
* @param existing Number of existing elements found while
|
|
* inserting the range [@c pos_beg, @c pos_end) (out)
|
|
* @param black_h Height of the tree @c t and of the resulting
|
|
* tree after the recursive calls (in and out)
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
* @return Resulting tree after the elements have been inserted
|
|
*/
|
|
template<typename StrictlyLessOrLessEqual>
|
|
_Rb_tree_node_ptr
|
|
_M_bulk_insertion_merge(_Rb_tree_node_ptr* r_array, _Rb_tree_node_ptr t, const size_type pos_beg, const size_type pos_end, size_type& existing, int& black_h, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
#ifndef NDEBUG
|
|
int count;
|
|
#endif
|
|
_GLIBCXX_PARALLEL_ASSERT(pos_beg<=pos_end);
|
|
|
|
// Leaf: a tree with the range must be constructed. Returns its
|
|
// height in black nodes and its root (in ip.t) If there is
|
|
// nothing to insert, we still need the height for balancing.
|
|
if (t == NULL)
|
|
{
|
|
if (pos_end == pos_beg) return NULL;
|
|
t = simple_tree_construct(r_array,pos_beg, pos_end, black_h);
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify_tree(t,count));
|
|
return t;
|
|
}
|
|
if (pos_end == pos_beg)
|
|
return t;
|
|
if ((pos_end - pos_beg) <= (size_type)(black_h))
|
|
{
|
|
// Exponential size tree with respect the number of elements
|
|
// to be inserted.
|
|
for (size_type p = pos_beg; p < pos_end; ++p)
|
|
{
|
|
t = _M_insert_local(t, r_array[p], existing, black_h, strictly_less_or_less_equal);
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify_tree(t,count));
|
|
return t;
|
|
}
|
|
|
|
size_type pos_beg_right;
|
|
size_type pos_end_left = divide(r_array, pos_beg, pos_end, base_type::_S_key(t), pos_beg_right, existing, strictly_less_or_less_equal);
|
|
|
|
|
|
int black_h_l, black_h_r;
|
|
if (t->_M_color == std::_S_black)
|
|
{
|
|
black_h_l = black_h_r = black_h - 1;
|
|
}
|
|
else
|
|
{
|
|
black_h_l = black_h_r = black_h;
|
|
}
|
|
force_black_root(t->_M_left, black_h_l);
|
|
_Rb_tree_node_ptr l = _M_bulk_insertion_merge(r_array, static_cast<_Rb_tree_node_ptr>(t->_M_left), pos_beg, pos_end_left, existing, black_h_l, strictly_less_or_less_equal);
|
|
force_black_root(t->_M_right, black_h_r);
|
|
_Rb_tree_node_ptr r = _M_bulk_insertion_merge(r_array, static_cast<_Rb_tree_node_ptr>(t->_M_right), pos_beg_right, pos_end, existing, black_h_r, strictly_less_or_less_equal);
|
|
|
|
concatenate(t, l, r, black_h_l, black_h_r, t, black_h);
|
|
|
|
return t;
|
|
}
|
|
|
|
/** @brief Solve a given insertion problem and all the parent
|
|
* concatenation problem that are ready to be solved.
|
|
*
|
|
* First, solve an insertion problem.
|
|
|
|
* Then, check if it is possible to solve the parent
|
|
* concatenation problem. If this is the case, solve it and go
|
|
* up recursively, as far as possible. Quit otherwise.
|
|
*
|
|
* @param r Array of nodes containing the nodes to be added into
|
|
* the tree (among others)
|
|
* @param ip Insertion problem to solve initially.
|
|
* @param existing Number of existing elements found while
|
|
* inserting the range defined by the insertion problem (out)
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
*/
|
|
template<typename StrictlyLessOrLessEqual>
|
|
void
|
|
_M_bulk_insertion_merge_concatenate(_Rb_tree_node_ptr* r, insertion_problem& ip, size_type& existing, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
concat_problem* conc = ip.conc;
|
|
_GLIBCXX_PARALLEL_ASSERT(ip.pos_beg <= ip.pos_end);
|
|
|
|
conc->t = _M_bulk_insertion_merge(r, ip.t, ip.pos_beg, ip.pos_end, existing, conc->black_h, strictly_less_or_less_equal);
|
|
_GLIBCXX_PARALLEL_ASSERT(conc->t == NULL or conc->t->_M_color == std::_S_black);
|
|
|
|
bool is_ready = true;
|
|
while (conc->par_problem != NULL and is_ready)
|
|
{
|
|
// Pre: exists left and right problem, so there is not a deadlock
|
|
if (compare_and_swap(&conc->par_problem->is_ready, concat_problem::READY_NO, concat_problem::READY_YES))
|
|
is_ready = false;
|
|
|
|
if (is_ready)
|
|
{
|
|
conc = conc->par_problem;
|
|
_GLIBCXX_PARALLEL_ASSERT(conc->left_problem!=NULL and conc->right_problem!=NULL);
|
|
_GLIBCXX_PARALLEL_ASSERT (conc->left_problem->black_h >=0 and conc->right_problem->black_h>=0);
|
|
// Finished working with the problems.
|
|
concatenate(conc->t, conc->left_problem->t, conc->right_problem->t, conc->left_problem->black_h, conc->right_problem->black_h, conc->t, conc->black_h);
|
|
|
|
delete conc->left_problem;
|
|
delete conc->right_problem;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Begin of sorting, searching and related comparison-based helper methods.
|
|
|
|
/** @brief Check whether a random-access sequence is sorted, and
|
|
* calculate its size.
|
|
*
|
|
* @param __first Begin iterator of sequence.
|
|
* @param __last End iterator of sequence.
|
|
* @param dist Size of the sequence (out)
|
|
* @return sequence is sorted. */
|
|
template<typename _RandomAccessIterator>
|
|
bool
|
|
is_sorted_distance(const _RandomAccessIterator __first, const _RandomAccessIterator __last, size_type& dist, std::random_access_iterator_tag) const
|
|
{
|
|
gr_or_eq<_Compare, _RandomAccessIterator> geq(base_type::_M_impl._M_key_compare);
|
|
dist = __last - __first;
|
|
|
|
// In parallel.
|
|
return equal(__first + 1, __last, __first, geq);
|
|
}
|
|
|
|
/** @brief Check whether an input sequence is sorted, and
|
|
* calculate its size.
|
|
*
|
|
* The list partitioning tool is used so that all the work is
|
|
* done in only one traversal.
|
|
* @param __first Begin iterator of sequence.
|
|
* @param __last End iterator of sequence.
|
|
* @param dist Size of the sequence (out)
|
|
* @return sequence is sorted. */
|
|
template<typename _InputIterator>
|
|
bool
|
|
is_sorted_distance(const _InputIterator __first, const _InputIterator __last, size_type& dist, std::input_iterator_tag) const
|
|
{
|
|
dist = 1;
|
|
bool is_sorted = true;
|
|
_InputIterator it = __first;
|
|
_InputIterator prev = it++;
|
|
while (it != __last)
|
|
{
|
|
++dist;
|
|
if (base_type::_M_impl._M_key_compare(_KeyOfValue()(*it),_KeyOfValue()(*prev)))
|
|
{
|
|
is_sorted = false;
|
|
++it;
|
|
break;
|
|
}
|
|
prev = it;
|
|
++it;
|
|
}
|
|
while (it != __last)
|
|
{
|
|
++dist;
|
|
++it;
|
|
}
|
|
return is_sorted;
|
|
}
|
|
|
|
/** @brief Check whether a random-access sequence is sorted,
|
|
* calculate its size, and obtain intermediate accessors to the
|
|
* sequence to ease parallelization.
|
|
*
|
|
* @param __first Begin iterator of sequence.
|
|
* @param __last End iterator of sequence.
|
|
* @param access Array of size @c num_pieces + 1 that defines @c
|
|
* num_pieces subsequences of the original sequence (out). Each
|
|
* position @c i will contain an iterator to the first element in
|
|
* the subsequence @c i.
|
|
* @param beg_partition Array of size @c num_pieces + 1 that
|
|
* defines @c num_pieces subsequences of the original sequence
|
|
* (out). Each position @c i will contain the rank of the first
|
|
* element in the subsequence @c i.
|
|
* @param dist Size of the sequence (out)
|
|
* @param num_pieces Number of pieces to generate.
|
|
* @return Sequence is sorted. */
|
|
template<typename _RandomAccessIterator>
|
|
bool
|
|
is_sorted_distance_accessors(const _RandomAccessIterator __first, const _RandomAccessIterator __last, _RandomAccessIterator* access, size_type* beg_partition, size_type& dist, thread_index_t& num_pieces, std::random_access_iterator_tag) const
|
|
{
|
|
bool is_sorted = is_sorted_distance(__first, __last, dist,std::__iterator_category(__first));
|
|
if (dist < (unsigned int) num_pieces)
|
|
num_pieces = dist;
|
|
|
|
// Do it opposite way to use accessors in equal function???
|
|
range_accessors(__first,__last, access, beg_partition, dist, num_pieces, std::__iterator_category(__first));
|
|
return is_sorted;
|
|
}
|
|
|
|
/** @brief Check whether an input sequence is sorted, calculate
|
|
* its size, and obtain intermediate accessors to the sequence to
|
|
* ease parallelization.
|
|
*
|
|
* The list partitioning tool is used so that all the work is
|
|
* done in only one traversal.
|
|
* @param __first Begin iterator of sequence.
|
|
* @param __last End iterator of sequence.
|
|
* @param access Array of size @c num_pieces + 1 that defines @c
|
|
* num_pieces subsequences of the original sequence (out). Each
|
|
* position @c i will contain an iterator to the first element in
|
|
* the subsequence @c i.
|
|
* @param beg_partition Array of size @c num_pieces + 1 that
|
|
* defines @c num_pieces subsequences of the original sequence
|
|
* (out). Each position @c i will contain the rank of the first
|
|
* element in the subsequence @c i.
|
|
* @param dist Size of the sequence (out)
|
|
* @param num_pieces Number of pieces to generate.
|
|
* @return Sequence is sorted. */
|
|
template<typename _InputIterator>
|
|
bool
|
|
is_sorted_distance_accessors(const _InputIterator __first, const _InputIterator __last, _InputIterator* access, size_type* beg_partition, size_type& dist, thread_index_t& num_pieces, std::input_iterator_tag) const
|
|
{
|
|
is_sorted_functor<_InputIterator, _Compare> sorted(__first, base_type::_M_impl._M_key_compare);
|
|
dist = list_partition(__first, __last, access, (beg_partition+1), num_pieces, sorted, 0);
|
|
|
|
// Calculate the rank of the beginning each partition from the
|
|
// sequence sizes (what is stored at this point in beg_partition
|
|
// array).
|
|
beg_partition[0] = 0;
|
|
for (int i = 0; i < num_pieces; ++i)
|
|
{
|
|
beg_partition[i+1] += beg_partition[i];
|
|
}
|
|
|
|
return sorted.is_sorted();
|
|
}
|
|
|
|
/** @brief Make a full copy of the elements of a sequence
|
|
*
|
|
* The uninitialized_copy method from the STL is called in parallel
|
|
* using the access array to point to the beginning of each
|
|
* partition
|
|
* @param access Array of size @c num_threads + 1 that defines @c
|
|
* num_threads subsequences. Each position @c i contains an
|
|
* iterator to the first element in the subsequence @c i.
|
|
* @param beg_partition Array of size @c num_threads + 1 that
|
|
* defines @c num_threads subsequences. Each position @c i
|
|
* contains the rank of the first element in the subsequence @c
|
|
* i.
|
|
* @param out Begin iterator of output sequence.
|
|
* @param num_threads Number of threads to use. */
|
|
template<typename _InputIterator, typename _OutputIterator>
|
|
static void
|
|
uninitialized_copy_from_accessors(_InputIterator* access, size_type* beg_partition, _OutputIterator out, const thread_index_t num_threads)
|
|
{
|
|
#pragma omp parallel num_threads(num_threads)
|
|
{
|
|
int iam = omp_get_thread_num();
|
|
uninitialized_copy(access[iam], access[iam+1], out+beg_partition[iam]);
|
|
}
|
|
}
|
|
|
|
/** @brief Make a copy of the pointers of the elements of a sequence
|
|
* @param access Array of size @c num_threads + 1 that defines @c
|
|
* num_threads subsequences. Each position @c i contains an
|
|
* iterator to the first element in the subsequence @c i.
|
|
* @param beg_partition Array of size @c num_threads + 1 that
|
|
* defines @c num_threads subsequences. Each position @c i
|
|
* contains the rank of the first element in the subsequence @c
|
|
* i.
|
|
* @param out Begin iterator of output sequence.
|
|
* @param num_threads Number of threads to use. */
|
|
template<typename _InputIterator, typename _OutputIterator>
|
|
static void
|
|
uninitialized_ptr_copy_from_accessors(_InputIterator* access, size_type* beg_partition, _OutputIterator out, const thread_index_t num_threads)
|
|
{
|
|
#pragma omp parallel num_threads(num_threads)
|
|
{
|
|
int iam = omp_get_thread_num();
|
|
_OutputIterator itout = out + beg_partition[iam];
|
|
for (_InputIterator it = access[iam]; it != access[iam+1]; ++it)
|
|
{
|
|
*itout = &(*it);
|
|
++itout;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @brief Split a sorted node array in two parts according to a key.
|
|
*
|
|
* For unique containers, if the splitting key is in the array of
|
|
* nodes, the corresponding node is erased.
|
|
* @param r Array of nodes containing the nodes to split (among others)
|
|
* @param pos_beg Position of the first node in the array of
|
|
* nodes to be considered
|
|
* @param pos_end Position of the first node in the array of
|
|
* nodes to be not considered
|
|
* @param key Splitting key
|
|
* @param pos_beg_right Position of the first node in the
|
|
* resulting right partition (out)
|
|
* @param existing Number of existing elements before dividing
|
|
* (in) and after (out). Specifically, the counter is
|
|
* incremented by one for unique containers if the splitting key
|
|
* was already in the array of nodes.
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
* @return Position of the last node (not included) in the
|
|
* resulting left partition (out)
|
|
*/
|
|
template<typename StrictlyLessOrLessEqual>
|
|
size_type
|
|
divide(_Rb_tree_node_ptr* r, const size_type pos_beg, const size_type pos_end, const key_type& key, size_type& pos_beg_right, size_type& existing, StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
pos_beg_right = std::lower_bound(r + pos_beg, r + pos_end, key, compare_node_key<_Compare>(base_type::_M_impl._M_key_compare)) - r;
|
|
|
|
//Check if the element exists.
|
|
size_type pos_end_left = pos_beg_right;
|
|
|
|
// If r[pos_beg_right] is equal to key, must be erased
|
|
/***** Dealing with repetitions (CORRECTNESS ISSUE) *****/
|
|
_GLIBCXX_PARALLEL_ASSERT((pos_beg_right == pos_end) or not base_type::_M_impl._M_key_compare(base_type::_S_key(r[pos_beg_right]),key));
|
|
_GLIBCXX_PARALLEL_ASSERT((pos_beg_right + 1 >= pos_end) or strictly_less_or_less_equal(key, base_type::_S_key(r[pos_beg_right + 1])));
|
|
if (pos_beg_right != pos_end and not strictly_less_or_less_equal(key, base_type::_S_key(r[pos_beg_right])))
|
|
{
|
|
_M_destroy_node(r[pos_beg_right]);
|
|
r[pos_beg_right] = NULL;
|
|
++pos_beg_right;
|
|
++existing;
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(pos_end_left <= pos_beg_right and pos_beg_right <= pos_end and pos_end_left >= pos_beg);
|
|
return pos_end_left;
|
|
}
|
|
|
|
|
|
/** @brief Parallelization helper method: Given a random-access
|
|
sequence of known size, divide it into pieces of almost the
|
|
same size.
|
|
* @param __first Begin iterator of sequence.
|
|
* @param __last End iterator of sequence.
|
|
* @param access Array of size @c num_pieces + 1 that defines @c
|
|
* num_pieces subsequences. Each position @c i contains an
|
|
* iterator to the first element in the subsequence @c i.
|
|
* @param beg_partition Array of size @c num_pieces + 1 that
|
|
* defines @c num_pieces subsequences. Each position @c i
|
|
* contains the rank of the first element in the subsequence @c
|
|
* i.
|
|
* @param n Sequence size
|
|
* @param num_pieces Number of pieces. */
|
|
template<typename _RandomAccessIterator>
|
|
static void
|
|
range_accessors(const _RandomAccessIterator __first, const _RandomAccessIterator __last, _RandomAccessIterator* access, size_type* beg_partition, const size_type n, const thread_index_t num_pieces, std::random_access_iterator_tag)
|
|
{
|
|
access[0] = __first;
|
|
for (int i=1; i< num_pieces; ++i)
|
|
{
|
|
access[i] = access[i-1] + (__last-__first)/num_pieces;
|
|
beg_partition[i]= beg_partition[i-1]+ (__last-__first)/num_pieces;
|
|
}
|
|
beg_partition[num_pieces] = __last - access[num_pieces-1] + beg_partition[num_pieces-1];
|
|
access[num_pieces]= __last;
|
|
}
|
|
|
|
/** @brief Parallelization helper method: Given an input-access
|
|
sequence of known size, divide it into pieces of almost the
|
|
same size.
|
|
* @param __first Begin iterator of sequence.
|
|
* @param __last End iterator of sequence.
|
|
* @param access Array of size @c num_pieces + 1 that defines @c
|
|
* num_pieces subsequences. Each position @c i contains an
|
|
* iterator to the first element in the subsequence @c i.
|
|
* @param beg_partition Array of size @c num_pieces + 1 that
|
|
* defines @c num_pieces subsequences. Each position @c i
|
|
* contains the rank of the first element in the subsequence @c
|
|
* i.
|
|
* @param n Sequence size
|
|
* @param num_pieces Number of pieces. */
|
|
template<typename _InputIterator>
|
|
static void
|
|
range_accessors(const _InputIterator __first, const _InputIterator __last, _InputIterator* access, size_type* beg_partition, const size_type n, const thread_index_t num_pieces, std::input_iterator_tag)
|
|
{
|
|
access[0] = __first;
|
|
_InputIterator it= __first;
|
|
for (int i=1; i< num_pieces; ++i)
|
|
{
|
|
for (int j=0; j< n/num_pieces; ++j)
|
|
++it;
|
|
access[i] = it;
|
|
beg_partition[i]= n/num_pieces + beg_partition[i-1];
|
|
}
|
|
access[num_pieces] = __last;
|
|
beg_partition[num_pieces] = n - (num_pieces-1)*(n/num_pieces) + beg_partition[num_pieces-1];
|
|
}
|
|
|
|
/** @brief Initialize an array of concatenation problems for bulk
|
|
insertion. They are linked as a tree with (end - beg) leaves.
|
|
* @param conc Array of concatenation problems pointers to initialize.
|
|
* @param beg Rank of the first leave to initialize
|
|
* @param end Rank of the last (not included) leave to initialize
|
|
* @param parent Pointer to the parent concatenation problem.
|
|
*/
|
|
static concat_problem*
|
|
_M_bulk_insertion_initialize_upper_problems(concat_problem** conc, const int beg, const int end, concat_problem* parent)
|
|
{
|
|
if (beg + 1 == end)
|
|
{
|
|
conc[2*beg]->par_problem = parent;
|
|
return conc[2*beg];
|
|
}
|
|
|
|
int size = end - beg;
|
|
int mid = beg + size/2;
|
|
conc[2*mid-1]->par_problem = parent;
|
|
conc[2*mid-1]->left_problem = _M_bulk_insertion_initialize_upper_problems(conc, beg, mid, conc[2*mid-1]);
|
|
conc[2*mid-1]->right_problem = _M_bulk_insertion_initialize_upper_problems(conc, mid, end, conc[2*mid-1]);
|
|
return conc[2*mid-1];
|
|
}
|
|
|
|
|
|
/** @brief Determine black height of a node recursively.
|
|
* @param t Node.
|
|
* @return Black height of the node. */
|
|
static int
|
|
black_height(const _Rb_tree_node_ptr t)
|
|
{
|
|
if (t == NULL)
|
|
return 0;
|
|
int bh = black_height (static_cast<const _Rb_tree_node_ptr> (t->_M_left));
|
|
if (t->_M_color == std::_S_black)
|
|
++bh;
|
|
return bh;
|
|
}
|
|
|
|
/** @brief Color a leaf black
|
|
* @param t Leaf pointer.
|
|
* @param black_h Black height of @c t (out) */
|
|
static void
|
|
make_black_leaf(const _Rb_tree_node_ptr t, int& black_h)
|
|
{
|
|
black_h = 0;
|
|
if (t != NULL)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(t->_M_left == NULL and t->_M_right == NULL);
|
|
black_h = 1;
|
|
t->_M_color = std::_S_black;
|
|
}
|
|
}
|
|
|
|
/** @brief Color a node black.
|
|
* @param t Node to color black.
|
|
* @param black_h Black height of @c t (out) */
|
|
static void
|
|
make_leaf(const _Rb_tree_node_ptr t, int& black_h)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(t != NULL);
|
|
black_h = 1;
|
|
t->_M_color = std::_S_black;
|
|
t->_M_left = NULL;
|
|
t->_M_right = NULL;
|
|
}
|
|
|
|
/** @brief Construct a tree from a root, a left subtree and a
|
|
right subtree.
|
|
* @param root Root of constructed tree.
|
|
* @param l Root of left subtree.
|
|
* @param r Root of right subtree.
|
|
* @pre @c l, @c r are black.
|
|
*/
|
|
template<typename S>
|
|
static _Rb_tree_node_ptr
|
|
plant(const _Rb_tree_node_ptr root, const _Rb_tree_node_ptr l,
|
|
const _Rb_tree_node_ptr r)
|
|
{
|
|
S::left(root) = l;
|
|
S::right(root) = r;
|
|
if (l != NULL)
|
|
l->_M_parent = root;
|
|
if (r != NULL)
|
|
r->_M_parent = root;
|
|
root->_M_color = std::_S_red;
|
|
return root;
|
|
}
|
|
|
|
/** @brief Concatenate two red-black subtrees using and an
|
|
intermediate node, which might be NULL
|
|
* @param root Intermediate node.
|
|
* @param l Left subtree.
|
|
* @param r Right subtree.
|
|
* @param black_h_l Black height of left subtree.
|
|
* @param black_h_r Black height of right subtree.
|
|
* @param t Tree resulting of the concatenation
|
|
* @param black_h Black height of the resulting tree
|
|
* @pre Left tree is higher than left tree
|
|
* @post @c t is correct red-black tree with height @c black_h.
|
|
*/
|
|
void
|
|
concatenate(_Rb_tree_node_ptr root, _Rb_tree_node_ptr l,
|
|
_Rb_tree_node_ptr r, int black_h_l, int black_h_r,
|
|
_Rb_tree_node_ptr& t, int& black_h) const
|
|
{
|
|
#ifndef NDEBUG
|
|
int count = 0, count1 = 0, count2 = 0;
|
|
#endif
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify_tree(l, count1));
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify_tree(r, count2));
|
|
|
|
_GLIBCXX_PARALLEL_ASSERT(l != NULL ? l->_M_color != std::_S_red and black_h_l > 0 : black_h_l == 0);
|
|
_GLIBCXX_PARALLEL_ASSERT(r != NULL ? r->_M_color != std::_S_red and black_h_r > 0 : black_h_r == 0);
|
|
|
|
if (black_h_l > black_h_r)
|
|
if (root != NULL)
|
|
concatenate<LeftRight>(root, l, r, black_h_l, black_h_r, t, black_h);
|
|
else
|
|
{
|
|
if (r == NULL)
|
|
{
|
|
t = l;
|
|
black_h = black_h_l;
|
|
}
|
|
else
|
|
{
|
|
// XXX SHOULD BE the same as extract_min but slower.
|
|
/*
|
|
root = static_cast<_Rb_tree_node_ptr>(_Rb_tree_node_base::_S_minimum(r));
|
|
split(r, _S_key(_Rb_tree_increment(root)), _S_key(root), root, t, r, black_h, black_h_r);
|
|
*/
|
|
extract_min(r, root, r, black_h_r);
|
|
_GLIBCXX_PARALLEL_ASSERT(root != NULL);
|
|
concatenate<LeftRight>(root, l, r, black_h_l, black_h_r, t, black_h);
|
|
}
|
|
}
|
|
else
|
|
if (root != NULL)
|
|
concatenate<RightLeft>(root, r, l, black_h_r, black_h_l, t, black_h);
|
|
else
|
|
{
|
|
if (l == NULL)
|
|
{
|
|
t = r;
|
|
black_h = black_h_r;
|
|
}
|
|
else
|
|
{
|
|
// XXX SHOULD BE the same as extract_max but slower
|
|
/*
|
|
root = static_cast<_Rb_tree_node_ptr>(_Rb_tree_node_base::_S_maximum(l));
|
|
split(l, _S_key(root), _S_key(_Rb_tree_decrement(root)), root, l, t, black_h_l, black_h);
|
|
*/
|
|
extract_max(l, root, l, black_h_l);
|
|
_GLIBCXX_PARALLEL_ASSERT(root != NULL);
|
|
concatenate<RightLeft>(root, r, l, black_h_r, black_h_l, t, black_h);
|
|
}
|
|
}
|
|
#ifndef NDEBUG
|
|
if (root!=NULL) ++count1;
|
|
_GLIBCXX_PARALLEL_ASSERT(t == NULL or t->_M_color == std::_S_black);
|
|
bool b = rb_verify_tree(t, count);
|
|
if (not b){
|
|
_GLIBCXX_PARALLEL_ASSERT(false);
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(count1+count2 == count);
|
|
#endif
|
|
}
|
|
|
|
/** @brief Concatenate two red-black subtrees using and a not NULL
|
|
* intermediate node.
|
|
*
|
|
* @c S is the symmetry parameter.
|
|
* @param rt Intermediate node.
|
|
* @param l Left subtree.
|
|
* @param r Right subtree.
|
|
* @param black_h_l Black height of left subtree.
|
|
* @param black_h_r Black height of right subtree.
|
|
* @param t Tree resulting of the concatenation
|
|
* @param black_h Black height of the resulting tree
|
|
* @pre Left tree is higher than right tree. @c rt != NULL
|
|
* @post @c t is correct red-black tree with height @c black_h.
|
|
*/
|
|
template<typename S>
|
|
static void
|
|
concatenate(const _Rb_tree_node_ptr rt, _Rb_tree_node_ptr l,
|
|
_Rb_tree_node_ptr r, int black_h_l, int black_h_r,
|
|
_Rb_tree_node_ptr& t, int& black_h)
|
|
{
|
|
_Rb_tree_node_base* root = l;
|
|
_Rb_tree_node_ptr parent = NULL;
|
|
black_h = black_h_l;
|
|
_GLIBCXX_PARALLEL_ASSERT(black_h_l >= black_h_r);
|
|
while (black_h_l != black_h_r)
|
|
{
|
|
if (l->_M_color == std::_S_black)
|
|
--black_h_l;
|
|
parent = l;
|
|
l = static_cast<_Rb_tree_node_ptr>(S::right(l));
|
|
_GLIBCXX_PARALLEL_ASSERT((black_h_l == 0 and (l == NULL or l->_M_color == std::_S_red)) or (black_h_l != 0 and l != NULL));
|
|
_GLIBCXX_PARALLEL_ASSERT((black_h_r == 0 and (r == NULL or r->_M_color == std::_S_red)) or (black_h_r != 0 and r != NULL));
|
|
}
|
|
if (l != NULL and l->_M_color == std::_S_red)
|
|
{
|
|
//the root needs to be black
|
|
parent = l;
|
|
l = static_cast<_Rb_tree_node_ptr>(S::right(l));
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(l != NULL ? l->_M_color == std::_S_black : true);
|
|
_GLIBCXX_PARALLEL_ASSERT(r != NULL ? r->_M_color == std::_S_black : true);
|
|
t = plant<S>(rt, l, r);
|
|
t->_M_parent = parent;
|
|
if (parent != NULL)
|
|
{
|
|
S::right(parent) = t;
|
|
black_h += _Rb_tree_rebalance(t, root);
|
|
t = static_cast<_Rb_tree_node_ptr> (root);
|
|
}
|
|
else
|
|
{
|
|
++black_h;
|
|
t->_M_color = std::_S_black;
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(t->_M_color == std::_S_black);
|
|
}
|
|
|
|
/** @brief Split a tree according to key in three parts: a left
|
|
* child, a right child and an intermediate node.
|
|
*
|
|
* Trees are concatenated once the recursive call returns. That
|
|
* is, from bottom to top (i. e. smaller to larger), so the cost
|
|
* bounds for split hold.
|
|
* @param t Root of the tree to split.
|
|
* @param key Key to split according to.
|
|
* @param prev_k Key to split the intermediate node
|
|
* @param root Out parameter. If a node exists whose key is
|
|
* smaller or equal than @c key, but strictly larger than @c
|
|
* prev_k, this is returned. Otherwise, it is null.
|
|
* @param l Root of left subtree returned, nodes less than @c key.
|
|
* @param r Root of right subtree returned, nodes greater or
|
|
* equal than @c key.
|
|
* @param black_h_l Black height of the left subtree.
|
|
* @param black_h_r Black height of the right subtree.
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
* @return Black height of t */
|
|
template<typename StrictlyLessOrEqual>
|
|
int
|
|
split(_Rb_tree_node_ptr t, const key_type& key, const key_type& prev_k,
|
|
_Rb_tree_node_ptr& root, _Rb_tree_node_ptr& l, _Rb_tree_node_ptr& r,
|
|
int& black_h_l, int& black_h_r,
|
|
StrictlyLessOrEqual strictly_less_or_less_equal) const
|
|
{
|
|
if (t != NULL)
|
|
{
|
|
// Must be initialized, in case we never go left!!!
|
|
root = NULL;
|
|
int h = split_not_null(t, key, prev_k, root, l, r, black_h_l, black_h_r, strictly_less_or_less_equal);
|
|
#ifndef NDEBUG
|
|
_GLIBCXX_PARALLEL_ASSERT(l == NULL or base_type::_M_impl._M_key_compare(base_type::_S_key(base_type::_S_maximum(l)),key));
|
|
_GLIBCXX_PARALLEL_ASSERT(r == NULL or not base_type::_M_impl._M_key_compare(base_type::_S_key(base_type::_S_minimum(r)),key));
|
|
int count1, count2;
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify_tree(l, count1));
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify_tree(r, count2));
|
|
_GLIBCXX_PARALLEL_ASSERT(root == NULL or base_type::_M_impl._M_key_compare(prev_k, base_type::_S_key(root)) and not base_type::_M_impl._M_key_compare(key, base_type::_S_key(root)));
|
|
_GLIBCXX_PARALLEL_ASSERT(root != NULL or l==NULL or not base_type::_M_impl._M_key_compare(prev_k, base_type::_S_key(base_type::_S_maximum(l))));
|
|
#endif
|
|
return h;
|
|
}
|
|
|
|
r = NULL;
|
|
root = NULL;
|
|
l = NULL;
|
|
black_h_r = 0;
|
|
black_h_l = 0;
|
|
return 0;
|
|
}
|
|
|
|
/** @brief Split a tree according to key in three parts: a left
|
|
* child, a right child and an intermediate node.
|
|
*
|
|
* @param t Root of the tree to split.
|
|
* @param key Key to split according to.
|
|
* @param prev_k Key to split the intermediate node
|
|
* @param root Out parameter. If a node exists whose key is
|
|
* smaller or equal than @c key, but strictly larger than @c
|
|
* prev_k, this is returned. Otherwise, it is null.
|
|
* @param l Root of left subtree returned, nodes less than @c key.
|
|
* @param r Root of right subtree returned, nodes greater or
|
|
* equal than @c key.
|
|
* @param black_h_l Black height of the left subtree.
|
|
* @param black_h_r Black height of the right subtree.
|
|
* @param strictly_less_or_equal Comparator to deal transparently
|
|
* with repetitions with respect to the uniqueness of the
|
|
* wrapping container
|
|
* @pre t != NULL
|
|
* @return Black height of t */
|
|
template<typename StrictlyLessOrEqual>
|
|
int
|
|
split_not_null(const _Rb_tree_node_ptr t, const key_type& key,
|
|
const key_type& prev_k, _Rb_tree_node_ptr& root,
|
|
_Rb_tree_node_ptr& l, _Rb_tree_node_ptr& r, int& black_h_l,
|
|
int& black_h_r,
|
|
StrictlyLessOrEqual strictly_less_or_equal) const
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT (t != NULL);
|
|
int black_h, b_h;
|
|
int black_node = 0;
|
|
if (t->_M_color == std::_S_black)
|
|
++black_node;
|
|
if (strictly_less_or_equal(key, base_type::_S_key(t)))
|
|
{
|
|
if (t->_M_left != NULL )
|
|
{
|
|
// t->M_right is at most one node
|
|
// go to the left
|
|
b_h = black_h = split_not_null( static_cast<_Rb_tree_node_ptr>(t->_M_left), key, prev_k, root, l, r, black_h_l, black_h_r, strictly_less_or_equal);
|
|
// Moin root and right subtree to already existing right
|
|
// half, leave left subtree.
|
|
force_black_root(t->_M_right, b_h);
|
|
concatenate(t, r, static_cast<_Rb_tree_node_ptr>(t->_M_right), black_h_r, b_h, r, black_h_r);
|
|
}
|
|
else
|
|
{
|
|
// t->M_right is at most one node
|
|
r = t;
|
|
black_h_r = black_node;
|
|
force_black_root(r, black_h_r);
|
|
|
|
black_h = 0;
|
|
l = NULL;
|
|
black_h_l = 0;
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(l == NULL or base_type::_M_impl._M_key_compare(base_type::_S_key(base_type::_S_maximum(l)),key));
|
|
_GLIBCXX_PARALLEL_ASSERT(r == NULL or not base_type::_M_impl._M_key_compare(base_type::_S_key(base_type::_S_minimum(r)),key));
|
|
}
|
|
else
|
|
{
|
|
if (t->_M_right != NULL )
|
|
{
|
|
// Go to the right.
|
|
if (strictly_less_or_equal(prev_k, base_type::_S_key(t)))
|
|
root = t;
|
|
b_h = black_h = split_not_null(static_cast<_Rb_tree_node_ptr>(t->_M_right), key, prev_k, root, l, r, black_h_l, black_h_r, strictly_less_or_equal);
|
|
// Join root and left subtree to already existing left
|
|
// half, leave right subtree.
|
|
force_black_root(t->_M_left, b_h);
|
|
if (root != t)
|
|
{
|
|
// There was another point where we went right.
|
|
concatenate(t, static_cast<_Rb_tree_node_ptr>(t->_M_left), l, b_h, black_h_l, l, black_h_l);
|
|
}
|
|
else
|
|
{
|
|
l = static_cast<_Rb_tree_node_ptr>(t->_M_left);
|
|
black_h_l = b_h;
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(l == NULL or base_type::_M_impl._M_key_compare(base_type::_S_key(base_type::_S_maximum(l)),key));
|
|
_GLIBCXX_PARALLEL_ASSERT(r == NULL or not base_type::_M_impl._M_key_compare(base_type::_S_key(base_type::_S_minimum(r)),key));
|
|
}
|
|
else
|
|
{
|
|
if (strictly_less_or_equal(prev_k, base_type::_S_key(t)))
|
|
{
|
|
root = t;
|
|
l= static_cast<_Rb_tree_node_ptr>(t->_M_left);
|
|
make_black_leaf(l, black_h_l);
|
|
_GLIBCXX_PARALLEL_ASSERT(l == NULL or base_type::_M_impl._M_key_compare(base_type::_S_key(base_type::_S_maximum(l)),key));
|
|
}
|
|
else
|
|
{
|
|
l= t;
|
|
black_h_l = black_node;
|
|
force_black_root(l, black_h_l);
|
|
_GLIBCXX_PARALLEL_ASSERT(l == NULL or base_type::_M_impl._M_key_compare(base_type::_S_key(base_type::_S_maximum(l)),key));
|
|
}
|
|
|
|
r = NULL;
|
|
black_h = 0;
|
|
black_h_r = 0;
|
|
}
|
|
}
|
|
return black_h + black_node;
|
|
}
|
|
|
|
/** @brief Color the root black and update the black height accordingly.
|
|
*
|
|
* @param t Root of the tree.
|
|
* @param black_h Black height of the tree @c t (out) */
|
|
static void force_black_root(_Rb_tree_node_base* t, int& black_h)
|
|
{
|
|
if (t != NULL and t->_M_color == std::_S_red)
|
|
{
|
|
t->_M_color = std::_S_black;
|
|
++ black_h;
|
|
}
|
|
}
|
|
|
|
/** @brief Split the tree in two parts: the minimum element from a
|
|
tree (i. e. leftmost) and the rest (right subtree)
|
|
* @param t Root of the tree
|
|
* @param root Minimum element (out)
|
|
* @param r Right subtree: @c t - {@c root}
|
|
* @param black_h_r Black height of the right subtree.
|
|
* @return Black height of the original tree */
|
|
int
|
|
extract_min(const _Rb_tree_node_ptr t, _Rb_tree_node_ptr& root,
|
|
_Rb_tree_node_ptr& r, int& black_h_r) const
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT (t != NULL);
|
|
int black_h, b_h;
|
|
int black_node = 0;
|
|
if (t->_M_color == std::_S_black)
|
|
++black_node;
|
|
|
|
if (t->_M_left != NULL )
|
|
{
|
|
// t->M_right is at most one node
|
|
// go to the left
|
|
b_h = black_h = extract_min( static_cast<_Rb_tree_node_ptr>(t->_M_left), root, r, black_h_r);
|
|
|
|
// Join root and right subtree to already existing right
|
|
// half, leave left subtree
|
|
force_black_root(t->_M_right, b_h);
|
|
concatenate(t, r, static_cast<_Rb_tree_node_ptr>(t->_M_right), black_h_r, b_h, r, black_h_r);
|
|
}
|
|
else
|
|
{
|
|
// t->M_right is at most one node
|
|
root = t;
|
|
if (t->_M_right == NULL)
|
|
{
|
|
r = NULL;
|
|
black_h_r = 0;
|
|
}
|
|
else
|
|
{
|
|
r = static_cast<_Rb_tree_node_ptr>(t->_M_right);
|
|
black_h_r = 1;
|
|
r->_M_color = std::_S_black;
|
|
}
|
|
black_h = 0;
|
|
}
|
|
return black_h + black_node;
|
|
}
|
|
|
|
|
|
/** @brief Split the tree in two parts: the greatest element from
|
|
a tree (i. e. rightmost) and the rest (left subtree)
|
|
* @param t Root of the tree
|
|
* @param root Maximum element (out)
|
|
* @param l Left subtree: @c t - {@c root}
|
|
* @param black_h_l Black height of the left subtree.
|
|
* @return Black height of the original tree */
|
|
int
|
|
extract_max(const _Rb_tree_node_ptr t, _Rb_tree_node_ptr& root,
|
|
_Rb_tree_node_ptr& l, int& black_h_l) const
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT (t != NULL);
|
|
int black_h, b_h;
|
|
int black_node = 0;
|
|
if (t->_M_color == std::_S_black)
|
|
++black_node;
|
|
|
|
if (t->_M_right != NULL )
|
|
{
|
|
b_h = black_h = extract_max(static_cast<_Rb_tree_node_ptr>(t->_M_right), root, l, black_h_l);
|
|
|
|
// Join root and left subtree to already existing left half,
|
|
// leave right subtree.
|
|
force_black_root(t->_M_left, b_h);
|
|
|
|
concatenate(t, static_cast<_Rb_tree_node_ptr>(t->_M_left), l, b_h, black_h_l, l, black_h_l);
|
|
}
|
|
else
|
|
{
|
|
root = t;
|
|
if (t->_M_left == NULL)
|
|
{
|
|
l = NULL;
|
|
black_h_l = 0;
|
|
}
|
|
else
|
|
{
|
|
l = static_cast<_Rb_tree_node_ptr>(t->_M_left);
|
|
black_h_l = 1;
|
|
l->_M_color = std::_S_black;
|
|
}
|
|
black_h = 0;
|
|
}
|
|
return black_h + black_node;
|
|
}
|
|
|
|
/** @brief Split tree according to key in two parts: a left tree
|
|
* and a right subtree
|
|
*
|
|
* Trees are concatenated once the recursive call returns. That
|
|
* is, from bottom to top (i. e. smaller to larger), so the cost
|
|
* bounds for split hold.
|
|
* @param t Root of the tree to split.
|
|
* @param key Key to split according to.
|
|
* @param l Root of left subtree returned, nodes less than @c key.
|
|
* @param r Root of right subtree returned, nodes greater than @c key.
|
|
* @param black_h_l Black height of the left subtree.
|
|
* @param black_h_r Black height of the right subtree.
|
|
* @return Black height of the original tree */
|
|
int
|
|
split(const _Rb_tree_node_ptr t, const key_type& key,
|
|
_Rb_tree_node_ptr& l, _Rb_tree_node_ptr& r, int& black_h_l,
|
|
int& black_h_r) const
|
|
{
|
|
if (t != NULL)
|
|
{
|
|
int black_h, b_h;
|
|
int black_node = 0;
|
|
if (t->_M_color == std::_S_black)
|
|
++black_node;
|
|
if (not (base_type::_M_impl._M_key_compare(base_type::_S_key(t), key)))
|
|
{
|
|
// Go to the left.
|
|
b_h = black_h = split( static_cast<_Rb_tree_node_ptr>(t->_M_left), key, l, r, black_h_l, black_h_r);
|
|
|
|
// Join root and right subtree to already existing right
|
|
// half, leave left subtree.
|
|
force_black_root(t->_M_right, b_h);
|
|
concatenate(t, r, static_cast<_Rb_tree_node_ptr>(t->_M_right), black_h_r, b_h, r, black_h_r);
|
|
}
|
|
else
|
|
{
|
|
// Go to the right.
|
|
b_h = black_h = split(static_cast<_Rb_tree_node_ptr>(t->_M_right), key, l, r, black_h_l, black_h_r);
|
|
|
|
// Join root and left subtree to already existing left
|
|
// half, leave right subtree.
|
|
force_black_root(t->_M_left, b_h);
|
|
concatenate(t, static_cast<_Rb_tree_node_ptr>(t->_M_left), l, b_h, black_h_l, l, black_h_l);
|
|
}
|
|
return black_h + black_node;
|
|
}
|
|
else
|
|
{
|
|
r = NULL;
|
|
l = NULL;
|
|
black_h_r = 0;
|
|
black_h_l = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/** @brief Insert an existing node in tree and rebalance it, if
|
|
* appropriate.
|
|
*
|
|
* The keyword "local" is used because no attributes of the
|
|
* red-black tree are changed, so this insertion is not yet seen
|
|
* by the global data structure.
|
|
* @param t Root of tree to insert into.
|
|
* @param new_t Existing node to insert.
|
|
* @param existing Number of existing elements before insertion
|
|
* (in) and after (out). Specifically, the counter is incremented
|
|
* by one for unique containers if the key of new_t was already
|
|
* in the tree.
|
|
* @param black_h Black height of the resulting tree (out)
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
* @return Resulting tree after insertion */
|
|
template<typename StrictlyLessOrLessEqual>
|
|
_Rb_tree_node_ptr
|
|
_M_insert_local(_Rb_tree_node_base* t, const _Rb_tree_node_ptr new_t,
|
|
size_type& existing, int& black_h,
|
|
StrictlyLessOrLessEqual strictly_less_or_less_equal)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(t != NULL);
|
|
if (_M_insert_local_top_down(t, new_t, NULL, NULL, true, strictly_less_or_less_equal))
|
|
{
|
|
t->_M_parent = NULL;
|
|
black_h += _Rb_tree_rebalance(new_t, t);
|
|
_GLIBCXX_PARALLEL_ASSERT(t->_M_color == std::_S_black);
|
|
return static_cast<_Rb_tree_node_ptr>(t);
|
|
}
|
|
else
|
|
{
|
|
base_type::_M_destroy_node(new_t);
|
|
++existing;
|
|
force_black_root(t, black_h);
|
|
return static_cast<_Rb_tree_node_ptr>(t);
|
|
}
|
|
}
|
|
|
|
/***** Dealing with repetitions (CORRECTNESS ISSUE) *****/
|
|
/** @brief Insert an existing node in tree, do no rebalancing.
|
|
* @param t Root of tree to insert into.
|
|
* @param new_t Existing node to insert.
|
|
* @param eq_t Node candidate to be equal than new_t, only
|
|
* relevant for unique containers
|
|
* @param parent Parent node of @c t
|
|
* @param is_left True if @c t is a left child of @c
|
|
* parent. False otherwise.
|
|
* @param strictly_less_or_less_equal Comparator to deal
|
|
* transparently with repetitions with respect to the uniqueness
|
|
* of the wrapping container
|
|
|
|
* @return Success of the insertion
|
|
*/
|
|
template<typename StrictlyLessOrLessEqual>
|
|
bool
|
|
_M_insert_local_top_down(_Rb_tree_node_base* t,
|
|
const _Rb_tree_node_ptr new_t,
|
|
_Rb_tree_node_base* eq_t,
|
|
_Rb_tree_node_base* parent, const bool is_left,
|
|
StrictlyLessOrLessEqual strictly_less_or_less_equal) const
|
|
{
|
|
if (t != NULL)
|
|
{
|
|
if (strictly_less_or_less_equal(_S_key(new_t), _S_key(static_cast<_Rb_tree_node_ptr>(t))))
|
|
{
|
|
return _M_insert_local_top_down(t->_M_left, new_t, eq_t, t, true, strictly_less_or_less_equal);
|
|
}
|
|
else
|
|
{
|
|
return _M_insert_local_top_down(t->_M_right, new_t, t, t, false, strictly_less_or_less_equal);
|
|
}
|
|
}
|
|
|
|
_GLIBCXX_PARALLEL_ASSERT(parent != NULL);
|
|
|
|
// Base case.
|
|
if (eq_t == NULL or strictly_less_or_less_equal(_S_key(static_cast<_Rb_tree_node_ptr>(eq_t)), _S_key(new_t)))
|
|
{
|
|
// The element to be inserted did not existed.
|
|
if (is_left)
|
|
{
|
|
parent->_M_left = new_t;
|
|
}
|
|
else
|
|
{
|
|
parent->_M_right = new_t;
|
|
}
|
|
|
|
new_t->_M_parent = parent;
|
|
new_t->_M_left = NULL;
|
|
new_t->_M_right = NULL;
|
|
new_t->_M_color = std::_S_red;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/** @brief Rebalance a tree locally.
|
|
*
|
|
* Essentially, it is the same function as insert_erase from the
|
|
* base class, but without the insertion and without using any
|
|
* tree attributes.
|
|
* @param __x Root of the current subtree to rebalance.
|
|
* @param __root Root of tree where @c __x is in (rebalancing
|
|
* stops when root is reached)
|
|
* @return Increment in the black height after rebalancing
|
|
*/
|
|
static int
|
|
_Rb_tree_rebalance(_Rb_tree_node_base* __x, _Rb_tree_node_base*& __root)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(__root->_M_color == std::_S_black);
|
|
// Rebalance.
|
|
while (__x != __root and __x->_M_parent != __root and
|
|
__x->_M_parent->_M_color == std::_S_red)
|
|
{
|
|
_Rb_tree_node_base* const __xpp = __x->_M_parent->_M_parent;
|
|
|
|
if (__x->_M_parent == __xpp->_M_left)
|
|
{
|
|
_Rb_tree_node_base* const __y = __xpp->_M_right;
|
|
if (__y && __y->_M_color == std::_S_red)
|
|
{
|
|
__x->_M_parent->_M_color = std::_S_black;
|
|
__y->_M_color = std::_S_black;
|
|
__xpp->_M_color = std::_S_red;
|
|
__x = __xpp;
|
|
}
|
|
else
|
|
{
|
|
if (__x == __x->_M_parent->_M_right)
|
|
{
|
|
__x = __x->_M_parent;
|
|
std::_Rb_tree_rotate_left(__x, __root);
|
|
}
|
|
__x->_M_parent->_M_color = std::_S_black;
|
|
__xpp->_M_color = std::_S_red;
|
|
std::_Rb_tree_rotate_right(__xpp, __root);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_Rb_tree_node_base* const __y = __xpp->_M_left;
|
|
if (__y && __y->_M_color == std::_S_red)
|
|
{
|
|
__x->_M_parent->_M_color = std::_S_black;
|
|
__y->_M_color = std::_S_black;
|
|
__xpp->_M_color = std::_S_red;
|
|
__x = __xpp;
|
|
}
|
|
else
|
|
{
|
|
if (__x == __x->_M_parent->_M_left)
|
|
{
|
|
__x = __x->_M_parent;
|
|
std::_Rb_tree_rotate_right(__x, __root);
|
|
}
|
|
__x->_M_parent->_M_color = std::_S_black;
|
|
__xpp->_M_color = std::_S_red;
|
|
std::_Rb_tree_rotate_left(__xpp, __root);
|
|
}
|
|
}
|
|
}
|
|
if (__root->_M_color == std::_S_red)
|
|
{
|
|
__root->_M_color = std::_S_black;
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify_tree(static_cast<typename base_type::_Const_Link_type>(__root)));
|
|
return 1;
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(rb_verify_tree(static_cast<typename base_type::_Const_Link_type>(__root)));
|
|
return 0;
|
|
}
|
|
|
|
/** @brief Analogous to class method rb_verify() but only for a subtree.
|
|
* @param __x Pointer to root of subtree to check.
|
|
* @param count Returned number of nodes.
|
|
* @return Tree correct.
|
|
*/
|
|
bool
|
|
rb_verify_tree(const typename base_type::_Const_Link_type __x, int& count) const
|
|
{
|
|
int bh;
|
|
return rb_verify_tree_node(__x) and rb_verify_tree(__x, count, bh);
|
|
}
|
|
|
|
/** @brief Verify that a subtree is binary search tree (verifies
|
|
key relationships)
|
|
* @param __x Pointer to root of subtree to check.
|
|
* @return Tree correct.
|
|
*/
|
|
bool
|
|
rb_verify_tree_node(const typename base_type::_Const_Link_type __x) const
|
|
{
|
|
if (__x == NULL)
|
|
return true;
|
|
else
|
|
{
|
|
return rb_verify_node(__x) and
|
|
rb_verify_tree_node(base_type::_S_left(__x)) and
|
|
rb_verify_tree_node( base_type::_S_right(__x));
|
|
}
|
|
}
|
|
|
|
/** @brief Verify all the properties of a red-black tree except
|
|
for the key ordering
|
|
* @param __x Pointer to (subtree) root node.
|
|
* @return Tree correct.
|
|
*/
|
|
static bool
|
|
rb_verify_tree(const typename base_type::_Const_Link_type __x)
|
|
{
|
|
int bh, count;
|
|
return rb_verify_tree(__x, count, bh);
|
|
}
|
|
|
|
/** @brief Verify all the properties of a red-black tree except
|
|
for the key ordering
|
|
* @param __x Pointer to (subtree) root node.
|
|
* @param count Number of nodes of @c __x (out).
|
|
* @param black_h Black height of @c __x (out).
|
|
* @return Tree correct.
|
|
*/
|
|
static bool
|
|
rb_verify_tree(const typename base_type::_Const_Link_type __x, int& count,
|
|
int& black_h)
|
|
{
|
|
if (__x == NULL)
|
|
{
|
|
count = 0;
|
|
black_h = 0;
|
|
return true;
|
|
}
|
|
typename base_type::_Const_Link_type __L = base_type::_S_left(__x);
|
|
typename base_type::_Const_Link_type __R = base_type::_S_right(__x);
|
|
int countL, countR = 0, bhL, bhR;
|
|
bool ret = rb_verify_tree(__L, countL, bhL);
|
|
ret = ret and rb_verify_tree(__R, countR, bhR);
|
|
count = 1 + countL + countR;
|
|
ret = ret and bhL == bhR;
|
|
black_h = bhL + ((__x->_M_color == std::_S_red)? 0 : 1);
|
|
return ret;
|
|
}
|
|
|
|
/** @brief Verify red-black properties (including key based) for a node
|
|
* @param __x Pointer to node.
|
|
* @return Node correct.
|
|
*/
|
|
bool
|
|
rb_verify_node(const typename base_type::_Const_Link_type __x) const
|
|
{
|
|
typename base_type::_Const_Link_type __L = base_type::_S_left(__x);
|
|
typename base_type::_Const_Link_type __R = base_type::_S_right(__x);
|
|
if (__x->_M_color == std::_S_red)
|
|
if ((__L && __L->_M_color == std::_S_red)
|
|
|| (__R && __R->_M_color == std::_S_red))
|
|
{
|
|
return false;
|
|
}
|
|
if (__L != NULL)
|
|
{
|
|
__L = static_cast<typename base_type::_Const_Link_type>(base_type::_S_maximum(__L));
|
|
if (base_type::_M_impl._M_key_compare(base_type::_S_key(__x), base_type::_S_key(__L)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (__R != NULL)
|
|
{
|
|
__R = static_cast<typename base_type::_Const_Link_type>(base_type::_S_minimum(__R));
|
|
if (base_type::_M_impl._M_key_compare(base_type::_S_key(__R), base_type::_S_key(__x)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** @brief Print all the information of the root.
|
|
* @param t Root of the tree.
|
|
*/
|
|
static void
|
|
print_root(_Rb_tree_node_base* t)
|
|
{
|
|
/*
|
|
if (t != NULL)
|
|
std::cout<< base_type::_S_key(t) << std::endl;
|
|
else
|
|
std::cout<< "NULL" << std::endl;
|
|
*/
|
|
}
|
|
|
|
/** @brief Print all the information of the tree.
|
|
* @param t Root of the tree.
|
|
*/
|
|
static void
|
|
print_tree(_Rb_tree_node_base* t)
|
|
{
|
|
/*
|
|
if (t != NULL)
|
|
{
|
|
print_tree(t->_M_left);
|
|
std::cout<< base_type::_S_key(t) << std::endl;
|
|
print_tree(t->_M_right);
|
|
}
|
|
*/
|
|
}
|
|
|
|
/** @brief Print blanks.
|
|
* @param b Number of blanks to print.
|
|
* @return A string with @c b blanks */
|
|
inline static std::string
|
|
blanks(int b)
|
|
{
|
|
/*
|
|
std::string s = "";
|
|
for (int i=0; i < b; ++i)
|
|
s += " ";
|
|
return s;
|
|
*/
|
|
}
|
|
|
|
/** @brief Print all the information of the tree.
|
|
* @param t Root of the tree.
|
|
* @param c Width of a printed key.
|
|
*/
|
|
template<typename Pointer>
|
|
static void
|
|
draw_tree(Pointer t, const int c)
|
|
{
|
|
/*
|
|
if (t == NULL)
|
|
{
|
|
std::cout << blanks(c) << "NULL" << std::endl;
|
|
return;
|
|
}
|
|
draw_tree(static_cast<Pointer>(t->_M_right), c + 8);
|
|
std::cout << blanks(c) << "" << base_type::_S_key(t) << " ";
|
|
if (t->_M_color == std::_S_black)
|
|
std::cout << "B" << std::endl;
|
|
else
|
|
std::cout << "R" << std::endl;
|
|
draw_tree(static_cast<Pointer>(t->_M_left), c + 8);
|
|
*/
|
|
}
|
|
|
|
public:
|
|
/** @brief Verify that all the red-black tree properties hold for
|
|
the stored tree, as well as the additional properties that the
|
|
STL implementation imposes.
|
|
*/
|
|
bool
|
|
rb_verify()
|
|
{
|
|
if (base_type::_M_impl._M_node_count == 0 || base_type::begin() == base_type::end())
|
|
{
|
|
bool res = base_type::_M_impl._M_node_count == 0 && base_type::begin() == base_type::end()
|
|
&& base_type::_M_impl._M_header._M_left ==base_type::_M_end()
|
|
&& base_type::_M_impl._M_header._M_right == base_type::_M_end();
|
|
_GLIBCXX_PARALLEL_ASSERT(res);
|
|
return res;
|
|
}
|
|
size_type i=0;
|
|
unsigned int __len = _Rb_tree_black_count(base_type::_M_leftmost(), base_type::_M_root());
|
|
for (typename base_type::const_iterator __it =base_type::begin(); __it != base_type::end(); ++__it)
|
|
{
|
|
typename base_type::_Const_Link_type __x = static_cast<typename base_type::_Const_Link_type>(__it._M_node);
|
|
if (not rb_verify_node(__x)) return false;
|
|
if (!base_type::_S_left(__x)&& !base_type::_S_right(__x) && _Rb_tree_black_count(__x,base_type::_M_root()) != __len)
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(false);
|
|
return false;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (i != base_type::_M_impl._M_node_count)
|
|
printf("%ld != %ld\n", i, base_type::_M_impl._M_node_count);
|
|
|
|
if (base_type::_M_leftmost() != std::_Rb_tree_node_base::_S_minimum(base_type::_M_root()))
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(false);
|
|
return false;
|
|
}
|
|
if (base_type::_M_rightmost() != std::_Rb_tree_node_base::_S_maximum(base_type::_M_root()))
|
|
{
|
|
_GLIBCXX_PARALLEL_ASSERT(false);
|
|
return false;
|
|
}
|
|
_GLIBCXX_PARALLEL_ASSERT(i == base_type::_M_impl._M_node_count);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|