!=========================================================================
! Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved.
!
! $Id: random.gs,v 1.9 2008-01-09 22:50:13 stever Exp $
!
! Superclass Hierarchy:
!   Stream, Object.
!
!=========================================================================

expectvalue %String
run
| cls |
cls := UserGlobals at:#Random ifAbsent:[
  Stream subclass: 'Random'
  instVarNames: #( 'seed' 'hi' 'lo')
  classVars: #( 'a' 'm' 'r' 'q')
  classInstVars: #()
  poolDictionaries: #[]
  inDictionary: UserGlobals
  constraints: #[  ]
  instancesInvariant: false
  isModifiable: false ].
^ cls definition
%
removeallmethods Random
removeallclassmethods Random

category: 'For Documentation Installation only'
classmethod: Random
installDocumentation

| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'Class Random is a port of Jeff Sutherland''s implementation of the
 Park & Miller random number generator.

From Jeff Sutherland''s original posting:

"In summary...(this is) a generator which has a full period, is 
demonstrably random, and can be implemented correctly on almost
any system.  The generator has been exhaustively tested and its
characteristics are well understood... Moreover, it has become a
standard ... subroutine DNUN in the IMSL library and ... DRAND
in the simulation language SLAM II...  we feel confident in 
recommending this random number generator as a minimal standard
against which all others should be judged."

This code has received minimal testing on an IBM P75 486 machine
and may break on a Pentium.  It is written in Enfin Smalltalk but
a port to another Smalltalk is trivial.  The validate function works
but any bugs you find are surely mine.  Send me a note at 
jsutherland@vmark.com.' .
doc documentClassWith: txt.

self description: doc.
%

category: 'accessing'
method: Random
nextPut: anObject
	"Random numbers do not implement nextPut: so provide an
	error notification."

	^self shouldNotImplement: #nextPut:
%
method: Random
contents
	"Random numbers do not have a contents so provide
	an error notification."

	^self shouldNotImplement: #contents
%
method: Random
flush
	"Random numbers do not need to flush."

	^self shouldNotImplement: #flush
%
category: 'testing'
method: Random
atEnd
	"Answer false that the stream is not at an end."

	^false
%

category: 'Accessing'
method: Random
seed

   "Return the value of the instance variable 'seed'."
   ^seed
%
category: 'Accessing'
method: Random
next
	"Answer the next random number."

"Real Random Number generator Version 2 from Park and Miller, 1988"
  hi := seed quo: q.
  lo := seed - (q * hi).
  seed := (a * lo) - (r * hi).
  seed > 0 ifFalse: [
    seed := seed + m
  ].
  ^ seed / m.
%
category: 'accessing'
method: Random
between: min and: max

   "Return a random value x such that min <= x < max"

  ^(self next * (max - min)) + min
%

category: 'Initialization'
method: Random
seed: aSeed
  (aSeed <= 0 or:[aSeed >= 524288000]) ifTrue:[ 
     self error:'seed out of range'
  ].
  seed := aSeed.
  hi := lo := nil.
%

category: 'Private'
method: Random
initialize

  "Initialize an instance, seeded by the time of day"

  self seed: (System _timeMs \\ 524288000)
%

category: 'Instance Creation'
classmethod: Random
new
	^self basicNew initialize.
%

category: 'Testing'
classmethod: Random
testRandom
       "Random testRandom will check your hardware environment"
|num|
num := Random new.
num seed: 1.
10000 timesRepeat: [
  num next.
].
(1043618065 == num seed) 
  ifTrue: [ ^'OK.' ]
  ifFalse: [ ^'Bad result. Fix this generator 
             by referring to Park & Miller,^M
             Communications of the ACM 31:10:1192-1201, 1988.' ].
%
category: 'Testing'
classmethod: Random
speed
       "Random speed will benchmark the efficiency of random number generation"

|rand|

rand := self new.

^(System millisecondsToRun: [ 1000 timesRepeat: [rand next]
  ]) / 1000.0
%

!=========================================================================
category: '(as yet unclassified)'
classmethod: Random
initialize

  "Class initialization"

  a := 16807.0 .
  m := 2147483647.0 .
  q := m // a.
  r := m \\ a.
%

expectvalue Random
run
  Random initialize.
%

!=========================================================================
! Install the GsClassDocumentation

run
Random installDocumentation.
(Random class) removeCategory: 'For Documentation Installation only'.
^ true
%
