/****   Copyright (c) 1997,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	"brdai.h"	
/****	$$ai2mbcast$ broadcasts a multiple message (array)
        For more information read file BRDPPF.rdm (README file)
PARAMS: fromp    : source processor
        multi    : size of the array for broadcasting
        from     : source data structure/array
        to       : destination (result of broadcasting goes here)
        nbytes   : each element of broadcast array is of this size
 ****/

void ai2mbcast (int fromp,int multi,char *from,char *to,int nbytes)
{
 int pid;    /* processor id */
 int nprocs; /* number of processors available */
 int i;
 int ceil_temp,left_temp,floor_temp;
 int size; /* total array size */
 int sze,offset; /* size, offset of various blocks */


 pid = AIPID(); nprocs = AINPROCS();

#ifdef SANITY_CHECK
 if ((fromp<0) || (fromp >= nprocs)) { 
  AIABORT("ai2mbcast: fromp out of range");
 }
#endif
     
 size=nbytes*multi; /* Size of broadcast */

 /* split array of size multi into blocks of size either*/
 /* floor(multi/nprocs) or ceil(multi/nprocs). Assign:  */
 left_temp=LEFT(multi,nprocs);
 ceil_temp=CEIL(multi,nprocs);  /* to procs less than left_temp */
 floor_temp=FLOOR(multi,nprocs);/* to procs at least left_temp */

 if ((pid == fromp ) && ( from != to))
    memcpy(to,from,size);

 /* Phase 1: Proc. fromp sends its i-th block to processor i */
 if (pid == fromp ) {
   for(i=0;i<nprocs;i++) {
      if (i < left_temp) {
         sze= ceil_temp; /* send a ceil_temp block */
         offset=i*sze;   /* starting at... */
      } else {
         sze= floor_temp;  /* send a floor_temp block */
         offset= left_temp*ceil_temp+(i-left_temp)*sze;
      }
      if (i != fromp ) 
         AIOHPPUT(i,from+offset*nbytes,to,offset*nbytes,sze*nbytes);
   }
 }
 AIOSYNC(to); 

 /* Where did fromp send the received block? */
 if (pid < left_temp) {
    sze= ceil_temp;
    offset=pid*sze;
 } else {
    sze= floor_temp;
    offset= left_temp*ceil_temp+(pid-left_temp)*sze;
 }


 for(i=0;i<nprocs;i++)
    if (i != pid)
        AIOHPPUT(i,to+offset*nbytes,to,offset*nbytes,sze*nbytes);

 AIOSYNC(to); 


 /* Unregister, if necessary */

}

/****	$aitmbcast$ tree-based broadcast
PARAMS	degree:	is the degree of the tree (number of descendants)
	Other parameters, as in previous function.
 ****/

void aitmbcast (int fromp, int multi, int degree,
			char *from, char *to, int nbytes)
{
  int	pid; /* processor id */
  int	nprocs; /* number of processors */
  int	mask_pid; /* number of processor with message */
  int	target_pid; /* target processor in routings */
  int	temp_pid; /* relative distance from fromp */
  int  	i;
  int	j;
  int	size; /* size in bytes of broadcast array */
  int 	sze,offset;

 pid=AIPID(); nprocs=AINPROCS();
 if ((degree < 2) || (degree >nprocs)) degree=nprocs;
 size=nbytes*multi; /* size of array to be broadcast */

 mask_pid = 1;


 /* Relative processor distance from fromp is: */
 temp_pid = ((pid-fromp)+nprocs)%nprocs;

 if ((pid == fromp) && ( from != to))
   memcpy(to,from,size);

 /* While there exists a processor without the message */
 while (mask_pid <nprocs) {
      /* Do i have the message? */
      if (temp_pid < mask_pid)
	for (i = 1,j=mask_pid;i < degree;i++,j+=mask_pid) {
   	   /* I do and transmit it further */
 	   target_pid = (temp_pid+j+fromp)%nprocs;
           if (target_pid < nprocs)
             AIOHPPUT(target_pid,to,to,0,size);
	   else
	     break;
        }
      AIOSYNC(to);
      mask_pid *= degree;
 } 

 /* Unregister, as necessary */

}

/****   $$aibcast$$ broadcasts a single message from processor fromp */
void aibcast (int fromp,char *from,char *to,int nbytes)
{
 register int 	d,k;

 if (nbytes < 0) {
   AIABORT("aibcast:size less than zero");
 } else if (nbytes ==0) {
   return;
 }

 if (__my_first_call == 0 ) {
   __my_first_call = 1;
   __my_estimate_bsp_values();
 }

 if (( __my_estimate_nprocs == 1) && (fromp == 0)) {
   memcpy(to,from,nbytes);
   return;
 }

 if (( fromp >= 0 ) && (fromp < __my_estimate_nprocs)) {
   k= (int) (__my_estimate_l / (__my_estimate_g));

   if ((k <= 1) || (k >= __my_estimate_nprocs)) {
    d= __my_estimate_nprocs; 
   } 
   else {
    d = k;
   }
   bsp_mbroadcast(fromp,1,d,from,to,nbytes);
 } else {
   AIABORT("aibcast: Source processor out of bounds\n");
 }
}

