#include <cmath>

#include "Header.hh"

//#include "AdvPhysicalParameterDNA.hh"
#include "UtsusemiUnitConverter.hh"
#include "UtsusemiHeader.hh"
#include "AdvMultiScatteringDNA.hh"
//#include "string.h"


AdvMultiScatteringDNA :: AdvMultiScatteringDNA(void){
  if (!(isReady())) {
    UtsusemiError("AdvMultiScatteringDNA >> Error Initializing (Maybe not found atomic data file).");
    return;
  }
  // read sample data

        //std::string filepath = getenv("HOME");
  std::string filepath = UtsusemiEnvGetUserDir();
  std::string filename = "sample_data.dat";
  //filepath += "/ana/setup/";
        filepath += "/ana/DNA/setup/";
  filepath += filename;

  std::cout << "sample_data_path = " << filepath <<std::endl;

  //filename="/home/dnaadmin/00Install/work.DNA/correction/sample_data.dat";
  wallInfo.initialize();
  sampleInfo.initialize();

  SetInfoData(filepath, &wallInfo, &sampleInfo);
  // in the case of solid, not gas
  // "wall" means outer cylinder and "sample" means inner cylinder
  sampleInfo.totalDensity=sampleInfo.weightPerVolume*MLF_KG2G/sampleInfo.effectiveAtomicWeight*MLF_NA;
  //  wallInfo.totalDensity=wallInfo.weightPerVolume*MLF_KG2G/wallInfo.effectiveAtomicWeight*MLF_NA;

  // Gauss-Legender parameter
  m=6;
  xk.clear();
  Hk.clear();
  xk.push_back(-0.9324695142);
  xk.push_back(-0.6612093865);
  xk.push_back(-0.2386191861);
  xk.push_back(0.2386191861);
  xk.push_back(0.6612093865);
  xk.push_back(0.9324695142);
  Hk.push_back(0.1713244924);
  Hk.push_back(0.3607615730);
  Hk.push_back(0.4679139346);
  Hk.push_back(0.4679139346);
  Hk.push_back(0.3607615730);
  Hk.push_back(0.1713244924);

  // integral threshold for determination of convergence
  threZero=1.e-16;
  epsilon=threZero*1.e-04;
  epsilonRatio=1.0e-4;


  // maximum number of cylinder
  // maxCylinderNum=3;

}

AdvMultiScatteringDNA :: AdvMultiScatteringDNA(std::string filepath){
  if (!(isReady())) {
    UtsusemiError("AdvMultiScatteringDNA >> Error Initializing (Maybe not found atomic data file).");
    return;
  }
  //filename="/home/dnaadmin/00Install/work.DNA/correction/sample_data.dat";
  wallInfo.initialize();
  sampleInfo.initialize();

  std::cout << "sample_data_path = " << filepath <<std::endl;

  SetInfoData(filepath, &wallInfo, &sampleInfo);
  // in the case of solid, not gas
  // "wall" means outer cylinder and "sample" means inner cylinder
  sampleInfo.totalDensity=sampleInfo.weightPerVolume*MLF_KG2G/sampleInfo.effectiveAtomicWeight*MLF_NA;
  //  wallInfo.totalDensity=wallInfo.weightPerVolume*MLF_KG2G/wallInfo.effectiveAtomicWeight*MLF_NA;

  // Gauss-Legender parameter
  m=6;
  xk.clear();
  Hk.clear();
  xk.push_back(-0.9324695142);
  xk.push_back(-0.6612093865);
  xk.push_back(-0.2386191861);
  xk.push_back(0.2386191861);
  xk.push_back(0.6612093865);
  xk.push_back(0.9324695142);
  Hk.push_back(0.1713244924);
  Hk.push_back(0.3607615730);
  Hk.push_back(0.4679139346);
  Hk.push_back(0.4679139346);
  Hk.push_back(0.3607615730);
  Hk.push_back(0.1713244924);

  // integral threshold for determination of convergence
  threZero=1.e-16;
  epsilon=threZero*1.e-04;
  epsilonRatio=1.0e-4;


  // maximum number of cylinder
  // maxCylinderNum=3;

}


AdvMultiScatteringDNA :: ~AdvMultiScatteringDNA(void){
}

//void ScatAbsoTestSample :: SetIntegThresholdValue(Double a){
//  epsilon=a;
//}


