
/**
 * 
 * @author Bryan Ritter
 * ITCS 3143 - Summer 1, 2012
 * Banker's Algorithm
 * Uses the Banker's Algorithm to determine if the input is a in a 'safe' state
 * @Version at least 2012-07-02_13:13:19.200194824_                     PM_Mon_Week+27_(-04:00:00EDT)
 * 
 */

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Scanner;

/**
 * This class demonstrates use of the Banker's Algorithm
 *
 */
public class BankersAlgorithm {

	// TODO: Generate checks to make sure input is right?
	// TODO: Set up loop to do more than one?
	// TODO: better/more proper way of dealing with exceptions

	@Override
	public String toString() {
		return "BankersAlgorithm [getClass()=" + getClass() + ", hashCode()="
				+ hashCode() + ", toString()=" + super.toString() + "]";
	}

	/**
	 * This reads the data from a file, and uses that to call the isSafeBank function.
	 * 
	 * @param filename  The name in current directory or name and location of the file to read
	 * @return rather or not this is a 'safe state'
	 * @throws IOException
	 * 
	 */
	public static boolean BAStatusFromFile(String filename) throws IOException {

		// TODO: Consider printing of these values in a separate function, Each function should ideally do only one thing.
		// TODO: maybe this should be an 'overloaded' function, (goes overboard for this assignment?)
		//         file input if running program with a valid file argument
		//         prompt for file, if no arguments are given
		//         manually input arguments
		// TODO: Add error message if there's an error reading the file at all
		// TODO: better/more proper way of dealing with exceptions

		Scanner fr = new Scanner(new FileInputStream(filename),"UTF-8"); // file reader, 'fr', for reading the file

		boolean enoughData=true; // this will change to false if there isn't enough data
		
		if(fr.hasNextInt()){ // checks if there's an int, for 'pc' the number/count of processes
			int pc = fr.nextInt();
			if(fr.hasNextInt()){ // checks if there's an int, for 'rc' the number/count of resources
				int rc = fr.nextInt();

				// read in claim matrix, cm
				System.out.println("claim matrix:");
				int[][] cm = new int[pc][rc];
				for(int i = 0; i < pc; i++){ // rows
					for(int j = 0; j < rc; j++){ // columns
						if(fr.hasNextInt()){
							cm[i][j]=fr.nextInt();
							System.out.print(cm[i][j]); // print out this 'cm' value
							System.out.print( " " ); // print a space between 'cm' values
						}
						else{
							System.out.println("Error in reading the proper number or type of items for the 'claim' matrix.");
							enoughData=false;
						}
					}
					System.out.println(); // after finishing one line, start at the next line
				}

				System.out.println(); // add a blank line between these

				// read in accumulator matrix, am
				System.out.println("accumulator matrix:");
				int[][] am = new int[pc][rc];
				for(int i = 0; i < pc; i++){ // rows
					for(int j = 0; j < rc; j++){ // columns
						if(fr.hasNextInt()){
							am[i][j]=fr.nextInt();
							System.out.print(am[i][j]); // print out this 'am' value
							System.out.print( " " ); // add spaces between 'am' values
						}
						else{
							System.out.println("Error in reading the proper number or types of items for the 'accumulator' matrix.");
							enoughData=false;
						}
					}
					System.out.println(); 
				}

				System.out.println(); // add a blank line between these

				// read in rv
				System.out.println("reserves vector:");
				int[] rv = new int[rc];
				// Available Vector
				int[] av = new int[rc]; // TODO: test & look up, if this initializes to 0
				for(int j = 0; j < rc; j++ ){ // go through the columns
					if(fr.hasNextInt()){
						rv[j]=fr.nextInt();
						System.out.print(rv[j]); // print out this 'rv' value
						System.out.print( " " ); // add spaces between 'rv' values						
					}
					else{
						System.out.println("Error in reading the proper number or types of items for the 'reserves' vector.");
						enoughData=false;
					}
				}
								
				if(fr.hasNext()){
					System.out.println("Caution: there's more data than specified by the process count number and resource count number, ");
					System.out.println("proceeding as if the extra data wasn't there.");
				}
				fr.close(); // make sure the file is closed as soon it's quit using it
				
				// Convert this value from rv to av;
				av=rv;
				for(int i = 0; i < rc; i++){ // go through a row
					for(int j = 0; j < pc; j++){ // go through a column
						av[i]=av[i]-am[j][i];
					}
				}
				
				System.out.println(); // go to next line and
				System.out.println(); // add an extra blank line
				if(enoughData==true){ // this defaults to true, and changes to false if there isn't enough data
					return isSafeBank(cm, am, rv); // the actual function that figures out rather or not this represents a 'safe' state
				}
				//else{ // Statement unnecessarily nested within else clause. The corresponding then clause does not complete normally
					return false;
				//}
			} // inner if(fr.hasNextInt())
			//else{ // Statement unnecessarily nested within else clause. The corresponding then clause does not complete normally
				System.out.println("no 'number of resources' (second int) stated at top of file.");
			//}
		} // outer  if(fr.hasNextInt())
		else{
			System.out.println("no 'number of processes' (first int) stated at top of file.");
		}
		
		fr.close(); // this line shouldn't be reached if the file was formatted the same way as the sample code
		System.out.println("Error in reading first two integers, make sure the file is formated correctly."); 
		return false; // actually, in this case this means the file wasn't done in the right format, it's written to return a boolean

	}// end BAfromFile(String filename)