/****   $$aimbcast$$ broadcasts a multiple message from fromp */
void aimbcast (int fromp,int multi,char *from,char *to,int nbytes)
{
 register int	d,k;

 if (nbytes < 0) {
   AIABORT("aimbcast:size less than zero");
 } else if (nbytes ==0) {
   return;
 }

 if (__my_first_call == 0 ) {
   __my_first_call = 1;
   __my_estimate_bsp_values();
 }



 if (( __my_estimate_nprocs == 1) && (fromp == 0)) {
  memcpy(to,from,multi*nbytes);
  return;
 }

 if (( fromp >= 0 ) && (fromp < __my_estimate_nprocs)) {
   if ((multi*nbytes) > (__my_estimate_brd * __my_estimate_nprocs)) {
 	   ai2mbcast (fromp,multi,from,to,nbytes); 

   } 
   else {
    k= (int) (__my_estimate_l / (__my_estimate_g));
    if ((k <= 1) || (k >= __my_estimate_nprocs)) {
     d= __my_estimate_nprocs; 
    } 
    else {
     d = k;
    }
    aitmbcast(fromp,multi,d,from,to,nbytes);
   }
 } 
 else {
  AIABORT("aimbcast: Source processor out of bounds\n");
 }
}

void mpibcast (char *to,  int multinbytes, int nbytes, int fromp)
{
  int	pid; /* processor id */
  int	nprocs; /* number of processors */
  int	mask_pid; /* number of processor with message */
  int	target_pid; /* target processor in routings */
  int	temp_pid; /* relative distance from fromp */
  int  	i;
  int	j;
  int	size; /* size in bytes of broadcast array */
  int 	sze,offset;

 pid=AIPID(); nprocs=AINPROCS();
 for (i = 0; i < nprocs;i++) {
   if (pid==fromp)
        AIOHPPUT(i,to,to,0,multinbytes);
 }
 AIOSYNC(to);


 /* Unregister, as necessary */

}

/******************/
/*** Same function as $$ai2mbcast$$ with an offset */

void ai2ombcast(int fromp,int multi,char *from,char *to,int off,int nbytes)
{
 int	pid; 	     /* processor id */
 int   nprocs; /* number of processors available */
 int   i;
 int   ceil_temp,left_temp,floor_temp;
 int   size; /* total array size */
 int   sze,offset; /* size, offset of various blocks */


 pid = AIPID(); nprocs = AINPROCS();

#ifdef SANITY_CHECK
 if ((fromp <0) || (fromp >= nprocs)) 
    AIABORT("ai2ombcast: from out of range");
#endif

 size=nbytes*multi; /* Size of broadcast */

 /* split array of size multi into blocks of size either*/
 /* floor(multi/nprocs) or ceil(multi/nprocs). Assign:  */
 left_temp=LEFT(multi,nprocs);
 ceil_temp=CEIL(multi,nprocs);  /* to procs less than left_temp */
 floor_temp=FLOOR(multi,nprocs);/* to procs at least left_temp */

 if ((pid == fromp ) && ( from != (to+off)))
    memcpy((to+off),from,size);

 /* Phase 1: Proc. fromp sends its i-th block to processor i */
 if (pid == fromp ) {
   for(i=0;i<nprocs;i++) {
      if (i < left_temp) {
         sze= ceil_temp; /* send a ceil_temp block */
         offset=i*sze;   /* starting at... */
      } else {
         sze= floor_temp;  /* send a floor_temp block */
         offset= left_temp*ceil_temp+(i-left_temp)*sze;
      }
      if (i != fromp ) 
         AIOPUT(i,&from[offset*nbytes],to,off+offset*nbytes,sze*nbytes);
   }
 }
 AIOSYNC(to);  

 /* Unregister, if necessary */

 /* Where did fromp send the received block? */
 if (pid < left_temp) {
    sze= ceil_temp;
    offset=pid*sze;
 } else {
    sze= floor_temp;
    offset= left_temp*ceil_temp+(pid-left_temp)*sze;
 }


 for(i=0;i<nprocs;i++)
    if (i != pid)
      AIOPUT(i,&to[offset*nbytes+off],to,off+offset*nbytes,sze*nbytes);
 AIOSYNC(to);


 /* Unregister, if necessary */

}



