// This file is part of fityk program. Copyright (C) Marcin Wojdyr
// Licence: GNU General Public License version 2
// $Id: bfunc.cpp 279 2007-04-07 23:16:35Z wojdyr $
/// Built-in function definitions

#include "bfunc.h"
#include "voigt.h"
#include "numfuncs.h"

using namespace std;


#define FUNC_CALCULATE_VALUE_BEGIN(NAME) \
void Func##NAME::calculate_value(vector<fp> const &xx, vector<fp> &yy) const\
{\
    int first, last; \
    get_nonzero_idx_range(xx, first, last); \
    for (int i = first; i < last; ++i) {\
        fp x = xx[i]; 


#define FUNC_CALCULATE_VALUE_END(VAL) \
        yy[i] += (VAL);\
    }\
}

#define FUNC_CALCULATE_VALUE(NAME, VAL) \
    FUNC_CALCULATE_VALUE_BEGIN(NAME) \
    FUNC_CALCULATE_VALUE_END(VAL)


#define PUT_DERIVATIVES_AND_VALUE(VAL) \
        if (!in_dx) { \
            yy[i] += (VAL); \
            for (vector<Multi>::const_iterator j = multi.begin(); \
                    j != multi.end(); ++j) \
                dy_da[dyn*i+j->p] += dy_dv[j->n] * j->mult;\
            dy_da[dyn*i+dyn-1] += dy_dx;\
        }\
        else {  \
            for (vector<Multi>::const_iterator j = multi.begin(); \
                    j != multi.end(); ++j) \
                dy_da[dyn*i+j->p] += dy_da[dyn*i+dyn-1] * dy_dv[j->n]*j->mult;\
        }


#define FUNC_CALCULATE_VALUE_DERIV_BEGIN(NAME) \
void Func##NAME::calculate_value_deriv(vector<fp> const &xx, \
                                         vector<fp> &yy, \
                                         vector<fp> &dy_da, \
                                         bool in_dx) const \
{ \
    int first, last; \
    get_nonzero_idx_range(xx, first, last); \
    int dyn = dy_da.size() / xx.size(); \
    vector<fp> dy_dv(nv); \
    for (int i = first; i < last; ++i) { \
        fp x = xx[i]; \
        fp dy_dx;


#define FUNC_CALCULATE_VALUE_DERIV_END(VAL) \
        PUT_DERIVATIVES_AND_VALUE(VAL)  \
    } \
}




///////////////////////////////////////////////////////////////////////

const char *FuncConstant::formula 
= "Constant(a=avgy) = a"; 

void FuncConstant::calculate_value(vector<fp> const&/*xx*/, vector<fp>&yy) const
{
    for (vector<fp>::iterator i = yy.begin(); i != yy.end(); ++i)
        *i += vv[0];
}

void FuncConstant::calculate_value_deriv(vector<fp> const &xx, 
                                         vector<fp> &yy, 
                                         vector<fp> &dy_da,
                                         bool in_dx) const
{
    // dy_da.size() == xx.size() * (parameters.size()+1)
    int dyn = dy_da.size() / xx.size();
    vector<fp> dy_dv(nv);
    for (int i = 0; i < size(yy); ++i) {
        dy_dv[0] = 1.;
        fp dy_dx = 0;
        PUT_DERIVATIVES_AND_VALUE(vv[0]);
    }
}

///////////////////////////////////////////////////////////////////////

const char *FuncLinear::formula 
= "Linear(a0=intercept,a1=slope) = a0 + a1 * x"; 


FUNC_CALCULATE_VALUE(Linear, vv[0] + x*vv[1])

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Linear)
    dy_dv[0] = 1.;
    dy_dv[1] = x;
    dy_dx = vv[1];
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] + x*vv[1])

///////////////////////////////////////////////////////////////////////

const char *FuncQuadratic::formula 
= "Quadratic(a0=avgy, a1=0, a2=0) = a0 + a1*x + a2*x^2"; 


FUNC_CALCULATE_VALUE(Quadratic, vv[0] + x*vv[1] + x*x*vv[2])

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Quadratic)
    dy_dv[0] = 1.;
    dy_dv[1] = x;
    dy_dv[2] = x*x;
    dy_dx = vv[1] + 2*x*vv[2];
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] + x*vv[1] + x*x*vv[2])

///////////////////////////////////////////////////////////////////////

const char *FuncCubic::formula 
= "Cubic(a0=avgy, a1=0, a2=0, a3=0) = a0 + a1*x + a2*x^2 + a3*x^3"; 


FUNC_CALCULATE_VALUE(Cubic, vv[0] + x*vv[1] + x*x*vv[2] + x*x*x*vv[3])

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Cubic)
    dy_dv[0] = 1.;
    dy_dv[1] = x;
    dy_dv[2] = x*x;
    dy_dv[3] = x*x*x;
    dy_dx = vv[1] + 2*x*vv[2] + 3*x*x*vv[3];
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] + x*vv[1] + x*x*vv[2] + x*x*x*vv[3])

///////////////////////////////////////////////////////////////////////

const char *FuncPolynomial4::formula 
= "Polynomial4(a0=avgy, a1=0, a2=0, a3=0, a4=0) = "
                                 "a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4"; 


