Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RHistImpl.hxx
Go to the documentation of this file.
1/// \file ROOT/RHistImpl.hxx
2/// \ingroup HistV7
3/// \author Axel Naumann <axel@cern.ch>
4/// \date 2015-03-23
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2015, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
16#ifndef ROOT7_RHistImpl
17#define ROOT7_RHistImpl
18
19#include <cassert>
20#include <cctype>
21#include <functional>
22#include "ROOT/RSpan.hxx"
23#include "ROOT/RTupleApply.hxx"
24
25#include "ROOT/RAxis.hxx"
26#include "ROOT/RHistBinIter.hxx"
27#include "ROOT/RHistUtils.hxx"
28#include "ROOT/RLogger.hxx"
29
30class TRootIOCtor;
31
32namespace ROOT {
33namespace Experimental {
34
35template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
36class RHist;
37
38namespace Hist {
39/// Iterator over n dimensional axes - an array of n axis iterators.
40template <int NDIMS>
41using AxisIter_t = std::array<RAxisBase::const_iterator, NDIMS>;
42/// Range over n dimensional axes - a pair of arrays of n axis iterators.
43template <int NDIMS>
44using AxisIterRange_t = std::array<AxisIter_t<NDIMS>, 2>;
45
46/// Kinds of under- and overflow handling.
47enum class EOverflow {
48 kNoOverflow = 0x0, ///< Exclude under- and overflows
49 kUnderflow = 0x1, ///< Include underflows
50 kOverflow = 0x2, ///< Include overflows
51 kUnderOver = 0x3, ///< Include both under- and overflows
52};
53
55{
56 return static_cast<int>(a) & static_cast<int>(b);
57}
58} // namespace Hist
59
60namespace Detail {
61
62/**
63 \class RHistImplPrecisionAgnosticBase
64 Base class for `RHistImplBase` that abstracts out the histogram's `PRECISION`.
65
66 For operations such as painting a histogram, the `PRECISION` (type of the bin
67 content) is not relevant; painting will cast the underlying bin type to double.
68 To facilitate this, `RHistImplBase` itself inherits from the
69 `RHistImplPrecisionAgnosticBase` interface.
70 */
71template <int DIMENSIONS>
73public:
74 /// Type of the coordinates.
76 /// Type of the local per-axis bin indices.
77 using BinArray_t = std::array<int, DIMENSIONS>;
78 /// Range type.
80
84 RHistImplPrecisionAgnosticBase(std::string_view title): fTitle(title) {}
86
87 /// Number of dimensions of the coordinates.
88 static constexpr int GetNDim() { return DIMENSIONS; }
89 /// Number of bins of this histogram, including all overflow and underflow
90 /// bins. Simply the product of all axes' total number of bins.
91 virtual int GetNBins() const noexcept = 0;
92 /// Number of bins of this histogram, excluding all overflow and underflow
93 /// bins. Simply the product of all axes' number of regular bins.
94 virtual int GetNBinsNoOver() const noexcept = 0;
95 /// Number of under- and overflow bins of this histogram, excluding all
96 /// regular bins.
97 virtual int GetNOverflowBins() const noexcept = 0;
98
99 /// Get the histogram title.
100 const std::string &GetTitle() const { return fTitle; }
101
102 /// Given the coordinate `x`, determine the index of the bin.
103 virtual int GetBinIndex(const CoordArray_t &x) const = 0;
104 /// Given the coordinate `x`, determine the index of the bin, possibly
105 /// growing axes for which `x` is out of range.
106 virtual int GetBinIndexAndGrow(const CoordArray_t &x) const = 0;
107
108 /// Given the local per-axis bins `x`, determine the index of the bin.
109 virtual int GetBinIndexFromLocalBins(const BinArray_t &x) const = 0;
110 /// Given the index of the bin, determine the local per-axis bins `x`.
111 virtual BinArray_t GetLocalBins(int binidx) const = 0;
112
113 /// Get the center in all dimensions of the bin with index `binidx`.
114 virtual CoordArray_t GetBinCenter(int binidx) const = 0;
115 /// Get the lower edge in all dimensions of the bin with index `binidx`.
116 virtual CoordArray_t GetBinFrom(int binidx) const = 0;
117 /// Get the upper edge in all dimensions of the bin with index `binidx`.
118 virtual CoordArray_t GetBinTo(int binidx) const = 0;
119
120 /// Get the uncertainty of the bin with index `binidx`.
121 virtual double GetBinUncertainty(int binidx) const = 0;
122
123 /// Whether this histogram's statistics provide storage for uncertainties, or
124 /// whether uncertainties are determined as poisson uncertainty of the content.
125 virtual bool HasBinUncertainty() const = 0;
126
127 /// The bin content, cast to double.
128 virtual double GetBinContentAsDouble(int binidx) const = 0;
129
130 /// Get a base-class view on axis with index `iAxis`.
131 ///
132 /// \param iAxis - index of the axis, must be `0 <= iAxis < DIMENSION`.
133 virtual const RAxisBase &GetAxis(int iAxis) const = 0;
134
135 /// Get an `AxisIterRange_t` for the whole histogram,
136 /// excluding under- and overflow.
137 virtual AxisIterRange_t GetRange() const = 0;
138
139private:
140 std::string fTitle; ///< The histogram's title.
141};
142
143/**
144 \class RHistImplBase
145 Interface class for `RHistImpl`.
146
147 `RHistImpl` is templated for a specific configuration of axes. To enable access
148 through `RHist`, `RHistImpl` inherits from `RHistImplBase`, exposing only dimension
149 (`DIMENSION`) and bin type (`PRECISION`).
150 */
151template <class DATA>
152class RHistImplBase: public RHistImplPrecisionAgnosticBase<DATA::GetNDim()> {
153public:
154 /// Type of the statistics (bin content, uncertainties etc).
155 using Stat_t = DATA;
156 /// Type of the coordinates.
157 using CoordArray_t = Hist::CoordArray_t<DATA::GetNDim()>;
158 /// Type of the local per-axis bin indices.
159 using BinArray_t = std::array<int, DATA::GetNDim()>;
160 /// Type of the bin content (and thus weights).
161 using Weight_t = typename DATA::Weight_t;
162
163 /// Type of the `Fill(x, w)` function
164 using FillFunc_t = void (RHistImplBase::*)(const CoordArray_t &x, Weight_t w);
165
166private:
167 /// The histogram's bin content, uncertainties etc.
169
170public:
171 RHistImplBase() = default;
172 RHistImplBase(size_t numBins, size_t numOverflowBins): fStatistics(numBins, numOverflowBins) {}
173 RHistImplBase(std::string_view title, size_t numBins, size_t numOverflowBins)
174 : RHistImplPrecisionAgnosticBase<DATA::GetNDim()>(title), fStatistics(numBins, numOverflowBins)
175 {}
176 RHistImplBase(const RHistImplBase &) = default;
178
179 virtual std::unique_ptr<RHistImplBase> Clone() const = 0;
180
181 /// Interface function to fill a vector or array of coordinates with
182 /// corresponding weights.
183 /// \note the size of `xN` and `weightN` must be the same!
184 virtual void FillN(const std::span<const CoordArray_t> xN, const std::span<const Weight_t> weightN) = 0;
185
186 /// Interface function to fill a vector or array of coordinates.
187 virtual void FillN(const std::span<const CoordArray_t> xN) = 0;
188
189 /// Retrieve the pointer to the overridden `Fill(x, w)` function.
190 virtual FillFunc_t GetFillFunc() const = 0;
191
192 /// Get the bin content (sum of weights) for the bin at coordinate `x`.
193 virtual Weight_t GetBinContent(const CoordArray_t &x) const = 0;
194
196
197 /// Get the bin uncertainty for the bin at coordinate x.
198 virtual double GetBinUncertainty(const CoordArray_t &x) const = 0;
199
200 /// Get the number of bins in this histogram, including possible under- and
201 /// overflow bins.
202 int GetNBins() const noexcept final { return fStatistics.size(); }
203
204 /// Get the number of bins in this histogram, excluding possible under- and
205 /// overflow bins.
206 int GetNBinsNoOver() const noexcept final { return fStatistics.sizeNoOver(); }
207
208 /// Get the number of under- and overflow bins of this histogram, excluding all
209 /// regular bins.
210 int GetNOverflowBins() const noexcept final { return fStatistics.sizeUnderOver(); }
211
212 /// Get the bin content (sum of weights) for bin index `binidx`.
213 Weight_t GetBinContent(int binidx) const
214 {
215 assert(binidx != 0);
216 return fStatistics[binidx];
217 }
218
219 /// Get the bin content (sum of weights) for bin index `binidx` (non-const).
221 {
222 assert(binidx != 0);
223 return fStatistics[binidx];
224 }
225
226 /// Const access to statistics.
227 const Stat_t &GetStat() const noexcept { return fStatistics; }
228
229 /// Non-const access to statistics.
230 Stat_t &GetStat() noexcept { return fStatistics; }
231
232 /// Get the bin content (sum of weights) for bin index `binidx`, cast to
233 /// `double`.
234 double GetBinContentAsDouble(int binidx) const final { return (double)GetBinContent(binidx); }
235
236 /// Add `w` to the bin at index `bin`.
237 void AddBinContent(int binidx, Weight_t w)
238 {
239 assert(binidx != 0);
240 fStatistics[binidx] += w;
241 }
242};
243} // namespace Detail
244
245namespace Internal {
246/** \name Histogram traits
247 Helper traits for histogram operations.
248 */
249///\{
250
251/// Specifies if the wanted result is the bin's lower edge, center or higher edge.
252enum class EBinCoord {
253 kBinFrom, ///< Get the lower bin edge
254 kBinCenter, ///< Get the bin center
255 kBinTo ///< Get the bin high edge
256};
257
258/// Status of FindBin(x) and FindAdjustedBin(x)
259enum class EFindStatus {
260 kCanGrow, ///< The coordinate could fit after growing the axis
261 kValid ///< The returned bin index is valid
262};
263
264/// \name Axis tuple operations
265/// Template operations on axis tuple.
266///@{
267
268/// Recursively gets the total number of bins in whole hist, excluding under- and overflow.
269/// Each call gets the current axis' number of bins (excluding under- and overflow) multiplied
270/// by that of the next axis.
271template <int IDX, class AXISTUPLE>
272struct RGetNBinsNoOverCount;
273
274template <class AXES>
275struct RGetNBinsNoOverCount<0, AXES> {
276 int operator()(const AXES &axes) const { return std::get<0>(axes).GetNBinsNoOver(); }
277};
278
279template <int I, class AXES>
281 int operator()(const AXES &axes) const { return std::get<I>(axes).GetNBinsNoOver() * RGetNBinsNoOverCount<I - 1, AXES>()(axes); }
282};
283
284/// Get the number of bins in whole hist, excluding under- and overflow.
285template <class... AXISCONFIG>
286int GetNBinsNoOverFromAxes(AXISCONFIG... axisArgs)
287{
288 using axesTuple = std::tuple<AXISCONFIG...>;
289 return RGetNBinsNoOverCount<sizeof...(AXISCONFIG) - 1, axesTuple>()(axesTuple{axisArgs...});
290}
291
292/// Recursively gets the total number of bins in whole hist, including under- and overflow.
293/// Each call gets the current axis' number of bins (including under- and overflow) multiplied
294/// by that of the next axis.
295template <int IDX, class AXISTUPLE>
296struct RGetNBinsCount;
297
298template <class AXES>
299struct RGetNBinsCount<0, AXES> {
300 int operator()(const AXES &axes) const { return std::get<0>(axes).GetNBins(); }
301};
302
303template <int I, class AXES>
305 int operator()(const AXES &axes) const { return std::get<I>(axes).GetNBins() * RGetNBinsCount<I - 1, AXES>()(axes); }
306};
307
308/// Get the number of bins in whole hist, including under- and overflow.
309template <class... AXISCONFIG>
310int GetNBinsFromAxes(AXISCONFIG... axisArgs)
311{
312 using axesTuple = std::tuple<AXISCONFIG...>;
313 return RGetNBinsCount<sizeof...(AXISCONFIG) - 1, axesTuple>()(axesTuple{axisArgs...});
314}
315
316/// Get the number of under- and overflow bins in whole hist, excluding regular bins.
317template <class... AXISCONFIG>
318int GetNOverflowBinsFromAxes(AXISCONFIG... axisArgs)
319{
320 using axesTuple = std::tuple<AXISCONFIG...>;
321 return RGetNBinsCount<sizeof...(AXISCONFIG) - 1, axesTuple>()(axesTuple{axisArgs...}) - RGetNBinsNoOverCount<sizeof...(AXISCONFIG) - 1, axesTuple>()(axesTuple{axisArgs...});
322}
323
324/// Recursively fills the ranges of all axes, excluding under- and overflow.
325/// Each call fills `range` with `begin()` and `end()` of the current axis, excluding
326/// under- and overflow.
327template <int I, class AXES>
328struct RFillIterRange;
329
330template <class AXES>
331struct RFillIterRange<-1, AXES> {
332 void operator()(Hist::AxisIterRange_t<std::tuple_size<AXES>::value> & /*range*/, const AXES & /*axes*/) const
333 {}
334};
335
336template <int I, class AXES>
338 void operator()(Hist::AxisIterRange_t<std::tuple_size<AXES>::value> &range, const AXES &axes) const
339 {
340 range[0][I] = std::get<I>(axes).begin();
341 range[1][I] = std::get<I>(axes).end();
342 RFillIterRange<I - 1, AXES>()(range, axes);
343 }
344};
345
346/// Recursively gets the number of regular bins just before the current dimension.
347/// Each call gets the previous axis' number of regular bins multiplied
348/// by the number of regular bins before the previous axis.
349template <int I, int NDIMS, typename BINS, class AXES>
350struct RGetNRegularBinsBefore;
351
352template <int NDIMS, typename BINS, class AXES>
353struct RGetNRegularBinsBefore<-1, NDIMS, BINS, AXES> {
354 void operator()(BINS &/*binSizes*/, const AXES &/*axes*/) const
355 {}
356};
357
358template <int I, int NDIMS, typename BINS, class AXES>
360 void operator()(BINS &binSizes, const AXES &axes) const
361 {
362 constexpr const int thisAxis = NDIMS - I - 1;
363 binSizes[thisAxis] = binSizes[thisAxis-1] * std::get<thisAxis-1>(axes).GetNBinsNoOver();
364 RGetNRegularBinsBefore<I - 1, NDIMS, BINS, AXES>()(binSizes, axes);
365 }
366};
367
368/// Recursively gets the total number of regular bins before the current dimension,
369/// when computing a global bin that is in under- or overflow in at least one
370/// dimension. That global bin's local per-axis bin indices are passed through
371/// the `localBins` parameter. These `localBins` were translated to 0-based bins,
372/// which is more convenient for some operations and which are the `virtualBins`
373/// parameter.
374/// Each call gets the current axis' number of regular bins before the global_bin
375/// in the current dimension multiplied by the number of regular bins before the
376/// current axis.
377/// If the global_bin is in under- or overflow in the current dimension (local bin),
378/// there is no need to process further.
379
380// - We want to know how many regular bins lie before the current overflow bin in the
381// histogram's global binning order (which so far I thought was row-major, but now I'm
382// not sure, maybe it's actually column-major... it doesn't matter, we don't need to spell out what is the global binning order anyway).
383
384template <int I, int NDIMS, typename BINS, class AXES>
385struct RComputeGlobalBin;
386
387template <int NDIMS, typename BINS, class AXES>
388struct RComputeGlobalBin<-1, NDIMS, BINS, AXES> {
389 int operator()(int total_regular_bins_before, const AXES &/*axes*/, const BINS &/*virtualBins*/, const BINS &/*binSizes*/, const BINS &/*localBins*/) const
390 {
391 return total_regular_bins_before;
392 }
393};
394
395template <int I, int NDIMS, typename BINS, class AXES>
397 int operator()(int total_regular_bins_before, const AXES &axes, const BINS &virtualBins, const BINS &binSizes, const BINS &localBins) const
398 {
399 // We can tell how many regular bins lie before us on this axis,
400 // accounting for the underflow bin of this axis if it has one.
401 const int num_underflow_bins = static_cast<int>(!std::get<I>(axes).CanGrow());
402 const int num_regular_bins_before =
403 std::max(virtualBins[I] - num_underflow_bins, 0);
404 total_regular_bins_before += num_regular_bins_before * binSizes[I];
405
406 // If we are on an overflow or underflow bin on this axis, we know that
407 // we don't need to look at the remaining axes. Projecting on those
408 // dimensions would only take us into an hyperplane of over/underflow
409 // bins for the current axis, and we know that by construction there
410 // will be no regular bins in there.
411 if (localBins[I] < 1)
412 return total_regular_bins_before;
413
414 return RComputeGlobalBin<I - 1, NDIMS, BINS, AXES>()(total_regular_bins_before, axes, virtualBins, binSizes, localBins);
415 }
416};
417
418/// Recursively compute some quantities needed for `ComputeLocalBins`, namely
419/// the total number of bins per hyperplane (overflow and regular) and the
420/// number of regular bins per hyperplane on the hyperplanes that have them.
421template <int I, int NDIMS, class AXES>
422struct RComputeLocalBinsInitialisation;
423
424template <int NDIMS, class AXES>
425struct RComputeLocalBinsInitialisation<0, NDIMS, AXES> {
426 void operator()(std::array<int, NDIMS-1> /* bins_per_hyperplane */, std::array<int, NDIMS-1> /* regular_bins_per_hyperplane */, const AXES & /*axes*/) const
427 {}
428};
429
430template <int I, int NDIMS, class AXES>
432 void operator()(std::array<int, NDIMS-1>& bins_per_hyperplane, std::array<int, NDIMS-1>& regular_bins_per_hyperplane, const AXES &axes) const
433 {
434 constexpr const int thisAxis = NDIMS - I - 1;
435 bins_per_hyperplane[thisAxis] = Internal::RGetNBinsCount<thisAxis, AXES>()(axes);
436 regular_bins_per_hyperplane[thisAxis] = Internal::RGetNBinsNoOverCount<thisAxis, AXES>()(axes);
437 RComputeLocalBinsInitialisation<I - 1, NDIMS, AXES>()(bins_per_hyperplane, regular_bins_per_hyperplane, axes);
438 }
439};
440
441/// Recursively computes the number of regular bins before the current dimension,
442/// as well as the number of under- and overflow bins left to account for, after
443/// the current dimension. If the latter is equal to 0, there is no need to process
444/// further.
445/// It is computing local bins that are in under- or overflow in at least one
446/// dimension.
447/// Starting at the highest dimension, it examines how many full hyperplanes of
448/// regular bins lie before, then projects on the remaining dimensions.
449
450template <int I, int NDIMS, class AXES>
451struct RComputeLocalBins;
452
453template <int NDIMS, class AXES>
454struct RComputeLocalBins<0, NDIMS, AXES> {
455 void operator()(const AXES &/*axes*/, int &/*unprocessed_previous_overflow_bin*/,
456 int &/*num_regular_bins_before*/, std::array<int, NDIMS-1> /* bins_per_hyperplane */,
457 std::array<int, NDIMS-1> /* regular_bins_per_hyperplane */, int /* curr_bins_per_hyperplane */,
458 int /* curr_regular_bins_per_hyperplane */) const
459 {}
460};
461
462template <int I, int NDIMS, class AXES>
464 void operator()(const AXES &axes, int &unprocessed_previous_overflow_bin,
465 int &num_regular_bins_before, std::array<int, NDIMS-1> bins_per_hyperplane,
466 std::array<int, NDIMS-1> regular_bins_per_hyperplane, int curr_bins_per_hyperplane,
467 int curr_regular_bins_per_hyperplane) const
468 {
469 // Let's start by computing the contribution of the underflow
470 // hyperplane (if any), in which we know there will be no regular bins
471 const int num_underflow_hyperplanes =
472 static_cast<int>(!std::get<I>(axes).CanGrow());
473 const int bins_in_underflow_hyperplane =
474 num_underflow_hyperplanes * bins_per_hyperplane[I-1];
475
476 // Next, from the total number of bins per hyperplane and the number of
477 // regular bins per hyperplane that has them, we deduce the number of
478 // overflow bins per hyperplane that has regular bins.
479 const int overflow_bins_per_regular_hyperplane =
480 bins_per_hyperplane[I-1] - regular_bins_per_hyperplane[I-1];
481
482 // This allows us to answer a key question: are there any under/overflow
483 // bins on the hyperplanes that have regular bins? It may not be the
484 // case if all of their axes are growable, and thus don't have overflow bins.
485 if (overflow_bins_per_regular_hyperplane != 0) {
486 // If so, we start by cutting off the contribution of the underflow
487 // and overflow hyperplanes, to focus specifically on regular bins.
488 const int overflow_bins_in_regular_hyperplanes =
489 std::min(
490 std::max(
491 unprocessed_previous_overflow_bin
492 - bins_in_underflow_hyperplane,
493 0
494 ),
495 overflow_bins_per_regular_hyperplane
496 * std::get<I>(axes).GetNBinsNoOver()
497 );
498
499 // We count how many _complete_ "regular" hyperplanes that leaves
500 // before us, and account for those in our regular bin count.
501 const int num_regular_hyperplanes_before =
502 overflow_bins_in_regular_hyperplanes
503 / overflow_bins_per_regular_hyperplane;
504 num_regular_bins_before +=
505 num_regular_hyperplanes_before
506 * regular_bins_per_hyperplane[I-1];
507
508 // This only leaves the _current_ hyperplane as a possible source of
509 // more regular bins that we haven't accounted for yet. We'll take
510 // those into account while processing previous dimensions.
511 unprocessed_previous_overflow_bin =
512 overflow_bins_in_regular_hyperplanes
513 % overflow_bins_per_regular_hyperplane;
514 } else {
515 // If there are no overflow bins in regular hyperplane, then the
516 // rule changes: observing _one_ overflow bin after the underflow
517 // hyperplane means that _all_ regular hyperplanes on this axis are
518 // already before us.
519 if (unprocessed_previous_overflow_bin >= bins_in_underflow_hyperplane) {
520 num_regular_bins_before +=
521 std::get<I>(axes).GetNBinsNoOver()
522 * regular_bins_per_hyperplane[I-1];
523 }
524
525 // In this case, we're done, because the current bin may only lie
526 // in the underflow or underflow hyperplane of this axis. Which
527 // means that there are no further regular bins to be accounted for
528 // in the current hyperplane.
529 unprocessed_previous_overflow_bin = 0;
530 }
531
532 // No need to continue this loop if we've taken into account all
533 // overflow bins that were associated with regular bins.
534 if (unprocessed_previous_overflow_bin == 0)
535 return;
536
537 return Internal::RComputeLocalBins<I - 1, NDIMS, AXES>()
538 (axes, unprocessed_previous_overflow_bin, num_regular_bins_before, bins_per_hyperplane,
539 regular_bins_per_hyperplane, curr_bins_per_hyperplane, curr_regular_bins_per_hyperplane);
540 }
541};
542
543/// Recursively computes zero-based local bin indices, given...
544///
545/// - A zero-based global bin index
546/// - The number of considered bins on each axis (can be either `GetNBinsNoOver`
547/// or `GetNBins` depending on what you are trying to do)
548/// - A policy of treating all bins as regular (i.e. no negative indices)
549template <int I, int NDIMS, typename BINS, class AXES, class BINTYPE>
550struct RComputeLocalBinsRaw;
551
552template <int NDIMS, typename BINS, class AXES, class BINTYPE>
553struct RComputeLocalBinsRaw<-1, NDIMS, BINS, AXES, BINTYPE> {
554 void operator()(BINS & /*virtualBins*/, const AXES & /*axes*/, int /*zeroBasedGlobalBin*/, BINTYPE /*GetNBinType*/) const
555 {}
556};
557
558template <int I, int NDIMS, typename BINS, class AXES, class BINTYPE>
560 void operator()(BINS &virtualBins, const AXES &axes, int zeroBasedGlobalBin, BINTYPE GetNBinType) const
561 {
562 constexpr const int thisAxis = NDIMS - I - 1;
563 virtualBins[thisAxis] = zeroBasedGlobalBin % (std::get<thisAxis>(axes).*GetNBinType)();
564 RComputeLocalBinsRaw<I - 1, NDIMS, BINS, AXES, BINTYPE>()(virtualBins, axes, zeroBasedGlobalBin / (std::get<thisAxis>(axes).*GetNBinType)(), GetNBinType);
565 }
566};
567
568/// Recursively computes a zero-based global bin index, given...
569///
570/// - A set of zero-based per-axis bin indices
571/// - The number of considered bins on each axis (can be either `GetNBinsNoOver`
572/// or `GetNBins` depending on what you are trying to do)
573/// - A policy of treating all bins qs regular (i.e. no negative indices)
574template <int I, int NDIMS, typename BINS, class AXES, class BINTYPE>
575struct RComputeGlobalBinRaw;
576
577template <int NDIMS, typename BINS, class AXES, class BINTYPE>
578struct RComputeGlobalBinRaw<-1, NDIMS, BINS, AXES, BINTYPE> {
579 int operator()(int globalVirtualBin, const AXES & /*axes*/, const BINS & /*zeroBasedLocalBins*/, int /*binSize*/, BINTYPE /*GetNBinType*/) const
580 {
581 return globalVirtualBin;
582 }
583};
584
585template <int I, int NDIMS, typename BINS, class AXES, class BINTYPE>
587 int operator()(int globalVirtualBin, const AXES &axes, const BINS &zeroBasedLocalBins, int binSize, BINTYPE GetNBinType) const
588 {
589 constexpr const int thisAxis = NDIMS - I - 1;
590 globalVirtualBin += zeroBasedLocalBins[thisAxis] * binSize;
591 binSize *= (std::get<thisAxis>(axes).*GetNBinType)();
592 return Internal::RComputeGlobalBinRaw<I - 1, NDIMS, BINS, AXES, BINTYPE>()(globalVirtualBin, axes, zeroBasedLocalBins, binSize, GetNBinType);
593 }
594};
595
596/// Recursively converts zero-based virtual bins where the underflow bin
597/// has index `0` and the overflow bin has index `N+1` where `N` is the axis'
598/// number of regular bins, to the standard `kUnderflowBin`/`kOverflowBin` for under/overflow
599/// bin indexing convention.
600///
601/// For growable axes, must add 1 to go back to standard indices as their virtual
602/// indexing convention is also 0-based, with zero designating the first regular bin.
603template <int I, int NDIMS, typename BINS, class AXES>
604struct RVirtualBinsToLocalBins;
605
606template <int NDIMS, typename BINS, class AXES>
607struct RVirtualBinsToLocalBins<-1, NDIMS, BINS, AXES> {
608 void operator()(BINS & /*localBins*/, const AXES & /*axes*/, const BINS & /*virtualBins*/) const
609 {}
610};
611
612template <int I, int NDIMS, typename BINS, class AXES>
614 void operator()(BINS &localBins, const AXES &axes, const BINS &virtualBins) const
615 {
616 constexpr const int thisAxis = NDIMS - I - 1;
617 if ((!std::get<thisAxis>(axes).CanGrow()) && (virtualBins[thisAxis] == 0)) {
618 localBins[thisAxis] = RAxisBase::kUnderflowBin;
619 } else if ((!std::get<thisAxis>(axes).CanGrow()) && (virtualBins[thisAxis] == (std::get<thisAxis>(axes).GetNBins() - 1))) {
620 localBins[thisAxis] = RAxisBase::kOverflowBin;
621 } else {
622 const int regular_bin_offset = -static_cast<int>(std::get<thisAxis>(axes).CanGrow());
623 localBins[thisAxis] = virtualBins[thisAxis] - regular_bin_offset;
624 }
625 RVirtualBinsToLocalBins<I - 1, NDIMS, BINS, AXES>()(localBins, axes, virtualBins);
626 }
627};
628
629/// Recursively converts local axis bins from the standard `kUnderflowBin`/`kOverflowBin` for under/overflow
630/// bin indexing convention, to a "virtual bin" convention where the underflow bin
631/// has index `0` and the overflow bin has index `N+1` where `N` is the axis'
632/// number of regular bins.
633///
634/// For growable axes, subtract 1 from regular indices so that the indexing
635/// convention remains zero-based (this means that there will be no "holes" in
636/// global binning, which matters more than the choice of regular index base)
637template <int I, int NDIMS, typename BINS, class AXES>
638struct RLocalBinsToVirtualBins;
639
640template <int NDIMS, typename BINS, class AXES>
641struct RLocalBinsToVirtualBins<-1, NDIMS, BINS, AXES> {
642 void operator()(BINS & /*virtualBins*/, const AXES & /*axes*/, const BINS & /*localBins*/) const
643 {}
644};
645
646template <int I, int NDIMS, typename BINS, class AXES>
648 void operator()(BINS &virtualBins, const AXES &axes, const BINS &localBins) const
649 {
650 constexpr const int thisAxis = NDIMS - I - 1;
651 switch (localBins[thisAxis]) {
653 virtualBins[thisAxis] = 0; break;
655 virtualBins[thisAxis] = std::get<thisAxis>(axes).GetNBins() - 1; break;
656 default:
657 virtualBins[thisAxis] = localBins[thisAxis] - static_cast<int>(std::get<thisAxis>(axes).CanGrow());
658 }
659 RLocalBinsToVirtualBins<I - 1, NDIMS, BINS, AXES>()(virtualBins, axes, localBins);
660 }
661};
662
663/// Find the per-axis local bin indices associated with a certain set of coordinates.
664template <int I, int NDIMS, typename BINS, typename COORD, class AXES>
665struct RFindLocalBins;
666
667template <int NDIMS, typename BINS, typename COORD, class AXES>
668struct RFindLocalBins<-1, NDIMS, BINS, COORD, AXES> {
669 void operator()(BINS & /*localBins*/, const AXES & /*axes*/, const COORD & /*coords*/) const
670 {}
671};
672
673template <int I, int NDIMS, typename BINS, typename COORD, class AXES>
675 void operator()(BINS &localBins, const AXES &axes, const COORD &coords) const
676 {
677 constexpr const int thisAxis = NDIMS - I - 1;
678 localBins[thisAxis] = std::get<thisAxis>(axes).FindBin(coords[thisAxis]);
679 RFindLocalBins<I - 1, NDIMS, BINS, COORD, AXES>()(localBins, axes, coords);
680 }
681};
682
683/// Recursively converts local axis bins from the standard `kUnderflowBin`/`kOverflowBin` for
684/// under/overflow bin indexing convention, to the corresponding bin coordinates.
685template <int I, int NDIMS, typename BINS, typename COORD, class AXES>
686struct RLocalBinsToCoords;
687
688template <int NDIMS, typename BINS, typename COORD, class AXES>
689struct RLocalBinsToCoords<-1, NDIMS, BINS, COORD, AXES> {
690 void operator()(COORD & /*coords*/, const AXES & /*axes*/, const BINS & /*localBins*/, EBinCoord /*kind*/) const
691 {}
692};
693
694template <int I, int NDIMS, typename BINS, typename COORD, class AXES>
696 void operator()(COORD &coords, const AXES &axes, const BINS &localBins, EBinCoord kind) const
697 {
698 constexpr const int thisAxis = NDIMS - I - 1;
699 int axisbin = localBins[thisAxis];
700 switch (kind) {
701 case EBinCoord::kBinFrom: coords[thisAxis] = std::get<thisAxis>(axes).GetBinFrom(axisbin); break;
702 case EBinCoord::kBinCenter: coords[thisAxis] = std::get<thisAxis>(axes).GetBinCenter(axisbin); break;
703 case EBinCoord::kBinTo: coords[thisAxis] = std::get<thisAxis>(axes).GetBinTo(axisbin); break;
704 }
705 RLocalBinsToCoords<I - 1, NDIMS, BINS, COORD, AXES>()(coords, axes, localBins, kind);
706 }
707};
708
709template <class... AXISCONFIG>
710static std::array<const RAxisBase *, sizeof...(AXISCONFIG)> GetAxisView(const AXISCONFIG &... axes) noexcept
711{
712 std::array<const RAxisBase *, sizeof...(AXISCONFIG)> axisViews{{&axes...}};
713 return axisViews;
714}
715
716///\}
717} // namespace Internal
718
719namespace Detail {
720
721template <class DATA, class... AXISCONFIG>
722class RHistImpl final: public RHistImplBase<DATA> {
723 static_assert(sizeof...(AXISCONFIG) == DATA::GetNDim(), "Number of axes must equal histogram dimension");
724
725 friend typename DATA::Hist_t;
726
727public:
732 using typename ImplBase_t::FillFunc_t;
733 template <int NDIMS = DATA::GetNDim()>
735
736private:
737 std::tuple<AXISCONFIG...> fAxes; ///< The histogram's axes
738
739public:
741 RHistImpl(AXISCONFIG... axisArgs);
742 RHistImpl(std::string_view title, AXISCONFIG... axisArgs);
743
744 std::unique_ptr<ImplBase_t> Clone() const override {
745 return std::unique_ptr<ImplBase_t>(new RHistImpl(*this));
746 }
747
748 /// Retrieve the fill function for this histogram implementation, to prevent
749 /// the virtual function call for high-frequency fills.
750 FillFunc_t GetFillFunc() const final {
752 }
753
754 /// Get the axes of this histogram.
755 const std::tuple<AXISCONFIG...> &GetAxes() const { return fAxes; }
756
757 /// Normalized axes access, converting from actual axis type to base class.
758 const RAxisBase &GetAxis(int iAxis) const final { return *std::apply(Internal::GetAxisView<AXISCONFIG...>, fAxes)[iAxis]; }
759
760 /// Computes a zero-based global bin index, given...
761 ///
762 /// - A set of zero-based per-axis bin indices
763 /// - The number of considered bins on each axis (can be either `GetNBinsNoOver`
764 /// or `GetNBins` depending on what you are trying to do)
765 /// - A policy of treating all bins qs regular (i.e. no negative indices)
766 template <int NDIMS, typename BINTYPE>
767 int ComputeGlobalBinRaw(const BinArray_t& zeroBasedLocalBins, BINTYPE GetNBinType) const {
768 int result = 0;
769 int binSize = 1;
770 return Internal::RComputeGlobalBinRaw<NDIMS - 1, NDIMS, BinArray_t, decltype(fAxes), BINTYPE>()(result, fAxes, zeroBasedLocalBins, binSize, GetNBinType);
771 }
772
773 /// Computes zero-based local bin indices, given...
774 ///
775 /// - A zero-based global bin index
776 /// - The number of considered bins on each axis (can be either `GetNBinsNoOver`
777 /// or `GetNBins` depending on what you are trying to do)
778 /// - A policy of treating all bins as regular (i.e. no negative indices)
779 template <int NDIMS, typename BINTYPE>
780 BinArray_t ComputeLocalBinsRaw(int zeroBasedGlobalBin, BINTYPE GetNBinType) const {
782 Internal::RComputeLocalBinsRaw<NDIMS - 1, NDIMS, BinArray_t, decltype(fAxes), BINTYPE>()(result, fAxes, zeroBasedGlobalBin, GetNBinType);
783 return result;
784 }
785
786 /// Converts local axis bins from the standard `kUnderflowBin`/`kOverflowBin` for under/overflow
787 /// bin indexing convention, to a "virtual bin" convention where the underflow bin
788 /// has index `0` and the overflow bin has index `N+1` where `N` is the axis'
789 /// number of regular bins.
790 template <int NDIMS>
792 BinArray_t virtualBins;
793 Internal::RLocalBinsToVirtualBins<NDIMS - 1, NDIMS, BinArray_t, decltype(fAxes)>()(virtualBins, fAxes, localBins);
794 return virtualBins;
795 }
796
797 /// Converts zero-based virtual bins where the underflow bin has
798 /// index `0` and the overflow bin has index `N+1` where `N` is the axis'
799 /// number of regular bins, to the standard `kUnderflowBin`/`kOverflowBin` for under/overflow
800 /// bin indexing convention.
801 template <int NDIMS>
802 BinArray_t VirtualBinsToLocalBins(const BinArray_t& virtualBins) const {
803 BinArray_t localBins = {};
804 Internal::RVirtualBinsToLocalBins<NDIMS - 1, NDIMS, BinArray_t, decltype(fAxes)>()(localBins, fAxes, virtualBins);
805 return localBins;
806 }
807
808 /// Computes the global index of a certain bin on an `NDIMS`-dimensional histogram,
809 /// knowing the local per-axis bin indices as returned by calling `FindBin()` on each axis.
810 template <int NDIMS>
811 int ComputeGlobalBin(BinArray_t& local_bins) const {
812 // Get regular bins out of the way
813 if (std::all_of(local_bins.cbegin(), local_bins.cend(),
814 [](int bin) { return bin >= 1; })) {
815 for (int bin = 0; bin < NDIMS; bin++)
816 local_bins[bin] -= 1;
817 return ComputeGlobalBinRaw<NDIMS>(local_bins, &ROOT::Experimental::RAxisBase::GetNBinsNoOver) + 1;
818 }
819
820 // Convert bin indices to a zero-based coordinate system where the underflow
821 // bin (if any) has coordinate 0 and the overflow bin (if any) has
822 // coordinate N-1, where N is the axis' total number of bins.
823 BinArray_t virtual_bins = LocalBinsToVirtualBins<NDIMS>(local_bins);
824
825 // Deduce what the global bin index would be in this coordinate system that
826 // unifies regular and overflow bins.
827 const int global_virtual_bin = ComputeGlobalBinRaw<NDIMS>(virtual_bins, &ROOT::Experimental::RAxisBase::GetNBins);
828
829 // Move to 1-based and negative indexing
830 const int neg_1based_virtual_bin = -global_virtual_bin - 1;
831
832 // At this point, we have an index that represents a count of all bins, both
833 // regular and overflow, that are located before the current bin when
834 // enumerating histogram bins in row-major order.
835 //
836 // We will next count the number of _regular_ bins which are located before
837 // the current bin, and by removing this offset from the above index, we
838 // will get a count of overflow bins that are located before the current bin
839 // in row-major order. Which is what we want as our overflow bin index.
840 //
841 int total_regular_bins_before = 0;
842
843 // First, we need to know how many regular bins we leave behind us for each
844 // step on each axis, assuming that the bin from which we come was regular.
845 //
846 // If mathematically inclined, you can also think of this as the size of an
847 // hyperplane of regular bins when projecting on lower-numbered dimensions.
848 // See the docs of ComputeLocalBins for more on this worldview.
849 //
850 BinArray_t bin_sizes;
851 bin_sizes[0] = 1;
852 Internal::RGetNRegularBinsBefore<NDIMS - 2, NDIMS, BinArray_t, decltype(fAxes)>()(bin_sizes, fAxes);
853
854 // With that, we can deduce how many regular bins lie before us.
855 total_regular_bins_before = Internal::RComputeGlobalBin<NDIMS - 1, NDIMS, BinArray_t, decltype(fAxes)>()
856 (total_regular_bins_before, fAxes, virtual_bins, bin_sizes, local_bins);
857
858 // Now that we know how many bins lie before us, and how many of those are
859 // regular bins, we can trivially deduce how many overflow bins lie before
860 // us, and emit that as our global overflow bin index.
861 return neg_1based_virtual_bin + total_regular_bins_before;
862 }
863
864 /// Computes the local per-axis bin indices of a certain bin on an `NDIMS`-dimensional histogram,
865 /// knowing the global histogram bin index.
866 template <int NDIMS>
867 BinArray_t ComputeLocalBins(int global_bin) const {
868 // Get regular bins out of the way
869 if (global_bin >= 1) {
870 BinArray_t computed_bins = ComputeLocalBinsRaw<NDIMS>(global_bin - 1, &ROOT::Experimental::RAxisBase::GetNBinsNoOver);
871 for (int bin = 0; bin < NDIMS; ++bin)
872 computed_bins[bin] += 1;
873 return computed_bins;
874 }
875
876 // Convert our negative index to something positive and 0-based, as that is
877 // more convenient to work with. Note, however, that this is _not_
878 // equivalent to the virtual_bin that we had before, because what we have
879 // here is a count of overflow bins, not of all bins...
880 const int corrected_virtual_overflow_bin = -global_bin - 1;
881
882 // ...so we need to retrieve and bring back the regular bin count, and this
883 // is where the fun begins.
884 //
885 // The main difficulty is that the number of regular bins is not fixed as
886 // one slides along a histogram axis. Using a 2D binning case as a simple
887 // motivating example...
888 //
889 // -1 -2 -3 -4 <- No regular bins on the underflow line of axis 1
890 // -5 1 2 -6 <- Some of them on middle lines of axis 1
891 // -7 3 4 -8
892 // -9 -10 -11 -12 <- No regular bins on the overflow line of axis 1
893 //
894 // As we go to higher dimensions, the geometry becomes more complex, but
895 // if we replace "line" with "plane", we get a similar picture in 3D when we
896 // slide along axis 2:
897 //
898 // No regular bins on the Some of them on the No regular bins again
899 // UF plane of axis 2 regular planes of ax.2 on the OF plane of ax.2
900 //
901 // -1 -2 -3 -4 -17 -18 -19 -20 -29 -30 -31 -32
902 // -5 -6 -7 -8 -21 1 2 -22 -33 -34 -35 -36
903 // -9 -10 -11 -12 -23 3 4 -24 -37 -37 -39 -40
904 // -13 -14 -15 -16 -25 -26 -27 -28 -41 -42 -43 -44
905 //
906 // We can generalize this to N dimensions by saying that as we slide along
907 // the last axis of an N-d histogram, we see an hyperplane full of overflow
908 // bins, then some hyperplanes with regular bins in the "middle" surrounded
909 // by overflow bins, then a last hyperplane full of overflow bins.
910 //
911 // From this, we can devise a recursive algorithm to recover the number of
912 // regular bins before the overflow bin we're currently looking at:
913 //
914 // - Start by processing the last histogram axis.
915 // - Ignore the first and last hyperplane on this axis, which only contain
916 // underflow and overflow bins respectively.
917 // - Count how many complete hyperplanes of regular bins lie before us on
918 // this axis, which we can do indirectly in our overflow bin based
919 // reasoning by computing the perimeter of the regular region and dividing
920 // our "regular" overflow bin count by that amount.
921 // - Now we counted previous hyperplanes on this last histogram axis, but
922 // we need to process the hyperplane that our bin is located in, if any.
923 // * For this, we reduce our overflow bin count to a count of
924 // _unaccounted_ overflow bins in the current hyperplane...
925 // * ...which allows us to recursively continue the computation by
926 // processing the next (well, previous) histogram axis in the context
927 // of this hyperplane, in the same manner as above.
928 //
929 // Alright, now that the general plan is sorted out, let's compute some
930 // quantities that we are going to need, namely the total number of bins per
931 // hyperplane (overflow and regular) and the number of regular bins per
932 // hyperplane on the hyperplanes that have them.
933 //
934 std::array<int, NDIMS - 1> bins_per_hyperplane{};
935 std::array<int, NDIMS - 1> regular_bins_per_hyperplane{};
936 Internal::RComputeLocalBinsInitialisation<NDIMS - 1, NDIMS, decltype(fAxes)>()(bins_per_hyperplane, regular_bins_per_hyperplane, fAxes);
937
938 int curr_bins_per_hyperplane = Internal::RGetNBinsCount<NDIMS - 1, decltype(fAxes)>()(fAxes);
939 int curr_regular_bins_per_hyperplane = Internal::RGetNBinsNoOverCount<NDIMS - 1, decltype(fAxes)>()(fAxes);
940
941 // Given that, we examine each axis, starting from the last one.
942 int unprocessed_previous_overflow_bin = corrected_virtual_overflow_bin;
943 int num_regular_bins_before = 0;
944 Internal::RComputeLocalBins<NDIMS - 1, NDIMS, decltype(fAxes)>()
945 (fAxes, unprocessed_previous_overflow_bin, num_regular_bins_before, bins_per_hyperplane,
946 regular_bins_per_hyperplane, curr_bins_per_hyperplane, curr_regular_bins_per_hyperplane);
947
948 // By the time we reach the first axis, there should only be at most one
949 // full row of regular bins before us:
950 //
951 // -1 1 2 3 -2
952 // ^ ^
953 // | |
954 // | Option 2: one overflow bin before us
955 // |
956 // Option 1: no overflow bin before us
957 //
958 num_regular_bins_before +=
959 unprocessed_previous_overflow_bin * std::get<0>(fAxes).GetNBinsNoOver();
960
961 // Now that we know the number of regular bins before us, we can add this to
962 // to the zero-based overflow bin index that we started with to get a global
963 // zero-based bin index accounting for both under/overflow bins and regular
964 // bins, just like what we had in the ComputeGlobalBin<DATA::GetNDim()>() implementation.
965 const int global_virtual_bin =
966 corrected_virtual_overflow_bin + num_regular_bins_before;
967
968 // We can then easily go back to zero-based "virtual" bin indices...
969 const BinArray_t virtual_bins = ComputeLocalBinsRaw<NDIMS>(global_virtual_bin, &ROOT::Experimental::RAxisBase::GetNBins);
970
971 // ...and from that go back to the -1/-2 overflow bin indexing convention.
972 return VirtualBinsToLocalBins<NDIMS>(virtual_bins);
973 }
974
975 /// Get the bin index for the given coordinates `x`. The use of `RFindLocalBins`
976 /// allows to convert the coordinates to local per-axis bin indices before using
977 /// `ComputeGlobalBin()`.
978 int GetBinIndex(const CoordArray_t &x) const final
979 {
980 BinArray_t localBins = {};
981 Internal::RFindLocalBins<DATA::GetNDim() - 1, DATA::GetNDim(), BinArray_t, CoordArray_t, decltype(fAxes)>()(localBins, fAxes, x);
982 int result = ComputeGlobalBin<DATA::GetNDim()>(localBins);
983 return result;
984 }
985
986 /// Get the bin index for the given coordinates `x`, growing the axes as needed.
987 /// The use of `RFindLocalBins` allows to convert the coordinates to local
988 /// per-axis bin indices before using `ComputeGlobalBin()`.
989 ///
990 /// TODO: implement growable behavior
991 int GetBinIndexAndGrow(const CoordArray_t &x) const final
992 {
994 int ret = 0;
995 BinArray_t localBins = {};
996 while (status == Internal::EFindStatus::kCanGrow) {
997 Internal::RFindLocalBins<DATA::GetNDim() - 1, DATA::GetNDim(), BinArray_t, CoordArray_t, decltype(fAxes)>()(localBins, fAxes, x);
998 ret = ComputeGlobalBin<DATA::GetNDim()>(localBins);
1000 }
1001 return ret;
1002 }
1003
1004 /// Get the bin index for the given local per-axis bin indices `x`, using
1005 /// `ComputeGlobalBin()`.
1006 int GetBinIndexFromLocalBins(const BinArray_t &x) const final
1007 {
1008 BinArray_t localBins = x;
1009 int result = ComputeGlobalBin<DATA::GetNDim()>(localBins);
1010 return result;
1011 }
1012
1013 /// Get the local per-axis bin indices `x` for the given bin index, using
1014 /// `ComputeLocalBins()`.
1015 BinArray_t GetLocalBins(int binidx) const final
1016 {
1017 BinArray_t localBins = ComputeLocalBins<DATA::GetNDim()>(binidx);
1018 return localBins;
1019 }
1020
1021 /// Get the center coordinates of the bin with index `binidx`.
1022 CoordArray_t GetBinCenter(int binidx) const final
1023 {
1024 BinArray_t localBins = ComputeLocalBins<DATA::GetNDim()>(binidx);
1025 CoordArray_t coords;
1026 Internal::RLocalBinsToCoords<DATA::GetNDim() - 1, DATA::GetNDim(), BinArray_t, CoordArray_t, decltype(fAxes)>()(coords, fAxes, localBins, Internal::EBinCoord::kBinCenter);
1027 return coords;
1028 }
1029
1030 /// Get the coordinates of the low limit of the bin with index `binidx`.
1031 CoordArray_t GetBinFrom(int binidx) const final
1032 {
1033 BinArray_t localBins = ComputeLocalBins<DATA::GetNDim()>(binidx);
1034 CoordArray_t coords;
1035 Internal::RLocalBinsToCoords<DATA::GetNDim() - 1, DATA::GetNDim(), BinArray_t, CoordArray_t, decltype(fAxes)>()(coords, fAxes, localBins, Internal::EBinCoord::kBinFrom);
1036 return coords;
1037 }
1038
1039 /// Get the coordinates of the high limit of the bin with index `binidx`.
1040 CoordArray_t GetBinTo(int binidx) const final
1041 {
1042 BinArray_t localBins = ComputeLocalBins<DATA::GetNDim()>(binidx);
1043 CoordArray_t coords;
1044 Internal::RLocalBinsToCoords<DATA::GetNDim() - 1, DATA::GetNDim(), BinArray_t, CoordArray_t, decltype(fAxes)>()(coords, fAxes, localBins, Internal::EBinCoord::kBinTo);
1045 return coords;
1046 }
1047
1048 /// Fill an array of `weightN` to the bins specified by coordinates `xN`.
1049 /// For each element `i`, the weight `weightN[i]` will be added to the bin
1050 /// at the coordinate `xN[i]`
1051 /// \note `xN` and `weightN` must have the same size!
1052 void FillN(const std::span<const CoordArray_t> xN, const std::span<const Weight_t> weightN) final
1053 {
1054#ifndef NDEBUG
1055 if (xN.size() != weightN.size()) {
1056 R__LOG_ERROR(HistLog()) << "Not the same number of points and weights!";
1057 return;
1058 }
1059#endif
1060
1061 for (size_t i = 0; i < xN.size(); ++i) {
1062 Fill(xN[i], weightN[i]);
1063 }
1064 }
1065
1066 /// Fill an array of `weightN` to the bins specified by coordinates `xN`.
1067 /// For each element `i`, the weight `weightN[i]` will be added to the bin
1068 /// at the coordinate `xN[i]`
1069 void FillN(const std::span<const CoordArray_t> xN) final
1070 {
1071 for (auto &&x: xN) {
1072 Fill(x);
1073 }
1074 }
1075
1076 /// Add a single weight `w` to the bin at coordinate `x`.
1077 void Fill(const CoordArray_t &x, Weight_t w = 1.)
1078 {
1079 int bin = GetBinIndexAndGrow(x);
1080 this->GetStat().Fill(x, bin, w);
1081 }
1082
1083 /// Get the content of the bin at position `x`.
1085 {
1086 int bin = GetBinIndex(x);
1087 return ImplBase_t::GetBinContent(bin);
1088 }
1089
1090 /// Return the uncertainties for the given bin index.
1091 double GetBinUncertainty(int binidx) const final { return this->GetStat().GetBinUncertainty(binidx); }
1092
1093 /// Get the bin uncertainty for the bin at coordinate `x`.
1094 double GetBinUncertainty(const CoordArray_t &x) const final
1095 {
1096 const int bin = GetBinIndex(x);
1097 return this->GetBinUncertainty(bin);
1098 }
1099
1100 /// Whether this histogram's statistics provide storage for uncertainties, or
1101 /// whether uncertainties are determined as poisson uncertainty of the content.
1102 bool HasBinUncertainty() const final { return this->GetStat().HasBinUncertainty(); }
1103
1104 /// Get the begin() and end() for each axis.
1105 AxisIterRange_t<DATA::GetNDim()>
1106 GetRange() const final
1107 {
1108 std::array<std::array<RAxisBase::const_iterator, DATA::GetNDim()>, 2> ret;
1109 Internal::RFillIterRange<DATA::GetNDim() - 1, decltype(fAxes)>()(ret, fAxes);
1110 return ret;
1111 }
1112
1113 /// Grow the axis number `iAxis` to fit the coordinate `x`.
1114 ///
1115 /// The histogram (conceptually) combines pairs of bins along this axis until
1116 /// `x` is within the range of the axis.
1117 /// The axis must support growing for this to work (e.g. a `RAxisGrow`).
1118 void GrowAxis(int /*iAxis*/, double /*x*/)
1119 {
1120 // TODO: Implement GrowAxis()
1121 }
1122
1123 /// \{
1124 /// \name Iterator interface
1127 iterator begin() noexcept { return iterator(*this); }
1128 const_iterator begin() const noexcept { return const_iterator(*this); }
1129 iterator end() noexcept { return iterator(*this, this->GetNBinsNoOver()); }
1130 const_iterator end() const noexcept { return const_iterator(*this, this->GetNBinsNoOver()); }
1131 /// \}
1132};
1133
1134template <class DATA, class... AXISCONFIG>
1136{}
1137
1138template <class DATA, class... AXISCONFIG>
1140 : ImplBase_t(Internal::GetNBinsNoOverFromAxes(axisArgs...), Internal::GetNOverflowBinsFromAxes(axisArgs...)), fAxes{axisArgs...}
1141{}
1142
1143template <class DATA, class... AXISCONFIG>
1144RHistImpl<DATA, AXISCONFIG...>::RHistImpl(std::string_view title, AXISCONFIG... axisArgs)
1145 : ImplBase_t(title, Internal::GetNBinsNoOverFromAxes(axisArgs...), Internal::GetNOverflowBinsFromAxes(axisArgs...)), fAxes{axisArgs...}
1146{}
1147
1148#if 0
1149// In principle we can also have a runtime version of RHistImpl, that does not
1150// contain a tuple of concrete axis types but a vector of `RAxisConfig`.
1151template <class DATA>
1152class RHistImplRuntime: public RHistImplBase<DATA> {
1153public:
1154 RHistImplRuntime(std::array<RAxisConfig, DATA::GetNDim()>&& axisCfg);
1155};
1156#endif
1157
1158} // namespace Detail
1159
1160} // namespace Experimental
1161} // namespace ROOT
1162
1163#endif
#define R__LOG_ERROR(...)
Definition RLogger.hxx:362
#define b(i)
Definition RSha256.hxx:100
#define a(i)
Definition RSha256.hxx:99
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Iterates over the bins of a RHist or RHistImpl.
Interface class for RHistImpl.
Weight_t & GetBinContent(int binidx)
Get the bin content (sum of weights) for bin index binidx (non-const).
int GetNOverflowBins() const noexcept final
Get the number of under- and overflow bins of this histogram, excluding all regular bins.
RHistImplBase(const RHistImplBase &)=default
void(RHistImplBase::*)(const CoordArray_t &x, Weight_t w) FillFunc_t
Type of the Fill(x, w) function.
virtual double GetBinUncertainty(const CoordArray_t &x) const =0
Get the bin uncertainty for the bin at coordinate x.
void AddBinContent(int binidx, Weight_t w)
Add w to the bin at index bin.
int GetNBinsNoOver() const noexcept final
Get the number of bins in this histogram, excluding possible under- and overflow bins.
RHistImplBase(RHistImplBase &&)=default
Hist::CoordArray_t< DATA::GetNDim()> CoordArray_t
Type of the coordinates.
virtual std::unique_ptr< RHistImplBase > Clone() const =0
DATA Stat_t
Type of the statistics (bin content, uncertainties etc).
Stat_t & GetStat() noexcept
Non-const access to statistics.
virtual void FillN(const std::span< const CoordArray_t > xN)=0
Interface function to fill a vector or array of coordinates.
Weight_t GetBinContent(int binidx) const
Get the bin content (sum of weights) for bin index binidx.
const Stat_t & GetStat() const noexcept
Const access to statistics.
Stat_t fStatistics
The histogram's bin content, uncertainties etc.
virtual void FillN(const std::span< const CoordArray_t > xN, const std::span< const Weight_t > weightN)=0
Interface function to fill a vector or array of coordinates with corresponding weights.
virtual Weight_t GetBinContent(const CoordArray_t &x) const =0
Get the bin content (sum of weights) for the bin at coordinate x.
std::array< int, DATA::GetNDim()> BinArray_t
Type of the local per-axis bin indices.
RHistImplBase(std::string_view title, size_t numBins, size_t numOverflowBins)
double GetBinContentAsDouble(int binidx) const final
Get the bin content (sum of weights) for bin index binidx, cast to double.
int GetNBins() const noexcept final
Get the number of bins in this histogram, including possible under- and overflow bins.
typename DATA::Weight_t Weight_t
Type of the bin content (and thus weights).
RHistImplBase(size_t numBins, size_t numOverflowBins)
virtual FillFunc_t GetFillFunc() const =0
Retrieve the pointer to the overridden Fill(x, w) function.
Base class for RHistImplBase that abstracts out the histogram's PRECISION.
Definition RHistImpl.hxx:72
virtual int GetNOverflowBins() const noexcept=0
Number of under- and overflow bins of this histogram, excluding all regular bins.
virtual int GetBinIndex(const CoordArray_t &x) const =0
Given the coordinate x, determine the index of the bin.
std::array< int, DIMENSIONS > BinArray_t
Type of the local per-axis bin indices.
Definition RHistImpl.hxx:77
RHistImplPrecisionAgnosticBase(RHistImplPrecisionAgnosticBase &&)=default
virtual int GetNBins() const noexcept=0
Number of bins of this histogram, including all overflow and underflow bins.
virtual bool HasBinUncertainty() const =0
Whether this histogram's statistics provide storage for uncertainties, or whether uncertainties are d...
virtual int GetNBinsNoOver() const noexcept=0
Number of bins of this histogram, excluding all overflow and underflow bins.
virtual CoordArray_t GetBinTo(int binidx) const =0
Get the upper edge in all dimensions of the bin with index binidx.
virtual BinArray_t GetLocalBins(int binidx) const =0
Given the index of the bin, determine the local per-axis bins x.
RHistImplPrecisionAgnosticBase(const RHistImplPrecisionAgnosticBase &)=default
const std::string & GetTitle() const
Get the histogram title.
Hist::AxisIterRange_t< DIMENSIONS > AxisIterRange_t
Range type.
Definition RHistImpl.hxx:79
static constexpr int GetNDim()
Number of dimensions of the coordinates.
Definition RHistImpl.hxx:88
virtual int GetBinIndexAndGrow(const CoordArray_t &x) const =0
Given the coordinate x, determine the index of the bin, possibly growing axes for which x is out of r...
virtual int GetBinIndexFromLocalBins(const BinArray_t &x) const =0
Given the local per-axis bins x, determine the index of the bin.
virtual double GetBinUncertainty(int binidx) const =0
Get the uncertainty of the bin with index binidx.
virtual double GetBinContentAsDouble(int binidx) const =0
The bin content, cast to double.
virtual const RAxisBase & GetAxis(int iAxis) const =0
Get a base-class view on axis with index iAxis.
virtual CoordArray_t GetBinCenter(int binidx) const =0
Get the center in all dimensions of the bin with index binidx.
virtual AxisIterRange_t GetRange() const =0
Get an AxisIterRange_t for the whole histogram, excluding under- and overflow.
virtual CoordArray_t GetBinFrom(int binidx) const =0
Get the lower edge in all dimensions of the bin with index binidx.
void Fill(const CoordArray_t &x, Weight_t w=1.)
Add a single weight w to the bin at coordinate x.
BinArray_t GetLocalBins(int binidx) const final
Get the local per-axis bin indices x for the given bin index, using ComputeLocalBins().
int GetBinIndexAndGrow(const CoordArray_t &x) const final
Get the bin index for the given coordinates x, growing the axes as needed.
int ComputeGlobalBinRaw(const BinArray_t &zeroBasedLocalBins, BINTYPE GetNBinType) const
Computes a zero-based global bin index, given...
const_iterator end() const noexcept
void(RHistImplBase::*)(const CoordArray_t &x, Weight_t w) FillFunc_t
Type of the Fill(x, w) function.
AxisIterRange_t< DATA::GetNDim()> GetRange() const final
Get the begin() and end() for each axis.
BinArray_t ComputeLocalBins(int global_bin) const
Computes the local per-axis bin indices of a certain bin on an NDIMS-dimensional histogram,...
double GetBinUncertainty(const CoordArray_t &x) const final
Get the bin uncertainty for the bin at coordinate x.
const_iterator begin() const noexcept
CoordArray_t GetBinCenter(int binidx) const final
Get the center coordinates of the bin with index binidx.
int GetBinIndexFromLocalBins(const BinArray_t &x) const final
Get the bin index for the given local per-axis bin indices x, using ComputeGlobalBin().
FillFunc_t GetFillFunc() const final
Retrieve the fill function for this histogram implementation, to prevent the virtual function call fo...
std::tuple< AXISCONFIG... > fAxes
The histogram's axes.
void GrowAxis(int, double)
Grow the axis number iAxis to fit the coordinate x.
Weight_t GetBinContent(const CoordArray_t &x) const final
Get the content of the bin at position x.
int GetBinIndex(const CoordArray_t &x) const final
Get the bin index for the given coordinates x.
BinArray_t VirtualBinsToLocalBins(const BinArray_t &virtualBins) const
Converts zero-based virtual bins where the underflow bin has index 0 and the overflow bin has index N...
const RAxisBase & GetAxis(int iAxis) const final
Normalized axes access, converting from actual axis type to base class.
typename Hist::AxisIterRange_t< NDIMS > AxisIterRange_t
int ComputeGlobalBin(BinArray_t &local_bins) const
Computes the global index of a certain bin on an NDIMS-dimensional histogram, knowing the local per-a...
typename ImplBase_t::BinArray_t BinArray_t
double GetBinUncertainty(int binidx) const final
Return the uncertainties for the given bin index.
bool HasBinUncertainty() const final
Whether this histogram's statistics provide storage for uncertainties, or whether uncertainties are d...
typename ImplBase_t::Weight_t Weight_t
std::unique_ptr< ImplBase_t > Clone() const override
RHistBinIter< ImplBase_t > iterator
void FillN(const std::span< const CoordArray_t > xN, const std::span< const Weight_t > weightN) final
Fill an array of weightN to the bins specified by coordinates xN.
BinArray_t ComputeLocalBinsRaw(int zeroBasedGlobalBin, BINTYPE GetNBinType) const
Computes zero-based local bin indices, given...
const std::tuple< AXISCONFIG... > & GetAxes() const
Get the axes of this histogram.
CoordArray_t GetBinTo(int binidx) const final
Get the coordinates of the high limit of the bin with index binidx.
void FillN(const std::span< const CoordArray_t > xN) final
Fill an array of weightN to the bins specified by coordinates xN.
CoordArray_t GetBinFrom(int binidx) const final
Get the coordinates of the low limit of the bin with index binidx.
BinArray_t LocalBinsToVirtualBins(const BinArray_t &localBins) const
Converts local axis bins from the standard kUnderflowBin/kOverflowBin for under/overflow bin indexing...
RHistBinIter< const ImplBase_t > const_iterator
typename ImplBase_t::CoordArray_t CoordArray_t
Random const_iterator through bins.
Definition RAxis.hxx:126
Histogram axis base class.
Definition RAxis.hxx:44
virtual int GetNBinsNoOver() const noexcept=0
Get the number of bins, excluding under- and overflow.
static constexpr const int kUnderflowBin
Index of the underflow bin, if any.
Definition RAxis.hxx:234
static constexpr const int kOverflowBin
Index of the overflow bin, if any.
Definition RAxis.hxx:237
int GetNBins() const noexcept
Get the number of bins, including under- and overflow.
Definition RAxis.hxx:249
Objects used to configure the different axis types.
Double_t x[n]
Definition legend1.C:17
#define I(x, y, z)
EOverflow
Kinds of under- and overflow handling.
Definition RHistImpl.hxx:47
@ kNoOverflow
Exclude under- and overflows.
@ kUnderOver
Include both under- and overflows.
std::array< AxisIter_t< NDIMS >, 2 > AxisIterRange_t
Range over n dimensional axes - a pair of arrays of n axis iterators.
Definition RHistImpl.hxx:44
bool operator&(EOverflow a, EOverflow b)
Definition RHistImpl.hxx:54
std::array< RAxisBase::const_iterator, NDIMS > AxisIter_t
Iterator over n dimensional axes - an array of n axis iterators.
Definition RHistImpl.hxx:41
EFindStatus
Status of FindBin(x) and FindAdjustedBin(x)
@ kValid
The returned bin index is valid.
@ kCanGrow
The coordinate could fit after growing the axis.
int GetNBinsFromAxes(AXISCONFIG... axisArgs)
Get the number of bins in whole hist, including under- and overflow.
EBinCoord
Specifies if the wanted result is the bin's lower edge, center or higher edge.
@ kBinFrom
Get the lower bin edge.
int GetNOverflowBinsFromAxes(AXISCONFIG... axisArgs)
Get the number of under- and overflow bins in whole hist, excluding regular bins.
int GetNBinsNoOverFromAxes(AXISCONFIG... axisArgs)
Get the number of bins in whole hist, excluding under- and overflow.
static std::array< const RAxisBase *, sizeof...(AXISCONFIG)> GetAxisView(const AXISCONFIG &... axes) noexcept
RLogChannel & HistLog()
Log channel for Hist diagnostics.
Definition RAxis.cxx:25
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
int operator()(int globalVirtualBin, const AXES &, const BINS &, int, BINTYPE) const
Recursively computes a zero-based global bin index, given...
int operator()(int globalVirtualBin, const AXES &axes, const BINS &zeroBasedLocalBins, int binSize, BINTYPE GetNBinType) const
int operator()(int total_regular_bins_before, const AXES &, const BINS &, const BINS &, const BINS &) const
Recursively gets the total number of regular bins before the current dimension, when computing a glob...
int operator()(int total_regular_bins_before, const AXES &axes, const BINS &virtualBins, const BINS &binSizes, const BINS &localBins) const
void operator()(std::array< int, NDIMS-1 >, std::array< int, NDIMS-1 >, const AXES &) const
Recursively compute some quantities needed for ComputeLocalBins, namely the total number of bins per ...
void operator()(std::array< int, NDIMS-1 > &bins_per_hyperplane, std::array< int, NDIMS-1 > &regular_bins_per_hyperplane, const AXES &axes) const
Recursively computes zero-based local bin indices, given...
void operator()(BINS &virtualBins, const AXES &axes, int zeroBasedGlobalBin, BINTYPE GetNBinType) const
void operator()(const AXES &, int &, int &, std::array< int, NDIMS-1 >, std::array< int, NDIMS-1 >, int, int) const
Recursively computes the number of regular bins before the current dimension, as well as the number o...
void operator()(const AXES &axes, int &unprocessed_previous_overflow_bin, int &num_regular_bins_before, std::array< int, NDIMS-1 > bins_per_hyperplane, std::array< int, NDIMS-1 > regular_bins_per_hyperplane, int curr_bins_per_hyperplane, int curr_regular_bins_per_hyperplane) const
void operator()(Hist::AxisIterRange_t< std::tuple_size< AXES >::value > &, const AXES &) const
Recursively fills the ranges of all axes, excluding under- and overflow.
void operator()(Hist::AxisIterRange_t< std::tuple_size< AXES >::value > &range, const AXES &axes) const
Find the per-axis local bin indices associated with a certain set of coordinates.
void operator()(BINS &localBins, const AXES &axes, const COORD &coords) const
Recursively gets the total number of bins in whole hist, including under- and overflow.
int operator()(const AXES &axes) const
Recursively gets the total number of bins in whole hist, excluding under- and overflow.
Recursively gets the number of regular bins just before the current dimension.
void operator()(BINS &binSizes, const AXES &axes) const
void operator()(COORD &, const AXES &, const BINS &, EBinCoord) const
Recursively converts local axis bins from the standard kUnderflowBin/kOverflowBin for under/overflow ...
void operator()(COORD &coords, const AXES &axes, const BINS &localBins, EBinCoord kind) const
Recursively converts local axis bins from the standard kUnderflowBin/kOverflowBin for under/overflow ...
void operator()(BINS &virtualBins, const AXES &axes, const BINS &localBins) const
Recursively converts zero-based virtual bins where the underflow bin has index 0 and the overflow bin...
void operator()(BINS &localBins, const AXES &axes, const BINS &virtualBins) const