import { sum } from './utils';
/**
 * Simple but effective PRNG function using the Mulberry32 agorhythm.
 * @param seed The seed of the number generator
 * @returns A callable PRNG machine that returns a number 0-1 when called
 */
export function mulberryPRNG(seed) {
    return function prng() {
        /* eslint-disable no-param-reassign, no-multi-assign, no-bitwise */
        let t = seed += 0x6D2B79F5;
        t = Math.imul(t ^ t >>> 15, t | 1);
        t ^= t + Math.imul(t ^ t >>> 7, t | 61);
        return ((t ^ t >>> 14) >>> 0) / 4294967296;
        /* eslint-enable */
    };
}
/**
 * Get a random number within the specified range
 * @param min The lowest possible value
 * @param max The highest possible value
 * @returns A random number between `min` and `max`
 */
export function getRandomRange(min, max) {
    if (min > max)
        throw new RangeError('min cannot be greater than max');
    return (max - min) * Math.random() + min;
}
/**
 * Randomly select an object out of an array of objects
 * @param objects A list of objects
 * @returns A randomly chosen object
 */
export function getRandomChoice(objects) {
    return objects[Math.floor(Math.random() * objects.length)];
}
/**
 * Randomly select an object out of an array of objects, with the possibility
 * of assigning different probabilities to each of the objects
 * @param objects A list of objects with their corresponding weights
 * @returns A randomly chosen object
 */
export function getWeightedRandomChoice(objects, weightFn) {
    if (objects.length === 0)
        throw new RangeError('Array cannot be empty');
    let currWeight = 0;
    const maxWeight = sum(...objects.map(weightFn));
    const expWeight = getRandomRange(0, maxWeight);
    for (let index = 0; index < objects.length; index++) {
        const obj = objects[index];
        currWeight += weightFn(obj, index);
        if (currWeight >= expWeight) {
            return obj;
        }
    }
    return objects[-1];
}
/**
 * Randomly shuffles an array, in-place.
 * @param array The array to shuffle
 */
export function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        // eslint-disable-next-line no-param-reassign
        [array[i], array[j]] = [array[j], array[i]];
    }
}
export function gaussianRandom(mean = 0, stdev = 1) {
    const u = 1 - Math.random(); // Converting [0,1) to (0,1]
    const v = Math.random();
    const z = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
    // Transform to the desired mean and standard deviation:
    return z * stdev + mean;
}
