/**
 * Students: Jennifer Yu (mainly FractionMain class) & Samar Qureshi (mainly Fraction class)
 * Course: ICS4U
 * Date: November 9, 2021
 * The Fraction Main class allows the user to find the harmonic sum of a natural number and the approximation of the Maclaurin series for e^x for an x value (integer). Additionally, it provides the user with other options such as converting to and from improper fractions, simplifying fractions, calculating expressions containing fractions, converting decimals to fractions, and sorting a list of fractions from least to greatest. These actions are done with the help of Fraction objects.
 */

import java.util.Scanner;

public class FractionMain {
	private static final Scanner INPUT = new Scanner(System.in);
	
	public static void main(String[] args) {
		// declarations
		String userInput;
		int userChoice;
		Fraction harmonicSum = new Fraction(), eX = new Fraction(), frac;
		long[] fraction, mixed;
		
		do {
			// main menu
			System.out.println("\\nMAIN MENU");
			System.out.println("0 - Exit");
			System.out.println("1 - Find the harmonic sum of a number                                      x"); // x is for the superscript for option 2
			System.out.println("2 - Approximate e to the power of a number using the Maclaurin Series for e");
			System.out.println("3 - Convert improper fraction to mixed number");
			System.out.println("4 - Convert mixed number to improper fraction");
			System.out.println("5 - See the simplified form of a fraction");
			System.out.println("6 - Fraction calculator");
			System.out.println("7 - Convert decimal to fraction");
			System.out.println("8 - Sort a list of fractions from least to greatest");
			System.out.print("Enter your choice: ");
			userInput = INPUT.nextLine();
			userChoice = makeInt(userInput);
			
			System.out.println();
			
			// corresponding choices
			if (userChoice == 1) {
				System.out.println("\\nHARMONIC SUM");
				harmonicSum = inputHarmonicSum();

			} else if (userChoice == 2) {
				System.out.println("                      x"); // to imitate superscript for e^x in the console
				System.out.println("MACLAURIN SERIES FOR e");
				eX = inputMaclaurinSeries();
				
			} else if (userChoice == 3) {
				System.out.println("\\nIMPROPER FRACTION --> MIXED NUMBER");
				fraction = inputConvertToMixed(harmonicSum, eX); // get input
				
				frac = new Fraction(fraction[0], fraction[1]); // construct frac
				displayConvertToMixed(frac); // convert to mixed number & display
				
			} else if (userChoice == 4) {
				System.out.println("\\nMIXED NUMBER --> IMPROPER FRACTION");
				mixed = inputConvertToImproper();
				
				frac = new Fraction(mixed[0], mixed[1], mixed[2]); // construct frac; whole number, numerator, denominator
				displayConvertToImproper(frac); // convert to improper & display
				
			} else if (userChoice == 5) {
				System.out.println("\\nFRACTION IN SIMPLEST FORM");
				fraction = inputSimplifyFraction();
				
				// construct frac & simplify it
				frac = new Fraction(fraction[0], fraction[1]);
				displaySimplifyFraction(frac);
				
			} else if (userChoice == 6) {
				System.out.println("\\nFRACTION CALCULATOR");
				inputCalculate();
				
			} else if (userChoice == 7) {
				System.out.println("\\nDECIMAL TO FRACTION");
				inputConvertToFraction();
				
			} else if (userChoice == 8) {
				System.out.println("\\nSORT ARRAY OF FRACTIONS");
				outputFracArr(sortFractions(inputFracArr()));
				
			} else if (userChoice != 0) { // input was either not an integer or not an option
				System.out.println("Invalid input. Please try again.");
			}
			
			System.out.println();
			
		} while (userChoice != 0);
		
		// farewell message
		System.out.println("Goodbye!");
	}
	