FUNC_CALCULATE_VALUE(Polynomial4, vv[0] + x*vv[1] + x*x*vv[2] 
                                         + x*x*x*vv[3] + x*x*x*x*vv[4])

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Polynomial4)
    dy_dv[0] = 1.;
    dy_dv[1] = x;
    dy_dv[2] = x*x;
    dy_dv[3] = x*x*x;
    dy_dv[4] = x*x*x*x;
    dy_dx = vv[1] + 2*x*vv[2] + 3*x*x*vv[3] + 4*x*x*x*vv[4];
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] + x*vv[1] + x*x*vv[2] + x*x*x*vv[3]
                                      + x*x*x*x*vv[4])

///////////////////////////////////////////////////////////////////////

const char *FuncPolynomial5::formula 
= "Polynomial5(a0=avgy, a1=0, a2=0, a3=0, a4=0, a5=0) = "
                             "a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4 + a5*x^5"; 


FUNC_CALCULATE_VALUE(Polynomial5, vv[0] + x*vv[1] + x*x*vv[2] 
                               + x*x*x*vv[3] + x*x*x*x*vv[4] + x*x*x*x*x*vv[5])

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Polynomial5)
    dy_dv[0] = 1.;
    dy_dv[1] = x;
    dy_dv[2] = x*x;
    dy_dv[3] = x*x*x;
    dy_dv[4] = x*x*x*x;
    dy_dv[5] = x*x*x*x*x;
    dy_dx = vv[1] + 2*x*vv[2] + 3*x*x*vv[3] + 4*x*x*x*vv[4] + 5*x*x*x*x*vv[5];
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] + x*vv[1] + x*x*vv[2] 
                               + x*x*x*vv[3] + x*x*x*x*vv[4] + x*x*x*x*x*vv[5])

///////////////////////////////////////////////////////////////////////

const char *FuncPolynomial6::formula 
= "Polynomial6(a0=avgy, a1=0, a2=0, a3=0, a4=0, a5=0, a6=0) = "
                     "a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4 + a5*x^5 + a6*x^6"; 


FUNC_CALCULATE_VALUE(Polynomial6, vv[0] + x*vv[1] + x*x*vv[2] 
                               + x*x*x*vv[3] + x*x*x*x*vv[4] + x*x*x*x*x*vv[5]
                               + x*x*x*x*x*x*vv[6])

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Polynomial6)
    dy_dv[0] = 1.;
    dy_dv[1] = x;
    dy_dv[2] = x*x;
    dy_dv[3] = x*x*x;
    dy_dv[4] = x*x*x*x;
    dy_dv[5] = x*x*x*x*x;
    dy_dv[6] = x*x*x*x*x*x;
    dy_dx = vv[1] + 2*x*vv[2] + 3*x*x*vv[3] + 4*x*x*x*vv[4] + 5*x*x*x*x*vv[5]
            + 6*x*x*x*x*x*vv[6];
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] + x*vv[1] + x*x*vv[2] 
                               + x*x*x*vv[3] + x*x*x*x*vv[4] + x*x*x*x*x*vv[5]
                               + x*x*x*x*x*x*vv[6])

///////////////////////////////////////////////////////////////////////

const char *FuncGaussian::formula 
= "Gaussian(height, center, hwhm) = "
               "height*exp(-ln(2)*((x-center)/hwhm)^2)"; 

void FuncGaussian::more_precomputations() 
{ 
    if (fabs(vv[2]) < epsilon) 
        vv[2] = epsilon; 
}

FUNC_CALCULATE_VALUE_BEGIN(Gaussian)
    fp xa1a2 = (x - vv[1]) / vv[2];
    fp ex = exp_ (- M_LN2 * xa1a2 * xa1a2);
FUNC_CALCULATE_VALUE_END(vv[0] * ex)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Gaussian)
    fp xa1a2 = (x - vv[1]) / vv[2];
    fp ex = exp_ (- M_LN2 * xa1a2 * xa1a2);
    dy_dv[0] = ex;
    fp dcenter = 2 * M_LN2 * vv[0] * ex * xa1a2 / vv[2];
    dy_dv[1] = dcenter;
    dy_dv[2] = dcenter * xa1a2;
    dy_dx = -dcenter;
FUNC_CALCULATE_VALUE_DERIV_END(vv[0]*ex)

bool FuncGaussian::get_nonzero_range (fp level, fp &left, fp &right) const
{  
    if (level == 0)
        return false;
    else if (fabs(level) >= fabs(vv[0]))
        left = right = 0;
    else {
        fp w = sqrt (log (fabs(vv[0]/level)) / M_LN2) * vv[2]; 
        left = vv[1] - w;             
        right = vv[1] + w;
    }
    return true;
}

///////////////////////////////////////////////////////////////////////

const char *FuncSplitGaussian::formula 
= "SplitGaussian(height, center, hwhm1=fwhm*0.5, hwhm2=fwhm*0.5) = "
                   "height*exp(-ln(2)*((x-center)/(x<center?hwhm1:hwhm2))^2)#"; 

void FuncSplitGaussian::more_precomputations() 
{ 
    if (fabs(vv[2]) < epsilon) 
        vv[2] = epsilon; 
    if (fabs(vv[3]) < epsilon) 
        vv[3] = epsilon; 
}

FUNC_CALCULATE_VALUE_BEGIN(SplitGaussian)
    fp hwhm = (x < vv[1] ? vv[2] : vv[3]);
    fp xa1a2 = (x - vv[1]) / hwhm;
    fp ex = exp_ (- M_LN2 * xa1a2 * xa1a2);
