;;; "prng.scm" schlep compiles to prng.c for comparison with Scheme.

#.
#+schlep
(declare-suffixes!
 '((eed (array "unsigned char"))
   (ate (array "unsigned char"))
   (sta (array "unsigned char"))
   (sra (array unsigned))
   ("rgv" (array (array char)))
   ("ean" double)
   ("iff" double)))

(require 'byte)
(require 'printf)
(require 'logical)
#+schlep(require "math")
#+schlep(require "stdio")
#+schlep(require "stdlib")

#+schlep
(define (integer-length n)
  (case n
    ((0) 0)
    ((1) 1)
    ((2 3) 2)
    ((4 5 6 7) 3)
    (else (+ 4 (integer-length (ash n -4))))))

(define (random:chunk sta)
  (cond ((positive? (array-ref sta 258))
	 (array-set! sta 0 258)
	 (printf "random state called reentrantly\n")
	 (exit 1)))
  (array-set! sta 1 258)
  (let* ((idx (logand #xff (+ 1 (array-ref sta 256))))
	 (xtm (array-ref sta idx))
	 (idy (logand #xff (+ (array-ref sta 257) xtm))))
    (array-set! sta idx 256)
    (array-set! sta idy 257)
    (let ((ytm (array-ref sta idy)))
      (array-set! sta xtm idy)
      (array-set! sta ytm idx)
      (let ((ans (array-ref sta (logand #xff (+ ytm xtm)))))
	(array-set! sta 0 258)
	ans))))

(define (random:random modu state)
  (define bitlen (integer-length (+ -1 modu)))
  (define (rnd)
    (do ((bln bitlen (+ -8 bln))
	 (rbs 0 (+ (ash rbs 8) (random:chunk state))))
	((<= bln 7)
	 (if (positive? bln)
	     (set! rbs (logxor (ash rbs bln)
			       (random:chunk state))))
	 (if (< rbs modu) rbs (rnd)))))
  (rnd))

(define (seed->random-state! sta seed)
					; initialize state
  (do ((idx #xff (+ -1 idx)))
      ((negative? idx))
    (array-set! sta idx idx))
					; merge seed into state
  (do ((i 0 (+ 1 i))
       (j 0 (modulo (+ 1 j) seed-len))
       (seed-len (bytes-length seed))
       (k 0))
      ((>= i 256))
    (let ((swp (array-ref sta i)))
      (set! k (logand #xff (+ k (byte-ref seed j) swp)))
      (array-set! sta (array-ref sta k) i)
      (array-set! sta swp k))))

#-schlep
(define (seed->random-state seed)
  (define sta (create-array (Au8 0) (+ 3 256)))
  (seed->random-state! sta seed)
  sta)

(define (square-diff x y)
  (define z (- x y))
  (* z z))

(define (prng! samples modu sta)
  (define sra (create-array (Au32) samples))
  (do ((cnt (+ -1 samples) (+ -1  cnt))
       (num (random:random modu sta) (random:random modu sta))
       (sum 0 (+ sum num)))
      ((negative? cnt)
       (set! sum (+ sum num))
       (let ((mean (/ sum samples)))
	 (do ((cnt (+ -1 samples) (+ -1 cnt))
	      (var2 0 (+ (square-diff mean (array-ref sra cnt)) var2)))
	     ((negative? cnt)
	      (printf "%d / %d = %g +/- %g\n"
		      sum samples mean (sqrt (/ var2 samples)))))))
    (array-set! sra num cnt)))

(define (main argc argv)
  (define n (if (> argc 1)
		(string->number (array-ref argv 1))
		1000))
  (define sta (create-array (Au8 0) (+ 3 256)))
  (seed->random-state! sta "http://swissnet.ai.mit.edu/~jaffer/SLIB.html")
  (prng! n 999 sta)
  0)
