#lang scheme

;; Creates a dice with the specified number of sides and optionally
;; with accumulating behaviour.
;; Params:
;;   sides       = The number of sides.
;;   cumulative? = Whether rolls resulting in the highest possible
;;                 of the die should be repeated, summing the results.
;; Returns:
;;   A procedure of one optional argument that simulates a dice roll.
;;   If present, the argument to the dice procedure specifies the
;;   PRNG to use.
(define (make-dice sides cumulative?)
  (letrec ([roll (if cumulative?
                     (lambda ([generator (current-pseudo-random-generator)])
                       (let ([result (+ (random sides generator) 1)])
                         (if (= result sides)
                             (+ result (roll generator))
                     (lambda ([generator (current-pseudo-random-generator)])
                       (+ (random sides generator) 1)))])

(provide/contract [make-dice (-> exact-positive-integer?
                                 (->* ()

;; Factorial function
;; Params:
;;   n = An integer.
;; Returns:
;;   The factorial of the given number.
(define (fact n)
  (for/fold ([f 1]) ([i (in-range 1 (+ n 1))])
    (* f i)))

(provide/contract [fact (-> exact-nonnegative-integer?

;; Structure for test results.
(define-struct test-result
  (successful? critical? value)

(provide/contract (struct test-result
                          ([successful? boolean?]
                           [critical? boolean?]
                           [value any/c])))

;; Structure for test statistics.
(define-struct test-statistics
  (successful critical expectation)

(provide/contract (struct test-statistics
                          ([successful (real-in 0 1)]
                           [critical (real-in 0 1)]
                           [expectation any/c])))