#lang racket/base
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; oneg-t-student-en.rkt (rev 0.02 2024.07.03 english)
;;; Development: Enrique Comer-Barragan
;;;              Lab. CEMATi (cemati (dot) org)
;;;              TecNM/ITTijuana
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ▼For usage examples please see line 139
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Purpose: basic implementation
;;;   of the t-Student distribution
;;;   with functions for: pdf, cdf & inv-cdf.
;;; Expected usage:
;;;   mainly academic and educational
;;;   (for the current implementation).
;;; Observation: the code is not optimal
;;;              with respect of time or memory,
;;;              and currently is untyped.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; License: Copyright 2024 Enrique Comer-Barragan

;;;   Licensed under the Apache License, Version 2.0
;;;   (the "License"); you may not use this file except
;;;   in compliance with the License.

;;;   You may obtain a copy of the License at
;;;       (http:) //www.apache.org/licenses/LICENSE-2.0

;;;   Unless required by applicable law or agreed to
;;;   in writing, software distributed under the
;;;   License is distributed on an "AS IS" BASIS,
;;;   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
;;;   either express or implied.

;;;   See the License for the specific language
;;;   governing permissions and limitations under
;;;   the License.
;;;;;;;;;;;;;;;;;
  
(require math)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; auxiliary local functions
(define (beta-inc-reg a b x)
  (/ (beta-inc a b x)
     (beta a b)))

;; secant method (elemental)
;; Notice:
;; in the context of the expected
;; applications, we assume convergence.
;; That is: we applied to monotonous
;; increasing functions, of the cdf type,
;; in another context, the return
;; value may be #f.
(define (secant-method f x0 x1 epsi n
                   #:debug? (debug? #f))
  (define-values (f0 f1)
    (values (f x0) (f x1)))
  (define x2
    (if (not (= f0 f1))
        (- x1 (* f1 (/ (- x1 x0)
                       (- f1 f0))))
        #f))
  (when debug?
    (printf "[~a: ~a, ~a; ~a (~a : ~a)]~n"
            n x0 x1 x2 f0 f1))
  (cond
    ((not x2) #f) ; division by zero
    ((< (abs (- x2 x1)) epsi)
     x2)
    ((<= n 0) #f) ; non convergence
    (else (secant-method f x1 x2 epsi
                     (sub1 n)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; function for (centered) t-Student

;; probability density function
;; with ν degrees of freedom
(define (make-pdf-ts ν)
  (λ(t)
    (* (/ (* (sqrt ν)
             (beta 1/2 (/ ν 2))))
       (expt
        (+ 1 (/ (sqr t) ν))
        (* -1/2 (+ ν 1))))))

;; cumulative distribution function
;; with ν degrees of freedom
(define (make-cdf-ts ν)
  (define (cdf-ts t)
    (cond
      ((< t 0)
       (- 1 (cdf-ts (- t))))
      ((= t 0) 0.5)
      (else
       (- 1
          (* 1/2
             (beta-inc-reg
              (/ ν 2)
              1/2
              (/ ν
                 (+ (sqr t)
                    ν))))))))
  cdf-ts)

;; inverse cdf with ν degrees of freedom
;; notice that we use epsilon=1e-12
;;        and maximum number of
;;        iterations in the secant-method
;;        of 30.
(define (make-inv-cdf-ts ν)
  (λ(r)
    (define (f x)
      (- ((make-cdf-ts ν) x) r))
    (secant-method f 1/2 1/3 1e-12 30)))

;; pdf with parameter ν
;; for t as main argument
(define (pdf-ts v t)
  ((make-pdf-ts v) t))

;; cdf with parameter ν
;; for t as main argument
(define (cdf-ts v t)
  ((make-cdf-ts v) t))

;; inv-cdf with parameter ν
;; for r as main argument
;; note: r should satisfy
;;       0 < r < 1
(define (inv-cdf-ts v r)
  ((make-inv-cdf-ts v) r))

;;;;;;;;;;;;;;;;;;;;;;;;
;;; ▼Replace or modify the
;;;   example function call in
;;;   line 163, or write from line 162 on
;;;   the function(s) that you want
;;;   to evaluate in order.
;;;   (after that, you can press
;;;   the [RUN] button, in order to
;;;   evaluate your function(s).
;;;
;;; For example: the following three
;;;   lines:
;;;   (pdf-ts 21 2) ; f_{21}(2)
;;;   (cdf-ts 21 2) ; F_{21}(2)
;;;   (inv-cdf-ts 21 0.7) ; Fi_{21}(0.7)
;;; Return the numbers:
;;;   0.057918057638886446
;;;   0.9706999939756457 
;;;   0.5324551470144957
;;; corresponding to the
;;; pdf, cdf, inv-cdf of the t-Student
;;; distribution with 21
;;; degrees of freedom.
;;;;;;;;;;;;;;;;;;;;;;

(inv-cdf-ts 21 0.7) 
by