#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( io_t *pio );
  void output_result(int lfil, io_t *pio, int iparam );
  int readhb_c(int *NN, double **AA, int **JA, int **IA, io_t *pio, 
	     double **rhs, double **guess, int *rsa);
  int read_inputs( char *in_file, io_t *pio );
  int get_matrix_info( FILE *fmat, io_t *pio );
  void randvec (double *v, int n);
/*-------------------- end protos */

int main(){
  int ierr = 0;
/*-------------------------------------------------------------------
 * options
 *-----------------------------------------------------------------*/
  int plotting = 0, skip_its = 0;
  char pltfile[256];
  FILE *fits = NULL;
  csptr csmat = NULL;
  SMatptr MAT;
  SPreptr PRE; 
  double *AA;
  int *JA, *IA;  
  iluptr lu = NULL;
  double *sol = NULL, *x = NULL, *rhs = NULL;
  int n, lfil, rsa;
  
  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 Harwell Boeing file
 *
 * 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;
  }
/*-----------------------------------------------------------------*/
  if( NULL == ( fmat = fopen( "matfile_hb", "r" ) ) ) {
    fprintf( flog, "Can't open matfile_hb...\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/ILUK.out for all performance
                       results of this run (all matrices and params) 
                       also set io->PrecMeth */
/* sprintf( io.outfile, "OUT/%s_ILUK.out", io.HBnameF );*/
    strcpy(io.outfile,"OUT/ILUK.out");
    strcpy(io.PrecMeth,"ILUK");
    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_hb...\n" );
      goto ERROR_HANDLE;
    }
    fprintf( flog, "MATRIX: %s...\n", io.HBnameF );
/*-------------------- Read in matrix and allocate memory-------*/
    ierr = readhb_c(&n, &AA, &JA, &IA, &io, &rhs, &sol, &rsa );
    if( ierr != 0 ) {
      fprintf( flog, "readhb_c error = %d\n", ierr );
      goto ERROR_HANDLE;
    }
/*-------------------- convert to C-style CSR matrix */
    csmat = (csptr)Malloc( sizeof(SparMat), "main" );
    if( ( ierr = CSRcs( n, AA, JA, IA, csmat, rsa ) ) != 0 ) {
      fprintf( stderr, "readhb: oidCSRcs error\n" );
      return ierr;
    }
    free( AA);    AA = NULL;
    free( JA );   JA = NULL;
    free( IA );   IA = NULL; 
/*----------------------------------------------------------------------
|  The right-hand side is generated by assuming the solution is
|  a vector of ones.  To be changed if rhs0 is available from data.
|---------------------------------------------------------------------*/
    x   = (double *)Malloc( io.ndim * sizeof(double), "main" );
    for( i = 0; i < io.ndim; i++ ) 
      x[i] = 1.0;
    matvec(csmat, x, rhs );
    
    output_header( &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 = (iluptr)Malloc( sizeof(ILUSpar), "main" );
      fprintf( flog, "begin iluk\n" );
      tm1 = sys_timer();
/*-------------------- call ILUK preconditioner set-up  */
      ierr = ilukC(lfil, csmat, lu, flog );
      tm2 = sys_timer();
      if( ierr == -2 ) {
	fprintf( io.fout, "zero diagonal element found...\n" );
	cleanILU( lu );
	goto NEXT_MAT;
      } else if( ierr != 0 ) {
	fprintf( flog, "*** iluk error, ierr != 0 ***\n" );
	goto ERROR_HANDLE;
      }
      io.tm_p = tm2 - tm1;
      io.fillfact = (double)nnz_ilu( lu )/(double)(io.nnz + 1);
      fprintf( flog, "iluk ends, fill factor (mem used) = %f\n", io.fillfact );
      
      if( skip_its ) {
	io.its = -1;
	io.tm_i = -1;
	io.enorm = -1;
	io.rnorm = -1;
	goto NEXT_PARA;
      }
      // check condition number estimation 
      if( condestLU( 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++ ) {
	sol[i] = 0.0;               
      }
*/
      randvec (sol,n);  /* initial guess */      
     
/*-------------------- create a file for printing
  'its -- time -- res' info from fgmres */
      if (plotting ) { 
	sprintf( pltfile, "OUT/%s_ILUK_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;
      
      io.its = io.maxits;
      tm1 = sys_timer();
/*-------------------- set up the structs before calling fgmr */
      MAT->n = n;
      MAT->CSR = csmat;
      MAT->matvec = matvecCSR; 
      PRE->ILU = lu;
      PRE->precon = preconILU;
/*-------------------- call fgmr */
      fgmr(MAT, PRE, rhs, sol, 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 */
      terr = 0.0;
      for( i = 0; i < io.ndim; i++ )
	terr += ( sol[i] - 1.0 ) * ( sol[i] - 1.0 );
      io.enorm = sqrt(terr);
      matvec( csmat, sol, x );
      terr = 0.0;
      for( i = 0; i < io.ndim; i++ )
	terr += ( rhs[i] - x[i] ) * ( rhs[i] - x[i] );
      io.rnorm = sqrt(terr);
/*-------------------- Test with next params   */           
NEXT_PARA:
      output_result( lfil, &io, iparam );
      lfil += io.fill_lev_inc;
      cleanILU( lu );
    }
/*-------------------- Test with next matrix   */              
NEXT_MAT:
    cleanCS( csmat );
    free( sol );
    free( x );
    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) ;
}
