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