Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TH1Merger.cxx
Go to the documentation of this file.
1 // Helper clas implementing some of the TH1 functionality
2 
3 #include "TH1Merger.h"
4 #include "TH1.h"
5 #include "TH2.h"
6 #include "TH3.h"
7 #include "TAxis.h"
8 #include "TError.h"
9 #include "THashList.h"
10 #include "TClass.h"
11 #include <iostream>
12 #include <limits>
13 #include <utility>
14 
15 #define PRINTRANGE(a, b, bn) \
16  Printf(" base: %f %f %d, %s: %f %f %d", a->GetXmin(), a->GetXmax(), a->GetNbins(), bn, b->GetXmin(), b->GetXmax(), \
17  b->GetNbins());
18 
19 Bool_t TH1Merger::AxesHaveLimits(const TH1 * h) {
20  Bool_t hasLimits = h->GetXaxis()->GetXmin() < h->GetXaxis()->GetXmax();
21  if (h->GetDimension() > 1) hasLimits &= h->GetYaxis()->GetXmin() < h->GetYaxis()->GetXmax();
22  if (h->GetDimension() > 2) hasLimits &= h->GetZaxis()->GetXmin() < h->GetZaxis()->GetXmax();
23  return hasLimits;
24 }
25 
26 /// Function performing the actual merge
27 Bool_t TH1Merger::operator() () {
28 
29 
30  EMergerType type = ExamineHistograms();
31 
32  if (gDebug) Info("Merge","Histogram Merge type is %d and new axis flag is %d",(int) type,(int) fNewAxisFlag);
33 
34  if (type == kNotCompatible) return kFALSE;
35 
36  if (type == kAllSameAxes)
37  return SameAxesMerge();
38 
39  if (type == kAllLabel)
40  return LabelMerge();
41 
42  if (type == kAllNoLimits)
43  return BufferMerge();
44 
45  if (type == kAutoP2HaveLimits || (type == kAutoP2NeedLimits && AutoP2BufferMerge()))
46  return AutoP2Merge();
47 
48  // this is the mixed case - more complicated
49  if (type == kHasNewLimits) {
50  // we need to define some new axes
51  DefineNewAxes();
52  // we might need to merge some histogram using the buffer
53  Bool_t ret = BufferMerge();
54  // if ret is true the merge is completed and we can exit
55  if (ret) return kTRUE;
56  // in the other cases then we merge using FindBin
57  return DifferentAxesMerge();
58  }
59  Error("TH1Merger","Unknown type of Merge for histogram %s",fH0->GetName());
60  return kFALSE;
61 }
62 
63 /////////////////////////////////////////////////////////////////////////////////////////
64 /// Determine final boundaries and number of bins for histograms created in power-of-2
65 /// autobin mode.
66 ///
67 /// Return kTRUE if compatible, updating fNewXaxis accordingly; return kFALSE if something
68 /// wrong.
69 ///
70 /// The histograms are not merge-compatible if
71 ///
72 /// 1. have different variable-size bins
73 /// 2. larger bin size is not an integer multiple of the smaller one
74 /// 3. the final estimated range is smalle then the bin size
75 ///
76 
77 Bool_t TH1Merger::AutoP2BuildAxes(TH1 *h)
78 {
79  // They must be both defined
80  if (!h) {
81  Error("AutoP2BuildAxes", "undefined histogram: %p", h);
82  return kFALSE;
83  }
84 
85  // They must be created in power-of-2 autobin mode
86  if (!h->TestBit(TH1::kAutoBinPTwo)) {
87  Error("AutoP2BuildAxes", "not in autobin-power-of-2 mode!");
88  return kFALSE;
89  }
90 
91  // Point to axes
92  TAxis *a0 = &fNewXAxis, *a1 = h->GetXaxis();
93 
94  // This is for future merging of detached ranges (only possible if no over/underflows)
95  Bool_t canextend = (h->GetBinContent(0) > 0 || h->GetBinContent(a1->GetNbins() + 1) > 0) ? kFALSE : kTRUE;
96 
97  // The first time we just copy the boundaries and bins
98  if (a0->GetFirst() == a0->GetLast()) {
99  a0->Set(a1->GetNbins(), a1->GetXmin(), a1->GetXmax());
100  // This is for future merging of detached ranges (only possible if no over/underflows)
101  a0->SetCanExtend(canextend);
102  return kTRUE;
103  }
104 
105  // Bin sizes must be in integer ratio
106  Double_t bwmax = (a0->GetXmax() - a0->GetXmin()) / a0->GetNbins();
107  Double_t bwmin = (a1->GetXmax() - a1->GetXmin()) / a1->GetNbins();
108  Bool_t b0 = kTRUE;
109  if (bwmin > bwmax) {
110  std::swap(bwmax, bwmin);
111  b0 = kFALSE;
112  }
113  if (!(bwmin > 0.)) {
114  PRINTRANGE(a0, a1, h->GetName());
115  Error("AutoP2BuildAxes", "minimal bin width negative or null: %f", bwmin);
116  return kFALSE;
117  }
118 
119  Double_t rt;
120  Double_t re = std::modf(bwmax / bwmin, &rt);
121  if (re > std::numeric_limits<Double_t>::epsilon()) {
122  PRINTRANGE(a0, a1, h->GetName());
123  Error("AutoP2BuildAxes", "bin widths not in integer ratio: %f", re);
124  return kFALSE;
125  }
126 
127  // Range of the merged histogram, taking into account overlaps
128  Bool_t domax = kFALSE;
129  Double_t xmax, xmin;
130  if (a0->GetXmin() < a1->GetXmin()) {
131  if (a0->GetXmax() < a1->GetXmin()) {
132  if (!a0->CanExtend() || !canextend) {
133  PRINTRANGE(a0, a1, h->GetName());
134  Error("AutoP2BuildAxes", "ranges are disconnected and under/overflows: cannot merge");
135  return kFALSE;
136  }
137  xmax = a1->GetXmax();
138  xmin = a0->GetXmin();
139  domax = b0 ? kTRUE : kFALSE;
140  } else {
141  if (a0->GetXmax() >= a1->GetXmax()) {
142  xmax = a1->GetXmax();
143  xmin = a1->GetXmin();
144  domax = !b0 ? kTRUE : kFALSE;
145  } else {
146  xmax = a0->GetXmax();
147  xmin = a1->GetXmin();
148  domax = !b0 ? kTRUE : kFALSE;
149  }
150  }
151  } else {
152  if (a1->GetXmax() < a0->GetXmin()) {
153  if (!a0->CanExtend() || !canextend) {
154  PRINTRANGE(a0, a1, h->GetName());
155  Error("AutoP2BuildAxes", "ranges are disconnected and under/overflows: cannot merge");
156  return kFALSE;
157  }
158  xmax = a0->GetXmax();
159  xmin = a1->GetXmin();
160  domax = !b0 ? kTRUE : kFALSE;
161  } else {
162  if (a1->GetXmax() >= a0->GetXmax()) {
163  xmax = a0->GetXmax();
164  xmin = a0->GetXmin();
165  domax = b0 ? kTRUE : kFALSE;
166  } else {
167  xmax = a1->GetXmax();
168  xmin = a0->GetXmin();
169  domax = b0 ? kTRUE : kFALSE;
170  }
171  }
172  }
173  Double_t range = xmax - xmin;
174 
175  re = std::modf(range / bwmax, &rt);
176  if (rt < 1.) {
177  PRINTRANGE(a0, a1, h->GetName());
178  Error("MergeCompatibleHistograms", "range smaller than bin width: %f %f %f", range, bwmax, rt);
179  return kFALSE;
180  }
181  if (re > std::numeric_limits<Double_t>::epsilon()) {
182  if (domax) {
183  xmax -= bwmax * re;
184  } else {
185  xmin += bwmax * re;
186  }
187  }
188  // Number of bins
189  Int_t nb = (Int_t)rt;
190 
191  // Set the result
192  a0->Set(nb, xmin, xmax);
193 
194  // This is for future merging of detached ranges (only possible if no over/underflows)
195  if (!a0->CanExtend())
196  a0->SetCanExtend(canextend);
197 
198  // Done
199  return kTRUE;
200 }
201 
202 /**
203  Examine the list of histograms to find out which type of Merge we need to do
204  Pass the input list containing the histogram to merge and h0 which is the initial histogram
205  on which all the histogram of the list will be merged
206  This are the possible cases:
207  - 1. All histogram have the same axis (allSameLimits = true)
208  - 2. Histogram have different axis but compatible (allSameLimits = false) and sameLimitsX,Y,Z specifies which axis
209  has different limits
210  - 3. Histogram do not have limits (so the Buffer is used) allHaveLimits = false
211  - 3b. One histogram has limits the other not : allHaveLimits = false AND initialLimitsFound = true
212  - 4. Histogram Have labels = allHaveLabels = true
213 
214 
215 */
216 TH1Merger::EMergerType TH1Merger::ExamineHistograms() {
217 
218 
219 
220  Bool_t initialLimitsFound = kFALSE;
221  Bool_t allHaveLabels = kTRUE; // assume all histo have labels and check later
222  Bool_t allHaveLimits = kTRUE;
223  Bool_t allSameLimits = kTRUE;
224  Bool_t sameLimitsX = kTRUE;
225  Bool_t sameLimitsY = kTRUE;
226  Bool_t sameLimitsZ = kTRUE;
227  Bool_t foundLabelHist = kFALSE;
228  Bool_t haveWeights = kFALSE;
229 
230  Bool_t isAutoP2 = kFALSE;
231 
232  // TAxis newXAxis;
233  // TAxis newYAxis;
234  // TAxis newZAxis;
235 
236  TIter next(&fInputList);
237  TH1 * h = fH0; // start with fH0
238 
239  int dimension = fH0->GetDimension();
240 
241  isAutoP2 = fH0->TestBit(TH1::kAutoBinPTwo) ? kTRUE : kFALSE;
242 
243  // if the option alphanumeric merge is set
244  // we assume we do not have labels
245  if (fNoLabelMerge) allHaveLabels = kFALSE;
246 
247  // start looping on the histograms
248 
249  do {
250 
251  // check first histogram compatibility
252  if (h != fH0) {
253  if (h->GetDimension() != dimension) {
254  Error("Merge", "Cannot merge histogram - dimensions are different\n "
255  "%s has dim=%d and %s has dim=%d",fH0->GetName(),dimension,h->GetName(),h->GetDimension());
256  return kNotCompatible;
257  }
258  }
259 
260  // check if one of the histogram is weighted
261  haveWeights |= h->GetSumw2N() != 0;
262 
263  // do not skip anymore empty histograms
264  // since are used to set the limits
265  Bool_t hasLimits = TH1Merger::AxesHaveLimits(h);
266  allHaveLimits = allHaveLimits && hasLimits;
267  allSameLimits &= allHaveLimits;
268 
269  if (isAutoP2 && !h->TestBit(TH1::kAutoBinPTwo)) {
270  Error("Merge", "Cannot merge histogram - some are in autobin-power-of-2 mode, but not %s!", h->GetName());
271  return kNotCompatible;
272  }
273  if (!isAutoP2 && h->TestBit(TH1::kAutoBinPTwo)) {
274  Error("Merge", "Cannot merge histogram - %s is in autobin-power-of-2 mode, but not the previous ones",
275  h->GetName());
276  return kNotCompatible;
277  }
278 
279  if (hasLimits) {
280  h->BufferEmpty();
281 
282 // // this is done in case the first histograms are empty and
283 // // the histogram have different limits
284 // #ifdef LATER
285 // if (firstHistWithLimits ) {
286 // // set axis limits in the case the first histogram did not have limits
287 // if (h != this && !SameLimitsAndNBins( fXaxis, *h->GetXaxis()) ) {
288 // if (h->GetXaxis()->GetXbins()->GetSize() != 0) fXaxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXbins()->GetArray());
289 // else fXaxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax());
290 // }
291 // firstHistWithLimits = kFALSE;
292 // }
293 // #endif
294 
295  // this is executed the first time an histogram with limits is found
296  // to set some initial values on the new axis
297  if (!initialLimitsFound) {
298  initialLimitsFound = kTRUE;
299  if (h->GetXaxis()->GetXbins()->GetSize() != 0)
300  fNewXAxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXbins()->GetArray());
301  else
302  fNewXAxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax());
303  if (dimension > 1) {
304  if (h->GetYaxis()->GetXbins()->GetSize() != 0)
305  fNewYAxis.Set(h->GetYaxis()->GetNbins(), h->GetYaxis()->GetXbins()->GetArray());
306  else
307  fNewYAxis.Set(h->GetYaxis()->GetNbins(), h->GetYaxis()->GetXmin(), h->GetYaxis()->GetXmax());
308  }
309  if (dimension > 2) {
310  if (h->GetZaxis()->GetXbins()->GetSize() != 0)
311  fNewZAxis.Set(h->GetZaxis()->GetNbins(), h->GetZaxis()->GetXbins()->GetArray());
312  else
313  fNewZAxis.Set(h->GetZaxis()->GetNbins(), h->GetZaxis()->GetXmin(), h->GetZaxis()->GetXmax());
314 
315  }
316  }
317  else {
318  // check first if histograms have same bins in X
319  if (!TH1::SameLimitsAndNBins(fNewXAxis, *(h->GetXaxis())) ) {
320  sameLimitsX = kFALSE;
321  // recompute the limits in this case the optimal limits
322  // The condition to works is that the histogram have same bin with
323  // and one common bin edge
324  if (!TH1::RecomputeAxisLimits(fNewXAxis, *(h->GetXaxis()))) {
325  Error("Merge", "Cannot merge histograms - limits are inconsistent:\n "
326  "first: %s (%d, %f, %f), second: %s (%d, %f, %f)", fH0->GetName(),
327  fNewXAxis.GetNbins(), fNewXAxis.GetXmin(), fNewXAxis.GetXmax(),
328  h->GetName(),h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXmin(),
329  h->GetXaxis()->GetXmax());
330  return kNotCompatible;
331  }
332  }
333  // check first if histograms have same bins in Y
334  if (dimension > 1 && !TH1::SameLimitsAndNBins(fNewYAxis, *(h->GetYaxis()))) {
335  sameLimitsY = kFALSE;
336  // recompute in this case the optimal limits
337  // The condition to works is that the histogram have same bin with
338  // and one common bin edge
339  if (!TH1::RecomputeAxisLimits(fNewYAxis, *(h->GetYaxis()))) {
340  Error("Merge", "Cannot merge histograms - limits are inconsistent:\n "
341  "first: %s (%d, %f, %f), second: %s (%d, %f, %f)", fH0->GetName(),
342  fNewYAxis.GetNbins(), fNewYAxis.GetXmin(), fNewYAxis.GetXmax(),
343  h->GetName(), h->GetYaxis()->GetNbins(), h->GetYaxis()->GetXmin(),
344  h->GetYaxis()->GetXmax());
345  return kNotCompatible;
346  }
347  }
348  if(dimension > 2 && !fH0->SameLimitsAndNBins(fNewZAxis, *(h->GetZaxis()))) {
349  sameLimitsZ = kFALSE;
350  if (!TH1::RecomputeAxisLimits(fNewZAxis, *(h->GetZaxis()))) {
351  Error("Merge", "Cannot merge histograms - limits are inconsistent:\n "
352  "first: %s (%d, %f, %f), second: %s (%d, %f, %f)", fH0->GetName(),
353  fNewZAxis.GetNbins(), fNewZAxis.GetXmin(), fNewZAxis.GetXmax(),
354  h->GetName(),h->GetZaxis()->GetNbins(), h->GetZaxis()->GetXmin(),
355  h->GetZaxis()->GetXmax());
356  return kNotCompatible;
357  }
358  }
359  allSameLimits = sameLimitsX && sameLimitsY && sameLimitsZ;
360 
361 
362  }
363  }
364  Bool_t histoIsEmpty = h->IsEmpty();
365  // std::cout << "considering histo " << h->GetName() << " labels - " << allHaveLabels << " is empty "
366  // << histoIsEmpty << std::endl;
367 
368  // if histogram is empty it does not matter if it has label or not
369  if (allHaveLabels && !histoIsEmpty) {
370  THashList* hlabels=h->GetXaxis()->GetLabels();
371  Bool_t haveOneLabel = (hlabels != nullptr);
372  // do here to print message only one time
373  if (foundLabelHist && allHaveLabels && !haveOneLabel) {
374  Warning("Merge","Not all histograms have labels. I will ignore labels,"
375  " falling back to bin numbering mode.");
376  }
377 
378  allHaveLabels &= (haveOneLabel);
379  // for the error message
380  if (haveOneLabel) foundLabelHist = kTRUE;
381 
382  if (foundLabelHist && gDebug)
383  Info("TH1Merger::ExamineHistogram","Histogram %s has labels",h->GetName() );
384 
385  // If histograms have labels but CanExtendAllAxes() is false
386  // use bin center mode
387  if (allHaveLabels && !fH0->CanExtendAllAxes()) {
388  // special case for this histogram when is empty
389  // and axis cannot be extended (because it is the default)
390  if ( fH0->IsEmpty() ) {
391  if (gDebug)
392  Info("TH1Merger::ExamineHistogram","Histogram %s to be merged is empty and we are merging with %s that has labels. Force the axis to be extended",fH0->GetName(),h->GetName());
393  UInt_t bitMaskX = fH0->GetXaxis()->CanBeAlphanumeric() & TH1::kXaxis;
394  UInt_t bitMaskY = (fH0->GetYaxis()->CanBeAlphanumeric() << 1 ) & TH1::kYaxis;
395  UInt_t bitMaskZ = (fH0->GetZaxis()->CanBeAlphanumeric() << 2 ) & TH1::kZaxis;
396  fH0->SetCanExtend(bitMaskX | bitMaskY | bitMaskZ );
397  }
398  if (!fH0->CanExtendAllAxes()) {
399  if (gDebug)
400  Info("TH1Merger::ExamineHistogram","Histogram %s to be merged has label but axis cannot be extended - using bin numeric mode to merge. Call TH1::SetExtendAllAxes() if want to merge using label mode",fH0->GetName());
401  allHaveLabels = kFALSE;
402  }
403  }
404  // I could add a check if histogram contains bins without a label
405  // and with non-zero bin content
406  // Do we want to support this ???
407  // only in case the !h->CanExtendAllAxes()
408  if (allHaveLabels && !h->CanExtendAllAxes()) {
409  // count number of bins with non-null content
410  Int_t non_zero_bins = 0;
411  Int_t nbins = h->GetXaxis()->GetNbins();
412  if (nbins > hlabels->GetEntries() ) {
413  for (Int_t i = 1; i <= nbins; i++) {
414  if (h->RetrieveBinContent(i) != 0 || (fH0->fSumw2.fN && h->GetBinError(i) != 0) ) {
415  non_zero_bins++;
416  }
417  }
418  if (non_zero_bins > hlabels->GetEntries() ) {
419  Warning("TH1Merger::ExamineHistograms","Histogram %s contains non-empty bins without labels - falling back to bin numbering mode",h->GetName() );
420  allHaveLabels = kFALSE;
421  }
422  }
423  }
424  }
425  if (gDebug)
426  Info("TH1Merger::ExamineHistogram","Examine histogram %s - labels %d - same limits %d - axis found %d",h->GetName(),allHaveLabels,allSameLimits,initialLimitsFound );
427 
428  } while ( ( h = dynamic_cast<TH1*> ( next() ) ) != NULL );
429 
430  if (!h && (*next) ) {
431  Error("Merge","Attempt to merge object of class: %s to a %s",
432  (*next)->ClassName(),fH0->ClassName());
433  return kNotCompatible;
434  }
435 
436  // in case of weighted histogram set Sumw2() on fH0 is is not weighted
437  if (haveWeights && fH0->GetSumw2N() == 0)
438  fH0->Sumw2();
439 
440  // AutoP2
441  if (isAutoP2) {
442  if (allHaveLimits)
443  return kAutoP2HaveLimits;
444  return kAutoP2NeedLimits;
445  }
446 
447  // return the type of merge
448  if (allHaveLabels) return kAllLabel;
449  if (allSameLimits) return kAllSameAxes;
450  if (!initialLimitsFound) {
451  R__ASSERT(!allHaveLimits);
452  // case where no limits are found and the buffer is used
453  return kAllNoLimits;
454  }
455  // remaining case should be the mixed one. Some histogram have limits some not
456  fNewAxisFlag = 0;
457  if (!sameLimitsX) fNewAxisFlag |= TH1::kXaxis;
458  if (!sameLimitsY) fNewAxisFlag |= TH1::kYaxis;
459  if (!sameLimitsZ) fNewAxisFlag |= TH1::kZaxis;
460 
461  // we need to set the flag also in case this histogram has no limits
462  // we need to set explicitly the flag to re-define a new axis
463  if (fH0->GetXaxis()->GetXmin() >= fH0->GetXaxis()->GetXmax()) fNewAxisFlag |= TH1::kXaxis;
464  if (dimension > 1 && fH0->GetYaxis()->GetXmin() >= fH0->GetYaxis()->GetXmax()) fNewAxisFlag |= TH1::kYaxis;
465  if (dimension > 2 && fH0->GetZaxis()->GetXmin() >= fH0->GetZaxis()->GetXmax()) fNewAxisFlag |= TH1::kZaxis;
466 
467 
468  return kHasNewLimits;
469 
470 }
471 
472 /**
473  Function to define new histogram axis when merging
474  It is call only in case of merging with different axis or with the
475  buffer (kHasNewLimits)
476 */
477 
478 void TH1Merger::DefineNewAxes() {
479 
480  // first we need to create a copy of the histogram in case is not empty
481 
482  if (!fH0->IsEmpty() ) {
483  Bool_t mustCleanup = fH0->TestBit(kMustCleanup);
484  if (mustCleanup) fH0->ResetBit(kMustCleanup);
485  fHClone = (TH1*)fH0->IsA()->New();
486  fHClone->SetDirectory(0);
487  fH0->Copy(*fHClone);
488  if (mustCleanup) fH0->SetBit(kMustCleanup);
489  fH0->BufferEmpty(1); // To remove buffer.
490  fH0->Reset(); // BufferEmpty sets limits so we can't use it later.
491  fH0->SetEntries(0);
492  fInputList.AddFirst(fHClone);
493 
494  }
495 
496  bool newLimitsX = (fNewAxisFlag & TH1::kXaxis);
497  bool newLimitsY = (fNewAxisFlag & TH1::kYaxis);
498  bool newLimitsZ = (fNewAxisFlag & TH1::kZaxis);
499  if (newLimitsX) {
500  fH0->fXaxis.SetRange(0,0);
501  if (fNewXAxis.GetXbins()->GetSize() != 0)
502  fH0->fXaxis.Set(fNewXAxis.GetNbins(),fNewXAxis.GetXbins()->GetArray());
503  else
504  fH0->fXaxis.Set(fNewXAxis.GetNbins(),fNewXAxis.GetXmin(), fNewXAxis.GetXmax());
505  }
506  if (newLimitsY) {
507  fH0->fYaxis.SetRange(0,0);
508  if (fNewYAxis.GetXbins()->GetSize() != 0)
509  fH0->fYaxis.Set(fNewYAxis.GetNbins(),fNewYAxis.GetXbins()->GetArray());
510  else
511  fH0->fYaxis.Set(fNewYAxis.GetNbins(),fNewYAxis.GetXmin(), fNewYAxis.GetXmax());
512  }
513  if (newLimitsZ) {
514  fH0->fZaxis.SetRange(0,0);
515  if (fNewZAxis.GetXbins()->GetSize() != 0)
516  fH0->fZaxis.Set(fNewZAxis.GetNbins(),fNewZAxis.GetXbins()->GetArray());
517  else
518  fH0->fZaxis.Set(fNewZAxis.GetNbins(),fNewZAxis.GetXmin(), fNewZAxis.GetXmax());
519  }
520 
521  // we need to recompute fNcells and set the array size (as in TH1::SetBins)
522  fH0->fNcells = fH0->fXaxis.GetNbins()+2;
523  if (fH0->fDimension > 1) fH0->fNcells *= fH0->fYaxis.GetNbins()+2;
524  if (fH0->fDimension > 2) fH0->fNcells *= fH0->fZaxis.GetNbins()+2;
525  fH0->SetBinsLength(fH0->fNcells);
526  if (fH0->fSumw2.fN) fH0->fSumw2.Set(fH0->fNcells);
527  // set dummy Y and Z axis for lower dim histogras
528  if (fH0->fDimension < 3) fH0->fZaxis.Set(1,0,1);
529  if (fH0->fDimension < 2) fH0->fYaxis.Set(1,0,1);
530 
531  if (gDebug) {
532  if (newLimitsX) Info("DefineNewAxis","A new X axis has been defined Nbins=%d , [%f,%f]", fH0->fXaxis.GetNbins(),
533  fH0->fXaxis.GetXmin(), fH0->fXaxis.GetXmax() );
534  if (newLimitsY) Info("DefineNewAxis","A new Y axis has been defined Nbins=%d , [%f,%f]", fH0->fYaxis.GetNbins(),
535  fH0->fYaxis.GetXmin(), fH0->fYaxis.GetXmax() );
536  if (newLimitsZ) Info("DefineNewAxis","A new Z axis has been defined Nbins=%d , [%f,%f]", fH0->fZaxis.GetNbins(),
537  fH0->fZaxis.GetXmin(), fH0->fZaxis.GetXmax() );
538  }
539 
540  return;
541 
542 }
543 
544 void TH1Merger::CopyBuffer(TH1 *hsrc, TH1 *hdes)
545 {
546  // Check inputs
547  //if (!hsrc || !hsrc->fBuffer || !hdes || !hdes->fBuffer) {
548  if (!hsrc || !hsrc->fBuffer || !hdes ) {
549  void *p1 = hsrc ? hsrc->fBuffer : 0;
550  //void *p2 = hdes ? hdes->fBuffer : 0;
551  //Warning("TH1Merger::CopyMerge", "invalid inputs: %p, %p, %p, %p -> do nothing", hsrc, hdes, p1, p2);
552  Warning("TH1Merger::CopyMerge", "invalid inputs: %p, %p, %p, -> do nothing", hsrc, hdes, p1);
553  }
554 
555  // Entries from buffers have to be filled one by one
556  // because FillN doesn't resize histograms.
557  Int_t nbentries = (Int_t)hsrc->fBuffer[0];
558  if (hdes->fDimension == 1) {
559  for (Int_t i = 0; i < nbentries; i++)
560  hdes->Fill(hsrc->fBuffer[2 * i + 2], hsrc->fBuffer[2 * i + 1]);
561  }
562  if (hdes->fDimension == 2) {
563  auto h2 = dynamic_cast<TH2 *>(hdes);
564  R__ASSERT(h2);
565  for (Int_t i = 0; i < nbentries; i++)
566  h2->Fill(hsrc->fBuffer[3 * i + 2], hsrc->fBuffer[3 * i + 3], hsrc->fBuffer[3 * i + 1]);
567  }
568  if (hdes->fDimension == 3) {
569  auto h3 = dynamic_cast<TH3 *>(hdes);
570  R__ASSERT(h3);
571  for (Int_t i = 0; i < nbentries; i++)
572  h3->Fill(hsrc->fBuffer[4 * i + 2], hsrc->fBuffer[4 * i + 3], hsrc->fBuffer[4 * i + 4],
573  hsrc->fBuffer[4 * i + 1]);
574  }
575 }
576 
577 Bool_t TH1Merger::AutoP2BufferMerge()
578 {
579 
580  TH1 *href = 0, *hist = 0;
581  TIter nextref(&fInputList);
582  if (TH1Merger::AxesHaveLimits(fH0)) {
583  href = fH0;
584  } else {
585  while ((hist = (TH1 *)nextref()) && !href) {
586  if (TH1Merger::AxesHaveLimits(hist))
587  href = hist;
588  }
589  }
590  Bool_t resetfH0 = kFALSE;
591  if (!href) {
592  // Merge all histograms to fH0 and do a final projection
593  href = fH0;
594  } else {
595  if (href != fH0) {
596  // Temporary add fH0 to the list for buffer merging
597  fInputList.Add(fH0);
598  resetfH0 = kTRUE;
599  }
600  }
601  TIter next(&fInputList);
602  while ((hist = (TH1 *)next())) {
603  if (!TH1Merger::AxesHaveLimits(hist) && hist->fBuffer) {
604  if (gDebug)
605  Info("AutoP2BufferMerge", "merging buffer of %s into %s", hist->GetName(), href->GetName());
606  CopyBuffer(hist, href);
607  fInputList.Remove(hist);
608  }
609  }
610  // Final projection
611  if (href->fBuffer)
612  href->BufferEmpty(1);
613  // Reset fH0, if already added, to avoid double counting
614  if (resetfH0)
615  fH0->Reset("ICES");
616  // Done, all histos have been processed
617  return kTRUE;
618 }
619 
620 Bool_t TH1Merger::AutoP2Merge()
621 {
622 
623  Double_t stats[TH1::kNstat], totstats[TH1::kNstat];
624  for (Int_t i = 0; i < TH1::kNstat; i++) {
625  totstats[i] = stats[i] = 0;
626  }
627 
628  TIter next(&fInputList);
629  TH1 *hist = 0;
630  // Calculate boundaries and bins
631  Double_t xmin = 0., xmax = 0.;
632  if (!(fH0->IsEmpty())) {
633  hist = fH0;
634  } else {
635  while ((hist = (TH1 *)next())) {
636  if (!hist->IsEmpty())
637  break;
638  }
639  }
640 
641  if (!hist) {
642  if (gDebug)
643  Info("TH1Merger::AutoP2Merge", "all histograms look empty!");
644  return kFALSE;
645  }
646 
647  // Start building the axes from the reference histogram
648  if (!AutoP2BuildAxes(hist)) {
649  Error("TH1Merger::AutoP2Merge", "cannot create axes from %s", hist->GetName());
650  return kFALSE;
651  }
652  TH1 *h = 0;
653  while ((h = (TH1 *)next())) {
654  if (!AutoP2BuildAxes(h)) {
655  Error("TH1Merger::AutoP2Merge", "cannot merge histogram %s: not merge compatible", h->GetName());
656  return kFALSE;
657  }
658  }
659  xmin = fNewXAxis.GetXmin();
660  xmax = fNewXAxis.GetXmax();
661  Int_t nbins = fNewXAxis.GetNbins();
662 
663  // Prepare stats
664  fH0->GetStats(totstats);
665  // Clone fH0 and add it to the list
666  if (!fH0->IsEmpty())
667  fInputList.Add(fH0->Clone());
668 
669  // reset fH0
670  fH0->Reset("ICES");
671  // Set the new boundaries
672  fH0->SetBins(nbins, xmin, xmax);
673 
674  next.Reset();
675  Double_t nentries = 0.;
676  while ((hist = (TH1 *)next())) {
677  // process only if the histogram has limits; otherwise it was processed before
678  // in the case of an existing buffer (see if statement just before)
679 
680  if (gDebug)
681  Info("TH1Merger::AutoP2Merge", "merging histogram %s into %s (entries: %f)", hist->GetName(), fH0->GetName(),
682  hist->GetEntries());
683 
684  // skip empty histograms
685  if (hist->IsEmpty())
686  continue;
687 
688  // import statistics
689  hist->GetStats(stats);
690  for (Int_t i = 0; i < TH1::kNstat; i++)
691  totstats[i] += stats[i];
692  nentries += hist->GetEntries();
693 
694  // Int_t nx = hist->GetXaxis()->GetNbins();
695  // loop on bins of the histogram and do the merge
696  for (Int_t ibin = 0; ibin < hist->fNcells; ibin++) {
697 
698  Double_t cu = hist->RetrieveBinContent(ibin);
699  Double_t e1sq = TMath::Abs(cu);
700  if (fH0->fSumw2.fN)
701  e1sq = hist->GetBinErrorSqUnchecked(ibin);
702 
703  Double_t xu = hist->GetBinCenter(ibin);
704  Int_t jbin = fH0->FindBin(xu);
705 
706  fH0->AddBinContent(jbin, cu);
707  if (fH0->fSumw2.fN)
708  fH0->fSumw2.fArray[jbin] += e1sq;
709  }
710  }
711  // copy merged stats
712  fH0->PutStats(totstats);
713  fH0->SetEntries(nentries);
714 
715  return kTRUE;
716 }
717 
718 Bool_t TH1Merger::BufferMerge()
719 {
720 
721  TIter next(&fInputList);
722  while (TH1* hist = (TH1*)next()) {
723  // support also case where some histogram have limits and some have the buffer
724  if ( !TH1Merger::AxesHaveLimits(hist) && hist->fBuffer ) {
725 
726  if (gDebug)
727  Info("TH1Merger::BufferMerge","Merging histogram %s into %s",hist->GetName(), fH0->GetName() );
728  CopyBuffer(hist, fH0);
729  fInputList.Remove(hist);
730  }
731  }
732  // return true if the merge is completed
733  if (fInputList.GetSize() == 0) {
734  // all histo have been merged
735  return kTRUE;
736  }
737  // we need to reset the buffer in case of merging later on
738  // is this really needed ???
739  if (fH0->fBuffer) fH0->BufferEmpty(1);
740 
741  return kFALSE;
742 }
743 
744 Bool_t TH1Merger::SameAxesMerge() {
745 
746 
747  Double_t stats[TH1::kNstat], totstats[TH1::kNstat];
748  for (Int_t i=0;i<TH1::kNstat;i++) {
749  totstats[i] = stats[i] = 0;
750  }
751  fH0->GetStats(totstats);
752  Double_t nentries = fH0->GetEntries();
753 
754  TIter next(&fInputList);
755  while (TH1* hist=(TH1*)next()) {
756  // process only if the histogram has limits; otherwise it was processed before
757  // in the case of an existing buffer (see if statement just before)
758 
759  if (gDebug)
760  Info("TH1Merger::SameAxesMerge","Merging histogram %s into %s",hist->GetName(), fH0->GetName() );
761 
762  // skip empty histograms
763  if (hist->IsEmpty()) continue;
764 
765  // import statistics
766  hist->GetStats(stats);
767  for (Int_t i=0; i<TH1::kNstat; i++)
768  totstats[i] += stats[i];
769  nentries += hist->GetEntries();
770 
771  //Int_t nx = hist->GetXaxis()->GetNbins();
772  // loop on bins of the histogram and do the merge
773  for (Int_t ibin = 0; ibin < hist->fNcells; ibin++) {
774 
775  Double_t cu = hist->RetrieveBinContent(ibin);
776  Double_t e1sq = TMath::Abs(cu);
777  if (fH0->fSumw2.fN) e1sq= hist->GetBinErrorSqUnchecked(ibin);
778 
779  fH0->AddBinContent(ibin,cu);
780  if (fH0->fSumw2.fN) fH0->fSumw2.fArray[ibin] += e1sq;
781 
782  }
783  }
784  //copy merged stats
785  fH0->PutStats(totstats);
786  fH0->SetEntries(nentries);
787 
788  return kTRUE;
789 }
790 
791 
792 /**
793  Merged histogram when axis can be different.
794  Histograms are merged looking at bin center positions
795 
796  */
797 Bool_t TH1Merger::DifferentAxesMerge() {
798 
799  Double_t stats[TH1::kNstat], totstats[TH1::kNstat];
800  for (Int_t i=0;i<TH1::kNstat;i++) {totstats[i] = stats[i] = 0;}
801  fH0->GetStats(totstats);
802  Double_t nentries = fH0->GetEntries();
803 
804  TIter next(&fInputList);
805  while (TH1* hist=(TH1*)next()) {
806 
807  if (gDebug)
808  Info("TH1Merger::DifferentAxesMerge","Merging histogram %s into %s",hist->GetName(), fH0->GetName() );
809 
810  // skip empty histograms
811  if (hist->IsEmpty()) continue;
812 
813  // import statistics
814  hist->GetStats(stats);
815  for (Int_t i=0;i<TH1::kNstat;i++)
816  totstats[i] += stats[i];
817  nentries += hist->GetEntries();
818 
819  // loop on bins of the histogram and do the merge
820  for (Int_t ibin = 0; ibin < hist->fNcells; ibin++) {
821 
822  Double_t cu = hist->RetrieveBinContent(ibin);
823  Double_t e1sq = TMath::Abs(cu);
824  if (fH0->fSumw2.fN) e1sq= hist->GetBinErrorSqUnchecked(ibin);
825 
826  // if bin is empty we can skip it
827  if (cu == 0 && e1sq == 0) continue;
828 
829  Int_t binx,biny,binz;
830  hist->GetBinXYZ(ibin, binx, biny, binz);
831 
832  // case of underflow/overflows in the histogram being merged
833  if (binx <= 0 || binx >= hist->GetNbinsX() + 1) {
834  if (fH0->fXaxis.CanExtend() || ( hist->fXaxis.GetBinCenter(binx) > fH0->fXaxis.GetXmin() && hist->fXaxis.GetBinCenter(binx) < fH0->fXaxis.GetXmax()) ) {
835  Error("TH1Merger::DifferentAxesMerge", "Cannot merge histograms - the histograms %s can extend the X axis or have"
836  " different limits and underflows/overflows are present in the histogram %s.",fH0->GetName(),hist->GetName());
837  return kFALSE;
838  }
839  }
840  if (biny <= 0 || biny >= hist->GetNbinsY() + 1) {
841  if (fH0->fYaxis.CanExtend() || ( hist->fYaxis.GetBinCenter(biny) > fH0->fYaxis.GetXmin() && hist->fYaxis.GetBinCenter(biny) < fH0->fYaxis.GetXmax()) ) {
842  Error("TH1Merger::DifferentAxesMerge", "Cannot merge histograms - the histograms %s can extend the Y axis or have"
843  " different limits and underflows/overflows are present in the histogram %s.",fH0->GetName(),hist->GetName());
844  return kFALSE;
845  }
846  }
847  if (binz <= 0 || binz >= hist->GetNbinsZ() + 1) {
848  if (fH0->fZaxis.CanExtend() || ( hist->fZaxis.GetBinCenter(binz) > fH0->fZaxis.GetXmin() && hist->fZaxis.GetBinCenter(binz) < fH0->fZaxis.GetXmax()) ) {
849  Error("TH1Merger::DifferentAxesMerge", "Cannot merge histograms - the histograms %s can extend the Z axis or have"
850  " different limits and underflows/overflows are present in the histogram %s.",fH0->GetName(),hist->GetName());
851  return kFALSE;
852  }
853  }
854 
855  Int_t ix = 0;
856  Int_t iy = 0;
857  Int_t iz = 0;
858 
859  // we can extend eventually the axis if histogram is capable of doing it
860  // by using FindBin
861  ix = fH0->fXaxis.FindBin(hist->GetXaxis()->GetBinCenter(binx));
862  if (fH0->fDimension > 1)
863  iy = fH0->fYaxis.FindBin(hist->GetYaxis()->GetBinCenter(biny));
864  if (fH0->fDimension > 2)
865  iz = fH0->fZaxis.FindBin(hist->GetZaxis()->GetBinCenter(binz));
866 
867  Int_t ib = fH0->GetBin(ix,iy,iz);
868  if (ib < 0 || ib > fH0->fNcells) {
869  Fatal("TH1Merger::LabelMerge","Fatal error merging histogram %s - bin number is %d and array size is %d",
870  fH0->GetName(), ib,fH0->fNcells);
871  }
872 
873  fH0->AddBinContent(ib,cu);
874  if (fH0->fSumw2.fN) fH0->fSumw2.fArray[ib] += e1sq;
875  }
876  }
877  //copy merged stats
878  fH0->PutStats(totstats);
879  fH0->SetEntries(nentries);
880 
881  return kTRUE;
882 }
883 
884 /**
885  Find a duplicate labels in an axis label list
886 */
887 Bool_t TH1Merger::HasDuplicateLabels(const THashList * labels) {
888 
889  if (!labels) return kFALSE;
890 
891  for (const auto * obj: *labels) {
892  auto objList = labels->GetListForObject(obj);
893  //objList->ls();
894  if (objList->GetSize() > 1 ) {
895  // check here if in the list we have duplicates
896  std::unordered_set<std::string> s;
897  for ( const auto * o: *objList) {
898  auto ret = s.insert(std::string(o->GetName() ));
899  if (!ret.second) return kTRUE;
900  }
901  }
902  }
903  return kFALSE;
904 }
905 
906 /**
907  Check if histogram has duplicate labels
908  Return an integer with bit set correponding
909  on the axis that has duplicate labels
910  e.g. duplicate labels on x axis : return 1
911  duplicate labels on x and z axis : return 5
912 
913 */
914 Int_t TH1Merger::CheckForDuplicateLabels(const TH1 * hist) {
915 
916  R__ASSERT(hist != nullptr);
917 
918  auto labelsX = hist->GetXaxis()->GetLabels();
919  auto labelsY = hist->GetYaxis()->GetLabels();
920  auto labelsZ = hist->GetZaxis()->GetLabels();
921 
922  Int_t res = 0;
923  if (HasDuplicateLabels(labelsX) ) {
924  Warning("TH1Merger::CheckForDuplicateLabels","Histogram %s has duplicate labels in the x axis. "
925  "Bin contents will be merged in a single bin",hist->GetName());
926  res |= 1;
927  }
928  if (HasDuplicateLabels(labelsY) ) {
929  Warning("TH1Merger::CheckForDuplicateLabels","Histogram %s has duplicate labels in the y axis. "
930  "Bin contents will be merged in a single bin",hist->GetName());
931  res |= 2;
932  }
933  if (HasDuplicateLabels(labelsZ) ) {
934  Warning("TH1Merger::CheckForDuplicateLabels","Histogram %s has duplicate labels in the z axis. "
935  "Bin contents will be merged in a single bin",hist->GetName());
936  res |= 4;
937  }
938  return res;
939 }
940 
941 /**
942  Merge histograms with labels
943 */
944 Bool_t TH1Merger::LabelMerge() {
945 
946  Double_t stats[TH1::kNstat], totstats[TH1::kNstat];
947  for (Int_t i=0;i<TH1::kNstat;i++) {totstats[i] = stats[i] = 0;}
948  fH0->GetStats(totstats);
949  Double_t nentries = fH0->GetEntries();
950 
951  // check for duplicate labels
952  if (!fNoCheck && nentries > 0) CheckForDuplicateLabels(fH0);
953 
954  TIter next(&fInputList);
955  while (TH1* hist=(TH1*)next()) {
956 
957  if (gDebug)
958  Info("TH1Merger::LabelMerge","Merging histogram %s into %s",hist->GetName(), fH0->GetName() );
959 
960  // skip empty histograms
961  if (hist->IsEmpty()) continue;
962 
963  // import statistics
964  hist->GetStats(stats);
965  for (Int_t i=0;i<TH1::kNstat;i++)
966  totstats[i] += stats[i];
967  nentries += hist->GetEntries();
968 
969  auto labelsX = hist->GetXaxis()->GetLabels();
970  auto labelsY = hist->GetYaxis()->GetLabels();
971  auto labelsZ = hist->GetZaxis()->GetLabels();
972  R__ASSERT(!( labelsX == nullptr && labelsY == nullptr && labelsZ == nullptr));
973 
974  // check if histogram has duplicate labels
975  if (!fNoCheck && hist->GetEntries() > 0) CheckForDuplicateLabels(hist);
976 
977  // loop on bins of the histogram and do the merge
978  for (Int_t ibin = 0; ibin < hist->fNcells; ibin++) {
979 
980  Double_t cu = hist->RetrieveBinContent(ibin);
981  Double_t e1sq = cu;
982  if (fH0->fSumw2.fN) e1sq= hist->GetBinErrorSqUnchecked(ibin);
983 
984  // if bin is empty we can skip it
985  if (cu == 0 && e1sq == 0) continue;
986 
987  Int_t binx,biny,binz;
988  hist->GetBinXYZ(ibin, binx, biny, binz);
989 
990  // here only in the case of bins with labels
991  const char * labelX = 0;
992  const char * labelY = 0;
993  const char * labelZ = 0;
994  labelX=hist->GetXaxis()->GetBinLabel(binx);
995  if (fH0->fDimension > 1) labelY = hist->GetYaxis()->GetBinLabel(biny);
996  if (fH0->fDimension > 2) labelZ = hist->GetYaxis()->GetBinLabel(binz);
997  // do we need to support case when there are bins with labels and bins without them ??
998  // this case should have been detected before when examining the histograms
999 
1000 
1001  Int_t ix = -1;
1002  Int_t iy = (fH0->fDimension > 1) ? -1 : 0;
1003  Int_t iz = (fH0->fDimension > 2) ? -1 : 0;
1004 
1005  // special case for underflow/overflows which have normally empty labels
1006  if (binx == 0 && TString(labelX) == "" ) ix = 0;
1007  if (binx == hist->fXaxis.GetNbins() +1 && TString(labelX) == "" ) ix = fH0->fXaxis.GetNbins() +1;
1008  if (fH0->fDimension > 1 ) {
1009  if (biny == 0 && TString(labelY) == "" ) iy = 0;
1010  if (biny == hist->fYaxis.GetNbins() +1 && TString(labelY) == "" ) iy = fH0->fYaxis.GetNbins() +1;
1011  }
1012  if (fH0->fDimension > 2 ) {
1013  if (binz == 0 && TString(labelZ) == "" ) iz = 0;
1014  if (binz == hist->fZaxis.GetNbins() +1 && TString(labelZ) == "" ) iz = fH0->fZaxis.GetNbins() +1;
1015  }
1016 
1017 
1018 
1019  // find corresponding case (in case bin is not overflow)
1020  if (ix == -1) {
1021  if (labelsX)
1022  ix = fH0->fXaxis.FindBin(labelX);
1023  else
1024  ix = FindFixBinNumber(binx, hist->fXaxis, fH0->fXaxis);
1025  }
1026 
1027  if (iy == -1 && fH0->fDimension> 1 ) { // check on dim should not be needed
1028  if (labelsY)
1029  iy= fH0->fYaxis.FindBin(labelY);
1030  else
1031  iy = FindFixBinNumber(biny, hist->fYaxis, fH0->fYaxis);
1032  }
1033  if (iz == -1 && fH0->fDimension> 2) {
1034  if (labelsZ)
1035  iz= fH0->fZaxis.FindBin(labelZ);
1036  else
1037  iz = FindFixBinNumber(binz, hist->fZaxis, fH0->fZaxis);
1038  }
1039 
1040  if (gDebug)
1041  Info("TH1Merge::LabelMerge","Merge bin [%d,%d,%d] with label [%s,%s,%s] into bin [%d,%d,%d]",
1042  binx,biny,binz,labelX,labelY,labelZ,ix,iy,iz);
1043 
1044  Int_t ib = fH0->GetBin(ix,iy,iz);
1045  if (ib < 0 || ib >= fH0->fNcells) {
1046  Fatal("TH1Merger::LabelMerge","Fatal error merging histogram %s - bin number is %d and array size is %d",
1047  fH0->GetName(), ib,fH0->fNcells);
1048  }
1049 
1050  fH0->AddBinContent(ib,cu);
1051  if (fH0->fSumw2.fN) fH0->fSumw2.fArray[ib] += e1sq;
1052  }
1053  }
1054  //copy merged stats
1055  fH0->PutStats(totstats);
1056  fH0->SetEntries(nentries);
1057 
1058  return kTRUE;
1059 }