#include <cassert>
#include <cmath>
#include <numeric>
#include <cfloat>
/* gnu scientific library */
#include "gsl/gsl_sf_gamma.h"
#include "gsl/gsl_sf_psi.h"
/* Manyo lib */
#include "AdvAugmentedLorentzianConv.hh"

/**
 *  constructor
 */
AdvAugmentedLorentzianConv::AdvAugmentedLorentzianConv() : AdvFuncBase(string("augmented lorentzian conv"), string("alc"), NumberOfParamsForAugmentedLorentzianConv) {
}

/**
 *  constructor
 */
AdvAugmentedLorentzianConv::~AdvAugmentedLorentzianConv() {
}

/**
 *  normarisation factor of the function
 *
 *  \param[in] p the power number
 *
 *  the value of the numarisation factor is the inverse number of Beta function B(p-0.5, 0.5).
 *  the Beta function can be represented using the Ganna Function; B(p, q) = G(p)G(q)/G(p+q).
 */
Double AdvAugmentedLorentzianConv::N(const Double p) {
    return gsl_sf_gamma(p)/(gsl_sf_gamma(p-0.5)*gsl_sf_gamma(0.5));
}

// /**
//  *  denominator of the function
//  *
//  *  \param[in] x  argument
//  *  \param[in] c  the center of the function
//  *  \param[in] w  the width of the function
//  *  \param[in] p  the power number of the 
//  *  \param[in] m  the order of diffrentiation
//  */
// Double AdvAugmentedLorentzianConv::denominator(const Double x, const Double c, const Double w, const Double p, const Double m) {
//     assert( w != 0.0 );
//     Double t=(x-c)/w;
//     return pow(w, 1.0+m)*pow(t*t + 1.0, p+m);
// }

/**
 *  evaluate the value of the function
 *
 *  \param[in] x  argument
 *  \param[in] h  the height of the function
 *  \param[in] c  the center of the function
 *  \param[in] w  the width of the function
 *  \param[in] p  the power number of the 
 */
Double AdvAugmentedLorentzianConv::AugmentedLorentzianFunc(const Double x, const Double h, const Double c, const Double w, const Double p) {
    assert( w != 0.0 );
    //return h*N(p)/denominator(x, c, w, p, 0.0);
    Double t=(x-c)/w;
    return h*N(p)/(w*pow(t*t+1.0, p));
}

