;;;; "random.scm" Pseudo-Random number generator for scheme.
;;; Copyright (C) 1991, 1993 Aubrey Jaffer.
;
;Permission to copy this software, to redistribute it, and to use it
;for any purpose is granted, subject to the following restrictions and
;understandings.
;
;1.  Any copy made of this software must include this copyright notice
;in full.
;
;2.  I have made no warrantee or representation that the operation of
;this software will be error-free, and I am under no obligation to
;provide any services, by way of maintenance, update, or otherwise.
;
;3.  In conjunction with products arising from the use of this
;material, there shall be no use of my name in any advertising,
;promotional, or sales literature without prior written consent in
;each case.

(require 'byte)
(require 'logical)

(define (make-rng seed)
  (define mutex #f)
  (define idx 0)
  (define idy 0)
  (define sta (make-bytes 256))
					; initialize state
  (do ((idx #xff (+ -1 idx)))
      ((negative? idx))
    (byte-set! sta idx idx))

  (if (number? seed)
      (set! seed (number->string seed)))

					; merge seed into state
  (do ((idx 0 (+ 1 idx))
       (kdx 0 (modulo (+ 1 kdx) seed-len))
       (seed-len (bytes-length seed)))
      ((>= idx 256) (set! idy 0))
    (let ((swp (byte-ref sta idx)))
      (set! idy (logand #xff (+ idy (byte-ref seed kdx) swp)))
      (byte-set! sta idx (byte-ref sta idy))
      (byte-set! sta idy swp)))
					; spew
  (lambda ()
    (if mutex (slib:error "random state called reentrantly"))
    (set! mutex #t)
    (set! idx (logand #xff (+ 1 idx)))
    (let ((xtm (byte-ref sta idx)))
      (set! idy (logand #xff (+ idy xtm)))
      (let ((ytm (byte-ref sta idy)))
	(byte-set! sta idy xtm)
	(byte-set! sta idx ytm)
	(let ((ans (byte-ref sta (logand #xff (+ ytm xtm)))))
	  (set! mutex #f)
	  ans)))))

(define *random-state*
  (make-rng "http://swissnet.ai.mit.edu/~jaffer/SLIB.html"))

;;; random:chunk returns an integer in the range of 0 to 255.
(define (random:chunk v) (v))

(define (random:random modu . args)
  (let ((state (if (null? args) *random-state* (car args))))
    (if (exact? modu)
	(let ((bitlen (integer-length (+ -1 modu))))
	  (do ((bln bitlen (+ -8 bln))
	       (rbs 0 (+ (ash rbs 8) (random:chunk state))))
	      ((<= bln 7)
	       (modulo
		(if (zero? bln) rbs
		    (+ (ash rbs bln)
		       (logand (bit-field (random:chunk state) 0 bln))))
		modu))))

	(* (random:uniform state) modu))))
;;;random:uniform is in randinex.scm.  It is needed only if inexact is
;;;supported.

(define (make-random-state . args)
  (let ((seed (if (null? args)
		  (do ((bts (make-bytes 10))
		       (idx 0 (+ 1 idx)))
		      ((>= idx 10) bts)
		    (byte-set! bts idx (random:random 256)))
		  (let ()
		    (require 'object->string)
		    (object->limited-string (car args) 20)))))
    (make-rng seed)))

(define random random:random)

(provide 'random)			;to prevent loops
(if (provided? 'inexact) (require 'random-inexact))
