program Tausgen ;

{
 Demonstration of Tausworthe Pseudorandom Number Generator
 30 April 1985
 David P Kirschbaum, Toad Hall
 7573 Jennings Lane, Fayetteville NC  28303
 on ARPANet:  ABN.ISCAMS@USC-ISID
 See extracts from Professor Thesen's paper (the source of this code) at
 the end of this program.

 Expect an initial delay during the initialization of the byte table and
 offset pointers when the program actually runs.  During actual implementation
 in a program, the table would be initialized only once (until you wanted to
 reseed again, anyway).

 If using my simple screen display version below, you have flow control.
 Enter any key to pause, and enter RETURN to continue.  (Your system may
 stop on its own occasionally if your system XOFFs high-speed printing to the
 console.  Just enter RETURN to continue.)

 Enter Q or q to end.
}

{Relax string parameter checking}

{V$-}

Type
  Line  = string[79] ;
  Menu  = string[25] ;


const
  pminusl = 97  ; {p = 98; q = 27}
  maxbit  = 255 ;
  maxs    = 26  ;
  Selection : array[1..4] of Menu  =
              ('1  Random Number Display ' ,
               '2  Speed Run (no display)' ,
               '3  Distribution Display  ' ,
               '4  Quit                  ') ;
var
  Ch        : Char ;
  Sentence  : Line ;
  BlankLine : Line ;
  Halted    : Boolean ;
  Quit      : Boolean ;
  Error     : Boolean ;
  f,s       : Integer ;
  x,y       : Integer ;
  count     : Integer ;
  prandom   : Integer ;
  choice    : Integer ;
  ByteTable : array[0..pminusl]of byte ;
  RCounter  : array[0..maxbit] of Integer ;

procedure CenterPrint(Sentence : Line) ;
var
  Centered : Line ;

  begin
    Centered := BlankLine ;
    y := (80 - Length(Sentence)) div 2 ;
    Insert(Sentence,Centered,y) ;
    Writeln(Centered) ;
  end ;


procedure PlotXY(var number : integer; value : integer) ;

  begin
    x := (number mod 12) * 4 + 12 ;
    y := (number div 12) + 2 ;
    GoToXY(x,y) ;
    Write(value:4) ;
  end ;  {PlotXY}


procedure CheckUser ;

  begin
    if KeyPressed then
      begin
        GoToXY(1,24) ;
        Read(Ch) ;
        Quit := Ch in ['Q', 'q'] ;
      end ;  {KeyPressed}
  end ; {CheckUser}

procedure ContPrompt ;

  begin
    Writeln ;
    GoToXY(1,22) ;
    CenterPrint('Hit any key to continue:  ' ) ;
    Writeln ;
    GoToXY(54,22) ;
    Repeat until KeyPressed = True ;
  end ;

procedure TauInit(var f,s : integer) ;
var
  BitIndex  : integer ;
  WorkTable : array[0..pminusl]of byte ;

  begin
    for count := 0 to pminusl do
      begin
        ByteTable[count] := maxbit ;
        WorkTable[count] := 0   ;
      end ;  {count loop}
    f := pminusl ;
    s := maxs ;
    Quit := False ;
    for BitIndex := 1 to 16 do
      begin
        CheckUser ;
        If NOT Quit then
          begin
            for count := 1 to 9800 do
              begin
                if f < pminusl then
                  f := f + 1
                else
                  f := 0 ;
                if s < pminusl then
                  s := s + 1
                else
                  s := 0 ;
                ByteTable[f] := ByteTable[f] xor ByteTable[s] ;
              end;  {count loop}
            if BitIndex > 8 then
              begin
                ClrScr ;
                Writeln ;
                Writeln('Loop ',BitIndex) ;
                for count := 0 to pminusl do
                  begin
                    if odd(ByteTable[count]) then
                      WorkTable[count] := (WorkTable[count] div 2) + 128
                    else
                      WorkTable[count] := WorkTable[count] div 2 ;
                    ByteTable[count] := ByteTable[count] div 2 ;
                    PlotXY(count,WorkTable[count]) ;
                  end;  {count loop}
              end ; {BitIndex > 8}
          end ;  {not Quit}
      end ;  {BitIndex loop}
    ClrScr ;
    CenterPrint('Final Byte Table.') ;
    for count := 0 to pminusl do
      begin
        ByteTable[count] := WorkTable[count] ;
        PlotXY(count,ByteTable[count]) ;
      end;  {count loop}
    f := pminusl ;
    s := maxs ;
  end ;  {TauInit}