FUNC_CALCULATE_VALUE_END(vv[0] * ex)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(SplitGaussian)
    fp hwhm = (x < vv[1] ? vv[2] : vv[3]);
    fp xa1a2 = (x - vv[1]) / hwhm;
    fp ex = exp_ (- M_LN2 * xa1a2 * xa1a2);
    dy_dv[0] = ex;
    fp dcenter = 2 * M_LN2 * vv[0] * ex * xa1a2 / hwhm;
    dy_dv[1] = dcenter;
    if (x < vv[1]) {
        dy_dv[2] = dcenter * xa1a2;
        dy_dv[3] = 0;
    }
    else {
        dy_dv[2] = 0;
        dy_dv[3] = dcenter * xa1a2;
    }
    dy_dx = -dcenter;
FUNC_CALCULATE_VALUE_DERIV_END(vv[0]*ex)

bool FuncSplitGaussian::get_nonzero_range (fp level, fp &left, fp &right) const
{  
    if (level == 0)
        return false;
    else if (fabs(level) >= fabs(vv[0]))
        left = right = 0;
    else {
        fp w1 = sqrt (log (fabs(vv[0]/level)) / M_LN2) * vv[2]; 
        fp w2 = sqrt (log (fabs(vv[0]/level)) / M_LN2) * vv[3]; 
        left = vv[1] - w1;             
        right = vv[1] + w2;
    }
    return true;
}

///////////////////////////////////////////////////////////////////////

const char *FuncLorentzian::formula 
= "Lorentzian(height, center, hwhm) = "
                        "height/(1+((x-center)/hwhm)^2)"; 


void FuncLorentzian::more_precomputations()
{ 
    if (fabs(vv[2]) < epsilon) 
        vv[2] = epsilon; 
}

FUNC_CALCULATE_VALUE_BEGIN(Lorentzian)
    fp xa1a2 = (x - vv[1]) / vv[2];
    fp inv_denomin = 1. / (1 + xa1a2 * xa1a2);
FUNC_CALCULATE_VALUE_END(vv[0] * inv_denomin)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Lorentzian)
    fp xa1a2 = (x - vv[1]) / vv[2];
    fp inv_denomin = 1. / (1 + xa1a2 * xa1a2);
    dy_dv[0] = inv_denomin;
    fp dcenter = 2 * vv[0] * xa1a2 / vv[2] * inv_denomin * inv_denomin;
    dy_dv[1] = dcenter;
    dy_dv[2] = dcenter * xa1a2;
    dy_dx = -dcenter;
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] * inv_denomin)

bool FuncLorentzian::get_nonzero_range (fp level, fp &left, fp &right) const
{  
    if (level == 0)
        return false;
    else if (fabs(level) >= fabs(vv[0]))
        left = right = 0;
    else {
        fp w = sqrt (fabs(vv[0]/level) - 1) * vv[2];
        left = vv[1] - w;             
        right = vv[1] + w;
    }
    return true;
}

///////////////////////////////////////////////////////////////////////

const char *FuncPearson7::formula 
= "Pearson7(height, center, hwhm, shape=2) = "
                   "height/(1+((x-center)/hwhm)^2*(2^(1/shape)-1))^shape"; 

void FuncPearson7::more_precomputations()
{ 
    if (fabs(vv[2]) < epsilon) 
        vv[2] = epsilon; 
    if (vv.size() != 5)
        vv.resize(5);
    // not checking for vv[3]>0.5 nor even >0
    vv[4] = pow(2, 1. / vv[3]) - 1;
}

FUNC_CALCULATE_VALUE_BEGIN(Pearson7)
    fp xa1a2 = (x - vv[1]) / vv[2];
    fp xa1a2sq = xa1a2 * xa1a2;
    fp pow_2_1_a3_1 = vv[4]; //pow (2, 1. / a3) - 1;
    fp denom_base = 1 + xa1a2sq * pow_2_1_a3_1;
    fp inv_denomin = pow (denom_base, - vv[3]);
FUNC_CALCULATE_VALUE_END(vv[0] * inv_denomin)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Pearson7)
    fp xa1a2 = (x - vv[1]) / vv[2];
    fp xa1a2sq = xa1a2 * xa1a2;
    fp pow_2_1_a3_1 = vv[4]; //pow (2, 1. / a3) - 1;
    fp denom_base = 1 + xa1a2sq * pow_2_1_a3_1;
    fp inv_denomin = pow (denom_base, - vv[3]);
    dy_dv[0] = inv_denomin;
    fp dcenter = 2 * vv[0] * vv[3] * pow_2_1_a3_1 * xa1a2 * inv_denomin /
                                                      (denom_base * vv[2]);
    dy_dv[1] = dcenter;
    dy_dv[2] = dcenter * xa1a2;
    dy_dv[3] = vv[0] * inv_denomin * (M_LN2 * (pow_2_1_a3_1 + 1)
                       * xa1a2sq / (denom_base * vv[3]) - log(denom_base));
    dy_dx = -dcenter;
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] * inv_denomin)


