//Renata Kopecna

#include <complex>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <math.h>
#include <TMath.h>

#include <TDecompChol.h>
#include <TDecompBK.h>
#include <TMatrixD.h>
#include <Math/GSLIntegrator.h>

#include <funcs.hh>
#include <event.hh>
#include <helpers.hh>
#include <constants.hh>
#include <integrals.hh>

#include <spdlog.h>

namespace fcnc
{


TMatrixD get_bsz_mu_invcov(){ //Used one in toystudy.hh
    TMatrixDSym bsz_cov(21);
    bsz_cov = get_bsz_cov();

    //need to do cholesky decomposition, use root functionality for this
    //TMatrixDSym mcoeffs_sym(21, &coeffs_sym[0]);
    TDecompChol chol(bsz_cov);
    bool success = chol.Decompose();
    assert(success);
    TMatrixD mU = chol.GetU();//gets upper triangular matrix

    if (spdlog_debug()){
        for (unsigned int i =0; i<21; i++){//column
            std::cout << i << std::endl;
            for (unsigned int j =0; j<21; j++){//row
                std::cout << mU(j,i) << " ";
            }
            std::cout << std::endl;
        }
    }

    return mU;
}

TMatrixDSym get_bsz_invcov()
{
    TMatrixDSym covariance_sym = TMatrixDSym(21);
    covariance_sym = get_bsz_cov();
    bool success = false;
    TDecompBK decompbk(covariance_sym);
    TMatrixDSym bsz_invcov = TMatrixDSym(decompbk.Invert(success));
    assert(success);
    return bsz_invcov;
}

TMatrixDSym get_bsz_cov()
{

    //current covariance matrix
    double covariance_raw[21*21] = {0.000837635, 0.00471065, 0.00272131, 0.00471065, 0.0659712, 0.204148, 0.00272131, 0.204148, 2.67119, 4.69403e-5, 0.000919267, 0.00714105, 0.0020347, 0.022787, 0.0846849, 0.0222044, 0.228333, 1.10882, 0.000602086, 0.00227154, 0.000674336, 0.00331857, 0.0250234, 0.079078, 0.00187104, 0.0687236, 0.713438, 3.79001e-5, 0.00134723, 0.00865921, 0.00239851, 0.0371657, 0.029583, 0.0296877, 0.312199, 0.0215381, 1.31212e-5, 0.000248351, -0.00751951, 0.00170386, 0.0218497, -0.0768858, 0.021364, 0.208973, -0.336619, 1.73162e-5, 0.000722872, 0.00072477, 0.00172356, 0.0211513, 0.0374082, 0.0213424, 0.190446, 0.359855, 0.000826815, 0.000674452, -0.022157, 0.00932974, 0.0304414, -0.109803, 0.0686369, 0.154917, -1.14562, 4.69403e-5, 0.0020347, 0.0222044, 0.000919267, 0.022787, 0.228333, 0.00714105, 0.0846849, 1.10882, 0.000694641, 0.00347885, 0.00415084, 0.00347885, 0.0353042, 0.144041, 0.00415084, 0.144041, 1.05126, 3.04401e-5, 3.57306e-5, 0.00355147, 0.000650453, 0.00688257, 0.0593046, 0.00522776, 0.0417512, 0.36039, 0.000809924, 0.00503414, -0.0144592, 0.00424412, 0.0423395, -0.0182202, 0.00664211, 0.138549, 0.397771, 0.000652147, 0.00340264, -0.0202476, 0.00325978, 0.0270724, -0.114933, 0.00381811, 0.0778571, -0.214537, 0.00065194, 0.00303435, -0.00428123, 0.00327701, 0.028583, 0.0341862, 0.00394892, 0.10177, 0.331577, 0.000942311, 0.00179228, -0.0210447, 0.0082808, 0.0190827, -0.18232, 0.0359498, 0.0574551, -0.914838, 0.000602086, 0.00331857, 0.00187104, 0.00227154, 0.0250234, 0.0687236, 0.000674336, 0.079078, 0.713438, 3.04401e-5, 0.000650453, 0.00522776, 3.57306e-5, 0.00688257, 0.0417512, 0.00355147, 0.0593046, 0.36039, 0.000432775, 0.00165482, 0.000605487, 0.00165482, 0.0165823, 0.059165, 0.000605487, 0.059165, 0.430695, 2.32114e-5, 0.000937822, 0.00645455, -6.04184e-5, 0.0117957, 0.0462642, 0.0042958, 0.0833038, 0.140056, 6.01978e-6, 0.000154238, -0.0052701, -8.61667e-5, 0.00572307, -0.0128712, 0.00312849, 0.0501177, -0.083906, 9.00023e-6, 0.000503353, 0.000573722, -7.58925e-5, 0.00611674, 0.0257193, 0.00313087, 0.0460709, 0.133667, 0.000590954, 0.000464079, -0.0159601, 0.00315256, 0.0153668, -0.0107164, 0.0167505, 0.083256, -0.0400335, 3.79001e-5, 0.00239851, 0.0296877, 0.00134723, 0.0371657, 0.312199, 0.00865921, 0.029583, 0.0215381, 0.000809924, 0.00424412, 0.00664211, 0.00503414, 0.0423395, 0.138549, -0.0144592, -0.0182202, 0.397771, 2.32114e-5, -6.04184e-5, 0.0042958, 0.000937822, 0.0117957, 0.0833038, 0.00645455, 0.0462642, 0.140056, 0.00110852, 0.00658825, -0.0202371, 0.00658825, 0.0682608, 0.0138459, -0.0202371, 0.0138459, 2.34402, 0.000825342, 0.004522, -0.0218767, 0.00509604, 0.0448809, -0.101388, -0.0149136, -0.0227422, 0.849315, 0.000823664, 0.00388965, -0.00393922, 0.00509672, 0.0397888, 0.0548511, -0.0148331, -0.0137692, 0.473698, 0.00120989, 0.00205499, -0.0264334, 0.0116269, 0.0278953, -0.204925, 0.00157102, 2.28401e-5, 0.19199, 1.31212e-5, 0.00170386, 0.021364, 0.000248351, 0.0218497, 0.208973, -0.00751951, -0.0768858, -0.336619, 0.000652147, 0.00325978, 0.00381811, 0.00340264, 0.0270724, 0.0778571, -0.0202476, -0.114933, -0.214537, 6.01978e-6, -8.61667e-5, 0.00312849, 0.000154238, 0.00572307, 0.0501177, -0.0052701, -0.0128712, -0.083906, 0.000825342, 0.00509604, -0.0149136, 0.004522, 0.0448809, -0.0227422, -0.0218767, -0.101388, 0.849315, 0.000756032, 0.00372113, -0.0213639, 0.00372113, 0.0359388, 0.00028928, -0.0213639, 0.00028928, 2.68844, 0.000756032, 0.00313686, -0.00507995, 0.0036848, 0.0277685, 0.0556352, -0.0217047, -0.0652209, 0.825691, 0.00089759, 0.00144256, -0.0188003, 0.0072161, 0.0179819, -0.0877721, -0.0281293, -0.0385387, 1.61537, 1.73162e-5, 0.00172356, 0.0213424, 0.000722872, 0.0211513, 0.190446, 0.00072477, 0.0374082, 0.359855, 0.00065194, 0.00327701, 0.00394892, 0.00303435, 0.028583, 0.10177, -0.00428123, 0.0341862, 0.331577, 9.00023e-6, -7.58925e-5, 0.00313087, 0.000503353, 0.00611674, 0.0460709, 0.000573722, 0.0257193, 0.133667, 0.000823664, 0.00509672, -0.0148331, 0.00388965, 0.0397888, -0.0137692, -0.00393922, 0.0548511, 0.473698, 0.000756032, 0.0036848, -0.0217047, 0.00313686, 0.0277685, -0.0652209, -0.00507995, 0.0556352, 0.825691, 0.000756032, 0.00316639, -0.00503577, 0.00316639, 0.0276287, 0.0511444, -0.00503577, 0.0511444, 0.646067, 0.000900667, 0.00145139, -0.0189859, 0.00693449, 0.0173072, -0.119675, 0.00493465, 0.0386434, 0.478871, 0.000826815, 0.00932974, 0.0686369, 0.000674452, 0.0304414, 0.154917, -0.022157, -0.109803, -1.14562, 0.000942311, 0.0082808, 0.0359498, 0.00179228, 0.0190827, 0.0574551, -0.0210447, -0.18232, -0.914838, 0.000590954, 0.00315256, 0.0167505, 0.000464079, 0.0153668, 0.083256, -0.0159601, -0.0107164, -0.0400335, 0.00120989, 0.0116269, 0.00157102, 0.00205499, 0.0278953, 2.28401e-5, -0.0264334, -0.204925, 0.19199, 0.00089759, 0.0072161, -0.0281293, 0.00144256, 0.0179819, -0.0385387, -0.0188003, -0.0877721, 1.61537, 0.000900667, 0.00693449, 0.00493465, 0.00145139, 0.0173072, 0.0386434, -0.0189859, -0.119675, 0.478871, 0.0040111, 0.00460369, -0.0993374, 0.00460369, 0.0493411, 0.140689, -0.0993374, 0.140689, 4.85487 };
    
    //spdlog::info("chi2 start"std::endl;

    double covariance_reordered[21*21];
    //double covariance_reordered_old[21*21];
    //want to reorder covariances into proper matrix A0a0 A0a1 A0a2 A1a0 A1a1 A1a2 A12a0 and so on
    for (unsigned int i =0; i<21; i++)//column
        for (unsigned int j =0; j<21; j++)//row
        {
            int to = 21*j+i;
            int parcol = (to%21);
            int parrow = to / 21;
            int ffcol = parcol/3;
            int ffrow = parrow/3;
            int alphacol = parcol%3;
            int alpharow = parrow%3;
            int from = 9*(7*ffrow+ffcol) + 3*alpharow + alphacol;
            covariance_reordered[to] = covariance_raw[from];
            //covariance_reordered_old[to] = covariance_old[from];
        }

    //for (unsigned int i=0; i<21; i++)
    //  spdlog::info("COVCOMP new ",sqrt(covariance_reordered[i*21+i])" old ",sqrt(covariance_reordered_old[i*21+i]) << );
    //now need to remove 100% correlated obs
    //a12_0 = (mB^2-mK*^2)/(8mBmK*)A3(0) = (mB^2-mK*^2)/(8mBmK*)A0(0)  B6 in BSZ paper
    //t2_0 = t1_0
    double covariance_final[21*21];
    for (unsigned int i =0; i<21; i++)//column
        for (unsigned int j =0; j<21; j++)//row
        {
            covariance_final[j*21+i] = covariance_reordered[j*21+i];
            if ((i==6 || j==6) || (i==15 || j==15))
                covariance_final[j*21+i] = 0.0;
            if ((i==6 && j==6) || (i==15 && j==15))
                covariance_final[j*21+i] = 0.1;
        }
    //test output
    //for (unsigned int i =0; i<21; i++)//column
    //  {
    //    std::cout << i << std::endl;
    //    for (unsigned int j =0; j<21; j++)//row
    //	std::cout << covariance_final[21*j+i] << " ";
    //    std::cout << std::endl << std::endl;
    //  }//seems to be ok
    // spdlog::info("BSZ correlations",std::endl << "    ";
    // for (unsigned int i =0; i<21; i++)
    //   std::cout << std::fixed << std::setw(5) << i << " ";
    // std::cout << std::endl;
    // for (unsigned int i =0; i<21; i++)//column
    //   {
    //     std::cout << i << (i<10 ? "   " : "  ");
    //     for (unsigned int j =0; j<21; j++)//row
    // 	  std::cout << std::fixed << std::setprecision(2) << std::setw(5) << covariance_final[21*j+i]/sqrt(covariance_final[21*i+i]*covariance_final[21*j+j]) << " ";
    //     std::cout << std::endl;
    //  }//seems to be ok


    //we should be able to invert this now
    TMatrixDSym covariance_sym(21, &covariance_final[0]);
    return covariance_sym;
}

double crystalball(double m, double mean, double sigma, double alpha, double n){

    //implementation taken from RooFit: https://root.cern.ch/doc/master/RooCBShape_8cxx_source.html#l00056

    double t = (m-mean)/sigma;
    if (alpha < 0) t = -t;
    
    double absAlpha = fabs(alpha);
    
    if (t >= -absAlpha) {
        return exp(-0.5*t*t);
    }
    else {
        double a =  TMath::Power(n/absAlpha,n)*exp(-0.5*absAlpha*absAlpha);
        double b= n/absAlpha - absAlpha;
        return a/TMath::Power(b - t, n);
    }
}

double twotailedcrystalball(double m, double mean, double sigma, double alpha1, double alpha2, double n1, double n2){

    double result = 0.0;
    if (alpha1 < 0.0 || alpha2 < 0.0 || isnan(alpha1) || isnan(alpha2)){
        spdlog::critical("sigma {0:f}\talpha1 {1:f}\talpha2 {2:f}\tn1 {3:f}\tn2 {4:f}",sigma,alpha1,alpha2,n1,n2);
        assert(0);
    }

    //implementation taken from RooFit: RooDoubleCB.cxx
    double t = (m-mean)/sigma;
    if(t>-alpha1 && t<alpha2){
        result = TMath::Exp(-0.5*t*t);
    }
    else if(t<=-alpha1){
        double alpha1invn1 = alpha1/n1;
        result = TMath::Exp(-0.5*alpha1*alpha1)*TMath::Power(1. - alpha1invn1*(alpha1+t), -n1);
    }
    else if(t>=alpha2){
        double alpha2invn2 = alpha2/n2;
        result = TMath::Exp(-0.5*alpha2*alpha2)*TMath::Power(1. - alpha2invn2*(alpha2-t), -n2);
    }
    else{
        spdlog::critical("Cannot sort t={0:f} in range with alphas: alpha1={1:f}, alpha2={2:f}",
                         t, alpha1, alpha2);
        assert(0);
    }

    if(std::isnan(result) || std::isinf(result)){
        spdlog::critical("Calculation of twotailed CB failed: {0:f}", result);
        spdlog::critical("m(B): {0:f}\tmean: {1:f}\tsigma: {2:f}\talpha1: {3:f}\talpha2: {4:f}\tn1: {5:f}\tn2: {6:f}",m, mean, sigma, alpha1, alpha2, n1, n2);
        assert(0);
    }

    return result;
}

double sqr(double x)
{
    return x*x;
}

//basic trigonometric functions
double costheta2(double costheta){
    return costheta*costheta; //cos^2(x)
}
double cos2theta(double costheta){
    return 2.0*costheta2(costheta) - 1.0; //cos(2x) = 2cos^2(x)-1
}
double sintheta2(double costheta){
    return 1.0-costheta2(costheta); //sin^2 = 1-cos^2
}
double sintheta_abs(double costheta){ //TODO: check if this is fine as sin(x) != |sin(x)|
    return sqrt(sintheta2(costheta));
}
double sin2theta(double costheta){ //TODO: check if this is fine as sin(x) != |sin(x)|
    return 2.0 * sintheta_abs(costheta) * costheta;
}


//calculates all chebyshev polynomials up to including degree n
void chebyshev(double x, int n, std::vector<double>& results)
{
    assert(n >= 0);
    results.clear();
    results.reserve(n+1);
    results.push_back(1.0);
    results.push_back(x);
    for (int i=2; i<n+1; i++){
        results.push_back(2.0*x*results.at(i-1) - results.at(i-2));
    }
}

//get polynomial coefficients from chebyshev coefficients
void chebyshev_to_poly(const std::vector<double>& chebychev, std::vector<double>& poly)
{
    poly.clear();
    poly.resize(chebychev.size(), 0.0);
    if (chebychev.size() > 0) poly.at(0) = chebychev.at(0);
    if (chebychev.size() > 1) poly.at(1) = chebychev.at(1);
    for (unsigned int n =2; n<chebychev.size(); n++)
    {
        for (unsigned int r=0; r<=n/2; r++)
        {
            poly.at(n-2*r) += chebychev.at(n)
                    *n/2.0*pow(-1.0,r)/double(n-r)*TMath::Binomial(n-r,r)*pow(2.0,n-2*r);
        }
    }
}


//correct the polynomial coefficients from -1..1 to min..max
void correct_poly(const std::vector<double>& poly, std::vector<double>& correct, double min, double max){
    correct.clear();

    double c = 2.0/(max-min); //What idiot names stuff like c, d and e without explanations
    double d = -2.0*min/(max-min)-1.0;
    double e = d/c; //e = -min ?!

    unsigned int order = poly.size();
    correct.resize(order, 0.0);
    for (unsigned int i = 0; i<order; i++){
        double c_dash = poly.at(i);
        for (unsigned int m=0; m<=i; m++){
            correct.at(m) +=
                    c_dash
                    *pow(c,int(i))
                    *TMath::Binomial(i,m)
                    *pow(e,int(i-m));
        }
    }
}

//calculates legendre polynomial of order n
double legendre(double x, int n){
    assert(n >= 0);
    if (n == 0)      return 1;
    else if (n == 1) return x;
    else             return ((2.0*n-1.0)*x*legendre(x, n-1) - (n-1.0)*legendre(x, n-2))/double(n);
}

//calculates all legendre polynomials up to including degree n
void legendre(double x, int n, std::vector<double>& results){
    //optimized
    assert(n > 0);
    //results = std::vector<double>(n+1,1.0);
    results.resize(n+1);
    results[0] = 1.0;
    results[1] = x;
    for (int i=2; i<n+1; i++){
        results[i] = ((2.0*i-1.0) * x * results[i-1] - (i-1.0) * results[i-2]) / i;
    }
}

void orthonormal_legendre(double x, int n, std::vector<double>& results){
    legendre(x, n, results);
    for (unsigned int i=0; i<results.size(); i++){
        results.at(i) *= sqrt(0.5*(2.0*i+1.0));
    }
}

void legendre_to_poly(const std::vector<double>& legendre, std::vector<double>& poly){
    poly.clear();
    poly.resize(legendre.size(), 0.0);
    if (legendre.size() > 0){//constant
        poly.at(0) = legendre.at(0);
    }
    if (legendre.size() > 1){//linear
        poly.at(1) = legendre.at(1);
    }
    for (unsigned int i=2; i<legendre.size(); i++)//quadratic and higher
        //example size = 3 -> quadratic
        //i = 2 and 2 only
        //k=0
        //k=1
        //as it should be
        //poly(2) +=
        //poly(0) +=
    {
        for (unsigned int k=0; k<=i/2; k++)
            poly.at(i-2*k) += legendre.at(i)
                    * pow(-1.0, k)/pow(2.0, i)*TMath::Binomial(i, k)*TMath::Binomial(2*i-2*k, i);
    }

}

//calculates chebyshev polynomial of order n
double chebyshev(double x, int n){
    assert(n >= 0);
    if (n == 0)         return 1;
    else if (n == 1)    return x;
    else                return 2.0*x*chebyshev(x, n-1) - chebyshev(x, n-2);
}


double legendre_poly(unsigned int l, double x){
    return ROOT::Math::legendre(l, x);
}

double assoc_legendre(unsigned int l, unsigned int m, double x){
    return ROOT::Math::assoc_legendre(l, m, x);
}

double spherical_harmonic_re(unsigned int l, unsigned int m, double cos_theta, double phi){
    return sqrt((2*l+1.0)/(4.0*TMath::Pi()))*sqrt(TMath::Factorial(l-m)/TMath::Factorial(l+m))
            *assoc_legendre(l, m, cos_theta)
            *cos(m*phi);
}

double spherical_harmonic_im(unsigned int l, unsigned int m, double cos_theta, double phi){
    return sqrt((2*l+1.0)/(4.0*TMath::Pi()))*sqrt(TMath::Factorial(l-m)/double(TMath::Factorial(l+m)))
            *assoc_legendre(l, m, cos_theta)
            *sin(m*phi);
}

void calculate_transversity_angles(const LorentzVector& mu_minus,
                                   const LorentzVector& mu_plus,
                                   const LorentzVector& k_minus,
                                   const LorentzVector& k_plus,
                                   double & cos_theta, double & angle_phi, double & cos_psi){
    //LorentzVector bs = mu_plus + mu_minus + k_plus + k_minus;
    LorentzVector jpsi = mu_plus + mu_minus;
    LorentzVector phi = k_plus + k_minus;
    LorentzBoost jpsiboost(jpsi.BoostToCM());//boosttocm actually gives beta vector
    LorentzBoost phiboost(phi.BoostToCM());//pxyzm has boost to cm pxyzm not?
    //boosted quantities in jpsi system
    //LorentzVector mu_minus_d = jpsiboost(mu_minus);
    LorentzVector mu_plus_d = jpsiboost(mu_plus);
    LorentzVector k_minus_d = jpsiboost(k_minus);
    LorentzVector k_plus_d = jpsiboost(k_plus);
    LorentzVector phi_d = jpsiboost(phi);
    //boosted quantities in phi system
    LorentzVector k_plus_dd = phiboost(k_plus);
    LorentzVector jpsi_dd = phiboost(jpsi);

    //cos theta is calculated in the cms of the jpsi
    //cos theta
    Vector3 normalxy = k_minus_d.Vect().Cross(k_plus_d.Vect());
    normalxy = normalxy.Unit();//normals should be normalized
    cos_theta = (normalxy.Dot(mu_plus_d.Vect())
                 / sqrt(normalxy.Mag2())
                 / sqrt(mu_plus_d.Vect().Mag2()));
    //phi is calculated in the cms of the jpsi
    //cos phi
    Vector3 mu_perp = normalxy * mu_plus_d.Vect().Dot(normalxy);
    Vector3 mu_parallel = mu_plus_d.Vect() - mu_perp;
    angle_phi = acos(mu_parallel.Dot(phi_d.Vect())
                     / sqrt(mu_parallel.Mag2())
                     / sqrt(phi_d.Vect().Mag2()));
    //now get orientation
    Vector3 checknormal = phi_d.Vect().Cross(mu_parallel);
    bool samedirection = (checknormal.Dot(normalxy) > 0.0);
    if (!samedirection)
        angle_phi = -angle_phi;

    //cos psi is calculated in the cms of the phi
    //cos psi
    cos_psi = k_plus_dd.Vect().Dot(-jpsi_dd.Vect())
            /sqrt(k_plus_dd.Vect().Mag2())
            /sqrt(jpsi_dd.Vect().Mag2());

}

//this is not normalized!
double convoluted_exp(double ct, double sigma_ct, double ctau){
    //use error function instead of numeric integration, should be a lot faster
    if (ct > -6.0*sigma_ct)//this safety is important for the plotting
    {
        const double sqrt2 = TMath::Sqrt(2.0);
        //  const double norm_factor = 2.0/TMath::Sqrt(TMath::Pi());
        // Erf(x) = (2/sqrt(pi)) Integral(exp(-t^2))dt between 0 and x, already taken into account?
        double exp_part = exp(-ct/ctau + sigma_ct*sigma_ct/(2.0*ctau*ctau));
        double erfc = TMath::Erfc(sigma_ct/ctau/sqrt2 - ct/sigma_ct/sqrt2);
        // / norm_factor; the normalization here is already done?
        //the expression below was normalized, but it shouldnt have been
        //double prob = 0.5/ctau * exp * erfc;//is the factor 2 correct? yes.
        double prob = 0.5 * exp_part * erfc;//is the factor 2 correct? yes.
        if (std::isnan(prob) || prob <= 0.0)
        {
            spdlog::warn("CONVEXP gives prob: {0:f}; ct/sigma_ct/ctau/exp/erfc/exparg/erfarg: {1:f} {2:f}",
                         prob, ct,sigma_ct );
            spdlog::warn("ctau/exp{0:f} {1:f}",ctau,exp_part);
            spdlog::warn("erfc/exparg/erfarg {0:f} {1:f} {2:f}",erfc,
                         -ct/ctau + sigma_ct*sigma_ct/(2.0*ctau*ctau),
                         sigma_ct/ctau/sqrt2 - ct/sigma_ct/sqrt2 );
            prob = 0.0;
        }
        return prob;
    }
    else return 0.0;//safety added
}


//may be needed later...
double expdecay(double t, double tau, bool normalized){
    if (t >= 0.0) {
        if (normalized) return 1.0/tau*exp(-t/tau);
        else            return exp(-t/tau);
    }
    else{
        return 0.0;
    }
}

double gauss(double x, double sigma, double mean){
    //what do we have libraries for...
    //this one is normalised
    return TMath::Gaus(x, mean, sigma, kTRUE);
}

double normed_gauss(double x, double sigma, double mean, double min, double max){
    //corrected error
    double norm = 1.0/2.0
            *(TMath::Erf((max-mean)/(TMath::Sqrt(2.0)*sigma))
              -TMath::Erf((min-mean)/(TMath::Sqrt(2.0)*sigma)));
    return 1.0/(TMath::Sqrt(2.0*TMath::Pi())*sigma)*exp(-(mean-x)*(mean-x)/(2*sigma*sigma))/norm;
}

double linear(double x, double min, double max, double slope){
    //have corrected the slope (parameter needs to be big enough for minuit -> divide by width = 500^2)
    double width = max - min;
    double ymin = 1.0/width - 0.5*slope/(width*width)*width;//corrected the 0.5 factor
    return ymin + slope/(width*width) * (x - min);
}


//computes erfc, because erfc(x)=exp(-x^2)*w(i*x)
std::complex<double> cErrF(const std::complex<double>& x){
    const std::complex<double> i(0.0,1.0);
    std::complex<double> z(i*x);
    //std::complex<double> z(-x.imag(), x.real());
    std::complex<double> result = exp(-x*x)*wErrF(z);
    return result;
}

//computes erfc, because erfc(x)=exp(-x^2)*w(i*x)
std::complex<double> cErrF_2(const std::complex<double>& x){
    const std::complex<double> i(0.0,1.0);
    std::complex<double> z(i*x);
    std::complex<double> result = exp(-x*x)*Faddeeva_2(z);

    if (x.real() > 20.0)  result = 0.0;
    if (x.real() < -20.0) result = 2.0;

    return result;
}

std::complex<double> ErrF_2(const std::complex<double>& x){
    return 1.0 - cErrF_2(x);
}

//computes erfc, because erfc(x)=exp(-x^2)*w(i*x)
std::complex<double> cErrF_3(const std::complex<double>& x){
    const std::complex<double> i(0.0,1.0);
    std::complex<double> z(i*x);
    std::complex<double> result = exp(-x*x)*nwwerf(z);
    return result;
    //careful! the fortran version is apparently not thread safe //TODO
}

//from matpack
//precision 1e-14
std::complex<double> Faddeeva_2 (const std::complex<double>& z)
{
    // table 1: coefficients for h = 0.5
    static double n1[12] =
    { 0.25, 1.0, 2.25, 4.0, 6.25, 9.0, 12.25, 16.0,
      20.25, 25.0, 30.25, 36.0 };
    static double e1[12] =
    { 0.7788007830714049,    0.3678794411714423,
      1.053992245618643e-1,  1.831563888873418e-2,
      1.930454136227709e-3,  1.234098040866795e-4,
      4.785117392129009e-6,  1.125351747192591e-7,
      1.605228055185612e-9,  1.388794386496402e-11,
      7.287724095819692e-14, 2.319522830243569e-16 };

    // table 2: coefficients for h = 0.53
    static double n2[12] =
    { 0.2809, 1.1236, 2.5281, 4.4944, 7.0225, 10.1124,
      13.7641, 17.9776, 22.7529, 28.09, 33.9889, 40.4496 };
    static double e2[12] =
    { 0.7551038420890235,    0.3251072991205958,
      7.981051630007964e-2,  1.117138143353082e-2,
      0.891593719995219e-3,  4.057331392320188e-5,
      1.052755021528803e-6,  1.557498087816203e-8,
      1.313835773243312e-10, 6.319285885175346e-13,
      1.733038792213266e-15, 2.709954036083074e-18 };
    
    // tables for Pade approximation
    static double C[7] =
    { 65536.0, -2885792.0, 69973904.0, -791494704.0,
      8962513560.0, -32794651890.0, 175685635125.0 };
    static double D[7] =
    { 192192.0, 8648640.0, 183783600.0, 2329725600.0,
      18332414100.0, 84329104860.0, 175685635125.0 };

    double *n,*e,t,u,r,s,d,f,g,h;
    std::complex<double> c,d2,v,w,zz;
    int i;
    
    // use Pade approximation
    s = norm(z);
    if (s < 1e-7) {
        zz = z*z;
        v  = exp(zz);
        c  = C[0];
        d2 = D[0];
        for (i = 1; i <= 6; i++) {
            c  = c  * zz + C[i];
            d2 = d2 * zz + D[i];
        }
        w = 1.0 / v + std::complex<double>(0.0,m_2_SQRTPI) * c/d2 * z * v;
        return w;

        // use trapezoid rule
    }
    else {
        // select default table 1
        n = n1;
        e = e1;
        r = M_1_PI * 0.5;

        // if z is too close to a pole select table 2
        if (fabs(imag(z)) < 0.01 && fabs(real(z)) < 6.01) {
            h = modf(2*fabs(real(z)),&g);
            if (h < 0.02 || h > 0.98) {
                n = n2;
                e = e2;
                r = M_1_PI * 0.53;
            }
        }
        
        d = (imag(z) - real(z)) * (imag(z) + real(z));
        f = 4 * real(z) * real(z) * imag(z) * imag(z);

        g = h = 0.0;
        for (i = 0; i < 12; i++) {
            t = d + n[i];
            u = e[i] / (t * t + f);
            g += (s + n[i]) * u;
            h += (s - n[i]) * u;
        }
        u = 1 / s;
        c = r * std::complex<double>(imag(z) * (u + 2.0 * g),
                                     real(z) * (u + 2.0 * h) );
        
        if (imag(z) < M_2PI) {
            s = 2.0 / r;
            t = s * real(z);
            u = s * imag(z);
            s = sin(t);
            h = cos(t);
            f = exp(- u) - h;
            g = 2.0 * exp(d-u) / (s * s + f * f);
            u = 2.0 * real(z) * imag(z);
            h = cos(u);
            t = sin(u);
            c += g * std::complex<double>( (h * f - t * s), -(h * s + t * f));
        }
        return c;
    }
}

//this is the complex error function w(z)
std::complex<double> wErrF(const std::complex<double>& arg){
    const double c1 = 7.4;
    const double c2 = 8.3;
    const double c3 = 0.3125;
    const double c4 = 1.6;

    const double p = 46768052394588893.38251791464692105662;

    //----------------------------------------------------------------------------
    std::complex<double>  r[37];
    std::complex<double>  zh, s, t, v;
    std::complex<double>  hh, den;

    double xl = p;

    double x  = arg.real();
    double y  = arg.imag();
    double xa = fabs(x);
    double ya = fabs(y);

    if (ya < c1 && xa < c2) {
        zh = std::complex<double>(ya+c4, xa);

        for (int n=35; n>-1; n--) {
            t    = zh + std::conj(double(n+1)*r[n+1]);
            r[n] = 0.5 / (t.real()*t.real() + t.imag()*t.imag()) * t;
        }

        for (int n=32; n>-1; n--) {
            xl =  xl*c3;
            s  = (xl+s)*r[n];
        }

        v = m_2_SQRTPI * s;
    }
    else {
        zh = std::complex<double>(ya, xa);

        for (int n=8; n>-1; n--) {
            t    = zh + std::conj(double(n+1)*r[0]);
            r[0] = 0.5 / (t.real()*t.real() + t.imag()*t.imag()) * t;
        }

        v = m_2_SQRTPI * r[0];
    }

    if (ya == 0){
        v = std::complex<double>(exp(-xa*xa), v.imag());
    }

    if (y < 0) {
        hh = std::complex<double>(xa, ya);
        std::complex<double> hprod = -hh*hh;
        std::complex<double> anexp(exp(hprod.real())*cos(hprod.imag()),exp(hprod.real())*sin(hprod.imag()));
        v = 2.0 * anexp - v;
    }
    else if (x < 0){
        v = conj(v);
    }

    if(std::isnan(v.real())) spdlog::warn("wErrF: the result is NaN, do not trust anything!");

    return v;

}

std::complex<double> nwwerf(const std::complex<double> z) {
    std::complex<double>  zh,r[38],s,t,v;

    const double z1 = 1;
    const double hf = z1/2;
    const double z10 = 10;
    const double c1 = 74/z10;
    const double c2 = 83/z10;
    const double c3 = z10/32;
    const double c4 = 16/z10;
    const double p = pow(2.0*c4,33);

    double x=z.real();
    double y=z.imag();
    double xa=(x >= 0) ? x : -x;
    double ya=(y >= 0) ? y : -y;
    if(ya < c1 && xa < c2){
        zh = std::complex<double>(ya+c4,xa);
        r[37]= std::complex<double>(0,0);
        //       do 1 n = 36,1,-1
        for(int n = 36; n>0; n--){
            t=zh+double(n)*std::conj(r[n+1]);
            r[n]=hf*t/std::norm(t);
        }
        double xl=p;
        s=std::complex<double>(0,0);
        //    do 2 n = 33,1,-1
        for(int k=33; k>0; k--){
            xl=c3*xl;
            s=r[k]*(s+xl);
        }
        v=m_2_SQRTPI*s;
    }
    else{
        zh=std::complex<double>(ya,xa);
        r[1]=std::complex<double>(0,0);
        //       do 3 n = 9,1,-1
        for(int n=9;n>0;n--){
            t=zh+double(n)*std::conj(r[1]);
            r[1]=hf*t/std::norm(t);
        }
        v=m_2_SQRTPI*r[1];
    }
    if(ya == 0) v= std::complex<double>(exp(-xa*xa),v.imag());
    if(y < 0) {
        v=2.0*std::exp(std::complex<double>(-xa,-ya)*std::complex<double>(xa,ya))-v;
        if(x > 0) v=std::conj(v);
    }
    else{
        if(x < 0) v=std::conj(v);
    }
    return v;
}

void norm_convoluted_exp_sincos(double tau, double deltam, double& one, double& two){
    //double tau = 1.0/gamma();//0.5*(ctau_l() + ctau_h());//is this correct? 1/(0.5(gammal + gammah))
    double tau_2 = tau*tau;
    double deltam_2 = deltam*deltam;
    one = tau_2 * deltam / (tau_2 * deltam_2 + 1.0);//sin
    two = tau / (tau_2 * deltam_2 + 1.0);//cos
}

//calculates the gaussian convolution sin(angle) from 0 to pi
double convoluted_sin(double angle, double sigma){
    double b = -angle/sqrt(2.0)/sigma + TMath::Pi()/sqrt(2.0)/sigma;
    double c = angle/sqrt(2.0)/sigma;
    double a = sigma/sqrt(2.0);
    double sigma_2 = sigma*sigma;
    double result = 0.0;
    std::complex<double> arg1(b, a);
    std::complex<double> arg2(c, a);
    std::complex<double> erf1 = 1.0-cErrF_2(arg1);
    std::complex<double> erf2 = 1.0-cErrF_2(arg2);
    result = 0.5*sin(angle)*exp(-0.5*sigma_2)
            *(erf1.real()+erf2.real())
            +0.5*cos(angle)*exp(-0.5*sigma_2)
            *(-erf1.imag()+erf2.imag());
    spdlog::trace("convoluted sin:\t{0:f}", result);
    return result;
}

double convoluted_cos_sin(double angle, double sigma){
    return 0.5*convoluted_2_sin(angle, sigma);
}

//calculates the gaussian convolution sin(angle) from 0 to pi
double convoluted_cos(double angle, double sigma)
{
    double b = -angle/sqrt(2.0)/sigma + TMath::Pi()/sqrt(2.0)/sigma;
    double c = -angle/sqrt(2.0)/sigma;//also changed sign here
    double a = sigma/sqrt(2.0);
    double sigma_2 = sigma*sigma;
    double result = 0.0;
    std::complex<double> arg1(b, a);
    std::complex<double> arg2(c, a);
    std::complex<double> erf1 = 1.0-cErrF_2(arg1);
    std::complex<double> erf2 = 1.0-cErrF_2(arg2);
    result = 0.5*sin(angle)*exp(-0.5*sigma_2)
            *(erf1.imag()-erf2.imag())
            +0.5*cos(angle)*exp(-0.5*sigma_2)
            *(erf1.real()-erf2.real());
    return result;
}

//calculates the gaussian convolution cos(angle)^2 from 0 to pi
double convoluted_cos_2(double angle, double sigma){
    double b = -angle/sqrt(2.0)/sigma + TMath::Pi()/sqrt(2.0)/sigma;
    double c = -angle/sqrt(2.0)/sigma;//changed the sign here, more consistent
    double a = sigma*sqrt(2.0);//!!!
    double sigma_2 = sigma*sigma;
    double result = 0.0;
    std::complex<double> arg1(b, a);
    std::complex<double> arg2(c, a);
    std::complex<double> erf1 = 1.0-cErrF_2(arg1);
    std::complex<double> erf2 = 1.0-cErrF_2(arg2);
    result = 0.25*sin(2.0*angle)*exp(-2.0*sigma_2)
            *(erf1.imag()-erf2.imag())
            +0.25*cos(2.0*angle)*exp(-2.0*sigma_2)
            *(erf1.real()-erf2.real())
            -0.25*erf(-b)
            +0.25*erf(-c);
    return result;
}

//calculates the gaussian convolution cos(angle)^2*sin(angle) from 0 to pi
double convoluted_cos_2_sin(double angle, double sigma){
    double b = -angle/sqrt(2.0)/sigma + TMath::Pi()/sqrt(2.0)/sigma;
    double c = -angle/sqrt(2.0)/sigma;//changed the sign here, more consistent

    double a = 3.0*sigma/sqrt(2.0);
    double sigma_2 = sigma*sigma;
    double result = 0.0;
    std::complex<double> arg1(b, a);
    std::complex<double> arg2(c, a);
    std::complex<double> erf1 = 1.0-cErrF_2(arg1);
    std::complex<double> erf2 = 1.0-cErrF_2(arg2);

    result = 1.0/8.0*sin(3.0*angle)*exp(-4.5*sigma_2)
            *(erf1.real()-erf2.real())
            +1.0/8.0*cos(3.0*angle)*exp(-4.5*sigma_2)
            *(-erf1.imag()+erf2.imag());

    a = sigma/sqrt(2.0);
    arg1 = std::complex<double>(b, a);
    arg2 = std::complex<double>(c, a);

    erf1 = 1.0-cErrF_2(arg1);
    erf2 = 1.0-cErrF_2(arg2);

    result += 1.0/8.0*sin(angle)*exp(-0.5*sigma_2)
            *(erf1.real()-erf2.real())
            +1.0/8.0*cos(angle)*exp(-0.5*sigma_2)
            *(-erf1.imag()+erf2.imag());
    return result;
}

//gaussian convolution sin(angle)*sin(2*angle) from 0 to pi
double convoluted_2_sin_sin(double angle, double sigma){
    double b = -angle/(sqrt(2.0)*sigma) + MY_PI/(sqrt(2.0)*sigma);
    double c = -angle/(sqrt(2.0)*sigma);
    double a = 3.0*sigma/sqrt(2.0);
    double sigma_2 = sigma*sigma;
    double result = 0.0;
    std::complex<double> arg1(b, a);
    std::complex<double> arg2(c, a);
    std::complex<double> erf1 = 1.0-cErrF_2(arg1);
    std::complex<double> erf2 = 1.0-cErrF_2(arg2);
    result = 1.0/4.0*sin(3.0*angle)*exp(-4.5*sigma_2)
            *(-erf1.imag()+erf2.imag())
            +1.0/4.0*cos(3.0*angle)*exp(-4.5*sigma_2)
            *(-erf1.real()+erf2.real());

    a = sigma/sqrt(2.0);
    arg1 = std::complex<double>(b, a);
    arg2 = std::complex<double>(c, a);

    erf1 = 1.0-cErrF_2(arg1);
    erf2 = 1.0-cErrF_2(arg2);

    result += 1.0/4.0*sin(angle)*exp(-0.5*sigma_2)
            *(+erf1.imag()-erf2.imag())
            +1.0/4.0*cos(angle)*exp(-0.5*sigma_2)
            *(+erf1.real()-erf2.real());
    return result;//please recheck //I tried but gave up quickly as there is NOT A SINGLE COMMENT ANYHWERE
    //TODO
}

//gaussian convolution sin(angle)^3 from 0 to pi
double convoluted_sin_3(double angle, double sigma){
    double b = -angle/sqrt(2.0)/sigma + TMath::Pi()/sqrt(2.0)/sigma;
    double c = -angle/sqrt(2.0)/sigma;//changed the sign here, more consistent
    double a = 3.0*sigma/sqrt(2.0);
    double sigma_2 = sigma*sigma;
    double result = 0.0;
    std::complex<double> arg1(b, a);
    std::complex<double> arg2(c, a);

    std::complex<double> erf1 = 1.0-cErrF_2(arg1);
    std::complex<double> erf2 = 1.0-cErrF_2(arg2);

    result = 1.0/8.0*sin(3.0*angle)*exp(-4.5*sigma_2)
            *(-erf1.real()+erf2.real())
            +1.0/8.0*cos(3.0*angle)*exp(-4.5*sigma_2)
            *(erf1.imag()-erf2.imag());

    a = sigma/sqrt(2.0);
    arg1 = std::complex<double>(b, a);
    arg2 = std::complex<double>(c, a);

    erf1 = 1.0-cErrF_2(arg1);
    erf2 = 1.0-cErrF_2(arg2);

    result += 3.0/8.0*sin(angle)*exp(-0.5*sigma_2)
            *(erf1.real()-erf2.real())
            +3.0/8.0*cos(angle)*exp(-0.5*sigma_2)
            *(-erf1.imag()+erf2.imag());
    return result;//this should be correct now->recheck //TODO//I tried but gave up quickly as there is NOT A SINGLE COMMENT ANYHWERE
    //Also it is not used anywhere, so why bother

}

//calculates the gaussian convolution sin(angle)^2 from 0 to pi
double convoluted_sin_2(double angle, double sigma){
    double b = -angle/sqrt(2.0)/sigma + TMath::Pi()/sqrt(2.0)/sigma;
    double c = -angle/sqrt(2.0)/sigma;//changed the sign here, more consistent
    double a = sigma*sqrt(2.0);//!!!
    double sigma_2 = sigma*sigma;
    double result = 0.0;
    std::complex<double> arg1(b, a);
    std::complex<double> arg2(c, a);
    std::complex<double> erf1 = 1.0-cErrF_2(arg1);
    std::complex<double> erf2 = 1.0-cErrF_2(arg2);
    result = 0.25*sin(2.0*angle)*exp(-2.0*sigma_2)
            *(-erf1.imag()+erf2.imag())
            +0.25*cos(2.0*angle)*exp(-2.0*sigma_2)
            *(-erf1.real()+erf2.real())
            -0.25*erf(-b)
            +0.25*erf(-c);
    return result;
}

//calculates the gaussian convolution cos(2*angle) from 0 to pi
double convoluted_2_cos(double angle, double sigma){
    double b = -angle/sqrt(2.0)/sigma + TMath::Pi()/sqrt(2.0)/sigma;
    double c = -angle/sqrt(2.0)/sigma;//changed the sign here, more consistent
    double a = sigma*sqrt(2.0);//!!!
    double sigma_2 = sigma*sigma;
    double result = 0.0;
    std::complex<double> arg1(b, a);
    std::complex<double> arg2(c, a);
    std::complex<double> erf1 = 1.0-cErrF_2(arg1);
    std::complex<double> erf2 = 1.0-cErrF_2(arg2);
    result =
            0.5*sin(2.0*angle)*exp(-2.0*sigma_2)
            *(erf1.imag()-erf2.imag())
            +0.5*cos(2.0*angle)*exp(-2.0*sigma_2)
            *(erf1.real()-erf2.real());
    return result;
}

//calculates the gaussian convolution sin(2*angle) from 0 to pi
double convoluted_2_sin(double angle, double sigma){
    double b = -angle/sqrt(2.0)/sigma + TMath::Pi()/sqrt(2.0)/sigma;
    double c = -angle/sqrt(2.0)/sigma;//changed the sign here, more consistent
    double a = sigma*sqrt(2.0);//rechcek this for all other convs!
    double sigma_2 = sigma*sigma;
    double result = 0.0;
    std::complex<double> arg1(b, a);
    std::complex<double> arg2(c, a);
    std::complex<double> erf1 = 1.0-cErrF_2(arg1);
    std::complex<double> erf2 = 1.0-cErrF_2(arg2);
    result = 0.5*sin(2.0*angle)*exp(-2.0*sigma_2)
            *(erf1.real()-erf2.real())//ok
            +0.5*cos(2.0*angle)*exp(-2.0*sigma_2)
            *(-erf1.imag()+erf2.imag());

    return result;
}

//integrate(sqrt(alpha/%pi)*sin(d)*exp(-alpha*(t-d)^2),d,-inf,inf);
//integrate(1/sqrt(2*%pi)/sigma*sin(d)*exp(-(t-d)^2/2/sigma^2),d,-inf,inf); why does this not work?
double inf_convoluted_sin(double angle, double sigma){
    //%e^-(1/(4*alpha))*sin(t)
    return exp(-0.5*sigma*sigma)*sin(angle);
}

//integrate(sqrt(alpha/%pi)*cos(d)*exp(-alpha*(t-d)^2),d,-inf,inf);
double inf_convoluted_cos(double angle, double sigma){
    // %e^-(1/(4*alpha))*cos(t)
    return exp(-0.5*sigma*sigma)*cos(angle);
}

//integrate(sqrt(alpha/%pi)*cos(d)^2*exp(-alpha*(t-d)^2),d,-inf,inf);
double inf_convoluted_cos_2(double angle, double sigma){
    //%e^-(1/alpha)*cos(2*t)/2+1/2
    return 0.5*exp(-2.0*sigma*sigma)*cos(2.0*angle)+0.5;
}

//integrate(sqrt(alpha/%pi)*sin(d)^2*exp(-alpha*(t-d)^2),d,-inf,inf);
double inf_convoluted_sin_2(double angle, double sigma){
    // 1/2-%e^-(1/alpha)*cos(2*t)/2
    return 0.5-0.5*exp(-2.0*sigma*sigma)*cos(2.0*angle);
}

//integrate(sqrt(alpha/%pi)*sin(2*d)*exp(-alpha*(t-d)^2),d,-inf,inf);
double inf_convoluted_2_sin(double angle, double sigma){
    //%e^-(1/alpha)*sin(2*t)
    return exp(-2.0*sigma*sigma)*sin(2.0*angle);
}

//string(expand(integrate(integrate(sqrt(alpha/%pi)*exp(-alpha*(t-d)^2),d,-inf,inf),t)));
double int_inf_convoluted_const(double angle, double sigma){
    return angle;
}

//string(expand(integrate(integrate(sqrt(alpha/%pi)*sin(d)*exp(-alpha*(t-d)^2),d,-inf,inf),t)));
double int_inf_convoluted_sin(double angle, double sigma){
    //-%e^-(1/(4*alpha))*cos(t)
    return -exp(-0.5*sigma*sigma)*cos(angle);
}

//string(expand(integrate(integrate(sqrt(alpha/%pi)*cos(d)*exp(-alpha*(t-d)^2),d,-inf,inf),t)));
double int_inf_convoluted_cos(double angle, double sigma){
    //%e^-(1/(4*alpha))*sin(t)
    return exp(-0.5*sigma*sigma)*sin(angle);
}

//string(expand(integrate(integrate(sqrt(alpha/%pi)*cos(d)^2*exp(-alpha*(t-d)^2),d,-inf,inf),t)));
double int_inf_convoluted_cos_2(double angle, double sigma){
    //%e^-(1/alpha)*sin(2*t)/4+t/2
    return 0.25*exp(-2.0*sigma*sigma)*sin(2.0*angle)+0.5*angle;
}

//string(expand(integrate(integrate(sqrt(alpha/%pi)*sin(d)^2*exp(-alpha*(t-d)^2),d,-inf,inf),t)));
double int_inf_convoluted_sin_2(double angle, double sigma){
    //t/2-%e^-(1/alpha)*sin(2*t)/4
    return 0.5*angle-0.25*exp(-2.0*sigma*sigma)*sin(2.0*angle);
}

//string(expand(integrate(integrate(sqrt(alpha/%pi)*sin(2*d)*exp(-alpha*(t-d)^2),d,-inf,inf),t)));
double int_inf_convoluted_2_sin(double angle, double sigma){
    // -%e^-(1/alpha)*cos(2*t)/2
    return -0.5*exp(-2.0*sigma*sigma)*cos(2.0*angle);
}

//expand(integrate(integrate(sqrt(alpha/%pi)*sin(d)*exp(-alpha*(t-d)^2),d,0,%pi), t));
double int_convoluted_sin(double angle, double sigma){
    double alpha = 1.0/(2.0*sigma*sigma);
    double sa = sqrt(alpha);
    double sat = sa*angle;
    double sapi = sa*TMath::Pi();
    double overtwosa = 1.0/(2.0*sa);
    double expa = exp(-1.0/(4.0*alpha));
    double cost = cos(angle);
    double sint = sin(angle);
    double result = 0.0;
    result += 0.5*expa*cost*ErrF_2(std::complex<double>(sat - sapi, overtwosa)).real();
    result += -0.5*expa*sint*ErrF_2(std::complex<double>(sat - sapi, overtwosa)).imag();
    result += -0.5*expa*cost*ErrF_2(std::complex<double>(sat, overtwosa)).real();
    result += 0.5*expa*sint*ErrF_2(std::complex<double>(sat, overtwosa)).imag();
    result += 0.5*erf(sat - sapi);
    result += 0.5*erf(sat);
    return result;
}

double int_convoluted_cos_sin(double angle, double sigma){
    return 0.5*int_convoluted_2_sin(angle, sigma);
}

//expand(integrate(integrate(sqrt(alpha/%pi)*cos(d)^2*sin(d)*exp(-alpha*(t-d)^2),d,0,%pi), t));
double int_convoluted_cos_2_sin(double angle, double sigma){
    double alpha = 1.0/(2.0*sigma*sigma);
    double sa = sqrt(alpha);
    double sat = sa*angle;
    double sapi = sa*TMath::Pi();
    double overtwosa = 1.0/(2.0*sa);
    double threeovertwosa = 3.0/(2.0*sa);
    double expa = exp(-1.0/(4.0*alpha));
    double expninea = exp(-9.0/(4.0*alpha));
    double cost = cos(angle);
    double sint = sin(angle);
    double costhreet = cos(3.0*angle);
    double sinthreet = sin(3.0*angle);
    double result = 0.0;

    result += -(1.0/24.0)*expninea*sinthreet*ErrF_2(std::complex<double>(sat - sapi, threeovertwosa)).imag();
    result += (1.0/24.0)*expninea*costhreet*ErrF_2(std::complex<double>(sat - sapi, threeovertwosa)).real();

    result += -(1.0/8.0)*expa*sint*ErrF_2(std::complex<double>(sat - sapi, overtwosa)).imag();
    result += (1.0/8.0)*expa*cost*ErrF_2(std::complex<double>(sat - sapi, overtwosa)).real();

    result += (1.0/24.0)*expninea*sinthreet*ErrF_2(std::complex<double>(sat, threeovertwosa)).imag();
    result += -(1.0/24.0)*expninea*costhreet*ErrF_2(std::complex<double>(sat, threeovertwosa)).real();

    result += (1.0/8.0)*expa*sint*ErrF_2(std::complex<double>(sat, overtwosa)).imag();
    result += -(1.0/8.0)*expa*cost*ErrF_2(std::complex<double>(sat, overtwosa)).real();

    result += (1.0/6.0)*erf(sat-sapi);
    result += (1.0/6.0)*erf(sat);

    return result;

}

//expand(integrate(integrate(sqrt(alpha/%pi)*sin(d)*sin(2*d)*exp(-alpha*(t-d)^2),d,0,%pi), t));
double int_convoluted_2_sin_sin(double angle, double sigma){
    double alpha = 1.0/(2.0*sigma*sigma);
    double sa = sqrt(alpha);
    double sat = sa*angle;
    double sapi = sa*TMath::Pi();
    double overtwosa = 1.0/(2.0*sa);
    double threeovertwosa = 3.0/(2.0*sa);
    double expa = exp(-1.0/(4.0*alpha));
    double expninea = exp(-9.0/(4.0*alpha));
    double cost = cos(angle);
    double sint = sin(angle);
    double costhreet = cos(3.0*angle);
    double sinthreet = sin(3.0*angle);
    double result = 0.0;

    result += (1.0/12.0)*expninea*sinthreet*ErrF_2(std::complex<double>(sat - sapi, threeovertwosa)).real();
    result += (1.0/12.0)*expninea*costhreet*ErrF_2(std::complex<double>(sat - sapi, threeovertwosa)).imag();

    result += -(1.0/4.0)*expa*sint*ErrF_2(std::complex<double>(sat - sapi, overtwosa)).real();
    result += -(1.0/4.0)*expa*cost*ErrF_2(std::complex<double>(sat - sapi, overtwosa)).imag();

    result += -(1.0/12.0)*expninea*sinthreet*ErrF_2(std::complex<double>(sat, threeovertwosa)).real();
    result += -(1.0/12.0)*expninea*costhreet*ErrF_2(std::complex<double>(sat, threeovertwosa)).imag();

    result += (1.0/4.0)*expa*sint*ErrF_2(std::complex<double>(sat, overtwosa)).real();
    result += (1.0/4.0)*expa*cost*ErrF_2(std::complex<double>(sat, overtwosa)).imag();

    return result;
}

//expand(integrate(integrate(sqrt(alpha/%pi)*sin(d)^3*exp(-alpha*(t-d)^2),d,0,%pi), t));
double int_convoluted_sin_3(double angle, double sigma){
    double alpha = 1.0/(2.0*sigma*sigma);
    double sa = sqrt(alpha);
    double sat = sa*angle;
    double sapi = sa*TMath::Pi();
    double overtwosa = 1.0/(2.0*sa);
    double threeovertwosa = 3.0/(2.0*sa);
    double expa = exp(-1.0/(4.0*alpha));
    double expninea = exp(-9.0/(4.0*alpha));
    double cost = cos(angle);
    double sint = sin(angle);
    double costhreet = cos(3.0*angle);
    double sinthreet = sin(3.0*angle);
    double result = 0.0;
    result += (1.0/24.0)*expninea*sinthreet*ErrF_2(std::complex<double>(sat - sapi, threeovertwosa)).imag();
    result += -(1.0/24.0)*expninea*costhreet*ErrF_2(std::complex<double>(sat - sapi, threeovertwosa)).real();
    result += -(3.0/8.0)*expa*sint*ErrF_2(std::complex<double>(sat - sapi, overtwosa)).imag();
    result += (3.0/8.0)*expa*cost*ErrF_2(std::complex<double>(sat - sapi, overtwosa)).real();

    result += -(1.0/24.0)*expninea*sinthreet*ErrF_2(std::complex<double>(sat, threeovertwosa)).imag();
    result += (1.0/24.0)*expninea*costhreet*ErrF_2(std::complex<double>(sat, threeovertwosa)).real();
    result += +(3.0/8.0)*expa*sint*ErrF_2(std::complex<double>(sat, overtwosa)).imag();
    result += -(3.0/8.0)*expa*cost*ErrF_2(std::complex<double>(sat, overtwosa)).real();

    result += 1.0/3.0*erf(sat-sapi);
    result += 1.0/3.0*erf(sat);

    return result;
}

//expand(integrate(integrate(sqrt(alpha/%pi)*sin(d)^2*exp(-alpha*(t-d)^2),d,0,%pi), t));
double int_convoluted_sin_2(double angle, double sigma){//used by s wave

    double t = angle;
    double alpha = 1.0/(2.0*sigma*sigma);
    double sa = sqrt(alpha);
    double sat = sa*angle;
    double sapi = sa*TMath::Pi();
    double oversa = 1.0/(sa);
    double expa = exp(-1.0/(alpha));
    double costwot = cos(2.0*angle);
    double sintwot = sin(2.0*angle);
    double pi = TMath::Pi();
    double spi = sqrt(pi);
    double result = 0.0;
    result += (1.0/8.0)*expa*sintwot*ErrF_2(std::complex<double>(sat - sapi, oversa)).real();
    result += (1.0/8.0)*expa*costwot*ErrF_2(std::complex<double>(sat - sapi, oversa)).imag();

    result += -(1.0/8.0)*expa*sintwot*ErrF_2(std::complex<double>(sat, oversa)).real();
    result += -(1.0/8.0)*expa*costwot*ErrF_2(std::complex<double>(sat, oversa)).imag();

    result += -(t/4.0)*erf(sat-sapi);
    result += (pi/4.0)*erf(sat-sapi);
    result += (t/4.0)*erf(sat);

    result += -(1.0/(4.0*sa*spi))*exp(-alpha*t*t+2.0*pi*alpha*t-pi*pi*alpha);
    result += (1.0/(4.0*sa*spi))*exp(-alpha*t*t);

    return result;

}

//expand(integrate(integrate(sqrt(alpha/%pi)*sin(2*d)*exp(-alpha*(t-d)^2),d,0,%pi), t));
double int_convoluted_2_sin(double angle, double sigma){//used by s wave

    double alpha = 1.0/(2.0*sigma*sigma);
    double sa = sqrt(alpha);
    double sat = sa*angle;
    double sapi = sa*TMath::Pi();
    double oversa = 1.0/(sa);
    double expa = exp(-1.0/(alpha));
    double costwot = cos(2.0*angle);
    double sintwot = sin(2.0*angle);
    double result = 0.0;
    result += -(1.0/4.0)*expa*sintwot*ErrF_2(std::complex<double>(sat - sapi, oversa)).imag();
    result += (1.0/4.0)*expa*costwot*ErrF_2(std::complex<double>(sat - sapi, oversa)).real();

    result += (1.0/4.0)*expa*sintwot*ErrF_2(std::complex<double>(sat, oversa)).imag();
    result += -(1.0/4.0)*expa*costwot*ErrF_2(std::complex<double>(sat, oversa)).real();

    result += -(1.0/4.0)*erf(sat-sapi);
    result += (1.0/4.0)*erf(sat);

    return result;

}

//for n:0 thru 8 do print("n=",n,": ",expand(integrate(sqrt(alpha/%pi)*exp(-alpha*(t-d)^2)*cos(t)^n, t, -inf, inf)));
//for k:0 thru n/2 do print(expand(1/2^(n-1)*binomial(n,k)*exp(-1/4/alpha*(n-2*k)^2)*cos((n-2*k)*t)));
//for k:0 thru 8/2 do print(expand(1/(2^(8-1))*binomial(8,k)*exp(-1/4/alpha*(8-2*k)^2)*cos((8-2*k)*t)));
//for k:0 thru 8/2 do print(expand(1/(2^(8))*binomial(8,k)*exp(-1/4/alpha*(8-2*k)^2)*cos((8-2*k)*t)));

//for k:0 thru 8/2 do print(expand(1/2^(8)*binomial(8-1,k)*exp(-1/4/alpha*(8-2*k)^2)*cos((8-2*k)*t)));

//for n:0 thru 20 do print("n=",n,": ",string(expand(integrate(integrate(sqrt(alpha/%pi)*exp(-alpha*(t)^2)*cos(t+d)^n*sin(d), t, -inf, inf), d, 0, %pi))));
double int_convoluted_sin_cos_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 1)
        return 0.0;
    switch (n) {
    case 0: return 2;
        break;
    case 2: return 1-exp(-1/alpha)/3;
        break;
    case 4: return -exp(-1/alpha)/3-exp(-4/alpha)/60+3.0/4.0;
        break;
    case 6: return -5*exp(-1/alpha)/16-exp(-4/alpha)/40-exp(-9/alpha)/560+5.0/8.0;
        break;
    case 8: return -7*exp(-1/alpha)/24-7*exp(-4/alpha)/240-exp(-9/alpha)/280-exp(-16/alpha)/4032+35.0/64.0;
        break;
    case 10: return -35*exp(-1/alpha)/128-exp(-4/alpha)/32-9*exp(-9/alpha)/1792-5*exp(-16/alpha)/8064-exp(-25/alpha)/25344+63.0/128.0;
        break;
    case 12: return -33*exp(-1/alpha)/128-33*exp(-4/alpha)/1024-11*exp(-9/alpha)/1792-11*exp(-16/alpha)/10752-exp(-25/alpha)/8448-exp(-36/alpha)/146432+231.0/512.0;
        break;
    case 14: return -1001*exp(-1/alpha)/4096-1001*exp(-4/alpha)/30720-143*exp(-9/alpha)/20480-13*exp(-16/alpha)/9216-91*exp(-25/alpha)/405504-7*exp(-36/alpha)/292864-exp(-49/alpha)/798720+429.0/1024.0;
        break;
    case 16: return -715*exp(-1/alpha)/3072-1001*exp(-4/alpha)/30720-39*exp(-9/alpha)/5120-65*exp(-16/alpha)/36864-35*exp(-25/alpha)/101376-15*exp(-36/alpha)/292864-exp(-49/alpha)/199680-exp(-64/alpha)/4177920+6435.0/16384.0;
        break;
    case 18: return -7293*exp(-1/alpha)/32768-663*exp(-4/alpha)/20480-663*exp(-9/alpha)/81920-17*exp(-16/alpha)/8192-85*exp(-25/alpha)/180224-51*exp(-36/alpha)/585728-51*exp(-49/alpha)/4259840-3*exp(-64/alpha)/2785280-exp(-81/alpha)/21168128+12155.0/32768.0;
        break;
    case 20: return -20995*exp(-1/alpha)/98304-4199*exp(-4/alpha)/131072-969*exp(-9/alpha)/114688-1615*exp(-16/alpha)/688128-323*exp(-25/alpha)/540672-4845*exp(-36/alpha)/37486592-19*exp(-49/alpha)/851968-19*exp(-64/alpha)/6684672-5*exp(-81/alpha)/21168128-exp(-100/alpha)/104595456+46189.0/131072.0;
        break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("n=",n,": ",string(expand(integrate(integrate(sqrt(alpha/%pi)*exp(-alpha*(t)^2)*cos(t+d)^n*sin(d)^3, t, -inf, inf), d, 0, %pi))));
double int_convoluted_sin_3_cos_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 1)
        return 0.0;
    switch (n) {
    case 0: return 4.0/3.0 ;
        break;
    case 2: return 2.0/3.0-2*exp(-1/alpha)/5 ;
        break;
    case 4: return -2*exp(-1/alpha)/5+exp(-4/alpha)/70+1.0/2.0 ;
        break;
    case 6: return -3*exp(-1/alpha)/8+3*exp(-4/alpha)/140+exp(-9/alpha)/2520+5.0/12.0 ;
        break;
    case 8: return -7*exp(-1/alpha)/20+exp(-4/alpha)/40+exp(-9/alpha)/1260+exp(-16/alpha)/36960+35.0/96.0 ;
        break;
    case 10: return -21*exp(-1/alpha)/64+3*exp(-4/alpha)/112+exp(-9/alpha)/896+exp(-16/alpha)/14784+exp(-25/alpha)/384384+21.0/64.0 ;
        break;
    case 12: return -99*exp(-1/alpha)/320+99*exp(-4/alpha)/3584+11*exp(-9/alpha)/8064+exp(-16/alpha)/8960+exp(-25/alpha)/128128+exp(-36/alpha)/3294720+77.0/256.0 ;
        break;
    case 14: return -3003*exp(-1/alpha)/10240+143*exp(-4/alpha)/5120+143*exp(-9/alpha)/92160+13*exp(-16/alpha)/84480+exp(-25/alpha)/67584+7*exp(-36/alpha)/6589440+exp(-49/alpha)/24893440+143.0/512.0 ;
        break;
    case 16: return -143*exp(-1/alpha)/512+143*exp(-4/alpha)/5120+13*exp(-9/alpha)/7680+13*exp(-16/alpha)/67584+5*exp(-25/alpha)/219648+exp(-36/alpha)/439296+exp(-49/alpha)/6223360+exp(-64/alpha)/171991040+2145.0/8192.0 ;
        break;
    case 18: return -21879*exp(-1/alpha)/81920+1989*exp(-4/alpha)/71680+221*exp(-9/alpha)/122880+51*exp(-16/alpha)/225280+255*exp(-25/alpha)/8200192+17*exp(-36/alpha)/4392960+9*exp(-49/alpha)/23429120+9*exp(-64/alpha)/343982080+exp(-81/alpha)/1111326720+12155.0/49152.0 ;
        break;
    case 20: return -4199*exp(-1/alpha)/16384+12597*exp(-4/alpha)/458752+323*exp(-9/alpha)/172032+323*exp(-16/alpha)/1261568+323*exp(-25/alpha)/8200192+323*exp(-36/alpha)/56229888+57*exp(-49/alpha)/79659008.0+exp(-64/alpha)/14483456.0+exp(-81/alpha)/222265344.0+exp(-100/alpha)/6816137216.0+46189.0/196608.0 ;
        break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("n=",n,": ",string(expand(integrate(integrate(sqrt(alpha/%pi)*exp(-alpha*(t)^2)*cos(t+d)^n*sin(d)*cos(d)^2, t, -inf, inf), d, 0, %pi))));
double int_convoluted_cos_2_sin_cos_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 1)
        return 0.0;
    switch (n) {
    case 0: return 2.0/3.0 ;
        break;
    case 2: return exp(-1/alpha)/15+1.0/3.0 ;
        break;
    case 4: return exp(-1/alpha)/15-13*exp(-4/alpha)/420+1.0/4.0 ;
        break;
    case 6: return exp(-1/alpha)/16-13*exp(-4/alpha)/280-11*exp(-9/alpha)/5040+5.0/24.0 ;
        break;
    case 8: return 7*exp(-1/alpha)/120-13*exp(-4/alpha)/240-11*exp(-9/alpha)/2520-61*exp(-16/alpha)/221760+35.0/192.0 ;
        break;
    case 10: return 7*exp(-1/alpha)/128-13*exp(-4/alpha)/224-11*exp(-9/alpha)/1792-61*exp(-16/alpha)/88704-97*exp(-25/alpha)/2306304+21.0/128.0 ;
        break;
    case 12: return 33*exp(-1/alpha)/640-429*exp(-4/alpha)/7168-121*exp(-9/alpha)/16128-61*exp(-16/alpha)/53760-97*exp(-25/alpha)/768768-47*exp(-36/alpha)/6589440+77.0/512.0 ;
        break;
    case 14: return 1001*exp(-1/alpha)/20480-1859*exp(-4/alpha)/30720-1573*exp(-9/alpha)/184320-793*exp(-16/alpha)/506880-97*exp(-25/alpha)/405504-329*exp(-36/alpha)/13178880-193*exp(-49/alpha)/149360640+143.0/1024.0 ;
        break;
    case 16: return 143*exp(-1/alpha)/3072-1859*exp(-4/alpha)/30720-143*exp(-9/alpha)/15360-793*exp(-16/alpha)/405504-485*exp(-25/alpha)/1317888-47*exp(-36/alpha)/878592-193*exp(-49/alpha)/37340160-253*exp(-64/alpha)/1031946240+2145.0/16384.0 ;
        break;
    case 18: return 7293*exp(-1/alpha)/163840-8619*exp(-4/alpha)/143360-2431*exp(-9/alpha)/245760-1037*exp(-16/alpha)/450560-8245*exp(-25/alpha)/16400384-799*exp(-36/alpha)/8785920.0-579*exp(-49/alpha)/46858240.0-759*exp(-64/alpha)/687964160.0-107*exp(-81/alpha)/2222653440.0+12155.0/98304.0 ;
        break;
    case 20: return 4199*exp(-1/alpha)/98304-54587*exp(-4/alpha)/917504-3553*exp(-9/alpha)/344064-19703*exp(-16/alpha)/7569408-31331*exp(-25/alpha)/49201152-15181*exp(-36/alpha)/112459776.0-3667*exp(-49/alpha)/159318016.0-253*exp(-64/alpha)/86900736.0-107*exp(-81/alpha)/444530688.0-397*exp(-100/alpha)/40896823296.0+46189.0/393216.0 ;
        break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("n=",n,": ",string(expand(integrate(integrate(sqrt(alpha/%pi)*exp(-alpha*(t)^2)*cos(t+d)^n*sin(2*d)*sin(d), t, -inf, inf), d, 0, %pi))));
double int_convoluted_2_sin_sin_cos_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double pi = TMath::Pi();
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 0)
        return 0.0;
    switch (n) {
    case 1: return pi*exp(-1/(4*alpha))/4 ;
        break;
    case 3: return 3*pi*exp(-1/(4*alpha))/16-pi*exp(-9/(4*alpha))/16 ;
        break;
    case 5: return 5*pi*exp(-1/(4*alpha))/32-5*pi*exp(-9/(4*alpha))/64 ;
        break;
    case 7: return 35*pi*exp(-1/(4*alpha))/256-21*pi*exp(-9/(4*alpha))/256 ;
        break;
    case 9: return 63*pi*exp(-1/(4*alpha))/512-21*pi*exp(-9/(4*alpha))/256 ;
        break;
    case 11: return 231*pi*exp(-1/(4*alpha))/2048-165*pi*exp(-9/(4*alpha))/2048 ;
        break;
    case 13: return 429*pi*exp(-1/(4*alpha))/4096-1287*pi*exp(-9/(4*alpha))/16384 ;
        break;
    case 15: return 6435*pi*exp(-1/(4*alpha))/65536-5005*pi*exp(-9/(4*alpha))/65536 ;
        break;
    case 17: return 12155*pi*exp(-1/(4*alpha))/131072-2431*pi*exp(-9/(4*alpha))/32768 ;
        break;
    case 19: return 46189*pi*exp(-1/(4*alpha))/524288-37791*pi*exp(-9/(4*alpha))/524288 ;
        break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("n=",n,": ",string(expand(integrate(integrate(sqrt(alpha/%pi)*exp(-alpha*(t)^2)*cos(t+d)^n*sin(d)*cos(d), t, -inf, inf), d, 0, %pi))));
double int_convoluted_cos_sin_cos_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 0)
        return 0.0;
    switch (n) {
    case 1: return 2*exp(-1/(4*alpha))/3 ;
        break;
    case 3: return exp(-1/(4*alpha))/2-exp(-9/(4*alpha))/10 ;
        break;
    case 5: return 5*exp(-1/(4*alpha))/12-exp(-9/(4*alpha))/8-exp(-25/(4*alpha))/168 ;
        break;
    case 7: return 35*exp(-1/(4*alpha))/96-21*exp(-9/(4*alpha))/160-exp(-25/(4*alpha))/96-exp(-49/(4*alpha))/1440 ;
        break;
    case 9: return 21*exp(-1/(4*alpha))/64-21*exp(-9/(4*alpha))/160-3*exp(-25/(4*alpha))/224-exp(-49/(4*alpha))/640-exp(-81/(4*alpha))/9856 ;
        break;
    case 11: return 77*exp(-1/(4*alpha))/256-33*exp(-9/(4*alpha))/256-55*exp(-25/(4*alpha))/3584-11*exp(-49/(4*alpha))/4608-exp(-81/(4*alpha))/3584-exp(-121/(4*alpha))/59904 ;
        break;
    case 13: return 143*exp(-1/(4*alpha))/512-1287*exp(-9/(4*alpha))/10240-715*exp(-25/(4*alpha))/43008-143*exp(-49/(4*alpha))/46080-39*exp(-81/(4*alpha))/78848-exp(-121/(4*alpha))/18432-exp(-169/(4*alpha))/337920 ;
        break;
    case 15: return 2145*exp(-1/(4*alpha))/8192-1001*exp(-9/(4*alpha))/8192-143*exp(-25/(4*alpha))/8192-91*exp(-49/(4*alpha))/24576-65*exp(-81/(4*alpha))/90112-35*exp(-121/(4*alpha))/319488-exp(-169/(4*alpha))/90112-exp(-225/(4*alpha))/1810432 ;
        break;
    case 17: return 12155*exp(-1/(4*alpha))/49152-2431*exp(-9/(4*alpha))/20480-221*exp(-25/(4*alpha))/12288-1547*exp(-49/(4*alpha))/368640-85*exp(-81/(4*alpha))/90112-85*exp(-121/(4*alpha))/479232-17*exp(-169/(4*alpha))/675840-exp(-225/(4*alpha))/425984-exp(-289/(4*alpha))/9338880 ;
        break;
    case 19: return 46189*exp(-1/(4*alpha))/196608-37791*exp(-9/(4*alpha))/327680-4199*exp(-25/(4*alpha))/229376-2261*exp(-49/(4*alpha))/491520-2907*exp(-81/(4*alpha))/2523136-323*exp(-121/(4*alpha))/1277952-323*exp(-169/(4*alpha))/7208960-171*exp(-225/(4*alpha))/28966912-exp(-289/(4*alpha))/1966080-exp(-361/(4*alpha))/46792704 ;
        break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("n=",n,": ",string(expand(integrate(integrate(sqrt(alpha/%pi)*exp(-alpha*(t)^2)*cos(t+d)^n*sin(d)^2, t, -inf, inf), d, 0, %pi))));
double int_convoluted_sin_2_cos_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double pi = TMath::Pi();
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 1)
        return 0.0;
    switch (n) {
    case 0: return pi/2 ;
        break;
    case 2: return pi/4-pi*exp(-1/alpha)/8 ;
        break;
    case 4: return 3*pi/16-pi*exp(-1/alpha)/8 ;
        break;
    case 6: return 5*pi/32-15*pi*exp(-1/alpha)/128 ;
        break;
    case 8: return 35*pi/256-7*pi*exp(-1/alpha)/64 ;
        break;
    case 10: return 63*pi/512-105*pi*exp(-1/alpha)/1024 ;
        break;
    case 12: return 231*pi/2048-99*pi*exp(-1/alpha)/1024 ;
        break;
    case 14: return 429*pi/4096-3003*pi*exp(-1/alpha)/32768 ;
        break;
    case 16: return 6435*pi/65536-715*pi*exp(-1/alpha)/8192 ;
        break;
    case 18: return 12155*pi/131072-21879*pi*exp(-1/alpha)/262144 ;
        break;
    case 20: return 46189*pi/524288-20995*pi*exp(-1/alpha)/262144 ;
        break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("case ",n,": return ",string(float(expand(integrate(integrate(sqrt(alpha/%pi)*(phi+d)^n*exp(-alpha*phi^2), phi, -inf, inf), d, -%pi, %pi)))), "; break;");
double int_inf_convoluted_const_x_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 1)
        return 0.0;
    switch (n) {
    case  0 : return  6.283185307179586 ; break;
    case  2 : return  3.141592653589793/alpha+20.67085112019988 ; break;
    case  4 : return  62.01255336059963/alpha+4.71238898038469/pow(alpha,2)+122.4078739141126 ; break;
    case  6 : return  918.0590543558442/alpha+232.5470751022486/pow(alpha,2)+11.78097245096172/pow(alpha,3)+862.9409222219404 ; break;
    case  8 : return  12081.17291110717/alpha+6426.41338049091/pow(alpha,2)+1085.219683810494/pow(alpha,3)+41.23340357836604/pow(alpha,4)+6624.244296321378 ; break;
    case  10 : return  149045.496667231/alpha+135913.1952499556/pow(alpha,2)+48198.10035368182/pow(alpha,3)+6104.360721434026/pow(alpha,4)+185.5503161026471/pow(alpha,5)+53491.63963161646 ; break;
    case  12 : return
                1765224.107843343/alpha+2459250.695009312/pow(alpha,2)+1495045.147749512/pow(alpha,3)+397634.327917875/pow(alpha,4)+40288.78076146457/pow(alpha,5)+1020.526738564559/pow(alpha,6)+446719.5800943512
                ; break;
    case  14 : return  2.0325740894292977E+7/alpha+4.0158848453436054E+7/pow(alpha,2)+3.7298635540974565E+7/pow(alpha,3)+1.7006138555650696E+7/pow(alpha,4)+3618472.384052663/pow(alpha,5)+305523.254107773/pow(alpha,6)+6633.423800669636/pow(alpha,7)+3821086.129251732 ; break;
    case  16 : return  2.2926516775510389E+8/alpha+6.0977222682878935E+8/pow(alpha,2)+8.0317696906872118E+8/pow(alpha,3)+5.5947953311461842E+8/pow(alpha,4)+2.0407366266780835E+8/pow(alpha,5)+3.6184723840526626E+7/pow(alpha,6)+2618770.749495197/pow(alpha,7)+49750.67850502227/pow(alpha,8)+3.3275831010180339E+7 ; break;
    case  18 : return  2.5456010722787962E+9/alpha+8.7693926666327229E+9/pow(alpha,2)+1.5549191784134127E+10/pow(alpha,3)+1.5360759533439291E+10/pow(alpha,4)+8.5600368566536617E+9/pow(alpha,5)+2.6019391990145564E+9/pow(alpha,6)+3.9544733911432672E+8/pow(alpha,7)+2.5041995292047825E+7/pow(alpha,8)+422880.7672926893/pow(alpha,9)+2.9384883679977304E+8 ; break;
    case  20 : return  2.7915639495978439E+10/alpha+1.2091605093324281E+11/pow(alpha,2)+2.7769743444336957E+11/pow(alpha,3)+3.6929330487318555E+11/pow(alpha,4)+2.9185443113534656E+11/pow(alpha,5)+1.3553391689701631E+11/pow(alpha,6)+3.5312031986626122E+10/pow(alpha,7)+4.6959371519826298E+9/pow(alpha,8)+2.6433217252717146E+8/pow(alpha,9)+4017367.289280548/pow(alpha,10)+2.623964937416502E+9
                ; break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("case ",n,": return ",string(float(expand(integrate(integrate(sqrt(alpha/%pi)*(phi+d)^n*exp(-alpha*phi^2)*cos(d)^2, phi, -inf, inf), d, -%pi, %pi)))), "; break;");
double int_inf_convoluted_cos_2_x_n(int n, double sigma)
{
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 1)
        return 0.0;
    switch (n) {
    case  0 : return  3.141592653589793 ; break;
    case  2 : return  1.570796326794897/alpha+11.90622188689484 ; break;
    case  4 : return  35.71866566068451/alpha+2.356194490192345/pow(alpha,2)+87.4978246569714 ; break;
    case  6 : return  656.2336849272856/alpha+133.9449962275669/pow(alpha,2)+5.890486225480862/pow(alpha,3)+693.2958305395289 ; break;
    case  8 : return  9706.141627553405/alpha+4593.635794490999/pow(alpha,2)+625.0766490619789/pow(alpha,3)+20.61670178918302/pow(alpha,4)+5687.153431714451 ; break;
    case  10 : return  127960.9522135751/alpha+109194.0933099758/pow(alpha,2)+34452.26845868249/pow(alpha,3)+3516.056150973631/pow(alpha,4)+92.77515805132357/pow(alpha,5)+47830.36426946412 ; break;
    case  12 : return  1578402.020892315/alpha+2111355.71152399/pow(alpha,2)+1201135.026409734/pow(alpha,3)+284231.2147841306/pow(alpha,4)+23205.97059642596/pow(alpha,5)+510.2633692822797/pow(alpha,6)+410181.8769982042
                ; break;
    case  14 : return  1.8663275403418235E+7/alpha+3.5908645975300178E+7/pow(alpha,2)+3.2022228291447181E+7/pow(alpha,3)+1.3662910925410721E+7/pow(alpha,4)+2586504.054535588/pow(alpha,5)+175978.6103562302/pow(alpha,6)+3316.711900334818/pow(alpha,7)+3573008.555500609 ; break;
    case  16 : return  2.1438051333003667E+8/alpha+5.5989826210254693E+8/pow(alpha,2)+7.1817291950600362E+8/pow(alpha,3)+4.8033342437170768E+8/pow(alpha,4)+1.6395493110492867E+8/pow(alpha,5)+2.5865040545355879E+7/pow(alpha,6)+1508388.088767688/pow(alpha,7)+24875.33925251113/pow(alpha,8)+3.1522569930157386E+7 ; break;
    case  18 : return  2.4114765996571231E+9/alpha+8.2000546348738604E+9/pow(alpha,2)+1.4277405683614956E+10/pow(alpha,3)+1.3735057085552319E+10/pow(alpha,4)+7.3491013928871279E+9/pow(alpha,5)+2.0904253715878406E+9/pow(alpha,6)+2.8266794310281783E+8/pow(alpha,7)+1.4423961098841013E+7/pow(alpha,8)+211440.3836463447/pow(alpha,9)+2.810488910215596E+8 ; break;
    case  20 : return  2.6699644647049049E+10/alpha+1.1454513848371289E+11/pow(alpha,2)+2.5966839677100656E+11/pow(alpha,3)+3.390883849858551E+11/pow(alpha,4)+2.6096608462549408E+11/pow(alpha,5)+1.1636077205404617E+11/pow(alpha,6)+2.8370058614406403E+10/pow(alpha,7)+3.3566818243459616E+9/pow(alpha,8)+1.5225292270998847E+8/pow(alpha,9)+2008683.644640274/pow(alpha,10)+2.527977317637641E+9
                ; break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("case ",n,": return ",string(float(expand(integrate(integrate(sqrt(alpha/%pi)*(phi+d)^n*exp(-alpha*phi^2)*sin(d)^2, phi, -inf, inf), d, -%pi, %pi)))), "; break;");
double int_inf_convoluted_sin_2_x_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 1)
        return 0.0;
    switch (n) {
    case  0 : return  3.141592653589793 ; break;
    case  2 : return  1.570796326794897/alpha+8.764629233305042 ; break;
    case  4 : return  26.29388769991513/alpha+2.356194490192345/pow(alpha,2)+34.91004925714116 ; break;
    case  6 : return  261.8253694285586/alpha+98.60207887468172/pow(alpha,2)+5.890486225480862/pow(alpha,3)+169.6450916824115 ; break;
    case  8 : return  2375.031283553761/alpha+1832.777585999911/pow(alpha,2)+460.1430347485147/pow(alpha,3)+20.61670178918302/pow(alpha,4)+937.0908646069279 ; break;
    case  10 : return  21084.54445365589/alpha+26719.10193997981/pow(alpha,2)+13745.83189499933/pow(alpha,3)+2588.304570460395/pow(alpha,4)+92.77515805132357/pow(alpha,5)+5661.275362152337 ; break;
    case  12 : return
                186822.0869510286/alpha+347894.9834853215/pow(alpha,2)+293910.121339778/pow(alpha,3)+113403.1131337445/pow(alpha,4)+17082.81016503861/pow(alpha,5)+510.2633692822797/pow(alpha,6)+36537.70309614705
                ; break;
    case  14 : return  1662465.490874743/alpha+4250202.478135873/pow(alpha,2)+5276407.249527384/pow(alpha,3)+3343227.630239975/pow(alpha,4)+1031968.329517075/pow(alpha,5)+129544.6437515428/pow(alpha,6)+3316.711900334818/pow(alpha,7)+248077.5737511227 ; break;
    case  16 : return  1.4884654425067216E+7/alpha+4.9873964726242363E+7/pow(alpha,2)+8.5004049562717617E+7/pow(alpha,3)+7.9146108742910743E+7/pow(alpha,4)+4.0118731562879689E+7/pow(alpha,5)+1.0319683295170747E+7/pow(alpha,6)+1110382.66072751/pow(alpha,7)+24875.33925251113/pow(alpha,8)+1753261.080022954 ; break;
    case  18 : return  1.3412447262167311E+8/alpha+5.693380317588625E+8/pow(alpha,2)+1.2717861005191698E+9/pow(alpha,3)+1.6257024478869734E+9/pow(alpha,4)+1.2109354637665339E+9/pow(alpha,5)+5.1151382742671597E+8/pow(alpha,6)+1.1277939601150887E+8/pow(alpha,7)+1.0618034193206811E+7/pow(alpha,8)+211440.3836463447/pow(alpha,9)+1.2799945778213412E+7 ; break;
    case  20 : return  1.21599484892939E+9/alpha+6.3709124495299149E+9/pow(alpha,2)+1.8029037672363007E+10/pow(alpha,3)+3.0204919887330444E+10/pow(alpha,4)+3.0888346509852478E+10/pow(alpha,5)+1.9173144842970131E+10/pow(alpha,6)+6.941973372219717E+9/pow(alpha,7)+1.339255327636668E+9/pow(alpha,8)+1.1207924981718299E+8/pow(alpha,9)+2008683.644640274/pow(alpha,10)+9.5987619778861046E+7 ; break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("case ",n,": return ",string(float(expand(integrate(integrate(sqrt(alpha/%pi)*(phi+d)^n*exp(-alpha*phi^2)*sin(d), phi, -inf, inf), d, -%pi, %pi)))), "; break;");
double int_inf_convoluted_sin_x_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 0)
        return 0.0;
    switch (n) {
    case  1 : return  6.283185307179586 ; break;
    case  3 : return  9.424777960769379/alpha+24.31344151752212 ; break;
    case  5 : return  121.5672075876106/alpha+23.56194490192345/pow(alpha,2)+125.7705392201204 ; break;
    case  7 : return  1320.590661811266/alpha+638.2278398349555/pow(alpha,2)+82.46680715673207/pow(alpha,3)+758.2238083085194 ; break;
    case  9 : return  13648.02854955342/alpha+11885.31595630138/pow(alpha,2)+3829.367039009733/pow(alpha,3)+371.1006322052943/pow(alpha,4)+5026.084468678731 ; break;
    case  11 : return  138217.3228886668/alpha+187660.3925563593/pow(alpha,2)+108948.7295994294/pow(alpha,3)+26326.89839319191/pow(alpha,4)+2041.053477129119/pow(alpha,5)+35538.744393114 ; break;
    case  13 : return
                1386011.031330336/alpha+2695237.796329141/pow(alpha,2)+2439585.103232659/pow(alpha,3)+1062250.113594437/pow(alpha,4)+205349.807466897/pow(alpha,5)+13266.84760133927/pow(alpha,6)+263310.4159052214
                ; break;
    case  15 : return  1.3823796834663689E+7/alpha+3.6382789572466373E+7/pow(alpha,2)+4.7166661435754895E+7/pow(alpha,3)+3.201955447992897E+7/pow(alpha,4)+1.1153626192741588E+7/pow(alpha,5)+1796810.815335348/pow(alpha,6)+99501.35701004454/pow(alpha,7)+2021104.600121215 ; break;
    case  17 : return  1.3743511271761036E+8/alpha+4.7000909238989449E+8/pow(alpha,2)+8.2467656364292908E+8/pow(alpha,3)+8.018332444078331E+8/pow(alpha,4)+4.3546594092703295E+8/pow(alpha,5)+1.2640776351773798E+8/pow(alpha,6)+1.7454733634686239E+7/pow(alpha,7)+845761.5345853786/pow(alpha,8)+1.5948676302624345E+7 ; break;
    case  19 : return  1.363611830458992E+9/alpha+5.8753510678547668E+9/pow(alpha,2)+1.3395259133206604E+10/pow(alpha,3)+1.7627461547833862E+10/pow(alpha,4)+1.3711348479374664E+10/pow(alpha,5)+6.2053896582101974E+9/pow(alpha,6)+1.5439805401095133E+9/pow(alpha,7)+1.865474657207092E+8/pow(alpha,8)+8034734.578561097/pow(alpha,9)+1.2868057735972023E+8 ; break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("case ",n,": return ",string(float(expand(integrate(integrate(sqrt(alpha/%pi)*(phi+d)^n*exp(-alpha*phi^2)*sin(2*d), phi, -inf, inf), d, -%pi, %pi)))), "; break;");
double int_inf_convoluted_2_sin_x_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 0)
        return 0.0;
    switch (n) {
    case  1 : return  -3.141592653589793 ; break;
    case  3 : return  -4.71238898038469/alpha-26.29388769991513 ; break;
    case  5 : return  -131.4694384995756/alpha-11.78097245096172/pow(alpha,2)-174.5502462857058 ; break;
    case  7 : return  -1832.777585999911/alpha-690.214552122772/pow(alpha,2)-41.23340357836604/pow(alpha,3)-1187.515641776881 ; break;
    case  9 : return  -21375.28155198386/alpha-16494.99827399919/pow(alpha,2)-4141.287312736632/pow(alpha,3)-185.5503161026471/pow(alpha,4)-8433.817781462349 ; break;
    case  11 : return  -231929.9889902145/alpha-293910.121339778/pow(alpha,2)-151204.1508449926/pow(alpha,3)-28471.35027506435/pow(alpha,4)-1020.526738564559/pow(alpha,5)-62274.02898367599 ; break;
    case  13 : return  -2428687.130363355/alpha-4522634.785309188/pow(alpha,2)-3820831.577417114/pow(alpha,3)-1474240.470738678/pow(alpha,4)-222076.5321455019/pow(alpha,5)-6633.423800669636/pow(alpha,6)-474990.140249928
                ; break;
    case  15 : return  -2.4936982363121182E+7/alpha-6.3753037172038078E+7/pow(alpha,2)-7.9146108742910743E+7/pow(alpha,3)-5.0148414453599617E+7/pow(alpha,4)-1.5479524942756122E+7/pow(alpha,5)-1943169.656273142/pow(alpha,6)-49750.67850502227/pow(alpha,7)-3721163.606266804 ; break;
    case  17 : return  -2.5303912522615099E+8/alpha-8.4785740034611607E+8/pow(alpha,2)-1.4450688425662041E+9/pow(alpha,3)-1.3454838486294813E+9/pow(alpha,4)-6.8201843656895471E+8/pow(alpha,5)-1.7543461601790273E+8/pow(alpha,6)-1.8876505232367665E+7/pow(alpha,7)-422880.7672926893/pow(alpha,8)-2.9805438360381901E+7 ; break;
    case  19 : return  -2.5483649798125954E+9/alpha-1.0817422603417984E+10/pow(alpha,2)-2.4163935909864288E+10/pow(alpha,3)-3.0888346509852478E+10/pow(alpha,4)-2.3007773811564163E+10/pow(alpha,5)-9.7187627211076069E+9/pow(alpha,6)-2.1428085242186687E+9/pow(alpha,7)-2.017426496709294E+8/pow(alpha,8)-4017367.289280548/pow(alpha,9)-2.4319896978524876E+8 ; break;
    }
    return 0.0;
}

