c_cpp ROOT TH1亮点

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c_cpp ROOT TH1亮点相关的知识,希望对你有一定的参考价值。

// @(#)root/hist:$Id$
// Author: Rene Brun   30/08/99

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


//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TVirtualHistPainter                                                  //
//                                                                      //
// Abstract base class for Histogram painters                           //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#ifndef ROOT_TObject
#include "TObject.h"
#endif

class TClass;
class TH1;
class TF1;
class TList;

class TVirtualHistPainter : public TObject {

private:
   static TClass   *fgPainter; //Pointer to class painter

public:
   TVirtualHistPainter() { }
   virtual ~TVirtualHistPainter() { }
   virtual Int_t      DistancetoPrimitive(Int_t px, Int_t py) = 0;
   virtual void       DrawPanel() = 0;
   virtual void       ExecuteEvent(Int_t event, Int_t px, Int_t py) = 0;
   virtual TList     *GetContourList(Double_t contour) const = 0;
   virtual char      *GetObjectInfo(Int_t px, Int_t py) const = 0;
   virtual TList     *GetStack() const = 0;
   virtual Int_t      GetXHighlightBin() const = 0;
   virtual Int_t      GetYHighlightBin() const = 0;
   virtual Bool_t     IsInside(Int_t x, Int_t y) = 0;
   virtual Bool_t     IsInside(Double_t x, Double_t y) = 0;
   virtual void       Paint(Option_t *option="") = 0;
   virtual void       PaintStat(Int_t dostat, TF1 *fit) = 0;
   virtual void       ProcessMessage(const char *mess, const TObject *obj) = 0;
   virtual void       SetHighlight() = 0;
   virtual void       SetHistogram(TH1 *h) = 0;
   virtual void       SetStack(TList *stack) = 0;
   virtual Int_t      MakeCuts(char *cutsopt) = 0;
   virtual void       SetShowProjection(const char *option, Int_t nbins) = 0;

   static TVirtualHistPainter *HistPainter(TH1 *obj);
   static void                 SetPainter(const char *painter);

   ClassDef(TVirtualHistPainter,0)  //Abstract interface for histogram painters
};

#endif
// @(#)root/histpainter:$Id$
// Author: Rene Brun   26/08/99

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


//////////////////////////////////////////////////////////////////////////
//                                                                      //
// THistPainter                                                         //
//                                                                      //
// helper class to draw histograms                                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


#ifndef ROOT_TVirtualHistPainter
#include "TVirtualHistPainter.h"
#endif
#ifndef ROOT_TString
#include "TString.h"
#endif


class TH1;
class TAxis;
class TCutG;
class TGaxis;
class TPainter3dAlgorithms;
class TGraph2DPainter;
class TPie;
const Int_t kMaxCuts = 16;

class THistPainter : public TVirtualHistPainter {

protected:
   TH1                  *fH;                 //pointer to histogram to paint
   TAxis                *fXaxis;             //pointer to X axis
   TAxis                *fYaxis;             //pointer to Y axis
   TAxis                *fZaxis;             //pointer to Z axis
   TList                *fFunctions;         //pointer to histogram list of functions
   TPainter3dAlgorithms *fLego;              //pointer to a TPainter3dAlgorithms object
   TGraph2DPainter      *fGraph2DPainter;    //pointer to a TGraph2DPainter object
   TPie                 *fPie;               //pointer to a TPie in case of option PIE
   Double_t             *fXbuf;              //X buffer coordinates
   Double_t             *fYbuf;              //Y buffer coordinates
   Int_t                 fNcuts;             //Number of graphical cuts
   Int_t                 fCutsOpt[kMaxCuts]; //sign of each cut
   TCutG                *fCuts[kMaxCuts];    //Pointers to graphical cuts
   TList                *fStack;             //Pointer to stack of histograms (if any)
   Int_t                 fShowProjection;    //True if a projection must be drawn
   TString               fShowOption;        //Option to draw the projection
   Int_t                 fXHighlightBin;     //X highlight bin
   Int_t                 fYHighlightBin;     //Y highlight bin

public:
   THistPainter();
   virtual ~THistPainter();
   virtual void       DefineColorLevels(Int_t ndivz);
   virtual Int_t      DistancetoPrimitive(Int_t px, Int_t py);
   virtual void       DrawPanel();
   virtual void       ExecuteEvent(Int_t event, Int_t px, Int_t py);
   virtual TList     *GetContourList(Double_t contour) const;
   virtual char      *GetObjectInfo(Int_t px, Int_t py) const;
   virtual TList     *GetStack() const {return fStack;}
   virtual Int_t      GetXHighlightBin() const { return fXHighlightBin; }
   virtual Int_t      GetYHighlightBin() const { return fYHighlightBin; }
   virtual void       HighlightBin(Int_t px, Int_t py);
   virtual Bool_t     IsInside(Int_t x, Int_t y);
   virtual Bool_t     IsInside(Double_t x, Double_t y);
   virtual Int_t      MakeChopt(Option_t *option);
   virtual Int_t      MakeCuts(char *cutsopt);
   virtual void       Paint(Option_t *option="");
   virtual void       PaintArrows(Option_t *option);
   virtual void       PaintAxis(Bool_t drawGridOnly=kFALSE);
   virtual void       PaintBar(Option_t *option);
   virtual void       PaintBarH(Option_t *option);
   virtual void       PaintBoxes(Option_t *option);
   virtual void       PaintCandlePlot(Option_t *option);
   virtual void       PaintViolinPlot(Option_t *option);
   virtual void       PaintColorLevels(Option_t *option);
   virtual void       PaintTH2PolyBins(Option_t *option);
   virtual void       PaintTH2PolyColorLevels(Option_t *option);
   virtual void       PaintTH2PolyScatterPlot(Option_t *option);
   virtual void       PaintTH2PolyText(Option_t *option);
   virtual void       PaintContour(Option_t *option);
   virtual Int_t      PaintContourLine(Double_t elev1, Int_t icont1, Double_t x1, Double_t y1,
                          Double_t elev2, Int_t icont2, Double_t x2, Double_t y2,
                          Double_t *xarr, Double_t *yarr, Int_t *itarr, Double_t *levels);
   virtual void       PaintErrors(Option_t *option);
   virtual void       Paint2DErrors(Option_t *option);
   virtual void       PaintFrame();
   virtual void       PaintFunction(Option_t *option);
   virtual void       PaintHighlightBin(Option_t *option="");
   virtual void       PaintHist(Option_t *option);
   virtual void       PaintH3(Option_t *option="");
   virtual void       PaintH3Iso();
   virtual Int_t      PaintInit();
   virtual Int_t      PaintInitH();
   virtual void       PaintLego(Option_t *option);
   virtual void       PaintLegoAxis(TGaxis *axis, Double_t ang);
   virtual void       PaintPalette();
   virtual void       PaintScatterPlot(Option_t *option);
   virtual void       PaintStat(Int_t dostat, TF1 *fit);
   virtual void       PaintStat2(Int_t dostat, TF1 *fit);
   virtual void       PaintStat3(Int_t dostat, TF1 *fit);
   virtual void       PaintSurface(Option_t *option);
   virtual void       PaintTriangles(Option_t *option);
   virtual void       PaintTable(Option_t *option);
   virtual void       PaintText(Option_t *option);
   virtual void       PaintTitle();
   virtual void       PaintTF3();
   virtual void       ProcessMessage(const char *mess, const TObject *obj);
   static  Int_t      ProjectAitoff2xy(Double_t l, Double_t b, Double_t &Al, Double_t &Ab);
   static  Int_t      ProjectMercator2xy(Double_t l, Double_t b, Double_t &Al, Double_t &Ab);
   static  Int_t      ProjectSinusoidal2xy(Double_t l, Double_t b, Double_t &Al, Double_t &Ab);
   static  Int_t      ProjectParabolic2xy(Double_t l, Double_t b, Double_t &Al, Double_t &Ab);
   virtual void       RecalculateRange();
   virtual void       RecursiveRemove(TObject *) {;}
   virtual void       SetHighlight();
   virtual void       SetHistogram(TH1 *h);
   virtual void       SetStack(TList *stack) {fStack = stack;}
   virtual void       SetShowProjection(const char *option,Int_t nbins);
   virtual void       ShowProjectionX(Int_t px, Int_t py);
   virtual void       ShowProjectionY(Int_t px, Int_t py);
   virtual void       ShowProjection3(Int_t px, Int_t py);
   virtual Int_t      TableInit();

   static const char * GetBestFormat(Double_t v, Double_t e, const char *f);
   static void       PaintSpecialObjects(const TObject *obj, Option_t *option);

   ClassDef(THistPainter,0)  //Helper class to draw histograms
};

#endif
// @(#)root/histpainter:$Id$
// Author: Rene Brun   26/08/99

/*************************************************************************
 * Copyright (C) 1995-2000, 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "Riostream.h"
#include "TROOT.h"
#include "TClass.h"
#include "TSystem.h"
#include "THistPainter.h"
#include "TH2.h"
#include "TH2Poly.h"
#include "TH3.h"
#include "TProfile.h"
#include "TProfile2D.h"
#include "THStack.h"
#include "TF2.h"
#include "TF3.h"
#include "TCutG.h"
#include "TMatrixDBase.h"
#include "TMatrixFBase.h"
#include "TVectorD.h"
#include "TVectorF.h"
#include "TCanvas.h"
#include "TPad.h"
#include "TPaveStats.h"
#include "TFrame.h"
#include "TLatex.h"
#include "TLine.h"
#include "TPolyLine.h"
#include "TPoints.h"
#include "TStyle.h"
#include "TGraph.h"
#include "TMultiGraph.h"
#include "TPie.h"
#include "TGaxis.h"
#include "TColor.h"
#include "TPainter3dAlgorithms.h"
#include "TGraph2DPainter.h"
#include "TGraphDelaunay.h"
#include "TView.h"
#include "TMath.h"
#include "TRandom2.h"
#include "TObjArray.h"
#include "TVectorD.h"
#include "Hoption.h"
#include "Hparam.h"
#include "TPluginManager.h"
#include "TPaletteAxis.h"
#include "TCrown.h"
#include "TVirtualPadEditor.h"
#include "TEnv.h"
#include "TPoint.h"


//______________________________________________________________________________
/* Begin_Html
<center><h2>The histogram painter class</h2></center>

<ul>
<li><a href="#HP00">Introduction</li></a>
<li><a href="#HP01">Histograms' plotting options</li></a>
<ul>
<li><a href="#HP01a">Options supported for 1D and 2D histograms</li></a>
<li><a href="#HP01b">Options supported for 1D histograms</li></a>
<li><a href="#HP01c">Options supported for 2D histograms</li></a>
<li><a href="#HP01d">Options supported for 3D histograms</li></a>
<li><a href="#HP01e">Options supported for histograms' stacks (<tt>THStack</tt>)</li></a>
</ul>
<li><a href="#HP02">Setting the Style</li></a>
<li><a href="#HP03">Setting line, fill, marker, and text attributes</li></a>
<li><a href="#HP04">Setting Tick marks on the histogram axis</li></a>
<li><a href="#HP05">Giving titles to the X, Y and Z axis</li></a>
<li><a href="#HP060">The option "SAME"</li></a>
<ul>
<li><a href="#HP060a">Limitations</li></a>
</ul>
<li><a href="#HP06">Superimposing two histograms with different scales in the same pad</li></a>
<li><a href="#HP07">Statistics Display</li></a>
<li><a href="#HP08">Fit Statistics</li></a>
<li><a href="#HP09">The error bars options</li></a>
<li><a href="#HP100">The bar chart option</li></a>
<li><a href="#HP10">The "BAR" and "HBAR" options</li></a>
<li><a href="#HP11">The SCATter plot option (default for 2D histograms)</li></a>
<li><a href="#HP12">The ARRow option</li></a>
<li><a href="#HP13">The BOX option</li></a>
<li><a href="#HP14">The COLor option</li></a>
<li><a href="#HP140">The CANDLE option</li></a>
<li><a href="#HP141">The VIOLIN option</li></a>
<li><a href="#HP15">The TEXT and TEXTnn Option</li></a>
<li><a href="#HP16">The CONTour options</li></a>
<ul>
<li><a href="#HP16a">The LIST option</li></a>
</ul>
<li><a href="#HP17">The LEGO options</li></a>
<li><a href="#HP18">The "SURFace" options</li></a>
<li><a href="#HP19">Cylindrical, Polar, Spherical and PseudoRapidity/Phi options</li></a>
<li><a href="#HP20">Base line for bar-charts and lego plots</li></a>
<li><a href="#HP20a">TH2Poly Drawing</li></a>
<li><a href="#HP21">The SPEC option</li></a>
<li><a href="#HP22">Option "Z" : Adding the color palette on the right side of the pad</li></a>
<li><a href="#HP23">Setting the color palette</li></a>
<li><a href="#HP24">Drawing a sub-range of a 2-D histogram; the [cutg] option</li></a>
<li><a href="#HP25">Drawing options for 3D histograms</li></a>
<li><a href="#HP26">Drawing option for histograms' stacks</li></a>
<li><a href="#HP27">Drawing of 3D implicit functions</li></a>
<li><a href="#HP28">Associated functions drawing</li></a>
<li><a href="#HP29">Drawing using OpenGL</li></a>
<ul>
<li><a href="#HP29a">General information: plot types and supported options</li></a>
<li><a href="#HP290">TH3 as color boxes</li></a>
<li><a href="#HP29b">TH3 as boxes (spheres)</li></a>
<li><a href="#HP29c">TH3 as iso-surface(s)</li></a>
<li><a href="#HP29d">TF3 (implicit function)</li></a>
<li><a href="#HP29e">Parametric surfaces</li></a>
<li><a href="#HP29f">Interaction with the plots</li></a>
<li><a href="#HP29g">Selectable parts</li></a>
<li><a href="#HP29h">Rotation and zooming</li></a>
<li><a href="#HP29i">Panning</li></a>
<li><a href="#HP29j">Box cut</li></a>
<li><a href="#HP29k">Plot specific interactions (dynamic slicing etc.)</li></a>
<li><a href="#HP29l">Surface with option "GLSURF"</li></a>
<li><a href="#HP29m">TF3</li></a>
<li><a href="#HP29n">Box</li></a>
<li><a href="#HP29o">Iso</li></a>
<li><a href="#HP29p">Parametric plot</li></a>
</ul>
</ul>


<a name="HP00"></a><h3>Introduction</h3>


Histograms are drawn via the <tt>THistPainter</tt> class. Each histogram has a
pointer to its own painter (to be usable in a multithreaded program). When the
canvas has to be redrawn, the <tt>Paint</tt> function of each objects in the
pad is called. In case of histograms, <tt>TH1::Paint</tt> invokes directly
<tt>THistPainter::Paint</tt>.

<p>To draw a histogram "<tt>h</tt>" is enough to do:
<pre>
      h->Draw();
</pre>
"<tt>h</tt>" can be of any kind: 1D, 2D or 3D. To choose how the histogram will
be drawn, the <tt>Draw()</tt> method can be invoked with an option. For instance
to draw a 2D histogram as a lego plot it is enough to do:
<pre>
      h->Draw("lego");
</pre>
<tt>THistPainter</tt> offers many options to paint 1D, 2D and 3D histograms.

<p>When the <tt>Draw()</tt> method of a histogram is called for the first time
(<tt>TH1::Draw</tt>), it creates a <tt>THistPainter</tt> object and saves a
pointer to this "painter" as a data member of the histogram. The
<tt>THistPainter</tt> class specializes in the drawing of histograms. It is
separated from the histogram so that one can have histograms without the
graphics overhead, for example in a batch program. Each histogram have its own
painter rather than a central singleton painter painting all histograms, allows
two histograms to be drawn in two threads without overwriting the painter's
values.

<p>When a displayed histogram is filled again, there is not need to call the
<tt>Draw()</tt> method again; the image will be refreshed the next time the
pad will be updated.

<p>A pad is updated after one of these three actions:
<ol>
<li>  a carriage control on the ROOT command line,
<li>  a click inside the pad,
<li>  a call to <tt>TPad::Update</tt>.
</ol>

<p>By default a call to <tt>TH1::Draw()</tt> clears the pad of all objects
before drawing the new image of the histogram. One can use the <tt>"SAME"</tt>
option to leave the previous display intact and superimpose the new histogram.
The same histogram can be drawn with different graphics options in different
pads.

<p>When a displayed histogram is deleted, its image is automatically removed
from the pad.

<p> To create a copy of the histogram when drawing it, one can use
<tt>TH1::DrawClone()</tt>. This will clone the histogram and allow to change
and delete the original one without affecting the clone.


<a name="HP01"></a><h3>Histograms' plotting options</h3>


Most options can be concatenated with or without spaces or commas, for example:
<pre>
      h->Draw("E1 SAME");
</pre>
The options are not case sensitive:
<pre>
      h->Draw("e1 same");
</pre>

The default drawing option can be set with <tt>TH1::SetOption</tt> and retrieve
using <tt>TH1::GetOption</tt>:
<pre>
      root [0] h->Draw();          // Draw "h" using the standard histogram representation.
      root [1] h->Draw("E");       // Draw "h" using error bars
      root [3] h->SetOption("E");  // Change the default drawing option for "h"
      root [4] h->Draw();          // Draw "h" using error bars
      root [5] h->GetOption();     // Retrieve the default drawing option for "h"
      (const Option_t* 0xa3ff948)"E"
</pre>

<a name="HP01a"></a><h4><u>Options supported for 1D and 2D histograms</u></h4>

<table border=0>

<tr><th valign=top>"E"</th><td>
Draw error bars.
</td></tr>

<tr><th valign=top>"AXIS"</th><td>
Draw only axis.
</td></tr>

<tr><th valign=top>"AXIG"</th><td>
Draw only grid (if the grid is requested).
</td></tr>

<tr><th valign=top>"HIST"</th><td>
When an histogram has errors it is visualized by default with error bars. To
visualize it without errors use the option "HIST" together with the required
option (eg "hist same c").  The "HIST" option can also be used to plot only the
histogram and not the associated function(s).
</td></tr>

<tr><th valign=top>"FUNC"</th><td>
When an histogram has a fitted function, this option allows to draw the fit
result only.
</td></tr>

<tr><th valign=top>"SAME"</th><td>
Superimpose on previous picture in the same pad.
</td></tr>

<tr><th valign=top>"LEGO"</th><td>
Draw a lego plot with hidden line removal.
</td></tr>

<tr><th valign=top>"LEGO1"</th><td>
Draw a lego plot with hidden surface removal.
</td></tr>

<tr><th valign=top>"LEGO2"</th><td>
Draw a lego plot using colors to show the cell contents When the option "0" is
used with any LEGO option, the empty bins are not drawn.
</td></tr>

<tr><th valign=top>"LEGO3"</th><td>
Draw a lego plot with hidden surface removal, like LEGO1 but the border lines
of each lego-bar are not drawn.
</td></tr>

<tr><th valign=top>"LEGO4"</th><td>
Draw a lego plot with hidden surface removal, like LEGO1 but without the
shadow effect on each lego-bar.
</td></tr>

<tr><th valign=top>"TEXT"</th><td>
Draw bin contents as text (format set via <tt>gStyle->SetPaintTextFormat</tt>).
</td></tr>

<tr><th valign=top>"TEXTnn"</th><td>
Draw bin contents as text at angle nn (0 < nn < 90).
</td></tr>

<tr><th valign=top>"X+"</th><td>
The X-axis is drawn on the top side of the plot.
</td></tr>

<tr><th valign=top>"Y+"</th><td>
The Y-axis is drawn on the right side of the plot.
</td></tr>

</table>

<a name="HP01b"></a><h4><u>Options supported for 1D histograms</u></h4>

<table border=0>

<tr><th valign=top>" "</th><td>
Default.
</td></tr>

<tr><th valign=top>"AH"</th><td>
Draw histogram without axis. "A" can be combined with any drawing option. For
instance, "AC" draws the histogram as a smooth Curve without axis.
</td></tr>

<tr><th valign=top>"]["</th><td>
When this option is selected the first and last vertical lines of the histogram
are not drawn.
</td></tr>

<tr><th valign=top>"B"</th><td>
Bar chart option.
</td></tr>

<tr><th valign=top>"BAR"</th><td>
Like option "B", but bars can be drawn with a 3D effect.
</td></tr>

<tr><th valign=top>"HBAR"</th><td>
Like option "BAR", but bars are drawn horizontally.
</td></tr>

<tr><th valign=top>"C"</th><td>
Draw a smooth Curve through the histogram bins.
</td></tr>

<tr><th valign=top>"E0"</th><td>
Draw error bars. Markers are drawn for bins with 0 contents.
</td></tr>

<tr><th valign=top>"E1"</th><td>
Draw error bars with perpendicular lines at the edges.
</td></tr>

<tr><th valign=top>"E2"</th><td>
Draw error bars with rectangles.
</td></tr>

<tr><th valign=top>"E3"</th><td>
Draw a fill area through the end points of the vertical error bars.
</td></tr>

<tr><th valign=top>"E4"</th><td>
Draw a smoothed filled area through the end points of the error bars.
</td></tr>

<tr><th valign=top>"E5"</th><td>
Like E3 but ignore the bins with 0 contents.
</td></tr>

<tr><th valign=top>"E6"</th><td>
Like E4 but ignore the bins with 0 contents.
</td></tr>

<tr><th valign=top>"X0"</th><td>
When used with one of the "E" option, it suppress the error bar along
X as <tt>gStyle->SetErrorX(0)</tt> would do.
</td></tr>

<tr><th valign=top>"L"</th><td>
Draw a line through the bin contents.
</td></tr>

<tr><th valign=top>"P"</th><td>
Draw current marker at each bin except empty bins.
</td></tr>

<tr><th valign=top>"P0"</th><td>
Draw current marker at each bin including empty bins.
</td></tr>

<tr><th valign=top>"PIE"</th><td>
Draw histogram as a Pie Chart.
</td></tr>

<tr><th valign=top>"*H"</th><td>
Draw histogram with a * at each bin.
</td></tr>

<tr><th valign=top>"LF2"</th><td>
Draw histogram like with option "L" but with a fill area. Note that "L" draws
also a fill area if the hist fill color is set but the fill area corresponds to
the histogram contour.
</td></tr>

</table>

<a name="HP01c"></a><h4><u>Options supported for 2D histograms</u></h4>

<table border=0>

<tr><th valign=top>" "</th><td>
Default (scatter plot).
</td></tr>

<tr><th valign=top>"ARR"</th><td>
Arrow mode. Shows gradient between adjacent cells.
</td></tr>

<tr><th valign=top>"BOX"</th><td>
A box is drawn for each cell with surface proportional to the content's
absolute value. A negative content is marked with a X.
</td></tr>

<tr><th valign=top>"BOX1"</th><td>
A button is drawn for each cell with surface proportional to content's absolute
value. A sunken button is drawn for negative values a raised one for positive.
</td></tr>

<tr><th valign=top>"COL"</th><td>
A box is drawn for each cell with a color scale varying with contents. All the
none empty bins are painted. Empty bins are not painted unless some bins have
a negative content because in that case the null bins might be not empty.
<tt>TProfile2D</tt> histograms are handled differently because, for this type of 2D
histograms, it is possible to know if an empty bin has been filled or not. So even
if all the bins' contents are positive some empty bins might be painted. And vice versa,
if some bins have a negative content some empty bins might be not painted.
</td></tr>

<tr><th valign=top>"COLZ"</th><td>
Same as "COL". In addition the color palette is also drawn.
</td></tr>

<tr><th valign=top>"CANDLE"</th><td>
Draw a candle plot along X axis.
</td></tr>

<tr><th valign=top>"CANDLEX"</th><td>
Same as "CANDLE".
</td></tr>

<tr><th valign=top>"CANDLEY"</th><td>
Draw a candle plot along Y axis.
</td></tr>

<tr><th valign=top>"VIOLIN"</th><td>
Draw a violin plot along X axis.
</td></tr>

<tr><th valign=top>"VIOLINX"</th><td>
Same as "VIOLIN".
</td></tr>

<tr><th valign=top>"VIOLINY"</th><td>
Draw a violin plot along Y axis.
</td></tr>

<tr><th valign=top>"CONT"</th><td>
Draw a contour plot (same as CONT0).
</td></tr>

<tr><th valign=top>"CONT0"</th><td>
Draw a contour plot using surface colors to distinguish contours.
</td></tr>

<tr><th valign=top>"CONT1"</th><td>
Draw a contour plot using line styles to distinguish contours.
</td></tr>

<tr><th valign=top>"CONT2"</th><td>
Draw a contour plot using the same line style for all contours.
</td></tr>

<tr><th valign=top>"CONT3"</th><td>
Draw a contour plot using fill area colors.
</td></tr>

<tr><th valign=top>"CONT4"</th><td>
Draw a contour plot using surface colors (SURF option at theta = 0).
</td></tr>

<tr><th valign=top>"CONT5"</th><td>
(TGraph2D only) Draw a contour plot using Delaunay triangles.
</td></tr>

<tr><th valign=top>"LIST"</th><td>
Generate a list of TGraph objects for each contour.
</td></tr>

<tr><th valign=top>"CYL"</th><td>
Use Cylindrical coordinates. The X coordinate is mapped on the angle and the Y
coordinate on the cylinder length.
</td></tr>

<tr><th valign=top>"POL"</th><td>
Use Polar coordinates. The X coordinate is mapped on the angle and the Y
coordinate on the radius.
</td></tr>

<tr><th valign=top>"SPH"</th><td>
Use Spherical coordinates. The X coordinate is mapped on the latitude and the Y
coordinate on the longitude.
</td></tr>

<tr><th valign=top>"PSR"</th><td>
Use PseudoRapidity/Phi coordinates. The X coordinate is mapped on Phi.
</td></tr>

<tr><th valign=top>"SURF"</th><td>
Draw a surface plot with hidden line removal.
</td></tr>

<tr><th valign=top>"SURF1"</th><td>
Draw a surface plot with hidden surface removal.
</td></tr>

<tr><th valign=top>"SURF2"</th><td>
Draw a surface plot using colors to show the cell contents.
</td></tr>

<tr><th valign=top>"SURF3"</th><td>
Same as SURF with in addition a contour view drawn on the top.
</td></tr>

<tr><th valign=top>"SURF4"</th><td>
Draw a surface using Gouraud shading.
</td></tr>

<tr><th valign=top>"SURF5"</th><td>
Same as SURF3 but only the colored contour is drawn. Used with option CYL, SPH
or PSR it allows to draw colored contours on a sphere, a cylinder or a in
pseudo rapidity space. In cartesian or polar coordinates, option SURF3 is used.
</td></tr>

<tr><th valign=top>"FB"</th><td>
With LEGO or SURFACE, suppress the Front-Box.
</td></tr>

<tr><th valign=top>"BB"</th><td>
With LEGO or SURFACE, suppress the Back-Box.
</td></tr>

<tr><th valign=top>"A"</th><td>
With LEGO or SURFACE, suppress the axis.
</td></tr>

<tr><th valign=top>"SCAT"</th><td>
Draw a scatter-plot (default).
</td></tr>

<tr><th valign=top>"[cutg]"</th><td>
Draw only the sub-range selected by the TCutG named "cutg".
</td></tr>

</table>

<a name="HP01d"></a><h4><u>Options supported for 3D histograms</u></h4>

<table border=0>

<tr><th valign=top>" "</th><td>
Default (scatter plot).
</td></tr>

<tr><th valign=top>"ISO"</th><td>
Draw a Gouraud shaded 3d iso surface through a 3d histogram. It paints one
surface at the value computed as follow:
<tt>SumOfWeights/(NbinsX*NbinsY*NbinsZ)</tt>.
</td></tr>

<tr><th valign=top>"BOX"</th><td>
Draw a for each cell with volume proportional to the content's absolute value.
</td></tr>

<tr><th valign=top>"LEGO"</th><td>
Same as <tt>BOX</tt>.
</td></tr>

</table>

<a name="HP01e"></a><h4><u>Options supported for histograms' stacks (<tt>THStack</tt>)</u></h4>

<table border=0>

<tr><th valign=top>" "</th><td>
Default, the histograms are drawn on top of each other (as lego plots for 2D
histograms).
</td></tr>

<tr><th valign=top>"NOSTACK"</th><td>
Histograms in the stack are all paint in the same pad as if the option
<tt>"SAME"</tt> had been specified.
</td></tr>

<tr><th valign=top>"PADS"</th><td>
The current pad/canvas is subdivided into a number of pads equal to the number
of histograms in the stack and each histogram is paint into a separate pad.
</td></tr>

</table>


<a name="HP02"></a><h3>Setting the Style</h3>


Histograms use the current style (<tt>gStyle</tt>). When one changes the current
style and would like to propagate the changes to the histogram,
<tt>TH1::UseCurrentStyle</tt> should be called. Call <tt>UseCurrentStyle</tt> on
each histogram is needed.
<br>
To force all the histogram to use the current style use:
<pre>
      gROOT->ForceStyle();
</pre>
All the histograms read after this call will use the current style.


<a name="HP03"></a><h3>Setting line, fill, marker, and text attributes</h3>


The histogram classes inherit from the attribute classes:
<tt>TAttLine</tt>, <tt>TAttFill</tt> and <tt>TAttMarker</tt>.
See the description of these classes for the list of options.


<a name="HP04"></a><h3>Setting Tick marks on the histogram axis</h3>


The <tt>TPad::SetTicks</tt> method specifies the type of tick marks on the axis.
If <tt> tx = gPad->GetTickx()</tt> and <tt>ty = gPad->GetTicky()</tt> then:
<pre>
      tx = 1;   tick marks on top side are drawn (inside)
      tx = 2;   tick marks and labels on top side are drawn
      ty = 1;   tick marks on right side are drawn (inside)
      ty = 2;   tick marks and labels on right side are drawn
</pre>
By default only the left Y axis and X bottom axis are drawn
(<tt>tx = ty = 0</tt>)

<p><tt>TPad::SetTicks(tx,ty)</tt> allows to set these options.
See also The <tt>TAxis</tt> functions to set specific axis attributes.

<p>In case multiple color filled histograms are drawn on the same pad, the fill
area may hide the axis tick marks. One can force a redraw of the axis over all
the histograms by calling:
<pre>
      gPad->RedrawAxis();
</pre>


<a name="HP05"></a><h3>Giving titles to the X, Y and Z axis</h3>


<pre>
      h->GetXaxis()->SetTitle("X axis title");
      h->GetYaxis()->SetTitle("Y axis title");
</pre>
The histogram title and the axis titles can be any <tt>TLatex</tt> string.
The titles are part of the persistent histogram.


<a name="HP060"></a><h3>The option "SAME"</h3>


By default, when an histogram is drawn, the current pad is cleared before
drawing. In order to keep the previous drawing and draw on top of it the
option <tt>"SAME"</tt> should be use. The histogram drawn with the option
<tt>"SAME"</tt> uses the coordinates system available in the current pad.
<p>
This option can be used alone or combined with any valid drawing option but
some combinations must be use with care.

<a name="HP060a"></a><h4><u>Limitations</u></h4>
<ul>
<li>It does not work when
combined with the <tt>"LEGO"</tt> and <tt>"SURF"</tt> options unless the
histogram plotted with the option <tt>"SAME"</tt> has <u>exactly</u> the same
ranges on the X, Y and Z axis as the currently drawn histogram. To superimpose
lego plots <a href="#HP26">histograms' stacks</a> should be used.</li>
</ul>

<a name="HP06"></a><h3>Superimposing two histograms with different scales in the same pad</h3>


The following example creates two histograms, the second histogram is the bins
integral of the first one. It shows a procedure to draw the two histograms in
the same pad and it draws the scale of the second histogram using a new vertical
axis on the right side. See also the tutorial <tt>transpad.C</tt> for a variant
of this example.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   // create/fill draw h1
   gStyle->SetOptStat(kFALSE);
   TH1F *h1 = new TH1F("h1","Superimposing two histograms with different scales",100,-3,3);
   Int_t i;
   for (i=0;i<10000;i++) h1->Fill(gRandom->Gaus(0,1));
   h1->Draw();
   c1->Update();

   // create hint1 filled with the bins integral of h1
   TH1F *hint1 = new TH1F("hint1","h1 bins integral",100,-3,3);
   Float_t sum = 0;
   for (i=1;i<=100;i++) {
      sum += h1->GetBinContent(i);
      hint1->SetBinContent(i,sum);
   }

   // scale hint1 to the pad coordinates
   Float_t rightmax = 1.1*hint1->GetMaximum();
   Float_t scale = gPad->GetUymax()/rightmax;
   hint1->SetLineColor(kRed);
   hint1->Scale(scale);
   hint1->Draw("same");

   // draw an axis on the right side
   TGaxis *axis = new TGaxis(gPad->GetUxmax(),gPad->GetUymin(),
   gPad->GetUxmax(), gPad->GetUymax(),0,rightmax,510,"+L");
   axis->SetLineColor(kRed);
   axis->SetTextColor(kRed);
   axis->Draw();
   return c1;
}
End_Macro
Begin_Html


<a name="HP07"></a><h3>Statistics Display</h3>


The type of information shown in the histogram statistics box can be selected
with:
<pre>
      gStyle->SetOptStat(mode);
</pre>
The "<tt>mode</tt>" has up to nine digits that can be set to on (1 or 2), off (0).
<pre>
      mode = ksiourmen  (default = 000001111)
      k = 1;  kurtosis printed
      k = 2;  kurtosis and kurtosis error printed
      s = 1;  skewness printed
      s = 2;  skewness and skewness error printed
      i = 1;  integral of bins printed
      i = 2;  integral of bins with option "width" printed
      o = 1;  number of overflows printed
      u = 1;  number of underflows printed
      r = 1;  rms printed
      r = 2;  rms and rms error printed
      m = 1;  mean value printed
      m = 2;  mean and mean error values printed
      e = 1;  number of entries printed
      n = 1;  name of histogram is printed
</pre>
For example:
<pre>
      gStyle->SetOptStat(11);
</pre>
displays only the name of histogram and the number of entries, whereas:
<pre>
      gStyle->SetOptStat(1101);
</pre>
displays the name of histogram, mean value and RMS.

<p><b>WARNING 1:</b> never do:
<pre>
      <s>gStyle->SetOptStat(0001111);</s>
</pre>
but instead do:
<pre>
      gStyle->SetOptStat(1111);
</pre>
because <tt>0001111</tt> will be taken as an octal number!

<p><b>WARNING 2:</b> for backward compatibility with older versions
<pre>
      gStyle->SetOptStat(1);
</pre>
is taken as:
<pre>
      gStyle->SetOptStat(1111)
</pre>
To print only the name of the histogram do:
<pre>
      gStyle->SetOptStat(1000000001);
</pre>
<b>NOTE</b> that in case of 2D histograms, when selecting only underflow
(10000) or overflow (100000), the statistics box will show all combinations
of underflow/overflows and not just one single number.

<p>The parameter mode can be any combination of the letters
<tt>kKsSiIourRmMen</tt>
<pre>
      k :  kurtosis printed
      K :  kurtosis and kurtosis error printed
      s :  skewness printed
      S :  skewness and skewness error printed
      i :  integral of bins printed
      I :  integral of bins with option "width" printed
      o :  number of overflows printed
      u :  number of underflows printed
      r :  rms printed
      R :  rms and rms error printed
      m :  mean value printed
      M :  mean value mean error values printed
      e :  number of entries printed
      n :  name of histogram is printed
</pre>
For example, to print only name of histogram and number of entries do:
<pre>
      gStyle->SetOptStat("ne");
</pre>
To print only the name of the histogram do:
<pre>
      gStyle->SetOptStat("n");
</pre>
The default value is:
<pre>
      gStyle->SetOptStat("nemr");
</pre>

<p>When a histogram is painted, a <tt>TPaveStats</tt> object is created and added
to the list of functions of the histogram. If a <tt>TPaveStats</tt> object
already exists in the histogram list of functions, the existing object is just
updated with the current histogram parameters.

<p>Once a histogram is painted, the statistics box can be accessed using
<tt>h->FindObject("stats")</tt>. In the command line it is enough to do:
<pre>
      Root > h->Draw()
      Root > TPaveStats *st = (TPaveStats*)h->FindObject("stats")
</pre>
because after <tt>h->Draw()</tt> the histogram is automatically painted. But
in a script file the painting should be forced using <tt>gPad->Update()</tt>
in order to make sure the statistics box is created:
<pre>
      h->Draw();
      gPad->Update();
      TPaveStats *st = (TPaveStats*)h->FindObject("stats");
</pre>

<p>Without <tt>gPad->Update()</tt> the line <tt>h->FindObject("stats")</tt>
returns a null pointer.

<p>When a histogram is drawn with the option "<tt>SAME</tt>", the statistics box
is not drawn. To force the statistics box drawing with the option
"<tt>SAME</tt>", the option "<tt>SAMES</tt>" must be used.
If the new statistics box hides the previous statistics box, one can change
its position with these lines ("<tt>h</tt>" being the pointer to the histogram):
<pre>
      Root > TPaveStats *st = (TPaveStats*)h->FindObject("stats")
      Root > st->SetX1NDC(newx1); //new x start position
      Root > st->SetX2NDC(newx2); //new x end position
</pre>
To change the type of information for an histogram with an existing
<tt>TPaveStats</tt> one should do:
<pre>
      st->SetOptStat(mode);
</pre>
Where "<tt>mode</tt>" has the same meaning than when calling
<tt>gStyle->SetOptStat(mode)</tt> (see above).

<p>One can delete the statistics box for a histogram <tt>TH1* h</tt> with:
<pre>
      h->SetStats(0)
</pre>
and activate it again with:
<pre>
      h->SetStats(1).
</pre>

<p>Labels used in the statistics box ("Mean", "RMS", ...) can be changed from
<a href="http://root.cern.ch/download/doc/primer/ROOTPrimer.html#configure-root-at-start-up">$ROOTSYS/etc/system.rootrc</a> or
<a href="http://root.cern.ch/download/doc/primer/ROOTPrimer.html#configure-root-at-start-up">.rootrc</a>
(look for the string <tt>"Hist.Stats."</tt>).
</p>


<a name="HP08"></a><h3>Fit Statistics</h3>


The type of information about fit parameters printed in the histogram statistics
box can be selected via the parameter mode. The parameter mode can be
<tt>= pcev</tt>  (default <tt>= 0111</tt>)
<pre>
      p = 1;  print Probability
      c = 1;  print Chisquare/Number of degrees of freedom
      e = 1;  print errors (if e=1, v must be 1)
      v = 1;  print name/values of parameters
</pre>
Example:
<pre>
      gStyle->SetOptFit(1011);
</pre>
print fit probability, parameter names/values and errors.
<ol>
<li> When <tt>"v" = 1</tt> is specified, only the non-fixed parameters are
     shown.
<li> When <tt>"v" = 2</tt> all parameters are shown.
</ol>
Note: <tt>gStyle->SetOptFit(1)</tt> means "default value", so it is equivalent
to <tt>gStyle->SetOptFit(111)</tt>


<a name="HP09"></a><h3>The error bars options</h3>


<table border=0>

<tr><th valign=top>"E"</th><td>
Default. Shows only the error bars, not a marker.
</td></tr>

<tr><th valign=top>"E1"</th><td>
Small lines are drawn at the end of the error bars.
</td></tr>

<tr><th valign=top>"E2"</th><td>
Error rectangles are drawn.
</td></tr>

<tr><th valign=top>"E3"</th><td>
A filled area is drawn through the end points of the vertical error bars.
</td></tr>

<tr><th valign=top>"E4"</th><td>
A smoothed filled area is drawn through the end points of the vertical error
bars.
</td></tr>

<tr><th valign=top>"E0"</th><td>
Draw also bins with null contents.
</td></tr>

</table>

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH1F *he = new TH1F("he","Distribution drawn with error bars (option E1)  ",100,-3,3);
   Int_t i;
   for (i=0;i<10000;i++) he->Fill(gRandom->Gaus(0,1));
   gStyle->SetEndErrorSize(3);
   gStyle->SetErrorX(1.);
   he->SetMarkerStyle(20);
   he->Draw("E1");
   return c1;
}
End_Macro
Begin_Html

<p>The options "E3" and "E4" draw an error band through the end points of the
vertical error bars. With "E4" the error band is smoothed. Because of the
smoothing algorithm used some artefacts may appear at the end of the band
like in the following example. In such cases "E3" should be used instead
of "E4".

End_Html
Begin_Macro(source)
{
   TCanvas *ce4 = new TCanvas("ce4","ce4",600,400);
   ce4->Divide(2,1);
   TH1F *he4 = new TH1F("he4","Distribution drawn with option E4",100,-3,3);
   Int_t i;
   for (i=0;i<10000;i++) he4->Fill(gRandom->Gaus(0,1));
   he4->SetFillColor(kRed);
   he4->GetXaxis()->SetRange(40,48);
   ce4->cd(1);
   he4->Draw("E4");
   ce4->cd(2);
   TH1F *he3 = (TH1F*)he4->DrawClone("E3");
   he3->SetTitle("Distribution drawn option E3");
   return ce4;
}
End_Macro
Begin_Html

<p>2D histograms can be drawn with error bars as shown is the following example:

End_Html
Begin_Macro(source)
{
   TCanvas *c2e = new TCanvas("c2e","c2e",600,400);
   TH2F *h2e = new TH2F("h2e","TH2 drawn with option E",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      h2e->Fill(px,5*py);
   }
   h2e->Draw("E");
   return c2e;
}
End_Macro
Begin_Html


<a name="HP100"></a><h3>The bar chart option</h3>


The option "B" allows to draw simple vertical bar charts.
The bar width is controlled with <tt>TH1::SetBarWidth()</tt>,
and the bar offset wihtin the bin, with <tt>TH1::SetBarOffset()</tt>.
These two settings are useful to draw several histograms on the
same plot as shown in the following example:

End_Html
Begin_Macro(source)
{
   int i;
   const Int_t nx = 8;
   char *os_X[nx]   = {"8","32","128","512","2048","8192","32768","131072"};
   float d_35_0[nx] = {0.75, -3.30, -0.92, 0.10, 0.08, -1.69, -1.29, -2.37};
   float d_35_1[nx] = {1.01, -3.02, -0.65, 0.37, 0.34, -1.42, -1.02, -2.10};

   TCanvas *cb = new TCanvas("cb","cb",600,400);
   cb->SetGrid();

   gStyle->SetHistMinimumZero();

   TH1F *h1b = new TH1F("h1b","Option B example",nx,0,nx);
   h1b->SetFillColor(4);
   h1b->SetBarWidth(0.4);
   h1b->SetBarOffset(0.1);
   h1b->SetStats(0);
   h1b->SetMinimum(-5);
   h1b->SetMaximum(5);

   for (i=1; i<=nx; i++) {
      h1b->Fill(os_X[i-1], d_35_0[i-1]);
      h1b->GetXaxis()->SetBinLabel(i,os_X[i-1]);
   }

   h1b->Draw("b");

   TH1F *h2b = new TH1F("h2b","h2b",nx,0,nx);
   h2b->SetFillColor(38);
   h2b->SetBarWidth(0.4);
   h2b->SetBarOffset(0.5);
   h2b->SetStats(0);
   for (i=1;i<=nx;i++) h2b->Fill(os_X[i-1], d_35_1[i-1]);

   h2b->Draw("b same");

   return cb;
}
End_Macro
Begin_Html


<a name="HP10"></a><h3>The "BAR" and "HBAR" options</h3>


When the option "bar" or "hbar" is specified, a bar chart is drawn. A vertical
bar-chart is drawn with the options <tt>"bar"</tt>, <tt>"bar0"</tt>,
<tt>"bar1"</tt>, <tt>"bar2"</tt>, <tt>"bar3"</tt>, <tt>"bar4"</tt>.
An horizontal bar-chart is drawn with the options <tt>"hbar"</tt>,
<tt>"hbar0"</tt>, <tt>"hbar1"</tt>, <tt>"hbar2"</tt></tt>, <tt>"hbar3"</tt>,
<tt>"hbar4"</tt>.
<ul>
<li> The bar is filled with the histogram fill color.
<li> The left side of the bar is drawn with a light fill color.
<li> The right side of the bar is drawn with a dark fill color.
<li> The percentage of the bar drawn with either the light or dark color is:
<ul>
<li>    0% for option "(h)bar" or "(h)bar0"
<li>   10% for option "(h)bar1"
<li>   20% for option "(h)bar2"
<li>   30% for option "(h)bar3"
<li>   40% for option "(h)bar4"
</ul>
</ul>

End_Html
Begin_Macro(source)
../../../tutorials/hist/hbars.C
End_Macro
Begin_Html

<p>To control the bar width (default is the bin width) <tt>TH1::SetBarWidth()</tt>
should be used.
<br>
To control the bar offset (default is 0) <tt>TH1::SetBarOffset()</tt> should
be used.
<br>
These two parameters are useful when several histograms are plotted using
the option <tt>SAME</tt>. They allow to plot the histograms next to each other.


<a name="HP11"></a><h3>The SCATter plot option (default for 2D histograms)</h3>


For each cell (i,j) a number of points proportional to the cell content is
drawn. A maximum of <tt>kNMAX</tt> points per cell is drawn. If the maximum is above
<tt>kNMAX</tt> contents are normalized to <tt>kNMAX</tt> (<tt>kNMAX=2000</tt>).
If option is of the form <tt>"scat=ff"</tt>, (eg <tt>scat=1.8</tt>,
<tt>scat=1e-3</tt>), then <tt>ff</tt> is used as a scale factor to compute the
number of dots. <tt>"scat=1"</tt> is the default.
<p>
By default the scatter plot is painted with a "dot marker" which not scalable
(see the <a href="http://root.cern.ch/root/html/TAttMarker.html#M3">TAttMarker
documentation</a>). To change the marker size, a scalable marker type should be
used. For instance a circle (marker style 20).


End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hscat = new TH2F("hscat","Option SCATter example (default for 2D histograms)  ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hscat->Fill(px,5*py);
      hscat->Fill(3+0.5*px,2*py-10.);
   }
   hscat->Draw("scat=0.5");
   return c1;
}
End_Macro
Begin_Html


<a name="HP12"></a><h3>The ARRow option</h3>


Shows gradient between adjacent cells. For each cell (i,j) an arrow is drawn
The orientation of the arrow follows the cell gradient.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *harr = new TH2F("harr","Option ARRow example",20,-4,4,20,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      harr->Fill(px,5*py);
      harr->Fill(3+0.5*px,2*py-10.,0.1);
   }
   harr->Draw("ARR");
   return c1;
}
End_Macro
Begin_Html


<a name="HP13"></a><h3>The BOX option</h3>


For each cell (i,j) a box is drawn. The size (surface) of the box is
proportional to the absolute value of the cell content.
The cells with a negative content draw with a <tt>X</tt> on top of the boxes.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   hbox  = new TH2F("hbox","Option BOX example",3,0,3,3,0,3);
   hbox->SetFillColor(42);
   hbox->Fill(0.5, 0.5,  1.);
   hbox->Fill(0.5, 1.5,  4.);
   hbox->Fill(0.5, 2.5,  3.);
   hbox->Fill(1.5, 0.5,  2.);
   hbox->Fill(1.5, 1.5, 12.);
   hbox->Fill(1.5, 2.5, -6.);
   hbox->Fill(2.5, 0.5, -4.);
   hbox->Fill(2.5, 1.5,  6.);
   hbox->Fill(2.5, 2.5,  0.5);
   hbox->Draw("BOX");
   return c1;
}
End_Macro
Begin_Html

<p>With option <tt>"BOX1"</tt> a button is drawn for each cell with surface
proportional to content's absolute value. A sunken button is drawn for
negative values a raised one for positive.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   hbox1  = new TH2F("hbox1","Option BOX1 example",3,0,3,3,0,3);
   hbox1->SetFillColor(42);
   hbox1->Fill(0.5, 0.5,  1.);
   hbox1->Fill(0.5, 1.5,  4.);
   hbox1->Fill(0.5, 2.5,  3.);
   hbox1->Fill(1.5, 0.5,  2.);
   hbox1->Fill(1.5, 1.5, 12.);
   hbox1->Fill(1.5, 2.5, -6.);
   hbox1->Fill(2.5, 0.5, -4.);
   hbox1->Fill(2.5, 1.5,  6.);
   hbox1->Fill(2.5, 2.5,  0.5);
   hbox1->Draw("BOX1");
   return c1;
}
End_Macro
Begin_Html

<p>When the option <tt>"SAME"</tt> (or "SAMES") is used with the option <tt>"BOX"</tt>,
the boxes' sizes are computing taking the previous plots into account. The range
along the Z axis is imposed by the first plot (the one without option
<tt>"SAME"</tt>); therefore the order in which the plots are done is relevant.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hb1 = new TH2F("hb1","Example of BOX plots with option SAME ",40,-3,3,40,-3,3);
   TH2F *hb2 = new TH2F("hb2","hb2",40,-3,3,40,-3,3);
   TH2F *hb3 = new TH2F("hb3","hb3",40,-3,3,40,-3,3);
   TH2F *hb4 = new TH2F("hb4","hb4",40,-3,3,40,-3,3);
   for (Int_t i=0;i<1000;i++) {
      double x,y;
      gRandom->Rannor(x,y);
      if (x>0 && y>0) hb1->Fill(x,y,4);
      if (x<0 && y<0) hb2->Fill(x,y,3);
      if (x>0 && y<0) hb3->Fill(x,y,2);
      if (x<0 && y>0) hb4->Fill(x,y,1);
   }
   hb1->SetFillColor(1);
   hb2->SetFillColor(2);
   hb3->SetFillColor(3);
   hb4->SetFillColor(4);
   hb1->Draw("box");
   hb2->Draw("box same");
   hb3->Draw("box same");
   hb4->Draw("box same");
   return c1;
}
End_Macro
Begin_Html


<a name="HP14"></a><h3>The COLor option</h3>


For each cell (i,j) a box is drawn with a color proportional to the cell
content.

<p>The color table used is defined in the current style.

<p>If the histogram's minimum and maximum are the same (flat histogram), the
mapping on colors is not possible, therefore nothing is painted. To paint a
flat histogram it is enough to set the histogram minimum
(<tt>TH1::SetMinimum()</tt>) different from the bins' content.

<p>The default number of color levels used to paint the cells is 20.
It can be changed with <tt>TH1::SetContour()</tt> or
<tt>TStyle::SetNumberContours()</tt>. The higher this number is, the smoother
is the color change between cells.

<p>The color palette in TStyle can be modified via <tt>gStyle->SetPalette()</tt>.

<p>All the none empty bins are painted. Empty bins are not painted unless
some bins have a negative content because in that case the null bins
might be not empty.

<p><tt>TProfile2D</tt> histograms are handled differently because, for this type of 2D
histograms, it is possible to know if an empty bin has been filled or not. So even
if all the bins' contents are positive some empty bins might be painted. And vice versa,
if some bins have a negative content some empty bins might be not painted.

<p>Combined with the option <tt>"COL"</tt>, the option <tt>"Z"</tt> allows to
display the color palette defined by <tt>gStyle->SetPalette()</tt>.

<p>In the following example, the histogram has only positive bins; the empty
bins (containing 0) <u>are not drawn</u>.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hcol1 = new TH2F("hcol1","Option COLor example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hcol1->Fill(px,5*py);
   }
   gStyle->SetPalette(1);
   hcol1->Draw("COLZ");
   return c1;
}
End_Macro
Begin_Html

<p>In the following example, the histogram has some negative bins; the empty
bins (containing 0) <u>are drawn</u>.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hcol2 = new TH2F("hcol2","Option COLor example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hcol2->Fill(px,5*py);
   }
   hcol2->Fill(0.,0.,-200.);
   gStyle->SetPalette(1);
   hcol2->Draw("COLZ");
   return c1;
}
End_Macro
Begin_Html


<a name="HP140"></a><h3>The CANDLE option</h3>


<a href="http://en.wikipedia.org/wiki/Box_plot">A Candle plot</a> (also known as
a "box-and whisker plot" or simply "box plot") is a convenient way to describe
graphically a data distribution (D) with only the five numbers. It was invented
in 1977 by John Tukey.
<p>
With the option CANDLEX five numbers are:
<ol>
<li> The minimum value of the distribution D (bottom dashed line).
<li> The lower quartile (Q1): 25% of the data points in D are less than
     Q1 (bottom of the box).
<li> The median (M): 50% of the data points in D are less than M
     (thick line segment inside the box).
<li> The upper quartile (Q3): 75% of the data points in D are less
     than Q3 (top of the box).
<li> The maximum value of the distribution D (top dashed line).
</ol>

The mean value of the distribution D is also represented as a circle.
<p>
In this implementation a TH2 is considered as a collection of TH1 along
X (option <tt>CANDLE</tt> or <tt>CANDLEX</tt>) or Y (option <tt>CANDLEY</tt>).
Each TH1 is represented as a candle plot.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hcandle = new TH2F("hcandle","Option CANDLE example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hcandle->Fill(px,5*py);
   }
   hcandle->SetMarkerSize(0.5);
   hcandle->Draw("CANDLE");
   return c1;
}
End_Macro
Begin_Html

<a name="HP141"></a><h3>The VIOLIN option</h3>


<a href="http://en.wikipedia.org/wiki/Violin_plot">A violin plot</a> is a box plot
that also encodes the pdf information at each point.

<p>
Quartiles and mean are also represented at each point, with a marker
and two lines.
</p>
<p>
In this implementation a TH2 is considered as a collection of TH1 along
X (option <tt>VIOLIN</tt> or <tt>VIOLINX</tt>) or Y (option <tt>VIOLINY</tt>).
</p>
<p>
A solid fill style is recommended for this plot (as opposed to a hollow or
hashed style).
</p>

End_Html
Begin_Macro(source)
{
    TCanvas *c1 = new TCanvas("c1","c1",600,400);
    Int_t nx(6), ny(40);
    Double_t xmin(0.0), xmax(+6.0), ymin(0.0), ymax(+4.0);
    TH2F* hviolin = new TH2F("hviolin", "Option VIOLIN example", nx, xmin, xmax, ny, ymin, ymax);
    TF1 f1("f1", "gaus", +0,0 +4.0);
    Double_t x,y;
    for (Int_t iBin=1; iBin<hviolin->GetNbinsX(); ++iBin) {
        Double_t xc = hviolin->GetXaxis()->GetBinCenter(iBin);
        f1.SetParameters(1, 2.0+TMath::Sin(1.0+xc), 0.2+0.1*(xc-xmin)/xmax);
        for(Int_t i=0; i<10000; ++i){
            x = xc;
            y = f1.GetRandom();
            hviolin->Fill(x, y);
        }
    }
    hviolin->SetFillColor(kGray);
    hviolin->SetMarkerStyle(20);
    hviolin->SetMarkerSize(0.5);
    hviolin->Draw("VIOLIN");
    c1->Update();
    return c1;
}
End_Macro
Begin_Html


<a name="HP15"></a><h3>The TEXT and TEXTnn Option</h3>


For each bin the content is printed. The text attributes are:
<ul>
<li> text font = current TStyle font (<tt>gStyle->SetTextFont()</tt>).
<li> text size = 0.02*padheight*markersize (if <tt>h</tt> is the histogram drawn
     with the option <tt>"TEXT"</tt> the marker size can be changed with
     <tt>h->SetMarkerSize(markersize)</tt>).
<li> text color = marker color.
</ul>
By default the format <tt>"g"</tt> is used. This format can be redefined
by calling <tt>gStyle->SetPaintTextFormat()</tt>.

<p>It is also possible to use <tt>"TEXTnn"</tt> in order to draw the text with
the angle <tt>nn</tt> (<tt>0 < nn < 90</tt>).

<p>For 2D histograms the text is plotted in the center of each non empty cells.
It is possible to plot empty cells by calling gStyle->SetHistMinimumZero().
For 1D histogram the text is plotted at a y position equal to the bin content.

<p>For 2D histograms when the option "E" (errors) is combined with the option
text ("TEXTE"), the error for each bin is also printed.

End_Html
Begin_Macro(source)
{
   TCanvas *c01 = new TCanvas("c01","c01",700,400);
   c01->Divide(2,1);
   TH1F *htext1 = new TH1F("htext1","Option TEXT on 1D histograms ",10,-4,4);
   TH2F *htext2 = new TH2F("htext2","Option TEXT on 2D histograms ",10,-4,4,10,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
        htext1->Fill(px,0.1);
      htext2->Fill(px,5*py,0.1);
   }
   gStyle->SetPaintTextFormat("4.1f m");
   htext2->SetMarkerSize(1.8);
   c01->cd(1);
   htext2->Draw("TEXT45");
   c01->cd(2);
   htext1->Draw();
   htext1->Draw("TEXT0 SAME");
   return c01;
}
End_Macro
Begin_Html

<p>In the case of profile histograms it is possible to print the number
of entries instead of the bin content. It is enough to combine the
option "E" (for entries) with the option "TEXT".

End_Html
Begin_Macro(source)
{
   TCanvas *c02 = new TCanvas("c02","c02",700,400);
   c02->Divide(2,1);
   gStyle->SetPaintTextFormat("g");

   TProfile *profile = new TProfile("profile","profile",10,0,10);
   profile->SetMarkerSize(2.2);
   profile->Fill(0.5,1);
   profile->Fill(1.5,2);
   profile->Fill(2.5,3);
   profile->Fill(3.5,4);
   profile->Fill(4.5,5);
   profile->Fill(5.5,5);
   profile->Fill(6.5,4);
   profile->Fill(7.5,3);
   profile->Fill(8.5,2);
   profile->Fill(9.5,1);
   c02->cd(1); profile->Draw("HIST TEXT0");
   c02->cd(2); profile->Draw("HIST TEXT0E");

   return c02;
}
End_Macro
Begin_Html

<a name="HP16"></a><h3>The CONTour options</h3>


The following contour options are supported:

<table border=0>

<tr><th valign=top>"CONT"</th><td>
Draw a contour plot (same as CONT0).
</td></tr>

<tr><th valign=top>"CONT0"</th><td>
Draw a contour plot using surface colors to distinguish contours.
</td></tr>

<tr><th valign=top>"CONT1"</th><td>
Draw a contour plot using the line colors to distinguish contours.
</td></tr>

<tr><th valign=top>"CONT2"</th><td>
Draw a contour plot using the line styles to distinguish contours.
</td></tr>

<tr><th valign=top>"CONT3"</th><td>
Draw a contour plot solid lines for all contours.
</td></tr>

<tr><th valign=top>"CONT4"</th><td>
Draw a contour plot using surface colors (<tt>"SURF"</tt> option at theta = 0).
</td></tr>

<tr><th valign=top>"CONT5"</th><td>
Draw a contour plot using Delaunay triangles.
</td></tr>

</table>

The following example shows a 2D histogram plotted with the option
<tt>"CONTZ"</tt>. The option <tt>"CONT"</tt> draws a contour plot using surface
colors to distinguish contours.  Combined with the option <tt>"CONT"</tt> (or
<tt>"CONT0"</tt>), the option <tt>"Z"</tt> allows to display the color palette
defined by <tt>gStyle->SetPalette()</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hcontz = new TH2F("hcontz","Option CONTZ example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hcontz->Fill(px-1,5*py);
      hcontz->Fill(2+0.5*px,2*py-10.,0.1);
   }
   gStyle->SetPalette(1);
   hcontz->Draw("CONTZ");
   return c1;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"CONT1Z"</tt>. The option <tt>"CONT1"</tt> draws a contour plot using the
line colors to distinguish contours. Combined with the option <tt>"CONT1"</tt>,
the option <tt>"Z"</tt> allows to display the color palette defined by
<tt>gStyle->SetPalette()</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hcont1 = new TH2F("hcont1","Option CONT1Z example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hcont1->Fill(px-1,5*py);
      hcont1->Fill(2+0.5*px,2*py-10.,0.1);
   }
   gStyle->SetPalette(1);
   hcont1->Draw("CONT1Z");
   return c1;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"CONT2"</tt>. The option <tt>"CONT2"</tt> draws a contour plot using the
line styles to distinguish contours.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hcont2 = new TH2F("hcont2","Option CONT2 example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hcont2->Fill(px-1,5*py);
      hcont2->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hcont2->Draw("CONT2");
   return c1;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"CONT3"</tt>. The option <tt>"CONT3"</tt> draws contour plot solid lines for
all contours.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hcont3 = new TH2F("hcont3","Option CONT3 example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hcont3->Fill(px-1,5*py);
      hcont3->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hcont3->Draw("CONT3");
   return c1;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"CONT4"</tt>. The option <tt>"CONT4"</tt> draws a contour plot using surface
colors to distinguish contours (<tt>"SURF"</tt> option at theta = 0). Combined
with the option <tt>"CONT"</tt> (or <tt>"CONT0"</tt>), the option <tt>"Z"</tt>
allows to display the color palette defined by <tt>gStyle->SetPalette()</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c1 = new TCanvas("c1","c1",600,400);
   TH2F *hcont4 = new TH2F("hcont4","Option CONT4Z example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hcont4->Fill(px-1,5*py);
      hcont4->Fill(2+0.5*px,2*py-10.,0.1);
   }
   gStyle->SetPalette(1);
   hcont4->Draw("CONT4Z");
   return c1;
}
End_Macro
Begin_Html

<p>The default number of contour levels is 20 equidistant levels and can be changed
with <tt>TH1::SetContour()</tt> or <tt>TStyle::SetNumberContours()</tt>.

<a name="HP16a"></a><h4><u>The LIST option</u></h4>

<p>When option <tt>"LIST"</tt> is specified together with option
<tt>"CONT"</tt>, the points used to draw the contours are saved in
<tt>TGraph</tt> objects:
<pre>
      h->Draw("CONT LIST");
      gPad->Update();
</pre>
The contour are saved in <tt>TGraph</tt> objects once the pad is painted.
Therefore to use this functionnality in a macro, <tt>gPad->Update()</tt>
should be performed after the histogram drawing. Once the list is
built, the contours are accessible in the following way:
<pre>
      TObjArray *contours = gROOT->GetListOfSpecials()->FindObject("contours")
      Int_t ncontours     = contours->GetSize();
      TList *list         = (TList*)contours->At(i);
</pre>
Where <tt>i</tt> is a contour number, and list contains a list of
<tt>TGraph</tt> objects.
For one given contour, more than one disjoint polyline may be generated.
The number of TGraphs per contour is given by:
<pre>
      list->GetSize();
</pre>
To access the first graph in the list one should do:
<pre>
      TGraph *gr1 = (TGraph*)list->First();
</pre>

The following example shows how to use this functionality.

End_Html
Begin_Macro(source)
../../../tutorials/hist/ContourList.C
End_Macro
Begin_Html

<p>The following options select the <tt>"CONT4"</tt> option and are useful for
sky maps or exposure maps.

<table border=0>

<tr><th valign=top>"AITOFF"</th><td>
Draw a contour via an AITOFF projection.
</td></tr>

<tr><th valign=top>"MERCATOR"</th><td>
Draw a contour via an Mercator projection.
</td></tr>

<tr><th valign=top>"SINUSOIDAL"</th><td>
Draw a contour via an Sinusoidal projection.
</td></tr>

<tr><th valign=top>"PARABOLIC"</th><td>
Draw a contour via an Parabolic projection.
</td></tr>

</table>

End_Html
Begin_Macro(source)
../../../tutorials/graphics/earth.C
End_Macro
Begin_Html


<a name="HP17"></a><h3>The LEGO options</h3>


In a lego plot the cell contents are drawn as 3-d boxes. The height of each box
is proportional to the cell content. The lego aspect is control with the
following options:

<table border=0>

<tr><th valign=top>"LEGO" </th><td>
Draw a lego plot using the hidden lines removal technique.
</td></tr>

<tr><th valign=top>"LEGO1"</th><td>
Draw a lego plot using the hidden surface removal technique.
</td></tr>

<tr><th valign=top>"LEGO2"</th><td>
Draw a lego plot using colors to show the cell contents.
</td></tr>

<tr><th valign=top>"LEGO3"</th><td>
Draw a lego plot with hidden surface removal, like LEGO1 but the border lines
of each lego-bar are not drawn.
</td></tr>

<tr><th valign=top>"LEGO4"</th><td>
Draw a lego plot with hidden surface removal, like LEGO1 but without the
shadow effect on each lego-bar.
</td></tr>

<tr><th valign=top>"0"</th><td>
When used with any LEGO option, the empty bins are not drawn.
</td></tr>

</table>
See the limitations with <a href="#HP060a">the option "SAME"</a>.

<p>Line attributes can be used in lego plots to change the edges' style.

<p>The following example shows a 2D histogram plotted with the option
<tt>"LEGO"</tt>. The option <tt>"LEGO"</tt> draws a lego plot using the hidden
lines removal technique.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hlego = new TH2F("hlego","Option LEGO example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hlego->Fill(px-1,5*py);
      hlego->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hlego->Draw("LEGO");
   return c2;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"LEGO1"</tt>. The option <tt>"LEGO1"</tt> draws a lego plot using the
hidden surface removal technique. Combined with any <tt>"LEGOn"</tt> option, the
option <tt>"0"</tt> allows to not drawn the empty bins.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hlego1 = new TH2F("hlego1","Option LEGO1 example (with option 0)  ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hlego1->Fill(px-1,5*py);
      hlego1->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hlego1->SetFillColor(kYellow);
   hlego1->Draw("LEGO1 0");
   return c2;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"LEGO3"</tt>. Like the option <tt>"LEGO1"</tt>, the option <tt>"LEGO3"</tt>
draws a lego plot using the hidden surface removal technique but doesn't draw
the border lines of each individual lego-bar. This is very useful for histograms
having many bins. With such histograms the option <tt>"LEGO1"</tt> gives a black
image because of the border lines. This option also works with stacked legos.
End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hlego3 = new TH2F("hlego3","Option LEGO3 example",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hlego3->Fill(px-1,5*py);
      hlego3->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hlego3->SetFillColor(kRed);
   hlego3->Draw("LEGO3");
   return c2;
 }
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"LEGO2"</tt>. The option <tt>"LEGO2"</tt> draws a lego plot using colors to
show the cell contents.  Combined with the option <tt>"LEGO2"</tt>, the option
<tt>"Z"</tt> allows to display the color palette defined by
<tt>gStyle->SetPalette()</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hlego2 = new TH2F("hlego2","Option LEGO2Z example ",40,-4,4,40,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hlego2->Fill(px-1,5*py);
      hlego2->Fill(2+0.5*px,2*py-10.,0.1);
   }
   gStyle->SetPalette(1);
   hlego2->Draw("LEGO2Z");
   return c2;
}
End_Macro
Begin_Html



<a name="HP18"></a><h3>The "SURFace" options</h3>


In a surface plot, cell contents are represented as a mesh.
The height of the mesh is proportional to the cell content.

<table border=0>

<tr><th valign=top>"SURF"</th><td>
Draw a surface plot using the hidden line removal technique.
</td></tr>

<tr><th valign=top>"SURF1"</th><td>
Draw a surface plot using the hidden surface removal technique.
</td></tr>

<tr><th valign=top>"SURF2"</th><td>
Draw a surface plot using colors to show the cell contents.
</td></tr>

<tr><th valign=top>"SURF3"</th><td>
Same as <tt>SURF</tt> with an additionial filled contour plot on top.
</td></tr>

<tr><th valign=top>"SURF4"</th><td>
Draw a surface using the Gouraud shading technique.
</td></tr>

<tr><th valign=top>"SURF5"</th><td>
Used with one of the options CYL, PSR and CYL this option allows to draw a
a filled contour plot.
</td></tr>

<tr><th valign=top>"SURF6"</th><td>
This option should not be used directly. It is used internally when the
CONT is used with option the option SAME on a 3D plot.
</td></tr>

<tr><th valign=top>"SURF7"</th><td>
Same as <tt>SURF2</tt> with an additionial line contour plot on top.
</td></tr>

</table>

See the limitations with <a href="#HP060a">the option "SAME"</a>.
<p>
The following example shows a 2D histogram plotted with the option
<tt>"SURF"</tt>. The option <tt>"SURF"</tt> draws a lego plot using the hidden
lines removal technique.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hsurf = new TH2F("hsurf","Option SURF example ",30,-4,4,30,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hsurf->Fill(px-1,5*py);
      hsurf->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hsurf->Draw("SURF");
   return c2;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"SURF1"</tt>. The option <tt>"SURF1"</tt> draws a surface plot using the
hidden surface removal technique.  Combined with the option <tt>"SURF1"</tt>,
the option <tt>"Z"</tt> allows to display the color palette defined by
<tt>gStyle->SetPalette()</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hsurf1 = new TH2F("hsurf1","Option SURF1 example ",30,-4,4,30,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hsurf1->Fill(px-1,5*py);
      hsurf1->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hsurf1->Draw("SURF1");
   return c2;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"SURF2"</tt>. The option <tt>"SURF2"</tt> draws a surface plot using colors
to show the cell contents. Combined with the option <tt>"SURF2"</tt>, the option
<tt>"Z"</tt> allows to display the color palette defined by
<tt>gStyle->SetPalette()</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hsurf2 = new TH2F("hsurf2","Option SURF2 example ",30,-4,4,30,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hsurf2->Fill(px-1,5*py);
      hsurf2->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hsurf2->Draw("SURF2");
   return c2;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"SURF3"</tt>. The option <tt>"SURF3"</tt> draws a surface plot using the
hidden line removal technique with, in addition, a filled contour view drawn on the
top.  Combined with the option <tt>"SURF3"</tt>, the option <tt>"Z"</tt> allows
to display the color palette defined by <tt>gStyle->SetPalette()</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hsurf3 = new TH2F("hsurf3","Option SURF3 example ",30,-4,4,30,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hsurf3->Fill(px-1,5*py);
      hsurf3->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hsurf3->Draw("SURF3");
   return c2;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"SURF4"</tt>. The option <tt>"SURF4"</tt> draws a surface using the Gouraud
shading technique.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hsurf4 = new TH2F("hsurf4","Option SURF4 example ",30,-4,4,30,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hsurf4->Fill(px-1,5*py);
      hsurf4->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hsurf4->SetFillColor(kOrange);
   hsurf4->Draw("SURF4");
   return c2;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"SURF5 CYL"</tt>.  Combined with the option <tt>"SURF5"</tt>, the option
<tt>"Z"</tt> allows to display the color palette defined by <tt>gStyle->SetPalette()</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hsurf5 = new TH2F("hsurf4","Option SURF5 example ",30,-4,4,30,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hsurf5->Fill(px-1,5*py);
      hsurf5->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hsurf5->SetFillColor(kOrange);
   hsurf5->Draw("SURF5 CYL");
   return c2;
}
End_Macro
Begin_Html

<p>The following example shows a 2D histogram plotted with the option
<tt>"SURF7"</tt>. The option <tt>"SURF7"</tt> draws a surface plot using the
hidden surfaces removal technique with, in addition, a line contour view drawn on the
top.  Combined with the option <tt>"SURF7"</tt>, the option <tt>"Z"</tt> allows
to display the color palette defined by <tt>gStyle->SetPalette()</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TH2F *hsurf7 = new TH2F("hsurf3","Option SURF7 example ",30,-4,4,30,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hsurf7->Fill(px-1,5*py);
      hsurf7->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hsurf7->Draw("SURF7");
   return c2;
}
End_Macro
Begin_Html

<p>As shown in the following example, when a contour plot is painted on top of a
surface plot using the option <tt>SAME</tt>, the contours appear in 3D on the
surface.

End_Html
Begin_Macro(source)
{
   TCanvas *c20=new TCanvas("c20","c20",600,400);
   int NBins = 50;
   double d = 2;
   TH2F* hsc = new TH2F("hsc", "Surface and contour with option SAME ", NBins, -d, d, NBins, -d, d);
   for (int bx = 1;  bx <= NBins; ++bx) {
      for (int by = 1;  by <= NBins; ++by) {
         double x = hsc->GetXaxis()->GetBinCenter(bx);
         double y = hsc->GetYaxis()->GetBinCenter(by);
         hsc->SetBinContent(bx, by, exp(-x*x)*exp(-y*y));
      }
   }
   gStyle->SetPalette(1);
   hsc->Draw("surf2");
   hsc->Draw("CONT1 SAME");
   return c20;
}
End_Macro
Begin_Html


<a name="HP19"></a><h3>Cylindrical, Polar, Spherical and PseudoRapidity/Phi options</h3>


Legos and surfaces plots are represented by default in Cartesian coordinates.
Combined with any <tt>"LEGOn"</tt> or <tt>"SURFn"</tt> options the following
options allow to draw a lego or a surface in other coordinates systems.

<table border=0>

<tr><th valign=top>"CYL"</th><td>
Use Cylindrical coordinates. The X coordinate is mapped on the angle and the Y
coordinate on the cylinder length.
</td></tr>

<tr><th valign=top>"POL"</th><td>
Use Polar coordinates. The X coordinate is mapped on the angle and the Y
coordinate on the radius.
</td></tr>

<tr><th valign=top>"SPH"</th><td>
Use Spherical coordinates. The X coordinate is mapped on the latitude and the
Y coordinate on the longitude.
</td></tr>

<tr><th valign=top>"PSR"</th><td>
Use PseudoRapidity/Phi coordinates. The X coordinate is mapped on Phi.
</td></tr>

</table>

<b>WARNING:</b> Axis are not drawn with these options.

<p>The following example shows the same histogram as a lego plot is the four
different coordinates systems.

End_Html
Begin_Macro(source)
{
   TCanvas *c3 = new TCanvas("c3","c3",600,400);
   c3->Divide(2,2);
   TH2F *hlcc = new TH2F("hlcc","Cylindrical coordinates",20,-4,4,20,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hlcc->Fill(px-1,5*py);
      hlcc->Fill(2+0.5*px,2*py-10.,0.1);
   }
   hlcc->SetFillColor(kYellow);
   c3->cd(1); hlcc->Draw("LEGO1 CYL");
   c3->cd(2); TH2F *hlpc = (TH2F*) hlcc->DrawClone("LEGO1 POL");
   hlpc->SetTitle("Polar coordinates");
   c3->cd(3); TH2F *hlsc = (TH2F*) hlcc->DrawClone("LEGO1 SPH");
   hlsc->SetTitle("Spherical coordinates");
   c3->cd(4); TH2F *hlprpc = (TH2F*) hlcc->DrawClone("LEGO1 PSR");
   hlprpc->SetTitle("PseudoRapidity/Phi coordinates");
   return c3;
}
End_Macro
Begin_Html

<p>The following example shows the same histogram as a surface plot is the four
different coordinates systems.

End_Html
Begin_Macro(source)
{
   TCanvas *c4 = new TCanvas("c4","c4",600,400);
   c4->Divide(2,2);
   TH2F *hscc = new TH2F("hscc","Cylindrical coordinates",20,-4,4,20,-20,20);
   Float_t px, py;
   for (Int_t i = 0; i < 25000; i++) {
      gRandom->Rannor(px,py);
      hscc->Fill(px-1,5*py);
      hscc->Fill(2+0.5*px,2*py-10.,0.1);
   }
   gStyle->SetPalette(1);
   c4->cd(1); hscc->Draw("SURF1 CYL");
   c4->cd(2); TH2F *hspc = (TH2F*) hscc->DrawClone("SURF1 POL");
   hspc->SetTitle("Polar coordinates");
   c4->cd(3); TH2F *hssc = (TH2F*) hscc->DrawClone("SURF1 SPH");
   hssc->SetTitle("Spherical coordinates");
   c4->cd(4); TH2F *hsprpc = (TH2F*) hscc->DrawClone("SURF1 PSR");
   hsprpc->SetTitle("PseudoRapidity/Phi coordinates");
   return c4;
}
End_Macro
Begin_Html


<a name="HP20"></a><h3>Base line for bar-charts and lego plots</h3>


By default the base line used to draw the boxes for bar-charts and lego plots is
the histogram minimum. It is possible to force this base line to be 0 with the
command:
<pre>
      gStyle->SetHistMinimumZero();
</pre>

End_Html
Begin_Macro(source)
{
   TCanvas *c5 = new TCanvas("c5","c5",700,400);
   c5->Divide(2,1);
   gStyle->SetHistMinimumZero(1);
   TH1F *hz1 = new TH1F("hz1","Bar-chart drawn from 0",20,-3,3);
   TH2F *hz2 = new TH2F("hz2","Lego plot drawn from 0",20,-3,3,20,-3,3);
   Int_t i;
   Double_t x,y;
   hz1->SetFillColor(kBlue);
   hz2->SetFillColor(kBlue);
   for (i=0;i<10000;i++) {
      x = gRandom->Gaus(0,1);
      y = gRandom->Gaus(0,1);
      if (x>0) {
         hz1->Fill(x,1);
         hz2->Fill(x,y,1);
      } else {
         hz1->Fill(x,-1);
         hz2->Fill(x,y,-2);
      }
   }
   c5->cd(1); hz1->Draw("bar2");
   c5->cd(2); hz2->Draw("lego1");
   return c5;
}
End_Macro
Begin_Html

<p>This option also works for horizontal plots. The example given in the section
<a href="http://root.cern.ch/root/html/THistPainter.html#HP100">
"The bar chart option"</a> appears as follow:

End_Html
Begin_Macro(source)
{
   int i;
   const Int_t nx = 8;
   char *os_X[nx]   = {"8","32","128","512","2048","8192","32768","131072"};
   float d_35_0[nx] = {0.75, -3.30, -0.92, 0.10, 0.08, -1.69, -1.29, -2.37};
   float d_35_1[nx] = {1.01, -3.02, -0.65, 0.37, 0.34, -1.42, -1.02, -2.10};

   TCanvas *cbh = new TCanvas("cbh","cbh",400,600);
   cbh->SetGrid();

   gStyle->SetHistMinimumZero();

   TH1F *h1bh = new TH1F("h1bh","Option HBAR centered on 0",nx,0,nx);
   h1bh->SetFillColor(4);
   h1bh->SetBarWidth(0.4);
   h1bh->SetBarOffset(0.1);
   h1bh->SetStats(0);
   h1bh->SetMinimum(-5);
   h1bh->SetMaximum(5);

   for (i=1; i<=nx; i++) {
      h1bh->Fill(os_X[i-1], d_35_0[i-1]);
      h1bh->GetXaxis()->SetBinLabel(i,os_X[i-1]);
   }

   h1bh->Draw("hbar");

   TH1F *h2bh = new TH1F("h2bh","h2bh",nx,0,nx);
   h2bh->SetFillColor(38);
   h2bh->SetBarWidth(0.4);
   h2bh->SetBarOffset(0.5);
   h2bh->SetStats(0);
   for (i=1;i<=nx;i++) h2bh->Fill(os_X[i-1], d_35_1[i-1]);

   h2bh->Draw("hbar same");

   return cbh;
}
End_Macro
Begin_Html


<a name="HP20a"></a><h3>TH2Poly Drawing</h3>


The following options are supported:

<table border=0>

<tr><th valign=top>"SCAT"</th><td>
Draw a scatter plot (default).
</td></tr>

<tr><th valign=top>"COL"</th><td>
Draw a color plot. All the none empty bins are painted. Empty bins are not
painted.
</td></tr>

<tr><th valign=top>"COLZ"</th><td>
Same as "COL". In addition the color palette is also drawn.
</td></tr>

<tr><th valign=top>"TEXT"</th><td>
Draw bin contents as text (format set via <tt>gStyle->SetPaintTextFormat</tt>).
</td></tr>

<tr><th valign=top>"TEXTN"</th><td>
Draw bin names as text.
</td></tr>

<tr><th valign=top>"TEXTnn"</th><td>
Draw bin contents as text at angle nn (0 < nn < 90).
</td></tr>

<tr><th valign=top>"L"</th><td>
Draw the bins boundaries as lines.
The lines attibutes are the TGraphs ones.
</td></tr>

<tr><th valign=top>"P"</th><td>
Draw the bins boundaries as markers.
The markers attibutes are the TGraphs ones.
</td></tr>

<tr><th valign=top>"F"</th><td>
Draw the bins boundaries as filled polygons.
The filled polygons attibutes are the TGraphs ones.
</td></tr>

</table>

<p><a href="http://root.cern.ch/root/html/TH2Poly.html"><tt>TH2Poly</tt></a> can
be drawn as a color plot (option COL). <tt>TH2Poly</tt> bins can have any
shapes. The bins are defined as graphs. The following macro is a very simple
example showing how to book a TH2Poly and draw it.
End_Html
Begin_Macro(source)
{
   TCanvas *ch2p1 = new TCanvas("ch2p1","ch2p1",600,400);
   TH2Poly *h2p = new TH2Poly();
   h2p->SetName("h2poly_name");
   h2p->SetTitle("h2poly_title");
   Double_t px1[] = {0, 5, 6};
   Double_t py1[] = {0, 0, 5};
   Double_t px2[] = {0, -1, -1, 0};
   Double_t py2[] = {0, 0, -1, 3};
   Double_t px3[] = {4, 3, 0, 1, 2.4};
   Double_t py3[] = {4, 3.7, 1, 3.7, 2.5};
   h2p->AddBin(3, px1, py1);
   h2p->AddBin(4, px2, py2);
   h2p->AddBin(5, px3, py3);
   h2p->Fill(0.1, 0.01, 3);
   h2p->Fill(-0.5, -0.5, 7);
   h2p->Fill(-0.7, -0.5, 1);
   h2p->Fill(1, 3, 1.5);
   Double_t fx[] = {0.1, -0.5, -0.7, 1};
   Double_t fy[] = {0.01, -0.5, -0.5, 3};
   Double_t fw[] = {3, 1, 1, 1.5};
   h2p->FillN(4, fx, fy, fw);
   gStyle->SetPalette(1);
   h2p->Draw("col");
   return ch2p1;
}
End_Macro
Begin_Html

<p>Rectangular bins are a frequent case. The special version of
the <tt>AddBin</tt> method allows to define them more easily like
shown in the following example.

End_Html
Begin_Macro(source)
../../../tutorials/hist/th2polyBoxes.C
End_Macro
Begin_Html

<p>One <tt>TH2Poly</tt> bin can be a list of polygons. Such bins are defined
by calling <tt>AddBin</tt> with a <tt>TMultiGraph</tt>. The following example
shows a such case:
End_Html
Begin_Macro(source)
{
   TCanvas *ch2p2 = new TCanvas("ch2p2","ch2p2",600,400);

   Int_t i, bin;
   const Int_t nx = 48;
   const char *states [nx] = {
      "alabama",      "arizona",        "arkansas",       "california",
      "colorado",     "connecticut",    "delaware",       "florida",
      "georgia",      "idaho",          "illinois",       "indiana",
      "iowa",         "kansas",         "kentucky",       "louisiana",
      "maine",        "maryland",       "massachusetts",  "michigan",
      "minnesota",    "mississippi",    "missouri",       "montana",
      "nebraska",     "nevada",         "new_hampshire",  "new_jersey",
      "new_mexico",   "new_york",       "north_carolina", "north_dakota",
      "ohio",         "oklahoma",       "oregon",         "pennsylvania",
      "rhode_island", "south_carolina", "south_dakota",   "tennessee",
      "texas",        "utah",           "vermont",        "virginia",
      "washington",   "west_virginia",  "wisconsin",      "wyoming"
   };
   Double_t pop[nx] = {
    4708708, 6595778,  2889450, 36961664, 5024748,  3518288,  885122, 18537969,
    9829211, 1545801, 12910409,  6423113, 3007856,  2818747, 4314113,  4492076,
    1318301, 5699478,  6593587,  9969727, 5266214,  2951996, 5987580,   974989,
    1796619, 2643085,  1324575,  8707739, 2009671, 19541453, 9380884,   646844,
   11542645, 3687050,  3825657, 12604767, 1053209,  4561242,  812383,  6296254,
   24782302, 2784572,   621760,  7882590, 6664195,  1819777, 5654774,   544270
   };

   Double_t lon1 = -130;
   Double_t lon2 = -65;
   Double_t lat1 = 24;
   Double_t lat2 = 50;
   TH2Poly *p = new TH2Poly("USA","USA Population",lon1,lon2,lat1,lat2);

   TFile *f;
   f = TFile::Open("http://root.cern.ch/files/usa.root");

   TMultiGraph *mg;
   TKey *key;
   TIter nextkey(gDirectory->GetListOfKeys());
   while ((key = (TKey*)nextkey())) {
      TObject *obj = key->ReadObj();
      if (obj->InheritsFrom("TMultiGraph")) {
         mg = (TMultiGraph*)obj;
         bin = p->AddBin(mg);
      }
   }

   for (i=0; i<nx; i++) p->Fill(states[i], pop[i]);

   gStyle->SetOptStat(11);
   gStyle->SetPalette(1);
   p->Draw("COLZ L");
   return ch2p2;
}
End_Macro
Begin_Html

<p> <tt>TH2Poly</tt> histograms can also be plotted using the GL interface using
the option "GLLEGO".

<a name="HP21"></a><h3>The SPEC option</h3>


This option allows to use the <tt>TSpectrum2Painter</tt> tools. See the full
documentation in <tt>TSpectrum2Painter::PaintSpectrum</tt>.


<a name="HP22"></a><h3>Option "Z" : Adding the color palette on the right side of the pad</h3>


When this option is specified, a color palette with an axis indicating the value
of the corresponding color is drawn on the right side of the picture. In case,
not enough space is left, one can increase the size of the right margin by
calling <tt>TPad::SetRightMargin()</tt>. The attributes used to display the
palette axis values are taken from the Z axis of the object. For example, to
set the labels size on the palette axis do:
<pre>
      hist->GetZaxis()->SetLabelSize().
</pre>
<b>WARNING:</b> The palette axis is always drawn vertically.


<a name="HP23"></a><h3>Setting the color palette</h3>


To change the color palette <tt>TStyle::SetPalette</tt> should be used, eg:
<pre>
      gStyle->SetPalette(ncolors,colors);
</pre>
For example the option <tt>"COL"</tt> draws a 2D histogram with cells
represented by a box filled with a color index which is a function
of the cell content.
If the cell content is N, the color index used will be the color number
in <tt>colors[N]</tt>, etc. If the maximum cell content is greater than
<tt>ncolors</tt>, all cell contents are scaled to <tt>ncolors</tt>.

<p>If <tt> ncolors <= 0</tt>, a default palette (see below) of 50 colors is
defined. This palette is recommended for pads, labels ...

<tt>if ncolors == 1 && colors == 0</tt>, then a Pretty Palette with a
Spectrum Violet->Red is created with 50 colors. That's the default rain bow
palette.
<p>
Other prefined palettes with 255 colors are available when <tt>colors == 0</tt>.
The following value of <tt>ncolors</tt> give access to:
<p>
<pre>
if ncolors = 51 and colors=0, a Deep Sea palette is used.
if ncolors = 52 and colors=0, a Grey Scale palette is used.
if ncolors = 53 and colors=0, a Dark Body Radiator palette is used.
if ncolors = 54 and colors=0, a two-color hue palette palette is used.(dark blue through neutral gray to bright yellow)
if ncolors = 55 and colors=0, a Rain Bow palette is used.
if ncolors = 56 and colors=0, an inverted Dark Body Radiator palette is used.
</pre>

<p> If <tt>ncolors > 0 && colors == 0</tt>, the default palette is used
with a maximum of ncolors.

<p> The default palette defines:
<ul>
<li> index  0  to  9 : shades of grey
<li> index 10  to 19 : shades of brown
<li> index 20  to 29 : shades of blue
<li> index 30  to 39 : shades of red
<li> index 40  to 49 : basic colors
</ul>
The color numbers specified in the palette can be viewed by selecting
the item <tt>"colors"</tt> in the <tt>"VIEW"</tt> menu of the canvas tool bar.
The red, green, and blue components of a color can be changed thanks to
<tt>TColor::SetRGB()</tt>.


<a name="HP24"></a><h3>Drawing a sub-range of a 2D histogram; the [cutg] option</h3>


Using a <tt>TCutG</tt> object, it is possible to draw a sub-range of a 2D
histogram. One must create a graphical cut (mouse or C++) and specify the name
of the cut between <tt>[]</tt> in the <tt>Draw()</tt> option.
For example, with a <tt>TCutG</tt> named <tt>"cutg"</tt>, one can call:
<pre>
      myhist->Draw("surf1 [cutg]");
</pre>
To invert the cut, it is enough to put a <tt>"-"</tt> in front of its name:
<pre>
      myhist->Draw("surf1 [-cutg]");
</pre>
It is possible to apply several cuts (<tt>","</tt> means logical AND):
<pre>
      myhist->Draw("surf1 [cutg1,cutg2]");
</pre>

End_Html
Begin_Macro(source)
../../../tutorials/fit/fit2a.C
End_Macro
Begin_Html


<a name="HP25"></a><h3>Drawing options for 3D histograms</h3>


<table border=0>

<tr><th valign=top>"ISO"</th><td>
Draw a Gouraud shaded 3d iso surface through a 3d histogram. It paints one
surface at the value computed as follow:
<tt>SumOfWeights/(NbinsX*NbinsY*NbinsZ)</tt>
</td></tr>

<tr><th valign=top>"BOX"</th><td>
Draw a for each cell with volume proportional to the content's absolute value.
</td></tr>

</table>

By default, like 2D histograms, 3D histograms are drawn as scatter plots.

<p>The following example shows a 3D histogram plotted as a scatter plot.

End_Html
Begin_Macro(source)
{
   TCanvas *c06 = new TCanvas("c06","c06",600,400);
   gStyle->SetOptStat(kFALSE);
   TH3F *h3scat = new TH3F("h3scat","Option SCAT (default) ",15,-2,2,15,-2,2,15,0,4);
   Double_t x, y, z;
   for (Int_t i=0;i<10000;i++) {
      gRandom->Rannor(x, y);
      z = x*x + y*y;
      h3scat->Fill(x,y,z);
   }
   h3scat->Draw();
   return c06;
}
End_Macro
Begin_Html

<p>The following example shows a 3D histogram plotted with the option <tt>"BOX"</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c16 = new TCanvas("c16","c16",600,400);
   gStyle->SetOptStat(kFALSE);
   TH3F *h3box = new TH3F("h3box","Option BOX",15,-2,2,15,-2,2,15,0,4);
   Double_t x, y, z;
   for (Int_t i=0;i<10000;i++) {
      gRandom->Rannor(x, y);
      z = x*x + y*y;
      h3box->Fill(x,y,z);
   }
   h3box->Draw("BOX");
   return c16;
}
End_Macro
Begin_Html

<p>The following example shows a 3D histogram plotted with the option <tt>"ISO"</tt>.

End_Html
Begin_Macro(source)
{
   TCanvas *c26 = new TCanvas("c26","c26",600,400);
   gStyle->SetOptStat(kFALSE);
   TH3F *h3iso = new TH3F("h3iso","Option ISO",15,-2,2,15,-2,2,15,0,4);
   Double_t x, y, z;
   for (Int_t i=0;i<10000;i++) {
      gRandom->Rannor(x, y);
      z = x*x + y*y;
      h3iso->Fill(x,y,z);
   }
   h3iso->SetFillColor(kCyan);
   h3iso->Draw("ISO");
   return c26;
}
End_Macro
Begin_Html


<a name="HP26"></a><h3>Drawing option for histograms' stacks</h3>


Stacks of histograms are managed with the <tt>THStack</tt>. A <tt>THStack</tt>
is a collection of <tt>TH1</tt> (or derived) objects. For painting only the
<tt>THStack</tt> containing <tt>TH1</tt> only or
<tt>THStack</tt> containing <tt>TH2</tt> only will be considered.

<p>By default, histograms are shown stacked:
<ol>
<li> The first histogram is paint.
<li> The the sum of the first and second, etc...
</ol>
If the option <tt>"NOSTACK"</tt> is specified, the histograms are all paint in
the same pad as if the option <tt>"SAME"</tt> had been specified. This allows to
compute X and Y scales common to all the histograms, like
<tt>TMultiGraph</tt> does for graphs.

<p>If the option <tt>"PADS"</tt> is specified, the current pad/canvas is
subdivided into a number of pads equal to the number of histograms and each
histogram is paint into a separate pad.

<p>The following example shows various types of stacks.

End_Html
Begin_Macro(source)
../../../tutorials/hist/hstack.C
End_Macro
Begin_Html

<p>If at least one of the histograms in the stack has errors, the whole stack is
visualized by default with error bars. To visualize it without errors the
option <tt>"HIST"</tt> should be used.

End_Html
Begin_Macro(source)
{
   TCanvas *cst1 = new TCanvas("cst1","cst1",700,400);
   cst1->Divide(2,1);

   TH1F * hst11 = new TH1F("hst11", "", 20, -10, 10);
   hst11->Sumw2();
   hst11->FillRandom("gaus", 1000);
   hst11->SetFillColor(kViolet);
   hst11->SetLineColor(kViolet);

   TH1F * hst12 = new TH1F("hst12", "", 20, -10, 10);
   hst12->FillRandom("gaus", 500);
   hst12->SetFillColor(kBlue);
   hst12->SetLineColor(kBlue);

   THStack st1("st1", "st1");
   st1.Add(hst11);
   st1.Add(hst12);

   cst1->cd(1); st1.Draw();
   cst1->cd(2); st1.Draw("hist");

   return cst1;
}
End_Macro
Begin_Html

<a name="HP27"></a><h3>Drawing of 3D implicit functions</h3>


3D implicit functions (<tt>TF3</tt>) can be drawn as iso-surfaces.
The implicit function f(x,y,z) = 0 is drawn in cartesian coordinates.
In the following example the options "FB" and "BB" suppress the
"Front Box" and "Back Box" around the plot.

End_Html
Begin_Macro(source)
{
   TCanvas *c2 = new TCanvas("c2","c2",600,400);
   TF3 *f3 = new TF3("f3","sin(x*x+y*y+z*z-36)",-2,2,-2,2,-2,2);
   f3->SetClippingBoxOn(0,0,0);
   f3->SetFillColor(30);
   f3->SetLineColor(15);
   f3->Draw("FBBB");
   return c2;
}
End_Macro
Begin_Html


<a name="HP28"></a><h3>Associated functions drawing</h3>


An associated function is created by <tt>TH1::Fit</tt>. More than on fitted
function can be associated with one histogram (see <tt>TH1::Fit</tt>).

<p>A <tt>TF1</tt> object <tt>f1</tt> can be added to the list of associated
functions of an histogram <tt>h</tt> without calling <tt>TH1::Fit</tt>
simply doing:
<pre>
      h->GetListOfFunctions()->Add(f1);
</pre>
or
<pre>
      h->GetListOfFunctions()->Add(f1,someoption);
</pre>
To retrieve a function by name from this list, do:
<pre>
      TF1 *f1 = (TF1*)h->GetListOfFunctions()->FindObject(name);
</pre>
or
<pre>
      TF1 *f1 = h->GetFunction(name);
</pre>
Associated functions are automatically painted when an histogram is drawn.
To avoid the painting of the associated functions the option <tt>HIST</tt>
should be added to the list of the options used to paint the histogram.


<a name="HP29"></a><h3>Drawing using OpenGL</h3>


The class <tt>TGLHistPainter</tt> allows to paint data set using the OpenGL 3D
graphics library. The plotting options start with <tt>GL</tt> keyword.
In addition, in order to inform canvases that OpenGL should be used to render
3D representations, the following option should be set:
<pre>
      gStyle->SetCanvasPreferGL(true);
</pre>

<a name="HP29a"></a><h4><u>General information: plot types and supported options</u></h4>

The following types of plots are provided:

<p>For lego plots the supported options are:

<table border=0>

<tr><th valign=top>"GLLEGO"</th><td>
Draw a lego plot. It works also for <tt>TH2Poly</tt>.
</td></tr>

<tr><th valign=top>"GLLEGO2</th><td>
Bins with color levels.
</td></tr>

<tr><th valign=top>"GLLEGO3</th><td>
Cylindrical bars.
</td></tr>

</table>

<p>Lego painter in cartesian supports logarithmic scales for X, Y, Z.
In polar only Z axis can be logarithmic, in cylindrical only Y.

<p>For surface plots (<tt>TF2</tt> and <tt>TH2</tt>) the supported options are:

<table border=0>

<tr><th valign=top>"GLSURF" </th><td>
Draw a surface.
</td></tr>

<tr><th valign=top>"GLSURF1"</th><td>
Surface with color levels
</td></tr>

<tr><th valign=top>"GLSURF2"</th><td>
The same as "GLSURF1" but without polygon outlines.
</td></tr>

<tr><th valign=top>"GLSURF3"</th><td>
Color level projection on top of plot (works only in cartesian coordinate
system).
</td></tr>

<tr><th valign=top>"GLSURF4"</th><td>
Same as "GLSURF" but without polygon outlines.
</td></tr>

</table>

The surface painting in cartesian coordinates supports logarithmic scales along
X, Y, Z axis. In polar coordinates only the Z axis can be logarithmic,
in cylindrical coordinates only the Y axis.

<p>Additional options to SURF and LEGO - Coordinate systems:

<table border=0>

<tr><th valign=top>" "</th><td>
Default, cartesian coordinates system.
</td></tr>

<tr><th valign=top>"POL"</th><td>
Polar coordinates system.
</td></tr>

<tr><th valign=top>"CYL"</th><td>
Cylindrical coordinates system.
</td></tr>

<tr><th valign=top>"SPH"</th><td>
Spherical coordinates system.
</td></tr>

</table>

<a name="HP290"></a><h4><u>TH3 as color boxes</u></h4>

The supported option is:

<table border=0>

<tr><th valign=top>"GLCOL" </th><td>
H3 is drawn using semi-transparent colored boxes.
See <tt>$ROOTSYS/tutorials/gl/glvox1.C</tt>.
</td></tr>

</table>

<a name="HP29b"></a><h4><u>TH3 as boxes (spheres)</u></h4>

The supported options are:

<table border=0>

<tr><th valign=top>GLBOX" </th><td>
TH3 as a set of boxes, size of box is proportional to bin content.
</td></tr>

<tr><th valign=top>GLBOX1"</th><td>
The same as "glbox", but spheres are drawn instead of boxes.
</td></tr>

</table>

<a name="HP29c"></a><h4><u>TH3 as iso-surface(s)</u></h4>

The supported option is:

<table border=0>

<tr><th valign=top>"GLISO" </th><td>
TH3 is drawn using iso-surfaces.
</td></tr>

</table>

<a name="HP29d"></a><h4><u>TF3 (implicit function)</u></h4>

The supported option is:

<table border=0>

<tr><th valign=top>GLTF3" </th><td>
Draw a TF3.
</td></tr>

</table>

<a name="HP29e"></a><h4><u>Parametric surfaces</u></h4>

<tt>$ROOTSYS/tutorials/gl/glparametric.C</tt> shows how to create parametric
equations and visualize the surface.

<a name="HP29f"></a><h4><u>Interaction with the plots</u></h4>

All the interactions are implemented via standard methods
<tt>DistancetoPrimitive()</tt> and <tt>ExecuteEvent()</tt>. That's why all the
interactions with the OpenGL plots are possible only when the mouse cursor is
in the plot's area (the plot's area is the part of a the pad occupied by
gl-produced picture). If the mouse cursor is not above gl-picture, the standard
pad interaction is performed.

<a name="HP29g"></a><h4><u>Selectable parts</u></h4>

Different parts of the plot can be selected:
<ul>
<li> xoz, yoz, xoy back planes:
When such a plane selected, it's highlighted in green if the
dynamic slicing by this plane is supported, and it's
highlighted in red, if the dynamic slicing is not supported.
<li> The plot itself:
On surfaces, the selected surface is outlined in red. (TF3 and
ISO are not outlined). On lego plots, the selected bin is
highlighted. The bin number and content are displayed in pad's
status bar. In box plots, the box or sphere is highlighted and
the bin info is displayed in pad's status bar.
</ul>

<a name="HP29h"></a><h4><u>Rotation and zooming</u></h4>

<ul>
<li>Rotation:
When the plot is selected, it can be rotated by pressing and
holding the left mouse button and move the cursor.
<li>Zoom/Unzoom:
Mouse wheel or 'j', 'J', 'k', 'K' keys.
</ul>

<a name="HP29i"></a><h4><u>Panning</u></h4>

The selected plot can be moved in a pad's area by pressing and
holding the left mouse button and the shift key.

<a name="HP29j"></a><h4><u>Box cut</u></h4>

Surface, iso, box, TF3 and parametric painters support box cut by
pressing the 'c' or 'C' key when the mouse cursor is in a plot's
area. That will display a transparent box, cutting away part of the
surface (or boxes) in order to show internal part of plot. This box
can be moved inside the plot's area (the full size of the box is
equal to the plot's surrounding box) by selecting one of the box
cut axes and pressing the left mouse button to move it.

<a name="HP29k"></a><h4><u>Plot specific interactions (dynamic slicing etc.)</u></h4>

Currently, all gl-plots support some form of slicing. When back plane
is selected (and if it's highlighted in green) you can press and hold
left mouse button and shift key and move this back plane inside
plot's area, creating the slice. During this "slicing" plot becomes
semi-transparent. To remove all slices (and projected curves for
surfaces) double click with left mouse button in a plot's area.

<a name="HP29l"></a><h4><u>Surface with option "GLSURF"</u></h4>

The surface profile is displayed on the slicing plane.
The profile projection is drawn on the back plane
by pressing <tt>'p'</tt> or <tt>'P'</tt> key.

<a name="HP29m"></a><h4><u>TF3</u></h4>

The contour plot is drawn on the slicing plane. For TF3 the color
scheme can be changed by pressing 's' or 'S'.

<a name="HP29n"></a><h4><u>Box</u></h4>

The contour plot corresponding to slice plane position is drawn in real time.

<a name="HP29o"></a><h4><u>Iso</u></h4>

Slicing is similar to "GLBOX" option.

<a name="HP29p"></a><h4><u>Parametric plot</u></h4>

No slicing. Additional keys: 's' or 'S' to change color scheme -
about 20 color schemes supported ('s' for "scheme"); 'l' or 'L' to
increase number of polygons ('l' for "level" of details), 'w' or 'W'
to show outlines ('w' for "wireframe").

End_Html */

TH1 *gCurrentHist = 0;

Hoption_t Hoption;
Hparam_t  Hparam;

const Int_t kNMAX = 2000;

const Int_t kMAXCONTOUR  = 104;
const UInt_t kCannotRotate = BIT(11);

static TString gStringEntries;
static TString gStringMean;
static TString gStringMeanX;
static TString gStringMeanY;
static TString gStringMeanZ;
static TString gStringRMS;
static TString gStringRMSX;
static TString gStringRMSY;
static TString gStringRMSZ;
static TString gStringUnderflow;
static TString gStringOverflow;
static TString gStringIntegral;
static TString gStringIntegralBinWidth;
static TString gStringSkewness;
static TString gStringSkewnessX;
static TString gStringSkewnessY;
static TString gStringSkewnessZ;
static TString gStringKurtosis;
static TString gStringKurtosisX;
static TString gStringKurtosisY;
static TString gStringKurtosisZ;

ClassImp(THistPainter)


//______________________________________________________________________________
THistPainter::THistPainter()
{
   /* Begin_html
   Default constructor.
   End_html */

   fH = 0;
   fXaxis = 0;
   fYaxis = 0;
   fZaxis = 0;
   fFunctions = 0;
   fXbuf  = 0;
   fYbuf  = 0;
   fNcuts = 0;
   fStack = 0;
   fLego  = 0;
   fPie   = 0;
   fGraph2DPainter = 0;
   fShowProjection = 0;
   fShowOption = "";
   for (int i=0; i<kMaxCuts; i++) {
      fCuts[i] = 0;
      fCutsOpt[i] = 0;
   }
   fXHighlightBin = -1;
   fYHighlightBin = -1;

   gStringEntries          = gEnv->GetValue("Hist.Stats.Entries",          "Entries");
   gStringMean             = gEnv->GetValue("Hist.Stats.Mean",             "Mean");
   gStringMeanX            = gEnv->GetValue("Hist.Stats.MeanX",            "Mean x");
   gStringMeanY            = gEnv->GetValue("Hist.Stats.MeanY",            "Mean y");
   gStringMeanZ            = gEnv->GetValue("Hist.Stats.MeanZ",            "Mean z");
   gStringRMS              = gEnv->GetValue("Hist.Stats.RMS",              "RMS");
   gStringRMSX             = gEnv->GetValue("Hist.Stats.RMSX",             "RMS x");
   gStringRMSY             = gEnv->GetValue("Hist.Stats.RMSY",             "RMS y");
   gStringRMSZ             = gEnv->GetValue("Hist.Stats.RMSZ",             "RMS z");
   gStringUnderflow        = gEnv->GetValue("Hist.Stats.Underflow",        "Underflow");
   gStringOverflow         = gEnv->GetValue("Hist.Stats.Overflow",         "Overflow");
   gStringIntegral         = gEnv->GetValue("Hist.Stats.Integral",         "Integral");
   gStringIntegralBinWidth = gEnv->GetValue("Hist.Stats.IntegralBinWidth", "Integral(w)");
   gStringSkewness         = gEnv->GetValue("Hist.Stats.Skewness",         "Skewness");
   gStringSkewnessX        = gEnv->GetValue("Hist.Stats.SkewnessX",        "Skewness x");
   gStringSkewnessY        = gEnv->GetValue("Hist.Stats.SkewnessY",        "Skewness y");
   gStringSkewnessZ        = gEnv->GetValue("Hist.Stats.SkewnessZ",        "Skewness z");
   gStringKurtosis         = gEnv->GetValue("Hist.Stats.Kurtosis",         "Kurtosis");
   gStringKurtosisX        = gEnv->GetValue("Hist.Stats.KurtosisX",        "Kurtosis x");
   gStringKurtosisY        = gEnv->GetValue("Hist.Stats.KurtosisY",        "Kurtosis y");
   gStringKurtosisZ        = gEnv->GetValue("Hist.Stats.KurtosisZ",        "Kurtosis z");
}


//______________________________________________________________________________
THistPainter::~THistPainter()
{
   /* Begin_html
   Default destructor.
   End_html */
}


//______________________________________________________________________________
Int_t THistPainter::DistancetoPrimitive(Int_t px, Int_t py)
{
   /* Begin_html
   Compute the distance from the point px,py to a line.
   <p>
   Compute the closest distance of approach from point px,py to elements of
   an histogram. The distance is computed in pixels units.
   <p>
   Algorithm:<br>
   Currently, this simple model computes the distance from the mouse to the
   histogram contour only.
   End_html */

   const Int_t big = 9999;
   const Int_t kMaxDiff = 7;

   if (fPie) return fPie->DistancetoPrimitive(px, py);

   Double_t x  = gPad->AbsPixeltoX(px);
   Double_t x1 = gPad->AbsPixeltoX(px+1);

   Int_t puxmin = gPad->XtoAbsPixel(gPad->GetUxmin());
   Int_t puymin = gPad->YtoAbsPixel(gPad->GetUymin());
   Int_t puxmax = gPad->XtoAbsPixel(gPad->GetUxmax());
   Int_t puymax = gPad->YtoAbsPixel(gPad->GetUymax());
   Int_t curdist = big;
   Int_t yxaxis, dyaxis,xyaxis, dxaxis;
   Bool_t dsame;
   TObject *PadPointer = gPad->GetPadPointer();
   if (!PadPointer) return 0;
   TString doption = PadPointer->GetDrawOption();
   Double_t factor = 1;
   if (fH->GetNormFactor() != 0) {
      factor = fH->GetNormFactor()/fH->GetSumOfWeights();
   }
   //     return if point is not in the histogram area

   //     If a 3D view exists, check distance to axis
   TView *view = gPad->GetView();
   Int_t d1,d2,d3;
   if (view && Hoption.Contour != 14) {
      Double_t ratio;
      d3 = view->GetDistancetoAxis(3, px, py, ratio);
      if (d3 <= kMaxDiff) {gPad->SetSelected(fZaxis); return 0;}
      d1 = view->GetDistancetoAxis(1, px, py, ratio);
      if (d1 <= kMaxDiff) {gPad->SetSelected(fXaxis); return 0;}
      d2 = view->GetDistancetoAxis(2, px, py, ratio);
      if (d2 <= kMaxDiff) {gPad->SetSelected(fYaxis); return 0;}
      if ( px > puxmin && px < puxmax && py > puymax && py < puymin) curdist = 1;
      goto FUNCTIONS;
   }
   //     check if point is close to an axis
   doption.ToLower();
   dsame = kFALSE;
   if (doption.Contains("same")) dsame = kTRUE;

   dyaxis = Int_t(2*(puymin-puymax)*fYaxis->GetLabelSize());
   if (doption.Contains("y+")) {
      xyaxis = puxmax + Int_t((puxmax-puxmin)*fYaxis->GetLabelOffset());
      if (px <= xyaxis+dyaxis && px >= xyaxis && py >puymax && py < puymin) {
         if (!dsame) {
            if (gPad->IsVertical()) gPad->SetSelected(fYaxis);
            else                    gPad->SetSelected(fXaxis);
            return 0;
         }
      }
   } else {
      xyaxis = puxmin - Int_t((puxmax-puxmin)*fYaxis->GetLabelOffset());
      if (px >= xyaxis-dyaxis && px <= xyaxis && py >puymax && py < puymin) {
         if (!dsame) {
            if (gPad->IsVertical()) gPad->SetSelected(fYaxis);
            else                    gPad->SetSelected(fXaxis);
            return 0;
         }
      }
   }

   dxaxis = Int_t((puymin-puymax)*fXaxis->GetLabelSize());
   if (doption.Contains("x+")) {
      yxaxis = puymax - Int_t((puymin-puymax)*fXaxis->GetLabelOffset());
      if (py >= yxaxis-dxaxis && py <= yxaxis && px <puxmax && px > puxmin) {
         if (!dsame) {
            if (gPad->IsVertical()) gPad->SetSelected(fXaxis);
            else                    gPad->SetSelected(fYaxis);
            return 0;
         }
      }
   } else {
      yxaxis = puymin + Int_t((puymin-puymax)*fXaxis->GetLabelOffset());
      if (yxaxis < puymin) yxaxis = puymin;
      if (py <= yxaxis+dxaxis && py >= yxaxis && px <puxmax && px > puxmin) {
         if (!dsame) {
            if (gPad->IsVertical()) gPad->SetSelected(fXaxis);
            else                    gPad->SetSelected(fYaxis);
            return 0;
         }
      }
   }

   if (fH->IsHighlight()) {
      if ((px > puxmin) && (py < puymin) && (px < puxmax) && (py > puymax))
         HighlightBin(px, py); // only inside frame
   }

   //     if object is 2D or 3D return this object
   if (fH->GetDimension() == 2) {
      if (fH->InheritsFrom(TH2Poly::Class())) {
         TH2Poly *th2 = (TH2Poly*)fH;
         Double_t xmin, ymin, xmax, ymax;
         gPad->GetRangeAxis(xmin, ymin, xmax, ymax);
         Double_t pxu = gPad->AbsPixeltoX(px);
         Double_t pyu = gPad->AbsPixeltoY(py);
         if ((pxu>xmax) || (pxu < xmin) || (pyu>ymax) || (pyu < ymin)) {
            curdist = big;
            goto FUNCTIONS;
         } else {
            Int_t bin = th2->FindBin(pxu, pyu);
            if (bin>0) curdist = 1;
            else       curdist = big;
            goto FUNCTIONS;
         }
      }
      Int_t delta2 = 5; //Give a margin of delta2 pixels to be in the 2-d area
      if ( px > puxmin + delta2
        && px < puxmax - delta2
        && py > puymax + delta2
        && py < puymin - delta2) {curdist =1; goto FUNCTIONS;}
   }

   //     point is inside histogram area. Find channel number
   if (gPad->IsVertical()) {
      Int_t bin      = fXaxis->FindFixBin(gPad->PadtoX(x));
      Int_t binsup   = fXaxis->FindFixBin(gPad->PadtoX(x1));
      Double_t binval = factor*fH->GetBinContent(bin);
      Int_t pybin    = gPad->YtoAbsPixel(gPad->YtoPad(binval));
      if (binval == 0 && pybin < puymin) pybin = 10000;
      // special case if more than one bin for the pixel
      if (binsup-bin>1) {
         Double_t binvalmin, binvalmax;
         binvalmin=binval;
         binvalmax=binval;
         for (Int_t ibin=bin+1; ibin<binsup; ibin++) {
            Double_t binvaltmp = factor*fH->GetBinContent(ibin);
            if (binvalmin>binvaltmp) binvalmin=binvaltmp;
            if (binvalmax<binvaltmp) binvalmax=binvaltmp;
         }
         Int_t pybinmin = gPad->YtoAbsPixel(gPad->YtoPad(binvalmax));
         Int_t pybinmax = gPad->YtoAbsPixel(gPad->YtoPad(binvalmin));
         if (py<pybinmax+kMaxDiff/2 && py>pybinmin-kMaxDiff/2) pybin = py;
      }
      if (TMath::Abs(py - pybin) <= kMaxDiff) return TMath::Abs(py - pybin);
   } else {
      Double_t y  = gPad->AbsPixeltoY(py);
      Double_t y1 = gPad->AbsPixeltoY(py+1);
      Int_t bin      = fXaxis->FindFixBin(gPad->PadtoY(y));
      Int_t binsup   = fXaxis->FindFixBin(gPad->PadtoY(y1));
      Double_t binval = factor*fH->GetBinContent(bin);
      Int_t pxbin    = gPad->XtoAbsPixel(gPad->XtoPad(binval));
      if (binval == 0 && pxbin > puxmin) pxbin = 10000;
      // special case if more than one bin for the pixel
      if (binsup-bin>1) {
         Double_t binvalmin, binvalmax;
         binvalmin=binval;
         binvalmax=binval;
         for (Int_t ibin=bin+1; ibin<binsup; ibin++) {
            Double_t binvaltmp = factor*fH->GetBinContent(ibin);
            if (binvalmin>binvaltmp) binvalmin=binvaltmp;
            if (binvalmax<binvaltmp) binvalmax=binvaltmp;
         }
         Int_t pxbinmin = gPad->XtoAbsPixel(gPad->XtoPad(binvalmax));
         Int_t pxbinmax = gPad->XtoAbsPixel(gPad->XtoPad(binvalmin));
         if (px<pxbinmax+kMaxDiff/2 && px>pxbinmin-kMaxDiff/2) pxbin = px;
      }
      if (TMath::Abs(px - pxbin) <= kMaxDiff) return TMath::Abs(px - pxbin);
   }
   //     Loop on the list of associated functions and user objects
FUNCTIONS:
   TObject *f;
   TIter   next(fFunctions);
   while ((f = (TObject*) next())) {
      Int_t dist;
      if (f->InheritsFrom(TF1::Class())) dist = f->DistancetoPrimitive(-px,py);
      else                               dist = f->DistancetoPrimitive(px,py);
      if (dist < kMaxDiff) {gPad->SetSelected(f); return dist;}
   }
   return curdist;
}


//______________________________________________________________________________
void THistPainter::DrawPanel()
{
   /* Begin_html
   Display a panel with all histogram drawing options.
   End_html */

   gCurrentHist = fH;
   if (!gPad) {
      Error("DrawPanel", "need to draw histogram first");
      return;
   }
   TVirtualPadEditor *editor = TVirtualPadEditor::GetPadEditor();
   editor->Show();
   gROOT->ProcessLine(Form("((TCanvas*)0x%lx)->Selected((TVirtualPad*)0x%lx,(TObject*)0x%lx,1)",
                           (ULong_t)gPad->GetCanvas(), (ULong_t)gPad, (ULong_t)fH));
}


//______________________________________________________________________________
void THistPainter::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   /* Begin_html
   Execute the actions corresponding to "event".
   <p>
   This function is called when a histogram is clicked with the locator at
   the pixel position px,py.
   End_html */

   static Int_t bin, px1, py1, px2, py2, pyold;
   static TBox *zoombox;
   Double_t zbx1,zbx2,zby1,zby2;

   Int_t bin1, bin2;
   Double_t xlow, xup, ylow, binval, x, baroffset, barwidth, binwidth;
   Bool_t opaque  = gPad->OpaqueMoving();

   if (!gPad->IsEditable()) return;

   if (fPie) {
      fPie->ExecuteEvent(event, px, py);
      return;
   }
   //     come here if we have a lego/surface in the pad
   TView *view = gPad->GetView();

   if (!fShowProjection && view && view->TestBit(kCannotRotate) == 0) {
      view->ExecuteRotateView(event, px, py);
      return;
   }

   TAxis *xaxis    = fH->GetXaxis();
   TAxis *yaxis    = fH->GetYaxis();
   Int_t dimension = fH->GetDimension();

   Double_t factor = 1;
   if (fH->GetNormFactor() != 0) {
      factor = fH->GetNormFactor()/fH->GetSumOfWeights();
   }

   switch (event) {

   case kButton1Down:

      if (!opaque) gVirtualX->SetLineColor(-1);
      fH->TAttLine::Modify();

      if (opaque && dimension ==2) {
         zbx1 = gPad->AbsPixeltoX(px);
         zbx2 = gPad->AbsPixeltoX(px);
         zby1 = gPad->AbsPixeltoY(py);
         zby2 = gPad->AbsPixeltoY(py);
         px1 = px;
         py1 = py;
         if (gPad->GetLogx()) {
            zbx1 = TMath::Power(10,zbx1);
            zbx2 = TMath::Power(10,zbx2);
         }
         if (gPad->GetLogy()) {
            zby1 = TMath::Power(10,zby1);
            zby2 = TMath::Power(10,zby2);
         }
         zoombox = new TBox(zbx1, zby1, zbx2, zby2);
         Int_t ci = TColor::GetColor("#7d7dff");
         TColor *zoomcolor = gROOT->GetColor(ci);
         if (!TCanvas::SupportAlpha()) zoombox->SetFillStyle(3002);
         else                          zoomcolor->SetAlpha(0.5);
         zoombox->SetFillColor(ci);
         zoombox->Draw();
         gPad->Modified();
         gPad->Update();
      }
      // No break !!!

   case kMouseMotion:

      if (fShowProjection) {ShowProjection3(px,py); break;}

      gPad->SetCursor(kPointer);
      if (dimension ==1) {
         if (Hoption.Bar) {
            baroffset = fH->GetBarOffset();
            barwidth  = fH->GetBarWidth();
         } else {
            baroffset = 0;
            barwidth  = 1;
         }
         x        = gPad->AbsPixeltoX(px);
         bin      = fXaxis->FindFixBin(gPad->PadtoX(x));
         binwidth = fXaxis->GetBinWidth(bin);
         xlow     = gPad->XtoPad(fXaxis->GetBinLowEdge(bin) + baroffset*binwidth);
         xup      = gPad->XtoPad(xlow + barwidth*binwidth);
         ylow     = gPad->GetUymin();
         px1      = gPad->XtoAbsPixel(xlow);
         px2      = gPad->XtoAbsPixel(xup);
         py1      = gPad->YtoAbsPixel(ylow);
         py2      = py;
         pyold    = py;
         if (gROOT->GetEditHistograms()) gPad->SetCursor(kArrowVer);
      }

      break;

   case kButton1Motion:

      if (dimension ==1) {
         if (gROOT->GetEditHistograms()) {
            if (!opaque) {
               gVirtualX->DrawBox(px1, py1, px2, py2,TVirtualX::kHollow);  // Draw the old box
               py2 += py - pyold;
               gVirtualX->DrawBox(px1, py1, px2, py2,TVirtualX::kHollow);  // Draw the new box
               pyold = py;
            } else {
               py2 += py - pyold;
               pyold = py;
               binval = gPad->PadtoY(gPad->AbsPixeltoY(py2))/factor;
               fH->SetBinContent(bin,binval);
               gPad->Modified(kTRUE);
            }
         }
      }

      if (opaque && dimension ==2) {
         if (TMath::Abs(px1-px)>5 && TMath::Abs(py1-py)>5) {
            zbx2 = gPad->AbsPixeltoX(px);
            zby2 = gPad->AbsPixeltoY(py);
            if (gPad->GetLogx()) zbx2 = TMath::Power(10,zbx2);
            if (gPad->GetLogy()) zby2 = TMath::Power(10,zby2);
            zoombox->SetX2(zbx2);
            zoombox->SetY2(zby2);
            gPad->Modified();
            gPad->Update();
         }
      }

      break;

   case kWheelUp:

      if (dimension ==2) {
         bin1 = xaxis->GetFirst()+1;
         bin2 = xaxis->GetLast()-1;
         bin1 = TMath::Max(bin1, 1);
         bin2 = TMath::Min(bin2, xaxis->GetNbins());
         if (bin2>bin1) xaxis->SetRange(bin1,bin2);
         bin1 = yaxis->GetFirst()+1;
         bin2 = yaxis->GetLast()-1;
         bin1 = TMath::Max(bin1, 1);
         bin2 = TMath::Min(bin2, yaxis->GetNbins());
         if (bin2>bin1) yaxis->SetRange(bin1,bin2);
      }
      gPad->Modified();
      gPad->Update();

      break;

   case kWheelDown:

      if (dimension == 2) {
         bin1 = xaxis->GetFirst()-1;
         bin2 = xaxis->GetLast()+1;
         bin1 = TMath::Max(bin1, 1);
         bin2 = TMath::Min(bin2, xaxis->GetNbins());
         if (bin2>bin1) xaxis->SetRange(bin1,bin2);
         bin1 = yaxis->GetFirst()-1;
         bin2 = yaxis->GetLast()+1;
         bin1 = TMath::Max(bin1, 1);
         bin2 = TMath::Min(bin2, yaxis->GetNbins());
         if (bin2>bin1) yaxis->SetRange(bin1,bin2);
      }
      gPad->Modified();
      gPad->Update();

      break;

   case kButton1Up:
      if (dimension ==1) {
         if (gROOT->GetEditHistograms()) {
            binval = gPad->PadtoY(gPad->AbsPixeltoY(py2))/factor;
            fH->SetBinContent(bin,binval);
            PaintInit();   // recalculate Hparam structure and recalculate range
         }

         // might resize pad pixmap so should be called before any paint routine
         RecalculateRange();
      }
      if (opaque && dimension ==2) {
         if (zoombox) {
            Double_t x1 = TMath::Min(zoombox->GetX1(), zoombox->GetX2());
            Double_t x2 = TMath::Max(zoombox->GetX1(), zoombox->GetX2());
            Double_t y1 = TMath::Min(zoombox->GetY1(), zoombox->GetY2());
            Double_t y2 = TMath::Max(zoombox->GetY1(), zoombox->GetY2());
            x1 = TMath::Max(x1,xaxis->GetXmin());
            x2 = TMath::Min(x2,xaxis->GetXmax());
            y1 = TMath::Max(y1,yaxis->GetXmin());
            y2 = TMath::Min(y2,yaxis->GetXmax());
            if (x1<x2 && y1<y2) {
               xaxis->SetRangeUser(x1, x2);
               yaxis->SetRangeUser(y1, y2);
            }
            zoombox->Delete();
            zoombox = 0;
         }
      }
      gPad->Modified(kTRUE);
      if (opaque) gVirtualX->SetLineColor(-1);

      break;

   case kButton1Locate:

      ExecuteEvent(kButton1Down, px, py);

      while (1) {
         px = py = 0;
         event = gVirtualX->RequestLocator(1, 1, px, py);

         ExecuteEvent(kButton1Motion, px, py);

         if (event != -1) {                     // button is released
            ExecuteEvent(kButton1Up, px, py);
            return;
         }
      }
   }
}


//______________________________________________________________________________
TList *THistPainter::GetContourList(Double_t contour) const
{
   /* Begin_html
   Get a contour (as a list of TGraphs) using the Delaunay triangulation.
   End_html */

   TGraphDelaunay *dt;

   // Check if fH contains a TGraphDelaunay
   TList *hl = fH->GetListOfFunctions();
   dt = (TGraphDelaunay*)hl->FindObject("TGraphDelaunay");
   if (!dt) return 0;

   gCurrentHist = fH;

   if (!fGraph2DPainter) ((THistPainter*)this)->fGraph2DPainter = new TGraph2DPainter(dt);

   return fGraph2DPainter->GetContourList(contour);
}


//______________________________________________________________________________
char *THistPainter::GetObjectInfo(Int_t px, Int_t py) const
{
   /* Begin_html
   Display the histogram info (bin number, contents, integral up to bin
   corresponding to cursor position px,py.
   End_html */

   if (!gPad) return (char*)"";
   static char info[200];
   Double_t x  = gPad->PadtoX(gPad->AbsPixeltoX(px));
   Double_t y  = gPad->PadtoY(gPad->AbsPixeltoY(py));
   Double_t x1 = gPad->PadtoX(gPad->AbsPixeltoX(px+1));
   const char *drawOption = fH->GetDrawOption();
   Double_t xmin, xmax, uxmin,uxmax;
   Double_t ymin, ymax, uymin,uymax;
   if (fH->GetDimension() == 2) {
      if (gPad->GetView() || strncmp(drawOption,"cont",4) == 0
                          || strncmp(drawOption,"CONT",4) == 0) {
         uxmin=gPad->GetUxmin();
         uxmax=gPad->GetUxmax();
         xmin = fXaxis->GetBinLowEdge(fXaxis->GetFirst());
         xmax = fXaxis->GetBinUpEdge(fXaxis->GetLast());
         x = xmin +(xmax-xmin)*(x-uxmin)/(uxmax-uxmin);
         uymin=gPad->GetUymin();
         uymax=gPad->GetUymax();
         ymin = fYaxis->GetBinLowEdge(fYaxis->GetFirst());
         ymax = fYaxis->GetBinUpEdge(fYaxis->GetLast());
         y = ymin +(ymax-ymin)*(y-uymin)/(uymax-uymin);
      }
   }
   Int_t binx,biny,binmin,binx1;
   if (gPad->IsVertical()) {
      binx   = fXaxis->FindFixBin(x);
      binmin = fXaxis->GetFirst();
      binx1  = fXaxis->FindFixBin(x1);
      // special case if more than 1 bin in x per pixel
      if (binx1-binx>1 && fH->GetDimension() == 1) {
         Double_t binval=fH->GetBinContent(binx);
         Int_t binnear=binx;
         for (Int_t ibin=binx+1; ibin<binx1; ibin++) {
            Double_t binvaltmp = fH->GetBinContent(ibin);
            if (TMath::Abs(y-binvaltmp) < TMath::Abs(y-binval)) {
               binval=binvaltmp;
               binnear=ibin;
            }
         }
         binx = binnear;
      }
   } else {
      x1 = gPad->PadtoY(gPad->AbsPixeltoY(py+1));
      binx   = fXaxis->FindFixBin(y);
      binmin = fXaxis->GetFirst();
      binx1  = fXaxis->FindFixBin(x1);
      // special case if more than 1 bin in x per pixel
      if (binx1-binx>1 && fH->GetDimension() == 1) {
         Double_t binval=fH->GetBinContent(binx);
         Int_t binnear=binx;
         for (Int_t ibin=binx+1; ibin<binx1; ibin++) {
            Double_t binvaltmp = fH->GetBinContent(ibin);
            if (TMath::Abs(x-binvaltmp) < TMath::Abs(x-binval)) {
               binval=binvaltmp;
               binnear=ibin;
            }
         }
         binx = binnear;
      }
   }
   if (fH->GetDimension() == 1) {
      if (fH->InheritsFrom(TProfile::Class())) {
         TProfile *tp = (TProfile*)fH;
         snprintf(info,200,"(x=%g, y=%g, binx=%d, binc=%g, bine=%g, binn=%d)",
                  x, y, binx, fH->GetBinContent(binx), fH->GetBinError(binx),
                  (Int_t) tp->GetBinEntries(binx));
      }
      else {
         Double_t integ = 0;
         for (Int_t bin=binmin;bin<=binx;bin++) {integ += fH->GetBinContent(bin);}
         snprintf(info,200,"(x=%g, y=%g, binx=%d, binc=%g, Sum=%g)",
                  x,y,binx,fH->GetBinContent(binx),integ);
      }
   } else if (fH->GetDimension() == 2) {
      if (fH->InheritsFrom(TH2Poly::Class())) {
         TH2Poly *th2 = (TH2Poly*)fH;
         biny = th2->FindBin(x,y);
         snprintf(info,200,"%s (x=%g, y=%g, bin=%d, binc=%g)",
                  th2->GetBinTitle(biny),x,y,biny,th2->GetBinContent(biny));
      }
      else if (fH->InheritsFrom(TProfile2D::Class())) {
         TProfile2D *tp = (TProfile2D*)fH;
         biny = fYaxis->FindFixBin(y);
         Int_t bin = fH->GetBin(binx,biny);
         snprintf(info,200,"(x=%g, y=%g, binx=%d, biny=%d, binc=%g, bine=%g, binn=%d)",
                  x, y, binx, biny, fH->GetBinContent(bin),
                  fH->GetBinError(bin), (Int_t) tp->GetBinEntries(bin));
      } else {
         biny = fYaxis->FindFixBin(y);
         snprintf(info,200,"(x=%g, y=%g, binx=%d, biny=%d, binc=%g bine=%g)",
                  x,y,binx,biny,fH->GetBinContent(binx,biny),
                  fH->GetBinError(binx,biny));
      }
   } else {
      // 3d case: retrieving the x,y,z bin is not yet implemented
      // print just the x,y info
      snprintf(info,200,"(x=%g, y=%g)",x,y);
   }
   return info;
}


//______________________________________________________________________________
void THistPainter::SetHighlight()
{
   // Set highlight (enable/disble) mode for fH

   if (fH->IsHighlight()) return;

   fXHighlightBin = -1;
   fYHighlightBin = -1;

   // delete previous highlight box (recursive call PaintHighlightBin)
   gPad->Modified(kTRUE);

   // !!!!!!!!!!!!!!!!!
   // TPad::Paint in ROOT5 calling only if CanvasPreferGL=1
   // CanvasPreferGL=1   != CanvasPreferGL=1 for updating
   // !!!!!!!!!!!!!!!!!

   //   // delete previous highlight box
   //   TIter next(gROOT->GetListOfCanvases());
   //   TVirtualPad *pad = 0;
   //   while ((pad = (TVirtualPad *)next()))
   //      if (pad && pad->FindObject(fH)) pad->Paint(); // force all (sub)pads
}


//______________________________________________________________________________
void THistPainter::HighlightBin(Int_t px, Int_t py)
{
   // Check for highlight bin, only if highlight is enable
   // call from DistancetoPrimitive

   Double_t x = gPad->PadtoX(gPad->AbsPixeltoX(px));
   Double_t y = gPad->PadtoY(gPad->AbsPixeltoY(py));
   Int_t binx = fXaxis->FindFixBin(x);
   Int_t biny = fYaxis->FindFixBin(y);
   if (!gPad->IsVertical()) binx = fXaxis->FindFixBin(y);

   Bool_t changedBin = kFALSE;
   if (binx != fXHighlightBin) {
      fXHighlightBin = binx;
      changedBin = kTRUE;
   } else if (fH->GetDimension() == 1) return;
   if (biny != fYHighlightBin) {
      fYHighlightBin = biny;
      changedBin = kTRUE;
   }
   if (!changedBin) return;

   Info("HighlightBin", "histo: %p '%s'\txbin: %d, ybin: %d",
        (void *)fH, fH->GetName(), fXHighlightBin, fYHighlightBin);

   // paint highlight bin as box (recursive call PaintHighlightBin)
   gPad->Modified(kTRUE);
   gPad->Update();



   //if (gPad->GetCanvas()) gPad->GetCanvas()->Picked((TPad *)gPad, fH, 4321);
   //   gROOT->ProcessLine(Form("((TCanvas*)0x%lx)->Selected((TVirtualPad*)0x%lx,(TObject*)0x%lx,1)",
   //                           (ULong_t)gPad->GetCanvas(), (ULong_t)gPad, (ULong_t)fH));
}


//______________________________________________________________________________
void THistPainter::PaintHighlightBin(Option_t * /*option*/)
{
   Printf("%f %s PaintHLB %s", gRandom->Uniform(), fH->GetName(), gPad->GetName());

   // Paint highlight bin as TBox object, only if highlight is enable

   static TBox *xhbox = 0;
   static TBox *yhbox = 0;
   if (!fH->IsHighlight()) {
      if (xhbox) { xhbox->Delete(); xhbox = 0; }
      if (yhbox) { yhbox->Delete(); yhbox = 0; }
      return;
   }

   Double_t uxmin = gPad->GetUxmin();
   Double_t uxmax = gPad->GetUxmax();
   Double_t uymin = gPad->GetUymin();
   Double_t uymax = gPad->GetUymax();
   if (gPad->GetLogx()) {
      uxmin = TMath::Power(10.0, uxmin);
      uxmax = TMath::Power(10.0, uxmax);
   }
   if (gPad->GetLogy()) {
      uymin = TMath::Power(10.0, uymin);
      uymax = TMath::Power(10.0, uymax);
   }

   // testing specific possibility (after zoom, draw with "same", log, etc.)
   Double_t hcenter;
   if (gPad->IsVertical()) {
      hcenter = fXaxis->GetBinCenter(fXHighlightBin);
      if ((hcenter < uxmin) || (hcenter > uxmax)) return;
   } else {
      hcenter = fYaxis->GetBinCenter(fXHighlightBin);
      if ((hcenter < uymin) || (hcenter > uymax)) return;
   }
   if (fH->GetDimension() == 2) {
      hcenter = fYaxis->GetBinCenter(fYHighlightBin);
      if ((hcenter < uymin) || (hcenter > uymax)) return;
   }

   // paint X highlight bin (for 1D or 2D)
   Double_t hbx1, hbx2, hby1, hby2;
   if (gPad->IsVertical()) {
      hbx1 = fXaxis->GetBinLowEdge(fXHighlightBin);
      hbx2 = fXaxis->GetBinUpEdge(fXHighlightBin);
      hby1 = uymin;
      hby2 = uymax;
   } else {
      hbx1 = uxmin;
      hbx2 = uxmax;
      hby1 = fYaxis->GetBinLowEdge(fXHighlightBin);
      hby2 = fYaxis->GetBinUpEdge(fXHighlightBin);
   }

   if (!xhbox) {
      xhbox = new TBox(hbx1, hby1, hbx2, hby2);
      xhbox->SetBit(kCannotPick);
      xhbox->SetFillColor(TColor::GetColor("#9797ff"));
      if (!TCanvas::SupportAlpha()) xhbox->SetFillStyle(3001);
      else gROOT->GetColor(xhbox->GetFillColor())->SetAlpha(0.5);
   }
   xhbox->SetX1(hbx1);
   xhbox->SetX2(hbx2);
   xhbox->SetY1(hby1);
   xhbox->SetY2(hby2);
   xhbox->Paint();

   Info("PaintHighlightBin", "histo: %p '%s'\txbin: %d, ybin: %d",
        (void *)fH, fH->GetName(), fXHighlightBin, fYHighlightBin);

   // paint Y highlight bin (only for 2D)
   if (fH->GetDimension() != 2) return;
   hbx1 = uxmin;
   hbx2 = uxmax;
   hby1 = fYaxis->GetBinLowEdge(fYHighlightBin);
   hby2 = fYaxis->GetBinUpEdge(fYHighlightBin);

   if (!yhbox) {
      yhbox = new TBox(hbx1, hby1, hbx2, hby2);
      yhbox->SetBit(kCannotPick);
      yhbox->SetFillColor(xhbox->GetFillColor());
      yhbox->SetFillStyle(xhbox->GetFillStyle());
   }
   yhbox->SetX1(hbx1);
   yhbox->SetX2(hbx2);
   yhbox->SetY1(hby1);
   yhbox->SetY2(hby2);
   yhbox->Paint();
}


//______________________________________________________________________________
Bool_t THistPainter::IsInside(Int_t ix, Int_t iy)
{
   /* Begin_html
   Return kTRUE if the cell ix, iy is inside one of the graphical cuts.
   End_html */

   for (Int_t i=0;i<fNcuts;i++) {
      Double_t x = fXaxis->GetBinCenter(ix);
      Double_t y = fYaxis->GetBinCenter(iy);
      if (fCutsOpt[i] > 0) {
         if (!fCuts[i]->IsInside(x,y)) return kFALSE;
      } else {
         if (fCuts[i]->IsInside(x,y))  return kFALSE;
      }
   }
   return kTRUE;
}


//______________________________________________________________________________
Bool_t THistPainter::IsInside(Double_t x, Double_t y)
{
   /* Begin_html
   Return kTRUE if the point x,y is inside one of the graphical cuts.
   End_html */

   for (Int_t i=0;i<fNcuts;i++) {
      if (fCutsOpt[i] > 0) {
         if (!fCuts[i]->IsInside(x,y)) return kFALSE;
      } else {
         if (fCuts[i]->IsInside(x,y))  return kFALSE;
      }
   }
   return kTRUE;
}


//______________________________________________________________________________
Int_t THistPainter::MakeChopt(Option_t *choptin)
{
   /* Begin_html
   Decode string "choptin" and fill Hoption structure.
   End_html */

   char *l;
   char chopt[128];
   Int_t nch = strlen(choptin);
   strlcpy(chopt,choptin,128);
   Int_t hdim = fH->GetDimension();

   Hoption.Axis = Hoption.Bar    = Hoption.Curve   = Hoption.Error   = 0;
   Hoption.Hist = Hoption.Line   = Hoption.Mark    = Hoption.Fill    = 0;
   Hoption.Same = Hoption.Func   = Hoption.Scat                      = 0;
   Hoption.Star = Hoption.Arrow  = Hoption.Box     = Hoption.Text    = 0;
   Hoption.Char = Hoption.Color  = Hoption.Contour = Hoption.Logx    = 0;
   Hoption.Logy = Hoption.Logz   = Hoption.Lego    = Hoption.Surf    = 0;
   Hoption.Off  = Hoption.Tri    = Hoption.Proj    = Hoption.AxisPos = 0;
   Hoption.Spec = Hoption.Pie    = Hoption.Candle  = Hoption.Violin  = 0;

   //    special 2D options
   Hoption.List     = 0;
   Hoption.Zscale   = 0;
   Hoption.FrontBox = 1;
   Hoption.BackBox  = 1;
   Hoption.System   = kCARTESIAN;

   Hoption.Zero     = 0;

   //check for graphical cuts
   MakeCuts(chopt);

   for (Int_t i=0;i<nch;i++) chopt[i] = toupper(chopt[i]);
   if (hdim > 1) Hoption.Scat = 1;
   if (!nch) Hoption.Hist = 1;
   if (fFunctions->First()) Hoption.Func = 1;
   if (fH->GetSumw2N() && hdim == 1) Hoption.Error = 2;

   l = strstr(chopt,"SPEC");
   if (l) {
      Hoption.Scat = 0;
      strncpy(l,"    ",4);
      Int_t bs=0;
      l = strstr(chopt,"BF(");
      if (l) {
         if (sscanf(&l[3],"%d",&bs) > 0) {
            Int_t i=0;
            while (l[i]!=')') {
               l[i] = ' ';
               i++;
            }
            l[i] = ' ';
         }
      }
      Hoption.Spec = TMath::Max(1600,bs);
      return 1;
   }

   l = strstr(chopt,"GL");
   if (l) {
      strncpy(l,"  ",2);
   }
   l = strstr(chopt,"X+");
   if (l) {
      Hoption.AxisPos = 10;
      strncpy(l,"  ",2);
   }
   l = strstr(chopt,"Y+");
   if (l) {
      Hoption.AxisPos += 1;
      strncpy(l,"  ",2);
   }
   if ((Hoption.AxisPos == 10 || Hoption.AxisPos == 1) && (nch == 2)) Hoption.Hist = 1;
   if (Hoption.AxisPos == 11 && nch == 4) Hoption.Hist = 1;

   l = strstr(chopt,"SAMES");
   if (l) {
      if (nch == 5) Hoption.Hist = 1;
      Hoption.Same = 2;
      strncpy(l,"     ",5);
   }
   l = strstr(chopt,"SAME");
   if (l) {
      if (nch == 4) Hoption.Hist = 1;
      Hoption.Same = 1;
      strncpy(l,"    ",4);
   }

   l = strstr(chopt,"PIE");
   if (l) {
      Hoption.Pie = 1;
      strncpy(l,"   ",3);
   }

   l = strstr(chopt,"CANDLE");
   if (l) {
      Hoption.Scat = 0;
      Hoption.Candle = 1;
      strncpy(l,"   ",6);
      if (l[6] == 'X') { Hoption.Candle = 1; l[6] = ' '; }
      if (l[6] == 'Y') { Hoption.Candle = 2; l[6] = ' '; }
   }

   l = strstr(chopt,"VIOLIN");
   if (l) {
      Hoption.Scat = 0;
      Hoption.Violin = 1;
      strncpy(l,"   ",6);
      if (l[6] == 'X') { Hoption.Violin = 1; l[6] = ' '; }
      if (l[6] == 'Y') { Hoption.Violin = 2; l[6] = ' '; }
   }

   l = strstr(chopt,"LEGO");
   if (l) {
      Hoption.Scat = 0;
      Hoption.Lego = 1; strncpy(l,"    ",4);
      if (l[4] == '1') { Hoption.Lego = 11; l[4] = ' '; }
      if (l[4] == '2') { Hoption.Lego = 12; l[4] = ' '; }
      if (l[4] == '3') { Hoption.Lego = 13; l[4] = ' '; }
      if (l[4] == '4') { Hoption.Lego = 14; l[4] = ' '; }
      l = strstr(chopt,"FB"); if (l) { Hoption.FrontBox = 0; strncpy(l,"  ",2); }
      l = strstr(chopt,"BB"); if (l) { Hoption.BackBox = 0;  strncpy(l,"  ",2); }
      l = strstr(chopt,"0");  if (l) { Hoption.Zero = 1;  strncpy(l," ",1); }
   }

   l = strstr(chopt,"SURF");
   if (l) {
      Hoption.Scat = 0;
      Hoption.Surf = 1; strncpy(l,"    ",4);
      if (l[4] == '1') { Hoption.Surf = 11; l[4] = ' '; }
      if (l[4] == '2') { Hoption.Surf = 12; l[4] = ' '; }
      if (l[4] == '3') { Hoption.Surf = 13; l[4] = ' '; }
      if (l[4] == '4') { Hoption.Surf = 14; l[4] = ' '; }
      if (l[4] == '5') { Hoption.Surf = 15; l[4] = ' '; }
      if (l[4] == '6') { Hoption.Surf = 16; l[4] = ' '; }
      if (l[4] == '7') { Hoption.Surf = 17; l[4] = ' '; }
      l = strstr(chopt,"FB");   if (l) { Hoption.FrontBox = 0; strncpy(l,"  ",2); }
      l = strstr(chopt,"BB");   if (l) { Hoption.BackBox = 0;  strncpy(l,"  ",2); }
   }

   l = strstr(chopt,"TF3");
   if (l) {
      l = strstr(chopt,"FB");   if (l) { Hoption.FrontBox = 0; strncpy(l,"  ",2); }
      l = strstr(chopt,"BB");   if (l) { Hoption.BackBox = 0;  strncpy(l,"  ",2); }
   }

   l = strstr(chopt,"ISO");
   if (l) {
      l = strstr(chopt,"FB");   if (l) { Hoption.FrontBox = 0; strncpy(l,"  ",2); }
      l = strstr(chopt,"BB");   if (l) { Hoption.BackBox = 0;  strncpy(l,"  ",2); }
   }

   l = strstr(chopt,"LIST");    if (l) { Hoption.List = 1;  strncpy(l,"    ",4);}

   l = strstr(chopt,"CONT");
   if (l) {
      strncpy(l,"    ",4);
      if (hdim>1) {
         Hoption.Scat = 0;
         Hoption.Contour = 1;
         if (l[4] == '1') { Hoption.Contour = 11; l[4] = ' '; }
         if (l[4] == '2') { Hoption.Contour = 12; l[4] = ' '; }
         if (l[4] == '3') { Hoption.Contour = 13; l[4] = ' '; }
         if (l[4] == '4') { Hoption.Contour = 14; l[4] = ' '; }
         if (l[4] == '5') { Hoption.Contour = 15; l[4] = ' '; }
      } else {
         Hoption.Hist = 1;
      }
   }
   l = strstr(chopt,"HBAR");
   if (l) {
      Hoption.Hist = 0;
      Hoption.Bar = 20; strncpy(l,"    ",4);
      if (l[4] == '1') { Hoption.Bar = 21; l[4] = ' '; }
      if (l[4] == '2') { Hoption.Bar = 22; l[4] = ' '; }
      if (l[4] == '3') { Hoption.Bar = 23; l[4] = ' '; }
      if (l[4] == '4') { Hoption.Bar = 24; l[4] = ' '; }
   }
   l = strstr(chopt,"BAR");
   if (l) {
      Hoption.Hist = 0;
      Hoption.Bar = 10; strncpy(l,"   ",3);
      if (l[3] == '1') { Hoption.Bar = 11; l[3] = ' '; }
      if (l[3] == '2') { Hoption.Bar = 12; l[3] = ' '; }
      if (l[3] == '3') { Hoption.Bar = 13; l[3] = ' '; }
      if (l[3] == '4') { Hoption.Bar = 14; l[3] = ' '; }
   }

   l = strstr(chopt,"ARR" );
   if (l) {
      strncpy(l,"   ", 3);
      if (hdim>1) {
         Hoption.Arrow  = 1;
         Hoption.Scat = 0;
      } else {
         Hoption.Hist = 1;
      }
   }
   l = strstr(chopt,"BOX" );
   if (l) {
      strncpy(l,"   ", 3);
      if (hdim>1) {
         Hoption.Scat = 0;
         Hoption.Box  = 1;
         if (l[3] == '1') { Hoption.Box = 11; l[3] = ' '; }
      } else {
         Hoption.Hist = 1;
      }
   }
   l = strstr(chopt,"COLZ");
   if (l) {
      strncpy(l,"    ",4);
      if (hdim>1) {
         Hoption.Color  = 2;
         Hoption.Scat   = 0;
         Hoption.Zscale = 1;
      } else {
         Hoption.Hist = 1;
      }
   }
   l = strstr(chopt,"COL" );
   if (l) {
      strncpy(l,"   ", 3);
      if (hdim>1) {
         Hoption.Color = 1;
         Hoption.Scat  = 0;
      } else {
         Hoption.Hist = 1;
      }
   }
   l = strstr(chopt,"CHAR"); if (l) { Hoption.Char   = 1; strncpy(l,"    ",4); Hoption.Scat = 0; }
   l = strstr(chopt,"FUNC"); if (l) { Hoption.Func   = 2; strncpy(l,"    ",4); Hoption.Hist = 0; }
   l = strstr(chopt,"HIST"); if (l) { Hoption.Hist   = 2; strncpy(l,"    ",4); Hoption.Func = 0; Hoption.Error = 0;}
   l = strstr(chopt,"AXIS"); if (l) { Hoption.Axis   = 1; strncpy(l,"    ",4); }
   l = strstr(chopt,"AXIG"); if (l) { Hoption.Axis   = 2; strncpy(l,"    ",4); }
   l = strstr(chopt,"SCAT"); if (l) { Hoption.Scat   = 1; strncpy(l,"    ",4); }
   l = strstr(chopt,"TEXT");
   if (l) {
      Int_t angle;
      if (sscanf(&l[4],"%d",&angle) > 0) {
         if (angle < 0)  angle=0;
         if (angle > 90) angle=90;
         Hoption.Text = 1000+angle;
      } else {
         Hoption.Text = 1;
      }
      strncpy(l,"    ", 4);
      l = strstr(chopt,"N");
      if (l && fH->InheritsFrom(TH2Poly::Class())) Hoption.Text += 3000;
      Hoption.Scat = 0;
   }
   l = strstr(chopt,"POL");  if (l) { Hoption.System = kPOLAR;       strncpy(l,"   ",3); }
   l = strstr(chopt,"CYL");  if (l) { Hoption.System = kCYLINDRICAL; strncpy(l,"   ",3); }
   l = strstr(chopt,"SPH");  if (l) { Hoption.System = kSPHERICAL;   strncpy(l,"   ",3); }
   l = strstr(chopt,"PSR");  if (l) { Hoption.System = kRAPIDITY;    strncpy(l,"   ",3); }

   l = strstr(chopt,"TRI");
   if (l) {
      Hoption.Scat = 0;
      Hoption.Color  = 0;
      Hoption.Tri = 1; strncpy(l,"   ",3);
      l = strstr(chopt,"FB");   if (l) { Hoption.FrontBox = 0; strncpy(l,"  ",2); }
      l = strstr(chopt,"BB");   if (l) { Hoption.BackBox = 0;  strncpy(l,"  ",2); }
      l = strstr(chopt,"ERR");  if (l) strncpy(l,"   ",3);
   }

   l = strstr(chopt,"AITOFF");
   if (l) {
      Hoption.Proj = 1; strncpy(l,"     ",6);       //Aitoff projection
   }
   l = strstr(chopt,"MERCATOR");
   if (l) {
      Hoption.Proj = 2; strncpy(l,"       ",8);     //Mercator projection
   }
   l = strstr(chopt,"SINUSOIDAL");
   if (l) {
      Hoption.Proj = 3; strncpy(l,"         ",10);  //Sinusoidal projection
   }
   l = strstr(chopt,"PARABOLIC");
   if (l) {
      Hoption.Proj = 4; strncpy(l,"        ",9);    //Parabolic projection
   }
   if (Hoption.Proj > 0) {
      Hoption.Scat = 0;
      Hoption.Contour = 14;
   }

   if (strstr(chopt,"A"))   Hoption.Axis = -1;
   if (strstr(chopt,"B"))   Hoption.Bar  = 1;
   if (strstr(chopt,"C")) { Hoption.Curve =1; Hoption.Hist = -1;}
   if (strstr(chopt,"F"))   Hoption.Fill =1;
   if (strstr(chopt,"][")) {Hoption.Off  =1; Hoption.Hist =1;}
   if (strstr(chopt,"F2"))  Hoption.Fill =2;
   if (strstr(chopt,"L")) { Hoption.Line =1; Hoption.Hist = -1;}
   if (strstr(chopt,"P")) { Hoption.Mark =1; Hoption.Hist = -1;}
   if (strstr(chopt,"Z"))   Hoption.Zscale =1;
   if (strstr(chopt,"*"))   Hoption.Star =1;
   if (strstr(chopt,"H"))   Hoption.Hist =2;
   if (strstr(chopt,"P0"))  Hoption.Mark =10;

   if (fH->InheritsFrom(TH2Poly::Class())) {
      if (Hoption.Fill+Hoption.Line+Hoption.Mark != 0 ) Hoption.Scat = 0;
   }

   if (strstr(chopt,"E")) {
      if (hdim == 1) {
         Hoption.Error = 1;
         if (strstr(chopt,"E0"))  Hoption.Error = 10;
         if (strstr(chopt,"E1"))  Hoption.Error = 11;
         if (strstr(chopt,"E2"))  Hoption.Error = 12;
         if (strstr(chopt,"E3"))  Hoption.Error = 13;
         if (strstr(chopt,"E4"))  Hoption.Error = 14;
         if (strstr(chopt,"E5"))  Hoption.Error = 15;
         if (strstr(chopt,"E6"))  Hoption.Error = 16;
         if (strstr(chopt,"X0")) {
            if (Hoption.Error == 1)  Hoption.Error += 20;
            Hoption.Error += 10;
         }
         if (Hoption.Text && fH->InheritsFrom(TProfile::Class())) {
            Hoption.Text += 2000;
            Hoption.Error = 0;
         }
      } else {
         if (Hoption.Error == 0) {
            Hoption.Error = 100;
            Hoption.Scat  = 0;
         }
         if (Hoption.Text) {
            Hoption.Text += 2000;
            Hoption.Error = 0;
         }
      }
   }

   if (Hoption.Surf == 15) {
      if (Hoption.System == kPOLAR || Hoption.System == kCARTESIAN) {
         Hoption.Surf = 13;
         Warning("MakeChopt","option SURF5 is not supported in Cartesian and Polar modes");
      }
   }

   //      Copy options from current style
   Hoption.Logx = gPad->GetLogx();
   Hoption.Logy = gPad->GetLogy();
   Hoption.Logz = gPad->GetLogz();

   //       Check options incompatibilities
   if (Hoption.Bar  == 1) Hoption.Hist = -1;
   return 1;
}


//______________________________________________________________________________
Int_t THistPainter::MakeCuts(char *choptin)
{
   /* Begin_html
   Decode string "choptin" and fill Graphical cuts structure.
   End_html */

   fNcuts = 0;
   char *left = (char*)strchr(choptin,'[');
   if (!left) return 0;
   char *right = (char*)strchr(choptin,']');
   if (!right) return 0;
   Int_t nch = right-left;
   if (nch < 2) return 0;
   char *cuts = left+1;
   *right = 0;
   char *comma, *minus;
   Int_t i;
   while (1) {
      comma = strchr(cuts,',');
      if (comma) *comma = 0;
      minus = strchr(cuts,'-');
      if (minus) cuts = minus+1;
      while (*cuts == ' ') cuts++;
      Int_t nc = strlen(cuts);
      while (cuts[nc-1] == ' ') {cuts[nc-1] = 0; nc--;}
      TIter next(gROOT->GetListOfSpecials());
      TCutG *cut=0;
      TObject *obj;
      while ((obj = next())) {
         if (!obj->InheritsFrom(TCutG::Class())) continue;
         if (strcmp(obj->GetName(),cuts)) continue;
         cut = (TCutG*)obj;
         break;
      }
      if (cut) {
         fCuts[fNcuts] = cut;
         fCutsOpt[fNcuts] = 1;
         if (minus) fCutsOpt[fNcuts] = -1;
         fNcuts++;
      }
      if (!comma) break;
      cuts = comma+1;
   }
   for (i=0;i<=nch;i++) left[i] = ' ';
   return fNcuts;
}


//______________________________________________________________________________
void THistPainter::Paint(Option_t *option)
{
   /* Begin_Html
   <a href="#HP00">Control routine to paint any kind of histograms.</a>
   End_html */

   if (fH->GetBuffer()) fH->BufferEmpty(-1);

   //For iOS: put the histogram on the top of stack of pickable objects.
   const TPickerStackGuard topPush(fH);

   gPad->SetVertical(kTRUE);

   TH1 *oldhist = gCurrentHist;
   gCurrentHist = fH;
   TH1 *hsave   = fH;
   Double_t minsav = fH->GetMinimumStored();

   if (!MakeChopt(option)) return; //check options and fill Hoption structure

   // Paint using TSpectrum2Painter
   if (Hoption.Spec) {
      if (!TableInit()) return;
      if (!TClass::GetClass("TSpectrum2Painter")) gSystem->Load("libSpectrumPainter");
      gROOT->ProcessLineFast(Form("TSpectrum2Painter::PaintSpectrum((TH2F*)0x%lx,\"%s\",%d)",
                                  (ULong_t)fH, option, Hoption.Spec));
      return;
   }

   if (Hoption.Pie) {
      if (fH->GetDimension() == 1) {
         if (!fPie) fPie = new TPie(fH);
         fPie->Paint(option);
      } else {
         Error("Paint", "Option PIE is for 1D histograms only");
      }
      return;
   } else {
      if (fPie) delete fPie;
      fPie = 0;
   }

   fXbuf  = new Double_t[kNMAX];
   fYbuf  = new Double_t[kNMAX];
   if (fH->GetDimension() > 2) {
      PaintH3(option);
      fH->SetMinimum(minsav);
      if (Hoption.Func) {
         Hoption_t hoptsave = Hoption;
         Hparam_t  hparsave = Hparam;
         PaintFunction(option);
         SetHistogram(hsave);
         Hoption = hoptsave;
         Hparam  = hparsave;
      }
      gCurrentHist = oldhist;
      delete [] fXbuf; delete [] fYbuf;
      return;
   }
   TView *view = gPad->GetView();
   if (view) {
      if (!Hoption.Lego && !Hoption.Surf && !Hoption.Tri) {
         delete view;
         gPad->SetView(0);
      }
   }
   if (fH->GetDimension() > 1 || Hoption.Lego || Hoption.Surf) {
      // In case of 1D histogram, Z axis becomes Y axis.
      Int_t logysav=0, logzsav=0;
      if (fH->GetDimension() == 1) {
         logysav = Hoption.Logy;
         logzsav = Hoption.Logz;
         Hoption.Logz = 0;
         if (Hoption.Logy) {
            Hoption.Logz = 1;
            Hoption.Logy = 0;
         }
      }
      PaintTable(option);
      fH->SetMinimum(minsav);
      if (Hoption.Func) {
         Hoption_t hoptsave = Hoption;
         Hparam_t  hparsave = Hparam;
         PaintFunction(option);
         SetHistogram(hsave);
         Hoption = hoptsave;
         Hparam  = hparsave;
      }
      gCurrentHist = oldhist;
      delete [] fXbuf; delete [] fYbuf;
      if (fH->GetDimension() == 1) {
         Hoption.Logy = logysav;
         Hoption.Logz = logzsav;
      }
      return;
   }

   if (Hoption.Bar >= 20) {PaintBarH(option);
      delete [] fXbuf; delete [] fYbuf;
      return;
   }

   // fill Hparam structure with histo parameters
   if (!PaintInit()) {
      delete [] fXbuf; delete [] fYbuf;
      return;
   }

   //          Picture surround (if new page) and page number (if requested).
   //          Histogram surround (if not option "Same").
   PaintFrame();

   //          Paint histogram axis only
   Bool_t gridx = gPad->GetGridx();
   Bool_t gridy = gPad->GetGridy();
   if (Hoption.Axis > 0) {
      if (Hoption.Axis > 1) PaintAxis(kTRUE);  //axis with grid
      else {
         if (gridx) gPad->SetGridx(0);
         if (gridy) gPad->SetGridy(0);
         PaintAxis(kFALSE);
         if (gridx) gPad->SetGridx(1);
         if (gridy) gPad->SetGridy(1);
      }
      if (Hoption.Same ==1) Hoption.Same = 2;
      goto paintstat;
   }
   if (gridx || gridy) PaintAxis(kTRUE); //    Draw the grid only

   //          test for options BAR or HBAR
   if (Hoption.Bar >= 10) {
      PaintBar(option);
   }

   //          do not draw histogram if error bars required
   if (!Hoption.Error) {
      if (Hoption.Hist && Hoption.Bar<10) PaintHist(option);
   }

   //         test for error bars or option E
   if (Hoption.Error) {
      PaintErrors(option);
      if (Hoption.Hist == 2) PaintHist(option);
   }

   if (Hoption.Text) PaintText(option);

   //         test for associated function
   if (Hoption.Func) {
      Hoption_t hoptsave = Hoption;
      Hparam_t  hparsave = Hparam;
      PaintFunction(option);
      SetHistogram(hsave);
      Hoption = hoptsave;
      Hparam  = hparsave;
   }

   if (gridx) gPad->SetGridx(0);
   if (gridy) gPad->SetGridy(0);
   PaintAxis(kFALSE);
   if (gridx) gPad->SetGridx(1);
   if (gridy) gPad->SetGridy(1);

   PaintTitle();  // Draw histogram title

   // Draw box with histogram statistics and/or fit parameters
paintstat:
   if (Hoption.Same != 1 && !fH->TestBit(TH1::kNoStats)) {  // bit set via TH1::SetStats
      TIter next(fFunctions);
      TObject *obj = 0;
      while ((obj = next())) {
         if (obj->InheritsFrom(TF1::Class())) break;
         obj = 0;
      }

      //Stat is painted twice (first, it will be in canvas' list of primitives),
      //second, it will be here, this is not required on iOS.
      //Condition is ALWAYS true on a platform different from iOS.
      if (!gPad->PadInSelectionMode() && !gPad->PadInHighlightMode())
         PaintStat(gStyle->GetOptStat(),(TF1*)obj);
   }
   fH->SetMinimum(minsav);
   gCurrentHist = oldhist;
   delete [] fXbuf; fXbuf = 0;
   delete [] fYbuf; fYbuf = 0;

}


//______________________________________________________________________________
void THistPainter::PaintArrows(Option_t *)
{
   /* Begin_html
   <a href="#HP12">Control function to draw a table as an arrow plot.</a>
   End_html */

   Style_t linesav   = fH->GetLineStyle();
   Width_t widthsav  = fH->GetLineWidth();
   fH->SetLineStyle(1);
   fH->SetLineWidth(1);
   fH->TAttLine::Modify();

   Double_t xk, xstep, yk, ystep;
   Double_t dx, dy, si, co, anr, x1, x2, y1, y2, xc, yc, dxn, dyn;
   Int_t   ncx  = Hparam.xlast - Hparam.xfirst + 1;
   Int_t   ncy  = Hparam.ylast - Hparam.yfirst + 1;
   Double_t xrg = gPad->GetUxmin();
   Double_t yrg = gPad->GetUymin();
   Double_t xln = gPad->GetUxmax() - xrg;
   Double_t yln = gPad->GetUymax() - yrg;
   Double_t cx  = (xln/Double_t(ncx) -0.03)/2;
   Double_t cy  = (yln/Double_t(ncy) -0.03)/2;
   Double_t dn  = 1.E-30;

   for (Int_t id=1;id<=2;id++) {
      for (Int_t j=Hparam.yfirst; j<=Hparam.ylast;j++) {
         yk    = fYaxis->GetBinLowEdge(j);
         ystep = fYaxis->GetBinWidth(j);
         for (Int_t i=Hparam.xfirst; i<=Hparam.xlast;i++) {
            xk    = fXaxis->GetBinLowEdge(i);
            xstep = fXaxis->GetBinWidth(i);
            if (!IsInside(xk+0.5*xstep,yk+0.5*ystep)) continue;
            if (i == Hparam.xfirst) {
               dx = fH->GetBinContent(i+1, j) - fH->GetBinContent(i, j);
            } else if (i == Hparam.xlast) {
               dx = fH->GetBinContent(i, j) - fH->GetBinContent(i-1, j);
            } else {
               dx = 0.5*(fH->GetBinContent(i+1, j) - fH->GetBinContent(i-1, j));
            }
            if (j == Hparam.yfirst) {
               dy = fH->GetBinContent(i, j+1) - fH->GetBinContent(i, j);
            } else if (j == Hparam.ylast) {
               dy = fH->GetBinContent(i, j) - fH->GetBinContent(i, j-1);
            } else {
               dy = 0.5*(fH->GetBinContent(i, j+1) - fH->GetBinContent(i, j-1));
            }
            if (id == 1) {
               dn = TMath::Max(dn, TMath::Abs(dx));
               dn = TMath::Max(dn, TMath::Abs(dy));
            } else if (id == 2) {
               xc  = xrg + xln*(Double_t(i - Hparam.xfirst+1)-0.5)/Double_t(ncx);
               dxn = cx*dx/dn;
               x1  = xc - dxn;
               x2  = xc + dxn;
               yc  = yrg + yln*(Double_t(j - Hparam.yfirst+1)-0.5)/Double_t(ncy);
               dyn = cy*dy/dn;
               y1  = yc - dyn;
               y2  = yc + dyn;
               fXbuf[0] = x1;
               fXbuf[1] = x2;
               fYbuf[0] = y1;
               fYbuf[1] = y2;
               if (TMath::Abs(x2-x1) > 0.01 || TMath::Abs(y2-y1) > 0.01) {
                  anr = 0.005*.5*TMath::Sqrt(2/(dxn*dxn + dyn*dyn));
                  si  = anr*(dxn + dyn);
                  co  = anr*(dxn - dyn);
                  fXbuf[2] = x2 - si;
                  fYbuf[2] = y2 + co;
                  gPad->PaintPolyLine(3, fXbuf, fYbuf);
                  fXbuf[0] = x2;
                  fXbuf[1] = x2 - co;
                  fYbuf[0] = y2;
                  fYbuf[1] = y2 - si;
                  gPad->PaintPolyLine(2, fXbuf, fYbuf);
               }
               else {
                  gPad->PaintPolyLine(2, fXbuf, fYbuf);
               }
            }
         }
      }
   }

   if (Hoption.Zscale) PaintPalette();
   fH->SetLineStyle(linesav);
   fH->SetLineWidth(widthsav);
   fH->TAttLine::Modify();
}


//______________________________________________________________________________
void THistPainter::PaintAxis(Bool_t drawGridOnly)
{
   /* Begin_html
   Draw axis (2D case) of an histogram.
   <p>
   If drawGridOnly is TRUE, only the grid is painted (if needed). This allows
   to draw the grid and the axis separately. In THistPainter::Paint this
   feature is used to make sure that the grid is drawn in the background and
   the axis tick marks in the foreground of the pad.
   End_html */

   //On iOS, grid should not be picable and can not be highlighted.
   //Condition is never true on a platform different from iOS.
   if (drawGridOnly && (gPad->PadInHighlightMode() || gPad->PadInSelectionMode()))
      return;

   if (Hoption.Axis == -1) return;
   if (Hoption.Same && Hoption.Axis <= 0) return;

   // Repainting alphanumeric labels axis on a plot done with
   // the option HBAR (horizontal) needs some adjustements.
   TAxis *xaxis = 0;
   TAxis *yaxis = 0;
   if (Hoption.Same && Hoption.Axis) { // Axis repainted (TPad::RedrawAxis)
      if (fXaxis->GetLabels() || fYaxis->GetLabels()) { // One axis has alphanumeric labels
         TIter next(gPad->GetListOfPrimitives());
         TObject *obj;
         // Check if the first TH1 of THStack in the pad is drawn with the option HBAR
         while ((obj = next())) {
            if (!obj->InheritsFrom(TH1::Class()) &&
                !obj->InheritsFrom(THStack::Class())) continue;
            TString opt = obj->GetDrawOption();
            opt.ToLower();
            // if drawn with HBAR, the axis should be inverted and the pad set to horizontal
            if (strstr(opt,"hbar")) {
               gPad->SetVertical(kFALSE);
               xaxis = fXaxis;
               yaxis = fYaxis;
               if (!strcmp(xaxis->GetName(),"xaxis")) {
                  fXaxis = yaxis;
                  fYaxis = xaxis;
               }
            }
            break;
         }
      }
   }

   static char chopt[10] = "";
   Double_t gridl = 0;
   Int_t ndiv, ndivx, ndivy, nx1, nx2, ndivsave;
   Int_t useHparam = 0;
   Double_t umin, umax, uminsave, umaxsave;
   Short_t xAxisPos = Hoption.AxisPos/10;
   Short_t yAxisPos = Hoption.AxisPos - 10*xAxisPos;

   Double_t axmin = gPad->GetUxmin();
   Double_t axmax = gPad->GetUxmax();
   Double_t aymin = gPad->GetUymin();
   Double_t aymax = gPad->GetUymax();
   char *cw = 0;
   TGaxis axis;

   // In case of option 'cont4' or in case of option 'same' over a 'cont4 plot'
   // Hparam must be use for the axis limits.
   if (Hoption.Contour == 14) useHparam = 1;
   if (Hoption.Same) {
      TObject *obj;
      TIter next(gPad->GetListOfPrimitives());
      while ((obj=next())) {
         if (strstr(obj->GetDrawOption(),"cont4")) {
            useHparam = 1;
            break;
         }
      }
   }

   // Paint X axis

   //To make X-axis selectable on iOS device.
   if (gPad->PadInSelectionMode())
      gPad->PushSelectableObject(fXaxis);

   //This condition is ALWAYS true, unless it works on iOS (can be false on iOS).
   if (gPad->PadInSelectionMode() || !gPad->PadInHighlightMode() || (gPad->PadInHighlightMode() && gPad->GetSelected() == fXaxis)) {
      ndivx = fXaxis->GetNdivisions();
      if (ndivx > 1000) {
         nx2   = ndivx/100;
         nx1   = TMath::Max(1, ndivx%100);
         ndivx = 100*nx2 + Int_t(Float_t(nx1)*gPad->GetAbsWNDC());
      }
      axis.SetTextAngle(0);
      axis.ImportAxisAttributes(fXaxis);

      chopt[0] = 0;
      // coverity [Calling risky function]
      strlcat(chopt, "SDH",10);
      // coverity [Calling risky function]
      if (ndivx < 0) strlcat(chopt, "N",10);
      if (gPad->GetGridx()) {
         gridl = (aymax-aymin)/(gPad->GetY2() - gPad->GetY1());
         // coverity [Calling risky function]
         strlcat(chopt, "W",10);
      }

      // Define X-Axis limits
      if (Hoption.Logx) {
         // coverity [Calling risky function]
         strlcat(chopt, "G",10);
         ndiv = TMath::Abs(ndivx);
         if (useHparam) {
            umin = TMath::Power(10,Hparam.xmin);
            umax = TMath::Power(10,Hparam.xmax);
         } else {
            umin = TMath::Power(10,axmin);
            umax = TMath::Power(10,axmax);
         }
      } else {
         ndiv = TMath::Abs(ndivx);
         if (useHparam) {
            umin = Hparam.xmin;
            umax = Hparam.xmax;
         } else {
            umin = axmin;
            umax = axmax;
         }
      }

      // Display axis as time
      if (fXaxis->GetTimeDisplay()) {
         // coverity [Calling risky function]
         strlcat(chopt,"t",10);
         if (strlen(fXaxis->GetTimeFormatOnly()) == 0) {
            axis.SetTimeFormat(fXaxis->ChooseTimeFormat(Hparam.xmax-Hparam.xmin));
         }
      }

      // The main X axis can be on the bottom or on the top of the pad
      Double_t xAxisYPos1, xAxisYPos2;
      if (xAxisPos == 1) {
         // Main X axis top
         xAxisYPos1 = aymax;
         xAxisYPos2 = aymin;
      } else {
         // Main X axis bottom
         xAxisYPos1 = aymin;
         xAxisYPos2 = aymax;
      }

      // Paint the main X axis (always)
      uminsave = umin;
      umaxsave = umax;
      ndivsave = ndiv;
      axis.SetOption(chopt);
      if (xAxisPos) {
         // coverity [Calling risky function]
         strlcat(chopt, "-",10);
         gridl = -gridl;
      }
      if (Hoption.Same && Hoption.Axis) { // Axis repainted (TPad::RedrawAxis)
         axis.SetLabelSize(0.);
         axis.SetTitle("");
      }
      axis.PaintAxis(axmin, xAxisYPos1,
                     axmax, xAxisYPos1,
                     umin, umax,  ndiv, chopt, gridl, drawGridOnly);

      // Paint additional X axis (if needed)
      // On iOS, this additional X axis is neither pickable, nor highlighted.
      // Additional checks PadInSelectionMode etc. does not effect non-iOS platform.
      if (gPad->GetTickx() && !gPad->PadInSelectionMode() && !gPad->PadInHighlightMode()) {
         if (xAxisPos) {
            cw=strstr(chopt,"-");
            *cw='z';
         } else {
            // coverity [Calling risky function]
            strlcat(chopt, "-",10);
         }
         // coverity [Calling risky function]
         if (gPad->GetTickx() < 2) strlcat(chopt, "U",10);
         if ((cw=strstr(chopt,"W"))) *cw='z';
         axis.SetTitle("");
         axis.PaintAxis(axmin, xAxisYPos2,
                        axmax, xAxisYPos2,
                        uminsave, umaxsave,  ndivsave, chopt, gridl, drawGridOnly);
      }
   }//End of "if pad in selection mode etc".

   // Paint Y axis
   //On iOS, Y axis must pushed into the stack of selectable objects.
   if (gPad->PadInSelectionMode())
      gPad->PushSelectableObject(fYaxis);

   //This conditions is ALWAYS true on a platform, different from iOS (on iOS can be true, can be false).
   if (gPad->PadInSelectionMode() || !gPad->PadInHighlightMode() || (gPad->PadInHighlightMode() && gPad->GetSelected() == fYaxis)) {
      ndivy = fYaxis->GetNdivisions();
      axis.ImportAxisAttributes(fYaxis);

      chopt[0] = 0;
      // coverity [Calling risky function]
      strlcat(chopt, "SDH",10);
      // coverity [Calling risky function]
      if (ndivy < 0) strlcat(chopt, "N",10);
      if (gPad->GetGridy()) {
         gridl = (axmax-axmin)/(gPad->GetX2() - gPad->GetX1());
         // coverity [Calling risky function]
         strlcat(chopt, "W",10);
      }

      // Define Y-Axis limits
      if (Hoption.Logy) {
         // coverity [Calling risky function]
         strlcat(chopt, "G",10);
         ndiv = TMath::Abs(ndivy);
         if (useHparam) {
            umin = TMath::Power(10,Hparam.ymin);
            umax = TMath::Power(10,Hparam.ymax);
         } else {
            umin = TMath::Power(10,aymin);
            umax = TMath::Power(10,aymax);
         }
      } else {
         ndiv = TMath::Abs(ndivy);
         if (useHparam) {
            umin = Hparam.ymin;
            umax = Hparam.ymax;
         } else {
            umin = aymin;
            umax = aymax;
         }
      }

      // Display axis as time
      if (fYaxis->GetTimeDisplay()) {
         // coverity [Calling risky function]
         strlcat(chopt,"t",10);
         if (strlen(fYaxis->GetTimeFormatOnly()) == 0) {
            axis.SetTimeFormat(fYaxis->ChooseTimeFormat(Hparam.ymax-Hparam.ymin));
         }
      }

      // The main Y axis can be on the left or on the right of the pad
      Double_t yAxisXPos1, yAxisXPos2;
      if (yAxisPos == 1) {
         // Main Y axis left
         yAxisXPos1 = axmax;
         yAxisXPos2 = axmin;
      } else {
         // Main Y axis right
         yAxisXPos1 = axmin;
         yAxisXPos2 = axmax;
      }

      // Paint the main Y axis (always)
      uminsave = umin;
      umaxsave = umax;
      ndivsave = ndiv;
      axis.SetOption(chopt);
      if (yAxisPos) {
         // coverity [Calling risky function]
         strlcat(chopt, "+L",10);
         gridl = -gridl;
      }
      if (Hoption.Same && Hoption.Axis) { // Axis repainted (TPad::RedrawAxis)
         axis.SetLabelSize(0.);
         axis.SetTitle("");
      }
      axis.PaintAxis(yAxisXPos1, aymin,
                     yAxisXPos1, aymax,
                     umin, umax,  ndiv, chopt, gridl, drawGridOnly);

      // Paint the additional Y axis (if needed)
      // Additional checks for pad mode are required on iOS: this "second" axis is
      // neither pickable, nor highlihted. Additional checks have no effect on non-iOS platform.
      if (gPad->GetTicky() && !gPad->PadInSelectionMode() && !gPad->PadInHighlightMode()) {
         if (gPad->GetTicky() < 2) {
            // coverity [Calling risky function]
            strlcat(chopt, "U",10);
            axis.SetTickSize(-fYaxis->GetTickLength());
         } else {
            // coverity [Calling risky function]
            strlcat(chopt, "+L",10);
         }
         if ((cw=strstr(chopt,"W"))) *cw='z';
         axis.SetTitle("");
         axis.PaintAxis(yAxisXPos2, aymin,
                        yAxisXPos2, aymax,
                        uminsave, umaxsave,  ndivsave, chopt, gridl, drawGridOnly);
      }
   }//End of "if pad is in selection mode etc."

   // Reset the axis if they have been inverted in case of option HBAR
   if (xaxis) {
      fXaxis = xaxis;
      fYaxis = yaxis;
   }
}


//______________________________________________________________________________
void THistPainter::PaintBar(Option_t *)
{
   /* Begin_html
   <a href="#HP10">Draw a bar-chart in a normal pad.</a>
   End_html */

   Int_t bar = Hoption.Bar - 10;
   Double_t xmin,xmax,ymin,ymax,umin,umax,w,y;
   Double_t offset = fH->GetBarOffset();
   Double_t width  = fH->GetBarWidth();
   TBox box;
   Int_t hcolor = fH->GetFillColor();
   if (hcolor == gPad->GetFrameFillColor()) ++hcolor;
   Int_t hstyle = fH->GetFillStyle();
   box.SetFillColor(hcolor);
   box.SetFillStyle(hstyle);
   for (Int_t bin=fXaxis->GetFirst();bin<=fXaxis->GetLast();bin++) {
      y    = fH->GetBinContent(bin);
      xmin = gPad->XtoPad(fXaxis->GetBinLowEdge(bin));
      xmax = gPad->XtoPad(fXaxis->GetBinUpEdge(bin));
      ymin = gPad->GetUymin();
      ymax = gPad->YtoPad(y);
      if (ymax < gPad->GetUymin()) continue;
      if (ymax > gPad->GetUymax()) ymax = gPad->GetUymax();
      if (ymin < gPad->GetUymin()) ymin = gPad->GetUymin();
      if (gStyle->GetHistMinimumZero() && ymin < 0)
         ymin=TMath::Min(0.,gPad->GetUymax());
      w    = (xmax-xmin)*width;
      xmin += offset*(xmax-xmin);
      xmax = xmin + w;
      if (bar < 1) {
         box.PaintBox(xmin,ymin,xmax,ymax);
      } else {
         umin = xmin + bar*(xmax-xmin)/10.;
         umax = xmax - bar*(xmax-xmin)/10.;
         //box.SetFillColor(hcolor+150); //bright
         box.SetFillColor(TColor::GetColorBright(hcolor)); //bright
         box.PaintBox(xmin,ymin,umin,ymax);
         box.SetFillColor(hcolor);
         box.PaintBox(umin,ymin,umax,ymax);
         box.SetFillColor(TColor::GetColorDark(hcolor)); //dark
         box.PaintBox(umax,ymin,xmax,ymax);
      }
   }
}


//______________________________________________________________________________
void THistPainter::PaintBarH(Option_t *)
{
   /* Begin_html
   <a href="#HP10">Draw a bar char in a rotated pad (X vertical, Y horizontal).</a>
   End_html */

   gPad->SetVertical(kFALSE);

   PaintInitH();

   TAxis *xaxis = fXaxis;
   TAxis *yaxis = fYaxis;
   if (!strcmp(xaxis->GetName(),"xaxis")) {
      fXaxis = yaxis;
      fYaxis = xaxis;
   }

   PaintFrame();

   Int_t bar = Hoption.Bar - 20;
   Double_t xmin,xmax,ymin,ymax,umin,umax,w;
   Double_t offset = fH->GetBarOffset();
   Double_t width  = fH->GetBarWidth();
   TBox box;
   Int_t hcolor = fH->GetFillColor();
   if (hcolor == gPad->GetFrameFillColor()) ++hcolor;
   Int_t hstyle = fH->GetFillStyle();
   box.SetFillColor(hcolor);
   box.SetFillStyle(hstyle);
   for (Int_t bin=fYaxis->GetFirst();bin<=fYaxis->GetLast();bin++) {
      ymin = gPad->YtoPad(fYaxis->GetBinLowEdge(bin));
      ymax = gPad->YtoPad(fYaxis->GetBinUpEdge(bin));
      xmin = gPad->GetUxmin();
      xmax = gPad->XtoPad(fH->GetBinContent(bin));
      if (xmax < gPad->GetUxmin()) continue;
      if (xmax > gPad->GetUxmax()) xmax = gPad->GetUxmax();
      if (xmin < gPad->GetUxmin()) xmin = gPad->GetUxmin();
      if (gStyle->GetHistMinimumZero() && xmin < 0)
         xmin=TMath::Min(0.,gPad->GetUxmax());
      w    = (ymax-ymin)*width;
      ymin += offset*(ymax-ymin);
      ymax = ymin + w;
      if (bar < 1) {
         box.PaintBox(xmin,ymin,xmax,ymax);
      } else {
         umin = ymin + bar*(ymax-ymin)/10.;
         umax = ymax - bar*(ymax-ymin)/10.;
         box.SetFillColor(TColor::GetColorDark(hcolor)); //dark
         box.PaintBox(xmin,ymin,xmax,umin);
         box.SetFillColor(hcolor);
         box.PaintBox(xmin,umin,xmax,umax);
         box.SetFillColor(TColor::GetColorBright(hcolor)); //bright
         box.PaintBox(xmin,umax,xmax,ymax);
      }
   }

   PaintTitle();
   //    Draw box with histogram statistics and/or fit parameters
   if (Hoption.Same != 1 && !fH->TestBit(TH1::kNoStats)) {  // bit set via TH1::SetStats
      TIter next(fFunctions);
      TObject *obj = 0;
      while ((obj = next())) {
         if (obj->InheritsFrom(TF1::Class())) break;
         obj = 0;
      }
      PaintStat(gStyle->GetOptStat(),(TF1*)obj);
   }

   PaintAxis(kFALSE);
   fXaxis = xaxis;
   fYaxis = yaxis;
}


//______________________________________________________________________________
void THistPainter::PaintBoxes(Option_t *)
{
   /* Begin_html
   <a href="#HP13">Control function to draw a 2D histogram as a box plot.</a>
   End_html */

   Style_t fillsav   = fH->GetFillStyle();
   Style_t colsav    = fH->GetFillColor();
   if (fH->GetFillColor() == 0)  fH->SetFillStyle(0);
   if (Hoption.Box == 11) fH->SetFillStyle(1001);
   fH->TAttLine::Modify();
   fH->TAttFill::Modify();

   Double_t z, xk,xstep, yk, ystep, xcent, ycent, xlow, xup, ylow, yup;
   Double_t ux1 = gPad->PixeltoX(1);
   Double_t ux0 = gPad->PixeltoX(0);
   Double_t uy1 = gPad->PixeltoY(1);
   Double_t uy0 = gPad->PixeltoY(0);
   Double_t dxmin = 0.51*(gPad->PadtoX(ux1)-gPad->PadtoX(ux0));
   Double_t dymin = 0.51*(gPad->PadtoY(uy0)-gPad->PadtoY(uy1));

   Double_t zmin = fH->GetMinimum();
   Double_t zmax = TMath::Max(TMath::Abs(fH->GetMaximum()),
                              TMath::Abs(fH->GetMinimum()));

   // In case of option SAME, zmin and zmax values are taken from the
   // first plotted 2D histogram.
   if (Hoption.Same) {
      TH2 *h2;
      TIter next(gPad->GetListOfPrimitives());
      while ((h2 = (TH2 *)next())) {
         if (!h2->InheritsFrom(TH2::Class())) continue;
         zmin = h2->GetMinimum();
         zmax = TMath::Max(TMath::Abs(h2->GetMaximum()),
                           TMath::Abs(h2->GetMinimum()));
         if (Hoption.Logz) {
            zmax = TMath::Log10(zmax);
            if (zmin <= 0) {
               zmin = TMath::Log10(zmax*0.001);
            } else {
               zmin = TMath::Log10(zmin);
            }
         }
         break;
      }
   }

   if (Hoption.Logz) {
      if (zmin > 0) {
         zmin = TMath::Log10(zmin*0.1);
         zmax = TMath::Log10(zmax);
      } else {
         return;
      }
   }

   Double_t zratio, dz = zmax - zmin;
   Bool_t kZNeg        = kFALSE;

   // Define the dark and light colors the "button style" boxes.
   Color_t color = fH->GetFillColor();
   Color_t light=0, dark=0;
   if (Hoption.Box == 11) {
      light = TColor::GetColorBright(color);
      dark  = TColor::GetColorDark(color);
   }

   // Loop over all the bins and draw the boxes
   for (Int_t j=Hparam.yfirst; j<=Hparam.ylast;j++) {
      yk    = fYaxis->GetBinLowEdge(j);
      ystep = fYaxis->GetBinWidth(j);
      ycent = 0.5*ystep;
      for (Int_t i=Hparam.xfirst; i<=Hparam.xlast;i++) {
         Int_t bin  = j*(fXaxis->GetNbins()+2) + i;
         xk    = fXaxis->GetBinLowEdge(i);
         xstep = fXaxis->GetBinWidth(i);
         if (!IsInside(xk+0.5*xstep,yk+0.5*ystep)) continue;
         xcent = 0.5*xstep;
         z     = Hparam.factor*fH->GetBinContent(bin);
         kZNeg = kFALSE;

         if (z <  zmin) continue; // Can be the case with
         if (z >  zmax) z = zmax; // option Same

         if (z < 0) {
            if (Hoption.Logz) continue;
            z = -z;
            kZNeg = kTRUE;
         }
         if (Hoption.Logz) {
            if (z != 0) z = TMath::Log10(z);
            else        z = zmin;
         }

         if (dz == 0) continue;
         zratio = TMath::Sqrt((z-zmin)/dz);
         if (zratio == 0) continue;

         xup  = xcent*zratio + xk + xcent;
         xlow = 2*(xk + xcent) - xup;
         if (xup-xlow < dxmin) xup = xlow+dxmin;
         if (Hoption.Logx) {
            if (xup > 0)  xup  = TMath::Log10(xup);
            else continue;
            if (xlow > 0) xlow = TMath::Log10(xlow);
            else continue;
         }

         yup  = ycent*zratio + yk + ycent;
         ylow = 2*(yk + ycent) - yup;
         if (yup-ylow < dymin) yup = ylow+dymin;
         if (Hoption.Logy) {
            if (yup > 0)  yup  = TMath::Log10(yup);
            else continue;
            if (ylow > 0) ylow = TMath::Log10(ylow);
            else continue;
         }

         xlow = TMath::Max(xlow, gPad->GetUxmin());
         ylow = TMath::Max(ylow, gPad->GetUymin());
         xup  = TMath::Min(xup , gPad->GetUxmax());
         yup  = TMath::Min(yup , gPad->GetUymax());

         if (xlow >= xup) continue;
         if (ylow >= yup) continue;

         if (Hoption.Box == 1) {
            fH->SetFillColor(color);
            fH->TAttFill::Modify();
            gPad->PaintBox(xlow, ylow, xup, yup);
            if (kZNeg) {
               gPad->PaintLine(xlow, ylow, xup, yup);
               gPad->PaintLine(xlow, yup, xup, ylow);
            }
         } else if (Hoption.Box == 11) {
            // Draw the center of the box
            fH->SetFillColor(color);
            fH->TAttFill::Modify();
            gPad->PaintBox(xlow, ylow, xup, yup);

            // Draw top&left part of the box
            Double_t x[7], y[7];
            Double_t bwidth = 0.1;
            x[0] = xlow;                     y[0] = ylow;
            x[1] = xlow + bwidth*(xup-xlow); y[1] = ylow + bwidth*(yup-ylow);
            x[2] = x[1];                     y[2] = yup - bwidth*(yup-ylow);
            x[3] = xup - bwidth*(xup-xlow);  y[3] = y[2];
            x[4] = xup;                      y[4] = yup;
            x[5] = xlow;                     y[5] = yup;
            x[6] = xlow;                     y[6] = ylow;
            if (kZNeg) fH->SetFillColor(dark);
            else       fH->SetFillColor(light);
            fH->TAttFill::Modify();
            gPad->PaintFillArea(7, x, y);

            // Draw bottom&right part of the box
            x[0] = xlow;                     y[0] = ylow;
            x[1] = xlow + bwidth*(xup-xlow); y[1] = ylow + bwidth*(yup-ylow);
            x[2] = xup - bwidth*(xup-xlow);  y[2] = y[1];
            x[3] = x[2];                     y[3] = yup - bwidth*(yup-ylow);
            x[4] = xup;                      y[4] = yup;
            x[5] = xup;                      y[5] = ylow;
            x[6] = xlow;                     y[6] = ylow;
            if (kZNeg) fH->SetFillColor(light);
            else       fH->SetFillColor(dark);
            fH->TAttFill::Modify();
            gPad->PaintFillArea(7, x, y);
         }
      }
   }

   if (Hoption.Zscale) PaintPalette();
   fH->SetFillStyle(fillsav);
   fH->SetFillColor(colsav);
   fH->TAttFill::Modify();
}


//______________________________________________________________________________
void THistPainter::PaintCandlePlot(Option_t *)
{
   /* Begin_html
   <a href="#HP14">Control function to draw a 2D histogram as a candle (box) plot.</a>
   End_html */

   Double_t x,y,w;
   Double_t m1 = 0.055, m2 = 0.25;
   Double_t xpm[1], ypm[1];

   TH1D *hp;
   TH2D *h2 = (TH2D*)fH;

   Double_t *quantiles = new Double_t[5];
   quantiles[0]=0.; quantiles[1]=0.; quantiles[2] = 0.; quantiles[3] = 0.; quantiles[4] = 0.;
   Double_t *prob = new Double_t[5];
   prob[0]=1E-15; prob[1]=0.25; prob[2]=0.5; prob[3]=0.75; prob[4]=1-1E-15;

   Style_t fillsav   = h2->GetFillStyle();
   Style_t colsav    = h2->GetFillColor();
   Style_t linesav   = h2->GetLineStyle();
   Style_t widthsav  = h2->GetLineWidth();
   Style_t pmssav    = h2->GetMarkerStyle();

   if (h2->GetFillColor() == 0)  h2->SetFillStyle(0);

   h2->SetMarkerStyle(24);
   h2->TAttLine::Modify();
   h2->TAttFill::Modify();
   h2->TAttMarker::Modify();

   // Candle plot along X
   if (Hoption.Candle == 1) {
      for (Int_t i=Hparam.xfirst; i<=Hparam.xlast; i++) {
         x = fXaxis->GetBinLowEdge(i);
         w = fXaxis->GetBinWidth(i);
         hp = h2->ProjectionY("_px", i, i);
         if (hp->GetEntries() !=0) {
            hp->GetQuantiles(5, quantiles, prob);
            ypm[0] = hp->GetMean();

            h2->SetLineStyle(1);
            h2->TAttLine::Modify();
            gPad->PaintBox(x+m1*w,  quantiles[1], x+(1-m1)*w, quantiles[3]);
            gPad->PaintLine(x+m2*w, quantiles[0], x+(1-m2)*w, quantiles[0]);
            gPad->PaintLine(x+m2*w, quantiles[4], x+(1-m2)*w, quantiles[4]);
            h2->SetLineWidth(3*widthsav);
            h2->TAttLine::Modify();
            gPad->PaintLine(x+m1*w, quantiles[2], x+(1-m1)*w, quantiles[2]);
            h2->SetLineWidth(widthsav);
            h2->TAttLine::Modify();

            h2->SetLineStyle(2);
            h2->TAttLine::Modify();
            gPad->PaintLine(x+w/2., quantiles[3], x+w/2., quantiles[4]);
            gPad->PaintLine(x+w/2., quantiles[0], x+w/2., quantiles[1]);

            xpm[0] = x+w/2;
            gPad->PaintPolyMarker(1,xpm,ypm);
         }
      }
   // Candle plot along Y
   } else {
      for (Int_t i=Hparam.yfirst; i<=Hparam.ylast; i++) {
         y = fYaxis->GetBinLowEdge(i);
         w = fYaxis->GetBinWidth(i);
         hp = h2->ProjectionX("_py", i, i);
         if (hp->GetEntries() !=0) {
            hp->GetQuantiles(5, quantiles, prob);
            xpm[0] = hp->GetMean();

            h2->SetLineStyle(1);
            h2->TAttLine::Modify();
            gPad->PaintBox(quantiles[1],  y+m1*w, quantiles[3], y+(1-m1)*w);
            gPad->PaintLine(quantiles[0], y+m2*w, quantiles[0], y+(1-m2)*w);
            gPad->PaintLine(quantiles[4], y+m2*w, quantiles[4], y+(1-m2)*w);
            h2->SetLineWidth(3*widthsav);
            h2->TAttLine::Modify();
            gPad->PaintLine(quantiles[2], y+m1*w, quantiles[2], y+(1-m1)*w);
            h2->SetLineWidth(widthsav);
            h2->TAttLine::Modify();

            h2->SetLineStyle(2);
            h2->TAttLine::Modify();
            gPad->PaintLine(quantiles[3], y+w/2., quantiles[4], y+w/2.);
            gPad->PaintLine(quantiles[0], y+w/2., quantiles[1], y+w/2.);

            ypm[0] = y+w/2;
            gPad->PaintPolyMarker(1,xpm,ypm);
         }
      }
   }

   h2->SetFillStyle(fillsav);
   h2->SetFillColor(colsav);
   h2->SetLineStyle(linesav);
   h2->SetMarkerStyle(pmssav);
   h2->SetLineWidth(widthsav);
   h2->TAttFill::Modify();
   h2->TAttLine::Modify();
   h2->TAttMarker::Modify();

   delete [] prob;
   delete [] quantiles;
}

//______________________________________________________________________________
void THistPainter::PaintViolinPlot(Option_t *)
{
    /* Begin_html
       <a href="#HP141">Control function to draw a 2D histogram as a violin plot.</a>
       End_html */

   Double_t x,y,w;
   Double_t bw, bcen, bcon;
   Double_t xpm[1], ypm[1];

   TH1D *hp;
   TH2D *h2 = (TH2D*)fH;

   Double_t *quantiles = new Double_t[5];
   quantiles[0]=0.; quantiles[1]=0.; quantiles[2] = 0.; quantiles[3] = 0.; quantiles[4] = 0.;
   Double_t *prob = new Double_t[5];
   prob[0]=1E-15; prob[1]=0.25; prob[2]=0.5; prob[3]=0.75; prob[4]=1-1E-15;

   Style_t fillsav   = h2->GetFillStyle();
   Style_t colsav    = h2->GetFillColor();
   Style_t linesav   = h2->GetLineStyle();
   Style_t widthsav  = h2->GetLineWidth();
   Style_t pmssav    = h2->GetMarkerStyle();

   if (h2->GetFillColor() == 0)  h2->SetFillStyle(0);

   h2->SetMarkerStyle(pmssav);
   h2->TAttLine::Modify();
   h2->TAttFill::Modify();
   h2->TAttMarker::Modify();

   // Violin plot along X
   if (Hoption.Violin == 1) {
      for (Int_t i=Hparam.xfirst; i<=Hparam.xlast; i++) {
         x = fXaxis->GetBinCenter(i);
         w = fXaxis->GetBinWidth(i);
         hp = h2->ProjectionY("_px", i, i);
         if (hp->GetEntries() !=0 && hp->GetMaximum()!=0) {
             hp->Scale(1.0/hp->Integral());
             hp->Scale(w/hp->GetMaximum());
             hp->GetQuantiles(5, quantiles, prob);
             ypm[0] = hp->GetMean();

             TAxis *ax = hp->GetXaxis();
             for(Int_t j=ax->GetFirst(); j<ax->GetLast(); ++j){
                 bw = ax->GetBinWidth(j);
                 bcen = ax->GetBinCenter(j);
                 bcon = hp->GetBinContent(j);
                 gPad->PaintBox(x-0.5*bcon, bcen-0.5*bw, x+0.5*bcon, bcen+0.5*bw);
             }

             h2->SetLineWidth(widthsav);
             h2->TAttLine::Modify();

             h2->SetLineStyle(linesav);
             h2->TAttLine::Modify();
             gPad->PaintLine(x, quantiles[3], x, quantiles[4]);
             gPad->PaintLine(x, quantiles[0], x, quantiles[1]);

             xpm[0] = x;
             gPad->PaintPolyMarker(1,xpm,ypm);
         }
      }
   // Violin plot along Y
   } else {
       for (Int_t i=Hparam.yfirst; i<=Hparam.ylast; i++) {
           y = fYaxis->GetBinCenter(i);
           w = fYaxis->GetBinWidth(i);
           hp = h2->ProjectionX("_py", i, i);
           if (hp->GetEntries() !=0 && hp->GetMaximum()!=0) {
             hp->Scale(1.0/hp->Integral());
             hp->Scale(w/hp->GetMaximum());
             hp->GetQuantiles(5, quantiles, prob);
             xpm[0] = hp->GetMean();

             h2->SetLineWidth(0);
             h2->TAttLine::Modify();
             TAxis *ax = hp->GetXaxis();
             for(Int_t j=ax->GetFirst(); j<ax->GetLast(); ++j){
                 bw = ax->GetBinWidth(j);
                 bcen = ax->GetBinCenter(j);
                 bcon = hp->GetBinContent(j);
                 gPad->PaintBox(bcen-0.5*bw, y-0.5*bcon, bcen+0.5*bw, y+0.5*bcon);
             }

             hp->GetQuantiles(5, quantiles, prob);
             xpm[0] = hp->GetMean();

             h2->SetLineWidth(widthsav);
             h2->SetLineStyle(2);
             h2->TAttLine::Modify();
             gPad->PaintLine(quantiles[3], y, quantiles[4], y);
             gPad->PaintLine(quantiles[0], y, quantiles[1], y);

             ypm[0] = y;
             gPad->PaintPolyMarker(1,xpm,ypm);
           }
       }
   }

   h2->SetFillStyle(fillsav);
   h2->SetFillColor(colsav);
   h2->SetLineStyle(linesav);
   h2->SetMarkerStyle(pmssav);
   h2->SetLineWidth(widthsav);
   h2->TAttFill::Modify();
   h2->TAttLine::Modify();
   h2->TAttMarker::Modify();

   delete [] prob;
   delete [] quantiles;
}

//______________________________________________________________________________
void THistPainter::PaintColorLevels(Option_t *)
{
   /* Begin_html
   <a href="#HP14">Control function to draw a 2D histogram as a color plot.</a>
   End_html */

   Double_t z, zc, xk, xstep, yk, ystep, xlow, xup, ylow, yup;

   Double_t zmin = fH->GetMinimum();
   Double_t zmax = fH->GetMaximum();

   Double_t dz = zmax - zmin;
   if (dz <= 0) { // Histogram filled with a constant value
      zmax += 0.1*TMath::Abs(zmax);
      zmin -= 0.1*TMath::Abs(zmin);
      dz = zmax - zmin;
   }

   if (Hoption.Logz) {
      if (zmin > 0) {
         zmin = TMath::Log10(zmin);
         zmax = TMath::Log10(zmax);
         dz = zmax - zmin;
      } else {
         return;
      }
   }

   Style_t fillsav   = fH->GetFillStyle();
   Style_t colsav    = fH->GetFillColor();
   fH->SetFillStyle(1001);
   fH->TAttFill::Modify();

   // Initialize the levels on the Z axis
   Int_t ncolors  = gStyle->GetNumberOfColors();
   Int_t ndiv   = fH->GetContour();
   if (ndiv == 0 ) {
      ndiv = gStyle->GetNumberContours();
      fH->SetContour(ndiv);
   }
   Int_t ndivz  = TMath::Abs(ndiv);
   if (fH->TestBit(TH1::kUserContour) == 0) fH->SetContour(ndiv);
   Double_t scale = ndivz/dz;

   Int_t color;
   TProfile2D* prof2d = dynamic_cast<TProfile2D*>(fH);
   for (Int_t j=Hparam.yfirst; j<=Hparam.ylast;j++) {
      yk    = fYaxis->GetBinLowEdge(j);
      ystep = fYaxis->GetBinWidth(j);
      for (Int_t i=Hparam.xfirst; i<=Hparam.xlast;i++) {
         Int_t bin  = j*(fXaxis->GetNbins()+2) + i;
         xk    = fXaxis->GetBinLowEdge(i);
         xstep = fXaxis->GetBinWidth(i);
         if (Hoption.System == kPOLAR && xk<0) xk= 2*TMath::Pi()+xk;
         if (!IsInside(xk+0.5*xstep,yk+0.5*ystep)) continue;
         z     = fH->GetBinContent(bin);
         // if fH is a profile histogram do not draw empty bins
         if (prof2d) {
            const Double_t binEntries = prof2d->GetBinEntries(bin);
            if (binEntries == 0)
               continue;
         } else {
            // don't draw the empty bins for non-profile histograms
            // with positive content
            if (z == 0 && (zmin >= 0 || Hoption.Logz)) continue;
         }

         if (Hoption.Logz) {
            if (z > 0) z = TMath::Log10(z);
            else       z = zmin;
         }
         if (z < zmin) continue;
         xup  = xk + xstep;
         xlow = xk;
         if (Hoption.Logx) {
            if (xup > 0)  xup  = TMath::Log10(xup);
            else continue;
            if (xlow > 0) xlow = TMath::Log10(xlow);
            else continue;
         }
         yup  = yk + ystep;
         ylow = yk;
         if (Hoption.System != kPOLAR) {
            if (Hoption.Logy) {
               if (yup > 0)  yup  = TMath::Log10(yup);
               else continue;
               if (ylow > 0) ylow = TMath::Log10(ylow);
               else continue;
            }
            if (xup  < gPad->GetUxmin()) continue;
            if (yup  < gPad->GetUymin()) continue;
            if (xlow > gPad->GetUxmax()) continue;
            if (ylow > gPad->GetUymax()) continue;
            if (xlow < gPad->GetUxmin()) xlow = gPad->GetUxmin();
            if (ylow < gPad->GetUymin()) ylow = gPad->GetUymin();
            if (xup  > gPad->GetUxmax()) xup  = gPad->GetUxmax();
            if (yup  > gPad->GetUymax()) yup  = gPad->GetUymax();
         }

         if (fH->TestBit(TH1::kUserContour)) {
            zc = fH->GetContourLevelPad(0);
            if (z < zc) continue;
            color = -1;
            for (Int_t k=0; k<ndiv; k++) {
               zc = fH->GetContourLevelPad(k);
               if (z < zc) {
                  continue;
               } else {
                  color++;
               }
            }
         } else {
            color = Int_t(0.01+(z-zmin)*scale);
         }

         Int_t theColor = Int_t((color+0.99)*Float_t(ncolors)/Float_t(ndivz));
         if (theColor > ncolors-1) theColor = ncolors-1;
         fH->SetFillColor(gStyle->GetColorPalette(theColor));
         fH->TAttFill::Modify();
         if (Hoption.System != kPOLAR) {
            gPad->PaintBox(xlow, ylow, xup, yup);
         } else  {
            TCrown crown(0,0,ylow,yup,xlow*TMath::RadToDeg(),xup*TMath::RadToDeg());
            crown.SetFillColor(gStyle->GetColorPalette(theColor));
            crown.Paint();
         }
      }
   }

   if (Hoption.Zscale) PaintPalette();

   fH->SetFillStyle(fillsav);
   fH->SetFillColor(colsav);
   fH->TAttFill::Modify();

}


//______________________________________________________________________________
void THistPainter::PaintContour(Option_t *option)
{
   /* Begin_html
   <a href="#HP16">Control function to draw a 2D histogram as a contour plot.</a>
   End_html */

   Int_t i, j, count, ncontour, icol, n, lj, m, ix, jx, ljfill;
   Int_t itars, mode, ir[4];
   Double_t xsave, ysave, thesave,phisave,x[4], y[4], zc[4];

   if (Hoption.Contour == 14) {
      Hoption.Surf = 12;
      Hoption.Axis = 1;
      thesave = gPad->GetTheta();
      phisave = gPad->GetPhi();
      gPad->SetPhi(0.);
      gPad->SetTheta(90.);
      PaintSurface(option);
      gPad->SetPhi(phisave);
      gPad->SetTheta(thesave);
      TView *view = gPad->GetView();
      if (view) view->SetBit(kCannotRotate); //tested in ExecuteEvent
      PaintAxis();
      return;
   }

   if (Hoption.Same) {
      // If the contour is painted on a 3d plot, the contour lines are
      // paint in 3d too.
      TObject *obj;
      TIter next(gPad->GetListOfPrimitives());
      while ((obj=next())) {
         if (strstr(obj->GetDrawOption(),"surf") ||
             strstr(obj->GetDrawOption(),"lego") ||
             strstr(obj->GetDrawOption(),"tri")) {
               Hoption.Surf = 16;
               PaintSurface(option);
               return;
         }
      }
   }

   if (Hoption.Contour == 15) {
      TGraphDelaunay *dt;
      TList *hl = fH->GetListOfFunctions();
      dt = (TGraphDelaunay*)hl->FindObject("TGraphDelaunay");
      if (!dt) return;
      if (!fGraph2DPainter) fGraph2DPainter = new TGraph2DPainter(dt);
      fGraph2DPainter->Paint(option);
      return;
   }

   gPad->SetBit(TGraph::kClipFrame);

   Double_t *levels  = new Double_t[2*kMAXCONTOUR];
   Double_t *xarr    = new Double_t[2*kMAXCONTOUR];
   Double_t *yarr    = new Double_t[2*kMAXCONTOUR];
   Int_t  *itarr     = new Int_t[2*kMAXCONTOUR];

   Int_t npmax = 0;
   for (i=0;i<2*kMAXCONTOUR;i++) itarr[i] = 0;

   ncontour = fH->GetContour();
   if (ncontour == 0) {
      ncontour = gStyle->GetNumberContours();
      fH->SetContour(ncontour);
   }
   if (ncontour > kMAXCONTOUR) {
      Warning("PaintContour", "maximum number of contours is %d, asked for %d",
              kMAXCONTOUR, ncontour);
      ncontour = kMAXCONTOUR-1;
   }
   if (fH->TestBit(TH1::kUserContour) == 0) fH->SetContour(ncontour);

   for (i=0;i<ncontour;i++) levels[i] = fH->GetContourLevelPad(i);
   //for (i=0;i<ncontour;i++)
   //   levels[i] = Hparam.zmin+(Hparam.zmax-Hparam.zmin)/ncontour*i;
   Int_t linesav   = fH->GetLineStyle();
   Int_t colorsav  = fH->GetLineColor();
   Int_t fillsav  = fH->GetFillColor();
   if (Hoption.Contour == 13) {
      fH->TAttLine::Modify();
   }

   TPolyLine **polys = 0;
   TPolyLine *poly=0;
   TObjArray *contours = 0;
   TList *list = 0;
   TGraph *graph = 0;
   Int_t *np = 0;
   if (Hoption.Contour == 1) {
      np = new Int_t[ncontour];
      for (i=0;i<ncontour;i++) np[i] = 0;
      polys = new TPolyLine*[ncontour];
      for (i=0;i<ncontour;i++) {
         polys[i] = new TPolyLine(100);
      }
      if (Hoption.List == 1) {
         contours = (TObjArray*)gROOT->GetListOfSpecials()->FindObject("contours");
         if (contours) {
            gROOT->GetListOfSpecials()->Remove(contours);
            count = contours->GetSize();
            for (i=0;i<count;i++) {
               list = (TList*)contours->At(i);
               if (list) list->Delete();
            }
         }
         contours = new TObjArray(ncontour);
         contours->SetName("contours");
         gROOT->GetListOfSpecials()->Add(contours);
         for (i=0;i<ncontour;i++) {
            list = new TList();
            contours->Add(list);
         }
      }
   }
   Int_t theColor;
   Int_t ncolors = gStyle->GetNumberOfColors();
   Int_t ndivz   = TMath::Abs(ncontour);

   Int_t k,ipoly;
   for (j=Hparam.yfirst; j<Hparam.ylast; j++) {
      y[0] = fYaxis->GetBinCenter(j);
      y[1] = y[0];
      y[2] = fYaxis->GetBinCenter(j+1);
      y[3] = y[2];
      for (i=Hparam.xfirst; i<Hparam.xlast; i++) {
         zc[0] = fH->GetBinContent(i,   j);
         zc[1] = fH->GetBinContent(i+1, j);
         zc[2] = fH->GetBinContent(i+1, j+1);
         zc[3] = fH->GetBinContent(i,   j+1);
         if (!IsInside(fXaxis->GetBinCenter(i),fYaxis->GetBinCenter(j))) continue;
         if (Hoption.Logz) {
            if (zc[0] > 0)   zc[0] = TMath::Log10(zc[0]);
            else             zc[0] = Hparam.zmin;
            if (zc[1] > 0)   zc[1] = TMath::Log10(zc[1]);
            else             zc[1] = Hparam.zmin;
            if (zc[2] > 0)   zc[2] = TMath::Log10(zc[2]);
            else             zc[2] = Hparam.zmin;
            if (zc[3] > 0)   zc[3] = TMath::Log10(zc[3]);
            else             zc[3] = Hparam.zmin;
         }
         for (k=0;k<4;k++) {
            ir[k] = TMath::BinarySearch(ncontour,levels,zc[k]);
         }
         if (ir[0] != ir[1] || ir[1] != ir[2] || ir[2] != ir[3] || ir[3] != ir[0]) {
            x[0] = fXaxis->GetBinCenter(i);
            x[3] = x[0];
            x[1] = fXaxis->GetBinCenter(i+1);
            x[2] = x[1];
            if (zc[0] <= zc[1]) n = 0; else n = 1;
            if (zc[2] <= zc[3]) m = 2; else m = 3;
            if (zc[n] > zc[m]) n = m;
            n++;
            lj=1;
            for (ix=1;ix<=4;ix++) {
               m = n%4 + 1;
               ljfill = PaintContourLine(zc[n-1],ir[n-1],x[n-1],y[n-1],zc[m-1],
                     ir[m-1],x[m-1],y[m-1],&xarr[lj-1],&yarr[lj-1],&itarr[lj-1], levels);
               lj += 2*ljfill;
               n = m;
            }

            if (zc[0] <= zc[1]) n = 0; else n = 1;
            if (zc[2] <= zc[3]) m = 2; else m = 3;
            if (zc[n] > zc[m]) n = m;
            n++;
            lj=2;
            for (ix=1;ix<=4;ix++) {
               if (n == 1) m = 4;
               else        m = n-1;
               ljfill = PaintContourLine(zc[n-1],ir[n-1],x[n-1],y[n-1],zc[m-1],
                     ir[m-1],x[m-1],y[m-1],&xarr[lj-1],&yarr[lj-1],&itarr[lj-1], levels);
               lj += 2*ljfill;
               n = m;
            }

   //     Re-order endpoints

            count = 0;
            for (ix=1; ix<=lj-5; ix +=2) {
               //count = 0;
               while (itarr[ix-1] != itarr[ix]) {
                  xsave = xarr[ix];
                  ysave = yarr[ix];
                  itars = itarr[ix];
                  for (jx=ix; jx<=lj-5; jx +=2) {
                     xarr[jx]  = xarr[jx+2];
                     yarr[jx]  = yarr[jx+2];
                     itarr[jx] = itarr[jx+2];
                  }
                  xarr[lj-3]  = xsave;
                  yarr[lj-3]  = ysave;
                  itarr[lj-3] = itars;
                  if (count > 100) break;
                  count++;
               }
            }

            if (count > 100) continue;
            for (ix=1; ix<=lj-2; ix +=2) {
               theColor = Int_t((itarr[ix-1]+0.99)*Float_t(ncolors)/Float_t(ndivz));
               icol = gStyle->GetColorPalette(theColor);
               if (Hoption.Contour == 11) {
                  fH->SetLineColor(icol);
               }
               if (Hoption.Contour == 12) {
                  mode = icol%5;
                  if (mode == 0) mode = 5;
                  fH->SetLineStyle(mode);
               }
               if (Hoption.Contour != 1) {
                  fH->TAttLine::Modify();
                  gPad->PaintPolyLine(2,&xarr[ix-1],&yarr[ix-1]);
                  continue;
               }

               ipoly = itarr[ix-1];
               if (ipoly >=0 && ipoly <ncontour) {
                  poly = polys[ipoly];
                  poly->SetPoint(np[ipoly]  ,xarr[ix-1],yarr[ix-1]);
                  poly->SetPoint(np[ipoly]+1,xarr[ix],  yarr[ix]);
                  np[ipoly] += 2;
                  if (npmax < np[ipoly]) npmax = np[ipoly];
               }
            }
         } // end of if (ir[0]
      } //end of for (i
   } //end of for (j

   Double_t xmin,ymin;
   Double_t *xp, *yp;
   Int_t nadd,iminus,iplus;
   Double_t *xx, *yy;
   Int_t istart;
   Int_t first = ncontour;
   Int_t *polysort = 0;
   Int_t contListNb;
   if (Hoption.Contour != 1) goto theEND;

   //The 2 points line generated above are now sorted/merged to generate
   //a list of consecutive points.
   // If the option "List" has been specified, the list of points is saved
   // in the form of TGraph objects in the ROOT list of special objects.
   xmin = gPad->GetUxmin();
   ymin = gPad->GetUymin();
   xp = new Double_t[2*npmax];
   yp = new Double_t[2*npmax];
   polysort = new Int_t[ncontour];
   //find first positive contour
   for (ipoly=0;ipoly<ncontour;ipoly++) {
      if (levels[ipoly] >= 0) {first = ipoly; break;}
   }
   //store negative contours from 0 to minimum, then all positive contours
   k = 0;
   for (ipoly=first-1;ipoly>=0;ipoly--) {polysort[k] = ipoly; k++;}
   for (ipoly=first;ipoly<ncontour;ipoly++) {polysort[k] = ipoly; k++;}
   // we can now draw sorted contours
   contListNb = 0;
   fH->SetFillStyle(1001);
   for (k=0;k<ncontour;k++) {
      ipoly = polysort[k];
      if (np[ipoly] == 0) continue;
      if (Hoption.List) list = (TList*)contours->At(contListNb);
      contListNb++;
      poly = polys[ipoly];
      xx = poly->GetX();
      yy = poly->GetY();
      istart = 0;
      while (1) {
         iminus = npmax;
         iplus  = iminus+1;
         xp[iminus]= xx[istart];   yp[iminus] = yy[istart];
         xp[iplus] = xx[istart+1]; yp[iplus]  = yy[istart+1];
         xx[istart]   = xmin; yy[istart]   = ymin;
         xx[istart+1] = xmin; yy[istart+1] = ymin;
         while (1) {
            nadd = 0;
            for (i=2;i<np[ipoly];i+=2) {
               if (xx[i] == xp[iplus] && yy[i] == yp[iplus]) {
                  iplus++;
                  xp[iplus] = xx[i+1]; yp[iplus]  = yy[i+1];
                  xx[i]   = xmin; yy[i]   = ymin;
                  xx[i+1] = xmin; yy[i+1] = ymin;
                  nadd++;
               }
               if (xx[i+1] == xp[iminus] && yy[i+1] == yp[iminus]) {
                  iminus--;
                  xp[iminus] = xx[i];   yp[iminus]  = yy[i];
                  xx[i]   = xmin; yy[i]   = ymin;
                  xx[i+1] = xmin; yy[i+1] = ymin;
                  nadd++;
               }
            }
            if (nadd == 0) break;
         }
         theColor = Int_t((ipoly+0.99)*Float_t(ncolors)/Float_t(ndivz));
         icol = gStyle->GetColorPalette(theColor);
         if (ndivz > 1) fH->SetFillColor(icol);
         fH->TAttFill::Modify();
         gPad->PaintFillArea(iplus-iminus+1,&xp[iminus],&yp[iminus]);
         if (Hoption.List) {
            graph = new TGraph(iplus-iminus+1,&xp[iminus],&yp[iminus]);
            graph->SetFillColor(icol);
            graph->SetLineWidth(fH->GetLineWidth());
            list->Add(graph);
         }
         //check if more points are left
         istart = 0;
         for (i=2;i<np[ipoly];i+=2) {
            if (xx[i] != xmin && yy[i] != ymin) {
               istart = i;
               break;
            }
         }
         if (istart == 0) break;
      }
   }

   for (i=0;i<ncontour;i++) delete polys[i];
   delete [] polys;
   delete [] xp;
   delete [] yp;
   delete [] polysort;

theEND:
   gPad->ResetBit(TGraph::kClipFrame);
   if (Hoption.Zscale) PaintPalette();
   fH->SetLineStyle(linesav);
   fH->SetLineColor(colorsav);
   fH->SetFillColor(fillsav);
   if (np) delete [] np;
   delete [] xarr;
   delete [] yarr;
   delete [] itarr;
   delete [] levels;
}


//______________________________________________________________________________
Int_t THistPainter::PaintContourLine(Double_t elev1, Int_t icont1, Double_t x1, Double_t y1,
                            Double_t elev2, Int_t icont2, Double_t x2, Double_t y2,
                            Double_t *xarr, Double_t *yarr, Int_t *itarr, Double_t *levels)
{
   /* Begin_html
   Fill the matrix XARR YARR for Contour Plot.
   End_html */

   Bool_t vert;
   Double_t tlen, tdif, elev, diff, pdif, xlen;
   Int_t n, i, icount;

   if (x1 == x2) {
      vert = kTRUE;
      tlen = y2 - y1;
   } else {
      vert = kFALSE;
      tlen = x2 - x1;
   }

   n = icont1 +1;
   tdif = elev2 - elev1;
   i = 0;
   icount = 0;
   while (n <= icont2 && i <= kMAXCONTOUR/2 -3) {
      //elev = fH->GetContourLevel(n);
      elev = levels[n];
      diff = elev - elev1;
      pdif = diff/tdif;
      xlen = tlen*pdif;
      if (vert) {
         if (Hoption.Logx)
            xarr[i] = TMath::Log10(x1);
         else
            xarr[i] = x1;
         if (Hoption.Logy)
            yarr[i] = TMath::Log10(y1 + xlen);
         else
            yarr[i] = y1 + xlen;
      } else {
         if (Hoption.Logx)
            xarr[i] = TMath::Log10(x1 + xlen);
         else
            xarr[i] = x1 + xlen;
         if (Hoption.Logy)
            yarr[i] = TMath::Log10(y1);
         else
            yarr[i] = y1;
      }
      itarr[i] = n;
      icount++;
      i +=2;
      n++;
   }
   return icount;
}


//______________________________________________________________________________
void THistPainter::PaintErrors(Option_t *)
{
   /* Begin_html
   <a href="#HP09">Draw 1D histograms error bars.</a>
   End_html */

   // On iOS, we do not highlight histogram, if it's not picked at the moment
   // (but part of histogram (axis or pavestat) was picked, that's why this code
   // is called at all. This conditional statement never executes on non-iOS platform.
   if (gPad->PadInHighlightMode() && gPad->GetSelected() != fH) return;

   const Int_t kBASEMARKER=8;
   Double_t xp, yp, ex1, ex2, ey1, ey2;
   Double_t delta;
   Double_t s2x, s2y, bxsize, bysize, symbolsize, xerror, sbase;
   Double_t xi1, xi2, xi3, xi4, yi1, yi2, yi3, yi4;
   Double_t xmin, xmax, ymin, ymax;
   Double_t logxmin = 0;
   Double_t logymin = 0;
   Int_t i, k, npoints, first, last, fixbin;
   Int_t if1 = 0;
   Int_t if2 = 0;
   Int_t drawmarker, errormarker;
   Int_t option0, option1, option2, option3, option4, optionE, optionEX0, optionI0;

   Double_t *xline = 0;
   Double_t *yline = 0;
   option0 = option1 = option2 = option3 = option4 = optionE = optionEX0 = optionI0 = 0;
   if (Int_t(Hoption.Error/10) == 2) {optionEX0 = 1; Hoption.Error -= 10;}
   if (Hoption.Error == 31) {optionEX0 = 1; Hoption.Error = 1;}
   if (Hoption.Error == 10) option0 = 1;
   if (Hoption.Error == 11) option1 = 1;
   if (Hoption.Error == 12) option2 = 1;
   if (Hoption.Error == 13) option3 = 1;
   if (Hoption.Error == 14) {option4 = 1; option3 = 1;}
   if (Hoption.Error == 15) {optionI0 = 1; option3 = 1;}
   if (Hoption.Error == 16) {optionI0 = 1; option4 = 1; option3 = 1;}
   if (option2+option3 == 0) optionE = 1;
   if (Hoption.Error == 0) optionE = 0;
   if (fXaxis->GetXbins()->fN) fixbin = 0;
   else                        fixbin = 1;

   errormarker = fH->GetMarkerStyle();
   if (optionEX0) {
      xerror = 0;
   } else {
      xerror = gStyle->GetErrorX();
   }
   symbolsize  = fH->GetMarkerSize();
   if (errormarker == 1) symbolsize = 0.01;
   sbase       = symbolsize*kBASEMARKER;
   // set the graphics attributes

   fH->TAttLine::Modify();
   fH->TAttFill::Modify();
   fH->TAttMarker::Modify();

   // set the first and last bin

   Double_t factor = Hparam.factor;
   first      = Hparam.xfirst;
   last       = Hparam.xlast;
   npoints    = last - first  +1;
   xmin       = gPad->GetUxmin();
   xmax       = gPad->GetUxmax();
   ymin       = gPad->GetUymin();
   ymax       = gPad->GetUymax();


   if (option3) {
      xline = new Double_t[2*npoints];
      yline = new Double_t[2*npoints];
      if (!xline || !yline) {
         Error("PaintErrors", "too many points, out of memory");
         return;
      }
      if1 = 1;
      if2 = 2*npoints;
   }

   //  compute the offset of the error bars due to the symbol size
   s2x    = gPad->PixeltoX(Int_t(0.5*sbase)) - gPad->PixeltoX(0);
   s2y    =-gPad->PixeltoY(Int_t(0.5*sbase)) + gPad->PixeltoY(0);

   // compute size of the lines at the end of the error bars
   Int_t dxend = Int_t(gStyle->GetEndErrorSize());
   bxsize    = gPad->PixeltoX(dxend) - gPad->PixeltoX(0);
   bysize    =-gPad->PixeltoY(dxend) + gPad->PixeltoY(0);


   if (fixbin) {
      if (Hoption.Logx) xp = TMath::Power(10,Hparam.xmin) + 0.5*Hparam.xbinsize;
      else              xp = Hparam.xmin + 0.5*Hparam.xbinsize;
   } else {
      delta = fH->GetBinWidth(first);
      xp    = fH->GetBinLowEdge(first) + 0.5*delta;
   }

   // if errormarker = 0 or symbolsize = 0. no symbol is drawn
   if (Hoption.Logx) logxmin = TMath::Power(10,Hparam.xmin);
   if (Hoption.Logy) logymin = TMath::Power(10,Hparam.ymin);

   //    ---------------------- Loop over the points---------------------
   for (k=first; k<=last; k++) {

      //          get the data
      //     xp    = X position of the current point
      //     yp    = Y position of the current point
      //     ex1   = Low X error
      //     ex2   = Up X error
      //     ey1   = Low Y error
      //     ey2   = Up Y error
      //     (xi,yi) = Error bars coordinates

      if (Hoption.Logx) {
         if (xp <= 0) goto L30;
         if (xp < logxmin) goto L30;
         if (xp > TMath::Power(10,xmax)) break;
      } else {
         if (xp < xmin) goto L30;
         if (xp > xmax) break;
      }
      yp = factor*fH->GetBinContent(k);
      if (optionI0 && yp==0) goto L30;
      if (fixbin) {
         ex1 = xerror*Hparam.xbinsize;
      } else {
         delta = fH->GetBinWidth(k);
         ex1 = xerror*delta;
      }
      if (fH->GetBinErrorOption() == TH1::kNormal) {
         ey1 = factor*fH->GetBinError(k);
         ey2 = ey1;
      } else {
         ey1 = factor*fH->GetBinErrorLow(k);
         ey2 = factor*fH->GetBinErrorUp(k);
      }
      ex2 = ex1;

      xi4 = xp;
      xi3 = xp;
      xi2 = xp + ex2;
      xi1 = xp - ex1;

      yi1 = yp;
      yi2 = yp;
      yi3 = yp - ey1;
      yi4 = yp + ey2;

      //          take the LOG if necessary
      if (Hoption.Logx) {
         xi1 = TMath::Log10(TMath::Max(xi1,logxmin));
         xi2 = TMath::Log10(TMath::Max(xi2,logxmin));
         xi3 = TMath::Log10(TMath::Max(xi3,logxmin));
         xi4 = TMath::Log10(TMath::Max(xi4,logxmin));
      }
      if (Hoption.Logy) {
         yi1 = TMath::Log10(TMath::Max(yi1,logymin));
         yi2 = TMath::Log10(TMath::Max(yi2,logymin));
         yi3 = TMath::Log10(TMath::Max(yi3,logymin));
         yi4 = TMath::Log10(TMath::Max(yi4,logymin));
      }

      // test if error bars are not outside the limits
      //  otherwise they are truncated

      xi1 = TMath::Max(xi1,xmin);
      xi2 = TMath::Min(xi2,xmax);
      yi3 = TMath::Max(yi3,ymin);
      yi4 = TMath::Min(yi4,ymax);

      //  test if the marker is on the frame limits. If "Yes", the
      //  marker will not be drawn and the error bars will be readjusted.

      drawmarker = kTRUE;
      if (!option0 && !option3) {
         if (Hoption.Logy && yp < logymin) goto L30;
         if (yi1 < ymin || yi1 > ymax) goto L30;
         if (Hoption.Error != 0 && yp == 0 && ey1 <= 0) drawmarker = kFALSE;
      }
      if (!symbolsize || !errormarker) drawmarker = kFALSE;

      //  draw the error rectangles
      if (option2) gPad->PaintBox(xi1,yi3,xi2,yi4);

      //  keep points for fill area drawing
      if (option3) {
         xline[if1-1] = xi3;
         xline[if2-1] = xi3;
         yline[if1-1] = yi4;
         yline[if2-1] = yi3;
         if1++;
         if2--;
      }

      //          draw the error bars
      if (Hoption.Logy && yp < logymin) drawmarker = kFALSE;
      if (optionE && drawmarker) {
         if ((yi3 < yi1 - s2y) && (yi3 < ymax)) gPad->PaintLine(xi3,yi3,xi4,TMath::Min(yi1 - s2y,ymax));
         if ((yi1 + s2y < yi4) && (yi4 > ymin)) gPad->PaintLine(xi3,TMath::Max(yi1 + s2y, ymin),xi4,yi4);
         // don't duplicate the horizontal line
         if (Hoption.Hist != 2) {
            if (yi1<ymax && yi1>ymin) {
              if (xi1 < xi3 - s2x) gPad->PaintLine(xi1,yi1,xi3 - s2x,yi2);
              if (xi3 + s2x < xi2) gPad->PaintLine(xi3 + s2x,yi1,xi2,yi2);
            }
         }
      }
      if (optionE && !drawmarker && (ey1 != 0 || ey2 !=0)) {
         if ((yi3 < yi1) && (yi3 < ymax)) gPad->PaintLine(xi3,yi3,xi4,TMath::Min(yi1,ymax));
         if ((yi1 < yi4) && (yi4 > ymin)) gPad->PaintLine(xi3,TMath::Max(yi1,ymin),xi4,yi4);
         // don't duplicate the horizontal line
         if (Hoption.Hist != 2) {
            if (yi1<ymax && yi1>ymin) {
               if (xi1 < xi3) gPad->PaintLine(xi1,yi1,xi3,yi2);
               if (xi3 < xi2) gPad->PaintLine(xi3,yi1,xi2,yi2);
            }
         }
      }

      //          draw line at the end of the error bars

      if (option1 && drawmarker) {
         if (yi3 < yi1-s2y) gPad->PaintLine(xi3 - bxsize,yi3,xi3 + bxsize,yi3);
         if (yi4 > yi1+s2y) gPad->PaintLine(xi3 - bxsize,yi4,xi3 + bxsize,yi4);
         if (xi1 < xi3-s2x) gPad->PaintLine(xi1,yi1 - bysize,xi1,yi1 + bysize);
         if (xi2 > xi3+s2x) gPad->PaintLine(xi2,yi1 - bysize,xi2,yi1 + bysize);
      }

      //          draw the marker

      if (drawmarker) gPad->PaintPolyMarker(1, &xi3, &yi1);

L30:
      if (fixbin) xp += Hparam.xbinsize;
      else {
         if (k < last) {
            delta = fH->GetBinWidth(k+1);
            xp    = fH->GetBinLowEdge(k+1) + 0.5*delta;
         }
      }
   }  //end of for loop

   //          draw the filled area

   if (option3) {
      TGraph graph;
      graph.SetLineStyle(fH->GetLineStyle());
      graph.SetLineColor(fH->GetLineColor());
      graph.SetLineWidth(fH->GetLineWidth());
      graph.SetFillStyle(fH->GetFillStyle());
      graph.SetFillColor(fH->GetFillColor());
      Int_t logx = gPad->GetLogx();
      Int_t logy = gPad->GetLogy();
      gPad->SetLogx(0);
      gPad->SetLogy(0);

      // In some cases the number of points in the fill area is smaller than
      // 2*npoints. In such cases the array xline and yline must be arranged
      // before being plotted. The next loop does that.
      if (if2 > npoints) {
         for (i=1; i<if1; i++) {
            xline[if1-2+i] = xline[if2-1+i];
            yline[if1-2+i] = yline[if2-1+i];
         }
         npoints = if1-1;
      }
      if (option4) graph.PaintGraph(2*npoints,xline,yline,"FC");
      else         graph.PaintGraph(2*npoints,xline,yline,"F");
      gPad->SetLogx(logx);
      gPad->SetLogy(logy);
      delete [] xline;
      delete [] yline;
   }
}


//______________________________________________________________________________
void THistPainter::Paint2DErrors(Option_t *)
{
   /* Begin_html
   Draw 2D histograms errors.
   End_html */

   fH->TAttMarker::Modify();
   fH->TAttLine::Modify();

   // Define the 3D view
   fXbuf[0] = Hparam.xmin;
   fYbuf[0] = Hparam.xmax;
   fXbuf[1] = Hparam.ymin;
   fYbuf[1] = Hparam.ymax;
   fXbuf[2] = Hparam.zmin;
   fYbuf[2] = Hparam.zmax;
   fLego = new TPainter3dAlgorithms(fXbuf, fYbuf);
   TView *view = gPad->GetView();
   if (!view) {
      Error("Paint2DErrors", "no TView in current pad");
      return;
   }
   Double_t thedeg =  90 - gPad->GetTheta();
   Double_t phideg = -90 - gPad->GetPhi();
   Double_t psideg = view->GetPsi();
   Int_t irep;
   view->SetView(phideg, thedeg, psideg, irep);

   // Set color/style for back box
   fLego->SetFillStyle(gPad->GetFrameFillStyle());
   fLego->SetFillColor(gPad->GetFrameFillColor());
   fLego->TAttFill::Modify();
   Int_t backcolor = gPad->GetFrameFillColor();
   if (Hoption.System != kCARTESIAN) backcolor = 0;
   view->PadRange(backcolor);
   fLego->SetFillStyle(fH->GetFillStyle());
   fLego->SetFillColor(fH->GetFillColor());
   fLego->TAttFill::Modify();

   // Paint the Back Box if needed
   if (Hoption.BackBox && !Hoption.Same && !Hoption.Lego && !Hoption.Surf) {
      fLego->InitMoveScreen(-1.1,1.1);
      fLego->DefineGridLevels(fZaxis->GetNdivisions()%100);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove1);
      fLego->BackBox(90);
   }

   // Paint the Errors
   Double_t x, ex, x1, x2;
   Double_t y, ey, y1, y2;
   Double_t z, ez1, ez2, z1, z2;
   Double_t temp1[3],temp2[3];
   Double_t xyerror;
   if (Hoption.Error == 110) {
      xyerror = 0;
   } else {
      xyerror = gStyle->GetErrorX();
   }

   Double_t xk, xstep, yk, ystep;
   for (Int_t j=Hparam.yfirst; j<=Hparam.ylast;j++) {
      y  = fYaxis->GetBinCenter(j);
      ey = fYaxis->GetBinWidth(j)*xyerror;
      y1 = y-ey;
      y2 = y+ey;
      if (Hoption.Logy) {
         if (y > 0)  y = TMath::Log10(y);
         else        continue;
         if (y1 > 0) y1 = TMath::Log10(y1);
         else        y1 = Hparam.ymin;
         if (y2 > 0) y2 = TMath::Log10(y2);
         else        y2 = Hparam.ymin;
      }
      yk    = fYaxis->GetBinLowEdge(j);
      ystep = fYaxis->GetBinWidth(j);
      for (Int_t i=Hparam.xfirst; i<=Hparam.xlast;i++) {
         xk    = fXaxis->GetBinLowEdge(i);
         xstep = fXaxis->GetBinWidth(i);
         if (!IsInside(xk+0.5*xstep,yk+0.5*ystep)) continue;
         Int_t bin = fH->GetBin(i,j);
         x  = fXaxis->GetBinCenter(i);
         ex = fXaxis->GetBinWidth(i)*xyerror;
         x1 = x-ex;
         x2 = x+ex;
         if (Hoption.Logx) {
            if (x > 0)  x = TMath::Log10(x);
            else        continue;
            if (x1 > 0) x1 = TMath::Log10(x1);
            else        x1 = Hparam.xmin;
            if (x2 > 0) x2 = TMath::Log10(x2);
            else        x2 = Hparam.xmin;
         }
         z  = fH->GetBinContent(bin);
         if (fH->GetBinErrorOption() == TH1::kNormal) {
            ez1 = fH->GetBinError(bin);
            ez2 = ez1;
         }
         else {
            ez1 = fH->GetBinErrorLow(bin);
            ez2 = fH->GetBinErrorUp(bin);
         }
         z1 = z - ez1;
         z2 = z + ez2;
         if (Hoption.Logz) {
            if (z > 0)   z = TMath::Log10(z);
            else         z = Hparam.zmin;
            if (z1 > 0) z1 = TMath::Log10(z1);
            else        z1 = Hparam.zmin;
            if (z2 > 0) z2 = TMath::Log10(z2);
            else        z2 = Hparam.zmin;

         }
         if (z <= Hparam.zmin) continue;
         if (z >  Hparam.zmax) z = Hparam.zmax;

         temp1[0] = x1;
         temp1[1] = y;
         temp1[2] = z;
         temp2[0] = x2;
         temp2[1] = y;
         temp2[2] = z;
         gPad->PaintLine3D(temp1, temp2);
         temp1[0] = x;
         temp1[1] = y1;
         temp1[2] = z;
         temp2[0] = x;
         temp2[1] = y2;
         temp2[2] = z;
         gPad->PaintLine3D(temp1, temp2);
         temp1[0] = x;
         temp1[1] = y;
         temp1[2] = z1;
         temp2[0] = x;
         temp2[1] = y;
         temp2[2] = z2;
         gPad->PaintLine3D(temp1, temp2);
         temp1[0] = x;
         temp1[1] = y;
         temp1[2] = z;
         view->WCtoNDC(temp1, &temp2[0]);
         gPad->PaintPolyMarker(1, &temp2[0], &temp2[1]);
      }
   }

   // Paint the Front Box if needed
   if (Hoption.FrontBox) {
      fLego->InitMoveScreen(-1.1,1.1);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove2);
      fLego->FrontBox(90);
   }

   // Paint the Axis if needed
   if (!Hoption.Axis && !Hoption.Same && !Hoption.Lego && !Hoption.Surf) {
      TGaxis *axis = new TGaxis();
      PaintLegoAxis(axis, 90);
      delete axis;
   }

   delete fLego; fLego = 0;
}


//______________________________________________________________________________
void THistPainter::PaintFrame()
{
   /* Begin_html
   Calculate range and clear pad (canvas).
   End_html */

   if (Hoption.Same) return;

   RecalculateRange();

   if (Hoption.Lego || Hoption.Surf || Hoption.Tri ||
       Hoption.Contour == 14 || Hoption.Error >= 100) {
      TObject *frame = gPad->FindObject("TFrame");
      if (frame) gPad->GetListOfPrimitives()->Remove(frame);
      return;
   }

   //The next statement is always executed on non-iOS platform,
   //on iOS depends on pad mode.
   if (!gPad->PadInSelectionMode() && !gPad->PadInHighlightMode())
      gPad->PaintPadFrame(Hparam.xmin,Hparam.ymin,Hparam.xmax,Hparam.ymax);
}


//______________________________________________________________________________
void THistPainter::PaintFunction(Option_t *)
{
   /* Begin_html
   <a href="#HP28">Paint functions associated to an histogram.</a>
   End_html */

   TObjOptLink *lnk = (TObjOptLink*)fFunctions->FirstLink();
   TObject *obj;

   while (lnk) {
      obj = lnk->GetObject();
      TVirtualPad *padsave = gPad;
      if (obj->InheritsFrom(TF2::Class())) {
         if (obj->TestBit(TF2::kNotDraw) == 0) {
            if (Hoption.Lego || Hoption.Surf) {
               TF2 *f2 = (TF2*)obj;
               f2->SetMinimum(fH->GetMinimum());
               f2->SetMaximum(fH->GetMaximum());
               f2->SetRange(fH->GetXaxis()->GetXmin(), fH->GetYaxis()->GetXmin(), fH->GetXaxis()->GetXmax(), fH->GetYaxis()->GetXmax() );
               f2->Paint("surf same");
            } else {
               obj->Paint("cont3 same");
            }
         }
      } else if (obj->InheritsFrom(TF1::Class())) {
         if (obj->TestBit(TF1::kNotDraw) == 0) obj->Paint("lsame");
      } else  {
         //Let's make this 'function' selectable on iOS device (for example, it can be TPaveStat).
         gPad->PushSelectableObject(obj);

         //The next statement is ALWAYS executed on non-iOS platform, on iOS it depends on pad's mode
         //and picked object.
         if (!gPad->PadInHighlightMode() || (gPad->PadInHighlightMode() && obj == gPad->GetSelected()))
            obj->Paint(lnk->GetOption());
      }
      lnk = (TObjOptLink*)lnk->Next();
      padsave->cd();
   }
}


//______________________________________________________________________________
void THistPainter::PaintHist(Option_t *)
{
   /* Begin_html
   <a href="#HP01b">Control routine to draw 1D histograms.</a>
   End_html */

   //On iOS: do not highlight hist, if part of it was selected.
   //Never executes on non-iOS platform.
   if (gPad->PadInHighlightMode() && gPad->GetSelected() != fH)
      return;

   static char chopth[17];

   Int_t htype, oldhtype;
   Int_t i, j, first, last, nbins, fixbin;
   Double_t c1, yb;
   yb = 0;

   strlcpy(chopth, "                ",17);

   Double_t ymin = Hparam.ymin;
   Double_t ymax = Hparam.ymax;
   Double_t baroffset = fH->GetBarOffset();
   Double_t barwidth  = fH->GetBarWidth();
   Double_t baroffsetsave = gStyle->GetBarOffset();
   Double_t barwidthsave  = gStyle->GetBarWidth();
   gStyle->SetBarOffset(baroffset);
   gStyle->SetBarWidth(barwidth);

   //       Create "LIFE" structure to keep current histogram status

   first = Hparam.xfirst;
   last  = Hparam.xlast;
   nbins = last - first + 1;

   Double_t *keepx = 0;
   Double_t *keepy = 0;
   if (fXaxis->GetXbins()->fN) fixbin = 0;
   else                        fixbin = 1;
   if (fixbin) keepx = new Double_t[2];
   else        keepx = new Double_t[nbins+1];
   keepy = new Double_t[nbins];
   Double_t logymin = 0;
   if (Hoption.Logy) logymin = TMath::Power(10,ymin);

   //      Loop on histogram bins

   for (j=first; j<=last;j++) {
      c1 = Hparam.factor*fH->GetBinContent(j);
      if (TMath::Abs(ymax-ymin) > 0) {
         if (Hoption.Logy) yb = TMath::Log10(TMath::Max(c1,.1*logymin));
         else              yb = c1;
      }
      if (!Hoption.Line) {
         yb = TMath::Max(yb, ymin);
         yb = TMath::Min(yb, ymax);
      }
      keepy[j-first] = yb;
   }

   //              Draw histogram according to value of FillStyle and FillColor

   if (fixbin) { keepx[0] = Hparam.xmin; keepx[1] = Hparam.xmax; }
   else {
      for (i=0; i<nbins; i++) keepx[i] = fXaxis->GetBinLowEdge(i+first);
      keepx[nbins] = fXaxis->GetBinUpEdge(nbins-1+first);
   }

   //         Prepare Fill area (systematic with option "Bar").

   oldhtype = fH->GetFillStyle();
   htype    = oldhtype;
   if (Hoption.Bar) {
      if (htype == 0 || htype == 1000) htype = 1001;
   }

   Width_t lw = (Width_t)fH->GetLineWidth();

   //         Code option for GrapHist

   if (Hoption.Line) chopth[0] = 'L';
   if (Hoption.Star) chopth[1] = '*';
   if (Hoption.Mark) chopth[2] = 'P';
   if (Hoption.Mark == 10) chopth[3] = '0';
   if (Hoption.Line || Hoption.Curve || Hoption.Hist || Hoption.Bar) {
      if (Hoption.Curve)    chopth[3] = 'C';
      if (Hoption.Hist > 0) chopth[4] = 'H';
      else if (Hoption.Bar) chopth[5] = 'B';
      if (fH->GetFillColor() && htype) {
         if (Hoption.Logy) {
            chopth[6] = '1';
         }
         if (Hoption.Hist > 0 || Hoption.Curve || Hoption.Line) {
            chopth[7] = 'F';
         }
      }
   }
   if (!fixbin && strlen(chopth)) {
      chopth[8] = 'N';
   }

   if (Hoption.Fill == 2)    chopth[13] = '2';

   //         Option LOGX

   if (Hoption.Logx) {
      chopth[9]  = 'G';
      chopth[10] = 'X';
      if (fixbin) {
         keepx[0] = TMath::Power(10,keepx[0]);
         keepx[1] = TMath::Power(10,keepx[1]);
      }
   }

   if (Hoption.Off) {
      chopth[11] = ']';
      chopth[12] = '[';
   }

   //         Draw the histogram

   TGraph graph;
   graph.SetLineWidth(lw);
   graph.SetLineStyle(fH->GetLineStyle());
   graph.SetLineColor(fH->GetLineColor());
   graph.SetFillStyle(htype);
   graph.SetFillColor(fH->GetFillColor());
   graph.SetMarkerStyle(fH->GetMarkerStyle());
   graph.SetMarkerSize(fH->GetMarkerSize());
   graph.SetMarkerColor(fH->GetMarkerColor());
   if (!Hoption.Same) graph.ResetBit(TGraph::kClipFrame);

   graph.PaintGrapHist(nbins, keepx, keepy ,chopth);

   delete [] keepx;
   delete [] keepy;
   gStyle->SetBarOffset(baroffsetsave);
   gStyle->SetBarWidth(barwidthsave);

   htype=oldhtype;
}


//______________________________________________________________________________
void THistPainter::PaintH3(Option_t *option)
{
   /* Begin_html
   <a href="#HP01d">Control function to draw a 3D histograms.</a>
   End_html */

   char *cmd;
   TString opt = fH->GetDrawOption();
   opt.ToLower();
   Int_t irep;

   if (fH->GetDrawOption() && (strstr(opt,"box") ||  strstr(opt,"lego"))) {
      cmd = Form("TMarker3DBox::PaintH3((TH1 *)0x%lx,\"%s\");",(Long_t)fH,option);
   } else if (fH->GetDrawOption() && strstr(opt,"iso")) {
      PaintH3Iso();
      return;
   } else if (strstr(option,"tf3")) {
      PaintTF3();
      return;
   } else {
      cmd = Form("TPolyMarker3D::PaintH3((TH1 *)0x%lx,\"%s\");",(Long_t)fH,option);
   }

   if (strstr(opt,"fb")) Hoption.FrontBox = 0;
   if (strstr(opt,"bb")) Hoption.BackBox = 0;

   TView *view = gPad->GetView();
   if (!view) return;
   Double_t thedeg =  90 - gPad->GetTheta();
   Double_t phideg = -90 - gPad->GetPhi();
   Double_t psideg = view->GetPsi();
   view->SetView(phideg, thedeg, psideg, irep);

   // Paint the data
   gROOT->ProcessLine(cmd);

   if (Hoption.Same) return;

   // Draw axis
   view->SetOutlineToCube();
   TSeqCollection *ol = view->GetOutline();
   if (ol && Hoption.BackBox && Hoption.FrontBox) ol->Paint(option);
   Hoption.System = kCARTESIAN;
   TGaxis *axis = new TGaxis();
   if (!Hoption.Axis && !Hoption.Same) PaintLegoAxis(axis, 90);
   delete axis;

   // Draw palette. In case of 4D plot with TTree::Draw() the palette should
   // be painted with the option colz.
   if (fH->GetDrawOption() && strstr(opt,"colz")) {
      Int_t ndiv   = fH->GetContour();
      if (ndiv == 0 ) {
         ndiv = gStyle->GetNumberContours();
         fH->SetContour(ndiv);
      }
      PaintPalette();
   }

   // Draw title
   PaintTitle();

   //Draw stats and fit results
   TF1 *fit  = 0;
   TIter next(fFunctions);
   TObject *obj;
   while ((obj = next())) {
      if (obj->InheritsFrom(TF1::Class())) {
         fit = (TF1*)obj;
         break;
      }
   }
   if (Hoption.Same != 1) {
      if (!fH->TestBit(TH1::kNoStats)) {  // bit set via TH1::SetStats
         PaintStat3(gStyle->GetOptStat(),fit);
      }
   }

}


//______________________________________________________________________________
Int_t THistPainter::PaintInit()
{
   /* Begin_html
   Compute histogram parameters used by the drawing routines.
   End_html */

   if (fH->GetDimension() > 1 || Hoption.Lego || Hoption.Surf) return 1;

   Int_t i;
   static const char *where = "PaintInit";
   Double_t yMARGIN = gStyle->GetHistTopMargin();
   Int_t maximum = 0;
   Int_t minimum = 0;
   if (fH->GetMaximumStored() != -1111) maximum = 1;
   if (fH->GetMinimumStored() != -1111) minimum = 1;

   //     Compute X axis parameters

   Int_t last      = fXaxis->GetLast();
   Int_t first     = fXaxis->GetFirst();
   Hparam.xlowedge = fXaxis->GetBinLowEdge(first);
   Hparam.xbinsize = fXaxis->GetBinWidth(first);
   Hparam.xlast    = last;
   Hparam.xfirst   = first;
   Hparam.xmin     = Hparam.xlowedge;
   Hparam.xmax     = fXaxis->GetBinLowEdge(last)+fXaxis->GetBinWidth(last);

   //       if log scale in X, replace xmin,max by the log
   if (Hoption.Logx) {
      if (Hparam.xmax<=0) {
         Error(where, "cannot set X axis to log scale");
         return 0;
      }
      if (Hparam.xlowedge <=0 ) {
         if (Hoption.Same) {
            Hparam.xlowedge = TMath::Power(10, gPad->GetUxmin());
         } else {
            for (i=first; i<=last; i++) {
               Double_t binLow = fXaxis->GetBinLowEdge(i);
               if (binLow>0) {
                  Hparam.xlowedge = binLow;
                  break;
               }
            }
            if (Hparam.xlowedge<=0) {
               Error(where, "cannot set X axis to log scale");
               return 0;
            }
         }
         Hparam.xmin  = Hparam.xlowedge;
      }
      Hparam.xfirst= fXaxis->FindFixBin(Hparam.xmin);
      Hparam.xlast = fXaxis->FindFixBin(Hparam.xmax);
      Hparam.xmin  = TMath::Log10(Hparam.xmin);
      Hparam.xmax  = TMath::Log10(Hparam.xmax);
      if (Hparam.xlast  > last)  Hparam.xlast  = last;
      if (Hparam.xfirst < first) Hparam.xfirst = first;
   }

   //     Compute Y axis parameters
   Double_t bigp = TMath::Power(10,32);
   Double_t ymax = -bigp;
   Double_t ymin = bigp;
   Double_t c1, e1;
   Double_t xv[1];
   Double_t fval;
   TObject *f;
   TF1 *f1;
   Double_t allchan = 0;
   Int_t nonNullErrors = 0;
   TIter   next(fFunctions);
   for (i=first; i<=last;i++) {
      c1 = fH->GetBinContent(i);
      ymax = TMath::Max(ymax,c1);
      if (Hoption.Logy) {
         if (c1 > 0) ymin = TMath::Min(ymin,c1);
      } else {
         ymin = TMath::Min(ymin,c1);
      }
      if (Hoption.Error) {
         if (fH->GetBinErrorOption() == TH1::kNormal)
            e1 = fH->GetBinError(i);
         else
            e1 = fH->GetBinErrorUp(i);
         if (e1 > 0) nonNullErrors++;
         ymax = TMath::Max(ymax,c1+e1);
         if (fH->GetBinErrorOption() != TH1::kNormal)
            e1 = fH->GetBinErrorLow(i);

         if (Hoption.Logy) {
            if (c1-e1>0.01*TMath::Abs(c1)) ymin = TMath::Min(ymin,c1-e1);
         } else {
            ymin = TMath::Min(ymin,c1-e1);
         }
      }
      if (Hoption.Func) {
         xv[0] = fXaxis->GetBinCenter(i);
         while ((f = (TObject*) next())) {
            if (f->IsA() == TF1::Class()) {
               f1 = (TF1*)f;
               if (xv[0] < f1->GetXmin() || xv[0] > f1->GetXmax()) continue;
               fval = f1->Eval(xv[0],0,0);
               if (f1->GetMaximumStored() != -1111) fval = TMath::Min(f1->GetMaximumStored(), fval);
               ymax = TMath::Max(ymax,fval);
               if (Hoption.Logy) {
                  if (c1 > 0 && fval > 0.3*c1) ymin = TMath::Min(ymin,fval);
               }
            }
         }
         next.Reset();
      }
      allchan += c1;
   }
   if (!nonNullErrors) {
      if (Hoption.Error) {
         if (!Hoption.Mark && !Hoption.Line && !Hoption.Star && !Hoption.Curve) Hoption.Hist = 2;
         Hoption.Error=0;
      }
   }


   //     Take into account maximum , minimum

   if (Hoption.Logy && ymin <= 0) {
      if (ymax >= 1) ymin = TMath::Max(.005,ymax*1e-10);
      else           ymin = 0.001*ymax;
   }

   Double_t xm = ymin;
   if (maximum) ymax = fH->GetMaximumStored();
   if (minimum) xm   = fH->GetMinimumStored();
   if (Hoption.Logy && xm < 0) {
      Error(where, "log scale requested with a negative argument (%f)", xm);
      return 0;
   } else if (Hoption.Logy && xm>=0 && ymax==0) { // empty histogram in log scale
      ymin = 0.01;
      ymax = 10.;
   } else {
      ymin = xm;
   }

   if (ymin >= ymax) {
      if (Hoption.Logy) {
         if (ymax > 0) ymin = 0.001*ymax;
         else {
            if (!Hoption.Same) Error(where, "log scale is requested but maximum is less or equal 0 (%f)", ymax);
            return 0;
         }
      }
      else {
         if (ymin > 0) {
            ymin = 0;
            ymax *= 2;
         } else if (ymin < 0) {
            ymax = 0;
            ymin *= 2;
         } else {
            ymin = 0;
            ymax = 1;
         }
      }
   }

   // In some cases, mainly because of precision issues, ymin and ymax could almost equal.
   if (TMath::AreEqualRel(ymin,ymax,1E-15)) {
      ymin = ymin*(1-1E-14);
      ymax = ymax*(1+1E-14);
   }

   //     take into account normalization factor
   Hparam.allchan = allchan;
   Double_t factor = allchan;
   if (fH->GetNormFactor() > 0) factor = fH->GetNormFactor();
   if (allchan) factor /= allchan;
   if (factor == 0) factor = 1;
   Hparam.factor = factor;
   ymax = factor*ymax;
   ymin = factor*ymin;
   //just in case the norm factor is negative
   // this may happen with a positive norm factor and a negative integral !
   if (ymax < ymin) {
      Double_t temp = ymax;
      ymax = ymin;
      ymin = temp;
   }

   //         For log scales, histogram coordinates are LOG10(ymin) and
   //         LOG10(ymax). Final adjustment (if not option "Same"
   //         or "+" for ymax) of ymax and ymin for logarithmic scale, if
   //         Maximum and Minimum are not defined.
   if (Hoption.Logy) {
      if (ymin <=0 || ymax <=0) {
         Error(where, "Cannot set Y axis to log scale");
         return 0;
      }
      ymin = TMath::Log10(ymin);
      if (!minimum) ymin += TMath::Log10(0.5);
      ymax = TMath::Log10(ymax);
      if (!maximum) ymax += TMath::Log10(2*(0.9/0.95));
      if (!Hoption.Same) {
         Hparam.ymin = ymin;
         Hparam.ymax = ymax;
      }
      return 1;
   }

   //         final adjustment of ymin for linear scale.
   //         if minimum is not set , then ymin is set to zero if >0
   //         or to ymin - margin if <0.
   if (!minimum) {
      if (gStyle->GetHistMinimumZero()) {
         if (ymin >= 0) ymin = 0;
         else           ymin -= yMARGIN*(ymax-ymin);
      } else {
         Double_t dymin = yMARGIN*(ymax-ymin);
         if (ymin >= 0 && (ymin-dymin <= 0)) ymin  = 0;
         else                                ymin -= dymin;
      }
   }

   //         final adjustment of YMAXI for linear scale (if not option "Same"):
   //         decrease histogram height to MAX% of allowed height if HMAXIM
   //         has not been called.
   if (!maximum) {
      ymax += yMARGIN*(ymax-ymin);
   }

   Hparam.ymin = ymin;
   Hparam.ymax = ymax;
   return 1;
}


//______________________________________________________________________________
Int_t THistPainter::PaintInitH()
{
   /* Begin_html
   Compute histogram parameters used by the drawing routines for a rotated pad.
   End_html */

   static const char *where = "PaintInitH";
   Double_t yMARGIN = gStyle->GetHistTopMargin();
   Int_t maximum = 0;
   Int_t minimum = 0;
   if (fH->GetMaximumStored() != -1111) maximum = 1;
   if (fH->GetMinimumStored() != -1111) minimum = 1;

   //     Compute X axis parameters

   Int_t last      = fXaxis->GetLast();
   Int_t first     = fXaxis->GetFirst();
   Hparam.xlowedge = fXaxis->GetBinLowEdge(first);
   Hparam.xbinsize = fXaxis->GetBinWidth(first);
   Hparam.xlast    = last;
   Hparam.xfirst   = first;
   Hparam.ymin     = Hparam.xlowedge;
   Hparam.ymax     = fXaxis->GetBinLowEdge(last)+fXaxis->GetBinWidth(last);

   //       if log scale in Y, replace ymin,max by the log
   if (Hoption.Logy) {
      if (Hparam.xlowedge <=0 ) {
         Hparam.xlowedge = 0.1*Hparam.xbinsize;
         Hparam.ymin  = Hparam.xlowedge;
      }
      if (Hparam.ymin <=0 || Hparam.ymax <=0) {
         Error(where, "cannot set Y axis to log scale");
         return 0;
      }
      Hparam.xfirst= fXaxis->FindFixBin(Hparam.ymin);
      Hparam.xlast = fXaxis->FindFixBin(Hparam.ymax);
      Hparam.ymin  = TMath::Log10(Hparam.ymin);
      Hparam.ymax  = TMath::Log10(Hparam.ymax);
      if (Hparam.xlast > last) Hparam.xlast = last;
   }

   //     Compute Y axis parameters
   Double_t bigp = TMath::Power(10,32);
   Double_t xmax = -bigp;
   Double_t xmin = bigp;
   Double_t c1, e1;
   Double_t xv[1];
   Double_t fval;
   Int_t i;
   TObject *f;
   TF1 *f1;
   Double_t allchan = 0;
   TIter   next(fFunctions);
   for (i=first; i<=last;i++) {
      c1 = fH->GetBinContent(i);
      xmax = TMath::Max(xmax,c1);
      xmin = TMath::Min(xmin,c1);
      if (Hoption.Error) {
         e1 = fH->GetBinError(i);
         xmax = TMath::Max(xmax,c1+e1);
         xmin = TMath::Min(xmin,c1-e1);
      }
      if (Hoption.Func) {
         xv[0] = fXaxis->GetBinCenter(i);
         while ((f = (TObject*) next())) {
            if (f->IsA() == TF1::Class()) {
               f1 = (TF1*)f;
               if (xv[0] < f1->GetXmin() || xv[0] > f1->GetXmax()) continue;
               fval = f1->Eval(xv[0],0,0);
               xmax = TMath::Max(xmax,fval);
               if (Hoption.Logy) {
                  if (fval > 0.3*c1) xmin = TMath::Min(xmin,fval);
               }
            }
         }
         next.Reset();
      }
      allchan += c1;
   }

   //     Take into account maximum , minimum

   if (Hoption.Logx && xmin <= 0) {
      if (xmax >= 1) xmin = TMath::Max(.5,xmax*1e-10);
      else           xmin = 0.001*xmax;
   }
   Double_t xm = xmin;
   if (maximum) xmax = fH->GetMaximumStored();
   if (minimum) xm   = fH->GetMinimumStored();
   if (Hoption.Logx && xm <= 0) {
      Error(where, "log scale requested with zero or negative argument (%f)", xm);
      return 0;
   }
   else xmin = xm;
   if (xmin >= xmax) {
      if (Hoption.Logx) {
         if (xmax > 0) xmin = 0.001*xmax;
         else {
            if (!Hoption.Same) Error(where, "log scale is requested but maximum is less or equal 0 (%f)", xmax);
            return 0;
         }
      }
      else {
         if (xmin > 0) {
            xmin = 0;
            xmax *= 2;
         } else if (xmin < 0) {
            xmax = 0;
            xmin *= 2;
         } else {
            xmin = -1;
            xmax = 1;
         }
      }
   }

   //     take into account normalization factor
   Hparam.allchan = allchan;
   Double_t factor = allchan;
   if (fH->GetNormFactor() > 0) factor = fH->GetNormFactor();
   if (allchan) factor /= allchan;
   if (factor == 0) factor = 1;
   Hparam.factor = factor;
   xmax = factor*xmax;
   xmin = factor*xmin;

   //         For log scales, histogram coordinates are LOG10(ymin) and
   //         LOG10(ymax). Final adjustment (if not option "Same"
   //         or "+" for ymax) of ymax and ymin for logarithmic scale, if
   //         Maximum and Minimum are not defined.
   if (Hoption.Logx) {
      if (xmin <=0 || xmax <=0) {
         Error(where, "Cannot set Y axis to log scale");
         return 0;
      }
      xmin = TMath::Log10(xmin);
      if (!minimum) xmin += TMath::Log10(0.5);
      xmax = TMath::Log10(xmax);
      if (!maximum) xmax += TMath::Log10(2*(0.9/0.95));
      if (!Hoption.Same) {
         Hparam.xmin = xmin;
         Hparam.xmax = xmax;
      }
      return 1;
   }

   //         final adjustment of ymin for linear scale.
   //         if minimum is not set , then ymin is set to zero if >0
   //         or to ymin - margin if <0.
   if (!minimum) {
      if (xmin >= 0) xmin = 0;
      else           xmin -= yMARGIN*(xmax-xmin);
   }

   //         final adjustment of YMAXI for linear scale (if not option "Same"):
   //         decrease histogram height to MAX% of allowed height if HMAXIM
   //         has not been called.
   if (!maximum) {
      xmax += yMARGIN*(xmax-xmin);
   }
   Hparam.xmin = xmin;
   Hparam.xmax = xmax;
   return 1;
}


//______________________________________________________________________________
void THistPainter::PaintH3Iso()
{
   /* Begin_html
   <a href="#HP25">Control function to draw a 3D histogram with Iso Surfaces.</a>
   End_html */

   const Double_t ydiff = 1;
   const Double_t yligh1 = 10;
   const Double_t qa = 0.15;
   const Double_t qd = 0.15;
   const Double_t qs = 0.8;
   Double_t fmin, fmax;
   Int_t i, irep;
   Int_t nbcol = 28;
   Int_t icol1 = 201;
   Int_t ic1 = icol1;
   Int_t ic2 = ic1+nbcol;
   Int_t ic3 = ic2+nbcol;

   TGaxis *axis = new TGaxis();
   TAxis *xaxis = fH->GetXaxis();
   TAxis *yaxis = fH->GetYaxis();
   TAxis *zaxis = fH->GetZaxis();

   Int_t nx = fH->GetNbinsX();
   Int_t ny = fH->GetNbinsY();
   Int_t nz = fH->GetNbinsZ();

   Double_t *x = new Double_t[nx];
   Double_t *y = new Double_t[ny];
   Double_t *z = new Double_t[nz];

   for (i=0; i<nx; i++) x[i] = xaxis->GetBinCenter(i+1);
   for (i=0; i<ny; i++) y[i] = yaxis->GetBinCenter(i+1);
   for (i=0; i<nz; i++) z[i] = zaxis->GetBinCenter(i+1);

   fXbuf[0] = xaxis->GetBinLowEdge(xaxis->GetFirst());
   fYbuf[0] = xaxis->GetBinUpEdge(xaxis->GetLast());
   fXbuf[1] = yaxis->GetBinLowEdge(yaxis->GetFirst());
   fYbuf[1] = yaxis->GetBinUpEdge(yaxis->GetLast());
   fXbuf[2] = zaxis->GetBinLowEdge(zaxis->GetFirst());
   fYbuf[2] = zaxis->GetBinUpEdge(zaxis->GetLast());

   Double_t s[3];
   s[0] = fH->GetSumOfWeights()/(fH->GetNbinsX()*fH->GetNbinsY()*fH->GetNbinsZ());
   s[1] = 0.5*s[0];
   s[2] = 1.5*s[0];

   fLego = new TPainter3dAlgorithms(fXbuf, fYbuf);

   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintH3Iso", "no TView in current pad");
      delete [] x;
      delete [] y;
      delete [] z;
      return;
   }
   Double_t thedeg =  90 - gPad->GetTheta();
   Double_t phideg = -90 - gPad->GetPhi();
   Double_t psideg = view->GetPsi();
   view->SetView(phideg, thedeg, psideg, irep);

   Int_t backcolor = gPad->GetFrameFillColor();
   if (Hoption.System != kCARTESIAN) backcolor = 0;
   view->PadRange(backcolor);

   Double_t dcol = 0.5/Double_t(nbcol);
   TColor *colref = gROOT->GetColor(fH->GetFillColor());
   if (!colref) {
      delete [] x;
      delete [] y;
      delete [] z;
      return;
   }
   Float_t r, g, b, hue, light, satur;
   colref->GetRGB(r,g,b);
   TColor::RGBtoHLS(r,g,b,hue,light,satur);
   TColor *acol;
   for (Int_t col=0;col<nbcol;col++) {
      acol = gROOT->GetColor(col+icol1);
      TColor::HLStoRGB(hue, .4+col*dcol, satur, r, g, b);
      if (acol) acol->SetRGB(r, g, b);
   }

   fLego->InitMoveScreen(-1.1,1.1);

   if (Hoption.BackBox) {
      fLego->DefineGridLevels(fZaxis->GetNdivisions()%100);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove1);
      fLego->BackBox(90);
   }

   fLego->LightSource(0, ydiff, 0, 0, 0, irep);
   fLego->LightSource(1, yligh1, 1, 1, 1, irep);
   fLego->SurfaceProperty(qa, qd, qs, 1, irep);
   fmin = ydiff*qa;
   fmax = ydiff*qa + (yligh1+0.1)*(qd+qs);
   fLego->SetIsoSurfaceParameters(fmin, fmax, nbcol, ic1, ic2, ic3);

   fLego->IsoSurface(1, s, nx, ny, nz, x, y, z, "BF");

   if (Hoption.FrontBox) {
      fLego->InitMoveScreen(-1.1,1.1);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove2);
      fLego->FrontBox(90);
   }
   if (!Hoption.Axis && !Hoption.Same) PaintLegoAxis(axis, 90);

   PaintTitle();

   delete axis;
   delete fLego; fLego = 0;
   delete [] x;
   delete [] y;
   delete [] z;
}


//______________________________________________________________________________
void THistPainter::PaintLego(Option_t *)
{
   /* Begin_html
   <a href="#HP17">Control function to draw a 2D histogram as a lego plot.</a>
   End_html */

   Int_t raster = 1;
   if (Hparam.zmin == 0 && Hparam.zmax == 0) {Hparam.zmin = -1; Hparam.zmax = 1;}
   Int_t   nx      = Hparam.xlast - Hparam.xfirst + 1;
   Int_t   ny      = Hparam.ylast - Hparam.yfirst + 1;
   Double_t zmin   = Hparam.zmin;
   Double_t zmax   = Hparam.zmax;
   Double_t xlab1  = Hparam.xmin;
   Double_t xlab2  = Hparam.xmax;
   Double_t ylab1  = Hparam.ymin;
   Double_t ylab2  = Hparam.ymax;
   Double_t dangle = 10*3.141592/180; //Delta angle for Rapidity option
   Double_t deltaz = TMath::Abs(zmin);
   if (deltaz == 0) deltaz = 1;
   if (zmin >= zmax) {
      zmin -= 0.5*deltaz;
      zmax += 0.5*deltaz;
   }
   Double_t z1c = zmin;
   Double_t z2c = zmin + (zmax-zmin)*(1+gStyle->GetHistTopMargin());

   //     Compute the lego limits and instantiate a lego object
   fXbuf[0] = -1;
   fYbuf[0] =  1;
   fXbuf[1] = -1;
   fYbuf[1] =  1;
   if (Hoption.System == kPOLAR) {
      fXbuf[2] = z1c;
      fYbuf[2] = z2c;
   } else if (Hoption.System == kCYLINDRICAL) {
      if (Hoption.Logy) {
         if (ylab1 > 0) fXbuf[2] = TMath::Log10(ylab1);
         else           fXbuf[2] = 0;
         if (ylab2 > 0) fYbuf[2] = TMath::Log10(ylab2);
         else           fYbuf[2] = 0;
      } else {
         fXbuf[2] = ylab1;
         fYbuf[2] = ylab2;
      }
      z1c = 0; z2c = 1;
   } else if (Hoption.System == kSPHERICAL) {
      fXbuf[2] = -1;
      fYbuf[2] =  1;
      z1c = 0; z2c = 1;
   } else if (Hoption.System == kRAPIDITY) {
      fXbuf[2] = -1/TMath::Tan(dangle);
      fYbuf[2] =  1/TMath::Tan(dangle);
   } else {
      fXbuf[0] = xlab1;
      fYbuf[0] = xlab2;
      fXbuf[1] = ylab1;
      fYbuf[1] = ylab2;
      fXbuf[2] = z1c;
      fYbuf[2] = z2c;
      raster  = 0;
   }

   fLego = new TPainter3dAlgorithms(fXbuf, fYbuf, Hoption.System);

   Int_t nids = -1;
   TH1 * hid = NULL;
   Color_t colormain = -1, colordark = -1;
   Bool_t drawShadowsInLego1 = kTRUE;

   // LEGO3 is like LEGO1 except that the black lines around each lego are not drawn.
   if (Hoption.Lego == 13) {
      Hoption.Lego = 11;
      fLego->SetMesh(0);
   }
   // LEGO4 is like LEGO1 except no shadows are drawn.
   if (Hoption.Lego == 14) {
      Hoption.Lego = 11;
      drawShadowsInLego1 = kFALSE;
   }

   //          Create axis object

   TGaxis *axis = new TGaxis();

   //                  Initialize the levels on the Z axis
   Int_t ndiv   = fH->GetContour();
   if (ndiv == 0 ) {
      ndiv = gStyle->GetNumberContours();
      fH->SetContour(ndiv);
   }
   Int_t ndivz  = TMath::Abs(ndiv);
   if (fH->TestBit(TH1::kUserContour) == 0) fH->SetContour(ndiv);

   //     Initialize colors
   if (!fStack) {
      fLego->SetEdgeAtt(fH->GetLineColor(),fH->GetLineStyle(),fH->GetLineWidth(),0);
   } else {
      for (Int_t id=0;id<=fStack->GetSize();id++) {
         hid = (TH1*)fStack->At((id==0)?id:id-1);
         fLego->SetEdgeAtt(hid->GetLineColor(),hid->GetLineStyle(),hid->GetLineWidth(),id);
      }
   }

   if (Hoption.Lego == 11) {
      nids = 1;
      if (fStack) nids = fStack->GetSize();
      hid = fH;
      for (Int_t id=0;id<=nids;id++) {
         if (id > 0 && fStack) hid = (TH1*)fStack->At(id-1);
         colormain = hid->GetFillColor();
         if (colormain == 1) colormain = 17; //avoid drawing with black
         if (drawShadowsInLego1) colordark = TColor::GetColorDark(colormain);
         else                    colordark = colormain;
         fLego->SetColorMain(colormain,id);
         fLego->SetColorDark(colordark,id);
         if (id <= 1)    fLego->SetColorMain(colormain,-1);  // Set Bottom color
         if (id == nids) fLego->SetColorMain(colormain,99);  // Set Top color
      }
   }

   //     Now ready to draw the lego plot
   Int_t irep = 0;

   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintLego", "no TView in current pad");
      return;
   }

   Double_t thedeg =  90 - gPad->GetTheta();
   Double_t phideg = -90 - gPad->GetPhi();
   Double_t psideg = view->GetPsi();
   view->SetView(phideg, thedeg, psideg, irep);

   fLego->SetLineColor(kBlack);  /// zgrid color for lego1 & lego2
   fLego->SetFillStyle(fH->GetFillStyle());

   //     Set color/style for back box
   fLego->SetFillStyle(gPad->GetFrameFillStyle());
   fLego->SetFillColor(gPad->GetFrameFillColor());
   fLego->TAttFill::Modify();

   Int_t backcolor = gPad->GetFrameFillColor();
   if (Hoption.System != kCARTESIAN) backcolor = 0;
   view->PadRange(backcolor);

   fLego->SetFillStyle(fH->GetFillStyle());
   fLego->SetFillColor(fH->GetFillColor());
   fLego->TAttFill::Modify();

   fLego->DefineGridLevels(fZaxis->GetNdivisions()%100);

   if (raster) fLego->InitRaster(-1.1,-1.1,1.1,1.1,1000,800);
   else        fLego->InitMoveScreen(-1.1,1.1);

   if (Hoption.Lego == 11 || Hoption.Lego == 12) {
      if (Hoption.System == kCARTESIAN && Hoption.BackBox) {
         fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove1);
         fLego->BackBox(90);
      }
   }

   if (Hoption.Lego == 12) DefineColorLevels(ndivz);

   fLego->SetLegoFunction(&TPainter3dAlgorithms::LegoFunction);
   if (Hoption.Lego ==  1) fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceRaster2);
   if (Hoption.Lego == 11) fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMode3);
   if (Hoption.Lego == 12) fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMode2);
   if (Hoption.System == kPOLAR) {
      if (Hoption.Lego ==  1) fLego->LegoPolar(1,nx,ny,"FB");
      if (Hoption.Lego == 11) fLego->LegoPolar(1,nx,ny,"BF");
      if (Hoption.Lego == 12) fLego->LegoPolar(1,nx,ny,"BF");
   } else if (Hoption.System == kCYLINDRICAL) {
      if (Hoption.Lego ==  1) fLego->LegoCylindrical(1,nx,ny,"FB");
      if (Hoption.Lego == 11) fLego->LegoCylindrical(1,nx,ny,"BF");
      if (Hoption.Lego == 12) fLego->LegoCylindrical(1,nx,ny,"BF");
   } else if (Hoption.System == kSPHERICAL) {
      if (Hoption.Lego ==  1) fLego->LegoSpherical(0,1,nx,ny,"FB");
      if (Hoption.Lego == 11) fLego->LegoSpherical(0,1,nx,ny,"BF");
      if (Hoption.Lego == 12) fLego->LegoSpherical(0,1,nx,ny,"BF");
   } else if (Hoption.System == kRAPIDITY) {
      if (Hoption.Lego ==  1) fLego->LegoSpherical(1,1,nx,ny,"FB");
      if (Hoption.Lego == 11) fLego->LegoSpherical(1,1,nx,ny,"BF");
      if (Hoption.Lego == 12) fLego->LegoSpherical(1,1,nx,ny,"BF");
   } else {
      if (Hoption.Lego ==  1) {
                              fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove2);
                              fLego->LegoCartesian(90,nx,ny,"FB");}
      if (Hoption.Lego == 11) fLego->LegoCartesian(90,nx,ny,"BF");
      if (Hoption.Lego == 12) fLego->LegoCartesian(90,nx,ny,"BF");
   }

   if (Hoption.Lego == 1 || Hoption.Lego == 11) {
      if (Hoption.System == kCARTESIAN && Hoption.BackBox) {
         fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove1);
         fLego->BackBox(90);
      }
   }
   if (Hoption.System == kCARTESIAN) {
      fLego->InitMoveScreen(-1.1,1.1);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove2);
      if (Hoption.FrontBox) fLego->FrontBox(90);
   }
   if (!Hoption.Axis && !Hoption.Same) PaintLegoAxis(axis, 90);
   if (Hoption.Zscale) PaintPalette();
   delete axis;
   delete fLego; fLego = 0;
}


//______________________________________________________________________________
void THistPainter::PaintLegoAxis(TGaxis *axis, Double_t ang)
{
   /* Begin_html
   Draw the axis for legos and surface plots.
   End_html */

   static Double_t epsil = 0.001;

   Double_t cosa, sina;
   Double_t bmin, bmax;
   Double_t r[24]        /* was [3][8] */;
   Int_t ndivx, ndivy, ndivz, i;
   Double_t x1[3], x2[3], y1[3], y2[3], z1[3], z2[3], av[24]  /*  was [3][8] */;
   static char chopax[8], chopay[8], chopaz[8];
   Int_t ix1, ix2, iy1, iy2, iz1, iz2;
   Double_t rad;

   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintLegoAxis", "no TView in current pad");
      return;
   }

   // In polar coordinates, draw a short line going from the external circle
   // corresponding to r = 1 up to r = 1.1
   if (Hoption.System == kPOLAR) {
      r[0] = 1;
      r[1] = 0;
      r[2] = 0;
      view->WCtoNDC(r, x1);
      r[0] = 1.1;
      r[1] = 0;
      r[2] = 0;
      view->WCtoNDC(r, x2);
      gPad->PaintLine(x1[0],x1[1],x2[0],x2[1]);
      return;
   }

   if (Hoption.System != kCARTESIAN) return;

   rad = TMath::ATan(1.) * 4. /180.;
   cosa = TMath::Cos(ang*rad);
   sina = TMath::Sin(ang*rad);

   view->AxisVertex(ang, av, ix1, ix2, iy1, iy2, iz1, iz2);
   for (i = 1; i <= 8; ++i) {
      r[i*3 - 3] = av[i*3 - 3] + av[i*3 - 2]*cosa;
      r[i*3 - 2] = av[i*3 - 2]*sina;
      r[i*3 - 1] = av[i*3 - 1];
   }

   view->WCtoNDC(&r[ix1*3 - 3], x1);
   view->WCtoNDC(&r[ix2*3 - 3], x2);
   view->WCtoNDC(&r[iy1*3 - 3], y1);
   view->WCtoNDC(&r[iy2*3 - 3], y2);
   view->WCtoNDC(&r[iz1*3 - 3], z1);
   view->WCtoNDC(&r[iz2*3 - 3], z2);

   view->SetAxisNDC(x1, x2, y1, y2, z1, z2);

   Double_t *rmin = view->GetRmin();
   Double_t *rmax = view->GetRmax();
   if (!rmin || !rmax) return;

   // Initialize the axis options
   if (x1[0] > x2[0]) strlcpy(chopax, "SDH=+",8);
   else               strlcpy(chopax, "SDH=-",8);
   if (y1[0] > y2[0]) strlcpy(chopay, "SDH=+",8);
   else               strlcpy(chopay, "SDH=-",8);
   strlcpy(chopaz, "SDH+=",8);

   // Option LOG is required ?
   // coverity [Calling risky function]
   if (Hoption.Logx) strlcat(chopax,"G",8);
   // coverity [Calling risky function]
   if (Hoption.Logy) strlcat(chopay,"G",8);
   // coverity [Calling risky function]
   if (Hoption.Logz) strlcat(chopaz,"G",8);

   // Initialize the number of divisions. If the
   // number of divisions is negative, option 'N' is required.
   ndivx = fXaxis->GetNdivisions();
   ndivy = fYaxis->GetNdivisions();
   ndivz = fZaxis->GetNdivisions();
   if (ndivx < 0) {
      ndivx = TMath::Abs(ndivx);
      // coverity [Calling risky function]
      strlcat(chopax, "N",8);
   }
   if (ndivy < 0) {
      ndivy = TMath::Abs(ndivy);
      // coverity [Calling risky function]
      strlcat(chopay, "N",8);
   }
   if (ndivz < 0) {
      ndivz = TMath::Abs(ndivz);
      // coverity [Calling risky function]
      strlcat(chopaz, "N",8);
   }

   // Set Axis attributes.
   // The variable SCALE  rescales the VSIZ
   // in order to have the same label size for all angles.

   axis->SetLineWidth(1);

   // X axis drawing
   if (TMath::Abs(x1[0] - x2[0]) >= epsil || TMath::Abs(x1[1] - x2[1]) > epsil) {
      axis->ImportAxisAttributes(fXaxis);
      axis->SetLabelOffset(fXaxis->GetLabelOffset()+fXaxis->GetTickLength());
      if (Hoption.Logx && !fH->InheritsFrom(TH3::Class())) {
         bmin = TMath::Power(10, rmin[0]);
         bmax = TMath::Power(10, rmax[0]);
      } else {
         bmin = rmin[0];
         bmax = rmax[0];
      }
      // Option time display is required ?
      if (fXaxis->GetTimeDisplay()) {
         // coverity [Calling risky function]
         strlcat(chopax,"t",8);
         if (strlen(fXaxis->GetTimeFormatOnly()) == 0) {
            axis->SetTimeFormat(fXaxis->ChooseTimeFormat(bmax-bmin));
         } else {
            axis->SetTimeFormat(fXaxis->GetTimeFormat());
         }
      }
      axis->SetOption(chopax);
      axis->PaintAxis(x1[0], x1[1], x2[0], x2[1], bmin, bmax, ndivx, chopax);
   }

   // Y axis drawing
   if (TMath::Abs(y1[0] - y2[0]) >= epsil || TMath::Abs(y1[1] - y2[1]) > epsil) {
      axis->ImportAxisAttributes(fYaxis);
      axis->SetLabelOffset(fYaxis->GetLabelOffset()+fYaxis->GetTickLength());

      if (fH->GetDimension() < 2) {
         strlcpy(chopay, "V=+UN",8);
         ndivy = 0;
      }
      if (TMath::Abs(y1[0] - y2[0]) < epsil) {
         y2[0] = y1[0];
      }
      if (Hoption.Logy && !fH->InheritsFrom(TH3::Class())) {
         bmin = TMath::Power(10, rmin[1]);
         bmax = TMath::Power(10, rmax[1]);
      } else {
         bmin = rmin[1];
         bmax = rmax[1];
      }
      // Option time display is required ?
      if (fYaxis->GetTimeDisplay()) {
         // coverity [Calling risky function]
         strlcat(chopay,"t",8);
         if (strlen(fYaxis->GetTimeFormatOnly()) == 0) {
            axis->SetTimeFormat(fYaxis->ChooseTimeFormat(bmax-bmin));
         } else {
            axis->SetTimeFormat(fYaxis->GetTimeFormat());
         }
      }
      axis->SetOption(chopay);
      axis->PaintAxis(y1[0], y1[1], y2[0], y2[1], bmin, bmax, ndivy, chopay);
   }

   // Z axis drawing
   if (TMath::Abs(z1[0] - z2[0]) >= 100*epsil || TMath::Abs(z1[1] - z2[1]) > 100*epsil) {
      axis->ImportAxisAttributes(fZaxis);
      if (Hoption.Logz && !fH->InheritsFrom(TH3::Class())) {
         bmin = TMath::Power(10, rmin[2]);
         bmax = TMath::Power(10, rmax[2]);
      } else {
         bmin = rmin[2];
         bmax = rmax[2];
      }
      // Option time display is required ?
      if (fZaxis->GetTimeDisplay()) {
         // coverity [Calling risky function]
         strlcat(chopaz,"t",8);
         if (strlen(fZaxis->GetTimeFormatOnly()) == 0) {
            axis->SetTimeFormat(fZaxis->ChooseTimeFormat(bmax-bmin));
         } else {
            axis->SetTimeFormat(fZaxis->GetTimeFormat());
         }
      }
      axis->SetOption(chopaz);
      axis->PaintAxis(z1[0], z1[1], z2[0], z2[1], bmin, bmax, ndivz, chopaz);
   }

   //fH->SetLineStyle(1);  /// otherwise fEdgeStyle[i] gets overwritten!
}


//______________________________________________________________________________
void THistPainter::PaintPalette()
{
   /* Begin_html
   <a href="#HP22">Paint the color palette on the right side of the pad.</a>
   End_html */

   TPaletteAxis *palette = (TPaletteAxis*)fFunctions->FindObject("palette");
   TView *view = gPad->GetView();
   if (palette) {
      if (view) {
         if (!palette->TestBit(TPaletteAxis::kHasView)) {
            fFunctions->Remove(palette);
            delete palette; palette = 0;
         }
      } else {
         if (palette->TestBit(TPaletteAxis::kHasView)) {
            fFunctions->Remove(palette);
            delete palette; palette = 0;
         }
      }
   }

   if (!palette) {
      Double_t xup  = gPad->GetUxmax();
      Double_t x2   = gPad->PadtoX(gPad->GetX2());
      Double_t ymin = gPad->PadtoY(gPad->GetUymin());
      Double_t ymax = gPad->PadtoY(gPad->GetUymax());
      Double_t xr   = 0.05*(gPad->GetX2() - gPad->GetX1());
      Double_t xmin = gPad->PadtoX(xup +0.1*xr);
      Double_t xmax = gPad->PadtoX(xup + xr);
      if (xmax > x2) xmax = gPad->PadtoX(gPad->GetX2()-0.01*xr);
      palette = new TPaletteAxis(xmin,ymin,xmax,ymax,fH);
      fFunctions->AddFirst(palette);
      palette->Paint();
   }
}


//______________________________________________________________________________
void THistPainter::PaintScatterPlot(Option_t *option)
{
   /* Begin_html
   <a href="#HP11">Control function to draw a 2D histogram as a scatter plot.</a>
   End_html */

   fH->TAttMarker::Modify();

   Int_t k, marker;
   Double_t dz, z, xk,xstep, yk, ystep;
   Double_t scale = 1;
   Bool_t ltest  = kFALSE;
   Double_t zmax  = fH->GetMaximum();
   Double_t zmin  = fH->GetMinimum();
   if (zmin == 0 && zmax == 0) return;
   if (zmin == zmax) {
      zmax += 0.1*TMath::Abs(zmax);
      zmin -= 0.1*TMath::Abs(zmin);
   }
   Int_t ncells = (Hparam.ylast-Hparam.yfirst)*(Hparam.xlast-Hparam.xfirst);
   if (Hoption.Logz) {
      if (zmin > 0) zmin = TMath::Log10(zmin);
      else          zmin = 0;
      if (zmax > 0) zmax = TMath::Log10(zmax);
      else          zmax = 0;
      if (zmin == 0 && zmax == 0) return;
      dz = zmax - zmin;
      scale = 100/dz;
      if (ncells > 10000) scale /= 5;
      ltest = kTRUE;
   } else {
      dz = zmax - zmin;
      if (dz >= kNMAX || zmax < 1) {
         scale = (kNMAX-1)/dz;
         if (ncells > 10000) scale /= 5;
         ltest = kTRUE;
      }
   }
   if (fH->GetMinimumStored() == -1111) {
      Double_t yMARGIN = gStyle->GetHistTopMargin();
      if (gStyle->GetHistMinimumZero()) {
         if (zmin >= 0) zmin = 0;
         else           zmin -= yMARGIN*(zmax-zmin);
      } else {
         Double_t dzmin = yMARGIN*(zmax-zmin);
         if (zmin >= 0 && (zmin-dzmin <= 0)) zmin  = 0;
         else                                zmin -= dzmin;
      }
   }

   TString opt = option;
   opt.ToLower();
   if (opt.Contains("scat=")) {
      char optscat[100];
      strlcpy(optscat,opt.Data(),100);
      char *oscat = strstr(optscat,"scat=");
      char *blank = strstr(oscat," "); if (blank) *blank = 0;
      sscanf(oscat+5,"%lg",&scale);
   }
   // use an independent instance of a random generator
   // instead of gRandom to avoid conflicts and
   // to get same random numbers when drawing the same histogram
   TRandom2 random;
   marker=0;
   for (Int_t j=Hparam.yfirst; j<=Hparam.ylast;j++) {
      yk    = fYaxis->GetBinLowEdge(j);
      ystep = fYaxis->GetBinWidth(j);
      for (Int_t i=Hparam.xfirst; i<=Hparam.xlast;i++) {
         Int_t bin  = j*(fXaxis->GetNbins()+2) + i;
         xk    = fXaxis->GetBinLowEdge(i);
         xstep = fXaxis->GetBinWidth(i);
         if (!IsInside(xk+0.5*xstep,yk+0.5*ystep)) continue;
         z     = fH->GetBinContent(bin);
         if (z < zmin) z = zmin;
         if (z > zmax) z = zmax;
         if (Hoption.Logz) {
            if (z > 0) z = TMath::Log10(z) - zmin;
         } else {
            z    -=  zmin;
         }
         if (z <= 0) continue;
         k = Int_t(z*scale);
         if (ltest) k++;
         if (k > 0) {
            for (Int_t loop=0; loop<k; loop++) {
               if (k+marker >= kNMAX) {
                  gPad->PaintPolyMarker(marker, fXbuf, fYbuf);
                  marker=0;
               }
               fXbuf[marker] = (random.Rndm(loop)*xstep) + xk;
               fYbuf[marker] = (random.Rndm(loop)*ystep) + yk;
               if (Hoption.Logx) {
                  if (fXbuf[marker] > 0) fXbuf[marker] = TMath::Log10(fXbuf[marker]);
                  else                   break;
               }
               if (Hoption.Logy) {
                  if (fYbuf[marker] > 0) fYbuf[marker] = TMath::Log10(fYbuf[marker]);
                  else                  break;
               }
               if (fXbuf[marker] < gPad->GetUxmin()) break;
               if (fYbuf[marker] < gPad->GetUymin()) break;
               if (fXbuf[marker] > gPad->GetUxmax()) break;
               if (fYbuf[marker] > gPad->GetUymax()) break;
               marker++;
            }
         }
      }
   }
   if (marker > 0) gPad->PaintPolyMarker(marker, fXbuf, fYbuf);

   if (Hoption.Zscale) PaintPalette();
}


//______________________________________________________________________________
void THistPainter::PaintSpecialObjects(const TObject *obj, Option_t *option)
{
   /* Begin_html
   Static function to paint special objects like vectors and matrices.
   This function is called via gROOT->ProcessLine to paint these objects
   without having a direct dependency of the graphics or histogramming
   system.
   End_html */

   if (!obj) return;
   Bool_t status = TH1::AddDirectoryStatus();
   TH1::AddDirectory(kFALSE);

   if (obj->InheritsFrom(TMatrixFBase::Class())) {
      // case TMatrixF
      TH2F *R__TMatrixFBase = new TH2F((TMatrixFBase &)*obj);
      R__TMatrixFBase->SetBit(kCanDelete);
      R__TMatrixFBase->Draw(option);

   } else if (obj->InheritsFrom(TMatrixDBase::Class())) {
      // case TMatrixD
      TH2D *R__TMatrixDBase = new TH2D((TMatrixDBase &)*obj);
      R__TMatrixDBase->SetBit(kCanDelete);
      R__TMatrixDBase->Draw(option);

   } else if (obj->InheritsFrom(TVectorF::Class())) {
      //case TVectorF
      TH1F *R__TVectorF = new TH1F((TVectorF &)*obj);
      R__TVectorF->SetBit(kCanDelete);
      R__TVectorF->Draw(option);

   } else if (obj->InheritsFrom(TVectorD::Class())) {
      //case TVectorD
      TH1D *R__TVectorD = new TH1D((TVectorD &)*obj);
      R__TVectorD->SetBit(kCanDelete);
      R__TVectorD->Draw(option);
   }

   TH1::AddDirectory(status);
}


//______________________________________________________________________________
void THistPainter::PaintStat(Int_t dostat, TF1 *fit)
{
   /* Begin_html
   <a href="#HP07">Draw the statistics box for 1D and profile histograms.</a>
   End_html */

   static char t[100];
   Int_t dofit;
   TPaveStats *stats  = 0;
   TIter next(fFunctions);
   TObject *obj;
   while ((obj = next())) {
      if (obj->InheritsFrom(TPaveStats::Class())) {
         stats = (TPaveStats*)obj;
         break;
      }
   }

   if (stats && dostat) {
      dofit  = stats->GetOptFit();
      dostat = stats->GetOptStat();
   } else {
      dofit  = gStyle->GetOptFit();
   }
   if (!dofit) fit = 0;
   if (dofit  == 1) dofit  =  111;
   if (dostat == 1) dostat = 1111;
   Int_t print_name    = dostat%10;
   Int_t print_entries = (dostat/10)%10;
   Int_t print_mean    = (dostat/100)%10;
   Int_t print_rms     = (dostat/1000)%10;
   Int_t print_under   = (dostat/10000)%10;
   Int_t print_over    = (dostat/100000)%10;
   Int_t print_integral= (dostat/1000000)%10;
   Int_t print_skew    = (dostat/10000000)%10;
   Int_t print_kurt    = (dostat/100000000)%10;
   Int_t nlines = print_name + print_entries + print_mean + print_rms +
                  print_under + print_over + print_integral +
                  print_skew + print_kurt;
   Int_t print_fval    = dofit%10;
   Int_t print_ferrors = (dofit/10)%10;
   Int_t print_fchi2   = (dofit/100)%10;
   Int_t print_fprob   = (dofit/1000)%10;
   Int_t nlinesf = print_fval + print_fchi2 + print_fprob;
   if (fit) {
      if (print_fval < 2) nlinesf += fit->GetNumberFreeParameters();
      else                nlinesf += fit->GetNpar();
   }
   if (fH->InheritsFrom(TProfile::Class())) nlinesf += print_mean + print_rms;

   // Pavetext with statistics
   Bool_t done = kFALSE;
   if (!dostat && !fit) {
      if (stats) { fFunctions->Remove(stats); delete stats;}
      return;
   }
   Double_t  statw  = gStyle->GetStatW();
   if (fit) statw   = 1.8*gStyle->GetStatW();
   Double_t  stath  = (nlines+nlinesf)*gStyle->GetStatFontSize();
   if (stath <= 0 || 3 == (gStyle->GetStatFont()%10)) {
      stath = 0.25*(nlines+nlinesf)*gStyle->GetStatH();
   }
   if (stats) {
      stats->Clear();
      done = kTRUE;
   } else {
      stats  = new TPaveStats(
               gStyle->GetStatX()-statw,
               gStyle->GetStatY()-stath,
               gStyle->GetStatX(),
               gStyle->GetStatY(),"brNDC");

      stats->SetParent(fH);
      stats->SetOptFit(dofit);
      stats->SetOptStat(dostat);
      stats->SetFillColor(gStyle->GetStatColor());
      stats->SetFillStyle(gStyle->GetStatStyle());
      stats->SetBorderSize(gStyle->GetStatBorderSize());
      stats->SetTextFont(gStyle->GetStatFont());
      if (gStyle->GetStatFont()%10 > 2)
         stats->SetTextSize(gStyle->GetStatFontSize());
      stats->SetFitFormat(gStyle->GetFitFormat());
      stats->SetStatFormat(gStyle->GetStatFormat());
      stats->SetName("stats");

      stats->SetTextColor(gStyle->GetStatTextColor());
      stats->SetTextAlign(12);
      stats->SetBit(kCanDelete);
      stats->SetBit(kMustCleanup);
   }
   if (print_name)  stats->AddText(fH->GetName());
   if (print_entries) {
      if (fH->GetEntries() < 1e7) snprintf(t,100,"%s = %-7d",gStringEntries.Data(),Int_t(fH->GetEntries()+0.5));
      else                        snprintf(t,100,"%s = %14.7g",gStringEntries.Data(),Float_t(fH->GetEntries()));
      stats->AddText(t);
   }
   char textstats[50];
   if (print_mean) {
      if (print_mean == 1) {
         snprintf(textstats,50,"%s  = %s%s",gStringMean.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->GetMean(1));
      } else {
         snprintf(textstats,50,"%s  = %s%s #pm %s%s",gStringMean.Data(),"%",stats->GetStatFormat()
                                                  ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->GetMean(1),fH->GetMeanError(1));
      }
      stats->AddText(t);
      if (fH->InheritsFrom(TProfile::Class())) {
         if (print_mean == 1) {
            snprintf(textstats,50,"%s = %s%s",gStringMeanY.Data(),"%",stats->GetStatFormat());
            snprintf(t,100,textstats,fH->GetMean(2));
         } else {
            snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringMeanY.Data(),"%",stats->GetStatFormat()
                                                      ,"%",stats->GetStatFormat());
            snprintf(t,100,textstats,fH->GetMean(2),fH->GetMeanError(2));
         }
         stats->AddText(t);
      }
   }
   if (print_rms) {
      if (print_rms == 1) {
         snprintf(textstats,50,"%s   = %s%s",gStringRMS.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->GetRMS(1));
      } else {
         snprintf(textstats,50,"%s   = %s%s #pm %s%s",gStringRMS.Data(),"%",stats->GetStatFormat()
                                                  ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->GetRMS(1),fH->GetRMSError(1));
      }
      stats->AddText(t);
      if (fH->InheritsFrom(TProfile::Class())) {
         if (print_rms == 1) {
            snprintf(textstats,50,"%s = %s%s",gStringRMSY.Data(),"%",stats->GetStatFormat());
            snprintf(t,100,textstats,fH->GetRMS(2));
         } else {
            snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringRMSY.Data(),"%",stats->GetStatFormat()
                                                     ,"%",stats->GetStatFormat());
            snprintf(t,100,textstats,fH->GetRMS(2),fH->GetRMSError(2));
         }
         stats->AddText(t);
      }
   }
   if (print_under) {
      snprintf(textstats,50,"%s = %s%s",gStringUnderflow.Data(),"%",stats->GetStatFormat());
      snprintf(t,100,textstats,fH->GetBinContent(0));
      stats->AddText(t);
   }
   if (print_over) {
      snprintf(textstats,50,"%s  = %s%s",gStringOverflow.Data(),"%",stats->GetStatFormat());
      snprintf(t,100,textstats,fH->GetBinContent(fXaxis->GetNbins()+1));
      stats->AddText(t);
   }
   if (print_integral) {
      if (print_integral == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringIntegral.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->Integral());
      } else {
         snprintf(textstats,50,"%s = %s%s",gStringIntegralBinWidth.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->Integral("width"));
      }
      stats->AddText(t);
   }
   if (print_skew) {
      if (print_skew == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringSkewness.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->GetSkewness(1));
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringSkewness.Data(),"%",stats->GetStatFormat()
                                                     ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->GetSkewness(1),fH->GetSkewness(11));
      }
      stats->AddText(t);
   }
   if (print_kurt) {
      if (print_kurt == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringKurtosis.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->GetKurtosis(1));
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringKurtosis.Data(),"%",stats->GetStatFormat()
                                                     ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,fH->GetKurtosis(1),fH->GetKurtosis(11));
      }
      stats->AddText(t);
   }

   // Draw Fit parameters
   if (fit) {
      Int_t ndf = fit->GetNDF();
      snprintf(textstats,50,"#chi^{2} / ndf = %s%s / %d","%",stats->GetFitFormat(),ndf);
      snprintf(t,100,textstats,(Float_t)fit->GetChisquare());
      if (print_fchi2) stats->AddText(t);
      if (print_fprob) {
         snprintf(textstats,50,"Prob  = %s%s","%",stats->GetFitFormat());
         snprintf(t,100,textstats,(Float_t)TMath::Prob(fit->GetChisquare(),ndf));
         stats->AddText(t);
      }
      if (print_fval || print_ferrors) {
         Double_t parmin,parmax;
         Int_t a;
         for (Int_t ipar=0;ipar<fit->GetNpar();ipar++) {
            fit->GetParLimits(ipar,parmin,parmax);
            if (print_fval < 2 && parmin*parmax != 0 && parmin >= parmax) continue;
            snprintf(t,100,"%-8s ",fit->GetParName(ipar));
            a = strlen(t);
            if (a>50) a = 50;
            if (print_ferrors) {
               snprintf(textstats,50,"= %s%s #pm %s ", "%",stats->GetFitFormat(),
                       GetBestFormat(fit->GetParameter(ipar), fit->GetParError(ipar), stats->GetFitFormat()));
               snprintf(&t[a],100,textstats,(Float_t)fit->GetParameter(ipar)
                               ,(Float_t)fit->GetParError(ipar));
            } else {
               snprintf(textstats,50,"= %s%s ","%",stats->GetFitFormat());
               snprintf(&t[a],100,textstats,(Float_t)fit->GetParameter(ipar));
            }
            t[63] = 0;
            stats->AddText(t);
         }
      }
   }

   if (!done) fFunctions->Add(stats);
   stats->Paint();
}


//______________________________________________________________________________
void THistPainter::PaintStat2(Int_t dostat, TF1 *fit)
{
   /* Begin_html
   <a href="#HP07">Draw the statistics box for 2D histograms.</a>
   End_html */

   if (fH->GetDimension() != 2) return;
   TH2 *h2 = (TH2*)fH;

   static char t[100];
   Int_t dofit;
   TPaveStats *stats  = 0;
   TIter next(fFunctions);
   TObject *obj;
   while ((obj = next())) {
      if (obj->InheritsFrom(TPaveStats::Class())) {
         stats = (TPaveStats*)obj;
         break;
      }
   }
   if (stats && dostat) {
      dofit  = stats->GetOptFit();
      dostat = stats->GetOptStat();
   } else {
      dofit  = gStyle->GetOptFit();
   }
   if (dostat == 1) dostat = 1111;
   Int_t print_name    = dostat%10;
   Int_t print_entries = (dostat/10)%10;
   Int_t print_mean    = (dostat/100)%10;
   Int_t print_rms     = (dostat/1000)%10;
   Int_t print_under   = (dostat/10000)%10;
   Int_t print_over    = (dostat/100000)%10;
   Int_t print_integral= (dostat/1000000)%10;
   Int_t print_skew    = (dostat/10000000)%10;
   Int_t print_kurt    = (dostat/100000000)%10;
   Int_t nlines = print_name + print_entries + 2*print_mean + 2*print_rms + print_integral;
   if (print_under || print_over) nlines += 3;

   // Pavetext with statistics
   if (!gStyle->GetOptFit()) fit = 0;
   Bool_t done = kFALSE;
   if (!dostat && !fit) {
      if (stats) { fFunctions->Remove(stats); delete stats;}
      return;
   }
   Double_t  statw  = gStyle->GetStatW();
   if (fit) statw   = 1.8*gStyle->GetStatW();
   Double_t  stath  = nlines*gStyle->GetStatFontSize();
   if (stath <= 0 || 3 == (gStyle->GetStatFont()%10)) {
      stath = 0.25*nlines*gStyle->GetStatH();
   }
   if (fit) stath += gStyle->GetStatH();
   if (stats) {
      stats->Clear();
      done = kTRUE;
   } else {
      stats  = new TPaveStats(
               gStyle->GetStatX()-statw,
               gStyle->GetStatY()-stath,
               gStyle->GetStatX(),
               gStyle->GetStatY(),"brNDC");

      stats->SetParent(fH);
      stats->SetOptFit(dofit);
      stats->SetOptStat(dostat);
      stats->SetFillColor(gStyle->GetStatColor());
      stats->SetFillStyle(gStyle->GetStatStyle());
      stats->SetBorderSize(gStyle->GetStatBorderSize());
      stats->SetName("stats");

      stats->SetTextColor(gStyle->GetStatTextColor());
      stats->SetTextAlign(12);
      stats->SetTextFont(gStyle->GetStatFont());
      if (gStyle->GetStatFont()%10 > 2)
         stats->SetTextSize(gStyle->GetStatFontSize());
      stats->SetFitFormat(gStyle->GetFitFormat());
      stats->SetStatFormat(gStyle->GetStatFormat());
      stats->SetBit(kCanDelete);
      stats->SetBit(kMustCleanup);
   }
   if (print_name)  stats->AddText(h2->GetName());
   if (print_entries) {
      if (h2->GetEntries() < 1e7) snprintf(t,100,"%s = %-7d",gStringEntries.Data(),Int_t(h2->GetEntries()+0.5));
      else                        snprintf(t,100,"%s = %14.7g",gStringEntries.Data(),Float_t(h2->GetEntries()));
      stats->AddText(t);
   }
   char textstats[50];
   if (print_mean) {
      if (print_mean == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringMeanX.Data(),"%",stats->GetStatFormat());
         snprintf(t,50,textstats,h2->GetMean(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringMeanY.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetMean(2));
         stats->AddText(t);
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringMeanX.Data(),"%",stats->GetStatFormat()
                                                   ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetMean(1),h2->GetMeanError(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringMeanY.Data(),"%",stats->GetStatFormat()
                                                   ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetMean(2),h2->GetMeanError(2));
         stats->AddText(t);
      }
   }
   if (print_rms) {
      if (print_rms == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringRMSX.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetRMS(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringRMSY.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetRMS(2));
         stats->AddText(t);
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringRMSX.Data(),"%",stats->GetStatFormat()
                                                  ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetRMS(1),h2->GetRMSError(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringRMSY.Data(),"%",stats->GetStatFormat()
                                                  ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetRMS(2),h2->GetRMSError(2));
         stats->AddText(t);
      }
   }
   if (print_integral) {
      snprintf(t,100,"%s  = %6.4g",gStringIntegral.Data(),h2->Integral());
      stats->AddText(t);
   }
   if (print_skew) {
      if (print_skew == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringSkewnessX.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetSkewness(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringSkewnessY.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetSkewness(2));
         stats->AddText(t);
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringSkewnessX.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetSkewness(1),h2->GetSkewness(11));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringSkewnessY.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetSkewness(2),h2->GetSkewness(12));
         stats->AddText(t);
      }
   }
   if (print_kurt) {
      if (print_kurt == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringKurtosisX.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetKurtosis(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringKurtosisY.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetKurtosis(2));
         stats->AddText(t);
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringKurtosisX.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetKurtosis(1),h2->GetKurtosis(11));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringKurtosisY.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h2->GetKurtosis(2),h2->GetKurtosis(12));
         stats->AddText(t);
      }
   }
   if (print_under || print_over) {
      //get 3*3 under/overflows for 2d hist
      Double_t unov[9];

      Int_t cellsX = h2->GetXaxis()->GetNbins() + 1;
      Int_t cellsY = h2->GetYaxis()->GetNbins() + 1;
      Int_t firstX = std::max(1, h2->GetXaxis()->GetFirst());
      Int_t firstY = std::max(1, h2->GetYaxis()->GetFirst());
      Int_t lastX  = std::min(h2->GetXaxis()->GetLast(), h2->GetXaxis()->GetNbins());
      Int_t lastY  = std::min(h2->GetYaxis()->GetLast(), h2->GetYaxis()->GetNbins());

      unov[0] = h2->Integral(      0, firstX-1, lastY+1, cellsY  );
      unov[1] = h2->Integral(firstX , lastX   , lastY+1, cellsY  );
      unov[2] = h2->Integral(lastX+1, cellsX  , lastY+1, cellsY  );
      unov[3] = h2->Integral(      0, firstX-1, firstY , lastY   );
      unov[4] = h2->Integral(firstX , lastX   , firstY , lastY   );
      unov[5] = h2->Integral(lastX+1, cellsX  , firstY , lastY   );
      unov[6] = h2->Integral(      0, firstX-1,       0, firstY-1);
      unov[7] = h2->Integral(firstX, lastX,           0, firstY-1);
      unov[8] = h2->Integral(lastX+1, cellsX  ,       0, firstY-1);

      snprintf(t, 100," %7d|%7d|%7d\n", (Int_t)unov[0], (Int_t)unov[1], (Int_t)unov[2]);
      stats->AddText(t);
      if (h2->GetEntries() < 1e7)
         snprintf(t, 100," %7d|%7d|%7d\n", (Int_t)unov[3], (Int_t)unov[4], (Int_t)unov[5]);
      else
         snprintf(t, 100," %7d|%14.7g|%7d\n", (Int_t)unov[3], (Float_t)unov[4], (Int_t)unov[5]);
      stats->AddText(t);
      snprintf(t, 100," %7d|%7d|%7d\n", (Int_t)unov[6], (Int_t)unov[7], (Int_t)unov[8]);
      stats->AddText(t);
   }

   // Draw Fit parameters
   if (fit) {
      Int_t ndf = fit->GetNDF();
      snprintf(t,100,"#chi^{2} / ndf = %6.4g / %d",(Float_t)fit->GetChisquare(),ndf);
      stats->AddText(t);
      for (Int_t ipar=0;ipar<fit->GetNpar();ipar++) {
         snprintf(t,100,"%-8s = %5.4g #pm %5.4g ",fit->GetParName(ipar)
                                   ,(Float_t)fit->GetParameter(ipar)
                                   ,(Float_t)fit->GetParError(ipar));
         t[63] = 0;
         stats->AddText(t);
      }
   }

   if (!done) fFunctions->Add(stats);
   stats->Paint();
}


//______________________________________________________________________________
void THistPainter::PaintStat3(Int_t dostat, TF1 *fit)
{
   /* Begin_html
   <a href="#HP07">Draw the statistics box for 3D histograms.</a>
   End_html */

   if (fH->GetDimension() != 3) return;
   TH3 *h3 = (TH3*)fH;

   static char t[100];
   Int_t dofit;
   TPaveStats *stats  = 0;
   TIter next(fFunctions);
   TObject *obj;
   while ((obj = next())) {
      if (obj->InheritsFrom(TPaveStats::Class())) {
         stats = (TPaveStats*)obj;
         break;
      }
   }
   if (stats && dostat) {
      dofit  = stats->GetOptFit();
      dostat = stats->GetOptStat();
   } else {
      dofit  = gStyle->GetOptFit();
   }
   if (dostat == 1) dostat = 1111;
   Int_t print_name    = dostat%10;
   Int_t print_entries = (dostat/10)%10;
   Int_t print_mean    = (dostat/100)%10;
   Int_t print_rms     = (dostat/1000)%10;
   Int_t print_under   = (dostat/10000)%10;
   Int_t print_over    = (dostat/100000)%10;
   Int_t print_integral= (dostat/1000000)%10;
   Int_t print_skew    = (dostat/10000000)%10;
   Int_t print_kurt    = (dostat/100000000)%10;
   Int_t nlines = print_name + print_entries + 3*print_mean + 3*print_rms + print_integral;
   if (print_under || print_over) nlines += 3;

   // Pavetext with statistics
   if (!gStyle->GetOptFit()) fit = 0;
   Bool_t done = kFALSE;
   if (!dostat && !fit) {
      if (stats) { fFunctions->Remove(stats); delete stats;}
      return;
   }
   Double_t  statw  = gStyle->GetStatW();
   if (fit) statw   = 1.8*gStyle->GetStatW();
   Double_t  stath  = nlines*gStyle->GetStatFontSize();
   if (stath <= 0 || 3 == (gStyle->GetStatFont()%10)) {
      stath = 0.25*nlines*gStyle->GetStatH();
   }
   if (fit) stath += gStyle->GetStatH();
   if (stats) {
      stats->Clear();
      done = kTRUE;
   } else {
      stats  = new TPaveStats(
               gStyle->GetStatX()-statw,
               gStyle->GetStatY()-stath,
               gStyle->GetStatX(),
               gStyle->GetStatY(),"brNDC");

      stats->SetParent(fH);
      stats->SetOptFit(dofit);
      stats->SetOptStat(dostat);
      stats->SetFillColor(gStyle->GetStatColor());
      stats->SetFillStyle(gStyle->GetStatStyle());
      stats->SetBorderSize(gStyle->GetStatBorderSize());
      stats->SetName("stats");

      stats->SetTextColor(gStyle->GetStatTextColor());
      stats->SetTextAlign(12);
      stats->SetTextFont(gStyle->GetStatFont());
      stats->SetFitFormat(gStyle->GetFitFormat());
      stats->SetStatFormat(gStyle->GetStatFormat());
      stats->SetBit(kCanDelete);
      stats->SetBit(kMustCleanup);
   }
   if (print_name)  stats->AddText(h3->GetName());
   if (print_entries) {
      if (h3->GetEntries() < 1e7) snprintf(t,100,"%s = %-7d",gStringEntries.Data(),Int_t(h3->GetEntries()+0.5));
      else                        snprintf(t,100,"%s = %14.7g",gStringEntries.Data(),Float_t(h3->GetEntries()+0.5));
      stats->AddText(t);
   }
   char textstats[50];
   if (print_mean) {
      if (print_mean == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringMeanX.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetMean(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringMeanY.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetMean(2));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringMeanZ.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetMean(3));
         stats->AddText(t);
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringMeanX.Data(),"%",stats->GetStatFormat()
                                                   ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetMean(1),h3->GetMeanError(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringMeanY.Data(),"%",stats->GetStatFormat()
                                                   ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetMean(2),h3->GetMeanError(2));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringMeanZ.Data(),"%",stats->GetStatFormat()
                                                   ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetMean(3),h3->GetMeanError(3));
         stats->AddText(t);
      }
   }
   if (print_rms) {
      if (print_rms == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringRMSX.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetRMS(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringRMSY.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetRMS(2));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringRMSZ.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetRMS(3));
         stats->AddText(t);
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringRMSX.Data(),"%",stats->GetStatFormat()
                                                  ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetRMS(1),h3->GetRMSError(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringRMSY.Data(),"%",stats->GetStatFormat()
                                                  ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetRMS(2),h3->GetRMSError(2));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringRMSZ.Data(),"%",stats->GetStatFormat()
                                                  ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetRMS(3),h3->GetRMSError(3));
         stats->AddText(t);
      }
   }
   if (print_integral) {
      snprintf(t,100,"%s  = %6.4g",gStringIntegral.Data(),h3->Integral());
      stats->AddText(t);
   }
   if (print_skew) {
      if (print_skew == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringSkewnessX.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetSkewness(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringSkewnessY.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetSkewness(2));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringSkewnessZ.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetSkewness(3));
         stats->AddText(t);
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringSkewnessX.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetSkewness(1),h3->GetSkewness(11));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringSkewnessY.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetSkewness(2),h3->GetSkewness(12));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringSkewnessZ.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetSkewness(3),h3->GetSkewness(13));
         stats->AddText(t);
      }
   }
   if (print_kurt) {
      if (print_kurt == 1) {
         snprintf(textstats,50,"%s = %s%s",gStringKurtosisX.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetKurtosis(1));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringKurtosisY.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetKurtosis(2));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s",gStringKurtosisZ.Data(),"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetKurtosis(3));
         stats->AddText(t);
      } else {
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringKurtosisX.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetKurtosis(1),h3->GetKurtosis(11));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringKurtosisY.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetKurtosis(2),h3->GetKurtosis(12));
         stats->AddText(t);
         snprintf(textstats,50,"%s = %s%s #pm %s%s",gStringKurtosisZ.Data(),"%",stats->GetStatFormat()
                                                       ,"%",stats->GetStatFormat());
         snprintf(t,100,textstats,h3->GetKurtosis(3),h3->GetKurtosis(13));
         stats->AddText(t);
      }
   }
   if (print_under || print_over) {
      // no underflow - overflow printing for a 3D histogram
      // one would need a 3D table
//       //get 3*3 under/overflows for 2d hist
//       Double_t unov[9];

//       unov[0] = h3->Integral(0,h3->GetXaxis()->GetFirst()-1,h3->GetYaxis()->GetLast()+1,h3->GetYaxis()->GetNbins()+1);
//       unov[1] = h3->Integral(h3->GetXaxis()->GetFirst(),h3->GetXaxis()->GetLast(),h3->GetYaxis()->GetLast()+1,h3->GetYaxis()->GetNbins()+1);
//       unov[2] = h3->Integral(h3->GetXaxis()->GetLast()+1,h3->GetXaxis()->GetNbins()+1,h3->GetYaxis()->GetLast()+1,h3->GetYaxis()->GetNbins()+1);
//       unov[3] = h3->Integral(0,h3->GetXaxis()->GetFirst()-1,h3->GetYaxis()->GetFirst(),h3->GetYaxis()->GetLast());
//       unov[4] = h3->Integral(h3->GetXaxis()->GetFirst(),h3->GetXaxis()->GetLast(),h3->GetYaxis()->GetFirst(),h3->GetYaxis()->GetLast());
//       unov[5] = h3->Integral(h3->GetXaxis()->GetLast()+1,h3->GetXaxis()->GetNbins()+1,h3->GetYaxis()->GetFirst(),h3->GetYaxis()->GetLast());
//       unov[6] = h3->Integral(0,h3->GetXaxis()->GetFirst()-1,0,h3->GetYaxis()->GetFirst()-1);
//       unov[7] = h3->Integral(h3->GetXaxis()->GetFirst(),h3->GetXaxis()->GetLast(),0,h3->GetYaxis()->GetFirst()-1);
//       unov[8] = h3->Integral(h3->GetXaxis()->GetLast()+1,h3->GetXaxis()->GetNbins()+1,0,h3->GetYaxis()->GetFirst()-1);

//       sprintf(t, " %7d|%7d|%7d\n", (Int_t)unov[0], (Int_t)unov[1], (Int_t)unov[2]);
//       stats->AddText(t);
//       if (h3->GetEntries() < 1e7)
//          sprintf(t, " %7d|%7d|%7d\n", (Int_t)unov[3], (Int_t)unov[4], (Int_t)unov[5]);
//       else
//          sprintf(t, " %7d|%14.7g|%7d\n", (Int_t)unov[3], (Float_t)unov[4], (Int_t)unov[5]);
//       stats->AddText(t);
//       sprintf(t, " %7d|%7d|%7d\n", (Int_t)unov[6], (Int_t)unov[7], (Int_t)unov[8]);
//       stats->AddText(t);
   }

   // Draw Fit parameters
   if (fit) {
      Int_t ndf = fit->GetNDF();
      snprintf(t,100,"#chi^{2} / ndf = %6.4g / %d",(Float_t)fit->GetChisquare(),ndf);
      stats->AddText(t);
      for (Int_t ipar=0;ipar<fit->GetNpar();ipar++) {
         snprintf(t,100,"%-8s = %5.4g #pm %5.4g ",fit->GetParName(ipar)
                                   ,(Float_t)fit->GetParameter(ipar)
                                   ,(Float_t)fit->GetParError(ipar));
         t[32] = 0;
         stats->AddText(t);
      }
   }

   if (!done) fFunctions->Add(stats);
   stats->Paint();
}


//______________________________________________________________________________
void THistPainter::PaintSurface(Option_t *)
{
   /* Begin_html
   <a href="#HP18">Control function to draw a 2D histogram as a surface plot.</a>
   End_html */

   const Double_t ydiff = 1;
   const Double_t yligh1 = 10;
   const Double_t qa = 0.15;
   const Double_t qd = 0.15;
   const Double_t qs = 0.8;
   Double_t fmin, fmax;
   Int_t raster = 0;
   Int_t irep   = 0;

   if (Hparam.zmin == 0 && Hparam.zmax == 0) {Hparam.zmin = -1; Hparam.zmax = 1;}
   Int_t   nx      = Hparam.xlast - Hparam.xfirst;
   Int_t   ny      = Hparam.ylast - Hparam.yfirst;
   Double_t zmin   = Hparam.zmin;
   Double_t zmax   = Hparam.zmax;
   Double_t xlab1  = Hparam.xmin;
   Double_t xlab2  = Hparam.xmax;
   Double_t ylab1  = Hparam.ymin;
   Double_t ylab2  = Hparam.ymax;
   Double_t dangle = 10*3.141592/180; //Delta angle for Rapidity option
   Double_t deltaz = TMath::Abs(zmin);
   if (deltaz == 0) deltaz = 1;
   if (zmin >= zmax) {
      zmin -= 0.5*deltaz;
      zmax += 0.5*deltaz;
   }
   Double_t z1c = zmin;
   Double_t z2c = zmin + (zmax-zmin)*(1+gStyle->GetHistTopMargin());
   //     Compute the lego limits and instantiate a lego object
   fXbuf[0] = -1;
   fYbuf[0] =  1;
   fXbuf[1] = -1;
   fYbuf[1] =  1;
   if (Hoption.System >= kPOLAR && (Hoption.Surf == 1 || Hoption.Surf == 13)) raster = 1;
   if (Hoption.System == kPOLAR) {
      fXbuf[2] = z1c;
      fYbuf[2] = z2c;
   } else if (Hoption.System == kCYLINDRICAL) {
      if (Hoption.Logy) {
         if (ylab1 > 0) fXbuf[2] = TMath::Log10(ylab1);
         else           fXbuf[2] = 0;
         if (ylab2 > 0) fYbuf[2] = TMath::Log10(ylab2);
         else           fYbuf[2] = 0;
      } else {
         fXbuf[2] = ylab1;
         fYbuf[2] = ylab2;
      }
      z1c = 0; z2c = 1;
   } else if (Hoption.System == kSPHERICAL) {
      fXbuf[2] = -1;
      fYbuf[2] =  1;
      z1c = 0; z2c = 1;
   } else if (Hoption.System == kRAPIDITY) {
      fXbuf[2] = -1/TMath::Tan(dangle);
      fYbuf[2] =  1/TMath::Tan(dangle);
   } else {
      fXbuf[0] = xlab1;
      fYbuf[0] = xlab2;
      fXbuf[1] = ylab1;
      fYbuf[1] = ylab2;
      fXbuf[2] = z1c;
      fYbuf[2] = z2c;
   }

   fLego = new TPainter3dAlgorithms(fXbuf, fYbuf, Hoption.System);
   fLego->SetEdgeAtt(fH->GetLineColor(),fH->GetLineStyle(),fH->GetLineWidth(),0);
   fLego->SetFillColor(fH->GetFillColor());

   //          Create axis object

   TGaxis *axis = new TGaxis();

   //                  Initialize the levels on the Z axis
   Int_t ndiv   = fH->GetContour();
   if (ndiv == 0 ) {
      ndiv = gStyle->GetNumberContours();
      fH->SetContour(ndiv);
   }
   Int_t ndivz  = TMath::Abs(ndiv);
   if (fH->TestBit(TH1::kUserContour) == 0) fH->SetContour(ndiv);

   if (Hoption.Surf == 13 || Hoption.Surf == 15) fLego->SetMesh(3);
   if (Hoption.Surf == 12 || Hoption.Surf == 14 || Hoption.Surf == 17) fLego->SetMesh(0);

   //     Close the surface in case of non cartesian coordinates.

   if (Hoption.System != kCARTESIAN) {nx++; ny++;}

   //     Now ready to draw the surface plot

   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintSurface", "no TView in current pad");
      return;
   }

   Double_t thedeg =  90 - gPad->GetTheta();
   Double_t phideg = -90 - gPad->GetPhi();
   Double_t psideg = view->GetPsi();
   view->SetView(phideg, thedeg, psideg, irep);

   //     Set color/style for back box
   if (Hoption.Same) {
      fLego->SetFillStyle(0);
      fLego->SetFillColor(1);
   } else {
      fLego->SetFillStyle(gPad->GetFrameFillStyle());
      fLego->SetFillColor(gPad->GetFrameFillColor());
   }
   fLego->TAttFill::Modify();

   Int_t backcolor = gPad->GetFrameFillColor();
   if (Hoption.System != kCARTESIAN) backcolor = 0;
   view->PadRange(backcolor);

   fLego->SetFillStyle(fH->GetFillStyle());
   fLego->SetFillColor(fH->GetFillColor());
   fLego->TAttFill::Modify();

   //     Draw the filled contour on top
   Int_t icol1 = fH->GetFillColor();

   Int_t hoption35 = Hoption.Surf;
   if (Hoption.Surf == 13 || Hoption.Surf == 15) {
      DefineColorLevels(ndivz);
      Hoption.Surf = 23;
      fLego->SetSurfaceFunction(&TPainter3dAlgorithms::SurfaceFunction);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMode2);
      if (Hoption.System == kPOLAR)       fLego->SurfacePolar(1,nx,ny,"BF");
      if (Hoption.System == kCYLINDRICAL) fLego->SurfaceCylindrical(1,nx,ny,"BF");
      if (Hoption.System == kSPHERICAL)   fLego->SurfaceSpherical(0,1,nx,ny,"BF");
      if (Hoption.System == kRAPIDITY )   fLego->SurfaceSpherical(1,1,nx,ny,"BF");
      if (Hoption.System == kCARTESIAN)   fLego->SurfaceCartesian(90,nx,ny,"BF");
      Hoption.Surf = hoption35;
      fLego->SetMesh(1);
   }

   if (raster) fLego->InitRaster(-1.1,-1.1,1.1,1.1,1000,800);
   else        fLego->InitMoveScreen(-1.1,1.1);

   if (Hoption.Surf == 11 || Hoption.Surf == 12 || Hoption.Surf == 14 || Hoption.Surf == 17) {
      fLego->DefineGridLevels(fZaxis->GetNdivisions()%100);
      if (Hoption.System == kCARTESIAN && Hoption.BackBox) {
         fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove1);
         fLego->BackBox(90);
      }
   }

   //     Gouraud Shading surface
   if (Hoption.Surf == 14) {
   //    Set light sources
      fLego->LightSource(0, ydiff, 0,0,0,irep);
      fLego->LightSource(1, yligh1 ,1,1,1,irep);
      fLego->SurfaceProperty(qa, qd, qs, 1, irep);
      fmin = ydiff*qa;
      fmax = fmin + (yligh1+0.1)*(qd+qs);
      Int_t nbcol = 28;
      icol1 = 201;
      Double_t dcol = 0.5/Double_t(nbcol);
      TColor *colref = gROOT->GetColor(fH->GetFillColor());
      if (!colref) return;
      Float_t r,g,b,hue,light,satur;
      colref->GetRGB(r,g,b);
      TColor::RGBtoHLS(r,g,b,hue,light,satur);
      TColor *acol;
      for (Int_t col=0;col<nbcol;col++) {
         acol = gROOT->GetColor(col+icol1);
         TColor::HLStoRGB(hue,.4+col*dcol,satur,r,g,b);
         acol->SetRGB(r,g,b);
      }
      fLego->Spectrum(nbcol, fmin, fmax, icol1, 1, irep);
      fLego->SetSurfaceFunction(&TPainter3dAlgorithms::GouraudFunction);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMode2);
      if (Hoption.System == kPOLAR)       fLego->SurfacePolar(1,nx,ny,"BF");
      if (Hoption.System == kCYLINDRICAL) fLego->SurfaceCylindrical(1,nx,ny,"BF");
      if (Hoption.System == kSPHERICAL)   fLego->SurfaceSpherical(0,1,nx,ny,"BF");
      if (Hoption.System == kRAPIDITY )   fLego->SurfaceSpherical(1,1,nx,ny,"BF");
      if (Hoption.System == kCARTESIAN)   fLego->SurfaceCartesian(90,nx,ny,"BF");
   } else if (Hoption.Surf == 15) {
   // The surface is not drawn in this case.
   } else {
   //     Draw the surface
      if (Hoption.Surf == 11 || Hoption.Surf == 12 || Hoption.Surf == 16 || Hoption.Surf == 17) {
         DefineColorLevels(ndivz);
      } else {
         fLego->DefineGridLevels(fZaxis->GetNdivisions()%100);
      }
      fLego->SetSurfaceFunction(&TPainter3dAlgorithms::SurfaceFunction);
      if (Hoption.Surf ==  1 || Hoption.Surf == 13) fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceRaster1);
      if (Hoption.Surf == 11 || Hoption.Surf == 12 || Hoption.Surf == 17) fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMode2);
      if (Hoption.System == kPOLAR) {
         if (Hoption.Surf ==  1 || Hoption.Surf == 13) fLego->SurfacePolar(1,nx,ny,"FB");
         if (Hoption.Surf == 11 || Hoption.Surf == 12 || Hoption.Surf == 17) fLego->SurfacePolar(1,nx,ny,"BF");
      } else if (Hoption.System == kCYLINDRICAL) {
         if (Hoption.Surf ==  1 || Hoption.Surf == 13) fLego->SurfaceCylindrical(1,nx,ny,"FB");
         if (Hoption.Surf == 11 || Hoption.Surf == 12 || Hoption.Surf == 17) fLego->SurfaceCylindrical(1,nx,ny,"BF");
      } else if (Hoption.System == kSPHERICAL) {
         if (Hoption.Surf ==  1 || Hoption.Surf == 13) fLego->SurfaceSpherical(0,1,nx,ny,"FB");
         if (Hoption.Surf == 11 || Hoption.Surf == 12 || Hoption.Surf == 17) fLego->SurfaceSpherical(0,1,nx,ny,"BF");
      } else if (Hoption.System == kRAPIDITY) {
         if (Hoption.Surf ==  1 || Hoption.Surf == 13) fLego->SurfaceSpherical(1,1,nx,ny,"FB");
         if (Hoption.Surf == 11 || Hoption.Surf == 12 || Hoption.Surf == 17) fLego->SurfaceSpherical(1,1,nx,ny,"BF");
      } else {
         if (Hoption.Surf ==  1 || Hoption.Surf == 13) fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove1);
         if (Hoption.Surf == 16) fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove3);
         if (Hoption.Surf ==  1 || Hoption.Surf == 13 || Hoption.Surf == 16) fLego->SurfaceCartesian(90,nx,ny,"FB");
         if (Hoption.Surf == 11 || Hoption.Surf == 12 || Hoption.Surf == 17) fLego->SurfaceCartesian(90,nx,ny,"BF");
      }
   }

   // Paint the line contour on top for option SURF7
   if (Hoption.Surf == 17) {
      fLego->InitMoveScreen(-1.1,1.1);
      fLego->DefineGridLevels(fZaxis->GetNdivisions()%100);
      Hoption.Surf = 23;
      fLego->SetSurfaceFunction(&TPainter3dAlgorithms::SurfaceFunction);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove3);
      if (Hoption.System == kPOLAR)       fLego->SurfacePolar(1,nx,ny,"FB");
      if (Hoption.System == kCYLINDRICAL) fLego->SurfaceCylindrical(1,nx,ny,"FB");
      if (Hoption.System == kSPHERICAL)   fLego->SurfaceSpherical(0,1,nx,ny,"FB");
      if (Hoption.System == kRAPIDITY )   fLego->SurfaceSpherical(1,1,nx,ny,"FB");
      if (Hoption.System == kCARTESIAN)   fLego->SurfaceCartesian(90,nx,ny,"FB");
   }

   if ((!Hoption.Same) &&
       (Hoption.Surf == 1 || Hoption.Surf == 13 || Hoption.Surf == 16)) {
      if (Hoption.System == kCARTESIAN && Hoption.BackBox) {
         fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove1);
         fLego->BackBox(90);
      }
   }
   if (Hoption.System == kCARTESIAN) {
      fLego->InitMoveScreen(-1.1,1.1);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove2);
      if (Hoption.FrontBox) fLego->FrontBox(90);
   }
   if (!Hoption.Axis && !Hoption.Same) PaintLegoAxis(axis, 90);

   if (Hoption.Zscale) PaintPalette();

   delete axis;
   delete fLego; fLego = 0;
}


//______________________________________________________________________________
void THistPainter::PaintTriangles(Option_t *option)
{
   /* Begin_html
   Control function to draw a table using Delaunay triangles.
   End_html */

   TGraphDelaunay *dt;

   // Check if fH contains a TGraphDelaunay
   TList *hl = fH->GetListOfFunctions();
   dt = (TGraphDelaunay*)hl->FindObject("TGraphDelaunay");
   if (!dt) return;

   // If needed, create a TGraph2DPainter
   if (!fGraph2DPainter) fGraph2DPainter = new TGraph2DPainter(dt);

   // Define the 3D view
   if (Hparam.zmin == 0 && Hparam.zmax == 0) {Hparam.zmin = -1; Hparam.zmax = 1;}
   if (Hoption.Same) {
      TView *viewsame = gPad->GetView();
      if (!viewsame) {
         Error("PaintTriangles", "no TView in current pad, do not use option SAME");
         return;
      }
      Double_t *rmin = viewsame->GetRmin();
      Double_t *rmax = viewsame->GetRmax();
      if (!rmin || !rmax) return;
      fXbuf[0] = rmin[0];
      fYbuf[0] = rmax[0];
      fXbuf[1] = rmin[1];
      fYbuf[1] = rmax[1];
      fXbuf[2] = rmin[2];
      fYbuf[2] = rmax[2];
   } else {
      fXbuf[0] = Hparam.xmin;
      fYbuf[0] = Hparam.xmax;
      fXbuf[1] = Hparam.ymin;
      fYbuf[1] = Hparam.ymax;
      fXbuf[2] = Hparam.zmin;
      fYbuf[2] = Hparam.zmax;
   }

   fLego = new TPainter3dAlgorithms(fXbuf, fYbuf);
   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintTriangles", "no TView in current pad");
      return;
   }
   Double_t thedeg =  90 - gPad->GetTheta();
   Double_t phideg = -90 - gPad->GetPhi();
   Double_t psideg = view->GetPsi();
   Int_t irep;
   view->SetView(phideg, thedeg, psideg, irep);

   // Set color/style for back box
   fLego->SetFillStyle(gPad->GetFrameFillStyle());
   fLego->SetFillColor(gPad->GetFrameFillColor());
   fLego->TAttFill::Modify();
   Int_t backcolor = gPad->GetFrameFillColor();
   if (Hoption.System != kCARTESIAN) backcolor = 0;
   view->PadRange(backcolor);
   fLego->SetFillStyle(fH->GetFillStyle());
   fLego->SetFillColor(fH->GetFillColor());
   fLego->TAttFill::Modify();

   // Paint the Back Box if needed
   if (Hoption.BackBox && !Hoption.Same) {
      fLego->InitMoveScreen(-1.1,1.1);
      fLego->DefineGridLevels(fZaxis->GetNdivisions()%100);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove1);
      fLego->BackBox(90);
   }

   // Paint the triangles
   fGraph2DPainter->Paint(option);

   // Paint the Front Box if needed
   if (Hoption.FrontBox) {
      fLego->InitMoveScreen(-1.1,1.1);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove2);
      fLego->FrontBox(90);
   }

   // Paint the Axis if needed
   if (!Hoption.Axis && !Hoption.Same) {
      TGaxis *axis = new TGaxis();
      PaintLegoAxis(axis, 90);
      delete axis;
   }

   if (Hoption.Zscale) PaintPalette();

   delete fLego; fLego = 0;
}


//______________________________________________________________________________
void THistPainter::DefineColorLevels(Int_t ndivz)
{
   /* Begin_html
   Define the color levels used to paint legos, surfaces etc..
   End_html */

   Int_t i, irep;

   // Initialize the color levels
   if (ndivz >= 100) {
      Warning("PaintSurface", "too many color levels, %d, reset to 8", ndivz);
      ndivz = 8;
   }
   Double_t *funlevel = new Double_t[ndivz+1];
   Int_t *colorlevel = new Int_t[ndivz+1];
   Int_t theColor;
   Int_t ncolors = gStyle->GetNumberOfColors();
   for (i = 0; i < ndivz; ++i) {
      funlevel[i] = fH->GetContourLevelPad(i);
      theColor = Int_t((i+0.99)*Float_t(ncolors)/Float_t(ndivz));
      colorlevel[i] = gStyle->GetColorPalette(theColor);
   }
   colorlevel[ndivz] = gStyle->GetColorPalette(ncolors-1);
   fLego->ColorFunction(ndivz, funlevel, colorlevel, irep);
   delete [] colorlevel;
   delete [] funlevel;
}


//______________________________________________________________________________
void THistPainter::PaintTable(Option_t *option)
{
   /* Begin_html
   <a href="#HP01c">Control function to draw 2D/3D histograms (tables).</a>
   End_html */

   // Fill Hparam structure with histo parameters
   if (!TableInit()) return;

   // Draw histogram frame
   PaintFrame();

   // If palette option not specified, delete a possible existing palette
   if (!Hoption.Zscale) {
      TObject *palette = fFunctions->FindObject("palette");
      if (palette) { fFunctions->Remove(palette); delete palette;}
   }

   // Do not draw the histogram. Only the attached functions will be drawn.
   if (Hoption.Func == 2) {
      if (Hoption.Zscale) {
         Int_t ndiv   = fH->GetContour();
         if (ndiv == 0 ) {
            ndiv = gStyle->GetNumberContours();
            fH->SetContour(ndiv);
         }
         PaintPalette();
      }

   // Draw the histogram according to the option
   } else {
      if (fH->InheritsFrom(TH2Poly::Class())) {
         if (Hoption.Fill)         PaintTH2PolyBins("f");
         if (Hoption.Color)        PaintTH2PolyColorLevels(option);
         if (Hoption.Scat)         PaintTH2PolyScatterPlot(option);
         if (Hoption.Text)         PaintTH2PolyText(option);
         if (Hoption.Line)         PaintTH2PolyBins("l");
         if (Hoption.Mark)         PaintTH2PolyBins("P");
      } else if (fH->GetEntries() != 0 && Hoption.Axis<=0) {
         if (Hoption.Scat)         PaintScatterPlot(option);
         if (Hoption.Arrow)        PaintArrows(option);
         if (Hoption.Box)          PaintBoxes(option);
         if (Hoption.Color)        PaintColorLevels(option);
         if (Hoption.Contour)      PaintContour(option);
         if (Hoption.Text)         PaintText(option);
         if (Hoption.Error >= 100) Paint2DErrors(option);
         if (Hoption.Candle)       PaintCandlePlot(option);
         if (Hoption.Violin)       PaintViolinPlot(option);
      }
      if (Hoption.Lego)                     PaintLego(option);
      if (Hoption.Surf && !Hoption.Contour) PaintSurface(option);
      if (Hoption.Tri)                      PaintTriangles(option);
   }

   // Draw histogram title
   PaintTitle();

   // Draw the axes
   if (!Hoption.Lego && !Hoption.Surf &&
       !Hoption.Tri  && !(Hoption.Error >= 100)) PaintAxis(kFALSE);

   TF1 *fit  = 0;
   TIter next(fFunctions);
   TObject *obj;
   while ((obj = next())) {
      if (obj->InheritsFrom(TF1::Class())) {
         fit = (TF1*)obj;
         break;
      }
   }
   if (Hoption.Same != 1) {
      if (!fH->TestBit(TH1::kNoStats)) {  // bit set via TH1::SetStats
         if (!gPad->PadInSelectionMode() && !gPad->PadInHighlightMode()) {
            //ALWAYS executed on non-iOS platform.
            //On iOS, depends on mode.
            PaintStat2(gStyle->GetOptStat(),fit);
         }
      }
   }
}


//______________________________________________________________________________
void THistPainter::PaintTH2PolyBins(Option_t *option)
{
   /* Begin_html
    Control function to draw a TH2Poly bins' contours.
    option = "F" draw the bins as filled areas.
    option = "L" draw the bins as line.
    option = "P" draw the bins as markers.
    End_html */

   //Do not highlight the histogram, if its part was picked.
   if (gPad->PadInHighlightMode() && gPad->GetSelected() != fH) return;

   TString opt = option;
   opt.ToLower();
   Bool_t line = kFALSE;
   Bool_t fill = kFALSE;
   Bool_t mark = kFALSE;
   if (opt.Contains("l")) line = kTRUE;
   if (opt.Contains("f")) fill = kTRUE;
   if (opt.Contains("p")) mark = kTRUE;

   TH2PolyBin  *b;

   TIter next(((TH2Poly*)fH)->GetBins());
   TObject *obj, *poly;

   while ((obj=next())) {
      b     = (TH2PolyBin*)obj;
      poly  = b->GetPolygon();

      // Paint the TGraph bins.
      if (poly->IsA() == TGraph::Class()) {
         TGraph *g  = (TGraph*)poly;
         g->TAttLine::Modify();
         g->TAttMarker::Modify();
         g->TAttFill::Modify();
         if (line) g->Paint("L");
         if (fill) g->Paint("F");
         if (mark) g->Paint("P");
      }

      // Paint the TMultiGraph bins.
      if (poly->IsA() == TMultiGraph::Class()) {
         TMultiGraph *mg = (TMultiGraph*)poly;
         TList *gl = mg->GetListOfGraphs();
         if (!gl) return;
         TGraph *g;
         TIter nextg(gl);
         while ((g = (TGraph*) nextg())) {
            g->TAttLine::Modify();
            g->TAttMarker::Modify();
            g->TAttFill::Modify();
            if (line) g->Paint("L");
            if (fill) g->Paint("F");
            if (mark) g->Paint("P");
         }
      }
   }
}


//______________________________________________________________________________
void THistPainter::PaintTH2PolyColorLevels(Option_t *)
{
   /* Begin_html
    <a href="#HP20a">Control function to draw a TH2Poly as a color plot.</a>
    End_html */

   //Do not highlight the histogram, if its part was picked.
   if (gPad->PadInHighlightMode() && gPad->GetSelected() != fH)
      return;

   Int_t ncolors, color, theColor;
   Double_t z, zc;
   Double_t zmin = fH->GetMinimum();
   Double_t zmax = fH->GetMaximum();
   if (Hoption.Logz) {
      if (zmax > 0) {
         if (zmin <= 0) zmin = TMath::Min((Double_t)1, (Double_t)0.001*zmax);
         zmin = TMath::Log10(zmin);
         zmax = TMath::Log10(zmax);
      } else {
         return;
      }
   }
   Double_t dz = zmax - zmin;

   // Initialize the levels on the Z axis
   ncolors     = gStyle->GetNumberOfColors();
   Int_t ndiv  = fH->GetContour();
   if (ndiv == 0 ) {
      ndiv = gStyle->GetNumberContours();
      fH->SetContour(ndiv);
   }
   Int_t ndivz  = TMath::Abs(ndiv);
   if (fH->TestBit(TH1::kUserContour) == 0) fH->SetContour(ndiv);
   Double_t scale = ndivz/dz;

   TH2PolyBin  *b;

   TIter next(((TH2Poly*)fH)->GetBins());
   TObject *obj, *poly;

   while ((obj=next())) {
      b     = (TH2PolyBin*)obj;
      poly  = b->GetPolygon();

      z = b->GetContent();
      if (Hoption.Logz) {
         if (z > 0) z = TMath::Log10(z);
         else       z = zmin;
      }
      if (z < zmin) continue;

      // Define the bin color.
      if (fH->TestBit(TH1::kUserContour)) {
         zc = fH->GetContourLevelPad(0);
         if (z < zc) continue;
         color = -1;
         for (Int_t k=0; k<ndiv; k++) {
            zc = fH->GetContourLevelPad(k);
            if (z < zc) {
               continue;
            } else {
               color++;
            }
         }
      } else {
         color = Int_t(0.01+(z-zmin)*scale);
      }
      theColor = Int_t((color+0.99)*Float_t(ncolors)/Float_t(ndivz));
      if (theColor > ncolors-1) theColor = ncolors-1;

      // Paint the TGraph bins.
      if (poly->IsA() == TGraph::Class()) {
         TGraph *g  = (TGraph*)poly;
         g->SetFillColor(gStyle->GetColorPalette(theColor));
         g->TAttFill::Modify();
         g->Paint("F");
      }

      // Paint the TMultiGraph bins.
      if (poly->IsA() == TMultiGraph::Class()) {
         TMultiGraph *mg = (TMultiGraph*)poly;
         TList *gl = mg->GetListOfGraphs();
         if (!gl) return;
         TGraph *g;
         TIter nextg(gl);
         while ((g = (TGraph*) nextg())) {
            g->SetFillColor(gStyle->GetColorPalette(theColor));
            g->TAttFill::Modify();
            g->Paint("F");
         }
      }
   }
   if (Hoption.Zscale) PaintPalette();
}


//______________________________________________________________________________
void THistPainter::PaintTH2PolyScatterPlot(Option_t *)
{
   /* Begin_html
    <a href="#HP20a">Control function to draw a TH2Poly as a scatter plot.</a>
    End_html */

   //Do not highlight the histogram, if its part was selected.
   if (gPad->PadInHighlightMode() && gPad->GetSelected() != fH)
      return;

   Int_t k, loop, marker=0;
   Double_t z, xk,xstep, yk, ystep, xp, yp;
   Double_t scale = 1;
   Double_t zmin = fH->GetMinimum();
   Double_t zmax = fH->GetMaximum();
   if (Hoption.Logz) {
      if (zmax > 0) {
         if (zmin <= 0) zmin = TMath::Min((Double_t)1, (Double_t)0.001*zmax);
         zmin = TMath::Log10(zmin);
         zmax = TMath::Log10(zmax);
      } else {
         return;
      }
   }
   Double_t dz = zmax - zmin;
   scale = (kNMAX-1)/dz;


   // use an independent instance of a random generator
   // instead of gRandom to avoid conflicts and
   // to get same random numbers when drawing the same histogram
   TRandom2 random;

   TH2PolyBin  *b;

   TIter next(((TH2Poly*)fH)->GetBins());
   TObject *obj, *poly;

   Double_t maxarea = 0, a;
   while ((obj=next())) {
      b     = (TH2PolyBin*)obj;
      a     = b->GetArea();
      if (a>maxarea) maxarea = a;
   }

   next.Reset();

   while ((obj=next())) {
      b     = (TH2PolyBin*)obj;
      poly  = b->GetPolygon();
      z     = b->GetContent();
      if (z < zmin) z = zmin;
      if (z > zmax) z = zmax;
      if (Hoption.Logz) {
         if (z > 0) z = TMath::Log10(z) - zmin;
      } else {
         z    -=  zmin;
      }
      k     = Int_t((z*scale)*(b->GetArea()/maxarea));
      xk    = b->GetXMin();
      yk    = b->GetYMin();
      xstep = b->GetXMax()-xk;
      ystep = b->GetYMax()-yk;

      // Paint the TGraph bins.
      if (poly->IsA() == TGraph::Class()) {
         TGraph *g  = (TGraph*)poly;
         if (k <= 0 || z <= 0) continue;
         loop = 0;
         while (loop<k) {
            if (k+marker >= kNMAX) {
               gPad->PaintPolyMarker(marker, fXbuf, fYbuf);
               marker=0;
            }
            xp = (random.Rndm(loop)*xstep) + xk;
            yp = (random.Rndm(loop)*ystep) + yk;
            if (g->IsInside(xp,yp)) {
               fXbuf[marker] = xp;
               fYbuf[marker] = yp;
               marker++;
               loop++;
            }
         }
         if (marker > 0) gPad->PaintPolyMarker(marker, fXbuf, fYbuf);
      }

      // Paint the TMultiGraph bins.
      if (poly->IsA() == TMultiGraph::Class()) {
         TMultiGraph *mg = (TMultiGraph*)poly;
         TList *gl = mg->GetListOfGraphs();
         if (!gl) return;
         if (k <= 0 || z <= 0) continue;
         loop = 0;
         while (loop<k) {
            if (k+marker >= kNMAX) {
               gPad->PaintPolyMarker(marker, fXbuf, fYbuf);
               marker=0;
            }
            xp = (random.Rndm(loop)*xstep) + xk;
            yp = (random.Rndm(loop)*ystep) + yk;
            if (mg->IsInside(xp,yp)) {
               fXbuf[marker] = xp;
               fYbuf[marker] = yp;
               marker++;
               loop++;
            }
         }
         if (marker > 0) gPad->PaintPolyMarker(marker, fXbuf, fYbuf);
      }
   }
   PaintTH2PolyBins("l");
}


//______________________________________________________________________________
void THistPainter::PaintTH2PolyText(Option_t *)
{
   /* Begin_html
    <a href="#HP20a">Control function to draw a TH2Poly as a text plot.</a>
    End_html */

   TLatex text;
   text.SetTextFont(gStyle->GetTextFont());
   text.SetTextColor(fH->GetMarkerColor());
   text.SetTextSize(0.02*fH->GetMarkerSize());

   Double_t x, y, z, e, angle = 0;
   char value[50];
   char format[32];
   snprintf(format,32,"%s%s","%",gStyle->GetPaintTextFormat());
   if (Hoption.Text >= 1000) angle = Hoption.Text%1000;
   Int_t opt = (Int_t)Hoption.Text/1000;

   text.SetTextAlign(22);
   if (Hoption.Text ==  1) angle = 0;
   text.SetTextAngle(angle);
   text.TAttText::Modify();

   TH2PolyBin *b;

   TIter next(((TH2Poly*)fH)->GetBins());
   TObject *obj, *p;

   while ((obj=next())) {
      b = (TH2PolyBin*)obj;
      p = b->GetPolygon();
      x = (b->GetXMin()+b->GetXMax())/2;
      if (Hoption.Logx) {
         if (x > 0)  x  = TMath::Log10(x);
         else continue;
      }
      y = (b->GetYMin()+b->GetYMax())/2;
      if (Hoption.Logy) {
         if (y > 0)  y  = TMath::Log10(y);
         else continue;
      }
      z = b->GetContent();
      if (z < Hparam.zmin || (z == 0 && !gStyle->GetHistMinimumZero()) ) continue;
      if (opt==2) {
         e = fH->GetBinError(b->GetBinNumber());
         snprintf(format,32,"#splitline{%s%s}{#pm %s%s}",
                                    "%",gStyle->GetPaintTextFormat(),
                                    "%",gStyle->GetPaintTextFormat());
         snprintf(value,50,format,z,e);
      } else {
         snprintf(value,50,format,z);
      }
      if (opt==3) text.PaintLatex(x,y,angle,0.02*fH->GetMarkerSize(),p->GetName());
      else        text.PaintLatex(x,y,angle,0.02*fH->GetMarkerSize(),value);
   }

   PaintTH2PolyBins("l");
}


//______________________________________________________________________________
void THistPainter::PaintText(Option_t *)
{
   /* Begin_html
   <a href="#HP15">Control function to draw a 1D/2D histograms with the bin values.</a>
   End_html */

   TLatex text;
   text.SetTextFont(gStyle->GetTextFont());
   text.SetTextColor(fH->GetMarkerColor());
   text.SetTextSize(0.02*fH->GetMarkerSize());

   Double_t x, y, z, e, angle = 0;
   char value[50];
   char format[32];
   snprintf(format,32,"%s%s","%",gStyle->GetPaintTextFormat());
   if (Hoption.Text >= 1000) angle = Hoption.Text%1000;

   // 1D histograms
   if (fH->GetDimension() == 1) {
      Bool_t getentries = kFALSE;
      Double_t yt;
      TProfile *hp = (TProfile*)fH;
      if (Hoption.Text>2000 && fH->InheritsFrom(TProfile::Class())) {
         Hoption.Text = Hoption.Text-2000;
         getentries = kTRUE;
      }
      if (Hoption.Text ==  1) angle = 90;
      text.SetTextAlign(11);
      if (angle == 90) text.SetTextAlign(12);
      if (angle ==  0) text.SetTextAlign(21);
      text.TAttText::Modify();
      Double_t dt = 0.02*(gPad->GetY2()-gPad->GetY1());
      for (Int_t i=Hparam.xfirst; i<=Hparam.xlast;i++) {
         if (Hoption.Bar) {
            x  = fH->GetXaxis()->GetBinLowEdge(i)+
                 fH->GetXaxis()->GetBinWidth(i)*
                 (fH->GetBarOffset()+0.5*fH->GetBarWidth());
         } else {
            x  = fH->GetXaxis()->GetBinCenter(i);
         }
         y  = fH->GetBinContent(i);
         yt = y;
         if (gStyle->GetHistMinimumZero() && y<0) y = 0;
         if (getentries) yt = hp->GetBinEntries(i);
         if (yt == 0.) continue;
         snprintf(value,50,format,yt);
         if (Hoption.Logx) {
            if (x > 0)  x  = TMath::Log10(x);
            else continue;
         }
         if (Hoption.Logy) {
            if (y > 0)  y  = TMath::Log10(y);
            else continue;
         }
         if (y >= gPad->GetY2()) continue;
         if (y <= gPad->GetY1()) continue;
         text.PaintLatex(x,y+0.2*dt,angle,0.02*fH->GetMarkerSize(),value);
      }

   // 2D histograms
   } else {
      text.SetTextAlign(22);
      if (Hoption.Text ==  1) angle = 0;
      text.SetTextAngle(angle);
      text.TAttText::Modify();
      for (Int_t j=Hparam.yfirst; j<=Hparam.ylast;j++) {
         y    = fYaxis->GetBinCenter(j);
         if (Hoption.Logy) {
            if (y > 0)  y  = TMath::Log10(y);
            else continue;
         }
         for (Int_t i=Hparam.xfirst; i<=Hparam.xlast;i++) {
            Int_t bin  = j*(fXaxis->GetNbins()+2) + i;
            x    = fXaxis->GetBinCenter(i);
            if (Hoption.Logx) {
               if (x > 0)  x  = TMath::Log10(x);
               else continue;
            }
            if (!IsInside(x,y)) continue;
            z = fH->GetBinContent(bin);
            if (z < Hparam.zmin || (z == 0 && !gStyle->GetHistMinimumZero()) ) continue;
            if (Hoption.Text>2000) {
               e = fH->GetBinError(bin);
               snprintf(format,32,"#splitline{%s%s}{#pm %s%s}",
                                          "%",gStyle->GetPaintTextFormat(),
                                          "%",gStyle->GetPaintTextFormat());
               snprintf(value,50,format,z,e);
            } else {
               snprintf(value,50,format,z);
            }
            text.PaintLatex(x,y,angle,0.02*fH->GetMarkerSize(),value);
         }
      }
   }
}


//______________________________________________________________________________
void THistPainter::PaintTF3()
{
   /* Begin_html
   <a href="#HP27">Control function to draw a 3D implicit functions.</a>
   End_html */

   Int_t irep;

   TGaxis *axis = new TGaxis();
   TAxis *xaxis = fH->GetXaxis();
   TAxis *yaxis = fH->GetYaxis();
   TAxis *zaxis = fH->GetZaxis();

   fXbuf[0] = xaxis->GetBinLowEdge(xaxis->GetFirst());
   fYbuf[0] = xaxis->GetBinUpEdge(xaxis->GetLast());
   fXbuf[1] = yaxis->GetBinLowEdge(yaxis->GetFirst());
   fYbuf[1] = yaxis->GetBinUpEdge(yaxis->GetLast());
   fXbuf[2] = zaxis->GetBinLowEdge(zaxis->GetFirst());
   fYbuf[2] = zaxis->GetBinUpEdge(zaxis->GetLast());

   fLego = new TPainter3dAlgorithms(fXbuf, fYbuf);

   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintTF3", "no TView in current pad");
      return;
   }
   Double_t thedeg =  90 - gPad->GetTheta();
   Double_t phideg = -90 - gPad->GetPhi();
   Double_t psideg = view->GetPsi();
   view->SetView(phideg, thedeg, psideg, irep);

   fLego->InitMoveScreen(-1.1,1.1);

   if (Hoption.BackBox) {
      fLego->DefineGridLevels(fZaxis->GetNdivisions()%100);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove1);
      fLego->BackBox(90);
   }

   fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMode1);

   fLego->ImplicitFunction(fXbuf, fYbuf, fH->GetNbinsX(),
                                         fH->GetNbinsY(),
                                         fH->GetNbinsZ(), "BF");

   if (Hoption.FrontBox) {
      fLego->InitMoveScreen(-1.1,1.1);
      fLego->SetDrawFace(&TPainter3dAlgorithms::DrawFaceMove2);
      fLego->FrontBox(90);
   }
   if (!Hoption.Axis && !Hoption.Same) PaintLegoAxis(axis, 90);

   PaintTitle();

   delete axis;
   delete fLego; fLego = 0;
}


//______________________________________________________________________________
void THistPainter::PaintTitle()
{
   /* Begin_html
   Draw the histogram title
   <p>
   The title is drawn according to the title alignment returned by
   <tt>GetTitleAlign()</tt>. It is a 2 digits integer): hv
   <p>
   where <tt>"h"</tt> is the horizontal alignment and <tt>"v"</tt> is the
   vertical alignment.
   <ul>
   <li> <tt>"h"</tt> can get the values 1 2 3 for left, center, and right
   <li> <tt>"v"</tt> can get the values 1 2 3 for bottom, middle and top
   </ul>
   for instance the default alignment is: 13 (left top)
   End_html */

   // probably best place for calling PaintHighlightBin
   // calling immediately after paint histo and before paint title, functions(stats), etc.
   if (!gPad->GetView()) PaintHighlightBin();

   if (Hoption.Same) return;
   if (fH->TestBit(TH1::kNoTitle)) return;
   Int_t nt = strlen(fH->GetTitle());
   TPaveText *title = 0;
   TObject *obj;
   TIter next(gPad->GetListOfPrimitives());
   while ((obj = next())) {
      if (!obj->InheritsFrom(TPaveText::Class())) continue;
      title = (TPaveText*)obj;
      if (strcmp(title->GetName(),"title")) {title = 0; continue;}
      break;
   }
   if (nt == 0 || gStyle->GetOptTitle() <= 0) {
      if (title) delete title;
      return;
   }
   Double_t ht = gStyle->GetTitleH();
   Double_t wt = gStyle->GetTitleW();
   if (ht <= 0) ht = 1.1*gStyle->GetTitleFontSize();
   if (ht <= 0) ht = 0.05;
   if (wt <= 0) {
      TLatex l;
      l.SetTextSize(ht);
      l.SetTitle(fH->GetTitle());
      // adjustment in case the title has several lines (#splitline)
      ht = TMath::Max(ht, 1.2*l.GetYsize()/(gPad->GetY2() - gPad->GetY1()));
      Double_t wndc = l.GetXsize()/(gPad->GetX2() - gPad->GetX1());
      wt = TMath::Min(0.7, 0.02+wndc);
   }
   if (title) {
      TText *t0 = (TText*)title->GetLine(0);
      if (t0) {
         if (!strcmp(t0->GetTitle(),fH->GetTitle())) return;
         t0->SetTitle(fH->GetTitle());
         if (wt > 0) title->SetX2NDC(title->GetX1NDC()+wt);
      }
      return;
   }

   Int_t talh = gStyle->GetTitleAlign()/10;
   if (talh < 1) talh = 1; if (talh > 3) talh = 3;
   Int_t talv = gStyle->GetTitleAlign()%10;
   if (talv < 1) talv = 1; if (talv > 3) talv = 3;
   Double_t xpos, ypos;
   xpos = gStyle->GetTitleX();
   ypos = gStyle->GetTitleY();
   if (talh == 2) xpos = xpos-wt/2.;
   if (talh == 3) xpos = xpos-wt;
   if (talv == 2) ypos = ypos+ht/2.;
   if (talv == 1) ypos = ypos+ht;

   TPaveText *ptitle = new TPaveText(xpos, ypos-ht, xpos+wt, ypos,"blNDC");

   //     box with the histogram title
   ptitle->SetFillColor(gStyle->GetTitleFillColor());
   ptitle->SetFillStyle(gStyle->GetTitleStyle());
   ptitle->SetName("title");
   ptitle->SetBorderSize(gStyle->GetTitleBorderSize());
   ptitle->SetTextColor(gStyle->GetTitleTextColor());
   ptitle->SetTextFont(gStyle->GetTitleFont(""));
   if (gStyle->GetTitleFont("")%10 > 2)
      ptitle->SetTextSize(gStyle->GetTitleFontSize());
   ptitle->AddText(fH->GetTitle());
   ptitle->SetBit(kCanDelete);
   ptitle->Draw();
   ptitle->Paint();

   if(!gPad->IsEditable()) delete ptitle;
}


//______________________________________________________________________________
void THistPainter::ProcessMessage(const char *mess, const TObject *obj)
{
   /* Begin_html
    Process message "mess".
   End_html */

   if (!strcmp(mess,"SetF3")) {
      TPainter3dAlgorithms::SetF3((TF3*)obj);
   } else if (!strcmp(mess,"SetF3ClippingBoxOff")) {
      TPainter3dAlgorithms::SetF3ClippingBoxOff();
   } else if (!strcmp(mess,"SetF3ClippingBoxOn")) {
      TVectorD &v =  (TVectorD&)(*obj);
      Double_t xclip = v(0);
      Double_t yclip = v(1);
      Double_t zclip = v(2);
      TPainter3dAlgorithms::SetF3ClippingBoxOn(xclip,yclip,zclip);
   }
}


//______________________________________________________________________________
Int_t THistPainter::ProjectAitoff2xy(Double_t l, Double_t b, Double_t &Al, Double_t &Ab)
{
   /* Begin_html
   Static function.<br>
   Convert Right Ascension, Declination to X,Y using an AITOFF projection.
   This procedure can be used to create an all-sky map in Galactic
   coordinates with an equal-area Aitoff projection.  Output map
   coordinates are zero longitude centered.
   Also called Hammer-Aitoff projection (first presented by Ernst von Hammer in 1892)
   <p>
   source: GMT<br>
   code from  Ernst-Jan Buis
   End_html */

   Double_t x, y;

   Double_t alpha2 = (l/2)*TMath::DegToRad();
   Double_t delta  = b*TMath::DegToRad();
   Double_t r2     = TMath::Sqrt(2.);
   Double_t f      = 2*r2/TMath::Pi();
   Double_t cdec   = TMath::Cos(delta);
   Double_t denom  = TMath::Sqrt(1. + cdec*TMath::Cos(alpha2));
   x      = cdec*TMath::Sin(alpha2)*2.*r2/denom;
   y      = TMath::Sin(delta)*r2/denom;
   x     *= TMath::RadToDeg()/f;
   y     *= TMath::RadToDeg()/f;
   //  x *= -1.; // for a skymap swap left<->right
   Al = x;
   Ab = y;

   return 0;
}


//______________________________________________________________________________
Int_t THistPainter::ProjectMercator2xy(Double_t l, Double_t b, Double_t &Al, Double_t &Ab)
{
   /* Begin_html
   Static function <br>
   Probably the most famous of the various map projections, the Mercator projection
   takes its name from Mercator who presented it in 1569. It is a cylindrical, conformal projection
   with no distortion along the equator.
   The Mercator projection has been used extensively for world maps in which the distortion towards
   the polar regions grows rather large, thus incorrectly giving the impression that, for example,
   Greenland is larger than South America. In reality, the latter is about eight times the size of
   Greenland. Also, the Former Soviet Union looks much bigger than Africa or South America. One may wonder
   whether this illusion has had any influence on U.S. foreign policy.' (Source: GMT)
   code from  Ernst-Jan Buis
   End_html */

   Al = l;
   Double_t aid = TMath::Tan((TMath::PiOver2() + b*TMath::DegToRad())/2);
   Ab = TMath::Log(aid);
   return 0;
}


//______________________________________________________________________________
Int_t THistPainter::ProjectSinusoidal2xy(Double_t l, Double_t b, Double_t &Al, Double_t &Ab)
{
   /* Begin_html
   Static function code from  Ernst-Jan Buis
   End_html */

   Al = l*cos(b*TMath::DegToRad());
   Ab = b;
   return 0;
}


//______________________________________________________________________________
Int_t THistPainter::ProjectParabolic2xy(Double_t l, Double_t b, Double_t &Al, Double_t &Ab)
{
   /* Begin_html
   Static function code from  Ernst-Jan Buis
   End_html */

   Al = l*(2.*TMath::Cos(2*b*TMath::DegToRad()/3) - 1);
   Ab = 180*TMath::Sin(b*TMath::DegToRad()/3);
   return 0;
}


//______________________________________________________________________________
void THistPainter::RecalculateRange()
{
   /* Begin_html
   Recompute the histogram range following graphics operations.
   End_html */

   if (Hoption.Same) return;

   //     Compute x,y range
   Double_t xmin = Hparam.xmin;
   Double_t xmax = Hparam.xmax;
   Double_t ymin = Hparam.ymin;
   Double_t ymax = Hparam.ymax;

   Double_t xmin_aid, ymin_aid, xmax_aid, ymax_aid;
   if (Hoption.Proj ==1) {
      // TODO : check x range not lower than -180 and not higher than 180
      THistPainter::ProjectAitoff2xy(Hparam.xmin, Hparam.ymin, xmin_aid, ymin_aid);
      THistPainter::ProjectAitoff2xy(Hparam.xmin, Hparam.ymax, xmin,     ymax_aid);
      THistPainter::ProjectAitoff2xy(Hparam.xmax, Hparam.ymax, xmax_aid, ymax);
      THistPainter::ProjectAitoff2xy(Hparam.xmax, Hparam.ymin, xmax,     ymin);

      if (xmin > xmin_aid) xmin = xmin_aid;
      if (ymin > ymin_aid) ymin = ymin_aid;
      if (xmax < xmax_aid) xmax = xmax_aid;
      if (ymax < ymax_aid) ymax = ymax_aid;
      if (Hparam.ymin<0 && Hparam.ymax>0) {
         // there is an  'equator', check its range in the plot..
         THistPainter::ProjectAitoff2xy(Hparam.xmin*0.9999, 0, xmin_aid, ymin_aid);
         THistPainter::ProjectAitoff2xy(Hparam.xmax*0.9999, 0, xmax_aid, ymin_aid);
         if (xmin >xmin_aid) xmin = xmin_aid;
         if (xmax <xmax_aid) xmax = xmax_aid;
      }
      if (Hparam.xmin<0 && Hparam.xmax>0) {
         THistPainter::ProjectAitoff2xy(0, Hparam.ymin, xmin_aid, ymin_aid);
         THistPainter::ProjectAitoff2xy(0, Hparam.ymax, xmax_aid, ymax_aid);
         if (ymin >ymin_aid) ymin = ymin_aid;
         if (ymax <ymax_aid) ymax = ymax_aid;
      }
   } else if ( Hoption.Proj ==2) {
      if (Hparam.ymin <= -90 || Hparam.ymax >=90) {
         Warning("Mercator Projection", "Latitude out of range %f or %f", Hparam.ymin, Hparam.ymax);
         Hoption.Proj = 0;
      } else {
         THistPainter::ProjectMercator2xy(Hparam.xmin, Hparam.ymin, xmin, ymin);
         THistPainter::ProjectMercator2xy(Hparam.xmax, Hparam.ymax, xmax, ymax);
      }
   } else if (Hoption.Proj == 3) {
      THistPainter::ProjectSinusoidal2xy(Hparam.xmin, Hparam.ymin, xmin_aid, ymin_aid);
      THistPainter::ProjectSinusoidal2xy(Hparam.xmin, Hparam.ymax, xmin,     ymax_aid);
      THistPainter::ProjectSinusoidal2xy(Hparam.xmax, Hparam.ymax, xmax_aid, ymax);
      THistPainter::ProjectSinusoidal2xy(Hparam.xmax, Hparam.ymin, xmax,     ymin);

      if (xmin > xmin_aid) xmin = xmin_aid;
      if (ymin > ymin_aid) ymin = ymin_aid;
      if (xmax < xmax_aid) xmax = xmax_aid;
      if (ymax < ymax_aid) ymax = ymax_aid;
      if (Hparam.ymin<0 && Hparam.ymax>0) {
         THistPainter::ProjectSinusoidal2xy(Hparam.xmin, 0, xmin_aid, ymin_aid);
         THistPainter::ProjectSinusoidal2xy(Hparam.xmax, 0, xmax_aid, ymin_aid);
         if (xmin >xmin_aid) xmin = xmin_aid;
         if (xmax <xmax_aid) xmax = xmax_aid;
      }
      if (Hparam.xmin<0 && Hparam.xmax>0) {
         THistPainter::ProjectSinusoidal2xy(0,Hparam.ymin, xmin_aid, ymin_aid);
         THistPainter::ProjectSinusoidal2xy(0, Hparam.ymax, xmax_aid, ymin_aid);
         if (ymin >ymin_aid) ymin = ymin_aid;
         if (ymax <ymax_aid) ymax = ymax_aid;
      }
   } else if (Hoption.Proj == 4) {
      THistPainter::ProjectParabolic2xy(Hparam.xmin, Hparam.ymin, xmin_aid, ymin_aid);
      THistPainter::ProjectParabolic2xy(Hparam.xmin, Hparam.ymax, xmin,     ymax_aid);
      THistPainter::ProjectParabolic2xy(Hparam.xmax, Hparam.ymax, xmax_aid, ymax);
      THistPainter::ProjectParabolic2xy(Hparam.xmax, Hparam.ymin, xmax,     ymin);

      if (xmin > xmin_aid) xmin = xmin_aid;
      if (ymin > ymin_aid) ymin = ymin_aid;
      if (xmax < xmax_aid) xmax = xmax_aid;
      if (ymax < ymax_aid) ymax = ymax_aid;
      if (Hparam.ymin<0 && Hparam.ymax>0) {
         THistPainter::ProjectParabolic2xy(Hparam.xmin, 0, xmin_aid, ymin_aid);
         THistPainter::ProjectParabolic2xy(Hparam.xmax, 0, xmax_aid, ymin_aid);
         if (xmin >xmin_aid) xmin = xmin_aid;
         if (xmax <xmax_aid) xmax = xmax_aid;
      }
      if (Hparam.xmin<0 && Hparam.xmax>0) {
         THistPainter::ProjectParabolic2xy(0, Hparam.ymin, xmin_aid, ymin_aid);
         THistPainter::ProjectParabolic2xy(0, Hparam.ymax, xmax_aid, ymin_aid);
         if (ymin >ymin_aid) ymin = ymin_aid;
         if (ymax <ymax_aid) ymax = ymax_aid;
      }
   }
   Hparam.xmin= xmin;
   Hparam.xmax= xmax;
   Hparam.ymin= ymin;
   Hparam.ymax= ymax;

   Double_t dx   = xmax-xmin;
   Double_t dy   = ymax-ymin;
   Double_t dxr  = dx/(1 - gPad->GetLeftMargin()   - gPad->GetRightMargin());
   Double_t dyr  = dy/(1 - gPad->GetBottomMargin() - gPad->GetTopMargin());

   // Range() could change the size of the pad pixmap and therefore should
   // be called before the other paint routines
   gPad->Range(xmin - dxr*gPad->GetLeftMargin(),
                      ymin - dyr*gPad->GetBottomMargin(),
                      xmax + dxr*gPad->GetRightMargin(),
                      ymax + dyr*gPad->GetTopMargin());
   gPad->RangeAxis(xmin, ymin, xmax, ymax);
}


//______________________________________________________________________________
void THistPainter::SetHistogram(TH1 *h)
{
   /* Begin_html
   Set current histogram to "h".
   End_html */

   if (h == 0)  return;
   fH = h;
   fXaxis = h->GetXaxis();
   fYaxis = h->GetYaxis();
   fZaxis = h->GetZaxis();
   fFunctions = fH->GetListOfFunctions();
}


//______________________________________________________________________________
Int_t THistPainter::TableInit()
{
   /* Begin_html
   Initialize various options to draw 2D histograms.
   End_html */

   static const char *where = "TableInit";

   Int_t first, last;
   Double_t yMARGIN= gStyle->GetHistTopMargin();
   Double_t zmin, zmax;
   Int_t maximum = 0;
   Int_t minimum = 0;
   if (fH->GetMaximumStored() != -1111) maximum = 1;
   if (fH->GetMinimumStored() != -1111) minimum = 1;

   //    -----------------  Compute X axis parameters
   first           = fXaxis->GetFirst();
   last            = fXaxis->GetLast();
   Hparam.xlast    = last;
   Hparam.xfirst   = first;
   Hparam.xlowedge = fXaxis->GetBinLowEdge(first);
   Hparam.xbinsize = fXaxis->GetBinWidth(first);
   Hparam.xmin     = Hparam.xlowedge;
   Hparam.xmax     = fXaxis->GetBinLowEdge(last)+fXaxis->GetBinWidth(last);

   //       if log scale in X, replace xmin,max by the log
   if (Hoption.Logx) {
   //   find the first edge of a bin that is > 0
      if (Hparam.xlowedge <=0 ) {
         Hparam.xlowedge = fXaxis->GetBinUpEdge(fXaxis->FindFixBin(0.01*Hparam.xbinsize));
         Hparam.xmin  = Hparam.xlowedge;
      }
      if (Hparam.xmin <=0 || Hparam.xmax <=0) {
         Error(where, "cannot set X axis to log scale");
         return 0;
      }
      Hparam.xfirst= fXaxis->FindFixBin(Hparam.xmin);
      if (Hparam.xfirst < first) Hparam.xfirst = first;
      Hparam.xlast = fXaxis->FindFixBin(Hparam.xmax);
      if (Hparam.xlast > last) Hparam.xlast = last;
      Hparam.xmin  = TMath::Log10(Hparam.xmin);
      Hparam.xmax  = TMath::Log10(Hparam.xmax);
   }

   //    -----------------  Compute Y axis parameters
   first           = fYaxis->GetFirst();
   last            = fYaxis->GetLast();
   Hparam.ylast    = last;
   Hparam.yfirst   = first;
   Hparam.ylowedge = fYaxis->GetBinLowEdge(first);
   Hparam.ybinsize = fYaxis->GetBinWidth(first);
   if (!Hparam.ybinsize) Hparam.ybinsize = 1;
   Hparam.ymin     = Hparam.ylowedge;
   Hparam.ymax     = fYaxis->GetBinLowEdge(last)+fYaxis->GetBinWidth(last);

   //       if log scale in Y, replace ymin,max by the log
   if (Hoption.Logy) {
      if (Hparam.ylowedge <=0 ) {
         Hparam.ylowedge = fYaxis->GetBinUpEdge(fYaxis->FindFixBin(0.01*Hparam.ybinsize));
         Hparam.ymin  = Hparam.ylowedge;
      }
      if (Hparam.ymin <=0 || Hparam.ymax <=0) {
         Error(where, "cannot set Y axis to log scale");
         return 0;
      }
      Hparam.yfirst= fYaxis->FindFixBin(Hparam.ymin);
      if (Hparam.yfirst < first) Hparam.yfirst = first;
      Hparam.ylast = fYaxis->FindFixBin(Hparam.ymax);
      if (Hparam.ylast > last) Hparam.ylast = last;
      Hparam.ymin  = TMath::Log10(Hparam.ymin);
      Hparam.ymax  = TMath::Log10(Hparam.ymax);
   }


   //    -----------------  Compute Z axis parameters
   Double_t bigp = TMath::Power(10,32);
   zmax = -bigp;
   zmin = bigp;
   Double_t c1, e1;
   Double_t allchan = 0;
   for (Int_t j=Hparam.yfirst; j<=Hparam.ylast;j++) {
      for (Int_t i=Hparam.xfirst; i<=Hparam.xlast;i++) {
         c1 = fH->GetBinContent(i,j);
         zmax = TMath::Max(zmax,c1);
         if (Hoption.Error) {
            e1 = fH->GetBinError(i,j);
            zmax = TMath::Max(zmax,c1+e1);
         }
         zmin = TMath::Min(zmin,c1);
         allchan += c1;
      }
   }

   //     Take into account maximum , minimum

   if (maximum) zmax = fH->GetMaximumStored();
   if (minimum) zmin = fH->GetMinimumStored();
   if (Hoption.Logz && zmax < 0) {
      if (!Hoption.Same) Error(where, "log scale is requested but maximum is less or equal 0 (%f)", zmax);
      return 0;
   } else if (Hoption.Logz && zmin>=0 && zmax==0) { // empty histogram in log scale
      zmin = 0.01;
      zmax = 10.;
   }
   if (zmin >= zmax) {
      if (Hoption.Logz) {
         if (zmax > 0) zmin = 0.001*zmax;
         else {
            if (!Hoption.Same) Error(where, "log scale is requested but maximum is less or equal 0 (%f)", zmax);
            return 0;
         }
      }
   }

   //     take into account normalization factor
   Hparam.allchan = allchan;
   Double_t factor = allchan;
   if (fH->GetNormFactor() > 0) factor = fH->GetNormFactor();
   if (allchan) factor /= allchan;
   if (factor == 0) factor = 1;
   Hparam.factor = factor;
   zmax = factor*zmax;
   zmin = factor*zmin;
   c1 = zmax;
   if (TMath::Abs(zmin) > TMath::Abs(c1)) c1 = zmin;

   //         For log scales, histogram coordinates are log10(ymin) and
   //         log10(ymax). Final adjustment (if not option "Same")
   //         or "+" for ymax) of ymax and ymin for logarithmic scale, if
   //         Maximum and Minimum are not defined.
   if (Hoption.Logz) {
      if (zmin <= 0) {
         zmin = TMath::Min((Double_t)1, (Double_t)0.001*zmax);
         fH->SetMinimum(zmin);
      }
      zmin = TMath::Log10(zmin);
      if (!minimum) zmin += TMath::Log10(0.5);
      zmax = TMath::Log10(zmax);
      if (!maximum) zmax += TMath::Log10(2*(0.9/0.95));
      goto LZMIN;
   }

   //         final adjustment of YMAXI for linear scale (if not option "Same"):
   //         decrease histogram height to MAX% of allowed height if HMAXIM
   //         has not been called.
   //         MAX% is the value in percent which has been set in HPLSET
   //         (default is 90%).
   if (!maximum) {
      zmax += yMARGIN*(zmax-zmin);
   }

   //         final adjustment of ymin for linear scale.
   //         if minimum is not set , then ymin is set to zero if >0
   //         or to ymin - yMARGIN if <0.
   if (!minimum) {
      if (gStyle->GetHistMinimumZero()) {
         if (zmin >= 0) zmin = 0;
         else           zmin -= yMARGIN*(zmax-zmin);
      } else {
         Double_t dzmin = yMARGIN*(zmax-zmin);
         if (zmin >= 0 && (zmin-dzmin <= 0)) zmin  = 0;
         else                                zmin -= dzmin;
      }
   }

LZMIN:
   Hparam.zmin = zmin;
   Hparam.zmax = zmax;

   //     Set bar offset and width
   Hparam.baroffset = fH->GetBarOffset();
   Hparam.barwidth  = fH->GetBarWidth();

   return 1;
}


//______________________________________________________________________________
const char * THistPainter::GetBestFormat(Double_t v, Double_t e, const char *f)
{
   /* Begin_html
   This function returns the best format to print the error value (e)
   knowing the parameter value (v) and the format (f) used to print it.
   End_html */

   static char ef[20];
   char tf[20], tv[64];

   // print v with the format f in tv.
   snprintf(tf,20,"%s%s","%",f);
   snprintf(tv,64,tf,v);

   // Analyse tv.
   TString sv = tv;
   int ie = sv.Index("e");
   int iE = sv.Index("E");
   int id = sv.Index(".");

   // v has been printed with the exponent notation.
   // There is 2 cases, the exponent is positive or negative
   if (ie >= 0 || iE >= 0) {
      if (sv.Index("+") >= 0) {
         if (e < 1) {
            snprintf(ef,20,"%s.1f","%");
         } else {
            if (ie >= 0) {
               snprintf(ef,20,"%s.%de","%",ie-id-1);
            } else {
               snprintf(ef,20,"%s.%dE","%",iE-id-1);
            }
         }
      } else {
         if (ie >= 0) {
            snprintf(ef,20,"%s.%de","%",ie-id-1);
         } else {
            snprintf(ef,20,"%s.%dE","%",iE-id-1);
         }
      }

   // There is not '.' in tv. e will be printed with one decimal digit.
   } else if (id < 0) {
      snprintf(ef,20,"%s.1f","%");

   // There is a '.' in tv and no exponent notation. e's decimal part will
   // have the same number of digits as v's one.
   } else {
      snprintf(ef,20,"%s.%df","%",sv.Length()-id-1);
   }

   return ef;
}


//______________________________________________________________________________
void THistPainter::SetShowProjection(const char *option,Int_t nbins)
{
   /* Begin_html
   Set projection.
   End_html */

   if (fShowProjection) return;
   TString opt = option;
   opt.ToLower();
   Int_t projection = 0;
   if (opt.Contains("x"))  projection = 1;
   if (opt.Contains("y"))  projection = 2;
   if (opt.Contains("z"))  projection = 3;
   if (opt.Contains("xy")) projection = 4;
   if (opt.Contains("yx")) projection = 5;
   if (opt.Contains("xz")) projection = 6;
   if (opt.Contains("zx")) projection = 7;
   if (opt.Contains("yz")) projection = 8;
   if (opt.Contains("zy")) projection = 9;
   if (projection < 4) fShowOption = option+1;
   else                fShowOption = option+2;
   fShowProjection = projection+100*nbins;
   gROOT->MakeDefCanvas();
   gPad->SetName(Form("c_%lx_projection_%d", (ULong_t)fH, fShowProjection));
   gPad->SetGrid();
}


//______________________________________________________________________________
void THistPainter::ShowProjectionX(Int_t /*px*/, Int_t py)
{
   /* Begin_html
   Show projection onto X.
   End_html */

   Int_t nbins = (Int_t)fShowProjection/100;
   gPad->SetDoubleBuffer(0); // turn off double buffer mode
   gVirtualX->SetDrawMode(TVirtualX::kInvert); // set the drawing mode to XOR mode

   // Erase old position and draw a line at current position
   static int pyold1 = 0;
   static int pyold2 = 0;
   float uxmin = gPad->GetUxmin();
   float uxmax = gPad->GetUxmax();
   int pxmin   = gPad->XtoAbsPixel(uxmin);
   int pxmax   = gPad->XtoAbsPixel(uxmax);
   Float_t upy = gPad->AbsPixeltoY(py);
   Float_t y   = gPad->PadtoY(upy);
   Int_t biny1 = fH->GetYaxis()->FindBin(y);
   Int_t biny2 = TMath::Min(biny1+nbins-1, fH->GetYaxis()->GetNbins());
   Int_t py1   = gPad->YtoAbsPixel(fH->GetYaxis()->GetBinLowEdge(biny1));
   Int_t py2   = gPad->YtoAbsPixel(fH->GetYaxis()->GetBinUpEdge(biny2));

   if (pyold1 || pyold2) gVirtualX->DrawBox(pxmin,pyold1,pxmax,pyold2,TVirtualX::kFilled);
   gVirtualX->DrawBox(pxmin,py1,pxmax,py2,TVirtualX::kFilled);
   pyold1 = py1;
   pyold2 = py2;

   // Create or set the new canvas proj x
   TVirtualPad *padsav = gPad;
   TVirtualPad *c = (TVirtualPad*)gROOT->GetListOfCanvases()->FindObject(Form("c_%lx_projection_%d",
                                                                              (ULong_t)fH, fShowProjection));
   if (c) {
      c->Clear();
   } else {
      fShowProjection = 0;
      pyold1 = 0;
      pyold2 = 0;
      return;
   }
   c->cd();
   c->SetLogy(padsav->GetLogz());
   c->SetLogx(padsav->GetLogx());

   // Draw slice corresponding to mouse position
   TString prjName = TString::Format("slice_px_of_%s",fH->GetName());
   TH1D *hp = ((TH2*)fH)->ProjectionX(prjName, biny1, biny2);
   if (hp) {
      hp->SetFillColor(38);
      // apply a patch from Oliver Freyermuth to set the title in the projection using the range of the projected Y values
      if (biny1 == biny2) {
         Double_t valueFrom   = fH->GetYaxis()->GetBinLowEdge(biny1);
         Double_t valueTo     = fH->GetYaxis()->GetBinUpEdge(biny1);
         // Limit precision to 1 digit more than the difference between upper and lower bound (to also catch 121.5-120.5).
         Int_t valuePrecision = -TMath::Nint(TMath::Log10(valueTo-valueFrom))+1;
         if (fH->GetYaxis()->GetLabels() != NULL) {
            hp->SetTitle(TString::Format("ProjectionX of biny=%d [y=%.*lf..%.*lf] %s", biny1, valuePrecision, valueFrom, valuePrecision, valueTo, fH->GetYaxis()->GetBinLabel(biny1)));
         } else {
            hp->SetTitle(TString::Format("ProjectionX of biny=%d [y=%.*lf..%.*lf]", biny1, valuePrecision, valueFrom, valuePrecision, valueTo));
         }
      } else {
         Double_t valueFrom   = fH->GetYaxis()->GetBinLowEdge(biny1);
         Double_t valueTo     = fH->GetYaxis()->GetBinUpEdge(biny2);
         // Limit precision to 1 digit more than the difference between upper and lower bound (to also catch 121.5-120.5).
         // biny1 is used here to get equal precision no matter how large the binrange is,
         // otherwise precision may change when moving the mouse to the histogram boundaries (limiting effective binrange).
         Int_t valuePrecision = -TMath::Nint(TMath::Log10(fH->GetYaxis()->GetBinUpEdge(biny1)-valueFrom))+1;
         if (fH->GetYaxis()->GetLabels() != NULL) {
            hp->SetTitle(TString::Format("ProjectionX of biny=[%d,%d] [y=%.*lf..%.*lf] [%s..%s]", biny1, biny2, valuePrecision, valueFrom, valuePrecision, valueTo, fH->GetYaxis()->GetBinLabel(biny1), fH->GetYaxis()->GetBinLabel(biny2)));
         } else {
            hp->SetTitle(TString::Format("ProjectionX of biny=[%d,%d] [y=%.*lf..%.*lf]", biny1, biny2, valuePrecision, valueFrom, valuePrecision, valueTo));
         }
      }
      hp->SetXTitle(fH->GetXaxis()->GetTitle());
      hp->SetYTitle("Number of Entries");
      hp->Draw();
      c->Update();
      padsav->cd();
   }
}


//______________________________________________________________________________
void THistPainter::ShowProjectionY(Int_t px, Int_t /*py*/)
{
   /* Begin_html
   Show projection onto Y.
   End_html */

   Int_t nbins = (Int_t)fShowProjection/100;
   gPad->SetDoubleBuffer(0);             // turn off double buffer mode
   gVirtualX->SetDrawMode(TVirtualX::kInvert);  // set the drawing mode to XOR mode

   // Erase old position and draw a line at current position
   static int pxold1 = 0;
   static int pxold2 = 0;
   float uymin = gPad->GetUymin();
   float uymax = gPad->GetUymax();
   int pymin   = gPad->YtoAbsPixel(uymin);
   int pymax   = gPad->YtoAbsPixel(uymax);
   Float_t upx = gPad->AbsPixeltoX(px);
   Float_t x   = gPad->PadtoX(upx);
   Int_t binx1 = fH->GetXaxis()->FindBin(x);
   Int_t binx2 = TMath::Min(binx1+nbins-1, fH->GetXaxis()->GetNbins());
   Int_t px1   = gPad->XtoAbsPixel(fH->GetXaxis()->GetBinLowEdge(binx1));
   Int_t px2   = gPad->XtoAbsPixel(fH->GetXaxis()->GetBinUpEdge(binx2));

   if (pxold1 || pxold2) gVirtualX->DrawBox(pxold1,pymin,pxold2,pymax,TVirtualX::kFilled);
   gVirtualX->DrawBox(px1,pymin,px2,pymax,TVirtualX::kFilled);
   pxold1 = px1;
   pxold2 = px2;

   // Create or set the new canvas proj y
   TVirtualPad *padsav = gPad;
   TVirtualPad *c = (TVirtualPad*)gROOT->GetListOfCanvases()->FindObject(Form("c_%lx_projection_%d",
                                                                              (ULong_t)fH, fShowProjection));
   if (c) {
      c->Clear();
   } else {
      fShowProjection = 0;
      pxold1 = 0;
      pxold2 = 0;
      return;
   }
   c->cd();
   c->SetLogy(padsav->GetLogz());
   c->SetLogx(padsav->GetLogy());

   // Draw slice corresponding to mouse position
   TString prjName = TString::Format("slice_py_of_%s",fH->GetName());
   TH1D *hp = ((TH2*)fH)->ProjectionY(prjName, binx1, binx2);
   if (hp) {
      hp->SetFillColor(38);
      // apply a patch from Oliver Freyermuth to set the title in the projection using the range of the projected X values
      if (binx1 == binx2) {
         Double_t valueFrom   = fH->GetXaxis()->GetBinLowEdge(binx1);
         Double_t valueTo     = fH->GetXaxis()->GetBinUpEdge(binx1);
         // Limit precision to 1 digit more than the difference between upper and lower bound (to also catch 121.5-120.5).
         Int_t valuePrecision = -TMath::Nint(TMath::Log10(valueTo-valueFrom))+1;
         if (fH->GetXaxis()->GetLabels() != NULL) {
            hp->SetTitle(TString::Format("ProjectionY of binx=%d [x=%.*lf..%.*lf] [%s]", binx1, valuePrecision, valueFrom, valuePrecision, valueTo, fH->GetXaxis()->GetBinLabel(binx1)));
         } else {
            hp->SetTitle(TString::Format("ProjectionY of binx=%d [x=%.*lf..%.*lf]", binx1, valuePrecision, valueFrom, valuePrecision, valueTo));
         }
      } else {
         Double_t valueFrom   = fH->GetXaxis()->GetBinLowEdge(binx1);
         Double_t valueTo     = fH->GetXaxis()->GetBinUpEdge(binx2);
         // Limit precision to 1 digit more than the difference between upper and lower bound (to also catch 121.5-120.5).
         // binx1 is used here to get equal precision no matter how large the binrange is,
         // otherwise precision may change when moving the mouse to the histogram boundaries (limiting effective binrange).
         Int_t valuePrecision = -TMath::Nint(TMath::Log10(fH->GetXaxis()->GetBinUpEdge(binx1)-valueFrom))+1;
         if (fH->GetXaxis()->GetLabels() != NULL) {
            hp->SetTitle(TString::Format("ProjectionY of binx=[%d,%d] [x=%.*lf..%.*lf] [%s..%s]", binx1, binx2, valuePrecision, valueFrom, valuePrecision, valueTo, fH->GetXaxis()->GetBinLabel(binx1), fH->GetXaxis()->GetBinLabel(binx2)));
         } else {
            hp->SetTitle(TString::Format("ProjectionY of binx=[%d,%d] [x=%.*lf..%.*lf]", binx1, binx2, valuePrecision, valueFrom, valuePrecision, valueTo));
         }
      }
      hp->SetXTitle(fH->GetYaxis()->GetTitle());
      hp->SetYTitle("Number of Entries");
      hp->Draw();
      c->Update();
      padsav->cd();
   }
}


//______________________________________________________________________________
void THistPainter::ShowProjection3(Int_t px, Int_t py)
{
   /* Begin_html
   Show projection (specified by <tt>fShowProjection</tt>) of a <tt>TH3</tt>.
   The drawing option for the projection is in <tt>fShowOption</tt>.
   <p>
   First implementation; R.Brun <br>
   Full implementation: Tim Tran (timtran@jlab.org)  April 2006
   End_html */

   Int_t nbins=(Int_t)fShowProjection/100; //decode nbins
   if (fH->GetDimension() < 3) {
      if (fShowProjection%100 == 1) {ShowProjectionX(px,py); return;}
      if (fShowProjection%100 == 2) {ShowProjectionY(px,py); return;}
   }

   gPad->SetDoubleBuffer(0);             // turn off double buffer mode
   gVirtualX->SetDrawMode(TVirtualX::kInvert);  // set the drawing mode to XOR mode

   // Erase old position and draw a line at current position
   TView *view = gPad->GetView();
   if (!view) return;
   TH3 *h3 = (TH3*)fH;
   TAxis *xaxis = h3->GetXaxis();
   TAxis *yaxis = h3->GetYaxis();
   TAxis *zaxis = h3->GetZaxis();
   Double_t u[3],xx[3];

   static TPoint line1[2];//store end points of a line, initialised 0 by default
   static TPoint line2[2];// second line when slice thickness > 1 bin thickness
   static TPoint line3[2];
   static TPoint line4[2];
   static TPoint endface1[5];
   static TPoint endface2[5];
   static TPoint rect1[5];//store vertices of the polyline (rectangle), initialsed 0 by default
   static TPoint rect2[5];// second rectangle when slice thickness > 1 bin thickness

   Double_t uxmin = gPad->GetUxmin();
   Double_t uxmax = gPad->GetUxmax();
   Double_t uymin = gPad->GetUymin();
   Double_t uymax = gPad->GetUymax();

   int pxmin = gPad->XtoAbsPixel(uxmin);
   int pxmax = gPad->XtoAbsPixel(uxmax);
   if (pxmin==pxmax) return;
   int pymin = gPad->YtoAbsPixel(uymin);
   int pymax = gPad->YtoAbsPixel(uymax);
   if (pymin==pymax) return;
   Double_t cx    = (pxmax-pxmin)/(uxmax-uxmin);
   Double_t cy    = (pymax-pymin)/(uymax-uymin);
   TVirtualPad *padsav = gPad;
   TVirtualPad *c = (TVirtualPad*)gROOT->GetListOfCanvases()->FindObject(Form("c_%lx_projection_%d",
                                                                              (ULong_t)fH, fShowProjection));
   if (!c) {
      fShowProjection = 0;
      return;
   }

   switch ((Int_t)fShowProjection%100) {
      case 1:
         // "x"
         {
            Int_t firstY = yaxis->GetFirst();
            Int_t lastY  = yaxis->GetLast();
            Int_t biny = firstY + Int_t((lastY-firstY)*(px-pxmin)/(pxmax-pxmin));
            Int_t biny2 = TMath::Min(biny+nbins-1,yaxis->GetNbins() );
            yaxis->SetRange(biny,biny2);
            Int_t firstZ = zaxis->GetFirst();
            Int_t lastZ  = zaxis->GetLast();
            Int_t binz = firstZ + Int_t((lastZ-firstZ)*(py-pymin)/(pymax-pymin));
            Int_t binz2 = TMath::Min(binz+nbins-1,zaxis->GetNbins() );
            zaxis->SetRange(binz,binz2);
            if (line1[0].GetX()) gVirtualX->DrawPolyLine(2,line1);
            if (nbins>1 && line1[0].GetX()) {
               gVirtualX->DrawPolyLine(2,line2);
               gVirtualX->DrawPolyLine(2,line3);
               gVirtualX->DrawPolyLine(2,line4);
               gVirtualX->DrawPolyLine(5,endface1);
               gVirtualX->DrawPolyLine(5,endface2);
            }
            xx[0] = xaxis->GetXmin();
            xx[2] = zaxis->GetBinCenter(binz);
            xx[1] = yaxis->GetBinCenter(biny);
            view->WCtoNDC(xx,u);
            line1[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            line1[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[0] = xaxis->GetXmax();
            view->WCtoNDC(xx,u);
            line1[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            line1[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
            gVirtualX->DrawPolyLine(2,line1);
            if (nbins>1) {
               xx[0] = xaxis->GetXmin();
               xx[2] = zaxis->GetBinCenter(binz+nbins-1);
               xx[1] = yaxis->GetBinCenter(biny);
               view->WCtoNDC(xx,u);
               line2[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line2[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[0] = xaxis->GetXmax();
               view->WCtoNDC(xx,u);
               line2[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line2[1].SetY(pymin + Int_t((u[1]-uymin)*cy));

               xx[0] = xaxis->GetXmin();
               xx[2] = zaxis->GetBinCenter(binz+nbins-1);
               xx[1] = yaxis->GetBinCenter(biny+nbins-1);
               view->WCtoNDC(xx,u);
               line3[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line3[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[0] = xaxis->GetXmax();
               view->WCtoNDC(xx,u);
               line3[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line3[1].SetY(pymin + Int_t((u[1]-uymin)*cy));

               xx[0] = xaxis->GetXmin();
               xx[2] = zaxis->GetBinCenter(binz);
               xx[1] = yaxis->GetBinCenter(biny+nbins-1);
               view->WCtoNDC(xx,u);
               line4[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line4[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[0] = xaxis->GetXmax();
               view->WCtoNDC(xx,u);
               line4[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line4[1].SetY(pymin + Int_t((u[1]-uymin)*cy));

               endface1[0].SetX(line1[0].GetX());
               endface1[0].SetY(line1[0].GetY());
               endface1[1].SetX(line2[0].GetX());
               endface1[1].SetY(line2[0].GetY());
               endface1[2].SetX(line3[0].GetX());
               endface1[2].SetY(line3[0].GetY());
               endface1[3].SetX(line4[0].GetX());
               endface1[3].SetY(line4[0].GetY());
               endface1[4].SetX(line1[0].GetX());
               endface1[4].SetY(line1[0].GetY());

               endface2[0].SetX(line1[1].GetX());
               endface2[0].SetY(line1[1].GetY());
               endface2[1].SetX(line2[1].GetX());
               endface2[1].SetY(line2[1].GetY());
               endface2[2].SetX(line3[1].GetX());
               endface2[2].SetY(line3[1].GetY());
               endface2[3].SetX(line4[1].GetX());
               endface2[3].SetY(line4[1].GetY());
               endface2[4].SetX(line1[1].GetX());
               endface2[4].SetY(line1[1].GetY());

               gVirtualX->DrawPolyLine(2,line2);
               gVirtualX->DrawPolyLine(2,line3);
               gVirtualX->DrawPolyLine(2,line4);
               gVirtualX->DrawPolyLine(5,endface1);
               gVirtualX->DrawPolyLine(5,endface2);
            }
            c->Clear();
            c->cd();
            TH1 *hp = h3->Project3D("x");
            yaxis->SetRange(firstY,lastY);
            zaxis->SetRange(firstZ,lastZ);
            if (hp) {
               hp->SetFillColor(38);
               if (nbins == 1)
                  hp->SetTitle(TString::Format("ProjectionX of biny=%d [y=%.1f..%.1f] binz=%d [z=%.1f..%.1f]", biny, yaxis->GetBinLowEdge(biny), yaxis->GetBinUpEdge(biny),
                                               binz, zaxis->GetBinLowEdge(binz), zaxis->GetBinUpEdge(binz)));
               else {
                  hp->SetTitle(TString::Format("ProjectionX, biny=[%d,%d] [y=%.1f..%.1f], binz=[%d,%d] [z=%.1f..%.1f]", biny, biny2, yaxis->GetBinLowEdge(biny), yaxis->GetBinUpEdge(biny2),
                                               binz, binz2, zaxis->GetBinLowEdge(binz), zaxis->GetBinUpEdge(binz2) ) );
               }
               hp->SetXTitle(fH->GetXaxis()->GetTitle());
               hp->SetYTitle("Number of Entries");
               hp->Draw(fShowOption.Data());
            }
         }
         break;

      case 2:
         // "y"
         {
            Int_t firstX = xaxis->GetFirst();
            Int_t lastX  = xaxis->GetLast();
            Int_t binx = firstX + Int_t((lastX-firstX)*(px-pxmin)/(pxmax-pxmin));
            Int_t binx2 = TMath::Min(binx+nbins-1,xaxis->GetNbins() );
            xaxis->SetRange(binx,binx2);
            Int_t firstZ = zaxis->GetFirst();
            Int_t lastZ  = zaxis->GetLast();
            Int_t binz = firstZ + Int_t((lastZ-firstZ)*(py-pymin)/(pymax-pymin));
            Int_t binz2 = TMath::Min(binz+nbins-1,zaxis->GetNbins() );
            zaxis->SetRange(binz,binz2);
            if (line1[0].GetX()) gVirtualX->DrawPolyLine(2,line1);
            if (nbins>1 && line1[0].GetX()) {
               gVirtualX->DrawPolyLine(2,line2);
               gVirtualX->DrawPolyLine(2,line3);
               gVirtualX->DrawPolyLine(2,line4);
               gVirtualX->DrawPolyLine(5,endface1);
               gVirtualX->DrawPolyLine(5,endface2);
            }
            xx[0]=xaxis->GetBinCenter(binx);
            xx[2] = zaxis->GetBinCenter(binz);
            xx[1] = yaxis->GetXmin();
            view->WCtoNDC(xx,u);
            line1[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            line1[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[1] = yaxis->GetXmax();
            view->WCtoNDC(xx,u);
            line1[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            line1[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
            gVirtualX->DrawPolyLine(2,line1);
            if (nbins>1) {
               xx[1] = yaxis->GetXmin();
               xx[2] = zaxis->GetBinCenter(binz+nbins-1);
               xx[0] = xaxis->GetBinCenter(binx);
               view->WCtoNDC(xx,u);
               line2[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line2[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[1] = yaxis->GetXmax();
               view->WCtoNDC(xx,u);
               line2[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line2[1].SetY(pymin + Int_t((u[1]-uymin)*cy));

               xx[1] = yaxis->GetXmin();
               xx[2] = zaxis->GetBinCenter(binz+nbins-1);
               xx[0] = xaxis->GetBinCenter(binx+nbins-1);
               view->WCtoNDC(xx,u);
               line3[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line3[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[1] = yaxis->GetXmax();
               view->WCtoNDC(xx,u);
               line3[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line3[1].SetY(pymin + Int_t((u[1]-uymin)*cy));

               xx[1] = yaxis->GetXmin();
               xx[2] = zaxis->GetBinCenter(binz);
               xx[0] = xaxis->GetBinCenter(binx+nbins-1);
               view->WCtoNDC(xx,u);
               line4[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line4[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[1] = yaxis->GetXmax();
               view->WCtoNDC(xx,u);
               line4[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line4[1].SetY(pymin + Int_t((u[1]-uymin)*cy));

               endface1[0].SetX(line1[0].GetX());
               endface1[0].SetY(line1[0].GetY());
               endface1[1].SetX(line2[0].GetX());
               endface1[1].SetY(line2[0].GetY());
               endface1[2].SetX(line3[0].GetX());
               endface1[2].SetY(line3[0].GetY());
               endface1[3].SetX(line4[0].GetX());
               endface1[3].SetY(line4[0].GetY());
               endface1[4].SetX(line1[0].GetX());
               endface1[4].SetY(line1[0].GetY());

               endface2[0].SetX(line1[1].GetX());
               endface2[0].SetY(line1[1].GetY());
               endface2[1].SetX(line2[1].GetX());
               endface2[1].SetY(line2[1].GetY());
               endface2[2].SetX(line3[1].GetX());
               endface2[2].SetY(line3[1].GetY());
               endface2[3].SetX(line4[1].GetX());
               endface2[3].SetY(line4[1].GetY());
               endface2[4].SetX(line1[1].GetX());
               endface2[4].SetY(line1[1].GetY());

               gVirtualX->DrawPolyLine(2,line2);
               gVirtualX->DrawPolyLine(2,line3);
               gVirtualX->DrawPolyLine(2,line4);
               gVirtualX->DrawPolyLine(5,endface1);
               gVirtualX->DrawPolyLine(5,endface2);
            }
            c->Clear();
            c->cd();
            TH1 *hp = h3->Project3D("y");
            xaxis->SetRange(firstX,lastX);
            zaxis->SetRange(firstZ,lastZ);
            if (hp) {
               hp->SetFillColor(38);
               if (nbins == 1)
                  hp->SetTitle(TString::Format("ProjectionY of binx=%d [x=%.1f..%.1f] binz=%d [z=%.1f..%.1f]", binx, xaxis->GetBinLowEdge(binx), xaxis->GetBinUpEdge(binx),
                                               binz, zaxis->GetBinLowEdge(binz), zaxis->GetBinUpEdge(binz)));
               else
                  hp->SetTitle(TString::Format("ProjectionY, binx=[%d,%d] [x=%.1f..%.1f], binz=[%d,%d] [z=%.1f..%.1f]", binx, binx2, xaxis->GetBinLowEdge(binx), xaxis->GetBinUpEdge(binx2),
                                               binz, binz2, zaxis->GetBinLowEdge(binz), zaxis->GetBinUpEdge(binz2) ) );
               hp->SetXTitle(fH->GetYaxis()->GetTitle());
               hp->SetYTitle("Number of Entries");
               hp->Draw(fShowOption.Data());
            }
         }
         break;

      case 3:
         // "z"
         {
            Int_t firstX = xaxis->GetFirst();
            Int_t lastX  = xaxis->GetLast();
            Int_t binx = firstX + Int_t((lastX-firstX)*(px-pxmin)/(pxmax-pxmin));
            Int_t binx2 = TMath::Min(binx+nbins-1,xaxis->GetNbins() );
            xaxis->SetRange(binx,binx2);
            Int_t firstY = yaxis->GetFirst();
            Int_t lastY  = yaxis->GetLast();
            Int_t biny = firstY + Int_t((lastY-firstY)*(py-pymin)/(pymax-pymin));
            Int_t biny2 = TMath::Min(biny+nbins-1,yaxis->GetNbins() );
            yaxis->SetRange(biny,biny2);
            if (line1[0].GetX()) gVirtualX->DrawPolyLine(2,line1);
            if (nbins>1 && line1[0].GetX()) {
               gVirtualX->DrawPolyLine(2,line2);
               gVirtualX->DrawPolyLine(2,line3);
               gVirtualX->DrawPolyLine(2,line4);
               gVirtualX->DrawPolyLine(5,endface1);
               gVirtualX->DrawPolyLine(5,endface2);
            }
            xx[0] = xaxis->GetBinCenter(binx);
            xx[1] = yaxis->GetBinCenter(biny);
            xx[2] = zaxis->GetXmin();
            view->WCtoNDC(xx,u);
            line1[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            line1[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[2] = zaxis->GetXmax();
            view->WCtoNDC(xx,u);
            line1[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            line1[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
            gVirtualX->DrawPolyLine(2,line1);
            if (nbins>1) {
               xx[2] = zaxis->GetXmin();
               xx[1] = yaxis->GetBinCenter(biny+nbins-1);
               xx[0] = xaxis->GetBinCenter(binx);
               view->WCtoNDC(xx,u);
               line2[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line2[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[2] = zaxis->GetXmax();
               view->WCtoNDC(xx,u);
               line2[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line2[1].SetY(pymin + Int_t((u[1]-uymin)*cy));

               xx[2] = zaxis->GetXmin();
               xx[1] = yaxis->GetBinCenter(biny+nbins-1);
               xx[0] = xaxis->GetBinCenter(binx+nbins-1);
               view->WCtoNDC(xx,u);
               line3[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line3[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[2] = zaxis->GetXmax();
               view->WCtoNDC(xx,u);
               line3[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line3[1].SetY(pymin + Int_t((u[1]-uymin)*cy));

               xx[2] = zaxis->GetXmin();
               xx[1] = yaxis->GetBinCenter(biny);
               xx[0] = xaxis->GetBinCenter(binx+nbins-1);
               view->WCtoNDC(xx,u);
               line4[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line4[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[2] = zaxis->GetXmax();
               view->WCtoNDC(xx,u);
               line4[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               line4[1].SetY(pymin + Int_t((u[1]-uymin)*cy));

               endface1[0].SetX(line1[0].GetX());
               endface1[0].SetY(line1[0].GetY());
               endface1[1].SetX(line2[0].GetX());
               endface1[1].SetY(line2[0].GetY());
               endface1[2].SetX(line3[0].GetX());
               endface1[2].SetY(line3[0].GetY());
               endface1[3].SetX(line4[0].GetX());
               endface1[3].SetY(line4[0].GetY());
               endface1[4].SetX(line1[0].GetX());
               endface1[4].SetY(line1[0].GetY());

               endface2[0].SetX(line1[1].GetX());
               endface2[0].SetY(line1[1].GetY());
               endface2[1].SetX(line2[1].GetX());
               endface2[1].SetY(line2[1].GetY());
               endface2[2].SetX(line3[1].GetX());
               endface2[2].SetY(line3[1].GetY());
               endface2[3].SetX(line4[1].GetX());
               endface2[3].SetY(line4[1].GetY());
               endface2[4].SetX(line1[1].GetX());
               endface2[4].SetY(line1[1].GetY());

               gVirtualX->DrawPolyLine(2,line2);
               gVirtualX->DrawPolyLine(2,line3);
               gVirtualX->DrawPolyLine(2,line4);
               gVirtualX->DrawPolyLine(5,endface1);
               gVirtualX->DrawPolyLine(5,endface2);
            }
            c->Clear();
            c->cd();
            TH1 *hp = h3->Project3D("z");
            xaxis->SetRange(firstX,lastX);
            yaxis->SetRange(firstY,lastY);
            if (hp) {
               hp->SetFillColor(38);
               if (nbins == 1)
                  hp->SetTitle(TString::Format("ProjectionZ of binx=%d [x=%.1f..%.1f] biny=%d [y=%.1f..%.1f]", binx, xaxis->GetBinLowEdge(binx), xaxis->GetBinUpEdge(binx),
                                               biny, yaxis->GetBinLowEdge(biny), yaxis->GetBinUpEdge(biny)));
               else
                  hp->SetTitle(TString::Format("ProjectionZ, binx=[%d,%d] [x=%.1f..%.1f], biny=[%d,%d] [y=%.1f..%.1f]", binx, binx2, xaxis->GetBinLowEdge(binx), xaxis->GetBinUpEdge(binx2),
                                               biny, biny2, yaxis->GetBinLowEdge(biny), yaxis->GetBinUpEdge(biny2) ) );
               hp->SetXTitle(fH->GetZaxis()->GetTitle());
               hp->SetYTitle("Number of Entries");
               hp->Draw(fShowOption.Data());
            }
         }
         break;

      case 4:
         // "xy"
         {
            Int_t first = zaxis->GetFirst();
            Int_t last  = zaxis->GetLast();
            Int_t binz  = first + Int_t((last-first)*(py-pymin)/(pymax-pymin));
            Int_t binz2 = TMath::Min(binz+nbins-1,zaxis->GetNbins() );
            zaxis->SetRange(binz,binz2);
            if (rect1[0].GetX())            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1 && rect2[0].GetX()) gVirtualX->DrawPolyLine(5,rect2);
            xx[0] = xaxis->GetXmin();
            xx[1] = yaxis->GetXmax();
            xx[2] = zaxis->GetBinCenter(binz);
            view->WCtoNDC(xx,u);
            rect1[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
            rect1[4].SetX(rect1[0].GetX());
            rect1[4].SetY(rect1[0].GetY());
            xx[0] = xaxis->GetXmax();
            view->WCtoNDC(xx,u);
            rect1[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[1] = yaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[0] = xaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1) {
               xx[0] = xaxis->GetXmin();
               xx[1] = yaxis->GetXmax();
               xx[2] = zaxis->GetBinCenter(binz+nbins-1);
               view->WCtoNDC(xx,u);
               rect2[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               rect2[4].SetX(rect2[0].GetX());
               rect2[4].SetY(rect2[0].GetY());
               xx[0] = xaxis->GetXmax();
               view->WCtoNDC(xx,u);
               rect2[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[1] = yaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[0] = xaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
               gVirtualX->DrawPolyLine(5,rect2);
            }

            c->Clear();
            c->cd();
            TH2 *hp = (TH2*)h3->Project3D("xy");
            zaxis->SetRange(first,last);
            if (hp) {
               hp->SetFillColor(38);
               if (nbins==1)hp->SetTitle(TString::Format("ProjectionXY of binz=%d [z=%.1f..%.f]", binz,zaxis->GetBinLowEdge(binz),zaxis->GetBinUpEdge(binz)));
               else        hp->SetTitle(TString::Format("ProjectionXY, binz=[%d,%d] [z=%.1f..%.1f]", binz,binz2,zaxis->GetBinLowEdge(binz),zaxis->GetBinUpEdge(binz2)));
               hp->SetXTitle(fH->GetYaxis()->GetTitle());
               hp->SetYTitle(fH->GetXaxis()->GetTitle());
               hp->SetZTitle("Number of Entries");
               hp->Draw(fShowOption.Data());
            }
         }
         break;

      case 5:
         // "yx"
         {
            Int_t first = zaxis->GetFirst();
            Int_t last  = zaxis->GetLast();
            Int_t binz = first + Int_t((last-first)*(py-pymin)/(pymax-pymin));
            Int_t binz2 = TMath::Min(binz+nbins-1,zaxis->GetNbins() );
            zaxis->SetRange(binz,binz2);
            if (rect1[0].GetX())            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1 && rect2[0].GetX()) gVirtualX->DrawPolyLine(5,rect2);
            xx[0] = xaxis->GetXmin();
            xx[1] = yaxis->GetXmax();
            xx[2] = zaxis->GetBinCenter(binz);
            view->WCtoNDC(xx,u);
            rect1[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
            rect1[4].SetX(rect1[0].GetX());
            rect1[4].SetY(rect1[0].GetY());
            xx[0] = xaxis->GetXmax();
            view->WCtoNDC(xx,u);
            rect1[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[1] = yaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[0] = xaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1) {
               xx[0] = xaxis->GetXmin();
               xx[1] = yaxis->GetXmax();
               xx[2] = zaxis->GetBinCenter(binz+nbins-1);
               view->WCtoNDC(xx,u);
               rect2[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               rect2[4].SetX(rect2[0].GetX());
               rect2[4].SetY(rect2[0].GetY());
               xx[0] = xaxis->GetXmax();
               view->WCtoNDC(xx,u);
               rect2[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[1] = yaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[0] = xaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
               gVirtualX->DrawPolyLine(5,rect2);
            }
            c->Clear();
            c->cd();
            TH2 *hp = (TH2*)h3->Project3D("yx");
            zaxis->SetRange(first,last);
            if (hp) {
               hp->SetFillColor(38);
               if (nbins==1)hp->SetTitle(TString::Format("ProjectionYX of binz=%d [z=%.1f..%.f]", binz,zaxis->GetBinLowEdge(binz),zaxis->GetBinUpEdge(binz)));
               else        hp->SetTitle(TString::Format("ProjectionYX, binz=[%d,%d] [z=%.1f..%.1f]", binz,binz2,zaxis->GetBinLowEdge(binz),zaxis->GetBinUpEdge(binz2)));
               hp->SetXTitle(fH->GetXaxis()->GetTitle());
               hp->SetYTitle(fH->GetYaxis()->GetTitle());
               hp->SetZTitle("Number of Entries");
               hp->Draw(fShowOption.Data());
            }
         }
         break;

      case 6:
         // "xz"
         {
            Int_t first = yaxis->GetFirst();
            Int_t last  = yaxis->GetLast();
            Int_t biny = first + Int_t((last-first)*(py-pymin)/(pymax-pymin));
            Int_t biny2 = TMath::Min(biny+nbins-1,yaxis->GetNbins() );
            yaxis->SetRange(biny,biny2);
            if (rect1[0].GetX())            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1 && rect1[0].GetX()) gVirtualX->DrawPolyLine(5,rect2);
            xx[0] = xaxis->GetXmin();
            xx[2] = zaxis->GetXmax();
            xx[1] = yaxis->GetBinCenter(biny);
            view->WCtoNDC(xx,u);
            rect1[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
            rect1[4].SetX(rect1[0].GetX());
            rect1[4].SetY(rect1[0].GetY());
            xx[0] = xaxis->GetXmax();
            view->WCtoNDC(xx,u);
            rect1[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[2] = zaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[0] = xaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1) {
               xx[0] = xaxis->GetXmin();
               xx[2] = zaxis->GetXmax();
               xx[1] = yaxis->GetBinCenter(biny+nbins-1);
               view->WCtoNDC(xx,u);
               rect2[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               rect2[4].SetX(rect2[0].GetX());
               rect2[4].SetY(rect2[0].GetY());
               xx[0] = xaxis->GetXmax();
               view->WCtoNDC(xx,u);
               rect2[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[2] = zaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[0] = xaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
               gVirtualX->DrawPolyLine(5,rect2);
            }
            c->Clear();
            c->cd();
            TH2 *hp = (TH2*)h3->Project3D("xz");
            yaxis->SetRange(first,last);
            if (hp) {
               hp->SetFillColor(38);
               if (nbins==1)hp->SetTitle(TString::Format("ProjectionXZ of biny=%d [y=%.1f..%.f]", biny,yaxis->GetBinLowEdge(biny),yaxis->GetBinUpEdge(biny)));
               else        hp->SetTitle(TString::Format("ProjectionXZ, biny=[%d,%d] [y=%.1f..%.1f]", biny,biny2,yaxis->GetBinLowEdge(biny),yaxis->GetBinUpEdge(biny2)));
               hp->SetXTitle(fH->GetZaxis()->GetTitle());
               hp->SetYTitle(fH->GetXaxis()->GetTitle());
               hp->SetZTitle("Number of Entries");
               hp->Draw(fShowOption.Data());
            }
         }
         break;

      case 7:
         // "zx"
         {
            Int_t first = yaxis->GetFirst();
            Int_t last  = yaxis->GetLast();
            Int_t biny = first + Int_t((last-first)*(py-pymin)/(pymax-pymin));
            Int_t biny2 = TMath::Min(biny+nbins-1,yaxis->GetNbins() );
            yaxis->SetRange(biny,biny2);
            if (rect1[0].GetX())            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1 && rect1[0].GetX()) gVirtualX->DrawPolyLine(5,rect2);
            xx[0] = xaxis->GetXmin();
            xx[2] = zaxis->GetXmax();
            xx[1] = yaxis->GetBinCenter(biny);
            view->WCtoNDC(xx,u);
            rect1[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
            rect1[4].SetX(rect1[0].GetX());
            rect1[4].SetY(rect1[0].GetY());
            xx[0] = xaxis->GetXmax();
            view->WCtoNDC(xx,u);
            rect1[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[2] = zaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[0] = xaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1) {
               xx[0] = xaxis->GetXmin();
               xx[2] = zaxis->GetXmax();
               xx[1] = yaxis->GetBinCenter(biny+nbins-1);
               view->WCtoNDC(xx,u);
               rect2[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               rect2[4].SetX(rect2[0].GetX());
               rect2[4].SetY(rect2[0].GetY());
               xx[0] = xaxis->GetXmax();
               view->WCtoNDC(xx,u);
               rect2[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[2] = zaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[0] = xaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
               gVirtualX->DrawPolyLine(5,rect2);
            }
            c->Clear();
            c->cd();
            TH2 *hp = (TH2*)h3->Project3D("zx");
            yaxis->SetRange(first,last);
            if (hp) {
               hp->SetFillColor(38);
               if (nbins==1)hp->SetTitle(TString::Format("ProjectionZX of biny=%d [y=%.1f..%.f]", biny,yaxis->GetBinLowEdge(biny),yaxis->GetBinUpEdge(biny)));
               else        hp->SetTitle(TString::Format("ProjectionZX, biny=[%d,%d] [y=%.1f..%.1f]", biny,biny2,yaxis->GetBinLowEdge(biny),yaxis->GetBinUpEdge(biny2)));
               hp->SetXTitle(fH->GetXaxis()->GetTitle());
               hp->SetYTitle(fH->GetZaxis()->GetTitle());
               hp->SetZTitle("Number of Entries");
               hp->Draw(fShowOption.Data());
            }
         }
         break;

      case 8:
         // "yz"
         {
            Int_t first = xaxis->GetFirst();
            Int_t last  = xaxis->GetLast();
            Int_t binx = first + Int_t((last-first)*(px-pxmin)/(pxmax-pxmin));
            Int_t binx2 = TMath::Min(binx+nbins-1,xaxis->GetNbins() );
            xaxis->SetRange(binx,binx2);
            if (rect1[0].GetX()) gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1 && rect1[0].GetX()) gVirtualX->DrawPolyLine(5,rect2);
            xx[2] = zaxis->GetXmin();
            xx[1] = yaxis->GetXmax();
            xx[0] = xaxis->GetBinCenter(binx);
            view->WCtoNDC(xx,u);
            rect1[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
            rect1[4].SetX(rect1[0].GetX());
            rect1[4].SetY(rect1[0].GetY());
            xx[2] = zaxis->GetXmax();
            view->WCtoNDC(xx,u);
            rect1[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[1] = yaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[2] = zaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1) {
               xx[2] = zaxis->GetXmin();
               xx[1] = yaxis->GetXmax();
               xx[0] = xaxis->GetBinCenter(binx+nbins-1);
               view->WCtoNDC(xx,u);
               rect2[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               rect2[4].SetX(rect2[0].GetX());
               rect2[4].SetY(rect2[0].GetY());
               xx[2] = zaxis->GetXmax();
               view->WCtoNDC(xx,u);
               rect2[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[1] = yaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[2] = zaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
               gVirtualX->DrawPolyLine(5,rect2);
            }
            c->Clear();
            c->cd();
            TH2 *hp = (TH2*)h3->Project3D("yz");
            xaxis->SetRange(first,last);
            if (hp) {
               hp->SetFillColor(38);
               if (nbins==1)hp->SetTitle(TString::Format("ProjectionYZ of binx=%d [x=%.1f..%.f]", binx,xaxis->GetBinLowEdge(binx),xaxis->GetBinUpEdge(binx)));
               else         hp->SetTitle(TString::Format("ProjectionYZ, binx=[%d,%d] [x=%.1f..%.1f]", binx,binx2,xaxis->GetBinLowEdge(binx),xaxis->GetBinUpEdge(binx2)));
               hp->SetXTitle(fH->GetZaxis()->GetTitle());
               hp->SetYTitle(fH->GetYaxis()->GetTitle());
               hp->SetZTitle("Number of Entries");
               hp->Draw(fShowOption.Data());
            }
         }
         break;

      case 9:
         // "zy"
         {
            Int_t first = xaxis->GetFirst();
            Int_t last  = xaxis->GetLast();
            Int_t binx = first + Int_t((last-first)*(px-pxmin)/(pxmax-pxmin));
            Int_t binx2 = TMath::Min(binx+nbins-1,xaxis->GetNbins() );
            xaxis->SetRange(binx,binx2);
            if (rect1[0].GetX()) gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1 && rect1[0].GetX()) gVirtualX->DrawPolyLine(5,rect2);
            xx[2] = zaxis->GetXmin();
            xx[1] = yaxis->GetXmax();
            xx[0] = xaxis->GetBinCenter(binx);
            view->WCtoNDC(xx,u);
            rect1[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
            rect1[4].SetX(rect1[0].GetX());
            rect1[4].SetY(rect1[0].GetY());
            xx[2] = zaxis->GetXmax();
            view->WCtoNDC(xx,u);
            rect1[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[1] = yaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
            xx[2] = zaxis->GetXmin();
            view->WCtoNDC(xx,u);
            rect1[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
            rect1[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
            gVirtualX->DrawPolyLine(5,rect1);
            if (nbins>1) {
               xx[2] = zaxis->GetXmin();
               xx[1] = yaxis->GetXmax();
               xx[0] = xaxis->GetBinCenter(binx+nbins-1);
               view->WCtoNDC(xx,u);
               rect2[0].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[0].SetY(pymin + Int_t((u[1]-uymin)*cy));
               rect2[4].SetX(rect2[0].GetX());
               rect2[4].SetY(rect2[0].GetY());
               xx[2] = zaxis->GetXmax();
               view->WCtoNDC(xx,u);
               rect2[1].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[1].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[1] = yaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[2].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[2].SetY(pymin + Int_t((u[1]-uymin)*cy));
               xx[2] = zaxis->GetXmin();
               view->WCtoNDC(xx,u);
               rect2[3].SetX(pxmin + Int_t((u[0]-uxmin)*cx));
               rect2[3].SetY(pymin + Int_t((u[1]-uymin)*cy));
               gVirtualX->DrawPolyLine(5,rect2);
            }
            c->Clear();
            c->cd();
            TH2 *hp = (TH2*)h3->Project3D("zy");
            xaxis->SetRange(first,last);
            if (hp) {
               hp->SetFillColor(38);
               if (nbins==1)hp->SetTitle(TString::Format("ProjectionZY of binx=%d [x=%.1f..%.f]", binx,xaxis->GetBinLowEdge(binx),xaxis->GetBinUpEdge(binx)));
               else         hp->SetTitle(TString::Format("ProjectionZY, binx=[%d,%d] [x=%.1f..%.1f]", binx,binx2,xaxis->GetBinLowEdge(binx),xaxis->GetBinUpEdge(binx2)));
               hp->SetXTitle(fH->GetYaxis()->GetTitle());
               hp->SetYTitle(fH->GetZaxis()->GetTitle());
               hp->SetZTitle("Number of Entries");
               hp->Draw(fShowOption.Data());
            }
         }
         break;

   }
   c->Update();
   padsav->cd();
}
// @(#)root/hist:$Id$
// Author: Rene Brun   26/12/94

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

#ifndef ROOT_TH1
#define ROOT_TH1


//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TH1                                                                  //
//                                                                      //
// 1-Dim histogram base class.                                          //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#ifndef ROOT_TAxis
#include "TAxis.h"
#endif

#ifndef ROOT_TAttLine
#include "TAttLine.h"
#endif

#ifndef ROOT_TAttFill
#include "TAttFill.h"
#endif

#ifndef ROOT_TAttMarker
#include "TAttMarker.h"
#endif

#ifndef ROOT_TArrayC
#include "TArrayC.h"
#endif
#ifndef ROOT_TArrayS
#include "TArrayS.h"
#endif
#ifndef ROOT_TArrayI
#include "TArrayI.h"
#endif
#ifndef ROOT_TArrayF
#include "TArrayF.h"
#endif
#ifndef ROOT_TArrayD
#include "TArrayD.h"
#endif
#include "Foption.h"

#ifndef ROOT_TVectorFfwd
#include "TVectorFfwd.h"
#endif
#ifndef ROOT_TVectorDfwd
#include "TVectorDfwd.h"
#endif

#include <float.h>

class TF1;
class TH1D;
class TBrowser;
class TDirectory;
class TList;
class TCollection;
class TVirtualFFT;
class TVirtualHistPainter;

#include "TFitResultPtr.h"

class TH1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {

public: 

   // enumeration specifying type of statistics for bin errors
   enum  EBinErrorOpt { 
         kNormal = 0,    // errors with Normal (Wald) approximation: errorUp=errorLow= sqrt(N)
         kPoisson = 1 ,  // errors from Poisson interval at 68.3% (1 sigma)
         kPoisson2 = 2   // errors from Poisson interval at 95% CL (~ 2 sigma)            
   };

protected:
    Int_t         fNcells;          //number of bins(1D), cells (2D) +U/Overflows
    TAxis         fXaxis;           //X axis descriptor
    TAxis         fYaxis;           //Y axis descriptor
    TAxis         fZaxis;           //Z axis descriptor
    Short_t       fBarOffset;       //(1000*offset) for bar charts or legos
    Short_t       fBarWidth;        //(1000*width) for bar charts or legos
    Double_t      fEntries;         //Number of entries
    Double_t      fTsumw;           //Total Sum of weights
    Double_t      fTsumw2;          //Total Sum of squares of weights
    Double_t      fTsumwx;          //Total Sum of weight*X
    Double_t      fTsumwx2;         //Total Sum of weight*X*X
    Double_t      fMaximum;         //Maximum value for plotting
    Double_t      fMinimum;         //Minimum value for plotting
    Double_t      fNormFactor;      //Normalization factor
    TArrayD       fContour;         //Array to display contour levels
    TArrayD       fSumw2;           //Array of sum of squares of weights
    TString       fOption;          //histogram options
    TList        *fFunctions;       //->Pointer to list of functions (fits and user)
    Int_t         fBufferSize;      //fBuffer size
    Double_t     *fBuffer;          //[fBufferSize] entry buffer
    TDirectory   *fDirectory;       //!Pointer to directory holding this histogram
    Int_t         fDimension;       //!Histogram dimension (1, 2 or 3 dim)
    Double_t     *fIntegral;        //!Integral of bins used by GetRandom
    TVirtualHistPainter *fPainter;  //!pointer to histogram painter
    EBinErrorOpt  fBinStatErrOpt;   //option for bin statistical errors 
    static Int_t  fgBufferSize;     //!default buffer size for automatic histograms
    static Bool_t fgAddDirectory;   //!flag to add histograms to the directory
    static Bool_t fgStatOverflows;  //!flag to use under/overflows in statistics
    static Bool_t fgDefaultSumw2;   //!flag to call TH1::Sumw2 automatically at histogram creation time

public:
   static Int_t FitOptionsMake(Option_t *option, Foption_t &Foption);

private:
   Int_t   AxisChoice(Option_t *axis) const;
   void    Build();

   TH1& operator=(const TH1&); // Not implemented


protected:
   TH1();
   TH1(const char *name,const char *title,Int_t nbinsx,Double_t xlow,Double_t xup);
   TH1(const char *name,const char *title,Int_t nbinsx,const Float_t *xbins);
   TH1(const char *name,const char *title,Int_t nbinsx,const Double_t *xbins);
   virtual Int_t    BufferFill(Double_t x, Double_t w);
   virtual Bool_t   FindNewAxisLimits(const TAxis* axis, const Double_t point, Double_t& newMin, Double_t &newMax);
   virtual void     SavePrimitiveHelp(ostream &out, const char *hname, Option_t *option = "");
   static Bool_t    RecomputeAxisLimits(TAxis& destAxis, const TAxis& anAxis);
   static Bool_t    SameLimitsAndNBins(const TAxis& axis1, const TAxis& axis2);

   virtual Double_t DoIntegral(Int_t ix1, Int_t ix2, Int_t iy1, Int_t iy2, Int_t iz1, Int_t iz2, Double_t & err, 
                               Option_t * opt, Bool_t doerr = kFALSE) const;

   virtual void     DoFillN(Int_t ntimes, const Double_t *x, const Double_t *w, Int_t stride=1);

   static bool CheckAxisLimits(const TAxis* a1, const TAxis* a2);
   static bool CheckBinLimits(const TAxis* a1, const TAxis* a2);
   static bool CheckBinLabels(const TAxis* a1, const TAxis* a2);
   static bool CheckEqualAxes(const TAxis* a1, const TAxis* a2);
   static bool CheckConsistentSubAxes(const TAxis *a1, Int_t firstBin1, Int_t lastBin1, const TAxis *a2, Int_t firstBin2=0, Int_t lastBin2=0);
   static bool CheckConsistency(const TH1* h1, const TH1* h2);

public:
   // TH1 status bits
   enum {
      kNoStats     = BIT(9),  // don't draw stats box
      kUserContour = BIT(10), // user specified contour levels
      kCanRebin    = BIT(11), // can rebin axis
      kLogX        = BIT(15), // X-axis in log scale
      kIsZoomed    = BIT(16), // bit set when zooming on Y axis
      kNoTitle     = BIT(17), // don't draw the histogram title
      kIsAverage   = BIT(18), // Bin contents are average (used by Add)
      kIsHighlight = BIT(19)  // bit set if histo is highlight
   };
   // size of statistics data (size of  array used in GetStats()/ PutStats ) 
   // s[0]  = sumw       s[1]  = sumw2
   // s[2]  = sumwx      s[3]  = sumwx2                     
   // s[4]  = sumwy      s[5]  = sumwy2   s[6]  = sumwxy
   // s[7]  = sumwz      s[8]  = sumwz2   s[9]  = sumwxz   s[10]  = sumwyz  
   // s[11] = sumwt      s[12] = sumwt2                 (11 and 12 used only by TProfile3D)
   enum { 
      kNstat       = 13  // size of statistics data (up to TProfile3D)
   };



   TH1(const TH1&);
   virtual ~TH1();

   virtual Bool_t   Add(TF1 *h1, Double_t c1=1, Option_t *option="");
   virtual Bool_t   Add(const TH1 *h1, Double_t c1=1);
   virtual Bool_t   Add(const TH1 *h, const TH1 *h2, Double_t c1=1, Double_t c2=1); // *MENU*
   virtual void     AddBinContent(Int_t bin);
   virtual void     AddBinContent(Int_t bin, Double_t w);
   static  void     AddDirectory(Bool_t add=kTRUE);
   static  Bool_t   AddDirectoryStatus();
   virtual void     Browse(TBrowser *b);
   virtual Double_t Chi2Test(const TH1* h2, Option_t *option = "UU", Double_t *res = 0) const;
   virtual Double_t Chi2TestX(const TH1* h2, Double_t &chi2, Int_t &ndf, Int_t &igood,Option_t *option = "UU",  Double_t *res = 0) const;
   
   virtual Double_t Chisquare(TF1 * f1, Option_t *option = "") const;
   virtual Double_t ComputeIntegral(Bool_t onlyPositive = false);
   virtual void     Copy(TObject &hnew) const;
   virtual void     DirectoryAutoAdd(TDirectory *);
   virtual Int_t    DistancetoPrimitive(Int_t px, Int_t py);
   virtual Bool_t   Divide(TF1 *f1, Double_t c1=1);
   virtual Bool_t   Divide(const TH1 *h1);
   virtual Bool_t   Divide(const TH1 *h1, const TH1 *h2, Double_t c1=1, Double_t c2=1, Option_t *option=""); // *MENU*
   virtual void     Draw(Option_t *option="");
   virtual TH1     *DrawCopy(Option_t *option="") const;
   virtual TH1     *DrawNormalized(Option_t *option="", Double_t norm=1) const;
   virtual void     DrawPanel(); // *MENU*
   virtual Int_t    BufferEmpty(Int_t action=0);
   virtual void     Eval(TF1 *f1, Option_t *option="");
   virtual void     ExecuteEvent(Int_t event, Int_t px, Int_t py);
   virtual TH1     *FFT(TH1* h_output, Option_t *option);
   virtual Int_t    Fill(Double_t x);
   virtual Int_t    Fill(Double_t x, Double_t w);
   virtual Int_t    Fill(const char *name, Double_t w);
   virtual void     FillN(Int_t ntimes, const Double_t *x, const Double_t *w, Int_t stride=1);
   virtual void     FillN(Int_t, const Double_t *, const Double_t *, const Double_t *, Int_t) {;}
   virtual void     FillRandom(const char *fname, Int_t ntimes=5000);
   virtual void     FillRandom(TH1 *h, Int_t ntimes=5000);
   virtual Int_t    FindBin(Double_t x, Double_t y=0, Double_t z=0);
   virtual Int_t    FindFixBin(Double_t x, Double_t y=0, Double_t z=0) const;
   virtual Int_t    FindFirstBinAbove(Double_t threshold=0, Int_t axis=1) const;
   virtual Int_t    FindLastBinAbove (Double_t threshold=0, Int_t axis=1) const;
   virtual TObject *FindObject(const char *name) const;
   virtual TObject *FindObject(const TObject *obj) const;
   virtual TFitResultPtr    Fit(const char *formula ,Option_t *option="" ,Option_t *goption="", Double_t xmin=0, Double_t xmax=0); // *MENU*
   virtual TFitResultPtr    Fit(TF1 *f1 ,Option_t *option="" ,Option_t *goption="", Double_t xmin=0, Double_t xmax=0);
   virtual void     FitPanel(); // *MENU*
   TH1             *GetAsymmetry(TH1* h2, Double_t c2=1, Double_t dc2=0);
   Int_t            GetBufferLength() const {return fBuffer ? (Int_t)fBuffer[0] : 0;}
   Int_t            GetBufferSize  () const {return fBufferSize;}
   const   Double_t *GetBuffer() const {return fBuffer;}
   static  Int_t    GetDefaultBufferSize();
   virtual Double_t *GetIntegral();
   TH1             *GetCumulative(Bool_t forward = kTRUE, const char* suffix = "_cumulative") const;

   TList           *GetListOfFunctions() const { return fFunctions; }

   virtual Int_t    GetNdivisions(Option_t *axis="X") const;
   virtual Color_t  GetAxisColor(Option_t *axis="X") const;
   virtual Color_t  GetLabelColor(Option_t *axis="X") const;
   virtual Style_t  GetLabelFont(Option_t *axis="X") const;
   virtual Float_t  GetLabelOffset(Option_t *axis="X") const;
   virtual Float_t  GetLabelSize(Option_t *axis="X") const;
   virtual Style_t  GetTitleFont(Option_t *axis="X") const;
   virtual Float_t  GetTitleOffset(Option_t *axis="X") const;
   virtual Float_t  GetTitleSize(Option_t *axis="X") const;
   virtual Float_t  GetTickLength(Option_t *axis="X") const;
   virtual Float_t  GetBarOffset() const {return Float_t(0.001*Float_t(fBarOffset));}
   virtual Float_t  GetBarWidth() const  {return Float_t(0.001*Float_t(fBarWidth));}
   virtual Int_t    GetContour(Double_t *levels=0);
   virtual Double_t GetContourLevel(Int_t level) const;
   virtual Double_t GetContourLevelPad(Int_t level) const;

   virtual Int_t    GetBin(Int_t binx, Int_t biny=0, Int_t binz=0) const;
   virtual void     GetBinXYZ(Int_t binglobal, Int_t &binx, Int_t &biny, Int_t &binz) const;
   virtual Double_t GetBinCenter(Int_t bin) const;
   virtual Double_t GetBinContent(Int_t bin) const;
   virtual Double_t GetBinContent(Int_t binx, Int_t biny) const;
   virtual Double_t GetBinContent(Int_t binx, Int_t biny, Int_t binz) const;
   virtual Double_t GetBinError(Int_t bin) const;
   virtual Double_t GetBinError(Int_t binx, Int_t biny) const;
   virtual Double_t GetBinError(Int_t binx, Int_t biny, Int_t binz) const;
   virtual Double_t GetBinErrorLow(Int_t bin) const;
   virtual Double_t GetBinErrorUp(Int_t bin) const;
   virtual EBinErrorOpt  GetBinErrorOption() const { return fBinStatErrOpt; }
   virtual Double_t GetBinLowEdge(Int_t bin) const;
   virtual Double_t GetBinWidth(Int_t bin) const;
   virtual Double_t GetBinWithContent(Double_t c, Int_t &binx, Int_t firstx=0, Int_t lastx=0,Double_t maxdiff=0) const;
   virtual Double_t GetCellContent(Int_t binx, Int_t biny) const;
   virtual Double_t GetCellError(Int_t binx, Int_t biny) const;
   virtual void     GetCenter(Double_t *center) const;
   static  Bool_t   GetDefaultSumw2();
   TDirectory      *GetDirectory() const {return fDirectory;}
   virtual Double_t GetEntries() const;
   virtual Double_t GetEffectiveEntries() const;
   virtual TF1     *GetFunction(const char *name) const;
   virtual Int_t    GetDimension() const { return fDimension; }
   virtual Double_t GetKurtosis(Int_t axis=1) const;
   virtual void     GetLowEdge(Double_t *edge) const;
   virtual Double_t GetMaximum(Double_t maxval=FLT_MAX) const;
   virtual Int_t    GetMaximumBin() const;
   virtual Int_t    GetMaximumBin(Int_t &locmax, Int_t &locmay, Int_t &locmaz) const;
   virtual Double_t GetMaximumStored() const {return fMaximum;}
   virtual Double_t GetMinimum(Double_t minval=-FLT_MAX) const;
   virtual Int_t    GetMinimumBin() const;
   virtual Int_t    GetMinimumBin(Int_t &locmix, Int_t &locmiy, Int_t &locmiz) const;
   virtual Double_t GetMinimumStored() const {return fMinimum;}
   virtual Double_t GetMean(Int_t axis=1) const;
   virtual Double_t GetMeanError(Int_t axis=1) const;
   virtual Int_t    GetNbinsX() const {return fXaxis.GetNbins();}
   virtual Int_t    GetNbinsY() const {return fYaxis.GetNbins();}
   virtual Int_t    GetNbinsZ() const {return fZaxis.GetNbins();}
   virtual Double_t GetNormFactor() const {return fNormFactor;}
   virtual char    *GetObjectInfo(Int_t px, Int_t py) const;
   Option_t        *GetOption() const {return fOption.Data();}

   TVirtualHistPainter *GetPainter(Option_t *option="");

   virtual Int_t    GetQuantiles(Int_t nprobSum, Double_t *q, const Double_t *probSum=0);
   virtual Double_t GetRandom() const;
   virtual void     GetStats(Double_t *stats) const;
           Double_t GetStdDev(Int_t axis=1) const { return GetRMS(axis); }                  
           Double_t GetStdDevError(Int_t axis=1) const { return GetRMSError(axis); }
   virtual Double_t GetSumOfWeights() const;
   virtual TArrayD *GetSumw2() {return &fSumw2;}
   virtual const TArrayD *GetSumw2() const {return &fSumw2;}
   virtual Int_t    GetSumw2N() const {return fSumw2.fN;}
   virtual Double_t GetRMS(Int_t axis=1) const;
   virtual Double_t GetRMSError(Int_t axis=1) const;
   virtual Double_t GetSkewness(Int_t axis=1) const;
           TAxis   *GetXaxis() const;
           TAxis   *GetYaxis() const;
           TAxis   *GetZaxis() const;
   virtual Int_t    GetXHighlightBin() const;
   virtual Int_t    GetYHighlightBin() const;
   virtual Double_t Integral(Option_t *option="") const;
   virtual Double_t Integral(Int_t binx1, Int_t binx2, Option_t *option="") const;
   virtual Double_t IntegralAndError(Int_t binx1, Int_t binx2, Double_t & err, Option_t *option="") const;
   virtual Double_t Interpolate(Double_t x);
   virtual Double_t Interpolate(Double_t x, Double_t y);
   virtual Double_t Interpolate(Double_t x, Double_t y, Double_t z);
           Bool_t   IsBinOverflow(Int_t bin) const;
           Bool_t   IsBinUnderflow(Int_t bin) const;
   virtual Bool_t   IsHighlight() const { return TestBit(kIsHighlight); }
   virtual Double_t AndersonDarlingTest(const TH1 *h2, Option_t *option="") const;
   virtual Double_t AndersonDarlingTest(const TH1 *h2, Double_t &advalue) const;
   virtual Double_t KolmogorovTest(const TH1 *h2, Option_t *option="") const;
   virtual void     LabelsDeflate(Option_t *axis="X");
   virtual void     LabelsInflate(Option_t *axis="X");
   virtual void     LabelsOption(Option_t *option="h", Option_t *axis="X");
   virtual Long64_t Merge(TCollection *list);
   virtual Bool_t   Multiply(TF1 *h1, Double_t c1=1);
   virtual Bool_t   Multiply(const TH1 *h1);
   virtual Bool_t   Multiply(const TH1 *h1, const TH1 *h2, Double_t c1=1, Double_t c2=1, Option_t *option=""); // *MENU*
   virtual void     Paint(Option_t *option="");
   virtual void     Print(Option_t *option="") const;
   virtual void     PutStats(Double_t *stats);
   virtual TH1     *Rebin(Int_t ngroup=2, const char*newname="", const Double_t *xbins=0);  // *MENU*
   virtual TH1     *RebinX(Int_t ngroup=2, const char*newname="") { return Rebin(ngroup,newname, (Double_t*) 0); }
   virtual void     RebinAxis(Double_t x, TAxis *axis);
   virtual void     Rebuild(Option_t *option="");
   virtual void     RecursiveRemove(TObject *obj);
   virtual void     Reset(Option_t *option="");
   virtual void     ResetStats();
   virtual void     SavePrimitive(ostream &out, Option_t *option = "");
   virtual void     Scale(Double_t c1=1, Option_t *option="");
   virtual void     SetAxisColor(Color_t color=1, Option_t *axis="X");
   virtual void     SetAxisRange(Double_t xmin, Double_t xmax, Option_t *axis="X");
   virtual void     SetBarOffset(Float_t offset=0.25) {fBarOffset = Short_t(1000*offset);}
   virtual void     SetBarWidth(Float_t width=0.5) {fBarWidth = Short_t(1000*width);}
   virtual void     SetBinContent(Int_t bin, Double_t content);
   virtual void     SetBinContent(Int_t binx, Int_t biny, Double_t content);
   virtual void     SetBinContent(Int_t binx, Int_t biny, Int_t binz, Double_t content);
   virtual void     SetBinError(Int_t bin, Double_t error);
   virtual void     SetBinError(Int_t binx, Int_t biny, Double_t error);
   virtual void     SetBinError(Int_t binx, Int_t biny, Int_t binz, Double_t error);
   virtual void     SetBins(Int_t nx, Double_t xmin, Double_t xmax);
   virtual void     SetBins(Int_t nx, const Double_t *xBins);
   virtual void     SetBins(Int_t nx, Double_t xmin, Double_t xmax, Int_t ny, Double_t ymin, Double_t ymax);
   virtual void     SetBins(Int_t nx, const Double_t *xBins, Int_t ny, const Double_t *yBins);
   virtual void     SetBins(Int_t nx, Double_t xmin, Double_t xmax, Int_t ny, Double_t ymin, Double_t ymax,
                            Int_t nz, Double_t zmin, Double_t zmax);
   virtual void     SetBins(Int_t nx, const Double_t *xBins, Int_t ny, const Double_t * yBins, Int_t nz,
                            const Double_t *zBins);
   virtual void     SetBinsLength(Int_t = -1) { } //redefined in derived classes
   virtual void     SetBinErrorOption(EBinErrorOpt type) { fBinStatErrOpt = type; }
   virtual void     SetBuffer(Int_t buffersize, Option_t *option="");
   virtual void     SetCellContent(Int_t binx, Int_t biny, Double_t content);
   virtual void     SetCellError(Int_t binx, Int_t biny, Double_t content);
   virtual void     SetContent(const Double_t *content);
   virtual void     SetContour(Int_t nlevels, const Double_t *levels=0);
   virtual void     SetContourLevel(Int_t level, Double_t value);
   static  void     SetDefaultBufferSize(Int_t buffersize=1000);
   static  void     SetDefaultSumw2(Bool_t sumw2=kTRUE);
   virtual void     SetDirectory(TDirectory *dir);
   virtual void     SetEntries(Double_t n) {fEntries = n;};
   virtual void     SetError(const Double_t *error);
   virtual void     SetHighlight(Bool_t highlight = kTRUE); // *TOGGLE* *GETTER=IsHighlight
   virtual void     SetLabelColor(Color_t color=1, Option_t *axis="X");
   virtual void     SetLabelFont(Style_t font=62, Option_t *axis="X");
   virtual void     SetLabelOffset(Float_t offset=0.005, Option_t *axis="X");
   virtual void     SetLabelSize(Float_t size=0.02, Option_t *axis="X");

   virtual void     SetMaximum(Double_t maximum=-1111); // *MENU*
   virtual void     SetMinimum(Double_t minimum=-1111); // *MENU*
   virtual void     SetName(const char *name); // *MENU*
   virtual void     SetNameTitle(const char *name, const char *title);
   virtual void     SetNdivisions(Int_t n=510, Option_t *axis="X");
   virtual void     SetNormFactor(Double_t factor=1) {fNormFactor = factor;}
   virtual void     SetStats(Bool_t stats=kTRUE); // *MENU*
   virtual void     SetOption(Option_t *option=" ") {fOption = option;}
   virtual void     SetTickLength(Float_t length=0.02, Option_t *axis="X");
   virtual void     SetTitleFont(Style_t font=62, Option_t *axis="X");
   virtual void     SetTitleOffset(Float_t offset=1, Option_t *axis="X");
   virtual void     SetTitleSize(Float_t size=0.02, Option_t *axis="X");
   virtual void     SetTitle(const char *title);  // *MENU*
   virtual void     SetXTitle(const char *title) {fXaxis.SetTitle(title);}
   virtual void     SetYTitle(const char *title) {fYaxis.SetTitle(title);}
   virtual void     SetZTitle(const char *title) {fZaxis.SetTitle(title);}
   virtual TH1     *ShowBackground(Int_t niter=20, Option_t *option="same"); // *MENU*
   virtual Int_t    ShowPeaks(Double_t sigma=2, Option_t *option="", Double_t threshold=0.05); // *MENU*
   virtual void     Smooth(Int_t ntimes=1, Option_t *option=""); // *MENU*
   static  void     SmoothArray(Int_t NN, Double_t *XX, Int_t ntimes=1);
   static  void     StatOverflows(Bool_t flag=kTRUE);
   virtual void     Sumw2(Bool_t flag = kTRUE);
   void             UseCurrentStyle();
   static  TH1     *TransformHisto(TVirtualFFT *fft, TH1* h_output,  Option_t *option);

   ClassDef(TH1,7)  //1-Dim histogram base class
};

//________________________________________________________________________

class TH1C : public TH1, public TArrayC {

public:
   TH1C();
   TH1C(const char *name,const char *title,Int_t nbinsx,Double_t xlow,Double_t xup);
   TH1C(const char *name,const char *title,Int_t nbinsx,const Float_t  *xbins);
   TH1C(const char *name,const char *title,Int_t nbinsx,const Double_t *xbins);
   TH1C(const TH1C &h1c);
   virtual ~TH1C();

   virtual void     AddBinContent(Int_t bin);
   virtual void     AddBinContent(Int_t bin, Double_t w);
   virtual void     Copy(TObject &hnew) const;
   virtual TH1     *DrawCopy(Option_t *option="") const;
   virtual Double_t GetBinContent(Int_t bin) const;
   virtual Double_t GetBinContent(Int_t bin, Int_t) const {return GetBinContent(bin);}
   virtual Double_t GetBinContent(Int_t bin, Int_t, Int_t) const {return GetBinContent(bin);}
   virtual void     Reset(Option_t *option="");
   virtual void     SetBinContent(Int_t bin, Double_t content);
   virtual void     SetBinContent(Int_t bin, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinContent(Int_t bin, Int_t, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinsLength(Int_t n=-1);
           TH1C&    operator=(const TH1C &h1);
   friend  TH1C     operator*(Double_t c1, const TH1C &h1);
   friend  TH1C     operator*(const TH1C &h1, Double_t c1);
   friend  TH1C     operator+(const TH1C &h1, const TH1C &h2);
   friend  TH1C     operator-(const TH1C &h1, const TH1C &h2);
   friend  TH1C     operator*(const TH1C &h1, const TH1C &h2);
   friend  TH1C     operator/(const TH1C &h1, const TH1C &h2);

   ClassDef(TH1C,1)  //1-Dim histograms (one char per channel)
};

TH1C operator*(Double_t c1, const TH1C &h1);
inline
TH1C operator*(const TH1C &h1, Double_t c1) {return operator*(c1,h1);}
TH1C operator+(const TH1C &h1, const TH1C &h2);
TH1C operator-(const TH1C &h1, const TH1C &h2);
TH1C operator*(const TH1C &h1, const TH1C &h2);
TH1C operator/(const TH1C &h1, const TH1C &h2);

//________________________________________________________________________

class TH1S : public TH1, public TArrayS {

public:
   TH1S();
   TH1S(const char *name,const char *title,Int_t nbinsx,Double_t xlow,Double_t xup);
   TH1S(const char *name,const char *title,Int_t nbinsx,const Float_t  *xbins);
   TH1S(const char *name,const char *title,Int_t nbinsx,const Double_t *xbins);
   TH1S(const TH1S &h1s);
   virtual ~TH1S();

   virtual void     AddBinContent(Int_t bin);
   virtual void     AddBinContent(Int_t bin, Double_t w);
   virtual void     Copy(TObject &hnew) const;
   virtual TH1     *DrawCopy(Option_t *option="") const;
   virtual Double_t GetBinContent(Int_t bin) const;
   virtual Double_t GetBinContent(Int_t bin, Int_t) const {return GetBinContent(bin);}
   virtual Double_t GetBinContent(Int_t bin, Int_t, Int_t) const {return GetBinContent(bin);}
   virtual void     Reset(Option_t *option="");
   virtual void     SetBinContent(Int_t bin, Double_t content);
   virtual void     SetBinContent(Int_t bin, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinContent(Int_t bin, Int_t, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinsLength(Int_t n=-1);
           TH1S&    operator=(const TH1S &h1);
   friend  TH1S     operator*(Double_t c1, const TH1S &h1);
   friend  TH1S     operator*(const TH1S &h1, Double_t c1);
   friend  TH1S     operator+(const TH1S &h1, const TH1S &h2);
   friend  TH1S     operator-(const TH1S &h1, const TH1S &h2);
   friend  TH1S     operator*(const TH1S &h1, const TH1S &h2);
   friend  TH1S     operator/(const TH1S &h1, const TH1S &h2);

   ClassDef(TH1S,1)  //1-Dim histograms (one short per channel)
};

TH1S operator*(Double_t c1, const TH1S &h1);
inline
TH1S operator*(const TH1S &h1, Double_t c1) {return operator*(c1,h1);}
TH1S operator+(const TH1S &h1, const TH1S &h2);
TH1S operator-(const TH1S &h1, const TH1S &h2);
TH1S operator*(const TH1S &h1, const TH1S &h2);
TH1S operator/(const TH1S &h1, const TH1S &h2);

//________________________________________________________________________

class TH1I: public TH1, public TArrayI {

public:
   TH1I();
   TH1I(const char *name,const char *title,Int_t nbinsx,Double_t xlow,Double_t xup);
   TH1I(const char *name,const char *title,Int_t nbinsx,const Float_t  *xbins);
   TH1I(const char *name,const char *title,Int_t nbinsx,const Double_t *xbins);
   TH1I(const TH1I &h1i);
   virtual ~TH1I();

   virtual void     AddBinContent(Int_t bin);
   virtual void     AddBinContent(Int_t bin, Double_t w);
   virtual void     Copy(TObject &hnew) const;
   virtual TH1     *DrawCopy(Option_t *option="") const;
   virtual Double_t GetBinContent(Int_t bin) const;
   virtual Double_t GetBinContent(Int_t bin, Int_t) const {return GetBinContent(bin);}
   virtual Double_t GetBinContent(Int_t bin, Int_t, Int_t) const {return GetBinContent(bin);}
   virtual void     Reset(Option_t *option="");
   virtual void     SetBinContent(Int_t bin, Double_t content);
   virtual void     SetBinContent(Int_t bin, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinContent(Int_t bin, Int_t, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinsLength(Int_t n=-1);
           TH1I&    operator=(const TH1I &h1);
   friend  TH1I     operator*(Double_t c1, const TH1I &h1);
   friend  TH1I     operator*(const TH1I &h1, Double_t c1);
   friend  TH1I     operator+(const TH1I &h1, const TH1I &h2);
   friend  TH1I     operator-(const TH1I &h1, const TH1I &h2);
   friend  TH1I     operator*(const TH1I &h1, const TH1I &h2);
   friend  TH1I     operator/(const TH1I &h1, const TH1I &h2);

   ClassDef(TH1I,1)  //1-Dim histograms (one 32 bits integer per channel)
};

TH1I operator*(Double_t c1, const TH1I &h1);
inline
TH1I operator*(const TH1I &h1, Double_t c1) {return operator*(c1,h1);}
TH1I operator+(const TH1I &h1, const TH1I &h2);
TH1I operator-(const TH1I &h1, const TH1I &h2);
TH1I operator*(const TH1I &h1, const TH1I &h2);
TH1I operator/(const TH1I &h1, const TH1I &h2);

//________________________________________________________________________

class TH1F : public TH1, public TArrayF {

public:
   TH1F();
   TH1F(const char *name,const char *title,Int_t nbinsx,Double_t xlow,Double_t xup);
   TH1F(const char *name,const char *title,Int_t nbinsx,const Float_t  *xbins);
   TH1F(const char *name,const char *title,Int_t nbinsx,const Double_t *xbins);
   TH1F(const TVectorF &v);
   TH1F(const TH1F &h1f);
   virtual ~TH1F();

   virtual void     AddBinContent(Int_t bin) {++fArray[bin];}
   virtual void     AddBinContent(Int_t bin, Double_t w)
                                 {fArray[bin] += Float_t (w);}
   virtual void     Copy(TObject &hnew) const;
   virtual TH1     *DrawCopy(Option_t *option="") const;
   virtual Double_t GetBinContent(Int_t bin) const;
   virtual Double_t GetBinContent(Int_t bin, Int_t) const {return GetBinContent(bin);}
   virtual Double_t GetBinContent(Int_t bin, Int_t, Int_t) const {return GetBinContent(bin);}
   virtual void     Reset(Option_t *option="");
   virtual void     SetBinContent(Int_t bin, Double_t content);
   virtual void     SetBinContent(Int_t bin, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinContent(Int_t bin, Int_t, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinsLength(Int_t n=-1);
           TH1F&    operator=(const TH1F &h1);
   friend  TH1F     operator*(Double_t c1, const TH1F &h1);
   friend  TH1F     operator*(const TH1F &h1, Double_t c1);
   friend  TH1F     operator+(const TH1F &h1, const TH1F &h2);
   friend  TH1F     operator-(const TH1F &h1, const TH1F &h2);
   friend  TH1F     operator*(const TH1F &h1, const TH1F &h2);
   friend  TH1F     operator/(const TH1F &h1, const TH1F &h2);

   ClassDef(TH1F,1)  //1-Dim histograms (one float per channel)
};

TH1F operator*(Double_t c1, const TH1F &h1);
inline
TH1F operator*(const TH1F &h1, Double_t c1) {return operator*(c1,h1);}
TH1F operator+(const TH1F &h1, const TH1F &h2);
TH1F operator-(const TH1F &h1, const TH1F &h2);
TH1F operator*(const TH1F &h1, const TH1F &h2);
TH1F operator/(const TH1F &h1, const TH1F &h2);

//________________________________________________________________________

class TH1D : public TH1, public TArrayD {

public:
   TH1D();
   TH1D(const char *name,const char *title,Int_t nbinsx,Double_t xlow,Double_t xup);
   TH1D(const char *name,const char *title,Int_t nbinsx,const Float_t  *xbins);
   TH1D(const char *name,const char *title,Int_t nbinsx,const Double_t *xbins);
   TH1D(const TVectorD &v);
   TH1D(const TH1D &h1d);
   virtual ~TH1D();

   virtual void     AddBinContent(Int_t bin) {++fArray[bin];}
   virtual void     AddBinContent(Int_t bin, Double_t w)
                                 {fArray[bin] += Double_t (w);}
   virtual void     Copy(TObject &hnew) const;
   virtual TH1     *DrawCopy(Option_t *option="") const;
   virtual Double_t GetBinContent(Int_t bin) const;
   virtual Double_t GetBinContent(Int_t bin, Int_t) const {return GetBinContent(bin);}
   virtual Double_t GetBinContent(Int_t bin, Int_t, Int_t) const {return GetBinContent(bin);}
   virtual void     Reset(Option_t *option="");
   virtual void     SetBinContent(Int_t bin, Double_t content);
   virtual void     SetBinContent(Int_t bin, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinContent(Int_t bin, Int_t, Int_t, Double_t content) {SetBinContent(bin,content);}
   virtual void     SetBinsLength(Int_t n=-1);
           TH1D&    operator=(const TH1D &h1);
   friend  TH1D     operator*(Double_t c1, const TH1D &h1);
   friend  TH1D     operator*(const TH1D &h1, Double_t c1);
   friend  TH1D     operator+(const TH1D &h1, const TH1D &h2);
   friend  TH1D     operator-(const TH1D &h1, const TH1D &h2);
   friend  TH1D     operator*(const TH1D &h1, const TH1D &h2);
   friend  TH1D     operator/(const TH1D &h1, const TH1D &h2);

   ClassDef(TH1D,1)  //1-Dim histograms (one double per channel)
};

TH1D operator*(Double_t c1, const TH1D &h1);
inline
TH1D operator*(const TH1D &h1, Double_t c1) {return operator*(c1,h1);}
TH1D operator+(const TH1D &h1, const TH1D &h2);
TH1D operator-(const TH1D &h1, const TH1D &h2);
TH1D operator*(const TH1D &h1, const TH1D &h2);
TH1D operator/(const TH1D &h1, const TH1D &h2);

   extern TH1 *R__H(Int_t hid);
   extern TH1 *R__H(const char *hname);

#endif
// @(#)root/hist:$Id$
// Author: Rene Brun   26/12/94

/*************************************************************************
 * Copyright (C) 1995-2000, 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "Riostream.h"
#include "TROOT.h"
#include "TClass.h"
#include "TMath.h"
#include "THashList.h"
#include "TH1.h"
#include "TH2.h"
#include "TF2.h"
#include "TF3.h"
#include "TPluginManager.h"
#include "TVirtualPad.h"
#include "TRandom.h"
#include "TVirtualFitter.h"
#include "THLimitsFinder.h"
#include "TProfile.h"
#include "TStyle.h"
#include "TVectorF.h"
#include "TVectorD.h"
#include "TBrowser.h"
#include "TObjString.h"
#include "TError.h"
#include "TVirtualHistPainter.h"
#include "TVirtualFFT.h"
#include "TSystem.h"

#include "HFitInterface.h"
#include "Fit/DataRange.h"
#include "Fit/BinData.h"
#include "Math/GoFTest.h"
#include "Math/MinimizerOptions.h"
#include "Math/QuantFuncMathCore.h"

//______________________________________________________________________________
/* Begin_Html
<center><h2>The Histogram classes</h2></center>
ROOT supports the following histogram types:
<ul>
  <li>1-D histograms:
   <ul>
         <li>TH1C : histograms with one byte per channel.   Maximum bin content = 127
         <li>TH1S : histograms with one short per channel.  Maximum bin content = 32767
         <li>TH1I : histograms with one int per channel.    Maximum bin content = 2147483647
         <li>TH1F : histograms with one float per channel.  Maximum precision 7 digits
         <li>TH1D : histograms with one double per channel. Maximum precision 14 digits
   </ul>

  <li>2-D histograms:
   <ul>
         <li>TH2C : histograms with one byte per channel.   Maximum bin content = 127
         <li>TH2S : histograms with one short per channel.  Maximum bin content = 32767
         <li>TH2I : histograms with one int per channel.    Maximum bin content = 2147483647
         <li>TH2F : histograms with one float per channel.  Maximum precision 7 digits
         <li>TH2D : histograms with one double per channel. Maximum precision 14 digits
   </ul>

  <li>3-D histograms:
   <ul>
         <li>TH3C : histograms with one byte per channel.   Maximum bin content = 127
         <li>TH3S : histograms with one short per channel.  Maximum bin content = 32767
         <li>TH3I : histograms with one int per channel.    Maximum bin content = 2147483647
         <li>TH3F : histograms with one float per channel.  Maximum precision 7 digits
         <li>TH3D : histograms with one double per channel. Maximum precision 14 digits
   </ul>
  <li>Profile histograms: See classes  TProfile, TProfile2D and TProfile3D.
      Profile histograms are used to display the mean value of Y and its RMS
      for each bin in X. Profile histograms are in many cases an elegant
      replacement of two-dimensional histograms : the inter-relation of two
      measured quantities X and Y can always be visualized by a two-dimensional
      histogram or scatter-plot; If Y is an unknown (but single-valued)
      approximate function of X, this function is displayed by a profile
      histogram with much better precision than by a scatter-plot.
</ul>

All histogram classes are derived from the base class TH1
<pre>
                                TH1
                                 ^
                                 |
                                 |
                                 |
         -----------------------------------------------------------
                |                |       |      |      |     |     |
                |                |      TH1C   TH1S   TH1I  TH1F  TH1D
                |                |                                 |
                |                |                                 |
                |               TH2                             TProfile
                |                |
                |                |
                |                ----------------------------------
                |                        |      |      |     |     |
                |                       TH2C   TH2S   TH2I  TH2F  TH2D
                |                                                  |
               TH3                                                 |
                |                                               TProfile2D
                |
                -------------------------------------
                        |      |      |      |      |
                       TH3C   TH3S   TH3I   TH3F   TH3D
                                                    |
                                                    |
                                                 TProfile3D

      The TH*C classes also inherit from the array class TArrayC.
      The TH*S classes also inherit from the array class TArrayS.
      The TH*I classes also inherit from the array class TArrayI.
      The TH*F classes also inherit from the array class TArrayF.
      The TH*D classes also inherit from the array class TArrayD.
</pre>

<h4>Creating histograms</h4>
<p>
     Histograms are created by invoking one of the constructors, e.g.
<pre>
       TH1F *h1 = new TH1F("h1", "h1 title", 100, 0, 4.4);
       TH2F *h2 = new TH2F("h2", "h2 title", 40, 0, 4, 30, -3, 3);
</pre>
<p>  Histograms may also be created by:
  <ul>
      <li> calling the Clone function, see below
      <li> making a projection from a 2-D or 3-D histogram, see below
      <li> reading an histogram from a file
   </ul>
<p>  When an histogram is created, a reference to it is automatically added
     to the list of in-memory objects for the current file or directory.
     This default behaviour can be changed by:
<pre>
       h->SetDirectory(0);          for the current histogram h
       TH1::AddDirectory(kFALSE);   sets a global switch disabling the reference
</pre>
     When the histogram is deleted, the reference to it is removed from
     the list of objects in memory.
     When a file is closed, all histograms in memory associated with this file
     are automatically deleted.

<h4>Fix or variable bin size</h4>

     All histogram types support either fix or variable bin sizes.
     2-D histograms may have fix size bins along X and variable size bins
     along Y or vice-versa. The functions to fill, manipulate, draw or access
     histograms are identical in both cases.
<p>     Each histogram always contains 3 objects TAxis: fXaxis, fYaxis and fZaxis
     To access the axis parameters, do:
<pre>
        TAxis *xaxis = h->GetXaxis(); etc.
        Double_t binCenter = xaxis->GetBinCenter(bin), etc.
</pre>
     See class TAxis for a description of all the access functions.
     The axis range is always stored internally in double precision.

<h4>Convention for numbering bins</h4>

      For all histogram types: nbins, xlow, xup
<pre>
        bin = 0;       underflow bin
        bin = 1;       first bin with low-edge xlow INCLUDED
        bin = nbins;   last bin with upper-edge xup EXCLUDED
        bin = nbins+1; overflow bin
</pre>
<p>      In case of 2-D or 3-D histograms, a "global bin" number is defined.
      For example, assuming a 3-D histogram with (binx, biny, binz), the function
<pre>
        Int_t gbin = h->GetBin(binx, biny, binz);
</pre>
      returns a global/linearized gbin number. This global gbin is useful
      to access the bin content/error information independently of the dimension.
      Note that to access the information other than bin content and errors
      one should use the TAxis object directly with e.g.:
<pre>
         Double_t xcenter = h3->GetZaxis()->GetBinCenter(27);
</pre>
       returns the center along z of bin number 27 (not the global bin)
       in the 3-D histogram h3.

<h4>Alphanumeric Bin Labels</h4>

     By default, an histogram axis is drawn with its numeric bin labels.
     One can specify alphanumeric labels instead with:
<ul>
       <li> call TAxis::SetBinLabel(bin, label);
           This can always be done before or after filling.
           When the histogram is drawn, bin labels will be automatically drawn.
           See example in $ROOTSYS/tutorials/graphs/labels1.C, labels2.C
       <li> call to a Fill function with one of the arguments being a string, e.g.
<pre>
           hist1->Fill(somename, weigth);
           hist2->Fill(x, somename, weight);
           hist2->Fill(somename, y, weight);
           hist2->Fill(somenamex, somenamey, weight);
</pre>
           See example in $ROOTSYS/tutorials/hist/hlabels1.C, hlabels2.C
       <li> via TTree::Draw.
           see for example $ROOTSYS/tutorials/tree/cernstaff.C
<pre>
           tree.Draw("Nation::Division");
</pre>
           where "Nation" and "Division" are two branches of a Tree.
</ul>

<p>     When using the options 2 or 3 above, the labels are automatically
     added to the list (THashList) of labels for a given axis.
     By default, an axis is drawn with the order of bins corresponding
     to the filling sequence. It is possible to reorder the axis

<ul>
          <li>alphabetically
          <li>by increasing or decreasing values
</ul>

<p>     The reordering can be triggered via the TAxis context menu by selecting
     the menu item "LabelsOption" or by calling directly
        TH1::LabelsOption(option, axis) where
<ul>
          <li>axis may be "X", "Y" or "Z"
          <li>option may be:
           <ul>
             <li>"a" sort by alphabetic order
             <li>">" sort by decreasing values
             <li>"<" sort by increasing values
             <li>"h" draw labels horizontal
             <li>"v" draw labels vertical
             <li>"u" draw labels up (end of label right adjusted)
             <li>"d" draw labels down (start of label left adjusted)
           </ul>
</ul>
<p>     When using the option 2 above, new labels are added by doubling the current
     number of bins in case one label does not exist yet.
     When the Filling is terminated, it is possible to trim the number
     of bins to match the number of active labels by calling
<pre>
           TH1::LabelsDeflate(axis) with axis = "X", "Y" or "Z"
</pre>
     This operation is automatic when using TTree::Draw.
     Once bin labels have been created, they become persistent if the histogram
     is written to a file or when generating the C++ code via SavePrimitive.

<h4>Histograms with automatic bins</h4>

     When an histogram is created with an axis lower limit greater or equal
     to its upper limit, the SetBuffer is automatically called with an
     argument fBufferSize equal to fgBufferSize (default value=1000).
     fgBufferSize may be reset via the static function TH1::SetDefaultBufferSize.
     The axis limits will be automatically computed when the buffer will
     be full or when the function BufferEmpty is called.

<h4>Filling histograms</h4>

     An histogram is typically filled with statements like:
<pre>
       h1->Fill(x);
       h1->Fill(x, w); //fill with weight
       h2->Fill(x, y)
       h2->Fill(x, y, w)
       h3->Fill(x, y, z)
       h3->Fill(x, y, z, w)
</pre>
     or via one of the Fill functions accepting names described above.
     The Fill functions compute the bin number corresponding to the given
     x, y or z argument and increment this bin by the given weight.
     The Fill functions return the bin number for 1-D histograms or global
     bin number for 2-D and 3-D histograms.
<p>     If TH1::Sumw2 has been called before filling, the sum of squares of
     weights is also stored.
     One can also increment directly a bin number via TH1::AddBinContent
     or replace the existing content via TH1::SetBinContent.
     To access the bin content of a given bin, do:
<pre>
       Double_t binContent = h->GetBinContent(bin);
</pre>

<p>     By default, the bin number is computed using the current axis ranges.
     If the automatic binning option has been set via
<pre>
       h->SetBit(TH1::kCanRebin);
</pre>
     then, the Fill Function will automatically extend the axis range to
     accomodate the new value specified in the Fill argument. The method
     used is to double the bin size until the new value fits in the range,
     merging bins two by two. This automatic binning options is extensively
     used by the TTree::Draw function when histogramming Tree variables
     with an unknown range.
<p>     This automatic binning option is supported for 1-D, 2-D and 3-D histograms.

     During filling, some statistics parameters are incremented to compute
     the mean value and Root Mean Square with the maximum precision.

<p>     In case of histograms of type TH1C, TH1S, TH2C, TH2S, TH3C, TH3S
     a check is made that the bin contents do not exceed the maximum positive
     capacity (127 or 32767). Histograms of all types may have positive
     or/and negative bin contents.

<h4>Rebinning</h4>

     At any time, an histogram can be rebinned via TH1::Rebin. This function
     returns a new histogram with the rebinned contents.
     If bin errors were stored, they are recomputed during the rebinning.

<h4>Associated errors</h4>

     By default, for each bin, the sum of weights is computed at fill time.
     One can also call TH1::Sumw2 to force the storage and computation
     of the sum of the square of weights per bin.
     If Sumw2 has been called, the error per bin is computed as the
     sqrt(sum of squares of weights), otherwise the error is set equal
     to the sqrt(bin content).
     To return the error for a given bin number, do:
<pre>
        Double_t error = h->GetBinError(bin);
</pre>

<h4>Associated functions</h4>

     One or more object (typically a TF1*) can be added to the list
     of functions (fFunctions) associated to each histogram.
     When TH1::Fit is invoked, the fitted function is added to this list.
     Given an histogram h, one can retrieve an associated function
     with:
<pre>
        TF1 *myfunc = h->GetFunction("myfunc");
</pre>

<h4>Operations on histograms</h4>


     Many types of operations are supported on histograms or between histograms
<ul>
     <li> Addition of an histogram to the current histogram.
     <li> Additions of two histograms with coefficients and storage into the current
       histogram.
     <li> Multiplications and Divisions are supported in the same way as additions.
     <li> The Add, Divide and Multiply functions also exist to add, divide or multiply
       an histogram by a function.
</ul>
     If an histogram has associated error bars (TH1::Sumw2 has been called),
     the resulting error bars are also computed assuming independent histograms.
     In case of divisions, Binomial errors are also supported.
     One can mark a histogram to be an "average" histogram by setting its bit kIsAverage via
       myhist.SetBit(TH1::kIsAverage);
     When adding (see TH1::Add) average histograms, the histograms are averaged and not summed.



<h4>Fitting histograms</h4>

     Histograms (1-D, 2-D, 3-D and Profiles) can be fitted with a user
     specified function via TH1::Fit. When an histogram is fitted, the
     resulting function with its parameters is added to the list of functions
     of this histogram. If the histogram is made persistent, the list of
     associated functions is also persistent. Given a pointer (see above)
     to an associated function myfunc, one can retrieve the function/fit
     parameters with calls such as:
<pre>
       Double_t chi2 = myfunc->GetChisquare();
       Double_t par0 = myfunc->GetParameter(0); value of 1st parameter
       Double_t err0 = myfunc->GetParError(0);  error on first parameter
</pre>

<h4>Projections of histograms</h4>

<p>     One can:
<ul>
      <li> make a 1-D projection of a 2-D histogram or Profile
        see functions TH2::ProjectionX,Y, TH2::ProfileX,Y, TProfile::ProjectionX
      <li> make a 1-D, 2-D or profile out of a 3-D histogram
        see functions TH3::ProjectionZ, TH3::Project3D.
</ul>

<p>     One can fit these projections via:
<pre>
      TH2::FitSlicesX,Y, TH3::FitSlicesZ.
</pre>

<h4>Random Numbers and histograms</h4>

     TH1::FillRandom can be used to randomly fill an histogram using
                    the contents of an existing TF1 function or another
                    TH1 histogram (for all dimensions).
<p>     For example the following two statements create and fill an histogram
     10000 times with a default gaussian distribution of mean 0 and sigma 1:
<pre>
       TH1F h1("h1", "histo from a gaussian", 100, -3, 3);
       h1.FillRandom("gaus", 10000);
</pre>
     TH1::GetRandom can be used to return a random number distributed
                    according the contents of an histogram.

<h4>Making a copy of an histogram</h4>

     Like for any other ROOT object derived from TObject, one can use
     the Clone() function. This makes an identical copy of the original
     histogram including all associated errors and functions, e.g.:
<pre>
       TH1F *hnew = (TH1F*)h->Clone("hnew");
</pre>

<h4>Normalizing histograms</h4>

     One can scale an histogram such that the bins integral is equal to
     the normalization parameter via TH1::Scale(Double_t norm), where norm
     is the desired normalization divided by the integral of the histogram.

<h4>Drawing histograms</h4>

     Histograms are drawn via the THistPainter class. Each histogram has
     a pointer to its own painter (to be usable in a multithreaded program).
     Many drawing options are supported.
     See THistPainter::Paint() for more details.
<p>
    The same histogram can be drawn with different options in different pads.
     When an histogram drawn in a pad is deleted, the histogram is
     automatically removed from the pad or pads where it was drawn.
     If an histogram is drawn in a pad, then filled again, the new status
     of the histogram will be automatically shown in the pad next time
     the pad is updated. One does not need to redraw the histogram.
     To draw the current version of an histogram in a pad, one can use
<pre>
        h->DrawCopy();
</pre>
     This makes a clone (see Clone below) of the histogram. Once the clone
     is drawn, the original histogram may be modified or deleted without
     affecting the aspect of the clone.
<p>
     One can use TH1::SetMaximum() and TH1::SetMinimum() to force a particular
     value for the maximum or the minimum scale on the plot. (For 1-D
     histograms this means the y-axis, while for 2-D histograms these
     functions affect the z-axis).
<p>
     TH1::UseCurrentStyle() can be used to change all histogram graphics
     attributes to correspond to the current selected style.
     This function must be called for each histogram.
     In case one reads and draws many histograms from a file, one can force
     the histograms to inherit automatically the current graphics style
     by calling before gROOT->ForceStyle().


<h4>Setting Drawing histogram contour levels (2-D hists only)</h4>

     By default contours are automatically generated at equidistant
     intervals. A default value of 20 levels is used. This can be modified
     via TH1::SetContour() or TH1::SetContourLevel().
     the contours level info is used by the drawing options "cont", "surf",
     and "lego".

<h4>Setting histogram graphics attributes</h4>

     The histogram classes inherit from the attribute classes:
       TAttLine, TAttFill, and TAttMarker.
     See the member functions of these classes for the list of options.

<h4>Giving titles to the X, Y and Z axis</h4>
<pre>
       h->GetXaxis()->SetTitle("X axis title");
       h->GetYaxis()->SetTitle("Y axis title");
</pre>
     The histogram title and the axis titles can be any TLatex string.
     The titles are part of the persistent histogram.
     It is also possible to specify the histogram title and the axis
     titles at creation time. These titles can be given in the "title"
     parameter. They must be separated by ";":
<pre>
        TH1F* h=new TH1F("h", "Histogram title;X Axis;Y Axis;Z Axis", 100, 0, 1);
</pre>
     Any title can be omitted:
<pre>
        TH1F* h=new TH1F("h", "Histogram title;;Y Axis", 100, 0, 1);
        TH1F* h=new TH1F("h", ";;Y Axis", 100, 0, 1);
</pre>
     The method SetTitle has the same syntax:
<pre>
</pre>
        h->SetTitle("Histogram title;Another X title Axis");

<h4>Saving/Reading histograms to/from a ROOT file</h4>

     The following statements create a ROOT file and store an histogram
     on the file. Because TH1 derives from TNamed, the key identifier on
     the file is the histogram name:
<pre>
        TFile f("histos.root", "new");
        TH1F h1("hgaus", "histo from a gaussian", 100, -3, 3);
        h1.FillRandom("gaus", 10000);
        h1->Write();
</pre>
     To read this histogram in another Root session, do:
<pre>
        TFile f("histos.root");
        TH1F *h = (TH1F*)f.Get("hgaus");
</pre>
     One can save all histograms in memory to the file by:
<pre>
        file->Write();
</pre>

<h4>Miscelaneous operations</h4>

<pre>
        TH1::KolmogorovTest(): statistical test of compatibility in shape
                             between two histograms
        TH1::Smooth() smooths the bin contents of a 1-d histogram
        TH1::Integral() returns the integral of bin contents in a given bin range
        TH1::GetMean(int axis) returns the mean value along axis
        TH1::GetRMS(int axis)  returns the sigma distribution along axis
        TH1::GetEntries() returns the number of entries
        TH1::Reset() resets the bin contents and errors of an histogram
</pre>
End_Html */



TF1 *gF1=0;  //left for back compatibility (use TVirtualFitter::GetUserFunc instead)

Int_t  TH1::fgBufferSize   = 1000;
Bool_t TH1::fgAddDirectory = kTRUE;
Bool_t TH1::fgDefaultSumw2 = kFALSE;
Bool_t TH1::fgStatOverflows= kFALSE;

extern void H1InitGaus();
extern void H1InitExpo();
extern void H1InitPolynom();
extern void H1LeastSquareFit(Int_t n, Int_t m, Double_t *a);
extern void H1LeastSquareLinearFit(Int_t ndata, Double_t &a0, Double_t &a1, Int_t &ifail);
extern void H1LeastSquareSeqnd(Int_t n, Double_t *a, Int_t idim, Int_t &ifail, Int_t k, Double_t *b);

// Internal exceptions for the CheckConsistency method
class DifferentDimension: public std::exception {};
class DifferentNumberOfBins: public std::exception {};
class DifferentAxisLimits: public std::exception {};
class DifferentBinLimits: public std::exception {};
class DifferentLabels: public std::exception {};

ClassImp(TH1)

//______________________________________________________________________________
TH1::TH1(): TNamed(), TAttLine(), TAttFill(), TAttMarker()
{
//   -*-*-*-*-*-*-*-*-*Histogram default constructor*-*-*-*-*-*-*-*-*-*-*-*-*
//                     =============================
   fDirectory     = 0;
   fFunctions     = new TList;
   fNcells        = 0;
   fIntegral      = 0;
   fPainter       = 0;
   fEntries       = 0;
   fNormFactor    = 0;
   fTsumw         = fTsumw2=fTsumwx=fTsumwx2=0;
   fMaximum       = -1111;
   fMinimum       = -1111;
   fBufferSize    = 0;
   fBuffer        = 0;
   fBinStatErrOpt = kNormal;
   fXaxis.SetName("xaxis");
   fYaxis.SetName("yaxis");
   fZaxis.SetName("zaxis");
   fXaxis.SetParent(this);
   fYaxis.SetParent(this);
   fZaxis.SetParent(this);
   UseCurrentStyle();
}

//______________________________________________________________________________
TH1::~TH1()
{
//   -*-*-*-*-*-*-*-*-*Histogram default destructor*-*-*-*-*-*-*-*-*-*-*-*-*-*
//                     ============================

   if (!TestBit(kNotDeleted)) {
      return;
   }
   delete[] fIntegral;
   fIntegral = 0;
   delete[] fBuffer;
   fBuffer = 0;
   if (fFunctions) {
      fFunctions->SetBit(kInvalidObject);
      TObject* obj = 0;
      //special logic to support the case where the same object is
      //added multiple times in fFunctions.
      //This case happens when the same object is added with different
      //drawing modes
      //In the loop below we must be careful with objects (eg TCutG) that may
      // have been added to the list of functions of several histograms
      //and may have been already deleted.
      while ((obj  = fFunctions->First())) {
         while(fFunctions->Remove(obj)) { }
         if (!obj->TestBit(kNotDeleted)) {
            break;
         }
         delete obj;
         obj = 0;
      }
      delete fFunctions;
      fFunctions = 0;
   }
   if (fDirectory) {
      fDirectory->Remove(this);
      fDirectory = 0;
   }
   delete fPainter;
   fPainter = 0;
}

//______________________________________________________________________________
TH1::TH1(const char *name,const char *title,Int_t nbins,Double_t xlow,Double_t xup)
    :TNamed(name,title), TAttLine(), TAttFill(), TAttMarker()
{
//   -*-*-*-*-*-*-*Normal constructor for fix bin size histograms*-*-*-*-*-*-*
//                 ==============================================
//
//     Creates the main histogram structure:
//        name   : name of histogram (avoid blanks)
//        title  : histogram title
//                 if title is of the form "stringt;stringx;stringy;stringz"
//                 the histogram title is set to stringt,
//                 the x axis title to stringy, the y axis title to stringy, etc.
//        nbins  : number of bins
//        xlow   : low edge of first bin
//        xup    : upper edge of last bin (not included in last bin)
//
//      When an histogram is created, it is automatically added to the list
//      of special objects in the current directory.
//      To find the pointer to this histogram in the current directory
//      by its name, do:
//      TH1F *h1 = (TH1F*)gDirectory->FindObject(name);
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   Build();
   if (nbins <= 0) {Warning("TH1","nbins is <=0 - set to nbins = 1"); nbins = 1; }
   fXaxis.Set(nbins,xlow,xup);
   fNcells = fXaxis.GetNbins()+2;
}

//______________________________________________________________________________
TH1::TH1(const char *name,const char *title,Int_t nbins,const Float_t *xbins)
    :TNamed(name,title), TAttLine(), TAttFill(), TAttMarker()
{
//   -*-*-*-*-*Normal constructor for variable bin size histograms*-*-*-*-*-*-*
//             ===================================================
//
//  Creates the main histogram structure:
//     name   : name of histogram (avoid blanks)
//     title  : histogram title
//              if title is of the form "stringt;stringx;stringy;stringz"
//              the histogram title is set to stringt,
//              the x axis title to stringx, the y axis title to stringy, etc.
//     nbins  : number of bins
//     xbins  : array of low-edges for each bin
//              This is an array of size nbins+1
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
   Build();
   if (nbins <= 0) {Warning("TH1","nbins is <=0 - set to nbins = 1"); nbins = 1; }
   if (xbins) fXaxis.Set(nbins,xbins);
   else       fXaxis.Set(nbins,0,1);
   fNcells = fXaxis.GetNbins()+2;
}

//______________________________________________________________________________
TH1::TH1(const char *name,const char *title,Int_t nbins,const Double_t *xbins)
    :TNamed(name,title), TAttLine(), TAttFill(), TAttMarker()
{
//   -*-*-*-*-*Normal constructor for variable bin size histograms*-*-*-*-*-*-*
//             ===================================================
//
//  Creates the main histogram structure:
//     name   : name of histogram (avoid blanks)
//     title  : histogram title
//              if title is of the form "stringt;stringx;stringy;stringz"
//              the histogram title is set to stringt,
//              the x axis title to stringx, the y axis title to stringy, etc.
//     nbins  : number of bins
//     xbins  : array of low-edges for each bin
//              This is an array of size nbins+1
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
   Build();
   if (nbins <= 0) {Warning("TH1","nbins is <=0 - set to nbins = 1"); nbins = 1; }
   if (xbins) fXaxis.Set(nbins,xbins);
   else       fXaxis.Set(nbins,0,1);
   fNcells = fXaxis.GetNbins()+2;
}

//______________________________________________________________________________
TH1::TH1(const TH1 &h) : TNamed(), TAttLine(), TAttFill(), TAttMarker()
{
   // Copy constructor.
   // The list of functions is not copied. (Use Clone if needed)

   ((TH1&)h).Copy(*this);
}

//______________________________________________________________________________
Bool_t TH1::AddDirectoryStatus()
{
   //static function: cannot be inlined on Windows/NT
   return fgAddDirectory;
}

//______________________________________________________________________________
void TH1::Browse(TBrowser *b)
{
   // Browe the Histogram object.

   Draw(b ? b->GetDrawOption() : "");
   gPad->Update();
}


//______________________________________________________________________________
void TH1::Build()
{
//   -*-*-*-*-*-*-*-*Creates histogram basic data structure*-*-*-*-*-*-*-*-*-*
//                   ======================================

   fDirectory     = 0;
   fPainter       = 0;
   fIntegral      = 0;
   fEntries       = 0;
   fNormFactor    = 0;
   fTsumw         = fTsumw2=fTsumwx=fTsumwx2=0;
   fMaximum       = -1111;
   fMinimum       = -1111;
   fBufferSize    = 0;
   fBuffer        = 0;
   fBinStatErrOpt = kNormal;
   fXaxis.SetName("xaxis");
   fYaxis.SetName("yaxis");
   fZaxis.SetName("zaxis");
   fYaxis.Set(1,0.,1.);
   fZaxis.Set(1,0.,1.);
   fXaxis.SetParent(this);
   fYaxis.SetParent(this);
   fZaxis.SetParent(this);

   SetTitle(fTitle.Data());

   fFunctions = new TList;

   UseCurrentStyle();

   if (TH1::AddDirectoryStatus()) {
      fDirectory = gDirectory;
      if (fDirectory) {
         fDirectory->Append(this,kTRUE);
      }
   }
}

//______________________________________________________________________________
Bool_t TH1::Add(TF1 *f1, Double_t c1, Option_t *option)
{
// Performs the operation: this = this + c1*f1
// if errors are defined (see TH1::Sumw2), errors are also recalculated.
//
// By default, the function is computed at the centre of the bin.
// if option "I" is specified (1-d histogram only), the integral of the
// function in each bin is used instead of the value of the function at
// the centre of the bin.
// Only bins inside the function range are recomputed.
// IMPORTANT NOTE: If you intend to use the errors of this histogram later
// you should call Sumw2 before making this operation.
// This is particularly important if you fit the histogram after TH1::Add
//
// The function return kFALSE if the Add operation failed

   if (!f1) {
      Error("Add","Attempt to add a non-existing function");
      return kFALSE;
   }

   TString opt = option;
   opt.ToLower();
   Bool_t integral = kFALSE;
   if (opt.Contains("i") && fDimension ==1) integral = kTRUE;

   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();
   if (fDimension < 2) nbinsy = -1;
   if (fDimension < 3) nbinsz = -1;

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

//   - Add statistics
   Double_t s1[10];
   Int_t i;
   for (i=0;i<10;i++) {s1[i] = 0;}
   PutStats(s1);
   SetMinimum();
   SetMaximum();

//   - Loop on bins (including underflows/overflows)
   Int_t bin, binx, biny, binz;
   Double_t cu=0;
   Double_t xx[3];
   Double_t *params = 0;
   f1->InitArgs(xx,params);
   for (binz=0;binz<=nbinsz+1;binz++) {
      xx[2] = fZaxis.GetBinCenter(binz);
      for (biny=0;biny<=nbinsy+1;biny++) {
         xx[1] = fYaxis.GetBinCenter(biny);
         for (binx=0;binx<=nbinsx+1;binx++) {
            xx[0] = fXaxis.GetBinCenter(binx);
            if (!f1->IsInside(xx)) continue;
            TF1::RejectPoint(kFALSE);
            bin = binx +(nbinsx+2)*(biny + (nbinsy+2)*binz);
            if (integral) {
               xx[0] = fXaxis.GetBinLowEdge(binx);
               cu  = c1*f1->EvalPar(xx);
               cu += c1*f1->Integral(fXaxis.GetBinLowEdge(binx),fXaxis.GetBinUpEdge(binx))*fXaxis.GetBinWidth(binx);
            } else {
               cu  = c1*f1->EvalPar(xx);
            }
            if (TF1::RejectedPoint()) continue;
            Double_t error1 = GetBinError(bin);
            AddBinContent(bin,cu);
            if (fSumw2.fN) {
               //errors are unchanged: error on f1 assumed 0
               fSumw2.fArray[bin] = error1*error1;
            }
         }
      }
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TH1::Add(const TH1 *h1, Double_t c1)
{
// Performs the operation: this = this + c1*h1
// if errors are defined (see TH1::Sumw2), errors are also recalculated.
// Note that if h1 has Sumw2 set, Sumw2 is automatically called for this
// if not already set.
// Note also that adding histogram with labels is not supported, histogram will be
// added merging them by bin number independently of the labels.
// For adding histogram with labels one should use TH1::Merge
//
// SPECIAL CASE (Average/Efficiency histograms)
// For histograms representing averages or efficiencies, one should compute the average
// of the two histograms and not the sum. One can mark a histogram to be an average
// histogram by setting its bit kIsAverage with
//    myhist.SetBit(TH1::kIsAverage);
// Note that the two histograms must have their kIsAverage bit set
//
// IMPORTANT NOTE1: If you intend to use the errors of this histogram later
// you should call Sumw2 before making this operation.
// This is particularly important if you fit the histogram after TH1::Add
//
// IMPORTANT NOTE2: if h1 has a normalisation factor, the normalisation factor
// is used , ie  this = this + c1*factor*h1
// Use the other TH1::Add function if you do not want this feature
//
// The function return kFALSE if the Add operation failed

   if (!h1) {
      Error("Add","Attempt to add a non-existing histogram");
      return kFALSE;
   }

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();

   try {
      CheckConsistency(this,h1);
   } catch(DifferentNumberOfBins&) {
      Error("Add","Attempt to add histograms with different number of bins");
      return kFALSE;
   } catch(DifferentAxisLimits&) {
      Warning("Add","Attempt to add histograms with different axis limits");
   } catch(DifferentBinLimits&) {
      Warning("Add","Attempt to add histograms with different bin limits");
   } catch(DifferentLabels&) {
      Warning("Add","Attempt to add histograms with different labels");
   }

   if (fDimension < 2) nbinsy = -1;
   if (fDimension < 3) nbinsz = -1;

//    Create Sumw2 if h1 has Sumw2 set
   if (fSumw2.fN == 0 && h1->GetSumw2N() != 0) Sumw2();

//   - Add statistics
   Double_t entries = TMath::Abs( GetEntries() + c1 * h1->GetEntries() );

// statistics can be preserbed only in case of positive coefficients
// otherwise with negative c1 (histogram subtraction) one risks to get negative variances
   Bool_t resetStats = (c1 < 0);
   Double_t s1[kNstat] = {0};
   Double_t s2[kNstat] = {0};
   if (!resetStats) {
      // need to initialize to zero s1 and s2 since
      // GetStats fills only used elements depending on dimension and type
      GetStats(s1);
      h1->GetStats(s2);
   }

   SetMinimum();
   SetMaximum();

//   - Loop on bins (including underflows/overflows)
   Int_t bin, binx, biny, binz;
   Double_t cu;
   Double_t factor =1;
   if (h1->GetNormFactor() != 0) factor = h1->GetNormFactor()/h1->GetSumOfWeights();;
   for (binz=0;binz<=nbinsz+1;binz++) {
      for (biny=0;biny<=nbinsy+1;biny++) {
         for (binx=0;binx<=nbinsx+1;binx++) {
            bin = binx +(nbinsx+2)*(biny + (nbinsy+2)*binz);
            //special case where histograms have the kIsAverage bit set
            if (this->TestBit(kIsAverage) && h1->TestBit(kIsAverage)) {
               Double_t y1 = h1->GetBinContent(bin);
               Double_t y2 = this->GetBinContent(bin);
               Double_t e1 = h1->GetBinError(bin);
               Double_t e2 = this->GetBinError(bin);
               Double_t w1 = 1., w2 = 1.;
               // consider all special cases  when bin errors are zero
               // see http://root.cern.ch/phpBB3//viewtopic.php?f=3&t=13299
               if (e1 > 0 )
                  w1 = 1./(e1*e1);
               else if (h1->fSumw2.fN) {
                  w1 = 1.E200; // use an arbitrary huge value
                  if (y1 == 0) {
                     // use an estimated error from the global histogram scale
                     double sf = (s2[0] != 0) ? s2[1]/s2[0] : 1;
                     w1 = 1./(sf*sf);
                  }
               }
               if (e2 > 0)
                  w2 = 1./(e2*e2);
               else if (fSumw2.fN) {
                  w2 = 1.E200; // use an arbitrary huge value
                  if (y2 == 0) {
                     // use an estimated error from the global histogram scale
                     double sf = (s1[0] != 0) ? s1[1]/s1[0] : 1;
                     w2 = 1./(sf*sf);
                  }
               }
               double y =  (w1*y1 + w2*y2)/(w1 + w2);
               SetBinContent(bin, y);
               if (fSumw2.fN) {
                  double err2 =  1./(w1 + w2);
                  if (err2 < 1.E-200) err2 = 0;  // to remove arbitrary value when e1=0 AND e2=0
                  fSumw2.fArray[bin] = err2;
               }
            }
            //normal case of addition between histograms
            else {
               cu  = c1*factor*h1->GetBinContent(bin);
               AddBinContent(bin,cu);
               if (fSumw2.fN) {
                  Double_t e1 = factor*h1->GetBinError(bin);
                  fSumw2.fArray[bin] += c1*c1*e1*e1;
               }
            }
         }
      }
   }

   // update statistics (do here to avoid changes by SetBinContent)
   if (resetStats)  {
      // statistics need to be reset in case coefficient are negative
      ResetStats();
   }
   else {
      for (Int_t i=0;i<kNstat;i++) {
         if (i == 1) s1[i] += c1*c1*s2[i];
         else        s1[i] += c1*s2[i];
      }
      PutStats(s1);
      SetEntries(entries);
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TH1::Add(const TH1 *h1, const TH1 *h2, Double_t c1, Double_t c2)
{
//   -*-*-*Replace contents of this histogram by the addition of h1 and h2*-*-*
//         ===============================================================
//
//   this = c1*h1 + c2*h2
//   if errors are defined (see TH1::Sumw2), errors are also recalculated
//   Note that if h1 or h2 have Sumw2 set, Sumw2 is automatically called for this
//   if not already set.
//   Note also that adding histogram with labels is not supported, histogram will be
//   added merging them by bin number independently of the labels.
//   For adding histogram ith labels one should use TH1::Merge
//
// SPECIAL CASE (Average/Efficiency histograms)
// For histograms representing averages or efficiencies, one should compute the average
// of the two histograms and not the sum. One can mark a histogram to be an average
// histogram by setting its bit kIsAverage with
//    myhist.SetBit(TH1::kIsAverage);
// Note that the two histograms must have their kIsAverage bit set
//
// IMPORTANT NOTE: If you intend to use the errors of this histogram later
// you should call Sumw2 before making this operation.
// This is particularly important if you fit the histogram after TH1::Add
//
//ANOTHER SPECIAL CASE : h1 = h2 and c2 < 0
// do a scaling   this = c1 * h1 / (bin Volume)
//
// The function returns kFALSE if the Add operation failed


   if (!h1 || !h2) {
      Error("Add","Attempt to add a non-existing histogram");
      return kFALSE;
   }

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   Bool_t normWidth = kFALSE;
   if (h1 == h2 && c2 < 0) {c2 = 0; normWidth = kTRUE;}
   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();

   if (h1 != h2) {
      try {
         CheckConsistency(h1,h2);
         CheckConsistency(this,h1);
      } catch(DifferentNumberOfBins&) {
         Error("Add","Attempt to add histograms with different number of bins");
         return kFALSE;
      } catch(DifferentAxisLimits&) {
         Warning("Add","Attempt to add histograms with different axis limits");
      } catch(DifferentBinLimits&) {
         Warning("Add","Attempt to add histograms with different bin limits");
      } catch(DifferentLabels&) {
         Warning("Add","Attempt to add histograms with different labels");
      }
   }

   if (fDimension < 2) nbinsy = -1;
   if (fDimension < 3) nbinsz = -1;

//    Create Sumw2 if h1 or h2 have Sumw2 set
   if (fSumw2.fN == 0 && (h1->GetSumw2N() != 0 || h2->GetSumw2N() != 0)) Sumw2();

//   - Add statistics
   Double_t nEntries = TMath::Abs( c1*h1->GetEntries() + c2*h2->GetEntries() );

// statistics can be preserved only in case of positive coefficients
// otherwise with negative c1 (histogram subtraction) one risks to get negative variances
// also in case of scaling with the width we cannot preserve the statistics
   Double_t s1[kNstat] = {0};
   Double_t s2[kNstat] = {0};
   Double_t s3[kNstat];
   Bool_t resetStats = (c1*c2 < 0) || normWidth;
   if (!resetStats) {
      // need to initialize to zero s1 and s2 since
      // GetStats fills only used elements depending on dimension and type
      h1->GetStats(s1);
      h2->GetStats(s2);
      for (Int_t i=0;i<kNstat;i++) {
         if (i == 1) s3[i] = c1*c1*s1[i] + c2*c2*s2[i];
         //else        s3[i] = TMath::Abs(c1)*s1[i] + TMath::Abs(c2)*s2[i];
         else        s3[i] = c1*s1[i] + c2*s2[i];
      }
   }

   SetMinimum();
   SetMaximum();

//    Reset the kCanRebin and time display option. Otherwise SetBinContent on the overflow bin
//    would resize the axis limits!
// we need to do for only X axis since only TH1x::SetBinContent resize the axis
   Bool_t canRebin = TestBit(kCanRebin);
   if (canRebin) ResetBit(kCanRebin);

   Bool_t timeDisplayX = fXaxis.GetTimeDisplay();
   if (timeDisplayX)  fXaxis.SetTimeDisplay(0);

//   - Loop on bins (including underflows/overflows)
   Int_t bin, binx, biny, binz;
   Double_t cu;
   for (binz=0;binz<=nbinsz+1;binz++) {
      Double_t wz = h1->GetZaxis()->GetBinWidth(binz);
      for (biny=0;biny<=nbinsy+1;biny++) {
         Double_t wy = h1->GetYaxis()->GetBinWidth(biny);
         for (binx=0;binx<=nbinsx+1;binx++) {
            Double_t wx = h1->GetXaxis()->GetBinWidth(binx);
            bin = binx +(nbinsx+2)*(biny + (nbinsy+2)*binz);
            //special case where histograms have the kIsAverage bit set
            if (h1->TestBit(kIsAverage) && h2->TestBit(kIsAverage)) {
               Double_t y1 = h1->GetBinContent(bin);
               Double_t y2 = h2->GetBinContent(bin);
               Double_t e1 = h1->GetBinError(bin);
               Double_t e2 = h2->GetBinError(bin);
               Double_t w1 = 1., w2 = 1.;
               // consider all special cases  when bin errors are zero
               // see http://root.cern.ch/phpBB3//viewtopic.php?f=3&t=13299
               if (e1 > 0 )
                  w1 = 1./(e1*e1);
               else if (h1->fSumw2.fN) {
                  w1 = 1.E200; // use an arbitrary huge value
                  if (y1 == 0 ) {
                     // use an estimated error from the global histogram scale
                     double sf = (s1[0] != 0) ? s1[1]/s1[0] : 1;
                     w1 = 1./(sf*sf);
                  }
               }
               if (e2 > 0)
                  w2 = 1./(e2*e2);
               else if (h2->fSumw2.fN) {
                  w2 = 1.E200; // use an arbitrary huge value
                  if (y2 == 0) {
                     // use an estimated error from the global histogram scale
                     double sf = (s2[0] != 0) ? s2[1]/s2[0] : 1;
                     w2 = 1./(sf*sf);
                  }
               }
               double y =  (w1*y1 + w2*y2)/(w1 + w2);
               SetBinContent(bin, y);
               if (fSumw2.fN) {
                  double err2 =  1./(w1 + w2);
                  if (err2 < 1.E-200) err2 = 0;  // to remove arbitrary value when e1=0 AND e2=0
                  fSumw2.fArray[bin] = err2;
               }
            }

            // case of histogram Addition
            else {
               if (normWidth) {
                  Double_t w = wx*wy*wz;
                  cu  = c1*h1->GetBinContent(bin)/w;
                  SetBinContent(bin,cu);
                  if (fSumw2.fN) {
                     Double_t e1 = h1->GetBinError(bin)/w;
                     fSumw2.fArray[bin] = c1*c1*e1*e1;
                  }
               } else {
                  cu  = c1*h1->GetBinContent(bin)+ c2*h2->GetBinContent(bin);
                  SetBinContent(bin,cu);
                  if (fSumw2.fN) {
                     Double_t e1 = h1->GetBinError(bin);
                     Double_t e2 = h2->GetBinError(bin);
                     fSumw2.fArray[bin] = c1*c1*e1*e1 + c2*c2*e2*e2;
                  }
               }
            }
         }
      }
   }
   if (resetStats)  {
      // statistics need to be reset in case coefficient are negative
      ResetStats();
   }
   else {
      // update statistics (do here to avoid changes by SetBinContent)
      PutStats(s3);
      SetEntries(nEntries);
   }

   if (canRebin) SetBit(kCanRebin);
   if (timeDisplayX)  fXaxis.SetTimeDisplay(1);

   return kTRUE;
}


//______________________________________________________________________________
void TH1::AddBinContent(Int_t)
{
//   -*-*-*-*-*-*-*-*Increment bin content by 1*-*-*-*-*-*-*-*-*-*-*-*-*-*
//                   ==========================
   AbstractMethod("AddBinContent");
}

//______________________________________________________________________________
void TH1::AddBinContent(Int_t, Double_t)
{
//   -*-*-*-*-*-*-*-*Increment bin content by a weight w*-*-*-*-*-*-*-*-*-*-*
//                   ===================================
   AbstractMethod("AddBinContent");
}

//______________________________________________________________________________
void TH1::AddDirectory(Bool_t add)
{
// Sets the flag controlling the automatic add of histograms in memory
//
// By default (fAddDirectory = kTRUE), histograms are automatically added
// to the list of objects in memory.
// Note that one histogram can be removed from its support directory
// by calling h->SetDirectory(0) or h->SetDirectory(dir) to add it
// to the list of objects in the directory dir.
//
//  NOTE that this is a static function. To call it, use;
//     TH1::AddDirectory

   fgAddDirectory = add;
}


//______________________________________________________________________________
Int_t TH1::BufferEmpty(Int_t action)
{
// Fill histogram with all entries in the buffer.
// action = -1 histogram is reset and refilled from the buffer (called by THistPainter::Paint)
// action =  0 histogram is reset and filled from the buffer. When the histogram is filled from the
//             buffer the value fBuffer[0] is set to a negative number (= - number of entries)
//             When calling with action == 0 the histogram is NOT refilled when fBuffer[0] is < 0
//             While when calling with action = -1 the histogram is reset and ALWAYS refilled independently if
//             the histogram was filled before. This is needed when drawing the histogram
//
// action =  1 histogram is filled and buffer is deleted
//             The buffer is automatically deleted when filling the histogram and the entries is
//             larger than the buffer size
//

   // do we need to compute the bin size?
   if (!fBuffer) return 0;
   Int_t nbentries = (Int_t)fBuffer[0];

   // nbentries correspond to the number of entries of histogram

   if (nbentries == 0) return 0;
   if (nbentries < 0 && action == 0) return 0;    // case histogram has been already filled from the buffer

   Double_t *buffer = fBuffer;
   if (nbentries < 0) {
      nbentries  = -nbentries;
      //  a reset might call BufferEmpty() giving an infinite loop
      // Protect it by setting fBuffer = 0
      fBuffer=0;
       //do not reset the list of functions
      Reset("ICES");
      fBuffer = buffer;
   }
   if (TestBit(kCanRebin) || (fXaxis.GetXmax() <= fXaxis.GetXmin())) {
      //find min, max of entries in buffer
      Double_t xmin = fBuffer[2];
      Double_t xmax = xmin;
      for (Int_t i=1;i<nbentries;i++) {
         Double_t x = fBuffer[2*i+2];
         if (x < xmin) xmin = x;
         if (x > xmax) xmax = x;
      }
      if (fXaxis.GetXmax() <= fXaxis.GetXmin()) {
         THLimitsFinder::GetLimitsFinder()->FindGoodLimits(this,xmin,xmax);
      } else {
         fBuffer = 0;
         Int_t keep = fBufferSize; fBufferSize = 0;
         if (xmin <  fXaxis.GetXmin()) RebinAxis(xmin,&fXaxis);
         if (xmax >= fXaxis.GetXmax()) RebinAxis(xmax,&fXaxis);
         fBuffer = buffer;
         fBufferSize = keep;
      }
   }

   // call DoFillN which will not put entries in the buffer as FillN does
   DoFillN(nbentries,&fBuffer[2],&fBuffer[1],2);

   // if action == 1 - delete the buffer
   if (action > 0) {
      delete [] fBuffer;
      fBuffer = 0;
      fBufferSize = 0;}
   else {
      // if number of entries is consistent with buffer - set it negative to avoid
      // refilling the histogram every time BufferEmpty(0) is called
      // In case it is not consistent, by setting fBuffer[0]=0 is like resetting the buffer
      // (it will not be used anymore the next time BufferEmpty is called)
      if (nbentries == (Int_t)fEntries)
         fBuffer[0] = -nbentries;
      else
         fBuffer[0] = 0;
   }
   return nbentries;
}

//______________________________________________________________________________
Int_t TH1::BufferFill(Double_t x, Double_t w)
{
// accumulate arguments in buffer. When buffer is full, empty the buffer
// fBuffer[0] = number of entries in buffer
// fBuffer[1] = w of first entry
// fBuffer[2] = x of first entry

   if (!fBuffer) return -2;
   Int_t nbentries = (Int_t)fBuffer[0];


   if (nbentries < 0) {
      // reset nbentries to a positive value so next time BufferEmpty()  is called
      // the histogram will be refilled
      nbentries  = -nbentries;
      fBuffer[0] =  nbentries;
      if (fEntries > 0) {
         // set fBuffer to zero to avoid calling BufferEmpty in Reset
         Double_t *buffer = fBuffer; fBuffer=0;
         Reset("ICES");  // do not reset list of functions
         fBuffer = buffer;
      }
   }
   if (2*nbentries+2 >= fBufferSize) {
      BufferEmpty(1);
      return Fill(x,w);
   }
   fBuffer[2*nbentries+1] = w;
   fBuffer[2*nbentries+2] = x;
   fBuffer[0] += 1;
   return -2;
}

bool TH1::CheckBinLimits(const TAxis* a1, const TAxis * a2)
{

   const TArrayD * h1Array = a1->GetXbins();
   const TArrayD * h2Array = a2->GetXbins();
   Int_t fN = h1Array->fN;
   if ( fN != 0 ) {
      if ( h2Array->fN != fN ) {
         throw DifferentBinLimits();
         return false;
      }
      else {
         for ( int i = 0; i < fN; ++i ) {
            if ( ! TMath::AreEqualRel( h1Array->GetAt(i), h2Array->GetAt(i), 1E-10 ) ) {
               throw DifferentBinLimits();
               return false;
            }
         }
      }
   }

   return true;
}

bool TH1::CheckBinLabels(const TAxis* a1, const TAxis * a2)
{
   // check that axis have same labels
   THashList *l1 = (const_cast<TAxis*>(a1))->GetLabels();
   THashList *l2 = (const_cast<TAxis*>(a2))->GetLabels();

   if (!l1 && !l2 )
      return true;
   if (!l1 ||  !l2 ) {
      throw DifferentLabels();
      return false;
   }
   // check now labels sizes  are the same
   if (l1->GetSize() != l2->GetSize() ) {
      throw DifferentLabels();
      return false;
   }
   for (int i = 1; i <= a1->GetNbins(); ++i) {
      TString label1 = a1->GetBinLabel(i);
      TString label2 = a2->GetBinLabel(i);
      if (label1 != label2) {
         throw DifferentLabels();
         return false;
      }
   }

   return true;
}


bool TH1::CheckAxisLimits(const TAxis *a1, const TAxis *a2 )
{
   // Check that the axis limits of the histograms are the same
   // if a first and last bin is passed the axis is compared between the given range

   if ( ! TMath::AreEqualRel(a1->GetXmin(), a2->GetXmin(),1.E-12) ||
        ! TMath::AreEqualRel(a1->GetXmax(), a2->GetXmax(),1.E-12) ) {
      throw DifferentAxisLimits();
      return false;
   }
   return true;
}

bool TH1::CheckEqualAxes(const TAxis *a1, const TAxis *a2 )
{
   // Check that the axis are the same
   if (a1->GetNbins() != a2->GetNbins() ) {
      //throw DifferentNumberOfBins();
      ::Info("CheckEqualAxes","Axes have different number of bins : nbin1 = %d nbin2 = %d",a1->GetNbins(),a2->GetNbins() );
      return false;
   }
   try {
      CheckAxisLimits(a1,a2);
   } catch (DifferentAxisLimits&) {
      ::Info("CheckEqualAxes","Axes have different limits");
      return false;
   }
   try {
      CheckBinLimits(a1,a2);
   } catch (DifferentBinLimits&) {
      ::Info("CheckEqualAxes","Axes have different bin limits");
      return false;
   }

   // check labels
   try {
      CheckBinLabels(a1,a2);
   } catch (DifferentLabels&) {
      ::Info("CheckEqualAxes","Axes have different labels");
      return false;
   }

   return true;
}

bool TH1::CheckConsistentSubAxes(const TAxis *a1, Int_t firstBin1, Int_t lastBin1, const TAxis * a2, Int_t firstBin2, Int_t lastBin2 )
{
   // Check that two sub axis are the same
   // the limits are defined by first bin and last bin
   // N.B. no check is done in this case for variable bins

   // By default is assumed that no bins are given for the second axis
   Int_t nbins1   = lastBin1-firstBin1 + 1;
   Double_t xmin1 = a1->GetBinLowEdge(firstBin1);
   Double_t xmax1 = a1->GetBinUpEdge(lastBin1);

   Int_t nbins2 = a2->GetNbins();
   Double_t xmin2 = a2->GetXmin();
   Double_t xmax2 = a2->GetXmax();

   if (firstBin2 <  lastBin2) {
      // in this case assume no bins are given for the second axis
      nbins2   = lastBin1-firstBin1 + 1;
      xmin2 = a1->GetBinLowEdge(firstBin1);
      xmax2 = a1->GetBinUpEdge(lastBin1);
   }

   if (nbins1 != nbins2 ) {
      ::Info("CheckConsistentSubAxes","Axes have different number of bins");
      return false;
   }

   if ( ! TMath::AreEqualRel(xmin1,xmin2,1.E-12) ||
        ! TMath::AreEqualRel(xmax1,xmax2,1.E-12) ) {
      ::Info("CheckConsistentSubAxes","Axes have different limits");
      return false;
   }

   return true;
}

//___________________________________________________________________________
bool TH1::CheckConsistency(const TH1* h1, const TH1* h2)
{
   // Check histogram compatibility
   if (h1 == h2) return true;

   if (h1->GetDimension() != h2->GetDimension() ) { 
      throw DifferentDimension();
      return false;
   }
   Int_t dim = h1->GetDimension(); 
 
   // returns kTRUE if number of bins and bin limits are identical
   Int_t nbinsx = h1->GetNbinsX();
   Int_t nbinsy = h1->GetNbinsY();
   Int_t nbinsz = h1->GetNbinsZ();

   // Check whether the histograms have the same number of bins.
   if (nbinsx != h2->GetNbinsX() || 
       (dim > 1 && nbinsy != h2->GetNbinsY())  || 
       (dim > 2 && nbinsz != h2->GetNbinsZ()) ) {
      throw DifferentNumberOfBins();
      return false;
   }

   bool ret = true;

   // check axis limits
   ret &= CheckAxisLimits(h1->GetXaxis(), h2->GetXaxis());
   if (dim > 1) ret &= CheckAxisLimits(h1->GetYaxis(), h2->GetYaxis());
   if (dim > 2) ret &= CheckAxisLimits(h1->GetZaxis(), h2->GetZaxis());

   // check bin limits
   ret &= CheckBinLimits(h1->GetXaxis(), h2->GetXaxis());
   if (dim > 1) ret &= CheckBinLimits(h1->GetYaxis(), h2->GetYaxis());
   if (dim > 2) ret &= CheckBinLimits(h1->GetZaxis(), h2->GetZaxis());

   // check labels if histograms are both not empty
   if ( (h1->fTsumw != 0 || h1->GetEntries() != 0) &&
        (h2->fTsumw != 0 || h2->GetEntries() != 0) ) {
      ret &= CheckBinLabels(h1->GetXaxis(), h2->GetXaxis());
      if (dim > 1) ret &= CheckBinLabels(h1->GetYaxis(), h2->GetYaxis());
      if (dim > 2) ret &= CheckBinLabels(h1->GetZaxis(), h2->GetZaxis());
   }

   return ret;
}

//___________________________________________________________________________
Double_t TH1::Chi2Test(const TH1* h2, Option_t *option, Double_t *res) const
{
   // Begin_Latex #chi^{2} End_Latex test for comparing weighted and unweighted histograms
   //
   // Function: Returns p-value. Other return values are specified by the 3rd parameter <br>
   //
   // Parameters:
   //
   //    - h2: the second histogram
   //    - option:
   //       o "UU" = experiment experiment comparison (unweighted-unweighted)
   //       o "UW" = experiment MC comparison (unweighted-weighted). Note that
   //          the first histogram should be unweighted
   //       o "WW" = MC MC comparison (weighted-weighted)
   //       o "NORM" = to be used when one or both of the histograms is scaled
   //                  but the histogram originally was unweighted
   //       o by default underflows and overlows are not included:
   //          * "OF" = overflows included
   //          * "UF" = underflows included
   //       o "P" = print chi2, ndf, p_value, igood
   //       o "CHI2" = returns chi2 instead of p-value
   //       o "CHI2/NDF" = returns Begin_Latex #chi^{2}/ndf End_Latex
   //    - res: not empty - computes normalized residuals and returns them in
   //      this array
   //
   // The current implementation is based on the papers Begin_Latex #chi^{2} End_Latex test for comparison
   // of weighted and unweighted histograms" in Proceedings of PHYSTAT05 and
   // "Comparison weighted and unweighted histograms", arXiv:physics/0605123
   // by N.Gagunashvili. This function has been implemented by Daniel Haertl in August 2006.
   //
   // Introduction:
   //
   //   A frequently used technique in data analysis is the comparison of
   //   histograms. First suggested by Pearson [1] the Begin_Latex #chi^{2} End_Latex test of
   //   homogeneity is used widely for comparing usual (unweighted) histograms.
   //   This paper describes the implementation modified Begin_Latex #chi^{2} End_Latex tests
   //   for comparison of weighted and unweighted  histograms and two weighted
   //   histograms [2] as well as usual Pearson's Begin_Latex #chi^{2} End_Latex test for
   //   comparison two usual (unweighted) histograms.
   //
   // Overview:
   //
   //   Comparison of two histograms expect hypotheses that two histograms
   //   represent identical distributions. To make a decision p-value should
   //   be calculated. The hypotheses of identity is rejected if the p-value is
   //   lower then some significance level. Traditionally significance levels
   //   0.1, 0.05 and 0.01 are used. The comparison procedure should include an
   //   analysis of the residuals which is often helpful in identifying the
   //   bins of histograms responsible for a significant overall Begin_Latex #chi^{2} End_Latex value.
   //   Residuals are the difference between bin contents and expected bin
   //   contents. Most convenient for analysis are the normalized residuals. If
   //   hypotheses of identity are valid then normalized residuals are
   //   approximately independent and identically distributed random variables
   //   having N(0,1) distribution. Analysis of residuals expect test of above
   //   mentioned properties of residuals. Notice that indirectly the analysis
   //   of residuals increase the power of Begin_Latex #chi^{2} End_Latex test.
   //
   // Methods of comparison:
   //
   //  Begin_Latex #chi^{2} End_Latex test for comparison two (unweighted) histograms:
   //   Let us consider two  histograms with the  same binning and the  number
   //   of bins equal to r. Let us denote the number of events in the ith bin
   //   in the first histogram as ni and as mi in the second one. The total
   //   number of events in the first histogram is equal to:
   //Begin_Latex
   //   N = #sum_{i=1}^{r} n_{i}
   //End_Latex
   //   and
   //Begin_Latex
   //   M = #sum_{i=1}^{r} m_{i}
   //End_Latex
   //   in the second histogram. The hypothesis of identity (homogeneity) [3]
   //   is that the two histograms represent random values with identical
   //   distributions. It is equivalent that there exist r constants p1,...,pr,
   //   such that
   //Begin_Latex
   //   #sum_{i=1}^{r} p_{i}=1
   //End_Latex
   //    and the probability of belonging to the ith bin for some measured value
   //    in both experiments is equal to pi. The number of events in the ith
   //    bin is a random variable with a distribution approximated by a Poisson
   //    probability distribution
   //Begin_Latex
   //   #frac{e^{-Np_{i}}(Np_{i})^{n_{i}}}{n_{i}!}
   //End_Latex
   //   for the first histogram and with distribution
   //Begin_Latex
   //   #frac{e^{-Mp_{i}}(Mp_{i})^{m_{i}}}{m_{i}!}
   //End_Latex
   //   for the second histogram. If the hypothesis of homogeneity is valid,
   //   then the  maximum likelihood estimator of pi, i=1,...,r, is
   //Begin_Latex
   //   #hat{p}_{i}= #frac{n_{i}+m_{i}}{N+M}
   //End_Latex
   //   and then
   //Begin_Latex
   //   X^{2} = #sum_{i=1}^{r}#frac{(n_{i}-N#hat{p}_{i})^{2}}{N#hat{p}_{i}} + #sum_{i=1}^{r}#frac{(m_{i}-M#hat{p}_{i})^{2}}{M#hat{p}_{i}} = #frac{1}{MN} #sum_{i=1}^{r}#frac{(Mn_{i}-Nm_{i})^{2}}{n_{i}+m_{i}}
   //End_Latex
   //   has approximately a Begin_Latex #chi^{2}_{(r-1)} End_Latex distribution [3].
   //   The comparison procedure can include an analysis of the residuals which
   //   is often helpful in identifying the bins of histograms responsible for
   //   a significant overall Begin_Latex #chi^{2} End_Latexvalue. Most convenient for
   //   analysis are the adjusted (normalized) residuals [4]
   //Begin_Latex
   //   r_{i} = #frac{n_{i}-N#hat{p}_{i}}{#sqrt{N#hat{p}_{i}}#sqrt{(1-N/(N+M))(1-(n_{i}+m_{i})/(N+M))}}
   //End_Latex
   //   If hypotheses of  homogeneity are valid then residuals ri are
   //   approximately independent and identically distributed random variables
   //   having N(0,1) distribution. The application of the Begin_Latex #chi^{2} End_latex test has
   //   restrictions related to the value of the expected frequencies Npi,
   //   Mpi, i=1,...,r. A conservative rule formulated in [5] is that all the
   //   expectations must be 1 or greater for both histograms. In practical
   //   cases when expected frequencies are not known the estimated expected
   //   frequencies Begin_Latex M#hat{p}_{i}, N#hat{p}_{i}, i=1,...,r End_Latex  can be used.
   //
   //  Unweighted and weighted histograms comparison:
   //
   //   A simple modification of the ideas described above can be used for the
   //   comparison of the usual (unweighted) and weighted histograms. Let us
   //   denote the number of events in the ith bin in the unweighted
   //   histogram as ni and the common weight of events in the ith bin of the
   //   weighted histogram as wi. The total number of events in the
   //   unweighted histogram is equal to
   //Begin_Latex
   //   N = #sum_{i=1}^{r} n_{i}
   //End_Latex
   //   and the total weight of events in the weighted histogram is equal to
   //Begin_Latex
   //   W = #sum_{i=1}^{r} w_{i}
   //End_Latex
   //   Let us formulate the hypothesis of identity of an unweighted histogram
   //   to a weighted histogram so that there exist r constants p1,...,pr, such
   //   that
   //Begin_Latex
   //   #sum_{i=1}^{r} p_{i} = 1
   //End_Latex
   //   for the unweighted histogram. The weight wi is a random variable with a
   //   distribution approximated by the normal probability distribution
   //   Begin_Latex N(Wp_{i},#sigma_{i}^{2}) End_Latex where Begin_Latex #sigma_{i}^{2} End_Latex is the variance of the weight wi.
   //   If we replace the variance Begin_Latex #sigma_{i}^{2} End_Latex
   //   with estimate Begin_Latex s_{i}^{2} End_Latex (sum of squares of weights of
   //   events in the ith bin) and the hypothesis of identity is valid, then the
   //   maximum likelihood estimator of  pi,i=1,...,r, is
   //Begin_Latex
   //   #hat{p}_{i} = #frac{Ww_{i}-Ns_{i}^{2}+#sqrt{(Ww_{i}-Ns_{i}^{2})^{2}+4W^{2}s_{i}^{2}n_{i}}}{2W^{2}}
   //End_Latex
   //   We may then use the test statistic
   //Begin_Latex
   //   X^{2} = #sum_{i=1}^{r} #frac{(n_{i}-N#hat{p}_{i})^{2}}{N#hat{p}_{i}} + #sum_{i=1}^{r} #frac{(w_{i}-W#hat{p}_{i})^{2}}{s_{i}^{2}}
   //End_Latex
   //   and it has approximately a Begin_Latex #chi^{2}_{(r-1)} End_Latex distribution [2]. This test, as well
   //   as the original one [3], has a restriction on the expected frequencies. The
   //   expected frequencies recommended for the weighted histogram is more than 25.
   //   The value of the minimal expected frequency can be decreased down to 10 for
   //   the case when the weights of the events are close to constant. In the case
   //   of a weighted histogram if the number of events is unknown, then we can
   //   apply this recommendation for the equivalent number of events as
   //Begin_Latex
   //   n_{i}^{equiv} = #frac{ w_{i}^{2} }{ s_{i}^{2} }
   //End_Latex
   //   The minimal expected frequency for an unweighted histogram must be 1. Notice
   //   that any usual (unweighted) histogram can be considered as a weighted
   //   histogram with events that have constant weights equal to 1.
   //   The variance Begin_Latex z_{i}^{2} End_Latex of the difference between the weight wi
   //   and the estimated expectation value of the weight is approximately equal to:
   //Begin_Latex
   //   z_{i}^{2} = Var(w_{i}-W#hat{p}_{i}) = N#hat{p}_{i}(1-N#hat{p}_{i})#left(#frac{Ws_{i}^{2}}{#sqrt{(Ns_{i}^{2}-w_{i}W)^{2}+4W^{2}s_{i}^{2}n_{i}}}#right)^{2}+#frac{s_{i}^{2}}{4}#left(1+#frac{Ns_{i}^{2}-w_{i}W}{#sqrt{(Ns_{i}^{2}-w_{i}W)^{2}+4W^{2}s_{i}^{2}n_{i}}}#right)^{2}
   //End_Latex
   //   The  residuals
   //Begin_Latex
   //   r_{i} = #frac{w_{i}-W#hat{p}_{i}}{z_{i}}
   //End_Latex
   //   have approximately a normal distribution with mean equal to 0 and standard
   //   deviation  equal to 1.
   //
   //  Two weighted histograms comparison:
   //
   //   Let us denote the common  weight of events of the ith bin in the first
   //   histogram as w1i and as w2i in the second one. The total weight of events
   //   in the first histogram is equal to
   //Begin_Latex
   //   W_{1} = #sum_{i=1}^{r} w_{1i}
   //End_Latex
   //   and
   //Begin_Latex
   //   W_{2} = #sum_{i=1}^{r} w_{2i}
   //End_Latex
   //   in the second histogram. Let us formulate the hypothesis of identity of
   //   weighted histograms so that there exist r constants p1,...,pr, such that
   //Begin_Latex
   //   #sum_{i=1}^{r} p_{i} = 1
   //End_Latex
   //   and also expectation value of weight w1i equal to W1pi and expectation value
   //   of weight w2i equal to W2pi. Weights in both the histograms are random
   //   variables with distributions which can be approximated by a normal
   //   probability distribution Begin_Latex N(W_{1}p_{i},#sigma_{1i}^{2}) End_Latex for the first histogram
   //   and by a distribution Begin_Latex N(W_{2}p_{i},#sigma_{2i}^{2}) End_Latex for the second.
   //   Here Begin_Latex #sigma_{1i}^{2} End_Latex and Begin_Latex #sigma_{2i}^{2} End_Latex are the variances
   //   of w1i and w2i with estimators Begin_Latex s_{1i}^{2} End_Latex and Begin_Latex s_{2i}^{2} End_Latex respectively.
   //   If the hypothesis of identity is valid, then the maximum likelihood and
   //   Least Square Method estimator of pi,i=1,...,r, is
   //Begin_Latex
   //   #hat{p}_{i} = #frac{w_{1i}W_{1}/s_{1i}^{2}+w_{2i}W_{2} /s_{2i}^{2}}{W_{1}^{2}/s_{1i}^{2}+W_{2}^{2}/s_{2i}^{2}}
   //End_Latex
   //   We may then use the test statistic
   //Begin_Latex
   //   X^{2} = #sum_{i=1}^{r} #frac{(w_{1i}-W_{1}#hat{p}_{i})^{2}}{s_{1i}^{2}} + #sum_{i=1}^{r} #frac{(w_{2i}-W_{2}#hat{p}_{i})^{2}}{s_{2i}^{2}} = #sum_{i=1}^{r} #frac{(W_{1}w_{2i}-W_{2}w_{1i})^{2}}{W_{1}^{2}s_{2i}^{2}+W_{2}^{2}s_{1i}^{2}}
   //End_Latex
   //   and it has approximately a Begin_Latex #chi^{2}_{(r-1)} End_Latex distribution [2].
   //   The normalized or studentised residuals [6]
   //Begin_Latex
   //   r_{i} = #frac{w_{1i}-W_{1}#hat{p}_{i}}{s_{1i}#sqrt{1 - #frac{1}{(1+W_{2}^{2}s_{1i}^{2}/W_{1}^{2}s_{2i}^{2})}}}
   //End_Latex
   //   have approximately a normal distribution with mean equal to 0 and standard
   //   deviation 1. A recommended minimal expected frequency is equal to 10 for
   //   the proposed test.
   //
   // Numerical examples:
   //
   //   The method described herein is now illustrated with an example.
   //   We take a distribution
   //Begin_Latex
   //   #phi(x) = #frac{2}{(x-10)^{2}+1} + #frac{1}{(x-14)^{2}+1}       (1)
   //End_Latex
   //   defined on the interval [4,16]. Events distributed according to the formula
   //   (1) are simulated to create the unweighted histogram. Uniformly distributed
   //   events are simulated for the weighted histogram with weights calculated by
   //   formula (1). Each histogram has the same number of bins: 20. Fig.1 shows
   //   the result of comparison of the unweighted histogram with 200 events
   //   (minimal expected frequency equal to one) and the weighted histogram with
   //   500 events (minimal expected frequency equal to 25)
   //Begin_Macro
   // ../../../tutorials/math/chi2test.C
   //End_Macro
   //   Fig 1. An example of comparison of the unweighted histogram with 200 events
   //   and the weighted histogram with 500 events:
   //      a) unweighted histogram;
   //      b) weighted histogram;
   //      c) normalized residuals plot;
   //      d) normal Q-Q plot of residuals.
   //
   //   The value of the test statistic Begin_Latex #chi^{2} End_Latex is equal to
   //   21.09 with p-value equal to 0.33, therefore the hypothesis of identity of
   //   the two histograms can be accepted for 0.05 significant level. The behavior
   //   of the normalized residuals plot (see Fig. 1c) and the normal Q-Q plot
   //   (see Fig. 1d) of residuals are regular and we cannot identify the outliers
   //   or bins with a big influence on Begin_Latex #chi^{2} End_Latex.
   //
   //   The second example presents the same two histograms but 17 events was added
   //   to content of bin number 15 in unweighted histogram. Fig.2 shows the result
   //   of comparison of the unweighted histogram with 217 events (minimal expected
   //   frequency equal to one) and the weighted histogram with 500 events (minimal
   //   expected frequency equal to 25)
   //Begin_Macro
   // ../../../tutorials/math/chi2test.C(17)
   //End_Macro
   //   Fig 2. An example of comparison of the unweighted histogram with 217 events
   //   and the weighted histogram with 500 events:
   //      a) unweighted histogram;
   //      b) weighted histogram;
   //      c) normalized residuals plot;
   //      d) normal Q-Q plot of residuals.
   //
   //   The value of the test statistic Begin_Latex #chi^{2} End_Latex is equal to
   //   32.33 with p-value equal to 0.029, therefore the hypothesis of identity of
   //   the two histograms is rejected for 0.05 significant level. The behavior of
   //   the normalized residuals plot (see Fig. 2c) and the normal Q-Q plot (see
   //   Fig. 2d) of residuals are not regular and we can identify the outlier or
   //   bin with a big influence on Begin_Latex #chi^{2} End_Latex.
   //
   // References:
   //
   // [1] Pearson, K., 1904. On the Theory of Contingency and Its Relation to
   //     Association and Normal Correlation. Drapers' Co. Memoirs, Biometric
   //     Series No. 1, London.
   // [2] Gagunashvili, N., 2006. Begin_Latex #chi^{2} End_Latex test for comparison
   //     of weighted and unweighted histograms. Statistical Problems in Particle
   //     Physics, Astrophysics and Cosmology, Proceedings of PHYSTAT05,
   //     Oxford, UK, 12-15 September 2005, Imperial College Press, London, 43-44.
   //     Gagunashvili,N., Comparison of weighted and unweighted histograms,
   //     arXiv:physics/0605123, 2006.
   // [3] Cramer, H., 1946. Mathematical methods of statistics.
   //     Princeton University Press, Princeton.
   // [4] Haberman, S.J., 1973. The analysis of residuals in cross-classified tables.
   //     Biometrics 29, 205-220.
   // [5] Lewontin, R.C. and Felsenstein, J., 1965. The robustness of homogeneity
   //     test in 2xN tables. Biometrics 21, 19-33.
   // [6] Seber, G.A.F., Lee, A.J., 2003, Linear Regression Analysis.
   //     John Wiley & Sons Inc., New York.

   Double_t chi2 = 0;
   Int_t ndf = 0, igood = 0;

   TString opt = option;
   opt.ToUpper();

   Double_t prob = Chi2TestX(h2,chi2,ndf,igood,option,res);

   if(opt.Contains("P")) {
      printf("Chi2 = %f, Prob = %g, NDF = %d, igood = %d\n", chi2,prob,ndf,igood);
   }
   if(opt.Contains("CHI2/NDF")) {
      if (ndf == 0) return 0;
      return chi2/ndf;
   }
   if(opt.Contains("CHI2")) {
      return chi2;
   }

   return prob;
}

//___________________________________________________________________________
Double_t TH1::Chi2TestX(const TH1* h2,  Double_t &chi2, Int_t &ndf, Int_t &igood, Option_t *option,  Double_t *res) const
{
   // The computation routine of the Chisquare test. For the method description,
   // see Chi2Test() function.
   // Returns p-value
   // parameters:
   //  - h2-second histogram
   //  - option:
   //     "UU" = experiment experiment comparison (unweighted-unweighted)
   //     "UW" = experiment MC comparison (unweighted-weighted). Note that the first
   //           histogram should be unweighted
   //     "WW" = MC MC comparison (weighted-weighted)
   //
   //     "NORM" = if one or both histograms is scaled
   //
   //     "OF" = overflows included
   //     "UF" = underflows included
   //         by default underflows and overflows are not included
   //
   //  - igood:
   //       igood=0 - no problems
   //        For unweighted unweighted  comparison
   //       igood=1'There is a bin in the 1st histogram with less than 1 event'
   //       igood=2'There is a bin in the 2nd histogram with less than 1 event'
   //       igood=3'when the conditions for igood=1 and igood=2 are satisfied'
   //        For  unweighted weighted  comparison
   //       igood=1'There is a bin in the 1st histogram with less then 1 event'
   //       igood=2'There is a bin in the 2nd histogram with less then 10 effective number of events'
   //       igood=3'when the conditions for igood=1 and igood=2 are satisfied'
   //        For  weighted weighted  comparison
   //       igood=1'There is a bin in the 1st  histogram with less then 10 effective
   //        number of events'
   //       igood=2'There is a bin in the 2nd  histogram with less then 10 effective
   //               number of events'
   //       igood=3'when the conditions for igood=1 and igood=2 are satisfied'
   //
   //  - chi2 - chisquare of the test
   //  - ndf  - number of degrees of freedom (important, when both histograms have the same
   //         empty bins)
   //  - res -  normalized residuals for further analysis


   Int_t i = 0, j=0, k = 0;
   Int_t i_start, i_end;
   Int_t j_start, j_end;
   Int_t k_start, k_end;

   Double_t bin1, bin2;
   Double_t err1,err2;
   Double_t sum1=0, sum2=0;
   Double_t sumw1=0, sumw2=0;


   chi2 = 0;
   ndf = 0;

   TString opt = option;
   opt.ToUpper();

   TAxis *xaxis1 = this->GetXaxis();
   TAxis *xaxis2 = h2->GetXaxis();
   TAxis *yaxis1 = this->GetYaxis();
   TAxis *yaxis2 = h2->GetYaxis();
   TAxis *zaxis1 = this->GetZaxis();
   TAxis *zaxis2 = h2->GetZaxis();
   
   Int_t nbinx1 = xaxis1->GetNbins();
   Int_t nbinx2 = xaxis2->GetNbins();
   Int_t nbiny1 = yaxis1->GetNbins();
   Int_t nbiny2 = yaxis2->GetNbins();
   Int_t nbinz1 = zaxis1->GetNbins();
   Int_t nbinz2 = zaxis2->GetNbins();

   //check dimensions
   if (this->GetDimension() != h2->GetDimension() ){
      Error("Chi2TestX","Histograms have different dimensions.");
      return 0;
   }

   //check number of channels
   if (nbinx1 != nbinx2) {
      Error("Chi2TestX","different number of x channels");
   }
   if (nbiny1 != nbiny2) {
      Error("Chi2TestX","different number of y channels");
   }
   if (nbinz1 != nbinz2) {
      Error("Chi2TestX","different number of z channels");
   }

   //check for ranges
   i_start = j_start = k_start = 1;
   i_end = nbinx1;
   j_end = nbiny1;
   k_end = nbinz1;

   if (xaxis1->TestBit(TAxis::kAxisRange)) {
      i_start = xaxis1->GetFirst();
      i_end   = xaxis1->GetLast();
   }
   if (yaxis1->TestBit(TAxis::kAxisRange)) {
      j_start = yaxis1->GetFirst();
      j_end   = yaxis1->GetLast();
   }
   if (zaxis1->TestBit(TAxis::kAxisRange)) {
      k_start = zaxis1->GetFirst();
      k_end   = zaxis1->GetLast();
   }


   if (opt.Contains("OF")) {
      if (this->GetDimension() == 3) k_end = ++nbinz1;
      if (this->GetDimension() >= 2) j_end = ++nbiny1;
      if (this->GetDimension() >= 1) i_end = ++nbinx1;
   }

   if (opt.Contains("UF")) {
      if (this->GetDimension() == 3) k_start = 0;
      if (this->GetDimension() >= 2) j_start = 0;
      if (this->GetDimension() >= 1) i_start = 0;
   }

   ndf = (i_end - i_start + 1)*(j_end - j_start + 1)*(k_end - k_start + 1) - 1;

   Bool_t comparisonUU = opt.Contains("UU");
   Bool_t comparisonUW = opt.Contains("UW");
   Bool_t comparisonWW = opt.Contains("WW");
   Bool_t scaledHistogram  = opt.Contains("NORM");
   if (scaledHistogram && !comparisonUU) {
      Info("Chi2TestX","NORM option should be used together with UU option. It is ignored");
   }
   // look at histo global bin content and effective entries
   Stat_t s[kNstat];
   this->GetStats(s);// s[1] sum of squares of weights, s[0] sum of weights
   double sumBinContent1 = s[0];
   double effEntries1 = (s[1] ? s[0]*s[0]/s[1] : 0.);

   h2->GetStats(s);// s[1] sum of squares of weights, s[0] sum of weights
   double sumBinContent2 = s[0];
   double effEntries2 = (s[1] ? s[0]*s[0]/s[1] : 0.);

   if (!comparisonUU && !comparisonUW && !comparisonWW ) {
      // deduce automatically from type of histogram
      if (TMath::Abs(sumBinContent1 - effEntries1) < 1) {
         if ( TMath::Abs(sumBinContent2 - effEntries2) < 1) comparisonUU = true;
         else comparisonUW = true;
      }
      else comparisonWW = true;
   }
   // check unweighted histogram
   if (comparisonUW) {
      if (TMath::Abs(sumBinContent1 - effEntries1) >= 1) {
         Warning("Chi2TestX","First histogram is not unweighted and option UW has been requested");
      }
   }
   if ( (!scaledHistogram && comparisonUU)   ) {
      if ( ( TMath::Abs(sumBinContent1 - effEntries1) >= 1) || (TMath::Abs(sumBinContent2 - effEntries2) >= 1) ) {
         Warning("Chi2TestX","Both histograms are not unweighted and option UU has been requested");
      }
   }


   //get number of events in histogram
   if (comparisonUU && scaledHistogram) {
      for (i=i_start; i<=i_end; i++) {
         for (j=j_start; j<=j_end; j++) {
            for (k=k_start; k<=k_end; k++) {
               bin1 = this->GetBinContent(i,j,k);
               bin2 = h2->GetBinContent(i,j,k);
               err1 = this->GetBinError(i,j,k);
               err2 = h2->GetBinError(i,j,k);
               if (err1 > 0 ) {
                  bin1 *= bin1/(err1*err1);
                  //avoid rounding errors
                  bin1 = TMath::Floor(bin1+0.5);
               }
               else
                  bin1 = 0;

               if (err2 > 0) {
                  bin2 *= bin2/(err2*err2);
                  //avoid rounding errors
                  bin2 = TMath::Floor(bin2+0.5);
               }
               else
                  bin2 = 0;

               // sum contents
               sum1 += bin1;
               sum2 += bin2;
               sumw1 += err1*err1;
               sumw2 += err2*err2;
            }
         }
      }
      if (sumw1 <= 0 || sumw2 <= 0) {
         Error("Chi2TestX","Cannot use option NORM when one histogram has all zero errors");
         return 0;
      }

   } else {
      for (i=i_start; i<=i_end; i++) {
         for (j=j_start; j<=j_end; j++) {
            for (k=k_start; k<=k_end; k++) {
               sum1 += this->GetBinContent(i,j,k);
               sum2 += h2->GetBinContent(i,j,k);
               if ( comparisonWW ) {
                  err1 = this->GetBinError(i,j,k);
                  sumw1 += err1*err1;
               }
               if ( comparisonUW || comparisonWW ) {
                  err2 = h2->GetBinError(i,j,k);
                  sumw2 += err2*err2;
               }
            }
         }
      }
   }
   //checks that the histograms are not empty
   if (sum1 == 0 || sum2 == 0) {
      Error("Chi2TestX","one histogram is empty");
      return 0;
   }

   if ( comparisonWW  && ( sumw1 <= 0 && sumw2 <=0 ) ){
      Error("Chi2TestX","Hist1 and Hist2 have both all zero errors\n");
      return 0;
   }

   //THE TEST
   Int_t m=0, n=0;

   //Experiment - experiment comparison
   if (comparisonUU) {
      Double_t sum = sum1 + sum2;
      for (i=i_start; i<=i_end; i++) {
         for (j=j_start; j<=j_end; j++) {
            for (k=k_start; k<=k_end; k++) {
               bin1 = this->GetBinContent(i,j,k);
               bin2 = h2->GetBinContent(i,j,k);


               if (scaledHistogram) {
                  // scale bin value to effective bin entries
                  err1 = this->GetBinError(i,j,k);
                  if (err1 > 0 ) {
                     bin1 *= bin1/(err1*err1);
                     //avoid rounding errors
                     bin1 = TMath::Floor(bin1+0.5);
                  }
                  else
                     bin1 = 0;

                  err2 = h2->GetBinError(i,j,k);
                  if (err2 > 0) {
                     bin2 *= bin2/(err2*err2);
                     //avoid rounding errors
                     bin2 = TMath::Floor(bin2+0.5);
                  }
                  else
                     bin2 = 0;

               }

               if ( (int(bin1) == 0)  && (int(bin2) == 0) ) {
                  --ndf;  //no data means one degree of freedom less
               } else {


                  Double_t binsum = bin1 + bin2;
                  Double_t nexp1 = binsum*sum1/sum;
                  //Double_t nexp2 = binsum*sum2/sum;

//                  if(opt.Contains("P")) printf("bin %d p = %g\t",i,binsum/sum);

                  if (res)
                     res[i-i_start] = (bin1-nexp1)/TMath::Sqrt(nexp1);

                  if (bin1 < 1) {
                     m++;
                  }
                  if (bin2 < 1) {
                     n++;
                  }

                  //Habermann correction for residuals
                  Double_t correc = (1-sum1/sum)*(1-binsum/sum);
                  if (res) {
                     res[i-i_start] /= TMath::Sqrt(correc);
                  }

                  Double_t delta = sum2*bin1-sum1*bin2;
                  chi2 += delta*delta/binsum;

               }
            }
         }
      }

      chi2 /= (sum1*sum2);
      // flag error only when of the two histogram is zero
      if (m) {
         igood += 1;
         Info("Chi2TestX","There is a bin in h1 with less than 1 event.\n");
      }
      if (n) {
         igood += 2;
         Info("Chi2TestX","There is a bin in h2 with less than 1 event.\n");
      }

      Double_t prob = TMath::Prob(chi2,ndf);
      return prob;

   }


   //unweighted - weighted  comparison
   // case of err2 = 0 and bin2 not zero is treated without problems
   // by excluding second chi2 sum
   // and can be considered as a comparison data-theory
   if ( comparisonUW ) {
      for (i=i_start; i<=i_end; i++) {
         for (j=j_start; j<=j_end; j++) {
            for (k=k_start; k<=k_end; k++) {
               Int_t x=0;
               bin1 = this->GetBinContent(i,j,k);
               bin2 = h2->GetBinContent(i,j,k);
               err2 = h2->GetBinError(i,j,k);

               err2 *= err2;

               // case both histogram have zero bin contents
               if ( (int(bin1) == 0) && (bin2*bin2 == 0) ) {
                  --ndf;  //no data means one degree of freedom less
                  continue;
               }

               // case weighted histogram has zero bin content and error
               if (bin2*bin2 == 0 && err2 == 0) {
                  if (sumw2 > 0) {
                     // use as approximated  error as 1 scaled by a scaling ratio
                     // estimated from the total sum weight and sum weight squared
                     err2 = sumw2/sum2;
                  }
                  else {
                     // return error because infinite discrepancy here:
                     // bin1 != 0 and bin2 =0 in a histogram with all errors zero
                     Error("Chi2TestX","Hist2 has in bin %d,%d,%d zero content and all zero errors\n", i,j,k);
                     chi2 = 0; return 0;
                  }
               }

               if (bin1 < 1)  m++;
               if (err2 > 0 && bin2*bin2/err2 < 10) n++;

               Double_t var1 = sum2*bin2 - sum1*err2;
               Double_t var2 = var1*var1 + 4*sum2*sum2*bin1*err2;
               // if bin1 is zero and bin2=1 and sum1=sum2 var1=0 && var2 ==0
               // approximate by adding +1 to bin1
               // LM (this need to be fixed for numerical errors)
               while (var1*var1+bin1 == 0 || var1+var2 == 0) {
                  sum1++;
                  bin1++;
                  x++;
                  var1 = sum2*bin2 - sum1*err2;
                  var2 = var1*var1 + 4*sum2*sum2*bin1*err2;
               }
               var2 = TMath::Sqrt(var2);
               while (var1+var2 == 0) {
                  sum1++;
                  bin1++;
                  x++;
                  var1 = sum2*bin2 - sum1*err2;
                  var2 = var1*var1 + 4*sum2*sum2*bin1*err2;
                  while (var1*var1+bin1 == 0 || var1+var2 == 0) {
                     sum1++;
                     bin1++;
                     x++;
                     var1 = sum2*bin2 - sum1*err2;
                     var2 = var1*var1 + 4*sum2*sum2*bin1*err2;
                  }
                  var2 = TMath::Sqrt(var2);
               }

               Double_t probb = (var1+var2)/(2*sum2*sum2);

               Double_t nexp1 = probb * sum1;
               Double_t nexp2 = probb * sum2;

//               if(opt.Contains("P")) printf("bin %d p = %g\t",i,probb);

               Double_t delta1 = bin1 - nexp1;
               Double_t delta2 = bin2 - nexp2;

               chi2 += delta1*delta1/nexp1;

               if (err2 > 0) {
                  chi2 += delta2*delta2/err2;
               }

               if (res) {
                  if (err2 > 0) {
                     Double_t temp1 = sum2*err2/var2;
                     Double_t temp2 = 1 + (sum1*err2 - sum2*bin2)/var2;
                     temp2 = temp1*temp1*sum1*probb*(1-probb) + temp2*temp2*err2/4;
                     // invert sign here
                     res[i-i_start] = - delta2/TMath::Sqrt(temp2);
                  }
                  else
                     res[i-i_start] = delta1/TMath::Sqrt(nexp1);

               }
            }
         }
      }

      if (m) {
         igood += 1;
         Info("Chi2TestX","There is a bin in h1 with less than 1 event.\n");
      }
      if (n) {
         igood += 2;
         Info("Chi2TestX","There is a bin in h2 with less than 10 effective events.\n");
      }

      Double_t prob = TMath::Prob(chi2,ndf);

      return prob;
   }

   // weighted - weighted  comparison
   if (comparisonWW) {
      for (i=i_start; i<=i_end; i++) {
         for (j=j_start; j<=j_end; j++) {
            for (k=k_start; k<=k_end; k++) {
               bin1 = this->GetBinContent(i,j,k);
               bin2 = h2->GetBinContent(i,j,k);
               err1 = this->GetBinError(i,j,k);
               err2 = h2->GetBinError(i,j,k);
               err1 *= err1;
               err2 *= err2;

               // case both histogram have zero bin contents
               // (use square of bin1 to avoid numerical errors)
                if ( (bin1*bin1 == 0) && (bin2*bin2 == 0) ) {
                   --ndf;  //no data means one degree of freedom less
                   continue;
                }

                if ( (err1 == 0) && (err2 == 0) ) {
                   // cannot treat case of booth histogram have zero zero errors
                  Error("Chi2TestX","h1 and h2 both have bin %d,%d,%d with all zero errors\n", i,j,k);
                  chi2 = 0; return 0;
               }

               Double_t sigma  = sum1*sum1*err2 + sum2*sum2*err1;
               Double_t delta = sum2*bin1 - sum1*bin2;
               chi2 += delta*delta/sigma;

//               if(opt.Contains("P")) printf("bin %d p = %g\t",i, (bin1*sum1/err1 + bin2*sum2/err2)/(sum1*sum1/err1 + sum2*sum2/err2));

               if (res) {
                  Double_t temp = bin1*sum1*err2 + bin2*sum2*err1;
                  Double_t probb = temp/sigma;
                  Double_t z = 0;
                  if (err1 > err2 ) {
                     Double_t d1 = (bin1 - sum1 * probb);
                     Double_t s1 = err1* ( 1. - err2 * sum1 * sum1 / sigma );
                     z = d1/ TMath::Sqrt(s1);
                  }
                  else {
                     Double_t d2 = (bin2 - sum2 * probb);
                     Double_t s2 = err2* ( 1. - err1 * sum2 * sum2 / sigma );
                     z = -d2/ TMath::Sqrt(s2);
                  }

                  res[i-i_start] = z;
               }

               if (err1 > 0 && bin1*bin1/err1 < 10) m++;
               if (err2 > 0 && bin2*bin2/err2 < 10) n++;
            }
         }
      }
      if (m) {
         igood += 1;
         Info("Chi2TestX","There is a bin in h1 with less than 10 effective events.\n");
      }
      if (n) {
         igood += 2;
         Info("Chi2TestX","There is a bin in h2 with less than 10 effective events.\n");
      }
      Double_t prob = TMath::Prob(chi2,ndf);
      return prob;
   }
   return 0;
}
//______________________________________________________________________________
Double_t TH1::Chisquare(TF1 * func, Option_t *option) const
{
   // Compute and return the chisquare of this histogram with respect to a function
   // The chisquare is computed by weighting each histogram point by the bin error
   // By default the full range of the histogram is used. 
   // Use option "R" for restricting the chisquare calculation to the given range of the function

   if (!func) { 
      Error("Chisquare","Function pointer is Null - return -1");
      return -1;
   }

   TString opt(option); opt.ToUpper(); 
   bool useRange = opt.Contains("R");
   
   return ROOT::Fit::Chisquare(*this, *func, useRange);

}
//______________________________________________________________________________
Double_t TH1::ComputeIntegral(Bool_t onlyPositive)
{
   //  Compute integral (cumulative sum of bins)
   //  The result stored in fIntegral is used by the GetRandom functions.
   //  This function is automatically called by GetRandom when the fIntegral
   //  array does not exist or when the number of entries in the histogram
   //  has changed since the previous call to GetRandom.
   //  The resulting integral is normalized to 1
   //  If the routine is called with the onlyPositive flag set an error will 
   //  be produced in case of negative bin content and a NaN value returned

   Int_t bin, binx, biny, binz, ibin;

   // delete previously computed integral (if any)
   if (fIntegral) delete [] fIntegral;

   //   - Allocate space to store the integral and compute integral
   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();
   Int_t nxy    = nbinsx*nbinsy;
   Int_t nbins  = nxy*nbinsz;

   fIntegral = new Double_t[nbins+2];
   ibin = 0;
   fIntegral[ibin] = 0;
   for (binz=1;binz<=nbinsz;binz++) {
      for (biny=1;biny<=nbinsy;biny++) {
         for (binx=1;binx<=nbinsx;binx++) {
            ibin++;
            bin  = GetBin(binx, biny, binz);
            Double_t y = GetBinContent(bin); 
            if (onlyPositive && y < 0) { 
               Error("ComputeIntegral","Bin content is negative - return a NaN value");
               fIntegral[nbins] = TMath::QuietNaN();
               break;
            }
            fIntegral[ibin] = fIntegral[ibin-1] + y;
         }
      }
   }

   //   - Normalize integral to 1
   if (fIntegral[nbins] == 0 ) {
      Error("ComputeIntegral", "Integral = zero"); return 0;
   }
   for (bin=1;bin<=nbins;bin++)  fIntegral[bin] /= fIntegral[nbins];
   fIntegral[nbins+1] = fEntries;
   return fIntegral[nbins];
}

//______________________________________________________________________________
Double_t *TH1::GetIntegral()
{
   //  Return a pointer to the array of bins integral.
   //  if the pointer fIntegral is null, TH1::ComputeIntegral is called
   // The array dimension is the number of bins in the histograms
   // including underflow and overflow (fNCells)
   // the last value integral[fNCells] is set to the number of entries of
   // the histogram

   if (!fIntegral) ComputeIntegral();
   return fIntegral;
}

//______________________________________________________________________________
TH1 *TH1::GetCumulative(Bool_t forward, const char* suffix) const
{
   //  Return a pointer to an histogram containing the cumulative The
   //  cumulative can be computed both in the forward (default) or backward
   //  direction; the name of the new histogram is constructed from
   //  the name of this histogram with the suffix suffix appended.
   //
   // The cumulative distribution is formed by filling each bin of the
   // resulting histogram with the sum of that bin and all previous
   // (forward == kTRUE) or following (forward = kFALSE) bins.
   //
   // note: while cumulative distributions make sense in one dimension, you
   // may not be getting what you expect in more than 1D because the concept
   // of a cumulative distribution is much trickier to define; make sure you
   // understand the order of summation before you use this method with
   // histograms of dimension >= 2.

   const Int_t nbinsx = GetNbinsX();
   const Int_t nbinsy = GetNbinsY();
   const Int_t nbinsz = GetNbinsZ();
   TH1* hintegrated = (TH1*) Clone(fName + suffix);
   hintegrated->Reset();
   if (forward) { // Forward computation
      Double_t sum = 0.;
      for (Int_t binz = 1; binz <= nbinsz; ++binz) {
	 for (Int_t biny = 1; biny <= nbinsy; ++biny) {
	    for (Int_t binx = 1; binx <= nbinsx; ++binx) {
	       const Int_t bin = hintegrated->GetBin(binx, biny, binz);
	       sum += GetBinContent(bin);
	       hintegrated->SetBinContent(bin, sum);
	    }
	 }
      }
   } else { // Backward computation
      Double_t sum = 0.;
      for (Int_t binz = nbinsz; binz >= 1; --binz) {
	 for (Int_t biny = nbinsy; biny >= 1; --biny) {
	    for (Int_t binx = nbinsx; binx >= 1; --binx) {
	       const Int_t bin = hintegrated->GetBin(binx, biny, binz);
	       sum += GetBinContent(bin);
	       hintegrated->SetBinContent(bin, sum);
	    }
	 }
      }
   }
   return hintegrated;
}

//______________________________________________________________________________
void TH1::Copy(TObject &obj) const
{
   //   -*-*-*-*-*Copy this histogram structure to newth1*-*-*-*-*-*-*-*-*-*-*-*
   //             =======================================
   //
   // Note that this function does not copy the list of associated functions.
   // Use TObject::Clone to make a full copy of an histogram.

   if (((TH1&)obj).fDirectory) {
      // We are likely to change the hash value of this object
      // with TNamed::Copy, to keep things correct, we need to
      // clean up its existing entries.
      ((TH1&)obj).fDirectory->Remove(&obj);
      ((TH1&)obj).fDirectory = 0;
   }
   TNamed::Copy(obj);
   ((TH1&)obj).fDimension = fDimension;
   ((TH1&)obj).fNormFactor= fNormFactor;
   ((TH1&)obj).fNcells    = fNcells;
   ((TH1&)obj).fBarOffset = fBarOffset;
   ((TH1&)obj).fBarWidth  = fBarWidth;
   ((TH1&)obj).fOption    = fOption;
   ((TH1&)obj).fBufferSize= fBufferSize;
   // copy the Buffer
   // delete first a previously existing buffer
   if (((TH1&)obj).fBuffer != 0)  {
      delete []  ((TH1&)obj).fBuffer;
      ((TH1&)obj).fBuffer = 0;
   }
   if (fBuffer) {
      Double_t *buf = new Double_t[fBufferSize];
      for (Int_t i=0;i<fBufferSize;i++) buf[i] = fBuffer[i];
      // obj.fBuffer has been deleted before
      ((TH1&)obj).fBuffer    = buf;
   }


   TArray* a = dynamic_cast<TArray*>(&obj);
   if (a) a->Set(fNcells);
   Int_t canRebin = ((TH1&)obj).TestBit(kCanRebin);
   ((TH1&)obj).ResetBit(kCanRebin);  //we want to avoid the call to LabelsInflate
   // we need to set fBuffer to zero to avoid calling BufferEmpty in GetBinContent
   Double_t * buffer = 0;
   if (fBuffer) {
      buffer = fBuffer;
      ((TH1*)this)->fBuffer = 0;
   }
   for (Int_t i=0;i<fNcells;i++) ((TH1&)obj).SetBinContent(i,this->GetBinContent(i));
   // restore rebin bit and buffer pointer
   if (canRebin) ((TH1&)obj).SetBit(kCanRebin);
   if (buffer) ((TH1*)this)->fBuffer  = buffer;
   ((TH1&)obj).fEntries   = fEntries;

   // which will call BufferEmpty(0) and set fBuffer[0] to a  Maybe one should call
   // assignment operator on the TArrayD

   ((TH1&)obj).fTsumw     = fTsumw;
   ((TH1&)obj).fTsumw2    = fTsumw2;
   ((TH1&)obj).fTsumwx    = fTsumwx;
   ((TH1&)obj).fTsumwx2   = fTsumwx2;
   ((TH1&)obj).fMaximum   = fMaximum;
   ((TH1&)obj).fMinimum   = fMinimum;

   TAttLine::Copy(((TH1&)obj));
   TAttFill::Copy(((TH1&)obj));
   TAttMarker::Copy(((TH1&)obj));
   fXaxis.Copy(((TH1&)obj).fXaxis);
   fYaxis.Copy(((TH1&)obj).fYaxis);
   fZaxis.Copy(((TH1&)obj).fZaxis);
   ((TH1&)obj).fXaxis.SetParent(&obj);
   ((TH1&)obj).fYaxis.SetParent(&obj);
   ((TH1&)obj).fZaxis.SetParent(&obj);
   fContour.Copy(((TH1&)obj).fContour);
   fSumw2.Copy(((TH1&)obj).fSumw2);
   //   fFunctions->Copy(((TH1&)obj).fFunctions);
   if (fgAddDirectory && gDirectory) {
      gDirectory->Append(&obj);
      ((TH1&)obj).fDirectory = gDirectory;
   }

}

//______________________________________________________________________________
void TH1::DirectoryAutoAdd(TDirectory *dir)
{
   // Perform the automatic addition of the histogram to the given directory
   //
   // Note this function is called in place when the semantic requires
   // this object to be added to a directory (I.e. when being read from
   // a TKey or being Cloned)
   //

   Bool_t addStatus = TH1::AddDirectoryStatus();
   if (addStatus) {
      SetDirectory(dir);
      if (dir) {
         ResetBit(kCanDelete);
      }
   }
}

//______________________________________________________________________________
Int_t TH1::DistancetoPrimitive(Int_t px, Int_t py)
{
   //   -*-*-*-*-*-*-*-*-*Compute distance from point px,py to a line*-*-*-*-*-*
   //                     ===========================================
   //     Compute the closest distance of approach from point px,py to elements
   //     of an histogram.
   //     The distance is computed in pixels units.
   //
   //     Algorithm:
   //     Currently, this simple model computes the distance from the mouse
   //     to the histogram contour only.
   //
   //   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   if (!fPainter) return 9999;
   return fPainter->DistancetoPrimitive(px,py);
}

//______________________________________________________________________________
Bool_t TH1::Divide(TF1 *f1, Double_t c1)
{
// Performs the operation: this = this/(c1*f1)
// if errors are defined (see TH1::Sumw2), errors are also recalculated.
//
// Only bins inside the function range are recomputed.
// IMPORTANT NOTE: If you intend to use the errors of this histogram later
// you should call Sumw2 before making this operation.
// This is particularly important if you fit the histogram after TH1::Divide
//
// The function return kFALSE if the divide operation failed

   if (!f1) {
      Error("Add","Attempt to divide by a non-existing function");
      return kFALSE;
   }

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();
   if (fDimension < 2) nbinsy = -1;
   if (fDimension < 3) nbinsz = -1;

   SetMinimum();
   SetMaximum();

//    Reset the kCanRebin option. Otherwise SetBinContent on the overflow bin
//    would resize the axis limits!
   ResetBit(kCanRebin);

//   - Loop on bins (including underflows/overflows)
   Int_t bin, binx, biny, binz;
   Double_t cu,w;
   Double_t xx[3];
   Double_t *params = 0;
   f1->InitArgs(xx,params);
   for (binz=0;binz<=nbinsz+1;binz++) {
      xx[2] = fZaxis.GetBinCenter(binz);
      for (biny=0;biny<=nbinsy+1;biny++) {
         xx[1] = fYaxis.GetBinCenter(biny);
         for (binx=0;binx<=nbinsx+1;binx++) {
            xx[0] = fXaxis.GetBinCenter(binx);
            if (!f1->IsInside(xx)) continue;
            TF1::RejectPoint(kFALSE);
            bin = binx +(nbinsx+2)*(biny + (nbinsy+2)*binz);
            Double_t error1 = GetBinError(bin);
            cu  = c1*f1->EvalPar(xx);
            if (TF1::RejectedPoint()) continue;
            if (cu) w = GetBinContent(bin)/cu;
            else    w = 0;
            SetBinContent(bin,w);
            if (fSumw2.fN) {
               if (cu != 0) fSumw2.fArray[bin] = error1*error1/(cu*cu);
               else         fSumw2.fArray[bin] = 0;
            }
         }
      }
   }
   ResetStats();
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TH1::Divide(const TH1 *h1)
{
//   -*-*-*-*-*-*-*-*-*Divide this histogram by h1*-*-*-*-*-*-*-*-*-*-*-*-*
//                     ===========================
//
//   this = this/h1
//   if errors are defined (see TH1::Sumw2), errors are also recalculated.
//   Note that if h1 has Sumw2 set, Sumw2 is automatically called for this
//   if not already set.
//   The resulting errors are calculated assuming uncorrelated histograms.
//   See the other TH1::Divide that gives the possibility to optionally
//   compute binomial errors.
//
// IMPORTANT NOTE: If you intend to use the errors of this histogram later
// you should call Sumw2 before making this operation.
// This is particularly important if you fit the histogram after TH1::Scale
//
// The function return kFALSE if the divide operation failed

   if (!h1) {
      Error("Divide","Attempt to divide by a non-existing histogram");
      return kFALSE;
   }

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();


   try {
      CheckConsistency(this,h1);
   } catch(DifferentNumberOfBins&) {
      Error("Divide","Attempt to divide histograms with different number of bins");
      return kFALSE;
   } catch(DifferentAxisLimits&) {
      Warning("Divide","Attempt to divide histograms with different axis limits");
   } catch(DifferentBinLimits&) {
      Warning("Divide","Attempt to divide histograms with different bin limits");
   } catch(DifferentLabels&) {
      Warning("Divide","Attempt to divide histograms with different labels");
   }


   if (fDimension < 2) nbinsy = -1;
   if (fDimension < 3) nbinsz = -1;

//    Create Sumw2 if h1 has Sumw2 set
   if (fSumw2.fN == 0 && h1->GetSumw2N() != 0) Sumw2();


//    Reset the kCanRebin option. Otherwise SetBinContent on the overflow bin
//    would resize the axis limits!
   ResetBit(kCanRebin);

//   - Loop on bins (including underflows/overflows)
   Int_t bin, binx, biny, binz;
   Double_t c0,c1,w;
   for (binz=0;binz<=nbinsz+1;binz++) {
      for (biny=0;biny<=nbinsy+1;biny++) {
         for (binx=0;binx<=nbinsx+1;binx++) {
            bin = GetBin(binx,biny,binz);
            c0  = GetBinContent(bin);
            c1  = h1->GetBinContent(bin);
            if (c1) w = c0/c1;
            else    w = 0;
            SetBinContent(bin,w);
            if (fSumw2.fN) {
               Double_t e0 = GetBinError(bin);
               Double_t e1 = h1->GetBinError(bin);
               Double_t c12= c1*c1;
               if (!c1) { fSumw2.fArray[bin] = 0; continue;}
               fSumw2.fArray[bin] = (e0*e0*c1*c1 + e1*e1*c0*c0)/(c12*c12);
            }
         }
      }
   }
   ResetStats();
   return kTRUE;
}


//______________________________________________________________________________
Bool_t TH1::Divide(const TH1 *h1, const TH1 *h2, Double_t c1, Double_t c2, Option_t *option)
{
//   -*-*-*Replace contents of this histogram by the division of h1 by h2*-*-*
//         ==============================================================
//
//   this = c1*h1/(c2*h2)
//
//   if errors are defined (see TH1::Sumw2), errors are also recalculated
//   Note that if h1 or h2 have Sumw2 set, Sumw2 is automatically called for this
//   if not already set.
//   The resulting errors are calculated assuming uncorrelated histograms.
//   However, if option ="B" is specified, Binomial errors are computed.
//   In this case c1 and c2 do not make real sense and they are ignored.
//
// IMPORTANT NOTE: If you intend to use the errors of this histogram later
// you should call Sumw2 before making this operation.
// This is particularly important if you fit the histogram after TH1::Divide
//
//  Please note also that in the binomial case errors are calculated using standard
//  binomial statistics, which means when b1 = b2, the error is zero.
//  If you prefer to have efficiency errors not going to zero when the efficiency is 1, you must
//  use the function TGraphAsymmErrors::BayesDivide, which will return an asymmetric and non-zero lower
//  error for the case b1=b2.
//
// The function return kFALSE if the divide operation failed


   TString opt = option;
   opt.ToLower();
   Bool_t binomial = kFALSE;
   if (opt.Contains("b")) binomial = kTRUE;
   if (!h1 || !h2) {
      Error("Divide","Attempt to divide by a non-existing histogram");
      return kFALSE;
   }

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();

   try {
      CheckConsistency(h1,h2);
      CheckConsistency(this,h1);
   } catch(DifferentNumberOfBins&) {
      Error("Divide","Attempt to divide histograms with different number of bins");
      return kFALSE;
   } catch(DifferentAxisLimits&) {
      Warning("Divide","Attempt to divide histograms with different axis limits");
   } catch(DifferentBinLimits&) {
      Warning("Divide","Attempt to divide histograms with different bin limits");
   }  catch(DifferentLabels&) {
      Warning("Divide","Attempt to divide histograms with different labels");
   }


   if (!c2) {
      Error("Divide","Coefficient of dividing histogram cannot be zero");
      return kFALSE;
   }

   if (fDimension < 2) nbinsy = -1;
   if (fDimension < 3) nbinsz = -1;

//    Create Sumw2 if h1 or h2 have Sumw2 set
   if (fSumw2.fN == 0 && (h1->GetSumw2N() != 0 || h2->GetSumw2N() != 0)) Sumw2();

   SetMinimum();
   SetMaximum();

//    Reset the kCanRebin option. Otherwise SetBinContent on the overflow bin
//    would resize the axis limits!
   ResetBit(kCanRebin);

//   - Loop on bins (including underflows/overflows)
   Int_t bin, binx, biny, binz;
   Double_t b1,b2,w,d1,d2;
   d1 = c1*c1;
   d2 = c2*c2;
   for (binz=0;binz<=nbinsz+1;binz++) {
      for (biny=0;biny<=nbinsy+1;biny++) {
         for (binx=0;binx<=nbinsx+1;binx++) {
            bin = binx +(nbinsx+2)*(biny + (nbinsy+2)*binz);
            b1  = h1->GetBinContent(bin);
            b2  = h2->GetBinContent(bin);
            if (b2) w = c1*b1/(c2*b2);
            else    w = 0;
            SetBinContent(bin,w);
            if (fSumw2.fN) {
               Double_t e1 = h1->GetBinError(bin);
               Double_t e2 = h2->GetBinError(bin);
               Double_t b22= b2*b2*d2;
               if (!b2) { fSumw2.fArray[bin] = 0; continue;}
               if (binomial) {
                  if (b1 != b2) {
                     // in the case of binomial statistics c1 and c2 must be 1 otherwise it does not make sense
                     w = b1/b2;    // c1 and c2 are ignored
                     //fSumw2.fArray[bin] = TMath::Abs(w*(1-w)/(c2*b2));//this is the formula in Hbook/Hoper1
                     //fSumw2.fArray[bin] = TMath::Abs(w*(1-w)/b2);     // old formula from G. Flucke
                     // formula which works also for weighted histogram (see http://root.cern.ch/phpBB2/viewtopic.php?t=3753 )
                     fSumw2.fArray[bin] = TMath::Abs( ( (1.-2.*w)*e1*e1 + w*w*e2*e2 )/(b2*b2) );
                  } else {
                     //in case b1=b2 error is zero
                     //use  TGraphAsymmErrors::BayesDivide for getting the asymmetric error not equal to zero
                     fSumw2.fArray[bin] = 0;
                  }
               } else {
                  fSumw2.fArray[bin] = d1*d2*(e1*e1*b2*b2 + e2*e2*b1*b1)/(b22*b22);
               }
            }
         }
      }
   }
   ResetStats();
   if (binomial)
      // in case of binomial division use denominator for number of entries
      SetEntries ( h2->GetEntries() );

   return kTRUE;
}

//______________________________________________________________________________
void TH1::Draw(Option_t *option)
{
   // Draw this histogram with options.
   //
   // Histograms are drawn via the THistPainter class. Each histogram has
   // a pointer to its own painter (to be usable in a multithreaded program).
   // The same histogram can be drawn with different options in different pads.
   // When an histogram drawn in a pad is deleted, the histogram is
   // automatically removed from the pad or pads where it was drawn.
   // If an histogram is drawn in a pad, then filled again, the new status
   // of the histogram will be automatically shown in the pad next time
   // the pad is updated. One does not need to redraw the histogram.
   // To draw the current version of an histogram in a pad, one can use
   //      h->DrawCopy();
   // This makes a clone of the histogram. Once the clone is drawn, the original
   // histogram may be modified or deleted without affecting the aspect of the
   // clone.
   // By default, TH1::Draw clears the current pad.
   //
   // One can use TH1::SetMaximum and TH1::SetMinimum to force a particular
   // value for the maximum or the minimum scale on the plot.
   //
   // TH1::UseCurrentStyle can be used to change all histogram graphics
   // attributes to correspond to the current selected style.
   // This function must be called for each histogram.
   // In case one reads and draws many histograms from a file, one can force
   // the histograms to inherit automatically the current graphics style
   // by calling before gROOT->ForceStyle();
   //
   // See the THistPainter class for a description of all the drawing options.

   TString opt1 = option; opt1.ToLower();
   TString opt2 = option;
   Int_t index  = opt1.Index("same");

   // Check if the string "same" is part of a TCutg name.
   if (index>=0) {
      Int_t indb = opt1.Index("[");
      if (indb>=0) {
         Int_t indk = opt1.Index("]");
         if (index>indb && index<indk) index = -1;
      }
   }

   // If there is no pad or an empty pad the the "same" is ignored.
   if (gPad) {
      if (!gPad->IsEditable()) gROOT->MakeDefCanvas();
      if (index>=0) {
         if (gPad->GetX1() == 0   && gPad->GetX2() == 1 &&
             gPad->GetY1() == 0   && gPad->GetY2() == 1 &&
             gPad->GetListOfPrimitives()->GetSize()==0) opt2.Remove(index,4);
      } else {
         //the following statement is necessary in case one attempts to draw
         //a temporary histogram already in the current pad
         if (TestBit(kCanDelete)) gPad->GetListOfPrimitives()->Remove(this);
         gPad->Clear();
      }
   } else {
      if (index>=0) opt2.Remove(index,4);
   }

   AppendPad(opt2.Data());
}

//______________________________________________________________________________
TH1 *TH1::DrawCopy(Option_t *) const
{
//   -*-*-*-*-*Copy this histogram and Draw in the current pad*-*-*-*-*-*-*-*
//             ===============================================
//
//     Once the histogram is drawn into the pad, any further modification
//     using graphics input will be made on the copy of the histogram,
//     and not to the original object.
//
//     See Draw for the list of options
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
   AbstractMethod("DrawCopy");
   return 0;
}

//______________________________________________________________________________
TH1 *TH1::DrawNormalized(Option_t *option, Double_t norm) const
{
//  Draw a normalized copy of this histogram.
//
//  A clone of this histogram is normalized to norm and drawn with option.
//  A pointer to the normalized histogram is returned.
//  The contents of the histogram copy are scaled such that the new
//  sum of weights (excluding under and overflow) is equal to norm.
//  Note that the returned normalized histogram is not added to the list
//  of histograms in the current directory in memory.
//  It is the user's responsability to delete this histogram.
//  The kCanDelete bit is set for the returned object. If a pad containing
//  this copy is cleared, the histogram will be automatically deleted
//
//     See Draw for the list of options
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   Double_t sum = GetSumOfWeights();
   if (sum == 0) {
      Error("DrawNormalized","Sum of weights is null. Cannot normalize histogram: %s",GetName());
      return 0;
   }
   Bool_t addStatus = TH1::AddDirectoryStatus();
   TH1::AddDirectory(kFALSE);
   TH1 *h = (TH1*)Clone();
   h->SetBit(kCanDelete);
   // in case of drawing with error options - scale correctly the error
   TString opt(option); opt.ToUpper(); 
   if (fSumw2.fN == 0) { 
      h->Sumw2();
      // do not use in this case the "Error option " for drawing which is enabled by default since the normalized histogram has now errors
      if (opt.IsNull() || opt == "SAME") opt += "HIST";
   }
   h->Scale(norm/sum);
   if (TMath::Abs(fMaximum+1111) > 1e-3) h->SetMaximum(fMaximum*norm/sum);
   if (TMath::Abs(fMinimum+1111) > 1e-3) h->SetMinimum(fMinimum*norm/sum);
   h->Draw(opt);
   TH1::AddDirectory(addStatus);
   return h;
}

//______________________________________________________________________________
void TH1::DrawPanel()
{
//   -*-*-*-*-*Display a panel with all histogram drawing options*-*-*-*-*-*
//             ==================================================
//
//      See class TDrawPanelHist for example

   if (!fPainter) {Draw(); if (gPad) gPad->Update();}
   if (fPainter) fPainter->DrawPanel();
}

//______________________________________________________________________________
void TH1::Eval(TF1 *f1, Option_t *option)
{
//   -*-*-*Evaluate function f1 at the center of bins of this histogram-*-*-*-*
//         ============================================================
//
//     If option "R" is specified, the function is evaluated only
//     for the bins included in the function range.
//     If option "A" is specified, the value of the function is added to the
//     existing bin contents
//     If option "S" is specified, the value of the function is used to
//     generate a value, distributed according to the Poisson
//     distribution, with f1 as the mean.
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
   Double_t x[3];
   Int_t range,stat,add,bin,binx,biny,binz,nbinsx, nbinsy, nbinsz;
   if (!f1) return;
   Double_t fu;
   Double_t e=0;
   TString opt = option;
   opt.ToLower();
   if (opt.Contains("a")) add   = 1;
   else                   add   = 0;
   if (opt.Contains("s")) stat  = 1;
   else                   stat  = 0;
   if (opt.Contains("r")) range = 1;
   else                   range = 0;

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   nbinsx  = fXaxis.GetNbins();
   nbinsy  = fYaxis.GetNbins();
   nbinsz  = fZaxis.GetNbins();
   if (!add) Reset();

   for (binz=1;binz<=nbinsz;binz++) {
      x[2]  = fZaxis.GetBinCenter(binz);
      for (biny=1;biny<=nbinsy;biny++) {
         x[1]  = fYaxis.GetBinCenter(biny);
         for (binx=1;binx<=nbinsx;binx++) {
            bin = GetBin(binx,biny,binz);
            x[0]  = fXaxis.GetBinCenter(binx);
            if (range && !f1->IsInside(x)) continue;
            fu = f1->Eval(x[0],x[1],x[2]);
            if (stat) fu = gRandom->PoissonD(fu);
            if (fSumw2.fN) e = fSumw2.fArray[bin];
            AddBinContent(bin,fu);
            if (fSumw2.fN) fSumw2.fArray[bin] = e+ TMath::Abs(fu);
         }
      }
   }
}

//______________________________________________________________________________
void TH1::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
//   -*-*-*-*-*-*-*-*-*Execute action corresponding to one event*-*-*-*
//                     =========================================
//     This member function is called when a histogram is clicked with the locator
//
//     If Left button clicked on the bin top value, then the content of this bin
//     is modified according to the new position of the mouse when it is released.
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   if (fPainter) fPainter->ExecuteEvent(event, px, py);
}

//______________________________________________________________________________
TH1* TH1::FFT(TH1* h_output, Option_t *option)
{
// This function allows to do discrete Fourier transforms of TH1 and TH2.
// Available transform types and flags are described below.
//
// To extract more information about the transform, use the function
//  TVirtualFFT::GetCurrentTransform() to get a pointer to the current
//  transform object.
//
// Parameters:
//  1st - histogram for the output. If a null pointer is passed, a new histogram is created
//  and returned, otherwise, the provided histogram is used and should be big enough
//
//  Options: option parameters consists of 3 parts:
//    - option on what to return
//   "RE" - returns a histogram of the real part of the output
//   "IM" - returns a histogram of the imaginary part of the output
//   "MAG"- returns a histogram of the magnitude of the output
//   "PH" - returns a histogram of the phase of the output
//
//    - option of transform type
//   "R2C"  - real to complex transforms - default
//   "R2HC" - real to halfcomplex (special format of storing output data,
//          results the same as for R2C)
//   "DHT" - discrete Hartley transform
//         real to real transforms (sine and cosine):
//   "R2R_0", "R2R_1", "R2R_2", "R2R_3" - discrete cosine transforms of types I-IV
//   "R2R_4", "R2R_5", "R2R_6", "R2R_7" - discrete sine transforms of types I-IV
//    To specify the type of each dimension of a 2-dimensional real to real
//    transform, use options of form "R2R_XX", for example, "R2R_02" for a transform,
//    which is of type "R2R_0" in 1st dimension and  "R2R_2" in the 2nd.
//
//    - option of transform flag
//    "ES" (from "estimate") - no time in preparing the transform, but probably sub-optimal
//       performance
//    "M" (from "measure")   - some time spend in finding the optimal way to do the transform
//    "P" (from "patient")   - more time spend in finding the optimal way to do the transform
//    "EX" (from "exhaustive") - the most optimal way is found
//     This option should be chosen depending on how many transforms of the same size and
//     type are going to be done. Planning is only done once, for the first transform of this
//     size and type. Default is "ES".
//   Examples of valid options: "Mag R2C M" "Re R2R_11" "Im R2C ES" "PH R2HC EX"


   Int_t ndim[3];
   ndim[0] = this->GetNbinsX();
   ndim[1] = this->GetNbinsY();
   ndim[2] = this->GetNbinsZ();

   TVirtualFFT *fft;
   TString opt = option;
   opt.ToUpper();
   if (!opt.Contains("2R")){
      if (!opt.Contains("2C") && !opt.Contains("2HC") && !opt.Contains("DHT")) {
         //no type specified, "R2C" by default
         opt.Append("R2C");
      }
      fft = TVirtualFFT::FFT(this->GetDimension(), ndim, opt.Data());
   }
   else {
      //find the kind of transform
      Int_t ind = opt.Index("R2R", 3);
      Int_t *kind = new Int_t[2];
      char t;
      t = opt[ind+4];
      kind[0] = atoi(&t);
      if (h_output->GetDimension()>1) {
         t = opt[ind+5];
         kind[1] = atoi(&t);
      }
      fft = TVirtualFFT::SineCosine(this->GetDimension(), ndim, kind, option);
      delete [] kind;
   }

   if (!fft) return 0;
   Int_t in=0;
   for (Int_t binx = 1; binx<=ndim[0]; binx++) {
      for (Int_t biny=1; biny<=ndim[1]; biny++) {
         for (Int_t binz=1; binz<=ndim[2]; binz++) {
            fft->SetPoint(in, this->GetBinContent(binx, biny, binz));
            in++;
         }
      }
   }
   fft->Transform();
   h_output = TransformHisto(fft, h_output, option);
   return h_output;
}

//______________________________________________________________________________
Int_t TH1::Fill(Double_t x)
{
//   -*-*-*-*-*-*-*-*Increment bin with abscissa X by 1*-*-*-*-*-*-*-*-*-*-*
//                   ==================================
//
//    if x is less than the low-edge of the first bin, the Underflow bin is incremented
//    if x is greater than the upper edge of last bin, the Overflow bin is incremented
//
//    If the storage of the sum of squares of weights has been triggered,
//    via the function Sumw2, then the sum of the squares of weights is incremented
//    by 1 in the bin corresponding to x.
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   if (fBuffer)  return BufferFill(x,1);

   Int_t bin;
   fEntries++;
   bin =fXaxis.FindBin(x);
   if (bin <0) return -1;
   AddBinContent(bin);
   if (fSumw2.fN) ++fSumw2.fArray[bin];
   if (bin == 0 || bin > fXaxis.GetNbins()) {
      if (!fgStatOverflows) return -1;
   }
   ++fTsumw;
   ++fTsumw2;
   fTsumwx  += x;
   fTsumwx2 += x*x;
   return bin;
}

//______________________________________________________________________________
Int_t TH1::Fill(Double_t x, Double_t w)
{
//   -*-*-*-*-*-*Increment bin with abscissa X with a weight w*-*-*-*-*-*-*-*
//               =============================================
//
//    if x is less than the low-edge of the first bin, the Underflow bin is incremented
//    if x is greater than the upper edge of last bin, the Overflow bin is incremented
//
//    If the storage of the sum of squares of weights has been triggered,
//    via the function Sumw2, then the sum of the squares of weights is incremented
//    by w^2 in the bin corresponding to x.
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   
   if (fBuffer) return BufferFill(x,w);

   Int_t bin;
   fEntries++;
   bin =fXaxis.FindBin(x);
   if (bin <0) return -1;
   AddBinContent(bin, w);
   if (fSumw2.fN) fSumw2.fArray[bin] += w*w;
   if (bin == 0 || bin > fXaxis.GetNbins()) {
      if (!fgStatOverflows) return -1;
   }
   //Double_t z= (w > 0 ? w : -w);
   Double_t z= w;
   fTsumw   += z;
   fTsumw2  += z*z;
   fTsumwx  += z*x;
   fTsumwx2 += z*x*x;
   return bin;
}

//______________________________________________________________________________
Int_t TH1::Fill(const char *namex, Double_t w)
{
// Increment bin with namex with a weight w
//
// if x is less than the low-edge of the first bin, the Underflow bin is incremented
// if x is greater than the upper edge of last bin, the Overflow bin is incremented
//
// If the storage of the sum of squares of weights has been triggered,
// via the function Sumw2, then the sum of the squares of weights is incremented
// by w^2 in the bin corresponding to x.
//

   Int_t bin;
   fEntries++;
   bin =fXaxis.FindBin(namex);
   if (bin <0) return -1;
   AddBinContent(bin, w);
   if (fSumw2.fN) fSumw2.fArray[bin] += w*w;
   if (bin == 0 || bin > fXaxis.GetNbins()) return -1;
   //Double_t z= (w > 0 ? w : -w);
   Double_t z= w;
   fTsumw   += z;
   fTsumw2  += z*z;
   // this make sense if the histogram is not expanding (kCanRebin is not set)
   if (!TestBit(TH1::kCanRebin)) {
      Double_t x = fXaxis.GetBinCenter(bin);
      fTsumwx  += z*x;
      fTsumwx2 += z*x*x;
   }
   return bin;
}

//______________________________________________________________________________
void TH1::FillN(Int_t ntimes, const Double_t *x, const Double_t *w, Int_t stride)
{
   // Fill this histogram with an array x and weights w.
   //
   //    ntimes:  number of entries in arrays x and w (array size must be ntimes*stride)
   //    x:       array of values to be histogrammed
   //    w:       array of weighs
   //    stride:  step size through arrays x and w
   //
   //    If the weight is not equal to 1, the storage of the sum of squares of
   //    weights is automatically triggered and the sum of the squares of weights is incremented
   //    by w^2 in the bin corresponding to x.
   //    if w is NULL each entry is assumed a weight=1

   
   //If a buffer is activated, fill buffer
   if (fBuffer) {
      ntimes *= stride;
      Int_t i = 0;
      for (i=0;i<ntimes;i+=stride) {
         if (!fBuffer) break;   // buffer can be deleted in BufferFill when is empty
         if (w) BufferFill(x[i],w[i]);
         else BufferFill(x[i], 1.);
      }
      // fill the remaining entries if the buffer has been deleted 
      if (i < ntimes && fBuffer==0) 
         DoFillN((ntimes-i)/stride,&x[i],&w[i],stride);
      return;
   }
   // call internal method 
   DoFillN(ntimes, x, w, stride);
}

//______________________________________________________________________________
void TH1::DoFillN(Int_t ntimes, const Double_t *x, const Double_t *w, Int_t stride)
{
   // internal method to fill histogram content from a vector
   // called directly by TH1::BufferEmpty

   Int_t bin,i;
   
   fEntries += ntimes;
   Double_t ww = 1;
   Int_t nbins   = fXaxis.GetNbins();
   ntimes *= stride;
   for (i=0;i<ntimes;i+=stride) {
      bin =fXaxis.FindBin(x[i]);
      if (bin <0) continue;
      if (w) ww = w[i];
      AddBinContent(bin, ww);
      if (fSumw2.fN) fSumw2.fArray[bin] += ww*ww;
      if (bin == 0 || bin > nbins) {
         if (!fgStatOverflows) continue;
      }
      //Double_t z= (ww > 0 ? ww : -ww);
      Double_t z= ww;
      fTsumw   += z;
      fTsumw2  += z*z;
      fTsumwx  += z*x[i];
      fTsumwx2 += z*x[i]*x[i];
   }
}

//______________________________________________________________________________
void TH1::FillRandom(const char *fname, Int_t ntimes)
{
//   -*-*-*-*-*Fill histogram following distribution in function fname*-*-*-*
//             =======================================================
//
//      The distribution contained in the function fname (TF1) is integrated
//      over the channel contents for the bin range of this histogram.
//      It is normalized to 1.
//      Getting one random number implies:
//        - Generating a random number between 0 and 1 (say r1)
//        - Look in which bin in the normalized integral r1 corresponds to
//        - Fill histogram channel
//      ntimes random numbers are generated
//
//     One can also call TF1::GetRandom to get a random variate from a function.
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*

   Int_t bin, binx, ibin, loop;
   Double_t r1, x;
//   - Search for fname in the list of ROOT defined functions
   TF1 *f1 = (TF1*)gROOT->GetFunction(fname);
   if (!f1) { Error("FillRandom", "Unknown function: %s",fname); return; }

//   - Allocate temporary space to store the integral and compute integral
   Int_t first  = fXaxis.GetFirst();
   Int_t last   = fXaxis.GetLast();
   Int_t nbinsx = last-first+1;

   Double_t *integral = new Double_t[nbinsx+1];
   integral[0] = 0;
   for (binx=1;binx<=nbinsx;binx++) {
      Double_t fint = f1->Integral(fXaxis.GetBinLowEdge(binx+first-1),fXaxis.GetBinUpEdge(binx+first-1));
      integral[binx] = integral[binx-1] + fint;
   }

//   - Normalize integral to 1
   if (integral[nbinsx] == 0 ) {
      delete [] integral;
      Error("FillRandom", "Integral = zero"); return;
   }
   for (bin=1;bin<=nbinsx;bin++)  integral[bin] /= integral[nbinsx];

//   --------------Start main loop ntimes
   for (loop=0;loop<ntimes;loop++) {
      r1 = gRandom->Rndm(loop);
      ibin = TMath::BinarySearch(nbinsx,&integral[0],r1);
      //binx = 1 + ibin;
      //x    = fXaxis.GetBinCenter(binx); //this is not OK when SetBuffer is used
      x    = fXaxis.GetBinLowEdge(ibin+first)
             +fXaxis.GetBinWidth(ibin+first)*(r1-integral[ibin])/(integral[ibin+1] - integral[ibin]);
      Fill(x, 1.);
   }
   delete [] integral;
}

//______________________________________________________________________________
void TH1::FillRandom(TH1 *h, Int_t ntimes)
{
//   -*-*-*-*-*Fill histogram following distribution in histogram h*-*-*-*
//             ====================================================
//
//      The distribution contained in the histogram h (TH1) is integrated
//      over the channel contents for the bin range of this histogram.
//      It is normalized to 1.
//      Getting one random number implies:
//        - Generating a random number between 0 and 1 (say r1)
//        - Look in which bin in the normalized integral r1 corresponds to
//        - Fill histogram channel
//      ntimes random numbers are generated
//
//    SPECIAL CASE when the target histogram has the same binning as the source.
//   in this case we simply use a poisson distribution where
//   the mean value per bin = bincontent/integral.
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*

   if (!h) { Error("FillRandom", "Null histogram"); return; }
   if (fDimension != h->GetDimension()) {
      Error("FillRandom", "Histograms with different dimensions"); return;
   }

   //in case the target histogram has the same binning and ntimes much greater
   //than the number of bins we can use a fast method
   Int_t first  = fXaxis.GetFirst();
   Int_t last   = fXaxis.GetLast();
   Int_t nbins = last-first+1;
   if (ntimes > 10*nbins) { 
      try {
         CheckConsistency(this,h);
         Double_t sumw = h->Integral(first,last);
         if (sumw == 0) return;
         Double_t sumgen = 0;
         for (Int_t bin=first;bin<=last;bin++) {
            Double_t mean = h->GetBinContent(bin)*ntimes/sumw;
            Double_t cont = (Double_t)gRandom->Poisson(mean);
            sumgen += cont;
            AddBinContent(bin,cont);
            if (fSumw2.fN) fSumw2.fArray[bin] += cont;
         }
         
         // fix for the fluctations in the total number n
         // since we use Poisson instead of multinomial
         // add a correction to have ntimes as generated entries
         Int_t i;
         if (sumgen < ntimes) {
            // add missing entries
            for (i = Int_t(sumgen+0.5); i < ntimes; ++i)
            {
               Double_t x = h->GetRandom();
               Fill(x);
            }
         }
         else if (sumgen > ntimes) {
            // remove extra entries
            i =  Int_t(sumgen+0.5);
            while( i > ntimes) {
               Double_t x = h->GetRandom();
               Int_t ibin = fXaxis.FindBin(x);
               Double_t y = GetBinContent(ibin);
               // skip in case bin is empty
               if (y > 0) {
                  SetBinContent(ibin, y-1.);
                  i--;
               }
            }
         }

         ResetStats();
         return;
      }
      catch(std::exception&) {}  // do nothing 
   }
   // case of different axis and not too large ntimes
   
   if (h->ComputeIntegral() ==0) return;
   Int_t loop;
   Double_t x;
   for (loop=0;loop<ntimes;loop++) {
      x = h->GetRandom();
      Fill(x);
   }
}


//______________________________________________________________________________
Int_t TH1::FindBin(Double_t x, Double_t y, Double_t z)
{
//   Return Global bin number corresponding to x,y,z
//   ===============================================
//
//      2-D and 3-D histograms are represented with a one dimensional
//      structure. This function tries to rebin the axis if the given point
//      belongs to an under-/overflow bin.
//      This has the advantage that all existing functions, such as
//        GetBinContent, GetBinError, GetBinFunction work for all dimensions.
//     See also TH1::GetBin, TAxis::FindBin and TAxis::FindFixBin
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   if (GetDimension() < 2) {
      return fXaxis.FindBin(x);
   }
   if (GetDimension() < 3) {
      Int_t nx   = fXaxis.GetNbins()+2;
      Int_t binx = fXaxis.FindBin(x);
      Int_t biny = fYaxis.FindBin(y);
      return  binx + nx*biny;
   }
   if (GetDimension() < 4) {
      Int_t nx   = fXaxis.GetNbins()+2;
      Int_t ny   = fYaxis.GetNbins()+2;
      Int_t binx = fXaxis.FindBin(x);
      Int_t biny = fYaxis.FindBin(y);
      Int_t binz = fZaxis.FindBin(z);
      return  binx + nx*(biny +ny*binz);
   }
   return -1;
}

//______________________________________________________________________________
Int_t TH1::FindFixBin(Double_t x, Double_t y, Double_t z) const
{
//   Return Global bin number corresponding to x,y,z
//   ===============================================
//
//      2-D and 3-D histograms are represented with a one dimensional
//      structure. This function DOES not try to rebin the axis if the given
//      point belongs to an under-/overflow bin.
//      This has the advantage that all existing functions, such as
//        GetBinContent, GetBinError, GetBinFunction work for all dimensions.
//     See also TH1::GetBin, TAxis::FindBin and TAxis::FindFixBin
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   if (GetDimension() < 2) {
      return fXaxis.FindFixBin(x);
   }
   if (GetDimension() < 3) {
      Int_t nx   = fXaxis.GetNbins()+2;
      Int_t binx = fXaxis.FindFixBin(x);
      Int_t biny = fYaxis.FindFixBin(y);
      return  binx + nx*biny;
   }
   if (GetDimension() < 4) {
      Int_t nx   = fXaxis.GetNbins()+2;
      Int_t ny   = fYaxis.GetNbins()+2;
      Int_t binx = fXaxis.FindFixBin(x);
      Int_t biny = fYaxis.FindFixBin(y);
      Int_t binz = fZaxis.FindFixBin(z);
      return  binx + nx*(biny +ny*binz);
   }
   return -1;
}

//______________________________________________________________________________
Int_t TH1::FindFirstBinAbove(Double_t threshold, Int_t axis) const
{
   //find first bin with content > threshold for axis (1=x, 2=y, 3=z)
   //if no bins with content > threshold is found the function returns -1.

   if (axis != 1) {
      Warning("FindFirstBinAbove","Invalid axis number : %d, axis x assumed\n",axis);
      axis = 1;
   }
   Int_t nbins = fXaxis.GetNbins();
   for (Int_t bin=1;bin<=nbins;bin++) {
      if (GetBinContent(bin) > threshold) return bin;
   }
   return -1;
}


//______________________________________________________________________________
Int_t TH1::FindLastBinAbove(Double_t threshold, Int_t axis) const
{
   //find last bin with content > threshold for axis (1=x, 2=y, 3=z)
   //if no bins with content > threshold is found the function returns -1.

   if (axis != 1) {
      Warning("FindLastBinAbove","Invalid axis number : %d, axis x assumed\n",axis);
      axis = 1;
   }
   Int_t nbins = fXaxis.GetNbins();
   for (Int_t bin=nbins;bin>=1;bin--) {
      if (GetBinContent(bin) > threshold) return bin;
   }
   return -1;
}

//______________________________________________________________________________
TObject *TH1::FindObject(const char *name) const
{
// search object named name in the list of functions

   if (fFunctions) return fFunctions->FindObject(name);
   return 0;
}

//______________________________________________________________________________
TObject *TH1::FindObject(const TObject *obj) const
{
// search object obj in the list of functions

   if (fFunctions) return fFunctions->FindObject(obj);
   return 0;
}

//______________________________________________________________________________
TFitResultPtr TH1::Fit(const char *fname ,Option_t *option ,Option_t *goption, Double_t xxmin, Double_t xxmax)
{
//                     Fit histogram with function fname
//                     =================================
//      fname is the name of an already predefined function created by TF1 or TF2
//      Predefined functions such as gaus, expo and poln are automatically
//      created by ROOT.
//      fname can also be a formula, accepted by the linear fitter (linear parts divided
//      by "++" sign), for example "x++sin(x)" for fitting "[0]*x+[1]*sin(x)"
//
//  This function finds a pointer to the TF1 object with name fname
//  and calls TH1::Fit(TF1 *f1,...)

   char *linear;
   linear= (char*)strstr(fname, "++");
   TF1 *f1=0;
   TF2 *f2=0;
   TF3 *f3=0;
   Int_t ndim=GetDimension();
   if (linear){
      if (ndim<2){
         f1=new TF1(fname, fname, xxmin, xxmax);
         return Fit(f1,option,goption,xxmin,xxmax);
      }
      else if (ndim<3){
         f2=new TF2(fname, fname);
         return Fit(f2,option,goption,xxmin,xxmax);
      }
      else{
         f3=new TF3(fname, fname);
         return Fit(f3,option,goption,xxmin,xxmax);
      }
   }

   else{
      f1 = (TF1*)gROOT->GetFunction(fname);
      if (!f1) { Printf("Unknown function: %s",fname); return -1; }
      return Fit(f1,option,goption,xxmin,xxmax);
   }
}

//______________________________________________________________________________
TFitResultPtr TH1::Fit(TF1 *f1 ,Option_t *option ,Option_t *goption, Double_t xxmin, Double_t xxmax)
{
//                     Fit histogram with function f1
//                     ==============================
//
//      Fit this histogram with function f1.
//
//      The list of fit options is given in parameter option.
//         option = "W"  Set all weights to 1 for non empty bins; ignore error bars
//                = "WW" Set all weights to 1 including empty bins; ignore error bars
//                = "I"  Use integral of function in bin, normalized by the bin volume,
//                       instead of value at bin center
//                = "L"  Use Loglikelihood method (default is chisquare method)
//                = "WL" Use Loglikelihood method and bin contents are not integer,
//                       i.e. histogram is weighted (must have Sumw2() set)
//                = "U"  Use a User specified fitting algorithm (via SetFCN)
//                = "Q"  Quiet mode (minimum printing)
//                = "V"  Verbose mode (default is between Q and V)
//                = "E"  Perform better Errors estimation using Minos technique
//                = "B"  User defined parameter settings are used for predefined functions
//                       like "gaus", "expo", "poln", "landau".
//                       Use this option when you want to fix one or more parameters for these functions.
//                = "M"  More. Improve fit results.
//                       It uses the IMPROVE command of TMinuit (see TMinuit::mnimpr).
//                       This algorithm attempts to improve the found local minimum by searching for a
//                       better one.
//                = "R"  Use the Range specified in the function range
//                = "N"  Do not store the graphics function, do not draw
//                = "0"  Do not plot the result of the fit. By default the fitted function
//                       is drawn unless the option"N" above is specified.
//                = "+"  Add this new fitted function to the list of fitted functions
//                       (by default, any previous function is deleted)
//                = "C"  In case of linear fitting, don't calculate the chisquare
//                       (saves time)
//                = "F"  If fitting a polN, switch to minuit fitter
//                = "S"  The result of the fit is returned in the TFitResultPtr
//                       (see below Access to the Fit Result)
//
//      When the fit is drawn (by default), the parameter goption may be used
//      to specify a list of graphics options. See TH1::Draw for a complete
//      list of these options.
//
//      In order to use the Range option, one must first create a function
//      with the expression to be fitted. For example, if your histogram
//      has a defined range between -4 and 4 and you want to fit a gaussian
//      only in the interval 1 to 3, you can do:
//           TF1 *f1 = new TF1("f1", "gaus", 1, 3);
//           histo->Fit("f1", "R");
//
//      Setting initial conditions
//      ==========================
//      Parameters must be initialized before invoking the Fit function.
//      The setting of the parameter initial values is automatic for the
//      predefined functions : poln, expo, gaus, landau. One can however disable
//      this automatic computation by specifying the option "B".
//      Note that if a predefined function is defined with an argument,
//      eg, gaus(0), expo(1), you must specify the initial values for
//      the parameters.
//      You can specify boundary limits for some or all parameters via
//           f1->SetParLimits(p_number, parmin, parmax);
//      if parmin>=parmax, the parameter is fixed
//      Note that you are not forced to fix the limits for all parameters.
//      For example, if you fit a function with 6 parameters, you can do:
//        func->SetParameters(0, 3.1, 1.e-6, -8, 0, 100);
//        func->SetParLimits(3, -10, -4);
//        func->FixParameter(4, 0);
//        func->SetParLimits(5, 1, 1);
//      With this setup, parameters 0->2 can vary freely
//      Parameter 3 has boundaries [-10,-4] with initial value -8
//      Parameter 4 is fixed to 0
//      Parameter 5 is fixed to 100.
//      When the lower limit and upper limit are equal, the parameter is fixed.
//      However to fix a parameter to 0, one must call the FixParameter function.
//
//      Note that option "I" gives better results but is slower.
//
//
//      Changing the fitting objective function
//      =============================
//     By default a chi square function is used for fitting. When option "L" (or "LL") is used
//     a Poisson likelihood function (see note below) is used.
//     The functions are defined in the header Fit/Chi2Func.h or Fit/PoissonLikelihoodFCN and they
//     are implemented using the routines FitUtil::EvaluateChi2 or FitUtil::EvaluatePoissonLogL in
//     the file math/mathcore/src/FitUtil.cxx.
//     To specify a User defined fitting function, specify option "U" and
//     call the following functions:
//       TVirtualFitter::Fitter(myhist)->SetFCN(MyFittingFunction)
//     where MyFittingFunction is of type:
//     extern void MyFittingFunction(Int_t &npar, Double_t *gin, Double_t &f, Double_t *u, Int_t flag);
//
//     Likelihood Fits
//     =================
//     When using option "L" a likelihood fit is used instead of the default chi2 square fit.
//     The likelihood is built assuming a Poisson probability density function for each bin.
//     This method can then be used only when the bin content represents counts (i.e. errors are sqrt(N) ).
//     The likelihood method has the advantage of treating correctly the empty bins and use them in the
//     fit procedure.
//     In the chi2 method the empty bins are skipped and not considered in the fit.
//     The likelihood method, although a bit slower, it is the recommended method in case of low
//     bin statistics, where the chi2 method may give incorrect results.
//
//      Fitting a histogram of dimension N with a function of dimension N-1
//      ===================================================================
//     It is possible to fit a TH2 with a TF1 or a TH3 with a TF2.
//     In this case the option "Integral" is not allowed and each cell has
//     equal weight.
//
//     Associated functions
//     ====================
//     One or more object (typically a TF1*) can be added to the list
//     of functions (fFunctions) associated to each histogram.
//     When TH1::Fit is invoked, the fitted function is added to this list.
//     Given an histogram h, one can retrieve an associated function
//     with:  TF1 *myfunc = h->GetFunction("myfunc");
//
//      Access to the fit result
//      ========================
//     The function returns a TFitResultPtr which can hold a  pointer to a TFitResult object.
//     By default the TFitResultPtr contains only the status of the fit which is return by an
//     automatic conversion of the TFitResultPtr to an integer. One can write in this case directly:
//     Int_t fitStatus =  h->Fit(myFunc)
//
//     If the option "S" is instead used, TFitResultPtr contains the TFitResult and behaves as a smart
//     pointer to it. For example one can do:
//     TFitResultPtr r = h->Fit(myFunc,"S");
//     TMatrixDSym cov = r->GetCovarianceMatrix();  //  to access the covariance matrix
//     Double_t chi2   = r->Chi2(); // to retrieve the fit chi2
//     Double_t par0   = r->Parameter(0); // retrieve the value for the parameter 0
//     Double_t err0   = r->ParError(0); // retrieve the error for the parameter 0
//     r->Print("V");     // print full information of fit including covariance matrix
//     r->Write();        // store the result in a file
//
//     The fit parameters, error and chi2 (but not covariance matrix) can be retrieved also
//     from the fitted function.
//     If the histogram is made persistent, the list of
//     associated functions is also persistent. Given a pointer (see above)
//     to an associated function myfunc, one can retrieve the function/fit
//     parameters with calls such as:
//       Double_t chi2 = myfunc->GetChisquare();
//       Double_t par0 = myfunc->GetParameter(0); //value of 1st parameter
//       Double_t err0 = myfunc->GetParError(0);  //error on first parameter
//
//
//     Access to the fit status
//     =====================
//     The status of the fit can be obtained converting the TFitResultPtr to an integer
//     independently if the fit option "S" is used or not:
//     TFitResultPtr r = h->Fit(myFunc,opt);
//     Int_t fitStatus = r;
//
//     The fitStatus is 0 if the fit is OK (i.e no error occurred).
//     The value of the fit status code is negative in case of an error not connected with the
//     minimization procedure, for example  when a wrong function is used.
//     Otherwise the return value is the one returned from the minimization procedure.
//     When TMinuit (default case) or Minuit2 are used as minimizer the status returned is :
//     fitStatus =  migradResult + 10*minosResult + 100*hesseResult + 1000*improveResult.
//     TMinuit will return 0 (for migrad, minos, hesse or improve) in case of success and 4 in
//     case of error (see the documentation of TMinuit::mnexcm). So for example, for an error
//     only in Minos but not in Migrad a fitStatus of 40 will be returned.
//     Minuit2 will return also 0 in case of success and different values in migrad minos or
//     hesse depending on the error. See in this case the documentation of
//     Minuit2Minimizer::Minimize for the migradResult, Minuit2Minimizer::GetMinosError for the
//     minosResult and Minuit2Minimizer::Hesse for the hesseResult.
//     If other minimizers are used see their specific documentation for the status code returned.
//     For example in the case of Fumili, for the status returned see TFumili::Minimize.
//
//      Excluding points
//      ================
//     Use TF1::RejectPoint inside your fitting function to exclude points
//     within a certain range from the fit. Example:
//     Double_t fline(Double_t *x, Double_t *par)
//     {
//         if (x[0] > 2.5 && x[0] < 3.5) {
//           TF1::RejectPoint();
//           return 0;
//        }
//        return par[0] + par[1]*x[0];
//     }
//
//     void exclude() {
//        TF1 *f1 = new TF1("f1", "[0] +[1]*x +gaus(2)", 0, 5);
//        f1->SetParameters(6, -1,5, 3, 0.2);
//        TH1F *h = new TH1F("h", "background + signal", 100, 0, 5);
//        h->FillRandom("f1", 2000);
//        TF1 *fline = new TF1("fline", fline, 0, 5, 2);
//        fline->SetParameters(2, -1);
//        h->Fit("fline", "l");
//     }
//
//      Warning when using the option "0"
//      =================================
//     When selecting the option "0", the fitted function is added to
//     the list of functions of the histogram, but it is not drawn.
//     You can undo what you disabled in the following way:
//       h.Fit("myFunction", "0"); // fit, store function but do not draw
//       h.Draw(); function is not drawn
//       const Int_t kNotDraw = 1<<9;
//       h.GetFunction("myFunction")->ResetBit(kNotDraw);
//       h.Draw();  // function is visible again
//
//      Access to the Minimizer information during fitting
//      ===============================================
//     This function calls, the ROOT::Fit::FitObject function implemented in HFitImpl.cxx
//     which uses the ROOT::Fit::Fitter class. The Fitter class creates the objective fuction
//     (e.g. chi2 or likelihood) and uses an implementation of the  Minimizer interface for minimizing
//     the function.
//     The default minimizer is Minuit (class TMinuitMinimizer which calls TMinuit).
//     The default  can be set in the resource file in etc/system.rootrc. For example
//     Root.Fitter:      Minuit2
//     A different fitter can also be set via ROOT::Math::MinimizerOptions::SetDefaultMinimizer
//     (or TVirtualFitter::SetDefaultFitter).
//     For example ROOT::Math::MinimizerOptions::SetDefaultMinimizer("GSLMultiMin","BFGS");
//     will set the usdage of the BFGS algorithm of the GSL multi-dimensional minimization
//     (implemented in libMathMore). ROOT::Math::MinimizerOptions can be used also to set other
//     default options, like maximum number of function calls, minimization tolerance or print
//     level. See the documentation of this class.
//
//     For fitting linear functions (containing the "++" sign" and polN functions,
//     the linear fitter is automatically initialized.
//
//   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   // implementation of Fit method is in file hist/src/HFitImpl.cxx
   Foption_t fitOption;

   if (!FitOptionsMake(option,fitOption)) return 0;
   // create range and minimizer options with default values
   ROOT::Fit::DataRange range(xxmin,xxmax);
   ROOT::Math::MinimizerOptions minOption;

   // need to empty the buffer before
   // (t.b.d. do a ML unbinned fit with buffer data)
   if (fBuffer) BufferEmpty();

   return ROOT::Fit::FitObject(this, f1 , fitOption , minOption, goption, range);
}

//______________________________________________________________________________
void TH1::FitPanel()
{
//   -*-*-*-*-*Display a panel with all histogram fit options*-*-*-*-*-*
//             ==============================================
//
//      See class TFitPanel for example

   if (!gPad)
      gROOT->MakeDefCanvas();

   if (!gPad) {
      Error("FitPanel", "Unable to create a default canvas");
      return;
   }


   // use plugin manager to create instance of TFitEditor
   TPluginHandler *handler = gROOT->GetPluginManager()->FindHandler("TFitEditor");
   if (handler && handler->LoadPlugin() != -1) {
      if (handler->ExecPlugin(2, gPad, this) == 0)
         Error("FitPanel", "Unable to create the FitPanel");
   }
   else
         Error("FitPanel", "Unable to find the FitPanel plug-in");
}

//______________________________________________________________________________
TH1 *TH1::GetAsymmetry(TH1* h2, Double_t c2, Double_t dc2)
{
   //  return an histogram containing the asymmetry of this histogram with h2,
   //  where the asymmetry is defined as:
   //
   //  Asymmetry = (h1 - h2)/(h1 + h2)  where h1 = this
   //
   //  works for 1D, 2D, etc. histograms
   //  c2 is an optional argument that gives a relative weight between the two
   //  histograms, and dc2 is the error on this weight.  This is useful, for example,
   //  when forming an asymmetry between two histograms from 2 different data sets that
   //  need to be normalized to each other in some way.  The function calculates
   //  the errors asumming Poisson statistics on h1 and h2 (that is, dh = sqrt(h)).
   //
   //  example:  assuming 'h1' and 'h2' are already filled
   //
   //     h3 = h1->GetAsymmetry(h2)
   //
   //  then 'h3' is created and filled with the asymmetry between 'h1' and 'h2';
   //  h1 and h2 are left intact.
   //
   //  Note that it is the user's responsibility to manage the created histogram.
   //
   //  code proposed by Jason Seely (seely@mit.edu) and adapted by R.Brun
   //
   // clone the histograms so top and bottom will have the
   // correct dimensions:
   // Sumw2 just makes sure the errors will be computed properly
   // when we form sums and ratios below.
   Bool_t addStatus = TH1::AddDirectoryStatus();
   TH1::AddDirectory(kFALSE);
   TH1 *asym   = (TH1*)Clone();
   asym->Sumw2();
   TH1 *top    = (TH1*)asym->Clone();
   TH1 *bottom = (TH1*)asym->Clone();
   TH1::AddDirectory(addStatus);

   // form the top and bottom of the asymmetry, and then divide:
   TH1 *h1 = this;
   top->Add(h1,h2,1,-c2);
   bottom->Add(h1,h2,1,c2);
   asym->Divide(top,bottom);

   Int_t   xmax = asym->GetNbinsX();
   Int_t   ymax = asym->GetNbinsY();
   Int_t   zmax = asym->GetNbinsZ();
   Double_t bot, error, a, b, da, db;

   // now loop over bins to calculate the correct errors
   // the reason this error calculation looks complex is because of c2
   for(Int_t i=1; i<= xmax; i++){
      for(Int_t j=1; j<= ymax; j++){
         for(Int_t k=1; k<= zmax; k++){

            // here some bin contents are written into variables to make the error
            // calculation a little more legible:
            a   = h1->GetBinContent(i,j,k);
            b   = h2->GetBinContent(i,j,k);
            bot = bottom->GetBinContent(i,j,k);

            // make sure there are some events, if not, then the errors are set = 0
            // automatically.
            //if(bot < 1){} was changed to the next line from recommendation of Jason Seely (28 Nov 2005)
            if(bot < 1e-6){}
            else{
               // computation of errors by Christos Leonidopoulos
               da    = h1->GetBinError(i,j,k);
               db    = h2->GetBinError(i,j,k);
               error = 2*TMath::Sqrt(a*a*c2*c2*db*db + c2*c2*b*b*da*da+a*a*b*b*dc2*dc2)/(bot*bot);
               asym->SetBinError(i,j,k,error);
            }
         }
      }
   }
   delete top;
   delete bottom;
   return asym;
}

//______________________________________________________________________________
Int_t TH1::GetDefaultBufferSize()
{
   // static function
   // return the default buffer size for automatic histograms
   // the parameter fgBufferSize may be changed via SetDefaultBufferSize

   return fgBufferSize;
}


//______________________________________________________________________________
Bool_t TH1::GetDefaultSumw2()
{
   // static function
   // return kTRUE if TH1::Sumw2 must be called when creating new histograms.
   // see TH1::SetDefaultSumw2.

   return fgDefaultSumw2;
}


//______________________________________________________________________________
Double_t TH1::GetEntries() const
{
   // return the current number of entries

   if (fBuffer) {
      Int_t nentries = (Int_t) fBuffer[0];
      if (nentries > 0) return nentries;
   }

   return fEntries;
}

//______________________________________________________________________________
Double_t TH1::GetEffectiveEntries() const
{
   // number of effective entries of the histogram,
   // neff = (Sum of weights )^2 / (Sum of weight^2 )
   // In case of an unweighted histogram this number is equivalent to the
   // number of entries of the histogram. 
   // For a weighted histogram, this number corresponds to the hypotetical number of unweighted entries 
   // a histogram would need to have the same statistical power as this weighted histogram.
   // Note: The underflow/overflow are included if one has set the TH1::StatOverFlows flag
   // and if the statistics has been computed at filling time. 
   // If a range is set in the histogram the number is computed from the given range. 

   Stat_t s[kNstat];
   this->GetStats(s);// s[1] sum of squares of weights, s[0] sum of weights
   return (s[1] ? s[0]*s[0]/s[1] : TMath::Abs(s[0]) );
}

//______________________________________________________________________________
char *TH1::GetObjectInfo(Int_t px, Int_t py) const
{
   //   Redefines TObject::GetObjectInfo.
   //   Displays the histogram info (bin number, contents, integral up to bin
   //   corresponding to cursor position px,py
   //
   return ((TH1*)this)->GetPainter()->GetObjectInfo(px,py);
}

//______________________________________________________________________________
TVirtualHistPainter *TH1::GetPainter(Option_t *option)
{
   // return pointer to painter
   // if painter does not exist, it is created
   if (!fPainter) {
      TString opt = option;
      opt.ToLower();
      if (opt.Contains("gl") || gStyle->GetCanvasPreferGL()) {
         //try to create TGLHistPainter
         TPluginHandler *handler = gROOT->GetPluginManager()->FindHandler("TGLHistPainter");

         if (handler && handler->LoadPlugin() != -1)
            fPainter = reinterpret_cast<TVirtualHistPainter *>(handler->ExecPlugin(1, this));
      }
   }

   if (!fPainter) fPainter = TVirtualHistPainter::HistPainter(this);

   return fPainter;
}

//______________________________________________________________________________
Int_t TH1::GetQuantiles(Int_t nprobSum, Double_t *q, const Double_t *probSum)
{
   //  Compute Quantiles for this histogram
   //     Quantile x_q of a probability distribution Function F is defined as
   //
   //        F(x_q) = q with 0 <= q <= 1.
   //
   //     For instance the median x_0.5 of a distribution is defined as that value
   //     of the random variable for which the distribution function equals 0.5:
   //
   //        F(x_0.5) = Probability(x < x_0.5) = 0.5
   //
   //  code from Eddy Offermann, Renaissance
   //
   // input parameters
   //   - this 1-d histogram (TH1F,D,etc). Could also be a TProfile
   //   - nprobSum maximum size of array q and size of array probSum (if given)
   //   - probSum array of positions where quantiles will be computed.
   //     if probSum is null, probSum will be computed internally and will
   //     have a size = number of bins + 1 in h. it will correspond to the
   //      quantiles calculated at the lowest edge of the histogram (quantile=0) and
   //     all the upper edges of the bins.
   //     if probSum is not null, it is assumed to contain at least nprobSum values.
   //  output
   //   - return value nq (<=nprobSum) with the number of quantiles computed
   //   - array q filled with nq quantiles
   //
   //  Note that the Integral of the histogram is automatically recomputed
   //  if the number of entries is different of the number of entries when
   //  the integral was computed last time. In case you do not use the Fill
   //  functions to fill your histogram, but SetBinContent, you must call
   //  TH1::ComputeIntegral before calling this function.
   //
   //  Getting quantiles q from two histograms and storing results in a TGraph,
   //   a so-called QQ-plot
   //
   //     TGraph *gr = new TGraph(nprob);
   //     h1->GetQuantiles(nprob,gr->GetX());
   //     h2->GetQuantiles(nprob,gr->GetY());
   //     gr->Draw("alp");
   //
   // Example:
   //     void quantiles() {
   //        // demo for quantiles
   //        const Int_t nq = 20;
   //        TH1F *h = new TH1F("h","demo quantiles",100,-3,3);
   //        h->FillRandom("gaus",5000);
   //
   //        Double_t xq[nq];  // position where to compute the quantiles in [0,1]
   //        Double_t yq[nq];  // array to contain the quantiles
   //        for (Int_t i=0;i<nq;i++) xq[i] = Float_t(i+1)/nq;
   //        h->GetQuantiles(nq,yq,xq);
   //
   //        //show the original histogram in the top pad
   //        TCanvas *c1 = new TCanvas("c1","demo quantiles",10,10,700,900);
   //        c1->Divide(1,2);
   //        c1->cd(1);
   //        h->Draw();
   //
   //        // show the quantiles in the bottom pad
   //        c1->cd(2);
   //        gPad->SetGrid();
   //        TGraph *gr = new TGraph(nq,xq,yq);
   //        gr->SetMarkerStyle(21);
   //        gr->Draw("alp");
   //     }

   if (GetDimension() > 1) {
      Error("GetQuantiles","Only available for 1-d histograms");
      return 0;
   }

   const Int_t nbins = GetXaxis()->GetNbins();
   if (!fIntegral) ComputeIntegral();
   if (fIntegral[nbins+1] != fEntries) ComputeIntegral();

   Int_t i, ibin;
   Double_t *prob = (Double_t*)probSum;
   Int_t nq = nprobSum;
   if (probSum == 0) {
      nq = nbins+1;
      prob = new Double_t[nq];
      prob[0] = 0;
      for (i=1;i<nq;i++) {
         prob[i] = fIntegral[i]/fIntegral[nbins];
      }
   }

   for (i = 0; i < nq; i++) {
      ibin = TMath::BinarySearch(nbins,fIntegral,prob[i]);
      while (ibin < nbins-1 && fIntegral[ibin+1] == prob[i]) {
         if (fIntegral[ibin+2] == prob[i]) ibin++;
         else break;
      }
      q[i] = GetBinLowEdge(ibin+1);
      const Double_t dint = fIntegral[ibin+1]-fIntegral[ibin];
      if (dint > 0) q[i] += GetBinWidth(ibin+1)*(prob[i]-fIntegral[ibin])/dint;
   }

   if (!probSum) delete [] prob;
   return nq;
}

//______________________________________________________________________________
Int_t TH1::FitOptionsMake(Option_t *choptin, Foption_t &fitOption)
{
   //   -*-*-*-*-*-*-*Decode string choptin and fill fitOption structure*-*-*-*-*-*
   //                 ================================================

   if (choptin == 0) return 1;
   if (strlen(choptin) == 0) return 1;
   TString opt = choptin;
   opt.ToUpper();
   if (opt.Contains("Q"))  fitOption.Quiet   = 1;
   if (opt.Contains("V")) {fitOption.Verbose = 1; fitOption.Quiet = 0;}
   if (opt.Contains("X"))  fitOption.Chi2    = 1;
   if (opt.Contains("W"))  fitOption.W1      = 1;
   if (opt.Contains("WW")) fitOption.W1      = 2; //all bins have weight=1, even empty bins
   // likelihood fit options
   if (opt.Contains("L")) {
      fitOption.Like    = 1;
      //if (opt.Contains("LL")) fitOption.Like    = 2;
      if (opt.Contains("W")){ fitOption.Like    = 2;  fitOption.W1=0;}//  (weighted likelihood)
      if (opt.Contains("MULTI")) {
         if (fitOption.Like == 2) fitOption.Like = 6; // weighted multinomial
         else fitOption.Like    = 4; // multinomial likelihood fit instead of Poisson
         opt.ReplaceAll("MULTI","");
      }
   }
   if (opt.Contains("E"))  fitOption.Errors  = 1;
   if (opt.Contains("M"))  fitOption.More    = 1;
   if (opt.Contains("R"))  fitOption.Range   = 1;
   if (opt.Contains("G"))  fitOption.Gradient= 1;
   if (opt.Contains("N"))  fitOption.Nostore = 1;
   if (opt.Contains("0"))  fitOption.Nograph = 1;
   if (opt.Contains("+"))  fitOption.Plus    = 1;
   if (opt.Contains("I"))  fitOption.Integral= 1;
   if (opt.Contains("B"))  fitOption.Bound   = 1;
   if (opt.Contains("U")) {fitOption.User    = 1; fitOption.Like = 0;}
   if (opt.Contains("F"))  fitOption.Minuit = 1;
   if (opt.Contains("C"))  fitOption.Nochisq = 1;
   if (opt.Contains("S"))  fitOption.StoreResult = 1;

   return 1;
}

//______________________________________________________________________________
void H1InitGaus()
{
   //   -*-*-*-*Compute Initial values of parameters for a gaussian*-*-*-*-*-*-*
   //           ===================================================

   Double_t allcha, sumx, sumx2, x, val, rms, mean;
   Int_t bin;
   const Double_t sqrtpi = 2.506628;

   //   - Compute mean value and RMS of the histogram in the given range
   TVirtualFitter *hFitter = TVirtualFitter::GetFitter();
   TH1 *curHist = (TH1*)hFitter->GetObjectFit();
   Int_t hxfirst = hFitter->GetXfirst();
   Int_t hxlast  = hFitter->GetXlast();
   Double_t valmax  = curHist->GetBinContent(hxfirst);
   Double_t binwidx = curHist->GetBinWidth(hxfirst);
   allcha = sumx = sumx2 = 0;
   for (bin=hxfirst;bin<=hxlast;bin++) {
      x       = curHist->GetBinCenter(bin);
      val     = TMath::Abs(curHist->GetBinContent(bin));
      if (val > valmax) valmax = val;
      sumx   += val*x;
      sumx2  += val*x*x;
      allcha += val;
   }
   if (allcha == 0) return;
   mean = sumx/allcha;
   rms  = sumx2/allcha - mean*mean;
   if (rms > 0) rms  = TMath::Sqrt(rms);
   else         rms  = 0;
   if (rms == 0) rms = binwidx*(hxlast-hxfirst+1)/4;
   //if the distribution is really gaussian, the best approximation
   //is binwidx*allcha/(sqrtpi*rms)
   //However, in case of non-gaussian tails, this underestimates
   //the normalisation constant. In this case the maximum value
   //is a better approximation.
   //We take the average of both quantities
   Double_t constant = 0.5*(valmax+binwidx*allcha/(sqrtpi*rms));

   //In case the mean value is outside the histo limits and
   //the RMS is bigger than the range, we take
   //  mean = center of bins
   //  rms  = half range
   Double_t xmin = curHist->GetXaxis()->GetXmin();
   Double_t xmax = curHist->GetXaxis()->GetXmax();
   if ((mean < xmin || mean > xmax) && rms > (xmax-xmin)) {
      mean = 0.5*(xmax+xmin);
      rms  = 0.5*(xmax-xmin);
   }
   TF1 *f1 = (TF1*)hFitter->GetUserFunc();
   f1->SetParameter(0,constant);
   f1->SetParameter(1,mean);
   f1->SetParameter(2,rms);
   f1->SetParLimits(2,0,10*rms);
}

//______________________________________________________________________________
void H1InitExpo()
{
   //   -*-*-*-*Compute Initial values of parameters for an exponential*-*-*-*-*
   //           =======================================================

   Double_t constant, slope;
   Int_t ifail;
   TVirtualFitter *hFitter = TVirtualFitter::GetFitter();
   Int_t hxfirst = hFitter->GetXfirst();
   Int_t hxlast  = hFitter->GetXlast();
   Int_t nchanx  = hxlast - hxfirst + 1;

   H1LeastSquareLinearFit(-nchanx, constant, slope, ifail);

   TF1 *f1 = (TF1*)hFitter->GetUserFunc();
   f1->SetParameter(0,constant);
   f1->SetParameter(1,slope);

}

//______________________________________________________________________________
void H1InitPolynom()
{
   //   -*-*-*-*Compute Initial values of parameters for a polynom*-*-*-*-*-*-*
   //           ===================================================

   Double_t fitpar[25];

   TVirtualFitter *hFitter = TVirtualFitter::GetFitter();
   TF1 *f1 = (TF1*)hFitter->GetUserFunc();
   Int_t hxfirst = hFitter->GetXfirst();
   Int_t hxlast  = hFitter->GetXlast();
   Int_t nchanx  = hxlast - hxfirst + 1;
   Int_t npar    = f1->GetNpar();

   if (nchanx <=1 || npar == 1) {
      TH1 *curHist = (TH1*)hFitter->GetObjectFit();
      fitpar[0] = curHist->GetSumOfWeights()/Double_t(nchanx);
   } else {
      H1LeastSquareFit( nchanx, npar, fitpar);
   }
   for (Int_t i=0;i<npar;i++) f1->SetParameter(i, fitpar[i]);
}

//______________________________________________________________________________
void H1LeastSquareFit(Int_t n, Int_t m, Double_t *a)
{
   //   -*-*-*-*-*-*Least squares lpolynomial fitting without weights*-*-*-*-*-*-*
   //               =================================================
   //
   //     n   number of points to fit
   //     m   number of parameters
   //     a   array of parameters
   //
   //      based on CERNLIB routine LSQ: Translated to C++ by Rene Brun
   //      (E.Keil.  revised by B.Schorr, 23.10.1981.)
   //
   //   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
   const Double_t zero = 0.;
   const Double_t one = 1.;
   const Int_t idim = 20;

   Double_t  b[400]        /* was [20][20] */;
   Int_t i, k, l, ifail;
   Double_t power;
   Double_t da[20], xk, yk;

   if (m <= 2) {
      H1LeastSquareLinearFit(n, a[0], a[1], ifail);
      return;
   }
   if (m > idim || m > n) return;
   b[0]  = Double_t(n);
   da[0] = zero;
   for (l = 2; l <= m; ++l) {
      b[l-1]           = zero;
      b[m + l*20 - 21] = zero;
      da[l-1]          = zero;
   }
   TVirtualFitter *hFitter = TVirtualFitter::GetFitter();
   TH1 *curHist  = (TH1*)hFitter->GetObjectFit();
   Int_t hxfirst = hFitter->GetXfirst();
   Int_t hxlast  = hFitter->GetXlast();
   for (k = hxfirst; k <= hxlast; ++k) {
      xk     = curHist->GetBinCenter(k);
      yk     = curHist->GetBinContent(k);
      power  = one;
      da[0] += yk;
      for (l = 2; l <= m; ++l) {
         power   *= xk;
         b[l-1]  += power;
         da[l-1] += power*yk;
      }
      for (l = 2; l <= m; ++l) {
         power            *= xk;
         b[m + l*20 - 21] += power;
      }
   }
   for (i = 3; i <= m; ++i) {
      for (k = i; k <= m; ++k) {
         b[k - 1 + (i-1)*20 - 21] = b[k + (i-2)*20 - 21];
      }
   }
   H1LeastSquareSeqnd(m, b, idim, ifail, 1, da);

   for (i=0; i<m; ++i) a[i] = da[i];

}

//______________________________________________________________________________
void H1LeastSquareLinearFit(Int_t ndata, Double_t &a0, Double_t &a1, Int_t &ifail)
{
   //   -*-*-*-*-*-*-*-*Least square linear fit without weights*-*-*-*-*-*-*-*-*
   //                   =======================================
   //
   //      extracted from CERNLIB LLSQ: Translated to C++ by Rene Brun
   //      (added to LSQ by B. Schorr, 15.02.1982.)
   //
   //   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   Double_t xbar, ybar, x2bar;
   Int_t i, n;
   Double_t xybar;
   Double_t fn, xk, yk;
   Double_t det;

   n     = TMath::Abs(ndata);
   ifail = -2;
   xbar  = ybar  = x2bar = xybar = 0;
   TVirtualFitter *hFitter = TVirtualFitter::GetFitter();
   TH1 *curHist  = (TH1*)hFitter->GetObjectFit();
   Int_t hxfirst = hFitter->GetXfirst();
   Int_t hxlast  = hFitter->GetXlast();
   for (i = hxfirst; i <= hxlast; ++i) {
      xk = curHist->GetBinCenter(i);
      yk = curHist->GetBinContent(i);
      if (ndata < 0) {
         if (yk <= 0) yk = 1e-9;
         yk = TMath::Log(yk);
      }
      xbar  += xk;
      ybar  += yk;
      x2bar += xk*xk;
      xybar += xk*yk;
   }
   fn    = Double_t(n);
   det   = fn*x2bar - xbar*xbar;
   ifail = -1;
   if (det <= 0) {
      a0 = ybar/fn;
      a1 = 0;
      return;
   }
   ifail = 0;
   a0 = (x2bar*ybar - xbar*xybar) / det;
   a1 = (fn*xybar - xbar*ybar) / det;

}

//______________________________________________________________________________
void H1LeastSquareSeqnd(Int_t n, Double_t *a, Int_t idim, Int_t &ifail, Int_t k, Double_t *b)
{
   //   -*-*-*-*-*-*Extracted from CERN Program library routine DSEQN*-*-*-*-*-*
   //               =================================================
   //
   //           : Translated to C++ by Rene Brun
   //
   //   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
   Int_t a_dim1, a_offset, b_dim1, b_offset;
   Int_t nmjp1, i, j, l;
   Int_t im1, jp1, nm1, nmi;
   Double_t s1, s21, s22;
   const Double_t one = 1.;

   /* Parameter adjustments */
   b_dim1 = idim;
   b_offset = b_dim1 + 1;
   b -= b_offset;
   a_dim1 = idim;
   a_offset = a_dim1 + 1;
   a -= a_offset;

   if (idim < n) return;

   ifail = 0;
   for (j = 1; j <= n; ++j) {
      if (a[j + j*a_dim1] <= 0) { ifail = -1; return; }
      a[j + j*a_dim1] = one / a[j + j*a_dim1];
      if (j == n) continue;
      jp1 = j + 1;
      for (l = jp1; l <= n; ++l) {
         a[j + l*a_dim1] = a[j + j*a_dim1] * a[l + j*a_dim1];
         s1 = -a[l + (j+1)*a_dim1];
         for (i = 1; i <= j; ++i) { s1 = a[l + i*a_dim1] * a[i + (j+1)*a_dim1] + s1; }
         a[l + (j+1)*a_dim1] = -s1;
      }
   }
   if (k <= 0) return;

   for (l = 1; l <= k; ++l) {
      b[l*b_dim1 + 1] = a[a_dim1 + 1]*b[l*b_dim1 + 1];
   }
   if (n == 1) return;
   for (l = 1; l <= k; ++l) {
      for (i = 2; i <= n; ++i) {
         im1 = i - 1;
         s21 = -b[i + l*b_dim1];
         for (j = 1; j <= im1; ++j) {
            s21 = a[i + j*a_dim1]*b[j + l*b_dim1] + s21;
         }
         b[i + l*b_dim1] = -a[i + i*a_dim1]*s21;
      }
      nm1 = n - 1;
      for (i = 1; i <= nm1; ++i) {
         nmi = n - i;
         s22 = -b[nmi + l*b_dim1];
         for (j = 1; j <= i; ++j) {
            nmjp1 = n - j + 1;
            s22 = a[nmi + nmjp1*a_dim1]*b[nmjp1 + l*b_dim1] + s22;
         }
         b[nmi + l*b_dim1] = -s22;
      }
   }
}


//______________________________________________________________________________
Int_t TH1::GetBin(Int_t binx, Int_t biny, Int_t binz) const
{
   //   -*-*-*-*Return Global bin number corresponding to binx,y,z*-*-*-*-*-*-*
   //           ==================================================
   //
   //      2-D and 3-D histograms are represented with a one dimensional
   //      structure.
   //      This has the advantage that all existing functions, such as
   //        GetBinContent, GetBinError, GetBinFunction work for all dimensions.
   //
   //     In case of a TH1x, returns binx directly.
   //     see TH1::GetBinXYZ for the inverse transformation.
   //
   //      Convention for numbering bins
   //      =============================
   //      For all histogram types: nbins, xlow, xup
   //        bin = 0;       underflow bin
   //        bin = 1;       first bin with low-edge xlow INCLUDED
   //        bin = nbins;   last bin with upper-edge xup EXCLUDED
   //        bin = nbins+1; overflow bin
   //      In case of 2-D or 3-D histograms, a "global bin" number is defined.
   //      For example, assuming a 3-D histogram with binx,biny,binz, the function
   //        Int_t bin = h->GetBin(binx,biny,binz);
   //      returns a global/linearized bin number. This global bin is useful
   //      to access the bin information independently of the dimension.
   //   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
   Int_t nx, ny, nz;
   if (GetDimension() < 2) {
      nx  = fXaxis.GetNbins()+2;
      if (binx < 0)   binx = 0;
      if (binx >= nx) binx = nx-1;
      return binx;
   }
   if (GetDimension() < 3) {
      nx  = fXaxis.GetNbins()+2;
      if (binx < 0)   binx = 0;
      if (binx >= nx) binx = nx-1;
      ny  = fYaxis.GetNbins()+2;
      if (biny < 0)   biny = 0;
      if (biny >= ny) biny = ny-1;
      return  binx + nx*biny;
   }
   if (GetDimension() < 4) {
      nx  = fXaxis.GetNbins()+2;
      if (binx < 0)   binx = 0;
      if (binx >= nx) binx = nx-1;
      ny  = fYaxis.GetNbins()+2;
      if (biny < 0)   biny = 0;
      if (biny >= ny) biny = ny-1;
      nz  = fZaxis.GetNbins()+2;
      if (binz < 0)   binz = 0;
      if (binz >= nz) binz = nz-1;
      return  binx + nx*(biny +ny*binz);
   }
   return -1;
}
//______________________________________________________________________________
void TH1::GetBinXYZ(Int_t binglobal, Int_t &binx, Int_t &biny, Int_t &binz) const
{
   // return binx, biny, binz corresponding to the global bin number globalbin
   // see TH1::GetBin function above

   Int_t nx  = fXaxis.GetNbins()+2;
   Int_t ny  = fYaxis.GetNbins()+2;

   if (GetDimension() < 2) {
      binx = binglobal%nx;
      biny = -1;
      binz = -1;
   }
   if (GetDimension() < 3) {
      binx = binglobal%nx;
      biny = ((binglobal-binx)/nx)%ny;
      binz = -1;
   }
   if (GetDimension() < 4) {
      binx = binglobal%nx;
      biny = ((binglobal-binx)/nx)%ny;
      binz = ((binglobal-binx)/nx -biny)/ny;
   }
}


//______________________________________________________________________________
Double_t TH1::GetRandom() const
{
   // return a random number distributed according the histogram bin contents.
   // This function checks if the bins integral exists. If not, the integral
   // is evaluated, normalized to one.
   // The integral is automatically recomputed if the number of entries
   // is not the same then when the integral was computed.
   // NB Only valid for 1-d histograms. Use GetRandom2 or 3 otherwise.
   // If the histogram has a bin with negative content a NaN is returned 

   if (fDimension > 1) {
      Error("GetRandom","Function only valid for 1-d histograms");
      return 0;
   }
   Int_t nbinsx = GetNbinsX();
   Double_t integral = 0;
   // compute integral checking that all bins have positive content (see ROOT-5894)
   if (fIntegral) {
      if (fIntegral[nbinsx+1] != fEntries) integral = ((TH1*)this)->ComputeIntegral(true);
      else  integral = fIntegral[nbinsx];
   } else {
      integral = ((TH1*)this)->ComputeIntegral(true);
   }
   if (integral == 0) return 0;
   // return a NaN in case some bins have negative content
   if (integral == TMath::QuietNaN() ) return TMath::QuietNaN(); 

   Double_t r1 = gRandom->Rndm();
   Int_t ibin = TMath::BinarySearch(nbinsx,fIntegral,r1);
   Double_t x = GetBinLowEdge(ibin+1);
   if (r1 > fIntegral[ibin]) x +=
      GetBinWidth(ibin+1)*(r1-fIntegral[ibin])/(fIntegral[ibin+1] - fIntegral[ibin]);
   return x;
}

//______________________________________________________________________________
Double_t TH1::GetBinContent(Int_t) const
{
   //   -*-*-*-*-*Return content of bin number bin
   //             ================================
   // Implemented in TH1C,S,F,D
   //
   //      Convention for numbering bins
   //      =============================
   //      For all histogram types: nbins, xlow, xup
   //        bin = 0;       underflow bin
   //        bin = 1;       first bin with low-edge xlow INCLUDED
   //        bin = nbins;   last bin with upper-edge xup EXCLUDED
   //        bin = nbins+1; overflow bin
   //      In case of 2-D or 3-D histograms, a "global bin" number is defined.
   //      For example, assuming a 3-D histogram with binx,biny,binz, the function
   //        Int_t bin = h->GetBin(binx,biny,binz);
   //      returns a global/linearized bin number. This global bin is useful
   //      to access the bin information independently of the dimension.

   AbstractMethod("GetBinContent");
   return 0;
}

//______________________________________________________________________________
Double_t TH1::GetBinContent(Int_t binx, Int_t biny) const
{
   //   -*-*-*-*-*Return content of bin number binx, biny
   //             =======================================
   // NB: Function to be called for 2-D histograms only
   // see convention for numbering bins in TH1::GetBin

   Int_t bin = GetBin(binx,biny);
   return GetBinContent(bin);
}

//______________________________________________________________________________
Double_t TH1::GetBinContent(Int_t binx, Int_t biny, Int_t binz) const
{
   //   -*-*-*-*-*Return content of bin number binx,biny,binz
   //             ===========================================
   // NB: Function to be called for 3-D histograms only
   // see convention for numbering bins in TH1::GetBin

   Int_t bin = GetBin(binx,biny,binz);
   return GetBinContent(bin);
}

//______________________________________________________________________________
Double_t TH1::GetBinWithContent(Double_t c, Int_t &binx, Int_t firstx, Int_t lastx,Double_t maxdiff) const
{
   // compute first binx in the range [firstx,lastx] for which
   // diff = abs(bin_content-c) <= maxdiff
   // In case several bins in the specified range with diff=0 are found
   // the first bin found is returned in binx.
   // In case several bins in the specified range satisfy diff <=maxdiff
   // the bin with the smallest difference is returned in binx.
   // In all cases the function returns the smallest difference.
   //
   // NOTE1: if firstx <= 0, firstx is set to bin 1
   //        if (lastx < firstx then firstx is set to the number of bins
   //        ie if firstx=0 and lastx=0 (default) the search is on all bins.
   // NOTE2: if maxdiff=0 (default), the first bin with content=c is returned.

   if (fDimension > 1) {
      binx = 0;
      Error("GetBinWithContent","function is only valid for 1-D histograms");
      return 0;
   }
   if (firstx <= 0) firstx = 1;
   if (lastx < firstx) lastx = fXaxis.GetNbins();
   Int_t binminx = 0;
   Double_t diff, curmax = 1.e240;
   for (Int_t i=firstx;i<=lastx;i++) {
      diff = TMath::Abs(GetBinContent(i)-c);
      if (diff <= 0) {binx = i; return diff;}
      if (diff < curmax && diff <= maxdiff) {curmax = diff, binminx=i;}
   }
   binx = binminx;
   return curmax;
}

//______________________________________________________________________________
TAxis *TH1::GetXaxis() const
{
   // return a pointer to the X axis object

   return &((TH1*)this)->fXaxis;
}


//______________________________________________________________________________
TAxis *TH1::GetYaxis() const
{
   // return a pointer to the Y axis object

   return &((TH1*)this)->fYaxis;
}
//______________________________________________________________________________
TAxis *TH1::GetZaxis() const
{
   // return a pointer to the Z axis object

   return &((TH1*)this)->fZaxis;
}

//______________________________________________________________________________
Int_t TH1::GetXHighlightBin() const
{
   // Return X highlighted bin for the histogram

   return fPainter ? fPainter->GetXHighlightBin() : -1;
}

//______________________________________________________________________________
Int_t TH1::GetYHighlightBin() const
{
   // Return Y highlighted bin for the histogram

   return fPainter ? fPainter->GetYHighlightBin() : -1;
}

//______________________________________________________________________________
void TH1::SetHighlight(Bool_t highlight)
{
   // Set highlight (enable/disble) mode for the histogram
   // by default highlight mode is disable

   if (fDimension > 2) {
      Warning("SetHighlight", "Supported only 1-D and 2-D histograms");
      return;
   }

//   SetBit(kIsHighlight, highlight);
//   ((TH1 *)this)->GetPainter()->SetHighlight();

      if (!fPainter) {
         Warning("SetHighlight", "Only after draw histogram");
         return;
      }

      SetBit(kIsHighlight, highlight);
      fPainter->SetHighlight();
}

//______________________________________________________________________________
Double_t TH1::Interpolate(Double_t x)
{
   // Given a point x, approximates the value via linear interpolation
   // based on the two nearest bin centers
   // Andy Mastbaum 10/21/08

   Int_t xbin = FindBin(x);
   Double_t x0,x1,y0,y1;

   if(x<=GetBinCenter(1)) {
      return GetBinContent(1);
   } else if(x>=GetBinCenter(GetNbinsX())) {
      return GetBinContent(GetNbinsX());
   } else {
      if(x<=GetBinCenter(xbin)) {
         y0 = GetBinContent(xbin-1);
         x0 = GetBinCenter(xbin-1);
         y1 = GetBinContent(xbin);
         x1 = GetBinCenter(xbin);
      } else {
         y0 = GetBinContent(xbin);
         x0 = GetBinCenter(xbin);
         y1 = GetBinContent(xbin+1);
         x1 = GetBinCenter(xbin+1);
      }
      return y0 + (x-x0)*((y1-y0)/(x1-x0));
   }
}

//______________________________________________________________________________
Double_t TH1::Interpolate(Double_t, Double_t)
{

   //Not yet implemented
   Error("Interpolate","This function must be called with 1 argument for a TH1");
   return 0;
}

//______________________________________________________________________________
Double_t TH1::Interpolate(Double_t, Double_t, Double_t)
{

   //Not yet implemented
   Error("Interpolate","This function must be called with 1 argument for a TH1");
   return 0;
}

//______________________________________________________________________________
Bool_t TH1::IsBinOverflow(Int_t bin) const
{

   // Return true if the bin is overflow.
   Int_t binx, biny, binz;
   GetBinXYZ(bin, binx, biny, binz);

   if ( fDimension == 1 )
      return binx >= GetNbinsX() + 1;
   else if ( fDimension == 2 )
      return (binx >= GetNbinsX() + 1) ||
             (biny >= GetNbinsY() + 1);
   else if ( fDimension == 3 )
      return (binx >= GetNbinsX() + 1) ||
             (biny >= GetNbinsY() + 1) ||
             (binz >= GetNbinsZ() + 1);
   else
      return 0;
}


//______________________________________________________________________________
Bool_t TH1::IsBinUnderflow(Int_t bin) const
{

   // Return true if the bin is overflow.
   Int_t binx, biny, binz;
   GetBinXYZ(bin, binx, biny, binz);

   if ( fDimension == 1 )
      return (binx <= 0);
   else if ( fDimension == 2 )
      return (binx <= 0 || biny <= 0);
   else if ( fDimension == 3 )
      return (binx <= 0 || biny <= 0 || binz <= 0);
   else
      return 0;
}

//___________________________________________________________________________
void TH1::LabelsDeflate(Option_t *ax)
{
   // Reduce the number of bins for the axis passed in the option to the number of bins having a label.
   // The method will remove only the extra bins existing after the last "labeled" bin.
   // Note that if there are "un-labeled" bins present between "labeled" bins they will not be removed

   Int_t iaxis = AxisChoice(ax);
   TAxis *axis = 0;
   if (iaxis == 1) axis = GetXaxis();
   if (iaxis == 2) axis = GetYaxis();
   if (iaxis == 3) axis = GetZaxis();
   if (!axis) {
      Error("LabelsDeflate","Invalid axis option %s",ax);
      return;
   }
   if (!axis->GetLabels()) return;

   // find bin with last labels
   // bin number is object ID in list of labels
   // therefore max bin number is number of bins of the deflated histograms
   TIter next(axis->GetLabels());
   TObject *obj;
   Int_t nbins = 0;
   while ((obj = next())) {
      Int_t ibin = obj->GetUniqueID();
      if (ibin > nbins) nbins = ibin;
   }
   if (nbins < 1) nbins = 1;
   TH1 *hold = (TH1*)IsA()->New();
   R__ASSERT(hold);
   hold->SetDirectory(0);
   Copy(*hold);

   Bool_t timedisp = axis->GetTimeDisplay();
   Double_t xmin = axis->GetXmin();
   Double_t xmax = axis->GetBinUpEdge(nbins);
   if (xmax <= xmin) xmax = xmin +nbins;
   axis->SetRange(0,0);
   axis->Set(nbins,xmin,xmax);
   SetBinsLength(-1);  // reset the number of cells
   Int_t errors = fSumw2.fN;
   if (errors) fSumw2.Set(fNcells);
   axis->SetTimeDisplay(timedisp);
   // reset histogram content
   Reset("ICE");

   //now loop on all bins and refill
   // NOTE that if the bins without labels have content
   // it will be put in the underflow/overflow.
   // For this reason we use AddBinContent method
   Double_t oldEntries = fEntries;
   Int_t bin,binx,biny,binz;
   for (bin=0; bin < hold->fNcells; ++bin) {
      hold->GetBinXYZ(bin,binx,biny,binz);
      Int_t ibin = GetBin(binx,biny,binz);
      Double_t cu = hold->GetBinContent(bin);
      AddBinContent(ibin,cu);
      if (errors) {
         fSumw2.fArray[ibin] += hold->fSumw2.fArray[bin];
      }
   }
   fEntries = oldEntries;
   delete hold;
}

//______________________________________________________________________________
void TH1::LabelsInflate(Option_t *ax)
{
   // Double the number of bins for axis.
   // Refill histogram
   // This function is called by TAxis::FindBin(const char *label)

   Int_t iaxis = AxisChoice(ax);
   TAxis *axis = 0;
   if (iaxis == 1) axis = GetXaxis();
   if (iaxis == 2) axis = GetYaxis();
   if (iaxis == 3) axis = GetZaxis();
   if (!axis) return;

   TH1 *hold = (TH1*)IsA()->New();;
   hold->SetDirectory(0);
   Copy(*hold);

   Bool_t timedisp = axis->GetTimeDisplay();
   Int_t  nbxold = fXaxis.GetNbins();
   Int_t  nbyold = fYaxis.GetNbins();
   Int_t  nbzold = fZaxis.GetNbins();
   Int_t nbins   = axis->GetNbins();
   Double_t xmin = axis->GetXmin();
   Double_t xmax = axis->GetXmax();
   xmax = xmin + 2*(xmax-xmin);
   axis->SetRange(0,0);
   // double the bins and recompute ncells
   axis->Set(2*nbins,xmin,xmax);
   SetBinsLength(-1);
   Int_t errors = fSumw2.fN;
   if (errors) fSumw2.Set(fNcells);
   axis->SetTimeDisplay(timedisp);

   Reset("ICE");  // reset content and error

   //now loop on all bins and refill
   Double_t oldEntries = fEntries;
   Int_t bin,ibin,binx,biny,binz;
   for (ibin =0; ibin < fNcells; ibin++) {
      GetBinXYZ(ibin,binx,biny,binz);
      bin = hold->GetBin(binx,biny,binz);
      // NOTE that overflow in hold will be not considered
      if (binx > nbxold  || biny > nbyold || binz > nbzold) bin = -1;
      if (bin > 0)  {
         Double_t cu  = hold->GetBinContent(bin);
         AddBinContent(ibin,cu);
         if (errors) fSumw2.fArray[ibin] += hold->fSumw2.fArray[bin];
      }
   }
   fEntries = oldEntries;
   delete hold;
}

//______________________________________________________________________________
void TH1::LabelsOption(Option_t *option, Option_t *ax)
{
   //  Set option(s) to draw axis with labels
   //  option = "a" sort by alphabetic order
   //         = ">" sort by decreasing values
   //         = "<" sort by increasing values
   //         = "h" draw labels horizontal
   //         = "v" draw labels vertical
   //         = "u" draw labels up (end of label right adjusted)
   //         = "d" draw labels down (start of label left adjusted)

   Int_t iaxis = AxisChoice(ax);
   TAxis *axis = 0;
   if (iaxis == 1) axis = GetXaxis();
   if (iaxis == 2) axis = GetYaxis();
   if (iaxis == 3) axis = GetZaxis();
   if (!axis) return;
   THashList *labels = axis->GetLabels();
   if (!labels) {
      Warning("LabelsOption","Cannot sort. No labels");
      return;
   }
   TString opt = option;
   opt.ToLower();
   if (opt.Contains("h")) {
      axis->SetBit(TAxis::kLabelsHori);
      axis->ResetBit(TAxis::kLabelsVert);
      axis->ResetBit(TAxis::kLabelsDown);
      axis->ResetBit(TAxis::kLabelsUp);
   }
   if (opt.Contains("v")) {
      axis->SetBit(TAxis::kLabelsVert);
      axis->ResetBit(TAxis::kLabelsHori);
      axis->ResetBit(TAxis::kLabelsDown);
      axis->ResetBit(TAxis::kLabelsUp);
   }
   if (opt.Contains("u")) {
      axis->SetBit(TAxis::kLabelsUp);
      axis->ResetBit(TAxis::kLabelsVert);
      axis->ResetBit(TAxis::kLabelsDown);
      axis->ResetBit(TAxis::kLabelsHori);
   }
   if (opt.Contains("d")) {
      axis->SetBit(TAxis::kLabelsDown);
      axis->ResetBit(TAxis::kLabelsVert);
      axis->ResetBit(TAxis::kLabelsHori);
      axis->ResetBit(TAxis::kLabelsUp);
   }
   Int_t sort = -1;
   if (opt.Contains("a")) sort = 0;
   if (opt.Contains(">")) sort = 1;
   if (opt.Contains("<")) sort = 2;
   if (sort < 0) return;
   if (sort > 0 && GetDimension() > 2) {
      Error("LabelsOption","Sorting by value not implemented for 3-D histograms");
      return;
   }

   Double_t entries = fEntries;
   Int_t n = TMath::Min(axis->GetNbins(), labels->GetSize());
   Int_t *a = new Int_t[n+2];

   Int_t i,j,k;
   Double_t *cont   = 0;
   Double_t *errors = 0;
   THashList *labold = new THashList(labels->GetSize(),1);
   TIter nextold(labels);
   TObject *obj;
   while ((obj=nextold())) {
      labold->Add(obj);
   }
   labels->Clear();
   if (sort > 0) {
      //---sort by values of bins
      if (GetDimension() == 1) {
         cont = new Double_t[n];
         if (fSumw2.fN) errors = new Double_t[n];
         for (i=1;i<=n;i++) {
            cont[i-1] = GetBinContent(i);
            if (errors) errors[i-1] = GetBinError(i);
         }
         if (sort ==1) TMath::Sort(n,cont,a,kTRUE);  //sort by decreasing values
         else          TMath::Sort(n,cont,a,kFALSE); //sort by increasing values
         for (i=1;i<=n;i++) {
            SetBinContent(i,cont[a[i-1]]);
            if (errors) SetBinError(i,errors[a[i-1]]);
         }
         for (i=1;i<=n;i++) {
            obj = labold->At(a[i-1]);
            labels->Add(obj);
            obj->SetUniqueID(i);
         }
      } else if (GetDimension()== 2) {
         Double_t *pcont = new Double_t[n+2];
         for (i=0;i<=n;i++) pcont[i] = 0;
         Int_t nx = fXaxis.GetNbins();
         Int_t ny = fYaxis.GetNbins();
         cont = new Double_t[(nx+2)*(ny+2)];
         if (fSumw2.fN) errors = new Double_t[(nx+2)*(ny+2)];
         for (i=1;i<=nx;i++) {
            for (j=1;j<=ny;j++) {
               cont[i+nx*j] = GetBinContent(i,j);
               if (errors) errors[i+nx*j] = GetBinError(i,j);
               if (axis == GetXaxis()) k = i;
               else                    k = j;
               pcont[k-1] += cont[i+nx*j];
            }
         }
         if (sort ==1) TMath::Sort(n,pcont,a,kTRUE);  //sort by decreasing values
         else          TMath::Sort(n,pcont,a,kFALSE); //sort by increasing values
         for (i=0;i<n;i++) {
            obj = labold->At(a[i]);
            labels->Add(obj);
            obj->SetUniqueID(i+1);
         }
         delete [] pcont;
         if (axis == GetXaxis()) {
            for (i=1;i<=n;i++) {
               for (j=1;j<=ny;j++) {
                  SetBinContent(i,j,cont[a[i-1]+1+nx*j]);
                  if (errors) SetBinError(i,j,errors[a[i-1]+1+nx*j]);
               }
            }
         }
         else {
            // using y axis
            for (i=1;i<=nx;i++) {
               for (j=1;j<=n;j++) {
                  SetBinContent(i,j,cont[i+nx*(a[j-1]+1)]);
                  if (errors) SetBinError(i,j,errors[i+nx*(a[j-1]+1)]);
               }
            }
         }
      } else {
         //to be implemented for 3d
      }
   } else {
      //---alphabetic sort
      const UInt_t kUsed = 1<<18;
      TObject *objk=0;
      a[0] = 0;
      a[n+1] = n+1;
      for (i=1;i<=n;i++) {
         const char *label = "zzzzzzzzzzzz";
         for (j=1;j<=n;j++) {
            obj = labold->At(j-1);
            if (!obj) continue;
            if (obj->TestBit(kUsed)) continue;
            //use strcasecmp for case non-sensitive sort (may be an option)
            if (strcmp(label,obj->GetName()) < 0) continue;
            objk = obj;
            a[i] = j;
            label = obj->GetName();
         }
         if (objk) {
            objk->SetUniqueID(i);
            labels->Add(objk);
            objk->SetBit(kUsed);
         }
      }
      for (i=1;i<=n;i++) {
         obj = labels->At(i-1);
         if (!obj) continue;
         obj->ResetBit(kUsed);
      }

      if (GetDimension() == 1) {
         cont = new Double_t[n+2];
         if (fSumw2.fN) errors = new Double_t[n+2];
         for (i=1;i<=n;i++) {
            cont[i] = GetBinContent(a[i]);
            if (errors) errors[i] = GetBinError(a[i]);
         }
         for (i=1;i<=n;i++) {
            SetBinContent(i,cont[i]);
            if (errors) SetBinError(i,errors[i]);
         }
      } else if (GetDimension()== 2) {
         Int_t nx = fXaxis.GetNbins()+2;
         Int_t ny = fYaxis.GetNbins()+2;
         cont = new Double_t[nx*ny];
         if (fSumw2.fN) errors = new Double_t[nx*ny];
         for (i=0;i<nx;i++) {
            for (j=0;j<ny;j++) {
               cont[i+nx*j] = GetBinContent(i,j);
               if (errors) errors[i+nx*j] = GetBinError(i,j);
            }
         }
         if (axis == GetXaxis()) {
            for (i=1;i<=n;i++) {
               for (j=0;j<ny;j++) {
                  SetBinContent(i,j,cont[a[i]+nx*j]);
                  if (errors) SetBinError(i,j,errors[a[i]+nx*j]);
               }
            }
         } else {
            for (i=0;i<nx;i++) {
               for (j=1;j<=n;j++) {
                  SetBinContent(i,j,cont[i+nx*a[j]]);
                  if (errors) SetBinError(i,j,errors[i+nx*a[j]]);
               }
            }
         }
      } else {
         Int_t nx = fXaxis.GetNbins()+2;
         Int_t ny = fYaxis.GetNbins()+2;
         Int_t nz = fZaxis.GetNbins()+2;
         cont = new Double_t[nx*ny*nz];
         if (fSumw2.fN) errors = new Double_t[nx*ny*nz];
         for (i=0;i<nx;i++) {
            for (j=0;j<ny;j++) {
               for (k=0;k<nz;k++) {
                  cont[i+nx*(j+ny*k)] = GetBinContent(i,j,k);
                  if (errors) errors[i+nx*(j+ny*k)] = GetBinError(i,j,k);
               }
            }
         }
         if (axis == GetXaxis()) {
            // labels on x axis
            for (i=1;i<=n;i++) {
               for (j=0;j<ny;j++) {
                  for (k=0;k<nz;k++) {
                     SetBinContent(i,j,k,cont[a[i]+nx*(j+ny*k)]);
                     if (errors) SetBinError(i,j,k,errors[a[i]+nx*(j+ny*k)]);
                  }
               }
            }
         }
         else if (axis == GetYaxis()) {
            // labels on y axis
            for (i=0;i<nx;i++) {
               for (j=1;j<=n;j++) {
                  for (k=0;k<nz;k++) {
                     SetBinContent(i,j,k,cont[i+nx*(a[j]+ny*k)]);
                     if (errors) SetBinError(i,j,k,errors[i+nx*(a[j]+ny*k)]);
                  }
               }
            }
         }
         else {
            // labels on z axis
            for (i=0;i<nx;i++) {
               for (j=0;j<ny;j++) {
                  for (k=1;k<=n;k++) {
                     SetBinContent(i,j,k,cont[i+nx*(j+ny*a[k])]);
                     if (errors) SetBinError(i,j,k,errors[i+nx*(j+ny*a[k])]);
                  }
               }
            }
         }
      }
   }
   fEntries = entries;
   delete labold;
   if (a)      delete [] a;
   if (cont)   delete [] cont;
   if (errors) delete [] errors;
}

//______________________________________________________________________________
static inline Bool_t AlmostEqual(Double_t a, Double_t b, Double_t epsilon = 0.00000001)
{
   return TMath::Abs(a - b) < epsilon;
}

//______________________________________________________________________________
static inline Bool_t AlmostInteger(Double_t a, Double_t epsilon = 0.00000001)
{
   return AlmostEqual(a - TMath::Floor(a), 0, epsilon) ||
      AlmostEqual(a - TMath::Floor(a), 1, epsilon);
}

//______________________________________________________________________________
Bool_t TH1::SameLimitsAndNBins(const TAxis& axis1, const TAxis& axis2)
{
   // Same limits and bins.

   if ((axis1.GetNbins() == axis2.GetNbins())
      && (axis1.GetXmin() == axis2.GetXmin())
      && (axis1.GetXmax() == axis2.GetXmax()))
      return kTRUE;
   else
      return kFALSE;
}

static inline bool IsEquidistantBinning(const TAxis& axis)
{
   // check if axis bin are equals
   if (!axis.GetXbins()->fN) return true;  //  
   // not able to check if there is only one axis entry
   bool isEquidistant = true;
   const Double_t firstBinWidth = axis.GetBinWidth(1);
   for (int i = 1; i < axis.GetNbins(); ++i) {
      const Double_t binWidth = axis.GetBinWidth(i);
      const bool match = TMath::AreEqualRel(firstBinWidth, binWidth, TMath::Limits<Double_t>::Epsilon());
      isEquidistant &= match;
      if (!match)
         break;
   }
   return isEquidistant;
}

//______________________________________________________________________________
Bool_t TH1::RecomputeAxisLimits(TAxis& destAxis, const TAxis& anAxis)
{
   // Finds new limits for the axis for the Merge function.
   // returns false if the limits are incompatible
   if (SameLimitsAndNBins(destAxis, anAxis))
      return kTRUE;

   if (!IsEquidistantBinning(destAxis) || !IsEquidistantBinning(anAxis))
      return kFALSE;       // not equidistant user binning not supported

   Double_t width1 = destAxis.GetBinWidth(0);
   Double_t width2 = anAxis.GetBinWidth(0);
   if (width1 == 0 || width2 == 0)
      return kFALSE;       // no binning not supported

   Double_t xmin = TMath::Min(destAxis.GetXmin(), anAxis.GetXmin());
   Double_t xmax = TMath::Max(destAxis.GetXmax(), anAxis.GetXmax());
   Double_t width = TMath::Max(width1, width2);

   // check the bin size
   if (!AlmostInteger(width/width1) || !AlmostInteger(width/width2))
      return kFALSE;

   // std::cout << "Find new limit using given axis " << anAxis.GetXmin() << " , " <<  anAxis.GetXmax() << " bin width " << width2 << std::endl;
   // std::cout << "           and destination axis " << destAxis.GetXmin() << " , " <<  destAxis.GetXmax() << " bin width " << width1 << std::endl;


   // check the limits
   Double_t delta;
   delta = (destAxis.GetXmin() - xmin)/width1;
   if (!AlmostInteger(delta))
      xmin -= (TMath::Ceil(delta) - delta)*width1;

   delta = (anAxis.GetXmin() - xmin)/width2;
   if (!AlmostInteger(delta))
      xmin -= (TMath::Ceil(delta) - delta)*width2;


   delta = (destAxis.GetXmin() - xmin)/width1;
   if (!AlmostInteger(delta))
      return kFALSE;


   delta = (xmax - destAxis.GetXmax())/width1;
   if (!AlmostInteger(delta))
      xmax += (TMath::Ceil(delta) - delta)*width1;


   delta = (xmax - anAxis.GetXmax())/width2;
   if (!AlmostInteger(delta))
      xmax += (TMath::Ceil(delta) - delta)*width2;


   delta = (xmax - destAxis.GetXmax())/width1;
   if (!AlmostInteger(delta))
      return kFALSE;
#ifdef DEBUG
   if (!AlmostInteger((xmax - xmin) / width)) {   // unnecessary check
      printf("TH1::RecomputeAxisLimits - Impossible\n");
      return kFALSE;
   }
#endif


   destAxis.Set(TMath::Nint((xmax - xmin)/width), xmin, xmax);

   //std::cout << "New re-computed axis : [ " << xmin << " , " << xmax << " ] width = " << width << " nbins " << destAxis.GetNbins() << std::endl;
   
   return kTRUE;
}

//______________________________________________________________________________
Long64_t TH1::Merge(TCollection *li)
{
   // Add all histograms in the collection to this histogram.
   // This function computes the min/max for the x axis,
   // compute a new number of bins, if necessary,
   // add bin contents, errors and statistics.
   // If all histograms have bin labels, bins with identical labels
   // will be merged, no matter what their order is.
   // If overflows are present and limits are different the function will fail.
   // The function returns the total number of entries in the result histogram
   // if the merge is successful, -1 otherwise.
   //
   // IMPORTANT remark. The axis x may have different number
   // of bins and different limits, BUT the largest bin width must be
   // a multiple of the smallest bin width and the upper limit must also
   // be a multiple of the bin width.
   // Example:
   // void atest() {
   //    TH1F *h1 = new TH1F("h1","h1",110,-110,0);
   //    TH1F *h2 = new TH1F("h2","h2",220,0,110);
   //    TH1F *h3 = new TH1F("h3","h3",330,-55,55);
   //    TRandom r;
   //    for (Int_t i=0;i<10000;i++) {
   //       h1->Fill(r.Gaus(-55,10));
   //       h2->Fill(r.Gaus(55,10));
   //       h3->Fill(r.Gaus(0,10));
   //    }
   //
   //    TList *list = new TList;
   //    list->Add(h1);
   //    list->Add(h2);
   //    list->Add(h3);
   //    TH1F *h = (TH1F*)h1->Clone("h");
   //    h->Reset();
   //    h->Merge(list);
   //    h->Draw();
   // }

   if (!li) return 0;
   if (li->IsEmpty()) return (Long64_t) GetEntries();

   // is this really needed ?
   TList inlist;
   inlist.AddAll(li);


   TAxis newXAxis;

   Bool_t initialLimitsFound = kFALSE;
   Bool_t allHaveLabels = kTRUE;  // assume all histo have labels and check later
   Bool_t allHaveLimits = kTRUE;
   Bool_t allSameLimits = kTRUE;
   Bool_t foundLabelHist = kFALSE;
   //Bool_t firstHistWithLimits = kTRUE;


   TIter next(&inlist);
   // start looping with this histogram
   TH1 * h = this;

   do  {
      // do not skip anymore empty histograms 
      // since are used to set the limits 
      Bool_t hasLimits = h->GetXaxis()->GetXmin() < h->GetXaxis()->GetXmax();
      allHaveLimits = allHaveLimits && hasLimits;

      if (hasLimits) {
         h->BufferEmpty();

         // this is done in case the first histograms are empty and
         // the histogram have different limits
#ifdef LATER
         if (firstHistWithLimits ) {
            // set axis limits in the case the first histogram did not have limits 
            if (h != this && !SameLimitsAndNBins( fXaxis, *h->GetXaxis()) ) {
              if (h->GetXaxis()->GetXbins()->GetSize() != 0) fXaxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXbins()->GetArray());
              else                                           fXaxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax());
            }
            firstHistWithLimits = kFALSE;
         }
#endif

         // this is executed the first time an histogram with limits is found
         // to set some initial values on the new axis
         if (!initialLimitsFound) {
            initialLimitsFound = kTRUE;
            if (h->GetXaxis()->GetXbins()->GetSize() != 0) newXAxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXbins()->GetArray());
            else                                           newXAxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax());
         }
         else {
            // check first if histograms have same bins
            if (!SameLimitsAndNBins(newXAxis, *(h->GetXaxis())) ) {
               allSameLimits = kFALSE;
               // recompute the limits in this case the optimal limits
               // The condition to works is that the histogram have same bin with
               // and one common bin edge
               if (!RecomputeAxisLimits(newXAxis, *(h->GetXaxis()))) {
                  Error("Merge", "Cannot merge histograms - limits are inconsistent:\n "
                        "first: (%d, %f, %f), second: (%d, %f, %f)",
                        newXAxis.GetNbins(), newXAxis.GetXmin(), newXAxis.GetXmax(),
                        h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXmin(),
                        h->GetXaxis()->GetXmax());
                  return -1;
               }
            }
         }
      }
      if (allHaveLabels) {
         THashList* hlabels=h->GetXaxis()->GetLabels();
         Bool_t haveOneLabel = (hlabels != 0);
         // do here to print message only one time
         if (foundLabelHist && allHaveLabels && !haveOneLabel) {
            Warning("Merge","Not all histograms have labels. I will ignore labels,"
            " falling back to bin numbering mode.");
         }

         allHaveLabels &= (haveOneLabel);
         // for the error message
         if (haveOneLabel) foundLabelHist = kTRUE;
         // I could add a check if histogram contains bins without a label
         // and with non-zero bin content
         // Do we want to support this ???
         // only in case the kCanRebin bit is not set
         if (allHaveLabels && !h->TestBit(TH1::kCanRebin) ) {
            // count number of bins with non-null content
            Int_t non_zero_bins = 0;
            Int_t nbins = h->GetXaxis()->GetNbins();
            if (nbins > hlabels->GetEntries() ) {
               for (Int_t i = 1; i <= nbins; i++) {
                  if (h->GetBinContent(i) != 0 || (fSumw2.fN && h->GetBinError(i) != 0) ) {
                     non_zero_bins++;
                  }
               }
               if (non_zero_bins > hlabels->GetEntries() ) {
                  Warning("Merge","Histogram %s contains non-empty bins without labels - falling back to bin numbering mode",h->GetName() );
                  allHaveLabels = kFALSE;
               }
            }
         }
      }
   }    while ( ( h = dynamic_cast<TH1*> ( next() ) ) != NULL );

   if (!h && (*next) ) {
      Error("Merge","Attempt to merge object of class: %s to a %s",
            (*next)->ClassName(),this->ClassName());
      return -1;
   }


   next.Reset();
   // In the case of histogram with different limits
   // newXAxis will now have the new found limits
   // but one needs first to clone this histogram to perform the merge
   // The clone is not needed when all histograms have the same limits
   TH1 * hclone = 0;
   if (!allSameLimits) {
      // We don't want to add the clone to gDirectory,
      // so remove our kMustCleanup bit temporarily
      Bool_t mustCleanup = TestBit(kMustCleanup);
      if (mustCleanup) ResetBit(kMustCleanup);
      hclone = (TH1*)IsA()->New();
      hclone->SetDirectory(0);
      Copy(*hclone);
      if (mustCleanup) SetBit(kMustCleanup);
      BufferEmpty(1);         // To remove buffer.
      Reset();                // BufferEmpty sets limits so we can't use it later.
      SetEntries(0);
      inlist.AddFirst(hclone);
   }

   // set the binning and cell content on the histogram to merge when the histograms do not have the same binning 
   // and when one of the histogram does not have limits
   if (initialLimitsFound && (!allSameLimits || !allHaveLimits )) {
     if (newXAxis.GetXbins()->GetSize() != 0) SetBins(newXAxis.GetNbins(), newXAxis.GetXbins()->GetArray());
     else                                     SetBins(newXAxis.GetNbins(), newXAxis.GetXmin(), newXAxis.GetXmax());
   }

   // std::cout << "Merging on histogram " << GetName() << std::endl;
   // std::cout << "Merging flags : allHaveLimits - allHaveLabels - initialLimitsFound - allSameLimits " << std::endl;
   // std::cout << "                 " << allHaveLimits << "\t\t" << allHaveLabels << "\t\t" <<  initialLimitsFound << "\t\t" <<  allSameLimits << std::endl;


   if (!allHaveLimits && !allHaveLabels) {
      // fill this histogram with all the data from buffers of histograms without limits
      while (TH1* hist = (TH1*)next()) {
         // support also case where some histogram have limits and some have the buffer
         if ( (hist->GetXaxis()->GetXmin() >= hist->GetXaxis()->GetXmax() ) && hist->fBuffer  ) {
            // no limits
            Int_t nbentries = (Int_t)hist->fBuffer[0];
            for (Int_t i = 0; i < nbentries; i++)
               Fill(hist->fBuffer[2*i + 2], hist->fBuffer[2*i + 1]);
            // Entries from buffers have to be filled one by one
            // because FillN doesn't resize histograms.
         }
      }

      // all histograms have been processed
      if (!initialLimitsFound ) {
         // here the case where all histograms don't have limits
         // In principle I should not have copied in hclone since
         // when initialLimitsFound = false then allSameLimits should be  true
         if (hclone) {
            inlist.Remove(hclone);
            delete hclone;
         }
         return (Long64_t) GetEntries();
      }

      // In case some of the histograms do not have limits 
      // I need to remove the buffer 
      if (fBuffer) BufferEmpty(1); 

      next.Reset();
   }

   //merge bin contents and errors
   // in case when histogram have limits

   Double_t stats[kNstat], totstats[kNstat];
   for (Int_t i=0;i<kNstat;i++) {totstats[i] = stats[i] = 0;}
   GetStats(totstats);
   Double_t nentries = GetEntries();
   Bool_t canRebin=TestBit(kCanRebin);
   // reset, otherwise setting the under/overflow will rebin and make a mess
   if (!allHaveLabels) ResetBit(kCanRebin);
   while (TH1* hist=(TH1*)next()) {
      // process only if the histogram has limits; otherwise it was processed before      
      // in the case of an existing buffer (see if statement just before)

      //std::cout << "merging histogram " << GetName() << " with " << hist->GetName() << std::endl;

      // skip empty histograms 
      Double_t histEntries = hist->GetEntries();
      if (hist->fTsumw == 0 && histEntries == 0) continue;


      // merge for labels or histogram with limits 
      if (allHaveLabels || (hist->GetXaxis()->GetXmin() < hist->GetXaxis()->GetXmax()) ) {
         // import statistics
         hist->GetStats(stats);
         for (Int_t i=0;i<kNstat;i++)
            totstats[i] += stats[i];
         nentries += histEntries;


         Int_t nx = hist->GetXaxis()->GetNbins();
         // loop on bins of the histogram and do the merge
         for (Int_t binx = 0; binx <= nx + 1; binx++) {
            Double_t cu = hist->GetBinContent(binx);
            Double_t error1 = 0;
            Int_t ix = -1;
            if (fSumw2.fN) error1= hist->GetBinError(binx);
            // do only for bins with non null bin content or non-null errors (if Sumw2)
            if (TMath::Abs(cu) > 0 || (fSumw2.fN && error1 > 0 ) ) {
               // case  of overflow bins
               // they do not make sense also in the case of labels
               if (!allHaveLabels) {
                  // case of bins without labels
                  if (!allSameLimits)  {
                     if ( binx==0 || binx== nx+1) {
                        Error("Merge", "Cannot merge histograms - the histograms have"
                              " different limits and undeflows/overflows are present."
                              " The initial histogram is now broken!");
                        return -1;
                     }
                     // NOTE: in the case of one of the histogram  as labels - it is treated as
                     // an error and it has been flagged before
                     // since calling FindBin(x) for histo with labels does not make sense
                     // and the result is unpredictable
                     ix = fXaxis.FindBin(hist->GetXaxis()->GetBinCenter(binx));
                  }
                  else {
                     // histogram have same limits - no need to call FindBin
                     ix = binx;
                  }
               } else {
                  // here only in the case of bins with labels
                  const char* label=hist->GetXaxis()->GetBinLabel(binx);
                  // do we need to support case when there are bins with labels and bins without them ??
                  // NO -then return an error
                  if (label == 0 ) {
                     Fatal("Merge","Histogram %s with labels has NULL label pointer for bin %d",
                           hist->GetName(),binx );
                     return -1;
                  }
                  if (label[0] == 0 ) { // case label is "" , i.e. is not set
                     // exclude underflow which could contain the non-existing labels
                     // thsi we could merge in all underflow
                     if ( binx > 0 && binx <= nx) {
                        Error("Merge","Cannot merge ! Label histogram %s contains a bin %d which has not a label and has non-zero content ",hist->GetName(),binx );
                        return -1;
                     }
                     else
                        // case of underflow/overflow
                        ix = binx;
                  }
                  else {
                     // if bin does not exists FindBin will add it automatically
                     // by calling LabelsInflate() if the bit is set
                     // otherwise it will return zero and bin will be merged in underflow/overflow
                     // Do we want to keep this case ??
                     ix = fXaxis.FindBin(label);
                  }
                  // ix cannot be -1 . Can be 0 in case label is not found and bit is not set
                  if (ix <0) {
                     Fatal("Merge","Error return from TAxis::FindBin for label %s",label);
                     return -1;
                  }
               }
               if (ix >= 0) {
                  // MERGE here the bin contents
                  //std::cout << "merging bin " << binx << " into " << ix << " with bin content " << cu << " bin center x = " << GetBinCenter(ix) << std::endl;
                  if (ix > fNcells )
                     Fatal("Merge","Fatal error merging histogram %s - bin number is %d and array size is %d",GetName(), ix,fNcells); 

                  AddBinContent(ix,cu);
                  if (fSumw2.fN)  fSumw2.fArray[ix] += error1*error1;
               }
            }
         }
      }
   }
   if (canRebin) SetBit(kCanRebin);


   //copy merged stats
   PutStats(totstats);
   SetEntries(nentries);
   if (hclone) {
      inlist.Remove(hclone);
      delete hclone;
   }
   return (Long64_t)nentries;
}

//______________________________________________________________________________
Bool_t TH1::Multiply(TF1 *f1, Double_t c1)
{
   // Performs the operation: this = this*c1*f1
   // if errors are defined (see TH1::Sumw2), errors are also recalculated.
   //
   // Only bins inside the function range are recomputed.
   // IMPORTANT NOTE: If you intend to use the errors of this histogram later
   // you should call Sumw2 before making this operation.
   // This is particularly important if you fit the histogram after TH1::Multiply
   //
   // The function return kFALSE if the Multiply operation failed

   if (!f1) {
      Error("Add","Attempt to multiply by a non-existing function");
      return kFALSE;
   }

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();
   if (fDimension < 2) nbinsy = -1;
   if (fDimension < 3) nbinsz = -1;

   // reset min-maximum
   SetMinimum();
   SetMaximum();

   //    Reset the kCanRebin option. Otherwise SetBinContent on the overflow bin
   //    would resize the axis limits!
   ResetBit(kCanRebin);

   //   - Loop on bins (including underflows/overflows)
   Int_t bin, binx, biny, binz;
   Double_t cu,w;
   Double_t xx[3];
   Double_t *params = 0;
   f1->InitArgs(xx,params);
   for (binz=0;binz<=nbinsz+1;binz++) {
      xx[2] = fZaxis.GetBinCenter(binz);
      for (biny=0;biny<=nbinsy+1;biny++) {
         xx[1] = fYaxis.GetBinCenter(biny);
         for (binx=0;binx<=nbinsx+1;binx++) {
            xx[0] = fXaxis.GetBinCenter(binx);
            if (!f1->IsInside(xx)) continue;
            TF1::RejectPoint(kFALSE);
            bin = binx +(nbinsx+2)*(biny + (nbinsy+2)*binz);
            Double_t error1 = GetBinError(bin);
            cu  = c1*f1->EvalPar(xx);
            if (TF1::RejectedPoint()) continue;
            w = GetBinContent(bin)*cu;
            SetBinContent(bin,w);
            if (fSumw2.fN) {
               fSumw2.fArray[bin] = cu*cu*error1*error1;
            }
         }
      }
   }
   ResetStats();
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TH1::Multiply(const TH1 *h1)
{
   //   -*-*-*-*-*-*-*-*-*Multiply this histogram by h1*-*-*-*-*-*-*-*-*-*-*-*-*
   //                     =============================
   //
   //   this = this*h1
   //
   //   If errors of this are available (TH1::Sumw2), errors are recalculated.
   //   Note that if h1 has Sumw2 set, Sumw2 is automatically called for this
   //   if not already set.
   //
   // IMPORTANT NOTE: If you intend to use the errors of this histogram later
   // you should call Sumw2 before making this operation.
   // This is particularly important if you fit the histogram after TH1::Multiply
   //
   // The function return kFALSE if the Multiply operation failed

   if (!h1) {
      Error("Multiply","Attempt to multiply by a non-existing histogram");
      return kFALSE;
   }

   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   try {
      CheckConsistency(this,h1);
   } catch(DifferentNumberOfBins&) {
      Error("Multiply","Attempt to multiply histograms with different number of bins");
      return kFALSE;
   } catch(DifferentAxisLimits&) {
      Warning("Multiply","Attempt to multiply histograms with different axis limits");
   } catch(DifferentBinLimits&) {
      Warning("Multiply","Attempt to multiply histograms with different bin limits");
   } catch(DifferentLabels&) {
      Warning("Multiply","Attempt to multiply histograms with different labels");
   }


   if (fDimension < 2) nbinsy = -1;
   if (fDimension < 3) nbinsz = -1;

   //    Create Sumw2 if h1 has Sumw2 set
   if (fSumw2.fN == 0 && h1->GetSumw2N() != 0) Sumw2();

   //   - Reset min-  maximum
   SetMinimum();
   SetMaximum();

   //    Reset the kCanRebin option. Otherwise SetBinContent on the overflow bin
   //    would resize the axis limits!
   ResetBit(kCanRebin);

   //   - Loop on bins (including underflows/overflows)
   Int_t bin, binx, biny, binz;
   Double_t c0,c1,w;
   for (binz=0;binz<=nbinsz+1;binz++) {
      for (biny=0;biny<=nbinsy+1;biny++) {
         for (binx=0;binx<=nbinsx+1;binx++) {
            bin = GetBin(binx,biny,binz);
            c0  = GetBinContent(bin);
            c1  = h1->GetBinContent(bin);
            w   = c0*c1;
            SetBinContent(bin,w);
            if (fSumw2.fN) {
               Double_t e0 = GetBinError(bin);
               Double_t e1 = h1->GetBinError(bin);
               fSumw2.fArray[bin] = (e0*e0*c1*c1 + e1*e1*c0*c0);
            }
         }
      }
   }
   ResetStats();
   return kTRUE;
}


//______________________________________________________________________________
Bool_t TH1::Multiply(const TH1 *h1, const TH1 *h2, Double_t c1, Double_t c2, Option_t *option)
{
   //   -*-*-*Replace contents of this histogram by multiplication of h1 by h2*-*
   //         ================================================================
   //
   //   this = (c1*h1)*(c2*h2)
   //
   //   If errors of this are available (TH1::Sumw2), errors are recalculated.
   //   Note that if h1 or h2 have Sumw2 set, Sumw2 is automatically called for this
   //   if not already set.
   //
   // IMPORTANT NOTE: If you intend to use the errors of this histogram later
   // you should call Sumw2 before making this operation.
   // This is particularly important if you fit the histogram after TH1::Multiply
   //
   // The function return kFALSE if the Multiply operation failed

   TString opt = option;
   opt.ToLower();
   //   Bool_t binomial = kFALSE;
   //   if (opt.Contains("b")) binomial = kTRUE;
   if (!h1 || !h2) {
      Error("Multiply","Attempt to multiply by a non-existing histogram");
      return kFALSE;
   }

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   Int_t nbinsx = GetNbinsX();
   Int_t nbinsy = GetNbinsY();
   Int_t nbinsz = GetNbinsZ();

   try {
      CheckConsistency(h1,h2);
      CheckConsistency(this,h1);
   } catch(DifferentNumberOfBins&) {
      Error("Multiply","Attempt to multiply histograms with different number of bins");
      return kFALSE;
   } catch(DifferentAxisLimits&) {
      Warning("Multiply","Attempt to multiply histograms with different axis limits");
   } catch(DifferentBinLimits&) {
      Warning("Multiply","Attempt to multiply histograms with different bin limits");
   } catch(DifferentLabels&) {
      Warning("Multiply","Attempt to multiply histograms with different labels");
   }

   if (fDimension < 2) nbinsy = -1;
   if (fDimension < 3) nbinsz = -1;

   //    Create Sumw2 if h1 or h2 have Sumw2 set
   if (fSumw2.fN == 0 && (h1->GetSumw2N() != 0 || h2->GetSumw2N() != 0)) Sumw2();

   //   - Reset min - maximum
   SetMinimum();
   SetMaximum();

   //    Reset the kCanRebin option. Otherwise SetBinContent on the overflow bin
   //    would resize the axis limits!
   ResetBit(kCanRebin);

   //   - Loop on bins (including underflows/overflows)
   Int_t bin, binx, biny, binz;
   Double_t b1,b2,w,d1,d2;
   d1 = c1*c1;
   d2 = c2*c2;
   for (binz=0;binz<=nbinsz+1;binz++) {
      for (biny=0;biny<=nbinsy+1;biny++) {
         for (binx=0;binx<=nbinsx+1;binx++) {
            bin = binx +(nbinsx+2)*(biny + (nbinsy+2)*binz);
            b1  = h1->GetBinContent(bin);
            b2  = h2->GetBinContent(bin);
            w   = (c1*b1)*(c2*b2);
            SetBinContent(bin,w);
            if (fSumw2.fN) {
               Double_t e1 = h1->GetBinError(bin);
               Double_t e2 = h2->GetBinError(bin);
               fSumw2.fArray[bin] = d1*d2*(e1*e1*b2*b2 + e2*e2*b1*b1);
            }
         }
      }
   }
   ResetStats();
   return kTRUE;
}

//______________________________________________________________________________
void TH1::Paint(Option_t *option)
{
   //   -*-*-*-*-*-*-*Control routine to paint any kind of histograms*-*-*-*-*-*-*
   //                 ===============================================
   //
   //  This function is automatically called by TCanvas::Update.
   //  (see TH1::Draw for the list of options)

   GetPainter(option);

   if (fPainter) {
      if (strlen(option) > 0) fPainter->Paint(option);
      else                    fPainter->Paint(fOption.Data());
   }
}

//______________________________________________________________________________
TH1 *TH1::Rebin(Int_t ngroup, const char*newname, const Double_t *xbins)
{
   //   Rebin this histogram
   //
   //  -case 1  xbins=0
   //   If newname is blank (default), the current histogram is modified and
   //   a pointer to it is returned.
   //
   //   If newname is not blank, the current histogram is not modified, and a
   //   new histogram is returned which is a Clone of the current histogram
   //   with its name set to newname.
   //
   //   The parameter ngroup indicates how many bins of this have to be merged
   //   into one bin of the result.
   //
   //   If the original histogram has errors stored (via Sumw2), the resulting
   //   histograms has new errors correctly calculated.
   //
   //   examples: if h1 is an existing TH1F histogram with 100 bins
   //     h1->Rebin();  //merges two bins in one in h1: previous contents of h1 are lost
   //     h1->Rebin(5); //merges five bins in one in h1
   //     TH1F *hnew = h1->Rebin(5,"hnew"); // creates a new histogram hnew
   //                                       // merging 5 bins of h1 in one bin
   //
   //   NOTE:  If ngroup is not an exact divider of the number of bins,
   //          the top limit of the rebinned histogram is reduced
   //          to the upper edge of the last bin that can make a complete
   //          group. The remaining bins are added to the overflow bin.
   //          Statistics will be recomputed from the new bin contents.
   //
   //  -case 2  xbins!=0
   //   A new histogram is created (you should specify newname).
   //   The parameter ngroup is the number of variable size bins in the created histogram.
   //   The array xbins must contain ngroup+1 elements that represent the low-edges
   //   of the bins.
   //   If the original histogram has errors stored (via Sumw2), the resulting
   //   histograms has new errors correctly calculated.
   //
   //   NOTE:  The bin edges specified in xbins should correspond to bin edges
   //          in the original histogram. If a bin edge in the new histogram is
   //          in the middle of a bin in the original histogram, all entries in
   //          the split bin in the original histogram will be transfered to the
   //          lower of the two possible bins in the new histogram. This is
   //          probably not what you want.
   //
   //   examples: if h1 is an existing TH1F histogram with 100 bins
   //     Double_t xbins[25] = {...} array of low-edges (xbins[25] is the upper edge of last bin
   //     h1->Rebin(24,"hnew",xbins);  //creates a new variable bin size histogram hnew

   Int_t nbins    = fXaxis.GetNbins();
   Double_t xmin  = fXaxis.GetXmin();
   Double_t xmax  = fXaxis.GetXmax();
   if ((ngroup <= 0) || (ngroup > nbins)) {
      Error("Rebin", "Illegal value of ngroup=%d",ngroup);
      return 0;
   }

   if (fDimension > 1 || InheritsFrom(TProfile::Class())) {
      Error("Rebin", "Operation valid on 1-D histograms only");
      return 0;
   }
   if (!newname && xbins) {
      Error("Rebin","if xbins is specified, newname must be given");
      return 0;
   }

   Int_t newbins = nbins/ngroup;
   if (!xbins) {
      Int_t nbg = nbins/ngroup;
      if (nbg*ngroup != nbins) {
         Warning("Rebin", "ngroup=%d is not an exact divider of nbins=%d.",ngroup,nbins);
      }
   }
   else {
   // in the case that xbins is given (rebinning in variable bins), ngroup is
   // the new number of bins and number of grouped bins is not constant.
   // when looping for setting the contents for the new histogram we
   // need to loop on all bins of original histogram.  Then set ngroup=nbins
      newbins = ngroup;
      ngroup = nbins;
   }

   // Save old bin contents into a new array
   Double_t entries = fEntries;
   Double_t *oldBins = new Double_t[nbins+2];
   Int_t bin, i;
   for (bin=0;bin<nbins+2;bin++) oldBins[bin] = GetBinContent(bin);
   Double_t *oldErrors = 0;
   if (fSumw2.fN != 0) {
      oldErrors = new Double_t[nbins+2];
      for (bin=0;bin<nbins+2;bin++) oldErrors[bin] = GetBinError(bin);
   }
   // rebin will not include underflow/overflow if new axis range is larger than old axis range 
   if (xbins) {  
      if (xbins[0] < fXaxis.GetXmin() && oldBins[0] != 0 )
         Warning("Rebin","underflow entries will not be used when rebinning");
      if (xbins[newbins] > fXaxis.GetXmax() && oldBins[nbins+1] != 0 )
         Warning("Rebin","overflow entries will not be used when rebinning");
   }


   // create a clone of the old histogram if newname is specified
   TH1 *hnew = this;
   if ((newname && strlen(newname) > 0) || xbins) {
      hnew = (TH1*)Clone(newname);
   }

   //reset kCanRebin bit to avoid a rebinning in SetBinContent
   Int_t bitRebin = hnew->TestBit(kCanRebin);
   hnew->SetBit(kCanRebin,0);

   // save original statistics
   Double_t stat[kNstat];
   GetStats(stat);
   bool resetStat = false;
   // change axis specs and rebuild bin contents array::RebinAx
   if(!xbins && (newbins*ngroup != nbins)) {
      xmax = fXaxis.GetBinUpEdge(newbins*ngroup);
      resetStat = true; //stats must be reset because top bins will be moved to overflow bin
   }
   // save the TAttAxis members (reset by SetBins)
   Int_t    nDivisions  = fXaxis.GetNdivisions();
   Color_t  axisColor   = fXaxis.GetAxisColor();
   Color_t  labelColor  = fXaxis.GetLabelColor();
   Style_t  labelFont   = fXaxis.GetLabelFont();
   Float_t  labelOffset = fXaxis.GetLabelOffset();
   Float_t  labelSize   = fXaxis.GetLabelSize();
   Float_t  tickLength  = fXaxis.GetTickLength();
   Float_t  titleOffset = fXaxis.GetTitleOffset();
   Float_t  titleSize   = fXaxis.GetTitleSize();
   Color_t  titleColor  = fXaxis.GetTitleColor();
   Style_t  titleFont   = fXaxis.GetTitleFont();

   if(!xbins && (fXaxis.GetXbins()->GetSize() > 0)){ // variable bin sizes
      Double_t *bins = new Double_t[newbins+1];
      for(i = 0; i <= newbins; ++i) bins[i] = fXaxis.GetBinLowEdge(1+i*ngroup);
      hnew->SetBins(newbins,bins); //this also changes errors array (if any)
      delete [] bins;
   } else if (xbins) {
      hnew->SetBins(newbins,xbins);
   } else {
      hnew->SetBins(newbins,xmin,xmax);
   }

   // Restore axis attributes
   fXaxis.SetNdivisions(nDivisions);
   fXaxis.SetAxisColor(axisColor);
   fXaxis.SetLabelColor(labelColor);
   fXaxis.SetLabelFont(labelFont);
   fXaxis.SetLabelOffset(labelOffset);
   fXaxis.SetLabelSize(labelSize);
   fXaxis.SetTickLength(tickLength);
   fXaxis.SetTitleOffset(titleOffset);
   fXaxis.SetTitleSize(titleSize);
   fXaxis.SetTitleColor(titleColor);
   fXaxis.SetTitleFont(titleFont);

   // copy merged bin contents (ignore under/overflows)
   // Start merging only once the new lowest edge is reached
   Int_t startbin = 1;
   const Double_t newxmin = hnew->GetXaxis()->GetBinLowEdge(1);
   while( fXaxis.GetBinCenter(startbin) < newxmin && startbin <= nbins ) {
      startbin++;
   }
   Int_t oldbin = startbin;
   Double_t binContent, binError;
   for (bin = 1;bin<=newbins;bin++) {
      binContent = 0;
      binError   = 0;
      Int_t imax = ngroup;
      Double_t xbinmax = hnew->GetXaxis()->GetBinUpEdge(bin);
      for (i=0;i<ngroup;i++) {
         if( (oldbin+i > nbins) ||
             ( hnew != this && (fXaxis.GetBinCenter(oldbin+i) > xbinmax)) ) {
            imax = i;
            break;
         }
         binContent += oldBins[oldbin+i];
         if (oldErrors) binError += oldErrors[oldbin+i]*oldErrors[oldbin+i];
      }
      hnew->SetBinContent(bin,binContent);
      if (oldErrors) hnew->SetBinError(bin,TMath::Sqrt(binError));
      oldbin += imax;
   }

   // sum underflow and overflow contents until startbin
   binContent = 0;
   binError = 0;
   for (i = 0; i < startbin; ++i)  {
      binContent += oldBins[i];
      if (oldErrors) binError += oldErrors[i]*oldErrors[i];
   }
   hnew->SetBinContent(0,binContent);
   if (oldErrors) hnew->SetBinError(0,TMath::Sqrt(binError));
   // sum overflow
   binContent = 0;
   binError = 0;
   for (i = oldbin; i <= nbins+1; ++i)  {
      binContent += oldBins[i];
      if (oldErrors) binError += oldErrors[i]*oldErrors[i];
   }
   hnew->SetBinContent(newbins+1,binContent);
   if (oldErrors) hnew->SetBinError(newbins+1,TMath::Sqrt(binError));
   hnew->SetBit(kCanRebin,bitRebin);

   // restore statistics and entries modified by SetBinContent
   hnew->SetEntries(entries);
   if (!resetStat) hnew->PutStats(stat);
   delete [] oldBins;
   if (oldErrors) delete [] oldErrors;
   return hnew;
}

//______________________________________________________________________________
Bool_t TH1::FindNewAxisLimits(const TAxis* axis, const Double_t point, Double_t& newMin, Double_t &newMax)
{
   // finds new limits for the axis so that *point* is within the range and
   // the limits are compatible with the previous ones (see TH1::Merge).
   // new limits are put into *newMin* and *newMax* variables.
   // axis - axis whose limits are to be recomputed
   // point - point that should fit within the new axis limits
   // newMin - new minimum will be stored here
   // newMax - new maximum will be stored here.
   // false if failed (e.g. if the initial axis limits are wrong
   // or the new range is more than 2^64 times the old one).

   Double_t xmin = axis->GetXmin();
   Double_t xmax = axis->GetXmax();
   if (xmin >= xmax) return kFALSE;
   Double_t range = xmax-xmin;
   Double_t binsize = range / axis->GetNbins();

   //recompute new axis limits by doubling the current range
   Int_t ntimes = 0;
   while (point < xmin) {
      if (ntimes++ > 64)
         return kFALSE;
      xmin = xmin - range;
      range *= 2;
      binsize *= 2;
      // // make sure that the merging will be correct
      // if ( xmin / binsize - TMath::Floor(xmin / binsize) >= 0.5) {
      //    xmin += 0.5 * binsize;
      //    xmax += 0.5 * binsize;  // won't work with a histogram with only one bin, but I don't care
      // }
   }
   while (point >= xmax) {
      if (ntimes++ > 64)
         return kFALSE;
      xmax = xmax + range;
      range *= 2;
      binsize *= 2;
      // // make sure that the merging will be correct
      // if ( xmin / binsize - TMath::Floor(xmin / binsize) >= 0.5) {
      //    xmin -= 0.5 * binsize;
      //    xmax -= 0.5 * binsize;  // won't work with a histogram with only one bin, but I don't care
      // }
   }
   newMin = xmin;
   newMax = xmax;
   //   Info("FindNewAxisLimits", "OldAxis: (%lf, %lf), new: (%lf, %lf), point: %lf",
   //      axis->GetXmin(), axis->GetXmax(), xmin, xmax, point);

   return kTRUE;
}

//______________________________________________________________________________
void TH1::RebinAxis(Double_t x, TAxis *axis)
{
   // Histogram is resized along axis such that x is in the axis range.
   // The new axis limits are recomputed by doubling iteratively
   // the current axis range until the specified value x is within the limits.
   // The algorithm makes a copy of the histogram, then loops on all bins
   // of the old histogram to fill the rebinned histogram.
   // Takes into account errors (Sumw2) if any.
   // The algorithm works for 1-d, 2-D and 3-D histograms.
   // The bit kCanRebin must be set before invoking this function.
   //  Ex:  h->SetBit(TH1::kCanRebin);

   if (!TestBit(kCanRebin)) return;
   if (TMath::IsNaN(x)) {         // x may be a NaN
      ResetBit(kCanRebin);
      return;
   }

   if (axis->GetXmin() >= axis->GetXmax()) return;
   if (axis->GetNbins() <= 0) return;

   Double_t xmin, xmax;
   if (!FindNewAxisLimits(axis, x, xmin, xmax))
      return;

   //save a copy of this histogram
   TH1 *hold = (TH1*)IsA()->New(); 
   hold->SetDirectory(0);
   Copy(*hold);
   //set new axis limits
   axis->SetLimits(xmin,xmax);

   Int_t  nbinsx = fXaxis.GetNbins();
   Int_t  nbinsy = fYaxis.GetNbins();
   Int_t  nbinsz = fZaxis.GetNbins();

   //now loop on all bins and refill
   Double_t err,cu;
   Double_t bx,by,bz;
   Int_t errors = GetSumw2N();
   Int_t ix,iy,iz,ibin,binx,biny,binz,bin;
   Reset("ICE"); //reset only Integral, contents and Errors
   for (binz=1;binz<=nbinsz;binz++) {
      bz  = hold->GetZaxis()->GetBinCenter(binz);
      iz  = fZaxis.FindFixBin(bz);
      for (biny=1;biny<=nbinsy;biny++) {
         by  = hold->GetYaxis()->GetBinCenter(biny);
         iy  = fYaxis.FindFixBin(by);
         for (binx=1;binx<=nbinsx;binx++) {
            bx = hold->GetXaxis()->GetBinCenter(binx);
            ix  = fXaxis.FindFixBin(bx);
            bin = hold->GetBin(binx,biny,binz);
            ibin= GetBin(ix,iy,iz);
            cu  = hold->GetBinContent(bin);
            AddBinContent(ibin,cu);
            if (errors) {
               err = hold->GetBinError(bin);
               fSumw2.fArray[ibin] += err*err;
            }
         }
      }
   }
   delete hold;
}

//______________________________________________________________________________
void TH1::RecursiveRemove(TObject *obj)
{
   // Recursively remove object from the list of functions

   if (fFunctions) {
      if (!fFunctions->TestBit(kInvalidObject)) fFunctions->RecursiveRemove(obj);
   }
}

//______________________________________________________________________________
void TH1::Scale(Double_t c1, Option_t *option)
{
   //   -*-*-*Multiply this histogram by a constant c1*-*-*-*-*-*-*-*-*
   //         ========================================
   //
   //   this = c1*this
   //
   // Note that both contents and errors(if any) are scaled.
   // This function uses the services of TH1::Add
   //
   // IMPORTANT NOTE: If you intend to use the errors of this histogram later
   // you should call Sumw2 before making this operation.
   // This is particularly important if you fit the histogram after TH1::Scale
   //
   // One can scale an histogram such that the bins integral is equal to
   // the normalization parameter via TH1::Scale(Double_t norm), where norm
   // is the desired normalization divided by the integral of the histogram.
   //
   // If option contains "width" the bin contents and errors are divided
   // by the bin width.

   TString opt = option;
   opt.ToLower();
   Double_t ent = fEntries;
   if (opt.Contains("width")) Add(this,this,c1,-1);
   else                       Add(this,this,c1,0);
   fEntries = ent;

   //if contours set, must also scale contours
   Int_t ncontours = GetContour();
   if (ncontours == 0) return;
   Double_t *levels = fContour.GetArray();
   for (Int_t i=0;i<ncontours;i++) {
      levels[i] *= c1;
   }
}


//______________________________________________________________________________
void TH1::SetDefaultBufferSize(Int_t buffersize)
{
   // static function to set the default buffer size for automatic histograms.
   // When an histogram is created with one of its axis lower limit greater
   // or equal to its upper limit, the function SetBuffer is automatically
   // called with the default buffer size.

   if (buffersize < 0) buffersize = 0;
   fgBufferSize = buffersize;
}


//______________________________________________________________________________
void TH1::SetDefaultSumw2(Bool_t sumw2)
{
   // static function.
   // When this static function is called with sumw2=kTRUE, all new
   // histograms will automatically activate the storage
   // of the sum of squares of errors, ie TH1::Sumw2 is automatically called.

   fgDefaultSumw2 = sumw2;
}

//______________________________________________________________________________
void TH1::SetTitle(const char *title)
{
   // Change (i.e. set) the title
   //
   //   if title is in the form "stringt;stringx;stringy;stringz"
   //   the histogram title is set to stringt, the x axis title to stringx,
   //   the y axis title to stringy, and the z axis title to stringz.
   //   To insert the character ";" in one of the titles, one should use "#;"
   //   or "#semicolon".

   fTitle = title;
   fTitle.ReplaceAll("#;",2,"#semicolon",10);

   // Decode fTitle. It may contain X, Y and Z titles
   TString str1 = fTitle, str2;
   Int_t isc = str1.Index(";");
   Int_t lns = str1.Length();

   if (isc >=0 ) {
      fTitle = str1(0,isc);
      str1   = str1(isc+1, lns);
      isc    = str1.Index(";");
      if (isc >=0 ) {
         str2 = str1(0,isc);
         str2.ReplaceAll("#semicolon",10,";",1);
         fXaxis.SetTitle(str2.Data());
         lns  = str1.Length();
         str1 = str1(isc+1, lns);
         isc  = str1.Index(";");
         if (isc >=0 ) {
            str2 = str1(0,isc);
            str2.ReplaceAll("#semicolon",10,";",1);
            fYaxis.SetTitle(str2.Data());
            lns  = str1.Length();
            str1 = str1(isc+1, lns);
            str1.ReplaceAll("#semicolon",10,";",1);
            fZaxis.SetTitle(str1.Data());
         } else {
            str1.ReplaceAll("#semicolon",10,";",1);
            fYaxis.SetTitle(str1.Data());
         }
      } else {
         str1.ReplaceAll("#semicolon",10,";",1);
         fXaxis.SetTitle(str1.Data());
      }
   }

   fTitle.ReplaceAll("#semicolon",10,";",1);

   if (gPad && TestBit(kMustCleanup)) gPad->Modified();
}

// -------------------------------------------------------------------------
void  TH1::SmoothArray(Int_t nn, Double_t *xx, Int_t ntimes)
{
   // smooth array xx, translation of Hbook routine hsmoof.F
   // based on algorithm 353QH twice presented by J. Friedman
   // in Proc.of the 1974 CERN School of Computing, Norway, 11-24 August, 1974.

   if (nn < 3 ) {
      ::Error("SmoothArray","Need at least 3 points for smoothing: n = %d",nn);
      return;
   }

   Int_t ii;
   Double_t hh[6] = {0,0,0,0,0,0};

   std::vector<double> yy(nn); 
   std::vector<double> zz(nn); 
   std::vector<double> rr(nn); 

   for (Int_t pass=0;pass<ntimes;pass++) {
      // first copy original data into temp array
      std::copy(xx, xx+nn, zz.begin() ); 



      for (int noent = 0; noent < 2; ++noent) { // run algorithm two times 
      
         //  do 353 i.e. running median 3, 5, and 3 in a single loop
         for  (int kk = 0; kk < 3; kk++)  {
            std::copy(zz.begin(), zz.end(), yy.begin());
            int medianType = (kk != 1)  ?  3 : 5; 
            int ifirst      = (kk != 1 ) ?  1 : 2;
            int ilast       = (kk != 1 ) ? nn-1 : nn -2; 
            //nn2 = nn - ik - 1;
            // do all elements beside the first and last point for median 3
            //  and first two and last 2 for median 5
            for  ( ii = ifirst; ii < ilast; ii++)  {
               assert(ii - ifirst >= 0);
               for  (int jj = 0; jj < medianType; jj++)   {
                  hh[jj] = yy[ii - ifirst + jj ];
               }
               zz[ii] = TMath::Median(medianType, hh);
            }

            if  (kk == 0)  {   // first median 3
               // first point
               hh[0] = zz[1];
               hh[1] = zz[0];
               hh[2] = 3*zz[1] - 2*zz[2];
               zz[0] = TMath::Median(3, hh);
               // last point
               hh[0] = zz[nn - 2];
               hh[1] = zz[nn - 1];
               hh[2] = 3*zz[nn - 2] - 2*zz[nn - 3];
               zz[nn - 1] = TMath::Median(3, hh);
            }


            if  (kk == 1)  {   //  median 5
               for  (ii = 0; ii < 3; ii++) {
                  hh[ii] = yy[ii];
               }
               zz[1] = TMath::Median(3, hh);
               // last two points
               for  (ii = 0; ii < 3; ii++) {
                  hh[ii] = yy[nn - 3 + ii];
               }
               zz[nn - 2] = TMath::Median(3, hh);
            }

         }

         std::copy ( zz.begin(), zz.end(), yy.begin() );

         // quadratic interpolation for flat segments
         for (ii = 2; ii < (nn - 2); ii++) {
            if  (zz[ii - 1] != zz[ii]) continue;
            if  (zz[ii] != zz[ii + 1]) continue;
            hh[0] = zz[ii - 2] - zz[ii];
            hh[1] = zz[ii + 2] - zz[ii];
            if  (hh[0] * hh[1] <= 0) continue;
            int jk = 1;
            if  ( TMath::Abs(hh[1]) > TMath::Abs(hh[0]) ) jk = -1;
            yy[ii] = -0.5*zz[ii - 2*jk] + zz[ii]/0.75 + zz[ii + 2*jk] /6.;
            yy[ii + jk] = 0.5*(zz[ii + 2*jk] - zz[ii - 2*jk]) + zz[ii];
         }

         // running means
         //std::copy(zz.begin(), zz.end(), yy.begin()); 
         for  (ii = 1; ii < nn - 1; ii++) {
            zz[ii] = 0.25*yy[ii - 1] + 0.5*yy[ii] + 0.25*yy[ii + 1];
         }
         zz[0] = yy[0];
         zz[nn - 1] = yy[nn - 1];

         if (noent == 0) { 

            // save computed values 
            std::copy(zz.begin(), zz.end(), rr.begin()); 

            // COMPUTE  residuals
            for  (ii = 0; ii < nn; ii++)  {
               zz[ii] = xx[ii] - zz[ii];
            }
         }

      }  // end loop on noent
      
 
      double xmin = TMath::MinElement(nn,xx);
      for  (ii = 0; ii < nn; ii++) {
         if (xmin < 0) xx[ii] = rr[ii] + zz[ii];
         // make smoothing defined positive - not better using 0 ?
         else  xx[ii] = TMath::Max((rr[ii] + zz[ii]),0.0 );
      }
   }
}


// ------------------------------------------------------------------------
void  TH1::Smooth(Int_t ntimes, Option_t *option)
{
   // Smooth bin contents of this histogram.
   // if option contains "R" smoothing is applied only to the bins
   // defined in the X axis range (default is to smooth all bins)
   // Bin contents are replaced by their smooth values.
   // Errors (if any) are not modified.
   // the smoothing procedure is repeated ntimes (default=1)

   if (fDimension != 1) {
      Error("Smooth","Smooth only supported for 1-d histograms");
      return;
   }
   Int_t nbins = fXaxis.GetNbins();
   if (nbins < 3) { 
      Error("Smooth","Smooth only supported for histograms with >= 3 bins. Nbins = %d",nbins);
      return;
   }

   // delete buffer if it is there since it will become invalid
   if (fBuffer) BufferEmpty(1);

   Int_t firstbin = 1, lastbin = nbins;
   TString opt = option;
   opt.ToLower();
   if (opt.Contains("r")) {
      firstbin= fXaxis.GetFirst();
      lastbin  = fXaxis.GetLast();
   }
   nbins = lastbin - firstbin + 1;
   Double_t *xx = new Double_t[nbins];
   Double_t nent = fEntries;
   Int_t i;
   for (i=0;i<nbins;i++) {
      xx[i] = GetBinContent(i+firstbin);
   }

   TH1::SmoothArray(nbins,xx,ntimes);

   for (i=0;i<nbins;i++) {
      SetBinContent(i+firstbin,xx[i]);
   }
   fEntries = nent;
   delete [] xx;

   if (gPad) gPad->Modified();
}


// ------------------------------------------------------------------------
void  TH1::StatOverflows(Bool_t flag)
{
   //  if flag=kTRUE, underflows and overflows are used by the Fill functions
   //  in the computation of statistics (mean value, RMS).
   //  By default, underflows or overflows are not used.

   fgStatOverflows = flag;
}

//_______________________________________________________________________
void TH1::Streamer(TBuffer &b)
{
   //   -*-*-*-*-*-*-*Stream a class object*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
   //                 =====================
   if (b.IsReading()) {
      UInt_t R__s, R__c;
      Version_t R__v = b.ReadVersion(&R__s, &R__c);
      if (fDirectory) fDirectory->Remove(this);
      fDirectory = 0;
      if (R__v > 2) {
         b.ReadClassBuffer(TH1::Class(), this, R__v, R__s, R__c);

         ResetBit(kMustCleanup);
         fXaxis.SetParent(this);
         fYaxis.SetParent(this);
         fZaxis.SetParent(this);
         TIter next(fFunctions);
         TObject *obj;
         while ((obj=next())) {
            if (obj->InheritsFrom(TF1::Class())) ((TF1*)obj)->SetParent(this);
         }
         return;
      }
      //process old versions before automatic schema evolution
      TNamed::Streamer(b);
      TAttLine::Streamer(b);
      TAttFill::Streamer(b);
      TAttMarker::Streamer(b);
      b >> fNcells;
      fXaxis.Streamer(b);
      fYaxis.Streamer(b);
      fZaxis.Streamer(b);
      fXaxis.SetParent(this);
      fYaxis.SetParent(this);
      fZaxis.SetParent(this);
      b >> fBarOffset;
      b >> fBarWidth;
      b >> fEntries;
      b >> fTsumw;
      b >> fTsumw2;
      b >> fTsumwx;
      b >> fTsumwx2;
      if (R__v < 2) {
         Float_t maximum, minimum, norm;
         Float_t *contour=0;
         b >> maximum; fMaximum = maximum;
         b >> minimum; fMinimum = minimum;
         b >> norm;    fNormFactor = norm;
         Int_t n = b.ReadArray(contour);
         fContour.Set(n);
         for (Int_t i=0;i<n;i++) fContour.fArray[i] = contour[i];
         delete [] contour;
      } else {
         b >> fMaximum;
         b >> fMinimum;
         b >> fNormFactor;
         fContour.Streamer(b);
      }
      fSumw2.Streamer(b);
      fOption.Streamer(b);
      fFunctions->Delete();
      fFunctions->Streamer(b);
      b.CheckByteCount(R__s, R__c, TH1::IsA());

   } else {
      b.WriteClassBuffer(TH1::Class(),this);
   }
}

//______________________________________________________________________________
void TH1::Print(Option_t *option) const
{
   //   -*-*-*-*-*Print some global quantities for this histogram*-*-*-*-*-*-*-*
   //             ===============================================
   //
   //  If option "base" is given, number of bins and ranges are also printed
   //  If option "range" is given, bin contents and errors are also printed
   //                     for all bins in the current range (default 1-->nbins)
   //  If option "all" is given, bin contents and errors are also printed
   //                     for all bins including under and overflows.

   printf( "TH1.Print Name  = %s, Entries= %d, Total sum= %g\n",GetName(),Int_t(GetEntries()),GetSumOfWeights());
   TString opt = option;
   opt.ToLower();
   Int_t all;
   if      (opt.Contains("all"))   all = 0;
   else if (opt.Contains("range")) all = 1;
   else if (opt.Contains("base"))  all = 2;
   else                            return;

   Int_t bin, binx, biny, binz;
   Int_t firstx=0,lastx=0,firsty=0,lasty=0,firstz=0,lastz=0;
   if (all == 0) {
      lastx  = fXaxis.GetNbins()+1;
      if (fDimension > 1) lasty = fYaxis.GetNbins()+1;
      if (fDimension > 2) lastz = fZaxis.GetNbins()+1;
   } else {
      firstx = fXaxis.GetFirst(); lastx  = fXaxis.GetLast();
      if (fDimension > 1) {firsty = fYaxis.GetFirst(); lasty = fYaxis.GetLast();}
      if (fDimension > 2) {firstz = fZaxis.GetFirst(); lastz = fZaxis.GetLast();}
   }

   if (all== 2) {
      printf("          Title = %s\n", GetTitle());
      printf("          NbinsX= %d, xmin= %g, xmax=%g", fXaxis.GetNbins(), fXaxis.GetXmin(), fXaxis.GetXmax());
      if( fDimension > 1) printf(", NbinsY= %d, ymin= %g, ymax=%g", fYaxis.GetNbins(), fYaxis.GetXmin(), fYaxis.GetXmax());
      if( fDimension > 2) printf(", NbinsZ= %d, zmin= %g, zmax=%g", fZaxis.GetNbins(), fZaxis.GetXmin(), fZaxis.GetXmax());
      printf("\n");
      return;
   }

   Double_t w,e;
   Double_t x,y,z;
   if (fDimension == 1) {
      for (binx=firstx;binx<=lastx;binx++) {
         x = fXaxis.GetBinCenter(binx);
         w = GetBinContent(binx);
         e = GetBinError(binx);
         if(fSumw2.fN) printf(" fSumw[%d]=%g, x=%g, error=%g\n",binx,w,x,e);
         else          printf(" fSumw[%d]=%g, x=%g\n",binx,w,x);
      }
   }
   if (fDimension == 2) {
      for (biny=firsty;biny<=lasty;biny++) {
         y = fYaxis.GetBinCenter(biny);
         for (binx=firstx;binx<=lastx;binx++) {
            bin = GetBin(binx,biny);
            x = fXaxis.GetBinCenter(binx);
            w = GetBinContent(bin);
            e = GetBinError(bin);
            if(fSumw2.fN) printf(" fSumw[%d][%d]=%g, x=%g, y=%g, error=%g\n",binx,biny,w,x,y,e);
            else          printf(" fSumw[%d][%d]=%g, x=%g, y=%g\n",binx,biny,w,x,y);
         }
      }
   }
   if (fDimension == 3) {
      for (binz=firstz;binz<=lastz;binz++) {
         z = fZaxis.GetBinCenter(binz);
         for (biny=firsty;biny<=lasty;biny++) {
            y = fYaxis.GetBinCenter(biny);
            for (binx=firstx;binx<=lastx;binx++) {
               bin = GetBin(binx,biny,binz);
               x = fXaxis.GetBinCenter(binx);
               w = GetBinContent(bin);
               e = GetBinError(bin);
               if(fSumw2.fN) printf(" fSumw[%d][%d][%d]=%g, x=%g, y=%g, z=%g, error=%g\n",binx,biny,binz,w,x,y,z,e);
               else          printf(" fSumw[%d][%d][%d]=%g, x=%g, y=%g, z=%g\n",binx,biny,binz,w,x,y,z);
            }
         }
      }
   }
}

//______________________________________________________________________________
void TH1::Rebuild(Option_t *)
{
   // Using the current bin info, recompute the arrays for contents and errors

   SetBinsLength();
   if (fSumw2.fN) {
      fSumw2.Set(fNcells);
   }
}

//______________________________________________________________________________
void TH1::Reset(Option_t *option)
{
   //   -*-*-*-*-*-*Reset this histogram: contents, errors, etc*-*-*-*-*-*-*-*
   //               ===========================================
   //
   // if option "ICE" is specified, resets only Integral, Contents and Errors.
   // if option "ICES" is specified, resets only Integral, Contents , Errors and Statistics
   //                  This option is used
   // if option "M"   is specified, resets also Minimum and Maximum

   // The option "ICE" is used when rebinning the histogram (in RebinAxis, LabelInflate, etc..)
   // The option "ICES is used in combination with the buffer (see BufferEmpty and BufferFill)

   TString opt = option;
   opt.ToUpper();
   fSumw2.Reset();
   if (fIntegral) {delete [] fIntegral; fIntegral = 0;}

   if (opt.Contains("M")) {
      SetMinimum();
      SetMaximum();
   }

   if (opt.Contains("ICE") && !opt.Contains("S")) return;

   // Setting fBuffer[0] = 0 is like resetting the buffer but not deleting it
   // But what is the sense of calling BufferEmpty() ? For making the axes ?
   // BufferEmpty will update contents that later will be
   // reset in calling TH1D::Reset. For this we need to reset the stats afterwards
   // It may be needed for computing the axis limits....
   if (fBuffer) {BufferEmpty(); fBuffer[0] = 0;}

   // need to reset also the statistics
   // (needs to be done after calling BufferEmpty() )
   fTsumw       = 0;
   fTsumw2      = 0;
   fTsumwx      = 0;
   fTsumwx2     = 0;
   fEntries     = 0;

   if (opt == "ICES") return;


   TObject *stats = fFunctions->FindObject("stats");
   fFunctions->Remove(stats);
   //special logic to support the case where the same object is
   //added multiple times in fFunctions.
   //This case happens when the same object is added with different
   //drawing modes
   TObject *obj;
   while ((obj  = fFunctions->First())) {
      while(fFunctions->Remove(obj)) { }
      delete obj;
   }
   if(stats) fFunctions->Add(stats);
   fContour.Set(0);
}

//______________________________________________________________________________
void TH1::SavePrimitive(ostream &out, Option_t *option /*= ""*/)
{
   // Save primitive as a C++ statement(s) on output stream out

   Bool_t nonEqiX = kFALSE;
   Bool_t nonEqiY = kFALSE;
   Bool_t nonEqiZ = kFALSE;
   Int_t i;
   static Int_t nxaxis = 0;
   static Int_t nyaxis = 0;
   static Int_t nzaxis = 0;
   TString sxaxis="xAxis",syaxis="yAxis",szaxis="zAxis";

   // Check if the histogram has equidistant X bins or not.  If not, we
   // create an array holding the bins.
   if (GetXaxis()->GetXbins()->fN && GetXaxis()->GetXbins()->fArray) {
      nonEqiX = kTRUE;
      nxaxis++;
      sxaxis += nxaxis;
      out << "   Double_t "<<sxaxis<<"[" << GetXaxis()->GetXbins()->fN
         << "] = {";
      for (i = 0; i < GetXaxis()->GetXbins()->fN; i++) {
         if (i != 0) out << ", ";
         out << GetXaxis()->GetXbins()->fArray[i];
      }
      out << "}; " << endl;
   }
   // If the histogram is 2 or 3 dimensional, check if the histogram
   // has equidistant Y bins or not.  If not, we create an array
   // holding the bins.
   if (fDimension > 1 && GetYaxis()->GetXbins()->fN &&
      GetYaxis()->GetXbins()->fArray) {
         nonEqiY = kTRUE;
         nyaxis++;
         syaxis += nyaxis;
         out << "   Double_t "<<syaxis<<"[" << GetYaxis()->GetXbins()->fN
            << "] = {";
         for (i = 0; i < GetYaxis()->GetXbins()->fN; i++) {
            if (i != 0) out << ", ";
            out << GetYaxis()->GetXbins()->fArray[i];
         }
         out << "}; " << endl;
   }
   // IF the histogram is 3 dimensional, check if the histogram
   // has equidistant Z bins or not.  If not, we create an array
   // holding the bins.
   if (fDimension > 2 && GetZaxis()->GetXbins()->fN &&
      GetZaxis()->GetXbins()->fArray) {
         nonEqiZ = kTRUE;
         nzaxis++;
         szaxis += nzaxis;
         out << "   Double_t "<<szaxis<<"[" << GetZaxis()->GetXbins()->fN
            << "] = {";
         for (i = 0; i < GetZaxis()->GetXbins()->fN; i++) {
            if (i != 0) out << ", ";
            out << GetZaxis()->GetXbins()->fArray[i];
         }
         out << "}; " << endl;
   }

   char quote = '"';
   out <<"   "<<endl;
   out <<"   "<< ClassName() <<" *";

   // Histogram pointer has by default the histogram name.
   // However, in case the histogram has no directory, it is safer to add a incremental suffix.
   // If the histogram belongs to a graph or a stack the suffix is not added because
   // the graph and stack objects are not aware of this new name. Same thing if
   // the histogram is drawn with the option COLZ because the TPaletteAxis drawn
   // when this option is selected, does not know this new name either.
   TString opt = option;
   opt.ToLower();
   static Int_t hcounter = 0;
   TString histName = GetName();
   if (!fDirectory && !histName.Contains("Graph")
                   && !histName.Contains("_stack_")
                   && !opt.Contains("colz")) {
      hcounter++;
      histName += "__";
      histName += hcounter;
   }
   const char *hname = histName.Data();
   if (!strlen(hname)) hname = "unnamed";

   TString t(GetTitle());
   t.ReplaceAll("\\","\\\\");
   t.ReplaceAll("\"","\\\"");
   out << hname << " = new " << ClassName() << "(" << quote
      << hname << quote << "," << quote<< t.Data() << quote
      << "," << GetXaxis()->GetNbins();
   if (nonEqiX)
      out << ", "<<sxaxis;
   else
      out << "," << GetXaxis()->GetXmin()
      << "," << GetXaxis()->GetXmax();
   if (fDimension > 1) {
      out << "," << GetYaxis()->GetNbins();
      if (nonEqiY)
         out << ", "<<syaxis;
      else
         out << "," << GetYaxis()->GetXmin()
         << "," << GetYaxis()->GetXmax();
   }
   if (fDimension > 2) {
      out << "," << GetZaxis()->GetNbins();
      if (nonEqiZ)
         out << ", "<<szaxis;
      else
         out << "," << GetZaxis()->GetXmin()
         << "," << GetZaxis()->GetXmax();
   }
   out << ");" << endl;

   // save bin contents
   Int_t bin;
   for (bin=0;bin<fNcells;bin++) {
      Double_t bc = GetBinContent(bin);
      if (bc) {
         out<<"   "<<hname<<"->SetBinContent("<<bin<<","<<bc<<");"<<endl;
      }
   }

   // save bin errors
   if (fSumw2.fN) {
      for (bin=0;bin<fNcells;bin++) {
         Double_t be = GetBinError(bin);
         if (be) {
            out<<"   "<<hname<<"->SetBinError("<<bin<<","<<be<<");"<<endl;
         }
      }
   }

   TH1::SavePrimitiveHelp(out, hname, option);
}

//______________________________________________________________________________
void TH1::SavePrimitiveHelp(ostream &out, const char *hname, Option_t *option /*= ""*/)
{
   // helper function for the SavePrimitive functions from TH1
   // or classes derived from TH1, eg TProfile, TProfile2D.

   char quote = '"';
   if (TMath::Abs(GetBarOffset()) > 1e-5) {
      out<<"   "<<hname<<"->SetBarOffset("<<GetBarOffset()<<");"<<endl;
   }
   if (TMath::Abs(GetBarWidth()-1) > 1e-5) {
      out<<"   "<<hname<<"->SetBarWidth("<<GetBarWidth()<<");"<<endl;
   }
   if (fMinimum != -1111) {
      out<<"   "<<hname<<"->SetMinimum("<<fMinimum<<");"<<endl;
   }
   if (fMaximum != -1111) {
      out<<"   "<<hname<<"->SetMaximum("<<fMaximum<<");"<<endl;
   }
   if (fNormFactor != 0) {
      out<<"   "<<hname<<"->SetNormFactor("<<fNormFactor<<");"<<endl;
   }
   if (fEntries != 0) {
      out<<"   "<<hname<<"->SetEntries("<<fEntries<<");"<<endl;
   }
   if (fDirectory == 0) {
      out<<"   "<<hname<<"->SetDirectory(0);"<<endl;
   }
   if (TestBit(kNoStats)) {
      out<<"   "<<hname<<"->SetStats(0);"<<endl;
   }
   if (fOption.Length() != 0) {
      out<<"   "<<hname<<"->SetOption("<<quote<<fOption.Data()<<quote<<");"<<endl;
   }

   // save contour levels
   Int_t ncontours = GetContour();
   if (ncontours > 0) {
      out<<"   "<<hname<<"->SetContour("<<ncontours<<");"<<endl;
      Double_t zlevel;
      for (Int_t bin=0;bin<ncontours;bin++) {
         if (gPad->GetLogz()) {
            zlevel = TMath::Power(10,GetContourLevel(bin));
         } else {
            zlevel = GetContourLevel(bin);
         }
         out<<"   "<<hname<<"->SetContourLevel("<<bin<<","<<zlevel<<");"<<endl;
      }
   }

   // save list of functions
   TObjOptLink *lnk = (TObjOptLink*)fFunctions->FirstLink();
   TObject *obj;
   while (lnk) {
      obj = lnk->GetObject();
      obj->SavePrimitive(out,"nodraw");
      if (obj->InheritsFrom(TF1::Class())) {
         out<<"   "<<hname<<"->GetListOfFunctions()->Add("<<obj->GetName()<<");"<<endl;
      } else if (obj->InheritsFrom("TPaveStats")) {
         out<<"   "<<hname<<"->GetListOfFunctions()->Add(ptstats);"<<endl;
         out<<"   ptstats->SetParent("<<hname<<");"<<endl;
      } else {
         out<<"   "<<hname<<"->GetListOfFunctions()->Add("<<obj->GetName()<<","<<quote<<lnk->GetOption()<<quote<<");"<<endl;
      }
      lnk = (TObjOptLink*)lnk->Next();
   }

   // save attributes
   SaveFillAttributes(out,hname,0,1001);
   SaveLineAttributes(out,hname,1,1,1);
   SaveMarkerAttributes(out,hname,1,1,1);
   fXaxis.SaveAttributes(out,hname,"->GetXaxis()");
   fYaxis.SaveAttributes(out,hname,"->GetYaxis()");
   fZaxis.SaveAttributes(out,hname,"->GetZaxis()");
   TString opt = option;
   opt.ToLower();
   if (!opt.Contains("nodraw")) {
      out<<"   "<<hname<<"->Draw("
         <<quote<<option<<quote<<");"<<endl;
   }
}

//______________________________________________________________________________
void TH1::UseCurrentStyle()
{
   //   Copy current attributes from/to current style

   if (!gStyle) return;
   if (gStyle->IsReading()) {
      fXaxis.ResetAttAxis("X");
      fYaxis.ResetAttAxis("Y");
      fZaxis.ResetAttAxis("Z");
      SetBarOffset(gStyle->GetBarOffset());
      SetBarWidth(gStyle->GetBarWidth());
      SetFillColor(gStyle->GetHistFillColor());
      SetFillStyle(gStyle->GetHistFillStyle());
      SetLineColor(gStyle->GetHistLineColor());
      SetLineStyle(gStyle->GetHistLineStyle());
      SetLineWidth(gStyle->GetHistLineWidth());
      SetMarkerColor(gStyle->GetMarkerColor());
      SetMarkerStyle(gStyle->GetMarkerStyle());
      SetMarkerSize(gStyle->GetMarkerSize());
      Int_t dostat = gStyle->GetOptStat();
      if (gStyle->GetOptFit() && !dostat) dostat = 1000000001;
      SetStats(dostat);
   } else {
      gStyle->SetBarOffset(fBarOffset);
      gStyle->SetBarWidth(fBarWidth);
      gStyle->SetHistFillColor(GetFillColor());
      gStyle->SetHistFillStyle(GetFillStyle());
      gStyle->SetHistLineColor(GetLineColor());
      gStyle->SetHistLineStyle(GetLineStyle());
      gStyle->SetHistLineWidth(GetLineWidth());
      gStyle->SetMarkerColor(GetMarkerColor());
      gStyle->SetMarkerStyle(GetMarkerStyle());
      gStyle->SetMarkerSize(GetMarkerSize());
      gStyle->SetOptStat(TestBit(kNoStats));
   }
   TIter next(GetListOfFunctions());
   TObject *obj;

   while ((obj = next())) {
      obj->UseCurrentStyle();
   }
}

//______________________________________________________________________________
Double_t TH1::GetMean(Int_t axis) const
{
   //  For axis = 1,2 or 3 returns the mean value of the histogram along
   //  X,Y or Z axis.
   //  For axis = 11, 12, 13 returns the standard error of the mean value
   //  of the histogram along X, Y or Z axis
   //
   //  Note that the mean value/RMS is computed using the bins in the currently
   //  defined range (see TAxis::SetRange). By default the range includes
   //  all bins from 1 to nbins included, excluding underflows and overflows.
   //  To force the underflows and overflows in the computation, one must
   //  call the static function TH1::StatOverflows(kTRUE) before filling
   //  the histogram.



   //   -*-*-*-*-*-*Return mean value of this histogram along the X axis*-*-*-*-*
   //               ====================================================
   //  Note that the mean value/RMS is computed using the bins in the currently
   //  defined range (see TAxis::SetRange). By default the range includes
   //  all bins from 1 to nbins included, excluding underflows and overflows.
   //  To force the underflows and overflows in the computation, one must
   //  call the static function TH1::StatOverflows(kTRUE) before filling
   //  the histogram.

   if (axis<1 || (axis>3 && axis<11) || axis>13) return 0;
   Double_t stats[kNstat];
   for (Int_t i=4;i<kNstat;i++) stats[i] = 0;
   GetStats(stats);
   if (stats[0] == 0) return 0;
   if (axis<4){
      Int_t ax[3] = {2,4,7};
      return stats[ax[axis-1]]/stats[0];
   } else {
      // mean error = RMS / sqrt( Neff )
      Double_t rms = GetRMS(axis-10);
      Double_t neff = GetEffectiveEntries();
      return ( neff > 0 ? rms/TMath::Sqrt(neff) : 0. );
   }
}

//______________________________________________________________________________
Double_t TH1::GetMeanError(Int_t axis) const
{
   //   -*-*-*-*-*-*Return standard error of mean of this histogram along the X axis*-*-*-*-*
   //               ====================================================
   //  Note that the mean value/RMS is computed using the bins in the currently
   //  defined range (see TAxis::SetRange). By default the range includes
   //  all bins from 1 to nbins included, excluding underflows and overflows.
   //  To force the underflows and overflows in the computation, one must
   //  call the static function TH1::StatOverflows(kTRUE) before filling
   //  the histogram.
   //  Also note, that although the definition of standard error doesn't include the
   //  assumption of normality, many uses of this feature implicitly assume it.

   return GetMean(axis+10);
}

//______________________________________________________________________________
Double_t TH1::GetRMS(Int_t axis) const
{
   //  For axis = 1,2 or 3 returns the Sigma value of the histogram along
   //  X, Y or Z axis
   //  For axis = 11, 12 or 13 returns the error of RMS estimation along
   //  X, Y or Z axis for Normal distribution
   //
   //     Note that the mean value/sigma is computed using the bins in the currently
   //  defined range (see TAxis::SetRange). By default the range includes
   //  all bins from 1 to nbins included, excluding underflows and overflows.
   //  To force the underflows and overflows in the computation, one must
   //  call the static function TH1::StatOverflows(kTRUE) before filling
   //  the histogram.
   //  Note that this function returns the Standard Deviation (Sigma)
   //  of the distribution (not RMS).
   //  The Sigma estimate is computed as Sqrt((1/N)*(Sum(x_i-x_mean)^2))
   //  The name "RMS" was introduced many years ago (Hbook/PAW times).
   //  We kept the name for continuity.

   if (axis<1 || (axis>3 && axis<11) || axis>13) return 0;

   Double_t x, rms2, stats[kNstat];
   for (Int_t i=4;i<kNstat;i++) stats[i] = 0;
   GetStats(stats);
   if (stats[0] == 0) return 0;
   Int_t ax[3] = {2,4,7};
   Int_t axm = ax[axis%10 - 1];
   x    = stats[axm]/stats[0];
   rms2 = TMath::Abs(stats[axm+1]/stats[0] -x*x);
   if (axis<10)
      return TMath::Sqrt(rms2);
   else {
      // The right formula for RMS error depends on 4th momentum (see Kendall-Stuart Vol 1 pag 243)
      // formula valid for only gaussian distribution ( 4-th momentum =  3 * sigma^4 )
      Double_t neff = GetEffectiveEntries();
      return ( neff > 0 ? TMath::Sqrt(rms2/(2*neff) ) : 0. );
   }
}

//______________________________________________________________________________
Double_t TH1::GetRMSError(Int_t axis) const
{
   //  Return error of RMS estimation for Normal distribution
   //
   //  Note that the mean value/RMS is computed using the bins in the currently
   //  defined range (see TAxis::SetRange). By default the range includes
   //  all bins from 1 to nbins included, excluding underflows and overflows.
   //  To force the underflows and overflows in the computation, one must
   //  call the static function TH1::StatOverflows(kTRUE) before filling
   //  the histogram.
   //  Value returned is standard deviation of sample standard deviation.
   //  Note that it is an approximated value which is valid only in the case that the
   //  original data distribution is Normal. The correct one would require
   //  the 4-th momentum value, which cannot be accurately estimated from an histogram since
   //  the x-information for all entries is not kept.

   return GetRMS(axis+10);
}

//______________________________________________________________________________
Double_t TH1::GetSkewness(Int_t axis) const
{
   //For axis = 1, 2 or 3 returns skewness of the histogram along x, y or z axis.
   //For axis = 11, 12 or 13 returns the approximate standard error of skewness
   //of the histogram along x, y or z axis
   //Note, that since third and fourth moment are not calculated
   //at the fill time, skewness and its standard error are computed bin by bin


   if (axis > 0 && axis <= 3){

      Double_t mean = GetMean(axis);
      Double_t rms = GetRMS(axis);
      Double_t rms3 = rms*rms*rms;

      Int_t firstBinX = fXaxis.GetFirst();
      Int_t lastBinX  = fXaxis.GetLast();
      Int_t firstBinY = fYaxis.GetFirst();
      Int_t lastBinY  = fYaxis.GetLast();
      Int_t firstBinZ = fZaxis.GetFirst();
      Int_t lastBinZ  = fZaxis.GetLast();
      // include underflow/overflow if TH1::StatOverflows(kTRUE) in case no range is set on the axis
      if (fgStatOverflows) {
        if ( !fXaxis.TestBit(TAxis::kAxisRange) ) {
            if (firstBinX == 1) firstBinX = 0;
            if (lastBinX ==  fXaxis.GetNbins() ) lastBinX += 1;
         }
         if ( !fYaxis.TestBit(TAxis::kAxisRange) ) {
            if (firstBinY == 1) firstBinY = 0;
            if (lastBinY ==  fYaxis.GetNbins() ) lastBinY += 1;
         }
         if ( !fZaxis.TestBit(TAxis::kAxisRange) ) {
            if (firstBinZ == 1) firstBinZ = 0;
            if (lastBinZ ==  fZaxis.GetNbins() ) lastBinZ += 1;
         }
      }

      Double_t x = 0;
      Double_t sum=0;
      Double_t np=0;
      for (Int_t  binx = firstBinX; binx <= lastBinX; binx++) {
         for (Int_t biny = firstBinY; biny <= lastBinY; biny++) {
            for (Int_t binz = firstBinZ; binz <= lastBinZ; binz++) {
               if (axis==1 ) x = fXaxis.GetBinCenter(binx);
               else if (axis==2 ) x = fYaxis.GetBinCenter(biny);
               else if (axis==3 ) x = fZaxis.GetBinCenter(binz);
               Double_t w = GetBinContent(binx,biny,binz);
               np+=w;
               sum+=w*(x-mean)*(x-mean)*(x-mean);
            }
         }
      }
      sum/=np*rms3;
      return sum;
   }
   else if (axis > 10 && axis <= 13) {
      //compute standard error of skewness
      // assume parent normal distribution use formula from  Kendall-Stuart, Vol 1 pag 243, second edition
      Double_t neff = GetEffectiveEntries();
      return ( neff > 0 ? TMath::Sqrt(6./neff ) : 0. );
   }
   else {
      Error("GetSkewness", "illegal value of parameter");
      return 0;
   }
}

//______________________________________________________________________________
Double_t TH1::GetKurtosis(Int_t axis) const
{
   //For axis =1, 2 or 3 returns kurtosis of the histogram along x, y or z axis.
   //Kurtosis(gaussian(0, 1)) = 0.
   //For axis =11, 12 or 13 returns the approximate standard error of kurtosis
   //of the histogram along x, y or z axis
   //Note, that since third and fourth moment are not calculated
   //at the fill time, kurtosis and its standard error are computed bin by bin

   if (axis > 0 && axis <= 3){

      Double_t mean = GetMean(axis);
      Double_t rms = GetRMS(axis);
      Double_t rms4 = rms*rms*rms*rms;

      Int_t firstBinX = fXaxis.GetFirst();
      Int_t lastBinX  = fXaxis.GetLast();
      Int_t firstBinY = fYaxis.GetFirst();
      Int_t lastBinY  = fYaxis.GetLast();
      Int_t firstBinZ = fZaxis.GetFirst();
      Int_t lastBinZ  = fZaxis.GetLast();
      // include underflow/overflow if TH1::StatOverflows(kTRUE) in case no range is set on the axis
      if (fgStatOverflows) {
        if ( !fXaxis.TestBit(TAxis::kAxisRange) ) {
            if (firstBinX == 1) firstBinX = 0;
            if (lastBinX ==  fXaxis.GetNbins() ) lastBinX += 1;
         }
         if ( !fYaxis.TestBit(TAxis::kAxisRange) ) {
            if (firstBinY == 1) firstBinY = 0;
            if (lastBinY ==  fYaxis.GetNbins() ) lastBinY += 1;
         }
         if ( !fZaxis.TestBit(TAxis::kAxisRange) ) {
            if (firstBinZ == 1) firstBinZ = 0;
            if (lastBinZ ==  fZaxis.GetNbins() ) lastBinZ += 1;
         }
      }

      Double_t x = 0;
      Double_t sum=0;
      Double_t np=0;
      for (Int_t binx = firstBinX; binx <= lastBinX; binx++) {
         for (Int_t biny = firstBinY; biny <= lastBinY; biny++) {
            for (Int_t binz = firstBinZ; binz <= lastBinZ; binz++) {
               if (axis==1 ) x = fXaxis.GetBinCenter(binx);
               else if (axis==2 ) x = fYaxis.GetBinCenter(biny);
               else if (axis==3 ) x = fZaxis.GetBinCenter(binz);
               Double_t w = GetBinContent(binx,biny,binz);
               np+=w;
               sum+=w*(x-mean)*(x-mean)*(x-mean)*(x-mean);
            }
         }
      }
      sum/=(np*rms4);
      return sum-3;

   } else if (axis > 10 && axis <= 13) {
      //compute standard error of skewness
      // assume parent normal distribution use formula from  Kendall-Stuart, Vol 1 pag 243, second edition
      Double_t neff = GetEffectiveEntries();
      return ( neff > 0 ? TMath::Sqrt(24./neff ) : 0. );
   }
   else {
      Error("GetKurtosis", "illegal value of parameter");
      return 0;
   }
}


//______________________________________________________________________________
void TH1::GetStats(Double_t *stats) const
{
   // fill the array stats from the contents of this histogram
   // The array stats must be correctly dimensioned in the calling program.
   // stats[0] = sumw
   // stats[1] = sumw2
   // stats[2] = sumwx
   // stats[3] = sumwx2
   //
   // If no axis-subrange is specified (via TAxis::SetRange), the array stats
   // is simply a copy of the statistics quantities computed at filling time.
   // If a sub-range is specified, the function recomputes these quantities
   // from the bin contents in the current axis range.
   //
   //  Note that the mean value/RMS is computed using the bins in the currently
   //  defined range (see TAxis::SetRange). By default the range includes
   //  all bins from 1 to nbins included, excluding underflows and overflows.
   //  To force the underflows and overflows in the computation, one must
   //  call the static function TH1::StatOverflows(kTRUE) before filling
   //  the histogram.

   if (fBuffer) ((TH1*)this)->BufferEmpty();

   // Loop on bins (possibly including underflows/overflows)
   Int_t bin, binx;
   Double_t w,err;
   Double_t x;
   // case of labels with rebin of axis set
   // statistics in x does not make any sense - set to zero
   if ((const_cast<TAxis&>(fXaxis)).GetLabels() && TestBit(TH1::kCanRebin) ) {
      stats[0] = fTsumw;
      stats[1] = fTsumw2;
      stats[2] = 0;
      stats[3] = 0;
   }
   else if ((fTsumw == 0 && fEntries > 0) || fXaxis.TestBit(TAxis::kAxisRange)) {
      for (bin=0;bin<4;bin++) stats[bin] = 0;

      Int_t firstBinX = fXaxis.GetFirst();
      Int_t lastBinX  = fXaxis.GetLast();
      // include underflow/overflow if TH1::StatOverflows(kTRUE) in case no range is set on the axis
      if (fgStatOverflows && !fXaxis.TestBit(TAxis::kAxisRange)) {
         if (firstBinX == 1) firstBinX = 0;
         if (lastBinX ==  fXaxis.GetNbins() ) lastBinX += 1;
      }
      for (binx = firstBinX; binx <= lastBinX; binx++) {
         x   = fXaxis.GetBinCenter(binx);
         //w   = TMath::Abs(GetBinContent(binx));
         // not sure what to do here if w < 0
         w   = GetBinContent(binx);
         err = TMath::Abs(GetBinError(binx));
         stats[0] += w;
         stats[1] += err*err;
         stats[2] += w*x;
         stats[3] += w*x*x;
      }
      // if (stats[0] < 0) {
      //    // in case total is negative do something ??
      //    stats[0] = 0;
      // }
   } else {
      stats[0] = fTsumw;
      stats[1] = fTsumw2;
      stats[2] = fTsumwx;
      stats[3] = fTsumwx2;
   }
}

//______________________________________________________________________________
void TH1::PutStats(Double_t *stats)
{
   // Replace current statistics with the values in array stats

   fTsumw   = stats[0];
   fTsumw2  = stats[1];
   fTsumwx  = stats[2];
   fTsumwx2 = stats[3];
}

//______________________________________________________________________________
void TH1::ResetStats()
{
   // Reset the statistics including the number of entries
   // and replace with values calculates from bin content
   // The number of entries is set to the total bin content or (in case of weighted histogram)
   // to number of effective entries
   Double_t stats[kNstat] = {0};
   fTsumw = 0;
   fEntries = 1; // to force re-calculation of the statistics in TH1::GetStats
   GetStats(stats);
   PutStats(stats);
   fEntries = TMath::Abs(fTsumw);
   // use effective entries for weighted histograms:  (sum_w) ^2 / sum_w2
   if (fSumw2.fN > 0 && fTsumw > 0 && stats[1] > 0 ) fEntries = stats[0]*stats[0]/ stats[1];
}

//______________________________________________________________________________
Double_t TH1::GetSumOfWeights() const
{
   //   -*-*-*-*-*-*Return the sum of weights excluding under/overflows*-*-*-*-*
   //               ===================================================

   Int_t bin,binx,biny,binz;
   Double_t sum =0;
   for(binz=1; binz<=fZaxis.GetNbins(); binz++) {
      for(biny=1; biny<=fYaxis.GetNbins(); biny++) {
         for(binx=1; binx<=fXaxis.GetNbins(); binx++) {
            bin = GetBin(binx,biny,binz);
            sum += GetBinContent(bin);
         }
      }
   }
   return sum;
}


//______________________________________________________________________________
Double_t TH1::Integral(Option_t *option) const
{
   //Return integral of bin contents. Only bins in the bins range are considered.
   // By default the integral is computed as the sum of bin contents in the range.
   // if option "width" is specified, the integral is the sum of
   // the bin contents multiplied by the bin width in x.

   return Integral(fXaxis.GetFirst(),fXaxis.GetLast(),option);
}

//______________________________________________________________________________
Double_t TH1::Integral(Int_t binx1, Int_t binx2, Option_t *option) const
{
   //Return integral of bin contents in range [binx1,binx2]
   // By default the integral is computed as the sum of bin contents in the range.
   // if option "width" is specified, the integral is the sum of
   // the bin contents multiplied by the bin width in x.
   double err = 0;
   return DoIntegral(binx1,binx2,0,-1,0,-1,err,option);
}
//______________________________________________________________________________
Double_t TH1::IntegralAndError(Int_t binx1, Int_t binx2, Double_t & error, Option_t *option) const
{
   //Return integral of bin contents in range [binx1,binx2] and its error
   // By default the integral is computed as the sum of bin contents in the range.
   // if option "width" is specified, the integral is the sum of
   // the bin contents multiplied by the bin width in x.
   // the error is computed using error propagation from the bin errors assumming that
   // all the bins are uncorrelated
   return DoIntegral(binx1,binx2,0,-1,0,-1,error,option,kTRUE);
}


//______________________________________________________________________________
Double_t TH1::DoIntegral(Int_t binx1, Int_t binx2, Int_t biny1, Int_t biny2, Int_t binz1, Int_t binz2, Double_t & error ,
                          Option_t *option, Bool_t doError) const
{
   // internal function compute integral and optionally the error  between the limits
   // specified by the bin number values working for all histograms (1D, 2D and 3D)

   Int_t nbinsx = GetNbinsX();
   if (binx1 < 0) binx1 = 0;
   if (binx2 > nbinsx+1 || binx2 < binx1) binx2 = nbinsx+1;
   if (GetDimension() > 1) {
      Int_t nbinsy = GetNbinsY();
      if (biny1 < 0) biny1 = 0;
      if (biny2 > nbinsy+1 || biny2 < biny1) biny2 = nbinsy+1;
   } else {
      biny1 = 0; biny2 = 0;
   }
   if (GetDimension() > 2) {
      Int_t nbinsz = GetNbinsZ();
      if (binz1 < 0) binz1 = 0;
      if (binz2 > nbinsz+1 || binz2 < binz1) binz2 = nbinsz+1;
   } else {
      binz1 = 0; binz2 = 0;
   }

   //   - Loop on bins in specified range
   TString opt = option;
   opt.ToLower();
   Bool_t width   = kFALSE;
   if (opt.Contains("width")) width = kTRUE;


   Double_t dx = 1.;
   Double_t dy = 1.;
   Double_t dz = 1.;
   Double_t integral = 0;
   Double_t igerr2 = 0;
   for (Int_t binx = binx1; binx <= binx2; ++binx) {
      if (width) dx = fXaxis.GetBinWidth(binx);
      for (Int_t biny = biny1; biny <= biny2; ++biny) {
         if (width) dy = fYaxis.GetBinWidth(biny);
         for (Int_t binz = binz1; binz <= binz2; ++binz) {
            if (width) dz = fZaxis.GetBinWidth(binz);
            Int_t bin  = GetBin(binx, biny, binz);
            if (width) integral += GetBinContent(bin)*dx*dy*dz;
            else       integral += GetBinContent(bin);
            if (doError) {
               if (width)  igerr2 += GetBinError(bin)*GetBinError(bin)*dx*dx*dy*dy*dz*dz;
               else        igerr2 += GetBinError(bin)*GetBinError(bin);
            }
         }
      }
   }

   if (doError) error = TMath::Sqrt(igerr2);
   return integral;
}


//______________________________________________________________________________
Double_t TH1::AndersonDarlingTest(const TH1 *h2, Option_t *option) const
{
   //  Statistical test of compatibility in shape between
   //  this histogram and h2, using the Anderson-Darling 2 sample test.
   //  The AD 2 sample test formula are derived from the paper 
   //  F.W Scholz, M.A. Stephens "k-Sample Anderson-Darling Test". 
   //  The test is implemented in root in the ROOT::Math::GoFTest class
   //  It is the same formula ( (6) in the paper), and also shown in this preprint
   //  http://arxiv.org/pdf/0804.0380v1.pdf
   //  Binned data are considered as un-binned data 
   //   with identical observation happening in the bin center. 
   //
   //     option is a character string to specify options
   //         "D" Put out a line of "Debug" printout
   //         "T" Return the normalized A-D test statistic
   // 
   //  Note1: Underflow and overflow are not considered in the test
   //  Note2:  The test works only for un-weighted histogram (i.e. representing counts)
   //  Note3:  The histograms are not required to have the same X axis
   //  Note4:  The test works only for 1-dimensional histograms

   Double_t advalue = 0; 
   Double_t pvalue = AndersonDarlingTest(h2, advalue); 

   TString opt = option;
   opt.ToUpper();
   if (opt.Contains("D") ) {
      printf(" AndersonDarlingTest Prob     = %g, AD TestStatistic  = %g\n",pvalue,advalue);
   } 
   if (opt.Contains("T") ) return advalue; 

   return pvalue;    
}

//______________________________________________________________________________
Double_t TH1::AndersonDarlingTest(const TH1 *h2, Double_t & advalue) const
{
   // Same funciton as above but returning also the test statistic value

   if (GetDimension() != 1 || h2->GetDimension() != 1) {
      Error("AndersonDarlingTest","Histograms must be 1-D");
      return -1; 
   }

   // use the BinData class 
   ROOT::Fit::BinData data1; 
   ROOT::Fit::BinData data2; 
   ROOT::Fit::FillData(data1, this, 0);
   ROOT::Fit::FillData(data2, h2, 0);

   double pvalue; 
   ROOT::Math::GoFTest::AndersonDarling2SamplesTest(data1,data2, pvalue,advalue);

   return pvalue; 
}

//______________________________________________________________________________
Double_t TH1::KolmogorovTest(const TH1 *h2, Option_t *option) const
{
   //  Statistical test of compatibility in shape between
   //  this histogram and h2, using Kolmogorov test.
   //  Note that the KolmogorovTest (KS) test should in theory be used only for unbinned data
   //  and not for binned data as in the case of the histogram (see NOTE 3 below). 
   //  So, before using this method blindly, read the NOTE 3. 
   //
   //
   //     Default: Ignore under- and overflow bins in comparison
   //
   //     option is a character string to specify options
   //         "U" include Underflows in test  (also for 2-dim)
   //         "O" include Overflows     (also valid for 2-dim)
   //         "N" include comparison of normalizations
   //         "D" Put out a line of "Debug" printout
   //         "M" Return the Maximum Kolmogorov distance instead of prob
   //         "X" Run the pseudo experiments post-processor with the following procedure:
   //             make pseudoexperiments based on random values from the parent
   //             distribution and compare the KS distance of the pseudoexperiment
   //             to the parent distribution. Bin the KS distances in a histogram,
   //             and then take the integral of all the KS values above the value
   //             obtained from the original data to Monte Carlo distribution.
   //             The number of pseudo-experiments nEXPT is currently fixed at 1000.
   //             The function returns the integral.
   //             (thanks to Ben Kilminster to submit this procedure). Note that
   //             this option "X" is much slower.
   //
   //   The returned function value is the probability of test
   //       (much less than one means NOT compatible)
   //
   //  Code adapted by Rene Brun from original HBOOK routine HDIFF
   //
   //  NOTE1
   //  A good description of the Kolmogorov test can be seen at:
   //    http://www.itl.nist.gov/div898/handbook/eda/section3/eda35g.htm
   //
   //  NOTE2
   //  see also alternative function TH1::Chi2Test
   //  The Kolmogorov test is assumed to give better results than Chi2Test
   //  in case of histograms with low statistics.
   //
   //  NOTE3 (Jan Conrad, Fred James)
   //  "The returned value PROB is calculated such that it will be
   //  uniformly distributed between zero and one for compatible histograms,
   //  provided the data are not binned (or the number of bins is very large
   //  compared with the number of events). Users who have access to unbinned
   //  data and wish exact confidence levels should therefore not put their data
   //  into histograms, but should call directly TMath::KolmogorovTest. On
   //  the other hand, since TH1 is a convenient way of collecting data and
   //  saving space, this function has been provided. However, the values of
   //  PROB for binned data will be shifted slightly higher than expected,
   //  depending on the effects of the binning. For example, when comparing two
   //  uniform distributions of 500 events in 100 bins, the values of PROB,
   //  instead of being exactly uniformly distributed between zero and one, have
   //  a mean value of about 0.56. We can apply a useful
   //  rule: As long as the bin width is small compared with any significant
   //  physical effect (for example the experimental resolution) then the binning
   //  cannot have an important effect. Therefore, we believe that for all
   //  practical purposes, the probability value PROB is calculated correctly
   //  provided the user is aware that:
   //     1. The value of PROB should not be expected to have exactly the correct
   //  distribution for binned data.
   //     2. The user is responsible for seeing to it that the bin widths are
   //  small compared with any physical phenomena of interest.
   //     3. The effect of binning (if any) is always to make the value of PROB
   //  slightly too big. That is, setting an acceptance criterion of (PROB>0.05
   //  will assure that at most 5% of truly compatible histograms are rejected,
   //  and usually somewhat less."
   //
   //  Note also that for GoF test of unbinned data ROOT provides also the class
   //  ROOT::Math::GoFTest. The class has also method for doing one sample tests
   //  (i.e. comparing the data with a given distribution). 

   TString opt = option;
   opt.ToUpper();

   Double_t prob = 0;
   TH1 *h1 = (TH1*)this;
   if (h2 == 0) return 0;
   TAxis *axis1 = h1->GetXaxis();
   TAxis *axis2 = h2->GetXaxis();
   Int_t ncx1   = axis1->GetNbins();
   Int_t ncx2   = axis2->GetNbins();

   // Check consistency of dimensions
   if (h1->GetDimension() != 1 || h2->GetDimension() != 1) {
      Error("KolmogorovTest","Histograms must be 1-D\n");
      return 0;
   }

   // Check consistency in number of channels
   if (ncx1 != ncx2) {
      Error("KolmogorovTest","Number of channels is different, %d and %d\n",ncx1,ncx2);
      return 0;
   }

   // Check consistency in channel edges
   Double_t difprec = 1e-5;
   Double_t diff1 = TMath::Abs(axis1->GetXmin() - axis2->GetXmin());
   Double_t diff2 = TMath::Abs(axis1->GetXmax() - axis2->GetXmax());
   if (diff1 > difprec || diff2 > difprec) {
      Error("KolmogorovTest","histograms with different binning");
      return 0;
   }

   Bool_t afunc1 = kFALSE;
   Bool_t afunc2 = kFALSE;
   Double_t sum1 = 0, sum2 = 0;
   Double_t ew1, ew2, w1 = 0, w2 = 0;
   Int_t bin;
   Int_t ifirst = 1;
   Int_t ilast = ncx1;
   // integral of all bins (use underflow/overflow if option)
   if (opt.Contains("U")) ifirst = 0;
   if (opt.Contains("O")) ilast = ncx1 +1;
   for (bin = ifirst; bin <= ilast; bin++) {
      sum1 += h1->GetBinContent(bin);
      sum2 += h2->GetBinContent(bin);
      ew1   = h1->GetBinError(bin);
      ew2   = h2->GetBinError(bin);
      w1   += ew1*ew1;
      w2   += ew2*ew2;
   }
   if (sum1 == 0) {
      Error("KolmogorovTest","Histogram1 %s integral is zero\n",h1->GetName());
      return 0;
   }
   if (sum2 == 0) {
      Error("KolmogorovTest","Histogram2 %s integral is zero\n",h2->GetName());
      return 0;
   }

   // calculate the effective entries.
   // the case when errors are zero (w1 == 0 or w2 ==0) are equivalent to
   // compare to a function. In that case the rescaling is done only on sqrt(esum2) or sqrt(esum1)
   Double_t esum1 = 0, esum2 = 0;
   if (w1 > 0)
      esum1 = sum1 * sum1 / w1;
   else
      afunc1 = kTRUE;    // use later for calculating z

   if (w2 > 0)
      esum2 = sum2 * sum2 / w2;
   else
      afunc2 = kTRUE;    // use later for calculating z

   if (afunc2 && afunc1) {
      Error("KolmogorovTest","Errors are zero for both histograms\n");
      return 0;
   }


   Double_t s1 = 1/sum1;
   Double_t s2 = 1/sum2;

   // Find largest difference for Kolmogorov Test
   Double_t dfmax =0, rsum1 = 0, rsum2 = 0;

   for (bin=ifirst;bin<=ilast;bin++) {
      rsum1 += s1*h1->GetBinContent(bin);
      rsum2 += s2*h2->GetBinContent(bin);
      dfmax = TMath::Max(dfmax,TMath::Abs(rsum1-rsum2));
   }

   // Get Kolmogorov probability
   Double_t z, prb1=0, prb2=0, prb3=0;

   // case h1 is exact (has zero errors)
  if  (afunc1)
      z = dfmax*TMath::Sqrt(esum2);
  // case h2 has zero errors
  else if (afunc2)
      z = dfmax*TMath::Sqrt(esum1);
  else
     // for comparison between two data sets
     z = dfmax*TMath::Sqrt(esum1*esum2/(esum1+esum2));

   prob = TMath::KolmogorovProb(z);

   // option N to combine normalization makes sense if both afunc1 and afunc2 are false
   if (opt.Contains("N") && !(afunc1 || afunc2 ) ) {
      // Combine probabilities for shape and normalization,
      prb1 = prob;
      Double_t d12    = esum1-esum2;
      Double_t chi2   = d12*d12/(esum1+esum2);
      prb2 = TMath::Prob(chi2,1);
      // see Eadie et al., section 11.6.2
      if (prob > 0 && prb2 > 0) prob *= prb2*(1-TMath::Log(prob*prb2));
      else                      prob = 0;
   }
   // X option. Pseudo-experiments post-processor to determine KS probability
   const Int_t nEXPT = 1000;
   if (opt.Contains("X") && !(afunc1 || afunc2 ) ) {
      Double_t dSEXPT;
      TH1 *hExpt = (TH1*)(gDirectory ? gDirectory->CloneObject(this,kFALSE) : gROOT->CloneObject(this,kFALSE));
      // make nEXPT experiments (this should be a parameter)
      prb3 = 0;
      for (Int_t i=0; i < nEXPT; i++) {
         hExpt->Reset();
         hExpt->FillRandom(h1,(Int_t)esum2);
         dSEXPT = KolmogorovTest(hExpt,"M");
         if (dSEXPT>dfmax) prb3 += 1.0;
      }
      prb3 /= (Double_t)nEXPT;
      delete hExpt;
   }

   // debug printout
   if (opt.Contains("D")) {
      printf(" Kolmo Prob  h1 = %s, sum bin content =%g  effective entries =%g\n",h1->GetName(),sum1,esum1);
      printf(" Kolmo Prob  h2 = %s, sum bin content =%g  effective entries =%g\n",h2->GetName(),sum2,esum2);
      printf(" Kolmo Prob     = %g, Max Dist = %g\n",prob,dfmax);
      if (opt.Contains("N"))
         printf(" Kolmo Prob     = %f for shape alone, =%f for normalisation alone\n",prb1,prb2);
      if (opt.Contains("X"))
         printf(" Kolmo Prob     = %f with %d pseudo-experiments\n",prb3,nEXPT);
   }
   // This numerical error condition should never occur:
   if (TMath::Abs(rsum1-1) > 0.002) Warning("KolmogorovTest","Numerical problems with h1=%s\n",h1->GetName());
   if (TMath::Abs(rsum2-1) > 0.002) Warning("KolmogorovTest","Numerical problems with h2=%s\n",h2->GetName());

   if(opt.Contains("M"))      return dfmax;
   else if(opt.Contains("X")) return prb3;
   else                       return prob;
}

//______________________________________________________________________________
void TH1::SetContent(const Double_t *content)
{
   //   -*-*-*-*-*-*Replace bin contents by the contents of array content*-*-*-*
   //               =====================================================
   Int_t bin;
   Double_t bincontent;
   int nbins = fNcells; // save value because SetBinContent can change fNCells
   for (bin=0; bin<nbins; bin++) {
      bincontent = *(content + bin);
      SetBinContent(bin, bincontent);
   }
}

//______________________________________________________________________________
Int_t TH1::GetContour(Double_t *levels)
{
   //  Return contour values into array levels if pointer levels is non zero
   //
   //  The function returns the number of contour levels.
   //  see GetContourLevel to return one contour only
   //

   Int_t nlevels = fContour.fN;
   if (levels) {
      if (nlevels == 0) {
         nlevels = 20;
         SetContour(nlevels);
      } else {
         if (TestBit(kUserContour) == 0) SetContour(nlevels);
      }
      for (Int_t level=0; level<nlevels; level++) levels[level] = fContour.fArray[level];
   }
   return nlevels;
}

//______________________________________________________________________________
Double_t TH1::GetContourLevel(Int_t level) const
{
   // Return value of contour number level
   // see GetContour to return the array of all contour levels

   if (level <0 || level >= fContour.fN) return 0;
   Double_t zlevel = fContour.fArray[level];
   return zlevel;
}

//______________________________________________________________________________
Double_t TH1::GetContourLevelPad(Int_t level) const
{
   // Return the value of contour number "level" in Pad coordinates ie: if the Pad
   // is in log scale along Z it returns le log of the contour level value.
   // see GetContour to return the array of all contour levels

   if (level <0 || level >= fContour.fN) return 0;
   Double_t zlevel = fContour.fArray[level];

   // In case of user defined contours and Pad in log scale along Z,
   // fContour.fArray doesn't contain the log of the contour whereas it does
   // in case of equidistant contours.
   if (gPad && gPad->GetLogz() && TestBit(kUserContour)) {
      if (zlevel <= 0) return 0;
      zlevel = TMath::Log10(zlevel);
   }
   return zlevel;
}

//______________________________________________________________________________
void TH1::SetBuffer(Int_t buffersize, Option_t * /*option*/)
{
   // set the maximum number of entries to be kept in the buffer

   if (fBuffer) {
      BufferEmpty();
      delete [] fBuffer;
      fBuffer = 0;
   }
   if (buffersize <= 0) {
      fBufferSize = 0;
      return;
   }
   if (buffersize < 100) buffersize = 100;
   fBufferSize = 1 + buffersize*(fDimension+1);
   fBuffer = new Double_t[fBufferSize];
   memset(fBuffer,0,sizeof(Double_t)*fBufferSize);
}

//______________________________________________________________________________
void TH1::SetContour(Int_t  nlevels, const Double_t *levels)
{
   //  Set the number and values of contour levels.
   //
   //  By default the number of contour levels is set to 20. The contours values
   //  in the array "levels" should be specified in increasing order.
   //
   //  if argument levels = 0 or missing, equidistant contours are computed

   Int_t level;
   ResetBit(kUserContour);
   if (nlevels <=0 ) {
      fContour.Set(0);
      return;
   }
   fContour.Set(nlevels);

   //   -  Contour levels are specified
   if (levels) {
      SetBit(kUserContour);
      for (level=0; level<nlevels; level++) fContour.fArray[level] = levels[level];
   } else {
      //   - contour levels are computed automatically as equidistant contours
      Double_t zmin = GetMinimum();
      Double_t zmax = GetMaximum();
      if ((zmin == zmax) && (zmin != 0)) {
         zmax += 0.01*TMath::Abs(zmax);
         zmin -= 0.01*TMath::Abs(zmin);
      }
      Double_t dz   = (zmax-zmin)/Double_t(nlevels);
      if (gPad && gPad->GetLogz()) {
         if (zmax <= 0) return;
         if (zmin <= 0) zmin = 0.001*zmax;
         zmin = TMath::Log10(zmin);
         zmax = TMath::Log10(zmax);
         dz   = (zmax-zmin)/Double_t(nlevels);
      }
      for (level=0; level<nlevels; level++) {
         fContour.fArray[level] = zmin + dz*Double_t(level);
      }
   }
}

//______________________________________________________________________________
void TH1::SetContourLevel(Int_t level, Double_t value)
{
   // Set value for one contour level.

   if (level <0 || level >= fContour.fN) return;
   SetBit(kUserContour);
   fContour.fArray[level] = value;
}

//______________________________________________________________________________
Double_t TH1::GetMaximum(Double_t maxval) const
{
   //  Return maximum value smaller than maxval of bins in the range,
   //  unless the value has been overridden by TH1::SetMaximum,
   //  in which case it returns that value. (This happens, for example,
   //  when the histogram is drawn and the y or z axis limits are changed
   //
   //  To get the maximum value of bins in the histogram regardless of
   //  whether the value has been overridden, use
   //      h->GetBinContent(h->GetMaximumBin())

   if (fMaximum != -1111) return fMaximum;
   Int_t bin, binx, biny, binz;
   Int_t xfirst  = fXaxis.GetFirst();
   Int_t xlast   = fXaxis.GetLast();
   Int_t yfirst  = fYaxis.GetFirst();
   Int_t ylast   = fYaxis.GetLast();
   Int_t zfirst  = fZaxis.GetFirst();
   Int_t zlast   = fZaxis.GetLast();
   Double_t maximum = -FLT_MAX, value;
   for (binz=zfirst;binz<=zlast;binz++) {
      for (biny=yfirst;biny<=ylast;biny++) {
         for (binx=xfirst;binx<=xlast;binx++) {
            bin = GetBin(binx,biny,binz);
            value = GetBinContent(bin);
            if (value > maximum && value < maxval) maximum = value;
         }
      }
   }
   return maximum;
}

//______________________________________________________________________________
Int_t TH1::GetMaximumBin() const
{
   //   -*-*-*-*-*Return location of bin with maximum value in the range*-*
   //             ======================================================
   Int_t locmax, locmay, locmaz;
   return GetMaximumBin(locmax, locmay, locmaz);
}

//______________________________________________________________________________
Int_t TH1::GetMaximumBin(Int_t &locmax, Int_t &locmay, Int_t &locmaz) const
{
   //   -*-*-*-*-*Return location of bin with maximum value in the range*-*
   //             ======================================================
   Int_t bin, binx, biny, binz;
   Int_t locm;
   Int_t xfirst  = fXaxis.GetFirst();
   Int_t xlast   = fXaxis.GetLast();
   Int_t yfirst  = fYaxis.GetFirst();
   Int_t ylast   = fYaxis.GetLast();
   Int_t zfirst  = fZaxis.GetFirst();
   Int_t zlast   = fZaxis.GetLast();
   Double_t maximum = -FLT_MAX, value;
   locm = locmax = locmay = locmaz = 0;
   for (binz=zfirst;binz<=zlast;binz++) {
      for (biny=yfirst;biny<=ylast;biny++) {
         for (binx=xfirst;binx<=xlast;binx++) {
            bin = GetBin(binx,biny,binz);
            value = GetBinContent(bin);
            if (value > maximum) {
               maximum = value;
               locm    = bin;
               locmax  = binx;
               locmay  = biny;
               locmaz  = binz;
            }
         }
      }
   }
   return locm;
}

//______________________________________________________________________________
Double_t TH1::GetMinimum(Double_t minval) const
{
   //  Return minimum value larger than minval of bins in the range,
   //  unless the value has been overridden by TH1::SetMinimum,
   //  in which case it returns that value. (This happens, for example,
   //  when the histogram is drawn and the y or z axis limits are changed
   //
   //  To get the minimum value of bins in the histogram regardless of
   //  whether the value has been overridden, use
   //     h->GetBinContent(h->GetMinimumBin())

   if (fMinimum != -1111) return fMinimum;
   Int_t bin, binx, biny, binz;
   Int_t xfirst  = fXaxis.GetFirst();
   Int_t xlast   = fXaxis.GetLast();
   Int_t yfirst  = fYaxis.GetFirst();
   Int_t ylast   = fYaxis.GetLast();
   Int_t zfirst  = fZaxis.GetFirst();
   Int_t zlast   = fZaxis.GetLast();
   Double_t minimum=FLT_MAX, value;
   for (binz=zfirst;binz<=zlast;binz++) {
      for (biny=yfirst;biny<=ylast;biny++) {
         for (binx=xfirst;binx<=xlast;binx++) {
            bin = GetBin(binx,biny,binz);
            value = GetBinContent(bin);
            if (value < minimum && value > minval) minimum = value;
         }
      }
   }
   return minimum;
}

//______________________________________________________________________________
Int_t TH1::GetMinimumBin() const
{
   //   -*-*-*-*-*Return location of bin with minimum value in the range*-*
   //             ======================================================
   Int_t locmix, locmiy, locmiz;
   return GetMinimumBin(locmix, locmiy, locmiz);
}

//______________________________________________________________________________
Int_t TH1::GetMinimumBin(Int_t &locmix, Int_t &locmiy, Int_t &locmiz) const
{
   //   -*-*-*-*-*Return location of bin with minimum value in the range*-*
   //             ======================================================
   Int_t bin, binx, biny, binz;
   Int_t locm;
   Int_t xfirst  = fXaxis.GetFirst();
   Int_t xlast   = fXaxis.GetLast();
   Int_t yfirst  = fYaxis.GetFirst();
   Int_t ylast   = fYaxis.GetLast();
   Int_t zfirst  = fZaxis.GetFirst();
   Int_t zlast   = fZaxis.GetLast();
   Double_t minimum = FLT_MAX, value;
   locm = locmix = locmiy = locmiz = 0;
   for (binz=zfirst;binz<=zlast;binz++) {
      for (biny=yfirst;biny<=ylast;biny++) {
         for (binx=xfirst;binx<=xlast;binx++) {
            bin = GetBin(binx,biny,binz);
            value = GetBinContent(bin);
            if (value < minimum) {
               minimum = value;
               locm    = bin;
               locmix  = binx;
               locmiy  = biny;
               locmiz  = binz;
            }
         }
      }
   }
   return locm;
}

//______________________________________________________________________________
void TH1::SetBins(Int_t nx, Double_t xmin, Double_t xmax)
{
   //   -*-*-*-*-*-*-*Redefine  x axis parameters*-*-*-*-*-*-*-*-*-*-*-*
   //                 ===========================
   // The X axis parameters are modified.
   // The bins content array is resized
   // if errors (Sumw2) the errors array is resized
   // The previous bin contents are lost
   // To change only the axis limits, see TAxis::SetRange

   if (GetDimension() != 1) {
      Error("SetBins","Operation only valid for 1-d histograms");
      return;
   }
   fXaxis.SetRange(0,0);
   fXaxis.Set(nx,xmin,xmax);
   fYaxis.Set(1,0,1);
   fZaxis.Set(1,0,1);
   fNcells = nx+2;
   SetBinsLength(fNcells);
   if (fSumw2.fN) {
      fSumw2.Set(fNcells);
   }
}

//______________________________________________________________________________
void TH1::SetBins(Int_t nx, const Double_t *xBins)
{
   //   -*-*-*-*-*-*-*Redefine  x axis parameters with variable bin sizes *-*-*-*-*-*-*-*-*-*
   //                 ===================================================
   // The X axis parameters are modified.
   // The bins content array is resized
   // if errors (Sumw2) the errors array is resized
   // The previous bin contents are lost
   // To change only the axis limits, see TAxis::SetRange
   // xBins is supposed to be of length nx+1
   if (GetDimension() != 1) {
      Error("SetBins","Operation only valid for 1-d histograms");
      return;
   }
   fXaxis.SetRange(0,0);
   fXaxis.Set(nx,xBins);
   fYaxis.Set(1,0,1);
   fZaxis.Set(1,0,1);
   fNcells = nx+2;
   SetBinsLength(fNcells);
   if (fSumw2.fN) {
      fSumw2.Set(fNcells);
   }
}

//______________________________________________________________________________
void TH1::SetBins(Int_t nx, Double_t xmin, Double_t xmax, Int_t ny, Double_t ymin, Double_t ymax)
{
   //   -*-*-*-*-*-*-*Redefine  x and y axis parameters*-*-*-*-*-*-*-*-*-*-*-*
   //                 =================================
   // The X and Y axis parameters are modified.
   // The bins content array is resized
   // if errors (Sumw2) the errors array is resized
   // The previous bin contents are lost
   // To change only the axis limits, see TAxis::SetRange

   if (GetDimension() != 2) {
      Error("SetBins","Operation only valid for 2-D histograms");
      return;
   }
   fXaxis.SetRange(0,0);
   fYaxis.SetRange(0,0);
   fXaxis.Set(nx,xmin,xmax);
   fYaxis.Set(ny,ymin,ymax);
   fZaxis.Set(1,0,1);
   fNcells = (nx+2)*(ny+2);
   SetBinsLength(fNcells);
   if (fSumw2.fN) {
      fSumw2.Set(fNcells);
   }
}

//______________________________________________________________________________
void TH1::SetBins(Int_t nx, const Double_t *xBins, Int_t ny, const Double_t *yBins)
{
   //   -*-*-*-*-*-*-*Redefine  x and y axis parameters with variable bin sizes *-*-*-*-*-*-*-*-*
   //                 =========================================================
   // The X and Y axis parameters are modified.
   // The bins content array is resized
   // if errors (Sumw2) the errors array is resized
   // The previous bin contents are lost
   // To change only the axis limits, see TAxis::SetRange
   // xBins is supposed to be of length nx+1, yBins is supposed to be of length ny+1

   if (GetDimension() != 2) {
      Error("SetBins","Operation only valid for 2-D histograms");
      return;
   }
   fXaxis.SetRange(0,0);
   fYaxis.SetRange(0,0);
   fXaxis.Set(nx,xBins);
   fYaxis.Set(ny,yBins);
   fZaxis.Set(1,0,1);
   fNcells = (nx+2)*(ny+2);
   SetBinsLength(fNcells);
   if (fSumw2.fN) {
      fSumw2.Set(fNcells);
   }
}


//______________________________________________________________________________
void TH1::SetBins(Int_t nx, Double_t xmin, Double_t xmax, Int_t ny, Double_t ymin, Double_t ymax, Int_t nz, Double_t zmin, Double_t zmax)
{
   //   -*-*-*-*-*-*-*Redefine  x, y and z axis parameters*-*-*-*-*-*-*-*-*-*-*-*
   //                 ====================================
   // The X, Y and Z axis parameters are modified.
   // The bins content array is resized
   // if errors (Sumw2) the errors array is resized
   // The previous bin contents are lost
   // To change only the axis limits, see TAxis::SetRange

   if (GetDimension() != 3) {
      Error("SetBins","Operation only valid for 3-D histograms");
      return;
   }
   fXaxis.SetRange(0,0);
   fYaxis.SetRange(0,0);
   fZaxis.SetRange(0,0);
   fXaxis.Set(nx,xmin,xmax);
   fYaxis.Set(ny,ymin,ymax);
   fZaxis.Set(nz,zmin,zmax);
   fNcells = (nx+2)*(ny+2)*(nz+2);
   SetBinsLength(fNcells);
   if (fSumw2.fN) {
      fSumw2.Set(fNcells);
   }
}

//______________________________________________________________________________
void TH1::SetBins(Int_t nx, const Double_t *xBins, Int_t ny, const Double_t *yBins, Int_t nz, const Double_t *zBins)
{
   //   -*-*-*-*-*-*-*Redefine  x, y and z axis parameters with variable bin sizes *-*-*-*-*-*-*-*-*
   //                 ============================================================
   // The X, Y and Z axis parameters are modified.
   // The bins content array is resized
   // if errors (Sumw2) the errors array is resized
   // The previous bin contents are lost
   // To change only the axis limits, see TAxis::SetRange
   // xBins is supposed to be of length nx+1, yBins is supposed to be of length ny+1,
   // zBins is supposed to be of length nz+1

   if (GetDimension() != 3) {
      Error("SetBins","Operation only valid for 3-D histograms");
      return;
   }
   fXaxis.SetRange(0,0);
   fYaxis.SetRange(0,0);
   fZaxis.SetRange(0,0);
   fXaxis.Set(nx,xBins);
   fYaxis.Set(ny,yBins);
   fZaxis.Set(nz,zBins);
   fNcells = (nx+2)*(ny+2)*(nz+2);
   SetBinsLength(fNcells);
   if (fSumw2.fN) {
      fSumw2.Set(fNcells);
   }
}

//______________________________________________________________________________
void TH1::SetMaximum(Double_t maximum)
{
   // Set the maximum value for the Y axis, in case of 1-D histograms,
   // or the Z axis in case of 2-D histograms
   //
   // By default the maximum value used in drawing is the maximum value of the histogram plus
   // a margin of 10 per cent. If this function has been called, the value of 'maximum' is
   // used, with no extra margin.
   //
   // TH1::GetMaximum returns the maximum value of the bins in the histogram, unless the
   // maximum has been set manually by this function or by altering the y/z axis limits.
   // Use TH1::GetMaximumBin to find the bin with the maximum value of an histogram
   //
   fMaximum = maximum;
}


//______________________________________________________________________________
void TH1::SetMinimum(Double_t minimum)
{
   // Set the minimum value for the Y axis, in case of 1-D histograms,
   // or the Z axis in case of 2-D histograms
   //
   // By default the minimum value used in drawing is the minimum value of the histogram plus
   // a margin of 10 per cent. If this function has been called, the value of 'minimum' is
   // used, with no extra margin.
   //
   // TH1::GetMinimum returns the minimum value of the bins in the histogram, unless the
   // minimum has been set manually by this function or by altering the y/z axis limits.
   // Use TH1::GetMinimumBin to find the bin with the minimum value of an histogram
   //
   fMinimum = minimum;
}

//______________________________________________________________________________
void TH1::SetDirectory(TDirectory *dir)
{
   // By default when an histogram is created, it is added to the list
   // of histogram objects in the current directory in memory.
   // Remove reference to this histogram from current directory and add
   // reference to new directory dir. dir can be 0 in which case the
   // histogram does not belong to any directory.

   if (fDirectory == dir) return;
   if (fDirectory) fDirectory->Remove(this);
   fDirectory = dir;
   if (fDirectory) fDirectory->Append(this);
}


//______________________________________________________________________________
void TH1::SetError(const Double_t *error)
{
   //   -*-*-*-*-*-*-*Replace bin errors by values in array error*-*-*-*-*-*-*-*-*
   //                 ===========================================
   Int_t bin;
   Double_t binerror;
   for (bin=0; bin<fNcells; bin++) {
      binerror = error[bin];
      SetBinError(bin, binerror);
   }
}

//______________________________________________________________________________
void TH1::SetName(const char *name)
{
   // Change the name of this histogram
   //

   //  Histograms are named objects in a THashList.
   //  We must update the hashlist if we change the name
   if (fDirectory) fDirectory->Remove(this);
   fName = name;
   if (fDirectory) fDirectory->Append(this);
}

//______________________________________________________________________________
void TH1::SetNameTitle(const char *name, const char *title)
{
   // Change the name and title of this histogram
   //

   //  Histograms are named objects in a THashList.
   //  We must update the hashlist if we change the name
   if (fDirectory) fDirectory->Remove(this);
   fName  = name;
   SetTitle(title);
   if (fDirectory) fDirectory->Append(this);
}

//______________________________________________________________________________
void TH1::SetStats(Bool_t stats)
{
   //   -*-*-*-*-*-*-*Set statistics option on/off
   //                 ============================
   //  By default, the statistics box is drawn.
   //  The paint options can be selected via gStyle->SetOptStats.
   //  This function sets/resets the kNoStats bin in the histogram object.
   //  It has priority over the Style option.

   ResetBit(kNoStats);
   if (!stats) {
      SetBit(kNoStats);
      //remove the "stats" object from the list of functions
      if (fFunctions) {
         TObject *obj = fFunctions->FindObject("stats");
         if (obj) {
            fFunctions->Remove(obj);
            delete obj;
         }
      }
   }
}

//______________________________________________________________________________
void TH1::Sumw2(Bool_t flag)
{
   // Create structure to store sum of squares of weights*-*-*-*-*-*-*-*
   //
   //     if histogram is already filled, the sum of squares of weights
   //     is filled with the existing bin contents
   //
   //     The error per bin will be computed as sqrt(sum of squares of weight)
   //     for each bin.
   //
   //  This function is automatically called when the histogram is created
   //  if the static function TH1::SetDefaultSumw2 has been called before.
   //  If flag = false the structure is deleted 
   
   if (!flag) { 
      // clear the array if existing - do nothing otherwise
      if (fSumw2.fN > 0 ) fSumw2.Set(0);
      return;
   }

   if (fSumw2.fN == fNcells) {
      if (!fgDefaultSumw2 )
         Warning("Sumw2","Sum of squares of weights structure already created");
      return;
   }

   fSumw2.Set(fNcells);

   if ( fEntries > 0 )
      for (Int_t bin=0; bin<fNcells; bin++) {
         fSumw2.fArray[bin] = TMath::Abs(GetBinContent(bin));
      }
}

//______________________________________________________________________________
TF1 *TH1::GetFunction(const char *name) const
{
   //   -*-*-*Return pointer to function with name*-*-*-*-*-*-*-*-*-*-*-*-*
   //         ===================================
   //
   // Functions such as TH1::Fit store the fitted function in the list of
   // functions of this histogram.

   return (TF1*)fFunctions->FindObject(name);
}

//______________________________________________________________________________
Double_t TH1::GetBinError(Int_t bin) const
{
   //   -*-*-*-*-*Return value of error associated to bin number bin*-*-*-*-*
   //             ==================================================
   //
   //    if the sum of squares of weights has been defined (via Sumw2),
   //    this function returns the sqrt(sum of w2).
   //    otherwise it returns the sqrt(contents) for this bin.
   //
   //   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   if (bin < 0) bin = 0;
   if (bin >= fNcells) bin = fNcells-1;
   if (fBuffer) ((TH1*)this)->BufferEmpty();
   if (fSumw2.fN) {
      Double_t err2 = fSumw2.fArray[bin];
      return TMath::Sqrt(err2);
   }
   Double_t error2 = TMath::Abs(GetBinContent(bin));
   return TMath::Sqrt(error2);
}

//______________________________________________________________________________
Double_t TH1::GetBinErrorLow(Int_t bin) const
{
   //   -*-*-*-*-*Return lower error associated to bin number bin*-*-*-*-*
   //             ==================================================
   //
   //    The error will depend on the statistic option used will return
   //     the binContent - lower interval value
   //
   //
   //   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   if (fBinStatErrOpt == kNormal || fSumw2.fN) return GetBinError(bin);
   if (bin < 0) bin = 0;
   if (bin >= fNcells) bin = fNcells-1;
   if (fBuffer) ((TH1*)this)->BufferEmpty();

   Double_t alpha = 1.- 0.682689492;
   if (fBinStatErrOpt == kPoisson2) alpha = 0.05;

   Double_t c = GetBinContent(bin);
   Int_t n = int(c);
   if (n < 0) {
      Warning("GetBinErrorLow","Histogram has negative bin content-force usage to normal errors");
      ((TH1*)this)->fBinStatErrOpt = kNormal;
      return GetBinError(bin);
   }

   if (n == 0) return 0;
   return c - ROOT::Math::gamma_quantile( alpha/2, n, 1.);
}

//______________________________________________________________________________
Double_t TH1::GetBinErrorUp(Int_t bin) const
{
   //   -*-*-*-*-*Return upper error associated to bin number bin*-*-*-*-*
   //             ==================================================
   //
   //    The error will depend on the statistic option used will return
   //     the binContent - upper interval value
   //
   //
   //   -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

   if (fBinStatErrOpt == kNormal || fSumw2.fN) return GetBinError(bin);
   if (bin < 0) bin = 0;
   if (bin >= fNcells) bin = fNcells-1;
   if (fBuffer) ((TH1*)this)->BufferEmpty();

   Double_t alpha = 1.- 0.682689492;
   if (fBinStatErrOpt == kPoisson2) alpha = 0.05;

   Double_t c = GetBinContent(bin);
   Int_t n = int(c);
   if (n < 0) {
      Warning("GetBinErrorUp","Histogram has negative bin content-force usage to normal errors");
      ((TH1*)this)->fBinStatErrOpt = kNormal;
      return GetBinError(bin);
   }

   // for N==0 return an upper limit at 0.68 or (1-alpha)/2 ?
   // decide to return always (1-alpha)/2 upper interval
   //if (n == 0) return ROOT::Math::gamma_quantile_c(alpha,n+1,1);
   return ROOT::Math::gamma_quantile_c( alpha/2, n+1, 1) - c;
}

//______________________________________________________________________________
Double_t TH1::GetBinError(Int_t binx, Int_t biny) const
{
   //   -*-*-*-*-*Return error of bin number binx, biny
   //             =====================================
   // NB: Function to be called for 2-D histograms only

   Int_t bin = GetBin(binx,biny);
   return GetBinError(bin);
}

//______________________________________________________________________________
Double_t TH1::GetBinError(Int_t binx, Int_t biny, Int_t binz) const
{
   //   -*-*-*-*-*Return error of bin number binx,biny,binz
   //             =========================================
   // NB: Function to be called for 3-D histograms only

   Int_t bin = GetBin(binx,biny,binz);
   return GetBinError(bin);
}

//______________________________________________________________________________
Double_t TH1::GetCellContent(Int_t binx, Int_t biny) const
{
   //   -*-*-*-*-*Return content of bin number binx, biny
   //             =====================================
   // NB: Function to be called for 2-D histograms only

   Int_t bin = GetBin(binx,biny);
   return GetBinContent(bin);
}

//______________________________________________________________________________
Double_t TH1::GetCellError(Int_t binx, Int_t biny) const
{
   //   -*-*-*-*-*Return error of bin number binx, biny
   //             =====================================
   // NB: Function to be called for 2-D histograms only

   Int_t bin = GetBin(binx,biny);
   return GetBinError(bin);
}

//L.M. These following getters are useless and should be probably deprecated
//______________________________________________________________________________
Double_t TH1::GetBinCenter(Int_t bin) const 
{
   // return bin center for 1D historam
   // Better to use h1.GetXaxis().GetBinCenter(bin)
   
   if (fDimension == 1) return  fXaxis.GetBinCenter(bin);
   Error("GetBinCenter","Invalid method for a %d-d histogram - return a NaN",fDimension);
   return TMath::QuietNaN();
}

//______________________________________________________________________________
Double_t TH1::GetBinLowEdge(Int_t bin) const 
{
   // return bin lower edge for 1D historam
   // Better to use h1.GetXaxis().GetBinLowEdge(bin)
   
   if (fDimension == 1) return  fXaxis.GetBinLowEdge(bin);
   Error("GetBinLowEdge","Invalid method for a %d-d histogram - return a NaN",fDimension);
   return TMath::QuietNaN();
}

//______________________________________________________________________________
Double_t TH1::GetBinWidth(Int_t bin) const 
{
   // return bin width for 1D historam
   // Better to use h1.GetXaxis().GetBinWidth(bin)
   
   if (fDimension == 1) return  fXaxis.GetBinWidth(bin);
   Error("GetBinWidth","Invalid method for a %d-d histogram - return a NaN",fDimension);
   return TMath::QuietNaN();
}

//______________________________________________________________________________
void TH1::GetCenter(Double_t *center) const 
{
   // Fill array with center of bins for 1D histogram
   // Better to use h1.GetXaxis().GetCenter(center)
   
   if (fDimension == 1) {
      fXaxis.GetCenter(center);
      return;
   }
   Error("GetCenter","Invalid method for a %d-d histogram ",fDimension);
}

//______________________________________________________________________________
void TH1::GetLowEdge(Double_t *edge) const 
{
   // Fill array with low edge of bins for 1D histogram
   // Better to use h1.GetXaxis().GetLowEdge(edge)
   
   if (fDimension == 1) {
      fXaxis.GetLowEdge(edge);
      return;
   }
   Error("GetLowEdge","Invalid method for a %d-d histogram ",fDimension);
}

//______________________________________________________________________________
void TH1::SetBinError(Int_t bin, Double_t error)
{
   // see convention for numbering bins in TH1::GetBin
   if (!fSumw2.fN) Sumw2();
   if (bin <0 || bin>= fSumw2.fN) return;
   fSumw2.fArray[bin] = error*error;
}

//______________________________________________________________________________
void TH1::SetBinContent(Int_t binx, Int_t biny, Double_t content)
{
   // see convention for numbering bins in TH1::GetBin
   if (binx <0 || binx>fXaxis.GetNbins()+1) return;
   if (biny <0 || biny>fYaxis.GetNbins()+1) return;
   Int_t bin = GetBin(binx,biny);
   SetBinContent(bin,content);
}

//______________________________________________________________________________
void TH1::SetBinContent(Int_t binx, Int_t biny, Int_t binz, Double_t content)
{
   // see convention for numbering bins in TH1::GetBin
   if (binx <0 || binx>fXaxis.GetNbins()+1) return;
   if (biny <0 || biny>fYaxis.GetNbins()+1) return;
   if (binz <0 || binz>fZaxis.GetNbins()+1) return;
   Int_t bin = GetBin(binx,biny,binz);
   SetBinContent(bin,content);
}

//______________________________________________________________________________
void TH1::SetCellContent(Int_t binx, Int_t biny, Double_t content)
{
   // Set cell content.

   if (binx <0 || binx>fXaxis.GetNbins()+1) return;
   if (biny <0 || biny>fYaxis.GetNbins()+1) return;
   Int_t bin = GetBin(binx,biny);
   SetBinContent(bin,content);
}

//______________________________________________________________________________
void TH1::SetBinError(Int_t binx, Int_t biny, Double_t error)
{
   // see convention for numbering bins in TH1::GetBin
   if (binx <0 || binx>fXaxis.GetNbins()+1) return;
   if (biny <0 || biny>fYaxis.GetNbins()+1) return;
   Int_t bin = GetBin(binx,biny);
   SetBinError(bin,error);
}

//______________________________________________________________________________
void TH1::SetBinError(Int_t binx, Int_t biny, Int_t binz, Double_t error)
{
   // see convention for numbering bins in TH1::GetBin
   if (binx <0 || binx>fXaxis.GetNbins()+1) return;
   if (biny <0 || biny>fYaxis.GetNbins()+1) return;
   if (binz <0 || binz>fZaxis.GetNbins()+1) return;
   Int_t bin = GetBin(binx,biny,binz);
   SetBinError(bin,error);
}

//______________________________________________________________________________
void TH1::SetCellError(Int_t binx, Int_t biny, Double_t error)
{
   // see convention for numbering bins in TH1::GetBin
   if (binx <0 || binx>fXaxis.GetNbins()+1) return;
   if (biny <0 || biny>fYaxis.GetNbins()+1) return;
   if (!fSumw2.fN) Sumw2();
   Int_t bin = biny*(fXaxis.GetNbins()+2) + binx;
   fSumw2.fArray[bin] = error*error;
}

//______________________________________________________________________________
void TH1::SetBinContent(Int_t, Double_t)
{
   // see convention for numbering bins in TH1::GetBin
   AbstractMethod("SetBinContent");
}

//______________________________________________________________________________
TH1 *TH1::ShowBackground(Int_t niter, Option_t *option)
{
//   This function calculates the background spectrum in this histogram.
//   The background is returned as a histogram.
//
//   Function parameters:
//   -niter, number of iterations (default value = 2)
//      Increasing niter make the result smoother and lower.
//   -option: may contain one of the following options
//      - to set the direction parameter
//        "BackDecreasingWindow". By default the direction is BackIncreasingWindow
//      - filterOrder-order of clipping filter,  (default "BackOrder2"
//                  -possible values= "BackOrder4"
//                                    "BackOrder6"
//                                    "BackOrder8"
//      - "nosmoothing"- if selected, the background is not smoothed
//           By default the background is smoothed.
//      - smoothWindow-width of smoothing window, (default is "BackSmoothing3")
//                  -possible values= "BackSmoothing5"
//                                    "BackSmoothing7"
//                                    "BackSmoothing9"
//                                    "BackSmoothing11"
//                                    "BackSmoothing13"
//                                    "BackSmoothing15"
//      - "nocompton"- if selected the estimation of Compton edge
//                  will be not be included   (by default the compton estimation is set)
//      - "same" : if this option is specified, the resulting background
//                 histogram is superimposed on the picture in the current pad.
//                 This option is given by default.
//
//  NOTE that the background is only evaluated in the current range of this histogram.
//  i.e., if this has a bin range (set via h->GetXaxis()->SetRange(binmin, binmax),
//  the returned histogram will be created with the same number of bins
//  as this input histogram, but only bins from binmin to binmax will be filled
//  with the estimated background.
//


   return (TH1*)gROOT->ProcessLineFast(Form("TSpectrum::StaticBackground((TH1*)0x%lx,%d,\"%s\")",
                                            (ULong_t)this, niter, option));
}

//______________________________________________________________________________
Int_t TH1::ShowPeaks(Double_t sigma, Option_t *option, Double_t threshold)
{
   //Interface to TSpectrum::Search.
   //The function finds peaks in this histogram where the width is > sigma
   //and the peak maximum greater than threshold*maximum bin content of this.
   //For more details see TSpectrum::Search.
   //Note the difference in the default value for option compared to TSpectrum::Search
   //option="" by default (instead of "goff").

   return (Int_t)gROOT->ProcessLineFast(Form("TSpectrum::StaticSearch((TH1*)0x%lx,%g,\"%s\",%g)",
                                             (ULong_t)this, sigma, option, threshold));
}


//______________________________________________________________________________
TH1* TH1::TransformHisto(TVirtualFFT *fft, TH1* h_output,  Option_t *option)
{
//For a given transform (first parameter), fills the histogram (second parameter)
//with the transform output data, specified in the third parameter
//If the 2nd parameter h_output is empty, a new histogram (TH1D or TH2D) is created
//and the user is responsible for deleting it.
// Available options:
//   "RE" - real part of the output
//   "IM" - imaginary part of the output
//   "MAG" - magnitude of the output
//   "PH"  - phase of the output

   if (!fft ||  !fft->GetN() ) {
      ::Error("TransformHisto","Invalid FFT transform class");
      return 0;
   }

   if (fft->GetNdim()>2){
      ::Error("TransformHisto","Only 1d and 2D transform are supported");
      return 0;
   }
   Int_t binx,biny;
   TString opt = option;
   opt.ToUpper();
   Int_t *n = fft->GetN();
   TH1 *hout=0;
   if (h_output) {
      hout = h_output;
   }
   else {
      TString name = TString::Format("out_%s", opt.Data());
      if (fft->GetNdim()==1)
         hout = new TH1D(name, name,n[0], 0, n[0]);
      else if (fft->GetNdim()==2)
         hout = new TH2D(name, name, n[0], 0, n[0], n[1], 0, n[1]);
   }
   R__ASSERT(hout != 0);
   TString type=fft->GetType();
   Int_t ind[2];
   if (opt.Contains("RE")){
      if (type.Contains("2C") || type.Contains("2HC")) {
         Double_t re, im;
         for (binx = 1; binx<=hout->GetNbinsX(); binx++) {
            for (biny=1; biny<=hout->GetNbinsY(); biny++) {
               ind[0] = binx-1; ind[1] = biny-1;
               fft->GetPointComplex(ind, re, im);
               hout->SetBinContent(binx, biny, re);
            }
         }
      } else {
         for (binx = 1; binx<=hout->GetNbinsX(); binx++) {
            for (biny=1; biny<=hout->GetNbinsY(); biny++) {
               ind[0] = binx-1; ind[1] = biny-1;
               hout->SetBinContent(binx, biny, fft->GetPointReal(ind));
            }
         }
      }
   }
   if (opt.Contains("IM")) {
      if (type.Contains("2C") || type.Contains("2HC")) {
         Double_t re, im;
         for (binx = 1; binx<=hout->GetNbinsX(); binx++) {
            for (biny=1; biny<=hout->GetNbinsY(); biny++) {
               ind[0] = binx-1; ind[1] = biny-1;
               fft->GetPointComplex(ind, re, im);
               hout->SetBinContent(binx, biny, im);
            }
         }
      } else {
         ::Error("TransformHisto","No complex numbers in the output");
         return 0;
      }
   }
   if (opt.Contains("MA")) {
      if (type.Contains("2C") || type.Contains("2HC")) {
         Double_t re, im;
         for (binx = 1; binx<=hout->GetNbinsX(); binx++) {
            for (biny=1; biny<=hout->GetNbinsY(); biny++) {
               ind[0] = binx-1; ind[1] = biny-1;
               fft->GetPointComplex(ind, re, im);
               hout->SetBinContent(binx, biny, TMath::Sqrt(re*re + im*im));
            }
         }
      } else {
         for (binx = 1; binx<=hout->GetNbinsX(); binx++) {
            for (biny=1; biny<=hout->GetNbinsY(); biny++) {
               ind[0] = binx-1; ind[1] = biny-1;
               hout->SetBinContent(binx, biny, TMath::Abs(fft->GetPointReal(ind)));
            }
         }
      }
   }
   if (opt.Contains("PH")) {
      if (type.Contains("2C") || type.Contains("2HC")){
         Double_t re, im, ph;
         for (binx = 1; binx<=hout->GetNbinsX(); binx++){
            for (biny=1; biny<=hout->GetNbinsY(); biny++){
               ind[0] = binx-1; ind[1] = biny-1;
               fft->GetPointComplex(ind, re, im);
               if (TMath::Abs(re) > 1e-13){
                  ph = TMath::ATan(im/re);
                  //find the correct quadrant
                  if (re<0 && im<0)
                     ph -= TMath::Pi();
                  if (re<0 && im>=0)
                     ph += TMath::Pi();
               } else {
                  if (TMath::Abs(im) < 1e-13)
                     ph = 0;
                  else if (im>0)
                     ph = TMath::Pi()*0.5;
                  else
                     ph = -TMath::Pi()*0.5;
               }
               hout->SetBinContent(binx, biny, ph);
            }
         }
      } else {
         printf("Pure real output, no phase");
         return 0;
      }
   }

   return hout;
}



//______________________________________________________________________________
//                     TH1C methods
// TH1C : histograms with one byte per channel.   Maximum bin content = 127
//______________________________________________________________________________

ClassImp(TH1C)

//______________________________________________________________________________
TH1C::TH1C(): TH1(), TArrayC()
{
   // Constructor.

   fDimension = 1;
   SetBinsLength(3);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1C::TH1C(const char *name,const char *title,Int_t nbins,Double_t xlow,Double_t xup)
: TH1(name,title,nbins,xlow,xup)
{
   //
   //    Create a 1-Dim histogram with fix bins of type char (one byte per channel)
   //    ==========================================================================
   //                    (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayC::Set(fNcells);

   if (xlow >= xup) SetBuffer(fgBufferSize);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1C::TH1C(const char *name,const char *title,Int_t nbins,const Float_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type char (one byte per channel)
   //    ==========================================================================
   //                    (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayC::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1C::TH1C(const char *name,const char *title,Int_t nbins,const Double_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type char (one byte per channel)
   //    ==========================================================================
   //                    (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayC::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1C::~TH1C()
{
   // Destructor.
}

//______________________________________________________________________________
TH1C::TH1C(const TH1C &h1c) : TH1(), TArrayC()
{
   // Copy constructor.

   ((TH1C&)h1c).Copy(*this);
}

//______________________________________________________________________________
void TH1C::AddBinContent(Int_t bin)
{
   //   -*-*-*-*-*-*-*-*Increment bin content by 1*-*-*-*-*-*-*-*-*-*-*-*-*-*
   //                   ==========================

   if (fArray[bin] < 127) fArray[bin]++;
}

//______________________________________________________________________________
void TH1C::AddBinContent(Int_t bin, Double_t w)
{
   //   -*-*-*-*-*-*-*-*Increment bin content by w*-*-*-*-*-*-*-*-*-*-*-*-*-*
   //                   ==========================

   Int_t newval = fArray[bin] + Int_t(w);
   if (newval > -128 && newval < 128) {fArray[bin] = Char_t(newval); return;}
   if (newval < -127) fArray[bin] = -127;
   if (newval >  127) fArray[bin] =  127;
}

//______________________________________________________________________________
void TH1C::Copy(TObject &newth1) const
{
   // Copy this to newth1

   TH1::Copy(newth1);
}

//______________________________________________________________________________
TH1 *TH1C::DrawCopy(Option_t *option) const
{
   // Draw copy.

   TString opt = option;
   opt.ToLower();
   if (gPad && !opt.Contains("same")) gPad->Clear();
   TH1C *newth1 = (TH1C*)Clone();
   newth1->SetDirectory(0);
   newth1->SetBit(kCanDelete);
   newth1->AppendPad(option);
   return newth1;
}

//______________________________________________________________________________
Double_t TH1C::GetBinContent(Int_t bin) const
{
   // see convention for numbering bins in TH1::GetBin

   if (fBuffer) ((TH1C*)this)->BufferEmpty();
   if (bin < 0) bin = 0;
   if (bin >= fNcells) bin = fNcells-1;
   if (!fArray) return 0;
   return Double_t (fArray[bin]);
}

//______________________________________________________________________________
void TH1C::Reset(Option_t *option)
{
   // Reset.

   TH1::Reset(option);
   TArrayC::Reset();
}

//______________________________________________________________________________
void TH1C::SetBinContent(Int_t bin, Double_t content)
{
   // Set bin content
   // see convention for numbering bins in TH1::GetBin
   // In case the bin number is greater than the number of bins and
   // the timedisplay option is set or the kCanRebin bit is set,
   // the number of bins is automatically doubled to accommodate the new bin

   fEntries++;
   fTsumw = 0;
   if (bin < 0) return;
   if (bin >= fNcells-1) {
      if (fXaxis.GetTimeDisplay()) {
         while (bin >=  fNcells-1)  LabelsInflate();
      } else {
         if (!TestBit(kCanRebin)) {
            if (bin == fNcells-1) fArray[bin] = Char_t (content);
            return;
         }
         while (bin >= fNcells-1)  LabelsInflate();
      }
   }
   fArray[bin] = Char_t (content);
}

//______________________________________________________________________________
void TH1C::SetBinsLength(Int_t n)
{
   // Set total number of bins including under/overflow
   // Reallocate bin contents array

   if (n < 0) n = fXaxis.GetNbins() + 2;
   fNcells = n;
   TArrayC::Set(n);
}

//______________________________________________________________________________
TH1C& TH1C::operator=(const TH1C &h1)
{
   // Operator =

   if (this != &h1)  ((TH1C&)h1).Copy(*this);
   return *this;
}


//______________________________________________________________________________
TH1C operator*(Double_t c1, const TH1C &h1)
{
   // Operator *

   TH1C hnew = h1;
   hnew.Scale(c1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1C operator+(const TH1C &h1, const TH1C &h2)
{
   // Operator +

   TH1C hnew = h1;
   hnew.Add(&h2,1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1C operator-(const TH1C &h1, const TH1C &h2)
{
   // Operator -

   TH1C hnew = h1;
   hnew.Add(&h2,-1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1C operator*(const TH1C &h1, const TH1C &h2)
{
   // Operator *

   TH1C hnew = h1;
   hnew.Multiply(&h2);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1C operator/(const TH1C &h1, const TH1C &h2)
{
   // Operator /

   TH1C hnew = h1;
   hnew.Divide(&h2);
   hnew.SetDirectory(0);
   return hnew;
}



//______________________________________________________________________________
//                     TH1S methods
// TH1S : histograms with one short per channel.  Maximum bin content = 32767
//______________________________________________________________________________

ClassImp(TH1S)

//______________________________________________________________________________
TH1S::TH1S(): TH1(), TArrayS()
{
   // Constructor.

   fDimension = 1;
   SetBinsLength(3);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1S::TH1S(const char *name,const char *title,Int_t nbins,Double_t xlow,Double_t xup)
: TH1(name,title,nbins,xlow,xup)
{
   //
   //    Create a 1-Dim histogram with fix bins of type short
   //    ====================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayS::Set(fNcells);

   if (xlow >= xup) SetBuffer(fgBufferSize);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1S::TH1S(const char *name,const char *title,Int_t nbins,const Float_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type short
   //    =========================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayS::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1S::TH1S(const char *name,const char *title,Int_t nbins,const Double_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type short
   //    =========================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayS::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1S::~TH1S()
{
   // Destructor.
}

//______________________________________________________________________________
TH1S::TH1S(const TH1S &h1s) : TH1(), TArrayS()
{
   // Copy constructor.

   ((TH1S&)h1s).Copy(*this);
}

//______________________________________________________________________________
void TH1S::AddBinContent(Int_t bin)
{
   //   -*-*-*-*-*-*-*-*Increment bin content by 1*-*-*-*-*-*-*-*-*-*-*-*-*-*
   //                   ==========================

   if (fArray[bin] < 32767) fArray[bin]++;
}

//______________________________________________________________________________
void TH1S::AddBinContent(Int_t bin, Double_t w)
{
   //                   Increment bin content by w
   //                   ==========================

   Int_t newval = fArray[bin] + Int_t(w);
   if (newval > -32768 && newval < 32768) {fArray[bin] = Short_t(newval); return;}
   if (newval < -32767) fArray[bin] = -32767;
   if (newval >  32767) fArray[bin] =  32767;
}

//______________________________________________________________________________
void TH1S::Copy(TObject &newth1) const
{
   // Copy this to newth1

   TH1::Copy(newth1);
}

//______________________________________________________________________________
TH1 *TH1S::DrawCopy(Option_t *option) const
{
   // Draw copy.

   TString opt = option;
   opt.ToLower();
   if (gPad && !opt.Contains("same")) gPad->Clear();
   TH1S *newth1 = (TH1S*)Clone();
   newth1->SetDirectory(0);
   newth1->SetBit(kCanDelete);
   newth1->AppendPad(option);
   return newth1;
}

//______________________________________________________________________________
Double_t TH1S::GetBinContent(Int_t bin) const
{
   // see convention for numbering bins in TH1::GetBin
   if (fBuffer) ((TH1S*)this)->BufferEmpty();
   if (bin < 0) bin = 0;
   if (bin >= fNcells) bin = fNcells-1;
   if (!fArray) return 0;
   return Double_t (fArray[bin]);
}

//______________________________________________________________________________
void TH1S::Reset(Option_t *option)
{
   // Reset.

   TH1::Reset(option);
   TArrayS::Reset();
}

//______________________________________________________________________________
void TH1S::SetBinContent(Int_t bin, Double_t content)
{
   // Set bin content
   // see convention for numbering bins in TH1::GetBin
   // In case the bin number is greater than the number of bins and
   // the timedisplay option is set or the kCanRebin bit is set,
   // the number of bins is automatically doubled to accommodate the new bin

   fEntries++;
   fTsumw = 0;
   if (bin < 0) return;
   if (bin >= fNcells-1) {
      if (fXaxis.GetTimeDisplay()) {
         while (bin >=  fNcells-1)  LabelsInflate();
      } else {
         if (!TestBit(kCanRebin)) {
            if (bin == fNcells-1) fArray[bin] = Short_t (content);
            return;
         }
         while (bin >= fNcells-1)  LabelsInflate();
      }
   }
   fArray[bin] = Short_t (content);
}

//______________________________________________________________________________
void TH1S::SetBinsLength(Int_t n)
{
   // Set total number of bins including under/overflow
   // Reallocate bin contents array

   if (n < 0) n = fXaxis.GetNbins() + 2;
   fNcells = n;
   TArrayS::Set(n);
}

//______________________________________________________________________________
TH1S& TH1S::operator=(const TH1S &h1)
{
   // Operator =

   if (this != &h1)  ((TH1S&)h1).Copy(*this);
   return *this;
}


//______________________________________________________________________________
TH1S operator*(Double_t c1, const TH1S &h1)
{
   // Operator *

   TH1S hnew = h1;
   hnew.Scale(c1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1S operator+(const TH1S &h1, const TH1S &h2)
{
   // Operator +

   TH1S hnew = h1;
   hnew.Add(&h2,1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1S operator-(const TH1S &h1, const TH1S &h2)
{
   // Operator -

   TH1S hnew = h1;
   hnew.Add(&h2,-1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1S operator*(const TH1S &h1, const TH1S &h2)
{
   // Operator *

   TH1S hnew = h1;
   hnew.Multiply(&h2);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1S operator/(const TH1S &h1, const TH1S &h2)
{
   // Operator /

   TH1S hnew = h1;
   hnew.Divide(&h2);
   hnew.SetDirectory(0);
   return hnew;
}


//______________________________________________________________________________
//                     TH1I methods
// TH1I : histograms with one int per channel.    Maximum bin content = 2147483647
//______________________________________________________________________________

ClassImp(TH1I)

//______________________________________________________________________________
TH1I::TH1I(): TH1(), TArrayI()
{
   // Constructor.

   fDimension = 1;
   SetBinsLength(3);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1I::TH1I(const char *name,const char *title,Int_t nbins,Double_t xlow,Double_t xup)
: TH1(name,title,nbins,xlow,xup)
{
   //
   //    Create a 1-Dim histogram with fix bins of type integer
   //    ====================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayI::Set(fNcells);

   if (xlow >= xup) SetBuffer(fgBufferSize);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1I::TH1I(const char *name,const char *title,Int_t nbins,const Float_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type integer
   //    =========================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayI::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1I::TH1I(const char *name,const char *title,Int_t nbins,const Double_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type integer
   //    =========================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayI::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1I::~TH1I()
{
   // Destructor.
}

//______________________________________________________________________________
TH1I::TH1I(const TH1I &h1i) : TH1(), TArrayI()
{
   // Copy constructor.

   ((TH1I&)h1i).Copy(*this);
}

//______________________________________________________________________________
void TH1I::AddBinContent(Int_t bin)
{
   //   -*-*-*-*-*-*-*-*Increment bin content by 1*-*-*-*-*-*-*-*-*-*-*-*-*-*
   //                   ==========================

   if (fArray[bin] < 2147483647) fArray[bin]++;
}

//______________________________________________________________________________
void TH1I::AddBinContent(Int_t bin, Double_t w)
{
   //                   Increment bin content by w
   //                   ==========================

   Int_t newval = fArray[bin] + Int_t(w);
   if (newval > -2147483647 && newval < 2147483647) {fArray[bin] = Int_t(newval); return;}
   if (newval < -2147483647) fArray[bin] = -2147483647;
   if (newval >  2147483647) fArray[bin] =  2147483647;
}

//______________________________________________________________________________
void TH1I::Copy(TObject &newth1) const
{
   // Copy this to newth1

   TH1::Copy(newth1);
}

//______________________________________________________________________________
TH1 *TH1I::DrawCopy(Option_t *option) const
{
   // Draw copy.

   TString opt = option;
   opt.ToLower();
   if (gPad && !opt.Contains("same")) gPad->Clear();
   TH1I *newth1 = (TH1I*)Clone();
   newth1->SetDirectory(0);
   newth1->SetBit(kCanDelete);
   newth1->AppendPad(option);
   return newth1;
}

//______________________________________________________________________________
Double_t TH1I::GetBinContent(Int_t bin) const
{
   // see convention for numbering bins in TH1::GetBin
   if (fBuffer) ((TH1I*)this)->BufferEmpty();
   if (bin < 0) bin = 0;
   if (bin >= fNcells) bin = fNcells-1;
   if (!fArray) return 0;
   return Double_t (fArray[bin]);
}

//______________________________________________________________________________
void TH1I::Reset(Option_t *option)
{
   // Reset.

   TH1::Reset(option);
   TArrayI::Reset();
}

//______________________________________________________________________________
void TH1I::SetBinContent(Int_t bin, Double_t content)
{
   // Set bin content
   // see convention for numbering bins in TH1::GetBin
   // In case the bin number is greater than the number of bins and
   // the timedisplay option is set or the kCanRebin bit is set,
   // the number of bins is automatically doubled to accommodate the new bin

   fEntries++;
   fTsumw = 0;
   if (bin < 0) return;
   if (bin >= fNcells-1) {
      if (fXaxis.GetTimeDisplay()) {
         while (bin >=  fNcells-1)  LabelsInflate();
      } else {
         if (!TestBit(kCanRebin)) {
            if (bin == fNcells-1) fArray[bin] = Int_t (content);
            return;
         }
         while (bin >= fNcells-1)  LabelsInflate();
      }
   }
   fArray[bin] = Int_t (content);
}

//______________________________________________________________________________
void TH1I::SetBinsLength(Int_t n)
{
   // Set total number of bins including under/overflow
   // Reallocate bin contents array

   if (n < 0) n = fXaxis.GetNbins() + 2;
   fNcells = n;
   TArrayI::Set(n);
}

//______________________________________________________________________________
TH1I& TH1I::operator=(const TH1I &h1)
{
   // Operator =

   if (this != &h1)  ((TH1I&)h1).Copy(*this);
   return *this;
}


//______________________________________________________________________________
TH1I operator*(Double_t c1, const TH1I &h1)
{
   // Operator *

   TH1I hnew = h1;
   hnew.Scale(c1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1I operator+(const TH1I &h1, const TH1I &h2)
{
   // Operator +

   TH1I hnew = h1;
   hnew.Add(&h2,1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1I operator-(const TH1I &h1, const TH1I &h2)
{
   // Operator -

   TH1I hnew = h1;
   hnew.Add(&h2,-1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1I operator*(const TH1I &h1, const TH1I &h2)
{
   // Operator *

   TH1I hnew = h1;
   hnew.Multiply(&h2);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1I operator/(const TH1I &h1, const TH1I &h2)
{
   // Operator /

   TH1I hnew = h1;
   hnew.Divide(&h2);
   hnew.SetDirectory(0);
   return hnew;
}


//______________________________________________________________________________
//                     TH1F methods
// TH1F : histograms with one float per channel.  Maximum precision 7 digits
//______________________________________________________________________________

ClassImp(TH1F)

//______________________________________________________________________________
TH1F::TH1F(): TH1(), TArrayF()
{
   // Constructor.

   fDimension = 1;
   SetBinsLength(3);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1F::TH1F(const char *name,const char *title,Int_t nbins,Double_t xlow,Double_t xup)
: TH1(name,title,nbins,xlow,xup)
{
   //
   //    Create a 1-Dim histogram with fix bins of type float
   //    ====================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayF::Set(fNcells);

   if (xlow >= xup) SetBuffer(fgBufferSize);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1F::TH1F(const char *name,const char *title,Int_t nbins,const Float_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type float
   //    =========================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayF::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1F::TH1F(const char *name,const char *title,Int_t nbins,const Double_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type float
   //    =========================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayF::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1F::TH1F(const TVectorF &v)
: TH1("TVectorF","",v.GetNrows(),0,v.GetNrows())
{
   // Create a histogram from a TVectorF
   // by default the histogram name is "TVectorF" and title = ""

   TArrayF::Set(fNcells);
   fDimension = 1;
   Int_t ivlow  = v.GetLwb();
   for (Int_t i=0;i<fNcells-2;i++) {
      SetBinContent(i+1,v(i+ivlow));
   }
   TArrayF::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1F::TH1F(const TH1F &h) : TH1(), TArrayF()
{
   // Copy Constructor.

   ((TH1F&)h).Copy(*this);
}

//______________________________________________________________________________
TH1F::~TH1F()
{
   // Destructor.
}

//______________________________________________________________________________
void TH1F::Copy(TObject &newth1) const
{
   // Copy this to newth1.

   TH1::Copy(newth1);
}

//______________________________________________________________________________
TH1 *TH1F::DrawCopy(Option_t *option) const
{
   // Draw copy.

   TString opt = option;
   opt.ToLower();
   if (gPad && !opt.Contains("same")) gPad->Clear();
   TH1F *newth1 = (TH1F*)Clone();
   newth1->SetDirectory(0);
   newth1->SetBit(kCanDelete);
   newth1->AppendPad(option);
   return newth1;
}

//______________________________________________________________________________
Double_t TH1F::GetBinContent(Int_t bin) const
{
   // see convention for numbering bins in TH1::GetBin
   if (fBuffer) ((TH1F*)this)->BufferEmpty();
   if (bin < 0) bin = 0;
   if (bin >= fNcells) bin = fNcells-1;
   if (!fArray) return 0;
   return Double_t (fArray[bin]);
}

//______________________________________________________________________________
void TH1F::Reset(Option_t *option)
{
   // Reset.

   TH1::Reset(option);
   TArrayF::Reset();
}

//______________________________________________________________________________
void TH1F::SetBinContent(Int_t bin, Double_t content)
{
   // Set bin content
   // see convention for numbering bins in TH1::GetBin
   // In case the bin number is greater than the number of bins and
   // the timedisplay option is set or the kCanRebin bit is set,
   // the number of bins is automatically doubled to accommodate the new bin

   fEntries++;
   fTsumw = 0;
   if (bin < 0) return;
   if (bin >= fNcells-1) {
      if (fXaxis.GetTimeDisplay()) {
         while (bin >=  fNcells-1)  LabelsInflate();
      } else {
         if (!TestBit(kCanRebin)) {
            if (bin == fNcells-1) fArray[bin] = Float_t (content);
            return;
         }
         while (bin >= fNcells-1)  LabelsInflate();
      }
   }
   fArray[bin] = Float_t (content);
}

//______________________________________________________________________________
void TH1F::SetBinsLength(Int_t n)
{
   // Set total number of bins including under/overflow
   // Reallocate bin contents array

   if (n < 0) n = fXaxis.GetNbins() + 2;
   fNcells = n;
   TArrayF::Set(n);
}

//______________________________________________________________________________
TH1F& TH1F::operator=(const TH1F &h1)
{
   // Operator =

   if (this != &h1)  ((TH1F&)h1).Copy(*this);
   return *this;
}


//______________________________________________________________________________
TH1F operator*(Double_t c1, const TH1F &h1)
{
   // Operator *

   TH1F hnew = h1;
   hnew.Scale(c1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1F operator+(const TH1F &h1, const TH1F &h2)
{
   // Operator +

   TH1F hnew = h1;
   hnew.Add(&h2,1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1F operator-(const TH1F &h1, const TH1F &h2)
{
   // Operator -

   TH1F hnew = h1;
   hnew.Add(&h2,-1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1F operator*(const TH1F &h1, const TH1F &h2)
{
   // Operator *

   TH1F hnew = h1;
   hnew.Multiply(&h2);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1F operator/(const TH1F &h1, const TH1F &h2)
{
   // Operator /

   TH1F hnew = h1;
   hnew.Divide(&h2);
   hnew.SetDirectory(0);
   return hnew;
}



//______________________________________________________________________________
//                     TH1D methods
// TH1D : histograms with one double per channel. Maximum precision 14 digits
//______________________________________________________________________________

ClassImp(TH1D)

//______________________________________________________________________________
TH1D::TH1D(): TH1(), TArrayD()
{
   // Constructor.

   fDimension = 1;
   SetBinsLength(3);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1D::TH1D(const char *name,const char *title,Int_t nbins,Double_t xlow,Double_t xup)
: TH1(name,title,nbins,xlow,xup)
{
   //
   //    Create a 1-Dim histogram with fix bins of type double
   //    =====================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayD::Set(fNcells);

   if (xlow >= xup) SetBuffer(fgBufferSize);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1D::TH1D(const char *name,const char *title,Int_t nbins,const Float_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type double
   //    =====================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayD::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1D::TH1D(const char *name,const char *title,Int_t nbins,const Double_t *xbins)
: TH1(name,title,nbins,xbins)
{
   //
   //    Create a 1-Dim histogram with variable bins of type double
   //    =====================================================
   //           (see TH1::TH1 for explanation of parameters)
   //
   fDimension = 1;
   TArrayD::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1D::TH1D(const TVectorD &v)
: TH1("TVectorD","",v.GetNrows(),0,v.GetNrows())
{
   // Create a histogram from a TVectorD
   // by default the histogram name is "TVectorD" and title = ""

   TArrayD::Set(fNcells);
   fDimension = 1;
   Int_t ivlow  = v.GetLwb();
   for (Int_t i=0;i<fNcells-2;i++) {
      SetBinContent(i+1,v(i+ivlow));
   }
   TArrayD::Set(fNcells);
   if (fgDefaultSumw2) Sumw2();
}

//______________________________________________________________________________
TH1D::~TH1D()
{
   // Destructor.
}

//______________________________________________________________________________
TH1D::TH1D(const TH1D &h1d) : TH1(), TArrayD()
{
   // Constructor.

   ((TH1D&)h1d).Copy(*this);
}

//______________________________________________________________________________
void TH1D::Copy(TObject &newth1) const
{
   // Copy this to newth1

   TH1::Copy(newth1);
}

//______________________________________________________________________________
TH1 *TH1D::DrawCopy(Option_t *option) const
{
   // Draw copy.

   TString opt = option;
   opt.ToLower();
   if (gPad && !opt.Contains("same")) gPad->Clear();
   TH1D *newth1 = (TH1D*)Clone();
   newth1->SetDirectory(0);
   newth1->SetBit(kCanDelete);
   newth1->AppendPad(option);
   return newth1;
}

//______________________________________________________________________________
Double_t TH1D::GetBinContent(Int_t bin) const
{
   // see convention for numbering bins in TH1::GetBin
   if (fBuffer) ((TH1D*)this)->BufferEmpty();
   if (bin < 0) bin = 0;
   if (bin >= fNcells) bin = fNcells-1;
   if (!fArray) return 0;
   return Double_t (fArray[bin]);
}

//______________________________________________________________________________
void TH1D::Reset(Option_t *option)
{
   // Reset.

   TH1::Reset(option);
   TArrayD::Reset();
}

//______________________________________________________________________________
void TH1D::SetBinContent(Int_t bin, Double_t content)
{
   // Set bin content
   // see convention for numbering bins in TH1::GetBin
   // In case the bin number is greater than the number of bins and
   // the timedisplay option is set or the kCanRebin bit is set,
   // the number of bins is automatically doubled to accommodate the new bin

   fEntries++;
   fTsumw = 0;
   if (bin < 0) return;
   if (bin >= fNcells-1) {
      if (fXaxis.GetTimeDisplay()) {
         while (bin >=  fNcells-1)  LabelsInflate();
      } else {
         if (!TestBit(kCanRebin)) {
            if (bin == fNcells-1) fArray[bin] = content;
            return;
         }
         while (bin >= fNcells-1)  LabelsInflate();
      }
   }
   fArray[bin] = content;
}

//______________________________________________________________________________
void TH1D::SetBinsLength(Int_t n)
{
   // Set total number of bins including under/overflow
   // Reallocate bin contents array

   if (n < 0) n = fXaxis.GetNbins() + 2;
   fNcells = n;
   TArrayD::Set(n);
}

//______________________________________________________________________________
TH1D& TH1D::operator=(const TH1D &h1)
{
   // Operator =

   if (this != &h1)  ((TH1D&)h1).Copy(*this);
   return *this;
}

//______________________________________________________________________________
TH1D operator*(Double_t c1, const TH1D &h1)
{
   // Operator *

   TH1D hnew = h1;
   hnew.Scale(c1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1D operator+(const TH1D &h1, const TH1D &h2)
{
   // Operator +

   TH1D hnew = h1;
   hnew.Add(&h2,1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1D operator-(const TH1D &h1, const TH1D &h2)
{
   // Operator -

   TH1D hnew = h1;
   hnew.Add(&h2,-1);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1D operator*(const TH1D &h1, const TH1D &h2)
{
   // Operator *

   TH1D hnew = h1;
   hnew.Multiply(&h2);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1D operator/(const TH1D &h1, const TH1D &h2)
{
   // Operator /

   TH1D hnew = h1;
   hnew.Divide(&h2);
   hnew.SetDirectory(0);
   return hnew;
}

//______________________________________________________________________________
TH1 *R__H(Int_t hid)
{
   //return pointer to histogram with name
   //   hid if id >=0
   //   h_id if id <0

   TString hname;
   if(hid >= 0) hname.Form("h%d",hid);
   else         hname.Form("h_%d",hid);
   return (TH1*)gDirectory->Get(hname);
}

//______________________________________________________________________________
TH1 *R__H(const char * hname)
{
   //return pointer to histogram with name hname

   return (TH1*)gDirectory->Get(hname);
}
// @(#)root/gl:$Id$
// Author:  Timur Pocheptsov  17/11/2005

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

#ifndef ROOT_TGLHistPainter
#define ROOT_TGLHistPainter

#include <memory>

#ifndef ROOT_TVirtualHistPainter
#include "TVirtualHistPainter.h"
#endif
#ifndef ROOT_TGLPlotPainter
#include "TGLPlotPainter.h"
#endif
#ifndef ROOT_TGLPlotCamera
#include "TGLPlotCamera.h"
#endif

/*
   TGLHistPainter is a proxy class. It inherits TVirtualHistPainter and
   overrides its virtual functions, but all actual work is done by :
      THistPainter - I name it "default" painter, it's the member of type
                     TVirtualHistPainter * and loaded via plugin-manager;
      TGLLegoPainter - it draws different legos (lego/lego1/lego2/lego3);
      TGLSurfacePainter - supports surfaces (surf/surf1/surf2/surf3/surf4/surf5);
      TGLBoxPainter - box option for TH3;
      TGLTF3Painter - TF3.
*/

class TGLParametricEquation;
class TGLTH3Composition;
class TGL5DDataSet;
class TString;
class TList;
class TF3;
class TH1;

class TGLHistPainter : public TVirtualHistPainter {
private:
   //Dynamic type is THistPainter, no problems with simultaneous inheritance and membership
   //TGLHistPainter delegates unsupported options/calls to this object
   std::auto_ptr<TVirtualHistPainter> fDefaultPainter;
   //This member can have different dynamic types: TGLLegoPainter, etc.
   std::auto_ptr<TGLPlotPainter>      fGLPainter;

   TGLParametricEquation *fEq;
   TH1                   *fHist;
   TF3                   *fF3;
   TList                 *fStack;
   EGLPlotType            fPlotType;
   TGLPlotCamera          fCamera;
   TGLPlotCoordinates     fCoord;

public:
   TGLHistPainter(TH1 *hist);
   TGLHistPainter(TGLParametricEquation *equation);
   TGLHistPainter(TGL5DDataSet *data);
   TGLHistPainter(TGLTH3Composition *comp);

   //TVirtualHistPainter final overriders
   Int_t          DistancetoPrimitive(Int_t px, Int_t py);
   void           DrawPanel();
   void           ExecuteEvent(Int_t event, Int_t px, Int_t py);
   TList         *GetContourList(Double_t contour)const;
   char          *GetObjectInfo(Int_t px, Int_t py)const;
   TList         *GetStack()const;
   Int_t          GetXHighlightBin() const;
   Int_t          GetYHighlightBin() const;
   Bool_t         IsInside(Int_t x, Int_t y);
   Bool_t         IsInside(Double_t x, Double_t y);
   void           Paint(Option_t *option);
   void           PaintStat(Int_t dostat, TF1 *fit);
   void           ProcessMessage(const char *message, const TObject *obj);
   void           SetHighlight();
   void           SetHistogram(TH1 *hist);
   void           SetStack(TList *stack);
   Int_t          MakeCuts(char *cutsOpt);
   void           SetShowProjection(const char *option, Int_t nbins);

   TGLPlotPainter *GetRealPainter(){return fGLPainter.get();}
private:

   struct PlotOption_t;

   PlotOption_t   ParsePaintOption(const TString &option)const;
   void           CreatePainter(const PlotOption_t &parsed,
                                const TString &option);

   void           PadToViewport(Bool_t selectionPass = kFALSE);
                                
   TGLHistPainter(const TGLHistPainter &);
   TGLHistPainter &operator = (const TGLHistPainter &);

   ClassDef(TGLHistPainter, 0) //Proxy class for GL hist painters.
};

#endif
// @(#)root/gl:$Id$
// Author:  Timur Pocheptsov  17/11/2005

/*************************************************************************
 * Copyright (C) 1995-2005, 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 <stdexcept>
#include <cstring>

#include "TVirtualGL.h"
#include "KeySymbols.h"
#include "Buttons.h"
#include "TH2Poly.h"
#include "TClass.h"
#include "TROOT.h"
#include "TGL5D.h"
#include "TMath.h"
#include "TPad.h"
#include "TH3.h"
#include "TF3.h"

#include "TGLSurfacePainter.h"
#include "TGLTH3Composition.h"
#include "TGLH2PolyPainter.h"
#include "TGLVoxelPainter.h"
#include "TGLHistPainter.h"
#include "TGLLegoPainter.h"
#include "TGLBoxPainter.h"
#include "TGLTF3Painter.h"
#include "TGLParametric.h"
#include "TGL5DPainter.h"
#include "TGLUtil.h"

ClassImp(TGLHistPainter)

//______________________________________________________________________________
/* Begin_Html
<center><h2>The histogram painter class using OpenGL</h2></center>

Histograms are, by default, drawn via the <tt>THistPainter</tt> class.
<tt>TGLHistPainter</tt> allows to paint them using the OpenGL 3D graphics
library. The plotting options provided by <tt>TGLHistPainter</tt> start with
<tt>GL</tt> keyword.

<h3>General information: plot types and supported options</h3>

The following types of plots are provided:
<ul>
<p><li><b>Lego - (<tt>TGLLegoPainter</tt>)</b>
  <br> The supported options are:
  <ul>
  <li> <tt>"GLLEGO"  :</tt> Draw a lego plot.
  <li> <tt>"GLLEGO2" :</tt> Bins with color levels.
  <li> <tt>"GLLEGO3" :</tt> Cylindrical bars.
  </ul>
  Lego painter in cartesian supports logarithmic scales for X, Y, Z.
  In polar only Z axis can be logarithmic, in cylindrical only Y (if you see
  what it means).


<p><li><b>Surfaces (<tt>TF2</tt> and <tt>TH2</tt> with <tt>"GLSURF"</tt> options) - (<tt>TGLSurfacePainter</tt>)</b>
  <br> The supported options are:
  <ul>
  <li> <tt>"GLSURF"  :</tt> Draw a surface.
  <li> <tt>"GLSURF1" :</tt> Surface with color levels
  <li> <tt>"GLSURF2" :</tt> The same as <tt>"GLSURF1"</tt> but without polygon outlines.
  <li> <tt>"GLSURF3" :</tt> Color level projection on top of plot (works only in cartesian coordinate system).
  <li> <tt>"GLSURF4" :</tt> Same as <tt>"GLSURF"</tt> but without polygon outlines.
  </ul>

  The surface painting in cartesian coordinates supports logarithmic scales along X, Y, Z axis.
  In polar coordinates only the Z axis can be logarithmic, in cylindrical coordinates only the Y axis.

<p><li><b>Additional options to <tt>SURF</tt> and <tt>LEGO</tt> - Coordinate systems:</b>
  <br> The supported options are:
  <ul>
  <li> <tt>" "   :</tt> Default, cartesian coordinates system.
  <li> <tt>"POL" :</tt> Polar coordinates system.
  <li> <tt>"CYL" :</tt> Cylindrical coordinates system.
  <li> <tt>"SPH" :</tt> Spherical coordinates system.
  </ul>

<p><li><b><tt>TH3</tt> as boxes (spheres) - (<tt>TGLBoxPainter</tt>)</b>
  <br> The supported options are:
  <ul>
  <li> <tt>"GLBOX" :</tt> TH3 as a set of boxes, size of box is proportional to bin content.
  <li> <tt>"GLBOX1":</tt> the same as "glbox", but spheres are drawn instead of boxes.
  </ul>

<p><li><b><tt>TH3</tt> as iso-surface(s) - (<tt>TGLIsoPainter</tt>)</b>
  <br> The supported option is:
  <ul>
  <li> <tt>"GLISO" :</tt> TH3 is drawn using iso-surfaces.
  </ul>

<p><li><b><tt>TH3</tt> as color boxes - (<tt>TGLVoxelPainter</tt>)</b>
  <br> The supported option is:
  <ul>
  <li> <tt>"GLCOL" :</tt> TH3 is drawn using semi-transparent colored boxes.
  <br>See <tt>$ROOTSYS/tutorials/gl/glvox1.C</tt>.
  </ul>

<p><li><b><tt>TF3</tt> (implicit function) - (<tt>TGLTF3Painter</tt>)</b>
  <br> The supported option is:
  <ul>
  <li> <tt>"GLTF3" :</tt> Draw a <tt>TF3</tt>.
  </ul>

<p><li><b>Parametric surfaces - (<tt>TGLParametricPlot</tt>)</b>
  <br><tt>$ROOTSYS/tutorials/gl/glparametric.C</tt> shows how to create parametric equations and
  visualize the surface.
</ul>

<h3>Interaction with the plots</h3>

<ul>
<p><li><b>General information.</b>
  <br>
  All the interactions are implemented via standard methods <tt>DistancetoPrimitive</tt> and
  <tt>ExecuteEvent</tt>. That's why all the interactions with the OpenGL plots are possible i
  only when the mouse cursor is in the plot's area (the plot's area is the part of a the pad
  occupied by gl-produced picture). If the mouse cursor is not above gl-picture,
  the standard pad interaction is performed.

<p><li><b>Selectable parts.</b>
  <br>
  Different parts of the plot can be selected:
  <ul>
  <li> <em>xoz, yoz, xoy back planes</em>:
     <br>When such a plane selected, it's highlighted in green if the dynamic slicing
     by this plane is supported, and it's highlighted in red, if the dynamic slicing
     is not supported.
  <li><em>The plot itself</em>:
     <br>On surfaces, the selected surface is outlined in red. (TF3 and ISO are not
     outlined). On lego plots, the selected bin is highlihted. The bin number and content are displayed in pad's status
     bar. In box plots, the box or sphere is highlighted and the bin info is displayed in pad's status bar.
  </ul>

<p><li><b>Rotation and zooming.</b>
  <br>
  <ul>
  <li> <em>Rotation</em>:
  <br>
  When the plot is selected, it can be rotated by pressing and holding the left mouse button and move the cursor.
  <li> <em>Zoom/Unzoom</em>:
  <br>
  Mouse wheel or <tt>'j'</tt>, <tt>'J'</tt>, <tt>'k'</tt>, <tt>'K'</tt> keys.
  </ul>

<p><li><b>Panning.</b>
 <br>
  The selected plot can be moved in a pad's area by
  pressing and holding the left mouse button and the shift key.
</ul>

<h3>Box cut</h3>
  Surface, iso, box, TF3 and parametric painters support box cut by pressing the <tt>'c'</tt> or
  <tt>'C'</tt> key when the mouse cursor is in a plot's area. That will display a transparent box,
  cutting away part of the surface (or boxes) in order to show internal part of plot.
  This box can be moved inside the plot's area (the full size of the box is equal to the plot's
  surrounding box) by selecting one of the box cut axes and pressing the left mouse button to move it.

<h3>Plot specific interactions (dynamic slicing etc.)</h3>
  Currently, all gl-plots support some form of slicing.
  When back plane is selected (and if it's highlighted in green)
  you can press and hold left mouse button and shift key
  and move this back plane inside plot's area, creating the slice.
  During this "slicing" plot becomes semi-transparent. To remove all slices (and projected curves for surfaces)
  - double click with left mouse button in a plot's area.
  <ul>
  <p><li><b>Surface with option <tt>"GLSURF"</tt></b>
  <br>
  The surface profile is displayed on the slicing plane.
  The profile projection is drawn on the back plane
  by pressing <tt>'p'</tt> or <tt>'P'</tt> key.

  <p><li><b>TF3</b>
  <br>
  The contour plot is drawn on the slicing plane.
  For <tt>TF3</tt> the color scheme can be changed by pressing <tt>'s'</tt> or <tt>'S'</tt>.

  <p><li><b>Box</b>
  <br>
  The contour plot corresponding to slice plane position is drawn in real time.

  <p><li><b>Iso</b>
  <br>
  Slicing is similar to <tt>"GLBOX"</tt> option.

  <p><li><b>Parametric plot</b>
  <br>
  No slicing. Additional keys: <tt>'s'</tt> or <tt>'S'</tt> to change color scheme - about 20 color schemes supported
  (<tt>'s'</tt> for "scheme"); <tt>'l'</tt> or <tt>'L'</tt> to increase number of polygons (<tt>'l'</tt> for "level" of details),
  <tt>'w'</tt> or <tt>'W'</tt> to show outlines (<tt>'w'</tt> for "wireframe").
  </ul>
End_Html */

//______________________________________________________________________________
TGLHistPainter::TGLHistPainter(TH1 *hist)
                   : fDefaultPainter(TVirtualHistPainter::HistPainter(hist)),
                     fEq(0),
                     fHist(hist),
                     fF3(0),
                     fStack(0),
                     fPlotType(kGLDefaultPlot)//THistPainter
{
   //ROOT does not use exceptions, so, if default painter's creation failed,
   //fDefaultPainter is 0. In each function, which use it, I have to check the pointer first.
}

//______________________________________________________________________________
TGLHistPainter::TGLHistPainter(TGLParametricEquation *equation)
                   : fEq(equation),
                     fHist(0),
                     fF3(0),
                     fStack(0),
                     fPlotType(kGLParametricPlot)//THistPainter
{
   //This ctor creates gl-parametric plot's painter.
   fGLPainter.reset(new TGLParametricPlot(equation, &fCamera));
}

//______________________________________________________________________________
TGLHistPainter::TGLHistPainter(TGL5DDataSet *data)
                   : fEq(0),
                     fHist(0),
                     fF3(0),
                     fStack(0),
                     fPlotType(kGL5D)//THistPainter
{
   //This ctor creates plot painter for TGL5DDataSet.
   fGLPainter.reset(new TGL5DPainter(data, &fCamera, &fCoord));
}

//______________________________________________________________________________
TGLHistPainter::TGLHistPainter(TGLTH3Composition *data)
                   : fEq(0),
                     fHist(data),
                     fF3(0),
                     fStack(0),
                     fPlotType(kGLTH3Composition)
{
   //This ctor creates plot painter for TGL5DDataSet.
   fGLPainter.reset(new TGLTH3CompositionPainter(data, &fCamera, &fCoord));
}

//______________________________________________________________________________
Int_t TGLHistPainter::DistancetoPrimitive(Int_t px, Int_t py)
{
   //Selects plot or axis.
   //9999 is the magic number, ROOT's classes use in DistancetoPrimitive.
   
   //[tp: return statement added.
   //tp]
   
   if (fPlotType == kGLDefaultPlot)
      return fDefaultPainter.get() ? fDefaultPainter->DistancetoPrimitive(px, py) : 9999;
   else {
      //Adjust px and py - canvas can have several pads inside, so we need to convert
      //the from canvas' system into pad's.
      
      //Retina-related adjustments must be done inside!!!
      
      py = gPad->GetWh() - py;

      //One hist can be appended to several pads,
      //the current pad should have valid OpenGL context.
      const Int_t glContext = gPad->GetGLDevice();

      if (glContext != -1) {
         //Add "viewport" extraction here.
         PadToViewport(kTRUE);

         if (!gGLManager->PlotSelected(fGLPainter.get(), px, py))
            gPad->SetSelected(gPad);
      } else {
         Error("DistancetoPrimitive",
               "Attempt to use TGLHistPainter, while the current pad (gPad) does not support gl");
         gPad->SetSelected(gPad);
      }

      return 0;
   }
}

//______________________________________________________________________________
void TGLHistPainter::DrawPanel()
{
   //Default implementation is OK
   //This function is called from a context menu
   //after right click on a plot's area. Opens window
   //("panel") with several controls.
   if (fDefaultPainter.get())
      fDefaultPainter->DrawPanel();
}

//______________________________________________________________________________
void TGLHistPainter::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   //Execute event.
   //Events are: mouse events in a plot's area,
   //key presses (while mouse cursor is in plot's area).
   //"Event execution" means one of the following actions:
   //1. Rotation.
   //2. Panning.
   //3. Zoom changing.
   //4. Moving dynamic profile.
   //5. Plot specific events - for example, 's' or 'S' key press for TF3.
   if (fPlotType == kGLDefaultPlot) {
      if(fDefaultPainter.get()) {
         fDefaultPainter->ExecuteEvent(event, px, py);
      }
   } else {
      //One hist can be appended to several pads,
      //the current pad should have valid OpenGL context.
      const Int_t glContext = gPad->GetGLDevice();

      if (glContext == -1) {
         Error("ExecuteEvent",
               "Attempt to use TGLHistPainter, while the current pad (gPad) does not support gl");
         return;
      } else {
         //Add viewport extraction here.
         /*fGLDevice.SetGLDevice(glContext);
         fGLPainter->SetGLDevice(&fGLDevice);*/
         PadToViewport();
      }

      if (event != kKeyPress) {
         //Adjust px and py - canvas can have several pads inside, so we need to convert
         //the from canvas' system into pad's. If it was a key press event,
         //px and py ARE NOT coordinates.
         py -= Int_t((1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh());
         px -= Int_t(gPad->GetXlowNDC() * gPad->GetWw());
         
         //We also have to take care of retina displays with a different viewports.
         TGLUtil::InitializeIfNeeded();
         const Float_t scale = TGLUtil::GetScreenScalingFactor();
         if (scale > 1) {
            px *= scale;
            py *= scale;
         }
      }

      switch (event) {
      case kButton1Double:
         //Left double click removes dynamic sections, user created (if plot type supports sections).
         fGLPainter->ProcessEvent(event, px, py);
         break;
      case kButton1Down:
         //Left mouse down in a plot area starts rotation.
         if (!fGLPainter->CutAxisSelected())
            fCamera.StartRotation(px, py);
         else
            fGLPainter->StartPan(px, py);
         //During rotation, usual TCanvas/TPad machinery (CopyPixmap/Flush/UpdateWindow/etc.)
         //is skipped - I use "bit blasting" functions to copy picture directly onto window.
         //gGLManager->MarkForDirectCopy(glContext, kTRUE);
         break;
      case kButton1Motion:
         //Rotation invalidates "selection buffer"
         // - (color-to-object map, previously read from gl-buffer).
         fGLPainter->InvalidateSelection();
         if (fGLPainter->CutAxisSelected())
            gGLManager->PanObject(fGLPainter.get(), px, py);
         else
            fCamera.RotateCamera(px, py);
         //Draw modified scene onto canvas' window.
         //gGLManager->PaintSingleObject(fGLPainter.get());
         gPad->Update();
         break;
      case kButton1Up:
      case kButton2Up:
         gGLManager->MarkForDirectCopy(glContext, kFALSE);
         break;
      case kMouseMotion:
         gPad->SetCursor(kRotate);
         break;
      case 7://kButton1Down + shift modifier
         //The current version of ROOT does not
         //have enumerators for button events + key modifiers,
         //so I use hardcoded literals. :(
         //With left mouse button down and shift pressed
         //we can move plot as the whole or move
         //plot's parts - dynamic sections.
         fGLPainter->StartPan(px, py);
         gGLManager->MarkForDirectCopy(glContext, kTRUE);
         break;
      case 8://kButton1Motion + shift modifier
         gGLManager->PanObject(fGLPainter.get(), px, py);
         //gGLManager->PaintSingleObject(fGLPainter.get());
         gPad->Update();
         break;
      case kKeyPress:
      case 5:
      case 6:
         //5, 6 are mouse wheel events (see comment about literals above).
         //'p'/'P' - specific events processed by TGLSurfacePainter,
         //'s'/'S' - specific events processed by TGLTF3Painter,
         //'c'/'C' - turn on/off box cut.
         gGLManager->MarkForDirectCopy(glContext, kTRUE);
         if (event == 6 || py == kKey_J || py == kKey_j) {
            fCamera.ZoomIn();
            fGLPainter->InvalidateSelection();
            //gGLManager->PaintSingleObject(fGLPainter.get());
            gPad->Update();
         } else if (event == 5 || py == kKey_K || py == kKey_k) {
            fCamera.ZoomOut();
            fGLPainter->InvalidateSelection();
            //gGLManager->PaintSingleObject(fGLPainter.get());
            gPad->Update();
         } else if (py == kKey_p || py == kKey_P || py == kKey_S || py == kKey_s
                    || py == kKey_c || py == kKey_C || py == kKey_x || py == kKey_X
                    || py == kKey_y || py == kKey_Y || py == kKey_z || py == kKey_Z
                    || py == kKey_w || py == kKey_W || py == kKey_l || py == kKey_L
                    /*|| py == kKey_r || py == kKey_R*/)
         {
            fGLPainter->ProcessEvent(event, px, py);
            //gGLManager->PaintSingleObject(fGLPainter.get());
            gPad->Update();
         }
         gGLManager->MarkForDirectCopy(glContext, kFALSE);
         break;
      }
   }
}

//______________________________________________________________________________
TList *TGLHistPainter::GetContourList(Double_t contour)const
{
   //Get contour list.
   //I do not use this function. Contours are implemented in
   //a completely different way by gl-painters.
   return fDefaultPainter.get() ? fDefaultPainter->GetContourList(contour) : 0;
}

//______________________________________________________________________________
char *TGLHistPainter::GetObjectInfo(Int_t px, Int_t py)const
{
   //Overrides TObject::GetObjectInfo.
   //For lego info is: bin numbers (i, j), bin content.
   //For TF2 info is: x,y,z 3d surface-point for 2d screen-point under cursor
   //(this can work incorrectly now, because of wrong code in TF2).
   //For TF3 no info now.
   //For box info is: bin numbers (i, j, k), bin content.
   static char errMsg[] = { "TGLHistPainter::GetObjectInfo: Error in a hist painter\n" };
   if (fPlotType == kGLDefaultPlot)
      return fDefaultPainter.get() ? fDefaultPainter->GetObjectInfo(px, py)
                                   : errMsg;
   else {
      TGLUtil::InitializeIfNeeded();
      const Float_t scale = TGLUtil::GetScreenScalingFactor();
      if (scale > 1.f) {
         px *= scale;
         py *= scale;
      }

      return gGLManager->GetPlotInfo(fGLPainter.get(), px, py);
   }
}

//______________________________________________________________________________
TList *TGLHistPainter::GetStack()const
{
   // Get stack.
   return fStack;
}

//______________________________________________________________________________
Int_t TGLHistPainter::GetXHighlightBin() const
{
   return fDefaultPainter.get() ? fDefaultPainter->GetXHighlightBin() : -1;
}

//______________________________________________________________________________
Int_t TGLHistPainter::GetYHighlightBin() const
{
   return fDefaultPainter.get() ? fDefaultPainter->GetYHighlightBin() : -1;
}

//______________________________________________________________________________
void TGLHistPainter::SetHighlight()
{
   if (fDefaultPainter.get()) fDefaultPainter->SetHighlight();
}

//______________________________________________________________________________
Bool_t TGLHistPainter::IsInside(Int_t x, Int_t y)
{
   //Returns kTRUE if the cell ix, iy is inside one of the graphical cuts.
   //I do not use this function anywhere, this is a "default implementation".
   if (fPlotType == kGLDefaultPlot)
      return fDefaultPainter.get() ? fDefaultPainter->IsInside(x, y) : kFALSE;

   return kFALSE;
}

//______________________________________________________________________________
Bool_t TGLHistPainter::IsInside(Double_t x, Double_t y)
{
   //Returns kTRUE if the cell x, y is inside one of the graphical cuts.
   //I do not use this function anywhere, this is a "default implementation".
   if (fPlotType == kGLDefaultPlot)
      return fDefaultPainter.get() ? fDefaultPainter->IsInside(x, y) : kFALSE;

   return kFALSE;
}

//______________________________________________________________________________
void TGLHistPainter::PaintStat(Int_t dostat, TF1 *fit)
{
   //Paint statistics.
   //This does not work on windows.
   if (fDefaultPainter.get())
      fDefaultPainter->PaintStat(dostat, fit);
}

//______________________________________________________________________________
void TGLHistPainter::ProcessMessage(const char *m, const TObject *o)
{
   // Process message.
   if (!std::strcmp(m, "SetF3"))
      fF3 = (TF3 *)o;

   if (fDefaultPainter.get())
      fDefaultPainter->ProcessMessage(m, o);
}

//______________________________________________________________________________
void TGLHistPainter::SetHistogram(TH1 *h)
{
   // Set histogram.
   fHist = h;

   if (fDefaultPainter.get())
      fDefaultPainter->SetHistogram(h);
}

//______________________________________________________________________________
void TGLHistPainter::SetStack(TList *s)
{
   // Set stack.
   fStack = s;

   if (fDefaultPainter.get())
      fDefaultPainter->SetStack(s);
}

//______________________________________________________________________________
Int_t TGLHistPainter::MakeCuts(char *o)
{
   // Make cuts.
   if (fPlotType == kGLDefaultPlot && fDefaultPainter.get())
      return fDefaultPainter->MakeCuts(o);

   return 0;
}

struct TGLHistPainter::PlotOption_t {
   EGLPlotType  fPlotType;
   EGLCoordType fCoordType;
   Bool_t       fBackBox;
   Bool_t       fFrontBox;
   Bool_t       fDrawAxes;
   Bool_t       fLogX;
   Bool_t       fLogY;
   Bool_t       fLogZ;
};

//______________________________________________________________________________
void TGLHistPainter::Paint(Option_t *o)
{
   //Final-overrider for TObject::Paint.
   TString option(o);
   option.ToLower();

   const Ssiz_t glPos = option.Index("gl");
   if (glPos != kNPOS)
      option.Remove(glPos, 2);
   else if (fPlotType != kGLParametricPlot && fPlotType != kGL5D && fPlotType != kGLTH3Composition) {
      gPad->SetCopyGLDevice(kFALSE);
      if (fDefaultPainter.get())
         fDefaultPainter->Paint(o);//option.Data());
      return;
   }

   if (fPlotType != kGLParametricPlot && fPlotType != kGL5D && fPlotType != kGLTH3Composition)
      CreatePainter(ParsePaintOption(option), option);

   if (fPlotType == kGLDefaultPlot) {
      //In case of default plot pad
      //should not copy gl-buffer (it will be simply black)
      
      //[tp: code was commented.
      //gPad->SetCopyGLDevice(kFALSE);
      //tp]

      if (fDefaultPainter.get())
         fDefaultPainter->Paint(option.Data());
   } else {
      Int_t glContext = gPad->GetGLDevice();

      if (glContext != -1) {
         //With gl-plot, pad should copy
         //gl-buffer into the final pad/canvas pixmap/DIB.
         //fGLDevice.SetGLDevice(glContext);
         
         //[tp: code commented.
         //gPad->SetCopyGLDevice(kTRUE);
         //tp]
         //fGLPainter->SetGLDevice(&fGLDevice);
         //Add viewport extraction here.
         PadToViewport();
         if (gPad->GetFrameFillColor() != kWhite)
            fGLPainter->SetFrameColor(gROOT->GetColor(gPad->GetFrameFillColor()));
         fGLPainter->SetPadColor(gROOT->GetColor(gPad->GetFillColor()));
         if (fGLPainter->InitGeometry())
            gGLManager->PaintSingleObject(fGLPainter.get());
      }
   }
}

namespace {

Bool_t FindAndRemoveOption(TString &options, const char *toFind)
{
   const UInt_t len = std::strlen(toFind);
   const Ssiz_t index = options.Index(toFind);

   if (index != kNPOS) {
      options.Remove(index, len);
      return kTRUE;
   }
   
   return kFALSE;
}

}

//______________________________________________________________________________
TGLHistPainter::PlotOption_t
TGLHistPainter::ParsePaintOption(const TString &o)const
{
   //In principle, we can have several conflicting options: "lego surf pol sph", surfbb: surf, fb, bb.
   //but only one will be selected, which one - depends on parsing order in this function.
   
   TString options(o);
   
   PlotOption_t parsedOption = {kGLDefaultPlot, kGLCartesian,
                                kTRUE, kTRUE, kTRUE, //Show back box, show front box, show axes.
                                Bool_t(gPad->GetLogx()), Bool_t(gPad->GetLogy()),
                                Bool_t(gPad->GetLogz())};

   //Check coordinate system type.
   if (FindAndRemoveOption(options, "pol"))
      parsedOption.fCoordType = kGLPolar;
   if (FindAndRemoveOption(options, "cyl"))
      parsedOption.fCoordType = kGLCylindrical;
   if (FindAndRemoveOption(options, "sph"))
      parsedOption.fCoordType = kGLSpherical;

   //Define plot type.
   if (FindAndRemoveOption(options, "lego"))
      fStack ? parsedOption.fPlotType = kGLStackPlot : parsedOption.fPlotType = kGLLegoPlot;
   if (FindAndRemoveOption(options, "surf"))
      parsedOption.fPlotType = kGLSurfacePlot;
   if (FindAndRemoveOption(options, "tf3"))
      parsedOption.fPlotType = kGLTF3Plot;
   if (FindAndRemoveOption(options, "box"))
      parsedOption.fPlotType = kGLBoxPlot;
   if (FindAndRemoveOption(options, "iso"))
      parsedOption.fPlotType = kGLIsoPlot;
   if (FindAndRemoveOption(options, "col"))
      parsedOption.fPlotType = kGLVoxel;

   //Check BB and FB options.
   if (FindAndRemoveOption(options, "bb"))
      parsedOption.fBackBox = kFALSE;
   if (FindAndRemoveOption(options, "fb"))
      parsedOption.fFrontBox = kFALSE;
      
   //Check A option.
   if (FindAndRemoveOption(options, "a"))
      parsedOption.fDrawAxes = kFALSE;

   return parsedOption;
}

//______________________________________________________________________________
void TGLHistPainter::CreatePainter(const PlotOption_t &option, const TString &addOption)
{
   // Create painter.
   if (option.fPlotType != fPlotType) {
      fCoord.ResetModified();
      fGLPainter.reset(0);
   }

   if (option.fPlotType == kGLLegoPlot) {
      if (!fGLPainter.get()) {
         if (dynamic_cast<TH2Poly*>(fHist))
            fGLPainter.reset(new TGLH2PolyPainter(fHist, &fCamera, &fCoord));
         else
            fGLPainter.reset(new TGLLegoPainter(fHist, &fCamera, &fCoord));
      }
   } else if (option.fPlotType == kGLSurfacePlot) {
      if (!fGLPainter.get())
         fGLPainter.reset(new TGLSurfacePainter(fHist, &fCamera, &fCoord));
   } else if (option.fPlotType == kGLBoxPlot) {
      if (!fGLPainter.get())
         fGLPainter.reset(new TGLBoxPainter(fHist, &fCamera, &fCoord));
   } else if (option.fPlotType == kGLTF3Plot) {
      if (!fGLPainter.get())
         fGLPainter.reset(new TGLTF3Painter(fF3, fHist, &fCamera, &fCoord));
   } else if (option.fPlotType == kGLIsoPlot) {
      if (!fGLPainter.get())
         fGLPainter.reset(new TGLIsoPainter(fHist, &fCamera, &fCoord));
   } else if (option.fPlotType == kGLVoxel) {
      if (!fGLPainter.get())
         fGLPainter.reset(new TGLVoxelPainter(fHist, &fCamera, &fCoord));
   }

   if (fGLPainter.get()) {
      fPlotType = option.fPlotType;
      fCoord.SetXLog(gPad->GetLogx());
      fCoord.SetYLog(gPad->GetLogy());
      fCoord.SetZLog(gPad->GetLogz());
      fCoord.SetCoordType(option.fCoordType);
      fGLPainter->AddOption(addOption);
      
      fGLPainter->SetDrawFrontBox(option.fFrontBox);
      fGLPainter->SetDrawBackBox(option.fBackBox);
      fGLPainter->SetDrawAxes(option.fDrawAxes);
   } else
      fPlotType = kGLDefaultPlot;
}

//______________________________________________________________________________
void TGLHistPainter::SetShowProjection(const char *option, Int_t nbins)
{
   // Set show projection.

   if (fDefaultPainter.get()) fDefaultPainter->SetShowProjection(option, nbins);
}

//______________________________________________________________________________
void TGLHistPainter::PadToViewport(Bool_t /*selectionPass*/)
{
   if (!fGLPainter.get())
      return;

   TGLRect vp;
   vp.Width()  = Int_t(gPad->GetAbsWNDC() * gPad->GetWw());
   vp.Height() = Int_t(gPad->GetAbsHNDC() * gPad->GetWh());
   
   vp.X() = Int_t(gPad->XtoAbsPixel(gPad->GetX1()));
   vp.Y() = Int_t((gPad->GetWh() - gPad->YtoAbsPixel(gPad->GetY1())));
   
   TGLUtil::InitializeIfNeeded();
   const Float_t scale = TGLUtil::GetScreenScalingFactor();

   if (scale > 1.f) {
      vp.X() = Int_t(vp.X() * scale);
      vp.Y() = Int_t(vp.Y() * scale);
      
      vp.Width() = Int_t(vp.Width() * scale);
      vp.Height() = Int_t(vp.Height() * scale);
   }
   
   fCamera.SetViewport(vp);
   if (fCamera.ViewportChanged() && fGLPainter.get())
      fGLPainter->InvalidateSelection();
}

以上是关于c_cpp ROOT TH1亮点的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 适用于ROOT TH1类的PrintWiki功能

c_cpp foundational_programming-CPP-length1.cpp

ROOT实时更新

CERN ROOT程序使用心得1

c_cpp ROOT积分TGraph

c_cpp ROOT图表突出显示