'===========================================================================
' Subject: RANDOM NUMBER GENERATOR            Date: 02-04-96 (13:42)       
'  Author: David Arigan                       Code: QB, QBasic, PDS        
'  Origin: FidoNet QUIK_BAS Echo            Packet: MISC.ABC
'===========================================================================
' -> RANDOMIZE TIMER
' > -> randNum = INT(RND * 50) + 1
' > -> I've always used the above method for calculating random numbers.
' > -> Does anyone have a better way of getting a truly random number?
' >  That's the truest "random" number you can get. Laterz..

'Hmmm... not really

' > Ok - I thought I had seen other ways of doing it before, but
' > apparently I was mistaken. 

'Here... I recently converted it from 'C'.  You might be able to use it.

DECLARE FUNCTION rmarin% (ij AS INTEGER, kl AS INTEGER)
DECLARE FUNCTION ranmar% (rvec() AS DOUBLE, ln AS INTEGER)

'************************************************************************
'This random number generator originally appeared in "Toward a Universal
'Random Number Generator" by George Marsaglia and Arif Zaman.
'Florida State University Report: FSU-SCRI-87-50 (1987)
'
'It was later modified by F. James and published in "A Review of Pseudo-
'random Number Generators"
'
'Converted from FORTRAN to C by Phil Linttell, James F. Hickling
'Management Consultants Ltd, Aug. 14, 1989.
'
'THIS IS THE BEST KNOWN RANDOM NUMBER GENERATOR AVAILABLE.
'      (However, a newly discovered technique can yield
'        a period of 10^600. But that is still in the development stage.)
'
'It passes ALL of the tests for random number generators and has a period
'  of 2^144, is completely portable (gives bit identical results on all
'  machines with at least 24-bit mantissas in the floating point
'  representation).
'
'The algorithm is a combination of a Fibonacci sequence (with lags of 97
'  and 33, and operation "subtraction plus one, modulo one") and an
'  "arithmetic sequence" (using subtraction).
'
'On a Vax 11/780, this random number generator can produce a number in
'   13 microseconds.
'***********************************************************************

  CONST TRUE = -1
  CONST FALSE = 0

  DIM SHARED u(97) AS DOUBLE, c AS DOUBLE, cd AS DOUBLE, cm AS DOUBLE
  DIM SHARED i97 AS INTEGER, j97 AS INTEGER, test AS INTEGER

  DIM temp(100) AS DOUBLE
  DIM i AS INTEGER
  DIM ij AS INTEGER, kl AS INTEGER, ln AS INTEGER

  ' These are the seeds needed to produce the test case results

  ij = 1802
  kl = 9373

  ' Do the initialization

  IF rmarin(ij, kl) = 1 THEN END

  ' Generate 20000 random numbers

  ln = 100
  FOR i = 0 TO 199
    IF ranmar(temp(), ln) = 1 THEN END
  NEXT i

  ' If the random number generator is working properly,
  ' the next six random numbers should be:
  '
  '          6533892.0  14220222.0   7275067.0
  '          6172232.0   8354498.0  10633180.0

  ln = 6
  IF ranmar(temp(), ln) = 1 THEN END
  FOR i = 0 TO 5
    PRINT USING "############.#"; 4096# * 4096# * temp(i)
  NEXT

FUNCTION ranmar% (rvec() AS DOUBLE, ln AS INTEGER)
  DIM uni AS DOUBLE
  DIM ivec AS INTEGER

  IF NOT test THEN
    PRINT "RANMAR: Call the initialization routine (RMARIN) ";
    PRINT "before calling RANMAR."
    ranmar = 1
    EXIT FUNCTION
  END IF
  FOR ivec = 0 TO ln - 1
    uni = u(i97) - u(j97)
    IF uni < 0# THEN uni = uni + 1#
    u(i97) = uni
    i97 = i97 - 1
    IF i97 < 0 THEN i97 = 96
    j97 = j97 - 1
    IF j97 < 0 THEN j97 = 96
    c = c - cd
    IF c < 0# THEN c = c + cm
    uni = uni - c
    IF uni < 0# THEN uni = uni + 1#
    rvec(ivec) = uni
    ranmar = 0
  NEXT ivec
END FUNCTION

'************************************************************************
'This is the initialization routine for the random number generator RANMAR()
'NOTE: The seed variables can have values between:    0 <= IJ <= 31328
'                                                     0 <= KL <= 30081
'The random number sequences created by these two seeds are of sufficient
'length to complete an entire calculation with. For example, if several
'different groups are working on different parts of the same calculation,
'each group could be assigned its own IJ seed. This would leave each group
'with 30000 choices for the second seed. That is to say, this random
'number generator can create 900 million different subsequences -- with
'each subsequence having a length of approximately 10^30.
'
'Use IJ = 1802 & KL = 9373 to test the random number generator. The
'subroutine RANMAR should be used to generate 20000 random numbers.
'Then display the next six random numbers generated multiplied by 4096*4096
'If the random number generator is working properly, the random numbers
'should be:
'          6533892.0  14220222.0   7275067.0
'          6172232.0   8354498.0  10633180.0
'***********************************************************************
FUNCTION rmarin% (ij AS INTEGER, kl AS INTEGER)
  DIM s AS DOUBLE, t AS DOUBLE
  DIM i AS INTEGER, j AS INTEGER, k AS INTEGER, l AS INTEGER, m AS INTEGER
  DIM ii AS INTEGER, jj AS INTEGER

  ' Change FALSE to TRUE in the next statement to test the
  ' random routine.

  test = TRUE
  IF (ij < 0 OR ij > 31328) OR (kl < 0 OR kl > 30081) THEN
    PRINT "RMARIN: The first random number seed must have a ";
    PRINT "value between 0 and 31328"
    PRINT "        The second random number seed must have a ";
    PRINT "value between 0 and 30081"
    rmarin = 1
    EXIT FUNCTION
  END IF
  i = INT(ij / 177# MOD 177#) + 2
  j = INT(ij MOD 177#) + 2
  k = INT(kl / 169# MOD 178#) + 1
  l = INT(kl MOD 169#)
  FOR ii = 0 TO 96
    s = 0#
    t = .5
    FOR jj = 0 TO 23
      m = INT(k * (i * j MOD 179#) MOD 179#)
      i = j
      j = k
      k = m
      l = INT((l * 53# + 1#) MOD 169#)
      IF (l * m MOD 64#) >= 32 THEN
        s = s + t
      END IF
      t = t * .5
    NEXT jj
    u(ii) = s
  NEXT
  c = 362436# / 16777216#
  cd = 7654321# / 16777216#
  cm = 16777213# / 16777216#
  i97 = 96
  j97 = 32
  test = TRUE
  rmarin = 0
END FUNCTION
