/* 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.
*/
/************************************************************/
/**                                                        **/
/**   NAME       : solver.h                                **/
/**                                                        **/
/**   AUTHORS    : David GOUDIN                            **/
/**                Pascal HENON                            **/
/**                Francois PELLEGRINI                     **/
/**                Pierre RAMET                            **/
/**                                                        **/
/**   FUNCTION   : Part of a parallel direct block solver. **/
/**                These lines are the data declarations   **/
/**                for the solver matrix.                  **/
/**                                                        **/
/**   DATES      : # Version 0.0  : from : 22 jul 1998     **/
/**                                 to     28 oct 1998     **/
/**                # Version 1.0  : from : 06 jun 2002     **/
/**                                 to     06 jun 2002     **/
/**                                                        **/
/************************************************************/

#ifndef SOLVER_H
#define SOLVER_H

#include <assert.h>
/*
**  The type and structure definitions.
*/

#define COMP_1D                     0
#define DIAG                        1
#define E1                          2
#define E2                          3
#define DRUNK                       4

typedef struct Task_ {
  PASTIX_INT                       taskid;               /*+ COMP_1D DIAG E1 E2                                     +*/
  PASTIX_INT                       prionum;              /*+ Priority value for the factorization                   +*/
  PASTIX_INT                       prionum2;             /*+ Priority value for solve steps                         +*/
  PASTIX_INT                       cblknum;              /*+ Attached column block                                  +*/
  PASTIX_INT                       bloknum;              /*+ Attached block                                         +*/
  PASTIX_INT volatile              ftgtcnt;              /*+ Number of fan-in targets                               +*/
  PASTIX_INT volatile              ctrbcnt;              /*+ Total number of contributions                          +*/
  volatile BlockTarget *    btagptr;              /*+ si non local, pointeur sur la structure (NB reception) +*/
  PASTIX_INT                       indnum;               /*+ For E2 (COMP_1D), index of ftgt (>0) else if local = -taskdest
                                                      For DIAG and E1 , index of btag (>0) if there is a
						      local one it must be the first of the chain of local E1   +*/
  PASTIX_INT                       tasknext;             /*+ chainage des E1 ou E2, si fin = -1 => liberer les btagptr +*/
  PASTIX_INT                       taskmstr;             /*+ Master task for E1 or E2 tasks                         +*/
                                                  /*+ Index of DIAG (or first E1) task for E1                +*/
                                                  /*+ Index of E1 (or first E2) task for E2                  +*/
#if (defined PASTIX_DYNSCHED) || (defined TRACE_SOPALIN) || (defined BLEND_COMPUTE_THREADID)
  PASTIX_INT                       threadid;             /*+ Index of the bubble which contains the task +*/
  PASTIX_INT                       GPUid;
  PASTIX_INT                       cand;		  /*+ Thread candidate in static version          +*/
#endif
#ifdef TRACE_SOPALIN
  PASTIX_INT                       fcandnum;             /*+ First thread candidate                      +*/
  PASTIX_INT                       lcandnum;		  /*+ Last thread candidate                       +*/
  PASTIX_INT                       id;                   /*+ Global cblknum of the attached column block +*/
#endif
} Task;

/*+ Solver column block structure. +*/

typedef struct SolverCblk_  {
  PASTIX_INT                       gcblknum;
  PASTIX_INT                       fcolnum;              /*+ First column index                     +*/
  PASTIX_INT                       lcolnum;              /*+ Last column index (inclusive)          +*/
  PASTIX_INT                       bloknum;              /*+ First block in column (diagonal)       +*/
  PASTIX_INT                       stride;               /*+ Column block stride                    +*/
  PASTIX_INT			    color;		  /*+ Color of column block (PICL trace)     +*/
#ifdef STARPU_GET_TASK_CTX
  PASTIX_INT                       ctx;                  /*+ Context given to StarPU                +*/
#endif
  PASTIX_INT                       gpuid;
  PASTIX_INT                       procdiag;             /*+ Processor owner of diagonal block      +*/
  PASTIX_INT                       cblkdiag;             /*+ Column block owner of diagonal block   +*/
  PASTIX_FLOAT * restrict          coeftab;              /*+ Coefficients access vector             +*/
  PASTIX_FLOAT * restrict          ucoeftab;             /*+ Coefficients access vector             +*/
} SolverCblk; 

/*+ Solver block structure. +*/

