/**
 *  PaStiX CSC management routines.
 *
 *  PaStiX is a software package provided by Inria Bordeaux - Sud-Ouest,
 *  LaBRI, University of Bordeaux 1 and IPB.
 *
 * @version 1.0.0
 * @author Mathieu Faverge
 * @author Pierre Ramet
 * @author Xavier Lacoste
 * @date 2011-11-11
 * @precisions normal z -> c d s
 *
 **/
#include "common.h"
#include "z_csc_utils.h"

/* /\* */
/*    Function: cmp_colrow */

/*    Used for qsort to sort arrays of pastix_int_t following their first element. */

/*    Returns the difference between the first element of *p1* and */
/*    the first element of *p2* */

/*    Parameters: */
/*      p1 - the first array to compare */
/*      p2 - the second array to compare */
/* *\/ */
/* int */
/* cmp_colrow(const void *p1, const void *p2) */
/* { */
/*   return ((* (pastix_int_t * const *) p1) - (*(pastix_int_t * const *) p2)); */
/* } */

/*
  Function: z_csc_symgraph


  Modify the CSC to a symetric graph one.
  Don't use it on a lower symetric CSC
  it would give you all the CSC upper + lower.

  External function.

  Parameters:
    n     - Number of columns/vertices
    ia    - Starting index of each column in *ja* and *a*
    ja    - Row index of each element
    a     - Value of each element,can be NULL
    newn  - New number of column
    newia - Starting index of each column in *ja* and *a*
    newja - Row index of each element
    newa  - Value of each element,can be NULL

 */
int z_csc_symgraph( pastix_int_t               n,
                    const pastix_int_t        *ia,
                    const pastix_int_t        *ja,
                    const pastix_complex64_t  *a,
                    pastix_int_t              *newn,
                    pastix_int_t             **newia,
                    pastix_int_t             **newja,
                    pastix_complex64_t       **newa)
{
  return z_csc_symgraph_int(n, ia, ja, a, newn, newia, newja, newa, API_NO);
}

/*
  Function: csc_symgraph_int


  Modify the CSC to a symetric graph one.
  Don't use it on a lower symetric CSC
  it would give you all the CSC upper + lower.

  Parameters:
    n           - Number of columns/vertices
    ia          - Starting index of each column in *ja* and *a*
    ja          - Row index of each element
    a           - Value of each element,can be NULL
    newn        - New number of column
    newia       - Starting index of each column in *ja* and *a*
    newja       - Row index of each element
    newa        - Value of each element,can be NULL
    malloc_flag - flag to indicate if function call is intern to pastix or extern.
 */
