#include "mex.h"
#include "matrix.h"
#include "windows.h"
#include "Element.h"
#include "XRLFunctions.h"
#include "Compound.h"
#include "IncidentEnergyObj.h"
#include "CinFunctioncs.h"
#include <vector>
#include <cmath>

#include <boost/math/special_functions/expint.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/complex/acos.hpp>

#define ZEROForComp 0.1
#define zeroTolerance 10000000

bool atomKroneckerDelta(Element *element1, Element *element2){

    if( element1->getZ() == element2->getZ() )
        return true;
    else
        return false;
}

double E1Fun(double x){

    if( (ceilf(x * zeroTolerance) / zeroTolerance) == 0  )
        return 0.0;

    // here we use the relation E1(x) = -Ei(-x)
    return -1.0*boost::math::expint(-1.0*x);

}

/*
 * the summed quantity in the Sharmen equation
 */

double xSjnu( Element *iElement, Element *jElement,Compound* compound, double energy, ElementEnergyLine *lambda, ElementEnergyLine *nu, double theta1, double theta2){

    double xS = (jElement->getW()) * XRLFunctions::xrlCSFluorLineKissel( iElement->getZ() , lambda->getXrlint(), nu->getEnergy()) * XRLFunctions::xrlCSFluorLineKissel( jElement->getZ() , nu->getXrlint(), energy) *xL(compound,energy, lambda->getEnergy(), nu->getEnergy(), theta1, theta2 );

    // because xL can be undetermined
    if (mxIsNaN(xS)==1){
        return 0.0;
    } else {
        return xS;
    }
}

/*
 * the dividing factor is defined here as tau_{i,\lambda}(E) + \sum_{j,\nu}S_{i,j,\lambda,\nu}
 * this is a somewhat diffrent definition as in the original formulas yet it is equaivalent
 */

double dividingFactor(Element *iElement, ElementEnergyLine *lambda, double energy, Compound* compound, double theta1, double theta2){

    int numberOfElements = (compound->getElements()).size();
    std::vector<Element> elementSet = (compound->getElements());
    double temp = 0.0;

    for(int i = 0; i < numberOfElements; i++){
        int numberOfEnergyLines = (elementSet[i].getEnergyLines()).size();
        for(int j = 0; j < numberOfEnergyLines; j++){
            temp = temp + xSjnu(iElement, &elementSet[i], compound, energy, lambda, &(elementSet[i].getEnergyLines())[j], theta1, theta2);
        }
    }

    // convenction with old dividing factor
    //double df = 1.0 + (temp/XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) );
    double df = XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) + temp;

    if (mxIsNaN(df)==1){
        return XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy);
    } else {
        return df;
    }
}

double dividingFactorOnePlus(Element *iElement, ElementEnergyLine *lambda, double energy, Compound* compound, double theta1, double theta2){

    int numberOfElements = (compound->getElements()).size();
    std::vector<Element> elementSet = (compound->getElements());
    double temp = 0.0;

    for(int i = 0; i < numberOfElements; i++){
        int numberOfEnergyLines = (elementSet[i].getEnergyLines()).size();
        for(int j = 0; j < numberOfEnergyLines; j++){
            temp = temp + xSjnu(iElement, &elementSet[i], compound, energy, lambda, &(elementSet[i].getEnergyLines())[j], theta1, theta2);
        }
    }

    // convenction with old dividing factor
    double df = 1.0 + (temp/XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) );
    //double df = XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) + temp;

    if (mxIsNaN(df)==1){
        return XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy);
    } else {
        return df;
    }
}

/*
 * Dividing factor for thin samples
 */

double dividingFactorThin(Element *iElement, ElementEnergyLine *lambda, double energy, Compound* compound, double theta1, double theta2, double mT){

    int numberOfElements = (compound->getElements()).size();
    std::vector<Element> elementSet = (compound->getElements());
    double temp = 0.0;

    for(int i = 0; i < numberOfElements; i++){
        int numberOfEnergyLines = (elementSet[i].getEnergyLines()).size();
        for(int j = 0; j < numberOfEnergyLines; j++){
            // xSjnuThin stands for the van Dyck, van Espen integral result
            temp = temp + xSjnuThin(iElement, &elementSet[i], compound, energy, lambda, &(elementSet[i].getEnergyLines())[j], theta1, theta2, mT);
        }
    }

    // convenction with old dividing factor
    //double df = 1.0 + (temp/XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) );
    double muS = muStar(compound, energy, lambda->getEnergy(), theta1, theta2);
    double df = XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy)*(1 - exp(-1.0*muS*mT)) + temp;

    if (mxIsNaN(df)==1){
        return XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy);
    } else {
        return df;
    }
}

/*
 * Dividing factor for thin samples - thin variant
 */

double dividingFactorThinOnePlus(Element *iElement, ElementEnergyLine *lambda, double energy, Compound* compound, double theta1, double theta2, double mT){

    int numberOfElements = (compound->getElements()).size();
    std::vector<Element> elementSet = (compound->getElements());
    double temp = 0.0;

    for(int i = 0; i < numberOfElements; i++){
        int numberOfEnergyLines = (elementSet[i].getEnergyLines()).size();
        for(int j = 0; j < numberOfEnergyLines; j++){
            // xSjnuThin stands for the van Dyck, van Espen integral result
            temp = temp + xSjnuThin(iElement, &elementSet[i], compound, energy, lambda, &(elementSet[i].getEnergyLines())[j], theta1, theta2, mT);
        }
    }

    // convenction with old dividing factor
    //double df = 1.0 + (temp/XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) );
    double muS = muStar(compound, energy, lambda->getEnergy(), theta1, theta2);
    double df = 1.0 + temp/(XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy)*(1 - exp(-1.0*muS*mT)));

    if (mxIsNaN(df)==1){
        return XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy);
    } else {
        return df;
    }
}

/*
 * the mu star term
 */

double muStar(Compound *compound, double energy, double lambdaEnergy, double theta1, double theta2) {

    return ((muTotal(compound, energy)/cos(theta1)) + (muTotal(compound,lambdaEnergy)/cos(theta2)));

}

/*
 * the derivative of the L term
 */

double xdL(Element *nElement, double energy, double nuEnergy, Compound *compound, double theta1){

   // depending on the value of muTotal can be undetermined (for example when Ejnu=0)
   double A =  XRLFunctions::xrlCSTotal(nElement->getZ(), energy)*(nElement->getW())*cos(theta1)/(2.0*muTotal(compound,energy));
   double B1 = 1.0 / (muTotal(compound,nuEnergy)*cos(theta1));
   double B2 = muTotal(compound,energy)*B1;
   double C1 = 1.0/muTotal(compound,energy);
   return A*(B1*(1.0/(1.0 + B2)    )  - C1*log(1.0 + B2)              );

}

/*
 * the L term of the Sherman equation
 */

double xL(Compound *compound, double energy, double lambdaEnergy, double nuEnergy, double theta1, double theta2 ){

    // the value of xL can be undetermined for example for Eilambda=0
    // this usually occurs for ligth elements
    return 0.5*( (cos(theta1)/ muTotal(compound, energy) )*log( 1.0 +  (muTotal(compound, energy)/( muTotal(compound, nuEnergy)*cos(theta1) )   ) )     +  (cos(theta2)/muTotal(compound,lambdaEnergy) ) * log( 1.0 +  (muTotal(compound, lambdaEnergy)/( muTotal(compound, nuEnergy)*cos(theta2) )   ) )  );

}

/*
 * total attenuation coefficient mu
 *
 */

double muTotal(Compound *compound, double energy){

    int numberOfElements = ((*compound).getElements()).size();

    double muTotal = 0.0;
    for(int i = 0; i < numberOfElements; i++) {
        muTotal = muTotal + ((*compound).getElements())[i].getW()*XRLFunctions::xrlCSTotal(((*compound).getElements())[i].getZ(), energy  );
    }

    return muTotal;
}

/*
 * makeElementVector
 */

std::vector<Element> makeElementVector(double *zArray, double* wArray, mwSize m) {

    std::vector<Element> elementVector;

    for(mwSize i = 0; i < m; i++){

        Element *tempElement = new Element((int)(zArray[i]), wArray[i]);
        elementVector.push_back( *tempElement);
    }
    return elementVector;
}

/*
 * beam attenuation part of Cin - thick variant
 */

