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

/** default constructor */
AdvLorentzianConv::AdvLorentzianConv() : AdvFuncBase(*(new std::string("lorentzianConv")), *(new std::string("lc")), NumberOfParamForLorentzianConv) {
}

AdvLorentzianConv::AdvLorentzianConv(const AdvLorentzianConv &obj) {
}

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

#ifdef USE_POINTER

Double AdvLorentzianConv::LorentzFunc(const Double x, const Double *p) {
    return p[0] / ( M_PI * p[2] * ( ( ( x-p[1] ) /p[2] )^2 + 1) );
}

Double AdvLorentzianConv::LorentzConvFunc(const Double x, const Double *p) {

    if(ResY.size()==0) ReadResolutionFile();
    Double normFactor = 0.0;
    Double conv_result = 0.0;
    for (UInt4 i=0;i<ResX.size()-1;i++)
        {
        Double f_res = ResY.at(i);
        Double f_det = LorentzFunc(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 AdvLorentzianConv::eval(const Double x, const Double *p) {
    return LorentzConvFunc(x, *p);
}

#ifdef HAVE_DIFFERENTIAL_MEMBER

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

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

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

#ifdef USE_VECTOR

Double AdvLorentzianConv::LorentzFunc(const Double x, const std::vector<Double> &p) {
    return p.at(0) / ( M_PI * p.at(2) * ( ( ( x-p.at(1) ) /p.at(2) )* ( ( x-p.at(1) ) /p.at(2) )+ 1) );
}

// Convolution debug function

Double AdvLorentzianConv::LorentzConvFuncTest(const Double x, const std::vector<Double> &p) {

    //define parameter for resolution function
    Double initial = -100.0;
    Double final = 100.0;
    Double delta = 1.0;
    Double a = 0.07;

    std::vector <Double> ResY = ResolTestYbin(initial,final,delta,a);
    std::vector <Double> ResX = ResolTestXbin(initial,final,delta);

    Double normFactor = 0.0;
    Double conv_result = 0.0;
    for (UInt4 i=0;i<ResX.size()-1;i++)
        {
        Double f_res = ResY.at(i);
        Double f_det = LorentzFunc(x-ResX.at(i), p);
        conv_result += f_det*f_res;
        normFactor += ResY.at(i);
        }
     return conv_result/normFactor;

}
// Convolution debug function --end

Double AdvLorentzianConv::LorentzConvFunc(const Double x, const std::vector<Double> &p) {

    if(ResY.size()==0) ReadResolutionFile();
    Double normFactor = 0.0;
    Double conv_result = 0.0;
    for (UInt4 i=0;i<ResX.size()-1;i++)
        {
        Double f_res = ResY.at(i);
        Double f_det = LorentzFunc(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 AdvLorentzianConv::eval(const Double x, const std::vector<Double> &p) {
        //return LorentzConvFuncTest(x, p);
        return LorentzConvFunc(x, p);
}

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

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

}

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