/* 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.
*/
/**
 *
 *  PaStiX is a software package provided by Inria Bordeaux - Sud-Ouest,
 *  LaBRI, University of Bordeaux 1 and IPB.
 *
 * @version 5.2.0
 * @author Mathieu Faverge
 * @author Pierre Ramet
 * @author Xavier Lacoste
 * @date 2011-11-11
 * @precisions normal z -> c d s
 *
 **/
#ifdef WITH_MAGMABLAS
#include <magmablas.h>
#endif /* WITH_MAGMABLAS */

#include "starpu_submit.h"
#include "starpu_defines.h"
#include "common_pastix.h"
#include "sopalin_thread.h"
#include "sopalin_acces.h"
#include "sopalin3d.h"
#include "sopalin_compute.h"
#include "starpu_kernels.h"
#include "starpu_submit_tasks.h"
#include "core_zgeadd.h"

#define pastix_complex64_t PASTIX_FLOAT
#define z_cblk_colnbr cblk_colnbr
#define z_cblk_save(a,b,c)
#define z_CORE_gemdm  API_CALL(CORE_gemdm)

int z_CORE_gemdm(int transA, int transB,
                   pastix_int_t M, pastix_int_t N, pastix_int_t K,
                   pastix_complex64_t alpha, pastix_complex64_t *A, int LDA,
                   pastix_complex64_t *B, int LDB,
                   pastix_complex64_t beta, pastix_complex64_t *C, int LDC,
                   pastix_complex64_t *D, int incD,
                   pastix_complex64_t *WORK, int LWORK);

#ifdef WITH_CUDA
#include "pastix_cuda_helper.h"
#  include "sparse_gemm_fermi.h"
#endif

#include <inttypes.h>

#ifdef STARPU_USE_CUDA
#if ((!defined PREC_DOUBLE)  || (!(defined __CUDA_ARCH__) || __CUDA_ARCH__ >= 130))
#if !(defined PREC_DOUBLE && defined TYPE_COMPLEX && CUDA_SM_VERSION < 20)
#      ifdef WITH_CUDA
#define STARPU_USE_CUDA_GEMM_FUNC
#endif
#endif
#endif
#endif


#if (!defined STARPU_USE_CUDA || !defined WITH_CUDA)
#  ifdef  WITH_MAGMABLAS
#    undef  WITH_MAGMABLAS
#  endif /* WITH_MAGMABLAS */
#endif /* !STARPU_USE_CUDA || !WITH_CUDA*/


#ifdef WITH_MAGMABLAS
#include "geadd_cuda.h"
#include "getra_cuda.h"
#include "zgetrf_stapiv_gpu.h"
#endif /* WITH_MAGMABLAS */

#define PASTIX_getrf       API_CALL(PASTIX_getrf)
#define PASTIX_potrf       API_CALL(PASTIX_potrf)
#define PASTIX_sytrf       API_CALL(PASTIX_sytrf)
#define PASTIX_hetrf       API_CALL(PASTIX_hetrf)
#define PASTIX_getrf_block API_CALL(PASTIX_getrf_block)
#define PASTIX_potrf_block API_CALL(PASTIX_potrf_block)
#define PASTIX_sytrf_block API_CALL(PASTIX_sytrf_block)
#define PASTIX_hetrf_block API_CALL(PASTIX_hetrf_block)
#define DimTrans                API_CALL(DimTrans)
#define factor_diag        API_CALL(factor_diag)
#define z_PASTIX_getrf       API_CALL(PASTIX_getrf)
#define z_PASTIX_potrf       API_CALL(PASTIX_potrf)
#define z_PASTIX_sytrf       API_CALL(PASTIX_sytrf)
#define z_PASTIX_hetrf       API_CALL(PASTIX_hetrf)
#define z_PASTIX_getrf_block API_CALL(PASTIX_getrf_block)
#define z_PASTIX_potrf_block API_CALL(PASTIX_potrf_block)
#define z_PASTIX_sytrf_block API_CALL(PASTIX_sytrf_block)
#define z_PASTIX_hetrf_block API_CALL(PASTIX_hetrf_block)
#define z_DimTrans           API_CALL(DimTrans)
#define z_factor_diag        API_CALL(factor_diag)
#define z_kernel_trsm        API_CALL(kernel_trsm)
void PASTIX_getrf       ( PASTIX_FLOAT *A, PASTIX_INT m, PASTIX_INT n, PASTIX_INT lda, PASTIX_INT *npvt, double crit );
void PASTIX_potrf       ( PASTIX_FLOAT *A, PASTIX_INT n,        PASTIX_INT lda, PASTIX_INT *npvt, double crit );
void PASTIX_sytrf       ( PASTIX_FLOAT *A, PASTIX_INT n,        PASTIX_INT lda, PASTIX_INT *npvt, double crit );
void PASTIX_hetrf       ( PASTIX_FLOAT *A, PASTIX_INT n,        PASTIX_INT lda, PASTIX_INT *npvt, double crit );
void PASTIX_getrf_block ( PASTIX_FLOAT *A, PASTIX_INT m, PASTIX_INT n, PASTIX_INT lda, PASTIX_INT *npvt, double crit );
void PASTIX_potrf_block ( PASTIX_FLOAT *A, PASTIX_INT n,        PASTIX_INT lda, PASTIX_INT *npvt, double crit );
void PASTIX_sytrf_block ( PASTIX_FLOAT *A, PASTIX_INT n,        PASTIX_INT lda, PASTIX_INT *npvt, double crit, PASTIX_FLOAT * tmp4   );
void PASTIX_hetrf_block ( PASTIX_FLOAT *A, PASTIX_INT n,        PASTIX_INT lda, PASTIX_INT *npvt, double crit, PASTIX_FLOAT * tmp4   );
void DimTrans           ( PASTIX_FLOAT *A, PASTIX_INT lda, PASTIX_INT size, PASTIX_FLOAT *B );
#if !(defined STARPU_USE_CUDA_GEMM_FUNC)
#define CUDA_SPARSE_GEMM(TRANSA, TRANSB,                \
                         dimi, dimj, dima,              \
                         alpha,                         \
                         A,  stride_A,                  \
                         B,  stride_B,                  \
                         beta,                          \
                         C, stride_C,                   \
                         blocknbr,  blocktab,           \
                         fblocknbr, fblocktab)          \
  do {                                                  \
  } while(0)
#define CUDA_SPARSE_GEMDM(TRANSA, TRANSB,                \
                          dimi, dimj, dima,              \
                          alpha,                         \
                          A,  stride_A,                  \
                          D,  stride_D,                  \
                          B,  stride_B,                  \
                          beta,                          \
                          C, stride_C,                   \
                          blocknbr,  blocktab,           \
                          fblocknbr, fblocktab)          \
  do {                                                   \
    assert(0);                                           \
    /* avoid warnings */                                 \
    assert(fblocktab);                                   \
    assert(blocktab);                                    \
    assert(fblocknbr);                                   \
    assert(blocknbr);                                    \
    assert(beta);                                        \
    assert(alpha);                                       \
    assert(dima);                                        \
    assert(dimj);                                        \
    assert(dimi);                                        \
    assert(stridefc);                                    \
  } while(0)