double CinBA(Element *iElement, Element *nElement, double energy, ElementEnergyLine *lambda, Compound *compound, double theta1, double theta2){

    // convenction with old dividing factor
    //double oneOverDividingFactor = 1.0/dividingFactor(iElement, lambda, energy, compound, theta1, theta2);
    double tauOverDividingFactor = XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) / dividingFactor(iElement, lambda, energy, compound, theta1, theta2);
    double A = atomKroneckerDelta(iElement, nElement) - XRLFunctions::xrlCSTotal(nElement->getZ(),energy)*(nElement->getW())/(cos(theta1)*  muStar(compound, energy, lambda->getEnergy(), theta1, theta2)  );
    return tauOverDividingFactor*A;
}

/*
 * beam attenuation part of Cin - thin variant
 */

double CinBAThin(Element *iElement, Element *nElement, double energy, ElementEnergyLine *lambda, Compound *compound, double theta1, double theta2, double mT){

    double tauRound = ceilf(XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) * zeroTolerance) / zeroTolerance;
    if(  tauRound == 0.0 ){
        return 0.0;
    }

    // convenction with old dividing factor
    //double oneOverDividingFactor = 1.0/dividingFactor(iElement, lambda, energy, compound, theta1, theta2);
    double mS = muStar(compound, energy, lambda->getEnergy(), theta1, theta2);
    double tauOverDividingFactor = (XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy)*(1 - exp(-1.0*mS*mT) ) ) / dividingFactorThin(iElement, lambda, energy, compound, theta1, theta2, mT);
    double oneMinusF = 1.0 - mS*mT*( exp(-mS*mT) / ( 1 - exp(-mS*mT) ) );
    double A = atomKroneckerDelta(iElement, nElement) - ( XRLFunctions::xrlCSTotal(nElement->getZ(),energy)*(nElement->getW())/(cos(theta1)*  mS  ) ) * oneMinusF;
    return tauOverDividingFactor*A;

}

/*
 * CinBa--Thick-no h division
 */

double CinBAThinHOnePlus(Element *iElement, Element *nElement, double energy, ElementEnergyLine *lambda, Compound *compound, double theta1, double theta2, double mT){

    double tauRound = ceilf(XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) * zeroTolerance) / zeroTolerance;
    if(  tauRound == 0.0 ){
        return 0.0;
    }

    // convenction with old dividing factor
    //double oneOverDividingFactor = 1.0/dividingFactor(iElement, lambda, energy, compound, theta1, theta2);
    double mS = muStar(compound, energy, lambda->getEnergy(), theta1, theta2);
    double tauOverDividingFactor = 1.0 / dividingFactorThinOnePlus(iElement, lambda, energy, compound, theta1, theta2, mT);
    double oneMinusF = 1.0 - mS*mT*( exp(-mS*mT) / ( 1 - exp(-mS*mT) ) );
    double A = atomKroneckerDelta(iElement, nElement) - ( XRLFunctions::xrlCSTotal(nElement->getZ(),energy)*(nElement->getW())/(cos(theta1)*  mS  ) ) * oneMinusF;
    return tauOverDividingFactor*A;

}

/*
 * indirect excitation part of Cin
 */

double CinIE(Element *iElement, Element *nElement, double energy, ElementEnergyLine *lambda, Compound *compound, double theta1, double theta2){

    int numberOfElements = (compound->getElements()).size();
    std::vector<Element> elementSet = (compound->getElements());
    double temp = 0.0;

    for(int i = 0; i < numberOfElements; i++){
        int numberOfEnergyLines = (elementSet[i].getEnergyLines()).size();
        for(int j = 0; j < numberOfEnergyLines; j++){

            double xLv = xL(compound, energy, lambda->getEnergy(), (elementSet[i].getEnergyLines())[j].getEnergy(), theta1, theta2 );
            double xdLv = xdL(nElement,  energy,  (elementSet[i].getEnergyLines())[j].getEnergy(), compound, theta1);

            double part1 = atomKroneckerDelta( &elementSet[i], nElement) -  XRLFunctions::xrlCSTotal(nElement->getZ(),energy)*(nElement->getW())/(cos(theta1)*  muStar(compound, energy, lambda->getEnergy(), theta1, theta2)  );
            double part2 = xdLv/xLv;
            double S = xSjnu(iElement, &elementSet[i], compound, energy, lambda, &(elementSet[i].getEnergyLines())[j], theta1, theta2) ;

            if(mxIsNaN(part1)==1) {continue;}
            if(mxIsNaN(part2)==1) {continue;}

            temp = temp +  S*(part1 + part2);
        }
    }

    // convenction with old dividing factor
    //double cieReturn = temp/(  XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) *dividingFactor(iElement, lambda, energy, compound, theta1, theta2)  );
    double cieReturn = temp/( dividingFactor(iElement, lambda, energy, compound, theta1, theta2) );

    if (mxIsNaN(cieReturn)==1){
        return 0.0;
    } else {
        return cieReturn;
    }

}

/*
 * Cin
 */

double Cin(Element *iElement, Element *nElement, double energy, ElementEnergyLine *lambda, Compound *compound, double theta1, double theta2){

    /*
     * when \tau_{i,\lambda}(E) is zero then the formulas do not make any sense because there is no fluorescence
     * therefore there is no point in counting the c coefficients
     */

    double tauRound = ceilf(XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) * zeroTolerance) / zeroTolerance;
    if(  tauRound == 0.0 ){
        return 0.0;
    }

    double baPart = CinBA(iElement, nElement, energy, lambda, compound, theta1, theta2);
    double iePart = CinIE(iElement, nElement, energy, lambda, compound, theta1, theta2);
    return baPart + iePart;

}

/*
 * Cin Thin
 */

double CinThin(Element *iElement, Element *nElement, double energy, ElementEnergyLine *lambda, Compound *compound, double theta1, double theta2, double mT){

    /*
     * when \tau_{i,\lambda}(E) is zero then the formulas do not make any sense because there is no fluorescence
     * therefore there is no point in counting the c coefficients
     */

    double tauRound = ceilf(XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) * zeroTolerance) / zeroTolerance;
    if(  tauRound == 0.0 ){
        return 0.0;
    }

    double baPart = CinBAThin(iElement, nElement, energy, lambda, compound, theta1, theta2, mT);
    double iePart = CinIEThin(iElement, nElement, energy, lambda, compound, theta1, theta2, mT);
    return baPart + iePart;

}

/*
 * yPart
 */

double yPart(Element *iElement,double energy, ElementEnergyLine *lambda,Compound *compound, double theta1, double theta2){

        double dF = dividingFactor(iElement, lambda, energy, compound, theta1, theta2);
        // convenction with old dividing factor
        //double yP = XRLFunctions::xrlCSFluorLineKissel( iElement->getZ() , lambda->getXrlint(), energy) / muStar(compound, energy, lambda->getEnergy(), theta1, theta2);
        double yP = 1.0 / muStar(compound, energy, lambda->getEnergy(), theta1, theta2);

        return yP*dF;

}

/*
 * yPart van Dyck
 */

double yPartThin(Element *iElement,double energy, ElementEnergyLine *lambda,Compound *compound, double theta1, double theta2, double mT){

        double dF = dividingFactorThin(iElement, lambda, energy, compound, theta1, theta2, mT);
        // convenction with old dividing factor
        //double yP = XRLFunctions::xrlCSFluorLineKissel( iElement->getZ() , lambda->getXrlint(), energy) / muStar(compound, energy, lambda->getEnergy(), theta1, theta2);
        double mS = muStar(compound, energy, lambda->getEnergy(), theta1, theta2);
        double yP = ( 1.0 - exp(-mS*mT) )/ mS ;

        return yP*dF;

}

/*
 * L as given by van Dyck
 */

double xLCurly(Compound *compound, double energy, double lambdaEnergy, double nuEnergy, double theta1, double theta2, double mT){

    double mEil = muTotal(compound, lambdaEnergy);
    double mEjnu = muTotal(compound, nuEnergy);
    double mE = muTotal(compound, energy);

    // when the sums in the IE term run over energy lines of the i atom
    // we get infiniteies beacuse these sum elements should not exist
    // they are cancelled by the tau's

    // F1, F2 ... F5 - in the future these have to be replaced with separate functions
    // so we don't repeat code
    double F1 = log( abs(1 - mE /(cos(theta1) * mEjnu) ) ) + E1Fun(  ( mEjnu - mE/cos(theta1) )*mT  );
    double F2 = log( abs(1 - mEil /(cos(theta2) * mEjnu) ) ) + E1Fun(  ( mEjnu - mEil/cos(theta2) )*mT  );
    double F3 = log(1 + mE /(cos(theta1) * mEjnu ) ) + E1Fun( ( mEjnu + mE/cos(theta1) )*mT );
    double F4 = log(1 + mEil /(cos(theta2) * mEjnu ) ) + E1Fun( ( mEjnu + mEil/cos(theta2) )*mT );
    double F5 = ( exp(-mEil*mT/cos(theta2)) + exp(-mE*mT/cos(theta1)) )*E1Fun(mEjnu*mT);

    double muS = muStar(compound, energy, lambdaEnergy, theta1, theta2);

    double sum1 = ( cos(theta1)/(2.0*mE) )*(exp(-muS*mT)*F1+F3-F5);
    double sum2 = ( cos(theta2)/(2.0*mEil) )*(exp(-muS*mT)*F2+F4-F5);

    return (sum1 + sum2);

}