//for n:0 thru 20 do print("case ",n,": return ",string(float(expand(integrate(integrate(sqrt(alpha/%pi)*(phi+d)^n*exp(-alpha*phi^2)*cos(d), phi, -inf, inf), d, -%pi, %pi)))), "; break;");
double int_inf_convoluted_cos_x_n(int n, double sigma){
    assert(n >= 0);
    assert(n <= 20);
    double alpha = 1.0/(2.0*sigma*sigma);
    if (n % 2 == 1)
        return 0.0;
    switch (n) {
    case  0 : return  0.0 ; break;
    case  2 : return  -12.56637061435917 ; break;
    case  4 : return  -37.69911184307752/alpha-97.25376607008846 ; break;
    case  6 : return  -729.4032455256634/alpha-141.3716694115407/pow(alpha,2)-754.6232353207233 ; break;
    case  8 : return  -10564.72529449013/alpha-5105.822718679644/pow(alpha,2)-659.7344572538566/pow(alpha,3)-6065.790466468156 ; break;
    case  10 : return  -136480.2854955334/alpha-118853.1595630139/pow(alpha,2)-38293.67039009733/pow(alpha,3)-3711.006322052943/pow(alpha,4)-50260.8446867906 ; break;
    case  12 : return  -1658607.874663904/alpha-2251924.710676324/pow(alpha,2)-1307384.755193153/pow(alpha,3)-315922.780718303/pow(alpha,4)-24492.64172554942/pow(alpha,5)-426464.9327177554 ; break;
    case  14 : return  -1.9404154438628018E+7/alpha-3.7733329148607552E+7/pow(alpha,2)-3.4154191445257187E+7/pow(alpha,3)-1.4871501590322122E+7/pow(alpha,4)-2874897.304536558/pow(alpha,5)-185735.8664187498/pow(alpha,6)-3686345.822659835 ; break;
    case  16 : return  -2.2118074935461903E+8/alpha-5.8212463315946198E+8/pow(alpha,2)-7.5466658297207832E+8/pow(alpha,3)-5.1231287167886353E+8/pow(alpha,4)-1.784580190838654E+8/pow(alpha,5)-2.8748973045365565E+7/pow(alpha,6)-1592021.712160713/pow(alpha,7)-3.233767360193944E+7 ; break;
    case  18 : return  -2.4738320296894684E+9/alpha-8.4601636629215393E+9/pow(alpha,2)-1.4844178145556E+10/pow(alpha,3)-1.4432998399342041E+10/pow(alpha,4)-7.8383869366865463E+9/pow(alpha,5)-2.2753397433192844E+9/pow(alpha,6)-3.1418520542435235E+8/pow(alpha,7)-1.5223707622536814E+7/pow(alpha,8)-2.8707617035731125E+8 ; break;
    case  20 : return  -2.7272236397462036E+10/alpha-1.1750702138356006E+11/pow(alpha,2)-2.6790518266332812E+11/pow(alpha,3)-3.5254923095672754E+11/pow(alpha,4)-2.742269695875127E+11/pow(alpha,5)-1.2410779316420337E+11/pow(alpha,6)-3.0879610802190308E+10/pow(alpha,7)-3.7309493144141827E+9/pow(alpha,8)-1.6069469157122192E+8/pow(alpha,9)-2.5736123940656128E+9 ; break;
    }
    return 0.0;
}