int z_csc_symgraph_int (pastix_int_t               n,
                        const pastix_int_t        *ia,
                        const pastix_int_t        *ja,
                        const pastix_complex64_t  *a,
                        pastix_int_t              *newn,
                        pastix_int_t             **newia,
                        pastix_int_t             **newja,
                        pastix_complex64_t       **newa,
                        int                        malloc_flag)
{
  pastix_int_t * nbrEltCol = NULL; /* nbrEltCol[i] = Number of elt to add in column i */
  pastix_int_t * cia       = NULL; /* ia of diff between good CSC and bad CSC */
  pastix_int_t * cja       = NULL; /* ja of diff between good CSC and bad CSC */
  pastix_int_t   nbr2add;          /* Number of elt to add */
  pastix_int_t   itercol, iterrow, iterrow2; /* iterators */
  pastix_int_t   l = ia[n] -1;
  pastix_int_t   newl;

  /* Ncol=Nrow don't need change */
  *newn = n;

  MALLOC_INTERN(nbrEltCol, n, pastix_int_t);
  /* !! Need check for malloc */

  /* Init nbrEltCol */
  for (itercol=0; itercol<n; itercol++)
    {
      nbrEltCol[itercol]=0;
    }

  /* Compute number of element by col to add for correcting the CSC */
  for (itercol=0; itercol<n; itercol++)
    {
      for (iterrow=ia[itercol]-1; iterrow<ia[itercol+1]-1; iterrow++)
        {
          if (ja[iterrow] != (itercol+1))
            {
              /* Not diagonal elt */
              /* So we have a (i,j) and we are looking for a (j,i) elt */
              /* i = itercol+1, j=ja[iterrow] */
              int rowidx=ja[iterrow]-1;
              int flag=0;

              for (iterrow2=ia[rowidx]-1; iterrow2<ia[rowidx+1]-1; iterrow2++)
                {
                  if (ja[iterrow2] == itercol+1)
                    {
                      /* Ok we found (j,i) so stop this madness */
                      flag = 1;
                      break;
                    }
                }

              if (flag==0)
                {
                  /* We never find (j,i) so increase nbrEltCol[j] */
                  (nbrEltCol[ja[iterrow]-1])++;
                }
            }
        }
    }

  /* Compute number of element to add */
  /* And cia=ia part of csc of element to add */
  /* kind of a diff between the corrected one and the original CSC */
  MALLOC_INTERN(cia, n+1, pastix_int_t);
  /* !! Need checking good alloc) */
  nbr2add=0;
  for (itercol=0;itercol<n;itercol++)
    {
      cia[itercol]=nbr2add;
      nbr2add += nbrEltCol[itercol];
    }
  cia[n]=nbr2add;
  /*fprintf(stderr, "nbr of elt to add %ld\n", nbr2add);*/

  if (nbr2add != 0)
    {
      /* Build cja */
      /* like cia, cja is ja part of diff CSC */
      MALLOC_INTERN(cja, nbr2add, pastix_int_t);
      /* !! again we need check of memAlloc */

      /* We walkthrough again the csc */
      for (itercol=0;itercol<n;itercol++)
        {
          for (iterrow=ia[itercol]-1;iterrow<ia[itercol+1]-1;iterrow++)
            {
              if (ja[iterrow] != itercol+1)
                {
                  /* we find (i,j) need to find (j,i) */
                  int rowidx=ja[iterrow]-1;
                  int flag=0;

                  for (iterrow2=ia[rowidx]-1;iterrow2<ia[rowidx+1]-1;iterrow2++)
                    {
                      if (ja[iterrow2] == itercol+1)
                        {
                          /* find (j,i) */
                          flag=1;
                          break;
                        }
                    }

                  if (flag==0)
                    {
                      /* We don't find (j,i) so put in diff CSC (cia,cja,0) */
                      pastix_int_t index=ja[iterrow]-1;
                      /* cia[index] = index to put in cja the elt */
                      cja[cia[index]] = itercol+1;
                      (cia[index])++;
                    }
                }
            }
        }

      /* Restore cia */
      cia[0]=0;
      for (itercol=0;itercol<n;itercol++)
        {
          cia[itercol+1]=cia[itercol]+nbrEltCol[itercol];
        }

      memFree_null(nbrEltCol);

      /* Build corrected csc */
      newl = l+nbr2add;

      if (malloc_flag == API_NO)
        {
          /* Ici on a des malloc car le free est externe */
          MALLOC_EXTERN(*newia, n+1, pastix_int_t);
          MALLOC_EXTERN(*newja, newl, pastix_int_t);
          if (a != NULL)
            MALLOC_EXTERN(*newa, newl, pastix_complex64_t);
        }
      else
        {
          /* Ici on a des memAlloc car le free est interne */
          MALLOC_INTERN(*newia, n+1, pastix_int_t);
          MALLOC_INTERN(*newja, newl, pastix_int_t);
          if (a != NULL)
            MALLOC_INTERN(*newa, newl, pastix_complex64_t);
        }
      iterrow2 = 0; /* iterator of the CSC diff */
      for (itercol=0; itercol<n; itercol++)
        {
          (*newia)[itercol] = ia[itercol]+iterrow2;
          for (iterrow=ia[itercol]-1;iterrow<ia[itercol+1]-1;iterrow++)
            {
              /* we add the new elt with respect of order in row */
              while ((iterrow2<cia[itercol+1]) &&
                     (ja[iterrow] > cja[iterrow2]))
                {
                  /* we have elt(s) to add with a row lower than ja[iterrow] */
                  (*newja)[iterrow+iterrow2]=cja[iterrow2];
                  if (a != NULL)
                    (*newa)[iterrow+iterrow2]=0.;
                  iterrow2++;
                }

              /* Put the elt from the origin CSC */
              (*newja)[iterrow+iterrow2] = ja[iterrow];
              if (a != NULL)
                (*newa)[iterrow+iterrow2] = a[iterrow];
            }

          /* Since we put elt with a row lower than elt in the origin CSC */
          /* We could have some elt to add after the last elt in the column */
          while(iterrow2<cia[itercol+1])
            {
              (*newja)[iterrow+iterrow2]=cja[iterrow2];
              if (a != NULL)
                (*newa)[iterrow+iterrow2]=0.;
              iterrow2++;
            }
        }

      (*newia)[n]=ia[n]+iterrow2;
      memFree_null(cja);

    }
  else
    {
      /* No correction to do */
      memFree_null(nbrEltCol);
      newl = l;
      if (malloc_flag == API_NO)
        {
          /* ici on a des mallocs car le free est externe */
          MALLOC_EXTERN(*newia, n+1, pastix_int_t);
          MALLOC_EXTERN(*newja, l, pastix_int_t);
          if (a != NULL)
            MALLOC_EXTERN(*newa, l, pastix_complex64_t);
        }
      else
        {
          /* ici on a des memAllocs car le free est interne */
          MALLOC_INTERN(*newia, n+1, pastix_int_t);
          MALLOC_INTERN(*newja, l,   pastix_int_t);
          if (a != NULL)
            MALLOC_INTERN(*newa, l, pastix_complex64_t);
        }
      memcpy((*newia), ia, (n+1)*sizeof(pastix_int_t));
      memcpy((*newja), ja, l * sizeof(pastix_int_t));
      if (a != NULL)
        memcpy((*newa) , a , l * sizeof(pastix_complex64_t));
    }
  memFree_null(cia);

  return EXIT_SUCCESS;
}


/**
    Function: z_csc_noDiag

    Supress diagonal term.
    After this call, *ja* can be reallocated to *ia[n] -1*.

    Parameters:
      n  - size of the matrix.
      ia - Index in *ja* and *a* of the first element of each column
      ja - row of each element
      a  - value of each element, can be set to NULL

    Returns:
      ia and ja tabulars modified.
*/
void z_csc_noDiag(pastix_int_t        baseval,
                  pastix_int_t        n,
                  pastix_int_t       *ia,
                  pastix_int_t       *ja,
                  pastix_complex64_t *a)
{
  pastix_int_t i, j;
  pastix_int_t indj;
  pastix_int_t *old_ia = NULL;

  MALLOC_INTERN(old_ia, n+1, pastix_int_t);
  memcpy(old_ia, ia, sizeof(pastix_int_t)*(n+1));

  ASSERT(ia[0]==baseval,MOD_SOPALIN);

  indj = 0;
  /*fprintf(stdout, "NNZ with diag = %ld \n", ia[n]);*/

  for(i=0;i<n;i++)
    {
      /* ia[i] = number of column already counted */
      ia[i] = indj+baseval;
      /* for each row number in each column i */
      for(j=old_ia[i];j<old_ia[i+1];j++)
        /* if element is not diagonal
           we add it in ja and we count it */
        if(ja[j-baseval] != i+baseval)
          {
            ja[indj] = ja[j-baseval];
            if (a != NULL)
              a[indj] = a [j -baseval];
            indj++;
          }
    }
  ia[n] = indj+baseval;

  assert( ia[n] <= old_ia[n] );

  /*fprintf(stdout, "NNZ without diag = %ld \n", ia[n]);*/
  memFree_null(old_ia);
}