bool FuncPearson7::get_nonzero_range (fp level, fp &left, fp &right) const
{  
    if (level == 0)
        return false;
    else if (fabs(level) >= fabs(vv[0]))
        left = right = 0;
    else {
        fp t = (pow(fabs(vv[0]/level), 1./vv[3]) - 1) / (pow (2, 1./vv[3]) - 1);
        fp w = sqrt(t) * vv[2];
        left = vv[1] - w;             
        right = vv[1] + w;
    }
    return true;
}

fp FuncPearson7::area() const
{
    if (vv[3] <= 0.5)
        return 0.;
    fp g = exp(lgammafn(vv[3] - 0.5) - lgammafn(vv[3]));
    return vv[0] * 2 * fabs(vv[2])
        * sqrt(M_PI) * g / (2 * sqrt (vv[4]));
    //in f_val_precomputations(): vv[4] = pow (2, 1. / a3) - 1;
}

///////////////////////////////////////////////////////////////////////

const char *FuncSplitPearson7::formula 
= "SplitPearson7(height, center, hwhm1=fwhm*0.5, hwhm2=fwhm*0.5, "
                                                        "shape1=2, shape2=2) = "
    "height/(1+((x-center)/(x<center?hwhm1:hwhm2))^2"
              "*(2^(1/(x<center?shape1:shape2))-1))^(x<center?shape1:shape2)#";

void FuncSplitPearson7::more_precomputations()
{ 
    if (fabs(vv[2]) < epsilon) 
        vv[2] = epsilon; 
    if (fabs(vv[3]) < epsilon) 
        vv[3] = epsilon; 
    if (vv.size() != 8)
        vv.resize(8);
    // not checking for vv[3]>0.5 nor even >0
    vv[6] = pow(2, 1. / vv[4]) - 1;
    vv[7] = pow(2, 1. / vv[5]) - 1;
}

FUNC_CALCULATE_VALUE_BEGIN(SplitPearson7)
    int lr = x < vv[1] ? 0 : 1;
    fp xa1a2 = (x - vv[1]) / vv[2+lr];
    fp xa1a2sq = xa1a2 * xa1a2;
    fp pow_2_1_a3_1 = vv[6+lr]; //pow(2, 1./shape) - 1;
    fp denom_base = 1 + xa1a2sq * pow_2_1_a3_1;
    fp inv_denomin = pow(denom_base, - vv[4+lr]);
FUNC_CALCULATE_VALUE_END(vv[0] * inv_denomin)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(SplitPearson7)
    int lr = x < vv[1] ? 0 : 1;
    fp hwhm = vv[2+lr];
    fp shape = vv[4+lr];
    fp xa1a2 = (x - vv[1]) / hwhm;
    fp xa1a2sq = xa1a2 * xa1a2;
    fp pow_2_1_a3_1 = vv[6+lr]; //pow(2, 1./shape) - 1;
    fp denom_base = 1 + xa1a2sq * pow_2_1_a3_1;
    fp inv_denomin = pow (denom_base, -shape);
    dy_dv[0] = inv_denomin;
    fp dcenter = 2 * vv[0] * shape * pow_2_1_a3_1 * xa1a2 * inv_denomin /
                                                      (denom_base * hwhm);
    dy_dv[1] = dcenter;
    dy_dv[2] = dy_dv[3] = dy_dv[4] = dy_dv[5] = 0;
    dy_dv[2+lr] = dcenter * xa1a2;
    dy_dv[4+lr] = vv[0] * inv_denomin * (M_LN2 * (pow_2_1_a3_1 + 1)
                           * xa1a2sq / (denom_base * shape) - log(denom_base));
    dy_dx = -dcenter;
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] * inv_denomin)


bool FuncSplitPearson7::get_nonzero_range (fp level, fp &left, fp &right) const
{  
    if (level == 0)
        return false;
    else if (fabs(level) >= fabs(vv[0]))
        left = right = 0;
    else {
        fp t1 = (pow(fabs(vv[0]/level), 1./vv[4]) - 1) / (pow(2, 1./vv[4]) - 1);
        fp w1 = sqrt(t1) * vv[2];
        fp t2 = (pow(fabs(vv[0]/level), 1./vv[5]) - 1) / (pow(2, 1./vv[5]) - 1);
        fp w2 = sqrt(t2) * vv[3];
        left = vv[1] - w1;             
        right = vv[1] + w2;
    }
    return true;
}

fp FuncSplitPearson7::area() const
{
    if (vv[4] <= 0.5 || vv[5] <= 0.5)
        return 0.;
    fp g1 = exp(lgammafn(vv[4] - 0.5) - lgammafn(vv[4]));
    fp g2 = exp(lgammafn(vv[5] - 0.5) - lgammafn(vv[5]));
    return vv[0] * fabs(vv[2]) * sqrt(M_PI) * g1 / (2 * sqrt (vv[6]))
         + vv[0] * fabs(vv[3]) * sqrt(M_PI) * g2 / (2 * sqrt (vv[7]));
}

///////////////////////////////////////////////////////////////////////

const char *FuncPseudoVoigt::formula 
= "PseudoVoigt(height, center, hwhm, shape=0.5) = "
                        "height*((1-shape)*exp(-ln(2)*((x-center)/hwhm)^2)"
                                 "+shape/(1+((x-center)/hwhm)^2))"; 

void FuncPseudoVoigt::more_precomputations() 
{ 
    if (fabs(vv[2]) < epsilon) 
        vv[2] = epsilon; 
}

