merge_block.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. //----------------------------------------------------------------------------
  2. /// @file merge_block.hpp
  3. /// @brief This file constains the class merge_block, which is part of the
  4. /// block_indirect_sort algorithm
  5. ///
  6. /// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n
  7. /// Distributed under the Boost Software License, Version 1.0.\n
  8. /// ( See accompanying file LICENSE_1_0.txt or copy at
  9. /// http://www.boost.org/LICENSE_1_0.txt )
  10. /// @version 0.1
  11. ///
  12. /// @remarks
  13. //-----------------------------------------------------------------------------
  14. #ifndef __BOOST_SORT_COMMON_MERGE_BLOCK_HPP
  15. #define __BOOST_SORT_COMMON_MERGE_BLOCK_HPP
  16. #include <boost/sort/common/range.hpp>
  17. #include <boost/sort/common/rearrange.hpp>
  18. #include <boost/sort/common/util/merge.hpp>
  19. #include <boost/sort/common/util/traits.hpp>
  20. namespace boost
  21. {
  22. namespace sort
  23. {
  24. namespace common
  25. {
  26. ///---------------------------------------------------------------------------
  27. /// @struct merge_block
  28. /// @brief This contains all the information shared betwen the classes of the
  29. /// block indirect sort algorithm
  30. //----------------------------------------------------------------------------
  31. template<class Iter_t, class Compare, uint32_t Power2 = 10>
  32. struct merge_block
  33. {
  34. //-------------------------------------------------------------------------
  35. // D E F I N I T I O N S
  36. //-------------------------------------------------------------------------
  37. typedef util::value_iter<Iter_t> value_t;
  38. typedef range<size_t> range_pos;
  39. typedef range<Iter_t> range_it;
  40. typedef range<value_t *> range_buf;
  41. typedef typename std::vector<size_t>::iterator it_index;
  42. typedef util::circular_buffer<value_t, Power2 + 1> circular_t;
  43. //------------------------------------------------------------------------
  44. // CONSTANTS
  45. //------------------------------------------------------------------------
  46. const size_t BLOCK_SIZE = (size_t) 1 << Power2;
  47. const size_t LOG_BLOCK = Power2;
  48. //------------------------------------------------------------------------
  49. // V A R I A B L E S
  50. //------------------------------------------------------------------------
  51. // range with all the element to sort
  52. range<Iter_t> global_range;
  53. // index vector of block_pos elements
  54. std::vector<size_t> index;
  55. // Number of elements to sort
  56. size_t nelem;
  57. // Number of blocks to sort
  58. size_t nblock;
  59. // Number of elements in the last block (tail)
  60. size_t ntail;
  61. // object for to compare two elements
  62. Compare cmp;
  63. // range of elements of the last block (tail)
  64. range_it range_tail;
  65. // circular buffer
  66. circular_t * ptr_circ;
  67. // indicate if the circulr buffer is owned by the data structure
  68. // or is received as parameter
  69. bool owned;
  70. //
  71. //------------------------------------------------------------------------
  72. // F U N C T I O N S
  73. //------------------------------------------------------------------------
  74. //
  75. //------------------------------------------------------------------------
  76. // function : merge_block
  77. /// @brief constructor of the class
  78. //
  79. /// @param first : iterator to the first element of the range to sort
  80. /// @param last : iterator after the last element to the range to sort
  81. /// @param comp : object for to compare two elements pointed by Iter_t
  82. /// iterators
  83. //------------------------------------------------------------------------
  84. merge_block (Iter_t first, Iter_t last, Compare comp,
  85. circular_t *pcirc_buffer)
  86. : global_range(first, last), cmp(comp), ptr_circ(pcirc_buffer),
  87. owned(pcirc_buffer == nullptr)
  88. {
  89. assert((last - first) >= 0);
  90. if (first == last) return; // nothing to do
  91. nelem = size_t(last - first);
  92. nblock = (nelem + BLOCK_SIZE - 1) / BLOCK_SIZE;
  93. ntail = (nelem % BLOCK_SIZE);
  94. index.reserve(nblock + 1);
  95. for (size_t i = 0; i < nblock; ++i)
  96. index.emplace_back(i);
  97. range_tail.first = first + ((nblock - 1) << LOG_BLOCK);
  98. range_tail.last = last;
  99. if (owned)
  100. {
  101. ptr_circ = new circular_t;
  102. ptr_circ->initialize(*first);
  103. };
  104. }
  105. merge_block(Iter_t first, Iter_t last, Compare comp)
  106. : merge_block(first, last, comp, nullptr) { };
  107. ~ merge_block()
  108. {
  109. if (ptr_circ != nullptr and owned)
  110. {
  111. delete ptr_circ;
  112. ptr_circ = nullptr;
  113. };
  114. };
  115. //-------------------------------------------------------------------------
  116. // function : get_range
  117. /// @brief obtain the range in the position pos
  118. /// @param pos : position of the range
  119. /// @return range required
  120. //-------------------------------------------------------------------------
  121. range_it get_range(size_t pos) const
  122. {
  123. Iter_t it1 = global_range.first + (pos << LOG_BLOCK);
  124. Iter_t it2 = (pos == (nblock - 1)) ?
  125. global_range.last : it1 + BLOCK_SIZE;
  126. return range_it(it1, it2);
  127. };
  128. //-------------------------------------------------------------------------
  129. // function : get_group_range
  130. /// @brief obtain the range of the contiguous blocks beginning in the
  131. // position pos
  132. /// @param pos : position of the first range
  133. /// @param nrange : number of ranges of the group
  134. /// @return range required
  135. //-------------------------------------------------------------------------
  136. range_it get_group_range(size_t pos, size_t nrange) const
  137. {
  138. Iter_t it1 = global_range.first + (pos << LOG_BLOCK);
  139. Iter_t it2 = ((pos + nrange) == nblock)?global_range.last: global_range.first + ((pos + nrange) << LOG_BLOCK);
  140. //Iter_t it2 = global_range.first + ((pos + nrange) << LOG_BLOCK);
  141. //if ((pos + nrange) == nblock) it2 = global_range.last;
  142. return range_it(it1, it2);
  143. };
  144. //-------------------------------------------------------------------------
  145. // function : is_tail
  146. /// @brief indicate if a block is the tail
  147. /// @param pos : position of the block
  148. /// @return true : taiol false : not tail
  149. //-------------------------------------------------------------------------
  150. bool is_tail(size_t pos) const
  151. {
  152. return (pos == (nblock - 1) and ntail != 0);
  153. };
  154. //-------------------------------------------------------------------------
  155. // function :
  156. /// @brief
  157. /// @param
  158. /// @return
  159. //-------------------------------------------------------------------------
  160. void merge_range_pos(it_index itx_first, it_index itx_mid,
  161. it_index itx_last);
  162. //-------------------------------------------------------------------------
  163. // function : move_range_pos_backward
  164. /// @brief Move backward the elements of a range of blocks in a index
  165. /// @param itx_first : iterator to the position of the first block
  166. /// @param itx_last : itertor to the position of the last block
  167. /// @param npos : number of positions to move. Must be less than BLOCK_SIZE
  168. /// @return
  169. //-------------------------------------------------------------------------
  170. void move_range_pos_backward(it_index itx_first, it_index itx_last,
  171. size_t npos);
  172. //-------------------------------------------------------------------------
  173. // function : rearrange_with_index
  174. /// @brief rearrange the blocks with the relative positions of the index
  175. /// @param
  176. /// @param
  177. /// @param
  178. /// @return
  179. //-------------------------------------------------------------------------
  180. void rearrange_with_index(void);
  181. //---------------------------------------------------------------------------
  182. };// end struct merge_block
  183. //---------------------------------------------------------------------------
  184. //
  185. //############################################################################
  186. // ##
  187. // N O N I N L I N E F U N C T IO N S ##
  188. // ##
  189. //############################################################################
  190. //
  191. //-------------------------------------------------------------------------
  192. // function :
  193. /// @brief
  194. /// @param
  195. /// @return
  196. //-------------------------------------------------------------------------
  197. template<class Iter_t, class Compare, uint32_t Power2>
  198. void merge_block<Iter_t, Compare, Power2>
  199. ::merge_range_pos(it_index itx_first, it_index itx_mid,it_index itx_last)
  200. {
  201. assert((itx_last - itx_mid) >= 0 and (itx_mid - itx_first) >= 0);
  202. size_t nelemA = (itx_mid - itx_first), nelemB = (itx_last - itx_mid);
  203. if (nelemA == 0 or nelemB == 0) return;
  204. //-------------------------------------------------------------------
  205. // Create two index with the position of the blocks to merge
  206. //-------------------------------------------------------------------
  207. std::vector<size_t> indexA, indexB;
  208. indexA.reserve(nelemA + 1);
  209. indexB.reserve(nelemB);
  210. indexA.insert(indexA.begin(), itx_first, itx_mid);
  211. indexB.insert(indexB.begin(), itx_mid, itx_last);
  212. it_index itx_out = itx_first;
  213. it_index itxA = indexA.begin(), itxB = indexB.begin();
  214. range_it rngA, rngB;
  215. Iter_t itA = global_range.first, itB = global_range.first;
  216. bool validA = false, validB = false;
  217. while (itxA != indexA.end() and itxB != indexB.end())
  218. { //----------------------------------------------------------------
  219. // Load valid ranges from the itxA and ItxB positions
  220. //----------------------------------------------------------------
  221. if (not validA)
  222. {
  223. rngA = get_range(*itxA);
  224. itA = rngA.first;
  225. validA = true;
  226. };
  227. if (not validB)
  228. {
  229. rngB = get_range(*itxB);
  230. itB = rngB.first;
  231. validB = true;
  232. };
  233. //----------------------------------------------------------------
  234. // If don't have merge betweeen the blocks, pass directly the
  235. // position of the block to itx_out
  236. //----------------------------------------------------------------
  237. if (ptr_circ->size() == 0)
  238. {
  239. if (not cmp(*rngB.front(), *rngA.back()))
  240. {
  241. *(itx_out++) = *(itxA++);
  242. validA = false;
  243. continue;
  244. };
  245. if (cmp(*rngB.back(), *rngA.front()))
  246. {
  247. if (not is_tail(*itxB))
  248. *(itx_out++) = *itxB;
  249. else ptr_circ->push_move_back(rngB.first, rngB.size());
  250. ++itxB;
  251. validB = false;
  252. continue;
  253. };
  254. };
  255. //----------------------------------------------------------------
  256. // Normal merge
  257. //----------------------------------------------------------------
  258. bool side = util::merge_circular(itA, rngA.last, itB, rngB.last,
  259. *ptr_circ, cmp, itA, itB);
  260. if (side)
  261. { // rngA is finished
  262. ptr_circ->pop_move_front(rngA.first, rngA.size());
  263. *(itx_out++) = *(itxA++);
  264. validA = false;
  265. }
  266. else
  267. { // rngB is finished
  268. if (not is_tail(*itxB))
  269. {
  270. ptr_circ->pop_move_front(rngB.first, rngB.size());
  271. *(itx_out++) = *itxB;
  272. };
  273. ++itxB;
  274. validB = false;
  275. };
  276. }; // end while
  277. if (itxA == indexA.end())
  278. { // the index A is finished
  279. rngB = get_range(*itxB);
  280. ptr_circ->pop_move_front(rngB.first, ptr_circ->size());
  281. while (itxB != indexB.end())
  282. *(itx_out++) = *(itxB++);
  283. }
  284. else
  285. { // The list B is finished
  286. rngA = get_range(*itxA);
  287. if (ntail != 0 and indexB.back() == (nblock - 1)) // exist tail
  288. { // add the tail block to indexA, and shift the element
  289. indexA.push_back(indexB.back());
  290. size_t numA = size_t(itA - rngA.first);
  291. ptr_circ->pop_move_back(rngA.first, numA);
  292. move_range_pos_backward(itxA, indexA.end(), ntail);
  293. };
  294. ptr_circ->pop_move_front(rngA.first, ptr_circ->size());
  295. while (itxA != indexA.end())
  296. *(itx_out++) = *(itxA++);
  297. };
  298. };
  299. //-------------------------------------------------------------------------
  300. // function : move_range_pos_backward
  301. /// @brief Move backward the elements of a range of blocks in a index
  302. /// @param itx_first : iterator to the position of the first block
  303. /// @param itx_last : itertor to the position of the last block
  304. /// @param npos : number of positions to move. Must be less than BLOCK_SIZE
  305. /// @return
  306. //-------------------------------------------------------------------------
  307. template<class Iter_t, class Compare, uint32_t Power2>
  308. void merge_block<Iter_t, Compare, Power2>
  309. ::move_range_pos_backward(it_index itx_first, it_index itx_last, size_t npos)
  310. {
  311. assert((itx_last - itx_first) >= 0 and npos <= BLOCK_SIZE);
  312. //--------------------------------------------------------------------
  313. // Processing the last block. Must be ready fore to accept npos
  314. // elements from the upper block
  315. //--------------------------------------------------------------------
  316. range_it rng1 = get_range(*(itx_last - 1));
  317. assert(rng1.size() >= npos);
  318. if (rng1.size() > npos)
  319. {
  320. size_t nmove = rng1.size() - npos;
  321. util::move_backward(rng1.last, rng1.first, rng1.first + nmove);
  322. };
  323. //--------------------------------------------------------------------
  324. // Movement of elements between blocks
  325. //--------------------------------------------------------------------
  326. for (it_index itx = itx_last - 1; itx != itx_first;)
  327. {
  328. --itx;
  329. range_it rng2 = rng1;
  330. rng1 = get_range(*itx);
  331. Iter_t it_mid1 = rng1.last - npos, it_mid2 = rng2.first + npos;
  332. util::move_backward(it_mid2, it_mid1, rng1.last);
  333. util::move_backward(rng1.last, rng1.first, it_mid1);
  334. };
  335. };
  336. //-------------------------------------------------------------------------
  337. // function : rearrange_with_index
  338. /// @brief rearrange the blocks with the relative positions of the index
  339. /// @param
  340. /// @param
  341. /// @param
  342. /// @return
  343. //-------------------------------------------------------------------------
  344. template<class Iter_t, class Compare, uint32_t Power2>
  345. void merge_block<Iter_t, Compare, Power2>
  346. ::rearrange_with_index(void)
  347. { //--------------------------------------------------------------------
  348. // Code
  349. //--------------------------------------------------------------------
  350. size_t pos_dest, pos_src, pos_ini;
  351. size_t nelem = index.size();
  352. ptr_circ->clear();
  353. value_t * aux = ptr_circ->get_buffer();
  354. range_buf rng_buf(aux, aux + ptr_circ->NMAX);
  355. pos_ini = 0;
  356. while (pos_ini < nelem)
  357. {
  358. while (pos_ini < nelem and index[pos_ini] == pos_ini)
  359. ++pos_ini;
  360. if (pos_ini == nelem) return;
  361. pos_dest = pos_src = pos_ini;
  362. rng_buf = move_forward(rng_buf, get_range(pos_ini));
  363. pos_src = index[pos_ini];
  364. while (pos_src != pos_ini)
  365. {
  366. move_forward(get_range(pos_dest), get_range(pos_src));
  367. index[pos_dest] = pos_dest;
  368. pos_dest = pos_src;
  369. pos_src = index[pos_src];
  370. };
  371. move_forward(get_range(pos_dest), rng_buf);
  372. index[pos_dest] = pos_dest;
  373. ++pos_ini;
  374. };
  375. };
  376. //****************************************************************************
  377. };// End namespace common
  378. };// End namespace sort
  379. };// End namespace boost
  380. //****************************************************************************
  381. #endif