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)
128 changes: 128 additions & 0 deletions matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import json
from exceptions import invalidmatrixerror, dimensionmismatcherror


def parse_matrix(s):
s = s.strip()
try:
mat = json.loads(s)
except (json.JSONDecodeError, ValueError):
raise invalidmatrixerror(f"cant parse matrix: '{s}'")

if not isinstance(mat, list) or not mat:
raise invalidmatrixerror("matrix must be a non-empty list of lists")

for row in mat:
if not isinstance(row, list):
raise invalidmatrixerror("each row must be a list")
for elem in row:
if not isinstance(elem, (int, float)):
raise invalidmatrixerror(f"element must be numeric, got: {elem}")

row_len = len(mat[0])
for i, row in enumerate(mat):
if len(row) != row_len:
raise invalidmatrixerror(
f"row 0 has {row_len} cols but row {i} has {len(row)}"
)

return mat


def format_matrix(mat):
rows = []
for row in mat:
formatted = []
for val in row:
if isinstance(val, float) and val == int(val):
formatted.append(str(int(val)))
else:
formatted.append(str(val))
rows.append('[' + ','.join(formatted) + ']')
return '[' + ','.join(rows) + ']'


def get_dims(mat):
r = len(mat)
c = len(mat[0]) if r > 0 else 0
return r, c


def add(a, b):
m1 = parse_matrix(a)
m2 = parse_matrix(b)
r1, c1 = get_dims(m1)
r2, c2 = get_dims(m2)

if r1 != r2 or c1 != c2:
raise dimensionmismatcherror(
f"cant add ({r1}x{c1}) and ({r2}x{c2})"
)

result = []
for i in range(r1):
row = []
for j in range(c1):
row.append(m1[i][j] + m2[i][j])
result.append(row)

return format_matrix(result)


def subtract(a, b):
m1 = parse_matrix(a)
m2 = parse_matrix(b)
r1, c1 = get_dims(m1)
r2, c2 = get_dims(m2)

if r1 != r2 or c1 != c2:
raise dimensionmismatcherror(
f"cant subtract ({r1}x{c1}) and ({r2}x{c2})"
)

result = []
for i in range(r1):
row = []
for j in range(c1):
row.append(m1[i][j] - m2[i][j])
result.append(row)

return format_matrix(result)


def multiply(a, b):
m1 = parse_matrix(a)
m2 = parse_matrix(b)
r1, c1 = get_dims(m1)
r2, c2 = get_dims(m2)

if c1 != r2:
raise dimensionmismatcherror(
f"cant multiply: cols of first ({c1}) != rows of second ({r2})"
)

result = []
for i in range(r1):
row = []
for j in range(c2):
total = 0
for k in range(c1):
total += m1[i][k] * m2[k][j]
row.append(total)
result.append(row)

return format_matrix(result)


def transpose(a):
m = parse_matrix(a)
rows, cols = get_dims(m)

result = []
for j in range(cols):
row = []
for i in range(rows):
row.append(m[i][j])
result.append(row)

return format_matrix(result)
63 changes: 63 additions & 0 deletions octal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from exceptions import invalidoctalerror


def check_octal(o):
o = o.strip()
if not o:
raise invalidoctalerror("empty octal string")
for ch in o:
if ch not in '01234567':
raise invalidoctalerror(f"invalid digit: '{ch}'")


def octal_to_decimal(o):
o = o.strip()
check_octal(o)
return str(int(o, 8))


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


def octal_add(a, b):
a, b = a.strip(), b.strip()
check_octal(a)
check_octal(b)
res = int(a, 8) + int(b, 8)
return oct(res)[2:]


def octal_subtract(a, b):
a, b = a.strip(), b.strip()
check_octal(a)
check_octal(b)
res = int(a, 8) - int(b, 8)
if res < 0:
return '-' + oct(abs(res))[2:]
return oct(res)[2:]


def octal_multiply(a, b):
a, b = a.strip(), b.strip()
check_octal(a)
check_octal(b)
res = int(a, 8) * int(b, 8)
return oct(res)[2:]


def sevens_complement(o):
o = o.strip()
check_octal(o)
return ''.join(str(7 - int(d)) for d in o)


def eights_complement(o):
o = o.strip()
check_octal(o)
sevens = sevens_complement(o)
res = int(sevens, 8) + 1
return oct(res)[2:]
82 changes: 82 additions & 0 deletions test_matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import unittest
from matrix import add, subtract, multiply, transpose
from exceptions import invalidmatrixerror, dimensionmismatcherror


class testmatrix(unittest.TestCase):

def test_matrix_add(self):
res = add('[[1,2],[3,4]]', '[[5,6],[7,8]]')
self.assertEqual(res, '[[6,8],[10,12]]')

def test_matrix_add_single_element(self):
res = add('[[1]]', '[[2]]')
self.assertEqual(res, '[[3]]')

