merge_block.hpp 16 KB

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