--
--	Random number generation
--	Copyright (C) 1985 David Morse
--	Copyright (C) 2009 Adam Sampson <ats@offog.org>
--
--	This library is free software; you can redistribute it and/or
--	modify it under the terms of the GNU Lesser General Public
--	License as published by the Free Software Foundation, either
--	version 2 of the License, or (at your option) any later version.
--
--	This library is distributed in the hope that it will be useful,
--	but WITHOUT ANY WARRANTY; without even the implied warranty of
--	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
--	Lesser General Public License for more details.
--
--	You should have received a copy of the GNU Lesser General Public
--	License along with this library.  If not, see
--	<http://www.gnu.org/licenses/>.
--

--** @module random

#INCLUDE "maths.module"

#INCLUDE "random.inc"

--{{{  primitive operations
--{{{  FUNCTION random.init
--* Initialise RNG state given (up to) 32 bits of initial entropy.
RANDOM.STATE FUNCTION random.init (VAL INT32 seed) IS
  [(seed >> 2) + 1] (RANDOM.STATE):
--}}}
--{{{  FUNCTION random.get
--* Generate 32 bits of random data.
-- Returns the data and a new RNG state.
INT32, RANDOM.STATE FUNCTION random.get (VAL RANDOM.STATE state)
  RANDOM.STATE new.state:

  VALOF
    --{{{  miscellaneous constants
    VAL INT32 magic IS 16807:
    VAL INT32 period IS 2147483647:
    VAL INT32 quotient IS period / magic:
    VAL INT32 remainder IS period \ magic:
    --}}}

    INT32 lo, hi, test:
    SEQ
      hi := state[seed] / quotient
      lo := state[seed] \ quotient
      test := (magic TIMES lo) MINUS (remainder TIMES hi)
      IF
        test > 0
          new.state[seed] := test
        TRUE
          new.state[seed] := test PLUS period
    RESULT new.state[seed], new.state
:
--}}}
--}}}
--{{{  state operations
--{{{  PROC random.init.from.time
--* Initialise an RNG using the current time as initial entropy.
PROC random.init.from.time (RESULT RANDOM.STATE state)
  TIMER tim:
  INT t:
  SEQ
    tim ? t
    state := random.init (INT32 t)
:
--}}}
--{{{  FUNCTION random.split
--* Given an RNG state, compute two new states deterministically from
-- it that have a low probability of generating overlapping sequences.
RANDOM.STATE, RANDOM.STATE FUNCTION random.split (VAL RANDOM.STATE state)
  RANDOM.STATE new.a, new.b:
  VALOF
    INT32 data:
    SEQ
      data, new.a := random.get (state)
      new.b := random.init (data)
    RESULT new.a, new.b
:
--}}}
--}}}
--{{{  generators
--{{{  FUNCTION random.int
--* Generate a random [@code INT].
--
-- @param max Upper limit
-- @param state Current RNG state
-- @return Output value, uniformly distributed in the range [0, [@code max]).
-- @return New RNG state
INT, RANDOM.STATE FUNCTION random.int (VAL INT max, VAL RANDOM.STATE state)
  INT value:
  RANDOM.STATE new.state:
  VALOF
    INT32 data:
    SEQ
      data, new.state := random.get (state)
      value := INT (data \ (INT32 max))
    RESULT value, new.state
:
--}}}
--{{{  FUNCTION random.real32
--* Generate a random [@code REAL32].
--
-- @param max Upper limit
-- @param state Current RNG state
-- @return Output value, uniformly distributed in the range [0, [@code max]).
-- @return New RNG state
REAL32, RANDOM.STATE FUNCTION random.real32 (VAL REAL32 max, VAL RANDOM.STATE state)
  REAL32 value:
  RANDOM.STATE new.state:
  VALOF
    INT32 data:
    SEQ
      data, new.state := random.get (state)
      value := (max * (REAL32 ROUND data)) / (REAL32 ROUND (MOSTPOS INT32))
    RESULT value, new.state
:
--}}}
--{{{  FUNCTION random.gaussian.real32s
--* Generate two random [@code REAL32]s from a Gaussian distribution.
--
-- This function exists because the Box-Muller transform used to generate
-- Gaussian-distributed numbers in this module produces two results at a time.
--
-- @param state Current RNG state
-- @returns value1, value2 Two output values from a Gaussian distribution with
--   mean 0.0 and standard deviation 1.0
-- @returns new.state New RNG state
REAL32, REAL32, RANDOM.STATE FUNCTION random.gaussian.real32s (VAL RANDOM.STATE state)
  REAL32 value1, value2:
  RANDOM.STATE new.state:
  VALOF
    REAL32 w, x1, x2:
    SEQ
      new.state := state
      --{{{  Box-Muller transform
      --
      -- This is based on Everett Carter's description:
      --   http://www.taygeta.com/random/gaussian.html
      -- but I've added the "w = 0.0" test to the loop to prevent a possible
      -- division by zero.
      --
      -- It'd probably be better to use the Ziggurat algorithm.

      w := 2.0
      WHILE (w >= 1.0) OR (w = 0.0)
        SEQ
          x1, new.state := random.real32 (2.0, new.state)
          x1 := x1 - 1.0
          x2, new.state := random.real32 (2.0, new.state)
          x2 := x2 - 1.0
          w := (x1 * x1) + (x2 * x2)
      w := SQRT (((-2.0) * ALOG (w)) / w)
      value1 := x1 * w
      value2 := x2 * w
      --}}}
    RESULT value1, value2, new.state
:
--}}}
--{{{  FUNCTION random.gaussian.real32
--* Generate a random [@code REAL32] from a Gaussian distribution.
--
-- @param state Current RNG state
-- @returns value Output value from a Gaussian distribution with mean 0.0 and
--   standard deviation 1.0
-- @returns new.state New RNG state
REAL32, RANDOM.STATE FUNCTION random.gaussian.real32 (VAL RANDOM.STATE state)
  REAL32 value:
  RANDOM.STATE new.state:
  VALOF
    --{{{  compute two and throw one away
    REAL32 dummy:
    value, dummy, new.state := random.gaussian.real32s (state)
    --}}}
    RESULT value, new.state
:
--}}}
--}}}