def test_matrix_add_negative(self):
res = add('[[1,2],[3,4]]', '[[-1,-2],[-3,-4]]')
self.assertEqual(res, '[[0,0],[0,0]]')

def test_matrix_add_dimension_mismatch(self):
with self.assertRaises(dimensionmismatcherror):
add('[[1,2]]', '[[1,2],[3,4]]')

def test_matrix_subtract(self):
res = subtract('[[5,6],[7,8]]', '[[1,2],[3,4]]')
self.assertEqual(res, '[[4,4],[4,4]]')

def test_matrix_subtract_to_zero(self):
res = subtract('[[3,4],[5,6]]', '[[3,4],[5,6]]')
self.assertEqual(res, '[[0,0],[0,0]]')

def test_matrix_subtract_dimension_mismatch(self):
with self.assertRaises(dimensionmismatcherror):
subtract('[[1,2,3]]', '[[1,2]]')

def test_matrix_multiply(self):
res = multiply('[[1,2],[3,4]]', '[[5,6],[7,8]]')
self.assertEqual(res, '[[19,22],[43,50]]')

def test_matrix_multiply_identity(self):
res = multiply('[[1,0],[0,1]]', '[[5,6],[7,8]]')
self.assertEqual(res, '[[5,6],[7,8]]')

def test_matrix_multiply_rectangular(self):
res = multiply('[[1,2,3]]', '[[4],[5],[6]]')
self.assertEqual(res, '[[32]]')

def test_matrix_multiply_dimension_mismatch(self):
with self.assertRaises(dimensionmismatcherror):
multiply('[[1,2]]', '[[1,2]]')

def test_matrix_transpose(self):
res = transpose('[[1,2],[3,4]]')
self.assertEqual(res, '[[1,3],[2,4]]')

def test_matrix_transpose_rectangular(self):
res = transpose('[[1,2,3],[4,5,6]]')
self.assertEqual(res, '[[1,4],[2,5],[3,6]]')

def test_matrix_transpose_single_row(self):
res = transpose('[[1,2,3]]')
self.assertEqual(res, '[[1],[2],[3]]')

def test_matrix_transpose_single_col(self):
res = transpose('[[1],[2],[3]]')
self.assertEqual(res, '[[1,2,3]]')

def test_invalid_matrix_format(self):
with self.assertRaises(invalidmatrixerror):
add('not_a_matrix', '[[1,2]]')

def test_invalid_matrix_non_numeric(self):
with self.assertRaises(invalidmatrixerror):
add('[["a","b"]]', '[[1,2]]')

def test_invalid_matrix_inconsistent_rows(self):
with self.assertRaises(invalidmatrixerror):
add('[[1,2],[3]]', '[[1,2],[3,4]]')


if __name__ == "__main__":
unittest.main()
68 changes: 68 additions & 0 deletions test_octal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import unittest
from octal import (
octal_to_decimal, decimal_to_octal, octal_add,
octal_subtract, octal_multiply, sevens_complement, eights_complement
)
from exceptions import invalidoctalerror


class testoctal(unittest.TestCase):

def test_octal_to_decimal(self):
self.assertEqual(octal_to_decimal('247'), '167')

def test_octal_to_decimal_zero(self):
self.assertEqual(octal_to_decimal('0'), '0')

def test_octal_to_decimal_seven(self):
self.assertEqual(octal_to_decimal('7'), '7')

def test_decimal_to_octal(self):
self.assertEqual(decimal_to_octal('167'), '247')

def test_decimal_to_octal_zero(self):
self.assertEqual(decimal_to_octal('0'), '0')

def test_decimal_to_octal_eight(self):
self.assertEqual(decimal_to_octal('8'), '10')

def test_octal_add(self):
self.assertEqual(octal_add('12', '15'), '27')

def test_octal_add_with_carry(self):
self.assertEqual(octal_add('77', '1'), '100')

def test_octal_subtract(self):
self.assertEqual(octal_subtract('27', '15'), '12')

def test_octal_multiply(self):
self.assertEqual(octal_multiply('3', '4'), '14')

def test_sevens_complement(self):
self.assertEqual(sevens_complement('247'), '530')

def test_sevens_complement_zero(self):
self.assertEqual(sevens_complement('000'), '777')

def test_eights_complement(self):
self.assertEqual(eights_complement('247'), '531')

def test_invalid_octal_digit_8(self):
with self.assertRaises(invalidoctalerror):
octal_to_decimal('128')

def test_invalid_octal_digit_9(self):
with self.assertRaises(invalidoctalerror):
octal_to_decimal('19')

def test_invalid_octal_letters(self):
with self.assertRaises(invalidoctalerror):
octal_to_decimal('12A')

def test_invalid_octal_empty(self):
with self.assertRaises(invalidoctalerror):
octal_to_decimal('')


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