	/**
	 * With the given the 'claim' matrix, the 'allocation' matrix, and the 'resource' vector, determine if it is a 'safe' state.
	 * 
	 * @param cm  the claim matrix
	 * @param am  the currently allocated matrix
	 * @param the resource available vector
	 * @return rather or not the given data is a 'safe' state
	 * 
	 */
	private static boolean isSafeBank(int[][] cm, int[][] am, int[] av) {
		// TODO: better organize this?
		// TODO: test to see if this works if a process doesn't need any of these resources when it starts
		// TODO: test other various situations out

		int rc = am[1].length; // resource count
		int pc = am.length; // process count
		
		boolean canZero; // this resource for this process doesn't prevent completion of the process
		int zeroCount = 0; // number of processes than can be completed
		boolean changed = true; // a process came to completion during the last iteration
		while(changed==true && zeroCount<rc){ // if a process has come to completion, and there's still a process to go
			changed = false; // resets the changed variable to false for each iteration in the while loop
			for(int i = 0; i < pc; i++){ // go through each process...
				canZero=true; // Default true, any one non true case otherwise cancels this for this process on this iteration
				for(int j = 0; j < am[i].length; j++){ // go through columns/resources in a row/process ...
					if((am[i][j]+av[j])-cm[i][j]<0){ // can zero for one element isn't same as can zero for row/process...
						canZero=false; // an item can't be finished with the resources available
						break; // ends row... //TODO: test algorithm more throughly!
					}
				} //finished a loop for one process

				// at the end of processing a row/process
				if(canZero==true){ // true only occurs when there's nothing to process
					zeroCount++; // number of processes that can come to completion so far
					changed=true; // could have done old zeroCount vs new zeroCount instead, this means at least one process came to conclusion during this iteration.
					for(int j = 0; j < am[i].length; j++){
						cm[i][j]=0; // process will finish set 'claims' back to zero
						av[j]=av[j]+am[i][j]; // add resources these back to the 'available vector'
					} // end for
				} // end if(canZero==true)...
			} // end a row/process
		} // end while(changed==true && zeroCount<rc)

		if(zeroCount == pc){ // all rows/processes have come to completion
			return true;
		}
		// Statement unnecessarily nested within else clause. The corresponding then clause does not complete normally
		//else{ // an iteration happened where no row/process could come to completion
			return false;
		//}
	} // end private static boolean isSafeBank(int[][] cm, int[][] am, int[] av)

	/**
	 * The main function, gets called when this file is run.
	 * 
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		
		boolean result;

		// TODO: figure out: why is BAStatusFromFile is 'static', etc...?, I've seen this problem before.

		//result = BAStatusFromFile("/My_Stuff/MyDocuments/ZZZ_My_Documents_2012-05-18_to_2012-07-03/sampleforprogramNoResources.txt");		
		//result = BAStatusFromFile("/My_Stuff/MyDocuments/ZZZ_My_Documents_2012-05-18_to_2012-07-03/sampleforprogram.txt");
		//result = BAStatusFromFile("/My_Stuff/MyDocuments/ZZZ_My_Documents_2012-05-18_to_2012-07-03/BlankFile.txt");
		//result = BAStatusFromFile("/My_Stuff/MyDocuments/ZZZ_My_Documents_2012-05-18_to_2012-07-03/2012-04_Summer_I_Classes/TeachersTestCase.txt");
		

		System.out.println("What's the file: ");
		BufferedReader inputBuffer = new BufferedReader(new InputStreamReader(System.in));
		String fileNameInput = inputBuffer.readLine();
		result =  BAStatusFromFile(fileNameInput); // a boolean

		if(result==true){
			System.out.println("This is a 'safe' state!");
		}
		else{
			System.out.println("This is an 'unsafe' state!");
		}
	}
}
