diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2019-12-05 15:12:59 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2019-12-05 15:12:59 +0900 |
commit | b8cf34c691623e4ec329053cbbf68522a855882d (patch) | |
tree | 34da08632a99677f6b79ecb65e5b655a5b69a67f /boost/sort | |
parent | 3fdc3e5ee96dca5b11d1694975a65200787eab86 (diff) | |
download | boost-b8cf34c691623e4ec329053cbbf68522a855882d.tar.gz boost-b8cf34c691623e4ec329053cbbf68522a855882d.tar.bz2 boost-b8cf34c691623e4ec329053cbbf68522a855882d.zip |
Imported Upstream version 1.67.0upstream/1.67.0
Diffstat (limited to 'boost/sort')
46 files changed, 13948 insertions, 3247 deletions
diff --git a/boost/sort/block_indirect_sort/blk_detail/backbone.hpp b/boost/sort/block_indirect_sort/blk_detail/backbone.hpp new file mode 100644 index 0000000000..1c2fdfec88 --- /dev/null +++ b/boost/sort/block_indirect_sort/blk_detail/backbone.hpp @@ -0,0 +1,219 @@ +//---------------------------------------------------------------------------- +/// @file backbone.hpp +/// @brief This file constains the class backbone, which is part of the +/// block_indirect_sort algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_BACKBONE_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_BACKBONE_HPP + +#include <atomic> +#include <boost/sort/pdqsort/pdqsort.hpp> +#include <boost/sort/common/util/atomic.hpp> +#include <boost/sort/common/util/algorithm.hpp> +#include <boost/sort/common/stack_cnc.hpp> +#include <future> +#include <iostream> +#include <iterator> + +#include <boost/sort/block_indirect_sort/blk_detail/block.hpp> + +namespace boost +{ +namespace sort +{ +namespace blk_detail +{ + +//--------------------------------------------------------------------------- +// USING SENTENCES +//--------------------------------------------------------------------------- +namespace bsc = boost::sort::common; +namespace bscu = bsc::util; +using bsc::stack_cnc; +using bsc::range; + +///--------------------------------------------------------------------------- +/// @struct backbone +/// @brief This contains all the information shared betwen the classes of the +/// block indirect sort algorithm + +//---------------------------------------------------------------------------- +template < uint32_t Block_size, class Iter_t, class Compare > +struct backbone +{ + //------------------------------------------------------------------------- + // D E F I N I T I O N S + //------------------------------------------------------------------------- + typedef typename std::iterator_traits< Iter_t >::value_type value_t; + typedef std::atomic< uint32_t > atomic_t; + typedef range< size_t > range_pos; + typedef range< Iter_t > range_it; + typedef range< value_t * > range_buf; + typedef std::function< void(void) > function_t; + typedef block< Block_size, Iter_t > block_t; + + //------------------------------------------------------------------------ + // V A R I A B L E S + //------------------------------------------------------------------------ + // range with all the element to sort + range< Iter_t > global_range; + + // index vector of block_pos elements + std::vector< block_pos > index; + + // Number of elements to sort + size_t nelem; + + // Number of blocks to sort + size_t nblock; + + // Number of elements in the last block (tail) + size_t ntail; + + // object for to compare two elements + Compare cmp; + + // range of elements of the last block (tail) + range_it range_tail; + + // thread local varible. It is a pointer to the buffer + static thread_local value_t *buf; + + // concurrent stack where store the function_t elements + stack_cnc< function_t > works; + + // global indicator of error + bool error; + // + //------------------------------------------------------------------------ + // F U N C T I O N S + //------------------------------------------------------------------------ + backbone (Iter_t first, Iter_t last, Compare comp); + + //------------------------------------------------------------------------ + // function : get_block + /// @brief obtain the block in the position pos + /// @param pos : position of the range + /// @return block required + //------------------------------------------------------------------------ + block_t get_block (size_t pos) const + { + return block_t (global_range.first + (pos * Block_size)); + }; + //------------------------------------------------------------------------- + // function : get_range + /// @brief obtain the range in the position pos + /// @param pos : position of the range + /// @return range required + //------------------------------------------------------------------------- + range_it get_range (size_t pos) const + { + Iter_t it1 = global_range.first + (pos * Block_size); + Iter_t it2 = + (pos == (nblock - 1)) ? global_range.last : it1 + Block_size; + return range_it (it1, it2); + }; + //------------------------------------------------------------------------- + // function : get_range_buf + /// @brief obtain the auxiliary buffer of the thread + //------------------------------------------------------------------------- + range_buf get_range_buf ( ) const + { + return range_buf (buf, buf + Block_size); + }; + + //------------------------------------------------------------------------- + // function : exec + /// @brief Initialize the thread local buffer with the ptr_buf pointer, + /// and begin with the execution of the functions stored in works + // + /// @param ptr_buf : Pointer to the memory assigned to the thread_local + /// buffer + /// @param counter : atomic counter for to invoke to the exec function + /// with only 1 parameter + //------------------------------------------------------------------------- + void exec (value_t *ptr_buf, atomic_t &counter) + { + buf = ptr_buf; + exec (counter); + }; + + void exec (atomic_t &counter); + +//--------------------------------------------------------------------------- +}; // end struct backbone +//--------------------------------------------------------------------------- +// +//############################################################################ +// ## +// ## +// N O N I N L I N E F U N C T I O N S ## +// ## +// ## +//############################################################################ +// +// initialization of the thread_local pointer to the auxiliary buffer +template < uint32_t Block_size, class Iter_t, class Compare > +thread_local typename std::iterator_traits< Iter_t > +::value_type *backbone< Block_size, Iter_t, Compare >::buf = nullptr; + +//------------------------------------------------------------------------ +// function : backbone +/// @brief constructor of the class +// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +//------------------------------------------------------------------------ +template < uint32_t Block_size, class Iter_t, class Compare > +backbone< Block_size, Iter_t, Compare > +::backbone (Iter_t first, Iter_t last, Compare comp) +: global_range (first, last), cmp (comp), error (false) +{ + assert ((last - first) >= 0); + if (first == last) return; // nothing to do + + nelem = size_t (last - first); + nblock = (nelem + Block_size - 1) / Block_size; + ntail = (nelem % Block_size); + index.reserve (nblock + 1); + + for (size_t i = 0; i < nblock; ++i) index.emplace_back (block_pos (i)); + + range_tail.first = + (ntail == 0) ? last : (first + ((nblock - 1) * Block_size)); + range_tail.last = last; +}; +// +//------------------------------------------------------------------------- +// function : exec +/// @brief execute the function_t stored in works, until counter is zero +// +/// @param counter : atomic counter. When 0 exits the function +//------------------------------------------------------------------------- +template < uint32_t Block_size, class Iter_t, class Compare > +void backbone< Block_size, Iter_t, Compare >::exec (atomic_t &counter) +{ + function_t func_exec; + while (bscu::atomic_read (counter) != 0) + { + if (works.pop_move_back (func_exec)) func_exec ( ); + else std::this_thread::yield ( ); + }; +}; +// +//**************************************************************************** +}; // End namespace blk_detail +}; // End namespace sort +}; // End namespace boost +//**************************************************************************** +#endif diff --git a/boost/sort/block_indirect_sort/blk_detail/block.hpp b/boost/sort/block_indirect_sort/blk_detail/block.hpp new file mode 100644 index 0000000000..9c14b6103f --- /dev/null +++ b/boost/sort/block_indirect_sort/blk_detail/block.hpp @@ -0,0 +1,180 @@ +//---------------------------------------------------------------------------- +/// @file block.hpp +/// @brief This file contains the internal data structures used in the +/// block_indirect_sort algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_BLOCK_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_BLOCK_HPP + +#include <boost/sort/common/range.hpp> + +namespace boost +{ +namespace sort +{ +namespace blk_detail +{ +//--------------------------------------------------------------------------- +// USING SENTENCES +//--------------------------------------------------------------------------- +using namespace boost::sort::common; +// +//--------------------------------------------------------------------------- +/// @struct block_pos +/// @brief represent a pair of values, a position represented as an unsigned +/// variable ( position ), and a bool variable ( side ). They are packed +/// in a size_t variable. The Least Significant Bit is the bool variable, +/// and the others bits are the position +//---------------------------------------------------------------------------- +class block_pos +{ + //------------------------------------------------------------------------ + // VARIABLES + //----------------------------------------------------------------------- + size_t num; // number which store a position and a bool side + + public: + //----------------------------- FUNCTIONS ------------------------------ + block_pos (void) : num (0){}; + // + //------------------------------------------------------------------------- + // function : block_pos + /// @brief constructor from a position and a side + /// @param position : position to sotre + /// @param side : side to store + //------------------------------------------------------------------------- + block_pos (size_t position, bool side = false) + { + num = (position << 1) + ((side) ? 1 : 0); + }; + // + //------------------------------------------------------------------------- + // function : pos + /// @brief obtain the position stored inside the block_pos + /// @return position + //------------------------------------------------------------------------- + size_t pos (void) const { return (num >> 1); }; + // + //------------------------------------------------------------------------- + // function : pos + /// @brief store a position inside the block_pos + /// @param position : value to store + //------------------------------------------------------------------------- + void set_pos (size_t position) { num = (position << 1) + (num & 1); }; + // + //------------------------------------------------------------------------- + // function : side + /// @brief obtain the side stored inside the block_pos + /// @return bool value + //------------------------------------------------------------------------- + bool side (void) const { return ((num & 1) != 0); }; + // + //------------------------------------------------------------------------- + // function : side + /// @brief store a bool value the block_pos + /// @param sd : bool value to store + //------------------------------------------------------------------------- + void set_side (bool sd) { num = (num & ~1) + ((sd) ? 1 : 0); }; +}; // end struct block_pos + +// +//--------------------------------------------------------------------------- +/// @struct block +/// @brief represent a group of Block_size contiguous elements, beginning +/// with the pointed by first +//---------------------------------------------------------------------------- +template < uint32_t Block_size, class Iter_t > +struct block +{ + //---------------------------------------------------------------------- + // VARIABLES + //---------------------------------------------------------------------- + Iter_t first; // iterator to the first element of the block + + //------------------------------------------------------------------------- + // function : block + /// @brief constructor from an iterator to the first element of the block + /// @param it : iterator to the first element of the block + //------------------------------------------------------------------------- + block (Iter_t it) : first (it){}; + + //------------------------------------------------------------------------- + // function : get_range + /// @brief convert a block in a range + /// @return range + //------------------------------------------------------------------------- + range< Iter_t > get_range (void) + { + return range_it (first, first + Block_size); + }; + +}; // end struct block + +// +//------------------------------------------------------------------------- +// function : compare_block +/// @brief compare two blocks using the content of the pointed by first +/// @param block1 : first block to compare +/// @param block2 : second block to compare +/// @param cmp : comparison operator +//------------------------------------------------------------------------- +template < uint32_t Block_size, class Iter_t, class Compare > +bool compare_block (block< Block_size, Iter_t > block1, + block< Block_size, Iter_t > block2, + Compare cmp = Compare ( )) +{ + return cmp (*block1.first, *block2.first); +}; +// +///--------------------------------------------------------------------------- +/// @struct compare_block_pos +/// @brief This is a object for to compare two block_pos objects +//---------------------------------------------------------------------------- +template < uint32_t Block_size, class Iter_t, class Compare > +struct compare_block_pos +{ + //----------------------------------------------------------------------- + // VARIABLES + //----------------------------------------------------------------------- + Iter_t global_first; // iterator to the first element to sort + Compare comp; // comparison object for to compare two elements + + //------------------------------------------------------------------------- + // function : compare_block_pos + /// @brief constructor + /// @param g_first : itertor to the first element to sort + /// @param cmp : comparison operator + //------------------------------------------------------------------------- + compare_block_pos (Iter_t g_first, Compare cmp) + : global_first (g_first), comp (cmp){}; + // + //------------------------------------------------------------------------- + // function : operator () + /// @brief compare two blocks using the content of the pointed by + /// global_first + /// @param block_pos1 : first block to compare + /// @param block_pos2 : second block to compare + //------------------------------------------------------------------------- + bool operator( ) (block_pos block_pos1, block_pos block_pos2) const + { + return comp (*(global_first + (block_pos1.pos ( ) * Block_size)), + *(global_first + (block_pos2.pos ( ) * Block_size))); + }; + +}; // end struct compare_block_pos + +//**************************************************************************** +}; // End namespace blk_detail +}; // End namespace sort +}; // End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/block_indirect_sort/blk_detail/constants.hpp b/boost/sort/block_indirect_sort/blk_detail/constants.hpp new file mode 100644 index 0000000000..c407243025 --- /dev/null +++ b/boost/sort/block_indirect_sort/blk_detail/constants.hpp @@ -0,0 +1,26 @@ +//---------------------------------------------------------------------------- +/// @file constants.hpp +/// @brief This file contains the constants values used in the algorithms +/// +/// @author Copyright (c) 2016 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_CONSTANTS_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_CONSTANTS_HPP + +// This value is the block size in the block_indirect_sort algorithm +#define BOOST_BLOCK_SIZE 1024 + +// This value represent the group size in the block_indirect_sort algorithm +#define BOOST_GROUP_SIZE 64 + +// This value is the minimal number of threads for to use the +// block_indirect_sort algorithm +#define BOOST_NTHREAD_BORDER 6 + +#endif diff --git a/boost/sort/block_indirect_sort/blk_detail/merge_blocks.hpp b/boost/sort/block_indirect_sort/blk_detail/merge_blocks.hpp new file mode 100644 index 0000000000..a4185b53af --- /dev/null +++ b/boost/sort/block_indirect_sort/blk_detail/merge_blocks.hpp @@ -0,0 +1,426 @@ +//---------------------------------------------------------------------------- +/// @file merge_blocks.hpp +/// @brief contains the class merge_blocks, which is part of the +/// block_indirect_sort algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_MERGE_BLOCKS_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_MERGE_BLOCKS_HPP + +#include <atomic> +#include <boost/sort/block_indirect_sort/blk_detail/backbone.hpp> +#include <boost/sort/common/range.hpp> +#include <future> +#include <iostream> +#include <iterator> + +namespace boost +{ +namespace sort +{ +namespace blk_detail +{ +//---------------------------------------------------------------------------- +// USING SENTENCES +//---------------------------------------------------------------------------- +namespace bsc = boost::sort::common; +namespace bscu = bsc::util; +using bsc::range; +using bsc::is_mergeable; +using bsc::merge_uncontiguous; +// +///--------------------------------------------------------------------------- +/// @struct merge_blocks +/// @brief This class merge the blocks. The blocks to merge are defined by two +/// ranges of positions in the index of the backbone +//---------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +struct merge_blocks +{ + //----------------------------------------------------------------------- + // D E F I N I T I O N S + //----------------------------------------------------------------------- + typedef typename std::iterator_traits<Iter_t>::value_type value_t; + typedef std::atomic<uint32_t> atomic_t; + typedef range<size_t> range_pos; + typedef range<Iter_t> range_it; + typedef range<value_t *> range_buf; + typedef std::function<void(void)> function_t; + typedef backbone<Block_size, Iter_t, Compare> backbone_t; + typedef compare_block_pos<Block_size, Iter_t, Compare> compare_block_pos_t; + + //------------------------------------------------------------------------ + // V A R I A B L E S + //------------------------------------------------------------------------ + // Object with the elements to sort and all internal data structures of the + // algorithm + backbone_t &bk; + // + //------------------------------------------------------------------------ + // F U N C T I O N S + //------------------------------------------------------------------------ + merge_blocks(backbone_t &bkb, size_t pos_index1, size_t pos_index2, + size_t pos_index3); + + void tail_process(std::vector<block_pos> &vblkpos1, + std::vector<block_pos> &vblkpos2); + + void cut_range(range_pos rng); + + void merge_range_pos(range_pos rng); + + void extract_ranges(range_pos range_input); + // + //------------------------------------------------------------------------ + // function : function_merge_range_pos + /// @brief create a function_t with a call to merge_range_pos, and insert + /// in the stack of the backbone + // + /// @param rng_input : range of positions of blocks in the index to merge + /// @param son_counter : atomic variable which is decremented when finish + /// the function. This variable is used for to know + /// when are finished all the function_t created + /// inside an object + /// @param error : global indicator of error. + /// + //------------------------------------------------------------------------ + void function_merge_range_pos(const range_pos &rng_input, atomic_t &counter, + bool &error) + { + bscu::atomic_add(counter, 1); + function_t f1 = [this, rng_input, &counter, &error]( ) -> void + { + if (not error) + { + try + { + this->merge_range_pos (rng_input); + } + catch (std::bad_alloc &ba) + { + error = true; + }; + } + bscu::atomic_sub (counter, 1); + }; + bk.works.emplace_back(f1); + } + ; + // + //------------------------------------------------------------------------ + // function : function_cut_range + /// @brief create a function_t with a call to cut_range, and inser in + /// the stack of the backbone + // + /// @param rng_input : range of positions in the index to cut + /// @param counter : atomic variable which is decremented when finish + /// the function. This variable is used for to know + /// when are finished all the function_t created + /// inside an object + /// @param error : global indicator of error. + //------------------------------------------------------------------------ + void function_cut_range(const range_pos &rng_input, atomic_t &counter, + bool &error) + { + bscu::atomic_add(counter, 1); + function_t f1 = [this, rng_input, &counter, &error]( ) -> void + { + if (not error) + { + try + { + this->cut_range (rng_input); + } + catch (std::bad_alloc &) + { + error = true; + }; + } + bscu::atomic_sub (counter, 1); + }; + bk.works.emplace_back(f1); + } + + +//---------------------------------------------------------------------------- +}; +// end struct merge_blocks +//---------------------------------------------------------------------------- +// +//############################################################################ +// ## +// ## +// N O N I N L I N E F U N C T I O N S ## +// ## +// ## +//############################################################################ +// +//------------------------------------------------------------------------- +// function : merge_blocks +/// @brief make the indirect merge of the two range_pos defined by their index +/// position [pos_index1, pos_index2 ) and [ pos_index2, pos_index3 ) +// +/// @param bkb : backbone with all the data to sort , and the internal data +/// structures of the algorithm +/// @param pos_index1 : first position of the first range in the index +/// @param pos_index2 : last position of the first range and first position +/// of the second range in the index +/// @param pos_index3 : last position of the second range in the index +//------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +merge_blocks<Block_size, Group_size, Iter_t, Compare> +::merge_blocks( backbone_t &bkb, size_t pos_index1, size_t pos_index2, + size_t pos_index3) : bk(bkb) +{ + size_t nblock1 = pos_index2 - pos_index1; + size_t nblock2 = pos_index3 - pos_index2; + if (nblock1 == 0 or nblock2 == 0) return; + + //----------------------------------------------------------------------- + // Merging of the two intervals + //----------------------------------------------------------------------- + std::vector<block_pos> vpos1, vpos2; + vpos1.reserve(nblock1 + 1); + vpos2.reserve(nblock2 + 1); + + for (size_t i = pos_index1; i < pos_index2; ++i) + { + vpos1.emplace_back(bk.index[i].pos(), true); + }; + + for (size_t i = pos_index2; i < pos_index3; ++i) + { + vpos2.emplace_back(bk.index[i].pos(), false); + }; + //------------------------------------------------------------------- + // tail process + //------------------------------------------------------------------- + if (vpos2.back().pos() == (bk.nblock - 1) + and bk.range_tail.first != bk.range_tail.last) + { + tail_process(vpos1, vpos2); + nblock1 = vpos1.size(); + nblock2 = vpos2.size(); + }; + + compare_block_pos_t cmp_blk(bk.global_range.first, bk.cmp); + if (bk.error) return; + bscu::merge(vpos1.begin(), vpos1.end(), vpos2.begin(), vpos2.end(), + bk.index.begin() + pos_index1, cmp_blk); + if (bk.error) return; + // Extracting the ranges for to merge the elements + extract_ranges(range_pos(pos_index1, pos_index1 + nblock1 + nblock2)); +} + + +// +//------------------------------------------------------------------------- +// function : tail_process +/// @brief make the process when the second vector of block_pos to merge is +/// the last, and have an incomplete block ( tail) +// +/// @param vblkpos1 : first vector of block_pos elements to merge +/// @param vblkpos2 : second vector of block_pos elements to merge +//------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +void merge_blocks<Block_size, Group_size, Iter_t, Compare> +::tail_process( std::vector<block_pos> &vblkpos1, + std::vector<block_pos> &vblkpos2 ) +{ + if (vblkpos1.size() == 0 or vblkpos2.size() == 0) return; + + vblkpos2.pop_back(); + + size_t posback1 = vblkpos1.back().pos(); + range_it range_back1 = bk.get_range(posback1); + + if (bsc::is_mergeable(range_back1, bk.range_tail, bk.cmp)) + { + bsc::merge_uncontiguous(range_back1, bk.range_tail, bk.get_range_buf(), + bk.cmp); + if (vblkpos1.size() > 1) + { + size_t pos_aux = vblkpos1[vblkpos1.size() - 2].pos(); + range_it range_aux = bk.get_range(pos_aux); + + if (bsc::is_mergeable(range_aux, range_back1, bk.cmp)) + { + vblkpos2.emplace_back(posback1, false); + vblkpos1.pop_back(); + }; + }; + }; +} + +// +//------------------------------------------------------------------------- +// function : cut_range +/// @brief when the rng_input is greather than Group_size, this function divide +/// it in several parts creating function_t elements, which are inserted +/// in the concurrent stack of the backbone +// +/// @param rng_input : range to divide +//------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +void merge_blocks<Block_size, Group_size, Iter_t, Compare> +::cut_range(range_pos rng_input) +{ + if (rng_input.size() < Group_size) + { + merge_range_pos(rng_input); + return; + }; + + atomic_t counter(0); + size_t npart = (rng_input.size() + Group_size - 1) / Group_size; + size_t size_part = rng_input.size() / npart; + + size_t pos_ini = rng_input.first; + size_t pos_last = rng_input.last; + + while (pos_ini < pos_last) + { + size_t pos = pos_ini + size_part; + while (pos < pos_last + and bk.index[pos - 1].side() == bk.index[pos].side()) + { + ++pos; + }; + if (pos < pos_last) + { + merge_uncontiguous(bk.get_range(bk.index[pos - 1].pos()), + bk.get_range(bk.index[pos].pos()), + bk.get_range_buf(), bk.cmp); + } + else pos = pos_last; + if ((pos - pos_ini) > 1) + { + range_pos rng_aux(pos_ini, pos); + function_merge_range_pos(rng_aux, counter, bk.error); + }; + pos_ini = pos; + }; + bk.exec(counter); // wait until finish all the ranges +} + + +// +//------------------------------------------------------------------------- +// function : merge_range_pos +/// @brief make the indirect merge of the blocks inside the rng_input +// +/// @param rng_input : range of positions of the blocks to merge +//------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +void merge_blocks<Block_size, Group_size, Iter_t, Compare> +::merge_range_pos(range_pos rng_input) +{ + if (rng_input.size() < 2) return; + range_buf rbuf = bk.get_range_buf(); + + range_it rng_prev = bk.get_range(bk.index[rng_input.first].pos()); + move_forward(rbuf, rng_prev); + range_it rng_posx(rng_prev); + + for (size_t posx = rng_input.first + 1; posx != rng_input.last; ++posx) + { + rng_posx = bk.get_range(bk.index[posx].pos()); + bsc::merge_flow(rng_prev, rbuf, rng_posx, bk.cmp); + rng_prev = rng_posx; + + }; + move_forward(rng_posx, rbuf); +} +// +//------------------------------------------------------------------------- +// function : extract_ranges +/// @brief from a big range of positions of blocks in the index. Examine which +/// are mergeable, and generate a couple of ranges for to be merged. +/// With the ranges obtained generate function_t elements and are +/// inserted in the concurrent stack. +/// When the range obtained is smaller than Group_size, generate a +/// function_t calling to merge_range_pos, when is greater, generate a +/// function_t calling to cut_range +// +/// @param rpos range_input : range of the position in the index, where must +/// extract the ranges to merge +//------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +void merge_blocks<Block_size, Group_size, Iter_t, Compare> +::extract_ranges(range_pos range_input) +{ + if (range_input.size() < 2) return; + atomic_t counter(0); + + // The names with x are positions of the index + size_t posx_ini = range_input.first; + block_pos bp_posx_ini = bk.index[posx_ini]; + + range_it rng_max = bk.get_range(bp_posx_ini.pos()); + bool side_max = bp_posx_ini.side(); + + block_pos bp_posx; + range_it rng_posx = rng_max; + bool side_posx = side_max; + + for (size_t posx = posx_ini + 1; posx <= range_input.last; ++posx) + { + bool final = (posx == range_input.last); + bool mergeable = false; + + if (not final) + { + bp_posx = bk.index[posx]; + rng_posx = bk.get_range(bp_posx.pos()); + side_posx = bp_posx.side(); + mergeable = (side_max != side_posx + and is_mergeable(rng_max, rng_posx, bk.cmp)); + }; + if (bk.error) return; + if (final or not mergeable) + { + range_pos rp_final(posx_ini, posx); + if (rp_final.size() > 1) + { + if (rp_final.size() > Group_size) + { + function_cut_range(rp_final, counter, bk.error); + } + else + { + function_merge_range_pos(rp_final, counter, bk.error); + }; + }; + posx_ini = posx; + if (not final) + { + rng_max = rng_posx; + side_max = side_posx; + }; + } + else + { + if (bk.cmp(*(rng_max.back()), *(rng_posx.back()))) + { + rng_max = rng_posx; + side_max = side_posx; + }; + }; + }; + bk.exec(counter); +} +// +//**************************************************************************** +}; // End namespace blk_detail +}; // End namespace sort +}; // End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/block_indirect_sort/blk_detail/move_blocks.hpp b/boost/sort/block_indirect_sort/blk_detail/move_blocks.hpp new file mode 100644 index 0000000000..6b556bcf47 --- /dev/null +++ b/boost/sort/block_indirect_sort/blk_detail/move_blocks.hpp @@ -0,0 +1,284 @@ +//---------------------------------------------------------------------------- +/// @file move_blocks.hpp +/// @brief contains the class move_blocks, which is part of the +/// block_indirect_sort algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_MOVE_BLOCKS_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_MOVE_BLOCKS_HPP + +#include <atomic> +#include <boost/sort/block_indirect_sort/blk_detail/backbone.hpp> +#include <future> +#include <iostream> +#include <iterator> + +namespace boost +{ +namespace sort +{ +namespace blk_detail +{ +//---------------------------------------------------------------------------- +// USING SENTENCES +//---------------------------------------------------------------------------- +namespace bsc = boost::sort::common; +// +///--------------------------------------------------------------------------- +/// @struct move_blocks +/// @brief This class move the blocks, trnasforming a logical sort by an index, +/// in physical sort +//---------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +struct move_blocks +{ + //------------------------------------------------------------------------- + // D E F I N I T I O N S + //------------------------------------------------------------------------- + typedef move_blocks<Block_size, Group_size, Iter_t, Compare> this_type; + typedef typename std::iterator_traits<Iter_t>::value_type value_t; + typedef std::atomic<uint32_t> atomic_t; + typedef bsc::range<size_t> range_pos; + typedef bsc::range<Iter_t> range_it; + typedef bsc::range<value_t *> range_buf; + typedef std::function<void(void)> function_t; + typedef backbone<Block_size, Iter_t, Compare> backbone_t; + + //------------------------------------------------------------------------ + // V A R I A B L E S + //------------------------------------------------------------------------ + // Object with the elements to sort and all internal data structures of the + // algorithm + backbone_t &bk; + + //------------------------------------------------------------------------ + // F U N C T I O N S + //------------------------------------------------------------------------ + move_blocks(backbone_t &bkb); + + void move_sequence(const std::vector<size_t> &init_sequence); + + void move_long_sequence(const std::vector<size_t> &init_sequence); + // + //------------------------------------------------------------------------ + // function : function_move_sequence + /// @brief create a function_t with a call to move_sequence, and insert + /// in the stack of the backbone + /// + /// @param sequence :sequence of positions for to move the blocks + /// @param counter : atomic variable which is decremented when finish + /// the function. This variable is used for to know + /// when are finished all the function_t created + /// inside an object + /// @param error : global indicator of error. + //------------------------------------------------------------------------ + void function_move_sequence(std::vector<size_t> &sequence, + atomic_t &counter, bool &error) + { + bscu::atomic_add(counter, 1); + function_t f1 = [this, sequence, &counter, &error]( ) -> void + { + if (not error) + { + try + { + this->move_sequence (sequence); + } + catch (std::bad_alloc &) + { + error = true; + }; + } + bscu::atomic_sub (counter, 1); + }; + bk.works.emplace_back(f1); + } + + // + //------------------------------------------------------------------------ + // function : function_move_long_sequence + /// @brief create a function_t with a call to move_long_sequence, and + /// insert in the stack of the backbone + // + /// @param sequence :sequence of positions for to move the blocks + /// @param counter : atomic variable which is decremented when finish + /// the function. This variable is used for to know + /// when are finished all the function_t created + /// inside an object + /// @param error : global indicator of error. + //------------------------------------------------------------------------ + void function_move_long_sequence(std::vector<size_t> &sequence, + atomic_t &counter, bool &error) + { + bscu::atomic_add(counter, 1); + function_t f1 = [this, sequence, &counter, &error]( ) -> void + { + if (not error) + { + try + { + this->move_long_sequence (sequence); + } + catch (std::bad_alloc &) + { + error = true; + }; + } + bscu::atomic_sub (counter, 1); + }; + bk.works.emplace_back(f1); + } + ; +//--------------------------------------------------------------------------- +}; // end of struct move_blocks +//--------------------------------------------------------------------------- +// +//############################################################################ +// ## +// ## +// N O N I N L I N E F U N C T I O N S ## +// ## +// ## +//############################################################################ +// +//------------------------------------------------------------------------- +// function : move_blocks +/// @brief constructor of the class for to move the blocks to their true +/// position obtained from the index +// +/// @param bkb : backbone with the index and the blocks +//------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +move_blocks<Block_size, Group_size, Iter_t, Compare> +::move_blocks(backbone_t &bkb) : bk(bkb) +{ + std::vector<std::vector<size_t> > vsequence; + vsequence.reserve(bk.index.size() >> 1); + std::vector<size_t> sequence; + atomic_t counter(0); + + size_t pos_index_ini = 0, pos_index_src = 0, pos_index_dest = 0; + while (pos_index_ini < bk.index.size()) + { + while (pos_index_ini < bk.index.size() + and bk.index[pos_index_ini].pos() == pos_index_ini) + { + ++pos_index_ini; + }; + + if (pos_index_ini == bk.index.size()) break; + + sequence.clear(); + pos_index_src = pos_index_dest = pos_index_ini; + sequence.push_back(pos_index_ini); + + while (bk.index[pos_index_dest].pos() != pos_index_ini) + { + pos_index_src = bk.index[pos_index_dest].pos(); + sequence.push_back(pos_index_src); + + bk.index[pos_index_dest].set_pos(pos_index_dest); + pos_index_dest = pos_index_src; + }; + + bk.index[pos_index_dest].set_pos(pos_index_dest); + vsequence.push_back(sequence); + + if (sequence.size() < Group_size) + { + function_move_sequence(vsequence.back(), counter, bk.error); + } + else + { + function_move_long_sequence(vsequence.back(), counter, bk.error); + }; + }; + bk.exec(counter); +} +; +// +//------------------------------------------------------------------------- +// function : move_sequence +/// @brief move the blocks, following the positions of the init_sequence +// +/// @param init_sequence : vector with the positions from and where move the +/// blocks +//------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +void move_blocks<Block_size, Group_size, Iter_t, Compare> +::move_sequence(const std::vector<size_t> &init_sequence) +{ + range_buf rbuf = bk.get_range_buf(); + size_t pos_range2 = init_sequence[0]; + + range_it range2 = bk.get_range(pos_range2); + move_forward(rbuf, range2); + + for (size_t i = 1; i < init_sequence.size(); ++i) + { + pos_range2 = init_sequence[i]; + range_it range1(range2); + range2 = bk.get_range(pos_range2); + move_forward(range1, range2); + }; + move_forward(range2, rbuf); +}; +// +//------------------------------------------------------------------------- +// function : move_long_sequence +/// @brief move the blocks, following the positions of the init_sequence. +/// if the sequence is greater than Group_size, it is divided in small +/// sequences, creating function_t elements, for to be inserted in the +/// concurrent stack +// +/// @param init_sequence : vector with the positions from and where move the +/// blocks +//------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +void move_blocks<Block_size, Group_size, Iter_t, Compare> +::move_long_sequence(const std::vector<size_t> &init_sequence) +{ + if (init_sequence.size() < Group_size) return move_sequence(init_sequence); + + size_t npart = (init_sequence.size() + Group_size - 1) / Group_size; + size_t size_part = init_sequence.size() / npart; + atomic_t son_counter(0); + + std::vector<size_t> sequence; + sequence.reserve(size_part); + + std::vector<size_t> index_seq; + index_seq.reserve(npart); + + auto it_pos = init_sequence.begin(); + for (size_t i = 0; i < (npart - 1); ++i, it_pos += size_part) + { + sequence.assign(it_pos, it_pos + size_part); + index_seq.emplace_back(*(it_pos + size_part - 1)); + function_move_sequence(sequence, son_counter, bk.error); + }; + + sequence.assign(it_pos, init_sequence.end()); + index_seq.emplace_back(init_sequence.back()); + function_move_sequence(sequence, son_counter, bk.error); + + bk.exec(son_counter); + if (bk.error) return; + move_long_sequence(index_seq); +} + +// +//**************************************************************************** +}; // End namespace blk_detail +}; // End namespace sort +}; // End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/block_indirect_sort/blk_detail/parallel_sort.hpp b/boost/sort/block_indirect_sort/blk_detail/parallel_sort.hpp new file mode 100644 index 0000000000..98c0e48a5c --- /dev/null +++ b/boost/sort/block_indirect_sort/blk_detail/parallel_sort.hpp @@ -0,0 +1,236 @@ +//---------------------------------------------------------------------------- +/// @file parallel_sort.hpp +/// @brief Contains the parallel_sort class, which is part of the +/// block_indirect_sort algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_PARALLEL_SORT_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_PARALLEL_SORT_HPP + +#include <boost/sort/block_indirect_sort/blk_detail/backbone.hpp> +#include <boost/sort/pdqsort/pdqsort.hpp> +#include <boost/sort/common/pivot.hpp> + +namespace boost +{ +namespace sort +{ +namespace blk_detail +{ + +//---------------------------------------------------------------------------- +// USING SENTENCES +//---------------------------------------------------------------------------- +namespace bsc = boost::sort::common; +namespace bscu = bsc::util; +using bscu::nbits64; +using bsc::pivot9; +using boost::sort::pdqsort; +// +///--------------------------------------------------------------------------- +/// @struct parallel_sort +/// @brief This class do a parallel sort, using the quicksort filtering, +/// splitting the data until the number of elements is smaller than a +/// predefined value (max_per_thread) +//---------------------------------------------------------------------------- +template<uint32_t Block_size, class Iter_t, class Compare> +struct parallel_sort +{ + //------------------------------------------------------------------------- + // D E F I N I T I O N S + //------------------------------------------------------------------------- + typedef typename std::iterator_traits<Iter_t>::value_type value_t; + typedef std::atomic<uint32_t> atomic_t; + typedef std::function<void(void)> function_t; + typedef backbone<Block_size, Iter_t, Compare> backbone_t; + + //------------------------------------------------------------------------ + // V A R I A B L E S + //------------------------------------------------------------------------ + // reference to a object with all the data to sort + backbone_t &bk; + + // maximun number of element to sort woth 1 thread + size_t max_per_thread; + + // atomic counter for to detect the end of the works created inside + // the object + atomic_t counter; + + //------------------------------------------------------------------------ + // F U N C T I O N S + //------------------------------------------------------------------------ + parallel_sort(backbone_t &bkbn, Iter_t first, Iter_t last); + + void divide_sort(Iter_t first, Iter_t last, uint32_t level); + // + //------------------------------------------------------------------------ + // function : function_divide_sort + /// @brief create a function_t with a call to divide_sort, and inser in + /// the stack of the backbone + // + /// @param first : iterator to the first element of the range to divide + /// @param last : iterator to the next element after the last element of + /// the range to divide + /// @param level : level of depth in the division.When zero call to + /// pdqsort + /// @param counter : atomic variable which is decremented when finish + /// the function. This variable is used for to know + /// when are finished all the function_t created + /// inside an object + /// @param error : global indicator of error. + //------------------------------------------------------------------------ + void function_divide_sort(Iter_t first, Iter_t last, uint32_t level, + atomic_t &counter, bool &error) + { + bscu::atomic_add(counter, 1); + function_t f1 = [this, first, last, level, &counter, &error]( ) + { + if (not error) + { + try + { + this->divide_sort (first, last, level); + } + catch (std::bad_alloc &) + { + error = true; + }; + }; + bscu::atomic_sub (counter, 1); + }; + bk.works.emplace_back(f1); + }; + +//-------------------------------------------------------------------------- +};// end struct parallel_sort +//-------------------------------------------------------------------------- +// +//############################################################################ +// ## +// ## +// N O N I N L I N E F U N C T I O N S ## +// ## +// ## +//############################################################################ +// +//------------------------------------------------------------------------ +// function : parallel_sort +/// @brief constructor of the class +/// @param [in] bkbn : backbone struct with all the information to sort +/// @param [in] first : iterator to the first element to sort +/// @param [in] last : iterator to the next element after the last +//------------------------------------------------------------------------ +template<uint32_t Block_size, class Iter_t, class Compare> +parallel_sort<Block_size, Iter_t, Compare> +::parallel_sort(backbone_t &bkbn, Iter_t first, Iter_t last) + : bk(bkbn), counter(0) +{ + assert((last - first) >= 0); + size_t nelem = size_t(last - first); + + //------------------- check if sort -------------------------------------- + bool sorted = true; + for (Iter_t it1 = first, it2 = first + 1; + it2 != last and (sorted = not bk.cmp(*it2, *it1)); it1 = it2++); + if (sorted) return; + + //------------------- check if reverse sort --------------------------- + sorted = true; + for (Iter_t it1 = first, it2 = first + 1; + it2 != last and (sorted = not bk.cmp(*it1, *it2)); it1 = it2++); + + if (sorted) + { + size_t nelem2 = nelem >> 1; + Iter_t it1 = first, it2 = last - 1; + for (size_t i = 0; i < nelem2; ++i) + std::swap(*(it1++), *(it2--)); + return; + }; + + //-------------------max_per_thread --------------------------- + uint32_t nbits_size = (nbits64(sizeof(value_t))) >> 1; + if (nbits_size > 5) nbits_size = 5; + max_per_thread = 1 << (18 - nbits_size); + + uint32_t level = ((nbits64(nelem / max_per_thread)) * 3) / 2; + + //---------------- check if only single thread ----------------------- + if (nelem < (max_per_thread)) + { + pdqsort(first, last, bk.cmp); + return; + }; + if (not bk.error) divide_sort(first, last, level); + + // wait until all the parts are finished + bk.exec(counter); +}; + +//------------------------------------------------------------------------ +// function : divide_sort +/// @brief this function divide the data in two part, for to be sorted in +/// a parallel mode +/// @param first : iterator to the first element to sort +/// @param last : iterator to the next element after the last +/// @param level : level of depth before call to pdqsort +//------------------------------------------------------------------------ +template<uint32_t Block_size, class Iter_t, class Compare> +void parallel_sort<Block_size, Iter_t, Compare> +::divide_sort(Iter_t first, Iter_t last, uint32_t level) +{ + //------------------- check if sort ----------------------------------- + bool sorted = true; + for (Iter_t it1 = first, it2 = first + 1; + it2 != last and (sorted = not bk.cmp(*it2, *it1)); it1 = it2++); + if (sorted) return; + + //---------------- check if finish the subdivision ------------------- + size_t nelem = last - first; + if (level == 0 or nelem < (max_per_thread)) + { + return pdqsort(first, last, bk.cmp); + }; + + //-------------------- pivoting ---------------------------------- + pivot9(first, last, bk.cmp); + const value_t &val = const_cast<value_t &>(*first); + Iter_t c_first = first + 1, c_last = last - 1; + + while (bk.cmp(*c_first, val)) ++c_first; + while (bk.cmp(val, *c_last)) --c_last; + + while (not (c_first > c_last)) + { + std::swap(*(c_first++), *(c_last--)); + while (bk.cmp(*c_first, val)) + ++c_first; + while (bk.cmp(val, *c_last)) + --c_last; + }; + + std::swap(*first, *c_last); + + // insert the work of the second half in the stack of works + function_divide_sort(c_first, last, level - 1, counter, bk.error); + if (bk.error) return; + + // The first half is done by the same thread + function_divide_sort(first, c_last, level - 1, counter, bk.error); +}; +// +//**************************************************************************** +};// End namespace blk_detail +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/block_indirect_sort/block_indirect_sort.hpp b/boost/sort/block_indirect_sort/block_indirect_sort.hpp new file mode 100644 index 0000000000..62abde29a5 --- /dev/null +++ b/boost/sort/block_indirect_sort/block_indirect_sort.hpp @@ -0,0 +1,501 @@ +//---------------------------------------------------------------------------- +/// @file block_indirect_sort.hpp +/// @brief block indirect sort algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_BLOCK_INDIRECT_SORT_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_BLOCK_INDIRECT_SORT_HPP + +#include <atomic> +#include <boost/sort/block_indirect_sort/blk_detail/merge_blocks.hpp> +#include <boost/sort/block_indirect_sort/blk_detail/move_blocks.hpp> +#include <boost/sort/block_indirect_sort/blk_detail/parallel_sort.hpp> +#include <boost/sort/pdqsort/pdqsort.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <boost/sort/common/util/algorithm.hpp> +#include <future> +#include <iterator> + +// This value is the minimal number of threads for to use the +// block_indirect_sort algorithm +#define BOOST_NTHREAD_BORDER 6 + +namespace boost +{ +namespace sort +{ +namespace blk_detail +{ +//--------------------------------------------------------------------------- +// USING SENTENCES +//--------------------------------------------------------------------------- +namespace bs = boost::sort; +namespace bsc = bs::common; +namespace bscu = bsc::util; +using bscu::compare_iter; +using bscu::value_iter; +using bsc::range; +using bsc::destroy; +using bsc::initialize; +using bscu::nbits64; +using bs::pdqsort; +using bscu::enable_if_string; +using bscu::enable_if_not_string; +using bscu::tmsb; +// +///--------------------------------------------------------------------------- +/// @struct block_indirect_sort +/// @brief This class is the entry point of the block indirect sort. The code +/// of this algorithm is divided in several classes: +/// bis/block.hpp : basic structures used in the algorithm +/// bis/backbone.hpp : data used by all the classes +/// bis/merge_blocks.hpp : merge the internal blocks +/// bis/move_blocks.hpp : move the blocks, and obtain all the elements +/// phisicaly sorted +/// bis/parallel_sort.hpp : make the parallel sort of each part in the +/// initial division of the data +/// +//---------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, + class Compare = compare_iter<Iter_t> > +struct block_indirect_sort +{ + //------------------------------------------------------------------------ + // D E F I N I T I O N S + //------------------------------------------------------------------------ + typedef typename std::iterator_traits<Iter_t>::value_type value_t; + typedef std::atomic<uint32_t> atomic_t; + typedef range<size_t> range_pos; + typedef range<Iter_t> range_it; + typedef range<value_t *> range_buf; + typedef std::function<void(void)> function_t; + + // classes used in the internal operations of the algorithm + typedef block_pos block_pos_t; + typedef block<Block_size, Iter_t> block_t; + typedef backbone<Block_size, Iter_t, Compare> backbone_t; + typedef parallel_sort<Block_size, Iter_t, Compare> parallel_sort_t; + + typedef merge_blocks<Block_size, Group_size, Iter_t, Compare> merge_blocks_t; + typedef move_blocks<Block_size, Group_size, Iter_t, Compare> move_blocks_t; + typedef compare_block_pos<Block_size, Iter_t, Compare> compare_block_pos_t; + // + //------------------------------------------------------------------------ + // V A R I A B L E S A N D C O N S T A N T S + //------------------------------------------------------------------------ + // contains the data and the internal data structures of the algorithm for + // to be shared between the classes which are part of the algorithm + backbone_t bk; + // atomic counter for to detect the end of the works created inside + // the object + atomic_t counter; + // pointer to the uninitialized memory used for the thread buffers + value_t *ptr; + // indicate if the memory pointed by ptr is initialized + bool construct; + // range from extract the buffers for the threads + range_buf rglobal_buf; + // number of threads to use + uint32_t nthread; + // + //------------------------------------------------------------------------ + // F U N C T I O N S + //------------------------------------------------------------------------ + + block_indirect_sort(Iter_t first, Iter_t last, Compare cmp, uint32_t nthr); + + block_indirect_sort(Iter_t first, Iter_t last) : + block_indirect_sort(first, last, Compare(), + std::thread::hardware_concurrency()) { } + + + block_indirect_sort(Iter_t first, Iter_t last, Compare cmp) : + block_indirect_sort(first, last, cmp, + std::thread::hardware_concurrency()) { } + + + block_indirect_sort(Iter_t first, Iter_t last, uint32_t nthread) : + block_indirect_sort(first, last, Compare(), nthread){} + + + // + //------------------------------------------------------------------------ + // function :destroy_all + /// @brief destructor all the data structures of the class (if the memory + /// is constructed, is destroyed) and return the uninitialized + /// memory + //------------------------------------------------------------------------ + void destroy_all(void) + { + if (ptr != nullptr) + { + if (construct) + { + destroy(rglobal_buf); + construct = false; + }; + std::return_temporary_buffer(ptr); + ptr = nullptr; + }; + } + // + //------------------------------------------------------------------------ + // function :~block_indirect_sort + /// @brief destructor of the class (if the memory is constructed, is + /// destroyed) and return the uninitialized memory + //------------------------------------------------------------------------ + ~block_indirect_sort(void) + { + destroy_all(); + } + + void split_range(size_t pos_index1, size_t pos_index2, + uint32_t level_thread); + + void start_function(void); + +//------------------------------------------------------------------------- +}; // End class block_indirect_sort +//---------------------------------------------------------------------------- +// +//############################################################################ +// ## +// ## +// N O N I N L I N E F U N C T I O N S ## +// ## +// ## +//############################################################################ +// +//------------------------------------------------------------------------- +// function : block_indirect_sort +/// @brief begin with the execution of the functions stored in works +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +/// @param nthr : Number of threads to use in the process.When this value +/// is lower than 2, the sorting is done with 1 thread +//------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +block_indirect_sort<Block_size, Group_size, Iter_t, Compare> +::block_indirect_sort(Iter_t first, Iter_t last, Compare cmp, uint32_t nthr) +: bk(first, last, cmp), counter(0), ptr(nullptr), construct(false), + nthread(nthr) +{ + try + { + assert((last - first) >= 0); + size_t nelem = size_t(last - first); + if (nelem == 0) return; + + //------------------- check if sort ----------------------------------- + bool sorted = true; + for (Iter_t it1 = first, it2 = first + 1; it2 != last and (sorted = + not bk.cmp(*it2, *it1)); it1 = it2++); + if (sorted) return; + + //------------------- check if reverse sort --------------------------- + sorted = true; + for (Iter_t it1 = first, it2 = first + 1; it2 != last and (sorted = + not bk.cmp(*it1, *it2)); it1 = it2++); + + if (sorted) + { + size_t nelem2 = nelem >> 1; + Iter_t it1 = first, it2 = last - 1; + for (size_t i = 0; i < nelem2; ++i) + { + std::swap(*(it1++), *(it2--)); + }; + return; + }; + + //---------------- check if only single thread ----------------------- + size_t nthreadmax = nelem / (Block_size * Group_size) + 1; + if (nthread > nthreadmax) nthread = (uint32_t) nthreadmax; + + uint32_t nbits_size = (nbits64(sizeof(value_t)) >> 1); + if (nbits_size > 5) nbits_size = 5; + size_t max_per_thread = 1 << (18 - nbits_size); + + if (nelem < (max_per_thread) or nthread < 2) + { + //intro_sort (first, last, bk.cmp); + pdqsort(first, last, bk.cmp); + return; + }; + + //----------- creation of the temporary buffer -------------------- + ptr = std::get_temporary_buffer<value_t>(Block_size * nthread).first; + if (ptr == nullptr) + { + bk.error = true; + throw std::bad_alloc(); + }; + + rglobal_buf = range_buf(ptr, ptr + (Block_size * nthread)); + initialize(rglobal_buf, *first); + construct = true; + + // creation of the buffers for the threads + std::vector<value_t *> vbuf(nthread); + for (uint32_t i = 0; i < nthread; ++i) + { + vbuf[i] = ptr + (i * Block_size); + }; + + // Insert the first work in the stack + bscu::atomic_write(counter, 1); + function_t f1 = [&]( ) + { + start_function ( ); + bscu::atomic_sub (counter, 1); + }; + bk.works.emplace_back(f1); + + //--------------------------------------------------------------------- + // PROCESS + //--------------------------------------------------------------------- + std::vector<std::future<void> > vfuture(nthread); + + // The function launched with the futures is "execute the functions of + // the stack until this->counter is zero + // vbuf[i] is the memory from the main thread for to configure the + // thread local buffer + for (uint32_t i = 0; i < nthread; ++i) + { + auto f1 = [=, &vbuf]( ) + { bk.exec (vbuf[i], this->counter);}; + vfuture[i] = std::async(std::launch::async, f1); + }; + for (uint32_t i = 0; i < nthread; ++i) + vfuture[i].get(); + if (bk.error) throw std::bad_alloc(); + } + catch (std::bad_alloc &) + { + destroy_all(); + throw; + } +}; +// +//----------------------------------------------------------------------------- +// function : split_rage +/// @brief this function splits a range of positions in the index, and +/// depending of the size, sort directly or make to a recursive call +/// to split_range +/// @param pos_index1 : first position in the index +/// @param pos_index2 : position after the last in the index +/// @param level_thread : depth of the call. When 0 sort the blocks +//----------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +void block_indirect_sort<Block_size, Group_size, Iter_t, Compare> +::split_range(size_t pos_index1, size_t pos_index2, uint32_t level_thread) +{ + size_t nblock = pos_index2 - pos_index1; + + //------------------------------------------------------------------------- + // In the blocks not sorted, the physical position is the logical position + //------------------------------------------------------------------------- + Iter_t first = bk.get_block(pos_index1).first; + Iter_t last = bk.get_range(pos_index2 - 1).last; + + if (nblock < Group_size) + { + pdqsort(first, last, bk.cmp); + return; + }; + + size_t pos_index_mid = pos_index1 + (nblock >> 1); + atomic_t son_counter(1); + + //------------------------------------------------------------------------- + // Insert in the stack the work for the second part, and the actual thread, + // execute the first part + //------------------------------------------------------------------------- + if (level_thread != 0) + { + auto f1 = [=, &son_counter]( ) + { + split_range (pos_index_mid, pos_index2, level_thread - 1); + bscu::atomic_sub (son_counter, 1); + }; + bk.works.emplace_back(f1); + if (bk.error) return; + split_range(pos_index1, pos_index_mid, level_thread - 1); + } + else + { + Iter_t mid = first + ((nblock >> 1) * Block_size); + auto f1 = [=, &son_counter]( ) + { + parallel_sort_t (bk, mid, last); + bscu::atomic_sub (son_counter, 1); + }; + bk.works.emplace_back(f1); + if (bk.error) return; + parallel_sort_t(bk, first, mid); + }; + bk.exec(son_counter); + if (bk.error) return; + merge_blocks_t(bk, pos_index1, pos_index_mid, pos_index2); +}; + +// +//----------------------------------------------------------------------------- +// function : start_function +/// @brief this function init the process. When the number of threads is lower +/// than a predefined value, sort the elements with a parallel pdqsort. +//----------------------------------------------------------------------------- +template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare> +void block_indirect_sort<Block_size, Group_size, Iter_t, Compare> +::start_function(void) +{ + if (nthread < BOOST_NTHREAD_BORDER) + { + parallel_sort_t(bk, bk.global_range.first, bk.global_range.last); + } + else + { + size_t level_thread = nbits64(nthread - 1) - 1; + split_range(0, bk.nblock, level_thread - 1); + if (bk.error) return; + move_blocks_t k(bk); + }; +}; + +///--------------------------------------------------------------------------- +// function block_indirect_sort_call +/// @brief This class is select the block size in the block_indirect_sort +/// algorithm depending of the type and size of the data to sort +/// +//---------------------------------------------------------------------------- +template <class Iter_t, class Compare, + enable_if_string<value_iter<Iter_t>> * = nullptr> +inline void block_indirect_sort_call(Iter_t first, Iter_t last, Compare cmp, + uint32_t nthr) +{ + block_indirect_sort<128, 128, Iter_t, Compare>(first, last, cmp, nthr); +}; + +template<size_t Size> +struct block_size +{ + static constexpr const uint32_t BitsSize = + (Size == 0) ? 0 : (Size > 256) ? 9 : tmsb[Size - 1]; + static constexpr const uint32_t sz[10] = + { 4096, 4096, 4096, 4096, 2048, 1024, 768, 512, 256, 128 }; + static constexpr const uint32_t data = sz[BitsSize]; +}; +// +///--------------------------------------------------------------------------- +/// @struct block_indirect_sort_call +/// @brief This class is select the block size in the block_indirect_sort +/// algorithm depending of the type and size of the data to sort +/// +//---------------------------------------------------------------------------- +template <class Iter_t, class Compare, + enable_if_not_string<value_iter<Iter_t>> * = nullptr> +inline void block_indirect_sort_call (Iter_t first, Iter_t last, Compare cmp, + uint32_t nthr) +{ + block_indirect_sort<block_size<sizeof (value_iter<Iter_t> )>::data, 64, + Iter_t, Compare> (first, last, cmp, nthr); +}; + +// +//**************************************************************************** +}; // End namespace blk_detail +//**************************************************************************** +// +namespace bscu = boost::sort::common::util; +// +//############################################################################ +// ## +// ## +// B L O C K _ I N D I R E C T _ S O R T ## +// ## +// ## +//############################################################################ +// +//----------------------------------------------------------------------------- +// function : block_indirect_sort +/// @brief parallel sample sort algorithm (stable sort) +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +//----------------------------------------------------------------------------- +template<class Iter_t> +void block_indirect_sort(Iter_t first, Iter_t last) +{ + typedef bscu::compare_iter<Iter_t> Compare; + blk_detail::block_indirect_sort_call (first, last, Compare(), + std::thread::hardware_concurrency()); +} + +// +//----------------------------------------------------------------------------- +// function : block_indirect_sort +/// @brief parallel sample sort algorithm (stable sort) +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param nthread : Number of threads to use in the process. When this value +/// is lower than 2, the sorting is done with 1 thread +//----------------------------------------------------------------------------- +template<class Iter_t> +void block_indirect_sort(Iter_t first, Iter_t last, uint32_t nthread) +{ + typedef bscu::compare_iter<Iter_t> Compare; + blk_detail::block_indirect_sort_call(first, last, Compare(), nthread); +} +// +//----------------------------------------------------------------------------- +// function : block_indirect_sort +/// @brief parallel sample sort algorithm (stable sort) +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +//----------------------------------------------------------------------------- +template <class Iter_t, class Compare, + bscu::enable_if_not_integral<Compare> * = nullptr> +void block_indirect_sort(Iter_t first, Iter_t last, Compare comp) +{ + blk_detail::block_indirect_sort_call (first, last, comp, + std::thread::hardware_concurrency()); +} + +// +//----------------------------------------------------------------------------- +// function : block_indirect_sort +/// @brief parallel sample sort algorithm (stable sort) +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +/// @param nthread : Number of threads to use in the process. When this value +/// is lower than 2, the sorting is done with 1 thread +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare> +void block_indirect_sort (Iter_t first, Iter_t last, Compare comp, + uint32_t nthread) +{ + blk_detail::block_indirect_sort_call(first, last, comp, nthread); +} +// +//**************************************************************************** +}; // End namespace sort +}; // End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/deque_cnc.hpp b/boost/sort/common/deque_cnc.hpp new file mode 100644 index 0000000000..eb3b31ee6a --- /dev/null +++ b/boost/sort/common/deque_cnc.hpp @@ -0,0 +1,366 @@ +//---------------------------------------------------------------------------- +/// @file deque_cnc.hpp +/// @brief This file contains the implementation of the several types of +/// recursive fastmutex for read and write +/// +/// @author Copyright (c) 2010 2015 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanyingfile LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __TOOLS_DEQUE_CNC_HPP +#define __TOOLS_DEQUE_CNC_HPP + +#include <sort/tools/spinlock.hpp> +#include <vector> +#include <deque> + +namespace sort +{ +namespace tools +{ + +//########################################################################### +// ## +// ################################################################ ## +// # # ## +// # C L A S S # ## +// # S T A C K _ C N C # ## +// # # ## +// ################################################################ ## +// ## +//########################################################################### +// +//--------------------------------------------------------------------------- +/// @class deque_cnc +/// @brief This class is a concurrent stack controled by a spin_lock +/// @remarks +//--------------------------------------------------------------------------- +template<typename T, typename Allocator = std::allocator<T> > +class deque_cnc +{ +public: + //----------------------------------------------------------------------- + // D E F I N I T I O N S + //----------------------------------------------------------------------- + typedef std::deque<T, Allocator> deque_t; + typedef typename deque_t::size_type size_type; + typedef typename deque_t::difference_type difference_type; + typedef typename deque_t::value_type value_type; + typedef typename deque_t::pointer pointer; + typedef typename deque_t::const_pointer const_pointer; + typedef typename deque_t::reference reference; + typedef typename deque_t::const_reference const_reference; + typedef typename deque_t::allocator_type allocator_type; + +protected: + //------------------------------------------------------------------------ + // VARIABLES + //------------------------------------------------------------------------ + deque_t dq; + mutable spinlock spl; + +public: + // + //----------------------------------------------------------------------- + // C O N S T R U C T O R S A N D D E S T R U C T O R + //----------------------------------------------------------------------- + // + //----------------------------------------------------------------------- + // function : deque_cnc + /// @brief constructor + //---------------------------------------------------------------------- + explicit inline deque_cnc(void): dq() { }; +// + //---------------------------------------------------------------------- + // function : deque_cnc + /// @brief constructor + /// @param [in] ALLC : Allocator + //---------------------------------------------------------------------- + explicit inline deque_cnc(const Allocator &ALLC): dq(ALLC){ }; + // + //---------------------------------------------------------------------- + // function : ~deque_cnc + /// @brief Destructor + //---------------------------------------------------------------------- + virtual ~deque_cnc(void){ dq.clear(); }; + // + //---------------------------------------------------------------------- + // function : clear + /// @brief Delete all the elements of the deque_cnc. + //---------------------------------------------------------------------- + void clear(void) + { + std::lock_guard < spinlock > S(spl); + dq.clear(); + }; + // + //------------------------------------------------------------------------ + // function : swap + /// @brief swap the data between the two deque_cnc + /// @param [in] A : deque_cnc to swap + /// @return none + //----------------------------------------------------------------------- + void swap(deque_cnc & A) noexcept + { + if (this == &A) return; + std::lock_guard < spinlock > S(spl); + dq.swap(A.dq); + }; + // + //----------------------------------------------------------------------- + // S I Z E , M A X _ S I Z E , R E S I Z E + // C A P A C I T Y , E M P T Y , R E S E R V E + //----------------------------------------------------------------------- + // + //------------------------------------------------------------------------ + // function : size + /// @brief return the number of elements in the deque_cnc + /// @return number of elements in the deque_cnc + //------------------------------------------------------------------------ + size_type size(void) const noexcept + { + std::lock_guard < spinlock > S(spl); + return dq.size(); + }; + // + //------------------------------------------------------------------------ + // function :max_size + /// @brief return the maximun size of the container + /// @return maximun size of the container + //------------------------------------------------------------------------ + size_type max_size(void) const noexcept + { + std::lock_guard < spinlock > S(spl); + return (dq.max_size()); + }; + // + //------------------------------------------------------------------------- + // function : shrink_to_fit + /// @brief resize the current vector size and change to size.\n + /// If sz is smaller than the current size, delete elements to end\n + /// If sz is greater than the current size, insert elements to the + /// end with the value c + /// @param [in] sz : new size of the deque_cnc after the resize + /// @param [in] c : Value to insert if sz is greather than the current size + /// @return none + //------------------------------------------------------------------------ + void shrink_to_fit() + { + std::lock_guard < spinlock > S(spl); + dq.shrink_to_fit(); + }; + // + //------------------------------------------------------------------------ + // function : empty + /// @brief indicate if the map is empty + /// @return true if the map is empty, false in any other case + //------------------------------------------------------------------------ + bool empty(void) const noexcept + { + std::lock_guard < spinlock > S(spl); + return (dq.empty()); + }; + //--------------------------------------------------------------------------- + // function : push_back + /// @brief Insert one element in the back of the container + /// @param [in] D : value to insert. Can ve a value, a reference or an + /// rvalue + //--------------------------------------------------------------------------- + void push_back(const value_type & D) + { + std::lock_guard < spinlock > S(spl); + dq.push_back(D); + }; + + //------------------------------------------------------------------------ + // function : emplace_back + /// @brief Insert one element in the back of the container + /// @param [in] args :group of arguments for to build the object to insert + //------------------------------------------------------------------------- + template<class ... Args> + void emplace_back(Args && ... args) + { + std::lock_guard < spinlock > S(spl); + dq.emplace_back(std::forward <Args>(args) ...); + }; + //------------------------------------------------------------------------ + // function : push_back + /// @brief Insert one element in the back of the container + /// @param [in] D : deque to insert in the actual deque, inserting a copy + /// of the elements + /// @return reference to the deque after the insertion + //------------------------------------------------------------------------ + template<class Allocator2> + deque_cnc & push_back(const std::deque<value_type, Allocator2> & D) + { + std::lock_guard < spinlock > S(spl); + for (size_type i = 0; i < D.size(); ++i) + dq.push_back(D[i]); + return *this; + }; + //------------------------------------------------------------------------ + // function : push_back + /// @brief Insert one element in the back of the container + /// @param [in] D : deque to insert in the actual deque, inserting a move + /// of the elements + /// @return reference to the deque after the insertion + //------------------------------------------------------------------------ + deque_cnc & push_back(std::deque<value_type, Allocator> && D) + { + std::lock_guard < spinlock > S(spl); + for (size_type i = 0; i < D.size(); ++i) + dq.emplace_back(std::move(D[i])); + return *this; + }; + // + //------------------------------------------------------------------------ + // function :pop_back + /// @brief erase the last element of the container + //----------------------------------------------------------------------- + void pop_back(void) + { + std::lock_guard < spinlock > S(spl); + dq.pop_back(); + }; + // + //------------------------------------------------------------------------ + // function :pop_copy_back + /// @brief erase the last element and return a copy over P + /// @param [out] P : reference to a variable where copy the element + /// @return code of the operation + /// true - Element erased + /// false - Empty tree + //------------------------------------------------------------------------ + bool pop_copy_back(value_type & P) + { //-------------------------- begin ----------------------------- + std::lock_guard < spinlock > S(spl); + if (dq.size() == 0) return false; + P = dq.back(); + dq.pop_back(); + return true; + }; + // + //------------------------------------------------------------------------ + // function :pop_move_back + /// @brief erase the last element and move over P + /// @param [out] P : reference to a variable where move the element + /// @return code of the operation + /// true - Element erased + /// false - Empty tree + //------------------------------------------------------------------------ + bool pop_move_back(value_type & P) + { //-------------------------- begin ----------------------------- + std::lock_guard < spinlock > S(spl); + if (dq.size() == 0) return false; + P = std::move(dq.back()); + dq.pop_back(); + return true; + }; + + //------------------------------------------------------------------------ + // function : push_front + /// @brief Insert one copy of the element in the front of the container + /// @param [in] D : value to insert + //------------------------------------------------------------------------ + void push_front(const value_type & D) + { + std::lock_guard < spinlock > S(spl); + dq.push_front(D); + }; + + //------------------------------------------------------------------------ + // function : emplace_front + /// @brief Insert one element in the front of the container + /// @param [in] args :group of arguments for to build the object to insert + //------------------------------------------------------------------------- + template<class ... Args> + void emplace_front(Args && ... args) + { + std::lock_guard < spinlock > S(spl); + dq.emplace_front(std::forward <Args>(args) ...); + }; + //------------------------------------------------------------------------ + // function : push_front + /// @brief Insert a copy of the elements of the deque V1 in the front + /// of the container + /// @param [in] V1 : deque with the elements to insert + /// @return reference to the deque after the insertion + //------------------------------------------------------------------------ + template<class Allocator2> + deque_cnc & push_front(const std::deque<value_type, Allocator2> & V1) + { + std::lock_guard < spinlock > S(spl); + for (size_type i = 0; i < V1.size(); ++i) + dq.push_front(V1[i]); + return *this; + }; + //----------------------------------------------------------------------- + // function : push_front + /// @brief Insert a move of the elements of the deque V1 in the front + /// of the container + /// @param [in] V1 : deque with the elements to insert + /// @return reference to the deque after the insertion + //----------------------------------------------------------------------- + deque_cnc & push_front(std::deque<value_type, Allocator> && V1) + { + std::lock_guard < spinlock > S(spl); + for (size_type i = 0; i < V1.size(); ++i) + dq.emplace_front(std::move(V1[i])); + return *this; + }; + // + //----------------------------------------------------------------------- + // function :pop_front + /// @brief erase the first element of the container + //----------------------------------------------------------------------- + void pop_front(void) + { + std::lock_guard < spinlock > S(spl); + dq.pop_front(); + }; + // + //----------------------------------------------------------------------- + // function :pop_copy_front + /// @brief erase the first element of the tree and return a copy over P + /// @param [out] P : reference to a variable where copy the element + /// @return code of the operation + /// true- Element erased + /// false - Empty tree + //----------------------------------------------------------------------- + bool pop_copy_front(value_type & P) + { //-------------------------- begin ----------------------------- + std::lock_guard < spinlock > S(spl); + if (dq.size() == 0) return false; + P = dq.front(); + dq.pop_front(); + return true; + }; + // + //------------------------------------------------------------------------ + // function :pop_move_front + /// @brief erase the first element of the tree and return a move over P + /// @param [out] P : reference to a variable where move the element + /// @return code of the operation + /// true- Element erased + /// false - Empty tree + //------------------------------------------------------------------------ + bool pop_move_front(value_type & P) + { //-------------------------- begin ----------------------------- + std::lock_guard < spinlock > S(spl); + if (dq.size() == 0) return false; + P = std::move(dq.front()); + dq.pop_front(); + return true; + }; +}; +// end class deque_cnc + +//*************************************************************************** +};// end namespace tools +};// end namespace sort +//*************************************************************************** +#endif diff --git a/boost/sort/common/file_vector.hpp b/boost/sort/common/file_vector.hpp new file mode 100644 index 0000000000..1dc62fc02f --- /dev/null +++ b/boost/sort/common/file_vector.hpp @@ -0,0 +1,272 @@ +//---------------------------------------------------------------------------- +/// @file file_vector.hpp +/// @brief This file contains functions for to work with random data and files +/// Have functions for to create a vector with random data, and +/// functions for lo load a vector of numbers or strings from the file +/// +/// @author Copyright (c) 2015 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanyingfile LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_FILE_VECTOR_HPP +#define __BOOST_SORT_COMMON_FILE_VECTOR_HPP + +#include <ios> +#include <cstdio> +#include <cstdlib> +#include <ciso646> +#include <vector> +#include <string> +#include <fstream> +#include <sstream> +#include <iostream> +#include <random> +#include <cstdint> + +namespace boost +{ +namespace sort +{ +namespace common +{ +// +//----------------------------------------------------------------------------- +// function : generate_file +/// @brief Generate a binary file filed with random numbers of 64 bits +/// @param [in] filename : name of the file +/// @param [in] NElem : number of 64 bits numbers to insert in the file +/// @exception +/// @return +/// @remarks +//----------------------------------------------------------------------------- +static int generate_file(const std::string & filename, size_t NElem) +{ //------------------------------- begin ---------------------------------- + std::ofstream ofile; + ofile.open(filename, std::ios_base::out | std::ios_base::binary | + std::ios_base::trunc); + if (ofile.bad()) + { + throw std::ios_base::failure("could not open file \n"); + }; + std::mt19937_64 my_rand(0); + + for (size_t i = 0; i < NElem; ++i) + { + uint64_t Aux = my_rand(); + ofile.write((char *) &Aux, 8); + } + ofile.close(); + return 0; +}; +// +//----------------------------------------------------------------------------- +// function : fill_vector_uint64 +/// @brief : fill a vector of uint64_t elements from a file +/// @param [in] filename : name of the file +/// @param [in] V : vector to fill +/// @param [in] NElem : number of elements for to read from the file +/// @exception +/// @return +/// @remarks +//----------------------------------------------------------------------------- +static int fill_vector_uint64(const std::string & filename, + std::vector<uint64_t> & V, size_t NElem) +{ //----------------------- begin ------------------------------------------ + std::ifstream input(filename, std::ios_base::in | std::ios_base::binary); + if (input.fail()) + { + throw std::ios_base::failure("could not open file \n"); + }; + //------------------------------------------------------------------------ + // Calculate the lenght of the file and the number of elements inside + //------------------------------------------------------------------------ + input.seekg(0, std::ios_base::end); + size_t length = input.tellg(); + size_t uCount = length / 8; + if (uCount < NElem) + { + throw std::ios_base::failure("incorrect lenght of the file\n"); + }; + V.clear(); + V.reserve(NElem); + + uint64_t Aux = 0; + input.seekg(0, std::ios_base::beg); + for (size_t i = 0; i < NElem; ++i) + { + input.read(reinterpret_cast<char *>(&Aux), 8); + V.push_back(Aux); + }; + input.close(); + return 0; +}; + +// +//----------------------------------------------------------------------------- +// function :write_file_uint64 +/// @brief Write a file with the contnt of a vector of Uint64_t elements +/// @param [in] V : vector from read the numbersl +/// @param [in] filename : name of the file +/// @exception +/// @return +/// @remarks +//----------------------------------------------------------------------------- +static int write_file_uint64 (const std::vector<uint64_t> & V, + const std::string & filename) +{ //--------------------------------- begin -------------------------------- + std::ofstream ofile; + ofile.open(filename, + std::ios_base::out | std::ios_base::binary + | std::ios_base::trunc); + if (ofile.bad()) + { + throw std::ios_base::failure("could not open file \n"); + }; + for (size_t i = 0; i < V.size(); ++i) + { + ofile.write((char *) &(V[i]), 8); + } + ofile.close(); + return 0; +}; +// +//----------------------------------------------------------------------------- +// function : fill_vector_string +/// @brief fill a vector of strings from a file +/// @param [in] filename : name of the file from read the strings +/// @param [in] V : vector where store the strings +/// @param [in] NElem : Number of strings for to read from the file +/// @exception +/// @return +/// @remarks +//----------------------------------------------------------------------------- +static int fill_vector_string (const std::string & filename, + std::vector<std::string> & V, size_t NElem) +{ //----------------------- begin ------------------------------------------ + std::ifstream input(filename, std::ios_base::in | std::ios_base::binary); + if (input.fail()) + { + throw std::ios_base::failure("could not open file \n"); + }; + //------------------------------------------------------------------------ + // Calculate the lenght of the file and the number of elements inside + //------------------------------------------------------------------------ + input.seekg(0, std::ios_base::end); + V.clear(); + V.reserve(NElem); + + std::string inval; + input.seekg(0, std::ios_base::beg); + + for (size_t i = 0; i < NElem; ++i) + { + if (!input.eof()) + { + input >> inval; + V.push_back(inval); + inval.clear(); + } + else + { + throw std::ios_base::failure("Insuficient lenght of the file\n"); + }; + }; + input.close(); + return 0; +}; + +// +//----------------------------------------------------------------------------- +// function :write_file_string +/// @brief : write a file with the strings of a vector +/// @param [in] V : vector from read the sttrings +/// @param [in] filename : file where store the strings +/// @exception +/// @return +/// @remarks +//----------------------------------------------------------------------------- +static int write_file_string (const std::vector<std::string> & V, + const std::string & filename) +{ //--------------------------------- begin -------------------------------- + std::ofstream ofile; + ofile.open(filename, + std::ios_base::out | std::ios_base::binary + | std::ios_base::trunc); + if (ofile.bad()) + { + throw std::ios_base::failure("could not open file \n"); + }; + for (size_t i = 0; i < V.size(); ++i) + { + ofile.write((char *) &(V[i][0]), V[i].size()); + ofile.put(0x0); + } + ofile.close(); + return 0; +}; +//--------------------------------------------------------------------------- +/// @struct uint64_file_generator +/// @brief This struct is a number generator from a file, with several options +/// for to limit the numbers between 0 and Max_Val +/// @remarks +//--------------------------------------------------------------------------- +struct uint64_file_generator +{ //---------------------------------------------------------------------- + // VARIABLES + //---------------------------------------------------------------------- + std::ifstream input; + size_t NMax, Pos; + size_t Max_Val; + std::string s; + + //---------------------------------------------------------------------- + // FUNCTIONS + //---------------------------------------------------------------------- + uint64_file_generator(const std::string & filename) + { //---------------------------- begin --------------------------------- + s = filename; + input.open(filename, std::ios_base::in | std::ios_base::binary); + if (input.fail() or input.bad()) + { + throw std::ios_base::failure("could not open file \n"); + }; + //-------------------------------------------------------------------- + // Calculate the lenght of the file and the number of elements inside + //-------------------------------------------------------------------- + input.seekg(0, std::ios_base::end); + size_t length = input.tellg(); + NMax = length / 8; + Pos = 0; + Max_Val = ~((size_t) 0); + input.seekg(0); + }; + + void set_max_val(size_t MV){ Max_Val = MV; }; + + size_t size() const { return NMax; }; + + uint64_t get(void) + { + uint64_t Aux; + input.read(reinterpret_cast<char *>(&Aux), 8); + return (Aux % Max_Val); + }; + + uint64_t operator ( )(){ return get(); }; + + void reset(void) { input.seekg(0, std::ios_base::beg); }; + + ~uint64_file_generator() { if (input.is_open()) input.close(); }; +}; +// +//**************************************************************************** +};// end namespace benchmark +};// end namespace sort +};// end namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/indirect.hpp b/boost/sort/common/indirect.hpp new file mode 100644 index 0000000000..a55ef82023 --- /dev/null +++ b/boost/sort/common/indirect.hpp @@ -0,0 +1,153 @@ +//---------------------------------------------------------------------------- +/// @file indirect.hpp +/// @brief Indirect algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_COMMON_INDIRECT_HPP +#define __BOOST_SORT_PARALLEL_COMMON_INDIRECT_HPP + +//#include <boost/sort/common/atomic.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <functional> +#include <iterator> +#include <type_traits> +#include <vector> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +// +//--------------------------------------------------------------------------- +/// @struct less_ptr_no_null +/// +/// @remarks this is the comparison object for pointers. Compare the objects +/// pointed by the iterators +//--------------------------------------------------------------------------- +template<class Iter_t, class Compare = util::compare_iter<Iter_t> > +struct less_ptr_no_null +{ + //----------------------------- Variables ----------------------- + Compare comp; // comparison object of the elements pointed by Iter_t + + //------------------------------------------------------------------------ + // function : less_ptr_no_null + /// @brief constructor from a Compare object + /// @param C1 : comparison object + //----------------------------------------------------------------------- + less_ptr_no_null(Compare C1 = Compare()): comp(C1) { }; + + //------------------------------------------------------------------------ + // function : operator ( ) + /// @brief Make the comparison of the objects pointed by T1 and T2, using + // the internal comp + // + /// @param T1 : first iterator + /// @param T2 : second iterator + /// @return bool result of the comparison + //----------------------------------------------------------------------- + bool operator( )(Iter_t T1, Iter_t T2) const + { + return comp(*T1, *T2); + }; +}; +// +//----------------------------------------------------------------------------- +// function : create_index +/// @brief From a vector of objects, create a vector of iterators to +/// the objects +/// +/// @param first : iterator to the first element of the range +/// @param last : iterator to the element after the last of the range +/// @param index : vector where store the iterators +//----------------------------------------------------------------------------- +template<class Iter_t> +static void create_index(Iter_t first, Iter_t last, std::vector<Iter_t> &index) +{ + auto nelem = last - first; + assert(nelem >= 0); + index.clear(); + index.reserve(nelem); + for (; first != last; ++first) index.push_back(first); +}; +// +//----------------------------------------------------------------------------- +// function : sort_index +/// @brief This function transform a logical sort of the elements in the index +/// in a physical sort +// +/// @param global_first : iterator to the first element of the data +/// @param [in] index : vector of the iterators +//----------------------------------------------------------------------------- +template<class Iter_t> +static void sort_index(Iter_t global_first, std::vector<Iter_t> &index) +{ + typedef util::value_iter<Iter_t> value_t; + + size_t pos_dest = 0; + size_t pos_src = 0; + size_t pos_in_vector = 0; + size_t nelem = index.size(); + Iter_t it_dest, it_src; + + while (pos_in_vector < nelem) + { + while (pos_in_vector < nelem and + (size_t(index[pos_in_vector] - global_first)) == pos_in_vector) + { + ++pos_in_vector; + }; + + if (pos_in_vector == nelem) return; + pos_dest = pos_src = pos_in_vector; + it_dest = global_first + pos_dest; + value_t Aux = std::move(*it_dest); + + while ((pos_src = (size_t(index[pos_dest] - global_first))) + != pos_in_vector) + { + index[pos_dest] = it_dest; + it_src = global_first + pos_src; + *it_dest = std::move(*it_src); + it_dest = it_src; + pos_dest = pos_src; + }; + + *it_dest = std::move(Aux); + index[pos_dest] = it_dest; + ++pos_in_vector; + }; +}; + +template<class func, class Iter_t, class Compare = compare_iter<Iter_t> > +static void indirect_sort(func method, Iter_t first, Iter_t last, Compare comp) +{ + auto nelem = (last - first); + assert(nelem >= 0); + if (nelem < 2) return; + std::vector<Iter_t> index; + index.reserve((size_t) nelem); + create_index(first, last, index); + less_ptr_no_null<Iter_t, Compare> index_comp(comp); + method(index.begin(), index.end(), index_comp); + sort_index(first, index); +}; + +// +//**************************************************************************** +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/int_array.hpp b/boost/sort/common/int_array.hpp new file mode 100644 index 0000000000..22c3b0c5a4 --- /dev/null +++ b/boost/sort/common/int_array.hpp @@ -0,0 +1,75 @@ +//---------------------------------------------------------------------------- +/// @file int_array.hpp +/// @brief This file contains the struct int_array , which is an array of +/// uint64_t elements, being the template parameter NN the number of +/// elements in the array +/// +/// @author Copyright (c) 2010 2015 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanyingfile LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_INT_ARRAY_HPP +#define __BOOST_SORT_COMMON_INT_ARRAY_HPP + +#include <cstdint> +#include <iostream> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +template<uint32_t NN> +struct int_array +{ + uint64_t M[NN]; + + template<class generator> + static int_array<NN> generate(generator & gen) + { + int_array<NN> result; + for (uint32_t i = 0; i < NN; ++i) + { + result.M[i] = gen(); + }; + return result; + }; + + uint64_t counter(void) const + { + uint64_t Acc = M[0]; + for (uint32_t i = 1; i < NN; Acc += M[i++]) + ; + return Acc; + }; +}; + +template<class IA> +struct H_comp +{ + bool operator ( )(const IA & A1, const IA & A2) const + { + return (A1.counter() < A2.counter()); + }; +}; + +template<class IA> +struct L_comp +{ + bool operator ( )(const IA & A1, const IA & A2) const + { + return (A1.M[0] < A2.M[0]); + }; +}; +//*************************************************************************** +};// End namespace benchmark +};// End namespace sort +};// End namespace boost +//*************************************************************************** +#endif // end of int_array.hpp diff --git a/boost/sort/common/merge_block.hpp b/boost/sort/common/merge_block.hpp new file mode 100644 index 0000000000..9a7b118270 --- /dev/null +++ b/boost/sort/common/merge_block.hpp @@ -0,0 +1,418 @@ +//---------------------------------------------------------------------------- +/// @file merge_block.hpp +/// @brief This file constains the class merge_block, which is part of the +/// block_indirect_sort algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_MERGE_BLOCK_HPP +#define __BOOST_SORT_COMMON_MERGE_BLOCK_HPP + +#include <boost/sort/common/range.hpp> +#include <boost/sort/common/rearrange.hpp> +#include <boost/sort/common/util/merge.hpp> +#include <boost/sort/common/util/traits.hpp> + +namespace boost +{ +namespace sort +{ +namespace common +{ +///--------------------------------------------------------------------------- +/// @struct merge_block +/// @brief This contains all the information shared betwen the classes of the +/// block indirect sort algorithm + +//---------------------------------------------------------------------------- +template<class Iter_t, class Compare, uint32_t Power2 = 10> +struct merge_block +{ + //------------------------------------------------------------------------- + // D E F I N I T I O N S + //------------------------------------------------------------------------- + typedef util::value_iter<Iter_t> value_t; + typedef range<size_t> range_pos; + typedef range<Iter_t> range_it; + typedef range<value_t *> range_buf; + typedef typename std::vector<size_t>::iterator it_index; + typedef util::circular_buffer<value_t, Power2 + 1> circular_t; + + //------------------------------------------------------------------------ + // CONSTANTS + //------------------------------------------------------------------------ + const size_t BLOCK_SIZE = (size_t) 1 << Power2; + const size_t LOG_BLOCK = Power2; + + //------------------------------------------------------------------------ + // V A R I A B L E S + //------------------------------------------------------------------------ + // range with all the element to sort + range<Iter_t> global_range; + + // index vector of block_pos elements + std::vector<size_t> index; + + // Number of elements to sort + size_t nelem; + + // Number of blocks to sort + size_t nblock; + + // Number of elements in the last block (tail) + size_t ntail; + + // object for to compare two elements + Compare cmp; + + // range of elements of the last block (tail) + range_it range_tail; + + // circular buffer + circular_t * ptr_circ; + + // indicate if the circulr buffer is owned by the data structure + // or is received as parameter + bool owned; + + // + //------------------------------------------------------------------------ + // F U N C T I O N S + //------------------------------------------------------------------------ + // + //------------------------------------------------------------------------ + // function : merge_block + /// @brief constructor of the class + // + /// @param first : iterator to the first element of the range to sort + /// @param last : iterator after the last element to the range to sort + /// @param comp : object for to compare two elements pointed by Iter_t + /// iterators + //------------------------------------------------------------------------ + merge_block (Iter_t first, Iter_t last, Compare comp, + circular_t *pcirc_buffer) + : global_range(first, last), cmp(comp), ptr_circ(pcirc_buffer), + owned(pcirc_buffer == nullptr) + { + assert((last - first) >= 0); + if (first == last) return; // nothing to do + + nelem = size_t(last - first); + nblock = (nelem + BLOCK_SIZE - 1) / BLOCK_SIZE; + ntail = (nelem % BLOCK_SIZE); + index.reserve(nblock + 1); + + for (size_t i = 0; i < nblock; ++i) + index.emplace_back(i); + + range_tail.first = first + ((nblock - 1) << LOG_BLOCK); + range_tail.last = last; + if (owned) + { + ptr_circ = new circular_t; + ptr_circ->initialize(*first); + }; + } + + merge_block(Iter_t first, Iter_t last, Compare comp) + : merge_block(first, last, comp, nullptr) { }; + + ~ merge_block() + { + if (ptr_circ != nullptr and owned) + { + delete ptr_circ; + ptr_circ = nullptr; + }; + }; + //------------------------------------------------------------------------- + // function : get_range + /// @brief obtain the range in the position pos + /// @param pos : position of the range + /// @return range required + //------------------------------------------------------------------------- + range_it get_range(size_t pos) const + { + Iter_t it1 = global_range.first + (pos << LOG_BLOCK); + Iter_t it2 = (pos == (nblock - 1)) ? + global_range.last : it1 + BLOCK_SIZE; + return range_it(it1, it2); + }; + //------------------------------------------------------------------------- + // function : get_group_range + /// @brief obtain the range of the contiguous blocks beginning in the + // position pos + /// @param pos : position of the first range + /// @param nrange : number of ranges of the group + /// @return range required + //------------------------------------------------------------------------- + range_it get_group_range(size_t pos, size_t nrange) const + { + Iter_t it1 = global_range.first + (pos << LOG_BLOCK); + + Iter_t it2 = ((pos + nrange) == nblock)?global_range.last: global_range.first + ((pos + nrange) << LOG_BLOCK); + //Iter_t it2 = global_range.first + ((pos + nrange) << LOG_BLOCK); + //if ((pos + nrange) == nblock) it2 = global_range.last; + + return range_it(it1, it2); + }; + //------------------------------------------------------------------------- + // function : is_tail + /// @brief indicate if a block is the tail + /// @param pos : position of the block + /// @return true : taiol false : not tail + //------------------------------------------------------------------------- + bool is_tail(size_t pos) const + { + return (pos == (nblock - 1) and ntail != 0); + }; + //------------------------------------------------------------------------- + // function : + /// @brief + /// @param + /// @return + //------------------------------------------------------------------------- + void merge_range_pos(it_index itx_first, it_index itx_mid, + it_index itx_last); + + //------------------------------------------------------------------------- + // function : move_range_pos_backward + /// @brief Move backward the elements of a range of blocks in a index + /// @param itx_first : iterator to the position of the first block + /// @param itx_last : itertor to the position of the last block + /// @param npos : number of positions to move. Must be less than BLOCK_SIZE + /// @return + //------------------------------------------------------------------------- + void move_range_pos_backward(it_index itx_first, it_index itx_last, + size_t npos); + + //------------------------------------------------------------------------- + // function : rearrange_with_index + /// @brief rearrange the blocks with the relative positions of the index + /// @param + /// @param + /// @param + /// @return + //------------------------------------------------------------------------- + void rearrange_with_index(void); + +//--------------------------------------------------------------------------- +};// end struct merge_block +//--------------------------------------------------------------------------- +// +//############################################################################ +// ## +// N O N I N L I N E F U N C T IO N S ## +// ## +//############################################################################ +// +//------------------------------------------------------------------------- +// function : +/// @brief +/// @param +/// @return +//------------------------------------------------------------------------- +template<class Iter_t, class Compare, uint32_t Power2> +void merge_block<Iter_t, Compare, Power2> +::merge_range_pos(it_index itx_first, it_index itx_mid,it_index itx_last) +{ + assert((itx_last - itx_mid) >= 0 and (itx_mid - itx_first) >= 0); + + size_t nelemA = (itx_mid - itx_first), nelemB = (itx_last - itx_mid); + if (nelemA == 0 or nelemB == 0) return; + + //------------------------------------------------------------------- + // Create two index with the position of the blocks to merge + //------------------------------------------------------------------- + std::vector<size_t> indexA, indexB; + indexA.reserve(nelemA + 1); + indexB.reserve(nelemB); + + indexA.insert(indexA.begin(), itx_first, itx_mid); + indexB.insert(indexB.begin(), itx_mid, itx_last); + + it_index itx_out = itx_first; + it_index itxA = indexA.begin(), itxB = indexB.begin(); + range_it rngA, rngB; + Iter_t itA = global_range.first, itB = global_range.first; + bool validA = false, validB = false; + + while (itxA != indexA.end() and itxB != indexB.end()) + { //---------------------------------------------------------------- + // Load valid ranges from the itxA and ItxB positions + //---------------------------------------------------------------- + if (not validA) + { + rngA = get_range(*itxA); + itA = rngA.first; + validA = true; + }; + if (not validB) + { + rngB = get_range(*itxB); + itB = rngB.first; + validB = true; + }; + //---------------------------------------------------------------- + // If don't have merge betweeen the blocks, pass directly the + // position of the block to itx_out + //---------------------------------------------------------------- + if (ptr_circ->size() == 0) + { + if (not cmp(*rngB.front(), *rngA.back())) + { + *(itx_out++) = *(itxA++); + validA = false; + continue; + }; + if (cmp(*rngB.back(), *rngA.front())) + { + if (not is_tail(*itxB)) + *(itx_out++) = *itxB; + else ptr_circ->push_move_back(rngB.first, rngB.size()); + ++itxB; + validB = false; + continue; + }; + }; + //---------------------------------------------------------------- + // Normal merge + //---------------------------------------------------------------- + bool side = util::merge_circular(itA, rngA.last, itB, rngB.last, + *ptr_circ, cmp, itA, itB); + if (side) + { // rngA is finished + ptr_circ->pop_move_front(rngA.first, rngA.size()); + *(itx_out++) = *(itxA++); + validA = false; + } + else + { // rngB is finished + if (not is_tail(*itxB)) + { + ptr_circ->pop_move_front(rngB.first, rngB.size()); + *(itx_out++) = *itxB; + }; + ++itxB; + validB = false; + }; + }; // end while + + if (itxA == indexA.end()) + { // the index A is finished + rngB = get_range(*itxB); + ptr_circ->pop_move_front(rngB.first, ptr_circ->size()); + while (itxB != indexB.end()) + *(itx_out++) = *(itxB++); + } + else + { // The list B is finished + rngA = get_range(*itxA); + if (ntail != 0 and indexB.back() == (nblock - 1)) // exist tail + { // add the tail block to indexA, and shift the element + indexA.push_back(indexB.back()); + size_t numA = size_t(itA - rngA.first); + ptr_circ->pop_move_back(rngA.first, numA); + move_range_pos_backward(itxA, indexA.end(), ntail); + }; + + ptr_circ->pop_move_front(rngA.first, ptr_circ->size()); + while (itxA != indexA.end()) + *(itx_out++) = *(itxA++); + }; +}; + +//------------------------------------------------------------------------- +// function : move_range_pos_backward +/// @brief Move backward the elements of a range of blocks in a index +/// @param itx_first : iterator to the position of the first block +/// @param itx_last : itertor to the position of the last block +/// @param npos : number of positions to move. Must be less than BLOCK_SIZE +/// @return +//------------------------------------------------------------------------- +template<class Iter_t, class Compare, uint32_t Power2> +void merge_block<Iter_t, Compare, Power2> +::move_range_pos_backward(it_index itx_first, it_index itx_last, size_t npos) +{ + assert((itx_last - itx_first) >= 0 and npos <= BLOCK_SIZE); + + //-------------------------------------------------------------------- + // Processing the last block. Must be ready fore to accept npos + // elements from the upper block + //-------------------------------------------------------------------- + range_it rng1 = get_range(*(itx_last - 1)); + assert(rng1.size() >= npos); + if (rng1.size() > npos) + { + size_t nmove = rng1.size() - npos; + util::move_backward(rng1.last, rng1.first, rng1.first + nmove); + }; + //-------------------------------------------------------------------- + // Movement of elements between blocks + //-------------------------------------------------------------------- + for (it_index itx = itx_last - 1; itx != itx_first;) + { + --itx; + range_it rng2 = rng1; + rng1 = get_range(*itx); + Iter_t it_mid1 = rng1.last - npos, it_mid2 = rng2.first + npos; + util::move_backward(it_mid2, it_mid1, rng1.last); + util::move_backward(rng1.last, rng1.first, it_mid1); + }; +}; +//------------------------------------------------------------------------- +// function : rearrange_with_index +/// @brief rearrange the blocks with the relative positions of the index +/// @param +/// @param +/// @param +/// @return +//------------------------------------------------------------------------- +template<class Iter_t, class Compare, uint32_t Power2> +void merge_block<Iter_t, Compare, Power2> +::rearrange_with_index(void) +{ //-------------------------------------------------------------------- + // Code + //-------------------------------------------------------------------- + size_t pos_dest, pos_src, pos_ini; + size_t nelem = index.size(); + + ptr_circ->clear(); + value_t * aux = ptr_circ->get_buffer(); + range_buf rng_buf(aux, aux + ptr_circ->NMAX); + + pos_ini = 0; + while (pos_ini < nelem) + { + while (pos_ini < nelem and index[pos_ini] == pos_ini) + ++pos_ini; + if (pos_ini == nelem) return; + pos_dest = pos_src = pos_ini; + rng_buf = move_forward(rng_buf, get_range(pos_ini)); + pos_src = index[pos_ini]; + + while (pos_src != pos_ini) + { + move_forward(get_range(pos_dest), get_range(pos_src)); + index[pos_dest] = pos_dest; + pos_dest = pos_src; + pos_src = index[pos_src]; + }; + move_forward(get_range(pos_dest), rng_buf); + index[pos_dest] = pos_dest; + ++pos_ini; + }; +}; + +//**************************************************************************** +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +#endif diff --git a/boost/sort/common/merge_four.hpp b/boost/sort/common/merge_four.hpp new file mode 100644 index 0000000000..edfb2ffc72 --- /dev/null +++ b/boost/sort/common/merge_four.hpp @@ -0,0 +1,327 @@ +//---------------------------------------------------------------------------- +/// @file merge_four.hpp +/// @brief This file have the functions for to merge 4 buffers +/// +/// @author Copyright (c) 2016 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_UTIL_MERGE_FOUR_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_UTIL_MERGE_FOUR_HPP + +#include <boost/sort/common/util/traits.hpp> +#include <boost/sort/common/range.hpp> +#include <functional> +#include <iterator> +#include <memory> +#include <vector> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +// +//############################################################################ +// ## +// F U S I O N O F ## +// ## +// F O U R E L E M E N T S R A N G E ## +// ## +//############################################################################ +// + +//----------------------------------------------------------------------------- +// function : less_range +/// @brief Compare the elements pointed by it1 and it2, and if they +/// are equals, compare their position, doing a stable comparison +/// +/// @param it1 : iterator to the first element +/// @param pos1 : position of the object pointed by it1 +/// @param it2 : iterator to the second element +/// @param pos2 : position of the element pointed by it2 +/// @param comp : comparison object +/// @return result of the comparison +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare = typename util::compare_iter<Iter_t> > +inline bool less_range(Iter_t it1, uint32_t pos1, Iter_t it2, uint32_t pos2, + Compare comp = Compare()) +{ + return (comp(*it1, *it2)) ? true : + (pos2 < pos1) ? false : not (comp(*it2, *it1)); +}; + +//----------------------------------------------------------------------------- +// function : full_merge4 +/// @brief Merge four ranges +/// +/// @param dest: range where move the elements merged. Their size must be +/// greater or equal than the sum of the sizes of the ranges +/// in vrange_input +/// @param vrange_input : array of ranges to merge +/// @param nrange_input : number of ranges in vrange_input +/// @param comp : comparison object +/// @return range with all the elements moved with the size adjusted +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +range<Iter1_t> full_merge4(const range<Iter1_t> &rdest, + range<Iter2_t> vrange_input[4], + uint32_t nrange_input, Compare comp) +{ + typedef range<Iter1_t> range1_t; + typedef util::value_iter<Iter1_t> type1; + typedef util::value_iter<Iter2_t> type2; + static_assert (std::is_same< type1, type2 >::value, + "Incompatible iterators\n"); + + size_t ndest = 0; + uint32_t i = 0; + while (i < nrange_input) + { + if (vrange_input[i].size() != 0) + { + ndest += vrange_input[i++].size(); + } + else + { + for (uint32_t k = i + 1; k < nrange_input; ++k) + { + vrange_input[k - 1] = vrange_input[k]; + }; + --nrange_input; + }; + }; + + if (nrange_input == 0) return range1_t(rdest.first, rdest.first); + if (nrange_input == 1) return move_forward(rdest, vrange_input[0]); + if (nrange_input == 2) + { + return merge(rdest, vrange_input[0], vrange_input[1], comp); + }; + + //------------------------------------------------------------------------ + // Initial sort + //------------------------------------------------------------------------ + uint32_t pos[4] = + { 0, 1, 2, 3 }, npos = nrange_input; + + //----------------------------------------------------------------------- + // thanks to Steven Ross by their suggestion about the optimal + // sorting networks + //----------------------------------------------------------------------- + if (less_range(vrange_input[pos[1]].first, pos[1], + vrange_input[pos[0]].first, pos[0], comp)) + { + std::swap(pos[0], pos[1]); + }; + if (npos == 4 and less_range(vrange_input[pos[3]].first, pos[3], + vrange_input[pos[2]].first, pos[2], comp)) + { + std::swap(pos[3], pos[2]); + }; + if (less_range (vrange_input[pos[2]].first, pos[2], + vrange_input[pos[0]].first, pos[0], comp)) + { + std::swap(pos[0], pos[2]); + }; + if (npos == 4 + and less_range (vrange_input[pos[3]].first, pos[3], + vrange_input[pos[1]].first, pos[1], comp)) + { + std::swap(pos[1], pos[3]); + }; + if (less_range (vrange_input[pos[2]].first, pos[2], + vrange_input[pos[1]].first, pos[1], comp)) + { + std::swap(pos[1], pos[2]); + }; + + Iter1_t it_dest = rdest.first; + while (npos > 2) + { + *(it_dest++) = std::move(*(vrange_input[pos[0]].first++)); + if (vrange_input[pos[0]].size() == 0) + { + pos[0] = pos[1]; + pos[1] = pos[2]; + pos[2] = pos[3]; + --npos; + } + else + { + if (less_range(vrange_input[pos[1]].first, pos[1], + vrange_input[pos[0]].first, pos[0], comp)) + { + std::swap(pos[0], pos[1]); + if (less_range(vrange_input[pos[2]].first, pos[2], + vrange_input[pos[1]].first, pos[1], comp)) + { + std::swap(pos[1], pos[2]); + if (npos == 4 + and less_range(vrange_input[pos[3]].first, + pos[3], + vrange_input[pos[2]].first, + pos[2], comp)) + { + std::swap(pos[2], pos[3]); + }; + }; + }; + }; + }; + + range1_t raux1(rdest.first, it_dest), raux2(it_dest, rdest.last); + if (pos[0] < pos[1]) + { + return concat(raux1,merge(raux2, vrange_input[pos[0]], + vrange_input[pos[1]], comp)); + } + else + { + return concat(raux1, merge (raux2, vrange_input[pos[1]], + vrange_input[pos[0]], comp)); + }; +}; + +//----------------------------------------------------------------------------- +// function : uninit_full_merge4 +/// @brief Merge four ranges and put the result in uninitialized memory +/// +/// @param dest: range where create and move the elements merged. Their +/// size must be greater or equal than the sum of the sizes +/// of the ranges in the array R +/// @param vrange_input : array of ranges to merge +/// @param nrange_input : number of ranges in vrange_input +/// @param comp : comparison object +/// @return range with all the elements move with the size adjusted +//----------------------------------------------------------------------------- +template<class Value_t, class Iter_t, class Compare> +range<Value_t *> uninit_full_merge4(const range<Value_t *> &dest, + range<Iter_t> vrange_input[4], + uint32_t nrange_input, Compare comp) +{ + typedef util::value_iter<Iter_t> type1; + static_assert (std::is_same< type1, Value_t >::value, + "Incompatible iterators\n"); + + size_t ndest = 0; + uint32_t i = 0; + while (i < nrange_input) + { + if (vrange_input[i].size() != 0) + { + ndest += vrange_input[i++].size(); + } + else + { + for (uint32_t k = i + 1; k < nrange_input; ++k) + { + vrange_input[k - 1] = vrange_input[k]; + }; + --nrange_input; + }; + }; + if (nrange_input == 0) return range<Value_t *>(dest.first, dest.first); + if (nrange_input == 1) return move_construct(dest, vrange_input[0]); + if (nrange_input == 2) + { + return merge_construct(dest, vrange_input[0], vrange_input[1], comp); + }; + + //------------------------------------------------------------------------ + // Initial sort + //------------------------------------------------------------------------ + uint32_t pos[4] = { 0, 1, 2, 3 }, npos = nrange_input; + + //----------------------------------------------------------------------- + // thanks to Steven Ross by their suggestion about the optimal + // sorting networks + //----------------------------------------------------------------------- + if (less_range(vrange_input[pos[1]].first, pos[1], + vrange_input[pos[0]].first, pos[0], comp)) + { + std::swap(pos[0], pos[1]); + }; + if (npos == 4 and less_range(vrange_input[pos[3]].first, pos[3], + vrange_input[pos[2]].first, pos[2], comp)) + { + std::swap(pos[3], pos[2]); + }; + if (less_range(vrange_input[pos[2]].first, pos[2], + vrange_input[pos[0]].first, pos[0], comp)) + { + std::swap(pos[0], pos[2]); + }; + if (npos == 4 and less_range(vrange_input[pos[3]].first, pos[3], + vrange_input[pos[1]].first, pos[1], comp)) + { + std::swap(pos[1], pos[3]); + }; + if (less_range(vrange_input[pos[2]].first, pos[2], + vrange_input[pos[1]].first, pos[1], comp)) + { + std::swap(pos[1], pos[2]); + }; + + Value_t *it_dest = dest.first; + while (npos > 2) + { + util::construct_object(&(*(it_dest++)), + std::move(*(vrange_input[pos[0]].first++))); + if (vrange_input[pos[0]].size() == 0) + { + pos[0] = pos[1]; + pos[1] = pos[2]; + pos[2] = pos[3]; + --npos; + } + else + { + if (less_range (vrange_input[pos[1]].first, pos[1], + vrange_input[pos[0]].first, pos[0], comp)) + { + std::swap(pos[0], pos[1]); + if (less_range (vrange_input[pos[2]].first, pos[2], + vrange_input[pos[1]].first, pos[1], comp)) + { + std::swap(pos[1], pos[2]); + if (npos == 4 and less_range(vrange_input[pos[3]].first, + pos[3], + vrange_input[pos[2]].first, + pos[2], comp)) + { + std::swap(pos[2], pos[3]); + }; + }; + }; + }; + }; // end while (npos > 2) + + range<Value_t *> raux1(dest.first, it_dest), raux2(it_dest, dest.last); + if (pos[0] < pos[1]) + { + return concat(raux1, + merge_construct(raux2, vrange_input[pos[0]], + vrange_input[pos[1]], comp)); + } + else + { + return concat(raux1, + merge_construct(raux2, vrange_input[pos[1]], + vrange_input[pos[0]], comp)); + }; +}; + +//**************************************************************************** +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/merge_vector.hpp b/boost/sort/common/merge_vector.hpp new file mode 100644 index 0000000000..84afea5a5e --- /dev/null +++ b/boost/sort/common/merge_vector.hpp @@ -0,0 +1,196 @@ +//---------------------------------------------------------------------------- +/// @file merge_vector.hpp +/// @brief In this file have the functions for to do a stable merge of +// ranges, in a vector +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_UTIL_MERGE_VECTOR_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_UTIL_MERGE_VECTOR_HPP + +#include <boost/sort/common/merge_four.hpp> +#include <functional> +#include <iterator> +#include <memory> +#include <type_traits> +#include <vector> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +//############################################################################ +// ## +// F U S I O N O F ## +// ## +// A V E C T O R O F R A N G E S ## +// ## +//############################################################################ + +// +//----------------------------------------------------------------------------- +// function : merge_level4 +/// @brief merge the ranges in the vector v_input with the full_merge4 function. +/// The v_output vector is used as auxiliary memory in the internal +/// process. The final results is in the dest range. +/// All the ranges of v_output are inside the range dest +/// @param dest : range where move the elements merged +/// @param v_input : vector of ranges to merge +/// @param v_output : vector of ranges obtained +/// @param comp : comparison object +/// @return range with all the elements moved +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +void merge_level4(range<Iter1_t> dest, std::vector<range<Iter2_t> > &v_input, + std::vector<range<Iter1_t> > &v_output, Compare comp) +{ + typedef range<Iter1_t> range1_t; + typedef util::value_iter<Iter1_t> type1; + typedef util::value_iter<Iter2_t> type2; + static_assert (std::is_same< type1, type2 >::value, + "Incompatible iterators\n"); + + v_output.clear(); + if (v_input.size() == 0) return; + if (v_input.size() == 1) + { + v_output.emplace_back(move_forward(dest, v_input[0])); + return; + }; + + uint32_t nrange = v_input.size(); + uint32_t pos_ini = 0; + while (pos_ini < v_input.size()) + { + uint32_t nmerge = (nrange + 3) >> 2; + uint32_t nelem = (nrange + nmerge - 1) / nmerge; + range1_t rz = full_merge4(dest, &v_input[pos_ini], nelem, comp); + v_output.emplace_back(rz); + dest.first = rz.last; + pos_ini += nelem; + nrange -= nelem; + }; + return; +}; +// +//----------------------------------------------------------------------------- +// function : uninit_merge_level4 +/// @brief merge the ranges moving the objects and constructing them in +/// uninitialized memory, in the vector v_input +/// using full_merge4. The v_output vector is used as auxiliary memory +/// in the internal process. The final results is in the dest range. +/// All the ranges of v_output are inside the range dest +/// +/// @param dest : range where move the elements merged +/// @param v_input : vector of ranges to merge +/// @param v_output : vector of ranges obtained +/// @param comp : comparison object +/// @return range with all the elements moved and constructed +//----------------------------------------------------------------------------- +template<class Value_t, class Iter_t, class Compare> +void uninit_merge_level4(range<Value_t *> dest, + std::vector<range<Iter_t> > &v_input, + std::vector<range<Value_t *> > &v_output, Compare comp) +{ + typedef range<Value_t *> range1_t; + typedef util::value_iter<Iter_t> type1; + static_assert (std::is_same< type1, Value_t >::value, + "Incompatible iterators\n"); + + v_output.clear(); + if (v_input.size() == 0) return; + if (v_input.size() == 1) + { + v_output.emplace_back(move_construct(dest, v_input[0])); + return; + }; + + uint32_t nrange = v_input.size(); + uint32_t pos_ini = 0; + while (pos_ini < v_input.size()) + { + uint32_t nmerge = (nrange + 3) >> 2; + uint32_t nelem = (nrange + nmerge - 1) / nmerge; + range1_t rz = uninit_full_merge4(dest, &v_input[pos_ini], nelem, comp); + v_output.emplace_back(rz); + dest.first = rz.last; + pos_ini += nelem; + nrange -= nelem; + }; + return; +}; +// +//----------------------------------------------------------------------------- +// function : merge_vector4 +/// @brief merge the ranges in the vector v_input using the merge_level4 +/// function. The v_output vector is used as auxiliary memory in the +/// internal process +/// The final results is in the range_output range. +/// All the ranges of v_output are inside the range range_output +/// All the ranges of v_input are inside the range range_input +/// @param range_input : range including all the ranges of v_input +/// @param ange_output : range including all the elements of v_output +/// @param v_input : vector of ranges to merge +/// @param v_output : vector of ranges obtained +/// @param comp : comparison object +/// @return range with all the elements moved +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +range<Iter2_t> merge_vector4(range<Iter1_t> range_input, + range<Iter2_t> range_output, + std::vector<range<Iter1_t> > &v_input, + std::vector<range<Iter2_t> > &v_output, + Compare comp) +{ + typedef range<Iter2_t> range2_t; + typedef util::value_iter<Iter1_t> type1; + typedef util::value_iter<Iter2_t> type2; + static_assert (std::is_same< type1, type2 >::value, + "Incompatible iterators\n"); + + v_output.clear(); + if (v_input.size() == 0) + { + return range2_t(range_output.first, range_output.first); + }; + if (v_input.size() == 1) + { + return move_forward(range_output, v_input[0]); + }; + bool sw = false; + uint32_t nrange = v_input.size(); + + while (nrange > 1) + { + if (sw) + { + merge_level4(range_input, v_output, v_input, comp); + sw = false; + nrange = v_input.size(); + } + else + { + merge_level4(range_output, v_input, v_output, comp); + sw = true; + nrange = v_output.size(); + }; + }; + return (sw) ? v_output[0] : move_forward(range_output, v_input[0]); +}; + +//**************************************************************************** +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/pivot.hpp b/boost/sort/common/pivot.hpp new file mode 100644 index 0000000000..5182fbd273 --- /dev/null +++ b/boost/sort/common/pivot.hpp @@ -0,0 +1,122 @@ +//---------------------------------------------------------------------------- +/// @file pivot.hpp +/// @brief This file contains the description of several low level algorithms +/// +/// @author Copyright (c) 2010 2015 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_PIVOT_HPP +#define __BOOST_SORT_COMMON_PIVOT_HPP + +#include <cstdint> + +namespace boost +{ +namespace sort +{ +namespace common +{ +// +//########################################################################## +// ## +// G L O B A L V A R I B L E S ## +// ## +//########################################################################## +// +//----------------------------------------------------------------------------- +// function : mid3 +/// @brief : return the iterator to the mid value of the three values passsed +/// as parameters +// +/// @param iter_1 : iterator to the first value +/// @param iter_2 : iterator to the second value +/// @param iter_3 : iterator to the third value +/// @param comp : object for to compare two values +/// @return iterator to mid value +//----------------------------------------------------------------------------- +template < typename Iter_t, typename Compare > +inline Iter_t mid3 (Iter_t iter_1, Iter_t iter_2, Iter_t iter_3, Compare comp) +{ + return comp (*iter_1, *iter_2) + ? (comp (*iter_2, *iter_3)? + iter_2 : (comp (*iter_1, *iter_3) ? iter_3 : iter_1)) + : (comp (*iter_3, *iter_2)? + iter_2 : (comp (*iter_3, *iter_1) ? iter_3 : iter_1)); +}; +// +//----------------------------------------------------------------------------- +// function : pivot3 +/// @brief : receive a range between first and last, calcule the mid iterator +/// with the first, the previous to the last, and the central +/// position. With this mid iterator swap with the first position +// +/// @param first : iterator to the first element +/// @param last : iterator to the last element +/// @param comp : object for to compare two elements +//----------------------------------------------------------------------------- +template < class Iter_t, class Compare > +inline void pivot3 (Iter_t first, Iter_t last, Compare comp) +{ + auto N2 = (last - first) >> 1; + Iter_t it_val = mid3 (first + 1, first + N2, last - 1, comp); + std::swap (*first, *it_val); +}; + +// +//----------------------------------------------------------------------------- +// function : mid9 +/// @brief : return the iterator to the mid value of the nine values passsed +/// as parameters +// +/// @param iter_1 : iterator to the first value +/// @param iter_2 : iterator to the second value +/// @param iter_3 : iterator to the third value +/// @param iter_4 : iterator to the fourth value +/// @param iter_5 : iterator to the fifth value +/// @param iter_6 : iterator to the sixth value +/// @param iter_7 : iterator to the seventh value +/// @param iter_8 : iterator to the eighth value +/// @param iter_9 : iterator to the ninth value +/// @return iterator to the mid value +//----------------------------------------------------------------------------- +template < class Iter_t, class Compare > +inline Iter_t mid9 (Iter_t iter_1, Iter_t iter_2, Iter_t iter_3, Iter_t iter_4, + Iter_t iter_5, Iter_t iter_6, Iter_t iter_7, Iter_t iter_8, + Iter_t iter_9, Compare comp) +{ + return mid3 (mid3 (iter_1, iter_2, iter_3, comp), + mid3 (iter_4, iter_5, iter_6, comp), + mid3 (iter_7, iter_8, iter_9, comp), comp); +}; +// +//----------------------------------------------------------------------------- +// function : pivot9 +/// @brief : receive a range between first and last, obtain 9 values between +/// the elements including the first and the previous to the last. +/// Obtain the iterator to the mid value and swap with the first +/// position +// +/// @param first : iterator to the first element +/// @param last : iterator to the last element +/// @param comp : object for to compare two elements +//----------------------------------------------------------------------------- +template < class Iter_t, class Compare > +inline void pivot9 (Iter_t first, Iter_t last, Compare comp) +{ + size_t cupo = (last - first) >> 3; + Iter_t itaux = mid9 (first + 1, first + cupo, first + 2 * cupo, + first + 3 * cupo, first + 4 * cupo, first + 5 * cupo, + first + 6 * cupo, first + 7 * cupo, last - 1, comp); + std::swap (*first, *itaux); +}; +//**************************************************************************** +}; // End namespace common +}; // End namespace sort +}; // End namespace boost +//**************************************************************************** +#endif diff --git a/boost/sort/common/range.hpp b/boost/sort/common/range.hpp new file mode 100644 index 0000000000..072d98a938 --- /dev/null +++ b/boost/sort/common/range.hpp @@ -0,0 +1,399 @@ +//---------------------------------------------------------------------------- +/// @file range.hpp +/// @brief Define a range [first, last), and the associated operations +/// +/// @author Copyright (c) 2016 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanyingfile LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_UTIL_RANGE_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_UTIL_RANGE_HPP + +#include <boost/sort/common/util/algorithm.hpp> +#include <boost/sort/common/util/merge.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <cassert> +#include <functional> +#include <memory> +#include <vector> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +///--------------------------------------------------------------------------- +/// @struct range +/// @brief this represent a range between two iterators +/// @remarks +//---------------------------------------------------------------------------- +template <class Iter_t> +struct range +{ + Iter_t first, last; + // + //------------------------------------------------------------------------ + // function : range + /// @brief empty constructor + //------------------------------------------------------------------------ + range(void) { }; + // + //------------------------------------------------------------------------ + // function : range + /// @brief constructor with two parameters + /// @param frs : iterator to the first element + /// @param lst : iterator to the last element + //----------------------------------------------------------------------- + range(const Iter_t &frs, const Iter_t &lst): first(frs), last(lst) { }; + // + //----------------------------------------------------------------------- + // function : empty + /// @brief indicate if the range is empty + /// @return true : empty false : not empty + //----------------------------------------------------------------------- + bool empty(void) const { return (first == last); }; + // + //----------------------------------------------------------------------- + // function : not_empty + /// @brief indicate if the range is not empty + /// @return true : not empty false : empty + //----------------------------------------------------------------------- + bool not_empty(void) const {return (first != last); }; + // + //----------------------------------------------------------------------- + // function : valid + /// @brief Indicate if the range is well constructed, and valid + /// @return true : valid, false : not valid + //----------------------------------------------------------------------- + bool valid(void) const { return ((last - first) >= 0); }; + // + //----------------------------------------------------------------------- + // function : size + /// @brief return the size of the range + /// @return size + //----------------------------------------------------------------------- + size_t size(void) const { return (last - first); }; + // + //------------------------------------------------------------------------ + // function : front + /// @brief return an iterator to the first element of the range + /// @return iterator + //----------------------------------------------------------------------- + Iter_t front(void) const { return first; }; + // + //------------------------------------------------------------------------- + // function : back + /// @brief return an iterator to the last element of the range + /// @return iterator + //------------------------------------------------------------------------- + Iter_t back(void) const {return (last - 1); }; +}; + +// +//----------------------------------------------------------------------------- +// function : concat +/// @brief concatenate two contiguous ranges +/// @param it1 : first range +/// @param it2 : second range +/// @return range resulting of the concatenation +//----------------------------------------------------------------------------- +template<class Iter_t> +inline range<Iter_t> concat(const range<Iter_t> &it1, const range<Iter_t> &it2) +{ + return range<Iter_t>(it1.first, it2.last); +} +; +// +//----------------------------------------------------------------------------- +// function : move_forward +/// @brief Move initialized objets from the range src to dest +/// @param dest : range where move the objects +/// @param src : range from where move the objects +/// @return range with the objects moved and the size adjusted +//----------------------------------------------------------------------------- +template <class Iter1_t, class Iter2_t> +inline range<Iter2_t> move_forward(const range<Iter2_t> &dest, + const range<Iter1_t> &src) +{ + assert(dest.size() >= src.size()); + Iter2_t it_aux = util::move_forward(dest.first, src.first, src.last); + return range<Iter2_t>(dest.first, it_aux); +}; +// +//----------------------------------------------------------------------------- +// function : move_backward +/// @brief Move initialized objets from the range src to dest +/// @param dest : range where move the objects +/// @param src : range from where move the objects +/// @return range with the objects moved and the size adjusted +//----------------------------------------------------------------------------- +template <class Iter1_t, class Iter2_t> +inline range<Iter2_t> move_backward(const range<Iter2_t> &dest, + const range<Iter1_t> &src) +{ + assert(dest.size() >= src.size()); + Iter2_t it_aux = util::move_backward(dest.first + src.size(), src.first, + src.last); + return range<Iter2_t>(dest.first, dest.src.size()); +}; + +//----------------------------------------------------------------------------- +// function : uninit_move +/// @brief Move uninitialized objets from the range src creating them in dest +/// +/// @param dest : range where move and create the objects +/// @param src : range from where move the objects +/// @return range with the objects moved and the size adjusted +//----------------------------------------------------------------------------- +template<class Iter_t, class Value_t = util::value_iter<Iter_t> > +inline range<Value_t*> move_construct(const range<Value_t*> &dest, + const range<Iter_t> &src) +{ + Value_t *ptr_aux = util::move_construct(dest.first, src.first, src.last); + return range<Value_t*>(dest.first, ptr_aux); +}; +// +//----------------------------------------------------------------------------- +// function : destroy +/// @brief destroy a range of objects +/// @param rng : range to destroy +//----------------------------------------------------------------------------- +template<class Iter_t> +inline void destroy(range<Iter_t> rng) +{ + util::destroy(rng.first, rng.last); +}; +// +//----------------------------------------------------------------------------- +// function : initialize +/// @brief initialize a range of objects with the object val moving across them +/// @param rng : range of elements not initialized +/// @param val : object used for the initialization +/// @return range initialized +//----------------------------------------------------------------------------- +template<class Iter_t, class Value_t = util::value_iter<Iter_t> > +inline range<Iter_t> initialize(const range<Iter_t> &rng, Value_t &val) +{ + util::initialize(rng.first, rng.last, val); + return rng; +}; +// +//----------------------------------------------------------------------------- +// function : is_mergeable +/// @brief : indicate if two ranges have a possible merge +/// @param src1 : first range +/// @param src2 : second range +/// @param comp : object for to compare elements +/// @return true : they can be merged +/// false : they can't be merged +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +inline bool is_mergeable(const range<Iter1_t> &src1, const range<Iter2_t> &src2, + Compare comp) +{ + //------------------------------------------------------------------------ + // Metaprogramming + //------------------------------------------------------------------------ + typedef util::value_iter<Iter1_t> type1; + typedef util::value_iter<Iter2_t> type2; + static_assert (std::is_same< type1, type2 >::value, + "Incompatible iterators\n"); + //------------------------------------------------------------------------ + // Code + //------------------------------------------------------------------------ + return comp(*(src2.front()), *(src1.back())); +}; +// +//----------------------------------------------------------------------------- +// function : is_mergeable_stable +/// @brief : indicate if two ranges have a possible merge +/// @param src1 : first range +/// @param src2 : second range +/// @param comp : object for to compare elements +/// @return true : they can be merged +/// false : they can't be merged +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +inline bool is_mergeable_stable(const range<Iter1_t> &src1, + const range<Iter2_t> &src2, Compare comp) +{ + //------------------------------------------------------------------------ + // Metaprogramming + //------------------------------------------------------------------------ + typedef util::value_iter<Iter1_t> type1; + typedef util::value_iter<Iter2_t> type2; + static_assert (std::is_same< type1, type2 >::value, + "Incompatible iterators\n"); + //------------------------------------------------------------------------ + // Code + //------------------------------------------------------------------------ + return not comp(*(src1.back()), *(src2.front())); +}; +// +//----------------------------------------------------------------------------- +// function : merge +/// @brief Merge two contiguous ranges src1 and src2, and put the result in +/// the range dest, returning the range merged +/// +/// @param dest : range where locate the lements merged. the size of dest +/// must be greater or equal than the sum of the sizes of +/// src1 and src2 +/// @param src1 : first range to merge +/// @param src2 : second range to merge +/// @param comp : comparison object +/// @return range with the elements merged and the size adjusted +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Iter3_t, class Compare> +inline range<Iter3_t> merge(const range<Iter3_t> &dest, + const range<Iter1_t> &src1, + const range<Iter2_t> &src2, Compare comp) +{ + Iter3_t it_aux = util::merge(src1.first, src1.last, src2.first, src2.last, + dest.first, comp); + return range<Iter3_t>(dest.first, it_aux); +}; + +//----------------------------------------------------------------------------- +// function : merge_construct +/// @brief Merge two contiguous uninitialized ranges src1 and src2, and create +/// and move the result in the uninitialized range dest, returning the +/// range merged +// +/// @param dest : range where locate the elements merged. the size of dest +/// must be greater or equal than the sum of the sizes of +/// src1 and src2. Initially is uninitialize memory +/// @param src1 : first range to merge +/// @param src2 : second range to merge +/// @param comp : comparison object +/// @return range with the elements merged and the size adjusted +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Value_t, class Compare> +inline range<Value_t *> merge_construct(const range<Value_t *> &dest, + const range<Iter1_t> &src1, + const range<Iter2_t> &src2, + Compare comp) +{ + Value_t * ptr_aux = util::merge_construct(src1.first, src1.last, src2.first, + src2.last, dest.first, comp); + return range<Value_t*>(dest.first, ptr_aux); +}; +// +//--------------------------------------------------------------------------- +// function : half_merge +/// @brief : Merge two initialized buffers. The first buffer is in a separate +/// memory +// +/// @param dest : range where finish the two buffers merged +/// @param src1 : first range to merge in a separate memory +/// @param src2 : second range to merge, in the final part of the +/// range where deposit the final results +/// @param comp : object for compare two elements of the type pointed +/// by the Iter1_t and Iter2_t +/// @return : range with the two buffers merged +//--------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +inline range<Iter2_t> merge_half(const range<Iter2_t> &dest, + const range<Iter1_t> &src1, + const range<Iter2_t> &src2, Compare comp) +{ + Iter2_t it_aux = util::merge_half(src1.first, src1.last, src2.first, + src2.last, dest.first, comp); + return range<Iter2_t>(dest.first, it_aux); +}; +// +//----------------------------------------------------------------------------- +// function : merge_uncontiguous +/// @brief : merge two non contiguous ranges src1, src2, using the range +/// aux as auxiliary memory. The results are in the original ranges +// +/// @param src1 : first range to merge +/// @param src2 : second range to merge +/// @param aux : auxiliary range used in the merge +/// @param comp : object for to compare elements +/// @return true : not changes done, false : changes in the buffers +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Iter3_t, class Compare> +inline bool merge_uncontiguous(const range<Iter1_t> &src1, + const range<Iter2_t> &src2, + const range<Iter3_t> &aux, Compare comp) +{ + return util::merge_uncontiguous(src1.first, src1.last, src2.first, + src2.last, aux.first, comp); +}; +// +//----------------------------------------------------------------------------- +// function : merge_contiguous +/// @brief : merge two contiguous ranges ( src1, src2) using buf as +/// auxiliary memory. The results are in the same ranges +/// @param src1 : first range to merge +/// @param src1 : second range to merge +/// @param buf : auxiliary memory used in the merge +/// @param comp : object for to compare elements +/// @return true : not changes done, false : changes in the buffers +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +inline range<Iter1_t> merge_contiguous(const range<Iter1_t> &src1, + const range<Iter1_t> &src2, + const range<Iter2_t> &buf, Compare comp) +{ + util::merge_contiguous(src1.first, src1.last, src2.last, buf.first, comp); + return concat(src1, src2); +}; +// +//----------------------------------------------------------------------------- +// function : merge_flow +/// @brief : merge two ranges, as part of a merge the ranges in a list. This +/// function reduce the number of movements compared with inplace_merge +/// when you need to merge a sequence of ranges. +/// This function merge the ranges rbuf and rng2, and the results +/// are in rng1 and rbuf +// +/// @param rng1 : range where locate the first elements of the merge +/// @param rbuf : range which provide the first elements, and where store +/// the last results of the merge +/// @param rng2 : range which provide the last elements to merge +/// @param comp : object for to compare elements +/// @return true : not changes done, false : changes in the buffers +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +static void merge_flow(range<Iter1_t> rng1, range<Iter2_t> rbuf, + range<Iter1_t> rng2, Compare cmp) +{ + //------------------------------------------------------------------------- + // Metaprogramming + //------------------------------------------------------------------------- + typedef util::value_iter<Iter1_t> type1; + typedef util::value_iter<Iter2_t> type2; + static_assert (std::is_same< type1, type2 >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------- + // Code + //------------------------------------------------------------------------- + range<Iter2_t> rbx(rbuf); + range<Iter1_t> rx1(rng1), rx2(rng2); + assert(rbx.size() == rx1.size() and rx1.size() == rx2.size()); + while (rx1.first != rx1.last) + { + *(rx1.first++) = (cmp(*rbx.first, *rx2.first)) ? + std::move(*(rbx.first++)): + std::move(*(rx2.first++)); + }; + if (rx2.first == rx2.last) return; + if (rbx.first == rbx.last) move_forward(rbuf, rng2); + else merge_half(rbuf, rx2, rbx, cmp); +}; + +//**************************************************************************** +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/rearrange.hpp b/boost/sort/common/rearrange.hpp new file mode 100644 index 0000000000..5c65c4f2b7 --- /dev/null +++ b/boost/sort/common/rearrange.hpp @@ -0,0 +1,168 @@ +//---------------------------------------------------------------------------- +/// @file rearrange.hpp +/// @brief Indirect algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_REARRANGE_HPP +#define __BOOST_SORT_COMMON_REARRANGE_HPP + +//#include <boost/sort/common/atomic.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <functional> +#include <iterator> +#include <type_traits> +#include <vector> +#include <cassert> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +template<class Iter_data> +struct filter_iterator +{ + //----------------------------------------------------------------------- + // Variables + //----------------------------------------------------------------------- + Iter_data origin; + + //----------------------------------------------------------------------- + // Functions + //----------------------------------------------------------------------- + filter_iterator(Iter_data global_first): origin(global_first) { }; + size_t operator ()(Iter_data itx) const + { + return size_t(itx - origin); + } +}; + +struct filter_pos +{ + size_t operator ()(size_t pos) const { return pos; }; +}; + +// +//----------------------------------------------------------------------------- +// function : rearrange +/// @brief This function transform a logical sort of the elements in the index +/// of iterators in a physical sort. +// +/// @param global_first : iterator to the first element of the data +/// @param [in] index : vector of the iterators +//----------------------------------------------------------------------------- +template<class Iter_data, class Iter_index, class Filter_pos> +void rearrange(Iter_data global_first, Iter_index itx_first, + Iter_index itx_last, Filter_pos pos) +{ + //----------------------------------------------------------------------- + // Metaprogramming + //----------------------------------------------------------------------- + typedef util::value_iter<Iter_data> value_data; + typedef util::value_iter<Iter_index> value_index; + + //------------------------------------------------------------------------- + // Code + //------------------------------------------------------------------------- + assert((itx_last - itx_first) >= 0); + size_t pos_dest, pos_src, pos_ini; + size_t nelem = size_t(itx_last - itx_first); + Iter_data data = global_first; + Iter_index index = itx_first; + + pos_ini = 0; + while (pos_ini < nelem) + { + while (pos_ini < nelem and pos(index[pos_ini]) == pos_ini) + ++pos_ini; + if (pos_ini == nelem) return; + pos_dest = pos_src = pos_ini; + value_data aux = std::move(data[pos_ini]); + value_index itx_src = std::move(index[pos_ini]); + + while ((pos_src = pos(itx_src)) != pos_ini) + { + data[pos_dest] = std::move(data[pos_src]); + std::swap(itx_src, index[pos_src]); + pos_dest = pos_src; + }; + + data[pos_dest] = std::move(aux); + index[pos_ini] = std::move(itx_src); + ++pos_ini; + }; +}; + +/* + // + //----------------------------------------------------------------------------- + // function : rearrange_pos + /// @brief This function transform a logical sort of the elements in the index + /// of iterators in a physical sort. + // + /// @param global_first : iterator to the first element of the data + /// @param [in] index : vector of the iterators + //----------------------------------------------------------------------------- + template < class Iter_t, class Number > + void rearrange_pos (Iter_t global_first, std::vector< Number> &index) + { + //------------------------------------------------------------------------- + // METAPROGRAMMING AND DEFINITIONS + //------------------------------------------------------------------------- + static_assert ( std::is_integral<Number>::value, "Incompatible Types"); + typedef iter_value< Iter_t > value_t; + + //------------------------------------------------------------------------- + // CODE + //------------------------------------------------------------------------- + size_t pos_dest = 0; + size_t pos_src = 0; + size_t pos_ini = 0; + size_t nelem = index.size ( ); + Iter_t it_dest (global_first), it_src(global_first); + + while (pos_ini < nelem) + { + while (pos_ini < nelem and + index[pos_ini] == pos_ini) + { + ++pos_ini; + }; + + if (pos_ini == nelem) return; + pos_dest = pos_src = pos_ini; + it_dest = global_first + pos_dest; + value_t Aux = std::move (*it_dest); + + while ((pos_src = index[pos_dest]) != pos_ini) + { + index[pos_dest] = it_dest - global_first; + it_src = global_first + pos_src; + *it_dest = std::move (*it_src); + it_dest = it_src; + pos_dest = pos_src; + }; + + *it_dest = std::move (Aux); + index[pos_dest] = it_dest - global_first; + ++pos_ini; + }; + }; + */ +// +//**************************************************************************** +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/scheduler.hpp b/boost/sort/common/scheduler.hpp new file mode 100644 index 0000000000..33074a4534 --- /dev/null +++ b/boost/sort/common/scheduler.hpp @@ -0,0 +1,276 @@ +//---------------------------------------------------------------------------- +/// @file scheduler.hpp +/// @brief This file contains the implementation of the scheduler for +/// dispatch the works stored +/// +/// @author Copyright (c) 2010 2015 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanyingfile LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_SCHEDULER_HPP +#define __BOOST_SORT_COMMON_SCHEDULER_HPP + +#include <boost/sort/common/spinlock.hpp> +#include <boost/sort/common/search.hpp> +#include <boost/sort/common/compare_traits.hpp> +#include <scoped_allocator> +#include <utility> +#include <vector> +#include <deque> +#include <iostream> +#include <unordered_map> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +// +//########################################################################### +// ## +// ################################################################ ## +// # # ## +// # C L A S S S C H E D U L E R # ## +// # # ## +// ################################################################ ## +// ## +//########################################################################### + +// +//--------------------------------------------------------------------------- +/// @class scheduler +/// @brief This class is a concurrent stack controled by a spin_lock +/// @remarks +//--------------------------------------------------------------------------- +template<typename Func_t, typename Allocator = std::allocator<Func_t> > +struct scheduler +{ + //----------------------------------------------------------------------- + // D E F I N I T I O N S + //----------------------------------------------------------------------- + typedef std::scoped_allocator_adaptor <Allocator> scoped_alloc; + typedef std::deque <Func_t, scoped_alloc> deque_t; + typedef typename deque_t::iterator it_deque; + typedef std::thread::id key_t; + typedef std::hash <key_t> hash_t; + typedef std::equal_to <key_t> equal_t; + typedef std::unique_lock <spinlock_t> lock_t; + typedef std::unordered_map <key_t, deque_t, hash_t, + equal_t, scoped_alloc> map_t; + typedef typename map_t::iterator it_map; + + //----------------------------------------------------------------------- + // V A R I A B L E S + //----------------------------------------------------------------------- + map_t mp; + size_t nelem; + mutable spinlock_t spl; + + //------------------------------------------------------------------------ + // function : scheduler + /// @brief constructor + //------------------------------------------------------------------------ + scheduler(void) : mp(), nelem(0) { }; + // + //----------------------------------------------------------------------- + // function : scheduler + /// @brief Copy & move constructor + /// @param [in] VT : stack_cnc from where copy the data + //----------------------------------------------------------------------- + scheduler(scheduler && VT) = delete; + scheduler(const scheduler & VT) = delete; + // + //------------------------------------------------------------------------ + // function : ~scheduler + /// @brief Destructor + //------------------------------------------------------------------------ + virtual ~scheduler(void) {mp.clear();}; + // + //------------------------------------------------------------------------ + // function : operator = + /// @brief Asignation operator + /// @param [in] VT : stack_cnc from where copy the data + /// @return Reference to the stack_cnc after the copy + //------------------------------------------------------------------------ + scheduler & operator=(const scheduler &VT) = delete; + // + //------------------------------------------------------------------------ + // function : size + /// @brief Asignation operator + /// @param [in] VT : stack_cnc from where copy the data + /// @return Reference to the stack_cnc after the copy + //------------------------------------------------------------------------ + size_t size(void) const + { + lock_t s(spl); + return nelem; + }; + // + //------------------------------------------------------------------------ + // function : clear + /// @brief Delete all the elements of the stack_cnc. + //------------------------------------------------------------------------ + void clear_all(void) + { + lock_t s(spl); + mp.clear(); + nelem = 0; + }; + + // + //------------------------------------------------------------------------ + // function : insert + /// @brief Insert one element in the back of the container + /// @param [in] D : value to insert. Can ve a value, a reference or an + /// rvalue + /// @return iterator to the element inserted + /// @remarks This operation is O ( const ) + //------------------------------------------------------------------------ + void insert(Func_t & f) + { + lock_t s(spl); + key_t th_id = std::this_thread::get_id(); + it_map itmp = mp.find(th_id); + if (itmp == mp.end()) + { + auto aux = mp.emplace(th_id, deque_t()); + if (aux.second == false) throw std::bad_alloc(); + itmp = aux.first; + }; + itmp->second.emplace_back(std::move(f)); + nelem++; + }; + + // + //------------------------------------------------------------------------ + // function :emplace + /// @brief Insert one element in the back of the container + /// @param [in] args :group of arguments for to build the object to insert + /// @return iterator to the element inserted + /// @remarks This operation is O ( const ) + //------------------------------------------------------------------------ + template<class ... Args> + void emplace(Args && ... args) + { + lock_t s(spl); + key_t th_id = std::this_thread::get_id(); + it_map itmp = mp.find(th_id); + if (itmp == mp.end()) + { + auto aux = mp.emplace(th_id, deque_t()); + if (aux.second == false) throw std::bad_alloc(); + itmp = aux.first; + }; + itmp->second.emplace_back(std::forward <Args>(args) ...); + nelem++; + }; + // + //------------------------------------------------------------------------ + // function : insert + /// @brief Insert one element in the back of the container + /// @param [in] D : value to insert. Can ve a value, a reference or an rvalue + /// @return iterator to the element inserted + /// @remarks This operation is O ( const ) + //------------------------------------------------------------------------ + template<class it_func> + void insert_range(it_func first, it_func last) + { + //-------------------------------------------------------------------- + // Metaprogramming + //-------------------------------------------------------------------- + typedef value_iter<it_func> value2_t; + static_assert (std::is_same< Func_t, value2_t >::value, + "Incompatible iterators\n"); + + //-------------------------------------------------------------------- + // Code + //-------------------------------------------------------------------- + assert((last - first) > 0); + + lock_t s(spl); + key_t th_id = std::this_thread::get_id(); + it_map itmp = mp.find(th_id); + if (itmp == mp.end()) + { + auto aux = mp.emplace(th_id, deque_t()); + if (aux.second == true) throw std::bad_alloc(); + itmp = aux.first; + }; + while (first != last) + { + itmp->second.emplace_back(std::move(*(first++))); + nelem++; + }; + }; + // + //------------------------------------------------------------------------ + // function : extract + /// @brief erase the last element of the tree and return a copy + /// @param [out] V : reference to a variable where copy the element + /// @return code of the operation + /// 0- Element erased + /// 1 - Empty tree + /// @remarks This operation is O(1) + //------------------------------------------------------------------------ + bool extract(Func_t & f) + { + lock_t s(spl); + if (nelem == 0) return false; + key_t th_id = std::this_thread::get_id(); + it_map itmp = mp.find(th_id); + if (itmp != mp.end() and not itmp->second.empty()) + { + f = std::move(itmp->second.back()); + itmp->second.pop_back(); + --nelem; + return true; + }; + for (itmp = mp.begin(); itmp != mp.end(); ++itmp) + { + if (itmp->second.empty()) continue; + f = std::move(itmp->second.back()); + itmp->second.pop_back(); + --nelem; + return true; + } + return false; + }; +}; +// end class scheduler +//************************************************************************* +// P R I N T F U N C T I O N S +//************************************************************************ +template<class ... Args> +std::ostream & operator <<(std::ostream &out, const std::deque<Args ...> & dq) +{ + for (uint32_t i = 0; i < dq.size(); ++i) + out << dq[i] << " "; + out << std::endl; + return out; +} + +template<typename Func_t, typename Allocator = std::allocator<Func_t> > +std::ostream & operator <<(std::ostream &out, + const scheduler<Func_t, Allocator> &sch) +{ + std::unique_lock < spinlock_t > s(sch.spl); + out << "Nelem :" << sch.nelem << std::endl; + for (auto it = sch.mp.begin(); it != sch.mp.end(); ++it) + { + out << it->first << " :" << it->second << std::endl; + } + return out; +} + +//*************************************************************************** +};// end namespace common +};// end namespace sort +};// end namespace boost +//*************************************************************************** +#endif diff --git a/boost/sort/common/sort_basic.hpp b/boost/sort/common/sort_basic.hpp new file mode 100644 index 0000000000..68a6f54048 --- /dev/null +++ b/boost/sort/common/sort_basic.hpp @@ -0,0 +1,334 @@ +//---------------------------------------------------------------------------- +/// @file sort_basic.hpp +/// @brief Spin Sort algorithm +/// +/// @author Copyright (c) 2016 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_SORT_BASIC_HPP +#define __BOOST_SORT_COMMON_SORT_BASIC_HPP + +//#include <boost/sort/spinsort/util/indirect.hpp> +#include <boost/sort/insert_sort/insert_sort.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <boost/sort/common/range.hpp> +#include <cstdlib> +#include <functional> +#include <iterator> +#include <memory> +#include <type_traits> +#include <vector> +#include <cstddef> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +//---------------------------------------------------------------------------- +// USING SENTENCES +//---------------------------------------------------------------------------- +using boost::sort::insert_sort; + +//----------------------------------------------------------------------------- +// function : is_stable_sorted_forward +/// @brief examine the elements in the range first, last if they are stable +/// sorted, and return an iterator to the first element not sorted +/// @param first : iterator to the first element in the range +/// @param last : ierator after the last element of the range +/// @param comp : object for to compare two elements +/// @return iterator to the first element not stable sorted. The number of +/// elements sorted is the iterator returned minus first +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > > +inline Iter_t is_stable_sorted_forward (Iter_t first, Iter_t last, + Compare comp = Compare()) +{ +#ifdef __BS_DEBUG + assert ( (last- first) >= 0); +#endif + if ((last - first) < 2) return first; + Iter_t it2 = first + 1; + for (Iter_t it1 = first; it2 != last and not comp(*it2, *it1); it1 = it2++); + return it2; +} +//----------------------------------------------------------------------------- +// function : is_reverse_stable_sorted_forward +/// @brief examine the elements in the range first, last if they are reverse +/// stable sorted, and return an iterator to the first element not +/// reverse stable sorted +/// @param first : iterator to the first element in the range +/// @param last : ierator after the last element of the range +/// @param comp : object for to compare two elements +/// @return iterator to the first element not reverse stable sorted. The number +/// of elements sorted is the iterator returned minus first +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > > +inline Iter_t is_reverse_stable_sorted_forward(Iter_t first, Iter_t last, + Compare comp = Compare()) +{ +#ifdef __BS_DEBUG + assert ( (last- first) >= 0); +#endif + if ((last - first) < 2) return first; + Iter_t it2 = first + 1; + for (Iter_t it1 = first; it2 != last and comp(*it2, *it1); it1 = it2++); + return it2; +}; +//----------------------------------------------------------------------------- +// function : number_stable_sorted_forward +/// @brief examine the elements in the range first, last if they are stable +/// sorted, and return the number of elements sorted +/// @param first : iterator to the first element in the range +/// @param last : ierator after the last element of the range +/// @param comp : object for to compare two elements +/// @param min_process : minimal number of elements to be consideer +/// @return number of element sorted. I f the number is lower than min_process +/// return 0 +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > > +size_t number_stable_sorted_forward (Iter_t first, Iter_t last, + size_t min_process, + Compare comp = Compare()) +{ +#ifdef __BS_DEBUG + assert ( (last- first) >= 0); +#endif + if ((last - first) < 2) return 0; + + // sorted elements + Iter_t it2 = first + 1; + for (Iter_t it1 = first; it2 != last and not comp(*it2, *it1); it1 = it2++); + size_t nsorted = size_t ( it2 - first); + if ( nsorted != 1) + return (nsorted >= min_process) ? nsorted: 0; + + // reverse sorted elements + it2 = first + 1; + for (Iter_t it1 = first; it2 != last and comp(*it2, *it1); it1 = it2++); + nsorted = size_t ( it2 - first); + + if ( nsorted < min_process) return 0 ; + util::reverse ( first , it2); + return nsorted; +}; + +//----------------------------------------------------------------------------- +// function : is_stable_sorted_backward +/// @brief examine the elements in the range first, last beginning at end, and +/// if they are stablesorted, and return an iterator to the last element +/// sorted +/// @param first : iterator to the first element in the range +/// @param last : ierator after the last element of the range +/// @param comp : object for to compare two elements +/// @return iterator to the last element stable sorted. The number of +/// elements sorted is the last minus the iterator returned +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > > +inline Iter_t is_stable_sorted_backward(Iter_t first, Iter_t last, + Compare comp = Compare()) +{ +#ifdef __BS_DEBUG + assert ( (last- first) >= 0); +#endif + if ((last - first) < 2) return first; + Iter_t itaux = last - 1; + while (itaux != first and not comp(*itaux, *(itaux - 1))) {--itaux; }; + return itaux; +} +//----------------------------------------------------------------------------- +// function : is_reverse_stable_sorted_backward +/// @brief examine the elements in the range first, last beginning at end, and +/// if they are stablesorted, and return an iterator to the last element +/// sorted +/// @param first : iterator to the first element in the range +/// @param last : ierator after the last element of the range +/// @param comp : object for to compare two elements +/// @return iterator to the last element stable sorted. The number of +/// elements sorted is the last minus the iterator returned +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > > +inline Iter_t is_reverse_stable_sorted_backward (Iter_t first, Iter_t last, + Compare comp = Compare()) +{ +#ifdef __BS_DEBUG + assert ( (last- first) >= 0); +#endif + if ((last - first) < 2) return first; + Iter_t itaux = last - 1; + for (; itaux != first and comp(*itaux, *(itaux - 1)); --itaux); + return itaux; +} + +//----------------------------------------------------------------------------- +// function : number_stable_sorted_backward +/// @brief examine the elements in the range first, last if they are stable +/// sorted, and return the number of elements sorted +/// @param first : iterator to the first element in the range +/// @param last : ierator after the last element of the range +/// @param comp : object for to compare two elements +/// @param min_process : minimal number of elements to be consideer +/// @return number of element sorted. I f the number is lower than min_process +/// return 0 +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > > +size_t number_stable_sorted_backward (Iter_t first, Iter_t last, + size_t min_process, + Compare comp = Compare()) +{ +#ifdef __BS_DEBUG + assert ( (last- first) >= 0); +#endif + if ((last - first) < 2) return 0; + Iter_t itaux = last - 1; + while (itaux != first and not comp(*itaux, *(itaux - 1))) {--itaux; }; + size_t nsorted = size_t ( last - itaux); + if ( nsorted != 1) + return ( nsorted >= min_process)?nsorted: 0 ; + + itaux = last - 1; + for (; itaux != first and comp(*itaux, *(itaux - 1)); --itaux); + nsorted = size_t ( last - itaux); + if ( nsorted < min_process) return 0 ; + util::reverse ( itaux, last ); + return nsorted; +} +//----------------------------------------------------------------------------- +// function : internal_sort +/// @brief this function divide r_input in two parts, sort it,and merge moving +/// the elements to range_buf +/// @param range_input : range with the elements to sort +/// @param range_buffer : range with the elements sorted +/// @param comp : object for to compare two elements +/// @param level : when is 1, sort with the insertionsort algorithm +/// if not make a recursive call splitting the ranges +// +//----------------------------------------------------------------------------- +template <class Iter1_t, class Iter2_t, class Compare> +inline void internal_sort (const range<Iter1_t> &rng1, + const range<Iter2_t> &rng2, + Compare comp, uint32_t level, bool even = true) +{ + //----------------------------------------------------------------------- + // metaprogram + //----------------------------------------------------------------------- + typedef value_iter<Iter1_t> value_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same< value_t, value2_t>::value, + "Incompatible iterators\n"); + + //----------------------------------------------------------------------- + // program + //----------------------------------------------------------------------- +#ifdef __BS_DEBUG + assert (rng1.size ( ) == rng2.size ( ) ); +#endif + size_t nelem = (rng1.size() + 1) >> 1; + + range<Iter1_t> rng1_left(rng1.first, rng1.first + nelem), + rng1_right(rng1.first + nelem, rng1.last); + + range<Iter2_t> rng2_left(rng2.first, rng2.first + nelem), + rng2_right(rng2.first + nelem, rng2.last); + + if (nelem <= 32 and (level & 1) == even) + { + insert_sort(rng1_left.first, rng1_left.last, comp); + insert_sort(rng1_right.first, rng1_right.last, comp); + } + else + { + internal_sort(rng2_left, rng1_left, comp, level + 1, even); + internal_sort(rng2_right, rng1_right, comp, level + 1, even); + }; + merge(rng2, rng1_left, rng1_right, comp); +}; +//----------------------------------------------------------------------------- +// function : range_sort_data +/// @brief this sort elements using the range_sort function and receiving a +/// buffer of initialized memory +/// @param rng_data : range with the elements to sort +/// @param rng_aux : range of at least the same memory than rng_data used as +/// auxiliary memory in the sorting +/// @param comp : object for to compare two elements +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +static void range_sort_data (const range<Iter1_t> & rng_data, + const range<Iter2_t> & rng_aux, Compare comp) +{ + //----------------------------------------------------------------------- + // metaprogram + //----------------------------------------------------------------------- + typedef value_iter<Iter1_t> value_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same< value_t, value2_t>::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------ + // program + //------------------------------------------------------------------------ +#ifdef __BS_DEBUG + assert ( rng_data.size() == rng_aux.size()); +#endif + // minimal number of element before to jump to insertionsort + const uint32_t sort_min = 32; + if (rng_data.size() <= sort_min) + { + insert_sort(rng_data.first, rng_data.last, comp); + return; + }; + + internal_sort(rng_aux, rng_data, comp, 0, true); +}; +//----------------------------------------------------------------------------- +// function : range_sort_buffer +/// @brief this sort elements using the range_sort function and receiving a +/// buffer of initialized memory +/// @param rng_data : range with the elements to sort +/// @param rng_aux : range of at least the same memory than rng_data used as +/// auxiliary memory in the sorting +/// @param comp : object for to compare two elements +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +static void range_sort_buffer(const range<Iter1_t> & rng_data, + const range<Iter2_t> & rng_aux, Compare comp) +{ + //----------------------------------------------------------------------- + // metaprogram + //----------------------------------------------------------------------- + typedef value_iter<Iter1_t> value_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same< value_t, value2_t>::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------ + // program + //------------------------------------------------------------------------ +#ifdef __BS_DEBUG + assert ( rng_data.size() == rng_aux.size()); +#endif + // minimal number of element before to jump to insertionsort + const uint32_t sort_min = 32; + if (rng_data.size() <= sort_min) + { + insert_sort(rng_data.first, rng_data.last, comp); + move_forward(rng_aux, rng_data); + return; + }; + + internal_sort(rng_data, rng_aux, comp, 0, false); +}; +//**************************************************************************** +};// End namespace common +};// End namespace sort +};// End namepspace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/spinlock.hpp b/boost/sort/common/spinlock.hpp new file mode 100644 index 0000000000..450ba6b53e --- /dev/null +++ b/boost/sort/common/spinlock.hpp @@ -0,0 +1,88 @@ +//---------------------------------------------------------------------------- +/// @file spinlock_t.hpp +/// @brief +/// +/// @author Copyright (c) 2010 2015 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanyingfile LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_UTIL_SPINLOCK_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_UTIL_SPINLOCK_HPP + +#include <atomic> +#include <ctime> +#include <functional> +#include <memory> +#include <mutex> +#include <thread> + +namespace boost +{ +namespace sort +{ +namespace common +{ +// +//--------------------------------------------------------------------------- +/// @class spinlock_t +/// @brief This class implement, from atomic variables, a spinlock +/// @remarks This class meet the BasicLockable requirements ( lock, unlock ) +//--------------------------------------------------------------------------- +class spinlock_t +{ + private: + //------------------------------------------------------------------------ + // P R I V A T E V A R I A B L E S + //------------------------------------------------------------------------ + std::atomic_flag af; + + public: + // + //------------------------------------------------------------------------- + // function : spinlock_t + /// @brief class constructor + /// @param [in] + //------------------------------------------------------------------------- + explicit spinlock_t ( ) noexcept { af.clear ( ); }; + // + //------------------------------------------------------------------------- + // function : lock + /// @brief Lock the spinlock_t + //------------------------------------------------------------------------- + void lock ( ) noexcept + { + while (af.test_and_set (std::memory_order_acquire)) + { + std::this_thread::yield ( ); + }; + }; + // + //------------------------------------------------------------------------- + // function : try_lock + /// @brief Try to lock the spinlock_t, if not, return false + /// @return true : locked + /// false: not previous locked + //------------------------------------------------------------------------- + bool try_lock ( ) noexcept + { + return not af.test_and_set (std::memory_order_acquire); + }; + // + //------------------------------------------------------------------------- + // function : unlock + /// @brief unlock the spinlock_t + //------------------------------------------------------------------------- + void unlock ( ) noexcept { af.clear (std::memory_order_release); }; + +}; // E N D C L A S S S P I N L O C K +// +//*************************************************************************** +}; // end namespace common +}; // end namespace sort +}; // end namespace boost +//*************************************************************************** +#endif diff --git a/boost/sort/common/stack_cnc.hpp b/boost/sort/common/stack_cnc.hpp new file mode 100644 index 0000000000..d4d6e53b25 --- /dev/null +++ b/boost/sort/common/stack_cnc.hpp @@ -0,0 +1,142 @@ +//---------------------------------------------------------------------------- +/// @file stack_cnc.hpp +/// @brief This file contains the implementation concurrent stack +/// +/// @author Copyright (c) 2010 2015 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanyingfile LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_UTIL_STACK_CNC_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_UTIL_STACK_CNC_HPP + +#include <boost/sort/common/spinlock.hpp> +#include <vector> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +// +//########################################################################### +// ## +// ################################################################ ## +// # # ## +// # C L A S S # ## +// # S T A C K _ C N C # ## +// # # ## +// ################################################################ ## +// ## +//########################################################################### +// +//--------------------------------------------------------------------------- +/// @class stack_cnc +/// @brief This class is a concurrent stack controled by a spin_lock +/// @remarks +//--------------------------------------------------------------------------- +template<typename T, typename Allocator = std::allocator<T> > +class stack_cnc +{ +public: + //------------------------------------------------------------------------ + // D E F I N I T I O N S + //------------------------------------------------------------------------ + typedef std::vector<T, Allocator> vector_t; + typedef typename vector_t::size_type size_type; + typedef typename vector_t::difference_type difference_type; + typedef typename vector_t::value_type value_type; + typedef typename vector_t::pointer pointer; + typedef typename vector_t::const_pointer const_pointer; + typedef typename vector_t::reference reference; + typedef typename vector_t::const_reference const_reference; + typedef typename vector_t::allocator_type allocator_type; + typedef Allocator alloc_t; + +protected: + //------------------------------------------------------------------------- + // INTERNAL VARIABLES + //------------------------------------------------------------------------- + vector_t v_t; + mutable spinlock_t spl; + +public: + // + //------------------------------------------------------------------------- + // function : stack_cnc + /// @brief constructor + //------------------------------------------------------------------------- + explicit stack_cnc(void): v_t() { }; + + // + //------------------------------------------------------------------------- + // function : stack_cnc + /// @brief Move constructor + //------------------------------------------------------------------------- + stack_cnc(stack_cnc &&) = delete; + // + //------------------------------------------------------------------------- + // function : ~stack_cnc + /// @brief Destructor + //------------------------------------------------------------------------- + virtual ~stack_cnc(void) { v_t.clear(); }; + + //------------------------------------------------------------------------- + // function : emplace_back + /// @brief Insert one element in the back of the container + /// @param args : group of arguments for to build the object to insert. Can + /// be values, references or rvalues + //------------------------------------------------------------------------- + template<class ... Args> + void emplace_back(Args &&... args) + { + std::lock_guard < spinlock_t > guard(spl); + v_t.emplace_back(std::forward< Args > (args)...); + }; + + // + //------------------------------------------------------------------------- + // function :pop_move_back + /// @brief if exist, move the last element to P, and delete it + /// @param P : reference to a variable where move the element + /// @return true - Element moved and deleted + /// false - Empty stack_cnc + //------------------------------------------------------------------------- + bool pop_move_back(value_type &P) + { + std::lock_guard < spinlock_t > S(spl); + if (v_t.size() == 0) return false; + P = std::move(v_t.back()); + v_t.pop_back(); + return true; + }; + //------------------------------------------------------------------------- + // function : push_back + /// @brief Insert one vector at the end of the container + /// @param v_other : vector to insert + /// @return reference to the stack_cnc after the insertion + //------------------------------------------------------------------------- + template<class Allocator2> + stack_cnc &push_back(const std::vector<value_type, Allocator2> &v_other) + { + std::lock_guard < spinlock_t > guard(spl); + for (size_type i = 0; i < v_other.size(); ++i) + { + v_t.push_back(v_other[i]); + } + return *this; + }; +}; +// end class stack_cnc + +//*************************************************************************** +};// end namespace common +};// end namespace sort +};// end namespace boost +//*************************************************************************** +#endif diff --git a/boost/sort/common/time_measure.hpp b/boost/sort/common/time_measure.hpp new file mode 100644 index 0000000000..ef00dd4930 --- /dev/null +++ b/boost/sort/common/time_measure.hpp @@ -0,0 +1,62 @@ +//---------------------------------------------------------------------------- +/// @file time_measure.hpp +/// @brief This class is done in order to simplify the time measure in the +/// benchmaark programs +/// +/// @author Copyright (c) 2010 2015 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanyingfile LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_TOOLS_TIME_MEASURE_HPP +#define __BOOST_SORT_PARALLEL_TOOLS_TIME_MEASURE_HPP + +#include <chrono> + +namespace boost +{ +namespace sort +{ +namespace common +{ + +namespace chrn = std::chrono; +// +//*************************************************************************** +// D E F I N I T I O N S +//*************************************************************************** +typedef chrn::steady_clock::time_point time_point; + +time_point now ( ); +double subtract_time ( const time_point & t1, const time_point & t2 ); +// +//--------------------------------------------------------------------------- +// function : now +/// @brief return the time system in a internal format ( steady_clock) +/// @return time in steady_clock format +//--------------------------------------------------------------------------- +time_point now ( ) { return chrn::steady_clock::now( ); }; +// +//--------------------------------------------------------------------------- +// function : subtract_time +/// @brief return the time in double format +/// @param [in] t1 : first time in time_point format +/// @param [in] t2 : second time in time_point format +/// @return time in seconds of the difference of t1 - t2 +//--------------------------------------------------------------------------- +double subtract_time ( const time_point & t1, const time_point & t2 ) +{ //------------------------ begin --------------------------------- + chrn::duration<double> time_span = + chrn::duration_cast < chrn::duration < double > > ( t1 - t2 ); + return time_span.count( ); +}; + +//*************************************************************************** +};// End namespace benchmark +};// End namespace sort +};// End namespace boost +//*************************************************************************** +#endif diff --git a/boost/sort/common/util/algorithm.hpp b/boost/sort/common/util/algorithm.hpp new file mode 100644 index 0000000000..db7607aaeb --- /dev/null +++ b/boost/sort/common/util/algorithm.hpp @@ -0,0 +1,309 @@ +//---------------------------------------------------------------------------- +/// @file algorithm.hpp +/// @brief low level functions of create, destroy, move and merge functions +/// +/// @author Copyright (c) 2017 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_UTIL_ALGORITHM_HPP +#define __BOOST_SORT_COMMON_UTIL_ALGORITHM_HPP + +#include <algorithm> +#include <functional> +#include <iterator> +#include <memory> +#include <type_traits> +#include <vector> +#include <boost/sort/common/util/traits.hpp> + +namespace boost +{ +namespace sort +{ +namespace common +{ +namespace util +{ +// +//########################################################################### +// +// I M P O R T A N T +// +// The functions of this file are for internal use only +// All the operations are done with move operations, because the copy +// operations are unnecesary +// +//########################################################################### +// +//---------------------------------------------------------------------------- +// +// F U N C T I O N S I N T H E F I L E +// +//---------------------------------------------------------------------------- +// +// static inline uint32_t nbits32 (uint32_t num) noexcept +// +// static inline uint32_t nbits64 (uint64_t num) +// +// template < class Value_t, class... Args > +// inline void construct_object (Value_t *ptr, Args &&... args) +// +// template < class Value_t > +// inline void destroy_object (Value_t *ptr) +// +// template < class Iter_t, class Value_t = value_iter<Iter_t> > +// void initialize (Iter_t first, Iter_t last, Value_t && val) +// +// template < class Iter1_t, class Iter2_t > +// Iter2_t move_forward (Iter2_t it_dest, Iter1_t first, Iter1_t last) +// +// template < class Iter1_t, class Iter2_t > +// Iter2_t move_backward (Iter2_t it_dest, Iter1_t first, Iter1_t last) +// +// template < class Iter_t, class Value_t = value_iter< Iter_t > > +// Value_t * move_construct (Value_t *ptr, Iter_t first, Iter_t last) +// +// template < class Iter_t > +// void destroy (Iter_t first, const Iter_t last) +// +// template < class Iter_t > +// void reverse (Iter_t first, const Iter_t last) +// +//---------------------------------------------------------------------------- +// +//-------------------------------------------------------------------------- +// +// G L O B A L V A R I B L E S +// +//-------------------------------------------------------------------------- +// +// this array represent the number of bits needed for to represent the +// first 256 numbers +static constexpr const uint32_t tmsb[256] = +{ 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }; +// +//--------------------------------------------------------------------------- +// +// F U N C T I O N S +// +//--------------------------------------------------------------------------- +// +//--------------------------------------------------------------------------- +// function : nbits32 +/// @brief Obtain the number of bits of a number equal or greater than num +/// @param num : Number to examine +/// @return Number of bits +//--------------------------------------------------------------------------- +static inline uint32_t nbits32 (uint32_t num) noexcept +{ + int Pos = (num & 0xffff0000U) ? 16 : 0; + if ((num >> Pos) & 0xff00U) Pos += 8; + return (tmsb[num >> Pos] + Pos); +} +// +//--------------------------------------------------------------------------- +// function : nbits64 +/// @brief Obtain the number of bits of a number equal or greater than num +/// @param num : Number to examine +/// @exception none +/// @return Number of bits +//--------------------------------------------------------------------------- +static inline uint32_t nbits64(uint64_t num)noexcept +{ + uint32_t Pos = (num & 0xffffffff00000000ULL) ? 32 : 0; + if ((num >> Pos) & 0xffff0000ULL) Pos += 16; + if ((num >> Pos) & 0xff00ULL) Pos += 8; + return (tmsb[num >> Pos] + Pos); +} +// +//----------------------------------------------------------------------------- +// function : construct_object +/// @brief create an object in the memory specified by ptr +/// +/// @param ptr : pointer to the memory where to create the object +/// @param args : arguments to the constructor +//----------------------------------------------------------------------------- +template <class Value_t, class ... Args> +inline void construct_object (Value_t *ptr, Args &&... args) +{ + (::new (static_cast<void *>(ptr)) Value_t(std::forward< Args > (args)...)); +}; +// +//----------------------------------------------------------------------------- +// function : destroy_object +/// @brief destroy an object in the memory specified by ptr +/// @param ptr : pointer to the object to destroy +//----------------------------------------------------------------------------- +template<class Value_t> +inline void destroy_object(Value_t *ptr) +{ + ptr->~Value_t(); +}; +// +//----------------------------------------------------------------------------- +// function : initialize +/// @brief initialize a range of objects with the object val moving across them +/// +/// @param first : itertor to the first element to initialize +/// @param last : iterator to the last element to initialize +/// @param val : object used for the initialization +//----------------------------------------------------------------------------- +template <class Iter_t, class Value_t = value_iter<Iter_t> > +inline void initialize (Iter_t first, Iter_t last, Value_t & val) +{ + //------------------------------------------------------------------------ + // Metaprogramming + //------------------------------------------------------------------------ + typedef value_iter<Iter_t> value_t; + static_assert (std::is_same< Value_t, value_t >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------ + // Code + //------------------------------------------------------------------------ + if (first == last) return; + construct_object(&(*first), std::move(val)); + + Iter_t it1 = first, it2 = first + 1; + while (it2 != last) + { + construct_object(&(*(it2++)), std::move(*(it1++))); + }; + val = std::move(*(last - 1)); +}; +// +//----------------------------------------------------------------------------- +// function : move_forward +/// @brief Move initialized objets +/// @param it_dest : iterator to the final place of the objects +/// @param first : iterator to the first element to move +/// @param last : iterator to the last element to move +/// @return Output iterator to the element past the last element +/// moved (it_dest + (last - first)) +//----------------------------------------------------------------------------- +template <class Iter1_t, class Iter2_t> +inline Iter2_t move_forward (Iter2_t it_dest, Iter1_t first, Iter1_t last) +{ + //------------------------------------------------------------------------ + // Metaprogramming + //------------------------------------------------------------------------ + typedef value_iter<Iter1_t> value1_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same< value1_t, value2_t >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------ + // Code + //------------------------------------------------------------------------ + while (first != last) + { *it_dest++ = std::move(*first++); + } + return it_dest; + +}; +// +//----------------------------------------------------------------------------- +// function : move_backard +/// @brief Move initialized objets in reverse order +/// @param it_dest : last iterator to the final place of the objects +/// @param first : iterator to the first element to move +/// @param last : iterator to the last element to move +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t> +inline Iter2_t move_backward(Iter2_t it_dest, Iter1_t first, Iter1_t last) +{ + //------------------------------------------------------------------------ + // Metaprogramming + //------------------------------------------------------------------------ + typedef value_iter<Iter1_t> value1_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same< value1_t, value2_t >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------ + // Code + //------------------------------------------------------------------------ + while (first != last) + { *(--it_dest) = std::move (*(--last)); + } + return it_dest; +}; + +// +//----------------------------------------------------------------------------- +// function : move_construct +/// @brief Move objets to uninitialized memory +/// +/// @param ptr : pointer to the memory where to create the objects +/// @param first : iterator to the first element to move +/// @param last : iterator to the last element to move +//----------------------------------------------------------------------------- +template<class Iter_t, class Value_t = value_iter<Iter_t> > +inline Value_t * move_construct(Value_t *ptr, Iter_t first, Iter_t last) +{ + //------------------------------------------------------------------------ + // Metaprogramming + //------------------------------------------------------------------------ + typedef typename iterator_traits<Iter_t>::value_type value2_t; + static_assert (std::is_same< Value_t, value2_t >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------ + // Code + //------------------------------------------------------------------------ + while (first != last) + { + ::new (static_cast<void *>(ptr++)) Value_t(std::move(*(first++))); + }; + return ptr; +}; +// +//----------------------------------------------------------------------------- +// function : destroy +/// @brief destroy the elements between first and last +/// @param first : iterator to the first element to destroy +/// @param last : iterator to the last element to destroy +//----------------------------------------------------------------------------- +template<class Iter_t> +inline void destroy(Iter_t first, const Iter_t last) +{ + while (first != last) + destroy_object(&(*(first++))); +}; +// +//----------------------------------------------------------------------------- +// function : reverse +/// @brief destroy the elements between first and last +/// @param first : iterator to the first element to destroy +/// @param last : iterator to the last element to destroy +//----------------------------------------------------------------------------- +template<class Iter_t> +inline void reverse(Iter_t first, Iter_t last) +{ + std::reverse ( first, last); +}; +// +//**************************************************************************** +};// End namespace util +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/util/atomic.hpp b/boost/sort/common/util/atomic.hpp new file mode 100644 index 0000000000..15906fe52a --- /dev/null +++ b/boost/sort/common/util/atomic.hpp @@ -0,0 +1,98 @@ +//---------------------------------------------------------------------------- +/// @file atomic.hpp +/// @brief Basic layer for to simplify the use of atomic functions +/// @author Copyright(c) 2016 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_UTIL_ATOMIC_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_UTIL_ATOMIC_HPP + +#include <atomic> +#include <cassert> +#include <type_traits> + +namespace boost +{ +namespace sort +{ +namespace common +{ +namespace util +{ +//----------------------------------------------------------------------------- +// function : atomic_read +/// @brief make the atomic read of an atomic variable, using a memory model +/// @param at_var : atomic variable to read +/// @return value obtained +//----------------------------------------------------------------------------- +template<typename T> +inline T atomic_read(std::atomic<T> &at_var) +{ + return std::atomic_load_explicit < T > (&at_var, std::memory_order_acquire); +}; +// +//----------------------------------------------------------------------------- +// function : atomic_add +/// @brief Add a number to an atomic variable, using a memory model +/// @param at_var : variable to add +/// @param num : value to add to at_var +/// @return result of the operation +//----------------------------------------------------------------------------- +template<typename T, typename T2> +inline T atomic_add(std::atomic<T> &at_var, T2 num) +{ + static_assert (std::is_integral< T2 >::value, "Bad parameter"); + return std::atomic_fetch_add_explicit <T> + (&at_var, (T) num, std::memory_order_acq_rel); +}; +// +//----------------------------------------------------------------------------- +// function : atomic_sub +/// @brief Atomic subtract of an atomic variable using memory model +/// @param at_var : Varibale to subtract +/// @param num : value to sub to at_var +/// @return result of the operation +//----------------------------------------------------------------------------- +template<typename T, typename T2> +inline T atomic_sub(std::atomic<T> &at_var, T2 num) +{ + static_assert (std::is_integral< T2 >::value, "Bad parameter"); + return std::atomic_fetch_sub_explicit <T> + (&at_var, (T) num, std::memory_order_acq_rel); +}; +// +//----------------------------------------------------------------------------- +// function : atomic_write +/// @brief Write a value in an atomic variable using memory model +/// @param at_var : varible to write +/// @param num : value to write in at_var +//----------------------------------------------------------------------------- +template<typename T, typename T2> +inline void atomic_write(std::atomic<T> &at_var, T2 num) +{ + static_assert (std::is_integral< T2 >::value, "Bad parameter"); + std::atomic_store_explicit <T> + (&at_var, (T) num, std::memory_order_release); +}; +template<typename T> +struct counter_guard +{ + typedef std::atomic<T> atomic_t; + atomic_t &count; + + counter_guard(atomic_t & counter): count(counter) { }; + ~counter_guard() {atomic_sub(count, 1); }; +}; +// +//**************************************************************************** +};// End namespace util +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +#endif diff --git a/boost/sort/common/util/circular_buffer.hpp b/boost/sort/common/util/circular_buffer.hpp new file mode 100644 index 0000000000..2fc7e973e1 --- /dev/null +++ b/boost/sort/common/util/circular_buffer.hpp @@ -0,0 +1,572 @@ +//---------------------------------------------------------------------------- +/// @file circular_buffer.hpp +/// @brief This file contains the implementation of the circular buffer +/// +/// @author Copyright (c) 2010 2015 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanyingfile LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_UTIL_CIRCULAR_BUFFER_HPP +#define __BOOST_SORT_COMMON_UTIL_CIRCULAR_BUFFER_HPP + +#include <memory> +#include <cassert> +#include <exception> +#include <boost/sort/common/util/algorithm.hpp> +#include <boost/sort/common/util/traits.hpp> + +namespace boost +{ +namespace sort +{ +namespace common +{ +namespace util +{ + +//--------------------------------------------------------------------------- +/// @class circular_buffer +/// @brief This class implement a circular buffer +/// @remarks +//--------------------------------------------------------------------------- +template <class Value_t, uint32_t Power2 = 11> +struct circular_buffer +{ + //------------------------------------------------------------------------ + // STATIC CHECK + //------------------------------------------------------------------------ + static_assert ( Power2 != 0, "Wrong Power2"); + + //------------------------------------------------------------------------ + // DEFINITIONS + //------------------------------------------------------------------------ + typedef Value_t value_t; + + //------------------------------------------------------------------------ + // VARIABLES + //------------------------------------------------------------------------ + const size_t NMAX = (size_t) 1 << Power2; + const size_t MASK = (NMAX - 1); + const size_t BLOCK_SIZE = NMAX >> 1; + const size_t LOG_BLOCK = Power2 - 1; + Value_t * ptr = nullptr; + + //------------------------------------------------------------------------ + // first and last are the position of the first and last elements + // always are in the range [0, NMAX - 1] + //------------------------------------------------------------------------ + size_t nelem, first_pos; + bool initialized; + + // + //------------------------------------------------------------------------ + // function : circular_buffer + /// @brief constructor of the class + //----------------------------------------------------------------------- + circular_buffer(void) + : ptr(nullptr), nelem(0), first_pos(0), initialized(false) + { + ptr = std::get_temporary_buffer < Value_t > (NMAX).first; + if (ptr == nullptr) throw std::bad_alloc(); + }; + // + //------------------------------------------------------------------------ + // function : ~circular_buffer + /// @brief destructor of the class + //----------------------------------------------------------------------- + ~circular_buffer() + { + if (initialized) + { for (size_t i = 0; i < NMAX; ++i) (ptr + i)->~Value_t(); + initialized = false; + }; + std::return_temporary_buffer(ptr); + } + ; + // + //------------------------------------------------------------------------ + // function : initialize + /// @brief : initialize the memory of the buffer from the uninitialize + // memory obtained from the temporary buffer + /// @param val : value used to initialize the memory + //----------------------------------------------------------------------- + void initialize(Value_t & val) + { + assert (initialized == false); + ::new (static_cast<void*>(ptr)) Value_t(std::move(val)); + for (size_t i = 1; i < NMAX; ++i) + ::new (static_cast<void*>(ptr + i)) Value_t(std::move(ptr[i - 1])); + val = std::move(ptr[NMAX - 1]); + initialized = true; + }; + // + //------------------------------------------------------------------------ + // function : destroy_all + /// @brief : destroy all the objects in the internal memory + //----------------------------------------------------------------------- + void destroy_all(void) { destroy(ptr, ptr + NMAX); }; + // + //------------------------------------------------------------------------ + // function : get_buffer + /// @brief return the internal memory of the circular buffer + /// @return pointer to the internal memory of the buffer + //----------------------------------------------------------------------- + Value_t * get_buffer(void) { return ptr; }; + // + //------------------------------------------------------------------------ + // function : empty + /// @brief return if the buffer is empty + /// @return true : empty + //----------------------------------------------------------------------- + bool empty(void) const {return (nelem == 0); }; + // + //------------------------------------------------------------------------ + // function : full + /// @brief return if the buffer is full + /// @return true : full + //----------------------------------------------------------------------- + bool full(void) const { return (nelem == NMAX); }; + // + //------------------------------------------------------------------------ + // function : size + /// @brief return the number of elements stored in the buffer + /// @return number of elements stored + //----------------------------------------------------------------------- + size_t size(void) const { return nelem;}; + // + //------------------------------------------------------------------------ + // function : capacity + /// @brief : return the maximun capacity of the buffer + /// @return number of elements + //----------------------------------------------------------------------- + size_t capacity(void) const { return NMAX;}; + // + //------------------------------------------------------------------------ + // function : free_size + /// @brief return the free positions in the buffer + /// @return number of elements + //----------------------------------------------------------------------- + size_t free_size(void) const { return (NMAX - nelem); }; + // + //------------------------------------------------------------------------ + // function : clear + /// @brief clear the buffer + //----------------------------------------------------------------------- + void clear(void) { nelem = first_pos = 0; }; + // + //------------------------------------------------------------------------ + // function : front + /// @brief return the first element of the buffer + /// @return reference to the first value + //----------------------------------------------------------------------- + Value_t & front(void) + { +#ifdef __BS_DEBUG + assert (nelem > 0); +#endif + return (ptr[first_pos]); + }; + // + //------------------------------------------------------------------------ + // function :front + /// @brief return the first element of the buffer + /// @return const reference to the first value + //----------------------------------------------------------------------- + const Value_t & front(void) const + { +#ifdef __BS_DEBUG + assert ( nelem > 0 ); +#endif + return (ptr[first_pos]); + }; + // + //------------------------------------------------------------------------ + // function : back + /// @brief reference to the last value of the buffer + /// @return reference to the last value + //----------------------------------------------------------------------- + Value_t & back(void) + { +#ifdef __BS_DEBUG + assert ( nelem > 0 ); +#endif + return (ptr[(first_pos + nelem - 1) & MASK]); + }; + // + //------------------------------------------------------------------------ + // function : back + /// @brief reference to the last value of the buffer + /// @return const reference to the last value + //----------------------------------------------------------------------- + const Value_t & back(void) const + { +#ifdef __BS_DEBUG + assert ( nelem > 0 ); +#endif + return (ptr[(first_pos + nelem - 1) & MASK]); + }; + // + //------------------------------------------------------------------------ + // function : operator [] + /// @brief positional access to the elements + /// @param pos rquested + /// @return reference to the element + //----------------------------------------------------------------------- + Value_t & operator[](uint32_t pos) + { +#ifdef __BS_DEBUG + assert ( nelem > 0 ); +#endif + return ptr[(first_pos + pos) & MASK]; + }; + // + //------------------------------------------------------------------------ + // function : operator [] + /// @brief positional access to the elements + /// @param pos rquested + /// @return const reference to the element + //----------------------------------------------------------------------- + const Value_t & operator[](uint32_t pos) const + { + +#ifdef __BS_DEBUG + assert ( nelem > 0 ); +#endif + return ptr[(first_pos + pos) & MASK]; + }; + // + //------------------------------------------------------------------------ + // function : push_front + /// @brief insert an element in the first position of the buffer + /// @param val : const value to insert + //----------------------------------------------------------------------- + void push_front(const Value_t & val) + { +#ifdef __BS_DEBUG + assert ( nelem != NMAX); +#endif + ++nelem; + first_pos = ((first_pos + MASK) & MASK); + ptr[first_pos] = val; + + }; + // + //------------------------------------------------------------------------ + // function : push_front + /// @brief insert an element in the first position of the buffer + /// @param val : rvalue to insert + //----------------------------------------------------------------------- + void push_front(Value_t && val) + { +#ifdef __BS_DEBUG + assert ( nelem != NMAX); +#endif + ++nelem; + first_pos = ((first_pos + MASK) & MASK); + ptr[first_pos] = val; + }; + // + //------------------------------------------------------------------------ + // function : push_back + /// @brief insert an element in the last position of the buffer + /// @param val : value to insert + //----------------------------------------------------------------------- + void push_back(const Value_t & val) + { +#ifdef __BS_DEBUG + assert ( nelem != NMAX); +#endif + ptr[(first_pos + (nelem++)) & MASK] = val; + }; + // + //------------------------------------------------------------------------ + // function : push_back + /// @brief insert an element in the last position of the buffer + /// @param val : value to insert + //----------------------------------------------------------------------- + void push_back(Value_t && val) + { +#ifdef __BS_DEBUG + assert ( nelem != NMAX); +#endif + ptr[(first_pos + (nelem++)) & MASK] = std::move(val); + }; + // + //------------------------------------------------------------------------ + // function : pop_front + /// @brief remove the first element of the buffer + //----------------------------------------------------------------------- + void pop_front(void) + { +#ifdef __BS_DEBUG + assert ( nelem > 0 ); +#endif + --nelem; + (++first_pos) &= MASK; + }; + // + //------------------------------------------------------------------------ + // function : pop_back + /// @brief remove the last element of the buffer + //----------------------------------------------------------------------- + void pop_back(void) + { +#ifdef __BS_DEBUG + assert ( nelem > 0 ); +#endif + --nelem; + }; + + template<class iter_t> + void pop_copy_front(iter_t it_dest, size_t num); + + template<class iter_t> + void pop_move_front(iter_t it_dest, size_t num); + + template<class iter_t> + void pop_copy_back(iter_t it_dest, size_t num); + + template<class iter_t> + void pop_move_back(iter_t it_dest, size_t num); + + template<class iter_t> + void push_copy_front(iter_t it_src, size_t num); + + template<class iter_t> + void push_move_front(iter_t it_src, size_t num); + + template<class iter_t> + void push_copy_back(iter_t it_src, size_t num); + + template<class iter_t> + void push_move_back(iter_t it_src, size_t num); + +//--------------------------------------------------------------------------- +};// End of class circular_buffer +//--------------------------------------------------------------------------- +// +// +//############################################################################ +// ## +// N O N I N L I N E F U N C T I O N S ## +// ## +//############################################################################ +// +//------------------------------------------------------------------------ +// function : pop_copy_front +/// @brief copy and delete num elements from the front of the buffer +/// @param it_dest : iterator to the first position where copy the elements +/// @param num : number of elements to copy +//----------------------------------------------------------------------- +template <class Value_t, uint32_t Power2> +template<class iter_t> +void circular_buffer<Value_t, Power2> +::pop_copy_front(iter_t it_dest, size_t num) +{ + static_assert ( std::is_same <value_iter<iter_t>, Value_t>::value, + "Incompatible iterator"); + if (num == 0) return; +#ifdef __BS_DEBUG + assert ( num <= nelem); +#endif + nelem -= num; + size_t pos = first_pos; + first_pos = (first_pos + num) & MASK; + for (size_t i = 0; i < num; ++i) + { + *(it_dest++) = ptr[pos++ & MASK]; + }; + first_pos &= MASK; +}; +// +//------------------------------------------------------------------------ +// function : pop_move_front +/// @brief move num elements from the front of the buffer to the place +// pointed by it_dest +/// @param it_dest : iterator to the first position where move the elements +/// @param num : number of elements to move +//----------------------------------------------------------------------- +template <class Value_t, uint32_t Power2> +template<class iter_t> +void circular_buffer<Value_t, Power2> +:: pop_move_front(iter_t it_dest, size_t num) +{ + static_assert ( std::is_same <value_iter<iter_t>, Value_t>::value, + "Incompatible iterator"); + if (num == 0) return; +#ifdef __BS_DEBUG + assert ( num <= nelem); +#endif + nelem -= num; + size_t pos = first_pos; + first_pos = (first_pos + num) & MASK; + for (size_t i = 0; i < num; ++i) + { + *(it_dest++) = std::move(ptr[pos++ & MASK]); + }; + first_pos &= MASK; +}; +// +//------------------------------------------------------------------------ +// function : pop_copy_back +/// @brief copy and delete num elements from the back of the buffer +/// @param p1 : iterator where begin to copy the elements +/// @param num : number of elements to copy +//----------------------------------------------------------------------- +template <class Value_t, uint32_t Power2> +template<class iter_t> +void circular_buffer<Value_t, Power2> +::pop_copy_back(iter_t it_dest, size_t num) +{ + static_assert ( std::is_same <value_iter<iter_t>, Value_t>::value, + "Incompatible iterator"); + if (num == 0) return; +#ifdef __BS_DEBUG + assert ( num <= nelem); +#endif + nelem -= num; + size_t pos = (first_pos + nelem) & MASK; + for (size_t i = 0; i < num; ++i) + { + *(it_dest++) = ptr[pos++ & MASK]; + }; +}; +// +//------------------------------------------------------------------------ +// function : pop_move_back +/// @brief move and delete num elements from the back of the buffer +/// @param p1 : iterator where begin to move the elements +/// @param num : number of elements to move +//----------------------------------------------------------------------- +template <class Value_t, uint32_t Power2> +template<class iter_t> +void circular_buffer<Value_t, Power2> +::pop_move_back(iter_t it_dest, size_t num) +{ + static_assert ( std::is_same <value_iter<iter_t>, Value_t>::value, + "Incompatible iterator"); + if (num == 0) return; +#ifdef __BS_DEBUG + assert ( num <= nelem); +#endif + nelem -= num; + size_t pos = (first_pos + nelem) & MASK; + for (size_t i = 0; i < num; ++i) + { + *(it_dest++) = std::move(ptr[pos++ & MASK]); + }; +}; +// +//------------------------------------------------------------------------ +// function : push_copy_front +/// @brief copy num elements in the front of the buffer +/// @param it_src : iterator from where begin to copy the elements +/// @param mun : number of element to copy +//----------------------------------------------------------------------- +template <class Value_t, uint32_t Power2> +template<class iter_t> +void circular_buffer<Value_t, Power2> +::push_copy_front(iter_t it_src, size_t num) +{ + static_assert ( std::is_same <value_iter<iter_t>, Value_t>::value, + "Incompatible iterator"); + if (num == 0) return; +#ifdef __BS_DEBUG + assert ( free_size() >= num); +#endif + nelem += num; + + first_pos = (first_pos + NMAX - num) & MASK; + size_t pos = first_pos; + for (size_t i = 0; i < num; ++i) + { + ptr[(pos++) & MASK] = *(it_src++); + }; +}; +// +//------------------------------------------------------------------------ +// function : push_move_front +/// @brief move num elements in the front of the buffer +/// @param p1 : iterator from where begin to move the elements +/// @param mun : number of element to move +//----------------------------------------------------------------------- +template <class Value_t, uint32_t Power2> +template<class iter_t> +void circular_buffer<Value_t, Power2> +::push_move_front(iter_t it_src, size_t num) +{ + static_assert ( std::is_same <value_iter<iter_t>, Value_t>::value, + "Incompatible iterator"); + if (num == 0) return; +#ifdef __BS_DEBUG + assert ( free_size() >= num); +#endif + nelem += num; + size_t pos = first_pos; + for (size_t i = 0; i < num; ++i) + { + ptr[(pos++) & MASK] = std::move(*(it_src++)); + }; +}; +// +//------------------------------------------------------------------------ +// function : push_copy_back +/// @brief copy num elements in the back of the buffer +/// @param p1 : iterator from where begin to copy the elements +/// @param mun : number of element to copy +//----------------------------------------------------------------------- +template <class Value_t, uint32_t Power2> +template<class iter_t> +void circular_buffer<Value_t, Power2> +::push_copy_back(iter_t it_src, size_t num) +{ + static_assert ( std::is_same <value_iter<iter_t>, Value_t>::value, + "Incompatible iterator"); + if (num == 0) return; +#ifdef __BS_DEBUG + assert ( free_size() >= num); +#endif + size_t pos = first_pos + nelem; + nelem += num; + for (size_t i = 0; i < num; ++i) + { + ptr[(pos++) & MASK] = *(it_src++); + }; +}; +// +//------------------------------------------------------------------------ +// function : push_move_back +/// @brief move num elements in the back of the buffer +/// @param p1 : iterator from where begin to move the elements +/// @param mun : number of element to move +//----------------------------------------------------------------------- +template <class Value_t, uint32_t Power2> +template<class iter_t> +void circular_buffer<Value_t, Power2> +::push_move_back(iter_t it_src, size_t num) +{ + static_assert ( std::is_same <value_iter<iter_t>, Value_t>::value, + "Incompatible iterator"); + if (num == 0) return; +#ifdef __BS_DEBUG + assert ( free_size() >= num); +#endif + size_t pos = first_pos + nelem; + nelem += num; + for (size_t i = 0; i < num; ++i) + { + ptr[(pos++) & MASK] = std::move(*(it_src++)); + }; +}; + +//**************************************************************************** +};// End namespace util +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +#endif diff --git a/boost/sort/common/util/insert.hpp b/boost/sort/common/util/insert.hpp new file mode 100644 index 0000000000..219fa8a351 --- /dev/null +++ b/boost/sort/common/util/insert.hpp @@ -0,0 +1,142 @@ +//---------------------------------------------------------------------------- +/// @file insert.hpp +/// @brief +/// +/// @author Copyright (c) 2016 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_UTIL_INSERT_HPP +#define __BOOST_SORT_COMMON_UTIL_INSERT_HPP + +//#include <boost/sort/spinsort/util/indirect.hpp> +#include <boost/sort/common/util/insert.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <boost/sort/common/util/algorithm.hpp> +#include <cstdlib> +#include <functional> +#include <iterator> +#include <memory> +#include <type_traits> +#include <vector> +#include <cstddef> + +namespace boost +{ +namespace sort +{ +namespace common +{ +namespace util +{ +namespace here = boost::sort::common::util; +// +//############################################################################ +// +// D E F I N I T I O N S O F F U N C T I O N S +// +// template < class Iter1_t, class Iter2_t, typename Compare> +// void insert_sorted (Iter1_t first, Iter1_t mid, Iter1_t last, +// Compare comp, Iter2_t it_aux) +// +//############################################################################ +// +//----------------------------------------------------------------------------- +// function : insert_sorted +/// @brief : Insertion sort of elements sorted +/// @param first: iterator to the first element of the range +/// @param mid : last pointer of the sorted data, and first pointer to the +/// elements to insert +/// @param last : iterator to the next element of the last in the range +/// @param comp : +/// @comments : the two ranges are sorted and in it_aux there is spave for +/// to store temporally the elements to insert +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, typename Compare> +static void insert_sorted(Iter1_t first, Iter1_t mid, Iter1_t last, + Compare comp, Iter2_t it_aux) +{ + //------------------------------------------------------------------------ + // metaprogram + //------------------------------------------------------------------------ + typedef value_iter<Iter1_t> value_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same< value_t, value2_t>::value, + "Incompatible iterators\n"); + + //-------------------------------------------------------------------- + // program + //-------------------------------------------------------------------- + if (mid == last) return; + if (first == mid) return; + + //------------------------------------------------------------------------ + // creation of the vector of elements to insert and their position in the + // sorted part + // the data are inserted in it_aux + //----------------------------------------------------------------------- + move_forward(it_aux, mid, last); + + // search of the iterators where insert the new elements + size_t ndata = last - mid; + Iter1_t mv_first = mid, mv_last = mid; + + for (size_t i = ndata; i > 0; --i) + { + mv_last = mv_first; + mv_first = std::upper_bound(first, mv_last, it_aux[i - 1], comp); + Iter1_t it1 = here::move_backward(mv_last + i, mv_first, mv_last); + *(it1 - 1) = std::move(it_aux[i - 1]); + }; +}; + +template<class Iter1_t, class Iter2_t, typename Compare> +static void insert_sorted_backward(Iter1_t first, Iter1_t mid, Iter1_t last, + Compare comp, Iter2_t it_aux) +{ + //------------------------------------------------------------------------ + // metaprogram + //------------------------------------------------------------------------ + typedef value_iter<Iter1_t> value_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same< value_t, value2_t>::value, + "Incompatible iterators\n"); + + //-------------------------------------------------------------------- + // program + //-------------------------------------------------------------------- + if (mid == last) return; + if (first == mid) return; + //------------------------------------------------------------------------ + // creation of the vector of elements to insert and their position in the + // sorted part + // the data are inserted in it_aux + //----------------------------------------------------------------------- + move_forward(it_aux, first, mid); + + // search of the iterators where insert the new elements + size_t ndata = mid - first; + Iter1_t mv_first = mid, mv_last = mid; + + for (size_t i = 0; i < ndata; ++i) + { + mv_first = mv_last; + mv_last = std::lower_bound(mv_first, last, it_aux[i], comp); + Iter1_t it1 = move_forward(mv_first - (ndata - i), mv_first, mv_last); + *(it1) = std::move(it_aux[i]); + }; + +}; +// +//**************************************************************************** +};// End namespace util +};// End namepspace common +};// End namespace sort +};// End namepspace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/util/merge.hpp b/boost/sort/common/util/merge.hpp new file mode 100644 index 0000000000..5fc90c0fd4 --- /dev/null +++ b/boost/sort/common/util/merge.hpp @@ -0,0 +1,494 @@ +//---------------------------------------------------------------------------- +/// @file merge.hpp +/// @brief low level merge functions +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_UTIL_MERGE_HPP +#define __BOOST_SORT_COMMON_UTIL_MERGE_HPP + +#include <algorithm> +#include <functional> +#include <iterator> +#include <memory> + +#include <boost/sort/common/util/algorithm.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <boost/sort/common/util/circular_buffer.hpp> + +namespace boost +{ +namespace sort +{ +namespace common +{ +namespace util +{ +namespace here = boost::sort::common::util; +//---------------------------------------------------------------------------- +// +// F U N C T I O N S I N T H E F I L E +//---------------------------------------------------------------------------- +// +// template < class Iter1_t, class Iter2_t, class Compare > +// Iter2_t merge (Iter1_t buf1, const Iter1_t end_buf1, Iter1_t buf2, +// const Iter1_t end_buf2, Iter2_t buf_out, Compare comp) +// +// template < class Iter_t, class Value_t, class Compare > +// Value_t *merge_construct (Iter_t first1, const Iter_t last1, Iter_t first2, +// const Iter_t last2, Value_t *it_out, Compare comp) +// +// template < class Iter1_t, class Iter2_t, class Compare > +// Iter2_t merge_half (Iter1_t buf1, const Iter1_t end_buf1, Iter2_t buf2, +// const Iter2_t end_buf2, Iter2_t buf_out, Compare comp) +// +// template < class Iter1_t, class Iter2_t, class Compare > +// Iter2_t merge_half_backward (Iter1_t buf1, Iter1_t end_buf1, +// Iter2_t buf2, Iter2_t end_buf2, +// Iter1_t end_buf_out, Compare comp) +// +// template < class Iter1_t, class Iter2_t, class Iter3_t, class Compare > +// bool merge_uncontiguous (Iter1_t src1, const Iter1_t end_src1, +// Iter2_t src2, const Iter2_t end_src2, +// Iter3_t aux, Compare comp) +// +// template < class Iter1_t, class Iter2_t, class Compare > +// bool merge_contiguous (Iter1_t src1, Iter1_t src2, Iter1_t end_src2, +// Iter2_t buf, Compare comp) +// +// template < class Iter_t, class Circular ,class Compare > +// bool merge_circular (Iter_t buf1, Iter_t end_buf1, +// Iter_t buf2, Iter_t end_buf2, +// Circular &circ, Compare comp, Iter_t &it_aux) +// +//---------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +// function : merge +/// @brief Merge two contiguous buffers pointed by buf1 and buf2, and put +/// in the buffer pointed by buf_out +/// +/// @param buf1 : iterator to the first element in the first buffer +/// @param end_buf1 : final iterator of first buffer +/// @param buf2 : iterator to the first iterator to the second buffer +/// @param end_buf2 : final iterator of the second buffer +/// @param buf_out : buffer where move the elements merged +/// @param comp : comparison object +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Iter3_t, class Compare> +static Iter3_t merge(Iter1_t buf1, const Iter1_t end_buf1, Iter2_t buf2, + const Iter2_t end_buf2, Iter3_t buf_out, Compare comp) +{ + //------------------------------------------------------------------------- + // Metaprogramming + //------------------------------------------------------------------------- + typedef value_iter<Iter1_t> value1_t; + typedef value_iter<Iter2_t> value2_t; + typedef value_iter<Iter3_t> value3_t; + static_assert (std::is_same< value1_t, value2_t >::value, + "Incompatible iterators\n"); + static_assert (std::is_same< value3_t, value2_t >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------- + // Code + //------------------------------------------------------------------------- + const size_t MIN_CHECK = 1024; + + if (size_t((end_buf1 - buf1) + (end_buf2 - buf2)) >= MIN_CHECK) + { + if (buf1 == end_buf1) return move_forward(buf_out, buf2, end_buf2); + if (buf2 == end_buf2) return move_forward(buf_out, buf1, end_buf1); + + if (not comp(*buf2, *(end_buf1 - 1))) + { + Iter3_t mid = move_forward(buf_out, buf1, end_buf1); + return move_forward(mid, buf2, end_buf2); + }; + + if (comp(*(end_buf2 - 1), *buf1)) + { + Iter3_t mid = move_forward(buf_out, buf2, end_buf2); + return move_forward(mid, buf1, end_buf1); + }; + }; + while ((buf1 != end_buf1) and (buf2 != end_buf2)) + { + *(buf_out++) = (not comp(*buf2, *buf1)) ? + std::move(*(buf1++)) : std::move(*(buf2++)); + }; + + return (buf1 == end_buf1) ? + move_forward(buf_out, buf2, end_buf2) : + move_forward(buf_out, buf1, end_buf1); +} +; +// +//----------------------------------------------------------------------------- +// function : merge_construct +/// @brief Merge two contiguous buffers pointed by first1 and first2, and put +/// in the uninitialized buffer pointed by it_out +/// +/// @param first1 : iterator to the first element in the first buffer +/// @param last1 : last iterator of the first buffer +/// @param first2 : iterator to the first element to the second buffer +/// @param last2 : final iterator of the second buffer +/// @param it_out : uninitialized buffer where move the elements merged +/// @param comp : comparison object +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Value_t, class Compare> +static Value_t *merge_construct(Iter1_t first1, const Iter1_t last1, + Iter2_t first2, const Iter2_t last2, + Value_t *it_out, Compare comp) +{ + //------------------------------------------------------------------------- + // Metaprogramming + //------------------------------------------------------------------------- + typedef value_iter<Iter1_t> type1; + typedef value_iter<Iter2_t> type2; + static_assert (std::is_same< Value_t, type1 >::value, + "Incompatible iterators\n"); + static_assert (std::is_same< Value_t, type2 >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------- + // Code + //------------------------------------------------------------------------- + const size_t MIN_CHECK = 1024; + + if (size_t((last1 - first1) + (last2 - first2)) >= MIN_CHECK) + { + if (first1 == last1) return move_construct(it_out, first2, last2); + if (first2 == last2) return move_construct(it_out, first1, last1); + + if (not comp(*first2, *(last1 - 1))) + { + Value_t* mid = move_construct(it_out, first1, last1); + return move_construct(mid, first2, last2); + }; + + if (comp(*(last2 - 1), *first1)) + { + Value_t* mid = move_construct(it_out, first2, last2); + return move_construct(mid, first1, last1); + }; + }; + while (first1 != last1 and first2 != last2) + { + construct_object((it_out++), + (not comp(*first2, *first1)) ? + std::move(*(first1++)) : + std::move(*(first2++))); + }; + return (first1 == last1) ? + move_construct(it_out, first2, last2) : + move_construct(it_out, first1, last1); +}; +// +//--------------------------------------------------------------------------- +// function : merge_half +/// @brief : Merge two buffers. The first buffer is in a separate memory. +/// The second buffer have a empty space before buf2 of the same size +/// than the (end_buf1 - buf1) +/// +/// @param buf1 : iterator to the first element of the first buffer +/// @param end_buf1 : iterator to the last element of the first buffer +/// @param buf2 : iterator to the first element of the second buffer +/// @param end_buf2 : iterator to the last element of the second buffer +/// @param buf_out : iterator to the first element to the buffer where put +/// the result +/// @param comp : object for Compare two elements of the type pointed +/// by the Iter1_t and Iter2_t +//--------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +static Iter2_t merge_half(Iter1_t buf1, const Iter1_t end_buf1, Iter2_t buf2, + const Iter2_t end_buf2, Iter2_t buf_out, Compare comp) +{ + //------------------------------------------------------------------------- + // Metaprogramming + //------------------------------------------------------------------------- + typedef value_iter<Iter1_t> value1_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same< value1_t, value2_t >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------- + // Code + //------------------------------------------------------------------------- +#ifdef __BS_DEBUG + assert ( (buf2 - buf_out) == ( end_buf1 - buf1)); +#endif + const size_t MIN_CHECK = 1024; + + if (size_t((end_buf1 - buf1) + (end_buf2 - buf2)) >= MIN_CHECK) + { + if (buf1 == end_buf1) return end_buf2; + if (buf2 == end_buf2) return move_forward(buf_out, buf1, end_buf1); + + if (not comp(*buf2, *(end_buf1 - 1))) + { + move_forward(buf_out, buf1, end_buf1); + return end_buf2; + }; + + if (comp(*(end_buf2 - 1), *buf1)) + { + Iter2_t mid = move_forward(buf_out, buf2, end_buf2); + return move_forward(mid, buf1, end_buf1); + }; + }; + while ((buf1 != end_buf1) and (buf2 != end_buf2)) + { + *(buf_out++) = (not comp(*buf2, *buf1)) ? + std::move(*(buf1++)) : std::move(*(buf2++)); + }; + return (buf2 == end_buf2)? move_forward(buf_out, buf1, end_buf1) : end_buf2; +}; + +// +//--------------------------------------------------------------------------- +// function : merge_half_backward +/// @brief : Merge two buffers. The first buffer is in a separate memory. +/// The second buffer have a empty space before buf2 of the same size +/// than the (end_buf1 - buf1) +/// +/// @param buf1 : iterator to the first element of the first buffer +/// @param end_buf1 : iterator to the last element of the first buffer +/// @param buf2 : iterator to the first element of the second buffer +/// @param end_buf2 : iterator to the last element of the second buffer +/// @param buf_out : iterator to the first element to the buffer where put +/// the result +/// @param comp : object for Compare two elements of the type pointed +/// by the Iter1_t and Iter2_t +//--------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +static Iter2_t merge_half_backward(Iter1_t buf1, Iter1_t end_buf1, Iter2_t buf2, + Iter2_t end_buf2, Iter1_t end_buf_out, + Compare comp) +{ + //------------------------------------------------------------------------- + // Metaprogramming + //------------------------------------------------------------------------- + typedef value_iter<Iter1_t> value1_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same< value1_t, value2_t >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------- + // Code + //------------------------------------------------------------------------- +#ifdef __BS_DEBUG + assert ((end_buf_out - end_buf1) == (end_buf2 - buf2) ); +#endif + const size_t MIN_CHECK = 1024; + + if (size_t((end_buf1 - buf1) + (end_buf2 - buf2)) >= MIN_CHECK) + { + if (buf2 == end_buf2) return buf1; + if (buf1 == end_buf1) + return here::move_backward(end_buf_out, buf2, end_buf2); + + if (not comp(*buf2, *(end_buf1 - 1))) + { + here::move_backward(end_buf_out, buf2, end_buf2); + return buf1; + }; + + if (comp(*(end_buf2 - 1), *buf1)) + { + Iter1_t mid = here::move_backward(end_buf_out, buf1, end_buf1); + return here::move_backward(mid, buf2, end_buf2); + }; + }; + while ((buf1 != end_buf1) and (buf2 != end_buf2)) + { + *(--end_buf_out) = + (not comp(*(end_buf2 - 1), *(end_buf1 - 1))) ? + std::move(*(--end_buf2)): + std::move(*(--end_buf1)); + }; + return (buf1 == end_buf1) ? + here::move_backward(end_buf_out, buf2, end_buf2) : buf1; +}; + +// +//----------------------------------------------------------------------------- +// function : merge_uncontiguous +/// @brief : merge two uncontiguous buffers, placing the results in the buffers +/// Use an auxiliary buffer pointed by aux +/// +/// @param src1 : iterator to the first element of the first buffer +/// @param end_src1 : last iterator of the first buffer +/// @param src2 : iterator to the first element of the second buffer +/// @param end_src2 : last iterator of the second buffer +/// @param aux : iterator to the first element of the auxiliary buffer +/// @param comp : object for to Compare elements +/// @return true : not changes done, false : changes in the buffers +/// @remarks +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Iter3_t, class Compare> +static bool merge_uncontiguous(Iter1_t src1, const Iter1_t end_src1, + Iter2_t src2, const Iter2_t end_src2, + Iter3_t aux, Compare comp) +{ + //------------------------------------------------------------------------- + // Metaprogramming + //------------------------------------------------------------------------- + typedef value_iter<Iter1_t> type1; + typedef value_iter<Iter2_t> type2; + typedef value_iter<Iter3_t> type3; + static_assert (std::is_same< type1, type2 >::value, + "Incompatible iterators\n"); + static_assert (std::is_same< type3, type2 >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------- + // Code + //------------------------------------------------------------------------- + if (src1 == end_src1 or src2 == end_src2 + or not comp(*src2, *(end_src1 - 1))) return true; + + while (src1 != end_src1 and not comp(*src2, *src1)) + ++src1; + + Iter3_t const end_aux = aux + (end_src1 - src1); + Iter2_t src2_first = src2; + move_forward(aux, src1, end_src1); + + while ((src1 != end_src1) and (src2 != end_src2)) + { + *(src1++) = std::move((not comp(*src2, *aux)) ? *(aux++) : *(src2++)); + } + + if (src2 == end_src2) + { + while (src1 != end_src1) + *(src1++) = std::move(*(aux++)); + move_forward(src2_first, aux, end_aux); + } + else + { + merge_half(aux, end_aux, src2, end_src2, src2_first, comp); + }; + return false; +}; + +// +//----------------------------------------------------------------------------- +// function : merge_contiguous +/// @brief : merge two contiguous buffers,using an auxiliary buffer pointed +/// by buf. The results are in src1 and src2 +/// +/// @param src1: iterator to the first position of the first buffer +/// @param src2: final iterator of the first buffer and first iterator +/// of the second buffer +/// @param end_src2 : final iterator of the second buffer +/// @param buf : iterator to buffer used as auxiliary memory +/// @param comp : object for to Compare elements +/// @return true : not changes done, false : changes in the buffers +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +static bool merge_contiguous(Iter1_t src1, Iter1_t src2, Iter1_t end_src2, + Iter2_t buf, Compare comp) +{ + //------------------------------------------------------------------------- + // Metaprogramming + //------------------------------------------------------------------------- + typedef value_iter<Iter1_t> type1; + typedef value_iter<Iter2_t> type2; + static_assert (std::is_same< type1, type2 >::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------- + // Code + //------------------------------------------------------------------------- + if (src1 == src2 or src2 == end_src2 or not comp(*src2, *(src2 - 1))) + return true; + + Iter1_t end_src1 = src2; + while (src1 != end_src1 and not comp(*src2, *src1)) + ++src1; + + if (src1 == end_src1) return false; + + size_t nx = end_src1 - src1; + move_forward(buf, src1, end_src1); + merge_half(buf, buf + nx, src2, end_src2, src1, comp); + return false; +}; +// +//----------------------------------------------------------------------------- +// function : merge_circular +/// @brief : merge two buffers,using a circular buffer +/// This function don't check the parameters +/// @param buf1: iterator to the first position of the first buffer +/// @param end_buf1: iterator after the last element of the first buffer +/// @param buf2: iterator to the first element of the secind buffer +/// @param end_buf2: iterator to the first element of the secind buffer +/// @param circ : circular buffer +/// @param comp : comparison object +/// @return true : finished buf1, false : finished buf2 +/// @comments : be carefully because the iterators buf1 and buf2 are modified +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Circular, class Compare> +static bool merge_circular(Iter1_t buf1, Iter1_t end_buf1, Iter2_t buf2, + Iter2_t end_buf2, Circular &circ, Compare comp, + Iter1_t &it1_out, Iter2_t &it2_out) +{ + //------------------------------------------------------------------------- + // Metaprogramming + //------------------------------------------------------------------------- + typedef value_iter<Iter1_t> type1; + typedef value_iter<Iter2_t> type2; + static_assert (std::is_same< type1, type2 >::value, + "Incompatible iterators\n"); + typedef typename Circular::value_t type3; + static_assert (std::is_same<type1, type3>::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------- + // Code + //------------------------------------------------------------------------- +#ifdef __BS_DEBUG + assert ( circ.free_size() >= size_t ((end_buf1-buf1) + (end_buf2-buf2))); +#endif + + if (not comp(*buf2, *(end_buf1 - 1))) + { + circ.push_move_back(buf1, (end_buf1 - buf1)); + it1_out = end_buf1; + it2_out = buf2; + return true; + }; + if (comp(*(end_buf2 - 1), *buf1)) + { + circ.push_move_back(buf2, (end_buf2 - buf2)); + it1_out = buf1; + it2_out = end_buf2; + return false; + } + while (buf1 != end_buf1 and buf2 != end_buf2) + { + circ.push_back(comp(*buf2, *buf1) ? std::move(*(buf2++)) + : std::move(*(buf1++))); + }; + it2_out = buf2; + it1_out = buf1; + bool ret = (buf1 == end_buf1); + return ret; +}; +// +//**************************************************************************** +};// End namespace util +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/util/search.hpp b/boost/sort/common/util/search.hpp new file mode 100644 index 0000000000..fbe056e2f8 --- /dev/null +++ b/boost/sort/common/util/search.hpp @@ -0,0 +1,529 @@ +//---------------------------------------------------------------------------- +/// @file search.hpp +/// @brief +/// @author Copyright (c) 2017 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See copy at http://www.boost.org/LICENSE_1_0.txt ) +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_SEARCH_HPP +#define __BOOST_SORT_COMMON_SEARCH_HPP + +#include <boost/sort/common/util/traits.hpp> +#include <cassert> + +namespace boost +{ +namespace sort +{ +namespace common +{ +namespace util +{ + +template<class T> +struct filter_pass +{ + typedef T key; + const key & operator()(const T & val) const + { + return val; + }; +}; + +// +//########################################################################### +// ## +// ################################################################ ## +// # # ## +// # I N T E R N A L F U N C T I O N S # ## +// # # ## +// ################################################################ ## +// ## +// I M P O R T A N T ## +// ## +// These functions are not directly callable by the user, are for internal ## +// use only. ## +// These functions don't check the parameters ## +// ## +//########################################################################### +// +//----------------------------------------------------------------------------- +// function : internal_find_first +/// @brief find if a value exist in the range [first, last). +/// Always return as valid iterator in the range [first, last-1] +/// If exist return the iterator to the first occurrence. If don't exist +/// return the first greater than val. +/// If val is greater than the *(last-1), return (last-1) +/// If val is lower than (*first), return first +// +/// @param [in] first : iterator to the first element of the range +/// @param [in] last : iterator to the last element of the range +/// @param [in] val : value to find +/// @param [in] comp : object for to compare two value_t objects +/// @return iterator to the element found, +//----------------------------------------------------------------------------- +template <class Iter_t, class Filter = filter_pass<value_iter<Iter_t> >, + class Compare = std::less<typename Filter::key> > +inline Iter_t internal_find_first(Iter_t first, Iter_t last, + const typename Filter::key &val, + const Compare & comp = Compare(), + Filter flt = Filter()) +{ + Iter_t LI = first, LS = last - 1, it_out = first; + while (LI != LS) + { + it_out = LI + ((LS - LI) >> 1); + if (comp(flt(*it_out), val)) + LI = it_out + 1; + else LS = it_out; + }; + return LS; +}; +// +//----------------------------------------------------------------------------- +// function : internal_find_last +/// @brief find if a value exist in the range [first, last). +/// Always return as valid iterator in the range [first, last-1] +/// If exist return the iterator to the last occurrence. +/// If don't exist return the first lower than val. +/// If val is greater than *(last-1) return (last-1). +/// If is lower than the first, return first +// +/// @param [in] first : iterator to the first element of the range +/// @param [in] last : iterator to the last element of the range +/// @param [in] val : value to find +/// @param [in] comp : object for to compare two value_t objects +/// @return iterator to the element found, if not found return last + +//----------------------------------------------------------------------------- +template<class Iter_t, class Filter = filter_pass<value_iter<Iter_t> >, + class Compare = std::less<typename Filter::key> > +inline Iter_t internal_find_last(Iter_t first, Iter_t last, + const typename Filter::key &val, + const Compare & comp = Compare(), Filter flt = + Filter()) +{ + Iter_t LI = first, LS = last - 1, it_out = first; + while (LI != LS) + { + it_out = LI + ((LS - LI + 1) >> 1); + if (comp(val, flt(*it_out))) LS = it_out - 1; + else LI = it_out; + }; + return LS; +}; + +// +//########################################################################### +// ## +// ################################################################ ## +// # # ## +// # P U B L I C F U N C T I O N S # ## +// # # ## +// ################################################################ ## +// ## +//########################################################################### +// +//----------------------------------------------------------------------------- +// function : find_first +/// @brief find if a value exist in the range [first, last). If exist return the +/// iterator to the first occurrence. If don't exist return last +// +/// @param [in] first : iterator to the first element of the range +/// @param [in] last : iterator to the last element of the range +/// @param [in] val : value to find +/// @param [in] comp : object for to compare two value_t objects +/// @return iterator to the element found, and if not last +//----------------------------------------------------------------------------- +template<class Iter_t, class Filter = filter_pass<value_iter<Iter_t> >, + class Compare = std::less<typename Filter::key> > +inline Iter_t find_first(Iter_t first, Iter_t last, + const typename Filter::key &val, + const Compare & comp = Compare(), + Filter flt = Filter()) +{ + assert((last - first) >= 0); + if (first == last) return last; + Iter_t LS = internal_find_first(first, last, val, comp, flt); + return (comp(flt(*LS), val) or comp(val, flt(*LS))) ? last : LS; +}; +// +//----------------------------------------------------------------------------- +// function : find_last +/// @brief find if a value exist in the range [first, last). If exist return the +/// iterator to the last occurrence. If don't exist return last +// +/// @param [in] first : iterator to the first element of the range +/// @param [in] last : iterator to the last element of the range +/// @param [in] val : value to find +/// @param [in] comp : object for to compare two value_t objects +/// @return iterator to the element found, if not found return last + +//----------------------------------------------------------------------------- +template <class Iter_t, class Filter = filter_pass<value_iter<Iter_t> >, + class Compare = std::less<typename Filter::key> > +inline Iter_t find_last(Iter_t first, Iter_t last, + const typename Filter::key &val, + const Compare & comp = Compare(), + Filter flt = Filter()) +{ + assert((last - first) >= 0); + if (last == first) return last; + Iter_t LS = internal_find_last(first, last, val, comp, flt); + return (comp(flt(*LS), val) or comp(val, flt(*LS))) ? last : LS; +}; + +//---------------------------------------------------------------------------- +// function : lower_bound +/// @brief Returns an iterator pointing to the first element in the range +/// [first, last) that is not less than (i.e. greater or equal to) val. +/// @param [in] last : iterator to the last element of the range +/// @param [in] val : value to find +/// @param [in] comp : object for to compare two value_t objects +/// @return iterator to the element found +//----------------------------------------------------------------------------- +template<class Iter_t, class Filter = filter_pass<value_iter<Iter_t> >, + class Compare = std::less<typename Filter::key> > +inline Iter_t lower_bound(Iter_t first, Iter_t last, + const typename Filter::key &val, + const Compare & comp = Compare(), + Filter flt = Filter()) +{ + assert((last - first) >= 0); + if (last == first) return last; + Iter_t itaux = internal_find_first(first, last, val, comp, flt); + return (itaux == (last - 1) and comp(flt(*itaux), val)) ? last : itaux; +}; +//---------------------------------------------------------------------------- +// function :upper_bound +/// @brief return the first element greather than val.If don't exist +/// return last +// +/// @param [in] first : iterator to the first element of the range +/// @param [in] last : iterator to the last element of the range +/// @param [in] val : value to find +/// @param [in] comp : object for to compare two value_t objects +/// @return iterator to the element found +/// @remarks +//----------------------------------------------------------------------------- +template<class Iter_t, class Filter = filter_pass<value_iter<Iter_t> >, + class Compare = std::less<typename Filter::key> > +inline Iter_t upper_bound(Iter_t first, Iter_t last, + const typename Filter::key &val, + const Compare & comp = Compare(), + Filter flt = Filter()) +{ + assert((last - first) >= 0); + if (last == first) return last; + Iter_t itaux = internal_find_last(first, last, val, comp, flt); + return (itaux == first and comp(val, flt(*itaux))) ? itaux : itaux + 1; +} +; +//---------------------------------------------------------------------------- +// function :equal_range +/// @brief return a pair of lower_bound and upper_bound with the value val.If +/// don't exist return last in the two elements of the pair +// +/// @param [in] first : iterator to the first element of the range +/// @param [in] last : iterator to the last element of the range +/// @param [in] val : value to find +/// @param [in] comp : object for to compare two value_t objects +/// @return pair of iterators +//----------------------------------------------------------------------------- +template<class Iter_t, class Filter = filter_pass<value_iter<Iter_t> >, + class Compare = std::less<typename Filter::key> > +inline std::pair<Iter_t, Iter_t> equal_range(Iter_t first, Iter_t last, + const typename Filter::key &val, + const Compare & comp = Compare(), + Filter flt = Filter()) +{ + return std::make_pair(lower_bound(first, last, val, comp, flt), + upper_bound(first, last, val, comp, flt)); +}; +// +//----------------------------------------------------------------------------- +// function : insert_first +/// @brief find if a value exist in the range [first, last). If exist return the +/// iterator to the first occurrence. If don't exist return last +// +/// @param [in] first : iterator to the first element of the range +/// @param [in] last : iterator to the last element of the range +/// @param [in] val : value to find +/// @param [in] comp : object for to compare two value_t objects +/// @return iterator to the element found, and if not last +//----------------------------------------------------------------------------- +template<class Iter_t, class Filter = filter_pass<value_iter<Iter_t> >, + class Compare = std::less<typename Filter::key> > +inline Iter_t insert_first(Iter_t first, Iter_t last, + const typename Filter::key &val, + const Compare & comp = Compare(), Filter flt = + Filter()) +{ + return lower_bound(first, last, val, comp, flt); +}; +// +//----------------------------------------------------------------------------- +// function : insert_last +/// @brief find if a value exist in the range [first, last). If exist return the +/// iterator to the last occurrence. If don't exist return last +// +/// @param [in] first : iterator to the first element of the range +/// @param [in] last : iterator to the last element of the range +/// @param [in] val : value to find +/// @param [in] comp : object for to compare two value_t objects +/// @return iterator to the element found, if not found return last + +//----------------------------------------------------------------------------- +template<class Iter_t, class Filter = filter_pass<value_iter<Iter_t> >, + class Compare = std::less<typename Filter::key> > +inline Iter_t insert_last(Iter_t first, Iter_t last, + const typename Filter::key &val, + const Compare & comp = Compare(), Filter flt = + Filter()) +{ + return upper_bound(first, last, val, comp, flt); +}; + +/* + + // + //########################################################################### + // ## + // ################################################################ ## + // # # ## + // # I N T E R N A L F U N C T I O N S # ## + // # # ## + // ################################################################ ## + // ## + // I M P O R T A N T ## + // ## + // These functions are not directly callable by the user, are for internal ## + // use only. ## + // These functions don't check the parameters ## + // ## + //########################################################################### + // + //----------------------------------------------------------------------------- + // function : internal_find_first + /// @brief find if a value exist in the range [first, last). + /// Always return as valid iterator in the range [first, last-1] + /// If exist return the iterator to the first occurrence. If don't exist + /// return the first greater than val. + /// If val is greater than the *(last-1), return (last-1) + /// If val is lower than (*first), return first + // + /// @param [in] first : iterator to the first element of the range + /// @param [in] last : iterator to the last element of the range + /// @param [in] val : value to find + /// @param [in] comp : object for to compare two value_t objects + /// @return iterator to the element found, + //----------------------------------------------------------------------------- + template < class Iter_t, class Compare = compare_iter<Iter_t> > + inline Iter_t internal_find_first ( Iter_t first, Iter_t last, + const value_iter<Iter_t> &val, + const Compare & comp= Compare() ) + { + Iter_t LI = first , LS = last - 1, it_out = first; + while ( LI != LS) + { it_out = LI + ( (LS - LI) >> 1); + if ( comp ( *it_out, val)) LI = it_out + 1 ; else LS = it_out ; + }; + return LS ; + }; + // + //----------------------------------------------------------------------------- + // function : internal_find_last + /// @brief find if a value exist in the range [first, last). + /// Always return as valid iterator in the range [first, last-1] + /// If exist return the iterator to the last occurrence. + /// If don't exist return the first lower than val. + /// If val is greater than *(last-1) return (last-1). + /// If is lower than the first, return first + // + /// @param [in] first : iterator to the first element of the range + /// @param [in] last : iterator to the last element of the range + /// @param [in] val : value to find + /// @param [in] comp : object for to compare two value_t objects + /// @return iterator to the element found, if not found return last + + //----------------------------------------------------------------------------- + template < class Iter_t, class Compare = compare_iter<Iter_t> > + inline Iter_t internal_find_last ( Iter_t first, Iter_t last , + const value_iter<Iter_t> &val, + const Compare &comp= Compare() ) + { + Iter_t LI = first , LS = last - 1, it_out = first ; + while ( LI != LS) + { it_out = LI + ( (LS - LI + 1) >> 1); + if ( comp (val, *it_out)) LS = it_out - 1 ; else LI = it_out ; + }; + return LS ; + }; + + // + //########################################################################### + // ## + // ################################################################ ## + // # # ## + // # P U B L I C F U N C T I O N S # ## + // # # ## + // ################################################################ ## + // ## + //########################################################################### + // + //----------------------------------------------------------------------------- + // function : find_first + /// @brief find if a value exist in the range [first, last). If exist return the + /// iterator to the first occurrence. If don't exist return last + // + /// @param [in] first : iterator to the first element of the range + /// @param [in] last : iterator to the last element of the range + /// @param [in] val : value to find + /// @param [in] comp : object for to compare two value_t objects + /// @return iterator to the element found, and if not last + //----------------------------------------------------------------------------- + template < class Iter_t, class Compare = compare_iter<Iter_t> > + inline Iter_t find_first ( Iter_t first, Iter_t last, + const value_iter<Iter_t> &val, + Compare comp = Compare() ) + { + assert ( (last - first) >= 0 ); + if ( first == last) return last ; + Iter_t LS = internal_find_first ( first, last, val, comp); + return (comp (*LS, val) or comp (val, *LS))?last:LS; + }; + // + //----------------------------------------------------------------------------- + // function : find_last + /// @brief find if a value exist in the range [first, last). If exist return the + /// iterator to the last occurrence. If don't exist return last + // + /// @param [in] first : iterator to the first element of the range + /// @param [in] last : iterator to the last element of the range + /// @param [in] val : value to find + /// @param [in] comp : object for to compare two value_t objects + /// @return iterator to the element found, if not found return last + + //----------------------------------------------------------------------------- + template < class Iter_t, class Compare = compare_iter<Iter_t> > + inline Iter_t find_last ( Iter_t first, Iter_t last , + const value_iter<Iter_t> &val, + Compare comp = Compare()) + { + assert ( (last - first ) >= 0 ); + if ( last == first ) return last ; + Iter_t LS = internal_find_last (first, last, val, comp); + return (comp (*LS, val) or comp (val, *LS))?last:LS ; + }; + + //---------------------------------------------------------------------------- + // function : lower_bound + /// @brief Returns an iterator pointing to the first element in the range + /// [first, last) that is not less than (i.e. greater or equal to) val. + /// @param [in] last : iterator to the last element of the range + /// @param [in] val : value to find + /// @param [in] comp : object for to compare two value_t objects + /// @return iterator to the element found + //----------------------------------------------------------------------------- + template < class Iter_t, class Compare = compare_iter<Iter_t> > + inline Iter_t lower_bound ( Iter_t first, Iter_t last , + const value_iter<Iter_t> &val, + Compare &comp = Compare() ) + { + assert ( (last - first ) >= 0 ); + if ( last == first ) return last ; + Iter_t itaux = internal_find_first( first, last, val,comp); + return (itaux == (last - 1) and comp (*itaux, val))?last: itaux; + }; + //---------------------------------------------------------------------------- + // function :upper_bound + /// @brief return the first element greather than val.If don't exist + /// return last + // + /// @param [in] first : iterator to the first element of the range + /// @param [in] last : iterator to the last element of the range + /// @param [in] val : value to find + /// @param [in] comp : object for to compare two value_t objects + /// @return iterator to the element found + /// @remarks + //----------------------------------------------------------------------------- + template < class Iter_t, class Compare = compare_iter<Iter_t> > + inline Iter_t upper_bound ( Iter_t first, Iter_t last , + const value_iter<Iter_t> &val, + Compare &comp = Compare() ) + { + assert ( (last - first ) >= 0 ); + if ( last == first ) return last ; + Iter_t itaux = internal_find_last( first, last, val,comp); + return ( itaux == first and comp (val,*itaux))? itaux: itaux + 1; + }; + //---------------------------------------------------------------------------- + // function :equal_range + /// @brief return a pair of lower_bound and upper_bound with the value val.If + /// don't exist return last in the two elements of the pair + // + /// @param [in] first : iterator to the first element of the range + /// @param [in] last : iterator to the last element of the range + /// @param [in] val : value to find + /// @param [in] comp : object for to compare two value_t objects + /// @return pair of iterators + //----------------------------------------------------------------------------- + template < class Iter_t, class Compare = compare_iter<Iter_t> > + inline std::pair<Iter_t, Iter_t> equal_range ( Iter_t first, Iter_t last , + const value_iter<Iter_t> &val, + Compare &comp = Compare() ) + { + return std::make_pair(lower_bound(first, last, val,comp), + upper_bound(first, last, val,comp)); + }; + // + //----------------------------------------------------------------------------- + // function : insert_first + /// @brief find if a value exist in the range [first, last). If exist return the + /// iterator to the first occurrence. If don't exist return last + // + /// @param [in] first : iterator to the first element of the range + /// @param [in] last : iterator to the last element of the range + /// @param [in] val : value to find + /// @param [in] comp : object for to compare two value_t objects + /// @return iterator to the element found, and if not last + //----------------------------------------------------------------------------- + template < class Iter_t, class Compare = compare_iter<Iter_t> > + inline Iter_t insert_first ( Iter_t first, Iter_t last, + const value_iter<Iter_t> &val, + Compare comp = Compare() ) + { + return lower_bound (first, last, val, comp); + }; + // + //----------------------------------------------------------------------------- + // function : insert_last + /// @brief find if a value exist in the range [first, last). If exist return the + /// iterator to the last occurrence. If don't exist return last + // + /// @param [in] first : iterator to the first element of the range + /// @param [in] last : iterator to the last element of the range + /// @param [in] val : value to find + /// @param [in] comp : object for to compare two value_t objects + /// @return iterator to the element found, if not found return last + + //----------------------------------------------------------------------------- + template < class Iter_t, class Compare = compare_iter<Iter_t> > + inline Iter_t insert_last ( Iter_t first, Iter_t last , + const value_iter<Iter_t> &val, + Compare comp = Compare()) + { + return upper_bound (first, last, val, comp); + }; + + */ +// +//**************************************************************************** +};// End namespace util +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/common/util/traits.hpp b/boost/sort/common/util/traits.hpp new file mode 100644 index 0000000000..68e5cf0359 --- /dev/null +++ b/boost/sort/common/util/traits.hpp @@ -0,0 +1,123 @@ +//---------------------------------------------------------------------------- +/// @file traits.hpp +/// @brief this file contains the metaprogramming classes compare_iter and +/// enable_if_not_integral +/// @author Copyright(c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_COMMON_UTIL_TRAITS_HPP +#define __BOOST_SORT_COMMON_UTIL_TRAITS_HPP + +#include <functional> +#include <iterator> +#include <type_traits> + +namespace boost +{ +namespace sort +{ +namespace common +{ +namespace util +{ +//---------------------------------------------------------------------------- +// USING SENTENCES +//---------------------------------------------------------------------------- +using std::iterator_traits; + +// +//--------------------------------------------------------------------------- +/// @class value_iter +/// @brief From the iterator, obtain the type pointed by it +/// @remarks The main utility of this, is simplify the default template +/// parameter of comparison +//--------------------------------------------------------------------------- +template<class iter_t> +using value_iter = typename iterator_traits< iter_t >::value_type; +// +//--------------------------------------------------------------------------- +/// @class compare_iter +/// @brief From the iterator, received as template parameter, obtain the type +/// of the object pointed by the iterator, and with this define the +/// std::less with this type obtained +/// @remarks The main utility of this, is simplify the default template +/// parameter of comparison +//--------------------------------------------------------------------------- +template<class iter_t> +using compare_iter = std::less< value_iter< iter_t > >; + +// +//--------------------------------------------------------------------------- +/// @class enable_if_not_integral +/// @brief This is a SFINAE class for to detect if the third parameter in the +/// invocation of the parallel sorting algorithms is an integer +/// representing the number of threads to use or is a comparison object +/// @remarks +//--------------------------------------------------------------------------- +template<class T> +using enable_if_not_integral = + typename std::enable_if< !std::is_integral< T >::value >::type; +// +//--------------------------------------------------------------------------- +/// @class enable_if_integral +/// @brief This is a SFINAE class for to detect if the third parameter in the +/// invocation of the parallel sorting algorithms is an integer +/// representing the number of threads to use or is a comparison object +/// @remarks +//--------------------------------------------------------------------------- +template<class T> +using enable_if_integral = + typename std::enable_if< std::is_integral< T >::value >::type; + +// +//--------------------------------------------------------------------------- +/// @class enable_if_string +/// @brief This is a SFINAE class for to detect if the parameter is a +/// std::string for to apply specialized parameters in the invocation +/// of the block_indirect_sort algorithm +/// @remarks +//--------------------------------------------------------------------------- +template<class T> +using enable_if_string = + typename std::enable_if< std::is_same< T, std::string >::value >::type; + +// +//--------------------------------------------------------------------------- +/// @class enable_if_not_string +/// @brief This is a SFINAE class for to detect if the parameter is a +/// std::string for to apply specialized parameters in the invocation +/// of the block_indirect_sort algorithm +/// @remarks +//--------------------------------------------------------------------------- +template<class T> +using enable_if_not_string = + typename std::enable_if<! std::is_same< T, std::string >::value >::type; + +// +//--------------------------------------------------------------------------- +/// @class constructor +/// @brief create a functor with the constructor of a class for to be invoked +/// from a bind or a lambda +/// @remarks +//--------------------------------------------------------------------------- +template<class T> +struct constructor +{ + template<class ... Args> + void operator()(Args && ... args) + { + T(std::forward<Args> (args) ...); + }; +}; +// +//**************************************************************************** +};// End namespace util +};// End namespace common +};// End namespace sort +};// End namespace boost +//**************************************************************************** +#endif diff --git a/boost/sort/flat_stable_sort/flat_stable_sort.hpp b/boost/sort/flat_stable_sort/flat_stable_sort.hpp new file mode 100644 index 0000000000..ee48e7b9a0 --- /dev/null +++ b/boost/sort/flat_stable_sort/flat_stable_sort.hpp @@ -0,0 +1,312 @@ +//---------------------------------------------------------------------------- +/// @file flat_stable_sort.hpp +/// @brief Flat stable sort algorithm +/// +/// @author Copyright (c) 2017 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_FLAT_STABLE_SORT_HPP +#define __BOOST_SORT_FLAT_STABLE_SORT_HPP + +#include <boost/sort/insert_sort/insert_sort.hpp> +#include <boost/sort/common/util/insert.hpp> +#include <boost/sort/common/merge_block.hpp> +#include <boost/sort/common/sort_basic.hpp> +#include <boost/sort/common/range.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <boost/sort/common/indirect.hpp> + +#include <cstdlib> +#include <functional> +#include <iterator> +#include <memory> +#include <type_traits> +#include <vector> + +namespace boost +{ +namespace sort +{ +namespace flat_internal +{ +namespace bsc = boost::sort::common; +namespace bscu = boost::sort::common::util; +//--------------------------------------------------------------------------- +/// @struct flat_stable_sort +/// @brief This class implement s stable sort algorithm with 1 thread, with +/// an auxiliary memory of N/2 elements +//---------------------------------------------------------------------------- +template <class Iter_t, typename Compare = bscu::compare_iter<Iter_t>, + uint32_t Power2 = 10> +class flat_stable_sort: public bsc::merge_block<Iter_t, Compare, Power2> +{ + //------------------------------------------------------------------------ + // DEFINITIONS AND CONSTANTS + //------------------------------------------------------------------------ + typedef bsc::merge_block<Iter_t, Compare, Power2> merge_block_t; + + //------------------------------------------------------------------------- + // D E F I N I T I O N S + //------------------------------------------------------------------------- + typedef typename merge_block_t::value_t value_t; + typedef typename merge_block_t::range_pos range_pos; + typedef typename merge_block_t::range_it range_it; + typedef typename merge_block_t::range_buf range_buf; + typedef typename merge_block_t::it_index it_index; + typedef typename merge_block_t::circular_t circular_t; + + //------------------------------------------------------------------------ + // CONSTANTS + //------------------------------------------------------------------------ + using merge_block_t::BLOCK_SIZE; + using merge_block_t::LOG_BLOCK; + + using merge_block_t::index; + using merge_block_t::cmp; + using merge_block_t::ptr_circ; + + using merge_block_t::get_range; + using merge_block_t::get_group_range; + using merge_block_t::merge_range_pos; + using merge_block_t::move_range_pos_backward; + using merge_block_t::rearrange_with_index; + +public: + //------------------------------------------------------------------------ + // PUBLIC FUNCTIONS + //------------------------------------------------------------------------- + flat_stable_sort(Iter_t first, Iter_t last, Compare comp, + circular_t *ptr_circ) + : merge_block_t(first, last, comp, ptr_circ) + { + divide(index.begin(), index.end()); + rearrange_with_index(); + }; + + flat_stable_sort(Iter_t first, Iter_t last, Compare comp = Compare()) + : flat_stable_sort(first, last, comp, nullptr) { }; + + void divide(it_index itx_first, it_index itx_last); + + void sort_small(it_index itx_first, it_index itx_last); + + bool is_sorted_forward(it_index itx_first, it_index itx_last); + + bool is_sorted_backward(it_index itx_first, it_index itx_last); +}; +//---------------------------------------------------------------------------- +// End of class flat_stable_sort +//---------------------------------------------------------------------------- +// +//------------------------------------------------------------------------ +// function : +/// @brief : +/// @param Pos : +/// @return +//------------------------------------------------------------------------ +template <class Iter_t, typename Compare, uint32_t Power2> +void flat_stable_sort <Iter_t, Compare, Power2> +::divide(it_index itx_first, it_index itx_last) +{ + size_t nblock = size_t(itx_last - itx_first); + if (nblock < 5) + { sort_small(itx_first, itx_last); + return; + }; + if ( nblock > 7) + { if (is_sorted_forward(itx_first, itx_last)) return; + if (is_sorted_backward(itx_first, itx_last)) return; + }; + size_t nblock1 = (nblock + 1) >> 1; + divide(itx_first, itx_first + nblock1); + divide(itx_first + nblock1, itx_last); + merge_range_pos(itx_first, itx_first + nblock1, itx_last); +}; +// +//------------------------------------------------------------------------ +// function : sort_small +/// @brief : +/// @param +/// @param +/// @param +//------------------------------------------------------------------------ +template <class Iter_t, typename Compare, uint32_t Power2> +void flat_stable_sort <Iter_t, Compare, Power2> +::sort_small(it_index itx_first, it_index itx_last) +{ + size_t nblock = size_t(itx_last - itx_first); + assert(nblock > 0 and nblock < 5); + value_t *paux = ptr_circ->get_buffer(); + range_it rng_data = get_group_range(*itx_first, nblock); + + if (nblock < 3) + { + range_buf rng_aux(paux, paux + rng_data.size()); + range_sort_data(rng_data, rng_aux, cmp); + return; + }; + + //-------------------------------------------------------------------- + // division of range_data in two ranges for be sorted and merged + //-------------------------------------------------------------------- + size_t nblock1 = (nblock + 1) >> 1; + range_it rng_data1 = get_group_range(*itx_first, nblock1); + range_it rng_data2(rng_data1.last, rng_data.last); + range_buf rng_aux1(paux, paux + rng_data1.size()); + range_buf rng_aux2(paux, paux + rng_data2.size()); + + range_sort_data(rng_data2, rng_aux2, cmp); + range_sort_buffer(rng_data1, rng_aux1, cmp); + merge_half(rng_data, rng_aux1, rng_data2, cmp); +}; +// +//------------------------------------------------------------------------ +// function : is_sorted_forward +/// @brief : return if the data are ordered, +/// @param itx_first : iterator to the first block in the index +/// @param itx_last : iterator to the last block in the index +/// @return : true : the data are ordered false : not ordered +//------------------------------------------------------------------------ +template <class Iter_t, typename Compare, uint32_t Power2> +bool flat_stable_sort <Iter_t, Compare, Power2> +::is_sorted_forward(it_index itx_first, it_index itx_last) +{ + size_t nblock = size_t(itx_last - itx_first); + range_it rng = get_group_range(*itx_first, nblock); + size_t nelem = rng.size(); + size_t min_process = std::max(BLOCK_SIZE, (nelem >> 3)); + + size_t nsorted1 = bsc::number_stable_sorted_forward (rng.first, rng.last, + min_process, cmp); + if (nsorted1 == nelem) return true; + if (nsorted1 == 0) return false; + + size_t nsorted2 = nelem - nsorted1; + Iter_t itaux = rng.first + nsorted1; + if (nsorted2 <= (BLOCK_SIZE << 1)) + { + flat_stable_sort(itaux, rng.last, cmp, ptr_circ); + bscu::insert_sorted(rng.first, itaux, rng.last, cmp, + ptr_circ->get_buffer()); + } + else + { // Adjust the size of the sorted data to a number of blocks + size_t mask = ~(BLOCK_SIZE - 1); + size_t nsorted1_adjust = nsorted1 & mask; + flat_stable_sort(rng.first + nsorted1_adjust, rng.last, cmp, + ptr_circ); + size_t nblock1 = nsorted1_adjust >> Power2; + merge_range_pos(itx_first, itx_first + nblock1, itx_last); + }; + return true; +}; +// +//------------------------------------------------------------------------ +// function : is_sorted_backward +/// @brief : return if the data are ordered, +/// @param itx_first : iterator to the first block in the index +/// @param itx_last : iterator to the last block in the index +/// @return : true : the data are ordered false : not ordered +//------------------------------------------------------------------------ +template <class Iter_t, typename Compare, uint32_t Power2> +bool flat_stable_sort <Iter_t, Compare, Power2> +::is_sorted_backward(it_index itx_first, it_index itx_last) +{ + size_t nblock = size_t(itx_last - itx_first); + range_it rng = get_group_range(*itx_first, nblock); + + size_t nelem = rng.size(); + size_t min_process = std::max(BLOCK_SIZE, (nelem >> 3)); + + size_t nsorted2 = bsc::number_stable_sorted_backward(rng.first, rng.last, + min_process, cmp); + if (nsorted2 == nelem) return true; + if (nsorted2 == 0 ) return false; + Iter_t itaux = rng.last - nsorted2; + size_t nsorted1 = nelem - nsorted2; + + if (nsorted1 <= (BLOCK_SIZE << 1)) + { + flat_stable_sort(rng.first, itaux, cmp, ptr_circ); + bscu::insert_sorted_backward(rng.first, itaux, rng.last, cmp, + ptr_circ->get_buffer()); + } + else + { // Adjust the size of nsorted2 for to be a number of blocks + size_t nblock1 = (nsorted1 + BLOCK_SIZE - 1) >> Power2; + size_t nsorted1_adjust = (nblock1 << Power2); + flat_stable_sort(rng.first, rng.first + nsorted1_adjust, cmp, + ptr_circ); + merge_range_pos(itx_first, itx_first + nblock1, itx_last); + }; + return true; +}; +//**************************************************************************** +};// End namespace flat_internal +//**************************************************************************** +// +namespace bscu = boost::sort::common::util; +namespace flat = boost::sort::flat_internal; +// +///--------------------------------------------------------------------------- +// function flat_stable_sort +/// @brief This class is select the block size in the block_indirect_sort +/// algorithm depending of the type and size of the data to sort +/// +//---------------------------------------------------------------------------- +template <class Iter_t, class Compare = bscu::compare_iter<Iter_t>, + bscu::enable_if_string<value_iter<Iter_t> > * = nullptr> +inline void flat_stable_sort (Iter_t first, Iter_t last, + Compare cmp = Compare()) +{ + flat::flat_stable_sort<Iter_t, Compare, 6> (first, last, cmp); +}; + +template<size_t Size> +struct block_size_fss +{ + static constexpr const uint32_t BitsSize = + (Size == 0) ? 0 : (Size > 128) ? 7 : bscu::tmsb[Size - 1]; + static constexpr const uint32_t sz[10] = + { 10, 10, 10, 9, 8, 7, 6, 6 }; + static constexpr const uint32_t data = sz[BitsSize]; +}; + +// +///--------------------------------------------------------------------------- +// function flat_stable_sort +/// @brief This class is select the block size in the flat_stable_sort +/// algorithm depending of the type and size of the data to sort +/// +//---------------------------------------------------------------------------- +template <class Iter_t, class Compare = bscu::compare_iter<Iter_t>, + bscu::enable_if_not_string<value_iter<Iter_t> >* = nullptr> +inline void flat_stable_sort (Iter_t first, Iter_t last, + Compare cmp = Compare()) +{ + flat::flat_stable_sort<Iter_t, Compare, + block_size_fss<sizeof(value_iter<Iter_t> )>::data> + (first, last, cmp); +}; + +template<class Iter_t, class Compare = compare_iter<Iter_t> > +inline void indirect_flat_stable_sort (Iter_t first, Iter_t last, + Compare comp = Compare()) +{ + typedef typename std::vector<Iter_t>::iterator itx_iter; + typedef common::less_ptr_no_null<Iter_t, Compare> itx_comp; + common::indirect_sort ( flat_stable_sort<itx_iter, itx_comp>, + first, last, comp); +}; + +//**************************************************************************** +};// End namespace sort +};// End namepspace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/heap_sort/heap_sort.hpp b/boost/sort/heap_sort/heap_sort.hpp new file mode 100644 index 0000000000..9e89d00b8c --- /dev/null +++ b/boost/sort/heap_sort/heap_sort.hpp @@ -0,0 +1,215 @@ +//---------------------------------------------------------------------------- +/// @file heap_sort.hpp +/// @brief Insertion Sort algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_INTROSORT_DETAIL_HEAP_SORT_HPP +#define __BOOST_SORT_INTROSORT_DETAIL_HEAP_SORT_HPP + +#include <cassert> +#include <cstdint> +#include <iterator> +#include <stdexcept> +#include <utility> // for std::swap +#include <boost/sort/common/util/traits.hpp> + +namespace boost +{ +namespace sort +{ +namespace heap_detail +{ +namespace bscu = boost::sort::common::util; +// +//--------------------------------------------------------------------------- +// struct : heap_sort +/// @brief : Heap sort algorithm +/// @remarks This algorithm is O(NLogN) +//--------------------------------------------------------------------------- +template < class Iter_t, class Compare > +struct heap_sort +{ + typedef bscu::value_iter<Iter_t> value_t; + + // + //------------------------------------------------------------------------ + // function : sort3 + /// @brief Sort and signal the changes of three values + /// @param val_0 : first value to compare + /// @param val_1 : second value to compare + /// @param val_2 : third value to compare + /// @param [out] bool_0 : if true indicates val_0 had been changed + /// @param [out] bool_1 : if true indicates val_1 had been changed + /// @param [out] bool_2 : if true indicates val_2 had been changed + /// @return if true , some value had changed + /// @remarks + //------------------------------------------------------------------------ + bool sort3 (value_t &val_0, value_t &val_1, value_t &val_2, bool &bool_0, + bool &bool_1, bool &bool_2) + { + bool_0 = bool_1 = bool_2 = false; + int value = 0; + if (val_0 < val_1) value += 4; + if (val_1 < val_2) value += 2; + if (val_0 < val_2) value += 1; + + switch (value) + { + case 0: break; + + case 2: + std::swap (val_1, val_2); + bool_1 = bool_2 = true; + break; + + case 3: + if (not(val_0 > val_1)) { + std::swap (val_0, val_2); + bool_0 = bool_2 = true; + } + else + { + auto aux = std::move (val_2); + val_2 = std::move (val_1); + val_1 = std::move (val_0); + val_0 = std::move (aux); + bool_0 = bool_1 = bool_2 = true; + }; + break; + + case 4: + std::swap (val_0, val_1); + bool_0 = bool_1 = true; + break; + + case 5: + if (val_1 > val_2) { + auto aux = std::move (val_0); + val_0 = std::move (val_1); + val_1 = std::move (val_2); + val_2 = std::move (aux); + bool_0 = bool_1 = bool_2 = true; + } + else + { + std::swap (val_0, val_2); + bool_0 = bool_2 = true; + }; + break; + + case 7: + std::swap (val_0, val_2); + bool_0 = bool_2 = true; + break; + + default: abort ( ); + }; + return (bool_0 or bool_1 or bool_2); + }; + // + //----------------------------------------------------------------------- + // function : make_heap + /// @brief Make the heap for to extract the sorted elements + /// @param first : iterator to the first element of the range + /// @param nelem : number of lements of the range + /// @param comp : object for to compare two elements + /// @remarks This algorithm is O(NLogN) + //------------------------------------------------------------------------ + void make_heap (Iter_t first, size_t nelem, Compare comp) + { + size_t pos_father, pos_son; + Iter_t iter_father = first, iter_son = first; + bool sw = false; + + for (size_t i = 1; i < nelem; ++i) + { + pos_father = i; + iter_father = first + i; + sw = false; + do + { + iter_son = iter_father; + pos_son = pos_father; + pos_father = (pos_son - 1) >> 1; + iter_father = first + pos_father; + if ((sw = comp (*iter_father, *iter_son))) + std::swap (*iter_father, *iter_son); + } while (sw and pos_father != 0); + }; + }; + // + //------------------------------------------------------------------------ + // function : heap_sort + /// @brief : Heap sort algorithm + /// @param first: iterator to the first element of the range + /// @param last : iterator to the next element of the last in the range + /// @param comp : object for to do the comparison between the elements + /// @remarks This algorithm is O(NLogN) + //------------------------------------------------------------------------ + heap_sort (Iter_t first, Iter_t last, Compare comp) + { + assert ((last - first) >= 0); + size_t nelem = last - first; + if (nelem < 2) return; + + //-------------------------------------------------------------------- + // Creating the initial heap + //-------------------------------------------------------------------- + make_heap (first, nelem, comp); + + //-------------------------------------------------------------------- + // Sort the heap + //-------------------------------------------------------------------- + size_t pos_father, pos_son; + Iter_t iter_father = first, iter_son = first; + + bool sw = false; + for (size_t i = 1; i < nelem; ++i) + { + std::swap (*first, *(first + (nelem - i))); + pos_father = 0; + pos_son = 1; + iter_father = first; + sw = true; + while (sw and pos_son < (nelem - i)) + { + // if the father have two sons must select the bigger + iter_son = first + pos_son; + if ((pos_son + 1) < (nelem - i) and + comp (*iter_son, *(iter_son + 1))) + { + ++pos_son; + ++iter_son; + }; + if ((sw = comp (*iter_father, *iter_son))) + std::swap (*iter_father, *iter_son); + pos_father = pos_son; + iter_father = iter_son; + pos_son = (pos_father << 1) + 1; + }; + }; + }; +}; // End class heap_sort +}; // end namespace heap_sort + +namespace bscu = boost::sort::common::util; + +template < class Iter_t, typename Compare = bscu::compare_iter < Iter_t > > +void heap_sort (Iter_t first, Iter_t last, Compare comp = Compare()) +{ + heap_detail::heap_sort<Iter_t, Compare> ( first, last, comp); +} +// +//**************************************************************************** +}; // End namespace sort +}; // End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/insert_sort/insert_sort.hpp b/boost/sort/insert_sort/insert_sort.hpp new file mode 100644 index 0000000000..d40302ad10 --- /dev/null +++ b/boost/sort/insert_sort/insert_sort.hpp @@ -0,0 +1,119 @@ +//---------------------------------------------------------------------------- +/// @file insert_sort.hpp +/// @brief Insertion Sort algorithm +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_INTROSORT_DETAIL_INSERT_SORT_HPP +#define __BOOST_SORT_INTROSORT_DETAIL_INSERT_SORT_HPP + +#include <functional> +#include <iterator> +#include <algorithm> +#include <utility> // std::swap +#include <boost/sort/common/util/traits.hpp> +#include <boost/sort/common/util/insert.hpp> + +namespace boost +{ +namespace sort +{ +using common::util::compare_iter; +using common::util::value_iter; +// +//----------------------------------------------------------------------------- +// function : insert_sort +/// @brief : Insertion sort algorithm +/// @param first: iterator to the first element of the range +/// @param last : iterator to the next element of the last in the range +/// @param comp : object for to do the comparison between the elements +/// @remarks This algorithm is O(N^2) +//----------------------------------------------------------------------------- +template < class Iter_t, typename Compare = compare_iter < Iter_t > > +static void insert_sort (Iter_t first, Iter_t last, + Compare comp = Compare()) +{ + //-------------------------------------------------------------------- + // DEFINITIONS + //-------------------------------------------------------------------- + typedef value_iter< Iter_t > value_t; + + if ((last - first) < 2) return; + + for (Iter_t it_examine = first + 1; it_examine != last; ++it_examine) + { + value_t Aux = std::move (*it_examine); + Iter_t it_insertion = it_examine; + + while (it_insertion != first and comp (Aux, *(it_insertion - 1))) + { + *it_insertion = std::move (*(it_insertion - 1)); + --it_insertion; + }; + *it_insertion = std::move (Aux); + }; +}; + +/* +// +//----------------------------------------------------------------------------- +// function : insert_partial_sort +/// @brief : Insertion sort of elements sorted +/// @param first: iterator to the first element of the range +/// @param mid : last pointer of the sorted data, and first pointer to the +/// elements to insert +/// @param last : iterator to the next element of the last in the range +/// @param comp : object for to do the comparison between the elements +/// @remarks This algorithm is O(N^2) +//----------------------------------------------------------------------------- +template < class Iter_t, typename Compare = compare_iter < Iter_t > > +void insert_partial_sort (Iter_t first, Iter_t mid, Iter_t last, + Compare comp = Compare()) +{ + //-------------------------------------------------------------------- + // DEFINITIONS + //-------------------------------------------------------------------- + typedef value_iter< Iter_t > value_t; + + if ( mid == last ) return ; + insert_sort ( mid, last, comp); + if (first == mid) return ; + + // creation of the vector of elements to insert and their position in the + // sorted part + std::vector<Iter_t> viter ; + std::vector<value_t> vdata ; + + for ( Iter_t alpha = mid ; alpha != last ; ++alpha) + vdata.push_back ( std::move ( *alpha)); + + Iter_t linf = first , lsup = mid ; + for ( uint32_t i= 0 ; i < vdata.size() ; ++i) + { Iter_t it1 = std::upper_bound ( linf, lsup , vdata[i], comp); + viter.push_back ( it1 ); + linf = it1 ; + }; + + // moving the elements + viter.push_back ( mid) ; + for ( uint32_t i = viter.size() -1 ; i!= 0 ; --i) + { Iter_t src = viter[i], limit = viter[i-1]; + Iter_t dest = src + ( i); + while ( src != limit) * (--dest) = std::move ( *(--src)); + *(viter[i-1] + (i -1)) = std::move (vdata[i-1]); + }; +} +*/ +// +//**************************************************************************** +}; // End namespace sort +}; // End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/parallel_stable_sort/parallel_stable_sort.hpp b/boost/sort/parallel_stable_sort/parallel_stable_sort.hpp new file mode 100644 index 0000000000..9df7dffd2a --- /dev/null +++ b/boost/sort/parallel_stable_sort/parallel_stable_sort.hpp @@ -0,0 +1,270 @@ +//---------------------------------------------------------------------------- +/// @file parallel_stable_sort.hpp +/// @brief This file contains the class parallel_stable_sort +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_PARALLEL_STABLE_SORT_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_PARALLEL_STABLE_SORT_HPP + +#include <boost/sort/sample_sort/sample_sort.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <functional> +#include <future> +#include <iterator> +#include <memory> +#include <type_traits> +#include <vector> + +namespace boost +{ +namespace sort +{ +namespace stable_detail +{ + +//--------------------------------------------------------------------------- +// USING SENTENCES +//--------------------------------------------------------------------------- +namespace bsc = boost::sort::common; +namespace bss = boost::sort::spin_detail; +using bsc::range; +using bsc::merge_half; +using boost::sort::sample_detail::sample_sort; +// +///--------------------------------------------------------------------------- +/// @struct parallel_stable_sort +/// @brief This a structure for to implement a parallel stable sort, exception +/// safe +//---------------------------------------------------------------------------- +template <class Iter_t, class Compare = compare_iter <Iter_t> > +struct parallel_stable_sort +{ + //------------------------------------------------------------------------- + // DEFINITIONS + //------------------------------------------------------------------------- + typedef value_iter<Iter_t> value_t; + + //------------------------------------------------------------------------- + // VARIABLES + //------------------------------------------------------------------------- + // Number of elements to sort + size_t nelem; + // Pointer to the auxiliary memory needed for the algorithm + value_t *ptr; + // Minimal number of elements for to be sorted in parallel mode + const size_t nelem_min = 1 << 16; + + //------------------------------------------------------------------------ + // F U N C T I O N S + //------------------------------------------------------------------------ + parallel_stable_sort (Iter_t first, Iter_t last) + : parallel_stable_sort (first, last, Compare(), + std::thread::hardware_concurrency()) { }; + + parallel_stable_sort (Iter_t first, Iter_t last, Compare cmp) + : parallel_stable_sort (first, last, cmp, + std::thread::hardware_concurrency()) { }; + + parallel_stable_sort (Iter_t first, Iter_t last, uint32_t num_thread) + : parallel_stable_sort (first, last, Compare(), num_thread) { }; + + parallel_stable_sort (Iter_t first, Iter_t last, Compare cmp, + uint32_t num_thread); + + // + //----------------------------------------------------------------------------- + // function : destroy_all + /// @brief The utility is to destroy the temporary buffer used in the + /// sorting process + //----------------------------------------------------------------------------- + void destroy_all() + { + if (ptr != nullptr) std::return_temporary_buffer(ptr); + }; + // + //----------------------------------------------------------------------------- + // function :~parallel_stable_sort + /// @brief destructor of the class. The utility is to destroy the temporary + /// buffer used in the sorting process + //----------------------------------------------------------------------------- + ~parallel_stable_sort() {destroy_all(); } ; +}; +// end struct parallel_stable_sort + +// +//############################################################################ +// ## +// ## +// N O N I N L I N E F U N C T I O N S ## +// ## +// ## +//############################################################################ +// +//----------------------------------------------------------------------------- +// function : parallel_stable_sort +/// @brief constructor of the class +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +/// @param nthread : Number of threads to use in the process. When this value +/// is lower than 2, the sorting is done with 1 thread +//----------------------------------------------------------------------------- +template <class Iter_t, class Compare> +parallel_stable_sort <Iter_t, Compare> +::parallel_stable_sort (Iter_t first, Iter_t last, Compare comp, + uint32_t nthread) : nelem(0), ptr(nullptr) +{ + range<Iter_t> range_initial(first, last); + assert(range_initial.valid()); + + nelem = range_initial.size(); + size_t nptr = (nelem + 1) >> 1; + + if (nelem < nelem_min or nthread < 2) + { + bss::spinsort<Iter_t, Compare> + (range_initial.first, range_initial.last, comp); + return; + }; + + //------------------- check if sort -------------------------------------- + bool sw = true; + for (Iter_t it1 = first, it2 = first + 1; + it2 != last and (sw = not comp(*it2, *it1)); it1 = it2++); + if (sw) return; + + //------------------- check if reverse sort --------------------------- + sw = true; + for (Iter_t it1 = first, it2 = first + 1; + it2 != last and (sw = comp(*it2, *it1)); it1 = it2++); + if (sw) + { + size_t nelem2 = nelem >> 1; + Iter_t it1 = first, it2 = last - 1; + for (size_t i = 0; i < nelem2; ++i) + std::swap(*(it1++), *(it2--)); + return; + }; + + ptr = std::get_temporary_buffer<value_t>(nptr).first; + if (ptr == nullptr) throw std::bad_alloc(); + + //--------------------------------------------------------------------- + // Parallel Process + //--------------------------------------------------------------------- + range<Iter_t> range_first(range_initial.first, range_initial.first + nptr); + + range<Iter_t> range_second(range_initial.first + nptr, range_initial.last); + + range<value_t *> range_buffer(ptr, ptr + nptr); + + try + { + sample_sort<Iter_t, Compare> + (range_initial.first, range_initial.first + nptr, + comp, nthread, range_buffer); + } catch (std::bad_alloc &) + { + destroy_all(); + throw std::bad_alloc(); + }; + + try + { + sample_sort<Iter_t, Compare> + (range_initial.first + nptr, + range_initial.last, comp, nthread, range_buffer); + } catch (std::bad_alloc &) + { + destroy_all(); + throw std::bad_alloc(); + }; + + range_buffer = move_forward(range_buffer, range_first); + range_initial = merge_half(range_initial, range_buffer, range_second, comp); +}; // end of constructor + +// +//**************************************************************************** +};// End namespace stable_detail +//**************************************************************************** +// + +//--------------------------------------------------------------------------- +// USING SENTENCES +//--------------------------------------------------------------------------- +namespace bsc = boost::sort::common; +namespace bscu = bsc::util; +namespace bss = boost::sort::spin_detail; +using bsc::range; +using bsc::merge_half; +// +//############################################################################ +// ## +// ## +// P A R A L L E L _ S T A B L E _ S O R T ## +// ## +// ## +//############################################################################ +// +//----------------------------------------------------------------------------- +// function : parallel_stable_sort +/// @brief : parallel stable sort algorithm. +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +//----------------------------------------------------------------------------- +template<class Iter_t> +void parallel_stable_sort(Iter_t first, Iter_t last) +{ + typedef bscu::compare_iter<Iter_t> Compare; + stable_detail::parallel_stable_sort<Iter_t, Compare>(first, last); +}; +// +//----------------------------------------------------------------------------- +// function : parallel_stable_sort +/// @brief parallel stable sort. +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param nthread : Number of threads to use in the process. When this value +/// is lower than 2, the sorting is done with 1 thread +//----------------------------------------------------------------------------- +template<class Iter_t> +void parallel_stable_sort(Iter_t first, Iter_t last, uint32_t nthread) +{ + typedef bscu::compare_iter<Iter_t> Compare; + stable_detail::parallel_stable_sort<Iter_t, Compare>(first, last, nthread); +}; +// +//----------------------------------------------------------------------------- +// function : parallel_stable_sort +/// @brief : parallel stable sort. +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +//----------------------------------------------------------------------------- +template <class Iter_t, class Compare, + bscu::enable_if_not_integral<Compare> * = nullptr> +void parallel_stable_sort(Iter_t first, Iter_t last, Compare comp) +{ + stable_detail::parallel_stable_sort<Iter_t, Compare>(first, last, comp); +}; +// +//**************************************************************************** +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/pdqsort/pdqsort.hpp b/boost/sort/pdqsort/pdqsort.hpp new file mode 100644 index 0000000000..dc81d87057 --- /dev/null +++ b/boost/sort/pdqsort/pdqsort.hpp @@ -0,0 +1,632 @@ +// Pattern-defeating quicksort + +// Copyright Orson Peters 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort/ for library home page. + + +#ifndef BOOST_SORT_PDQSORT_HPP +#define BOOST_SORT_PDQSORT_HPP + +#include <algorithm> +#include <cstddef> +#include <functional> +#include <iterator> +#include <utility> +#include <boost/type_traits.hpp> + +#if __cplusplus >= 201103L + #include <cstdint> + #define BOOST_PDQSORT_PREFER_MOVE(x) std::move(x) +#else + #define BOOST_PDQSORT_PREFER_MOVE(x) (x) +#endif + +namespace boost { +namespace sort { + +namespace pdqsort_detail { + enum { + // Partitions below this size are sorted using insertion sort. + insertion_sort_threshold = 24, + + // Partitions above this size use Tukey's ninther to select the pivot. + ninther_threshold = 128, + + // When we detect an already sorted partition, attempt an insertion sort that allows this + // amount of element moves before giving up. + partial_insertion_sort_limit = 8, + + // Must be multiple of 8 due to loop unrolling, and < 256 to fit in unsigned char. + block_size = 64, + + // Cacheline size, assumes power of two. + cacheline_size = 64 + }; + + template<class T> struct is_default_compare : boost::false_type { }; + template<class T> struct is_default_compare<std::less<T> > : boost::true_type { }; + template<class T> struct is_default_compare<std::greater<T> > : boost::true_type { }; + + // Returns floor(log2(n)), assumes n > 0. + template<class T> + inline int log2(T n) { + int log = 0; + while (n >>= 1) ++log; + return log; + } + + // Sorts [begin, end) using insertion sort with the given comparison function. + template<class Iter, class Compare> + inline void insertion_sort(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits<Iter>::value_type T; + if (begin == end) return; + + for (Iter cur = begin + 1; cur != end; ++cur) { + Iter sift = cur; + Iter sift_1 = cur - 1; + + // Compare first so we can avoid 2 moves for an element already positioned correctly. + if (comp(*sift, *sift_1)) { + T tmp = BOOST_PDQSORT_PREFER_MOVE(*sift); + + do { *sift-- = BOOST_PDQSORT_PREFER_MOVE(*sift_1); } + while (sift != begin && comp(tmp, *--sift_1)); + + *sift = BOOST_PDQSORT_PREFER_MOVE(tmp); + } + } + } + + // Sorts [begin, end) using insertion sort with the given comparison function. Assumes + // *(begin - 1) is an element smaller than or equal to any element in [begin, end). + template<class Iter, class Compare> + inline void unguarded_insertion_sort(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits<Iter>::value_type T; + if (begin == end) return; + + for (Iter cur = begin + 1; cur != end; ++cur) { + Iter sift = cur; + Iter sift_1 = cur - 1; + + // Compare first so we can avoid 2 moves for an element already positioned correctly. + if (comp(*sift, *sift_1)) { + T tmp = BOOST_PDQSORT_PREFER_MOVE(*sift); + + do { *sift-- = BOOST_PDQSORT_PREFER_MOVE(*sift_1); } + while (comp(tmp, *--sift_1)); + + *sift = BOOST_PDQSORT_PREFER_MOVE(tmp); + } + } + } + + // Attempts to use insertion sort on [begin, end). Will return false if more than + // partial_insertion_sort_limit elements were moved, and abort sorting. Otherwise it will + // successfully sort and return true. + template<class Iter, class Compare> + inline bool partial_insertion_sort(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits<Iter>::value_type T; + if (begin == end) return true; + + int limit = 0; + for (Iter cur = begin + 1; cur != end; ++cur) { + if (limit > partial_insertion_sort_limit) return false; + + Iter sift = cur; + Iter sift_1 = cur - 1; + + // Compare first so we can avoid 2 moves for an element already positioned correctly. + if (comp(*sift, *sift_1)) { + T tmp = BOOST_PDQSORT_PREFER_MOVE(*sift); + + do { *sift-- = BOOST_PDQSORT_PREFER_MOVE(*sift_1); } + while (sift != begin && comp(tmp, *--sift_1)); + + *sift = BOOST_PDQSORT_PREFER_MOVE(tmp); + limit += cur - sift; + } + } + + return true; + } + + template<class Iter, class Compare> + inline void sort2(Iter a, Iter b, Compare comp) { + if (comp(*b, *a)) std::iter_swap(a, b); + } + + // Sorts the elements *a, *b and *c using comparison function comp. + template<class Iter, class Compare> + inline void sort3(Iter a, Iter b, Iter c, Compare comp) { + sort2(a, b, comp); + sort2(b, c, comp); + sort2(a, b, comp); + } + + template<class T> + inline T* align_cacheline(T* p) { +#if defined(UINTPTR_MAX) && __cplusplus >= 201103L + std::uintptr_t ip = reinterpret_cast<std::uintptr_t>(p); +#else + std::size_t ip = reinterpret_cast<std::size_t>(p); +#endif + ip = (ip + cacheline_size - 1) & -cacheline_size; + return reinterpret_cast<T*>(ip); + } + + template<class Iter> + inline void swap_offsets(Iter first, Iter last, + unsigned char* offsets_l, unsigned char* offsets_r, + int num, bool use_swaps) { + typedef typename std::iterator_traits<Iter>::value_type T; + if (use_swaps) { + // This case is needed for the descending distribution, where we need + // to have proper swapping for pdqsort to remain O(n). + for (int i = 0; i < num; ++i) { + std::iter_swap(first + offsets_l[i], last - offsets_r[i]); + } + } else if (num > 0) { + Iter l = first + offsets_l[0]; Iter r = last - offsets_r[0]; + T tmp(BOOST_PDQSORT_PREFER_MOVE(*l)); *l = BOOST_PDQSORT_PREFER_MOVE(*r); + for (int i = 1; i < num; ++i) { + l = first + offsets_l[i]; *r = BOOST_PDQSORT_PREFER_MOVE(*l); + r = last - offsets_r[i]; *l = BOOST_PDQSORT_PREFER_MOVE(*r); + } + *r = BOOST_PDQSORT_PREFER_MOVE(tmp); + } + } + + // Partitions [begin, end) around pivot *begin using comparison function comp. Elements equal + // to the pivot are put in the right-hand partition. Returns the position of the pivot after + // partitioning and whether the passed sequence already was correctly partitioned. Assumes the + // pivot is a median of at least 3 elements and that [begin, end) is at least + // insertion_sort_threshold long. Uses branchless partitioning. + template<class Iter, class Compare> + inline std::pair<Iter, bool> partition_right_branchless(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits<Iter>::value_type T; + + // Move pivot into local for speed. + T pivot(BOOST_PDQSORT_PREFER_MOVE(*begin)); + Iter first = begin; + Iter last = end; + + // Find the first element greater than or equal than the pivot (the median of 3 guarantees + // this exists). + while (comp(*++first, pivot)); + + // Find the first element strictly smaller than the pivot. We have to guard this search if + // there was no element before *first. + if (first - 1 == begin) while (first < last && !comp(*--last, pivot)); + else while ( !comp(*--last, pivot)); + + // If the first pair of elements that should be swapped to partition are the same element, + // the passed in sequence already was correctly partitioned. + bool already_partitioned = first >= last; + if (!already_partitioned) { + std::iter_swap(first, last); + ++first; + } + + // The following branchless partitioning is derived from "BlockQuicksort: How Branch + // Mispredictions don't affect Quicksort" by Stefan Edelkamp and Armin Weiss. + unsigned char offsets_l_storage[block_size + cacheline_size]; + unsigned char offsets_r_storage[block_size + cacheline_size]; + unsigned char* offsets_l = align_cacheline(offsets_l_storage); + unsigned char* offsets_r = align_cacheline(offsets_r_storage); + int num_l, num_r, start_l, start_r; + num_l = num_r = start_l = start_r = 0; + + while (last - first > 2 * block_size) { + // Fill up offset blocks with elements that are on the wrong side. + if (num_l == 0) { + start_l = 0; + Iter it = first; + for (unsigned char i = 0; i < block_size;) { + offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it; + offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it; + offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it; + offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it; + offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it; + offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it; + offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it; + offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it; + } + } + if (num_r == 0) { + start_r = 0; + Iter it = last; + for (unsigned char i = 0; i < block_size;) { + offsets_r[num_r] = ++i; num_r += comp(*--it, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--it, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--it, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--it, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--it, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--it, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--it, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--it, pivot); + } + } + + // Swap elements and update block sizes and first/last boundaries. + int num = (std::min)(num_l, num_r); + swap_offsets(first, last, offsets_l + start_l, offsets_r + start_r, + num, num_l == num_r); + num_l -= num; num_r -= num; + start_l += num; start_r += num; + if (num_l == 0) first += block_size; + if (num_r == 0) last -= block_size; + } + + int l_size = 0, r_size = 0; + int unknown_left = (last - first) - ((num_r || num_l) ? block_size : 0); + if (num_r) { + // Handle leftover block by assigning the unknown elements to the other block. + l_size = unknown_left; + r_size = block_size; + } else if (num_l) { + l_size = block_size; + r_size = unknown_left; + } else { + // No leftover block, split the unknown elements in two blocks. + l_size = unknown_left/2; + r_size = unknown_left - l_size; + } + + // Fill offset buffers if needed. + if (unknown_left && !num_l) { + start_l = 0; + Iter it = first; + for (unsigned char i = 0; i < l_size;) { + offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it; + } + } + if (unknown_left && !num_r) { + start_r = 0; + Iter it = last; + for (unsigned char i = 0; i < r_size;) { + offsets_r[num_r] = ++i; num_r += comp(*--it, pivot); + } + } + + int num = (std::min)(num_l, num_r); + swap_offsets(first, last, offsets_l + start_l, offsets_r + start_r, num, num_l == num_r); + num_l -= num; num_r -= num; + start_l += num; start_r += num; + if (num_l == 0) first += l_size; + if (num_r == 0) last -= r_size; + + // We have now fully identified [first, last)'s proper position. Swap the last elements. + if (num_l) { + offsets_l += start_l; + while (num_l--) std::iter_swap(first + offsets_l[num_l], --last); + first = last; + } + if (num_r) { + offsets_r += start_r; + while (num_r--) std::iter_swap(last - offsets_r[num_r], first), ++first; + last = first; + } + + // Put the pivot in the right place. + Iter pivot_pos = first - 1; + *begin = BOOST_PDQSORT_PREFER_MOVE(*pivot_pos); + *pivot_pos = BOOST_PDQSORT_PREFER_MOVE(pivot); + + return std::make_pair(pivot_pos, already_partitioned); + } + + // Partitions [begin, end) around pivot *begin using comparison function comp. Elements equal + // to the pivot are put in the right-hand partition. Returns the position of the pivot after + // partitioning and whether the passed sequence already was correctly partitioned. Assumes the + // pivot is a median of at least 3 elements and that [begin, end) is at least + // insertion_sort_threshold long. + template<class Iter, class Compare> + inline std::pair<Iter, bool> partition_right(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits<Iter>::value_type T; + + // Move pivot into local for speed. + T pivot(BOOST_PDQSORT_PREFER_MOVE(*begin)); + + Iter first = begin; + Iter last = end; + + // Find the first element greater than or equal than the pivot (the median of 3 guarantees + // this exists). + while (comp(*++first, pivot)); + + // Find the first element strictly smaller than the pivot. We have to guard this search if + // there was no element before *first. + if (first - 1 == begin) while (first < last && !comp(*--last, pivot)); + else while ( !comp(*--last, pivot)); + + // If the first pair of elements that should be swapped to partition are the same element, + // the passed in sequence already was correctly partitioned. + bool already_partitioned = first >= last; + + // Keep swapping pairs of elements that are on the wrong side of the pivot. Previously + // swapped pairs guard the searches, which is why the first iteration is special-cased + // above. + while (first < last) { + std::iter_swap(first, last); + while (comp(*++first, pivot)); + while (!comp(*--last, pivot)); + } + + // Put the pivot in the right place. + Iter pivot_pos = first - 1; + *begin = BOOST_PDQSORT_PREFER_MOVE(*pivot_pos); + *pivot_pos = BOOST_PDQSORT_PREFER_MOVE(pivot); + + return std::make_pair(pivot_pos, already_partitioned); + } + + // Similar function to the one above, except elements equal to the pivot are put to the left of + // the pivot and it doesn't check or return if the passed sequence already was partitioned. + // Since this is rarely used (the many equal case), and in that case pdqsort already has O(n) + // performance, no block quicksort is applied here for simplicity. + template<class Iter, class Compare> + inline Iter partition_left(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits<Iter>::value_type T; + + T pivot(BOOST_PDQSORT_PREFER_MOVE(*begin)); + Iter first = begin; + Iter last = end; + + while (comp(pivot, *--last)); + + if (last + 1 == end) while (first < last && !comp(pivot, *++first)); + else while ( !comp(pivot, *++first)); + + while (first < last) { + std::iter_swap(first, last); + while (comp(pivot, *--last)); + while (!comp(pivot, *++first)); + } + + Iter pivot_pos = last; + *begin = BOOST_PDQSORT_PREFER_MOVE(*pivot_pos); + *pivot_pos = BOOST_PDQSORT_PREFER_MOVE(pivot); + + return pivot_pos; + } + + + template<class Iter, class Compare, bool Branchless> + inline void pdqsort_loop(Iter begin, Iter end, Compare comp, int bad_allowed, bool leftmost = true) { + typedef typename std::iterator_traits<Iter>::difference_type diff_t; + + // Use a while loop for tail recursion elimination. + while (true) { + diff_t size = end - begin; + + // Insertion sort is faster for small arrays. + if (size < insertion_sort_threshold) { + if (leftmost) insertion_sort(begin, end, comp); + else unguarded_insertion_sort(begin, end, comp); + return; + } + + // Choose pivot as median of 3 or pseudomedian of 9. + diff_t s2 = size / 2; + if (size > ninther_threshold) { + sort3(begin, begin + s2, end - 1, comp); + sort3(begin + 1, begin + (s2 - 1), end - 2, comp); + sort3(begin + 2, begin + (s2 + 1), end - 3, comp); + sort3(begin + (s2 - 1), begin + s2, begin + (s2 + 1), comp); + std::iter_swap(begin, begin + s2); + } else sort3(begin + s2, begin, end - 1, comp); + + // If *(begin - 1) is the end of the right partition of a previous partition operation + // there is no element in [begin, end) that is smaller than *(begin - 1). Then if our + // pivot compares equal to *(begin - 1) we change strategy, putting equal elements in + // the left partition, greater elements in the right partition. We do not have to + // recurse on the left partition, since it's sorted (all equal). + if (!leftmost && !comp(*(begin - 1), *begin)) { + begin = partition_left(begin, end, comp) + 1; + continue; + } + + // Partition and get results. + std::pair<Iter, bool> part_result = + Branchless ? partition_right_branchless(begin, end, comp) + : partition_right(begin, end, comp); + Iter pivot_pos = part_result.first; + bool already_partitioned = part_result.second; + + // Check for a highly unbalanced partition. + diff_t l_size = pivot_pos - begin; + diff_t r_size = end - (pivot_pos + 1); + bool highly_unbalanced = l_size < size / 8 || r_size < size / 8; + + // If we got a highly unbalanced partition we shuffle elements to break many patterns. + if (highly_unbalanced) { + // If we had too many bad partitions, switch to heapsort to guarantee O(n log n). + if (--bad_allowed == 0) { + std::make_heap(begin, end, comp); + std::sort_heap(begin, end, comp); + return; + } + + if (l_size >= insertion_sort_threshold) { + std::iter_swap(begin, begin + l_size / 4); + std::iter_swap(pivot_pos - 1, pivot_pos - l_size / 4); + + if (l_size > ninther_threshold) { + std::iter_swap(begin + 1, begin + (l_size / 4 + 1)); + std::iter_swap(begin + 2, begin + (l_size / 4 + 2)); + std::iter_swap(pivot_pos - 2, pivot_pos - (l_size / 4 + 1)); + std::iter_swap(pivot_pos - 3, pivot_pos - (l_size / 4 + 2)); + } + } + + if (r_size >= insertion_sort_threshold) { + std::iter_swap(pivot_pos + 1, pivot_pos + (1 + r_size / 4)); + std::iter_swap(end - 1, end - r_size / 4); + + if (r_size > ninther_threshold) { + std::iter_swap(pivot_pos + 2, pivot_pos + (2 + r_size / 4)); + std::iter_swap(pivot_pos + 3, pivot_pos + (3 + r_size / 4)); + std::iter_swap(end - 2, end - (1 + r_size / 4)); + std::iter_swap(end - 3, end - (2 + r_size / 4)); + } + } + } else { + // If we were decently balanced and we tried to sort an already partitioned + // sequence try to use insertion sort. + if (already_partitioned && partial_insertion_sort(begin, pivot_pos, comp) + && partial_insertion_sort(pivot_pos + 1, end, comp)) return; + } + + // Sort the left partition first using recursion and do tail recursion elimination for + // the right-hand partition. + pdqsort_loop<Iter, Compare, Branchless>(begin, pivot_pos, comp, bad_allowed, leftmost); + begin = pivot_pos + 1; + leftmost = false; + } + } +} + + +/*! \brief Generic sort algorithm using random access iterators and a user-defined comparison operator. + + \details @c pdqsort is a fast generic sorting algorithm that is similar in concept to introsort +but runs faster on certain patterns. @c pdqsort is in-place, unstable, deterministic, has a worst +case runtime of <em>O(N * lg(N))</em> and a best case of <em>O(N)</em>. Even without patterns, the +quicksort has been very efficiently implemented, and @c pdqsort runs 1-5% faster than GCC 6.2's +@c std::sort. If the type being sorted is @c std::is_arithmetic and Compare is @c std::less or +@c std::greater this function will automatically use @c pdqsort_branchless for far greater speedups. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveAssignable">MoveAssignable</a> + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveConstructible">MoveConstructible</a> + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps + (or moves), functors, or any operations on iterators throw. + \warning Invalid arguments cause undefined behaviour. + \warning Throwing an exception may cause data loss. +*/ +template<class Iter, class Compare> +inline void pdqsort(Iter first, Iter last, Compare comp) { + if (first == last) return; + pdqsort_detail::pdqsort_loop<Iter, Compare, + pdqsort_detail::is_default_compare<typename boost::decay<Compare>::type>::value && + boost::is_arithmetic<typename std::iterator_traits<Iter>::value_type>::value>( + first, last, comp, pdqsort_detail::log2(last - first)); +} + + +/*! \brief Generic sort algorithm using random access iterators and a user-defined comparison operator. + + \details @c pdqsort_branchless is a fast generic sorting algorithm that is similar in concept to +introsort but runs faster on certain patterns. @c pdqsort_branchless is in-place, unstable, +deterministic, has a worst case runtime of <em>O(N * lg(N))</em> and a best case of <em>O(N)</em>. +Even without patterns, the quicksort has been very efficiently implemented with block based +partitioning, and @c pdqsort_branchless runs 80-90% faster than GCC 6.2's @c std::sort when sorting +small data such as integers. However, this speedup is gained by totally bypassing the branch +predictor, if your comparison operator or iterator contains branches you will most likely see little +gain or a small loss in performance. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveAssignable">MoveAssignable</a> + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveConstructible">MoveConstructible</a> + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps + (or moves), functors, or any operations on iterators throw. + \warning Invalid arguments cause undefined behaviour. + \warning Throwing an exception may cause data loss. +*/ +template<class Iter, class Compare> +inline void pdqsort_branchless(Iter first, Iter last, Compare comp) { + if (first == last) return; + pdqsort_detail::pdqsort_loop<Iter, Compare, true>( + first, last, comp, pdqsort_detail::log2(last - first)); +} + + +/*! \brief Generic sort algorithm using random access iterators. + + \details @c pdqsort is a fast generic sorting algorithm that is similar in concept to introsort +but runs faster on certain patterns. @c pdqsort is in-place, unstable, deterministic, has a worst +case runtime of <em>O(N * lg(N))</em> and a best case of <em>O(N)</em>. Even without patterns, the +quicksort partitioning has been very efficiently implemented, and @c pdqsort runs 80-90% faster than +GCC 6.2's @c std::sort. If the type being sorted is @c std::is_arithmetic this function will +automatically use @c pdqsort_branchless. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveAssignable">MoveAssignable</a> + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveConstructible">MoveConstructible</a> + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps + (or moves), functors, or any operations on iterators throw. + \warning Invalid arguments cause undefined behaviour. + \warning Throwing an exception may cause data loss. +*/ +template<class Iter> +inline void pdqsort(Iter first, Iter last) { + typedef typename std::iterator_traits<Iter>::value_type T; + pdqsort(first, last, std::less<T>()); +} + + +/*! \brief Generic sort algorithm using random access iterators. + + \details @c pdqsort_branchless is a fast generic sorting algorithm that is similar in concept to +introsort but runs faster on certain patterns. @c pdqsort_branchless is in-place, unstable, +deterministic, has a worst case runtime of <em>O(N * lg(N))</em> and a best case of <em>O(N)</em>. +Even without patterns, the quicksort has been very efficiently implemented with block based +partitioning, and @c pdqsort_branchless runs 80-90% faster than GCC 6.2's @c std::sort when sorting +small data such as integers. However, this speedup is gained by totally bypassing the branch +predictor, if your comparison operator or iterator contains branches you will most likely see little +gain or a small loss in performance. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveAssignable">MoveAssignable</a> + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveConstructible">MoveConstructible</a> + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps + (or moves), functors, or any operations on iterators throw. + \warning Invalid arguments cause undefined behaviour. + \warning Throwing an exception may cause data loss. +*/ +template<class Iter> +inline void pdqsort_branchless(Iter first, Iter last) { + typedef typename std::iterator_traits<Iter>::value_type T; + pdqsort_branchless(first, last, std::less<T>()); +} + +} +} + +#undef BOOST_PDQSORT_PREFER_MOVE + +#endif diff --git a/boost/sort/sample_sort/sample_sort.hpp b/boost/sort/sample_sort/sample_sort.hpp new file mode 100644 index 0000000000..ded1781cfa --- /dev/null +++ b/boost/sort/sample_sort/sample_sort.hpp @@ -0,0 +1,560 @@ +//---------------------------------------------------------------------------- +/// @file sample_sort.hpp +/// @brief contains the class sample_sort +/// +/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_DETAIL_SAMPLE_SORT_HPP +#define __BOOST_SORT_PARALLEL_DETAIL_SAMPLE_SORT_HPP + +#include <functional> +#include <future> +#include <iterator> +#include <memory> +#include <type_traits> +#include <vector> + +#include <algorithm> +#include <boost/sort/spinsort/spinsort.hpp> +#include <boost/sort/common/indirect.hpp> +#include <boost/sort/common/util/atomic.hpp> +#include <boost/sort/common/merge_four.hpp> +#include <boost/sort/common/merge_vector.hpp> +#include <boost/sort/common/range.hpp> + +namespace boost +{ +namespace sort +{ +namespace sample_detail +{ +//--------------------------------------------------------------------------- +// USING SENTENCES +//--------------------------------------------------------------------------- +namespace bsc = boost::sort::common; +namespace bss = boost::sort::spin_detail; +namespace bscu = boost::sort::common::util; +using bsc::range; +using bscu::atomic_add; +using bsc::merge_vector4; +using bsc::uninit_merge_level4; +using bsc::less_ptr_no_null; + +// +///--------------------------------------------------------------------------- +/// @struct sample_sort +/// @brief This a structure for to implement a sample sort, exception +/// safe +/// @tparam +/// @remarks +//---------------------------------------------------------------------------- +template<class Iter_t, class Compare> +struct sample_sort +{ + //------------------------------------------------------------------------ + // DEFINITIONS + //------------------------------------------------------------------------ + typedef value_iter<Iter_t> value_t; + typedef range<Iter_t> range_it; + typedef range<value_t *> range_buf; + typedef sample_sort<Iter_t, Compare> this_t; + + //------------------------------------------------------------------------ + // VARIABLES AND CONSTANTS + //------------------------------------------------------------------------ + // minimun numbers of elements for to be sortd in parallel mode + static const uint32_t thread_min = (1 << 16); + + // Number of threads to use in the algorithm + // Number of intervals for to do the internal division of the data + uint32_t nthread, ninterval; + + // Bool variables indicating if the auxiliary memory is constructed + // and indicating in the auxiliary memory had been obtained inside the + /// algorithm or had been received as a parameter + bool construct = false, owner = false; + + // Comparison object for to compare two elements + Compare comp; + + // Range with all the elements to sort + range_it global_range; + + // range with the auxiliary memory + range_buf global_buf; + + // vector of futures + std::vector<std::future<void>> vfuture; + + // vector of vectors which contains the ranges to merge obtained in the + // subdivision + std::vector<std::vector<range_it>> vv_range_it; + + // each vector of ranges of the vv_range_it, need their corresponding buffer + // for to do the merge + std::vector<std::vector<range_buf>> vv_range_buf; + + // Initial vector of ranges + std::vector<range_it> vrange_it_ini; + + // Initial vector of buffers + std::vector<range_buf> vrange_buf_ini; + + // atomic counter for to know when are finished the function_t created + // inside a function + std::atomic<uint32_t> njob; + + // Indicate if an error in the algorithm for to undo all + bool error; + + //------------------------------------------------------------------------ + // FUNCTIONS OF THE STRUCT + //------------------------------------------------------------------------ + void initial_configuration(void); + + sample_sort (Iter_t first, Iter_t last, Compare cmp, uint32_t num_thread, + value_t *paux, size_t naux); + + sample_sort(Iter_t first, Iter_t last) + : sample_sort (first, last, Compare(), std::thread::hardware_concurrency(), + nullptr, 0) { }; + + sample_sort(Iter_t first, Iter_t last, Compare cmp) + : sample_sort(first, last, cmp, std::thread::hardware_concurrency(), + nullptr, 0) { }; + + sample_sort(Iter_t first, Iter_t last, uint32_t num_thread) + : sample_sort(first, last, Compare(), num_thread, nullptr, 0) { }; + + sample_sort(Iter_t first, Iter_t last, Compare cmp, uint32_t num_thread) + : sample_sort(first, last, cmp, num_thread, nullptr, 0) { }; + + sample_sort(Iter_t first, Iter_t last, Compare cmp, uint32_t num_thread, + range_buf range_buf_initial) + : sample_sort(first, last, cmp, num_thread, + range_buf_initial.first, range_buf_initial.size()) { }; + + void destroy_all(void); + // + //----------------------------------------------------------------------------- + // function :~sample_sort + /// @brief destructor of the class. The utility is to destroy the temporary + /// buffer used in the sorting process + //----------------------------------------------------------------------------- + ~sample_sort(void) { destroy_all(); }; + // + //----------------------------------------------------------------------- + // function : execute first + /// @brief this a function to assign to each thread in the first merge + //----------------------------------------------------------------------- + void execute_first(void) + { + uint32_t job = 0; + while ((job = atomic_add(njob, 1)) < ninterval) + { + uninit_merge_level4(vrange_buf_ini[job], vv_range_it[job], + vv_range_buf[job], comp); + }; + }; + // + //----------------------------------------------------------------------- + // function : execute + /// @brief this is a function to assignt each thread the final merge + //----------------------------------------------------------------------- + void execute(void) + { + uint32_t job = 0; + while ((job = atomic_add(njob, 1)) < ninterval) + { + merge_vector4(vrange_buf_ini[job], vrange_it_ini[job], + vv_range_buf[job], vv_range_it[job], comp); + }; + }; + // + //----------------------------------------------------------------------- + // function : first merge + /// @brief Implement the merge of the initially sparse ranges + //----------------------------------------------------------------------- + void first_merge(void) + { //---------------------------------- begin -------------------------- + njob = 0; + + for (uint32_t i = 0; i < nthread; ++i) + { + vfuture[i] = std::async(std::launch::async, &this_t::execute_first, + this); + }; + for (uint32_t i = 0; i < nthread; ++i) + vfuture[i].get(); + }; + // + //----------------------------------------------------------------------- + // function : final merge + /// @brief Implement the final merge of the ranges + //----------------------------------------------------------------------- + void final_merge(void) + { //---------------------------------- begin -------------------------- + njob = 0; + + for (uint32_t i = 0; i < nthread; ++i) + { + vfuture[i] = std::async(std::launch::async, &this_t::execute, this); + }; + for (uint32_t i = 0; i < nthread; ++i) + vfuture[i].get(); + }; + //---------------------------------------------------------------------------- +}; +// End class sample_sort +//---------------------------------------------------------------------------- +// +//############################################################################ +// ## +// N O N I N L I N E F U N C T I O N S ## +// ## +// ## +//############################################################################ +// +//----------------------------------------------------------------------------- +// function : sample_sort +/// @brief constructor of the class +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param cmp : object for to compare two elements pointed by Iter_t iterators +/// @param num_thread : Number of threads to use in the process. When this value +/// is lower than 2, the sorting is done with 1 thread +/// @param paux : pointer to the auxiliary memory. If nullptr, the memory is +/// created inside the class +/// @param naux : number of elements of the memory pointed by paux +//----------------------------------------------------------------------------- +template<class Iter_t, typename Compare> +sample_sort<Iter_t, Compare> +::sample_sort (Iter_t first, Iter_t last, Compare cmp, uint32_t num_thread, + value_t *paux, size_t naux) +: nthread(num_thread), owner(false), comp(cmp), global_range(first, last), + global_buf(nullptr, nullptr), error(false) +{ + assert((last - first) >= 0); + size_t nelem = size_t(last - first); + construct = false; + njob = 0; + vfuture.resize(nthread); + + // Adjust when have many threads and only a few elements + while (nelem > thread_min and (nthread * nthread) > (nelem >> 3)) + { + nthread /= 2; + }; + ninterval = (nthread << 3); + + if (nthread < 2 or nelem <= (thread_min)) + { + bss::spinsort<Iter_t, Compare>(first, last, comp); + return; + }; + + //------------------- check if sort -------------------------------------- + bool sw = true; + for (Iter_t it1 = first, it2 = first + 1; + it2 != last and (sw = not comp(*it2, *it1)); it1 = it2++); + if (sw) return; + + //------------------- check if reverse sort --------------------------- + sw = true; + for (Iter_t it1 = first, it2 = first + 1; + it2 != last and (sw = comp(*it2, *it1)); it1 = it2++); + if (sw) + { + size_t nelem2 = nelem >> 1; + Iter_t it1 = first, it2 = last - 1; + for (size_t i = 0; i < nelem2; ++i) + std::swap(*(it1++), *(it2--)); + return; + }; + + if (paux != nullptr) + { + assert(naux != 0); + global_buf.first = paux; + global_buf.last = paux + naux; + owner = false; + } + else + { + value_t *ptr = std::get_temporary_buffer<value_t>(nelem).first; + if (ptr == nullptr) throw std::bad_alloc(); + owner = true; + global_buf = range_buf(ptr, ptr + nelem); + }; + //------------------------------------------------------------------------ + // PROCESS + //------------------------------------------------------------------------ + try + { + initial_configuration(); + } catch (std::bad_alloc &) + { + error = true; + }; + if (not error) + { + first_merge(); + construct = true; + final_merge(); + }; + if (error) + { + destroy_all(); + throw std::bad_alloc(); + }; +} +; +// +//----------------------------------------------------------------------------- +// function : destroy_all +/// @brief destructor of the class. The utility is to destroy the temporary +/// buffer used in the sorting process +//----------------------------------------------------------------------------- +template<class Iter_t, typename Compare> +void sample_sort<Iter_t, Compare>::destroy_all(void) +{ + if (construct) + { + destroy(global_buf); + construct = false; + } + if (global_buf.first != nullptr and owner) + std::return_temporary_buffer(global_buf.first); +} +// +//----------------------------------------------------------------------------- +// function : initial_configuration +/// @brief Create the internal data structures, and obtain the inital set of +/// ranges to merge +//----------------------------------------------------------------------------- +template<class Iter_t, typename Compare> +void sample_sort<Iter_t, Compare>::initial_configuration(void) +{ + std::vector<range_it> vmem_thread; + std::vector<range_buf> vbuf_thread; + size_t nelem = global_range.size(); + + //------------------------------------------------------------------------ + size_t cupo = nelem / nthread; + Iter_t it_first = global_range.first; + value_t *buf_first = global_buf.first; + vmem_thread.reserve(nthread + 1); + vbuf_thread.reserve(nthread + 1); + + for (uint32_t i = 0; i < (nthread - 1); ++i, it_first += cupo, buf_first += + cupo) + { + vmem_thread.emplace_back(it_first, it_first + cupo); + vbuf_thread.emplace_back(buf_first, buf_first + cupo); + }; + + vmem_thread.emplace_back(it_first, global_range.last); + vbuf_thread.emplace_back(buf_first, global_buf.last); + + //------------------------------------------------------------------------ + // Sorting of the ranges + //------------------------------------------------------------------------ + std::vector<std::future<void>> vfuture(nthread); + + for (uint32_t i = 0; i < nthread; ++i) + { + auto func = [=]() + { + bss::spinsort<Iter_t, Compare> (vmem_thread[i].first, + vmem_thread[i].last, comp, + vbuf_thread[i]); + }; + vfuture[i] = std::async(std::launch::async, func); + }; + + for (uint32_t i = 0; i < nthread; ++i) + vfuture[i].get(); + + //------------------------------------------------------------------------ + // Obtain the vector of milestones + //------------------------------------------------------------------------ + std::vector<Iter_t> vsample; + vsample.reserve(nthread * (ninterval - 1)); + + for (uint32_t i = 0; i < nthread; ++i) + { + size_t distance = vmem_thread[i].size() / ninterval; + for (size_t j = 1, pos = distance; j < ninterval; ++j, pos += distance) + { + vsample.push_back(vmem_thread[i].first + pos); + }; + }; + typedef less_ptr_no_null<Iter_t, Compare> compare_ptr; + typedef typename std::vector<Iter_t>::iterator it_to_it; + + bss::spinsort<it_to_it, compare_ptr>(vsample.begin(), vsample.end(), + compare_ptr(comp)); + + //------------------------------------------------------------------------ + // Create the final milestone vector + //------------------------------------------------------------------------ + std::vector<Iter_t> vmilestone; + vmilestone.reserve(ninterval); + + for (uint32_t pos = nthread >> 1; pos < vsample.size(); pos += nthread) + { + vmilestone.push_back(vsample[pos]); + }; + + //------------------------------------------------------------------------ + // Creation of the first vector of ranges + //------------------------------------------------------------------------ + std::vector<std::vector<range<Iter_t>>>vv_range_first (nthread); + + for (uint32_t i = 0; i < nthread; ++i) + { + Iter_t itaux = vmem_thread[i].first; + + for (uint32_t k = 0; k < (ninterval - 1); ++k) + { + Iter_t it2 = std::upper_bound(itaux, vmem_thread[i].last, + *vmilestone[k], comp); + + vv_range_first[i].emplace_back(itaux, it2); + itaux = it2; + }; + vv_range_first[i].emplace_back(itaux, vmem_thread[i].last); + }; + + //------------------------------------------------------------------------ + // Copy in buffer and creation of the final matrix of ranges + //------------------------------------------------------------------------ + vv_range_it.resize(ninterval); + vv_range_buf.resize(ninterval); + vrange_it_ini.reserve(ninterval); + vrange_buf_ini.reserve(ninterval); + + for (uint32_t i = 0; i < ninterval; ++i) + { + vv_range_it[i].reserve(nthread); + vv_range_buf[i].reserve(nthread); + }; + + Iter_t it = global_range.first; + value_t *it_buf = global_buf.first; + + for (uint32_t k = 0; k < ninterval; ++k) + { + size_t nelem_interval = 0; + + for (uint32_t i = 0; i < nthread; ++i) + { + size_t nelem_range = vv_range_first[i][k].size(); + if (nelem_range != 0) + { + vv_range_it[k].push_back(vv_range_first[i][k]); + }; + nelem_interval += nelem_range; + }; + + vrange_it_ini.emplace_back(it, it + nelem_interval); + vrange_buf_ini.emplace_back(it_buf, it_buf + nelem_interval); + + it += nelem_interval; + it_buf += nelem_interval; + }; +} +; +// +//**************************************************************************** +} +; +// End namespace sample_detail +//**************************************************************************** +// +namespace bscu = boost::sort::common::util; +// +//############################################################################ +// ## +// ## +// S A M P L E _ S O R T ## +// ## +// ## +//############################################################################ +// +//----------------------------------------------------------------------------- +// function : sample_sort +/// @brief parallel sample sort algorithm (stable sort) +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +//----------------------------------------------------------------------------- +template<class Iter_t> +void sample_sort(Iter_t first, Iter_t last) +{ + typedef compare_iter<Iter_t> Compare; + sample_detail::sample_sort<Iter_t, Compare>(first, last); +}; +// +//----------------------------------------------------------------------------- +// function : sample_sort +/// @brief parallel sample sort algorithm (stable sort) +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param nthread : Number of threads to use in the process. When this value +/// is lower than 2, the sorting is done with 1 thread +//----------------------------------------------------------------------------- +template<class Iter_t> +void sample_sort(Iter_t first, Iter_t last, uint32_t nthread) +{ + typedef compare_iter<Iter_t> Compare; + sample_detail::sample_sort<Iter_t, Compare>(first, last, nthread); +}; +// +//----------------------------------------------------------------------------- +// function : sample_sort +/// @brief parallel sample sort algorithm (stable sort) +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare, bscu::enable_if_not_integral<Compare> * = + nullptr> +void sample_sort(Iter_t first, Iter_t last, Compare comp) +{ + sample_detail::sample_sort<Iter_t, Compare>(first, last, comp); +}; +// +//----------------------------------------------------------------------------- +// function : sample_sort +/// @brief parallel sample sort algorithm (stable sort) +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +/// @param nthread : Number of threads to use in the process. When this value +/// is lower than 2, the sorting is done with 1 thread +//----------------------------------------------------------------------------- +template<class Iter_t, class Compare> +void sample_sort(Iter_t first, Iter_t last, Compare comp, uint32_t nthread) +{ + sample_detail::sample_sort<Iter_t, Compare>(first, last, comp, nthread); +}; +// +//**************************************************************************** +};// End namespace sort +};// End namespace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/sort.hpp b/boost/sort/sort.hpp index bc4fe974a6..625f134b5c 100644 --- a/boost/sort/sort.hpp +++ b/boost/sort/sort.hpp @@ -1,19 +1,24 @@ -// The Boost Sort library cumulative header.
-
-// Copyright Steven J. Ross 2014
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort/ for library home page.
-
-#ifndef BOOST_SORT_HPP
-#define BOOST_SORT_HPP
-
-/*
-Cumulative include for the Boost Sort library
-*/
-
-#include <boost/sort/spreadsort/spreadsort.hpp>
-
-#endif
+// The Boost Sort library cumulative header. + +// Copyright Steven J. Ross 2014 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort/ for library home page. + +#ifndef BOOST_SORT_HPP +#define BOOST_SORT_HPP + +/* +Cumulative include for the Boost Sort library +*/ +#include <boost/sort/spreadsort/spreadsort.hpp> +#include <boost/sort/spinsort/spinsort.hpp> +#include <boost/sort/flat_stable_sort/flat_stable_sort.hpp> +#include <boost/sort/pdqsort/pdqsort.hpp> +#include <boost/sort/block_indirect_sort/block_indirect_sort.hpp> +#include <boost/sort/sample_sort/sample_sort.hpp> +#include <boost/sort/parallel_stable_sort/parallel_stable_sort.hpp> + +#endif diff --git a/boost/sort/spinsort/spinsort.hpp b/boost/sort/spinsort/spinsort.hpp new file mode 100644 index 0000000000..0e9f2d5572 --- /dev/null +++ b/boost/sort/spinsort/spinsort.hpp @@ -0,0 +1,564 @@ +//---------------------------------------------------------------------------- +/// @file spinsort.hpp +/// @brief Spin Sort algorithm +/// +/// @author Copyright (c) 2016 Francisco José Tapia (fjtapia@gmail.com )\n +/// Distributed under the Boost Software License, Version 1.0.\n +/// ( See accompanying file LICENSE_1_0.txt or copy at +/// http://www.boost.org/LICENSE_1_0.txt ) +/// @version 0.1 +/// +/// @remarks +//----------------------------------------------------------------------------- +#ifndef __BOOST_SORT_PARALLEL_ALGORITHM_SPIN_SORT_HPP +#define __BOOST_SORT_PARALLEL_ALGORITHM_SPIN_SORT_HPP + +//#include <boost/sort/spinsort/util/indirect.hpp> +#include <boost/sort/insert_sort/insert_sort.hpp> +#include <boost/sort/common/util/traits.hpp> +#include <boost/sort/common/util/algorithm.hpp> +#include <boost/sort/common/range.hpp> +#include <boost/sort/common/indirect.hpp> +#include <cstdlib> +#include <functional> +#include <iterator> +#include <memory> +#include <type_traits> +#include <vector> +#include <cstddef> + +namespace boost +{ +namespace sort +{ +namespace spin_detail +{ + +//---------------------------------------------------------------------------- +// USING SENTENCES +//---------------------------------------------------------------------------- +namespace bsc = boost::sort::common; +using bsc::range; +using bsc::util::nbits64; +using bsc::util::compare_iter; +using bsc::util::value_iter; +using boost::sort::insert_sort; + +// +//############################################################################ +// ## +// D E F I N I T I O N S O F F U N C T I O N S ## +// ## +//############################################################################ +// +template <class Iter1_t, class Iter2_t, typename Compare> +static void insert_partial_sort (Iter1_t first, Iter1_t mid, + Iter1_t last, Compare comp, + const range<Iter2_t> &rng_aux); + +template<class Iter1_t, class Iter2_t, class Compare> +static bool check_stable_sort (const range<Iter1_t> &rng_data, + const range<Iter2_t> &rng_aux, Compare comp); + +template<class Iter1_t, class Iter2_t, class Compare> +static void range_sort (const range<Iter1_t> &range1, + const range<Iter2_t> &range2, Compare comp, + uint32_t level); + +template<class Iter1_t, class Iter2_t, class Compare> +static void sort_range_sort (const range<Iter1_t> &rng_data, + const range<Iter2_t> &rng_aux, Compare comp); + +// +//----------------------------------------------------------------------------- +// function : insert_partial_sort +/// @brief : Insertion sort of elements sorted +/// @param first: iterator to the first element of the range +/// @param mid : last pointer of the sorted data, and first pointer to the +/// elements to insert +/// @param last : iterator to the next element of the last in the range +/// @param comp : +/// @comments : the two ranges are sorted +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, typename Compare> +static void insert_partial_sort (Iter1_t first, Iter1_t mid, Iter1_t last, + Compare comp, const range<Iter2_t> &rng_aux) +{ + //------------------------------------------------------------------------ + // metaprogram + //------------------------------------------------------------------------ + typedef value_iter<Iter1_t> value_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same<value_t, value2_t>::value, + "Incompatible iterators\n"); + + //-------------------------------------------------------------------- + // program + //-------------------------------------------------------------------- + assert(size_t(last - mid) <= rng_aux.size()); + + if (mid == last) return; + //insertionsort ( mid, last, comp); + if (first == mid) return; + + //------------------------------------------------------------------------ + // creation of the vector of elements to insert and their position in the + // sorted part + // the data are inserted in rng_aux + //----------------------------------------------------------------------- + std::vector<Iter1_t> viter; + Iter2_t beta = rng_aux.first, data = rng_aux.first; + + for (Iter1_t alpha = mid; alpha != last; ++alpha) + *(beta++) = std::move(*alpha); + + size_t ndata = last - mid; + + Iter1_t linf = first, lsup = mid; + for (uint32_t i = 0; i < ndata; ++i) + { + Iter1_t it1 = std::upper_bound(linf, lsup, *(data + i), comp); + viter.push_back(it1); + linf = it1; + }; + + // moving the elements + viter.push_back(mid); + for (uint32_t i = viter.size() - 1; i != 0; --i) + { + Iter1_t src = viter[i], limit = viter[i - 1]; + Iter1_t dest = src + (i); + while (src != limit) *(--dest) = std::move(*(--src)); + *(viter[i - 1] + (i - 1)) = std::move(*(data + (i - 1))); + }; +} +; +//----------------------------------------------------------------------------- +// function : check_stable_sort +/// @brief check if the elements between first and last are osted or reverse +/// sorted. If the number of elements not sorted is small, insert in +/// the sorted part +/// @param range_input : range with the elements to sort +/// @param range_buffer : range with the elements sorted +/// @param comp : object for to compare two elements +/// @param level : when is 1, sort with the insertionsort algorithm +/// if not make a recursive call splitting the ranges +// +/// @comments : if the number of levels is odd, the data are in the first +/// parameter of range_sort, and the results appear in the second parameter +/// If the number of levels is even, the data are in the second +/// parameter of range_sort, and the results are in the same parameter +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +static bool check_stable_sort(const range<Iter1_t> &rng_data, + const range<Iter2_t> &rng_aux, Compare comp) +{ + //------------------------------------------------------------------------ + // metaprogramming + //------------------------------------------------------------------------ + typedef value_iter<Iter1_t> value_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same<value_t, value2_t>::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------ + // program + //------------------------------------------------------------------------ + // the maximun number of elements not ordered, for to be inserted in the + // sorted part + //const ptrdiff_t min_insert_partial_sort = 32 ; + const size_t ndata = rng_data.size(); + if (ndata < 32) + { + insert_sort(rng_data.first, rng_data.last, comp); + return true; + }; + const size_t min_insert_partial_sort = + ((ndata >> 3) < 33) ? 32 : (ndata >> 3); + if (ndata < 2) return true; + + // check if sorted + bool sw = true; + Iter1_t it2 = rng_data.first + 1; + for (Iter1_t it1 = rng_data.first; + it2 != rng_data.last and (sw = not comp(*it2, *it1)); it1 = + it2++) + ; + if (sw) return true; + + // insert the elements between it1 and last + if (size_t(rng_data.last - it2) < min_insert_partial_sort) + { + sort_range_sort(range<Iter1_t>(it2, rng_data.last), rng_aux, comp); + insert_partial_sort(rng_data.first, it2, rng_data.last, comp, rng_aux); + return true; + }; + + // check if reverse sorted + if ((it2 != (rng_data.first + 1))) return false; + sw = true; + for (Iter1_t it1 = rng_data.first; + it2 != rng_data.last and (sw = comp(*it2, *it1)); it1 = + it2++) + ; + if (size_t(rng_data.last - it2) >= min_insert_partial_sort) return false; + + // reverse the elements between first and it1 + size_t nreverse = it2 - rng_data.first; + Iter1_t alpha(rng_data.first), beta(it2 - 1), mid( + rng_data.first + (nreverse >> 1)); + while (alpha != mid) + std::swap(*(alpha++), *(beta--)); + + // insert the elements between it1 and last + if (it2 != rng_data.last) + { + sort_range_sort(range<Iter1_t>(it2, rng_data.last), rng_aux, comp); + insert_partial_sort(rng_data.first, it2, rng_data.last, comp, rng_aux); + }; + return true; +} +; +//----------------------------------------------------------------------------- +// function : range_sort +/// @brief this function divide r_input in two parts, sort it,and merge moving +/// the elements to range_buf +/// @param range_input : range with the elements to sort +/// @param range_buffer : range with the elements sorted +/// @param comp : object for to compare two elements +/// @param level : when is 1, sort with the insertionsort algorithm +/// if not make a recursive call splitting the ranges +// +/// @comments : if the number of levels is odd, the data are in the first +/// parameter of range_sort, and the results appear in the second parameter +/// If the number of levels is even, the data are in the second +/// parameter of range_sort, and the results are in the same parameter +/// The two ranges must have the same size +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +static void range_sort(const range<Iter1_t> &range1, + const range<Iter2_t> &range2, Compare comp, + uint32_t level) +{ + //----------------------------------------------------------------------- + // metaprogram + //----------------------------------------------------------------------- + typedef value_iter<Iter1_t> value_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same<value_t, value2_t>::value, + "Incompatible iterators\n"); + + //----------------------------------------------------------------------- + // program + //----------------------------------------------------------------------- + typedef range<Iter1_t> range_it1; + typedef range<Iter2_t> range_it2; + assert(range1.size() == range2.size() and level != 0); + + //------------------- check if sort -------------------------------------- + if (range1.size() > 1024) + { + if ((level & 1) == 0) + { + if (check_stable_sort(range2, range1, comp)) return; + } + else + { + if (check_stable_sort(range1, range2, comp)) + { + move_forward(range2, range1); + return; + }; + }; + }; + + //------------------- normal process ----------------------------------- + size_t nelem1 = (range1.size() + 1) >> 1; + range_it1 range_input1(range1.first, range1.first + nelem1), + range_input2(range1.first + nelem1, range1.last); + + if (level < 2) + { + insert_sort(range_input1.first, range_input1.last, comp); + insert_sort(range_input2.first, range_input2.last, comp); + } + else + { + range_sort (range_it2(range2.first, range2.first + nelem1), + range_input1, comp, level - 1); + + range_sort (range_it2(range2.first + nelem1, range2.last), + range_input2, comp, level - 1); + }; + + merge(range2, range_input1, range_input2, comp); +} +; +//----------------------------------------------------------------------------- +// function : sort_range_sort +/// @brief this sort elements using the range_sort function and receiving a +/// buffer of initialized memory +/// @param rng_data : range with the elements to sort +/// @param rng_aux : range of at least the same memory than rng_data used as +/// auxiliary memory in the sorting +/// @param comp : object for to compare two elements +//----------------------------------------------------------------------------- +template<class Iter1_t, class Iter2_t, class Compare> +static void sort_range_sort(const range<Iter1_t> &rng_data, + const range<Iter2_t> &rng_aux, Compare comp) +{ + //----------------------------------------------------------------------- + // metaprogram + //----------------------------------------------------------------------- + typedef value_iter<Iter1_t> value_t; + typedef value_iter<Iter2_t> value2_t; + static_assert (std::is_same<value_t, value2_t>::value, + "Incompatible iterators\n"); + + //------------------------------------------------------------------------ + // program + //------------------------------------------------------------------------ + // minimal number of element before to jump to insertionsort + static const uint32_t sort_min = 32; + if (rng_data.size() <= sort_min) + { + insert_sort(rng_data.first, rng_data.last, comp); + return; + }; + +#ifdef __BS_DEBUG + assert (rng_aux.size () >= rng_data.size ()); +#endif + + range<Iter2_t> rng_buffer(rng_aux.first, rng_aux.first + rng_data.size()); + uint32_t nlevel = + nbits64(((rng_data.size() + sort_min - 1) / sort_min) - 1); + //assert (nlevel != 0); + + if ((nlevel & 1) == 0) + { + range_sort(rng_buffer, rng_data, comp, nlevel); + } + else + { + range_sort(rng_data, rng_buffer, comp, nlevel); + move_forward(rng_data, rng_buffer); + }; +} +; +// +//############################################################################ +// ## +// S T R U C T ## +// ## +// S P I N _ S O R T ## +// ## +//############################################################################ +//--------------------------------------------------------------------------- +/// @struct spin_sort +/// @brief This class implement s stable sort algorithm with 1 thread, with +/// an auxiliary memory of N/2 elements +//---------------------------------------------------------------------------- +template<class Iter_t, typename Compare = compare_iter<Iter_t>> +class spinsort +{ + //------------------------------------------------------------------------ + // DEFINITIONS AND CONSTANTS + //------------------------------------------------------------------------ + typedef value_iter<Iter_t> value_t; + typedef range<Iter_t> range_it; + typedef range<value_t *> range_buf; + // When the number of elements to sort is smaller than Sort_min, are sorted + // by the insertion sort algorithm + static const uint32_t Sort_min = 36; + + //------------------------------------------------------------------------ + // VARIABLES + //------------------------------------------------------------------------ + // Pointer to the auxiliary memory + value_t *ptr; + + // Number of elements in the auxiliary memory + size_t nptr; + + // construct indicate if the auxiliary memory in initialized, and owner + // indicate if the auxiliary memory had been created inside the object or + // had + // been received as a parameter + bool construct = false, owner = false; + + //------------------------------------------------------------------------ + // PRIVATE FUNCTIONS + //------------------------------------------------------------------------- + spinsort (Iter_t first, Iter_t last, Compare comp, value_t *paux, + size_t naux); + +public: + //------------------------------------------------------------------------ + // PUBLIC FUNCTIONS + //------------------------------------------------------------------------- + spinsort(Iter_t first, Iter_t last, Compare comp = Compare()) + : spinsort(first, last, comp, nullptr, 0) { }; + + spinsort(Iter_t first, Iter_t last, Compare comp, range_buf range_aux) + : spinsort(first, last, comp, range_aux.first, range_aux.size()) { }; + // + //----------------------------------------------------------------------- + // function :~spinsort + /// @brief destructor of the struct. Destroy the elements if construct is + /// true, + /// and return the memory if owner is true + //----------------------------------------------------------------------- + ~spinsort(void) + { + if (construct) + { + destroy(range<value_t *>(ptr, ptr + nptr)); + construct = false; + }; + if (owner and ptr != nullptr) std::return_temporary_buffer(ptr); + }; +}; +//---------------------------------------------------------------------------- +// End of class spinsort +//---------------------------------------------------------------------------- +// +//------------------------------------------------------------------------- +// function : spinsort +/// @brief constructor of the struct +// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +/// @param paux : pointer to the auxiliary memory provided. If nullptr, the +/// memory is created inside the class +/// @param naux : number of elements pointed by paux +//------------------------------------------------------------------------ +template <class Iter_t, typename Compare> +spinsort <Iter_t, Compare> +::spinsort (Iter_t first, Iter_t last, Compare comp, value_t *paux, size_t naux) +: ptr(paux), nptr(naux), construct(false), owner(false) +{ + range<Iter_t> range_input(first, last); + assert(range_input.valid()); + + size_t nelem = range_input.size(); + owner = construct = false; + + nptr = (nelem + 1) >> 1; + size_t nelem_1 = nptr; + size_t nelem_2 = nelem - nelem_1; + + if (nelem <= (Sort_min << 1)) + { + insert_sort(range_input.first, range_input.last, comp); + return; + }; + + //------------------- check if sort --------------------------------- + bool sw = true; + for (Iter_t it1 = first, it2 = first + 1; it2 != last + and (sw = not comp(*it2, *it1)); it1 = it2++) ; + if (sw) return; + + //------------------- check if reverse sort ------------------------- + sw = true; + for (Iter_t it1 = first, it2 = first + 1; + it2 != last and (sw = comp(*it2, *it1)); it1 = it2++); + if (sw) + { + size_t nelem2 = nelem >> 1; + Iter_t it1 = first, it2 = last - 1; + for (size_t i = 0; i < nelem2; ++i) + std::swap(*(it1++), *(it2--)); + return; + }; + + if (ptr == nullptr) + { + ptr = std::get_temporary_buffer<value_t>(nptr).first; + if (ptr == nullptr) throw std::bad_alloc(); + owner = true; + }; + range_buf range_aux(ptr, (ptr + nptr)); + + //--------------------------------------------------------------------- + // Process + //--------------------------------------------------------------------- + uint32_t nlevel = nbits64(((nelem + Sort_min - 1) / Sort_min) - 1) - 1; + assert(nlevel != 0); + + if ((nlevel & 1) == 1) + { + //---------------------------------------------------------------- + // if the number of levels is odd, the data are in the first + // parameter of range_sort, and the results appear in the second + // parameter + //---------------------------------------------------------------- + range_it range_1(first, first + nelem_2), range_2(first + nelem_2, + last); + range_aux = move_construct(range_aux, range_2); + construct = true; + + range_sort(range_aux, range_2, comp, nlevel); + range_buf rng_bx(range_aux.first, range_aux.first + nelem_2); + + range_sort(range_1, rng_bx, comp, nlevel); + merge_half(range_input, rng_bx, range_2, comp); + } + else + { + //---------------------------------------------------------------- + // If the number of levels is even, the data are in the second + // parameter of range_sort, and the results are in the same + // parameter + //---------------------------------------------------------------- + range_it range_1(first, first + nelem_1), range_2(first + nelem_1, + last); + range_aux = move_construct(range_aux, range_1); + construct = true; + + range_sort(range_1, range_aux, comp, nlevel); + + range_1.last = range_1.first + range_2.size(); + range_sort(range_1, range_2, comp, nlevel); + merge_half(range_input, range_aux, range_2, comp); + }; +}; + +//**************************************************************************** +};// End namepspace spin_detail +//**************************************************************************** +// +namespace bsc = boost::sort::common; +//----------------------------------------------------------------------------- +// function : spinsort +/// @brief this function implement a single thread stable sort +/// +/// @param first : iterator to the first element of the range to sort +/// @param last : iterator after the last element to the range to sort +/// @param comp : object for to compare two elements pointed by Iter_t +/// iterators +//----------------------------------------------------------------------------- +template <class Iter_t, class Compare = compare_iter<Iter_t>> +inline void spinsort (Iter_t first, Iter_t last, Compare comp = Compare()) +{ + spin_detail::spinsort <Iter_t, Compare> (first, last, comp); +}; + +template <class Iter_t, class Compare = compare_iter<Iter_t>> +inline void indirect_spinsort (Iter_t first, Iter_t last, + Compare comp = Compare()) +{ + typedef typename std::vector<Iter_t>::iterator itx_iter; + typedef common::less_ptr_no_null <Iter_t, Compare> itx_comp; + common::indirect_sort (spinsort<itx_iter, itx_comp>, first, last, comp); +}; + +//**************************************************************************** +};// End namespace sort +};// End namepspace boost +//**************************************************************************** +// +#endif diff --git a/boost/sort/spreadsort/detail/constants.hpp b/boost/sort/spreadsort/detail/constants.hpp index a134761e59..9eebc43c69 100644 --- a/boost/sort/spreadsort/detail/constants.hpp +++ b/boost/sort/spreadsort/detail/constants.hpp @@ -1,46 +1,46 @@ -//constant definitions for the Boost Sort library
-
-// Copyright Steven J. Ross 2001 - 2014
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort for library home page.
-#ifndef BOOST_SORT_SPREADSORT_DETAIL_CONSTANTS
-#define BOOST_SORT_SPREADSORT_DETAIL_CONSTANTS
-namespace boost {
-namespace sort {
-namespace spreadsort {
-namespace detail {
-//Tuning constants
-//This should be tuned to your processor cache;
-//if you go too large you get cache misses on bins
-//The smaller this number, the less worst-case memory usage.
-//If too small, too many recursions slow down spreadsort
-enum { max_splits = 11,
-//It's better to have a few cache misses and finish sorting
-//than to run another iteration
-max_finishing_splits = max_splits + 1,
-//Sets the minimum number of items per bin.
-int_log_mean_bin_size = 2,
-//Used to force a comparison-based sorting for small bins, if it's faster.
-//Minimum value 1
-int_log_min_split_count = 9,
-//This is the minimum split count to use spreadsort when it will finish in one
-//iteration. Make this larger the faster std::sort is relative to integer_sort.
-int_log_finishing_count = 31,
-//Sets the minimum number of items per bin for floating point.
-float_log_mean_bin_size = 2,
-//Used to force a comparison-based sorting for small bins, if it's faster.
-//Minimum value 1
-float_log_min_split_count = 8,
-//This is the minimum split count to use spreadsort when it will finish in one
-//iteration. Make this larger the faster std::sort is relative to float_sort.
-float_log_finishing_count = 4,
-//There is a minimum size below which it is not worth using spreadsort
-min_sort_size = 1000 };
-}
-}
-}
-}
-#endif
+//constant definitions for the Boost Sort library + +// Copyright Steven J. Ross 2001 - 2014 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort for library home page. +#ifndef BOOST_SORT_SPREADSORT_DETAIL_CONSTANTS +#define BOOST_SORT_SPREADSORT_DETAIL_CONSTANTS +namespace boost { +namespace sort { +namespace spreadsort { +namespace detail { +//Tuning constants +//This should be tuned to your processor cache; +//if you go too large you get cache misses on bins +//The smaller this number, the less worst-case memory usage. +//If too small, too many recursions slow down spreadsort +enum { max_splits = 11, +//It's better to have a few cache misses and finish sorting +//than to run another iteration +max_finishing_splits = max_splits + 1, +//Sets the minimum number of items per bin. +int_log_mean_bin_size = 2, +//Used to force a comparison-based sorting for small bins, if it's faster. +//Minimum value 1 +int_log_min_split_count = 9, +//This is the minimum split count to use spreadsort when it will finish in one +//iteration. Make this larger the faster std::sort is relative to integer_sort. +int_log_finishing_count = 31, +//Sets the minimum number of items per bin for floating point. +float_log_mean_bin_size = 2, +//Used to force a comparison-based sorting for small bins, if it's faster. +//Minimum value 1 +float_log_min_split_count = 8, +//This is the minimum split count to use spreadsort when it will finish in one +//iteration. Make this larger the faster std::sort is relative to float_sort. +float_log_finishing_count = 4, +//There is a minimum size below which it is not worth using spreadsort +min_sort_size = 1000 }; +} +} +} +} +#endif diff --git a/boost/sort/spreadsort/detail/float_sort.hpp b/boost/sort/spreadsort/detail/float_sort.hpp index 03dcbaf4f6..93aaa2f69e 100644 --- a/boost/sort/spreadsort/detail/float_sort.hpp +++ b/boost/sort/spreadsort/detail/float_sort.hpp @@ -1,831 +1,831 @@ -// Details for templated Spreadsort-based float_sort.
-
-// Copyright Steven J. Ross 2001 - 2014.
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort for library home page.
-
-/*
-Some improvements suggested by:
-Phil Endecott and Frank Gennari
-float_mem_cast fix provided by:
-Scott McMurray
-*/
-
-#ifndef BOOST_SORT_SPREADSORT_DETAIL_FLOAT_SORT_HPP
-#define BOOST_SORT_SPREADSORT_DETAIL_FLOAT_SORT_HPP
-#include <algorithm>
-#include <vector>
-#include <limits>
-#include <functional>
-#include <boost/static_assert.hpp>
-#include <boost/serialization/static_warning.hpp>
-#include <boost/utility/enable_if.hpp>
-#include <boost/sort/spreadsort/detail/constants.hpp>
-#include <boost/sort/spreadsort/detail/integer_sort.hpp>
-#include <boost/sort/spreadsort/detail/spreadsort_common.hpp>
-#include <boost/cstdint.hpp>
-
-namespace boost {
-namespace sort {
-namespace spreadsort {
- namespace detail {
- //Casts a RandomAccessIter to the specified integer type
- template<class Cast_type, class RandomAccessIter>
- inline Cast_type
- cast_float_iter(const RandomAccessIter & floatiter)
- {
- typedef typename std::iterator_traits<RandomAccessIter>::value_type
- Data_type;
- //Only cast IEEE floating-point numbers, and only to same-sized integers
- BOOST_STATIC_ASSERT(sizeof(Cast_type) == sizeof(Data_type));
- BOOST_STATIC_ASSERT(std::numeric_limits<Data_type>::is_iec559);
- BOOST_STATIC_ASSERT(std::numeric_limits<Cast_type>::is_integer);
- Cast_type result;
- std::memcpy(&result, &(*floatiter), sizeof(Data_type));
- return result;
- }
-
- // Return true if the list is sorted. Otherwise, find the minimum and
- // maximum. Values are Right_shifted 0 bits before comparison.
- template <class RandomAccessIter, class Div_type, class Right_shift>
- inline bool
- is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last,
- Div_type & max, Div_type & min, Right_shift rshift)
- {
- min = max = rshift(*current, 0);
- RandomAccessIter prev = current;
- bool sorted = true;
- while (++current < last) {
- Div_type value = rshift(*current, 0);
- sorted &= *current >= *prev;
- prev = current;
- if (max < value)
- max = value;
- else if (value < min)
- min = value;
- }
- return sorted;
- }
-
- // Return true if the list is sorted. Otherwise, find the minimum and
- // maximum. Uses comp to check if the data is already sorted.
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare>
- inline bool
- is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last,
- Div_type & max, Div_type & min,
- Right_shift rshift, Compare comp)
- {
- min = max = rshift(*current, 0);
- RandomAccessIter prev = current;
- bool sorted = true;
- while (++current < last) {
- Div_type value = rshift(*current, 0);
- sorted &= !comp(*current, *prev);
- prev = current;
- if (max < value)
- max = value;
- else if (value < min)
- min = value;
- }
- return sorted;
- }
-
- //Specialized swap loops for floating-point casting
- template <class RandomAccessIter, class Div_type>
- inline void inner_float_swap_loop(RandomAccessIter * bins,
- const RandomAccessIter & nextbinstart, unsigned ii
- , const unsigned log_divisor, const Div_type div_min)
- {
- RandomAccessIter * local_bin = bins + ii;
- for (RandomAccessIter current = *local_bin; current < nextbinstart;
- ++current) {
- for (RandomAccessIter * target_bin =
- (bins + ((cast_float_iter<Div_type, RandomAccessIter>(current) >>
- log_divisor) - div_min)); target_bin != local_bin;
- target_bin = bins + ((cast_float_iter<Div_type, RandomAccessIter>
- (current) >> log_divisor) - div_min)) {
- typename std::iterator_traits<RandomAccessIter>::value_type tmp;
- RandomAccessIter b = (*target_bin)++;
- RandomAccessIter * b_bin = bins + ((cast_float_iter<Div_type,
- RandomAccessIter>(b) >> log_divisor) - div_min);
- //Three-way swap; if the item to be swapped doesn't belong in the
- //current bin, swap it to where it belongs
- if (b_bin != local_bin) {
- RandomAccessIter c = (*b_bin)++;
- tmp = *c;
- *c = *b;
- }
- else
- tmp = *b;
- *b = *current;
- *current = tmp;
- }
- }
- *local_bin = nextbinstart;
- }
-
- template <class RandomAccessIter, class Div_type>
- inline void float_swap_loop(RandomAccessIter * bins,
- RandomAccessIter & nextbinstart, unsigned ii,
- const size_t *bin_sizes,
- const unsigned log_divisor, const Div_type div_min)
- {
- nextbinstart += bin_sizes[ii];
- inner_float_swap_loop<RandomAccessIter, Div_type>
- (bins, nextbinstart, ii, log_divisor, div_min);
- }
-
- // Return true if the list is sorted. Otherwise, find the minimum and
- // maximum. Values are cast to Cast_type before comparison.
- template <class RandomAccessIter, class Cast_type>
- inline bool
- is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last,
- Cast_type & max, Cast_type & min)
- {
- min = max = cast_float_iter<Cast_type, RandomAccessIter>(current);
- RandomAccessIter prev = current;
- bool sorted = true;
- while (++current < last) {
- Cast_type value = cast_float_iter<Cast_type, RandomAccessIter>(current);
- sorted &= *current >= *prev;
- prev = current;
- if (max < value)
- max = value;
- else if (value < min)
- min = value;
- }
- return sorted;
- }
-
- //Special-case sorting of positive floats with casting
- template <class RandomAccessIter, class Div_type, class Size_type>
- inline void
- positive_float_sort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
- , size_t *bin_sizes)
- {
- Div_type max, min;
- if (is_sorted_or_find_extremes<RandomAccessIter, Div_type>(first, last,
- max, min))
- return;
- unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>(
- last - first, rough_log_2_size(Size_type(max - min)));
- Div_type div_min = min >> log_divisor;
- Div_type div_max = max >> log_divisor;
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, bin_count);
-
- //Calculating the size of each bin
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[unsigned((cast_float_iter<Div_type, RandomAccessIter>(
- current++) >> log_divisor) - div_min)]++;
- bins[0] = first;
- for (unsigned u = 0; u < bin_count - 1; u++)
- bins[u + 1] = bins[u] + bin_sizes[u];
-
-
- //Swap into place
- RandomAccessIter nextbinstart = first;
- for (unsigned u = 0; u < bin_count - 1; ++u)
- float_swap_loop<RandomAccessIter, Div_type>
- (bins, nextbinstart, u, bin_sizes, log_divisor, div_min);
- bins[bin_count - 1] = last;
-
- //Return if we've completed bucketsorting
- if (!log_divisor)
- return;
-
- //Recursing
- size_t max_count = get_min_count<float_log_mean_bin_size,
- float_log_min_split_count,
- float_log_finishing_count>(log_divisor);
- RandomAccessIter lastPos = first;
- for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
- ++u) {
- size_t count = bin_cache[u] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[u]);
- else
- positive_float_sort_rec<RandomAccessIter, Div_type, Size_type>
- (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes);
- }
- }
-
- //Sorting negative floats
- //Bins are iterated in reverse because max_neg_float = min_neg_int
- template <class RandomAccessIter, class Div_type, class Size_type>
- inline void
- negative_float_sort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache,
- unsigned cache_offset, size_t *bin_sizes)
- {
- Div_type max, min;
- if (is_sorted_or_find_extremes<RandomAccessIter, Div_type>(first, last,
- max, min))
- return;
-
- unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>(
- last - first, rough_log_2_size(Size_type(max - min)));
- Div_type div_min = min >> log_divisor;
- Div_type div_max = max >> log_divisor;
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, bin_count);
-
- //Calculating the size of each bin
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[unsigned((cast_float_iter<Div_type, RandomAccessIter>(
- current++) >> log_divisor) - div_min)]++;
- bins[bin_count - 1] = first;
- for (int ii = bin_count - 2; ii >= 0; --ii)
- bins[ii] = bins[ii + 1] + bin_sizes[ii + 1];
-
- //Swap into place
- RandomAccessIter nextbinstart = first;
- //The last bin will always have the correct elements in it
- for (int ii = bin_count - 1; ii > 0; --ii)
- float_swap_loop<RandomAccessIter, Div_type>
- (bins, nextbinstart, ii, bin_sizes, log_divisor, div_min);
- //Update the end position because we don't process the last bin
- bin_cache[cache_offset] = last;
-
- //Return if we've completed bucketsorting
- if (!log_divisor)
- return;
-
- //Recursing
- size_t max_count = get_min_count<float_log_mean_bin_size,
- float_log_min_split_count,
- float_log_finishing_count>(log_divisor);
- RandomAccessIter lastPos = first;
- for (int ii = cache_end - 1; ii >= static_cast<int>(cache_offset);
- lastPos = bin_cache[ii], --ii) {
- size_t count = bin_cache[ii] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[ii]);
- else
- negative_float_sort_rec<RandomAccessIter, Div_type, Size_type>
- (lastPos, bin_cache[ii], bin_cache, cache_end, bin_sizes);
- }
- }
-
- //Sorting negative floats
- //Bins are iterated in reverse order because max_neg_float = min_neg_int
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Size_type>
- inline void
- negative_float_sort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
- , size_t *bin_sizes, Right_shift rshift)
- {
- Div_type max, min;
- if (is_sorted_or_find_extremes(first, last, max, min, rshift))
- return;
- unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>(
- last - first, rough_log_2_size(Size_type(max - min)));
- Div_type div_min = min >> log_divisor;
- Div_type div_max = max >> log_divisor;
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, bin_count);
-
- //Calculating the size of each bin
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
- bins[bin_count - 1] = first;
- for (int ii = bin_count - 2; ii >= 0; --ii)
- bins[ii] = bins[ii + 1] + bin_sizes[ii + 1];
-
- //Swap into place
- RandomAccessIter nextbinstart = first;
- //The last bin will always have the correct elements in it
- for (int ii = bin_count - 1; ii > 0; --ii)
- swap_loop<RandomAccessIter, Div_type, Right_shift>
- (bins, nextbinstart, ii, rshift, bin_sizes, log_divisor, div_min);
- //Update the end position of the unprocessed last bin
- bin_cache[cache_offset] = last;
-
- //Return if we've completed bucketsorting
- if (!log_divisor)
- return;
-
- //Recursing
- size_t max_count = get_min_count<float_log_mean_bin_size,
- float_log_min_split_count,
- float_log_finishing_count>(log_divisor);
- RandomAccessIter lastPos = first;
- for (int ii = cache_end - 1; ii >= static_cast<int>(cache_offset);
- lastPos = bin_cache[ii], --ii) {
- size_t count = bin_cache[ii] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[ii]);
- else
- negative_float_sort_rec<RandomAccessIter, Div_type, Right_shift,
- Size_type>
- (lastPos, bin_cache[ii], bin_cache, cache_end, bin_sizes, rshift);
- }
- }
-
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare, class Size_type>
- inline void
- negative_float_sort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset,
- size_t *bin_sizes, Right_shift rshift, Compare comp)
- {
- Div_type max, min;
- if (is_sorted_or_find_extremes(first, last, max, min, rshift, comp))
- return;
- unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>(
- last - first, rough_log_2_size(Size_type(max - min)));
- Div_type div_min = min >> log_divisor;
- Div_type div_max = max >> log_divisor;
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, bin_count);
-
- //Calculating the size of each bin
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
- bins[bin_count - 1] = first;
- for (int ii = bin_count - 2; ii >= 0; --ii)
- bins[ii] = bins[ii + 1] + bin_sizes[ii + 1];
-
- //Swap into place
- RandomAccessIter nextbinstart = first;
- //The last bin will always have the correct elements in it
- for (int ii = bin_count - 1; ii > 0; --ii)
- swap_loop<RandomAccessIter, Div_type, Right_shift>
- (bins, nextbinstart, ii, rshift, bin_sizes, log_divisor, div_min);
- //Update the end position of the unprocessed last bin
- bin_cache[cache_offset] = last;
-
- //Return if we've completed bucketsorting
- if (!log_divisor)
- return;
-
- //Recursing
- size_t max_count = get_min_count<float_log_mean_bin_size,
- float_log_min_split_count,
- float_log_finishing_count>(log_divisor);
- RandomAccessIter lastPos = first;
- for (int ii = cache_end - 1; ii >= static_cast<int>(cache_offset);
- lastPos = bin_cache[ii], --ii) {
- size_t count = bin_cache[ii] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[ii], comp);
- else
- negative_float_sort_rec<RandomAccessIter, Div_type, Right_shift,
- Compare, Size_type>(lastPos, bin_cache[ii],
- bin_cache, cache_end,
- bin_sizes, rshift, comp);
- }
- }
-
- //Casting special-case for floating-point sorting
- template <class RandomAccessIter, class Div_type, class Size_type>
- inline void
- float_sort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
- , size_t *bin_sizes)
- {
- Div_type max, min;
- if (is_sorted_or_find_extremes<RandomAccessIter, Div_type>(first, last,
- max, min))
- return;
- unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>(
- last - first, rough_log_2_size(Size_type(max - min)));
- Div_type div_min = min >> log_divisor;
- Div_type div_max = max >> log_divisor;
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, bin_count);
-
- //Calculating the size of each bin
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[unsigned((cast_float_iter<Div_type, RandomAccessIter>(
- current++) >> log_divisor) - div_min)]++;
- //The index of the first positive bin
- //Must be divided small enough to fit into an integer
- unsigned first_positive = (div_min < 0) ? unsigned(-div_min) : 0;
- //Resetting if all bins are negative
- if (cache_offset + first_positive > cache_end)
- first_positive = cache_end - cache_offset;
- //Reversing the order of the negative bins
- //Note that because of the negative/positive ordering direction flip
- //We can not depend upon bin order and positions matching up
- //so bin_sizes must be reused to contain the end of the bin
- if (first_positive > 0) {
- bins[first_positive - 1] = first;
- for (int ii = first_positive - 2; ii >= 0; --ii) {
- bins[ii] = first + bin_sizes[ii + 1];
- bin_sizes[ii] += bin_sizes[ii + 1];
- }
- //Handling positives following negatives
- if (first_positive < bin_count) {
- bins[first_positive] = first + bin_sizes[0];
- bin_sizes[first_positive] += bin_sizes[0];
- }
- }
- else
- bins[0] = first;
- for (unsigned u = first_positive; u < bin_count - 1; u++) {
- bins[u + 1] = first + bin_sizes[u];
- bin_sizes[u + 1] += bin_sizes[u];
- }
-
- //Swap into place
- RandomAccessIter nextbinstart = first;
- for (unsigned u = 0; u < bin_count; ++u) {
- nextbinstart = first + bin_sizes[u];
- inner_float_swap_loop<RandomAccessIter, Div_type>
- (bins, nextbinstart, u, log_divisor, div_min);
- }
-
- if (!log_divisor)
- return;
-
- //Handling negative values first
- size_t max_count = get_min_count<float_log_mean_bin_size,
- float_log_min_split_count,
- float_log_finishing_count>(log_divisor);
- RandomAccessIter lastPos = first;
- for (int ii = cache_offset + first_positive - 1;
- ii >= static_cast<int>(cache_offset);
- lastPos = bin_cache[ii--]) {
- size_t count = bin_cache[ii] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[ii]);
- //sort negative values using reversed-bin spreadsort
- else
- negative_float_sort_rec<RandomAccessIter, Div_type, Size_type>
- (lastPos, bin_cache[ii], bin_cache, cache_end, bin_sizes);
- }
-
- for (unsigned u = cache_offset + first_positive; u < cache_end;
- lastPos = bin_cache[u], ++u) {
- size_t count = bin_cache[u] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[u]);
- //sort positive values using normal spreadsort
- else
- positive_float_sort_rec<RandomAccessIter, Div_type, Size_type>
- (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes);
- }
- }
-
- //Functor implementation for recursive sorting
- template <class RandomAccessIter, class Div_type, class Right_shift
- , class Size_type>
- inline void
- float_sort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
- , size_t *bin_sizes, Right_shift rshift)
- {
- Div_type max, min;
- if (is_sorted_or_find_extremes(first, last, max, min, rshift))
- return;
- unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>(
- last - first, rough_log_2_size(Size_type(max - min)));
- Div_type div_min = min >> log_divisor;
- Div_type div_max = max >> log_divisor;
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, bin_count);
-
- //Calculating the size of each bin
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
- //The index of the first positive bin
- unsigned first_positive = (div_min < 0) ? unsigned(-div_min) : 0;
- //Resetting if all bins are negative
- if (cache_offset + first_positive > cache_end)
- first_positive = cache_end - cache_offset;
- //Reversing the order of the negative bins
- //Note that because of the negative/positive ordering direction flip
- //We can not depend upon bin order and positions matching up
- //so bin_sizes must be reused to contain the end of the bin
- if (first_positive > 0) {
- bins[first_positive - 1] = first;
- for (int ii = first_positive - 2; ii >= 0; --ii) {
- bins[ii] = first + bin_sizes[ii + 1];
- bin_sizes[ii] += bin_sizes[ii + 1];
- }
- //Handling positives following negatives
- if (static_cast<unsigned>(first_positive) < bin_count) {
- bins[first_positive] = first + bin_sizes[0];
- bin_sizes[first_positive] += bin_sizes[0];
- }
- }
- else
- bins[0] = first;
- for (unsigned u = first_positive; u < bin_count - 1; u++) {
- bins[u + 1] = first + bin_sizes[u];
- bin_sizes[u + 1] += bin_sizes[u];
- }
-
- //Swap into place
- RandomAccessIter next_bin_start = first;
- for (unsigned u = 0; u < bin_count; ++u) {
- next_bin_start = first + bin_sizes[u];
- inner_swap_loop<RandomAccessIter, Div_type, Right_shift>
- (bins, next_bin_start, u, rshift, log_divisor, div_min);
- }
-
- //Return if we've completed bucketsorting
- if (!log_divisor)
- return;
-
- //Handling negative values first
- size_t max_count = get_min_count<float_log_mean_bin_size,
- float_log_min_split_count,
- float_log_finishing_count>(log_divisor);
- RandomAccessIter lastPos = first;
- for (int ii = cache_offset + first_positive - 1;
- ii >= static_cast<int>(cache_offset);
- lastPos = bin_cache[ii--]) {
- size_t count = bin_cache[ii] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[ii]);
- //sort negative values using reversed-bin spreadsort
- else
- negative_float_sort_rec<RandomAccessIter, Div_type,
- Right_shift, Size_type>(lastPos, bin_cache[ii], bin_cache,
- cache_end, bin_sizes, rshift);
- }
-
- for (unsigned u = cache_offset + first_positive; u < cache_end;
- lastPos = bin_cache[u], ++u) {
- size_t count = bin_cache[u] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[u]);
- //sort positive values using normal spreadsort
- else
- spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Size_type,
- float_log_mean_bin_size, float_log_min_split_count,
- float_log_finishing_count>
- (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes, rshift);
- }
- }
-
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare, class Size_type>
- inline void
- float_sort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset,
- size_t *bin_sizes, Right_shift rshift, Compare comp)
- {
- Div_type max, min;
- if (is_sorted_or_find_extremes(first, last, max, min, rshift, comp))
- return;
- unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>(
- last - first, rough_log_2_size(Size_type(max - min)));
- Div_type div_min = min >> log_divisor;
- Div_type div_max = max >> log_divisor;
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, bin_count);
-
- //Calculating the size of each bin
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
- //The index of the first positive bin
- unsigned first_positive =
- (div_min < 0) ? static_cast<unsigned>(-div_min) : 0;
- //Resetting if all bins are negative
- if (cache_offset + first_positive > cache_end)
- first_positive = cache_end - cache_offset;
- //Reversing the order of the negative bins
- //Note that because of the negative/positive ordering direction flip
- //We can not depend upon bin order and positions matching up
- //so bin_sizes must be reused to contain the end of the bin
- if (first_positive > 0) {
- bins[first_positive - 1] = first;
- for (int ii = first_positive - 2; ii >= 0; --ii) {
- bins[ii] = first + bin_sizes[ii + 1];
- bin_sizes[ii] += bin_sizes[ii + 1];
- }
- //Handling positives following negatives
- if (static_cast<unsigned>(first_positive) < bin_count) {
- bins[first_positive] = first + bin_sizes[0];
- bin_sizes[first_positive] += bin_sizes[0];
- }
- }
- else
- bins[0] = first;
- for (unsigned u = first_positive; u < bin_count - 1; u++) {
- bins[u + 1] = first + bin_sizes[u];
- bin_sizes[u + 1] += bin_sizes[u];
- }
-
- //Swap into place
- RandomAccessIter next_bin_start = first;
- for (unsigned u = 0; u < bin_count; ++u) {
- next_bin_start = first + bin_sizes[u];
- inner_swap_loop<RandomAccessIter, Div_type, Right_shift>
- (bins, next_bin_start, u, rshift, log_divisor, div_min);
- }
-
- //Return if we've completed bucketsorting
- if (!log_divisor)
- return;
-
- //Handling negative values first
- size_t max_count = get_min_count<float_log_mean_bin_size,
- float_log_min_split_count,
- float_log_finishing_count>(log_divisor);
- RandomAccessIter lastPos = first;
- for (int ii = cache_offset + first_positive - 1;
- ii >= static_cast<int>(cache_offset);
- lastPos = bin_cache[ii--]) {
- size_t count = bin_cache[ii] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[ii], comp);
- //sort negative values using reversed-bin spreadsort
- else
- negative_float_sort_rec<RandomAccessIter, Div_type, Right_shift,
- Compare, Size_type>(lastPos, bin_cache[ii],
- bin_cache, cache_end,
- bin_sizes, rshift, comp);
- }
-
- for (unsigned u = cache_offset + first_positive; u < cache_end;
- lastPos = bin_cache[u], ++u) {
- size_t count = bin_cache[u] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[u], comp);
- //sort positive values using normal spreadsort
- else
- spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
- Size_type, float_log_mean_bin_size,
- float_log_min_split_count, float_log_finishing_count>
- (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes, rshift, comp);
- }
- }
-
- //Checking whether the value type is a float, and trying a 32-bit integer
- template <class RandomAccessIter>
- inline typename boost::enable_if_c< sizeof(boost::uint32_t) ==
- sizeof(typename std::iterator_traits<RandomAccessIter>::value_type)
- && std::numeric_limits<typename
- std::iterator_traits<RandomAccessIter>::value_type>::is_iec559,
- void >::type
- float_sort(RandomAccessIter first, RandomAccessIter last)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- float_sort_rec<RandomAccessIter, boost::int32_t, boost::uint32_t>
- (first, last, bin_cache, 0, bin_sizes);
- }
-
- //Checking whether the value type is a double, and using a 64-bit integer
- template <class RandomAccessIter>
- inline typename boost::enable_if_c< sizeof(boost::uint64_t) ==
- sizeof(typename std::iterator_traits<RandomAccessIter>::value_type)
- && std::numeric_limits<typename
- std::iterator_traits<RandomAccessIter>::value_type>::is_iec559,
- void >::type
- float_sort(RandomAccessIter first, RandomAccessIter last)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- float_sort_rec<RandomAccessIter, boost::int64_t, boost::uint64_t>
- (first, last, bin_cache, 0, bin_sizes);
- }
-
- template <class RandomAccessIter>
- inline typename boost::disable_if_c< (sizeof(boost::uint64_t) ==
- sizeof(typename std::iterator_traits<RandomAccessIter>::value_type)
- || sizeof(boost::uint32_t) ==
- sizeof(typename std::iterator_traits<RandomAccessIter>::value_type))
- && std::numeric_limits<typename
- std::iterator_traits<RandomAccessIter>::value_type>::is_iec559,
- void >::type
- float_sort(RandomAccessIter first, RandomAccessIter last)
- {
- BOOST_STATIC_WARNING(!(sizeof(boost::uint64_t) ==
- sizeof(typename std::iterator_traits<RandomAccessIter>::value_type)
- || sizeof(boost::uint32_t) ==
- sizeof(typename std::iterator_traits<RandomAccessIter>::value_type))
- || !std::numeric_limits<typename
- std::iterator_traits<RandomAccessIter>::value_type>::is_iec559);
- std::sort(first, last);
- }
-
- //These approaches require the user to do the typecast
- //with rshift but default comparision
- template <class RandomAccessIter, class Div_type, class Right_shift>
- inline typename boost::enable_if_c< sizeof(size_t) >= sizeof(Div_type),
- void >::type
- float_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift rshift)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- float_sort_rec<RandomAccessIter, Div_type, Right_shift, size_t>
- (first, last, bin_cache, 0, bin_sizes, rshift);
- }
-
- //maximum integer size with rshift but default comparision
- template <class RandomAccessIter, class Div_type, class Right_shift>
- inline typename boost::enable_if_c< sizeof(size_t) < sizeof(Div_type)
- && sizeof(boost::uintmax_t) >= sizeof(Div_type), void >::type
- float_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift rshift)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- float_sort_rec<RandomAccessIter, Div_type, Right_shift, boost::uintmax_t>
- (first, last, bin_cache, 0, bin_sizes, rshift);
- }
-
- //sizeof(Div_type) doesn't match, so use std::sort
- template <class RandomAccessIter, class Div_type, class Right_shift>
- inline typename boost::disable_if_c< sizeof(boost::uintmax_t) >=
- sizeof(Div_type), void >::type
- float_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift rshift)
- {
- BOOST_STATIC_WARNING(sizeof(boost::uintmax_t) >= sizeof(Div_type));
- std::sort(first, last);
- }
-
- //specialized comparison
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare>
- inline typename boost::enable_if_c< sizeof(size_t) >= sizeof(Div_type),
- void >::type
- float_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift rshift, Compare comp)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- float_sort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
- size_t>
- (first, last, bin_cache, 0, bin_sizes, rshift, comp);
- }
-
- //max-sized integer with specialized comparison
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare>
- inline typename boost::enable_if_c< sizeof(size_t) < sizeof(Div_type)
- && sizeof(boost::uintmax_t) >= sizeof(Div_type), void >::type
- float_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift rshift, Compare comp)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- float_sort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
- boost::uintmax_t>
- (first, last, bin_cache, 0, bin_sizes, rshift, comp);
- }
-
- //sizeof(Div_type) doesn't match, so use std::sort
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare>
- inline typename boost::disable_if_c< sizeof(boost::uintmax_t) >=
- sizeof(Div_type), void >::type
- float_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift rshift, Compare comp)
- {
- BOOST_STATIC_WARNING(sizeof(boost::uintmax_t) >= sizeof(Div_type));
- std::sort(first, last, comp);
- }
- }
-}
-}
-}
-
-#endif
+// Details for templated Spreadsort-based float_sort. + +// Copyright Steven J. Ross 2001 - 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort for library home page. + +/* +Some improvements suggested by: +Phil Endecott and Frank Gennari +float_mem_cast fix provided by: +Scott McMurray +*/ + +#ifndef BOOST_SORT_SPREADSORT_DETAIL_FLOAT_SORT_HPP +#define BOOST_SORT_SPREADSORT_DETAIL_FLOAT_SORT_HPP +#include <algorithm> +#include <vector> +#include <limits> +#include <functional> +#include <boost/static_assert.hpp> +#include <boost/serialization/static_warning.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/sort/spreadsort/detail/constants.hpp> +#include <boost/sort/spreadsort/detail/integer_sort.hpp> +#include <boost/sort/spreadsort/detail/spreadsort_common.hpp> +#include <boost/cstdint.hpp> + +namespace boost { +namespace sort { +namespace spreadsort { + namespace detail { + //Casts a RandomAccessIter to the specified integer type + template<class Cast_type, class RandomAccessIter> + inline Cast_type + cast_float_iter(const RandomAccessIter & floatiter) + { + typedef typename std::iterator_traits<RandomAccessIter>::value_type + Data_type; + //Only cast IEEE floating-point numbers, and only to same-sized integers + BOOST_STATIC_ASSERT(sizeof(Cast_type) == sizeof(Data_type)); + BOOST_STATIC_ASSERT(std::numeric_limits<Data_type>::is_iec559); + BOOST_STATIC_ASSERT(std::numeric_limits<Cast_type>::is_integer); + Cast_type result; + std::memcpy(&result, &(*floatiter), sizeof(Data_type)); + return result; + } + + // Return true if the list is sorted. Otherwise, find the minimum and + // maximum. Values are Right_shifted 0 bits before comparison. + template <class RandomAccessIter, class Div_type, class Right_shift> + inline bool + is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last, + Div_type & max, Div_type & min, Right_shift rshift) + { + min = max = rshift(*current, 0); + RandomAccessIter prev = current; + bool sorted = true; + while (++current < last) { + Div_type value = rshift(*current, 0); + sorted &= *current >= *prev; + prev = current; + if (max < value) + max = value; + else if (value < min) + min = value; + } + return sorted; + } + + // Return true if the list is sorted. Otherwise, find the minimum and + // maximum. Uses comp to check if the data is already sorted. + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare> + inline bool + is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last, + Div_type & max, Div_type & min, + Right_shift rshift, Compare comp) + { + min = max = rshift(*current, 0); + RandomAccessIter prev = current; + bool sorted = true; + while (++current < last) { + Div_type value = rshift(*current, 0); + sorted &= !comp(*current, *prev); + prev = current; + if (max < value) + max = value; + else if (value < min) + min = value; + } + return sorted; + } + + //Specialized swap loops for floating-point casting + template <class RandomAccessIter, class Div_type> + inline void inner_float_swap_loop(RandomAccessIter * bins, + const RandomAccessIter & nextbinstart, unsigned ii + , const unsigned log_divisor, const Div_type div_min) + { + RandomAccessIter * local_bin = bins + ii; + for (RandomAccessIter current = *local_bin; current < nextbinstart; + ++current) { + for (RandomAccessIter * target_bin = + (bins + ((cast_float_iter<Div_type, RandomAccessIter>(current) >> + log_divisor) - div_min)); target_bin != local_bin; + target_bin = bins + ((cast_float_iter<Div_type, RandomAccessIter> + (current) >> log_divisor) - div_min)) { + typename std::iterator_traits<RandomAccessIter>::value_type tmp; + RandomAccessIter b = (*target_bin)++; + RandomAccessIter * b_bin = bins + ((cast_float_iter<Div_type, + RandomAccessIter>(b) >> log_divisor) - div_min); + //Three-way swap; if the item to be swapped doesn't belong in the + //current bin, swap it to where it belongs + if (b_bin != local_bin) { + RandomAccessIter c = (*b_bin)++; + tmp = *c; + *c = *b; + } + else + tmp = *b; + *b = *current; + *current = tmp; + } + } + *local_bin = nextbinstart; + } + + template <class RandomAccessIter, class Div_type> + inline void float_swap_loop(RandomAccessIter * bins, + RandomAccessIter & nextbinstart, unsigned ii, + const size_t *bin_sizes, + const unsigned log_divisor, const Div_type div_min) + { + nextbinstart += bin_sizes[ii]; + inner_float_swap_loop<RandomAccessIter, Div_type> + (bins, nextbinstart, ii, log_divisor, div_min); + } + + // Return true if the list is sorted. Otherwise, find the minimum and + // maximum. Values are cast to Cast_type before comparison. + template <class RandomAccessIter, class Cast_type> + inline bool + is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last, + Cast_type & max, Cast_type & min) + { + min = max = cast_float_iter<Cast_type, RandomAccessIter>(current); + RandomAccessIter prev = current; + bool sorted = true; + while (++current < last) { + Cast_type value = cast_float_iter<Cast_type, RandomAccessIter>(current); + sorted &= *current >= *prev; + prev = current; + if (max < value) + max = value; + else if (value < min) + min = value; + } + return sorted; + } + + //Special-case sorting of positive floats with casting + template <class RandomAccessIter, class Div_type, class Size_type> + inline void + positive_float_sort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset + , size_t *bin_sizes) + { + Div_type max, min; + if (is_sorted_or_find_extremes<RandomAccessIter, Div_type>(first, last, + max, min)) + return; + unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>( + last - first, rough_log_2_size(Size_type(max - min))); + Div_type div_min = min >> log_divisor; + Div_type div_max = max >> log_divisor; + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, bin_count); + + //Calculating the size of each bin + for (RandomAccessIter current = first; current != last;) + bin_sizes[unsigned((cast_float_iter<Div_type, RandomAccessIter>( + current++) >> log_divisor) - div_min)]++; + bins[0] = first; + for (unsigned u = 0; u < bin_count - 1; u++) + bins[u + 1] = bins[u] + bin_sizes[u]; + + + //Swap into place + RandomAccessIter nextbinstart = first; + for (unsigned u = 0; u < bin_count - 1; ++u) + float_swap_loop<RandomAccessIter, Div_type> + (bins, nextbinstart, u, bin_sizes, log_divisor, div_min); + bins[bin_count - 1] = last; + + //Return if we've completed bucketsorting + if (!log_divisor) + return; + + //Recursing + size_t max_count = get_min_count<float_log_mean_bin_size, + float_log_min_split_count, + float_log_finishing_count>(log_divisor); + RandomAccessIter lastPos = first; + for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u], + ++u) { + size_t count = bin_cache[u] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[u]); + else + positive_float_sort_rec<RandomAccessIter, Div_type, Size_type> + (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes); + } + } + + //Sorting negative floats + //Bins are iterated in reverse because max_neg_float = min_neg_int + template <class RandomAccessIter, class Div_type, class Size_type> + inline void + negative_float_sort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, + unsigned cache_offset, size_t *bin_sizes) + { + Div_type max, min; + if (is_sorted_or_find_extremes<RandomAccessIter, Div_type>(first, last, + max, min)) + return; + + unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>( + last - first, rough_log_2_size(Size_type(max - min))); + Div_type div_min = min >> log_divisor; + Div_type div_max = max >> log_divisor; + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, bin_count); + + //Calculating the size of each bin + for (RandomAccessIter current = first; current != last;) + bin_sizes[unsigned((cast_float_iter<Div_type, RandomAccessIter>( + current++) >> log_divisor) - div_min)]++; + bins[bin_count - 1] = first; + for (int ii = bin_count - 2; ii >= 0; --ii) + bins[ii] = bins[ii + 1] + bin_sizes[ii + 1]; + + //Swap into place + RandomAccessIter nextbinstart = first; + //The last bin will always have the correct elements in it + for (int ii = bin_count - 1; ii > 0; --ii) + float_swap_loop<RandomAccessIter, Div_type> + (bins, nextbinstart, ii, bin_sizes, log_divisor, div_min); + //Update the end position because we don't process the last bin + bin_cache[cache_offset] = last; + + //Return if we've completed bucketsorting + if (!log_divisor) + return; + + //Recursing + size_t max_count = get_min_count<float_log_mean_bin_size, + float_log_min_split_count, + float_log_finishing_count>(log_divisor); + RandomAccessIter lastPos = first; + for (int ii = cache_end - 1; ii >= static_cast<int>(cache_offset); + lastPos = bin_cache[ii], --ii) { + size_t count = bin_cache[ii] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[ii]); + else + negative_float_sort_rec<RandomAccessIter, Div_type, Size_type> + (lastPos, bin_cache[ii], bin_cache, cache_end, bin_sizes); + } + } + + //Sorting negative floats + //Bins are iterated in reverse order because max_neg_float = min_neg_int + template <class RandomAccessIter, class Div_type, class Right_shift, + class Size_type> + inline void + negative_float_sort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset + , size_t *bin_sizes, Right_shift rshift) + { + Div_type max, min; + if (is_sorted_or_find_extremes(first, last, max, min, rshift)) + return; + unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>( + last - first, rough_log_2_size(Size_type(max - min))); + Div_type div_min = min >> log_divisor; + Div_type div_max = max >> log_divisor; + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, bin_count); + + //Calculating the size of each bin + for (RandomAccessIter current = first; current != last;) + bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++; + bins[bin_count - 1] = first; + for (int ii = bin_count - 2; ii >= 0; --ii) + bins[ii] = bins[ii + 1] + bin_sizes[ii + 1]; + + //Swap into place + RandomAccessIter nextbinstart = first; + //The last bin will always have the correct elements in it + for (int ii = bin_count - 1; ii > 0; --ii) + swap_loop<RandomAccessIter, Div_type, Right_shift> + (bins, nextbinstart, ii, rshift, bin_sizes, log_divisor, div_min); + //Update the end position of the unprocessed last bin + bin_cache[cache_offset] = last; + + //Return if we've completed bucketsorting + if (!log_divisor) + return; + + //Recursing + size_t max_count = get_min_count<float_log_mean_bin_size, + float_log_min_split_count, + float_log_finishing_count>(log_divisor); + RandomAccessIter lastPos = first; + for (int ii = cache_end - 1; ii >= static_cast<int>(cache_offset); + lastPos = bin_cache[ii], --ii) { + size_t count = bin_cache[ii] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[ii]); + else + negative_float_sort_rec<RandomAccessIter, Div_type, Right_shift, + Size_type> + (lastPos, bin_cache[ii], bin_cache, cache_end, bin_sizes, rshift); + } + } + + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare, class Size_type> + inline void + negative_float_sort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset, + size_t *bin_sizes, Right_shift rshift, Compare comp) + { + Div_type max, min; + if (is_sorted_or_find_extremes(first, last, max, min, rshift, comp)) + return; + unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>( + last - first, rough_log_2_size(Size_type(max - min))); + Div_type div_min = min >> log_divisor; + Div_type div_max = max >> log_divisor; + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, bin_count); + + //Calculating the size of each bin + for (RandomAccessIter current = first; current != last;) + bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++; + bins[bin_count - 1] = first; + for (int ii = bin_count - 2; ii >= 0; --ii) + bins[ii] = bins[ii + 1] + bin_sizes[ii + 1]; + + //Swap into place + RandomAccessIter nextbinstart = first; + //The last bin will always have the correct elements in it + for (int ii = bin_count - 1; ii > 0; --ii) + swap_loop<RandomAccessIter, Div_type, Right_shift> + (bins, nextbinstart, ii, rshift, bin_sizes, log_divisor, div_min); + //Update the end position of the unprocessed last bin + bin_cache[cache_offset] = last; + + //Return if we've completed bucketsorting + if (!log_divisor) + return; + + //Recursing + size_t max_count = get_min_count<float_log_mean_bin_size, + float_log_min_split_count, + float_log_finishing_count>(log_divisor); + RandomAccessIter lastPos = first; + for (int ii = cache_end - 1; ii >= static_cast<int>(cache_offset); + lastPos = bin_cache[ii], --ii) { + size_t count = bin_cache[ii] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[ii], comp); + else + negative_float_sort_rec<RandomAccessIter, Div_type, Right_shift, + Compare, Size_type>(lastPos, bin_cache[ii], + bin_cache, cache_end, + bin_sizes, rshift, comp); + } + } + + //Casting special-case for floating-point sorting + template <class RandomAccessIter, class Div_type, class Size_type> + inline void + float_sort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset + , size_t *bin_sizes) + { + Div_type max, min; + if (is_sorted_or_find_extremes<RandomAccessIter, Div_type>(first, last, + max, min)) + return; + unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>( + last - first, rough_log_2_size(Size_type(max - min))); + Div_type div_min = min >> log_divisor; + Div_type div_max = max >> log_divisor; + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, bin_count); + + //Calculating the size of each bin + for (RandomAccessIter current = first; current != last;) + bin_sizes[unsigned((cast_float_iter<Div_type, RandomAccessIter>( + current++) >> log_divisor) - div_min)]++; + //The index of the first positive bin + //Must be divided small enough to fit into an integer + unsigned first_positive = (div_min < 0) ? unsigned(-div_min) : 0; + //Resetting if all bins are negative + if (cache_offset + first_positive > cache_end) + first_positive = cache_end - cache_offset; + //Reversing the order of the negative bins + //Note that because of the negative/positive ordering direction flip + //We can not depend upon bin order and positions matching up + //so bin_sizes must be reused to contain the end of the bin + if (first_positive > 0) { + bins[first_positive - 1] = first; + for (int ii = first_positive - 2; ii >= 0; --ii) { + bins[ii] = first + bin_sizes[ii + 1]; + bin_sizes[ii] += bin_sizes[ii + 1]; + } + //Handling positives following negatives + if (first_positive < bin_count) { + bins[first_positive] = first + bin_sizes[0]; + bin_sizes[first_positive] += bin_sizes[0]; + } + } + else + bins[0] = first; + for (unsigned u = first_positive; u < bin_count - 1; u++) { + bins[u + 1] = first + bin_sizes[u]; + bin_sizes[u + 1] += bin_sizes[u]; + } + + //Swap into place + RandomAccessIter nextbinstart = first; + for (unsigned u = 0; u < bin_count; ++u) { + nextbinstart = first + bin_sizes[u]; + inner_float_swap_loop<RandomAccessIter, Div_type> + (bins, nextbinstart, u, log_divisor, div_min); + } + + if (!log_divisor) + return; + + //Handling negative values first + size_t max_count = get_min_count<float_log_mean_bin_size, + float_log_min_split_count, + float_log_finishing_count>(log_divisor); + RandomAccessIter lastPos = first; + for (int ii = cache_offset + first_positive - 1; + ii >= static_cast<int>(cache_offset); + lastPos = bin_cache[ii--]) { + size_t count = bin_cache[ii] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[ii]); + //sort negative values using reversed-bin spreadsort + else + negative_float_sort_rec<RandomAccessIter, Div_type, Size_type> + (lastPos, bin_cache[ii], bin_cache, cache_end, bin_sizes); + } + + for (unsigned u = cache_offset + first_positive; u < cache_end; + lastPos = bin_cache[u], ++u) { + size_t count = bin_cache[u] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[u]); + //sort positive values using normal spreadsort + else + positive_float_sort_rec<RandomAccessIter, Div_type, Size_type> + (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes); + } + } + + //Functor implementation for recursive sorting + template <class RandomAccessIter, class Div_type, class Right_shift + , class Size_type> + inline void + float_sort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset + , size_t *bin_sizes, Right_shift rshift) + { + Div_type max, min; + if (is_sorted_or_find_extremes(first, last, max, min, rshift)) + return; + unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>( + last - first, rough_log_2_size(Size_type(max - min))); + Div_type div_min = min >> log_divisor; + Div_type div_max = max >> log_divisor; + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, bin_count); + + //Calculating the size of each bin + for (RandomAccessIter current = first; current != last;) + bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++; + //The index of the first positive bin + unsigned first_positive = (div_min < 0) ? unsigned(-div_min) : 0; + //Resetting if all bins are negative + if (cache_offset + first_positive > cache_end) + first_positive = cache_end - cache_offset; + //Reversing the order of the negative bins + //Note that because of the negative/positive ordering direction flip + //We can not depend upon bin order and positions matching up + //so bin_sizes must be reused to contain the end of the bin + if (first_positive > 0) { + bins[first_positive - 1] = first; + for (int ii = first_positive - 2; ii >= 0; --ii) { + bins[ii] = first + bin_sizes[ii + 1]; + bin_sizes[ii] += bin_sizes[ii + 1]; + } + //Handling positives following negatives + if (static_cast<unsigned>(first_positive) < bin_count) { + bins[first_positive] = first + bin_sizes[0]; + bin_sizes[first_positive] += bin_sizes[0]; + } + } + else + bins[0] = first; + for (unsigned u = first_positive; u < bin_count - 1; u++) { + bins[u + 1] = first + bin_sizes[u]; + bin_sizes[u + 1] += bin_sizes[u]; + } + + //Swap into place + RandomAccessIter next_bin_start = first; + for (unsigned u = 0; u < bin_count; ++u) { + next_bin_start = first + bin_sizes[u]; + inner_swap_loop<RandomAccessIter, Div_type, Right_shift> + (bins, next_bin_start, u, rshift, log_divisor, div_min); + } + + //Return if we've completed bucketsorting + if (!log_divisor) + return; + + //Handling negative values first + size_t max_count = get_min_count<float_log_mean_bin_size, + float_log_min_split_count, + float_log_finishing_count>(log_divisor); + RandomAccessIter lastPos = first; + for (int ii = cache_offset + first_positive - 1; + ii >= static_cast<int>(cache_offset); + lastPos = bin_cache[ii--]) { + size_t count = bin_cache[ii] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[ii]); + //sort negative values using reversed-bin spreadsort + else + negative_float_sort_rec<RandomAccessIter, Div_type, + Right_shift, Size_type>(lastPos, bin_cache[ii], bin_cache, + cache_end, bin_sizes, rshift); + } + + for (unsigned u = cache_offset + first_positive; u < cache_end; + lastPos = bin_cache[u], ++u) { + size_t count = bin_cache[u] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[u]); + //sort positive values using normal spreadsort + else + spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Size_type, + float_log_mean_bin_size, float_log_min_split_count, + float_log_finishing_count> + (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes, rshift); + } + } + + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare, class Size_type> + inline void + float_sort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset, + size_t *bin_sizes, Right_shift rshift, Compare comp) + { + Div_type max, min; + if (is_sorted_or_find_extremes(first, last, max, min, rshift, comp)) + return; + unsigned log_divisor = get_log_divisor<float_log_mean_bin_size>( + last - first, rough_log_2_size(Size_type(max - min))); + Div_type div_min = min >> log_divisor; + Div_type div_max = max >> log_divisor; + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, bin_count); + + //Calculating the size of each bin + for (RandomAccessIter current = first; current != last;) + bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++; + //The index of the first positive bin + unsigned first_positive = + (div_min < 0) ? static_cast<unsigned>(-div_min) : 0; + //Resetting if all bins are negative + if (cache_offset + first_positive > cache_end) + first_positive = cache_end - cache_offset; + //Reversing the order of the negative bins + //Note that because of the negative/positive ordering direction flip + //We can not depend upon bin order and positions matching up + //so bin_sizes must be reused to contain the end of the bin + if (first_positive > 0) { + bins[first_positive - 1] = first; + for (int ii = first_positive - 2; ii >= 0; --ii) { + bins[ii] = first + bin_sizes[ii + 1]; + bin_sizes[ii] += bin_sizes[ii + 1]; + } + //Handling positives following negatives + if (static_cast<unsigned>(first_positive) < bin_count) { + bins[first_positive] = first + bin_sizes[0]; + bin_sizes[first_positive] += bin_sizes[0]; + } + } + else + bins[0] = first; + for (unsigned u = first_positive; u < bin_count - 1; u++) { + bins[u + 1] = first + bin_sizes[u]; + bin_sizes[u + 1] += bin_sizes[u]; + } + + //Swap into place + RandomAccessIter next_bin_start = first; + for (unsigned u = 0; u < bin_count; ++u) { + next_bin_start = first + bin_sizes[u]; + inner_swap_loop<RandomAccessIter, Div_type, Right_shift> + (bins, next_bin_start, u, rshift, log_divisor, div_min); + } + + //Return if we've completed bucketsorting + if (!log_divisor) + return; + + //Handling negative values first + size_t max_count = get_min_count<float_log_mean_bin_size, + float_log_min_split_count, + float_log_finishing_count>(log_divisor); + RandomAccessIter lastPos = first; + for (int ii = cache_offset + first_positive - 1; + ii >= static_cast<int>(cache_offset); + lastPos = bin_cache[ii--]) { + size_t count = bin_cache[ii] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[ii], comp); + //sort negative values using reversed-bin spreadsort + else + negative_float_sort_rec<RandomAccessIter, Div_type, Right_shift, + Compare, Size_type>(lastPos, bin_cache[ii], + bin_cache, cache_end, + bin_sizes, rshift, comp); + } + + for (unsigned u = cache_offset + first_positive; u < cache_end; + lastPos = bin_cache[u], ++u) { + size_t count = bin_cache[u] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[u], comp); + //sort positive values using normal spreadsort + else + spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare, + Size_type, float_log_mean_bin_size, + float_log_min_split_count, float_log_finishing_count> + (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes, rshift, comp); + } + } + + //Checking whether the value type is a float, and trying a 32-bit integer + template <class RandomAccessIter> + inline typename boost::enable_if_c< sizeof(boost::uint32_t) == + sizeof(typename std::iterator_traits<RandomAccessIter>::value_type) + && std::numeric_limits<typename + std::iterator_traits<RandomAccessIter>::value_type>::is_iec559, + void >::type + float_sort(RandomAccessIter first, RandomAccessIter last) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + float_sort_rec<RandomAccessIter, boost::int32_t, boost::uint32_t> + (first, last, bin_cache, 0, bin_sizes); + } + + //Checking whether the value type is a double, and using a 64-bit integer + template <class RandomAccessIter> + inline typename boost::enable_if_c< sizeof(boost::uint64_t) == + sizeof(typename std::iterator_traits<RandomAccessIter>::value_type) + && std::numeric_limits<typename + std::iterator_traits<RandomAccessIter>::value_type>::is_iec559, + void >::type + float_sort(RandomAccessIter first, RandomAccessIter last) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + float_sort_rec<RandomAccessIter, boost::int64_t, boost::uint64_t> + (first, last, bin_cache, 0, bin_sizes); + } + + template <class RandomAccessIter> + inline typename boost::disable_if_c< (sizeof(boost::uint64_t) == + sizeof(typename std::iterator_traits<RandomAccessIter>::value_type) + || sizeof(boost::uint32_t) == + sizeof(typename std::iterator_traits<RandomAccessIter>::value_type)) + && std::numeric_limits<typename + std::iterator_traits<RandomAccessIter>::value_type>::is_iec559, + void >::type + float_sort(RandomAccessIter first, RandomAccessIter last) + { + BOOST_STATIC_WARNING(!(sizeof(boost::uint64_t) == + sizeof(typename std::iterator_traits<RandomAccessIter>::value_type) + || sizeof(boost::uint32_t) == + sizeof(typename std::iterator_traits<RandomAccessIter>::value_type)) + || !std::numeric_limits<typename + std::iterator_traits<RandomAccessIter>::value_type>::is_iec559); + std::sort(first, last); + } + + //These approaches require the user to do the typecast + //with rshift but default comparision + template <class RandomAccessIter, class Div_type, class Right_shift> + inline typename boost::enable_if_c< sizeof(size_t) >= sizeof(Div_type), + void >::type + float_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift rshift) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + float_sort_rec<RandomAccessIter, Div_type, Right_shift, size_t> + (first, last, bin_cache, 0, bin_sizes, rshift); + } + + //maximum integer size with rshift but default comparision + template <class RandomAccessIter, class Div_type, class Right_shift> + inline typename boost::enable_if_c< sizeof(size_t) < sizeof(Div_type) + && sizeof(boost::uintmax_t) >= sizeof(Div_type), void >::type + float_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift rshift) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + float_sort_rec<RandomAccessIter, Div_type, Right_shift, boost::uintmax_t> + (first, last, bin_cache, 0, bin_sizes, rshift); + } + + //sizeof(Div_type) doesn't match, so use std::sort + template <class RandomAccessIter, class Div_type, class Right_shift> + inline typename boost::disable_if_c< sizeof(boost::uintmax_t) >= + sizeof(Div_type), void >::type + float_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift rshift) + { + BOOST_STATIC_WARNING(sizeof(boost::uintmax_t) >= sizeof(Div_type)); + std::sort(first, last); + } + + //specialized comparison + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare> + inline typename boost::enable_if_c< sizeof(size_t) >= sizeof(Div_type), + void >::type + float_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift rshift, Compare comp) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + float_sort_rec<RandomAccessIter, Div_type, Right_shift, Compare, + size_t> + (first, last, bin_cache, 0, bin_sizes, rshift, comp); + } + + //max-sized integer with specialized comparison + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare> + inline typename boost::enable_if_c< sizeof(size_t) < sizeof(Div_type) + && sizeof(boost::uintmax_t) >= sizeof(Div_type), void >::type + float_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift rshift, Compare comp) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + float_sort_rec<RandomAccessIter, Div_type, Right_shift, Compare, + boost::uintmax_t> + (first, last, bin_cache, 0, bin_sizes, rshift, comp); + } + + //sizeof(Div_type) doesn't match, so use std::sort + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare> + inline typename boost::disable_if_c< sizeof(boost::uintmax_t) >= + sizeof(Div_type), void >::type + float_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift rshift, Compare comp) + { + BOOST_STATIC_WARNING(sizeof(boost::uintmax_t) >= sizeof(Div_type)); + std::sort(first, last, comp); + } + } +} +} +} + +#endif diff --git a/boost/sort/spreadsort/detail/integer_sort.hpp b/boost/sort/spreadsort/detail/integer_sort.hpp index bc14b3585c..6d6886cfd9 100644 --- a/boost/sort/spreadsort/detail/integer_sort.hpp +++ b/boost/sort/spreadsort/detail/integer_sort.hpp @@ -1,494 +1,494 @@ -// Details for templated Spreadsort-based integer_sort.
-
-// Copyright Steven J. Ross 2001 - 2014.
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort for library home page.
-
-/*
-Some improvements suggested by:
-Phil Endecott and Frank Gennari
-*/
-
-#ifndef BOOST_SORT_SPREADSORT_DETAIL_INTEGER_SORT_HPP
-#define BOOST_SORT_SPREADSORT_DETAIL_INTEGER_SORT_HPP
-#include <algorithm>
-#include <vector>
-#include <limits>
-#include <functional>
-#include <boost/static_assert.hpp>
-#include <boost/serialization/static_warning.hpp>
-#include <boost/utility/enable_if.hpp>
-#include <boost/sort/spreadsort/detail/constants.hpp>
-#include <boost/sort/spreadsort/detail/spreadsort_common.hpp>
-#include <boost/cstdint.hpp>
-
-namespace boost {
-namespace sort {
-namespace spreadsort {
- namespace detail {
- // Return true if the list is sorted. Otherwise, find the minimum and
- // maximum using <.
- template <class RandomAccessIter>
- inline bool
- is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last,
- RandomAccessIter & max, RandomAccessIter & min)
- {
- min = max = current;
- //This assumes we have more than 1 element based on prior checks.
- while (!(*(current + 1) < *current)) {
- //If everything is in sorted order, return
- if (++current == last - 1)
- return true;
- }
-
- //The maximum is the last sorted element
- max = current;
- //Start from the first unsorted element
- while (++current < last) {
- if (*max < *current)
- max = current;
- else if (*current < *min)
- min = current;
- }
- return false;
- }
-
- // Return true if the list is sorted. Otherwise, find the minimum and
- // maximum.
- // Use a user-defined comparison operator
- template <class RandomAccessIter, class Compare>
- inline bool
- is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last,
- RandomAccessIter & max, RandomAccessIter & min, Compare comp)
- {
- min = max = current;
- while (!comp(*(current + 1), *current)) {
- //If everything is in sorted order, return
- if (++current == last - 1)
- return true;
- }
-
- //The maximum is the last sorted element
- max = current;
- while (++current < last) {
- if (comp(*max, *current))
- max = current;
- else if (comp(*current, *min))
- min = current;
- }
- return false;
- }
-
- //Gets a non-negative right bit shift to operate as a logarithmic divisor
- template<unsigned log_mean_bin_size>
- inline int
- get_log_divisor(size_t count, int log_range)
- {
- int log_divisor;
- //If we can finish in one iteration without exceeding either
- //(2 to the max_finishing_splits) or n bins, do so
- if ((log_divisor = log_range - rough_log_2_size(count)) <= 0 &&
- log_range <= max_finishing_splits)
- log_divisor = 0;
- else {
- //otherwise divide the data into an optimized number of pieces
- log_divisor += log_mean_bin_size;
- //Cannot exceed max_splits or cache misses slow down bin lookups
- if ((log_range - log_divisor) > max_splits)
- log_divisor = log_range - max_splits;
- }
- return log_divisor;
- }
-
- //Implementation for recursive integer sorting
- template <class RandomAccessIter, class Div_type, class Size_type>
- inline void
- spreadsort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
- , size_t *bin_sizes)
- {
- //This step is roughly 10% of runtime, but it helps avoid worst-case
- //behavior and improve behavior with real data
- //If you know the maximum and minimum ahead of time, you can pass those
- //values in and skip this step for the first iteration
- RandomAccessIter max, min;
- if (is_sorted_or_find_extremes(first, last, max, min))
- return;
- RandomAccessIter * target_bin;
- unsigned log_divisor = get_log_divisor<int_log_mean_bin_size>(
- last - first, rough_log_2_size(Size_type((*max >> 0) - (*min >> 0))));
- Div_type div_min = *min >> log_divisor;
- Div_type div_max = *max >> log_divisor;
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins =
- size_bins(bin_sizes, bin_cache, cache_offset, cache_end, bin_count);
-
- //Calculating the size of each bin; this takes roughly 10% of runtime
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[size_t((*(current++) >> log_divisor) - div_min)]++;
- //Assign the bin positions
- bins[0] = first;
- for (unsigned u = 0; u < bin_count - 1; u++)
- bins[u + 1] = bins[u] + bin_sizes[u];
-
- RandomAccessIter nextbinstart = first;
- //Swap into place
- //This dominates runtime, mostly in the swap and bin lookups
- for (unsigned u = 0; u < bin_count - 1; ++u) {
- RandomAccessIter * local_bin = bins + u;
- nextbinstart += bin_sizes[u];
- //Iterating over each element in this bin
- for (RandomAccessIter current = *local_bin; current < nextbinstart;
- ++current) {
- //Swapping elements in current into place until the correct
- //element has been swapped in
- for (target_bin = (bins + ((*current >> log_divisor) - div_min));
- target_bin != local_bin;
- target_bin = bins + ((*current >> log_divisor) - div_min)) {
- //3-way swap; this is about 1% faster than a 2-way swap
- //The main advantage is less copies are involved per item
- //put in the correct place
- typename std::iterator_traits<RandomAccessIter>::value_type tmp;
- RandomAccessIter b = (*target_bin)++;
- RandomAccessIter * b_bin = bins + ((*b >> log_divisor) - div_min);
- if (b_bin != local_bin) {
- RandomAccessIter c = (*b_bin)++;
- tmp = *c;
- *c = *b;
- }
- else
- tmp = *b;
- *b = *current;
- *current = tmp;
- }
- }
- *local_bin = nextbinstart;
- }
- bins[bin_count - 1] = last;
-
- //If we've bucketsorted, the array is sorted and we should skip recursion
- if (!log_divisor)
- return;
- //log_divisor is the remaining range; calculating the comparison threshold
- size_t max_count =
- get_min_count<int_log_mean_bin_size, int_log_min_split_count,
- int_log_finishing_count>(log_divisor);
-
- //Recursing
- RandomAccessIter lastPos = first;
- for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
- ++u) {
- Size_type count = bin_cache[u] - lastPos;
- //don't sort unless there are at least two items to Compare
- if (count < 2)
- continue;
- //using std::sort if its worst-case is better
- if (count < max_count)
- std::sort(lastPos, bin_cache[u]);
- else
- spreadsort_rec<RandomAccessIter, Div_type, Size_type>(lastPos,
- bin_cache[u],
- bin_cache,
- cache_end,
- bin_sizes);
- }
- }
-
- //Generic bitshift-based 3-way swapping code
- template <class RandomAccessIter, class Div_type, class Right_shift>
- inline void inner_swap_loop(RandomAccessIter * bins,
- const RandomAccessIter & next_bin_start, unsigned ii, Right_shift &rshift
- , const unsigned log_divisor, const Div_type div_min)
- {
- RandomAccessIter * local_bin = bins + ii;
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- for (RandomAccessIter * target_bin =
- (bins + (rshift(*current, log_divisor) - div_min));
- target_bin != local_bin;
- target_bin = bins + (rshift(*current, log_divisor) - div_min)) {
- typename std::iterator_traits<RandomAccessIter>::value_type tmp;
- RandomAccessIter b = (*target_bin)++;
- RandomAccessIter * b_bin =
- bins + (rshift(*b, log_divisor) - div_min);
- //Three-way swap; if the item to be swapped doesn't belong
- //in the current bin, swap it to where it belongs
- if (b_bin != local_bin) {
- RandomAccessIter c = (*b_bin)++;
- tmp = *c;
- *c = *b;
- }
- //Note: we could increment current once the swap is done in this case
- //but that seems to impair performance
- else
- tmp = *b;
- *b = *current;
- *current = tmp;
- }
- }
- *local_bin = next_bin_start;
- }
-
- //Standard swapping wrapper for ascending values
- template <class RandomAccessIter, class Div_type, class Right_shift>
- inline void swap_loop(RandomAccessIter * bins,
- RandomAccessIter & next_bin_start, unsigned ii, Right_shift &rshift
- , const size_t *bin_sizes
- , const unsigned log_divisor, const Div_type div_min)
- {
- next_bin_start += bin_sizes[ii];
- inner_swap_loop<RandomAccessIter, Div_type, Right_shift>(bins,
- next_bin_start, ii, rshift, log_divisor, div_min);
- }
-
- //Functor implementation for recursive sorting
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare, class Size_type, unsigned log_mean_bin_size,
- unsigned log_min_split_count, unsigned log_finishing_count>
- inline void
- spreadsort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
- , size_t *bin_sizes, Right_shift rshift, Compare comp)
- {
- RandomAccessIter max, min;
- if (is_sorted_or_find_extremes(first, last, max, min, comp))
- return;
- unsigned log_divisor = get_log_divisor<log_mean_bin_size>(last - first,
- rough_log_2_size(Size_type(rshift(*max, 0) - rshift(*min, 0))));
- Div_type div_min = rshift(*min, log_divisor);
- Div_type div_max = rshift(*max, log_divisor);
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, bin_count);
-
- //Calculating the size of each bin
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
- bins[0] = first;
- for (unsigned u = 0; u < bin_count - 1; u++)
- bins[u + 1] = bins[u] + bin_sizes[u];
-
- //Swap into place
- RandomAccessIter next_bin_start = first;
- for (unsigned u = 0; u < bin_count - 1; ++u)
- swap_loop<RandomAccessIter, Div_type, Right_shift>(bins, next_bin_start,
- u, rshift, bin_sizes, log_divisor, div_min);
- bins[bin_count - 1] = last;
-
- //If we've bucketsorted, the array is sorted
- if (!log_divisor)
- return;
-
- //Recursing
- size_t max_count = get_min_count<log_mean_bin_size, log_min_split_count,
- log_finishing_count>(log_divisor);
- RandomAccessIter lastPos = first;
- for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
- ++u) {
- size_t count = bin_cache[u] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[u], comp);
- else
- spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
- Size_type, log_mean_bin_size, log_min_split_count, log_finishing_count>
- (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes, rshift, comp);
- }
- }
-
- //Functor implementation for recursive sorting with only Shift overridden
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Size_type, unsigned log_mean_bin_size,
- unsigned log_min_split_count, unsigned log_finishing_count>
- inline void
- spreadsort_rec(RandomAccessIter first, RandomAccessIter last,
- std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
- , size_t *bin_sizes, Right_shift rshift)
- {
- RandomAccessIter max, min;
- if (is_sorted_or_find_extremes(first, last, max, min))
- return;
- unsigned log_divisor = get_log_divisor<log_mean_bin_size>(last - first,
- rough_log_2_size(Size_type(rshift(*max, 0) - rshift(*min, 0))));
- Div_type div_min = rshift(*min, log_divisor);
- Div_type div_max = rshift(*max, log_divisor);
- unsigned bin_count = unsigned(div_max - div_min) + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, bin_count);
-
- //Calculating the size of each bin
- for (RandomAccessIter current = first; current != last;)
- bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
- bins[0] = first;
- for (unsigned u = 0; u < bin_count - 1; u++)
- bins[u + 1] = bins[u] + bin_sizes[u];
-
- //Swap into place
- RandomAccessIter nextbinstart = first;
- for (unsigned ii = 0; ii < bin_count - 1; ++ii)
- swap_loop<RandomAccessIter, Div_type, Right_shift>(bins, nextbinstart,
- ii, rshift, bin_sizes, log_divisor, div_min);
- bins[bin_count - 1] = last;
-
- //If we've bucketsorted, the array is sorted
- if (!log_divisor)
- return;
-
- //Recursing
- size_t max_count = get_min_count<log_mean_bin_size, log_min_split_count,
- log_finishing_count>(log_divisor);
- RandomAccessIter lastPos = first;
- for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
- ++u) {
- size_t count = bin_cache[u] - lastPos;
- if (count < 2)
- continue;
- if (count < max_count)
- std::sort(lastPos, bin_cache[u]);
- else
- spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Size_type,
- log_mean_bin_size, log_min_split_count, log_finishing_count>(lastPos,
- bin_cache[u], bin_cache, cache_end, bin_sizes, rshift);
- }
- }
-
- //Holds the bin vector and makes the initial recursive call
- template <class RandomAccessIter, class Div_type>
- //Only use spreadsort if the integer can fit in a size_t
- inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t),
- void >::type
- integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- spreadsort_rec<RandomAccessIter, Div_type, size_t>(first, last,
- bin_cache, 0, bin_sizes);
- }
-
- //Holds the bin vector and makes the initial recursive call
- template <class RandomAccessIter, class Div_type>
- //Only use spreadsort if the integer can fit in a uintmax_t
- inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t))
- && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
- integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- spreadsort_rec<RandomAccessIter, Div_type, boost::uintmax_t>(first,
- last, bin_cache, 0, bin_sizes);
- }
-
- template <class RandomAccessIter, class Div_type>
- inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t)
- || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
- //defaulting to std::sort when integer_sort won't work
- integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type)
- {
- //Warning that we're using std::sort, even though integer_sort was called
- BOOST_STATIC_WARNING( sizeof(Div_type) <= sizeof(size_t) );
- std::sort(first, last);
- }
-
-
- //Same for the full functor version
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare>
- //Only use spreadsort if the integer can fit in a size_t
- inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t),
- void >::type
- integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift shift, Compare comp)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
- size_t, int_log_mean_bin_size, int_log_min_split_count,
- int_log_finishing_count>
- (first, last, bin_cache, 0, bin_sizes, shift, comp);
- }
-
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare>
- //Only use spreadsort if the integer can fit in a uintmax_t
- inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t))
- && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
- integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift shift, Compare comp)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
- boost::uintmax_t, int_log_mean_bin_size,
- int_log_min_split_count, int_log_finishing_count>
- (first, last, bin_cache, 0, bin_sizes, shift, comp);
- }
-
- template <class RandomAccessIter, class Div_type, class Right_shift,
- class Compare>
- inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t)
- || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
- //defaulting to std::sort when integer_sort won't work
- integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift shift, Compare comp)
- {
- //Warning that we're using std::sort, even though integer_sort was called
- BOOST_STATIC_WARNING( sizeof(Div_type) <= sizeof(size_t) );
- std::sort(first, last, comp);
- }
-
-
- //Same for the right shift version
- template <class RandomAccessIter, class Div_type, class Right_shift>
- //Only use spreadsort if the integer can fit in a size_t
- inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t),
- void >::type
- integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift shift)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- spreadsort_rec<RandomAccessIter, Div_type, Right_shift, size_t,
- int_log_mean_bin_size, int_log_min_split_count,
- int_log_finishing_count>
- (first, last, bin_cache, 0, bin_sizes, shift);
- }
-
- template <class RandomAccessIter, class Div_type, class Right_shift>
- //Only use spreadsort if the integer can fit in a uintmax_t
- inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t))
- && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
- integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift shift)
- {
- size_t bin_sizes[1 << max_finishing_splits];
- std::vector<RandomAccessIter> bin_cache;
- spreadsort_rec<RandomAccessIter, Div_type, Right_shift,
- boost::uintmax_t, int_log_mean_bin_size,
- int_log_min_split_count, int_log_finishing_count>
- (first, last, bin_cache, 0, bin_sizes, shift);
- }
-
- template <class RandomAccessIter, class Div_type, class Right_shift>
- inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t)
- || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
- //defaulting to std::sort when integer_sort won't work
- integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
- Right_shift shift)
- {
- //Warning that we're using std::sort, even though integer_sort was called
- BOOST_STATIC_WARNING( sizeof(Div_type) <= sizeof(size_t) );
- std::sort(first, last);
- }
- }
-}
-}
-}
-
-#endif
+// Details for templated Spreadsort-based integer_sort. + +// Copyright Steven J. Ross 2001 - 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort for library home page. + +/* +Some improvements suggested by: +Phil Endecott and Frank Gennari +*/ + +#ifndef BOOST_SORT_SPREADSORT_DETAIL_INTEGER_SORT_HPP +#define BOOST_SORT_SPREADSORT_DETAIL_INTEGER_SORT_HPP +#include <algorithm> +#include <vector> +#include <limits> +#include <functional> +#include <boost/static_assert.hpp> +#include <boost/serialization/static_warning.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/sort/spreadsort/detail/constants.hpp> +#include <boost/sort/spreadsort/detail/spreadsort_common.hpp> +#include <boost/cstdint.hpp> + +namespace boost { +namespace sort { +namespace spreadsort { + namespace detail { + // Return true if the list is sorted. Otherwise, find the minimum and + // maximum using <. + template <class RandomAccessIter> + inline bool + is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last, + RandomAccessIter & max, RandomAccessIter & min) + { + min = max = current; + //This assumes we have more than 1 element based on prior checks. + while (!(*(current + 1) < *current)) { + //If everything is in sorted order, return + if (++current == last - 1) + return true; + } + + //The maximum is the last sorted element + max = current; + //Start from the first unsorted element + while (++current < last) { + if (*max < *current) + max = current; + else if (*current < *min) + min = current; + } + return false; + } + + // Return true if the list is sorted. Otherwise, find the minimum and + // maximum. + // Use a user-defined comparison operator + template <class RandomAccessIter, class Compare> + inline bool + is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last, + RandomAccessIter & max, RandomAccessIter & min, Compare comp) + { + min = max = current; + while (!comp(*(current + 1), *current)) { + //If everything is in sorted order, return + if (++current == last - 1) + return true; + } + + //The maximum is the last sorted element + max = current; + while (++current < last) { + if (comp(*max, *current)) + max = current; + else if (comp(*current, *min)) + min = current; + } + return false; + } + + //Gets a non-negative right bit shift to operate as a logarithmic divisor + template<unsigned log_mean_bin_size> + inline int + get_log_divisor(size_t count, int log_range) + { + int log_divisor; + //If we can finish in one iteration without exceeding either + //(2 to the max_finishing_splits) or n bins, do so + if ((log_divisor = log_range - rough_log_2_size(count)) <= 0 && + log_range <= max_finishing_splits) + log_divisor = 0; + else { + //otherwise divide the data into an optimized number of pieces + log_divisor += log_mean_bin_size; + //Cannot exceed max_splits or cache misses slow down bin lookups + if ((log_range - log_divisor) > max_splits) + log_divisor = log_range - max_splits; + } + return log_divisor; + } + + //Implementation for recursive integer sorting + template <class RandomAccessIter, class Div_type, class Size_type> + inline void + spreadsort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset + , size_t *bin_sizes) + { + //This step is roughly 10% of runtime, but it helps avoid worst-case + //behavior and improve behavior with real data + //If you know the maximum and minimum ahead of time, you can pass those + //values in and skip this step for the first iteration + RandomAccessIter max, min; + if (is_sorted_or_find_extremes(first, last, max, min)) + return; + RandomAccessIter * target_bin; + unsigned log_divisor = get_log_divisor<int_log_mean_bin_size>( + last - first, rough_log_2_size(Size_type((*max >> 0) - (*min >> 0)))); + Div_type div_min = *min >> log_divisor; + Div_type div_max = *max >> log_divisor; + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = + size_bins(bin_sizes, bin_cache, cache_offset, cache_end, bin_count); + + //Calculating the size of each bin; this takes roughly 10% of runtime + for (RandomAccessIter current = first; current != last;) + bin_sizes[size_t((*(current++) >> log_divisor) - div_min)]++; + //Assign the bin positions + bins[0] = first; + for (unsigned u = 0; u < bin_count - 1; u++) + bins[u + 1] = bins[u] + bin_sizes[u]; + + RandomAccessIter nextbinstart = first; + //Swap into place + //This dominates runtime, mostly in the swap and bin lookups + for (unsigned u = 0; u < bin_count - 1; ++u) { + RandomAccessIter * local_bin = bins + u; + nextbinstart += bin_sizes[u]; + //Iterating over each element in this bin + for (RandomAccessIter current = *local_bin; current < nextbinstart; + ++current) { + //Swapping elements in current into place until the correct + //element has been swapped in + for (target_bin = (bins + ((*current >> log_divisor) - div_min)); + target_bin != local_bin; + target_bin = bins + ((*current >> log_divisor) - div_min)) { + //3-way swap; this is about 1% faster than a 2-way swap + //The main advantage is less copies are involved per item + //put in the correct place + typename std::iterator_traits<RandomAccessIter>::value_type tmp; + RandomAccessIter b = (*target_bin)++; + RandomAccessIter * b_bin = bins + ((*b >> log_divisor) - div_min); + if (b_bin != local_bin) { + RandomAccessIter c = (*b_bin)++; + tmp = *c; + *c = *b; + } + else + tmp = *b; + *b = *current; + *current = tmp; + } + } + *local_bin = nextbinstart; + } + bins[bin_count - 1] = last; + + //If we've bucketsorted, the array is sorted and we should skip recursion + if (!log_divisor) + return; + //log_divisor is the remaining range; calculating the comparison threshold + size_t max_count = + get_min_count<int_log_mean_bin_size, int_log_min_split_count, + int_log_finishing_count>(log_divisor); + + //Recursing + RandomAccessIter lastPos = first; + for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u], + ++u) { + Size_type count = bin_cache[u] - lastPos; + //don't sort unless there are at least two items to Compare + if (count < 2) + continue; + //using std::sort if its worst-case is better + if (count < max_count) + std::sort(lastPos, bin_cache[u]); + else + spreadsort_rec<RandomAccessIter, Div_type, Size_type>(lastPos, + bin_cache[u], + bin_cache, + cache_end, + bin_sizes); + } + } + + //Generic bitshift-based 3-way swapping code + template <class RandomAccessIter, class Div_type, class Right_shift> + inline void inner_swap_loop(RandomAccessIter * bins, + const RandomAccessIter & next_bin_start, unsigned ii, Right_shift &rshift + , const unsigned log_divisor, const Div_type div_min) + { + RandomAccessIter * local_bin = bins + ii; + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + for (RandomAccessIter * target_bin = + (bins + (rshift(*current, log_divisor) - div_min)); + target_bin != local_bin; + target_bin = bins + (rshift(*current, log_divisor) - div_min)) { + typename std::iterator_traits<RandomAccessIter>::value_type tmp; + RandomAccessIter b = (*target_bin)++; + RandomAccessIter * b_bin = + bins + (rshift(*b, log_divisor) - div_min); + //Three-way swap; if the item to be swapped doesn't belong + //in the current bin, swap it to where it belongs + if (b_bin != local_bin) { + RandomAccessIter c = (*b_bin)++; + tmp = *c; + *c = *b; + } + //Note: we could increment current once the swap is done in this case + //but that seems to impair performance + else + tmp = *b; + *b = *current; + *current = tmp; + } + } + *local_bin = next_bin_start; + } + + //Standard swapping wrapper for ascending values + template <class RandomAccessIter, class Div_type, class Right_shift> + inline void swap_loop(RandomAccessIter * bins, + RandomAccessIter & next_bin_start, unsigned ii, Right_shift &rshift + , const size_t *bin_sizes + , const unsigned log_divisor, const Div_type div_min) + { + next_bin_start += bin_sizes[ii]; + inner_swap_loop<RandomAccessIter, Div_type, Right_shift>(bins, + next_bin_start, ii, rshift, log_divisor, div_min); + } + + //Functor implementation for recursive sorting + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare, class Size_type, unsigned log_mean_bin_size, + unsigned log_min_split_count, unsigned log_finishing_count> + inline void + spreadsort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset + , size_t *bin_sizes, Right_shift rshift, Compare comp) + { + RandomAccessIter max, min; + if (is_sorted_or_find_extremes(first, last, max, min, comp)) + return; + unsigned log_divisor = get_log_divisor<log_mean_bin_size>(last - first, + rough_log_2_size(Size_type(rshift(*max, 0) - rshift(*min, 0)))); + Div_type div_min = rshift(*min, log_divisor); + Div_type div_max = rshift(*max, log_divisor); + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, bin_count); + + //Calculating the size of each bin + for (RandomAccessIter current = first; current != last;) + bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++; + bins[0] = first; + for (unsigned u = 0; u < bin_count - 1; u++) + bins[u + 1] = bins[u] + bin_sizes[u]; + + //Swap into place + RandomAccessIter next_bin_start = first; + for (unsigned u = 0; u < bin_count - 1; ++u) + swap_loop<RandomAccessIter, Div_type, Right_shift>(bins, next_bin_start, + u, rshift, bin_sizes, log_divisor, div_min); + bins[bin_count - 1] = last; + + //If we've bucketsorted, the array is sorted + if (!log_divisor) + return; + + //Recursing + size_t max_count = get_min_count<log_mean_bin_size, log_min_split_count, + log_finishing_count>(log_divisor); + RandomAccessIter lastPos = first; + for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u], + ++u) { + size_t count = bin_cache[u] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[u], comp); + else + spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare, + Size_type, log_mean_bin_size, log_min_split_count, log_finishing_count> + (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes, rshift, comp); + } + } + + //Functor implementation for recursive sorting with only Shift overridden + template <class RandomAccessIter, class Div_type, class Right_shift, + class Size_type, unsigned log_mean_bin_size, + unsigned log_min_split_count, unsigned log_finishing_count> + inline void + spreadsort_rec(RandomAccessIter first, RandomAccessIter last, + std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset + , size_t *bin_sizes, Right_shift rshift) + { + RandomAccessIter max, min; + if (is_sorted_or_find_extremes(first, last, max, min)) + return; + unsigned log_divisor = get_log_divisor<log_mean_bin_size>(last - first, + rough_log_2_size(Size_type(rshift(*max, 0) - rshift(*min, 0)))); + Div_type div_min = rshift(*min, log_divisor); + Div_type div_max = rshift(*max, log_divisor); + unsigned bin_count = unsigned(div_max - div_min) + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, bin_count); + + //Calculating the size of each bin + for (RandomAccessIter current = first; current != last;) + bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++; + bins[0] = first; + for (unsigned u = 0; u < bin_count - 1; u++) + bins[u + 1] = bins[u] + bin_sizes[u]; + + //Swap into place + RandomAccessIter nextbinstart = first; + for (unsigned ii = 0; ii < bin_count - 1; ++ii) + swap_loop<RandomAccessIter, Div_type, Right_shift>(bins, nextbinstart, + ii, rshift, bin_sizes, log_divisor, div_min); + bins[bin_count - 1] = last; + + //If we've bucketsorted, the array is sorted + if (!log_divisor) + return; + + //Recursing + size_t max_count = get_min_count<log_mean_bin_size, log_min_split_count, + log_finishing_count>(log_divisor); + RandomAccessIter lastPos = first; + for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u], + ++u) { + size_t count = bin_cache[u] - lastPos; + if (count < 2) + continue; + if (count < max_count) + std::sort(lastPos, bin_cache[u]); + else + spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Size_type, + log_mean_bin_size, log_min_split_count, log_finishing_count>(lastPos, + bin_cache[u], bin_cache, cache_end, bin_sizes, rshift); + } + } + + //Holds the bin vector and makes the initial recursive call + template <class RandomAccessIter, class Div_type> + //Only use spreadsort if the integer can fit in a size_t + inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t), + void >::type + integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + spreadsort_rec<RandomAccessIter, Div_type, size_t>(first, last, + bin_cache, 0, bin_sizes); + } + + //Holds the bin vector and makes the initial recursive call + template <class RandomAccessIter, class Div_type> + //Only use spreadsort if the integer can fit in a uintmax_t + inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t)) + && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type + integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + spreadsort_rec<RandomAccessIter, Div_type, boost::uintmax_t>(first, + last, bin_cache, 0, bin_sizes); + } + + template <class RandomAccessIter, class Div_type> + inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t) + || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type + //defaulting to std::sort when integer_sort won't work + integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type) + { + //Warning that we're using std::sort, even though integer_sort was called + BOOST_STATIC_WARNING( sizeof(Div_type) <= sizeof(size_t) ); + std::sort(first, last); + } + + + //Same for the full functor version + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare> + //Only use spreadsort if the integer can fit in a size_t + inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t), + void >::type + integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift shift, Compare comp) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare, + size_t, int_log_mean_bin_size, int_log_min_split_count, + int_log_finishing_count> + (first, last, bin_cache, 0, bin_sizes, shift, comp); + } + + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare> + //Only use spreadsort if the integer can fit in a uintmax_t + inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t)) + && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type + integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift shift, Compare comp) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare, + boost::uintmax_t, int_log_mean_bin_size, + int_log_min_split_count, int_log_finishing_count> + (first, last, bin_cache, 0, bin_sizes, shift, comp); + } + + template <class RandomAccessIter, class Div_type, class Right_shift, + class Compare> + inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t) + || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type + //defaulting to std::sort when integer_sort won't work + integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift shift, Compare comp) + { + //Warning that we're using std::sort, even though integer_sort was called + BOOST_STATIC_WARNING( sizeof(Div_type) <= sizeof(size_t) ); + std::sort(first, last, comp); + } + + + //Same for the right shift version + template <class RandomAccessIter, class Div_type, class Right_shift> + //Only use spreadsort if the integer can fit in a size_t + inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t), + void >::type + integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift shift) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + spreadsort_rec<RandomAccessIter, Div_type, Right_shift, size_t, + int_log_mean_bin_size, int_log_min_split_count, + int_log_finishing_count> + (first, last, bin_cache, 0, bin_sizes, shift); + } + + template <class RandomAccessIter, class Div_type, class Right_shift> + //Only use spreadsort if the integer can fit in a uintmax_t + inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t)) + && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type + integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift shift) + { + size_t bin_sizes[1 << max_finishing_splits]; + std::vector<RandomAccessIter> bin_cache; + spreadsort_rec<RandomAccessIter, Div_type, Right_shift, + boost::uintmax_t, int_log_mean_bin_size, + int_log_min_split_count, int_log_finishing_count> + (first, last, bin_cache, 0, bin_sizes, shift); + } + + template <class RandomAccessIter, class Div_type, class Right_shift> + inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t) + || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type + //defaulting to std::sort when integer_sort won't work + integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type, + Right_shift shift) + { + //Warning that we're using std::sort, even though integer_sort was called + BOOST_STATIC_WARNING( sizeof(Div_type) <= sizeof(size_t) ); + std::sort(first, last); + } + } +} +} +} + +#endif diff --git a/boost/sort/spreadsort/detail/spreadsort_common.hpp b/boost/sort/spreadsort/detail/spreadsort_common.hpp index 7b299ad5f3..7917fddae0 100644 --- a/boost/sort/spreadsort/detail/spreadsort_common.hpp +++ b/boost/sort/spreadsort/detail/spreadsort_common.hpp @@ -1,124 +1,124 @@ -// Contains get_min_count, the core optimization of the spreadsort algorithm.
-// Also has other helper functions commonly useful across variants.
-
-// Copyright Steven J. Ross 2001 - 2014.
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort for library home page.
-
-/*
-Some improvements suggested by:
-Phil Endecott and Frank Gennari
-*/
-
-#ifndef BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_COMMON_HPP
-#define BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_COMMON_HPP
-#include <algorithm>
-#include <vector>
-#include <cstring>
-#include <limits>
-#include <functional>
-#include <boost/static_assert.hpp>
-#include <boost/serialization/static_warning.hpp>
-#include <boost/sort/spreadsort/detail/constants.hpp>
-#include <boost/cstdint.hpp>
-
-namespace boost {
-namespace sort {
-namespace spreadsort {
- namespace detail {
- //This only works on unsigned data types
- template <typename T>
- inline unsigned
- rough_log_2_size(const T& input)
- {
- unsigned result = 0;
- //The && is necessary on some compilers to avoid infinite loops
- //it doesn't significantly impair performance
- while ((input >> result) && (result < (8*sizeof(T)))) ++result;
- return result;
- }
-
- //Gets the minimum size to call spreadsort on to control worst-case runtime.
- //This is called for a set of bins, instead of bin-by-bin, to minimize
- //runtime overhead.
- //This could be replaced by a lookup table of sizeof(Div_type)*8 but this
- //function is more general.
- template<unsigned log_mean_bin_size,
- unsigned log_min_split_count, unsigned log_finishing_count>
- inline size_t
- get_min_count(unsigned log_range)
- {
- const size_t typed_one = 1;
- const unsigned min_size = log_mean_bin_size + log_min_split_count;
- //Assuring that constants have valid settings
- BOOST_STATIC_ASSERT(log_min_split_count <= max_splits &&
- log_min_split_count > 0);
- BOOST_STATIC_ASSERT(max_splits > 1 &&
- max_splits < (8 * sizeof(unsigned)));
- BOOST_STATIC_ASSERT(max_finishing_splits >= max_splits &&
- max_finishing_splits < (8 * sizeof(unsigned)));
- BOOST_STATIC_ASSERT(log_mean_bin_size >= 0);
- BOOST_STATIC_ASSERT(log_finishing_count >= 0);
- //if we can complete in one iteration, do so
- //This first check allows the compiler to optimize never-executed code out
- if (log_finishing_count < min_size) {
- if (log_range <= min_size && log_range <= max_splits) {
- //Return no smaller than a certain minimum limit
- if (log_range <= log_finishing_count)
- return typed_one << log_finishing_count;
- return typed_one << log_range;
- }
- }
- const unsigned base_iterations = max_splits - log_min_split_count;
- //sum of n to n + x = ((x + 1) * (n + (n + x)))/2 + log_mean_bin_size
- const unsigned base_range =
- ((base_iterations + 1) * (max_splits + log_min_split_count))/2
- + log_mean_bin_size;
- //Calculating the required number of iterations, and returning
- //1 << (iteration_count + min_size)
- if (log_range < base_range) {
- unsigned result = log_min_split_count;
- for (unsigned offset = min_size; offset < log_range;
- offset += ++result);
- //Preventing overflow; this situation shouldn't occur
- if ((result + log_mean_bin_size) >= (8 * sizeof(size_t)))
- return typed_one << ((8 * sizeof(size_t)) - 1);
- return typed_one << (result + log_mean_bin_size);
- }
- //A quick division can calculate the worst-case runtime for larger ranges
- unsigned remainder = log_range - base_range;
- //the max_splits - 1 is used to calculate the ceiling of the division
- unsigned bit_length = ((((max_splits - 1) + remainder)/max_splits)
- + base_iterations + min_size);
- //Preventing overflow; this situation shouldn't occur
- if (bit_length >= (8 * sizeof(size_t)))
- return typed_one << ((8 * sizeof(size_t)) - 1);
- //n(log_range)/max_splits + C, optimizing worst-case performance
- return typed_one << bit_length;
- }
-
- // Resizes the bin cache and bin sizes, and initializes each bin size to 0.
- // This generates the memory overhead to use in radix sorting.
- template <class RandomAccessIter>
- inline RandomAccessIter *
- size_bins(size_t *bin_sizes, std::vector<RandomAccessIter>
- &bin_cache, unsigned cache_offset, unsigned &cache_end, unsigned bin_count)
- {
- // Clear the bin sizes
- for (size_t u = 0; u < bin_count; u++)
- bin_sizes[u] = 0;
- //Make sure there is space for the bins
- cache_end = cache_offset + bin_count;
- if (cache_end > bin_cache.size())
- bin_cache.resize(cache_end);
- return &(bin_cache[cache_offset]);
- }
- }
-}
-}
-}
-
-#endif
+// Contains get_min_count, the core optimization of the spreadsort algorithm. +// Also has other helper functions commonly useful across variants. + +// Copyright Steven J. Ross 2001 - 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort for library home page. + +/* +Some improvements suggested by: +Phil Endecott and Frank Gennari +*/ + +#ifndef BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_COMMON_HPP +#define BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_COMMON_HPP +#include <algorithm> +#include <vector> +#include <cstring> +#include <limits> +#include <functional> +#include <boost/static_assert.hpp> +#include <boost/serialization/static_warning.hpp> +#include <boost/sort/spreadsort/detail/constants.hpp> +#include <boost/cstdint.hpp> + +namespace boost { +namespace sort { +namespace spreadsort { + namespace detail { + //This only works on unsigned data types + template <typename T> + inline unsigned + rough_log_2_size(const T& input) + { + unsigned result = 0; + //The && is necessary on some compilers to avoid infinite loops + //it doesn't significantly impair performance + while ((input >> result) && (result < (8*sizeof(T)))) ++result; + return result; + } + + //Gets the minimum size to call spreadsort on to control worst-case runtime. + //This is called for a set of bins, instead of bin-by-bin, to minimize + //runtime overhead. + //This could be replaced by a lookup table of sizeof(Div_type)*8 but this + //function is more general. + template<unsigned log_mean_bin_size, + unsigned log_min_split_count, unsigned log_finishing_count> + inline size_t + get_min_count(unsigned log_range) + { + const size_t typed_one = 1; + const unsigned min_size = log_mean_bin_size + log_min_split_count; + //Assuring that constants have valid settings + BOOST_STATIC_ASSERT(log_min_split_count <= max_splits && + log_min_split_count > 0); + BOOST_STATIC_ASSERT(max_splits > 1 && + max_splits < (8 * sizeof(unsigned))); + BOOST_STATIC_ASSERT(max_finishing_splits >= max_splits && + max_finishing_splits < (8 * sizeof(unsigned))); + BOOST_STATIC_ASSERT(log_mean_bin_size >= 0); + BOOST_STATIC_ASSERT(log_finishing_count >= 0); + //if we can complete in one iteration, do so + //This first check allows the compiler to optimize never-executed code out + if (log_finishing_count < min_size) { + if (log_range <= min_size && log_range <= max_splits) { + //Return no smaller than a certain minimum limit + if (log_range <= log_finishing_count) + return typed_one << log_finishing_count; + return typed_one << log_range; + } + } + const unsigned base_iterations = max_splits - log_min_split_count; + //sum of n to n + x = ((x + 1) * (n + (n + x)))/2 + log_mean_bin_size + const unsigned base_range = + ((base_iterations + 1) * (max_splits + log_min_split_count))/2 + + log_mean_bin_size; + //Calculating the required number of iterations, and returning + //1 << (iteration_count + min_size) + if (log_range < base_range) { + unsigned result = log_min_split_count; + for (unsigned offset = min_size; offset < log_range; + offset += ++result); + //Preventing overflow; this situation shouldn't occur + if ((result + log_mean_bin_size) >= (8 * sizeof(size_t))) + return typed_one << ((8 * sizeof(size_t)) - 1); + return typed_one << (result + log_mean_bin_size); + } + //A quick division can calculate the worst-case runtime for larger ranges + unsigned remainder = log_range - base_range; + //the max_splits - 1 is used to calculate the ceiling of the division + unsigned bit_length = ((((max_splits - 1) + remainder)/max_splits) + + base_iterations + min_size); + //Preventing overflow; this situation shouldn't occur + if (bit_length >= (8 * sizeof(size_t))) + return typed_one << ((8 * sizeof(size_t)) - 1); + //n(log_range)/max_splits + C, optimizing worst-case performance + return typed_one << bit_length; + } + + // Resizes the bin cache and bin sizes, and initializes each bin size to 0. + // This generates the memory overhead to use in radix sorting. + template <class RandomAccessIter> + inline RandomAccessIter * + size_bins(size_t *bin_sizes, std::vector<RandomAccessIter> + &bin_cache, unsigned cache_offset, unsigned &cache_end, unsigned bin_count) + { + // Clear the bin sizes + for (size_t u = 0; u < bin_count; u++) + bin_sizes[u] = 0; + //Make sure there is space for the bins + cache_end = cache_offset + bin_count; + if (cache_end > bin_cache.size()) + bin_cache.resize(cache_end); + return &(bin_cache[cache_offset]); + } + } +} +} +} + +#endif diff --git a/boost/sort/spreadsort/detail/string_sort.hpp b/boost/sort/spreadsort/detail/string_sort.hpp index 582508fb7b..a548ebefa5 100644 --- a/boost/sort/spreadsort/detail/string_sort.hpp +++ b/boost/sort/spreadsort/detail/string_sort.hpp @@ -1,819 +1,819 @@ -// Details for a templated general-case hybrid-radix string_sort.
-
-// Copyright Steven J. Ross 2001 - 2014.
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort for library home page.
-
-/*
-Some improvements suggested by:
-Phil Endecott and Frank Gennari
-*/
-
-#ifndef BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_HPP
-#define BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_HPP
-#include <algorithm>
-#include <vector>
-#include <cstring>
-#include <limits>
-#include <functional>
-#include <boost/static_assert.hpp>
-#include <boost/serialization/static_warning.hpp>
-#include <boost/utility/enable_if.hpp>
-#include <boost/sort/spreadsort/detail/constants.hpp>
-#include <boost/sort/spreadsort/detail/spreadsort_common.hpp>
-#include <boost/cstdint.hpp>
-
-namespace boost {
-namespace sort {
-namespace spreadsort {
- namespace detail {
- static const int max_step_size = 64;
-
- //Offsetting on identical characters. This function works a chunk of
- //characters at a time for cache efficiency and optimal worst-case
- //performance.
- template<class RandomAccessIter, class Unsigned_char_type>
- inline void
- update_offset(RandomAccessIter first, RandomAccessIter finish,
- size_t &char_offset)
- {
- const int char_size = sizeof(Unsigned_char_type);
- size_t nextOffset = char_offset;
- int step_size = max_step_size / char_size;
- while (true) {
- RandomAccessIter curr = first;
- do {
- //Ignore empties, but if the nextOffset would exceed the length or
- //not match, exit; we've found the last matching character
- //This will reduce the step_size if the current step doesn't match.
- if ((*curr).size() > char_offset) {
- if((*curr).size() <= (nextOffset + step_size)) {
- step_size = (*curr).size() - nextOffset - 1;
- if (step_size < 1) {
- char_offset = nextOffset;
- return;
- }
- }
- const int step_byte_size = step_size * char_size;
- if (memcmp(curr->data() + nextOffset, first->data() + nextOffset,
- step_byte_size) != 0) {
- if (step_size == 1) {
- char_offset = nextOffset;
- return;
- }
- step_size = (step_size > 4) ? 4 : 1;
- continue;
- }
- }
- ++curr;
- } while (curr != finish);
- nextOffset += step_size;
- }
- }
-
- //Offsetting on identical characters. This function works a character
- //at a time for optimal worst-case performance.
- template<class RandomAccessIter, class Get_char, class Get_length>
- inline void
- update_offset(RandomAccessIter first, RandomAccessIter finish,
- size_t &char_offset, Get_char getchar, Get_length length)
- {
- size_t nextOffset = char_offset;
- while (true) {
- RandomAccessIter curr = first;
- do {
- //ignore empties, but if the nextOffset would exceed the length or
- //not match, exit; we've found the last matching character
- if (length(*curr) > char_offset && (length(*curr) <= (nextOffset + 1)
- || getchar((*curr), nextOffset) != getchar((*first), nextOffset))) {
- char_offset = nextOffset;
- return;
- }
- } while (++curr != finish);
- ++nextOffset;
- }
- }
-
- //This comparison functor assumes strings are identical up to char_offset
- template<class Data_type, class Unsigned_char_type>
- struct offset_less_than {
- offset_less_than(size_t char_offset) : fchar_offset(char_offset){}
- inline bool operator()(const Data_type &x, const Data_type &y) const
- {
- size_t minSize = (std::min)(x.size(), y.size());
- for (size_t u = fchar_offset; u < minSize; ++u) {
- BOOST_STATIC_ASSERT(sizeof(x[u]) == sizeof(Unsigned_char_type));
- if (static_cast<Unsigned_char_type>(x[u]) !=
- static_cast<Unsigned_char_type>(y[u])) {
- return static_cast<Unsigned_char_type>(x[u]) <
- static_cast<Unsigned_char_type>(y[u]);
- }
- }
- return x.size() < y.size();
- }
- size_t fchar_offset;
- };
-
- //Compares strings assuming they are identical up to char_offset
- template<class Data_type, class Unsigned_char_type>
- struct offset_greater_than {
- offset_greater_than(size_t char_offset) : fchar_offset(char_offset){}
- inline bool operator()(const Data_type &x, const Data_type &y) const
- {
- size_t minSize = (std::min)(x.size(), y.size());
- for (size_t u = fchar_offset; u < minSize; ++u) {
- BOOST_STATIC_ASSERT(sizeof(x[u]) == sizeof(Unsigned_char_type));
- if (static_cast<Unsigned_char_type>(x[u]) !=
- static_cast<Unsigned_char_type>(y[u])) {
- return static_cast<Unsigned_char_type>(x[u]) >
- static_cast<Unsigned_char_type>(y[u]);
- }
- }
- return x.size() > y.size();
- }
- size_t fchar_offset;
- };
-
- //This comparison functor assumes strings are identical up to char_offset
- template<class Data_type, class Get_char, class Get_length>
- struct offset_char_less_than {
- offset_char_less_than(size_t char_offset) : fchar_offset(char_offset){}
- inline bool operator()(const Data_type &x, const Data_type &y) const
- {
- size_t minSize = (std::min)(length(x), length(y));
- for (size_t u = fchar_offset; u < minSize; ++u) {
- if (getchar(x, u) != getchar(y, u)) {
- return getchar(x, u) < getchar(y, u);
- }
- }
- return length(x) < length(y);
- }
- size_t fchar_offset;
- Get_char getchar;
- Get_length length;
- };
-
- //String sorting recursive implementation
- template <class RandomAccessIter, class Unsigned_char_type>
- inline void
- string_sort_rec(RandomAccessIter first, RandomAccessIter last,
- size_t char_offset,
- std::vector<RandomAccessIter> &bin_cache,
- unsigned cache_offset, size_t *bin_sizes)
- {
- typedef typename std::iterator_traits<RandomAccessIter>::value_type
- Data_type;
- //This section makes handling of long identical substrings much faster
- //with a mild average performance impact.
- //Iterate to the end of the empties. If all empty, return
- while ((*first).size() <= char_offset) {
- if (++first == last)
- return;
- }
- RandomAccessIter finish = last - 1;
- //Getting the last non-empty
- for (;(*finish).size() <= char_offset; --finish);
- ++finish;
- //Offsetting on identical characters. This section works
- //a few characters at a time for optimal worst-case performance.
- update_offset<RandomAccessIter, Unsigned_char_type>(first, finish,
- char_offset);
-
- const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
- //Equal worst-case of radix and comparison is when bin_count = n*log(n).
- const unsigned max_size = bin_count;
- const unsigned membin_count = bin_count + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, membin_count) + 1;
-
- //Calculating the size of each bin; this takes roughly 10% of runtime
- for (RandomAccessIter current = first; current != last; ++current) {
- if ((*current).size() <= char_offset) {
- bin_sizes[0]++;
- }
- else
- bin_sizes[static_cast<Unsigned_char_type>((*current)[char_offset])
- + 1]++;
- }
- //Assign the bin positions
- bin_cache[cache_offset] = first;
- for (unsigned u = 0; u < membin_count - 1; u++)
- bin_cache[cache_offset + u + 1] =
- bin_cache[cache_offset + u] + bin_sizes[u];
-
- //Swap into place
- RandomAccessIter next_bin_start = first;
- //handling empty bins
- RandomAccessIter * local_bin = &(bin_cache[cache_offset]);
- next_bin_start += bin_sizes[0];
- RandomAccessIter * target_bin;
- //Iterating over each element in the bin of empties
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //empties belong in this bin
- while ((*current).size() > char_offset) {
- target_bin =
- bins + static_cast<Unsigned_char_type>((*current)[char_offset]);
- iter_swap(current, (*target_bin)++);
- }
- }
- *local_bin = next_bin_start;
- //iterate backwards to find the last bin with elements in it
- //this saves iterations in multiple loops
- unsigned last_bin = bin_count - 1;
- for (; last_bin && !bin_sizes[last_bin + 1]; --last_bin);
- //This dominates runtime, mostly in the swap and bin lookups
- for (unsigned u = 0; u < last_bin; ++u) {
- local_bin = bins + u;
- next_bin_start += bin_sizes[u + 1];
- //Iterating over each element in this bin
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //Swapping into place until the correct element has been swapped in
- for (target_bin = bins + static_cast<Unsigned_char_type>
- ((*current)[char_offset]); target_bin != local_bin;
- target_bin = bins + static_cast<Unsigned_char_type>
- ((*current)[char_offset])) iter_swap(current, (*target_bin)++);
- }
- *local_bin = next_bin_start;
- }
- bins[last_bin] = last;
- //Recursing
- RandomAccessIter lastPos = bin_cache[cache_offset];
- //Skip this loop for empties
- for (unsigned u = cache_offset + 1; u < cache_offset + last_bin + 2;
- lastPos = bin_cache[u], ++u) {
- size_t count = bin_cache[u] - lastPos;
- //don't sort unless there are at least two items to Compare
- if (count < 2)
- continue;
- //using std::sort if its worst-case is better
- if (count < max_size)
- std::sort(lastPos, bin_cache[u],
- offset_less_than<Data_type, Unsigned_char_type>(char_offset + 1));
- else
- string_sort_rec<RandomAccessIter, Unsigned_char_type>(lastPos,
- bin_cache[u], char_offset + 1, bin_cache, cache_end, bin_sizes);
- }
- }
-
- //Sorts strings in reverse order, with empties at the end
- template <class RandomAccessIter, class Unsigned_char_type>
- inline void
- reverse_string_sort_rec(RandomAccessIter first, RandomAccessIter last,
- size_t char_offset,
- std::vector<RandomAccessIter> &bin_cache,
- unsigned cache_offset,
- size_t *bin_sizes)
- {
- typedef typename std::iterator_traits<RandomAccessIter>::value_type
- Data_type;
- //This section makes handling of long identical substrings much faster
- //with a mild average performance impact.
- RandomAccessIter curr = first;
- //Iterate to the end of the empties. If all empty, return
- while ((*curr).size() <= char_offset) {
- if (++curr == last)
- return;
- }
- //Getting the last non-empty
- while ((*(--last)).size() <= char_offset);
- ++last;
- //Offsetting on identical characters. This section works
- //a few characters at a time for optimal worst-case performance.
- update_offset<RandomAccessIter, Unsigned_char_type>(curr, last,
- char_offset);
- RandomAccessIter * target_bin;
-
- const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
- //Equal worst-case of radix and comparison when bin_count = n*log(n).
- const unsigned max_size = bin_count;
- const unsigned membin_count = bin_count + 1;
- const unsigned max_bin = bin_count - 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, membin_count);
- RandomAccessIter * end_bin = &(bin_cache[cache_offset + max_bin]);
-
- //Calculating the size of each bin; this takes roughly 10% of runtime
- for (RandomAccessIter current = first; current != last; ++current) {
- if ((*current).size() <= char_offset) {
- bin_sizes[bin_count]++;
- }
- else
- bin_sizes[max_bin - static_cast<Unsigned_char_type>
- ((*current)[char_offset])]++;
- }
- //Assign the bin positions
- bin_cache[cache_offset] = first;
- for (unsigned u = 0; u < membin_count - 1; u++)
- bin_cache[cache_offset + u + 1] =
- bin_cache[cache_offset + u] + bin_sizes[u];
-
- //Swap into place
- RandomAccessIter next_bin_start = last;
- //handling empty bins
- RandomAccessIter * local_bin = &(bin_cache[cache_offset + bin_count]);
- RandomAccessIter lastFull = *local_bin;
- //Iterating over each element in the bin of empties
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //empties belong in this bin
- while ((*current).size() > char_offset) {
- target_bin =
- end_bin - static_cast<Unsigned_char_type>((*current)[char_offset]);
- iter_swap(current, (*target_bin)++);
- }
- }
- *local_bin = next_bin_start;
- next_bin_start = first;
- //iterate backwards to find the last non-empty bin
- //this saves iterations in multiple loops
- unsigned last_bin = max_bin;
- for (; last_bin && !bin_sizes[last_bin]; --last_bin);
- //This dominates runtime, mostly in the swap and bin lookups
- for (unsigned u = 0; u < last_bin; ++u) {
- local_bin = bins + u;
- next_bin_start += bin_sizes[u];
- //Iterating over each element in this bin
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //Swapping into place until the correct element has been swapped in
- for (target_bin =
- end_bin - static_cast<Unsigned_char_type>((*current)[char_offset]);
- target_bin != local_bin;
- target_bin =
- end_bin - static_cast<Unsigned_char_type>((*current)[char_offset]))
- iter_swap(current, (*target_bin)++);
- }
- *local_bin = next_bin_start;
- }
- bins[last_bin] = lastFull;
- //Recursing
- RandomAccessIter lastPos = first;
- //Skip this loop for empties
- for (unsigned u = cache_offset; u <= cache_offset + last_bin;
- lastPos = bin_cache[u], ++u) {
- size_t count = bin_cache[u] - lastPos;
- //don't sort unless there are at least two items to Compare
- if (count < 2)
- continue;
- //using std::sort if its worst-case is better
- if (count < max_size)
- std::sort(lastPos, bin_cache[u], offset_greater_than<Data_type,
- Unsigned_char_type>(char_offset + 1));
- else
- reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type>
- (lastPos, bin_cache[u], char_offset + 1, bin_cache, cache_end, bin_sizes);
- }
- }
-
- //String sorting recursive implementation
- template <class RandomAccessIter, class Unsigned_char_type, class Get_char,
- class Get_length>
- inline void
- string_sort_rec(RandomAccessIter first, RandomAccessIter last,
- size_t char_offset, std::vector<RandomAccessIter> &bin_cache,
- unsigned cache_offset, size_t *bin_sizes,
- Get_char getchar, Get_length length)
- {
- typedef typename std::iterator_traits<RandomAccessIter>::value_type
- Data_type;
- //This section makes handling of long identical substrings much faster
- //with a mild average performance impact.
- //Iterate to the end of the empties. If all empty, return
- while (length(*first) <= char_offset) {
- if (++first == last)
- return;
- }
- RandomAccessIter finish = last - 1;
- //Getting the last non-empty
- for (;length(*finish) <= char_offset; --finish);
- ++finish;
- update_offset(first, finish, char_offset, getchar, length);
-
- const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
- //Equal worst-case of radix and comparison is when bin_count = n*log(n).
- const unsigned max_size = bin_count;
- const unsigned membin_count = bin_count + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, membin_count) + 1;
-
- //Calculating the size of each bin; this takes roughly 10% of runtime
- for (RandomAccessIter current = first; current != last; ++current) {
- if (length(*current) <= char_offset) {
- bin_sizes[0]++;
- }
- else
- bin_sizes[getchar((*current), char_offset) + 1]++;
- }
- //Assign the bin positions
- bin_cache[cache_offset] = first;
- for (unsigned u = 0; u < membin_count - 1; u++)
- bin_cache[cache_offset + u + 1] =
- bin_cache[cache_offset + u] + bin_sizes[u];
-
- //Swap into place
- RandomAccessIter next_bin_start = first;
- //handling empty bins
- RandomAccessIter * local_bin = &(bin_cache[cache_offset]);
- next_bin_start += bin_sizes[0];
- RandomAccessIter * target_bin;
- //Iterating over each element in the bin of empties
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //empties belong in this bin
- while (length(*current) > char_offset) {
- target_bin = bins + getchar((*current), char_offset);
- iter_swap(current, (*target_bin)++);
- }
- }
- *local_bin = next_bin_start;
- //iterate backwards to find the last bin with elements in it
- //this saves iterations in multiple loops
- unsigned last_bin = bin_count - 1;
- for (; last_bin && !bin_sizes[last_bin + 1]; --last_bin);
- //This dominates runtime, mostly in the swap and bin lookups
- for (unsigned ii = 0; ii < last_bin; ++ii) {
- local_bin = bins + ii;
- next_bin_start += bin_sizes[ii + 1];
- //Iterating over each element in this bin
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //Swapping into place until the correct element has been swapped in
- for (target_bin = bins + getchar((*current), char_offset);
- target_bin != local_bin;
- target_bin = bins + getchar((*current), char_offset))
- iter_swap(current, (*target_bin)++);
- }
- *local_bin = next_bin_start;
- }
- bins[last_bin] = last;
-
- //Recursing
- RandomAccessIter lastPos = bin_cache[cache_offset];
- //Skip this loop for empties
- for (unsigned u = cache_offset + 1; u < cache_offset + last_bin + 2;
- lastPos = bin_cache[u], ++u) {
- size_t count = bin_cache[u] - lastPos;
- //don't sort unless there are at least two items to Compare
- if (count < 2)
- continue;
- //using std::sort if its worst-case is better
- if (count < max_size)
- std::sort(lastPos, bin_cache[u], offset_char_less_than<Data_type,
- Get_char, Get_length>(char_offset + 1));
- else
- string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char,
- Get_length>(lastPos, bin_cache[u], char_offset + 1, bin_cache,
- cache_end, bin_sizes, getchar, length);
- }
- }
-
- //String sorting recursive implementation
- template <class RandomAccessIter, class Unsigned_char_type, class Get_char,
- class Get_length, class Compare>
- inline void
- string_sort_rec(RandomAccessIter first, RandomAccessIter last,
- size_t char_offset, std::vector<RandomAccessIter> &bin_cache,
- unsigned cache_offset, size_t *bin_sizes,
- Get_char getchar, Get_length length, Compare comp)
- {
- //This section makes handling of long identical substrings much faster
- //with a mild average performance impact.
- //Iterate to the end of the empties. If all empty, return
- while (length(*first) <= char_offset) {
- if (++first == last)
- return;
- }
- RandomAccessIter finish = last - 1;
- //Getting the last non-empty
- for (;length(*finish) <= char_offset; --finish);
- ++finish;
- update_offset(first, finish, char_offset, getchar, length);
-
- const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
- //Equal worst-case of radix and comparison is when bin_count = n*log(n).
- const unsigned max_size = bin_count;
- const unsigned membin_count = bin_count + 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, membin_count) + 1;
-
- //Calculating the size of each bin; this takes roughly 10% of runtime
- for (RandomAccessIter current = first; current != last; ++current) {
- if (length(*current) <= char_offset) {
- bin_sizes[0]++;
- }
- else
- bin_sizes[getchar((*current), char_offset) + 1]++;
- }
- //Assign the bin positions
- bin_cache[cache_offset] = first;
- for (unsigned u = 0; u < membin_count - 1; u++)
- bin_cache[cache_offset + u + 1] =
- bin_cache[cache_offset + u] + bin_sizes[u];
-
- //Swap into place
- RandomAccessIter next_bin_start = first;
- //handling empty bins
- RandomAccessIter * local_bin = &(bin_cache[cache_offset]);
- next_bin_start += bin_sizes[0];
- RandomAccessIter * target_bin;
- //Iterating over each element in the bin of empties
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //empties belong in this bin
- while (length(*current) > char_offset) {
- target_bin = bins + getchar((*current), char_offset);
- iter_swap(current, (*target_bin)++);
- }
- }
- *local_bin = next_bin_start;
- //iterate backwards to find the last bin with elements in it
- //this saves iterations in multiple loops
- unsigned last_bin = bin_count - 1;
- for (; last_bin && !bin_sizes[last_bin + 1]; --last_bin);
- //This dominates runtime, mostly in the swap and bin lookups
- for (unsigned u = 0; u < last_bin; ++u) {
- local_bin = bins + u;
- next_bin_start += bin_sizes[u + 1];
- //Iterating over each element in this bin
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //Swapping into place until the correct element has been swapped in
- for (target_bin = bins + getchar((*current), char_offset);
- target_bin != local_bin;
- target_bin = bins + getchar((*current), char_offset))
- iter_swap(current, (*target_bin)++);
- }
- *local_bin = next_bin_start;
- }
- bins[last_bin] = last;
-
- //Recursing
- RandomAccessIter lastPos = bin_cache[cache_offset];
- //Skip this loop for empties
- for (unsigned u = cache_offset + 1; u < cache_offset + last_bin + 2;
- lastPos = bin_cache[u], ++u) {
- size_t count = bin_cache[u] - lastPos;
- //don't sort unless there are at least two items to Compare
- if (count < 2)
- continue;
- //using std::sort if its worst-case is better
- if (count < max_size)
- std::sort(lastPos, bin_cache[u], comp);
- else
- string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char,
- Get_length, Compare>
- (lastPos, bin_cache[u], char_offset + 1, bin_cache, cache_end,
- bin_sizes, getchar, length, comp);
- }
- }
-
- //Sorts strings in reverse order, with empties at the end
- template <class RandomAccessIter, class Unsigned_char_type, class Get_char,
- class Get_length, class Compare>
- inline void
- reverse_string_sort_rec(RandomAccessIter first, RandomAccessIter last,
- size_t char_offset, std::vector<RandomAccessIter> &bin_cache,
- unsigned cache_offset, size_t *bin_sizes,
- Get_char getchar, Get_length length, Compare comp)
- {
- //This section makes handling of long identical substrings much faster
- //with a mild average performance impact.
- RandomAccessIter curr = first;
- //Iterate to the end of the empties. If all empty, return
- while (length(*curr) <= char_offset) {
- if (++curr == last)
- return;
- }
- //Getting the last non-empty
- while (length(*(--last)) <= char_offset);
- ++last;
- //Offsetting on identical characters. This section works
- //a character at a time for optimal worst-case performance.
- update_offset(curr, last, char_offset, getchar, length);
-
- const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8));
- //Equal worst-case of radix and comparison is when bin_count = n*log(n).
- const unsigned max_size = bin_count;
- const unsigned membin_count = bin_count + 1;
- const unsigned max_bin = bin_count - 1;
- unsigned cache_end;
- RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
- cache_end, membin_count);
- RandomAccessIter *end_bin = &(bin_cache[cache_offset + max_bin]);
-
- //Calculating the size of each bin; this takes roughly 10% of runtime
- for (RandomAccessIter current = first; current != last; ++current) {
- if (length(*current) <= char_offset) {
- bin_sizes[bin_count]++;
- }
- else
- bin_sizes[max_bin - getchar((*current), char_offset)]++;
- }
- //Assign the bin positions
- bin_cache[cache_offset] = first;
- for (unsigned u = 0; u < membin_count - 1; u++)
- bin_cache[cache_offset + u + 1] =
- bin_cache[cache_offset + u] + bin_sizes[u];
-
- //Swap into place
- RandomAccessIter next_bin_start = last;
- //handling empty bins
- RandomAccessIter * local_bin = &(bin_cache[cache_offset + bin_count]);
- RandomAccessIter lastFull = *local_bin;
- RandomAccessIter * target_bin;
- //Iterating over each element in the bin of empties
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //empties belong in this bin
- while (length(*current) > char_offset) {
- target_bin = end_bin - getchar((*current), char_offset);
- iter_swap(current, (*target_bin)++);
- }
- }
- *local_bin = next_bin_start;
- next_bin_start = first;
- //iterate backwards to find the last bin with elements in it
- //this saves iterations in multiple loops
- unsigned last_bin = max_bin;
- for (; last_bin && !bin_sizes[last_bin]; --last_bin);
- //This dominates runtime, mostly in the swap and bin lookups
- for (unsigned u = 0; u < last_bin; ++u) {
- local_bin = bins + u;
- next_bin_start += bin_sizes[u];
- //Iterating over each element in this bin
- for (RandomAccessIter current = *local_bin; current < next_bin_start;
- ++current) {
- //Swapping into place until the correct element has been swapped in
- for (target_bin = end_bin - getchar((*current), char_offset);
- target_bin != local_bin;
- target_bin = end_bin - getchar((*current), char_offset))
- iter_swap(current, (*target_bin)++);
- }
- *local_bin = next_bin_start;
- }
- bins[last_bin] = lastFull;
- //Recursing
- RandomAccessIter lastPos = first;
- //Skip this loop for empties
- for (unsigned u = cache_offset; u <= cache_offset + last_bin;
- lastPos = bin_cache[u], ++u) {
- size_t count = bin_cache[u] - lastPos;
- //don't sort unless there are at least two items to Compare
- if (count < 2)
- continue;
- //using std::sort if its worst-case is better
- if (count < max_size)
- std::sort(lastPos, bin_cache[u], comp);
- else
- reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type,
- Get_char, Get_length, Compare>
- (lastPos, bin_cache[u], char_offset + 1, bin_cache, cache_end,
- bin_sizes, getchar, length, comp);
- }
- }
-
- //Holds the bin vector and makes the initial recursive call
- template <class RandomAccessIter, class Unsigned_char_type>
- inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
- >::type
- string_sort(RandomAccessIter first, RandomAccessIter last,
- Unsigned_char_type)
- {
- size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
- std::vector<RandomAccessIter> bin_cache;
- string_sort_rec<RandomAccessIter, Unsigned_char_type>
- (first, last, 0, bin_cache, 0, bin_sizes);
- }
-
- template <class RandomAccessIter, class Unsigned_char_type>
- inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void
- >::type
- string_sort(RandomAccessIter first, RandomAccessIter last,
- Unsigned_char_type)
- {
- //Warning that we're using std::sort, even though string_sort was called
- BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
- std::sort(first, last);
- }
-
- //Holds the bin vector and makes the initial recursive call
- template <class RandomAccessIter, class Unsigned_char_type>
- inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
- >::type
- reverse_string_sort(RandomAccessIter first, RandomAccessIter last,
- Unsigned_char_type)
- {
- size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
- std::vector<RandomAccessIter> bin_cache;
- reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type>
- (first, last, 0, bin_cache, 0, bin_sizes);
- }
-
- template <class RandomAccessIter, class Unsigned_char_type>
- inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void
- >::type
- reverse_string_sort(RandomAccessIter first, RandomAccessIter last,
- Unsigned_char_type)
- {
- typedef typename std::iterator_traits<RandomAccessIter>::value_type
- Data_type;
- //Warning that we're using std::sort, even though string_sort was called
- BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
- std::sort(first, last, std::greater<Data_type>());
- }
-
- //Holds the bin vector and makes the initial recursive call
- template <class RandomAccessIter, class Get_char, class Get_length,
- class Unsigned_char_type>
- inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
- >::type
- string_sort(RandomAccessIter first, RandomAccessIter last,
- Get_char getchar, Get_length length, Unsigned_char_type)
- {
- size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
- std::vector<RandomAccessIter> bin_cache;
- string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char,
- Get_length>(first, last, 0, bin_cache, 0, bin_sizes, getchar, length);
- }
-
- template <class RandomAccessIter, class Get_char, class Get_length,
- class Unsigned_char_type>
- inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void
- >::type
- string_sort(RandomAccessIter first, RandomAccessIter last,
- Get_char getchar, Get_length length, Unsigned_char_type)
- {
- //Warning that we're using std::sort, even though string_sort was called
- BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
- std::sort(first, last);
- }
-
- //Holds the bin vector and makes the initial recursive call
- template <class RandomAccessIter, class Get_char, class Get_length,
- class Compare, class Unsigned_char_type>
- inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
- >::type
- string_sort(RandomAccessIter first, RandomAccessIter last,
- Get_char getchar, Get_length length, Compare comp, Unsigned_char_type)
- {
- size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
- std::vector<RandomAccessIter> bin_cache;
- string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char
- , Get_length, Compare>
- (first, last, 0, bin_cache, 0, bin_sizes, getchar, length, comp);
- }
-
- //disable_if_c was refusing to compile, so rewrote to use enable_if_c
- template <class RandomAccessIter, class Get_char, class Get_length,
- class Compare, class Unsigned_char_type>
- inline typename boost::enable_if_c< (sizeof(Unsigned_char_type) > 2), void
- >::type
- string_sort(RandomAccessIter first, RandomAccessIter last,
- Get_char getchar, Get_length length, Compare comp, Unsigned_char_type)
- {
- //Warning that we're using std::sort, even though string_sort was called
- BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
- std::sort(first, last, comp);
- }
-
- //Holds the bin vector and makes the initial recursive call
- template <class RandomAccessIter, class Get_char, class Get_length,
- class Compare, class Unsigned_char_type>
- inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void
- >::type
- reverse_string_sort(RandomAccessIter first, RandomAccessIter last,
- Get_char getchar, Get_length length, Compare comp, Unsigned_char_type)
- {
- size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1];
- std::vector<RandomAccessIter> bin_cache;
- reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char,
- Get_length, Compare>
- (first, last, 0, bin_cache, 0, bin_sizes, getchar, length, comp);
- }
-
- template <class RandomAccessIter, class Get_char, class Get_length,
- class Compare, class Unsigned_char_type>
- inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void
- >::type
- reverse_string_sort(RandomAccessIter first, RandomAccessIter last,
- Get_char getchar, Get_length length, Compare comp, Unsigned_char_type)
- {
- //Warning that we're using std::sort, even though string_sort was called
- BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 );
- std::sort(first, last, comp);
- }
- }
-}
-}
-}
-
-#endif
+// Details for a templated general-case hybrid-radix string_sort. + +// Copyright Steven J. Ross 2001 - 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort for library home page. + +/* +Some improvements suggested by: +Phil Endecott and Frank Gennari +*/ + +#ifndef BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_HPP +#define BOOST_SORT_SPREADSORT_DETAIL_SPREAD_SORT_HPP +#include <algorithm> +#include <vector> +#include <cstring> +#include <limits> +#include <functional> +#include <boost/static_assert.hpp> +#include <boost/serialization/static_warning.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/sort/spreadsort/detail/constants.hpp> +#include <boost/sort/spreadsort/detail/spreadsort_common.hpp> +#include <boost/cstdint.hpp> + +namespace boost { +namespace sort { +namespace spreadsort { + namespace detail { + static const int max_step_size = 64; + + //Offsetting on identical characters. This function works a chunk of + //characters at a time for cache efficiency and optimal worst-case + //performance. + template<class RandomAccessIter, class Unsigned_char_type> + inline void + update_offset(RandomAccessIter first, RandomAccessIter finish, + size_t &char_offset) + { + const int char_size = sizeof(Unsigned_char_type); + size_t nextOffset = char_offset; + int step_size = max_step_size / char_size; + while (true) { + RandomAccessIter curr = first; + do { + //Ignore empties, but if the nextOffset would exceed the length or + //not match, exit; we've found the last matching character + //This will reduce the step_size if the current step doesn't match. + if ((*curr).size() > char_offset) { + if((*curr).size() <= (nextOffset + step_size)) { + step_size = (*curr).size() - nextOffset - 1; + if (step_size < 1) { + char_offset = nextOffset; + return; + } + } + const int step_byte_size = step_size * char_size; + if (memcmp(curr->data() + nextOffset, first->data() + nextOffset, + step_byte_size) != 0) { + if (step_size == 1) { + char_offset = nextOffset; + return; + } + step_size = (step_size > 4) ? 4 : 1; + continue; + } + } + ++curr; + } while (curr != finish); + nextOffset += step_size; + } + } + + //Offsetting on identical characters. This function works a character + //at a time for optimal worst-case performance. + template<class RandomAccessIter, class Get_char, class Get_length> + inline void + update_offset(RandomAccessIter first, RandomAccessIter finish, + size_t &char_offset, Get_char get_character, Get_length length) + { + size_t nextOffset = char_offset; + while (true) { + RandomAccessIter curr = first; + do { + //ignore empties, but if the nextOffset would exceed the length or + //not match, exit; we've found the last matching character + if (length(*curr) > char_offset && (length(*curr) <= (nextOffset + 1) + || get_character((*curr), nextOffset) != get_character((*first), nextOffset))) { + char_offset = nextOffset; + return; + } + } while (++curr != finish); + ++nextOffset; + } + } + + //This comparison functor assumes strings are identical up to char_offset + template<class Data_type, class Unsigned_char_type> + struct offset_less_than { + offset_less_than(size_t char_offset) : fchar_offset(char_offset){} + inline bool operator()(const Data_type &x, const Data_type &y) const + { + size_t minSize = (std::min)(x.size(), y.size()); + for (size_t u = fchar_offset; u < minSize; ++u) { + BOOST_STATIC_ASSERT(sizeof(x[u]) == sizeof(Unsigned_char_type)); + if (static_cast<Unsigned_char_type>(x[u]) != + static_cast<Unsigned_char_type>(y[u])) { + return static_cast<Unsigned_char_type>(x[u]) < + static_cast<Unsigned_char_type>(y[u]); + } + } + return x.size() < y.size(); + } + size_t fchar_offset; + }; + + //Compares strings assuming they are identical up to char_offset + template<class Data_type, class Unsigned_char_type> + struct offset_greater_than { + offset_greater_than(size_t char_offset) : fchar_offset(char_offset){} + inline bool operator()(const Data_type &x, const Data_type &y) const + { + size_t minSize = (std::min)(x.size(), y.size()); + for (size_t u = fchar_offset; u < minSize; ++u) { + BOOST_STATIC_ASSERT(sizeof(x[u]) == sizeof(Unsigned_char_type)); + if (static_cast<Unsigned_char_type>(x[u]) != + static_cast<Unsigned_char_type>(y[u])) { + return static_cast<Unsigned_char_type>(x[u]) > + static_cast<Unsigned_char_type>(y[u]); + } + } + return x.size() > y.size(); + } + size_t fchar_offset; + }; + + //This comparison functor assumes strings are identical up to char_offset + template<class Data_type, class Get_char, class Get_length> + struct offset_char_less_than { + offset_char_less_than(size_t char_offset) : fchar_offset(char_offset){} + inline bool operator()(const Data_type &x, const Data_type &y) const + { + size_t minSize = (std::min)(length(x), length(y)); + for (size_t u = fchar_offset; u < minSize; ++u) { + if (get_character(x, u) != get_character(y, u)) { + return get_character(x, u) < get_character(y, u); + } + } + return length(x) < length(y); + } + size_t fchar_offset; + Get_char get_character; + Get_length length; + }; + + //String sorting recursive implementation + template <class RandomAccessIter, class Unsigned_char_type> + inline void + string_sort_rec(RandomAccessIter first, RandomAccessIter last, + size_t char_offset, + std::vector<RandomAccessIter> &bin_cache, + unsigned cache_offset, size_t *bin_sizes) + { + typedef typename std::iterator_traits<RandomAccessIter>::value_type + Data_type; + //This section makes handling of long identical substrings much faster + //with a mild average performance impact. + //Iterate to the end of the empties. If all empty, return + while ((*first).size() <= char_offset) { + if (++first == last) + return; + } + RandomAccessIter finish = last - 1; + //Getting the last non-empty + for (;(*finish).size() <= char_offset; --finish); + ++finish; + //Offsetting on identical characters. This section works + //a few characters at a time for optimal worst-case performance. + update_offset<RandomAccessIter, Unsigned_char_type>(first, finish, + char_offset); + + const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8)); + //Equal worst-case of radix and comparison is when bin_count = n*log(n). + const unsigned max_size = bin_count; + const unsigned membin_count = bin_count + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, membin_count) + 1; + + //Calculating the size of each bin; this takes roughly 10% of runtime + for (RandomAccessIter current = first; current != last; ++current) { + if ((*current).size() <= char_offset) { + bin_sizes[0]++; + } + else + bin_sizes[static_cast<Unsigned_char_type>((*current)[char_offset]) + + 1]++; + } + //Assign the bin positions + bin_cache[cache_offset] = first; + for (unsigned u = 0; u < membin_count - 1; u++) + bin_cache[cache_offset + u + 1] = + bin_cache[cache_offset + u] + bin_sizes[u]; + + //Swap into place + RandomAccessIter next_bin_start = first; + //handling empty bins + RandomAccessIter * local_bin = &(bin_cache[cache_offset]); + next_bin_start += bin_sizes[0]; + RandomAccessIter * target_bin; + //Iterating over each element in the bin of empties + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //empties belong in this bin + while ((*current).size() > char_offset) { + target_bin = + bins + static_cast<Unsigned_char_type>((*current)[char_offset]); + iter_swap(current, (*target_bin)++); + } + } + *local_bin = next_bin_start; + //iterate backwards to find the last bin with elements in it + //this saves iterations in multiple loops + unsigned last_bin = bin_count - 1; + for (; last_bin && !bin_sizes[last_bin + 1]; --last_bin); + //This dominates runtime, mostly in the swap and bin lookups + for (unsigned u = 0; u < last_bin; ++u) { + local_bin = bins + u; + next_bin_start += bin_sizes[u + 1]; + //Iterating over each element in this bin + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //Swapping into place until the correct element has been swapped in + for (target_bin = bins + static_cast<Unsigned_char_type> + ((*current)[char_offset]); target_bin != local_bin; + target_bin = bins + static_cast<Unsigned_char_type> + ((*current)[char_offset])) iter_swap(current, (*target_bin)++); + } + *local_bin = next_bin_start; + } + bins[last_bin] = last; + //Recursing + RandomAccessIter lastPos = bin_cache[cache_offset]; + //Skip this loop for empties + for (unsigned u = cache_offset + 1; u < cache_offset + last_bin + 2; + lastPos = bin_cache[u], ++u) { + size_t count = bin_cache[u] - lastPos; + //don't sort unless there are at least two items to Compare + if (count < 2) + continue; + //using std::sort if its worst-case is better + if (count < max_size) + std::sort(lastPos, bin_cache[u], + offset_less_than<Data_type, Unsigned_char_type>(char_offset + 1)); + else + string_sort_rec<RandomAccessIter, Unsigned_char_type>(lastPos, + bin_cache[u], char_offset + 1, bin_cache, cache_end, bin_sizes); + } + } + + //Sorts strings in reverse order, with empties at the end + template <class RandomAccessIter, class Unsigned_char_type> + inline void + reverse_string_sort_rec(RandomAccessIter first, RandomAccessIter last, + size_t char_offset, + std::vector<RandomAccessIter> &bin_cache, + unsigned cache_offset, + size_t *bin_sizes) + { + typedef typename std::iterator_traits<RandomAccessIter>::value_type + Data_type; + //This section makes handling of long identical substrings much faster + //with a mild average performance impact. + RandomAccessIter curr = first; + //Iterate to the end of the empties. If all empty, return + while ((*curr).size() <= char_offset) { + if (++curr == last) + return; + } + //Getting the last non-empty + while ((*(--last)).size() <= char_offset); + ++last; + //Offsetting on identical characters. This section works + //a few characters at a time for optimal worst-case performance. + update_offset<RandomAccessIter, Unsigned_char_type>(curr, last, + char_offset); + RandomAccessIter * target_bin; + + const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8)); + //Equal worst-case of radix and comparison when bin_count = n*log(n). + const unsigned max_size = bin_count; + const unsigned membin_count = bin_count + 1; + const unsigned max_bin = bin_count - 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, membin_count); + RandomAccessIter * end_bin = &(bin_cache[cache_offset + max_bin]); + + //Calculating the size of each bin; this takes roughly 10% of runtime + for (RandomAccessIter current = first; current != last; ++current) { + if ((*current).size() <= char_offset) { + bin_sizes[bin_count]++; + } + else + bin_sizes[max_bin - static_cast<Unsigned_char_type> + ((*current)[char_offset])]++; + } + //Assign the bin positions + bin_cache[cache_offset] = first; + for (unsigned u = 0; u < membin_count - 1; u++) + bin_cache[cache_offset + u + 1] = + bin_cache[cache_offset + u] + bin_sizes[u]; + + //Swap into place + RandomAccessIter next_bin_start = last; + //handling empty bins + RandomAccessIter * local_bin = &(bin_cache[cache_offset + bin_count]); + RandomAccessIter lastFull = *local_bin; + //Iterating over each element in the bin of empties + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //empties belong in this bin + while ((*current).size() > char_offset) { + target_bin = + end_bin - static_cast<Unsigned_char_type>((*current)[char_offset]); + iter_swap(current, (*target_bin)++); + } + } + *local_bin = next_bin_start; + next_bin_start = first; + //iterate backwards to find the last non-empty bin + //this saves iterations in multiple loops + unsigned last_bin = max_bin; + for (; last_bin && !bin_sizes[last_bin]; --last_bin); + //This dominates runtime, mostly in the swap and bin lookups + for (unsigned u = 0; u < last_bin; ++u) { + local_bin = bins + u; + next_bin_start += bin_sizes[u]; + //Iterating over each element in this bin + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //Swapping into place until the correct element has been swapped in + for (target_bin = + end_bin - static_cast<Unsigned_char_type>((*current)[char_offset]); + target_bin != local_bin; + target_bin = + end_bin - static_cast<Unsigned_char_type>((*current)[char_offset])) + iter_swap(current, (*target_bin)++); + } + *local_bin = next_bin_start; + } + bins[last_bin] = lastFull; + //Recursing + RandomAccessIter lastPos = first; + //Skip this loop for empties + for (unsigned u = cache_offset; u <= cache_offset + last_bin; + lastPos = bin_cache[u], ++u) { + size_t count = bin_cache[u] - lastPos; + //don't sort unless there are at least two items to Compare + if (count < 2) + continue; + //using std::sort if its worst-case is better + if (count < max_size) + std::sort(lastPos, bin_cache[u], offset_greater_than<Data_type, + Unsigned_char_type>(char_offset + 1)); + else + reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type> + (lastPos, bin_cache[u], char_offset + 1, bin_cache, cache_end, bin_sizes); + } + } + + //String sorting recursive implementation + template <class RandomAccessIter, class Unsigned_char_type, class Get_char, + class Get_length> + inline void + string_sort_rec(RandomAccessIter first, RandomAccessIter last, + size_t char_offset, std::vector<RandomAccessIter> &bin_cache, + unsigned cache_offset, size_t *bin_sizes, + Get_char get_character, Get_length length) + { + typedef typename std::iterator_traits<RandomAccessIter>::value_type + Data_type; + //This section makes handling of long identical substrings much faster + //with a mild average performance impact. + //Iterate to the end of the empties. If all empty, return + while (length(*first) <= char_offset) { + if (++first == last) + return; + } + RandomAccessIter finish = last - 1; + //Getting the last non-empty + for (;length(*finish) <= char_offset; --finish); + ++finish; + update_offset(first, finish, char_offset, get_character, length); + + const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8)); + //Equal worst-case of radix and comparison is when bin_count = n*log(n). + const unsigned max_size = bin_count; + const unsigned membin_count = bin_count + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, membin_count) + 1; + + //Calculating the size of each bin; this takes roughly 10% of runtime + for (RandomAccessIter current = first; current != last; ++current) { + if (length(*current) <= char_offset) { + bin_sizes[0]++; + } + else + bin_sizes[get_character((*current), char_offset) + 1]++; + } + //Assign the bin positions + bin_cache[cache_offset] = first; + for (unsigned u = 0; u < membin_count - 1; u++) + bin_cache[cache_offset + u + 1] = + bin_cache[cache_offset + u] + bin_sizes[u]; + + //Swap into place + RandomAccessIter next_bin_start = first; + //handling empty bins + RandomAccessIter * local_bin = &(bin_cache[cache_offset]); + next_bin_start += bin_sizes[0]; + RandomAccessIter * target_bin; + //Iterating over each element in the bin of empties + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //empties belong in this bin + while (length(*current) > char_offset) { + target_bin = bins + get_character((*current), char_offset); + iter_swap(current, (*target_bin)++); + } + } + *local_bin = next_bin_start; + //iterate backwards to find the last bin with elements in it + //this saves iterations in multiple loops + unsigned last_bin = bin_count - 1; + for (; last_bin && !bin_sizes[last_bin + 1]; --last_bin); + //This dominates runtime, mostly in the swap and bin lookups + for (unsigned ii = 0; ii < last_bin; ++ii) { + local_bin = bins + ii; + next_bin_start += bin_sizes[ii + 1]; + //Iterating over each element in this bin + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //Swapping into place until the correct element has been swapped in + for (target_bin = bins + get_character((*current), char_offset); + target_bin != local_bin; + target_bin = bins + get_character((*current), char_offset)) + iter_swap(current, (*target_bin)++); + } + *local_bin = next_bin_start; + } + bins[last_bin] = last; + + //Recursing + RandomAccessIter lastPos = bin_cache[cache_offset]; + //Skip this loop for empties + for (unsigned u = cache_offset + 1; u < cache_offset + last_bin + 2; + lastPos = bin_cache[u], ++u) { + size_t count = bin_cache[u] - lastPos; + //don't sort unless there are at least two items to Compare + if (count < 2) + continue; + //using std::sort if its worst-case is better + if (count < max_size) + std::sort(lastPos, bin_cache[u], offset_char_less_than<Data_type, + Get_char, Get_length>(char_offset + 1)); + else + string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char, + Get_length>(lastPos, bin_cache[u], char_offset + 1, bin_cache, + cache_end, bin_sizes, get_character, length); + } + } + + //String sorting recursive implementation + template <class RandomAccessIter, class Unsigned_char_type, class Get_char, + class Get_length, class Compare> + inline void + string_sort_rec(RandomAccessIter first, RandomAccessIter last, + size_t char_offset, std::vector<RandomAccessIter> &bin_cache, + unsigned cache_offset, size_t *bin_sizes, + Get_char get_character, Get_length length, Compare comp) + { + //This section makes handling of long identical substrings much faster + //with a mild average performance impact. + //Iterate to the end of the empties. If all empty, return + while (length(*first) <= char_offset) { + if (++first == last) + return; + } + RandomAccessIter finish = last - 1; + //Getting the last non-empty + for (;length(*finish) <= char_offset; --finish); + ++finish; + update_offset(first, finish, char_offset, get_character, length); + + const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8)); + //Equal worst-case of radix and comparison is when bin_count = n*log(n). + const unsigned max_size = bin_count; + const unsigned membin_count = bin_count + 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, membin_count) + 1; + + //Calculating the size of each bin; this takes roughly 10% of runtime + for (RandomAccessIter current = first; current != last; ++current) { + if (length(*current) <= char_offset) { + bin_sizes[0]++; + } + else + bin_sizes[get_character((*current), char_offset) + 1]++; + } + //Assign the bin positions + bin_cache[cache_offset] = first; + for (unsigned u = 0; u < membin_count - 1; u++) + bin_cache[cache_offset + u + 1] = + bin_cache[cache_offset + u] + bin_sizes[u]; + + //Swap into place + RandomAccessIter next_bin_start = first; + //handling empty bins + RandomAccessIter * local_bin = &(bin_cache[cache_offset]); + next_bin_start += bin_sizes[0]; + RandomAccessIter * target_bin; + //Iterating over each element in the bin of empties + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //empties belong in this bin + while (length(*current) > char_offset) { + target_bin = bins + get_character((*current), char_offset); + iter_swap(current, (*target_bin)++); + } + } + *local_bin = next_bin_start; + //iterate backwards to find the last bin with elements in it + //this saves iterations in multiple loops + unsigned last_bin = bin_count - 1; + for (; last_bin && !bin_sizes[last_bin + 1]; --last_bin); + //This dominates runtime, mostly in the swap and bin lookups + for (unsigned u = 0; u < last_bin; ++u) { + local_bin = bins + u; + next_bin_start += bin_sizes[u + 1]; + //Iterating over each element in this bin + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //Swapping into place until the correct element has been swapped in + for (target_bin = bins + get_character((*current), char_offset); + target_bin != local_bin; + target_bin = bins + get_character((*current), char_offset)) + iter_swap(current, (*target_bin)++); + } + *local_bin = next_bin_start; + } + bins[last_bin] = last; + + //Recursing + RandomAccessIter lastPos = bin_cache[cache_offset]; + //Skip this loop for empties + for (unsigned u = cache_offset + 1; u < cache_offset + last_bin + 2; + lastPos = bin_cache[u], ++u) { + size_t count = bin_cache[u] - lastPos; + //don't sort unless there are at least two items to Compare + if (count < 2) + continue; + //using std::sort if its worst-case is better + if (count < max_size) + std::sort(lastPos, bin_cache[u], comp); + else + string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char, + Get_length, Compare> + (lastPos, bin_cache[u], char_offset + 1, bin_cache, cache_end, + bin_sizes, get_character, length, comp); + } + } + + //Sorts strings in reverse order, with empties at the end + template <class RandomAccessIter, class Unsigned_char_type, class Get_char, + class Get_length, class Compare> + inline void + reverse_string_sort_rec(RandomAccessIter first, RandomAccessIter last, + size_t char_offset, std::vector<RandomAccessIter> &bin_cache, + unsigned cache_offset, size_t *bin_sizes, + Get_char get_character, Get_length length, Compare comp) + { + //This section makes handling of long identical substrings much faster + //with a mild average performance impact. + RandomAccessIter curr = first; + //Iterate to the end of the empties. If all empty, return + while (length(*curr) <= char_offset) { + if (++curr == last) + return; + } + //Getting the last non-empty + while (length(*(--last)) <= char_offset); + ++last; + //Offsetting on identical characters. This section works + //a character at a time for optimal worst-case performance. + update_offset(curr, last, char_offset, get_character, length); + + const unsigned bin_count = (1 << (sizeof(Unsigned_char_type)*8)); + //Equal worst-case of radix and comparison is when bin_count = n*log(n). + const unsigned max_size = bin_count; + const unsigned membin_count = bin_count + 1; + const unsigned max_bin = bin_count - 1; + unsigned cache_end; + RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset, + cache_end, membin_count); + RandomAccessIter *end_bin = &(bin_cache[cache_offset + max_bin]); + + //Calculating the size of each bin; this takes roughly 10% of runtime + for (RandomAccessIter current = first; current != last; ++current) { + if (length(*current) <= char_offset) { + bin_sizes[bin_count]++; + } + else + bin_sizes[max_bin - get_character((*current), char_offset)]++; + } + //Assign the bin positions + bin_cache[cache_offset] = first; + for (unsigned u = 0; u < membin_count - 1; u++) + bin_cache[cache_offset + u + 1] = + bin_cache[cache_offset + u] + bin_sizes[u]; + + //Swap into place + RandomAccessIter next_bin_start = last; + //handling empty bins + RandomAccessIter * local_bin = &(bin_cache[cache_offset + bin_count]); + RandomAccessIter lastFull = *local_bin; + RandomAccessIter * target_bin; + //Iterating over each element in the bin of empties + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //empties belong in this bin + while (length(*current) > char_offset) { + target_bin = end_bin - get_character((*current), char_offset); + iter_swap(current, (*target_bin)++); + } + } + *local_bin = next_bin_start; + next_bin_start = first; + //iterate backwards to find the last bin with elements in it + //this saves iterations in multiple loops + unsigned last_bin = max_bin; + for (; last_bin && !bin_sizes[last_bin]; --last_bin); + //This dominates runtime, mostly in the swap and bin lookups + for (unsigned u = 0; u < last_bin; ++u) { + local_bin = bins + u; + next_bin_start += bin_sizes[u]; + //Iterating over each element in this bin + for (RandomAccessIter current = *local_bin; current < next_bin_start; + ++current) { + //Swapping into place until the correct element has been swapped in + for (target_bin = end_bin - get_character((*current), char_offset); + target_bin != local_bin; + target_bin = end_bin - get_character((*current), char_offset)) + iter_swap(current, (*target_bin)++); + } + *local_bin = next_bin_start; + } + bins[last_bin] = lastFull; + //Recursing + RandomAccessIter lastPos = first; + //Skip this loop for empties + for (unsigned u = cache_offset; u <= cache_offset + last_bin; + lastPos = bin_cache[u], ++u) { + size_t count = bin_cache[u] - lastPos; + //don't sort unless there are at least two items to Compare + if (count < 2) + continue; + //using std::sort if its worst-case is better + if (count < max_size) + std::sort(lastPos, bin_cache[u], comp); + else + reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type, + Get_char, Get_length, Compare> + (lastPos, bin_cache[u], char_offset + 1, bin_cache, cache_end, + bin_sizes, get_character, length, comp); + } + } + + //Holds the bin vector and makes the initial recursive call + template <class RandomAccessIter, class Unsigned_char_type> + inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void + >::type + string_sort(RandomAccessIter first, RandomAccessIter last, + Unsigned_char_type) + { + size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1]; + std::vector<RandomAccessIter> bin_cache; + string_sort_rec<RandomAccessIter, Unsigned_char_type> + (first, last, 0, bin_cache, 0, bin_sizes); + } + + template <class RandomAccessIter, class Unsigned_char_type> + inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void + >::type + string_sort(RandomAccessIter first, RandomAccessIter last, + Unsigned_char_type) + { + //Warning that we're using std::sort, even though string_sort was called + BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 ); + std::sort(first, last); + } + + //Holds the bin vector and makes the initial recursive call + template <class RandomAccessIter, class Unsigned_char_type> + inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void + >::type + reverse_string_sort(RandomAccessIter first, RandomAccessIter last, + Unsigned_char_type) + { + size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1]; + std::vector<RandomAccessIter> bin_cache; + reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type> + (first, last, 0, bin_cache, 0, bin_sizes); + } + + template <class RandomAccessIter, class Unsigned_char_type> + inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void + >::type + reverse_string_sort(RandomAccessIter first, RandomAccessIter last, + Unsigned_char_type) + { + typedef typename std::iterator_traits<RandomAccessIter>::value_type + Data_type; + //Warning that we're using std::sort, even though string_sort was called + BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 ); + std::sort(first, last, std::greater<Data_type>()); + } + + //Holds the bin vector and makes the initial recursive call + template <class RandomAccessIter, class Get_char, class Get_length, + class Unsigned_char_type> + inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void + >::type + string_sort(RandomAccessIter first, RandomAccessIter last, + Get_char get_character, Get_length length, Unsigned_char_type) + { + size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1]; + std::vector<RandomAccessIter> bin_cache; + string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char, + Get_length>(first, last, 0, bin_cache, 0, bin_sizes, get_character, length); + } + + template <class RandomAccessIter, class Get_char, class Get_length, + class Unsigned_char_type> + inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void + >::type + string_sort(RandomAccessIter first, RandomAccessIter last, + Get_char get_character, Get_length length, Unsigned_char_type) + { + //Warning that we're using std::sort, even though string_sort was called + BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 ); + std::sort(first, last); + } + + //Holds the bin vector and makes the initial recursive call + template <class RandomAccessIter, class Get_char, class Get_length, + class Compare, class Unsigned_char_type> + inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void + >::type + string_sort(RandomAccessIter first, RandomAccessIter last, + Get_char get_character, Get_length length, Compare comp, Unsigned_char_type) + { + size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1]; + std::vector<RandomAccessIter> bin_cache; + string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char + , Get_length, Compare> + (first, last, 0, bin_cache, 0, bin_sizes, get_character, length, comp); + } + + //disable_if_c was refusing to compile, so rewrote to use enable_if_c + template <class RandomAccessIter, class Get_char, class Get_length, + class Compare, class Unsigned_char_type> + inline typename boost::enable_if_c< (sizeof(Unsigned_char_type) > 2), void + >::type + string_sort(RandomAccessIter first, RandomAccessIter last, + Get_char get_character, Get_length length, Compare comp, Unsigned_char_type) + { + //Warning that we're using std::sort, even though string_sort was called + BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 ); + std::sort(first, last, comp); + } + + //Holds the bin vector and makes the initial recursive call + template <class RandomAccessIter, class Get_char, class Get_length, + class Compare, class Unsigned_char_type> + inline typename boost::enable_if_c< sizeof(Unsigned_char_type) <= 2, void + >::type + reverse_string_sort(RandomAccessIter first, RandomAccessIter last, + Get_char get_character, Get_length length, Compare comp, Unsigned_char_type) + { + size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1]; + std::vector<RandomAccessIter> bin_cache; + reverse_string_sort_rec<RandomAccessIter, Unsigned_char_type, Get_char, + Get_length, Compare> + (first, last, 0, bin_cache, 0, bin_sizes, get_character, length, comp); + } + + template <class RandomAccessIter, class Get_char, class Get_length, + class Compare, class Unsigned_char_type> + inline typename boost::disable_if_c< sizeof(Unsigned_char_type) <= 2, void + >::type + reverse_string_sort(RandomAccessIter first, RandomAccessIter last, + Get_char get_character, Get_length length, Compare comp, Unsigned_char_type) + { + //Warning that we're using std::sort, even though string_sort was called + BOOST_STATIC_WARNING( sizeof(Unsigned_char_type) <= 2 ); + std::sort(first, last, comp); + } + } +} +} +} + +#endif diff --git a/boost/sort/spreadsort/float_sort.hpp b/boost/sort/spreadsort/float_sort.hpp index 37966c28db..d5310d19ce 100644 --- a/boost/sort/spreadsort/float_sort.hpp +++ b/boost/sort/spreadsort/float_sort.hpp @@ -1,134 +1,176 @@ -//Templated Spreadsort-based implementation of float_sort and float_mem_cast
-
-// Copyright Steven J. Ross 2001 - 2014.
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort/ for library home page.
-
-/*
-Some improvements suggested by:
-Phil Endecott and Frank Gennari
-float_mem_cast fix provided by:
-Scott McMurray
-*/
-
-#ifndef BOOST_FLOAT_SORT_HPP
-#define BOOST_FLOAT_SORT_HPP
-#include <algorithm>
-#include <vector>
-#include <cstring>
-#include <limits>
-#include <boost/static_assert.hpp>
-#include <boost/sort/spreadsort/detail/constants.hpp>
-#include <boost/sort/spreadsort/detail/float_sort.hpp>
-
-namespace boost {
-namespace sort {
-namespace spreadsort {
-
- /*!
- \brief Casts a float to the specified integer type.
-
- \tparam Data_type Floating-point IEEE 754/IEC559 type.
- \tparam Cast_type Integer type (same size) to which to cast.
-
- \par Example:
- \code
- struct rightshift {
- int operator()(const DATA_TYPE &x, const unsigned offset) const {
- return float_mem_cast<KEY_TYPE, CAST_TYPE>(x.key) >> offset;
- }
- };
- \endcode
- */
- template<class Data_type, class Cast_type>
- inline Cast_type
- float_mem_cast(const Data_type & data)
- {
- // Only cast IEEE floating-point numbers, and only to a same-sized integer.
- BOOST_STATIC_ASSERT(sizeof(Cast_type) == sizeof(Data_type));
- BOOST_STATIC_ASSERT(std::numeric_limits<Data_type>::is_iec559);
- BOOST_STATIC_ASSERT(std::numeric_limits<Cast_type>::is_integer);
- Cast_type result;
- std::memcpy(&result, &data, sizeof(Cast_type));
- return result;
- }
-
-
- /*!
- \brief @c float_sort with casting to the appropriate size.
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
-
-Some performance plots of runtime vs. n and log(range) are provided:\n
- <a href="../../doc/graph/windows_float_sort.htm"> windows_float_sort</a>
- \n
- <a href="../../doc/graph/osx_float_sort.htm"> osx_float_sort</a>
-
-
-
- \par A simple example of sorting some floating-point is:
- \code
- vector<float> vec;
- vec.push_back(1.0);
- vec.push_back(2.3);
- vec.push_back(1.3);
- spreadsort(vec.begin(), vec.end());
- \endcode
- \par The sorted vector contains ascending values "1.0 1.3 2.3".
-
- */
- template <class RandomAccessIter>
- inline void float_sort(RandomAccessIter first, RandomAccessIter last)
- {
- if (last - first < detail::min_sort_size)
- std::sort(first, last);
- else
- detail::float_sort(first, last);
- }
-
- /*!
- \brief Floating-point sort algorithm using random access iterators with just right-shift functor.
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] rshift Functor that returns the result of shifting the value_type right a specified number of bits.
-
- */
- template <class RandomAccessIter, class Right_shift>
- inline void float_sort(RandomAccessIter first, RandomAccessIter last,
- Right_shift rshift)
- {
- if (last - first < detail::min_sort_size)
- std::sort(first, last);
- else
- detail::float_sort(first, last, rshift(*first, 0), rshift);
- }
-
-
- /*!
- \brief Float sort algorithm using random access iterators with both right-shift and user-defined comparison operator.
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] rshift Functor that returns the result of shifting the value_type right a specified number of bits.
- \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.
- */
-
- template <class RandomAccessIter, class Right_shift, class Compare>
- inline void float_sort(RandomAccessIter first, RandomAccessIter last,
- Right_shift rshift, Compare comp)
- {
- if (last - first < detail::min_sort_size)
- std::sort(first, last, comp);
- else
- detail::float_sort(first, last, rshift(*first, 0), rshift, comp);
- }
-}
-}
-}
-
-#endif
+//Templated Spreadsort-based implementation of float_sort and float_mem_cast + +// Copyright Steven J. Ross 2001 - 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort/ for library home page. + +/* +Some improvements suggested by: +Phil Endecott and Frank Gennari +float_mem_cast fix provided by: +Scott McMurray +*/ + +#ifndef BOOST_FLOAT_SORT_HPP +#define BOOST_FLOAT_SORT_HPP +#include <algorithm> +#include <vector> +#include <cstring> +#include <limits> +#include <boost/static_assert.hpp> +#include <boost/sort/spreadsort/detail/constants.hpp> +#include <boost/sort/spreadsort/detail/float_sort.hpp> +#include <boost/range/begin.hpp> +#include <boost/range/end.hpp> + +namespace boost { +namespace sort { +namespace spreadsort { + + /*! + \brief Casts a float to the specified integer type. + + \tparam Data_type Floating-point IEEE 754/IEC559 type. + \tparam Cast_type Integer type (same size) to which to cast. + + \par Example: + \code + struct rightshift { + int operator()(const DATA_TYPE &x, const unsigned offset) const { + return float_mem_cast<KEY_TYPE, CAST_TYPE>(x.key) >> offset; + } + }; + \endcode + */ + template<class Data_type, class Cast_type> + inline Cast_type + float_mem_cast(const Data_type & data) + { + // Only cast IEEE floating-point numbers, and only to a same-sized integer. + BOOST_STATIC_ASSERT(sizeof(Cast_type) == sizeof(Data_type)); + BOOST_STATIC_ASSERT(std::numeric_limits<Data_type>::is_iec559); + BOOST_STATIC_ASSERT(std::numeric_limits<Cast_type>::is_integer); + Cast_type result; + std::memcpy(&result, &data, sizeof(Cast_type)); + return result; + } + + + /*! + \brief @c float_sort with casting to the appropriate size. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + +Some performance plots of runtime vs. n and log(range) are provided:\n + <a href="../../doc/graph/windows_float_sort.htm"> windows_float_sort</a> + \n + <a href="../../doc/graph/osx_float_sort.htm"> osx_float_sort</a> + + + + \par A simple example of sorting some floating-point is: + \code + vector<float> vec; + vec.push_back(1.0); + vec.push_back(2.3); + vec.push_back(1.3); + spreadsort(vec.begin(), vec.end()); + \endcode + \par The sorted vector contains ascending values "1.0 1.3 2.3". + + */ + template <class RandomAccessIter> + inline void float_sort(RandomAccessIter first, RandomAccessIter last) + { + if (last - first < detail::min_sort_size) + std::sort(first, last); + else + detail::float_sort(first, last); + } + + /*! + \brief Floating-point sort algorithm using range. + + \param[in] range Range [first, last) for sorting. + + */ + template <class Range> + inline void float_sort(Range& range) + { + float_sort(boost::begin(range), boost::end(range)); + } + + /*! + \brief Floating-point sort algorithm using random access iterators with just right-shift functor. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] rshift Functor that returns the result of shifting the value_type right a specified number of bits. + + */ + template <class RandomAccessIter, class Right_shift> + inline void float_sort(RandomAccessIter first, RandomAccessIter last, + Right_shift rshift) + { + if (last - first < detail::min_sort_size) + std::sort(first, last); + else + detail::float_sort(first, last, rshift(*first, 0), rshift); + } + + /*! + \brief Floating-point sort algorithm using range with just right-shift functor. + + \param[in] range Range [first, last) for sorting. + \param[in] rshift Functor that returns the result of shifting the value_type right a specified number of bits. + + */ + template <class Range, class Right_shift> + inline void float_sort(Range& range, Right_shift rshift) + { + float_sort(boost::begin(range), boost::end(range), rshift); + } + + + /*! + \brief Float sort algorithm using random access iterators with both right-shift and user-defined comparison operator. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] rshift Functor that returns the result of shifting the value_type right a specified number of bits. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + */ + + template <class RandomAccessIter, class Right_shift, class Compare> + inline void float_sort(RandomAccessIter first, RandomAccessIter last, + Right_shift rshift, Compare comp) + { + if (last - first < detail::min_sort_size) + std::sort(first, last, comp); + else + detail::float_sort(first, last, rshift(*first, 0), rshift, comp); + } + + + /*! + \brief Float sort algorithm using range with both right-shift and user-defined comparison operator. + + \param[in] range Range [first, last) for sorting. + \param[in] rshift Functor that returns the result of shifting the value_type right a specified number of bits. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + */ + + template <class Range, class Right_shift, class Compare> + inline void float_sort(Range& range, Right_shift rshift, Compare comp) + { + float_sort(boost::begin(range), boost::end(range), rshift, comp); + } +} +} +} + +#endif diff --git a/boost/sort/spreadsort/integer_sort.hpp b/boost/sort/spreadsort/integer_sort.hpp index 0727ccd4a0..6bf3f683e1 100644 --- a/boost/sort/spreadsort/integer_sort.hpp +++ b/boost/sort/spreadsort/integer_sort.hpp @@ -1,185 +1,315 @@ -//Templated Spreadsort-based implementation of integer_sort
-
-// Copyright Steven J. Ross 2001 - 2014.
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort/ for library home page.
-
-/*
-Some improvements suggested by:
-Phil Endecott and Frank Gennari
-
-Doxygen comments by Paul A. Bristow Jan 2015
-
-*/
-
-#ifndef BOOST_INTEGER_SORT_HPP
-#define BOOST_INTEGER_SORT_HPP
-#include <algorithm>
-#include <vector>
-#include <cstring>
-#include <limits>
-#include <boost/static_assert.hpp>
-#include <boost/sort/spreadsort/detail/constants.hpp>
-#include <boost/sort/spreadsort/detail/integer_sort.hpp>
-
-namespace boost {
-namespace sort {
-namespace spreadsort {
- //Top-level sorting call for integers.
-
-
-/*! \brief Integer sort algorithm using random access iterators.
- (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size).
-
- \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
- <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>
- \n
- <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a>
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors, or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-
-*/
- template <class RandomAccessIter>
- inline void integer_sort(RandomAccessIter first, RandomAccessIter last)
- {
- // Don't sort if it's too small to optimize.
- if (last - first < detail::min_sort_size)
- std::sort(first, last);
- else
- detail::integer_sort(first, last, *first >> 0);
- }
-
-/*! \brief Integer sort algorithm using random access iterators with both right-shift and user-defined comparison operator.
- (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size).
-
- \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
- <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>
- \n
- <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a>
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] shift Functor that returns the result of shifting the value_type right a specified number of bits.
- \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \return @c void.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors,
- or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-*/
- template <class RandomAccessIter, class Right_shift, class Compare>
- inline void integer_sort(RandomAccessIter first, RandomAccessIter last,
- Right_shift shift, Compare comp) {
- if (last - first < detail::min_sort_size)
- std::sort(first, last, comp);
- else
- detail::integer_sort(first, last, shift(*first, 0), shift, comp);
- }
-
-/*! \brief Integer sort algorithm using random access iterators with just right-shift functor.
- (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size).
-
- \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-
-\par Performance:
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
- * <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>\n
- * <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a>
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] shift A functor that returns the result of shifting the value_type right a specified number of bits.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors,
- or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-
-*/
- template <class RandomAccessIter, class Right_shift>
- inline void integer_sort(RandomAccessIter first, RandomAccessIter last,
- Right_shift shift) {
- if (last - first < detail::min_sort_size)
- std::sort(first, last);
- else
- detail::integer_sort(first, last, shift(*first, 0), shift);
- }
-}
-}
-}
-
-#endif
-
+//Templated Spreadsort-based implementation of integer_sort + +// Copyright Steven J. Ross 2001 - 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort/ for library home page. + +/* +Some improvements suggested by: +Phil Endecott and Frank Gennari + +Doxygen comments by Paul A. Bristow Jan 2015 + +*/ + +#ifndef BOOST_INTEGER_SORT_HPP +#define BOOST_INTEGER_SORT_HPP +#include <algorithm> +#include <vector> +#include <cstring> +#include <limits> +#include <boost/static_assert.hpp> +#include <boost/sort/spreadsort/detail/constants.hpp> +#include <boost/sort/spreadsort/detail/integer_sort.hpp> +#include <boost/range/begin.hpp> +#include <boost/range/end.hpp> + +namespace boost { +namespace sort { +namespace spreadsort { + //Top-level sorting call for integers. + + +/*! \brief Integer sort algorithm using random access iterators. + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c integer_sort is asymptotically faster +than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11, +so its worst-case with default settings for 32-bit integers is +<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n +Some performance plots of runtime vs. n and log(range) are provided:\n + <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a> + \n + <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a> + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ + template <class RandomAccessIter> + inline void integer_sort(RandomAccessIter first, RandomAccessIter last) + { + // Don't sort if it's too small to optimize. + if (last - first < detail::min_sort_size) + std::sort(first, last); + else + detail::integer_sort(first, last, *first >> 0); + } + +/*! \brief Integer sort algorithm using range. + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c integer_sort is asymptotically faster +than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11, +so its worst-case with default settings for 32-bit integers is +<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n +Some performance plots of runtime vs. n and log(range) are provided:\n + <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a> + \n + <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a> + + \param[in] range Range [first, last) for sorting. + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ +template <class Range> +inline void integer_sort(Range& range) +{ + integer_sort(boost::begin(range), boost::end(range)); +} + +/*! \brief Integer sort algorithm using random access iterators with both right-shift and user-defined comparison operator. + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c integer_sort is asymptotically faster +than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11, +so its worst-case with default settings for 32-bit integers is +<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n +Some performance plots of runtime vs. n and log(range) are provided:\n + <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a> + \n + <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a> + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] shift Functor that returns the result of shifting the value_type right a specified number of bits. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). +*/ + template <class RandomAccessIter, class Right_shift, class Compare> + inline void integer_sort(RandomAccessIter first, RandomAccessIter last, + Right_shift shift, Compare comp) { + if (last - first < detail::min_sort_size) + std::sort(first, last, comp); + else + detail::integer_sort(first, last, shift(*first, 0), shift, comp); + } + +/*! \brief Integer sort algorithm using range with both right-shift and user-defined comparison operator. + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c integer_sort is asymptotically faster +than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11, +so its worst-case with default settings for 32-bit integers is +<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n +Some performance plots of runtime vs. n and log(range) are provided:\n + <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a> + \n + <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a> + + \param[in] range Range [first, last) for sorting. + \param[in] shift Functor that returns the result of shifting the value_type right a specified number of bits. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). +*/ +template <class Range, class Right_shift, class Compare> +inline void integer_sort(Range& range, Right_shift shift, Compare comp) +{ + integer_sort(boost::begin(range), boost::end(range), shift, comp); +} + +/*! \brief Integer sort algorithm using random access iterators with just right-shift functor. + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n + +\par Performance: +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c integer_sort is asymptotically faster +than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11, +so its worst-case with default settings for 32-bit integers is +<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n +Some performance plots of runtime vs. n and log(range) are provided:\n + * <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>\n + * <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a> + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] shift A functor that returns the result of shifting the value_type right a specified number of bits. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ + template <class RandomAccessIter, class Right_shift> + inline void integer_sort(RandomAccessIter first, RandomAccessIter last, + Right_shift shift) { + if (last - first < detail::min_sort_size) + std::sort(first, last); + else + detail::integer_sort(first, last, shift(*first, 0), shift); + } + + +/*! \brief Integer sort algorithm using range with just right-shift functor. + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n + +\par Performance: +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c integer_sort is asymptotically faster +than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11, +so its worst-case with default settings for 32-bit integers is +<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n +Some performance plots of runtime vs. n and log(range) are provided:\n + * <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>\n + * <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a> + + \param[in] range Range [first, last) for sorting. + \param[in] shift A functor that returns the result of shifting the value_type right a specified number of bits. + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ +template <class Range, class Right_shift> +inline void integer_sort(Range& range, Right_shift shift) +{ + integer_sort(boost::begin(range), boost::end(range), shift); +} +} +} +} + +#endif + diff --git a/boost/sort/spreadsort/spreadsort.hpp b/boost/sort/spreadsort/spreadsort.hpp index 48377123e3..49f20ed147 100644 --- a/boost/sort/spreadsort/spreadsort.hpp +++ b/boost/sort/spreadsort/spreadsort.hpp @@ -1,146 +1,169 @@ -// Templated generic hybrid sorting
-
-// Copyright Steven J. Ross 2001 - 2009.
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort/ for library home page.
-
-/*
-Some improvements suggested by:
-Phil Endecott and Frank Gennari
-float_mem_cast fix provided by:
-Scott McMurray
-*/
-
-#ifndef BOOST_SORT_SPREADSORT_HPP
-#define BOOST_SORT_SPREADSORT_HPP
-#include <algorithm>
-#include <vector>
-#include <cstring>
-#include <string>
-#include <limits>
-#include <boost/type_traits.hpp>
-#include <boost/sort/spreadsort/integer_sort.hpp>
-#include <boost/sort/spreadsort/float_sort.hpp>
-#include <boost/sort/spreadsort/string_sort.hpp>
-
-namespace boost {
-namespace sort {
-
-/*! Namespace for spreadsort sort variants for different data types.
-\note Use hyperlinks (coloured) to get detailed information about functions.
-*/
-namespace spreadsort {
-
- /*!
- \brief Generic @c spreadsort variant detecting integer-type elements so call to @c integer_sort.
- \details If the data type provided is an integer, @c integer_sort is used.
- \note Sorting other data types requires picking between @c integer_sort, @c float_sort and @c string_sort directly,
- as @c spreadsort won't accept types that don't have the appropriate @c type_traits.
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
- */
-
- template <class RandomAccessIter>
- inline typename boost::enable_if_c< std::numeric_limits<
- typename std::iterator_traits<RandomAccessIter>::value_type >::is_integer,
- void >::type
- spreadsort(RandomAccessIter first, RandomAccessIter last)
- {
- integer_sort(first, last);
- }
-
- /*!
- \brief Generic @c spreadsort variant detecting float element type so call to @c float_sort.
- \details If the data type provided is a float or castable-float, @c float_sort is used.
- \note Sorting other data types requires picking between @c integer_sort, @c float_sort and @c string_sort directly,
- as @c spreadsort won't accept types that don't have the appropriate @c type_traits.
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
- */
-
- template <class RandomAccessIter>
- inline typename boost::enable_if_c< !std::numeric_limits<
- typename std::iterator_traits<RandomAccessIter>::value_type >::is_integer
- && std::numeric_limits<
- typename std::iterator_traits<RandomAccessIter>::value_type >::is_iec559,
- void >::type
- spreadsort(RandomAccessIter first, RandomAccessIter last)
- {
- float_sort(first, last);
- }
-
- /*!
- \brief Generic @c spreadsort variant detecting string element type so call to @c string_sort for @c std::strings.
- \details If the data type provided is a string, @c string_sort is used.
- \note Sorting other data types requires picking between @c integer_sort, @c float_sort and @c string_sort directly,
- as @c spreadsort won't accept types that don't have the appropriate @c type_traits.
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
- */
-
- template <class RandomAccessIter>
- inline typename boost::enable_if_c<
- is_same<typename std::iterator_traits<RandomAccessIter>::value_type,
- typename std::string>::value, void >::type
- spreadsort(RandomAccessIter first, RandomAccessIter last)
- {
- string_sort(first, last);
- }
-
- /*!
- \brief Generic @c spreadsort variant detecting string element type so call to @c string_sort for @c std::wstrings.
- \details If the data type provided is a wstring, @c string_sort is used.
- \note Sorting other data types requires picking between @c integer_sort, @c float_sort and @c string_sort directly,
- as @c spreadsort won't accept types that don't have the appropriate @c type_traits. Also, 2-byte wide-characters are the limit above which string_sort is inefficient, so on platforms with wider characters, this will not accept wstrings.
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
- */
- template <class RandomAccessIter>
- inline typename boost::enable_if_c<
- is_same<typename std::iterator_traits<RandomAccessIter>::value_type,
- typename std::wstring>::value &&
- sizeof(wchar_t) == 2, void >::type
- spreadsort(RandomAccessIter first, RandomAccessIter last)
- {
- boost::uint16_t unused = 0;
- string_sort(first, last, unused);
- }
-} // namespace spreadsort
-} // namespace sort
-} // namespace boost
-
-#endif
+// Templated generic hybrid sorting + +// Copyright Steven J. Ross 2001 - 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort/ for library home page. + +/* +Some improvements suggested by: +Phil Endecott and Frank Gennari +float_mem_cast fix provided by: +Scott McMurray + Range support provided by: + Alexander Zaitsev +*/ + +#ifndef BOOST_SORT_SPREADSORT_HPP +#define BOOST_SORT_SPREADSORT_HPP +#include <algorithm> +#include <vector> +#include <cstring> +#include <string> +#include <limits> +#include <boost/type_traits.hpp> +#include <boost/sort/spreadsort/integer_sort.hpp> +#include <boost/sort/spreadsort/float_sort.hpp> +#include <boost/sort/spreadsort/string_sort.hpp> +#include <boost/range/begin.hpp> +#include <boost/range/end.hpp> + +namespace boost { +namespace sort { + +/*! Namespace for spreadsort sort variants for different data types. +\note Use hyperlinks (coloured) to get detailed information about functions. +*/ +namespace spreadsort { + + /*! + \brief Generic @c spreadsort variant detecting integer-type elements so call to @c integer_sort. + \details If the data type provided is an integer, @c integer_sort is used. + \note Sorting other data types requires picking between @c integer_sort, @c float_sort and @c string_sort directly, + as @c spreadsort won't accept types that don't have the appropriate @c type_traits. + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + */ + + template <class RandomAccessIter> + inline typename boost::enable_if_c< std::numeric_limits< + typename std::iterator_traits<RandomAccessIter>::value_type >::is_integer, + void >::type + spreadsort(RandomAccessIter first, RandomAccessIter last) + { + integer_sort(first, last); + } + + /*! + \brief Generic @c spreadsort variant detecting float element type so call to @c float_sort. + \details If the data type provided is a float or castable-float, @c float_sort is used. + \note Sorting other data types requires picking between @c integer_sort, @c float_sort and @c string_sort directly, + as @c spreadsort won't accept types that don't have the appropriate @c type_traits. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + */ + + template <class RandomAccessIter> + inline typename boost::enable_if_c< !std::numeric_limits< + typename std::iterator_traits<RandomAccessIter>::value_type >::is_integer + && std::numeric_limits< + typename std::iterator_traits<RandomAccessIter>::value_type >::is_iec559, + void >::type + spreadsort(RandomAccessIter first, RandomAccessIter last) + { + float_sort(first, last); + } + + /*! + \brief Generic @c spreadsort variant detecting string element type so call to @c string_sort for @c std::strings. + \details If the data type provided is a string, @c string_sort is used. + \note Sorting other data types requires picking between @c integer_sort, @c float_sort and @c string_sort directly, + as @c spreadsort won't accept types that don't have the appropriate @c type_traits. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + */ + + template <class RandomAccessIter> + inline typename boost::enable_if_c< + is_same<typename std::iterator_traits<RandomAccessIter>::value_type, + typename std::string>::value, void >::type + spreadsort(RandomAccessIter first, RandomAccessIter last) + { + string_sort(first, last); + } + + /*! + \brief Generic @c spreadsort variant detecting string element type so call to @c string_sort for @c std::wstrings. + \details If the data type provided is a wstring, @c string_sort is used. + \note Sorting other data types requires picking between @c integer_sort, @c float_sort and @c string_sort directly, + as @c spreadsort won't accept types that don't have the appropriate @c type_traits. Also, 2-byte wide-characters are the limit above which string_sort is inefficient, so on platforms with wider characters, this will not accept wstrings. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + */ + template <class RandomAccessIter> + inline typename boost::enable_if_c< + is_same<typename std::iterator_traits<RandomAccessIter>::value_type, + typename std::wstring>::value && + sizeof(wchar_t) == 2, void >::type + spreadsort(RandomAccessIter first, RandomAccessIter last) + { + boost::uint16_t unused = 0; + string_sort(first, last, unused); + } + +/*! +\brief Generic @c spreadsort variant detects value_type and calls required sort function. +\note Sorting other data types requires picking between @c integer_sort, @c float_sort and @c string_sort directly, +as @c spreadsort won't accept types that don't have the appropriate @c type_traits. + +\param[in] range Range [first, last) for sorting. + +\pre [@c first, @c last) is a valid range. +\post The elements in the range [@c first, @c last) are sorted in ascending order. +*/ + +template <class Range> +void spreadsort(Range& range) +{ + spreadsort(boost::begin(range), boost::end(range)); +} + + +} // namespace spreadsort +} // namespace sort +} // namespace boost + +#endif diff --git a/boost/sort/spreadsort/string_sort.hpp b/boost/sort/spreadsort/string_sort.hpp index 4c3f1fbfe7..daaa054b6c 100644 --- a/boost/sort/spreadsort/string_sort.hpp +++ b/boost/sort/spreadsort/string_sort.hpp @@ -1,449 +1,741 @@ -//Templated hybrid string_sort
-
-// Copyright Steven J. Ross 2001 - 2009.
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org/libs/sort/ for library home page.
-
-/*
-Some improvements suggested by:
-Phil Endecott and Frank Gennari
-*/
-
-#ifndef BOOST_STRING_SORT_HPP
-#define BOOST_STRING_SORT_HPP
-#include <algorithm>
-#include <vector>
-#include <cstring>
-#include <limits>
-#include <boost/static_assert.hpp>
-#include <boost/sort/spreadsort/detail/constants.hpp>
-#include <boost/sort/spreadsort/detail/string_sort.hpp>
-
-namespace boost {
-namespace sort {
-namespace spreadsort {
-
-/*! \brief String sort algorithm using random access iterators, allowing character-type overloads.\n
- (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size).
-
- \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-\par
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
-<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n
-<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a>
-
- \tparam RandomAccessIter <a href="http://www.cplusplus.com/reference/iterator/RandomAccessIterator/">Random access iterator</a>
- \tparam Unsigned_char_type Unsigned character type used for string.
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] unused value with the same type as the result of the [] operator, defining the Unsigned_char_type. The actual value is unused.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors,
- or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-
-*/
-
- template <class RandomAccessIter, class Unsigned_char_type>
- inline void string_sort(RandomAccessIter first, RandomAccessIter last,
- Unsigned_char_type unused)
- {
- //Don't sort if it's too small to optimize
- if (last - first < detail::min_sort_size)
- std::sort(first, last);
- else
- detail::string_sort(first, last, unused);
- }
-
-
-/*! \brief String sort algorithm using random access iterators, wraps using default of unsigned char.
- (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size).
-
- \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
- <a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>
- \n
- <a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a>
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors,
- or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-
-*/
- template <class RandomAccessIter>
- inline void string_sort(RandomAccessIter first, RandomAccessIter last)
- {
- unsigned char unused = '\0';
- string_sort(first, last, unused);
- }
-
-
-/*! \brief String sort algorithm using random access iterators, allowing character-type overloads.
-
- (All variants fall back to @c std::sort if the data size is too small, < detail::min_sort_size).
-
- \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
- <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>
- \n
- <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a>
-
-
- \tparam RandomAccessIter <a href="http://www.cplusplus.com/reference/iterator/RandomAccessIterator/">Random access iterator</a>
- \tparam Comp Functor type to use for comparison.
- \tparam Unsigned_char_type Unsigned character type used for string.
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.
- \param[in] unused value with the same type as the result of the [] operator, defining the Unsigned_char_type. The actual value is unused.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \return @c void.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors,
- or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-*/
- template <class RandomAccessIter, class Compare, class Unsigned_char_type>
- inline void reverse_string_sort(RandomAccessIter first,
- RandomAccessIter last, Compare comp, Unsigned_char_type unused)
- {
- //Don't sort if it's too small to optimize.
- if (last - first < detail::min_sort_size)
- std::sort(first, last, comp);
- else
- detail::reverse_string_sort(first, last, unused);
- }
-
-
-/*! \brief String sort algorithm using random access iterators, wraps using default of @c unsigned char.
-
- (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size).
-
- \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
- <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>
- \n
- <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a>
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \return @c void.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors,
- or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-*/
- template <class RandomAccessIter, class Compare>
- inline void reverse_string_sort(RandomAccessIter first,
- RandomAccessIter last, Compare comp)
- {
- unsigned char unused = '\0';
- reverse_string_sort(first, last, comp, unused);
- }
-
-
-/*! \brief String sort algorithm using random access iterators, wraps using default of @c unsigned char.
-
- (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size).
-
- \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
- <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>
- \n
- <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a>
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] getchar Bracket functor equivalent to @c operator[], taking a number corresponding to the character offset.
- \param[in] length Functor to get the length of the string in characters.
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \pre @c RandomAccessIter @c value_type supports the @c operator>>,
- which returns an integer-type right-shifted a specified number of bits.
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \return @c void.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors,
- or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-
-*/
- template <class RandomAccessIter, class Get_char, class Get_length>
- inline void string_sort(RandomAccessIter first, RandomAccessIter last,
- Get_char getchar, Get_length length)
- {
- //Don't sort if it's too small to optimize
- if (last - first < detail::min_sort_size)
- std::sort(first, last);
- else {
- //skipping past empties, which allows us to get the character type
- //.empty() is not used so as not to require a user declaration of it
- while (!length(*first)) {
- if (++first == last)
- return;
- }
- detail::string_sort(first, last, getchar, length, getchar((*first), 0));
- }
- }
-
-
-
-/*! \brief String sort algorithm using random access iterators, wraps using default of @c unsigned char.
-
- (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size).
-
- \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
- <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>
- \n
- <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a>
-
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] getchar Bracket functor equivalent to @c operator[], taking a number corresponding to the character offset.
- \param[in] length Functor to get the length of the string in characters.
- \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.
-
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \return @c void.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors,
- or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-
-*/
- template <class RandomAccessIter, class Get_char, class Get_length,
- class Compare>
- inline void string_sort(RandomAccessIter first, RandomAccessIter last,
- Get_char getchar, Get_length length, Compare comp)
- {
- //Don't sort if it's too small to optimize
- if (last - first < detail::min_sort_size)
- std::sort(first, last, comp);
- else {
- //skipping past empties, which allows us to get the character type
- //.empty() is not used so as not to require a user declaration of it
- while (!length(*first)) {
- if (++first == last)
- return;
- }
- detail::string_sort(first, last, getchar, length, comp,
- getchar((*first), 0));
- }
- }
-
-
-/*! \brief Reverse String sort algorithm using random access iterators.
-
- (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size).
-
- \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
-which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
-Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>,
-so @c integer_sort is asymptotically faster
-than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
-so its worst-case with default settings for 32-bit integers is
-<em> O(N * ((32/11) </em> slow radix-based iterations fast comparison-based iterations).\n\n
-Some performance plots of runtime vs. n and log(range) are provided:\n
- <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a>
- \n
- <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a>
-
-
- \param[in] first Iterator pointer to first element.
- \param[in] last Iterator pointing to one beyond the end of data.
- \param[in] getchar Bracket functor equivalent to @c operator[], taking a number corresponding to the character offset.
- \param[in] length Functor to get the length of the string in characters.
- \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.
-
-
- \pre [@c first, @c last) is a valid range.
- \pre @c RandomAccessIter @c value_type is mutable.
- \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
- \post The elements in the range [@c first, @c last) are sorted in ascending order.
-
- \return @c void.
-
- \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
- the right shift, subtraction of right-shifted elements, functors,
- or any operations on iterators throw.
-
- \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
- \warning Invalid arguments cause undefined behaviour.
- \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
- enabling faster generic-programming.
-
- \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where:
- \remark * N is @c last - @c first,
- \remark * K is the log of the range in bits (32 for 32-bit integers using their full range),
- \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
-
-*/
- template <class RandomAccessIter, class Get_char, class Get_length,
- class Compare>
- inline void reverse_string_sort(RandomAccessIter first,
- RandomAccessIter last, Get_char getchar, Get_length length, Compare comp)
- {
- //Don't sort if it's too small to optimize
- if (last - first < detail::min_sort_size)
- std::sort(first, last, comp);
- else {
- //skipping past empties, which allows us to get the character type
- //.empty() is not used so as not to require a user declaration of it
- while (!length(*(--last))) {
- //If there is just one non-empty at the beginning, this is sorted
- if (first == last)
- return;
- }
- //making last just after the end of the non-empty part of the array
- detail::reverse_string_sort(first, last + 1, getchar, length, comp,
- getchar((*last), 0));
- }
- }
-}
-}
-}
-
-#endif
+//Templated hybrid string_sort + +// Copyright Steven J. Ross 2001 - 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/sort/ for library home page. + +/* +Some improvements suggested by: +Phil Endecott and Frank Gennari +*/ + +#ifndef BOOST_STRING_SORT_HPP +#define BOOST_STRING_SORT_HPP +#include <algorithm> +#include <vector> +#include <cstring> +#include <limits> +#include <boost/static_assert.hpp> +#include <boost/sort/spreadsort/detail/constants.hpp> +#include <boost/sort/spreadsort/detail/string_sort.hpp> +#include <boost/range/begin.hpp> +#include <boost/range/end.hpp> + +namespace boost { +namespace sort { +namespace spreadsort { + +/*! \brief String sort algorithm using random access iterators, allowing character-type overloads.\n + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + \tparam RandomAccessIter <a href="http://www.cplusplus.com/reference/iterator/RandomAccessIterator/">Random access iterator</a> + \tparam Unsigned_char_type Unsigned character type used for string. + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] unused value with the same type as the result of the [] operator, defining the Unsigned_char_type. The actual value is unused. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ + + template <class RandomAccessIter, class Unsigned_char_type> + inline void string_sort(RandomAccessIter first, RandomAccessIter last, + Unsigned_char_type unused) + { + //Don't sort if it's too small to optimize + if (last - first < detail::min_sort_size) + std::sort(first, last); + else + detail::string_sort(first, last, unused); + } + +/*! \brief String sort algorithm using range, allowing character-type overloads.\n + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + \tparam Unsigned_char_type Unsigned character type used for string. + \param[in] range Range [first, last) for sorting. + \param[in] unused value with the same type as the result of the [] operator, defining the Unsigned_char_type. The actual value is unused. + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ + +template <class Range, class Unsigned_char_type> +inline void string_sort(Range& range, Unsigned_char_type unused) +{ + string_sort(boost::begin(range), boost::end(range), unused); +} + +/*! \brief String sort algorithm using random access iterators, wraps using default of unsigned char. + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n + <a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a> + \n + <a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ + template <class RandomAccessIter> + inline void string_sort(RandomAccessIter first, RandomAccessIter last) + { + unsigned char unused = '\0'; + string_sort(first, last, unused); + } + +/*! \brief String sort algorithm using range, wraps using default of unsigned char. + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n + <a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a> + \n + <a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + \param[in] range Range [first, last) for sorting. + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ +template <class Range> +inline void string_sort(Range& range) +{ + string_sort(boost::begin(range), boost::end(range)); +} + +/*! \brief String sort algorithm using random access iterators, allowing character-type overloads. + + (All variants fall back to @c std::sort if the data size is too small, < detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + + \tparam RandomAccessIter <a href="http://www.cplusplus.com/reference/iterator/RandomAccessIterator/">Random access iterator</a> + \tparam Comp Functor type to use for comparison. + \tparam Unsigned_char_type Unsigned character type used for string. + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + \param[in] unused value with the same type as the result of the [] operator, defining the Unsigned_char_type. The actual value is unused. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). +*/ + template <class RandomAccessIter, class Compare, class Unsigned_char_type> + inline void reverse_string_sort(RandomAccessIter first, + RandomAccessIter last, Compare comp, Unsigned_char_type unused) + { + //Don't sort if it's too small to optimize. + if (last - first < detail::min_sort_size) + std::sort(first, last, comp); + else + detail::reverse_string_sort(first, last, unused); + } + +/*! \brief String sort algorithm using range, allowing character-type overloads. + + (All variants fall back to @c std::sort if the data size is too small, < detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n + <a href="../../doc/graph/windows_integer_sort.htm"> windows_integer_sort</a> + \n + <a href="../../doc/graph/osx_integer_sort.htm"> osx_integer_sort</a> + + + \tparam Comp Functor type to use for comparison. + \tparam Unsigned_char_type Unsigned character type used for string. + + \param[in] range Range [first, last) for sorting. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + \param[in] unused value with the same type as the result of the [] operator, defining the Unsigned_char_type. The actual value is unused. + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). +*/ +template <class Range, class Compare, class Unsigned_char_type> +inline void reverse_string_sort(Range& range, Compare comp, Unsigned_char_type unused) +{ + reverse_string_sort(boost::begin(range), boost::end(range), comp, unused); +} + +/*! \brief String sort algorithm using random access iterators, wraps using default of @c unsigned char. + + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms.\n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). +*/ + template <class RandomAccessIter, class Compare> + inline void reverse_string_sort(RandomAccessIter first, + RandomAccessIter last, Compare comp) + { + unsigned char unused = '\0'; + reverse_string_sort(first, last, comp, unused); + } + +/*! \brief String sort algorithm using range, wraps using default of @c unsigned char. + + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + \param[in] range Range [first, last) for sorting. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). +*/ +template <class Range, class Compare> +inline void reverse_string_sort(Range& range, Compare comp) +{ + reverse_string_sort(boost::begin(range), boost::end(range), comp); +} + +/*! \brief String sort algorithm using random access iterators, wraps using default of @c unsigned char. + + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] get_character Bracket functor equivalent to @c operator[], taking a number corresponding to the character offset. + \param[in] length Functor to get the length of the string in characters. + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \pre @c RandomAccessIter @c value_type supports the @c operator>>, + which returns an integer-type right-shifted a specified number of bits. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ + template <class RandomAccessIter, class Get_char, class Get_length> + inline void string_sort(RandomAccessIter first, RandomAccessIter last, + Get_char get_character, Get_length length) + { + //Don't sort if it's too small to optimize + if (last - first < detail::min_sort_size) + std::sort(first, last); + else { + //skipping past empties, which allows us to get the character type + //.empty() is not used so as not to require a user declaration of it + while (!length(*first)) { + if (++first == last) + return; + } + detail::string_sort(first, last, get_character, length, get_character((*first), 0)); + } + } + +/*! \brief String sort algorithm using range, wraps using default of @c unsigned char. + + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + \param[in] range Range [first, last) for sorting. + \param[in] get_character Bracket functor equivalent to @c operator[], taking a number corresponding to the character offset. + \param[in] length Functor to get the length of the string in characters. + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ +template <class Range, class Get_char, class Get_length> +inline void string_sort(Range& range, Get_char get_character, Get_length length) +{ + string_sort(boost::begin(range), boost::end(range), get_character, length); +} + + +/*! \brief String sort algorithm using random access iterators, wraps using default of @c unsigned char. + + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] get_character Bracket functor equivalent to @c operator[], taking a number corresponding to the character offset. + \param[in] length Functor to get the length of the string in characters. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ + template <class RandomAccessIter, class Get_char, class Get_length, + class Compare> + inline void string_sort(RandomAccessIter first, RandomAccessIter last, + Get_char get_character, Get_length length, Compare comp) + { + //Don't sort if it's too small to optimize + if (last - first < detail::min_sort_size) + std::sort(first, last, comp); + else { + //skipping past empties, which allows us to get the character type + //.empty() is not used so as not to require a user declaration of it + while (!length(*first)) { + if (++first == last) + return; + } + detail::string_sort(first, last, get_character, length, comp, + get_character((*first), 0)); + } + } + +/*! \brief String sort algorithm using range, wraps using default of @c unsigned char. + + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + + \param[in] range Range [first, last) for sorting. + \param[in] get_character Bracket functor equivalent to @c operator[], taking a number corresponding to the character offset. + \param[in] length Functor to get the length of the string in characters. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ +template <class Range, class Get_char, class Get_length, class Compare> +inline void string_sort(Range& range, + Get_char get_character, Get_length length, Compare comp) +{ + string_sort(boost::begin(range), boost::end(range), get_character, length, comp); +} + +/*! \brief Reverse String sort algorithm using random access iterators. + + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + + \param[in] first Iterator pointer to first element. + \param[in] last Iterator pointing to one beyond the end of data. + \param[in] get_character Bracket functor equivalent to @c operator[], taking a number corresponding to the character offset. + \param[in] length Functor to get the length of the string in characters. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + + + \pre [@c first, @c last) is a valid range. + \pre @c RandomAccessIter @c value_type is mutable. + \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a> + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ + template <class RandomAccessIter, class Get_char, class Get_length, + class Compare> + inline void reverse_string_sort(RandomAccessIter first, + RandomAccessIter last, Get_char get_character, Get_length length, Compare comp) + { + //Don't sort if it's too small to optimize + if (last - first < detail::min_sort_size) + std::sort(first, last, comp); + else { + //skipping past empties, which allows us to get the character type + //.empty() is not used so as not to require a user declaration of it + while (!length(*(--last))) { + //If there is just one non-empty at the beginning, this is sorted + if (first == last) + return; + } + //making last just after the end of the non-empty part of the array + detail::reverse_string_sort(first, last + 1, get_character, length, comp, + get_character((*last), 0)); + } + } + +/*! \brief Reverse String sort algorithm using range. + + (All variants fall back to @c std::sort if the data size is too small, < @c detail::min_sort_size). + + \details @c string_sort is a fast templated in-place hybrid radix/comparison algorithm, +which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n +\par +Worst-case performance is <em> O(N * (lg(range)/s + s)) </em>, +so @c string_sort is asymptotically faster +than pure comparison-based algorithms. \n\n +Some performance plots of runtime vs. n and log(range) are provided:\n +<a href="../../doc/graph/windows_string_sort.htm"> windows_string_sort</a>\n +<a href="../../doc/graph/osx_string_sort.htm"> osx_string_sort</a> + + + \param[in] range Range [first, last) for sorting. + \param[in] get_character Bracket functor equivalent to @c operator[], taking a number corresponding to the character offset. + \param[in] length Functor to get the length of the string in characters. + \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order. + + + \pre [@c first, @c last) is a valid range. + \post The elements in the range [@c first, @c last) are sorted in ascending order. + + \return @c void. + + \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves), + the right shift, subtraction of right-shifted elements, functors, + or any operations on iterators throw. + + \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss. + \warning Invalid arguments cause undefined behaviour. + \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type, + enabling faster generic-programming. + + \remark The lesser of <em> O(N*log(N)) </em> comparisons and <em> O(N*log(K/S + S)) </em>operations worst-case, where: + \remark * N is @c last - @c first, + \remark * K is the log of the range in bits (32 for 32-bit integers using their full range), + \remark * S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size). + +*/ +template <class Range, class Get_char, class Get_length, + class Compare> +inline void reverse_string_sort(Range& range, Get_char get_character, Get_length length, Compare comp) +{ + reverse_string_sort(boost::begin(range), boost::end(range), get_character, length, comp); +} +} +} +} + +#endif |