import math
import numpy as np
import random
import matplotlib.pyplot as plt


def random_in_square(N):
    return [(2 * random.random() - 1, 2 * random.random() - 1) for i in range(N)]


def random_in_square_reject(N):
    points = []
    while len(points) < N:
        pt = (2 * random.random() - 1, 2 * random.random() - 1)
        if pt[0] ** 2 + pt[1] ** 2 <= 1:
            points.append(pt)
    return points


def x_then_y(N):
    points = []
    while len(points) < N:
        x = 2 * random.random() - 1
        max_y = math.sqrt(1 - x**2)
        y = (random.random() - 0.5) * 2 * max_y
        points.append((x, y))
    return points


def angle_then_radius(N):
    points = []
    while len(points) < N:
        angle = random.random() * 2 * math.pi
        radius = random.random()

        x = radius * math.cos(angle)
        y = radius * math.sin(angle)

        points.append((x, y))
    return points


def angle_then_scaled_radius(N):
    points = []
    while len(points) < N:
        angle = random.random() * 2 * math.pi
        radius = math.sqrt(random.random())

        x = radius * math.cos(angle)
        y = radius * math.sin(angle)

        points.append((x, y))
    return points


# from: http://extremelearning.com.au/how-to-generate-uniformly-random-points-on-n-spheres-and-n-balls/
def muller(N, d):
    points = []
    while len(points) < N:
        u = np.random.normal(0, 1, d)
        norm = np.sum(u**2) ** (0.5)
        r = random.random() ** (1.0 / d)
        pt = r * u / norm
        points.append(tuple(pt))
    return points


fig, ax = plt.subplots()
circle1 = plt.Circle((0, 0), 1, color="black", fill=False)
ax.add_patch(circle1)

num = 100_000

# points = random_in_square(num)
# points = random_in_square_reject(num)
points = x_then_y(num)
# points = angle_then_radius(num)
# points = angle_then_scaled_radius(num)
# points = muller(num,2)

x = [p[0] for p in points]
y = [p[1] for p in points]

plt.scatter(x, y, marker=".", s=1)
plt.show()
