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