// @(#)root/hist:$Id$
// Authors: L. Moneta, A. Flandi   08/2014
//
/**********************************************************************
 *                                                                    *
 * Copyright (c) 2015  ROOT  Team, CERN/PH-SFT                        *
 *                                                                    *
 *                                                                    *
 **********************************************************************/
//
//  TF1Convolution.cxx
//  
//
//  Created by Aurélie Flandi on 27.08.14.
//
//
//

#include "TF1Convolution.h"
#include "Riostream.h"
#include "TROOT.h"
#include "TObject.h"
#include "TObjString.h"
#include "TMath.h"
#include "Math/Integrator.h"
#include "Math/IntegratorMultiDim.h"
#include "Math/IntegratorOptions.h"
#include "Math/GaussIntegrator.h"
#include "Math/GaussLegendreIntegrator.h"
#include "Math/AdaptiveIntegratorMultiDim.h"
#include "Math/Functor.h"
#include "TVirtualFFT.h"
#include "TClass.h"

//________________________________________________________________________
//***********************************************************************
// (f*g)(t) = int(f(x)g(x-t)dx)   *
//*********************************
// class wrapping convolution of two function : evaluation of TF1(t) * TF1(x-t)
//
// The convolution is performed by default using FFTW if it is available .
// One can pass optionally the range of the convolution (by default the first function range is used).
// Note that when using Discrete Fouriere Transform (as FFTW), it is a circular transform, so the functions should be
// approximatly zero at the end of the range. If they are significantly different than zero on one side (e.g. the left side)
// a spill over will occur on the other side (e.g right side).
// If no function range is given by default the function1 range + 10% is used 
// One shoud use also a not too small number of points for the DFT (a minimum of 1000).  By default 10000 points are used . 
//
class TF1Convolution_EvalWrapper
{
   std::shared_ptr < TF1 > fFunction1;
   std::shared_ptr < TF1 > fFunction2;
   Double_t fT0;
   
public:

   TF1Convolution_EvalWrapper(std::shared_ptr<TF1> & f1 , std::shared_ptr<TF1> & f2, Double_t t)
      : fFunction1(f1), fFunction2(f2), fT0(t)
   {
   }
   Double_t operator()(Double_t x) const
   {
      return fFunction1->Eval(x) * fFunction2->Eval(x-fT0);
   }
};

//________________________________________________________________________
void TF1Convolution::InitializeDataMembers(TF1* function1, TF1* function2, Bool_t useFFT)
{
   // use copy instead of Clone
   if (function1) { 
      TF1 * fnew1 = (TF1*) function1->IsA()->New();
      function1->Copy(*fnew1); 
      fFunction1  = std::shared_ptr<TF1>(fnew1);
   }
   if (function2) { 
      TF1 * fnew2 = (TF1*) function2->IsA()->New();
      function2->Copy(*fnew2); 
      fFunction2  = std::shared_ptr<TF1>(fnew2);
   }
   if (fFunction1.get() == nullptr|| fFunction2.get() == nullptr)
      Fatal("InitializeDataMembers","Invalid functions - Abort");

   // add by default an extra 10% on  each side
   fFunction1->GetRange(fXmin, fXmax);
   Double_t range = fXmax - fXmin; 
   fXmin       -= 0.1*range;
   fXmax       += 0.1*range; 
   fNofParams1 = fFunction1->GetNpar();
   fNofParams2 = fFunction2->GetNpar();
   fParams1    = std::vector<Double_t>(fNofParams1);
   fParams2    = std::vector<Double_t>(fNofParams2);
   fCstIndex   = fFunction2-> GetParNumber("Constant");
   fFlagFFT    = useFFT;
   fFlagGraph  = false;
   fNofPoints  = 10000;
   
   //std::cout<<"before: NofParams2 = "<<fNofParams2<<std::endl;

   fParNames.reserve( fNofParams1 + fNofParams2);
   for (int i=0; i<fNofParams1; i++)
   {
      fParams1[i] = fFunction1 -> GetParameter(i);
      fParNames.push_back(fFunction1 -> GetParName(i) );
   }
   for (int i=0; i<fNofParams2; i++)
   {
      fParams2[i] = fFunction2 -> GetParameter(i);
      if (i != fCstIndex) fParNames.push_back(fFunction2 -> GetParName(i) );
   }
   if (fCstIndex!=-1)
   {
      fFunction2  -> FixParameter(fCstIndex,1.);
      fNofParams2 =  fNofParams2-1;
      fParams2.erase(fParams2.begin()+fCstIndex);
   }
}
//________________________________________________________________________
TF1Convolution::TF1Convolution(TF1* function1, TF1* function2, Bool_t useFFT)
{
   // constructor from the two function pointer and a flag is using FFT
   InitializeDataMembers(function1,function2, useFFT);   
}

