From Martin Maechler <maechler@stat.math.ethz.ch>  Mon Nov 24 13:51:13 1997
From: Martin Maechler <maechler@stat.math.ethz.ch> (Martin Maechler)
Date: Mon, 24 Nov 1997 14:51:13 +0100
Subject: R-alpha: random number generator -- S-plus's
In-Reply-To: <97Nov21.164332est.26501@mailgate.bank-banque-canada.ca> (message
 from Paul Gilbert on Fri, 21 Nov 1997 16:40:01 -0500)
Message-ID: <199711241351.OAA01213@sophie.ethz.ch>

--Multipart_Mon_Nov_24_14:51:09_1997-1
Content-Type: text/plain; charset=US-ASCII


>>>>> "PaulG" == Paul Gilbert <pgilbert@bank-banque-canada.ca> writes:

    MM> The code is basically in V&R 1 and 2; V&R2 on p.167.  I have it as a
    MM> C function that I used to dyn.load into S-plus in order
    MM> to prove that S-plus was using it.

    PaulG> Could you send this to me. I'd like to try to reproduce some S
    PaulG> results in R.

Okay here is a little shar file which contains a little more;
it also includes (the S code for) my experiments on what S-plus's  set.seed(.)
does. The C code must be compiled (cc  and ld) ``a la R'' before dyn.load.

I did not yet test this in R,
but I  CC: it to R-devel since others may want to comment.

[appended at the end !]

--------------------------------------------------------

  PaulG> Maybe there is some subtlty here that I don't understand, but is it not
  PaulG> possible to just add an optional argument to the functions which call
  PaulG> the random number generator, with the default being the current
  PaulG> generator?
Reasons why I wouldn't like this:

  - There are too many of these functions;  some are quite nested;
  - adding an extra argument to them is ugly and not backwards (R-minus)
	  compatible; 
  - you shouldn't have to change the R code of your simulation, but rather
	  change a global option in order to use a different RNG.
  - You shouldn't accidentally mix two different generators, since the 
	  resulting RNG has unknown properties.be allowed t

  - It isn't too hard, to implement the more flexible scheme,
	   in the current R implementation.

>From the UI, I'd propose
to use  a function instead of  .Random.seed,

	set.random(generator = "Wichmann-Hill", seed = NULL)

The function (also when called with no arguments) returns a list with the
two components
	$generator : character(1)  giving the name of the RNG.
	$seed	   : integer vector

AND also accepts such a list as input (alternatively), i.e.

	I could
	
first)
	.R.seed <- set.random()
	## ....do simulations

later)
	set.random(.R.seed)
	## ....Re-do  simulations indentically

-------------------------------------------------------------------------------

--Multipart_Mon_Nov_24_14:51:09_1997-1
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="runif.shar"
Content-Transfer-Encoding: 7bit

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1997-11-24 14:21 MET by <maechler@sophie>.
# Source directory was `/users/home2/staff/maechler/S/MISC'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#   7088 -rw-r--r-- runif-tst.S
#   3646 -rw-r--r-- setseed-ex.S
#   1132 -rw-r--r-- ../C-Progs/programs/Runif.c
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
  if test "$gettext_dir" = FAILED && test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    set `$dir/gettext --version 2>&1`
    if test "$3" = GNU
    then
      gettext_dir=$dir
    fi
  fi
  if test "$locale_dir" = FAILED && test -f $dir/shar \
     && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
  then
    locale_dir=`$dir/shar --print-text-domain-dir`
  fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
  echo=echo
else
  TEXTDOMAINDIR=$locale_dir
  export TEXTDOMAINDIR
  TEXTDOMAIN=sharutils
  export TEXTDOMAIN
  echo="$gettext_dir/gettext -s"
fi
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  $echo 'WARNING: not restoring timestamps.  Consider getting and'
  $echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if mkdir _sh01181; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= runif-tst.S ==============
