random.pony

"""
# Random package

The Random package provides support generating random numbers. The package
provides random number generators you can use in your code, a dice roller and
a trait for implementing your own random number generator.

If your application does not require a specific generator, use Rand.

Seed values can contain up to 128 bits of randomness in the form of two U64s.
A common non-cryptographically secure way to seed a generator is with
`Time.now`.

```pony
let rand = Rand
let n = rand.next()
```
"""
type Rand is XorOshiro128Plus

trait Random
  """
  The `Random` trait should be implemented by all random number generators. The
  only method you need to implement is `fun ref next(): 64`. Once that method
  has been implemented, the `Random` trait provides default implementations of
  conversions to other number types.
  """
  new create(x: U64 = 5489, y: U64 = 0)
    """
    Create with the specified seed. Returned values are deterministic for a
    given seed.
    """

  fun tag has_next(): Bool =>
    """
    If used as an iterator, this always has another value.
    """
    true

  fun ref next(): U64
    """
    A random integer in [0, 2^64)
    """

  fun ref u8(): U8 =>
    """
    A random integer in [0, 2^8)
    """
    (next() >> 56).u8()

  fun ref u16(): U16 =>
    """
    A random integer in [0, 2^16)
    """
    (next() >> 48).u16()

  fun ref u32(): U32 =>
    """
    A random integer in [0, 2^32)
    """
    (next() >> 32).u32()

  fun ref u64(): U64 =>
    """
    A random integer in [0, 2^64)
    """
    next()

  fun ref u128(): U128 =>
    """
    A random integer in [0, 2^128)
    """
    (next().u128() << 64) or next().u128()

  fun ref ulong(): ULong =>
    """
    A random integer in [0, ULong.max_value()]
    """
    ifdef ilp32 or llp64 then
      (next() >> 32).ulong()
    else
      next().ulong()
    end

  fun ref usize(): USize =>
    """
    A random integer in [0, USize.max_value()]
    """
    ifdef ilp32 then
      (next() >> 32).usize()
    else
      next().usize()
    end

  fun ref i8(): I8 =>
    """
    A random integer in [-2^7, 2^7)
    """
    u8().i8()

  fun ref i16(): I16 =>
    """
    A random integer in [-2^15, 2^15)
    """
    u16().i16()

  fun ref i32(): I32 =>
    """
    A random integer in [-2^31, 2^31)
    """
    u32().i32()

  fun ref i64(): I64 =>
    """
    A random integer in [-2^63, 2^63)
    """
    u64().i64()

  fun ref i128(): I128 =>
    """
    A random integer in [-2^127, 2^127)
    """
    u128().i128()

  fun ref ilong(): ILong =>
    """
    A random integer in [ILong.min_value(), ILong.max_value()]
    """
    ulong().ilong()

  fun ref isize(): ISize =>
    """
    A random integer in [ISize.min_value(), ISize.max_value()]
    """
    usize().isize()

  fun ref int[N: (Unsigned val & Real[N] val) = U64](n: N): N =>
    """
    A random integer in [0, n)
    """
    N.from[F64](real() * n.f64())

  fun ref real(): F64 =>
    """
    A random number in [0, 1)
    """
    (next() >> 11).f64() * (F64(1) / 9007199254740992)

  fun ref shuffle[A](array: Array[A]) =>
    """
    Shuffle the elements of the array into a random order, mutating the array.
    """
    var i: USize = array.size()
    try
      while i > 1 do
        let ceil = i = i - 1
        array.swap_elements(i, int[USize](ceil))?
      end
    end