//________________________________________________________________________
TF1Convolution::TF1Convolution(TF1* function1, TF1* function2, Double_t xmin, Double_t xmax, Bool_t useFFT)
{
   // constructor from the two function pointer and the convolution range
   InitializeDataMembers(function1, function2,useFFT);
   if (xmin < xmax) { 
      fXmin      = xmin;
      fXmax      = xmax;
   }
}

//________________________________________________________________________
TF1Convolution::TF1Convolution(TString formula,  Double_t xmin, Double_t xmax, Bool_t useFFT)
{
   // constructor from a formula expression as f1 * f2 where f1 and f2 are two functions known to ROOT
   TF1::InitStandardFunctions();
   
   TObjArray *objarray   = formula.Tokenize("*");
   std::vector < TString > stringarray(2);
   std::vector < TF1*    > funcarray(2);
   for (int i=0; i<2; i++)
   {
      stringarray[i] = ((TObjString*)((*objarray)[i])) -> GetString();
      stringarray[i].ReplaceAll(" ","");
      funcarray[i]   = (TF1*)(gROOT -> GetListOfFunctions() -> FindObject(stringarray[i]));
      // case function is not found try to use as a TFormula
      if (funcarray[i] == nullptr) {
         TF1 * f = new TF1(TString::Format("f_conv_%d",i+1),stringarray[i]);
         if (!f->GetFormula()->IsValid() )
            Error("TF1Convolution","Invalid formula : %s",stringarray[i].Data() );
         if (i == 0)
            fFunction1 = std::shared_ptr<TF1>(f);
         else
            fFunction2 = std::shared_ptr<TF1>(f);
      }
   }
   InitializeDataMembers(funcarray[0], funcarray[1],useFFT);
   if (xmin < xmax) {
      fXmin      = xmin;
      fXmax      = xmax;
   }
}

//________________________________________________________________________
TF1Convolution::TF1Convolution(TString formula1, TString formula2,  Double_t xmin, Double_t xmax, Bool_t useFFT)
{
   // constructor from 2 function names where f1 and f2 are two functions known to ROOT
   // if the function names are not knwon to ROOT then a corresponding 
   TF1::InitStandardFunctions();
   (TString)formula1.ReplaceAll(" ","");
   (TString)formula2.ReplaceAll(" ","");
   TF1* f1 = (TF1*)(gROOT -> GetListOfFunctions() -> FindObject(formula1));
   TF1* f2 = (TF1*)(gROOT -> GetListOfFunctions() -> FindObject(formula2));
   // if function do not exists try using TFormula
   if (!f1) {
      fFunction1 = std::shared_ptr<TF1>(new TF1("f_conv_1",formula1) );
      if (!fFunction1->GetFormula()->IsValid() )
         Error("TF1Convolution","Invalid formula for : %s",formula1.Data() );
   }
   if (!f2) {
      fFunction2 = std::shared_ptr<TF1>(new TF1("f_conv_1",formula2) );
      if (!fFunction2->GetFormula()->IsValid() )
         Error("TF1Convolution","Invalid formula for : %s",formula2.Data() );
   }
   // if f1 or f2 are null ptr are not used in InitializeDataMembers
   InitializeDataMembers(f1, f2,useFFT);
   if (xmin < xmax) {
      fXmin      = xmin;
      fXmax      = xmax;
   }
}

