Random.st
changeset 4600 ee760be38331
parent 4599 169bd0f282fb
child 4611 6357d7d30179
--- a/Random.st	Fri Mar 02 16:15:00 2018 +0100
+++ b/Random.st	Fri Mar 02 17:05:37 2018 +0100
@@ -1,37 +1,23 @@
 "{ Encoding: utf8 }"
 
 "
-======================================================================
-|
-| Copyright (C) 1988, 1989 Free Software Foundation, Inc.
-| Written by Steve Byrne.
-|
-| This file is part of GNU Smalltalk.
-|
-| GNU Smalltalk is free software; you can redistribute it and/or modify it
-| under the terms of the GNU General Public License as published by the Free
-| Software Foundation; either version 1, or (at your option) any later version.
-| 
-| GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
-| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-| FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
-| details.
-| 
-| You should have received a copy of the GNU General Public License along with
-| GNU Smalltalk; see the file LICENSE.  If not, write to the Free Software
-| Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
-|
-======================================================================
+ COPYRIGHT (c) 1989 by Claus Gittinger
+              All Rights Reserved
 
-see notice in (Random>>documentation)
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
 "
 "{ Package: 'stx:libbasic2' }"
 
 "{ NameSpace: Smalltalk }"
 
 Stream subclass:#Random
-	instanceVariableNames:'seed increment multiplier modulus'
-	classVariableNames:'SharedGenerator RandomSalt'
+	instanceVariableNames:'seed'
+	classVariableNames:'RandomSalt SharedGenerator'
 	poolDictionaries:''
 	category:'Magnitude-Numbers-Random'
 !
@@ -40,62 +26,31 @@
 
 copyright
 "
-======================================================================
-|
-| Copyright (C) 1988, 1989 Free Software Foundation, Inc.
-| Written by Steve Byrne.
-|
-| This file is part of GNU Smalltalk.
-|
-| GNU Smalltalk is free software; you can redistribute it and/or modify it
-| under the terms of the GNU General Public License as published by the Free
-| Software Foundation; either version 1, or (at your option) any later version.
-| 
-| GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
-| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-| FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
-| details.
-| 
-| You should have received a copy of the GNU General Public License along with
-| GNU Smalltalk; see the file LICENSE.  If not, write to the Free Software
-| Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
-|
-======================================================================
+ COPYRIGHT (c) 1989 by Claus Gittinger
+              All Rights Reserved
 
-see notice in (Random>>documentation)
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
 "
 !
 
 documentation
 "
-    WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
-    =======================================================================================
-    DO NOT USE THIS GENERATOR FOR CRYPTOGRAPHY OR OTHER SECURITY RELATED WORK, 
-    because linear congruential generators are predictable and can be broken easily!!
-
-    Smalltalk/X includes a better generator named RandomGenerator, which uses the best
-    available generator of the system (either OS-random, /dev/random or as a less dangerous
-    fallback, an RC4-based random generator).
-    Please use that one.
-    =======================================================================================
-
-    A simple random numbers - thanks to Steves GNU Smalltalk
+    A facade to the actual RandomGenerator.
+    In previous versions, Random was a very bad fallback random generator,
+    with an included warning, to not use it.
 
-    This implements a linear congruential maximum period random number generator
-    which passes the spectral test for randomness for dimensions 2 3 4 5 6.
+    Now this old generator was renamed to RandomGNUSmalltalk,
+    and the default generator used is the one provided by the much
+    better RandomGenerator class.
 
-    Notice: although being included here,
-            this file is NOT covered by the ST/X license, but by
-            the FSF copyLeft (see copyright method).
-
-            You can redistribute it under the terms stated there ...
-            Also, the price you pay for ST/X does not include a charge for
-            this file - it has to be considered as a separate piece of
-            software, which can be copied and given away without any 
-            restriction from my (CG) side.
+        Random new next:10   
 
     [author:]
-        Steve Byrne
         Claus Gittinger
 
     [see also:]
@@ -131,24 +86,30 @@
 !Random class methodsFor:'instance creation'!
 
 new