if test -f 'runif-tst.S' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'runif-tst.S' '(file already exists)'
else
  $echo 'x -' extracting 'runif-tst.S' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'runif-tst.S' &&
##SunOS4: print(dyn.load2("/u/maechler/C/C-for-S/Runif.o"))
print(dyn.load("/u/maechler/C/C-for-S/Runif.o"))
X.C("Init_seed",
X   Cval = as.integer(12345),
X   Tval = as.integer(987654321))
str(.C("Get_seed", Cval = integer(1), Tval = integer(1)))
X
rU_ numeric(10)
for(i in 1:10) rU[i] <- unlist(.C("Runif", numeric(1)))
rU
str(.C("Get_seed", Cval = integer(1), Tval = integer(1)))
X
Set.seed <- function(Cval = 12345, Tval =987654321)
{
X  ## Purpose: Initialize my implementation of the 'S' RNG (following Ripley)
X  ## -------------------------------------------------------------------------
X  ## Arguments: Congrval and Tausval, the 2 long integers  [== seed]
X  ## -------------------------------------------------------------------------
X  ## Author: Martin Maechler, Date:  9 May 94, 12:04
X  if(!is.loaded(C.symbol("Init_seed")))
X    dyn.load("/u/maechler/C/C-for-S/Runif.o")
X  invisible(.C("Init_seed",
X	       Cval = as.integer(Cval),
X	       Tval = as.integer(Tval)))
}
X
Runif <- function()
{
X  ## Purpose:  test my runif(.)
X  ## -------------------------------------------------------------------------
X  ## Author: Martin Maechler, Date:  9 May 94, 11:54
X  if(!is.loaded(C.symbol("Runif")))
X    dyn.load("/u/maechler/C/C-for-S/Runif.o")
X  unlist(.C("Runif", numeric(1)))
}
X
X
tst.runif <- function(Cval = 12345, Tval =987654321)
{
X  ## Purpose: test my (following Ripley) implementation of S  RNG
X  ##        return 'next' seeds
X  ## -------------------------------------------------------------------------
X  ## Arguments: Congrval and Tausval  [== seed]
X  ## -------------------------------------------------------------------------
X  ## Author: Martin Maechler, Date:  9 May 94, 12:04
X  if(!is.loaded(C.symbol("Runif")))
X    dyn.load("/u/maechler/C/C-for-S/Runif.o")
X  .C("Init_seed",
X     Cval = as.integer(Cval),
X     Tval = as.integer(Tval))
X  .C("Runif", numeric(1))
X  unlist(.C("Get_seed", Cval = integer(1), Tval = integer(1)))
}
X
###
### Test the 'standard'  .Random.seed  --- compare with  my version
###
X
tst.R.S <- function(nn)
{
X  ## Purpose:  return many .Random.seed s
X  ## -------------------------------------------------------------------------
X  ## Arguments: number
X  ## -------------------------------------------------------------------------
X  ## Author: Martin Maechler, Date:  9 May 94, 11:07
X
X  RR <- matrix(0,nrow = 12, ncol= nn)
X  for(i in 1:nn) { RR[,i] _ .Random.seed; runif(1) }
X  t(RR)
}
X
RR <- tst.R.S(2000)
summary(as.data.frame(RR))#-- Random.seed components
RRt <- apply(RR, 2, table)
RRt2 <- lapply(RRt, function(x) as.integer(names(x)))
str(RRt2, vec.len = 20)
for(j in 1:12) {
X  cat("\n j=",j, " 0:63 = R..[[j]]: ", all.equal(0:63, RRt2[[j]]))
}
X
X
X
X
X
X
##--> only   1, 6, 12  are NOT ==  0...63
## [[1]] =          1 5 9 .. 61  (( = 1 + 4*j ,  j= 0..15 ))
## [[6]] = [[12]] = 0..3
X
X
RS   <- rep(0,12)
#.Random.seed _ RS
##>> Error: Invalid random number seed: congruential part must be odd
RS[1] _ 1
X.Random.seed _ RS  #--> 1st part == congruential part !
for(i in 1:20){ print(.Random.seed); runif(1)} #- the 2nd part STAYS 0 !!
RS2 <- RS
RS2[12] _ 1
X.Random.seed _ RS2
for(i in 1:20){ print(.Random.seed); runif(1)} #- 1st part == 1st part above!
X
###==> first   6 numbers are one long integer, == congrval  (see above)
###    second  6 numbers are 2nd long integer  == tausval
X
### congrval is made up from 4 + 4* 6 + 3 = 31 bits
###                          - actually CAN use ALL odd 1:63 for [1] -> 32 bits
###  tausval is made up from     5* 6 + 3 = 33 bits (?)
X
X
###  The following  PROOFS  that  Runif(.) returns the SAME as  runif(.)
###  AND  that .Random.seed  =^=  ( Congr.val, Taus.val )
X
if(!exists("polyn.eval"))
X  polyn.eval <- function(coef, x)
X  {
X    ## Purpose: Compute coef[1] + coef[2]*x + ... + coef[p+1]* x^p
X    ## 	      x can be any array; result if of same dimensions !
X    ## Author: Martin Maechler
X    lc <- length(coef)
X    if(lc == 0) 0
X      else {
X	r <- coef[lc]
X	if(lc > 1) for(i in (lc - 1):1) r <- coef[i] + r * x
X	r
X      }
X  }
X
qR <- c( 1, rep(0,5))
X.Random.seed <- Rs <- c(qR, qR) #---  1 0 0 0 0 0  1 0 0 0 0 0
Set.seed(1,1)
for (i in 1:100) {
X  CT <- unlist(.C("Get_seed", Cval = integer(1), Tval = integer(1)))
X  RS.C <- .Random.seed[1:6]
X  RS.T <- .Random.seed[1:6 +6]
X  cat(i,": ")
X  ##print(c(CT, RS.C = RS.C, RS.T = RS.T))
X  if(CT["Cval"]<0) { CT["Cval"] <- 2^32 + CT["Cval"]
X		     cat("'Cval' corrected by adding 2^32\n") }
X  if(CT["Tval"]<0) { CT["Tval"] <- 2^32 + CT["Tval"]
X		     cat("'Tval' corrected by adding 2^32\n") }
X  if(CT["Cval"] != polyn.eval(RS.C, 2^6)) stop("'Congr.val' do not match!")
X  if(CT["Tval"] != polyn.eval(RS.T, 2^6)) stop(" 'Taus.val' do not match!")
X  u1 <- runif(1)
X  u2 <- Runif()
X  if((u1-u2)!=0) stop("u1 != u2")
}
X
X
X.Random.seed <- Rs <- c(qR, qR) #---  1 0 0 0 0 0  1 0 0 0 0 0
Set.seed(1,1)
X
runif(1)
##> [1] 4.659873e-05
Runif()
##> [1] 4.659873e-05
unlist(.C("Get_seed", Cval = integer(1), Tval = integer(1)))
##>   Cval   Tval
##>  69069 131073
X.Random.seed
##>  [1] 13 55 16  0  0  0  1  0 32  0  0  0
X
Runif()
##> [1] 0.1106027
runif(1)
##> [1] 0.1106027
unlist(.C("Get_seed", Cval = integer(1), Tval = integer(1)))
##>       Cval   Tval
##>  475559465 524293
X.Random.seed
##>  [1] 41 24  7 22 28  0  5  0  0  2  0  0
X
X
### --- finding out things about   set.seed(.) -----
X
for(i in 0:1100){
X  cat("set.seed(i=",formatC(i,dig=4),") -> .Random.s = "); set.seed(i)
X  cat(format(.Random.seed),"\n")
}
##- set.seed(i=     0 ) -> .Random.s = 21 14 49   32 43  1   32 22 36 23 28  3
##- set.seed(i=     1 ) -> .Random.s = 21 14 49   48 24  1   32 22 36 23 28  3
##- set.seed(i=     2 ) -> .Random.s = 21 14 49    0  6  1   32 22 36 23 28  3
##- set.seed(i=     3 ) -> .Random.s = 21 14 49   16 51  0   32 22 36 23 28  3
##- set.seed(i=     4 ) -> .Random.s = 21 14 49   32 32  0   32 22 36 23 28  3
##- set.seed(i=     5 ) -> .Random.s = 21 14 49   48 13  0   32 22 36 23 28  3
##- set.seed(i=     6 ) -> .Random.s = 21 14 49    0 59  3   32 22 36 23 28  3
##- set.seed(i=     7 ) -> .Random.s = 21 14 49   16 40  3   32 22 36 23 28  3
##- .........
##-          			      [ 1  2  3    4  5  6    7  8  9 10 12 12 ]
###--> only  [4:6]  really change !!
###- They have  6 + 6 + 2 = 14  bits each (the ranges are 0:63, 0:63, and 0:3),
###- i.e.,  2^14 = 16384  different possibilities.
###-        ~~~~   ~~~~~
###===>  Period of  set.seed(.) =: P | 2^14
X
## test this period
tst.sseed <- function(i)
{
X  ## Purpose:  test  set.seed(.)
X  ## -------------------------------------------------------------------------
X  ## Author: Martin Maechler, Date:  9 May 94, 11:54
X  set.seed(i)
X  .Random.seed
}
X
for (i in c(0:7, 11, 17, 20, 23, 40, 53, 59, 67, 76, 97)) {
X  cat("i=",i,": ")
X  print(all.equal(tst.sseed(i), tst.sseed(i + 2^ 14)))
}
##--> all  'TRUE'
##
## same thing for  2^13, 2^12, 2^11, 2^10 ! --
##
## Test a full period for  2^10 = 1024 :
X
for (i in 0:(2^10 - 1))
X  if(!is.all.equal(tst.sseed(i), tst.sseed(i + 2^ 10)))
X  cat("i=",i," DIFFERENT ! ")
## -> nothing (i.e., all equal !)
X
###==> Full period is from  set.seed(0) to  set.seed(1023) ! --
SHAR_EOF
  $shar_touch -am 1124142097 'runif-tst.S' &&
  chmod 0644 'runif-tst.S' ||
  $echo 'restore of' 'runif-tst.S' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'runif-tst.S:' 'MD5 check failed'
