From e43b090418b0729ab174c905a1b9929c499a58da Mon Sep 17 00:00:00 2001
From: Olivier Couet <olivier.couet@cern.ch>
Date: Wed, 23 Nov 2016 15:47:29 +0100
Subject: [PATCH] Add new tutorials for TUnfold. Stefan Schmitt provided the
 examples. More work was needed to: - Create the index.md file - Make the
 source files are Doxygen Compliant (formatting, Math formulae) - Spell check

---
 tutorials/unfold/index.md               |    3 +
 tutorials/unfold/testUnfold1.C          |  517 +++++++
 tutorials/unfold/testUnfold2.C          |  366 +++++
 tutorials/unfold/testUnfold3.C          |  631 +++++++++
 tutorials/unfold/testUnfold4.C          |  222 +++
 tutorials/unfold/testUnfold5a.C         |  338 +++++
 tutorials/unfold/testUnfold5b.C         |  182 +++
 tutorials/unfold/testUnfold5c.C         |  290 ++++
 tutorials/unfold/testUnfold5d.C         |  296 ++++
 tutorials/unfold/testUnfold6.C          |  117 ++
 tutorials/unfold/testUnfold6binning.xml |   27 +
 tutorials/unfold/testUnfold7a.C         |  447 ++++++
 tutorials/unfold/testUnfold7b.C         |  302 +++++
 tutorials/unfold/testUnfold7binning.xml |   18 +
 tutorials/unfold/testUnfold7c.C         | 1655 +++++++++++++++++++++++
 15 files changed, 5411 insertions(+)
 create mode 100644 tutorials/unfold/index.md
 create mode 100644 tutorials/unfold/testUnfold1.C
 create mode 100644 tutorials/unfold/testUnfold2.C
 create mode 100644 tutorials/unfold/testUnfold3.C
 create mode 100644 tutorials/unfold/testUnfold4.C
 create mode 100644 tutorials/unfold/testUnfold5a.C
 create mode 100644 tutorials/unfold/testUnfold5b.C
 create mode 100644 tutorials/unfold/testUnfold5c.C
 create mode 100644 tutorials/unfold/testUnfold5d.C
 create mode 100644 tutorials/unfold/testUnfold6.C
 create mode 100644 tutorials/unfold/testUnfold6binning.xml
 create mode 100644 tutorials/unfold/testUnfold7a.C
 create mode 100644 tutorials/unfold/testUnfold7b.C
 create mode 100644 tutorials/unfold/testUnfold7binning.xml
 create mode 100644 tutorials/unfold/testUnfold7c.C