/*
 *van Dyck integration result
 */

double xSjnuThin( Element *iElement, Element *jElement,Compound* compound, double energy, ElementEnergyLine *lambda, ElementEnergyLine *nu, double theta1, double theta2, double mT){

    double tauila = XRLFunctions::xrlCSFluorLineKissel( iElement->getZ() , lambda->getXrlint(), nu->getEnergy());
    double taujnu = XRLFunctions::xrlCSFluorLineKissel( jElement->getZ() , nu->getXrlint(), energy);

    if ( ( (ceilf(tauila * zeroTolerance) / zeroTolerance) == 0 )  || ( (ceilf(taujnu * zeroTolerance) / zeroTolerance) == 0 ) )
        return 0.0;

    double LCurly = xLCurly(compound, energy, lambda->getEnergy(), nu->getEnergy(), theta1, theta2, mT);

    double res = (jElement->getW())*tauila*taujnu*LCurly;
    return res;

}

/*
derivative of van Dyck equation
this should reduce to xdL for T -> infinity
the absolute value from van Dyck is not present
 */

double xdLThin(Element *nElement, double energy,double lambdaEnergy ,double nuEnergy, Compound *compound, double theta1, double theta2, double mT){

    double mEil = muTotal(compound, lambdaEnergy);
    double mEjnu = muTotal(compound, nuEnergy );
    double mE = muTotal(compound, energy );
    double mEn = XRLFunctions::xrlCSTotal(nElement->getZ(), energy);
    double wn = nElement->getW();

    // F1, F2 ... F5 - in the future these have to be replaced with separate functions
    // so we don't repeat code
    double F1 = log( abs(1 - mE /(cos(theta1) * mEjnu) ) ) + E1Fun(  ( mEjnu - mE/cos(theta1) )*mT  );
    double F2 = log( abs(1 - mEil /(cos(theta2) * mEjnu) ) ) + E1Fun(  ( mEjnu - mEil/cos(theta2) )*mT  );
    double F3 = log(1 + mE /(cos(theta1) * mEjnu ) ) + E1Fun( ( mEjnu + mE/cos(theta1) )*mT );
    double F4 = log(1 + mEil /(cos(theta2) * mEjnu ) ) + E1Fun( ( mEjnu + mEil/cos(theta2) )*mT );
    double F5 = ( exp(-mEil*mT/cos(theta2)) + exp(-mE*mT/cos(theta1)) )*E1Fun(mEjnu*mT);

    double muS = muStar(compound, energy, lambdaEnergy, theta1, theta2);

    double F1F3F5 = exp(-muS*mT)*F1+F3-F5;
    double F2F4F5 = exp(-muS*mT)*F2+F4-F5;

    double xdLThinPart1 = -F1F3F5*(  (mEn*wn*cos(theta1)) / (2*mE*mE) );

    double onePlusExp = 1.0 - exp( - (mEjnu + mE/cos(theta1) )*mT );
    double onePlusmExp = 1.0 - exp( - (mEjnu - mE/cos(theta1) )*mT );
    double oneMinusFrac = 1.0 - mE/(mEjnu*cos(theta1));
    double onePlusFrac = 1.0 + mE/(mEjnu*cos(theta1));

    double bigBraketPart1 = -F1*mT*exp(-muS*mT);
    double bigBraketPart2 = -1.0*( (exp(-muS*mT)/mEjnu)/oneMinusFrac )*onePlusmExp;
    double bigBraketPart3 = ( (1.0/mEjnu)/onePlusFrac )*onePlusExp;
    double bigBraketPart4 = mT*E1Fun(mEjnu*mT)*exp(-(mE*mT)/cos(theta1));
    double frontOfBigBraket = ((wn*mEn)/(2.0*mE)) ;
    double bigBraketTerm = frontOfBigBraket*(bigBraketPart1 + bigBraketPart2 + bigBraketPart3 + bigBraketPart4);

    double smallBraket1 = F2*exp(-muS*mT);
    double smallBraket2 = -1.0*E1Fun(mEjnu*mT)*exp(-(mE*mT)/cos(theta1));
    double frontSmallBraket = -1.0*mT*( (mEn*wn*cos(theta2))/(2*mEil*cos(theta1)) );
    double smallBraketTerm = frontSmallBraket*(smallBraket1 + smallBraket2);

    double xdLThinValue = xdLThinPart1 + bigBraketTerm + smallBraketTerm;

    return xdLThinValue;

}

/*
 * C^IE for thin samples
 */

double CinIEThin(Element *iElement, Element *nElement, double energy, ElementEnergyLine *lambda, Compound *compound, double theta1, double theta2, double mT){

    int numberOfElements = (compound->getElements()).size();
    std::vector<Element> elementSet = (compound->getElements());
    double temp = 0.0;

    for(int i = 0; i < numberOfElements; i++){
        int numberOfEnergyLines = (elementSet[i].getEnergyLines()).size();
        for(int j = 0; j < numberOfEnergyLines; j++){

            double xLv = xLCurly(compound, energy, lambda->getEnergy(), (elementSet[i].getEnergyLines())[j].getEnergy(), theta1, theta2, mT );
            double xdLv = xdLThin(nElement, energy, lambda->getEnergy() , (elementSet[i].getEnergyLines())[j].getEnergy(), compound, theta1, theta2, mT);

            double part1 = atomKroneckerDelta( &elementSet[i], nElement) -  XRLFunctions::xrlCSTotal(nElement->getZ(),energy)*(nElement->getW())/(cos(theta1)*  muStar(compound, energy, lambda->getEnergy(), theta1, theta2)  );
            double part2 = xdLv/xLv;
            double S = xSjnuThin(iElement, &elementSet[i], compound, energy, lambda, &(elementSet[i].getEnergyLines())[j], theta1, theta2, mT) ;

            if(mxIsNaN(part1)==1) {continue;}
            if(mxIsNaN(part2)==1) {continue;}

            temp = temp +  S*(part1 + part2);
        }
    }

    // convenction with old dividing factor
    //double cieReturn = temp/(  XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) *dividingFactor(iElement, lambda, energy, compound, theta1, theta2)  );
    double cieReturn = temp/( dividingFactorThin(iElement, lambda, energy, compound, theta1, theta2, mT) );

    if (mxIsNaN(cieReturn)==1){
        return 0.0;
    } else {
        return cieReturn;
    }

}

/*
 * C^IE for thin samples H one plus
 */

double CinIEThinHOnePlus(Element *iElement, Element *nElement, double energy, ElementEnergyLine *lambda, Compound *compound, double theta1, double theta2, double mT){

    int numberOfElements = (compound->getElements()).size();
    std::vector<Element> elementSet = (compound->getElements());
    double temp = 0.0;

    for(int i = 0; i < numberOfElements; i++){
        int numberOfEnergyLines = (elementSet[i].getEnergyLines()).size();
        for(int j = 0; j < numberOfEnergyLines; j++){

            double xLv = xLCurly(compound, energy, lambda->getEnergy(), (elementSet[i].getEnergyLines())[j].getEnergy(), theta1, theta2, mT );
            double xdLv = xdLThin(nElement, energy, lambda->getEnergy() , (elementSet[i].getEnergyLines())[j].getEnergy(), compound, theta1, theta2, mT);

            double part1 = atomKroneckerDelta( &elementSet[i], nElement) -  XRLFunctions::xrlCSTotal(nElement->getZ(),energy)*(nElement->getW())/(cos(theta1)*  muStar(compound, energy, lambda->getEnergy(), theta1, theta2)  );
            double part2 = xdLv/xLv;
            double S = xSjnuThin(iElement, &elementSet[i], compound, energy, lambda, &(elementSet[i].getEnergyLines())[j], theta1, theta2, mT) ;

            if(mxIsNaN(part1)==1) {continue;}
            if(mxIsNaN(part2)==1) {continue;}

            temp = temp +  S*(part1 + part2);
        }
    }

    // convenction with old dividing factor
    //double cieReturn = temp/(  XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy) *dividingFactor(iElement, lambda, energy, compound, theta1, theta2)  );
    double muS = muStar(compound, energy, lambda->getEnergy(), theta1, theta2);
    double tau = XRLFunctions::xrlCSFluorLineKissel(iElement->getZ(), lambda->getXrlint(), energy);
    double df = dividingFactorThinOnePlus(iElement, lambda, energy, compound, theta1, theta2, mT);
    double cieReturn = temp/( tau*(1.0 - exp(-1.0*muS*mT) )*df );

    if (mxIsNaN(cieReturn)==1){
        return 0.0;
    } else {
        return cieReturn;
    }

}


