///|
const A : UInt64 = 1839567234

///|
const M : UInt64 = 8239451023

///|
const C : UInt64 = 972348567

///|
pub typealias State[UInt64, A] as RandomRNG[A]

///|
pub fn randomRng() -> RandomRNG[UInt64] {
  State(fn(seed) {
    let nextSeed = (A * seed + C) % M
    (nextSeed, nextSeed)
  })
}

///|
pub fn[A] random_with(gen : (UInt64) -> A) -> RandomRNG[A] {
  State(fn(seed) {
    let nextSeed = (A * seed + C) % M
    (gen(nextSeed), nextSeed)
  })
}

///|
pub fn random_double(min~ : Double = 0, max~ : Double = 1) -> RandomRNG[Double] {
  random_with(fn(s) {
    min + (s << 11 >> 11).to_double() / M.to_double() * (max - min)
  })
}

///|
pub fn random_int(min : Int, max : Int) -> RandomRNG[Int] {
  random_with(fn(s) {
    min +
    (s.to_double() / M.to_double() * (max - min).to_double()).floor().to_int()
  })
}
// extern "js" fn now() -> UInt =
//   #| Date.now()

///|
test "random" {
  inspect((1).to_byte(), content="b'\\x01'")
  inspect(
    "12".to_bytes(),
    content=
      #|b"\x31\x00\x32\x00"
    ,
  )
  let rnd = @random.Rand::new()
  inspect(rnd.int(limit=10), content="7")
  inspect(rnd.int(limit=10), content="0")
  inspect(rnd.int(limit=10), content="5")
  inspect(rnd.int(limit=10), content="4")
  inspect(random_int(0, 10).evaluate(123), content="5")
  inspect(random_int(0, 10).evaluate(1234), content="6")
  inspect(random_int(0, 10).evaluate(12345), content="3")
  inspect(random_int(0, 10).evaluate(123456), content="3")
  inspect(random_int(0, 10).evaluate(1234567), content="6")
  inspect(random_int(0, 10).evaluate(12345678), content="2")
  inspect(random_int(0, 10).evaluate(123456789), content="3")
}