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

double
pareto_dev(const double a, const double c, const gsl_rng *r2)
{
    double         logdum;

    logdum = log(a) - (1.0 / c) * log(1.0 - gsl_rng_uniform(r2));

    return (exp(logdum));
}


double
pareto_pdf(const double x, const double a, const double c)
{
    if (x < c)
        return (0.0);
    else 
        return(exp(log(c) + c * log(a) - (c+1) * log(x)));
}


double
pareto_lnpdf(const double x, const double a, const double c)
{
    if (x == 0.0)
        return(log(c) + c * log(a));
    else 
        return(log(c) + c * log(a) - (c+1) * log(x));
}


double
pareto_cdf(const double x, const double a, const double c)
{
    if (x < a)
        return(0.0);
    else
        return(1.0 - pareto_sdf(x, a, c));
}


double
pareto_sdf(const double x, const double a, const double c)
{
    if (x <= a)
        return(1.0);
    else if (a == 0.0)
        return(0.0);
    else if (c == 0.0)
        return(1.0);
    else
        return(exp(c * log(a / x)));
}


double
pareto_int(const double x, const double y, const double a, const double c)
{
    return (pareto_sdf(x, a, c) - pareto_sdf(y, a, c));
}


double
pareto_logL(const double a, const double c)
{
    return (-log(a / c) - 1.0 - 1.0 / c);
}


/* Maximum likelihood fit to a pareto distribution
   Based on _Statistical Distributions_ 3rd ed. Evans, Hastings, and Peacock, p 154. */
double
pareto_fit(const double *data, const int num, double *a, double *c, double *prob)
{
    double          invc, min = DBL_MAX;
    int             i;

    for (i = 0; i < num; ++i)
        if(data[i] < min)
            min = data[i];

    if (min <= 0.0)
    {
        fprintf(stderr, "\n ERROR345: min data = %e; Pareto distributed data must be > 0.0 ", min);
        return(-1.0);
    }

    *a = min;

    invc = 0.0;
    for (i = 0; i < num; ++i)
        invc += log(data[i] / min);
    invc /= (double) num;

    *c = 1.0 / invc;

    return(chi_sqr_adapt(data, num, 0, prob, *a, *c, pareto_pdf, pareto_lnpdf, pareto_int));
}
