MODULE RandomGen; (***************************************************************************) (* Copyright (C) Olivetti 1989 *) (* All Rights reserved *) (* *) (* Use and copy of this software and preparation of derivative works based *) (* upon this software are permitted to any person, provided this same *) (* copyright notice and the following Olivetti warranty disclaimer are *) (* included in any copy of the software or any modification thereof or *) (* derivative work therefrom made by any person. *) (* *) (* This software is made available AS IS and Olivetti disclaims all *) (* warranties with respect to this software, whether expressed or implied *) (* under any law, including all implied warranties of merchantibility and *) (* fitness for any purpose. In no event shall Olivetti be liable for any *) (* damages whatsoever resulting from loss of use, data or profits or *) (* otherwise arising out of or in connection with the use or performance *) (* of this software. *) (***************************************************************************) IMPORT Thread, Word; TYPE LockableGenerator = Generator OBJECT mutex: Thread.Mutex END; (* useful for generators with private state - provides a mutex to protect it *) (* The first generator uses the same algorithm as the BBC micro. This produces a pretty good psuedo random sequence. As coded here it is only suitable for machines where an INTEGER is 32 bits *) TYPE BBCGenerator = LockableGenerator OBJECT word: INTEGER := -1; bit33: INTEGER := 0; OVERRIDES int := BBCInt; seed := BBCSeed; END; VAR bbc_g := NEW(BBCGenerator, mutex := Thread.NewMutex()); PROCEDURE BBCSeed(g: BBCGenerator; seed: INTEGER) RAISES {}= BEGIN LOCK g.mutex DO IF seed = 0 THEN (* leave 'g.word' unchanged *) g.bit33 := 1; ELSE g.word := seed; g.bit33 := 0; END; (* if *) END; (* lock *) END BBCSeed; PROCEDURE BBCInt(g: BBCGenerator): INTEGER RAISES {}= VAR seed, r: INTEGER; BEGIN LOCK g.mutex DO seed := g.word; r := Word.Shift(seed, -1); IF g.bit33 # 0 THEN r := Word.Insert(r, 1, Word.Size - 1, 1) END; g.bit33 := Word.And(seed, 1); r := Word.Xor(r, Word.Shift(seed, 12)); seed := Word.Xor(r, Word.Shift(r, -20)); g.word := seed; RETURN seed; END; (* lock *) END BBCInt; <*INLINE*> PROCEDURE BBC(): Generator RAISES {}= BEGIN RETURN bbc_g; END BBC; (* The second generator uses the ANSII C algorithm. This produces an average psuedo random sequence, but it is standard. *) TYPE ANSI_CGenerator = LockableGenerator OBJECT word: INTEGER := -1; OVERRIDES int := ANSI_CInt; seed := ANSI_CSeed; END; VAR ansi_c_g := NEW(ANSI_CGenerator, mutex := Thread.NewMutex()); PROCEDURE ANSI_CSeed(g: ANSI_CGenerator; seed: INTEGER) RAISES {}= BEGIN LOCK g.mutex DO g.word := seed END; END ANSI_CSeed; PROCEDURE ANSI_CInt(g: ANSI_CGenerator): INTEGER RAISES {}= BEGIN LOCK g.mutex DO g.word := Word.Plus(Word.Times(g.word, 1103515245), 12345); RETURN g.word; END; (* lock *) END ANSI_CInt; <*INLINE*> PROCEDURE ANSI_C(): Generator RAISES {}= BEGIN RETURN ansi_c_g; END ANSI_C; (* default *) VAR mutex_g := Thread.NewMutex(); default_g := bbc_g; PROCEDURE DefaultGenerator(): Generator RAISES {}= BEGIN LOCK mutex_g DO RETURN default_g END; END DefaultGenerator; PROCEDURE SetDefaultGenerator(g: Generator): Generator RAISES {}= BEGIN LOCK mutex_g DO VAR old := default_g; BEGIN default_g := g; RETURN old; END; END; END SetDefaultGenerator; (* Now the operations on the output of a random number generator. There are not many of these at the moment; more are needed, especially some way of producing REAL and LONGREAL randoms *) <*INLINE*> PROCEDURE Uniform( g: Generator; lwb, upb: INTEGER) : INTEGER RAISES {}= BEGIN IF lwb > upb THEN VAR temp := upb; BEGIN upb := lwb; lwb := temp; END; END; (* The following only works if 'Word' works in two's complement *) RETURN Word.Plus(lwb, Word.Mod(g.int(), Word.Plus(Word.Minus(upb, lwb), 1))); END Uniform; <*INLINE*> PROCEDURE ZeroTo(g: Generator; limit: CARDINAL): CARDINAL RAISES {}= BEGIN RETURN Word.Mod(g.int(), Word.Plus(limit, 1)); END ZeroTo; <*INLINE*> PROCEDURE OneTo( g: Generator; limit: NonZeroCARDINAL) : NonZeroCARDINAL RAISES {}= BEGIN RETURN Word.Mod(g.int(), limit) + 1; END OneTo; BEGIN END RandomGen.