Compare commits

...

10 Commits

5 changed files with 595 additions and 13 deletions

17
chatgpt_qs.cpp Normal file
View File

@ -0,0 +1,17 @@
#include <iostream>
#include <vector>
#include <cstdint>
#include "chatgpt_qs.h"
// Example usage
int main() {
std::vector<uint32_t> arr = {3, 6, 8, 10, 1, 2, 1};
std::vector<uint32_t> sorted = gpt_quicksort(arr);
for (uint32_t num : sorted) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}

37
chatgpt_qs.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef CHATGPT_QS_H
#define CHATGPT_QS_H
std::vector<uint32_t> gpt_quicksort(const std::vector<uint32_t> &arr) {
if (arr.size() <= 1) {
return arr;
}
uint32_t pivot = arr[arr.size() / 2];
std::vector<uint32_t> left;
std::vector<uint32_t> middle;
std::vector<uint32_t> right;
for (uint32_t x : arr) {
if (x < pivot) {
left.push_back(x);
} else if (x == pivot) {
middle.push_back(x);
} else {
right.push_back(x);
}
}
std::vector<uint32_t> sorted_left = gpt_quicksort(left);
std::vector<uint32_t> sorted_right = gpt_quicksort(right);
// Concatenate sorted_left + middle + sorted_right
std::vector<uint32_t> result;
result.reserve(sorted_left.size() + middle.size() + sorted_right.size());
result.insert(result.end(), sorted_left.begin(), sorted_left.end());
result.insert(result.end(), middle.begin(), middle.end());
result.insert(result.end(), sorted_right.begin(), sorted_right.end());
return result;
}
#endif // CHATGPT_QS_H

19
qs.c
View File

