-- Copyright (C) 1984 by Xerox Corporation. All rights reserved. -- TestRandomness.mesa, HGM, 17-Apr-84 2:20:54 DIRECTORY Format USING [], Inline USING [BITAND, BITSHIFT], Process USING [Yield], Put USING [CR, Line, LongDecimal, LongNumber, Number, Text], System USING [GetClockPulses, Pulses, PulsesToMicroseconds], Window USING [Handle], OthelloDefs USING [ CommandProcessor, IndexTooLarge, MyNameIs, RegisterCommandProc], DicentraInputOutput USING [Input], MultibusAddresses USING [IOAddress, timeout]; TestRandomness: PROGRAM IMPORTS Inline, Process, Put, System, OthelloDefs, DicentraInputOutput = BEGIN log: Window.Handle ← NIL; count: CARDINAL = 50000; bits: PACKED ARRAY [0..count) OF [0..1); BiasedBit: PROCEDURE RETURNS [bit: [0..1)] = INLINE BEGIN where: LONG POINTER = MultibusAddresses.timeout + 00EH; -- Port B Data byte: WORD ← DicentraInputOutput.Input[where]; bit ← Inline.BITSHIFT[Inline.BITAND[byte, 080H], -7]; -- Wait[1]; END; Bit: PROCEDURE RETURNS [bit: [0..1)] = BEGIN DO bit ← BiasedBit[]; IF bit # BiasedBit[] THEN RETURN; ENDLOOP; END; Wait: PROCEDURE [ms: CARDINAL] = BEGIN start: System.Pulses = System.GetClockPulses[]; DO now: System.Pulses = System.GetClockPulses[]; IF System.PulsesToMicroseconds[[now-start]] > ms*1000 THEN EXIT; Process.Yield[]; -- Yetch. Pause is too crude ENDLOOP; END; RandomBits: PROCEDURE = BEGIN ones, zeros: LONG CARDINAL ← 0; FOR i: CARDINAL IN [0..500) DO bit: [0..1) ← BiasedBit[]; Put.Number[log, bit, [16, FALSE, TRUE, 1]]; IF (i MOD 50) = 49 THEN Put.CR[log]; IF bit = 0 THEN zeros ← zeros + 1 ELSE ones ← ones + 1; ENDLOOP; Put.Text[log, "Ones: "L]; Put.LongDecimal[log, ones]; Put.Text[log, " "L]; Put.LongDecimal[log, ones*100/(ones+zeros)]; Put.Text[log, "%, Zeros: "L]; Put.LongDecimal[log, zeros]; Put.Text[log, " "L]; Put.LongDecimal[log, zeros*100/(ones+zeros)]; Put.Text[log, "%."L]; Put.CR[log]; END; CollectBits: PROCEDURE = BEGIN start, stop: System.Pulses; Put.Line[log, "Collecting bits..."L]; start ← System.GetClockPulses[]; FOR i: CARDINAL IN [0..count) DO bits[i] ← Bit[]; ENDLOOP; stop ← System.GetClockPulses[]; Put.Text[log, "That's "L]; Put.LongDecimal[log, LONG[1000]*count/(System.PulsesToMicroseconds[[stop-start]]/1000)]; Put.Line[log, " bits/second."L]; END; FrequencyTest: PROCEDURE = BEGIN total: LONG CARDINAL = count; s0, s1: LONG CARDINAL ← 0; chi: LONG CARDINAL; Put.Line[log, "Frequency test..."L]; FOR i: CARDINAL IN [0..count) DO bit: [0..1) ← bits[i]; SELECT bit FROM 0 => s0 ← s0 + 1; 1 => s1 ← s1 + 1; ENDCASE => ERROR; ENDLOOP; PrintPercent["0"L, s0, total]; PrintPercent["1"L, s1, total]; chi ← 2*(s0*s0 + s1*s1)/(total/100) - 100*total; Put.Text[log, "Chi↑2: "L]; Put.LongDecimal[log, chi]; Put.Text[log, "%."L]; Put.CR[log]; END; PairTest: PROCEDURE = BEGIN total: LONG CARDINAL = count/2; s0, s1, s2, s3: LONG CARDINAL ← 0; chi: LONG CARDINAL; Put.Line[log, "Pair test..."L]; FOR i: CARDINAL IN [0..count/2) DO pair: [0..3) ← bits[i]*2 + bits[i+count/2]; SELECT pair FROM 0 => s0 ← s0 + 1; 1 => s1 ← s1 + 1; 2 => s2 ← s2 + 1; 3 => s3 ← s3 + 1; ENDCASE => ERROR; ENDLOOP; PrintPercent["00"L, s0, total]; PrintPercent["01"L, s1, total]; PrintPercent["10"L, s2, total]; PrintPercent["11"L, s3, total]; chi ← 4*(s0*s0 + s1*s1 + s2*s2 + s3*s3)/(total/100) - 100*total; Put.Text[log, "Chi↑2: "L]; Put.LongDecimal[log, chi]; Put.Text[log, "%."L]; Put.CR[log]; END; PairTest2: PROCEDURE = BEGIN total: LONG CARDINAL = count/2; s0, s1, s2, s3: LONG CARDINAL ← 0; chi: LONG CARDINAL; Put.Line[log, "Pair test (2)..."L]; FOR i: CARDINAL ← 0, i + 2 WHILE i < count DO pair: [0..3) ← bits[i]*2 + bits[i+1]; SELECT pair FROM 0 => s0 ← s0 + 1; 1 => s1 ← s1 + 1; 2 => s2 ← s2 + 1; 3 => s3 ← s3 + 1; ENDCASE => ERROR; ENDLOOP; PrintPercent["00"L, s0, total]; PrintPercent["01"L, s1, total]; PrintPercent["10"L, s2, total]; PrintPercent["11"L, s3, total]; chi ← 4*(s0*s0 + s1*s1 + s2*s2 + s3*s3)/(total/100) - 100*total; Put.Text[log, "Chi↑2: "L]; Put.LongDecimal[log, chi]; Put.Text[log, "%."L]; Put.CR[log]; END; ByteTest: PROCEDURE = BEGIN bytes: POINTER TO PACKED ARRAY [0..0) OF [0..256) = LOOPHOLE[@bits]; total: LONG CARDINAL = count/8; s: ARRAY [0..256) OF LONG CARDINAL ← ALL[0]; chi: LONG CARDINAL ← 0; Put.Line[log, "Byte test..."L]; FOR i: CARDINAL IN [0..count/8) DO byte: [0..3) ← bytes[i]; s[byte] ← s[byte] + 1; ENDLOOP; Put.Text[log, "Expected is "L]; Put.LongDecimal[log, 100*total/256]; Put.Line[log, "%."L]; FOR i: CARDINAL IN [0..256) DO Put.LongNumber[log, s[i], [10, FALSE, TRUE, 4]]; IF (i MOD 010H) = 0FH THEN Put.CR[log]; chi ← chi + s[i]*s[i]; ENDLOOP; chi ← 256*chi/(total/100) - 100*total; Put.Text[log, "Chi↑2: "L]; Put.LongDecimal[log, chi]; Put.Text[log, "%."L]; Put.CR[log]; END; GapTest: PROCEDURE = BEGIN max: CARDINAL = 24; total: LONG CARDINAL ← 0; gap: CARDINAL ← 0; b: ARRAY [0..max] OF LONG CARDINAL ← ALL[0]; expected: ARRAY [0..max] OF LONG CARDINAL; chi: LONG CARDINAL ← 0; Put.Line[log, "Gap test..."L]; FOR i: CARDINAL IN [0..count) DO IF bits[i] = 0 THEN gap ← gap + 1 ELSE BEGIN gap ← MIN[gap, max]; b[gap] ← b[gap] + 1; total ← total + 1; gap ← 0; END; ENDLOOP; expected[0] ← 100*total/2; FOR i: CARDINAL IN [1..max) DO expected[i] ← expected[i-1]/2; ENDLOOP; expected[max] ← expected[max-1]; FOR i: CARDINAL IN [1..max] DO int: LONG INTEGER ← 100*b[i]-expected[i]; diff: LONG CARDINAL ← ABS[int]; SELECT TRUE FROM expected[i] = 0 => NULL; diff > LAST[CARDINAL] => chi ← chi + (diff/10)*(diff/10)/(expected[i]/100); ENDCASE => chi ← chi + diff*diff/expected[i]; Put.LongNumber[log, i, [10, FALSE, TRUE, 2]]; Put.LongNumber[log, b[i], [10, FALSE, TRUE, 8]]; Put.LongNumber[log, expected[i], [10, FALSE, TRUE, 8]]; Put.LongNumber[log, chi, [10, FALSE, TRUE, 8]]; Put.CR[log]; ENDLOOP; Put.Text[log, "Chi↑2: "L]; Put.LongDecimal[log, chi]; Put.Text[log, "%."L]; Put.CR[log]; END; PrintPercent: PROCEDURE [s: STRING, n, total: LONG CARDINAL] = BEGIN Put.Text[log, s]; FOR i: CARDINAL IN [s.length..6) DO Put.Text[log, " "L]; ENDLOOP; Put.LongNumber[log, n, [10, FALSE, TRUE, 8]]; Put.LongNumber[log, n*100/total, [10, FALSE, TRUE, 5]]; Put.CR[log]; END; commandProcessor: OthelloDefs.CommandProcessor ← [Commands]; Commands: PROCEDURE [index: CARDINAL] = BEGIN SELECT index FROM 0 => BEGIN OthelloDefs.MyNameIs[ myNameIs: "Random Biased Bits"L, myHelpIs: "Print some raw bits from the random number generator"L]; RandomBits[]; END; 1 => BEGIN OthelloDefs.MyNameIs[ myNameIs: "Random Collect"L, myHelpIs: "Collect (and save) a bunch of random bits"L]; CollectBits[]; END; 2 => BEGIN OthelloDefs.MyNameIs[ myNameIs: "Random Info"L, myHelpIs: "Alanyze previously saved bits"L]; FrequencyTest[]; PairTest[]; PairTest2[]; ByteTest[]; GapTest[]; END; ENDCASE => OthelloDefs.IndexTooLarge; END; -- Main Body OthelloDefs.RegisterCommandProc[@commandProcessor]; END.