/*
    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 <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include "DLTmath.h"
#include "statistics.h"
#include "lognormal_dist.h"

double
lognormal_dev(const double zeta, const double sigma, const gsl_rng *r2)
{
    double u, v, rr2, normal, z;

    do
    {
        /* choose x,y in uniform square (-1,-1) to (+1,+1) */
        u = -1.0 + 2.0 * gsl_rng_uniform(r2);
        v = -1.0 + 2.0 * gsl_rng_uniform(r2);

        /* see if it is in the unit circle */
        rr2 = u*u + v*v;
    }
    while (rr2 > 1.0 || rr2 == 0.0);

    normal = u * sqrt(-2.0 * log(rr2) / rr2);

    z = exp(sigma * normal + zeta);

    return (z);
}


double
lognormal_pdf(const double x, const double zeta, const double sigma)
{
    double          u;

    if (x <= 0.0)
    {
        return (0.0);
    }
    else
    {
        u = (log(x) - zeta) / sigma;

        return (exp(-0.5*u*u) / (x * fabs(sigma) * sqrt(2.0 * MY_PI)));
    }
}


double
lognormal_lnpdf(const double x, const double zeta, const double sigma)
{
    double          u, p;

    u = (log(x) - zeta) / sigma;
    p = -log(x * fabs(sigma)) - (log(2.0 * MY_PI) / 2.0) - (mysquare(u) / 2.0);

    return (p);
}


double
lognormal_cdf(const double x, const double zeta, const double sigma)
{
    if (x <= 0.0)
        return (0.0);
    else
        return (0.5 * (1.0 + erf((log(x) - zeta) / (sigma * sqrt(2.0)))));
}


double
lognormal_sdf(const double x, const double zeta, const double sigma)
{
    if (x <= 0.0)
        return (1.0);
    else
        return (1.0 - lognormal_cdf(x, zeta, sigma));
}


double
lognormal_int(const double x, const double y, const double zeta, const double sigma)
{
    if (x <= 0.0)
        return (lognormal_cdf(y, zeta, sigma));
    else
        return (lognormal_cdf(y, zeta, sigma) - lognormal_cdf(x, zeta, sigma));
}


/* correct */
double
lognormal_logL(const double zeta, const double sigma)
{
    return (-(zeta + 0.5 * log(2.0 * MY_PI * MY_E * mysquare(sigma))));
}


/* fit a lognormal distribution by maximum likelihood */
double
lognormal_fit(const double *data, const int num, double *zeta, double *sigma, double *prob)
{
    double          ave, avesqr, var, x;
    int             i;

    ave = avesqr = 0.0;
    for (i = 0; i < num; ++i)
    {
        if (data[i] == 0.0)
            continue;
        else if (data[i] < 0.0)
        {
            fprintf(stderr, "\n ERROR345: lognormal data must be > 0.0 ");
            return(-1.0);
        }
        else
        {
            x = log(data[i]);
            ave += x;
            avesqr += (x*x);
        }
    }
    ave /= (double) num;
    avesqr /= (double) num;

    /* m = exp(ave); */ /* this is the scale parameter */

    var = 0.0;
    for (i = 0; i < num; ++i)
    {
        if (data[i] == 0.0)
            var += mysquare(ave);
        else
            var += mysquare(log(data[i]) - ave);
    }
    var /= (double) num - 1;

    *zeta = ave;
    *sigma = sqrt(var);

    return(chi_sqr_adapt(data, num, 0, prob, *zeta, *sigma, lognormal_pdf, lognormal_lnpdf, lognormal_int));
}