Double AdvMultiScatteringDNA :: GetEffectMultiScatSlab(Double inputEnergy, Double inputTheta, Double inputWidth){
  incEnergy=inputEnergy*MLF_MEV2J;
  outEnergy=incEnergy;

  if(incEnergy<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input Einc should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }

  theta=inputTheta*MLF_DEGREE2RADIAN;
  width=inputWidth*MLF_MM2M;

  if(width<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input width should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }

  //Double initialTheta=0.0;

  sigmaAbs=ReturnSigmaFromVelocityRule(&sampleInfo, incEnergy, 'a');
  sigmaSca=ReturnSigmaFromVelocityRule(&sampleInfo, outEnergy, 's');

  sigmaAbs*=sampleInfo.totalDensity/4./PI;
  sigmaSca*=sampleInfo.totalDensity/4./PI;
  sigmaTot=sigmaAbs+sigmaSca;

//  std::printf("sigmaAbs=%e, sigmaSca=%e, sigmaTot=%e\n", sigmaAbs, sigmaSca, sigmaTot); fflush(stdout);

  Double eta=0.5e0*sigmaTot*width;
  Double alpha0=eta;
  Double alpha=eta/cos(theta);

  if(alpha==alpha0) {
    alpha=alpha*(1.0+epsilonRatio);
  }

  Double absAlpha=alpha;
  Double absAlpha0=alpha0;

  if(absAlpha<0.0) absAlpha=-absAlpha;
  if(absAlpha0<0.0) absAlpha0=-absAlpha0;

  Double expmAbsAlpha=exp(-absAlpha);
  Double expmAbsAlpha0=exp(-absAlpha0);
  Double expAlpha=exp(alpha);
  Double expmAlpha=1./expAlpha;
  Double expAlpha0=exp(alpha0);
  Double expmAlpha0=1./expAlpha0;

  s1tilde=expmAbsAlpha*expmAbsAlpha0*funcU(alpha-alpha0);

  s2tilde=expmAbsAlpha*expmAbsAlpha0*sigmaSca*width/(8.0*alpha*alpha0*(alpha-alpha0))
         *(-(expAlpha*expAlpha0+expmAlpha*expmAlpha0-expAlpha*expmAlpha0-expmAlpha*expAlpha0)*(alpha-alpha0)*log(2.0*eta)
           +(expAlpha*expAlpha0+expmAlpha*expmAlpha0)*(alpha-alpha0)*funcE(2.0*eta)
           +expAlpha*expmAlpha0*(alpha0*funcE(2.0*(eta+alpha))-alpha*funcE(2.0*(eta-alpha0)))
           +expmAlpha*expAlpha0*(alpha0*funcE(2.0*(eta-alpha))-alpha*funcE(2.0*(eta+alpha0))) );

  delta=s2tilde/s1tilde;
//  std::printf("eta=%e, alpha=%e, alpha0=%e\n", eta, alpha, alpha0); fflush(stdout);
//  std::printf("sitide=%e, s2tilde=%e, delta=%e\n", s1tilde, s2tilde, delta); fflush(stdout);

  shape="slab";
  Double LargeDelta=GetDelta(shape, delta);
  return LargeDelta;
}



