From: Bruno Luong on
There are few bugs in my previous indexing sorting code. Here is a new one. The default quicksort is out: none of the pivoting strategy I tested is completely satisfied.

Bruno

/**************************************************************************
* MATLAB mex function sort_idx.c
* Calling syntax:
* >> I = sort_idx(A)
* returns I such that A(I) is sorted
*
* Work in still in progress...
*
* Compile on 32-bit platform
* mex -O -v sort_idx.c
* On 64-bit platform
* mex -v -O -largeArrayDims sort_idx.c
*
* Author Bruno Luong <brunoluong@?????.com>
* Date: 10-Jan-2010
*************************************************************************/

#include "mex.h"
#include "matrix.h"
#include "string.h"
#include "math.h"

/* Define correct type depending on platform */
#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef unsigned __int32 uint32;
typedef signed __int32 int32;
typedef unsigned __int64 ulong64;
typedef signed __int64 long64;
#define INTMIN64 0x8000000000000000
#else
typedef unsigned int uint32;
typedef signed int int32;
typedef unsigned long long ulong64;
typedef signed long long long64;
#define INTMIN64 0x8000000000000000ULL
#endif

/* Different options for Pivot selection */
/* Different options for Pivot selection */
#define MIDPOINT 0
#define MEDIAN3 1
#define MEDIANMEDIANS 2

/* Pivot Strategy, use one of the above */
#define PIVOT MEDIAN3

#define INSERTSORT 0
#define SHELLSORT 1
#define QUICKSORT 2
#define HEAPSORT 3
#define INTROSORT 4

/* Method for sorting small arrays */
/* INSERTSORT | SHELLSORT */
#define BASESORT INSERTSORT

/* Method dor sorting large arrays */
/* QUICKSORT | HEAPSORT | INTROSORT */
#define MAINSORT INTROSORT

/* Increment for shellshort */
#define SizeIncs 3
mwIndex incs[SizeIncs] = { 7, 3, 1 };

/* A limit below the recusion stops, we which we switch to InsertionSort */
/* This must be smaller than */
#define SWITCH_ALGO_THRESHOLD 12

/* TYPE */
/* Type of elements to be sorted */
#define ARRAYTYPE double

/* Global variables, used to avoid stacking them during recusive call since
they do not change */
mwIndex *pos;
ARRAYTYPE *list;

/*************************************************************************/
/* Insertion sort, this is stable */
void InsertionSort(mwIndex left, mwIndex right)
{
mwIndex pival;
mwIndex *pi, *pj, *pleft, *pright;
ARRAYTYPE Value;

pleft = pos+left;
pright = pos+right;
for (pi=pleft; pi<pright; pi++) {
Value = list[pival=(*(pi+1))];
pj = pi;

while ((pj>=pleft) && (list[*pj]>Value)) { /* Comparison */
*(pj+1) = *pj; /* Permute */
pj--;
}
*(pj+1) = pival; /* Insert */
} /* for */

return;
} /* InsertionSort */

/*************************************************************************/

/* Shell sort */
void ShellSort(mwIndex left, mwIndex right)
{
mwIndex pival, pjval, h, k;
mwIndex *pi, *pj, *ph;
mwIndex *pleft, *pright;
ARRAYTYPE Value;

if (right > left) {

pleft = pos+left;
pright = pos+right;

/* Loop on gap size */
for ( k = 0; k < SizeIncs; k++) {
h = incs[k];
ph = pos + h;
/* Insertion loop */
for (pi=pleft+h; pi<=pright; pi++)
{
Value = list[pival=*pi];
pj = pi;
while ((pj>=ph) && (list[pjval=*(pj-h)]>Value)) { /* Comparison */
*pj = pjval; /* Permute */
pj -= h;
}
*pj = pival; /* Insert */
}
}
}

return;
} /* ShellSort */

/*************************************************************************/
/*Find the index of the Median of the elements
of array that occur at every "shift" positions.*/
mwIndex findMedianIndex(mwIndex left, mwIndex right, mwIndex shift)
{
mwIndex tmp, groups, n;
ARRAYTYPE minValue;
mwIndex *pi, *pj, *pk, *pright, *pminIndex;

groups = (right-left)/shift + 1;
pk = pos + (n = left + (groups/2)*shift);
pright = pos + right;
for (pi=pos+left; pi<=pk; pi+= shift)
{
pminIndex = pi;
minValue = list[*pminIndex];

for (pj=pi; pj<=pright; pj+=shift)
if (list[*pj]<minValue) /* Comparison */
minValue = list[*(pminIndex=pj)];
/* Swap pos[i] with pos[minIndex] */
tmp = *pi;
*pi = *pminIndex;
*pminIndex = tmp;
}

return n;

} /* findMedianIndex */

