# Problem Description
This is a grid pollution tracking problem where:
- We have a matrix of size m×n
- Given a list of polluted points (coordinates)
- Each polluted point affects its entire row and column
- Need to calculate how many cells remain unpolluted after all pollution spreads
- A polluted point creates pollution in its row and column, marked by different colors (black for source, gray for spread)

# Visual Facts
1. Grid Properties:
   - Two examples are shown: 6×4 and 6×5 grids
   - Initial grids start completely clean (all white)
   - Each example shows two sequential pollution points

2. Color Coding:
   - Black squares: Source of pollution (given coordinates)
   - Gray squares: Areas affected by pollution spread
   - White squares: Unpolluted areas

3. Example Results:
   - First example (6×4 grid):
     * Pollute (4,2) then (2,4)
     * Final result: 8 unpolluted blocks
   
   - Second example (6×5 grid):
     * Pollute (2,2) then (5,4)
     * Final result: 12 unpolluted blocks

# Visual Patterns
1. Pollution Spread Rules:
   - When a point is polluted (black), the entire row and column become affected (gray)
   - Multiple polluted points can affect the same row/column
   - When pollution spreads overlap, the affected cells remain gray

2. Counting Rules:
   - Unpolluted cells are those not in any polluted row or column
   - A cell is polluted if it's either:
     * A source point (black)
     * In the same row as any polluted point
     * In the same column as any polluted point

3. Important Constraints:
   - Grid coordinates appear to be 1-based indexing
   - Pollution spreads instantly across entire rows and columns
   - Previous pollution patterns remain when new points are polluted


# 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)]
    for x, y in polluted_points:
        for i in range(m):
            matrix[i][y-1] = False
        for j in range(n):
            matrix[x-1][j] = 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)], 8),
        (6, 4, [(4,2)], 15),
        (6, 5, [(2,2),(2,4)], 15),
        (6, 5, [(2,2),(5,4)], 12),
        (6, 5, [(2,2)], 20),
        
    ]
    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()