diff --git a/.github/workflows/pipeline.yaml b/.github/workflows/pipeline.yaml new file mode 100644 index 0000000..bb29a05 --- /dev/null +++ b/.github/workflows/pipeline.yaml @@ -0,0 +1,28 @@ +name: Test Matrix + +on: + push: + branches: [ "dev-refactor", "dev", "master" ] + pull_request: + branches: [ "dev-refactor", "dev", "master" ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Simple Python setup + uses: actions/setup-python@v3 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run tests + run: | + python -m pytest tests/ -v -s \ No newline at end of file diff --git a/.gitignore b/.gitignore index e52acf8..2108fd4 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ Thumbs.db *.ppt *.docx +*.zip diff --git a/app.py b/app.py index c8ce4d4..013246c 100644 --- a/app.py +++ b/app.py @@ -3,414 +3,164 @@ app = Flask(__name__) -@app.route('/') -def index(): - """ - Página inicial/menu. - Irá renderizar o template 'index.html'. - """ - return render_template('index.html') +# Reader Function -#Soma&Subtracao -@app.route('/sum', methods=['GET', 'POST']) -def sum_sub(): +def handle_matrix_operation(operation_func, *args, **spargs): """ - Soma/Subtração de matrizes. - No POST, processa o cálculo. No GET, mostra o formulário. + Generic handler for matrix operations. + Catches errors and returns proper JSON response. """ - if request.method == 'POST': - try: - if not request.data: - return jsonify({'error': 'No data received'}), 400 - - data = request.get_json(force=True) - - if not data: - return jsonify({'error': 'Invalid JSON'}), 400 - - if 'rows' not in data or 'cols' not in data: - return jsonify({'error': 'Dimensões não fornecidas'}), 400 - - if 'matrix_a' not in data or 'matrix_b' not in data: - return jsonify({'error': 'Matrizes não fornecidas'}), 400 - - matrix_a = Matrix(data['rows'], data['cols'], data['matrix_a']) - matrix_b = Matrix(data['rows'], data['cols'], data['matrix_b']) - - result = matrix_a.add(matrix_b) if data['operation'] == 'sum' else matrix_a.subtract(matrix_b) - op_name = 'Soma' if data['operation'] == 'sum' else 'Subtração' - - return jsonify({ - 'matrix_a': matrix_a.to_list(), - 'matrix_b': matrix_b.to_list(), - 'result': result['data'], - 'operation': op_name, - 'dimensions': f"{data['rows']}x{data['cols']}" - }) + try: + result = operation_func(*args, **spargs) + + # If result is a Matrix object, convert to list + if isinstance(result, Matrix): + return jsonify({'result': result.to_list()}) + # if result is already a dict, return it as is + elif isinstance(result, dict): + return jsonify(result) + # else return normal result + else: + return jsonify({'result': result}) - except Exception as e: - return jsonify({'error': str(e)}), 400 - - return render_template('sum_sub.html') + except ValueError as e: + return jsonify({'error': str(e)}), 400 + except Exception as e: + return jsonify({'error': f'Erro: {str(e)}'}), 500 -@app.route('/scalar', methods=['GET', 'POST']) -def scalar(): - """ - Multiplicação de matriz por escalar. - """ - if request.method == "POST": - try: - if not request.data: - return jsonify({'error': 'No data received'}), 400 - data = request.get_json(force=True) - print(data) - if not data: - return jsonify({'error': 'Invalid JSON'}), 400 - - if 'rows' not in data or 'cols' not in data: - return jsonify({'error': 'Dimensões não fornecidas'}), 400 - - if 'matrix_a' not in data: - return jsonify({'error': 'Matriz não fornecida'}), 400 - - if 'scalar' not in data: - return jsonify({'error': 'Escalar não fornecido'}), 400 - - matrix_a = Matrix(data['rows'], data['cols'], data['matrix_a']) - result = matrix_a.scalar_multiply(data['scalar']) +# App Routes - return jsonify({ - 'matrix_a': matrix_a.to_list(), - 'result': result['data'], - 'dimensions': f"{data['rows']}x{data['cols']}" - }) - - except Exception as e: - return jsonify({'error': str(e)}), 400 - return render_template('scalar.html') +@app.route('/') +def index(): + """Página inicial/menu.""" + return render_template('index.html') + +@app.route('/sum-sub', methods=['GET', 'POST']) +def sum_sub(): + """Soma/Subtração de matrizes.""" + if request.method == 'GET': + return render_template('sum_sub.html') + + data = request.get_json() + matrix_a = Matrix(data['rows'], + data['cols'], + data['matrix_a']) + + matrix_b = Matrix(data['rows'], + data['cols'], + data['matrix_b']) + + if data['operation'] == 'add': + return handle_matrix_operation(matrix_a.add, matrix_b) + elif data['operation'] == 'subtract': + return handle_matrix_operation(matrix_a.subtract, matrix_b) + else: + return jsonify({'error': 'Operação inválida'}), 400 -#Multiplicacao @app.route('/multiply', methods=['GET', 'POST']) def multiply(): - if request.method == 'POST': - try: - - data = request.get_json(force=True) - - if not data: - return jsonify({'error': 'Invalid JSON'}), 400 - - if 'matrix_a' not in data or 'matrix_b' not in data: - return jsonify({'error': 'Matrizes não fornecidas'}), 400 - - matrix_a_data = data['matrix_a'] - matrix_b_data = data['matrix_b'] - - rows_a = len(matrix_a_data) - cols_a = len(matrix_a_data[0]) if matrix_a_data else 0 - rows_b = len(matrix_b_data) - cols_b = len(matrix_b_data[0]) if matrix_b_data else 0 - if cols_a != rows_b: - return jsonify({ - 'error': f'Matrizes incompatíveis: A.cols({cols_a}) ≠ B.rows({rows_b})' - }), 400 - - matrix_a = Matrix(rows_a, cols_a, matrix_a_data) - matrix_b = Matrix(rows_b, cols_b, matrix_b_data) - - result = matrix_a.multiply(matrix_b) - - return jsonify({ - 'matrix_a': matrix_a.to_list(), - 'matrix_b': matrix_b.to_list(), - 'result': result.to_list(), - }) - - except Exception as e: - return jsonify({'error': str(e)}), 400 + """Multiplicação de matrizes.""" + if request.method == 'GET': + return render_template('multiply.html') + + data = request.get_json() + matrix_a = Matrix(data['rows_a'], + data['cols_a'], + data['matrix_a']) + + matrix_b = Matrix(data['rows_b'], + data['cols_b'], + data['matrix_b']) - return render_template('multiply.html') + return handle_matrix_operation(matrix_a.multiply, matrix_b) -@app.route('/determinant', methods=['GET', 'POST']) +@app.route('/transpose', methods=['GET', 'POST']) +def transpose(): + """Transpor uma Matriz""" + if request.method == 'GET': + return render_template('transpose.html') + + data = request.get_json() + matrix = Matrix(data['rows'], + data['cols'], + data['matrix']) + + return handle_matrix_operation(matrix.transpose) +@app.route('/scalar', methods=['GET', 'POST']) +def scalar(): + """Multiplicação de matriz por escalar.""" + if request.method == 'GET': + return render_template('scalar.html') + + data = request.get_json() + matrix = Matrix(data['rows'], + data['cols'], + data['matrix']) + + return handle_matrix_operation(matrix.scalar_multiply, data['scalar']) + +@app.route('/determinant', methods=['GET', 'POST']) def determinant(): - if request.method == 'POST': - try: - data = request.get_json(force=True) - - if not data: - return jsonify({'error': 'Invalid JSON'}), 400 - - if 'size' not in data or 'matrix' not in data: - return jsonify({'error': 'Tamanho ou matriz não fornecidos'}), 400 - - size = data['size'] - matrix_data = data['matrix'] - method = data.get('method', 'auto') - - # Validar se é matriz quadrada - if len(matrix_data) != size or any(len(row) != size for row in matrix_data): - return jsonify({'error': 'A matriz deve ser quadrada'}), 400 - - # Criar objeto Matrix - matrix = Matrix(size, size, matrix_data) - - if not matrix.is_square(): - return jsonify({'error': 'Determinante só existe para matrizes quadradas'}), 400 - - # Calcular determinante - try: - determinant_value = matrix.determinant() - - # Arredondar para evitar -0.0000000000001 - if abs(determinant_value) < 1e-10: - determinant_value = 0.0 - - result = { - 'matrix': matrix.to_list(), - 'determinant': determinant_value, - 'size': size, - 'method': method, - 'is_singular': determinant_value == 0 - } - - # Adicionar detalhes do método se for pequena - if size <= 3: - if size == 2: - a, b = matrix_data[0][0], matrix_data[0][1] - c, d = matrix_data[1][0], matrix_data[1][1] - result['method_details'] = f"2×2: det = ad - bc = ({a}×{d}) - ({b}×{c}) = {a*d} - {b*c} = {determinant_value}" - elif size == 3: - a, b, c = matrix_data[0][0], matrix_data[0][1], matrix_data[0][2] - d, e, f = matrix_data[1][0], matrix_data[1][1], matrix_data[1][2] - g, h, i = matrix_data[2][0], matrix_data[2][1], matrix_data[2][2] - result['method_details'] = f"3×3 (Sarrus):\n" - result['method_details'] += f" (a*e*i + b*f*g + c*d*h) - (c*e*g + b*d*i + a*f*h)\n" - result['method_details'] += f" = ({a}×{e}×{i} + {b}×{f}×{g} + {c}×{d}×{h}) - ({c}×{e}×{g} + {b}×{d}×{i} + {a}×{f}×{h})\n" - result['method_details'] += f" = ({a*e*i} + {b*f*g} + {c*d*h}) - ({c*e*g} + {b*d*i} + {a*f*h})\n" - result['method_details'] += f" = {determinant_value}" - - return jsonify(result) - - except Exception as e: - return jsonify({'error': f'Erro ao calcular determinante: {str(e)}'}), 400 - - except Exception as e: - return jsonify({'error': str(e)}), 400 + """Cálculo de determinante.""" + if request.method == 'GET': + return render_template('determinant.html') + + data = request.get_json() + matrix = Matrix(data['size'], + data['size'], + data['matrix']) - return render_template('determinant.html') + return handle_matrix_operation(matrix.determinant) @app.route('/inverse', methods=['GET', 'POST']) def inverse(): - if request.method == 'POST': - try: - data = request.get_json(force=True) - - if not data: - return jsonify({'error': 'Invalid JSON'}), 400 - - if 'size' not in data or 'matrix' not in data: - return jsonify({'error': 'Tamanho ou matriz não fornecidos'}), 400 - - size = data['size'] - matrix_data = data['matrix'] - method = data.get('method', 'adjugate') # 'adjugate' ou 'gauss-jordan' - - # Validar se é matriz quadrada - if len(matrix_data) != size or any(len(row) != size for row in matrix_data): - return jsonify({'error': 'A matriz deve ser quadrada'}), 400 - - # Criar objeto Matrix - matrix = Matrix(size, size, matrix_data) - - # Verificar se é quadrada - if not matrix.is_square(): - return jsonify({'error': 'Matriz inversa só existe para matrizes quadradas'}), 400 - - # Calcular determinante primeiro - determinant_value = matrix.determinant() - - # Verificar se é singular - if abs(determinant_value) < 1e-10: - result = { - 'matrix': matrix.to_list(), - 'determinant': 0.0, - 'is_singular': True, - 'error': 'Matriz singular (determinante = 0). Não tem inversa.', - 'size': size - } - return jsonify(result) - - # Calcular inversa com método escolhido - if method == 'gauss-jordan': - inverse_matrix = matrix.gauss_jordan_inverse() - else: - inverse_matrix = matrix.inverse() - - # Verificar o resultado - verification = matrix.verify_inverse(inverse_matrix) - - # Preparar resposta - result = { - 'matrix': matrix.to_list(), - 'inverse': inverse_matrix.to_list(), - 'determinant': determinant_value, - 'is_singular': False, - 'size': size, - 'method': method, - 'verification': verification, - 'verification_ok': verification['is_correct'] - } - - # Adicionar detalhes para matrizes 2x2 - if size == 2: - a, b = matrix_data[0][0], matrix_data[0][1] - c, d = matrix_data[1][0], matrix_data[1][1] - det = determinant_value - result['formula_details'] = f"Inversa 2×2: (1/det) × [[d, -b], [-c, a]] = (1/{det}) × [[{d}, -{b}], [-{c}, {a}]]" - - return jsonify(result) - - except ValueError as e: - return jsonify({'error': str(e)}), 400 - except Exception as e: - return jsonify({'error': f'Erro ao calcular inversa: {str(e)}'}), 500 + """Cálculo de matriz inversa.""" + if request.method == 'GET': + return render_template('inverse.html') + + data = request.get_json() + matrix = Matrix(data['size'], + data['size'], + data['matrix']) - return render_template('inverse.html') + return handle_matrix_operation(matrix.inverse) @app.route('/encrypt', methods=['GET', 'POST']) def encrypt(): - """ - Criptografia de mensagens usando multiplicação matricial. - """ - if request.method == 'POST': - try: - data = request.get_json(force=True) - - if not data: - return jsonify({'error': 'Invalid JSON'}), 400 - - message = data.get('message', '') - matrix_data = data.get('encoding_matrix', []) - operation = data.get('operation', 'encrypt') - - if not message: - return jsonify({'error': 'Mensagem não pode estar vazia'}), 400 - - if not matrix_data: - return jsonify({'error': 'Matriz codificadora não fornecida'}), 400 - - size = len(matrix_data) - - # Validar matriz quadrada - if any(len(row) != size for row in matrix_data): - return jsonify({'error': 'A matriz deve ser quadrada'}), 400 - - # Criar matriz codificadora - encoding_matrix = Matrix(size, size, matrix_data) - - # Verificar se a matriz é invertível - det = encoding_matrix.determinant() - if abs(det) < 1e-10: - return jsonify({ - 'error': 'Matriz singular (det=0). Escolha uma matriz invertível para criptografia.' - }), 400 - - if operation == 'encrypt': - # Criptografar - result = encoding_matrix.encrypt_message(message, encoding_matrix) - - return jsonify({ - 'operation': 'encrypt', - 'original_message': message, - 'encoding_matrix': encoding_matrix.to_list(), - 'message_matrix': result['message_matrix'].to_list(), - 'encrypted_matrix': result['encrypted_matrix'].to_list(), - 'numeric_sequence': result['numeric_sequence'], - 'determinant': det - }) - - elif operation == 'decrypt': - # Para descriptografar, precisamos da matriz criptografada - encrypted_data = data.get('encrypted_matrix', []) - - if not encrypted_data: - return jsonify({'error': 'Matriz criptografada não fornecida'}), 400 - - # Criar matriz criptografada - encrypted_rows = len(encrypted_data) - encrypted_cols = len(encrypted_data[0]) if encrypted_data else 0 - encrypted_matrix = Matrix(encrypted_rows, encrypted_cols, encrypted_data) - - # Calcular matriz decodificadora (inversa) - decoding_matrix = encoding_matrix.inverse() - - # Descriptografar - result = encoding_matrix.decrypt_message(encrypted_matrix, decoding_matrix) - - return jsonify({ - 'operation': 'decrypt', - 'encrypted_matrix': encrypted_matrix.to_list(), - 'decoding_matrix': decoding_matrix.to_list(), - 'message_matrix': result['message_matrix'].to_list(), - 'decrypted_message': result['decrypted_message'], - 'numeric_sequence': result['numeric_sequence'] - }) - - else: - return jsonify({'error': 'Operação inválida. Use "encrypt" ou "decrypt"'}), 400 - - except ValueError as e: - return jsonify({'error': str(e)}), 400 - except Exception as e: - return jsonify({'error': f'Erro: {str(e)}'}), 500 + """Criptografia de mensagens usando multiplicação matricial.""" + if request.method == 'GET': + return render_template('encrypt.html') + + data = request.get_json() + encoding_matrix = Matrix(data['size'], + data['size'], + data['encoding_matrix']) - return render_template('encrypt.html') + result = encoding_matrix.encrypt_message(data['message']) + + return jsonify({ + 'encrypted_matrix': result['encrypted_matrix'].to_list(), + 'numeric_sequence': result['numeric_sequence'] + }) + +@app.route('/decrypt', methods=['GET', 'POST']) +def decrypt(): + """Descriptografia de mensagens usando multiplicação matricial.""" + if request.method == 'GET': + return render_template('decrypt.html') + + data = request.get_json() + encoding_matrix = Matrix(data['size'], + data['size'], + data['encoding_matrix']) + + encrypted_matrix = Matrix(data['size'], + data['encrypted_cols'], + data['encrypted_matrix']) + + return handle_matrix_operation(encoding_matrix.decrypt_message, encrypted_matrix) -@app.route('/check_matrix', methods=['POST']) -def check_matrix(): - """Endpoint para verificar se uma matriz é válida para criptografia.""" - try: - data = request.get_json(force=True) - - matrix_data = data['matrix'] - size = data['size'] - - # Validar tamanho - if len(matrix_data) != size or any(len(row) != size for row in matrix_data): - return jsonify({'valid': False, 'error': 'Matriz não é quadrada'}) - - # Criar objeto Matrix - matrix = Matrix(size, size, matrix_data) - - # Verificar se é quadrada - if not matrix.is_square(): - return jsonify({'valid': False, 'error': 'Matriz não é quadrada'}) - - # Calcular determinante - try: - det = matrix.determinant() - if abs(det) < 1e-10: - return jsonify({ - 'valid': False, - 'error': 'Matriz singular (determinante = 0)', - 'determinant': det - }) - - return jsonify({ - 'valid': True, - 'determinant': det, - 'message': 'Matriz válida para criptografia' - }) - - except Exception as e: - return jsonify({'valid': False, 'error': f'Erro ao calcular determinante: {str(e)}'}) - - except Exception as e: - return jsonify({'valid': False, 'error': str(e)}) - -# --- Execução do Servidor --- if __name__ == '__main__': - # Em ambiente de desenvolvimento, usar debug=True - app.run(debug=True) \ No newline at end of file + app.run(debug=True, host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/core/matrix.py b/core/matrix.py index 33ffdcb..4223d1c 100644 --- a/core/matrix.py +++ b/core/matrix.py @@ -1,384 +1,280 @@ class Matrix: - """A custom Matrix class implementing basic linear algebra operations.""" + + # Characters to numbers mapping for encryption/decryption + CHAR_TO_NUM = { + 'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7, 'H': 8, 'I': 9, 'J': 10, + 'K': 11, 'L': 12, 'M': 13, 'N': 14, 'O': 15, 'P': 16, 'Q': 17, 'R': 18, 'S': 19, + 'T': 20, 'U': 21, 'V': 22, 'W': 23, 'X': 24, 'Y': 25, 'Z': 26, + '.': 27, ',': 28, ' ': 29, '_': 29, '-': 30 + } + + # Reverse mapping from numbers to characters using CHAR_TO_NUM + NUM_TO_CHAR = {v: k for k, v in CHAR_TO_NUM.items()} + # Ensure ' ' space maps to 29 as well + NUM_TO_CHAR[29] = ' ' def __init__(self, rows, cols, data=None): + # Intialize matrix with given rows and columns self.rows = rows self.cols = cols - if data is None: - self.data = [[0 for _ in range(cols)] for _ in range(rows)] + self.data = [] + for i in range(rows): + row = [] + for j in range(cols): + row.append(0) + self.data.append(row) else: - if len(data) != rows or any(len(row) != cols for row in data): - raise ValueError(f"Dimensões incorretas: esperado {rows}×{cols}") - self.data = [row[:] for row in data] + if not isinstance(data, list): + raise ValueError("Data deve ser uma lista") + if len(data) != rows: + raise ValueError(f"Número de linhas incorreto: esperado {rows}, recebido {len(data)}") + + for i, row in enumerate(data): + if not isinstance(row, list): + raise ValueError(f"Linha {i} não é uma lista") + if len(row) != cols: + raise ValueError(f"Linha {i}: esperado {cols} colunas, recebido {len(row)} colunas") + + self.data = [] + for row in data: + self.data.append(row[:]) def __str__(self): - return "\n".join("[" + " ".join(f"{x:8.2f}" for x in row) + "]" for row in self.data) + # String representation of the matrix + result = [] + for row in self.data: + row_str = "[" + for x in row: + row_str += f"{x:8.2f} " + row_str += "]" + result.append(row_str) + return "\n".join(result) def __repr__(self): - """Return representation of the matrix.""" - return f"Matrix({self.rows}x{self.cols})" + # Official representation in matrix form + return f"Matrix({self.rows}×{self.cols})" + + def to_list(self): + # Convert matrix data to a list of lists + result = [] + for row in self.data: + result.append(row[:]) + return result def get_element(self, row, col): - """Get element at position [row][col] (1-indexed).""" - return self.data[row - 1][col - 1] + # Get element at specified row and column + pass def set_element(self, row, col, value): - """Set element at position [row][col] (1-indexed).""" - self.data[row - 1][col - 1] = value + # Set element at specified row and column + pass + + def is_square(self): + # Check if the matrix is square (rows == cols) + return self.rows == self.cols def dimensions(self): - """Return dimensions as tuple (rows, cols).""" + # Return the dimensions of the matrix as (rows, cols) return (self.rows, self.cols) - def is_same_dimension(self, other): - """Check if two matrices have the same dimensions.""" + def add(self, other): + # Add two matrices (A + B) if not isinstance(other, Matrix): - raise TypeError("Can only compare with another Matrix") - return self.rows == other.rows and self.cols == other.cols - - def to_list(self): - return [row[:] for row in self.data] - - def is_square(self): - # Validar que a matriz é quadrada (mesmo número de linhas e colunas) - return self.cols == self.rows - - def _get_minor(self, row, col): - # ter a matriz menor, remover linha e coluna para calcular determinante com metodo laplace - minor_data = [] + raise TypeError("Só é possível adicionar outra Matrix") + if self.rows != other.rows or self.cols != other.cols: + raise ValueError(f"Dimensões incompatíveis: {self.dimensions()} vs {other.dimensions()}") + + result_data = [] for i in range(self.rows): - if i == row: - continue - new_row = [] + row = [] for j in range(self.cols): - if j == col: - continue - new_row.append(self.data[i][j]) - minor_data.append(new_row) - return Matrix(self.rows - 1, self.cols - 1, minor_data) - - def add(self, other): - """Add two matrices: C[i][j] = A[i][j] + B[i][j]""" - if not self.is_same_dimension(other): - raise ValueError(f"Dimensões incompatíveis: {self.dimensions()} vs {other.dimensions()}") - result = [[self.data[i][j] + other.data[i][j] for j in range(self.cols)] for i in range(self.rows)] - return {'data': result} + row.append(self.data[i][j] + other.data[i][j]) + result_data.append(row) + return Matrix(self.rows, self.cols, result_data) def subtract(self, other): - """Subtract two matrices: C[i][j] = A[i][j] - B[i][j]""" - if not self.is_same_dimension(other): + # Subtract two matrices (A - B) + if not isinstance(other, Matrix): + raise TypeError("Só é possível subtrair outra Matrix") + if self.rows != other.rows or self.cols != other.cols: raise ValueError(f"Dimensões incompatíveis: {self.dimensions()} vs {other.dimensions()}") - result = [[self.data[i][j] - other.data[i][j] for j in range(self.cols)] for i in range(self.rows)] - return {'data': result} + + result_data = [] + for i in range(self.rows): + row = [] + for j in range(self.cols): + row.append(self.data[i][j] - other.data[i][j]) + result_data.append(row) + return Matrix(self.rows, self.cols, result_data) def scalar_multiply(self, scalar): - result = [[self.data[i][j] * scalar for j in range(self.cols)] for i in range(self.rows)] - return {'data': result} + # Multiply matrix by a scalar value (scalar number * A) + result_data = [] + for i in range(self.rows): + row = [] + for j in range(self.cols): + row.append(self.data[i][j] * scalar) + result_data.append(row) + return Matrix(self.rows, self.cols, result_data) def multiply(self, other): + # Multiply two matrices (A * B) if not isinstance(other, Matrix): - raise TypeError("Operando deve ser uma Matrix") - + raise ValueError("Só é possivel multiplicar por outra Matrix") if self.cols != other.rows: - raise ValueError(f"Não é possível multiplicar: A.cols({self.cols}) ≠ B.rows({other.rows})") - - # Inicializar matriz resultado - result = Matrix(self.rows, other.cols) + raise ValueError(f"Dimensões incompatíveis para multiplicar | Tem de ser igual {self.cols} = {other.rows}") - # Multiplicação matricial + result_data = [] for i in range(self.rows): + row = [] for j in range(other.cols): - soma = 0 - for k in range(self.cols): # ou other.rows - soma += self.data[i][k] * other.data[k][j] - result.data[i][j] = soma - - return result + sum_value = 0 + for k in range(self.cols): + sum_value += self.data[i][k] * other.data[k][j] + row.append(sum_value) + result_data.append(row) + return Matrix(self.rows, other.cols, result_data) + + def transpose(self): + # Transpose the matrix (A^T) + result_data = [] + for i in range(self.cols): + row = [] + for j in range(self.rows): + row.append(self.data[j][i]) + result_data.append(row) + return Matrix(self.cols, self.rows, result_data) def determinant(self): - # calcular determinante com o uso de laplace + # Calculate the determinant of the matrix (det(A)) if not self.is_square(): - raise ValueError("Determinante so tem pa matrizes quadradas") - - # matrizes 1x1 + raise ValueError("Determinante só funciona para matrizes quadradas") + if self.rows == 1: return self.data[0][0] - # matrizes 2x2 if self.rows == 2: - return self.data[0][0] * self.data[1][1] - self.data[0][1] * self.data[1][0] + # (a*d - b*c) + return self.data[0][0] * self.data[1][1] - self.data[0][1] * self.data [1][0] - # matrizes 3x3 (regra sarras) if self.rows == 3: - a, b, c = self.data[0][0], self.data[0][1], self.data[0][2] - d, e, f = self.data[1][0], self.data[1][1], self.data[1][2] - g, h, i = self.data[2][0], self.data[2][1], self.data[2][2] - - # aquela cena de diagonal principal vezes diagonal secundaria + # Sarrus Rule criss cross. + a, b, c = self.data[0] + d, e, f = self.data[1] + g, h, i = self.data[2] + return (a*e*i + b*f*g + c*d*h) - (c*e*g + b*d*i + a*f*h) - # matrizes 4x4 ou maiores (cpfatores) + # Laplace method for any of the above & +(3x3) matrices det = 0 for j in range(self.cols): minor = self._get_minor(0, j) - cofactor = ((-1) ** j) * self.data[0][j] * minor.determinant() - det += cofactor + sign = (-1) ** j + det += sign * self.data[0][j] * minor.determinant() return det - - def get_cofactor(self, row, col): - """Calculate the cofactor at position (row, col).""" - minor = self._get_minor(row, col) - sign = 1 if (row + col) % 2 == 0 else -1 - return sign * minor.determinant() - - def adjugate(self): - """Calculate the adjugate (adjoint) matrix.""" - if not self.is_square(): - raise ValueError("Adjunta só existe para matrizes quadradas") - - n = self.rows - adj_data = [[0 for _ in range(n)] for _ in range(n)] - for i in range(n): - for j in range(n): - # adjunta é a transposta da matriz de cofatores - adj_data[j][i] = self.get_cofactor(i, j) - - return Matrix(n, n, adj_data) - - def transpose(self): - """Return the transpose of the matrix.""" - transposed_data = [[self.data[j][i] for j in range(self.rows)] for i in range(self.cols)] - return Matrix(self.cols, self.rows, transposed_data) - - def identity(self, n=None): - """Create an identity matrix of size n×n.""" - if n is None: - n = self.rows - identity_data = [[1 if i == j else 0 for j in range(n)] for i in range(n)] - return Matrix(n, n, identity_data) - + def inverse(self): - """Calculate matrix inverse using adjugate method.""" - # Verificar se é quadrada + # Calculate the inverse of the matrix (A^-1) if not self.is_square(): raise ValueError("Matriz inversa só existe para matrizes quadradas") - # Calcular determinante - det = self.determinant() - - # Verificar se é singular - if abs(det) < 1e-10: - raise ValueError("Matriz singular (determinante = 0). Não tem inversa.") - - n = self.rows - - # matriz 1x1 - if n == 1: - inverse_data = [[1 / self.data[0][0]]] - return Matrix(1, 1, inverse_data) - - # matriz 2x2 - if n == 2: - a, b = self.data[0][0], self.data[0][1] - c, d = self.data[1][0], self.data[1][1] - - inverse_data = [ - [d / det, -b / det], - [-c / det, a / det] - ] - return Matrix(2, 2, inverse_data) - - # Matrizes maiores: método da adjunta - # A⁻¹ = (1/det(A)) * adj(A) - adj = self.adjugate() - scalar = 1 / det - - # Multiplicar adjunta pelo escalar 1/det - inverse_data = [[adj.data[i][j] * scalar for j in range(n)] for i in range(n)] - - # Arredondar valores próximos de zero - for i in range(n): - for j in range(n): - if abs(inverse_data[i][j]) < 1e-10: - inverse_data[i][j] = 0.0 - - return Matrix(n, n, inverse_data) - - def gauss_jordan_inverse(self): - """Calculate inverse using Gauss-Jordan elimination (método alternativo).""" - if not self.is_square(): - raise ValueError("Matriz deve ser quadrada para ter inversa") - - n = self.rows det = self.determinant() - if abs(det) < 1e-10: - raise ValueError("Matriz singular (determinante = 0)") - - # Criar matriz aumentada [A|I] - augmented = [[0 for _ in range(2*n)] for _ in range(n)] - - for i in range(n): - for j in range(n): - augmented[i][j] = self.data[i][j] - augmented[i][n + i] = 1 + raise ValueError("Matriz singular (det=0), não possui inversa") - # Aplicar eliminação de Gauss-Jordan - for i in range(n): - # Pivot - pivot = augmented[i][i] - - # Normalizar linha - for j in range(2*n): - augmented[i][j] /= pivot - - # Eliminar outras linhas - for k in range(n): - if k != i: - factor = augmented[k][i] - for j in range(2*n): - augmented[k][j] -= factor * augmented[i][j] - - # Extrair a inversa da parte direita - inverse_data = [[augmented[i][n + j] for j in range(n)] for i in range(n)] - - # Arredondar valores próximos de zero - for i in range(n): - for j in range(n): - if abs(inverse_data[i][j]) < 1e-10: - inverse_data[i][j] = 0.0 - - return Matrix(n, n, inverse_data) - - def verify_inverse(self, inverse_matrix): - """Verify that A × A⁻¹ = I.""" - n = self.rows - - # Multiplicar A × A⁻¹ - product = self.multiply(inverse_matrix) - - # Criar matriz identidade - identity = self.identity(n) - - # Verificar se o produto é aproximadamente a identidade - is_correct = True - max_error = 0 - - for i in range(n): - for j in range(n): - error = abs(product.data[i][j] - identity.data[i][j]) - max_error = max(max_error, error) - if error > 1e-8: - is_correct = False - - return { - 'is_correct': is_correct, - 'product_matrix': product.to_list(), - 'identity_matrix': identity.to_list(), - 'max_error': max_error - } + adj = self._adjugate() + return adj.scalar_multiply(1 / det) + + def _get_minor(self, row, col): + # Get minor matrix after removing specified row and column (for determinant calculation) + minor_data = [] + for i in range(self.rows): + if i != row: + row_data = [] + for j in range(self.cols): + if j != col: + row_data.append(self.data[i][j]) + minor_data.append(row_data) + return Matrix(self.rows - 1, self.cols - 1, minor_data) + + def _get_cofactor(self, row, col): + # Get cofactor of element at specified row and column (for inverse calculation) + minor = self._get_minor(row, col) + sign = (-1) ** (row + col) + return sign * minor.determinant() + + def _adjugate(self): + # Calculate the adjugate of the matrix (for inverse calculation) + cofactor_data = [] + for i in range(self.rows): + row = [] + for j in range(self.cols): + row.append(self._get_cofactor(i, j)) + cofactor_data.append(row) + cofactor_matrix = Matrix(self.rows, self.cols, cofactor_data) + return cofactor_matrix.transpose() - def encrypt_message(self, message, encoding_matrix): - """ - Encrypt a message using matrix multiplication. - - Args: - message: String to encrypt (letters, spaces, punctuation) - encoding_matrix: Matrix object used for encoding (must be square) - - Returns: - dict with 'encrypted_matrix', 'message_matrix', 'numeric_sequence' - """ - if not isinstance(encoding_matrix, Matrix): - raise ValueError("Encoding matrix must be a Matrix object") - - if not encoding_matrix.is_square(): - raise ValueError("Encoding matrix must be square") - - # Mapping de letras para números - char_map = { - 'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7, 'H': 8, 'I': 9, - 'J': 10, 'K': 11, 'L': 12, 'M': 13, 'N': 14, 'O': 15, 'P': 16, 'Q': 17, - 'R': 18, 'S': 19, 'T': 20, 'U': 21, 'V': 22, 'W': 23, 'X': 24, 'Y': 25, - 'Z': 26, ' ': 27, '.': 28, 'Ú': 29, 'Ã': 30, 'Ç': 31, 'Õ': 32, 'É': 33 - } - - # Converter mensagem para sequência numérica + # --------------Encryption/Decryption specific methods-------------- + @staticmethod + def char_to_num(char): + # Convert character to corresponding number based on CHAR_TO_NUM mapping "encryption" + return Matrix.CHAR_TO_NUM.get(char.upper(), 29) + + @staticmethod + def num_to_char(num): + # Convert number to corresponding character based on CHAR_TO_NUM mapping "decryption" + return Matrix.NUM_TO_CHAR.get(num, ' ') + + def encrypt_message(self, message): + # Encrypt message using matrix multiplication (message_matrix * encoding_matrix = encrypted_matrix) message = message.upper() - numeric_sequence = [char_map.get(char, 27) for char in message] + numbers = [] + for a in message: + numbers.append(Matrix.char_to_num(a)) - # Determinar dimensões da matriz - rows = encoding_matrix.rows - cols = len(numeric_sequence) // rows + while len(numbers) % self.rows != 0: + numbers.append(29) - # Preencher com espaços (27) se necessário - if len(numeric_sequence) % rows != 0: - cols += 1 - padding_needed = (rows * cols) - len(numeric_sequence) - numeric_sequence.extend([27] * padding_needed) - - # Criar a mensagem - message_data = [] - for i in range(rows): + num_cols = len(numbers) // self.rows + message_matrix_data = [] + for i in range(self.rows): row = [] - for j in range(cols): - index = j * rows + i - row.append(numeric_sequence[index]) - message_data.append(row) + for j in range(num_cols): + row.append(numbers[i * num_cols + j]) + message_matrix_data.append(row) + message_matrix = Matrix(self.rows, num_cols, message_matrix_data) - message_matrix = Matrix(rows, cols, message_data) + encrypted_matrix = self.multiply(message_matrix) - # Encriptar: A × M = C - encrypted_matrix = encoding_matrix.multiply(message_matrix) + numeric_sequence = [] + for rows in range(encrypted_matrix.rows): + for cols in range(encrypted_matrix.cols): + numeric_sequence.append(int(encrypted_matrix.data[rows][cols])) return { + 'message_matrix': message_matrix.to_list(), 'encrypted_matrix': encrypted_matrix, - 'message_matrix': message_matrix, - 'numeric_sequence': numeric_sequence, - 'original_message': message + 'numeric_sequence': numeric_sequence } - def decrypt_message(self, encrypted_matrix, decoding_matrix): - """ - Decrypt an encrypted matrix using the inverse matrix. - - Args: - encrypted_matrix: Matrix object with encrypted data - decoding_matrix: Inverse of the encoding matrix - - Returns: - dict with 'decrypted_message', 'numeric_sequence', 'message_matrix' - """ - if not isinstance(encrypted_matrix, Matrix): - raise ValueError("Encrypted matrix must be a Matrix object") - - if not isinstance(decoding_matrix, Matrix): - raise ValueError("Decoding matrix must be a Matrix object") - - # Mapping reverso - reverse_map = { - 1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E', 6: 'F', 7: 'G', 8: 'H', 9: 'I', - 10: 'J', 11: 'K', 12: 'L', 13: 'M', 14: 'N', 15: 'O', 16: 'P', 17: 'Q', - 18: 'R', 19: 'S', 20: 'T', 21: 'U', 22: 'V', 23: 'W', 24: 'X', 25: 'Y', - 26: 'Z', 27: ' ', 28: '.', 29: 'Ú', 30: 'Ã', 31: 'Ç', 32: 'Õ', 33: 'É' - } - - # Decriptar: B × C = M + def decrypt_message(self, encrypted_matrix): + # Decrypt message using matrix multiplication (encrypted_matrix * decoding_matrix = original_message_matrix) + decoding_matrix = self.inverse() message_matrix = decoding_matrix.multiply(encrypted_matrix) - # Extrair numeros da matriz - numeric_sequence = [] - for j in range(message_matrix.cols): - for i in range(message_matrix.rows): - numeric_sequence.append(round(message_matrix.get_element(i + 1, j + 1))) + numbers = [] + for rows in range(message_matrix.rows): + for cols in range(message_matrix.cols): + numbers.append(round(message_matrix.data[rows][cols])) - # Converter para mensagem - decrypted_message = ''.join([reverse_map.get(num, ' ') for num in numeric_sequence]) + decrypted_message = '' + for num in numbers: + decrypted_message += Matrix.num_to_char(num) return { - 'decrypted_message': decrypted_message.rstrip(), - 'numeric_sequence': numeric_sequence, - 'message_matrix': message_matrix + 'message_matrix': message_matrix.to_list(), + 'decrypted_message': decrypted_message, + 'numeric_sequence': numbers } \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index b0cd3db..3b9c1a5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ -# Matrix Project +# Matrix ## How to use - Clone the project using ```git clone https://github.com/tskxz/matrix``` diff --git a/docs/UMLS/Sequence_Diagram.png b/docs/UMLS/Sequence_Diagram.png new file mode 100644 index 0000000..2a85b9f Binary files /dev/null and b/docs/UMLS/Sequence_Diagram.png differ diff --git a/docs/UMLS/addition_subtraction_diagram.png b/docs/UMLS/addition_subtraction_diagram.png new file mode 100644 index 0000000..5c215aa Binary files /dev/null and b/docs/UMLS/addition_subtraction_diagram.png differ diff --git a/docs/UMLS/casos_uso.png b/docs/UMLS/casos_uso.png new file mode 100644 index 0000000..5ca4669 Binary files /dev/null and b/docs/UMLS/casos_uso.png differ diff --git a/docs/UMLS/decryption_diagram.png b/docs/UMLS/decryption_diagram.png new file mode 100644 index 0000000..e7e38f2 Binary files /dev/null and b/docs/UMLS/decryption_diagram.png differ diff --git a/docs/UMLS/determinant_diagram.png b/docs/UMLS/determinant_diagram.png new file mode 100644 index 0000000..e032a36 Binary files /dev/null and b/docs/UMLS/determinant_diagram.png differ diff --git a/docs/UMLS/encryption_diagram.png b/docs/UMLS/encryption_diagram.png new file mode 100644 index 0000000..008e715 Binary files /dev/null and b/docs/UMLS/encryption_diagram.png differ diff --git a/docs/UMLS/inverse_diagram.png b/docs/UMLS/inverse_diagram.png new file mode 100644 index 0000000..13c7afe Binary files /dev/null and b/docs/UMLS/inverse_diagram.png differ diff --git a/docs/UMLS/matrix_class.png b/docs/UMLS/matrix_class.png new file mode 100644 index 0000000..0ef3097 Binary files /dev/null and b/docs/UMLS/matrix_class.png differ diff --git a/docs/UMLS/multiply_diagram.png b/docs/UMLS/multiply_diagram.png new file mode 100644 index 0000000..8d60209 Binary files /dev/null and b/docs/UMLS/multiply_diagram.png differ diff --git a/docs/UMLS/unit_tests_diagram.png b/docs/UMLS/unit_tests_diagram.png new file mode 100644 index 0000000..a5ae9f6 Binary files /dev/null and b/docs/UMLS/unit_tests_diagram.png differ diff --git a/docs/UMLS/use_case_diagram.png b/docs/UMLS/use_case_diagram.png new file mode 100644 index 0000000..014b1f7 Binary files /dev/null and b/docs/UMLS/use_case_diagram.png differ diff --git a/docs/criptografia.md b/docs/algebra/criptografia.md similarity index 100% rename from docs/criptografia.md rename to docs/algebra/criptografia.md diff --git a/docs/algebra/matrix.pdf b/docs/algebra/matrix.pdf new file mode 100644 index 0000000..8889521 Binary files /dev/null and b/docs/algebra/matrix.pdf differ diff --git a/docs/algebra/matrix.png b/docs/algebra/matrix.png new file mode 100644 index 0000000..2d2ea01 Binary files /dev/null and b/docs/algebra/matrix.png differ diff --git a/docs/calculus.md b/docs/algebra/operations.md similarity index 100% rename from docs/calculus.md rename to docs/algebra/operations.md diff --git a/docs/analise_sistema.MD b/docs/analise_sistema.MD new file mode 100644 index 0000000..51cdc29 --- /dev/null +++ b/docs/analise_sistema.MD @@ -0,0 +1,86 @@ +Análise de sistema +Authors: Tanjil Khan, Dmytro Bohutsky, Luís Martins, Ricardo Magalhães +Date: 01/19/2026 + +## Requisitos funcionais +O sistema deve ter as seguintes funcionalidades: +##### RF01 – Introdução de Matrizes +O sistema deve permitir ao utilizador introduzir matrizes de dimensão variável, definindo explicitamente o número de linhas e colunas, bem como os respetivos valores reais. + +##### RF02 – Soma de Matrizes +O sistema deve permitir a soma de duas matrizes + +##### RF03 – Subtração de Matrizes +O sistema deve permitir a subtração de duas matrizes + +##### RF04 – Multiplicação de Matriz por Escalar +O sistema deve permitir a multiplicação de uma matriz + +##### RF05 – Multiplicação de Matrizes +O sistema deve permitir a multiplicação de duas matrizes + +##### RF06 – Cálculo do Determinante +O sistema deve calcular o determinante de uma matriz quadrada: + - Matrizes de ordem superior através da expansão por cofatores + - Caso a matriz não seja quadrada, o sistema deve informar o utilizador. + +##### RF07 – Cálculo da Matriz Inversa +O sistema deve calcular a matriz inversa de uma matriz quadrada, desde que o determinante seja diferente de zero. +Caso contrário, o sistema deve apresentar uma mensagem de erro adequada. + +##### RF08 – Conversão de Texto para Matriz Numérica +O sistema deve converter uma mensagem de texto numa matriz numérica com base numa tabela de codificação pré-definida (A=1, B=2, …, Z=26, sinais especiais). + +##### RF09 – Criptografia de Mensagens +O sistema deve permitir a criptografia de mensagens através da multiplicação da matriz da mensagem por uma matriz de codificação. + +##### RF10 – Descriptografia de Mensagens +O sistema deve permitir a recuperação da mensagem original através da multiplicação da matriz criptografada pela matriz inversa de decodificação. + +##### RF11 – Validação de Dados +O sistema deve validar: +- Dimensões das matrizes +- Valores numéricos introduzidos +- Condições matemáticas necessárias para cada operação + +## Requisitos não funcionais + +##### RNF01 – Desempenho +O sistema deve executar operações matriciais de pequena e média dimensão num tempo de resposta inferior a 1 segundo, garantindo fluidez na interação com o utilizador. + +##### RNF02 – Usabilidade +O sistema deve apresentar uma interface simples e intuitiva, permitindo que utilizadores com conhecimentos básicos de matemática consigam realizar operações sem dificuldade. + +##### RNF03 – Fiabilidade +O sistema deve garantir resultados matematicamente corretos, respeitando rigorosamente as definições formais das operações matriciais. + +##### RNF04 – Segurança dos Dados +O sistema não deve armazenar permanentemente mensagens introduzidas para criptografia, garantindo a confidencialidade dos dados do utilizador durante a execução. + +##### RNF05 – Robustez +O sistema deve lidar corretamente com erros, como: + - Matrizes incompatíveis + - Determinantes nulos + - Introdução de dados inválidos + - Apresentar mensagens claras ao utilizador. + +##### RNF06 – Escalabilidade +O sistema deve ser projetado de forma a permitir, no futuro, a adição de novas operações matemáticas ou métodos de criptografia sem necessidade de reestruturação profunda. + +##### RNF07 – Conformidade +O sistema deve respeitar boas práticas de desenvolvimento de software e normas académicas, garantindo clareza, organização e manutenção do código. + +### Casos de uso + + +### Diagrama de classes +Neste diagrama, representamos a estrutura e relações da classe Matriz: + + +### Diagrama de sequências +O seguinte diagrama mostra a sequência dos processos e a estrutura do nossso projeto: + + +### Fluxograma - unit test +O seguinte fluxograma descreve como seria o fluxo enquanto se executa os testes: + \ No newline at end of file diff --git a/docs/analise_sistema.html b/docs/analise_sistema.html new file mode 100644 index 0000000..0debc5c --- /dev/null +++ b/docs/analise_sistema.html @@ -0,0 +1,435 @@ + + +
+Análise de sistema
+Authors: Tanjil Khan, Dmytro Bohutsky, Luís Martins, Ricardo Magalhães
+Date: 01/19/2026
O sistema deve ter as seguintes funcionalidades:
+O sistema deve permitir ao utilizador introduzir matrizes de dimensão variável, definindo explicitamente o número de linhas e colunas, bem como os respetivos valores reais.
+O sistema deve permitir a soma de duas matrizes
+O sistema deve permitir a subtração de duas matrizes
+O sistema deve permitir a multiplicação de uma matriz
+O sistema deve permitir a multiplicação de duas matrizes
+O sistema deve calcular o determinante de uma matriz quadrada: +- Matrizes de ordem superior através da expansão por cofatores +- Caso a matriz não seja quadrada, o sistema deve informar o utilizador.
+O sistema deve calcular a matriz inversa de uma matriz quadrada, desde que o determinante seja diferente de zero. +Caso contrário, o sistema deve apresentar uma mensagem de erro adequada.
+O sistema deve converter uma mensagem de texto numa matriz numérica com base numa tabela de codificação pré-definida (A=1, B=2, …, Z=26, sinais especiais).
+O sistema deve permitir a criptografia de mensagens através da multiplicação da matriz da mensagem por uma matriz de codificação.
+O sistema deve permitir a recuperação da mensagem original através da multiplicação da matriz criptografada pela matriz inversa de decodificação.
+O sistema deve validar:
+O sistema deve executar operações matriciais de pequena e média dimensão num tempo de resposta inferior a 1 segundo, garantindo fluidez na interação com o utilizador.
+O sistema deve apresentar uma interface simples e intuitiva, permitindo que utilizadores com conhecimentos básicos de matemática consigam realizar operações sem dificuldade.
+O sistema deve garantir resultados matematicamente corretos, respeitando rigorosamente as definições formais das operações matriciais.
+O sistema não deve armazenar permanentemente mensagens introduzidas para criptografia, garantindo a confidencialidade dos dados do utilizador durante a execução.
+O sistema deve lidar corretamente com erros, como: +- Matrizes incompatíveis +- Determinantes nulos +- Introdução de dados inválidos +- Apresentar mensagens claras ao utilizador.
+O sistema deve ser projetado de forma a permitir, no futuro, a adição de novas operações matemáticas ou métodos de criptografia sem necessidade de reestruturação profunda.
+O sistema deve respeitar boas práticas de desenvolvimento de software e normas académicas, garantindo clareza, organização e manutenção do código.
+
Neste diagrama, representamos a estrutura e relações da classe Matriz:
+
O seguinte diagrama mostra a sequência dos processos e a estrutura do nossso projeto:
+
O seguinte fluxograma descreve como seria o fluxo enquanto se executa os testes:
+
${result.decrypted_message}
`; + showResult(); + + const exportBtn = document.createElement('button'); + exportBtn.textContent = 'Exportar como JSON'; + exportBtn.className = 'btn-secondary'; + exportBtn.style.marginTop = '1rem'; + + exportBtn.onclick = () => exportDecryptAsJSON( + payload.encoding_matrix, + payload.encrypted_matrix, + result.decrypted_message + ); + + resultDiv.appendChild(exportBtn); + + showResult(); + + + const exportXMLBtn = document.createElement('button'); + exportXMLBtn.textContent = 'Exportar como XML'; + exportXMLBtn.className = 'btn-secondary'; + exportXMLBtn.style.marginTop = '0.5rem'; + + exportXMLBtn.onclick = () => exportDecryptAsXML( + payload.encoding_matrix, + payload.encrypted_matrix, + result.decrypted_message + ); + + resultDiv.appendChild(exportXMLBtn); + + const exportHTMLBtn = document.createElement('button'); + exportHTMLBtn.textContent = 'Exportar como HTML'; + exportHTMLBtn.className = 'btn-secondary'; + exportHTMLBtn.style.marginTop = '0.5rem'; + + exportHTMLBtn.onclick = () => exportDecryptAsHTML( + payload.encoding_matrix, + payload.encrypted_matrix, + result.decrypted_message +); + +document.getElementById('result').appendChild(exportHTMLBtn); + + } catch (error) { + showError(error.message); + } +}); + +generateBtn.click(); + +function exportDecryptAsJSON(encodingMatrix, encryptedMatrix, decryptedMessage) { + const json = +`{ + "operation": "decrypt", + "encodingMatrix": ${prettyJson(encodingMatrix, 4)}, + "encryptedMatrix": ${prettyJson(encryptedMatrix, 4)}, + "decryptedMessage": "${decryptedMessage}" +}`; + + const blob = new Blob([json], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = 'desencriptacao.json'; + a.click(); + + URL.revokeObjectURL(url); +} + +function exportDecryptAsXML(encodingMatrix, encryptedMatrix, decryptedMessage) { + const xml = +` ++ ${decryptedMessage} +
+ +`; + + const blob = new Blob([html], { type: 'text/html' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = 'desencriptacao.html'; + a.click(); + + URL.revokeObjectURL(url); +} \ No newline at end of file diff --git a/static/js/determinant.js b/static/js/determinant.js new file mode 100644 index 0000000..cb25ad4 --- /dev/null +++ b/static/js/determinant.js @@ -0,0 +1,142 @@ +const form = document.getElementById('matrix-form'); +const generateBtn = document.getElementById('generate-btn'); + +generateBtn.addEventListener('click', function() { + const size = parseInt(document.getElementById('size').value); + + clearMatrixInputs(); + hideError(); + hideResult(); + + generateMatrixInput(size, size, 'matrix-inputs', 'Matriz', 'matrix-a'); +}); + +form.addEventListener('submit', async function(e) { + e.preventDefault(); + hideError(); + hideResult(); + + const size = parseInt(document.getElementById('size').value); + + const payload = { + size: size, + matrix: readMatrixValues('matrix-a') + }; + + try { + const result = await apiCall('/determinant', payload); + const resultDiv = document.getElementById('result'); + resultDiv.innerHTML = `${result.result}
`; + showResult(); + + const exportBtn = document.createElement('button'); + exportBtn.textContent = 'Exportar como JSON'; + exportBtn.className = 'btn-secondary'; + exportBtn.style.marginTop = '1rem'; + + exportBtn.onclick = () => exportDeterminantAsJSON( + payload.matrix, + result.result + ); + + resultDiv.appendChild(exportBtn); + + showResult(); + + const exportXMLBtn = document.createElement('button'); + exportXMLBtn.textContent = 'Exportar como XML'; + exportXMLBtn.className = 'btn-secondary'; + exportXMLBtn.style.marginTop = '0.5rem'; + + exportXMLBtn.onclick = () => exportDeterminantAsXML( + payload.matrix, + result.result + ); + + resultDiv.appendChild(exportXMLBtn); + + const exportHTMLBtn = document.createElement('button'); + exportHTMLBtn.textContent = 'Exportar como HTML'; + exportHTMLBtn.className = 'btn-secondary'; + exportHTMLBtn.style.marginTop = '0.5rem'; + + exportHTMLBtn.onclick = () => exportDeterminantAsHTML( + payload.matrix, + result.result +); + + document.getElementById('result').appendChild(exportHTMLBtn); + + } catch (error) { + showError(error.message); + } +}); + +generateBtn.click(); + +function exportDeterminantAsJSON(matrix, determinant) { + const json = +`{ + "operation": "determinant", + "matrix": ${prettyJson(matrix, 4)}, + "result": ${determinant} +}`; + + const blob = new Blob([json], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = 'determinante.json'; + a.click(); + + URL.revokeObjectURL(url); +} + +function exportDeterminantAsXML(matrix, determinant) { + const xml = +` +${determinant}
+ +`; + + const blob = new Blob([html], { type: 'text/html' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = 'determinante.html'; + a.click(); + + URL.revokeObjectURL(url); +} diff --git a/static/js/encrypt.js b/static/js/encrypt.js new file mode 100644 index 0000000..09c11cc --- /dev/null +++ b/static/js/encrypt.js @@ -0,0 +1,153 @@ +const form = document.getElementById('encrypt-form'); +const generateBtn = document.getElementById('generate-btn'); + +generateBtn.addEventListener('click', function() { + const size = parseInt(document.getElementById('size').value); + + clearMatrixInputs(); + hideError(); + hideResult(); + + generateMatrixInput(size, size, 'matrix-inputs', 'Matriz de Codificação', 'encoding-matrix'); +}); + +form.addEventListener('submit', async function(e) { + e.preventDefault(); + hideError(); + hideResult(); + + const payload = { + size: parseInt(document.getElementById('size').value), + encoding_matrix: readMatrixValues('encoding-matrix'), + message: document.getElementById('message').value + }; + + const payload_determinant = { + size: parseInt(document.getElementById('size').value), + matrix: readMatrixValues('encoding-matrix') + }; + + try { + const detResult = await apiCall('/determinant', payload_determinant); + if (detResult.result === 0) { + showError('A matriz de codificação deve ser invertível (det ≠ 0).'); + return; + } + const result = await apiCall('/encrypt', payload); + displayMatrix(result.encrypted_matrix, 'Mensagem Encriptada'); + + const exportBtn = document.createElement('button'); + exportBtn.textContent = 'Exportar como JSON'; + exportBtn.className = 'btn-secondary'; + exportBtn.style.marginTop = '1rem'; + + exportBtn.onclick = () => exportEncryptAsJSON( + payload.message, + payload.encoding_matrix, + result.encrypted_matrix + ); + document.getElementById('result').appendChild(exportBtn); + + const exportXMLBtn = document.createElement('button'); + exportXMLBtn.textContent = 'Exportar como XML'; + exportXMLBtn.className = 'btn-secondary'; + exportXMLBtn.style.marginTop = '0.5rem'; + + exportXMLBtn.onclick = () => exportEncryptAsXML( + payload.message, + payload.encoding_matrix, + result.encrypted_matrix + ); + + document.getElementById('result').appendChild(exportXMLBtn); + + const exportHTMLBtn = document.createElement('button'); + exportHTMLBtn.textContent = 'Exportar como HTML'; + exportHTMLBtn.className = 'btn-secondary'; + exportHTMLBtn.style.marginTop = '0.5rem'; + + exportHTMLBtn.onclick = () => exportEncryptAsHTML( + payload.message, + payload.encoding_matrix, + result.encrypted_matrix +); + +document.getElementById('result').appendChild(exportHTMLBtn); + + + } catch (error) { + showError(error.message); + } +}); + +generateBtn.click(); + +function exportEncryptAsJSON(message, encodingMatrix, encryptedMatrix) { + const json = +`{ + "operation": "encrypt", + "message": "${message}", + "encodingMatrix": ${prettyJson(encodingMatrix, 4)}, + "encryptedMatrix": ${prettyJson(encryptedMatrix, 4)} +}`; + + const blob = new Blob([json], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = 'encriptacao.json'; + a.click(); + + URL.revokeObjectURL(url); +} + +function exportEncryptAsXML(message, encodingMatrix, encryptedMatrix) { + const xml = +` +${message}
+ + ${prettyHTML(encodingMatrix, 'Matriz de Codificação')} + ${prettyHTML(encryptedMatrix, 'Matriz Encriptada')} + +`; + + const blob = new Blob([html], { type: 'text/html' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = 'encriptacao.html'; + a.click(); + + URL.revokeObjectURL(url); +} diff --git a/static/js/inverse.js b/static/js/inverse.js new file mode 100644 index 0000000..c4a775d --- /dev/null +++ b/static/js/inverse.js @@ -0,0 +1,139 @@ +const form = document.getElementById('matrix-form'); +const generateBtn = document.getElementById('generate-btn'); + +generateBtn.addEventListener('click', function() { + const size = parseInt(document.getElementById('size').value); + + clearMatrixInputs(); + hideError(); + hideResult(); + + generateMatrixInput(size, size, 'matrix-inputs', 'Matriz', 'matrix-a'); +}); + +form.addEventListener('submit', async function(e) { + e.preventDefault(); + hideError(); + hideResult(); + + const size = parseInt(document.getElementById('size').value); + + const payload = { + size: size, + matrix: readMatrixValues('matrix-a') + }; + + try { + const result = await apiCall('/inverse', payload); + displayMatrix(result.result, 'Matriz Inversa'); + + const exportBtn = document.createElement('button'); + exportBtn.textContent = 'Exportar como JSON'; + exportBtn.className = 'btn-secondary'; + exportBtn.style.marginTop = '1rem'; + + exportBtn.onclick = () => exportInverseAsJSON( + payload.matrix, + decimalMatrix(result.result, 2) + ); + + document.getElementById('result').appendChild(exportBtn); + + const exportXMLBtn = document.createElement('button'); + exportXMLBtn.textContent = 'Exportar como XML'; + exportXMLBtn.className = 'btn-secondary'; + exportXMLBtn.style.marginTop = '0.5rem'; + + exportXMLBtn.onclick = () => exportInverseAsXML( + payload.matrix, + decimalMatrix(result.result, 2) + ); + + document.getElementById('result').appendChild(exportXMLBtn); + + const exportHTMLBtn = document.createElement('button'); + exportHTMLBtn.textContent = 'Exportar como HTML'; + exportHTMLBtn.className = 'btn-secondary'; + exportHTMLBtn.style.marginTop = '0.5rem'; + + exportHTMLBtn.onclick = () => exportInverseAsHTML( + payload.matrix, + decimalMatrix(result.result, 2) +); + +document.getElementById('result').appendChild(exportHTMLBtn); + + } catch (error) { + showError(error.message); + } +}); + +generateBtn.click(); + +const json = prettyJson(matrixA, 4); + + +function exportInverseAsJSON(originalMatrix, inverseMatrix) { + const json = +`{ + "operation": "inverse", + "originalMatrix": ${prettyJson(originalMatrix, 4)}, + "inverseMatrix": ${prettyJson(inverseMatrix, 4)} +}`; + + const blob = new Blob([json], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = 'matriz_inversa.json'; + a.click(); + + URL.revokeObjectURL(url); +} + +function exportInverseAsXML(originalMatrix, inverseMatrix) { + const xml = +` +${data.result}
${data.details}
| ${parseFloat(cell).toFixed(2)} | `; - }); - } else { - html += `${parseFloat(row).toFixed(2)} | `; - } - html += '
Calculando...
'; - document.body.appendChild(loader); - } - loader.style.display = 'flex'; - } else { - if (loader) { - loader.style.display = 'none'; - } - } -} - -/** - * Show alert message - */ -function showAlert(type, message) { - const alert = document.createElement('div'); - alert.className = `alert alert-${type}`; - alert.innerHTML = `${message}
`; - document.querySelector('main').prepend(alert); - setTimeout(() => alert.remove(), 5000); -} - -/** - * Parse matrix input from form - * Expected format: rows separated by semicolons, elements by commas - * Example: "1,2,3;4,5,6" - */ -function parseMatrixInput(input) { - if (!input) return null; - - try { - const rows = input.split(';').map(row => { - return row.trim().split(',').map(num => { - const parsed = parseFloat(num.trim()); - if (isNaN(parsed)) { - throw new Error(`Valor inválido: "${num.trim()}"`); - } - return parsed; - }); - }); - return rows; - } catch (error) { - showAlert('error', `Erro ao processar matriz: ${error.message}`); - return null; - } -} - -/** - * Validate matrix dimensions - */ -function validateMatrixDimensions(matrix) { - if (!matrix || !Array.isArray(matrix) || matrix.length === 0) { - return false; - } - - const firstRowLength = matrix[0].length; - return matrix.every(row => row.length === firstRowLength); -} - -/** - * Get matrix dimensions - */ -function getMatrixDimensions(matrix) { - if (!matrix || matrix.length === 0) return null; - return { - rows: matrix.length, - columns: matrix[0].length - }; -} - -/** - * Format number for display - */ -function formatNumber(num) { - if (Number.isInteger(num)) { - return num.toString(); - } - return parseFloat(num).toFixed(4); -} - -/** - * Clear form and results - */ -function clearResults() { - const resultContainer = document.querySelector('.result-container'); - if (resultContainer) { - resultContainer.classList.remove('show'); - setTimeout(() => { - resultContainer.innerHTML = ''; - }, 300); - } -} - -/** - * Export matrix as CSV - */ -function exportMatrixAsCSV(matrix, filename = 'matrix.csv') { - if (!matrix || !Array.isArray(matrix)) return; - - let csvContent = 'data:text/csv;charset=utf-8,'; - matrix.forEach(row => { - csvContent += row.join(',') + '\n'; - }); - - const link = document.createElement('a'); - link.setAttribute('href', encodeURI(csvContent)); - link.setAttribute('download', filename); - link.click(); -} - -/** - * Show matrix info (dimensions, type, etc) - */ -function showMatrixInfo(matrix) { - const dims = getMatrixDimensions(matrix); - if (!dims) return ''; - - return `Dimensões: ${dims.rows}×${dims.columns}`; -} - -console.log('✓ main.js loaded'); \ No newline at end of file diff --git a/static/js/matrix-builder.js b/static/js/matrix-builder.js new file mode 100644 index 0000000..4e90f97 --- /dev/null +++ b/static/js/matrix-builder.js @@ -0,0 +1,71 @@ +function generateMatrixInput(rows, cols, containerId, label, gridId) { + const container = document.getElementById(containerId); + + const section = document.createElement('div'); + section.className = 'matrix-section'; + section.innerHTML = `Preencha todos os elementos da matriz quadrada
-| ${formatted} | `; - }).join('')} -
Erro: ${msg}
-| ${parseFloat(val).toFixed( - 2 - )} | ` - ) - .join("")} -
Dimensões: ${data.dimensions}
-${msg}
| ${parseFloat(val).toFixed( - 2 - )} | ` - ) - .join("")} -
Dimensões: ${data.dimensions}
-${msg}
| ${parseFloat(val).toFixed( - 2 - )} | ` - ) - .join("")} -
Dimensões: ${data.dimensions}
-${msg}
| ${value} | `; + }); + html += '
Regras importantes:
-Como funciona:
-Tabela de conversão: A=1, B=2, ..., Z=26, Espaço=27, .=28, Ú=29, Ã=30, Ç=31, Õ=32, É=33
-Somar ou subtrair Matrizes
+Multiplicar duas Matrizes
+Inverter linhas e colunas
+Multiplicar por um valor
+Calcular o determinante
+Encontrar a matriz inversa
+Encriptar mensagens
+Desencriptar mensagens
+Regras importantes:
-