/*Computes the median of each group of 5 elements and stores
it as the first element of the group (left). Recursively does this
till there is only one group and hence only one Median */
mwIndex findMedianOfMedians(mwIndex left, mwIndex right)
{
mwIndex i, shift, step, tmp;
mwIndex endIndex, medianIndex;

if (left==right) return left;

shift = 1;
while (shift <= (right-left))
{
step=shift*5;
for (i=left; i<=right; i+=step)
{
if ((endIndex=i+step-1)>=right)
endIndex=right;
medianIndex = findMedianIndex(i, endIndex, shift);
/* Swap pos[i] with pos[medianIndex] */
tmp = pos[i];
pos[i] = pos[medianIndex];
pos[medianIndex] = tmp;
}
shift = step;
}
return left;
} /* findMedianOfMedians */

/*************************************************************************/
/*Computes the median of three points (left,right,and mid) */
mwIndex findMedianThree(mwIndex left, mwIndex right)
{
ARRAYTYPE vleft, vright, vmid;
mwIndex mid;

if (left==right) return left;

vleft = list[pos[left]];
vright = list[pos[right]];
vmid = list[pos[mid = (left+right+1)/2]];

if (vleft<vright)
{
if (vmid>vright)
return right;
else if (vmid<vleft)
return left;
else
return mid;

} else { /* (vleft>=vright) */

if (vmid>vleft)
return left;
else if (vmid<vright)
return right;
else
return mid;

}
} /* findMedianThree */

/*************************************************************************/
/* Partitioning the list around pivot pivotValue := l[pivotIndex];
* After runing, at exit we obtain:
l[left]...l[index-1] < pivotValue <= l[index] ... l[right]
where l[i] := list[pos[i]] for all i */
mwIndex PartitionV2(mwIndex left, mwIndex right, mwIndex pivotIndex) {

ARRAYTYPE pivotValue, li;
mwIndex *pindex, *pright, *pleft;
mwIndex *plast;
mwIndex ip, il, ir;

plast=pos+right;
pindex=pos+pivotIndex;
pivotValue = list[ip = *pindex];
/* Swap pos[pivotIndex] with pos[right] */
*pindex = *plast;
*plast = ip;

pleft = pos+left;
pright = plast-1;
while (1) {

li = list[il = *pleft];
while ((li<pivotValue) ||
(li==pivotValue && il<ip)) /* <- test for stable sort */
li = list[il = *(++pleft)];

li = list[ir = *(pright)];
while ((pright>pleft) &&
((li>pivotValue) ||
(li==pivotValue && ir>ip))) /* <- test for stable sort */
li = list[ir = *(--pright)];

if (pleft<pright) {
*(pleft++) = ir;
*(pright--) = il;
}
else {
/* swap left with the pivot */
*pleft = *plast;
*plast = il;
return (mwIndex)(pleft-pos); /* Pointer arithmetic */
}
}
} /* PartitionV2 */

/*************************************************************************/
void DownHeap(mwIndex i, mwIndex n, mwIndex lo) {
ARRAYTYPE d;
mwIndex child, posd, nhalf;
mwIndex *plo, *pchild;

plo = pos + lo;
posd = *(plo+i-1);
d = list[posd];
nhalf = n/2;
while (i<=nhalf) {
child = 2*i;
pchild = plo + child;
if ((child < n) &&
(list[*(pchild-1)] < list[*pchild]))
{
child++;
pchild++;
}
if (d >= list[*(pchild-1)]) break;
*(plo+i-1) = *(pchild-1);
i = child;
}
*(plo+i-1) = posd;

return;
} /* DownHeap */

/*http://users.encs.concordia.ca/~chvatal/notes/hsort.html*/
void HeapSort(mwIndex lo, mwIndex hi) { /* (lo,hi) included */
mwIndex n, i, tmp;
mwIndex *plo;

hi++;
n = hi-lo;

for (i=n/2; i>=1; i--)
DownHeap(i, n, lo);

plo = pos + lo;
for (i=n; i>1; i--) {

tmp = *(plo);
*(plo) = *(plo+i-1);
*(plo+i-1) = tmp;

DownHeap(1, i-1, lo);
}
return;

} /* HeapSort */

/*************************************************************************/

void introsort_loop(mwIndex lo, mwIndex hi, int depth_limit)
{
mwIndex pivotIndex;
while (hi+1 >= lo + SWITCH_ALGO_THRESHOLD) {
if (depth_limit == 0) {
HeapSort(lo, hi);
return;
}
depth_limit--;

#if (PIVOT==MEDIANMEDIANS)
pivotIndex = findMedianOfMedians(lo, hi);
#elif (PIVOT==MEDIAN3)
pivotIndex = findMedianThree(lo, hi);
#else /* MIDPOINT */
pivotIndex = (lo+hi+1)/2;
#endif
pivotIndex = PartitionV2(lo, hi, pivotIndex);
introsort_loop(pivotIndex+1, hi, depth_limit);
hi = pivotIndex-1;
}
return;
} /* introsort_loop */

