// @(#)root/unuran:$Id$
// Authors: L. Moneta, J. Leydold Wed Feb 28 2007

/**********************************************************************
 *                                                                    *
 * Copyright (c) 2010  LCG ROOT Math Team, CERN/PH-SFT                *
 *                                                                    *
 *                                                                    *
 **********************************************************************/

// Implementation file for class TUnuranSampler
#include "TUnuranSampler.h"

#include "TUnuranContDist.h"
#include "TUnuranDiscrDist.h"
#include "TUnuranMultiContDist.h"
#include "TUnuran.h"
#include "Math/OneDimFunctionAdapter.h"
#include "Math/DistSamplerOptions.h"
#include "Fit/DataRange.h"
//#include "Math/WrappedTF1.h"

#include "TRandom.h"
#include "TError.h"

#include "TF1.h"
#include <cassert>
#include <cmath>

ClassImp(TUnuranSampler)

TUnuranSampler::TUnuranSampler() : ROOT::Math::DistSampler(),
   fOneDim(false),
   fDiscrete(false),
   fHasMode(false), fHasArea(false),
   fMode(0), fArea(0),
   fFunc1D(0),
   fUnuran(new TUnuran()  )
{
   fLevel = ROOT::Math::DistSamplerOptions::DefaultPrintLevel();
}

TUnuranSampler::~TUnuranSampler() {
   assert(fUnuran != 0);
   delete fUnuran;
}

bool TUnuranSampler::Init(const char * algo) {
   // initialize unuran classes using the given algorithm
   assert (fUnuran != 0 );
   if (NDim() == 0)  {
      Error("TUnuranSampler::Init","Distribution function has not been set ! Need to call SetFunction first.");
      return false;
   }

   if (fLevel < 0) fLevel =  ROOT::Math::DistSamplerOptions::DefaultPrintLevel();

   TString method(algo);
   if (method.IsNull() ) {
      if (NDim() == 1) method = ROOT::Math::DistSamplerOptions::DefaultAlgorithm1D();
      else  method = ROOT::Math::DistSamplerOptions::DefaultAlgorithmND();
   }
   method.ToUpper();

   bool ret = false;
   if (NDim() == 1) {
       // check if distribution is discrete by
      // using first string in the method name is "D"
      if (method.First("D") == 0) {
         if (fLevel>1) Info("TUnuranSampler::Init","Initialize one-dim discrete distribution with method %s",method.Data());
         ret =  DoInitDiscrete1D(method);
      }
      else {
         if (fLevel>1) Info("TUnuranSampler::Init","Initialize one-dim continuous distribution with method %s",method.Data());
         ret =  DoInit1D(method);
      }
   }
   else {
      if (fLevel>1) Info("TUnuranSampler::Init","Initialize multi-dim continuous distribution with method %s",method.Data());
      ret = DoInitND(method);
   }
   // set print level in UNURAN (must be done after having initialized) -
   if (fLevel>0) {
      //fUnuran->SetLogLevel(fLevel); ( seems not to work  disable for the time being)
      if (ret) Info("TUnuranSampler::Init","Successfully initailized Unuran with method %s",method.Data() );
      else Error("TUnuranSampler::Init","Failed to  initailize Unuran with method %s",method.Data() );
      // seems not to work in UNURAN (cll only when level > 0 )
   }
   return ret;
}


bool TUnuranSampler::Init(const ROOT::Math::DistSamplerOptions & opt ) {
   // default initialization with algorithm name
   SetPrintLevel(opt.PrintLevel() );
   return Init(opt.Algorithm().c_str() );
}


bool TUnuranSampler::DoInit1D(const char * method) {
   // initilize for 1D sampling
   // need to create 1D interface from Multidim one
   // (to do: use directly 1D functions ??)
   fOneDim = true;
   TUnuranContDist * dist = 0;
   if (fFunc1D == 0) {
      ROOT::Math::OneDimMultiFunctionAdapter<> function(ParentPdf() );
      dist = new TUnuranContDist(function,0,false,true);
   }
   else {
      dist = new TUnuranContDist(*fFunc1D); // no need to copy the function
   }
   // set range in distribution (support only one range)
   const ROOT::Fit::DataRange & range = PdfRange();
   if (range.Size(0) > 0) {
      double xmin, xmax;
      range.GetRange(0,xmin,xmax);
      dist->SetDomain(xmin,xmax);
   }
   if (fHasMode) dist->SetMode(fMode);
   if (fHasArea) dist->SetPdfArea(fArea);

   bool ret = false;
   if (method) ret =  fUnuran->Init(*dist, method);
   else ret =  fUnuran->Init(*dist);
   delete dist;
   return ret;
}

