#include "Sort.h"

long Random1(long lo, long hi) {

    /* This functions returns a randomly chosen index i in the range lo..hi
       if lo <= hi. Otherwise, it returns LONG_MAX. */

    double range;
    long i;

    if (lo > hi)
        return LONG_MAX;
    if (lo == hi) 
        return lo;

    range = (double) (hi - lo + 1);
    i = lo + (long) ((range*rand()) / (RAND_MAX+1.0));
    if (i < lo)
        i = lo;
    if (i > hi)
        i = hi;

    return i;

} /* end Random1 */

int SetRandomSeed(long seed) {

    /* This function sets the seed of the random number generator 
       to the given value if seed >= 0, and to a random value
       in the range 0-999999 determined by the UNIX wallclock
       if seed = -1. */

    unsigned int seed1;

#ifdef UNIX
    struct timeval timeval;
#endif

    if (seed < -1) {
        fprintf(stderr, "SetRandomSeed(): Warning, seed < -1!\n");
    }

    if (seed >= 0)
        /* set the seed to the given value */
        seed1 = (unsigned int) seed;
    else {
        /* set the seed to a random value using the UNIX wallclock */
#ifdef UNIX
        gettimeofday(&timeval, NULL);
        seed1 = (unsigned int) timeval.tv_usec; /* clock time in microseconds */
#endif
#ifndef UNIX
        /* fprintf(stderr, "SetRandomSeed(): random seed unavailable on non-UNIX systems!\n"); */
        seed1 = time(0);
#endif
    }

    srand(seed1);
#ifdef INFO2
    printf("Random number seed = %u\n", seed1);
#endif
    
    return TRUE;
} /* end SetRandomSeed */


void SwapLong(long *x, const long j0, const long j1) {
    /* This function swaps x[j0] and x[j1] in an array x of type long */
    const long tmp = x[j1];

    x[j1] = x[j0];
    x[j0] = tmp;
} /* end SwapLong */


void SwapDouble(double *x, long j0, long j1) { 
 
    /* This function swaps x[j0] and x[j1] in an array x of type double */ 
 
    double tmp; 
 
    tmp = x[j1]; 
    x[j1] = x[j0]; 
    x[j0] = tmp; 
 
} /* end SwapDouble*/ 


long *QSort(long *X, long LengthX) {

    /* This function sorts array X in decreasing order by
       randomised quicksort. It allocates and returns a bookkeeping 
       array I, which stores the original indices of the array items. */

    register long t; 
    long *I;
    
    if (!X) return 0;
   
    I  = (long *) malloc(LengthX * sizeof(long));
    
    if (I == NULL) {
        fprintf(stderr, "QSort(): Not enough memory for sort array!\n");
        
        return 0;
    }
  
    /*## Initialise index array at original values */
    for (t = 0; t < LengthX; t++)
        I[t] = t;
  
    quicksort(X, I, 0, LengthX - 1);
  
    return I;
}   /* end Qsort */


void quicksort(long *item, long *Index, long lo, long hi) {
 
    /* This function recursively sorts the items lo..hi in decreasing order
       and moves the corresponding index values in the same way.  */
 
    register long i, j;
    long i0, i1, i2, tmpidx, split;
 
    if (lo >= hi)
        return;
 
    /* Choose three splitters randomly */
    i0 = Random1(lo, hi);
    i1 = Random1(lo, hi);
    i2 = Random1(lo, hi);
 
    /* Sort i0, i1, i2 in decreasing order of item value */
    if (item[i0] < item[i1]) {
        /* swap i0 and i1 */
        tmpidx = i0; i0 = i1; i1 = tmpidx;
    }

    if (item[i1] <  item[i2]) {
        /* swap i1 and i2 */
        tmpidx = i1; i1 = i2; i2 = tmpidx;
    }
    /* item[i2] is minimum value of three */

    if (item[i0] < item[i1]) {
        /* swap i0 and i1 */
        tmpidx = i0; i0 = i1; i1 = tmpidx;
    }
    /* item[i1] is median value of three */
 
    /* Use item[i1] as a splitter */
    split = item[i1];
    i = lo;
    j = hi;
    while (i <= j) {
        /* Loop Invariant (LI):
              for all r: lo <= r < i : item[r] >= split 
              for all r: j < r <= hi : item[r] <= split 
        */
        while (item[i] > split && i < hi)
            i++;
        while (item[j] < split && j > lo)
            j--;
        /* LI and lo <= i,j <= hi */
        if (i < j) {
            /* item[i] <= split <= item[j], so swap item i and j */
            SwapLong(item, i, j);
            SwapLong(Index, i, j);
            i++;
            j--;
        } else if (i == j) {
            if (item[i] >= split)
                i++;
            else
                j--;
        }
        /* LI */
    }
    /* i > j and LI */
 
    quicksort(item, Index, lo, j);
    quicksort(item, Index, i, hi);
    return;

} /* end quicksort */


int CSort(long *J, long *val, long maxval, long lo, long hi) {

    /* This function sorts the items J[lo..hi] by increasing value val,
       using a counting sort.
       Items with the same value retain the original order.
       maxval >= 0 is the maximum value that can occur;
       0 is the minimum value */

    long t, j, r, total, tmp, *start, *C;
    
    if (lo >= hi)
        return TRUE;

    C = (long *)malloc((hi-lo+1)*sizeof(long));
    start = (long *)malloc((maxval+1)*sizeof(long));
    
    if (C == NULL || start == NULL) {
        fprintf(stderr, "CSort(): Not enough memory!\n");
        return FALSE;
    }

    for (r=0; r<=maxval; r++)
        start[r] = 0;

    /* First pass. Count the number of items for each value. */
    for (t=lo; t<=hi; t++) {
        j = J[t];
        
        if (val[j] > maxval) {
            fprintf(stderr, "CSort(): val > maxval");
            return FALSE;
        }
        
        start[val[j]]++;
    }

    /* Make start cumulative */
    total = 0;
    for (r=0; r<=maxval; r++) {
        tmp = total;
        total += start[r];
        start[r] = tmp;
    }

    /* Second pass. Copy the items into C. */
    for (t=lo; t<=hi; t++) {
        j = J[t]; 
        C[start[val[j]]]= j;
        start[val[j]]++;
    }

    /* Third pass. Copy the items from C back into J. */
    for (t=lo; t<=hi; t++)
        J[t]= C[t-lo];

    free(start);
    free(C);
    
    return TRUE;
} /* end CSort */


void RandomPermute(long *X, long *Y, double *D, double *E, long lo, long hi) {

    /* This function randomly permutes the contents of the arrays
           X[lo..hi], Y[lo..hi] of type long and
           D[lo..hi], E[lo..hi] of type double, 
       all by the same random permutation.
       The function can also be used for < 4 arrays:
       e.g., if X=NULL on input, then X is not changed. */
       
    register long i;
  
    while (hi > lo) {
        i = Random1(lo, hi);
  
        if (X != NULL) {
            /*## swap: X[i] <-> X[hi] ##*/
            SwapLong(X, i, hi);
        }
        if (Y != NULL) {
            /*## swap: Y[i] <-> Y[hi] ##*/
            SwapLong(Y, i, hi);  
        }
        if (D != NULL) {
            /*## swap: D[i] <-> D[hi] ##*/
            SwapDouble(D, i, hi);  
        }

        if (E != NULL) { 
            /*## swap: E[i] <-> E[hi] ##*/
            SwapDouble(E, i, hi);   
        } 

        hi--;
    }      

} /* end RandomPermute */