//  (C) Copyright Nick Thompson 2018.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_MATH_TOOLS_NORMS_HPP
#define BOOST_MATH_TOOLS_NORMS_HPP
#include <algorithm>
#include <iterator>
#include <boost/assert.hpp>
#include <boost/math/tools/complex.hpp>

namespace boost::math::tools {

// Mallat, "A Wavelet Tour of Signal Processing", equation 2.60:
template<class ForwardIterator>
auto total_variation(ForwardIterator first, ForwardIterator last)
{
    using T = typename std::iterator_traits<ForwardIterator>::value_type;
    using std::abs;
    BOOST_ASSERT_MSG(first != last && std::next(first) != last, "At least two samples are required to compute the total variation.");
    auto it = first;
    if constexpr (std::is_unsigned<T>::value)
    {
        T tmp = *it;
        double tv = 0;
        while (++it != last)
        {
            if (*it > tmp)
            {
                tv += *it - tmp;
            }
            else
            {
                tv += tmp - *it;
            }
            tmp = *it;
        }
        return tv;
    }
    else if constexpr (std::is_integral<T>::value)
    {
        double tv = 0;
        double tmp = *it;
        while(++it != last)
        {
            double tmp2 = *it;
            tv += abs(tmp2 - tmp);
            tmp = *it;
        }
        return tv;
    }
    else
    {
        T tmp = *it;
        T tv = 0;
        while (++it != last)
        {
            tv += abs(*it - tmp);
            tmp = *it;
        }
        return tv;
    }
}

template<class Container>
inline auto total_variation(Container const & v)
{
    return total_variation(v.cbegin(), v.cend());
}

template<class ForwardIterator>
auto sup_norm(ForwardIterator first, ForwardIterator last)
{
    BOOST_ASSERT_MSG(first != last, "At least one value is required to compute the sup norm.");
    using T = typename std::iterator_traits<ForwardIterator>::value_type;
    using std::abs;
    if constexpr (boost::math::tools::is_complex_type<T>::value)
    {
        auto it = std::max_element(first, last, [](T a, T b) { return abs(b) > abs(a); });
        return abs(*it);
    }
    else if constexpr (std::is_unsigned<T>::value)
    {
        return *std::max_element(first, last);
    }
    else
    {
        auto pair = std::minmax_element(first, last);
        if (abs(*pair.first) > abs(*pair.second))
        {
            return abs(*pair.first);
        }
        else
        {
            return abs(*pair.second);
        }
    }
}

template<class Container>
inline auto sup_norm(Container const & v)
{
    return sup_norm(v.cbegin(), v.cend());
}

template<class ForwardIterator>
auto l1_norm(ForwardIterator first, ForwardIterator last)
{
    using T = typename std::iterator_traits<ForwardIterator>::value_type;
    using std::abs;
    if constexpr (std::is_unsigned<T>::value)
    {
        double l1 = 0;
        for (auto it = first; it != last; ++it)
        {
            l1 += *it;
        }
        return l1;
    }
    else if constexpr (std::is_integral<T>::value)
    {
        double l1 = 0;
        for (auto it = first; it != last; ++it)
        {
            double tmp = *it;
            l1 += abs(tmp);
        }
        return l1;
    }
    else
    {
        decltype(abs(*first)) l1 = 0;
        for (auto it = first; it != last; ++it)
        {
            l1 += abs(*it);
        }
        return l1;
    }

}

template<class Container>
inline auto l1_norm(Container const & v)
{
    return l1_norm(v.cbegin(), v.cend());
}

template<class ForwardIterator>
auto l2_norm(ForwardIterator first, ForwardIterator last)
{
    using T = typename std::iterator_traits<ForwardIterator>::value_type;
    using std::abs;
    using std::norm;
    using std::sqrt;
    using std::is_floating_point;
    if constexpr (boost::math::tools::is_complex_type<T>::value)
    {
        typedef typename T::value_type Real;
        Real l2 = 0;
        for (auto it = first; it != last; ++it)
        {
            l2 += norm(*it);
        }
        Real result = sqrt(l2);
        if (!isfinite(result))
        {
            Real a = sup_norm(first, last);
            l2 = 0;
            for (auto it = first; it != last; ++it)
            {
                l2 += norm(*it/a);
            }
            return a*sqrt(l2);
        }
        return result;
    }
    else if constexpr (is_floating_point<T>::value ||
                       std::numeric_limits<T>::max_exponent)
    {
        T l2 = 0;
        for (auto it = first; it != last; ++it)
        {
            l2 += (*it)*(*it);
        }
        T result = sqrt(l2);
        // Higham, Accuracy and Stability of Numerical Algorithms,
        // Problem 27.5 presents a different algorithm to deal with overflow.
        // The algorithm used here takes 3 passes *if* there is overflow.
        // Higham's algorithm is 1 pass, but more requires operations than the no overflow case.
        // I'm operating under the assumption that overflow is rare since the dynamic range of floating point numbers is huge.
        if (!isfinite(result))
        {
            T a = sup_norm(first, last);
            l2 = 0;
            for (auto it = first; it != last; ++it)
            {
                T tmp = *it/a;
                l2 += tmp*tmp;
            }
            return a*sqrt(l2);
        }
        return result;
    }
    else
    {
        double l2 = 0;
        for (auto it = first; it != last; ++it)
        {
            double tmp = *it;
            l2 += tmp*tmp;
        }
        return sqrt(l2);
    }
}

template<class Container>
inline auto l2_norm(Container const & v)
{
    return l2_norm(v.cbegin(), v.cend());
}

template<class ForwardIterator>
size_t l0_pseudo_norm(ForwardIterator first, ForwardIterator last)
{
    using RealOrComplex = typename std::iterator_traits<ForwardIterator>::value_type;
    size_t count = 0;
    for (auto it = first; it != last; ++it)
    {
        if (*it != RealOrComplex(0))
        {
            ++count;
        }
    }
    return count;
}

template<class Container>
inline size_t l0_pseudo_norm(Container const & v)
{
    return l0_pseudo_norm(v.cbegin(), v.cend());
}

template<class ForwardIterator>
size_t hamming_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
{
    size_t count = 0;
    auto it1 = first1;
    auto it2 = first2;
    while (it1 != last1)
    {
        if (*it1++ != *it2++)
        {
            ++count;
        }
    }
    return count;
}

template<class Container>
inline size_t hamming_distance(Container const & v, Container const & w)
{
    return hamming_distance(v.cbegin(), v.cend(), w.cbegin());
}

template<class ForwardIterator>
auto lp_norm(ForwardIterator first, ForwardIterator last, unsigned p)
{
    using std::abs;
    using std::pow;
    using std::is_floating_point;
    using std::isfinite;
    using RealOrComplex = typename std::iterator_traits<ForwardIterator>::value_type;
    if constexpr (boost::math::tools::is_complex_type<RealOrComplex>::value)
    {
        using std::norm;
        using Real = typename RealOrComplex::value_type;
        Real lp = 0;
        for (auto it = first; it != last; ++it)
        {
            lp += pow(abs(*it), p);
        }

        auto result = pow(lp, Real(1)/Real(p));
        if (!isfinite(result))
        {
            auto a = boost::math::tools::sup_norm(first, last);
            Real lp = 0;
            for (auto it = first; it != last; ++it)
            {
                lp += pow(abs(*it)/a, p);
            }
            result = a*pow(lp, Real(1)/Real(p));
        }
        return result;
    }
    else if constexpr (is_floating_point<RealOrComplex>::value || std::numeric_limits<RealOrComplex>::max_exponent)
    {
        BOOST_ASSERT_MSG(p >= 0, "For p < 0, the lp norm is not a norm");
        RealOrComplex lp = 0;

        for (auto it = first; it != last; ++it)
        {
            lp += pow(abs(*it), p);
        }

        RealOrComplex result = pow(lp, RealOrComplex(1)/RealOrComplex(p));
        if (!isfinite(result))
        {
            RealOrComplex a = boost::math::tools::sup_norm(first, last);
            lp = 0;
            for (auto it = first; it != last; ++it)
            {
                lp += pow(abs(*it)/a, p);
            }
            result = a*pow(lp, RealOrComplex(1)/RealOrComplex(p));
        }
        return result;
    }
    else
    {
        double lp = 0;

        for (auto it = first; it != last; ++it)
        {
            double tmp = *it;
            lp += pow(abs(tmp), p);
        }
        double result = pow(lp, 1.0/double(p));
        if (!isfinite(result))
        {
            double a = boost::math::tools::sup_norm(first, last);
            lp = 0;
            for (auto it = first; it != last; ++it)
            {
                double tmp = *it;
                lp += pow(abs(tmp)/a, p);
            }
            result = a*pow(lp, double(1)/double(p));
        }
        return result;
    }
}

template<class Container>
inline auto lp_norm(Container const & v, unsigned p)
{
    return lp_norm(v.cbegin(), v.cend(), p);
}

template<class ForwardIterator>
auto lp_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2, unsigned p)
{
    using std::pow;
    using std::abs;
    using std::is_floating_point;
    using std::isfinite;
    using RealOrComplex = typename std::iterator_traits<ForwardIterator>::value_type;
    auto it1 = first1;
    auto it2 = first2;

    if constexpr (boost::math::tools::is_complex_type<RealOrComplex>::value)
    {
        using Real = typename RealOrComplex::value_type;
        using std::norm;
        Real dist = 0;
        while(it1 != last1)
        {
            auto tmp = *it1++ - *it2++;
            dist += pow(abs(tmp), p);
        }
        return pow(dist, Real(1)/Real(p));
    }
    else if constexpr (is_floating_point<RealOrComplex>::value || std::numeric_limits<RealOrComplex>::max_exponent)
    {
        RealOrComplex dist = 0;
        while(it1 != last1)
        {
            auto tmp = *it1++ - *it2++;
            dist += pow(abs(tmp), p);
        }
        return pow(dist, RealOrComplex(1)/RealOrComplex(p));
    }
    else
    {
        double dist = 0;
        while(it1 != last1)
        {
            double tmp1 = *it1++;
            double tmp2 = *it2++;
            // Naively you'd expect the integer subtraction to be faster,
            // but this can overflow or wraparound:
            //double tmp = *it1++ - *it2++;
            dist += pow(abs(tmp1 - tmp2), p);
        }
        return pow(dist, 1.0/double(p));
    }
}

