/**************************************************************************
 ****  Radix Sort      primitive TEST functions (contains a main() )
 ****  Copyright (c) 2001-2003  Alexandros V. Gerbessiotis
 ****
 ****   Permission to use, copy, modify, and distribute this software,
 ****   and to incorporate it, in whole or in part, into other software,
 ****   is hereby granted without fee, provided that
 ****     (1) the above copyright notice and this permission notice appear in
 ****         all copies of the source code, and the above copyright notice
 ****         appear in clearly visible form on all supporting documentation
 ****         and distribution media, and
 ****     (2) any redistribution of the software, in original
 ****         or modified form, be without fee and subject to
 ****         these same conditions.
 ****   No guarantee is offered that the code works as
 ****   advertised or is absent of any, even damaging,
 ****   side-effects; use this code on your own personal risk
 ****/

#include <stdio.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "rdxai.h"
#include "aimisc.h"
/*
    extern int _bsp_noslots, _bsp_buffer_size, _bsp_nbuffers, _bsp_slotsize_usecs, 
               _bsp_roundtrip_usecs, _bsp_sendlatency_usecs;
    void setup_nonblocksend_buffersize();
*/

/* What to change:
   PENTE   runs that number of times the testing function before timing starts
   SUM     broadcast from sum3 to sum4; if you want broadcasting from sum3 to sum3
           array change this into sum3
   INT_TYPE or FLOAT_TYPE Choose one or the other to broadcast floats or ints
   BRD_DEBUG if set prints the input/output array to so that one can check the
             behavior of the testing function.
 */

#define PENTE  3
#define INT_TYPE
#undef INT_TYPE

#ifdef FLOAT_TYPE
#define BPRINTLINE(X) if (pid ==(nprocs-1)) printf(#X"Before  [P:%2d],val=%4.1f,%4.1f,..,%4.1f \n",pid,SUM[0],SUM[1],SUM[size-1]); fflush(stdout);
#define PRINTLINE(X) if (pid >(nprocs-4)) printf(#X" PPF:[P:%2d],val=%4.1f,%4.1f,..,%4.1f  (%8.6f)\n",pid,SUM[0],SUM[1],SUM[size-1],(t4-t3));fflush(stdout);
#define pRINTLINE(X) if (pid >(nprocs-4)) printf(#X" PPF:[P:%2d],val=%4.1f,%4.1f,..,%4.1f  (%8.6f)\n",pid,SUM2[0],SUM2[1],SUM2[size-1],(t4-t3));fflush(stdout);
#endif

#ifdef INT_TYPE
#define BPRINTLINE(X) if (pid >(nprocs-4)) printf(#X"Before  PPF:[P:%2d],val=%3d,%3d,..,%3d \n",pid,SUM[0],SUM[1],SUM[size-1]); fflush(stdout);
#define PRINTLINE(X) if (pid >(nprocs-4)) printf(#X" PPF:[P:%2d],val=%3d,%3d,..,%3d (%8.6f)\n",pid,SUM[0],SUM[1],SUM[size-1],(t4-t3));fflush(stdout);
#define pRINTLINE(X) if (pid >(nprocs-4)) printf(#X" PPF:[P:%2d],val=%3d,%3d,..,%3d (%8.6f)\n",pid,SUM2[0],SUM2[1],SUM2[size-1],(t4-t3));fflush(stdout);
#endif

#define PRINTEND()   bspfold((void (*)(void *,void *,void *)) foldmin,(char *)&t1,(char *)&t3,sizeof(double)); bspfold((void (*)(void *,void *,void *)) foldmax,(char *)&t1,(char *)&t4,sizeof(double)); if (pid == 0) printf("** %10s ** Time %9.6f-%9.6f \n",labels[alg], t3,t4); 

#ifdef MPILIB
MPI_Op mympiop;
#endif

void oper_Sadd(data *result,data *left,data *right)
{
                *result=*left + *right;
}

/* BSPlib associative operator definition */
void oper_Add(data *result,data *left,data *right,int *size)
{
        register int    i;

        for (i=0;i<(*size/sizeof(data));i++)
                result[i]=left[i]+right[i];

}

/* Associative operator definition for my functions */

void oper_add(data *, data *, data * ,int); 

int compare(const void *left,const void *right)
{
 return(*((data *)left)- *((data *)right));
}

void  datainit(data *d, int length)
{
 register int i,temp;
 register int pid;
 pid=AIPID();
 srandom(21+1001*11);
 for (i=0;i<length*pid;i++) {
   temp=random(); 
 }
 for (i=0;i<length;i++) {
   d[i]= (data ) (random() ); 
 }
}

void  datachec(data *d,int length)
{
 int i;
 AIBARRIER();
 for(i=0;i<length-1;i++) {
  if (d[i]>d[i+1])
   fprintf(stderr,"Error at (%d) %d %d %d\n",AIPID(),i,d[i], d[i+1]);
 }
} 

void print_uint(data *inp,int size,char *s)
{
 int i,j;
 int pid;
 pid=AIPID();
   fprintf(stdout,"[%f]pid=%d:%3d %s: ",AITIME(),pid,size,s);
 for(i=0;i<PENTE;i++){
   fprintf(stdout,"%3d,", (unsigned int) inp[i]);
 }
   fprintf(stdout,"...");
 for(i=size-PENTE;i<size;i++){
   fprintf(stdout,"%3d,", (unsigned int) inp[i]);
 }
 fprintf(stdout,"\n");

}

