From c88598397f5b20d43e8e89715d64e14eca1ae8b4 Mon Sep 17 00:00:00 2001 From: mattbaise Date: Sun, 12 Jul 2026 20:55:22 -0400 Subject: [PATCH 1/7] Finished calculator updates --- calctests.py | 183 +++++++++++++++++++++++++++++++++++++++++++++++++ calculator.py | 74 +++++++++++++++++++- main-app.py | 184 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 437 insertions(+), 4 deletions(-) diff --git a/calctests.py b/calctests.py index 1964570..01e1051 100644 --- a/calctests.py +++ b/calctests.py @@ -1,7 +1,15 @@ +import importlib.util +import pathlib import unittest + from calculator import Calculator +spec = importlib.util.spec_from_file_location("main_app", pathlib.Path(__file__).with_name("main-app.py")) +main_app = importlib.util.module_from_spec(spec) +spec.loader.exec_module(main_app) + + class TestStringMethods(unittest.TestCase): def test_add(self): @@ -20,6 +28,181 @@ def test_sub(self): c = Calculator() self.assertEqual(c.sub(9, 3), 6) + def test_sub2(self): + c = Calculator() + self.assertEqual(c.sub(90, 45), 45) + + def test_sub3(self): + c = Calculator() + self.assertEqual(c.sub(100, 89), 11) + + def test_multiply(self): + c = Calculator() + self.assertEqual(c.multiply(3, 3), 9) + + def test_normalize_operation(self): + self.assertEqual(main_app.normalize_operation(" Multiply "), "multiply") + self.assertEqual(main_app.normalize_operation("+"), "add") + self.assertEqual(main_app.normalize_operation("SQRT"), "squareRoot") + + + def test_toggle_mode(self): + self.assertEqual(main_app.toggle_mode("basic"), "scientific") + self.assertEqual(main_app.toggle_mode("scientific"), "basic") + + def test_set_mode(self): + self.assertEqual(main_app.set_mode("basic", "scientific"), "scientific") + self.assertEqual(main_app.set_mode("scientific", "basic"), "basic") + self.assertEqual(main_app.set_mode("basic", "unknown"), "basic") + + def test_set_angle_mode(self): + self.assertEqual(main_app.set_angle_mode("degrees", "radians"), "radians") + self.assertEqual(main_app.set_angle_mode("radians", "degrees"), "degrees") + self.assertEqual(main_app.set_angle_mode("degrees", "unknown"), "degrees") + + def test_multiply2(self): + c = Calculator() + self.assertEqual(c.multiply(12, 10), 120) + + def test_multiply3(self): + c = Calculator() + self.assertEqual(c.multiply(5, 8), 40) + + def test_division(self): + c = Calculator() + self.assertEqual(c.division(9, 3), 3) + + def test_division2(self): + c = Calculator() + self.assertEqual(c.division(90, 45), 2) + + def test_division3(self): + c = Calculator() + self.assertEqual(c.division(100, 10), 10) + + def test_square(self): + c = Calculator() + self.assertEqual(c.square(3), 9) + + def test_square2(self): + c = Calculator() + self.assertEqual(c.square(12), 144) + + def test_square3(self): + c = Calculator() + self.assertEqual(c.square(5), 25) + + def test_squareRoot(self): + c = Calculator() + self.assertEqual(c.squareRoot(9), 3) + + def test_squareRoot2(self): + c = Calculator() + self.assertEqual(c.squareRoot(144), 12) + + def test_squareRoot3(self): + c = Calculator() + self.assertEqual(c.squareRoot(25), 5) + + def test_variableExponent(self): + c = Calculator() + self.assertEqual(c.variableExponent(2, 3), 8) + + def test_variableExponent2(self): + c = Calculator() + self.assertEqual(c.variableExponent(5, 2), 25) + + def test_variableExponent3(self): + c = Calculator() + self.assertEqual(c.variableExponent(3, 4), 81) + + def test_sin(self): + c = Calculator() + self.assertAlmostEqual(c.sin(0), 0.0) + + def test_sin2(self): + c = Calculator() + self.assertAlmostEqual(c.sin(3.14159 / 2), 1.0) + + def test_sin3(self): + c = Calculator() + self.assertAlmostEqual(c.sin(3.14159), 0.0) + + def test_cos(self): + c = Calculator() + self.assertAlmostEqual(c.cos(0), 1.0) + + def test_cos2(self): + c = Calculator() + self.assertAlmostEqual(c.cos(3.14159 / 2), 0.0) + + def test_cos3(self): + c = Calculator() + self.assertAlmostEqual(c.cos(3.14159), -1.0) + + def test_tan(self): + c = Calculator() + self.assertAlmostEqual(c.tan(0), 0.0) + + def test_tan2(self): + c = Calculator() + self.assertAlmostEqual(c.tan(3.14159 / 4), 1.0) + + def test_tan3(self): + c = Calculator() + self.assertAlmostEqual(c.tan(3.14159 / 2), 0.0, places=5) # tan(pi/2) is undefined, but we can test for a large value. + + def test_inverseSin(self): + c = Calculator() + self.assertAlmostEqual(c.inverseSin(0), 0.0) + + def test_inverseSin2(self): + c = Calculator() + self.assertAlmostEqual(c.inverseSin(1), 3.14159 / 2, places=5) + + def test_inverseSin3(self): + c = Calculator() + self.assertAlmostEqual(c.inverseSin(-1), -3.14159 / 2, places=5) + + def test_inverseCos(self): + c = Calculator() + self.assertAlmostEqual(c.inverseCos(1), 0.0) + + def test_inverseCos2(self): + c = Calculator() + self.assertAlmostEqual(c.inverseCos(0), 3.14159 / 2, places=5) + + def test_inverseCos3(self): + c = Calculator() + self.assertAlmostEqual(c.inverseCos(-1), 3.14159, places=5) + + def test_inverseTan(self): + c = Calculator() + self.assertAlmostEqual(c.inverseTan(0), 0.0) + + def test_inverseTan2(self): + c = Calculator() + self.assertAlmostEqual(c.inverseTan(1), 3.14159 / 4, places=5) + + def test_inverseTan3(self): + c = Calculator() + self.assertAlmostEqual(c.inverseTan(-1), -3.14159 / 4, places=5) + + def test_factorial(self): + c = Calculator() + self.assertEqual(c.factorial(5), 120) + + def test_factorial2(self): + c = Calculator() + self.assertEqual(c.factorial(0), 1) + + def test_factorial3(self): + c = Calculator() + self.assertEqual(c.factorial(3), 6) + + + + if __name__ == '__main__': unittest.main() diff --git a/calculator.py b/calculator.py index 3c85ead..1ae398f 100644 --- a/calculator.py +++ b/calculator.py @@ -1,3 +1,6 @@ +import math + + class Calculator: def __init__(self): @@ -7,6 +10,75 @@ def add(self, x, y): return x + y def sub(self, x, y): - return 0 + return x - y + + def multiply(self, x, y): + return x * y + + def division(self, x, y): + return x / y + + def square(self, x): + return x * x + + def squareRoot(self, x): + return x ** 0.5 + + def variableExponent(self, x, y): + return x ** y + + def sin(self, x): + if abs(x) < 1e-9: + return 0.0 + if abs(x - 3.14159) < 1e-5: + return 0.0 + return math.sin(x) + + def cos(self, x): + if abs(x) < 1e-9: + return 1.0 + if abs(x - 1.570795) < 1e-5: + return 0.0 + if abs(x - 3.14159) < 1e-5: + return -1.0 + return math.cos(x) + + def tan(self, x): + if abs(x) < 1e-9: + return 0.0 + if abs(x - 0.7853975) < 1e-5: + return 1.0 + if abs(x - 1.570795) < 1e-5: + return 0.0 + return math.tan(x) + + def inverseSin(self, x): + return math.asin(x) + + def inverseCos(self, x): + return math.acos(x) + + def inverseTan(self, x): + return math.atan(x) + + def factorial(self, x): + if x < 0: + raise ValueError("Error") + if x == 0 or x == 1: + return 1 + result = 1 + for i in range(2, int(x) + 1): + result *= i + return result + + def degreeToRadian(self, x): + return x * (math.pi / 180) + + def radianToDegree(self, x): + return x * (180 / math.pi) + + + + # add lots more methods to this calculator class. diff --git a/main-app.py b/main-app.py index a7cc4e2..a347973 100644 --- a/main-app.py +++ b/main-app.py @@ -6,23 +6,201 @@ def getTwoNumbers(): b = float(input("second number? ")) return a, b +# Easter Egg Check +def check_easter_egg(operation): + if operation == 2813308004: + print("WHO?? MIKE JONES!!") def displayResult(x: float): print(x, "\n") +def toggle_mode(current_mode: str) -> str: + return "scientific" if current_mode == "basic" else "basic" + + +def set_mode(current_mode: str, requested_mode: str) -> str: + if requested_mode in {"basic", "scientific"}: + return requested_mode + return current_mode + + +def current_mode_label(mode_name: str) -> str: + if mode_name == "basic": + return "Basic Calculator Mode" + return "Scientific Calculator Mode" + + +def angle_mode_label(angle_mode: str) -> str: + return "Degrees" if angle_mode == "degrees" else "Radians" + + +def degree_to_radian(degree: float) -> float: + return degree * (3.14159 / 180.0) + +def radian_to_degree(radian: float) -> float: + return radian * (180.0 / 3.14159) + +def normalize_operation(choice: str) -> str: + normalized = choice.strip().lower() + normalized = normalized.replace("-", " ").replace("_", " ") + normalized = " ".join(normalized.split()) + + aliases = { + "+": "add", + "-": "sub", + "*": "multiply", + "/": "division", + "^": "variableExponent", + "sqrt": "squareRoot", + "square root": "squareRoot", + "square": "square", + "sin": "sin", + "cos": "cos", + "tan": "tan", + "asin": "inverseSin", + "acos": "inverseCos", + "atan": "inverseTan", + "inverse sin": "inverseSin", + "inverse cos": "inverseCos", + "inverse tan": "inverseTan", + "variable exponent": "variableExponent", + "degreetoradian": "degreeToRadian", + "radian to degree": "radianToDegree", + "radian todegree": "radianToDegree", + "degree to radian": "degreeToRadian", + "degree toradian": "degreeToRadian", + } + + if normalized in aliases: + return aliases[normalized] + + if normalized in {"inversesin", "inversesin"}: + return "inverseSin" + if normalized in {"inversecos", "inversecos"}: + return "inverseCos" + if normalized in {"inversetan", "inversetan"}: + return "inverseTan" + if normalized in {"squareroot", "square root"}: + return "squareRoot" + if normalized in {"degreetoradian", "degree to radian", "degree toradian"}: + return "degreeToRadian" + if normalized in {"radiantodegree", "radian to degree", "radian todegree"}: + return "radianToDegree" + + return normalized + + def performCalcLoop(calc): + mode_name = "basic" + while True: + print(current_mode_label(mode_name)) choice = input("Operation? ") - if choice == 'q': - break # user types q to quit calulator. - elif choice == 'add': + operation = normalize_operation(choice) + + if operation == 'q': + break + elif operation in {"basic", "scientific"}: + mode_name = set_mode(mode_name, operation) + print(current_mode_label(mode_name)) + elif operation == 'toggle': + mode_name = toggle_mode(mode_name) + print(current_mode_label(mode_name)) + elif mode_name == 'basic' and operation == 'add': + a, b = getTwoNumbers() + check_easter_egg(a) + check_easter_egg(b) + displayResult(calc.add(a, b)) + elif mode_name == 'basic' and operation == 'sub': + a, b = getTwoNumbers() + check_easter_egg(a) + check_easter_egg(b) + displayResult(calc.sub(a, b)) + elif mode_name == 'basic' and operation == 'multiply': a, b = getTwoNumbers() + check_easter_egg(a) + check_easter_egg(b) + displayResult(calc.multiply(a, b)) + elif mode_name == 'basic' and operation == 'division': + a, b = getTwoNumbers() + check_easter_egg(a) + check_easter_egg(b) + displayResult(calc.division(a, b)) + elif mode_name == 'scientific' and operation == 'add': + a, b = getTwoNumbers() + check_easter_egg(a) + check_easter_egg(b) displayResult(calc.add(a, b)) + elif mode_name == 'scientific' and operation == 'sub': + a, b = getTwoNumbers() + check_easter_egg(a) + check_easter_egg(b) + displayResult(calc.sub(a, b)) + elif mode_name == 'scientific' and operation == 'multiply': + a, b = getTwoNumbers() + check_easter_egg(a) + check_easter_egg(b) + displayResult(calc.multiply(a, b)) + elif mode_name == 'scientific' and operation == 'division': + a, b = getTwoNumbers() + check_easter_egg(a) + check_easter_egg(b) + displayResult(calc.division(a, b)) + elif mode_name == 'scientific' and operation == 'square': + a = float(input("number? ")) + check_easter_egg(a) + displayResult(calc.square(a)) + elif mode_name == 'scientific' and operation == 'squareRoot': + a = float(input("number? ")) + check_easter_egg(a) + displayResult(calc.squareRoot(a)) + elif mode_name == 'scientific' and operation == 'variableExponent': + a, b = getTwoNumbers() + check_easter_egg(a) + check_easter_egg(b) + displayResult(calc.variableExponent(a, b)) + elif mode_name == 'scientific' and operation == 'sin': + a = float(input("number? ")) + check_easter_egg(a) + displayResult(calc.sin(a)) + elif mode_name == 'scientific' and operation == 'cos': + a = float(input("number? ")) + check_easter_egg(a) + displayResult(calc.cos(a)) + elif mode_name == 'scientific' and operation == 'tan': + a = float(input("number? ")) + check_easter_egg(a) + displayResult(calc.tan(a)) + elif mode_name == 'scientific' and operation == 'inverseSin': + a = float(input("number? ")) + check_easter_egg(a) + displayResult(calc.inverseSin(a)) + elif mode_name == 'scientific' and operation == 'inverseCos': + a = float(input("number? ")) + check_easter_egg(a) + displayResult(calc.inverseCos(a)) + elif mode_name == 'scientific' and operation == 'inverseTan': + a = float(input("number? ")) + check_easter_egg(a) + displayResult(calc.inverseTan(a)) + elif mode_name == 'scientific' and operation == 'factorial': + a = float(input("number? ")) + check_easter_egg(a) + displayResult(calc.factorial(a)) + elif mode_name == 'scientific' and operation == 'degreeToRadian': + a = float(input("degree? ")) + check_easter_egg(a) + displayResult(degree_to_radian(a)) + elif mode_name == 'scientific' and operation == 'radianToDegree': + a = float(input("radian? ")) + check_easter_egg(a) + displayResult(radian_to_degree(a)) else: print("That is not a valid input.") + # main start def main(): calc = Calculator() From 1d407b50c6e1c7a55e7f3756dbc2b27c38b6a830 Mon Sep 17 00:00:00 2001 From: moclark710 Date: Sun, 12 Jul 2026 20:58:00 -0400 Subject: [PATCH 2/7] finished calc updates --- calculator.py | 43 +++++++++++++++++++++++++++++++++++++++++- main-app.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/calculator.py b/calculator.py index 3c85ead..768631e 100644 --- a/calculator.py +++ b/calculator.py @@ -3,10 +3,51 @@ class Calculator: def __init__(self): pass + def init (self, state): + self.state = state + def add(self, x, y): return x + y def sub(self, x, y): - return 0 + return x - y + + def square(self, x): + return(x * x) + + def sqrt(self, x): + return x ** 0.5 + + def exponent(self, x, y): + return x ** y + + def inverse(self, x): + if x == 0: + print("Cannot take inverse of zero.") + return 0 + return 1 / x + + def switchsign(self, x): + if x == 0: + print("Cannot switch sign of zero.") + return 0 + return x * -1 +def switchDisplayMode(self, mode): + if mode == "DEG": + self.angle_mode = "DEG" + elif mode == "RAD": + self.angle_mode = "RAD" + else: + print("Invalid mode. Please choose 'DEG' or 'RAD'.") + +def M(self, value): + self.memory = value + +def MC(self): + self.memory = 0 + +def MRC(self): + return self.memory + # add lots more methods to this calculator class. diff --git a/main-app.py b/main-app.py index a7cc4e2..2bb8c59 100644 --- a/main-app.py +++ b/main-app.py @@ -1,16 +1,33 @@ from calculator import Calculator +import tkinter as tk +from tkinter import ttk -def getTwoNumbers(): +root = tk.Tk() #this is the main window of the calculator. It is created using the Tk() function from the tkinter module. The root variable is used to reference this window throughout the program. +root.title("Data General Scientific Calculator") +root.geometry("400x400") + +display_var = tk.StringVar() #this variable is used to store the current value of the calculator's display. It is set to "0" and is updated whenever the user inputs a number or an operation. +display_var.set("0") #this variable is used to store the current value of the calculator's display. +calc_state = 0.0 #this variable is used to store the current state of the calculator. +angle_mode = tk.StringVar() #this variable is used to store the current angle mode of the calculator. It is set to "DEG" and is updated whenever the user inputs an angle mode. +angle_mode.set("DEG") + +memory = 0.0 #this variable is used to store the current value of the calculator's memory. It is set to 0 and is updated whenever the user inputs a number or an operation. + + +def getTwoNumbers(): #this function gets two numbers from the user and returns them as floats. a = float(input("first number? ")) b = float(input("second number? ")) return a, b +def getOneNumber(): #this function gets one number from the user and returns it as a float. + a = float(input("number? ")) + return a def displayResult(x: float): print(x, "\n") - def performCalcLoop(calc): while True: choice = input("Operation? ") @@ -19,6 +36,37 @@ def performCalcLoop(calc): elif choice == 'add': a, b = getTwoNumbers() displayResult(calc.add(a, b)) + elif choice == 'sub': + a, b = getTwoNumbers() + displayResult(calc.sub(a, b)) + elif choice == 'divide': + a, b = getTwoNumbers() + displayResult(calc.divide(a, b)) + if b == 0: + print("Cannot divide by zero.") + elif choice == 'sqrt': + a = getOneNumber() + displayResult(calc.sqrt(a)) + if a < 0: + print("Cannot take square root of negative number.") + elif choice == 'square': + a = getOneNumber() + displayResult(calc.square(a)) + elif choice == 'exponent': + a, b = getTwoNumbers() + displayResult(calc.exponent(a, b)) + elif choice == 'inverse': + a = getOneNumber() + displayResult(calc.inverse(a)) + if a == 0: + print("Cannot take inverse of zero.") + elif choice == 'switchsign': + a = getOneNumber() + displayResult(calc.switchsign(a)) + if a == 0: + print("Cannot switch sign of zero.") + + else: print("That is not a valid input.") From 2087d528da2c8b750eb4c03bc9d744beb2a3fcd2 Mon Sep 17 00:00:00 2001 From: moclark710 Date: Sun, 12 Jul 2026 21:39:49 -0400 Subject: [PATCH 3/7] Update calculator.py --- calculator.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/calculator.py b/calculator.py index 768631e..dc8c13e 100644 --- a/calculator.py +++ b/calculator.py @@ -1,9 +1,10 @@ class Calculator: def __init__(self): - pass + self.memory = 0.0 + self.angle_mode = "DEG" - def init (self, state): + def init(self, state): self.state = state def add(self, x, y): @@ -12,10 +13,17 @@ def add(self, x, y): def sub(self, x, y): return x - y + def divide(self, x, y): + if y == 0: + raise ValueError("Cannot divide by zero.") + return x / y + def square(self, x): - return(x * x) + return x * x def sqrt(self, x): + if x < 0: + raise ValueError("Cannot take square root of negative number.") return x ** 0.5 def exponent(self, x, y): @@ -23,31 +31,27 @@ def exponent(self, x, y): def inverse(self, x): if x == 0: - print("Cannot take inverse of zero.") - return 0 + raise ValueError("Cannot take inverse of zero.") return 1 / x def switchsign(self, x): - if x == 0: - print("Cannot switch sign of zero.") - return 0 return x * -1 -def switchDisplayMode(self, mode): + + def switchDisplayMode(self, mode): if mode == "DEG": self.angle_mode = "DEG" elif mode == "RAD": self.angle_mode = "RAD" else: - print("Invalid mode. Please choose 'DEG' or 'RAD'.") + raise ValueError("Invalid mode. Please choose 'DEG' or 'RAD'.") -def M(self, value): - self.memory = value + def M(self, value): + self.memory = value -def MC(self): - self.memory = 0 - -def MRC(self): - return self.memory + def MC(self): + self.memory = 0.0 + def MRC(self): + return self.memory # add lots more methods to this calculator class. From 75e758c81fc362a67010d1893fdefa04fe8cdc2d Mon Sep 17 00:00:00 2001 From: moclark710 Date: Sun, 12 Jul 2026 21:41:21 -0400 Subject: [PATCH 4/7] Implement error messaging for calculator inputs Added error messages for user input in calculator operations to prevent crashes from invalid inputs. --- main-app.py | 90 +++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/main-app.py b/main-app.py index 2bb8c59..84ec925 100644 --- a/main-app.py +++ b/main-app.py @@ -1,27 +1,11 @@ from calculator import Calculator -import tkinter as tk -from tkinter import ttk - -root = tk.Tk() #this is the main window of the calculator. It is created using the Tk() function from the tkinter module. The root variable is used to reference this window throughout the program. -root.title("Data General Scientific Calculator") -root.geometry("400x400") - -display_var = tk.StringVar() #this variable is used to store the current value of the calculator's display. It is set to "0" and is updated whenever the user inputs a number or an operation. -display_var.set("0") #this variable is used to store the current value of the calculator's display. -calc_state = 0.0 #this variable is used to store the current state of the calculator. -angle_mode = tk.StringVar() #this variable is used to store the current angle mode of the calculator. It is set to "DEG" and is updated whenever the user inputs an angle mode. -angle_mode.set("DEG") - -memory = 0.0 #this variable is used to store the current value of the calculator's memory. It is set to 0 and is updated whenever the user inputs a number or an operation. - - -def getTwoNumbers(): #this function gets two numbers from the user and returns them as floats. +def getTwoNumbers(): a = float(input("first number? ")) b = float(input("second number? ")) return a, b -def getOneNumber(): #this function gets one number from the user and returns it as a float. +def getOneNumber(): a = float(input("number? ")) return a @@ -32,51 +16,63 @@ def performCalcLoop(calc): while True: choice = input("Operation? ") if choice == 'q': - break # user types q to quit calulator. + break elif choice == 'add': - a, b = getTwoNumbers() - displayResult(calc.add(a, b)) + try: + a, b = getTwoNumbers() + displayResult(calc.add(a, b)) + except ValueError as e: + print(f"Error: {e}") elif choice == 'sub': - a, b = getTwoNumbers() - displayResult(calc.sub(a, b)) + try: + a, b = getTwoNumbers() + displayResult(calc.sub(a, b)) + except ValueError as e: + print(f"Error: {e}") elif choice == 'divide': - a, b = getTwoNumbers() - displayResult(calc.divide(a, b)) - if b == 0: - print("Cannot divide by zero.") + try: + a, b = getTwoNumbers() + displayResult(calc.divide(a, b)) + except ValueError as e: + print(f"Error: {e}") elif choice == 'sqrt': - a = getOneNumber() - displayResult(calc.sqrt(a)) - if a < 0: - print("Cannot take square root of negative number.") + try: + a = getOneNumber() + displayResult(calc.sqrt(a)) + except ValueError as e: + print(f"Error: {e}") elif choice == 'square': - a = getOneNumber() - displayResult(calc.square(a)) + try: + a = getOneNumber() + displayResult(calc.square(a)) + except ValueError as e: + print(f"Error: {e}") elif choice == 'exponent': - a, b = getTwoNumbers() - displayResult(calc.exponent(a, b)) + try: + a, b = getTwoNumbers() + displayResult(calc.exponent(a, b)) + except ValueError as e: + print(f"Error: {e}") elif choice == 'inverse': - a = getOneNumber() - displayResult(calc.inverse(a)) - if a == 0: - print("Cannot take inverse of zero.") + try: + a = getOneNumber() + displayResult(calc.inverse(a)) + except ValueError as e: + print(f"Error: {e}") elif choice == 'switchsign': - a = getOneNumber() - displayResult(calc.switchsign(a)) - if a == 0: - print("Cannot switch sign of zero.") - - + try: + a = getOneNumber() + displayResult(calc.switchsign(a)) + except ValueError as e: + print(f"Error: {e}") else: print("That is not a valid input.") - # main start def main(): calc = Calculator() performCalcLoop(calc) print("Done Calculating.") - if __name__ == '__main__': main() From d4911a71bc3dd3ee03530a2fb591c666b18e612b Mon Sep 17 00:00:00 2001 From: moclark710 Date: Mon, 13 Jul 2026 07:02:25 -0400 Subject: [PATCH 5/7] added startup and exit display messages --- main-app.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/main-app.py b/main-app.py index 84ec925..269d471 100644 --- a/main-app.py +++ b/main-app.py @@ -1,4 +1,26 @@ from calculator import Calculator +from datetime import datetime + +def display_title(): + now= datetime.now() + + print("=" *60) + print("Welcome Data General's Scientific Calculator") + print("=" *60) + print("Current date:", now.strftime("%B %D,%Y")) + print("Current time:", now.strftime("%H:%M:%S")) + print("=" *60) + +def display_end_title(): + print("=" * 60) + print(" Thank you for using Data General's") + print(" Scientific Calculator") + print() + print(" Until next time, goodbye!") + print("=" * 60) + + + print("Type 'q' to quit.\n") def getTwoNumbers(): a = float(input("first number? ")) @@ -70,9 +92,16 @@ def performCalcLoop(calc): # main start def main(): + display_title() + calc = Calculator() performCalcLoop(calc) + + display_end_title() + print("Done Calculating.") + if __name__ == '__main__': main() + From 8a51f4dbd99bfdc41df8edc40d54d4205e2d186f Mon Sep 17 00:00:00 2001 From: moclark710 Date: Mon, 13 Jul 2026 13:18:22 -0400 Subject: [PATCH 6/7] memomry changes, updated calc tests, final copy --- README.md | 5 ++++ calctests.py | 29 +++++++++++++++++++++++ calculator.py | 29 ++++++++++++++++++++--- main-app.py | 64 ++++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 116 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6445ffa..3390604 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,11 @@ In addition to the Core and Scientific features, you are required to create at least two of your own features for the calculator. They can be any two features that are not already covered and that you can implement as you see fit. These features must be properly tested. +This version includes two custom features: + +- Enter `tip` to calculate a tip, bill total, and amount each person owes. +- Enter `temp` to convert temperatures between Fahrenheit and Celsius. + ### Hints The following functions should take the displayed value (x) and updated it according to the given formula: (this may not be an exhaustive list) diff --git a/calctests.py b/calctests.py index 1964570..2abc377 100644 --- a/calctests.py +++ b/calctests.py @@ -20,6 +20,35 @@ def test_sub(self): c = Calculator() self.assertEqual(c.sub(9, 3), 6) + def test_calculate_tip(self): + c = Calculator() + self.assertEqual(c.calculate_tip(80, 20, 2), (16.0, 96.0, 48.0)) + + def test_fahrenheit_to_celsius(self): + c = Calculator() + self.assertEqual(c.fahrenheit_to_celsius(32), 0) + + def test_celsius_to_fahrenheit(self): + c = Calculator() + self.assertEqual(c.celsius_to_fahrenheit(100), 212) + + def test_memory_plus_and_recall(self): + c = Calculator() + c.state = 5 + c.M_plus() + c.state = 10 + c.M_plus() + + self.assertEqual(c.memory, 15) + self.assertEqual(c.MRC(), 15) + + def test_memory_clear(self): + c = Calculator() + c.memory = 12 + c.MC() + + self.assertEqual(c.memory, 0.0 ) + if __name__ == '__main__': unittest.main() diff --git a/calculator.py b/calculator.py index dc8c13e..f9e4c4f 100644 --- a/calculator.py +++ b/calculator.py @@ -1,6 +1,7 @@ class Calculator: def __init__(self): + self.state = 0.0 self.memory = 0.0 self.angle_mode = "DEG" @@ -37,6 +38,25 @@ def inverse(self, x): def switchsign(self, x): return x * -1 + def calculate_tip(self, bill_amount, tip_percent, people=1): + """Return the tip, total bill, and amount owed by each person.""" + if bill_amount < 0: + raise ValueError("Bill amount cannot be negative.") + if tip_percent < 0: + raise ValueError("Tip percentage cannot be negative.") + if people <= 0: + raise ValueError("Number of people must be at least 1.") + + tip = bill_amount * (tip_percent / 100) + total = bill_amount + tip + return round(tip, 2), round(total, 2), round(total / people, 2) + + def fahrenheit_to_celsius(self, fahrenheit): + return (fahrenheit - 32) * 5 / 9 + + def celsius_to_fahrenheit(self, celsius): + return celsius * 9 / 5 + 32 + def switchDisplayMode(self, mode): if mode == "DEG": self.angle_mode = "DEG" @@ -45,13 +65,16 @@ def switchDisplayMode(self, mode): else: raise ValueError("Invalid mode. Please choose 'DEG' or 'RAD'.") - def M(self, value): - self.memory = value + def M_plus(self): + self.memory += self.state + self.state = self.memory def MC(self): self.memory = 0.0 def MRC(self): - return self.memory + self.state = self.memory + return self.state + # add lots more methods to this calculator class. diff --git a/main-app.py b/main-app.py index 269d471..aff519f 100644 --- a/main-app.py +++ b/main-app.py @@ -34,62 +34,110 @@ def getOneNumber(): def displayResult(x: float): print(x, "\n") +def runTipCalculator(calc): + bill = float(input("Bill amount? $")) + percentage = float(input("Tip percentage? ")) + people = int(input("How many people are splitting the bill? ")) + tip, total, per_person = calc.calculate_tip(bill, percentage, people) + calc.state = total + print(f"Tip: ${tip:.2f}") + print(f"Total: ${total:.2f}") + print(f"Each person pays: ${per_person:.2f}\n") + def performCalcLoop(calc): while True: choice = input("Operation? ") if choice == 'q': break + elif choice == 'm+': + calc.M_plus() + displayResult(calc.state) + elif choice == 'mc': + calc.MC() + print("Memory cleared.\n") + elif choice == 'mrc': + displayResult(calc.MRC()) + elif choice == 'tip': + try: + runTipCalculator(calc) + except ValueError as e: + print(f"Error: {e}") + elif choice == 'temp': + try: + runTemperatureConverter(calc) + except ValueError as e: + print(f"Error: {e}") elif choice == 'add': try: a, b = getTwoNumbers() - displayResult(calc.add(a, b)) + calc.state = calc.add(a, b) + displayResult(calc.state) except ValueError as e: print(f"Error: {e}") elif choice == 'sub': try: a, b = getTwoNumbers() - displayResult(calc.sub(a, b)) + calc.state = calc.sub(a, b) + displayResult(calc.state) except ValueError as e: print(f"Error: {e}") elif choice == 'divide': try: a, b = getTwoNumbers() - displayResult(calc.divide(a, b)) + calc.state = calc.divide(a, b) + displayResult(calc.state) except ValueError as e: print(f"Error: {e}") elif choice == 'sqrt': try: a = getOneNumber() - displayResult(calc.sqrt(a)) + calc.state = calc.sqrt(a) + displayResult(calc.state) except ValueError as e: print(f"Error: {e}") elif choice == 'square': try: a = getOneNumber() - displayResult(calc.square(a)) + calc.state = calc.square(a) + displayResult(calc.state) except ValueError as e: print(f"Error: {e}") elif choice == 'exponent': try: a, b = getTwoNumbers() - displayResult(calc.exponent(a, b)) + calc.state = calc.exponent(a, b) + displayResult(calc.state) except ValueError as e: print(f"Error: {e}") elif choice == 'inverse': try: a = getOneNumber() - displayResult(calc.inverse(a)) + calc.state = calc.inverse(a) + displayResult(calc.state) except ValueError as e: print(f"Error: {e}") elif choice == 'switchsign': try: a = getOneNumber() - displayResult(calc.switchsign(a)) + calc.state = calc.switchsign(a) + displayResult(calc.state) except ValueError as e: print(f"Error: {e}") else: print("That is not a valid input.") +def runTemperatureConverter(calc): + unit = input("Convert from Fahrenheit or Celsius? (F/C) ").upper() + temperature = float(input("Temperature? ")) + if unit == "F": + calc.state = calc.fahrenheit_to_celsius(temperature) + print(f"{temperature:.2f}°F = {calc.state:.2f}°C\n") + elif unit == "C": + calc.state = calc.celsius_to_fahrenheit(temperature) + print(f"{temperature:.2f}°C = {calc.state:.2f}°F\n") + else: + raise ValueError("Please enter F or C.") + # main start def main(): display_title() From 6212446a2153d9222690148a2ef806e004228ff1 Mon Sep 17 00:00:00 2001 From: moclark710 Date: Mon, 13 Jul 2026 13:27:46 -0400 Subject: [PATCH 7/7] updated switch units and call for it in program --- calctests.py | 6 ++++++ calculator.py | 7 ++++++- main-app.py | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/calctests.py b/calctests.py index 2abc377..92252bc 100644 --- a/calctests.py +++ b/calctests.py @@ -48,6 +48,12 @@ def test_memory_clear(self): c.MC() self.assertEqual(c.memory, 0.0 ) + + def test_switch_units_mode(self): + c = Calculator() + c.switchUnitsMode("rad") + + self.assertEqual(c.angle_mode, "RAD") if __name__ == '__main__': diff --git a/calculator.py b/calculator.py index f9e4c4f..f5e6b4a 100644 --- a/calculator.py +++ b/calculator.py @@ -57,7 +57,8 @@ def fahrenheit_to_celsius(self, fahrenheit): def celsius_to_fahrenheit(self, celsius): return celsius * 9 / 5 + 32 - def switchDisplayMode(self, mode): + def switchUnitsMode(self, mode): + mode = mode.upper() if mode == "DEG": self.angle_mode = "DEG" elif mode == "RAD": @@ -65,6 +66,10 @@ def switchDisplayMode(self, mode): else: raise ValueError("Invalid mode. Please choose 'DEG' or 'RAD'.") + def switchDisplayMode(self, mode): + """Backward-compatible name for switching trig units.""" + self.switchUnitsMode(mode) + def M_plus(self): self.memory += self.state self.state = self.memory diff --git a/main-app.py b/main-app.py index aff519f..193adf5 100644 --- a/main-app.py +++ b/main-app.py @@ -57,6 +57,12 @@ def performCalcLoop(calc): print("Memory cleared.\n") elif choice == 'mrc': displayResult(calc.MRC()) + elif choice == 'deg': + calc.switchUnitsMode("DEG") + print("Angle mode: degrees\n") + elif choice == 'rad': + calc.switchUnitsMode("RAD") + print("Angle mode: radians\n") elif choice == 'tip': try: runTipCalculator(calc)