FUNC_CALCULATE_VALUE_BEGIN(PseudoVoigt)
    fp xa1a2 = (x - vv[1]) / vv[2];
    fp ex = exp_ (- M_LN2 * xa1a2 * xa1a2);
    fp lor = 1. / (1 + xa1a2 * xa1a2);
    fp without_height =  (1-vv[3]) * ex + vv[3] * lor;
FUNC_CALCULATE_VALUE_END(vv[0] * without_height)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(PseudoVoigt)
    fp xa1a2 = (x - vv[1]) / vv[2];
    fp ex = exp_ (- M_LN2 * xa1a2 * xa1a2);
    fp lor = 1. / (1 + xa1a2 * xa1a2);
    fp without_height =  (1-vv[3]) * ex + vv[3] * lor;
    dy_dv[0] = without_height;
    fp dcenter = 2 * vv[0] * xa1a2 / vv[2]
                    * (vv[3]*lor*lor + (1-vv[3])*M_LN2*ex);
    dy_dv[1] = dcenter;
    dy_dv[2] = dcenter * xa1a2;
    dy_dv[3] =  vv[0] * (lor - ex);
    dy_dx = -dcenter;
FUNC_CALCULATE_VALUE_DERIV_END(vv[0] * without_height)

bool FuncPseudoVoigt::get_nonzero_range (fp level, fp &left, fp &right) const
{  
    if (level == 0)
        return false;
    else if (fabs(level) >= fabs(vv[0]))
        left = right = 0;
    else {
        // neglecting Gaussian part and adding 4.0 to compensate it
        fp w = (sqrt (vv[3] * fabs(vv[0]/level) - 1) + 4.) * vv[2];
        left = vv[1] - w;             
        right = vv[1] + w;
    }
    return true;
}

///////////////////////////////////////////////////////////////////////

const char *FuncVoigt::formula 
= "Voigt(height, center, gwidth=fwhm*0.4, shape=0.1) ="
                            " convolution of Gaussian and Lorentzian #";

void FuncVoigt::more_precomputations() 
{ 
    if (vv.size() != 6)
        vv.resize(6);
    float k, l, dkdx, dkdy;
    humdev(0, fabs(vv[3]), k, l, dkdx, dkdy);
    vv[4] = 1. / k;
    vv[5] = dkdy / k;

    if (fabs(vv[2]) < epsilon) 
        vv[2] = epsilon; 
}

FUNC_CALCULATE_VALUE_BEGIN(Voigt)
    // humdev/humlik routines require with y (a3 here) parameter >0.
    float k;
    fp xa1a2 = (x - vv[1]) / vv[2];
    k = humlik(xa1a2, fabs(vv[3]));
FUNC_CALCULATE_VALUE_END(vv[0] * vv[4] * k)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(Voigt)
    // humdev/humlik routines require with y (a3 here) parameter >0.
    // here fabs(vv[3]) is used, and dy_dv[3] is negated if vv[3]<0.
    float k;
    fp xa1a2 = (x-vv[1]) / vv[2];
    fp a0a4 = vv[0] * vv[4];
    float l, dkdx, dkdy;
    humdev(xa1a2, fabs(vv[3]), k, l, dkdx, dkdy);
    dy_dv[0] = vv[4] * k;
    fp dcenter = -a0a4 * dkdx / vv[2];
    dy_dv[1] = dcenter;
    dy_dv[2] = dcenter * xa1a2;
    dy_dv[3] = a0a4 * (dkdy - k * vv[5]);
    if (vv[3] < 0)
        dy_dv[3] = -dy_dv[3];
    dy_dx = -dcenter;
FUNC_CALCULATE_VALUE_DERIV_END(a0a4 * k)

bool FuncVoigt::get_nonzero_range (fp level, fp &left, fp &right) const
{  
    if (level == 0)
        return false;
    else if (fabs(level) >= fabs(vv[0]))
        left = right = 0;
    else {
        //TODO
        return false; 
    }
    return true;
}

///estimation according to
/// http://en.wikipedia.org/w/index.php?title=Voigt_profile&oldid=115518205
///
/// a2 = sqrt(2) * sigma
/// a3 = gamma / (sqrt(2) * sigma)
///  
/// sigma = a2 / sqrt(2) 
/// gamma = a2 * a3
/// 

fp FuncVoigt::fwhm() const 
{ 
    fp sigma = fabs(vv[2]) / M_SQRT2;
    fp gamma = fabs(vv[2]) * vv[3];

    fp fG = 2 * sigma * sqrt(2 * M_LN2);
    fp fL = 2 * gamma;

    fp fV = 0.5346 * fL + sqrt(0.2166 * fL * fL + fG * fG);
    return fV;
}

fp FuncVoigt::area() const
{
    return vv[0] * fabs(vv[2] * sqrt(M_PI) * vv[4]);
}
                                                            
vector<string> FuncVoigt::get_other_prop_names() const
{
    return vector2(string("GaussianFWHM"), string("LorentzianFWHM"));
}

fp FuncVoigt::other_prop(string const& name) const
{
    if (name == "GaussianFWHM") {
        fp sigma = fabs(vv[2]) / M_SQRT2;
        return 2 * sigma * sqrt(2 * M_LN2);
    }
    else if (name == "LorentzianFWHM") {
        fp gamma = fabs(vv[2]) * vv[3];
        return 2 * gamma;
    }
    else 
        return 0.;
}

///////////////////////////////////////////////////////////////////////