template<class Container>
inline auto lp_distance(Container const & v, Container const & w, unsigned p)
{
    return lp_distance(v.cbegin(), v.cend(), w.cbegin(), p);
}

template<class ForwardIterator>
auto l1_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
{
    using std::abs;
    using std::is_floating_point;
    using std::isfinite;
    using T = typename std::iterator_traits<ForwardIterator>::value_type;
    auto it1 = first1;
    auto it2 = first2;
    if constexpr (boost::math::tools::is_complex_type<T>::value)
    {
        using Real = typename T::value_type;
        Real sum = 0;
        while (it1 != last1) {
            sum += abs(*it1++ - *it2++);
        }
        return sum;
    }
    else if constexpr (is_floating_point<T>::value || std::numeric_limits<T>::max_exponent)
    {
        T sum = 0;
        while (it1 != last1)
        {
            sum += abs(*it1++ - *it2++);
        }
        return sum;
    }
    else if constexpr (std::is_unsigned<T>::value)
    {
        double sum = 0;
        while(it1 != last1)
        {
            T x1 = *it1++;
            T x2 = *it2++;
            if (x1 > x2)
            {
                sum += (x1 - x2);
            }
            else
            {
                sum += (x2 - x1);
            }
        }
        return sum;
    }
    else if constexpr (std::is_integral<T>::value)
    {
        double sum = 0;
        while(it1 != last1)
        {
            double x1 = *it1++;
            double x2 = *it2++;
            sum += abs(x1-x2);
        }
        return sum;
    }
    else
    {
        BOOST_ASSERT_MSG(false, "Could not recognize type.");
    }

}

template<class Container>
auto l1_distance(Container const & v, Container const & w)
{
    using std::size;
    BOOST_ASSERT_MSG(size(v) == size(w),
                     "L1 distance requires both containers to have the same number of elements");
    return l1_distance(v.cbegin(), v.cend(), w.begin());
}

template<class ForwardIterator>
auto l2_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
{
    using std::abs;
    using std::norm;
    using std::sqrt;
    using std::is_floating_point;
    using std::isfinite;
    using T = typename std::iterator_traits<ForwardIterator>::value_type;
    auto it1 = first1;
    auto it2 = first2;
    if constexpr (boost::math::tools::is_complex_type<T>::value)
    {
        using Real = typename T::value_type;
        Real sum = 0;
        while (it1 != last1) {
            sum += norm(*it1++ - *it2++);
        }
        return sqrt(sum);
    }
    else if constexpr (is_floating_point<T>::value || std::numeric_limits<T>::max_exponent)
    {
        T sum = 0;
        while (it1 != last1)
        {
            T tmp = *it1++ - *it2++;
            sum += tmp*tmp;
        }
        return sqrt(sum);
    }
    else if constexpr (std::is_unsigned<T>::value)
    {
        double sum = 0;
        while(it1 != last1)
        {
            T x1 = *it1++;
            T x2 = *it2++;
            if (x1 > x2)
            {
                double tmp = x1-x2;
                sum += tmp*tmp;
            }
            else
            {
                double tmp = x2 - x1;
                sum += tmp*tmp;
            }
        }
        return sqrt(sum);
    }
    else
    {
        double sum = 0;
        while(it1 != last1)
        {
            double x1 = *it1++;
            double x2 = *it2++;
            double tmp = x1-x2;
            sum += tmp*tmp;
        }
        return sqrt(sum);
    }
}

template<class Container>
auto l2_distance(Container const & v, Container const & w)
{
    using std::size;
    BOOST_ASSERT_MSG(size(v) == size(w),
                     "L2 distance requires both containers to have the same number of elements");
    return l2_distance(v.cbegin(), v.cend(), w.begin());
}

template<class ForwardIterator>
auto sup_distance(ForwardIterator first1, ForwardIterator last1, ForwardIterator first2)
{
    using std::abs;
    using std::norm;
    using std::sqrt;
    using std::is_floating_point;
    using std::isfinite;
    using T = typename std::iterator_traits<ForwardIterator>::value_type;
    auto it1 = first1;
    auto it2 = first2;
    if constexpr (boost::math::tools::is_complex_type<T>::value)
    {
        using Real = typename T::value_type;
        Real sup_sq = 0;
        while (it1 != last1) {
            Real tmp = norm(*it1++ - *it2++);
            if (tmp > sup_sq) {
                sup_sq = tmp;
            }
        }
        return sqrt(sup_sq);
    }
    else if constexpr (is_floating_point<T>::value || std::numeric_limits<T>::max_exponent)
    {
        T sup = 0;
        while (it1 != last1)
        {
            T tmp = *it1++ - *it2++;
            if (sup < abs(tmp))
            {
                sup = abs(tmp);
            }
        }
        return sup;
    }
    else // integral values:
    {
        double sup = 0;
        while(it1 != last1)
        {
            T x1 = *it1++;
            T x2 = *it2++;
            double tmp;
            if (x1 > x2)
            {
                tmp = x1-x2;
            }
            else
            {
                tmp = x2 - x1;
            }
            if (sup < tmp) {
                sup = tmp;
            }
        }
        return sup;
    }
}

template<class Container>
auto sup_distance(Container const & v, Container const & w)
{
    using std::size;
    BOOST_ASSERT_MSG(size(v) == size(w),
                     "sup distance requires both containers to have the same number of elements");
    return sup_distance(v.cbegin(), v.cend(), w.begin());
}

}
#endif


#include <boost/hana.hpp>

int main()
{
    constexpr auto t{ boost::hana::make_tuple(5, 2, 3, 1, 4) };
    constexpr bool r1{ boost::hana::any_of(t, boost::hana::equal.to(3)) };
    static_assert(r1);

    constexpr int a[5]{ 5, 2, 3 };
    constexpr bool r2{ boost::hana::any_of(a, boost::hana::equal.to(6)) };
    static_assert(!r2);
}


