from __future__ import division

import numpy as np

from stats_arrays.errors import InvalidParamsError
from stats_arrays.distributions.base import UncertaintyBase


class StudentsTUncertainty(UncertaintyBase):
    """
    The Student's T uncertainty distribution probability density function is a function of :math:`\\nu`, the degrees of freedom:

    .. math:: f(x; \\nu) = \\frac{\\Gamma(\\frac{\\nu+1}{2})} {\\sqrt{\\nu\\pi},\\Gamma(\\frac{\\nu}{2})} \\left(1+\\frac{x^2}{\\nu} \\right)^{-\\frac{\\nu+1}{2}}

    A non-standardized distribution, with a location and scale parameter, is also possible, through the transformation:

    .. math:: X = \\mu + \\sigma f

    In our implementation, the location parameter :math:`\\mu` is ``location``, the scale parameter :math:`\\sigma` is ``scale``, and :math:`\\nu` (the degrees of freedom)  is ``shape``.

    See http://en.wikipedia.org/wiki/Student%27s_t-distribution
    """

    id = 12
    description = "Student's T uncertainty"

    @classmethod
    def validate(cls, params):
        if (params["shape"] <= 0).sum() or np.isnan(params["shape"]).sum():
            raise InvalidParamsError(
                "Positive ``nu`` (degrees of freedom) values are required for"
                " Student's T."
            )
        if (params["scale"] <= 0).sum():
            raise InvalidParamsError(
                "Scale values, if specified, must be greater than zero for"
                " Student's T."
            )

    @classmethod
    def random_variables(cls, params, size, seeded_random=None, **kwargs):
        if seeded_random is None:
            seeded_random = np.random
        scale = params["scale"].copy()
        scale[np.isnan(scale)] = 1
        location = params["loc"].copy()
        location[np.isnan(location)] = 0
        data = (
            location
            + scale
            * seeded_random.standard_t(params["shape"], size=(size, params.shape[0])).T
        )
        return data
