/****Interconnection Network Performance Assessmnet
 ****by establishing BSP model parameters p,L, and g.
 ****Copyright (c) 2001-2002  Alexandros V. Gerbessiotis.  
 **** All rights reserved.                               
 ****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, whatsoever, is offered that the code works as
 ****   advertised or is absent of any, even damaging,
 ****   side-effects. Use this code at your own personal risk.
 ****/
#include <stdio.h>
#include <math.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "aimisc.h"
#include "assess.h"
	    
#define FINDG
#undef  FINDG

#undef  FINDL
#define FINDL

#define MAD
#undef MAD

FILE *fp,*fpdata;

void printinfo(FILE *pf,char *u,char *v)
{
  if (0==AIPID()) {  
     printf("#%s %s (%5.2f)\n",u,v,AITIME());
     fprintf(pf,"#%s %s (%5.2f)\n",u,v,AITIME());
     fflush(stdout); fflush(pf);
  }
}

/* Random number generator initializer */
void init_random(unsigned int *seed, unsigned int value)
{
 *seed=value;
}

int get_random(int a, int b, unsigned int *seed)
{
  register long hi, lo;
  register long saved_seed;
  saved_seed=*seed;

  hi = MULTIPLIER * (saved_seed >> 16);
  lo = MULTIPLIER * (saved_seed & 0xffff);
  hi += (lo>>16);
  lo &= 0xffff;
  lo += (hi>>15);
  hi &= 0x7fff;
  lo -= MODULUS;
  if ((saved_seed = (hi<<16) + lo) < 0)
    saved_seed += MODULUS;

  *seed=saved_seed;
  if (b <= a)
    return b;
  return a + saved_seed % (b - a + 1);
}

int main(int argc, char **argv) 
{  
 int  pid,nprocs;
 int  *src;
 int  size,runs,step;
 int  i,j;
 int  result;
 char hostname[512];

 AIBEGIN(AINPROCS());
 pid = AIPID(); nprocs=AINPROCS();
 gethostname(hostname,sizeof(hostname));

/* 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);
    }
 }
 AIBARRIER();

/* Command line processing */
 if (0==pid) {
   if (5 != argc )	 {
     printf("Usage: %s max_size runs step filename\n",argv[0]);
     fflush(stdout);
     AIABORT("Program terminated\n");
   } else { 
     size=atoi(argv[1]);
     runs=atoi(argv[2]);
     step=atoi(argv[3]);
     fp=fopen(argv[4],"w");
     }
 }
 if (0==pid) {
    printf("P=%d Msz =%d MaxHrel= %d Runs=%d\n",nprocs,size,size*nprocs/2,
          runs);
    fprintf(fp,"P=%d Msz =%d MaxHrel= %d Runs=%d\n",nprocs,size,size*nprocs/2,
          runs);
 }
   
/* Parallel command line processing */

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

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


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

   

/* Initializations */
   src = (int *) malloc((1+nprocs)*size*sizeof(int));
   check_if_null((void *) src, "main","src");

   for(i=0;i<nprocs*size;i++) {
      src[i]=(pid*10000+i);    
   }


/* ESTIMATION OF L */ 

   printinfo(fp,"Lsuite","start"); 
    estimate_L(src,src,runs,size,step); 
   printinfo(fp,"Lsuite","end"); 

/*  *********************************************************
    TEST SUITE g::: Estimation of g
    *********************************************************
 */

/* Suite Hrelation1 */
   printinfo(fp,"H1suite","start (each proc sends/recvs Hrelsize)");
    h1test(src,src,runs,size,step); /*hphrel*/
   printinfo(fp,"H1suite","ends");

/* Suite Hrandomrelation */
   printinfo(fp,"H2suite","start (each proc sends Hrelsize rcvs rnd)");
    h2test(src,src,runs,size,step);
   printinfo(fp,"H2suite","ends");

/* End stage clean-up */
  if (pid==0) fclose(fp);

  result= (src[0]+src[size-1]);
  if (pid==0) printf("To fool the optimizer print a value %d\n",result);

  free((void *)src);
  AIEND();
  return(0);
}

/***** Estimate L *****/