bool TUnuranSampler::DoInitDiscrete1D(const char * method) {
   // initilize for 1D sampling of discrete distributions
   fOneDim = true;
   fDiscrete = true;
   TUnuranDiscrDist * dist = 0;
   if (fFunc1D == 0) {
      // need to copy the passed function pointer in this case
      ROOT::Math::OneDimMultiFunctionAdapter<> function(ParentPdf() );
      dist = new TUnuranDiscrDist(function,true);
   }
   else {
      // no need to copy the function since fFunc1D is managed outside
      dist = new TUnuranDiscrDist(*fFunc1D, false);
   }
   // set range in distribution (support only one range)
   // otherwise 0, inf is assumed
   const ROOT::Fit::DataRange & range = PdfRange();
   if (range.Size(0) > 0) {
      double xmin, xmax;
      range.GetRange(0,xmin,xmax);
      if (xmin < 0) {
         Warning("DoInitDiscrete1D","range starts from negative values - set minimum to zero");
         xmin = 0;
      }
      dist->SetDomain(int(xmin+0.1),int(xmax+0.1));
   }
   if (fHasMode) dist->SetMode(int(fMode+0.1));
   if (fHasArea) dist->SetProbSum(fArea);

   bool ret =  fUnuran->Init(*dist, method);
   delete dist;
   return ret;
}


bool TUnuranSampler::DoInitND(const char * method) {
   // initilize for 1D sampling
   TUnuranMultiContDist dist(ParentPdf());
   // set range in distribution (support only one range)
   const ROOT::Fit::DataRange & range = PdfRange();
   if (range.IsSet()) {
      std::vector<double> xmin(range.NDim() );
      std::vector<double> xmax(range.NDim() );
      range.GetRange(&xmin[0],&xmax[0]);
      dist.SetDomain(&xmin.front(),&xmax.front());
//       std::cout << " range is min = ";
//       for (int j = 0; j < NDim(); ++j) std::cout << xmin[j] << "   ";
//       std::cout << " max = ";
//       for (int j = 0; j < NDim(); ++j) std::cout << xmax[j] << "   ";
//       std::cout << std::endl;
   }
   fOneDim = false;
   if (method) return fUnuran->Init(dist, method);
   return fUnuran->Init(dist);
}

void TUnuranSampler::SetFunction(TF1 * pdf) {
   // set function from a TF1 pointer
   SetFunction<TF1>(*pdf, pdf->GetNdim());
}

void TUnuranSampler::SetRandom(TRandom * r) {
   // set random generator (must be called before Init to have effect)
   fUnuran->SetRandom(r);
}

void TUnuranSampler::SetSeed(unsigned int seed) {
   // set random generator seed (must be called before Init to have effect)
   fUnuran->SetSeed(seed);
}

TRandom * TUnuranSampler::GetRandom() {
   // get random generator used
   return  fUnuran->GetRandom();
}

double TUnuranSampler::Sample1D() {
   // sample 1D distributions
   return (fDiscrete) ? (double) fUnuran->SampleDiscr() : fUnuran->Sample();
}

bool TUnuranSampler::Sample(double * x) {
   // sample multi-dim distributions
   if (!fOneDim) return fUnuran->SampleMulti(x);
   x[0] = Sample1D();
   return true;
}


