All files / algorithms/random/src random.ts

100% Statements 28/28
100% Branches 10/10
100% Functions 2/2
100% Lines 26/26

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 55 56 57 58  2x 2x   2x 3x 2x       1x 1x 1x 1x 1x 1x     2x   2x 3x 1x   2x 1x           1x 1x   3x             1x 1x     1x   3x   1x       2x        
// @src https://basarat.gitbook.io/algorithms/basics/tips#random-numbers
export const random = (min: number, max: number): number => {
    if (!window.crypto) {
        return ~~(Math.random() * (max - min) + min);
    } else {
        // eslint-disable-next-line compat/compat
        const randomBuffer = new Uint32Array(1);
 
        window.crypto.getRandomValues(randomBuffer);
 
        const randomNumber = randomBuffer[0] / (0xffffffff + 1);
 
        min = Math.ceil(min);
        max = Math.floor(max);
 
        return Math.floor(randomNumber * (max - min + 1)) + min;
    }
};
 
// @src https://github.com/trekhleb/javascript-algorithms/blob/master/src/algorithms/statistics/weighted-random/weightedRandom.js
export const weightedRandom = <Item>(items: Item[], weights: number[]): Item | void => {
    if (items.length !== weights.length) {
        throw new Error("Items and weights must be of the same size");
    }
 
    if (!items.length) {
        throw new Error("Items must not be empty");
    }
 
    // Preparing the cumulative weights array.
    // For example:
    // - weights = [1, 4, 3]
    // - cumulativeWeights = [1, 5, 8]
    const cumulativeWeights: number[] = [];
    for (let i = 0; i < weights.length; i += 1) {
        // eslint-disable-next-line security/detect-object-injection
        cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
    }
 
    // Getting the random number in a range of [0...sum(weights)]
    // For example:
    // - weights = [1, 4, 3]
    // - maxCumulativeWeight = 8
    // - range for the random number is [0...8]
    const maxCumulativeWeight = cumulativeWeights[cumulativeWeights.length - 1];
    const randomNumber = random(0, maxCumulativeWeight);
 
    // Picking the random item based on its weight.
    // The items with higher weight will be picked more often.
    for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
        // eslint-disable-next-line security/detect-object-injection
        if (cumulativeWeights[itemIndex] >= randomNumber) {
            // eslint-disable-next-line security/detect-object-injection
            return items[itemIndex];
        }
    }
};