/****   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	"rdxai.h"	
#define ex2(X)  ((int)((unsigned int)(1)<<(X)))

int flg (int x)
{
  register int l;
  if (x <= 1) {
    return(0);
  }
  else {
    l = 0;
    while (x>1) {
      x=Div2(x);
      l++;
    }
    return(l);
  }
}

int clg (int x)
{
  register int l;
  if (x <= 1) {
    return(0);
  }
  else {
    x--;l = 1;
    while (x>1) {
      x=Div2(x);
      l++;
    }
    return(l);
  }

}

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];
}

void ai2scan(void (*operator)(),int multi,char *from,char *to, char *all,int nbytes)
{
 register int   pid;            /* processor id */
 register int   nprocs;         /* number of processors available */
 register int   i,j,size,sze,offset;    /* index variables */
 register int   ceil_temp,left_temp,floor_temp;
 char           *temp;                  /* buffer array */

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

 /* 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 */

 /* allocate temp space and check */
 temp =  (char *) malloc(ceil_temp*nprocs*nbytes);
#ifdef SANITY_CHECK
 check_if_null((void *)temp,"ai2scan","temp");
#endif


 AIOREGISTER(temp,ceil_temp*nprocs*nbytes);
 AIOINIT(temp);
 AIOCOMMIT();

 /* In first phase, each processor sends its i-th block to
  * processor i
  */
 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;
    }
    AIOHPPUT(i,&from[offset*nbytes],temp,pid*sze*nbytes,sze*nbytes);
 }
 AIOSYNC(temp);
 AIBARRIER();

 /* In second phase a local prefix operation is performed */

 /* Did pid get a ceil_temp or floor_temp block? */
 if (pid < left_temp)
       size=ceil_temp;
    else
       size=floor_temp;


 for(i=1;i<nprocs;i++) {
    operator(&temp[i*size*nbytes],&temp[(i-1)*size*nbytes],
                                  &temp[i*size*nbytes],size);
 }

 /* Registration for put  operations */
 /* Register if necessary. PUSHED is defined in my_bsp.h */

 /* In third phase the computed prefices are sent back  */
 /* Where does pid send this 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++) {
         AIOPUT(i,&temp[i*size*nbytes],to,offset*nbytes,size*nbytes);
 }
 AIOSYNC(to);
 AIBARRIER();

 for (i=0;i<nprocs;i++) {
         AIOPUT(i,&temp[(nprocs-1)*size*nbytes],all,offset*nbytes,size*nbytes);
 }
 AIOSYNC(all);
 AIBARRIER();

 /* free memory and deregister information */
 AIODEREGISTER(temp);
 /* Unregister, if necessary */
 free((void*)temp);
}

void foldsum(int *z,int *x,int *y)
{
  *z= *x + *y;
}


