    /*****************************************************************************
     * Project: RooFit                                                           *
     * Package: RooFitCore                                                       *
     *    File: $Id: RooAbsData.cc,v 1.28 2005/06/20 15:44:44 wverkerke Exp $
     * Authors:                                                                  *
     *   WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu       *
     *   DK, David Kirkby,    UC Irvine,         dkirkby@uci.edu                 *
     *                                                                           *
     * Copyright (c) 2000-2005, Regents of the University of California          *
     *                          and Stanford University. All rights reserved.    *
     *                                                                           *
     * Redistribution and use in source and binary forms,                        *
     * with or without modification, are permitted according to the terms        *
     * listed in LICENSE (http://roofit.sourceforge.net/license.txt)             *
     *****************************************************************************/

    // -- CLASS DESCRIPTION [DATA] --
    // RooAbsData is the common abstract base class for binned and unbinned
    // datasets. The abstract interface defines plotting and tabulating entry
    // points for its contents and provides an iterator over its elements
    // (bins for binned data sets, data points for unbinned datasets).

    #include "RooFit.h"

    #include "RooAbsData.h"
    #include "RooAbsData.h"
    #include "RooFormulaVar.h"
    #include "RooCmdConfig.h"
    #include "RooAbsRealLValue.h"

    ClassImp(RooAbsData)
    ;

    RooAbsData::RooAbsData()
    {
      // Default constructor
      _iterator = _vars.createIterator() ;
      _cacheIter = _cachedVars.createIterator() ;
    }

    RooAbsData::RooAbsData(const char *name, const char *title, const RooArgSet& vars) :
      TNamed(name,title), _vars("Dataset Variables"), _cachedVars("Cached Variables"),
      _doDirtyProp(kTRUE)
    {
      // Constructor from a set of variables. Only fundamental elements of vars
      // (RooRealVar,RooCategory etc) are stored as part of the dataset

      // clone the fundamentals of the given data set into internal buffer
      TIterator* iter = vars.createIterator() ;
      RooAbsArg *var;
      while((0 != (var= (RooAbsArg*)iter->Next()))) {
        if (!var->isFundamental()) {
          cout << "RooAbsSet::initialize(" << GetName()
    	   << "): Data set cannot contain non-fundamental types, ignoring "
    	   << var->GetName() << endl ;
        } else {
          _vars.addClone(*var);
        }
      }
      delete iter ;

      _iterator= _vars.createIterator();
      _cacheIter = _cachedVars.createIterator() ;
    }

    RooAbsData::RooAbsData(const RooAbsData& other, const char* newname) :
      TNamed(newname?newname:other.GetName(),other.GetTitle()),
      RooPrintable(other), _vars(),
      _cachedVars("Cached Variables"), _doDirtyProp(kTRUE)
    {
      // Copy constructor
      _vars.addClone(other._vars) ;
      _iterator= _vars.createIterator();
      _cacheIter = _cachedVars.createIterator() ;
    }

     RooAbsData::~RooAbsData()
    {
      // Destructor

      // delete owned contents.
      delete _iterator ;
      delete _cacheIter ;
    }

     RooAbsData* RooAbsData::reduce(RooCmdArg arg1,RooCmdArg arg2,RooCmdArg arg3,RooCmdArg arg4,
    			       RooCmdArg arg5,RooCmdArg arg6,RooCmdArg arg7,RooCmdArg arg8)
    {
      // Create a reduced copy of this dataset. The caller takes ownership of the returned dataset
      //
      // The following optional named arguments are accepted
      //
      //   SelectVars(const RooArgSet& vars) -- Only retain the listed observables in the output dataset
      //   Cut(const char* expression)       -- Only retain event surviving the given cut expression
      //   Cut(const RooFormulaVar& expr)    -- Only retain event surviving the given cut formula
      //   CutRange(const char* name)        -- Only retain events inside range with given name. Multiple CutRange
      //                                        arguments may be given to select multiple ranges
      //   EventRange(int lo, int hi)        -- Only retain events with given sequential event numbers
      //   Name(const char* name)            -- Give specified name to output dataset
      //   Title(const char* name)           -- Give specified title to output dataset
      //

      // Define configuration for this method
      RooCmdConfig pc(Form("RooAbsData::reduce(%s)",GetName())) ;
      pc.defineString("name","Name",0,"") ;
      pc.defineString("title","Title",0,"") ;
      pc.defineString("cutRange","CutRange",0,"") ;
      pc.defineString("cutSpec","CutSpec",0,"") ;
      pc.defineObject("cutVar","CutVar",0,0) ;
      pc.defineInt("evtStart","EventRange",0,0) ;
      pc.defineInt("evtStop","EventRange",1,2000000000) ;
      pc.defineObject("varSel","SelectVars",0,0) ;
      pc.defineMutex("CutVar","CutSpec") ;

      // Process & check varargs
      pc.process(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8) ;
      if (!pc.ok(kTRUE)) {
        return 0 ;
      }

      // Extract values from named arguments
      const char* cutRange = pc.getString("cutRange",0,kTRUE) ;
      const char* cutSpec = pc.getString("cutSpec",0,kTRUE) ;
      RooFormulaVar* cutVar = static_cast<RooFormulaVar*>(pc.getObject("cutVar",0)) ;
      Int_t nStart = pc.getInt("evtStart",0) ;
      Int_t nStop = pc.getInt("evtStop",2000000000) ;
      RooArgSet* varSet = static_cast<RooArgSet*>(pc.getObject("varSel")) ;
      const char* name = pc.getString("name",0,kTRUE) ;
      const char* title = pc.getString("title",0,kTRUE) ;

      // Make sure varSubset doesn't contain any variable not in this dataset
      RooArgSet varSubset ;
      if (varSet) {
        varSubset.add(*varSet) ;
        TIterator* iter = varSubset.createIterator() ;
        RooAbsArg* arg ;
        while((arg=(RooAbsArg*)iter->Next())) {
          if (!_vars.find(arg->GetName())) {
    	cout << "RooAbsData::reduce(" << GetName() << ") WARNING: variable "
    	     << arg->GetName() << " not in dataset, ignored" << endl ;
    	varSubset.remove(*arg) ;
          }
        }
        delete iter ;
      } else {
        varSubset.add(*get()) ;
      }

      RooAbsData* ret = 0 ;
      if (cutSpec) {

        RooFormulaVar cutVarTmp(cutSpec,cutSpec,*get()) ;
        ret =  reduceEng(varSubset,&cutVarTmp,cutRange,nStart,nStop,kFALSE) ;

      } else if (cutVar) {

        ret = reduceEng(varSubset,cutVar,cutRange,nStart,nStop,kFALSE) ;

      } else {

        ret = reduceEng(varSubset,0,cutRange,nStart,nStop,kFALSE) ;

      }

      if (!ret) return 0 ;

      if (name) {
        ret->SetName(name) ;
      }
      if (title) {
        ret->SetTitle(title) ;
      }

      return ret ;
    }

     RooAbsData* RooAbsData::reduce(const char* cut)
    {
      // Create a subset of the data set by applying the given cut on the data points.
      // The cut expression can refer to any variable in the data set. For cuts involving
      // other variables, such as intermediate formula objects, use the equivalent
      // reduce method specifying the as a RooFormulVar reference.

      RooFormulaVar cutVar(cut,cut,*get()) ;
      return reduceEng(*get(),&cutVar,0,0,2000000000,kFALSE) ;
    }

     RooAbsData* RooAbsData::reduce(const RooFormulaVar& cutVar)
    {
      // Create a subset of the data set by applying the given cut on the data points.
      // The 'cutVar' formula variable is used to select the subset of data points to be
      // retained in the reduced data collection.
      return reduceEng(*get(),&cutVar,0,0,2000000000,kFALSE) ;
    }

     RooAbsData* RooAbsData::reduce(const RooArgSet& varSubset, const char* cut)
    {
      // Create a subset of the data set by applying the given cut on the data points
      // and reducing the dimensions to the specified set.
      //
      // The cut expression can refer to any variable in the data set. For cuts involving
      // other variables, such as intermediate formula objects, use the equivalent
      // reduce method specifying the as a RooFormulVar reference.

      // Make sure varSubset doesn't contain any variable not in this dataset
      RooArgSet varSubset2(varSubset) ;
      TIterator* iter = varSubset.createIterator() ;
      RooAbsArg* arg ;
      while((arg=(RooAbsArg*)iter->Next())) {
        if (!_vars.find(arg->GetName())) {
          cout << "RooAbsData::reduce(" << GetName() << ") WARNING: variable "
    	   << arg->GetName() << " not in dataset, ignored" << endl ;
          varSubset2.remove(*arg) ;
        }
      }
      delete iter ;

      if (cut && strlen(cut)>0) {
        RooFormulaVar cutVar(cut,cut,*get()) ;
        return reduceEng(varSubset2,&cutVar,0,0,2000000000,kFALSE) ;
      }
      return reduceEng(varSubset2,0,0,0,2000000000,kFALSE) ;
    }

     RooAbsData* RooAbsData::reduce(const RooArgSet& varSubset, const RooFormulaVar& cutVar)
    {
      // Create a subset of the data set by applying the given cut on the data points
      // and reducing the dimensions to the specified set.
      //
      // The 'cutVar' formula variable is used to select the subset of data points to be
      // retained in the reduced data collection.

      // Make sure varSubset doesn't contain any variable not in this dataset
      RooArgSet varSubset2(varSubset) ;
      TIterator* iter = varSubset.createIterator() ;
      RooAbsArg* arg ;
      while((arg=(RooAbsArg*)iter->Next())) {
        if (!_vars.find(arg->GetName())) {
          cout << "RooAbsData::reduce(" << GetName() << ") WARNING: variable "
    	   << arg->GetName() << " not in dataset, ignored" << endl ;
          varSubset2.remove(*arg) ;
        }
      }
      delete iter ;

      return reduceEng(varSubset2,&cutVar,0,0,2000000000,kFALSE) ;
    }

     Double_t RooAbsData::weightError(ErrorType) const
    {
      return 0 ;
    }

     void RooAbsData::weightError(Double_t& lo, Double_t& hi, ErrorType) const
    {
      lo=0 ; hi=0 ;
    }

     RooPlot* RooAbsData::plotOn(RooPlot* frame, const RooCmdArg& arg1, const RooCmdArg& arg2,
    			    const RooCmdArg& arg3, const RooCmdArg& arg4, const RooCmdArg& arg5,
    			    const RooCmdArg& arg6, const RooCmdArg& arg7, const RooCmdArg& arg8) const
    {
      // Plot dataset on specified frame. By default an unbinned dataset will use the default binning of
      // the target frame. A binned dataset will by default retain its intrinsic binning.
      //
      // The following optional named arguments can be used to modify the default behavior
      //
      // Data representation options
      // ---------------------------
      // Asymmetry(const RooCategory& c) -- Show the asymmetry of the daya in given two-state category [F(+)-F(-)] / [F(+)+F(-)].
      //                                    Category must have two states with indices -1 and +1 or three states with indeces -1,0 and +1.
      // ErrorType(RooAbsData::EType)    -- Select the type of error drawn: Poisson (default) draws asymmetric Poisson
      //                                    confidence intervals. SumW2 draws symmetric sum-of-weights error
      // Binning(double xlo, double xhi, -- Use specified binning to draw dataset
      //                      int nbins)
      // Binning(const RooAbsBinning&)   -- Use specified binning to draw dataset
      // Binning(const char* name)       -- Use binning with specified name to draw dataset
      // RefreshNorm(Bool_t flag)        -- Force refreshing for PDF normalization information in frame.
      //                                    If set, any subsequent PDF will normalize to this dataset, even if it is
      //                                    not the first one added to the frame. By default only the 1st dataset
      //                                    added to a frame will update the normalization information
      //
      // Histogram drawing options
      // -------------------------
      // DrawOption(const char* opt)     -- Select ROOT draw option for resulting TGraph object
      // LineStyle(Int_t style)          -- Select line style by ROOT line style code, default is solid
      // LineColor(Int_t color)          -- Select line color by ROOT color code, default is black
      // LineWidth(Int_t width)          -- Select line with in pixels, default is 3
      // MarkerStyle(Int_t style)        -- Select the ROOT marker style, default is 21
      // MarkerColor(Int_t color)        -- Select the ROOT marker color, default is black
      // MarkerSize(Double_t size)       -- Select the ROOT marker size
      // XErrorSize(Double_t frac)       -- Select size of X error bar as fraction of the bin width, default is 1
      //
      //
      // Misc. other options
      // -------------------
      // Name(const chat* name)          -- Give curve specified name in frame. Useful if curve is to be referenced later
      // Invisble(Bool_t flag)           -- Add curve to frame, but do not display. Useful in combination AddTo()
      // AddTo(const char* name,         -- Add constructed histogram to already existing histogram with given name and relative weight factors
      // double_t wgtSelf, double_t wgtOther)
      //
      //
      RooLinkedList l ;
      l.Add((TObject*)&arg1) ;  l.Add((TObject*)&arg2) ;
      l.Add((TObject*)&arg3) ;  l.Add((TObject*)&arg4) ;
      l.Add((TObject*)&arg5) ;  l.Add((TObject*)&arg6) ;
      l.Add((TObject*)&arg7) ;  l.Add((TObject*)&arg8) ;
      return plotOn(frame,l) ;
    }

     TH1 *RooAbsData::createHistogram(const char *name, const RooAbsRealLValue& xvar,
    				 const RooCmdArg& arg1, const RooCmdArg& arg2, const RooCmdArg& arg3, const RooCmdArg& arg4,
    				 const RooCmdArg& arg5, const RooCmdArg& arg6, const RooCmdArg& arg7, const RooCmdArg& arg8) const
    {
      // Create and fill a ROOT histogram TH1,TH2 or TH3 with the values of this dataset.
      //
      // This function accepts the following arguments
      //
      // name -- Name of the ROOT histogram
      // xvar -- Observable to be mapped on x axis of ROOT histogram
      //
      // Binning(const char* name)                    -- Apply binning with given name to x axis of histogram
      // Binning(RooAbsBinning& binning)              -- Apply specified binning to x axis of histogram
      // Binning(double lo, double hi, int nbins)     -- Apply specified binning to x axis of histogram
      //
      // YVar(const RooAbsRealLValue& var,...)    -- Observable to be mapped on y axis of ROOT histogram
      // ZVar(const RooAbsRealLValue& var,...)    -- Observable to be mapped on z axis of ROOT histogram
      //
      // The YVar() and ZVar() arguments can be supplied with optional Binning() arguments to control the binning of the Y and Z axes, e.g.
      // createHistogram("histo",x,Binning(-1,1,20), YVar(y,Binning(-1,1,30)), ZVar(z,Binning("zbinning")))
      //
      // The caller takes ownership of the returned histogram

      RooLinkedList l ;
      l.Add((TObject*)&arg1) ;  l.Add((TObject*)&arg2) ;
      l.Add((TObject*)&arg3) ;  l.Add((TObject*)&arg4) ;
      l.Add((TObject*)&arg5) ;  l.Add((TObject*)&arg6) ;
      l.Add((TObject*)&arg7) ;  l.Add((TObject*)&arg8) ;

      // Define configuration for this method
      RooCmdConfig pc(Form("RooAbsData::createHistogram(%s)",GetName())) ;
      pc.defineString("cutRange","CutRange",0,"",kTRUE) ;
      pc.defineString("cutString","CutSpec",0,"") ;
      pc.defineObject("yvar","YVar",0,0) ;
      pc.defineObject("zvar","ZVar",0,0) ;
      pc.allowUndefined() ;

      // Process & check varargs
      pc.process(l) ;
      if (!pc.ok(kTRUE)) {
        return 0 ;
      }

      const char* cutSpec = pc.getString("cutString",0,kTRUE) ;
      const char* cutRange = pc.getString("cutRange",0,kTRUE) ;

      RooArgList vars(xvar) ;
      RooAbsArg* yvar = static_cast<RooAbsArg*>(pc.getObject("yvar")) ;
      if (yvar) {
        vars.add(*yvar) ;
      }
      RooAbsArg* zvar = static_cast<RooAbsArg*>(pc.getObject("zvar")) ;
      if (zvar) {
        vars.add(*zvar) ;
      }

      pc.stripCmdList(l,"CutRange,CutSpec") ;
      TH1* histo = xvar.createHistogram(name,l) ;
      fillHistogram(histo,vars,cutSpec,cutRange) ;

      return histo ;
    }