{-# LANGUAGE DeriveDataTypeable #-}
-- |
-- Module    : Statistics.Distribution.StudentT
-- Copyright : (c) 2011 Aleksey Khudyakov
-- License   : BSD3
--
-- Maintainer  : bos@serpentine.com
-- Stability   : experimental
-- Portability : portable
--
-- Student-T distribution
module Statistics.Distribution.StudentT (
    StudentT
  , studentT
  , studentTndf
  ) where

import qualified Statistics.Distribution as D
import Data.Typeable         (Typeable)
import Numeric.SpecFunctions (logBeta, incompleteBeta, invIncompleteBeta)



-- | Student-T distribution
newtype StudentT = StudentT { studentTndf :: Double }
                   deriving (Eq,Show,Read,Typeable)


-- | Create Student-T distribution. Number of parameters must be positive.
studentT :: Double -> StudentT
studentT ndf
  | ndf > 0   = StudentT ndf
  | otherwise =
    error "Statistics.Distribution.StudentT.studentT: non-positive number of degrees of freedom"

instance D.Distribution StudentT where
  cumulative = cumulative 

instance D.ContDistr StudentT where
  density  = density
  quantile = quantile
  
cumulative :: StudentT -> Double -> Double
cumulative (StudentT ndf) x
  | x > 0     = 1 - 0.5 * ibeta
  | otherwise = 0.5 * ibeta
  where
    ibeta = incompleteBeta (0.5 * ndf) 0.5 (ndf / (ndf + x*x))

density :: StudentT -> Double -> Double
density (StudentT ndf) x =
    exp( log (ndf / (ndf + x*x)) * (0.5 * (1 + ndf)) - logBeta 0.5 (0.5 * ndf) ) / sqrt ndf

quantile :: StudentT -> Double -> Double
quantile (StudentT ndf) p
  | p >= 0 && p <= 1 = 
    let x = invIncompleteBeta (0.5 * ndf) 0.5 (2 * min p (1 - p))
    in case sqrt $ ndf * (1 - x) / x of
         r | p < 0.5   -> -r
           | otherwise -> r 
  | otherwise =
    error $ "Statistics.Distribution.Uniform.quantile: p must be in [0,1] range. Got: "++show p


instance D.MaybeMean StudentT where
  maybeMean (StudentT ndf) | ndf > 1   = Just 0
                           | otherwise = Nothing

instance D.MaybeVariance StudentT where
  maybeStdDev (StudentT ndf) | ndf > 2   = Just $ ndf / (ndf - 2)
                             | otherwise = Nothing

instance D.ContGen StudentT where
  genContVar = D.genContinous
