#' Logarithm of the sum of two exponentials
#' 
#' `log_add_exp` computes the logarithm of the sum
#' of the exponential of two value.
#' 
#' \deqn{log\_add\_exp(a,b) = \log(e^a + e^b)}{%
#'       log_add_exp(a,b) = log(exp(a) + exp(b))}
#' 
#' @param a first logarithmic values to add 
#' @param b second logarithmic values to add 
#' 
#' @return a numeric vector containing the logarithm of the sum
#' of the exponential of `a` and `b`.
#'
#' @author Eric Coissac
#' @export
log_add_exp = function(a, b) {
     a + log1p(exp(b - a))
}

#' Logarithm of the difference of two exponentials
#' 
#' `log_add_exp` computes the logarithm of the difference
#' of the exponential of two value.
#' 
#' \deqn{log\_add\_exp(a,b) = \log(e^a - e^b)}{%
#'       log_add_exp(a,b) = log(exp(a) - exp(b))}
#' 
#' @param a first logarithmic values to add 
#' @param b second logarithmic values to add 
#' 
#' @return a numeric vector containing the logarithm of the 
#' difference of the exponential of `a` and `b`.
#'
#' @author Eric Coissac
#' @export
log_diff_exp = function(a, b) {
    a + log(1 - exp(b - a))
}

#' Logarithm of the sum of exponentials
#' 
#' `log_sum_exp` computes the logarithm of the sum
#' of exponential of values.
#' 
#' \deqn{log\_add\_exp(x_1,x_2,\ldots) = \log(e^{x_1} + e^{x_2} + \ldots)}{%
#'       log_add_exp(x_1,x_2,\ldots) = log(exp(x_1) + exp(x_2) + \ldots)}
#' 
#' @param x logarithmic values to sum 
#' 
#' @return a numeric value containing the logarithm of the 
#' sum of the exponential of the values of `x`.
#'
#' @author Eric Coissac
#' @export
log_sum_exp = function(x) Reduce(log_add_exp, x)

#' Density probability function of a multinomial distribution for nucleotides
#' 
#' Computes the probalility to observe a given number of `A`, `C`, `G`, `T`
#' when probalilities of each nucleotide are `pA`, `pC`, `pG`, `pT`.
#' 
#' @param A numeric; number of observed *A* at a given site.
#' @param C numeric; number of observed *C* at a given site.
#' @param G numeric; number of observed *G* at a given site.
#' @param T numeric; number of observed *T* at a given site.
#' @param pA numeric; probability to observed a *A* at a given site.
#' @param pC numeric; probability to observed a *C* at a given site.
#' @param pG numeric; probability to observed a *G* at a given site.
#' @param pT numeric; probability to observed a *T* at a given site.
#' @param log logical; if TRUE, probabilities p are given as log(p).
#' 
#' @return numeric; the density of probability of the log that probability
#'         of the observations given the probabilities of observation
#'         
#' @examples
#'   dmultinomialACGT(c(3,4),c(3,6),c(3,2),c(3,5),
#'                    0.25,0.25,0.25,0.25)
#'   dmultinomialACGT(c(3,4),c(3,6),c(3,2),c(3,5),
#'                    pA = 0.25, pC = 0.25, pG = 0.25, pT = 0.25,
#'                    log=TRUE)
#' 
#' @author Eric Coissac
#' @export
dmultinomialACGT = function(A,C,G,T,pA,pC,pG,pT,log=FALSE) {

   lpcomb =(  log(pA) * A 
            + log(pC) * C 
            + log(pG) * G 
            + log(pT) * T)
    
   N = A + C + G + T
   
   tetranome = (lgamma(N+1) - lgamma(A+1) 
                            - lgamma(C+1) 
                            - lgamma(G+1) 
                            - lgamma(T+1))

   lp = lpcomb + tetranome
   
   if (log) lp else exp(lp)
}