	/*
	 * Takes input (limits the input to prevent long overflow when calculating the harmonic sum) and calls the method that calculates and displays the harmonic sum
	 * Pre: user must have chosen option 1 in the main menu
	 * Post: returns a Fraction object
	 */
	public static Fraction inputHarmonicSum() {
		// declarations
		String userInput;
		int userNum;
		boolean isValid = false;
		Fraction harmonicSum = new Fraction();
		
		// get input and call the calcHarmonicSum method if it is valid; keep repeating while input is invalid
		do {
			System.out.print("Enter a natural number (from 1 to 43) to find its harmonic sum: ");
			userInput = INPUT.nextLine();
			userNum = makeInt(userInput); // convert user's input to an int if possible (-1 if the input was not a whole number)
			
			if (userNum != -1 && userNum != 0 && userNum < 44) { // if the input is a natural number, then display the harmonic sum by calling the method (limits the user to natural numbers under 44 to prevent long overflow)
				harmonicSum = calcHarmonicSum(userNum);
				isValid = true;
				
			} else if (userNum == -1 && userInput.length() > 0 && userInput.charAt(0) == '-' && makeInt(userInput.substring(1)) != -1) { // if the user entered a number but it was not positive, then user its absolute value instead
				userNum = makeInt(userInput.substring(1));
				
				if (userNum > 0 && userNum < 44) { // make sure the user's number is between 0 and 44
					System.out.println("Although you inputted a negative number, here is the harmonic sum of its absolute value.");
					harmonicSum = calcHarmonicSum((int) Math.abs(userNum)); // display the harmonic sum of the absolute value of the user's number
					isValid = true;
				}
			}
			// tell user to try again
			if (!isValid) {
				System.out.println("Remember, it must be a natural number (positive integer) from 1 to 43. Try again.");
			}
		} while (!isValid);
		
		return(harmonicSum);
	}
	
	/*
	 * Calculates and shows the steps to getting the harmonic sum and displays it to the user. Also returns the harmonic sum as a Fraction object
	 * Pre: int representing the user's input
	 * Post: displays the terms of the series and the final harmonic sum, returns the harmonic sum as a Fraction object
	 */
	public static Fraction calcHarmonicSum(int n) {
		// declarations and initializations
		Fraction harmonicSum = new Fraction();
		Fraction currentFrac;
		String[][] fractionArr = new String[n + 1][3];
		
		// adding the fractions
		for (int i = 1; i <= n; i++) {
			currentFrac = new Fraction(1, i); // construct currentFrac
			harmonicSum.add(currentFrac); // add the current fraction to the sum
			fractionArr[i - 1] = currentFrac.formatImproper(); // format it for output later
		}
		
		// format the harmonic sum
		fractionArr[n] = harmonicSum.formatImproper();
		
		// output; go through every numerator first, then fraction bars, then denominators
		for (int j = 0; j < fractionArr[0].length; j++) {
			for (int i = 0; i < fractionArr.length; i++) {
				
				if (i == fractionArr.length - 1) { // if we are trying to print a part of the harmonic sum, then nothing comes after
					System.out.println(fractionArr[i][j]);
					
				} else if (i == fractionArr.length - 2 && j == 1) { // if we are printing the fraction bar of the last term, it must be followed by =
					System.out.print(fractionArr[i][j] + " = ");
					
				} else if (j != 1) {
					System.out.print(fractionArr[i][j] + "   "); // if we are printing the numerator or denominator of a term
					
				} else {
					System.out.print(fractionArr[i][j] + " + "); // if we are printing the fraction bar of a term, then we must also add a + to show that we are adding
				}
			}
		}
		
		// decimal output
		System.out.println("\\nAs a decimal, the harmonic sum of " + n + " is " + harmonicSum.calcDecimal());
		
		return(harmonicSum);
	}
	
	/*
	 * Takes input and calls the method that calculates the approximation of e^x for the inputted x value
	 * Pre: user must have chosen option 2 in the main menu
	 * Post: returns a Fraction object
	 */
	public static Fraction inputMaclaurinSeries() {
		// declarations
		String userInput;
		int userNum;
		boolean isValid = false;
		Fraction eX = new Fraction();
		
		do {
			// get user input & convert to integer
			System.out.print("Enter an integer, x, from -4 to 6 to find the approximation of e to the power of x: ");
			userInput = INPUT.nextLine();
			userNum = makeInt(userInput);
			
			// call the method that calculates e^x if the input is valid
			if (userNum != -1 && userNum < 7) { // whole number, less than 7 to ensure that the answer is somewhat accurate
				eX = calcMaclaurinSeries(userNum);
				isValid = true;
				
			} else if (userNum == -1 && userInput.length() > 0 && userInput.charAt(0) == '-' && makeInt(userInput.substring(1)) != -1) { // negative integer
				userNum = -makeInt(userInput.substring(1));
				
				if (userNum > -5) { // limits the input to prevent long overflow
					eX = calcMaclaurinSeries(userNum);
					isValid = true;

				}
				
				userNum = 1; // or else the do while loop will continue to run
			}
			
			// invalid input message
			if (!isValid) { 
				System.out.println("Remember, it must be an integer (from -4 to 6). Try again.");
			}
		} while (!isValid);
		
		return(eX);
	}
	