//void fitter::ct_tagging_part(measurement* meas, double& one, double& two)
//this function calculates the convolution of the product exp(-t/tau) * sin(deltam*t) and exp(-t/tau) * cos(deltam*t)
void convoluted_exp_sincos(double ct, double sigma, double tau, double deltam, double& one, double& two){
    double sigma_2 = sigma*sigma;
    double tau_2 = tau*tau;
    double deltam_2 = deltam*deltam;
    const double sqrt2 = TMath::Sqrt(2.0);

    const bool cdf_way = false;
    if (!cdf_way){
        //safety for large negative decay times
        if (ct < - 6.0*sigma){
            one = 0.0;
            two = 0.0;
            return;
        }
        double c1 = 0.5;
        //      double exp1arg = 0.5*sigma_2/tau_2 - 0.5*deltam_2*sigma_2 - ct/tau;
        double exp1arg = 0.5*sigma_2*(1.0/tau_2 - deltam_2) - ct/tau;
        double exp1 = exp(exp1arg);
        double exp2arg = -deltam*(ct - sigma_2/tau);
        std::complex<double> exp2(cos(exp2arg), sin(exp2arg));
        std::complex<double> cerfarg(sigma/(tau*sqrt2) - ct/(sigma*sqrt2), +deltam*sigma/sqrt2);
        std::complex<double> cerf;
        if  (cerfarg.real() < -20.0) cerf = std::complex<double>(2.0,0.0);
        else                         cerf = cErrF_2(cerfarg);//best std::complex error function
        std::complex<double> c2(exp2*cerf);
        double im = -c2.imag();//exp*sin
        double re = +c2.real();//exp*cos
        one = c1*exp1*im ;
        two = c1*exp1*re ;
    }
    else{ //all three methods are analytically the same. There might be numerical differences, though
        if (ct < -6.0*sigma) {
            one = 0.0;
            two = 0.0;
            return;
        }

        //current cdf
        std::complex<double> z(deltam*sigma/sqrt2, (sigma/tau-ct/sigma)/sqrt2);
        if (ct<0) {//i do not quite get this part. the calculation should also be corerct for ct < 0, right?
            one= 2.0*nwwerf(z).real()/4.0*exp(-ct*ct/2.0/sigma/sigma);
            two= 2.0*nwwerf(z).imag()/4.0*exp(-ct*ct/2.0/sigma/sigma);
        }
        else {
            one= -2.0*nwwerf(std::conj(z)).real()/tau/4*exp(-ct*ct/2.0/sigma/sigma) +
                    exp(sigma*sigma/2 *(1/tau/tau - deltam*deltam) - ct/tau)*cos(deltam*ct - deltam/tau*sigma*sigma);
            two= +2.0*nwwerf(std::conj(z)).imag()/tau/4*exp(-ct*ct/2.0/sigma/sigma) +
                    exp(sigma*sigma/2 *(1/tau/tau - deltam*deltam) - ct/tau)*sin(deltam*ct - deltam/tau*sigma*sigma);
        }

    }

    if (std::isnan(one) || std::isnan(two)) {
        //void convoluted_exp_sincos(double ct, double sigma, double tau, double deltam, double& one, double& two)
        spdlog::error("ct= {0:f} sigma={1:f} tau={2:f} deltam={3:f} one={4:f} two={5:f}",
                ct, sigma, tau, deltam, one, two);
        spdlog::error("Error: NaN");
        assert(0);

    }
    return;
}