Double AdvAugmentedLorentzianConv::AugmentedLorentzianConvFunc(const Double x,  const Double h, const Double c, const Double w, const Double p) {

    if(ResY.size()==0) ReadResolutionFile();
    Double normFactor = 0.0;
    Double conv_result = 0.0;
    for (int i=0;i<ResX.size()-1;i++)
        {
        Double f_res = ResY.at(i);
        Double f_det = AugmentedLorentzianFunc(x-(ResX.at(i)+ResX.at(i+1))*0.5, h,c,w,p);
        conv_result += f_det*f_res;
        normFactor += ResY.at(i);
        }
     return conv_result/normFactor;
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
/**
 *  evaluate the value of the 1st. differentiation coefficient of the function, dL/dx
 *
 *  \param[in] x  argument
 *  \param[in] h  the height of the function
 *  \param[in] c  the center of the function
 *  \param[in] w  the width of the function
 *  \param[in] p  the power number of the 
 */
/*
Double AdvAugmentedLorentzianConv::der1st(const Double x, const Double h, const Double c, const Double w, const Double p) {
    assert( w != 0.0 );
    Double t=(x-c)/w;
    //return -2.0*h*p*N(p)*t*denominator(x, c, w, p, 2.0);
    return -2.0*((p-1.0/2.0)/w)*t*(this->eval(x, h, c, w, p+1.0));
}
*/
/**
 *  evaluate the value of the 2nd. differentiation coefficient of the function, d^2L/dx^2
 *
 *  \param[in] x  argument
 *  \param[in] h  the height of the function
 *  \param[in] c  the center of the function
 *  \param[in] w  the HWHM of the function
 *  \param[in] p  the power number of the 
 */
/*
Double AdvAugmentedLorentzianConv::der2nd(const Double x, const Double h, const Double c, const Double w, const Double p) {
    assert( w != 0.0 );
    Double t=(x-c)/w;
    //return 2.0*h*p*N(p)*((2.0*p+1.0)*t*t-1.0)*denominator(x, c, w, p, 2.0);
    return 2.0*((p-1.0/2.0)*(p+1.0-1.0/2.0)/(p+1.0))/(w*w)*((2.0*p+1.0)*t*t-1.0)*(this->eval(x, h, c, w, p+2.0));
}
*/
/**
 *  evaluate the value of the differentiation coefficient for w, dL/dw
 *
 *  \param[in] x  argument
 *  \param[in] h  the height of the function
 *  \param[in] c  the center of the function
 *  \param[in] w  the HWHM of the function
 *  \param[in] p  the power number of the 
 */
/*
Double AdvAugmentedLorentzianConv::derW(const Double x, const Double h, const Double c, const Double w, const Double p) {
    assert( w != 0.0 );
    Double t=(x-c)/w;
    //return N(p)*h*((2.0*p-1.0)*t*t - 1.0)/denominator(x, c, w, p, 1.0);
    return ((p-1.0/2.0)/p)*((1.0-t*t)/w)*(this->eval(x, h, c, w, p));
}
*/
/**
 *  evaluate the value of the differentiation coefficient for p, dL/dp
 *
 *  \param[in] x argument
 *  \param[in] h the height of the function
 *  \param[in] c the center of the function
 *  \param[in] w the HWHM of the function
 *  \param[in] p the power number of the 
 */
/*
Double AdvAugmentedLorentzianConv::derP(const Double x, const Double h, const Double c, const Double w, const Double p) {
    assert( w != 0.0 );
    Double t=(x-c)/w;
    return eval(x, h, c, w, p)*(gsl_sf_psi(p)-gsl_sf_psi(p-0.5)-log(t*t+1.0));
}
*/
#endif // HAVE_DIFFERENTIAL_MEMBER

#ifdef USE_POINTER
Double AdvAugmentedLorentzianConv::eval(const Double x, const Double *p) {
    return AugmentedLorentzianConvFunc(x, p[0], p[1], p[2], p[3]);
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
Double AdvAugmentedLorentzianConv::der1st(const Double x, const Double *p) {
    std::cout << "Convolution::der1st is not implemented" << endl;
    return DBL_MAX;
}

Double AdvAugmentedLorentzianConv::der2nd(const Double x, const Double *p) {
    std::cout << "Convolution::der2nd is not implemented" << endl;
    return DBL_MAX;
}

Double *AdvAugmentedLorentzianConv::gradient(const Double x, const Double *p) {
//    std::cout << "Convolution::gradient is not implemented" << endl;
    return NULL;
}
#endif // HAVE_DIFFERENTIAL_MEMBER
#endif // USE_POINTER

#ifdef USE_VECTOR
Double AdvAugmentedLorentzianConv::eval(const Double x, const vector<Double> &p) {
    return AugmentedLorentzianConvFunc(x, p[0], p[1], p[2], p[3]);
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
Double AdvAugmentedLorentzianConv::der1st(const Double x, const vector<Double> &p) {
    std::cout << "Convolution::der1st is not implemented" << endl;
    return DBL_MAX;
}

Double AdvAugmentedLorentzianConv::der2nd(const Double x, const vector<Double> &p) {
    std::cout << "Convolution::der2nd is not implemented" << endl;
    return DBL_MAX;
}

vector<Double> AdvAugmentedLorentzianConv::gradient(const Double x, const vector<Double> &p) {
//    std::cout << "Convolution::gradient is not implemented" << endl;
    vector<Double> v;
    v.clear();
    return v;
}
#endif // HAVE_DIFFERENTIAL_MEMBER
#endif // USE_VECTOR
