commit 0c3931c02928613d622eb6febcd8ee300ec3dd19 Author: Julian Heinlein Date: Thu Oct 15 17:02:49 2026 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1eb6e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.venv/ +__pycache__/ +.env \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/matrixmania.iml b/.idea/matrixmania.iml new file mode 100644 index 0000000..d1d22b2 --- /dev/null +++ b/.idea/matrixmania.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7b1daae --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..8507c6e --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1792076204680 + + + + + + \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..79a2eba --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,4 @@ +# Regeln für dieses Projekt +- Code nach PEP 8, Zeilenlänge maximal 88 Zeichen. +- Jede Funktion und Methode hat einen Docstring und Type Hints. +- Vor jedem Commit: `ruff check` und `ruff format`. \ No newline at end of file diff --git a/compute.py b/compute.py new file mode 100644 index 0000000..5a6a2df --- /dev/null +++ b/compute.py @@ -0,0 +1,244 @@ +"""Modul zur Matrizenmultiplikation.""" + +import math +import sys +from numbers import Number + + +def _validate_matrix(matrix: object, name: str) -> list[list[Number]]: + """Prüft, ob es sich um eine gültige Matrix handelt. + + Args: + matrix (object): Zu prüfende Matrix. + name (str): Name der Matrix für die Fehlermeldung. + + Returns: + list[list[Number]]: Validierte Matrix. + + Raises: + TypeError: Wenn die Matrix keine Liste ist oder nicht-numerische + Elemente enthält. + ValueError: Wenn die Matrix leer ist oder unregelmäßige Zeilenlängen + aufweist. + """ + if not isinstance(matrix, list): + raise TypeError(f"'{name}' muss eine Liste von Listen sein, nicht {type(matrix).__name__}.") + + if len(matrix) == 0: + raise ValueError(f"'{name}' darf nicht leer sein.") + + for index, row in enumerate(matrix): + if not isinstance(row, list): + raise TypeError(f"Zeile {index} von '{name}' muss eine Liste sein, nicht {type(row).__name__}.") + + if len(row) == 0: + raise ValueError(f"Zeile {index} von '{name}' darf nicht leer sein.") + + if len(row) != len(matrix[0]): + raise ValueError( + f"Zeile {index} von '{name}' hat {len(row)} Spalten, erwartet werden {len(matrix[0])}." + ) + + for column, value in enumerate(row): + if isinstance(value, bool) or not isinstance(value, Number): + raise TypeError( + f"Element [{index}][{column}] von '{name}' muss eine Zahl sein, " + f"nicht {type(value).__name__}." + ) + + return matrix + + +def matmul(matrix_a: list[list[Number]], matrix_b: list[list[Number]]) -> list[list[Number]]: + """Multipliziert zwei Matrizen. + + Die Anzahl der Spalten von ``matrix_a`` muss mit der Anzahl der Zeilen von + ``matrix_b`` übereinstimmen. + + Args: + matrix_a (list[list[Number]]): Erste Matrix der Form m x n. + matrix_b (list[list[Number]]): Zweite Matrix der Form n x p. + + Returns: + list[list[Number]]: Ergebnis der Multiplikation der Form m x p. + + Raises: + TypeError: Wenn eine der Eingaben keine gültige Matrix aus Zahlen ist. + ValueError: Wenn die Matrizen leer sind oder die Dimensionen nicht + zueinander passen. + """ + matrix_a = _validate_matrix(matrix_a, "matrix_a") + matrix_b = _validate_matrix(matrix_b, "matrix_b") + + rows_a = len(matrix_a) + columns_a = len(matrix_a[0]) + rows_b = len(matrix_b) + columns_b = len(matrix_b[0]) + + if columns_a != rows_b: + raise ValueError( + f"Dimensionen passen nicht: matrix_a hat {columns_a} Spalten, " + f"matrix_b hat {rows_b} Zeilen." + ) + + result: list[list[Number]] = [[0 for _ in range(columns_b)] for _ in range(rows_a)] + + for i in range(rows_a): + for j in range(columns_b): + total = 0 + for k in range(columns_a): + total += matrix_a[i][k] * matrix_b[k][j] + result[i][j] = total + + return result + + +def transpose(matrix: list[list[Number]]) -> list[list[Number]]: + """Transponiert eine Matrix. + + Vertauscht Zeilen und Spalten der Matrix. Aus einer Matrix der Form m x n + wird eine Matrix der Form n x m. + + Args: + matrix (list[list[Number]]): Zu transponierende Matrix. + + Returns: + list[list[Number]]: Transponierte Matrix der Form n x m. + + Raises: + TypeError: Wenn die Eingabe keine gültige Matrix aus Zahlen ist. + ValueError: Wenn die Matrix leer ist oder unregelmäßige Zeilenlängen + aufweist. + """ + matrix = _validate_matrix(matrix, "matrix") + + rows = len(matrix) + columns = len(matrix[0]) + + return [[matrix[i][j] for i in range(rows)] for j in range(columns)] + + +def rotate(angle: float) -> list[list[float]]: + """Erstellt die Rotationsmatrix für einen Winkel. + + Args: + angle (float): Drehwinkel im Bogenmaß. + + Returns: + list[list[float]]: 2x2-Rotationsmatrix, die auf einen Vektor + angewendet werden kann. + + Raises: + TypeError: Wenn ``angle`` keine Zahl ist. + """ + + if isinstance(angle, bool) or not isinstance(angle, Number): + raise TypeError(f"'angle' muss eine Zahl sein, nicht {type(angle).__name__}.") + + cosine = math.cos(angle) + sine = math.sin(angle) + + return [[cosine, -sine], [sine, cosine]] + + +def _parse_number(token: str) -> float: + """Wandelt einen eingegebenen Text in eine Zahl um. + + Args: + token (str): Einzelner Eingabewert. + + Returns: + float: Zahlwert des Eingabetokens. + + Raises: + ValueError: Wenn der Text keine gültige Zahl ist. + """ + try: + return float(token) + except ValueError as error: + raise ValueError(f"'{token}' ist keine gültige Zahl.") from error + + +def _read_matrix(name: str) -> list[list[float]]: + """Liest eine Matrix zeilenweise aus der Konsole ein. + + Der Benutzer gibt zuerst die Anzahl der Zeilen an. Anschließend wird jede + Zeile als Leerzeichen-getrennte Liste von Zahlen eingegeben. Bei ungültiger + Eingabe wird die jeweilige Eingabe erneut abgefragt. + + Args: + name (str): Name der Matrix für die Ausgabe. + + Returns: + list[list[float]]: Eingelesene Matrix. + """ + print(f"\nEingabe der Matrix {name}:") + + while True: + raw = input("Anzahl der Zeilen: ").strip() + try: + rows = int(raw) + except ValueError: + print(f"Fehler: '{raw}' ist keine ganze Zahl. Bitte erneut eingeben.") + continue + if rows <= 0: + print("Fehler: Die Anzahl der Zeilen muss größer als 0 sein.") + continue + break + + matrix: list[list[float]] = [] + expected_columns: int | None = None + + for index in range(rows): + while True: + raw_row = input(f"Zeile {index} (Zahlen durch Leerzeichen getrennt): ").strip() + tokens = raw_row.split() + + if not tokens: + print("Fehler: Die Zeile darf nicht leer sein. Bitte erneut eingeben.") + continue + + try: + row = [_parse_number(token) for token in tokens] + except ValueError as error: + print(f"Fehler: {error} Bitte erneut eingeben.") + continue + + if expected_columns is None: + expected_columns = len(row) + elif len(row) != expected_columns: + print( + f"Fehler: Die Zeile hat {len(row)} Spalten, " + f"erwartet werden {expected_columns}. Bitte erneut eingeben." + ) + continue + + matrix.append(row) + break + + return matrix + + +def _print_matrix(matrix: list[list[Number]]) -> None: + """Gibt eine Matrix formatiert auf der Konsole aus. + + Args: + matrix (list[list[Number]]): Auszugebende Matrix. + """ + for row in matrix: + print(" [" + ", ".join(str(value) for value in row) + "]") + + +def main() -> int: + """Startet das Programm und liest zwei Matrizen zur Multiplikation ein. + + Returns: + int: Exit-Code des Programms (0 bei Erfolg, 1 bei Fehler). + """ + print(rotate(45)) + + + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..46e3b47 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[project] +name = "matrixmania" +version = "0.1.0" \ No newline at end of file