 
 isObject = isType("Object")
 
-randomBoundedFloat = (min = 0, max = 1) ->
+randomBoundedFloat = (prng, min = 0, max = 1) ->
     spread = max - min
-    Math.random() * spread + min
-
-randomBoundedInteger = (min = 0, max = 1) ->
+    prng() * spread + min
+
+randomBoundedInteger = (prng, min = 0, max = 1) ->
     spread = 1 + max - min
-    Math.floor(Math.random() * spread) + min
-
-randomColor = ->
-    byte = kind: "integer", min: 0, max: 255
+    Math.floor(prng() * spread) + min
+
+randomColor = (prng) ->
+    byte = kind: "integer", min: 0, max: 255, prng: prng
     mutator = (bytes) ->
         [red, green, blue] = bytes
         { red, green, blue }
 
     new Stochator(byte, byte, byte, mutator).next
 
-randomNormallyDistributedFloat = (mean, stdev, min, max) ->
-    seed = randomBoundedFloat()
+randomNormallyDistributedFloat = (prng, mean, stdev, min, max) ->
+    seed = randomBoundedFloat(prng)
     float = inverseNormalCumulativeDistribution(seed) * stdev + mean
     if min? and max?
         Math.min(max, Math.max(min, float))
     else
         float
 
-randomCharacter = (lowercase) ->
+randomCharacter = (prng, 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) ->
+    new Stochator({ kind: "integer", min, max, prng }, mutator).next
+
+randomSetMember = (prng, set) ->
     max = set.length - 1
-    set.get(randomBoundedInteger(0, max))
-
-randomSetMemberWithoutReplacement = (set) ->
+    set.get(randomBoundedInteger(prng, 0, max))
+
+randomSetMemberWithoutReplacement = (prng, 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.pop(randomBoundedInteger(prng, 0, set.length))
+
+randomWeightedSetMember = (prng, set, weights) ->
+    [member, weightSum, float] = [undefined, 0, randomBoundedFloat(prng)]
     set.each((value, index) ->
         return if member
         weight = weights.get(index)


    values

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

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

setGenerator = (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(set)
    else if replacement
        if weights
            weightsSet = new Set(weights)
            -> randomWeightedSetMember(set, weightsSet)
        else
            -> randomSetMember(set)
    else
        -> randomSetMemberWithoutReplacement(set)

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



    coefficient * numerator / denominator

shuffleSet = (prng, set) ->
    values = set.copy()
    for index in [values.length - 1...0]
        randomIndex = randomBoundedInteger(prng, 0, index)

        tmp = values[index]
        values[index] = values[randomIndex]