Double AdvMultiScatteringDNA :: GetDeltaForSlab(Double inputEnergy, Double inputTheta, Double inputWidth){
  incEnergy=inputEnergy*MLF_MEV2J;
  outEnergy=incEnergy;
  theta=inputTheta*MLF_DEGREE2RADIAN;
  width=inputWidth*MLF_MM2M;
  //Double initialTheta=0.0;

  if(incEnergy<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input Einc should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(width<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input width should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }

  sigmaAbs=ReturnSigmaFromVelocityRule(&sampleInfo, incEnergy, 'a');
  sigmaSca=ReturnSigmaFromVelocityRule(&sampleInfo, outEnergy, 's');

  sigmaAbs*=sampleInfo.totalDensity/4./PI;
  sigmaSca*=sampleInfo.totalDensity/4./PI;
  sigmaTot=sigmaAbs+sigmaSca;

//  std::printf("sigmaAbs=%e, sigmaSca=%e, sigmaTot=%e\n", sigmaAbs, sigmaSca, sigmaTot);

  Double eta=0.5e0*sigmaTot*width;
  Double alpha0=eta;
  Double alpha=eta/cos(theta);

  if(alpha==alpha0) {
    alpha=alpha*(1.0+epsilonRatio);
  }

  Double absAlpha=alpha;
  Double absAlpha0=alpha0;

  if(absAlpha<0.0) absAlpha=-absAlpha;
  if(absAlpha0<0.0) absAlpha0=-absAlpha0;

  Double expmAbsAlpha=exp(-absAlpha);
  Double expmAbsAlpha0=exp(-absAlpha0);
  Double expAlpha=exp(alpha);
  Double expmAlpha=1./expAlpha;
  Double expAlpha0=exp(alpha0);
  Double expmAlpha0=1./expAlpha0;

  s1tilde=expmAbsAlpha*expmAbsAlpha0*funcU(alpha-alpha0);

  s2tilde=expmAbsAlpha*expmAbsAlpha0*sigmaSca*width/(8.0*alpha*alpha0*(alpha-alpha0))
         *(-(expAlpha*expAlpha0+expmAlpha*expmAlpha0-expAlpha*expmAlpha0-expmAlpha*expAlpha0)*(alpha-alpha0)*log(2.0*eta)
           +(expAlpha*expAlpha0+expmAlpha*expmAlpha0)*(alpha-alpha0)*funcE(2.0*eta)
           +expAlpha*expmAlpha0*(alpha0*funcE(2.0*(eta+alpha))-alpha*funcE(2.0*(eta-alpha0)))
           +expmAlpha*expAlpha0*(alpha0*funcE(2.0*(eta-alpha))-alpha*funcE(2.0*(eta+alpha0))) );

  delta=s2tilde/s1tilde;
//  std::printf("eta=%e, alpha=%e, alpha0=%e\n", eta, alpha, alpha0);
//  std::printf("sitide=%e, s2tilde=%e, delta=%e\n", s1tilde, s2tilde, delta);
//  deltas[0]=delta;
//
//  shape="slab";
//  Double LargeDelta=GetDelta(shape, delta);
//
//  deltas[1]=LargeDelta;
//  deltas[2]=1.0/(1.0+LargeDelta);
//  deltas[3]=delta/(1.0+LargeDelta);
//  deltas[4]=(LargeDelta-delta)/(1.0+LargeDelta);

  return delta;
}

Double AdvMultiScatteringDNA :: GetLargeDeltaForSlab(Double delta){
  shape="slab";
  Double LargeDelta=GetDelta(shape, delta);
  return LargeDelta;
}

Double AdvMultiScatteringDNA :: GetFracSingle(Double Delta, Double delta){
  if(delta>Delta){
    double tmp=delta;
    delta=Delta;
    Delta=tmp;
  }
  return 1.0/(1.0+Delta);
}

Double AdvMultiScatteringDNA :: GetFracDouble(Double Delta, Double delta){
  if(delta>Delta){
    double tmp=delta;
    delta=Delta;
    Delta=tmp;
  }
  return delta/(1.0+Delta);
}

Double AdvMultiScatteringDNA :: GetFracOverTriple(Double Delta, Double delta){
  if(delta>Delta){
    double tmp=delta;
    delta=Delta;
    Delta=tmp;
  }
  return (Delta-delta)/(1.0+Delta);
}




Double AdvMultiScatteringDNA :: funcU(Double x){
  Double xin=x;
  if(xin==0.0) {
    return 1.0;
  }
//  std::printf("exp(xin)=%e, exp(-xin)=%e, add=%e, xin=%e, dev=%e\n",
//          exp(xin), exp(-xin), exp(xin)-exp(-xin), xin, (exp(xin)-exp(-xin))/xin);
//  return 0.5e0*(exp(xin)-exp(-xin))/xin;

//  std::printf(" nearly equal to 1 ? :: %e\n", 0.5e0*(exp(xin)-exp(-xin))/xin);

//  return 1.0;
  return 0.5e0*(exp(xin)-exp(-xin))/xin;
}

Double AdvMultiScatteringDNA :: funcE(Double x){
  Double gamma=0.5772156649;
  Double xin=x;
//  std::printf("funcE :: x=%e, integ(x)=%e, -gamma+integ(x)=%e\n",xin, funcEin(xin), -gamma+funcEin(xin) );
  Double value=-gamma+funcEin(xin);
  return value;
}

Double AdvMultiScatteringDNA :: GetEffectMultiScatSlab(Double inputEnergy, Double inputWidth){
// in the case of thin slab. In this case Delta is independent from k0 and k.
  incEnergy=inputEnergy*MLF_MEV2J;
  outEnergy=incEnergy;
  width=inputWidth*MLF_MM2M;

  if(incEnergy<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input Einc should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(width<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input width should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }

  sigmaAbs=ReturnSigmaFromVelocityRule(&sampleInfo, incEnergy, 'a');
  sigmaSca=ReturnSigmaFromVelocityRule(&sampleInfo, outEnergy, 's');
  sigmaAbs*=sampleInfo.totalDensity/4./PI;
  sigmaSca*=sampleInfo.totalDensity/4./PI;
  sigmaTot=sigmaAbs+sigmaSca;

  Double gamma=0.5772156649;
  Double c=1.5e0-gamma;
  Double sigmaD=sigmaTot*width;

  delta=0.5e0*sigmaSca*width*(-log(sigmaD)+c+1.0/3.0*sigmaD);

  shape="slab";
  Double LargeDelta=GetDelta(shape, delta);
  return LargeDelta;
}

Double AdvMultiScatteringDNA :: GetDeltaForSlab(Double inputEnergy, Double inputWidth){
// in the case of thin slab. In this case Delta is independent from k0 and k.
  incEnergy=inputEnergy*MLF_MEV2J;
  outEnergy=incEnergy;
  width=inputWidth*MLF_MM2M;

  if(incEnergy<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input Einc should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(width<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input width should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }

  sigmaAbs=ReturnSigmaFromVelocityRule(&sampleInfo, incEnergy, 'a');
  sigmaSca=ReturnSigmaFromVelocityRule(&sampleInfo, outEnergy, 's');
  sigmaAbs*=sampleInfo.totalDensity/4./PI;
  sigmaSca*=sampleInfo.totalDensity/4./PI;
  sigmaTot=sigmaAbs+sigmaSca;

  Double gamma=0.5772156649;
  Double c=1.5e0-gamma;
  Double sigmaD=sigmaTot*width;

  delta=0.5e0*sigmaSca*width*(-log(sigmaD)+c+1.0/3.0*sigmaD);

//  shape="slab";
//  Double LargeDelta=GetDelta(shape, delta);
  return delta;
}


Double AdvMultiScatteringDNA :: GetEffectMultiScatColumn(Double inputEnergy, Double inputDiameter, Double inputHeight){
  incEnergy=inputEnergy*MLF_MEV2J;
  outEnergy=incEnergy;
  Double r=inputDiameter*MLF_MM2M*0.5;
  Double d=inputHeight*MLF_MM2M;

  if(incEnergy<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input Einc should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(r<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input diameter should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(d<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input height should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }

  sigmaAbs=ReturnSigmaFromVelocityRule(&sampleInfo, incEnergy, 'a');
  sigmaSca=ReturnSigmaFromVelocityRule(&sampleInfo, outEnergy, 's');
  sigmaAbs*=sampleInfo.totalDensity/4./PI;
  sigmaSca*=sampleInfo.totalDensity/4./PI;
  sigmaTot=sigmaAbs+sigmaSca;

  Double pPara=0.75;
  Double pPerp=2./3.;
  Double qPara=16.0/(9.0*PI*PI-64);
  Double qPerp=3.*PI/4.-16./3/PI;

  Double beta=atan(2.0*r/d);

  delta=sigmaSca/sigmaTot
        *(pPara*(1.0-cos(beta)-funcI(beta, qPara*sigmaTot*d))
          +pPerp*(cos(beta)-funcJ(beta, qPerp*sigmaTot*r)) );

//  std::printf("delta=%e\n",delta);

  shape="column";
  Double LargeDelta=GetDelta(shape, delta);
  return LargeDelta;
}

Double AdvMultiScatteringDNA :: GetDeltaForColumn(Double inputEnergy, Double inputDiameter, Double inputHeight){
  incEnergy=inputEnergy*MLF_MEV2J;
  outEnergy=incEnergy;
  Double r=inputDiameter*MLF_MM2M*0.5;
  Double d=inputHeight*MLF_MM2M;

  if(incEnergy<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input Einc should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(r<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input diameter should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(d<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input height should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }

  sigmaAbs=ReturnSigmaFromVelocityRule(&sampleInfo, incEnergy, 'a');
  sigmaSca=ReturnSigmaFromVelocityRule(&sampleInfo, outEnergy, 's');
  sigmaAbs*=sampleInfo.totalDensity/4./PI;
  sigmaSca*=sampleInfo.totalDensity/4./PI;
  sigmaTot=sigmaAbs+sigmaSca;

  Double pPara=0.75;
  Double pPerp=2./3.;
  Double qPara=16.0/(9.0*PI*PI-64);
  Double qPerp=3.*PI/4.-16./3/PI;

  Double beta=atan(2.0*r/d);

  delta=sigmaSca/sigmaTot
        *(pPara*(1.0-cos(beta)-funcI(beta, qPara*sigmaTot*d))
          +pPerp*(cos(beta)-funcJ(beta, qPerp*sigmaTot*r)) );

//  std::printf("delta=%e\n",delta);

//  shape="column";
//  Double LargeDelta=GetDelta(shape, delta);
  return delta;
}

Double AdvMultiScatteringDNA :: GetLargeDeltaForColumn(Double delta){
  shape="column";
  Double LargeDelta=GetDelta(shape, delta);
  return LargeDelta;
}

Double AdvMultiScatteringDNA :: GetEffectMultiScatCylinder(Double inputEnergy, Double inputLargerDiameter, Double inputThickness, Double inputHeight){
  incEnergy=inputEnergy*MLF_MEV2J;
  outEnergy=incEnergy;
  Double radiusLarge=inputLargerDiameter*MLF_MM2M*0.5;
  Double radiusSmall=radiusLarge-inputThickness*MLF_MM2M;
  Double d=inputHeight*MLF_MM2M;

  if(incEnergy<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input Einc should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(radiusLarge<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input diameter should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(inputThickness<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input thickness should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(d<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input height should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }

  sigmaAbs=ReturnSigmaFromVelocityRule(&sampleInfo, incEnergy, 'a');
  sigmaSca=ReturnSigmaFromVelocityRule(&sampleInfo, outEnergy, 's');
  sigmaAbs*=sampleInfo.totalDensity/4./PI;
  sigmaSca*=sampleInfo.totalDensity/4./PI;
  sigmaTot=sigmaAbs+sigmaSca;

  Double pPara=0.75;
  Double pPerp=2./3.;
  Double qPara=16.0/(9.0*PI*PI-64);
  Double qPerp=3.*PI/4.-16./3/PI;

  Double beta=atan(2.0*radiusLarge/d);
  Double beta1=atan((radiusLarge-radiusSmall)/d);
  Double beta2=atan((radiusLarge+radiusSmall)/d);

  delta=sigmaSca/sigmaTot
        *(pPara*( 1.0-cos(beta)
                 -funcI(beta1, qPara*sigmaTot*d))
                 -funcI1(beta1, beta2, qPara*sigmaTot*(radiusLarge-radiusSmall))
                 -funcI2(beta2, beta, qPara*sigmaTot*d, d, radiusSmall)
          +pPerp*(cos(beta)-funcJ(beta, qPerp*sigmaTot*radiusLarge)) );

  shape="column";
  Double LargeDelta=GetDelta(shape, delta);
  return LargeDelta;
}


Double AdvMultiScatteringDNA :: GetDeltaForCylinder(Double inputEnergy, Double inputLargerDiameter, Double inputThickness, Double inputHeight){
  incEnergy=inputEnergy*MLF_MEV2J;
  outEnergy=incEnergy;
  Double radiusLarge=inputLargerDiameter*MLF_MM2M*0.5;
  Double radiusSmall=radiusLarge-inputThickness*MLF_MM2M;
  Double d=inputHeight*MLF_MM2M;

  if(incEnergy<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input Einc should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(radiusLarge<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input diameter should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(inputThickness<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input thickness should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }
  if(d<0.0) {
    std::printf("*** Error ***\n");
    std::printf("    input height should be larger than zero.\n");
    std::printf("    force to exit\n");
    exit(1);
  }

  sigmaAbs=ReturnSigmaFromVelocityRule(&sampleInfo, incEnergy, 'a');
  sigmaSca=ReturnSigmaFromVelocityRule(&sampleInfo, outEnergy, 's');
  sigmaAbs*=sampleInfo.totalDensity/4./PI;
  sigmaSca*=sampleInfo.totalDensity/4./PI;
  sigmaTot=sigmaAbs+sigmaSca;

  Double pPara=0.75;
  Double pPerp=2./3.;
  Double qPara=16.0/(9.0*PI*PI-64);
  Double qPerp=3.*PI/4.-16./3/PI;

  Double beta=atan(2.0*radiusLarge/d);
  Double beta1=atan((radiusLarge-radiusSmall)/d);
  Double beta2=atan((radiusLarge+radiusSmall)/d);

  delta=sigmaSca/sigmaTot
        *(pPara*( 1.0-cos(beta)
                 -funcI(beta1, qPara*sigmaTot*d))
                 -funcI1(beta1, beta2, qPara*sigmaTot*(radiusLarge-radiusSmall))
                 -funcI2(beta2, beta, qPara*sigmaTot*d, d, radiusSmall)
          +pPerp*(cos(beta)-funcJ(beta, qPerp*sigmaTot*radiusLarge)) );

//  shape="column";
//  Double LargeDelta=GetDelta(shape, delta);
  return delta;
}

Double AdvMultiScatteringDNA :: GetLargeDeltaForCylinder(Double delta){
  shape="column";
  Double LargeDelta=GetDelta(shape, delta);
  return LargeDelta;
}




Double AdvMultiScatteringDNA :: funcI(Double xend, Double yinput){
  // calc 3D integral by use of cylindrical coordinate, Int(dz)Int(dr)Int(dtheta)
  // max iteration for devision of integrated region for Int(dz)
  UInt4 max=10;
  UInt4 nDiv;
  Double dx;
//  Double z=sampleInfo.height;
  Double x1=0.0;
  Double x2=xend;
  Double xi;
  Double x[m],y[m/2], f[m];

  Double s1, s;
  s1=s=0.0;
  Double diff;
  nDiv=1;

  clock_t start, end;
  start=clock();

  for (UInt4 i=0; i<max; i++) {
    nDiv*=2;
    dx=(x2-x1)/nDiv;
    for (UInt4 j=0; j<m/2; j++) {
      y[j]=0.0;
    }
    for (UInt4 j=0; j<nDiv; j++){
      xi=x1+((j+1)-0.5)*dx;
      for (UInt4 k=0; k<m; k++) {
        x[k]=xi+xk[k]*dx/2;
      }
      for (UInt4 k=0; k<m; k++) {
        f[k]=exp(-yinput/cos(x[k]))*sin(x[k]);
      }
      for (UInt4 k=0; k<m/2; k++) {
        y[k]+=f[k]+f[m-k-1];
      }
    }
    for (UInt4 j=0; j<m/2; j++) {
      y[j]*=Hk[j];
    }
    Double sum=0.0;
    for (UInt4 j=0; j<m/2; j++) {
      sum+=y[j];
    }
    s=0.5*dx*sum;
    diff=s-s1;
    if (diff<0.0) {
      diff=-diff;
    }
//    std::printf("main :: %d s=%e, diff=%e, diff_ratio=%f\n", i, s, diff, diff/s); fflush(stdout);
    if (i>0) {
      if (diff < epsilon ){
        break;
      }
      if (s>threZero && diff/s<epsilonRatio){
        break;
      }
    }
    s1=s;
  }

  end=clock();
//  std::printf("%e second need for %d steps\n", (double) (end-start)/CLOCKS_PER_SEC, m*nDiv);
  return s;
}




Double AdvMultiScatteringDNA :: funcI1(Double xstart, Double xend, Double yinput){
  // calc 3D integral by use of cylindrical coordinate, Int(dz)Int(dr)Int(dtheta)
  // max iteration for devision of integrated region for Int(dz)
  UInt4 max=10;
  UInt4 nDiv;
  Double dx;
//  Double z=sampleInfo.height;
  Double x1=xstart;
  Double x2=xend;
  Double xi;
  Double x[m],y[m/2], f[m];

  Double s1, s;
  s1=s=0.0;
  Double diff;
  nDiv=1;

  clock_t start, end;
  start=clock();

  for (UInt4 i=0; i<max; i++) {
    nDiv*=2;
    dx=(x2-x1)/nDiv;
    for (UInt4 j=0; j<m/2; j++) {
      y[j]=0.0;
    }
    for (UInt4 j=0; j<nDiv; j++){
      xi=x1+((j+1)-0.5)*dx;
      for (UInt4 k=0; k<m; k++) {
        x[k]=xi+xk[k]*dx/2;
      }
      for (UInt4 k=0; k<m; k++) {
        f[k]=exp(-yinput/sin(x[k]))*sin(x[k]);
      }
      for (UInt4 k=0; k<m/2; k++) {
        y[k]+=f[k]+f[m-k-1];
      }
    }
    for (UInt4 j=0; j<m/2; j++) {
      y[j]*=Hk[j];
    }
    Double sum=0.0;
    for (UInt4 j=0; j<m/2; j++) {
      sum+=y[j];
    }
    s=0.5*dx*sum;
    diff=s-s1;
    if (diff<0.0) {
      diff=-diff;
    }
//    std::printf("main :: %d s=%e, diff=%e, diff_ratio=%f\n", i, s, diff, diff/s); fflush(stdout);
    if (i>0) {
      if (diff < epsilon ){
        break;
      }
      if (s>threZero && diff/s<epsilonRatio){
        break;
      }
    }
    s1=s;
  }

  end=clock();
//  std::printf("%e second need for %d steps\n", (double) (end-start)/CLOCKS_PER_SEC, m*nDiv);
  return s;
}



Double AdvMultiScatteringDNA :: funcI2(Double xstart, Double xend, Double yinput, Double a, Double b){
  // calc 3D integral by use of cylindrical coordinate, Int(dz)Int(dr)Int(dtheta)
  // max iteration for devision of integrated region for Int(dz)
  UInt4 max=10;
  UInt4 nDiv;
  Double dx;
//  Double z=sampleInfo.height;
  Double x1=xstart;
  Double x2=xend;
  Double xi;
  Double x[m],y[m/2], f[m];

  Double s1, s;
  s1=s=0.0;
  Double diff;
  nDiv=1;

  clock_t start, end;
  start=clock();

  for (UInt4 i=0; i<max; i++) {
    nDiv*=2;
    dx=(x2-x1)/nDiv;
    for (UInt4 j=0; j<m/2; j++) {
      y[j]=0.0;
    }
    for (UInt4 j=0; j<nDiv; j++){
      xi=x1+((j+1)-0.5)*dx;
      for (UInt4 k=0; k<m; k++) {
        x[k]=xi+xk[k]*dx/2;
      }
      for (UInt4 k=0; k<m; k++) {
        f[k]=exp( -yinput/cos(x[k])* (1.0-2.0*b/(a*tan(x[k])) ) )*sin(x[k]);
      }
      for (UInt4 k=0; k<m/2; k++) {
        y[k]+=f[k]+f[m-k-1];
      }
    }
    for (UInt4 j=0; j<m/2; j++) {
      y[j]*=Hk[j];
    }
    Double sum=0.0;
    for (UInt4 j=0; j<m/2; j++) {
      sum+=y[j];
    }
    s=0.5*dx*sum;
    diff=s-s1;
    if (diff<0.0) {
      diff=-diff;
    }
//    std::printf("main :: %d s=%e, diff=%e, diff_ratio=%f\n", i, s, diff, diff/s); fflush(stdout);
    if (i>0) {
      if (diff < epsilon ){
        break;
      }
      if (s>threZero && diff/s<epsilonRatio){
        break;
      }
    }
    s1=s;
  }

  end=clock();
//  std::printf("%e second need for %d steps\n", (double) (end-start)/CLOCKS_PER_SEC, m*nDiv);
  return s;
}


Double AdvMultiScatteringDNA :: funcJ(Double xstart, Double yinput){
  // calc 3D integral by use of cylindrical coordinate, Int(dz)Int(dr)Int(dtheta)
  // max iteration for devision of integrated region for Int(dz)
  UInt4 max=10;
  UInt4 nDiv;
  Double dx;
//  Double z=sampleInfo.height;
  Double x1=xstart;
  Double x2=PI/2;
  Double xi;
  Double x[m],y[m/2], f[m];

  Double s1, s;
  s1=s=0.0;
  Double diff;
  nDiv=1;

  clock_t start, end;
  start=clock();

  for (UInt4 i=0; i<max; i++) {
    nDiv*=2;
    dx=(x2-x1)/nDiv;
    for (UInt4 j=0; j<m/2; j++) {
      y[j]=0.0;
    }
    for (UInt4 j=0; j<nDiv; j++){
      xi=x1+((j+1)-0.5)*dx;
      for (UInt4 k=0; k<m; k++) {
        x[k]=xi+xk[k]*dx/2;
      }
      for (UInt4 k=0; k<m; k++) {
        f[k]=exp(-yinput/sin(x[k]))*sin(x[k]);
      }
      for (UInt4 k=0; k<m/2; k++) {
        y[k]+=f[k]+f[m-k-1];
      }
    }
    for (UInt4 j=0; j<m/2; j++) {
      y[j]*=Hk[j];
    }
    Double sum=0.0;
    for (UInt4 j=0; j<m/2; j++) {
      sum+=y[j];
    }
    s=0.5*dx*sum;
    diff=s-s1;
    if (diff<0.0) {
      diff=-diff;
    }
//    std::printf("main :: %d s=%e, diff=%e, diff_ratio=%f\n", i, s, diff, diff/s); fflush(stdout);
    if (i>0) {
      if (diff < epsilon ){
        break;
      }
      if (s>threZero && diff/s<epsilonRatio){
        break;
      }
    }
    s1=s;
  }

  end=clock();
//  std::printf("%e second need for %d steps\n", (double) (end-start)/CLOCKS_PER_SEC, m*nDiv);
  return s;
}

Double AdvMultiScatteringDNA :: GetDelta(std::string shape, Double delta){
  UInt4 j=1;
  UInt4 i;
  Double cj;
  Double jmax=100;
  Double presum=0.0;
  Double sum=0.0;
//  Double delta=deltain;
  Double delta_jminus1=1.0;

  if(shape=="slab"){
    j++;
    cj=1.0;
    delta_jminus1*=delta;
    sum+=cj*delta_jminus1;
    presum=sum;
    for(;;){
      j++;
      cj=1.0;
      delta_jminus1*=delta;
      sum+=cj*delta_jminus1;
      if(-epsilonRatio<(cj*delta_jminus1/presum) && (cj*delta_jminus1/presum)<epsilonRatio){
        break;
      }
      else {
        presum=sum;
      }
      if(j>jmax){
        std::printf("j>jmax, so iteration end :: sum=%e, delta_jminus1=%e\n", sum, delta_jminus1);
        break;
      }
    }
  }
  else if(shape=="column" || shape=="cyliner"){
    j++;
    Double lamda1=PI/4;
    Double lamda2=2.0/3;
    Double lamdaj=lamda2;
    Double lamdajm1;
    cj=lamdaj/lamda1/j*(2.0*lamda1/lamda2);
    delta_jminus1=delta;
    sum+=cj*delta_jminus1;
    presum=sum;
    for(;;){
      j++;
      lamdajm1=lamdaj;
      if(j%2){
        lamdaj=1.0;
        for(i=0; i<(j+1)/2; i++){
          lamdaj*=(Double ) (2*i+1)/(2*i+2);
        }
        lamdaj*=PI/2;
      }
      else {
        lamdaj=1.0;
        for(i=0; i<j/2; i++){
          lamdaj*=(Double ) (2*i+2)/(2*i+3);
        }
      }

      cj=cj*lamdaj/(lamdajm1*j)*(2.0*lamda1/lamda2);
      delta_jminus1*=delta;
      sum+=cj*delta_jminus1;

      if(-epsilonRatio<(cj*delta_jminus1/presum) && (cj*delta_jminus1/presum)<epsilonRatio){
        break;
      }
      else {
        presum=sum;
      }
      if(j>jmax){
        std::printf("j>jmax, so iteration end :: sum=%e, delta_jminus1=%e\n", sum, delta_jminus1);
        break;
      }
    }




  }
  else {
    std::printf("something wrong\n");
    exit(1);
  }

  return sum;
}

Double AdvMultiScatteringDNA :: funcEin(Double xend){
  // calc 3D integral by use of cylindrical coordinate, Int(dz)Int(dr)Int(dtheta)
  // max iteration for devision of integrated region for Int(dz)
  UInt4 max=10;
  UInt4 nDiv;
  Double dx;
//  Double z=sampleInfo.height;
  Double x1=0;
  Double x2=xend;
  Double xi;
  Double x[m],y[m/2], f[m];

  Double s1, s;
  s1=s=0.0;
  Double diff;
  nDiv=8;

//  std::printf("funcEin :: x1=%e, x2=%e\n", x1, x2);

  Double coff=x2-x1;
  if(coff==0.0) {
    return 0.0;
  }
  x1/=coff;
  x2/=coff;
//  std::printf("coff=%e, x1=%e, x2=%e\n", coff, x1, x2);

  clock_t start, end;
  start=clock();

  for (UInt4 i=0; i<max; i++) {
    nDiv*=2;
    dx=(x2-x1)/nDiv;
//    dx=1.0/nDiv;
    for (UInt4 j=0; j<m/2; j++) {
      y[j]=0.0;
    }
    for (UInt4 j=0; j<nDiv; j++){
      xi=x1+((j+1)-0.5)*dx;
      for (UInt4 k=0; k<m; k++) {
        x[k]=xi+xk[k]*dx/2;
      }
      for (UInt4 k=0; k<m; k++) {
        f[k]=(1.0e0-exp(-x[k]))/x[k];
      }
      for (UInt4 k=0; k<m/2; k++) {
        y[k]+=f[k]+f[m-k-1];
      }
    }
    for (UInt4 j=0; j<m/2; j++) {
      y[j]*=Hk[j];
    }
    Double sum=0.0;
    for (UInt4 j=0; j<m/2; j++) {
      sum+=y[j];
    }
    s=0.5*dx*sum;
    diff=s-s1;
    if (diff<0.0) {
      diff=-diff;
    }
//    std::printf("main :: %d s=%e, diff=%e, diff_ratio=%f\n", i, s, diff, diff/s); fflush(stdout);
    if (i>0) {
      if (diff < epsilon ){
        break;
      }
      if (s>threZero && diff/s<epsilonRatio){
        break;
      }
    }
    s1=s;
  }

  end=clock();
//  std::printf("%e second need for %d steps\n", (double) (end-start)/CLOCKS_PER_SEC, m*nDiv);

  s*=coff;
//  std::printf("s=%e, %e second need for %d steps\n", s, (double) (end-start)/CLOCKS_PER_SEC, m*nDiv);

  return s;
}
