/* Copyright 2008 BORDEAUX I UNIVERSITY & INRIA 
**
** This file is part of the PaStiX parallel sparse matrix package.
**
** This software is governed by the CeCILL-C license under French law
** and abiding by the rules of distribution of free software. You can
** use, modify and/or redistribute the software under the terms of the
** CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
** URL: "http://www.cecill.info".
** 
** As a counterpart to the access to the source code and rights to copy,
** modify and redistribute granted by the license, users are provided
** only with a limited warranty and the software's author, the holder of
** the economic rights, and the successive licensors have only limited
** liability.
** 
** In this respect, the user's attention is drawn to the risks associated
** with loading, using, modifying and/or developing or reproducing the
** software by the user in light of its specific status of free software,
** that may mean that it is complicated to manipulate, and that also
** therefore means that it is reserved for developers and experienced
** professionals having in-depth computer knowledge. Users are therefore
** encouraged to load and test the software's suitability as regards
** their requirements in conditions enabling the security of their
** systems and/or data to be ensured and, more generally, to use and
** operate it in the same conditions as regards security.
** 
** The fact that you are presently reading this means that you have had
** knowledge of the CeCILL-C license and that you accept its terms.
*/
/**
 * @file starpu_zdefines.h
 *
 * @author Xavier Lacoste
 * @precisions normal z -> s d c
 */
#ifndef STARPU_ZDEFINES_H
#define STARPU_ZDEFINES_H

#ifdef STARPU_USE_DEPRECATED_API
#  undef STARPU_USE_DEPRECATED_API
#endif
#ifdef STARPU_USE_DEPRECATED_ONE_ZERO_API
#  undef STARPU_USE_DEPRECATED_ONE_ZERO_API
#endif
#  ifdef FORCE_NOMPI
#    include "nompi.h"
#  else
#    include <mpi.h>
#  endif
#include <starpu.h>
#include "common_pastix.h"
#ifndef FORCE_NOMPI
#  include <starpu_mpi.h>
#endif
#include <starpu_profiling.h>

#include "sopalin_define.h"
#include "sopalin_acces.h"
#include "symbol.h"
#include "ftgt.h"
#include "csc.h"
#include "updown.h"
#include "queue.h"
#include "bulles.h"
#include "solver.h"
#include "sopalin_thread.h"
#include "sopalin_time.h"
#include "sopalin3d.h"

#if (STARPU_MAJOR_VERSION < 1)
#  error "PaStiX requires STARPU >= 1"
#endif /* STARPU_MAJOR_VERSION */

#ifdef FORCE_NOMPI
#  define starpu_mpi_init(a, ...)            starpu_init(a)
#  define starpu_mpi_insert_task(a, b, ...)  starpu_insert_task(b, __VA_ARGS__)
#  define starpu_mpi_data_register(...)
#  define starpu_mpi_shutdown()
#  define starpu_data_get_rank(...)          0
#  define starpu_mpi_cache_flush(...)
#  define starpu_mpi_cache_flush_all_data(...)
#endif

//#define STARPU_HACK_TAG
#ifdef STARPU_HACK_TAG
/* hack to get the first tag on the current communicator
 * TAGS :
 *     L        - [0:solvmtx->gcblknbr-1]
 *     U        - [solvmtx->gcblknbr:2*solvmtx->gcblknbr-1]
 *     Lfanin   - [2*solvmtx->gcblknbr:solvmtx->gcblknbr*(clustnbr+2)-1]
 *     Ufanin   - [solvmtx->gcblknbr*(clustnbr+2):solvmtx->gcblknbr*(clustnbr*2+2)-1]
 *     blocktab - [SOLV_GCBLKNBR*(SOLV_PROCNBR*2+2):SOLV_GCBLKNBR*(SOLV_PROCNBR*2+2)+SOLV_PROCNBR-1]
 *     work     - SOLV_GCBLKNBR*(SOLV_PROCNBR*2+2)+SOLV_PROCNBR
 */
static inline
int starpu_get_first_tag(MPI_Comm comm, SolverMatrix * datacode) {
  int size, grank, idcomm;
  MPI_Comm_size(comm, &size);
  MPI_Comm_rank(MPI_COMM_WORLD, &grank);
  idcomm = grank/size;
  fprintf(stdout, "First TAG %d\n", idcomm*(SOLV_GCBLKNBR*(SOLV_PROCNBR*2+2)+SOLV_PROCNBR+1));
  return idcomm*(SOLV_GCBLKNBR*(SOLV_PROCNBR*2+2)+SOLV_PROCNBR+1);
}

static inline
int starpu_get_first_rank(MPI_Comm comm) {
  int size, rank, grank, idcomm;
  MPI_Comm_size(comm, &size);
  MPI_Comm_rank(MPI_COMM_WORLD, &grank);
  idcomm = grank/size;
  fprintf(stdout, "First rank %d\n", idcomm*size);
  return idcomm*size;
}
#  define starpu_mpi_insert_task(a, b, ...)  starpu_mpi_insert_task(MPI_COMM_WORLD, b, __VA_ARGS__)
#  define starpu_data_get_rank(h) (starpu_data_get_rank(h) - starpu_get_first_rank(sopalin_data->sopar->pastix_comm))
#  define starpu_mpi_data_register(h,t,r) starpu_mpi_data_register(h, starpu_get_first_tag(sopalin_data->sopar->pastix_comm, sopalin_data->datacode)+t, starpu_get_first_rank(sopalin_data->sopar->pastix_comm) + r)
#elif !defined(FORCE_NOMPI)
#  define starpu_mpi_data_register(h,t,r) starpu_mpi_data_register_comm(h, t, r, sopalin_data->sopar->pastix_comm)
#  define starpu_data_get_tag starpu_mpi_data_get_tag
#  define starpu_data_get_rank starpu_mpi_data_get_rank
#endif

