# Problem Description:
The problem involves calculating the number of unpolluted cells in a matrix after certain points have been polluted. The pollution spreads from a given point to its immediate neighboring cells (up, down, left, right). The matrix is defined by its number of rows (m) and columns (n), and the polluted points are provided as a list of coordinates. The task is to determine how many cells remain unpolluted after applying the pollution spread from all given points.

# Visual Facts:
1. The matrix is represented as a grid with rows and columns.
2. Polluted points are marked in black.
3. Pollution spreads to the immediate neighboring cells (up, down, left, right) from the polluted point, marking them in gray.
4. The initial matrix is clean with no polluted cells.
5. The final count of unpolluted cells is displayed after all pollution spreads are applied.

# Visual Patterns:
1. Each polluted point (black cell) affects its four immediate neighbors (gray cells).
2. If a polluted point is at the edge or corner of the matrix, the pollution spread is limited to the available neighboring cells within the matrix boundaries.
3. Overlapping pollution spreads do not double-count the pollution; a cell can only be polluted once.
4. The total number of cells in the matrix is m * n.
5. The number of unpolluted cells is calculated by subtracting the number of polluted cells (both black and gray) from the total number of cells.
6. The given polluted points are all 1-indexed.

# Solution
from typing import Tuple, List

def solution(m: int, n: int, polluted_points: List[Tuple[int, int]]) -> int:
    """
    Calculates the number of unpolluted cells in a matrix given polluted points and their spread pattern.

    Parameters:
        m (int): Number of rows in the matrix.
        n (int): Number of columns in the matrix.
        polluted_points (List[Tuple[int, int]]): List of coordinates representing polluted points.

    Returns:
        int: Number of unpolluted cells remaining in the matrix.
    """
    matrix = [[True for i in range(n)] for j in range(m)]
    # rewrite the eight nearest cells of polluted points to False
    for point in polluted_points:
        x, y = point[0] - 1, point[1] - 1
        matrix[x][y] = False
        if x > 0:
            matrix[x - 1][y] = False
        if y > 0:
            matrix[x][y - 1] = False
        if y < n - 1:
            matrix[x][y + 1] = False
        if x < m - 1:
            matrix[x + 1][y] = False
    # calculate the number of True block in matrix
    count = 0
    for i in range(m):
        for j in range(n):
            if matrix[i][j]:
                count += 1

    return count


# Test Script
def test():
    test_cases = [
        (6, 4, [(4,2),(2,4)], 15),
        (6, 4, [(4,2)], 19),
        (6, 5, [(2,2),(2,4)], 21),
        (6, 5, [(2,2),(5,4)], 20),
        (6, 5, [(2,2)], 25),
        
    ]
    for m, n, polluted_points, expected_output in test_cases:
        try:
            output = solution(m, n, polluted_points)
        except Exception as e:
            error_message = (
                f"An exception occurred while running the test case:\n"
                f"`m`: {m}\n"
                f"`n`: {n}\n"
                f"`polluted_points`: {polluted_points}\n\n"
                f"Exception: '{str(e)}'"
            )
            assert False, error_message
            
        if output != expected_output:
            error_message = (
                f"The following Test Case failed:\n"
                f"`m`: {m}\n"
                f"`n`: {n}\n" 
                f"`polluted_points`: {polluted_points}\n\n"
                f"Actual Output: {output}\n"
                f"Expected Output: {expected_output}"
            )
            assert False, error_message

test()