#undef SEEK_SET
#undef SEEK_END
#undef SEEK_CUR
#include <mpi.h>
#include <iostream>
using namespace std;
/************************************************************************************
* copyright © 2013 Soheil Hooshdaran												*
*  The purpose of this program is to multiply the two arrays 'A(n*m)' and  'B(m*p)' *
*  and store the results 															*
*  into the array 'c'																*
************************************************************************************/
int i, j, k;   		 //Loop indices
int	n, m, p;   		 //Used as the dimensions of the arrays
     

int main( int argc, char*argv[])
{
/***************Executed on all processors**************/
	 int 	**A, **B, **C;   //Arrays to be created
	 int    width=0;   		 //Row size in bytes
	 int  	sent=0;    		 //Holds the number of sent rows
	 int    itag=1;    		 //Holds the data tag for point-to-point massage passings 
	 int 	iRank; 			 //The rank of a processor
	 int    row=0;   			   //used to traverse the rows of the array
	 

	 
	 MPI_Init(&argc, &argv);
	 
	 int	iWorldSize;
	 MPI_Comm_size( MPI_COMM_WORLD, &iWorldSize);
	
	 MPI_Comm_rank( MPI_COMM_WORLD, &iRank);	
/***************Executed on all processors**************/
  if(iRank==0)   //The processor is the master processor	
  { 
	  
	cout << "root process************"<< endl;
		 cout << "Please enter the number of rows of the first array:" <<  endl;
	 cin >> n;
	
	 cout << "Please enter the number of columns of the first array:" <<  endl;
	 cin >> m;
	
	 cout << "Please enter the number of columns of the second array:" <<  endl;
	 cin >> p;
/***********Creating an n*m array**********************/
          
	 A  = new int *[n];
	 
	 for(row=0;row<n;++row)
       A[row] = new int[m];
       
/***********Creating an m*p array**********************/
	 B  = new int *[m];
	 
	 for(row=0;row<m;++row)
       B[row] = new int[p];
/***********Creating an n*p array**********************/
	 C  = new int*[n];
	
	 for(row=0;row<n;++row)
	   C[row] = new int[p];
/***********************************************/
        int row, col;  //indices for rows  and columns
     
/********generating array A***********/
	 for(row=0;row<n;++row)
	   for(i=0;i<m;++i)
          A[row][i] = 2*i;
     	
/********generating array B***********/
     for(row=0;row<n;++row)
      for(i=0;i<p;++i)
	    B[row][i] = i;   
   
 
   i=1;
  
  cout << "world size = " << iWorldSize << "\n****\n****\n****" << endl;
	    while(i<iWorldSize)
	    {
			cout << "executing send for the " << i << "th time" <<  endl;
		   MPI_Send(&(A[i][0]), n/iWorldSize * m, MPI_INT, i, itag, MPI_COMM_WORLD);
		   cout << "executed send for the " << i << "th time, sending " <<
		         n/iWorldSize * m << " bytes " << endl;
		   
		   i++;
	   }//end while
	   
   MPI_Bcast(&m, 1, MPI_INT, 0,  MPI_COMM_WORLD);
   MPI_Bcast(&n, 1, MPI_INT, 0,  MPI_COMM_WORLD);   
   MPI_Bcast(&p, 1, MPI_INT, 0,  MPI_COMM_WORLD);     
   MPI_Bcast(&(B[0]), m*p, MPI_INT, 0, MPI_COMM_WORLD);
   }//END IF processor is the root
   else{//rank <> 0, the processor is a slave 
   
   cout << "my rank is " <<  iRank << endl;
/***********Creating an n*p array**********************/
	 C  = new int*[n];
	
	 for(row=0;row<n;++row)
	   C[row] = new int[p];

       
      int	sum;	   //holds each element of the product array
	  
	  MPI_Status status;   
	  
	  cout << "number of elements in array B equals " << sizeof(B) << endl;
	  	  
	  int *buffer = new int[1000];
      cout << "\n\n\n**********Executing receive*******" << endl;
      MPI_Recv(&buffer, 250, MPI_INT, 0, itag, MPI_COMM_WORLD, &status);
      if(status.MPI_SOURCE == 0)   
        for(i=0;i<(n/iWorldSize);++i)
          for(j=0;j<m;++j)
          {
           sum = 0;
           for(k=0;k<m;++k)
             sum += A[i][k] * B[k][j];
           C[i][j] = sum;
           cout << "computed element (" << i << ", " << j << ") of the product array"  
                << endl;
          }
       }//END ELSE rank<>0
    MPI_Finalize();
      
   return 0;
} 