/*
 [auto_generated]
 boost/numeric/odeint/iterator/times_iterator.hpp

 [begin_description]
 Iterator for iterating through the solution of an ODE with oscillator calls at times from a given sequence.
 [end_description]

 Copyright 2009-2013 Karsten Ahnert
 Copyright 2009-2013 Mario Mulansky

 Distributed under the Boost Software License, Version 1.0.
 (See accompanying file LICENSE_1_0.txt or
 copy at http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_NUMERIC_ODEINT_ITERATOR_TIMES_ITERATOR_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_ITERATOR_TIMES_ITERATOR_HPP_INCLUDED

#include <boost/numeric/odeint/util/stepper_traits.hpp>
#include <boost/numeric/odeint/stepper/stepper_categories.hpp>
#include <boost/numeric/odeint/iterator/detail/ode_iterator_base.hpp>
#include <boost/numeric/odeint/iterator/impl/times_iterator_impl.hpp>

namespace boost {
namespace numeric {
namespace odeint {

    /* use the times_iterator_impl with the right tags */
    template< class Stepper , class System , class State , class TimeIterator
#ifndef DOXYGEN_SKIP
        , class StepperTag = typename base_tag< typename traits::stepper_category< Stepper >::type >::type
#endif
    >
    class times_iterator : public times_iterator_impl<
            times_iterator< Stepper , System , State , TimeIterator , StepperTag > ,
            Stepper , System , State , TimeIterator , detail::ode_state_iterator_tag , StepperTag
        >
    {
        typedef typename traits::time_type< Stepper >::type time_type;
        typedef times_iterator< Stepper , System , State , TimeIterator , StepperTag > iterator_type;

    public:
        times_iterator( Stepper stepper , System sys , State &s ,
                        TimeIterator t_start , TimeIterator t_end , time_type dt )
            : times_iterator_impl< iterator_type , Stepper , System , State , TimeIterator, detail::ode_state_iterator_tag , StepperTag >( stepper , sys , s , t_start , t_end , dt )
        {}

        times_iterator( Stepper stepper , System sys , State &s )
            : times_iterator_impl< iterator_type , Stepper , System , State , TimeIterator , detail::ode_state_iterator_tag , StepperTag >( stepper , sys , s )
        {}
    };

    /* make functions */

    template< class Stepper , class System , class State , class TimeIterator >
    times_iterator< Stepper , System, State , TimeIterator > make_times_iterator_begin(
        Stepper stepper ,
        System system ,
        State &x ,
        TimeIterator t_start ,
        TimeIterator t_end ,
        typename traits::time_type< Stepper >::type dt )
    {
        return times_iterator< Stepper , System , State , TimeIterator >( stepper , system , x , t_start , t_end , dt );
    }

    // ToDo: requires to specifically provide the TimeIterator template parameter, can this be improved?
    template< class TimeIterator , class Stepper , class System , class State >
    times_iterator< Stepper , System , State , TimeIterator > make_times_iterator_end(
        Stepper stepper ,
        System system ,
        State &x )
        //TimeIterator t_end )
    {
        return times_iterator< Stepper , System , State , TimeIterator >( stepper , system , x );
    }

    template< class Stepper , class System , class State , class TimeIterator >
    std::pair< times_iterator< Stepper , System , State , TimeIterator > ,
               times_iterator< Stepper , System , State , TimeIterator > >
    make_times_range(
        Stepper stepper ,
        System system ,
        State &x ,
        TimeIterator t_start ,
        TimeIterator t_end ,
        typename traits::time_type< Stepper >::type dt )
    {
        return std::make_pair(
            times_iterator< Stepper , System , State , TimeIterator >( stepper , system , x , t_start , t_end , dt ) ,
            times_iterator< Stepper , System , State , TimeIterator >( stepper , system , x )
            );
    }

    /**
     * \class times_iterator
     *
     * \brief ODE Iterator with given evaluation points. The value type of this iterator is the state type of the stepper.
     *
     * Implements an iterator representing the solution of an ODE from *t_start
     * to *t_end evaluated at time points given by the sequence t_start to t_end.
     * t_start and t_end are iterators representing a sequence of time points
     * where the solution of the ODE should be evaluated.
     * After each iteration the iterator dereferences to the state x at the next
     * time *t_start++ until t_end is reached.
     * This iterator can be used with Steppers, ControlledSteppers and
     * DenseOutputSteppers and it always makes use of the all the given steppers
     * capabilities. A for_each over such an iterator range behaves similar to
     * the integrate_times routine.
     *
     * times_iterator is a model of single-pass iterator.
     *
     * The value type of this iterator is the state type of the stepper. Hence one can only access the state and not the current time.
     *
     * \tparam Stepper The stepper type which should be used during the iteration.
     * \tparam System The type of the system function (ODE) which should be solved.
     * \tparam State The state type of the ODE.
     * \tparam TimeIterator The iterator type for the sequence of time points.
     */

    /**
     * \fn make_times_iterator_begin( Stepper stepper ,
        System system ,
        State &x ,
        TimeIterator t_start ,
        TimeIterator t_end ,
        typename traits::time_type< Stepper >::type dt )
     *
     * \brief Factory function for times_iterator. Constructs a begin iterator.
     *
     * \param stepper The stepper to use during the iteration.
     * \param system The system function (ODE) to solve.
     * \param x The initial state. const_step_iterator stores a reference of s and changes its value during the iteration.
     * \param t_start Begin iterator of the sequence of evaluation time points.
     * \param t_end End iterator of the sequence of evaluation time points.
     * \param dt The initial time step.
     * \returns The times iterator.
     */

    /**
     * \fn make_times_iterator_end( Stepper stepper , System system , State &x )
     * \brief Factory function for times_iterator. Constructs an end iterator.
     *
     * \tparam TimesIterator The iterator type of the time sequence, must be specifically provided.
     *
     * \param stepper The stepper to use during the iteration.
     * \param system The system function (ODE) to solve.
     * \param x The initial state. const_step_iterator stores a reference of s and changes its value during the iteration.
     * \returns The times iterator.
     *
     * This function needs the TimeIterator type specifically defined as a
     * template parameter.
     */

    /**
     * \fn make_times_range( Stepper stepper , System system , State &x ,
        TimeIterator t_start ,
        TimeIterator t_end ,
        typename traits::time_type< Stepper >::type dt )
     *
     * \brief Factory function to construct a single pass range of times iterators. A range is here a pair
     * of times_iterator.
     *
     * \param stepper The stepper to use during the iteration.
     * \param system The system function (ODE) to solve.
     * \param x The initial state. const_step_iterator store a reference of s and changes its value during the iteration.
     * \param t_start Begin iterator of the sequence of evaluation time points.
     * \param t_end End iterator of the sequence of evaluation time points.
     * \param dt The initial time step.
     * \returns The times iterator range.
     */

} // namespace odeint
} // namespace numeric
} // namespace boost

#endif // BOOST_NUMERIC_ODEINT_ITERATOR_TIMES_ITERATOR_HPP_INCLUDED


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <boost/simd/include/functions/dist.hpp>
#include <boost/simd/include/functions/splat.hpp>

#include <boost/simd/include/constants/three.hpp>
#include <boost/simd/include/constants/two.hpp>
#include <boost/simd/include/constants/one.hpp>
#include <boost/simd/include/constants/mone.hpp>
#include <boost/simd/include/constants/valmin.hpp>
#include <boost/simd/include/constants/valmax.hpp>
#include <boost/simd/include/constants/ten.hpp>
#include <boost/simd/include/constants/mten.hpp>
#include <boost/simd/include/constants/inf.hpp>
#include <boost/simd/include/constants/minf.hpp>
#include <boost/simd/include/constants/nan.hpp>

#include <boost/dispatch/functor/meta/call.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>

NT2_TEST_CASE_TPL ( dist_real,  BOOST_SIMD_SIMD_REAL_TYPES)
{
  using boost::simd::dist;
  using boost::simd::tag::dist_;
  using boost::simd::native;

  NT2_TEST_TYPE_IS( typename boost::dispatch::meta::call<dist_(T,T)>::type
                  , T
                  );

#ifndef BOOST_SIMD_NO_INVALIDS
  NT2_TEST_EQUAL(dist(boost::simd::Inf<T>() , boost::simd::Inf<T>()) , boost::simd::Nan<T>());
  NT2_TEST_EQUAL(dist(boost::simd::Minf<T>(), boost::simd::Minf<T>()), boost::simd::Nan<T>());
  NT2_TEST_EQUAL(dist(boost::simd::Nan<T>() , boost::simd::Nan<T>()) , boost::simd::Nan<T>());
#endif

  NT2_TEST_EQUAL(dist(boost::simd::Zero<T>(), boost::simd::Zero<T>()), boost::simd::Zero<T>());
  NT2_TEST_EQUAL(dist(boost::simd::Mone<T>(), boost::simd::One<T>()), boost::simd::Two<T>());
  NT2_TEST_EQUAL(dist(boost::simd::One<T>(), boost::simd::Three<T>()), boost::simd::Two<T>());
}

