(module t-distribution mzscheme
(require (lib "contract.ss"))
(provide/contract
(random-t-distribution
(case-> (-> random-source? real? real?)
(-> real? real?)))
(t-distribution-pdf
(-> real? real? (>=/c 0.0)))
(t-distribution-cdf
(-> real? real? (real-in 0.0 1.0))))
(require "../math.ss")
(require "../random-source.ss")
(require "../special-functions/gamma.ss")
(require "cdf-beta-inc.ss")
(require "gaussian.ss")
(require "chi-squared.ss")
(require "exponential.ss")
(define random-t-distribution
(case-lambda
((r nu)
(if (<= nu 2)
(let ((y1 (random-unit-gaussian r))
(y2 (random-chi-squared r nu)))
(/ y1 (sqrt (/ y2 nu))))
(let ((y1 0)
(y2 0)
(z 0))
(let loop ()
(set! y1 (random-unit-gaussian r))
(set! y2 (random-exponential r (/ 1 (- (/ nu 2) 1))))
(set! z (/ (* y1 y1) (- nu 2)))
(if (or (< (- 1 z) 0)
(> (exp (- (- y2) z)) (- 1 z)))
(loop)))
(* (/ y2 (sqrt (- 1 (/ 2 nu)))) (- 1 z)))))
((nu)
(random-t-distribution (current-random-source) nu))))
(define (t-distribution-pdf x nu)
(let ((lg1 (lngamma (/ nu 2.0)))
(lg2 (lngamma (/ (+ nu 1.0) 2.0))))
(* (/ (exp (- lg2 lg1)) (sqrt (* pi nu)))
(expt (+ 1.0 (/ (* x x) nu)) (/ (- (+ nu 1.0)) 2.0)))))
(define (poly-eval c n x)
(let ((y (* (vector-ref c 0) x)))
(do ((i 1 (+ i 1)))
((= i n)(+ y (vector-ref c n)))
(set! y (* x (+ y (vector-ref c i)))))))
(define (cornish-fisher t n)
(define coeffs6 '#(0.265974025974025974026
5.449696969696969696970
122.20294372294372294372
2354.7298701298701298701
37625.00902597402597403
486996.1392857142857143
4960870.65
37978595.55
201505390.875
622437908.625))
(define coeffs5 '#(0.2742857142857142857142
4.499047619047619047619
78.45142857142857142857
1118.710714285714285714
12387.6
101024.55
559494.0
1764959.625))
(define coeffs4 '#(0.3047619047619047619048
3.752380952380952380952
46.67142857142857142857
427.5
2587.5
8518.5))
(define coeffs3 '#(0.4
3.3
24.0
85.5))
(let* ((a (- n 0.5))
(b (* 48.0 a a))
(z2 (* a (log1p (/ (* t t) n))))
(z (sqrt z2))
(p5 (* z (poly-eval coeffs6 9 z2)))
(p4 (* (- z) (poly-eval coeffs5 7 z2)))
(p3 (* z (poly-eval coeffs4 5 z2)))
(p2 (* (- z) (poly-eval coeffs3 3 z2)))
(p1 (* z (+ z2 3.0)))
(p0 z)
(y p5))
(set! y (+ (/ y b) p4))
(set! y (+ (/ y b) p3))
(set! y (+ (/ y b) p2))
(set! y (+ (/ y b) p1))
(set! y (+ (/ y b) p0))
(if (< t 0.0)
(* y -1.0)
y)))
(define (t-distribution-cdf x nu)
(let ((x2 (* x x)))
(cond ((and (> nu 30.0)
(< x2 (* 10.0 nu)))
(let ((u (cornish-fisher x nu)))
(unit-gaussian-cdf u)))
((< x2 nu)
(let* ((u (/ x2 nu))
(eps (/ u (+ 1.0 u))))
(if (>= x 0.0)
(beta-inc-axpy 0.5 0.5 0.5 (/ nu 2.0) eps)
(beta-inc-axpy -0.5 0.5 0.5 (/ nu 2.0) eps))))
(else
(let* ((v (/ nu (* x x)))
(eps (/ v (+ 1.0 v))))
(if (>= x 0.0)
(beta-inc-axpy -0.5 1.0 (/ nu 2.0) 0.5 eps)
(beta-inc-axpy 0.5 0.0 (/ nu 2.0) 0.5 eps)))))))
)