-- 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.