Source: compute/sampleElection/sampleCanDnGeom2D.js

/** @module */

/**
 * Use this to sample a random candidate from a distribution.
 * @param {object} canDnGeom
 * @returns {object} x,y coordinates
 */
export default function sampleCanDnGeom2D(canDnGeom, rng) {
    const { x, y, w, densityProfile } = canDnGeom

    const randomSample = (densityProfile === 'gaussian') ? randomInsideGaussian : randomInsideCircle
    const canPoint = randomSample(x, y, w * 0.5, rng)
    return canPoint
}

function randomInsideCircle(X, Y, R, rng) {
    const [a, b] = randomUnitCircle(rng)
    const x = X + R * a
    const y = Y + R * b
    return [x, y]
}

function randomUnitCircle(rng) {
    let a; let b; let c
    do {
        a = 2 * rng() - 1
        b = 2 * rng() - 1
        c = a * a + b * b
    } while (c > 1 || c === 0)
    return [a, b]
}

const invSqrt2 = 1 / Math.sqrt(2)

/**
 * Two samples from gaussian around center with stdev scaled to R.
 * @param {number} X center.x
 * @param {number} Y center.y
 * @param {number} R radius = stdev * sqrt(2)
 * @returns {object} point
 */
function randomInsideGaussian(X, Y, R, rng) {
    const [a, b] = randomStandardNormal(rng)
    // pdfN01 fits inside circle of radius sqrt(2) and density pdfN01([0,0])
    // So we increase the density a little more to fit inside unit circle.
    // Then expand to fill circle of radius R.
    // The circle has a radius R = stdev * sqrt(2).
    const scale = R * invSqrt2
    const x = X + scale * a
    const y = Y + scale * b
    return [x, y]
}

/**
 * Marsaglia Method
 * https://www.alanzucconi.com/2015/09/16/how-to-sample-from-a-gaussian-distribution/
 * @returns {number[]} Two samples from Random standard normal with mean 0 and stdev 1.
 */
function randomStandardNormal(rng) {
    let a; let b; let c
    do {
        a = 2 * rng() - 1
        b = 2 * rng() - 1
        c = a * a + b * b
    } while (c > 1 || c === 0)
    const d = Math.log(c) / c
    const e = Math.sqrt(-2 * d)
    return [e * a, e * b]
}