/**************************************************************************
 ****  Parallel Prefix 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 "ppfai.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 sum4
#define SUM2 sum5
#undef  FLOAT_TYPE
#define  FLOAT_TYPE
#define INT_TYPE
#undef INT_TYPE
#define BRD_DEBUG
#undef BRD_DEBUG

#ifdef FLOAT_TYPE
#define BPRINTLINE(X) if (pid >(nprocs-4)) printf(#X"Before  PPF:[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-4)) printf(#X" PPF:[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 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[sze-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[sze-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[sze-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[sze-1],(t4-t3));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); 

#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 *result,data *left,data *right,int size)
{
        register int    i;

        for (i=0;i<size;i++)
                result[i]=left[i]+right[i];
}

#ifdef MPILIB
/*  Associative operator definition for LAMMPI functions */
void oper_mpiadd(data *left,data *inout,int *size, MPI_Datatype *dptr)
{
        register int    i;

        for (i=0;i< *size;i++)
                inout[i]=left[i]+inout[i];
}
#endif


/* Initializes input array */
void  datainit(int pid,int fromp, int nprocs,int sze,data *sum3,data *sum4,data *sum5)
{
 int i;
    for(i=0;i<sze;i++) sum3[i]= (data) (pid+1);    
    for(i=0;i<sze;i++) sum4[i]= (data) 0;    
    for(i=0;i<sze;i++) sum5[i]= (data) 0;    
}

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


/* MAIN function */

int main(int argc, char *argv[])
{  
  int pid,nprocs;
  data sum,*sum3,*sum4,*sum5;
  int i,deg,sze;
  int fromp=0;
  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;      
    if (4 != argc){
      printf("Usage: %s degree size runs\n",argv[0]);
      fflush(stdout);
      AIABORT("exit");
    } else {
	deg=atoi(argv[1]);
	sze=atoi(argv[2]);
	runs=atoi(argv[3]);
    }
   }
	
   /* 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(&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  runs = %d\n",argv[0],pid, nprocs,deg,sze, runs);
     fflush(stdout);
   }

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

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

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


  /* Initialize data */
    for(i=0;i<sze;i++) sum3[i]= (data) (pid+1);    

/*************** Suite A :: Library supported prefix/scan operations  */
  datainit(pid,fromp,nprocs,sze,sum3,sum4,sum5);
  BPRINTLINE(Lib);
  AIBARRIER();


#ifdef BSPLIB
  AIBARRIER();
  AIOREGISTER(SUM ,sze*sizeof(data));
  AIOSYNC(SUM);
  t1=0.0;
  AIBARRIER();
  for(i=0;i<runs;i++) {
    t3=AITIME();  
    bsp_scan(oper_Add,(void *)sum3,(void *)SUM ,sze*sizeof(data));
    t4=AITIME(); t1 +=(t4-t3);  
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,SUM,"Bscan");
  }
PRINTLINE(BSPlib);
PRINTEND(BSPLibPPF);
#endif
AIBARRIER();

#ifdef MPILIB
  AIBARRIER();
  AIOREGISTER(SUM ,sze*sizeof(data));
  AIOSYNC(SUM);
  MPI_Op_create((MPI_User_function *) oper_mpiadd,0,&mympiop);
  t1=0.0;
  AIBARRIER();
  for(i=0;i<runs;i++) {
    t3=AITIME();  
    MPI_Scan((void *)sum3,(void *)SUM,sze,MPI_INT ,mympiop,MPI_COMM_WORLD);
    t4=AITIME();  t1 += (t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,SUM,"Mscan");
  }
PRINTLINE(MPIlib);
PRINTEND(MPILibPPF);
#endif
AIBARRIER();
 /********************** END ****************/

/************* Suite B :: Author's  2-phase parallel prefix */
  datainit(pid,fromp,nprocs,sze,sum3,sum4,sum5);
  BPRINTLINE(2-phase);
  AIBARRIER();
  AIOREGISTER(SUM ,sze*sizeof(data));
  AIOSYNC(SUM);
  AIOREGISTER(SUM2,sze*sizeof(data));
  AIOSYNC(SUM2);
  AIBARRIER();
  t1=0.0;
  for(i=0;i<runs;i++) {
    t3=AITIME();  
    ai2mprefix(oper_add,sze,(void *)sum3,(void *)SUM ,sizeof(data));
    t4=AITIME();   t1 += (t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,SUM,"2pha");
  }
PRINTLINE(2-phase);
/*
pRINTLINE(2xphase);
*/
PRINTEND (2-phasePPF);

AIBARRIER();
/**************  Suite C ::  Author's  Tree-based prefix*/
  datainit(pid,fromp,nprocs,sze,sum3,sum4,sum5);
  BPRINTLINE(LadnerF);
  AIBARRIER();

  AIBARRIER();
  AIOREGISTER(SUM ,sze*sizeof(data));
  AIOSYNC(SUM);
  t1=0.0;
  AIBARRIER();
  for(i=0;i<runs;i++) {
    t3=AITIME();
    aitmprefix(oper_add,sze,deg,(void *)sum3,(void *)SUM ,sizeof(data));
    t4=AITIME(); t1 += (t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,SUM,"tpha");
  }
PRINTLINE(LadnerF);
PRINTEND(LadnerFPPF);

AIBARRIER();
/**************  Suite C2 ::  Author's  Tree-based prefix (another)*/
  datainit(pid,fromp,nprocs,sze,sum3,sum4,sum5);
  BPRINTLINE(ButterP);
  AIBARRIER();

  AIBARRIER();
  AIOREGISTER(SUM ,sze*sizeof(data));
  AIOSYNC(SUM);
  t1=0.0;
  AIBARRIER();
  for(i=0;i<runs;i++) {
    t3=AITIME();
    aitmmprefix(oper_add,sze,deg,(void *)sum3,(void *)SUM ,sizeof(data));
    t4=AITIME(); t1 += (t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,SUM,"tbut");
  }
PRINTLINE(ButterP);
PRINTEND(ButterflyP);


AIBARRIER();
/************** SUite D :: Author's self determining (chooses between C and c2) */
  datainit(pid,fromp,nprocs,sze,sum3,sum4,sum5);
  BPRINTLINE(DecBest);
  AIBARRIER();
  AIBARRIER();
  AIOREGISTER(SUM ,sze*sizeof(data));
  AIOSYNC(SUM);
  t1=0.0;
  AIBARRIER();
  for(i=0;i<runs;i++) {
    t3=AITIME();  
/*
    aitmmprefix(oper_add,sze,deg,(void *)sum3,(void *)SUM ,sizeof(data));
    aittmprefix(oper_add,sze,deg,(void *)sum3,(void *)SUM ,sizeof(data));
*/
    aimprefix(oper_add,sze,(void *)sum3,(void *)SUM ,sizeof(data));
    t4=AITIME();   t1 +=(t4-t3);
    AIBARRIER();
    datachec(pid,fromp,nprocs,sze,sum3,SUM,"best");
  }
PRINTLINE(DecBest);
PRINTEND(DecideBest);

AIBARRIER();
AIEND();
}
