/*
			                                Program-3
			             	           Class Matrix Design		Due: 10/2/2012
	Objectives:
	1.	Continued Use of class and object creation
	2.	Use of 'double' primitive type for increased precision and a small 
		tolerance value for checking for
		the equality of two floating-point values.
		Namely, use of "Math.abs(double1-double2) < 0.0000000001" rather 
		than "double1==double2"
	3.	Using an input text file using two classes FileInputStream and Scanner.
	4.	Creating an output text file using two classes, FileOutputStream and PrintWriter.
		(Alternatively using three classes, FileWriter. BufferedWriter and PrintWriter)
	5.	Calculating matrix inverses and determinants
	6.	Use of two command-line arguments for an input file name and an 
		output file name (for appending)

	Tasks:
	Modify the following program by completely defining four methods:
		1. public void calInverse(): Calculates the instance variable inverse[][] which is the inverse of
		    original[][].
		2. public double findDeterminant (double [][] mat, int howBig): Recursively calculates the
		    determinant of the current matrix of given size, howBig.
		3. public void matrixMultiply(): Multiplies two instance variables, original and inverse, expecting
		    to get the identity matrix to confirm the correctness of inverse calculation.
	      	4. public static void main (String args [])    
	
	Required Inputs:
	Use the following three input cases plus some more of your own (at least three more)
	  	3
	  	2.00    1.00   3.00
  	 	4.00    0.00   -2.00
  		5.00    0.00    4.00
  		3
  		2.00    1.00    3.000
  		4.000   0.000   -2.000
  		8.000   2.000   4.0000
  		5
  		1.0000   2.0000   1.2000  -2.000   -2.500
  		-1.000   0.0000   0.0000  2.5000   -2.5000
  		-2.2000   3.3333    4.444   -2.000  -5.0000
  		3.000    0.0004   0.0000   1.1111    -2.222
   		-3.3333   -4.444   0.2222   0.25000  123.00  


	*/
	import java.util.Scanner;
	import java.io.FileInputStream;
	import java.io.FileOutputStream;
	import java.io.PrintWriter;
	import java.io.FileNotFoundException;	// a subclass of IOException
	import java.io.IOException;
	public class Matrix
	{
	// private instance variables
        	private static int MAX=20; 
	private int size;
        	private double[][] original = new double [MAX][MAX];
        	private double[][] inverse = new double [MAX][MAX];   
				// inverse of original when it does exists
	private boolean exists;	// indicator of whether or not the inverse does exist. 
        	private double determinant;            // the determinant of original matrix.

	// public methods

	public Matrix (Scanner inputStream)
	// This one-argument constructor will create an object of this class, Matrix, 
	// by reading the given inputStream that must have been connected to an physical 
	// input file by the main() for the size
	// of a new object and its original matrix of that given size.
	{
	exists = false;	// Initially assume the non-existence of inverse.
	try
	  {
	  if (inputStream.hasNext())
		{
		size = inputStream.nextInt();	//The instance variable size is defined.
		for (int row=0; row<=size-1; row++)
		  for (int col=0; col<=size-1; col++)
		    original[row][col] = inputStream.nextDouble();
		}	
	   else	// an input file exception 
             
                     throw new FileNotFoundException ("ERROR:::END-OF-INPUT-FILE:::");   
           
	  }	// end of the construction and also of try block
	catch (FileNotFoundException e)
	  {
	  System.out.println ("\n:::INPUT FILE NOT FOUND OR SOMETHING ELSE:::");
	  System.exit (0);
	  }	// end of catch block
	}	// end of constructor Matrix()
	////////////////////////////////////////////////////////////////////
	// end of constructor method.
	////////////////////////////////////////////////////////////////////
	public void calInverse ()
	// Calculates two instance variables, inverse and exists
	{
	exists = false;		// assume that the original matrix is not invertible.
				// The constructor already made this assumption.
	double [][] result = new double [size][size];
	double [][] temp   = new double [size][size];
	int row, col, belowRow, index;
	double tolerance = 0.000000000001;
	double factor;
	boolean found;
	// left empty for you to fill in!!!
        	}	// end of calInverse()
	//////////////////////////////////////////////////////////////////
	public void exchange (double[][] mat, int row1, int row2)
	// exchanges the two given rows of the given matrix of doubles
	{
	  double temp;
	  for (int col=0; col<=size-1; col++)
		{
		temp = mat[row1][col];
		mat[row1][col] = mat[row2][col];
		mat[row2][col] = temp;
		}
	}	// end of exchange()
	//////////////////////////////////////////////////////////////////
	public void matrixMultiply()
        	// Multiplies the matrix original and the matrix inverse
        	// only when the latter, in fact, exists.
        	// Additionally, prints out the product matrix to System.in when multiplication
        	// actually carried out as the product must be an identity matrix
        	// if the inverse is correctly calculated.
	{
          	double[][] product = new double [MAX][MAX];
	double temp, sum;
	// left empty for you to fill in!!!
	{
	}
	// Computation of matrix product is over, and time to print it out to the screen.
          	System.out.println ("\nPRODUCT MATRIX SUPPOSED TO BE AN IDENTITY MATRIX FOLLOWS:::\n");
	for (int row=0; row<=size-1; row++)
	      {
	      for (int col=0; col<=size-1; col++)
                  		System.out.printf ("%10.6f", product[row][col]);
	      System.out.println();
	      }
	// printing is over and also end of method matrixMultiply()
	}	// end of method matrixMultiply()
	/////////////////////////////////////////////////////////////////
	public double getDeterminant()
	// Simply returns the instance variable, determinant.
	{
	  return determinant;
	}	// end of this method().
	/////////////////////////////////////////////////////////////////
	public void calDeterminant ()
	// Calculates the determinant of the matrix original by calling a recursive
	// method findDeterminant (double[][] mat, int howBig) and sets the Instance Variable,
	// determinant, at the same time.
	{
	 determinant = findDeterminant (original, size);
	}	// end of method calDeterminant()
	////////////////////////////////////////////////////////////////
	public double findDeterminant (double[][] mat, int howBig)
	// Calculates the determinant of the given square matrix of given size recursively
	// if the given size is at least 2.
	{ // left empty for you to fill in!!!
	}	// end of findDeterminant()
	//////////////////////////////////////////////////////////////////////////
        	public boolean getExists()
        	// Simply returns the boolean instance variable, exists.
        	{
          	return exists;
        	}       // end of method getExists()
        	//////////////////////////////////////////////////////////////////////////
	public void filePrint (PrintWriter outputStream)
	// Prints both the matrix original and the matrix inverse to an output file.
	{
	int row, col;
	try
          	{     
	if (outputStream == null)
                         throw new FileNotFoundException ("OUTPUT FILE NOT FOUND:::");
	outputStream.println ("\nTHE ORIGINAL MATRIX:::");
	for (row=0; row<=size-1; row++)
	      {
	      for (col=0; col<=size-1; col++)
                    	outputStream.printf ("%12.5f", original[row][col]);
	      outputStream.println();
	      }	// end of printing the entire matrix original
	if (exists)	// Then inverse exists and is calculated.
	      {
	      outputStream.println ("\nTHE INVERSE MATRIX EXISTS:::");	
	      for (row=0; row<=size-1; row++)
		{
		 for (col=0; col<=size-1; col++)
                      		outputStream.printf ("%12.5f", inverse[row][col]);
		 outputStream.println();
		 }
	      }	// end of printing an existing inverse.
	else	// inverse does not exist.
	      outputStream.println ("\nTHE INVERSE DOES NOT EXIST, SORRY:::");
	// end of printing both matrices when both do exist...
	}	// end of try block
	catch (FileNotFoundException e)
	{
            	System.out.println (e.getMessage());
            	System.out.println ("Program aborted:::");
	System.exit(0);
	}	// end of catch block
	}	// end of method filePrint()
	//////////////////////////////////////////////////////////////////////
        	public static void main (String[] args) 
	{
          	      PrintWriter outputStream = null;      // To be safe,
                                                // without nullifying Appending Operation
          	      Scanner     inputStream = null;       // To be safe.
	      int inputCount = 0;
	      Matrix matObj;
	      try
	     {
            		FileInputStream inStream =  new FileInputStream  (args[0]);
            		inputStream = new Scanner (inStream);
            		FileOutputStream outStream = new FileOutputStream (args[1], true); // Appending
            		outputStream = new PrintWriter (outStream);
	    	while (inputStream.hasNext())
		{
		      inputCount++;
		      /* Your tasks: Repeat as long as there are enough inputs left:
		          1. create an object of this class, Matrix, which will read an input file for the two instance variables;
		              size and original.
		          2. calculate the inverse and determinant of this object's original.
		          3. print to an output file, the size, original, and inverse (if, in fact, it does exists) and determinant.
		}	// all input matrices of varying sizes have been processed
	    	outputStream.close();
	    	inputStream.close();
	        }		// end of try block
	        catch (FileNotFoundException e)
	        {
	        	System.out.println ("SOME EXCEPTION WITH INPUT/OUTPUT FILES:::");
	    	System.exit (0);
	         }
	System.out.println ("\nMY PROGRAM PROCESSED:::" + inputCount + "  OBJECTS IN SUCCESS.");
	}	// end of main()
        }		// end of class Matrix