void estimate_L(int *src,int *dest,int runs,int size,int step)
{
  int pid;
  int nprocs;
  int i,j,t;
  double t0=0.0,t2=0.0,t1=0.0;
 
  pid=AIPID();
  nprocs=AINPROCS();

  AIBARRIER();

/* Test 1 */
  AIBARRIER();
  t1=AITIME();
  for(i=0;i<runs;i++) {
     AIBARRIER();
  }
  t2=AITIME();
  AIBARRIER();
  t0=t2-t1;


  bspfold((void (*)(void *,void *,void *)) foldmin,(char *)&t0,(char *)&t1,sizeof(double));


  bspfold((void (*)(void *,void *,void *)) foldmax,(char *)&t0,(char *)&t2,sizeof(double));

 if (0==pid) {
   printf(    "Test 1: Empty L= %6.4f-%6.4f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fprintf(fp,"Test 1: Empty L= %6.2f-%6.2f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fflush(stdout);
  }


  /* Test 2 */
  AIBARRIER();
  t1=AITIME();
  for(i=0;i<runs;i++) {
     (src[0])++;
     AIBARRIER();
  }
  t2=AITIME();
  AIBARRIER();
  t0=t2-t1;


  bspfold((void (*)(void *,void *,void *)) foldmin,(char *)&t0,(char *)&t1,sizeof(double));


  bspfold((void (*)(void *,void *,void *)) foldmax,(char *)&t0,(char *)&t2,sizeof(double));

  if (0==pid) {
   printf(    "Test 2: Comp. L= %6.2f-%6.2f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fprintf(fp,"Test 2: Comp. L= %6.2f-%6.2f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fflush(stdout);
  }

  /* Test 3 */
  AIREGISTER(dest, (1+nprocs)*size*sizeof(int));
  AIINIT();
  AICOMMIT();
  AIBARRIER();
  t1=AITIME();
  for(i=0;i<runs;i++) {
     for(j=0;j<nprocs;j++)
       AIHPPUT(j,src,dest,pid*sizeof(int),sizeof(int));
     AISYNC();
  }
  t2=AITIME();
  AIBARRIER();
  t0=t2-t1;


  bspfold((void (*)(void *,void *,void *)) foldmin,(char *)&t0,(char *)&t1,sizeof(double));

  bspfold((void (*)(void *,void *,void *)) foldmax,(char *)&t0,(char *)&t2,sizeof(double));

  AIBARRIER();
  src[0] += dest[0];

  if (0==pid) {
   printf(    "Test 3: FComm L= %6.2f-%6.2f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fprintf(fp,"Test 3: FComm L= %6.2f-%6.2f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fflush(stdout);
  }

  /* Test 4 */
  AIREGISTER(dest, (1+nprocs)*size*sizeof(int));
  AIINIT();
  AICOMMIT();
  AIBARRIER();
  t1=AITIME();
  for(i=0;i<runs;i++) {
     if (0 == pid)
       AIHPPUT(nprocs-1,src,dest,0,sizeof(int));
     AISYNC();
  }
  t2=AITIME();
  AIBARRIER();
  t0=t2-t1;
  bspfold((void (*)(void *,void *,void *)) foldmin,(char *)&t0,(char *)&t1,sizeof(double));
  bspfold((void (*)(void *,void *,void *)) foldmax,(char *)&t0,(char *)&t2,sizeof(double));
  src[0] += dest[0];

  if (0==pid) {
   printf(    "Test 4: PComm L= %6.2f-%6.2f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fprintf(fp,"Test 4: PComm L= %6.2f-%6.2f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fflush(stdout);
  }

  /* Test 5 */
  AIREGISTER(dest,sizeof(int));
  AIINIT();
  AICOMMIT();
  AIBARRIER();
  t1=AITIME();
  for(i=0;i<runs;i++) {
     for(j=0;j<nprocs;j++)
       if (pid ==0)
         AIHPPUT(j,src,dest,0,sizeof(int));
     AISYNC();
  }
  t2=AITIME();
  AIBARRIER();
  t0=t2-t1;
  bspfold((void (*)(void *,void *,void *)) foldmin,(char *)&t0,(char *)&t1,sizeof(double));
  bspfold((void (*)(void *,void *,void *)) foldmax,(char *)&t0,(char *)&t2,sizeof(double));
  src[0] += dest[0];

  if (0==pid) {
   printf(    "Test 5: SComm L= %6.2f-%6.2f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fprintf(fp,"Test 5: SComm L= %6.2f-%6.2f usec \n",
            t1*SCALE/runs,t2*SCALE/runs);
   fflush(stdout);
  }
/*
   AIDEREGISTER(&t2);
   AIDEREGISTER(&t1);
*/
   if (0==pid) {
    fflush(stdout);fflush(fp);
   }
   AIBARRIER();

}


/**** H1 and H2 test suites are defined below *****/

void  h1test(int *src,int *dest,int runs,int size,int step)
{
  int pid;
  int nprocs;
  double t0,t1,t2;
  unsigned int t;
  int i,j,h;

 pid=AIPID();nprocs=AINPROCS();
 t0=t1=t2=0.0;
#ifdef MADX
 if ((nprocs-1)==pid) {
  printf("h1test %d of %d \n",pid,nprocs); fflush(stdout);
 }
#endif

 for(t=1; t<size; t=(t<<step)) {
  t0=0.0;
  h=nprocs*t;

 AIREGISTER(dest, (1+nprocs)*size*sizeof(int));
 AIINIT(); AICOMMIT();
  for(i=0;i<runs;i++) {
    AIBARRIER();
    t1=AITIME();
     for(j=0;j<nprocs;j++)
       AIHPPUT(j,src,dest,pid*t*sizeof(int),t*sizeof(int));
    AISYNC();
    t2=AITIME();
    AIBARRIER();
   t0 +=(t2-t1); 
   for(j=1;j<h;j+=t) dest[j]=dest[j]+dest[j-1];

  }
    bspfold((void (*)(void *,void *,void *))foldmax,(char *)&t0,(char *)&t1,sizeof(double));

  if (0==pid){
       fprintf(fp,"H=:%7d:T=:%9.5f:(PerRun:%9.5f:) g=:%7.3f :(Send:%6d)\n",
         h,t1,t1/runs,(SCALE*t1)/(runs*h),h);
       fflush(stdout);
  }
 }
}

void  h2test(int *src,int *dest,int runs, int size, int step)
{
  int pid;
  int nprocs;
  double t0,t1,t2;
  unsigned int seed,t;
  int i,j,h,l,sz;
  int *sze,*rsze;
  double szd,szdmx,szdmn;
 
 pid=AIPID();nprocs=AINPROCS();
#ifdef MADX
 if ((nprocs-1)==pid) {
  printf("h2test %d of %d \n",pid,nprocs); fflush(stdout);
 }
#endif
  sze= (int *) malloc(nprocs*sizeof(int));
  check_if_null(sze,"h2test","sze");
  rsze= (int *) malloc(nprocs*sizeof(int));
  check_if_null(sze,"h2test","rsze");

  
  for(i=0;i<nprocs;i++) {
    rsze[i]=sze[i]=0;
  }

 init_random(&seed,2047*pid+11);


 for(t=1;t<size;t=(t<<step)) {

  t0=0.0;
  h=nprocs*t;
  l= h;

  AIOREGISTER(rsze,nprocs*sizeof(int));
  AIOINIT(rsze);
  AIOCOMMIT();
  for(j=0;j<nprocs;j++) {
     if (l<0) l=0;
     sz=get_random((h/(2*nprocs)),MIN(l,((3*h/(nprocs*2)))),&seed);
     if (sz>=l) 
       sze[j]=l;
     else
       sze[j]=sz;
     l=l-sze[j];
     if ((j==(nprocs-1)) && (l>0)) sze[(j+pid)%nprocs]=sze[(j+pid)%nprocs]+l;
     AIOPUT(j,&sze[j],rsze,pid*sizeof(int),sizeof(int));
  }
  AIOSYNC(rsze);
  sz=0;
  for(j=0;j<nprocs;j++) {
     sz+=rsze[j];
  }
  szd=sz;
  bspfold((void (*)(void *,void *,void *))foldmin,(char *)&szd,(char *)&szdmn,sizeof(double));
  bspfold((void (*)(void *,void *,void *))foldmax,(char *)&szd,(char *)&szdmx,sizeof(double));

 AIOREGISTER(dest, (1+nprocs)*size*sizeof(int));
 AIOINIT(dest);
 AIOCOMMIT();
  for(i=0;i<runs;i++) {
    /* AIOSYNC(dest);*/
    t1=AITIME();
     for(j=0;j<nprocs;j++)
       AIOHPPUT(j,src,dest,pid*(size)*sizeof(int),(sze[j])*sizeof(int));
    AIOSYNC(dest);
    t2=AITIME();
   t0 +=(t2-t1);
   for(j=1;j<h;j+=t) dest[j]=dest[j]+dest[j-1];

  }
    bspfold((void (*)(void *, void *, void *))foldmin,(char *)&t0,(char *)&t1,sizeof(double));
    bspfold((void (*)(void *, void *, void *))foldmax,(char *)&t0,(char *)&t2,sizeof(double));

 if (pid == 0) {
   fprintf(fp,"H=:%7d:TT=:%5.2f:(PerRun:%8.5f:) g=:%7.3f:(Min:%7d:-Max:%7d:):\n",MAX(h,(int)szdmx),t2,t2/runs,(1000000.0*t2)/(runs*MAX(h,(int)szdmx)),(int)szdmn,(int)szdmx);
  fflush(fp);
 }
 }
}


