#include <omp.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[]) {
  /*omp_set_num_threads(4);
  printf("je suis le master thread\n\n");
  #pragma omp parallel
  {
  //  #pragma omp critical
    printf("Je suis un thread soumis :< mon nul est %d\n",omp_get_thread_num());
  }

  printf("au dodo les esclaves\n");*/

  int a=2;
  int b=3;
  int c=20;

  omp_set_num_threads(4);
  printf("je suis le master thread\n\n");
  #pragma omp parallel firstprivate(a) shared(b)
  {
    #pragma omp critical
    {
      a++;
      b++;
      printf("Thread %d a : %d b : %d\n",omp_get_thread_num(),a,b);
    }
    #pragma omp single
    {
      a++;
      b++;
      printf("Single Thread %d a : %d b : %d\n",omp_get_thread_num(),a,b);
    }

    #pragma omp reduction(+:c)
    {
      c=c+omp_get_thread_num();
      printf("Thread %d c : %d\n",omp_get_thread_num(),c);
    }
  }
  printf("\nMaster thread a : %d b : %d\n",a,b);
  printf("c : %d\n",c);
  #pragma omp parallel reduction(+:c)
  {
    c=c+omp_get_thread_num();
    printf("Thread %d c : %d\n",omp_get_thread_num(),c);
  }
printf("c : %d\n",c);
  //reduction -> résultat final d'unbe opération associative (+,*,max,min,and,or)
printf("\n\n\n");
int i, id;
const int SIZE = 17;
int A[17];
for (int i = 0; i < SIZE; i++) {
  A[i]=5;
}
for (int i = 0; i < SIZE; i++) {
  printf("a[%d] = %d\n",i,A[i]);
}

  #pragma omp parallel
  {
    #pragma omp for private(id)
    for (i=0;i<SIZE;i++){
      id = omp_get_thread_num();
      printf("execution de %d\n",id);
      A[i] = id;
    }
  }
  for (int i = 0; i < SIZE; i++) {
    printf("a[%d] = %d\n",i,A[i]);
  }


  //  #pragma omp lastprivate(a) sort la derniere valeur de a prise par la derniere thread executé

  // #pragma omp for schedule(static[,chunk]) iteration divisé en blocs de taille chunk
    //les chunks sont assignes aux thread en round robin
    //si chunk non specifié -> division par blocs sooit~ num_it/noùbre thread

  // #pragma omp for schedule(dynamic[,chunk])
    //similaire mais chunk attribué au premier dispo

  // #pragma omp for schedule(guided[,chunk]) ichunk initialisé a num_it/num_thread
    //chunk de plus en plus petit


  // #pragma omp for schedule(runtime) Selon la var d'environement OMP_SCHEDULE


  // #pragma omp for schedule(auto)  auto, on laisse le pc faire


  /*--------------------------------------------------------------------*/

  //#prama omp for collapse(2) -> fait s'ecrouler les boucles ensembles
  /*
  #prama omp for collapse(2)
  for (i;i<10,i++)
    for(j;j<10;j++)
      fct(i,j)

      =>
      for(i,i<100,i++)
      .......
*/
//--------------------------------------------------------------------------------
/*partage de travail
#pragma omp sections [clause]
    {
      #pragma omp section
        bloc de code
      #pragma omp section
        bloc de code
    }

*/

//SYNCHRO DES Threads


/*
  #pragma omp master
    bloc de code
-> uniquement master

  #pragma omp critical[(nom)]
    bloc de code
-> permet de definir plusieurs region critique

  #pragma omp atomic
    sentence de code
->protéger les ecritures des shared

  #pragma omp barrier
-> fait s'attendre les threads

  #pragma omp ordered
    bloc de code
-> etre sur de la bonne execution de la boucle
--> forcez lexecution en sequentiel


*/
  return 0;
}
