// @(#)root/hist:$Id$
// Author: Axel Naumann (2011-12-13)

/*************************************************************************
 * Copyright (C) 1995-2012, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "THn.h"

#include "TClass.h"

namespace {
   //______________________________________________________________________________
   //
   // Helper struct to hold one dimension's bin range for THnBinIter.
   //______________________________________________________________________________
   struct CounterRange_t {
      Int_t i;
      Int_t first;
      Int_t last;
      Int_t len;
      Long64_t cellSize;
   };

   //______________________________________________________________________________
   //
   // THnBinIter iterates over all bins of a THn, recursing over all dimensions.
   //______________________________________________________________________________
   class THnBinIter: public ROOT::THnBaseBinIter {
   public:
      THnBinIter(Int_t dim, const TObjArray* axes, const TNDArray* arr,
                 Bool_t respectAxisRange);
      ~THnBinIter() { delete [] fCounter; }

      Long64_t Next(Int_t* coord = 0);
      Int_t GetCoord(Int_t dim) const { return fCounter[dim].i; }
   private:
      THnBinIter(const THnBinIter&); // intentionally unimplemented
      THnBinIter& operator=(const THnBinIter&); // intentionally unimplemented

   public:
      Int_t fNdimensions;
      Long64_t fIndex;
      const TNDArray* fArray;
      CounterRange_t* fCounter;
   };


   //______________________________________________________________________________
   THnBinIter::THnBinIter(Int_t dim, const TObjArray* axes,
                              const TNDArray* arr, Bool_t respectAxisRange):
      ROOT::THnBaseBinIter(respectAxisRange),
      fNdimensions(dim), fIndex(-1), fArray(arr) {
      // Construct a THnBinIter.
      fCounter = new CounterRange_t[dim]();
      for (Int_t i = 0; i < dim; ++i) {
         TAxis *axis = (TAxis*) axes->At(i);
         fCounter[i].len  = axis->GetNbins() + 2;
         fCounter[i].cellSize  = arr->GetCellSize(i);
         if (!respectAxisRange || !axis->TestBit(TAxis::kAxisRange)) {
            fCounter[i].first = 0;
            fCounter[i].last  = fCounter[i].len - 1;
            fCounter[i].i     = 0;
            continue;
         }
         fHaveSkippedBin = kTRUE;
         Int_t min = axis->GetFirst();
         Int_t max = axis->GetLast();
         if (min == 0 && max == 0) {
            // special case where TAxis::SetBit(kAxisRange) and
            // over- and underflow bins are de-selected.
            // first and last are == 0 due to axis12->SetRange(1, axis12->GetNbins());
            min = 1;
            max = axis->GetNbins();
         }
         fCounter[i].first = min;
         fCounter[i].last  = max;
         fCounter[i].i     = min;
         fIndex += fCounter[i].first * fCounter[i].cellSize;
      }
      // First Next() will increment it:
      --fCounter[dim - 1].i;
   }

   //______________________________________________________________________________
   Long64_t THnBinIter::Next(Int_t* coord /*= 0*/) {
      // Return the current linear bin index (in range), then go to the next bin.
      // If all bins have been visited, return -1.
      if (fNdimensions < 0) return -1; // end
      ++fCounter[fNdimensions - 1].i;
      ++fIndex;
      // Wrap around if needed
      for (Int_t d = fNdimensions - 1; d > 0 && fCounter[d].i > fCounter[d].last; --d) {
         // We skip last + 1..size and 0..first - 1, adjust fIndex
         Int_t skippedCells = fCounter[d].len - (fCounter[d].last + 1);
         skippedCells += fCounter[d].first;
         fIndex += skippedCells * fCounter[d].cellSize;
         fCounter[d].i = fCounter[d].first;
         ++fCounter[d - 1].i;
      }
      if (fCounter[0].i > fCounter[0].last) {
         fNdimensions = -1;
         return -1;
      }
      if (coord) {
         for (Int_t d = 0; d < fNdimensions; ++d) {
            coord[d] = fCounter[d].i;
         }
      }
      return fIndex;
   }
} // unnamed namespce



