1 | /*************** CSort C Program Source Code File (.CPP) ***************/ |
2 | /* PROGRAM NAME: CSORT */ |
3 | /* ------------- */ |
4 | /* Version 2.2 */ |
5 | /* */ |
6 | /* COPYRIGHT: */ |
7 | /* ---------- */ |
8 | /* (C) Copyright to the author Olivier Bertrand 1995-2016 */ |
9 | /* */ |
10 | /* WHAT THIS PROGRAM DOES: */ |
11 | /* ----------------------- */ |
12 | /* This program is the C++ sorting routines that use qsort/insert */ |
13 | /* algorithm and produces an offset/break table while sorting. */ |
14 | /* */ |
15 | /* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ |
16 | /* -------------------------------------- */ |
17 | /* */ |
18 | /* REQUIRED FILES: */ |
19 | /* --------------- */ |
20 | /* csort.cpp - Source code */ |
21 | /* */ |
22 | /* REQUIRED LIBRARIES: */ |
23 | /* ------------------- */ |
24 | /* OS2DEF.LIB - OS2 libray definition subset. */ |
25 | /* */ |
26 | /* REQUIRED PROGRAMS: */ |
27 | /* ------------------ */ |
28 | /* Microsoft C++ Compiler */ |
29 | /* or GNU Compiler/Linker */ |
30 | /* or BORLAND 4.5 C++ compiler */ |
31 | /* */ |
32 | /* NOTE */ |
33 | /* ---- */ |
34 | /* These functions are not 64-bits ready. */ |
35 | /* */ |
36 | /***********************************************************************/ |
37 | |
38 | /***********************************************************************/ |
39 | /* Include relevant MariaDB header file. */ |
40 | /***********************************************************************/ |
41 | #include "my_global.h" |
42 | |
43 | /***********************************************************************/ |
44 | /* Include application header files */ |
45 | /***********************************************************************/ |
46 | #include <stdlib.h> /* C standard library */ |
47 | #include <string.h> /* String manipulation declares */ |
48 | #include <stdio.h> /* Required for sprintf declare */ |
49 | #if defined(_DEBUG) |
50 | #include <assert.h> /* Assertion routine declares */ |
51 | #endif |
52 | |
53 | /***********************************************************************/ |
54 | /* Include CSort class header file */ |
55 | /***********************************************************************/ |
56 | #include "global.h" |
57 | #include "plgdbsem.h" /* For MBLOCK type definition */ |
58 | #include "csort.h" /* CSort class definition */ |
59 | #include "osutil.h" |
60 | |
61 | #if !defined(BIGSORT) |
62 | #define BIGSORT 200000 |
63 | #endif // !BIGSORT |
64 | |
65 | /***********************************************************************/ |
66 | /* DB static external variables. */ |
67 | /***********************************************************************/ |
68 | extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */ |
69 | |
70 | /***********************************************************************/ |
71 | /* Initialize the CSORT static members. */ |
72 | /***********************************************************************/ |
73 | int CSORT::Limit = 0; |
74 | double CSORT::Lg2 = log(2.0); |
75 | size_t CSORT::Cpn[1000] = {0}; /* Precalculated cmpnum values */ |
76 | |
77 | /***********************************************************************/ |
78 | /* CSORT constructor. */ |
79 | /***********************************************************************/ |
80 | CSORT::CSORT(bool cns, int th, int mth) |
81 | : Pex((int*&)Index.Memp), Pof((int*&)Offset.Memp) |
82 | { |
83 | G = NULL; |
84 | Dup =NULL; |
85 | Cons = cns; |
86 | Thresh = th; |
87 | Mthresh = mth; |
88 | Nitem = 0; |
89 | Index = Nmblk; |
90 | Offset = Nmblk; |
91 | Swix = NULL; |
92 | Savmax = 0; |
93 | Savcur = 0; |
94 | Savstep = NULL; |
95 | } // end of CSORT constructor |
96 | |
97 | /***********************************************************************/ |
98 | /* CSORT intialization. */ |
99 | /***********************************************************************/ |
100 | int CSORT::Qsort(PGLOBAL g, int nb) |
101 | { |
102 | int rc; |
103 | |
104 | #if defined(_DEBUG) |
105 | assert(Index.Size >= nb * sizeof(int)); |
106 | #endif |
107 | |
108 | if (nb > BIGSORT) { |
109 | G = g; |
110 | Dup = (PDBUSER)g->Activityp->Aptr; |
111 | |
112 | if (Dup->Proginfo) { |
113 | Savstep = Dup->Step; |
114 | Savmax = Dup->ProgMax; |
115 | Savcur = Dup->ProgCur; |
116 | |
117 | // Evaluate the number of comparisons that we will do |
118 | Dup->ProgMax = Cmpnum(nb); |
119 | Dup->ProgCur = 0; |
120 | Dup->Step = (char*)PlugSubAlloc(g, NULL, 32); |
121 | sprintf((char*)Dup->Step, MSG(SORTING_VAL), nb); |
122 | } else |
123 | Dup = NULL; |
124 | |
125 | } else |
126 | Dup = NULL; |
127 | |
128 | Nitem = nb; |
129 | |
130 | for (int n = 0; n < Nitem; n++) |
131 | Pex[n] = n; |
132 | |
133 | rc = (Cons) ? Qsortc() : Qsortx(); |
134 | |
135 | if (Dup) { |
136 | // Restore any change in progress info settings |
137 | // printf("Progcur=%u\n", Dup->ProgCur); |
138 | |
139 | Dup->Step = Savstep; |
140 | Dup->ProgMax = Savmax; |
141 | Dup->ProgCur = Savcur; |
142 | } // endif Subcor |
143 | |
144 | return rc; |
145 | } // end of QSort |
146 | |
147 | #if defined(DEBTRACE) |
148 | /***********************************************************************/ |
149 | /* Debug routine to be used by sort for specific data (dummy as now) */ |
150 | /***********************************************************************/ |
151 | void CSORT::DebugSort(int ph, int n, int *base, int *mid, int *tmp) |
152 | { |
153 | htrc("phase=%d n=%d base=%p mid=%p tmp=%p\n" , |
154 | ph, n, base, mid, tmp); |
155 | } // end of DebugSort |
156 | #endif |
157 | |
158 | /***********************************************************************/ |
159 | /* Qsortx: Version adapted from qsortx.c by O.Bertrand */ |
160 | /* This version is specialy adapted for Index sorting, meaning that */ |
161 | /* the data is not moved, but the Index only is sorted. */ |
162 | /* Index array elements are any 4-byte word (a pointer or a int int */ |
163 | /* array index), they are not interpreted except by the user provided */ |
164 | /* comparison routine which must works accordingly. */ |
165 | /* In addition, this program takes care of data in which there is a */ |
166 | /* high rate of repetitions. */ |
167 | /* CAUTION: the sort algorithm used here is not conservative. Equal */ |
168 | /* values will be internally stored in unpredictable order. */ |
169 | /* The THRESHold below is the insertion sort threshold, and also the */ |
170 | /* threshold for continuing que quicksort partitioning. */ |
171 | /* The MTHREShold is where we stop finding a better median. */ |
172 | /* These two quantities should be adjusted dynamically depending upon */ |
173 | /* the repetition rate of the data. */ |
174 | /* Algorithm used: */ |
175 | /* First, set up some global parameters for Qstx to share. Then, */ |
176 | /* quicksort with Qstx(), and then a cleanup insertion sort ourselves. */ |
177 | /* Sound simple? It's not... */ |
178 | /***********************************************************************/ |
179 | int CSORT::Qsortx(void) |
180 | { |
181 | register int c; |
182 | register int lo, hi, min; |
183 | register int i, j, rc = 0; |
184 | // To do: rc should be checked for being used uninitialized |
185 | int *top; |
186 | #ifdef DEBTRACE |
187 | int ncp; |
188 | |
189 | num_comp = 0; |
190 | #endif |
191 | |
192 | /*********************************************************************/ |
193 | /* Prepare the Offset array that will be updated during sorts. */ |
194 | /*********************************************************************/ |
195 | if (Pof) |
196 | for (Pof[Nitem] = Nitem, j = 0; j < Nitem; j++) |
197 | Pof[j] = 0; |
198 | else |
199 | j = Nitem + 1; |
200 | |
201 | /*********************************************************************/ |
202 | /* Sort on one or zero element is obvious. */ |
203 | /*********************************************************************/ |
204 | if (Nitem <= 1) |
205 | return Nitem; |
206 | |
207 | /*********************************************************************/ |
208 | /* Thresh seems to be good as (10 * n / rep). But for testing we */ |
209 | /* set it directly as one parameter of the Xset function call. */ |
210 | /* Note: this should be final as the rep parameter is no more used. */ |
211 | /*********************************************************************/ |
212 | top = Pex + Nitem; |
213 | |
214 | #ifdef DEBTRACE |
215 | htrc("Qsortx: nitem=%d thresh=%d mthresh=%d\n" , |
216 | Nitem, Thresh, Mthresh); |
217 | #endif |
218 | |
219 | /*********************************************************************/ |
220 | /* If applicable, do a rough preliminary quick sort. */ |
221 | /*********************************************************************/ |
222 | if (Nitem >= Thresh) |
223 | Qstx(Pex, top); |
224 | |
225 | #ifdef DEBTRACE |
226 | htrc(" after quick sort num_comp=%d\n" , num_comp); |
227 | ncp = num_comp; |
228 | num_comp = 0; |
229 | #ifdef DEBUG2 |
230 | DebugSort((Pof) ? 1 : 4, Nitem, Pex, NULL, NULL); |
231 | #endif |
232 | #endif |
233 | |
234 | if (Thresh > 2) { |
235 | if (Pof) |
236 | /*****************************************************************/ |
237 | /* The preliminary search for the smallest element has been */ |
238 | /* removed so with no sentinel in place, we must check for x */ |
239 | /* going below the Pof pointer. For each remaining element */ |
240 | /* group from [1] to [n-1], set hi to the index of the element */ |
241 | /* AFTER which this one goes. Then, do the standard insertion */ |
242 | /* sort shift on an integer at a time basis for each equal */ |
243 | /* element group in the frob. */ |
244 | /*****************************************************************/ |
245 | for (min = hi = 0; min < Nitem; min = hi) { |
246 | if (Pof[hi]) { |
247 | hi += Pof[hi]; |
248 | continue; |
249 | } // endif Pof |
250 | |
251 | Pof[min] = 1; |
252 | |
253 | #ifdef DEBUG2 |
254 | htrc("insert from min=%d\n" , min); |
255 | #endif |
256 | |
257 | for (lo = hi; !Pof[++hi]; lo = hi) { |
258 | while (lo >= min && (rc = Qcompare(Pex + lo, Pex + hi)) > 0) |
259 | if (Pof[lo] > 0) |
260 | lo -= Pof[lo]; |
261 | else |
262 | return -2; |
263 | |
264 | if (++lo != hi) { |
265 | c = Pex[hi]; |
266 | |
267 | for (i = j = hi; i > 0; i = j) |
268 | if (Pof[i - 1] <= 0) |
269 | return -3; |
270 | else if ((j -= Pof[i - 1]) >= lo) { |
271 | Pex[i] = Pex[j]; |
272 | Pof[j + 1] = Pof[i] = Pof[j]; |
273 | } else |
274 | break; |
275 | |
276 | Pex[i] = c; |
277 | } // endif lo |
278 | |
279 | if (rc) |
280 | Pof[lo] = 1; |
281 | else { |
282 | i = lo - Pof[lo - 1]; |
283 | Pof[lo] = ++Pof[i]; |
284 | } // endelse |
285 | |
286 | #ifdef DEBUG2 |
287 | htrc("rc=%d lo=%d hi=%d trx=%d\n" , rc, lo, hi, Pof[lo]); |
288 | #endif |
289 | |
290 | } // endfor hi |
291 | |
292 | } // endfor min |
293 | |
294 | else |
295 | /*****************************************************************/ |
296 | /* Call conservative insertion sort not using/setting offset. */ |
297 | /*****************************************************************/ |
298 | Istc(Pex, Pex + MY_MIN(Nitem, Thresh), top); |
299 | |
300 | } // endif Thresh |
301 | |
302 | #ifdef DEBTRACE |
303 | htrc(" after insert sort num_comp=%d\n" , num_comp); |
304 | num_comp += ncp; |
305 | #endif |
306 | |
307 | if (Pof) |
308 | /*******************************************************************/ |
309 | /* Reduce the Offset array. */ |
310 | /*******************************************************************/ |
311 | for (i = j = 0; i <= Nitem; j++, i += c) { |
312 | #ifdef DEBUG2 |
313 | htrc(" trxp(%d)=%d trxp(%d)=%d c=%d\n" , |
314 | i, Pof[i], j, Pof[j], c); |
315 | #endif |
316 | if ((c = Pof[i])) |
317 | Pof[j] = i; |
318 | else |
319 | return -4; |
320 | |
321 | } // endfor i |
322 | |
323 | return (j - 1); |
324 | } // end of Qsortx |
325 | |
326 | /***********************************************************************/ |
327 | /* Qstx: Do a quicksort on index elements (just one int int). */ |
328 | /* First, find the median element, and put that one in the first place */ |
329 | /* as the discriminator. (This "median" is just the median of the */ |
330 | /* first, last and middle elements). (Using this median instead of */ |
331 | /* the first element is a big win). Then, the usual partitioning/ */ |
332 | /* swapping, followed by moving the discriminator into the right place.*/ |
333 | /* Element equal to the discriminator are placed against it, so the */ |
334 | /* mid (discriminator) block grows when equal elements exist. This is */ |
335 | /* a huge win in case of repartitions with few different elements. */ |
336 | /* The mid block being at its final position, its first and last */ |
337 | /* elements are marked in the offset list (used to make break list). */ |
338 | /* Then, figure out the sizes of the two partitions, do the smaller */ |
339 | /* one recursively and the larger one via a repeat of this code. */ |
340 | /* Stopping when there are less than THRESH elements in a partition */ |
341 | /* and cleaning up with an insertion sort (in our caller) is a huge */ |
342 | /* win(?). All data swaps are done in-line, which is space-losing but */ |
343 | /* time-saving. (And there are only three places where this is done). */ |
344 | /***********************************************************************/ |
345 | void CSORT::Qstx(int *base, int *max) |
346 | { |
347 | register int *i, *j, *jj, *mid, *him, c; |
348 | int *tmp; |
349 | int lo, hi, rc; |
350 | size_t zlo, zhi, cnm; |
351 | |
352 | zlo = zhi = cnm = 0; // Avoid warning message |
353 | |
354 | lo = (int)(max - base); // Number of elements as longs |
355 | |
356 | if (Dup) |
357 | cnm = Cmpnum(lo); |
358 | |
359 | do { |
360 | /*******************************************************************/ |
361 | /* At the top here, lo is the number of integers of elements in */ |
362 | /* the current partition. (Which should be max - base). */ |
363 | /* Find the median of the first, last, and middle element and make */ |
364 | /* that the middle element. Set j to largest of first and middle. */ |
365 | /* If max is larger than that guy, then it's that guy, else */ |
366 | /* compare max with loser of first and take larger. Things are */ |
367 | /* set up to prefer the middle, then the first in case of ties. */ |
368 | /* In addition, hi and rc are set to comparison results. So if hi */ |
369 | /* is null, the two high values are equal and if rc is null, the */ |
370 | /* two low values are equal. This was used to set which test will */ |
371 | /* be made by LE and which one by LT (does not apply anymore). */ |
372 | /*******************************************************************/ |
373 | him = mid = i = base + (lo >> 1); |
374 | hi = rc = 0; |
375 | |
376 | #ifdef DEBTRACE |
377 | tmp = max - 1; |
378 | htrc("--> block base=%d size=%d\n" , base - Pex, lo); |
379 | DebugSort(2, 0, base, mid, tmp); |
380 | #endif |
381 | |
382 | if (lo >= Mthresh) { |
383 | rc = Qcompare((jj = base), i); |
384 | j = (rc > 0) ? jj : i; |
385 | hi = Qcompare(j, (tmp = max - 1)); |
386 | |
387 | if (hi > 0 && rc) { |
388 | j = (j == jj) ? i : jj; // switch to first loser |
389 | |
390 | if ((rc = Qcompare(j, tmp)) < 0) |
391 | j = tmp; |
392 | |
393 | } // endif |
394 | |
395 | if (j != i) { |
396 | c = *i; |
397 | *i = *j; |
398 | *j = c; |
399 | } // endif j |
400 | |
401 | } else if (lo == 2) { |
402 | /*****************************************************************/ |
403 | /* Small group. Do special quicker processing. */ |
404 | /*****************************************************************/ |
405 | if ((rc = Qcompare(base, (him = base + 1))) > 0) |
406 | c = *base, *base = *him, *him = c; |
407 | |
408 | if (Pof) |
409 | Pof[base - Pex] = Pof[him - Pex] = (rc) ? 1 : 2; |
410 | |
411 | break; |
412 | } // endif lo |
413 | |
414 | #ifdef DEBTRACE |
415 | DebugSort(3, hi, NULL, mid, &rc); |
416 | #endif |
417 | |
418 | /*******************************************************************/ |
419 | /* Semi-standard quicksort partitioning/swapping. Added here is */ |
420 | /* a test on equality. All values equal to the mid element are */ |
421 | /* placed under or over it. Mid block can be also moved when it */ |
422 | /* is necessary because the other partition is full. At the end */ |
423 | /* of the for loop the mid block is definitely positionned. */ |
424 | /*******************************************************************/ |
425 | for (i = base, j = max - 1; ;) { |
426 | CONT: |
427 | while (i < mid) |
428 | if ((rc = Qcompare(i, mid)) < 0) |
429 | i++; |
430 | else if (!rc) { |
431 | c = *i; |
432 | *i = *(--mid); |
433 | *mid = c; |
434 | } else |
435 | break; |
436 | |
437 | while (j > him) |
438 | if ((rc = Qcompare(him, j)) < 0) |
439 | j--; |
440 | else if (!rc) { |
441 | c = *j; |
442 | *j = *(++him); |
443 | *him = c; |
444 | } else if (i == mid) { // Triple move: |
445 | c = *j; // j goes under mid block |
446 | *j = *(++him); // val over mid block -> j |
447 | *him = *mid++; // and mid block goes one |
448 | *i++ = c; // position higher. |
449 | } else { // i <-> j |
450 | c = *i; |
451 | *i++ = *j; |
452 | *j-- = c; |
453 | goto CONT; |
454 | } // endif's |
455 | |
456 | if (i == mid) |
457 | break; |
458 | else { // Triple move: |
459 | c = *i; // i goes over mid block |
460 | *i = *(--mid); // val under mid block -> i |
461 | *mid = *him--; // and mid block goes one |
462 | *j-- = c; // position lower. |
463 | } // endelse |
464 | |
465 | } // endfor i |
466 | |
467 | /*******************************************************************/ |
468 | /* The mid block being placed at its final position we can now set */ |
469 | /* the offset array values indicating break point and block size. */ |
470 | /*******************************************************************/ |
471 | j = mid; |
472 | i = him + 1; |
473 | |
474 | if (Pof) |
475 | Pof[him - Pex] = Pof[mid - Pex] = (int)(i - j); |
476 | |
477 | /*******************************************************************/ |
478 | /* Look at sizes of the two partitions, do the smaller one first */ |
479 | /* by recursion, then do the larger one by making sure lo is its */ |
480 | /* size, base and max are update correctly, and branching back. */ |
481 | /* But only repeat (recursively or by branching) if the partition */ |
482 | /* is of at least size THRESH. */ |
483 | /*******************************************************************/ |
484 | lo = (int)(j - base); |
485 | hi = (int)(max - i); |
486 | |
487 | if (Dup) { // Update progress information |
488 | zlo = Cmpnum(lo); |
489 | zhi = Cmpnum(hi); |
490 | Dup->ProgCur += cnm - (zlo + zhi); |
491 | } // endif Dup |
492 | |
493 | #ifdef DEBTRACE |
494 | htrc(" done lo=%d sep=%d hi=%d\n" , lo, i - j, hi); |
495 | #endif |
496 | |
497 | if (lo <= hi) { |
498 | if (lo >= Thresh) |
499 | Qstx(base, j); |
500 | else if (lo == 1 && Pof) |
501 | Pof[base - Pex] = 1; |
502 | |
503 | base = i; |
504 | lo = hi; |
505 | cnm = zhi; |
506 | } else { |
507 | if (hi >= Thresh) |
508 | Qstx(i, max); |
509 | else if (hi == 1 && Pof) |
510 | Pof[i - Pex] = 1; |
511 | |
512 | max = j; |
513 | cnm = zlo; |
514 | } // endif |
515 | |
516 | if (lo == 1 && Pof) |
517 | Pof[base - Pex] = 1; |
518 | |
519 | } while (lo >= Thresh); // enddo |
520 | |
521 | } // end of Qstx |
522 | |
523 | /***********************************************************************/ |
524 | /* Qsortc.c: Version adapted from qsort.c by O.Bertrand */ |
525 | /* This version is specialy adapted for Index sorting, meaning that */ |
526 | /* the data is not moved, but the Index only is sorted. */ |
527 | /* Index array elements are any 4-byte word (a pointer or a int int */ |
528 | /* array index), they are not interpreted except by the user provided */ |
529 | /* comparison routine which must works accordingly. */ |
530 | /* In addition, this program takes care of data in which there is a */ |
531 | /* high rate of repetitions. */ |
532 | /* NOTE: the sort algorithm used here is conservative. Equal and */ |
533 | /* greater than values are internally stored in additional work area. */ |
534 | /* The THRESHold below is the insertion sort threshold, and also the */ |
535 | /* threshold for continuing que quicksort partitioning. */ |
536 | /* The MTHREShold is where we stop finding a better median. */ |
537 | /* These two quantities should be adjusted dynamically depending upon */ |
538 | /* the repetition rate of the data. */ |
539 | /* Algorithm used: */ |
540 | /* First, set up some global parameters for Qstc to share. Then, */ |
541 | /* quicksort with Qstc(), and then a cleanup insertion sort ourselves.*/ |
542 | /* Sound simple? It's not... */ |
543 | /***********************************************************************/ |
544 | int CSORT::Qsortc(void) |
545 | { |
546 | register int c; |
547 | register int lo, hi, min; |
548 | register int i, j, k, m, rc = 0; |
549 | // To do: rc should be checked for being used uninitialized |
550 | int *max; |
551 | #ifdef DEBTRACE |
552 | int ncp; |
553 | |
554 | num_comp = 0; |
555 | #endif |
556 | |
557 | /*********************************************************************/ |
558 | /* Prepare the Offset array that will be updated during sorts. */ |
559 | /*********************************************************************/ |
560 | if (Pof) |
561 | for (Pof[Nitem] = Nitem, j = 0; j < Nitem; j++) |
562 | Pof[j] = 0; |
563 | else |
564 | j = Nitem + 1; |
565 | |
566 | /*********************************************************************/ |
567 | /* Sort on one or zero element is obvious. */ |
568 | /*********************************************************************/ |
569 | if (Nitem <= 1) |
570 | return Nitem; |
571 | |
572 | /*********************************************************************/ |
573 | /* Thresh seems to be good as (10 * n / rep). But for testing we */ |
574 | /* set it directly as one parameter of the Xset function call. */ |
575 | /* Note: this should be final as the rep parameter is no more used. */ |
576 | /*********************************************************************/ |
577 | max = Pex + Nitem; |
578 | |
579 | #ifdef DEBTRACE |
580 | htrc("Qsortc: nitem=%d thresh=%d mthresh=%d\n" , |
581 | Nitem, Thresh, Mthresh); |
582 | #endif |
583 | |
584 | /*********************************************************************/ |
585 | /* If applicable, do a rough preliminary conservative quick sort. */ |
586 | /*********************************************************************/ |
587 | if (Nitem >= Thresh) { |
588 | if (!(Swix = (int *)malloc(Nitem * sizeof(int)))) |
589 | return -1; |
590 | |
591 | Qstc(Pex, max); |
592 | |
593 | free(Swix); |
594 | Swix = NULL; |
595 | } // endif n |
596 | |
597 | #ifdef DEBTRACE |
598 | htrc(" after quick sort num_comp=%d\n" , num_comp); |
599 | ncp = num_comp; |
600 | num_comp = 0; |
601 | #ifdef DEBUG2 |
602 | DebugSort((Pof) ? 1 : 4, Nitem, Pex, NULL, NULL); |
603 | #endif |
604 | #endif |
605 | |
606 | if (Thresh > 2) { |
607 | if (Pof) |
608 | /*****************************************************************/ |
609 | /* The preliminary search for the smallest element has been */ |
610 | /* removed so with no sentinel in place, we must check for x */ |
611 | /* going below the Pof pointer. For each remaining element */ |
612 | /* group from [1] to [n-1], set hi to the index of the element */ |
613 | /* AFTER which this one goes. Then, do the standard insertion */ |
614 | /* sort shift on an integer at a time basis for each equal */ |
615 | /* element group in the frob. */ |
616 | /*****************************************************************/ |
617 | for (min = hi = 0; min < Nitem; min = hi) { |
618 | if (Pof[hi]) { |
619 | hi += Pof[hi]; |
620 | continue; |
621 | } // endif |
622 | |
623 | Pof[min] = 1; |
624 | |
625 | #ifdef DEBUG2 |
626 | htrc("insert from min=%d\n" , min); |
627 | #endif |
628 | |
629 | for (lo = hi; !Pof[++hi]; lo = hi) { |
630 | while (lo >= min && (rc = Qcompare(Pex + lo, Pex + hi)) > 0) |
631 | if (Pof[lo] > 0) |
632 | lo -= Pof[lo]; |
633 | else |
634 | return -2; |
635 | |
636 | if (++lo != hi) { |
637 | c = Pex[hi]; |
638 | |
639 | for (i = j = hi; i > 0; i = j) |
640 | if (Pof[i - 1] <= 0) |
641 | return -3; |
642 | else if ((j -= Pof[i - 1]) >= lo) { |
643 | for (k = m = i; --m >= j; k--) // Move intermediate |
644 | Pex[k] = Pex[m]; // for conservation. |
645 | |
646 | Pof[j + 1] = Pof[i] = Pof[j]; |
647 | } else |
648 | break; |
649 | |
650 | Pex[i] = c; |
651 | } // endif |
652 | |
653 | if (rc) |
654 | Pof[lo] = 1; |
655 | else { |
656 | i = lo - Pof[lo - 1]; |
657 | Pof[lo] = ++Pof[i]; |
658 | } // endelse |
659 | |
660 | #ifdef DEBUG2 |
661 | htrc("rc=%d lo=%d hi=%d ofx=%d\n" , rc, lo, hi, Pof[lo]); |
662 | #endif |
663 | |
664 | } // endfor hi |
665 | |
666 | } // endfor min |
667 | |
668 | else |
669 | /*****************************************************************/ |
670 | /* Call conservative insertion sort not using/setting offset. */ |
671 | /*****************************************************************/ |
672 | Istc(Pex, Pex + MY_MIN(Nitem, Thresh), max); |
673 | |
674 | } // endif Thresh |
675 | |
676 | #ifdef DEBTRACE |
677 | htrc(" after insert sort num_comp=%d\n" , num_comp); |
678 | num_comp += ncp; |
679 | #endif |
680 | |
681 | if (Pof) |
682 | /*******************************************************************/ |
683 | /* Reduce the Offset array. */ |
684 | /*******************************************************************/ |
685 | for (i = j = 0; i <= Nitem; j++, i += c) { |
686 | #ifdef DEBUG2 |
687 | htrc(" Pof(%d)=%d Pof(%d)=%d c=%d\n" , |
688 | i, Pof[i], j, Pof[j], c); |
689 | #endif |
690 | if ((c = Pof[i])) |
691 | Pof[j] = i; |
692 | else |
693 | return -4; |
694 | |
695 | } // endfor i |
696 | |
697 | return (j - 1); |
698 | } // end of Qsortc |
699 | |
700 | /***********************************************************************/ |
701 | /* Qstc: Do a quicksort on index elements (just one int int). */ |
702 | /* First, find the median element, and set it as the discriminator. */ |
703 | /* (This "median" is just the median of the first, last and middle */ |
704 | /* elements). (Using this median instead of the first element is a */ |
705 | /* big win). Then, the special partitioning/swapping, where elements */ |
706 | /* smaller than the discriminator are placed in the sorted block, */ |
707 | /* elements equal to the discriminator are placed backward from the */ |
708 | /* top of the work area and elements greater than *j (discriminator) */ |
709 | /* are placed in the work area from its bottom. Then the elements in */ |
710 | /* the work area are placed back in the sort area in natural order, */ |
711 | /* making the sort conservative. Non equal blocks shrink faster when */ |
712 | /* equal elements exist. This is a huge win in case of repartitions */ |
713 | /* with few different elements. The mid block being at its final */ |
714 | /* position, its first and last elements are marked in the offset */ |
715 | /* list (used to make break list). Then, figure out the sizes of the */ |
716 | /* two partitions, do the smaller one recursively and the larger one */ |
717 | /* via a repeat of this code. Stopping when there are less than */ |
718 | /* THRESH elements in a partition and cleaning up with an insertion */ |
719 | /* sort (in our caller) is a huge win (yet to be proved?). */ |
720 | /***********************************************************************/ |
721 | void CSORT::Qstc(int *base, int *max) |
722 | { |
723 | register int *i, *j, *jj, *lt, *eq, *gt, *mid; |
724 | int c = 0, lo, hi, rc; |
725 | size_t zlo, zhi, cnm; |
726 | |
727 | zlo = zhi = cnm = 0; // Avoid warning message |
728 | |
729 | lo = (int)(max - base); // Number of elements as longs |
730 | |
731 | if (Dup) |
732 | cnm = Cmpnum(lo); |
733 | |
734 | do { |
735 | /*******************************************************************/ |
736 | /* At the top here, lo is the number of integers of elements in */ |
737 | /* the current partition. (Which should be max - base). Find the */ |
738 | /* median of the first, last, and middle element and make that */ |
739 | /* the compare element. Set jj to smallest of middle and last. */ |
740 | /* If base is smaller or equal than that guy, then it's that guy, */ |
741 | /* else compare base with loser of first and take smaller. Things */ |
742 | /* are set up to prefer the top, then the middle in case of ties. */ |
743 | /*******************************************************************/ |
744 | i = base + (lo >> 1); |
745 | jj = mid = max - 1; |
746 | |
747 | #ifdef DEBTRACE |
748 | htrc("--> block base=%d size=%d\n" , base - Pex, lo); |
749 | DebugSort(2, 0, base, i, mid); |
750 | #endif |
751 | |
752 | if (lo >= Mthresh) { |
753 | jj = ((rc = Qcompare(i, mid)) < 0) ? i : mid; |
754 | |
755 | if (rc && Qcompare(base, jj) > 0) { |
756 | jj = (jj == mid) ? i : mid; // switch to first loser |
757 | |
758 | if (Qcompare(base, jj) < 0) |
759 | jj = base; |
760 | |
761 | } // endif |
762 | |
763 | if (jj != mid) { |
764 | /***************************************************************/ |
765 | /* The compare element must be at the top of the block so it */ |
766 | /* cannot be overwritten while making the partitioning. So */ |
767 | /* save the last block value which will be compared later. */ |
768 | /***************************************************************/ |
769 | c = *mid; |
770 | *mid = *jj; |
771 | } // endif |
772 | |
773 | } else if (lo == 2) { |
774 | /*****************************************************************/ |
775 | /* Small group. Do special quicker processing. */ |
776 | /*****************************************************************/ |
777 | if ((rc = Qcompare(base, (i = base + 1))) > 0) { |
778 | c = *base; |
779 | *base = *i; |
780 | *i = c; |
781 | } // endif rc |
782 | |
783 | if (Pof) |
784 | Pof[base - Pex] = Pof[i - Pex] = (rc) ? 1 : 2; |
785 | |
786 | break; |
787 | } // endif lo |
788 | |
789 | #ifdef DEBTRACE |
790 | DebugSort(3, lo, NULL, jj, &rc); |
791 | #endif |
792 | |
793 | /*******************************************************************/ |
794 | /* Non-standard quicksort partitioning using additional storage */ |
795 | /* to store values less than, equal or greater than the middle */ |
796 | /* element. This uses more memory but provides conservation of */ |
797 | /* the equal elements order. */ |
798 | /*******************************************************************/ |
799 | lt = base; |
800 | eq = Swix + lo; |
801 | gt = Swix; |
802 | |
803 | if (jj == mid) { |
804 | /*****************************************************************/ |
805 | /* Compare element was last. No problem. */ |
806 | /*****************************************************************/ |
807 | for (i = base; i < max; i++) |
808 | if ((rc = Qcompare(i, mid)) < 0) |
809 | *lt++ = *i; |
810 | else if (rc > 0) |
811 | *gt++ = *i; |
812 | else |
813 | *--eq = *i; |
814 | |
815 | } else { |
816 | /*****************************************************************/ |
817 | /* Compare element was not last and was copied to top of block. */ |
818 | /*****************************************************************/ |
819 | for (i = base; i < mid; i++) |
820 | if ((rc = Qcompare(i, mid)) < 0) |
821 | *lt++ = *i; |
822 | else if (rc > 0) |
823 | *gt++ = *i; |
824 | else |
825 | *--eq = *i; |
826 | |
827 | /*****************************************************************/ |
828 | /* Restore saved last value and do the comparison from there. */ |
829 | /*****************************************************************/ |
830 | *--i = c; |
831 | |
832 | if ((rc = Qcompare(i, mid)) < 0) |
833 | *lt++ = *i; |
834 | else if (rc > 0) |
835 | *gt++ = *i; |
836 | else |
837 | *--eq = *i; |
838 | |
839 | } // endif |
840 | |
841 | /*******************************************************************/ |
842 | /* Now copy the equal and greater values back in the main array in */ |
843 | /* the same order they have been placed in the work area. */ |
844 | /*******************************************************************/ |
845 | for (j = Swix + lo, i = lt; j > eq; ) |
846 | *i++ = *--j; |
847 | |
848 | for (j = Swix, jj = i; j < gt; ) |
849 | *i++ = *j++; |
850 | |
851 | /*******************************************************************/ |
852 | /* The mid block being placed at its final position we can now set */ |
853 | /* the offset array values indicating break point and block size. */ |
854 | /*******************************************************************/ |
855 | if (Pof) |
856 | Pof[lt - Pex] = Pof[(jj - 1) - Pex] = (int)(jj - lt); |
857 | |
858 | /*******************************************************************/ |
859 | /* Look at sizes of the two partitions, do the smaller one first */ |
860 | /* by recursion, then do the larger one by making sure lo is its */ |
861 | /* size, base and max are update correctly, and branching back. */ |
862 | /* But only repeat (recursively or by branching) if the partition */ |
863 | /* is of at least size THRESH. */ |
864 | /*******************************************************************/ |
865 | lo = (int)(lt - base); |
866 | hi = (int)(gt - Swix); |
867 | |
868 | if (Dup) { // Update progress information |
869 | zlo = Cmpnum(lo); |
870 | zhi = Cmpnum(hi); |
871 | Dup->ProgCur += cnm - (zlo + zhi); |
872 | } // endif Dup |
873 | |
874 | #ifdef DEBTRACE |
875 | htrc(" done lo=%d hi=%d\n" , |
876 | lo, /*Swix + lt - base - eq,*/ hi); |
877 | #endif |
878 | |
879 | if (lo <= hi) { |
880 | if (lo >= Thresh) |
881 | Qstc(base, lt); |
882 | else if (lo == 1 && Pof) |
883 | Pof[base - Pex] = 1; |
884 | |
885 | base = jj; |
886 | lo = hi; |
887 | cnm = zhi; |
888 | } else { |
889 | if (hi >= Thresh) |
890 | Qstc(jj, max); |
891 | else if (hi == 1 && Pof) |
892 | Pof[jj - Pex] = 1; |
893 | |
894 | max = lt; |
895 | cnm = zlo; |
896 | } // endif |
897 | |
898 | if (lo == 1 && Pof) |
899 | Pof[base - Pex] = 1; |
900 | |
901 | } while (lo >= Thresh); // enddo |
902 | |
903 | } // end of Qstc |
904 | |
905 | /***********************************************************************/ |
906 | /* Conservative insertion sort not using/setting offset array. */ |
907 | /***********************************************************************/ |
908 | void CSORT::Istc(int *base, int *hi, int *max) |
909 | { |
910 | register int c = 0; |
911 | register int *lo; |
912 | register int *i, *j; |
913 | |
914 | /*********************************************************************/ |
915 | /* First put smallest element, which must be in the first THRESH, */ |
916 | /* in the first position as a sentinel. This is done just by */ |
917 | /* searching the 1st THRESH elements (or the 1st n if n < THRESH) */ |
918 | /* finding the min, and shifting it into the first position. */ |
919 | /*********************************************************************/ |
920 | for (j = lo = base; ++lo < hi; ) |
921 | if (Qcompare(j, lo) > 0) |
922 | j = lo; |
923 | |
924 | if (j != base) { // shift j into place |
925 | c = *j; |
926 | |
927 | for (i = j; --j >= base; i = j) |
928 | *i = *j; |
929 | |
930 | *base = c; |
931 | } // endif j |
932 | |
933 | #ifdef DEBTRACE |
934 | htrc("sentinel %d in place, base=%p hi=%p max=%p\n" , |
935 | c, base, hi, max); |
936 | #endif |
937 | |
938 | /*********************************************************************/ |
939 | /* With our sentinel in place, we now run the following hyper- */ |
940 | /* fast insertion sort. For each remaining element, lo, from [1] */ |
941 | /* to [n-1], set hi to the index of the element AFTER which this */ |
942 | /* one goes. Then, do the standard insertion sort shift for each */ |
943 | /* element in the frob. */ |
944 | /*********************************************************************/ |
945 | for (lo = base; (hi = ++lo) < max;) { |
946 | while (Qcompare(--hi, lo) > 0) ; |
947 | |
948 | #ifdef DEBUG2 |
949 | htrc("after while: hi(%p)=%d lo(%p)=%d\n" , |
950 | hi, *hi, lo, *lo); |
951 | #endif |
952 | |
953 | if (++hi != lo) { |
954 | c = *lo; |
955 | |
956 | for (i = j = lo; --j >= hi; i = j) |
957 | *i = *j; |
958 | |
959 | *i = c; |
960 | } // endif hi |
961 | |
962 | } // endfor lo |
963 | |
964 | } // end of Istc |
965 | |
966 | /* -------------------------- End of CSort --------------------------- */ |
967 | |