	/*
	 * Calculates the approximation for e^x given an x value, displays it to the user, and returns the fraction object.
	 * Pre: int for x
	 * Post: displays the approximation for e^x, returns a Fraction object
	 */
	public static Fraction calcMaclaurinSeries(int x) {
		// declarations & initializations
		String[] fractionArr = new String[3];
		Fraction sum = new Fraction();
		Fraction currentFrac;
		
		// calculating
		for (int i = 0; i < 15; i++) { // the more iterations of the loop, the lower the number the user can input for x because a long is only 64 bits (-2^61 to 2^61-1) so after that, the fraction won't be accurate. But, this also means the approximation will not be as close given larger x values
			currentFrac = new Fraction((long) Math.pow(x, i), calcFactorial(i)); // maclaurin series for e^x

			currentFrac.simplify(); // reduce the fraction to lowest terms to try and prevent long overflow

			sum.add(currentFrac);
			sum.simplify(); // reduce the fraction to lowest terms to try and prevent long overflow
			
			// prevent long overflow when x = 1 or -1
			if (Math.abs(x) == 1 && i == 13) {
				i = 15;
			}
		}
		
		fractionArr = sum.formatImproper();
		
		// output
		System.out.println(" " + x + getSpaces(" is approximately equal to: ".length()) + fractionArr[0]); // x is to act as the superscript of e^x
		System.out.println("e" + getSpaces((x + "").length()) + " is approximately equal to: " + fractionArr[1]);
		System.out.println(getSpaces(1 + (x + "").length() + " is approximately equal to: ".length()) + fractionArr[2]);
		
		// decimal output
		System.out.println("\\nAs a decimal, it is approximately " + sum.calcDecimal());
		
		return(sum);
	}
	
	/*
	 * Takes input of what the user would like to convert (either the harmonic sum, ex, or a new fraction) and ensures that if the user wants to convert a new fraction, it is valid
	 * Pre: two Fraction objects (harmonic sum and e^x), user must have chosen option 3 from the main menu
	 * Post: returns a long array
	 */
	public static long[] inputConvertToMixed(Fraction harmonicSum, Fraction eX) {
		// declarations
		String userInput;
		char userSubChoice;
		long[] fraction = new long[2];
		
		// display options and make user choose
		do {
			// options
			System.out.println("A - Convert the harmonic sum to mixed number       x");
			System.out.println("B - Convert the Maclaurin Series approximation of e  to mixed number");
			System.out.println("C - Convert a new fraction to a mixed number");
			System.out.print("Enter your choice: ");
			userInput = INPUT.nextLine();
			
			// convert to correct char
			if (userInput.length() != 1) {
				userSubChoice = 'z';
			} else {
				userSubChoice = userInput.toUpperCase().charAt(0);
			}
			
			// invalid message
			if (userSubChoice < 65 || userSubChoice > 67) {
				System.out.println("That is invalid. Try again.\\n");
			}
			
		} while (userSubChoice < 65 || userSubChoice > 67); // keeps looping while it isn't A, B, or C
		
		// run corresponding option
		if (userSubChoice == 'A') { // harmonic sum
			fraction[0] = harmonicSum.getNumerator();
			fraction[1] = harmonicSum.getDenominator();
		} else if (userSubChoice == 'B') { // e^x
			fraction[0] = eX.getNumerator();
			fraction[1] = eX.getDenominator();
		} else { // user fraction
			do {
				System.out.print("Enter a fraction to display as a mixed number: ");
				fraction = checkValidFraction(INPUT.nextLine());
			} while (fraction[1] == 0); // while invalid fraction (aka denominator = 0)
		}
		
		return(fraction);
	}
	