typedef struct SolverBlok_ {
  PASTIX_INT                       frownum;              /*+ First row index            +*/
  PASTIX_INT                       lrownum;              /*+ Last row index (inclusive) +*/
  PASTIX_INT                       cblknum;              /*+ Facing column block        +*/
  PASTIX_INT                       levfval;              /*+ Level-of-fill value        +*/
  PASTIX_INT                       coefind;              /*+ Index in coeftab           +*/
} SolverBlok;

/*+ Solver matrix structure. +*/

typedef struct SolverMatrix_ {
  PASTIX_INT                       baseval;              /*+ Base value for numberings                 +*/
  PASTIX_INT                       nodenbr;              /*+ Number of nodes in matrix                 +*/
  PASTIX_INT                       cblknbr;              /*+ Number of column blocks                   +*/
  PASTIX_INT                       gcblknbr;             /*+ Global Number of column blocks                   +*/
#ifdef WITH_STARPU
  /* All this part concern halo of the local matrix
   * ie: column blocks which will:
   *  - be updated by local column blocks
   *  - update local column blocks
   */
  PASTIX_INT              hcblknbr;             /*+ Number of column block in the halo        +*/
  PASTIX_INT *            gcblk2halo;           /*+ Indicate the local number corresponding
                                                   *  global column block.
                                                   *  gcblk2halo[gcblk] == 0 : gcblk not local nor in halo
                                                   *                    >  0 : local cblk number + 1
                                                   *                    <  0 : - (halo cblk number + 1)
                                                   *                                            +*/
  SolverCblk   * restrict hcblktab;             /*+ Array of halo column blocks               +*/
  SolverBlok   * restrict hbloktab;             /*+ Array of halo blocks                      +*/
  PASTIX_INT *          fcblknbr;               /*+ Number of fanin buffer to send or recv    +*/
  SolverCblk  ** restrict fcblktab;               /*+ Fanin column block array                  +*/
  SolverBlok  ** restrict fbloktab;               /*+ Fanin block array                         +*/
#endif
  PASTIX_INT                       bloknbr;              /*+ Number of blocks                          +*/
  SolverCblk * restrict     cblktab;              /*+ Array of solver column blocks             +*/
  SolverBlok * restrict     bloktab;              /*+ Array of solver blocks                    +*/
  PASTIX_INT                       coefnbr;              /*+ Number of coefficients                    +*/

  PASTIX_INT                       ftgtnbr;              /*+ Number of fanintargets                    +*/
  PASTIX_INT                       ftgtcnt;              /*+ Number of fanintargets to receive         +*/
  FanInTarget * restrict    ftgttab;              /*+ Fanintarget access vector                 +*/

  PASTIX_INT                       coefmax;              /*+ Working block max size (cblk coeff 1D)    +*/
  PASTIX_INT                       bpftmax;              /*+ Maximum of block size for btag to receive +*/
  PASTIX_INT                       cpftmax;              /*+ Maximum of block size for ftgt to receive +*/
  PASTIX_INT                       nbftmax;              /*+ Maximum block number in ftgt              +*/
  PASTIX_INT                       arftmax;              /*+ Maximum block area in ftgt                +*/

  PASTIX_INT                       clustnum;             /*+ current processor number                  +*/
  PASTIX_INT                       clustnbr;             /*+ number of processors                      +*/
  PASTIX_INT                       procnbr;              /*+ Number of physical processor used         +*/
  PASTIX_INT                       thrdnbr;              /*+ Number of local computation threads       +*/
  PASTIX_INT                       bublnbr;              /*+ Number of local computation threads       +*/
  BubbleTree  * restrict    btree;                /*+ Bubbles tree                              +*/

  BlockTarget * restrict    btagtab;              /*+ Blocktarget access vector                 +*/
  PASTIX_INT                       btagnbr;              /*+ Number of Blocktargets                    +*/
  PASTIX_INT                       btgsnbr;              /*+ Number of Blocktargets to send            +*/
  PASTIX_INT                       btgrnbr;              /*+ Number of Blocktargets to recv            +*/
  BlockCoeff  * restrict    bcoftab;              /*+ BlockCoeff access vector                  +*/
  PASTIX_INT                       bcofnbr;

  PASTIX_INT                       indnbr;
  PASTIX_INT * restrict            indtab;
  Task * restrict           tasktab;              /*+ Task access vector                        +*/
  PASTIX_INT                       tasknbr;              /*+ Number of Tasks                           +*/
  PASTIX_INT **                    ttsktab;              /*+ Task access vector by thread              +*/
  PASTIX_INT *                     ttsknbr;              /*+ Number of tasks by thread                 +*/

  PASTIX_INT *                     proc2clust;           /*+ proc -> cluster                           +*/
  PASTIX_INT                       gridldim;             /*+ Dimensions of the virtual processors      +*/
  PASTIX_INT                       gridcdim;             /*+ grid if dense end block                   +*/
  UpDownVector              updovct;              /*+ UpDown vector                             +*/
#ifdef STARPU_GET_TASK_CTX
  PASTIX_INT                       starpu_subtree_nbr;
#endif
} SolverMatrix;

