/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*             ********   ***                                 SparseLib++    */
/*          *******  **  ***       ***      ***               v. 1.5c        */
/*           *****      ***     ******** ********                            */
/*            *****    ***     ******** ********              R. Pozo        */
/*       **  *******  ***   **   ***      ***                 K. Remington   */
/*        ********   ********                                 A. Lumsdaine   */
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*                                                                           */
/*                                                                           */
/*                     SparseLib++ : Sparse Matrix Library                   */
/*                                                                           */
/*               National Institute of Standards and Technology              */
/*                        University of Notre Dame                           */
/*              Authors: R. Pozo, K. Remington, A. Lumsdaine                 */
/*                                                                           */
/*                                 NOTICE                                    */
/*                                                                           */
/* Permission to use, copy, modify, and distribute this software and         */
/* its documentation for any purpose and without fee is hereby granted       */
/* provided that the above notice appear in all copies and supporting        */
/* documentation.                                                            */
/*                                                                           */
/* Neither the Institutions (National Institute of Standards and Technology, */
/* University of Notre Dame) nor the Authors make any representations about  */
/* the suitability of this software for any purpose.  This software is       */
/* provided ``as is'' without expressed or implied warranty.                 */
/*                                                                           */
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
// modification GR :
// introduction du cas de la matrice abstraite
// utilisation de deux fonctions d'initialisation internes

#include <stdlib.h>
#include <math.h>
#include "icpre_double_GR.h" // modif GR : découle d'une classe virtuelle
// pour avoir une interface standard

#include "qsort_double.h"
#include "qsort_int.h"
#include "spblas.h"
#include "Sortie.h"
#include "Mat_creuse_CompCol.h"


static void
ICFactor(const VECTOR_int &ia, const VECTOR_int &ja, VECTOR_double &sa);
static void
ICSolve(const VECTOR_int &ia, const VECTOR_int &ja, 
    const VECTOR_double &sa, VECTOR_double &dest);


ICPreconditioner_double::ICPreconditioner_double(const CompCol_Mat_double &A)
  : val_(0), pntr_(A.dim(1)+1), indx_(0), nz_(0)
{ Init_CompCol_Mat_double( A);
  }


ICPreconditioner_double::ICPreconditioner_double(const CompRow_Mat_double &A)
  : val_(0), pntr_(A.dim(0)+1), indx_(0), nz_(0)
{Init_CompRow_Mat_double( A);
}

// cas d'une matrice abstraite qui est en faite de type sparse_col_comp
 ICPreconditioner_double::ICPreconditioner_double(const Mat_abstraite &A)
  : val_(0), pntr_(0), indx_(0), nz_(0)
{
   switch (A.Type_matrice())
	 { case CREUSE_COMPRESSEE_COLONNE :
	    {pntr_.newsize(A.Nb_colonne()+1);
	     Init_Mat_creuse_CompCol(A);
		 break;
		 }
	   case CREUSE_COMPRESSEE_LIGNE :
	     // ici il y aura une modif a faire comme pour les matrices compressées colonne
	     // il faut passer la matrice dérivée et non la matrice mère
	    {cout << "\n problème dans DiagPreconditioner_double(.. "
	          << "voir le source .cpp, il y a du travail a faire";
	     Sortie(1);     
	     pntr_.newsize(A.Nb_ligne()+1);
		 Init_CompRow_Mat_double((CompRow_Mat_double&) A);
		 break;
		 }
	   default :
			cout << "\nErreur : valeur incorrecte du type de matrice !"
			     << " = " << A.Type_matrice() << " , ce tye n'est pas encore "
			     << " pris en compte dans le préconditionnement de cholesky incomplet\n";
			cout << "\n ICPreconditioner_double::ICPreconditioner_double(... )\n";
			Sortie(1);
	  };
  }


VECTOR_double
ICPreconditioner_double::solve(const VECTOR_double &x) const
{
  VECTOR_double y(x);

  ICSolve(pntr_, indx_, val_, y);

  return y;
}


VECTOR_double
ICPreconditioner_double::trans_solve(const VECTOR_double &x) const
{
  VECTOR_double y(x);

  ICSolve(pntr_, indx_, val_, y);

  return y;
}


static void
ICFactor(const VECTOR_int &pntr, const VECTOR_int &indx, 
     VECTOR_double &val)
{
  int d, g, h, i, j, k, n = pntr.size() - 1;
  double z;

  for (k = 0; k < n - 1; k++) {
    d = pntr[k];
    z = val[d] = sqrt(val[d]);

    for (i = d + 1; i < pntr[k+1]; i++)
      val[i] /= z;

    for (i = d + 1; i < pntr[k+1]; i++) {
      z = val[i];
      h = indx[i];
      g = i;

      for (j = pntr[h] ; j < pntr[h+1]; j++)
    for ( ; g < pntr[k+1] && indx[g+1] <= indx[j]; g++)
      if (indx[g] == indx[j])
        val[j] -= z * val[g];
    }
  }
  d = pntr[n-1];
  val[d] = sqrt(val[d]);
}