#else
#if (CUDA_SM_VERSION >= 20)
#ifdef STARPU_1_2
#  define STREAM_SYNCHRONIZE do {} while (0)
#else
#  define STREAM_SYNCHRONIZE do {                               \
      cudaStreamSynchronize(starpu_cuda_get_local_stream());    \
    } while(0)
#endif
#define CUDA_SPARSE_GEMM(TRANSA, TRANSB,                                \
                         dimi, dimj, dima,                              \
                         alpha,                                         \
                         A,  stride_A,                                  \
                         B,  stride_B,                                  \
                         beta,                                          \
                         C, stride_C,                                   \
                         blocknbr, blocktab,                            \
                         fblocknbr, fblocktab)                          \
  do {                                                                  \
    CU_FLOAT cu_alpha = CU_FLOAT_INIT(creal(alpha), cimag(alpha));      \
    CU_FLOAT cu_beta  = CU_FLOAT_INIT(creal(beta),  cimag(beta));       \
    GENERATE_SM_VERSION_NAME(gemm)(*TRANSA, *TRANSB,                    \
                                   (int)dimi, (int)dimj, (int)dima,     \
                                   cu_alpha,                            \
                                   (CU_FLOAT*)A, (int)stride_A,         \
                                   (CU_FLOAT*)B, (int)stride_B,         \
                                   cu_beta,                             \
                                   (CU_FLOAT*)C, (int)stride_C,         \
                                   blocknbr,  blocktab,                 \
                                   fblocknbr, fblocktab,                \
                                   starpu_cuda_get_local_stream());     \
    STREAM_SYNCHRONIZE;                                                 \
  } while(0)
#define CUDA_SPARSE_GEMDM(TRANSA, TRANSB,                               \
                          dimi, dimj, dima,                             \
                          alpha,                                        \
                          A,  stride_A,                                 \
                          D, stride_D,                                  \
                          B,  stride_B,                                 \
                          beta,                                         \
                          C, stride_C,                                  \
                          blocknbr, blocktab,                           \
                          fblocknbr, fblocktab)                         \
    do {                                                                \
      CU_FLOAT cu_alpha = CU_FLOAT_INIT(creal(alpha), cimag(alpha));    \
      CU_FLOAT cu_beta  = CU_FLOAT_INIT(creal(beta),  cimag(beta));     \
      GENERATE_SM_VERSION_NAME(gemdm)(*TRANSA, *TRANSB,                 \
                                      (int)dimi, (int)dimj, (int)dima,  \
                                      cu_alpha,                         \
                                      (CU_FLOAT*)A, (int)stride_A,      \
                                      (CU_FLOAT*)D, (int)stride_D,      \
                                      (CU_FLOAT*)B, (int)stride_B,      \
                                      cu_beta,                          \
                                      (CU_FLOAT*)C, (int)stride_C,      \
                                      blocknbr,  blocktab,              \
                                      fblocknbr, fblocktab,             \
                                      starpu_cuda_get_local_stream());  \
      STREAM_SYNCHRONIZE;                                               \
    } while(0)
#else
#define CUDA_SPARSE_GEMM(TRANSA, TRANSB,                                \
                         dimi, dimj, dima,                              \
                         alpha,                                         \
                         A,  stride_A,                                  \
                         B,  stride_B,                                  \
                         beta,                                          \
                         C, stride_C,                                   \
                         blocknbr, blocktab,                            \
                         fblocknbr, fblocktab)                          \
  do {                                                                  \
    magmablas_sparse_gemm_kernel_N_T_64_16_4_16_4((int)dimi,            \
                                                  (int)dimj,            \
                                                  (int)dima,            \
                                                  (float)alpha,         \
                                                  A,                    \
                                                  (int)stride_A,        \
                                                  B,                    \
                                                  (int)stride_B,        \
                                                  (float)beta,          \
                                                  C,                    \
                                                  (int)stride_C,        \
                                                  blocknbr,             \
                                                  blocktab,             \
                                                  fblocknbr,            \
                                                  fblocktab);           \
    STREAM_SYNCHRONIZE;                                                 \
  } while(0)
#define CUDA_SPARSE_GEMDM(TRANSA, TRANSB,                               \
                          dimi, dimj, dima,                             \
                          alpha,                                        \
                          A,  stride_A,                                 \
                          D,  stride_D,                                 \
                          B,  stride_B,                                 \
                          beta,                                         \
                          C, stride_C,                                  \
                          blocknbr, blocktab,                           \
                          fblocknbr, fblocktab)                         \
  do {                                                                  \
    magmablas_sparse_gemdm_kernel_N_T_64_16_4_16_4((int)dimi,           \
                                                   (int)dimj,           \
                                                   (int)dima,           \
                                                   (float)alpha,        \
                                                   A,                   \
                                                   (int)stride_A,       \
                                                   D,                   \
                                                   (int)stride_D,       \
                                                   B,                   \
                                                   (int)stride_B,       \
                                                   (float)beta,         \
                                                   C,                   \
                                                   (int)stride_C,       \
                                                   blocknbr,            \
                                                   blocktab,            \
                                                   fblocknbr,           \
                                                   fblocktab);          \
    STREAM_SYNCHRONIZE;                                                 \
  } while(0)
#endif
#endif


#define DECLARE_ARGS_GEMM                       \
    z_Sopalin_Data_t *sopalin_data;             \
    z_SolverMatrix   *datacode;                 \
    pastix_int_t      bloknum;                  \
    pastix_int_t      stride;                   \
    pastix_int_t      indblok;                  \
    pastix_int_t      dimi;                     \
    pastix_int_t      dimj;                     \
    pastix_int_t      dima;                     \
    int              *blocktab;                 \
    z_SolverCblk     *cblk, *fcblk;             \
    z_SolverBlok     *firstBlok;		\
    z_SolverBlok     *blok, *_bloktab, *_fbloktab, *_fblok; \
    int              *fblocktab;                \
    int               blocknbr

