/******************************************************************************
* FILE: mpi_array.c
* DESCRIPTION: 
*   
Exemple MPI - Affectation Array - Version C
Ce programme illustre une décomposition simple des données. 
La tâche maître initialise d'abord un tableau de taille N et distribue ensuite à portion 
égale ce tableau pour les autres tâches. Après avoir reçu leurs portions de tableau
les exécutent une opération d'addition pour chaque élément de la tranche du tableau.
Ils maintiennent également une somme pour leur partie du tableau. 
La tâche maître en fait de même avec sa partie du tableau. 
Lorsque chacunes des tacjes non maitre terminent le calcul, ellesls envoient 
leur partie mise à jour du tableau pour le maître.
Un appel de communication MPI collective est utilisée pour collecter 
les sommes tenus par chaque tâche. 
Enfin, la tâche principale affiche tout le tableau mise à jour, 
et la somme globale de tous les éléments du tableau.

REMARQUE: le nombre de tâches MPI doit être uniformément disible par N.
* AUTHOR: 
* LAST REVISED: 04/13/05
****************************************************************************/

#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
#define  ARRAYSIZE	16000000
#define  MASTER		0

float update(int myoffset, int chunk, int myid);

float  data[ARRAYSIZE];

int main (int argc, char *argv[])
{
int   numtasks, taskid, rc, dest, offset, i, j, tag1,
      tag2, source, chunksize; 
float mysum, sum;
float update(int myoffset, int chunk, int myid);
MPI_Status status;

/***** Initialisations *****/
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);

if (numtasks % 4 != 0) {
   printf("Quitter. Nombre de processus doit être divisible par 4.\n");
   MPI_Abort(MPI_COMM_WORLD, rc);
   exit(0);
   }
   
MPI_Comm_rank(MPI_COMM_WORLD,&taskid);
printf ("traitement de la tache  %d a demarre...\n", taskid);
chunksize = (ARRAYSIZE / numtasks);
tag2 = 1;
tag1 = 2;

/***** tache maitre uniquement ******/
if (taskid == MASTER){

  /* Initialise le tableau */
  sum = 0;
  for(i=0; i<ARRAYSIZE; i++) {
    data[i] =  i * 1.0;
    //sum = sum + data[i];
    }
 // printf("tableau initialise, la somme = %e\n",sum);

  /* Envoie a chaque processus sa tranche de tableau - 
  le processus maitre garde la 1er tranche. 
  Offset du prochaine tache commence à partif de l'index egal
  a l'offset*/
  offset = chunksize;
  for (dest=1; dest<numtasks; dest++) {
    MPI_Send(&offset, 1, MPI_INT, dest, tag1, MPI_COMM_WORLD);
    MPI_Send(&data[offset], chunksize, MPI_FLOAT, dest, tag2, MPI_COMM_WORLD);
    printf("Envie %d elements a la tache %d offset= %d\n",chunksize,dest,offset);
    offset = offset + chunksize;
    }

  /* Maitre execute sa part du travail */
  offset = 0;
  mysum = update(offset, chunksize, taskid);

  /* attente de la reception des resultat de chaque tache */
  for (i=1; i<numtasks; i++) {
    source = i;
    MPI_Recv(&offset, 1, MPI_INT, source, tag1, MPI_COMM_WORLD, &status);
    MPI_Recv(&data[offset], chunksize, MPI_FLOAT, source, tag2,
      MPI_COMM_WORLD, &status);
    }

  /* Collecte la somme globale et affiche le tableau final puis la somme globale */  
  MPI_Reduce(&mysum, &sum, 1, MPI_FLOAT, MPI_SUM, MASTER, MPI_COMM_WORLD);
  
  printf("Sample results: \n"); 
  printf("*** Final sum= %e ***\n",sum);
  offset = 0;
  for (i=0; i<numtasks; i++) {
    for (j=0; j<chunksize; j++) 
      printf("  %e",data[offset+j]);
      printf("\n");
      offset = offset + chunksize;
    }
  

  }  /* fin de la section du traitement de la tache principale */



/***** uniquement les autres taches sans la tache maitre *****/

if (taskid > MASTER) {
  /* Reception de ma tranche de tableau de la tache principale */
  source = MASTER;
  MPI_Recv(&offset, 1, MPI_INT, source, tag1, MPI_COMM_WORLD, &status);
  MPI_Recv(&data[offset], chunksize, MPI_FLOAT, source, tag2, 
    MPI_COMM_WORLD, &status);

  /* je fais la mise à jour des elements de ma tranche et je calcule leur somme*/ 
  mysum = update(offset, chunksize, taskid);
  MPI_Reduce(&mysum, &sum, 1, MPI_FLOAT, MPI_SUM, MASTER, MPI_COMM_WORLD);
  /* Envoie mes resultats a la tache principale */
  dest = MASTER;
  MPI_Send(&offset, 1, MPI_INT, dest, tag1, MPI_COMM_WORLD);
  MPI_Send(&data[offset], chunksize, MPI_FLOAT, MASTER, tag2, MPI_COMM_WORLD);
 } /* fin du traitement d'une tache non maitre */


MPI_Finalize();

}   /* fin du main */


//fonction de mise à jour d'un tableau et le calcul de la somme de ses elements
float update(int myoffset, int chunk, int myid) {
  int i; 
  float mysum;
  /* Perform addition to each of my array elements and keep my sum */
  mysum = 0;
  for(i=myoffset; i < myoffset + chunk; i++) {
    data[i] = data[i] + i * 1.0;
    mysum = mysum + data[i];
    }
  printf("Task %d mysum = %e\n",myid,mysum);
  return(mysum);
  }

