All files / projects-dev/libs/algorithms/src random.ts

96.29% Statements 52/54
91.66% Branches 11/12
100% Functions 3/3
96.29% Lines 52/54

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 551x 1x 1x 1x 1x 1126x     1126x 1126x 1126x 1126x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1126x 1126x 1126x 1126x 1126x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1004x 3x 3x 1001x 1001x 1001x 1001x 1001x 1001x 1001x 1001x 1001x 1001x 1001x 2001x 1001x 1001x  
/**
 * Generate a random number between 0 and 1 using the `Math.random()` function or `window.crypto`
 * @returns A random number between 0 and 1.
 */
const generateRandom = (): number => {
    if (!window.crypto) {
        return Math.random();
    }
    const randomBuffer = new Uint32Array(1);
    window.crypto.getRandomValues(randomBuffer);
    return randomBuffer[0] / (0xff_ff_ff_ff + 1);
};
 
/**
 * "Generate a random number between min and max."
 *
 * @param {number} min - The minimum value to return.
 * @param {number} max - The maximum value of the random number.
 * @returns A random number between the min and max values.
 */
export const random = (min: number, max: number): number => {
    const randomNumber = generateRandom();
    const minimum = Math.ceil(min);
    const maximum = Math.floor(max);
    return Math.floor(randomNumber * (maximum - minimum + 1)) + minimum;
};
 
/**
 * @src https://github.com/trekhleb/javascript-algorithms/blob/master/src/algorithms/statistics/weighted-random/weightedRandom.js
 * "Given an array of items and an array of weights, pick one item randomly based on the weights."
 *
 * The function is generic, so it can be used with any type of items
 * @param {Item[]} items - The array of items to pick from.
 * @param {number[]} weights - An array of numbers that represent the weight of each item.
 * @returns Nothing.
 */
export const weightedRandom = <Item>(items: Item[], weights: number[]): Item | null | undefined => {
    if (items.length !== weights.length || items.length === 0) {
        throw new Error(items.length === 0 ? "Items must not be empty" : "Items and weights must be of the same size");
    }
 
    // Preparing the cumulative weights array.
    const cumulativeWeights = weights.reduce(
        (acc: number[], curr: number, index: number) => [...acc, curr + (acc[index - 1] || 0)],
        [],
    );
 
    const maxCumulativeWeight = cumulativeWeights.at(-1) as number;
    const randomNumber = random(0, maxCumulativeWeight);
 
    return items.find((_item, index) => {
        return cumulativeWeights[index] >= randomNumber;
    });
};