#define UNPACK_ARGS_GEMM(_args, sopalin_data, bloknum,			\
			 all_blocktab, datacode,			\
			 indblok,					\
			 blocktab, fblocktab, stride, dimi, dimj, dima,\
                         blocknbr)                                              \
    do {                                                                \
      starpu_codelet_unpack_args(_args, &sopalin_data,                  \
                                 &cblk, &blok, &fcblk);                 \
      datacode  = sopalin_data->datacode;                               \
      if (z_cblk_islocal(datacode, fcblk)) {                            \
        fblocktab = &(all_blocktab[2*fcblk->bloknum]);			\
	_fbloktab = datacode->bloktab;					\
      } else {                                                          \
        assert(z_cblk_isfanin(datacode, fcblk));                        \
	_fbloktab = datacode->fbloktab[SOLV_PROCNUM];			\
	      }								\
      if (z_cblk_ishalo(datacode, cblk)) {                              \
        /* HALO Gemm*/                                                  \
        bloknum = blok-datacode->hbloktab;                              \
        blocktab  = &(all_blocktab[2*(SYMB_BLOKNBR + bloknum)]);        \
	_bloktab =datacode->hbloktab;					\
      } else {                                                          \
        assert(z_cblk_islocal(datacode, cblk));				\
	bloknum = blok-datacode->bloktab;				\
	blocktab  = &(all_blocktab[2*bloknum]);				\
	_bloktab=datacode->bloktab;					\
      }                                                                 \
      blocknbr = cblk[1].bloknum - bloknum;                             \
      firstBlok = _bloktab+cblk->bloknum;				\
      assert(blok >= firstBlok);					\
      assert(blok <  _bloktab+cblk[1].bloknum);				\
      _fblok = _fbloktab +fcblk->bloknum;				\
      assert(_fblok >= _fbloktab+fcblk->bloknum);			\
      assert(_fblok <  _fbloktab+fcblk[1].bloknum);			\
      stride = cblk->stride;                                            \
      indblok = blok->coefind;                                          \
      dimi = stride - indblok;                                          \
      dimj = blok_rownbr(blok);						\
      dima = z_cblk_colnbr(cblk);                                       \
    } while (0)

#define DECLARE_ARGS_TRSM                                               \
        z_Sopalin_Data_t    * sopalin_data;                               \
        z_SolverMatrix      * datacode;                                   \
        pastix_int_t          tasknum;                                  \
        z_SolverCblk * cblk;                                              \
        pastix_int_t          cblknum;                                  \
        pastix_int_t          fblknum;                                  \
        pastix_int_t          lblknum;                                  \
        pastix_int_t          dima;                                     \
        pastix_int_t          dimb



#define UNPACK_ARGS_TRSM(_args, sopalin_data, cblknum, tasknum,         \
                         datacode, fblknum, lblknum, dima, dimb)        \
        do {                                                            \
            starpu_codelet_unpack_args(_args, &sopalin_data, &cblk);    \
            datacode = sopalin_data->datacode;                          \
            cblknum  = cblk - datacode->cblktab;                        \
            tasknum  = cblknum;                                         \
            fblknum  = cblk->bloknum;                                   \
            lblknum  = cblk[1].bloknum;                                 \
            dima     = cblk->lcolnum - cblk->fcolnum + 1;               \
            dimb     = stride - dima;                                   \
        } while (0)

/* #define SOPALIN_SPARSE_GEMM(TRANSA, TRANSB,                     \ */
/*                             dimi, dimj, dima,                   \ */
/*                             alpha,  A,  stride_A,               \ */
/*                             B,  stride_B,                       \ */
/*                             beta,  C, stride_C,                 \ */
/*                             nblocs, blocs_idx, facing_bloc_idx, \ */
/*                             wtmp, wtmpsize)                     \ */
/*   do {                                                          \ */
/*     sparse_gemm(TRANSA, TRANSB,                                 \ */
/*                 dimi, dimj, dima,                               \ */
/*                 alpha,  A,  stride_A,                           \ */
/*                 B,  stride_B,                                   \ */
/*                 beta,  C, stride_C,                             \ */
/*                 nblocs, blocs_idx, facing_bloc_idx,             \ */
/*                 wtmp, wtmpsize);                                \ */
/*   } while(0) */

#define SOPALIN_SPARSE_GEMM(TRANSA, TRANSB,                 \
                            dimi, dimj, dima,               \
                            alpha,  A,  stride_A,           \
                            B,  stride_B,                   \
                            beta,  C, stride_C,             \
                            blocknbr, blocktab,             \
                            fblocknbr, fblocktab,           \
                            wtmp, wtmpsize)                 \
  do {                                                      \
    assert(0);                                              \
    assert(wtmp); assert(wtmpsize);                         \
    /* Avoid warnings */                                    \
    assert(fblocktab);                                      \
    assert(blocktab );                                      \
    assert(fblocknbr);                                      \
    assert(blocknbr );                                      \
    assert(beta     );                                      \
    assert(alpha    );                                      \
    assert(dima     );                                      \
    assert(dimj     );                                      \
    assert(dimi     );                                      \
    assert(stridefc );                                      \
  } while(0)

#ifdef TYPE_COMPLEX
#  ifdef PREC_DOUBLE
#    define CUDA_TRSM(RL, UL, transa, transb, dimb, dima,        \
                      one_cuf,                                   \
                      A, stridea,                                \
                      B, strideb) do {                           \
      cublasZtrsm(RL, UL, transa, transb, dimb, dima, one_cuf,   \
                  (CU_FLOAT*)((void*)A), stridea,                \
                  (CU_FLOAT*)((void*)B), strideb);               \
    } while(0)
#  else /* not PREC_DOUBLE */
#    define CUDA_TRSM(RL, UL, transa, transb, dimb, dima,        \
                      one_cuf,                                   \
                      A, stridea,                                \
                      B, strideb) do {                           \
      cublasCtrsm(RL, UL, transa, transb, dimb, dima, one_cuf,   \
                  (CU_FLOAT*)((void*)A), stridea,                \
                  (CU_FLOAT*)((void*)B), strideb);               \
    } while(0)
#  endif /* not PREC_DOUBLE */
#else /* not TYPE_COMPLEX */
#  ifdef PREC_DOUBLE
#    define CUDA_TRSM(RL, UL, transa, transb, dimb, dima,        \
                      one_cuf,                                   \
                      A, stridea,                                \
                      B, strideb) do {                           \
      cublasDtrsm(RL, UL, transa, transb, dimb, dima, one_cuf,   \
                  (CU_FLOAT*)((void*)A), stridea,                \
                  (CU_FLOAT*)((void*)B), strideb);               \
    } while(0)
#  else /* not PREC_DOUBLE */
#    define CUDA_TRSM(RL, UL, transa, transb, dimb, dima,        \
                      one_cuf,                                   \
                      A, stridea,                                \
                      B, strideb) do {                           \
      cublasStrsm(RL, UL, transa, transb, dimb, dima, one_cuf,   \
                  (CU_FLOAT*)((void*)A), stridea,                \
                  (CU_FLOAT*)((void*)B), strideb);               \
    } while(0)
#  endif /* not PREC_DOUBLE */
#endif /* not TYPE_COMPLEX */

/*
 * Function: xxtrf_starpu_common
 *
 * Diagonal block factorization.
 *
 * Parameters:
 *   buffers    - Data handlers :
 *     0            - L column block
 *     1            - U column block
 *   _args      - Codelet arguments:
 *     sopalin_data - global PaStiX internal data.
 *     cblknum      - Current column block index.
 */