35e91fce41f56de76bab514ecfc5c840  runif-tst.S
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'runif-tst.S'`"
    test 7088 -eq "$shar_count" ||
    $echo 'runif-tst.S:' 'original size' '7088,' 'current size' "$shar_count!"
  fi
fi
# ============= setseed-ex.S ==============
if test -f 'setseed-ex.S' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'setseed-ex.S' '(file already exists)'
else
  $echo 'x -' extracting 'setseed-ex.S' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'setseed-ex.S' &&
###--> See also (the much OLDER)  ./runif-tst.S
###				  ~~~~~~~~~~~~~
X
ss <- function(i) { set.seed(i); .Random.seed }
s0 <- ss(0)
for(i in -1000:4000)
X if(any(ss(i)[-(4:6)] != s0[-(4:6)])) cat("i=",i," .R.s=",.Random.seed,"\n")
##--> no output, i.e., it's really
##    only .Random.seed[c(4,5,6)] which are modified by set.seed(.)
X
ni <- length(i.set <- -2:2000)
m456 <- matrix(NA, ni, 3, dimnames=list(paste(i.set),paste(4:6)))
for(i in i.set)  m456[paste(i),] <- ss(i)[4:6]
t(m456[1:25,])
X
apply(m456, 2,  function(x) sort(unique(x)))
##- $"4":
##- [1]  0 16 32 48
##-
##- $"5":
##-[1]  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
##[25] 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
##[49] 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
##-
##- $"6":
##- [1] 0 1 2 3
len456 <- apply(m456, 2,  function(x) length(unique(x)))
len456
prod(len456) #-- 1024 : no more than these many different
X
d456 <- dim(table(m456[,"4"], m456[,"5"], m456[,"6"]))#- and these all appear ..
d456
d456 == len456 # Yes !
X
all(m456[,"4"] == rep(16*(0:3), length=ni))#-> TRUE
X
for(i in -4:10) print(ss(i)[4] == 16*(i+2)%%4)
X
table(m456[,"6"]) #-- only 0, 1, 2, 3, but not as regularly ...
##-    0   1   2   3
##-  502 501 500 500
X
##---- mI  contains .Random.seed[4:6]  after set.seed(i = 1:1024) : ----
mI <- m456[paste(1:1024),]
dim(table(mI[,"4"], mI[,"5"], mI[,"6"]))#- and these all appear ..
X
all(ss(0) == ss(1024)) #-- TRUE
for(i in -100:2000)
X   if(any(ss(i) != ss(i + 1024))) cat("i=",i," .R.s=",.Random.seed,"\n")
#- no output, i.e., it's true
X
# the part of the 'congrval'
all(table(mI %*% 2^(6*0:2)) == 1)# TRUE
X
X
## Return 'congrval' part of .Random.seed after  set.seed(.):
## need 'polyn.eval' from my goodies:
ssC <- function(i){ set.seed(i);polyn.eval(.Random.seed[  1:6],2^6)}
ssT <- function(i){ set.seed(i);polyn.eval(.Random.seed[6+1:6],2^6)}
X
##-- All the 'congrval' that set.seed(.) produces ..
Cs <- ii <- 1:1024
for(i in ii) Cs[i] <- ssC(i)
dim(tCs <- table(Cs))# = 1024
all(tCs == 1) # TRUE  (each once)
X
summary(Cs / 2^32)
##-       Min. 1st Qu. Median   Mean 3rd Qu.   Max.
##-  4.694e-05  0.2498 0.4996 0.4996  0.7493 0.9991
X
dCs <- unique(diff(srt.C <- sort(Cs)))
dCs##->  4194304  only one value !
log(dCs, base =2)# == 22
X
C1 <- srt.C[1]
all(srt.C == C1 + (ii-1) * 2^22) #- TRUE !
X
## the values are not quite symmetrical in {0,..,2^32-1} :
2^32 - srt.C[1024]# 3992683
C1                #  201621
X
###------------ Martin Mchler's Summary : ------------------------
X
###--- Mon, Sept. 15, 1997 ----------
X
##- Unfortunately, we don't know what exactly happens in
##- the S-plus code for set.seed(i), namely,  .C("setseed", as.integer(i))
##- However, I have found out the following which should suffice :
##-
##-  1) Only .Random.seed[4:6]  are varyied by set.seed, i.e.,
##-    after set.seed(i), .Random.seed always is
##-
##-            1  2  3  4  5  6  7  8  9 10 11 12
##-       [1] 21 14 49  *  *  * 32 22 36 23 28  3
##-                    X4 X5 X6
##-
##- which means that only the 'congrval' {Venables & Ripley, 2nd ed., p.166-167}
##-    is varied
##-
##-  2) X4 is always ==  16*((i+2) %% 4)
##-     X5 takes (all) values in 0:63  [also with some periodicity]
##-     X6 takes the values of 0:3
##-
##-  3) set.seed(i) <==> set.seed(i + n*1024)  for all integers n, i
##-
##-  4) { set.seed(i) ;  i \in {integers}}
##-     results in a set of 1024 different values of 'congrval'.
##-     These values are EXACTLY EQUISPACED lying in {0,..,2^32}.
##-
##-     If C[1:1024] are these sorted values, we have
##-
##- 	C[i] == 201621 + (i-1)* 2^22  , for i in 1:1024
##-
X
SHAR_EOF
  $shar_touch -am 1124095797 'setseed-ex.S' &&
  chmod 0644 'setseed-ex.S' ||
  $echo 'restore of' 'setseed-ex.S' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'setseed-ex.S:' 'MD5 check failed'
c2d03d4318c03a8f2ed8b4f0b656c647  setseed-ex.S
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'setseed-ex.S'`"
    test 3646 -eq "$shar_count" ||
    $echo 'setseed-ex.S:' 'original size' '3646,' 'current size' "$shar_count!"
  fi
