# Problem Description
This is a geometric problem that involves analyzing intersections between a polyline (chain) and a rectangular region on a grid. The goal is to count two types of intersections:
1. Red regions: where the polyline crosses the yellow rectangle's boundary vertically
2. Green regions: where the polyline crosses the yellow rectangle's boundary horizontally

The solution needs to return a tuple containing the count of these two types of intersections.

# Visual Facts
1. Grid Properties:
   - 4×4 grid (n=4)
   - Grid points labeled with coordinates (x,y)
   - Origin (0,0) at top-left

2. Yellow Region:
   - Rectangular area marked with dashed yellow border
   - Top-left coordinate: (1,0)
   - Bottom-right coordinate: (2,2)

3. Blue Chain:
   - Connected blue line segments
   - Moves only horizontally or vertically
   - Chain points: (0,0)→(0,1)→(1,1)→(1,0)→(2,0)→(3,0)→(3,1)→(2,1)→(2,2)→(2,3)→(1,3)→(1,2)

4. Intersection Markers:
   - Red circles (3): Mark vertical crossings at boundaries
      * Between (0,1) and (1,1). (0,1) is outside the yellow region, (1,1) is inside
      * Between (2,0) and (3,0). (2,0) is inside the yellow region, (3,0) is outside
      * Between (3,1) and (2,1). (3,1) is outside the yellow region, (2,1) is inside
   - Green circles (2): Mark horizontal crossings at boundaries
      * Between (2,2) and (2,3). (2,2) is inside the yellow region, (2,3) is outside
      * Between (1,2) and (1,3). (1,2) is inside the yellow region, (1,3) is outside

# Visual Patterns
1. Determining the points inside the yellow region:
   - use (a, b) to represent the top-left corner of the yellow region, and (c, d) to represent the bottom-right corner of the yellow region.
   - a point (x, y) is inside the yellow region if a ≤ x ≤ c and b ≤ y ≤ d.

2. Intersection Classification Rules:
   - Vertical crossings (red):
     * Chain segment must be vertical (identical y coordinate) at crossing point
   - Horizontal crossings (green):
     * Chain segment must be horizontal (identical x coordinate) at crossing point


# Solution
from typing import List, Tuple

def solution(n: int, chain: List[Tuple[int, int]], top_left: Tuple[int, int], bottom_right: Tuple[int, int]) -> Tuple[int, int]:
    """
    Given a chain and a yellow region in an n x n 2D matrix, find the number of red regions and green regions that are formed.

    Parameters:
        n (int): The size of the matrix.
        chain (List[Tuple[int, int]]): Coordinates of each node in the chain in order.
        top_left (Tuple[int, int]): Coordinates of the top-left corner of the yellow region.
        bottom_right (Tuple[int, int]): Coordinates of the bottom-right corner of the yellow region.

    Returns:
        Tuple[int,int]: The number of red regions and green regions respectively.
    """
    def is_inside(x: int, y: int) -> bool:
        """ Check if a point is inside the yellow region """
        return top_left[0] <= x <= bottom_right[0] and top_left[1] <= y <= bottom_right[1]

    v_crossings = 0
    h_crossings = 0

    for i in range(len(chain) - 1):
        x1, y1 = chain[i]
        x2, y2 = chain[i + 1]

        # Check if one point is inside and the other is outside the yellow region
        if is_inside(x1, y1) != is_inside(x2, y2):
            if x1 == x2:
                v_crossings += 1
            else:
                h_crossings += 1

    return (v_crossings, h_crossings)


# Test Script
def test():
    test_cases = [
        (2, [(0, 0), (0, 1), (1, 1), (1, 0)], (1, 0), (1, 1), (0, 1)),
        (2, [(0, 0), (0, 1), (1, 1), (1, 0)], (1, 1), (1, 1), (1, 1)),
        (2, [(0, 0), (0, 1), (1, 0)], (1, 1), (1, 1), (0, 0)),
        (3, [(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (2, 2), (1, 2), (1, 1)], (1, 0), (2, 1), (2,1)),
        (3, [(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (2, 2), (1, 2), (1, 1)], (1, 0), (2, 2), (0, 1)),
        (4, [(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (3, 0), (3, 1), (2, 1), (2, 2), (2, 3), (1, 3), (1, 2)], (1, 0),(2, 2),(2,3)),
        (4, [(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (3, 0), (3, 1), (2, 1), (2, 2), (2, 3), (1, 3)], (1, 0), (2, 2), (1, 3)),
        (4, [(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (3, 0), (3, 1), (2, 1), (2, 2), (2, 3), (1, 3), (1, 2)], (1, 0), (2, 2), (2,3)),
        (4, [(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (3, 0), (3, 1), (2, 1), (2, 2), (2, 3), (1, 3), (1, 2), (0, 2)], (1, 0), (2, 2), (2, 4)),
        (4, [(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (3, 0), (3, 1), (2, 1), (2, 2), (2, 3), (1, 3), (1, 2), (0, 2)], (0, 0), (2, 2), (2, 2)),
        (4, [(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (3, 0), (3, 1), (2, 1), (2, 2), (2, 3), (1, 3), (1, 2), (0, 2)], (0, 0), (3, 3), (0, 0)),
    ]

    for i, (n, chain, top_left, bottom_right, expected_output) in enumerate(test_cases):
        try:
            output = solution(n, chain, top_left, bottom_right)
        except Exception as e:
            error_message = (
                f"An exception occurred while running the test case:\n"
                f"`n`: {n}\n"
                f"`chain`: {chain}\n" 
                f"`top_left`: {top_left}\n"
                f"`bottom_right`: {bottom_right}\n\n"
                f"Exception: '{str(e)}'"
            )
            assert False, error_message

        if output != expected_output:
            error_message = (
                f"The following Test Case failed:\n"
                f"`n`: {n}\n"
                f"`chain`: {chain}\n"
                f"`top_left`: {top_left}\n" 
                f"`bottom_right`: {bottom_right}\n\n"
                f"Actual Output: {output}\n"
                f"Expected Output: {expected_output}"
            )
            assert False, error_message

test()