double daughtermom(double m, double m1, double m2){
    //E1+E2 = sqrt(m1*m1+p1*p1)+sqrt(m2*m2+p2*p2), where p1=-p2
    //=> p= sqrt(m*m-(m1+m2)*(m1+m2))*(m*m-(m1-m2)*(m1-m2))/(2.0*m)
    //E1 = (m*m+m1*m1-m2*m2)/(2.0*M)
    //E2 = (m*m-m1*m1+m2*m2)/(2.0*M)
    //So this returns daughter momentum and has nothing to do with the mother (=mom)

    double arg = (m*m-(m1+m2)*(m1+m2))*(m*m-(m1-m2)*(m1-m2));//original, reactivate

    if (arg < 0.0 || m1+m2>m)   return 0.0;

    return sqrt(arg)/(2.0*m);
    //all ok, confirmed that it gives breakup momentum
}


std::complex<double> mkpi_simple_bw_kstar_amp(double mkpi, double qsquared, double gammakstar, double mkstar){
    double gamma = gammakstar;
    std::complex<double> bwamp = 1.0/std::complex<double>(mkstar*mkstar-mkpi*mkpi,-mkstar*gamma);
    //combine everything, eq 28-30 in LHCb-ANA-2013-053
    return sqrt(mkstar*gamma/TMath::Pi()) * bwamp;
}