static inline
void xxtrf_starpu_common(void * buffers[], void * _args, int arch) {
  pastix_complex64_t      * lDiag        = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[0]);
  pastix_int_t          stride       = STARPU_MATRIX_GET_LD(buffers[0]);
  pastix_complex64_t      * lExtraDiag   = NULL;
  int                 me           = starpu_worker_get_id();
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
  pastix_complex64_t      * uDiag        = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[1]);
  pastix_complex64_t      * uExtraDiag   = NULL;
#endif
#ifndef CHOL_SOPALIN
  pastix_complex64_t      * tmp4         = (pastix_complex64_t*)STARPU_VECTOR_GET_PTR(buffers[1]);
#endif
#ifdef WITH_MAGMABLAS
  magma_int_t         info;
#endif /* WITH_MAGMABLAS */

  DECLARE_ARGS_TRSM;
  UNPACK_ARGS_TRSM(_args, sopalin_data, cblknum, tasknum, datacode,
                   fblknum, lblknum, dima, dimb);

  switch(arch) {
  case ARCH_CPU:
#ifdef CHOL_SOPALIN
#  ifdef SOPALIN_LU
    /* Add U diagonal updates into L */
    /* Add U diagonal updates into L */
    SOPALIN_GEAM("T", "N", dima, dima, 1.0,
                 uDiag, stride,
                 lDiag, stride);
 /* core_zgeadd( CblasTrans, dima, dima, 1.0, */
    /*              uDiag, stride, */
    /*              lDiag, stride); */
    /* Factorize diagonal block (two terms version with workspace) */
    z_PASTIX_getrf_block(lDiag, dima, dima, stride,
                       &(sopalin_data->starpu_loop_data->pivot_cnt[me]),
                       sopalin_data->critere);
    /* Transpose L_diag in U_diag Matrix */
    z_DimTrans(lDiag,stride, dima,uDiag);
#  else /* SOPALIN_LU */
    z_PASTIX_potrf_block(lDiag, dima, stride,
                       &(sopalin_data->starpu_loop_data->pivot_cnt[me]),
                       sopalin_data->critere);

#  endif /* SOPALIN_LU */
#else /* CHOL_SOPALIN */
#  ifdef HERMITIAN
    z_PASTIX_hetrf_block(lDiag, dima, stride,
                         &(sopalin_data->starpu_loop_data->pivot_cnt[me]),
                         sopalin_data->critere,
                         tmp4);
#  else
    z_PASTIX_sytrf_block(lDiag, dima, stride,
                         &(sopalin_data->starpu_loop_data->pivot_cnt[me]),
                         sopalin_data->critere,
                         tmp4);
#  endif
#endif /* CHOL_SOPALIN */
    break;
#ifdef WITH_MAGMABLAS
  case ARCH_CUDA:
#ifdef CHOL_SOPALIN
#  ifdef SOPALIN_LU
    geadd_cuda("T", "N", dima, dima,
               1.0, uDiag, stride,
               1.0, lDiag, stride);
    magma_zgetrf_stapiv_gpu(dima, dima, (CU_FLOAT*)lDiag, stride,
                            sopalin_data->critere,
                            &(sopalin_data->starpu_loop_data->pivot_cnt[me]), &info);
    getra_cuda(lDiag, stride, uDiag, stride, dima);
#  else /* SOPALIN_LU */
    magma_zpotrf_stapiv_gpu("L", dima, (CU_FLOAT*)lDiag, stride,
                            sopalin_data->critere,
                            &(sopalin_data->starpu_loop_data->pivot_cnt[me]), &info);
#  endif /* SOPALIN_LU */
#else /* CHOL_SOPALIN */
#  ifdef HERMITIAN
    magma_zhetrf_stapiv_gpu('L', dima,
                            lDiag, stride,
                            sopalin_data->critere,
                            &(sopalin_data->starpu_loop_data->pivot_cnt[me]),
                            tmp4,
                            info);
#  else
    magma_zsytrf_stapiv_gpu('L', dima,
                            lDiag, stride,
                            sopalin_data->critere,
                            &(sopalin_data->starpu_loop_data->pivot_cnt[me]),
                            tmp4,
                            info);
#  endif
#endif /* CHOL_SOPALIN */
    break;
#endif /* WITH_MAGMABLAS */
  default:
    errorPrint("%s:%d Unknown Architecture", __FILE__, __LINE__);
    assert(0);
    break;
  }
  }

void xxtrf_starpu_cpu(void * buffers[], void * _args) {
  xxtrf_starpu_common(buffers, _args, ARCH_CPU);
}
void xxtrf_starpu_cuda(void * buffers[], void * _args) {
  xxtrf_starpu_common(buffers, _args, ARCH_CUDA);
}
/*
 * Function: trsm_starpu_common
 *
 * Extra diagonal block system solve.
 *
 * Parameters:
 *   buffers    - Data handlers :
 *     0            - L column block
 *     1            - U column block
 *   _args      - Codelet arguments:
 *     sopalin_data - global PaStiX internal data.
 *     cblknum      - Current column block index.
 */
static inline
void trsm_starpu_common(void * buffers[], void * _args, int arch)
    {
  pastix_complex64_t      * lDiag        = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[0]);
  pastix_int_t          stride       = STARPU_MATRIX_GET_LD(buffers[0]);
  pastix_complex64_t      * lExtraDiag   = NULL;
  int                 me           = starpu_worker_get_id();
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
  pastix_complex64_t      * uDiag        = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[1]);
  pastix_complex64_t      * uExtraDiag   = NULL;
#endif
#ifndef CHOL_SOPALIN
  pastix_complex64_t      * tmp4         = (pastix_complex64_t*)STARPU_VECTOR_GET_PTR(buffers[1]);
#else
  pastix_complex64_t      * tmp4         = NULL;
#endif
#ifdef WITH_CUDA /* see pastix_cuda_helper in old branch */
  CU_FLOAT            one_cuf      = CU_FLOAT_INIT(1.0, 0.0);
