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
48 changes: 48 additions & 0 deletions exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
class calculatorerror(Exception):
def __init__(self, msg="calculator error"):
self.msg = msg
super().__init__(self.msg)

class invalidinputerror(calculatorerror):
def __init__(self, msg="invalid input"):
super().__init__(msg)

class zerodenominatorerror(calculatorerror):
def __init__(self, msg="denominator cannot be zero"):
super().__init__(msg)

class invalidfractionerror(calculatorerror):
def __init__(self, msg="invalid fraction format"):
super().__init__(msg)

class invalidcomplexerror(calculatorerror):
def __init__(self, msg="invalid complex number"):
super().__init__(msg)

class invalidbinaryerror(calculatorerror):
def __init__(self, msg="invalid binary number"):
super().__init__(msg)

class invalidoctalerror(calculatorerror):
def __init__(self, msg="invalid octal number"):
super().__init__(msg)

class invalidhexerror(calculatorerror):
def __init__(self, msg="invalid hex number"):
super().__init__(msg)

class invalidseterror(calculatorerror):
def __init__(self, msg="invalid set format"):
super().__init__(msg)

class invalidmatrixerror(calculatorerror):
def __init__(self, msg="invalid matrix format"):
super().__init__(msg)

class dimensionmismatcherror(calculatorerror):
def __init__(self, msg="matrix dimensions dont match"):
super().__init__(msg)

class undefinedoperationerror(calculatorerror):
def __init__(self, msg="operation is undefined"):
super().__init__(msg)
74 changes: 74 additions & 0 deletions fraction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import math
from exceptions import zerodenominatorerror, invalidfractionerror


def parse_frac(s):
s = s.strip()
if '/' in s:
parts = s.split('/')
if len(parts) != 2:
raise invalidfractionerror(f"bad fraction: '{s}'")
try:
num = int(parts[0].strip())
den = int(parts[1].strip())
except ValueError:
raise invalidfractionerror(f"bad fraction: '{s}'")
else:
try:
num = int(s)
den = 1
except ValueError:
raise invalidfractionerror(f"bad fraction: '{s}'")
if den == 0:
raise zerodenominatorerror("denominator is zero")
return num, den


def simplify(num, den):
if num == 0:
return 0, 1
if den < 0:
num, den = -num, -den
g = math.gcd(abs(num), abs(den))
return num // g, den // g


def format_frac(num, den):
num, den = simplify(num, den)
if den == 1:
return str(num)
return f"{num}/{den}"


def add_fraction(a, b):
n1, d1 = parse_frac(a)
n2, d2 = parse_frac(b)
num = n1 * d2 + n2 * d1
den = d1 * d2
return format_frac(num, den)


def sub_fraction(a, b):
n1, d1 = parse_frac(a)
n2, d2 = parse_frac(b)
num = n1 * d2 - n2 * d1
den = d1 * d2
return format_frac(num, den)


def mul_fraction(a, b):
n1, d1 = parse_frac(a)
n2, d2 = parse_frac(b)
num = n1 * n2
den = d1 * d2
return format_frac(num, den)


def div_fraction(a, b):
n1, d1 = parse_frac(a)
n2, d2 = parse_frac(b)
if n2 == 0:
raise zerodenominatorerror("cant divide by zero fraction")
num = n1 * d2
den = d1 * n2
return format_frac(num, den)
68 changes: 68 additions & 0 deletions hex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from exceptions import invalidhexerror


def check_hex(h):
h = h.strip()
if not h:
raise invalidhexerror("empty hex string")
valid = set('0123456789ABCDEFabcdef')
for ch in h:
if ch not in valid:
raise invalidhexerror(f"invalid character: '{ch}'")


def hex_to_decimal(h):
h = h.strip()
check_hex(h)
return str(int(h, 16))


def decimal_to_hex(d):
num = int(d.strip())
if num < 0:
return '-' + hex(abs(num))[2:].upper()
return hex(num)[2:].upper()


def hex_add(a, b):
a, b = a.strip(), b.strip()
check_hex(a)
check_hex(b)
res = int(a, 16) + int(b, 16)
return hex(res)[2:].upper()


def hex_subtract(a, b):
a, b = a.strip(), b.strip()
check_hex(a)
check_hex(b)
res = int(a, 16) - int(b, 16)
if res < 0:
return '-' + hex(abs(res))[2:].upper()
return hex(res)[2:].upper()


def hex_multiply(a, b):
a, b = a.strip(), b.strip()
check_hex(a)
check_hex(b)
res = int(a, 16) * int(b, 16)
return hex(res)[2:].upper()


def fifteens_complement(h):
h = h.strip()
check_hex(h)
result = []
for ch in h.upper():
val = int(ch, 16)
result.append(hex(15 - val)[2:].upper())
return ''.join(result)