double mkpi_simple_bw_kstar_amp_squared(double mkpi, double qsquared, double gammakstar, double mkstar){
    std::complex<double> result = mkpi_simple_bw_kstar_amp(mkpi, qsquared, gammakstar, mkstar);
    return norm(result);
}

double gsl_mkpi_simple_bw_kstar_amp_squared(double x, void* par){
    double* v = (double*)par;
    return mkpi_simple_bw_kstar_amp_squared(sqrt(x), v[0], v[1], v[2]);
}

double int_mkpi_simple_bw_kstar_amp_squared(const double& mkpia, const double& mkpib, const double& qsquared, const double& gammakstar, const double& mkstar){
    double pars[3] = {qsquared, gammakstar, mkstar};
    ROOT::Math::GSLIntegrator gslint;
    gslint.SetFunction(gsl_mkpi_simple_bw_kstar_amp_squared, pars);
    return gslint.Integral(mkpia*mkpia, mkpib*mkpib);
}


std::complex<double> mkpi_simple_kstarzero_amp(double mkpi, double qsquared, double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero){
    std::complex<double> gk(f800mag*cos(f800phase),f800mag*sin(f800phase));
    std::complex<double> denomkappa(mf800,-0.5*gammaf800);
    std::complex<double> denomkstar(mkstarzero,-0.5*gammakstarzero);

    std::complex<double> result = (-gk/(denomkappa*denomkappa-mkpi*mkpi)
                                   +1.0/(denomkstar*denomkstar-mkpi*mkpi));
    return result;
}

