LCOV - code coverage report
Current view: top level - src/foreign/RoutingKit/include/routingkit - sort.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 66.0 % 47 31
Test Date: 2026-09-23 15:43:02 Functions: 30.8 % 39 12

            Line data    Source code
       1              : #ifndef ROUTING_KIT_SORT_H
       2              : #define ROUTING_KIT_SORT_H
       3              : 
       4              : #include <routingkit/permutation.h>
       5              : 
       6              : #include <vector>
       7              : #include <assert.h>
       8              : #include <algorithm>
       9              : #include <functional>
      10              : 
      11              : namespace RoutingKit{
      12              : 
      13              : //
      14              : // A sort permutation p of a vector v is a permutation such that
      15              : // v[p[0]] <= v[p[1]] <= v[p[2]] <= ... . Applying a sort permutation p of v to
      16              : // v yields a sorted vector.
      17              : //
      18              : // The functions in this header follow the following naming sheme:
      19              : //
      20              : //   1) compute_[inverse_][stable_]sort_using_[(key|less|comparator](vector, ...order...)
      21              : //   2) [stable_]sort_using_[key|less|comparator](vector, ...order...)
      22              : //   3) is_sorted_using_[key|less|comparator](vector, ...order...)
      23              : //
      24              : //
      25              : // The functionality is explained as following:
      26              : //
      27              : //   * The functions of category 1 compute sort permutations but do not actually
      28              : //     sort any array. The sorting is done by applying the resulting permutation
      29              : //     to one or more vectors.
      30              : //   * The functions of category 2 copy their argument vector, sort the copy and
      31              : //     return it. If an rvalue vector is given as parameter then the copy is
      32              : //     omitted.
      33              : //   * The functions of category 3 sort their argument array inplace.
      34              : //   * The functions of category 4 do not sort anything but check whether their
      35              : //     argument array is sorted.
      36              : //   * "inverse" indicates that not a sort permutation but its inverse is
      37              : //     computed. In combination with the _using_key function it is slightly
      38              : //     faster to compute an inverse sort permutation and then to use
      39              : //     apply_inverse_permutation then to compute a sort permutation and to use
      40              : //     apply_permutation.
      41              : //   * "stable" indicates that the relative order of values that compare equally
      42              : //     is not changed.
      43              : //   * "key" indicates that elements are sorted using bucket sort. All functions
      44              : //     take a (key_count, get_key) pair of parameters to specify the order.
      45              : //     get_key is a function  object that must map every element onto a value in
      46              : //     [0,key_count).  get_key is assumed to have a quick execution and is
      47              : //     called several times per sort operation. However, get_key is never
      48              : //     copied.
      49              : //   * "comparator" indicates that an STL like comparator is used and needs to
      50              : //     be passed to every function.
      51              : //   * "less" indicates that < is used for comparision. No order paramters need
      52              : //     to be passed to the functions.
      53              : //
      54              : 
      55              : template<class T, class C>
      56            0 : std::vector<unsigned> compute_stable_sort_permutation_using_comparator(const std::vector<T>&v, const C&is_less){
      57            0 :         std::vector<unsigned>p = identity_permutation(v.size());
      58            0 :         std::stable_sort(
      59              :                 p.begin(), p.end(),
      60              :                 [&](unsigned l, unsigned r){
      61            0 :                         return is_less(v[l], v[r]);
      62              :                 }
      63              :         );
      64            0 :         return p; // NVRO
      65              : }
      66              : 
      67              : template<class T, class C>
      68              : std::vector<T>stable_sort_using_comparator(const std::vector<T>&v, const C&is_less){
      69              :         std::vector<T>r = v;
      70              :         std::stable_sort(r.begin(), r.end(), is_less);
      71              :         return r; // NVRO
      72              : }
      73              : 
      74              : template<class T, class C>
      75              : std::vector<T>stable_sort_using_comparator(std::vector<T>&&v, const C&is_less){
      76              :         std::stable_sort(v.begin(), v.end(), is_less);
      77              :         return std::move(v); // NVRO
      78              : }
      79              : 
      80              : template<class T, class C>
      81              : std::vector<unsigned> compute_sort_permutation_using_comparator(const std::vector<T>&v, const C&is_less){
      82              :         std::vector<unsigned>p = identity_permutation(v.size());
      83              :         std::sort(
      84              :                 p.begin(), p.end(),
      85              :                 [&](unsigned l, unsigned r){
      86              :                         return is_less(v[l], v[r]);
      87              :                 }
      88              :         );
      89              :         return p; // NVRO
      90              : }
      91              : 
      92              : template<class T, class C>
      93              : std::vector<T>sort_using_comparator(const std::vector<T>&v, const C&is_less){
      94              :         std::vector<T>r = v;
      95              :         std::sort(r.begin(), r.end(), is_less);
      96              :         return r; // NVRO
      97              : }
      98              : 
      99              : template<class T, class C>
     100              : std::vector<T>sort_using_comparator(std::vector<T>&&v, const C&is_less){
     101              :         std::sort(v.begin(), v.end(), is_less);
     102              :         return std::move(v); // NVRO
     103              : }
     104              : 
     105              : 
     106              : template<class T, class C>
     107              : bool is_sorted_using_comparator(const std::vector<T>&v, const C&is_less){
     108            0 :         for(unsigned i=1; i<v.size(); ++i)
     109            0 :                 if(is_less(v[i], v[i-1]))
     110              :                         return false;
     111              :         return true;
     112              : }
     113              : 
     114              : 
     115              : template<class T, class C>
     116              : std::vector<unsigned> compute_inverse_sort_permutation_using_comparator(const std::vector<T>&v, const C&is_less){
     117              :         return invert_permutation(compute_sort_permutation_using_comparator(v, is_less));
     118              : }
     119              : 
     120              : template<class T, class C>
     121            0 : std::vector<unsigned> compute_inverse_stable_sort_permutation_using_comparator(const std::vector<T>&v, const C&is_less){
     122            0 :         return invert_permutation(compute_stable_sort_permutation_using_comparator(v, is_less));
     123              : }
     124              : 
     125              : 
     126              : 
     127              : namespace detail{
     128              :         const unsigned bucket_sort_min_key_to_element_ratio = 16;
     129              : 
     130              :         template<class T, class K>
     131        22888 :         std::vector<unsigned>compute_key_pos(const std::vector<T>&v, unsigned key_count, const K&get_key){
     132        22888 :                 std::vector<unsigned>key_pos(key_count, 0);
     133      1266536 :                 for(unsigned i=0; i<v.size(); ++i){
     134      1243648 :                         unsigned k = get_key(v[i]);
     135              :                         assert(k <= key_count && "key is too large");
     136      1243648 :                         ++key_pos[k];
     137              :                 }
     138              :                 unsigned sum = 0;
     139       324410 :                 for(unsigned i=0; i<key_count; ++i){
     140       301522 :                         unsigned tmp = sum + key_pos[i];
     141       301522 :                         key_pos[i] = sum;
     142              :                         sum = tmp;
     143              :                 }
     144        22888 :                 return key_pos; // NVRO
     145              :         }
     146              : 
     147              :         template<class K>
     148              :         struct CompareByKey{
     149              :                 explicit CompareByKey(unsigned n, const K&k):get_key(k){
     150              :                         #ifndef NDEBUG
     151              :                         key_count = n;
     152              :                         #else
     153              :                         (void)n;
     154              :                         #endif
     155              :                 }
     156              : 
     157              :                 template<class T>
     158              :                 bool operator()(const T&l, const T&r)const{
     159            0 :                         unsigned l_key = get_key(l);
     160              :                         assert(l_key < key_count);
     161            0 :                         unsigned r_key = get_key(r);
     162              :                         assert(r_key < key_count);
     163              :                         return l_key < r_key;
     164              :                 }
     165              : 
     166              :                 #ifndef NDEBUG
     167              :                 unsigned key_count;
     168              :                 #endif
     169              :                 const K&get_key;
     170              :         };
     171              : 
     172              :         template<class K>
     173              :         CompareByKey<K> make_compare_by_key(unsigned n, const K&k){
     174              :                 return CompareByKey<K>(n, k);
     175              :         }
     176              : }
     177              : 
     178              : namespace detail{
     179              :         // Sorting by key uses bucket sort if the number of elements is
     180              :         // not significantly smaller than the number of keys. Otherwise
     181              :         // we use the sort by comparator function, which (at the time
     182              :         // of writing this comment) forward to std::sort and
     183              :         // std::stable_sort. Bucket sort is always stable whereas
     184              :         // comparator-based sort is not. To avoid code dublication, we
     185              :         // therefore have maybe_stable functions that take a compile time
     186              :         // boolean which inidcates whether the fallback function should
     187              :         // be stable.
     188              : 
     189              :         template<bool is_stable, class T, class K>
     190         3396 :         std::vector<unsigned> compute_maybe_stable_sort_permutation_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     191              :                 std::vector<unsigned>p;
     192              : 
     193         3396 :                 if(v.size() >= key_count / bucket_sort_min_key_to_element_ratio){
     194         3396 :                         p.resize(v.size());
     195         3396 :                         std::vector<unsigned>key_pos = detail::compute_key_pos(v, key_count, get_key);
     196       304296 :                         for(unsigned i=0; i<v.size(); ++i){
     197       300900 :                                 unsigned k = get_key(v[i]);
     198              :                                 assert(k <= key_count && "key is too large");
     199       300900 :                                 p[key_pos[k]] = i;
     200       300900 :                                 ++key_pos[k];
     201              :                         }
     202         3396 :                 }else if(is_stable){
     203            0 :                         p = compute_stable_sort_permutation_using_comparator(v, detail::make_compare_by_key(key_count, get_key));
     204              :                 }else{
     205              :                         p = compute_sort_permutation_using_comparator(v, detail::make_compare_by_key(key_count, get_key));
     206              :                 }
     207         3396 :                 return p; // NVRO
     208            0 :         }
     209              : 
     210              : }
     211              : 
     212              : template<class T, class K>
     213              : std::vector<unsigned> compute_sort_permutation_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     214              :         return detail::compute_maybe_stable_sort_permutation_using_key<false>(v, key_count, get_key);
     215              : }
     216              : 
     217              : template<class T, class K>
     218              : std::vector<unsigned> compute_stable_sort_permutation_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     219         3396 :         return detail::compute_maybe_stable_sort_permutation_using_key<true>(v, key_count, get_key);
     220              : }
     221              : 
     222              : namespace detail{
     223              :         template<bool is_stable, class T, class K>
     224        19492 :         std::vector<unsigned> compute_inverse_maybe_stable_sort_permutation_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     225              :                 std::vector<unsigned>p;
     226        19492 :                 if(v.size() >= key_count / bucket_sort_min_key_to_element_ratio){
     227        19492 :                         p.resize(v.size());
     228        19492 :                         std::vector<unsigned>key_pos = detail::compute_key_pos(v, key_count, get_key);
     229       962240 :                         for(unsigned i=0; i<v.size(); ++i){
     230       942748 :                                 unsigned k = get_key(v[i]);
     231              :                                 assert(k <= key_count && "key is too large");
     232       942748 :                                 p[i] = key_pos[k];
     233       942748 :                                 ++key_pos[k];
     234              :                         }
     235        19492 :                 } else if(is_stable){
     236            0 :                         p = compute_inverse_stable_sort_permutation_using_comparator(v, detail::make_compare_by_key(key_count, get_key));
     237              :                 }else{
     238              :                         p = compute_inverse_sort_permutation_using_comparator(v, detail::make_compare_by_key(key_count, get_key));
     239              :                 }
     240        19492 :                 return p; // NVRO
     241            0 :         }
     242              : }
     243              : 
     244              : template<class T, class K>
     245              : std::vector<unsigned> compute_inverse_sort_permutation_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     246              :         return detail::compute_inverse_maybe_stable_sort_permutation_using_key<false>(v, key_count, get_key);
     247              : }
     248              : 
     249              : template<class T, class K>
     250              : std::vector<unsigned> compute_inverse_stable_sort_permutation_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     251        19492 :         return detail::compute_inverse_maybe_stable_sort_permutation_using_key<true>(v, key_count, get_key);
     252              : }
     253              : 
     254              : namespace detail{
     255              :         template<bool is_stable, class T, class K>
     256              :         std::vector<T>maybe_stable_sort_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     257              :                 std::vector<T>r;
     258              : 
     259              :                 if(v.size() >= key_count / bucket_sort_min_key_to_element_ratio){
     260              :                         std::vector<unsigned>key_pos = detail::compute_key_pos(v, key_count, get_key);
     261              :                         r.resize(v.size());
     262              : 
     263              :                         for(unsigned i=0; i<v.size(); ++i){
     264              :                                 unsigned k = get_key(v[i]);
     265              :                                 assert(k <= key_count && "key is too large");
     266              :                                 r[key_pos[k]] = v[i];
     267              :                                 ++key_pos[k];
     268              :                         }
     269              :                 } else if(is_stable) {
     270              :                         r = stable_sort_using_comparator(v, detail::make_compare_by_key(key_count, get_key));
     271              :                 } else {
     272              :                         r = sort_using_comparator(v, detail::make_compare_by_key(key_count, get_key));
     273              :                 }
     274              :                 return r; // NVRO
     275              :         }
     276              : 
     277              :         template<bool is_stable, class T, class K>
     278              :         std::vector<T>maybe_stable_sort_using_key(std::vector<T>&&v, unsigned key_count, const K&get_key){
     279              :                 std::vector<T>r;
     280              :                 if(v.size() >= key_count / bucket_sort_min_key_to_element_ratio){
     281              :                         std::vector<unsigned>key_pos = detail::compute_key_pos(v, key_count, get_key);
     282              :                         r.resize(v.size());
     283              : 
     284              :                         for(unsigned i=0; i<v.size(); ++i){
     285              :                                 unsigned k = get_key(v[i]);
     286              :                                 assert(k <= key_count && "key is too large");
     287              :                                 r[key_pos[k]] = std::move(v[i]);
     288              :                                 ++key_pos[k];
     289              :                         }
     290              :                 } else if(is_stable) {
     291              :                         r = stable_sort_using_comparator(v, detail::make_compare_by_key(key_count, get_key));
     292              :                 } else {
     293              :                         r = sort_using_comparator(v, detail::make_compare_by_key(key_count, get_key));
     294              :                 }
     295              :                 return r; // NVRO
     296              :         }
     297              : }
     298              : 
     299              : template<class T, class K>
     300              : std::vector<T>sort_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     301              :         return detail::maybe_stable_sort_using_key<false>(v, key_count, get_key); // NVRO
     302              : }
     303              : 
     304              : template<class T, class K>
     305              : std::vector<T>sort_using_key(std::vector<T>&&v, unsigned key_count, const K&get_key){
     306              :         return detail::maybe_stable_sort_using_key<false>(std::move(v), key_count, get_key); // NVRO
     307              : }
     308              : 
     309              : template<class T, class K>
     310              : std::vector<T>stable_sort_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     311              :         return detail::maybe_stable_sort_using_key<true>(v, key_count, get_key); // NVRO
     312              : }
     313              : 
     314              : template<class T, class K>
     315              : std::vector<T>stable_sort_using_key(std::vector<T>&&v, unsigned key_count, const K&get_key){
     316              :         return detail::maybe_stable_sort_using_key<true>(std::move(v), key_count, get_key); // NVRO
     317              : }
     318              : 
     319              : 
     320              : template<class T, class K>
     321              : bool is_sorted_using_key(const std::vector<T>&v, unsigned key_count, const K&get_key){
     322              :         for(unsigned i=1; i<v.size(); ++i)
     323              :                 if(get_key(v[i]) < get_key(v[i-1]))
     324              :                         return false;
     325              :         return true;
     326              : }
     327              : 
     328              : 
     329              : 
     330              : 
     331              : 
     332              : 
     333              : template<class T>
     334              : std::vector<unsigned> compute_sort_permutation_using_less(const std::vector<T>&v){
     335              :         return compute_sort_permutation_using_comparator(v, [](const T&l, const T&r){return l < r;});
     336              : }
     337              : 
     338              : template<class T>
     339              : std::vector<unsigned> compute_stable_sort_permutation_using_less(const std::vector<T>&v){
     340              :         return compute_stable_sort_permutation_using_comparator(v, [](const T&l, const T&r){return l < r;});
     341              : }
     342              : 
     343              : template<class T>
     344              : std::vector<unsigned> compute_inverse_sort_permutation_using_less(const std::vector<T>&v){
     345              :         return invert_permutation(compute_sort_permutation_using_less(v));
     346              : }
     347              : 
     348              : template<class T>
     349              : std::vector<unsigned> compute_inverse_stable_sort_permutation_using_less(const std::vector<T>&v){
     350              :         return invert_permutation(compute_stable_sort_permutation_using_less(v));
     351              : }
     352              : 
     353              : template<class T>
     354              : std::vector<T>stable_sort_using_less(const std::vector<T>&v){
     355              :         return stable_sort_using_comparator(v, [](const T&l, const T&r){return l < r;});
     356              : }
     357              : 
     358              : template<class T>
     359              : std::vector<T>stable_sort_using_less(std::vector<T>&&v){
     360              :         return stable_sort_using_comparator(std::move(v), [](const T&l, const T&r){return l < r;});
     361              : }
     362              : 
     363              : template<class T>
     364              : std::vector<T>sort_using_less(const std::vector<T>&v){
     365              :         return sort_using_comparator(v, [](const T&l, const T&r){return l < r;});
     366              : }
     367              : 
     368              : template<class T>
     369              : std::vector<T>sort_using_less(std::vector<T>&&v){
     370              :         return sort_using_comparator(std::move(v), [](const T&l, const T&r){return l < r;});
     371              : }
     372              : 
     373              : template<class T>
     374              : bool is_sorted_using_less(const std::vector<T>&v){
     375            0 :         return is_sorted_using_comparator(v, [](const T&l, const T&r){return l < r;});
     376              : }
     377              : 
     378              : } // RoutingKit
     379              : 
     380              : #endif
        

Generated by: LCOV version 2.0-1