fi
# ============= ../C-Progs/programs/Runif.c ==============
if test ! -d '..'; then
  $echo 'x -' 'creating directory' '..'
  mkdir '..'
fi
if test ! -d '../C-Progs'; then
  $echo 'x -' 'creating directory' '../C-Progs'
  mkdir '../C-Progs'
fi
if test ! -d '../C-Progs/programs'; then
  $echo 'x -' 'creating directory' '../C-Progs/programs'
  mkdir '../C-Progs/programs'
fi
if test -f '../C-Progs/programs/Runif.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING '../C-Progs/programs/Runif.c' '(file already exists)'
else
  $echo 'x -' extracting '../C-Progs/programs/Runif.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > '../C-Progs/programs/Runif.c' &&
/*--- this code is from a posting of
X  Brian Ripley to the S-news, 7 Aug 92,
X  and a slight correction from  Allen Mcintosh <mcintosh@larch.bellcore.com>
X  + a correction of mine (B.R. wrote '69096' instead of '69069'))
X
X  -->  /u/maechler/tex/o.peopl/Ripley-S-RNG.tex
*/
X
/*--- This code is perfectly tested out via S-code
X
X  in -->  /u/maechler/S/MISC/runif-tst.S
*/
X
unsigned long congr_MM, taus_MM;
X
static double xuni()
{
X        unsigned long n, lambda = 69069;
X        congr_MM *= lambda;
X        taus_MM  ^= taus_MM >> 15;
X        taus_MM  ^= taus_MM << 17;
/* Orig (Splus <= 3.2):
X        n = taus_MM ^ congr_MM;
X        return ( (n>>1) / 2147483648.);
X * New: */
X        n = (taus_MM ^ congr_MM) >> 1;
X        if(n) { /* n != 0 */ return( n / 2147483648.);
X	} else return(xuni());
}
X
X
/*--- The following are to be  dyn.load(..)ed   and called  from  S(plus) --*/
X
void Runif (double* res) {  *res = xuni(); }
X
void Init_seed (long* Cval, long* Tval)
{
X  congr_MM = (unsigned long)*Cval;
X  taus_MM  = (unsigned long)*Tval;
}
X
void Get_seed (long* Cval, long* Tval)
{
X  *Cval = (long) congr_MM;
X  *Tval = (long) taus_MM;
}
SHAR_EOF
  $shar_touch -am 1124141497 '../C-Progs/programs/Runif.c' &&
  chmod 0644 '../C-Progs/programs/Runif.c' ||
  $echo 'restore of' '../C-Progs/programs/Runif.c' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo '../C-Progs/programs/Runif.c:' 'MD5 check failed'
f5888c70e55b647a34d8bee3aae3b4ad  ../C-Progs/programs/Runif.c
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < '../C-Progs/programs/Runif.c'`"
    test 1132 -eq "$shar_count" ||
    $echo '../C-Progs/programs/Runif.c:' 'original size' '1132,' 'current size' "$shar_count!"
  fi
fi
rm -fr _sh01181
exit 0

--Multipart_Mon_Nov_24_14:51:09_1997-1
Content-Type: text/plain; charset=US-ASCII



--Multipart_Mon_Nov_24_14:51:09_1997-1--
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=---
r-devel mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
Send "info", "help", or "[un]subscribe"
(in the "body", not the subject !)  To: r-devel-request@stat.math.ethz.ch
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=---


R-alpha: random number generator -- S-plus's
Martin Maechler