 
     coefficient * numerator / denominator
 
-shuffleSet = (set) ->
+shuffleSet = (prng, set) ->
     values = set.copy()
     for index in [values.length - 1...0]
-        randomIndex = randomBoundedInteger(0, index)
+        randomIndex = randomBoundedInteger(prng, 0, index)
 
         tmp = values[index]
         values[index] = values[randomIndex]


isObject = isType("Object")

randomBoundedFloat = (min = 0, max = 1) ->
    spread = max - min
    Math.random() * spread + min

randomBoundedInteger = (min = 0, max = 1) ->
    spread = 1 + max - min
    Math.floor(Math.random() * spread) + min

randomColor = ->
    byte = kind: "integer", min: 0, max: 255
    mutator = (bytes) ->
        [red, green, blue] = bytes
        { red, green, blue }

    new Stochator(byte, byte, byte, mutator).next

randomNormallyDistributedFloat = (mean, stdev, min, max) ->
    seed = randomBoundedFloat()
    float = inverseNormalCumulativeDistribution(seed) * stdev + mean
    if min? and max?
        Math.min(max, Math.max(min, float))
    else
        float

randomCharacter = (lowercase) ->
    [min, max] = if lowercase then [97, 122] else [65, 90]
    mutator = (charCode) -> String.fromCharCode(charCode)
    new Stochator({ kind: "integer", min, max }, mutator).next

randomSetMember = (set) ->
    max = set.length - 1
    set.get(randomBoundedInteger(0, max))

randomSetMemberWithoutReplacement = (set) ->
    return undefined unless set.get(0)
    set.length -= 1
    set.pop(randomBoundedInteger(0, set.length))

randomWeightedSetMember = (set, weights) ->
    [member, weightSum, float] = [undefined, 0, randomBoundedFloat()]
    set.each((value, index) ->
        return if member
        weight = weights.get(index)


    values

floatGenerator = (prng, min, max, mean, stdev) ->
    if mean and stdev
        -> randomNormallyDistributedFloat(prng, mean, stdev, min, max)
    else
        -> randomBoundedFloat(prng, min, max)

integerGenerator = (prng, min = 0, max = 1) ->
    -> randomBoundedInteger(prng, min, max)

setGenerator = (prng, values, replacement = true, shuffle = false, weights = null) ->
    if not values or not values.length
        throw Error("Must provide a 'values' array for a set generator.")

    set = new Set(values)
    if shuffle
        -> shuffleSet(prng, set)
    else if replacement
        if weights
            weightsSet = new Set(weights)
            -> randomWeightedSetMember(prng, set, weightsSet)
        else
            -> randomSetMember(prng, set)
    else
        -> randomSetMemberWithoutReplacement(prng, set)

createGenerator = (config) ->
    kind = config.kind or "float"
    prng = config.prng or Math.random
    generator = switch kind
        when "float"
            { min, max, mean, stdev } = config
            floatGenerator(prng, min, max, mean, stdev)
        when "integer"
            integerGenerator(prng, config.min, config.max)
        when "set"
            { values, replacement, shuffle, weights } = config
            setGenerator(prng, values, replacement, shuffle, weights)
        when "color", "rgb" then randomColor(prng)
        when "a-z", "A-Z" then randomCharacter(prng, kind is "a-z")
    if not generator
        throw Error("#{ kind } not a recognized kind.")
    else
        generator

