-- File: RandomImpl.mesa -- Last edited by: -- MBrown on August 27, 1982 3:09 pm -- Taft on June 9, 1983 3:49 pm DIRECTORY Random USING [ErrorType], PrincOpsUtils USING [GetClockPulses]; RandomImpl: CEDAR MONITOR IMPORTS PrincOpsUtils EXPORTS Random = { -- Module to produce a random sequence of long integers. Error: PUBLIC ERROR [ec: Random.ErrorType] = CODE; -- Constant definitions defaultSeed: INT = 1354117939; -- Described in InitRandom below. numCalls: INTEGER = 3; -- Described in InitRandom below. -- Module state a: ARRAY [0..55] OF INT; -- Holds 55 random long integers, to be returned by Random. (A[0] is wasted to make the --code generator produce better array accesses.) p: INTEGER [0..55]; -- a[1..p-1] has not yet been returned by Random; p is [1..55] except within Random. maxRand: LONG CARDINAL; -- Largest integer produced by the generator + 1. -- Procedures Init: PUBLIC ENTRY PROC [range, seed: INT] RETURNS [INT] = { -- The parameters range and seed determine the sequence generated by procedure --Next (and Choose) below. -- If range<=0, Next will produce results in [0..LAST[INT]]; otherwise, Next --will produce results in [0..range). If seed=0, a default seed value is used to --determine the starting point of the sequence; if seed>0, seed is scaled if --necessary and then used; if seed<0, a seed value is derived from the system --clock. In any case, the seed value actually used (after scaling) is the INT --value returned by Init. -- Avoid small values of the range parameter; the intent of range is to allow the same --sequence to be produced different machines. minSeed,maxSeed: INT; g, gPrev, gSave: INT; PickSeed: INTERNAL PROC [] RETURNS [INT] = TRUSTED INLINE { -- Returns a "random" seed in [0..LAST[INT]]. RETURN[ABS[LOOPHOLE[PrincOpsUtils.GetClockPulses[],INT]]]; };--PickSeed IF range<=0 THEN { maxRand ← LOOPHOLE[LAST[INT],LONG CARDINAL] + 1; minSeed ← LAST[INT]/10; maxSeed ← LAST[INT] - minSeed } ELSE { maxRand ← range; minSeed ← range/10; maxSeed ← range - minSeed }; IF seed<0 THEN seed ← PickSeed[]; IF seed=0 THEN seed ← defaultSeed; -- Now scale the seed into the proper range (no log routine available...) WHILE seed<minSeed DO seed ← seed*3 ENDLOOP; WHILE seed>maxSeed DO seed ← seed/3 ENDLOOP; -- The array a is initialized by placing seed in a[55], and scattering the values -- (-1)**(i-1) * (F(i) - seed*F(i-1)) MOD maxRand, 0<i<55, -- (where F(i) denotes the i-th Fibonacci number) throughout the rest of a. Then --the generating procedure RandomGen is called, numCalls times, to make things --sufficiently random. a[55] ← gPrev ← seed; g ← 1; FOR i: CARDINAL IN [1..54] DO p ← (21*i) MOD 55; a[p] ← gSave ← g; g ← gPrev-g; gPrev ← gSave; IF g<0 THEN g ← g + maxRand; ENDLOOP; THROUGH [1..numCalls) DO RandomGen[]; ENDLOOP; -- Show a as being empty; first call to Random will call RandomGen again. p ← 1; RETURN[seed] };--Init Next: PUBLIC ENTRY PROC [] RETURNS [INT] = { -- A call to Next returns the next term in the sequence determined by the most --recent call to Init. p ← p-1; IF p=0 THEN { RandomGen[]; p ← 55 }; RETURN[a[p]] }; RandomGen: INTERNAL PROC [] = { -- Additive random number generator using the recurrence y(n) = y(n-55) - y(n-24) --mod maxRand. See John F. Reiser, "Analysis of Additive Random Number Generators", --STAN-CS-77-601, March 1977, or Knuth Volume 2 (second edition.) g: INT; FOR i: INTEGER IN [1..24] DO g ← a[i] - a[i+31]; IF g<0 THEN g ← g + maxRand; a[i] ← g ENDLOOP; FOR i: INTEGER IN [25..55] DO g ← a[i] - a[i-24]; IF g<0 THEN g ← g + maxRand; a[i] ← g ENDLOOP }; Choose: PUBLIC--EXTERNAL--PROC [min, max: INT] RETURNS [INT--[min..max]--] = { Next: ENTRY PROC [] RETURNS [INT] = INLINE { -- INLINE copy of public Next above. p ← p-1; IF p=0 THEN { RandomGen[]; p ← 55 }; RETURN[a[p]] }; intervalLen: LONG CARDINAL; IF min > max THEN ERROR Error[badInterval]; intervalLen ← max - min + 1; --is 0 when min=FIRST[INT], max=LAST[INT] IF (intervalLen = 0) OR (intervalLen > maxRand) THEN ERROR Error[badInterval]; DO -- Draw a number in [0..maxRand). We want to reject this number if it lies in the --"odd interval" at the high end of this range (there is no odd interval if intervalLen --divides maxRand). The funny test below does it (claim). The average number of numbers --drawn is less than 2, and much closer to 1 for small intervalLen. r, rem: LONG CARDINAL; rem ← (r ← Next[]) MOD intervalLen; IF (r - rem) > (maxRand-intervalLen) THEN LOOP; RETURN[min + rem]; ENDLOOP }; [] ← Init [range: 0, seed: 0]; }.--RandomImpl CHANGE LOG Created by MBrown on July 2, 1979 Changed by MBrown on May 2, 1980 11:07 PM -- Renamed module to conform to Cedar standards. Commented-out all subrange --declarations on local integer variables, since subranges that don't start at 0 produce --bad code. Fixed bug in InitRandom that caused it to loop if PickSeed returned 0. Changed by MBrown on September 25, 1980 9:41 AM -- Added Choose; made minor changes to improve code generated. See RandomCardImpl for details. Changed by MBrown on December 6, 1980 9:59 PM -- Conversion to Pilot. Use GetClockPulses to generate random seed. Changed by MBrown on March 9, 1982 10:37 am -- Converted into a module monitor. Added Error to interface. Changed by MBrown on June 8, 1982 10:59 am -- Changed module name from RandomLongIntImpl, Next from Random, Init from InitRandom, --made implementation CEDAR (with one TRUSTED call to System.GetClockPulses). Changed by MBrown on August 27, 1982 3:09 pm -- Call Init [0, 0] on start trap. Changed by Taft on June 9, 1983 3:48 pm -- Convert for Nucleus.