#include <stdio.h>
#include <stdlib.h>
#include "cbem_cell_util.h"
#include "bem_p_itslv.h"


void cbem_lap_solv(void)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/********************************  COLLOCATION BEM for 3D LAPLACE EQUATION: SINGULAR FORMULATION  *********************************/
/***************************************  3-NODED TRIANGULAR ELEMENT: ANALYTIC INTEGRATION  ***************************************/
/***********************************************  PIECEWISE CONSTANT APPROXIMATION  ***********************************************/
/***************************************************  Solve the Discretized BIE  **************************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.3                 */
/* Released:  Jun 14, 2012          */

 
   extern int nbelt,*eltbc; /* nbelt == Total number of boundary elements */
                            /* eltbc[k] = {0,1} ==> {Dirichlet,Neunamm} boundary condition is prescribed on element k */
   extern double *g,*rhs;   /* g == Discretized BIE matrix or Left-hand side of the BEM equation*/
                            /* rhs == Right-hand side of the Discretized BIE */
   extern double *phi,*flx; /* phi == Vector containing boundary potentials */
                            /* flx == Vector containing boundary fluxes */
   extern int ldim;       /* ldim == Dimension of the iterative solver BiCGSTAB(ldim) */
   extern int mxmatvec;   /* mxmatvec == Maximum number of matrix-vector mutiplications in the iterative solver BiCGSTAB(ldim) */
   extern double slv_eps; /* slv_eps == Tolerance on the BiCGSTAB(ldim) iterative solver */
   extern int sparseflg; /* sparseflg == Parameter that determines the sparsity pattern of the preconditoner */
			 /* sparseflg = 0 ==> no preconditoner                                                                        */
			 /* sparseflg = 1 ==> the sparsity pattern of the preconditoner is given by Cell self-interactions            */
			 /* sparseflg = 1+n ==> the sparsity pattern of the preconditoner is given by Cell + n Neighbors interactions */
   extern int ncwelt;    /* ncwelt == Number of computional Cells containing boundary elements <==> number of non-empty cells */
   extern int *cwelt;    /* cwelt[k] == Global index of the k-th non-empty cell */
   extern cellinfo_t *cell; /* cell[k] == Structure containing information about the k-th cell */ 
   extern double *gcsr;      /* gcsr == Preconditioner <==> Sparse approximation of g in Compressed Sparse Row (CSR) format */
   extern int *igcsr,*jgcsr; /* igcsr == Pointers in gcsr of the beginning of each row of g */
                             /* jgcsr == Column indices of the entries of gcsr as stored in the Discretized BIE matrix g */

   bool pcond_flg; /* pcond_flg = true ==> there is a preconditioner; pcond_flg = false ==> there is no preconditioner */
   int i,ii,k,kk,l,j,jj,nk,nj,info,cl_stride;
   int nmatvec; /* nmatvec == Number of matrix-vector mutiplications performed until convergence in BiCGSTAB(ldim) */
   int nnz_gcsr; /* nnz_gcsr == Number of non-zero entries in the Sparse approximation of the Discretized BIE matrix g */
   double tol; /* tol == Residual achieved at the solution */
   double *solvec; /* solvec == Solution vector */



    if ( (solvec = (double *)calloc(nbelt,sizeof(double))) == NULL )  /* If solvec is not successfully allocated */
    {printf("cbem_lap_solv: Allocate array \"solvec\" failed\n"); exit(1);}

    nmatvec = mxmatvec; tol = slv_eps; 

    if ( sparseflg == 0 ) /*  sparseflg=0 ==> no sparse approximation of the Discretized BIE matrix g <==> no preconditioner */
          pcond_flg = false; /* pcond_flg = false <==> no preconditioner */
    else if ( sparseflg > 0 ) /* sparseflg > 0 <==> use a preconditioner */
    {
          pcond_flg = true; /* pcond_flg = true <==> use a preconditioner */
	  printf("           >>>  Preconditioning <<<\n");
	  printf("           >>>  Cell Initialization <<<\n");
          cbem_cell_init(); /* Construct the computational box and distribute without repetition boundary elements into cells, */
	                    /* i.e, Initialize non-empty cells in arrays cell and cwelt, define the parameter ncwelt           */
	  if ( sparseflg == 1 ) /* sparseflg=1 ==> the sparsity pattern of the preconditoner is given by cell self-interactions */
	  {
		for (nnz_gcsr=0, k=0; k<ncwelt; k++) /* Loop over all non-empty cells */
		{
		   nk = cell[cwelt[k]].nec; /* nk = Number of boundary elements in the k-th cell */
		   nnz_gcsr += nk*nk;
		}
	  
/* gcsr == Sparse approximation of the Discretized BIE matrix g */
		if ( (igcsr = (int *)malloc(nnz_gcsr*sizeof(int))) == NULL ) /* If igcsr is not successfully allocated */
		{printf("cbem_lap_solv: C1: Allocate array \"igcsr\" failed\n"); exit(1);}
		
		if ( (jgcsr = (int *)malloc(nnz_gcsr*sizeof(int))) == NULL ) /* If jgcsr is not successfully allocated */
		{printf("cbem_lap_solv: C1: Allocate array \"jgcsr\" failed\n"); exit(1);}

		if ( (gcsr = (double *)malloc(nnz_gcsr*sizeof(double))) == NULL ) /* If gcsr is not successfully allocated */
		{printf("cbem_lap_solv: C1: Allocate array \"gcsr\" failed\n"); exit(1);}

		      for (ii=0, k=0; k<ncwelt; k++) /* Loop over all non-empty cells */
		      {
			 kk = cwelt[k]; /* kk == Global index of the k-th cell */
			 nk = cell[kk].nec; /* nk == Number of boundary elements (or Representative nodes) in the k-th cell */
			 for (l=0; l<nk; l++) /* Loop over all representative nodes in the k-th cell */
			 {
			    cl_stride = nbelt*cell[kk].elt_id[l];
			    for (i=0; i<nk; i++) /* Loop over all representative nodes in the k-th cell */
			    {
				gcsr[ii] = g[cell[kk].elt_id[i] + cl_stride]; /* Here gcsr is stored in COOrdinate (COO) format */
			       igcsr[ii] = cell[kk].elt_id[i]; /* igcsr(ii) =    Row index in g of the ii entry of gcsr */
			       jgcsr[ii] = cell[kk].elt_id[l]; /* jgcsr(ii) = Column index in g of the ii entry of gcsr */
			       ii++;
			    }
			 }
 		      }
	  }
	  else /* sparseflg > 1 */
	  {
	        cbem_cell_neighbor(); /* For all non-empty cell, find near-neighbors */
                nnz_gcsr = 0;
	        for (k=0; k<ncwelt; k++) /* Loop over all non-empty cells */
		{
	           nk = cell[cwelt[k]].nec; /* nk == Number of boundary elements (or Representative nodes) in the k-th cell */
		   if ( cell[cwelt[k]].nnnc >= sparseflg ) 
		      cell[cwelt[k]].nnbsprs = sparseflg;
		   else /* cell[cwelt[k]].nnnc == Number of Near-neighboring cells to the k-th cell including the k-th cell itself */
		      cell[cwelt[k]].nnbsprs = cell[cwelt[k]].nnnc; 
		      /* cell[cwelt[k]].nnbsprs == Number of Near-Neighboring cells to the k-th cell used to define gcsr */
		   
		   for (j=0; j<cell[cwelt[k]].nnbsprs; j++) /* Loop over the near-neighboring cells to the k-th cell */
		   { /* cell[cwelt[k]].nbor[j] == Global index of the j-th near-neighboring cell to the k-th cell */
 		       nj = cell[cell[cwelt[k]].nbor[j]].nec; /* nj == Number of boundary elements (or Representative nodes) */
		                                              /*       in the j-th cell                                      */
	               nnz_gcsr += nk*nj; /* nnz_gcsr == Number of non-zero entries in gcsr */
 		   }
                }

/* gcsr == Sparse approximation of the Discretized BIE matrix g */
		if ( (igcsr = (int *)malloc(nnz_gcsr*sizeof(int))) == NULL ) /* If igcsr is not successfully allocated */
		{printf("cbem_lap_solv: C2: Allocate array \"igcsr\" failed\n"); exit(1);}
		
		if ( (jgcsr = (int *)malloc(nnz_gcsr*sizeof(int))) == NULL ) /* If jgcsr is not successfully allocated */
		{printf("cbem_lap_solv: C2: Allocate array \"jgcsr\" failed\n"); exit(1);}

		if ( (gcsr = (double *)malloc(nnz_gcsr*sizeof(double))) == NULL ) /* If gcsr is not successfully allocated */
		{printf("cbem_lap_solv: C2: Allocate array \"gcsr\" failed\n"); exit(1);}

		      for (ii=0, k=0; k<ncwelt; k++) /* Loop over all non-empty cells */
		      {
			 kk = cwelt[k]; /* kk == Global index of the k-th cell */
			 nk = cell[kk].nec; /* nk = Number of boundary elements (or Representative nodes) in the k-th cell */
			 for (j=0; j<cell[kk].nnbsprs; j++) /* Loop over the near-neighboring cells to the k-th cell */
			 {
                            jj = cell[kk].nbor[j]; /* jj == Global index of the j-th near-neighboring cell to the k-th cell */
 		            nj = cell[jj].nec; /* nj == Number of boundary elements (or Representative nodes) in the j-th cell */

			    for (l=0; l<nj; l++) /* Loop over all representative nodes in the j-th cell */
			    {
			       cl_stride = nbelt*cell[jj].elt_id[l];
			       for (i=0; i<nk; i++) /* Loop over all representative nodes in the k-th cell */
			       {
				  gcsr[ii] = g[cell[kk].elt_id[i] + cl_stride]; /* Here gcsr is stored in COOrdinate (COO) format */
			          igcsr[ii] = cell[kk].elt_id[i];  /* igcsr(ii) =    Row index in g of the ii entry of gcsr */
			          jgcsr[ii] = cell[jj].elt_id[l];  /* jgcsr(ii) = Column index in g of the ii entry of gcsr */
			          ii++;
			       }
			    }   
			 }
 		      }

	  } /* sparseflg > 1 */

	coo2csr(nbelt,nnz_gcsr,gcsr,igcsr,jgcsr); /* Convert gcsr from COO to Compressed Sparse Row (CSR) format */
 	printf("           >>>  Zero Fill-in Incomplete LU: ILU(0) <<<\n");
	csr_ilu0(nbelt,gcsr,igcsr,jgcsr); /* Generate the left preconditioner: Zero Fill-in Incomplete LU Factorization */

    }
    else /* sparseflg < 0 */
    {
       printf("Warning: Incorrect value for sparseflg ==> sparseflg set to 0\n");
       sparseflg = 0; pcond_flg = false; /* pcond_flg = false ==> there is no preconditioner */
    }

    printf("           >>>  BICGSTABL Iterative Solver <<<\n");
    bicgstabl(ldim,nbelt,solvec,false,rhs,mat_vec,ilu0_l_pcond,
              pcond_flg,&tol,&nmatvec,&info); /* Iterative solution of the Discretized BIE */
    free(g); free(rhs);

    if ( pcond_flg ){ free(igcsr); free(jgcsr); free(gcsr); }

    printf("nmatvec == %d\n",nmatvec);
    printf("Residual == %.17E\n",tol);
    printf("err == %d\n",info);

    for (k=0; k<nbelt; k++)
       if ( eltbc[k] == 0 ) /* eltbc[k] = 0 ==> Dirichlet data is prescribed on element k */
          flx[k] = solvec[k];
       else /* eltbc[k] = 1 ==> Neumann data is prescribed on element k */
          phi[k] = solvec[k];  

   free(solvec); free(eltbc);

   if ( sparseflg != 0 )
   {
        for (k=0; k<ncwelt; k++) /* Loop over all non-empty Cells */
	{
           free(cell[cwelt[k]].elt_id);
	   if ( sparseflg > 1) free(cell[cwelt[k]].nbor);
        }
        free(cell); free(cwelt);
   }

}
