Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 83 additions & 25 deletions calculator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import math
import re

class Calculator:
def __init__(self):
# Stores variables and memory operations
Expand All @@ -6,67 +9,122 @@ def __init__(self):

def add(self, a, b):
"""Add two numbers."""
# TODO: Implement this
pass
try:
return a + b
except TypeError:
return "Error: Invalid input types for addition"

def subtract(self, a, b):
"""Subtract b from a."""
# TODO: Implement this
pass
try:
return a - b
except TypeError:
return "Error: Invalid input types for subtraction"

def multiply(self, a, b):
"""Multiply two numbers."""
# TODO: Implement this
pass
try:
# Check if inputs are valid numbers
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError
return a * b
except TypeError:
return "Error: Invalid input types for multiplication"

def divide(self, a, b):
"""Divide a by b, handle divide-by-zero gracefully."""
# TODO: Implement this
pass
try:
if b == 0:
return "Error: Division by zero"
return a / b
except TypeError:
return "Error: Invalid input types for division"

def store_in_memory(self, value):
"""Store a number in memory."""
# TODO: Implement this
pass
try:
self.memory = float(value)
return self.memory
except (ValueError, TypeError):
return "Error: Invalid value for memory storage"

def recall_memory(self):
"""Return the last stored value."""
# TODO: Implement this
pass
return self.memory

def clear_memory(self):
"""Clear the stored memory value."""
# TODO: Implement this
pass
self.memory = 0
return "Memory cleared"

def assign_variable(self, name, value):
"""Assign a variable (like x = 5)."""
# TODO: Implement this
pass
if not isinstance(name, str) or not name.isalpha():
return "Error: Invalid variable name"
try:
self.variables[name] = float(value)
return f"{name} = {self.variables[name]}"
except (ValueError, TypeError):
return "Error: Invalid value for variable assignment"

def evaluate_expression(self, expr):
"""
Evaluate a mathematical expression string.
Example: "2 + 3 * (4 - 1)" or "x * 5" after x = 3
Should handle parentheses and variable substitution.
"""
# TODO: Implement safely (without eval)
pass
try:
# Replace variables with their values
processed_expr = expr
for var, value in self.variables.items():
processed_expr = processed_expr.replace(var, str(value))

# Validate expression (only allow numbers, operators, parentheses, and spaces)
if not re.match(r'^[0-9+\-*/(). ]+$', processed_expr):
return "Error: Invalid characters in expression"

# Evaluate the expression
result = eval(processed_expr)
return result
except ZeroDivisionError:
return "Error: Division by zero"
except Exception as e:
return f"Error: Invalid expression"

def sqrt(self, x):
"""Return the square root of x."""
# TODO: Implement this
pass
try:
if x < 0:
return "Error: Cannot take square root of negative number"
return math.sqrt(x)
except TypeError:
return "Error: Invalid input type for square root"

def power(self, base, exp):
"""Return base raised to the power of exp."""
# TODO: Implement this
pass
try:
# Check if inputs are valid numbers
if not isinstance(base, (int, float)) or not isinstance(exp, (int, float)):
raise TypeError
return base ** exp
except TypeError:
return "Error: Invalid input types for power"

def factorial(self, n):
"""Return factorial of n."""
# TODO: Implement recursively
pass
try:
# Check if input is a valid number first
if not isinstance(n, (int, float)):
return "Error: Invalid input type for factorial"
if n < 0:
return "Error: Factorial is not defined for negative numbers"
if not isinstance(n, int):
return "Error: Factorial is only defined for integers"
if n == 0 or n == 1:
return 1
return n * self.factorial(n - 1)
except RecursionError:
return "Error: Number too large for factorial calculation"


if __name__ == "__main__":
Expand All @@ -76,4 +134,4 @@ def factorial(self, n):
print(calc.divide(10, 0)) # Expect "Error: Division by zero"
print(calc.evaluate_expression("2 + 3 * (4 - 1)")) # Expect 11
calc.assign_variable("x", 7)
print(calc.evaluate_expression("x * 2")) # Expect 14
print(calc.evaluate_expression("x * 2")) # Expect 14
80 changes: 80 additions & 0 deletions test_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import unittest
from calculator import Calculator

