//---------------------------------------------------------------------------- /// @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 #include #include #include #include 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 struct merge_block { //------------------------------------------------------------------------- // D E F I N I T I O N S //------------------------------------------------------------------------- typedef util::value_iter value_t; typedef range range_pos; typedef range range_it; typedef range range_buf; typedef typename std::vector::iterator it_index; typedef util::circular_buffer 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 global_range; // index vector of block_pos elements std::vector 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 void merge_block ::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 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 void merge_block ::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 void merge_block ::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