const char *FuncVoigtA::formula 
= "VoigtA(area, center, gwidth=fwhm*0.4, shape=0.1) = "
                            "convolution of Gaussian and Lorentzian #";

void FuncVoigtA::more_precomputations() 
{ 
    if (vv.size() != 6)
        vv.resize(6);
    vv[4] = 1. / humlik(0, fabs(vv[3]));

    if (fabs(vv[2]) < epsilon) 
        vv[2] = epsilon; 
}

FUNC_CALCULATE_VALUE_BEGIN(VoigtA)
    // humdev/humlik routines require with y (a3 here) parameter >0.
    float k;
    fp xa1a2 = (x - vv[1]) / vv[2];
    k = humlik(xa1a2, fabs(vv[3]));
FUNC_CALCULATE_VALUE_END(vv[0] / (sqrt(M_PI) * vv[2]) * k)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(VoigtA)
    // humdev/humlik routines require with y (a3 here) parameter >0.
    // here fabs(vv[3]) is used, and dy_dv[3] is negated if vv[3]<0.
    float k;
    fp xa1a2 = (x-vv[1]) / vv[2];
    fp f = vv[0] / (sqrt(M_PI) * vv[2]);
    float l, dkdx, dkdy;
    humdev(xa1a2, fabs(vv[3]), k, l, dkdx, dkdy);
    dy_dv[0] = k / (sqrt(M_PI) * vv[2]);
    fp dcenter = -f * dkdx / vv[2];
    dy_dv[1] = dcenter;
    dy_dv[2] = dcenter * xa1a2 - f * k / vv[2];
    dy_dv[3] = f * dkdy;
    if (vv[3] < 0)
        dy_dv[3] = -dy_dv[3];
    dy_dx = -dcenter;
FUNC_CALCULATE_VALUE_DERIV_END(f * k)

bool FuncVoigtA::get_nonzero_range (fp level, fp &left, fp &right) const
{  
    if (level == 0)
        return false;
    else if (fabs(level) >= fabs(vv[0]))
        left = right = 0;
    else {
        //TODO
        return false; 
    }
    return true;
}

///estimation according to
///http://en.wikipedia.org/w/index.php?title=Voigt_profile&oldid=29250968
fp FuncVoigtA::fwhm() const 
{ 
    fp gauss_fwhm = 2 * fabs(vv[2]);
    fp const c0 = 2.0056;
    fp const c1 = 1.0593;
    fp phi = vv[3];
    return gauss_fwhm * (1 - c0*c1 + sqrt(phi*phi + 2*c1*phi + c0*c0*c1*c1));
}

fp FuncVoigtA::height() const
{
    return vv[0] / fabs(vv[2] * sqrt(M_PI) * vv[4]);
}
                                                            

///////////////////////////////////////////////////////////////////////

const char *FuncEMG::formula 
= "EMG(a=height, b=center, c=fwhm*0.4, d=fwhm*0.04) ="
                " a*c*(2*pi)^0.5/(2*d) * exp((b-x)/d + c^2/(2*d^2))"
                " * (sign(d) - erf((b-x)/(2^0.5*c) + c/(2^0.5*d)))";

void FuncEMG::more_precomputations() 
{ 
}

bool FuncEMG::get_nonzero_range(fp/*level*/, fp&/*left*/, fp&/*right*/) const
    { return false; }

FUNC_CALCULATE_VALUE_BEGIN(EMG)
    fp a = vv[0];
    fp bx = vv[1] - x;
    fp c = vv[2];
    fp d = vv[3];
    fp fact = a*c*sqrt(2*M_PI)/(2*d);
    fp ex = exp(bx/d + c*c/(2*d*d));
    //fp erf_arg = bx/(M_SQRT2*c) + c/(M_SQRT2*d);
    fp erf_arg = (bx/c + c/d) / M_SQRT2;
    fp t = fact * ex * (d >= 0 ? erfc(erf_arg) : -erfc(-erf_arg));
    //fp t = fact * ex * (d >= 0 ? 1-erf(erf_arg) : -1-erf(erf_arg));
FUNC_CALCULATE_VALUE_END(t)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(EMG)
    fp a = vv[0];
    fp bx = vv[1] - x;
    fp c = vv[2];
    fp d = vv[3];
    fp cs2d = c/(M_SQRT2*d);
    fp cc = c*sqrt(M_PI/2)/d;
    fp ex = exp(bx/d + cs2d*cs2d); //==exp((c^2+2bd-2dx) / 2d^2)
    fp bx2c = bx/(M_SQRT2*c);
    fp erf_arg = bx2c + cs2d; //== (c*c+b*d-d*x)/(M_SQRT2*c*d);
    //fp er = erf(erf_arg);
    //fp d_sign = d >= 0 ? 1 : -1;
    //fp ser = d_sign - er;
    fp ser = (d >= 0 ? erfc(erf_arg) : -erfc(-erf_arg));
    fp t = cc * ex * ser;
    fp eee = exp(erf_arg*erf_arg);
    dy_dv[0] = t;
    dy_dv[1] = -a/d * ex / eee + a*t/d;
    dy_dv[2] = - a/(2*c*d*d*d)*exp(-bx2c*bx2c) 
                  * (2*d*(c*c-bx*d) - sqrt(2*M_PI)*c*(c*c+d*d) * eee * ser);
    //dy_dv[3] = a*c/(d*d*d)*ex * (c/eee 
    //                             - d_sign * (c*cc + sqrt(M_PI/2)*(b+d-x))
    //                             + sqrt(M_PI/2) * (c*c/d + b+d-x) * er);
    dy_dv[3] = a*c/(d*d*d)*ex * (c/eee - ser * (c*cc + sqrt(M_PI/2)*(bx+d)));
    dy_dx = - dy_dv[1];