//________________________________________________________________________
void TF1Convolution::MakeFFTConv()
{
   //perform the FFT of the two functions

   if (gDebug)
      Info("MakeFFTConv","Making FFT convolution using %d points in range [%g,%g]",fNofPoints,fXmin,fXmax);
   
   std::vector < Double_t > x  (fNofPoints);
   std::vector < Double_t > in1(fNofPoints);
   std::vector < Double_t > in2(fNofPoints);
   
   TVirtualFFT *fft1 = TVirtualFFT::FFT(1, &fNofPoints, "R2C K");
   TVirtualFFT *fft2 = TVirtualFFT::FFT(1, &fNofPoints, "R2C K");
   if (fft1 == nullptr || fft2 == nullptr) {
      Warning("MakeFFTConv","Cannot use FFT, probably FFTW# package is not available. Switch to numerical convolution");
      fFlagFFT = false; 
   }

   // apply a shift in order to have the second function centered around middle of the range of the convolution
   Double_t shift2 = 0.5*(fXmin+fXmax);
   Double_t x2; 
   for (int i=0; i<fNofPoints; i++)
   {
      x[i]   = fXmin + (fXmax-fXmin)/(fNofPoints-1)*i;
      x2     = x[i] - shift2; 
      in1[i] = fFunction1 -> EvalPar( &x[i], nullptr);
      in2[i] = fFunction2 -> EvalPar( &x2, nullptr);
      fft1  -> SetPoint(i, in1[i]);
      fft2  -> SetPoint(i, in2[i]);
   }
   fft1 -> Transform();
   fft2 -> Transform();
   
   //inverse transformation of the product
   
   TVirtualFFT *fftinverse = TVirtualFFT::FFT(1, &fNofPoints, "C2R K");
   Double_t re1, re2, im1, im2, out_re, out_im;
   
   for (int i=0;i<=fNofPoints/2.;i++)
   {
      fft1 -> GetPointComplex(i,re1,im1);
      fft2 -> GetPointComplex(i,re2,im2);
      out_re = re1*re2 - im1*im2;
      out_im = re1*im2 + re2*im1;
      fftinverse -> SetPoint(i, out_re, out_im);
   }
   fftinverse -> Transform();

   // fill a graph with the result of the convolution
   if (!fGraphConv) fGraphConv  = std::shared_ptr< TGraph >(new TGraph(fNofPoints));

   for (int i=0;i<fNofPoints;i++)
   {
      // we need this since we have applied a shift in the middle of f2
      int j = i + fNofPoints/2;
      if (j >= fNofPoints) j -= fNofPoints; 
      fGraphConv->SetPoint(i, x[i], fftinverse->GetPointReal(j)/fNofPoints);//because it is not normalized
   }
   fFlagGraph = true; // we can use the graph
}

//________________________________________________________________________
Double_t TF1Convolution::EvalFFTConv(Double_t t)
{
   if (!fFlagGraph)  MakeFFTConv();
   // if cannot make FFT use numconv
   if (fGraphConv)
      return  fGraphConv -> Eval(t);
   else
      return EvalNumConv(t); 
}