/*
  Function: z_csc_check_doubles

  Check if the csc contains doubles and if correct if asked

  Assumes that the CSC is sorted.

  Assumes that the CSC is Fortran numeroted (base 1)

  Parameters:
    n         - Size of the matrix.
    colptr    - Index in *rows* and *values* of the first element of each column
    rows      - row of each element
    values    - value of each element (Can be NULL)
    dof       - Number of degrees of freedom
    flag      - Indicate if user wants correction (<API_BOOLEAN>)
    flagalloc - indicate if allocation on CSC uses internal malloc.

  Returns:
    API_YES - If the matrix contained no double or was successfully corrected.
    API_NO  - Otherwise.
*/
int z_csc_check_doubles(pastix_int_t         n,
                        pastix_int_t        *colptr,
                        pastix_int_t       **rowptr,
                        pastix_complex64_t **values,
                        int                  dof,
                        int                  flag,
                        int                  flagalloc)
{
  pastix_int_t     i,j,k,d;
  int     doubles = 0;
  pastix_int_t   * tmprows = NULL;
  pastix_complex64_t * tmpvals = NULL;
  pastix_int_t     index = 0;
  pastix_int_t     lastindex = 0;

  ASSERT(values == NULL || dof > 0, MOD_SOPALIN);
  ASSERT(flag == API_NO || flag == API_YES, MOD_SOPALIN);
  ASSERT(colptr[0] == 1, MOD_SOPALIN);
  ASSERT(n >= 0, MOD_SOPALIN);

  for (i = 0; i < n; i++)
    {
      for (j = colptr[i]-1; j < colptr[i+1]-1; j = k)
        {
          (*rowptr)[index]   = (*rowptr)[j];
          if (values != NULL)
            for (d = 0; d < dof*dof; d++)
              (*values)[index*dof*dof+d] = (*values)[j*dof*dof+d];

          k = j+1;
          while (k < colptr[i+1]-1 && (*rowptr)[j] == (*rowptr)[k])
            {
              if (flag == API_NO)
                return API_NO;
              if (values != NULL)
                for (d = 0; d < dof*dof; d++)
                  (*values)[index*dof*dof+d] += (*values)[k*dof*dof+d];
              doubles++;
              k++;
            }
          index++;
        }

      colptr[i] = lastindex+1;
      lastindex = index;
    }
  if (flag == API_NO)
    return API_YES;
  ASSERT(index == colptr[n]-1-doubles, MOD_SOPALIN);
  if (doubles > 0)
    {
      colptr[n] = lastindex+1;
      if (flagalloc == API_NO)
        {
          MALLOC_EXTERN(tmprows, lastindex, pastix_int_t);
          if (values != NULL)
            MALLOC_EXTERN(tmpvals, lastindex*dof*dof, pastix_complex64_t);
        }
      else
        {
          MALLOC_INTERN(tmprows, lastindex, pastix_int_t);
          if (values != NULL)
            MALLOC_INTERN(tmpvals, lastindex*dof*dof, pastix_complex64_t);
        }

      memcpy(tmprows, *rowptr,   lastindex*sizeof(pastix_int_t));
      if (values != NULL)
        memcpy(tmpvals, *values, lastindex*dof*dof*sizeof(pastix_complex64_t));
      if (flagalloc == API_NO)
        {
          free(*rowptr);
          if (values != NULL)
            free(*values);
        }
      else
        {
          memFree_null(*rowptr);
          if (values != NULL)
            memFree_null(*values);
        }
      *rowptr   = tmprows;
      if (values != NULL)
        *values = tmpvals;
    }
  return API_YES;

}

/*
  Function: z_csc_checksym

    Check if the CSC graph is symetric.

    For all local column C,

    For all row R in the column C,

    We look in column R if we have the row number C.

    If we can correct we had missing non zeros.

    Assumes that the CSC is Fortran numbered (1 based).

    Assumes that the matrix is sorted.

  Parameters:
    n        - Number of local columns
    colptr   - Starting index of each columns in *ja*
    rows     - Row of each element.
    values   - Value of each element.
    correct  - Flag indicating if we can correct the symmetry.
    alloc    - indicate if allocation on CSC uses internal malloc.
    dof      - Number of degrees of freedom.
*/
int z_csc_checksym(pastix_int_t         n,
                   pastix_int_t        *colptr,
                   pastix_int_t       **rows,
                   pastix_complex64_t **values,
                   int                  correct,
                   int                  alloc,
                   int                  dof)
{
  pastix_int_t            i,j,k,l,d;
  pastix_int_t            index1;
  pastix_int_t            index2;
  int            found;
  pastix_int_t            toaddsize;
  pastix_int_t            toaddcnt;
  pastix_int_t         *  toadd      = NULL;
  pastix_int_t         *  tmpcolptr  = NULL;
  pastix_int_t         *  tmprows    = NULL;
  pastix_complex64_t       *  tmpvals    = NULL;

  /* For all local column C,
     For all row R in the column C,

     If the row number R correspond to a local column,
     We look in column R if we have the row number C.

     Else,
   */
  toaddcnt  = 0;
  toaddsize = 0;
  for (i = 0; i < n; i++)
    {
      for (j = (colptr)[i]-1; j < (colptr)[i+1]-1; j++)
        {
          if ((*rows)[j] != i+1)
            {
              /* not in diagonal */
              k = (*rows)[j];
              found = 0;
              for (l = (colptr)[k-1]-1; l < (colptr)[k-1+1]-1; l++)
                {
                  if (i+1 == (*rows)[l])
                    {
                      found = 1;
                      break;
                    }
                  if (i+1 < (*rows)[l])
                    {
                      /* The CSC is sorted */
                      found = 0;
                      break;
                    }
                }
              if (found == 0)
                {
                  if (correct == API_NO)
                    return EXIT_FAILURE;
                  else
                    {
                      if (toaddsize == 0)
                        {
                          toaddsize = n/2;
                          MALLOC_INTERN(toadd, 2*toaddsize, pastix_int_t);
                        }
                      if (toaddcnt >= toaddsize)
                        {
                          toaddsize += toaddsize/2 + 1;
                          if (NULL ==
                              (toadd =
                               (pastix_int_t*)memRealloc(toadd,
                                                2*toaddsize*sizeof(pastix_int_t))))
                              MALLOC_ERROR("toadd");
                        }
                      toadd[2*toaddcnt]     = (*rows)[j];
                      toadd[2*toaddcnt + 1] = i+1;
                      /* fprintf(stdout, "Adding %ld, %ld\n", (long)(i+1), (long)(*rows)[j]); */
                      toaddcnt++;
                    }
                }
            }
        }
    }

  if (toaddcnt > 0)
    {

      intSort2asc1(toadd, toaddcnt);
      /* Correct here is API_YES, otherwise we would have return EXIT_FAILURE
         Or toaddcnt == 0*/
      MALLOC_INTERN(tmpcolptr, n + 1, pastix_int_t);
      if (alloc == API_NO)
        {
          MALLOC_EXTERN(tmprows, colptr[n]-1 + toaddcnt, pastix_int_t);
          if (values != NULL)
            {
              MALLOC_EXTERN(tmpvals, colptr[n]-1 + toaddcnt, pastix_complex64_t);
            }
        }
      else
        {
          MALLOC_INTERN(tmprows, colptr[n]-1 + toaddcnt, pastix_int_t);
          if (values != NULL)
            {
              MALLOC_INTERN(tmpvals, colptr[n]-1 + toaddcnt, pastix_complex64_t);
            }
        }
      /* Build tmpcolptr

         tmpcolptr[i+1] will contain the number of element of
         the column i
       */
      index1 = 0;
      index2 = 0;
      for (i = 0; i <  n; i++)
        {
          tmpcolptr[i] = index2+1;
          for (j = colptr[i]-1; j < colptr[i+1]-1; j++)
            {
              if (index1 < toaddcnt &&
                  (toadd[2*index1] == i+1) &&
                  (toadd[2*index1+1] < (*rows)[j]))
                {
                  tmprows[index2] = toadd[2*index1+1];
                  if (values != NULL)
                    {
                      for (d = 0; d < dof*dof ; d++)
                        tmpvals[index2*dof*dof+d] = 0.0;
                    }
                  index1++;
                  j--; /* hack do not increment j this step of the loop */
                }
              else
                {
                  tmprows[index2] = (*rows)[j];
                  if (values != NULL)
                    {
                      for (d = 0; d < dof*dof ; d++)
                        tmpvals[index2*dof*dof+d] = (*values)[j*dof*dof+d];
                    }
                }
              index2++;
            }

          while(index1 < toaddcnt && toadd[2*index1] == i+1)
            {
              tmprows[index2] = toadd[2*index1+1];
              if (values != NULL)
                {
                  for (d = 0; d < dof*dof ; d++)
                    tmpvals[index2*dof*dof+d] = 0.0;
                }
              index1++;
              index2++;
            }
        }
      tmpcolptr[n] = index2+1;
      ASSERT((tmpcolptr[n] - 1) == (colptr[n] - 1 + toaddcnt), MOD_SOPALIN);

      memcpy(colptr, tmpcolptr, (n+1)*sizeof(pastix_int_t));
      memFree_null(tmpcolptr);
      memFree_null(toadd);
      if (alloc == API_NO)
        {
          free(*rows);
          if (values != NULL)
            {
              free(*values);
            }
        }
      else
        {
          memFree_null(*rows);
          if (values != NULL)
            {
              memFree_null(*values);
            }
        }
      *rows   = tmprows;
      if (values != NULL)
        {
          *values = tmpvals;
        }
    }
  return EXIT_SUCCESS;
}


