summaryrefslogtreecommitdiffstats
path: root/storage/connect/csort.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
commita175314c3e5827eb193872241446f2f8f5c9d33c (patch)
treecd3d60ca99ae00829c52a6ca79150a5b6e62528b /storage/connect/csort.cpp
parentInitial commit. (diff)
downloadmariadb-10.5-upstream/1%10.5.12.tar.xz
mariadb-10.5-upstream/1%10.5.12.zip
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/connect/csort.cpp')
-rw-r--r--storage/connect/csort.cpp966
1 files changed, 966 insertions, 0 deletions
diff --git a/storage/connect/csort.cpp b/storage/connect/csort.cpp
new file mode 100644
index 00000000..1e4ba674
--- /dev/null
+++ b/storage/connect/csort.cpp
@@ -0,0 +1,966 @@
+/*************** CSort C Program Source Code File (.CPP) ***************/
+/* PROGRAM NAME: CSORT */
+/* ------------- */
+/* Version 2.2 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier Bertrand 1995-2016 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program is the C++ sorting routines that use qsort/insert */
+/* algorithm and produces an offset/break table while sorting. */
+/* */
+/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
+/* -------------------------------------- */
+/* */
+/* REQUIRED FILES: */
+/* --------------- */
+/* csort.cpp - Source code */
+/* */
+/* REQUIRED LIBRARIES: */
+/* ------------------- */
+/* OS2DEF.LIB - OS2 libray definition subset. */
+/* */
+/* REQUIRED PROGRAMS: */
+/* ------------------ */
+/* Microsoft C++ Compiler */
+/* or GNU Compiler/Linker */
+/* or BORLAND 4.5 C++ compiler */
+/* */
+/* NOTE */
+/* ---- */
+/* These functions are not 64-bits ready. */
+/* */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#include "my_global.h"
+
+/***********************************************************************/
+/* Include application header files */
+/***********************************************************************/
+#include <stdlib.h> /* C standard library */
+#include <string.h> /* String manipulation declares */
+#include <stdio.h> /* Required for sprintf declare */
+#if defined(_DEBUG)
+#include <assert.h> /* Assertion routine declares */
+#endif
+
+/***********************************************************************/
+/* Include CSort class header file */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h" /* For MBLOCK type definition */
+#include "csort.h" /* CSort class definition */
+#include "osutil.h"
+
+#if !defined(BIGSORT)
+#define BIGSORT 200000
+#endif // !BIGSORT
+
+/***********************************************************************/
+/* DB static external variables. */
+/***********************************************************************/
+extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
+
+/***********************************************************************/
+/* Initialize the CSORT static members. */
+/***********************************************************************/
+int CSORT::Limit = 0;
+double CSORT::Lg2 = log(2.0);
+size_t CSORT::Cpn[1000] = {0}; /* Precalculated cmpnum values */
+
+/***********************************************************************/
+/* CSORT constructor. */
+/***********************************************************************/
+CSORT::CSORT(bool cns, int th, int mth)
+ : Pex((int*&)Index.Memp), Pof((int*&)Offset.Memp)
+ {
+ G = NULL;
+ Dup =NULL;
+ Cons = cns;
+ Thresh = th;
+ Mthresh = mth;
+ Nitem = 0;
+ Index = Nmblk;
+ Offset = Nmblk;
+ Swix = NULL;
+ Savmax = 0;
+ Savcur = 0;
+ Savstep = NULL;
+ } // end of CSORT constructor
+
+/***********************************************************************/
+/* CSORT intialization. */
+/***********************************************************************/
+int CSORT::Qsort(PGLOBAL g, int nb)
+ {
+ int rc;
+
+#if defined(_DEBUG)
+ assert(Index.Size >= nb * sizeof(int));
+#endif
+
+ if (nb > BIGSORT) {
+ G = g;
+ Dup = (PDBUSER)g->Activityp->Aptr;
+
+ if (Dup->Proginfo) {
+ Savstep = Dup->Step;
+ Savmax = Dup->ProgMax;
+ Savcur = Dup->ProgCur;
+
+ // Evaluate the number of comparisons that we will do
+ Dup->ProgMax = Cmpnum(nb);
+ Dup->ProgCur = 0;
+ Dup->Step = (char*)PlugSubAlloc(g, NULL, 32);
+ sprintf((char*)Dup->Step, MSG(SORTING_VAL), nb);
+ } else
+ Dup = NULL;
+
+ } else
+ Dup = NULL;
+
+ Nitem = nb;
+
+ for (int n = 0; n < Nitem; n++)
+ Pex[n] = n;
+
+ rc = (Cons) ? Qsortc() : Qsortx();
+
+ if (Dup) {
+ // Restore any change in progress info settings
+// printf("Progcur=%u\n", Dup->ProgCur);
+
+ Dup->Step = Savstep;
+ Dup->ProgMax = Savmax;
+ Dup->ProgCur = Savcur;
+ } // endif Subcor
+
+ return rc;
+ } // end of QSort
+
+#if defined(DEBTRACE)
+/***********************************************************************/
+/* Debug routine to be used by sort for specific data (dummy as now) */
+/***********************************************************************/
+void CSORT::DebugSort(int ph, int n, int *base, int *mid, int *tmp)
+ {
+ htrc("phase=%d n=%d base=%p mid=%p tmp=%p\n",
+ ph, n, base, mid, tmp);
+ } // end of DebugSort
+#endif
+
+/***********************************************************************/
+/* Qsortx: Version adapted from qsortx.c by O.Bertrand */
+/* This version is specialy adapted for Index sorting, meaning that */
+/* the data is not moved, but the Index only is sorted. */
+/* Index array elements are any 4-byte word (a pointer or a int int */
+/* array index), they are not interpreted except by the user provided */
+/* comparison routine which must works accordingly. */
+/* In addition, this program takes care of data in which there is a */
+/* high rate of repetitions. */
+/* CAUTION: the sort algorithm used here is not conservative. Equal */
+/* values will be internally stored in unpredictable order. */
+/* The THRESHold below is the insertion sort threshold, and also the */
+/* threshold for continuing que quicksort partitioning. */
+/* The MTHREShold is where we stop finding a better median. */
+/* These two quantities should be adjusted dynamically depending upon */
+/* the repetition rate of the data. */
+/* Algorithm used: */
+/* First, set up some global parameters for Qstx to share. Then, */
+/* quicksort with Qstx(), and then a cleanup insertion sort ourselves. */
+/* Sound simple? It's not... */
+/***********************************************************************/
+int CSORT::Qsortx(void)
+ {
+ int c;
+ int lo, hi, min;
+ int i, j, rc = 0;
+ // To do: rc should be checked for being used uninitialized
+ int *top;
+#ifdef DEBTRACE
+ int ncp;
+
+ num_comp = 0;
+#endif
+
+ /*********************************************************************/
+ /* Prepare the Offset array that will be updated during sorts. */
+ /*********************************************************************/
+ if (Pof)
+ for (Pof[Nitem] = Nitem, j = 0; j < Nitem; j++)
+ Pof[j] = 0;
+ else
+ j = Nitem + 1;
+
+ /*********************************************************************/
+ /* Sort on one or zero element is obvious. */
+ /*********************************************************************/
+ if (Nitem <= 1)
+ return Nitem;
+
+ /*********************************************************************/
+ /* Thresh seems to be good as (10 * n / rep). But for testing we */
+ /* set it directly as one parameter of the Xset function call. */
+ /* Note: this should be final as the rep parameter is no more used. */
+ /*********************************************************************/
+ top = Pex + Nitem;
+
+#ifdef DEBTRACE
+ htrc("Qsortx: nitem=%d thresh=%d mthresh=%d\n",
+ Nitem, Thresh, Mthresh);
+#endif
+
+ /*********************************************************************/
+ /* If applicable, do a rough preliminary quick sort. */
+ /*********************************************************************/
+ if (Nitem >= Thresh)
+ Qstx(Pex, top);
+
+#ifdef DEBTRACE
+ htrc(" after quick sort num_comp=%d\n", num_comp);
+ ncp = num_comp;
+ num_comp = 0;
+#ifdef DEBUG2
+ DebugSort((Pof) ? 1 : 4, Nitem, Pex, NULL, NULL);
+#endif
+#endif
+
+ if (Thresh > 2) {
+ if (Pof)
+ /*****************************************************************/
+ /* The preliminary search for the smallest element has been */
+ /* removed so with no sentinel in place, we must check for x */
+ /* going below the Pof pointer. For each remaining element */
+ /* group 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 an integer at a time basis for each equal */
+ /* element group in the frob. */
+ /*****************************************************************/
+ for (min = hi = 0; min < Nitem; min = hi) {
+ if (Pof[hi]) {
+ hi += Pof[hi];
+ continue;
+ } // endif Pof
+
+ Pof[min] = 1;
+
+#ifdef DEBUG2
+ htrc("insert from min=%d\n", min);
+#endif
+
+ for (lo = hi; !Pof[++hi]; lo = hi) {
+ while (lo >= min && (rc = Qcompare(Pex + lo, Pex + hi)) > 0)
+ if (Pof[lo] > 0)
+ lo -= Pof[lo];
+ else
+ return -2;
+
+ if (++lo != hi) {
+ c = Pex[hi];
+
+ for (i = j = hi; i > 0; i = j)
+ if (Pof[i - 1] <= 0)
+ return -3;
+ else if ((j -= Pof[i - 1]) >= lo) {
+ Pex[i] = Pex[j];
+ Pof[j + 1] = Pof[i] = Pof[j];
+ } else
+ break;
+
+ Pex[i] = c;
+ } // endif lo
+
+ if (rc)
+ Pof[lo] = 1;
+ else {
+ i = lo - Pof[lo - 1];
+ Pof[lo] = ++Pof[i];
+ } // endelse
+
+#ifdef DEBUG2
+ htrc("rc=%d lo=%d hi=%d trx=%d\n", rc, lo, hi, Pof[lo]);
+#endif
+
+ } // endfor hi
+
+ } // endfor min
+
+ else
+ /*****************************************************************/
+ /* Call conservative insertion sort not using/setting offset. */
+ /*****************************************************************/
+ Istc(Pex, Pex + MY_MIN(Nitem, Thresh), top);
+
+ } // endif Thresh
+
+#ifdef DEBTRACE
+ htrc(" after insert sort num_comp=%d\n", num_comp);
+ num_comp += ncp;
+#endif
+
+ if (Pof)
+ /*******************************************************************/
+ /* Reduce the Offset array. */
+ /*******************************************************************/
+ for (i = j = 0; i <= Nitem; j++, i += c) {
+#ifdef DEBUG2
+ htrc(" trxp(%d)=%d trxp(%d)=%d c=%d\n",
+ i, Pof[i], j, Pof[j], c);
+#endif
+ if ((c = Pof[i]))
+ Pof[j] = i;
+ else
+ return -4;
+
+ } // endfor i
+
+ return (j - 1);
+ } // end of Qsortx
+
+/***********************************************************************/
+/* Qstx: Do a quicksort on index elements (just one int int). */
+/* 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.*/
+/* Element equal to the discriminator are placed against it, so the */
+/* mid (discriminator) block grows when equal elements exist. This is */
+/* a huge win in case of repartitions with few different elements. */
+/* The mid block being at its final position, its first and last */
+/* elements are marked in the offset list (used to make break list). */
+/* Then, figure out the sizes of the two partitions, 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 CSORT::Qstx(int *base, int *max)
+ {
+ int *i, *j, *jj, *mid, *him, c;
+ int *tmp;
+ int lo, hi, rc;
+ size_t zlo, zhi, cnm;
+
+ zlo = zhi = cnm = 0; // Avoid warning message
+
+ lo = (int)(max - base); // Number of elements as longs
+
+ if (Dup)
+ cnm = Cmpnum(lo);
+
+ do {
+ /*******************************************************************/
+ /* At the top here, lo is the number of integers 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. */
+ /* In addition, hi and rc are set to comparison results. So if hi */
+ /* is null, the two high values are equal and if rc is null, the */
+ /* two low values are equal. This was used to set which test will */
+ /* be made by LE and which one by LT (does not apply anymore). */
+ /*******************************************************************/
+ him = mid = i = base + (lo >> 1);
+ hi = rc = 0;
+
+#ifdef DEBTRACE
+ tmp = max - 1;
+ htrc("--> block base=%d size=%d\n", base - Pex, lo);
+ DebugSort(2, 0, base, mid, tmp);
+#endif
+
+ if (lo >= Mthresh) {
+ rc = Qcompare((jj = base), i);
+ j = (rc > 0) ? jj : i;
+ hi = Qcompare(j, (tmp = max - 1));
+
+ if (hi > 0 && rc) {
+ j = (j == jj) ? i : jj; // switch to first loser
+
+ if ((rc = Qcompare(j, tmp)) < 0)
+ j = tmp;
+
+ } // endif
+
+ if (j != i) {
+ c = *i;
+ *i = *j;
+ *j = c;
+ } // endif j
+
+ } else if (lo == 2) {
+ /*****************************************************************/
+ /* Small group. Do special quicker processing. */
+ /*****************************************************************/
+ if ((rc = Qcompare(base, (him = base + 1))) > 0)
+ c = *base, *base = *him, *him = c;
+
+ if (Pof)
+ Pof[base - Pex] = Pof[him - Pex] = (rc) ? 1 : 2;
+
+ break;
+ } // endif lo
+
+#ifdef DEBTRACE
+ DebugSort(3, hi, NULL, mid, &rc);
+#endif
+
+ /*******************************************************************/
+ /* Semi-standard quicksort partitioning/swapping. Added here is */
+ /* a test on equality. All values equal to the mid element are */
+ /* placed under or over it. Mid block can be also moved when it */
+ /* is necessary because the other partition is full. At the end */
+ /* of the for loop the mid block is definitely positionned. */
+ /*******************************************************************/
+ for (i = base, j = max - 1; ;) {
+ CONT:
+ while (i < mid)
+ if ((rc = Qcompare(i, mid)) < 0)
+ i++;
+ else if (!rc) {
+ c = *i;
+ *i = *(--mid);
+ *mid = c;
+ } else
+ break;
+
+ while (j > him)
+ if ((rc = Qcompare(him, j)) < 0)
+ j--;
+ else if (!rc) {
+ c = *j;
+ *j = *(++him);
+ *him = c;
+ } else if (i == mid) { // Triple move:
+ c = *j; // j goes under mid block
+ *j = *(++him); // val over mid block -> j
+ *him = *mid++; // and mid block goes one
+ *i++ = c; // position higher.
+ } else { // i <-> j
+ c = *i;
+ *i++ = *j;
+ *j-- = c;
+ goto CONT;
+ } // endif's
+
+ if (i == mid)
+ break;
+ else { // Triple move:
+ c = *i; // i goes over mid block
+ *i = *(--mid); // val under mid block -> i
+ *mid = *him--; // and mid block goes one
+ *j-- = c; // position lower.
+ } // endelse
+
+ } // endfor i
+
+ /*******************************************************************/
+ /* The mid block being placed at its final position we can now set */
+ /* the offset array values indicating break point and block size. */
+ /*******************************************************************/
+ j = mid;
+ i = him + 1;
+
+ if (Pof)
+ Pof[him - Pex] = Pof[mid - Pex] = (int)(i - j);
+
+ /*******************************************************************/
+ /* 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. */
+ /*******************************************************************/
+ lo = (int)(j - base);
+ hi = (int)(max - i);
+
+ if (Dup) { // Update progress information
+ zlo = Cmpnum(lo);
+ zhi = Cmpnum(hi);
+ Dup->ProgCur += cnm - (zlo + zhi);
+ } // endif Dup
+
+#ifdef DEBTRACE
+ htrc(" done lo=%d sep=%d hi=%d\n", lo, i - j, hi);
+#endif
+
+ if (lo <= hi) {
+ if (lo >= Thresh)
+ Qstx(base, j);
+ else if (lo == 1 && Pof)
+ Pof[base - Pex] = 1;
+
+ base = i;
+ lo = hi;
+ cnm = zhi;
+ } else {
+ if (hi >= Thresh)
+ Qstx(i, max);
+ else if (hi == 1 && Pof)
+ Pof[i - Pex] = 1;
+
+ max = j;
+ cnm = zlo;
+ } // endif
+
+ if (lo == 1 && Pof)
+ Pof[base - Pex] = 1;
+
+ } while (lo >= Thresh); // enddo
+
+ } // end of Qstx
+
+/***********************************************************************/
+/* Qsortc.c: Version adapted from qsort.c by O.Bertrand */
+/* This version is specialy adapted for Index sorting, meaning that */
+/* the data is not moved, but the Index only is sorted. */
+/* Index array elements are any 4-byte word (a pointer or a int int */
+/* array index), they are not interpreted except by the user provided */
+/* comparison routine which must works accordingly. */
+/* In addition, this program takes care of data in which there is a */
+/* high rate of repetitions. */
+/* NOTE: the sort algorithm used here is conservative. Equal and */
+/* greater than values are internally stored in additional work area. */
+/* The THRESHold below is the insertion sort threshold, and also the */
+/* threshold for continuing que quicksort partitioning. */
+/* The MTHREShold is where we stop finding a better median. */
+/* These two quantities should be adjusted dynamically depending upon */
+/* the repetition rate of the data. */
+/* Algorithm used: */
+/* First, set up some global parameters for Qstc to share. Then, */
+/* quicksort with Qstc(), and then a cleanup insertion sort ourselves.*/
+/* Sound simple? It's not... */
+/***********************************************************************/
+int CSORT::Qsortc(void)
+ {
+ int c;
+ int lo, hi, min;
+ int i, j, k, m, rc = 0;
+ // To do: rc should be checked for being used uninitialized
+ int *max;
+#ifdef DEBTRACE
+ int ncp;
+
+ num_comp = 0;
+#endif
+
+ /*********************************************************************/
+ /* Prepare the Offset array that will be updated during sorts. */
+ /*********************************************************************/
+ if (Pof)
+ for (Pof[Nitem] = Nitem, j = 0; j < Nitem; j++)
+ Pof[j] = 0;
+ else
+ j = Nitem + 1;
+
+ /*********************************************************************/
+ /* Sort on one or zero element is obvious. */
+ /*********************************************************************/
+ if (Nitem <= 1)
+ return Nitem;
+
+ /*********************************************************************/
+ /* Thresh seems to be good as (10 * n / rep). But for testing we */
+ /* set it directly as one parameter of the Xset function call. */
+ /* Note: this should be final as the rep parameter is no more used. */
+ /*********************************************************************/
+ max = Pex + Nitem;
+
+#ifdef DEBTRACE
+ htrc("Qsortc: nitem=%d thresh=%d mthresh=%d\n",
+ Nitem, Thresh, Mthresh);
+#endif
+
+ /*********************************************************************/
+ /* If applicable, do a rough preliminary conservative quick sort. */
+ /*********************************************************************/
+ if (Nitem >= Thresh) {
+ if (!(Swix = (int *)malloc(Nitem * sizeof(int))))
+ return -1;
+
+ Qstc(Pex, max);
+
+ free(Swix);
+ Swix = NULL;
+ } // endif n
+
+#ifdef DEBTRACE
+ htrc(" after quick sort num_comp=%d\n", num_comp);
+ ncp = num_comp;
+ num_comp = 0;
+#ifdef DEBUG2
+ DebugSort((Pof) ? 1 : 4, Nitem, Pex, NULL, NULL);
+#endif
+#endif
+
+ if (Thresh > 2) {
+ if (Pof)
+ /*****************************************************************/
+ /* The preliminary search for the smallest element has been */
+ /* removed so with no sentinel in place, we must check for x */
+ /* going below the Pof pointer. For each remaining element */
+ /* group 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 an integer at a time basis for each equal */
+ /* element group in the frob. */
+ /*****************************************************************/
+ for (min = hi = 0; min < Nitem; min = hi) {
+ if (Pof[hi]) {
+ hi += Pof[hi];
+ continue;
+ } // endif
+
+ Pof[min] = 1;
+
+#ifdef DEBUG2
+ htrc("insert from min=%d\n", min);
+#endif
+
+ for (lo = hi; !Pof[++hi]; lo = hi) {
+ while (lo >= min && (rc = Qcompare(Pex + lo, Pex + hi)) > 0)
+ if (Pof[lo] > 0)
+ lo -= Pof[lo];
+ else
+ return -2;
+
+ if (++lo != hi) {
+ c = Pex[hi];
+
+ for (i = j = hi; i > 0; i = j)
+ if (Pof[i - 1] <= 0)
+ return -3;
+ else if ((j -= Pof[i - 1]) >= lo) {
+ for (k = m = i; --m >= j; k--) // Move intermediate
+ Pex[k] = Pex[m]; // for conservation.
+
+ Pof[j + 1] = Pof[i] = Pof[j];
+ } else
+ break;
+
+ Pex[i] = c;
+ } // endif
+
+ if (rc)
+ Pof[lo] = 1;
+ else {
+ i = lo - Pof[lo - 1];
+ Pof[lo] = ++Pof[i];
+ } // endelse
+
+#ifdef DEBUG2
+ htrc("rc=%d lo=%d hi=%d ofx=%d\n", rc, lo, hi, Pof[lo]);
+#endif
+
+ } // endfor hi
+
+ } // endfor min
+
+ else
+ /*****************************************************************/
+ /* Call conservative insertion sort not using/setting offset. */
+ /*****************************************************************/
+ Istc(Pex, Pex + MY_MIN(Nitem, Thresh), max);
+
+ } // endif Thresh
+
+#ifdef DEBTRACE
+ htrc(" after insert sort num_comp=%d\n", num_comp);
+ num_comp += ncp;
+#endif
+
+ if (Pof)
+ /*******************************************************************/
+ /* Reduce the Offset array. */
+ /*******************************************************************/
+ for (i = j = 0; i <= Nitem; j++, i += c) {
+#ifdef DEBUG2
+ htrc(" Pof(%d)=%d Pof(%d)=%d c=%d\n",
+ i, Pof[i], j, Pof[j], c);
+#endif
+ if ((c = Pof[i]))
+ Pof[j] = i;
+ else
+ return -4;
+
+ } // endfor i
+
+ return (j - 1);
+ } // end of Qsortc
+
+/***********************************************************************/
+/* Qstc: Do a quicksort on index elements (just one int int). */
+/* First, find the median element, and set it 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 special partitioning/swapping, where elements */
+/* smaller than the discriminator are placed in the sorted block, */
+/* elements equal to the discriminator are placed backward from the */
+/* top of the work area and elements greater than *j (discriminator) */
+/* are placed in the work area from its bottom. Then the elements in */
+/* the work area are placed back in the sort area in natural order, */
+/* making the sort conservative. Non equal blocks shrink faster when */
+/* equal elements exist. This is a huge win in case of repartitions */
+/* with few different elements. The mid block being at its final */
+/* position, its first and last elements are marked in the offset */
+/* list (used to make break list). Then, figure out the sizes of the */
+/* two partitions, 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 (yet to be proved?). */
+/***********************************************************************/
+void CSORT::Qstc(int *base, int *max)
+ {
+ int *i, *j, *jj, *lt, *eq, *gt, *mid;
+ int c = 0, lo, hi, rc;
+ size_t zlo, zhi, cnm;
+
+ zlo = zhi = cnm = 0; // Avoid warning message
+
+ lo = (int)(max - base); // Number of elements as longs
+
+ if (Dup)
+ cnm = Cmpnum(lo);
+
+ do {
+ /*******************************************************************/
+ /* At the top here, lo is the number of integers 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 compare element. Set jj to smallest of middle and last. */
+ /* If base is smaller or equal than that guy, then it's that guy, */
+ /* else compare base with loser of first and take smaller. Things */
+ /* are set up to prefer the top, then the middle in case of ties. */
+ /*******************************************************************/
+ i = base + (lo >> 1);
+ jj = mid = max - 1;
+
+#ifdef DEBTRACE
+ htrc("--> block base=%d size=%d\n", base - Pex, lo);
+ DebugSort(2, 0, base, i, mid);
+#endif
+
+ if (lo >= Mthresh) {
+ jj = ((rc = Qcompare(i, mid)) < 0) ? i : mid;
+
+ if (rc && Qcompare(base, jj) > 0) {
+ jj = (jj == mid) ? i : mid; // switch to first loser
+
+ if (Qcompare(base, jj) < 0)
+ jj = base;
+
+ } // endif
+
+ if (jj != mid) {
+ /***************************************************************/
+ /* The compare element must be at the top of the block so it */
+ /* cannot be overwritten while making the partitioning. So */
+ /* save the last block value which will be compared later. */
+ /***************************************************************/
+ c = *mid;
+ *mid = *jj;
+ } // endif
+
+ } else if (lo == 2) {
+ /*****************************************************************/
+ /* Small group. Do special quicker processing. */
+ /*****************************************************************/
+ if ((rc = Qcompare(base, (i = base + 1))) > 0) {
+ c = *base;
+ *base = *i;
+ *i = c;
+ } // endif rc
+
+ if (Pof)
+ Pof[base - Pex] = Pof[i - Pex] = (rc) ? 1 : 2;
+
+ break;
+ } // endif lo
+
+#ifdef DEBTRACE
+ DebugSort(3, lo, NULL, jj, &rc);
+#endif
+
+ /*******************************************************************/
+ /* Non-standard quicksort partitioning using additional storage */
+ /* to store values less than, equal or greater than the middle */
+ /* element. This uses more memory but provides conservation of */
+ /* the equal elements order. */
+ /*******************************************************************/
+ lt = base;
+ eq = Swix + lo;
+ gt = Swix;
+
+ if (jj == mid) {
+ /*****************************************************************/
+ /* Compare element was last. No problem. */
+ /*****************************************************************/
+ for (i = base; i < max; i++)
+ if ((rc = Qcompare(i, mid)) < 0)
+ *lt++ = *i;
+ else if (rc > 0)
+ *gt++ = *i;
+ else
+ *--eq = *i;
+
+ } else {
+ /*****************************************************************/
+ /* Compare element was not last and was copied to top of block. */
+ /*****************************************************************/
+ for (i = base; i < mid; i++)
+ if ((rc = Qcompare(i, mid)) < 0)
+ *lt++ = *i;
+ else if (rc > 0)
+ *gt++ = *i;
+ else
+ *--eq = *i;
+
+ /*****************************************************************/
+ /* Restore saved last value and do the comparison from there. */
+ /*****************************************************************/
+ *--i = c;
+
+ if ((rc = Qcompare(i, mid)) < 0)
+ *lt++ = *i;
+ else if (rc > 0)
+ *gt++ = *i;
+ else
+ *--eq = *i;
+
+ } // endif
+
+ /*******************************************************************/
+ /* Now copy the equal and greater values back in the main array in */
+ /* the same order they have been placed in the work area. */
+ /*******************************************************************/
+ for (j = Swix + lo, i = lt; j > eq; )
+ *i++ = *--j;
+
+ for (j = Swix, jj = i; j < gt; )
+ *i++ = *j++;
+
+ /*******************************************************************/
+ /* The mid block being placed at its final position we can now set */
+ /* the offset array values indicating break point and block size. */
+ /*******************************************************************/
+ if (Pof)
+ Pof[lt - Pex] = Pof[(jj - 1) - Pex] = (int)(jj - lt);
+
+ /*******************************************************************/
+ /* 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. */
+ /*******************************************************************/
+ lo = (int)(lt - base);
+ hi = (int)(gt - Swix);
+
+ if (Dup) { // Update progress information
+ zlo = Cmpnum(lo);
+ zhi = Cmpnum(hi);
+ Dup->ProgCur += cnm - (zlo + zhi);
+ } // endif Dup
+
+#ifdef DEBTRACE
+ htrc(" done lo=%d hi=%d\n",
+ lo, /*Swix + lt - base - eq,*/ hi);
+#endif
+
+ if (lo <= hi) {
+ if (lo >= Thresh)
+ Qstc(base, lt);
+ else if (lo == 1 && Pof)
+ Pof[base - Pex] = 1;
+
+ base = jj;
+ lo = hi;
+ cnm = zhi;
+ } else {
+ if (hi >= Thresh)
+ Qstc(jj, max);
+ else if (hi == 1 && Pof)
+ Pof[jj - Pex] = 1;
+
+ max = lt;
+ cnm = zlo;
+ } // endif
+
+ if (lo == 1 && Pof)
+ Pof[base - Pex] = 1;
+
+ } while (lo >= Thresh); // enddo
+
+ } // end of Qstc
+
+/***********************************************************************/
+/* Conservative insertion sort not using/setting offset array. */
+/***********************************************************************/
+void CSORT::Istc(int *base, int *hi, int *max)
+ {
+ int c = 0;
+ int *lo;
+ int *i, *j;
+
+ /*********************************************************************/
+ /* 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 1st THRESH elements (or the 1st n if n < THRESH) */
+ /* finding the min, and shifting it into the first position. */
+ /*********************************************************************/
+ for (j = lo = base; ++lo < hi; )
+ if (Qcompare(j, lo) > 0)
+ j = lo;
+
+ if (j != base) { // shift j into place
+ c = *j;
+
+ for (i = j; --j >= base; i = j)
+ *i = *j;
+
+ *base = c;
+ } // endif j
+
+#ifdef DEBTRACE
+ htrc("sentinel %d in place, base=%p hi=%p max=%p\n",
+ c, base, hi, max);
+#endif
+
+ /*********************************************************************/
+ /* With our sentinel in place, we now run the following hyper- */
+ /* fast insertion sort. For each remaining element, lo, 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 for each */
+ /* element in the frob. */
+ /*********************************************************************/
+ for (lo = base; (hi = ++lo) < max;) {
+ while (Qcompare(--hi, lo) > 0) ;
+
+#ifdef DEBUG2
+ htrc("after while: hi(%p)=%d lo(%p)=%d\n",
+ hi, *hi, lo, *lo);
+#endif
+
+ if (++hi != lo) {
+ c = *lo;
+
+ for (i = j = lo; --j >= hi; i = j)
+ *i = *j;
+
+ *i = c;
+ } // endif hi
+
+ } // endfor lo
+
+ } // end of Istc
+
+/* -------------------------- End of CSort --------------------------- */