//________________________________________________________________________
Double_t TF1Convolution::EvalNumConv(Double_t t)
{
   // perform numerical convolution
   // could in principle cache the integral  in a Grap[h as it is done for the FFTW
   TF1Convolution_EvalWrapper fconv( fFunction1, fFunction2, t);
   Double_t result = 0;
   
   if (ROOT::Math::IntegratorOneDimOptions::DefaultIntegratorType() == ROOT::Math::IntegrationOneDim::kGAUSS )
   {
      ROOT::Math::GaussIntegrator integrator(1e-9, 1e-9);
      integrator.SetFunction(ROOT::Math::Functor1D(fconv));
      if      (fXmin != - TMath::Infinity() && fXmax != TMath::Infinity())
         result =  integrator.Integral(fXmin, fXmax);
      else if (fXmin == - TMath::Infinity() && fXmax != TMath::Infinity())
         result = integrator.IntegralLow(fXmax);
      else if (fXmin != - TMath::Infinity() && fXmax == TMath::Infinity())
         result = integrator.IntegralUp(fXmin);
      else if (fXmin == - TMath::Infinity() && fXmax == TMath::Infinity())
         result = integrator.Integral();
   }
   else
   {
      ROOT::Math::IntegratorOneDim integrator(fconv, ROOT::Math::IntegratorOneDimOptions::DefaultIntegratorType(), 1e-9, 1e-9);
      if      (fXmin != - TMath::Infinity() && fXmax != TMath::Infinity() )
         result =  integrator.Integral(fXmin, fXmax);
      else if (fXmin == - TMath::Infinity() && fXmax != TMath::Infinity() )
         result = integrator.IntegralLow(fXmax);
      else if (fXmin != - TMath::Infinity() && fXmax == TMath::Infinity() )
         result = integrator.IntegralUp(fXmin);
      else if (fXmin == - TMath::Infinity() && fXmax == TMath::Infinity() )
         result = integrator.Integral();
   }
   return result;
}

//________________________________________________________________________
Double_t TF1Convolution::operator()(Double_t* t, Double_t* p)//used in TF1 when doing the fit, will be valuated at each point
{
   if (p!=0)   TF1Convolution::SetParameters(p);                           // first refresh the parameters
  
   Double_t result = 0.;
   if (fFlagFFT)  result = EvalFFTConv(t[0]);
   else           result = EvalNumConv(t[0]);
   return result;
}
//________________________________________________________________________
void TF1Convolution::SetNofPointsFFT(Int_t n)
{
   if (n<0) return;
   fNofPoints = n;
   if (fGraphConv) fGraphConv -> Set(fNofPoints); //set nof points of the Tgraph
   fFlagGraph = false; // to indicate we need to re-do the graph
}

//________________________________________________________________________
void TF1Convolution::SetParameters(Double_t* p)
{
   bool equalParams = true;
   for (int i=0; i<fNofParams1; i++) {
      fFunction1 -> SetParameter(i,p[i]);
      equalParams &= ( fParams1[i] == p[i] );   
      fParams1[i] = p[i];      
   }
   Int_t k       = 0;
   Int_t offset  = 0;
   Int_t offset2 = 0;
   if (fCstIndex!=-1)   offset = 1;
   Int_t totalnofparams = fNofParams1+fNofParams2+offset;
   for (int i=fNofParams1; i<totalnofparams; i++)   {
      if (k==fCstIndex)
      {
         k++;
         offset2=1;
         continue;
      }
      fFunction2 -> SetParameter(k,p[i-offset2]);
      equalParams &= ( fParams2[k-offset2] == p[i-offset2] );   
      fParams2[k-offset2] = p[i-offset2];
      k++;
   }
   // std::cout << "parameters for function1   :  ";
   // for (int i = 0; i < fFunction1->GetNpar(); ++i)
   //    std::cout << fFunction1->GetParameter(i) << "  ";
   // std::cout << "\nparameters for function2   :  ";
   // for (int i = 0; i < fFunction2->GetNpar(); ++i)
   //    std::cout << fFunction2->GetParameter(i) << "  ";
   // std::cout << std::endl;

//do the graph for FFT convolution
   if (!equalParams) fFlagGraph = false; // to indicate we need to re-do the convolution
   // if (fFlagFFT)
   // {
   //    MakeFFTConv();
   // }
}

//________________________________________________________________________
void TF1Convolution::SetParameters(Double_t p0, Double_t p1, Double_t p2, Double_t p3,
                                   Double_t p4, Double_t p5, Double_t p6, Double_t p7)
{
   Double_t params[]={p0,p1,p2,p3,p4,p5,p6,p7};
   TF1Convolution::SetParameters(params);
}

