/*---------------------------------------------------------------------------*
 * main test driver for VBILU                                                *
 *---------------------------------------------------------------------------*
 * Na Li, Aug 26, 2001  -- Y.Saad 07/05                                      *
 *                                                                           *
 * Report bugs / send comments to: saad@cs.umn.edu, nli@cs.umn.edu           *
 *---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#include <math.h>
#include "../LIB/globheads.h"
#include "../LIB/defs.h" 
#include "../LIB/protos.h"  
#include "../ios.h"
#include <sys/time.h>


/*-------------------- protos */
void output_header_vb( io_t *pio );
void output_result(int lfil, io_t *pio, int iparam );
int read_coo(double **VAL, int **COL, int **ROW, io_t *pio,
	      double **rhs, double **sol);
int read_inputs( char *in_file, io_t *pio );
int get_matrix_info(FILE *fmat, io_t *pio );
void randvec (double *v, int n);
void output_blocks( int nBlock, int *nB, FILE *f );
void output_perm( int n, int *perm, FILE *f );
/*-------------------- end protos */

int main () { 
  int ierr = 0;
/*-------------------------------------------------------------------
 * options
 *-----------------------------------------------------------------*/
  int plotting = 0, output_mat = 0, diagscal = 0;
  char pltfile[256];
  FILE *fits = NULL;
  csptr csmat = NULL;
  vbsptr vbmat = NULL;
  vbiluptr lu = NULL;
  SMatptr MAT; 
  SPreptr PRE; 

  double *sol = NULL, *x = NULL, *rhs0 = NULL, *rhs = NULL;
  int lfil, nBlock, *nB = NULL, *perm = NULL;
/*-------------------- temp HB arrays - fortran style */
  int *ROW,  *COL;
  double *VAL;
  int n, nnz; 
  
  FILE *flog = stdout, *fmat = NULL;
  io_t io;
  double tm1, tm2;
  
  int mat, numat, iparam, i;
  double terr;
  char line[MAX_LINE];

  MAT = (SMatptr)Malloc( sizeof(SMat), "main:MAT" );
  PRE = (SPreptr)Malloc( sizeof(SPre), "main:PRE" );

/*-------------------------------------------------------------------
 * reads matrix from file in coordinate format
 *
 * solves using block level of fill ILU preconditioned fgmres
 *
 *-----------------------------------------------------------------*/
  
  memset( &io, 0, sizeof(io) );
/*-----------------------------------------------------------------*/
  if( read_inputs( "inputs", &io ) != 0 ) {
    fprintf( flog, "Invalid inputs file...\n" );
    goto ERROR_HANDLE;
  }
  /* set any parameters manually */
  /* io.eps  is the angle tolerance for grouping two columns in same
     supernode. This is a cosine and should be <= 1.  */
  io.eps = 0.8;
/*----------------------Read COO mat list-----------------------------------*/
  if( NULL == ( fmat = fopen( "matfile_coo", "r" ) ) ) {
    fprintf( flog, "Can't open matfile_coo...\n" );
    goto ERROR_HANDLE;
  }
  memset( line, 0, MAX_LINE );
  fgets( line, MAX_LINE, fmat );
  if( ( numat = atoi( line ) ) <= 0 ) {
    fprintf( flog, "Invalid count of matrices...\n" );
    goto ERROR_HANDLE;
  }
/*-------------------- open file OUT/VBILUK.out for all performance
                       results of this run (all matrices and params) 
                       also set io->PrecMeth */
    /* sprintf( io.outfile, "OUT/%s_VBILUK.out", io.HBnameF );*/
    strcpy(io.outfile,"OUT/VBILUK.out");
    strcpy(io.PrecMeth,"Variable Block ILUK (VBILUK)");
    if( NULL == ( io.fout = fopen( io.outfile, "w" ) ) ) {
      fprintf(flog,"Can't open output file %s...\n", io.outfile);
      goto ERROR_HANDLE;
   }
/*------------------------------------------------------------*/
/*-------------------- LOOP through MATRICES                  */
/*------------------------------------------------------------*/
  for( mat = 1; mat <= numat; mat++ ) {
    if( get_matrix_info( fmat, &io ) != 0 ) {
      fprintf( flog, "Invalid format in matfile_coo...\n" );
      goto ERROR_HANDLE;
    }
    fprintf( flog, "MATRIX: %s...\n", io.HBnameF );
/* Read in matrix and allocate memory------------------------------*/
    csmat = (csptr)Malloc( sizeof(SparMat), "main" );
    ierr = read_coo(&VAL,&COL, &ROW, &io, &rhs, &sol); 
    n = io.ndim; 
    nnz = io.nnz;
    if( ierr != 0 ) {
      fprintf( flog, "read_coo error = %d\n", ierr );
      goto ERROR_HANDLE;
    }
     else
	    fprintf(stdout,"  matrix read successfully \n"); 
/*-------------------- convert to C-style CSR matrix */
    if( ( ierr = COOcs(  n, nnz, VAL, COL, ROW, csmat ) ) != 0 ) {
      fprintf( stderr, "VBILUK: COOcs error\n" );
      return ierr;
    }
/* Free memory */    
    free( VAL);    VAL = NULL;
    free( COL );   COL = NULL;
    free( ROW );   ROW = NULL; 
/*-------------------- DIAGONAL SCALING -----  */	

    if (diagscal ==1) {  
      int nrm=1;
      double *diag;
      diag = (double *)Malloc( sizeof(double)*n, "mainILUC:diag" );
      ierr = roscalC( csmat, diag, nrm);
      if( ierr != 0 ) {
	fprintf( stderr, "main-vbiluk: roscal: a zero row...\n" );
	return ierr;
      }
      ierr = coscalC( csmat, diag, nrm );
      if( ierr != 0 ) {
	fprintf( stderr, "main-vbiluk: roscal: a zero col...\n" );
	return ierr;
      }
      free(diag);
    }

/*----------------------------------------------------------------------
|  The right-hand side is generated by assuming the solution is
|  a vector of ones.  To be changed if rhs0 is available from data.
|---------------------------------------------------------------------*/
    rhs0 = (double *)Malloc( io.ndim * sizeof(double), "main" );
    rhs = (double *)Malloc( io.ndim * sizeof(double), "main" );
    x   = (double *)Malloc( io.ndim * sizeof(double), "main" );
    for( i = 0; i < io.ndim; i++ ) 
      x[i] = 1.0;
    matvec( csmat, x, rhs0 );
    
    ierr = init_blocks( csmat, &nBlock, &nB, &perm, 
			io.eps, &io.tm_h, &io.tm_a );
    io.tm_b = io.tm_h + io.tm_a;
    if( ierr != 0 ) {
      fprintf( flog, "*** in init_blocks ierr != 0 ***\n" );
      goto ERROR_HANDLE;
    }
/*-------------------- permutes the rows and columns of the matrix */
    if( dpermC( csmat, perm ) != 0 ) {
      fprintf( flog, "*** dpermC error ***\n" );
      goto ERROR_HANDLE;
    }
/*-------------------- permutes right hand side  */
    for( i = 0; i < io.ndim; i++ ) 
      rhs[perm[i]] = rhs0[i]; 
/*-------------------- convert to block matrix. */
    vbmat = (vbsptr)Malloc( sizeof(VBSparMat), "main" );
    ierr = csrvbsrC( 1, nBlock, nB, csmat, vbmat );
    if( ierr != 0 ) {
      fprintf( flog, "*** in csrvbsr ierr != 0 ***\n" );
      goto ERROR_HANDLE;
    }
    if( output_mat ) {
      char matdata[MAX_LINE];
      FILE *fmatlab;
      int ii, jj;
      sprintf( matdata, "OUT/%s.dat", io.HBnameF );
      if( NULL != ( fmatlab = fopen( matdata, "w" ) ) ) {
	fprintf( fmatlab, "%d %d 0\n", csmat->n, csmat->n );
	for( ii = 0; ii < csmat->n; ii++ )
	  for( jj = 0; jj < csmat->nzcount[ii]; jj++ )
	    fprintf( fmatlab, "%d %d 1\n", ii+1, csmat->ja[ii][jj]+1 );
	fclose( fmatlab );
      }
    }
    io.rt_v = (double)csmat->n / (double)vbmat->n;
    io.rt_e = (double)nnzCS( csmat ) / (double)nnzVBMat( vbmat );
    io.ceff = (double)nnzCS( csmat ) / (double)memVBMat( vbmat ) * 100;

    output_header_vb( &io );
    lfil = io.fill_lev;
    /*  make sure this is set to zero*/
    io.tol0 = 0.0;    
/*-------------------- LOOP through parameters */
    for( iparam = 1; iparam <= io.nparam; iparam++ ) {
      fprintf( flog, "iparam = %d\n", iparam );
      lu = (vbiluptr)Malloc( sizeof(VBILUSpar), "main" );
      fprintf( flog, "begin vbiluk\n" );
      tm1 = sys_timer();
/*-------------------- call VBILUK preconditioner set-up  */
      ierr = vbilukC( lfil, vbmat, lu, flog );
      tm2 = sys_timer();
      if( ierr == -2 ) {
	fprintf( io.fout,
		 "Singular diagonal block...\n" );
	cleanVBILU( lu );
	goto NEXT_MAT;
      } else if( ierr != 0 ) {
	fprintf( flog, "*** vbilu error, ierr != 0 ***\n" );
	goto ERROR_HANDLE;
      }
      io.tm_p = tm2 - tm1;
      io.fillfact = (double)nnz_vbilu( lu )/(double)(io.nnz + 1);
      fprintf( flog, "vbiluk ends, fill factor (mem used) = %f\n", io.fillfact );
      
      if( VBcondestC( lu, sol, x, flog ) != 0 ) {
	fprintf( flog, "Not attempting iterative solution.\n" );
	fprintf( io.fout, "Not attempting iterative solution.\n" );
	io.its = -1;
	io.tm_i = -1;
	io.enorm = -1;
	io.rnorm = -1;
	goto NEXT_PARA;
      }
/*-------------------- initial guess */      
/*      for( i = 0; i < io.ndim; i++ ) x[i] = 0.0; */

       randvec(x, n);
/*-------------------- create a file for printing
  'its -- time -- res' info from fgmres */
      if (plotting ) { 
	sprintf( pltfile, "OUT/%s_VBILUK_F%05d", io.HBnameF, lfil);
	if( NULL == ( fits = fopen( pltfile, "w" ) ) ) {
	  fprintf( flog, "Can't open output file %s...\n", pltfile );
	  goto ERROR_HANDLE;
	}
      } else 
	fits  =NULL;
/*-------------------- set up the structs before calling fgmr */
      MAT->n = n;
      MAT->CSR = csmat;
      MAT->matvec = matvecCSR; 
      PRE->VBILU = lu; 
      PRE->precon = preconVBR;
/*-------------------- call fgmr */
      io.its = io.maxits;
      tm1 = sys_timer();
      fgmr(MAT, PRE, rhs, x, io.tol, io.im, &io.its, fits);
      tm2 = sys_timer();
      io.tm_i = tm2 - tm1;
      if( io.its < io.maxits ) 
 			fprintf( flog, "param %03d OK: converged in %d steps...\n\n", iparam, io.its );
      else 
 			fprintf( flog, "not converged in %d steps...\n\n", io.maxits );
       
      if( fits ) fclose( fits );
      
/*-------------------- calculate error and residual norms */
      vbmatvec( vbmat, x, sol );
      terr = 0.0;
      for( i = 0; i < io.ndim; i++ )
	terr += ( rhs[i] - sol[i] ) * ( rhs[i] - sol[i] );
      io.rnorm = sqrt(terr);
      
      /* get sol vector of original matrix (before permutation) */
      for( i = 0; i < io.ndim; i++ )
	sol[perm[i]] = x[i];
      /* calculate error norm */
      terr = 0.0;
      for( i = 0; i < io.ndim; i++ )
	terr += ( sol[i] - 1.0 ) * ( sol[i] - 1.0 );
      io.enorm = sqrt(terr);
/*-------------------- next params */      
    NEXT_PARA:
      output_result( lfil, &io, iparam );
      lfil += io.fill_lev_inc;
      cleanVBILU( lu );
    }
/*-------------------- next matrix */          
  NEXT_MAT:
    /*  output_blocks( nBlock, nB, io.fout );*/
    cleanCS( csmat );
    cleanVBMat( vbmat );
    free( nB );
    free( perm );
    free( sol );
    free( x );
    free( rhs0 );
    free( rhs );
  }

  fclose( io.fout );  
  if( flog != stdout ) fclose ( flog );
  fclose( fmat );
  free(MAT);
  free(PRE);
  return 0;

ERROR_HANDLE:
  errexit( "main.c\n" );
  return 1;
}

