Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:51:47

0001 //----------------------------------------------------------------------------
0002 /// @file spinsort.hpp
0003 /// @brief Spin Sort algorithm
0004 ///
0005 /// @author Copyright (c) 2016 Francisco José Tapia (fjtapia@gmail.com )\n
0006 ///         Distributed under the Boost Software License, Version 1.0.\n
0007 ///         ( See accompanying file LICENSE_1_0.txt or copy at
0008 ///           http://www.boost.org/LICENSE_1_0.txt  )
0009 /// @version 0.1
0010 ///
0011 /// @remarks
0012 //-----------------------------------------------------------------------------
0013 #ifndef __BOOST_SORT_PARALLEL_ALGORITHM_SPIN_SORT_HPP
0014 #define __BOOST_SORT_PARALLEL_ALGORITHM_SPIN_SORT_HPP
0015 
0016 
0017 #include <ciso646>
0018 #include <cstdlib>
0019 #include <functional>
0020 #include <iterator>
0021 #include <memory>
0022 #include <type_traits>
0023 #include <vector>
0024 #include <cstddef>
0025 #include <boost/sort/insert_sort/insert_sort.hpp>
0026 #include <boost/sort/common/util/traits.hpp>
0027 #include <boost/sort/common/util/algorithm.hpp>
0028 #include <boost/sort/common/range.hpp>
0029 #include <boost/sort/common/indirect.hpp>
0030 
0031 
0032 namespace boost
0033 {
0034 namespace sort
0035 {
0036 namespace spin_detail
0037 {
0038 
0039 //----------------------------------------------------------------------------
0040 //                USING SENTENCES
0041 //----------------------------------------------------------------------------
0042 namespace bsc = boost::sort::common;
0043 using bsc::range;
0044 using bsc::util::nbits64;
0045 using bsc::util::compare_iter;
0046 using bsc::util::value_iter;
0047 using boost::sort::insert_sort;
0048 
0049 //
0050 //############################################################################
0051 //                                                                          ##
0052 //          D E F I N I T I O N S    O F    F U N C T I O N S               ##
0053 //                                                                          ##
0054 //############################################################################
0055 //
0056 template <class Iter1_t, class Iter2_t, typename Compare>
0057 static void insert_partial_sort (Iter1_t first, Iter1_t mid,
0058                                  Iter1_t last, Compare comp,
0059                                  const range<Iter2_t> &rng_aux);
0060 
0061 template<class Iter1_t, class Iter2_t, class Compare>
0062 static bool check_stable_sort (const range<Iter1_t> &rng_data,
0063                                const range<Iter2_t> &rng_aux, Compare comp);
0064 
0065 template<class Iter1_t, class Iter2_t, class Compare>
0066 static void range_sort (const range<Iter1_t> &range1,
0067                         const range<Iter2_t> &range2, Compare comp,
0068                         uint32_t level);
0069 
0070 template<class Iter1_t, class Iter2_t, class Compare>
0071 static void sort_range_sort (const range<Iter1_t> &rng_data,
0072                              const range<Iter2_t> &rng_aux, Compare comp);
0073 
0074 //
0075 //-----------------------------------------------------------------------------
0076 //  function : insert_partial_sort
0077 /// @brief : Insertion sort of elements sorted
0078 /// @param first: iterator to the first element of the range
0079 /// @param mid : last pointer of the sorted data, and first pointer to the
0080 ///               elements to insert
0081 /// @param last : iterator to the next element of the last in the range
0082 /// @param comp :
0083 /// @comments : the two ranges are sorted
0084 //-----------------------------------------------------------------------------
0085 template<class Iter1_t, class Iter2_t, typename Compare>
0086 static void insert_partial_sort (Iter1_t first, Iter1_t mid, Iter1_t last,
0087                                  Compare comp, const range <Iter2_t> &rng_aux)
0088 {
0089     //------------------------------------------------------------------------
0090     //                 metaprogram
0091     //------------------------------------------------------------------------
0092     typedef value_iter<Iter1_t> value_t;
0093     typedef value_iter<Iter2_t> value2_t;
0094     static_assert (std::is_same<value_t, value2_t>::value,
0095                     "Incompatible iterators\n");
0096 
0097     //--------------------------------------------------------------------
0098     //                   program
0099     //--------------------------------------------------------------------
0100     assert(size_t(last - mid) <= rng_aux.size());
0101 
0102     if (mid == last) return;
0103     //insertionsort ( mid, last, comp);
0104     if (first == mid) return;
0105 
0106     //------------------------------------------------------------------------
0107     // creation of the vector of elements to insert and their position in the
0108     // sorted part
0109     // the data are inserted in rng_aux
0110     //-----------------------------------------------------------------------
0111     std::vector<Iter1_t> viter;
0112     Iter2_t beta = rng_aux.first, data = rng_aux.first;
0113 
0114     for (Iter1_t alpha = mid; alpha != last; ++alpha)
0115         *(beta++) = std::move(*alpha);
0116 
0117     size_t ndata = last - mid;
0118 
0119     Iter1_t linf = first, lsup = mid;
0120     for (uint32_t i = 0; i < ndata; ++i)
0121     {
0122         Iter1_t it1 = std::upper_bound(linf, lsup, *(data + i), comp);
0123         viter.push_back(it1);
0124         linf = it1;
0125     };
0126 
0127     // moving the elements
0128     viter.push_back(mid);
0129     for (uint32_t i = viter.size() - 1; i != 0; --i)
0130     {
0131         Iter1_t src = viter[i], limit = viter[i - 1];
0132         Iter1_t dest = src + (i);
0133         while (src != limit) *(--dest) = std::move(*(--src));
0134         *(viter[i - 1] + (i - 1)) = std::move(*(data + (i - 1)));
0135     };
0136 }
0137 ;
0138 //-----------------------------------------------------------------------------
0139 //  function : check_stable_sort
0140 /// @brief check if the elements between first and last are osted or reverse
0141 ///        sorted. If the number of elements not sorted is small, insert in
0142 ///        the sorted part
0143 /// @param range_input : range with the elements to sort
0144 /// @param range_buffer : range with the elements sorted
0145 /// @param comp : object for to compare two elements
0146 /// @param level : when is 1, sort with the insertionsort algorithm
0147 ///                if not make a recursive call splitting the ranges
0148 //
0149 /// @comments : if the number of levels is odd, the data are in the first
0150 /// parameter of range_sort, and the results appear in the second parameter
0151 /// If the number of levels is even, the data are in the second
0152 /// parameter of range_sort, and the results are in the same parameter
0153 //-----------------------------------------------------------------------------
0154 template<class Iter1_t, class Iter2_t, class Compare>
0155 static bool check_stable_sort(const range<Iter1_t> &rng_data,
0156                               const range<Iter2_t> &rng_aux, Compare comp)
0157 {
0158     //------------------------------------------------------------------------
0159     //              metaprogramming
0160     //------------------------------------------------------------------------
0161     typedef value_iter<Iter1_t> value_t;
0162     typedef value_iter<Iter2_t> value2_t;
0163     static_assert (std::is_same<value_t, value2_t>::value,
0164                     "Incompatible iterators\n");
0165 
0166     //------------------------------------------------------------------------
0167     //                    program
0168     //------------------------------------------------------------------------
0169     // the maximun number of elements not ordered, for to be inserted in the
0170     // sorted part
0171     //const ptrdiff_t  min_insert_partial_sort = 32 ;
0172     const size_t ndata = rng_data.size();
0173     if (ndata < 32)
0174     {
0175         insert_sort(rng_data.first, rng_data.last, comp);
0176         return true;
0177     };
0178     const size_t min_insert_partial_sort =
0179                     ((ndata >> 3) < 33) ? 32 : (ndata >> 3);
0180     if (ndata < 2) return true;
0181 
0182     // check if sorted
0183     bool sw = true;
0184     Iter1_t it2 = rng_data.first + 1;
0185     for (Iter1_t it1 = rng_data.first;
0186                     it2 != rng_data.last and (sw = not comp(*it2, *it1)); it1 =
0187                                     it2++)
0188         ;
0189     if (sw) return true;
0190 
0191     // insert the elements between it1 and last
0192     if (size_t(rng_data.last - it2) < min_insert_partial_sort)
0193     {
0194         sort_range_sort(range<Iter1_t>(it2, rng_data.last), rng_aux, comp);
0195         insert_partial_sort(rng_data.first, it2, rng_data.last, comp, rng_aux);
0196         return true;
0197     };
0198 
0199     // check if reverse sorted
0200     if ((it2 != (rng_data.first + 1))) return false;
0201     sw = true;
0202     for (Iter1_t it1 = rng_data.first;
0203                     it2 != rng_data.last and (sw = comp(*it2, *it1)); it1 =
0204                                     it2++)
0205         ;
0206     if (size_t(rng_data.last - it2) >= min_insert_partial_sort) return false;
0207 
0208     // reverse the elements between first and it1
0209     size_t nreverse = it2 - rng_data.first;
0210     Iter1_t alpha(rng_data.first), beta(it2 - 1), mid(
0211                     rng_data.first + (nreverse >> 1));
0212     while (alpha != mid) {
0213     using std::swap;
0214         swap(*(alpha++), *(beta--));
0215     }
0216 
0217     // insert the elements between it1 and last
0218     if (it2 != rng_data.last)
0219     {
0220         sort_range_sort(range<Iter1_t>(it2, rng_data.last), rng_aux, comp);
0221         insert_partial_sort(rng_data.first, it2, rng_data.last, comp, rng_aux);
0222     };
0223     return true;
0224 }
0225 ;
0226 //-----------------------------------------------------------------------------
0227 //  function : range_sort
0228 /// @brief this function divide r_input in two parts, sort it,and merge moving
0229 ///        the elements to range_buf
0230 /// @param range_input : range with the elements to sort
0231 /// @param range_buffer : range with the elements sorted
0232 /// @param comp : object for to compare two elements
0233 /// @param level : when is 1, sort with the insertionsort algorithm
0234 ///                if not make a recursive call splitting the ranges
0235 //
0236 /// @comments : if the number of levels is odd, the data are in the first
0237 /// parameter of range_sort, and the results appear in the second parameter
0238 /// If the number of levels is even, the data are in the second
0239 /// parameter of range_sort, and the results are in the same parameter
0240 /// The two ranges must have the same size
0241 //-----------------------------------------------------------------------------
0242 template<class Iter1_t, class Iter2_t, class Compare>
0243 static void range_sort(const range<Iter1_t> &range1,
0244                        const range<Iter2_t> &range2, Compare comp,
0245                        uint32_t level)
0246 {
0247     //-----------------------------------------------------------------------
0248     //                  metaprogram
0249     //-----------------------------------------------------------------------
0250     typedef value_iter<Iter1_t> value_t;
0251     typedef value_iter<Iter2_t> value2_t;
0252     static_assert (std::is_same<value_t, value2_t>::value,
0253                     "Incompatible iterators\n");
0254 
0255     //-----------------------------------------------------------------------
0256     //                  program
0257     //-----------------------------------------------------------------------
0258     typedef range<Iter1_t> range_it1;
0259     typedef range<Iter2_t> range_it2;
0260     assert(range1.size() == range2.size() and level != 0);
0261 
0262     //------------------- check if sort --------------------------------------
0263     if (range1.size() > 1024)
0264     {
0265         if ((level & 1) == 0)
0266         {
0267             if (check_stable_sort(range2, range1, comp)) return;
0268         }
0269         else
0270         {
0271             if (check_stable_sort(range1, range2, comp))
0272             {
0273                 move_forward(range2, range1);
0274                 return;
0275             };
0276         };
0277     };
0278 
0279     //------------------- normal process -----------------------------------
0280     size_t nelem1 = (range1.size() + 1) >> 1;
0281     range_it1 range_input1(range1.first, range1.first + nelem1),
0282                            range_input2(range1.first + nelem1, range1.last);
0283 
0284     if (level < 2)
0285     {
0286         insert_sort(range_input1.first, range_input1.last, comp);
0287         insert_sort(range_input2.first, range_input2.last, comp);
0288     }
0289     else
0290     {
0291         range_sort (range_it2(range2.first, range2.first + nelem1),
0292                     range_input1, comp, level - 1);
0293 
0294         range_sort (range_it2(range2.first + nelem1, range2.last),
0295                     range_input2, comp, level - 1);
0296     };
0297 
0298     merge(range2, range_input1, range_input2, comp);
0299 }
0300 ;
0301 //-----------------------------------------------------------------------------
0302 //  function : sort_range_sort
0303 /// @brief this sort elements using the range_sort function and receiving a
0304 ///        buffer of initialized memory
0305 /// @param rng_data : range with the elements to sort
0306 /// @param rng_aux : range of at least the same memory than rng_data used as
0307 ///                  auxiliary memory in the sorting
0308 /// @param comp : object for to compare two elements
0309 //-----------------------------------------------------------------------------
0310 template<class Iter1_t, class Iter2_t, class Compare>
0311 static void sort_range_sort(const range<Iter1_t> &rng_data,
0312                             const range<Iter2_t> &rng_aux, Compare comp)
0313 {
0314     //-----------------------------------------------------------------------
0315     //                  metaprogram
0316     //-----------------------------------------------------------------------
0317     typedef value_iter<Iter1_t> value_t;
0318     typedef value_iter<Iter2_t> value2_t;
0319     static_assert (std::is_same<value_t, value2_t>::value,
0320                     "Incompatible iterators\n");
0321 
0322     //------------------------------------------------------------------------
0323     //                    program
0324     //------------------------------------------------------------------------
0325     // minimal number of element before to jump to insertionsort
0326     static const uint32_t sort_min = 32;
0327     if (rng_data.size() <= sort_min)
0328     {
0329         insert_sort(rng_data.first, rng_data.last, comp);
0330         return;
0331     };
0332 
0333 #ifdef __BS_DEBUG
0334     assert (rng_aux.size () >= rng_data.size ());
0335 #endif
0336 
0337     range<Iter2_t> rng_buffer(rng_aux.first, rng_aux.first + rng_data.size());
0338     uint32_t nlevel =
0339                     nbits64(((rng_data.size() + sort_min - 1) / sort_min) - 1);
0340     //assert (nlevel != 0);
0341 
0342     if ((nlevel & 1) == 0)
0343     {
0344         range_sort(rng_buffer, rng_data, comp, nlevel);
0345     }
0346     else
0347     {
0348         range_sort(rng_data, rng_buffer, comp, nlevel);
0349         move_forward(rng_data, rng_buffer);
0350     };
0351 }
0352 ;
0353 //
0354 //############################################################################
0355 //                                                                          ##
0356 //                              S T R U C T                                 ##
0357 //                                                                          ##
0358 //                           S P I N _ S O R T                              ##
0359 //                                                                          ##
0360 //############################################################################
0361 //---------------------------------------------------------------------------
0362 /// @struct spin_sort
0363 /// @brief  This class implement s stable sort algorithm with 1 thread, with
0364 ///         an auxiliary memory of N/2 elements
0365 //----------------------------------------------------------------------------
0366 template<class Iter_t, typename Compare = compare_iter<Iter_t>>
0367 class spinsort
0368 {
0369     //------------------------------------------------------------------------
0370     //               DEFINITIONS AND CONSTANTS
0371     //------------------------------------------------------------------------
0372     typedef value_iter<Iter_t> value_t;
0373     typedef range<Iter_t> range_it;
0374     typedef range<value_t *> range_buf;
0375     // When the number of elements to sort is smaller than Sort_min, are sorted
0376     // by the insertion sort algorithm
0377     static const uint32_t Sort_min = 36;
0378 
0379     //------------------------------------------------------------------------
0380     //                      VARIABLES
0381     //------------------------------------------------------------------------
0382     // Pointer to the auxiliary memory
0383     value_t *ptr;
0384 
0385     // Number of elements in the auxiliary memory
0386     size_t nptr;
0387 
0388     // construct indicate if the auxiliary memory in initialized, and owner
0389     // indicate if the auxiliary memory had been created inside the object or
0390     // had
0391     // been received as a parameter
0392     bool construct = false, owner = false;
0393 
0394     //------------------------------------------------------------------------
0395     //                   PRIVATE FUNCTIONS
0396     //-------------------------------------------------------------------------
0397     spinsort (Iter_t first, Iter_t last, Compare comp, value_t *paux,
0398                size_t naux);
0399 
0400 public:
0401     //------------------------------------------------------------------------
0402     //                   PUBLIC FUNCTIONS
0403     //-------------------------------------------------------------------------
0404     spinsort(Iter_t first, Iter_t last, Compare comp = Compare())
0405     : spinsort(first, last, comp, nullptr, 0) { };
0406 
0407     spinsort(Iter_t first, Iter_t last, Compare comp, range_buf range_aux)
0408     : spinsort(first, last, comp, range_aux.first, range_aux.size()) { };
0409     //
0410     //-----------------------------------------------------------------------
0411     //  function :~spinsort
0412     /// @brief destructor of the struct. Destroy the elements if construct is
0413     /// true,
0414     ///        and return the memory if owner is true
0415     //-----------------------------------------------------------------------
0416     ~spinsort(void)
0417     {
0418         if (construct)
0419         {
0420             destroy(range<value_t *>(ptr, ptr + nptr));
0421             construct = false;
0422         };
0423         if (owner and ptr != nullptr) std::free (ptr);
0424     };
0425 };
0426 //----------------------------------------------------------------------------
0427 //        End of class spinsort
0428 //----------------------------------------------------------------------------
0429 //
0430 //-------------------------------------------------------------------------
0431 //  function : spinsort
0432 /// @brief constructor of the struct
0433 //
0434 /// @param first : iterator to the first element of the range to sort
0435 /// @param last : iterator after the last element to the range to sort
0436 /// @param comp : object for to compare two elements pointed by Iter_t
0437 ///               iterators
0438 /// @param paux : pointer to the auxiliary memory provided. If nullptr, the
0439 ///               memory is created inside the class
0440 /// @param naux : number of elements pointed by paux
0441 //------------------------------------------------------------------------
0442 template <class Iter_t, typename Compare>
0443 spinsort <Iter_t, Compare>
0444 ::spinsort (Iter_t first, Iter_t last, Compare comp, value_t *paux, size_t naux)
0445 : ptr(paux), nptr(naux), construct(false), owner(false)
0446 {
0447     range<Iter_t> range_input(first, last);
0448     assert(range_input.valid());
0449 
0450     size_t nelem = range_input.size();
0451     owner = construct = false;
0452 
0453     nptr = (nelem + 1) >> 1;
0454     size_t nelem_1 = nptr;
0455     size_t nelem_2 = nelem - nelem_1;
0456 
0457     if (nelem <= (Sort_min << 1))
0458     {
0459         insert_sort(range_input.first, range_input.last, comp);
0460         return;
0461     };
0462 
0463     //------------------- check if sort ---------------------------------
0464     bool sw = true;
0465     for (Iter_t it1 = first, it2 = first + 1; it2 != last
0466          and (sw = not comp(*it2, *it1)); it1 = it2++) ;
0467     if (sw) return;
0468 
0469     //------------------- check if reverse sort -------------------------
0470     sw = true;
0471     for (Iter_t it1 = first, it2 = first + 1;
0472          it2 != last and (sw = comp(*it2, *it1)); it1 = it2++);
0473     if (sw)
0474     {
0475     using std::swap;
0476         size_t nelem2 = nelem >> 1;
0477         Iter_t it1 = first, it2 = last - 1;
0478         for (size_t i = 0; i < nelem2; ++i)
0479             swap(*(it1++), *(it2--));
0480         return;
0481     };
0482 
0483     if (ptr == nullptr)
0484     {
0485         ptr = reinterpret_cast <value_t*> 
0486                 (std::malloc (nptr * sizeof(value_t)));
0487         
0488         if (ptr == nullptr) throw std::bad_alloc();
0489         owner = true;
0490     };
0491     range_buf range_aux(ptr, (ptr + nptr));
0492 
0493     //---------------------------------------------------------------------
0494     //                  Process
0495     //---------------------------------------------------------------------
0496     uint32_t nlevel = nbits64(((nelem + Sort_min - 1) / Sort_min) - 1) - 1;
0497     assert(nlevel != 0);
0498 
0499     if ((nlevel & 1) == 1)
0500     {
0501         //----------------------------------------------------------------
0502         // if the number of levels is odd, the data are in the first
0503         // parameter of range_sort, and the results appear in the second
0504         // parameter
0505         //----------------------------------------------------------------
0506         range_it range_1(first, first + nelem_2), range_2(first + nelem_2,
0507                         last);
0508         range_aux = move_construct(range_aux, range_2);
0509         construct = true;
0510 
0511         range_sort(range_aux, range_2, comp, nlevel);
0512         range_buf rng_bx(range_aux.first, range_aux.first + nelem_2);
0513 
0514         range_sort(range_1, rng_bx, comp, nlevel);
0515         merge_half(range_input, rng_bx, range_2, comp);
0516     }
0517     else
0518     {
0519         //----------------------------------------------------------------
0520         // If the number of levels is even, the data are in the second
0521         // parameter of range_sort, and the results are in the same
0522         //  parameter
0523         //----------------------------------------------------------------
0524         range_it range_1(first, first + nelem_1), range_2(first + nelem_1,
0525                         last);
0526         range_aux = move_construct(range_aux, range_1);
0527         construct = true;
0528 
0529         range_sort(range_1, range_aux, comp, nlevel);
0530 
0531         range_1.last = range_1.first + range_2.size();
0532         range_sort(range_1, range_2, comp, nlevel);
0533         merge_half(range_input, range_aux, range_2, comp);
0534     };
0535 };
0536 
0537 //****************************************************************************
0538 };//    End namepspace spin_detail
0539 //****************************************************************************
0540 //
0541 namespace bsc = boost::sort::common;
0542 //-----------------------------------------------------------------------------
0543 //  function : spinsort
0544 /// @brief this function implement a single thread stable sort
0545 ///
0546 /// @param first : iterator to the first element of the range to sort
0547 /// @param last : iterator after the last element to the range to sort
0548 /// @param comp : object for to compare two elements pointed by Iter_t
0549 ///               iterators
0550 //-----------------------------------------------------------------------------
0551 template <class Iter_t, class Compare = compare_iter<Iter_t>>
0552 inline void spinsort (Iter_t first, Iter_t last, Compare comp = Compare())
0553 {
0554     spin_detail::spinsort <Iter_t, Compare> (first, last, comp);
0555 };
0556 
0557 template <class Iter_t, class Compare = compare_iter<Iter_t>>
0558 inline void indirect_spinsort (Iter_t first, Iter_t last,
0559                                Compare comp = Compare())
0560 {
0561     typedef typename std::vector<Iter_t>::iterator itx_iter;
0562     typedef common::less_ptr_no_null <Iter_t, Compare> itx_comp;
0563     common::indirect_sort (spinsort<itx_iter, itx_comp>, first, last, comp);
0564 };
0565 
0566 //****************************************************************************
0567 };//    End namespace sort
0568 };//    End namepspace boost
0569 //****************************************************************************
0570 //
0571 #endif