//________________________________________________________________________
void TF1Convolution::SetExtraRange(Double_t percentage)
{
   if (percentage<0) return;
   double range = fXmax = fXmin; 
   fXmin -= percentage * range; 
   fXmax += percentage * range;
   fFlagGraph = false;  // to indicate we need to re-do the convolution
}

//________________________________________________________________________
void TF1Convolution::SetRange(Double_t a, Double_t b)
{
   if (a>=b)   return;
   fXmin = a;
   fXmax = b; 
   if (fFlagFFT && ( a==-TMath::Infinity() || b==TMath::Infinity() ) )
   {
      Warning("TF1Convolution::SetRange()","In FFT mode, range can not be infinite. Infinity has been replaced by range of first function plus a bufferzone to avoid spillover.");
      if (a ==-TMath::Infinity()) fXmin = fFunction1 -> GetXmin();
      if ( b== TMath::Infinity()) fXmax = fFunction1 -> GetXmax();
      // add a spill over of 10% in this case
      SetExtraRange(0.1); 
   }
   fFlagGraph = false;  // to indicate we need to re-do the convolution
}
 TF1Convolution.cxx:1
 TF1Convolution.cxx:2
 TF1Convolution.cxx:3
 TF1Convolution.cxx:4
 TF1Convolution.cxx:5
 TF1Convolution.cxx:6
 TF1Convolution.cxx:7
 TF1Convolution.cxx:8
 TF1Convolution.cxx:9
 TF1Convolution.cxx:10
 TF1Convolution.cxx:11
 TF1Convolution.cxx:12
 TF1Convolution.cxx:13
 TF1Convolution.cxx:14
 TF1Convolution.cxx:15
 TF1Convolution.cxx:16
 TF1Convolution.cxx:17
 TF1Convolution.cxx:18
 TF1Convolution.cxx:19
 TF1Convolution.cxx:20
 TF1Convolution.cxx:21
 TF1Convolution.cxx:22
 TF1Convolution.cxx:23
 TF1Convolution.cxx:24
 TF1Convolution.cxx:25
 TF1Convolution.cxx:26
 TF1Convolution.cxx:27
 TF1Convolution.cxx:28
 TF1Convolution.cxx:29
 TF1Convolution.cxx:30
 TF1Convolution.cxx:31
 TF1Convolution.cxx:32
 TF1Convolution.cxx:33
 TF1Convolution.cxx:34
 TF1Convolution.cxx:35
 TF1Convolution.cxx:36
 TF1Convolution.cxx:37
 TF1Convolution.cxx:38
 TF1Convolution.cxx:39
 TF1Convolution.cxx:40
 TF1Convolution.cxx:41
 TF1Convolution.cxx:42
 TF1Convolution.cxx:43
 TF1Convolution.cxx:44
 TF1Convolution.cxx:45
 TF1Convolution.cxx:46
 TF1Convolution.cxx:47
 TF1Convolution.cxx:48
 TF1Convolution.cxx:49
 TF1Convolution.cxx:50
 TF1Convolution.cxx:51
 TF1Convolution.cxx:52
 TF1Convolution.cxx:53
 TF1Convolution.cxx:54
 TF1Convolution.cxx:55
 TF1Convolution.cxx:56
 TF1Convolution.cxx:57
 TF1Convolution.cxx:58
 TF1Convolution.cxx:59
 TF1Convolution.cxx:60
 TF1Convolution.cxx:61
 TF1Convolution.cxx:62
 TF1Convolution.cxx:63
 TF1Convolution.cxx:64
 TF1Convolution.cxx:65
 TF1Convolution.cxx:66
 TF1Convolution.cxx:67
 TF1Convolution.cxx:68
 TF1Convolution.cxx:69
 TF1Convolution.cxx:70
 TF1Convolution.cxx:71
 TF1Convolution.cxx:72
 TF1Convolution.cxx:73
 TF1Convolution.cxx:74
 TF1Convolution.cxx:75
 TF1Convolution.cxx:76
 TF1Convolution.cxx:77
 TF1Convolution.cxx:78
 TF1Convolution.cxx:79
 TF1Convolution.cxx:80
 TF1Convolution.cxx:81
 TF1Convolution.cxx:82
 TF1Convolution.cxx:83
 TF1Convolution.cxx:84
 TF1Convolution.cxx:85
 TF1Convolution.cxx:86
 TF1Convolution.cxx:87
 TF1Convolution.cxx:88
 TF1Convolution.cxx:89
 TF1Convolution.cxx:90
 TF1Convolution.cxx:91
 TF1Convolution.cxx:92
 TF1Convolution.cxx:93
 TF1Convolution.cxx:94
 TF1Convolution.cxx:95
 TF1Convolution.cxx:96
 TF1Convolution.cxx:97
 TF1Convolution.cxx:98
 TF1Convolution.cxx:99
 TF1Convolution.cxx:100
 TF1Convolution.cxx:101
 TF1Convolution.cxx:102
 TF1Convolution.cxx:103
 TF1Convolution.cxx:104
 TF1Convolution.cxx:105
 TF1Convolution.cxx:106
 TF1Convolution.cxx:107
 TF1Convolution.cxx:108
 TF1Convolution.cxx:109
 TF1Convolution.cxx:110
 TF1Convolution.cxx:111
 TF1Convolution.cxx:112
 TF1Convolution.cxx:113
 TF1Convolution.cxx:114
 TF1Convolution.cxx:115
 TF1Convolution.cxx:116
 TF1Convolution.cxx:117
 TF1Convolution.cxx:118
 TF1Convolution.cxx:119
 TF1Convolution.cxx:120
 TF1Convolution.cxx:121
 TF1Convolution.cxx:122
 TF1Convolution.cxx:123
 TF1Convolution.cxx:124
 TF1Convolution.cxx:125
 TF1Convolution.cxx:126
 TF1Convolution.cxx:127
 TF1Convolution.cxx:128
 TF1Convolution.cxx:129
 TF1Convolution.cxx:130
 TF1Convolution.cxx:131
 TF1Convolution.cxx:132
 TF1Convolution.cxx:133
 TF1Convolution.cxx:134
 TF1Convolution.cxx:135
 TF1Convolution.cxx:136
 TF1Convolution.cxx:137
 TF1Convolution.cxx:138
 TF1Convolution.cxx:139
 TF1Convolution.cxx:140
 TF1Convolution.cxx:141
 TF1Convolution.cxx:142
 TF1Convolution.cxx:143
 TF1Convolution.cxx:144
 TF1Convolution.cxx:145
 TF1Convolution.cxx:146
 TF1Convolution.cxx:147
 TF1Convolution.cxx:148
 TF1Convolution.cxx:149
 TF1Convolution.cxx:150
 TF1Convolution.cxx:151
 TF1Convolution.cxx:152
 TF1Convolution.cxx:153
 TF1Convolution.cxx:154
 TF1Convolution.cxx:155
 TF1Convolution.cxx:156
 TF1Convolution.cxx:157
 TF1Convolution.cxx:158
 TF1Convolution.cxx:159
 TF1Convolution.cxx:160
 TF1Convolution.cxx:161
 TF1Convolution.cxx:162
 TF1Convolution.cxx:163
 TF1Convolution.cxx:164
 TF1Convolution.cxx:165
 TF1Convolution.cxx:166
 TF1Convolution.cxx:167
 TF1Convolution.cxx:168
 TF1Convolution.cxx:169
 TF1Convolution.cxx:170
 TF1Convolution.cxx:171
 TF1Convolution.cxx:172
 TF1Convolution.cxx:173
 TF1Convolution.cxx:174
 TF1Convolution.cxx:175
 TF1Convolution.cxx:176
 TF1Convolution.cxx:177
 TF1Convolution.cxx:178
 TF1Convolution.cxx:179
 TF1Convolution.cxx:180
 TF1Convolution.cxx:181
 TF1Convolution.cxx:182
 TF1Convolution.cxx:183
 TF1Convolution.cxx:184
 TF1Convolution.cxx:185
 TF1Convolution.cxx:186
 TF1Convolution.cxx:187
 TF1Convolution.cxx:188
 TF1Convolution.cxx:189
 TF1Convolution.cxx:190
 TF1Convolution.cxx:191
 TF1Convolution.cxx:192
 TF1Convolution.cxx:193
 TF1Convolution.cxx:194
 TF1Convolution.cxx:195
 TF1Convolution.cxx:196
 TF1Convolution.cxx:197
 TF1Convolution.cxx:198
 TF1Convolution.cxx:199
 TF1Convolution.cxx:200
 TF1Convolution.cxx:201
 TF1Convolution.cxx:202
 TF1Convolution.cxx:203
 TF1Convolution.cxx:204
 TF1Convolution.cxx:205
 TF1Convolution.cxx:206
 TF1Convolution.cxx:207
 TF1Convolution.cxx:208
 TF1Convolution.cxx:209
 TF1Convolution.cxx:210
 TF1Convolution.cxx:211
 TF1Convolution.cxx:212
 TF1Convolution.cxx:213
 TF1Convolution.cxx:214
 TF1Convolution.cxx:215
 TF1Convolution.cxx:216
 TF1Convolution.cxx:217
 TF1Convolution.cxx:218
 TF1Convolution.cxx:219
 TF1Convolution.cxx:220
 TF1Convolution.cxx:221
 TF1Convolution.cxx:222
 TF1Convolution.cxx:223
 TF1Convolution.cxx:224
 TF1Convolution.cxx:225
 TF1Convolution.cxx:226
 TF1Convolution.cxx:227
 TF1Convolution.cxx:228
 TF1Convolution.cxx:229
 TF1Convolution.cxx:230
 TF1Convolution.cxx:231
 TF1Convolution.cxx:232
 TF1Convolution.cxx:233
 TF1Convolution.cxx:234
 TF1Convolution.cxx:235
 TF1Convolution.cxx:236
 TF1Convolution.cxx:237
 TF1Convolution.cxx:238
 TF1Convolution.cxx:239
 TF1Convolution.cxx:240
 TF1Convolution.cxx:241
 TF1Convolution.cxx:242
 TF1Convolution.cxx:243
 TF1Convolution.cxx:244
 TF1Convolution.cxx:245
 TF1Convolution.cxx:246
 TF1Convolution.cxx:247
 TF1Convolution.cxx:248
 TF1Convolution.cxx:249
 TF1Convolution.cxx:250
 TF1Convolution.cxx:251
 TF1Convolution.cxx:252
 TF1Convolution.cxx:253
 TF1Convolution.cxx:254
 TF1Convolution.cxx:255
 TF1Convolution.cxx:256
 TF1Convolution.cxx:257
 TF1Convolution.cxx:258
 TF1Convolution.cxx:259
 TF1Convolution.cxx:260
 TF1Convolution.cxx:261
 TF1Convolution.cxx:262
 TF1Convolution.cxx:263
 TF1Convolution.cxx:264
 TF1Convolution.cxx:265
 TF1Convolution.cxx:266
 TF1Convolution.cxx:267
 TF1Convolution.cxx:268
 TF1Convolution.cxx:269
 TF1Convolution.cxx:270
 TF1Convolution.cxx:271
 TF1Convolution.cxx:272
 TF1Convolution.cxx:273
 TF1Convolution.cxx:274
 TF1Convolution.cxx:275
 TF1Convolution.cxx:276
 TF1Convolution.cxx:277
 TF1Convolution.cxx:278
 TF1Convolution.cxx:279
 TF1Convolution.cxx:280
 TF1Convolution.cxx:281
 TF1Convolution.cxx:282
 TF1Convolution.cxx:283
 TF1Convolution.cxx:284
 TF1Convolution.cxx:285
 TF1Convolution.cxx:286
 TF1Convolution.cxx:287
 TF1Convolution.cxx:288
 TF1Convolution.cxx:289
 TF1Convolution.cxx:290
 TF1Convolution.cxx:291
 TF1Convolution.cxx:292
 TF1Convolution.cxx:293
 TF1Convolution.cxx:294
 TF1Convolution.cxx:295
 TF1Convolution.cxx:296
 TF1Convolution.cxx:297
 TF1Convolution.cxx:298
 TF1Convolution.cxx:299
 TF1Convolution.cxx:300
 TF1Convolution.cxx:301
 TF1Convolution.cxx:302
 TF1Convolution.cxx:303
 TF1Convolution.cxx:304
 TF1Convolution.cxx:305
 TF1Convolution.cxx:306
 TF1Convolution.cxx:307
 TF1Convolution.cxx:308
 TF1Convolution.cxx:309
 TF1Convolution.cxx:310
 TF1Convolution.cxx:311
 TF1Convolution.cxx:312
 TF1Convolution.cxx:313
 TF1Convolution.cxx:314
 TF1Convolution.cxx:315
 TF1Convolution.cxx:316
 TF1Convolution.cxx:317
 TF1Convolution.cxx:318
 TF1Convolution.cxx:319
 TF1Convolution.cxx:320
 TF1Convolution.cxx:321
 TF1Convolution.cxx:322
 TF1Convolution.cxx:323
 TF1Convolution.cxx:324
 TF1Convolution.cxx:325
 TF1Convolution.cxx:326
 TF1Convolution.cxx:327
 TF1Convolution.cxx:328
 TF1Convolution.cxx:329
 TF1Convolution.cxx:330
 TF1Convolution.cxx:331
 TF1Convolution.cxx:332
 TF1Convolution.cxx:333
 TF1Convolution.cxx:334
 TF1Convolution.cxx:335
 TF1Convolution.cxx:336
 TF1Convolution.cxx:337
 TF1Convolution.cxx:338
 TF1Convolution.cxx:339
 TF1Convolution.cxx:340
 TF1Convolution.cxx:341
 TF1Convolution.cxx:342
 TF1Convolution.cxx:343
 TF1Convolution.cxx:344
 TF1Convolution.cxx:345
 TF1Convolution.cxx:346
 TF1Convolution.cxx:347
 TF1Convolution.cxx:348
 TF1Convolution.cxx:349
 TF1Convolution.cxx:350
 TF1Convolution.cxx:351
 TF1Convolution.cxx:352
 TF1Convolution.cxx:353
 TF1Convolution.cxx:354
 TF1Convolution.cxx:355
 TF1Convolution.cxx:356
 TF1Convolution.cxx:357
 TF1Convolution.cxx:358
 TF1Convolution.cxx:359
 TF1Convolution.cxx:360
 TF1Convolution.cxx:361
 TF1Convolution.cxx:362
 TF1Convolution.cxx:363
 TF1Convolution.cxx:364
 TF1Convolution.cxx:365
 TF1Convolution.cxx:366
 TF1Convolution.cxx:367
 TF1Convolution.cxx:368
 TF1Convolution.cxx:369
 TF1Convolution.cxx:370
 TF1Convolution.cxx:371
 TF1Convolution.cxx:372
 TF1Convolution.cxx:373
 TF1Convolution.cxx:374
 TF1Convolution.cxx:375
 TF1Convolution.cxx:376
 TF1Convolution.cxx:377
 TF1Convolution.cxx:378
 TF1Convolution.cxx:379
 TF1Convolution.cxx:380
 TF1Convolution.cxx:381
 TF1Convolution.cxx:382
 TF1Convolution.cxx:383
 TF1Convolution.cxx:384
 TF1Convolution.cxx:385
 TF1Convolution.cxx:386
 TF1Convolution.cxx:387
 TF1Convolution.cxx:388
 TF1Convolution.cxx:389
 TF1Convolution.cxx:390
 TF1Convolution.cxx:391
 TF1Convolution.cxx:392
 TF1Convolution.cxx:393
 TF1Convolution.cxx:394
 TF1Convolution.cxx:395
 TF1Convolution.cxx:396
 TF1Convolution.cxx:397
 TF1Convolution.cxx:398
 TF1Convolution.cxx:399
 TF1Convolution.cxx:400