# Problem Description:
The problem requires calculating the number of unpolluted cells in a matrix after certain points are polluted. Each polluted point affects its surrounding cells in a specific pattern. The matrix size is defined by `m` rows and `n` columns, and the polluted points are given as a list of coordinates. The function should return the number of unpolluted cells remaining in the matrix after considering the pollution spread from the given points.

# Visual Facts:
1. The matrix is represented as a grid with rows and columns.
2. Polluted points are marked in black.
3. Each polluted point affects its surrounding cells, marking them in gray.
4. The pollution spreads to the 8 surrounding cells (up, down, left, right, and diagonals).
5. The initial matrix is completely unpolluted (all cells are white).

# Visual Patterns:
1. **Pollution Spread Pattern**: Each polluted point (black cell) affects its 8 neighboring cells (gray cells).
2. **Edge Handling**: If a polluted point is near the edge of the matrix, the pollution does not spread outside the matrix boundaries.
3. **Counting Unpolluted Cells**: The total number of cells in the matrix is `m * n`. The number of unpolluted cells can be calculated by subtracting the number of polluted and affected cells from the total number of cells.
4. **Overlap Handling**: If multiple polluted points affect the same cell, it should only be counted once as polluted.
5. **Index**： The given polluted point 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 - 1][y - 1] = False
            if y < n - 1:
                matrix[x - 1][y + 1] = False
        if x < m - 1:
            matrix[x + 1][y] = False
            if y > 0:
                matrix[x + 1][y - 1] = False
            if y < n - 1:
                matrix[x + 1][y + 1] = False
        if y > 0:
            matrix[x][y - 1] = False
        if y < n - 1:
            matrix[x][y + 1] = 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)], 10),
        (6, 4, [(4,2)], 15),
        (6, 5, [(2,2),(2,4)], 15),
        (6, 5, [(2,2),(5,4)], 12),
        (6, 5, [(2,2)], 21),
        
    ]
    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()