function RByte : byte ;
  begin
    if f < pminusl then
      f := f + 1
    else
      f := 0 ;
    if s < pminusl then
      s := s + 1
    else
      s := 0 ;
    RByte := ByteTable[f] ;
    ByteTable[f] := ByteTable[f] xor ByteTable[s] ;
  end ;  {RByte}


{main program               [Toad Hall]}
begin
  FillChar(BlankLine,79,chr(32)) ;
  ClrScr ;
  CenterPrint('A Toad Hall Demonstration.') ;
  CenterPrint('of a Tausworthe Pseudorandom Number Generator.') ;
  CenterPrint('as described by Thesen et al.') ;
  Writeln ;
  CenterPrint('Beginning table initialization.') ;
  TauInit(f,s) ;
  ContPrompt ;
  ClrScr ;
  Writeln ;
  CenterPrint('Initialization complete.') ;
  Writeln ;
  for x := 1 to 4 do
    CenterPrint(Selection[x]) ;
  Writeln ;
  Quit  := False ;
  Error := True ;
  repeat
    GoToXY(30,10) ;
    Write('Enter Selection (1-4):  ') ;
    Readln(choice) ;
    if choice in [1..4] then
      Error := False ;
    if choice = 4 then
      Quit := True ;
  until Error = False ;

  If NOT Quit then
    begin
      ClrScr ;
      CenterPrint('Beginning random number generation.') ;
      for prandom := 0 to maxbit do
        RCounter[prandom] := 0 ;

{ This is a screen display so you (human) can see the numbers.  Comment
 this out, and use the next segment if you want clock timing.  Add in your
 own file-related stuff if you want to collect lots of numbers for some sort
 of statistical analysis.  Easy... but YOU can do it!
}

      if choice = 1 then
        repeat
          for y := 5 to 23 do
            begin
              If NOT Quit then
                begin
                  for x := 1 to 16 do
                    begin
                      GoToXY(x * 4,y) ;
                      Write(RByte:4) ;
                    end;  {x}
                  CheckUser ;
                end ;  {NOT Quit}
            end;  {y}
        until Quit = True ;  {choice 1}

{And if you want to do some timing loops - this sucker is FAST!
 Kludge in any system clock you have at the beginning and end of this
 next routine, bring it on line instead of the display one above, and
 do some timing.  On my Z80B CP/M system (Morrow Decision I), wristwatch
 timing looked like 8 or 9 seconds for the 32000 loop.  Prof. Thesen
 reported 8 seconds on an IBM-PC.}

        if choice = 2 then
          begin
            CenterPrint('30 000 random numbers.') ;
            Writeln ;
            CenterPrint('The big hand is pointing to .') ;
            for count := 1 to 32000 do
              prandom := RByte ;
            CenterPrint('The big hand is pointing to .') ;
            for x := 1 to 20 do
              Write(' ') ;
            Write('Completed with count = ',count) ;
            Writeln(', and last number = ',prandom) ;
          end ;  {choice 2}

{
 And if you want a down-and-dirty analysis of random number distribution,
 try this one ...
}

        if choice = 3 then
          repeat
            CheckUser ;
            prandom := RByte ;
            if RCounter[prandom] < maxbit then
              RCounter[prandom] := RCounter[prandom] + 1
            else RCounter[prandom] := 0 ;
            PlotXY(prandom,RCounter[prandom]) ;
          until Quit = True ;  {choice 3}

    end  ;  {not Quit}

  GoToXY(1,23) ;
  Writeln ;
  Writeln ;
  CenterPrint('Terminating.') ;
end.

