opennurbs_qsort_template.h
1 // NOTE: 14 April 2011 Dale Lear:
2 // Replace this code with Mikko's "quacksort", once "quacksort" is fully debugged
3 // This code is based ont the VC 2010 crt qsort.c file and must not be released
4 // with public opennurbs.
5 
6 #if !defined(ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS)
7 /*
8 See opennurbs_sort.cpp for examples of using openurbs_qsort_template.c
9 to define type specific quick sort functions.
10 */
11 #error Do not compile openurbs_qsort_template.c directly.
12 #endif
13 
14 #define ON_QSORT_CUTOFF 8 /* testing shows that this is good value */
15 
16 /* Note: the theoretical number of stack entries required is
17  no more than 1 + log2(num). But we switch to insertion
18  sort for CUTOFF elements or less, so we really only need
19  1 + log2(num) - log2(CUTOFF) stack entries. For a CUTOFF
20  of 8, that means we need no more than 30 stack entries for
21  32 bit platforms, and 62 for 64-bit platforms. */
22 #define ON_QSORT_STKSIZ (8*sizeof(void*) - 2)
23 
24 
25 // ON_SORT_TEMPLATE_TYPE -> double, int, ....
26 #if !defined(ON_SORT_TEMPLATE_TYPE)
27 #error Define ON_SORT_TEMPLATE_TYPE macro before including opennurbs_qsort_template.c
28 #endif
29 
30 #if !defined(ON_QSORT_FNAME)
31 #error Define ON_QSORT_FNAME macro before including opennurbs_qsort_template.c
32 #endif
33 
34 #if !defined(ON_QSORT_GT) && !defined(ON_QSORT_LE) && !defined(ON_QSORT_EQ)
35 
36 #if defined(ON_SORT_TEMPLATE_COMPARE)
37 // use a compare function like strcmp for char* strings
38 #define ON_QSORT_GT(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) > 0
39 #define ON_QSORT_LE(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) <= 0
40 #define ON_QSORT_EQ(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) == 0
41 #else
42 // use type compares
43 #define ON_QSORT_GT(A,B) *A > *B
44 #define ON_QSORT_LE(A,B) *A <= *B
45 #define ON_QSORT_EQ(A,B) *A == *B
46 #endif
47 
48 #endif
49 
50 #if defined(ON_SORT_TEMPLATE_SWAP)
51 #define ON_QSORT_SWAP ON_SORT_TEMPLATE_SWAP
52 #elif defined(ON_SORT_TEMPLATE_USE_MEMCPY)
53 #define ON_QSORT_SWAP(A,B) memcpy(&tmp,A,sizeof(tmp));memcpy(A,B,sizeof(tmp));memcpy(B,&tmp,sizeof(tmp))
54 #else
55 #define ON_QSORT_SWAP(A,B) tmp = *A; *A = *B; *B = tmp
56 #endif
57 
58 
59 // When opennurbs_qsort_template.h is included more than once
60 // in the same file for sorting the same type with different
61 // compare functions, then either
62 // 1) After the first include, define ON_SORT_TEMPLATE_HAVE_SHORT_SORT
63 // to prevent generation of an identical short-sort function
64 // or
65 // 2) Define different values of ON_QSORT_SHORT_SORT_FNAME to generate
66 // different short-sort helper functions.
67 #if !defined(ON_SORT_TEMPLATE_HAVE_SHORT_SORT)
68 
69 #if !defined(ON_QSORT_SHORT_SORT_FNAME)
70 // The default name for the short sort helper function is ON__shortsort
71 #define ON_QSORT_SHORT_SORT_FNAME ON__shortsort
72 #endif
73 
74 static void ON_QSORT_SHORT_SORT_FNAME(ON_SORT_TEMPLATE_TYPE *, ON_SORT_TEMPLATE_TYPE *);
75 static void ON_QSORT_SHORT_SORT_FNAME(ON_SORT_TEMPLATE_TYPE *lo, ON_SORT_TEMPLATE_TYPE *hi)
76 {
77  ON_SORT_TEMPLATE_TYPE *p;
78  ON_SORT_TEMPLATE_TYPE *max;
79  ON_SORT_TEMPLATE_TYPE tmp;
80 
81  /* Note: in assertions below, i and j are alway inside original bound of
82  array to sort. */
83 
84  while (hi > lo)
85  {
86  /* A[i] <= A[j] for i <= j, j > hi */
87  max = lo;
88  for (p = lo+1; p <= hi; p++)
89  {
90  /* A[i] <= A[max] for lo <= i < p */
91  if ( ON_QSORT_GT(p,max) )
92  {
93  max = p;
94  }
95  /* A[i] <= A[max] for lo <= i <= p */
96  }
97 
98  /* A[i] <= A[max] for lo <= i <= hi */
99 
100  ON_QSORT_SWAP(max,hi);
101 
102  /* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi */
103 
104  hi--;
105 
106  /* A[i] <= A[j] for i <= j, j > hi, loop top condition established */
107  }
108  /* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
109  so array is sorted */
110 }
111 #endif
112 
113 /* this parameter defines the cutoff between using quick sort and
114  insertion sort for arrays; arrays with lengths shorter or equal to the
115  below value use insertion sort */
116 
117 #if defined(ON_SORT_TEMPLATE_STATIC_FUNCTION)
118 static
119 #endif
120 void
121 ON_QSORT_FNAME (
122  ON_SORT_TEMPLATE_TYPE *base,
123  size_t num
124  )
125 {
126  ON_SORT_TEMPLATE_TYPE *lo; /* start of sub-array currently sorting */
127  ON_SORT_TEMPLATE_TYPE *hi; /* end of sub-array currently sorting */
128  ON_SORT_TEMPLATE_TYPE *mid; /* points to middle of subarray */
129  ON_SORT_TEMPLATE_TYPE *loguy; /* traveling pointers for partition step */
130  ON_SORT_TEMPLATE_TYPE *higuy; /* traveling pointers for partition step */
131  ON_SORT_TEMPLATE_TYPE *lostk[ON_QSORT_STKSIZ];
132  ON_SORT_TEMPLATE_TYPE *histk[ON_QSORT_STKSIZ];
133  size_t size; /* size of the sub-array */
134  int stkptr; /* stack for saving sub-array to be processed */
135  ON_SORT_TEMPLATE_TYPE tmp;
136 
137  if ( 0 == base || num < 2 )
138  return;
139 
140  stkptr = 0; /* initialize stack */
141 
142  lo = base;
143  hi = base + (num-1); /* initialize limits */
144 
145  /* this entry point is for pseudo-recursion calling: setting
146  lo and hi and jumping to here is like recursion, but stkptr is
147  preserved, locals aren't, so we preserve stuff on the stack */
148 recurse:
149 
150  size = (hi - lo) + 1; /* number of el's to sort */
151 
152  /* below a certain size, it is faster to use a O(n^2) sorting method */
153  if (size <= ON_QSORT_CUTOFF)
154  {
155  ON_QSORT_SHORT_SORT_FNAME(lo, hi);
156  }
157  else {
158  /* First we pick a partitioning element. The efficiency of the
159  algorithm demands that we find one that is approximately the median
160  of the values, but also that we select one fast. We choose the
161  median of the first, middle, and last elements, to avoid bad
162  performance in the face of already sorted data, or data that is made
163  up of multiple sorted runs appended together. Testing shows that a
164  median-of-three algorithm provides better performance than simply
165  picking the middle element for the latter case. */
166 
167  mid = lo + (size / 2); /* find middle element */
168 
169  /* Sort the first, middle, last elements into order */
170  if ( ON_QSORT_GT(lo,mid) ) {ON_QSORT_SWAP(lo,mid);}
171  if ( ON_QSORT_GT(lo,hi) ) {ON_QSORT_SWAP(lo,hi);}
172  if ( ON_QSORT_GT(mid,hi) ) {ON_QSORT_SWAP(mid,hi);}
173 
174  /* We now wish to partition the array into three pieces, one consisting
175  of elements <= partition element, one of elements equal to the
176  partition element, and one of elements > than it. This is done
177  below; comments indicate conditions established at every step. */
178 
179  loguy = lo;
180  higuy = hi;
181 
182  /* Note that higuy decreases and loguy increases on every iteration,
183  so loop must terminate. */
184  for (;;)
185  {
186  /* lo <= loguy < hi, lo < higuy <= hi,
187  A[i] <= A[mid] for lo <= i <= loguy,
188  A[i] > A[mid] for higuy <= i < hi,
189  A[hi] >= A[mid] */
190 
191  /* The doubled loop is to avoid calling comp(mid,mid), since some
192  existing comparison funcs don't work when passed the same
193  value for both pointers. */
194 
195  if (mid > loguy)
196  {
197  do {
198  loguy++;
199  } while (loguy < mid && ON_QSORT_LE(loguy,mid));
200  }
201  if (mid <= loguy)
202  {
203  do {
204  loguy++;
205  } while (loguy <= hi && ON_QSORT_LE(loguy,mid));
206  }
207 
208  /* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy,
209  either loguy > hi or A[loguy] > A[mid] */
210 
211  do {
212  higuy--;
213  } while (higuy > mid && ON_QSORT_GT(higuy,mid));
214 
215  /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi,
216  either higuy == lo or A[higuy] <= A[mid] */
217 
218  if (higuy < loguy)
219  break;
220 
221  /* if loguy > hi or higuy == lo, then we would have exited, so
222  A[loguy] > A[mid], A[higuy] <= A[mid],
223  loguy <= hi, higuy > lo */
224 
225  ON_QSORT_SWAP(loguy,higuy);
226 
227  /* If the partition element was moved, follow it. Only need
228  to check for mid == higuy, since before the swap,
229  A[loguy] > A[mid] implies loguy != mid. */
230 
231  if (mid == higuy)
232  mid = loguy;
233 
234  /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top
235  of loop is re-established */
236  }
237 
238  /* A[i] <= A[mid] for lo <= i < loguy,
239  A[i] > A[mid] for higuy < i < hi,
240  A[hi] >= A[mid]
241  higuy < loguy
242  implying:
243  higuy == loguy-1
244  or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */
245 
246  /* Find adjacent elements equal to the partition element. The
247  doubled loop is to avoid calling comp(mid,mid), since some
248  existing comparison funcs don't work when passed the same value
249  for both pointers. */
250 
251  higuy++;
252  if (mid < higuy) {
253  do {
254  higuy--;
255  } while (higuy > mid && ON_QSORT_EQ(higuy,mid));
256  }
257  if (mid >= higuy) {
258  do {
259  higuy--;
260  } while (higuy > lo && ON_QSORT_EQ(higuy,mid));
261  }
262 
263  /* OK, now we have the following:
264  higuy < loguy
265  lo <= higuy <= hi
266  A[i] <= A[mid] for lo <= i <= higuy
267  A[i] == A[mid] for higuy < i < loguy
268  A[i] > A[mid] for loguy <= i < hi
269  A[hi] >= A[mid] */
270 
271  /* We've finished the partition, now we want to sort the subarrays
272  [lo, higuy] and [loguy, hi].
273  We do the smaller one first to minimize stack usage.
274  We only sort arrays of length 2 or more.*/
275 
276  if ( higuy - lo >= hi - loguy ) {
277  if (lo < higuy) {
278  lostk[stkptr] = lo;
279  histk[stkptr] = higuy;
280  ++stkptr;
281  } /* save big recursion for later */
282 
283  if (loguy < hi) {
284  lo = loguy;
285  goto recurse; /* do small recursion */
286  }
287  }
288  else {
289  if (loguy < hi) {
290  lostk[stkptr] = loguy;
291  histk[stkptr] = hi;
292  ++stkptr; /* save big recursion for later */
293  }
294 
295  if (lo < higuy) {
296  hi = higuy;
297  goto recurse; /* do small recursion */
298  }
299  }
300  }
301 
302  /* We have sorted the array, except for any pending sorts on the stack.
303  Check if there are any, and do them. */
304 
305  --stkptr;
306  if (stkptr >= 0) {
307  lo = lostk[stkptr];
308  hi = histk[stkptr];
309  goto recurse; /* pop subarray from stack */
310  }
311  else
312  return; /* all subarrays done */
313 }
314 
315 #undef ON_QSORT_GT
316 #undef ON_QSORT_LE
317 #undef ON_QSORT_EQ
318 #undef ON_QSORT_SWAP
319 #undef ON_QSORT_CUTOFF
320 #undef ON_QSORT_STKSIZ
321 
322