// OpenMP introductory example:  sampling bucket sort

// compile:  gcc -fopenmp -o bsort bucketsort.c

#include <omp.h>  // required
#include <stdlib.h>

// needed for call to qsort()
int cmpints(int *u, int *v) 
{  if (*u < *v) return -1;
   if (*u > *v) return 1;
   return 0;
}

// adds xi to the part array, increments npart, the length of part
void grab(int xi, int *part, int *npart)
{
    part[*npart] = xi;
    *npart += 1;
}

// finds the min and max in y, length ny, 
// placing them in miny and maxy
void findminmax(int *y, int ny, int *miny, int *maxy)
{  int i,yi;
   *miny = *maxy = y[0];
   for (i = 1; i < ny; i++) {
      yi = y[i];
      if (yi < *miny) *miny = yi;
      else if (yi > *maxy) *maxy = yi;
   }
}

// sort the array x of length n
void bsort(int *x, int n)
{  float *bdries; int *counts;
   #pragma omp parallel  
   // entering this block activates the threads, each executing it
   {  // these are local to each thread:
      int me = omp_get_thread_num();
      int nth = omp_get_num_threads();
      int i;
      #pragma omp single  // only 1 thread does this, implied barrier at end
      {  int xi,minx,maxx;
         #define SAMPLESIZE 1000
         findminmax(x,SAMPLESIZE,&minx,&maxx);
         bdries = malloc(nth*sizeof(float));
         float increm = (maxx - minx) / (float) nth;
         for (i = 0; i < nth; i++) bdries[i] = minx + i * increm;
         counts = malloc(nth*sizeof(int));
      }
      // now have this thread grab its portion of the array; thread 0
      // takes everything below bdries[0], thread 1 everything between
      // bdries[0] and bdries[1], etc., with thread nth-1 taking
      // everything over bdries[nth-1]
      int *mypart = malloc(n*sizeof(int)); int nummypart = 0;
      for (i = 0; i < n; i++) {  
         if (me == 0) {
            if (x[i] <= bdries[0]) grab(x[i],mypart,&nummypart);
         }
         else if (me < nth-1) {
            if (x[i] > bdries[i-1] && x[i] <= bdries[i]) 
               grab(x[i],mypart,&nummypart);
         } else
            if (x[i] > bdries[i]) grab(x[i],mypart,&nummypart);
      }
      // now record how many this thread got
      counts[me] = nummypart;
      #pragma omp barrier  // other threads will need to know all of counts
      // sort my part
      qsort(mypart,nummypart,sizeof(int *),cmpints);
      // copy back to the original array; first find starting point
      int start = 0;
      for (i = 0; i < me; i++) start += counts[i];
      for (i = 0; i < nummypart; i++) {
         x[start+i] = mypart[i];
      }
      #pragma omp barrier  // wait til all done
   }
}

int main(int argc, char **argv)
{  
   int n = atoi(argv[1]), *x = malloc(n*sizeof(int));
   int i;
   for (i = 0; i < n; i++) x[i] = rand();
   bsort(x,n);
}