-    "return a new random generator"
+    "return a new random generator.
+     Here, this instance creation message is forwarded to the standardGenerator"
 
-    ^ self basicNew initialize
+    ^ self standard
+
+    "
+     Random new
+    "
 !
 
 random
     "return a new random generator.
      Defined here for compatibility with StreamCipher"
 
-    ^ self new
+    ^ self standard
 
     "Created: / 12.11.1999 / 17:52:08 / stefan"
 !
 
 seed:seedValue
-    "return a new random generator with initial seed"
+    "return a new random generator with initial seed.
+     Here, this instance creation message is forwarded to the standardGenerator"
 
-    ^self basicNew setSeed:seedValue
+    ^ self standardGeneratorClass basicNew setSeed:seedValue
 
     "Created: / 26-05-2007 / 21:27:18 / cg"
 !
@@ -157,7 +118,7 @@
     "return a shared random generator."
 
     SharedGenerator isNil ifTrue:[
-        SharedGenerator := self new.
+        SharedGenerator := self standard.
     ].
     ^ SharedGenerator
 !
@@ -165,7 +126,13 @@
 standard
     "return the 'standard' generator"
 
-    ^ self new
+    ^ self standardGeneratorClass new
+!
+
+standardGeneratorClass
+    "return the class used for the 'standard' generator"
+
+    ^ RandomGenerator
 ! !
 
 !Random class methodsFor:'random numbers'!
@@ -445,8 +412,7 @@
 next
     "return the next random number in the range ]0..1["
 
-    self step.
-    ^ seed / modulus asFloat
+    self subclassResponsibility.
 
     "
      |r|
@@ -486,8 +452,7 @@
 nextBoolean
     "return true or false by random"
 
