opennurbs_quacksort_template.h
1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2011 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Assoicates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16 
17 /*
18 See opennurbs_sort.cpp for examples showing how to use
19 opennurbs_quacksort_template.h to define type specific
20 sorting functions.
21 */
22 
23 #if !defined(ON_QSORT_FNAME)
24 #error Define ON_QSORT_FNAME macro before including opennurbs_quacksort_template.h
25 #endif
26 
27 // ON_SORT_TEMPLATE_TYPE -> double, int, ....
28 #if !defined(ON_SORT_TEMPLATE_TYPE)
29 
30 #define BASETYPE void *
31 #define DATATYPE unsigned char
32 #define DATAWIDTH m_width
33 
34 #define Swap(a,b) m_swapfunc(a,b,m_width)
35 
36 #if defined(ON_SORT_TEMPLATE_USE_CONTEXT)
37 // use a compare function with context parameter
38 #define GreaterThan(A,B) m_compare(m_context,A,B) > 0
39 #else
40 // use a compare function without context parameter
41 #define GreaterThan(A,B) m_compare(A,B) > 0
42 #endif
43 
44 #else
45 
46 #define BASETYPE ON_SORT_TEMPLATE_TYPE *
47 #define DATATYPE ON_SORT_TEMPLATE_TYPE
48 #define DATAWIDTH 1
49 
50 #if defined(ON_SORT_TEMPLATE_USE_SWAP)
51 #define Swap(a,b) m_swapfunc(a,b,m_width)
52 #else
53 // use intrinsic assigment
54 #define Swap(a,b) ON_SORT_TEMPLATE_TYPE tmp = *a; *a = *b; *b = tmp
55 #endif
56 
57 #if defined(ON_SORT_TEMPLATE_COMPARE)
58 // use a compare function like strcmp for char* strings
59 #define GreaterThan(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) > 0
60 #else
61 // use intrinsic type compares
62 #define GreaterThan(A,B) *A > *B
63 #endif
64 
65 #endif
66 
67 #if !defined(ON_QUACKSORT_SWAP_FUNCS_DEFINED)
68 #if !defined(ON_SORT_TEMPLATE_TYPE) || defined(ON_SORT_TEMPLATE_USE_SWAP)
69 
70 // In some files this template is used multiple times.
71 // The ON_QUACKSORT_SWAP_FUNCS_DEFINED define prevents
72 // multiple definitions of the static Swap*() functions.
73 #define ON_QUACKSORT_SWAP_FUNCS_DEFINED
74 
75 static void SwapChars( unsigned char* a, unsigned char* b, size_t width)
76 {
77  do
78  {
79  unsigned char x = *a;
80  *a++ = *b;
81  *b++ = x;
82  }
83  while( --width);
84 }
85 
86 static void SwapInts( unsigned char* a, unsigned char* b, size_t width)
87 {
88  ON__UINT32* ai = (ON__UINT32*)a;
89  ON__UINT32* bi = (ON__UINT32*)b;
90  do
91  {
92  ON__UINT32 x = *ai;
93  *ai++ = *bi;
94  *bi++ = x;
95  width -= sizeof(x);
96  }
97  while( width);
98 }
99 
100 static void SwapBigInts( unsigned char* a, unsigned char* b, size_t width)
101 {
102  ON__UINT64* ai = (ON__UINT64*)a;
103  ON__UINT64* bi = (ON__UINT64*)b;
104  do
105  {
106  ON__UINT64 x = *ai;
107  *ai++ = *bi;
108  *bi++ = x;
109  width -= sizeof(x);
110  }
111  while( width);
112 }
113 
114 #endif
115 #endif
116 
117 // implementation of quick sort with minimum swaps for partition sizes 4 and less
118 void ON_quacksort(
119  BASETYPE *base
120  ,size_t nel
121 #if !defined(ON_SORT_TEMPLATE_TYPE)
122  ,size_t width
123 #if defined(ON_SORT_TEMPLATE_USE_CONTEXT)
124  ,int (*compar)(void*, const void *, const void *)
125  ,void* context
126 #else
127  ,int (*compar)(const void *, const void *)
128 #endif
129 #endif
130  )
131 {
132  class CSorter
133  {
134  public:
135  DATATYPE *m_base;
136  size_t m_nel;
137  const size_t m_width;
138  int (*m_compar)(const void *, const void *);
139  void (*m_swapfunc)(unsigned char *, unsigned char *, size_t width);
140  unsigned int m_rnd;
141 //#if defined(ON_SORT_TEMPLATE_TYPE) && !defined(ON_SORT_TEMPLATE_USE_SWAP)
142 // ON_SORT_TEMPLATE_TYPE m_tmp;
143 //#endif
144 
145  CSorter(
146  DATATYPE *base
147  , size_t nel
148  , size_t width
149  , int (*compar)(const void *, const void *)
150  )
151  : m_base((DATATYPE*)base)
152  , m_nel(nel)
153  , m_width(width)
154  , m_compar(compar)
155  , m_rnd(62538161)
156  {
157  // When width is a multiple of 8 or 4 (with most arrays it probably is),
158  // use faster integer swappers instead of byte swapper
159  if ( 0 == width%sizeof(ON__UINT64))
160  m_swapfunc = SwapBigInts;
161  else if ( 0 == width%sizeof(ON__UINT32))
162  m_swapfunc = SwapInts;
163  else
164  m_swapfunc = SwapChars;
165  };
166 
167  ~CSorter() {};
168 
169  DATATYPE* Pivot( DATATYPE* base, size_t count)
170  {
171  // Uses local quick and dirty pseudorandom number generator to
172  // give a fuzzy answer to avoid having the data be arranged in
173  // a way that mechanically always picking the pivot the same way
174  // affects the speed. Mostly affects chevron etc. patterns.
175  //
176  // Totally random pivot would guarantee O(nlogn) worst case, but
177  // does not perform as well on sorted or nearly sorted sets.
178 
179  m_rnd *= 1664525;
180  m_rnd += 1013904223;
181  unsigned int dice = (m_rnd>>16)&7;
182 
183  size_t p=count>>1; // 1/2
184 
185  if ( dice&4)
186  p += count>>3; // +1/8
187  if ( dice&2)
188  p -= count>>4; // -1/16
189  if ( dice&1)
190  p -= count>>5; // -1/32
191 
192  return base + p*DATAWIDTH;
193  }
194 
195  void SortSmallRange( DATATYPE* p0, size_t count)
196  {
197  // use minimum compares and swaps for 2 to 4 items
198  switch (count)
199  {
200  case 2:
201  {
202  DATATYPE* p1 = p0 + DATAWIDTH;
203  if ( GreaterThan( p0, p1)) { Swap( p0, p1);}
204  return;
205  }
206  case 3:
207  {
208  DATATYPE* p1 = p0 + DATAWIDTH;
209  DATATYPE* p2 = p1 + DATAWIDTH;
210  bool b = false;
211  if ( GreaterThan( p0, p1)) { Swap( p0, p1); b = true;}
212  if ( GreaterThan( p1, p2)) { Swap( p1, p2); b = true;}
213  if ( b && GreaterThan( p0, p1)) { Swap( p0, p1);}
214  return;
215  }
216  case 4:
217  {
218  DATATYPE* p1 = p0 + DATAWIDTH;
219  DATATYPE* p2 = p1 + DATAWIDTH;
220  DATATYPE* p3 = p2 + DATAWIDTH;
221  if ( GreaterThan( p0, p3)) { Swap( p0, p3);}
222  if ( GreaterThan( p1, p2)) { Swap( p1, p2);}
223  bool b = false;
224  if ( GreaterThan( p2, p3)) { Swap( p2, p3); b = true;}
225  if ( GreaterThan( p0, p1)) { Swap( p0, p1); b = true;}
226  if ( b && GreaterThan( p1, p2)) { Swap( p1, p2);}
227  return;
228  }
229  }
230  }
231 
232  void SortRange( DATATYPE* left, DATATYPE* right)
233  {
234  while ( left<right)
235  {
236  size_t count = (right-left)/DATAWIDTH+1;
237 
238  if ( count < 5)
239  return SortSmallRange( left, count);
240 
241  DATATYPE* pivotleft;
242  DATATYPE* pivotright;
243 
244  // partition range
245  {
246  pivotleft = Pivot( left, count);
247 
248  // move pivot to left end
249  Swap( left, pivotleft);
250 
251  pivotleft = left;
252  pivotright = right + DATAWIDTH;
253 
254  // move =< pivot to left, and > pivot to right
255  for(;;)
256  {
257  // find next first item > pivot
258  pivotleft += DATAWIDTH;
259  if ( pivotleft >= pivotright)
260  break;
261  if ( !GreaterThan( pivotleft, left))
262  continue;
263 
264  // find next last item =< pivot
265  do
266  {
267  pivotright -= DATAWIDTH;
268  if ( pivotleft >= pivotright)
269  goto END; // to quickly exit a nested loop
270  }
271  while( GreaterThan( pivotright, left));
272 
273  Swap( pivotleft, pivotright);
274  }
275 
276  END:
277 
278  pivotright -= DATAWIDTH;
279  // move pivot to final place
280  Swap( left, pivotright);
281  pivotleft = pivotright;
282 
283  // avoid overhead when not likely that there are multiple items == pivot
284  if ( pivotright >= right)
285  {
286  // the whole range is less or equal than pivot
287  // check if there are values == pivot left of it. Speeds up sorting arrays with all or lots of equal items.
288  for ( pivotleft -= DATAWIDTH; pivotleft > left; pivotleft -= DATAWIDTH)
289  {
290  if ( GreaterThan( pivotright, pivotleft))
291  break;
292  }
293  pivotleft += DATAWIDTH;
294  }
295  }
296 
297  // limit max recursion depth to log(nel) by only recursing shorter part
298  if ( pivotleft-left < right-pivotright)
299  {
300  // lower part is shorter
301  SortRange( left, pivotleft-DATAWIDTH);
302  left = pivotright+DATAWIDTH;
303  }
304  else
305  {
306  // upper part is shorter
307  SortRange( pivotright+DATAWIDTH, right);
308  right = pivotleft-DATAWIDTH;
309  }
310  }
311  }
312 
313  void Sort()
314  {
315  SortRange( m_base, m_base + (m_nel-1)*DATAWIDTH);
316  }
317  };
318 
319  if ( !base || nel < 2 )
320  return;
321 #if !defined(ON_SORT_TEMPLATE_TYPE)
322  if ( width < 1 || !compar)
323  return;
324 #endif
325 
326  CSorter sorter( base, nel, width, compar);
327  sorter.Sort();
328 }
329 
330 #undef Swap
331 #undef GreaterThan
332 #undef DATAWIDTH
333 #undef DATATYPE