//______________________________________________________________________________
//
//
//    Multidimensional histogram.
//
// Use a THn if you really, really have to store more than three dimensions,
// and if a large fraction of all bins are filled.
// Better alternatives are
//   * THnSparse if a fraction of all bins are filled
//   * TTree
// The major problem of THn is the memory use caused by n-dimensional
// histogramming: a THnD with 8 dimensions and 100 bins per dimension needs
// more than 2.5GB of RAM!
//
// To construct a THn object you must use one of its templated, derived
// classes:
// THnD (typedef for THnT<Double_t>): bin content held by a Double_t,
// THnF (typedef for THnT<Float_t>): bin content held by a Float_t,
// THnL (typedef for THnT<Long_t>): bin content held by a Long_t,
// THnI (typedef for THnT<Int_t>): bin content held by an Int_t,
// THnS (typedef for THnT<Short_t>): bin content held by a Short_t,
// THnC (typedef for THnT<Char_t>): bin content held by a Char_t,
//
// They take name and title, the number of dimensions, and for each dimension
// the number of bins, the minimal, and the maximal value on the dimension's
// axis. A TH2F h("h","h",10, 0., 10., 20, -5., 5.) would correspond to
//   Int_t bins[2] = {10, 20};
//   Double_t xmin[2] = {0., -5.};
//   Double_t xmax[2] = {10., 5.};
//   THnF hn("hn", "hn", 2, bins, min, max);
//
// * Filling
// A THn is filled just like a regular histogram, using
// THn::Fill(x, weight), where x is a n-dimensional Double_t value.
// To take errors into account, Sumw2() must be called before filling the
// histogram.
// Storage is allocated when the first bin content is stored.
//
// * Projections
// The dimensionality of a THn can be reduced by projecting it to
// 1, 2, 3, or n dimensions, which can be represented by a TH1, TH2, TH3, or
// a THn. See the Projection() members. To only project parts of the
// histogram, call
//   hn->GetAxis(12)->SetRange(from_bin, to_bin);
//
// * Conversion from other histogram classes
// The static factory function THn::CreateHn() can be used to create a THn
// from a TH1, TH2, TH3, THnSparse and (for copying) even from a THn. The
// created THn will have compatble storage type, i.e. calling CreateHn() on
// a TH2F will create a THnF.

ClassImp(THn);

//______________________________________________________________________________
THn::THn(const char* name, const char* title,
         Int_t dim, const Int_t* nbins,
         const Double_t* xmin, const Double_t* xmax):
   THnBase(name, title, dim, nbins, xmin, xmax),
   fSumw2(dim, nbins, kTRUE /*overflow*/),
   fCoordBuf() {
   // Construct a THn.
}

//______________________________________________________________________________
THn::~THn()
{
   // Destruct a THn
   delete [] fCoordBuf;
}


//______________________________________________________________________________
ROOT::THnBaseBinIter* THn::CreateIter(Bool_t respectAxisRange) const
{
   // Create an iterator over all bins. Public interface is THnIter.
   return new THnBinIter(GetNdimensions(), GetListOfAxes(), &GetArray(),
                         respectAxisRange);
}

//______________________________________________________________________________
void THn::Sumw2() {
   // Enable calculation of errors
   if (!GetCalculateErrors()) {
      fTsumw2 = 0.;
   }
}

 
//______________________________________________________________________________
void THn::AllocCoordBuf() const
{
   // Create the coordinate buffer. Outlined to hide allocation
   // from inlined functions.
   fCoordBuf = new Int_t[fNdimensions]();
}

//______________________________________________________________________________
void THn::InitStorage(Int_t* nbins, Int_t /*chunkSize*/)
{
   // Initialize the storage of a histogram created via Init()
   fCoordBuf = new Int_t[fNdimensions]();
   GetArray().Init(fNdimensions, nbins, true /*addOverflow*/);
   fSumw2.Init(fNdimensions, nbins, true /*addOverflow*/);
}

//______________________________________________________________________________
void THn::Reset(Option_t* option /*= ""*/)
{
   // Reset the contents of a THn.
   GetArray().Reset(option);
   fSumw2.Reset(option);
}
