sort

sort algorithms for any type of array
git clone https://noulin.net/git/sort.git
Log | Files | Refs | README | LICENSE

flashsort.h (5310B)


      1 #pragma once
      2 
      3 /**
      4  * \file
      5  *
      6  * type safe flashsort implementation
      7  *
      8  * flashsort is in the classification sort category (no string compare) and sorts numbers (int, uint, float, ...).
      9  *
     10  * struct and pointer elements in the input array are supported
     11  *
     12  *
     13  * the performance depends on how uniform is the distribution,
     14  * the more uniform the better (high variance).
     15  *
     16  * adjust the m parameter (number of classes) to change the performance/memory usage ratio.
     17  *
     18  * when the variance is high, a m/size ratio of 0.1 gives good performance and low memory usage.
     19  *
     20  * an array of size m * sizeof(size_t) is allocated the heap and then freed
     21  *
     22  *
     23  * Example:
     24  *
     25  * // declare an array
     26  * typ struct {
     27  *   i32 key;
     28  *   i32 value;
     29  *   } elemt;
     30  *
     31  * elemt array[1000];
     32  *
     33  * // define flashsortGet
     34  * #define flashsortGet(ptr) (ptr)->key
     35  *
     36  * // call flashsort
     37  * flashsort(array, ARRAY_SIZE(array));
     38  *
     39  * // or call flashsortM to be able to set the number of classes
     40  * // flashsortM(array, ARRAY_SIZE(array), 0.43 * ARRAY_SIZE(array));
     41  *
     42  * #undef flashsortGet
     43  */
     44 
     45 
     46 /**
     47  * flashsort with predefined m/size ratio for high variance distributions
     48  *
     49  * the flashsortGet macro or function has to be defined before calling flashsortM
     50  * flashsortGet takes an address in parameter and returns a key
     51  *
     52  * \param
     53  * arr array to sort
     54  * \param
     55  * size number of elements in the array
     56  */
     57 #define flashsort(arr, size) flashsortM(arr, size, size / 16 /* m classes */)
     58 
     59 
     60 
     61 
     62 
     63 // ********
     64 // internal helper macros
     65 #define flashsortSubKMacro(num)\
     66   if (num > 0 and min < 0)\
     67     k = c * ((toUnsignedType(num))(num) + (toUnsignedType(num))(-min)) + 1;\
     68   else\
     69     k = c * ((num) - min) + 1
     70 // end internal helper macros
     71 
     72 
     73 // ********
     74 /**
     75  * flashsort with predefined m/size ratio for high variance distributions
     76  *
     77  * the flashsortGet macro or function has to be defined before calling flashsortM
     78  * flashsortGet takes an address in parameter and returns a key
     79  *
     80  * \param
     81  * arr array to sort, this parameter is evaluated multiple times,
     82  *     it cannot be a value returned by a function that changes during the flashsort execution
     83  * \param
     84  * size number of elements in the array
     85  * \param
     86  * m number of classes
     87  */
     88 #define flashsortM(arr, asize, mc) do {\
     89     size_t UNIQVAR(size) = asize;\
     90     size_t UNIQVAR(m)    = mc;\
     91     if (UNIQVAR(m) < 2) {\
     92       logE("number of class m is too low: %zu", UNIQVAR(m));\
     93       break;\
     94     }\
     95     /* Phase 1*/\
     96     var min = flashsortGet(&arr[0]);\
     97     var max = flashsortGet(&arr[0]);\
     98     /* added { to isolate variable definition in range */\
     99     {rangeFrom(i, 0, UNIQVAR(size)) {\
    100       min = MIN(flashsortGet(&arr[i]), min);\
    101       max = MAX(flashsortGet(&arr[i]), max);\
    102       /*logVarG(i);\
    103       logI("v %u %d", flashsortGet(&arr[i]), flashsortGet(&arr[i)]);\
    104       logI("min %u max %u", min, max);\
    105       logVarG(flashsortGet(&arr[i)]);\
    106       logVarG(arr[i]);\
    107       logVarG(flashsortGet(&arr[i]));*/\
    108     }}\
    109     \
    110     /*logVarG(min);\
    111     logVarG(max);*/\
    112     \
    113     if (min == max)\
    114       break;\
    115     \
    116     /* Divide to UNIQVAR(m) class */\
    117     /* c could be float instead of double, but the performance is better with double (intel skylake cpu) */\
    118     double c;\
    119     /* avoid overflow */\
    120     if (max > 0 and min < 0)\
    121       c = (double)(UNIQVAR(m) - 1) / ((toUnsignedType(max))max + (toUnsignedType(min))(-min));\
    122     else\
    123       c = (double)(UNIQVAR(m) - 1) / (max - min);\
    124     /*logVarG(UNIQVAR(m));\
    125     logVarG((double)c);*/\
    126     \
    127     size_t *L  = callocArray(L, UNIQVAR(m) + 1);\
    128     /* added { to isolate variable definition in range */\
    129     {range(i, UNIQVAR(size)) {\
    130       size_t k;\
    131       flashsortSubKMacro(flashsortGet(&arr[i]));\
    132       /* L[k]: the number of class K */\
    133       L[k]++;\
    134     }}\
    135     /* added { to isolate variable definition in range */\
    136     {rangeFrom(k, 2, UNIQVAR(m)+1) {\
    137       /* L[k]: upper bound of class K */\
    138       L[k] += L[k - 1];\
    139     }}\
    140     \
    141     /* Phase 2 */\
    142     /* number of move */\
    143     size_t move = 0;\
    144     size_t j    = 0;\
    145     size_t k    = UNIQVAR(m);\
    146     /* Only move UNIQVAR(size) - 1 step */\
    147     while (move < UNIQVAR(size) - 1)\
    148     {\
    149       /* flashsortGet(&arr[j]) is in right class */\
    150       while (j >= L[k])\
    151       {\
    152         j++;\
    153         flashsortSubKMacro(flashsortGet(&arr[j]));\
    154       }\
    155       /* temp to hold the value */\
    156       var flash   = flashsortGet(&arr[j]);\
    157       var flashEl = arr[j];\
    158       /* stop when flashsortGet(&arr[j]) is changed */\
    159       while (j < L[k])\
    160       {\
    161         flashsortSubKMacro(flash);\
    162         /* swap */\
    163         var tmpEl = flashEl;\
    164         flashEl   = arr[--L[k]];\
    165         arr[L[k]] = tmpEl;\
    166         flash     = flashsortGet(&flashEl);\
    167         move++;\
    168       }\
    169     }\
    170     \
    171     /* Phase 3
    172        Use Insertion sort for every class
    173        Don't sort the UNIQVAR(m) class,
    174        Because the UNIQVAR(m) class is all max */\
    175     rangeFrom(k, 1, UNIQVAR(m)) {\
    176       for (size_t i = L[k] + 1; i < L[k + 1]; i++) {\
    177         var key   = flashsortGet(&arr[i]);\
    178         var keyEl = arr[i];\
    179         i64 j   = i - 1;\
    180         while (j >= 0 and flashsortGet(&arr[j]) > key)\
    181         {\
    182           arr[j+1] = arr[j];\
    183           j--;\
    184         }\
    185         arr[j+1] = keyEl;\
    186       }\
    187     }\
    188     free(L);\
    189   }while(0)
    190 
    191 // vim: set expandtab ts=2 sw=2: