/**************************************************************************
 ****  Broadcasting primitives 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 "brdai.h"
#include "aimisc.h"

/* 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  0
#define SUM sum3
#undef INT_TYPE
#define  FLOAT_TYPE
#define BRD_DEBUG
#undef BRD_DEBUG

#ifdef FLOAT_TYPE
#define BPRINTLINE(X) if (pid == (nprocs/2)) printf(#X"BeforE BRD:[P:%2d],val=%4.1f,%4.1f,..,%4.1f\n",pid,SUM[0],SUM[1],SUM[sze-1]); fflush(stdout);
#define PRINTLINE(X) if (pid == (nprocs/2)) printf(#X" BRD:[P:%2d],val=%4.1f,%4.1f,..,%4.1f (%8.6f)\n",pid,SUM[0],SUM[1],SUM[sze-1],(t4-t3));fflush(stdout);
#define ZPRINTLINE(X) printf(#X" BRD:[P:%2d],val=%4.1f,%4.1f,..,%4.1f\n",pid,SUM[nprocs+0],SUM[nprocs+1],SUM[nprocs+sze-1]);fflush(stdout);
#endif

#ifdef INT_TYPE
#define BPRINTLINE(X) if (pid == (nprocs/2)) printf(#X"Before  BRD:[P:%2d],val=%3d,%3d,..,%3d\n",pid,SUM[0],SUM[1],SUM[sze-1]); fflush(stdout);
#define PRINTLINE(X) if (pid == (nprocs/2)) printf(#X" BRD:[P:%2d],val=%3d,%3d,..,%3d\n",pid,SUM[0],SUM[1],SUM[sze-1]);fflush(stdout);
#define ZPRINTLINE(X) printf(#X" BRD:[P:%2d],val=%3d,%3d,..,%3d\n",pid,SUM[nprocs+0],SUM[nprocs+1],SUM[nprocs+sze-1]);fflush(stdout);
#endif

#ifndef BRD_DEBUG
#define BPRINTLINE(X) 
#define PRINTLINE(X) 
#endif

#define PRINTEND(X)   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("**** "#X" **** Time %9.6f-%9.6f (per run: %8.6f-%8.6f)\n", t3,t4,(t3)/runs,(t4)/runs); 

/* Initializes inputand output array */
void  datainit(int pid,int fromp,int nprocs,int sze,data *sum3,data *sum4)
{
 int i;
  if (pid == fromp )  {
    for(i=0;i<sze;i++) sum3[i]= (data) (i+1);    
  }
  else {
      for(i=0;i<sze;i++) SUM [i]= (data) -2;    
   }
}

void  datachec(int pid,int fromp,int nprocs,int sze,data *sum3,data *sum4)
{
 int i;
    for(i=0;i<sze;i++) {
      if (SUM[i] != ((data) (i+1)) )   
       fprintf(stderr,"Error at [%d] %f %f\n",i,(data)SUM[i],(data)(i+1));
    }
}

/* MAIN function */

int main(int argc, char *argv[])
{  
  int pid,nprocs;
  data sum,*sum3,*sum4,temp,temp2;
  data ident = (data)0;
  int i,deg,sze;
  int fromp;
  int runs;
  double t1,t2,t3,t4;
  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 ){
      runs=1;     deg=4;
      sze=1;      fromp=0;
    if (5 != argc){
      printf("Usage: %s degree size  origin runs\n",argv[0]);
      fflush(stdout);
      AIABORT("exit");
    } else {
	deg=atoi(argv[1]);
	sze=atoi(argv[2]);
	fromp=atoi(argv[3]);
	runs=atoi(argv[4]);
    }
   }
	
   /* Parallel Command line processing */
   AIREGISTER(&deg,sizeof(int));
   AISYNC();
      bspbroad(0,(void *)&deg ,(void *)&deg ,sizeof(int));
   AISYNC();
   AIDEREGISTER(&deg );

   AIREGISTER(&sze,sizeof(int));
   AISYNC();
      bspbroad(0,(void *)&sze ,(void *)&sze ,sizeof(int));
   AISYNC();
   AIDEREGISTER(&sze );

   AIREGISTER(&fromp,sizeof(int));
   AISYNC();
      bspbroad(0,(void *)&fromp,(void *)&fromp ,sizeof(int));
   AISYNC();
   AIDEREGISTER(&fromp);

   AIREGISTER(&runs,sizeof(int));
   AISYNC();
      bspbroad(0,(void *)&runs,(void *)&runs  ,sizeof(int));
   AISYNC();
   AIDEREGISTER(&runs);

   if ((nprocs-1)==pid) {
     printf("%s: Proc %2d of %d:: Deg = %d size= %d fromp = %d runs = %d\n",
             argv[0],pid, nprocs,deg,sze, fromp,runs);
     fflush(stdout);
   }

   /* Memory allocation */
   sum3= (data *) malloc((nprocs+sze)*sizeof(data));
   check_if_null((void *) sum3,"brdmain::","sum3");

   sum4= (data *) malloc((nprocs+sze)*sizeof(data));
   check_if_null((void *) sum4,"brdmain::","sum4");


  /* Initialize data */
  if (pid == fromp ) {
    for(i=0;i<sze;i++) {sum4[i]=sum3[i]= (data) (i+1);    }
  }
  else {
    for(i=0;i<sze;i++) {sum4[i]=sum3[i]= (data) (-2);    }
  }	

  BPRINTLINE(Init);


/*************** Suite A :: Library supported broadcasting operation  */

/****************************
 For bsplib() it tests bsp_bcast by J.Hill
 For LAMMPI   it tests MPI_Bcast 
 ****************************/
  datainit(pid,fromp,nprocs,sze,sum3,sum4);
  AIBARRIER();
  AIOREGISTER(SUM ,sze*sizeof(data));
  AIOSYNC(SUM);
  /***********BSPlib test *****************/
#ifdef BSPLIB
  for(i=0;i< PENTE;i++)
    bsp_bcast(fromp,(void *)sum3,(void *)SUM ,sze*sizeof(data));
  t1=0.0;
  AIBARRIER();
  for(i=0;i<runs;i++) {
    t3=AITIME();  
    bsp_bcast(fromp,(void *)sum3,(void *)SUM ,sze*sizeof(data));
    t4=AITIME();  t1 += (t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,sum4);
  }
  for(i=0;i<PENTE;i++)
    bsp_bcast(fromp,(void *)sum3,(void *)SUM ,sze*sizeof(data));
PRINTLINE(BSPLibr);
PRINTEND(BSPLibrary);
#endif


/* *************MPI_Bcast test */
/* Check whether PUSH is required here */
  AIBARRIER();
  AIOREGISTER(sum3,sze*sizeof(data));
  AIOSYNC(SUM);
#ifdef MPILIB
  for(i=0;i< PENTE;i++)
    MPI_Bcast((void *)SUM ,sze*sizeof(data),MPI_CHAR,fromp,MPI_COMM_WORLD);
  t1=0.0;
  AIBARRIER();
  for(i=0;i<runs;i++) {
    t3=AITIME();  
    MPI_Bcast((void *)sum3 ,sze*sizeof(data),MPI_CHAR,fromp,MPI_COMM_WORLD);
    memcpy(sum4,sum3,sze*sizeof(data));
/*
*/
    t4=AITIME();  t1+=(t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,sum4);
  }
  for(i=0;i<PENTE;i++)
    MPI_Bcast((void *)sum3,sze*sizeof(data),MPI_CHAR,fromp,MPI_COMM_WORLD);
PRINTLINE(MPILibr);
PRINTEND(MPILIbrary);
#endif


AIBARRIER();
 /********************** END ****************/