FUNC_CALCULATE_VALUE_DERIV_END(a*t)

///////////////////////////////////////////////////////////////////////

const char *FuncDoniachSunjic::formula 
= "DoniachSunjic(h=height, a=0.1, F=1, E=center) ="
    "h * cos(pi*a/2 + (1-a)*atan((x-E)/F)) / (F^2+(x-E)^2)^((1-a)/2)";

bool FuncDoniachSunjic::get_nonzero_range(fp/*level*/, fp&/*left*/, 
                                          fp&/*right*/) const
{ return false; }

FUNC_CALCULATE_VALUE_BEGIN(DoniachSunjic)
    fp h = vv[0];
    fp a = vv[1];
    fp F = vv[2];
    fp xE = x - vv[3];
    fp t = h * cos(M_PI*a/2 + (1-a)*atan(xE/F)) / pow(F*F+xE*xE, (1-a)/2);
FUNC_CALCULATE_VALUE_END(t)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(DoniachSunjic)
    fp h = vv[0];
    fp a = vv[1];
    fp F = vv[2];
    fp xE = x - vv[3];
    fp fe2 = F*F+xE*xE;
    fp ac = 1-a;
    fp p = pow(fe2, -ac/2);
    fp at = atan(xE/F);
    fp cos_arg = M_PI*a/2 + ac*at;
    fp co = cos(cos_arg);
    fp si = sin(cos_arg);
    fp t = co * p;
    dy_dv[0] = t;
    dy_dv[1] = h * p * (co/2 * log(fe2) + (at-M_PI/2) * si);
    dy_dv[2] = h * ac*p/fe2 * (xE*si - F*co);
    dy_dv[3] = h * ac*p/fe2 * (xE*co + si*F);
    dy_dx = -dy_dv[3];
FUNC_CALCULATE_VALUE_DERIV_END(h*t)
///////////////////////////////////////////////////////////////////////


const char *FuncPielaszekCube::formula 
= "PielaszekCube(a=height*0.016, center, r=300, s=150) = ...#"; 


FUNC_CALCULATE_VALUE_BEGIN(PielaszekCube)
    fp height = vv[0];
    fp center = vv[1];
    fp R = vv[2];
    fp s = vv[3];
    fp s2 = s*s;
    fp s4 = s2*s2;
    fp R2 = R*R;

    fp q = (x-center);
    fp q2 = q*q;
    fp t = height * (-3*R*(-1 - (R2*(-1 +
                          pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))
                          * cos(2*(-1.5 + R2/(2.*s2)) * atan((q*s2)/R))))/
           (2.*q2*(-1.5 + R2/(2.*s2))* (-1 + R2/(2.*s2))*s4)))/
      (sqrt(2*M_PI)*q2*(-0.5 + R2/(2.*s2))* s2);
FUNC_CALCULATE_VALUE_END(t)