/*
  Function: z_csc_colPerm

  Performs column permutation on a CSC

  Parameters:
    n     - Size of the matrix.
    ia    - Index of first element of each column in *ia* and *a*
    ja    - Rows of non zeros of the matrix.
    a     - Values of non zeros of the matrix.
    cperm - Permutation to perform
*/
void z_csc_colPerm(pastix_int_t n, pastix_int_t *ia, pastix_int_t *ja, pastix_complex64_t *a, pastix_int_t *cperm)
{
  pastix_int_t i, k;
  pastix_int_t   *newja = NULL;
  pastix_int_t   *newia = NULL;
  pastix_complex64_t *newa  = NULL;
  int numflag, numflag2;

  numflag = ia[0];
  numflag2 = 1;
  for(i=0;i<n;i++)
    if(cperm[i] == 0)
      {
        numflag2 = 0;
        break;
      }

  if(numflag2 != numflag)
    {
      errorPrint("CSC_colPerm: rperm not in same numbering than the CSC.");
      exit(-1);
    }


  if(numflag == 1)
    {
      z_csc_Fnum2Cnum(ja, ia, n);
      for(i=0;i<n;i++)
        cperm[i]--;
    }

  MALLOC_INTERN(newia, n+1,   pastix_int_t);
  MALLOC_INTERN(newja, ia[n], pastix_int_t);
  MALLOC_INTERN(newa,  ia[n], pastix_complex64_t);


  newia[0] = 0;
  for(i=0;i<n;i++)
    {
#ifdef DEBUG_KASS
      ASSERT(cperm[i]>=0 && cperm[i] < n, MOD_KASS);
#endif
      newia[cperm[i]+1] = ia[i+1]-ia[i];
    }

#ifdef DEBUG_KASS
  for(i=1;i<=n;i++)
    ASSERT(newia[i] >0, MOD_KASS);
#endif

  for(i=1;i<=n;i++)
    newia[i] += newia[i-1];

#ifdef DEBUG_KASS
  ASSERT(newia[n] == ia[n], MOD_KASS);
#endif


  for(i=0;i<n;i++)
    {
      k = cperm[i];
#ifdef DEBUG_KASS
      ASSERT(newia[k+1]-newia[k] == ia[i+1]-ia[i], MOD_KASS);
#endif
      memcpy(newja + newia[k], ja + ia[i], sizeof(pastix_int_t)*(ia[i+1]-ia[i]));
      memcpy(newa + newia[k], a + ia[i], sizeof(pastix_complex64_t)*(ia[i+1]-ia[i]));

    }

  memcpy(ia, newia, sizeof(pastix_int_t)*(n+1));
  memcpy(ja, newja, sizeof(pastix_int_t)*ia[n]);
  memcpy(a, newa, sizeof(pastix_complex64_t)*ia[n]);

  memFree(newia);
  memFree(newja);
  memFree(newa);
  if(numflag == 1)
    {
      z_csc_Cnum2Fnum(ja, ia, n);
      for(i=0;i<n;i++)
        cperm[i]++;
    }
}