/**
 *  Function: sizeofsolver
 *
 *  Computes the size in memory of the SolverMatrix.
 *
 *  Parameters:
 *    solvptr - address of the SolverMatrix
 *
 *  Returns:
 *    SolverMatrix size.
 */
#define sizeofsolver PASTIX_PREFIX_F(sizeofsolver)
static inline
PASTIX_INT sizeofsolver(SolverMatrix *solvptr, PASTIX_INT *iparm)
{
  PASTIX_INT result=sizeof(SolverMatrix);
  PASTIX_INT iter;

  /* cblk and blocks arrays */
  result += solvptr->cblknbr*sizeof(SolverCblk);
  result += solvptr->bloknbr*sizeof(SolverBlok);

  /* fanin target */
  result += solvptr->ftgtnbr*sizeof(FanInTarget);
  result += solvptr->btagnbr*sizeof(BlockTarget);
  result += solvptr->bcofnbr*sizeof(BlockCoeff);
  result += solvptr->indnbr *sizeof(PASTIX_INT);

  result += solvptr->bublnbr*sizeof(BubbleTree);

  /* task */
  result += solvptr->tasknbr*sizeof(Task);

  /* ttsktab */
  result += solvptr->thrdnbr*sizeof(PASTIX_INT);
  result += solvptr->thrdnbr*sizeof(PASTIX_INT*);
  for (iter=0; iter<solvptr->thrdnbr; iter++)
    {
      result += solvptr->ttsknbr[iter]*sizeof(PASTIX_INT);
    }

  /* proc2clust */
  result += solvptr->procnbr*sizeof(PASTIX_INT);

  /* UpDownVector */
  /* TODO: 2D    */
  if (iparm[IPARM_DISTRIBUTION_LEVEL] == 0)
    {
      /* UpDownCblk */
      result += solvptr->cblknbr      *sizeof(UpDownCblk);
      for (iter=0; iter<solvptr->cblknbr; iter++)
        {
          /* browproctab / browcblktab */
          result += 2*solvptr->updovct.cblktab[iter].browprocnbr*sizeof(PASTIX_INT);
        }
      /* gcblk2list */
      result += solvptr->updovct.gcblk2listnbr*sizeof(PASTIX_INT);
      /* listptr */
      result += solvptr->updovct.listptrnbr   *sizeof(PASTIX_INT);
      /* listcblk / listblok */
      result += 2*solvptr->updovct.listnbr    *sizeof(PASTIX_INT);
      /* loc2glob */
      result += solvptr->updovct.loc2globnbr  *sizeof(PASTIX_INT);
      /* lblk2gcblk */
      result += solvptr->bloknbr              *sizeof(PASTIX_INT);
    }

  return result;
}

/**
 * Indicates whether a column block is in halo.
 *
 * @param datacode SolverMatrix structure.
 * @param column block SolverCblk structure to test.
 *
 * @retval API_YES if the column block is in halo.
 * @retval API_NO  if the column block is not in halo.
 */
static inline
int cblk_ishalo( SolverMatrix * datacode,
                 SolverCblk   * cblk ) {
#ifdef WITH_STARPU
    if ((size_t)cblk >= (size_t)datacode->hcblktab &&
        (size_t)cblk < (size_t)(datacode->hcblktab+datacode->hcblknbr)) {
        return API_YES;
    }
#endif
    return API_NO;
}

/**
 * Indicates whether a column block is a fanin column block.
 *
 * @param datacode SolverMatrix structure.
 * @param column block SolverCblk structure to test.
 *
 * @retval API_YES if the column block is a fanin column block.
 * @retval API_NO  if the column block is not a fanin column block.
 */
static inline
int cblk_isfanin( SolverMatrix * datacode,
                  SolverCblk   * cblk ) {
#ifdef WITH_STARPU
    PASTIX_INT clustnum;
    for (clustnum = 0; clustnum < datacode->clustnbr; clustnum++) {
        if ((size_t)cblk >= (size_t)datacode->fcblktab[clustnum] &&
            (size_t)cblk < (size_t)(datacode->fcblktab[clustnum] +
                                    datacode->fcblknbr[clustnum])) {
            return API_YES;
        }
    }
#endif
    return API_NO;
}