double mkpi_simple_kstarzero_amp_squared(double mkpi, double qsquared, double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero)
{
    std::complex<double> result = mkpi_simple_kstarzero_amp(mkpi, qsquared, f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero);
    return norm(result);
}

double gsl_mkpi_simple_kstarzero_amp_squared(double x, void* par)
{
    double* v = (double*)par;
    return mkpi_simple_kstarzero_amp_squared(sqrt(x), v[0], v[1], v[2], v[3], v[4], v[5], v[6]);//this is for integrating over mkpi^2, only then is the norm 1
}

double gsl_mkpi_simple_re_bw_kstar_amp_kstarzero_amp_bar(double x, void* par)
{
    double* v = (double*)par;
    std::complex<double> result = mkpi_simple_bw_kstar_amp_kstarzero_amp_bar(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9]);
    return result.real();
}

double gsl_mkpi_simple_im_bw_kstar_amp_kstarzero_amp_bar(double x, void* par)
{
    double* v = (double*)par;
    std::complex<double> result = mkpi_simple_bw_kstar_amp_kstarzero_amp_bar(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9]);
    return result.imag();
}

double gsl_mkpi_simple_re_bw_kstar_amp_bar_kstarzero_amp(double x, void* par)
{
    double* v = (double*)par;
    std::complex<double> result = mkpi_simple_bw_kstar_amp_bar_kstarzero_amp(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9]);
    return result.real();
}

double gsl_mkpi_simple_im_bw_kstar_amp_bar_kstarzero_amp(double x, void* par)
{
    double* v = (double*)par;
    std::complex<double> result = mkpi_simple_bw_kstar_amp_bar_kstarzero_amp(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9]);
    return result.imag();
}



double int_mkpi_simple_kstarzero_amp_squared(const double& mkpia, const double& mkpib, const double& qsquared, const double& f800mag, const double& f800phase, const double& gammaf800, const double& mf800, const double& gammakstarzero, const double& mkstarzero){
    double pars[7] = {qsquared, f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero};
    ROOT::Math::GSLIntegrator gslint;//(1.0e-10, 1.0e-8, 10000);
    gslint.SetFunction(gsl_mkpi_simple_kstarzero_amp_squared, pars);
    return gslint.Integral(mkpia*mkpia, mkpib*mkpib);
}

std::complex<double> mkpi_simple_bw_kstar_amp_kstarzero_amp_bar(double mkpi, double qsquared,
                                                                double gammakstar, double mkstar, double asphase,
                                                                double f800mag, double f800phase, double gammaf800, double mf800,
                                                                double gammakstarzero, double mkstarzero){
    std::complex<double> A = mkpi_simple_bw_kstar_amp(mkpi, qsquared, gammakstar, mkstar);
    std::complex<double> B = std::complex<double>(cos(asphase),sin(asphase))
            *mkpi_simple_kstarzero_amp(mkpi, qsquared, f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero);
    return A*conj(B);
}

std::complex<double> mkpi_simple_bw_kstar_amp_bar_kstarzero_amp(double mkpi, double qsquared,
                                                                double gammakstar, double mkstar, double asphase,
                                                                double f800mag, double f800phase, double gammaf800, double mf800,
                                                                double gammakstarzero, double mkstarzero){
    std::complex<double> A = mkpi_simple_bw_kstar_amp(mkpi, qsquared, gammakstar, mkstar);
    std::complex<double> B = std::complex<double>(cos(asphase),sin(asphase))
            *mkpi_simple_kstarzero_amp(mkpi, qsquared, f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero);
    return conj(A)*B;
}

std::complex<double> int_mkpi_simple_bw_kstar_amp_kstarzero_amp_bar(const double& mkpia, const double& mkpib, const double& qsquared,
                                                                    const double& gammakstar, const double& mkstar, const double& asphase,
                                                                    const double& f800mag, const double& f800phase,
                                                                    const double& gammaf800, const double& mf800,
                                                                    const double& gammakstarzero, const double& mkstarzero){
    double pars[10] = {qsquared, gammakstar, mkstar, asphase,
                       f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero};
    ROOT::Math::GSLIntegrator gslintre;
    gslintre.SetFunction(gsl_mkpi_simple_re_bw_kstar_amp_kstarzero_amp_bar, pars);
    double result_re = gslintre.Integral(mkpia, mkpib);
    ROOT::Math::GSLIntegrator gslintim;
    gslintim.SetFunction(gsl_mkpi_simple_im_bw_kstar_amp_kstarzero_amp_bar, pars);
    double result_im = gslintim.Integral(mkpia, mkpib);
    return std::complex<double>(result_re, result_im);
}

std::complex<double> int_mkpi_simple_bw_kstar_amp_bar_kstarzero_amp(const double& mkpia, const double& mkpib, const double& qsquared,
                                                                    const double& gammakstar, const double& mkstar, const double& asphase,
                                                                    const double& f800mag, const double& f800phase,
                                                                    const double& gammaf800, const double& mf800,
                                                                    const double& gammakstarzero, const double& mkstarzero){
    double pars[10] = {qsquared, gammakstar, mkstar, asphase,
                       f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero};
    //double pars[8] = {qsquared, gammakstar, mkstar, asphase, a, r, gammakstarzero, mkstarzero};
    ROOT::Math::GSLIntegrator gslintre;
    gslintre.SetFunction(gsl_mkpi_simple_re_bw_kstar_amp_bar_kstarzero_amp, pars);
    double result_re = gslintre.Integral(mkpia, mkpib);
    ROOT::Math::GSLIntegrator gslintim;
    gslintim.SetFunction(gsl_mkpi_simple_im_bw_kstar_amp_bar_kstarzero_amp, pars);
    double result_im = gslintim.Integral(mkpia, mkpib);
    return std::complex<double>(result_re, result_im);
}

//the kstar amplitude
std::complex<double> mkpi_bw_kstar_amp(double mkpi, double qsquared, double gammakstar, double mkstar, double R){
    const double mk = PDGMASS_KST_KAON/1000.0;
    const double mpi = PDGMASS_KST_PION/1000.0;
    const double mb = PDGMASS_B/1000.0;
    double p     = daughtermom(mkpi, mk, mpi);
    double pzero = daughtermom(mkstar, mk, mpi);
    double k     = daughtermom(mb, mkpi, sqrt(qsquared));
    double phsp  = sqrt(k*p);
    //double gamma = gammakstar*pow(p/pzero, 3.0)*mkstar/mkpi*sqrt((1.0+R*R*pzero*pzero)/(1.0+R*R*p*p));//nominal
    double gamma = gammakstar*pow(p/pzero, 3.0)*mkstar/mkpi*((1.0+R*R*pzero*pzero)/(1.0+R*R*p*p));//squared??? !!!
    std::complex<double> bwamp = 1.0/std::complex<double>(mkstar*mkstar-mkpi*mkpi,-mkstar*gamma);
    //combine everything, eq 28-30 in LHCb-ANA-2013-053
    return phsp * bwamp
            * sqrt((1.0+R*R*pzero*pzero)/(1.0+R*R*p*p))
            * p/mkstar;
}

double mkpi_bw_kstar_amp_squared(double mkpi, double qsquared, double gammakstar, double mkstar, double R){
    std::complex<double> result = mkpi_bw_kstar_amp(mkpi, qsquared, gammakstar, mkstar, R);
    return norm(result);
}

double mkpi_kstarzero_lass_amp_squared(double mkpi, double qsquared, double asphase, double a, double r, double gammakstarzero, double mkstarzero, double R){
    std::complex<double> result = mkpi_kstarzero_lass_amp(mkpi, qsquared, asphase, a, r, gammakstarzero, mkstarzero, R);
    return norm(result);
}

