 			return false;
 		}
 
+		// Process the current argument.
 		switch (args.OptionId()) {
-
-		case OPT_HELP:
-			ShowUsage();
-			return false;
-
-		case OPT_PART:
-			pb.numPart = atoi(args.OptionArg());
-			if (pb.numPart <= 0) {
-				cout << "Invalid value for the number of partitions. P = " << pb.numPart << endl;
+			case OPT_HELP:
+				ShowUsage();
 				return false;
-			}
-			break;
-
-		case OPT_TOL:
-			pb.tol = atof(args.OptionArg());
-			break;
-			
-		case OPT_MAXIT:
-			pb.maxIt = atoi(args.OptionArg());
-			break;
-
-		case OPT_DROPOFF_FRAC:
-			pb.fraction = atof(args.OptionArg());
-			break;
-
-		case OPT_NO_REORDERING:
-			pb.reorder = false;
-			break;
-
-		case OPT_NO_SCALING:
-			pb.scale = false;
-			break;
-
-		case OPT_VERBOSE:
-			pb.verbose = true;
-			break;
-
-		case OPT_MATFILE:
-			pb.fileMat = args.OptionArg();
-			break;
-
-		case OPT_RHSFILE:
-			pb.fileRhs = args.OptionArg();
-			break;
-
-		case OPT_OUTFILE:
-			pb.fileSol = args.OptionArg();
-			break;
-
-		case OPT_FACTORIZATION:
-			{
-				string fact = args.OptionArg();
-				std::transform(fact.begin(), fact.end(), fact.begin(), ::toupper);
-				if (fact == "0" || fact == "LU_UL")
-					pb.factorization = spike::LU_UL;
-				else if (fact == "1" || fact == "LU_LU")
-					pb.factorization = spike::LU_only;
-				else
-					return false;
-			}
-
-			break;
-
-		case OPT_PRECOND:
-			{
-				string precond = args.OptionArg();
-				std::transform(precond.begin(), precond.end(), precond.begin(), ::toupper);
-				if (precond == "0" || precond == "SPIKE")
-					pb.precondMethod = spike::Spike;
-				else if(precond == "1" || precond == "BLOCK")
-					pb.precondMethod = spike::Block;
-				else
-					return false;
-			}
-
-			break;
-
-		case OPT_KRYLOV:
-			{
-				string kry = args.OptionArg();
-				std::transform(kry.begin(), kry.end(), kry.begin(), ::toupper);
-				if (kry == "0" || kry == "BICGSTAB")
-					pb.krylov = spike::BiCGStab;
-				else if (kry == "1" || kry == "BICGSTAB2")
-					pb.krylov = spike::BiCGStab2;
-				else
-					return false;
-			}
-
-			break;
-
-		case OPT_SINGLE_COMP:
-			pb.singleComponent = true;
-			break;
-
-		case OPT_SAFE_FACT:
-			pb.safeFactorization = true;
-			break;
-
-		case OPT_CONST_BAND:
-			pb.variousBandwidth = false;
-			break;
+			case OPT_PART:
+				numPart = atoi(args.OptionArg());
+				break;
+			case OPT_TOL:
+				opts.tolerance = atof(args.OptionArg());
+				break;
+			case OPT_MAXIT:
+				opts.maxNumIterations = atoi(args.OptionArg());
+				break;
+			case OPT_DROPOFF_FRAC:
+				opts.dropOffFraction = atof(args.OptionArg());
+				break;
+			case OPT_NO_REORDERING:
+				opts.performReorder = false;
+				break;
+			case OPT_NO_SCALING:
+				opts.applyScaling = false;
+				break;
+			case OPT_MATFILE:
+				fileMat = args.OptionArg();
+				break;
+			case OPT_RHSFILE:
+				fileRhs = args.OptionArg();
+				break;
+			case OPT_OUTFILE:
+				fileSol = args.OptionArg();
+				break;
+			case OPT_FACTORIZATION:
+				{
+					string fact = args.OptionArg();
+					std::transform(fact.begin(), fact.end(), fact.begin(), ::toupper);
+					if (fact == "0" || fact == "LU_UL")
+						opts.method = spike::LU_UL;
+					else if (fact == "1" || fact == "LU_LU")
+						opts.method = spike::LU_only;
+					else
+						return false;
+				}
+				break;
+			case OPT_PRECOND:
+				{
+					string precond = args.OptionArg();
+					std::transform(precond.begin(), precond.end(), precond.begin(), ::toupper);
+					if (precond == "0" || precond == "SPIKE")
+						opts.precondMethod = spike::Spike;
+					else if(precond == "1" || precond == "BLOCK")
+						opts.precondMethod = spike::Block;
+					else
+						return false;
+				}
+				break;
+			case OPT_KRYLOV:
+				{
+					string kry = args.OptionArg();
+					std::transform(kry.begin(), kry.end(), kry.begin(), ::toupper);
+					if (kry == "0" || kry == "BICGSTAB")
+						opts.solverType = spike::BiCGStab;
+					else if (kry == "1" || kry == "BICGSTAB2")
+						opts.solverType = spike::BiCGStab2;
+					else
+						return false;
+				}
+				break;
+			case OPT_SINGLE_COMP:
+				opts.singleComponent = true;
+				break;
+			case OPT_SAFE_FACT:
+				opts.safeFactorization = true;
+				break;
+			case OPT_CONST_BAND:
+				opts.variousBandwidth = false;
+				break;
 		}