-    self step.
-    ^ seed < (modulus // 2)
+    ^ self next bitAnd:16r10.
 
     "
     |r|
@@ -514,8 +479,7 @@
 nextByte
     "return the next integral random number byte in the range 0 .. 16rFF"
 
-    self step.
-    ^ seed bitAnd:16rFF
+    ^ self next bitAnd:16rFF
 
     "
      |r|
@@ -532,20 +496,10 @@
 nextBytes:count
     "return count random bytes (0..16rFF each)"
 
-    |res cnt "{Class: SmallInteger}"|
-
-    cnt := count.
-    res := ByteArray uninitializedNew:cnt.
-
-    1 to:cnt do:[:i|
-        self step.
-        res at:i put:(seed bitAnd:16rFF).
-    ].
-
-    ^ res
+    ^ (1 to:count) collect:[:i | self nextByte] as:ByteArray
 
     "
-     Transcript showCR:(Random new nextBytes:20).
+     Transcript showCR:(RandomGenerator new nextBytes:20).  
     "
 
     "Modified: 1.4.1997 / 22:42:53 / cg"
@@ -582,32 +536,18 @@
     "get the next cnt printable characters.
      We answer printable characters in the ascii range (codepoints 32 - 126)"
 
-    |res cnt "{ Class:SmallInteger }"|
-
-    cnt := count.
-    res := String uninitializedNew:cnt.
-
-    1 to:cnt do:[:i|
-        self step.
-        res at:i put:(Character value:(seed \\ 94 + 32)).
-    ].
-
-    ^ res
+    ^ (1 to:count) collect:[:i | Character value:(self next \\ 94 + 32) ] as:String
 
     "
-      Random new nextCharacters:8
+      RandomGenerator new nextCharacters:8 
     "
 !
 
 nextInteger
     "return the next integral random number,
-     in the range 0 .. modulus (which is less than 16r3FFFFFFF).
-     From Sedgewick's 'Algorithms', based on Lehmer's method.
+     in the range 0 .. self maxInteger"
 
-     Take care, this returns an even number after each odd number!!"
-
-    self step.
-    ^ seed
+    self subclassResponsibility
 
     "
      |r|
@@ -622,22 +562,53 @@
 !
 
 nextIntegerBetween:start and:stop
-    "return an integral random number between start and stop"
+    "return an integral random number in [start..stop] (inclusive)"
 
     |rnd range bytesNeeded|
 
     range := stop - start + 1.
-
-    range < modulus ifTrue:[
-        "we need the float computation in order to not return alternate even and odd numbers"
-        rnd := (self next * range) truncated.    
-        ^ rnd + start.
+    "Fetch at least 2 bytes, otherwise we get some unbalanced distributions for small ranges"
+    bytesNeeded := (range highBit + 15) // 8.
+    bytesNeeded < 4 ifTrue:[
+        rnd := (((self nextByte bitShift:8) + self nextByte) bitShift:8) + self nextByte.
+        rnd := rnd \\ range.
     ] ifFalse:[
-        bytesNeeded := (range highBit + 7) // 8.
-        rnd := LargeInteger digitBytes:(self nextBytes:bytesNeeded).      
-        rnd := rnd bitXor:(seed < (modulus//2) ifTrue:[0] ifFalse:[1]). "do not alternately return even and odd numbers"
-        ^ start + (rnd \\ range).
+        rnd := (LargeInteger digitBytes:(self nextBytes:bytesNeeded)) compressed.
+        rnd := rnd \\ range.
     ].
+    ^ rnd + start
+
+    "shows pair-combination counts
+
+     |r v prev this counts|
+
+     v := TextView new .
+     v extent:500@250.
+     v list:#('xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+              'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+              'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+              'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+              'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+              'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+              'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+              'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+              'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+              'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx').
+     v openAndWait.
+     counts := (1 to:10) collect:[:row | Array new:10 withAll:0].
+
+     r := self new.
+     prev := r nextIntegerBetween:1 and:10.
+     10000 timesRepeat:[
+         100 timesRepeat:[
+            this := r nextIntegerBetween:1 and:10.
+            (counts at:this) at:prev put:((counts at:this) at:prev)+1.
+         ].
+         v list:(counts collect:[:row |
+                    '%4d %4d %4d %4d %4d %4d %4d %4d %4d %4d'printf:row ]).
+         prev := this.
+     ].
+    "
 
     "
      |r|
@@ -711,7 +682,7 @@
 !
 
 nextLettersOrDigits:count
-    "get the next count printable letters or digits [0-9A-Za-z]."
+    "get a random string consisting of count printable letters or digits [0-9A-Za-z]."
 
     |res cnt "{ Class:SmallInteger }"|
 
@@ -728,7 +699,7 @@
     ^ res
 
     "
-      Random new nextLettersOrDigits:40
+      Random new nextLettersOrDigits:40 
     "
 ! !
 
@@ -787,20 +758,9 @@
 !
 
 setSeed:seedValue
-    "set the initial seed and intialite the PRNG parameters.
-     These numbers implement a maximum period generator which passes
-     the spectral test for randomness for dimensions 2 3 4 5 6 and
-     the product does not overflow  2 raisedTo:29.
+    "set the initial seed and intialize the PRNG parameters."
 
-     These numbers are carefully choosen, so don't change them, 
-     unless you know what you are doing!!"
-
-    modulus := 244944 " 244957 " .
-    multiplier := 1597.
-    increment := 51749.
-    seed := seedValue \\ modulus.
-
-    self step.
+    self subclassResponsibility.
 
     "Modified: / 12-11-1999 / 17:50:52 / stefan"
     "Created: / 26-05-2007 / 21:25:10 / cg"
@@ -809,10 +769,7 @@
 step
     "compute the next random integer"
 
-    seed := (seed * multiplier + increment) \\ modulus
-
-    "Created: 1.4.1997 / 22:40:45 / cg"
-    "Modified: 1.4.1997 / 22:43:01 / cg"
+    self subclassResponsibility
 ! !
 
 !Random methodsFor:'testing'!
@@ -834,6 +791,12 @@
     ^ false
 
     "Created: 1.4.1997 / 22:38:27 / cg"
+!
+
+maxInteger
+    "the max value returned by self nextInteger"
+
+    self subclassResponsibility
 ! !
 
 !Random class methodsFor:'documentation'!