
Randomizer.pudu
Pudu63 lines2.5 KB
1/** @Resilience.Randomizer.Seam — uniform draws for jitter and injection */2module PuduLangResilience.Randomizer34import Std.Decimal as Decimal5import Std.Random as Random6import PuduLangResilience.Utils.Numeric as Numeric7import PuduLangResilience.Utils.Shared as Shared89/** @Resilience.Randomizer.Randomizer — answers a whole number below a bound */10export type Randomizer = { draw: fn(Int) -> Int }111213export const SCALE: Int = 1000000000141516export fn system() -> Randomizer { fromGenerator(Random.fromClock()) }171819export fn seeded(seed: Int) -> Randomizer {20 match convertInteger[UInt64](seed) {21 case Some(value) => fromGenerator(Random.fromSeed(value))22 case None => fromGenerator(Random.fromSeed(1u64))23 }24}25262728export fn fixed(ratio: Decimal) -> Randomizer {29 let parts = if ratio <= 0d { 0 } else if ratio >= 1d { SCALE - 1 } else { Numeric.partsOf(ratio, SCALE) }30 Randomizer{draw: fn(bound: Int) -> Int {31 if bound <= 0 { 0 } else { Numeric.multiply(parts, bound) / SCALE }32 } }33}343536export fn below(source: &Randomizer, bound: Int) -> Int {37 if bound <= 0 { return 0 }38 let drawn = (source.draw)(bound)39 if drawn < 0 { 0 } else if drawn >= bound { bound - 1 } else { drawn }40}414243export fn fraction(source: &Randomizer) -> Int { below(source, SCALE) }444546export fn unit(source: &Randomizer) -> Float { Numeric.toFloat(fraction(source)) / Numeric.toFloat(SCALE) }47484950export fn chance(source: &Randomizer, rate: Decimal) -> Bool {51 if rate <= 0d { return false }52 if rate >= 1d { return true }53 fraction(source) < Numeric.partsOf(rate, SCALE)54}555657fn fromGenerator(start: Random.Generator) -> Randomizer {58 let state = Shared.shared(start)59 Randomizer{draw: fn(bound: Int) -> Int {60 Shared.change(&state, fn(generator: Random.Generator) -> (Random.Generator, Int) { Random.below(&generator, bound) })61 } }62}63