language-Modula2-0.1: examples/Modula-2_Libraries/PMOS/sources/general/randcard.mod
IMPLEMENTATION MODULE RandCard;
(********************************************************)
(* *)
(* Random number generator *)
(* *)
(* This version is a more portable (but slightly *)
(* slower) version of the assembly language *)
(* module of the same name. *)
(* *)
(* Programmer: P. Moylan *)
(* Last edited: 18 March 1995 *)
(* Status: Working *)
(* *)
(********************************************************)
(************************************************************************)
(* *)
(* The algorithm used is Schrage's method, as described in *)
(* Stephen K. Park and Keith W. Miller, "Random Number Generators: *)
(* Good ones are hard to find", CACM 31(10), Oct 1988, 1192-1201. *)
(* A basic property of this particular implementation is that all *)
(* intermediate results fit into 32 bits (including sign). *)
(* *)
(************************************************************************)
FROM LowLevel IMPORT
(* proc *) HighWord, MakeLongword, Mul;
(************************************************************************)
CONST
a = 16807; (* 7^5 *)
divisor = modulus DIV a; (* = 127773 *)
divisorL = divisor - 65536; (* low order 16 bits of divisor *)
(************************************************************************)
PROCEDURE Divide (number: LONGCARD;
VAR (*OUT*) quotient: CARDINAL;
VAR (*OUT*) remainder: LONGCARD);
(* Special-purpose division procedure - be careful about using *)
(* this in other applications, because it takes advantage of some *)
(* foreknowledge of the numbers which arise in this application. *)
(* Divides number by divisor=127773. *)
VAR step: CARDINAL;
BEGIN
remainder := number;
quotient := 0;
WHILE remainder >= divisor DO
step := ORD(HighWord(remainder)) DIV 2;
IF step = 0 THEN step := 1 END(*IF*);
INC (quotient, step);
(* Here, we take advantage of the fact that *)
(* divisor := 2^16 + divisorL *)
remainder := remainder - MakeLongword(step, 0)
- Mul(divisorL,step);
END (*WHILE*);
END Divide;
(************************************************************************)
PROCEDURE RandCardinal (): LONGCARD;
(* Let a = 7^5 = 16807, q = modulus DIV a = 127773, and *)
(* r = modulus MOD a = 2836. *)
(* We perform the sequence of calculations: *)
(* high := SHORT(seed DIV q); low := seed MOD q; *)
(* test := a*low - r*LONG(high); *)
(* IF test > 0 THEN seed := test *)
(* ELSE seed := test + modulus; *)
(* ENDIF; *)
(* RETURN seed; *)
(* The division in the first step is complicated by the fact that *)
(* our processor does not provide a 32-bit division operation. *)
CONST r = modulus MOD a; (* 2836 *)
VAR high: CARDINAL; low: LONGCARD; test: LONGINT;
BEGIN
Divide (seed, high, low);
test := VAL(LONGINT,a*low) - VAL(LONGINT,Mul(r, high));
IF test > 0 THEN seed := test
ELSE seed := test + modulus;
END (*IF*);
RETURN seed;
END RandCardinal;
(****************************************************************)
(* MODULE INITIALISATION *)
(****************************************************************)
BEGIN
seed := 1;
END RandCard.