# Problem Description
The problem requires implementing a matrix transformation function that takes a NxN input matrix and produces a NxN output matrix following specific rotation patterns. The transformation appears to involve both repositioning and rearranging elements in a systematic way.

# Visual Facts
1. Matrix Dimensions:
   - Both input and output matrices are 4x4
   - Two example pairs are shown
   - A green arrow indicates a rotation transformation from input to output
   
2. First Example Contents:
   - Input Matrix Row 1: A, B, C, D
   - Input Matrix Row 2: +, -, *, /
   - Input Matrix Row 3: D, C, B, A
   - Input Matrix Row 4: /, *, -, +

   - Output Matrix Column 1: +, -, *, /
   - Output Matrix Column 2: A, B, C, D
   - Output Matrix Column 3: /, *, -, +
   - Output Matrix Column 4: D, C, B, A

3. Second Example Contents:
   - Input Matrix Row 1: 4, @, 1, 8
   - Input Matrix Row 2: #, a, Q, E
   - Input Matrix Row 3: 9, ?, 6, &
   - Input Matrix Row 4: b, $, F, t

   - Output Matrix Column 1: t, F, $, b
   - Output Matrix Column 2: &, 6, ?, 9
   - Output Matrix Column 3: E, Q, a, #
   - Output Matrix Column 4: 8, 1, @, 4

# Visual Patterns
- The input matrix is rotated 180 degrees clockwise to produce the output matrix


# Solution
from typing import List

def solution(input_matrix: List[List[str]]) -> List[List[str]]:
    """
    Transform the input matrix based on the pattern shown in the figure
    
    Parameters:
        input_matrix (List[List[str]]): Input matrix as a 2d array.
    
    Returns:
        output_matrix (List[List[str]]): Output matrix as a 2d array.
    """
    # To rotate 180 degrees, we can reverse the rows and then reverse each row
    # Or reverse each row and then reverse the rows - both work
    rows = len(input_matrix)
    cols = len(input_matrix[0])
    
    # Create a new matrix to store the result
    new_matrix = []
    for i in range(rows-1, -1, -1):
        new_row = []
        for j in range(cols-1, -1, -1):
            new_row.append(input_matrix[i][j])
        new_matrix.append(new_row)
    
    return new_matrix


# Test Script
def test():
    test_cases = [
        [['1', '2', '3'], ['4', '#', '6'], ['D', '8', 'a']],
        [['1', 'a'], ['3', '#']],
        [['4', '@', '1', '8'], ['#', 'a', 'Q', '&'], ['9', '?', '&', '%'], ['b', '$', 'F', 't']],
        [['6', '@', '2', '1'], ['9', '#', 'Q', '1'], ['9', '4', '5', '4'], ['1', '1', '1', '1']],
        [['4', '2'], ['6', '9']],
        [['7', '8', '9'], ['+', '#', '_'], [')', '8', '$']],
        [['6', '@', '2', '1', '&'], ['9', '#', 'Q', '1', '@'], ['9', '4', '5', '4', '!'], ['1', '1', '1', '1', '?'], ['(', ')', '#', '%', '5']],
        [['6', '@', '2', '1', '&', ']'], ['9', '#', 'Q', '1', '@', '['], ['9', '4', '5', '4', '!', '='], ['1', '1', '1', '1', '?', '9'], ['(', ')', '#', '%', '5', '4'], ['(', '6', '#', '4', '5', '-']],
        [['!', '2', '$'], ['6', '!', '9'], ['+', '!', '-']],
        [['1', '1', '1'], ['2', '2', '2'], ['3', '3', '3']],
    ]

    for i, input_matrix in enumerate(test_cases):
        # Create deep copy of input matrix
        original_input_matrix = [row[:] for row in input_matrix]
        
        try:
            result = solution(input_matrix)
        except Exception as e:
            error_message = (
                f"An exception occurred while running the test case:\n"
                f"`input_matrix`: {original_input_matrix}\n\n"
                f"Exception: '{str(e)}'"
            )
            assert False, error_message

        # Create expected result by rotating 180 degrees
        expected = []
        rows = len(original_input_matrix)
        cols = len(original_input_matrix[0])
        for i in range(rows-1, -1, -1):
            new_row = []
            for j in range(cols-1, -1, -1):
                new_row.append(original_input_matrix[i][j])
            expected.append(new_row)

        if result != expected:
            error_message = (
                f"The following Test Case failed:\n"
                f"`input_matrix`: {original_input_matrix}\n\n"
                f"Actual Output: {result}\n"
                f"Expected Output: {expected}"
            )
            assert False, error_message

test()