int floor_lg(mwIndex a) {
return (int)(floor(log((double)a)/log(2.0)));
} /* floor_lg */


void IntroSort(mwIndex left, mwIndex right)
{
int depthlimit;
depthlimit=2*floor_lg(right-left+1);

introsort_loop(left, right, depthlimit);
#if (BASESORT==INSERTSORT)
InsertionSort(left, right);
#else /* #if (BASESORT==SHELLSORT)*/
ShellSort(left, right);
#endif

return;
} /* IntroSort */

/*************************************************************************/
/* Recursive engine quicksort */
void quicksort(mwIndex left, mwIndex right) {

mwIndex pivotIndex;

/* Switch to Insertion sort for small size array */
if (right+1 < left + SWITCH_ALGO_THRESHOLD)
{
#if (BASESORT==INSERTSORT)
InsertionSort(left, right);
#else /* #if (BASESORT==SHELLSORT)*/
ShellSort(left, right);
#endif
}
else
{
#if (PIVOT==MEDIANMEDIANS)
pivotIndex = findMedianOfMedians(left, right);
#elif (PIVOT==MEDIAN3)
pivotIndex = findMedianThree(left, right);
#else /* MIDPOINT */
pivotIndex = (left+right+1)/2;
#endif

pivotIndex = PartitionV2(left, right, pivotIndex);
quicksort(left, pivotIndex-1);
quicksort(pivotIndex+1, right);
}

return;
} /* quicksort */

/* Allocate and Initialize the table of position before sorting */
void AllocatePos(mwIndex l)
{
mwIndex n;

/* clean programming */
pos=NULL;

if (l>0) {
pos = mxMalloc(sizeof(mwIndex)*l);
if (pos==NULL)
mexErrMsgTxt("Out of memory (pos).");
/* Initialize the array of position (one-based Matlab index) */
pos--;
for (n=1; n<=l; n++) pos[n]=n;
pos++;
}

return;
} /* AllocatePos */


/* Return the platform dependent the class corresponding to mwIndex */
mxClassID IdxClass(void) {
switch (sizeof(mwIndex)) {
case 4:
return mxINT32_CLASS; /* 2^31-1 */
case 8:
return mxINT64_CLASS; /* 2^48-1 */
}
} /* MaxSize */

/* MainSortDbl, sorting interface for double */
void MainSortDbl(mwIndex l) {

/* Allocate the array of index and get the data pointer */
AllocatePos(l);

/* We reduce the pointer by 1 temporary
to cope with Matlab one-base indexing */
list--;
/* Work for non-empty array */
if (l>1 && list!=NULL && pos!=NULL) {
/* Call the recursive engine */
#if (MAINSORT==QUICKSORT)
quicksort(0, l-1);
#elif (MAINSORT==INTROSORT)
IntroSort(0, l-1);
#elif (MAINSORT==HEAPSORT)
HeapSort(0, l-1);
#else
#error "Non valid MAINSORT"
#endif
} /* if (l>0) */
list++;

return;

} /* MainSortDbl */

/* Gateway routine sort_idx */
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[]) {

const mxArray *A;
mwIndex m, n, l;
mxClassID DataClass;
mxArray* I;
mwSize dimI[2];

if (nrhs!=1)
mexErrMsgTxt("One input argument is required.");

/* Check data type of input argument */

A = prhs[0];
if (mxIsSparse(A))
mexErrMsgTxt("First input argument must be a full matrix.");

DataClass = mxGetClassID(A);
if ((DataClass != mxDOUBLE_CLASS))
mexErrMsgTxt("A must be of class DOUBLE.");

if (mxGetNumberOfDimensions(A) != 2)
mexErrMsgTxt("First input argument must be a vector.");

m = (mwIndex)mxGetM(A);
n = (mwIndex)mxGetN(A);

if ((m > 1) && (n > 1))
mexErrMsgTxt("First input argument must be a vector.");

/* Get the total number elements of A */
l = m*n;

/* Pointers on data */
list = mxGetPr(A);

/* Sort the indexes, the result is put on pos[] */
MainSortDbl(l);

/* Return the index array */
dimI[0] = 0; dimI[1] = 0;
I = mxCreateNumericArray(2, dimI, IdxClass(), mxREAL);
mxSetData(I, pos);
mxSetM(I, m);
mxSetN(I, n);
plhs[0] = I;

return;

} /* sort_idx */