/************* Suite B :: Author's  2-phase broadcasting */
  datainit(pid,fromp,nprocs,sze,sum3,sum4);
  BPRINTLINE(2-phase);

  AIBARRIER();
  AIOREGISTER(SUM ,sze*sizeof(data));
  AIOSYNC(SUM);
  /* Two phase broadcasting */
  for(i=0;i< PENTE;i++)
    ai2mbcast       (fromp,sze,(void *)sum3,(void *)SUM,sizeof(data));
  AIBARRIER();
  t1=0.0;
  for(i=0;i<runs;i++) {
    t3=AITIME();  
    ai2mbcast       (fromp,sze,(void *)sum3,(void *)SUM ,sizeof(data));
    t4=AITIME();  t1+=(t4-t3);
    AIBARRIER();
  }
    datachec(pid,fromp,nprocs,sze,sum3,sum4);
  for(i=0;i< PENTE;i++)
    ai2mbcast       (fromp,sze,(void *)sum3,(void *)SUM ,sizeof(data));

PRINTLINE(2-phase);
PRINTEND(2-phaseBRD);

/**************  Suite C ::  Author's  Tree-based broadcast */
  datainit(pid,fromp,nprocs,sze,sum3,sum4);
  BPRINTLINE(TreeBRD);

  AIBARRIER();
  AIOREGISTER(SUM ,sze*sizeof(data));
  AIOSYNC(SUM);
  /* Tree-based broadcastign degree of replication is deg (user defined) */
  for(i=0;i< PENTE;i++)
    aitmbcast     (fromp,sze,deg,(void *)sum3,(void *)SUM ,sizeof(data));
  t1=0.0;
  AIBARRIER();
  for(i=0;i<runs;i++) {
    t3=AITIME();
    aitmbcast     (fromp,sze,deg,(void *)sum3,(void *)SUM ,sizeof(data));
    t4=AITIME(); t1+=(t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,sum4);
  }
  for(i=0;i<PENTE;i++)
    aitmbcast     (fromp,sze,deg,(void *)sum3,(void *)SUM ,sizeof(data));

PRINTLINE(TreeBRD);
PRINTEND(TreeBroadc);


AIBARRIER();
/************** Suite D :: Author's self determining (chooses between 1 and 1c) */
  datainit(pid,fromp,nprocs,sze,sum3,sum4);
  BPRINTLINE(DecBest);
  AIBARRIER();
  AIOREGISTER(SUM ,nprocs*sizeof(data)+sze*sizeof(data));
  AIOSYNC(SUM);
  /* It automatically choose between B and C depending on BSP param p,L,g */
  for(i=0;i< PENTE;i++)
    aimbcast          (fromp,sze,(void *)sum3,(void *)SUM ,sizeof(data));
  AIBARRIER();
  t1=0.0;
  for(i=0;i<runs;i++) {
    t3=AITIME();  
    aimbcast          (fromp,sze,(void *)sum3,(void *)SUM ,sizeof(data));
    t4=AITIME();   t1 += (t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,sum4);
  }
  for(i=0;i<PENTE;i++)
    aimbcast          (fromp,sze,(void *)sum3,(void *)SUM ,sizeof(data));

PRINTLINE(DecBest);
PRINTEND(DecideBest);

AIBARRIER();
/************** SUite E :: Author's MPI_Bcast equivalent */
  datainit(pid,fromp,nprocs,sze,sum3,sum4);
  BPRINTLINE(MpiComp);
  AIBARRIER();
  AIOREGISTER(SUM ,nprocs*sizeof(data)+sze*sizeof(data));
  AIOSYNC(SUM);
  /* It provides an interface similar to that of the MPI_Bcast function) */
  for(i=0;i< PENTE;i++)
    mpibcast((void *)SUM ,sze*sizeof(data),sizeof(data), fromp);
  AIBARRIER();
  t1=0.0;
  for(i=0;i<runs;i++) {
    t3=AITIME();  
    mpibcast((void *)SUM ,sze*sizeof(data),sizeof(data), fromp);
    t4=AITIME();   t1 += (t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,sum4);
  }
  for(i=0;i<PENTE;i++)
    mpibcast((void *)SUM ,sze*sizeof(data),sizeof(data), fromp);

PRINTLINE(MpiBrdcast);
PRINTEND(MpiBrdcast);
#ifdef ALEXG
#endif


AIBARRIER();
  AIEND();
}
