-------------------------------------------------------------------------------
-- The following random number generator is an implementation of the
-- Minimum Standard generator recommended in
--
--    Random Number Generators: Good ones are hard to find
--       Stephen K Park & Keith W Miller
--       Communications of the ACM, Oct 88, Vol 31 No 10 1192 - 1201
--
-- Seeds must be in the range 1..2147483646, that is (1..(2**31)-2)
-- Output will also be in that range. The generator is full period so that
-- all 2147483646 values will be generated before the initial seed repeats.
-- Dividing by 2147483647 (real) as in the Pascal code below will map it
-- into the range (0..1) if required.
--
-- [This program assumes that you are working on a machine with (at least)
-- 32 bit integers.  Folks using Hugs on a PC will have to stick with the
-- less sophisticated random number generator in the file `randoms'.]
-------------------------------------------------------------------------------

module Minsrand where

min_stand_test  :: Int -> Int
min_stand_test n = if test > 0 then test else test + 2147483647
		   where test = 16807 * lo - 2836 * hi
		         hi   = n `div` 127773
		         lo   = n `rem` 127773

min_stand_randoms :: Int -> [Int]
min_stand_randoms  = iterate min_stand_test

-- The article produced below also gives a test to check that the
-- random number generator is working.  We can duplicate this test
-- as follows:
--
--   ? strictIterate min_stand_test 1 !! 10000
--   1043618065
--   (149758 reductions, 240096 cells, 2 garbage collections)
--
-- Happily, this is the result that we expect to obtain.
--
-- The function strictIterate is defined below.  It is similar to the
-- standard iterate function except that it forces the evaluation of
-- each element in the list produced (except possibly the first).
-- Had we instead tried to evaluate:
--
--   iterate min_stand_test 1 !! 10000
--
-- Hugs would have first constructed the expression graph:
--
--   min_stand_test (min_stand_test (... (min_stand_test 1) ...))
--
-- in which the min_stand_test function is applied 10000 times to 1
-- and then attempted to evaluate this.  In either case, you'd need a
-- large heap to represent the complete expression and a large stack so
-- that you could handle 10000 levels of function calling.  Most standard
-- configurations of Hugs aren't set up with sufficiently large defaults
-- to make this possible, so the most likely outcome would be a runtime
-- error of one kind or another!

strictIterate    :: (a -> a) -> a -> [a]
strictIterate f x = x : (strictIterate f $! f x)

-------------------------------------------------------------------------------
-- Some comments and code from:
--
-- Random Number Generators: Good ones are hard to find
--    Stephen K Park & Keith W Miller
--    Communications of the ACM, Oct 88, Vol 31 No 10 1192 - 1201
-- 
-- Minimum standard random number generator implementations
-- 
-- This version of Random will be correct if reals are represented
-- with a 46-bit or larger mantissa (excluding the sign bit).
-- For example, this version will be correct on all systems that support
-- the IEEE 64-bit real arithmetic standard since the mantissa in that case
-- is 53-bits.
-- ... from page 1195 upper right quadrant
-- 
-- var seed : real;
-- ...
-- function Random : real;
-- 	(* Real Version 1 *)
-- const
--    a = 16807.0;
--    m = 2147483647.0;
-- var
--    temp : real;
-- begin
--    temp := a * seed;
--    seed :=
--       temp - m * Trunc(temp / m);
--    Random := seed / m;
-- end;
-- 
-- ... from page 1195 lower right quadrant, variant by L. Schrage, 1979, 1983
--
-- var seed : integer;
-- ...
-- function Random : real;
-- 	(* Integer Version 2 *)
-- const
--    a = 16807;
--    m = 2147483647;
--    q = 127773;	(* m div a *)
--    r = 2836;	(* m mod a *)
-- var
--    lo, hi, test : integer;
-- begin
--    hi := seed div q;
--    lo := seed mod q;
--    test := a * lo - r * hi;
--    if test > 0 then
--       seed := test
--    else
--       seed := test + m;
-- 
--    Random := seed / m;
-- end;
-- 
-- From page 1195 lower left quadrant
--
-- seed := 1;
-- for n := 1 to 10000 do
--    u := Random;
-- Writeln('The current value of seed is : ', seed);
-- (* Expect 1043618065 *)
-------------------------------------------------------------------------------