diff --git a/tutorials/unfold/index.md b/tutorials/unfold/index.md
new file mode 100644
index 00000000000..a6a554a2a2a
--- /dev/null
+++ b/tutorials/unfold/index.md
@@ -0,0 +1,3 @@
+\defgroup tutorial_unfold Unfold tutorials
+\ingroup Tutorials
+\brief Examples showing the TUnfold usage.
diff --git a/tutorials/unfold/testUnfold1.C b/tutorials/unfold/testUnfold1.C
new file mode 100644
index 00000000000..d32d2af1055
--- /dev/null
+++ b/tutorials/unfold/testUnfold1.C
@@ -0,0 +1,517 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+///  Test program for the classes TUnfold and related.
+///
+///  1. Generate Monte Carlo and Data events
+///      The events consist of
+///        signal
+///        background
+///
+///      The signal is a resonance. It is generated with a Breit-Wigner,
+///      smeared by a Gaussian
+///
+///  2. Unfold the data. The result is:
+///      The background level
+///      The shape of the resonance, corrected for detector effects
+///
+///      Systematic errors from the MC shape variation are included
+///      and propagated to the result
+///
+///  3. fit the unfolded distribution, including the correlation matrix
+///
+///  4. save six plots to a file `testUnfold1.ps`
+/// ~~~
+///        1  2  3
+///        4  5  6
+/// ~~~
+///      1. 2d-plot of the matrix describing the migrations
+///      2. generator-level distributions
+///            - blue: unfolded data, total errors
+///            - green: unfolded data, statistical errors
+///            - red: generated data
+///            - black: fit to green data points
+///      3. detector level distributions
+///            - blue: unfolded data, folded back through the matrix
+///            - black: Monte Carlo (with wrong peal position)
+///            - blue: data
+///      4. global correlation coefficients
+///      5. \f$ \chi^2 \f$ as a function of \f$ log(\tau) \f$
+///           the star indicates the final choice of \f$ \tau \f$
+///      6. the L curve
+///
+/// \macro_output
+/// \macro_image
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+/// #### History:
+///  - Version 17.5, in parallel to changes in TUnfold
+///  - Version 17.4, in parallel to changes in TUnfold
+///  - Version 17.3, in parallel to changes in TUnfold
+///  - Version 17.2, in parallel to changes in TUnfold
+///  - Version 17.1, in parallel to changes in TUnfold
+///  - Version 17.0, updated for using the classes TUnfoldDensity, TUnfoldBinning
+///  - Version 16.1, parallel to changes in TUnfold
+///  - Version 16.0, parallel to changes in TUnfold
+///  - Version 15, with automated L-curve scan
+///  - Version 14, with changes in TUnfoldSys.cxx
+///  - Version 13, include test of systematic errors
+///  - Version 12, catch error when defining the input
+///  - Version 11,  print chi**2 and number of degrees of freedom
+///  - Version 10,  with bug-fix in TUnfold.cxx
+///  - Version 9,  with bug-fix in TUnfold.cxx and TUnfold.h
+///  - Version 8,  with bug-fix in TUnfold.cxx and TUnfold.h
+///  - Version 7,  with bug-fix in TUnfold.cxx and TUnfold.h
+///  - Version 6a, fix problem with dynamic array allocation under windows
+///  - Version 6, bug-fixes in TUnfold.C
+///  - Version 5, replace main() by testUnfold1()
+///  - Version 4, with bug-fix in TUnfold.C
+///  - Version 3, with bug-fix in TUnfold.C
+///  - Version 2, with changed ScanLcurve() arguments
+///  - Version 1, remove L curve analysis, use ScanLcurve() method instead
+///  - Version 0, L curve analysis included here
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+
+#include <TError.h>
+#include <TMath.h>
+#include <TCanvas.h>
+#include <TRandom3.h>
+#include <TFitter.h>
+#include <TF1.h>
+#include <TStyle.h>
+#include <TVector.h>
+#include <TGraph.h>
+
+#include "TUnfoldDensity.h"
+
+// #define VERBOSE_LCURVE_SCAN
+
+using namespace std;
+
+TRandom *rnd=0;
+
+TH2 *gHistInvEMatrix;
+
+TVirtualFitter *gFitter=0;
+
+void chisquare_corr(Int_t &npar, Double_t * /*gin */, Double_t &f, Double_t *u, Int_t /*flag */) {
+  //  Minimization function for H1s using a Chisquare method
+  //  only one-dimensional histograms are supported
+  //  Correlated errors are taken from an external inverse covariance matrix
+  //  stored in a 2-dimensional histogram
+  Double_t x;
+  TH1 *hfit = (TH1*)gFitter->GetObjectFit();
+  TF1 *f1   = (TF1*)gFitter->GetUserFunc();
+
+  f1->InitArgs(&x,u);
+  npar = f1->GetNpar();
+  f = 0;
+
+  Int_t npfit = 0;
+  Int_t nPoints=hfit->GetNbinsX();
+  Double_t *df=new Double_t[nPoints];
+  for (Int_t i=0;i<nPoints;i++) {
+    x     = hfit->GetBinCenter(i+1);
+    TF1::RejectPoint(kFALSE);
+    df[i] = f1->EvalPar(&x,u)-hfit->GetBinContent(i+1);
+    if (TF1::RejectedPoint()) df[i]=0.0;
+    else npfit++;
+  }
+  for (Int_t i=0;i<nPoints;i++) {
+    for (Int_t j=0;j<nPoints;j++) {
+      f += df[i]*df[j]*gHistInvEMatrix->GetBinContent(i+1,j+1);
+    }
+  }
+  delete[] df;
+  f1->SetNumberFitPoints(npfit);
+}
+
+Double_t bw_func(Double_t *x,Double_t *par) {
+  Double_t dm=x[0]-par[1];
+  return par[0]/(dm*dm+par[2]*par[2]);
+}
+
+
+// generate an event
+// output:
+//  negative mass: background event
+//  positive mass: signal event
+Double_t GenerateEvent(Double_t bgr, // relative fraction of background
+                       Double_t mass, // peak position
+                       Double_t gamma) // peak width
+{
+  Double_t t;
+  if(rnd->Rndm()>bgr) {
+    // generate signal event
+    // with positive mass
+    do {
+      do {
+        t=rnd->Rndm();
+      } while(t>=1.0);
+      t=TMath::Tan((t-0.5)*TMath::Pi())*gamma+mass;
+    } while(t<=0.0);
+    return t;
+  } else {
+    // generate background event
+    // generate events following a power-law distribution
+    //   f(E) = K * TMath::power((E0+E),N0)
+    static Double_t const E0=2.4;
+    static Double_t const N0=2.9;
+    do {
+      do {
+        t=rnd->Rndm();
+      } while(t>=1.0);
+      // the mass is returned negative
+      // In our example a convenient way to indicate it is a background event.
+      t= -(TMath::Power(1.-t,1./(1.-N0))-1.0)*E0;
+    } while(t>=0.0);
+    return t;
+  }
+}
+
+// smear the event to detector level
+// input:
+//   mass on generator level (mTrue>0 !)
+// output:
+//   mass on detector level
+Double_t DetectorEvent(Double_t mTrue) {
+  // smear by double-gaussian
+  static Double_t frac=0.1;
+  static Double_t wideBias=0.03;
+  static Double_t wideSigma=0.5;
+  static Double_t smallBias=0.0;
+  static Double_t smallSigma=0.1;
+  if(rnd->Rndm()>frac) {
+    return rnd->Gaus(mTrue+smallBias,smallSigma);
+  } else {
+    return rnd->Gaus(mTrue+wideBias,wideSigma);
+  }
+}
+
+int testUnfold1()
+{
+  // switch on histogram errors
+  TH1::SetDefaultSumw2();
+
+  // show fit result
+  gStyle->SetOptFit(1111);
+
+  // random generator
+  rnd=new TRandom3();
+
+  // data and MC luminosity, cross-section
+  Double_t const luminosityData=100000;
+  Double_t const luminosityMC=1000000;
+  Double_t const crossSection=1.0;
+
+  Int_t const nDet=250;
+  Int_t const nGen=100;
+  Double_t const xminDet=0.0;
+  Double_t const xmaxDet=10.0;
+  Double_t const xminGen=0.0;
+  Double_t const xmaxGen=10.0;
+
+  //============================================
+  // generate MC distribution
+  //
+  TH1D *histMgenMC=new TH1D("MgenMC",";mass(gen)",nGen,xminGen,xmaxGen);
+  TH1D *histMdetMC=new TH1D("MdetMC",";mass(det)",nDet,xminDet,xmaxDet);
+  TH2D *histMdetGenMC=new TH2D("MdetgenMC",";mass(det);mass(gen)",
+                               nDet,xminDet,xmaxDet,nGen,xminGen,xmaxGen);
+  Int_t neventMC=rnd->Poisson(luminosityMC*crossSection);
+  for(Int_t i=0;i<neventMC;i++) {
+    Double_t mGen=GenerateEvent(0.3, // relative fraction of background
+                                4.0, // peak position in MC
+                                0.2); // peak width in MC
+    Double_t mDet=DetectorEvent(TMath::Abs(mGen));
+    // the generated mass is negative for background
+    // and positive for signal
+    // so it will be filled in the underflow bin
+    // this is very convenient for the unfolding:
+    // the unfolded result will contain the number of background
+    // events in the underflow bin
+
+    // generated MC distribution (for comparison only)
+    histMgenMC->Fill(mGen,luminosityData/luminosityMC);
+    // reconstructed MC distribution (for comparison only)
+    histMdetMC->Fill(mDet,luminosityData/luminosityMC);
+
+    // matrix describing how the generator input migrates to the
+    // reconstructed level. Unfolding input.
+    // NOTE on underflow/overflow bins:
+    //  (1) the detector level under/overflow bins are used for
+    //       normalisation ("efficiency" correction)
+    //       in our toy example, these bins are populated from tails
+    //       of the initial MC distribution.
+    //  (2) the generator level underflow/overflow bins are
+    //       unfolded. In this example:
+    //       underflow bin: background events reconstructed in the detector
+    //       overflow bin: signal events generated at masses > xmaxDet
+    // for the unfolded result these bins will be filled
+    //  -> the background normalisation will be contained in the underflow bin
+    histMdetGenMC->Fill(mDet,mGen,luminosityData/luminosityMC);
+  }
+
+  //============================================
+  // generate alternative MC
+  // this will be used to derive a systematic error due to MC
+  // parameter uncertainties
+  TH2D *histMdetGenSysMC=new TH2D("MdetgenSysMC",";mass(det);mass(gen)",
+                                  nDet,xminDet,xmaxDet,nGen,xminGen,xmaxGen);
+  neventMC=rnd->Poisson(luminosityMC*crossSection);
+  for(Int_t i=0;i<neventMC;i++) {
+    Double_t mGen=GenerateEvent
+       (0.5, // relative fraction of background
+        3.6, // peak position in MC with systematic shift
+        0.15); // peak width in MC
+    Double_t mDet=DetectorEvent(TMath::Abs(mGen));
+    histMdetGenSysMC->Fill(mDet,mGen,luminosityData/luminosityMC);
+  }
+
+  //============================================
+  // generate data distribution
+  //
+  TH1D *histMgenData=new TH1D("MgenData",";mass(gen)",nGen,xminGen,xmaxGen);
+  TH1D *histMdetData=new TH1D("MdetData",";mass(det)",nDet,xminDet,xmaxDet);
+  Int_t neventData=rnd->Poisson(luminosityData*crossSection);
+  for(Int_t i=0;i<neventData;i++) {
+    Double_t mGen=GenerateEvent(0.4, // relative fraction of background
+                                3.8, // peak position in data
+                                0.15); // peak width in data
+    Double_t mDet=DetectorEvent(TMath::Abs(mGen));
+    // generated data mass for comparison plots
+    // for real data, we do not have this histogram
+    histMgenData->Fill(mGen);
+
+    // reconstructed mass, unfolding input
+    histMdetData->Fill(mDet);
+  }
+
+  //=========================================================================
+  // divide by bin width to get density distributions
+  TH1D *histDensityGenData=new TH1D("DensityGenData",";mass(gen)",
+                                    nGen,xminGen,xmaxGen);
+  TH1D *histDensityGenMC=new TH1D("DensityGenMC",";mass(gen)",
+                                    nGen,xminGen,xmaxGen);
+  for(Int_t i=1;i<=nGen;i++) {
+     histDensityGenData->SetBinContent(i,histMgenData->GetBinContent(i)/
+                                       histMgenData->GetBinWidth(i));
+     histDensityGenMC->SetBinContent(i,histMgenMC->GetBinContent(i)/
+                                       histMgenMC->GetBinWidth(i));
+  }
+
+  //=========================================================================
+  // set up the unfolding
+  // define migration matrix
+  TUnfoldDensity unfold(histMdetGenMC,TUnfold::kHistMapOutputVert);
+
+  // define input and bias scheme
+  // do not use the bias, because MC peak may be at the wrong place
+  // watch out for error codes returned by the SetInput method
+  // errors larger or equal 10000 are fatal:
+  // the data points specified as input are not sufficient to constrain the
+  // unfolding process
+  if(unfold.SetInput(histMdetData)>=10000) {
+    std::cout<<"Unfolding result may be wrong\n";
+  }
+
+  //========================================================================
+  // the unfolding is done here
+  //
+  // scan L curve and find best point
+  Int_t nScan=30;
+  // use automatic L-curve scan: start with taumin=taumax=0.0
+  Double_t tauMin=0.0;
+  Double_t tauMax=0.0;
+  Int_t iBest;
+  TSpline *logTauX,*logTauY;
+  TGraph *lCurve;
+
+  // if required, report Info messages (for debugging the L-curve scan)
+#ifdef VERBOSE_LCURVE_SCAN
+  Int_t oldinfo=gErrorIgnoreLevel;
+  gErrorIgnoreLevel=kInfo;
+#endif
+  // this method scans the parameter tau and finds the kink in the L curve
+  // finally, the unfolding is done for the best choice of tau
+  iBest=unfold.ScanLcurve(nScan,tauMin,tauMax,&lCurve,&logTauX,&logTauY);
+
+  // if required, switch to previous log-level
+#ifdef VERBOSE_LCURVE_SCAN
+  gErrorIgnoreLevel=oldinfo;
+#endif
+
+  //==========================================================================
+  // define a correlated systematic error
+  // for example, assume there is a 10% correlated error for all reconstructed
+  // masses larger than 7
+  Double_t SYS_ERROR1_MSTART=6;
+  Double_t SYS_ERROR1_SIZE=0.1;
+  TH2D *histMdetGenSys1=new TH2D("Mdetgensys1",";mass(det);mass(gen)",
+                                 nDet,xminDet,xmaxDet,nGen,xminGen,xmaxGen);
+  for(Int_t i=0;i<=nDet+1;i++) {
+     if(histMdetData->GetBinCenter(i)>=SYS_ERROR1_MSTART) {
+        for(Int_t j=0;j<=nGen+1;j++) {
+           histMdetGenSys1->SetBinContent(i,j,SYS_ERROR1_SIZE);
+        }
+     }
+  }
+  unfold.AddSysError(histMdetGenSysMC,"SYSERROR_MC",TUnfold::kHistMapOutputVert,
+                     TUnfoldSys::kSysErrModeMatrix);
+  unfold.AddSysError(histMdetGenSys1,"SYSERROR1",TUnfold::kHistMapOutputVert,
+                     TUnfoldSys::kSysErrModeRelative);
+
+  //==========================================================================
+  // print some results
+  //
+  std::cout<<"tau="<<unfold.GetTau()<<"\n";
+  std::cout<<"chi**2="<<unfold.GetChi2A()<<"+"<<unfold.GetChi2L()
+           <<" / "<<unfold.GetNdf()<<"\n";
+  std::cout<<"chi**2(sys)="<<unfold.GetChi2Sys()<<"\n";
+
+
+  //==========================================================================
+  // create graphs with one point to visualize the best choice of tau
+  //
+  Double_t t[1],x[1],y[1];
+  logTauX->GetKnot(iBest,t[0],x[0]);
+  logTauY->GetKnot(iBest,t[0],y[0]);
+  TGraph *bestLcurve=new TGraph(1,x,y);
+  TGraph *bestLogTauLogChi2=new TGraph(1,t,x);
+
+  //==========================================================================
+  // retrieve results into histograms
+
+  // get unfolded distribution
+  TH1 *histMunfold=unfold.GetOutput("Unfolded");
+
+  // get unfolding result, folded back
+  TH1 *histMdetFold=unfold.GetFoldedOutput("FoldedBack");
+
+  // get error matrix (input distribution [stat] errors only)
+  // TH2D *histEmatData=unfold.GetEmatrix("EmatData");
+
+  // get total error matrix:
+  //   migration matrix uncorrelated and correlated systematic errors
+  //   added in quadrature to the data statistical errors
+  TH2 *histEmatTotal=unfold.GetEmatrixTotal("EmatTotal");
+
+  // create data histogram with the total errors
+  TH1D *histTotalError=
+     new TH1D("TotalError",";mass(gen)",nGen,xminGen,xmaxGen);
+  for(Int_t bin=1;bin<=nGen;bin++) {
+    histTotalError->SetBinContent(bin,histMunfold->GetBinContent(bin));
+    histTotalError->SetBinError
+       (bin,TMath::Sqrt(histEmatTotal->GetBinContent(bin,bin)));
+  }
+
+  // get global correlation coefficients
+  // for this calculation one has to specify whether the
+  // underflow/overflow bins are included or not
+  // default: include all bins
+  // here: exclude underflow and overflow bins
+  TH1 *histRhoi=unfold.GetRhoItotal("rho_I",
+                                    0, // use default title
+                                    0, // all distributions
+                                    "*[UO]", // discard underflow and overflow bins on all axes
+                                    kTRUE, // use original binning
+                                    &gHistInvEMatrix // store inverse of error matrix
+                                    );
+
+  //======================================================================
+  // fit Breit-Wigner shape to unfolded data, using the full error matrix
+  // here we use a "user" chi**2 function to take into account
+  // the full covariance matrix
+
+  gFitter=TVirtualFitter::Fitter(histMunfold);
+  gFitter->SetFCN(chisquare_corr);
+
+  TF1 *bw=new TF1("bw",bw_func,xminGen,xmaxGen,3);
+  bw->SetParameter(0,1000.);
+  bw->SetParameter(1,3.8);
+  bw->SetParameter(2,0.2);
+
+  // for (wrong!) fitting without correlations, drop the option "U"
+  // here.
+  histMunfold->Fit(bw,"UE");
+
+  //=====================================================================
+  // plot some histograms
+  TCanvas output;
+  output.Divide(3,2);
+
+  // Show the matrix which connects input and output
+  // There are overflow bins at the bottom, not shown in the plot
+  // These contain the background shape.
+  // The overflow bins to the left and right contain
+  // events which are not reconstructed. These are necessary for proper MC
+  // normalisation
+  output.cd(1);
+  histMdetGenMC->Draw("BOX");
+
+  // draw generator-level distribution:
+  //   data (red) [for real data this is not available]
+  //   MC input (black) [with completely wrong peak position and shape]
+  //   unfolded data (blue)
+  output.cd(2);
+  histTotalError->SetLineColor(kBlue);
+  histTotalError->Draw("E");
+  histMunfold->SetLineColor(kGreen);
+  histMunfold->Draw("SAME E1");
+  histDensityGenData->SetLineColor(kRed);
+  histDensityGenData->Draw("SAME");
+  histDensityGenMC->Draw("SAME HIST");
+
+  // show detector level distributions
+  //    data (red)
+  //    MC (black) [with completely wrong peak position and shape]
+  //    unfolded data (blue)
+  output.cd(3);
+  histMdetFold->SetLineColor(kBlue);
+  histMdetFold->Draw();
+  histMdetMC->Draw("SAME HIST");
+
+  TH1 *histInput=unfold.GetInput("Minput",";mass(det)");
+
+  histInput->SetLineColor(kRed);
+  histInput->Draw("SAME");
+
+  // show correlation coefficients
+  output.cd(4);
+  histRhoi->Draw();
+
+  // show tau as a function of chi**2
+  output.cd(5);
+  logTauX->Draw();
+  bestLogTauLogChi2->SetMarkerColor(kRed);
+  bestLogTauLogChi2->Draw("*");
+
+  // show the L curve
+  output.cd(6);
+  lCurve->Draw("AL");
+  bestLcurve->SetMarkerColor(kRed);
+  bestLcurve->Draw("*");
+
+  output.SaveAs("testUnfold1.ps");
+
+  return 0;
+}
diff --git a/tutorials/unfold/testUnfold2.C b/tutorials/unfold/testUnfold2.C
new file mode 100644
index 00000000000..a82fbbb747d
--- /dev/null
+++ b/tutorials/unfold/testUnfold2.C
@@ -0,0 +1,366 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+///  Test program as an example for a user specific regularisation scheme.
+///
+///  1. Generate Monte Carlo and Data events
+///      The events consist of:
+///       - signal
+///       - background
+///
+///      The signal is a resonance. It is generated with a Breit-Wigner,
+///      smeared by a Gaussian
+///
+///  2. Unfold the data. The result is:
+///     - The background level
+///     - The shape of the resonance, corrected for detector effects
+///
+///      The regularisation is done on the curvature, excluding the bins
+///      near the peak.
+///
+///  3. produce some plots
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+/// #### History:
+///  - Version 17.5, in parallel to changes in TUnfold
+///  - Version 17.4, in parallel to changes in TUnfold
+///  - Version 17.3, in parallel to changes in TUnfold
+///  - Version 17.2, in parallel to changes in TUnfold
+///  - Version 17.1, in parallel to changes in TUnfold
+///  - Version 17.0, updated for changed methods in TUnfold
+///  - Version 16.1, parallel to changes in TUnfold
+///  - Version 16.0, parallel to changes in TUnfold
+///  - Version 15, with automatic L-curve scan, simplified example
+///  - Version 14, with changes in TUnfoldSys.cxx
+///  - Version 13,  with changes to TUnfold.C
+///  - Version 12,  with improvements to TUnfold.cxx
+///  - Version 11,  print chi**2 and number of degrees of freedom
+///  - Version 10, with bug-fix in TUnfold.cxx
+///  - Version 9, with bug-fix in TUnfold.cxx, TUnfold.h
+///  - Version 8, with bug-fix in TUnfold.cxx, TUnfold.h
+///  - Version 7, with bug-fix in TUnfold.cxx, TUnfold.h
+///  - Version 6a, fix problem with dynamic array allocation under windows
+///  - Version 6, re-include class MyUnfold in the example
+///  - Version 5, move class MyUnfold to separate files
+///  - Version 4, with bug-fix in TUnfold.C
+///  - Version 3, with bug-fix in TUnfold.C
+///  - Version 2, with changed ScanLcurve() arguments
+///  - Version 1, remove L curve analysis, use ScanLcurve() method instead
+///  - Version 0, L curve analysis included here
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+#include <TMath.h>
+#include <TCanvas.h>
+#include <TRandom3.h>
+#include <TFitter.h>
+#include <TF1.h>
+#include <TStyle.h>
+#include <TVector.h>
+#include <TGraph.h>
+#include "TUnfold.h"
+
+using namespace std;
+
+
+TRandom *rnd=0;
+
+// generate an event
+// output:
+//  negative mass: background event
+//  positive mass: signal event
+Double_t GenerateEvent(Double_t bgr, // relative fraction of background
+                       Double_t mass, // peak position
+                       Double_t gamma) // peak width
+{
+  Double_t t;
+  if(rnd->Rndm()>bgr) {
+    // generate signal event
+    // with positive mass
+    do {
+      do {
+        t=rnd->Rndm();
+      } while(t>=1.0);
+      t=TMath::Tan((t-0.5)*TMath::Pi())*gamma+mass;
+    } while(t<=0.0);
+    return t;
+  } else {
+    // generate background event
+    // generate events following a power-law distribution
+    //   f(E) = K * TMath::power((E0+E),N0)
+    static Double_t const E0=2.4;
+    static Double_t const N0=2.9;
+    do {
+      do {
+        t=rnd->Rndm();
+      } while(t>=1.0);
+      // the mass is returned negative
+      // In our example a convenient way to indicate it is a background event.
+      t= -(TMath::Power(1.-t,1./(1.-N0))-1.0)*E0;
+    } while(t>=0.0);
+    return t;
+  }
+}
+
+// smear the event to detector level
+// input:
+//   mass on generator level (mTrue>0 !)
+// output:
+//   mass on detector level
+Double_t DetectorEvent(Double_t mTrue) {
+  // smear by double-gaussian
+  static Double_t frac=0.1;
+  static Double_t wideBias=0.03;
+  static Double_t wideSigma=0.5;
+  static Double_t smallBias=0.0;
+  static Double_t smallSigma=0.1;
+  if(rnd->Rndm()>frac) {
+    return rnd->Gaus(mTrue+smallBias,smallSigma);
+  } else {
+    return rnd->Gaus(mTrue+wideBias,wideSigma);
+  }
+}
+
+int testUnfold2()
+{
+  // switch on histogram errors
+  TH1::SetDefaultSumw2();
+
+  // random generator
+  rnd=new TRandom3();
+
+  // data and MC luminosity, cross-section
+  Double_t const luminosityData=100000;
+  Double_t const luminosityMC=1000000;
+  Double_t const crossSection=1.0;
+
+  Int_t const nDet=250;
+  Int_t const nGen=100;
+  Double_t const xminDet=0.0;
+  Double_t const xmaxDet=10.0;
+  Double_t const xminGen=0.0;
+  Double_t const xmaxGen=10.0;
+
+  //============================================
+  // generate MC distribution
+  //
+  TH1D *histMgenMC=new TH1D("MgenMC",";mass(gen)",nGen,xminGen,xmaxGen);
+  TH1D *histMdetMC=new TH1D("MdetMC",";mass(det)",nDet,xminDet,xmaxDet);
+  TH2D *histMdetGenMC=new TH2D("MdetgenMC",";mass(det);mass(gen)",nDet,xminDet,xmaxDet,
+                              nGen,xminGen,xmaxGen);
+  Int_t neventMC=rnd->Poisson(luminosityMC*crossSection);
+  for(Int_t i=0;i<neventMC;i++) {
+    Double_t mGen=GenerateEvent(0.3, // relative fraction of background
+                                4.0, // peak position in MC
+                                0.2); // peak width in MC
+    Double_t mDet=DetectorEvent(TMath::Abs(mGen));
+    // the generated mass is negative for background
+    // and positive for signal
+    // so it will be filled in the underflow bin
+    // this is very convenient for the unfolding:
+    // the unfolded result will contain the number of background
+    // events in the underflow bin
+
+    // generated MC distribution (for comparison only)
+    histMgenMC->Fill(mGen,luminosityData/luminosityMC);
+    // reconstructed MC distribution (for comparison only)
+    histMdetMC->Fill(mDet,luminosityData/luminosityMC);
+
+    // matrix describing how the generator input migrates to the
+    // reconstructed level. Unfolding input.
+    // NOTE on underflow/overflow bins:
+    //  (1) the detector level under/overflow bins are used for
+    //       normalisation ("efficiency" correction)
+    //       in our toy example, these bins are populated from tails
+    //       of the initial MC distribution.
+    //  (2) the generator level underflow/overflow bins are
+    //       unfolded. In this example:
+    //       underflow bin: background events reconstructed in the detector
+    //       overflow bin: signal events generated at masses > xmaxDet
+    // for the unfolded result these bins will be filled
+    //  -> the background normalisation will be contained in the underflow bin
+    histMdetGenMC->Fill(mDet,mGen,luminosityData/luminosityMC);
+  }
+
+  //============================================
+  // generate data distribution
+  //
+  TH1D *histMgenData=new TH1D("MgenData",";mass(gen)",nGen,xminGen,xmaxGen);
+  TH1D *histMdetData=new TH1D("MdetData",";mass(det)",nDet,xminDet,xmaxDet);
+  Int_t neventData=rnd->Poisson(luminosityData*crossSection);
+  for(Int_t i=0;i<neventData;i++) {
+    Double_t mGen=GenerateEvent(0.4, // relative fraction of background
+                                3.8, // peak position
+                                0.15); // peak width
+    Double_t mDet=DetectorEvent(TMath::Abs(mGen));
+    // generated data mass for comparison plots
+    // for real data, we do not have this histogram
+    histMgenData->Fill(mGen);
+
+    // reconstructed mass, unfolding input
+    histMdetData->Fill(mDet);
+  }
+
+  //=========================================================================
+  // set up the unfolding
+  TUnfold unfold(histMdetGenMC,TUnfold::kHistMapOutputVert,
+                 TUnfold::kRegModeNone);
+  // regularisation
+  //----------------
+  // the regularisation is done on the curvature (2nd derivative) of
+  // the output distribution
+  //
+  // One has to exclude the bins near the peak of the Breit-Wigner,
+  // because there the curvature is high
+  // (and the regularisation eventually could enforce a small
+  //  curvature, thus biasing result)
+  //
+  // in real life, the parameters below would have to be optimized,
+  // depending on the data peak position and width
+  // Or maybe one finds a different regularisation scheme... this is
+  // just an example...
+  Double_t estimatedPeakPosition=3.8;
+  Int_t nPeek=3;
+  TUnfold::ERegMode regMode=TUnfold::kRegModeCurvature;
+  // calculate bin number corresponding to estimated peak position
+  Int_t iPeek=(Int_t)(nGen*(estimatedPeakPosition-xminGen)/(xmaxGen-xminGen)
+                      // offset 1.5
+                      // accounts for start bin 1
+                      // and rounding errors +0.5
+                      +1.5);
+  // regularize output bins 1..iPeek-nPeek
+  unfold.RegularizeBins(1,1,iPeek-nPeek,regMode);
+  // regularize output bins iPeek+nPeek..nGen
+  unfold.RegularizeBins(iPeek+nPeek,1,nGen-(iPeek+nPeek),regMode);
+
+  // unfolding
+  //-----------
+
+  // set input distribution and bias scale (=0)
+  if(unfold.SetInput(histMdetData,0.0)>=10000) {
+    std::cout<<"Unfolding result may be wrong\n";
+  }
+
+  // do the unfolding here
+  Double_t tauMin=0.0;
+  Double_t tauMax=0.0;
+  Int_t nScan=30;
+  Int_t iBest;
+  TSpline *logTauX,*logTauY;
+  TGraph *lCurve;
+  // this method scans the parameter tau and finds the kink in the L curve
+  // finally, the unfolding is done for the "best" choice of tau
+  iBest=unfold.ScanLcurve(nScan,tauMin,tauMax,&lCurve,&logTauX,&logTauY);
+  std::cout<<"tau="<<unfold.GetTau()<<"\n";
+  std::cout<<"chi**2="<<unfold.GetChi2A()<<"+"<<unfold.GetChi2L()
+           <<" / "<<unfold.GetNdf()<<"\n";
+
+  // save point corresponding to the kink in the L curve as TGraph
+  Double_t t[1],x[1],y[1];
+  logTauX->GetKnot(iBest,t[0],x[0]);
+  logTauY->GetKnot(iBest,t[0],y[0]);
+  TGraph *bestLcurve=new TGraph(1,x,y);
+  TGraph *bestLogTauX=new TGraph(1,t,x);
+
+  //============================================================
+  // extract unfolding results into histograms
+
+  // set up a bin map, excluding underflow and overflow bins
+  // the binMap relates the the output of the unfolding to the final
+  // histogram bins
+  Int_t *binMap=new Int_t[nGen+2];
+  for(Int_t i=1;i<=nGen;i++) binMap[i]=i;
+  binMap[0]=-1;
+  binMap[nGen+1]=-1;
+
+  TH1D *histMunfold=new TH1D("Unfolded",";mass(gen)",nGen,xminGen,xmaxGen);
+  unfold.GetOutput(histMunfold,binMap);
+  TH1D *histMdetFold=new TH1D("FoldedBack","mass(det)",nDet,xminDet,xmaxDet);
+  unfold.GetFoldedOutput(histMdetFold);
+
+  // store global correlation coefficients
+  TH1D *histRhoi=new TH1D("rho_I","mass",nGen,xminGen,xmaxGen);
+  unfold.GetRhoI(histRhoi,binMap);
+
+  delete[] binMap;
+  binMap=0;
+
+  //=====================================================================
+  // plot some histograms
+  TCanvas output;
+
+  // produce some plots
+  output.Divide(3,2);
+
+  // Show the matrix which connects input and output
+  // There are overflow bins at the bottom, not shown in the plot
+  // These contain the background shape.
+  // The overflow bins to the left and right contain
+  // events which are not reconstructed. These are necessary for proper MC
+  // normalisation
+  output.cd(1);
+  histMdetGenMC->Draw("BOX");
+
+  // draw generator-level distribution:
+  //   data (red) [for real data this is not available]
+  //   MC input (black) [with completely wrong peak position and shape]
+  //   unfolded data (blue)
+  output.cd(2);
+  histMunfold->SetLineColor(kBlue);
+  histMunfold->Draw();
+  histMgenData->SetLineColor(kRed);
+  histMgenData->Draw("SAME");
+  histMgenMC->Draw("SAME HIST");
+
+  // show detector level distributions
+  //    data (red)
+  //    MC (black)
+  //    unfolded data (blue)
+  output.cd(3);
+  histMdetFold->SetLineColor(kBlue);
+  histMdetFold->Draw();
+  histMdetData->SetLineColor(kRed);
+  histMdetData->Draw("SAME");
+  histMdetMC->Draw("SAME HIST");
+
+  // show correlation coefficients
+  //     all bins outside the peak are found to be highly correlated
+  //     But they are compatible with zero anyway
+  //     If the peak shape is fitted,
+  //     these correlations have to be taken into account, see example
+  output.cd(4);
+  histRhoi->Draw();
+
+  // show rhoi_max(tau) distribution
+  output.cd(5);
+  logTauX->Draw();
+  bestLogTauX->SetMarkerColor(kRed);
+  bestLogTauX->Draw("*");
+
+  output.cd(6);
+  lCurve->Draw("AL");
+  bestLcurve->SetMarkerColor(kRed);
+  bestLcurve->Draw("*");
+
+  output.SaveAs("testUnfold2.ps");
+  return 0;
+}
diff --git a/tutorials/unfold/testUnfold3.C b/tutorials/unfold/testUnfold3.C
new file mode 100644
index 00000000000..bb83f95410e
--- /dev/null
+++ b/tutorials/unfold/testUnfold3.C
@@ -0,0 +1,631 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Simple Test program for the class TUnfoldDensity.
+///
+/// 1-dimensional unfolding with background subtraction
+///
+///  the goal is to unfold the underlying "true" distribution of a variable Pt
+///
+///  the reconstructed Pt is measured in 24 bins from 4 to 28
+///  the generator-level Pt is unfolded into 10 bins from 6 to 26
+///   - plus underflow bin from 0 to 6
+///   - plus overflow bin above 26
+///  there are two background sources bgr1 and bgr2
+///  the signal has a finite trigger efficiency at a threshold of 8 GeV
+///
+///  one type of systematic error is studied, where the signal parameters are
+///  changed
+///
+///  Finally, the unfolding is compared to a "bin-by-bin" correction method
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+/// #### History:
+///  - Version 17.5, in parallel to changes in TUnfold
+///  - Version 17.4, in parallel to changes in TUnfold
+///  - Version 17.3, in parallel to changes in TUnfold
+///  - Version 17.2, in parallel to changes in TUnfold
+///  - Version 17.1, in parallel to changes in TUnfold
+///  - Version 17.0, change to use the class TUnfoldDensity
+///  - Version 16.1, parallel to changes in TUnfold
+///  - Version 16.0, parallel to changes in TUnfold
+///  - Version 15, simple example including background subtraction
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+#include <TMath.h>
+#include <TCanvas.h>
+#include <TRandom3.h>
+#include <TFitter.h>
+#include <TF1.h>
+#include <TStyle.h>
+#include <TVector.h>
+#include <TGraph.h>
+
+#include "TUnfoldDensity.h"
+
+using namespace std;
+
+TRandom *rnd=0;
+
+Double_t GenerateEvent(const Double_t *parm,
+                       const Double_t *triggerParm,
+                       Double_t *intLumi,
+                       Bool_t *triggerFlag,
+                       Double_t *ptGen,Int_t *iType)
+{
+   // generate an event
+   // input:
+   //      parameters for the event generator
+   // return value:
+   //      reconstructed Pt
+   // output to pointers:
+   //      integrated luminosity
+   //      several variables only accessible on generator level
+   //
+   // the parm array defines the physical parameters
+   //  parm[0]: background source 1 fraction
+   //  parm[1]: background source 2 fraction
+   //  parm[2]: lower limit of generated Pt distribution
+   //  parm[3]: upper limit of generated Pt distribution
+   //  parm[4]: exponent for Pt distribution signal
+   //  parm[5]: exponent for Pt distribution background 1
+   //  parm[6]: exponent for Pt distribution background 2
+   //  parm[7]: resolution parameter a goes with sqrt(Pt)
+   //  parm[8]: resolution parameter b goes with Pt
+   //  triggerParm[0]: trigger threshold turn-on position
+   //  triggerParm[1]: trigger threshold turn-on width
+   //  triggerParm[2]: trigger efficiency for high pt
+   //
+   // intLumi is advanced bu 1 for each *generated* event
+   // for data, several events may be generated, until one passes the trigger
+   //
+   // some generator-level quantities are also returned:
+   //   triggerFlag: whether the event passed the trigger threshold
+   //   ptGen: the generated pt
+   //   iType: which type of process was simulated
+   //
+   // the "triggerFlag" also has another meaning:
+   //   if(triggerFlag==0)   only triggered events are returned
+   //
+   // Usage to generate data events
+   //      ptObs=GenerateEvent(parm,triggerParm,0,0,0)
+   //
+   // Usage to generate MC events
+   //      ptGen=GenerateEvent(parm,triggerParm,&triggerFlag,&ptGen,&iType);
+   //
+   Double_t ptObs;
+   Bool_t isTriggered=kFALSE;
+   do {
+      Int_t itype;
+      Double_t ptgen;
+      Double_t f=rnd->Rndm();
+      // decide whether this is background or signal
+      itype=0;
+      if(f<parm[0]) itype=1;
+      else if(f<parm[0]+parm[1]) itype=2;
+      // generate Pt according to distribution  pow(ptgen,a)
+      // get exponent
+      Double_t a=parm[4+itype];
+      Double_t a1=a+1.0;
+      Double_t t=rnd->Rndm();
+      if(a1 == 0.0) {
+         Double_t x0=TMath::Log(parm[2]);
+         ptgen=TMath::Exp(t*(TMath::Log(parm[3])-x0)+x0);
+      } else {
+         Double_t x0=pow(parm[2],a1);
+         ptgen=pow(t*(pow(parm[3],a1)-x0)+x0,1./a1);
+      }
+      if(iType) *iType=itype;
+      if(ptGen) *ptGen=ptgen;
+
+      // smearing in Pt with large asymmetric tail
+      Double_t sigma=
+         TMath::Sqrt(parm[7]*parm[7]*ptgen+parm[8]*parm[8]*ptgen*ptgen);
+      ptObs=rnd->BreitWigner(ptgen,sigma);
+
+      // decide whether event was triggered
+      Double_t triggerProb =
+         triggerParm[2]/(1.+TMath::Exp((triggerParm[0]-ptObs)/triggerParm[1]));
+      isTriggered= rnd->Rndm()<triggerProb;
+      (*intLumi) ++;
+   } while((!triggerFlag) && (!isTriggered));
+   // return trigger decision
+   if(triggerFlag) *triggerFlag=isTriggered;
+   return ptObs;
+}
+
+//int main(int argc, char *argv[])
+void testUnfold3()
+{
+  // switch on histogram errors
+  TH1::SetDefaultSumw2();
+
+  // show fit result
+  gStyle->SetOptFit(1111);
+
+  // random generator
+  rnd=new TRandom3();
+
+  // data and MC luminosities
+  Double_t const lumiData= 30000;
+  Double_t const lumiMC  =1000000;
+
+  //========================
+  // Step 1: define binning, book histograms
+
+  // reconstructed pt (fine binning)
+  Int_t const nDet=24;
+  Double_t const xminDet=4.0;
+  Double_t const xmaxDet=28.0;
+
+  // generated pt (coarse binning)
+  Int_t const nGen=10;
+  Double_t const xminGen= 6.0;
+  Double_t const xmaxGen=26.0;
+
+  //==================================================================
+  // book histograms
+  //  (1) unfolding input: binning scheme is fine for detector,coarse for gen
+  //   histUnfoldInput : reconstructed data, binning for unfolding
+  //   histUnfoldMatrix : generated vs reconstructed distribution
+  //   histUnfoldBgr1 : background source1, as predicted by MC
+  //   histUnfoldBgr2 : background source2, as predicted by MC
+  //      for systematic studies
+  //   histUnfoldMatrixSys : generated vs reconstructed with different signal
+  //
+  //  (2) histograms required for bin-by-bin method
+  //   histDetDATAbbb : reconstructed data for bin-by-bin
+  //   histDetMCbbb : reconstructed MC for bin-by-bin
+  //   histDetMCBgr1bbb : reconstructed MC bgr1 for bin-by-bin
+  //   histDetMCBgr2bbb : reconstructed MC bgr2 for bin-by-bin
+  //   histDetMCBgrPtbbb : reconstructed MC bgr from low/high pt for bin-by-bin
+  //   histGenMCbbb : generated MC truth
+  //     for systematic studies
+  //   histDetMCSysbbb : reconstructed with changed trigger
+  //   histGenMCSysbbb : generated MC truth
+  //
+  //  (3) monitoring and control
+  //   histGenData : data truth for bias tests
+  //   histDetMC : MC prediction
+
+  // (1) create histograms required for unfolding
+  TH1D *histUnfoldInput=
+     new TH1D("unfolding input rec",";ptrec",nDet,xminDet,xmaxDet);
+  TH2D *histUnfoldMatrix=
+     new TH2D("unfolding matrix",";ptgen;ptrec",
+              nGen,xminGen,xmaxGen,nDet,xminDet,xmaxDet);
+  TH1D *histUnfoldBgr1=
+     new TH1D("unfolding bgr1 rec",";ptrec",nDet,xminDet,xmaxDet);
+  TH1D *histUnfoldBgr2=
+     new TH1D("unfolding bgr2 rec",";ptrec",nDet,xminDet,xmaxDet);
+  TH2D *histUnfoldMatrixSys=
+     new TH2D("unfolding matrix sys",";ptgen;ptrec",
+              nGen,xminGen,xmaxGen,nDet,xminDet,xmaxDet);
+
+  // (2) histograms required for bin-by-bin
+  TH1D *histBbbInput=
+     new TH1D("bbb input rec",";ptrec",nGen,xminGen,xmaxGen);
+  TH1D *histBbbSignalRec=
+     new TH1D("bbb signal rec",";ptrec",nGen,xminGen,xmaxGen);
+  TH1D *histBbbBgr1=
+     new TH1D("bbb bgr1 rec",";ptrec",nGen,xminGen,xmaxGen);
+  TH1D *histBbbBgr2=
+     new TH1D("bbb bgr2 rec",";ptrec",nGen,xminGen,xmaxGen);
+  TH1D *histBbbBgrPt=
+     new TH1D("bbb bgrPt rec",";ptrec",nGen,xminGen,xmaxGen);
+  TH1D *histBbbSignalGen=
+     new TH1D("bbb signal gen",";ptgen",nGen,xminGen,xmaxGen);
+  TH1D *histBbbSignalRecSys=
+     new TH1D("bbb signal sys rec",";ptrec",nGen,xminGen,xmaxGen);
+  TH1D *histBbbBgrPtSys=
+     new TH1D("bbb bgrPt sys rec",";ptrec",nGen,xminGen,xmaxGen);
+  TH1D *histBbbSignalGenSys=
+     new TH1D("bbb signal sys gen",";ptgen",nGen,xminGen,xmaxGen);
+
+  // (3) control histograms
+  TH1D *histDataTruth=
+     new TH1D("DATA truth gen",";ptgen",nGen,xminGen,xmaxGen);
+  TH1D *histDetMC=
+     new TH1D("MC prediction rec",";ptrec",nDet,xminDet,xmaxDet);
+  // ==============================================================
+  // Step 2: generate data distributions
+  //
+  // data parameters: in real life these are unknown
+  static Double_t parm_DATA[]={
+     0.05, // fraction of background 1 (on generator level)
+     0.05, // fraction of background 2 (on generator level)
+     3.5, // lower Pt cut (generator level)
+     100.,// upper Pt cut (generator level)
+     -3.0,// signal exponent
+     0.1, // background 1 exponent
+     -0.5, // background 2 exponent
+     0.2, // energy resolution a term
+     0.01, // energy resolution b term
+  };
+  static Double_t triggerParm_DATA[]={8.0, // threshold is 8 GeV
+                               4.0, // width is 4 GeV
+                               0.95 // high Pt efficiency os 95%
+  };
+
+  Double_t intLumi=0.0;
+  while(intLumi<lumiData) {
+     Int_t iTypeGen;
+     Bool_t isTriggered;
+     Double_t ptGen;
+     Double_t ptObs=GenerateEvent(parm_DATA,triggerParm_DATA,&intLumi,
+                                  &isTriggered,&ptGen,&iTypeGen);
+     if(isTriggered) {
+        // (1) histogram for unfolding
+        histUnfoldInput->Fill(ptObs);
+
+        // (2) histogram for bin-by-bin
+        histBbbInput->Fill(ptObs);
+     }
+     // (3) monitoring
+     if(iTypeGen==0) histDataTruth->Fill(ptGen);
+  }
+
+  // ==============================================================
+  // Step 3: generate default MC distributions
+  //
+  // MC parameters
+  // default settings
+  static Double_t parm_MC[]={
+     0.05, // fraction of background 1 (on generator level)
+     0.05, // fraction of background 2 (on generator level)
+     3.5, // lower Pt cut (generator level)
+     100.,// upper Pt cut (generator level)
+     -4.0,// signal exponent !!! steeper than in data
+          //                     to illustrate bin-by-bin bias
+     0.1, // background 1 exponent
+     -0.5, // background 2 exponent
+     0.2, // energy resolution a term
+     0.01, // energy resolution b term
+  };
+  static Double_t triggerParm_MC[]={8.0, // threshold is 8 GeV
+                             4.0, // width is 4 GeV
+                             0.95 // high Pt efficiency is 95%
+  };
+
+  // weighting factor to accomodate for the different luminosity in data and MC
+  Double_t lumiWeight = lumiData/lumiMC;
+  intLumi=0.0;
+  while(intLumi<lumiMC) {
+     Int_t iTypeGen;
+     Bool_t isTriggered;
+     Double_t ptGen;
+     Double_t ptObs=GenerateEvent(parm_MC,triggerParm_MC,&intLumi,&isTriggered,
+                                  &ptGen,&iTypeGen);
+     if(!isTriggered) ptObs=0.0;
+
+     // (1) distribution required for unfolding
+
+     if(iTypeGen==0) {
+        histUnfoldMatrix->Fill(ptGen,ptObs,lumiWeight);
+     } else if(iTypeGen==1) {
+        histUnfoldBgr1->Fill(ptObs,lumiWeight);
+     } else if(iTypeGen==2) {
+        histUnfoldBgr2->Fill(ptObs,lumiWeight);
+     }
+
+     // (2) distributions for bbb
+     if(iTypeGen==0) {
+        if((ptGen>=xminGen)&&(ptGen<xmaxGen)) {
+           histBbbSignalRec->Fill(ptObs,lumiWeight);
+        } else {
+           histBbbBgrPt->Fill(ptObs,lumiWeight);
+        }
+        histBbbSignalGen->Fill(ptGen,lumiWeight);
+     } else if(iTypeGen==1) {
+        histBbbBgr1->Fill(ptObs,lumiWeight);
+     } else if(iTypeGen==2) {
+        histBbbBgr2->Fill(ptObs,lumiWeight);
+     }
+
+     // (3) control distribution
+     histDetMC ->Fill(ptObs,lumiWeight);
+  }
+
+  // ==============================================================
+  // Step 4: generate MC distributions for systematic study
+  // example: study dependence on initial signal shape
+  //   -> BGR distributions do not change
+  static Double_t parm_MC_SYS[]={
+     0.05, // fraction of background: unchanged
+     0.05, // fraction of background: unchanged
+     3.5, // lower Pt cut (generator level)
+     100.,// upper Pt cut (generator level)
+     -2.0, // signal exponent changed
+     0.1, // background 1 exponent
+     -0.5, // background 2 exponent
+     0.2, // energy resolution a term
+     0.01, // energy resolution b term
+  };
+
+  intLumi=0.0;
+  while(intLumi<lumiMC) {
+     Int_t iTypeGen;
+     Bool_t isTriggered;
+     Double_t ptGen;
+     Double_t ptObs=GenerateEvent(parm_MC_SYS,triggerParm_MC,&intLumi,
+                                  &isTriggered,&ptGen,&iTypeGen);
+     if(!isTriggered) ptObs=0.0;
+
+     // (1) for unfolding
+     if(iTypeGen==0) {
+        histUnfoldMatrixSys->Fill(ptGen,ptObs);
+     }
+
+     // (2) for bin-by-bin
+     if(iTypeGen==0) {
+        if((ptGen>=xminGen)&&(ptGen<xmaxGen)) {
+           histBbbSignalRecSys->Fill(ptObs);
+        } else {
+           histBbbBgrPtSys->Fill(ptObs);
+        }
+        histBbbSignalGenSys->Fill(ptGen);
+     }
+  }
+
+  // this method is new in version 16 of TUnfold
+  cout<<"TUnfold version is "<<TUnfold::GetTUnfoldVersion()<<"\n";
+
+  //========================
+  // Step 5: unfolding
+  //
+
+  // here one has to decide about the regularisation scheme
+
+  // regularize curvature
+  TUnfold::ERegMode regMode =
+     TUnfold::kRegModeCurvature;
+
+ // preserve the area
+  TUnfold::EConstraint constraintMode=
+     TUnfold::kEConstraintArea;
+
+  // bin content is divided by the bin width
+  TUnfoldDensity::EDensityMode densityFlags=
+     TUnfoldDensity::kDensityModeBinWidth;
+
+  // set up matrix of migrations
+  TUnfoldDensity unfold(histUnfoldMatrix,TUnfold::kHistMapOutputHoriz,
+                        regMode,constraintMode,densityFlags);
+
+  // define the input vector (the measured data distribution)
+  unfold.SetInput(histUnfoldInput);
+
+  // subtract background, normalized to data luminosity
+  //  with 10% scale error each
+  Double_t scale_bgr=1.0;
+  Double_t dscale_bgr=0.1;
+  unfold.SubtractBackground(histUnfoldBgr1,"background1",scale_bgr,dscale_bgr);
+  unfold.SubtractBackground(histUnfoldBgr2,"background2",scale_bgr,dscale_bgr);
+
+  // add systematic error
+  unfold.AddSysError(histUnfoldMatrixSys,"signalshape_SYS",
+                     TUnfold::kHistMapOutputHoriz,
+                     TUnfoldSys::kSysErrModeMatrix);
+
+  // run the unfolding
+  Int_t nScan=30;
+  TSpline *logTauX,*logTauY;
+  TGraph *lCurve;
+  // this method scans the parameter tau and finds the kink in the L curve
+  // finally, the unfolding is done for the best choice of tau
+  Int_t iBest=unfold.ScanLcurve(nScan,0.,0.,&lCurve,&logTauX,&logTauY);
+
+  cout<<"chi**2="<<unfold.GetChi2A()<<"+"<<unfold.GetChi2L()
+      <<" / "<<unfold.GetNdf()<<"\n";
+
+  // save graphs with one point to visualize best choice of tau
+  Double_t t[1],x[1],y[1];
+  logTauX->GetKnot(iBest,t[0],x[0]);
+  logTauY->GetKnot(iBest,t[0],y[0]);
+  TGraph *bestLcurve=new TGraph(1,x,y);
+  TGraph *bestLogTauLogChi2=new TGraph(1,t,x);
+
+
+  //===========================
+  // Step 6: retrieve unfolding results
+
+  // get unfolding output
+  // includes the statistical and background errors
+  // but not the other systematic uncertainties
+  TH1 *histUnfoldOutput=unfold.GetOutput("PT(unfold,stat+bgrerr)");
+
+  // retrieve error matrix of statistical errors
+  TH2 *histEmatStat=unfold.GetEmatrixInput("unfolding stat error matrix");
+
+  // retrieve full error matrix
+  // This includes all systematic errors
+  TH2 *histEmatTotal=unfold.GetEmatrixTotal("unfolding total error matrix");
+
+  // create two copies of the unfolded data, one with statistical errors
+  // the other with total errors
+  TH1 *histUnfoldStat=new TH1D("PT(unfold,staterr)",";Pt(gen)",
+                                nGen,xminGen,xmaxGen);
+  TH1 *histUnfoldTotal=new TH1D("PT(unfold,totalerr)",";Pt(gen)",
+                                 nGen,xminGen,xmaxGen);
+  for(Int_t i=0;i<nGen+2;i++) {
+     Double_t c=histUnfoldOutput->GetBinContent(i);
+
+     // histogram with unfolded data and stat errors
+     histUnfoldStat->SetBinContent(i,c);
+     histUnfoldStat->SetBinError
+        (i,TMath::Sqrt(histEmatStat->GetBinContent(i,i)));
+
+     // histogram with unfolded data and total errors
+     histUnfoldTotal->SetBinContent(i,c);
+     histUnfoldTotal->SetBinError
+        (i,TMath::Sqrt(histEmatTotal->GetBinContent(i,i)));
+  }
+
+  // create histogram with correlation matrix
+  TH2D *histCorr=new TH2D("Corr(total)",";Pt(gen);Pt(gen)",
+                          nGen,xminGen,xmaxGen,nGen,xminGen,xmaxGen);
+  for(Int_t i=0;i<nGen+2;i++) {
+     Double_t ei,ej;
+     ei=TMath::Sqrt(histEmatTotal->GetBinContent(i,i));
+     if(ei<=0.0) continue;
+     for(Int_t j=0;j<nGen+2;j++) {
+        ej=TMath::Sqrt(histEmatTotal->GetBinContent(j,j));
+        if(ej<=0.0) continue;
+        histCorr->SetBinContent(i,j,histEmatTotal->GetBinContent(i,j)/ei/ej);
+     }
+  }
+
+  // retrieve bgr source 1
+  TH1 *histDetNormBgr1=unfold.GetBackground("bgr1 normalized",
+                                             "background1");
+  TH1 *histDetNormBgrTotal=unfold.GetBackground("bgr total normalized");
+
+  //========================
+  // Step 7: plots
+
+  TCanvas output;
+  output.Divide(3,2);
+  output.cd(1);
+  // data, MC prediction, background
+  histUnfoldInput->SetMinimum(0.0);
+  histUnfoldInput->Draw("E");
+  histDetMC->SetMinimum(0.0);
+  histDetMC->SetLineColor(kBlue);
+  histDetNormBgrTotal->SetLineColor(kRed);
+  histDetNormBgr1->SetLineColor(kCyan);
+  histDetMC->Draw("SAME HIST");
+  histDetNormBgr1->Draw("SAME HIST");
+  histDetNormBgrTotal->Draw("SAME HIST");
+
+  output.cd(2);
+  // unfolded data, data truth, MC truth
+  histUnfoldTotal->SetMinimum(0.0);
+  histUnfoldTotal->SetMaximum(histUnfoldTotal->GetMaximum()*1.5);
+  // outer error: total error
+  histUnfoldTotal->Draw("E");
+  // middle error: stat+bgr
+  histUnfoldOutput->Draw("SAME E1");
+  // inner error: stat only
+  histUnfoldStat->Draw("SAME E1");
+  histDataTruth->Draw("SAME HIST");
+  histBbbSignalGen->SetLineColor(kBlue);
+  histBbbSignalGen->Draw("SAME HIST");
+
+  output.cd(3);
+  // unfolding matrix
+  histUnfoldMatrix->SetLineColor(kBlue);
+  histUnfoldMatrix->Draw("BOX");
+
+  // show tau as a function of chi**2
+  output.cd(4);
+  logTauX->Draw();
+  bestLogTauLogChi2->SetMarkerColor(kRed);
+  bestLogTauLogChi2->Draw("*");
+
+  // show the L curve
+  output.cd(5);
+  lCurve->Draw("AL");
+  bestLcurve->SetMarkerColor(kRed);
+  bestLcurve->Draw("*");
+
+  // show correlation matrix
+  output.cd(6);
+  histCorr->Draw("BOX");
+
+  output.SaveAs("testUnfold3.ps");
+
+  //============================================================
+  // step 8: compare results to the so-called bin-by-bin "correction"
+
+  std::cout<<"bin truth   result   (stat)   (bgr)    (sys)\n";
+  std::cout<<"===+=====+=========+========+========+=======\n";
+  for(Int_t i=1;i<=nGen;i++) {
+     // data contribution in this bin
+     Double_t data=histBbbInput->GetBinContent(i);
+     Double_t errData=histBbbInput->GetBinError(i);
+
+     // subtract background contributions
+     Double_t data_bgr=data
+        - scale_bgr*(histBbbBgr1->GetBinContent(i)
+                     + histBbbBgr2->GetBinContent(i)
+                     + histBbbBgrPt->GetBinContent(i));
+     Double_t errData_bgr=TMath::Sqrt
+        (errData*errData+
+         TMath::Power(dscale_bgr*histBbbBgr1->GetBinContent(i),2)+
+         TMath::Power(scale_bgr*histBbbBgr1->GetBinError(i),2)+
+         TMath::Power(dscale_bgr*histBbbBgr2->GetBinContent(i),2)+
+         TMath::Power(scale_bgr*histBbbBgr2->GetBinError(i),2)+
+         TMath::Power(dscale_bgr*histBbbBgrPt->GetBinContent(i),2)+
+         TMath::Power(scale_bgr*histBbbBgrPt->GetBinError(i),2));
+     // "correct" the data, using the Monte Carlo and neglecting off-diagonals
+     Double_t fCorr=(histBbbSignalGen->GetBinContent(i)/
+                     histBbbSignalRec->GetBinContent(i));
+     Double_t data_bbb= data_bgr *fCorr;
+     // stat only error
+     Double_t errData_stat_bbb = errData*fCorr;
+     // stat plus background subtraction
+     Double_t errData_statbgr_bbb = errData_bgr*fCorr;
+
+     // estimate systematic error by repeating the exercise
+     // using the MC with systematic shifts applied
+     Double_t fCorr_sys=(histBbbSignalGenSys->GetBinContent(i)/
+                         histBbbSignalRecSys->GetBinContent(i));
+     Double_t shift_sys= data_bgr*fCorr_sys - data_bbb;
+
+     // add systematic shift quadratically and get total error
+     Double_t errData_total_bbb=
+        TMath::Sqrt(errData_statbgr_bbb*errData_statbgr_bbb
+                    +shift_sys*shift_sys);
+
+     // get results from real unfolding
+     Double_t data_unfold= histUnfoldStat->GetBinContent(i);
+     Double_t errData_stat_unfold=histUnfoldStat->GetBinError(i);
+     Double_t errData_statbgr_unfold=histUnfoldOutput->GetBinError(i);
+     Double_t errData_total_unfold=histUnfoldTotal->GetBinError(i);
+
+     // compare
+
+     std::cout<<TString::Format
+        ("%3d %5.0f %8.1f +/-%5.1f +/-%5.1f +/-%5.1f (unfolding)",
+         i,histDataTruth->GetBinContent(i),data_unfold,
+         errData_stat_unfold,TMath::Sqrt(errData_statbgr_unfold*
+                                         errData_statbgr_unfold-
+                                         errData_stat_unfold*
+                                         errData_stat_unfold),
+         TMath::Sqrt(errData_total_unfold*
+                     errData_total_unfold-
+                     errData_statbgr_unfold*
+                     errData_statbgr_unfold))<<"\n";
+     std::cout<<TString::Format
+        ("          %8.1f +/-%5.1f +/-%5.1f +/-%5.1f (bin-by-bin)",
+         data_bbb,errData_stat_bbb,TMath::Sqrt(errData_statbgr_bbb*
+                                               errData_statbgr_bbb-
+                                               errData_stat_bbb*
+                                               errData_stat_bbb),
+         TMath::Sqrt(errData_total_bbb*
+                     errData_total_bbb-
+                     errData_statbgr_bbb*
+                     errData_statbgr_bbb))
+              <<"\n\n";
+  }
+}
diff --git a/tutorials/unfold/testUnfold4.C b/tutorials/unfold/testUnfold4.C
new file mode 100644
index 00000000000..f8a9a2a418b
--- /dev/null
+++ b/tutorials/unfold/testUnfold4.C
@@ -0,0 +1,222 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Test program for the class TUnfoldSys.
+///
+/// Simple toy tests of the TUnfold package
+///
+/// Pseudo data (5000 events) are unfolded into three components
+/// The unfolding is performed once without and once with area constraint
+///
+/// Ideally, the pulls may show that the result is biased if no constraint
+/// is applied. This is expected because the true data errors are not known,
+/// and instead the sqrt(data) errors are used.
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+/// #### History:
+///  - Version 17.5, in parallel to changes in TUnfold
+///  - Version 17.4, in parallel to changes in TUnfold
+///  - Version 17.3, in parallel to changes in TUnfold
+///  - Version 17.2, in parallel to changes in TUnfold
+///  - Version 17.1, in parallel to changes in TUnfold
+///  - Version 16.1, parallel to changes in TUnfold
+///  - Version 16.0, parallel to changes in TUnfold
+///  - Version 15, use L-curve scan to scan the average correlation
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+#include <TMath.h>
+#include <TCanvas.h>
+#include <TRandom3.h>
+#include <TFitter.h>
+#include <TF1.h>
+#include <TStyle.h>
+#include <TVector.h>
+#include <TGraph.h>
+
+#include "TUnfoldDensity.h"
+
+using namespace std;
+
+TRandom *rnd=0;
+
+Int_t GenerateGenEvent(Int_t nmax,const Double_t *probability) {
+   // choose an integer random number in the range [0,nmax]
+   //    (the generator level bin)
+   // depending on the probabilities
+   //   probability[0],probability[1],...probability[nmax-1]
+   Double_t f=rnd->Rndm();
+   Int_t r=0;
+   while((r<nmax)&&(f>=probability[r])) {
+      f -= probability[r];
+      r++;
+   }
+   return r;
+}
+
+Double_t GenerateRecEvent(const Double_t *shapeParm) {
+   // return a coordinate (the reconstructed variable)
+   // depending on shapeParm[]
+   //  shapeParm[0]: fraction of events with Gaussian distribution
+   //  shapeParm[1]: mean of Gaussian
+   //  shapeParm[2]: width of Gaussian
+   //  (1-shapeParm[0]): fraction of events with flat distribution
+   //  shapeParm[3]: minimum of flat component
+   //  shapeParm[4]: maximum of flat component
+   Double_t f=rnd->Rndm();
+   Double_t r;
+   if(f<shapeParm[0]) {
+      r=rnd->Gaus(shapeParm[1],shapeParm[2]);
+   } else {
+      r=rnd->Rndm()*(shapeParm[4]-shapeParm[3])+shapeParm[3];
+   }
+   return r;
+}
+
+void testUnfold4()
+{
+  // switch on histogram errors
+  TH1::SetDefaultSumw2();
+
+  // random generator
+  rnd=new TRandom3();
+
+  // data and MC number of events
+  Double_t const nData0=    500.0;
+  Double_t const nMC0  =  50000.0;
+
+  // Binning
+  // reconstructed variable (0-10)
+  Int_t const nDet=15;
+  Double_t const xminDet=0.0;
+  Double_t const xmaxDet=15.0;
+
+  // signal binning (three shapes: 0,1,2)
+  Int_t const nGen=3;
+  Double_t const xminGen=-0.5;
+  Double_t const xmaxGen= 2.5;
+
+  // parameters
+  // fraction of events per signal shape
+  static const Double_t genFrac[]={0.3,0.6,0.1};
+
+  // signal shapes
+  static const Double_t genShape[][5]=
+     {{1.0,2.0,1.5,0.,15.},
+      {1.0,7.0,2.5,0.,15.},
+      {0.0,0.0,0.0,0.,15.}};
+
+  // define DATA histograms
+  // observed data distribution
+  TH1D *histDetDATA=new TH1D("Yrec",";DATA(Yrec)",nDet,xminDet,xmaxDet);
+
+  // define MC histograms
+  // matrix of migrations
+  TH2D *histGenDetMC=new TH2D("Yrec%Xgen","MC(Xgen,Yrec)",
+                              nGen,xminGen,xmaxGen,nDet,xminDet,xmaxDet);
+
+  TH1D *histUnfold=new TH1D("Xgen",";DATA(Xgen)",nGen,xminGen,xmaxGen);
+
+  TH1D **histPullNC=new TH1D* [nGen];
+  TH1D **histPullArea=new TH1D* [nGen];
+  for(int i=0;i<nGen;i++) {
+     histPullNC[i]=new TH1D(TString::Format("PullNC%d",i),"pull",15,-3.,3.);
+     histPullArea[i]=new TH1D(TString::Format("PullArea%d",i),"pull",15,-3.,3.);
+  }
+
+  // this method is new in version 16 of TUnfold
+  cout<<"TUnfold version is "<<TUnfold::GetTUnfoldVersion()<<"\n";
+
+  for(int itoy=0;itoy<1000;itoy++) {
+     if(!(itoy %10)) cout<<"toy iteration: "<<itoy<<"\n";
+     histDetDATA->Reset();
+     histGenDetMC->Reset();
+
+     Int_t nData=rnd->Poisson(nData0);
+     for(Int_t i=0;i<nData;i++) {
+        Int_t iGen=GenerateGenEvent(nGen,genFrac);
+        Double_t yObs=GenerateRecEvent(genShape[iGen]);
+        histDetDATA->Fill(yObs);
+     }
+
+     Int_t nMC=rnd->Poisson(nMC0);
+     for(Int_t i=0;i<nMC;i++) {
+        Int_t iGen=GenerateGenEvent(nGen,genFrac);
+        Double_t yObs=GenerateRecEvent(genShape[iGen]);
+        histGenDetMC->Fill(iGen,yObs);
+     }
+     /* for(Int_t ix=0;ix<=histGenDetMC->GetNbinsX()+1;ix++) {
+        for(Int_t iy=0;iy<=histGenDetMC->GetNbinsY()+1;iy++) {
+           cout<<ix<<iy<<" : "<<histGenDetMC->GetBinContent(ix,iy)<<"\n";
+        }
+        } */
+     //========================
+     // unfolding
+
+     TUnfoldSys unfold(histGenDetMC,TUnfold::kHistMapOutputHoriz,
+                       TUnfold::kRegModeSize,TUnfold::kEConstraintNone);
+     // define the input vector (the measured data distribution)
+     unfold.SetInput(histDetDATA,0.0,1.0);
+
+     // run the unfolding
+     unfold.ScanLcurve(50,0.,0.,0,0,0);
+
+     // fill pull distributions without constraint
+     unfold.GetOutput(histUnfold);
+
+     for(int i=0;i<nGen;i++) {
+        histPullNC[i]->Fill((histUnfold->GetBinContent(i+1)-genFrac[i]*nData0)/
+                            histUnfold->GetBinError(i+1));
+     }
+
+     // repeat unfolding on the same data, now with Area constraint
+     unfold.SetConstraint(TUnfold::kEConstraintArea);
+
+     // run the unfolding
+     unfold.ScanLcurve(50,0.,0.,0,0,0);
+
+     // fill pull distributions with constraint
+     unfold.GetOutput(histUnfold);
+
+     for(int i=0;i<nGen;i++) {
+        histPullArea[i]->Fill((histUnfold->GetBinContent(i+1)-genFrac[i]*nData0)/
+                            histUnfold->GetBinError(i+1));
+     }
+
+  }
+  TCanvas output;
+  output.Divide(3,2);
+
+  gStyle->SetOptFit(1111);
+
+  for(int i=0;i<nGen;i++) {
+     output.cd(i+1);
+     histPullNC[i]->Fit("gaus");
+     histPullNC[i]->Draw();
+  }
+  for(int i=0;i<nGen;i++) {
+     output.cd(i+4);
+     histPullArea[i]->Fit("gaus");
+     histPullArea[i]->Draw();
+  }
+  output.SaveAs("testUnfold4.ps");
+}
diff --git a/tutorials/unfold/testUnfold5a.C b/tutorials/unfold/testUnfold5a.C
new file mode 100644
index 00000000000..ce57172e0bc
--- /dev/null
+++ b/tutorials/unfold/testUnfold5a.C
@@ -0,0 +1,338 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Test program for the classes TUnfoldDensity and TUnfoldBinning
+///
+/// A toy test of the TUnfold package
+///
+/// This is an example of unfolding a two-dimensional distribution
+/// also using an auxiliary measurement to constrain some background
+///
+/// The example comprises several macros
+///  - testUnfold5a.C   create root files with TTree objects for
+///                      signal, background and data
+///            - write files  testUnfold5_signal.root
+///                            testUnfold5_background.root
+///                            testUnfold5_data.root
+///
+///  - testUnfold5b.C   create a root file with the TUnfoldBinning objects
+///            - write file  testUnfold5_binning.root
+///
+///  - testUnfold5c.C   loop over trees and fill histograms based on the
+///                      TUnfoldBinning objects
+///            - read  testUnfold5_binning.root
+///                     testUnfold5_signal.root
+///                     testUnfold5_background.root
+///                     testUnfold5_data.root
+///
+///            - write testUnfold5_histograms.root
+///
+///  - testUnfold5d.C   run the unfolding
+///            - read  testUnfold5_histograms.root
+///            - write testUnfold5_result.root
+///                     testUnfold5_result.ps
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+/// #### History:
+///  - Version 17.5, in parallel to changes in TUnfold
+///  - Version 17.4, in parallel to changes in TUnfold
+///  - Version 17.3, in parallel to changes in TUnfold
+///  - Version 17.2, in parallel to changes in TUnfold
+///  - Version 17.1, in parallel to changes in TUnfold
+///  - Version 17.0 example for multi-dimensional unfolding
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+#include <iostream>
+#include <map>
+#include <cmath>
+#include <TMath.h>
+#include <TRandom3.h>
+#include <TFile.h>
+#include <TTree.h>
+
+using namespace std;
+
+TRandom *g_rnd=0;
+
+class ToyEvent {
+public:
+   void GenerateDataEvent(TRandom *rnd);
+   void GenerateSignalEvent(TRandom *rnd);
+   void GenerateBgrEvent(TRandom *rnd);
+   // reconstructed quantities
+   inline Double_t GetPtRec(void) const { return fPtRec; }
+   inline Double_t GetEtaRec(void) const { return fEtaRec; }
+   inline Double_t GetDiscriminator(void) const {return fDiscriminator; }
+   inline Bool_t IsTriggered(void) const { return fIsTriggered; }
+
+   // generator level quantities
+   inline Double_t GetPtGen(void) const {
+      if(IsSignal()) return fPtGen;
+      else return -1.0;
+   }
+   inline Double_t GetEtaGen(void) const {
+       if(IsSignal()) return fEtaGen;
+       else return 999.0;
+   }
+   inline Bool_t IsSignal(void) const { return fIsSignal; }
+protected:
+
+   void GenerateSignalKinematics(TRandom *rnd,Bool_t isData);
+   void GenerateBgrKinematics(TRandom *rnd,Bool_t isData);
+   void GenerateReco(TRandom *rnd);
+
+   // reconstructed quantities
+   Double_t fPtRec;
+   Double_t fEtaRec;
+   Double_t fDiscriminator;
+   Bool_t fIsTriggered;
+   // generated quantities
+   Double_t fPtGen;
+   Double_t fEtaGen;
+   Bool_t fIsSignal;
+
+   static Double_t kDataSignalFraction;
+
+};
+
+void testUnfold5a()
+{
+  // random generator
+  g_rnd=new TRandom3();
+
+  // data and MC number of events
+  const Int_t neventData         =  20000;
+  Double_t const neventSignalMC  =2000000;
+  Double_t const neventBgrMC     =2000000;
+
+  Float_t etaRec,ptRec,discr,etaGen,ptGen;
+  Int_t istriggered,issignal;
+
+  //==================================================================
+  // Step 1: generate data TTree
+
+  TFile *dataFile=new TFile("testUnfold5_data.root","recreate");
+  TTree *dataTree=new TTree("data","event");
+
+  dataTree->Branch("etarec",&etaRec,"etarec/F");
+  dataTree->Branch("ptrec",&ptRec,"ptrec/F");
+  dataTree->Branch("discr",&discr,"discr/F");
+
+  // for real data, only the triggered events are available
+  dataTree->Branch("istriggered",&istriggered,"istriggered/I");
+  // data truth parameters
+  dataTree->Branch("etagen",&etaGen,"etagen/F");
+  dataTree->Branch("ptgen",&ptGen,"ptgen/F");
+  dataTree->Branch("issignal",&issignal,"issignal/I");
+
+  cout<<"fill data tree\n";
+
+  Int_t nEvent=0,nTriggered=0;
+  while(nTriggered<neventData) {
+     ToyEvent event;
+     event.GenerateDataEvent(g_rnd);
+
+     etaRec=event.GetEtaRec();
+     ptRec=event.GetPtRec();
+     discr=event.GetDiscriminator();
+     istriggered=event.IsTriggered() ? 1 : 0;
+     etaGen=event.GetEtaGen();
+     ptGen=event.GetPtGen();
+     issignal=event.IsSignal() ? 1 : 0;
+
+     dataTree->Fill();
+
+     if(!(nEvent%100000)) cout<<"   data event "<<nEvent<<"\n";
+
+     if(istriggered) nTriggered++;
+     nEvent++;
+
+  }
+
+  dataTree->Write();
+  delete dataTree;
+  delete dataFile;
+
+  //==================================================================
+  // Step 2: generate signal TTree
+
+  TFile *signalFile=new TFile("testUnfold5_signal.root","recreate");
+  TTree *signalTree=new TTree("signal","event");
+
+  signalTree->Branch("etarec",&etaRec,"etarec/F");
+  signalTree->Branch("ptrec",&ptRec,"ptrec/F");
+  signalTree->Branch("discr",&discr,"discr/F");
+  signalTree->Branch("istriggered",&istriggered,"istriggered/I");
+  signalTree->Branch("etagen",&etaGen,"etagen/F");
+  signalTree->Branch("ptgen",&ptGen,"ptgen/F");
+
+  cout<<"fill signal tree\n";
+
+  for(int ievent=0;ievent<neventSignalMC;ievent++) {
+     ToyEvent event;
+     event.GenerateSignalEvent(g_rnd);
+
+     etaRec=event.GetEtaRec();
+     ptRec=event.GetPtRec();
+     discr=event.GetDiscriminator();
+     istriggered=event.IsTriggered() ? 1 : 0;
+     etaGen=event.GetEtaGen();
+     ptGen=event.GetPtGen();
+
+     if(!(ievent%100000)) cout<<"   signal event "<<ievent<<"\n";
+
+     signalTree->Fill();
+  }
+
+  signalTree->Write();
+  delete signalTree;
+  delete signalFile;
+
+  // ==============================================================
+  // Step 3: generate background MC TTree
+
+  TFile *bgrFile=new TFile("testUnfold5_background.root","recreate");
+  TTree *bgrTree=new TTree("background","event");
+
+  bgrTree->Branch("etarec",&etaRec,"etarec/F");
+  bgrTree->Branch("ptrec",&ptRec,"ptrec/F");
+  bgrTree->Branch("discr",&discr,"discr/F");
+  bgrTree->Branch("istriggered",&istriggered,"istriggered/I");
+
+  cout<<"fill background tree\n";
+
+  for(int ievent=0;ievent<neventBgrMC;ievent++) {
+     ToyEvent event;
+     event.GenerateBgrEvent(g_rnd);
+     etaRec=event.GetEtaRec();
+     ptRec=event.GetPtRec();
+     discr=event.GetDiscriminator();
+     istriggered=event.IsTriggered() ? 1 : 0;
+
+     if(!(ievent%100000)) cout<<"   background event "<<ievent<<"\n";
+
+     bgrTree->Fill();
+  }
+
+  bgrTree->Write();
+  delete bgrTree;
+  delete bgrFile;
+}
+
+Double_t ToyEvent::kDataSignalFraction=0.8;
+
+void ToyEvent::GenerateDataEvent(TRandom *rnd) {
+   fIsSignal=rnd->Uniform()<kDataSignalFraction;
+   if(IsSignal()) {
+      GenerateSignalKinematics(rnd,kTRUE);
+   } else {
+      GenerateBgrKinematics(rnd,kTRUE);
+   }
+   GenerateReco(rnd);
+}
+
+void ToyEvent::GenerateSignalEvent(TRandom *rnd) {
+   fIsSignal=1;
+   GenerateSignalKinematics(rnd,kFALSE);
+   GenerateReco(rnd);
+}
+
+void ToyEvent::GenerateBgrEvent(TRandom *rnd) {
+   fIsSignal=0;
+   GenerateBgrKinematics(rnd,kFALSE);
+   GenerateReco(rnd);
+}
+
+void ToyEvent::GenerateSignalKinematics(TRandom *rnd,Bool_t isData) {
+   Double_t e_T0=0.5;
+   Double_t e_T0_eta=0.0;
+   Double_t e_n=2.0;
+   Double_t e_n_eta=0.0;
+   Double_t eta_p2=0.0;
+   Double_t etaMax=4.0;
+   if(isData) {
+      e_T0=0.6;
+      e_n=2.5;
+      e_T0_eta=0.05;
+      e_n_eta=-0.05;
+      eta_p2=1.5;
+   }
+   if(eta_p2>0.0) {
+      fEtaGen=TMath::Power(rnd->Uniform(),eta_p2)*etaMax;
+      if(rnd->Uniform()>=0.5) fEtaGen= -fEtaGen;
+   } else {
+      fEtaGen=rnd->Uniform(-etaMax,etaMax);
+   }
+   Double_t n=e_n   + e_n_eta*fEtaGen;
+   Double_t T0=e_T0 + e_T0_eta*fEtaGen;
+   fPtGen=(TMath::Power(rnd->Uniform(),1./(1.-n))-1.)*T0;
+   /*   static int print=100;
+      if(print) {
+         cout<<fEtaGen
+            <<" "<<fPtGen
+             <<"\n";
+         print--;
+         } */
+}
+
+void ToyEvent::GenerateBgrKinematics(TRandom *rnd,Bool_t isData) {
+   fPtGen=0.0;
+   fEtaGen=0.0;
+   fPtRec=rnd->Exp(isData ? 2.5 : 2.5);
+   fEtaRec=rnd->Uniform(-3.,3.);
+}
+
+void ToyEvent::GenerateReco(TRandom *rnd) {
+   if(fIsSignal) {
+      Double_t expEta=TMath::Exp(fEtaGen);
+      Double_t eGen=fPtGen*(expEta+1./expEta);
+      Double_t sigmaE=0.1*TMath::Sqrt(eGen)+(0.01+0.002*TMath::Abs(fEtaGen))
+         *eGen;
+      Double_t eRec;
+      do {
+         eRec=rnd->Gaus(eGen,sigmaE);
+      } while(eRec<=0.0);
+      Double_t sigmaEta=0.1+0.02*fEtaGen;
+      fEtaRec=rnd->Gaus(fEtaGen,sigmaEta);
+      fPtRec=eRec/(expEta+1./expEta);
+      do {
+         Double_t tauDiscr=0.08-0.04/(1.+fPtRec/10.0);
+         Double_t sigmaDiscr=0.01;
+         fDiscriminator=1.0-rnd->Exp(tauDiscr)+rnd->Gaus(0.,sigmaDiscr);
+      } while((fDiscriminator<=0.)||(fDiscriminator>=1.));
+      /* static int print=100;
+         if(print) {
+         cout<<fEtaGen<<" "<<fPtGen
+             <<" -> "<<fEtaRec<<" "<<fPtRec
+             <<"\n";
+         print--;
+         } */
+   } else {
+      do {
+         Double_t tauDiscr=0.15-0.05/(1.+fPtRec/5.0)+0.1*fEtaRec;
+         Double_t sigmaDiscr=0.02+0.01*fEtaRec;
+         fDiscriminator=rnd->Exp(tauDiscr)+rnd->Gaus(0.,sigmaDiscr);
+      } while((fDiscriminator<=0.)||(fDiscriminator>=1.));
+   }
+   fIsTriggered=(rnd->Uniform()<1./(TMath::Exp(-fPtRec+3.5)+1.));
+}
diff --git a/tutorials/unfold/testUnfold5b.C b/tutorials/unfold/testUnfold5b.C
new file mode 100644
index 00000000000..b96fc0791b0
--- /dev/null
+++ b/tutorials/unfold/testUnfold5b.C
@@ -0,0 +1,182 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Test program for the classes TUnfoldDensity and TUnfoldBinning.
+///
+/// A toy test of the TUnfold package
+///
+/// This is an example of unfolding a two-dimensional distribution
+/// also using an auxiliary measurement to constrain some background
+///
+/// The example comprises several macros
+///  - testUnfold5a.C   create root files with TTree objects for
+///                      signal, background and data
+///            - write files  testUnfold5_signal.root
+///                           testUnfold5_background.root
+///                           testUnfold5_data.root
+///
+///  - testUnfold5b.C   create a root file with the TUnfoldBinning objects
+///            - write file  testUnfold5_binning.root
+///
+///  - testUnfold5c.C   loop over trees and fill histograms based on the
+///                      TUnfoldBinning objects
+///            - read  testUnfold5_binning.root
+///                    testUnfold5_signal.root
+///                    testUnfold5_background.root
+///                    testUnfold5_data.root
+///
+///            - write testUnfold5_histograms.root
+///
+///  - testUnfold5d.C   run the unfolding
+///            - read  testUnfold5_histograms.root
+///            - write testUnfold5_result.root
+///                    testUnfold5_result.ps
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+/// #### History:
+///  - Version 17.5, updated for writing out XML code
+///  - Version 17.4, updated for writing out XML code
+///  - Version 17.3, updated for writing out XML code
+///  - Version 17.2, updated for writing out XML code
+///  - Version 17.1, in parallel to changes in TUnfold
+///  - Version 17.0 example for multi-dimensional unfolding
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+#include <iostream>
+#include <fstream>
+#include <TFile.h>
+#include "TUnfoldBinningXML.h"
+#include <TF2.h>
+
+using namespace std;
+
+void testUnfold5b()
+{
+
+  // write binning schemes to root file
+  TFile *binningSchemes=new TFile("testUnfold5_binning.root","recreate");
+
+  // reconstructed pt, eta, discriminator
+#define NBIN_PT_FINE 8
+#define NBIN_ETA_FINE 10
+#define NBIN_DISCR 4
+
+  // generated pt, eta
+#define NBIN_PT_COARSE 3
+#define NBIN_ETA_COARSE 3
+
+  // pt binning
+  Double_t ptBinsFine[NBIN_PT_FINE+1]=
+     {3.5,4.0,4.5,5.0,6.0,7.0,8.0,10.0,13.0};
+  Double_t ptBinsCoarse[NBIN_PT_COARSE+1]=
+     {    4.0,    5.0,    7.0,    10.0};
+  // eta binning
+  Double_t etaBinsFine[NBIN_ETA_FINE+1]=
+     {-3.,-2.5,-2.0,-1.,-0.5,0.0,0.5,1.,2.,2.5,3.};
+  Double_t etaBinsCoarse[NBIN_ETA_COARSE+1]=
+     {         -2.0,    -0.5,    0.5,   2. };
+
+  // discriminator bins
+  Double_t discrBins[NBIN_DISCR+1]={0.,0.15,0.5,0.85,1.0};
+
+  //=======================================================================
+  // detector level binning scheme
+
+  TUnfoldBinning *detectorBinning=new TUnfoldBinning("detector");
+  // highest discriminator bin has fine binning
+  TUnfoldBinning *detectorDistribution=
+     detectorBinning->AddBinning("detectordistribution");
+  detectorDistribution->AddAxis("pt",NBIN_PT_FINE,ptBinsFine,
+                                false, // no underflow bin (not reconstructed)
+                                true // overflow bin
+                                );
+  detectorDistribution->AddAxis("eta",NBIN_ETA_FINE,etaBinsFine,
+                                false, // no underflow bin (not reconstructed)
+                                false // no overflow bin (not reconstructed)
+                                );
+  detectorDistribution->AddAxis("discriminator",NBIN_DISCR,discrBins,
+                                false, // no underflow bin (empty)
+                                false // no overflow bin (empty)
+                                );
+  /* TUnfoldBinning *detectorExtra=
+     detectorBinning->AddBinning("detectorextra",7,"one;zwei;three");
+     detectorBinning->PrintStream(cout); */
+
+  //=======================================================================
+  // generator level binning
+  TUnfoldBinning *generatorBinning=new TUnfoldBinning("generator");
+
+  // signal distribution is measured with coarse binning
+  // underflow and overflow bins are needed ot take care of
+  // what happens outside the phase-space
+  TUnfoldBinning *signalBinning = generatorBinning->AddBinning("signal");
+  signalBinning->AddAxis("ptgen",NBIN_PT_COARSE,ptBinsCoarse,
+                         true, // underflow bin
+                         true // overflow bin
+                         );
+  signalBinning->AddAxis("etagen",NBIN_ETA_COARSE,etaBinsCoarse,
+                         true, // underflow bin
+                         true // overflow bin
+                         );
+
+  // this is just an example how to set bin-dependent factors
+  // for the regularisation
+  TF2 *userFunc=new TF2("userfunc","1./x+0.2*y^2",ptBinsCoarse[0],
+                        ptBinsCoarse[NBIN_PT_COARSE],
+                        etaBinsCoarse[0],etaBinsCoarse[NBIN_ETA_COARSE]);
+  signalBinning->SetBinFactorFunction(1.0,userFunc);
+
+  // background distribution is unfolded with fine binning
+  // !!! in the reconstructed variable !!!
+  //
+  // This has the effect of "normalizing" the background in each
+  // pt,eta bin to the low discriminator values
+  // Only the shape of the discriminator in each (pt,eta) bin
+  // is taken from Monte Carlo
+  //
+  // This method has been applied e.g. in
+  //   H1 Collaboration, "Prompt photons in Photoproduction"
+  //   Eur.Phys.J. C66 (2010) 17
+  //
+  TUnfoldBinning *bgrBinning = generatorBinning->AddBinning("background");
+  bgrBinning->AddAxis("ptrec",NBIN_PT_FINE,ptBinsFine,
+                      false, // no underflow bin (not reconstructed)
+                      true // overflow bin
+                      );
+  bgrBinning->AddAxis("etarec",NBIN_ETA_FINE,etaBinsFine,
+                      false, // no underflow bin (not reconstructed)
+                      false // no overflow bin (not reconstructed)
+                      );
+  generatorBinning->PrintStream(cout);
+
+  detectorBinning->Write();
+  generatorBinning->Write();
+
+  ofstream xmlOut("testUnfold5binning.xml");
+  TUnfoldBinningXML::ExportXML(*detectorBinning,xmlOut,kTRUE,kFALSE);
+  TUnfoldBinningXML::ExportXML(*generatorBinning,xmlOut,kFALSE,kTRUE);
+  TUnfoldBinningXML::WriteDTD();
+  xmlOut.close();
+
+  delete binningSchemes;
+}
diff --git a/tutorials/unfold/testUnfold5c.C b/tutorials/unfold/testUnfold5c.C
new file mode 100644
index 00000000000..ee09cda40c2
--- /dev/null
+++ b/tutorials/unfold/testUnfold5c.C
@@ -0,0 +1,290 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Test program for the classes TUnfoldDensity and TUnfoldBinning.
+///
+/// A toy test of the TUnfold package
+///
+/// This is an example of unfolding a two-dimensional distribution
+/// also using an auxiliary measurement to constrain some background
+///
+/// The example comprises several macros
+///  - testUnfold5a.C   create root files with TTree objects for
+///                      signal, background and data
+///            - write files  testUnfold5_signal.root
+///                           testUnfold5_background.root
+///                           testUnfold5_data.root
+///
+///  - testUnfold5b.C   create a root file with the TUnfoldBinning objects
+///            - write file  testUnfold5_binning.root
+///
+///  - testUnfold5c.C   loop over trees and fill histograms based on the
+///                      TUnfoldBinning objects
+///            - read  testUnfold5_binning.root
+///                    testUnfold5_signal.root
+///                    testUnfold5_background.root
+///                    testUnfold5_data.root
+///
+///            - write testUnfold5_histograms.root
+///
+///  - testUnfold5d.C   run the unfolding
+///            - read  testUnfold5_histograms.root
+///            - write testUnfold5_result.root
+///                    testUnfold5_result.ps
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+/// #### History:
+///  - Version 17.5, updated for reading binning from XML file
+///  - Version 17.4, updated for reading binning from XML file
+///  - Version 17.3, updated for reading binning from XML file
+///  - Version 17.2, updated for reading binning from XML file
+///  - Version 17.1, in parallel to changes in TUnfold
+///  - Version 17.0 example for multi-dimensional unfolding
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+// uncomment this to read the binning schemes from the root file
+// by default the binning is read from the XML file
+// #define READ_BINNING_CINT
+
+
+#include <iostream>
+#include <map>
+#include <cmath>
+#include <TMath.h>
+#include <TFile.h>
+#include <TTree.h>
+#include <TH1.h>
+#ifndef READ_BINNING_CINT
+#include <TDOMParser.h>
+#include <TXMLDocument.h>
+#include "TUnfoldBinningXML.h"
+#else
+#include "TUnfoldBinning.h"
+#endif
+
+using namespace std;
+
+
+void testUnfold5c()
+{
+  // switch on histogram errors
+  TH1::SetDefaultSumw2();
+
+  //=======================================================
+  // Step 1: open file to save histograms and binning schemes
+
+  TFile *outputFile=new TFile("testUnfold5_histograms.root","recreate");
+
+  //=======================================================
+  // Step 2: read binning from XML
+  //         and save them to output file
+
+#ifdef READ_BINNING_CINT
+  TFile *binningSchemes=new TFile("testUnfold5_binning.root");
+#endif
+
+  TUnfoldBinning *detectorBinning,*generatorBinning;
+
+  outputFile->cd();
+
+  // read binning schemes in XML format
+#ifndef READ_BINNING_CINT
+  TDOMParser parser;
+  Int_t error=parser.ParseFile("testUnfold5binning.xml");
+  if(error) cout<<"error="<<error<<" from TDOMParser\n";
+  TXMLDocument const *XMLdocument=parser.GetXMLDocument();
+  detectorBinning=
+     TUnfoldBinningXML::ImportXML(XMLdocument,"detector");
+  generatorBinning=
+     TUnfoldBinningXML::ImportXML(XMLdocument,"generator");
+#else
+  binningSchemes->GetObject("detector",detectorBinning);
+  binningSchemes->GetObject("generator",generatorBinning);
+
+  delete binningSchemes;
+#endif
+  detectorBinning->Write();
+  generatorBinning->Write();
+
+  if(detectorBinning) {
+     detectorBinning->PrintStream(cout);
+  } else {
+     cout<<"could not read 'detector' binning\n";
+  }
+  if(generatorBinning) {
+     generatorBinning->PrintStream(cout);
+  } else {
+     cout<<"could not read 'generator' binning\n";
+  }
+
+  // pointers to various nodes in the binning scheme
+  const TUnfoldBinning *detectordistribution=
+     detectorBinning->FindNode("detectordistribution");
+
+  const TUnfoldBinning *signalBinning=
+     generatorBinning->FindNode("signal");
+
+  const TUnfoldBinning *bgrBinning=
+     generatorBinning->FindNode("background");
+
+  // write binning schemes to output file
+
+  //=======================================================
+  // Step 3: book and fill data histograms
+
+  Float_t etaRec,ptRec,discr,etaGen,ptGen;
+  Int_t istriggered,issignal;
+
+  outputFile->cd();
+
+  TH1 *histDataReco=detectorBinning->CreateHistogram("histDataReco");
+  TH1 *histDataTruth=generatorBinning->CreateHistogram("histDataTruth");
+
+  TFile *dataFile=new TFile("testUnfold5_data.root");
+  TTree *dataTree=(TTree *) dataFile->Get("data");
+
+  if(!dataTree) {
+     cout<<"could not read 'data' tree\n";
+  }
+
+  dataTree->ResetBranchAddresses();
+  dataTree->SetBranchAddress("etarec",&etaRec);
+  dataTree->SetBranchAddress("ptrec",&ptRec);
+  dataTree->SetBranchAddress("discr",&discr);
+  // for real data, only the triggered events are available
+  dataTree->SetBranchAddress("istriggered",&istriggered);
+  // data truth parameters
+  dataTree->SetBranchAddress("etagen",&etaGen);
+  dataTree->SetBranchAddress("ptgen",&ptGen);
+  dataTree->SetBranchAddress("issignal",&issignal);
+  dataTree->SetBranchStatus("*",1);
+
+
+  cout<<"loop over data events\n";
+
+  for(Int_t ievent=0;ievent<dataTree->GetEntriesFast();ievent++) {
+     if(dataTree->GetEntry(ievent)<=0) break;
+     // fill histogram with reconstructed quantities
+     if(istriggered) {
+        Int_t binNumber=
+           detectordistribution->GetGlobalBinNumber(ptRec,etaRec,discr);
+        histDataReco->Fill(binNumber);
+     }
+     // fill histogram with data truth parameters
+     if(issignal) {
+        // signal has true eta and pt
+        Int_t binNumber=signalBinning->GetGlobalBinNumber(ptGen,etaGen);
+        histDataTruth->Fill(binNumber);
+     } else {
+        // background only has reconstructed pt and eta
+        Int_t binNumber=bgrBinning->GetGlobalBinNumber(ptRec,etaRec);
+        histDataTruth->Fill(binNumber);
+     }
+  }
+
+  delete dataTree;
+  delete dataFile;
+
+  //=======================================================
+  // Step 4: book and fill histogram of migrations
+  //         it receives events from both signal MC and background MC
+
+  outputFile->cd();
+
+  TH2 *histMCGenRec=TUnfoldBinning::CreateHistogramOfMigrations
+     (generatorBinning,detectorBinning,"histMCGenRec");
+
+  TFile *signalFile=new TFile("testUnfold5_signal.root");
+  TTree *signalTree=(TTree *) signalFile->Get("signal");
+
+  if(!signalTree) {
+     cout<<"could not read 'signal' tree\n";
+  }
+
+  signalTree->ResetBranchAddresses();
+  signalTree->SetBranchAddress("etarec",&etaRec);
+  signalTree->SetBranchAddress("ptrec",&ptRec);
+  signalTree->SetBranchAddress("discr",&discr);
+  signalTree->SetBranchAddress("istriggered",&istriggered);
+  signalTree->SetBranchAddress("etagen",&etaGen);
+  signalTree->SetBranchAddress("ptgen",&ptGen);
+  signalTree->SetBranchStatus("*",1);
+
+  cout<<"loop over MC signal events\n";
+
+  for(Int_t ievent=0;ievent<signalTree->GetEntriesFast();ievent++) {
+     if(signalTree->GetEntry(ievent)<=0) break;
+
+     // bin number on generator level for signal
+     Int_t genBin=signalBinning->GetGlobalBinNumber(ptGen,etaGen);
+
+     // bin number on reconstructed level
+     // bin number 0 corresponds to non-reconstructed events
+     Int_t recBin=0;
+     if(istriggered) {
+        recBin=detectordistribution->GetGlobalBinNumber(ptRec,etaRec,discr);
+     }
+     histMCGenRec->Fill(genBin,recBin);
+  }
+
+  delete signalTree;
+  delete signalFile;
+
+  TFile *bgrFile=new TFile("testUnfold5_background.root");
+  TTree *bgrTree=(TTree *) bgrFile->Get("background");
+
+  if(!bgrTree) {
+     cout<<"could not read 'background' tree\n";
+  }
+
+  bgrTree->ResetBranchAddresses();
+  bgrTree->SetBranchAddress("etarec",&etaRec);
+  bgrTree->SetBranchAddress("ptrec",&ptRec);
+  bgrTree->SetBranchAddress("discr",&discr);
+  bgrTree->SetBranchAddress("istriggered",&istriggered);
+  bgrTree->SetBranchStatus("*",1);
+
+  cout<<"loop over MC background events\n";
+
+  for(Int_t ievent=0;ievent<bgrTree->GetEntriesFast();ievent++) {
+     if(bgrTree->GetEntry(ievent)<=0) break;
+
+     // here, for background only reconstructed quantities are known
+     // and only the reconstructed events are relevant
+     if(istriggered) {
+        // bin number on generator level for background
+        Int_t genBin=bgrBinning->GetGlobalBinNumber(ptRec,etaRec);
+        // bin number on reconstructed level
+        Int_t recBin=detectordistribution->GetGlobalBinNumber
+           (ptRec,etaRec,discr);
+        histMCGenRec->Fill(genBin,recBin);
+     }
+  }
+
+  delete bgrTree;
+  delete bgrFile;
+
+  outputFile->Write();
+  delete outputFile;
+
+}
diff --git a/tutorials/unfold/testUnfold5d.C b/tutorials/unfold/testUnfold5d.C
new file mode 100644
index 00000000000..fd310077708
--- /dev/null
+++ b/tutorials/unfold/testUnfold5d.C
@@ -0,0 +1,296 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Test program for the classes TUnfoldDensity and TUnfoldBinning.
+///
+/// A toy test of the TUnfold package
+///
+/// This is an example of unfolding a two-dimensional distribution
+/// also using an auxiliary measurement to constrain some background
+///
+/// The example comprises several macros
+///   testUnfold5a.C   create root files with TTree objects for
+///                      signal, background and data
+///            -> write files  testUnfold5_signal.root
+///                            testUnfold5_background.root
+///                            testUnfold5_data.root
+///
+///   testUnfold5b.C   create a root file with the TUnfoldBinning objects
+///            -> write file  testUnfold5_binning.root
+///
+///   testUnfold5c.C   loop over trees and fill histograms based on the
+///                      TUnfoldBinning objects
+///            -> read  testUnfold5_binning.root
+///                     testUnfold5_signal.root
+///                     testUnfold5_background.root
+///                     testUnfold5_data.root
+///
+///            -> write testUnfold5_histograms.root
+///
+///   testUnfold5d.C   run the unfolding
+///            -> read  testUnfold5_histograms.root
+///            -> write testUnfold5_result.root
+///                     testUnfold5_result.ps
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+/// #### History:
+///  - Version 17.5, in parallel to changes in TUnfold
+///  - Version 17.4, in parallel to changes in TUnfold
+///  - Version 17.3, in parallel to changes in TUnfold
+///  - Version 17.2, in parallel to changes in TUnfold
+///  - Version 17.1, in parallel to changes in TUnfold
+///  - Version 17.0 example for multi-dimensional unfolding
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+#include <iostream>
+#include <cmath>
+#include <map>
+#include <TMath.h>
+#include <TCanvas.h>
+#include <TStyle.h>
+#include <TGraph.h>
+#include <TFile.h>
+#include <TH1.h>
+#include "TUnfoldDensity.h"
+
+using namespace std;
+
+// #define PRINT_MATRIX_L
+
+#define TEST_INPUT_COVARIANCE
+
+void testUnfold5d()
+{
+  // switch on histogram errors
+  TH1::SetDefaultSumw2();
+
+  //==============================================
+  // step 1 : open output file
+  TFile *outputFile=new TFile("testUnfold5_results.root","recreate");
+
+  //==============================================
+  // step 2 : read binning schemes and input histograms
+  TFile *inputFile=new TFile("testUnfold5_histograms.root");
+
+  outputFile->cd();
+
+  TUnfoldBinning *detectorBinning,*generatorBinning;
+
+  inputFile->GetObject("detector",detectorBinning);
+  inputFile->GetObject("generator",generatorBinning);
+
+  if((!detectorBinning)||(!generatorBinning)) {
+     cout<<"problem to read binning schemes\n";
+  }
+
+  // save binning schemes to output file
+  detectorBinning->Write();
+  generatorBinning->Write();
+
+  // read histograms
+  TH1 *histDataReco,*histDataTruth;
+  TH2 *histMCGenRec;
+
+  inputFile->GetObject("histDataReco",histDataReco);
+  inputFile->GetObject("histDataTruth",histDataTruth);
+  inputFile->GetObject("histMCGenRec",histMCGenRec);
+
+#ifdef TEST_ZERO_UNCORR_ERROR
+  // special test (bug in version 17.2 and below)
+  // set all errors in hisMCGenRec to zero
+  // -> program will crash
+  for(int i=0;i<=histMCGenRec->GetNbinsX()+1;i++) {
+     for(int j=0;j<=histMCGenRec->GetNbinsY()+1;j++) {
+        histMCGenRec->SetBinError(i,j,0.0);
+     }
+  }
+#endif
+
+  histDataReco->Write();
+  histDataTruth->Write();
+  histMCGenRec->Write();
+
+  if((!histDataReco)||(!histDataTruth)||(!histMCGenRec)) {
+     cout<<"problem to read input histograms\n";
+  }
+
+  //========================
+  // Step 3: unfolding
+
+ // preserve the area
+  TUnfold::EConstraint constraintMode= TUnfold::kEConstraintArea;
+
+  // basic choice of regularisation scheme:
+  //    curvature (second derivative)
+  TUnfold::ERegMode regMode = TUnfold::kRegModeCurvature;
+
+  // density flags
+  TUnfoldDensity::EDensityMode densityFlags=
+    TUnfoldDensity::kDensityModeBinWidth;
+
+  // detailed steering for regularisation
+  const char *REGULARISATION_DISTRIBUTION=0;
+  const char *REGULARISATION_AXISSTEERING="*[B]";
+
+  // set up matrix of migrations
+  TUnfoldDensity unfold(histMCGenRec,TUnfold::kHistMapOutputHoriz,
+                        regMode,constraintMode,densityFlags,
+                        generatorBinning,detectorBinning,
+			REGULARISATION_DISTRIBUTION,
+			REGULARISATION_AXISSTEERING);
+
+  // define the input vector (the measured data distribution)
+
+#ifdef TEST_INPUT_COVARIANCE
+  // special test to use input covariance matrix
+  TH2D *inputEmatrix=
+     detectorBinning->CreateErrorMatrixHistogram("input_covar",true);
+  for(int i=1;i<=inputEmatrix->GetNbinsX();i++) {
+     Double_t e=histDataReco->GetBinError(i);
+     inputEmatrix->SetBinContent(i,i,e*e);
+     // test: non-zero covariance where variance is zero
+     //   if(e<=0.) inputEmatrix->SetBinContent(i,i+1,1.0);
+  }
+  unfold.SetInput(histDataReco,0.0,0.0,inputEmatrix);
+#else
+  unfold.SetInput(histDataReco /* ,0.0,1.0 */);
+#endif
+  // print matrix of regularisation conditions
+#ifdef PRINT_MATRIX_L
+  TH2 *histL= unfold.GetL("L");
+  for(Int_t j=1;j<=histL->GetNbinsY();j++) {
+     cout<<"L["<<unfold.GetLBinning()->GetBinName(j)<<"]";
+     for(Int_t i=1;i<=histL->GetNbinsX();i++) {
+        Double_t c=histL->GetBinContent(i,j);
+        if(c!=0.0) cout<<" ["<<i<<"]="<<c;
+     }
+     cout<<"\n";
+  }
+#endif
+  // run the unfolding
+  //
+  // here, tau is determined by scanning the global correlation coefficients
+
+  Int_t nScan=30;
+  TSpline *rhoLogTau=0;
+  TGraph *lCurve=0;
+
+  // for determining tau, scan the correlation coefficients
+  // correlation coefficients may be probed for all distributions
+  // or only for selected distributions
+  // underflow/overflow bins may be included/excluded
+  //
+  const char *SCAN_DISTRIBUTION="signal";
+  const char *SCAN_AXISSTEERING=0;
+
+  Int_t iBest=unfold.ScanTau(nScan,0.,0.,&rhoLogTau,
+                             TUnfoldDensity::kEScanTauRhoMax,
+                             SCAN_DISTRIBUTION,SCAN_AXISSTEERING,
+                             &lCurve);
+
+  // create graphs with one point to visualize best choice of tau
+  Double_t t[1],rho[1],x[1],y[1];
+  rhoLogTau->GetKnot(iBest,t[0],rho[0]);
+  lCurve->GetPoint(iBest,x[0],y[0]);
+  TGraph *bestRhoLogTau=new TGraph(1,t,rho);
+  TGraph *bestLCurve=new TGraph(1,x,y);
+  Double_t *tAll=new Double_t[nScan],*rhoAll=new Double_t[nScan];
+  for(Int_t i=0;i<nScan;i++) {
+     rhoLogTau->GetKnot(i,tAll[i],rhoAll[i]);
+  }
+  TGraph *knots=new TGraph(nScan,tAll,rhoAll);
+
+  cout<<"chi**2="<<unfold.GetChi2A()<<"+"<<unfold.GetChi2L()
+      <<" / "<<unfold.GetNdf()<<"\n";
+
+
+  //===========================
+  // Step 4: retrieve and plot unfolding results
+
+  // get unfolding output
+  TH1 *histDataUnfold=unfold.GetOutput("unfolded signal",0,0,0,kFALSE);
+  // get Monte Carlo reconstructed data
+  TH1 *histMCReco=histMCGenRec->ProjectionY("histMCReco",0,-1,"e");
+  TH1 *histMCTruth=histMCGenRec->ProjectionX("histMCTruth",0,-1,"e");
+  Double_t scaleFactor=histDataTruth->GetSumOfWeights()/
+     histMCTruth->GetSumOfWeights();
+  histMCReco->Scale(scaleFactor);
+  histMCTruth->Scale(scaleFactor);
+  // get matrix of probabilities
+  TH2 *histProbability=unfold.GetProbabilityMatrix("histProbability");
+  // get global correlation coefficients
+  /* TH1 *histGlobalCorr=*/ unfold.GetRhoItotal("histGlobalCorr",0,0,0,kFALSE);
+  TH1 *histGlobalCorrScan=unfold.GetRhoItotal
+     ("histGlobalCorrScan",0,SCAN_DISTRIBUTION,SCAN_AXISSTEERING,kFALSE);
+  /* TH2 *histCorrCoeff=*/ unfold.GetRhoIJtotal("histCorrCoeff",0,0,0,kFALSE);
+
+  TCanvas canvas;
+  canvas.Print("testUnfold5.ps[");
+
+  //========== page 1 ============
+  // unfolding control plots
+  // input, matrix, output
+  // tau-scan, global correlations, correlation coefficients
+  canvas.Clear();
+  canvas.Divide(3,2);
+
+  // (1) all bins, compare to original MC distribution
+  canvas.cd(1);
+  histDataReco->SetMinimum(0.0);
+  histDataReco->Draw("E");
+  histMCReco->SetLineColor(kBlue);
+  histMCReco->Draw("SAME HIST");
+  // (2) matrix of probabilities
+  canvas.cd(2);
+  histProbability->Draw("BOX");
+  // (3) unfolded data, data truth, MC truth
+  canvas.cd(3);
+  gPad->SetLogy();
+  histDataUnfold->Draw("E");
+  histDataTruth->SetLineColor(kBlue);
+  histDataTruth->Draw("SAME HIST");
+  histMCTruth->SetLineColor(kRed);
+  histMCTruth->Draw("SAME HIST");
+  // (4) scan of correlation vs tau
+  canvas.cd(4);
+  rhoLogTau->Draw();
+  knots->Draw("*");
+  bestRhoLogTau->SetMarkerColor(kRed);
+  bestRhoLogTau->Draw("*");
+  // (5) global correlation coefficients for the distributions
+  //     used during the scan
+  canvas.cd(5);
+  //histCorrCoeff->Draw("BOX");
+  histGlobalCorrScan->Draw("HIST");
+  // (6) L-curve
+  canvas.cd(6);
+  lCurve->Draw("AL");
+  bestLCurve->SetMarkerColor(kRed);
+  bestLCurve->Draw("*");
+
+
+  canvas.Print("testUnfold5.ps");
+
+  canvas.Print("testUnfold5.ps]");
+
+}
diff --git a/tutorials/unfold/testUnfold6.C b/tutorials/unfold/testUnfold6.C
new file mode 100644
index 00000000000..455af206d1c
--- /dev/null
+++ b/tutorials/unfold/testUnfold6.C
@@ -0,0 +1,117 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Test program for the class TUnfoldBinning.
+///
+/// read a simple binning scheme and create/test bin maps
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+/// #### History:
+///  - Version 17.5, in parallel to changes in TUnfold
+///  - Version 17.4, in parallel to changes in TUnfold
+///  - Version 17.3, test bin map functionality in TUnfoldBinning
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <map>
+#include <TDOMParser.h>
+#include <TXMLDocument.h>
+#include "TUnfoldBinningXML.h"
+
+using namespace std;
+
+void PrintBinMap(TUnfoldBinning *binning,const char * where,
+                 const Int_t *binMap);
+
+void testUnfold6()
+{
+  TDOMParser parser;
+  ofstream dtdFile("tunfoldbinning.dtd");
+  TUnfoldBinningXML::WriteDTD(dtdFile);
+  dtdFile.close();
+  Int_t error=parser.ParseFile("testUnfold6binning.xml");
+  if(error) cout<<"error="<<error<<" from TDOMParser\n";
+  TXMLDocument const *XMLdocument=parser.GetXMLDocument();
+  TUnfoldBinningXML *binning=
+     TUnfoldBinningXML::ImportXML(XMLdocument,"binning");
+  if(!binning) {
+     cout<<"error: can not read binning (document empty?)\n";
+  } else {
+     cout<<"Binning scheme:\n =================================\n";
+     binning->PrintStream(cout);
+     Int_t *binMap=binning->CreateEmptyBinMap();
+     PrintBinMap(binning,"CreateEmptyBinMap",binMap);
+
+     TUnfoldBinning const *branch1=binning->FindNode("branch1");
+     branch1->FillBinMap1D(binMap,"y[C]",2);
+     PrintBinMap(binning,"branch1->FillBinMap1D(...,\"y[C]\",...,2)",binMap);
+
+     delete binMap; binMap=binning->CreateEmptyBinMap();
+     TUnfoldBinning const *branch2=binning->FindNode("branch2");
+     branch2->FillBinMap1D(binMap,"x[C]",7);
+     PrintBinMap(binning,"branch2->FillBinMap1D(...,\"x[C]\",...,7)",binMap);
+
+     delete binMap; binMap=binning->CreateEmptyBinMap();
+     binning->FillBinMap1D(binMap,"y[C]",1);
+     PrintBinMap(binning,"binning->FillBinMap1D(...,\"y[C]\",...,1)",binMap);
+
+     binning->ExportXML("testUnfold6.out.xml");
+  }
+}
+
+void PrintBinMap(TUnfoldBinning *binning,const char * where,
+                 const Int_t *binMap) {
+
+   cout<<"\n"<<where<<"\n=======================\n";
+   cout<<"global bin:";
+   for(int i=0;i<binning->GetEndBin()+1;i++) {
+      cout<<setw(3)<<i;
+   }
+   cout<<"\n";
+   cout<<"mapped to: ";
+   for(int i=0;i<binning->GetEndBin()+1;i++) {
+      cout<<setw(3)<<binMap[i];
+   }
+   cout<<"\n";
+   map<int,vector<int> > destBin;
+   for(int i=0;i<binning->GetEndBin()+1;i++) {
+      destBin[binMap[i]].push_back(i);
+   }
+   bool printed=false;
+   for(map<int,vector<int> >::const_iterator i=destBin.begin();i!=destBin.end();i++) {
+      if((*i).first>=0) {
+         if(!printed) {
+            cout<<"\ndest |contributing bins\n"
+                <<"=====+======================================\n";
+            printed=true;
+         }
+         for(size_t j=0;j<(*i).second.size();j++) {
+            cout<<setw(4)<<(*i).first<<" |";
+            cout<<setw(3)<<binning->GetBinName((*i).second[j])<<"\n";
+         }
+         cout<<"=====+======================================\n";
+      }
+   }
+}
diff --git a/tutorials/unfold/testUnfold6binning.xml b/tutorials/unfold/testUnfold6binning.xml
new file mode 100644
index 00000000000..fc7cfda6637
--- /dev/null
+++ b/tutorials/unfold/testUnfold6binning.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE TUnfoldBinning SYSTEM "tunfoldbinning.dtd">
+<TUnfoldBinning>
+<BinningNode name="binning" firstbin="1" factor="1">
+ <BinningNode name="branch1" firstbin="1" factor="1">
+  <Axis name="x" lowEdge="0">
+   <Bin repeat="4" width="1" center="0.5" />
+   <Axis name="y" lowEdge="0">
+    <Bin repeat="3" width="1" center="0.5" />
+   </Axis>
+  </Axis>
+ </BinningNode>
+ <BinningNode name="branch2" firstbin="13" factor="1">
+  <Axis name="x" lowEdge="0">
+   <Bin repeat="3" width="1" center="0.5" />
+   <Axis name="y" lowEdge="0">
+    <Bin repeat="2" width="1" center="0.5" />
+   </Axis>
+  </Axis>
+ </BinningNode>
+ <BinningNode name="branch3" firstbin="19" factor="1">
+  <Axis name="x" lowEdge="0">
+   <Bin repeat="2" width="1" center="0.5" />
+  </Axis>
+ </BinningNode>
+</BinningNode>
+</TUnfoldBinning>
diff --git a/tutorials/unfold/testUnfold7a.C b/tutorials/unfold/testUnfold7a.C
new file mode 100644
index 00000000000..3aafe5f06d2
--- /dev/null
+++ b/tutorials/unfold/testUnfold7a.C
@@ -0,0 +1,447 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Test program for the classes TUnfoldDensity and TUnfoldBinning.
+///
+/// A toy test of the TUnfold package
+///
+/// This example is documented in conference proceedings:
+///
+///   arXiv:1611.01927
+///   12th Conference on Quark Confinement and the Hadron Spectrum (Confinement XII)
+///
+/// This is an example of unfolding a two-dimensional distribution
+/// also using an auxiliary measurement to constrain some background
+///
+/// The example comprises several macros
+///  - testUnfold7a.C   create root files with TTree objects for
+///                      signal, background and data
+///            - write files  testUnfold7_signal.root
+///                           testUnfold7_background.root
+///                           testUnfold7_data.root
+///
+///  - testUnfold7b.C   loop over trees and fill histograms based on the
+///                      TUnfoldBinning objects
+///            - read  testUnfold7binning.xml
+///                    testUnfold7_signal.root
+///                    testUnfold7_background.root
+///                    testUnfold7_data.root
+///
+///            - write testUnfold7_histograms.root
+///
+///  - testUnfold7c.C   run the unfolding
+///            - read  testUnfold7_histograms.root
+///            - write testUnfold7_result.root
+///                    testUnfold7_result.ps
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+#include <iostream>
+#include <map>
+#include <cmath>
+#include <TMath.h>
+#include <TRandom3.h>
+#include <TFile.h>
+#include <TTree.h>
+#include <TLorentzVector.h>
+
+#define MASS1 0.511E-3
+
+using namespace std;
+
+TRandom *g_rnd=0;
+
+class ToyEvent7 {
+public:
+   void GenerateDataEvent(TRandom *rnd);
+   void GenerateSignalEvent(TRandom *rnd);
+   void GenerateBgrEvent(TRandom *rnd);
+   // reconstructed quantities
+   inline Double_t GetMRec(int i) const { return fMRec[i]; }
+   inline Double_t GetPtRec(int i) const { return fPtRec[i]; }
+   inline Double_t GetEtaRec(int i) const { return fEtaRec[i]; }
+   inline Double_t GetDiscriminator(void) const {return fDiscriminator; }
+   inline Double_t GetPhiRec(int i) const { return fPhiRec[i]; }
+   inline Bool_t IsTriggered(void) const { return fIsTriggered; }
+
+   // generator level quantities
+   inline Double_t GetMGen(int i) const {
+      if(IsSignal()) return fMGen[i];
+      else return -1.0;
+   }
+   inline Double_t GetPtGen(int i) const {
+      if(IsSignal()) return fPtGen[i];
+      else return -1.0;
+   }
+   inline Double_t GetEtaGen(int i) const {
+       if(IsSignal()) return fEtaGen[i];
+       else return 999.0;
+   }
+   inline Double_t GetPhiGen(int i) const {
+       if(IsSignal()) return fPhiGen[i];
+       else return 999.0;
+   }
+   inline Bool_t IsSignal(void) const { return fIsSignal; }
+protected:
+
+   void GenerateSignalKinematics(TRandom *rnd,Bool_t isData);
+   void GenerateBgrKinematics(TRandom *rnd,Bool_t isData);
+   void GenerateReco(TRandom *rnd);
+
+   // reconstructed quantities
+   Double_t fMRec[3];
+   Double_t fPtRec[3];
+   Double_t fEtaRec[3];
+   Double_t fPhiRec[3];
+   Double_t fDiscriminator;
+   Bool_t fIsTriggered;
+   // generated quantities
+   Double_t fMGen[3];
+   Double_t fPtGen[3];
+   Double_t fEtaGen[3];
+   Double_t fPhiGen[3];
+   Bool_t fIsSignal;
+public:
+   static Double_t kDataSignalFraction;
+   static Double_t kMCSignalFraction;
+
+};
+
+void testUnfold7a()
+{
+  // random generator
+  g_rnd=new TRandom3(4711);
+
+  // data and MC number of events
+  Double_t muData0=5000.;
+  // luminosity error
+  Double_t muData=muData0*g_rnd->Gaus(1.0,0.03);
+  // stat error
+  Int_t neventData      = g_rnd->Poisson( muData);
+
+  // generated number of MC events
+  Int_t neventSigmc     = 250000;
+  Int_t neventBgrmc     = 100000;
+
+  Float_t etaRec[3],ptRec[3],phiRec[3],mRec[3],discr;
+  Float_t etaGen[3],ptGen[3],phiGen[3],mGen[3];
+  Float_t weight;
+  Int_t istriggered,issignal;
+
+  //==================================================================
+  // Step 1: generate data TTree
+
+  TFile *dataFile=new TFile("testUnfold7_data.root","recreate");
+  TTree *dataTree=new TTree("data","event");
+
+  dataTree->Branch("etarec",etaRec,"etarec[3]/F");
+  dataTree->Branch("ptrec",ptRec,"ptrec[3]/F");
+  dataTree->Branch("phirec",phiRec,"phirec[3]/F");
+  dataTree->Branch("mrec",mRec,"mrec[3]/F");
+  dataTree->Branch("discr",&discr,"discr/F");
+
+  // for real data, only the triggered events are available
+  dataTree->Branch("istriggered",&istriggered,"istriggered/I");
+  // data truth parameters
+  dataTree->Branch("etagen",etaGen,"etagen[3]/F");
+  dataTree->Branch("ptgen",ptGen,"ptgen[3]/F");
+  dataTree->Branch("phigen",phiGen,"phigen[3]/F");
+  dataTree->Branch("mgen",mGen,"mgen[3]/F");
+  dataTree->Branch("issignal",&issignal,"issignal/I");
+
+  cout<<"fill data tree\n";
+
+  //Int_t nEvent=0,nTriggered=0;
+  for(int ievent=0;ievent<neventData;ievent++) {
+     ToyEvent7 event;
+     event.GenerateDataEvent(g_rnd);
+     for(int i=0;i<3;i++) {
+        etaRec[i]=event.GetEtaRec(i);
+        ptRec[i]=event.GetPtRec(i);
+        phiRec[i]=event.GetPhiRec(i);
+        mRec[i]=event.GetMRec(i);
+        etaGen[i]=event.GetEtaGen(i);
+        ptGen[i]=event.GetPtGen(i);
+        phiGen[i]=event.GetPhiGen(i);
+        mGen[i]=event.GetMGen(i);
+     }
+     discr=event.GetDiscriminator();
+     istriggered=event.IsTriggered() ? 1 : 0;
+     issignal=event.IsSignal() ? 1 : 0;
+
+     dataTree->Fill();
+
+     if(!(ievent%100000)) cout<<"   data event "<<ievent<<"\n";
+
+     //if(istriggered) nTriggered++;
+     //nEvent++;
+
+  }
+
+  dataTree->Write();
+  delete dataTree;
+  delete dataFile;
+
+  //==================================================================
+  // Step 2: generate signal TTree
+
+  TFile *signalFile=new TFile("testUnfold7_signal.root","recreate");
+  TTree *signalTree=new TTree("signal","event");
+
+  signalTree->Branch("etarec",etaRec,"etarec[3]/F");
+  signalTree->Branch("ptrec",ptRec,"ptrec[3]/F");
+  signalTree->Branch("phirec",ptRec,"phirec[3]/F");
+  signalTree->Branch("mrec",mRec,"mrec[3]/F");
+  signalTree->Branch("discr",&discr,"discr/F");
+
+  // for real data, only the triggered events are available
+  signalTree->Branch("istriggered",&istriggered,"istriggered/I");
+  // data truth parameters
+  signalTree->Branch("etagen",etaGen,"etagen[3]/F");
+  signalTree->Branch("ptgen",ptGen,"ptgen[3]/F");
+  signalTree->Branch("phigen",phiGen,"phigen[3]/F");
+  signalTree->Branch("weight",&weight,"weight/F");
+  signalTree->Branch("mgen",mGen,"mgen[3]/F");
+
+  cout<<"fill signal tree\n";
+
+  weight=ToyEvent7::kMCSignalFraction*muData0/neventSigmc;
+
+  for(int ievent=0;ievent<neventSigmc;ievent++) {
+     ToyEvent7 event;
+     event.GenerateSignalEvent(g_rnd);
+
+     for(int i=0;i<3;i++) {
+        etaRec[i]=event.GetEtaRec(i);
+        ptRec[i]=event.GetPtRec(i);
+        phiRec[i]=event.GetPhiRec(i);
+        mRec[i]=event.GetMRec(i);
+        etaGen[i]=event.GetEtaGen(i);
+        ptGen[i]=event.GetPtGen(i);
+        phiGen[i]=event.GetPhiGen(i);
+        mGen[i]=event.GetMGen(i);
+     }
+     discr=event.GetDiscriminator();
+     istriggered=event.IsTriggered() ? 1 : 0;
+
+     if(!(ievent%100000)) cout<<"   signal event "<<ievent<<"\n";
+
+     signalTree->Fill();
+  }
+
+  signalTree->Write();
+  delete signalTree;
+  delete signalFile;
+
+  // ==============================================================
+  // Step 3: generate background MC TTree
+
+  TFile *bgrFile=new TFile("testUnfold7_background.root","recreate");
+  TTree *bgrTree=new TTree("background","event");
+
+  bgrTree->Branch("etarec",&etaRec,"etarec[3]/F");
+  bgrTree->Branch("ptrec",&ptRec,"ptrec[3]/F");
+  bgrTree->Branch("phirec",&phiRec,"phirec[3]/F");
+  bgrTree->Branch("mrec",&mRec,"mrec[3]/F");
+  bgrTree->Branch("discr",&discr,"discr/F");
+  bgrTree->Branch("istriggered",&istriggered,"istriggered/I");
+  signalTree->Branch("weight",&weight,"weight/F");
+
+  cout<<"fill background tree\n";
+
+  weight=(1.-ToyEvent7::kMCSignalFraction)*muData0/neventBgrmc;
+
+  for(int ievent=0;ievent<neventBgrmc;ievent++) {
+     ToyEvent7 event;
+     event.GenerateBgrEvent(g_rnd);
+     for(int i=0;i<3;i++) {
+        etaRec[i]=event.GetEtaRec(i);
+        ptRec[i]=event.GetPtRec(i);
+        phiRec[i]=event.GetPhiRec(i);
+     }
+     discr=event.GetDiscriminator();
+     istriggered=event.IsTriggered() ? 1 : 0;
+
+     if(!(ievent%100000)) cout<<"   background event "<<ievent<<"\n";
+
+     bgrTree->Fill();
+  }
+
+  bgrTree->Write();
+  delete bgrTree;
+  delete bgrFile;
+}
+
+Double_t ToyEvent7::kDataSignalFraction=0.75;
+Double_t ToyEvent7::kMCSignalFraction=0.75;
+
+void ToyEvent7::GenerateDataEvent(TRandom *rnd) {
+   fIsSignal=rnd->Uniform()<kDataSignalFraction;
+   if(IsSignal()) {
+      GenerateSignalKinematics(rnd,kTRUE);
+   } else {
+      GenerateBgrKinematics(rnd,kTRUE);
+   }
+   GenerateReco(rnd);
+}
+
+void ToyEvent7::GenerateSignalEvent(TRandom *rnd) {
+   fIsSignal=1;
+   GenerateSignalKinematics(rnd,kFALSE);
+   GenerateReco(rnd);
+}
+
+void ToyEvent7::GenerateBgrEvent(TRandom *rnd) {
+   fIsSignal=0;
+   GenerateBgrKinematics(rnd,kFALSE);
+   GenerateReco(rnd);
+}
+
+void ToyEvent7::GenerateSignalKinematics(TRandom *rnd,Bool_t isData) {
+
+   // fake decay of Z0 to two fermions
+   double M0=91.1876;
+   double Gamma=2.4952;
+   // generated mass
+   do {
+      fMGen[2]=rnd->BreitWigner(M0,Gamma);
+   } while(fMGen[2]<=0.0);
+
+   double N_ETA=3.0;
+   double MU_PT=5.;
+   double SIGMA_PT=2.0;
+   double DECAY_A=0.2;
+   if(isData) {
+      //N_ETA=2.5;
+      MU_PT=6.;
+      SIGMA_PT=1.8;
+      //DECAY_A=0.5;
+   }
+   fEtaGen[2]=TMath::Power(rnd->Uniform(0,1.5),N_ETA);
+   if(rnd->Uniform(-1.,1.)<0.) fEtaGen[2] *= -1.;
+   fPhiGen[2]=rnd->Uniform(-M_PI,M_PI);
+   do {
+      fPtGen[2]=rnd->Landau(MU_PT,SIGMA_PT);
+   } while((fPtGen[2]<=0.0)||(fPtGen[2]>500.));
+   //========================== decay
+   TLorentzVector sum;
+   sum.SetPtEtaPhiM(fPtGen[2],fEtaGen[2],fPhiGen[2],fMGen[2]);
+   // boost into lab-frame
+   TVector3 boost=sum.BoostVector();
+   // decay in rest-frame
+
+   TLorentzVector p[3];
+   double m=MASS1;
+   double costh;
+   do {
+      double r=rnd->Uniform(-1.,1.);
+      costh=r*(1.+DECAY_A*r*r);
+   } while(fabs(costh)>=1.0);
+   double phi=rnd->Uniform(-M_PI,M_PI);
+   double e=0.5*sum.M();
+   double ptot=TMath::Sqrt(e+m)*TMath::Sqrt(e-m);
+   double pz=ptot*costh;
+   double pt=TMath::Sqrt(ptot+pz)*TMath::Sqrt(ptot-pz);
+   double px=pt*cos(phi);
+   double py=pt*sin(phi);
+   p[0].SetXYZT(px,py,pz,e);
+   p[1].SetXYZT(-px,-py,-pz,e);
+   for(int i=0;i<2;i++) {
+      p[i].Boost(boost);
+   }
+   p[2]=p[0]+p[1];
+   for(int i=0;i<3;i++) {
+      fPtGen[i]=p[i].Pt();
+      fEtaGen[i]=p[i].Eta();
+      fPhiGen[i]=p[i].Phi();
+      fMGen[i]=p[i].M();
+   }
+}
+
+void ToyEvent7::GenerateBgrKinematics(TRandom *rnd,Bool_t isData) {
+   for(int i=0;i<3;i++) {
+      fPtGen[i]=0.0;
+      fEtaGen[i]=0.0;
+      fPhiGen[i]=0.0;
+   }
+   TLorentzVector p[3];
+   for(int i=0;i<2;i++) {
+      p[i].SetPtEtaPhiM(rnd->Exp(15.0),rnd->Uniform(-3.,3.),
+                        rnd->Uniform(-M_PI,M_PI),isData ? MASS1 : MASS1);
+   }
+   p[2]=p[0]+p[1];
+   for(int i=0;i<3;i++) {
+      fPtRec[i]=p[i].Pt();
+      fEtaRec[i]=p[i].Eta();
+      fPhiRec[i]=p[i].Phi();
+      fMRec[i]=p[i].M();
+   }
+}
+
+void ToyEvent7::GenerateReco(TRandom *rnd) {
+   if(fIsSignal) {
+      TLorentzVector p[3];
+      for(int i=0;i<2;i++) {
+         Double_t expEta=TMath::Exp(fEtaGen[i]);
+         Double_t coshEta=(expEta+1./expEta);
+         Double_t eGen=fPtGen[i]*coshEta;
+         Double_t sigmaE=
+            0.1*TMath::Sqrt(eGen)
+            +1.0*coshEta
+            +0.01*eGen;
+         Double_t eRec;
+         do {
+            eRec=rnd->Gaus(eGen,sigmaE);
+         } while(eRec<=0.0);
+         Double_t sigmaEta=0.1+0.02*TMath::Abs(fEtaGen[i]);
+         p[i].SetPtEtaPhiM(eRec/(expEta+1./expEta),
+                           rnd->Gaus(fEtaGen[i],sigmaEta),
+                           remainder(rnd->Gaus(fPhiGen[i],0.03),2.*M_PI),
+                           MASS1);
+      }
+      p[2]=p[0]+p[1];
+      for(int i=0;i<3;i++) {
+         fPtRec[i]=p[i].Pt();
+         fEtaRec[i]=p[i].Eta();
+         fPhiRec[i]=p[i].Phi();
+         fMRec[i]=p[i].M();
+      }
+   }
+   if(fIsSignal) {
+      do {
+         Double_t tauDiscr=0.08-0.04/(1.+fPtRec[2]/10.0);
+         Double_t sigmaDiscr=0.01;
+         fDiscriminator=1.0-rnd->Exp(tauDiscr)+rnd->Gaus(0.,sigmaDiscr);
+      } while((fDiscriminator<=0.)||(fDiscriminator>=1.));
+   } else {
+      do {
+         Double_t tauDiscr=0.15-0.05/(1.+fPtRec[2]/5.0)+0.1*fEtaRec[2];
+         Double_t sigmaDiscr=0.02+0.01*fEtaRec[2];
+         fDiscriminator=rnd->Exp(tauDiscr)+rnd->Gaus(0.,sigmaDiscr);
+      } while((fDiscriminator<=0.)||(fDiscriminator>=1.));
+   }
+   fIsTriggered=false;
+   for(int i=0;i<2;i++) {
+      if(rnd->Uniform()<0.92/(TMath::Exp(-(fPtRec[i]-15.5)/2.5)+1.)) fIsTriggered=true;
+   }
+}
diff --git a/tutorials/unfold/testUnfold7b.C b/tutorials/unfold/testUnfold7b.C
new file mode 100644
index 00000000000..00ea583fc16
--- /dev/null
+++ b/tutorials/unfold/testUnfold7b.C
@@ -0,0 +1,302 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Test program for the classes TUnfoldDensity and TUnfoldBinning
+///
+/// A toy test of the TUnfold package
+///
+/// This example is documented in conference proceedings:
+///
+///   arXiv:1611.01927
+/// 12th Conference on Quark Confinement and the Hadron Spectrum (Confinement XII)
+///
+/// This is an example of unfolding a two-dimensional distribution
+/// also using an auxiliary measurement to constrain some background
+///
+/// The example comprises several macros
+///  - testUnfold7a.C   create root files with TTree objects for
+///                      signal, background and data
+///            - write files  testUnfold7_signal.root
+///                           testUnfold7_background.root
+///                           testUnfold7_data.root
+///
+///  - testUnfold7b.C   loop over trees and fill histograms based on the
+///                      TUnfoldBinning objects
+///            - read  testUnfold7binning.xml
+///                    testUnfold7_signal.root
+///                    testUnfold7_background.root
+///                    testUnfold7_data.root
+///
+///            - write testUnfold7_histograms.root
+///
+///  - testUnfold7c.C   run the unfolding
+///            - read  testUnfold7_histograms.root
+///            - write testUnfold7_result.root
+///                    testUnfold7_result.ps
+///
+/// \macro_output
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+
+/* below is the content of the file testUnfold7binning.xml,
+   which is required as input to run this example.
+
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE TUnfoldBinning SYSTEM "tunfoldbinning.dtd">
+<TUnfoldBinning>
+<BinningNode name="fine" firstbin="1" factor="1">
+  <Axis name="pt" lowEdge="0.">
+   <Bin repeat="20" width="1."/>
+   <Bin repeat="12" width="2.5"/>
+   <Bin location="overflow" width="10"/>
+  </Axis>
+</BinningNode>
+<BinningNode name="coarse" firstbin="1" factor="1">
+  <Axis name="pt" lowEdge="0.">
+   <Bin repeat="10" width="2"/>
+   <Bin repeat="6" width="5"/>
+   <Bin location="overflow" width="10"/>
+  </Axis>
+</BinningNode>
+</TUnfoldBinning>
+
+ */
+
+#include <iostream>
+#include <map>
+#include <cmath>
+#include <TMath.h>
+#include <TFile.h>
+#include <TTree.h>
+#include <TH1.h>
+#include <TDOMParser.h>
+#include <TXMLDocument.h>
+#include "TUnfoldBinningXML.h"
+
+using namespace std;
+
+
+void testUnfold7b()
+{
+  // switch on histogram errors
+  TH1::SetDefaultSumw2();
+
+  //=======================================================
+  // Step 1: open file to save histograms and binning schemes
+
+  TFile *outputFile=new TFile("testUnfold7_histograms.root","recreate");
+
+  //=======================================================
+  // Step 2: read binning from XML
+  //         and save them to output file
+
+  TUnfoldBinning *fineBinningRoot,*coarseBinningRoot;
+
+  outputFile->cd();
+
+  // read binning schemes in XML format
+
+  TDOMParser parser;
+  Int_t error=parser.ParseFile("testUnfold7binning.xml");
+  if(error) {
+     cout<<"error="<<error<<" from TDOMParser\n";
+     cout<<"==============================================================\n";
+     cout<<"Maybe the file testUnfold7binning.xml is missing?\n";
+     cout<<"The content of the file is included in the comments section\n";
+     cout<<"of this macro \"testUnfold7b.C\"\n";
+     cout<<"==============================================================\n";
+  }
+  TXMLDocument const *XMLdocument=parser.GetXMLDocument();
+  fineBinningRoot=
+     TUnfoldBinningXML::ImportXML(XMLdocument,"fine");
+  coarseBinningRoot=
+     TUnfoldBinningXML::ImportXML(XMLdocument,"coarse");
+
+  // write binning schemes to output file
+  fineBinningRoot->Write();
+  coarseBinningRoot->Write();
+
+  if(fineBinningRoot) {
+     fineBinningRoot->PrintStream(cout);
+  } else {
+     cout<<"could not read 'detector' binning\n";
+  }
+  if(coarseBinningRoot) {
+     coarseBinningRoot->PrintStream(cout);
+  } else {
+     cout<<"could not read 'generator' binning\n";
+  }
+
+  TUnfoldBinning const *fineBinning=fineBinningRoot;//->FindNode("ptfine");
+  TUnfoldBinning const *coarseBinning=coarseBinningRoot;//->FindNode("ptcoarse");
+
+
+  //=======================================================
+  // Step 3: book and fill data histograms
+
+  Float_t ptRec[3],ptGen[3],weight;
+  Int_t isTriggered,isSignal;
+
+  outputFile->cd();
+
+  TH1 *histDataRecF=fineBinning->CreateHistogram("histDataRecF");
+  TH1 *histDataRecC=coarseBinning->CreateHistogram("histDataRecC");
+  TH1 *histDataBgrF=fineBinning->CreateHistogram("histDataBgrF");
+  TH1 *histDataBgrC=coarseBinning->CreateHistogram("histDataBgrC");
+  TH1 *histDataGen=coarseBinning->CreateHistogram("histDataGen");
+
+  TFile *dataFile=new TFile("testUnfold7_data.root");
+  TTree *dataTree=(TTree *) dataFile->Get("data");
+
+  if(!dataTree) {
+     cout<<"could not read 'data' tree\n";
+  }
+
+  dataTree->ResetBranchAddresses();
+  dataTree->SetBranchAddress("ptrec",ptRec);
+  //dataTree->SetBranchAddress("discr",&discr);
+  // for real data, only the triggered events are available
+  dataTree->SetBranchAddress("istriggered",&isTriggered);
+  // data truth parameters
+  dataTree->SetBranchAddress("ptgen",ptGen);
+  dataTree->SetBranchAddress("issignal",&isSignal);
+  dataTree->SetBranchStatus("*",1);
+
+  cout<<"loop over data events\n";
+
+#define VAR_REC (ptRec[2])
+#define VAR_GEN (ptGen[2])
+
+  for(Int_t ievent=0;ievent<dataTree->GetEntriesFast();ievent++) {
+     if(dataTree->GetEntry(ievent)<=0) break;
+     // fill histogram with reconstructed quantities
+     if(isTriggered) {
+        int binF=fineBinning->GetGlobalBinNumber(VAR_REC);
+        int binC=coarseBinning->GetGlobalBinNumber(VAR_REC);
+        histDataRecF->Fill(binF);
+        histDataRecC->Fill(binC);
+        if(!isSignal) {
+           histDataBgrF->Fill(binF);
+           histDataBgrC->Fill(binC);
+        }
+     }
+     // fill histogram with data truth parameters
+     if(isSignal) {
+        int binGen=coarseBinning->GetGlobalBinNumber(VAR_GEN);
+        histDataGen->Fill(binGen);
+     }
+  }
+
+  delete dataTree;
+  delete dataFile;
+
+  //=======================================================
+  // Step 4: book and fill histogram of migrations
+  //         it receives events from both signal MC and background MC
+
+  outputFile->cd();
+
+  TH2 *histMcsigGenRecF=TUnfoldBinning::CreateHistogramOfMigrations
+     (coarseBinning,fineBinning,"histMcsigGenRecF");
+  TH2 *histMcsigGenRecC=TUnfoldBinning::CreateHistogramOfMigrations
+     (coarseBinning,coarseBinning,"histMcsigGenRecC");
+  TH1 *histMcsigRecF=fineBinning->CreateHistogram("histMcsigRecF");
+  TH1 *histMcsigRecC=coarseBinning->CreateHistogram("histMcsigRecC");
+  TH1 *histMcsigGen=coarseBinning->CreateHistogram("histMcsigGen");
+
+  TFile *signalFile=new TFile("testUnfold7_signal.root");
+  TTree *signalTree=(TTree *) signalFile->Get("signal");
+
+  if(!signalTree) {
+     cout<<"could not read 'signal' tree\n";
+  }
+
+  signalTree->ResetBranchAddresses();
+  signalTree->SetBranchAddress("ptrec",&ptRec);
+  //signalTree->SetBranchAddress("discr",&discr);
+  signalTree->SetBranchAddress("istriggered",&isTriggered);
+  signalTree->SetBranchAddress("ptgen",&ptGen);
+  signalTree->SetBranchAddress("weight",&weight);
+  signalTree->SetBranchStatus("*",1);
+
+  cout<<"loop over MC signal events\n";
+
+  for(Int_t ievent=0;ievent<signalTree->GetEntriesFast();ievent++) {
+     if(signalTree->GetEntry(ievent)<=0) break;
+
+     int binC=0,binF=0;
+     if(isTriggered) {
+        binF=fineBinning->GetGlobalBinNumber(VAR_REC);
+        binC=coarseBinning->GetGlobalBinNumber(VAR_REC);
+     }
+     int binGen=coarseBinning->GetGlobalBinNumber(VAR_GEN);
+     histMcsigGenRecF->Fill(binGen,binF,weight);
+     histMcsigGenRecC->Fill(binGen,binC,weight);
+     histMcsigRecF->Fill(binF,weight);
+     histMcsigRecC->Fill(binC,weight);
+     histMcsigGen->Fill(binGen,weight);
+  }
+
+  delete signalTree;
+  delete signalFile;
+
+  outputFile->cd();
+
+  TH1 *histMcbgrRecF=fineBinning->CreateHistogram("histMcbgrRecF");
+  TH1 *histMcbgrRecC=coarseBinning->CreateHistogram("histMcbgrRecC");
+
+  TFile *bgrFile=new TFile("testUnfold7_background.root");
+  TTree *bgrTree=(TTree *) bgrFile->Get("background");
+
+  if(!bgrTree) {
+     cout<<"could not read 'background' tree\n";
+  }
+
+  bgrTree->ResetBranchAddresses();
+  bgrTree->SetBranchAddress("ptrec",&ptRec);
+  //bgrTree->SetBranchAddress("discr",&discr);
+  bgrTree->SetBranchAddress("istriggered",&isTriggered);
+  bgrTree->SetBranchAddress("weight",&weight);
+  bgrTree->SetBranchStatus("*",1);
+
+  cout<<"loop over MC background events\n";
+
+  for(Int_t ievent=0;ievent<bgrTree->GetEntriesFast();ievent++) {
+     if(bgrTree->GetEntry(ievent)<=0) break;
+
+     // here, for background only reconstructed quantities are known
+     // and only the reconstructed events are relevant
+     if(isTriggered) {
+        int binF=fineBinning->GetGlobalBinNumber(VAR_REC);
+        int binC=coarseBinning->GetGlobalBinNumber(VAR_REC);
+        histMcbgrRecF->Fill(binF,weight);
+        histMcbgrRecC->Fill(binC,weight);
+     }
+  }
+
+  delete bgrTree;
+  delete bgrFile;
+
+  outputFile->Write();
+  delete outputFile;
+
+}
diff --git a/tutorials/unfold/testUnfold7binning.xml b/tutorials/unfold/testUnfold7binning.xml
new file mode 100644
index 00000000000..635179045b8
--- /dev/null
+++ b/tutorials/unfold/testUnfold7binning.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE TUnfoldBinning SYSTEM "tunfoldbinning.dtd">
+<TUnfoldBinning>
+<BinningNode name="fine" firstbin="1" factor="1">
+  <Axis name="pt" lowEdge="0.">
+   <Bin repeat="20" width="1."/>
+   <Bin repeat="12" width="2.5"/>
+   <Bin location="overflow" width="10"/>
+  </Axis>
+</BinningNode>
+<BinningNode name="coarse" firstbin="1" factor="1">
+  <Axis name="pt" lowEdge="0.">
+   <Bin repeat="10" width="2"/>
+   <Bin repeat="6" width="5"/>
+   <Bin location="overflow" width="10"/>
+  </Axis>
+</BinningNode>
+</TUnfoldBinning>
diff --git a/tutorials/unfold/testUnfold7c.C b/tutorials/unfold/testUnfold7c.C
new file mode 100644
index 00000000000..e889e99f324
--- /dev/null
+++ b/tutorials/unfold/testUnfold7c.C
@@ -0,0 +1,1655 @@
+/// \file
+/// \ingroup tutorial_unfold
+/// \notebook
+/// Test program for the classes TUnfoldDensity and TUnfoldBinning.
+///
+/// A toy test of the TUnfold package
+///
+///
+/// This example is documented in conference proceedings:
+///
+///   arXiv:1611.01927
+///   12th Conference on Quark Confinement and the Hadron Spectrum (Confinement XII)
+///
+/// This is an example of unfolding a one-dimensional distribution. It compares
+/// various unfolding methods:
+///
+///           matrix inversion, template fit, L-curve scan,
+///           iterative unfolding, etc
+///
+/// Further details can  be found in talk by S.Schmitt at:
+///
+///   XII Quark Confinement and the Hadron Spectrum
+///   29.8. - 3.9.2016	Thessaloniki, Greece
+///   statictics session (+proceedings)
+///
+/// The example comprises several macros
+///  - testUnfold7a.C   create root files with TTree objects for
+///                     signal, background and data
+///            - write files  testUnfold7_signal.root
+///                           testUnfold7_background.root
+///                           testUnfold7_data.root
+///
+///  - testUnfold7b.C   loop over trees and fill histograms based on the
+///                     TUnfoldBinning objects
+///            - read  testUnfold7binning.xml
+///                    testUnfold7_signal.root
+///                    testUnfold7_background.root
+///                    testUnfold7_data.root
+///
+///            - write testUnfold7_histograms.root
+///
+///  - testUnfold7c.C   run the unfolding
+///            - read  testUnfold7_histograms.root
+///            - write testUnfold7_result.root
+///            - write many histograms, to compare various unfolding methods
+///
+/// \macro_output
+/// \macro_image
+/// \macro_code
+///
+///  **Version 17.6, in parallel to changes in TUnfold**
+///
+///  This file is part of TUnfold.
+///
+///  TUnfold is free software: you can redistribute it and/or modify
+///  it under the terms of the GNU General Public License as published by
+///  the Free Software Foundation, either version 3 of the License, or
+///  (at your option) any later version.
+///
+///  TUnfold is distributed in the hope that it will be useful,
+///  but WITHOUT ANY WARRANTY; without even the implied warranty of
+///  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///  GNU General Public License for more details.
+///
+///  You should have received a copy of the GNU General Public License
+///  along with TUnfold.  If not, see <http://www.gnu.org/licenses/>.
+///
+/// \author Stefan Schmitt DESY, 14.10.2008
+
+#include <iostream>
+#include <cmath>
+#include <map>
+#include <TMath.h>
+#include <TCanvas.h>
+#include <TStyle.h>
+#include <TGraph.h>
+#include <TGraphErrors.h>
+#include <TFile.h>
+#include <TROOT.h>
+#include <TText.h>
+#include <TLine.h>
+#include <TLegend.h>
+#include <TH1.h>
+#include <TF1.h>
+#include <TFitter.h>
+#include <TMatrixD.h>
+#include <TMatrixDSym.h>
+#include <TVectorD.h>
+#include <TMatrixDSymEigen.h>
+#include <TFitResult.h>
+#include <TRandom3.h>
+#include "TUnfoldDensity.h"
+
+using namespace std;
+
+// #define PRINT_MATRIX_L
+
+#define TEST_INPUT_COVARIANCE
+
+void CreateHistogramCopies(TH1 *h[3],TUnfoldBinning const *binning);
+void CreateHistogramCopies(TH2 *h[3],TUnfoldBinning const *binningX);
+
+TH2 *AddOverflowXY(TH2 *h,double widthX,double widthY);
+TH1 *AddOverflowX(TH1 *h,double width);
+
+void DrawOverflowX(TH1 *h,double posy);
+void DrawOverflowY(TH1 *h,double posx);
+
+
+double const kLegendFontSize=0.05;
+int kNbinC=0;
+
+void DrawPadProbability(TH2 *h);
+void DrawPadEfficiency(TH1 *h);
+void DrawPadReco(TH1 *histMcRec,TH1 *histMcbgrRec,TH1 *histDataRec,
+                 TH1 *histDataUnfold,TH2 *histProbability,TH2 *histRhoij);
+void DrawPadTruth(TH1 *histMcsigGen,TH1 *histDataGen,TH1 *histDataUnfold,
+                  char const *text=0,double tau=0.0,vector<double> const *r=0,
+                  TF1 *f=0);
+void DrawPadCorrelations(TH2 *h,
+                         vector<pair<TF1*,vector<double> > > const *table);
+
+TFitResultPtr DoFit(TH1 *h,TH2 *rho,TH1 *truth,char const *text,
+                    vector<pair<TF1*,vector<double> > > &table,int niter=0);
+
+void GetNiterGraphs(int iFirst,int iLast,vector<pair<TF1*,
+                    vector<double> > > const &table,int color,
+                    TGraph *graph[4],int style);
+void GetNiterHist(int ifit,vector<pair<TF1*,vector<double> > > const &table,
+                  TH1 *hist[4],int color,int style,int fillStyle);
+
+#ifdef WITH_IDS
+void IDSfirst(TVectorD *data, TVectorD *dataErr, TMatrixD *A_, Double_t lambdaL_, TVectorD* &unfres1IDS_,TVectorD *&soustr);
+
+void IDSiterate(TVectorD *data, TVectorD *dataErr, TMatrixD *A_,TMatrixD *Am_,
+                Double_t lambdaU_, Double_t lambdaM_, Double_t lambdaS_,
+                TVectorD* &unfres2IDS_ ,TVectorD *&soustr);
+#endif
+
+TRandom3 *g_rnd;
+
+void testUnfold7c()
+{
+   gErrorIgnoreLevel=kInfo;
+  // switch on histogram errors
+  TH1::SetDefaultSumw2();
+
+  gStyle->SetOptStat(0);
+
+  g_rnd=new TRandom3(4711);
+
+  //==============================================
+  // step 1 : open output file
+  TFile *outputFile=new TFile("testUnfold7_results.root","recreate");
+
+  //==============================================
+  // step 2 : read binning schemes and input histograms
+  TFile *inputFile=new TFile("testUnfold7_histograms.root");
+
+  outputFile->cd();
+
+  TUnfoldBinning *fineBinning,*coarseBinning;
+
+  inputFile->GetObject("fine",fineBinning);
+  inputFile->GetObject("coarse",coarseBinning);
+
+  if((!fineBinning)||(!coarseBinning)) {
+     cout<<"problem to read binning schemes\n";
+  }
+
+  // save binning schemes to output file
+  fineBinning->Write();
+  coarseBinning->Write();
+
+  // read histograms
+#define READ(TYPE,binning,name)                       \
+  TYPE *name[3]; inputFile->GetObject(#name,name[0]); \
+  name[0]->Write();                                   \
+  if(!name[0]) cout<<"Error reading " #name "\n";     \
+  CreateHistogramCopies(name,binning);
+
+  outputFile->cd();
+
+  READ(TH1,fineBinning,histDataRecF);
+  READ(TH1,coarseBinning,histDataRecC);
+  READ(TH1,fineBinning,histDataBgrF);
+  READ(TH1,coarseBinning,histDataBgrC);
+  READ(TH1,coarseBinning,histDataGen);
+
+  READ(TH2,fineBinning,histMcsigGenRecF);
+  READ(TH2,coarseBinning,histMcsigGenRecC);
+  READ(TH1,fineBinning,histMcsigRecF);
+  READ(TH1,coarseBinning,histMcsigRecC);
+  READ(TH1,coarseBinning,histMcsigGen);
+
+  READ(TH1,fineBinning,histMcbgrRecF);
+  READ(TH1,coarseBinning,histMcbgrRecC);
+
+  TH1 *histOutputCtau0[3];
+  TH2 *histRhoCtau0;
+  TH1 *histOutputCLCurve[3];
+  //TH2 *histRhoCLCurve;
+  TH2 *histProbC;
+  double tauMin=1.e-4;
+  double tauMax=1.e-1;
+  double fBgr=1.0; // 0.2/0.25;
+  double biasScale=1.0;
+  TUnfold::ERegMode mode=TUnfold::kRegModeSize; //Derivative;
+
+  //double tauC;
+  {
+  TUnfoldDensity *tunfoldC=
+     new TUnfoldDensity(histMcsigGenRecC[0],
+                        TUnfold::kHistMapOutputHoriz,
+                        mode,
+                        TUnfold::kEConstraintNone,//Area,
+                        TUnfoldDensity::kDensityModeNone,
+                        coarseBinning,
+                        coarseBinning);
+  tunfoldC->SetInput(histDataRecC[0],biasScale);
+  tunfoldC->SubtractBackground(histMcbgrRecC[0],"BGR",fBgr,0.0);
+  tunfoldC->DoUnfold(0.);
+  histOutputCtau0[0]=tunfoldC->GetOutput("histOutputCtau0");
+  histRhoCtau0=tunfoldC->GetRhoIJtotal("histRhoCtau0");
+  CreateHistogramCopies(histOutputCtau0,coarseBinning);
+  tunfoldC->ScanLcurve(50,tauMin,tauMax,0);
+  /* tauC= */tunfoldC->GetTau();
+  //tunfoldC->ScanTau(50,1.E-7,1.E-1,0,TUnfoldDensity::kEScanTauRhoAvg);
+  histOutputCLCurve[0]=tunfoldC->GetOutput("histOutputCLCurve");
+  /* histRhoCLCurve= */tunfoldC->GetRhoIJtotal("histRhoCLCurve");
+  CreateHistogramCopies(histOutputCLCurve,coarseBinning);
+  histProbC=tunfoldC->GetProbabilityMatrix("histProbC",";P_T(gen);P_T(rec)");
+  }
+  TH1 *histOutputFtau0[3];
+  TH2 *histRhoFtau0;
+  TH1 *histOutputFLCurve[3];
+  //TH2 *histRhoFLCurve;
+  TH2 *histProbF;
+  TGraph *lCurve;
+  TSpline *logTauX,*logTauY;
+  tauMin=3.E-4;
+  tauMax=3.E-2;
+  //double tauF;
+  {
+  TUnfoldDensity *tunfoldF=
+     new TUnfoldDensity(histMcsigGenRecF[0],
+                        TUnfold::kHistMapOutputHoriz,
+                        mode,
+                        TUnfold::kEConstraintNone,//Area,
+                        TUnfoldDensity::kDensityModeNone,
+                        coarseBinning,
+                        fineBinning);
+  tunfoldF->SetInput(histDataRecF[0],biasScale);
+  tunfoldF->SubtractBackground(histMcbgrRecF[0],"BGR",fBgr,0.0);
+  tunfoldF->DoUnfold(0.);
+  histOutputFtau0[0]=tunfoldF->GetOutput("histOutputFtau0");
+  histRhoFtau0=tunfoldF->GetRhoIJtotal("histRhoFtau0");
+  CreateHistogramCopies(histOutputFtau0,coarseBinning);
+  tunfoldF->ScanLcurve(50,tauMin,tauMax,0);
+  //tunfoldF->DoUnfold(tauC);
+  /* tauF= */tunfoldF->GetTau();
+  //tunfoldF->ScanTau(50,1.E-7,1.E-1,0,TUnfoldDensity::kEScanTauRhoAvg);
+  histOutputFLCurve[0]=tunfoldF->GetOutput("histOutputFLCurve");
+  /* histRhoFLCurve= */tunfoldF->GetRhoIJtotal("histRhoFLCurve");
+  CreateHistogramCopies(histOutputFLCurve,coarseBinning);
+  histProbF=tunfoldF->GetProbabilityMatrix("histProbF",";P_T(gen);P_T(rec)");
+  }
+  TH1 *histOutputFAtau0[3];
+  TH2 *histRhoFAtau0;
+  TH1 *histOutputFALCurve[3];
+  TH2 *histRhoFALCurve;
+  TH1 *histOutputFArho[3];
+  TH2 *histRhoFArho;
+  TSpline *rhoScan=0;
+  TSpline *logTauCurvature=0;
+
+  double tauFA,tauFArho;
+  {
+  TUnfoldDensity *tunfoldFA=
+     new TUnfoldDensity(histMcsigGenRecF[0],
+                        TUnfold::kHistMapOutputHoriz,
+                        mode,
+                        TUnfold::kEConstraintArea,
+                        TUnfoldDensity::kDensityModeNone,
+                        coarseBinning,
+                        fineBinning);
+  tunfoldFA->SetInput(histDataRecF[0],biasScale);
+  tunfoldFA->SubtractBackground(histMcbgrRecF[0],"BGR",fBgr,0.0);
+  tunfoldFA->DoUnfold(0.);
+  histOutputFAtau0[0]=tunfoldFA->GetOutput("histOutputFAtau0");
+  histRhoFAtau0=tunfoldFA->GetRhoIJtotal("histRhoFAtau0");
+  CreateHistogramCopies(histOutputFAtau0,coarseBinning);
+  tunfoldFA->ScanTau(50,tauMin,tauMax,&rhoScan,TUnfoldDensity::kEScanTauRhoAvg);
+  tauFArho=tunfoldFA->GetTau();
+  histOutputFArho[0]=tunfoldFA->GetOutput("histOutputFArho");
+  histRhoFArho=tunfoldFA->GetRhoIJtotal("histRhoFArho");
+  CreateHistogramCopies(histOutputFArho,coarseBinning);
+
+  tunfoldFA->ScanLcurve(50,tauMin,tauMax,&lCurve,&logTauX,&logTauY,&logTauCurvature);
+  tauFA=tunfoldFA->GetTau();
+  histOutputFALCurve[0]=tunfoldFA->GetOutput("histOutputFALCurve");
+  histRhoFALCurve=tunfoldFA->GetRhoIJtotal("histRhoFALCurve");
+  CreateHistogramCopies(histOutputFALCurve,coarseBinning);
+  }
+  lCurve->Write();
+  logTauX->Write();
+  logTauY->Write();
+
+
+  double widthC=coarseBinning->GetBinSize(histProbC->GetNbinsY()+1);
+  double widthF=fineBinning->GetBinSize(histProbF->GetNbinsY()+1);
+
+  TH2 *histProbCO=AddOverflowXY(histProbC,widthC,widthC);
+  TH2 *histProbFO=AddOverflowXY(histProbF,widthC,widthF);
+
+  // efficiency
+  TH1 *histEfficiencyC=histProbCO->ProjectionX("histEfficiencyC");
+  kNbinC=histProbCO->GetNbinsX();
+
+  // reconstructed quantities with overflow (coarse binning)
+  // MC: add signal and bgr
+  TH1 *histMcsigRecCO=AddOverflowX(histMcsigRecC[2],widthC);
+  TH1 *histMcbgrRecCO=AddOverflowX(histMcbgrRecC[2],widthC);
+  histMcbgrRecCO->Scale(fBgr);
+  TH1 *histMcRecCO=(TH1 *)histMcsigRecCO->Clone("histMcRecC0");
+  histMcRecCO->Add(histMcsigRecCO,histMcbgrRecCO);
+  TH1 *histDataRecCO=AddOverflowX(histDataRecC[2],widthC);
+  //TH1 *histDataRecCNO=AddOverflowX(histDataRecC[1],widthC);
+
+  TH1 *histMcsigRecFO=AddOverflowX(histMcsigRecF[2],widthF);
+  TH1 *histMcbgrRecFO=AddOverflowX(histMcbgrRecF[2],widthF);
+  histMcbgrRecFO->Scale(fBgr);
+  TH1 *histMcRecFO=(TH1 *)histMcsigRecFO->Clone("histMcRecF0");
+  histMcRecFO->Add(histMcsigRecFO,histMcbgrRecFO);
+  TH1 *histDataRecFO=AddOverflowX(histDataRecF[2],widthF);
+
+  // truth level with overflow
+  TH1 *histMcsigGenO=AddOverflowX(histMcsigGen[2],widthC);
+  TH1 *histDataGenO=AddOverflowX(histDataGen[2],widthC);
+
+  // unfolding result with overflow
+  TH1 *histOutputCtau0O=AddOverflowX(histOutputCtau0[2],widthC);
+  TH2 *histRhoCtau0O=AddOverflowXY(histRhoCtau0,widthC,widthC);
+  //TH1 *histOutputCLCurveO=AddOverflowX(histOutputCLCurve[2],widthC);
+  //TH2 *histRhoCLCurveO=AddOverflowXY(histRhoCLCurve,widthC,widthC);
+  TH1 *histOutputFtau0O=AddOverflowX(histOutputFtau0[2],widthC);
+  TH2 *histRhoFtau0O=AddOverflowXY(histRhoFtau0,widthC,widthC);
+  TH1 *histOutputFAtau0O=AddOverflowX(histOutputFAtau0[2],widthC);
+  TH2 *histRhoFAtau0O=AddOverflowXY(histRhoFAtau0,widthC,widthC);
+  //TH1 *histOutputFLCurveO=AddOverflowX(histOutputFLCurve[2],widthC);
+  //TH2 *histRhoFLCurveO=AddOverflowXY(histRhoFLCurve,widthC,widthC);
+  TH1 *histOutputFALCurveO=AddOverflowX(histOutputFALCurve[2],widthC);
+  TH2 *histRhoFALCurveO=AddOverflowXY(histRhoFALCurve,widthC,widthC);
+  TH1 *histOutputFArhoO=AddOverflowX(histOutputFArho[2],widthC);
+  TH2 *histRhoFArhoO=AddOverflowXY(histRhoFArho,widthC,widthC);
+
+  // bin-by-bin
+  TH2 *histRhoBBBO=(TH2 *)histRhoCtau0O->Clone("histRhoBBBO");
+  for(int i=1;i<=histRhoBBBO->GetNbinsX();i++) {
+     for(int j=1;j<=histRhoBBBO->GetNbinsX();j++) {
+        histRhoBBBO->SetBinContent(i,j,(i==j)?1.:0.);
+     }
+  }
+  TH1 *histDataBgrsub=(TH1 *)histDataRecCO->Clone("histDataBgrsub");
+  histDataBgrsub->Add(histMcbgrRecCO,-fBgr);
+  TH1 *histOutputBBBO=(TH1 *)histDataBgrsub->Clone("histOutputBBBO");
+  histOutputBBBO->Divide(histMcsigRecCO);
+  histOutputBBBO->Multiply(histMcsigGenO);
+
+  // iterative
+  int niter=1000;
+  cout<<"maximum number of iterations: "<<niter<<"\n";
+
+  vector <TH1 *>histOutputAgo,histOutputAgorep;
+  vector <TH2 *>histRhoAgo,histRhoAgorep;
+  vector<int> nIter;
+  histOutputAgo.push_back((TH1*)histMcsigGenO->Clone("histOutputAgo-1"));
+  histOutputAgorep.push_back((TH1*)histMcsigGenO->Clone("histOutputAgorep-1"));
+  histRhoAgo.push_back((TH2*)histRhoBBBO->Clone("histRhoAgo-1"));
+  histRhoAgorep.push_back((TH2*)histRhoBBBO->Clone("histRhoAgorep-1"));
+  nIter.push_back(-1);
+
+  int nx=histProbCO->GetNbinsX();
+  int ny=histProbCO->GetNbinsY();
+  TMatrixD covAgo(nx+ny,nx+ny);
+  TMatrixD A(ny,nx);
+  TMatrixD AToverEps(nx,ny);
+  for(int i=0;i<nx;i++) {
+     double epsilonI=0.;
+     for(int j=0;j<ny;j++) {
+        epsilonI+= histProbCO->GetBinContent(i+1,j+1);
+     }
+     for(int j=0;j<ny;j++) {
+        double aji=histProbCO->GetBinContent(i+1,j+1);
+        A(j,i)=aji;
+        AToverEps(i,j)=aji/epsilonI;
+     }
+  }
+  for(int i=0;i<nx;i++) {
+     covAgo(i,i)=TMath::Power
+        (histOutputAgo[0]->GetBinError(i+1)
+         *histOutputAgo[0]->GetXaxis()->GetBinWidth(i+1),2.);
+  }
+  for(int i=0;i<ny;i++) {
+     covAgo(i+nx,i+nx)=TMath::Power
+        (histDataRecCO->GetBinError(i+1)
+         *histDataRecCO->GetXaxis()->GetBinWidth(i+1),2.);
+  }
+#define NREPLICA 300
+  vector<TVectorD *> y(NREPLICA);
+  vector<TVectorD *> yMb(NREPLICA);
+  vector<TVectorD *> yErr(NREPLICA);
+  vector<TVectorD *> x(NREPLICA);
+  TVectorD b(nx);
+  for(int nr=0;nr<NREPLICA;nr++) {
+     x[nr]=new TVectorD(nx);
+     y[nr]=new TVectorD(ny);
+     yMb[nr]=new TVectorD(ny);
+     yErr[nr]=new TVectorD(ny);
+  }
+  for(int i=0;i<nx;i++) {
+     (*x[0])(i)=histOutputAgo[0]->GetBinContent(i+1)
+        *histOutputAgo[0]->GetXaxis()->GetBinWidth(i+1);
+     for(int nr=1;nr<NREPLICA;nr++) {
+        (*x[nr])(i)=(*x[0])(i);
+     }
+  }
+  for(int i=0;i<ny;i++) {
+     (*y[0])(i)=histDataRecCO->GetBinContent(i+1)
+        *histDataRecCO->GetXaxis()->GetBinWidth(i+1);
+     for(int nr=1;nr<NREPLICA;nr++) {
+        (*y[nr])(i)=g_rnd->Poisson((*y[0])(i));
+        (*yErr[nr])(i)=TMath::Sqrt((*y[nr])(i));
+     }
+     b(i)=histMcbgrRecCO->GetBinContent(i+1)*
+        histMcbgrRecCO->GetXaxis()->GetBinWidth(i+1);
+     for(int nr=0;nr<NREPLICA;nr++) {
+        (*yMb[nr])(i)=(*y[nr])(i)-b(i);
+     }
+  }
+  for(int iter=0;iter<=niter;iter++) {
+     if(!(iter %100)) cout<<iter<<"\n";
+     for(int nr=0;nr<NREPLICA;nr++) {
+        TVectorD yrec=A*(*x[nr])+b;
+        TVectorD yOverYrec(ny);
+        for(int j=0;j<ny;j++) {
+           yOverYrec(j)=(*y[nr])(j)/yrec(j);
+        }
+        TVectorD f=AToverEps * yOverYrec;
+        TVectorD xx(nx);
+        for(int i=0;i<nx;i++) {
+           xx(i) = (*x[nr])(i) * f(i);
+        }
+        if(nr==0) {
+           TMatrixD xdf_dr=AToverEps;
+           for(int i=0;i<nx;i++) {
+              for(int j=0;j<ny;j++) {
+                 xdf_dr(i,j) *= (*x[nr])(i);
+              }
+           }
+           TMatrixD dr_dxdy(ny,nx+ny);
+           for(int j=0;j<ny;j++) {
+              dr_dxdy(j,nx+j)=1.0/yrec(j);
+              for(int i=0;i<nx;i++) {
+                 dr_dxdy(j,i)= -yOverYrec(j)/yrec(j)*A(j,i);
+              }
+           }
+           TMatrixD dxy_dxy(nx+ny,nx+ny);
+           dxy_dxy.SetSub(0,0,xdf_dr*dr_dxdy);
+           for(int i=0;i<nx;i++) {
+              dxy_dxy(i,i) +=f(i);
+           }
+           for(int i=0;i<ny;i++) {
+              dxy_dxy(nx+i,nx+i) +=1.0;
+           }
+           TMatrixD VDT(covAgo,TMatrixD::kMultTranspose,dxy_dxy);
+           covAgo= dxy_dxy*VDT;
+        }
+        (*x[nr])=xx;
+     }
+     if((iter<=25)||
+        ((iter<=100)&&(iter %5==0))||
+        ((iter<=1000)&&(iter %50==0))||
+        (iter %1000==0)) {
+        nIter.push_back(iter);
+        TH1 * h=(TH1*)histOutputAgo[0]->Clone
+           (TString::Format("histOutputAgo%d",iter));
+        histOutputAgo.push_back(h);
+        for(int i=0;i<nx;i++) {
+           double bw=h->GetXaxis()->GetBinWidth(i+1);
+           h->SetBinContent(i+1,(*x[0])(i)/bw);
+           h->SetBinError(i+1,TMath::Sqrt(covAgo(i,i))/bw);
+        }
+        TH2 *h2=(TH2*)histRhoAgo[0]->Clone
+           (TString::Format("histRhoAgo%d",iter));
+        histRhoAgo.push_back(h2);
+        for(int i=0;i<nx;i++) {
+           for(int j=0;j<nx;j++) {
+              double rho= covAgo(i,j)/TMath::Sqrt(covAgo(i,i)*covAgo(j,j));
+              if((i!=j)&&(TMath::Abs(rho)>=1.0)) {
+                 cout<<"bad error matrix: iter="<<iter<<"\n";
+                 exit(0);
+              }
+              h2->SetBinContent(i+1,j+1,rho);
+           }
+        }
+        // error and correlations from replica analysis
+        h=(TH1*)histOutputAgo[0]->Clone
+           (TString::Format("histOutputAgorep%d",iter));
+        h2=(TH2*)histRhoAgo[0]->Clone
+           (TString::Format("histRhoAgorep%d",iter));
+        histOutputAgorep.push_back(h);
+        histRhoAgorep.push_back(h2);
+
+        TVectorD mean(nx);
+        double w=1./(NREPLICA-1.);
+        for(int nr=1;nr<NREPLICA;nr++) {
+           mean += w* *x[nr];
+        }
+        TMatrixD covAgorep(nx,nx);
+        for(int nr=1;nr<NREPLICA;nr++) {
+           //TMatrixD dx= (*x)-mean;
+           TMatrixD dx(nx,1);
+           for(int i=0;i<nx;i++) {
+              dx(i,0)= (*x[nr])(i)-(*x[0])(i);
+           }
+           covAgorep += w*TMatrixD(dx,TMatrixD::kMultTranspose,dx);
+        }
+
+        for(int i=0;i<nx;i++) {
+           double bw=h->GetXaxis()->GetBinWidth(i+1);
+           h->SetBinContent(i+1,(*x[0])(i)/bw);
+           h->SetBinError(i+1,TMath::Sqrt(covAgorep(i,i))/bw);
+           // cout<<i<<" "<<(*x[0])(i)/bw<<" +/-"<<TMath::Sqrt(covAgorep(i,i))/bw<<" "<<TMath::Sqrt(covAgo(i,i))/bw<<"\n";
+        }
+        for(int i=0;i<nx;i++) {
+           for(int j=0;j<nx;j++) {
+              double rho= covAgorep(i,j)/
+                 TMath::Sqrt(covAgorep(i,i)*covAgorep(j,j));
+              if((i!=j)&&(TMath::Abs(rho)>=1.0)) {
+                 cout<<"bad error matrix: iter="<<iter<<"\n";
+                 exit(0);
+              }
+              h2->SetBinContent(i+1,j+1,rho);
+           }
+        }
+     }
+  }
+
+#ifdef WITH_IDS
+  // IDS Malaescu
+  int niterIDS=100;
+  vector<TVectorD*> unfresIDS(NREPLICA),soustr(NREPLICA);
+  cout<<"IDS number of iterations: "<<niterIDS<<"\n";
+  TMatrixD *Am_IDS[NREPLICA];
+  TMatrixD A_IDS(ny,nx);
+  for(int nr=0;nr<NREPLICA;nr++) {
+     Am_IDS[nr]=new TMatrixD(ny,nx);
+  }
+  for(int iy=0;iy<ny;iy++) {
+     for(int ix=0;ix<nx;ix++) {
+        A_IDS(iy,ix)=histMcsigGenRecC[0]->GetBinContent(ix+1,iy+1);
+     }
+  }
+  double lambdaL=0.;
+  Double_t lambdaUmin = 1.0000002;
+  Double_t lambdaMmin = 0.0000001;
+  Double_t lambdaS = 0.000001;
+  double lambdaU=lambdaUmin;
+  double lambdaM=lambdaMmin;
+  vector<TH1 *> histOutputIDS;
+  vector<TH2 *> histRhoIDS;
+  histOutputIDS.push_back((TH1*)histOutputAgo[0]->Clone("histOutputIDS-1"));
+  histRhoIDS.push_back((TH2*)histRhoAgo[0]->Clone("histRhoIDS-1"));
+  histOutputIDS.push_back((TH1*)histOutputAgo[0]->Clone("histOutputIDS0"));
+  histRhoIDS.push_back((TH2*)histRhoAgo[0]->Clone("histRhoIDS0"));
+  for(int iter=1;iter<=niterIDS;iter++) {
+     if(!(iter %10)) cout<<iter<<"\n";
+
+     for(int nr=0;nr<NREPLICA;nr++) {
+        if(iter==1) {
+           IDSfirst(yMb[nr],yErr[nr],&A_IDS,lambdaL,unfresIDS[nr],soustr[nr]);
+        } else {
+           IDSiterate(yMb[nr],yErr[nr],&A_IDS,Am_IDS[nr],
+                      lambdaU,lambdaM,lambdaS,
+                      unfresIDS[nr],soustr[nr]);
+        }
+     }
+     unsigned ix;
+     for(ix=0;ix<nIter.size();ix++) {
+        if(nIter[ix]==iter) break;
+     }
+     if(ix<nIter.size()) {
+        TH1 * h=(TH1*)histOutputIDS[0]->Clone
+           (TString::Format("histOutputIDS%d",iter));
+        TH2 *h2=(TH2*)histRhoIDS[0]->Clone
+           (TString::Format("histRhoIDS%d",iter));
+        histOutputIDS.push_back(h);
+        histRhoIDS.push_back(h2);
+        TVectorD mean(nx);
+        double w=1./(NREPLICA-1.);
+        for(int nr=1;nr<NREPLICA;nr++) {
+           mean += w* (*unfresIDS[nr]);
+        }
+        TMatrixD covIDSrep(nx,nx);
+        for(int nr=1;nr<NREPLICA;nr++) {
+           //TMatrixD dx= (*x)-mean;
+           TMatrixD dx(nx,1);
+           for(int i=0;i<nx;i++) {
+              dx(i,0)= (*unfresIDS[nr])(i)-(*unfresIDS[0])(i);
+           }
+           covIDSrep += w*TMatrixD(dx,TMatrixD::kMultTranspose,dx);
+        }
+        for(int i=0;i<nx;i++) {
+           double bw=h->GetXaxis()->GetBinWidth(i+1);
+           h->SetBinContent(i+1,(*unfresIDS[0])(i)/bw/
+                            histEfficiencyC->GetBinContent(i+1));
+           h->SetBinError(i+1,TMath::Sqrt(covIDSrep(i,i))/bw/
+                          histEfficiencyC->GetBinContent(i+1));
+           // cout<<i<<" "<<(*x[0])(i)/bw<<" +/-"<<TMath::Sqrt(covAgorep(i,i))/bw<<" "<<TMath::Sqrt(covAgo(i,i))/bw<<"\n";
+        }
+        for(int i=0;i<nx;i++) {
+           for(int j=0;j<nx;j++) {
+              double rho= covIDSrep(i,j)/
+                 TMath::Sqrt(covIDSrep(i,i)*covIDSrep(j,j));
+              if((i!=j)&&(TMath::Abs(rho)>=1.0)) {
+                 cout<<"bad error matrix: iter="<<iter<<"\n";
+                 exit(0);
+              }
+              h2->SetBinContent(i+1,j+1,rho);
+           }
+        }
+     }
+  }
+#endif
+
+  //double NEdSmc=histDataBgrsub->GetSumOfWeights();
+
+  vector<pair<TF1 *,vector<double> > > table;
+
+  TCanvas *c1=new TCanvas("c1","",600,600);
+  TCanvas *c2sq=new TCanvas("c2sq","",600,600);
+  c2sq->Divide(1,2);
+  TCanvas *c2w=new TCanvas("c2w","",600,300);
+  c2w->Divide(2,1);
+  TCanvas *c4=new TCanvas("c4","",600,600);
+  c4->Divide(2,2);
+  //TCanvas *c3n=new TCanvas("c3n","",600,600);
+  TPad *subn[3];
+  //gROOT->SetStyle("xTimes2");
+  subn[0]= new TPad("subn0","",0.,0.5,1.,1.);
+  //gROOT->SetStyle("square");
+  subn[1]= new TPad("subn1","",0.,0.,0.5,0.5);
+  subn[2]= new TPad("subn2","",0.5,0.0,1.,0.5);
+  for(int i=0;i<3;i++) {
+     subn[i]->SetFillStyle(0);
+     subn[i]->Draw();
+  }
+  TCanvas *c3c=new TCanvas("c3c","",600,600);
+  TPad *subc[3];
+  //gROOT->SetStyle("xTimes2");
+  subc[0]= new TPad("sub0","",0.,0.5,1.,1.);
+  //gROOT->SetStyle("squareCOLZ");
+  subc[1]= new TPad("sub1","",0.,0.,0.5,0.5);
+  //gROOT->SetStyle("square");
+  subc[2]= new TPad("sub2","",0.5,0.0,1.,0.5);
+  for(int i=0;i<3;i++) {
+     subc[i]->SetFillStyle(0);
+     subc[i]->Draw();
+  }
+
+  //=========================== example ==================================
+
+  c2w->cd(1);
+  DrawPadTruth(histMcsigGenO,histDataGenO,0);
+  c2w->cd(2);
+  DrawPadReco(histMcRecCO,histMcbgrRecCO,histDataRecCO,0,0,0);
+  c2w->SaveAs("exampleTR.eps");
+
+  //=========================== example ==================================
+
+  c2w->cd(1);
+  DrawPadProbability(histProbCO);
+  c2w->cd(2);
+  DrawPadEfficiency(histEfficiencyC);
+  c2w->SaveAs("exampleAE.eps");
+
+  int iFitInversion=table.size();
+  DoFit(histOutputCtau0O,histRhoCtau0O,histDataGenO,"inversion",table);
+  //=========================== inversion ==================================
+
+  subc[0]->cd();
+  DrawPadTruth(histMcsigGenO,histDataGenO,histOutputCtau0O,"inversion",0.,
+               &table[table.size()-1].second);
+  subc[1]->cd();
+  DrawPadCorrelations(histRhoCtau0O,&table);
+  subc[2]->cd();
+  DrawPadReco(histMcRecCO,histMcbgrRecCO,histDataRecCO,
+              histOutputCtau0O,histProbCO,histRhoCtau0O);
+  c3c->SaveAs("inversion.eps");
+
+
+  DoFit(histOutputFtau0O,histRhoFtau0O,histDataGenO,"template",table);
+  //=========================== template ==================================
+
+  subc[0]->cd();
+  DrawPadTruth(histMcsigGenO,histDataGenO,histOutputFtau0O,"fit",0.,
+               &table[table.size()-1].second);
+  subc[1]->cd();
+  DrawPadCorrelations(histRhoFtau0O,&table);
+  subc[2]->cd();
+  DrawPadReco(histMcRecFO,histMcbgrRecFO,histDataRecFO,
+              histOutputFtau0O,histProbFO,histRhoFtau0O);
+  c3c->SaveAs("template.eps");
+
+  DoFit(histOutputFAtau0O,histRhoFAtau0O,histDataGenO,"template+area",table);
+  //=========================== template+area ==================================
+
+  subc[0]->cd();
+  DrawPadTruth(histMcsigGenO,histDataGenO,histOutputFAtau0O,"fit",0.,
+               &table[table.size()-1].second);
+  subc[1]->cd();
+  DrawPadCorrelations(histRhoFAtau0O,&table);
+  subc[2]->cd();
+  DrawPadReco(histMcRecFO,histMcbgrRecFO,histDataRecFO,
+              histOutputFAtau0O,histProbFO,histRhoFAtau0O);
+  c3c->SaveAs("templateA.eps");
+
+  int iFitFALCurve=table.size();
+  DoFit(histOutputFALCurveO,histRhoFALCurveO,histDataGenO,"Tikhonov+area",table);
+  //=========================== template+area+tikhonov =====================
+  subc[0]->cd();
+  DrawPadTruth(histMcsigGenO,histDataGenO,histOutputFALCurveO,"Tikhonov",tauFA,
+                &table[table.size()-1].second);
+  subc[1]->cd();
+  DrawPadCorrelations(histRhoFALCurveO,&table);
+  subc[2]->cd();
+  DrawPadReco(histMcRecFO,histMcbgrRecFO,histDataRecFO,
+              histOutputFALCurveO,histProbFO,histRhoFALCurveO);
+  c3c->SaveAs("lcurveFA.eps");
+
+  int iFitFArho=table.size();
+  DoFit(histOutputFArhoO,histRhoFArhoO,histDataGenO,"min(rhomax)",table);
+  //=========================== template+area+tikhonov =====================
+  subc[0]->cd();
+  DrawPadTruth(histMcsigGenO,histDataGenO,histOutputFArhoO,"Tikhonov",tauFArho,
+                &table[table.size()-1].second);
+  subc[1]->cd();
+  DrawPadCorrelations(histRhoFArho,&table);
+  subc[2]->cd();
+  DrawPadReco(histMcRecFO,histMcbgrRecFO,histDataRecFO,
+              histOutputFArhoO,histProbFO,histRhoFArhoO);
+  c3c->SaveAs("rhoscanFA.eps");
+
+  int iFitBinByBin=table.size();
+  DoFit(histOutputBBBO,histRhoBBBO,histDataGenO,"bin-by-bin",table);
+  //=========================== bin-by-bin =================================
+  //c->cd(1);
+  //DrawPadProbability(histProbCO);
+  //c->cd(2);
+  //DrawPadCorrelations(histRhoBBBO,&table);
+  c2sq->cd(1);
+  DrawPadReco(histMcRecCO,histMcbgrRecCO,histDataRecCO,
+              histOutputBBBO,histProbCO,histRhoBBBO);
+  c2sq->cd(2);
+  DrawPadTruth(histMcsigGenO,histDataGenO,histOutputBBBO,"bin-by-bin",0.,
+               &table[table.size()-1].second);
+  c2sq->SaveAs("binbybin.eps");
+
+
+  //=========================== iterative ===================================
+  int iAgoFirstFit=table.size();
+  for(size_t i=1;i<histRhoAgorep.size();i++) {
+     int n=nIter[i];
+     bool isFitted=false;
+     DoFit(histOutputAgorep[i],histRhoAgorep[i],histDataGenO,
+           TString::Format("iterative, N=%d",n),table,n);
+     isFitted=true;
+     subc[0]->cd();
+     DrawPadTruth(histMcsigGenO,histDataGenO,histOutputAgorep[i],
+                  TString::Format("iterative N=%d",nIter[i]),0.,
+                  isFitted ? &table[table.size()-1].second : 0);
+     subc[1]->cd();
+     DrawPadCorrelations(histRhoAgorep[i],&table);
+     subc[2]->cd();
+     DrawPadReco(histMcRecCO,histMcbgrRecCO,histDataRecCO,
+                 histOutputAgorep[i],histProbCO,histRhoAgorep[i]);
+     c3c->SaveAs(TString::Format("iterative%d.eps",nIter[i]));
+  }
+  int iAgoLastFit=table.size();
+
+#ifdef WITH_IDS
+  int iIDSFirstFit=table.size();
+
+  //=========================== IDS ===================================
+
+  for(size_t i=2;i<histRhoIDS.size();i++) {
+     int n=nIter[i];
+     bool isFitted=false;
+     DoFit(histOutputIDS[i],histRhoIDS[i],histDataGenO,
+           TString::Format("IDS, N=%d",n),table,n);
+     isFitted=true;
+     subc[0]->cd();
+     DrawPadTruth(histMcsigGenO,histDataGenO,histOutputIDS[i],
+                  TString::Format("IDS N=%d",nIter[i]),0.,
+                  isFitted ? &table[table.size()-1].second : 0);
+     subc[1]->cd();
+     DrawPadCorrelations(histRhoIDS[i],&table);
+     subc[2]->cd();
+     DrawPadReco(histMcRecCO,histMcbgrRecCO,histDataRecCO,
+                 histOutputIDS[i],histProbCO,histRhoIDS[i]);
+     c3c->SaveAs(TString::Format("ids%d.eps",nIter[i]));
+  }
+  int iIDSLastFit=table.size();
+#endif
+
+  int nfit=table.size();
+  TH1D *fitChindf=new TH1D("fitChindf",";algorithm;#chi^{2}/NDF",nfit,0,nfit);
+  TH1D *fitNorm=new TH1D("fitNorm",";algorithm;Landau amplitude [1/GeV]",nfit,0,nfit);
+  TH1D *fitMu=new TH1D("fitMu",";algorithm;Landau #mu [GeV]",nfit,0,nfit);
+  TH1D *fitSigma=new TH1D("fitSigma",";algorithm;Landau #sigma [GeV]",nfit,0,nfit);
+  for(int fit=0;fit<nfit;fit++) {
+     TF1 *f=table[fit].first;
+     vector<double> const &r=table[fit].second;
+     fitChindf->GetXaxis()->SetBinLabel(fit+1,f->GetName());
+     fitNorm->GetXaxis()->SetBinLabel(fit+1,f->GetName());
+     fitMu->GetXaxis()->SetBinLabel(fit+1,f->GetName());
+     fitSigma->GetXaxis()->SetBinLabel(fit+1,f->GetName());
+     double chi2=r[0];
+     double ndf=r[1];
+     fitChindf->SetBinContent(fit+1,chi2/ndf);
+     fitChindf->SetBinError(fit+1,TMath::Sqrt(2./ndf));
+     fitNorm->SetBinContent(fit+1,f->GetParameter(0));
+     fitNorm->SetBinError(fit+1,f->GetParError(0));
+     fitMu->SetBinContent(fit+1,f->GetParameter(1));
+     fitMu->SetBinError(fit+1,f->GetParError(1));
+     fitSigma->SetBinContent(fit+1,f->GetParameter(2));
+     fitSigma->SetBinError(fit+1,f->GetParError(2));
+     cout<<"\""<<f->GetName()<<"\","<<r[2]/r[3]<<","<<r[3]
+         <<","<<TMath::Prob(r[2],r[3])
+         <<","<<chi2/ndf
+         <<","<<ndf
+         <<","<<TMath::Prob(r[0],r[1])
+         <<","<<r[5];
+     for(int i=1;i<3;i++) {
+        cout<<","<<f->GetParameter(i)<<",\"\302\261\","<<f->GetParError(i);
+     }
+     cout<<"\n";
+  }
+
+  //=========================== L-curve ==========================
+  c4->cd(1);
+  lCurve->SetTitle("L curve;log_{10} L_{x};log_{10} L_{y}");
+  lCurve->SetLineColor(kRed);
+  lCurve->Draw("AL");
+  c4->cd(2);
+  gPad->Clear();
+  c4->cd(3);
+  logTauX->SetTitle(";log_{10} #tau;log_{10} L_{x}");
+  logTauX->SetLineColor(kBlue);
+  logTauX->Draw();
+  c4->cd(4);
+  logTauY->SetTitle(";log_{10} #tau;log_{10} L_{y}");
+  logTauY->SetLineColor(kBlue);
+  logTauY->Draw();
+  c4->SaveAs("lcurveL.eps");
+
+  //========================= rho and L-curve scan ===============
+  c4->cd(1);
+  logTauCurvature->SetTitle(";log_{10}(#tau);L curve curvature");
+  logTauCurvature->SetLineColor(kRed);
+  logTauCurvature->Draw();
+  c4->cd(2);
+  rhoScan->SetTitle(";log_{10}(#tau);average(#rho_{i})");
+  rhoScan->SetLineColor(kRed);
+  rhoScan->Draw();
+  c4->cd(3);
+  DrawPadTruth(histMcsigGenO,histDataGenO,histOutputFALCurveO,"Tikhonov",tauFA,
+                &table[iFitFALCurve].second);
+  c4->cd(4);
+  DrawPadTruth(histMcsigGenO,histDataGenO,histOutputFArhoO,"Tikhonov",tauFArho,
+                &table[iFitFArho].second);
+
+  c4->SaveAs("scanTau.eps");
+
+
+  TGraph *graphNiterAgoBay[4];
+  GetNiterGraphs(iAgoFirstFit,iAgoFirstFit+1,table,kRed-2,graphNiterAgoBay,20);
+  TGraph *graphNiterAgo[4];
+  GetNiterGraphs(iAgoFirstFit,iAgoLastFit,table,kRed,graphNiterAgo,24);
+#ifdef WITH_IDS
+  TGraph *graphNiterIDS[4];
+  GetNiterGraphs(iIDSFirstFit,iIDSLastFit,table,kMagenta,graphNiterIDS,21);
+#endif
+  TH1 *histNiterInversion[4];
+  GetNiterHist(iFitInversion,table,histNiterInversion,kCyan,1,1001);
+  TH1 *histNiterFALCurve[4];
+  GetNiterHist(iFitFALCurve,table,histNiterFALCurve,kBlue,2,3353);
+  TH1 *histNiterFArho[4];
+  GetNiterHist(iFitFArho,table,histNiterFArho,kAzure-4,3,3353);
+  TH1 *histNiterBinByBin[4];
+  GetNiterHist(iFitBinByBin,table,histNiterBinByBin,kOrange+1,3,3335);
+
+  histNiterInversion[0]->GetYaxis()->SetRangeUser(0.3,500.);
+  histNiterInversion[1]->GetYaxis()->SetRangeUser(-0.1,0.9);
+  histNiterInversion[2]->GetYaxis()->SetRangeUser(5.6,6.3);
+  histNiterInversion[3]->GetYaxis()->SetRangeUser(1.6,2.4);
+
+  TLine *line=0;
+  c1->cd();
+  for(int i=0;i<2;i++) {
+     gPad->Clear();
+     gPad->SetLogx();
+     gPad->SetLogy((i<1));
+     if(! histNiterInversion[i]) continue;
+     histNiterInversion[i]->Draw("][");
+     histNiterFALCurve[i]->Draw("SAME ][");
+     histNiterFArho[i]->Draw("SAME ][");
+     histNiterBinByBin[i]->Draw("SAME ][");
+     graphNiterAgo[i]->Draw("LP");
+     graphNiterAgoBay[i]->SetMarkerStyle(20);
+     graphNiterAgoBay[i]->Draw("P");
+#ifdef WITH_IDS
+     graphNiterIDS[i]->Draw("LP");
+#endif
+     TLegend *legend;
+     if(i==1) {
+        legend=new TLegend(0.48,0.28,0.87,0.63);
+     } else {
+        legend=new TLegend(0.45,0.5,0.88,0.88);
+     }
+     legend->SetBorderSize(0);
+     legend->SetFillStyle(1001);
+     legend->SetFillColor(kWhite);
+     legend->SetTextSize(kLegendFontSize*0.7);
+     legend->AddEntry( histNiterInversion[0],"inversion","l");
+     legend->AddEntry( histNiterFALCurve[0],"Tikhonov L-curve","l");
+     legend->AddEntry( histNiterFArho[0],"Tikhonov global cor.","l");
+     legend->AddEntry( histNiterBinByBin[0],"bin-by-bin","l");
+     legend->AddEntry( graphNiterAgoBay[0],"\"Bayesian\"","p");
+     legend->AddEntry( graphNiterAgo[0],"iterative","p");
+#ifdef WITH_IDS
+     legend->AddEntry( graphNiterIDS[0],"IDS","p");
+#endif
+     legend->Draw();
+
+     c1->SaveAs(TString::Format("niter%d.eps",i));
+  }
+
+  //c4->cd(1);
+  //DrawPadCorrelations(histRhoFALCurveO,&table);
+  c2sq->cd(1);
+  DrawPadTruth(histMcsigGenO,histDataGenO,histOutputFALCurveO,"Tikhonov",tauFA,
+               &table[iFitFALCurve].second,table[iFitFALCurve].first);
+
+  c2sq->cd(2);
+  gPad->SetLogx();
+  gPad->SetLogy(false);
+  histNiterInversion[3]->DrawClone("E2");
+  histNiterInversion[3]->SetFillStyle(0);
+  histNiterInversion[3]->Draw("SAME HIST ][");
+  histNiterFALCurve[3]->DrawClone("SAME E2");
+  histNiterFALCurve[3]->SetFillStyle(0);
+  histNiterFALCurve[3]->Draw("SAME HIST ][");
+  histNiterFArho[3]->DrawClone("SAME E2");
+  histNiterFArho[3]->SetFillStyle(0);
+  histNiterFArho[3]->Draw("SAME HIST ][");
+  histNiterBinByBin[3]->DrawClone("SAME E2");
+  histNiterBinByBin[3]->SetFillStyle(0);
+  histNiterBinByBin[3]->Draw("SAME HIST ][");
+  double yTrue=1.8;
+  line=new TLine(histNiterInversion[3]->GetXaxis()->GetXmin(),
+                 yTrue,
+                 histNiterInversion[3]->GetXaxis()->GetXmax(),
+                 yTrue);
+  line->SetLineWidth(3);
+  line->Draw();
+
+  graphNiterAgo[3]->Draw("LP");
+  graphNiterAgoBay[3]->SetMarkerStyle(20);
+  graphNiterAgoBay[3]->Draw("P");
+#ifdef WITH_IDS
+  graphNiterIDS[3]->Draw("LP");
+#endif
+
+  TLegend *legend;
+  legend=new TLegend(0.55,0.53,0.95,0.97);
+  legend->SetBorderSize(0);
+  legend->SetFillStyle(1001);
+  legend->SetFillColor(kWhite);
+  legend->SetTextSize(kLegendFontSize);
+  legend->AddEntry( line,"truth","l");
+  legend->AddEntry( histNiterInversion[3],"inversion","l");
+  legend->AddEntry( histNiterFALCurve[3],"Tikhonov L-curve","l");
+  legend->AddEntry( histNiterFArho[3],"Tikhonov global cor.","l");
+  legend->AddEntry( histNiterBinByBin[3],"bin-by-bin","l");
+  legend->AddEntry( graphNiterAgoBay[3],"\"Bayesian\"","p");
+  legend->AddEntry( graphNiterAgo[3],"iterative","p");
+#ifdef WITH_IDS
+  legend->AddEntry( graphNiterIDS[3],"IDS","p");
+#endif
+  legend->Draw();
+
+  c2sq->SaveAs("fitSigma.eps");
+
+  outputFile->Write();
+
+  delete outputFile;
+
+}
+
+void GetNiterGraphs(int iFirst,int iLast,vector<pair<TF1*,
+                    vector<double> > > const &table,int color,
+                    TGraph *graph[4],int style) {
+   TVectorD niter(iLast-iFirst);
+   TVectorD eniter(iLast-iFirst);
+   TVectorD chi2(iLast-iFirst);
+   TVectorD gcor(iLast-iFirst);
+   TVectorD mean(iLast-iFirst);
+   TVectorD emean(iLast-iFirst);
+   TVectorD sigma(iLast-iFirst);
+   TVectorD esigma(iLast-iFirst);
+   for(int ifit=iFirst;ifit<iLast;ifit++) {
+      vector<double> const &r=table[ifit].second;
+      niter(ifit-iFirst)=r[4];
+      chi2(ifit-iFirst)=r[2]/r[3];
+      gcor(ifit-iFirst)=r[5];
+      TF1 const *f=table[ifit].first;
+      mean(ifit-iFirst)=f->GetParameter(1);
+      emean(ifit-iFirst)=f->GetParError(1);
+      sigma(ifit-iFirst)=f->GetParameter(2);
+      esigma(ifit-iFirst)=f->GetParError(2);
+   }
+   graph[0]=new TGraph(niter,chi2);
+   graph[1]=new TGraph(niter,gcor);
+   graph[2]=new TGraphErrors(niter,mean,eniter,emean);
+   graph[3]=new TGraphErrors(niter,sigma,eniter,esigma);
+   for(int g=0;g<4;g++) {
+      if(graph[g]) {
+         graph[g]->SetLineColor(color);
+         graph[g]->SetMarkerColor(color);
+         graph[g]->SetMarkerStyle(style);
+      }
+   }
+}
+
+void GetNiterHist(int ifit,vector<pair<TF1*,vector<double> > > const &table,
+                  TH1 *hist[4],int color,int style,int fillStyle) {
+   vector<double> const &r=table[ifit].second;
+   TF1 const *f=table[ifit].first;
+   hist[0]=new TH1D(table[ifit].first->GetName()+TString("_chi2"),
+                    ";iteration;unfold-truth #chi^{2}/N_{D.F.}",1,0.2,1500.);
+   hist[0]->SetBinContent(1,r[2]/r[3]);
+
+   hist[1]=new TH1D(table[ifit].first->GetName()+TString("_gcor"),
+                    ";iteration;avg(#rho_{i})",1,0.2,1500.);
+   hist[1]->SetBinContent(1,r[5]);
+   hist[2]=new TH1D(table[ifit].first->GetName()+TString("_mu"),
+                     ";iteration;parameter #mu",1,0.2,1500.);
+   hist[2]->SetBinContent(1,f->GetParameter(1));
+   hist[2]->SetBinError(1,f->GetParError(1));
+   hist[3]=new TH1D(table[ifit].first->GetName()+TString("_sigma"),
+                    ";iteration;parameter #sigma",1,0.2,1500.);
+   hist[3]->SetBinContent(1,f->GetParameter(2));
+   hist[3]->SetBinError(1,f->GetParError(2));
+   for(int h=0;h<4;h++) {
+      if(hist[h]) {
+         hist[h]->SetLineColor(color);
+         hist[h]->SetLineStyle(style);
+         if( hist[h]->GetBinError(1)>0.0) {
+            hist[h]->SetFillColor(color-10);
+            hist[h]->SetFillStyle(fillStyle);
+         }
+         hist[h]->SetMarkerStyle(0);
+      }
+   }
+}
+
+void CreateHistogramCopies(TH1 *h[3],TUnfoldBinning const *binning) {
+   TString baseName(h[0]->GetName());
+   Int_t *binMap;
+   h[1]=binning->CreateHistogram(baseName+"_axis",kTRUE,&binMap);
+   h[2]=(TH1 *)h[1]->Clone(baseName+"_binw");
+   Int_t nMax=binning->GetEndBin()+1;
+   for(Int_t iSrc=0;iSrc<nMax;iSrc++) {
+      Int_t iDest=binMap[iSrc];
+      double c=h[0]->GetBinContent(iSrc)+h[1]->GetBinContent(iDest);
+      double e=TMath::Hypot(h[0]->GetBinError(iSrc),h[1]->GetBinError(iDest));
+      h[1]->SetBinContent(iDest,c);
+      h[1]->SetBinError(iDest,e);
+      h[2]->SetBinContent(iDest,c);
+      h[2]->SetBinError(iDest,e);
+   }
+   for(int iDest=0;iDest<=h[2]->GetNbinsX()+1;iDest++) {
+      double c=h[2]->GetBinContent(iDest);
+      double e=h[2]->GetBinError(iDest);
+      double bw=binning->GetBinSize(iDest);
+      /* if(bw!=h[2]->GetBinWidth(iDest)) {
+         cout<<"bin "<<iDest<<" width="<<bw<<" "<<h[2]->GetBinWidth(iDest)
+             <<"\n";
+             } */
+      if(bw>0.0) {
+         h[2]->SetBinContent(iDest,c/bw);
+         h[2]->SetBinError(iDest,e/bw);
+      } else {
+      }
+   }
+}
+
+void CreateHistogramCopies(TH2 *h[3],TUnfoldBinning const *binningX) {
+  if(binningX) {
+    h[2]=0;
+    h[3]=0;
+  } else {
+    h[2]=0;
+    h[3]=0;
+  }
+}
+
+TH2 *AddOverflowXY(TH2 *h,double widthX,double widthY) {
+   // add overflow bin to X-axis
+   int nx=h->GetNbinsX();
+   int ny=h->GetNbinsY();
+   double *xBins=new double[nx+2];
+   double *yBins=new double[ny+2];
+   for(int i=1;i<=nx;i++) {
+      xBins[i-1]=h->GetXaxis()->GetBinLowEdge(i);
+   }
+   xBins[nx]=h->GetXaxis()->GetBinUpEdge(nx);
+   xBins[nx+1]=xBins[nx]+widthX;
+   for(int i=1;i<=ny;i++) {
+      yBins[i-1]=h->GetYaxis()->GetBinLowEdge(i);
+   }
+   yBins[ny]=h->GetYaxis()->GetBinUpEdge(ny);
+   yBins[ny+1]=yBins[ny]+widthY;
+   TString name(h->GetName());
+   name+="U";
+   TH2 *r=new TH2D(name,h->GetTitle(),nx+1,xBins,ny+1,yBins);
+   for(int ix=0;ix<=nx+1;ix++) {
+     for(int iy=0;iy<=ny+1;iy++) {
+        r->SetBinContent(ix,iy,h->GetBinContent(ix,iy));
+        r->SetBinError(ix,iy,h->GetBinError(ix,iy));
+     }
+   }
+   delete [] yBins;
+   delete [] xBins;
+   return r;
+}
+
+TH1 *AddOverflowX(TH1 *h,double widthX) {
+   // add overflow bin to X-axis
+   int nx=h->GetNbinsX();
+   double *xBins=new double[nx+2];
+   for(int i=1;i<=nx;i++) {
+      xBins[i-1]=h->GetXaxis()->GetBinLowEdge(i);
+   }
+   xBins[nx]=h->GetXaxis()->GetBinUpEdge(nx);
+   xBins[nx+1]=xBins[nx]+widthX;
+   TString name(h->GetName());
+   name+="U";
+   TH1 *r=new TH1D(name,h->GetTitle(),nx+1,xBins);
+   for(int ix=0;ix<=nx+1;ix++) {
+      r->SetBinContent(ix,h->GetBinContent(ix));
+      r->SetBinError(ix,h->GetBinError(ix));
+   }
+   delete [] xBins;
+   return r;
+}
+
+void DrawOverflowX(TH1 *h,double posy) {
+  double x1=h->GetXaxis()->GetBinLowEdge(h->GetNbinsX());
+  double x2=h->GetXaxis()->GetBinUpEdge(h->GetNbinsX());
+  double y0=h->GetYaxis()->GetBinLowEdge(1);
+  double y2=h->GetYaxis()->GetBinUpEdge(h->GetNbinsY());;
+  if(h->GetDimension()==1) {
+     y0=h->GetMinimum();
+     y2=h->GetMaximum();
+  }
+  double w1=-0.3;
+  TText *textX=new TText((1.+w1)*x2-w1*x1,(1.-posy)*y0+posy*y2,"Overflow bin");
+  textX->SetNDC(kFALSE);
+  textX->SetTextSize(0.05);
+  textX->SetTextAngle(90.);
+  textX->Draw();
+  TLine *lineX=new TLine(x1,y0,x1,y2);
+  lineX->Draw();
+}
+
+void DrawOverflowY(TH1 *h,double posx) {
+   double x0=h->GetXaxis()->GetBinLowEdge(1);
+  double x2=h->GetXaxis()->GetBinUpEdge(h->GetNbinsX());
+  double y1=h->GetYaxis()->GetBinLowEdge(h->GetNbinsY());;
+  double y2=h->GetYaxis()->GetBinUpEdge(h->GetNbinsY());;
+  double w1=-0.3;
+  TText *textY=new TText((1.-posx)*x0+posx*x2,(1.+w1)*y1-w1*y2,"Overflow bin");
+  textY->SetNDC(kFALSE);
+  textY->SetTextSize(0.05);
+  textY->Draw();
+  TLine *lineY=new TLine(x0,y1,x2,y1);
+  lineY->Draw();
+}
+
+void DrawPadProbability(TH2 *h) {
+  h->Draw("COLZ");
+  h->SetTitle("migration probabilities;P_{T}(gen) [GeV];P_{T}(rec) [GeV]");
+  DrawOverflowX(h,0.05);
+  DrawOverflowY(h,0.35);
+}
+
+void DrawPadEfficiency(TH1 *h) {
+  h->SetTitle("efficiency;P_{T}(gen) [GeV];#epsilon");
+  h->SetLineColor(kBlue);
+  h->SetMinimum(0.75);
+  h->SetMaximum(1.0);
+  h->Draw();
+  DrawOverflowX(h,0.05);
+  TLegend *legEfficiency=new TLegend(0.3,0.58,0.6,0.75);
+  legEfficiency->SetBorderSize(0);
+  legEfficiency->SetFillStyle(0);
+  legEfficiency->SetTextSize(kLegendFontSize);
+  legEfficiency->AddEntry(h,"reconstruction","l");
+  legEfficiency->AddEntry((TObject*)0,"   efficiency","");
+  legEfficiency->Draw();
+}
+
+void DrawPadReco(TH1 *histMcRec,TH1 *histMcbgrRec,TH1 *histDataRec,
+                 TH1 *histDataUnfold,TH2 *histProbability,TH2 *histRhoij) {
+   //gPad->SetLogy(kTRUE);
+   double amax=0.0;
+   for(int i=1;i<=histMcRec->GetNbinsX();i++) {
+      amax=TMath::Max(amax,histMcRec->GetBinContent(i)
+                      +5.0*histMcRec->GetBinError(i));
+      amax=TMath::Max(amax,histDataRec->GetBinContent(i)
+                      +2.0*histDataRec->GetBinError(i));
+   }
+   histMcRec->SetTitle("Reconstructed;P_{T}(rec);Nevent / GeV");
+   histMcRec->Draw("HIST");
+   histMcRec->SetLineColor(kBlue);
+   histMcRec->SetMinimum(1.0);
+   histMcRec->SetMaximum(amax);
+   //histMcbgrRec->SetFillMode(1);
+   histMcbgrRec->SetLineColor(kBlue-6);
+   histMcbgrRec->SetFillColor(kBlue-10);
+   histMcbgrRec->Draw("SAME HIST");
+
+   TH1 * histFoldBack=0;
+   if(histDataUnfold && histProbability && histRhoij) {
+      histFoldBack=(TH1 *)
+         histMcRec->Clone(histDataUnfold->GetName()+TString("_folded"));
+      int nrec=histFoldBack->GetNbinsX();
+      if((nrec==histProbability->GetNbinsY())&&
+         (nrec==histMcbgrRec->GetNbinsX())&&
+         (nrec==histDataRec->GetNbinsX())
+         ) {
+         for(int ix=1;ix<=nrec;ix++) {
+            double sum=0.0;
+            double sume2=0.0;
+            for(int iy=0;iy<=histProbability->GetNbinsX()+1;iy++) {
+               sum += histDataUnfold->GetBinContent(iy)*
+                  histDataUnfold->GetBinWidth(iy)*
+                  histProbability->GetBinContent(iy,ix);
+               for(int iy2=0;iy2<=histProbability->GetNbinsX()+1;iy2++) {
+                  sume2 += histDataUnfold->GetBinError(iy)*
+                     histDataUnfold->GetBinWidth(iy)*
+                     histProbability->GetBinContent(iy,ix)*
+                     histDataUnfold->GetBinError(iy2)*
+                     histDataUnfold->GetBinWidth(iy2)*
+                     histProbability->GetBinContent(iy2,ix)*
+                     histRhoij->GetBinContent(iy,iy2);
+               }
+            }
+            sum /= histFoldBack->GetBinWidth(ix);
+            sum += histMcbgrRec->GetBinContent(ix);
+            histFoldBack->SetBinContent(ix,sum);
+            histFoldBack->SetBinError(ix,TMath::Sqrt(sume2)
+                                      /histFoldBack->GetBinWidth(ix));
+         }
+      } else {
+         cout<<"can not fold back: "<<nrec
+             <<" "<<histProbability->GetNbinsY()
+             <<" "<<histMcbgrRec->GetNbinsX()
+             <<" "<<histDataRec->GetNbinsX()
+             <<"\n";
+         exit(0);
+      }
+
+      histFoldBack->SetLineColor(kBlack);
+      histFoldBack->SetMarkerStyle(0);
+      histFoldBack->Draw("SAME HIST");
+   }
+
+   histDataRec->SetLineColor(kRed);
+   histDataRec->SetMarkerColor(kRed);
+   histDataRec->Draw("SAME");
+   DrawOverflowX(histMcRec,0.5);
+
+   TLegend *legRec=new TLegend(0.4,0.5,0.68,0.85);
+   legRec->SetBorderSize(0);
+   legRec->SetFillStyle(0);
+   legRec->SetTextSize(kLegendFontSize);
+   legRec->AddEntry(histMcRec,"MC total","l");
+   legRec->AddEntry(histMcbgrRec,"background","f");
+   if(histFoldBack) {
+      int ndf=-kNbinC;
+      double sumD=0.,sumF=0.,chi2=0.;
+      for(int i=1;i<=histDataRec->GetNbinsX();i++) {
+         //cout<<histDataRec->GetBinContent(i)<<" "<<histFoldBack->GetBinContent(i)<<" "<<" w="<<histFoldBack->GetBinWidth(i)<<"\n";
+         sumD+=histDataRec->GetBinContent(i)*histDataRec->GetBinWidth(i);
+         sumF+=histFoldBack->GetBinContent(i)*histFoldBack->GetBinWidth(i);
+         double pull=(histFoldBack->GetBinContent(i)-histDataRec->GetBinContent(i))/histDataRec->GetBinError(i);
+         chi2+= pull*pull;
+         ndf+=1;
+      }
+      legRec->AddEntry(histDataRec,TString::Format("data N_{evt}=%.0f",sumD),"lp");
+      legRec->AddEntry(histFoldBack,TString::Format("folded N_{evt}=%.0f",sumF),"l");
+      legRec->AddEntry((TObject*)0,TString::Format("#chi^{2}=%.1f ndf=%d",chi2,ndf),"");
+      //exit(0);
+   } else {
+      legRec->AddEntry(histDataRec,"data","lp");
+   }
+   legRec->Draw();
+}
+
+void DrawPadTruth(TH1 *histMcsigGen,TH1 *histDataGen,TH1 *histDataUnfold,
+                  char const *text,double tau,vector<double> const *r,
+                  TF1 *f) {
+  //gPad->SetLogy(kTRUE);
+   double amin=0.;
+   double amax=0.;
+   for(int i=1;i<=histMcsigGen->GetNbinsX();i++) {
+      if(histDataUnfold) {
+         amin=TMath::Min(amin,histDataUnfold->GetBinContent(i)
+                         -1.1*histDataUnfold->GetBinError(i));
+         amax=TMath::Max(amax,histDataUnfold->GetBinContent(i)
+                         +1.1*histDataUnfold->GetBinError(i));
+      }
+      amin=TMath::Min(amin,histMcsigGen->GetBinContent(i)
+                      -histMcsigGen->GetBinError(i));
+      amin=TMath::Min(amin,histDataGen->GetBinContent(i)
+                      -histDataGen->GetBinError(i));
+      amax=TMath::Max(amax,histMcsigGen->GetBinContent(i)
+                      +10.*histMcsigGen->GetBinError(i));
+      amax=TMath::Max(amax,histDataGen->GetBinContent(i)
+                      +2.*histDataGen->GetBinError(i));
+   }
+   histMcsigGen->SetMinimum(amin);
+   histMcsigGen->SetMaximum(amax);
+
+   histMcsigGen->SetTitle("Truth;P_{T};Nevent / GeV");
+   histMcsigGen->SetLineColor(kBlue);
+   histMcsigGen->Draw("HIST");
+   histDataGen->SetLineColor(kRed);
+   histDataGen->SetMarkerColor(kRed);
+   histDataGen->SetMarkerSize(1.0);
+   histDataGen->Draw("SAME HIST");
+   if(histDataUnfold) {
+      histDataUnfold->SetMarkerStyle(21);
+      histDataUnfold->SetMarkerSize(0.7);
+      histDataUnfold->Draw("SAME");
+   }
+   DrawOverflowX(histMcsigGen,0.5);
+
+   if(f) {
+      f->SetLineStyle(1);
+      f->Draw("SAME");
+   }
+
+   TLegend *legTruth=new TLegend(0.32,0.65,0.6,0.9);
+   legTruth->SetBorderSize(0);
+   legTruth->SetFillStyle(0);
+   legTruth->SetTextSize(kLegendFontSize);
+   legTruth->AddEntry(histMcsigGen,"MC","l");
+   if(!histDataUnfold) legTruth->AddEntry((TObject *)0,"  Landau(5,2)","");
+   legTruth->AddEntry(histDataGen,"data","l");
+   if(!histDataUnfold) legTruth->AddEntry((TObject *)0,"  Landau(6,1.8)","");
+   if(histDataUnfold) {
+      TString t;
+      if(text) t=text;
+      else t=histDataUnfold->GetName();
+      if(tau>0) {
+         t+=TString::Format(" #tau=%.2g",tau);
+      }
+      legTruth->AddEntry(histDataUnfold,t,"lp");
+      if(r) {
+         legTruth->AddEntry((TObject *)0,"test wrt data:","");
+         legTruth->AddEntry((TObject *)0,TString::Format
+                            ("#chi^{2}/%d=%.1f prob=%.3f",
+                             (int)(*r)[3],(*r)[2]/(*r)[3],
+                             TMath::Prob((*r)[2],(*r)[3])),"");
+      }
+   }
+   if(f) {
+      legTruth->AddEntry(f,"fit","l");
+   }
+   legTruth->Draw();
+   if(histDataUnfold ) {
+      TPad *subpad = new TPad("subpad","",0.35,0.29,0.88,0.68);
+      subpad->SetFillStyle(0);
+      subpad->Draw();
+      subpad->cd();
+      amin=0.;
+      amax=0.;
+      int istart=11;
+      for(int i=istart;i<=histMcsigGen->GetNbinsX();i++) {
+         amin=TMath::Min(amin,histMcsigGen->GetBinContent(i)
+                         -histMcsigGen->GetBinError(i));
+         amin=TMath::Min(amin,histDataGen->GetBinContent(i)
+                         -histDataGen->GetBinError(i));
+         amin=TMath::Min(amin,histDataUnfold->GetBinContent(i)
+                         -histDataUnfold->GetBinError(i));
+         amax=TMath::Max(amax,histMcsigGen->GetBinContent(i)
+                         +histMcsigGen->GetBinError(i));
+         amax=TMath::Max(amax,histDataGen->GetBinContent(i)
+                         +histDataGen->GetBinError(i));
+         amax=TMath::Max(amax,histDataUnfold->GetBinContent(i)
+                         +histDataUnfold->GetBinError(i));
+      }
+      TH1 *copyMcsigGen=(TH1*)histMcsigGen->Clone();
+      TH1 *copyDataGen=(TH1*)histDataGen->Clone();
+      TH1 *copyDataUnfold=(TH1*)histDataUnfold->Clone();
+      copyMcsigGen->GetXaxis()->SetRangeUser
+         (copyMcsigGen->GetXaxis()->GetBinLowEdge(istart),
+          copyMcsigGen->GetXaxis()->GetBinUpEdge(copyMcsigGen->GetNbinsX()-1));
+      copyMcsigGen->SetTitle(";;");
+      copyMcsigGen->GetYaxis()->SetRangeUser(amin,amax);
+      copyMcsigGen->Draw("HIST");
+      copyDataGen->Draw("SAME HIST");
+      copyDataUnfold->Draw("SAME");
+      if(f) {
+         ((TF1 *)f->Clone())->Draw("SAME");
+      }
+   }
+}
+
+void DrawPadCorrelations(TH2 *h,
+                         vector<pair<TF1*,vector<double> > > const *table) {
+   h->SetMinimum(-1.);
+   h->SetMaximum(1.);
+   h->SetTitle("correlation coefficients;P_{T}(gen) [GeV];P_{T}(gen) [GeV]");
+   h->Draw("COLZ");
+   DrawOverflowX(h,0.05);
+   DrawOverflowY(h,0.05);
+   if(table) {
+      TLegend *legGCor=new TLegend(0.13,0.6,0.5,0.8);
+      legGCor->SetBorderSize(0);
+      legGCor->SetFillStyle(0);
+      legGCor->SetTextSize(kLegendFontSize);
+      vector<double> const &r=(*table)[table->size()-1].second;
+      legGCor->AddEntry((TObject *)0,TString::Format("min(#rho_{ij})=%5.2f",r[6]),"");
+      legGCor->AddEntry((TObject *)0,TString::Format("max(#rho_{ij})=%5.2f",r[7]),"");
+      legGCor->AddEntry((TObject *)0,TString::Format("avg(#rho_i)=%5.2f",r[5]),"");
+      legGCor->Draw();
+   }
+}
+
+TH1 *g_fcnHist=0;
+TMatrixD *g_fcnMatrix=0;
+
+void fcn(Int_t &npar, Double_t *gin, Double_t &f, Double_t *u, Int_t flag) {
+  if(flag==0) {
+    cout<<"fcn flag=0: npar="<<npar<<" gin="<<gin<<" par=[";
+    for(int i=0;i<npar;i++) {
+      cout<<" "<<u[i];
+    }
+    cout<<"]\n";
+  }
+   int n=g_fcnMatrix->GetNrows();
+   TVectorD dy(n);
+   double x0=0,y0=0.;
+   for(int i=0;i<=n;i++) {
+      double x1;
+      if(i<1) x1=g_fcnHist->GetXaxis()->GetBinLowEdge(i+1);
+      else x1=g_fcnHist->GetXaxis()->GetBinUpEdge(i);
+      double y1=TMath::LandauI((x1-u[1])/u[2]);
+      if(i>0) {
+         double iy=u[0]*u[2]*(y1-y0)/(x1-x0);
+         dy(i-1)=iy-g_fcnHist->GetBinContent(i);
+         //cout<<"i="<<i<<" iy="<<iy<<" delta="<< dy(i-1)<<"\n";
+      }
+      x0=x1;
+      y0=y1;
+      //cout<<"i="<<i<<" y1="<<y1<<" x1="<<x1<<"\n";
+   }
+   TVectorD Hdy=(*g_fcnMatrix) * dy;
+   //Hdy.Print();
+   f=Hdy*dy;
+   //exit(0);
+}
+
+
+TFitResultPtr DoFit(TH1 *h,TH2 *rho,TH1 *truth,const char *text,
+                    vector<pair<TF1 *,vector<double> > > &table,int niter) {
+   TString option="IESN";
+   cout<<h->GetName()<<"\n";
+   double gcorAvg=0.;
+   double rhoMin=0.;
+   double rhoMax=0.;
+   if(rho) {
+      g_fcnHist=h;
+      int n=h->GetNbinsX()-1; // overflow is included as extra bin, exclude in fit
+      TMatrixDSym v(n);
+      //g_fcnMatrix=new TMatrixD(n,n);
+      for(int i=0;i<n;i++) {
+         for(int j=0;j<n;j++) {
+            v(i,j)=rho->GetBinContent(i+1,j+1)*
+               (h->GetBinError(i+1)*h->GetBinError(j+1));
+         }
+      }
+      TMatrixDSymEigen ev(v);
+      TMatrixD d(n,n);
+      TVectorD di(ev.GetEigenValues());
+      for(int i=0;i<n;i++) {
+         if(di(i)>0.0) {
+            d(i,i)=1./di(i);
+         } else {
+            cout<<"bad eigenvalue i="<<i<<" di="<<di(i)<<"\n";
+            exit(0);
+         }
+      }
+      TMatrixD O(ev.GetEigenVectors());
+      TMatrixD DOT(d,TMatrixD::kMultTranspose,O);
+      g_fcnMatrix=new TMatrixD(O,TMatrixD::kMult,DOT);
+      TMatrixD test(*g_fcnMatrix,TMatrixD::kMult,v);
+      int error=0;
+      for(int i=0;i<n;i++) {
+         if(TMath::Abs(test(i,i)-1.0)>1.E-7) {
+            error++;
+         }
+         for(int j=0;j<n;j++) {
+            if(i==j) continue;
+            if(TMath::Abs(test(i,j)>1.E-7)) error++;
+         }
+      }
+      // calculate global correlation coefficient (all bins)
+      TMatrixDSym v1(n+1);
+      rhoMin=1.;
+      rhoMax=-1.;
+      for(int i=0;i<=n;i++) {
+         for(int j=0;j<=n;j++) {
+            double rho_ij=rho->GetBinContent(i+1,j+1);
+            v1(i,j)=rho_ij*
+               (h->GetBinError(i+1)*h->GetBinError(j+1));
+            if(i!=j) {
+               if(rho_ij<rhoMin) rhoMin=rho_ij;
+               if(rho_ij>rhoMax) rhoMax=rho_ij;
+            }
+         }
+      }
+      TMatrixDSymEigen ev1(v1);
+      TMatrixD d1(n+1,n+1);
+      TVectorD di1(ev1.GetEigenValues());
+      for(int i=0;i<=n;i++) {
+         if(di1(i)>0.0) {
+            d1(i,i)=1./di1(i);
+         } else {
+            cout<<"bad eigenvalue i="<<i<<" di1="<<di1(i)<<"\n";
+            exit(0);
+         }
+      }
+      TMatrixD O1(ev1.GetEigenVectors());
+      TMatrixD DOT1(d1,TMatrixD::kMultTranspose,O1);
+      TMatrixD vinv1(O1,TMatrixD::kMult,DOT1);
+      for(int i=0;i<=n;i++) {
+         double gcor2=1.-1./(vinv1(i,i)*v1(i,i));
+         if(gcor2>=0.0) {
+            double gcor=TMath::Sqrt(gcor2);
+            gcorAvg += gcor;
+         } else {
+            cout<<"bad global correlation "<<i<<" "<<gcor2<<"\n";
+         }
+      }
+      gcorAvg /=(n+1);
+      /* if(error) {
+         v.Print();
+         g_fcnMatrix->Print();
+         exit(0);
+         } */
+      //g_fcnMatrix->Invert();
+      //from: HFitImpl.cxx
+      // TVirtualFitter::FCNFunc_t  userFcn = 0;
+      //    typedef void   (* FCNFunc_t )(Int_t &npar, Double_t *gin, Double_t &f, Double_t *u, Int_t flag);
+      // userFcn = (TVirtualFitter::GetFitter())->GetFCN();
+      //  (TVirtualFitter::GetFitter())->SetUserFunc(f1);
+      //...
+      //fitok = fitter->FitFCN( userFcn );
+
+      TVirtualFitter::Fitter(h)->SetFCN(fcn);
+      option += "U";
+
+   }
+   double xmax=h->GetXaxis()->GetBinUpEdge(h->GetNbinsX()-1);
+   TF1 *landau=new TF1(text,"[0]*TMath::Landau(x,[1],[2],0)",
+                       0.,xmax);
+   landau->SetParameter(0,6000.);
+   landau->SetParameter(1,5.);
+   landau->SetParameter(2,2.);
+   landau->SetParError(0,10.);
+   landau->SetParError(1,0.5);
+   landau->SetParError(2,0.1);
+   TFitResultPtr s=h->Fit(landau,option,0,0.,xmax);
+   vector<double> r(8);
+   int np=landau->GetNpar();
+   fcn(np,0,r[0],landau->GetParameters(),0);
+   r[1]=h->GetNbinsX()-1-landau->GetNpar();
+   for(int i=0;i<h->GetNbinsX()-1;i++) {
+      double di=h->GetBinContent(i+1)-truth->GetBinContent(i+1);
+      if(g_fcnMatrix) {
+         for(int j=0;j<h->GetNbinsX()-1;j++) {
+            double dj=h->GetBinContent(j+1)-truth->GetBinContent(j+1);
+            r[2]+=di*dj*(*g_fcnMatrix)(i,j);
+         }
+      } else {
+         double pull=di/h->GetBinError(i+1);
+         r[2]+=pull*pull;
+      }
+      r[3]+=1.0;
+   }
+   r[4]=niter;
+   if(!niter) r[4]=0.25;
+   r[5]=gcorAvg;
+   r[6]=rhoMin;
+   r[7]=rhoMax;
+   if(rho) {
+      g_fcnHist=0;
+      delete g_fcnMatrix;
+      g_fcnMatrix=0;
+   }
+   table.push_back(make_pair(landau,r));
+   return s;
+}
+
+#ifdef WITH_IDS
+
+//===================== interface to IDS unfolding code follows here
+// contact Bogdan Malescu to find it
+
+#include "ids_code.cc"
+
+void IDSfirst(TVectorD *data, TVectorD *dataErr, TMatrixD *A_, Double_t lambdaL_, TVectorD* &unfres1IDS_,TVectorD *&soustr){
+
+   int N_=data->GetNrows();
+   soustr = new TVectorD(N_);
+   for( Int_t i=0; i<N_; i++ ){ (*soustr)[i] = 0.; }
+   unfres1IDS_ = Unfold( data, dataErr, A_, N_, lambdaL_, soustr );
+}
+
+void IDSiterate(TVectorD *data, TVectorD *dataErr, TMatrixD *A_, TMatrixD *Am_, Double_t lambdaU_, Double_t lambdaM_, Double_t lambdaS_,TVectorD* &unfres2IDS_ ,TVectorD *&soustr) {
+
+   int N_=data->GetNrows();
+   ModifyMatrix( Am_, A_, unfres2IDS_, dataErr, N_, lambdaM_, soustr, lambdaS_ );
+   delete unfres2IDS_;
+   unfres2IDS_ = Unfold( data, dataErr, Am_, N_, lambdaU_, soustr );
+}
+
+#endif
-- 
GitLab