/*
  Function: z_csc_colScale

  Moved from kass, only used in MC64
*/
void z_csc_colScale(pastix_int_t        n,
                    pastix_int_t       *ia,
                    pastix_int_t       *ja,
                    pastix_complex64_t *a,
                    pastix_complex64_t *dcol)
{
  pastix_int_t i, j;
  int numflag;
  pastix_complex64_t d;
  numflag = ia[0];

  if(numflag == 1)
    z_csc_Fnum2Cnum(ja, ia, n);

  for(i=0;i<n;i++)
    {
      d = dcol[i];
      for(j=ia[i];j<ia[i+1];j++)
        {
          /***@@@ OIMBE DSCAL **/
          a[j] *= d;
        }
    }

  if(numflag == 1)
    z_csc_Cnum2Fnum(ja, ia, n);
}

/*
  Function: z_csc_rowScale

  Moved from kass, only used in MC64
*/
void z_csc_rowScale(pastix_int_t        n,
                    pastix_int_t       *ia,
                    pastix_int_t       *ja,
                    pastix_complex64_t *a,
                    pastix_complex64_t *drow)
{
  pastix_int_t i, j;
  int numflag;
  numflag = ia[0];

  if(numflag == 1)
    z_csc_Fnum2Cnum(ja, ia, n);

  for(i=0;i<n;i++)
    {
      for(j=ia[i];j<ia[i+1];j++)
        {
#ifdef DEBUG_KASS
          ASSERT(ja[j]>0 && ja[j] <n, MOD_KASS);
#endif
          a[j] *= drow[ja[j]];
        }
    }

  if(numflag == 1)
    z_csc_Cnum2Fnum(ja, ia, n);
}

/*
 * z_csc_sort:
 *
 * Sort CSC columns
 *
 * Parameters:
 *   n  - Number of columns
 *   ia - Index of first element of each column in *ia*.
 *   ja - Rows of each non zeros.
 *   a  - Values of each non zeros.
*/
void z_csc_sort(pastix_int_t        n,
                pastix_int_t       *ia,
                pastix_int_t       *ja,
                pastix_complex64_t *a,
                pastix_int_t        ndof)
{
  pastix_int_t i;
  int numflag;
  pastix_int_t ndof2 = ndof * ndof;
  void * sortptr[3];
  numflag = ia[0];
  if(numflag == 1)
    z_csc_Fnum2Cnum(ja, ia, n);
  if (a != NULL)
    {

      for(i=0;i<n;i++)
        {
          sortptr[0] = &ja[ia[i]];
          sortptr[1] = &a[ia[i]*ndof2];
          sortptr[2] = &ndof2;
          z_qsortIntFloatAsc(sortptr, ia[i+1] - ia[i]);
        }

    }
  else
    {
      for(i=0;i<n;i++)
        intSort1asc1(&ja[ia[i]], ia[i+1] - ia[i]);

    }
  if(numflag == 1)
    z_csc_Cnum2Fnum(ja, ia, n);
}

/*
  Function: z_csc_Fnum2Cnum

  Convert CSC numbering from fortran numbering to C numbering.

  Parameters:
    ja - Rows of each element.
    ia - First index of each column in *ja*
    n  - Number of columns
*/
void z_csc_Fnum2Cnum(pastix_int_t *ja,
                     pastix_int_t *ia,
                     pastix_int_t  n)
{
  pastix_int_t i, j;
  for(i=0;i<=n;i++)
    ia[i]--;

  for(i=0;i<n;i++)
    for(j=ia[i];j<ia[i+1];j++)
      ja[j]--;

}