static inline
int fanin_getclustnum( SolverMatrix * datacode,
                       SolverCblk   * cblk ) {
#ifdef WITH_STARPU
    PASTIX_INT clustnum;
    for (clustnum = 0; clustnum < datacode->clustnbr; clustnum++) {
        if ((size_t)cblk >= (size_t)datacode->fcblktab[clustnum] &&
            (size_t)cblk < (size_t)(datacode->fcblktab[clustnum] +
                                    datacode->fcblknbr[clustnum])) {
            return clustnum;
        }
    }
    assert(-1);
#endif
    return 0;
}


/**
 * Indicates whether a column block is a local column block.
 *
 * @param datacode SolverMatrix structure.
 * @param column block SolverCblk structure to test.
 *
 * @retval API_YES if the column block is a local column block.
 * @retval API_NO  if the column block is not a local column block.
 */
static inline
int cblk_islocal( SolverMatrix * datacode,
                  SolverCblk   * cblk ) {
    if ((size_t)cblk >= (size_t)datacode->cblktab &&
        (size_t)cblk < (size_t)(datacode->cblktab+datacode->cblknbr)) {
        return API_YES;
    }
    return API_NO;
}


/**
 * Get the index of a local column block.
 *
 * @param datacode SolverMatrix structure.
 * @param column block SolverCblk structure to test.
 *
 * @returns the index of the column block.
 */
static inline
PASTIX_INT cblk_getnum( SolverMatrix * datacode,
                          SolverCblk   * cblk ) {
    assert(cblk_islocal(datacode, cblk) == API_YES);
    return cblk - datacode->cblktab;
}


/**
 * Get the index of a halo column block.
 *
 * @param datacode SolverMatrix structure.
 * @param column block SolverCblk structure to test.
 *
 * @returns the index of the column block.
 */
static inline
PASTIX_INT hcblk_getnum( SolverMatrix * datacode,
                           SolverCblk   * cblk ) {
#ifdef WITH_STARPU
    assert(cblk_ishalo(datacode, cblk) == API_YES);
    return cblk - datacode->hcblktab;
#else
    return -1;
#endif
}


/**
 * Get the index of a fanin block.
 *
 * @param datacode SolverMatrix structure.
 * @param block SolverBlok structure to test.
 *
 * @returns the index of the column block.
 */
static inline
PASTIX_INT fblok_getnum( SolverMatrix * datacode,
                           SolverBlok   * blok,
                           PASTIX_INT   procnum ) {
#ifdef WITH_STARPU
    return blok - datacode->fbloktab[procnum];

#else
    return -1;
#endif
}

/**
 * Get the index of a local block.
 *
 * @param datacode SolverMatrix structure.
 * @param column block SolverBlok structure to test.
 *
 * @returns the index of the column block.
 */
static inline
PASTIX_INT blok_getnum( SolverMatrix * datacode,
                          SolverBlok   * blok ) {
    return blok - datacode->bloktab;
}


/**
 * Get the index of a halo column block.
 *
 * @param datacode SolverMatrix structure.
 * @param column block SolverBlok structure to test.
 *
 * @returns the index of the column block.
 */
static inline
PASTIX_INT hblok_getnum( SolverMatrix * datacode,
                           SolverBlok   * blok ) {
#ifdef WITH_STARPU
    return blok - datacode->hbloktab;
#else
    return -1;
#endif
}


/**
 * Get the index of a fanin column block.
 *
 * @param datacode SolverMatrix structure.
 * @param column block SolverCblk structure to test.
 *
 * @returns the index of the column block.
 */
static inline
PASTIX_INT fcblk_getnum( SolverMatrix * datacode,
                           SolverCblk   * cblk,
                           PASTIX_INT   procnum ) {
#ifdef WITH_STARPU
    assert(cblk_isfanin(datacode, cblk) == API_YES);
    return cblk - datacode->fcblktab[procnum];

#else
    return -1;
#endif
}

static inline
PASTIX_INT fcblk_getorigin( SolverMatrix * datacode,
                              SolverMatrix * cblk ) {
#ifdef WITH_STARPU
    PASTIX_INT clustnum;
    for (clustnum = 0; clustnum < datacode->clustnbr; clustnum++) {
        if ((size_t)cblk >= (size_t)datacode->fcblktab[clustnum] &&
            (size_t)cblk < (size_t)(datacode->fcblktab[clustnum] +
                                    datacode->fcblknbr[clustnum])) {
            return clustnum;
        }
    }
#endif
    return -1;
}
/**
 * Get the number of columns of a column block.
 *
 * @param column block SolverCblk structure.
 *
 * @returns the number of columns in the column block.
 */