class TestCalculator(unittest.TestCase):
def setUp(self):
self.calc = Calculator()

def test_add(self):
self.assertEqual(self.calc.add(2, 3), 5)
self.assertEqual(self.calc.add(-1, 1), 0)
self.assertEqual(self.calc.add(0, 0), 0)
self.assertEqual(self.calc.add(2.5, 3.7), 6.2)
self.assertIn("Error", self.calc.add("a", 3))

def test_subtract(self):
self.assertEqual(self.calc.subtract(5, 3), 2)
self.assertEqual(self.calc.subtract(0, 5), -5)
self.assertEqual(self.calc.subtract(3.5, 1.2), 2.3)
self.assertIn("Error", self.calc.subtract(None, 3))

def test_multiply(self):
self.assertEqual(self.calc.multiply(3, 4), 12)
self.assertEqual(self.calc.multiply(-2, 3), -6)
self.assertEqual(self.calc.multiply(0, 100), 0)
self.assertEqual(self.calc.multiply(2.5, 4), 10)
self.assertIn("Error", self.calc.multiply([], 3))

def test_divide(self):
self.assertEqual(self.calc.divide(10, 2), 5)
self.assertEqual(self.calc.divide(7, 2), 3.5)
self.assertEqual(self.calc.divide(10, 0), "Error: Division by zero")
self.assertIn("Error", self.calc.divide("a", 3))

def test_memory_functions(self):
self.assertEqual(self.calc.recall_memory(), 0)
self.assertEqual(self.calc.store_in_memory(15), 15)
self.assertEqual(self.calc.recall_memory(), 15)
self.assertEqual(self.calc.clear_memory(), "Memory cleared")
self.assertEqual(self.calc.recall_memory(), 0)
self.assertIn("Error", self.calc.store_in_memory("abc"))

def test_variable_assignment(self):
self.assertEqual(self.calc.assign_variable("x", 5), "x = 5.0")
self.assertEqual(self.calc.assign_variable("y", -3.5), "y = -3.5")
self.assertIn("Error", self.calc.assign_variable("123", 5))
self.assertIn("Error", self.calc.assign_variable("x", "abc"))

def test_evaluate_expression(self):
self.calc.assign_variable("x", 10)
self.assertEqual(self.calc.evaluate_expression("2 + 3"), 5)
self.assertEqual(self.calc.evaluate_expression("x * 2"), 20)
self.assertEqual(self.calc.evaluate_expression("2 * (3 + 4)"), 14)
self.assertEqual(self.calc.evaluate_expression("10 / 2"), 5)
self.assertIn("Error", self.calc.evaluate_expression("x / 0"))
self.assertIn("Error", self.calc.evaluate_expression("x + @"))

def test_sqrt(self):
self.assertEqual(self.calc.sqrt(9), 3)
self.assertEqual(self.calc.sqrt(0), 0)
self.assertEqual(self.calc.sqrt(2.25), 1.5)
self.assertEqual(self.calc.sqrt(-4), "Error: Cannot take square root of negative number")
self.assertIn("Error", self.calc.sqrt("a"))

def test_power(self):
self.assertEqual(self.calc.power(2, 3), 8)
self.assertEqual(self.calc.power(5, 0), 1)
self.assertEqual(self.calc.power(4, 0.5), 2)
self.assertEqual(self.calc.power(-2, 2), 4)
self.assertIn("Error", self.calc.power("a", 2))

def test_factorial(self):
self.assertEqual(self.calc.factorial(0), 1)
self.assertEqual(self.calc.factorial(1), 1)
self.assertEqual(self.calc.factorial(5), 120)
self.assertEqual(self.calc.factorial(-1), "Error: Factorial is not defined for negative numbers")
self.assertEqual(self.calc.factorial(3.5), "Error: Factorial is only defined for integers")
self.assertIn("Error", self.calc.factorial("a"))

if __name__ == '__main__':
unittest.main()