#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cmath>
#include "calnum.h"
using namespace std;
double gammln(double);
void gser(double &, double, double, double &);

template < class T >
inline string convierteAString(T i)
{
    ostringstream ostr;

    ostr << i << ends;
    return ostr.str();
}


double
gammln(double xx)
{
    double x, y, tmp, ser;
    static double cof[6] = {
        76.18009172947146, -86.50532032941677,
        24.01409824083091, -1.231739572450155,
        0.1208650973866179e-2, -0.5395239384953e-5
    };
    int j;

    y = x = xx;
    tmp = x + 5.5;
    tmp -= (x + 0.5) * log(tmp);
    ser = 1.000000000190015;
    for (j = 0; j <= 5; j++)
        ser += cof[j] / ++y;
    return -tmp + log(2.5066282746310005 * ser / x);
}


void
gser(double &gamser, double a, double x, double &gln)
{
    int n;
    double sum, del, ap;

    gln = gammln(a);
    if (x <= 0.0) {
        if (x < 0.0)
            cout << "x <0 en gser" << endl;
        gamser = 0.0;
        return;
    }
    else {
        ap = a;
        del = sum = 1.0 / a;
        for (n = 1; n <= ITMAX; n++) {
            ++ap;
            del *= x / ap;
            sum += del;
            if (fabs(del) < fabs(sum) * EPS) {
                gamser = sum * exp(-x + a * log(x) - (gln));
                return;
            }
        }
        cout << "ITMAX demasiado pequeo en gser" << endl;
        return;
    }
}


void
gcf(double &gammcf, double a, double x, double &gln)
{
    int i;
    double an, b, c, d, del, h;

    gln = gammln(a);
    b = x + 1.0 - a;
    c = 1.0 / FPMIN;
    d = 1.0 / b;
    h = d;
    for (i = 1; i <= ITMAX; i++) {
        an = -i * (i - a);
        b += 2.0;
        d = an * d + b;
        if (fabs(d) < FPMIN)
            d = FPMIN;
        c = b + an / c;
        if (fabs(c) < FPMIN)
            c = FPMIN;
        d = 1.0 / d;
        del = d * c;
        h *= del;
        if (fabs(del - 1.0) < EPS)
            break;
    }
    if (i > ITMAX)
        cout << "ITMAX demasiado pequeo en gcf" << endl;
    gammcf = exp(-x + a * log(x) - (gln)) * h;
}


double
GammaP(double a, double x)
{
    double gamser, gammcf, gln;

    if (x < 0.0 || a <= 0.0)
        cout << "Argumento invalido en gammp" << endl;
    if (x < (a + 1.0)) {
        gser(gamser, a, x, gln);
        return gamser;
    }
    else {
        gcf(gammcf, a, x, gln);
        return 1.0 - gammcf;
    }
}


double
Chi2(double x, double nu)
{
    if (x <= 0)
        cout << "chi2<= 0  en Chi2" << endl;
    if (nu <= 0)
        cout << "nu <= 0 en Chi2" << endl;

    double a = nu / 2.;
    double lnf = (a - 1) * log(x) - a * LN2 - gammln(a) - 0.5 * x;

    return exp(lnf);
}


//F(chi02, nu) = P(nu / 2, x / 2)
// Donde P(a, x)
//es la complementaria de la funcion gamma incompleta

double Chi2F(double x, double nu)
{
    if (x <= 0)
        cout << "chi2<= 0  en Chi2" << endl;
    if (nu <= 0)
        cout << "nu <= 0 en Chi2" << endl;

    return GammaP(nu / 2., x / 2.);
}


double
Chi2P(double x, double nu)
{
    if (x <= 0)
        cout << "chi2 <= 0  en Chi2" << endl;
    if (nu <= 0)
        cout << "nu <= 0 en Chi2" << endl;

    return 1. - Chi2F(x, nu);
}
