"""Provides matmul, transpose, and rotate functions for 2‑D numeric matrices.""" from __future__ import annotations import math from collections.abc import Iterable from typing import Literal from tabulate import tabulate __all__ = ["matmul", "print_matrix", "rot_2d", "rotate", "transpose"] _NUMERIC_TYPES = (int, float) def _validate_matrix( matrix: Iterable[Iterable[object]], name: str ) -> list[list[float]]: """Validate that *matrix* is a rectangular list of lists of numeric values. Returns the matrix converted to ``list[list[float]]`` if valid. Raises ``TypeError`` for non‑numeric values or if any element is a ``bool``. Raises ``ValueError`` for empty matrices, empty rows, or ragged matrices. """ try: rows = list(map(list, matrix)) except TypeError as exc: raise TypeError(f"{name} must be a sequence of sequences") from exc if not rows: raise ValueError(f"{name} must not be empty") if not rows[0]: raise ValueError(f"{name} must not contain empty rows") row_len = len(rows[0]) for row in rows: if len(row) != row_len: raise ValueError(f"{name} must be rectangular (row lengths differ)") for value in row: if isinstance(value, bool): raise TypeError(f"{name} contains boolean value {value!r}") if not isinstance(value, _NUMERIC_TYPES): raise TypeError(f"{name} contains non‑numeric value {value!r}") return [[float(value) for value in row] for row in rows] def matmul(a: list[list[float]], b: list[list[float]]) -> list[list[float]]: """Multiply two matrices ``a`` and ``b``. Parameters ---------- a: list[list[float]] Left operand matrix. b: list[list[float]] Right operand matrix. Returns ------- list[list[float]] The product matrix. Raises ------ ValueError If matrix dimensions are incompatible or if a matrix is ragged. TypeError If inputs are not sequences of numeric values. """ a_matrix = _validate_matrix(a, "a") b_matrix = _validate_matrix(b, "b") num_rows_a = len(a_matrix) num_cols_a = len(a_matrix[0]) num_rows_b = len(b_matrix) num_cols_b = len(b_matrix[0]) if num_cols_a != num_rows_b: raise ValueError( f"Incompatible dimensions: a has {num_cols_a} columns; " f"b has {num_rows_b} rows" ) result: list[list[float]] = [ [0.0 for _ in range(num_cols_b)] for _ in range(num_rows_a) ] for i in range(num_rows_a): for j in range(num_cols_b): dot_product = 0.0 for k in range(num_cols_a): dot_product += a_matrix[i][k] * b_matrix[k][j] result[i][j] = dot_product return result def transpose(matrix: list[list[float]]) -> list[list[float]]: """Return the transpose of *matrix*. Parameters ---------- matrix: list[list[float]] Input matrix as a list of rows. Returns ------- list[list[float]] Transposed matrix. Raises ------ TypeError, ValueError Propagated from :func:`_validate_matrix` when the input is not a rectangular numeric matrix. """ m_matrix = _validate_matrix(matrix, "matrix") rows, cols = len(m_matrix), len(m_matrix[0]) return [[m_matrix[i][j] for i in range(rows)] for j in range(cols)] def rotate(matrix: list[list[float]]) -> list[list[float]]: """Return the matrix rotated 90° clockwise. Parameters ---------- matrix: list[list[float]] Input matrix as a list of rows. Returns ------- list[list[float]] Rotated matrix. Raises ------ TypeError, ValueError Propagated from :func:`_validate_matrix` when the input is not a rectangular numeric matrix. """ m_matrix = _validate_matrix(matrix, "matrix") return [list(row) for row in zip(*reversed(m_matrix))] def rot_2d(angle: float, unit: Literal["deg", "rad"] = "deg") -> list[list[float]]: """Return a 2×2 rotation matrix for a given angle. Parameters ---------- angle : float Rotation angle. unit : {"deg", "rad"}, default "deg" Whether *angle* is given in degrees or radians. Returns ------- list[list[float]] 2×2 rotation matrix. """ theta = math.radians(angle) if unit == "deg" else angle c = math.cos(theta) s = math.sin(theta) return [[c, -s], [s, c]] def print_matrix(title: str, matrix: list[list[float]]) -> None: """Print a title followed by each matrix row on its own line.""" print(title) for row in matrix: print(" ", row) if __name__ == "__main__": # Matrix multiplication tests a1 = [[1, 2, 3], [4, 5, 6]] b1 = [[7, 8], [9, 10], [11, 12]] print_matrix("Matrix A:", a1) print_matrix("Matrix B:", b1) product = matmul(a1, b1) print_matrix("A x B:", product) assert product == [[58, 64], [139, 154]], "matmul first test failed" print() a2 = [[1, 2], [3, 4]] b2 = [[5, 6], [7, 8]] print_matrix("Matrix A:", a2) print_matrix("Matrix B:", b2) product = matmul(a2, b2) print_matrix("A x B:", product) assert product == [[19, 22], [43, 50]], "matmul second test failed" print() identity = [[1, 0], [0, 1]] print_matrix("Matrix A:", a2) print_matrix("Identity matrix:", identity) product = matmul(a2, identity) print_matrix("A x Identity:", product) assert product == a2, "matmul identity test failed" print() # Dimension mismatch must raise ValueError try: matmul([[1, 2]], [[1, 2]]) except ValueError as error: print(f"Expected error for mismatched dimensions: {error}") else: raise AssertionError("matmul did not raise ValueError") print() # Transpose tests print_matrix("Matrix:", a1) transposed = transpose(a1) print_matrix("Transposed:", transposed) assert transposed == [[1, 4], [2, 5], [3, 6]], "transpose first test failed" assert transpose(transposed) == a1, "double transpose failed" print() column = [[1], [2], [3]] print_matrix("Matrix:", column) transposed = transpose(column) print_matrix("Transposed:", transposed) assert transposed == [[1, 2, 3]], "transpose column test failed" print() # Rotation tests print_matrix("Matrix:", a1) rotated = rotate(a1) print_matrix("Rotated 90 degrees clockwise:", rotated) assert rotated == [[4, 1], [5, 2], [6, 3]], "rotate 2x3 test failed" print() square = [[1, 2], [3, 4]] print_matrix("Matrix:", square) rotated = rotate(square) print_matrix("Rotated 90 degrees clockwise:", rotated) assert rotated == [[3, 1], [4, 2]], "rotate square test failed" print() # Four rotations must return the original matrix rotated = square for _ in range(4): rotated = rotate(rotated) print_matrix("Square matrix rotated four times:", rotated) assert rotated == square, "four rotations did not return original" print() # 2D rotation matrix tests rotation_0 = rot_2d(0) print_matrix("Rotation matrix for 0 degrees:", rotation_0) assert rotation_0 == [[1.0, -0.0], [0.0, 1.0]], "rot_2d 0 degrees failed" print() rotation_90 = rot_2d(90) print_matrix("Rotation matrix for 90 degrees:", rotation_90) point = [[1], [0]] rotated_point = matmul(rotation_90, point) print_matrix("Point (1, 0) rotated by 90 degrees:", rotated_point) assert math.isclose(rotated_point[0][0], 0.0, abs_tol=1e-9), "rot_2d x failed" assert math.isclose(rotated_point[1][0], 1.0, abs_tol=1e-9), "rot_2d y failed" print() print("All tests passed.") # This is a test commit print("This is a test") print(tabulate(rot_2d(1.0)))