;The Scheme code in this file provides some compatibility with scripts that
;were originally written for use with the older SIOD based Script-Fu plug-in
;of GIMP.
;
;All items defined in this file except for the random number routines are
;deprecated. Existing scripts should be updated to avoid the use of the
;compatibility functions and define statements which follow the random number 
;generator routines.
;
;The items marked as deprecated at the end of this file may be removed
;at some later date.


;The random number generator routines below have been slightly reformatted.
;A couple of define blocks which are not needed have been commented out.
;It has also been extended to enable it to generate numbers with exactly 31
;bits or more.
;The original file was called rand2.scm and can be found in:
;http://www-2.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/scheme/code/math/random/

; Minimal Standard Random Number Generator
; Park & Miller, CACM 31(10), Oct 1988, 32 bit integer version.
; better constants, as proposed by Park.
; By Ozan Yigit

;(define *seed* 1)

(define (srand seed)
  (set! *seed* seed)
  *seed*
)

(define (msrg-rand)
  (let (
       (A 48271)
       (M 2147483647)
       (Q 44488)
       (R 3399)
       )
    (let* (
          (hi (quotient *seed* Q))
          (lo (modulo *seed* Q))
          (test (- (* A lo) (* R hi)))
          )
      (if (> test 0)
        (set! *seed* test)
        (set! *seed* (+ test M))
      )
    )
  )
  *seed*
)

; poker test
; seed 1
; cards 0-9 inclusive (random 10)
; five cards per hand
; 10000 hands
;
; Poker Hand     Example    Probability  Calculated
; 5 of a kind    (aaaaa)      0.0001      0
; 4 of a kind    (aaaab)      0.0045      0.0053
; Full house     (aaabb)      0.009       0.0093
; 3 of a kind    (aaabc)      0.072       0.0682
; two pairs      (aabbc)      0.108       0.1104
; Pair           (aabcd)      0.504       0.501
; Bust           (abcde)      0.3024      0.3058

(define (random n)
  (define (internal-random n)
    (let* (
          (n (inexact->exact (truncate n)))
          (M 2147483647)
          (slop (modulo M (abs n)))
          )
      (let loop ((r (msrg-rand)))
        (if (>= r slop)
          (modulo r n)
          (loop (msrg-rand))
        )
      )
    )
  )

  ; Negative numbers have a bigger range in twos complement platforms
  ; (nearly all platforms out there) than positive ones, so we deal with
  ; the numbers in negative form.
  (if (> n 0)
    (+ n (random (- n)))

    (if (>= n -2147483647)
      (internal-random n)

      ; 31-or-more-bits number requested - needs multiple extractions
      ; because we don't generate enough random bits.
      (if (>= n -1152921504606846975)
        ; Up to 2^60-1, two extractions are enough
        (let ((q (- (quotient (+ n 1) 1073741824) 1))) ; q=floor(n/2^30)
          (let loop ()
            (let ((big (+ (* (internal-random q) 1073741824)
                          (internal-random -1073741824)
                       )
                 ))
              (if (> big n)
                big
                (loop)
              )
            )
          )
        )

        ; From 2^60 up, we do three extractions.
        ; The code is better understood if seen as generating three
        ; digits in base 2^30. q is the maximum value the first digit
        ; can take. The other digits can take the full range.
        ;
        ; The strategy is to generate a random number digit by digit.
        ; Here's an example in base 10. Say the input n is 348
        ; (thus requesting a number between 0 and 347). Then the algorithm
        ; first calls (internal-random 4) to get a digit between 0 and 3,
        ; then (internal-random 10) twice to get two more digits between
        ; 0 and 9. Say the result is 366: since it is greater than 347,
        ; it's discarded and the process restarted. When the result is
        ; <= 347, that's the returned value. The probability of it being
        ; greater than the max is always strictly less than 1/2.
        ;
        ; This is the same idea but in base 2^30 (1073741824). The
        ; first digit's weight is (2^30)^2 = 1152921504606846976,
        ; similarly to how in our base 10 example, the first digit's
        ; weight is 10^2 = 100. In the base 10 example we first divide
        ; the target number 348 by 100, taking the ceiling, to get 4.
        ; Here we divide by (2^30)^2 instead, taking the ceiling too.
        ;
        ; The math is a bit obscured by the fact that we generate
        ; the digits as negative, so that the result is negative as
        ; well, but it's really the same thing. Changing the sign of
        ; every digit just changes the sign of the result.
        ;
        ; This method works for n up to (2^30)^2*(2^31-1) which is
        ; 2475880077417839045191401472 (slightly under 91 bits). That
        ; covers the 64-bit range comfortably, and some more. If larger
        ; numbers are needed, they'll have to be composed with a
        ; user-defined procedure.

        (if (>= n -2475880077417839045191401472)
          (let ((q (- (quotient (+ n 1) 1152921504606846976) 1))) ; q=floor(n/2^60)
            (let loop ()
              (let ((big (+ (* (internal-random q) 1152921504606846976)
                            (* (internal-random -1073741824) 1073741824)
                            (internal-random -1073741824)
                         )
                   ))
                (if (> big n)
                  big
                  (loop)
                )
              )
            )
          )
          (error "requested (random n) range too large")
        )
      )
    )
  )
)