bool TUnuranSampler::SampleBin(double prob, double & value, double *error) {
   // sample a bin according to Poisson statistics

   TRandom * r = fUnuran->GetRandom();
   if (!r) return false;
   value = r->Poisson(prob);
   if (error) *error = std::sqrt(value);
   return true;
}
 TUnuranSampler.cxx:1
 TUnuranSampler.cxx:2
 TUnuranSampler.cxx:3
 TUnuranSampler.cxx:4
 TUnuranSampler.cxx:5
 TUnuranSampler.cxx:6
 TUnuranSampler.cxx:7
 TUnuranSampler.cxx:8
 TUnuranSampler.cxx:9
 TUnuranSampler.cxx:10
 TUnuranSampler.cxx:11
 TUnuranSampler.cxx:12
 TUnuranSampler.cxx:13
 TUnuranSampler.cxx:14
 TUnuranSampler.cxx:15
 TUnuranSampler.cxx:16
 TUnuranSampler.cxx:17
 TUnuranSampler.cxx:18
 TUnuranSampler.cxx:19
 TUnuranSampler.cxx:20
 TUnuranSampler.cxx:21
 TUnuranSampler.cxx:22
 TUnuranSampler.cxx:23
 TUnuranSampler.cxx:24
 TUnuranSampler.cxx:25
 TUnuranSampler.cxx:26
 TUnuranSampler.cxx:27
 TUnuranSampler.cxx:28
 TUnuranSampler.cxx:29
 TUnuranSampler.cxx:30
 TUnuranSampler.cxx:31
 TUnuranSampler.cxx:32
 TUnuranSampler.cxx:33
 TUnuranSampler.cxx:34
 TUnuranSampler.cxx:35
 TUnuranSampler.cxx:36
 TUnuranSampler.cxx:37
 TUnuranSampler.cxx:38
 TUnuranSampler.cxx:39
 TUnuranSampler.cxx:40
 TUnuranSampler.cxx:41
 TUnuranSampler.cxx:42
 TUnuranSampler.cxx:43
 TUnuranSampler.cxx:44
 TUnuranSampler.cxx:45
 TUnuranSampler.cxx:46
 TUnuranSampler.cxx:47
 TUnuranSampler.cxx:48
 TUnuranSampler.cxx:49
 TUnuranSampler.cxx:50
 TUnuranSampler.cxx:51
 TUnuranSampler.cxx:52
 TUnuranSampler.cxx:53
 TUnuranSampler.cxx:54
 TUnuranSampler.cxx:55
 TUnuranSampler.cxx:56
 TUnuranSampler.cxx:57
 TUnuranSampler.cxx:58
 TUnuranSampler.cxx:59
 TUnuranSampler.cxx:60
 TUnuranSampler.cxx:61
 TUnuranSampler.cxx:62
 TUnuranSampler.cxx:63
 TUnuranSampler.cxx:64
 TUnuranSampler.cxx:65
 TUnuranSampler.cxx:66
 TUnuranSampler.cxx:67
 TUnuranSampler.cxx:68
 TUnuranSampler.cxx:69
 TUnuranSampler.cxx:70
 TUnuranSampler.cxx:71
 TUnuranSampler.cxx:72
 TUnuranSampler.cxx:73
 TUnuranSampler.cxx:74
 TUnuranSampler.cxx:75
 TUnuranSampler.cxx:76
 TUnuranSampler.cxx:77
 TUnuranSampler.cxx:78
 TUnuranSampler.cxx:79
 TUnuranSampler.cxx:80
 TUnuranSampler.cxx:81
 TUnuranSampler.cxx:82
 TUnuranSampler.cxx:83
 TUnuranSampler.cxx:84
 TUnuranSampler.cxx:85
 TUnuranSampler.cxx:86
 TUnuranSampler.cxx:87
 TUnuranSampler.cxx:88
 TUnuranSampler.cxx:89
 TUnuranSampler.cxx:90
 TUnuranSampler.cxx:91
 TUnuranSampler.cxx:92
 TUnuranSampler.cxx:93
 TUnuranSampler.cxx:94
 TUnuranSampler.cxx:95
 TUnuranSampler.cxx:96
 TUnuranSampler.cxx:97
 TUnuranSampler.cxx:98
 TUnuranSampler.cxx:99
 TUnuranSampler.cxx:100
 TUnuranSampler.cxx:101
 TUnuranSampler.cxx:102
 TUnuranSampler.cxx:103
 TUnuranSampler.cxx:104
 TUnuranSampler.cxx:105
 TUnuranSampler.cxx:106
 TUnuranSampler.cxx:107
 TUnuranSampler.cxx:108
 TUnuranSampler.cxx:109
 TUnuranSampler.cxx:110
 TUnuranSampler.cxx:111
 TUnuranSampler.cxx:112
 TUnuranSampler.cxx:113
 TUnuranSampler.cxx:114
 TUnuranSampler.cxx:115
 TUnuranSampler.cxx:116
 TUnuranSampler.cxx:117
 TUnuranSampler.cxx:118
 TUnuranSampler.cxx:119
 TUnuranSampler.cxx:120
 TUnuranSampler.cxx:121
 TUnuranSampler.cxx:122
 TUnuranSampler.cxx:123
 TUnuranSampler.cxx:124
 TUnuranSampler.cxx:125
 TUnuranSampler.cxx:126
 TUnuranSampler.cxx:127
 TUnuranSampler.cxx:128
 TUnuranSampler.cxx:129
 TUnuranSampler.cxx:130
 TUnuranSampler.cxx:131
 TUnuranSampler.cxx:132
 TUnuranSampler.cxx:133
 TUnuranSampler.cxx:134
 TUnuranSampler.cxx:135
 TUnuranSampler.cxx:136
 TUnuranSampler.cxx:137
 TUnuranSampler.cxx:138
 TUnuranSampler.cxx:139
 TUnuranSampler.cxx:140
 TUnuranSampler.cxx:141
 TUnuranSampler.cxx:142
 TUnuranSampler.cxx:143
 TUnuranSampler.cxx:144
 TUnuranSampler.cxx:145
 TUnuranSampler.cxx:146
 TUnuranSampler.cxx:147
 TUnuranSampler.cxx:148
 TUnuranSampler.cxx:149
 TUnuranSampler.cxx:150
 TUnuranSampler.cxx:151
 TUnuranSampler.cxx:152
 TUnuranSampler.cxx:153
 TUnuranSampler.cxx:154
 TUnuranSampler.cxx:155
 TUnuranSampler.cxx:156
 TUnuranSampler.cxx:157
 TUnuranSampler.cxx:158
 TUnuranSampler.cxx:159
 TUnuranSampler.cxx:160
 TUnuranSampler.cxx:161
 TUnuranSampler.cxx:162
 TUnuranSampler.cxx:163
 TUnuranSampler.cxx:164
 TUnuranSampler.cxx:165
 TUnuranSampler.cxx:166
 TUnuranSampler.cxx:167
 TUnuranSampler.cxx:168
 TUnuranSampler.cxx:169
 TUnuranSampler.cxx:170
 TUnuranSampler.cxx:171
 TUnuranSampler.cxx:172
 TUnuranSampler.cxx:173
 TUnuranSampler.cxx:174
 TUnuranSampler.cxx:175
 TUnuranSampler.cxx:176
 TUnuranSampler.cxx:177
 TUnuranSampler.cxx:178
 TUnuranSampler.cxx:179
 TUnuranSampler.cxx:180
 TUnuranSampler.cxx:181
 TUnuranSampler.cxx:182
 TUnuranSampler.cxx:183
 TUnuranSampler.cxx:184
 TUnuranSampler.cxx:185
 TUnuranSampler.cxx:186
 TUnuranSampler.cxx:187
 TUnuranSampler.cxx:188
 TUnuranSampler.cxx:189
 TUnuranSampler.cxx:190
 TUnuranSampler.cxx:191
 TUnuranSampler.cxx:192
 TUnuranSampler.cxx:193
 TUnuranSampler.cxx:194
 TUnuranSampler.cxx:195
 TUnuranSampler.cxx:196
 TUnuranSampler.cxx:197
 TUnuranSampler.cxx:198
 TUnuranSampler.cxx:199
 TUnuranSampler.cxx:200
 TUnuranSampler.cxx:201
 TUnuranSampler.cxx:202
 TUnuranSampler.cxx:203
 TUnuranSampler.cxx:204
 TUnuranSampler.cxx:205
 TUnuranSampler.cxx:206
 TUnuranSampler.cxx:207
 TUnuranSampler.cxx:208
 TUnuranSampler.cxx:209
 TUnuranSampler.cxx:210
 TUnuranSampler.cxx:211
 TUnuranSampler.cxx:212
 TUnuranSampler.cxx:213
 TUnuranSampler.cxx:214
 TUnuranSampler.cxx:215
 TUnuranSampler.cxx:216
 TUnuranSampler.cxx:217
 TUnuranSampler.cxx:218
 TUnuranSampler.cxx:219
 TUnuranSampler.cxx:220
 TUnuranSampler.cxx:221
 TUnuranSampler.cxx:222
 TUnuranSampler.cxx:223
 TUnuranSampler.cxx:224
 TUnuranSampler.cxx:225
 TUnuranSampler.cxx:226
 TUnuranSampler.cxx:227