# Problem Description
The task is to generate a dataset of 1000 2D points that follows a specific distribution pattern shown in the figure. The points should be distributed within a 1x1 square area with special constraints around a circular region. The output should be a 2D array of shape (1000, 2) where each row represents the (x,y) coordinates of a point.

# Visual Facts
1. The plot shows a square coordinate system with both x and y axes ranging from 0.0 to 1.0
2. Blue dots are scattered throughout the square area
3. There's an orange dashed line labeled "R=0.25" indicating a radius
4. The plot appears to have approximately 1000 points
5. Points appear to be more sparse in a circular region around (0.5, 0.5)
6. The density of points outside this circular region appears uniform

# Visual Patterns
1. Distribution Pattern:
   - The points appear to avoid a circular region centered at (0.5, 0.5)
   - The circular region has a radius of 0.25 (as indicated by "R=0.25")
   - Outside this circular region, points appear to be uniformly distributed

2. Mathematical Constraints:
   - For any point (x,y), 0 ≤ x ≤ 1 and 0 ≤ y ≤ 1 (square boundary)
   - Points are less likely to appear within: (x-0.5)² + (y-0.5)² ≤ 0.25²
   - The distribution appears to be uniform random outside this circular region

3. Generation Rules:
   - Generate random points uniformly in the 1x1 square
   - Reject points that fall within the circle defined by (x-0.5)² + (y-0.5)² ≤ 0.25²
   - Continue generating points until 1000 valid points are obtained


# Solution
from typing import List
import numpy as np

def solution() -> List[List[float]]:
    """
    generate a set of 1000 data points that match the distribution shown in the figure

    Returns:
        List[List[float]]: A 2D array of shape (1000, 2), where each row contains the x and y coordinates of a point.
    """
    num_points = 1000
    points = np.zeros((num_points, 2))  # Initialize array to hold points
    center_x, center_y = 0.5, 0.5
    radius = 0.25
    count = 0

    while count < num_points:
        x, y = np.random.rand(2)  # Generate random x, y coordinates
        # Check if the point is outside the circle
        if (x - center_x)**2 + (y - center_y)**2 >= radius**2:
            points[count] = [x, y]
            count += 1

    return points.tolist()



# Test Script

import numpy as np

def quartile_test(points):
    x_quartiles = np.percentile(points[:, 0], [25, 50, 75])
    y_quartiles = np.percentile(points[:, 1], [25, 50, 75])

    theoretical_quartiles = [0.2, 0.5, 0.8]
    x_check = np.allclose(x_quartiles, theoretical_quartiles, atol=0.1)
    y_check = np.allclose(y_quartiles, theoretical_quartiles, atol=0.1)
    
    return x_check and y_check

def test():
    try:
        points = solution()
        points = np.array(points)
    except Exception as e:
        error_message = (
            f"An exception occurred while running the solution function:\n"
            f"Exception: '{str(e)}'"
        )
        assert False, error_message

    # Test shape of output
    if points.shape != (1000, 2):
        error_message = (
            f"The following Test Case failed:\n"
            f"`points.shape`: {points.shape}\n"
            f"Expected Output: (1000, 2)"
        )
        assert False, error_message

    # Test exclusion zone
    center_x, center_y = 0.5, 0.5
    radius = 0.25
    distances = np.sqrt((points[:, 0] - center_x)**2 + (points[:, 1] - center_y)**2)
    if not np.all(distances >= radius):
        error_message = (
            f"The following Test Case failed:\n"
            f"`center_point`: ({center_x}, {center_y})\n"
            f"`radius`: {radius}\n"
            f"`min_distance`: {np.min(distances)}\n"
            f"Points detected inside the exclusion zone"
        )
        assert False, error_message

    # Test distribution uniformity
    if not quartile_test(points):
        error_message = (
            f"The following Test Case failed:\n"
            f"`x_quartiles`: {np.percentile(points[:, 0], [25, 50, 75])}\n"
            f"`y_quartiles`: {np.percentile(points[:, 1], [25, 50, 75])}\n"
            f"`expected_quartiles`: [0.2, 0.5, 0.8]\n"
            f"Points distribution does not match expected uniformity"
        )
        assert False, error_message

test()