///|
/// Stable pseudo-random generator used by every generative operation.
/// The Park-Miller recurrence is intentionally small and reproducible on all
/// MoonBit targets; it is not a cryptographic generator.
pub(all) struct Random {
mut state : Int64
initial_seed : Int64
mut draws : Int
}
///|
fn normalize_seed(seed : Int64) -> Int64 {
let reduced = seed % 2147483647L
if reduced < 0L {
-reduced
} else if reduced == 0L {
1L
} else {
reduced
}
}
///|
pub fn Random::new(seed : Int64) -> Random {
{ state: normalize_seed(seed), initial_seed: seed, draws: 0, }
}
///|
pub fn Random::seed(self : Random) -> Int64 {
self.initial_seed
}
///|
pub fn Random::draw_count(self : Random) -> Int {
self.draws
}
///|
pub fn Random::next_raw(self : Random) -> Int {
self.state = self.state * 48271L % 2147483647L
self.draws += 1
self.state.to_int()
}
///|
pub fn Random::below(self : Random, exclusive : Int) -> Int {
if exclusive <= 0 {
0
} else {
self.next_raw() % exclusive
}
}
///|
pub fn Random::between(self : Random, minimum : Int, maximum : Int) -> Int {
if maximum <= minimum {
minimum
} else {
let span = Int64::from_int(maximum) - Int64::from_int(minimum) + 1L
let high = Int64::from_int(self.next_raw())
let low = Int64::from_int(self.next_raw())
let combined = high * 2147483647L + low
(Int64::from_int(minimum) + combined % span).to_int()
}
}
///|
pub fn Random::chance(
self : Random,
numerator : Int,
denominator : Int,
) -> Bool {
if numerator <= 0 || denominator <= 0 {
false
} else if numerator >= denominator {
true
} else {
self.below(denominator) < numerator
}
}
///|
pub fn Random::choose_index(self : Random, length : Int) -> Int? {
if length <= 0 {
None
} else {
Some(self.below(length))
}
}
///|
pub fn Random::weighted_index(self : Random, weights : Array[Int]) -> Int? {
let mut total = 0
for weight in weights {
if weight > 0 {
total += weight
}
}
if total <= 0 {
return None
}
let ticket = self.below(total)
let mut cursor = 0
for index, weight in weights {
if weight > 0 {
cursor += weight
if ticket < cursor {
return Some(index)
}
}
}
Some(weights.length() - 1)
}
///|
pub fn[T] Random::shuffle(self : Random, input : Array[T]) -> Array[T] {
let result = input.copy()
let mut index = result.length() - 1
while index > 0 {
let other = self.below(index + 1)
let value = result[index]
result[index] = result[other]
result[other] = value
index -= 1
}
result
}
///|
pub fn Random::sample_without_replacement(
self : Random,
population : Int,
requested : Int,
) -> Array[Int] {
if population <= 0 || requested <= 0 {
return []
}
let values : Array[Int] = []
for index = 0; index < population; index = index + 1 {
values.push(index)
}
let shuffled = self.shuffle(values)
let count = requested.min(population)
let result : Array[Int] = []
for index = 0; index < count; index = index + 1 {
result.push(shuffled[index])
}
result
}
///|
pub fn Random::fork(self : Random, stream : Int) -> Random {
let mixed = Int64::from_int(self.next_raw()) +
Int64::from_int(stream) * 104729L +
self.initial_seed * 8191L
Random::new(mixed)
}
///|
pub fn Random::advance(self : Random, count : Int) -> Unit {
for index = 0; index < count.max(0); index = index + 1 {
ignore(self.next_raw())
}
}
///|
pub fn Random::snapshot(self : Random) -> RandomSnapshot {
{ seed: self.initial_seed, state: self.state, draws: self.draws, }
}
///|
pub(all) struct RandomSnapshot {
seed : Int64
state : Int64
draws : Int
} derive(Debug, Eq)
///|
pub fn Random::restore(snapshot : RandomSnapshot) -> Random {
{
state: normalize_seed(snapshot.state),
initial_seed: snapshot.seed,
draws: snapshot.draws.max(0),
}
}
///|
pub fn deterministic_permutation(seed : Int64, size : Int) -> Array[Int] {
let values : Array[Int] = []
for index = 0; index < size.max(0); index = index + 1 {
values.push(index)
}
Random::new(seed).shuffle(values)
}