def sixteens_complement(h):
h = h.strip()
check_hex(h)
fifteens = fifteens_complement(h)
res = int(fifteens, 16) + 1
return hex(res)[2:].upper()
68 changes: 68 additions & 0 deletions test_fraction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import unittest
from fraction import add_fraction, sub_fraction, mul_fraction, div_fraction
from exceptions import zerodenominatorerror, invalidfractionerror


class testfraction(unittest.TestCase):

def test_fraction_add(self):
self.assertEqual(add_fraction('1/2', '1/4'), '3/4')

def test_fraction_add_same_denom(self):
self.assertEqual(add_fraction('1/5', '2/5'), '3/5')

def test_fraction_add_simplify(self):
self.assertEqual(add_fraction('1/4', '1/4'), '1/2')

def test_fraction_add_whole_result(self):
self.assertEqual(add_fraction('1/2', '1/2'), '1')

def test_fraction_add_negative(self):
self.assertEqual(add_fraction('-1/2', '1/4'), '-1/4')

def test_fraction_add_zero(self):
self.assertEqual(add_fraction('0/5', '3/7'), '3/7')

def test_fraction_sub(self):
self.assertEqual(sub_fraction('3/4', '1/4'), '1/2')

def test_fraction_sub_negative_result(self):
self.assertEqual(sub_fraction('1/4', '3/4'), '-1/2')

def test_fraction_sub_same(self):
self.assertEqual(sub_fraction('3/5', '3/5'), '0')

def test_fraction_mul(self):
self.assertEqual(mul_fraction('2/3', '3/4'), '1/2')

def test_fraction_mul_by_zero(self):
self.assertEqual(mul_fraction('2/3', '0/5'), '0')

def test_fraction_mul_whole_number(self):
self.assertEqual(mul_fraction('1/2', '2'), '1')

def test_fraction_div(self):
self.assertEqual(div_fraction('1/2', '1/4'), '2')

def test_fraction_div_result_fraction(self):
self.assertEqual(div_fraction('1/3', '2/3'), '1/2')

def test_fraction_div_by_zero(self):
with self.assertRaises(zerodenominatorerror):
div_fraction('1/2', '0/3')

def test_fraction_zero_denominator(self):
with self.assertRaises(zerodenominatorerror):
add_fraction('1/0', '1/2')

def test_fraction_invalid_format(self):
with self.assertRaises(invalidfractionerror):
add_fraction('abc', '1/2')

def test_fraction_invalid_format_multiple_slashes(self):
with self.assertRaises(invalidfractionerror):
add_fraction('1/2/3', '1/2')


if __name__ == "__main__":
unittest.main()
67 changes: 67 additions & 0 deletions test_hex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import unittest
from hex import (
hex_to_decimal, decimal_to_hex, hex_add,
hex_subtract, hex_multiply, fifteens_complement, sixteens_complement
)
from exceptions import invalidhexerror


class testhex(unittest.TestCase):

def test_hex_to_decimal(self):
self.assertEqual(hex_to_decimal('1A5'), '421')

def test_hex_to_decimal_zero(self):
self.assertEqual(hex_to_decimal('0'), '0')

def test_hex_to_decimal_single(self):
self.assertEqual(hex_to_decimal('F'), '15')

def test_hex_to_decimal_lowercase(self):
self.assertEqual(hex_to_decimal('ff'), '255')

def test_decimal_to_hex(self):
self.assertEqual(decimal_to_hex('243'), 'F3')

def test_decimal_to_hex_zero(self):
self.assertEqual(decimal_to_hex('0'), '0')

def test_decimal_to_hex_255(self):
self.assertEqual(decimal_to_hex('255'), 'FF')

def test_hex_add(self):
self.assertEqual(hex_add('1A', '0F'), '29')

def test_hex_add_with_carry(self):
self.assertEqual(hex_add('FF', '1'), '100')

def test_hex_subtract(self):
self.assertEqual(hex_subtract('1A', '0F'), 'B')

def test_hex_multiply(self):
self.assertEqual(hex_multiply('A', '2'), '14')

def test_fifteens_complement(self):
self.assertEqual(fifteens_complement('1A5'), 'E5A')

def test_fifteens_complement_all_f(self):
self.assertEqual(fifteens_complement('FFF'), '000')

def test_sixteens_complement(self):
self.assertEqual(sixteens_complement('1A5'), 'E5B')

def test_invalid_hex_char(self):
with self.assertRaises(invalidhexerror):
hex_to_decimal('1G5')

def test_invalid_hex_special(self):
with self.assertRaises(invalidhexerror):
hex_to_decimal('12#')

def test_invalid_hex_empty(self):
with self.assertRaises(invalidhexerror):
hex_to_decimal('')


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