/****************************************************************************
**  SCALASCA    http://www.scalasca.org/                                   **
**  KOJAK       http://www.fz-juelich.de/zam/kojak/                        **
*****************************************************************************
**  Copyright (c) 1998-2008                                                **
**  Forschungszentrum Juelich, Zentralinstitut fuer Angewandte Mathematik  **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#ifdef OPENMP
#  include <omp.h>
#endif
#include <mpi.h>
#include "epik_user.h"

double do_work(int units) {
  int i, j;
  double x1, x2;

  for (i=0; i<units; ++i) {
    for (j=0; j<10000; ++j) {
      x1 = j*3.1415;
      x2 = x1 * 42.0 * (x1 - 42.0);
    }
  }
  return x2;
}

void step(int id, int tid, int i) {
  EPIK_FUNC_START();

  /* printf("%d:%d: step %d\n", id, tid, i); */
  do_work(100);

  EPIK_FUNC_END();
}

void sequential(int id) {
  int i, tid=0;
  EPIK_FUNC_START();

  do_work(100);
  #pragma omp parallel private(tid)
  {
    #ifdef OPENMP
    tid = omp_get_thread_num();
    #endif
    #pragma omp for
    for (i=0; i<12; i++) {
      step(id, tid, i);
    }
  }
  do_work(100);

  EPIK_FUNC_END();
}

void p2p(MPI_Comm comm) {
  int id=0, numprocs, value=42, i, dest;
  MPI_Request req;
  MPI_Status stat;
  EPIK_FUNC_START();

  MPI_Comm_size(comm, &numprocs);
  MPI_Comm_rank(comm, &id);

  MPI_Barrier(comm);
  for (i=0; i<5; i++) {
    if ( id % 2 == 0 ) {
      value = 42;
      dest = (id+1+numprocs) % numprocs;
      if ( dest != id && dest < numprocs ) {
        MPI_Isend(&value, 1, MPI_INT, dest, 42, comm, &req);
        MPI_Wait(&req, &stat);
      }
    } else {
      value = 0;
      MPI_Irecv(&value, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, comm, &req);
      MPI_Wait(&req, &stat);
    }
  }
  MPI_Barrier(comm);

  EPIK_FUNC_END();
}

void parallel(MPI_Comm comm) {
  int id=0, numprocs, value=42;
  EPIK_USER_REG(r_init,"<<init>>");
  EPIK_FUNC_START();

  EPIK_USER_START(r_init);
  MPI_Comm_size(comm, &numprocs);
  MPI_Comm_rank(comm, &id);
  MPI_Bcast(&value, 1, MPI_INT, numprocs-1, comm);
  EPIK_USER_END(r_init);

  sequential(id);
  MPI_Barrier(comm);
  sequential(id);
  MPI_Barrier(comm);
  sequential(id);
  MPI_Barrier(comm);

  p2p(comm);

  EPIK_FUNC_END();
}

int main(int argc, char* argv[]) {
  int id=0, numprocs;
  int color;
  MPI_Comm local;
  EPIK_USER_REG(r_init,"<<init>>");
  EPIK_USER_REG(r_fini,"<<fini>>");
  EPIK_FUNC_START();

  EPIK_USER_START(r_init);
  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
  MPI_Comm_rank(MPI_COMM_WORLD, &id);
  printf("%03d: ctest-man start\n", id);
  color = (id >= numprocs/2);
  MPI_Comm_split(MPI_COMM_WORLD, color, id, &local);
  EPIK_USER_END(r_init);

  parallel(MPI_COMM_WORLD);
  parallel(local);
  parallel(MPI_COMM_WORLD);

  EPIK_USER_START(r_fini);
  MPI_Comm_free(&local);
  MPI_Finalize();
  printf("%03d: ctest-man end\n", id);
  EPIK_USER_END(r_fini);

  EPIK_FUNC_END();
  return 0;
}