#include "Amesos_ConfigDefs.h"
#ifdef HAVE_MPI
#include "mpi.h"
#include "Epetra_MpiComm.h"
#else
#include "Epetra_SerialComm.h"
#endif
#include "Epetra_Vector.h"
#include "Epetra_Time.h"
#include "Epetra_RowMatrix.h"
#include "Epetra_CrsMatrix.h"
#include "Amesos.h"
#include "Galeri_Maps.h"
#include "Galeri_CrsMatrices.h"
using namespace Teuchos;
using namespace Galeri;
#include "Trilinos_Util.h"
#include "Trilinos_Util_ReadMatrixMarket2Epetra.h"
#include "CrsMatrixTranspose.h"
#include "Epetra_Export.h"
int MyCreateCrsMatrix( 
const char *in_filename, 
const Epetra_Comm &Comm,
 
                     const bool transpose, const bool distribute,
  
  
  
  
  FILE *in_file = fopen( in_filename, "r");
  const char *filename;
  if (in_file == NULL )
    filename = &in_filename[1] ; 
                                 
  else {
    filename = in_filename ;
    fclose( in_file );
  }
  symmetric = false ;
  std::string FileName (filename);
  int FN_Size = FileName.size() ;
  std::string LastFiveBytes = FileName.substr( EPETRA_MAX(0,FN_Size-5), FN_Size );
  std::string LastFourBytes = FileName.substr( EPETRA_MAX(0,FN_Size-4), FN_Size );
  if ( LastFiveBytes == ".TimD" ) {
    
    EPETRA_CHK_ERR( Trilinos_Util_ReadTriples2Epetra( filename, false, Comm, readMap, readA, readx,
                                                      readb, readxexact, false, true, true ) );
    symmetric = false;
  } else {
    if ( LastFiveBytes == ".triU" ) {
      
      EPETRA_CHK_ERR( Trilinos_Util_ReadTriples2Epetra( filename, false, Comm, readMap, readA, readx,
                                                        readb, readxexact) );
      symmetric = false;
    } else {
      if ( LastFiveBytes == ".triS" ) {
        
        EPETRA_CHK_ERR( Trilinos_Util_ReadTriples2Epetra( filename, true, Comm, readMap, readA, readx,
                                                          readb, readxexact) );
        symmetric = true;
      } else {
        if (  LastFourBytes == ".mtx" ) {
          EPETRA_CHK_ERR( Trilinos_Util_ReadMatrixMarket2Epetra( filename, Comm, readMap,
                                                                 readA, readx, readb, readxexact) );
          FILE* in_file = fopen( filename, "r");
          assert (in_file != NULL) ;  
          const int BUFSIZE = 800 ;
          char buffer[BUFSIZE] ;
          fgets( buffer, BUFSIZE, in_file ) ;  
          std::string headerline1 = buffer;
#ifdef TFLOP
          if ( headerline1.find("symmetric") < BUFSIZE ) symmetric = true;
#else
          if ( headerline1.find("symmetric") != std::string::npos) symmetric = true;
#endif
          fclose(in_file);
        } else {
          
          Trilinos_Util_ReadHb2Epetra( filename, Comm, readMap, readA, readx,
                                       readb, readxexact) ;
          if (  LastFourBytes == ".rsa" ) symmetric = true ;
        }
      }
    }
  }
  if ( readb )  delete readb;
  if ( readx ) delete readx;
  if ( readxexact ) delete readxexact;
  if ( transpose ) {
    assert( CrsMatrixTranspose( readA, transposeA ) == 0 );
    serialA = transposeA ;
    delete readA;
    readA = 0 ;
  } else {
    serialA = readA ;
  }
  assert( (
void *) &serialA->
Graph() ) ;
  assert( (
void *) &serialA->
RowMap() ) ;
  if ( distribute ) {
    
    
    Amat->Export(*serialA, exporter, Add);
    Matrix = Amat;
    
    
    
    
    
    
    
    delete readMap;
    readMap = 0 ;
    delete serialA;
  } else {
    Matrix = serialA;
  }
  return 0;
}
int main(int argc, char *argv[])
{
#ifdef HAVE_MPI
  MPI_Init(&argc, &argv);
#else
#endif
  bool verbose = (Comm.
MyPID() == 0);
 
  double TotalResidual = 0.0;
  
  
  
#ifndef FILENAME_SPECIFIED_ON_COMMAND_LINE
  Epetra_Map* Map = CreateMap(
"Cartesian3D", Comm, GaleriList);
 
  
  
  
  
  
#else
  bool transpose = false ;
  bool distribute = false ;
  bool symmetric ;
  MyCreateCrsMatrix( argv[1], Comm, Map, transpose, distribute, symmetric, Matrix ) ;
#endif
  
  
  
  
  
  
  List.
set(
"PrintTiming",
true);
  List.
set(
"PrintStatus",
true);
  std::vector<std::string> SolverType;
  SolverType.push_back("Amesos_Paraklete");
  SolverType.push_back("Amesos_Klu");
#if 1
  SolverType.push_back("Amesos_Lapack");
  SolverType.push_back("Amesos_Umfpack");
  SolverType.push_back("Amesos_Pardiso");
  SolverType.push_back("Amesos_Taucs");
  SolverType.push_back("Amesos_Superlu");
  SolverType.push_back("Amesos_Superludist");
  SolverType.push_back("Amesos_Mumps");
  SolverType.push_back("Amesos_Dscpack");
  SolverType.push_back("Amesos_Scalapack");
#endif
  
  
  
  
  for (unsigned int i = 0 ; i < SolverType.size() ; ++i)
  {
    
    if (Factory.
Query(SolverType[i]))
 
    {
      
      LHS.PutScalar(1.0);
      
      
      LHS.Random();
      
      assert (Solver != 0);
      
      if (verbose)
        std::cout << std::endl
             << "Solver " << SolverType[i]
             << ", verbose = " << verbose << std::endl ;
      if (verbose)
        std::cout << std::endl
             << "Solver " << SolverType[i]
             << ", symbolic factorization time = "
             << 
Time.ElapsedTime() << std::endl;
      if (verbose)
        std::cout << "Solver " << SolverType[i]
             << ", numeric factorization time = "
             << 
Time.ElapsedTime() << std::endl;
      AMESOS_CHK_ERR(Solver->
Solve());
      if (verbose)
        std::cout << "Solver " << SolverType[i]
             << ", solve time = "
             << 
Time.ElapsedTime() << std::endl;
      
      
      
      double d = 0.0, d_tot = 0.0;
      for (int j = 0 ; j< LHS.Map().NumMyElements() ; ++j)
        d += (LHS[0][j] - 1.0) * (LHS[0][j] - 1.0);
      if (verbose)
        std::cout << "Solver " << SolverType[i] << ", ||x - x_exact||_2 = "
             << sqrt(d_tot) << std::endl;
      
      delete Solver;
      TotalResidual += d_tot;
    }
  }
  delete Matrix;
  delete Map;
  if (TotalResidual > 1e-9)
    exit(EXIT_FAILURE);
#ifdef HAVE_MPI
  MPI_Finalize();
#endif
  return(EXIT_SUCCESS);
}