/* Quick sort in C */ #ifndef MY_QUICKSORT_H #define MY_QUICKSORT_H #include /* Structure: * * - BASICS * - EXTRAS */ /* BASICS */ /** Swap operation */ static inline void swapit(uint32_t *a, uint32_t *b) { uint32_t t = *a; *a = *b; *b = t; } /** * Partition the array and find the pivot element such that * * - Elements smaller than pivot are on left of pivot * - Elements greater than pivot are on right of pivot * * @param array The array to partition * @param low From when. (inclusive) * @param high Until when. (inclusive too!) * @returns The partition point. */ static inline int partition(uint32_t array[], int low, int high) { /* This is "Lomuto"s unidirectional partitioner - see algorithms book */ /* select the rightmost element as pivot */ uint32_t pivot = array[high]; /* index until smaller or eq elements lay */ int i = (low - 1); /* traverse each element of the array */ /* compare them with the pivot */ #pragma GCC unroll 4 for (int j = low; j < high; ++j) { if (array[j] <= pivot) { /* if element smaller than pivot is found */ /* swap it with the greater element pointed by i */ ++i; /* swap element at i with element at j */ swapit(&array[i], &array[j]); } } /* swap the pivot element with the greater element at i */ swapit(&array[i + 1], &array[high]); /* return the partition point */ return (i + 1); } /** Simple in-place recursive quicksort on array for elements in [low, high) indices */ static inline void quicksort(uint32_t array[], int low, int high) { if (low < high) { int pi = partition(array, low, high); /* recursive call on the left of pivot */ quicksort(array, low, pi - 1); /* recursive call on the right of pivot */ quicksort(array, pi + 1, high); } } /* EXTRAS */ /** * Partition the array and using the pivot index * * - Elements smaller than pivot are on left of pivot * - Elements greater than pivot are on right of pivot * * @param array The array to partition * @param pi The index of the pivot element to use. 0 or high is what OG quicksorts do. * @param low From when. (inclusive) * @param high Until when. (inclusive too!) * @returns The partition point. */ static inline int partition_with_pivot(uint32_t array[], int pi, int low, int high) { /* * Rem.: This looks like overhead, * but after seriously considering * writing the whole out I can tell * this is still fastests basically. */ /* swap pivot with rightmost */ swapit(&array[high], &array[pi]); /* delegate to previous sol. */ return partition(array, low, high); } /** * Partition the array while doing a min-max search and find the pivot element such that * * - Elements smaller than pivot are on left of pivot * - Elements greater than pivot are on right of pivot * * @param array The array to partition * @param low From when. (inclusive) * @param high Until when. (inclusive too!) * @param minout OUT: Will be filled with the minimum key * @param maxout OUT: Will be filled with the maximum key * @returns The partition point. */ static inline int partition_and_minmax(uint32_t array[], int low, int high, uint32_t *minout, uint32_t *maxout) { /* This is "Lomuto"s unidirectional partitioner - see algorithms book */ /* select the rightmost element as pivot */ uint32_t pivot = array[high]; *minout = pivot; *maxout = pivot; /* index until smaller or eq elements lay */ int i = (low - 1); /* traverse each element of the array */ /* compare them with the pivot */ #pragma GCC unroll 4 for (int j = low; j < high; ++j) { /* Branchless min-max */ *minout = array[j] < *minout ? array[j] : *minout; *maxout = array[j] > *maxout ? array[j] : *maxout; /* Lomuto partitioning */ if (array[j] <= pivot) { /* if element smaller than pivot is found */ /* swap it with the greater element pointed by i */ ++i; /* swap element at i with element at j */ swapit(&array[i], &array[j]); } } /* swap the pivot element with the greater element at i */ swapit(&array[i + 1], &array[high]); /* return the partition point */ return (i + 1); } /** * Partition the array and min-max and using the pivot index * * - Elements smaller than pivot are on left of pivot * - Elements greater than pivot are on right of pivot * * @param array The array to partition * @param pi The index of the pivot element to use. 0 or high is what OG quicksorts do. * @param low From when. (inclusive) * @param high Until when. (inclusive too!) * @param minout OUT: Will be filled with the minimum key * @param maxout OUT: Will be filled with the maximum key * @returns The partition point. */ static inline int partition_and_minmax_with_pivot(uint32_t array[], int pi, int low, int high, uint32_t *minout, uint32_t *maxout) { /* * Rem.: This looks like overhead, * but after seriously considering * writing the whole out I can tell * this is still fastests basically. */ /* swap pivot with rightmost */ swapit(&array[high], &array[pi]); /* delegate to previous sol. */ return partition_and_minmax(array, low, high, minout, maxout); } // 32-bit LCG for fast random generations static inline uint32_t lcg(uint32_t *state) { *state = *state * 1664525u + 1013904223u; return *state; } /** Get pivot index in [0, len-1] without modulus - see our fastrand.h */ static inline uint32_t pick_pivot(uint32_t *state, uint32_t len) { uint32_t rand = lcg(state); /* Multiply by len, take the upper 32 bits of the 64-bit result */ return (uint32_t)(((uint64_t)rand * len) >> 32); } typedef uint32_t rpivotstate; /** Randomized pivoting in-place recursive quicksort on array for elements in [low, high] indices */ static inline void quicksort_rand(uint32_t array[], int low, int high, rpivotstate *state) { if (low < high) { int pi = pick_pivot(state, (high + 1) - low) + low; pi = partition_with_pivot(array, pi, low, high); /* recursive call on the left of pivot */ quicksort_rand(array, low, pi - 1, state); /* recursive call on the right of pivot */ quicksort_rand(array, pi + 1, high, state); } } #endif /* MY_QUICKSORT_H */