Back to home page

EIC code displayed by LXR

 
 

    


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

0001 // Details for templated Spreadsort-based integer_sort.
0002 
0003 //          Copyright Steven J. Ross 2001 - 2014.
0004 // Distributed under the Boost Software License, Version 1.0.
0005 //    (See accompanying file LICENSE_1_0.txt or copy at
0006 //          http://www.boost.org/LICENSE_1_0.txt)
0007 
0008 // See http://www.boost.org/libs/sort for library home page.
0009 
0010 /*
0011 Some improvements suggested by:
0012 Phil Endecott and Frank Gennari
0013 */
0014 
0015 #ifndef BOOST_SORT_SPREADSORT_DETAIL_INTEGER_SORT_HPP
0016 #define BOOST_SORT_SPREADSORT_DETAIL_INTEGER_SORT_HPP
0017 #include <algorithm>
0018 #include <vector>
0019 #include <limits>
0020 #include <functional>
0021 #include <boost/static_assert.hpp>
0022 #include <boost/utility/enable_if.hpp>
0023 #include <boost/sort/spreadsort/detail/constants.hpp>
0024 #include <boost/sort/spreadsort/detail/spreadsort_common.hpp>
0025 #include <boost/cstdint.hpp>
0026 
0027 namespace boost {
0028 namespace sort {
0029 namespace spreadsort {
0030   namespace detail {
0031     // Return true if the list is sorted.  Otherwise, find the minimum and
0032     // maximum using <.
0033     template <class RandomAccessIter>
0034     inline bool
0035     is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last,
0036                                RandomAccessIter & max, RandomAccessIter & min)
0037     {
0038       min = max = current;
0039       //This assumes we have more than 1 element based on prior checks.
0040       while (!(*(current + 1) < *current)) {
0041         //If everything is in sorted order, return
0042         if (++current == last - 1)
0043           return true;
0044       }
0045 
0046       //The maximum is the last sorted element
0047       max = current;
0048       //Start from the first unsorted element
0049       while (++current < last) {
0050         if (*max < *current)
0051           max = current;
0052         else if (*current < *min)
0053           min = current;
0054       }
0055       return false;
0056     }
0057 
0058     // Return true if the list is sorted.  Otherwise, find the minimum and
0059     // maximum.
0060     // Use a user-defined comparison operator
0061     template <class RandomAccessIter, class Compare>
0062     inline bool
0063     is_sorted_or_find_extremes(RandomAccessIter current, RandomAccessIter last,
0064                 RandomAccessIter & max, RandomAccessIter & min, Compare comp)
0065     {
0066       min = max = current;
0067       while (!comp(*(current + 1), *current)) {
0068         //If everything is in sorted order, return
0069         if (++current == last - 1)
0070           return true;
0071       }
0072 
0073       //The maximum is the last sorted element
0074       max = current;
0075       while (++current < last) {
0076         if (comp(*max, *current))
0077           max = current;
0078         else if (comp(*current, *min))
0079           min = current;
0080       }
0081       return false;
0082     }
0083 
0084     //Gets a non-negative right bit shift to operate as a logarithmic divisor
0085     template<unsigned log_mean_bin_size>
0086     inline int
0087     get_log_divisor(size_t count, int log_range)
0088     {
0089       int log_divisor;
0090       //If we can finish in one iteration without exceeding either
0091       //(2 to the max_finishing_splits) or n bins, do so
0092       if ((log_divisor = log_range - rough_log_2_size(count)) <= 0 && 
0093          log_range <= max_finishing_splits)
0094         log_divisor = 0; 
0095       else {
0096         //otherwise divide the data into an optimized number of pieces
0097         log_divisor += log_mean_bin_size;
0098         //Cannot exceed max_splits or cache misses slow down bin lookups
0099         if ((log_range - log_divisor) > max_splits)
0100           log_divisor = log_range - max_splits;
0101       }
0102       return log_divisor;
0103     }
0104 
0105     //Implementation for recursive integer sorting
0106     template <class RandomAccessIter, class Div_type, class Size_type>
0107     inline void
0108     spreadsort_rec(RandomAccessIter first, RandomAccessIter last,
0109               std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
0110               , size_t *bin_sizes)
0111     {
0112       //This step is roughly 10% of runtime, but it helps avoid worst-case
0113       //behavior and improve behavior with real data
0114       //If you know the maximum and minimum ahead of time, you can pass those
0115       //values in and skip this step for the first iteration
0116       RandomAccessIter max, min;
0117       if (is_sorted_or_find_extremes(first, last, max, min))
0118         return;
0119       RandomAccessIter * target_bin;
0120       unsigned log_divisor = get_log_divisor<int_log_mean_bin_size>(
0121           last - first, rough_log_2_size(Size_type((*max >> 0) - (*min >> 0))));
0122       Div_type div_min = *min >> log_divisor;
0123       Div_type div_max = *max >> log_divisor;
0124       unsigned bin_count = unsigned(div_max - div_min) + 1;
0125       unsigned cache_end;
0126       RandomAccessIter * bins =
0127         size_bins(bin_sizes, bin_cache, cache_offset, cache_end, bin_count);
0128 
0129       //Calculating the size of each bin; this takes roughly 10% of runtime
0130       for (RandomAccessIter current = first; current != last;)
0131         bin_sizes[size_t((*(current++) >> log_divisor) - div_min)]++;
0132       //Assign the bin positions
0133       bins[0] = first;
0134       for (unsigned u = 0; u < bin_count - 1; u++)
0135         bins[u + 1] = bins[u] + bin_sizes[u];
0136 
0137       RandomAccessIter nextbinstart = first;
0138       //Swap into place
0139       //This dominates runtime, mostly in the swap and bin lookups
0140       for (unsigned u = 0; u < bin_count - 1; ++u) {
0141         RandomAccessIter * local_bin = bins + u;
0142         nextbinstart += bin_sizes[u];
0143         //Iterating over each element in this bin
0144         for (RandomAccessIter current = *local_bin; current < nextbinstart;
0145             ++current) {
0146           //Swapping elements in current into place until the correct
0147           //element has been swapped in
0148           for (target_bin = (bins + ((*current >> log_divisor) - div_min));
0149               target_bin != local_bin;
0150             target_bin = bins + ((*current >> log_divisor) - div_min)) {
0151             //3-way swap; this is about 1% faster than a 2-way swap
0152             //The main advantage is less copies are involved per item
0153             //put in the correct place
0154             typename std::iterator_traits<RandomAccessIter>::value_type tmp;
0155             RandomAccessIter b = (*target_bin)++;
0156             RandomAccessIter * b_bin = bins + ((*b >> log_divisor) - div_min);
0157             if (b_bin != local_bin) {
0158               RandomAccessIter c = (*b_bin)++;
0159               tmp = *c;
0160               *c = *b;
0161             }
0162             else
0163               tmp = *b;
0164             *b = *current;
0165             *current = tmp;
0166           }
0167         }
0168         *local_bin = nextbinstart;
0169       }
0170       bins[bin_count - 1] = last;
0171 
0172       //If we've bucketsorted, the array is sorted and we should skip recursion
0173       if (!log_divisor)
0174         return;
0175       //log_divisor is the remaining range; calculating the comparison threshold
0176       size_t max_count =
0177         get_min_count<int_log_mean_bin_size, int_log_min_split_count,
0178                       int_log_finishing_count>(log_divisor);
0179 
0180       //Recursing
0181       RandomAccessIter lastPos = first;
0182       for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
0183           ++u) {
0184         Size_type count = bin_cache[u] - lastPos;
0185         //don't sort unless there are at least two items to Compare
0186         if (count < 2)
0187           continue;
0188         //using boost::sort::pdqsort if its worst-case is better
0189         if (count < max_count)
0190           boost::sort::pdqsort(lastPos, bin_cache[u]);
0191         else
0192           spreadsort_rec<RandomAccessIter, Div_type, Size_type>(lastPos,
0193                                                                  bin_cache[u],
0194                                                                  bin_cache,
0195                                                                  cache_end,
0196                                                                  bin_sizes);
0197       }
0198     }
0199 
0200     //Generic bitshift-based 3-way swapping code
0201     template <class RandomAccessIter, class Div_type, class Right_shift>
0202     inline void inner_swap_loop(RandomAccessIter * bins,
0203       const RandomAccessIter & next_bin_start, unsigned ii, Right_shift &rshift
0204       , const unsigned log_divisor, const Div_type div_min)
0205     {
0206       RandomAccessIter * local_bin = bins + ii;
0207       for (RandomAccessIter current = *local_bin; current < next_bin_start;
0208           ++current) {
0209         for (RandomAccessIter * target_bin =
0210             (bins + (rshift(*current, log_divisor) - div_min));
0211             target_bin != local_bin;
0212             target_bin = bins + (rshift(*current, log_divisor) - div_min)) {
0213           typename std::iterator_traits<RandomAccessIter>::value_type tmp;
0214           RandomAccessIter b = (*target_bin)++;
0215           RandomAccessIter * b_bin =
0216             bins + (rshift(*b, log_divisor) - div_min);
0217           //Three-way swap; if the item to be swapped doesn't belong
0218           //in the current bin, swap it to where it belongs
0219           if (b_bin != local_bin) {
0220             RandomAccessIter c = (*b_bin)++;
0221             tmp = *c;
0222             *c = *b;
0223           }
0224           //Note: we could increment current once the swap is done in this case
0225           //but that seems to impair performance
0226           else
0227             tmp = *b;
0228           *b = *current;
0229           *current = tmp;
0230         }
0231       }
0232       *local_bin = next_bin_start;
0233     }
0234 
0235     //Standard swapping wrapper for ascending values
0236     template <class RandomAccessIter, class Div_type, class Right_shift>
0237     inline void swap_loop(RandomAccessIter * bins,
0238              RandomAccessIter & next_bin_start, unsigned ii, Right_shift &rshift
0239              , const size_t *bin_sizes
0240              , const unsigned log_divisor, const Div_type div_min)
0241     {
0242       next_bin_start += bin_sizes[ii];
0243       inner_swap_loop<RandomAccessIter, Div_type, Right_shift>(bins,
0244                               next_bin_start, ii, rshift, log_divisor, div_min);
0245     }
0246 
0247     //Functor implementation for recursive sorting
0248     template <class RandomAccessIter, class Div_type, class Right_shift,
0249               class Compare, class Size_type, unsigned log_mean_bin_size,
0250                 unsigned log_min_split_count, unsigned log_finishing_count>
0251     inline void
0252     spreadsort_rec(RandomAccessIter first, RandomAccessIter last,
0253           std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
0254           , size_t *bin_sizes, Right_shift rshift, Compare comp)
0255     {
0256       RandomAccessIter max, min;
0257       if (is_sorted_or_find_extremes(first, last, max, min, comp))
0258         return;
0259       unsigned log_divisor = get_log_divisor<log_mean_bin_size>(last - first,
0260             rough_log_2_size(Size_type(rshift(*max, 0) - rshift(*min, 0))));
0261       Div_type div_min = rshift(*min, log_divisor);
0262       Div_type div_max = rshift(*max, log_divisor);
0263       unsigned bin_count = unsigned(div_max - div_min) + 1;
0264       unsigned cache_end;
0265       RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
0266                                           cache_end, bin_count);
0267 
0268       //Calculating the size of each bin
0269       for (RandomAccessIter current = first; current != last;)
0270         bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
0271       bins[0] = first;
0272       for (unsigned u = 0; u < bin_count - 1; u++)
0273         bins[u + 1] = bins[u] + bin_sizes[u];
0274 
0275       //Swap into place
0276       RandomAccessIter next_bin_start = first;
0277       for (unsigned u = 0; u < bin_count - 1; ++u)
0278         swap_loop<RandomAccessIter, Div_type, Right_shift>(bins, next_bin_start,
0279                                   u, rshift, bin_sizes, log_divisor, div_min);
0280       bins[bin_count - 1] = last;
0281 
0282       //If we've bucketsorted, the array is sorted
0283       if (!log_divisor)
0284         return;
0285 
0286       //Recursing
0287       size_t max_count = get_min_count<log_mean_bin_size, log_min_split_count,
0288                           log_finishing_count>(log_divisor);
0289       RandomAccessIter lastPos = first;
0290       for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
0291           ++u) {
0292         size_t count = bin_cache[u] - lastPos;
0293         if (count < 2)
0294           continue;
0295         if (count < max_count)
0296           boost::sort::pdqsort(lastPos, bin_cache[u], comp);
0297         else
0298           spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
0299         Size_type, log_mean_bin_size, log_min_split_count, log_finishing_count>
0300       (lastPos, bin_cache[u], bin_cache, cache_end, bin_sizes, rshift, comp);
0301       }
0302     }
0303 
0304     //Functor implementation for recursive sorting with only Shift overridden
0305     template <class RandomAccessIter, class Div_type, class Right_shift,
0306               class Size_type, unsigned log_mean_bin_size,
0307               unsigned log_min_split_count, unsigned log_finishing_count>
0308     inline void
0309     spreadsort_rec(RandomAccessIter first, RandomAccessIter last,
0310               std::vector<RandomAccessIter> &bin_cache, unsigned cache_offset
0311               , size_t *bin_sizes, Right_shift rshift)
0312     {
0313       RandomAccessIter max, min;
0314       if (is_sorted_or_find_extremes(first, last, max, min))
0315         return;
0316       unsigned log_divisor = get_log_divisor<log_mean_bin_size>(last - first,
0317             rough_log_2_size(Size_type(rshift(*max, 0) - rshift(*min, 0))));
0318       Div_type div_min = rshift(*min, log_divisor);
0319       Div_type div_max = rshift(*max, log_divisor);
0320       unsigned bin_count = unsigned(div_max - div_min) + 1;
0321       unsigned cache_end;
0322       RandomAccessIter * bins = size_bins(bin_sizes, bin_cache, cache_offset,
0323                                           cache_end, bin_count);
0324 
0325       //Calculating the size of each bin
0326       for (RandomAccessIter current = first; current != last;)
0327         bin_sizes[unsigned(rshift(*(current++), log_divisor) - div_min)]++;
0328       bins[0] = first;
0329       for (unsigned u = 0; u < bin_count - 1; u++)
0330         bins[u + 1] = bins[u] + bin_sizes[u];
0331 
0332       //Swap into place
0333       RandomAccessIter nextbinstart = first;
0334       for (unsigned ii = 0; ii < bin_count - 1; ++ii)
0335         swap_loop<RandomAccessIter, Div_type, Right_shift>(bins, nextbinstart,
0336                                 ii, rshift, bin_sizes, log_divisor, div_min);
0337       bins[bin_count - 1] = last;
0338 
0339       //If we've bucketsorted, the array is sorted
0340       if (!log_divisor)
0341         return;
0342 
0343       //Recursing
0344       size_t max_count = get_min_count<log_mean_bin_size, log_min_split_count,
0345                           log_finishing_count>(log_divisor);
0346       RandomAccessIter lastPos = first;
0347       for (unsigned u = cache_offset; u < cache_end; lastPos = bin_cache[u],
0348           ++u) {
0349         size_t count = bin_cache[u] - lastPos;
0350         if (count < 2)
0351           continue;
0352         if (count < max_count)
0353           boost::sort::pdqsort(lastPos, bin_cache[u]);
0354         else
0355           spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Size_type,
0356           log_mean_bin_size, log_min_split_count, log_finishing_count>(lastPos,
0357                       bin_cache[u], bin_cache, cache_end, bin_sizes, rshift);
0358       }
0359     }
0360 
0361     //Holds the bin vector and makes the initial recursive call
0362     template <class RandomAccessIter, class Div_type>
0363     //Only use spreadsort if the integer can fit in a size_t
0364     inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t),
0365                                                             void >::type
0366     integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type)
0367     {
0368       size_t bin_sizes[1 << max_finishing_splits];
0369       std::vector<RandomAccessIter> bin_cache;
0370       spreadsort_rec<RandomAccessIter, Div_type, size_t>(first, last,
0371           bin_cache, 0, bin_sizes);
0372     }
0373 
0374     //Holds the bin vector and makes the initial recursive call
0375     template <class RandomAccessIter, class Div_type>
0376     //Only use spreadsort if the integer can fit in a uintmax_t
0377     inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t))
0378       && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
0379     integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type)
0380     {
0381       size_t bin_sizes[1 << max_finishing_splits];
0382       std::vector<RandomAccessIter> bin_cache;
0383       spreadsort_rec<RandomAccessIter, Div_type, boost::uintmax_t>(first,
0384           last, bin_cache, 0, bin_sizes);
0385     }
0386 
0387     template <class RandomAccessIter, class Div_type>
0388     inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t)
0389       || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
0390     //defaulting to boost::sort::pdqsort when integer_sort won't work
0391     integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type)
0392     {
0393       boost::sort::pdqsort(first, last);
0394     }
0395 
0396 
0397     //Same for the full functor version
0398     template <class RandomAccessIter, class Div_type, class Right_shift,
0399               class Compare>
0400     //Only use spreadsort if the integer can fit in a size_t
0401     inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t),
0402                                  void >::type
0403     integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
0404                 Right_shift shift, Compare comp)
0405     {
0406       size_t bin_sizes[1 << max_finishing_splits];
0407       std::vector<RandomAccessIter> bin_cache;
0408       spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
0409           size_t, int_log_mean_bin_size, int_log_min_split_count, 
0410                         int_log_finishing_count>
0411           (first, last, bin_cache, 0, bin_sizes, shift, comp);
0412     }
0413 
0414     template <class RandomAccessIter, class Div_type, class Right_shift,
0415               class Compare>
0416     //Only use spreadsort if the integer can fit in a uintmax_t
0417     inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t))
0418       && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
0419     integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
0420                 Right_shift shift, Compare comp)
0421     {
0422       size_t bin_sizes[1 << max_finishing_splits];
0423       std::vector<RandomAccessIter> bin_cache;
0424       spreadsort_rec<RandomAccessIter, Div_type, Right_shift, Compare,
0425                         boost::uintmax_t, int_log_mean_bin_size,
0426                         int_log_min_split_count, int_log_finishing_count>
0427           (first, last, bin_cache, 0, bin_sizes, shift, comp);
0428     }
0429 
0430     template <class RandomAccessIter, class Div_type, class Right_shift,
0431               class Compare>
0432     inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t)
0433       || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
0434     //defaulting to boost::sort::pdqsort when integer_sort won't work
0435     integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
0436                 Right_shift shift, Compare comp)
0437     {
0438       boost::sort::pdqsort(first, last, comp);
0439     }
0440 
0441 
0442     //Same for the right shift version
0443     template <class RandomAccessIter, class Div_type, class Right_shift>
0444     //Only use spreadsort if the integer can fit in a size_t
0445     inline typename boost::enable_if_c< sizeof(Div_type) <= sizeof(size_t),
0446                                  void >::type
0447     integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
0448                 Right_shift shift)
0449     {
0450       size_t bin_sizes[1 << max_finishing_splits];
0451       std::vector<RandomAccessIter> bin_cache;
0452       spreadsort_rec<RandomAccessIter, Div_type, Right_shift, size_t,
0453           int_log_mean_bin_size, int_log_min_split_count, 
0454                         int_log_finishing_count>
0455           (first, last, bin_cache, 0, bin_sizes, shift);
0456     }
0457 
0458     template <class RandomAccessIter, class Div_type, class Right_shift>
0459     //Only use spreadsort if the integer can fit in a uintmax_t
0460     inline typename boost::enable_if_c< (sizeof(Div_type) > sizeof(size_t))
0461       && sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
0462     integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
0463                 Right_shift shift)
0464     {
0465       size_t bin_sizes[1 << max_finishing_splits];
0466       std::vector<RandomAccessIter> bin_cache;
0467       spreadsort_rec<RandomAccessIter, Div_type, Right_shift,
0468                         boost::uintmax_t, int_log_mean_bin_size,
0469                         int_log_min_split_count, int_log_finishing_count>
0470           (first, last, bin_cache, 0, bin_sizes, shift);
0471     }
0472 
0473     template <class RandomAccessIter, class Div_type, class Right_shift>
0474     inline typename boost::disable_if_c< sizeof(Div_type) <= sizeof(size_t)
0475       || sizeof(Div_type) <= sizeof(boost::uintmax_t), void >::type
0476     //defaulting to boost::sort::pdqsort when integer_sort won't work
0477     integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type,
0478                 Right_shift shift)
0479     {
0480       boost::sort::pdqsort(first, last);
0481     }
0482   }
0483 }
0484 }
0485 }
0486 
0487 #endif