NT2_TEST_CASE_TPL ( dist_integer_ui,  BOOST_SIMD_SIMD_UNSIGNED_TYPES)
{
  using boost::simd::dist;
  using boost::simd::tag::dist_;

  NT2_TEST_TYPE_IS( typename boost::dispatch::meta::call<dist_(T,T)>::type
                  , T
                  );

  NT2_TEST_EQUAL(dist(boost::simd::Zero<T>(), boost::simd::Zero<T>()), boost::simd::Zero<T>());
  NT2_TEST_EQUAL(dist(boost::simd::One<T>(), boost::simd::Three<T>()), boost::simd::Two<T>());
  NT2_TEST_EQUAL(dist(boost::simd::Valmax<T>(), boost::simd::Zero<T>()), boost::simd::Valmax<T>());
  NT2_TEST_EQUAL(dist(boost::simd::Zero<T>(), boost::simd::Valmax<T>()), boost::simd::Valmax<T>());
}

NT2_TEST_CASE_TPL ( dist_integer_si,  BOOST_SIMD_SIMD_INTEGRAL_SIGNED_TYPES)
{
  using boost::simd::dist;
  using boost::simd::tag::dist_;

  NT2_TEST_TYPE_IS( typename boost::dispatch::meta::call<dist_(T,T)>::type
                  , T
                  );

  NT2_TEST_EQUAL(dist(boost::simd::Zero<T>(), boost::simd::Zero<T>()), boost::simd::Zero<T>());
  NT2_TEST_EQUAL(dist(boost::simd::Mone<T>(), boost::simd::One<T>()), boost::simd::Two<T>());
  NT2_TEST_EQUAL(dist(boost::simd::One<T>(), boost::simd::Three<T>()), boost::simd::Two<T>());
  NT2_TEST_EQUAL(dist(boost::simd::Valmax<T>(), boost::simd::Zero<T>()), boost::simd::Valmax<T>());
  NT2_TEST_EQUAL(dist(boost::simd::Zero<T>(), boost::simd::Valmax<T>()), boost::simd::Valmax<T>());

  NT2_TEST_EQUAL(dist(boost::simd::Ten<T>(), boost::simd::Mten<T>()), boost::simd::splat<T>(20));
  NT2_TEST_EQUAL(dist(boost::simd::Mten<T>(), boost::simd::Ten<T>()), boost::simd::splat<T>(20));
}


#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE BruteForceSolverTests
#include <boost/test/unit_test.hpp>

#include <vector>
#include <set>

#include "graph/PBQPGraph.hpp"
#include "graph/Vector.hpp"
#include "graph/PBQPNode.hpp"
#include "graph/PBQPEdge.hpp"
#include "analysis/PBQPHandler.hpp"
#include "solve/PBQPSolver.hpp"
#include "solve/BruteForceSolver.hpp"
#include "graph/PBQPSolution.hpp"

#include "util/TestUtils.hpp"

namespace pbqppapa {

BOOST_AUTO_TEST_CASE(emptyGraphTest) {
	//make sure this doesnt explode
	PBQPGraph<int> graph;
	BruteForceSolver<int> solver(&graph);
	PBQPSolution<int>* sol = solver.calcSolution();
	delete sol;
}

BOOST_AUTO_TEST_CASE(simpleNodeNoEdgesTest) {
	PBQPGraph<int> graph;
	int arr [] { 3, 1, 2};
	Vector<int> vek (3, arr);
	PBQPNode<int>* node1 = graph.addNode(vek);
	int arr2 [] { 3, 5, 2, 1, 4};
	Vector<int> vek2 (5, arr2);
	PBQPNode<int>* node2 = graph.addNode(vek2);
	int arr3 [] { 10, 9, 0, 2, 4};
	Vector<int> vek3 (5, arr3);
	PBQPNode<int>* node3 = graph.addNode(vek3);
	BruteForceSolver<int> solver(&graph);
	PBQPSolution<int>* sol = solver.calcSolution();
	BOOST_CHECK_EQUAL(sol->getSolution(node1->getIndex()), 1);
	BOOST_CHECK_EQUAL(sol->getSolution(node2->getIndex()), 3);
	BOOST_CHECK_EQUAL(sol->getSolution(node3->getIndex()), 2);
	delete sol;
}

BOOST_AUTO_TEST_CASE(simpleEdge) {
	PBQPGraph<int> graph;
	int arr [] { 3, 1, 1};
	Vector<int> vek (3, arr);
	PBQPNode<int>* node1 = graph.addNode(vek);
	int arr2 [] { 3, 5, 3};
	Vector<int> vek2 (3, arr2);
	PBQPNode<int>* node2 = graph.addNode(vek2);
	int matArr [] {20, 31, 20, 20, 22, 10, 24, 25, 26};
	Matrix<int> mat (3, 3, matArr);
	graph.addEdge(node1, node2, mat);
	BruteForceSolver<int> solver(&graph);
	PBQPSolution<int>* sol = solver.calcSolution();
	BOOST_CHECK_EQUAL(sol->getSolution(node1->getIndex()), 1);
	BOOST_CHECK_EQUAL(sol->getSolution(node2->getIndex()), 2);
	delete sol;
}

}