#ifdef USE_TASK_DEP
#  undef USE_TASK_DEP
#endif
#if (STARPU_MAJOR_VERSION == 1 &&  STARPU_MINOR_VERSION == 0)
/* 1.1 : starpu_perf_archtype => starpu_perfmodel_archtype
 *       starpu_profiling_worker_info => starpu_profiling_worker_info
 */
#  define STARPU_1_0
#  define starpu_perfmodel_archtype        starpu_perf_archtype
#  define starpu_profiling_worker_info     starpu_worker_profiling_info
#  define starpu_profiling_task_info       starpu_task_profiling_info
#  define starpu_profiling_worker_get_info starpu_worker_get_profiling_info
#  define starpu_profiling_set_id          starpu_set_profiling_id
#endif

#if (STARPU_MAJOR_VERSION == 1 &&  STARPU_MINOR_VERSION == 1)
#  define STARPU_1_0
#  define STARPU_1_1
#endif

#if (STARPU_MAJOR_VERSION == 1 &&  STARPU_MINOR_VERSION == 2)
#  define STARPU_1_0
#  define STARPU_1_1
#  define STARPU_1_2
#  define STARPU_COMMUTABLE
#  define STARPU_WITH_ASYNC_CUDA
#endif

#ifndef STARPU_COMMUTABLE
#  define STARPU_COMMUTE 0
#endif

#define ARCH_CPU  0
#define ARCH_CUDA 1
#define SUBMIT_FANIN_IF_NEEDED                                          \
    do {                                                                \
        PASTIX_INT faninnum = fcblk_getnum(datacode,                \
                                             fcblk,                     \
                                             SOLV_PROCNUM);             \
        PASTIX_INT *fanin_ctrbcnt;                                    \
        fanin_ctrbcnt = sopalin_data->fanin_ctrbcnt;                    \
        fanin_ctrbcnt[faninnum]--;                                      \
        if (fanin_ctrbcnt[faninnum] == 0) {                             \
            PASTIX_INT hcblknum;                                      \
            SolverCblk * hcblk;                                       \
            hcblknum = SOLV_GCBLK2HALO(fcblk->gcblknum);                \
            hcblk = datacode->hcblktab + hcblknum;                      \
            starpu_submit_outgoing_fanin( sopalin_data,                 \
                                          fcblk,                        \
                                          hcblk);                       \
        }                                                               \
    } while (0)

// NOTE: The STARPU_FANIN_IF_NEEDED requires mutex protection if StarPU revision is
// lower than 12794
#define SUBMIT_TRF_IF_NEEDED                                            \
    do {                                                                \
      PASTIX_INT * iparm = sopalin_data->sopar->iparm;                  \
      if (iparm[IPARM_STARPU_NESTED_TASKS] == API_YES) {                \
        SolverMatrix *datacode = sopalin_data->datacode;                \
        if (cblk_islocal(datacode, fcblk)) {                            \
          PASTIX_INT fcblknum = cblk_getnum(datacode, fcblk);           \
          TASK_CTRBCNT(fcblknum)--;                                     \
          if ( ( ( TASK_CTRBCNT(fcblknum) == 0 &&                       \
                   iparm[IPARM_STARPU_FANIN] == API_YES) ||             \
                 ( iparm[IPARM_STARPU_FANIN] == API_NO &&               \
                   TASK_CTRBCNT(fcblknum) ==                            \
                   sopalin_data->fanout_ctrbcnt_in[                     \
                     TASK_CBLKNUM(fcblknum)]) ) ) {                     \
            starpu_zsubmit_one_trf(fcblknum, sopalin_data);             \
          }                                                             \
        } else {                                                        \
          if ( iparm[IPARM_STARPU_FANIN] == API_YES ) {                 \
            SUBMIT_FANIN_IF_NEEDED;                                     \
          }                                                             \
        }                                                               \
      }                                                                 \
    } while(0)


#define SUBMIT_GEMMS_IF_NEEDED                                          \
        do {                                                            \
            char * nested;                                              \
            SolverMatrix *datacode = sopalin_data->datacode;            \
            PASTIX_INT * iparm = sopalin_data->sopar->iparm;            \
            PASTIX_INT tasknum = cblk_getnum(datacode, cblk);           \
            if (iparm[IPARM_STARPU_NESTED_TASKS == API_YES) {           \
                starpu_zsubmit_bunch_of_gemm(tasknum, sopalin_data);    \
            }                                                           \
        } while(0)

struct starpu_loop_data_ {
    int                     me;
    starpu_data_handle_t *  L_handle;
    starpu_data_handle_t *  Lhalo_handle;
    starpu_data_handle_t *  U_handle;
    starpu_data_handle_t *  Uhalo_handle;
    starpu_data_handle_t ** Lfanin_handle;
    starpu_data_handle_t ** Ufanin_handle;
    starpu_data_handle_t    WORK_handle;
    starpu_data_handle_t *  blocktab_handles;
    Sopalin_Data_t       *  sopalin_data;
    int                     ctx;
    int                     ctx_nbr;
    int                     thread_per_ctx;
    int                  *  sched_ctxs;
    int                     first, last;
    int                   * facto_finished;
    pthread_cond_t        * cond_end_facto;
    pthread_mutex_t       * mutex_end_facto;
    int                   * cpu_workerids;
    int                     ncpus;
    int                   * gpu_workerids;
    int                     ngpus;
    int                   * gpu_gemm_count;
    PASTIX_INT            * pivot_cnt;
};

#define PASTIX_SUCCESS NO_ERR

#endif /* STARPU_ZDEFINES_H */
