" Rndm:
"  Author:  Charles E. Campbell, Jr.
"  Date:    May 28, 2004
"  Version: 3
"
"  Discussion:  algorithm developed at MIT
"
"           <Rndm.vim> uses three pseudo-random seed variables
"              g:rndm_m1 g:rndm_m2 g:rndm_m3
"           Each should be on the interval 0 - 100,000,000
"
" RndmInit(s1,s2,s3): takes three arguments to set the three seeds (optional)
" Rndm()            : generates a pseudo-random variate on [0,100000000)
" Urndm(a,b)        : generates a uniformly distributed pseudo-random variate
"                     on the interval [a,b]
" Dice(qty,sides)   : emulates a variate from sum of "qty" user-specified
"                     dice, each of which can take on values [1,sides]
" ---------------------------------------------------------------------

" Randomization Variables:
" with a little extra randomized start from localtime()
let g:rndm_m1 = 32007779 + (localtime()%100 - 50)
let g:rndm_m2 = 23717810 + (localtime()/86400)%100
let g:rndm_m3 = 52636370 + (localtime()/3600)%100

" ---------------------------------------------------------------------

" RndmInit: allow user to initialize pseudo-random number generator seeds
fun! RndmInit(lm1,lm2,lm3)
  let g:rndm_m1 = a:lm1
  let g:rndm_m2 = a:lm2
  let g:rndm_m3 = a:lm3
endfun

" ---------------------------------------------------------------------

" Rndm: generate pseudo-random variate on [0,100000000)
fun! Rndm()
  let m4= g:rndm_m1 + g:rndm_m2 + g:rndm_m3
  if( g:rndm_m2 < 50000000 )
    let m4= m4 + 1357
  endif
  if( m4 >= 100000000 )
    let m4= m4 - 100000000
  endif
  if( m4 >= 100000000 )
    let m4= m4 - 100000000
  endif
  let g:rndm_m1      = g:rndm_m2
  let g:rndm_m2      = g:rndm_m3
  let g:rndm_m3      = m4
  return g:rndm_m3
endfun

" ---------------------------------------------------------------------

" Urndm: generate uniformly-distributed pseudo-random variate on [a,b]
fun! Urndm(a,b)

  " sanity checks
  if a:b < a:a
   return 0
  endif
  if a:b == a:a
   return a:a
  endif

  " Using modulus: rnd%(b-a+1) + a  loses high-bit information
  " and makes for a poor random variate.  Following code uses
  " rejection technique to adjust maximum interval range to
  " a multiple of (b-a+1)
  let amb       = a:b - a:a + 1
  let maxintrvl = 100000000 - ( 100000000 % amb)
  let isz       = maxintrvl / amb

  let rnd= Rndm()
  while rnd > maxintrvl
   let rnd= Rndm()
  endw

  return a:a + rnd/isz
endfun

" ---------------------------------------------------------------------

" Dice: assumes one is rolling a qty of dice with "sides" sides.
"       Example - to roll 5 four-sided dice, call Dice(5,4)
fun! Dice(qty,sides)
  let roll= 0

  let sum= 0
  while roll < a:qty
   let sum = sum + Urndm(1,a:sides)
   let roll= roll + 1
  endw

  return sum
endfun

" ---------------------------------------------------------------------