-
 	}
 
-	// If no problem was defined, show usage and exit.
-	if (pb.fileMat.length() == 0) {
-		cout << "No matrix file was defined!" << endl << endl;
+	// If the number of partitions was not defined, show usage and exit.
+	if (numPart <= 0) {
+		cout << "The number of partitions must be specified." << endl << endl;
 		ShowUsage();
 		return false;
 	}
 
+	// If no problem was defined, show usage and exit.
+	if (fileMat.length() == 0) {
+		cout << "The matrix filename is required." << endl << endl;
+		ShowUsage();
+		return false;
+	}
+
+	// If no reordering, force using constant bandwidth.
+	if (!opts.performReorder)
+		opts.variousBandwidth = false;
+
+	// If using variable bandwidth, force using LU factorization.
+	if (opts.variousBandwidth)
+		opts.method = spike::LU_only;
+
+	// Print out the problem specifications.
+	cout << endl;
+	cout << "Matrix file: " << fileMat << endl;
+	if (fileRhs.length() > 0)
+		cout << "Rhs file:    " << fileRhs << endl;
+	if (fileSol.length() > 0)
+		cout << "Sol file:    " << fileSol << endl;
+	cout << "Using " << numPart << (numPart ==1 ? " partition." : " partitions.") << endl;
+	cout << "Iterative solver: " << (opts.solverType == spike::BiCGStab2 ? "BiCGStab2" : "BiCGStab") << endl;
+	cout << "Tolerance: " << opts.tolerance << endl;
+	cout << "Max. iterations: " << opts.maxNumIterations << endl;
+	cout << "Preconditioner: " << (opts.precondMethod == spike::Spike ? "SPIKE" : "BLOCK DIAGONAL") << endl;
+	cout << "Factorization method: " << (opts.method == spike::LU_UL ? "LU - UL" : "LU - LU") << endl;
+	if (opts.dropOffFraction > 0)
+		cout << "Drop-off fraction: " << opts.dropOffFraction << endl;
+	else
+		cout << "No drop-off." << endl;
+	cout << (opts.singleComponent ? "Do not break the problem into several components." : "Attempt to break the problem into several components.") << endl;
+	cout << (opts.performReorder ? "Perform reordering." : "Do not perform reordering.") << endl;
+	cout << (opts.applyScaling ? "Apply scaling." : "Do not apply scaling.") << endl;
+	cout << (opts.safeFactorization ? "Use safe factorization." : "Use non-safe fast factorization.") << endl;
+	cout << (opts.variousBandwidth ? "Use variable bandwidth method." : "Use constant bandwidth method.") << endl;
+	cout << endl << endl;
+
 	return true;
 }
 
 
-// -------------------------------------------------------------------
+// -----------------------------------------------------------------------------
 // ShowUsage()
 //
 // This function displays the correct usage of this program
-// -------------------------------------------------------------------
+// -----------------------------------------------------------------------------
 void ShowUsage()
 {
 	cout << "Usage:  driver_mm [OPTIONS]" << endl;

};


// -------------------------------------------------------------------
// Problem types
// Problem definition
// -------------------------------------------------------------------
struct Problem {
	int           N;
	int			  nnz;
	int           k;

	int           numPart;

	int           maxIt;
	REAL          tol;
	REAL          fraction;

	bool          reorder;
	bool          scale;

	string        fileMat;
	string        fileRhs;
	string        fileSol;

	spike::SolverType    krylov;
	spike::SolverMethod  factorization;
	spike::PrecondMethod precondMethod;

	bool		  singleComponent;
	bool          safeFactorization;
	bool		  variousBandwidth;

	bool          verbose;
};


