#include "AdvPseudoVoigt2.hh"

/** default constructor */
AdvPseudoVoigt2::AdvPseudoVoigt2() : AdvFuncBase(*(new string("pseudo_voigt_2")), *(new string("pv2")), NumberOfParamForPseudoVoigt2) {
    this->gaussian   = *(new AdvGaussian()  );
    this->lorentzian = *(new AdvLorentzian());
}

/** copy constructor */
AdvPseudoVoigt2::AdvPseudoVoigt2(const AdvPseudoVoigt2 &obj) {
    this->gaussian   = *(new AdvGaussian(obj.gaussian)   );
    this->lorentzian = *(new AdvLorentzian(obj.lorentzian));
}

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

/** assignment operator */
//AdvPseudoVoigt2 AdvPseudoVoigt2::operator=(const AdvPseudoVoigt2 &obj) {
//    this->gaussian   = *(new Gaussian(obj.gaussian)    );
//    this->lorentzian = *(new Lorentzian(obj.lorentzian));
//    return *this;
//}

/**
 *  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] s the HWHM of the gaussian   part, sigma
 *  \param[in] g the HWHM of the lorentzian part, gamma
 *  \param[in] m the linear cobination cefficient for gaussian and lorentzian
 */
Double AdvPseudoVoigt2::eval(const Double x, const Double h, const Double c, const Double s, const Double g, const Double m) {
    return m*(this->gaussian.eval(x, h, c, s)) + (1.0-m)*(this->lorentzian.eval(x, h, c, g));
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
/**
 *  evaluate the value of the 1st diffrentiation coefficient at x
 *
 *  \param[in] x argument
 *  \param[in] h the height of the function
 *  \param[in] c the center of the function
 *  \param[in] s the HWHM of the gaussian   part, sigma
 *  \param[in] g the HWHM of the lorentzian part, gamma
 *  \param[in] m the linear cobination cefficient for gaussian and lorentzian
 */
Double AdvPseudoVoigt2::der1st(const Double x, const Double h, const Double c, const Double s, const Double g, const Double m) {
    return m*(this->gaussian.der1st(x, h, c, s)) + (1.0-m)*(this->lorentzian.der1st(x, h, c, g));
}

/**
 *  evaluate the value of the 2nd diffrentiation coefficient at x
 *
 *  \param[in] x argument
 *  \param[in] h the height of the function
 *  \param[in] c the center of the function
 *  \param[in] s the HWHM of the gaussian   part, sigma
 *  \param[in] g the HWHM of the lorentzian part, gamma
 *  \param[in] m the linear cobination cefficient for gaussian and lorentzian
 */
Double AdvPseudoVoigt2::der2nd(const Double x, const Double h, const Double c, const Double s, const Double g, const Double m) {
    return m*(this->gaussian.der2nd(x, h, c, s)) + (1.0-m)*(this->lorentzian.der2nd(x, h, c, g));
}
#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 AdvPseudoVoigt2::eval(const Double x, const Double *p) {
    return this->eval(x, p[0], p[1], p[2], p[3], p[4]);
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
/**
 *  evaluate the value of the 1st. differential coefficient
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 */
Double AdvPseudoVoigt2::der1st(const Double x, const Double *p) {
    return this->der1st(x, p[0], p[1], p[2], p[3], p[4]);
}

/**
 *  evaluate the value of the 2nd. differential coefficient
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 */
Double AdvPseudoVoigt2::der2nd(const Double x, const Double *p) {
    return p[4]*gaussian.der2nd(x, p[0], p[1], p[2]) + (1.0-p[4])*lorentzian.der2nd(x, p[0], p[1], p[3]);
}

/**
 *  evaluate the gradient of the coefficient for parameters
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 */
Double *AdvPseudoVoigt2::gradient(const Double x, const Double *p) {

    Double *retval=new Double[NumberOfParamForPseudoVoigt2];
    retval[0]=        this->eval(x, 1.0,  p[1], p[2], p[3], p[4]);
    retval[1]=-1.0*(this->der1st(x, 1.0,  p[1], p[2], p[3], p[4]));
    retval[2]=        p[4]*(this->gaussian.eval(x, p[0], p[1], p[2]));
    retval[3]=(1.0-p[4])*(this->lorentzian.eval(x, p[0], p[1], p[3]));
    retval[4]=(this->gaussian.eval(x, p[0], p[1], p[2])) - (this->lorentzian.eval(x, p[0], p[1], p[3]));

    return retval;
}
#endif // HAVE_DIFFERENTIAL_MEMBER
#endif // USE_POINTER

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

#ifdef HAVE_DIFFERENTIAL_MEMBER
Double AdvPseudoVoigt2::der1st(const Double x, const vector<Double> &p) {
    return this->der1st(x, p[0], p[1], p[2], p[3], p[4]);
}

Double AdvPseudoVoigt2::der2nd(const Double x, const vector<Double> &p) {
    return p[4]*gaussian.der2nd(x, p[0], p[1], p[2]) + (1.0-p[4])*lorentzian.der2nd(x, p[0], p[1], p[3]);
}

vector<Double> AdvPseudoVoigt2::gradient(const Double x, const vector<Double> &p) {

    vector<Double> *retval=new vector<Double>();
    retval->push_back(                       this->eval(x, 1.0,  p[1], p[2], p[3], p[4]) );
    retval->push_back(               -1.0*(this->der1st(x, 1.0,  p[1], p[2], p[3], p[4])) );
    retval->push_back(        p[4]*(this->gaussian.derW(x, p[0], p[1], p[2])) );
    retval->push_back((1.0-p[4])*(this->lorentzian.derW(x, p[0], p[1], p[3])) );
    retval->push_back((this->gaussian.eval(x, p[0], p[1], p[2])) - (this->lorentzian.eval(x, p[0], p[1], p[3])) );

    return *retval;
}
#endif // HAVE_DIFFERENTIAL_MEMBER
#endif // USE_VECTOR

#include "AdvLorentzian.hh"
