diff --git a/Display_functions.py b/Display_functions.py new file mode 100644 index 0000000..87e2fc9 --- /dev/null +++ b/Display_functions.py @@ -0,0 +1,124 @@ +import time + +class Display: + def store_memory(self, value, memory_value, current_result): + # Code to store the value in memory + if value.lower() == "m+": + memory_value = current_result + print(f"\nCurrent memory value: {memory_value}", end="") + elif value.lower() == "mc": + memory_value = 0 + print(f"\nCurrent memory value: {memory_value}", end="") + elif value.lower() == "mrc": + print(f"\nCurrent memory value: {memory_value}", end="") + return memory_value + + def memory_history(self, value, history_value): + history_value.append(value) + + def display_history(self, value, history_value): + if value.lower() == "h": + print("\nMemory history:", end="") + print(history_value, end="") + elif value.lower() == "hc": + history_value.clear() + print("\nMemory history cleared.", end="") + + def history_call(self, value, history_value): + if value < 0 or value >= len(history_value): + print("\nInvalid history index. Please try again.") + return None + else: + return history_value[value] + + + def trig_display(self, mode, switch): + # Code to display trigonometric results in the correct mode + if mode.lower() == "degree": + print("\nDisplaying result in degree mode.", end="") + return mode.lower() + elif mode.lower() == "radian": + print("\nDisplaying result in radian mode.", end="") + return mode.lower() + else: + return switch.lower() # Return the current display mode if no switch command is given + + def auto_trig_display(self, switch): + # Code to automatically switch trigonometric display mode based on the current mode + if switch == "degree": + print("\nDisplaying result in radian mode.", end="") + return "radian" + elif switch == "radian": + print("\nDisplaying result in degree mode.", end="") + return "degree" + else: + return switch # Return the current display mode if no auto switch is defined + + def switch_display_mode(self, mode, switch): + # Code to switch display mode + if mode.lower() == "hexadecimal": + print("\nSwitched to hexadecimal display mode.", end="") + return mode.lower() + elif mode.lower() == "binary": + print("\nSwitched to binary display mode.", end="") + return mode.lower() + elif mode.lower() == "decimal": + print("\nSwitched to decimal display mode.", end="") + return mode.lower() + elif mode.lower() == "octal": + print("\nSwitched to octal display mode.", end="") + return mode.lower() + else: + return switch.lower() # Return the current display mode if no switch command is given + + def auto_display_mode(self, switch): + # Code to automatically switch display mode based on the result + if switch == "hexadecimal": + print("\nDisplaying result in binary display mode.", end="") + return "binary" + elif switch == "binary": + print("\nDisplaying result in octal display mode.", end="") + return "octal" + elif switch == "octal": + print("\nDisplaying result in decimal display mode.", end="") + return "decimal" + elif switch == "decimal": + print("\nDisplaying result in hexadecimal display mode.", end="") + return "hexadecimal" + + else: + return switch # Return the current display mode if no auto switch is defined + + + def self_destruct(self): + # Code to simulate self destruct sequence and exit the program + time.sleep(0.5) + print("Self destruct sequence initiated. \n Loading self_destruct.exe... \n initating count down sequence...") + time.sleep(1) + print("3...") + time.sleep(1) + print("2...") + time.sleep(1) + print("1...") + time.sleep(1) + print("\033[2J\033[H", end="") + print("\nBOOM! \n Error: self_destruct.exe has caused a fatal error \n Initiating backup restoration and kill command...") + + for i in range(3): + time.sleep(2) + print("\nLoading...", end="") + + print("\033[2J\033[H", end="") + print("\n\nBackup restoration complete. \n Kill command successful. \n Exiting program...") + print("Have a nice day") + exit() + + def display_clear(self, value): + # Code to clear the display + if value.lower() == "clear": + print("\033[2J\033[H", end="") # ANSI escape codes to clear the terminal screen + + def display_quitcancel(self): + # Code to display quit cancel message and return to previous state + print("\nReturning to previous state. Please wait...") + time.sleep(1) diff --git a/calctests.py b/calctests.py index 1964570..91e1cbb 100644 --- a/calctests.py +++ b/calctests.py @@ -1,3 +1,4 @@ +import math import unittest from calculator import Calculator @@ -20,6 +21,96 @@ def test_sub(self): c = Calculator() self.assertEqual(c.sub(9, 3), 6) + def test_mult(self): + c = Calculator() + self.assertEqual(c.mult(9, 3), 27) + + def test_div(self): + c = Calculator() + self.assertEqual(c.div(9, 3), 3) + + def test_div(self): + c = Calculator() + self.assertEqual(c.sqr(9), 81) + + def test_root2(self): + c = Calculator() + self.assertEqual(c.root2(9), 3) + + def test_exp(self): + c = Calculator() + self.assertEqual(c.exp(9,3), 729) + + def inv(self): + c = Calculator() + self.assertEqual(c.inv(5), .2) + + def neg(self): + c = Calculator() + self.assertEqual(c.neg(9), -9) + + def neg(self): + c = Calculator() + self.assertEqual(c.neg(-9), 9) + + def sin(self): + c = Calculator() + self.assertEqual(c.sin(9), 0.4121184852417566) + + def cos(self): + c = Calculator() + self.assertEqual(c.cos(9), -0.9111302618846769) + + def tan(self): + c = Calculator() + self.assertEqual(c.tan(9), 0.4523156594418099) + def isin(self): + c = Calculator() + self.assertEqual(c.isin(-1), -1.5707963267948966) + + def icos(self): + c = Calculator() + self.assertEqual(c.icos(-1), 3.141592653589793) + + def itan(self): + c = Calculator() + self.assertEqual(c.itan(-1), -0.7853981633974483) + + def deg(self): + c = Calculator() + self.assertEqual(c.deg(45), 2578.3100780887044) + + def rad(self): + c = Calculator() + self.assertEqual(c.rad(45), 0.7853981633974483) + + def fac(self): + c = Calculator() + self.assertEqual(c.fac(9), 362880) + + def log(self): + c = Calculator() + self.assertEqual(c.log(9), 2.1972245773362196) + + def log10(self): + c = Calculator() + self.assertEqual(c.log10(9), 0.9542425094393249) + + def natlog(self): + c = Calculator() + self.assertEqual(c.natlog(9), 2.302585092994046) + + def in_natlog(self): + c = Calculator() + self.assertEqual(c.in_natlog(9), 0.43429448190325176) + + def infinity(self): + c = Calculator() + self.assertEqual(c.infinity(9),NaN) + + def abs_val(self): + c = Calculator() + self.assertEqual(c.abs_val(-9),9) if __name__ == '__main__': unittest.main() diff --git a/calculator.py b/calculator.py index 3c85ead..2bf9ebd 100644 --- a/calculator.py +++ b/calculator.py @@ -1,3 +1,5 @@ +import math + class Calculator: def __init__(self): @@ -7,6 +9,91 @@ def add(self, x, y): return x + y def sub(self, x, y): - return 0 + return x - y + + def mult(self, x, y): + return x * y + + def div(self, x, y): + if y == 0: + print("\nError: Division by zero is not defined.", end="") + return None + else: + return x / y + + def sqr(self, x): + return x * x + + def root2(self, x): + if x < 0: + print("\nError: Square root of a negative number is not defined in the real number system.", end="") + return None + else: + return math.sqrt(x) + + def exp(self, x, y): + return math.pow(x, y) + + def inv(self, x): + return x ** -1 + + def neg(self, x): + return x * -1 + + def sin(self, x): + return math.sin(x) + + def cos(self, x): + return math.cos(x) + + def tan(self, x): + return math.tan(x) + + def isin(self, x): + return math.asin(x) + + def icos(self, x): + return math.acos(x) + + def itan(self, x): + return math.atan(x) + def deg(self, x): + return math.degrees(x) + + def rad(self, x): + return math.radians(x) + + def fac(self, x): + return math.factorial(x) + + def log(self, x): + if x <= 0: + print("\nError: Logarithm of non-positive numbers is not defined.", end="") + return None + else: + return math.log(x) + + def log10(self, x): + if x <= 0: + print("\nError: Logarithm of non-positive numbers is not defined.", end="") + return None + else: + return math.log10(x) + + def natlog(self, x): + return math.log1p(x) + + def in_natlog(self, x): + if x <= 0: + print("\nError: Inverse logarithm of numbers less than or equal to 0 is not defined.", end="") + return None + else: + return math.pow(math.log1p(x),-1) + + def infinity(self, x): + return math.isfinite(x) + + def abs_val(self, x): + return math.fabs(x) # add lots more methods to this calculator class. diff --git a/main-app.py b/main-app.py index a7cc4e2..d36a845 100644 --- a/main-app.py +++ b/main-app.py @@ -1,33 +1,318 @@ from calculator import Calculator +from Display_functions import Display +def history_call(value, result_history): + if value == "h": + while True: + Display.display_history(None, "h", result_history) + value = input("\nEnter the index of the history value you wish to use or 'e' to return to number input \nIndex: ") + if value == "e": + return None + value = int(value) + value = Display.history_call(None, value, result_history) + if value is not None: + print(f"\nUsing history value: {value}") + value = float(value) + return value + else: + continue + else: + value = float(value) + return value -def getTwoNumbers(): - a = float(input("first number? ")) - b = float(input("second number? ")) + +def getTwoNumbers(result_history): + while True: + a = input("\nIf wish to use history input 'h' else input number \nfirst number? ") + a = history_call(a, result_history) + if a is not None: + break + while True: + b = input("\nIf wish to use history input 'h' else input number \nsecond number? ") + b = history_call(b, result_history) + if b is not None: + break return a, b +def getOneNumber(result_history): + while True: + a = input("\nIf wish to use history input 'h' else input number \nnumber? ") + a = history_call(a, result_history) + if a is not None: + break + return a + +def displayResult(x: float, mode: str): + if mode == "hexadecimal": + print(hex(int(x))[2:], end="") # [2:] to remove the '0x' prefix from the hexadecimal representation + elif mode == "binary": + print(bin(int(x))[2:], end="") # [2:] to remove the '0b' prefix from the binary representation + elif mode == "decimal": + print(x, end="") + elif mode == "octal": + print(oct(int(x))[2:], end="") # [2:] to remove the '0o' prefix from the octal representation + else: + print(x, "\n") -def displayResult(x: float): - print(x, "\n") +def displayTrigResult(x: float, mode: str): + if mode == "degree": + print(f"{Calculator.deg(x,x)} degrees", end="") + return Calculator.deg(x,x) + elif mode == "radian": + print(f"{x} radians", end="") + return x + else: + print(x, "\n") -def performCalcLoop(calc): +def performCalcLoop(calc, dis): + result = 0 + result_format = "" + memory = 0 + result_history = [] + switch = "decimal" # Default display mode + trig_switch = "degree" # Default trigonometric display mode + print("\nWelcome to the Scientific Calculator!", end="") + while True: - choice = input("Operation? ") + print("\n") + choice = input("Type 'help' for a list of operations or enter an operation \noperation: ") + + switch = dis.switch_display_mode(choice, switch) # Switch display mode if the user input is a display mode command + memory = dis.store_memory(choice, memory, result) # Store the user's choice in memory + trig_switch = dis.trig_display(choice, trig_switch) # Switch trigonometric display mode if the user input is a trig display mode command + dis.display_history(choice, result_history) + + # Display operations and quit command if choice == 'q': - break # user types q to quit calulator. + while True: + final_choice = input("\nAre you sure you want to quit? (y/n) ") + if final_choice.lower() == 'y': + return # user types q to quit calculator. + elif final_choice.lower() == 'n': + dis.display_quitcancel() # user cancels quit command, return to previous state. + break # user chooses not to quit, continue with calculator. + else: + print("\nCommand not recognized. Rerunning user_interface.exe, please try again.") + + elif choice == 'help': + print("\nAvailable operations: add, sub, mult, div, sqr, root2, exp, inv, neg, sin, cos, tan, isin, icos, itan, deg, rad, fac, log, log10, natlog, in_natlog, finite, abs_val") + print("Memory operations: M+ (store current result in memory), MC (clear memory), MRC (recall memory)") + print("Memory history operations: H (display memory history), HC (clear memory history)") + print("Display mode operations: hexadecimal, binary, decimal, octal, switch (automatically switch display mode), trig (automatically switch trigonometric display mode), degree (switch to degree mode), radian (switch to radian mode)") + print("Other operations: clear (clears the display), q (quit the calculator)") + continue + elif choice == 'clear': + dis.display_clear(choice) + continue + + elif choice.lower() == "switch": + switch = dis.auto_display_mode(switch) + continue + elif choice.lower() == "trig": + trig_switch = dis.auto_trig_display(trig_switch) + continue + + # Implementing calculator operations elif choice == 'add': - a, b = getTwoNumbers() - displayResult(calc.add(a, b)) + print("\nFormat: [number1] + [number2]") + a, b = getTwoNumbers(result_history) + result = calc.add(a, b) + result_format = f"{a} + {b} = {result}" + + elif choice == 'sub': + print("\nFormat: [number1] - [number2]") + a, b = getTwoNumbers(result_history) + result = calc.sub(a, b) + result_format = f"{a} - {b} = {result}" + + elif choice == 'mult': + print("\nFormat: [number1] * [number2]") + a, b = getTwoNumbers(result_history) + result = calc.mult(a, b) + result_format = f"{a} * {b} = {result}" + + elif choice == 'div': + print("\nFormat: [number1] / [number2]") + a, b = getTwoNumbers(result_history) + result = calc.div(a, b) + if b == 0: + continue + else: + result_format = f"{a} / {b} = {result}" + + elif choice == 'sqr': + print("\nFormat: [number] ^ 2") + a = getOneNumber(result_history) + result = calc.sqr(a) + result_format = f"{a} ^ 2 = {result}" + + elif choice == 'root2': + print("\nFormat: sqrt([number])") + a = getOneNumber(result_history) + result = calc.root2(a) + if a < 0: + continue + else: + result_format = f"sqrt({a}) = {result}" + + elif choice == 'exp': + print("\nFormat: [number1] ^ [number2]") + a, b = getTwoNumbers(result_history) + result = calc.exp(a, b) + result_format = f"{a} ^ {b} = {result}" + + elif choice == 'inv': + print("\nFormat: 1 / [number]") + a = getOneNumber(result_history) + result = calc.inv(a) + result_format = f"1 / {a} = {result}" + + elif choice == 'neg': + print("\nFormat: -[number]") + a = getOneNumber(result_history) + result = calc.neg(a) + result_format = f"-{a} = {result}" + + elif choice == 'sin': + print(f"\nCurrent trigonometric input mode: {trig_switch}") + print("\nFormat: sin([number])") + a = getOneNumber(result_history) + a = calc.rad(a) if trig_switch == "degree" else a # Convert input to radians if in degree mode + result = calc.sin(a) + result_format = f"sin({a}) = {result}" + + elif choice == 'cos': + print(f"\nCurrent trigonometric input mode: {trig_switch}") + print("\nFormat: cos([number])") + a = getOneNumber(result_history) + result = calc.cos(a) + result_format = f"cos({a}) = {result}" + + elif choice == 'tan': + print(f"\nCurrent trigonometric input mode: {trig_switch}") + print("\nFormat: tan([number])") + a = getOneNumber(result_history) + result = calc.tan(a) + result_format = f"tan({a}) = {result}" + + elif choice == 'isin': + print(f"\nCurrent trigonometric input mode: {trig_switch}") + print("\nFormat: arcsin([number])") + a = getOneNumber(result_history) + result = calc.isin(a) + result_format = f"arcsin({a}) = {result}" + + elif choice == 'icos': + print(f"\nCurrent trigonometric input mode: {trig_switch}") + print("\nFormat: arccos([number])") + a = getOneNumber(result_history) + result = calc.icos(a) + result_format = f"arccos({a}) = {result}" + + elif choice == 'itan': + print(f"\nCurrent trigonometric input mode: {trig_switch}") + print("\nFormat: arctan([number])") + a = getOneNumber(result_history) + result = calc.itan(a) + result_format = f"arctan({a}) = {result}" + + elif choice == 'deg': + print("\nFormat: deg([number])") + a = getOneNumber(result_history) + result = calc.deg(a) + result_format = f"deg({a}) = {result}" + + elif choice == 'rad': + print("\nFormat: rad([number])") + a = getOneNumber(result_history) + result = calc.rad(a) + result_format = f"rad({a}) = {result}" + + elif choice == 'fac': + print("\nFormat: [number]!") + a = getOneNumber(result_history) + result = calc.fac(a) + result_format = f"{a}! = {result}" + + elif choice == 'log': + print("\nFormat: log([number])") + a = getOneNumber(result_history) + result = calc.log(a) + if a <= 0: + continue + else: + result_format = f"log({a}) = {result}" + + elif choice == 'log10': + print("\nFormat: log10([number])") + a = getOneNumber(result_history) + result = calc.log10(a) + if a <= 0: + continue + else: + result_format = f"log10({a}) = {result}" + + elif choice == 'natlog': + print("\nFormat: ln([number])") + a = getOneNumber(result_history) + result = calc.natlog(a) + result_format = f"ln({a}) = {result}" + + elif choice == 'in_natlog': + print("\nFormat: in_ln([number])") + a = getOneNumber(result_history) + result = calc.in_natlog(a) + if result is None: + continue + else: + result_format = f"in_ln({a}) = {result}" + + elif choice == 'finite': + print("\nFormat: finite([number])") + a = getOneNumber(result_history) + result = calc.infinity(a) + result_format = f"is finite({a}) = {result}" + + elif choice == 'abs_val': + print("\nFormat: abs([number])") + a = getOneNumber(result_history) + result = calc.abs_val(a) + result_format = f"abs({a}) = {result}" + + # Passes or invalid imput + elif choice.lower() == "m+" or choice.lower() == "mc" or choice.lower() == "mrc" \ + or choice.lower() == "hexadecimal" or choice.lower() == "binary" or choice.lower() == "decimal" or choice.lower() == "octal" \ + or choice.lower() == "degree" or choice.lower() == "radian" or choice.lower() == "switch" or choice.lower() == "trig" \ + or choice.lower() == "h" or choice.lower() == "hc": + continue else: - print("That is not a valid input.") + print("\nThat is not a valid input. Please try again.", end="") + continue + print(f"\nCurrent decimal memory value: {memory}") + print(f"Current display mode: {switch}\n") + if choice in ['sin', 'cos', 'tan', 'isin', 'icos', 'itan']: + print(f"Current trigonometric display mode: {trig_switch}\n") + print(f"{result_format}") + displayTrigResult(result, trig_switch) + if trig_switch == "degree": + result = calc.deg(result) + memory_history = dis.memory_history(result, result_history) + else: + print(f"{result_format}") + print("Displaying result in current display mode: ", end="") + displayResult(result, switch) + memory_history = dis.memory_history(result, result_history) # main start def main(): calc = Calculator() - performCalcLoop(calc) - print("Done Calculating.") + dis = Display() + dis.display_clear("clear") + performCalcLoop(calc, dis) + print("\nDone Calculating.\n") + dis.self_destruct() if __name__ == '__main__':