    {-# LANGUAGE
        MultiParamTypeClasses, FlexibleContexts
      #-}
    module Data.Random.Distribution where

    import Data.Random.Lift
    import Data.Random.RVar

    -- |A 'Distribution' is a data representation of a random variable's probability
    -- structure.  For example, in "Data.Random.Distribution.Normal", the 'Normal'
    -- distribution is defined as:
    --
    -- > data Normal a
    -- >     = StdNormal
    -- >     | Normal a a
    --
    -- Where the two parameters of the 'Normal' data constructor are the mean and
    -- standard deviation of the random variable, respectively.  To make use of
    -- the 'Normal' type, one can convert it to an 'rvar' and manipulate it or
    -- sample it directly:
    --
    -- > x <- sample (rvar (Normal 10 2))
    -- > x <- sample (Normal 10 2)
    --
    -- A 'Distribution' is typically more transparent than an 'RVar'
    -- but less composable (precisely because of that transparency).  There are
    -- several practical uses for types implementing 'Distribution':
    --
    -- * Typically, a 'Distribution' will expose several parameters of a standard
    -- mathematical model of a probability distribution, such as mean and std deviation for
    -- the normal distribution.  Thus, they can be manipulated analytically using
    -- mathematical insights about the distributions they represent.  For example,
    -- a collection of bernoulli variables could be simplified into a (hopefully) smaller
    -- collection of binomial variables.
    --
    -- * Because they are generally just containers for parameters, they can be
    -- easily serialized to persistent storage or read from user-supplied
    -- configurations (eg, initialization data for a simulation).
    --
    -- * If a type additionally implements the 'CDF' subclass, which extends
    -- 'Distribution' with a cumulative density function, an arbitrary random
    -- variable 'x' can be tested against the distribution by testing
    -- @fmap (cdf dist) x@ for uniformity.
    --
    -- On the other hand, most 'Distribution's will not be closed under all the
    -- same operations as 'RVar' (which, being a monad, has a fully turing-complete
    -- internal computational model).  The sum of two uniformly-distributed
    -- variables, for example, is not uniformly distributed.  To support general
    -- composition, the 'Distribution' class defines a function 'rvar' to
    -- construct the more-abstract and more-composable 'RVar' representation
    -- of a random variable.
    class Distribution d t where
        -- |Return a random variable with this distribution.
        rvar :: d t -> RVar t
        rvar = d t -> RVar t
    forall (n :: * -> *). d t -> RVarT n t
    forall (d :: * -> *) t (n :: * -> *).
    Distribution d t =>
    d t -> RVarT n t
    rvarT

        -- |Return a random variable with the given distribution, pre-lifted to an arbitrary 'RVarT'.
        -- Any arbitrary 'RVar' can also be converted to an 'RVarT m' for an arbitrary 'm', using
        -- either 'lift' or 'sample'.
        rvarT :: d t -> RVarT n t
        rvarT d t
    d = RVar t -> RVarT n t
    forall a. RVarT Identity a -> RVarT n a
    forall (m :: * -> *) (n :: * -> *) a. Lift m n => m a -> n a
    lift (d t -> RVar t
    forall (d :: * -> *) t. Distribution d t => d t -> RVar t
    rvar d t
    d)

    -- FIXME: I am not sure about giving default instances
    class Distribution d t => PDF d t where
        pdf :: d t -> t -> Double
        pdf d t
    d = Double -> Double
    forall a. Floating a => a -> a
    exp (Double -> Double) -> (t -> Double) -> t -> Double
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . d t -> t -> Double
    forall (d :: * -> *) t. PDF d t => d t -> t -> Double
    logPdf d t
    d
        logPdf :: d t -> t -> Double
        logPdf d t
    d = Double -> Double
    forall a. Floating a => a -> a
    log (Double -> Double) -> (t -> Double) -> t -> Double
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . d t -> t -> Double
    forall (d :: * -> *) t. PDF d t => d t -> t -> Double
    pdf d t
    d

    class Distribution d t => CDF d t where
        -- |Return the cumulative distribution function of this distribution.
        -- That is, a function taking @x :: t@ to the probability that the next
        -- sample will return a value less than or equal to x, according to some
        -- order or partial order (not necessarily an obvious one).
        --
        -- In the case where 't' is an instance of Ord, 'cdf' should correspond
        -- to the CDF with respect to that order.
        --
        -- In other cases, 'cdf' is only required to satisfy the following law:
        -- @fmap (cdf d) (rvar d)@
        -- must be uniformly distributed over (0,1).  Inclusion of either endpoint is optional,
        -- though the preferred range is (0,1].
        --
        -- Note that this definition requires that  'cdf' for a product type
        -- should _not_ be a joint CDF as commonly defined, as that definition
        -- violates both conditions.
        -- Instead, it should be a univariate CDF over the product type.  That is,
        -- it should represent the CDF with respect to the lexicographic order
        -- of the product.
        --
        -- The present specification is probably only really useful for testing
        -- conformance of a variable to its target distribution, and I am open to
        -- suggestions for more-useful specifications (especially with regard to
        -- the interaction with product types).
        cdf :: d t -> t -> Double