/* L-12 MCS 572 Monday 6 Feb 2006 implementation of bucket sort. */

#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define EXPANSION_FACTOR 100

typedef struct bucket BUCKET;

struct bucket
{
   int size;     /* number of elements in the bucket */
   double *a;    /* elements are a[0], .. , a[size-1] */
};

typedef struct list LIST;

struct list
{
   BUCKET info;  /* a bucket of doubles */
   LIST *next;   /* pointer to next element in the list */
};

void random_sequence ( int n, double a[n] );
/* returns n random doubles in [0,1] */

LIST *generate_list_of_buckets ( int n, int s );
/* returns a list of n buckets of size s with random doubles in [0,1] */

LIST *allocate_list_of_buckets ( int n, int s );
/* returns a list of n buckets with space allocated for s doubles in 
 * each bucket; the size of each bucket is initialized to zero */

void print_sequence ( int n, double a[n] );
/* writes the sequence a of n numbers to screen */

void print_list_of_buckets ( LIST *bl );
/* writes the list bl of buckets to screen */

int partition ( int n, double x );
/* returns the destination of the double x (assumed to be in [0,1]),
 * to be distributed among n buckets */

void place_in_bucket ( int n, double x, LIST *bl );
/* places the number x in one of the n buckets of the list bl */

void distribute ( int n, LIST *from_bl, LIST *to_bl );
/* distributes the numbers in the list from_bl to the list to_bl */

int compare ( const void *e1, const void *e2 );
/* compares two elements of any type, for use in the qsort of stdlib,
 * returns -1 if e1 < e2, 0 if e1 == e2, +1 if e1 > e2.  */

void sort_buckets ( LIST *bl );
/* applies qsort to all the buckets in the list bl */

int main ( int argc, char* argv[] )
{
   int n,s;
   LIST *bl,*workspace;

   srand(time(NULL));

   printf("Give number of buckets to sort : "); scanf("%d", &n);
   printf("Give the size of each bucket : "); scanf("%d", &s);
   
   bl = generate_list_of_buckets(n,s);
   printf("The generated list of buckets :\n"); print_list_of_buckets(bl);
   workspace = allocate_list_of_buckets(n,s*EXPANSION_FACTOR);
   distribute(n,bl,workspace);
   printf("After distribution :\n"); print_list_of_buckets(workspace);
   sort_buckets(workspace);
   printf("The list of sorted buckets :\n"); print_list_of_buckets(workspace);

   return 0;
}

void random_sequence ( int n, double a[n] )
{
   int i;
   for(i=0; i<n; i++) a[i] = ((double) rand())/RAND_MAX;
}

LIST *generate_list_of_buckets ( int n, int s )
{
   LIST *bl = NULL;
   int i;
   for(i=0; i<n; i++)
   {
      LIST *new_item;
      BUCKET b;
      new_item = (LIST*)calloc(1,sizeof(LIST));
      b.size = s;
      b.a = (double*)calloc(s,sizeof(double));
      random_sequence(s,b.a);
      new_item->info = b;
      new_item->next = bl;
      bl = new_item;
   }
   return bl;
}

LIST *allocate_list_of_buckets ( int n, int s )
{
   LIST *bl = NULL;
   int i;
   for(i=0; i<n; i++)
   {
      LIST *new_item;
      BUCKET b;
      new_item = (LIST*)calloc(1,sizeof(LIST));
      b.size = 0;
      b.a = (double*)calloc(s,sizeof(double));
      new_item->info = b;
      new_item->next = bl;
      bl = new_item;
   }
   return bl;
}

void print_sequence ( int n, double a[n] )
{
   int i;
   for(i=0; i<n; i++) printf(" %.15lf", a[i]);
   printf("\n");
}

void print_list_of_buckets ( LIST *bl )
{
   LIST *p;
   int i;

   for(i=0, p=bl; p != NULL; i++, p=p->next)
   {
      BUCKET b = p->info;
      printf("  bucket %d has %d elements:\n", i, b.size);
      print_sequence(b.size,b.a);
   }
}

int partition ( int n, double x )
{
   double nx = n*x;
   int label = (int) nx;

   return ((label < nx) ? label : label-1);  /* truncate */
}

void place_in_bucket ( int n, double x, LIST *bl )
{
   int label = partition(n,x);
   int i=0;
   LIST *p=bl;
   BUCKET b;

   while ((i++<label) && (p!=NULL)) p = p->next;
   b = p->info;
   b.a[b.size++] = x;
   p->info = b;
}

void distribute ( int n, LIST *from_bl, LIST *to_bl )
{
   LIST *p;
   int i,j;

   for(i=0, p=from_bl; p != NULL; i++, p=p->next)
   {
      BUCKET b = p->info;
      for(j=0; j<b.size; j++) place_in_bucket(n,b.a[j],to_bl);
   }
}

int compare ( const void *e1, const void *e2 )
{
   double *i1 = (double*)e1;
   double *i2 = (double*)e2;
   return ((*i1 < *i2) ? -1 : (*i1 > *i2) ? +1 : 0);
}

void sort_buckets ( LIST *bl )
{
   LIST *p;
   int i;

   for(i=0, p=bl; p != NULL; i++, p=p->next)
   {
      BUCKET b = p->info;
      qsort((void*)b.a,(size_t)b.size,sizeof(double),compare);
   }
}
