/*
    Theseus - maximum likelihood superpositioning of macromolecular structures

    Copyright (C) 2004-2015 Douglas L. Theobald

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the:

    Free Software Foundation, Inc.,
    59 Temple Place, Suite 330,
    Boston, MA  02111-1307  USA

    -/_|:|_|_\-
*/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <float.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include "DLTmath.h"
#include "statistics.h"
#include "invgauss_dist.h"
#include "recinvgauss_dist.h"

/* The Reciprocal Inverse Gaussian distribution of is defined by:

   p(x) dx = sqrt(lambda/(2 PI x)) e^{-(lambda x/2) + (lambda/mu) - (lambda/(2 x mu^2))} dx

   0 < x < +inf
   lambda > 0
   mu > 0
*/


/* returns a random variate from the Reciprocal Inverse Gaussian */
double
recinvgauss_dev(const double mu, const double lambda, const gsl_rng *r2)
{
    return(1.0 / invgauss_dev(mu, lambda, r2));
}


double
recinvgauss_pdf(const double x, const double mu, const double lambda)
{
    double          prob;

    if (x == 0.0)
        return(0.0);

    prob = sqrt(0.5 * lambda / (MY_PI * x)) *
           exp((lambda / mu) - (0.5 * lambda * x) - (0.5 * lambda / (mu * mu * x)));

    return(prob);
}


double
recinvgauss_lnpdf(const double x, const double mu, const double lambda)
{
    double           prob =
    (lambda / mu) - (0.5 * lambda * x) - (0.5 * lambda / (mu * mu * x))
    + 0.5 * (log(0.5 * lambda / (MY_PI * x)));

    return(prob);
}


double
recinvgauss_cdf(const double x, const double mu, const double lambda)
{
    return(0.0);
}


double
recinvgauss_sdf(const double x, const double mu, const double lambda)
{
    return(1.0 - recinvgauss_cdf(x, mu, lambda));
}


double
recinvgauss_int(const double x, const double y, const double mu, const double lambda)
{
    return(integrate_romberg(recinvgauss_pdf, mu, lambda, x, y));
}


/* Maximum likelihood fit to the reciprocal inverse gaussian distribution.
   My own derivation:
       mu = \Sum(1/x_i) / N
       lambda = N / ( \Sum{x_i + 1/(x_i mu^2)} - (2 N / mu))
*/
double
recinvgauss_fit(const double *data, const int num, double *mu, double *lambda, double *logL)
{
    double          sum_mu, sum_lambda;
    const double    numd = (const double) num;
    int             i, nbins = 0;

    sum_mu = 0.0;
    for (i = 0; i < num; ++i)
    {
        if (data[i] <= 0.0)
        {
            fprintf(stderr, "\n ERROR234: reciprocal inverse gaussian distributed data must be > 0 ");
            return(-1.0);
        }
        else
        {
            sum_mu += 1.0 / data[i];
        }
    }

    *mu = sum_mu / numd;

    sum_lambda = 0.0;
    for (i = 0; i < num; ++i)
    {
        sum_lambda += (data[i] + 1.0 / (*mu * *mu * data[i]));
    }

    *lambda = numd / (sum_lambda - 2.0 * numd / *mu);

    return(chi_sqr_adapt(data, num, nbins, logL, *mu, *lambda, recinvgauss_pdf, recinvgauss_lnpdf, recinvgauss_int));
}