#endif
  DECLARE_ARGS_TRSM;
  UNPACK_ARGS_TRSM(_args, sopalin_data, cblknum, tasknum, datacode,
                   fblknum, lblknum, dima, dimb);

      switch(arch) {
      case ARCH_CPU:
  if ( fblknum+1 < lblknum )
    {
      lExtraDiag = lDiag + SOLV_COEFIND(fblknum+1);
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
      uExtraDiag = uDiag + SOLV_COEFIND(fblknum+1);
#endif
          z_kernel_trsm(dimb, dima,
                      lDiag,
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
                      uDiag,
#endif
                      stride,
                      lExtraDiag,
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
                      uExtraDiag,
#endif
#ifndef CHOL_SOPALIN
                      tmp4,
#endif
                      stride);
      }
        break;
#if defined (WITH_CUDA)
      case ARCH_CUDA:
      /* if there is an extra-diagonal bloc in column block */
      if ( fblknum+1 < lblknum ) {

          lExtraDiag = lDiag + SOLV_COEFIND(fblknum+1);
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
          uExtraDiag = uDiag + SOLV_COEFIND(fblknum+1);
#endif
          CUDA_TRSM('R',
#  ifdef CHOL_SOPALIN
#    ifdef SOPALIN_LU
                    'U', 'N',
#    else
                    'L', 'T',
#    endif
#  else
#    ifdef HERMITIAN
                    'L', 'C',
#    else
                    'L', 'T',
#    endif
#  endif
                    'N', dimb, dima,
                  one_cuf, lDiag, stride, lExtraDiag, stride);
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
        CUDA_TRSM('R', 'U', 'N', 'U', dimb, dima,
                  one_cuf, uDiag, stride, uExtraDiag, stride);
#endif
      }
        break;
#endif /* WITH_CUDA */
      default:
      errorPrint("%s:%d Unknown Architecture", __FILE__, __LINE__);
        assert(0);
        break;
      }
  if (sopalin_data->sopar->iparm[IPARM_STARPU_NESTED_TASKS] == API_YES) {
      starpu_zsubmit_bunch_of_gemm(tasknum, sopalin_data);
    }
    /* { */
    /*     char name[256]; */
    /*     sprintf(name, "cblk_%ld_after_trf_trsm", (long)cblk->gcblknum); */
    /*     z_cblk_save(cblk, name, lDiag); */
    /* } */
}

void trsm_starpu_cpu(void * buffers[], void * _args) {
  trsm_starpu_common(buffers, _args, ARCH_CPU);
}
void trsm_starpu_cuda(void * buffers[], void * _args) {
  trsm_starpu_common(buffers, _args, ARCH_CUDA);
}
/*
 * Function: trfsp1d_starpu_common
 *
 * Diagonal block factorization and column block update for LU decomposition.
 *
 * Parameters:
 *   buffers    - Data handlers :
 *     0            - L column block
 *     1            - U column block
 *   _args      - Codelet arguments:
 *     sopalin_data - global PaStiX internal data.
 *     cblknum      - Current column block index.
 */
static inline
void trfsp1d_starpu_common(void * buffers[], void * _args, int arch)
{
  xxtrf_starpu_common(buffers, _args, arch);
  trsm_starpu_common(buffers, _args, arch);
}



/*
 * Function: trfsp1d_starpu_cpu
 *
 * Diagonal block factorization and column block update for
 * LU/LLT/LDLT/LDLH decomposition.
 *
 * CPU function.
 *
 * Parameters:
 *   buffers    - Data handlers :
 *     0            - L column block
 *     1            - U column block
 *   _args      - Codelet arguments:
 *     sopalin_data - global PaStiX internal data.
 *     cblknum      - Current column block index.
 */
void trfsp1d_starpu_cpu(void * buffers[], void * _args)
{
  trfsp1d_starpu_common(buffers, _args, ARCH_CPU);
}

#ifdef WITH_MAGMABLAS
/*
 * Function: trfsp1d_starpu_cuda
 *
 * Diagonal block factorization and column block update for LU decomposition.
 *
 * CUDA function.
 *
 * Parameters:
 *   buffers    - Data handlers :
 *     0            - L column block
 *     1            - U column block
 *   _args      - Codelet arguments:
 *     sopalin_data - global PaStiX internal data.
 *     cblknum      - Current column block index.
 */
void trfsp1d_starpu_cuda(void * buffers[], void * _args)
{
  trfsp1d_starpu_common(buffers, _args, ARCH_CUDA);
}
#endif /* WITH_MAGMABLAS */

/*
  Function: trfsp1d_gemm_starpu_common

  General update of left block column facing current block.

  Common function for CPU and GPU.

  Update block by block.

  Parameters:
    buffers - Data handlers :
      0 - L column block.
      1 - L facing column block.
      2 - U column block.
      3 - U facing column block.
      4 - Working memory area.
    _args   - codelet arguments :
      sopalin_data - global PaStiX internal data.
      cblknum      - Current column block index.
      bloknum      - Current block index.
      fcblknum     - Facing column block index.
   arch     - indicate if the codelet is runned on CPU or CUDA node.
*/
static inline void
trfsp1d_gemm_starpu_common(void * buffers[], void * _args, int arch)
{
  pastix_complex64_t       * L            = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[0]);
  pastix_complex64_t       * Cl           = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[1]);
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
  pastix_complex64_t       * U            = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[2]);
  pastix_complex64_t       * Cu           = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[3]);
  pastix_complex64_t       * work         = (pastix_complex64_t*)STARPU_VECTOR_GET_PTR(buffers[4]);
  pastix_int_t           ldw          = (pastix_int_t)STARPU_VECTOR_GET_NX(buffers[4]);
  int                * all_blocktab = (int*)STARPU_VECTOR_GET_PTR(buffers[5]);;
#else
  pastix_complex64_t       * work         = (pastix_complex64_t*)STARPU_VECTOR_GET_PTR(buffers[2]);
  pastix_int_t           ldw          = (pastix_int_t)STARPU_VECTOR_GET_NX(buffers[2])/2;
  pastix_complex64_t       * work2        = work + ldw;
  int                * all_blocktab = (int*)STARPU_VECTOR_GET_PTR(buffers[3]);;
#endif
#ifndef CHOL_SOPALIN
#endif
  pastix_int_t           stridefc     = STARPU_MATRIX_GET_LD(buffers[1]);
  pastix_complex64_t       * Aik, *C;
#ifdef CHOL_SOPALIN
  pastix_complex64_t       * Akj;
#endif
  pastix_complex64_t       * Aij;
  pastix_int_t           frownum;
  pastix_int_t           b, j, dimb;
  pastix_complex64_t       * wtmp = work;
#ifdef  CHOL_SOPALIN
#  ifdef SOPALIN_LU
  char * trans = "T";
#  else  /* SOPALIN_LU */
  char * trans = "C";
#  endif
#else
#  ifdef HERMITIAN
  char * trans = "C";
#  else
  char * trans = "T";