;(define (rngtest)
;  (display "implementation ")
;  (srand 1)
;  (do
;    ( (n 0 (+ n 1)) )
;    ( (>= n 10000) )
;    (msrg-rand)
;  )
;  (if (= *seed* 399268537)
;      (display "looks correct.")
;      (begin
;        (display "failed.")
;        (newline)
;        (display "   current seed ") (display *seed*)
;        (newline)
;        (display "   correct seed 399268537")
;      )
;  )
;  (newline)
;)


;This macro defines a while loop which is needed by some older scripts.
;This is here since it is not defined in R5RS and could be handy to have.

;This while macro was found at:
;http://www.aracnet.com/~briand/scheme_eval.html
(define-macro (while test . body)
  `(let loop ()
     (cond
       (,test
         ,@body
         (loop)
       )
     )
   )
)


;The following define block(s) require the tsx extension to be loaded

(define (realtime)
  (car (gettimeofday))
)


;Items below this line are for compatibility with Script-Fu but
;may be useful enough to keep around

(define (delq item lis)
  (let ((l '()))
    (unless (null? lis)
      (while (pair? lis)
        (if (<> item (car lis))
          (set! l (append l (list (car lis))))
        )
        (set! lis (cdr lis))
      )
    )

    l
  )
)

(define (make-list count fill)
  (vector->list (make-vector count fill))
)

(define (strbreakup str sep)
  (let* (
        (seplen (string-length sep))
        (start 0)
        (end (string-length str))
        (i start)
        (l '())
        )

    (if (= seplen 0)
      (set! l (list str))
      (begin
        (while (<= i (- end seplen))
          (if (substring-equal? sep str i (+ i seplen))
            (begin
               (if (= start 0)
                 (set! l (list (substring str start i)))
                 (set! l (append l (list (substring str start i))))
               )
               (set! start (+ i seplen))
               (set! i (+ i seplen -1))
            )
          )

          (set! i (+ i 1))
        )

        (set! l (append l (list (substring str start end))))
      )
    )

    l
  )
)

(define (string-downcase str)
  (list->string (map char-downcase (string->list str)))
)

(define (string-trim str)
  (string-trim-right (string-trim-left str))
)

(define (string-trim-left str)
  (let (
       (strlen (string-length str))
       (i 0)
       )

    (while (and (< i strlen)
                (char-whitespace? (string-ref str i))
           )
      (set! i (+ i 1))
    )

    (substring str i (string-length str))
  )
)

(define (string-trim-right str)
  (let ((i (- (string-length str) 1)))

    (while (and (>= i 0)
                (char-whitespace? (string-ref str i))
           )
      (set! i (- i 1))
    )

    (substring str 0 (+ i 1))
  )
)

(define (string-upcase str)
  (list->string (map char-upcase (string->list str)))
)

(define (substring-equal? str str2 start end)
  (string=? str (substring str2 start end))
)

(define (unbreakupstr stringlist sep)
  (let ((str (car stringlist)))

    (set! stringlist (cdr stringlist))
    (while (not (null? stringlist))
      (set! str (string-append str sep (car stringlist)))
      (set! stringlist (cdr stringlist))
    )

    str
  )
)


;Items below this line are deprecated and should not be used in new scripts.

(define aset vector-set!)
(define aref vector-ref)
(define fopen open-input-file)
(define mapcar map)
(define nil '())
(define nreverse reverse)
(define pow expt)
(define prin1 write)

(define (print obj . port)
  (apply write obj port)
  (newline)
)

(define strcat string-append)
(define string-lessp string<?)
(define symbol-bound? defined?)
(define the-environment current-environment)

(define *pi*
  (* 4 (atan 1.0))
)

(define (butlast x)
  (if (= (length x) 1)
    '()
    (reverse (cdr (reverse x)))
  )
)

(define (cons-array count type)
  (case type
    ((long)   (make-vector count 0))
    ((short)  (make-vector count 0))
    ((byte)   (make-vector count 0))
    ((double) (make-vector count 0.0))
    ((string) (vector->list (make-vector count "")))
    (else type)
  )
)

(define (fmod a b)
  (- a (* (truncate (/ a b)) b))
)

(define (fread arg1 file)

  (define (fread-get-chars count file)
    (let (
         (str "")
         (c 0)
         )

      (while (> count 0)
        (set! count (- count 1))
        (set! c (read-char file))
        (if (eof-object? c)
            (set! count 0)
            (set! str (string-append str (make-string 1 c)))
        )
      )

      (if (eof-object? c)
          ()
          str
      )
    )
  )

  (if (number? arg1)
      (begin
        (set! arg1 (inexact->exact (truncate arg1)))
        (fread-get-chars arg1 file)
      )
      (begin
        (set! arg1 (fread-get-chars (string-length arg1) file))
        (string-length arg1)
      )
  )
)

(define (last x)
  (cons (car (reverse x)) '())
)

(define (nth k list)
  (list-ref list k)
)

(define (prog1 form1 . form2)
  (let ((a form1))
    (if (not (null? form2))
      form2
    )
    a
  )
)

(define (rand . modulus)
  (if (null? modulus)
    (msrg-rand)
    (apply random modulus)
  )
)

(define (strcmp str1 str2)
  (if (string<? str1 str2)
      -1
      (if (string>? str1 str2)
          1
          0
      )
  )
)

(define (trunc n)
  (inexact->exact (truncate n))
)

(define verbose
  (lambda n
    (if (or (null? n) (not (number? (car n))))
      0
      (car n)
    )
  )
)
