/*---------------------------------------------------------------------------*
 * main test driver for ILUT                                                 *
 *---------------------------------------------------------------------------*
 * Na Li, Oct 31, 2001                                                       *
 *                                                                           *
 * 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( io_t *pio );
  void output_result( int lfil, io_t *pio, int iparam );
  int read_inputs( char *in_file, io_t *pio );
  int get_matrix_info( FILE *fmat, io_t *pio );
  int read_coo(double **VAL, int **COL, int **ROW, io_t *pio,
		double **rhs, double **sol); 
  void randvec (double *v, int n);
/*-------------------- end protos                                  */

int main () { 

  int ierr = 0;
/*-------------------------------------------------------------------
 * options
 *-----------------------------------------------------------------*/
  /* -- plotting is for writing stats into OUT/.... in raw form    */
  int plotting = 0, output_lu = 0, diagscal = 0;
  char pltfile[256];
  FILE *fits = NULL;
  
  csptr csmat = NULL;
  SMatptr MAT;
  SPreptr PRE; 
  
  iluptr lu = NULL;
  double *sol = NULL, *x = NULL, *rhs = NULL;

  int lfil;
  double tol; 
  
 /*-------------------- temp Harwell Boeing arrays */
  double *VAL;
  int *ROW, *COL;
  int n, nnz;
 /*-------------------- end*/
  
  FILE *flog = stdout, *fmat = NULL;
  io_t io;
  double tm1=0.0, tm2=0.0;
  
  int mat, numat, iparam, i;
  double terr;
  char line[MAX_LINE];
/*-------------------- allocates structs for preconditoiner and matrix */  
  MAT = (SMatptr)Malloc( sizeof(SMat), "main:MAT" );
  PRE = (SPreptr)Malloc( sizeof(SPre), "main:PRE" );

/*-------------------- read parameters and other inputs */
  memset( &io, 0, sizeof(io) );
  if( read_inputs( "inputs", &io ) != 0 ) {
    fprintf( flog, "Invalid inputs file...\n" );
    goto ERROR_HANDLE;
  }
/*-------------------- matfile_coo  contains  paths to matrices */
  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/ILUT.out for all performance
                       results of this run (all matrices and params) 
                       also set io->PrecMeth */
    /* sprintf( io.outfile, "OUT/%s_ILUT.out", io.HBnameF );*/
    strcpy(io.outfile,"OUT/ILUT.out");
    strcpy(io.PrecMeth,"ILUT");
    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 matrix in COO format*/
    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");

/*-------------------- conversion from COO to CS format */
    if( ( ierr = COOcs( n, nnz, VAL, COL, ROW, csmat ) ) != 0 ) {
      fprintf( stderr, "ILUT: zCOOcs error\n" );
      return ierr;
    }
/*-------------------- COO arrays no longer needed -- free */    
    free(ROW); ROW = NULL;
    free(VAL); VAL = NULL;
    free(COL); COL = 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-ilut: roscal: a zero row...\n" );
	return ierr;
      }
      ierr = coscalC( csmat, diag, nrm );
      if( ierr != 0 ) {
	fprintf( stderr, "main-ilut: 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 rhs 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 );
/*-------------------- set initial lfil and tol */ 
   lfil = io.lfil0;
   tol  = io.tol0;
/*-------------------- 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 ilut\n" );
     tm1 = sys_timer();
/*-------------------- call ILUT preconditioner set-up  */
     ierr = ilut( csmat, lu, lfil, tol, flog );
     tm2 = sys_timer();

     if( ierr != 0 ) {
       fprintf( io.fout, " *** ILUT error - code %d \n", ierr);
       io.its = -1;
       io.tm_i = -1;
       io.enorm = -1;
       io.rnorm = -1;
       goto NEXT_PARA;
     }
     
     if(output_lu )       {
       char matdata[MAX_LINE];
       sprintf( matdata, "OUT/%s.dat", io.HBnameF );
       outputLU( lu, matdata );
     } 
     io.tm_p = tm2 - tm1;
     io.fillfact = (double)nnz_ilu( lu )/(double)(io.nnz + 1);
     fprintf( flog, "ilut ends, fill factor (mem used) = %f\n", io.fillfact );
      
     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++ )  x[i] = 0.0; */

      randvec(x, n);
                
/*-------------------- create a file for printing
                       'its -- time -- res' info from fgmres */
     if(plotting ) { 
       sprintf( pltfile, "OUT/%s_ILUT_F%05d_T%08.6f", io.HBnameF, lfil,tol);
       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->ILU = lu;
      PRE->precon = preconILU;
/*-------------------- 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 res norms */
      terr = 0.0;
      for( i = 0; i < io.ndim; i++ )
	terr += ( x[i] - 1.0 ) * ( x[i] - 1.0 );
      io.enorm = sqrt(terr);
      matvec( csmat, 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);
/*-------------------- Test with next params   */     
   NEXT_PARA:
     output_result( lfil, &io, iparam );
     lfil += io.lfilInc;
     tol  *= io.tolMul;
     cleanILU(lu);
   }
/*-------------------- Test with next matrix   */        
/*  NEXT_MAT:  --- not used! */ 
   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:
  exit( -1 );
}