#  endif
#endif

  SolverBlok *iterblok, *fblok;
  /* {									 */
  /*   unsigned char * myargs = (unsigned char*)_args;			 */
  /*   SolverCblk * mycblk;						 */
  /*   SolverBlok * myblok;						 */
  /*   Sopalin_Data_t *mys; */
  /*   fprintf(stdout, "nargs %d\n", *(unsigned char*)myargs);		 */
  /*   myargs += sizeof(char);			 */

  /*   fprintf(stdout, "size %d\n", *(size_t*)myargs);			 */
  /*   myargs += sizeof(size_t);						 */
  /*   mys = *(Sopalin_Data_t**)myargs; */
  /*   myargs+= sizeof(Sopalin_Data_t*); */
  /*   fprintf(stdout, "%p mys \n", mys); */
  /*   fprintf(stdout, "size %d\n", *(size_t*)myargs);			 */
  /*   myargs += sizeof(size_t);						 */

  /*   mycblk = *(SolverCblk**)myargs;					 */
  /*   fprintf(stdout, "%p mycblk \n", mycblk); */
  /*   fprintf(stdout, "%d [%d %d]\n", mycblk->gcblknum, mycblk->fcolnum, mycblk->lcolnum);	 */
  /*   myargs += sizeof(SolverCblk*);					 */

  /*   fprintf(stdout, "size %d\n", *(size_t*)myargs);			 */
  /*   myargs += sizeof(size_t);						 */

  /*   myblok = *(SolverBlok**)myargs;					 */
  /*   fprintf(stdout, "%p myblok \n", myblok); */
  /*   myargs += sizeof(SolverBlok*);					 */
  /*   fprintf(stdout, "[%d %d]\n", myblok->frownum, myblok->lrownum);	 */

  /*   fprintf(stdout, "size %d\n", *(size_t*)myargs);			 */
  /*   myargs += sizeof(size_t);						 */

  /*   mycblk = *(SolverCblk**)myargs;					 */
  /*   fprintf(stdout, "%p mycblk \n", mycblk); */
  /*   myargs += sizeof(SolverCblk*);					 */
  /*   fprintf(stdout, "%d [%d %d]\n", mycblk->gcblknum, mycblk->fcolnum, mycblk->lcolnum);	 */
  /* }									 */

  DECLARE_ARGS_GEMM;
  UNPACK_ARGS_GEMM(_args, sopalin_data, bloknum,
                   all_blocktab, datacode,
		   indblok, blocktab, fblocktab,
		   stride, dimi, dimj, dima, blocknbr);
  /* Matrix A = Aik */
  Aik = L + indblok;
#ifdef CHOL_SOPALIN
#  ifdef SOPALIN_LU
  Akj = U + indblok;
#  else
  Akj = Aik;
#  endif
#endif

  switch(arch) {
  case ARCH_CPU:
#ifdef CHOL_SOPALIN
    SOPALIN_GEMM( "N", trans,
                  dimi, dimj, dima,
                  1.,  Aik,  stride,
                  Akj,  stride,
                  0.,  wtmp, dimi);
#else
  /* Compute the contribution */
    z_CORE_gemdm( PastixNoTrans,
#  ifdef HERMITIAN
                  PastixConjTrans,
#  else
                  PastixTrans,
#  endif
                  dimi, dimj, dima,
                  1.,  Aik,   stride,
                  Aik,   stride,
                  0.,  wtmp, dimi,
                  L,     stride+1,
                  work2, ldw );
#endif
    break;
  default:
    errorPrint("Unknown Architecture");
    assert(0);
    break;
  }
  /*
   * Add contribution to facing cblk
   */
  b = fcblk->bloknum;
  assert(!z_cblk_ishalo(datacode, fcblk));
  fblok = _fblok;
  assert(stridefc == fcblk->stride);
  C = Cl + ( blok->frownum - fcblk->fcolnum ) * stridefc;
  /* for all following blocks in block column */
  for (iterblok=blok; iterblok<firstBlok+cblk_bloknbr(cblk); iterblok++) {
    /* Find facing bloknum */
    while (!z_is_block_inside_fblock(iterblok, fblok)) {
      fblok++;b++;
      assert( b == fcblk->bloknum + (fblok - _fblok));
      assert( b < fcblk[1].bloknum );
      }
    Aij = C + fblok->coefind + iterblok->frownum -fblok->frownum;
    dimb = blok_rownbr(iterblok);
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
      SOPALIN_GEAM("N", "N", dimb, dimj, -1.0,
                   wtmp, dimi,
                   Aij,  stridefc );
#else
    core_zgeadd( CblasNoTrans, dimb, dimj, -1.0,
    		 wtmp, dimi,
    		 Aij,   stridefc );
    /* MAT_zaxpy( dimb, dimj, -1.0, */
    /* 	       wtmp, dimi, */
    /* 	       Aij,  stridefc ); */
#endif
    /* Displacement to next block */
    wtmp += dimb;
  }

#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
  /*
   * Compute update on U
   */
  Aik = U + indblok;
  Akj = L + indblok;

  /* Restore data */
  wtmp = work;
  b = fcblk->bloknum;
  fblok = _fblok;

    SOPALIN_GEMM( "N", "T",
                  dimi, dimj, dima,
                  1.,  Aik,  stride,
                  Akj,  stride,
                  0.,  wtmp, dimi  );
  wtmp += blok->lrownum - blok->frownum + 1;

  /*
   * Add contribution to facing cblk
   */
  C  = Cl  + ( blok->frownum - fcblk->fcolnum );
  Cu = Cu  + ( blok->frownum - fcblk->fcolnum ) * stridefc;
  /* for all following blocks in block column */
  for (iterblok=blok+1; iterblok<firstBlok+cblk_bloknbr(cblk); iterblok++) {
    /* Find facing bloknum */
    if (!z_is_block_inside_fblock( iterblok, fblok ))
      break;
    Aij = C + (iterblok->frownum - fblok->frownum)*stridefc;
    dimb = iterblok->lrownum - iterblok->frownum + 1;
    switch(arch) {
    case ARCH_CPU:
      core_zgeadd( CblasTrans, dimj, dimb, -1.0,
                    wtmp, dimi,
                    Aij,  stridefc );
      /* SOPALIN_GEAM( "T", "N", dimj, dimb, -1.0, */
      /*               wtmp, dimi, */
      /*               Aij,  stridefc ); */
      break;
    default:
      errorPrint("Unknown Architecture");
      assert(0);
      break;
    }

    /* Displacement to next block */
    wtmp += dimb;
  }

  C = Cu;
  /* Keep updating on U */
  for ( ; iterblok<firstBlok+cblk_bloknbr(cblk); iterblok++) {
    /* Find facing bloknum */
    while (!z_is_block_inside_fblock( iterblok, fblok ))
      {
	fblok++;b++;
	assert( b == fcblk->bloknum + (fblok - _fblok));
	assert( b < fcblk[1].bloknum );

      }
    dimb = iterblok->lrownum - iterblok->frownum + 1;
    Aij = C + fblok->coefind + iterblok->frownum - fblok->frownum;
    core_zgeadd( CblasNoTrans, dimb, dimj, -1.0,
                   wtmp, dimi,
                   Aij,  stridefc );
    /* Sopalin_GEAM("N", "N", dimb, dimj, -1.0, */
    /* 		 wtmp, dimi, */
    /* 		 Aij,  stridefc ); */

    /* Displacement to next block */
    wtmp += dimb;
  }
#endif
  SUBMIT_TRF_IF_NEEDED;
    /* { */
    /*     char name[256]; */
    /*     sprintf(name, "cblk_%ld_after_gemm_%ld_%ld_%ld_on_%ld", */
    /*             (long)fcblk->gcblknum, */
    /*             (long)cblk->gcblknum, (long)((bloknum - cblk->bloknum)), */
    /*             (long)fcblk->gcblknum, */
    /*             (long)sopalin_data->datacode->clustnum); */
    /*     z_cblk_save(fcblk, name, Cl); */
    /* } */
}

