import { sum, map, isEmpty } from 'kyanite'
/**
* @name weighted
* @function
* @since v0.1.0
* @category Number
* @sig [any, Number][] -> RandomGenerator
* @description Takes a list of weighted values and creates a generator to pick one
* @param {Array} list An array of weighted values
* @returns {Array} An Array pair with a new generator at [0] and the next seed at [1]
* @example
* import { step, weighted } from 'randoscando'
*
* step(weighted([
* ['a', 20],
* ['b', 20],
* ['c', 20],
* ['d', 20],
* ['e', 20],
* ]), 'abc123') // => ['d', 0.8987810940016061]
*/
function weighted ([a, ...rest]) {
return {
value: a[0],
step (seed) {
if (isEmpty(rest)) {
return [weighted([[a]]), seed.next()]
}
const [, firstWeight] = a
const weightSum = Math.abs(firstWeight) + sum(map(([_, weight]) => Math.abs(weight), rest))
const selected = seed.next() * weightSum
let total = 0
for (const [val, prob] of [a, ...rest]) {
total += prob
if (selected <= total) {
return [weighted([[val]]), seed.next()]
}
}
return [weighted([[a]]), seed.next()]
}
}
}
export default weighted