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
32 changes: 32 additions & 0 deletions calculator.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,43 @@
from set import SetOperations


class Calculator:
def __init__(self):
self.set_ops = SetOperations()

def add(self, a, b):
return a + b

def subtract(self, a, b):
return a - b

def multiply(self, a, b):
return a * b

def divide(self, a, b):
if b == 0:
raise ValueError("Division by zero")
return a / b

def set_union(self, left, right):
return self.set_ops.union(left, right)

def set_intersection(self, left, right):
return self.set_ops.intersection(left, right)

def set_difference(self, left, right):
return self.set_ops.difference(left, right)

def set_symmetric_difference(self, left, right):
return self.set_ops.symmetric_difference(left, right)

def set_is_subset(self, left, right):
return self.set_ops.is_subset(left, right)

def set_is_superset(self, left, right):
return self.set_ops.is_superset(left, right)

def evaluate(self, expression, mode="set"):
if mode != "set":
raise ValueError("Only set mode is supported in this repository state")
return self.set_ops.evaluate_expression(expression)
69 changes: 69 additions & 0 deletions set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import ast


class SetOperations:
def parse_set(self, value):
if isinstance(value, set):
return value

if not isinstance(value, str):
raise ValueError("Set input must be a string or set")

text = value.strip()
if text == "{}":
return set()

try:
parsed = ast.literal_eval(text)
except (SyntaxError, ValueError) as exc:
raise ValueError("Invalid set input") from exc

if isinstance(parsed, (set, list, tuple)):
return set(parsed)

raise ValueError("Set input must be a set, list, or tuple")

def union(self, left, right):
return self.parse_set(left) | self.parse_set(right)

def intersection(self, left, right):
return self.parse_set(left) & self.parse_set(right)

def difference(self, left, right):
return self.parse_set(left) - self.parse_set(right)

def symmetric_difference(self, left, right):
return self.parse_set(left) ^ self.parse_set(right)

def is_subset(self, left, right):
return self.parse_set(left).issubset(self.parse_set(right))

def is_superset(self, left, right):
return self.parse_set(left).issuperset(self.parse_set(right))

def evaluate_expression(self, expression):
if not isinstance(expression, str) or not expression.strip():
raise ValueError("Expression must be a non-empty string")

text = expression.strip()
lowered = text.lower()

operations = [
(" union ", self.union),
(" intersection ", self.intersection),
(" difference ", self.difference),
(" symdiff ", self.symmetric_difference),
(" subset ", self.is_subset),
(" superset ", self.is_superset),
]

for marker, function in operations:
if marker in lowered:
idx = lowered.index(marker)
left = text[:idx].strip()
right = text[idx + len(marker):].strip()
if not left or not right:
raise ValueError("Invalid set expression")
return function(left, right)

raise ValueError("Invalid set expression")
46 changes: 40 additions & 6 deletions test_calculator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest
from calculator import Calculator


class TestCalculator(unittest.TestCase):
# base test cases
def setUp(self):
Expand All @@ -18,16 +19,49 @@ def test_multiply(self):
def test_divide(self):
self.assertEqual(self.calc.divide(2, 4), 0.5)

def test_divide(self):
def test_divide_negative(self):
self.assertEqual(self.calc.divide(4, -2), -2)
def test_divide_fail(self): # this will fail

def test_divide_fail(self):
self.assertNotEqual(self.calc.divide(4, -2), 2)

def test_divide_by_zero(self):
with self.assertRaises(ValueError):
self.calc.divide(5, 0)

# Optional: this allows running the script directly
if __name__ == '__main__':
unittest.main() #
# set operation test cases
def test_set_union(self):
self.assertEqual(self.calc.set_union("{1,2}", "{2,3}"), {1, 2, 3})

def test_set_intersection(self):
self.assertEqual(self.calc.set_intersection("{1,2,3}", "{2,4}"), {2})

def test_set_difference(self):
self.assertEqual(self.calc.set_difference("{1,2,3}", "{2}"), {1, 3})

def test_set_symmetric_difference(self):
self.assertEqual(self.calc.set_symmetric_difference("{1,2,3}", "{2,4}"), {1, 3, 4})

def test_set_subset(self):
self.assertTrue(self.calc.set_is_subset("{1,2}", "{1,2,3}"))

def test_set_superset(self):
self.assertTrue(self.calc.set_is_superset("{1,2,3}", "{1,2}"))

def test_set_empty_set_boundary(self):
self.assertEqual(self.calc.set_union("{}", "{1,2}"), {1, 2})

def test_set_invalid_input(self):
with self.assertRaises(ValueError):
self.calc.set_union("{1,2}", "not_a_set")

def test_set_expression_union(self):
self.assertEqual(self.calc.evaluate("{1,2} union {2,3}"), {1, 2, 3})

def test_set_expression_invalid_operator(self):
with self.assertRaises(ValueError):
self.calc.evaluate("{1,2} merge {2,3}")


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