/*
  Function: trfsp1d_gemm_starpu_cpu

  General update of left block column facing current block.

  Update block by block.

  Parameters:
    buffers - Data handlers :
      0 - L column block.
      1 - L facing column block.
      2 - U column block.
      3 - U facing column block.
      4 - Working memory area.
    _args   - codelet arguments :
      sopalin_data - global PaStiX internal data.
      cblknum      - Current column block index.
      bloknum      - Current block index.
      fcblknum     - Facing column block index.
*/
void trfsp1d_gemm_starpu_cpu(void * buffers[], void * _args)
{
  trfsp1d_gemm_starpu_common(buffers, _args, ARCH_CPU);
}

#ifdef STARPU_USE_CUDA
/*
  Function: trfsp1d_gemm_starpu_cuda

  General update of left block column facing current block.

  Update block by block.

  Parameters:
    buffers - Data handlers :
      0 - L column block.
      1 - L facing column block.
      2 - U column block.
      3 - U facing column block.
      4 - Working memory area.
    _args   - codelet arguments :
      sopalin_data - global PaStiX internal data.
      cblknum      - Current column block index.
      bloknum      - Current block index.
      fcblknum     - Facing column block index.
*/
void trfsp1d_gemm_starpu_cuda(void * buffers[], void * _args)
{
  trfsp1d_gemm_starpu_common(buffers, _args, ARCH_CUDA);
}
#endif


/*
 *  Function: trfsp1d_sparse_gemm_starpu_common
 * 
 *  Sparse update of left block column facing current block.
 * 
 *  Common function for CPU and GPU.
 * 
 *  Update all the facing column block at once.
 * 
 *  TODO: Implement the CPU version
 * 
 *  Parameters:
 *    buffers - Data handlers :
 *      0 - L column block.
 *      1 - L facing column block.
 *      2 - U column block.
 *      3 - U facing column block.
 *      4 - Working memory area.
 *      5 - blocktab
 *    _args   - codelet arguments :
 *      sopalin_data - global PaStiX internal data.
 *      cblknum      - Current column block index.
 *      bloknum      - Current block index.
 *      nblocs       - Number of blocks in current column block.
 *    arch     - indicate if the codelet is runned on CPU or CUDA node.
*/
static inline
void trfsp1d_sparse_gemm_starpu_common(void * buffers[], void * _args,
                                         int arch)
{
  pastix_complex64_t       * L            = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[0]);
  pastix_complex64_t       * Cl           = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[1]);
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
  pastix_complex64_t       * U            = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[2]);
  pastix_complex64_t       * Cu           = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[3]);
  pastix_complex64_t       * work         = (pastix_complex64_t*)STARPU_VECTOR_GET_PTR(buffers[4]);
  size_t               worksize     = STARPU_VECTOR_GET_NX(buffers[4]);
  int                * all_blocktab = (int*)STARPU_VECTOR_GET_PTR(buffers[5]);
#else
  pastix_complex64_t       * work         = (pastix_complex64_t*)STARPU_VECTOR_GET_PTR(buffers[2]);
  size_t               worksize     = STARPU_VECTOR_GET_NX(buffers[2]);
  int                * all_blocktab = (int*)STARPU_VECTOR_GET_PTR(buffers[3]);
#endif
  pastix_int_t           stridefc     = STARPU_MATRIX_GET_LD(buffers[1]);
  pastix_complex64_t         alpha        = -1.;
  pastix_complex64_t         beta         = 1.;
  pastix_complex64_t       * Aik;
  pastix_complex64_t       * Akj;
  pastix_complex64_t       * Aij;
#ifdef CHOL_SOPALIN
#  ifdef SOPALIN_LU
  char * TRANS = "T";
  char * trans = "t";
#  else /* SOPALIN_LU */
  char * TRANS = "C";
  char * trans = "c";
#  endif
#else
#  ifdef HERMITIAN
  char * TRANS = "C";
  char * trans = "c";
#  else
  char * TRANS = "T";
  char * trans = "t";
#endif
#endif

  DECLARE_ARGS_GEMM;
  UNPACK_ARGS_GEMM(_args, sopalin_data, bloknum, all_blocktab,
                   datacode, indblok, blocktab, fblocktab,
                   stride, dimi, dimj, dima, blocknbr);

  Aik = L + indblok;
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
  Akj = U + indblok;
#else
  Akj = Aik;
#endif
  Aij = Cl + ( blok->frownum - fcblk->fcolnum ) * fcblk->stride;
  switch(arch) {
  case ARCH_CPU:
    SOPALIN_SPARSE_GEMM( "N", TRANS,
                         dimi, dimj, dima,
                         alpha,  Aik,  stride,
                         Akj,  stride,
                         beta,  Aij, stridefc,
                         blocknbr, blocktab,
			 cblk_bloknbr(fcblk), fblocktab,
                         work, worksize);
    break;
#ifdef WITH_CUDA
  case ARCH_CUDA:
#  ifdef CHOL_SOPALIN
    CUDA_SPARSE_GEMM( "n", trans,
                      dimi, dimj, dima,
                      alpha,  Aik,  stride,
                      Akj,  stride,
                      beta,  Aij, stridefc,
                      blocknbr, blocktab,
		      cblk_bloknbr(fcblk), fblocktab);
#  else
    CUDA_SPARSE_GEMDM( "n", trans,
    		       dimi, dimj, dima,
    		       alpha,
    		       Aik,  stride,
    		       L,    stride+1,
    		       Akj,  stride,
    		       beta,  Aij, stridefc,
    		       blocknbr, blocktab,
		       cblk_bloknbr(fcblk), fblocktab);
#  endif
    break;
#endif
  default:
    errorPrint("%s:%d Unknown Architecture", __FILE__, __LINE__);
    assert(0);
    break;
  }

#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
  /*
   * Compute update on U
   */
  if ( blocknbr > 1 ) {
    dimi = dimi - (blok[1].coefind - blok->coefind);
    Aik = U + blok[1].coefind;
    Aij = Cu + (blok->frownum - fcblk->fcolnum)*fcblk->stride;
    Akj = L + indblok;
    switch(arch) {
    case ARCH_CPU:
      SOPALIN_SPARSE_GEMM( "N", "T",
                           dimi, dimj, dima,
                           alpha,  Aik,  stride,
                           Akj,  stride,
                           beta,  Aij, stridefc,
                           blocknbr-1, &(blocktab[2]),
                           cblk_bloknbr(fcblk), fblocktab,
                           work, worksize);
      break;
#if defined(WITH_CUDA)
    case ARCH_CUDA:
      CUDA_SPARSE_GEMM( "n", "t",
                        dimi, dimj, dima,
                        alpha,  Aik,  stride,
                        Akj,  stride,
                        beta,  Aij, stridefc,
                        blocknbr-1, &(blocktab[2]),
                        cblk_bloknbr(fcblk), fblocktab);
      break;
#endif /* defined(WITH_CUDA) */
    default:
      errorPrint("%s:%d Unknown Architecture", __FILE__, __LINE__);
      assert(0);
      break;
    }
  }