//the combined s-wave amplitude
std::complex<double> mkpi_kstarzero_lass_amp(double mkpi, double qsquared, double asphase, double a, double r, double gammakstarzero, double mkstarzero, double R){
    const double mk = PDGMASS_KST_KAON/1000.0;
    const double mpi = PDGMASS_KST_PION/1000.0;
    const double mb = PDGMASS_B/1000.0;
    double p = daughtermom(mkpi, mk, mpi);
    double pzero = daughtermom(mkstarzero, mk, mpi);
    double k = daughtermom(mb, mkpi, sqrt(qsquared));
    double kzero = daughtermom(mb, mkstarzero, sqrt(qsquared));
    double phsp = sqrt(k*p);
    // const double R = 1.6;//[GeV^-1] from Belle, see Z paper LHCb-ANA-2013-053
    //as amplitude
    double asmag = 1.0;
    std::complex<double> as(asmag*cos(asphase),asmag*sin(asphase));
    //LASS shape
    double cotdeltaB = 1.0/(a*p) + 0.5*r*p;
    double deltaB = atan(1.0/cotdeltaB);
    double gamma = gammakstarzero*p/pzero*mkstarzero/mkpi;
    double cotdeltaR = (mkstarzero*mkstarzero - mkpi*mkpi)/(mkstarzero*gamma);
    std::complex<double> A = 1.0/std::complex<double>(cotdeltaB, -1.0);
    std::complex<double> B = 1.0/std::complex<double>(cotdeltaR, -1.0);
    std::complex<double> factor(cos(2.0*deltaB),sin(2.0*deltaB));
    std::complex<double> lass = A+factor*B;
    //combine everything, eq 52-53 in LHCb-ANA-2013-053
    return phsp * as * lass
            //* mkpi/p ///David: this is not derivable from given formulas, but somehow appears in eq 52 of LHCb-ANA-2013-053
            * k/mb
            * sqrt((1.0+R*R*kzero*kzero)/(1.0+R*R*k*k));
}


std::complex<double> mkpi_kstarzero_isobar_amp(double mkpi, double qsquared, double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero, double R){
    const double mk = PDGMASS_KST_KAON/1000.0;
    const double mpi = PDGMASS_KST_PION/1000.0;
    const double mb = PDGMASS_B/1000.0;
    //const double R = 1.6;//[GeV^-1] from Belle, see Z paper LHCb-ANA-2013-053

    double p = daughtermom(mkpi, mk, mpi);
    double pzero = daughtermom(mf800, mk, mpi);
    double k = daughtermom(mb, mkpi, sqrt(qsquared));
    double kzero = daughtermom(mb, mf800, sqrt(qsquared));
    double phsp = sqrt(k*p);
    double gamma = gammaf800*pow(p/pzero, 1.0)*mf800/mkpi;
    std::complex<double> f800amp =
            phsp
            *k/mb*sqrt((1.0+R*R*kzero*kzero)/(1.0+R*R*k*k))
            *1.0/std::complex<double>(mf800*mf800-mkpi*mkpi,-mf800*gamma);

    p = daughtermom(mkpi, mk, mpi);
    pzero = daughtermom(mkstarzero, mk, mpi);
    k = daughtermom(mb, mkpi, sqrt(qsquared));
    kzero = daughtermom(mb, mkstarzero, sqrt(qsquared));
    phsp = sqrt(k*p);
    gamma = gammakstarzero*pow(p/pzero, 1.0)*mkstarzero/mkpi;
    std::complex<double> kstarzeroamp =
            phsp
            *k/mb*sqrt((1.0+R*R*kzero*kzero)/(1.0+R*R*k*k))
            *1.0/std::complex<double>(mkstarzero*mkstarzero-mkpi*mkpi,-mkstarzero*gamma);

    return f800mag*std::complex<double>(cos(f800phase),sin(f800phase))*f800amp + (1.0-f800mag)*kstarzeroamp;
}

double mkpi_kstarzero_isobar_amp_squared(double mkpi, double qsquared, double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero, double R){
    std::complex<double> result = mkpi_kstarzero_isobar_amp(mkpi, qsquared, f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero, R);
    return norm(result);
}


std::complex<double> mkpi_bw_kstar_amp_kstarzero_isobar_amp_bar(double mkpi, double qsquared,
                                                                double gammakstar, double mkstar, double asphase,
                                                                double f800mag, double f800phase,
                                                                double gammaf800, double mf800,
                                                                double gammakstarzero, double mkstarzero, double R){
    std::complex<double> A = mkpi_bw_kstar_amp(mkpi, qsquared, gammakstar, mkstar, R);
    std::complex<double> B = std::complex<double>(cos(asphase),sin(asphase))
            *mkpi_kstarzero_isobar_amp(mkpi, qsquared, f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero, R);
    return A*conj(B);
}

std::complex<double> mkpi_bw_kstar_amp_bar_kstarzero_isobar_amp(double mkpi, double qsquared,
                                                                double gammakstar, double mkstar, double asphase,
                                                                double f800mag, double f800phase,
                                                                double gammaf800, double mf800,
                                                                double gammakstarzero, double mkstarzero, double R){
    std::complex<double> A = mkpi_bw_kstar_amp(mkpi, qsquared, gammakstar, mkstar, R);
    std::complex<double> B = std::complex<double>(cos(asphase),sin(asphase))
            *mkpi_kstarzero_isobar_amp(mkpi, qsquared, f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero, R);
    return conj(A)*B;
}

std::complex<double> mkpi_bw_kstar_amp_kstarzero_lass_amp_bar(double mkpi, double qsquared,
                                                              double gammakstar, double mkstar,
                                                              double asphase, double a, double r,
                                                              double gammakstarzero, double mkstarzero, double R){
    std::complex<double> A = mkpi_bw_kstar_amp(mkpi, qsquared, gammakstar, mkstar, R);
    std::complex<double> B = mkpi_kstarzero_lass_amp(mkpi, qsquared, asphase, a, r, gammakstarzero, mkstarzero, R);
    return A*conj(B);
}

std::complex<double> mkpi_bw_kstar_amp_bar_kstarzero_lass_amp(double mkpi, double qsquared,
                                                              double gammakstar, double mkstar,
                                                              double asphase, double a, double r,
                                                              double gammakstarzero, double mkstarzero, double R){
    std::complex<double> A = mkpi_bw_kstar_amp(mkpi, qsquared, gammakstar, mkstar, R);
    std::complex<double> B = mkpi_kstarzero_lass_amp(mkpi, qsquared, asphase, a, r, gammakstarzero, mkstarzero, R);
    return conj(A)*B;
}

double gsl_mkpi_bw_kstar_amp_squared(double x, void* par){
    double* v = (double*)par;
    return mkpi_bw_kstar_amp_squared(x, v[0], v[1], v[2], v[3]);
}

double gsl_mkpi_kstarzero_lass_amp_squared(double x, void* par){
    double* v = (double*)par;
    return mkpi_kstarzero_lass_amp_squared(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6]);
}

double gsl_mkpi_re_bw_kstar_amp_kstarzero_lass_amp_bar(double x, void* par){
    double* v = (double*)par;
    std::complex<double> result = mkpi_bw_kstar_amp_kstarzero_lass_amp_bar(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]);
    return result.real();
}

double gsl_mkpi_im_bw_kstar_amp_kstarzero_lass_amp_bar(double x, void* par){
    double* v = (double*)par;
    std::complex<double> result = mkpi_bw_kstar_amp_kstarzero_lass_amp_bar(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]);
    return result.imag();
}

double gsl_mkpi_re_bw_kstar_amp_bar_kstarzero_lass_amp(double x, void* par){
    double* v = (double*)par;
    std::complex<double> result = mkpi_bw_kstar_amp_bar_kstarzero_lass_amp(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]);
    return result.real();
}

double gsl_mkpi_im_bw_kstar_amp_bar_kstarzero_lass_amp(double x, void* par){
    double* v = (double*)par;
    std::complex<double> result = mkpi_bw_kstar_amp_bar_kstarzero_lass_amp(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]);
    return result.imag();
}

double gsl_mkpi_kstarzero_isobar_amp_squared(double x, void* par){
    double* v = (double*)par;
    return mkpi_kstarzero_isobar_amp_squared(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
}

double gsl_mkpi_re_bw_kstar_amp_kstarzero_isobar_amp_bar(double x, void* par){
    double* v = (double*)par;
    std::complex<double> result = mkpi_bw_kstar_amp_kstarzero_isobar_amp_bar(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10]);
    return result.real();
}

double gsl_mkpi_im_bw_kstar_amp_kstarzero_isobar_amp_bar(double x, void* par){
    double* v = (double*)par;
    std::complex<double> result = mkpi_bw_kstar_amp_kstarzero_isobar_amp_bar(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10]);
    return result.imag();
}

double gsl_mkpi_re_bw_kstar_amp_bar_kstarzero_isobar_amp(double x, void* par){
    double* v = (double*)par;
    std::complex<double> result = mkpi_bw_kstar_amp_bar_kstarzero_isobar_amp(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10]);
    return result.real();
}

double gsl_mkpi_im_bw_kstar_amp_bar_kstarzero_isobar_amp(double x, void* par){
    double* v = (double*)par;
    std::complex<double> result = mkpi_bw_kstar_amp_bar_kstarzero_isobar_amp(x, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10]);
    return result.imag();
}

double int_mkpi_bw_kstar_amp_squared(double mkpia, double mkpib, double qsquared, double gammakstar,
                                     double mkstar, double R){
    double pars[4] = {qsquared, gammakstar, mkstar, R};
    ROOT::Math::GSLIntegrator gslint;
    gslint.SetFunction(gsl_mkpi_bw_kstar_amp_squared, pars);
    return gslint.Integral(mkpia, mkpib);
}

double int_mkpi_bw_kstar_amp_squared(double qsquared, double gammakstar, double mkstar, double R){
    double pars[4] = {qsquared, gammakstar, mkstar, R};
    ROOT::Math::GSLIntegrator gslint;//(1.0e-9, 1.0e-8, 100000);
    gslint.SetFunction(gsl_mkpi_bw_kstar_amp_squared, pars);
    return gslint.Integral();
}

