/* gensort.c
   Performs a general sort on data stored as an array. Demonstrates
   calling a function gsort() which calls a comparison function passed as
   a parameter. The gsort() function is called twice to sort a set of
   records firstly by name, then by mark.
   
   This first version requires the user of gsort() to define
   a data type called RECORD
   
   Richard Kay
   3 Jan 2001
*/

#include <stdio.h>
#include <string.h>

struct student {
  char name[30];
  float mark;
};
typedef struct student RECORD;

/* use of RECORD typedef within swap() and gsort() functions places
   requirement that type RECORD is defined globally. 
   To
   make gsort truly general we would need to pass the record length
   as a parameter to these functions and calculate void address offsets
   for comparisons and copy 1 byte (char) at a time up to the
   record length (e.g. using strcpy() ) in order to do the swap. This
   isn't a problem with variable record length data as with this we just
   swap fixed size pointers to sort it.
*/


void gsort(RECORD *a,int rows, int (*comp)(RECORD *x,RECORD *y));
  /* gsort parameters:
     *a: address of array to be sorted
     rows: number of records in the array
     *comp: address of the user-defined comparison function */

  /* comp parameters:
     *x,*y: address of first and second records to compare    */

void swap(RECORD a[], int x,int y);
  /* swap parameters:
     *v the address of the start of the array
     x and y: indexes of array records to be swapped */

void pause(void); /* pauses program */

RECORD a[]={{"Ashok",92.6},
	    {"Seamus",74.7},
	    {"Jaswinder",56.4},
	    {"George",63.2},
	    {"Suresh",34.2},
	    {"Stuart",11.1},
	    {"Harry",87.4},
	    {"Delroy",55.5}};
/* 8 records */

/* comp_name() and comp_mark() functions are defined above the
   call of gsort() in main() so that their addresses are known 
   beforehand. */


int comp_name(RECORD *x,RECORD *y){
  /* compares 2 names to see if in order, same or in reverse sort order */
  return strcasecmp(x->name,y->name); 
      /* Linux strcasecmp() is the same as Borland stricmp() */
    /* both return -1 , 0 or +1 */
}

int comp_mark(RECORD *x,RECORD *y){
  /* compares sort position based on mark */
  if(x->mark > y->mark)
    return 1;
  else if(y->mark > x->mark)
    return -1;
  else
    return 0;
}


int main(void){
  int n,i;
  n=sizeof(a)/sizeof(RECORD); /* should be 8 */
  printf("there are %d records\n",n);

  /* sort and display by name */
  gsort(a,n,comp_name);
  printf("data sorted by name\n");
  for(i=0;i<n;i++)
    printf("%30s %f\n",a[i].name,a[i].mark);
  pause();

  /* sort and display by mark */
  gsort(a,n,comp_mark);
  printf("data sorted by mark\n");
  for(i=0;i<n;i++)
    printf("%30s %f\n",a[i].name,a[i].mark);

  pause();
  return 0;
}


void pause(void){
  printf("press enter to continue\n");
  getchar();
}

void gsort(RECORD a[],int rows, int (*comp)(RECORD *x,RECORD *y) ){
   /* realistic general sorts functions are more likely to use the
      quicksort recursive algorithm. This one uses bubble
      sort for simplicity.  */
   int i,j=rows;
   for(j=rows;j>0;j--){
     for(i=0;i<j-1;i++){
       if((*comp)(a+i,a+i+1) > 0 )
	 swap(a,i,i+1);
     }
   }
}

void swap(RECORD *v,int x,int y){
  RECORD temp;
  temp=v[x];
  v[x]=v[y];
  v[y]=temp;
}


