GRSISort "v4.1.1.0"
An extension of the ROOT analysis Framework
Loading...
Searching...
No Matches
GHSym.cxx
Go to the documentation of this file.
1#include "GHSym.h"
2
3#include "TROOT.h"
4#include "THashList.h"
5#include "THLimitsFinder.h"
6#include "TVirtualHistPainter.h"
7#include "TObjString.h"
8#include "TVirtualPad.h"
9#include "TMath.h"
10#include "TPad.h"
11#include "TF1.h"
12#include "TF2.h"
13#include "TRandom.h"
14#include "TClass.h"
15
16#include <iostream>
17
18// Internal exceptions for the CheckConsistency method
19class DifferentDimension : public std::exception {
20};
21class DifferentNumberOfBins : public std::exception {
22};
23class DifferentAxisLimits : public std::exception {
24};
25class DifferentBinLimits : public std::exception {
26};
27class DifferentLabels : public std::exception {
28};
29
31{
32 fDimension = 2;
33}
34
35// we have to repeat the code from the default constructor here, because calling the TH1 constructor and the GHSym
36// default constructor gives an error
37GHSym::GHSym(const char* name, const char* title, Int_t nbins, Double_t low, Double_t up)
38 : TH1(name, title, nbins, low, up)
39{
40 fDimension = 2;
41 fYaxis.Set(nbins, low, up);
42 // TH1 constructor sets fNcells to nbins+2
43 // we need (nbins+2)*((nbins+2)+1)/2 cells
44 fNcells = (fNcells * (nbins + 3)) / 2;
45}
46
47GHSym::GHSym(const char* name, const char* title, Int_t nbins, const Double_t* bins)
48 : TH1(name, title, nbins, bins)
49{
50 fDimension = 2;
51 fYaxis.Set(nbins, bins);
52 // TH1 constructor sets fNcells to nbins+2
53 // we need (nbins+2)*((nbins+2)+1)/2 cells
54 fNcells = (fNcells * (nbins + 3)) / 2;
55}
56
57GHSym::GHSym(const char* name, const char* title, Int_t nbins, const Float_t* bins)
58 : TH1(name, title, nbins, bins)
59{
60 fDimension = 2;
61 fYaxis.Set(nbins, bins);
62 // TH1 constructor sets fNcells to nbins+2
63 // we need (nbins+2)*((nbins+2)+1)/2 cells
64 fNcells = (fNcells * (nbins + 3)) / 2;
65}
66
67GHSym::GHSym(const GHSym& rhs) : TH1() // NOLINT(readability-redundant-member-init)
68{
69 rhs.Copy(*this);
70}
71
72GHSym::GHSym(GHSym&& rhs) noexcept : TH1() // NOLINT(readability-redundant-member-init)
73{
74 rhs.Copy(*this);
75}
76
77Int_t GHSym::BufferEmpty(Int_t action)
78{
79 /// Fill histogram with all entries in the buffer.
80 /// action = -1 histogram is reset and refilled from the buffer (called by THistPainter::Paint)
81 /// action = 0 histogram is filled from the buffer
82 /// action = 1 histogram is filled and buffer is deleted
83 /// The buffer is automatically deleted when the number of entries
84 /// in the buffer is greater than the number of entries in the histogram
85 if(fBuffer == nullptr) {
86 return 0;
87 }
88
89 auto nbEntries = static_cast<Int_t>(fBuffer[0]);
90 if(nbEntries == 0) {
91 return 0;
92 }
93 if(nbEntries < 0 && action == 0) {
94 return 0; // histogram has been already filled from the buffer
95 }
96 Double_t* buffer = fBuffer;
97 if(nbEntries < 0) {
98 nbEntries = -nbEntries;
99 fBuffer = nullptr;
100 Reset("ICES");
101 fBuffer = buffer;
102 }
103
104 const bool xbinAuto = fXaxis.GetXmax() <= fXaxis.GetXmin();
105 const bool ybinAuto = fYaxis.GetXmax() <= fYaxis.GetXmin();
106 const bool extend = CanExtendAllAxes();
107 if(extend || xbinAuto || ybinAuto) {
108 // find min, max of entries in buffer
109 // for the symmetric matrix x- and y-range are the same
110 Double_t min = fBuffer[2];
111 Double_t max = min;
112 if(fBuffer[3] < min) {
113 min = fBuffer[3];
114 }
115 if(fBuffer[3] > max) {
116 max = fBuffer[3];
117 }
118 for(Int_t i = 1; i < nbEntries; ++i) {
119 Double_t x = fBuffer[3 * i + 2];
120 if(x < min) {
121 min = x;
122 }
123 if(x > max) {
124 max = x;
125 }
126 Double_t y = fBuffer[3 * i + 3];
127 if(y < min) {
128 min = y;
129 }
130 if(y > max) {
131 max = y;
132 }
133 }
134 if(xbinAuto || ybinAuto) {
135#if ROOT_VERSION_CODE < ROOT_VERSION(6, 40, 0)
136 THLimitsFinder::GetLimitsFinder()->FindGoodLimits(this, min, max, min, max);
137#else
138 THLimitsFinder::GetLimitsFinder()->FindGoodLimitsXY(this, min, max, min, max, xbinAuto ? 0 : fXaxis.GetNbins(), ybinAuto ? 0 : fYaxis.GetNbins());
139#endif
140 } else {
141 fBuffer = nullptr;
142 Int_t keep = fBufferSize;
143 fBufferSize = 0;
144 if(min < fXaxis.GetXmin()) {
145 RebinAxis(min, &fXaxis);
146 }
147 if(max >= fXaxis.GetXmax()) {
148 RebinAxis(max, &fXaxis);
149 }
150 if(min < fYaxis.GetXmin()) {
151 RebinAxis(min, &fYaxis);
152 }
153 if(max >= fYaxis.GetXmax()) {
154 RebinAxis(max, &fYaxis);
155 }
156 fBuffer = buffer;
157 fBufferSize = keep;
158 }
159 }
160
161 fBuffer = nullptr;
162 for(Int_t i = 0; i < nbEntries; ++i) {
163 Fill(buffer[3 * i + 2], buffer[3 * i + 3], buffer[3 * i + 1]);
164 }
165 fBuffer = buffer;
166
167 if(action > 0) {
168 delete[] fBuffer;
169 fBuffer = nullptr;
170 fBufferSize = 0;
171 } else {
172 if(nbEntries == static_cast<Int_t>(fEntries)) {
173 fBuffer[0] = -nbEntries;
174 } else {
175 fBuffer[0] = 0;
176 }
177 }
178 return nbEntries;
179}
180
181Int_t GHSym::BufferFill(Double_t x, Double_t y, Double_t w)
182{
183 if(fBuffer == nullptr) {
184 return -3;
185 }
186
187 auto nbEntries = static_cast<Int_t>(fBuffer[0]);
188 if(nbEntries < 0) {
189 nbEntries = -nbEntries;
190 fBuffer[0] = nbEntries;
191 if(fEntries > 0) {
192 Double_t* buffer = fBuffer;
193 fBuffer = nullptr;
194 Reset("ICES");
195 fBuffer = buffer;
196 }
197 }
198 if(3 * nbEntries + 3 >= fBufferSize) {
199 BufferEmpty(1);
200 return Fill(x, y, w);
201 }
202 fBuffer[3 * nbEntries + 1] = w;
203 fBuffer[3 * nbEntries + 2] = x;
204 fBuffer[3 * nbEntries + 3] = y;
205 fBuffer[0] += 1;
206
207 return -3;
208}
209
210void GHSym::Copy(TObject& obj) const
211{
212 // Copy.
213
214 TH1::Copy(obj);
215 static_cast<GHSym&>(obj).fTsumwy = fTsumwy;
216 static_cast<GHSym&>(obj).fTsumwy2 = fTsumwy2;
217 static_cast<GHSym&>(obj).fTsumwxy = fTsumwxy;
218 static_cast<GHSym&>(obj).fMatrix = nullptr;
219}
220
221Double_t GHSym::DoIntegral(Int_t binx1, Int_t binx2, Int_t biny1, Int_t biny2, Double_t& error, Option_t* option, Bool_t doError) const
222{
223 // internal function compute integral and optionally the error between the limits
224 // specified by the bin number values working for all histograms (1D, 2D and 3D)
225
226 Int_t nbinsx = GetNbinsX();
227 if(binx1 < 0) {
228 binx1 = 0;
229 }
230 if(binx2 > nbinsx + 1 || binx2 < binx1) {
231 binx2 = nbinsx + 1;
232 }
233 if(GetDimension() > 1) {
234 Int_t nbinsy = GetNbinsY();
235 if(biny1 < 0) {
236 biny1 = 0;
237 }
238 if(biny2 > nbinsy + 1 || biny2 < biny1) {
239 biny2 = nbinsy + 1;
240 }
241 } else {
242 biny1 = 0;
243 biny2 = 0;
244 }
245
246 // - Loop on bins in specified range
247 TString opt = option;
248 opt.ToLower();
249 Bool_t width = kFALSE;
250 if(opt.Contains("width")) {
251 width = kTRUE;
252 }
253
254 Double_t dx = 1.;
255 Double_t dy = 1.;
256 Double_t integral = 0;
257 Double_t igerr2 = 0;
258 for(Int_t binx = binx1; binx <= binx2; ++binx) {
259 if(width) {
260 dx = fXaxis.GetBinWidth(binx);
261 }
262 for(Int_t biny = biny1; biny <= biny2; ++biny) {
263 if(width) {
264 dy = fYaxis.GetBinWidth(biny);
265 }
266 Int_t bin = GetBin(binx, biny);
267 if(width) {
268 integral += GetBinContent(bin) * dx * dy;
269 } else {
270 integral += GetBinContent(bin);
271 }
272 if(doError) {
273 if(width) {
274 igerr2 += GetBinError(bin) * GetBinError(bin) * dx * dx * dy * dy;
275 } else {
276 igerr2 += GetBinError(bin) * GetBinError(bin);
277 }
278 }
279 }
280 }
281
282 if(doError) {
283 error = TMath::Sqrt(igerr2);
284 }
285 return integral;
286}
287
288Int_t GHSym::Fill(Double_t)
289{
290 // Invalid Fill method
291 Error("Fill", "Invalid signature - do nothing");
292 return -1;
293}
294
295Int_t GHSym::Fill(Double_t x, Double_t y)
296{
297 /// Increment cell defined by x,y by 1.
298 if(fBuffer != nullptr) {
299 return BufferFill(x, y, 1);
300 }
301
302 Int_t binx = 0;
303 Int_t biny = 0;
304 fEntries++;
305 if(y <= x) {
306 binx = fXaxis.FindBin(x);
307 biny = fYaxis.FindBin(y);
308 } else {
309 binx = fXaxis.FindBin(y);
310 biny = fYaxis.FindBin(x);
311 }
312 if(binx < 0 || biny < 0) {
313 return -1;
314 }
315 Int_t bin = biny * (2 * fXaxis.GetNbins() - biny + 3) / 2 + binx;
316 AddBinContent(bin);
317 if(fSumw2.fN != 0) {
318 ++fSumw2.fArray[bin];
319 }
320 if(binx == 0 || binx > fXaxis.GetNbins()) {
321 if(!fgStatOverflows) {
322 return -1;
323 }
324 }
325 if(biny == 0 || biny > fYaxis.GetNbins()) {
326 if(!fgStatOverflows) {
327 return -1;
328 }
329 }
330 // not sure if these summed weights are calculated correct
331 // as of now this is the method used in TH2
332 ++fTsumw;
333 ++fTsumw2;
334 fTsumwx += x;
335 fTsumwx2 += x * x;
336 fTsumwy += y;
337 fTsumwy2 += y * y;
338 fTsumwxy += x * y;
339 return bin;
340}
341
342Int_t GHSym::Fill(Double_t x, Double_t y, Double_t w)
343{
344 /// Increment cell defined by x,y by 1.
345 if(fBuffer != nullptr) {
346 return BufferFill(x, y, 1);
347 }
348
349 Int_t binx = 0;
350 Int_t biny = 0;
351 fEntries++;
352 if(y <= x) {
353 binx = fXaxis.FindBin(x);
354 biny = fYaxis.FindBin(y);
355 } else {
356 binx = fXaxis.FindBin(y);
357 biny = fYaxis.FindBin(x);
358 }
359 if(binx < 0 || biny < 0) {
360 return -1;
361 }
362 Int_t bin = biny * (2 * fXaxis.GetNbins() - biny + 3) / 2 + binx;
363 AddBinContent(bin, w);
364 if(fSumw2.fN != 0) {
365 fSumw2.fArray[bin] += w * w;
366 }
367 if(binx == 0 || binx > fXaxis.GetNbins()) {
368 if(!fgStatOverflows) {
369 return -1;
370 }
371 }
372 if(biny == 0 || biny > fYaxis.GetNbins()) {
373 if(!fgStatOverflows) {
374 return -1;
375 }
376 }
377 // not sure if these summed weights are calculated correct
378 // as of now this is the method used in TH2
379 fTsumw += w;
380 fTsumw2 += w * w;
381 fTsumwx += w * x;
382 fTsumwx2 += w * x * x;
383 fTsumwy += w * y;
384 fTsumwy2 += w * y * y;
385 fTsumwxy += w * x * y;
386 return bin;
387}
388
389Int_t GHSym::Fill(const char* namex, const char* namey, Double_t w)
390{
391 // Increment cell defined by namex,namey by a weight w
392 //
393 // if x or/and y is less than the low-edge of the corresponding axis first bin,
394 // the Underflow cell is incremented.
395 // if x or/and y is greater than the upper edge of corresponding axis last bin,
396 // the Overflow cell is incremented.
397 //
398 // If the storage of the sum of squares of weights has been triggered,
399 // via the function Sumw2, then the sum of the squares of weights is incremented
400 // by w^2 in the cell corresponding to x,y.
401 //
402
403 Int_t binx = 0;
404 Int_t biny = 0;
405 fEntries++;
406 binx = fXaxis.FindBin(namex);
407 biny = fYaxis.FindBin(namey);
408 if(binx < 0 || biny < 0) {
409 return -1;
410 }
411 if(biny >= binx) {
412 std::swap(binx, biny);
413 }
414 Int_t bin = biny * (2 * fXaxis.GetNbins() - biny + 3) / 2 + binx;
415 AddBinContent(bin, w);
416 if(fSumw2.fN != 0) {
417 fSumw2.fArray[bin] += w * w;
418 }
419 if(binx == 0 || binx > fXaxis.GetNbins()) {
420 return -1;
421 }
422 if(biny == 0 || biny > fYaxis.GetNbins()) {
423 return -1;
424 }
425 Double_t x = fXaxis.GetBinCenter(binx);
426 Double_t y = fYaxis.GetBinCenter(biny);
427 fTsumw += w;
428 fTsumw2 += w * w;
429 fTsumwx += w * x;
430 fTsumwx2 += w * x * x;
431 fTsumwy += w * y;
432 fTsumwy2 += w * y * y;
433 fTsumwxy += w * x * y;
434 return bin;
435}
436
437void GHSym::FillN(Int_t ntimes, const Double_t* x, const Double_t* y, const Double_t* w, Int_t stride)
438{
439 //*-*-*-*-*-*-*Fill a 2-D histogram with an array of values and weights*-*-*-*
440 //*-* ========================================================
441 //*-*
442 //*-* ntimes: number of entries in arrays x and w (array size must be ntimes*stride)
443 //*-* x: array of x values to be histogrammed
444 //*-* y: array of y values to be histogrammed
445 //*-* w: array of weights
446 //*-* stride: step size through arrays x, y and w
447 //*-*
448 //*-* If the storage of the sum of squares of weights has been triggered,
449 //*-* via the function Sumw2, then the sum of the squares of weights is incremented
450 //*-* by w[i]^2 in the cell corresponding to x[i],y[i].
451 //*-* if w is NULL each entry is assumed a weight=1
452 //*-*
453 //*-* NB: function only valid for a TH2x object
454 //*-*
455 //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
456 ntimes *= stride;
457 for(int i = 0; i < ntimes; i += stride) {
458 if(w != nullptr) {
459 Fill(x[i], y[i], w[i]);
460 } else {
461 Fill(x[i], y[i]);
462 }
463 }
464}
465
466void GHSym::FillRandom(const char* fname, Int_t ntimes, TRandom* rng)
467{
468 //*-*-*-*-*-*-*Fill histogram following distribution in function fname*-*-*-*
469 //*-* =======================================================
470 //*-*
471 //*-* The distribution contained in the function fname (TF2) is integrated
472 //*-* over the channel contents.
473 //*-* It is normalized to 1.
474 //*-* Getting one random number implies:
475 //*-* - Generating a random number between 0 and 1 (say r1)
476 //*-* - Look in which bin in the normalized integral r1 corresponds to
477 //*-* - Fill histogram channel
478 //*-* ntimes random numbers are generated
479 //*-*
480 //*-* One can also call TF2::GetRandom2 to get a random variate from a function.
481 //*-*
482 //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*
483
484 //*-*- Search for fname in the list of ROOT defined functions
485 TObject* fobj = gROOT->GetFunction(fname);
486 if(fobj == nullptr) {
487 Error("FillRandom", "Unknown function: %s", fname);
488 return;
489 }
490 TF2* f1 = static_cast<TF2*>(fobj);
491 if(f1 == nullptr) {
492 Error("FillRandom", "Function: %s is not a TF2", fname);
493 return;
494 }
495
496 //*-*- Allocate temporary space to store the integral and compute integral
497 Int_t nbinsx = GetNbinsX();
498 Int_t nbinsy = GetNbinsY();
499 Int_t nbins = nbinsx * nbinsy;
500
501 auto* integral = new Double_t[nbins + 1];
502 Int_t ibin = 0;
503 integral[ibin] = 0;
504 for(Int_t biny = 1; biny <= nbinsy; ++biny) {
505 for(Int_t binx = 1; binx <= nbinsx; ++binx) {
506 ++ibin;
507 Double_t fint = f1->Integral(fXaxis.GetBinLowEdge(binx), fXaxis.GetBinUpEdge(binx), fYaxis.GetBinLowEdge(biny),
508 fYaxis.GetBinUpEdge(biny));
509 integral[ibin] = integral[ibin - 1] + fint;
510 }
511 }
512
513 //*-*- Normalize integral to 1
514 if(integral[nbins] == 0) {
515 delete[] integral;
516 Error("FillRandom", "Integral = zero");
517 return;
518 }
519 for(Int_t bin = 1; bin <= nbins; ++bin) {
520 integral[bin] /= integral[nbins];
521 }
522
523 //*-*--------------Start main loop ntimes
524 for(int loop = 0; loop < ntimes; ++loop) {
525 Double_t r1 = (rng != nullptr) ? rng->Rndm(loop) : gRandom->Rndm(loop);
526 ibin = static_cast<int>(TMath::BinarySearch(nbins, &integral[0], r1));
527 Int_t biny = ibin / nbinsx;
528 Int_t binx = 1 + ibin - nbinsx * biny;
529 ++biny;
530 Fill(fXaxis.GetBinCenter(binx), fYaxis.GetBinCenter(biny));
531 }
532 delete[] integral;
533}
534
535#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 0)
536void GHSym::FillRandom(TH1* h, Int_t ntimes, TRandom*)
537#else
538void GHSym::FillRandom(TH1* hist, Int_t ntimes, TRandom* rng)
539#endif
540{
541 //*-*-*-*-*-*-*Fill histogram following distribution in histogram h*-*-*-*
542 //*-* ====================================================
543 //*-*
544 //*-* The distribution contained in the histogram h (TH2) is integrated
545 //*-* over the channel contents.
546 //*-* It is normalized to 1.
547 //*-* Getting one random number implies:
548 //*-* - Generating a random number between 0 and 1 (say r1)
549 //*-* - Look in which bin in the normalized integral r1 corresponds to
550 //*-* - Fill histogram channel
551 //*-* ntimes random numbers are generated
552 //*-*
553 //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*
554
555 if(hist == nullptr) {
556 Error("FillRandom", "Null histogram");
557 return;
558 }
559 if(fDimension != hist->GetDimension()) {
560 Error("FillRandom", "Histograms with different dimensions");
561 return;
562 }
563
564 if(hist->ComputeIntegral() == 0) {
565 return;
566 }
567
568 Double_t x = 0.;
569 Double_t y = 0.;
570 TH2* h2 = static_cast<TH2*>(hist);
571 for(int loop = 0; loop < ntimes; ++loop) {
572#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 0)
573 h2->GetRandom2(x, y);
574#else
575 h2->GetRandom2(x, y, rng);
576#endif
577 Fill(x, y);
578 }
579}
580
581Int_t GHSym::FindFirstBinAbove(Double_t threshold, Int_t axis, Int_t firstBin, Int_t lastBin) const
582{
583 // find first bin with content > threshold for axis (1=x, 2=y, 3=z)
584 // if no bins with content > threshold is found the function returns -1.
585
586 if(axis < 1 || axis > 2) {
587 Warning("FindFirstBinAbove", "Invalid axis number : %d, axis x assumed\n", axis);
588 axis = 1;
589 }
590 Int_t nbinsx = fXaxis.GetNbins();
591 if(lastBin > firstBin && lastBin < nbinsx) { nbinsx = lastBin; }
592 Int_t nbinsy = fYaxis.GetNbins();
593 if(lastBin > firstBin && lastBin < nbinsy) { nbinsy = lastBin; }
594 if(axis == 1) {
595 for(Int_t binx = firstBin; binx <= nbinsx; ++binx) {
596 for(Int_t biny = firstBin; biny <= nbinsy; ++biny) {
597 if(GetBinContent(binx, biny) > threshold) {
598 return binx;
599 }
600 }
601 }
602 } else {
603 for(Int_t biny = firstBin; biny <= nbinsy; ++biny) {
604 for(Int_t binx = firstBin; binx <= nbinsx; ++binx) {
605 if(GetBinContent(binx, biny) > threshold) {
606 return biny;
607 }
608 }
609 }
610 }
611 return -1;
612}
613
614Int_t GHSym::FindLastBinAbove(Double_t threshold, Int_t axis, Int_t firstBin, Int_t lastBin) const
615{
616 // find last bin with content > threshold for axis (1=x, 2=y, 3=z)
617 // if no bins with content > threshold is found the function returns -1.
618
619 if(axis < 1 || axis > 2) {
620 Warning("FindLastBinAbove", "Invalid axis number : %d, axis x assumed\n", axis);
621 axis = 1;
622 }
623 Int_t nbinsx = fXaxis.GetNbins();
624 if(lastBin > firstBin && lastBin < nbinsx) { nbinsx = lastBin; }
625 Int_t nbinsy = fYaxis.GetNbins();
626 if(lastBin > firstBin && lastBin < nbinsy) { nbinsy = lastBin; }
627 if(axis == 1) {
628 for(Int_t binx = nbinsx; binx >= firstBin; --binx) {
629 for(Int_t biny = firstBin; biny <= nbinsy; ++biny) {
630 if(GetBinContent(binx, biny) > threshold) {
631 return binx;
632 }
633 }
634 }
635 } else {
636 for(Int_t biny = nbinsy; biny >= firstBin; --biny) {
637 for(Int_t binx = firstBin; binx <= nbinsx; ++binx) {
638 if(GetBinContent(binx, biny) > threshold) {
639 return biny;
640 }
641 }
642 }
643 }
644 return -1;
645}
646
647void GHSym::FitSlices(TF1* f1, Int_t firstbin, Int_t lastbin, Int_t cut, Option_t* option, TObjArray* arr)
648{
649 // Project slices along X in case of a 2-D histogram, then fit each slice
650 // with function f1 and make a histogram for each fit parameter
651 // Only bins along Y between firstbin and lastbin are considered.
652 // By default (firstbin == 0, lastbin == -1), all bins in y including
653 // over- and underflows are taken into account.
654 // If f1=0, a gaussian is assumed
655 // Before invoking this function, one can set a subrange to be fitted along X
656 // via f1->SetRange(xmin,xmax)
657 // The argument option (default="QNR") can be used to change the fit options.
658 // "Q" means Quiet mode
659 // "N" means do not show the result of the fit
660 // "R" means fit the function in the specified function range
661 // "G2" merge 2 consecutive bins along X
662 // "G3" merge 3 consecutive bins along X
663 // "G4" merge 4 consecutive bins along X
664 // "G5" merge 5 consecutive bins along X
665 // "S" sliding merge: merge n consecutive bins along X accordingly to what Gn is given.
666 // It makes sense when used together with a Gn option
667 //
668 // The generated histograms are returned by adding them to arr, if arr is not NULL.
669 // arr's SetOwner() is called, to signal that it is the user's respponsability to
670 // delete the histograms, possibly by deleting the arrary.
671 // TObjArray aSlices;
672 // h2->FitSlicesX(func, 0, -1, 0, "QNR", &aSlices);
673 // will already delete the histograms once aSlice goes out of scope. aSlices will
674 // contain the histogram for the i-th parameter of the fit function at aSlices[i];
675 // aSlices[n] (n being the number of parameters) contains the chi2 distribution of
676 // the fits.
677 //
678 // If arr is NULL, the generated histograms are added to the list of objects
679 // in the current directory. It is the user's responsability to delete
680 // these histograms.
681 //
682 // Example: Assume a 2-d histogram h2
683 // Root > h2->FitSlicesX(); produces 4 TH1D histograms
684 // with h2_0 containing parameter 0(Constant) for a Gaus fit
685 // of each bin in Y projected along X
686 // with h2_1 containing parameter 1(Mean) for a gaus fit
687 // with h2_2 containing parameter 2(RMS) for a gaus fit
688 // with h2_chi2 containing the chisquare/number of degrees of freedom for a gaus fit
689 //
690 // Root > h2->FitSlicesX(0,15,22,10);
691 // same as above, but only for bins 15 to 22 along Y
692 // and only for bins in Y for which the corresponding projection
693 // along X has more than cut bins filled.
694 //
695 // NOTE: To access the generated histograms in the current directory, do eg:
696 // TH1D *h2_1 = (TH1D*)gDirectory->Get("h2_1");
697
698 Int_t nbins = fYaxis.GetNbins();
699 if(firstbin < 0) {
700 firstbin = 0;
701 }
702 if(lastbin < 0 || lastbin > nbins + 1) {
703 lastbin = nbins + 1;
704 }
705 if(lastbin < firstbin) {
706 firstbin = 0;
707 lastbin = nbins + 1;
708 }
709 TString opt = option;
710 opt.ToLower();
711 Int_t ngroup = 1;
712 if(opt.Contains("g2")) {
713 ngroup = 2;
714 opt.ReplaceAll("g2", "");
715 }
716 if(opt.Contains("g3")) {
717 ngroup = 3;
718 opt.ReplaceAll("g3", "");
719 }
720 if(opt.Contains("g4")) {
721 ngroup = 4;
722 opt.ReplaceAll("g4", "");
723 }
724 if(opt.Contains("g5")) {
725 ngroup = 5;
726 opt.ReplaceAll("g5", "");
727 }
728
729 // implement option S sliding merge for each bin using in conjunction with a given Gn
730 Int_t nstep = ngroup;
731 if(opt.Contains("s")) {
732 nstep = 1;
733 }
734
735 // default is to fit with a gaussian
736 if(f1 == nullptr) {
737 f1 = static_cast<TF1*>(gROOT->GetFunction("gaus"));
738 if(f1 == nullptr) {
739 f1 = new TF1("gaus", "gaus", fXaxis.GetXmin(), fXaxis.GetXmax());
740 } else {
741 f1->SetRange(fXaxis.GetXmin(), fXaxis.GetXmax());
742 }
743 }
744 Int_t npar = f1->GetNpar();
745 if(npar <= 0) {
746 return;
747 }
748 auto* parsave = new Double_t[npar];
749 f1->GetParameters(parsave);
750
751 if(arr != nullptr) {
752 arr->SetOwner();
753 arr->Expand(npar + 1);
754 }
755
756 // Create one histogram for each function parameter
757 auto** hlist = new TH1D*[npar];
758 auto* name = new char[2000];
759 auto* title = new char[2000];
760 const TArrayD* bins = fYaxis.GetXbins();
761 for(Int_t ipar = 0; ipar < npar; ++ipar) {
762 snprintf(name, 2000, "%s_%d", GetName(), ipar);
763 snprintf(title, 2000, "Fitted value of par[%d]=%s", ipar, f1->GetParName(ipar));
764 delete gDirectory->FindObject(name);
765 if(bins->fN == 0) {
766 hlist[ipar] = new TH1D(name, title, nbins, fYaxis.GetXmin(), fYaxis.GetXmax());
767 } else {
768 hlist[ipar] = new TH1D(name, title, nbins, bins->fArray);
769 }
770 hlist[ipar]->GetXaxis()->SetTitle(fYaxis.GetTitle());
771 if(arr != nullptr) {
772 (*arr)[ipar] = hlist[ipar];
773 }
774 }
775 snprintf(name, 2000, "%s_chi2", GetName());
776 delete gDirectory->FindObject(name);
777 TH1D* hchi2 = nullptr;
778 if(bins->fN == 0) {
779 hchi2 = new TH1D(name, "chisquare", nbins, fYaxis.GetXmin(), fYaxis.GetXmax());
780 } else {
781 hchi2 = new TH1D(name, "chisquare", nbins, bins->fArray);
782 }
783 hchi2->GetXaxis()->SetTitle(fYaxis.GetTitle());
784 if(arr != nullptr) {
785 (*arr)[npar] = hchi2;
786 }
787
788 // Loop on all bins in Y, generate a projection along X
789 // in case of sliding merge nstep=1, i.e. do slices starting for every bin
790 // now do not slices case with overflow (makes more sense)
791 for(Int_t bin = firstbin; bin + ngroup - 1 <= lastbin; bin += nstep) {
792 TH1D* proj = Projection("_temp", bin, bin + ngroup - 1, "e");
793 if(proj == nullptr) {
794 continue;
795 }
796 auto nentries = static_cast<Long64_t>(proj->GetEntries());
797 if(nentries == 0 || nentries < cut) {
798 delete proj;
799 continue;
800 }
801 f1->SetParameters(parsave);
802 proj->Fit(f1, opt.Data());
803 Int_t npfits = f1->GetNumberFitPoints();
804 if(npfits > npar && npfits >= cut) {
805 Int_t binOn = bin + ngroup / 2;
806 for(Int_t ipar = 0; ipar < npar; ++ipar) {
807 hlist[ipar]->Fill(fYaxis.GetBinCenter(binOn), f1->GetParameter(ipar));
808 hlist[ipar]->SetBinError(binOn, f1->GetParError(ipar));
809 }
810 hchi2->Fill(fYaxis.GetBinCenter(binOn), f1->GetChisquare() / (npfits - npar));
811 }
812 delete proj;
813 }
814 delete[] parsave;
815 delete[] name;
816 delete[] title;
817 delete[] hlist;
818}
819
820Int_t GHSym::GetBin(Int_t binx, Int_t biny, Int_t) const
821{
822 Int_t nBin = fXaxis.GetNbins() + 2;
823 if(binx < 0) {
824 binx = 0;
825 }
826 if(binx >= nBin) {
827 binx = nBin - 1;
828 }
829 if(biny < 0) {
830 biny = 0;
831 }
832 if(biny >= nBin) {
833 biny = nBin - 1;
834 }
835 if(biny <= binx) {
836 return binx + biny * (2 * fXaxis.GetNbins() - biny + 3) / 2;
837 }
838 return biny + binx * (2 * fXaxis.GetNbins() - binx + 3) / 2;
839}
840
841Double_t GHSym::GetBinWithContent2(Double_t content, Int_t& binx, Int_t& biny, Int_t firstxbin, Int_t lastxbin, Int_t firstybin, Int_t lastybin, Double_t maxdiff) const
842{
843 // compute first cell (binx,biny) in the range [firstxbin,lastxbin][firstybin,lastybin] for which
844 // diff = abs(cell_content-c) <= maxdiff
845 // In case several cells in the specified range with diff=0 are found
846 // the first cell found is returned in binx,biny.
847 // In case several cells in the specified range satisfy diff <=maxdiff
848 // the cell with the smallest difference is returned in binx,biny.
849 // In all cases the function returns the smallest difference.
850 //
851 // NOTE1: if firstxbin < 0, firstxbin is set to 1
852 // if(lastxbin < firstxbin then lastxbin is set to the number of bins in X
853 // ie if firstxbin=1 and lastxbin=0 (default) the search is on all bins in X except
854 // for X's under- and overflow bins.
855 // if firstybin < 0, firstybin is set to 1
856 // if(lastybin < firstybin then lastybin is set to the number of bins in Y
857 // ie if firstybin=1 and lastybin=0 (default) the search is on all bins in Y except
858 // for Y's under- and overflow bins.
859 // NOTE2: if maxdiff=0 (default), the first cell with content=c is returned.
860
861 if(fDimension != 2) {
862 binx = -1;
863 biny = -1;
864 Error("GetBinWithContent2", "function is only valid for 2-D histograms");
865 return 0;
866 }
867 if(firstxbin < 0) {
868 firstxbin = 1;
869 }
870 if(lastxbin < firstxbin) {
871 lastxbin = fXaxis.GetNbins();
872 }
873 if(firstybin < 0) {
874 firstybin = 1;
875 }
876 if(lastybin < firstybin) {
877 lastybin = fYaxis.GetNbins();
878 }
879 Double_t curmax = 1e240;
880 for(Int_t j = firstybin; j <= lastybin; j++) {
881 for(Int_t i = firstxbin; i <= lastxbin; i++) {
882 Double_t diff = TMath::Abs(GetBinContent(i, j) - content);
883 if(diff <= 0) {
884 binx = i;
885 biny = j;
886 return diff;
887 }
888 if(diff < curmax && diff <= maxdiff) {
889 curmax = diff, binx = i;
890 biny = j;
891 }
892 }
893 }
894 return curmax;
895}
896
897Double_t GHSym::GetCellContent(Int_t binx, Int_t biny) const
898{
899 return GetBinContent(GetBin(binx, biny));
900}
901
902Double_t GHSym::GetCellError(Int_t binx, Int_t biny) const
903{
904 return GetBinError(GetBin(binx, biny));
905}
906
907Double_t GHSym::GetCorrelationFactor(Int_t axis1, Int_t axis2) const
908{
909 //*-*-*-*-*-*-*-*Return correlation factor between axis1 and axis2*-*-*-*-*
910 //*-* ====================================================
911 if(axis1 < 1 || axis2 < 1 || axis1 > 2 || axis2 > 2) {
912 Error("GetCorrelationFactor", "Wrong parameters");
913 return 0;
914 }
915 if(axis1 == axis2) {
916 return 1;
917 }
918 Double_t rms1 = GetRMS(axis1);
919 if(rms1 == 0) {
920 return 0;
921 }
922 Double_t rms2 = GetRMS(axis2);
923 if(rms2 == 0) {
924 return 0;
925 }
926 return GetCovariance(axis1, axis2) / rms1 / rms2;
927}
928
929Double_t GHSym::GetCovariance(Int_t axis1, Int_t axis2) const
930{
931 //*-*-*-*-*-*-*-*Return covariance between axis1 and axis2*-*-*-*-*
932 //*-* ====================================================
933
934 if(axis1 < 1 || axis2 < 1 || axis1 > 2 || axis2 > 2) {
935 Error("GetCovariance", "Wrong parameters");
936 return 0;
937 }
938 std::array<Double_t, kNstat> stats;
939 GetStats(stats.data());
940 Double_t sumw = stats[0];
941 // Double_t sumw2 = stats[1];
942 Double_t sumwx = stats[2];
943 Double_t sumwx2 = stats[3];
944 Double_t sumwy = stats[4];
945 Double_t sumwy2 = stats[5];
946 Double_t sumwxy = stats[6];
947
948 if(sumw == 0) {
949 return 0;
950 }
951 if(axis1 == 1 && axis2 == 1) {
952 return TMath::Abs(sumwx2 / sumw - sumwx / sumw * sumwx / sumw);
953 }
954 if(axis1 == 2 && axis2 == 2) {
955 return TMath::Abs(sumwy2 / sumw - sumwy / sumw * sumwy / sumw);
956 }
957 return sumwxy / sumw - sumwx / sumw * sumwy / sumw;
958}
959
960void GHSym::GetRandom2(Double_t& x, Double_t& y)
961{
962 // return 2 random numbers along axis x and y distributed according
963 // the cellcontents of a 2-dim histogram
964 // return a NaN if the histogram has a bin with negative content
965
966 Int_t nbinsx = GetNbinsX();
967 Int_t nbinsy = GetNbinsY();
968 Int_t nbins = nbinsx * nbinsy;
969 Double_t integral = 0.;
970 // compute integral checking that all bins have positive content (see ROOT-5894)
971 if(fIntegral != nullptr) {
972 if(fIntegral[nbins + 1] != fEntries) {
973 integral = ComputeIntegral(true);
974 } else {
975 integral = fIntegral[nbins];
976 }
977 } else {
978 integral = ComputeIntegral(true);
979 }
980 if(integral == 0) {
981 x = 0;
982 y = 0;
983 return;
984 }
985 // case histogram has negative bins
986 if(integral == TMath::QuietNaN()) {
987 x = TMath::QuietNaN();
988 y = TMath::QuietNaN();
989 return;
990 }
991
992 Double_t r1 = gRandom->Rndm();
993 auto ibin = static_cast<int>(TMath::BinarySearch(nbins, fIntegral, r1));
994 Int_t biny = ibin / nbinsx;
995 Int_t binx = ibin - nbinsx * biny;
996 x = fXaxis.GetBinLowEdge(binx + 1);
997 if(r1 > fIntegral[ibin]) {
998 x += fXaxis.GetBinWidth(binx + 1) * (r1 - fIntegral[ibin]) / (fIntegral[ibin + 1] - fIntegral[ibin]);
999 }
1000 y = fYaxis.GetBinLowEdge(biny + 1) + fYaxis.GetBinWidth(biny + 1) * gRandom->Rndm();
1001}
1002
1003void GHSym::GetStats(Double_t* stats) const
1004{
1005 // fill the array stats from the contents of this histogram
1006 // The array stats must be correctly dimensionned in the calling program.
1007 // stats[0] = sumw
1008 // stats[1] = sumw2
1009 // stats[2] = sumwx
1010 // stats[3] = sumwx2
1011 // stats[4] = sumwy
1012 // stats[5] = sumwy2
1013 // stats[6] = sumwxy
1014 //
1015 // If no axis-subranges are specified (via TAxis::SetRange), the array stats
1016 // is simply a copy of the statistics quantities computed at filling time.
1017 // If sub-ranges are specified, the function recomputes these quantities
1018 // from the bin contents in the current axis ranges.
1019 //
1020 // Note that the mean value/RMS is computed using the bins in the currently
1021 // defined ranges (see TAxis::SetRange). By default the ranges include
1022 // all bins from 1 to nbins included, excluding underflows and overflows.
1023 // To force the underflows and overflows in the computation, one must
1024 // call the static function TH1::StatOverflows(kTRUE) before filling
1025 // the histogram.
1026
1027 if(fBuffer != nullptr) {
1028 const_cast<GHSym*>(this)->BufferEmpty(); // NOLINT(cppcoreguidelines-pro-type-const-cast)
1029 }
1030
1031 if((fTsumw == 0 && fEntries > 0) || fXaxis.TestBit(TAxis::kAxisRange) || fYaxis.TestBit(TAxis::kAxisRange)) {
1032 for(Int_t bin = 0; bin < 7; ++bin) {
1033 stats[bin] = 0;
1034 }
1035
1036 Int_t firstBinX = fXaxis.GetFirst();
1037 Int_t lastBinX = fXaxis.GetLast();
1038 Int_t firstBinY = fYaxis.GetFirst();
1039 Int_t lastBinY = fYaxis.GetLast();
1040 // include underflow/overflow if TH1::StatOverflows(kTRUE) in case no range is set on the axis
1041 if(fgStatOverflows) {
1042 if(!fXaxis.TestBit(TAxis::kAxisRange)) {
1043 if(firstBinX == 1) {
1044 firstBinX = 0;
1045 }
1046 if(lastBinX == fXaxis.GetNbins()) {
1047 lastBinX += 1;
1048 }
1049 }
1050 if(!fYaxis.TestBit(TAxis::kAxisRange)) {
1051 if(firstBinY == 1) {
1052 firstBinY = 0;
1053 }
1054 if(lastBinY == fYaxis.GetNbins()) {
1055 lastBinY += 1;
1056 }
1057 }
1058 }
1059 for(Int_t biny = firstBinY; biny <= lastBinY; ++biny) {
1060 Double_t y = fYaxis.GetBinCenter(biny);
1061 for(Int_t binx = firstBinX; binx <= lastBinX; ++binx) {
1062 Int_t bin = GetBin(binx, biny);
1063 Double_t x = fXaxis.GetBinCenter(binx);
1064 Double_t w = GetBinContent(bin);
1065 Double_t err = TMath::Abs(GetBinError(bin));
1066 stats[0] += w;
1067 stats[1] += err * err;
1068 stats[2] += w * x;
1069 stats[3] += w * x * x;
1070 stats[4] += w * y;
1071 stats[5] += w * y * y;
1072 stats[6] += w * x * y;
1073 }
1074 }
1075 } else {
1076 stats[0] = fTsumw;
1077 stats[1] = fTsumw2;
1078 stats[2] = fTsumwx;
1079 stats[3] = fTsumwx2;
1080 stats[4] = fTsumwy;
1081 stats[5] = fTsumwy2;
1082 stats[6] = fTsumwxy;
1083 }
1084}
1085
1086Double_t GHSym::Integral(Option_t* option) const
1087{
1088 // Return integral of bin contents. Only bins in the bins range are considered.
1089 // By default the integral is computed as the sum of bin contents in the range.
1090 // if option "width" is specified, the integral is the sum of
1091 // the bin contents multiplied by the bin width in x and in y.
1092
1093 return Integral(fXaxis.GetFirst(), fXaxis.GetLast(), fYaxis.GetFirst(), fYaxis.GetLast(), option);
1094}
1095
1096Double_t GHSym::Integral(Int_t firstxbin, Int_t lastxbin, Int_t firstybin, Int_t lastybin, Option_t* option) const
1097{
1098 // Return integral of bin contents in range [firstxbin,lastxbin],[firstybin,lastybin]
1099 // for a 2-D histogram
1100 // By default the integral is computed as the sum of bin contents in the range.
1101 // if option "width" is specified, the integral is the sum of
1102 // the bin contents multiplied by the bin width in x and in y.
1103 double err = 0;
1104 return DoIntegral(firstxbin, lastxbin, firstybin, lastybin, err, option);
1105}
1106
1107Double_t GHSym::IntegralAndError(Int_t firstxbin, Int_t lastxbin, Int_t firstybin, Int_t lastybin, Double_t& error,
1108 Option_t* option) const
1109{
1110 // Return integral of bin contents in range [firstxbin,lastxbin],[firstybin,lastybin]
1111 // for a 2-D histogram. Calculates also the integral error using error propagation
1112 // from the bin errors assumming that all the bins are uncorrelated.
1113 // By default the integral is computed as the sum of bin contents in the range.
1114 // if option "width" is specified, the integral is the sum of
1115 // the bin contents multiplied by the bin width in x and in y.
1116
1117 return DoIntegral(firstxbin, lastxbin, firstybin, lastybin, error, option, kTRUE);
1118}
1119
1120#if ROOT_VERSION_CODE < ROOT_VERSION(6, 20, 0)
1121Double_t GHSym::Interpolate(Double_t)
1122#else
1123Double_t GHSym::Interpolate(Double_t) const
1124#endif
1125{
1126 // illegal for a TH2
1127 Error("Interpolate", "This function must be called with 2 arguments for a TH2");
1128 return 0;
1129}
1130
1131#if ROOT_VERSION_CODE < ROOT_VERSION(6, 20, 0)
1132Double_t GHSym::Interpolate(Double_t x, Double_t y)
1133#else
1134Double_t GHSym::Interpolate(Double_t x, Double_t y) const
1135#endif
1136{
1137 // Given a point P(x,y), Interpolate approximates the value via bilinear
1138 // interpolation based on the four nearest bin centers
1139 // see Wikipedia, Bilinear Interpolation
1140 // Andy Mastbaum 10/8/2008
1141 // vaguely based on R.Raja 6-Sep-2008
1142
1143 Double_t f = 0;
1144 Double_t x1 = 0;
1145 Double_t x2 = 0;
1146 Double_t y1 = 0;
1147 Double_t y2 = 0;
1148 Double_t dx = 0.;
1149 Double_t dy = 0.;
1150 Int_t bin_x = fXaxis.FindBin(x);
1151 Int_t bin_y = fYaxis.FindBin(y);
1152 if(bin_x < 1 || bin_x > GetNbinsX() || bin_y < 1 || bin_y > GetNbinsY()) {
1153 Error("Interpolate", "Cannot interpolate outside histogram domain.");
1154 return 0;
1155 }
1156 Int_t quadrant = 0; // CCW from UR 1,2,3,4
1157 // which quadrant of the bin (bin_P) are we in?
1158 dx = fXaxis.GetBinUpEdge(bin_x) - x;
1159 dy = fYaxis.GetBinUpEdge(bin_y) - y;
1160 if(dx <= fXaxis.GetBinWidth(bin_x) / 2 && dy <= fYaxis.GetBinWidth(bin_y) / 2) {
1161 quadrant = 1; // upper right
1162 }
1163 if(dx > fXaxis.GetBinWidth(bin_x) / 2 && dy <= fYaxis.GetBinWidth(bin_y) / 2) {
1164 quadrant = 2; // upper left
1165 }
1166 if(dx > fXaxis.GetBinWidth(bin_x) / 2 && dy > fYaxis.GetBinWidth(bin_y) / 2) {
1167 quadrant = 3; // lower left
1168 }
1169 if(dx <= fXaxis.GetBinWidth(bin_x) / 2 && dy > fYaxis.GetBinWidth(bin_y) / 2) {
1170 quadrant = 4; // lower right
1171 }
1172 switch(quadrant) {
1173 case 1:
1174 x1 = fXaxis.GetBinCenter(bin_x);
1175 y1 = fYaxis.GetBinCenter(bin_y);
1176 x2 = fXaxis.GetBinCenter(bin_x + 1);
1177 y2 = fYaxis.GetBinCenter(bin_y + 1);
1178 break;
1179 case 2:
1180 x1 = fXaxis.GetBinCenter(bin_x - 1);
1181 y1 = fYaxis.GetBinCenter(bin_y);
1182 x2 = fXaxis.GetBinCenter(bin_x);
1183 y2 = fYaxis.GetBinCenter(bin_y + 1);
1184 break;
1185 case 3:
1186 x1 = fXaxis.GetBinCenter(bin_x - 1);
1187 y1 = fYaxis.GetBinCenter(bin_y - 1);
1188 x2 = fXaxis.GetBinCenter(bin_x);
1189 y2 = fYaxis.GetBinCenter(bin_y);
1190 break;
1191 case 4:
1192 x1 = fXaxis.GetBinCenter(bin_x);
1193 y1 = fYaxis.GetBinCenter(bin_y - 1);
1194 x2 = fXaxis.GetBinCenter(bin_x + 1);
1195 y2 = fYaxis.GetBinCenter(bin_y);
1196 break;
1197 }
1198 Int_t bin_x1 = fXaxis.FindBin(x1);
1199 if(bin_x1 < 1) {
1200 bin_x1 = 1;
1201 }
1202 Int_t bin_x2 = fXaxis.FindBin(x2);
1203 if(bin_x2 > GetNbinsX()) {
1204 bin_x2 = GetNbinsX();
1205 }
1206 Int_t bin_y1 = fYaxis.FindBin(y1);
1207 if(bin_y1 < 1) {
1208 bin_y1 = 1;
1209 }
1210 Int_t bin_y2 = fYaxis.FindBin(y2);
1211 if(bin_y2 > GetNbinsY()) {
1212 bin_y2 = GetNbinsY();
1213 }
1214 Int_t bin_q22 = GetBin(bin_x2, bin_y2);
1215 Int_t bin_q12 = GetBin(bin_x1, bin_y2);
1216 Int_t bin_q11 = GetBin(bin_x1, bin_y1);
1217 Int_t bin_q21 = GetBin(bin_x2, bin_y1);
1218 Double_t q11 = GetBinContent(bin_q11);
1219 Double_t q12 = GetBinContent(bin_q12);
1220 Double_t q21 = GetBinContent(bin_q21);
1221 Double_t q22 = GetBinContent(bin_q22);
1222 Double_t d = 1.0 * (x2 - x1) * (y2 - y1);
1223 f = 1.0 * q11 / d * (x2 - x) * (y2 - y) + 1.0 * q21 / d * (x - x1) * (y2 - y) + 1.0 * q12 / d * (x2 - x) * (y - y1) +
1224 1.0 * q22 / d * (x - x1) * (y - y1);
1225 return f;
1226}
1227
1228#if ROOT_VERSION_CODE < ROOT_VERSION(6, 20, 0)
1229Double_t GHSym::Interpolate(Double_t, Double_t, Double_t)
1230#else
1231Double_t GHSym::Interpolate(Double_t, Double_t, Double_t) const
1232#endif
1233{
1234 // illegal for a TH2
1235 Error("Interpolate", "This function must be called with 2 arguments for a TH2");
1236 return 0;
1237}
1238
1239Double_t GHSym::KolmogorovTest(const TH1* h2, Option_t* option) const
1240{
1241 // Statistical test of compatibility in shape between
1242 // THIS histogram and h2, using Kolmogorov test.
1243 // Default: Ignore under- and overflow bins in comparison
1244 //
1245 // option is a character string to specify options
1246 // "U" include Underflows in test
1247 // "O" include Overflows
1248 // "N" include comparison of normalizations
1249 // "D" Put out a line of "Debug" printout
1250 // "M" Return the Maximum Kolmogorov distance instead of prob
1251 //
1252 // The returned function value is the probability of test
1253 // (much less than one means NOT compatible)
1254 //
1255 // The KS test uses the distance between the pseudo-CDF's obtained
1256 // from the histogram. Since in 2D the order for generating the pseudo-CDF is
1257 // arbitrary, two pairs of pseudo-CDF are used, one starting from the x axis the
1258 // other from the y axis and the maximum distance is the average of the two maximum
1259 // distances obtained.
1260 //
1261 // Code adapted by Rene Brun from original HBOOK routine HDIFF
1262
1263 TString opt = option;
1264 opt.ToUpper();
1265
1266 Double_t prb = 0;
1267 TH1* h1 = const_cast<TH1*>(static_cast<const TH1*>(this)); // NOLINT(cppcoreguidelines-pro-type-const-cast)
1268 if(h2 == nullptr) {
1269 return 0;
1270 }
1271 TAxis* xaxis1 = h1->GetXaxis();
1272 auto* xaxis2 = const_cast<TAxis*>(h2->GetXaxis()); // NOLINT(cppcoreguidelines-pro-type-const-cast)
1273 TAxis* yaxis1 = h1->GetYaxis();
1274 auto* yaxis2 = const_cast<TAxis*>(h2->GetYaxis()); // NOLINT(cppcoreguidelines-pro-type-const-cast)
1275 Int_t ncx1 = xaxis1->GetNbins();
1276 Int_t ncx2 = xaxis2->GetNbins();
1277 Int_t ncy1 = yaxis1->GetNbins();
1278 Int_t ncy2 = yaxis2->GetNbins();
1279
1280 // Check consistency of dimensions
1281 if(h1->GetDimension() != 2 || h2->GetDimension() != 2) {
1282 Error("KolmogorovTest", "Histograms must be 2-D\n");
1283 return 0;
1284 }
1285
1286 // Check consistency in number of channels
1287 if(ncx1 != ncx2) {
1288 Error("KolmogorovTest", "Number of channels in X is different, %d and %d\n", ncx1, ncx2);
1289 return 0;
1290 }
1291 if(ncy1 != ncy2) {
1292 Error("KolmogorovTest", "Number of channels in Y is different, %d and %d\n", ncy1, ncy2);
1293 return 0;
1294 }
1295
1296 // Check consistency in channel edges
1297 Bool_t afunc1 = kFALSE;
1298 Bool_t afunc2 = kFALSE;
1299 Double_t difprec = 1e-5;
1300 Double_t diff1 = TMath::Abs(xaxis1->GetXmin() - xaxis2->GetXmin());
1301 Double_t diff2 = TMath::Abs(xaxis1->GetXmax() - xaxis2->GetXmax());
1302 if(diff1 > difprec || diff2 > difprec) {
1303 Error("KolmogorovTest", "histograms with different binning along X");
1304 return 0;
1305 }
1306 diff1 = TMath::Abs(yaxis1->GetXmin() - yaxis2->GetXmin());
1307 diff2 = TMath::Abs(yaxis1->GetXmax() - yaxis2->GetXmax());
1308 if(diff1 > difprec || diff2 > difprec) {
1309 Error("KolmogorovTest", "histograms with different binning along Y");
1310 return 0;
1311 }
1312
1313 // Should we include Uflows, Oflows?
1314 Int_t ibeg = 1;
1315 Int_t jbeg = 1;
1316 Int_t iend = ncx1;
1317 Int_t jend = ncy1;
1318 if(opt.Contains("U")) {
1319 ibeg = 0;
1320 jbeg = 0;
1321 }
1322 if(opt.Contains("O")) {
1323 iend = ncx1 + 1;
1324 jend = ncy1 + 1;
1325 }
1326
1327 Double_t sum1 = 0;
1328 Double_t sum2 = 0;
1329 Double_t w1 = 0;
1330 Double_t w2 = 0;
1331 for(Int_t i = ibeg; i <= iend; ++i) {
1332 for(Int_t j = jbeg; j <= jend; ++j) {
1333 sum1 += h1->GetBinContent(i, j);
1334 sum2 += h2->GetBinContent(i, j);
1335 Double_t ew1 = h1->GetBinError(i, j);
1336 Double_t ew2 = h2->GetBinError(i, j);
1337 w1 += ew1 * ew1;
1338 w2 += ew2 * ew2;
1339 }
1340 }
1341
1342 // Check that both scatterplots contain events
1343 if(sum1 == 0) {
1344 Error("KolmogorovTest", "Integral is zero for h1=%s\n", h1->GetName());
1345 return 0;
1346 }
1347 if(sum2 == 0) {
1348 Error("KolmogorovTest", "Integral is zero for h2=%s\n", h2->GetName());
1349 return 0;
1350 }
1351 // calculate the effective entries.
1352 // the case when errors are zero (w1 == 0 or w2 ==0) are equivalent to
1353 // compare to a function. In that case the rescaling is done only on sqrt(esum2) or sqrt(esum1)
1354 Double_t esum1 = 0.;
1355 Double_t esum2 = 0.;
1356 if(w1 > 0) {
1357 esum1 = sum1 * sum1 / w1;
1358 } else {
1359 afunc1 = kTRUE; // use later for calculating z
1360 }
1361
1362 if(w2 > 0) {
1363 esum2 = sum2 * sum2 / w2;
1364 } else {
1365 afunc2 = kTRUE; // use later for calculating z
1366 }
1367
1368 if(afunc2 && afunc1) {
1369 Error("KolmogorovTest", "Errors are zero for both histograms\n");
1370 return 0;
1371 }
1372
1373 // Find first Kolmogorov distance
1374 Double_t s1 = 1 / sum1;
1375 Double_t s2 = 1 / sum2;
1376 Double_t dfmax1 = 0;
1377 Double_t rsum1 = 0.;
1378 Double_t rsum2 = 0.;
1379 for(Int_t i = ibeg; i <= iend; ++i) {
1380 for(Int_t j = jbeg; j <= jend; ++j) {
1381 rsum1 += s1 * h1->GetCellContent(i, j);
1382 rsum2 += s2 * h2->GetCellContent(i, j);
1383 dfmax1 = TMath::Max(dfmax1, TMath::Abs(rsum1 - rsum2));
1384 }
1385 }
1386
1387 // Find second Kolmogorov distance
1388 Double_t dfmax2 = 0;
1389 rsum1 = 0.;
1390 rsum2 = 0.;
1391 for(Int_t j = jbeg; j <= jend; ++j) {
1392 for(Int_t i = ibeg; i <= iend; ++i) {
1393 rsum1 += s1 * h1->GetCellContent(i, j);
1394 rsum2 += s2 * h2->GetCellContent(i, j);
1395 dfmax2 = TMath::Max(dfmax2, TMath::Abs(rsum1 - rsum2));
1396 }
1397 }
1398
1399 // Get Kolmogorov probability: use effective entries, esum1 or esum2, for normalizing it
1400 Double_t factnm = 0.;
1401 if(afunc1) {
1402 factnm = TMath::Sqrt(esum2);
1403 } else if(afunc2) {
1404 factnm = TMath::Sqrt(esum1);
1405 } else {
1406 factnm = TMath::Sqrt(esum1 * sum2 / (esum1 + esum2));
1407 }
1408
1409 // take average of the two distances
1410 Double_t dfmax = 0.5 * (dfmax1 + dfmax2);
1411 Double_t z = dfmax * factnm;
1412
1413 prb = TMath::KolmogorovProb(z);
1414
1415 Double_t prb1 = 0;
1416 Double_t prb2 = 0;
1417 // option N to combine normalization makes sense if both afunc1 and afunc2 are false
1418 if(opt.Contains("N") && !(afunc1 || afunc2)) {
1419 // Combine probabilities for shape and normalization
1420 prb1 = prb;
1421 Double_t d12 = esum1 - esum2;
1422 Double_t chi2 = d12 * d12 / (esum1 + esum2);
1423 prb2 = TMath::Prob(chi2, 1);
1424 // see Eadie et al., section 11.6.2
1425 if(prb > 0 && prb2 > 0) {
1426 prb = prb * prb2 * (1 - TMath::Log(prb * prb2));
1427 } else {
1428 prb = 0;
1429 }
1430 }
1431 // debug printout
1432 if(opt.Contains("D")) {
1433 std::cout << " Kolmo Prob h1 = " << h1->GetName() << ", sum1 = " << sum1 << std::endl;
1434 std::cout << " Kolmo Prob h2 = " << h2->GetName() << ", sum2 = " << sum2 << std::endl;
1435 std::cout << " Kolmo Probabil = " << prb << ", Max dist = " << dfmax << std::endl;
1436 if(opt.Contains("N")) {
1437 std::cout << " Kolmo Probabil = " << prb1 << " for shape alone, " << prb2 << " for normalisation alone" << std::endl;
1438 }
1439 }
1440 // This numerical error condition should never occur:
1441 if(TMath::Abs(rsum1 - 1) > 0.002) {
1442 Warning("KolmogorovTest", "Numerical problems with h1=%s\n", h1->GetName());
1443 }
1444 if(TMath::Abs(rsum2 - 1) > 0.002) {
1445 Warning("KolmogorovTest", "Numerical problems with h2=%s\n", h2->GetName());
1446 }
1447
1448 if(opt.Contains("M")) {
1449 return dfmax; // return avergae of max distance
1450 }
1451
1452 return prb;
1453}
1454
1455Long64_t GHSym::Merge(TCollection* list)
1456{
1457 // Add all histograms in the collection to this histogram.
1458 // This function computes the min/max for the axes,
1459 // compute a new number of bins, if necessary,
1460 // add bin contents, errors and statistics.
1461 // If overflows are present and limits are different the function will fail.
1462 // The function returns the total number of entries in the result histogram
1463 // if the merge is successfull, -1 otherwise.
1464 //
1465 // IMPORTANT remark. The 2 axis x and y may have different number
1466 // of bins and different limits, BUT the largest bin width must be
1467 // a multiple of the smallest bin width and the upper limit must also
1468 // be a multiple of the bin width.
1469
1470 if(list == nullptr) {
1471 return 0;
1472 }
1473 if(list->IsEmpty()) {
1474 return static_cast<Long64_t>(GetEntries());
1475 }
1476
1477 TList inlist;
1478 inlist.AddAll(list);
1479
1480 TAxis newXAxis;
1481 TAxis newYAxis;
1482 Bool_t initialLimitsFound = kFALSE;
1483 Bool_t allSameLimits = kTRUE;
1484 Bool_t sameLimitsX = kTRUE;
1485 Bool_t sameLimitsY = kTRUE;
1486 Bool_t allHaveLimits = kTRUE;
1487 Bool_t firstHistWithLimits = kTRUE;
1488
1489 TIter next(&inlist);
1490 GHSym* h = this;
1491 do {
1492 Bool_t hasLimits = h->GetXaxis()->GetXmin() < h->GetXaxis()->GetXmax();
1493 allHaveLimits = allHaveLimits && hasLimits;
1494
1495 if(hasLimits) {
1496 h->BufferEmpty();
1497
1498 // this is done in case the first histograms are empty and
1499 // the histogram have different limits
1500 if(firstHistWithLimits) {
1501 // set axis limits in the case the first histogram did not have limits
1502 if(h != this) {
1503 if(!SameLimitsAndNBins(fXaxis, *(h->GetXaxis()))) {
1504 if(h->GetXaxis()->GetXbins()->GetSize() != 0) {
1505 fXaxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXbins()->GetArray());
1506 } else {
1507 fXaxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax());
1508 }
1509 }
1510 if(!SameLimitsAndNBins(fYaxis, *(h->GetYaxis()))) {
1511 if(h->GetYaxis()->GetXbins()->GetSize() != 0) {
1512 fYaxis.Set(h->GetYaxis()->GetNbins(), h->GetYaxis()->GetXbins()->GetArray());
1513 } else {
1514 fYaxis.Set(h->GetYaxis()->GetNbins(), h->GetYaxis()->GetXmin(), h->GetYaxis()->GetXmax());
1515 }
1516 }
1517 }
1518 firstHistWithLimits = kFALSE;
1519 }
1520
1521 if(!initialLimitsFound) {
1522 // this is executed the first time an histogram with limits is found
1523 // to set some initial values on the new axes
1524 initialLimitsFound = kTRUE;
1525 if(h->GetXaxis()->GetXbins()->GetSize() != 0) {
1526 newXAxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXbins()->GetArray());
1527 } else {
1528 newXAxis.Set(h->GetXaxis()->GetNbins(), h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax());
1529 }
1530 if(h->GetYaxis()->GetXbins()->GetSize() != 0) {
1531 newYAxis.Set(h->GetYaxis()->GetNbins(), h->GetYaxis()->GetXbins()->GetArray());
1532 } else {
1533 newYAxis.Set(h->GetYaxis()->GetNbins(), h->GetYaxis()->GetXmin(), h->GetYaxis()->GetXmax());
1534 }
1535 } else {
1536 // check first if histograms have same bins in X
1537 if(!SameLimitsAndNBins(newXAxis, *(h->GetXaxis()))) {
1538 sameLimitsX = kFALSE;
1539 // recompute in this case the optimal limits
1540 // The condition to works is that the histogram have same bin with
1541 // and one common bin edge
1542 if(!RecomputeAxisLimits(newXAxis, *(h->GetXaxis()))) {
1543 Error("Merge", "Cannot merge histograms - limits are inconsistent:\n "
1544 "first: (%d, %f, %f), second: (%d, %f, %f)",
1545 newXAxis.GetNbins(), newXAxis.GetXmin(), newXAxis.GetXmax(), h->GetXaxis()->GetNbins(),
1546 h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax());
1547 return -1;
1548 }
1549 }
1550
1551 // check first if histograms have same bins in Y
1552 if(!SameLimitsAndNBins(newYAxis, *(h->GetYaxis()))) {
1553 sameLimitsY = kFALSE;
1554 // recompute in this case the optimal limits
1555 // The condition to works is that the histogram have same bin with
1556 // and one common bin edge
1557 if(!RecomputeAxisLimits(newYAxis, *(h->GetYaxis()))) {
1558 Error("Merge", "Cannot merge histograms - limits are inconsistent:\n "
1559 "first: (%d, %f, %f), second: (%d, %f, %f)",
1560 newYAxis.GetNbins(), newYAxis.GetXmin(), newYAxis.GetXmax(), h->GetYaxis()->GetNbins(),
1561 h->GetYaxis()->GetXmin(), h->GetYaxis()->GetXmax());
1562 return -1;
1563 }
1564 }
1565 allSameLimits = sameLimitsY && sameLimitsX;
1566 }
1567 }
1568 } while((h = static_cast<GHSym*>(next())) != nullptr);
1569 if(h == nullptr && ((*next) != nullptr)) {
1570 Error("Merge", "Attempt to merge object of class: %s to a %s", (*next)->ClassName(), ClassName());
1571 return -1;
1572 }
1573 next.Reset();
1574
1575 // In the case of histogram with different limits
1576 // newX(Y)Axis will now have the new found limits
1577 // but one needs first to clone this histogram to perform the merge
1578 // The clone is not needed when all histograms have the same limits
1579 TH2* hclone = nullptr;
1580 if(!allSameLimits) {
1581 // We don't want to add the clone to gDirectory,
1582 // so remove our kMustCleanup bit temporarily
1583 Bool_t mustCleanup = TestBit(kMustCleanup);
1584 if(mustCleanup) {
1585 ResetBit(kMustCleanup);
1586 }
1587 hclone = static_cast<TH2*>(IsA()->New());
1588 hclone->SetDirectory(nullptr);
1589 Copy(*hclone);
1590 if(mustCleanup) {
1591 SetBit(kMustCleanup);
1592 }
1593 BufferEmpty(1); // To remove buffer.
1594 Reset(); // BufferEmpty sets limits so we can't use it later.
1595 SetEntries(0);
1596 inlist.AddFirst(hclone);
1597 }
1598
1599 if(!allSameLimits && initialLimitsFound) {
1600 if(!sameLimitsX) {
1601 fXaxis.SetRange(0, 0);
1602 if(newXAxis.GetXbins()->GetSize() != 0) {
1603 fXaxis.Set(newXAxis.GetNbins(), newXAxis.GetXbins()->GetArray());
1604 } else {
1605 fXaxis.Set(newXAxis.GetNbins(), newXAxis.GetXmin(), newXAxis.GetXmax());
1606 }
1607 }
1608 if(!sameLimitsY) {
1609 fYaxis.SetRange(0, 0);
1610 if(newYAxis.GetXbins()->GetSize() != 0) {
1611 fYaxis.Set(newYAxis.GetNbins(), newYAxis.GetXbins()->GetArray());
1612 } else {
1613 fYaxis.Set(newYAxis.GetNbins(), newYAxis.GetXmin(), newYAxis.GetXmax());
1614 }
1615 }
1616 fZaxis.Set(1, 0, 1);
1617 fNcells = (fXaxis.GetNbins() + 2) * (fYaxis.GetNbins() + 2);
1618 SetBinsLength(fNcells);
1619 if(fSumw2.fN != 0) {
1620 fSumw2.Set(fNcells);
1621 }
1622 }
1623
1624 if(!allHaveLimits) {
1625 // fill this histogram with all the data from buffers of histograms without limits
1626 while((h = static_cast<GHSym*>(next())) != nullptr) {
1627 if(h->GetXaxis()->GetXmin() >= h->GetXaxis()->GetXmax() && (h->fBuffer != nullptr)) {
1628 // no limits
1629 auto nbentries = static_cast<Int_t>(h->fBuffer[0]);
1630 for(Int_t i = 0; i < nbentries; i++) {
1631 Fill(h->fBuffer[3 * i + 2], h->fBuffer[3 * i + 3], h->fBuffer[3 * i + 1]);
1632 }
1633 // Entries from buffers have to be filled one by one
1634 // because FillN doesn't resize histograms.
1635 }
1636 }
1637 if(!initialLimitsFound) {
1638 if(hclone != nullptr) {
1639 inlist.Remove(hclone);
1640 delete hclone;
1641 }
1642 return static_cast<Long64_t>(GetEntries()); // all histograms have been processed
1643 }
1644 next.Reset();
1645 }
1646
1647 // merge bin contents and errors
1648 std::array<Double_t, kNstat> stats;
1649 std::array<Double_t, kNstat> totstats;
1650 for(Int_t i = 0; i < kNstat; ++i) {
1651 totstats[i] = stats[i] = 0;
1652 }
1653 GetStats(totstats.data());
1654 Double_t nentries = GetEntries();
1655 Int_t ix = 0;
1656 Int_t iy = 0;
1657 Double_t cu = 0.;
1658 Bool_t canExtend = CanExtendAllAxes();
1659 SetCanExtend(TH1::kNoAxis); // reset, otherwise setting the under/overflow will extend the axis
1660
1661 while((h = static_cast<GHSym*>(next())) != nullptr) {
1662 // skip empty histograms
1663 Double_t histEntries = h->GetEntries();
1664 if(h->fTsumw == 0 && histEntries == 0) {
1665 continue;
1666 }
1667
1668 // process only if the histogram has limits; otherwise it was processed before
1669 if(h->GetXaxis()->GetXmin() < h->GetXaxis()->GetXmax()) {
1670 // import statistics
1671 h->GetStats(stats.data());
1672 for(Int_t i = 0; i < kNstat; ++i) {
1673 totstats[i] += stats[i];
1674 }
1675 nentries += histEntries;
1676
1677 Int_t nx = h->GetXaxis()->GetNbins();
1678 Int_t ny = h->GetYaxis()->GetNbins();
1679
1680 for(Int_t biny = 0; biny <= ny + 1; ++biny) {
1681 if(!allSameLimits) {
1682 iy = fYaxis.FindBin(h->GetYaxis()->GetBinCenter(biny));
1683 } else {
1684 iy = biny;
1685 }
1686 for(Int_t binx = 0; binx <= nx + 1; ++binx) {
1687 cu = h->GetBinContent(binx, biny);
1688 if(!allSameLimits) {
1689 if(cu != 0 && ((!sameLimitsX && (binx == 0 || binx == nx + 1)) ||
1690 (!sameLimitsY && (biny == 0 || biny == ny + 1)))) {
1691 Error("Merge", "Cannot merge histograms - the histograms have"
1692 " different limits and undeflows/overflows are present."
1693 " The initial histogram is now broken!");
1694 return -1;
1695 }
1696 ix = fXaxis.FindBin(h->GetXaxis()->GetBinCenter(binx));
1697 } else {
1698 // case histograms with the same limits
1699 ix = binx;
1700 }
1701 Int_t ibin = GetBin(ix, iy);
1702
1703 if(ibin < 0) {
1704 continue;
1705 }
1706 AddBinContent(ibin, cu);
1707 if(fSumw2.fN != 0) {
1708 Double_t error1 = h->GetBinError(GetBin(binx, biny));
1709 fSumw2.fArray[ibin] += error1 * error1;
1710 }
1711 }
1712 }
1713 }
1714 }
1715 SetCanExtend(static_cast<UInt_t>(canExtend));
1716
1717 // copy merged stats
1718 PutStats(totstats.data());
1719 SetEntries(nentries);
1720 if(hclone != nullptr) {
1721 inlist.Remove(hclone);
1722 delete hclone;
1723 }
1724 return static_cast<Long64_t>(nentries);
1725}
1726
1727TProfile* GHSym::Profile(const char* name, Int_t firstbin, Int_t lastbin, Option_t* option) const
1728{
1729 // *-*-*-*-*Project a 2-D histogram into a profile histogram along X*-*-*-*-*-*
1730 // *-* ========================================================
1731 //
1732 // The projection is made from the channels along the Y axis
1733 // ranging from firstybin to lastybin included.
1734 // By default, bins 1 to ny are included
1735 // When all bins are included, the number of entries in the projection
1736 // is set to the number of entries of the 2-D histogram, otherwise
1737 // the number of entries is incremented by 1 for all non empty cells.
1738 //
1739 // if option "d" is specified, the profile is drawn in the current pad.
1740 //
1741 // if option "o" original axis range of the target axes will be
1742 // kept, but only bins inside the selected range will be filled.
1743 //
1744 // The option can also be used to specify the projected profile error type.
1745 // Values which can be used are 's', 'i', or 'g'. See TProfile::BuildOptions for details
1746 //
1747 // Using a TCutG object, it is possible to select a sub-range of a 2-D histogram.
1748 // One must create a graphical cut (mouse or C++) and specify the name
1749 // of the cut between [] in the option.
1750 // For example, with a TCutG named "cutg", one can call:
1751 // myhist->ProfileX(" ",firstybin,lastybin,"[cutg]");
1752 // To invert the cut, it is enough to put a "-" in front of its name:
1753 // myhist->ProfileX(" ",firstybin,lastybin,"[-cutg]");
1754 // It is possible to apply several cuts ("," means logical AND):
1755 // myhist->ProfileX(" ",firstybin,lastybin,"[cutg1,cutg2]");
1756 //
1757 // NOTE that if a TProfile named "name" exists in the current directory or pad with
1758 // a compatible axis the profile is reset and filled again with the projected contents of the TH2.
1759 // In the case of axis incompatibility an error is reported and a NULL pointer is returned.
1760 //
1761 // NOTE that the X axis attributes of the TH2 are copied to the X axis of the profile.
1762 //
1763 // NOTE that the default under- / overflow behavior differs from what ProjectionX
1764 // does! Profiles take the bin center into account, so here the under- and overflow
1765 // bins are ignored by default.
1766
1767 TString opt = option;
1768 // extract cut infor
1769 TString cut;
1770 Int_t i1 = opt.Index("[");
1771 if(i1 >= 0) {
1772 Int_t i2 = opt.Index("]");
1773 cut = opt(i1, i2 - i1 + 1);
1774 }
1775 opt.ToLower();
1776 bool originalRange = opt.Contains("o");
1777
1778 Int_t inN = fYaxis.GetNbins();
1779 const char* expectedName = "_pf";
1780
1781 Int_t firstOutBin = fXaxis.GetFirst();
1782 Int_t lastOutBin = fXaxis.GetLast();
1783 if(firstOutBin == 0 && lastOutBin == 0) {
1784 firstOutBin = 1;
1785 lastOutBin = fXaxis.GetNbins();
1786 }
1787
1788 if(lastbin < firstbin && fYaxis.TestBit(TAxis::kAxisRange)) {
1789 firstbin = fYaxis.GetFirst();
1790 lastbin = fYaxis.GetLast();
1791 // For special case of TAxis::SetRange, when first == 1 and last
1792 // = N and the range bit has been set, the TAxis will return 0
1793 // for both.
1794 if(firstbin == 0 && lastbin == 0) {
1795 firstbin = 1;
1796 lastbin = fYaxis.GetNbins();
1797 }
1798 }
1799 if(firstbin < 0) {
1800 firstbin = 1;
1801 }
1802 if(lastbin < 0) {
1803 lastbin = inN;
1804 }
1805 if(lastbin > inN + 1) {
1806 lastbin = inN;
1807 }
1808
1809 // Create the profile histogram
1810 char* pname = const_cast<char*>(name); // NOLINT(cppcoreguidelines-pro-type-const-cast)
1811 if((name != nullptr) && strcmp(name, expectedName) == 0) {
1812 auto nch = strlen(GetName()) + 5;
1813 pname = new char[nch];
1814 snprintf(pname, nch, "%s%s", GetName(), name);
1815 }
1816 TProfile* h1 = nullptr;
1817 // check if a profile with identical name exist
1818 // if compatible reset and re-use previous histogram
1819 TObject* h1obj = gROOT->FindObject(pname);
1820 if((h1obj != nullptr) && h1obj->InheritsFrom(TH1::Class())) {
1821 if(h1obj->IsA() != TProfile::Class()) {
1822 Error("DoProfile", "Histogram with name %s must be a TProfile and is a %s", name, h1obj->ClassName());
1823 return nullptr;
1824 }
1825 h1 = static_cast<TProfile*>(h1obj);
1826 // reset the existing histogram and set always the new binning for the axis
1827 // This avoid problems when the histogram already exists and the histograms is rebinned or its range has changed
1828 // (see https://savannah.cern.ch/bugs/?94101 or https://savannah.cern.ch/bugs/?95808 )
1829 h1->Reset();
1830 const TArrayD* xbins = fXaxis.GetXbins();
1831 if(xbins->fN == 0) {
1832 if(originalRange) {
1833 h1->SetBins(fXaxis.GetNbins(), fXaxis.GetXmin(), fXaxis.GetXmax());
1834 } else {
1835 h1->SetBins(lastOutBin - firstOutBin + 1, fXaxis.GetBinLowEdge(firstOutBin),
1836 fXaxis.GetBinUpEdge(lastOutBin));
1837 }
1838 } else {
1839 // case variable bins
1840 if(originalRange) {
1841 h1->SetBins(fXaxis.GetNbins(), xbins->fArray);
1842 } else {
1843 h1->SetBins(lastOutBin - firstOutBin + 1, &xbins->fArray[firstOutBin - 1]);
1844 }
1845 }
1846 }
1847
1848 Int_t ncuts = 0;
1849 if(opt.Contains("[")) {
1850 const_cast<GHSym*>(this)->GetPainter(); // NOLINT(cppcoreguidelines-pro-type-const-cast)
1851 if(fPainter != nullptr) {
1852 ncuts = fPainter->MakeCuts(const_cast<char*>(cut.Data())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
1853 }
1854 }
1855
1856 if(h1 == nullptr) {
1857 const TArrayD* bins = fXaxis.GetXbins();
1858 if(bins->fN == 0) {
1859 if(originalRange) {
1860 h1 = new TProfile(pname, GetTitle(), fXaxis.GetNbins(), fXaxis.GetXmin(), fXaxis.GetXmax(), opt);
1861 } else {
1862 h1 = new TProfile(pname, GetTitle(), lastOutBin - firstOutBin + 1, fXaxis.GetBinLowEdge(firstOutBin),
1863 fXaxis.GetBinUpEdge(lastOutBin), opt);
1864 }
1865 } else {
1866 // case variable bins
1867 if(originalRange) {
1868 h1 = new TProfile(pname, GetTitle(), fXaxis.GetNbins(), bins->fArray, opt);
1869 } else {
1870 h1 = new TProfile(pname, GetTitle(), lastOutBin - firstOutBin + 1, &bins->fArray[firstOutBin - 1], opt);
1871 }
1872 }
1873 }
1874 if(pname != name) {
1875 delete[] pname;
1876 }
1877
1878 // Copy attributes
1879 h1->GetXaxis()->ImportAttributes(&fXaxis);
1880 h1->SetLineColor(GetLineColor());
1881 h1->SetFillColor(GetFillColor());
1882 h1->SetMarkerColor(GetMarkerColor());
1883 h1->SetMarkerStyle(GetMarkerStyle());
1884
1885 // check if histogram is weighted
1886 // in case need to store sum of weight square/bin for the profile
1887 bool useWeights = (GetSumw2N() > 0);
1888 if(useWeights) {
1889 h1->Sumw2();
1890 }
1891
1892 // Fill the profile histogram
1893 // no entries/bin is available so can fill only using bin content as weight
1894 TArrayD& binSumw2 = *(h1->GetBinSumw2());
1895
1896 // implement filling of projected histogram
1897 // outbin is bin number of fXaxis (the projected axis). Loop is done on all bin of TH2 histograms
1898 // inbin is the axis being integrated. Loop is done only on the selected bins
1899 for(Int_t outbin = 0; outbin <= fXaxis.GetNbins() + 1; ++outbin) {
1900 if(fXaxis.TestBit(TAxis::kAxisRange) && (outbin < firstOutBin || outbin > lastOutBin)) {
1901 continue;
1902 }
1903
1904 // find corresponding bin number in h1 for outbin (binOut)
1905 Double_t xOut = fXaxis.GetBinCenter(outbin);
1906 Int_t binOut = h1->GetXaxis()->FindBin(xOut);
1907 if(binOut < 0) {
1908 continue;
1909 }
1910
1911 for(Int_t inbin = firstbin; inbin <= lastbin; ++inbin) {
1912 Int_t binx = outbin;
1913 Int_t biny = inbin;
1914
1915 if(ncuts != 0) {
1916 if(!fPainter->IsInside(binx, biny)) {
1917 continue;
1918 }
1919 }
1920 Int_t bin = GetBin(binx, biny);
1921 Double_t cxy = GetBinContent(bin);
1922
1923 if(cxy != 0.0) {
1924 Double_t tmp = 0;
1925 // the following fill update wrongly the fBinSumw2- need to save it before
1926 if(useWeights) {
1927 tmp = binSumw2.fArray[binOut];
1928 }
1929 h1->Fill(xOut, fYaxis.GetBinCenter(inbin), cxy);
1930 if(useWeights) {
1931 binSumw2.fArray[binOut] = tmp + fSumw2.fArray[bin];
1932 }
1933 }
1934 }
1935 }
1936
1937 // the statistics must be recalculated since by using the Fill method the total sum of weight^2 is
1938 // not computed correctly
1939 // for a profile does not much sense to re-use statistics of original TH2
1940 h1->ResetStats();
1941 // Also we need to set the entries since they have not been correctly calculated during the projection
1942 // we can only set them to the effective entries
1943 h1->SetEntries(h1->GetEffectiveEntries());
1944
1945 if(opt.Contains("d")) {
1946 TVirtualPad* padsav = gPad;
1947 TVirtualPad* pad = gROOT->GetSelectedPad();
1948 if(pad != nullptr) {
1949 pad->cd();
1950 }
1951 opt.Remove(opt.First("d"), 1);
1952 if(!gPad || !gPad->FindObject(h1)) {
1953 h1->Draw(opt);
1954 } else {
1955 h1->Paint(opt);
1956 }
1957 if(padsav != nullptr) {
1958 padsav->cd();
1959 }
1960 }
1961 return h1;
1962}
1963
1964TH1D* GHSym::Projection(const char* name, Int_t firstBin, Int_t lastBin, Option_t* option) const
1965{
1966 /// method for performing projection
1967
1968 const char* expectedName = "_pr";
1969
1970 TString opt = option;
1971 TString cut;
1972 Int_t i1 = opt.Index("[");
1973 if(i1 >= 0) {
1974 Int_t i2 = opt.Index("]");
1975 cut = opt(i1, i2 - i1 + 1);
1976 }
1977 opt.ToLower(); // must be called after having parsed the cut name
1978 bool originalRange = opt.Contains("o");
1979
1980 Int_t firstXBin = fXaxis.GetFirst();
1981 Int_t lastXBin = fXaxis.GetLast();
1982
1983 if(firstXBin == 0 && lastXBin == 0) {
1984 firstXBin = 1;
1985 lastXBin = fXaxis.GetNbins();
1986 }
1987
1988 if(lastBin < firstBin && fYaxis.TestBit(TAxis::kAxisRange)) {
1989 firstBin = fYaxis.GetFirst();
1990 lastBin = fYaxis.GetLast();
1991 // For special case of TAxis::SetRange, when first == 1 and last
1992 // = N and the range bit has been set, the TAxis will return 0
1993 // for both.
1994 if(firstBin == 0 && lastBin == 0) {
1995 firstBin = 1;
1996 lastBin = fYaxis.GetNbins();
1997 }
1998 }
1999 if(firstBin < 0) {
2000 firstBin = 0;
2001 }
2002 if(lastBin < 0) {
2003 lastBin = fYaxis.GetLast() + 1;
2004 }
2005 if(lastBin > fYaxis.GetLast() + 1) {
2006 lastBin = fYaxis.GetLast() + 1;
2007 }
2008
2009 // Create the projection histogram
2010 char* pname = const_cast<char*>(name); // NOLINT(cppcoreguidelines-pro-type-const-cast)
2011 if(name != nullptr && strcmp(name, expectedName) == 0) {
2012 auto nch = strlen(GetName()) + 4;
2013 pname = new char[nch];
2014 snprintf(pname, nch, "%s%s", GetName(), name);
2015 }
2016 TH1D* h1 = nullptr;
2017 // check if histogram with identical name exist
2018 // if compatible reset and re-use previous histogram
2019 // (see https://savannah.cern.ch/bugs/?54340)
2020 TObject* h1obj = gROOT->FindObject(pname);
2021 if((h1obj != nullptr) && h1obj->InheritsFrom(TH1::Class())) {
2022 if(h1obj->IsA() != TH1D::Class()) {
2023 Error("DoProjection", "Histogram with name %s must be a TH1D and is a %s", name, h1obj->ClassName());
2024 return nullptr;
2025 }
2026 h1 = static_cast<TH1D*>(h1obj);
2027 // reset the existing histogram and set always the new binning for the axis
2028 // This avoid problems when the histogram already exists and the histograms is rebinned or its range has changed
2029 // (see https://savannah.cern.ch/bugs/?94101 or https://savannah.cern.ch/bugs/?95808 )
2030 h1->Reset();
2031 const TArrayD* xbins = fXaxis.GetXbins();
2032 if(xbins->fN == 0) {
2033 if(originalRange) {
2034 h1->SetBins(fXaxis.GetNbins(), fXaxis.GetXmin(), fXaxis.GetXmax());
2035 } else {
2036 h1->SetBins(lastXBin - firstXBin + 1, fXaxis.GetBinLowEdge(firstXBin), fXaxis.GetBinUpEdge(lastXBin));
2037 }
2038 } else {
2039 // case variable bins
2040 if(originalRange) {
2041 h1->SetBins(fXaxis.GetNbins(), xbins->fArray);
2042 } else {
2043 h1->SetBins(lastXBin - firstXBin + 1, &(xbins->fArray[firstXBin - 1]));
2044 }
2045 }
2046 }
2047 Int_t ncuts = 0;
2048 if(opt.Contains("[")) {
2049 const_cast<GHSym*>(this)->GetPainter(); // NOLINT(cppcoreguidelines-pro-type-const-cast)
2050 if(fPainter != nullptr) {
2051 ncuts = fPainter->MakeCuts(const_cast<char*>(cut.Data())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
2052 }
2053 }
2054
2055 if(h1 == nullptr) {
2056 const TArrayD* bins = fXaxis.GetXbins();
2057 if(bins->fN == 0) {
2058 if(originalRange) {
2059 h1 = new TH1D(pname, GetTitle(), fXaxis.GetNbins(), fXaxis.GetXmin(), fXaxis.GetXmax());
2060 } else {
2061 h1 = new TH1D(pname, GetTitle(), lastXBin - firstXBin + 1, fXaxis.GetBinLowEdge(firstXBin),
2062 fXaxis.GetBinUpEdge(lastXBin));
2063 }
2064 } else {
2065 // case variable bins
2066 if(originalRange) {
2067 h1 = new TH1D(pname, GetTitle(), fXaxis.GetNbins(), bins->fArray);
2068 } else {
2069 h1 = new TH1D(pname, GetTitle(), lastXBin - firstXBin + 1, &(bins->fArray[firstXBin - 1]));
2070 }
2071 }
2072 if(opt.Contains("e") || (GetSumw2N() != 0)) {
2073 h1->Sumw2();
2074 }
2075 }
2076 if(pname != name) {
2077 delete[] pname;
2078 }
2079
2080 // Copy the axis attributes and the axis labels if needed.
2081 h1->GetXaxis()->ImportAttributes(&fXaxis);
2082 THashList* labels = const_cast<TAxis*>(&fXaxis)->GetLabels(); // NOLINT(cppcoreguidelines-pro-type-const-cast)
2083 if(labels != nullptr) {
2084 TIter iL(labels);
2085 TObjString* lb = nullptr;
2086 Int_t i = 1;
2087 while((lb = static_cast<TObjString*>(iL())) != nullptr) {
2088 h1->GetXaxis()->SetBinLabel(i, lb->String().Data());
2089 i++;
2090 }
2091 }
2092
2093 h1->SetLineColor(GetLineColor());
2094 h1->SetFillColor(GetFillColor());
2095 h1->SetMarkerColor(GetMarkerColor());
2096 h1->SetMarkerStyle(GetMarkerStyle());
2097
2098 // Fill the projected histogram
2099 Double_t totcont = 0;
2100 Bool_t computeErrors = h1->GetSumw2N() != 0;
2101
2102 // implement filling of projected histogram
2103 // xbin is bin number of xAxis (the projected axis). Loop is done on all bin of TH2 histograms
2104 // inbin is the axis being integrated. Loop is done only on the selected bins
2105 for(Int_t xbin = 0; xbin <= fXaxis.GetNbins() + 1; ++xbin) {
2106 Double_t err2 = 0.;
2107 Double_t cont = 0.;
2108 if(fXaxis.TestBit(TAxis::kAxisRange) && (xbin < firstXBin || xbin > lastXBin)) {
2109 continue;
2110 }
2111
2112 for(Int_t ybin = firstBin; ybin <= lastBin; ++ybin) {
2113 if(ncuts != 0) {
2114 if(!fPainter->IsInside(xbin, ybin)) {
2115 continue;
2116 }
2117 }
2118 // sum bin content and error if needed
2119 cont += GetCellContent(xbin, ybin);
2120 if(computeErrors) {
2121 Double_t exy = GetCellError(xbin, ybin);
2122 err2 += exy * exy;
2123 }
2124 }
2125 // find corresponding bin number in h1 for xbin
2126 Int_t binOut = h1->GetXaxis()->FindBin(fXaxis.GetBinCenter(xbin));
2127 h1->SetBinContent(binOut, cont);
2128 if(computeErrors) {
2129 h1->SetBinError(binOut, TMath::Sqrt(err2));
2130 }
2131 // sum all content
2132 totcont += cont;
2133 }
2134
2135 // check if we can re-use the original statistics from the previous histogram
2136 bool reuseStats = false;
2137 if((!fgStatOverflows && firstBin == 1 && lastBin == fYaxis.GetLast()) ||
2138 (fgStatOverflows && firstBin == 0 && lastBin == fYaxis.GetLast() + 1)) {
2139 reuseStats = true;
2140 } else {
2141 // also if total content match we can re-use
2142 double eps = 1.E-12;
2143 if(IsA() == GHSymF::Class()) {
2144 eps = 1.E-6;
2145 }
2146 if(fTsumw != 0 && TMath::Abs(fTsumw - totcont) < TMath::Abs(fTsumw) * eps) {
2147 reuseStats = true;
2148 }
2149 }
2150 if(ncuts != 0) {
2151 reuseStats = false;
2152 }
2153 // retrieve the statistics and set in projected histogram if we can re-use it
2154 bool reuseEntries = reuseStats;
2155 // can re-use entries if underflow/overflow are included
2156 reuseEntries &= static_cast<int>(firstBin == 0 && lastBin == fYaxis.GetLast() + 1);
2157 if(reuseStats) {
2158 std::array<Double_t, kNstat> stats;
2159 GetStats(stats.data());
2160 h1->PutStats(stats.data());
2161 } else {
2162 // the statistics is automatically recalulated since it is reset by the call to SetBinContent
2163 // we just need to set the entries since they have not been correctly calculated during the projection
2164 // we can only set them to the effective entries
2165 h1->SetEntries(h1->GetEffectiveEntries());
2166 }
2167 if(reuseEntries) {
2168 h1->SetEntries(fEntries);
2169 } else {
2170 // re-compute the entries
2171 // in case of error calculation (i.e. when Sumw2() is set)
2172 // use the effective entries for the entries
2173 // since this is the only way to estimate them
2174 Double_t entries = TMath::Floor(totcont + 0.5); // to avoid numerical rounding
2175 if(h1->GetSumw2N() != 0) {
2176 entries = h1->GetEffectiveEntries();
2177 }
2178 h1->SetEntries(entries);
2179 }
2180
2181 if(opt.Contains("d")) {
2182 TVirtualPad* padsav = gPad;
2183 TVirtualPad* pad = gROOT->GetSelectedPad();
2184 if(pad != nullptr) {
2185 pad->cd();
2186 }
2187 opt.Remove(opt.First("d"), 1);
2188 // remove also other options
2189 if(opt.Contains("e")) {
2190 opt.Remove(opt.First("e"), 1);
2191 }
2192 if(!gPad || !gPad->FindObject(h1)) {
2193 h1->Draw(opt);
2194 } else {
2195 h1->Paint(opt);
2196 }
2197 if(padsav != nullptr) {
2198 padsav->cd();
2199 }
2200 }
2201
2202 return h1;
2203}
2204
2205void GHSym::PutStats(Double_t* stats)
2206{
2207 // Replace current statistics with the values in array stats
2208 TH1::PutStats(stats);
2209 fTsumwy = stats[4];
2210 fTsumwy2 = stats[5];
2211 fTsumwxy = stats[6];
2212}
2213
2214GHSym* GHSym::Rebin2D(Int_t ngroup, const char* newname)
2215{
2216 // -*-*-*Rebin this histogram grouping ngroup/ngroup bins along the xaxis/yaxis together*-*-*-*-
2217 // =================================================================================
2218 // if newname is not blank a new temporary histogram hnew is created.
2219 // else the current histogram is modified (default)
2220 // The parameter ngroup indicates how many bins along the xaxis/yaxis of this
2221 // have to me merged into one bin of hnew
2222 // If the original histogram has errors stored (via Sumw2), the resulting
2223 // histograms has new errors correctly calculated.
2224 //
2225 // examples: if hpxpy is an existing GHSym histogram with 40 x 40 bins
2226 // hpxpy->Rebin2D(); // merges two bins along the xaxis and yaxis in one in hpxpy
2227 // // Carefull: previous contents of hpxpy are lost
2228 // hpxpy->Rebin2D(5); //merges five bins along the xaxisi and yaxis in one in hpxpy
2229 // GHSym* hnew = hpxpy->Rebin2D(5,"hnew"); // creates a new histogram hnew
2230 // // merging 5 bins of h1 along the xaxis and yaxis in one bin
2231 //
2232 // NOTE : If ngroup is not an exact divider of the number of bins,
2233 // along the xaxis/yaxis the top limit(s) of the rebinned histogram
2234 // is changed to the upper edge of the bin=newbins*ngroup
2235 // and the corresponding bins are added to
2236 // the overflow bin.
2237 // Statistics will be recomputed from the new bin contents.
2238
2239 Int_t nbins = fXaxis.GetNbins();
2240 Double_t min = fXaxis.GetXmin();
2241 Double_t max = fXaxis.GetXmax();
2242 if((ngroup <= 0) || (ngroup > nbins)) {
2243 Error("Rebin", "Illegal value of ngroup=%d", ngroup);
2244 return nullptr;
2245 }
2246
2247 Int_t newbins = nbins / ngroup;
2248
2249 // Save old bin contents into a new array
2250 Double_t entries = fEntries;
2251 auto* oldBins = new Double_t[(nbins + 2) * (nbins + 3) / 2];
2252 for(Int_t xbin = 0; xbin < nbins + 2; xbin++) {
2253 for(Int_t ybin = 0; ybin <= xbin; ybin++) {
2254 Int_t bin = GetBin(xbin, ybin);
2255 oldBins[bin] = GetBinContent(bin);
2256 }
2257 }
2258 Double_t* oldErrors = nullptr;
2259 if(fSumw2.fN != 0) {
2260 oldErrors = new Double_t[(nbins + 2) * (nbins + 3) / 2];
2261 for(Int_t xbin = 0; xbin < nbins + 2; xbin++) {
2262 for(Int_t ybin = 0; ybin <= xbin; ybin++) {
2263 Int_t bin = GetBin(xbin, ybin);
2264 oldErrors[bin] = GetBinError(bin);
2265 }
2266 }
2267 }
2268
2269 // create a clone of the old histogram if newname is specified
2270 GHSym* hnew = this;
2271 if((newname != nullptr) && (strlen(newname) != 0u)) {
2272 hnew = static_cast<GHSym*>(Clone());
2273 hnew->SetName(newname);
2274 }
2275
2276 // save original statistics
2277 std::array<Double_t, kNstat> stats;
2278 GetStats(stats.data());
2279 bool resetStat = false;
2280
2281 // change axis specs and rebuild bin contents array
2282 if(newbins * ngroup != nbins) {
2283 max = fXaxis.GetBinUpEdge(newbins * ngroup);
2284 resetStat = true; // stats must be reset because top bins will be moved to overflow bin
2285 }
2286 // save the TAttAxis members (reset by SetBins) for x axis
2287 Int_t nXdivisions = fXaxis.GetNdivisions();
2288 Color_t xAxisColor = fXaxis.GetAxisColor();
2289 Color_t xLabelColor = fXaxis.GetLabelColor();
2290 Style_t xLabelFont = fXaxis.GetLabelFont();
2291 Float_t xLabelOffset = fXaxis.GetLabelOffset();
2292 Float_t xLabelSize = fXaxis.GetLabelSize();
2293 Float_t xTickLength = fXaxis.GetTickLength();
2294 Float_t xTitleOffset = fXaxis.GetTitleOffset();
2295 Float_t xTitleSize = fXaxis.GetTitleSize();
2296 Color_t xTitleColor = fXaxis.GetTitleColor();
2297 Style_t xTitleFont = fXaxis.GetTitleFont();
2298 // save the TAttAxis members (reset by SetBins) for y axis
2299 Int_t nYdivisions = fYaxis.GetNdivisions();
2300 Color_t yAxisColor = fYaxis.GetAxisColor();
2301 Color_t yLabelColor = fYaxis.GetLabelColor();
2302 Style_t yLabelFont = fYaxis.GetLabelFont();
2303 Float_t yLabelOffset = fYaxis.GetLabelOffset();
2304 Float_t yLabelSize = fYaxis.GetLabelSize();
2305 Float_t yTickLength = fYaxis.GetTickLength();
2306 Float_t yTitleOffset = fYaxis.GetTitleOffset();
2307 Float_t yTitleSize = fYaxis.GetTitleSize();
2308 Color_t yTitleColor = fYaxis.GetTitleColor();
2309 Style_t yTitleFont = fYaxis.GetTitleFont();
2310
2311 // copy merged bin contents (ignore under/overflows)
2312 if(ngroup != 1) {
2313 if(fXaxis.GetXbins()->GetSize() > 0 || fYaxis.GetXbins()->GetSize() > 0) {
2314 // variable bin sizes in x or y, don't treat both cases separately
2315 auto* bins = new Double_t[newbins + 1];
2316 for(Int_t i = 0; i <= newbins; ++i) {
2317 bins[i] = fXaxis.GetBinLowEdge(1 + i * ngroup);
2318 }
2319 hnew->SetBins(newbins, bins, newbins, bins); // changes also errors array (if any)
2320 delete[] bins;
2321 } else {
2322 hnew->SetBins(newbins, min, max, newbins, min, max); // changes also errors array
2323 }
2324
2325 Int_t oldxbin = 1;
2326 Int_t oldybin = 1;
2327 for(Int_t xbin = 1; xbin <= newbins; ++xbin) {
2328 oldybin = 1;
2329 for(Int_t ybin = 1; ybin <= xbin; ++ybin) {
2330 Double_t binContent = 0.;
2331 Double_t binError = 0.;
2332 for(Int_t i = 0; i < ngroup; ++i) {
2333 if(oldxbin + i > nbins) {
2334 break;
2335 }
2336 for(Int_t j = 0; j < ngroup; ++j) {
2337 if(oldybin + j > nbins) {
2338 break;
2339 }
2340 // get global bin (same conventions as in GHSym::GetBin(xbin,ybin)
2341 Int_t bin = 0;
2342 if(oldybin + j <= oldxbin + i) {
2343 bin = oldxbin + i + (oldybin + j) * (2 * fXaxis.GetNbins() - (oldybin + j) + 3) / 2;
2344 } else {
2345 bin = oldybin + j + (oldxbin + i) * (2 * fXaxis.GetNbins() - (oldxbin + i) + 3) / 2;
2346 }
2347 binContent += oldBins[bin];
2348 if(oldErrors != nullptr) {
2349 binError += oldErrors[bin] * oldErrors[bin];
2350 }
2351 }
2352 }
2353 hnew->SetBinContent(xbin, ybin, binContent);
2354 if(oldErrors != nullptr) {
2355 hnew->SetBinError(xbin, ybin, TMath::Sqrt(binError));
2356 }
2357 oldybin += ngroup;
2358 }
2359 oldxbin += ngroup;
2360 }
2361
2362 // Recompute correct underflows and overflows.
2363
2364 // copy old underflow bin in x and y (0,0)
2365 hnew->SetBinContent(0, 0, oldBins[0]);
2366 if(oldErrors != nullptr) {
2367 hnew->SetBinError(0, 0, oldErrors[0]);
2368 }
2369
2370 // calculate new overflow bin in x and y (newbins+1,newbins+1)
2371 Double_t binContent = 0.;
2372 Double_t binError = 0.;
2373 for(Int_t xbin = oldxbin; xbin <= nbins + 1; ++xbin) {
2374 for(Int_t ybin = oldybin; ybin <= xbin; ++ybin) {
2375 Int_t bin = xbin + ybin * (2 * nbins - ybin + 3) / 2;
2376 binContent += oldBins[bin];
2377 if(oldErrors != nullptr) {
2378 binError += oldErrors[bin] * oldErrors[bin];
2379 }
2380 }
2381 }
2382 hnew->SetBinContent(newbins + 1, newbins + 1, binContent);
2383 if(oldErrors != nullptr) {
2384 hnew->SetBinError(newbins + 1, newbins + 1, TMath::Sqrt(binError));
2385 }
2386
2387 // calculate new underflow bin in x and overflow in y (0,newbins+1)
2388 binContent = 0.;
2389 binError = 0.;
2390 for(Int_t ybin = oldybin; ybin <= nbins + 1; ++ybin) {
2391 Int_t bin = ybin * (2 * nbins - ybin + 3) / 2;
2392 binContent += oldBins[bin];
2393 if(oldErrors != nullptr) {
2394 binError += oldErrors[bin] * oldErrors[bin];
2395 }
2396 }
2397 hnew->SetBinContent(0, newbins + 1, binContent);
2398 if(oldErrors != nullptr) {
2399 hnew->SetBinError(0, newbins + 1, TMath::Sqrt(binError));
2400 }
2401
2402 // calculate new overflow bin in x and underflow in y (newbins+1,0)
2403 binContent = 0.;
2404 binError = 0.;
2405 for(Int_t xbin = oldxbin; xbin <= nbins + 1; ++xbin) {
2406 Int_t bin = xbin;
2407 binContent += oldBins[bin];
2408 if(oldErrors != nullptr) {
2409 binError += oldErrors[bin] * oldErrors[bin];
2410 }
2411 }
2412 hnew->SetBinContent(newbins + 1, 0, binContent);
2413 if(oldErrors != nullptr) {
2414 hnew->SetBinError(newbins + 1, 0, TMath::Sqrt(binError));
2415 }
2416
2417 // recompute under/overflow contents in y for the new x bins
2418 Int_t oldxbin2 = 1;
2419 for(Int_t xbin = 1; xbin <= newbins; ++xbin) {
2420 Double_t binContent0 = 0.;
2421 Double_t binContent2 = 0.;
2422 Double_t binError0 = 0.;
2423 Double_t binError2 = 0.;
2424 for(Int_t i = 0; i < ngroup; ++i) {
2425 if(oldxbin2 + i > nbins) {
2426 break;
2427 }
2428 // old underflow bin (in y)
2429 Int_t ufbin = oldxbin2 + i;
2430 binContent0 += oldBins[ufbin];
2431 if(oldErrors != nullptr) {
2432 binError0 += oldErrors[ufbin] * oldErrors[ufbin];
2433 }
2434 for(Int_t ybin = oldybin; ybin <= nbins + 1; ++ybin) {
2435 // old overflow bin (in y)
2436 Int_t ofbin = ufbin + ybin * (nbins + 2);
2437 binContent2 += oldBins[ofbin];
2438 if(oldErrors != nullptr) {
2439 binError2 += oldErrors[ofbin] * oldErrors[ofbin];
2440 }
2441 }
2442 }
2443 hnew->SetBinContent(xbin, 0, binContent0);
2444 hnew->SetBinContent(xbin, newbins + 1, binContent2);
2445 if(oldErrors != nullptr) {
2446 hnew->SetBinError(xbin, 0, TMath::Sqrt(binError0));
2447 hnew->SetBinError(xbin, newbins + 1, TMath::Sqrt(binError2));
2448 }
2449 oldxbin2 += ngroup;
2450 }
2451
2452 // recompute under/overflow contents in x for the new y bins
2453 Int_t oldybin2 = 1;
2454 for(Int_t ybin = 1; ybin <= newbins; ++ybin) {
2455 Double_t binContent0 = 0.;
2456 Double_t binContent2 = 0.;
2457 Double_t binError0 = 0.;
2458 Double_t binError2 = 0.;
2459 for(Int_t i = 0; i < ngroup; ++i) {
2460 if(oldybin2 + i > nbins) {
2461 break;
2462 }
2463 // old underflow bin (in x)
2464 Int_t ufbin = (oldybin2 + i) * (nbins + 2);
2465 binContent0 += oldBins[ufbin];
2466 if(oldErrors != nullptr) {
2467 binError0 += oldErrors[ufbin] * oldErrors[ufbin];
2468 }
2469 for(Int_t xbin = oldxbin; xbin <= nbins + 1; ++xbin) {
2470 Int_t ofbin = ufbin + xbin;
2471 binContent2 += oldBins[ofbin];
2472 if(oldErrors != nullptr) {
2473 binError2 += oldErrors[ofbin] * oldErrors[ofbin];
2474 }
2475 }
2476 }
2477 hnew->SetBinContent(0, ybin, binContent0);
2478 hnew->SetBinContent(newbins + 1, ybin, binContent2);
2479 if(oldErrors != nullptr) {
2480 hnew->SetBinError(0, ybin, TMath::Sqrt(binError0));
2481 hnew->SetBinError(newbins + 1, ybin, TMath::Sqrt(binError2));
2482 }
2483 oldybin2 += ngroup;
2484 }
2485 }
2486
2487 // Restore x axis attributes
2488 fXaxis.SetNdivisions(nXdivisions);
2489 fXaxis.SetAxisColor(xAxisColor);
2490 fXaxis.SetLabelColor(xLabelColor);
2491 fXaxis.SetLabelFont(xLabelFont);
2492 fXaxis.SetLabelOffset(xLabelOffset);
2493 fXaxis.SetLabelSize(xLabelSize);
2494 fXaxis.SetTickLength(xTickLength);
2495 fXaxis.SetTitleOffset(xTitleOffset);
2496 fXaxis.SetTitleSize(xTitleSize);
2497 fXaxis.SetTitleColor(xTitleColor);
2498 fXaxis.SetTitleFont(xTitleFont);
2499 // Restore y axis attributes
2500 fYaxis.SetNdivisions(nYdivisions);
2501 fYaxis.SetAxisColor(yAxisColor);
2502 fYaxis.SetLabelColor(yLabelColor);
2503 fYaxis.SetLabelFont(yLabelFont);
2504 fYaxis.SetLabelOffset(yLabelOffset);
2505 fYaxis.SetLabelSize(yLabelSize);
2506 fYaxis.SetTickLength(yTickLength);
2507 fYaxis.SetTitleOffset(yTitleOffset);
2508 fYaxis.SetTitleSize(yTitleSize);
2509 fYaxis.SetTitleColor(yTitleColor);
2510 fYaxis.SetTitleFont(yTitleFont);
2511
2512 // restore statistics and entries modified by SetBinContent
2513 hnew->SetEntries(entries);
2514 if(!resetStat) {
2515 hnew->PutStats(stats.data());
2516 }
2517
2518 delete[] oldBins;
2519 delete[] oldErrors;
2520 return hnew;
2521}
2522
2523void GHSym::Reset(Option_t* option)
2524{
2525 //*-*-*-*-*-*-*-*Reset this histogram: contents, errors, etc*-*-*-*-*-*-*-*
2526 //*-* ===========================================
2527
2528 TH1::Reset(option);
2529 TString opt = option;
2530 opt.ToUpper();
2531
2532 if(opt.Contains("ICE") && !opt.Contains("S")) {
2533 return;
2534 }
2535 fTsumwy = 0;
2536 fTsumwy2 = 0;
2537 fTsumwxy = 0;
2538 if(fMatrix != nullptr) {
2539 try {
2540 delete fMatrix;
2541 fMatrix = nullptr;
2542 } catch(std::exception& e) {
2543 fMatrix = nullptr;
2544 }
2545 }
2546}
2547
2548void GHSym::SetCellContent(Int_t binx, Int_t biny, Double_t content)
2549{
2550 SetBinContent(GetBin(binx, biny), content);
2551}
2552
2553void GHSym::SetCellError(Int_t binx, Int_t biny, Double_t content)
2554{
2555 SetBinError(GetBin(binx, biny), content);
2556}
2557
2559{
2560 // When the mouse is moved in a pad containing a 2-d view of this histogram
2561 // a second canvas shows the projection along X corresponding to the
2562 // mouse position along Y.
2563 // To stop the generation of the projections, delete the canvas
2564 // containing the projection.
2565
2566 GetPainter();
2567 if(fPainter != nullptr) {
2568 fPainter->SetShowProjection("x", nbins);
2569 }
2570}
2571
2573{
2574 // When the mouse is moved in a pad containing a 2-d view of this histogram
2575 // a second canvas shows the projection along Y corresponding to the
2576 // mouse position along X.
2577 // To stop the generation of the projections, delete the canvas
2578 // containing the projection.
2579
2580 GetPainter();
2581 if(fPainter != nullptr) {
2582 fPainter->SetShowProjection("y", nbins);
2583 }
2584}
2585
2586TH1* GHSym::ShowBackground(Int_t niter, Option_t* option)
2587{
2588 // This function calculates the background spectrum in this histogram.
2589 // The background is returned as a histogram.
2590 // to be implemented (may be)
2591
2592 return reinterpret_cast<TH1*>(gROOT->ProcessLineFast( // NOLINT(performance-no-int-to-ptr)
2593 Form(R"(TSpectrum2::StaticBackground((TH1*)0x%lx,%d,"%s"))", reinterpret_cast<ULong_t>(this), niter, option)));
2594}
2595
2596Int_t GHSym::ShowPeaks(Double_t sigma, Option_t* option, Double_t threshold)
2597{
2598 // Interface to TSpectrum2::Search
2599 // the function finds peaks in this histogram where the width is > sigma
2600 // and the peak maximum greater than threshold*maximum bin content of this.
2601 // for more detauils see TSpectrum::Search.
2602 // note the difference in the default value for option compared to TSpectrum2::Search
2603 // option="" by default (instead of "goff")
2604
2605 return static_cast<Int_t>(gROOT->ProcessLineFast(
2606 Form(R"(TSpectrum2::StaticSearch((TH1*)0x%lx,%g,"%s",%g))", reinterpret_cast<ULong_t>(this), sigma, option, threshold)));
2607}
2608
2609void GHSym::Smooth(Int_t ntimes, Option_t* option)
2610{
2611 // Smooth bin contents of this 2-d histogram using kernel algorithms
2612 // similar to the ones used in the raster graphics community.
2613 // Bin contents in the active range are replaced by their smooth values.
2614 // If Errors are defined via Sumw2, they are also scaled and computed.
2615 // However, note the resulting errors will be correlated between different-bins, so
2616 // the errors should not be used blindly to perform any calculation involving several bins,
2617 // like fitting the histogram. One would need to compute also the bin by bin correlation matrix.
2618 //
2619 // 3 kernels are proposed k5a, k5b and k3a.
2620 // k5a and k5b act on 5x5 cells (i-2,i-1,i,i+1,i+2, and same for j)
2621 // k5b is a bit more stronger in smoothing
2622 // k3a acts only on 3x3 cells (i-1,i,i+1, and same for j).
2623 // By default the kernel "k5a" is used. You can select the kernels "k5b" or "k3a"
2624 // via the option argument.
2625 // If TAxis::SetRange has been called on the x or/and y axis, only the bins
2626 // in the specified range are smoothed.
2627 // In the current implementation if the first argument is not used (default value=1).
2628 //
2629 // implementation by David McKee (dmckee@bama.ua.edu). Extended by Rene Brun
2630
2631 std::array<std::array<Double_t, 5>, 5> k5a = {{{0, 0, 1, 0, 0}, {0, 2, 2, 2, 0}, {1, 2, 5, 2, 1}, {0, 2, 2, 2, 0}, {0, 0, 1, 0, 0}}};
2632 std::array<std::array<Double_t, 5>, 5> k5b = {{{0, 1, 2, 1, 0}, {1, 2, 4, 2, 1}, {2, 4, 8, 4, 2}, {1, 2, 4, 2, 1}, {0, 1, 2, 1, 0}}};
2633 std::array<std::array<Double_t, 3>, 3> k3a = {{{0, 1, 0}, {1, 2, 1}, {0, 1, 0}}};
2634
2635 if(ntimes > 1) {
2636 Warning("Smooth", "Currently only ntimes=1 is supported");
2637 }
2638 TString opt = option;
2639 opt.ToLower();
2640 Int_t ksize_x = k5a.size();
2641 Int_t ksize_y = k5a.size();
2642 Double_t* kernel = k5a.data()->data();
2643 if(opt.Contains("k5b")) {
2644 kernel = k5b.data()->data();
2645 }
2646 if(opt.Contains("k3a")) {
2647 kernel = k3a.data()->data();
2648 ksize_x = k3a.size();
2649 ksize_y = k3a.size();
2650 }
2651
2652 // find i,j ranges
2653 Int_t ifirst = fXaxis.GetFirst();
2654 Int_t ilast = fXaxis.GetLast();
2655 Int_t jfirst = fYaxis.GetFirst();
2656 Int_t jlast = fYaxis.GetLast();
2657
2658 // Determine the size of the bin buffer(s) needed
2659 Double_t nentries = fEntries;
2660 Int_t nx = GetNbinsX();
2661 Int_t ny = GetNbinsY();
2662 Int_t bufSize = (nx + 2) * (ny + 2);
2663 auto* buf = new Double_t[bufSize];
2664 Double_t* ebuf = nullptr;
2665 if(fSumw2.fN != 0) {
2666 ebuf = new Double_t[bufSize];
2667 }
2668
2669 // Copy all the data to the temporary buffers
2670 for(Int_t i = ifirst; i <= ilast; ++i) {
2671 for(Int_t j = jfirst; j <= jlast; ++j) {
2672 Int_t bin = GetBin(i, j);
2673 buf[bin] = GetBinContent(bin);
2674 if(ebuf != nullptr) {
2675 ebuf[bin] = GetBinError(bin);
2676 }
2677 }
2678 }
2679
2680 // Kernel tail sizes (kernel sizes must be odd for this to work!)
2681 Int_t x_push = (ksize_x - 1) / 2;
2682 Int_t y_push = (ksize_y - 1) / 2;
2683
2684 // main work loop
2685 for(Int_t i = ifirst; i <= ilast; ++i) {
2686 for(Int_t j = jfirst; j <= jlast; ++j) {
2687 Double_t content = 0.0;
2688 Double_t error = 0.0;
2689 Double_t norm = 0.0;
2690
2691 for(Int_t n = 0; n < ksize_x; ++n) {
2692 for(Int_t m = 0; m < ksize_y; ++m) {
2693 Int_t xb = i + (n - x_push);
2694 Int_t yb = j + (m - y_push);
2695 if((xb >= 1) && (xb <= nx) && (yb >= 1) && (yb <= ny)) {
2696 Int_t bin = GetBin(xb, yb);
2697 Double_t k = kernel[n * ksize_y + m];
2698 if(k != 0.0) {
2699 norm += k;
2700 content += k * buf[bin];
2701 if(ebuf != nullptr) {
2702 error += k * k * ebuf[bin] * ebuf[bin];
2703 }
2704 }
2705 }
2706 }
2707 }
2708
2709 if(norm != 0.0) {
2710 SetBinContent(i, j, content / norm);
2711 if(ebuf != nullptr) {
2712 error /= (norm * norm);
2713 SetBinError(i, j, sqrt(error));
2714 }
2715 }
2716 }
2717 }
2718 fEntries = nentries;
2719
2720 delete[] buf;
2721 delete[] ebuf;
2722}
2723
2724//------------------------------------------------------------
2725// GHSymF methods (float = four bytes per cell)
2726//------------------------------------------------------------
2727
2729{
2730 SetBinsLength(9);
2731 if(fgDefaultSumw2) {
2732 Sumw2();
2733 }
2734}
2735
2736GHSymF::GHSymF(const char* name, const char* title, Int_t nbins, Double_t low, Double_t up)
2737 : GHSym(name, title, nbins, low, up)
2738{
2739 TArrayF::Set(fNcells);
2740 if(fgDefaultSumw2) {
2741 Sumw2();
2742 }
2743
2744 if(low >= up) {
2745 SetBuffer(fgBufferSize);
2746 }
2747}
2748
2749GHSymF::GHSymF(const char* name, const char* title, Int_t nbins, const Double_t* bins) : GHSym(name, title, nbins, bins)
2750{
2751 TArrayF::Set(fNcells);
2752 if(fgDefaultSumw2) {
2753 Sumw2();
2754 }
2755}
2756
2757GHSymF::GHSymF(const char* name, const char* title, Int_t nbins, const Float_t* bins) : GHSym(name, title, nbins, bins)
2758{
2759 TArrayF::Set(fNcells);
2760 if(fgDefaultSumw2) {
2761 Sumw2();
2762 }
2763}
2764
2766 : GHSym(rhs), TArrayF(rhs)
2767{
2768 rhs.Copy(*this);
2769}
2770
2771GHSymF::GHSymF(GHSymF&& rhs) noexcept
2772 : GHSym(std::move(rhs)), TArrayF(rhs)
2773{
2774 rhs.Copy(*this);
2775}
2776
2777GHSymF::~GHSymF() = default;
2778
2779TH2F* GHSymF::GetMatrix(bool force)
2780{
2781 if(Matrix() != nullptr && !force) {
2782 return static_cast<TH2F*>(Matrix());
2783 }
2784 if(force) {
2785 delete Matrix();
2786 }
2787
2788 Matrix(new TH2F(Form("%s_mat", GetName()), GetTitle(), fXaxis.GetNbins(), fXaxis.GetXmin(), fXaxis.GetXmax(),
2789 fYaxis.GetNbins(), fYaxis.GetXmin(), fYaxis.GetXmax()));
2790 // copy cell contents (including all overflow and underflow cells)
2791 for(int i = 0; i < fXaxis.GetNbins() + 2; ++i) {
2792 for(int j = 0; j < fXaxis.GetNbins() + 2; ++j) {
2793 Matrix()->SetBinContent(i, j, GetBinContent(i, j));
2794 }
2795 }
2796 return static_cast<TH2F*>(Matrix());
2797}
2798
2799void GHSymF::Copy(TObject& rhs) const
2800{
2801 GHSym::Copy(static_cast<GHSymF&>(rhs));
2802}
2803
2804TH1* GHSymF::DrawCopy(Option_t* option, const char* name_postfix) const
2805{
2806 // Draw copy.
2807
2808 TString opt = option;
2809 opt.ToLower();
2810 if(gPad != nullptr && !opt.Contains("same")) {
2811 gPad->Clear();
2812 }
2813 TString newName = (name_postfix) != nullptr ? TString::Format("%s%s", GetName(), name_postfix) : "";
2814 TH1* newth1 = static_cast<TH1*>(Clone(newName));
2815 newth1->SetDirectory(nullptr);
2816 newth1->SetBit(kCanDelete);
2817 newth1->AppendPad(option);
2818 return newth1;
2819}
2820
2821Double_t GHSymF::GetBinContent(Int_t bin) const
2822{
2823 // Get bin content.
2824
2825 if(fBuffer != nullptr) {
2826 const_cast<GHSymF*>(this)->BufferEmpty(); // NOLINT(cppcoreguidelines-pro-type-const-cast)
2827 }
2828 if(bin < 0) {
2829 bin = 0;
2830 }
2831 if(bin >= fNcells) {
2832 bin = fNcells - 1;
2833 }
2834 if(fArray == nullptr) {
2835 return 0;
2836 }
2837 return static_cast<Double_t>(fArray[bin]);
2838}
2839
2840void GHSymF::Reset(Option_t* option)
2841{
2842 //*-*-*-*-*-*-*-*Reset this histogram: contents, errors, etc*-*-*-*-*-*-*-*
2843 //*-* ===========================================
2844
2845 GHSym::Reset(option);
2846 TArrayF::Reset();
2847}
2848
2849void GHSymF::SetBinContent(Int_t bin, Double_t content)
2850{
2851 // Set bin content
2852 fEntries++;
2853 fTsumw = 0;
2854 if(bin < 0) {
2855 return;
2856 }
2857 if(bin >= fNcells) {
2858 return;
2859 }
2860 fArray[bin] = static_cast<Float_t>(content);
2861}
2862
2864{
2865 // Set total number of bins including under/overflow
2866 // Reallocate bin contents array
2867
2868 if(n < 0) {
2869 n = (fXaxis.GetNbins() + 2) * (fYaxis.GetNbins() + 2);
2870 }
2871 fNcells = n;
2872 TArrayF::Set(n);
2873}
2874
2876{
2877 // Operator =
2878
2879 if(this != &h1) {
2880 h1.Copy(*this);
2881 }
2882 return *this;
2883}
2884
2886{
2887 // Operator =
2888
2889 if(this != &h1) {
2890 h1.Copy(*this);
2891 }
2892 return *this;
2893}
2894
2895GHSymF operator*(Float_t c1, GHSymF& h1)
2896{
2897 // Operator *
2898
2899 GHSymF hnew = h1;
2900 hnew.Scale(c1);
2901 hnew.SetDirectory(nullptr);
2902 return hnew;
2903}
2904
2906{
2907 // Operator +
2908
2909 GHSymF hnew = h1;
2910 hnew.Add(&h2, 1);
2911 hnew.SetDirectory(nullptr);
2912 return hnew;
2913}
2914
2916{
2917 // Operator -
2918
2919 GHSymF hnew = h1;
2920 hnew.Add(&h2, -1);
2921 hnew.SetDirectory(nullptr);
2922 return hnew;
2923}
2924
2926{
2927 // Operator *
2928
2929 GHSymF hnew = h1;
2930 hnew.Multiply(&h2);
2931 hnew.SetDirectory(nullptr);
2932 return hnew;
2933}
2934
2936{
2937 // Operator /
2938
2939 GHSymF hnew = h1;
2940 hnew.Divide(&h2);
2941 hnew.SetDirectory(nullptr);
2942 return hnew;
2943}
2944
2945//------------------------------------------------------------
2946// GHSymD methods (double = eight bytes per cell)
2947//------------------------------------------------------------
2948
2950{
2951 SetBinsLength(9);
2952 if(fgDefaultSumw2) {
2953 Sumw2();
2954 }
2955}
2956
2957GHSymD::GHSymD(const char* name, const char* title, Int_t nbins, Double_t low, Double_t up)
2958 : GHSym(name, title, nbins, low, up)
2959{
2960 TArrayD::Set(fNcells);
2961 if(fgDefaultSumw2) {
2962 Sumw2();
2963 }
2964
2965 if(low >= up) {
2966 SetBuffer(fgBufferSize);
2967 }
2968}
2969
2970GHSymD::GHSymD(const char* name, const char* title, Int_t nbins, const Double_t* bins) : GHSym(name, title, nbins, bins)
2971{
2972 TArrayD::Set(fNcells);
2973 if(fgDefaultSumw2) {
2974 Sumw2();
2975 }
2976}
2977
2978GHSymD::GHSymD(const char* name, const char* title, Int_t nbins, const Float_t* bins) : GHSym(name, title, nbins, bins)
2979{
2980 TArrayD::Set(fNcells);
2981 if(fgDefaultSumw2) {
2982 Sumw2();
2983 }
2984}
2985
2987 : GHSym(rhs), TArrayD(rhs)
2988{
2989 rhs.Copy(*this);
2990}
2991
2992GHSymD::GHSymD(GHSymD&& rhs) noexcept
2993 : GHSym(std::move(rhs)), TArrayD(rhs)
2994{
2995 rhs.Copy(*this);
2996}
2997
2998GHSymD::~GHSymD() = default;
2999
3000TH2D* GHSymD::GetMatrix(bool force)
3001{
3002 if(Matrix() != nullptr && !force) {
3003 return static_cast<TH2D*>(Matrix());
3004 }
3005 if(force) {
3006 delete Matrix();
3007 }
3008
3009 Matrix(new TH2D(Form("%s_mat", GetName()), Form("%s;%s;%s", GetTitle(), fXaxis.GetTitle(), fYaxis.GetTitle()),
3010 fXaxis.GetNbins(), fXaxis.GetXmin(), fXaxis.GetXmax(), fYaxis.GetNbins(), fYaxis.GetXmin(),
3011 fYaxis.GetXmax()));
3012 // copy cell contents (including all overflow and underflow cells)
3013 for(int i = 0; i < fXaxis.GetNbins() + 2; ++i) {
3014 for(int j = 0; j < fXaxis.GetNbins() + 2; ++j) {
3015 Matrix()->SetBinContent(i, j, GetBinContent(i, j));
3016 }
3017 }
3018 return static_cast<TH2D*>(Matrix());
3019}
3020
3021void GHSymD::Copy(TObject& rhs) const
3022{
3023 GHSym::Copy(static_cast<GHSymD&>(rhs));
3024}
3025
3026TH1* GHSymD::DrawCopy(Option_t* option, const char* name_postfix) const
3027{
3028 // Draw copy.
3029
3030 TString opt = option;
3031 opt.ToLower();
3032 if(gPad != nullptr && !opt.Contains("same")) {
3033 gPad->Clear();
3034 }
3035 TString newName = (name_postfix) != nullptr ? TString::Format("%s%s", GetName(), name_postfix) : "";
3036 TH1* newth1 = static_cast<TH1*>(Clone(newName));
3037 newth1->SetDirectory(nullptr);
3038 newth1->SetBit(kCanDelete);
3039 newth1->AppendPad(option);
3040 return newth1;
3041}
3042
3043Double_t GHSymD::GetBinContent(Int_t bin) const
3044{
3045 // Get bin content.
3046 if(fBuffer != nullptr) {
3047 const_cast<GHSymD*>(this)->BufferEmpty(); // NOLINT(cppcoreguidelines-pro-type-const-cast)
3048 }
3049 if(bin < 0) {
3050 bin = 0;
3051 }
3052 if(bin >= fNcells) {
3053 bin = fNcells - 1;
3054 }
3055 if(fArray == nullptr) {
3056 return 0;
3057 }
3058 return static_cast<Double_t>(fArray[bin]);
3059}
3060
3061void GHSymD::Reset(Option_t* option)
3062{
3063 //*-*-*-*-*-*-*-*Reset this histogram: contents, errors, etc*-*-*-*-*-*-*-*
3064 //*-* ===========================================
3065
3066 GHSym::Reset(option);
3067 TArrayD::Reset();
3068}
3069
3070void GHSymD::SetBinContent(Int_t bin, Double_t content)
3071{
3072 // Set bin content
3073 fEntries++;
3074 fTsumw = 0;
3075 if(bin < 0) {
3076 return;
3077 }
3078 if(bin >= fNcells) {
3079 return;
3080 }
3081 fArray[bin] = static_cast<Float_t>(content);
3082}
3083
3085{
3086 // Set total number of bins including under/overflow
3087 // Reallocate bin contents array
3088
3089 if(n < 0) {
3090 n = (fXaxis.GetNbins() + 2) * (fYaxis.GetNbins() + 2);
3091 }
3092 fNcells = n;
3093 TArrayD::Set(n);
3094}
3095
3097{
3098 // Operator =
3099
3100 if(this != &h1) {
3101 h1.Copy(*this);
3102 }
3103 return *this;
3104}
3105
3107{
3108 // Operator =
3109
3110 if(this != &h1) {
3111 h1.Copy(*this);
3112 }
3113 return *this;
3114}
3115
3116GHSymD operator*(Float_t c1, GHSymD& h1)
3117{
3118 // Operator *
3119
3120 GHSymD hnew = h1;
3121 hnew.Scale(c1);
3122 hnew.SetDirectory(nullptr);
3123 return hnew;
3124}
3125
3127{
3128 // Operator +
3129
3130 GHSymD hnew = h1;
3131 hnew.Add(&h2, 1);
3132 hnew.SetDirectory(nullptr);
3133 return hnew;
3134}
3135
3137{
3138 // Operator -
3139
3140 GHSymD hnew = h1;
3141 hnew.Add(&h2, -1);
3142 hnew.SetDirectory(nullptr);
3143 return hnew;
3144}
3145
3147{
3148 // Operator *
3149
3150 GHSymD hnew = h1;
3151 hnew.Multiply(&h2);
3152 hnew.SetDirectory(nullptr);
3153 return hnew;
3154}
3155
3157{
3158 // Operator /
3159
3160 GHSymD hnew = h1;
3161 hnew.Divide(&h2);
3162 hnew.SetDirectory(nullptr);
3163 return hnew;
3164}
3165
3166//------------------------------------------------------------
GHSymF operator/(GHSymF &h1, GHSymF &h2)
Definition GHSym.cxx:2935
GHSymF operator+(GHSymF &h1, GHSymF &h2)
Definition GHSym.cxx:2905
GHSymF operator-(GHSymF &h1, GHSymF &h2)
Definition GHSym.cxx:2915
GHSymF operator*(Float_t c1, GHSymF &h1)
Definition GHSym.cxx:2895
TH1D * hist
Definition UserFillObj.h:3
Double_t GetBinContent(Int_t bin) const override
Definition GHSym.cxx:3043
TH1 * DrawCopy(Option_t *option="", const char *name_postfix="_copy") const override
Definition GHSym.cxx:3026
GHSymD()
Definition GHSym.cxx:2949
TH2D * GetMatrix(bool force=false)
Definition GHSym.cxx:3000
void Reset(Option_t *option="") override
Definition GHSym.cxx:3061
void Copy(TObject &rh) const override
Definition GHSym.cxx:3021
void SetBinsLength(Int_t n=-1) override
Definition GHSym.cxx:3084
GHSymD & operator=(const GHSymD &h1)
Definition GHSym.cxx:3096
void SetBinContent(Int_t bin, Double_t content) override
Definition GHSym.cxx:3070
Double_t GetBinContent(Int_t bin) const override
Definition GHSym.cxx:2821
void SetBinsLength(Int_t n=-1) override
Definition GHSym.cxx:2863
GHSymF & operator=(const GHSymF &h1)
Definition GHSym.cxx:2875
void Reset(Option_t *option="") override
Definition GHSym.cxx:2840
void SetBinContent(Int_t bin, Double_t content) override
Definition GHSym.cxx:2849
TH1 * DrawCopy(Option_t *option="", const char *name_postfix="_copy") const override
Definition GHSym.cxx:2804
TH2F * GetMatrix(bool force=false)
Definition GHSym.cxx:2779
void Copy(TObject &rh) const override
Definition GHSym.cxx:2799
GHSymF()
Definition GHSym.cxx:2728
Definition GHSym.h:12
virtual Double_t DoIntegral(Int_t binx1, Int_t binx2, Int_t biny1, Int_t biny2, Double_t &error, Option_t *option, Bool_t doError=kFALSE) const
Definition GHSym.cxx:221
Double_t Integral(Option_t *option="") const override
Definition GHSym.cxx:1086
Long64_t Merge(TCollection *list) override
Definition GHSym.cxx:1455
virtual Double_t GetCovariance(Int_t axis1=1, Int_t axis2=2) const
Definition GHSym.cxx:929
Double_t fTsumwy
Total Sum of weight*Y.
Definition GHSym.h:117
void Copy(TObject &obj) const override
Definition GHSym.cxx:210
void FillRandom(const char *fname, Int_t ntimes=5000, TRandom *rng=nullptr)
Definition GHSym.cxx:466
virtual Double_t GetBinWithContent2(Double_t c, Int_t &binx, Int_t &biny, Int_t firstxbin=1, Int_t lastxbin=-1, Int_t firstybin=1, Int_t lastybin=-1, Double_t maxdiff=0) const
Definition GHSym.cxx:841
virtual TProfile * Profile(const char *name="_pf", Int_t firstbin=1, Int_t lastbin=-1, Option_t *option="") const
Definition GHSym.cxx:1727
Double_t KolmogorovTest(const TH1 *h2, Option_t *option="") const override
Definition GHSym.cxx:1239
void SetCellError(Int_t binx, Int_t biny, Double_t content) override
Definition GHSym.cxx:2553
void GetStats(Double_t *stats) const override
Definition GHSym.cxx:1003
void PutStats(Double_t *stats) override
Definition GHSym.cxx:2205
TH2 * fMatrix
! Transient pointer to the 2D-Matrix used in Draw() or GetMatrix()
Definition GHSym.h:120
Int_t BufferFill(Double_t, Double_t) override
Definition GHSym.h:26
void Reset(Option_t *option="") override
Definition GHSym.cxx:2523
void Smooth(Int_t ntimes=1, Option_t *option="") override
Definition GHSym.cxx:2609
TH1 * ShowBackground(Int_t niter=20, Option_t *option="same") override
Definition GHSym.cxx:2586
virtual Double_t IntegralAndError(Int_t firstxbin, Int_t lastxbin, Int_t firstybin, Int_t lastybin, Double_t &error, Option_t *option="") const
Definition GHSym.cxx:1107
TH2 * Matrix()
Definition GHSym.h:113
virtual Double_t GetCorrelationFactor(Int_t axis1=1, Int_t axis2=2) const
Definition GHSym.cxx:907
virtual void SetShowProjectionY(Int_t nbins=1)
Definition GHSym.cxx:2572
Double_t fTsumwy2
Total Sum of weight*Y*Y.
Definition GHSym.h:118
Int_t Fill(Double_t) override
Definition GHSym.cxx:288
Int_t BufferEmpty(Int_t action=0) override
Definition GHSym.cxx:77
Double_t Interpolate(Double_t) const override
Definition GHSym.cxx:1123
virtual TH1D * Projection(const char *name="_pr", Int_t firstBin=0, Int_t lastBin=-1, Option_t *option="") const
Definition GHSym.cxx:1964
Int_t ShowPeaks(Double_t sigma=2, Option_t *option="", Double_t threshold=0.05) override
Definition GHSym.cxx:2596
void FillN(Int_t, const Double_t *, const Double_t *, Int_t) override
Definition GHSym.h:34
Double_t GetCellError(Int_t binx, Int_t biny) const override
Definition GHSym.cxx:902
Int_t GetBin(Int_t binx, Int_t biny=0, Int_t binz=0) const override
Definition GHSym.cxx:820
virtual GHSym * Rebin2D(Int_t ngroup=2, const char *newname="")
Definition GHSym.cxx:2214
virtual void FitSlices(TF1 *f1=nullptr, Int_t firstbin=0, Int_t lastbin=-1, Int_t cut=0, Option_t *option="QNR", TObjArray *arr=nullptr)
Definition GHSym.cxx:647
Int_t FindFirstBinAbove(Double_t threshold=0, Int_t axis=1, Int_t firstBin=1, Int_t lastBin=-1) const override
Definition GHSym.cxx:581
Int_t FindLastBinAbove(Double_t threshold=0, Int_t axis=1, Int_t firstBin=1, Int_t lastBin=-1) const override
Definition GHSym.cxx:614
void SetCellContent(Int_t binx, Int_t biny, Double_t content) override
Definition GHSym.cxx:2548
Double_t GetCellContent(Int_t binx, Int_t biny) const override
Definition GHSym.cxx:897
virtual void GetRandom2(Double_t &x, Double_t &y)
Definition GHSym.cxx:960
virtual void SetShowProjectionX(Int_t nbins=1)
Definition GHSym.cxx:2558
GHSym()
Definition GHSym.cxx:30
Double_t fTsumwxy
Total Sum of weight*X*Y.
Definition GHSym.h:119