#include "AdvGaussianConv.hh"
#include <cassert>
#include <iostream>
#include <ostream>
#include <cmath>
#include <numeric>
#include <cfloat>

//#define USE_NORMARIZED_FORM

const Double AdvGaussianConv::LOG2=log(2.0);
/** constant for normarize factor : sqry(log(2.0)/pi) */
const Double AdvGaussianConv::NC=sqrt(LOG2/fabs(atan2(0.0, -1.0)));

/** default constructor */
AdvGaussianConv::AdvGaussianConv() : AdvFuncBase(*(new string("gaussianConv")), *(new string("gc")), NumberOfParamForGaussianConv) {
}

/** copy constructor */
AdvGaussianConv::AdvGaussianConv(const AdvGaussianConv &) {
}

/** destructor */
AdvGaussianConv::~AdvGaussianConv() {
}

/**
 *  evaluate the value of the function
 *
 *  \param[in] x
 *  \param[in] h the height of the function
 *  \param[in] c the center of the function
 *  \param[in] w the half-value half width of the function
 *
 */


#ifdef HAVE_DIFFERENTIAL_MEMBER
/**
 *  evaluate the value of the 1st. differential coefficient
 */
Double AdvGaussianConv::der1st(const Double x, const Double h, const Double c, const Double w) {
    std::cout << "Convolution::der1st is not implemented" << endl;
    return DBL_MAX;
}

/*
 *  evaluate the value of the 2nd. differential coefficient
 */
Double AdvGaussianConv::der2nd(const Double x, const Double h, const Double c, const Double w) {
    std::cout << "Convolution::der2nd is not implemented" << endl;
    return DBL_MAX;
}

/**
 *  d
 *  --G(x, h, c, w)
 *  dx
 */
Double AdvGaussianConv::derW(const Double x, const Double h, const Double c, const Double w) {
    std::cout << "Convolution::derW is not implemented" << endl;
    return DBL_MAX;
}

#endif // HAVE_DIFFERENTIAL_MEMBER

#ifdef USE_POINTER
/**
 *  evaluate the value of the function
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 */

Double AdvGaussianConv::GaussianFunc(const Double x, const Double *p) {
    Double t=(x-p[1])/p[2];
#ifdef USE_NORMARIZED_FORM
    return p[0]*exp(-LOG2*t*t)*NC/p[2]; // nomarized form
#else
    return p[0]*exp(-LOG2*t*t);
#endif
}

Double AdvGaussianConv::GaussianConvFunc(const Double x, 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 = GaussianFunc(x-(ResX.at(i)+ResX.at(i+1))*0.5, *p);
        conv_result += f_det*f_res;
        normFactor += ResY.at(i);
        }
     return conv_result/normFactor;
}

Double AdvGaussianConv::eval(const Double x, const Double *p) {
    return GaussianConvFunc(x, *p);
}

/**
 *  evaluate the value of the 1st. differential coefficient
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 */
Double AdvGaussianConv::der1st(const Double x, const Double *p) {
    std::cout << "Convolution::der1st is not implemented" << endl;
    return DBL_MAX;
}

/**
 *  evaluate the value of the 2nd. differential coefficient
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 */
Double AdvGaussianConv::der2nd(const Double x, const Double *p) {
    std::cout << "Convolution::der2nd is not implemented" << endl;
    return DBL_MAX;
}

/**
 *  evaluate the gradient of the coefficient for parameters
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 *
 */
Double *AdvGaussianConv::gradient(const Double x, const Double *p) {
//    std::cout << "Convolution::gradient is not implemented" << endl;
    return NULL;
}

#endif // USE_POINTER

#ifdef USE_VECTOR
/**
 *  evaluate the value of the function
 *
 *  \param[in] x  argument of the function
 *  \param[in] p  parameters of the function
 */

Double AdvGaussianConv::GaussianFunc(const Double x, const vector<Double> &p) {
    Double t=(x-p.at(1))/p.at(2);
#ifdef USE_NORMARIZED_FORM
    return p.at(0)*exp(-LOG2*t*t)*NC/p.at(2); // nomarized form
#else
    return p.at(0)*exp(-LOG2*t*t);
#endif
}

Double AdvGaussianConv::GaussianConvFunc(const Double x, const vector<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 = GaussianFunc(x-(ResX.at(i)+ResX.at(i+1))*0.5, p);
        conv_result += f_det*f_res;
        normFactor += ResY.at(i);
        }
     return conv_result/normFactor;
}

Double AdvGaussianConv::eval(const Double x, const vector<Double> &p) {
        return GaussianConvFunc(x, p);
}

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

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

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