	/*
	 * Displays the equivalent mixed number given an improper fraction
	 * Pre: Fraction object
	 * Post: returns nothing but displays the mixed number to the user
	 */
	public static void displayConvertToMixed(Fraction frac) {
		// declaration & initialization
		String[][] fractionArr = new String[2][3]; // fractionArr[0] will be the improper fraction and fractionArr[1] will have part of the fraction part of the mixed number
		
		fractionArr[0] = frac.formatImproper(); // format improper fraction
		
		// get the mixed number values of the fraction
		frac.improperToMixed();
		
		fractionArr[1] = frac.formatMixed(); // format mixed number
		
		System.out.println();
				
		// output
		if (frac.getWholeNumber() == 0) { // normal fraction
			System.out.println(fractionArr[0][0] + getSpaces(" as a mixed number is equal to: ".length())+ fractionArr[1][0]); // numerators
			System.out.println(fractionArr[0][1] + " as a mixed number is equal to: " + fractionArr[1][1]); // fraction bars & words
			System.out.println(fractionArr[0][2] + getSpaces(" as a mixed number is equal to: ".length()) + fractionArr[1][2]); // denominators
			
		} else if (frac.getDenominator() == 1) { // whole number
			System.out.println(fractionArr[0][0]); // numerators
			System.out.println(fractionArr[0][1] + " as a mixed number is equal to: " + frac.getWholeNumber()); // words and whole number of mixed number
			System.out.println(fractionArr[0][2]); // denominators
			
		} else {
			System.out.println(fractionArr[0][0] + getSpaces(" as a mixed number is equal to: ".length() + (frac.getWholeNumber() + "").length() + 1) + fractionArr[1][0]); // numerators
			System.out.println(fractionArr[0][1] + " as a mixed number is equal to: " + frac.getWholeNumber() + " " + fractionArr[1][1]); // fraction bars & words, and whole number of mixed number
			System.out.println(fractionArr[0][2] + getSpaces(" as a mixed number is equal to: ".length() + (frac.getWholeNumber() + "").length() + 1) + fractionArr[1][2]); // denominators
		}

	}
	
	/*
	 * Takes input of a mixed number
	 * Pre: user must have chosen option 4 from the main menu
	 * Post: returns a long array
	 */
	public static long[] inputConvertToImproper() {
		// declaration
		long[] mixed = new long[3];
		
		// get input
		do {
			System.out.print("Enter a mixed number to display as an improper fraction (e.g. 2 5/13): ");
			mixed = checkValidMixed(INPUT.nextLine().trim()); // remove spaces from start & end to help with checking for valid mixed
		} while (mixed[2] == 0); // denominator = 0 --> invalid input
		
		return(mixed);
	}
	
	/*
	 * Displays the equivalent improper fraction given a mixed number
	 * Pre: Fraction object
	 * Post: returns nothing but displays the improper fraction to the user
	 */
	public static void displayConvertToImproper(Fraction frac) {
		// declaration
		String[][] fractionArr = new String[2][3];
		boolean alreadyImproper = true;
		
		// initializing/assigning values
		fractionArr[0] = frac.formatMixed();
		
		// formatting the user's mixed number
		if (frac.getWholeNumber() != 0) {
			alreadyImproper = false; // the fraction given is mixed
			fractionArr[0][0] = getSpaces((frac.getWholeNumber() + "").length() + 1) + fractionArr[0][0];
			fractionArr[0][1] = frac.getWholeNumber() + " " + fractionArr[0][1];
			fractionArr[0][2] = getSpaces((frac.getWholeNumber() + "").length() + 1) + fractionArr[0][2];
			
		} else if (frac.getDenominator() == 1) { // no need to show denominator
			fractionArr[0][0] = getSpaces((frac.getMixedNumerator() + "").length());
			fractionArr[0][1] = frac.getMixedNumerator() + "";
			fractionArr[0][2] = getSpaces((frac.getMixedNumerator() + "").length());
		}
		
		frac.mixedToImproper();
		
		fractionArr[1] = frac.formatImproper();
		
		System.out.println();
		
		// output
		if (alreadyImproper) { // the whole number was always 0 (even before converting)
			System.out.println(fractionArr[1][0]);
			System.out.println(fractionArr[1][1] + " is already an improper fraction!");
			System.out.println(fractionArr[1][2]);
		} else { // regular case
			System.out.println(fractionArr[0][0] + getSpaces(" as an improper fraction is equal to: ".length()) + fractionArr[1][0]);
			System.out.println(fractionArr[0][1] + " as an improper fraction is equal to: " + fractionArr[1][1]);
			System.out.println(fractionArr[0][2] + getSpaces(" as an improper fraction is equal to: ".length()) + fractionArr[1][2]);
		}
	}
	
