///|
/// Tiny deterministic RNG used for sampling.
pub struct ReplayRng {
  mut state : Int
}

///|
pub fn ReplayRng::new(seed : Int) -> ReplayRng {
  { state: if seed == 0 { 1 } else { seed } }
}

///|
pub fn ReplayRng::next_state(self : ReplayRng) -> Int {
  // Small LCG, good enough for sampling order in tests and examples.
  self.state = self.state * 1103515245 + 12345
  self.state
}

///|
pub fn ReplayRng::next_index(self : ReplayRng, upper : Int) -> Int {
  if upper <= 0 {
    0
  } else {
    let raw = self.next_state()
    let rem = raw % upper
    if rem < 0 {
      rem + upper
    } else {
      rem
    }
  }
}

///|
pub fn ReplayRng::next_unit(self : ReplayRng) -> Double {
  let raw = self.next_state()
  let normalized = if raw < 0 { -raw } else { raw }
  (normalized % 1_000_000).to_double() / 1_000_000.0
}

///|
fn make_sequential_indices(size : Int) -> Array[Int] {
  let indices : Array[Int] = []
  let mut i = 0
  while i < size {
    indices.push(i)
    i = i + 1
  }
  indices
}

///|
fn shuffle_prefix(
  indices : Array[Int],
  prefix_len : Int,
  seed : Int,
) -> Array[Int] {
  let rng = ReplayRng::new(seed)
  let mut i = 0
  while i < prefix_len {
    let remaining = indices.length() - i
    let j = i + rng.next_index(remaining)
    let tmp = indices[i]
    indices[i] = indices[j]
    indices[j] = tmp
    i = i + 1
  }
  indices
}

///|
fn normalize_positive(value : Double) -> Double {
  if value < 0.0 {
    -value
  } else {
    value
  }
}