/* Example of some group function and reduction functions in PVM 
 *
 * 11 March 1994 - Creation by P. Papadopoulos (phil@msr.epm.ornl.gov)
 *
 *
*/
#include <stdio.h>
#ifdef HASSTDLIB
#include <stdlib.h>
#endif
#include "pvm3.h"
#define min(u,v) ( (u) < (v) ? (u) : (v) )
#define max(u,v) ( (u) > (v) ? (u) : (v) )
#define MATRIXGROUP "matrix"
#define DEFAULT_DIMENSION 100
#define DEFAULT_NPROC     10
#define INITTAG 1000
#define SUMTAG INITTAG + 1
#define PRODTAG SUMTAG + 1
extern void calcprod();
main()
{
int info, mytid, myinst, gsize, nproc = 0;
int maxmax;
int dimension = 0;
int ninst, error;
int tids[32]; int *subblock, *colsum;
double *colprod;
int blksize,nextra,mysrow,i,j,sumsqr,itemp;
    mytid = pvm_mytid(); /* enroll */
    if( (myinst = pvm_joingroup(MATRIXGROUP)) < 0 )
    {
         pvm_perror( "Could not join group \n" );
         pvm_exit();
         exit( -1 );
    }
    if ( myinst == 0 )              
    {
         printf(" This program demonstrates some group and reduction  \n");
         printf(" operations in PVM.  The output displays the \n");
         printf(" the product of the first column of a Toeplitz matrix\n");
         printf(" and the matrix 1-norm. The matrix data is distributed \n");
         printf(" among several processors.  The Toeplitz matrix is \n"); 
         printf(" symmetric with the first row being the row \n");
         printf(" vector [1 2 ... n].\n");
         printf("\n\n");
         if ( pvm_parent() == PvmNoParent )
         {
            while ( nproc <= 0 || nproc > 32 || dimension <= 0)
            {
                printf( " Input dimension ( >0 ) of matrix: " );
                scanf( "%d", &dimension );
                printf( " Input number of tasks (1-32): " );
                scanf( "%d", &nproc );
            }
          }
          else
          {
               nproc = DEFAULT_NPROC;
               dimension = DEFAULT_DIMENSION;
          }
          if (nproc > 1) 
          {
             ninst = pvm_spawn( "gexample", (char **) 0, 0, "",  
                                nproc-1, tids);  
             nproc = min (nproc, ninst + 1); 
          }
          pvm_initsend( PvmDataDefault );
          pvm_pkint( &nproc, 1, 1 );
          pvm_pkint( &dimension, 1, 1 );
          pvm_mcast( tids, nproc - 1, INITTAG ); 
     }
     else
     {
          pvm_recv( -1, INITTAG );
          pvm_upkint( &nproc, 1, 1 );
          pvm_upkint( &dimension, 1, 1);
     }
     /* Make the group static. freezegroup will wait until nproc tids have
        joined the group.                  */
     info = pvm_freezegroup(MATRIXGROUP,nproc);
/* Map matrix rows to processors --      */ 
     blksize =  dimension/nproc ;
     nextra =   dimension % nproc; 
     if( myinst < nextra ) 
     {
         mysrow = (blksize + 1) * myinst;  
         blksize++; 
     }
     else
         mysrow = (blksize + 1)*(nextra) + blksize*(myinst - nextra);
     subblock = (int *) calloc(blksize*dimension,sizeof(int)); 
     colsum   = (int *) calloc(dimension,sizeof(int));
     colprod  = (double *) calloc(dimension,sizeof(double));
     if (mysrow >= dimension)    /* too many processors ! */
        blksize = 0;
    
/* Assign data to this subblock.  The entries below make the entire matrix
   a symmetric Toeplitz matrix (i.e. diagonals are of constant value)  */
     for (i = 0; i < blksize; i++)
        for (j = 0; j < dimension; j++)
           *(subblock + i*dimension + j) = 1 + abs(mysrow + i - j); 

/* Locally compute the sum of each column and put into colsum */
     for (j = 0; j < dimension; j++)
     {
        colsum[j] = 0;
        colprod[j] = 1.0;
     }
     for (i = 0; i < blksize; i ++)
       for(j = 0; j < dimension; j++)
        {
              itemp = *(subblock + j + i*dimension);
              colsum[j] += abs( itemp );
              colprod[j] *= abs( itemp );
        }
/* synchronize the computation and then reduce using pvm_sum. This gives
   a row vector that has the all the columns sums  */
     pvm_barrier(MATRIXGROUP,-1);        /* make sure processes are finished*/
     if ( pvm_reduce(PvmSum,colsum,dimension,PVM_INT,SUMTAG,
                      MATRIXGROUP,0) < 0  )
         pvm_perror( "pvm_reduce had an error \n") ; 
     pvm_reduce(calcprod,colprod,dimension,PVM_DOUBLE,PRODTAG,
                      MATRIXGROUP,0); 
     if( myinst == 0)
     {
          maxmax = 0;
          for (j = 0; j < dimension; j++)
             maxmax = max(colsum[j],maxmax);
          printf(" The 1-Norm is %d  \n" , maxmax );
          printf(" ( Should be the sum of the integers from 1 to %d )\n", 
                              dimension );
          printf(" The product of column 1 is %g  \n" , colprod[0] );
          printf(" ( Should be %d factorial)\n", 
                              dimension);
     }
     info = pvm_barrier(MATRIXGROUP,-1);  /* make sure processes are finished*/
     if (info < 0) 
         printf("Barrier failed with result code %d\n", info);
     pvm_lvgroup(MATRIXGROUP);
     free(subblock); free(colsum); free(colprod); 
     pvm_exit();
}

/*** A User-defined Reduction Function ***/
void
calcprod(datatype,x,y,num,info)
int *datatype;
double  *x,*y;
int *num, *info;
{
int i;
     for (i = 0 ; i < *num; i++)
        x[i] *= y[i];
}        