static void
ICSolve(const VECTOR_int &pntr, const VECTOR_int &indx, 
    const VECTOR_double &val, VECTOR_double &dest)
{
  int M = dest.size();
  VECTOR_double work(M);

  int descra[9];

  descra[0] = 0;

  // lower diag
  descra[1] = 1;
  descra[2] = 0;

  F77NAME(dcscsm) (0, M, 1, 1, NULL, 1.0,
           descra, &val(0), &indx(0), &pntr(0),
           &dest(0), M, 0.0, &dest(1), M,
           &work(0), M);

  // lower diag transpose
  F77NAME(dcscsm) (1, M, 1, 1, NULL, 1.0,
           descra, &val(0), &indx(0), &pntr(0),
           &dest(0), M, 0.0, &dest(1), M,
           &work(0), M);
}


  
//====================== protégée ========================

  // fonction interne d'initialisation pour éviter la recopie
void ICPreconditioner_double::Init_CompCol_Mat_double(const CompCol_Mat_double &A)
{
  dim_[0] = A.dim(0);
  dim_[1] = A.dim(1);

  int i, j, k;

  for (k = 0; k < dim_[1]; k++)
    for (j = A.col_ptr(k); j < A.col_ptr(k+1); j++)
      if (A.row_ind(j) >= k)
    nz_++;

  val_.newsize(nz_);
  indx_.newsize(nz_);

  // Copy just triangular part
  pntr_(0) = 0;
  for (k = 0; k < dim_[1]; k++) {
    pntr_(k+1) = pntr_(k);
    for (j = A.col_ptr(k); j < A.col_ptr(k+1); j++) {
      if (A.row_ind(j) >= k) {
    i = pntr_(k+1)++;
    val_(i) = A.val(j);
    indx_(i) = A.row_ind(j);
      }
    }
  }

  for (i = 0; i < dim_[1]; i++)
    QSort(indx_, val_, pntr_[i], pntr_[i+1] - pntr_[i]);

  for (i = 0; i < dim_[1]; i++)
    if (indx_[pntr_(i)] != i) {
      cerr << "IC Preconditioner: diagonal not found!" << i << endl;
      Sortie(1);
    }
  
  ICFactor(pntr_, indx_, val_);
}; 

void ICPreconditioner_double::Init_Mat_creuse_CompCol(const Mat_abstraite &B)
{ Mat_creuse_CompCol & A = (Mat_creuse_CompCol &) B;
  dim_[0] = A.dim(0);
  dim_[1] = A.dim(1);

  int i, j, k;

  for (k = 0; k < dim_[1]; k++)
    for (j = A.col_ptr(k); j < A.col_ptr(k+1); j++)
      if (A.row_ind(j) >= k)
    nz_++;

  val_.newsize(nz_);
  indx_.newsize(nz_);

  // Copy just triangular part
  pntr_(0) = 0;
  for (k = 0; k < dim_[1]; k++) {
    pntr_(k+1) = pntr_(k);
    for (j = A.col_ptr(k); j < A.col_ptr(k+1); j++) {
      if (A.row_ind(j) >= k) {
    i = pntr_(k+1)++;
    val_(i) = A.val(j);
    indx_(i) = A.row_ind(j);
      }
    }
  }

  for (i = 0; i < dim_[1]; i++)
    QSort(indx_, val_, pntr_[i], pntr_[i+1] - pntr_[i]);

  for (i = 0; i < dim_[1]; i++)
    if (indx_[pntr_(i)] != i) {
      cerr << "IC Preconditioner: diagonal not found!" << i << endl;
      Sortie(1);
    }
  
  ICFactor(pntr_, indx_, val_);
}; 

void ICPreconditioner_double::Init_CompRow_Mat_double(const CompRow_Mat_double &A)
{
  dim_[0] = A.dim(0);
  dim_[1] = A.dim(1);

  int i, j, k;

  for (k = 0; k < dim_[0]; k++)
    for (j = A.row_ptr(k); j < A.row_ptr(k+1); j++)
      if (A.col_ind(j) >= k)
    nz_++;

  val_.newsize(nz_);
  indx_.newsize(nz_);

  // Copy just triangular part (including diagonal)
  pntr_(0) = 0;
  for (k = 0; k < dim_[0]; k++) {
    pntr_(k+1) = pntr_(k);
    for (j = A.row_ptr(k); j < A.row_ptr(k+1); j++) {
      if (A.col_ind(j) >= k) {
    i = pntr_(k+1)++;
    val_(i) = A.val(j);
    indx_(i) = A.col_ind(j);
      }
    }
  }

  for (i = 0; i < dim_[0]; i++)
    QSort(indx_, val_, pntr_[i], pntr_[i+1] - pntr_[i]);

  for (i = 0; i < dim_[0]; i++)
    if (indx_[pntr_(i)] != i) {
      cerr << "IC Preconditioner: diagonal not found!" << i << endl;
      Sortie(1);
    }
  
  ICFactor(pntr_, indx_, val_);
};
