gnss-sim/3rdparty/boost/sort/common/merge_block.hpp

420 lines
16 KiB
C++

//----------------------------------------------------------------------------
/// @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 <ciso646>
#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