{
 Algorithms extracted from
 "Some Efficient Random Number Generators for Microcomputers"
 by Arne Thesen, Shanshan Sun and Tzyh-Jong Wang
 Industrial Engineering Department
 University of Wisconsin-Madison

 (Released to the Public Domain by Professor Thesen, 23 April 1985
 (message on file at Toad Hall))

 This is a Tausworthe Generator.  Extracts from Prof. Thesen's paper, the
 implementing demonstration code, and some comments...

 by David Kirschbaum, Toad Hall (ABN.ISCAMS@USC-ISID
 "A Tausworthe generator

 "Tausworthe [5] has suggested a different approach to the generation of
 pseudo-random integers.  This procedure, which operates directly on bits
 to form a stream of random bits, has been shown to produce random number
 sequences that (1) have improved statistical properties over LC [linear
 congruential] generators, and (2) have an arbitrarily long period independent
 of the word size of the computer used.

"Tausworthe generators are not in widespread use.  This could be because they
are difficult to implement efficiently in a higher level language, or because
their improved statistical properties are of marginal utility on larger
computers.  On microcomputers, the situation is quite different.  Here the
improvement in period length and in statistical properties is quite
substantial, and well written Tausworthe generators are not necessarily
more time consuming than other classes of generators.

"Algorithm

The basic procedure of a Tausworthe type generator is illustrated in
Figure 1:

         B[i-p]   B[i-r]     B[i]
   B = . . x . . . . x . . . . x . . . . .
           |         |         |
           +-->xor<--+         |
                |              |
                +--------------+

 "Figure 1: Relationship between bits in a Tausworthe
            generated bit stream.

 "Here B is defined as a sequence of bits, and the relationship between
 individual bits in the sequence is defined in Algorithm 2:

    B[i] = B[i-r]  XOR  B[i-p]

    where:   i   = any integer
         r , p   = fixed integers with 0<r<p.
          XOR    = the exclusive OR operator
                   yielding 0 if the terms are
                   equal and 1 if they are not.

  "Algorithm 2: Tausworthe generator

 "When r and p are properly selected (as primitive trimodals [8]), the maximum
 period of the stream (B) is 2**p - 1.

 "Implementation

 "In Program 3 we present a FORTRAN implementation of the Tausworthe based on
 an idea first proposed by Lewis and Payne [3].  To avoid the need to access
 individual bits, the algorithm maintains 15 independent and parallel streams
 of bits, and the exclusive OR operation is performed on all bits at once.
 Since each of the independent bit streams have a period of 2**p - 1, the
 resulting stream of integers will also have a period of 2**p - 1."

 [Algorithm omitted - Toad Hall]

 "BIBLIOGRAPHY  [segment - Toad Hall]
  ...
  5.  Tausworthe, R.C., Random Numbers Generated by
      Linear Recurrence, Modulo Two, Math. Comp. 19
      (1965) 201-209.

  6.  Thesen, Arne, An Efficient Generator of Uniformly
      Distributed Random Deviates Between Zero and One.
      Technical Report.  Mathematics Research Center,
      University of Wisconsin-Madison, 1983.  To appear
      in Simulation.

  7.  Thesen, Arne and Tzyh-Jong Wang, Some Efficient
      Random Number Generators for Micro Computers.  MRC
      Technical Summary Report #2562.  Mathematics Research
      Center, University of Wisconsin-Madison, 1983.

  8.  Zierler, Niel and John Brillhart, On Primitive
      Trinomials (Mod 2), Information and Control,
      Vol. 13 pp 541-554, 1968."

 Toad Hall Notes

 In the Pascal code from Prof. Thesen et al, a random byte (expressed as a
 integer 0-255) is generated from eight parallel streams of random bits.  It
 uses the xor operator built in to TurboPascal.

 The initial seed values pminusl and s were (I believe) chosen because of
 their long period.  Not fully understanding all this yet, I expect there are
 other suitable seeds (perhaps more of the primitive trimodals referred to in
 the basic paper), or perhaps ANY value between 0 and 97 for pminusl, and any
 lesser value for s?  (Sorry, guys, if my thinking is kind of dumb - I'm no
 mathemetician - just a cut-and-try hacker.)

 Prof. Thesen states the Tausworthe algorithm is not constrained by the word
 size of the computer.  This leads me to believe we might be able to implement
 more than the 8 random bit streams he used in this example, but I expect it
 will impact severely on the code speed.
}
