; CONFIDENCE TESTS FOR SRFI-27 "Sources of Random Bits" ; ===================================================== ; ; Sebastian.Egner@philips.com, 2002. ; ; This file contains a small collection of checks for the ; implementation of SRFI-27. It is not meant to be complete ; or to test the actual properties of the underlying generator. ; It is merely meant to run the code and to check some of the ; assumptions made by specification. There is an interface to ; G. Marsaglia's DIEHARD battery of tests for random number ; generators, though. ; ; running this file in Scheme 48 0.57 with SRFI-27 installed: ; ; ,open srfi-27 srfi-23 ascii ; ,load conftest.scm ; ; speed in Scheme 48 0.57 with SRFI-27 opened: ; ; ,time (do ((k 0 (+ k 1))) ((= k 100000)) (random-integer 2)) ; ; about 30000/s on P3@800MHz for the Scheme-impl. ; ; about 160000/s on P3@800MHz for the C/Scheme-impl. ; ; ,time (do ((k 0 (+ k 1))) ((= k 100000)) (random-real)) ; ; about 3000/s on P3@800MHz for the Scheme-impl. ; ; about 180000/s on P3@800MHz for the C/Scheme-impl. ; ; running this file in Gambit 3.0 (interpreter) with SRFI-27 loaded: ; ; (define ascii->char integer->char) ; (load "conftest.scm") ; ; compiling a simple program in Gambit 3.0 (compiler) using this file: ; ; 1. create 'a.scm' with content; ; (include "srfi-27-c.scm") ; (time (do ((k 0 (+ k 1))) ((= k 1000000)) (random-integer 2))) ; (time (do ((k 0 (+ k 1))) ((= k 1000000)) (random-real))) ; 2. compile Scheme into C (verbose paths for clarity) ; GAMBCDIR=$GAMBIT/lib LD_LIBRARY_PATH=$GAMBIT/lib $GAMBIT/gsc/gsc a ; 3. compile and link C code into an executable ; gcc -I$GAMBIT/lib -o a a.c a_.c -lm -lgambc -L$GAMBIT/lib ; 4. run the executable ; GAMBCDIR=$GAMBIT/lib LD_LIBRARY_PATH=$GAMBIT/lib a ; ; speed in Gambit 3.0 with SRFI-27 loaded: ; ; (time (do ((k 0 (+ k 1))) ((= k 100000)) (random-integer 2))) ; ; about 5000/s on P3@800MHz, interpreted ; ; about 200000/s on P3@800MHz, compiled ; ; (time (do ((k 0 (+ k 1))) ((= k 100000)) (random-real))) ; ; about 25000/s on P3@800MHz, interpreted ; ; about 400000/s on P3@800MHz, compiled ; ; history of this file: ; SE, 19-Mar-2002: initial version, based on earlier tests ; SE, 22-Mar-2002: adapted to new procedure names ; SE, 25-Mar-2002: more descriptive output ; SE, 04-Apr-2002: some quick timings; check up ; (check expr) ; evals expr and issues an error if it is not #t. (define (check expr) (if (not (eq? (eval expr (interaction-environment)) #t)) (error "check failed" expr))) ; Basic Tests of the Interface ; ============================ (define (my-random-integer n) (let ((x (random-integer n))) (if (<= 0 x (- n 1)) x (error "(random-integer n) returned illegal value" x)))) (define (my-random-real) (let ((x (random-real))) (if (< 0 x 1) x (error "(random-real) returned illegal value" x)))) (define (check-basics-1) ; generate increasingly large numbers (display "; generating large numbers [bits]: ") (do ((k 0 (+ k 1)) (n 1 (* n 2))) ((> k 1024)) (display k) (display " ") (my-random-integer n)) (display "ok") (newline) ; generate some reals (display "; generating reals [1000 times]: ") (do ((k 0 (+ k 1)) (x (my-random-real) (+ x (my-random-real)))) ((= k 1000) x)) (display "ok") (newline) ; get/set the state (display "; get/set state: ") (let* ((state1 (random-source-state-ref default-random-source)) (x1 (my-random-integer (expt 2 32))) (state2 (random-source-state-ref default-random-source)) (x2 (my-random-integer (expt 2 32)))) (random-source-state-set! default-random-source state1) (let ((y1 (my-random-integer (expt 2 32)))) (if (not (= x1 y1)) (error "state get/set doesn't work" x1 y1 state1))) (random-source-state-set! default-random-source state2) (let ((y2 (my-random-integer (expt 2 32)))) (if (not (= x2 y2)) (error "state get/set doesn't work" x2 y2 state2)))) (display "ok") (newline) ; randomize! (display "; randomize!: ") (let* ((state1 (random-source-state-ref default-random-source)) (x1 (my-random-integer (expt 2 32)))) (random-source-state-set! default-random-source state1) (random-source-randomize! default-random-source) (let ((y1 (my-random-integer (expt 2 32)))) (if (= x1 y1) (error "random-source-randomize! didn't work" x1 state1)))) (display "ok") (newline) ; pseudo-randomize! (display "; pseudo-randomize!: ") (let* ((state1 (random-source-state-ref default-random-source)) (x1 (my-random-integer (expt 2 32)))) (random-source-state-set! default-random-source state1) (random-source-pseudo-randomize! default-random-source 0 1) (let ((y1 (my-random-integer (expt 2 32)))) (if (= x1 y1) (error "random-source-pseudo-randomize! didn't work" x1 state1))) (random-source-state-set! default-random-source state1) (random-source-pseudo-randomize! default-random-source 1 0) (let ((y1 (my-random-integer (expt 2 32)))) (if (= x1 y1) (error "random-source-pseudo-randomize! didn't work" x1 state1)))) (display "ok") (newline) (newline)) ; Testing the MRG32k3a Generator (if implemented) ; =============================================== ; (check-mrg32k3a) ; tests if the underlying generator is the MRG32k3a generator ; as implemented in the reference implementation. This function ; is useful to check whether the reference implementation computes ; the right numbers. (define (check-mrg32k3a) ; check if the initial state is A^16 * (1 0 0 1 0 0) (display "; check A^16 * (1 0 0 1 0 0)") (let* ((s (make-random-source)) (state1 (random-source-state-ref s)) (rand (random-source-make-reals s))) (random-source-state-set! s '(lecuyer-mrg32k3a 1 0 0 1 0 0)) (do ((k 0 (+ k 1))) ((= k 16) (let ((state2 (random-source-state-ref s))) (if (not (equal? state1 state2)) (error "16-th state after (1 0 0 1 0 0) is wrong")))) (rand))) (display "ok") (newline) ; check if pseudo-randomize! advances properly (display "; checking (random-source-pseudo-randomize! s 1 2)") (let ((s (make-random-source))) (random-source-pseudo-randomize! s 1 2) (if (not (equal? (random-source-state-ref s) '(lecuyer-mrg32k3a 1250826159 3004357423 431373563 3322526864 623307378 2983662421))) (error "pseudo-randomize! gives wrong result"))) (display "ok") (newline) ; run the check published by Pierre L'Ecuyer: ; Note that the reference implementation deals slightly different ; with reals mapping m1-1 into 1-1/(m1+1) and not into 0 as in ; L'Ecuyer's original proposal. However, for the first 10^7 reals ; that makes no difference as m1-1 is not generated. (display "; checking (random-source-pseudo-randomize! s 1 2)...") (let* ((x 0.0) (s (make-random-source)) (rand (random-source-make-reals s))) (random-source-state-set! s '(lecuyer-mrg32k3a 12345 12345 12345 12345 12345 12345)) (do ((k 0 (+ k 1))) ((= k 10000000) (if (not (< (abs (- x 5001090.95)) 0.01)) (error "bad sum over 10^7 reals" x))) (set! x (+ x (rand))))) (display "ok") (newline)) ; Writing Data to DIEHARD ; ======================= ; (write-diehard filename s bytes-per-call calls) ; creates a binary file to which bytes-per-call * calls bytes are ; written. The bytes are obtained from the random source s using ; the range n = (expt 256 bytes-per-call). ; The intention of write-diehard is to give implementors a ; '15 min.'-way of running their favourite random number generator ; through a pretty tough testsuite. ; ; try: For the reference implementation, the call ; ; (write-diehard "outfile" (make-random-source) 4 2867200) ; ; should create a file that looks as follows (od -A x -t x1 outfile): ; ; 0000000 92 bb 7e db 1b 14 f6 bb bb 54 a1 55 c2 3e cd ca ; 0000010 23 01 20 35 06 47 65 b0 52 4c b8 c0 21 48 af 67 ; 0000020 63 a9 8c 78 50 73 29 08 62 d1 22 7f a6 89 96 77 ; 0000030 98 28 65 2d 2d 8b f9 52 41 be 8e 3f c5 84 0f ca ; 0000040 c0 fa 03 d6 f0 65 9d 3a 9b ab 6f fe d1 aa 5f 92 ; 0000050 0f ea f6 3b 78 b9 fe ad 63 5e 49 f1 9d c9 8e 2f ; 0000060 53 a9 5d 32 d4 20 51 1d 1c 2e 82 f0 8b 26 40 c0 ; ...total length is 11468800 bytes. ; ; The message digest is md5sum = 4df554f56cb5ed251bd04b0d50767443. ; ; try: For the reference implementation, the call ; ; (write-diehard "outfile" (make-random-source) 3 3822934) ; ; should create a file that looks as follows (od -A x -t x1 outfile): ; ; 000000 bb 7e db 30 a3 49 14 f6 bb d0 f2 d0 54 a1 55 8b ; 000010 8c 03 3e cd ca a3 88 1d 01 20 35 e8 50 c8 47 65 ; 000020 b0 e7 d9 28 4c b8 c0 f2 82 35 48 af 67 42 3e 8a ; 000030 a9 8c 78 12 ef b6 73 29 08 ff e9 71 d1 22 7f 52 ; 000040 b8 f0 89 96 77 dc 71 86 28 65 2d c2 82 fc 8b f9 ; 000050 52 d7 23 2a be 8e 3f 61 a8 99 84 0f ca 44 83 65 ; 000060 fa 03 d6 c2 11 c0 65 9d 3a c2 7a dd ab 6f fe 1c ; ...total length is 11468802 bytes. ; ; The message digest is md5sum = 750ac219ff40c50bb2d04ff5eff9b24c. (define (write-diehard filename s bytes-per-call calls) (let ((port (open-output-file filename)) (rand (random-source-make-integers s)) (n (expt 256 bytes-per-call))) (do ((i 0 (+ i 1))) ((= i calls) (close-output-port port)) (let ((x (rand n))) (do ((k 0 (+ k 1))) ((= k bytes-per-call)) (write-char (ascii->char (modulo x 256)) port) (set! x (quotient x 256))))))) ; run some tests (check-basics-1) (display "passed (check-basics-1)") (newline)