// -------------------------------------------------------------------
// Forward declarations.
// -------------------------------------------------------------------
void ShowUsage();
static const char* GetLastErrorText(int a_nError) ;

void spikeSetDevice();

bool GetProblemSpecs(int argc, char** argv, Problem& pb);

void GetRhsVector(const Matrix& A, Vector& b, Vector& x_target);

void PrintProblem(const Problem& pb, bool verbose);

void PrintStats(const Problem&	   pb,
				bool               success,
                const SpikeSolver& mySolver,
                const SpmvFunctor& mySpmv,
                bool               verbose);

void ClearStats(const SpikeSolver& mySolver);

// -------------------------------------------------------------------
// MAIN
// -------------------------------------------------------------------
int main(int argc, char** argv) 
{
	Problem pb;

	pb.N = 0;
	pb.nnz = 0;
	pb.k = 0;
	pb.maxIt = 100;
	pb.tol = 1e-6;
	pb.fraction = 0.0;
	pb.reorder = true;
	pb.scale = true;
	pb.numPart = 1;

	pb.factorization = spike::LU_only;
	pb.precondMethod = spike::Spike;
	pb.singleComponent = false;
	pb.safeFactorization = false;
	pb.variousBandwidth = true;
	pb.krylov = spike::BiCGStab2;

	pb.verbose = false;


	// Get the problem specification from the program arguments.
	if (!GetProblemSpecs(argc, argv, pb))
		return 1;

	if (!pb.reorder)
		pb.variousBandwidth = false;

	if (pb.variousBandwidth)
		pb.factorization = spike::LU_only;

	// Print information on the problem that will be solved.
	if (pb.verbose)
		PrintProblem(pb, pb.verbose);


	// Get the device with most available memory.
	spikeSetDevice();


	// Get matrix and rhs. Note that the 'target' solution is only
	// set if using a generated RHS.
	Matrix A;
	Vector b;
	Vector x_target;
	Vector delta_x_target;

	cusp::io::read_matrix_market_file(A, pb.fileMat);

	pb.N = A.num_rows;
	pb.nnz = A.num_entries;
	if (pb.fileRhs.length() > 0)
		cusp::io::read_matrix_market_file(b, pb.fileRhs);
	else
		GetRhsVector(A, b, x_target);

	SolverOptions solverOptions;

	solverOptions.solverType = pb.krylov;
	solverOptions.maxNumIterations = pb.maxIt;
	solverOptions.tolerance = pb.tol;

	solverOptions.performReorder = pb.reorder;
	solverOptions.applyScaling = pb.scale;
	solverOptions.dropOffFraction = pb.fraction;

	solverOptions.method = pb.factorization;
	solverOptions.precondMethod = pb.precondMethod;
	solverOptions.safeFactorization = pb.safeFactorization;
	solverOptions.variousBandwidth = pb.variousBandwidth;
	solverOptions.trackReordering = false;

	// Create the SPIKE Solver object and the SPMV functor.
	// Set the initial guess to the zero vector.
	SpikeSolver mySolver(pb.numPart, solverOptions);

	SpmvFunctor  mySpmv(A);
	Vector       x(pb.N, 0);

	mySolver.setup(A);
	bool success = mySolver.solve(mySpmv, b, x);

	// If an output file was specified, write the solution vector
	// in MatrixMarket format.
	if (pb.fileSol.length() > 0)
		cusp::io::write_matrix_market_file(x, pb.fileSol);

	// Print solution statistics.
	PrintStats(pb, success, mySolver, mySpmv, pb.verbose);

	// That's all folks!
	return 0;
}

// -------------------------------------------------------------------
// spikeSetDevice()
//
// This function gets the device with maximum free space and set that
// device to working device. 
// FIXME:
// Note that this function shall be removed when we start multi-gpu
// support. 
// -------------------------------------------------------------------
void spikeSetDevice() {
	int deviceCount = 0;
	

#include <spike/solver.h>


// -----------------------------------------------------------------------------
// Typedefs
// -----------------------------------------------------------------------------
typedef double REAL;

typedef typename cusp::csr_matrix<int, REAL, cusp::device_memory> Matrix;
typedef typename cusp::array1d<REAL, cusp::device_memory>         Vector;
typedef typename cusp::array1d<REAL, cusp::host_memory>           VectorHost;

typedef typename spike::Solver<Matrix, Vector>       SpikeSolver;
typedef typename spike::SpmvCusp<Matrix, Vector>     SpmvFunctor;


// -----------------------------------------------------------------------------
using std::cout;
using std::cin;
using std::endl;