#endif /* CHOL_SOPALIN && SOPALIN_LU */
  SUBMIT_TRF_IF_NEEDED;
}


/*
 *  Function: trfsp1d_sparse_gemm_starpu_cpu
 *
 *  Sparse update of left block column facing current block.
 *
 *  Update all the facing column block at once.
 *
 * Parameters:
 *   buffers - Data handlers :
 *      0 - L column block.
 *      1 - L facing column block.
 *      2 - U column block.
 *      3 - U facing column block.
 *      4 - Working memory area.
 *      5 - blocktab.
 *    _args   - codelet arguments
 *    sopalin_data - global PaStiX internal data.
 *      bloknum      - Current block index.
 *      nblocs       - Number of blocks in current column block.
 */
void trfsp1d_sparse_gemm_starpu_cpu(void * buffers[], void * _args)
{
  trfsp1d_gemm_starpu_common(buffers, _args, ARCH_CPU);
}

/*
 * Function: trfsp1d_sparse_gemm_starpu_cuda
 *
 * Sparse update of left block column facing current block.
 *
 * Update all the facing column block at once.
 *
 * Parameters:
 *   buffers - Data handlers :
 *     0 - L column block.
 *     1 - L facing column block.
 *     2 - U column block.
 *     3 - U facing column block.
 *     4 - Working memory area.
 *     5 - blocktab.
 *   _args   - codelet arguments :
 *    sopalin_data - global PaStiX internal data.
 *    cblknum      - Current column block index.
 *     bloknum      - Current block index.
 *     nblocs       - Number of blocks in current column block.
 */
void trfsp1d_sparse_gemm_starpu_cuda(void * buffers[], void * _args)
{
  trfsp1d_sparse_gemm_starpu_common(buffers, _args, ARCH_CUDA);
}


static inline
void init_coeftab_common(void * buffers[], void * _args, int arch) {
  pastix_complex64_t       * L          = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[0]);
  pastix_int_t           stride     = STARPU_MATRIX_GET_LD(buffers[0]);
  pastix_int_t           nx         = STARPU_MATRIX_GET_NX(buffers[0]);

  switch(arch) {
  case ARCH_CPU:
    memset(L, 0, stride*nx*sizeof(pastix_complex64_t));
    break;
#if defined(WITH_CUDA)
  case ARCH_CUDA:
    cudaMemset(L, 0, stride*nx*sizeof(pastix_complex64_t));
    break;
#endif /* defined(WITH_CUDA) */
  default:
    errorPrint("%s:%d Unknown Architecture", __FILE__, __LINE__);
    assert(0);
    break;
  }

}

void init_coeftab_cpu(void * buffers[], void * _args) {
  init_coeftab_common(buffers, _args, ARCH_CPU);
}

void init_coeftab_cuda(void * buffers[], void * _args) {
  init_coeftab_common(buffers, _args, ARCH_CUDA);
}


static inline
void fill_coeftab_common(void * buffers[], void * _args, int arch) {
  pastix_complex64_t       * L          = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[0]);
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
  pastix_complex64_t       * U          = (pastix_complex64_t*)STARPU_MATRIX_GET_PTR(buffers[1]);
#endif
  pastix_int_t           stride     = STARPU_MATRIX_GET_LD(buffers[0]);
  pastix_int_t           nx         = STARPU_MATRIX_GET_NX(buffers[0]);
  z_Sopalin_Data_t    * sopalin_data;
  z_SolverMatrix      * datacode;
  pastix_int_t          cblknum;
  pastix_int_t          tasknum;
  pastix_int_t          fblknum;
  pastix_int_t          lblknum;
  pastix_int_t          dima;
  pastix_int_t          dimb;
  const z_CscMatrix   * cscmtx;
  pastix_complex64_t      * trandcsc;
  SolverCblk * cblk;
  starpu_codelet_unpack_args(_args, &sopalin_data, &cblknum);


  datacode = sopalin_data->datacode;
  cblk     = &(datacode->cblktab[cblknum]);
  fblknum  = cblk->bloknum;
  lblknum  = cblk[1].bloknum;
  dima     = cblk_colnbr(cblk);
  dimb     = cblk->stride - dima;
  cscmtx   = sopalin_data->sopar->cscmtx;
  trandcsc = sopalin_data->sopar->transcsc;
  switch(arch) {
  case ARCH_CPU:
    if (cblknum < CSC_FNBR(cscmtx)) {
      pastix_int_t itercoltab;
      for (itercoltab=0;
           itercoltab < CSC_COLNBR(cscmtx,cblknum);
           itercoltab++) {
        pastix_int_t iterval;
        for (iterval = CSC_COL(cscmtx,cblknum,itercoltab);
             iterval < CSC_COL(cscmtx,cblknum,itercoltab+1);
             iterval++) {
          if (CSC_ROW(cscmtx,iterval) >=
              cblk->fcolnum) {
            SolverBlok * fblok = &(datacode->bloktab[fblknum]);
            SolverBlok * lblok = &(datacode->bloktab[lblknum]);
            SolverBlok * blok  = fblok;
            while (( blok < lblok) &&
                   (( blok->lrownum < CSC_ROW(cscmtx,iterval)) ||
                    ( blok->frownum > CSC_ROW(cscmtx,iterval)))) {
              blok++;
            }

            if ( blok < lblok ) {
              pastix_int_t coefindx;
              coefindx = blok->coefind;

              coefindx += CSC_ROW(cscmtx,iterval) - blok->frownum;

              coefindx += cblk->stride*itercoltab;
              L[coefindx] += CSC_VAL(cscmtx,iterval);
#if (defined CHOL_SOPALIN && defined SOPALIN_LU)
              if (trandcsc != NULL && blok != fblok) {
                if (cscmtx->type == 'H')
                  U[coefindx] += CONJ_FLOAT(trandcsc[iterval]);
                else
                  U[coefindx] += trandcsc[iterval];
              }
#endif
            } else {
              printf("ILU: csc2solv drop coeff from CSC c=%ld(%ld) l=%ld(%ld)"
                     " cblk=%ld fcol=%ld lcol=%ld\n",
                     (long)cblk->fcolnum+
                     (long)itercoltab,(long)itercoltab,
                     (long)CSC_ROW(cscmtx,iterval),(long)iterval,
                     (long)cblknum,
                     (long)cblk->fcolnum,
                     (long)cblk->lcolnum);
            }
          }
        }
      }
    }
    break;
  case ARCH_CUDA:
  default:
    errorPrint("%s:%d Unknown Architecture", __FILE__, __LINE__);
    assert(0);
    break;
  }
}

void fill_coeftab_cpu(void * buffers[], void * _args) {
  fill_coeftab_common(buffers, _args, ARCH_CPU);
}

void fill_coeftab_cuda(void * buffers[], void * _args) {
  fill_coeftab_common(buffers, _args, ARCH_CUDA);
}

void empty_task_func(void * buffers[], void * _args)
{
}
