/* * qsort.c: * Our own version of the system qsort routine which is faster by an average * of 25%, with lows and highs of 10% and 50%. * The THRESHold below is the insertion sort threshold, and has been adjusted * for records of size 48 bytes. * The MTHREShold is where we stop finding a better median. */ #include "element.h" #define MTHRESH 6 /* threshold for median */ /* * qsort: * First, set up some global parameters for qst to share. Then, quicksort * with qst(), and then a cleanup insertion sort ourselves. Sound simple? * It's not... */ static int comparison(element *s1,element *s2) { int d,j=0; do { if ((d=s1->c[j] - s2->c[j])) return(d); } while (++j= THRESH) { qst (base, max); hi = base + THRESH; } else { hi = max; } /* * First put smallest element, which must be in the first THRESH, in * the first position as a sentinel. This is done just by searching * the first THRESH elements (or the first n if n < THRESH), finding * the min, and swapping it into the first position. */ for (j = lo = base; (lo += 1) < hi; ) { if (COMPARE(j, lo) > 0) j = lo; } if (j != base) { /* swap j into place */ for (i = base, hi = base + 1; i < hi;) { SWAPP(i,j); i+=1;j+=1; } } /* * With our sentinel in place, we now run the following hyper-fast * insertion sort. For each remaining element, min, from [1] to [n-1], * set hi to the index of the element AFTER which this one goes. * Then, do the standard insertion sort shift on a character at a time * basis for each element in the frob. */ for (min = base; (hi = min += 1) < max;) { while ( COMPARE(hi -= 1, min) > 0); if ((hi += 1) != min) { element cI; element *loI,*minI=(element *)min,*hiI=(element *)hi,*iI,*jI; for (loI = minI + 1; --loI >= minI;) { cI = *loI; for (iI = jI = loI; (jI -= 1) >= hiI; iI = jI) *iI = *jI; *iI = cI; } } } } /* * qst: * Do a quicksort * First, find the median element, and put that one in the first place as the * discriminator. (This "median" is just the median of the first, last and * middle elements). (Using this median instead of the first element is a big * win). Then, the usual partitioning/swapping, followed by moving the * discriminator into the right place. Then, figure out the sizes of the two * partions, do the smaller one recursively and the larger one via a repeat of * this code. Stopping when there are less than THRESH elements in a partition * and cleaning up with an insertion sort (in our caller) is a huge win. * All data swaps are done in-line, which is space-losing but time-saving. * (And there are only three places where this is done). */ void qst (base, max) element *base, *max; { element *i, *j, *jj, *mid; element *tmp; int lo, hi; lo = max - base; /* number of elements as chars */ do { /* * At the top here, lo is the number of characters of elements in the * current partition. (Which should be max - base). * Find the median of the first, last, and middle element and make that the * middle element. Set j to largest of first and middle. If max is larger * than that guy, then it's that guy, else compare max with loser of first * and take larger. Things are set up to prefer the middle, then the first * in case of ties. */ mid = i = base + (lo >> 1); if (lo >= MTHRESH) { jj = base; j = (COMPARE(jj, i) > 0 ? jj : i); tmp = max - 1; if (COMPARE(j, tmp) > 0) { j = (j == jj ? i : jj); /* switch to first loser */ if (COMPARE(j, tmp) < 0) j = tmp; } if (j != i) { SWAPP(i,j); i+=1;j+=1; } } /* * Semi-standard quicksort partitioning/swapping */ for (i = base, j = max - 1; ;) { while (i < mid && COMPARE(i, mid) <= 0) i += 1; while (j > mid) { if (COMPARE(mid, j) <= 0) { j -= 1; continue; } tmp = i + 1; /* value of i after swap */ if (i == mid) { /* j <-> mid, new mid is j */ mid = jj = j; } else { /* i <-> j */ jj = j; j -= 1; } goto swap; } if (i == mid) { break; } else { /* i <-> mid, new mid is i */ jj = mid; tmp = mid = i; /* value of i after swap */ j -= 1; } swap: SWAPP(i,jj); i = tmp; } /* * Look at sizes of the two partitions, do the smaller one first by * recursion, then do the larger one by making sure lo is its size, * base and max are update correctly, and branching back. * But only repeat (recursively or by branching) if the partition is * of at least size THRESH. */ i = (j = mid) + 1; if ((lo = j - base) <= (hi = max - i)) { if (lo >= THRESH) qst (base, j); base = i; lo = hi; } else { if (hi >= THRESH) qst (i, max); max = j; } } while (lo >= THRESH); } void rsort(char *base,int n) { gnu_qsort(base,n,ELSIZE,comparison); }