/*  RANDOM.PL  */


/*
This module exports a random-number generator needed by "spin".
*/

/*
:- module random.


:- public random/2,
          randomise/0,
          randomise/1,
          random_element/3,
          random_element/2,
          random_permutation/2.
 */

/*
SPECIFICATION
-------------


PUBLIC random( N+, I? ):

Given an integer N >= 1, random(N, I) unifies I with a random integer
between 0 and N - 1.


PUBLIC randomise:
Restart the random sequence from the beginning


PUBLIC randomise( Seed+ ):

Instantiate the seeds to your own favourite value


PUBLIC random_element( List+, Elem?, Rest? ):

Given an non-empty List, unifies Elem with a random element of List and
Rest with the other elements.


PUBLIC random_element( List+, Elem? ):

Equivalent to random_element( List, Elem, _ ).


PUBLIC random_permutation( List+, Perm? ):

Unifies Perm with a random permutation of List. What good this may be
I'm not sure, and there's bound to be a more efficient way of doing it.
Oh well.
*/


/*
IMPLEMENTATION
--------------

I have adapted this file from RANDOM.PL in the DEC-10 public-domain
library. The original heading was
Author : R.A.O'Keefe
Updated: 1 October 1984
NIP version: 13 May 1987
Purpose: Random number generator.

The code should be portable (I don't know if there are any dependencies
on word size), except that you will need to change 'intof' as a right
operand of 'is' in 'random' to whatever your system uses for converting
reals to integers with truncation towards zero.
*/

/*
:- dynamic '$random_seed'/1.


:- needs
   bug / 2,
   length / 2,
   nth1 / 4.
 */

random( N, I ) :-
    (
        '$random_seed'( [A0,A1,A2] )
    ->
        retractall( '$random_seed'(_) )
    ;
        A0 = 3172, A1 = 9814, A2 = 20125
    ),
    B0 is (A0*171) mod 30269,
    B1 is (A1*172) mod 30307,
    B2 is (A2*170) mod 30323,
    asserta( '$random_seed'([B0,B1,B2]) ),
    R is A0/30269 + A1/30307 + A2/30323,
    I is intof((R - intof(R)) * N).


/*
The next bit: K R Johnson, 13-5-87
*/
randomise :-
    (
        '$random_seed'( _ )
    ->
        retractall( '$random_seed'(_) )
    ;
        true
    ),
    asserta( '$random_seed'([3172,9814,20125]) ).


randomise(Seed) :-
    integer(Seed),
    Seed > 0,
    !,
    (
        '$random_seed'(_)
    ->
        retractall( '$random_seed'(_) )
    ;
        true
    ),
    S0 is Seed mod 30269,
    S1 is Seed mod 30307,
    S2 is Seed mod 30323,
    asserta( '$random_seed'([S0,S1,S2]) ).

randomise( Seed ) :-
    bug( 'randomise: Seed must be a positive integer', [Seed] ).


random_element( [E], E, [] ) :- !.

random_element( List, Elem, Rest ) :-
    length( List, N ),
    N > 0,
    random( N, I ),
    I_ is I + 1,
    nth1( I_, List, Elem, Rest ).


random_element( List, Elem ) :-
    random_element( List, Elem, _ ).


random_permutation([], []).
random_permutation([H1|T1], [H2|T2]) :-
    random_element([H1|T1], H2, T3),
    random_permutation(T3, T2).

/*
:- endmodule.
 */
