#include "AdvDampedHarmonicOscillatorConv.hh"
#include <cassert>
#include <cmath>
#include <numeric>
#include <cfloat>

const Double AdvDampedHarmonicOscillatorConv::N=4.0/abs(atan2(0.0, -1.0));  // 4.0/PI=4.0/abs(atan2(0.0, -1.0)

AdvDampedHarmonicOscillatorConv::AdvDampedHarmonicOscillatorConv() : AdvFuncBase(std::string("Damped Harmonic Oscillator Conv"), std::string("dhoc"), NumberOfParamsForDampedHarmonicOscillatorConv) {
}

AdvDampedHarmonicOscillatorConv::~AdvDampedHarmonicOscillatorConv() {
}

/**
 *  evaluate the value of the function
 *
 *  \param[in] x
 *  \param[in] h
 *  \param[in] w0
 *  \param[in] g
 */
Double AdvDampedHarmonicOscillatorConv::DampedHarmonicOscillatorFunc(const Double x, const Double h, const Double w0, const Double g) {

    assert(abs(w0) > abs(g));

    Double xSq=x*x;
    Double w0Sq=w0*w0;
    Double gSq=g*g;
    Double t=w0Sq-xSq;
    Double u=w0Sq-gSq;
    Double numerator  = h*sqrt(u)*g*x;
    Double denominator = t*t + 4.0*gSq*xSq;
    return N*numerator/denominator;
}

Double AdvDampedHarmonicOscillatorConv::DampedHarmonicOscillatorConvFunc(const Double x, const Double h, const Double w0, const Double g) {

    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 = DampedHarmonicOscillatorFunc(x-(ResX.at(i)+ResX.at(i+1))*0.5, h,w0,g);
        conv_result += f_det*f_res;
        normFactor += ResY.at(i);
        }
     return conv_result/normFactor;
}


#ifdef HAVE_DIFFERENTIAL_MEMBER

/*
Double AdvDampedHarmonicOscillatorConv::der1st(const Double x, const Double h, const Double w0, const Double g) {
    assert(abs(w0) > abs(g));

    Double xSq=x*x;
    Double w0Sq=w0*w0;
    Double gSq=g*g;
    Double t=w0Sq-xSq;
    Double numerator  = h*g*sqrt(w0Sq-gSq)*(t*(w0Sq+3.0*xSq)-4.0*gSq*xSq);
    Double denominator = pow(t*t + 4.0*gSq*xSq, 2.0);
    return N*numerator/denominator;
}

Double AdvDampedHarmonicOscillatorConv::der2nd(const Double x, const Double h, const Double w0, const Double g) {
    assert(abs(w0) > abs(g));

    Double xSq=x*x;
    Double w0Sq=w0*w0;
    Double gSq=g*g;
    Double t=w0Sq-xSq;
    Double u=w0Sq-gSq;
    Double numerator   =h*g*x*sqrt(u)*(3.0*t*(w0Sq+xSq)*(t-2.0*gSq)-8.0*gSq*xSq*(u));
    Double denominator = pow(t*t + 4.0*gSq*xSq, 3.0);
    return N*numerator/denominator;
}

Double AdvDampedHarmonicOscillatorConv::derW0(const Double x, const Double h, const Double w0, const Double g) {
    assert(abs(w0) > abs(g));

    Double xSq=x*x;
    Double w0Sq=w0*w0;
    Double gSq=g*g;
    Double t=w0Sq-xSq;
    Double u=w0Sq-gSq;
    Double numerator   = h*g*w0*x*(t*(3.0*w0Sq+xSq)-4.0*gSq*xSq);
    Double denominator = sqrt(u)*pow(t*t+4.0*gSq*xSq, 2.0);
    return N*numerator/denominator;
}

Double AdvDampedHarmonicOscillatorConv::derG(const Double x, const Double h, const Double w0, const Double g) {
    assert(abs(w0) > abs(g));

    Double xSq=x*x;
    Double w0Sq=w0*w0;
    Double gSq=g*g;
    Double t=w0Sq-xSq;
    Double u=w0Sq-gSq;
    Double numerator   = h*x*t*(w0Sq*t-2.0*gSq*(w0Sq+xSq));
    Double denominator = sqrt(u)*pow(t*t+4.0*gSq*xSq, 2.0);
    return N*numerator/denominator;
}
*/
#endif

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

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

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

Double *AdvDampedHarmonicOscillatorConv::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 AdvDampedHarmonicOscillatorConv::eval(const Double x, const std::vector<Double> &p) {
    return DampedHarmonicOscillatorConvFunc(x, p[0], p[1], p[2]);
}

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

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

std::vector<Double> AdvDampedHarmonicOscillatorConv::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 // USE_VECTOR
#endif // HAVE_DIFFERENTIAL_MEMBER