	/*
	 * Takes input of what the user would like to simplify
	 * Pre: user must have chosen option 5 in the main menu
	 * Post: returns a long array
	 */
	public static long[] inputSimplifyFraction() {
		// declaration
		long[] fraction = new long[2];
		
		// get input
		do {
			System.out.print("Enter a fraction to simplify: ");
			fraction = checkValidFraction(INPUT.nextLine());
		} while (fraction[1] == 0); // keep looping until a valid fraction is entered
		
		return(fraction);
	}
	
	/*
	 * Displays the simplified fraction
	 * Pre: Fraction object
	 * Post: doesn't return anything but displays the formatted, simplified fraction
	 */
	public static void displaySimplifyFraction(Fraction frac) {
		// declaration & initialization
		String[] fractionArr = new String[3];
		
		frac.simplify(); // simplify fraction if possible
		
		fractionArr = frac.formatImproper();
		
		// output
		if (frac.getDenominator() == 1) {
			System.out.println(fractionArr[0]); // only display the numerator if the denominator is 1
		} else {
			for (int i = 0; i < fractionArr.length; i++) { // display full fraction
				System.out.println(fractionArr[i]);
			}
		}

	}
	
	/*
	 * Insists for a string containing a valid expression with fractions as input and calls the method that calculates the expression
	 * Pre: user must have chosen option 6 from the main menu
	 * Post: returns nothing
	 */
	public static void inputCalculate() {
		// declarations
		String userInput;
		
		// get valid input
		do {
			System.out.println("Enter an expression containing fractions (e.g. 3/5 + 7/2 * 18/29). You can use the following symbols: ");
			System.out.println(" o Addition --> +\\n o Subtraction --> _\\n o Multiplication --> *\\n o Division --> |");
			System.out.print("Enter an expression (use +, _, *, |): "); // subtraction & division operators are different to avoid confusion with negatives and fraction bars
			userInput = checkValidExpression(INPUT.nextLine());
			
			// invalid input message
			if (userInput.equals("invalid")) {
				System.out.println("Invalid expression. Try again.\\n");
			}
			
		} while (userInput.equals("invalid")); // keep asking for a valid expression
		
		calculate(userInput);
	}
	
	/*
	 * Calculates the expression following the order of operations (multiplication & division before addition & subtraction) and displays the final fraction and decimal
	 * Pre: String
	 * Post: returns nothing but displays the final fraction and decimal
	 */
	public static void calculate(String expression) {
		// declarations
		boolean hasDM = false; // BEDMAS --> DM = division/multiplication
		Fraction frac;
		long[] fractionArr;
		
		// calculate the expression; multiplication/division come before addition/subtraction
		for (int i = 0; i < expression.length(); i++) {
			if (expression.charAt(i) == '*') {
				expression = evaluate(expression, i, '*');
				i = -1; // restart from the beginning of the expression
				
			} else if (expression.charAt(i) == '|') {
				expression = evaluate(expression, i, '|');
				i = -1;
				
			} else if (i == expression.length() - 1 && !hasDM) {
				hasDM = true; // lets the program know that addition/subtraction can occur now
				i = -1;
				
			} else if (hasDM && expression.charAt(i) == '+') {
				expression = evaluate(expression, i, '+');
				i = -1;
				
			} else if (hasDM && expression.charAt(i) == '_') {
				expression = evaluate(expression, i, '_');
				i = -1;
				
			} 

		}
		
		// format and output
		fractionArr = checkValidFraction(expression);
		frac = new Fraction(fractionArr[0], fractionArr[1]);
		displaySimplifyFraction(frac);
		
		System.out.println("As a decimal, it is " + frac.calcDecimal());
	}
	
