diff --git a/.gitignore b/.gitignore index de469bb3..dd8dd225 100644 --- a/.gitignore +++ b/.gitignore @@ -150,6 +150,6 @@ fabric.properties .classpath .settings - #maven build target -target/ \ No newline at end of file +target/ +/bin diff --git a/pom.xml b/pom.xml index e7cb4f6b..b49f26af 100644 --- a/pom.xml +++ b/pom.xml @@ -8,5 +8,13 @@ scientific_calculator 1.0-SNAPSHOT + + + junit + junit + 4.13 + test + + \ No newline at end of file diff --git a/src/main/java/com/zipcodewilmington/scientificcalculator/Application/MainApplication.java b/src/main/java/com/zipcodewilmington/scientificcalculator/Application/MainApplication.java new file mode 100644 index 00000000..2ba1abb4 --- /dev/null +++ b/src/main/java/com/zipcodewilmington/scientificcalculator/Application/MainApplication.java @@ -0,0 +1,22 @@ +package com.zipcodewilmington.scientificcalculator.Application; + +import com.zipcodewilmington.scientificcalculator.Calculator.Calculator; +import com.zipcodewilmington.scientificcalculator.Utilities.*; + +/** + * Created by leon on 2/9/18. + */ +public class MainApplication { + + public final static Calculator calc; + + public static void main(String[] args) { + + Util.println("Welcome to my calculator!"); + ConsoleCommands.fullPrompt(); + } + + static { + calc = new Calculator(); + } +} diff --git a/src/main/java/com/zipcodewilmington/scientificcalculator/Calculator/Calculator.java b/src/main/java/com/zipcodewilmington/scientificcalculator/Calculator/Calculator.java new file mode 100644 index 00000000..3ba30dda --- /dev/null +++ b/src/main/java/com/zipcodewilmington/scientificcalculator/Calculator/Calculator.java @@ -0,0 +1,301 @@ +package com.zipcodewilmington.scientificcalculator.Calculator; + +import com.zipcodewilmington.scientificcalculator.Utilities.Util; + +public class Calculator +{ + private float displayValue; + private float storedValue; + private boolean allowNegative; + private boolean isError; + private DisplayMode numMode; + private TrigDisplayMode trigMode; + private String display; + + public enum DisplayMode { + BINARY, + OCTAL, + DECIMAL, + HEXADECIMAL + } + + public enum TrigDisplayMode { + DEGREES, + RADIANS + } + + public Calculator() { + this(0, 0, true, DisplayMode.DECIMAL, TrigDisplayMode.DEGREES); + } + + public Calculator(int displayVal) { + this(displayVal, 0, true, DisplayMode.DECIMAL, TrigDisplayMode.DEGREES); + } + + public Calculator(int displayVal, int storedVal, boolean allowNegatives, DisplayMode numMode, TrigDisplayMode trigMode) { + this.displayValue = displayVal; + this.storedValue = storedVal; + this.allowNegative = allowNegatives; + this.isError = false; + this.display = ""; + this.trigMode = trigMode; + this.numMode = numMode; + } + + @SuppressWarnings("incomplete-switch") + public void updateDisplay() { + if (this.isError) { + this.display = "Err"; + return; + } + + float numToDisplay = this.displayValue; + switch (this.numMode) { + case BINARY: + this.display = "" + Integer.toBinaryString((int)numToDisplay); + return; + case HEXADECIMAL: + this.display = "" + Integer.toHexString((int)numToDisplay); + return; + case OCTAL: + this.display = "" + Integer.toOctalString((int)numToDisplay); + return; + } + + if (this.trigMode == TrigDisplayMode.RADIANS) { + this.display = "" + Math.toRadians(numToDisplay); + return; + } + + if (!this.allowNegative && numToDisplay < 0.0f) { + numToDisplay = 0.0f; + } + + this.display = "" + (float)numToDisplay; + } + + + public void clearDisplay() { + this.displayValue = 0; + this.isError = false; + this.allowNegative = true; + } + + public void clearMemory() { + this.storedValue = 0; + } + + public void totalReset() { + this.displayValue = 0; + this.storedValue = 0; + this.allowNegative = true; + this.isError = false; + this.numMode = DisplayMode.DECIMAL; + this.trigMode = TrigDisplayMode.DEGREES; + } + + + // MATH //////////////////////////////////////////////////////////////////////////////////////////////// + public void add(float val) { + this.displayValue += val; + } + + public void subtract(float val) { + this.displayValue -= val; + } + + public void mult(float val) { + this.displayValue *= val; + } + + public void div(float num) { + if (num == 0) { + this.isError = true; + updateDisplay(); + return; + } + this.displayValue /= num; + } + + public void sqRt() { + this.displayValue = Util.squareRoot(this.displayValue); + } + + public void square() { + this.displayValue = Util.square(this.displayValue); + } + + public void pow(float exponent) { + this.displayValue = (float) Math.pow(this.displayValue, exponent); + } + + public void inverse() { + if (this.displayValue == 0.0f) { + this.isError = true; + updateDisplay(); + return; + } + this.displayValue = 1.0f / this.displayValue; + } + + public void flipSign() { + this.displayValue = -this.displayValue; + } + + public void factorial() { + if (this.displayValue < 13) { + this.displayValue = Util.factorial((int) this.displayValue); + } + else { + this.displayValue = Float.MAX_VALUE; + } + } + + public void sin() { + this.displayValue = Util.sine(this.displayValue); + } + + public void cosine() { + this.displayValue = Util.cosine(this.displayValue); + } + + public void tangent() { + this.displayValue = Util.tangent(this.displayValue); + } + + public void invSin() { + this.displayValue = Util.invSine(this.displayValue); + } + + public void invCosine() { + this.displayValue = Util.invCosine(this.displayValue); + } + + public void invTangent() { + this.displayValue = Util.invTangent(this.displayValue); + } + + public void log() { + this.displayValue = Util.log(this.displayValue); + } + + public void invNatLog() { + this.displayValue = Util.inverseNaturalLog(this.displayValue); + } + // END MATH /////////////////////////////////////////////////////////////////////////////////////////////// + + + // GETTERS //////////////////////////////////////////////////////////////////////////////////////////////// + public String getDisplay() { + updateDisplay(); + return display; + } + + public float getDisplayVal() { + if (this.displayValue > 0.0f || this.allowNegative) { + return this.displayValue; + } + else { + return 0.0f; + } + } + + public float getStoredVal() { + return this.storedValue; + } + + public boolean isInErrorMode() { + return this.isError; + } + + public boolean allowingNegative() { + return this.allowNegative; + } + + public DisplayMode getNumMode() { + return this.numMode; + } + + public String getNumModeStr() { + switch (this.numMode) { + case BINARY: + return "Binary"; + case DECIMAL: + return "Decimal"; + case HEXADECIMAL: + return "Hexadecimal"; + case OCTAL: + return "Octal"; + default: + return "Uh.OH."; + } + } + + public TrigDisplayMode getTrigMode() { + return this.trigMode; + } + + public String getTrigModeStr() { + switch (this.trigMode) { + case DEGREES: + return "Degrees"; + case RADIANS: + return "Radians"; + default: + return "Uh.OH."; + } + } + // END GETTERS /////////////////////////////////////////////////////////////////////////////////////////// + + + // SETTERS /////////////////////////////////////////////////////////////////////////////////////////////// + public void setDisplayVal(int val) { + this.displayValue = val; + } + + public void setAllowNegative(boolean allow) { + this.allowNegative = allow; + } + + public void setDisplay(String newDisp) { + this.display = newDisp; + } + + public void setError(boolean isError) { + this.isError = isError; + } + + public void setStoredVal(int newStoredVal) { + this.storedValue = newStoredVal; + } + + public void incStoredVal(float inc) { + this.storedValue += inc; + } + + public void setDisplayMode(DisplayMode newMode) { + this.numMode = newMode; + updateDisplay(); + } + + public void setTrigMode(TrigDisplayMode newMode) { + this.trigMode = newMode; + updateDisplay(); + } + + public void toggleAllowNegatives() { + this.allowNegative = !this.allowNegative; + } + + public void toggleTrigMode() { + if (this.trigMode.equals(TrigDisplayMode.DEGREES)) { + this.trigMode = TrigDisplayMode.RADIANS; + } + else { + this.trigMode = TrigDisplayMode.DEGREES; + } + } + // END SETTERS ///////////////////////////////////////////////////////////////////////////////////////// + + +} diff --git a/src/main/java/com/zipcodewilmington/scientificcalculator/Console.java b/src/main/java/com/zipcodewilmington/scientificcalculator/Console.java deleted file mode 100644 index 83f0e97f..00000000 --- a/src/main/java/com/zipcodewilmington/scientificcalculator/Console.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zipcodewilmington.scientificcalculator; - -import java.util.Scanner; - -/** - * Created by leon on 2/9/18. - */ -public class Console { - - public static void print(String output, Object... args) { - System.out.printf(output, args); - } - - public static void println(String output, Object... args) { - print(output + "\n", args); - } - - public static String getStringInput(String prompt) { - Scanner scanner = new Scanner(System.in); - println(prompt); - String userInput = scanner.nextLine(); - return userInput; - } - - public static Integer getIntegerInput(String prompt) { - return null; - } - - public static Double getDoubleInput(String prompt) { - return null; - } -} diff --git a/src/main/java/com/zipcodewilmington/scientificcalculator/MainApplication.java b/src/main/java/com/zipcodewilmington/scientificcalculator/MainApplication.java deleted file mode 100644 index 5f421325..00000000 --- a/src/main/java/com/zipcodewilmington/scientificcalculator/MainApplication.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.zipcodewilmington.scientificcalculator; - -/** - * Created by leon on 2/9/18. - */ -public class MainApplication { - public static void main(String[] args) { - Console.println("Welcome to my calculator!"); - String s = Console.getStringInput("Enter a string"); - Integer i = Console.getIntegerInput("Enter an integer"); - Double d = Console.getDoubleInput("Enter a double."); - - Console.println("The user input %s as a string", s); - Console.println("The user input %s as a integer", i); - Console.println("The user input %s as a d", d); - } -} diff --git a/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/ConsoleCommands.java b/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/ConsoleCommands.java new file mode 100644 index 00000000..9191b8cd --- /dev/null +++ b/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/ConsoleCommands.java @@ -0,0 +1,251 @@ +package com.zipcodewilmington.scientificcalculator.Utilities; + +import java.util.*; +import java.util.Map.Entry; + +import com.zipcodewilmington.scientificcalculator.Application.MainApplication; + +public class ConsoleCommands +{ + private static Map commandMap = new HashMap(); + + public enum Command { + CLEAR, + HELP, + INFO, + MATH, + RECALL, + RESET, + STORE, + SWITCH_DISP, + SWITCH_DISP_TRIG, + TOGGLE_NEGATIVE, + BAD_COMMAND, + EXIT, + DISPLAY + } + + public enum MenuType { + CONSOLE, + MATH, + DISPLAY, + ERROR + } + + public static boolean commandExists(String cmd) { + if (commandMap != null && commandMap.containsKey(cmd.toLowerCase())) { + return true; + } + return false; + } + + public static void fullPrompt() { + + // If in error mode, we are forcing a clear of the display + if (MainApplication.calc.isInErrorMode()) { + errorPrompt(); + return; + } + + // Otherwise, get some input for a console command + String input = Util.getStringInput("Enter a CONSOLE COMMAND to continue: "); + + // Split the input into an array of seperate strings (this splits by spaces) + String[] splited = input.split("\\s+"); + + // Save the strings to an arraylist so we can pass it around more easily + ArrayList argus = new ArrayList<>(); + for (String s : splited) { + argus.add(s); + } + + // Loop to check for input + // If input is not entered, this waits for more + while (argus.size() < 1) { + argus = prompt(MenuType.CONSOLE); + } + + // Once we have at least 1 string of input, run a command + runCommand(argus); + } + + public static void runCommand(ArrayList args) { + if (commandMap != null && commandMap.containsKey(args.get(0).toLowerCase())) { + run(commandMap.get(args.get(0).toLowerCase()), args); + } + else if (MathCommands.commandExists(args.get(0).toLowerCase())) { + MathCommands.runCommand(args); + } + else if (DisplayModeCommands.commandExists(args.get(0).toLowerCase())) { + DisplayModeCommands.runCommand(args); + } + else if (args.size() > 1 && args.get(0).toLowerCase().equals("best") && args.get(1).toLowerCase().equals("programmer")) { + Util.prln("Nobles"); + fullPrompt(); + } + else { + run(Command.BAD_COMMAND, args); + } + } + + public static ArrayList prompt(MenuType type) { + String inptCmd; + switch (type) { + case CONSOLE: + inptCmd = "Enter a CONSOLE COMMAND to continue: "; + break; + case DISPLAY: + inptCmd = "Enter a DISPLAY MODE to continue: "; + break; + case MATH: + inptCmd = "Enter a MATH COMMAND to continue: "; + break; + case ERROR: + inptCmd = "Error Mode - Please Clear the Display: "; + default: + inptCmd = "Enter a CONSOLE COMMAND to continue: "; + break; + } + String input = Util.getStringInput(inptCmd); + String[] splited = input.split("\\s+"); + ArrayList argus = new ArrayList<>(); + for (String s : splited) { + argus.add(s); + } + return argus; + } + + public static void errorPrompt() { + String input = Util.getStringInput("Error Mode - Please Clear the Display: "); + while (!input.toLowerCase().equals("clear")) { + input = Util.getStringInput("Error Mode - Please Clear the Display: "); + } + run(Command.CLEAR, null); + } + + public static void promptMath() { + if (MainApplication.calc.isInErrorMode()) { + errorPrompt(); + return; + } + String input = Util.getStringInput("Enter a MATH COMMAND to continue: "); + String[] splited = input.split("\\s+"); + ArrayList argus = new ArrayList<>(); + for (String s : splited) { + argus.add(s); + } + while (argus.size() < 1) { + argus = prompt(MenuType.MATH); + } + runCommand(argus); + } + + public static void promptDispSwitch() { + if (MainApplication.calc.isInErrorMode()) { + errorPrompt(); + return; + } + String input = Util.getStringInput("Enter a DISPLAY MODE to continue: "); + String[] splited = input.split("\\s+"); + ArrayList argus = new ArrayList<>(); + for (String s : splited) { + argus.add(s); + } + while (argus.size() < 1) { + argus = prompt(MenuType.DISPLAY); + } + runCommand(argus); + } + + public static void run(Command cmd, ArrayList args) { + switch (cmd) + { + case BAD_COMMAND: + Util.prln("Bad command! Please enter a valid command, or enter 'Help' to view a list of all commands."); + fullPrompt(); + return; + case CLEAR: + MainApplication.calc.clearDisplay(); + run(Command.DISPLAY, null); + return; + case HELP: + Util.prln("Printing a list of all available commands in this menu: "); + ArrayList uniques = new ArrayList<>(); + for (Entry i : commandMap.entrySet()) { + uniques.add(i.getKey().toUpperCase()); + } + Collections.sort(uniques); + for (String s : uniques) { + Util.prln(s); + } + fullPrompt(); + return; + case INFO: + Util.prln("This calculator was made by Aarti, Adam, and Matt! We hope you find it useful."); + fullPrompt(); + return; + case RECALL: + Util.prln("Stored Memory Value: " + MainApplication.calc.getStoredVal()); + fullPrompt(); + return; + case RESET: + MainApplication.calc.totalReset(); + Util.prln("Reset all calculator values! Display mode is now DECIMAL/DEGREES."); + run(Command.DISPLAY, null); + return; + case STORE: + MainApplication.calc.incStoredVal(MainApplication.calc.getDisplayVal()); + Util.prln("Stored " + MainApplication.calc.getDisplayVal() + " in memory"); + fullPrompt(); + return; + case SWITCH_DISP_TRIG: + MainApplication.calc.toggleTrigMode(); + Util.prln("Trig Mode switched to " + MainApplication.calc.getTrigModeStr()); + fullPrompt(); + return; + case TOGGLE_NEGATIVE: + MainApplication.calc.toggleAllowNegatives(); + if (MainApplication.calc.allowingNegative()) { + Util.prln("Calculator is allowing negative numbers again."); + } + else { + Util.prln("Calculator will no longer show negative numbers."); + } + fullPrompt(); + return; + case EXIT: + System.exit(0); + return; + case DISPLAY: + Util.prln("Value: " + MainApplication.calc.getDisplay()); + fullPrompt(); + return; + case MATH: + MathCommands.fullPrompt(); + return; + case SWITCH_DISP: + DisplayModeCommands.fullPrompt(); + return; + default: + run(Command.BAD_COMMAND, null); + return; + } + } + + static { + commandMap = new HashMap<>(); + commandMap.put("clear", Command.CLEAR); + commandMap.put("help", Command.HELP); + commandMap.put("info", Command.INFO); + commandMap.put("math", Command.MATH); + commandMap.put("recall", Command.RECALL); + commandMap.put("reset", Command.RESET); + commandMap.put("store", Command.STORE); + commandMap.put("switchmode", Command.SWITCH_DISP); + commandMap.put("switchtrig", Command.SWITCH_DISP_TRIG); + commandMap.put("togglenegative", Command.TOGGLE_NEGATIVE); + commandMap.put("display", Command.DISPLAY); + commandMap.put("exit", Command.EXIT); + } + +} diff --git a/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/DisplayModeCommands.java b/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/DisplayModeCommands.java new file mode 100644 index 00000000..328eb65f --- /dev/null +++ b/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/DisplayModeCommands.java @@ -0,0 +1,122 @@ +package com.zipcodewilmington.scientificcalculator.Utilities; + +import java.util.*; +import java.util.Map.Entry; + +import com.zipcodewilmington.scientificcalculator.Application.*; +import com.zipcodewilmington.scientificcalculator.Calculator.Calculator.DisplayMode; +import com.zipcodewilmington.scientificcalculator.Utilities.ConsoleCommands.Command; + +public class DisplayModeCommands +{ + private static Map commandMap = new HashMap(); + + public enum Mode { + BINARY, + OCTAL, + DECIMAL, + HEXADECIMAL, + RETURN, + HELP + } + + public static boolean commandExists(String cmd) { + if (commandMap != null && commandMap.containsKey(cmd.toLowerCase())) { + return true; + } + return false; + } + + public static void runCommand(ArrayList args) { + if (commandMap != null && commandMap.containsKey(args.get(0).toLowerCase())) { + run(commandMap.get(args.get(0).toLowerCase())); + } + else if (MathCommands.commandExists(args.get(0).toLowerCase())) { + MathCommands.runCommand(args); + } + else if (DisplayModeCommands.commandExists(args.get(0).toLowerCase())) { + DisplayModeCommands.runCommand(args); + } + else { + run(Mode.RETURN); + } + } + + public static ArrayList prompt() { + String input = Util.getStringInput("Enter a DISPLAY MODE to continue: "); + String[] splited = input.split("\\s+"); + ArrayList argus = new ArrayList<>(); + for (String s : splited) { + argus.add(s); + } + return argus; + } + + public static void fullPrompt() { + String input = Util.getStringInput("Enter a DISPLAY MODE to continue: "); + String[] splited = input.split("\\s+"); + ArrayList argus = new ArrayList<>(); + for (String s : splited) { + argus.add(s); + } + while (argus.size() < 1) { + argus = DisplayModeCommands.prompt(); + } + DisplayModeCommands.runCommand(argus); + } + + public static void run(Mode cmd) { + switch (cmd) + { + case BINARY: + MainApplication.calc.setDisplayMode(DisplayMode.BINARY); + Util.prln("Calculator switched to BINARY mode."); + ConsoleCommands.run(Command.DISPLAY, null); + return; + case DECIMAL: + MainApplication.calc.setDisplayMode(DisplayMode.DECIMAL); + Util.prln("Calculator switched to DECIMAL mode."); + ConsoleCommands.run(Command.DISPLAY, null); + return; + case HEXADECIMAL: + MainApplication.calc.setDisplayMode(DisplayMode.HEXADECIMAL); + Util.prln("Calculator switched to HEXADECIMAL mode."); + ConsoleCommands.run(Command.DISPLAY, null); + return; + case OCTAL: + MainApplication.calc.setDisplayMode(DisplayMode.OCTAL); + Util.prln("Calculator switched to OCTAL mode."); + ConsoleCommands.run(Command.DISPLAY, null); + return; + case RETURN: + ConsoleCommands.fullPrompt(); + return; + case HELP: + Util.prln("Printing a list of all available commands in this menu: "); + ArrayList uniques = new ArrayList<>(); + for (Entry i : commandMap.entrySet()) { + uniques.add(i.getKey().toUpperCase()); + } + Collections.sort(uniques); + for (String s : uniques) { + Util.prln(s); + } + fullPrompt(); + return; + default: + fullPrompt(); + return; + } + } + + static { + commandMap = new HashMap<>(); + commandMap.put("binary", Mode.BINARY); + commandMap.put("octal", Mode.OCTAL); + commandMap.put("decimal", Mode.DECIMAL); + commandMap.put("hexadecimal", Mode.HEXADECIMAL); + commandMap.put("return", Mode.RETURN); + commandMap.put("help", Mode.HELP); + } + +} diff --git a/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/MathCommands.java b/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/MathCommands.java new file mode 100644 index 00000000..2ed4c3f6 --- /dev/null +++ b/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/MathCommands.java @@ -0,0 +1,415 @@ +package com.zipcodewilmington.scientificcalculator.Utilities; + +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.ThreadLocalRandom; + +import com.zipcodewilmington.scientificcalculator.Application.MainApplication; + +public class MathCommands +{ + private static Map commandMap = new HashMap(); + + public enum Command { + ADD, + SUBTRACT, + DIVIDE, + MULTIPLY, + SQRT, + SQUARE, + INVERSE, + VAR_EXP, + FLIP_SIGN, + HELP, + RETURN, + FACTORIAL, + SINE, + COSINE, + TANGENT, + INV_SINE, + INV_COSINE, + INV_TANGENT, + LOG, + INV_LOG, + NATURAL_LOG, + INV_NAT_LOG, + RANDOM_NUM, + CLEAR, + TOGGLE_NEGATIVE, + DISPLAY, + EXIT, + RANDOM_FLOAT, + EXPRESSION + } + + public static boolean commandExists(String cmd) { + if (commandMap != null && commandMap.containsKey(cmd.toLowerCase())) { + return true; + } + return false; + } + + public static void runCommand(ArrayList args) { + if (commandMap != null && commandMap.containsKey(args.get(0).toLowerCase())) { + run(commandMap.get(args.get(0).toLowerCase()), args); + } + else if (ConsoleCommands.commandExists(args.get(0).toLowerCase())) { + ConsoleCommands.runCommand(args); + } + else if (DisplayModeCommands.commandExists(args.get(0).toLowerCase())) { + DisplayModeCommands.runCommand(args); + } + else { + run(Command.RETURN, args); + } + } + + public static ArrayList prompt() { + String input = Util.getStringInput("Enter a MATH COMMAND to continue: "); + String[] splited = input.split("\\s+"); + ArrayList argus = new ArrayList<>(); + for (String s : splited) { + argus.add(s); + } + return argus; + } + + public static void fullPrompt() { + if (MainApplication.calc.isInErrorMode()) { + ConsoleCommands.errorPrompt(); + return; + } + String input = Util.getStringInput("Enter a MATH COMMAND to continue: "); + String[] splited = input.split("\\s+"); + ArrayList argus = new ArrayList<>(); + for (String s : splited) { + argus.add(s); + } + while (argus.size() < 1) { + argus = MathCommands.prompt(); + } + MathCommands.runCommand(argus); + } + + + private static void reprompt() { + Util.prln("Value: " + MainApplication.calc.getDisplay()); + fullPrompt(); + return; + } + + public static void run(Command cmd, ArrayList args) { + switch (cmd) { + case ADD: + if (args.size() > 1) { + float sum = 0; + for (int i = 1; i < args.size(); i++) { + try { + sum += Float.parseFloat(args.get(i)); + } catch (NumberFormatException e) {} + } + MainApplication.calc.add(sum); + reprompt(); + return; + } + else { + Util.prln("Not enough arguments!"); + } + fullPrompt(); + return; + case COSINE: + MainApplication.calc.cosine(); + reprompt(); + return; + case DIVIDE: + if (args.size() > 1) { + for (int i = 1; i < args.size(); i++) { + try { + float denom = Float.parseFloat(args.get(i)); + MainApplication.calc.div(denom); + } catch (NumberFormatException e) { } + } + reprompt(); + return; + } + else { + Util.prln("Not enough arguments!"); + } + fullPrompt(); + return; + case FACTORIAL: + MainApplication.calc.factorial(); + reprompt(); + return; + case FLIP_SIGN: + MainApplication.calc.flipSign(); + reprompt(); + return; + case HELP: + Util.prln("Printing a list of all available commands in this menu: "); + ArrayList uniques = new ArrayList<>(); + for (Entry i : commandMap.entrySet()) { + uniques.add(i.getKey().toUpperCase()); + } + Collections.sort(uniques); + for (String s : uniques) { + Util.prln(s); + } + fullPrompt(); + return; + case INVERSE: + MainApplication.calc.inverse(); + reprompt(); + break; + case INV_COSINE: + MainApplication.calc.invCosine(); + reprompt(); + return; + case INV_NAT_LOG: + MainApplication.calc.invNatLog(); + reprompt(); + return; + case INV_SINE: + MainApplication.calc.invSin(); + reprompt(); + return; + case INV_TANGENT: + MainApplication.calc.invTangent(); + reprompt(); + return; + case LOG: + MainApplication.calc.log(); + reprompt(); + return; + case MULTIPLY: + if (args.size() > 1) { + for (int i = 1; i < args.size(); i++) { + try { + MainApplication.calc.mult(Float.parseFloat(args.get(i))); + } catch (NumberFormatException e) { } + } + reprompt(); + return; + } + else { + Util.prln("Not enough arguments!"); + } + fullPrompt(); + return; + case RANDOM_NUM: + if (args.size() > 2 && args.size() < 4) { + handleRandomNum(args, new ArrayList()); + } + else if (args.size() > 2) { + ArrayList excluded = new ArrayList<>(); + for (int i = 3; i < args.size(); i++) { + try { + excluded.add(Integer.parseInt(args.get(i))); + } catch (NumberFormatException e) { Util.prln("Expects extra numbers as additional arguments. Numbers after the initial bound arguments will be excluded from random generation."); } + } + handleRandomNum(args, excluded); + } + else { + Util.prln("Wrong amount of arguments! Expecting at least 2 numbers - lower and upper bound (inclusive). \n You may also include extra numbers to exclude from random generation."); + } + fullPrompt(); + return; + case RANDOM_FLOAT: + float rand = ThreadLocalRandom.current().nextFloat(); + Util.prln("Randomly generated float (0-1): " + rand); + fullPrompt(); + return; + case RETURN: + ConsoleCommands.fullPrompt(); + return; + case SINE: + MainApplication.calc.sin(); + reprompt(); + return; + case SQRT: + MainApplication.calc.sqRt(); + reprompt(); + break; + case SQUARE: + MainApplication.calc.square(); + reprompt(); + break; + case SUBTRACT: + if (args.size() > 1) { + for (int i = 1; i < args.size(); i++) { + try { + MainApplication.calc.subtract(Float.parseFloat(args.get(i))); + } catch (NumberFormatException e) { } + } + reprompt(); + return; + } + else { + Util.prln("Not enough arguments!"); + } + fullPrompt(); + return; + case TANGENT: + MainApplication.calc.tangent(); + reprompt(); + return; + case VAR_EXP: + if (args.size() > 1) { + for (int i = 1; i < args.size(); i++) { + try { + MainApplication.calc.pow(Float.parseFloat(args.get(i))); + } catch (NumberFormatException e) { } + } + reprompt(); + return; + } + else { + Util.prln("Not enough arguments!"); + } + fullPrompt(); + return; + case CLEAR: + MainApplication.calc.clearDisplay(); + reprompt(); + return; + case TOGGLE_NEGATIVE: + MainApplication.calc.toggleAllowNegatives(); + if (MainApplication.calc.allowingNegative()) { + Util.prln("Calculator is allowing negative numbers again."); + } + else { + Util.prln("Calculator will no longer show negative numbers."); + } + reprompt(); + return; + case DISPLAY: + Util.prln("Value: " + MainApplication.calc.getDisplay()); + fullPrompt(); + return; + case INV_LOG: + //TODO + case NATURAL_LOG: + //TODO + case EXIT: + System.exit(0); + case EXPRESSION: + if (args.size() > 1) { + args.remove(0); + } + try { + Double result = handleExpression(args); + System.out.println("Result: " + result); + } catch (NumberFormatException e) { System.out.println("Improper input. Expected an expression."); } + fullPrompt(); + return; + default: + fullPrompt(); + return; + } + } + + // Copied from my boy Dijkstra + public static Double handleExpression(ArrayList args) { + Stack ops = new Stack(); Stack vals = new Stack(); + for (int i = 0; i < args.size();) { + String s = args.remove(i); + if (s.equals("(")) {} + else if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/") || s.equals("sqrt")) { ops.push(s); } + else if (s.equals(")")) { + String op = ops.pop(); double v = vals.pop(); + if (op.equals("+")) { v = vals.pop() + v; } + else if (op.equals("-")) { v = vals.pop() - v; } + else if (op.equals("*")) { v = vals.pop() * v; } + else if (op.equals("/")) { v = vals.pop() / v; } + else if (op.equals("sqrt")) { v = Math.sqrt(v); } + vals.push(v); + } else vals.push(Double.parseDouble(s)); + } return vals.pop(); + } + + private static void handleRandomNum(ArrayList args, ArrayList excludedNums) { + int lower; + int higher; + int timeOut = 1000; + boolean generated = false; + try { + lower = Integer.parseInt(args.get(1)); + try { + higher = Integer.parseInt(args.get(2)); + generated = true; + } catch (NumberFormatException e) { + Util.prln("Only integers accepted as arguments."); + lower = 1; + higher = 100; + generated = false; + } + } catch (NumberFormatException e) { + Util.prln("Only integers accepted as arguments."); + lower = 1; + higher = 100; + generated = false; + } + if (generated) { + int rand = ThreadLocalRandom.current().nextInt(lower, higher + 1); + while (excludedNums.contains(rand) && timeOut > 0) { + rand = ThreadLocalRandom.current().nextInt(lower, higher + 1); + timeOut--; + } + Util.prln("Randomly generated number (" + lower + "-" + higher + "): " + rand); + fullPrompt(); + } + else { + int rand = ThreadLocalRandom.current().nextInt(lower, higher + 1); + while (excludedNums.contains(rand) && timeOut > 0) { + rand = ThreadLocalRandom.current().nextInt(lower, higher + 1); + timeOut--; + } + Util.prln("Randomly generated number (1-100)[default]: " + rand); + fullPrompt(); + } + } + + static { + commandMap = new HashMap<>(); + commandMap.put("sin", Command.SINE); + commandMap.put("cos", Command.COSINE); + commandMap.put("tan", Command.TANGENT); + commandMap.put("arcsin", Command.INV_SINE); + commandMap.put("arccos", Command.INV_COSINE); + commandMap.put("arctan", Command.INV_TANGENT); + commandMap.put("log", Command.LOG); + commandMap.put("invnatlog", Command.INV_NAT_LOG); + commandMap.put("add", Command.ADD); + commandMap.put("subtract", Command.SUBTRACT); + commandMap.put("minus", Command.SUBTRACT); + commandMap.put("mult", Command.MULTIPLY); + commandMap.put("multiply", Command.MULTIPLY); + commandMap.put("divide", Command.DIVIDE); + commandMap.put("sqrt", Command.SQRT); + commandMap.put("square", Command.SQUARE); + commandMap.put("inverse", Command.INVERSE); + commandMap.put("varexp", Command.VAR_EXP); + commandMap.put("power", Command.VAR_EXP); + commandMap.put("pow", Command.VAR_EXP); + commandMap.put("exponent", Command.VAR_EXP); + commandMap.put("exp", Command.VAR_EXP); + commandMap.put("signflip", Command.FLIP_SIGN); + commandMap.put("flipsign", Command.FLIP_SIGN); + commandMap.put("help", Command.HELP); + commandMap.put("return", Command.RETURN); + commandMap.put("factorial", Command.FACTORIAL); + commandMap.put("random", Command.RANDOM_NUM); + commandMap.put("clear", Command.CLEAR); + commandMap.put("togglenegative", Command.TOGGLE_NEGATIVE); + commandMap.put("display", Command.DISPLAY); + commandMap.put("exit", Command.EXIT); + commandMap.put("randomfloat", Command.RANDOM_FLOAT); + commandMap.put("expression", Command.EXPRESSION); + + + // TODO + //commandMap.put("invlog", Command.INV_LOG); + //commandMap.put("naturallog", Command.NATURAL_LOG); + } + +} diff --git a/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/Util.java b/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/Util.java new file mode 100644 index 00000000..8f40ad29 --- /dev/null +++ b/src/main/java/com/zipcodewilmington/scientificcalculator/Utilities/Util.java @@ -0,0 +1,110 @@ +package com.zipcodewilmington.scientificcalculator.Utilities; + +import java.util.Scanner; + +@SuppressWarnings("resource") +public class Util +{ + public static float square(float num) { + return num * num; + } + + public static float squareRoot(float num) { + return (float) Math.sqrt(num); + } + + public static int squareRoot(int num) { + return (int)squareRoot((float)num); + } + + public static float sine(float num) { + return (float) Math.sin(num); + } + + public static float cosine(float num) { + return (float) Math.cos(num); + } + + public static float tangent(float num) { + return (float) Math.tan(num); + } + + public static float invSine(float num) { + return (float) Math.asin(num); + } + + public static float invCosine(float num) { + return (float) Math.acos(num); + } + + public static float invTangent(float num) { + return (float) Math.atan(num); + } + + public static float toRadians(float num) { + return (float) Math.toRadians(num); + } + + public static float log(float num) { + return (float) Math.log(num); + } + + public static float inverseNaturalLog(float num) { + return (float) (Math.expm1(num) + 1); + } + + public static int factorial(int n) + { + return (n == 1 || n == 0) ? 1 : n * factorial(n - 1); + } + + public static Float getFloatInput(String prompt) { + Scanner scanner = new Scanner(System.in); + Util.println(prompt); + String userInput = scanner.nextLine(); + try { + return Float.parseFloat(userInput); + } catch (NumberFormatException e) { e.printStackTrace(); } + return 0.0f; + } + + + public static Double getDoubleInput(String prompt) { + Scanner scanner = new Scanner(System.in); + Util.println(prompt); + String userInput = scanner.nextLine(); + try { + return Double.parseDouble(userInput); + } catch (NumberFormatException e) { e.printStackTrace(); } + return 0.0; + } + + public static Integer getIntegerInput(String prompt) { + Scanner scanner = new Scanner(System.in); + Util.println(prompt); + String userInput = scanner.nextLine(); + try { + return Integer.parseInt(userInput); + } catch (NumberFormatException e) { e.printStackTrace(); } + return 0; + } + + public static String getStringInput(String prompt) { + Scanner scanner = new Scanner(System.in); + Util.println(prompt); + String userInput = scanner.nextLine(); + return userInput; + } + + public static void print(String output, Object... args) { + System.out.printf(output, args); + } + + public static void println(String output, Object... args) { + print(output + "\n", args); + } + + public static void prln(String print) { + System.out.println(print); + } +} diff --git a/src/test/java/com/zipcodewilmington/scientific_calculator/Calculator/TestCalculator.java b/src/test/java/com/zipcodewilmington/scientific_calculator/Calculator/TestCalculator.java new file mode 100644 index 00000000..484910ea --- /dev/null +++ b/src/test/java/com/zipcodewilmington/scientific_calculator/Calculator/TestCalculator.java @@ -0,0 +1,276 @@ +package com.zipcodewilmington.scientific_calculator.Calculator; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.zipcodewilmington.scientificcalculator.Calculator.Calculator; +import com.zipcodewilmington.scientificcalculator.Calculator.Calculator.*; + +public class TestCalculator +{ + @Test + public void TestCalculatorInit() { + Calculator calc = new Calculator(); + assertEquals(0, (int)calc.getDisplayVal()); + } + + @Test + public void TestClearDisplay() { + Calculator calc = new Calculator(); + calc.add(10); + assertEquals(10, (int)calc.getDisplayVal()); + + calc.clearDisplay(); + assertEquals(0, (int)calc.getDisplayVal()); + } + + @Test + public void TestStoringMemory() { + Calculator calc = new Calculator(); + calc.setStoredVal(10); + assertEquals(10, (int)calc.getStoredVal()); + calc.clearMemory(); + assertEquals(0, (int)calc.getStoredVal()); + } + + @Test + public void TestReset() { + Calculator calc = new Calculator(); + calc.add(15); + calc.setStoredVal(10); + assertEquals(10, (int)calc.getStoredVal()); + assertEquals(15, (int)calc.getDisplayVal()); + calc.totalReset(); + assertEquals(0, (int)calc.getDisplayVal()); + assertEquals(0, (int)calc.getStoredVal()); + assertEquals(DisplayMode.DECIMAL, calc.getNumMode()); + assertEquals(TrigDisplayMode.DEGREES, calc.getTrigMode()); + } + + @Test + public void TestAllowNegatives() { + Calculator calc = new Calculator(); + calc.toggleAllowNegatives(); + assertEquals(false, calc.allowingNegative()); + calc.add(-100); + assertEquals("0.0", calc.getDisplay()); + assertEquals(0, (int)calc.getDisplayVal()); + calc.toggleAllowNegatives(); + assertEquals("-100.0", calc.getDisplay()); + assertEquals(-100, (int)calc.getDisplayVal()); + } + + @Test + public void TestTrigModes() { + Calculator calc = new Calculator(); + calc.add(180); + calc.setTrigMode(TrigDisplayMode.RADIANS); + assertEquals("3.141592653589793", calc.getDisplay()); + calc.setDisplayVal(27); + calc.setTrigMode(TrigDisplayMode.RADIANS); + assertEquals("0.47123889803846897", calc.getDisplay()); + calc.setTrigMode(TrigDisplayMode.DEGREES); + assertEquals(27.0, calc.getDisplayVal(), 0.001); + } + + @Test + public void TestSqrt() { + Calculator calc = new Calculator(); + calc.add(9); + calc.sqRt(); + assertEquals(3, (int)calc.getDisplayVal()); + } + + @Test + public void TestSquare() { + Calculator calc = new Calculator(); + calc.add(9); + calc.square(); + assertEquals(81, (int)calc.getDisplayVal()); + } + + @Test + public void TestPow() { + Calculator calc = new Calculator(); + calc.add(3); + calc.pow(3); + assertEquals(27, (int)calc.getDisplayVal()); + } + + @Test + public void TestFlipSign() { + Calculator calc = new Calculator(); + calc.add(5); + calc.flipSign(); + assertEquals(-5, (int)calc.getDisplayVal()); + } + + @Test + public void TestFactorial() { + Calculator calc = new Calculator(); + calc.setDisplayVal(1); + calc.factorial(); + assertEquals(1, (int)calc.getDisplayVal()); + + calc.setDisplayVal(2); + calc.factorial(); + assertEquals(2, (int)calc.getDisplayVal()); + + calc.setDisplayVal(3); + calc.factorial(); + assertEquals(6, (int)calc.getDisplayVal()); + + calc.setDisplayVal(4); + calc.factorial(); + assertEquals(24, (int)calc.getDisplayVal()); + + calc.setDisplayVal(5); + calc.factorial(); + assertEquals(120, (int)calc.getDisplayVal()); + + calc.setDisplayVal(6); + calc.factorial(); + assertEquals(720, (int)calc.getDisplayVal()); + + calc.setDisplayVal(7); + calc.factorial(); + assertEquals(5040, (int)calc.getDisplayVal()); + + calc.setDisplayVal(8); + calc.factorial(); + assertEquals(40320, (int)calc.getDisplayVal()); + + calc.setDisplayVal(9); + calc.factorial(); + assertEquals(362880, (int)calc.getDisplayVal()); + + calc.setDisplayVal(10); + calc.factorial(); + assertEquals(3628800, (int)calc.getDisplayVal()); + + calc.setDisplayVal(11); + calc.factorial(); + assertEquals(39916800, (int)calc.getDisplayVal()); + + calc.setDisplayVal(12); + calc.factorial(); + assertEquals(479001600, (int)calc.getDisplayVal()); + + calc.setDisplayVal(13); + calc.factorial(); + assertEquals((int)Float.MAX_VALUE, (int)calc.getDisplayVal()); + + calc.setDisplayVal(14); + calc.factorial(); + assertEquals((int)Float.MAX_VALUE, (int)calc.getDisplayVal()); + + calc.setDisplayVal(15); + calc.factorial(); + assertEquals((int)Float.MAX_VALUE, (int)calc.getDisplayVal()); + + calc.setDisplayVal(9999); + calc.factorial(); + assertEquals((int)Float.MAX_VALUE, (int)calc.getDisplayVal()); + } + + @Test + public void TestSine() { + Calculator calc = new Calculator(); + calc.add(10.0f); + calc.sin(); + assertEquals(-0.544021, calc.getDisplayVal(), 0.001); + } + + + @Test + public void TestCosine() { + Calculator calc = new Calculator(); + calc.add(10.0f); + calc.cosine(); + assertEquals(-0.839072, calc.getDisplayVal(), 0.001); + } + + @Test + public void TestTangent() { + Calculator calc = new Calculator(); + calc.add(10.0f); + calc.tangent(); + assertEquals(0.648361, calc.getDisplayVal(), 0.001); + } + + @Test + public void TestInvSin() { + Calculator calc = new Calculator(); + calc.add(0.33f); + calc.invSin(); + assertEquals(0.336304, calc.getDisplayVal(), 0.001); + } + + @Test + public void TestInvCosine() { + Calculator calc = new Calculator(); + calc.add(0.33f); + calc.invCosine(); + assertEquals(1.234493, calc.getDisplayVal(), 0.001); + } + + @Test + public void TestInvTan() { + Calculator calc = new Calculator(); + calc.add(0.33f); + calc.invTangent(); + assertEquals(0.318748, calc.getDisplayVal(), 0.001); + } + + @Test + public void TestLog() { + Calculator calc = new Calculator(); + calc.add(10); + calc.log(); + assertEquals(2.302585092994045684, calc.getDisplayVal(), 0.001); + } + + @Test + public void TestInvNatLog() { + Calculator calc = new Calculator(); + calc.add(10); + calc.invNatLog(); + assertEquals(22026.465, calc.getDisplayVal(), 0.001); + + calc.setDisplayVal(7); + calc.invNatLog(); + assertEquals(1096.633, calc.getDisplayVal(), 0.001); + } + + @Test + public void TestBinaryMode() { + Calculator calc = new Calculator(); + calc.add(4); + calc.setDisplayMode(DisplayMode.BINARY); + assertEquals("100", calc.getDisplay()); + calc.setDisplayMode(DisplayMode.DECIMAL); + assertEquals("4.0", calc.getDisplay()); + } + + @Test + public void TestOctalMode() { + Calculator calc = new Calculator(); + calc.add(12); + calc.setDisplayMode(DisplayMode.OCTAL); + assertEquals("14", calc.getDisplay()); + calc.setDisplayMode(DisplayMode.DECIMAL); + assertEquals("12.0", calc.getDisplay()); + } + + @Test + public void TestHexMode() { + Calculator calc = new Calculator(); + calc.add(22); + calc.setDisplayMode(DisplayMode.HEXADECIMAL); + assertEquals(Integer.toHexString(22), calc.getDisplay()); + calc.setDisplayMode(DisplayMode.DECIMAL); + assertEquals("22.0", calc.getDisplay()); + } + +} diff --git a/src/test/java/com/zipcodewilmington/scientific_calculator/TestMainApplication.java b/src/test/java/com/zipcodewilmington/scientific_calculator/TestMainApplication.java deleted file mode 100644 index 94e8d987..00000000 --- a/src/test/java/com/zipcodewilmington/scientific_calculator/TestMainApplication.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.zipcodewilmington.scientific_calculator; - -/** - * Created by leon on 2/9/18. - */ -public class TestMainApplication { -} diff --git a/src/test/java/com/zipcodewilmington/scientificcalculator/Utilities/MathCommandsTest.java b/src/test/java/com/zipcodewilmington/scientificcalculator/Utilities/MathCommandsTest.java new file mode 100644 index 00000000..8322b470 --- /dev/null +++ b/src/test/java/com/zipcodewilmington/scientificcalculator/Utilities/MathCommandsTest.java @@ -0,0 +1,102 @@ +package com.zipcodewilmington.scientificcalculator.Utilities; + + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Random; + +import static org.junit.Assert.*; + + +public class MathCommandsTest { + + @Test + public void TestHandleExpression() { + ArrayList input = new ArrayList<>(); + input.add("("); + input.add("("); + input.add("22"); + input.add("+"); + input.add("24"); + input.add(")"); + input.add("/"); + input.add("2"); + input.add(")"); + Double expected = 23.0; + Double actual = MathCommands.handleExpression(input); + assertEquals(expected, actual); + } + + @Test + public void TestHandleExpressionExampleA() { + ArrayList input = new ArrayList<>(); + input.add("("); + input.add("1"); + input.add("+"); + input.add("("); + input.add("("); + input.add("2"); + input.add("+"); + input.add("3"); + input.add(")"); + input.add("*"); + input.add("("); + input.add("4"); + input.add("*"); + input.add("5"); + input.add(")"); + input.add(")"); + input.add(")"); + Double expected = 101.0; + Double actual = MathCommands.handleExpression(input); + assertEquals(expected, actual); + } + + @Test + public void TestHandleExpressionExampleB() { + ArrayList input = new ArrayList<>(); + input.add("("); + input.add("("); + input.add("1"); + input.add("+"); + input.add("sqrt"); + input.add("("); + input.add("5"); + input.add(")"); + input.add(")"); + input.add("/"); + input.add("2.0"); + input.add(")"); + Double expected = 1.618033988749895; + Double actual = MathCommands.handleExpression(input); + assertEquals(expected, actual); + } + + @Test + public void TestHandleExpressionWeirdInput() { + ArrayList input = new ArrayList<>(); + input.add("("); + input.add("1"); + input.add("+"); + input.add("("); + input.add("("); + input.add("2"); + input.add("3"); + input.add("+"); + input.add(")"); + input.add("("); + input.add("4"); + input.add("5"); + input.add("*"); + input.add(")"); + input.add("*"); + input.add(")"); + input.add("+"); + input.add(")"); + Double expected = 101.0; + Double actual = MathCommands.handleExpression(input); + assertEquals(expected, actual); + } +}