# 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 an elliptical 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 ellipse centered at (0.5, 0.5).
4. The major axis of the ellipse is 0.25, and the minor axis is 0.125.
5. The plot appears to have approximately 1000 points.
6. Points are non-existent within the elliptical region around (0.5, 0.5).
7. The density of points outside this elliptical region appears uniform.

# Visual Patterns
1. Distribution Pattern:
   - The points appear to avoid an elliptical region centered at (0.5, 0.5).
   - The elliptical region has a major axis of 0.25 and a minor axis of 0.125.
   - Outside this elliptical 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 the ellipse defined by the equation:
     \[
     \left(\frac{x-0.5}{0.25}\right)^2 + \left(\frac{y-0.5}{0.125}\right)^2 \leq 1
     \]
   - The distribution appears to be uniform random outside this elliptical region.

3. Generation Rules:
   - Generate random points uniformly in the 1x1 square.
   - Reject points that fall within the ellipse defined by the equation above.
   - 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
    width = 0.5   # Width of the ellipse
    height = 0.25 # Height of the ellipse
    count = 0

    while count < num_points:
        x, y = np.random.rand(2)  # Generate random x, y coordinates
        # Check if the point is outside the ellipse
        if ((x - center_x)**2 / (width / 2)**2 +
                  (y - center_y)**2 / (height / 2)**2) >= 1:
            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_x_quartiles = [0.2, 0.5, 0.8]
    theoretical_y_quartiles = [0.2, 0.5, 0.8]
    x_check = np.allclose(x_quartiles, theoretical_x_quartiles, atol=0.1)
    y_check = np.allclose(y_quartiles, theoretical_y_quartiles, atol=0.1)
    
    return x_check and y_check

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

    # Shape test
    if points.shape != (1000, 2):
        error_message = (
            f"Generated points have incorrect shape:\n"
            f"Actual shape: {points.shape}\n"
            f"Expected shape: (1000, 2)"
        )
        assert False, error_message

    # Exclusion zone test
    center_x, center_y = 0.5, 0.5
    width = 0.5   # Width of the ellipse
    height = 0.25 # Height of the ellipse
    inside_points = points[((points[:, 0] - center_x)**2 / (width / 2)**2 +
                          (points[:, 1] - center_y)**2 / (height / 2)**2) < 1]
    
    if len(inside_points) > 0:
        error_message = (
            f"Found points inside the exclusion zone (ellipse):\n"
            f"Points inside exclusion zone: {inside_points}\n"
            f"Ellipse parameters:\n"
            f"Center: ({center_x}, {center_y})\n"
            f"Width: {width}\n"
            f"Height: {height}"
        )
        assert False, error_message

    # Distribution test
    if not quartile_test(points):
        x_quartiles = np.percentile(points[:, 0], [25, 50, 75])
        y_quartiles = np.percentile(points[:, 1], [25, 50, 75])
        error_message = (
            f"Points are not uniformly distributed:\n"
            f"Actual x quartiles: {x_quartiles}\n"
            f"Expected x quartiles: [0.2, 0.5, 0.8]\n"
            f"Actual y quartiles: {y_quartiles}\n"
            f"Expected y quartiles: [0.2, 0.5, 0.8]"
        )
        assert False, error_message

test()