/*
  Function: z_csc_Cnum2Fnum

  Convert CSC numbering from C numbering to Fortran numbering.

  Parameters:
    ja - Rows of each element.
    ia - First index of each column in *ja*
    n  - Number of columns
*/
void z_csc_Cnum2Fnum(pastix_int_t *ja,
                     pastix_int_t *ia,
                     pastix_int_t  n)
{
  pastix_int_t i, j;

  for(i=0;i<n;i++)
    for(j=ia[i];j<ia[i+1];j++)
      ja[j]++;

  for(i=0;i<=n;i++)
    ia[i]++;
}
/*
  Function: z_csc_buildZerosAndNonZerosGraphs

  Separate a graph in two graphs, following
  wether the diagonal term of a column is null or not.

  Parameters:
    n, colptr, rows, values  - The initial CSC
    n_nz, colptr_nz, rows_nz - The graph of the non-null diagonal part.
    n_z, colptr_z, rows_z    - The graph of the null diagonal part.
    perm                     - Permutation to go from the first graph to
                               the one composed of the two graph concatenated.
    revperm                  - Reverse permutation tabular.
    criteria                 - Value beside which a number is said null.
*/
int z_csc_buildZerosAndNonZerosGraphs(pastix_int_t         n,
                                      pastix_int_t        *colptr,
                                      pastix_int_t        *rows,
                                      pastix_complex64_t  *values,
                                      pastix_int_t        *n_nz,
                                      pastix_int_t       **colptr_nz,
                                      pastix_int_t       **rows_nz,
                                      pastix_int_t        *n_z,
                                      pastix_int_t       **colptr_z,
                                      pastix_int_t       **rows_z,
                                      pastix_int_t        *perm,
                                      pastix_int_t        *revperm,
                                      double               criteria)
{
  pastix_int_t  itercol;
  pastix_int_t  iterrow;

  pastix_int_t  ncoefszeros  = 0;
  pastix_int_t  ncoefsnzeros = 0;
  pastix_int_t  itercol_nz   = 0;
  pastix_int_t  itercol_z    = 0;
  int  seen;
  pastix_int_t  cntrows;

  for (itercol = 0; itercol <n; itercol++)
    {
      seen = 0;
      for (iterrow = colptr[itercol]-1; iterrow < colptr[itercol+1]-1; iterrow++)
        {
          if (itercol == rows[iterrow] -1 )
            {
              if (ABS_FLOAT(values[iterrow]) < criteria)
                {
                  (*n_z) ++;
                  ncoefszeros += colptr[itercol+1] - colptr[itercol];
                  seen = 1;
                }
              else
                {
                  (*n_nz) ++;
                  ncoefsnzeros += colptr[itercol+1] - colptr[itercol];
                  seen = 1;
                }
              break;
            }
        }
      if (colptr[itercol+1] == colptr[itercol]) /* empty column */
        {
          (*n_z)++;
          seen = 1;
        }
      if (seen == 0)/*  column without diag*/
        {
          (*n_z)++;
          ncoefszeros += colptr[itercol+1] - colptr[itercol];
        }
    }
  fprintf(stdout, "n_z %ld\n",  (long)*n_z);
  fprintf(stdout, "n_nz %ld\n", (long)*n_nz);
  ASSERT(*n_z+*n_nz == n, MOD_SOPALIN);
  if (*n_z == 0 || *n_nz == 0)
    return PASTIX_SUCCESS;

  for (itercol = 0; itercol <n; itercol++)
    {
      seen = 0;
      for (iterrow = colptr[itercol]-1; iterrow < colptr[itercol+1]-1; iterrow++)
        {
          if (itercol == rows[iterrow] -1 )
            {
              if (ABS_FLOAT(values[iterrow]) < criteria)
                {
                  perm[itercol] = (*n_nz) + itercol_z + 1;
                  itercol_z++;
                  seen = 1;
                }
              else
                {
                  perm[itercol] = itercol_nz + 1;
                  itercol_nz++;
                  seen = 1;
                }
            }
        }
      if (colptr[itercol] == colptr[itercol+1])
        { /* empty column */
          perm[itercol] = (*n_nz) + itercol_z + 1;
          itercol_z++;
          seen =1;
        }
      if (seen == 0)/*  column without diag*/
        {
          perm[itercol] = (*n_nz) + itercol_z + 1;
          itercol_z++;
        }
    }

  ASSERT(itercol_nz == *n_nz, MOD_SOPALIN);
  ASSERT(itercol_z  == *n_z, MOD_SOPALIN);
  for(itercol = 0; itercol < n; itercol++)
    revperm[perm[itercol]-1] = itercol + 1;

  MALLOC_INTERN(*colptr_nz, *n_nz + 1, pastix_int_t);
  MALLOC_INTERN(*rows_nz, ncoefsnzeros, pastix_int_t);
  cntrows = 0;
  for (itercol = 0; itercol <*n_nz; itercol++)
    {
      (*colptr_nz)[itercol] = cntrows + 1;
      for (iterrow = colptr[revperm[itercol]-1]-1; iterrow < colptr[revperm[itercol]-1+1]-1; iterrow++)
        {
          if (perm[rows[iterrow]-1] - 1 < *n_nz )
            {
              (*rows_nz)[cntrows] = perm[rows[iterrow]-1];
              cntrows++;
            }

        }

    }
  (*colptr_nz)[*n_nz] = cntrows+1;

  MALLOC_INTERN(*colptr_z, *n_z + 1, pastix_int_t);
  MALLOC_INTERN(*rows_z, ncoefszeros, pastix_int_t);
  cntrows = 0;
  for (itercol = 0; itercol <*n_z; itercol++)
    {
      (*colptr_z)[itercol] = cntrows + 1;
      for (iterrow = colptr[revperm[itercol+*n_nz]-1]-1; iterrow < colptr[revperm[itercol+*n_nz]-1+1]-1; iterrow++)
        {
          if (perm[rows[iterrow]-1]  > *n_nz )
            {
              (*rows_z)[cntrows] = perm[rows[iterrow]-1] - *n_nz;
              cntrows++;
            }

        }

    }
  (*colptr_z)[*n_z] = cntrows+1;

  return PASTIX_SUCCESS;
}

/*
  Function: z_csc_isolate

  Isolate a list of unknowns at the end of the CSC.

  Parameters:
    n            - Number of columns.
    colptr       - Index of first element of each column in *ia*.
    rows         - Rows of each non zeros.
    n_isolate    - Number of unknow to isolate.
    isolate_list - List of unknown to isolate.
    perm         - permutation tabular.
    revperm      - reverse permutation tabular.
*/
int z_csc_isolate(pastix_int_t  n,
                  pastix_int_t *colptr,
                  pastix_int_t *rows,
                  pastix_int_t  n_isolate,
                  pastix_int_t *isolate_list,
                  pastix_int_t *perm,
                  pastix_int_t *revperm)
{
  pastix_int_t  itercol;
  pastix_int_t  iterrow;

  pastix_int_t  iter_isolate  = 0;
  pastix_int_t  iter_non_isolate  = 0;
  pastix_int_t *tmpcolptr = NULL;
  pastix_int_t *tmprows   = NULL;

  if (n_isolate == 0)
    {
      errorPrintW("No schur complement\n");
      return PASTIX_SUCCESS;
    }
  intSort1asc1(isolate_list, n_isolate);

  for (itercol = 0; itercol <n; itercol++)
    {
      if (iter_isolate < n_isolate &&
          itercol == isolate_list[iter_isolate]-1)
        {
          revperm[n-n_isolate+iter_isolate] = itercol;
          iter_isolate++;
        }
      else
        {
          revperm[iter_non_isolate] = itercol;
          iter_non_isolate++;
        }
    }
  ASSERT(iter_non_isolate == n - n_isolate, MOD_SOPALIN);
  ASSERT(iter_isolate == n_isolate, MOD_SOPALIN);

  MALLOC_INTERN(tmpcolptr, n - n_isolate + 1,       pastix_int_t);
  memset(tmpcolptr, 0, (n - n_isolate + 1)*sizeof(pastix_int_t));

  for(itercol = 0; itercol < n; itercol++)
    perm[revperm[itercol]] = itercol;

  for(itercol = 0; itercol < n; itercol++)
    {
      ASSERT(perm[itercol] < n, MOD_SOPALIN);
      ASSERT(perm[itercol] > -1, MOD_SOPALIN);
    }

  tmpcolptr[0] = 1;
  for (itercol = 0; itercol <n; itercol++)
    {
      if (perm[itercol] < n - n_isolate)
        {
          for (iterrow = colptr[itercol]-1; iterrow < colptr[itercol+1]-1; iterrow ++)
            {
              /* Count edges in each column of the new graph */
              if (perm[rows[iterrow]-1] < n-n_isolate)
                {
                  tmpcolptr[perm[itercol]+1]++;
                }
            }
        }
    }

  for (itercol = 0; itercol <n - n_isolate; itercol++)
    tmpcolptr[itercol+1] += tmpcolptr[itercol];

  MALLOC_INTERN(tmprows,   tmpcolptr[n- n_isolate]-1, pastix_int_t);
  for (itercol = 0; itercol <n; itercol++)
    {
      if (perm[itercol] < n - n_isolate)
        {
          for (iterrow = colptr[itercol]-1; iterrow < colptr[itercol+1]-1; iterrow ++)
            {
              /* Count edges in each column of the new graph */
              if (perm[rows[iterrow]-1] < n-n_isolate)
                {
                  tmprows[tmpcolptr[perm[itercol]]-1] = perm[rows[iterrow]-1]+1;
                  tmpcolptr[perm[itercol]]++;
                }
            }
        }
    }


  /* restore tmpcolptr */

  for (itercol = 1; itercol <n - n_isolate ; itercol++)
    {
      tmpcolptr[n - n_isolate - itercol] = tmpcolptr[n - n_isolate - itercol - 1];
    }
  tmpcolptr[0] = 1;



  ASSERT(colptr[n] >= tmpcolptr[n-n_isolate], MOD_SOPALIN);
  memcpy(colptr, tmpcolptr, (n-n_isolate + 1)*sizeof(pastix_int_t));
  memcpy(rows,   tmprows,   (colptr[n-n_isolate]-1)*sizeof(pastix_int_t));

  memFree_null(tmpcolptr);
  memFree_null(tmprows);

  return PASTIX_SUCCESS;
}