/* MAIN function */

int main(int argc, char *argv[])
{  
  int pid,nprocs,size,i,runs,k,alg,j,debug;
  data *src;
  double t1,t2,t3,t4,t0;
  char hostname[512];
#ifdef MPILIB
  void (*funcrdx[6])()={rdx4,mprdx4,sbrdx,mpsbrdx,sprdx,mpsprdx};
  char *labels[6]={    "rdx4","mprdx4","sbrdx","mpsbrdx","sprdx","mpsprdx"};
#else
  void (*funcrdx[6])()={rdx4,rdx4  ,sbrdx,sbrdx,sbrdx,sbrdx};
  char *labels[6]={    "rdx4","rdx4","sbrdx","sbrdx","sprdx","sprdx"};
#endif
  void (*frdx)(unsigned int *,int);

  AIBEGIN(AINPROCS());
  pid = AIPID(); nprocs = AINPROCS();
  runs=1;
  gethostname(hostname,sizeof(hostname));
#ifdef SANITY_CHECK
  /* Show hostnames of processors; */
  for(i=0;i<nprocs;i++) {
    if (i==pid) {
       printf("Processor %d of %d [%s]\n",pid,nprocs,hostname);
       fflush(stdout);
    }
  }
#endif
  AIBARRIER();
/*
 if (nprocs ==16) {
    _bsp_noslots          = 4;
    _bsp_nbuffers         = 2000;
    _bsp_slotsize_usecs   = 64;
    _bsp_roundtrip_usecs  = 180;
    _bsp_sendlatency_usecs= 30;
 }
 if (nprocs ==4) {
    _bsp_noslots          = 4;
    _bsp_nbuffers         = 2000; 
    _bsp_slotsize_usecs   = 32;
    _bsp_roundtrip_usecs  = 180;
    _bsp_sendlatency_usecs= 30;
 }
 fprintf(stdout,"%d %d %d %d %d \n",
    _bsp_noslots         , 
    _bsp_nbuffers        , 
    _bsp_slotsize_usecs  , 
    _bsp_roundtrip_usecs , 
    _bsp_sendlatency_usecs);
*/

  /* Command line processing */
  if (0 == pid ){
    size=1;      
    if ((1!= argc) &&(5 != argc)){
      printf("Usage: %s size algorithm runs debug\n",argv[0]);
      fflush(stdout);
      AIABORT("exit");
    } else {
	size=atoi(argv[1]);
        alg=atoi(argv[2]);
       runs=atoi(argv[3]);
      debug=atoi(argv[4]);
    }
 }
	
   /* Parallel Command line processing */
   AIREGISTER(&size,sizeof(int));
   AIINIT();AICOMMIT();
      bspbroad(0,(char *)&size ,(char *)&size ,sizeof(int));
   AISYNC();AIBARRIER();
   AIDEREGISTER(&size );

   AIREGISTER(&alg ,sizeof(int));
   AIINIT();AICOMMIT();
      bspbroad(0,(char *)&alg  ,(char *)&alg  ,sizeof(int));
   AISYNC();AIBARRIER();
   AIDEREGISTER(&alg);

   AIREGISTER(&runs  ,sizeof(int));
   AIINIT();AICOMMIT();
      bspbroad(0,(char *)&runs ,(char *)&runs ,sizeof(int));
   AISYNC();AIBARRIER();
   AIDEREGISTER(&runs );
   AIREGISTER(&debug ,sizeof(int));
   AIINIT();AICOMMIT();
      bspbroad(0,(char *)&debug,(char *)&debug,sizeof(int));
   AISYNC();AIBARRIER();
   AIDEREGISTER(&debug);

   if ((nprocs-1)==pid) {
     printf("%s %10s:Proc %2d of %d:: size per proc= %d Total size=%d\n",argv[0],labels[alg],pid, nprocs,size,size*nprocs);
     fflush(stdout);
   }
   AIBARRIER();

   /* Memory allocation */
   src= (data *) malloc((1+size)*sizeof(data));
   check_if_null((void *) src,"rdxsrt ::","src");
/*
 if (pid==0)
 fprintf(stdout,"%d %d %d %d %d \n",
    _bsp_noslots         , 
    _bsp_nbuffers        , 
    _bsp_slotsize_usecs  , 
    _bsp_roundtrip_usecs , 
    _bsp_sendlatency_usecs);
*/
  frdx=funcrdx[alg];
/************* Radix Sort time */
  /* Initialize data */
  t1=0.0;t0=0.0;
  for(j=0;j<runs;j++){
  datainit(src,size);
  if (debug ==1) print_uint(src, size,"src "); 
  AIBARRIER();
  t3=AITIME();  
  (*frdx)(src,size);
  t4=AITIME();   t1 = (t4-t3);
  AIBARRIER();
  datachec(src,size);
  PRINTEND(); fflush(stdout);t0+=t4;
  }
  if (pid==0) {fprintf(stdout,"**** Average Time is = %8.4f\n",t0/runs);}
  if (debug ==1) print_uint(src, size,"src "); 
  free((void *)src);
  AIEND();
  return(0);
}