@ -43,7 +43,7 @@ void qsr() {
printArray(data, n); printArray(data, n);
} }
// qs test function // zss test function
void zss() { void zss() {
#include "data.inc" #include "data.inc"
@ -56,9 +56,26 @@ void zss() {
printArray(data, n); printArray(data, n);
} }
// qs3 test function
void qs3() {
#include "data.inc"
int n = sizeof(data) / sizeof(data[0]);
// memory junnnk is enough
rpivotstate rand;
// perform qs3 on data
quicksort_rand3(data, 0, n - 1, &rand);
printf("(qs3) Sorted array in ascending order: \n");
printArray(data, n);
}
int main() { int main() {
qs(); qs();
qsr(); qsr();
zss(); zss();
qs3();
return 0; return 0;
} }

368
qsort.h
View File

@ -4,10 +4,16 @@
#include <stdint.h> #include <stdint.h>
/* Uncomment for debugging with if(someevent) raise(SIGINT); */
#include <signal.h>
/* Structure: /* Structure:
* *
* - BASICS * - BASICS: Basic quicksort
* - EXTRAS * - RANDOMPIV: Randomized pivoting and partitioning by given pivot-indexed element.
* - MINMAX: partitioning while doing min-max over the whole + related extras.
* - PMINMAX: Partitioning with min-max searches in generated partitions + related.
* - VALPART: Partitioning by value - not by random or given pivot index!
*/ */
/* BASICS */ /* BASICS */
@ -28,13 +34,14 @@ static inline void swapit(uint32_t *a, uint32_t *b) {
* @param array The array to partition * @param array The array to partition
* @param low From when. (inclusive) * @param low From when. (inclusive)
* @param high Until when. (inclusive too!) * @param high Until when. (inclusive too!)
* @returns The partition point.
*/ */
static inline int partition(uint32_t array[], int low, int high) { static inline int partition(uint32_t array[], int low, int high) {
/* This is "Lomuto"s unidirectional partitioner - see algorithms book */ /* This is "Lomuto"s unidirectional partitioner - see algorithms book */
/* select the rightmost element as pivot */ /* select the rightmost element as pivot */
uint32_t pivot = array[high]; uint32_t pivot = array[high];
/* index until smaller or eq elements lay */ /* index until smaller or eq elements lay */
int i = (low - 1); int i = (low - 1);
@ -43,11 +50,10 @@ static inline int partition(uint32_t array[], int low, int high) {
#pragma GCC unroll 4 #pragma GCC unroll 4
for (int j = low; j < high; ++j) { for (int j = low; j < high; ++j) {
if (array[j] <= pivot) { if (array[j] <= pivot) {
/* if element smaller than pivot is found */ /* if element smaller than pivot is found */
/* swap it with the greater element pointed by i */ /* swap it with the greater element pointed by i */
++i; ++i;
/* swap element at i with element at j */ /* swap element at i with element at j */
swapit(&array[i], &array[j]); swapit(&array[i], &array[j]);
} }
@ -55,7 +61,7 @@ static inline int partition(uint32_t array[], int low, int high) {
/* swap the pivot element with the greater element at i */ /* swap the pivot element with the greater element at i */
swapit(&array[i + 1], &array[high]); swapit(&array[i + 1], &array[high]);
/* return the partition point */ /* return the partition point */
return (i + 1); return (i + 1);
} }
@ -64,16 +70,88 @@ static inline int partition(uint32_t array[], int low, int high) {
static inline void quicksort(uint32_t array[], int low, int high) { static inline void quicksort(uint32_t array[], int low, int high) {
if (low < high) { if (low < high) {
int pi = partition(array, low, high); int pi = partition(array, low, high);
/* recursive call on the left of pivot */ /* recursive call on the left of pivot */
quicksort(array, low, pi - 1); quicksort(array, low, pi - 1);
/* recursive call on the right of pivot */ /* recursive call on the right of pivot */
quicksort(array, pi + 1, high); quicksort(array, pi + 1, high);
} }
} }
/* EXTRAS */ /* THREEWAY */
struct pret3 {
int leftend;
int rightend;
};
typedef struct pret3 pret3;
/**
* Partition the array threeway by puutting ALL pivots to the middle and smaller/biggers left-right
*
* @param array The array to partition
* @param low From when. (inclusive)
* @param high Until when. (inclusive too!)
* @param pivotval This value is used to partition the array.
* @returns The partition points, end of left and right (inclusive)
*/
static inline pret3 partition3(uint32_t array[], int low, int high, uint32_t pivotval) {
/* select the rightmost element as pivot */
uint32_t pivot = pivotval;
/* index until smaller or eq elements lay */
int i = (low - 1);
uint32_t pc = 0;
/* traverse each element of the array */
/* compare them with the pivot */
#pragma GCC unroll 4
for (int j = low; j <= high; ++j) {
/* Branchless pivot-count */
pc += (array[j] == pivot);
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]);
}
}
/* Can spare out the second loop in these cases */
if(pc < 2) {
pret3 ret;
ret.leftend = i;
ret.rightend = i + 1;
return ret;
}
/* index until smaller or eq elements lay */
int i2 = (high + 1);
#pragma GCC unroll 4
for (int j = high; j > i; --j) {
if (array[j] > pivot) {
/* if element smaller than pivot is found */
/* swap it with the greater element pointed by i */
--i2;
/* swap element at i with element at j */
swapit(&array[i2], &array[j]);
}
}
/* return the partition points */
pret3 ret;
ret.leftend = i;
ret.rightend = i2;
return ret;
}
/* RANDOMPIV */
/** /**
* Partition the array and using the pivot index * Partition the array and using the pivot index
@ -85,6 +163,7 @@ static inline void quicksort(uint32_t array[], int low, int high) {
* @param pi The index of the pivot element to use. 0 or high is what OG quicksorts do. * @param pi The index of the pivot element to use. 0 or high is what OG quicksorts do.
* @param low From when. (inclusive) * @param low From when. (inclusive)
* @param high Until when. (inclusive too!) * @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) { static inline int partition_with_pivot(uint32_t array[], int pi, int low, int high) {
/* /*
@ -93,7 +172,7 @@ static inline int partition_with_pivot(uint32_t array[], int pi, int low, int hi
* writing the whole out I can tell * writing the whole out I can tell
* this is still fastests basically. * this is still fastests basically.
*/ */
/* swap pivot with rightmost */ /* swap pivot with rightmost */
swapit(&array[high], &array[pi]); swapit(&array[high], &array[pi]);
/* delegate to previous sol. */ /* delegate to previous sol. */
@ -120,13 +199,278 @@ static inline void quicksort_rand(uint32_t array[], int low, int high, rpivotsta
if (low < high) { if (low < high) {
int pi = pick_pivot(state, (high + 1) - low) + low; int pi = pick_pivot(state, (high + 1) - low) + low;
pi = partition_with_pivot(array, pi, low, high); pi = partition_with_pivot(array, pi, low, high);
/* recursive call on the left of pivot */ /* recursive call on the left of pivot */
quicksort_rand(array, low, pi - 1, state); quicksort_rand(array, low, pi - 1, state);
/* recursive call on the right of pivot */ /* recursive call on the right of pivot */
quicksort_rand(array, pi + 1, high, state); quicksort_rand(array, pi + 1, high, state);
} }
} }
/* THREEWAY_RAND */
/** Randomized pivoting in-place recursive quicksort on array for elements in [low, high] indices */
static inline void quicksort_rand3(uint32_t array[], int low, int high, rpivotstate *state) {
if (low < high) {
/* partition threeway by random pivot */
int pi = pick_pivot(state, (high + 1) - low) + low;
pret3 res = partition3(array, low, high, array[pi]);
/* recursive call on the left of pivot */
quicksort_rand3(array, low, res.leftend, state);
/* recursive call on the right of pivot */
quicksort_rand3(array, res.rightend, high, state);
}
}
/* MINMAX */
/**
* 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);
}
/* PMINMAX */
/**
* Partition the array with partition-based min-max search (4 values: 2 per partition) 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
*
* (***): The left min-max outputs can return not just the left partition, but left partition ++ pivotpoint region min-max!
* This does not only happen if partition is empty, but also when pivot was already highest and last earlier.
* Would be hard to handle this edge-case and min-max out is generally used as output hints only so I prefer speed..
*
* @param array The array to partition
* @param low From when. (inclusive)
* @param high Until when. (inclusive too!)
* @param minout_left OUT: Will be filled with the minimum key of left partition or pivot value when partition empty. Also (***)
* @param maxout_left OUT: Will be filled with the maximum key of left partition or pivot value when partition empty. Also (***)
* @param minout_right OUT: Will be filled with the minimum key of right partition or pivot value when partition empty
* @param maxout_right OUT: Will be filled with the maximum key of right partition or pivot value when partition empty
* @returns The partition point.
*/
static inline int partition_and_pminmax(
uint32_t array[],
int low,
int high,
uint32_t *minout_left,
uint32_t *maxout_left,
uint32_t *minout_right,
uint32_t *maxout_right) {
/* This is "Lomuto"s unidirectional partitioner - see algorithms book */
/* select the rightmost element as pivot */
uint32_t pivot = array[high];
*minout_left = pivot;
*maxout_left = pivot;
*minout_right = pivot;
*maxout_right = 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) {
/* Lomuto partitioning */
if (array[j] <= pivot) {
/* Branchless min-max */
*minout_left = array[j] < *minout_left ? array[j] : *minout_left;
*maxout_left = array[j] > *maxout_left ? array[j] : *maxout_left;
/* 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]);
} else {
/* Branchless min-max */
*minout_right = array[j] < *minout_right ? array[j] : *minout_right;
*maxout_right = array[j] > *maxout_right ? array[j] : *maxout_right;
}
}
/* 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 with partition-based min-max search (4 values: 2 per partition) 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_pminmax_with_pivot(
uint32_t array[],
int pi,
int low,
int high,
uint32_t *minout_left,
uint32_t *maxout_left,
uint32_t *minout_right,
uint32_t *maxout_right) {
/*
* 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_pminmax(array, low, high, minout_left, maxout_left, minout_right, maxout_right);
}
/* VALPART */
/**
* Partition the array using pivot value - and find pivot closest to that value (and place them at proper pivot index)
*
* - Elements smaller-eq than pivotval are on left of pivot
* - Elements greater than pivotval are on right of pivot
* - The "pivot" element we find is the biggest among the ones smaller-eq to the pivot value
* or if there is no such (all is greater) we return first greater-than value index (right[0])
*
* @param array The array to partition
* @param low From when. (inclusive)
* @param high Until when. (inclusive too!)
* @param pivotval This value is used to partition the array.
* @returns The partition point.
*/
static inline int partition_with_pivotval(uint32_t array[], int low, int high, uint32_t pivotval) {
/* This is "Lomuto"s unidirectional partitioner - see algorithms book */
/* Select the rightmost element as pivot just because */
/* Need some start-value for min(abs(pv - p)) search! */
int64_t leftmax = -1;
/* Index of currently found pivot value */
uint32_t pivoti = low;
/* index until smaller or eq elements lay */
int i = (low - 1);
/* traverse each element of the array */
/* compare them with the pivotval */
/* The "<=" is needed for our trickz here too */
#pragma GCC unroll 4
for (int j = low; j <= high; ++j) {
/* This "<=" ensures pivoti must be only searched among "left" values! */
if (array[j] <= pivotval) {
/* 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]);
/* After this, array[i] can never change - so we can save it as a found pivot-index */
/* Max-search on elements by telling which is closest to pivotval by abs difference! */
if(array[i] > leftmax) {
pivoti = i;
leftmax = array[i];
}
}
}
/* Must check if above loop found elem at all - because its guessing */
if(i != (low - 1)) {
/* swap the pivot element into its place */
swapit(&array[i], &array[pivoti]);
}
/* return the partition point: index of pivot element */
return i;
}
#endif /* MY_QUICKSORT_H */ #endif /* MY_QUICKSORT_H */

167
zssort.h
View File

@ -1,9 +1,19 @@
#ifndef ZS_SORT_H #ifndef ZS_SORT_H
#define ZS_SORT_H #define ZS_SORT_H
/*
* Structure:
* - ZSSORT: The at most log(n) space needing quicksort
* - RZSSORT: Randomized pivoting variant at most log(n) space needing quicksort
* - RZSSORTC: Added check for fully same value const array (also randomized pivot)
* - MEANQS: Randomized pivoting AND pivoting based on min-max range - dual recursive
*/
#include <stdint.h> #include <stdint.h>
#include "qsort.h" #include "qsort.h"
/* ZSSORT */
/** Always at most log(n) space needing quicksort variant */
static inline void zssort(uint32_t array[], int low, int high) { static inline void zssort(uint32_t array[], int low, int high) {
/* (*) Loop handles original "other half recursion"! */ /* (*) Loop handles original "other half recursion"! */
while(low < high) { while(low < high) {
@ -27,6 +37,9 @@ static inline void zssort(uint32_t array[], int low, int high) {
} }
} }
/* ZSSORTR */
/** Always at most log(n) space needing randomized quicksort variant */
static inline void zssort_rand(uint32_t array[], int low, int high, rpivotstate *state) { static inline void zssort_rand(uint32_t array[], int low, int high, rpivotstate *state) {
while (low < high) { while (low < high) {
int pi = pick_pivot(state, (high + 1) - low) + low; int pi = pick_pivot(state, (high + 1) - low) + low;
@ -50,4 +63,158 @@ static inline void zssort_rand(uint32_t array[], int low, int high, rpivotstate
} }
} }
/* THREEWAY_ZS */
/** Always at most log(n) space needing randomized quicksort variant */
static inline void zssort_rand3(uint32_t array[], int low, int high, rpivotstate *state) {
while (low < high) {
int pi = pick_pivot(state, (high + 1) - low) + low;
pret3 res = partition3(array, low, high, array[pi]);
/* 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((res.leftend - low) < (high - res.rightend)) {
// Left smaller: recurse left of pivot
zssort_rand3(array, low, res.leftend, state);
// (*) Update partitioning loop for remaining part
low = res.rightend;
} else {
// Right smaller: recurse right of pivot
zssort_rand3(array, res.rightend, high, state);
// (*) Update partitioning loop for remaining part
high = res.leftend; /* high inclusive! */
}
}
}
/* ZSSORTC */
/** Always at most log(n) space needing randomized quicksort variant - with checking for sameconst-arrays */
static inline void zsrc(uint32_t array[], int low, int high, rpivotstate *state) {
if (low < high) {
uint32_t min, max;
int pi = pick_pivot(state, (high + 1) - low) + low;
pi = partition_and_minmax_with_pivot(array, pi, low, high, &min, &max);
if(min != max) {
/* Recursion with zssort_rand ensures logn+1 stack spacen need! */
zssort_rand(array, low, pi - 1, state);
zssort_rand(array, pi + 1, high, state);
}
}
}
/* MEANQS */
/**
* Rangsort entry point and outer-recursive function of meanqs.
*
* We pick a random pivot, but in the meantime try to quess mean for next level partitioning.
* This and meanqs_inner is co-recursive (call each other)
*
* @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 meanqs(uint32_t array[], int low, int high, rpivotstate *state);
/**
* Inner-recursive function of meanqs - calls meanqs() 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 meanqs_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
meanqs(array, low, pi - 1, state);
// (*) Update partitioning loop for remaining part
low = pi + 1;
} else {
// Right smaller: recurse right of pivot
meanqs(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
meanqs(array, low, pi - 1, state);
// (*) Update partitioning loop for remaining part
low = pi + 1;
} else {
// Right smaller: recurse right of pivot
meanqs(array, pi + 1, high, state);
// (*) Update partitioning loop for remaining part
high = pi - 1; /* high inclusive! */
}
}
}
/**
* Rangsort entry point and outer-recursive function of meanqs.
*
* We pick a random pivot, but in the meantime try to quess mean for next level partitioning.
* This and meanqs_inner is co-recursive (call each other)
*
* @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 meanqs(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;
/* Recursion with inter "ensures" logn stack spacen need! */
meanqs_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;
/* Recursion with inter "ensures" logn stack spacen need! */
meanqs_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 */ #endif /* ZS_SORT_H */