double int_mkpi_kstarzero_lass_amp_squared(double mkpia, double mkpib, double qsquared, double asphase, double a, double r, double gammakstarzero, double mkstarzero, double R)
{
    double pars[7] = {qsquared, asphase, a, r, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslint;
    gslint.SetFunction(gsl_mkpi_kstarzero_lass_amp_squared, pars);
    return gslint.Integral(mkpia, mkpib);
}

double int_mkpi_kstarzero_lass_amp_squared(double qsquared, double asphase, double a, double r, double gammakstarzero, double mkstarzero, double R)
{
    double pars[7] = {qsquared, asphase, a, r, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslint;//(1.0e-9, 1.0e-8, 100000);
    gslint.SetFunction(gsl_mkpi_kstarzero_lass_amp_squared, pars);
    return gslint.Integral();
}

double int_mkpi_kstarzero_isobar_amp_squared(double mkpia, double mkpib, double qsquared, double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero, double R)
{
    double pars[8] = {qsquared, f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslint;
    gslint.SetFunction(gsl_mkpi_kstarzero_isobar_amp_squared, pars);
    return gslint.Integral(mkpia, mkpib);
}

double int_mkpi_kstarzero_isobar_amp_squared(double qsquared, double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero, double R)
{
    double pars[8] = {qsquared, f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslint;//(1.0e-9, 1.0e-8, 100000);
    gslint.SetFunction(gsl_mkpi_kstarzero_isobar_amp_squared, pars);
    return gslint.Integral();
}

std::complex<double> int_mkpi_bw_kstar_amp_kstarzero_lass_amp_bar(double mkpia, double mkpib, double qsquared, double gammakstar, double mkstar, double asphase, double a, double r, double gammakstarzero, double mkstarzero, double R)
{
    double pars[9] = {qsquared, gammakstar, mkstar, asphase, a, r, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslintre;
    gslintre.SetFunction(gsl_mkpi_re_bw_kstar_amp_kstarzero_lass_amp_bar, pars);
    double result_re = gslintre.Integral(mkpia, mkpib);
    ROOT::Math::GSLIntegrator gslintim;
    gslintim.SetFunction(gsl_mkpi_im_bw_kstar_amp_kstarzero_lass_amp_bar, pars);
    double result_im = gslintim.Integral(mkpia, mkpib);
    return std::complex<double>(result_re, result_im);
}

std::complex<double> int_mkpi_bw_kstar_amp_bar_kstarzero_lass_amp(double mkpia, double mkpib, double qsquared, double gammakstar, double mkstar, double asphase, double a, double r, double gammakstarzero, double mkstarzero, double R)
{
    double pars[9] = {qsquared, gammakstar, mkstar, asphase, a, r, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslintre;
    gslintre.SetFunction(gsl_mkpi_re_bw_kstar_amp_bar_kstarzero_lass_amp, pars);
    double result_re = gslintre.Integral(mkpia, mkpib);
    ROOT::Math::GSLIntegrator gslintim;
    gslintim.SetFunction(gsl_mkpi_im_bw_kstar_amp_bar_kstarzero_lass_amp, pars);
    double result_im = gslintim.Integral(mkpia, mkpib);
    return std::complex<double>(result_re, result_im);
}


std::complex<double> int_mkpi_bw_kstar_amp_kstarzero_lass_amp_bar(double qsquared, double gammakstar, double mkstar, double asphase, double a, double r, double gammakstarzero, double mkstarzero, double R)
{
    double pars[9] = {qsquared, gammakstar, mkstar, asphase, a, r, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslintre;//(1.0e-9, 1.0e-8, 100000);
    gslintre.SetFunction(gsl_mkpi_re_bw_kstar_amp_kstarzero_lass_amp_bar, pars);
    double result_re = gslintre.Integral();//mkpisquareda, mkpisquaredb);
    ROOT::Math::GSLIntegrator gslintim;//(1.0e-9, 1.0e-8, 100000);
    gslintim.SetFunction(gsl_mkpi_im_bw_kstar_amp_kstarzero_lass_amp_bar, pars);
    double result_im = gslintim.Integral();//mkpisquareda, mkpisquaredb);
    return std::complex<double>(result_re, result_im);
}

std::complex<double> int_mkpi_bw_kstar_amp_bar_kstarzero_lass_amp(double qsquared, double gammakstar, double mkstar, double asphase, double a, double r, double gammakstarzero, double mkstarzero, double R)
{
    double pars[9] = {qsquared, gammakstar, mkstar, asphase, a, r, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslintre;//(1.0e-9, 1.0e-8, 100000);
    gslintre.SetFunction(gsl_mkpi_re_bw_kstar_amp_bar_kstarzero_lass_amp, pars);
    double result_re = gslintre.Integral();//mkpisquareda, mkpisquaredb);
    ROOT::Math::GSLIntegrator gslintim;//(1.0e-9, 1.0e-8, 100000);
    gslintim.SetFunction(gsl_mkpi_im_bw_kstar_amp_bar_kstarzero_lass_amp, pars);
    double result_im = gslintim.Integral();//mkpisquareda, mkpisquaredb);
    return std::complex<double>(result_re, result_im);
}

///////////////
std::complex<double> int_mkpi_bw_kstar_amp_kstarzero_isobar_amp_bar(double mkpia, double mkpib, double qsquared,
                                                                    double gammakstar, double mkstar, double asphase,
                                                                    double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero, double R)
{
    double pars[11] = {qsquared, gammakstar, mkstar, asphase,
                       f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslintre;
    gslintre.SetFunction(gsl_mkpi_re_bw_kstar_amp_kstarzero_isobar_amp_bar, pars);
    double result_re = gslintre.Integral(mkpia, mkpib);
    ROOT::Math::GSLIntegrator gslintim;
    gslintim.SetFunction(gsl_mkpi_im_bw_kstar_amp_kstarzero_isobar_amp_bar, pars);
    double result_im = gslintim.Integral(mkpia, mkpib);
    return std::complex<double>(result_re, result_im);
}

std::complex<double> int_mkpi_bw_kstar_amp_bar_kstarzero_isobar_amp(double mkpia, double mkpib, double qsquared,
                                                                    double gammakstar, double mkstar, double asphase,
                                                                    double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero, double R)
{
    double pars[11] = {qsquared, gammakstar, mkstar, asphase,
                       f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero, R};
    //double pars[8] = {qsquared, gammakstar, mkstar, asphase, a, r, gammakstarzero, mkstarzero};
    ROOT::Math::GSLIntegrator gslintre;
    gslintre.SetFunction(gsl_mkpi_re_bw_kstar_amp_bar_kstarzero_isobar_amp, pars);
    double result_re = gslintre.Integral(mkpia, mkpib);
    ROOT::Math::GSLIntegrator gslintim;
    gslintim.SetFunction(gsl_mkpi_im_bw_kstar_amp_bar_kstarzero_isobar_amp, pars);
    double result_im = gslintim.Integral(mkpia, mkpib);
    return std::complex<double>(result_re, result_im);
}


std::complex<double> int_mkpi_bw_kstar_amp_kstarzero_isobar_amp_bar(double qsquared,
                                                                    double gammakstar, double mkstar, double asphase,
                                                                    double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero, double R)
{
    double pars[11] = {qsquared, gammakstar, mkstar, asphase,
                       f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero, R};
    //double pars[8] = {qsquared, gammakstar, mkstar, asphase, a, r, gammakstarzero, mkstarzero};
    ROOT::Math::GSLIntegrator gslintre;//(1.0e-9, 1.0e-8, 100000);
    gslintre.SetFunction(gsl_mkpi_re_bw_kstar_amp_kstarzero_isobar_amp_bar, pars);
    double result_re = gslintre.Integral();//mkpisquareda, mkpisquaredb);
    ROOT::Math::GSLIntegrator gslintim;//(1.0e-9, 1.0e-8, 100000);
    gslintim.SetFunction(gsl_mkpi_im_bw_kstar_amp_kstarzero_isobar_amp_bar, pars);
    double result_im = gslintim.Integral();//mkpisquareda, mkpisquaredb);
    return std::complex<double>(result_re, result_im);
}

std::complex<double> int_mkpi_bw_kstar_amp_bar_kstarzero_isobar_amp(double qsquared,
                                                                    double gammakstar, double mkstar, double asphase,
                                                                    double f800mag, double f800phase, double gammaf800, double mf800, double gammakstarzero, double mkstarzero, double R)
{
    double pars[11] = {qsquared, gammakstar, mkstar, asphase,
                       f800mag, f800phase, gammaf800, mf800, gammakstarzero, mkstarzero, R};
    ROOT::Math::GSLIntegrator gslintre;//(1.0e-9, 1.0e-8, 100000);
    gslintre.SetFunction(gsl_mkpi_re_bw_kstar_amp_bar_kstarzero_isobar_amp, pars);
    double result_re = gslintre.Integral();//mkpisquareda, mkpisquaredb);
    ROOT::Math::GSLIntegrator gslintim;//(1.0e-9, 1.0e-8, 100000);
    gslintim.SetFunction(gsl_mkpi_im_bw_kstar_amp_bar_kstarzero_isobar_amp, pars);
    double result_im = gslintim.Integral();//mkpisquareda, mkpisquaredb);
    return std::complex<double>(result_re, result_im);
}

void moments_to_observables(TRandom3* rnd,
                            std::vector<double> mom,
                            std::vector<double> momcov,
                            double fsext,
                            double dfsext,
                            std::vector<double>& obs,
                            std::vector<double>& obscov,
                            bool usefsext
                            ){

    assert(mom.size() == 18);
    assert(momcov.size() == 18*18);

    if(spdlog_debug()){
        std::cout << std::setw(10) << std::setprecision(6);
        for(UInt_t j = 0; j < 18; j++){
            for(UInt_t k = 0; k < 18; k++){
                std::cout << momcov.at(j*18+k);
            }
            std::cout << std::endl;
        }
    }

    //without s-waves, the matrix only comprises of 12 elements:
    bool swave = true;
    if(momcov.at(12) == 0.0 && momcov.at(13) == 0.0 && momcov.at(14) == 0.0 && momcov.at(15) == 0.0 && momcov.at(16) == 0.0 && momcov.at(17) == 0.0){
        swave = false;
    }
    spdlog::info("[MoM]\tSwave is "+std::string(swave ? "ON" : "OFF")+"!");
    const UInt_t N = swave ? 18 : 12;

    //convert 18x18 matrix to 12x12 matrix
    if(!swave){
        std::vector<double> momcov_tmp;
        for(UInt_t i = 0; i < 12; i++){
            for(UInt_t j = 0; j < 12; j++){
                momcov_tmp.push_back(momcov.at(i*18+j));
            }
        }
        momcov.clear();
        momcov = momcov_tmp;
    }

    assert(momcov.size() == N*N);

    //all off-diagonal matrix elements for S1c, S2s, S2c are forced to zero
    //all digaonal covariant elements are set equal to the one of S1s
    for (unsigned int i=0; i<N; i++){
        for (unsigned int j=0; j<N; j++){
            if (i==1 || i==2 || i==3 || j==1 || j==2 || j==3){
                if (i==j) momcov.at(i*N+j) = momcov.at(0);
                else momcov.at(i*N+j) = 0.0;
            }
        }
    }

    //cholesky decomposition of covariance matrix
    TMatrixDSym cov_sym(N, &momcov[0]);
    TDecompChol chol(cov_sym);
    bool success = chol.Decompose();
    assert(success);
    TMatrixD mU = chol.GetU();//gets upper triangular matrix
    //decomposition check
    
    TMatrixD mUT(mU);
    mUT.Transpose(mUT);
    TMatrixD check = mUT * mU;
    double distance = 0.0;
    for (unsigned int i=0; i<N; i++){
        for (unsigned int j=0; j<N; j++){
            distance += fabs(momcov.at(i+j*N) - check(i,j));
        }
    }
    spdlog::info("[MoM]\tDistance from Cholesky decomposition is {e:0}", distance);
    assert(fabs(distance) < 1.0e-8);

    //determine mean and rms (or rather confidence interval) from toys according to cov matrix
    unsigned int nruns = 10000;
    std::vector<std::vector< double > > randomizedobs;
    for (unsigned int l=0; l<nruns; l++){

        std::vector<double> rndobs(N, 0.0);

        //generate 10000 random moments according to covariance matrix
        //rndm gaussian(0,1) vector
        std::vector<double> rndvec(N, 0.0);
        for (unsigned int i=0;i<N;i++){
            //if (i!=1 && i!=2 && i!=3)
            rndvec.at(i) = rnd->Gaus(0.0, 1.0);
        }

        //random gaussian vector times upper triangular matrix
        std::vector<double> murndvec(N, 0.0);
        for (unsigned int i=0; i<N; i++){
            for (unsigned int j=0; j<N; j++){
                murndvec.at(i) += mU(j,i)*rndvec.at(j);
            }
        }

        //new random vector
        std::vector<double> result(N, 0.0);
        for (unsigned int i=0; i<N; i++){
            result.at(i) = mom.at(i) + murndvec.at(i);
        }

        //now get observables
        rndobs = std::vector<double>(N, 0.0);
        double j6c = 0.0;
        double pi = TMath::Pi();

        bool useLinearCombinationForS1s = false;
        bool useLinearCombinationForS6s = true;

        if(swave){

            double fl = (80.0*pi*result.at(0)+80.0*pi*result.at(12)-27.0)/(80.0*pi*result.at(0)+240.0*pi*result.at(12)-51.0);
            double fs = (80.0*pi*result.at(0)+240.0*pi*result.at(12)-45.0)/6.0;
            if (usefsext){
                fs = fsext+rnd->Gaus(0.0,dfsext);
            }
            rndobs.at(0) = 3.0/4.0*(1.0-fl); //S_1s
            rndobs.at(1) = 0.0; //S_1c
            rndobs.at(2) = 0.0; //S_2s
            rndobs.at(3) = 0.0; //S_2c
            rndobs.at(4) = -100.0*pi*result.at(4)/(9.0*fs-9.0); //S_3
            rndobs.at(5) = -100.0*pi*result.at(5)/(9.0*fs-9.0); //S_4
            rndobs.at(6) = -80*pi*result.at(6)/(9.0*fs-9.0); //S_5
            if(useLinearCombinationForS6s){
                rndobs.at(7) = -32.0*pi*(3.0*result.at(7) - 2.0*result.at(8))/(9.0*fs-9.0); //S_6s
                rndobs.at(8) = -32.0*pi*(8.0*result.at(8) - 2.0*result.at(7))/(9.0*fs-9.0); //S_6c
            }
            else{
                rndobs.at(7) = -(9.0*j6c*fs+320.0*pi*result.at(7)-9*j6c)/(36.0*fs-36.0); //S_6s
                double j6s = rndobs.at(7);
                rndobs.at(8) = -(18.0*j6s*fs+640.0*pi*result.at(8)-18.0*j6s)/(27.0*fs-27.0); //S_6c
                //no strange usage of S_6c:
                rndobs.at(7) = -80.0*pi*result.at(7)/(9.0*fs-9.0); //S_6s
                rndobs.at(8) = 0.0; //S_6c
            }
            rndobs.at(9) = -80.0*pi*result.at(9)/(9.0*fs-9.0);//S_7
            rndobs.at(10) = -100.0*pi*result.at(10)/(9.0*fs-9.0);//S_8
            rndobs.at(11) = -100.0*pi*result.at(11)/(9.0*fs-9.0);//S_9
            rndobs.at(12) = fs;//rnd->Gaus();
            rndobs.at(13) = 20.0*pi*result.at(13);//S_S1
            rndobs.at(14) = 20.0*pi*result.at(14);//S_S2
            rndobs.at(15) = 16.0*pi*result.at(15);//S_S3
            rndobs.at(16) = 16.0*pi*result.at(16);//S_S4
            rndobs.at(17) = 20.0*pi*result.at(17);//S_S5
        }
        else{
            if(useLinearCombinationForS1s){
                rndobs.at(0) = 32.0/9.0*pi*(21.0/16.0*result.at(0)-7.0/8.0*result.at(1)+1125.0/1184.0*result.at(2)-25.0/37.0*result.at(3)); //David recalculation
            }
            else{
                rndobs.at(0) = 15.0/8.0*(32.0/9.0*pi*result.at(0) - 2.0/5.0); //Thommis thesis
            }
            rndobs.at(1) = 0.0; //S_1c
            rndobs.at(2) = 0.0; //S_2s
            rndobs.at(3) = 0.0; //S_2c
            rndobs.at(4) = 100.0/9.0*pi*result.at(4); //S_3
            rndobs.at(5) = 100.0/9.0*pi*result.at(5); //S_4
            rndobs.at(6) = 80.0/9.0*pi*result.at(6); //S_5
            if(useLinearCombinationForS6s){
                rndobs.at(7) = 32.0/9.0*pi*(3.0*result.at(7) - 2.0*result.at(8)); //S_6s
                rndobs.at(8) = 32.0/9.0*pi*(8.0*result.at(8) - 2.0*result.at(7)); //S_6c
            }
            else{
                rndobs.at(7) = 80.0/9.0*pi*result.at(7);
                rndobs.at(8) = 0.0;
            }
            rndobs.at(9) = 80.0/9.0*pi*result.at(9); //S_7
            rndobs.at(10) = 100.0/9.0*pi*result.at(10); //S_8
            rndobs.at(11) = 100.0/9.0*pi*result.at(11); //S_9
        }
        randomizedobs.push_back(rndobs);
    }
    std::vector<double> res(18,0.0);
    std::vector<double> rescov(18*18,0.0);

    //determine mean value of moments M_i
    for (unsigned int i=0; i<nruns; i++){
        for (unsigned int j=0; j<N; j++){
            res.at(j) += randomizedobs.at(i).at(j)/nruns;
        }
    }

    //determine variance for each M_i
    for (unsigned int i=0; i<nruns; i++){
        for (unsigned int j=0; j<N; j++){
            for (unsigned int k=0; k<N; k++){
                rescov.at(j*18+k) += (randomizedobs.at(i).at(j) - res.at(j)) * (randomizedobs.at(i).at(k) - res.at(k)) / (nruns);
                if(std::isnan(rescov.at(j*18+k))){
                    spdlog::info("[MoM]\tCov element ({0:d},{1:d}) is nan at run={2:d}",j, k, i);
                    assert(0);
                }
            }
        }
    }

    obs = res;
    obscov = rescov;

    return;
}

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

double threshold(double x, double thr, double n){
    return pow(x-thr,1.0/n);
}

double int_threshold(double a, double b, double thr, double n){
    return pow(b-thr,1.0/n+1.0)/(1.0/n+1.0)
            -pow(a-thr,1.0/n+1.0)/(1.0/n+1.0);
}

//int throw_multivariate_normal(unsigned int ndim, unsigned int ntoys, std::vector<double> xi, std::vector<double> cov, std::vector<std::vector<double> > results)
int throw_multivariate_normal(const unsigned int ndim, const unsigned int ntoys, const std::vector<double>& xi, const std::vector<double>& cov, std::vector<std::vector<double> >& results){
    assert(xi.size() == ndim);
    assert(cov.size() == ndim*ndim);
    results.clear();
    TRandom3* rnd = new TRandom3();
    
    TMatrixDSym cov_sym(ndim, &cov[0]);
    TDecompChol chol(cov_sym);
    bool success = chol.Decompose();

    assert(success);
    TMatrixD mU = chol.GetU();//gets upper triangular matrix
    
    for (unsigned int l=0;l<ntoys;l++){
        std::vector<double> result(ndim, 0.0);

        std::vector<double> rndvec(ndim, 0.0);
        for (unsigned int i=0;i<ndim;i++){
            rndvec.at(i) = rnd->Gaus(0.0, 1.0);
        }
        //random gaussian vector times upper triangular matrix
        std::vector<double> murndvec(ndim, 0.0);
        for (unsigned int i=0; i<ndim; i++){
            for (unsigned int j=0; j<ndim; j++){
                murndvec.at(i) += mU(j,i)*rndvec.at(j);
            }
        }

        //new random vector
        for (unsigned int i=0; i<ndim; i++){
            result.at(i) = xi.at(i) + murndvec.at(i);
        }
        results.push_back(result);
    }
    return 0;
}

int get_mean_cov(const std::vector<std::vector<double> >& meas, std::vector<double>& mean, std::vector<double>& cov){
    assert(meas.size() > 0);
    unsigned int ndim = meas.at(0).size();
    unsigned int ntoys = meas.size();

    std::vector<double> m(ndim, 0.0);
    
    for (unsigned int l=0; l<meas.size(); l++){
        for (unsigned int i=0; i<ndim; i++){
            m.at(i) += (meas.at(l).at(i))/(double(ntoys));
        }
    }

    std::vector<long double> c(ndim*ndim, 0.0);
    for (unsigned int l=0; l<meas.size(); l++){
        for (unsigned int i=0; i<ndim; i++){
            for (unsigned int j=0; j<ndim; j++){
                c.at(i*ndim+j) += (m.at(i)-meas.at(l).at(i))*(m.at(j)-meas.at(l).at(j))/double(ntoys);
            }
        }
    }
    mean.resize(m.size());
    cov.resize(c.size());
    for (unsigned int l=0; l<m.size(); l++){
        mean.at(l) = m.at(l);
    }
    for (unsigned int l=0; l<c.size(); l++){
        cov.at(l) = c.at(l);
    }

    return 0;
}

void sis_to_pis(TRandom3* rnd,
                std::vector<double> sis,
                std::vector<double> sicov,
                std::vector<double>& pis,
                std::vector<double>& picov){
    assert(sis.size()==8);
    assert(sicov.size()==8*8);
    
    //cholesky decomposition of covariance matrix
    TMatrixDSym cov_sym(8, &sicov[0]);
    TDecompChol chol(cov_sym);
    bool success = chol.Decompose();
    assert(success);
    TMatrixD mU = chol.GetU();//gets upper triangular matrix
    //decomposition check
    //determine mean and rms (or rather confidence interval) from toys according to cov matrix
    unsigned int nruns = 50000;
    std::vector<std::vector< double > > randomizedpis;
    for (unsigned int l=0; l<nruns; l++) {

        //generate random moments according to covariance matrix
        //rndm gaussian(0,1) vector
        std::vector<double> rndvec(8, 0.0);
        for (unsigned int i=0;i<8;i++){
            rndvec.at(i) = rnd->Gaus(0.0, 1.0);
        }
        //random gaussian vector times upper triangular matrix
        std::vector<double> murndvec(8, 0.0);
        for (unsigned int i=0; i<8; i++){
            for (unsigned int j=0; j<8; j++){
                murndvec.at(i) += mU(j,i)*rndvec.at(j);
            }
        }
        //new random vector
        std::vector<double> result(8, 0.0);
        for (unsigned int i=0; i<8; i++){
            result.at(i) = sis.at(i) + murndvec.at(i);
        }
        //now get observables
        //rndpi = std::vector<double>(8, 0.0);
        std::vector<double> rndpi(8, 0.0);

        double fl = result.at(0);
        double ft = 1.0-fl;
        double s3 = result.at(1);
        double s4 = result.at(2);
        double s5 = result.at(3);
        double afb = result.at(4);
        double s7 = result.at(5);
        double s8 = result.at(6);
        double s9 = result.at(7);

        rndpi.at(0) = fl;//fl
        rndpi.at(1) = 2.0*s3/(1.0-fl);//p1
        rndpi.at(2) = 0.5*4.0/3.0*afb/(1.0-fl);//p2
        rndpi.at(3) = -s9/(1-fl);//p3
        rndpi.at(4) = s4/sqrt(fl*ft);//p4
        rndpi.at(5) = s5/sqrt(fl*ft);//p5
        rndpi.at(6) = s7/sqrt(fl*ft);//p6
        rndpi.at(7) = s8/sqrt(fl*ft);//p8

        randomizedpis.push_back(rndpi);
    }
    std::vector<double> res(8,0.0);
    std::vector<double> rescov(8*8,0.0);
    for (unsigned int i=0; i<nruns; i++){
        for (unsigned int j=0; j<8; j++){
            res.at(j) += randomizedpis.at(i).at(j)/nruns;
        }
    }
    for (unsigned int i=0; i<nruns; i++){
        for (unsigned int j=0; j<8; j++){
            for (unsigned int k=0; k<8; k++){
                rescov.at(j*8+k) += (randomizedpis.at(i).at(j)-res.at(j))*(randomizedpis.at(i).at(k)-res.at(k))/(nruns);
            }
        }
    }
    pis = res;
    picov = rescov;
}

double rndgauss(TRandom3* rnd, double mean, double width, double min, double max){
    double r = 0.0;
    bool finished = false;
    while (!finished) {
        r = rnd->Gaus(mean, width);
        if (r > min && r < max) finished = true;
    }
    return r;
}

double rndgauss(TRandom3* rnd, double mean, double widthlo, double widthhi, double min, double max)
{
    double r = 0.0;
    bool finished = false;
    bool lo = rnd->Rndm() < 0.5;
    while (!finished){
        if (lo) r = mean - fabs(rnd->Gaus(0.0, widthlo));
        else        r = mean + fabs(rnd->Gaus(0.0, widthhi));

        if (r > min && r < max) finished = true;
    }
    return r;
}

std::vector<UInt_t> GetNOutOfM(UInt_t N, UInt_t M, Int_t Seed, bool DoubleCounts, bool PoissonFluctuate){

    TRandom3 * R = new TRandom3(Seed);

    if(PoissonFluctuate) N = TMath::Abs(R->Poisson(N));

    std::vector<UInt_t> v;

    if(DoubleCounts){
        while(v.size() < N){
            UInt_t I = R->Integer(M);
            v.push_back(I);
        }
    }
    else{
        assert(M > N);
        if(N <= M / 2){ //for small N get white list of Integers
            while(v.size() < N){
                UInt_t I = R->Integer(M);
                bool AlreadyChosen = false;
                for(UInt_t i = 0; i < v.size(); i++){
                    if(I == v.at(i)){
                        AlreadyChosen = true;
                        break;
                    }
                }
                if(!AlreadyChosen){
                    v.push_back(I);
                }
            }
        }
        else{ //large N: get black list and remove those numbers
            for(UInt_t i = 0; i < M; i++){
                v.push_back(i);
            }
            while(v.size() > N){
                UInt_t I = R->Integer(M);
                for(UInt_t i = 0; i < v.size(); i++){
                    if(I == v.at(i)){
                        v.erase(v.begin()+i);
                        break;
                    }
                }
            }
        }
    }

    assert(v.size() == N);

    return v;

}


}
