diff --git a/zssort.h b/zssort.h index d0de2d2..a0eba14 100644 --- a/zssort.h +++ b/zssort.h @@ -1,5 +1,11 @@ #ifndef ZS_SORT_H #define ZS_SORT_H + +/** Used in neoqs for applying smallrange heuristics below value-domains of this size */ +#ifndef NEOQS_SMALLRANGE_TRESHOLD +#define NEOQS_SMALLRANGE_TRESHOLD 256 +#endif + /* * Structure: * - ZSSORT: The at most log(n) space needing quicksort @@ -268,6 +274,123 @@ static inline void meanqs(uint32_t array[], int low, int high, rpivotstate *stat } } +/* NEOQS */ + +/** + * Neoquicksort entry point and outer-recursive function. + * + * We pick a random pivot, but in the meantime try to quess mean for next level partitioning. + * This and neoqs_inner is co-recursive (call each other) + * We utilize zsr3_sp2 in case the range is smaller than NEOQS_SMALLRANGE_TRESHOLD. + * + * @param array The array to sort + * @param low From when. (inclusive) + * @param high Until when. (inclusive too!) + * @param state The random pivot picking state + */ +static inline void neoqs(uint32_t array[], int low, int high, rpivotstate *state); + +/** + * Inner-recursive function of neoqs - calls neoqs() as next level recursion. + * + * @param array The array to sort + * @param low From when. (inclusive) + * @param high Until when. (inclusive too!) + * @param pivotval We first partition to smaller-eq/bigger than pivotval, later we might pick random though + * @param state The random pivot picking state + */ +static inline void neoqs_inter(uint32_t array[], int low, int high, uint32_t pivotval, rpivotstate *state) { + + /* First: partition using pivot val + recurse */ + + if (low < high) { + /* For the first level, use the pivotval they provide */ + /* This only helps somewhat, because below loop.... */ + /* ..But code it better if you can do consistently! */ + int pi = partition_with_pivotval(array, low, high, pivotval); + + /* If we recurse only the smaller part */ + /* That ensures at most n/2 elements can */ + /* be on any given level of the recursion */ + /* tree: that is we ensure log2(N) memuse! */ + if((pi - low) < (high - pi)) { + // Left smaller: recurse left of pivot + neoqs(array, low, pi - 1, state); + // (*) Update partitioning loop for remaining part + low = pi + 1; + } else { + // Right smaller: recurse right of pivot + neoqs(array, pi + 1, high, state); + // (*) Update partitioning loop for remaining part + high = pi - 1; /* high inclusive! */ + } + } + + /* Later: picking random pivot and partition like that */ + + /* (*) Rem.: The above if can be understood as different first iteration of this while loop... */ + while (low < high) { + /* Random picking here - no info anymore about mean */ + int pi = pick_pivot(state, (high + 1) - low) + low; + pi = partition_with_pivot(array, pi, low, high); + + /* If we recurse only the smaller part */ + /* That ensures at most n/2 elements can */ + /* be on any given level of the recursion */ + /* tree: that is we ensure log2(N) memuse! */ + if((pi - low) < (high - pi)) { + // Left smaller: recurse left of pivot + neoqs(array, low, pi - 1, state); + // (*) Update partitioning loop for remaining part + low = pi + 1; + } else { + // Right smaller: recurse right of pivot + neoqs(array, pi + 1, high, state); + // (*) Update partitioning loop for remaining part + high = pi - 1; /* high inclusive! */ + } + } +} + +/** + * Neoquicksort entry point and outer-recursive function. + * + * We pick a random pivot, but in the meantime try to guess mean for next level partitioning. + * This and neoqs_inner is co-recursive (call each other) + * We utilize zsr3_sp2 in case the range is smaller than NEOQS_SMALLRANGE_TRESHOLD. + * + * @param array The array to sort + * @param low From when. (inclusive) + * @param high Until when. (inclusive too!) + * @param state The random pivot picking state + */ +static inline void neoqs(uint32_t array[], int low, int high, rpivotstate *state) { + if (low < high) { + uint32_t lmin, lmax, rmin, rmax; + int pi = pick_pivot(state, (high + 1) - low) + low; + pi = partition_and_pminmax_with_pivot(array, pi, low, high, &lmin, &lmax, &rmin, &rmax); + + /* These also handle constant (always same valued) subarrays */ + if(lmin != lmax) { + /* Better than (a+b)/2 avg because of overflow possibilities */ + uint32_t lavg = lmin + (lmax - lmin) / 2; + /* Changing to zzsort_rand3_sp2 ensures better smallranges */ + /* Recursion with inter "ensures" logn stack spacen need! */ + if(lmax - lmin < NEOQS_SMALLRANGE_TRESHOLD) zssort_rand3_sp2(array, low, pi - 1, state); + else neoqs_inter(array, low, pi - 1, lavg, state); + } + if(rmin != rmax) { + /* Better than (a+b)/2 avg because of overflow possibilities */ + uint32_t ravg = rmin + (rmax - rmin) / 2; + /* Changing to zzsort_rand3_sp2 ensures better smallranges */ + /* Recursion with inter "ensures" logn stack spacen need! */ + if(rmax - rmin < NEOQS_SMALLRANGE_TRESHOLD) zssort_rand3_sp2(array, pi + 1, high, state); + else neoqs_inter(array, pi + 1, high, ravg, state); + } + } +} + + // TODO: Idea: Quadratic time happens when you repeatedly pick pivots close to the maximum or minimum - so why not re-pick near extremals being picked which now can be cheked? I think we should have a treshold - maybe dynamically set by first minmax - and logarithmically divide the treshold by 2 in case its still too close the next randomization time. This handles smallrange badly though. #endif /* ZS_SORT_H */