/*
  Function: csc_save

  Save a csc on disk.

  Parameters:
    n       - number of columns
    colptr  - First cscd starting index of each column in *ja* and *a*
    rows    - Row of each element in first CSCD
    values  - value of each cscd in first CSCD (can be NULL)
    dof     - Number of degrees of freedom
    outfile - Output stream.

  Return:
    PASTIX_SUCCESS

*/
int z_csc_save(pastix_int_t        n,
               pastix_int_t       *colptr,
               pastix_int_t       *rows,
               pastix_complex64_t *values,
               int                 dof,
               FILE               *outfile)
{

  pastix_int_t i;
  fprintf(outfile, "%ld %ld %d\n", (long)n, (long)dof, ((values == NULL)?0:1));
  /* Copie du colptr */
  for (i=0; i<n+1; i++)
    {
      fprintf(outfile, "%ld ", (long)colptr[i]);
      if (i%4 == 3) fprintf(outfile, "\n");
    }
  if ((i-1)%4 !=3) fprintf(outfile, "\n");

  /* Copie de JA */
  for (i=0; i<colptr[n]-1; i++)
    {
      fprintf(outfile, "%ld ", (long)rows[i]);
      if (i%4 == 3) fprintf(outfile, "\n");
    }
  if ((i-1)%4 !=3) fprintf(outfile, "\n");

  /* Copie de Avals */

  if (values != NULL)
    {
      for (i=0; i<(colptr[n]-1)*dof*dof; i++)
        {
#ifdef TYPE_COMPLEX
          fprintf(outfile, "%lg %lg ", (double)(creal(values[i])), (double)(cimag(values[i])));
#else
          fprintf(outfile, "%lg ", (double)(values[i]));
#endif
          if (i%4 == 3) fprintf(outfile, "\n");
        }
      if ((i-1)%4 !=3) fprintf(outfile, "\n");
    }
  return PASTIX_SUCCESS;

}

