///|
pub(all) struct RNG {
  mut state : UInt
}

///|
pub fn make_rng(seed : UInt) -> RNG {
  { state: if seed == 0U { 1U } else { seed } }
}

///|
pub fn RNG::next(self : RNG) -> UInt {
  // xorshift32
  let mut s = self.state
  s = s ^ (s << 13)
  s = s ^ (s >> 17)
  s = s ^ (s << 5)
  self.state = s
  s
}

///|
pub fn RNG::next_int(self : RNG, max : Int) -> Int {
  if max <= 0 {
    return 0
  }
  (self.next().reinterpret_as_int() & 0x7FFFFFFF) % max
}

///|
pub fn RNG::rand_range(self : RNG, min : Int, max : Int) -> Int {
  if min >= max {
    return min
  }
  min + self.next_int(max - min)
}

///|
pub fn RNG::rand_bool(self : RNG, probability : Double) -> Bool {
  let v = (self.next().reinterpret_as_int() & 0x7FFFFFFF).to_double() /
    2147483647.0
  v < probability
}

///|
pub fn[T] RNG::rand_pick(self : RNG, arr : Array[T]) -> T {
  arr[self.next_int(arr.length())]
}

///|
pub fn[T] RNG::rand_shuffle(self : RNG, arr : Array[T]) -> Unit {
  for i = arr.length() - 1; i > 0; {
    let j = self.next_int(i + 1)
    arr.swap(i, j)
    continue i - 1
  }
}

///|
pub fn RNG::rand_direction(self : RNG) -> Point {
  let dirs : Array[Point] = [
    { x: -1, y: 0 },
    { x: 1, y: 0 },
    { x: 0, y: -1 },
    { x: 0, y: 1 },
  ]
  self.rand_pick(dirs)
}