FUNC_CALCULATE_VALUE_DERIV_BEGIN(PielaszekCube)
    fp height = vv[0];
    fp center = vv[1];
    fp R = vv[2];
    fp s = vv[3];
    fp s2 = s*s;
    fp s3 = s*s2;
    fp s4 = s2*s2;
    fp R2 = R*R;
    fp R4 = R2*R2;
    fp R3 = R*R2;

    fp q = (x-center);
    fp q2 = q*q;
    fp t = (-3*R*(-1 - (R2*(-1 +
                              pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))
                              * cos(2*(-1.5 + R2/(2.*s2)) * atan((q*s2)/R))))/
               (2.*q2*(-1.5 + R2/(2.*s2))* (-1 + R2/(2.*s2))*s4)))/
          (sqrt(2*M_PI)*q2*(-0.5 + R2/(2.*s2))* s2);

    fp dcenter = height * (
            (3*sqrt(2/M_PI)*R*(-1 - 
                        (R2* (-1 + pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))*
             cos(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))))/
        (2.*q2*(-1.5 + R2/(2.*s2))* (-1 + R2/(2.*s2))*s4)))/
   (q*q2*(-0.5 + R2/(2.*s2))*s2) - (3*R*((R2*(-1 + 
            pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))*
             cos(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))))/
        (q*q2*(-1.5 + R2/(2.*s2))* (-1 + R2/(2.*s2))*s4) - 
       (R2*((2*q*(1.5 - R2/(2.*s2))* s4*
               pow(1 + (q2*s4)/R2, 0.5 - R2/(2.*s2))*
               cos(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R)))/R2 - 
            (2*(-1.5 + R2/(2.*s2))*s2* pow(1 + (q2*s4)/R2,
                0.5 - R2/(2.*s2))* sin(2*(-1.5 + R2/(2.*s2))*
                 atan((q*s2)/R)))/R))/
        (2.*q2*(-1.5 + R2/(2.*s2))* (-1 + R2/(2.*s2))*s4)))/
   (sqrt(2*M_PI)*q2*(-0.5 + R2/(2.*s2))* s2));

    fp dR = height * (
        (3*R2*(-1 - (R2* (-1 + pow(1 + (q2*s4)/R2,
              1.5 - R2/(2.*s2))* cos(2*(-1.5 + R2/(2.*s2))*
               atan((q*s2)/R))))/ (2.*q2*(-1.5 + R2/(2.*s2))*
          (-1 + R2/(2.*s2))*s4)))/ (sqrt(2*M_PI)*q2*pow(-0.5 + R2/(2.*s2),2)*
     s4) - (3*(-1 - (R2*(-1 + pow(1 + (q2*s4)/R2,
              1.5 - R2/(2.*s2))* cos(2*(-1.5 + R2/(2.*s2))*
               atan((q*s2)/R))))/ (2.*q2*(-1.5 + R2/(2.*s2))*
          (-1 + R2/(2.*s2))*s4)))/ (sqrt(2*M_PI)*q2*(-0.5 + R2/(2.*s2))*
     s2) - (3*R*((R3* (-1 + pow(1 + (q2*s4)/R2,
              1.5 - R2/(2.*s2))* cos(2*(-1.5 + R2/(2.*s2))*
               atan((q*s2)/R))))/ (2.*q2*(-1.5 + R2/(2.*s2))*
          pow(-1 + R2/(2.*s2),2)*s4*s2) + (R3*(-1 + pow(1 + (q2*s4)/R2,
              1.5 - R2/(2.*s2))* cos(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))))/
        (2.*q2*pow(-1.5 + R2/(2.*s2),2)* (-1 + R2/(2.*s2))*(s4*s2)) - 
       (R*(-1 + pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))*
             cos(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))))/
        (q2*(-1.5 + R2/(2.*s2))* (-1 + R2/(2.*s2))*s4) - 
       (R2*(pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))*
             cos(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))*
             ((-2*q2*(1.5 - R2/(2.*s2))* s4)/ (R3*
                  (1 + (q2*s4)/R2)) - (R*log(1 + (q2*s4)/R2))/ s2) + 
            pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))* ((2*q*(-1.5 + R2/(2.*s2))*
                  s2)/ (R2* (1 + (q2*s4)/R2)) - (2*R*atan((q*s2)/R))/s2)*
             sin(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))))/
        (2.*q2*(-1.5 + R2/(2.*s2))* (-1 + R2/(2.*s2))*s4)))/
   (sqrt(2*M_PI)*q2*(-0.5 + R2/(2.*s2))* s2));

    fp ds = height * (
            (-3*R3*(-1 - (R2* (-1 + pow(1 + (q2*s4)/R2,
              1.5 - R2/(2.*s2))* cos(2*(-1.5 + R2/(2.*s2))*
               atan((q*s2)/R))))/ (2.*q2*(-1.5 + R2/(2.*s2))*
          (-1 + R2/(2.*s2))*s4)))/
   (sqrt(2*M_PI)*q2*pow(-0.5 + R2/(2.*s2),2)* (s4*s)) + (3*sqrt(2/M_PI)*R*
     (-1 - (R2*(-1 + pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))*
             cos(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))))/
        (2.*q2*(-1.5 + R2/(2.*s2))* (-1 + R2/(2.*s2))*s4)))/
   (q2*(-0.5 + R2/(2.*s2))*s3) - (3*R*(-(R4*(-1 + 
             pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))*
              cos(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))))/
        (2.*q2*(-1.5 + R2/(2.*s2))* pow(-1 + R2/(2.*s2),2)*(s4*s3)) - 
       (R4*(-1 + pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))*
             cos(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))))/
        (2.*q2*pow(-1.5 + R2/(2.*s2),2)* (-1 + R2/(2.*s2))*(s4*s3)) + 
       (2*R2*(-1 + pow(1 + (q2*s4)/R2,
              1.5 - R2/(2.*s2))* cos(2*(-1.5 + R2/(2.*s2))*
               atan((q*s2)/R))))/ (q2*(-1.5 + R2/(2.*s2))*
          (-1 + R2/(2.*s2))*(s4*s)) - (R2*(pow(1 + (q2*s4)/R2,
              1.5 - R2/(2.*s2))* cos(2*(-1.5 + R2/(2.*s2))*
               atan((q*s2)/R))* ((4*q2*(1.5 - R2/(2.*s2))* s3)/
                (R2* (1 + (q2*s4)/R2)) + (R2*log(1 + 
                    (q2*s4)/R2))/ s3) + 
            pow(1 + (q2*s4)/R2, 1.5 - R2/(2.*s2))*
             ((-4*q*(-1.5 + R2/(2.*s2))*s)/ (R*(1 + (q2*s4)/R2)) + 
               (2*R2*atan((q*s2)/R))/ s3)*
             sin(2*(-1.5 + R2/(2.*s2))* atan((q*s2)/R))))/
        (2.*q2*(-1.5 + R2/(2.*s2))* (-1 + R2/(2.*s2))*s4)))/
   (sqrt(2*M_PI)*q2*(-0.5 + R2/(2.*s2))* s2));

    dy_dv[0] = t;
    dy_dv[1] = -dcenter;
    dy_dv[2] = dR;
    dy_dv[3] = ds;
    dy_dx = dcenter;
FUNC_CALCULATE_VALUE_DERIV_END(height*t);




syntax highlighted by Code2HTML, v. 0.9.1