#include "AdvErrorFunction.hh"

/** */
const Double AdvErrorFunction::A=sqrt(log(2.0));
/** normalisation factor */
const Double AdvErrorFunction::N=2.0/sqrt(abs(atan2(-1.0, 1.0)));

/** default constructor */
AdvErrorFunction::AdvErrorFunction() : AdvFuncBase(*(new std::string("AdvErrorFunction")), *(new std::string("erf")), NumberOfParamForErrorFunction) {
}

/** copy constructor */
AdvErrorFunction::AdvErrorFunction(const AdvErrorFunction &) {
    this->gaussian = new AdvGaussian();
}

/** destructor */
AdvErrorFunction::~AdvErrorFunction() {
    delete this->gaussian;
}

/** evaluate the value of the function.
 *
 * \param[in] x argument
 * \param[in] h the height (amplitude) of the function
 * \param[in] c the center of the function
 * \param[in] w the NMHW of the gaussian.
 *
 *  erf(x; h, c, w)=h*erf(A*(x-c)/w; 1.0, 0.0, 1.0);, A=sqrt(log(2.0));
 *
 *  erf(-x+c; h, c, w)=-erf(x+c; h, c, w);
 *
 *  erf( inf.; 1.0, 0.0, 1.0)= 1.0
 *  erf(-inf.; 1.0, 0.0, 1.0)=-1.0
 */
Double AdvErrorFunction::eval(const Double x, const Double h, const Double c, const Double w) {
    assert(w != 0.0);
    return h*gsl_sf_erf(A*(x-c)/w);
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
Double AdvErrorFunction::der1st(const Double x, const Double h, const Double c, const Double w) {
    assert(w != 0.0);
    return N*(A/w)*(this->gaussian->eval(x, h, c, w));
}

Double AdvErrorFunction::der2nd(const Double x, const Double h, const Double c, const Double w) {
    assert(w != 0.0);
    return -2.0*N*(A/w)*(A/w)*(this->gaussian->der1st(x, h, c, w));
}

Double AdvErrorFunction::derH(const Double x, const Double h, const Double c, const Double w) {
    assert(w != 0.0);
    return this->eval(x, 1.0, c, w);
}

Double AdvErrorFunction::derC(const Double x, const Double h, const Double c, const Double w) {
    assert(w != 0.0);
    return -this->der1st(x, h, c, w);
}

Double AdvErrorFunction::derW(const Double x, const Double h, const Double c, const Double w) {
    assert(w != 0.0);
    return (this->der2nd(x, h, c, w))*w/(2.0*A*A);
}
#endif // HAVE_DIFFERENTIAL_MEMBER


#ifdef USE_VECTOR
Double AdvErrorFunction::eval(const Double x, const std::vector<Double> &p) {
    assert(p.at(2) != 0.0);
    return this->eval(x, p.at(0), p.at(1), p.at(2));
}

#ifdef HAVE_DIFFERENTIAL_MEMBER
Double AdvErrorFunction::der1st(const Double x, const std::vector<Double> &p) {
    assert(p.at(2) != 0.0);
    return this->der1st(x, p.at(0), p.at(1), p.at(2));
}

Double AdvErrorFunction::der2nd(const Double x, const std::vector<Double> &p) {
    assert(p.at(2) != 0.0);
    return this->der2nd(x, p.at(0), p.at(1), p.at(2));
}

std::vector<Double> AdvErrorFunction::gradient(const Double x, const std::vector<Double> &p) {
    assert(p.at(2) != 0.0);
    std::vector<Double> *retval=new std::vector<Double>(NumberOfParamForErrorFunction);
    retval->at(0)=this->derH(x, p[0], p[1], p[2]);
    retval->at(1)=this->derC(x, p[0], p[1], p[2]);
    retval->at(2)=this->derW(x, p[0], p[1], p[2]);
    return *retval;
}
#endif // HAVE_DIFFERENTIAL_MEMBER
#endif // USE_VECTOR
