/******************************************************************************
* FILE: mpi_array_genom.c
* DESCRIPTION: 
*   
Exemple MPI - Affectation de tableau - 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 de mise à jour de la tranche du tableau 
et de comptage du nombre de 'A' dans chaque tranche du tableau.
Ils maintiennent également la somme de 'A'  pour leur partie du tableau. 
La tâche maître en fait de même avec sa partie du tableau. 
Lorsque chacunes des taches non maitre terminent le calcul, 
elles 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 'A' du tableau.

* REMARQUE: la taille du tableau doit être uniformément disible par le nombre de taches N.
* AUTHOR: 
* LAST REVISED: 11/01/13
****************************************************************************/

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

char  data[ARRAYSIZE];

int main (int argc, char *argv[])
{
int   numtasks, taskid, rc, dest, offset, i, j, tag1,
      tag2, source, chunksize; 
int mysum, sum;

float update(int myoffset, int chunk, int myid);
char Random_Nucleotide();
char Random(char m,char n);

MPI_Status status;

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

srand(time(NULL));

if (numtask % 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 initial de nucléotides */
  sum = 0;
  for(i=0; i<ARRAYSIZE; i++) {
    data[i] =  Random_Nucliotide();
    }

  /* 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_CHAR, 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_CHAR, 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_INT, MPI_SUM, MASTER, MPI_COMM_WORLD);
  
  printf("Sample results: \n"); 
  printf("*** Final sum= %d ***\n",sum);
  offset = 0;
  for (i=0; i<numtasks; i++) {
    for (j=0; j<chunksize; j++) 
      printf("  %c",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_CHAR, 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_INT, 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_CHAR, 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
int update(int myoffset, int chunk, int myid) {
  int i; 
  float mysum;
  /* clacul de l'addition de chaque element de ma tranche de tableau
   et garder la somme mysum */
  mysum = 0;
  
  for(i=myoffset; i < myoffset + chunk; i++) {
     //remplacement du nucleoitide R par A ou G 
    if (data[i]='R' data[i]= Random('A', 'G');
	//remplacement du nucleoitide W par A ou T 
	if (data[i]='W' data[i]= Random('A', 'T');
	//remplacement du nucleoitide H par A ou T 
	if (data[i]='H' data[i]= Random('A', 'C');
    }
  // comptage du nombre de nucleotides 'A'
 for(i=myoffset; i < myoffset + chunk; i++) 
  if data[i]=='A'  mysum++;
  
  printf("Task %d mysum = %d\n",myid,mysum);
  return(mysum);
  }

  // generation aléatoire d'un nucleotide
  char Random_Nucleotide(){
        char tab[6]= {'A','G', 'T', 'C', 'R', 'W','H'};
        return tab[(rand()%6)]; 
  }
  //choix aléatoire d'un nucleotide m ou n
  char Random(char m,char n){
  if( (rand()%2) == 0) return m
  else return n;
  }