//==================================================================================================
/*!

  Copyright 2015 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/constant/mhalf.hpp>
#include <boost/simd/as.hpp>
#include <simd_test.hpp>

STF_CASE_TPL( "Check mhalf behavior for integral types"
            , (std::uint8_t)(std::uint16_t)(std::uint32_t)(std::uint64_t)
              (std::int8_t)(std::int16_t)(std::int32_t)(std::int64_t)
            )
{
  using boost::simd::as;
  using boost::simd::detail::mhalf;
  using boost::simd::Mhalf;

  STF_TYPE_IS(decltype(Mhalf<T>()), T);
  STF_EQUAL(Mhalf<T>(), T(0));
  STF_EQUAL(mhalf( as(T{}) ),T(0));
}

STF_CASE_TPL( "Check mhalf behavior for floating types"
            , (double)(float)
            )
{
  using boost::simd::as;
  using boost::simd::detail::mhalf;
  using boost::simd::Mhalf;

  STF_TYPE_IS(decltype(Mhalf<T>()), T);
  STF_IEEE_EQUAL(Mhalf<T>(), T(-1/2.0));
  STF_IEEE_EQUAL(mhalf( as(T{}) ), T(-1/2.0));
}


#include <elle/from-string.hh>

#include <stdexcept>
#include <type_traits>
#include <unordered_map>

#include <boost/algorithm/string/case_conv.hpp>

#include <elle/err.hh>
#include <elle/find.hh>
#include <elle/meta.hh>
#include <elle/printf.hh> // for elle::err

namespace elle
{
  // GCC 4.9 does not catch negative numbers as an error for unsigned
  // types.
  namespace
  {
    template <typename Type>
    auto
    check_unsigned(std::string const& s)
    {
      meta::static_if<std::is_unsigned<Type>{}>
        ([&s]
        {
          for (auto c: s)
            if (c == '-')
              err<std::out_of_range>("invalid unsigned number: %s", s);
            else if (!std::isspace(c))
              break;
        })();
    }
  }

  // Types that have their std::sto<TYPE> function.
#define ELLE_DEFINE(Type, Conversion)                           \
  Type                                                          \
  from_string(std::string const& s, Type*)                      \
  {                                                             \
    check_unsigned<Type>(s);                                    \
    auto size = size_t{};                                       \
    auto res = Conversion(s, &size);                            \
    if (size < s.size())                                        \
      err<std::invalid_argument>("invalid number: %s", s);      \
    return res;                                                 \
  }

  ELLE_DEFINE(double, std::stod);
  ELLE_DEFINE(float, std::stof);
  ELLE_DEFINE(int, std::stoi);
  ELLE_DEFINE(long double, std::stold);
  ELLE_DEFINE(long long, std::stoll);
  ELLE_DEFINE(long, std::stol);
  ELLE_DEFINE(unsigned long long, std::stoull);
  ELLE_DEFINE(unsigned long, std::stoul);
  ELLE_DEFINE(unsigned, std::stou);

#undef ELLE_DEFINE

  // Types that don't have a standard conversion routine.
#define ELLE_DEFINE(Type)                               \
  Type                                                  \
  from_string(std::string const& s, Type*)              \
  {                                                     \
    auto v = from_string<int>(s);                       \
    if (std::numeric_limits<Type>::min() <= v           \
        && v <= std::numeric_limits<Type>::max())       \
      return v;                                         \
    else                                                \
      err<std::out_of_range>("invalid number: %s", s);  \
  }

  ELLE_DEFINE(signed char);
  ELLE_DEFINE(signed short);
  ELLE_DEFINE(unsigned char);
  ELLE_DEFINE(unsigned short);

#undef ELLE_DEFINE

  bool
  from_string(std::string const& s, bool*)
  {
    static auto const map = std::unordered_map<std::string, bool>
      {
        {"0",     false},
        {"1",     true},
        {"false", false},
        {"n",     false},
        {"no",    false},
        {"true",  true},
        {"y",     true},
        {"yes",   true},
      };
    if (auto it = elle::find(map, boost::to_lower_copy(s)))
      return it->second;
    else
      elle::err<std::invalid_argument>("invalid boolean value: %s", s);
  }
}


#include "localization-summary-map/localization-summary-map-queries.h"

#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <Eigen/Core>
#include <glog/logging.h>
#include <maplab-common/accessors.h>
#include <maplab-common/geometry.h>
#include <vi-map/landmark.h>

#include "localization-summary-map/localization-summary-map.h"

namespace summary_map {

SummaryMapCachedLookups::SummaryMapCachedLookups(
    const LocalizationSummaryMap& map)
    : map_(map) {
  buildLandmarkIdToObserverIndicesMap();
}

const std::unordered_set<size_t>&
SummaryMapCachedLookups::getAllObserverIndicesForLandmark(
    const vi_map::LandmarkId& landmark_id) const {
  return common::getChecked(landmarkid_observerindices_, landmark_id);
}

void SummaryMapCachedLookups::buildLandmarkIdToObserverIndicesMap() {
  const Eigen::Matrix<unsigned int, Eigen::Dynamic, 1>&
      observation_to_landmark_index = map_.observationToLandmarkIndex();
  const Eigen::Matrix<unsigned int, Eigen::Dynamic, 1>&
      observation_to_observer_index = map_.observerIndices();
  vi_map::LandmarkIdList landmark_ids;
  map_.getAllLandmarkIds(&landmark_ids);

  for (int oidx = 0u; oidx < observation_to_landmark_index.rows(); ++oidx) {
    CHECK_LT(oidx, observation_to_landmark_index.rows());
    const size_t landmark_idx = observation_to_landmark_index(oidx, 0);
    CHECK_LT(oidx, observation_to_observer_index.rows());
    const size_t observer_idx = observation_to_observer_index(oidx, 0);
    CHECK_LT(landmark_idx, landmark_ids.size());
    const vi_map::LandmarkId landmark_id = landmark_ids[landmark_idx];
    landmarkid_observerindices_[landmark_id].emplace(observer_idx);
  }
}

double getMaxDisparityRadAngleOfLandmarkObservationBundle(
    const LocalizationSummaryMap& map,
    const SummaryMapCachedLookups& map_lookup_cache,
    const vi_map::LandmarkId& landmark_id) {
  CHECK(landmark_id.isValid());

  // Build landmark observation bundle.
  const Eigen::Matrix3Xf& G_observers = map.GObserverPosition();

  const std::unordered_set<size_t>& observer_indices =
      map_lookup_cache.getAllObserverIndicesForLandmark(landmark_id);
  CHECK(!observer_indices.empty());

  Aligned<std::vector, Eigen::Vector3d> G_normalized_incidence_rays;
  G_normalized_incidence_rays.resize(observer_indices.size());

  size_t idx = 0u;
  for (size_t observer_idx : observer_indices) {
    const Eigen::Vector3d G_landmark = map.getGLandmarkPosition(landmark_id);
    CHECK_LT(static_cast<int>(observer_idx), G_observers.cols());
    const Eigen::Vector3d G_observer =
        G_observers.col(observer_idx).cast<double>();

    const Eigen::Vector3d G_incidence_ray = G_observer - G_landmark;
    G_normalized_incidence_rays[idx] = G_incidence_ray.normalized();
    ++idx;
  }

  // Build bundle of incidence rays to the landmark.
  return common::getMaxDisparityRadAngleOfUnitVectorBundle(
      G_normalized_incidence_rays);
}

// Consider building the required lookup externally for efficiency.
double getMaxDisparityRadAngleOfLandmarkObservationBundle(
    const LocalizationSummaryMap& map, const vi_map::LandmarkId& id) {
  SummaryMapCachedLookups map_lookup_cache(map);
  return getMaxDisparityRadAngleOfLandmarkObservationBundle(
      map, map_lookup_cache, id);
}

}  // namespace summary_map


///////////////////////////////////////////////////////////////////////////////
// distribution::model::models::likelihood::iid::detail::lpdf.hpp            //
//                                                                           //
//  Copyright 2009 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_DISTRIBUTION_MODEL_MODELS_LIKELIHOOD_IID_DETAIL_LPDF_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_DISTRIBUTION_MODEL_MODELS_LIKELIHOOD_IID_DETAIL_LPDF_HPP_ER_2009
#include <numeric>
#include <boost/range.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/function.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include <boost/fusion/include/at_key.hpp>
#include <boost/statistics/detail/traits/remove_cvref.hpp>
#include <boost/statistics/detail/distribution_common/meta/value.hpp>
#include <boost/statistics/detail/distribution_common/distributions/reference/wrapper.hpp>
#include <boost/statistics/detail/distribution_common/distributions/reference/log_unnormalized_pdf.hpp>
#include <boost/statistics/detail/distribution_common/functor/log_unnormalized_pdf.hpp>
#include <boost/statistics/detail/distribution/model/models/likelihood/iid/iid.hpp>
#include <boost/statistics/detail/distribution/model/models/likelihood/iid/detail/lpdf.hpp>

namespace boost{
namespace statistics{
namespace detail{
namespace distribution{
namespace model{
namespace detail{

    template<typename M,typename D>
    struct lpdf_iid_likelihood{
        typedef model::iid_likelihood<M,D> l_;
        typedef typename distribution::meta::value< l_ >::type value_type;

        template<typename P>
        static value_type call(const l_&l,const P& p)
        {
            typedef typename l_::model_type m_;
            typedef typename l_::dataset_type d_;
            typedef
                typename statistics::detail::traits::add_cref<m_>::type cref_m_;
            typedef
                typename statistics::detail::traits::add_cref<d_>::type cref_d_;

            typedef distribution::reference_wrapper<cref_m_> ref_;
            typedef distribution::functor::log_unnormalized_pdf_<ref_> f_;

            cref_m_ m = l.model();

            m.set_parameter(p);
            ref_ ref(m);
            f_ f(ref);

            typedef boost::function<
                value_type(const value_type&,const value_type&)
            > outer_;
            outer_ outer = boost::lambda::_1 + boost::lambda::_2;

            return std::accumulate(
                boost::begin(l.dataset()),
                boost::end(l.dataset()),
                static_cast<value_type>(0),
                boost::lambda::bind(
                    outer,
                    boost::lambda::_1,
                    boost::lambda::bind<value_type>(f,boost::lambda::_2)
                )
            );
        }
    };

}// detail
}// model
}// distribution
}// detail
}// statistics
}// boost

#endif


/**
 * @file upwindfinitevolume_main.cc
 * @brief NPDE homework UpwindFiniteVolume code
 * @author Philipp Egg
 * @date 08.09.2020
 * @copyright Developed at ETH Zurich
 */

#include <lf/assemble/assemble.h>
#include <lf/geometry/geometry.h>
#include <lf/io/gmsh_reader.h>
#include <lf/mesh/hybrid2d/hybrid2d.h>
#include <lf/mesh/mesh.h>
#include <lf/refinement/refinement.h>

#include <Eigen/Core>
#include <Eigen/SparseCore>
#include <Eigen/SparseLU>
#include <cmath>
#include <iostream>
#include <memory>
#include <vector>

#include "upwindfinitevolume.h"

int main() {
  /* SAM_LISTING_BEGIN_1 */
  //====================
  // Your code goes here
  //====================
  return 0;
}