	/*
	 * Takes input of a valid decimal and calls a method to convert it to a fraction and display it
	 * Pre: user must have chosen option 7 from the main menu
	 * Post: returns nothing
	 */
	public static void inputConvertToFraction() {
		// declarations
		Fraction frac = new Fraction();
		String userInput;
		boolean isValid;
		
		// get valid input
		do {
			isValid = true;
			System.out.print("Enter a decimal: ");
			userInput = INPUT.nextLine();
			
			if (userInput.replaceAll("\\\\.", "").length() + 1 < userInput.length()) { // more than one decimal
				isValid = false;
			} else if (makeInt(userInput.replaceAll("\\\\.", "")) == -1 && userInput.length() > 0 && makeInt(userInput.replaceAll("\\\\.", "").substring(1)) == -1) { // not negative or positive
				isValid = false;
			} else if (userInput.length() == 0) { // empty string
				isValid = false;
			}
			
		} while (!isValid);
		
		
		// convert & display
		frac.decimalToFrac(userInput);
		
		displaySimplifyFraction(frac);
	}
	
	/*
	 * Evaluates part of the according to the operator given
	 * Pre: String expression, int (index), char operator
	 * Post: returns a string
	 */
	public static String evaluate(String expression, int i, char operator) {
		// declarations
		Fraction frac1, frac2;
		int previousOperator, nextOperator;
		long[] fraction;
		
		// find the start and end of the binomial
		previousOperator = findPreviousOperator(expression, i);
		nextOperator = findNextOperator(expression, i);
		
		
		// make sure the terms before and after the operator are valid
		fraction = checkValidFraction(expression.substring(previousOperator + 1, i)); // get numerator & denominator
		frac1 = new Fraction(fraction[0], fraction[1]); // create fraction
		
		fraction = checkValidFraction(expression.substring(i + 1, nextOperator));
		frac2 = new Fraction(fraction[0], fraction[1]); // create fraction

		// evaluate accordingly
		if (operator == '*') {
			frac1.multiply(frac2);
		} else if (operator == '|') {
			frac1.divide(frac2);
		} else if (operator == '+') {
			frac1.add(frac2);
		} else {
			frac1.subtract(frac2);
		}
		
		// replace the two terms with the answer and return
		return(expression.substring(0, previousOperator + 1) + frac1 + expression.substring(nextOperator));
	}
	
	/*
	 * Finds the nearest operator (+, _, *, |) BEFORE the index given
	 * Pre: String, int
	 * Post: returns an int
	 */
	public static int findPreviousOperator(String expression, int index) {
		// find & return the index of the operator that comes before the index given
		for (int i = index - 1; i > 0; i--) {
			if (expression.charAt(i) == '+' || expression.charAt(i) == '_' || expression.charAt(i) == '*' || expression.charAt(i) == '|') {
				return(i);
			}
		}
		
		return(-1);
	}
	
	/*
	 * Finds the nearest operator AFTER the index given
	 * Pre: String, int
	 * Post: returns an int
	 */
	public static int findNextOperator(String expression, int index) {
		// find and return the index of the operator that comes after the index given
		for (int i = index + 1; i < expression.length(); i++) {
			
			if (expression.charAt(i) == '+' || expression.charAt(i) == '_' || expression.charAt(i) == '*' || expression.charAt(i) == '|') {
				return(i);
			}
		}
		
		return(expression.length());
	}
	
	/*
	 * Returns a string containing a valid expression or "invalid" if the expression is invalid
	 * Pre: String
	 * Post: returns a formatted String
	 */
	public static String checkValidExpression(String potential) {
		// declarations & initializations
		int nextOperator = -1;
		potential = potential.replaceAll(" ", ""); // remove spaces
		
		// checking validity
		for (int i = 0; i < potential.length(); i++) {
			// look for the next operator 
			nextOperator = findNextOperator(potential, i);
			
			// see if the term is valid
			if (checkValidFraction(potential.substring(i, nextOperator))[1] == 0) { // denominator = 0, meaning invalid fraction
				return("invalid");
			}
			
			i = nextOperator; // check next term in the next iteration
		} 
		
		// return invalid if the last character of the potential expression is an operator
		if (makeInt(potential.charAt(potential.length() - 1) + "") == -1) {
			return("invalid");
		}
		
		// return formatted (spaces removed) valid expression
		return(potential);
	}
	