static inline
PASTIX_INT cblk_colnbr( SolverCblk * cblk ) {
    return cblk->lcolnum - cblk->fcolnum + 1;
}

/**
 * Get the number of columns of a column block.
 *
 * @param column block SolverCblk structure.
 *
 * @returns the number of columns in the column block.
 */
static inline
PASTIX_INT cblk_bloknbr( SolverCblk * cblk ) {
    return (cblk+1)->bloknum - cblk->bloknum;
}

/**
 * Get the number of rows of a block.
 *
 * @param block SolverBlok structure.
 *
 * @returns the number of rows in the block.
 */
static inline
PASTIX_INT blok_rownbr( SolverBlok * blok ) {
    return blok->lrownum - blok->frownum + 1;
}

/**
 * Get the number of rows of a column block.
 *
 * @param column block SolverCblk structure.
 *
 * @returns the number of rows in the column block.
 */
static inline
PASTIX_INT cblk_rownbr( SolverMatrix * s, SolverCblk * cblk ) {
    PASTIX_INT rownbr = 0;
    SolverBlok * blok;
    SolverBlok * first;
#ifdef WITH_STARPU
    if (cblk_islocal(s, cblk))
#endif
      first = s->bloktab;
#ifdef WITH_STARPU
    else if (cblk_ishalo(s, cblk))
      first = s->hbloktab;
    else {
      PASTIX_INT clustnum;
      for (clustnum = 0; clustnum < s->clustnbr; clustnum++) {
        if ((size_t)cblk >= (size_t)s->fcblktab[clustnum] &&
            (size_t)cblk < (size_t)(s->fcblktab[clustnum] +
                                    s->fcblknbr[clustnum])) {
          first = s->fbloktab[clustnum];
        }
      }
    }
#endif
    for (blok = first+cblk->bloknum; blok < first+cblk[1].bloknum; blok++)
        rownbr += blok_rownbr(blok);
    return rownbr;
}


/* static inline */
/* PASTIX_INT cblk_save( SolverCblk * cblk, */
/*                       char * name, */
/*                       pastix_complex64_t * coef) { */
/* #ifdef PASTIX_DUMP_CBLK */
/*     PASTIX_INT i,j; */
/*     SolverBlok *b; */
/*     FILE * file = fopen(name, "w"); */
/*     for ( b = cblk->bloknum; b < cblk[1].bloknum; b++) { */
/*         fprintf(file, "%ld %ld\n", b->frownum, b->lrownum); */
/*     } */
/*     for (j = 0; j < cblk->stride; j++) { */
/*         for (i = 0; i < cblk_colnbr(cblk); i++) { */
/*             fprintf(file, "(%10.5lg %10.5lg)", */
/*                 creal(coef[j+i*cblk->stride]), */
/*                 cimag(coef[j+i*cblk->stride])); */
/*         } */
/*         fprintf(file, "\n"); */
/*     } */
/*     fclose(file); */
/* #endif */
/*     return PASTIX_SUCCESS; */
/* } */
/**
 * Indicate if a blok is included inside an other block.
 * i.e. indicate if the row range of the first block is included in the
 * one of the second.
 *
 * @param first block SolverBlok structure to test.
 * @param second block SolverBlok structure to test.
 *
 * @retval true   if the first block is     included in the second one.
 * @retval false  if the first block is not included in the second one.
 */
#  if defined(NAPA_SOPALIN)
static inline int is_block_inside_fblock( SolverBlok *blok,
                                            SolverBlok *fblok ) {
    return (((blok->frownum >= fblok->frownum) &&
             (blok->lrownum <= fblok->lrownum)) ||
            ((blok->frownum <= fblok->frownum) &&
             (blok->lrownum >= fblok->lrownum)) ||
            ((blok->frownum <= fblok->frownum) &&
             (blok->lrownum >= fblok->frownum)) ||
            ((blok->frownum <= fblok->lrownum) &&
             (blok->lrownum >= fblok->lrownum)));
}
#  else
static inline int is_block_inside_fblock( SolverBlok *blok,
                                            SolverBlok *fblok ) {
    return ((blok->frownum >= fblok->frownum) &&
            (blok->lrownum <= fblok->lrownum));
}
#  endif /* defined(NAPA_SOPALIN) */

#endif /* SOLVER_H */
