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
Binary file added __pycache__/calculator.cpython-313.pyc
Binary file not shown.
Binary file added __pycache__/exceptions.cpython-313.pyc
Binary file not shown.
Binary file added __pycache__/fraction.cpython-313.pyc
Binary file not shown.
Binary file added __pycache__/test_calculator.cpython-313.pyc
Binary file not shown.
Binary file added __pycache__/test_fraction.cpython-313.pyc
Binary file not shown.
3 changes: 3 additions & 0 deletions calculator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@

from fraction import parse_fraction
from exceptions import InvalidInputError, InvalidFormatError
class Calculator:
# mode can be 1: Fraction, 2: Bin, 3: Oct, 4: Hex, 5: Set, 6: Matrix, default = 0
mode = 0
Expand Down
18 changes: 18 additions & 0 deletions exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class CalculatorError(Exception):
"""Base Calculator Exceptions"""
pass

class InvalidInputError(CalculatorError):
"""Raised when Input is Invalid"""
def __init__(self,message="Invalid Input"):
super().__init__(message)

class InvalidFormatError(CalculatorError):
"""Raised when Fraction Format is Invalid"""
def __init__(self,message="Invalid Format"):
super().__init__(message)

class InvalidFractionError(CalculatorError):
"""Raised when Fraction is Invalid"""
def __init__(self,message="Invalid Fraction"):
super().__init__(message)
82 changes: 82 additions & 0 deletions fraction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from exceptions import InvalidInputError, InvalidFormatError, InvalidFractionError


def parse_fraction(s):
if not isinstance(s, str):
raise InvalidInputError()

s = s.strip()

if '/' not in s:
raise InvalidFormatError()

parts = s.split('/')

if len(parts) != 2:
raise InvalidFormatError()

try:
num = int(parts[0])
den = int(parts[1])
except:
raise InvalidInputError()

return Fraction(num, den)
class Fraction:
def __init__(self, numerator, denominator):
if denominator == 0:
raise InvalidFractionError()

# normalize sign
if denominator < 0:
numerator = -numerator
denominator = -denominator

gcd = self._gcd(abs(numerator), abs(denominator))
self.numerator = numerator // gcd
self.denominator = denominator // gcd

def _gcd(self, a, b):
while b:
a, b = b, a % b
return a

def __add__(self, other):
if not isinstance(other, Fraction):
raise InvalidInputError()

num = self.numerator * other.denominator + other.numerator * self.denominator
den = self.denominator * other.denominator
return Fraction(num, den)

def __sub__(self, other):
if not isinstance(other, Fraction):
raise InvalidInputError()

num = self.numerator * other.denominator - other.numerator * self.denominator
den = self.denominator * other.denominator
return Fraction(num, den)

def __mul__(self, other):
if not isinstance(other, Fraction):
raise InvalidInputError()

num = self.numerator * other.numerator
den = self.denominator * other.denominator
return Fraction(num, den)

def __truediv__(self, other):
if not isinstance(other, Fraction):
raise InvalidInputError()

if other.numerator == 0:
raise InvalidFractionError()

num = self.numerator * other.denominator
den = self.denominator * other.numerator
return Fraction(num, den)

def __str__(self):
return f"{self.numerator}/{self.denominator}"


76 changes: 76 additions & 0 deletions test_fraction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import unittest
from fraction import parse_fraction, Fraction
from exceptions import InvalidInputError, InvalidFormatError, InvalidFractionError
from test_calculator import TestCalculator

class TestFractionParsing(unittest.TestCase):
def test_invalid_type(self):
f = Fraction(1, 2)
with self.assertRaises(InvalidInputError):
f + 5

def test_valid_parse(self):
self.assertEqual(str(parse_fraction("1/2")), "1/2")
self.assertEqual(str(parse_fraction(" 2/4 ")), "1/2") # simplification

def test_invalid_input_type(self):
with self.assertRaises(InvalidInputError):
parse_fraction(123)

def test_invalid_format(self):
with self.assertRaises(InvalidFormatError):
parse_fraction("123")

with self.assertRaises(InvalidFormatError):
parse_fraction("1//2")

with self.assertRaises(InvalidFormatError):
parse_fraction("1/2/3")

def test_invalid_numbers(self):
with self.assertRaises(InvalidInputError):
parse_fraction("a/b")

def test_zero_denominator(self):
with self.assertRaises(InvalidFractionError):
parse_fraction("5/0")


class TestFractionArithmetic(unittest.TestCase):

def test_addition(self):
self.assertEqual(str(parse_fraction("1/2") + parse_fraction("1/2")), "1/1")
self.assertEqual(str(parse_fraction("1/3") + parse_fraction("1/6")), "1/2")

def test_subtraction(self):
self.assertEqual(str(parse_fraction("3/4") - parse_fraction("1/4")), "1/2")
self.assertEqual(str(parse_fraction("1/2") - parse_fraction("1/2")), "0/1")

def test_multiplication(self):
self.assertEqual(str(parse_fraction("2/3") * parse_fraction("3/4")), "1/2")

def test_division(self):
self.assertEqual(str(parse_fraction("1/2") / parse_fraction("3/4")), "2/3")

def test_divide_by_zero_fraction(self):
with self.assertRaises(InvalidFractionError):
parse_fraction("1/2") / parse_fraction("0/5")


class TestEdgeCases(unittest.TestCase):

def test_negative_handling(self):
self.assertEqual(str(parse_fraction("1/-2")), "-1/2")
self.assertEqual(str(parse_fraction("-1/-2")), "1/2")
self.assertEqual(str(parse_fraction("-1/2")), "-1/2")

def test_zero_numerator(self):
self.assertEqual(str(parse_fraction("0/5")), "0/1")

def test_simplification(self):
self.assertEqual(str(parse_fraction("10/20")), "1/2")
self.assertEqual(str(parse_fraction("100/400")), "1/4")


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