	/*
	 * Checks if the given String contains a valid fraction (numerator/denominator). If so, then it returns a long array containing two elements: the numerator and the denominator. If not, then long array returned has the denominator equal to 0
	 * Pre: String
	 * Post: returns a long array
	 */
	public static long[] checkValidFraction(String potentialFrac) {
		// declarations & initialization
		String potentialN, potentialD;
		int slashIndex = -1;
		long[] frac = new long[2];
		
		potentialFrac = potentialFrac.replaceAll(" ", ""); // remove all spaces
		
		// see how many /s and where
		for (int i = 0; i < potentialFrac.length(); i++) {
			if (potentialFrac.charAt(i) == '/' && slashIndex == -1 && i + 1 != potentialFrac.length()) {
				slashIndex = i;
				frac[1] = 1;
			} else if (potentialFrac.charAt(i) == '/') { // string has more than 1 slash or the slash is at the end, making it an invalid fraction for our purposes
				frac[1] = 0;
				return(frac);
			}
		}
		
		// if there was no slash (potential integer)
		if (slashIndex == -1) {
			frac[1] = 1;
			if (makeInt(potentialFrac) != -1) {
				frac[0] = makeInt(potentialFrac);
			} else if (potentialFrac.length() > 0 && potentialFrac.charAt(0) == '-' && makeInt(potentialFrac.substring(1)) != -1) {
				frac[0] = -makeInt(potentialFrac.substring(1));
			} else {
				frac[1] = 0;
			}
			return(frac);
		}
		
		// initialize the potential numerator & denominator
		potentialN = potentialFrac.substring(0, slashIndex);
		potentialD = potentialFrac.substring(slashIndex + 1);
		
		// assigning the correct numerator to the long array
		if (makeInt(potentialN) != -1) {
			frac[0] = makeInt(potentialN);
		} else if (potentialN.length() > 0 && potentialN.charAt(0) == '-' && makeInt(potentialN.substring(1)) != -1) {
			frac[0] = -makeInt(potentialN.substring(1));
		} else {
			frac[1] = 0;
			return(frac); // means that the fraction is invalid since the denominator is 0
		}
		
		// assigning the correct denominator to the long array
		if (makeInt(potentialD) != -1 && makeInt(potentialD) != 0) { // make sure that not only is denominator an integer but it also isn't 0
			frac[1] = makeInt(potentialD);
		} else if (potentialD.length() > 0 && potentialD.charAt(0) == '-' && makeInt(potentialD.substring(1)) != -1 && makeInt(potentialD.substring(1)) != 0) {
			frac[1] = -makeInt(potentialD.substring(1));
		} else {
			frac[1] = 0;
			return(frac); // means that the fraction is invalid
		}

		return(frac);
		
		
	}
	
	/*
	 * Checks if the given String contains a valid mixed number (wholeNum numerator/denominator where numerator and denominator are positive). If so, then a long array containing three elements (wholeNum, numerator, denominator) is returned. Otherwise, the long array is returned with the denominator equal to 0.
	 * Pre: String
	 * Post: returns a long array
	 */
	public static long[] checkValidMixed(String potentialMixed) {
		// declarations & initializations
		long[] mixed = new long[3], frac = new long[2];
		int spaceStart, spaceEnd = -1, slashIndex;
		String potentialWhole, potentialFrac;
		
		// Check that there is a space section (separates whole vs. fraction) & it comes before /, if there are any
		spaceStart = potentialMixed.indexOf(" ");
		slashIndex = potentialMixed.indexOf("/");
		
		if (slashIndex != -1 && slashIndex < spaceStart) {
			return(mixed); // denominator will be 0 to signal invalid mixed
		} else if (spaceStart == -1) {
			potentialFrac = potentialMixed; // no space
		} else {
			// find spaceEnd
			for (int i = spaceStart; i < potentialMixed.length(); i++) {
				if (potentialMixed.charAt(i) != ' ') {
					spaceEnd = i - 1;
					i = potentialMixed.length(); // exit the loop once space section ends
				}
			}
			
			// initialize strings
			potentialWhole = potentialMixed.substring(0, spaceStart);
			potentialFrac = potentialMixed.substring(spaceEnd + 1);
			
			// convert to int
			// whole part
			if (makeInt(potentialWhole) != -1) {
				mixed[0] = makeInt(potentialWhole);
			} else if (potentialWhole.length() > 0 && potentialWhole.charAt(0) == '-' && makeInt(potentialWhole.substring(1)) != -1) {
				mixed[0] = -makeInt(potentialWhole.substring(1));
			} else {
				return(mixed); // not an int
			}
		}

		// fraction part
		// if the fraction part is negative instead of the whole number, make the user try again
		if (potentialFrac.indexOf("-") != -1 && slashIndex != -1) {
			return(mixed); // signals invalid input since denominator = 0
		}
		
		frac = checkValidFraction(potentialFrac);
		mixed[1] = frac[0];
		mixed[2] = frac[1]; // will indicate whether or not the fraction & mixed number were valid
		
		return(mixed);

	}
	
