diff --git a/README.md b/README.md index 32affe1..516c8d4 100644 --- a/README.md +++ b/README.md @@ -52,4 +52,4 @@ Contributions to this project are welcome. If you have any suggestions or would ## Future Developments -We will be working on adding features to work with trignometeric function, pmatrix options, and showing intermediate steps in n-dimensional matrix multiplication +We will be working on adding features to work with trignometeric function, pmatrix options, showing row-echelon forms in n-dimensional latex matrix forms, and cross product solutions diff --git a/calc.py b/calc.py index 238f63f..872845e 100644 --- a/calc.py +++ b/calc.py @@ -1,9 +1,13 @@ -from sympy import * -import math +""" +Main +""" +from sympy import Matrix, latex, pprint # create n numbers of n*n matrix def create_matrix(matrix_count, rows, columns): - + """ + Forms n number of new matrix with "a" rows and "b" columns + """ matrix_list = [] while matrix_count > 0: matrix = [] @@ -30,36 +34,41 @@ def create_matrix(matrix_count, rows, columns): return matrix_list def validate_input(): + """ + Validate input reprompts for the input if you miss inputting values + """ while True: - try: + try: matrix_count = int(input("No. of unique matrix undergoing operations: ")) if matrix_count > 0: return matrix_count - else: - print("ValueError: Invalid input. Please enter a positive number.") except ValueError: print("ValueError: Invalid input. Please enter a positive number.") def validate_rowcol(): + """ + Validates initializing the rows and columns of matrix + No negative inputs of rows or column values + """ while True: try: row = int(input("No. of rows: ")) col = int(input("No. of columns: ")) if (row > 0) and (col > 0): return row, col - elif (row < 0) or (col < 0): - row = int(input("No. of rows: ")) - col = int(input("No. of columns: ")) except ValueError: print("Invalid Input re-enter positive value") # sum and subtraction of matrix shape (m*n) def add_subtract_matrix(matrix_list): - + """ + Sum/Subtracts n numbers a*b matrix with same dimensions together + Additional feature for latex support + """ matrix_num = int(input("How many matrix participates in operation? ")) latex_operation = [] - # first index defines what is the dimension of matrix after operation, dimensions stays same through out + # first index defines what is the dimension of matrix after operation first_index = int(input("Left-most matrix index: ")) first_matrix = matrix_list[first_index] @@ -67,21 +76,20 @@ def add_subtract_matrix(matrix_list): # empty matrix initialize, print index on screen, and rows cols initialize operation_sum_sub = first_matrix - for _ in range(matrix_num - 1): - + for _ in range(matrix_num - 1): index = int(input("Index of matrix to the right: ")) - - operation_type = input("What operation do you want to do? Add - press A or a, Subtract - press S or s:: ") + operation_type = input("What operation? Add[A] Subtract[S]:: ").upper().strip() # check dimensions of the matrix - if (matrix_list[index].rows == first_matrix.rows) and (matrix_list[index].cols == first_matrix.cols): + if (matrix_list[index].rows == first_matrix.rows + and matrix_list[index].cols == first_matrix.cols): - if operation_type == "A" or operation_type == "a": + if operation_type == "A": operation_sum_sub += matrix_list[index] latex_operation.append("+") latex_operation.append(matrix_list[index]) - elif operation_type == "S" or operation_type == "s": + elif operation_type == "S": operation_sum_sub -= matrix_list[index] latex_operation.append("-") latex_operation.append(matrix_list[index]) @@ -96,52 +104,83 @@ def add_subtract_matrix(matrix_list): # dot product matrix shape (m*n) def dot_product_matrix(matrix_list): + """ + Dot product of n numbers of a*b matrix with proper dimension + Latex based output with intermediate steps and final solution + """ matrix_num = int(input("How many matrix participates in operation? ")) - + # latex print of all matrix for matrix in matrix_list: - with open("output.txt", "a") as f: - f.write("$"+ latex(matrix) + "$") + with open("output.txt", "a", encoding="utf-8") as output_file: + output_file.write("$"+ latex(matrix) + "$\n") - # first index defines what is the dimension of matrix after operation, dimensions stays same through out - first_matrix = matrix_list[int(input("Which will be your Rightmost matrix: "))] + with open("output.txt", "a", encoding="utf-8") as output_file: + output_file.write(r"\\By law of commutativity, dot product of matrices right to left, \\") - # initialize rightmost matrix - initialize_product = first_matrix + # first index defines what is the dimension of matrix after operation + initialize_product = matrix_list[int(input("Which will be your Rightmost matrix: "))] + # intermediate method print for _ in range(matrix_num-1): index = int(input("Inner matrix index: ")) + # intermediate steps + steps = ["="] + outer_product = initialize_product.T + _, symbol_count = outer_product.shape + outer_product = outer_product.tolist() + i = 0 + + for element in outer_product: + for atom, column in zip(element, range(matrix_list[index].cols)): + step = str(latex(atom)) + str(latex(matrix_list[index].col(column))) + steps.append(step) + i += 1 + if i % symbol_count != 0: + steps.append("+") + else: + steps.append("\\hspace{0.5cm}") + print() + # check dimensions of the matrix - if (matrix_list[index].cols == initialize_product.rows): + if matrix_list[index].cols == initialize_product.rows: initialize_product = matrix_list[index]*(initialize_product) else: print("Dimension out of range") + for _ in steps: + with open("output.txt", "a", encoding="utf-8") as output_file: + output_file.write("\n$" + str(_) + "$") + + print(steps) + return initialize_product - + def operations(): - while True: - operation_key = input("Operation key Product -> P and Sum/Subtract -> S: ") + """ + Choose matrix summation or product + """ + while True: + operation_key = input("Operation key Product or Sum/Subtract [P/S]: ").upper().strip() - if (operation_key == "S") or (operation_key == "P"): + if (operation_key in ["S", "P"]): return operation_key - else: - operation_key = input("Operation key Product -> P and Sum/Subtract -> S: ") -# intermediate steps devlopment - def main(): - # initialize new .txt file to be copied in LaTeX, future development targets user input to ask if "a" or "w" - with open("output.txt", "w") as f: - f.write("") + """ + Calling and Printing all the functions + """ + # initialize new .txt file for LaTeX, future development targets user input to ask if "a" or "w" + with open("output.txt", "w", encoding="utf-8") as output_file: + output_file.write("") # validate user values validated_count = validate_input() - validated_rows, validated_columns = validate_rowcol() + validated_rows, validated_columns = validate_rowcol() matrix_list = create_matrix(validated_count, validated_rows, validated_columns) @@ -150,28 +189,26 @@ def main(): pprint(en_matrix) operation_key = operations() - # what operation do you want to do? + if operation_key == "P": + # intermediate steps product = dot_product_matrix(matrix_list) pprint(product) # latex_dot_product(matrix_list) - with open("output.txt", "a") as f: - f.write("\n\n$="+ latex(product) + "$") - + with open("output.txt", "a", encoding="utf-8") as output_file: + output_file.write("\n\n$="+ latex(product) + "$") + elif operation_key == "S": sum_sub, latex_operation = add_subtract_matrix(matrix_list) pprint(sum_sub) for matrix in latex_operation: - with open("output.txt", "a") as f: - f.write("$"+ latex(matrix) + "$") - with open("output.txt", "a") as f: - f.write("\n\n$="+ latex(sum_sub) + "$") - + with open("output.txt", "a", encoding="utf-8") as output_file: + output_file.write("$"+ latex(matrix) + "$") + with open("output.txt", "a", encoding="utf-8") as output_file: + output_file.write("\n\n$="+ latex(sum_sub) + "$") + else: print("Error in input") - + if __name__ == "__main__": main() - - - diff --git a/output.txt b/output.txt index d48acbb..c6d75b6 100644 --- a/output.txt +++ b/output.txt @@ -1,3 +1,18 @@ -$\left[\begin{matrix}x & y & z\\a & b & c\\d & e & f\end{matrix}\right]$$\left[\begin{matrix}x & y & c\\a & b & c\\d & e & f\end{matrix}\right]$ +$\left[\begin{matrix}2 & 3\\4 & 3\\5 & 2\end{matrix}\right]$ +$\left[\begin{matrix}4 & 5 & 6\\4 & 3 & 2\end{matrix}\right]$ +\\By law of commutativity, dot product of matrices right to left, \\ +$=$ +$2\left[\begin{matrix}4\\4\end{matrix}\right]$ +$+$ +$4\left[\begin{matrix}5\\3\end{matrix}\right]$ +$+$ +$5\left[\begin{matrix}6\\2\end{matrix}\right]$ +$\hspace{0.5cm}$ +$3\left[\begin{matrix}4\\4\end{matrix}\right]$ +$+$ +$3\left[\begin{matrix}5\\3\end{matrix}\right]$ +$+$ +$2\left[\begin{matrix}6\\2\end{matrix}\right]$ +$\hspace{0.5cm}$ -$=\left[\begin{matrix}a y + c d + x^{2} & b y + c e + x y & c f + c y + x z\\a b + a x + c d & a y + b^{2} + c e & a z + b c + c f\\a e + d f + d x & b e + d y + e f & c e + d z + f^{2}\end{matrix}\right]$ \ No newline at end of file +$=\left[\begin{matrix}58 & 39\\30 & 25\end{matrix}\right]$ \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e797554..667447f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -sympy==1.12 -numpy==1.24.3 -pytest -untitest.mock +sympy==1.12 +numpy==1.24.3 +pytest +untitest.mock math \ No newline at end of file diff --git a/test_calc/test_calc.py b/test_calc/test_calc.py index f538c5a..a22480f 100644 --- a/test_calc/test_calc.py +++ b/test_calc/test_calc.py @@ -1,27 +1,51 @@ +""" +Unit testing +""" # standard imports -import pytest -import os, sys -from sympy import Matrix +import os from unittest.mock import patch -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) +import sys +import pytest +from sympy import Matrix -# local import + +# local importi from calc import create_matrix, add_subtract_matrix, dot_product_matrix +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) + # passing the mocked input in the decorator itself to define second matrix -@pytest.mark.parametrize("test_input,expected", [(["1","2","2","3","3","4","3","2","1","2","2","3","3","4"], [Matrix([[1,2], [2,3], [3,4]]), Matrix([[1,2], [2,3], [3,4]])])]) +@pytest.mark.parametrize("test_input,expected", + [(["1","2","2","3","3","4","3","2","1","2","2","3","3","4"], + [Matrix([[1,2], [2,3], [3,4]]), Matrix([[1,2], [2,3], [3,4]])])]) def test_create_matrix(test_input, expected): + """ + Unit testing creating_matrix + """ with patch("builtins.input", side_effect=test_input): assert create_matrix(2, 3, 2) == expected -@pytest.mark.parametrize("test_input,expected", [(["1","3","4","2","2","2","1","4","5","2","2","0","1"], Matrix([[17,11],[13,19]]))]) +@pytest.mark.parametrize("test_input,expected", + [(["1","3","4","2","2","2","1","4","5","2","2","0","1"], + Matrix([[17,11],[13,19]]))]) def test_dot_product_matrix(test_input,expected): + """ + Unit testing of dot_product_matrix + """ with patch("builtins.input", side_effect=test_input): matrix = create_matrix(2,2,2) assert dot_product_matrix(matrix) == expected -@pytest.mark.parametrize("test_input,expected", [(["1","2","3","4","5","6","2","3","1","2","3","4","5","6","2","0","1","A"], (Matrix([[2,4,6],[8,10,12]]), [Matrix([[1,2,3],[4,5,6]]), "+", Matrix([[1,2,3],[4,5,6]])]))]) +@pytest.mark.parametrize("test_input,expected", + [(["1","2","3","4","5","6","2","3","1","2","3", + "4","5","6","2","0","1","A"], + (Matrix([[2,4,6],[8,10,12]]), + [Matrix([[1,2,3],[4,5,6]]), "+", + Matrix([[1,2,3],[4,5,6]])]))]) def test_add_subtract_matrix(test_input,expected): + """ + Unit testing of add subtract matrix + """ with patch("builtins.input", side_effect=test_input): matrix = create_matrix(2,2,3) assert add_subtract_matrix(matrix) == expected