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