	/*
	 * Calculates the factorial of a given integer
	 * Pre: int
	 * Post: returns a long
	 */
	public static long calcFactorial(int num) {
		long factorial = 1;
		
		// calculate
		for (int i = num; i > 1; i--) {
			factorial *= i;
		}
		
		return(factorial);
	}
	
	/*
	 * Returns a string with the specified number of spaces
	 * Pre: int representing the number of spaces
	 * Post: returns a string containing spaces
	 */
	public static String getSpaces(int numSpaces) {
		// declare & initialize an empty string to be returned at the end
		String returnStr = "";
		
		// add spaces to the return string
		for (int i = 0; i < numSpaces; i++) {
			returnStr += " ";
		}
		
		return(returnStr);
	}
	
	/*
	 * Converts the given string into an integer
	 * Pre: String (number must be positive)
	 * Post: returns the integer, and -1 if the string is not a number
	 */
	public static int makeInt(String str) {
		// declaration and initialization
		int returnInt = -1;
		int multiplier = (int) Math.pow(10, str.length() - 1);
		boolean isAllInt = true;
		
		// check for int digits
		for (int i = 0; i < str.length() && isAllInt; i++) {
			if (str.charAt(i) < 48 || str.charAt(i) > 57) { // if the char is not in this ascii range, it is not a digit
				isAllInt = false;
			}
		}
		
		// calculate the int value
		if (isAllInt && str.length() > 0) {
			returnInt = 0;
			for (int i = 0; i < str.length(); i++, multiplier /= 10) {
				returnInt += (str.charAt(i) - 48) * multiplier;
			}
		}
		
		return(returnInt);
	}

	/*
	 * SAMAR CODE: Takes input of the valid fractions to be sorted from least to greatest
	 * Pre: user must have chosen option 8 from the main menu
	 * Post: returns a Fraction array
	 */
	public static Fraction[] inputFracArr(){
		long[]fraction;
		String arrSize;
		do {
			System.out.print("How many fractions would you like to sort?: ");
			arrSize = INPUT.nextLine();
		} while (makeInt(arrSize) < 1);

		Fraction [] arr = new Fraction [(int)Fraction.parseLong(arrSize)]; //creates a Fraction array of arrSize number of Fractions
		for(int i = 0; i<arr.length; i++){

			do {
				System.out.print("Enter fraction " + (i+1) + ": ");
				fraction = checkValidFraction(INPUT.nextLine());
			} while (fraction[1] == 0); // while invalid fraction (aka denominator = 0)
			
			arr[i] = new Fraction(fraction[0], fraction[1]);
		}	
		return arr;
	}
	
	/*
	 * SAMAR CODE: Sorts the Fractions in the array from least to greatest using bubble sort
	 * Pre: Fraction array
	 * Post: returns the fraction array
	 */
	public static Fraction[] sortFractions(Fraction[]arr){ //sorts a fraction array from least to greatest
		Fraction temp;;
        for (int i = 0; i<arr.length-1; i++) {
            for (int j = 0; j<arr.length-1-i; j++) {
                if(arr[j].calcDecimal() > arr[j+1].calcDecimal()) {
                    temp = new Fraction(arr[j].getNumerator(), arr[j].getDenominator());
                    arr[j] = arr[j+1];
				    arr[j+1] = temp;
                }
            }
        }
            return arr;
	}

	/*
	 * SAMAR CODE: Displays the elements in the sorted fraction array, each separated by a comma
	 * Pre: Fraction array
	 * Post: returns nothing but displays the elements to the user
	 */
	public static void outputFracArr(Fraction[]arr){
		System.out.print("Your sorted fractions: ");
		for(int i = 0; i<arr.length; i++){
			System.out.print(arr[i].toString());
			if(i<arr.length-1){
				System.out.print(", ");
			}
		}
		System.out.println();
	}

}

FractionMain v2 (without static final Scanner)