/*
  Function: csc_load

  Load a csc from disk.

  Fill *n*, *colptr*, *rows*, *values* and *dof* from *infile*.

  Parameters:
    n       - number of columns
    colptr  - First cscd starting index of each column in *ja* and *a*
    rows    - Row of each element in first CSCD
    values  - value of each cscd in first CSCD (can be NULL)
    dof     - Number of degrees of freedom
    outfile - Output stream.

  Return:
    PASTIX_SUCCESS

*/
int z_csc_load(pastix_int_t        *n,
               pastix_int_t       **colptr,
               pastix_int_t       **rows,
               pastix_complex64_t **values,
               int                 *dof,
               FILE                *infile)
{
  int  hasval;
  long tmp1, tmp2, tmp3, tmp4;
  double tmpflt1, tmpflt2, tmpflt3, tmpflt4;
#ifdef TYPE_COMPLEX
  double tmpflt5, tmpflt6, tmpflt7, tmpflt8;
#endif
  pastix_int_t i;
  if (3 != fscanf(infile, "%ld %ld %d\n", &tmp1, &tmp2, &hasval)){
    errorPrint("CSCD badly formated");
    return EXIT_FAILURE;
  }
  *n   = (pastix_int_t)tmp1;
  *dof = (int)tmp2;
  /* Copie de IA */
  *colptr = NULL;
  MALLOC_INTERN(*colptr, *n+1, pastix_int_t);
  for (i=0; i<*n+1+1-4; i+=4)
    {
      if (4 != fscanf(infile, "%ld %ld %ld %ld", &tmp1, &tmp2, &tmp3, &tmp4)){
        errorPrint("CSCD badly formated");
        return EXIT_FAILURE;
      }

      (*colptr)[i  ] = tmp1;
      (*colptr)[i+1] = tmp2;
      (*colptr)[i+2] = tmp3;
      (*colptr)[i+3] = tmp4;
    }
  switch (*n +1 - i)
    {
    case 3:
      if (3 != fscanf(infile, "%ld %ld %ld", &tmp1, &tmp2, &tmp3)){
        errorPrint("CSCD badly formated");
        return EXIT_FAILURE;
      }

      (*colptr)[i  ] = tmp1;
      (*colptr)[i+1] = tmp2;
      (*colptr)[i+2] = tmp3;
      break;
    case 2:
      if (2 != fscanf(infile, "%ld %ld", &tmp1, &tmp2)){
        errorPrint("CSCD badly formated");
        return EXIT_FAILURE;
      }

      (*colptr)[i  ] = tmp1;
      (*colptr)[i+1] = tmp2;
      break;
    case 1:
      if (1 != fscanf(infile, "%ld", &tmp1)){
        errorPrint("CSCD badly formated");
        return EXIT_FAILURE;
      }

      (*colptr)[i  ] = tmp1;
      break;
    }


  /* Copie de JA */
  (*rows) = NULL;
  MALLOC_INTERN(*rows, (*colptr)[*n]-(*colptr)[0], pastix_int_t);
  for (i=0; i< (*colptr)[*n]-(*colptr)[0]+1-4; i+=4)
    {
      if (4 != fscanf(infile, "%ld %ld %ld %ld", &tmp1, &tmp2, &tmp3, &tmp4)){
        errorPrint("CSCD badly formated");
        return EXIT_FAILURE;
      }

      (*rows)[i  ] = tmp1;
      (*rows)[i+1] = tmp2;
      (*rows)[i+2] = tmp3;
      (*rows)[i+3] = tmp4;
    }

  switch ( (*colptr)[*n]-(*colptr)[0] - i)
    {
    case 3:
      if (3 != fscanf(infile, "%ld %ld %ld", &tmp1, &tmp2, &tmp3)){
        errorPrint("CSCD badly formated");
        return EXIT_FAILURE;
      }

      (*rows)[i  ] = tmp1;
      (*rows)[i+1] = tmp2;
      (*rows)[i+2] = tmp3;
      break;
    case 2:
      if (2 != fscanf(infile, "%ld %ld", &tmp1, &tmp2)){
        errorPrint("CSCD badly formated");
        return EXIT_FAILURE;
      }

      (*rows)[i  ] = tmp1;
      (*rows)[i+1] = tmp2;
      break;
    case 1:
      if (1 != fscanf(infile, "%ld", &tmp1)){
        errorPrint("CSCD badly formated");
        return EXIT_FAILURE;
      }

      (*rows)[i  ] = tmp1;
      break;
    }

  /* Copie de Avals */
  if (hasval)
    {
      (*values) = NULL;

      MALLOC_INTERN(*values,  (*colptr)[*n]-(*colptr)[0], pastix_complex64_t);

      for (i=0; i< (*colptr)[*n]-(*colptr)[0]+1-4; i+=4)
        {
#ifdef TYPE_COMPLEX
          if (8 != fscanf(infile, "%lg %lg %lg %lg %lg %lg %lg %lg",
                          &tmpflt1, &tmpflt2, &tmpflt3, &tmpflt4,
                          &tmpflt5, &tmpflt6, &tmpflt7, &tmpflt8)){
            errorPrint("CSCD badly formated");
            return EXIT_FAILURE;
          }
          (*values)[i  ] = (pastix_complex64_t)(tmpflt1 + I * tmpflt2);
          (*values)[i+1] = (pastix_complex64_t)(tmpflt3 + I * tmpflt4);
          (*values)[i+2] = (pastix_complex64_t)(tmpflt5 + I * tmpflt6);
          (*values)[i+3] = (pastix_complex64_t)(tmpflt7 + I * tmpflt8);
#else
          if (4 != fscanf(infile, "%lg %lg %lg %lg",
                          &tmpflt1, &tmpflt2, &tmpflt3, &tmpflt4)){
            errorPrint("CSCD badly formated");
            return EXIT_FAILURE;
          }
          (*values)[i  ] = (pastix_complex64_t)tmpflt1;
          (*values)[i+1] = (pastix_complex64_t)tmpflt2;
          (*values)[i+2] = (pastix_complex64_t)tmpflt3;
          (*values)[i+3] = (pastix_complex64_t)tmpflt4;
#endif
        }
      switch ( (*colptr)[*n]-(*colptr)[0] - i )
        {
        case 3:
#ifdef TYPE_COMPLEX
          if (6 != fscanf(infile, "%lg %lg %lg %lg %lg %lg",
                          &tmpflt1, &tmpflt2, &tmpflt3, &tmpflt4,
                          &tmpflt5, &tmpflt6)){
            errorPrint("CSCD badly formated");
            return EXIT_FAILURE;
          }
          (*values)[i  ] = (pastix_complex64_t)(tmpflt1 + I * tmpflt2);
          (*values)[i+1] = (pastix_complex64_t)(tmpflt3 + I * tmpflt4);
          (*values)[i+2] = (pastix_complex64_t)(tmpflt5 + I * tmpflt6);
#else
          if (3 != fscanf(infile, "%lg %lg %lg",
                          &tmpflt1, &tmpflt2, &tmpflt3)){
            errorPrint("CSCD badly formated");
            return EXIT_FAILURE;
          }
          (*values)[i  ] = (pastix_complex64_t)tmpflt1;
          (*values)[i+1] = (pastix_complex64_t)tmpflt2;
          (*values)[i+2] = (pastix_complex64_t)tmpflt3;
#endif
          break;
        case 2:
#ifdef TYPE_COMPLEX
          if (4 != fscanf(infile, "%lg %lg %lg %lg",
                          &tmpflt1, &tmpflt2, &tmpflt3, &tmpflt4)){
            errorPrint("CSCD badly formated");
            return EXIT_FAILURE;
          }
          (*values)[i  ] = (pastix_complex64_t)(tmpflt1 + I * tmpflt2);
          (*values)[i+1] = (pastix_complex64_t)(tmpflt3 + I * tmpflt4);
#else
          if (2 != fscanf(infile, "%lg %lg",
                          &tmpflt1, &tmpflt2)){
            errorPrint("CSCD badly formated");
            return EXIT_FAILURE;
          }
          (*values)[i  ] = (pastix_complex64_t)tmpflt1;
          (*values)[i+1] = (pastix_complex64_t)tmpflt2;
#endif
          break;
        case 1:
#ifdef TYPE_COMPLEX
          if (2 != fscanf(infile, "%lg %lg",
                          &tmpflt1, &tmpflt2)){
            errorPrint("CSCD badly formated");
            return EXIT_FAILURE;
          }
          (*values)[i  ] = (pastix_complex64_t)(tmpflt1 + I * tmpflt2);
#else
          if (1 != fscanf(infile, "%lg",
                          &tmpflt1)){
            errorPrint("CSCD badly formated");
            return EXIT_FAILURE;
          }
          (*values)[i  ] = (pastix_complex64_t)tmpflt1;
#endif
          break;
        }
    }
  return PASTIX_SUCCESS;
}