Logo ROOT   master
Reference Guide
RAxis.hxx
Go to the documentation of this file.
1 /// \file ROOT/RAxis.h
2 /// \ingroup Hist ROOT7
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_RAxis
17 #define ROOT7_RAxis
18 
19 #include <algorithm>
20 #include <cmath>
21 #include <limits>
22 #include <string>
23 #include <unordered_map>
24 #include <vector>
25 
26 #include "ROOT/RAxisConfig.hxx"
27 #include "ROOT/RStringView.hxx"
28 #include "ROOT/RLogger.hxx"
29 
30 namespace ROOT {
31 namespace Experimental {
32 
33 /**
34  \class RAxisBase
35  Histogram axis base class. Keeps track of the number of bins and overflow
36  handling. Offers bin iteration.
37 
38  Bin indices are starting from 0 for the underflow bin (representing values that
39  are lower than the axis range). Starting at index 1 are the actual bins of the
40  axis, up to N + 1 for an axis with N bins. Index N + 2 is the overflow bin for
41  values larger than the axis range.
42  */
43 class RAxisBase {
44 public:
45  /// Status of FindBin(x)
46  enum class EFindStatus {
47  kCanGrow, ///< Coordinate could fit after growing the axis
48  kValid ///< The returned bin index is valid
49  };
50 
51 protected:
52  ///\name Inaccessible copy, assignment
53  /// The copy and move constructors and assignment operators are protected to
54  /// prevent slicing.
55  ///\{
56  RAxisBase(const RAxisBase &) = default;
57  RAxisBase(RAxisBase &&) = default;
58  RAxisBase &operator=(const RAxisBase &) = default;
59  RAxisBase &operator=(RAxisBase &&) = default;
60  ///\}
61 
62  /// Default construct a RAxisBase (for use by derived classes for I/O)
63  RAxisBase() noexcept = default;
64 
65  /// Virtual destructor needed in this inheritance-based design
66  virtual ~RAxisBase();
67 
68  /// Construct a RAxisBase.
69  ///
70  ///\param[in] title - axis title used for graphics and text representation.
71  RAxisBase(std::string_view title) noexcept: fTitle(title) {}
72 
73  /// Given rawbin (<0 for underflow, >= GetNBinsNoOver() for overflow), determine the
74  /// actual bin number taking into account how over/underflow should be
75  /// handled.
76  ///
77  /// \param[out] status result status of the bin determination.
78  /// \return Returns the bin number adjusted for potential over- and underflow
79  /// bins. Returns kIgnoreBin if the axis cannot handle the over- / underflow,
80  /// in which case `status` will tell how to deal with this overflow.
81  ///
82  int AdjustOverflowBinNumber(double rawbin) const
83  {
84  // Underflow: Put in underflow bin if any, otherwise ignore
85  if (rawbin < 0)
86  return CanGrow() ? kIgnoreBin : GetUnderflowBin();
87 
88  // Account for the presence of underflow bin, if any
89  if (!CanGrow())
90  ++rawbin;
91 
92  // Overflow: Put in overflow bin if any, otherwise ignore
93  if (rawbin >= GetNBins())
94  return CanGrow() ? kIgnoreBin : GetOverflowBin();
95 
96  // Bin index is in range and has been corrected for over/underflow
97  return (int)rawbin;
98  }
99 
100  /// Check if two axis have the same bin borders
101  ///
102  /// Default implementation should work for any RAxis type, but is quite
103  /// inefficient as it does virtual GetBinFrom calls in a loop. RAxis
104  /// implementations are encouraged to provide optimized overrides for common
105  /// axis binning comparison scenarios.
106  virtual bool HasSameBinBordersAs(const RAxisBase& other) const {
107  // Axis growability (and thus under/overflow bin existence) must match
108  if (CanGrow() != other.CanGrow())
109  return false;
110 
111  // Number of normal bins must match
112  if (GetNBinsNoOver() != other.GetNBinsNoOver())
113  return false;
114 
115  // Left borders of normal bins must match
116  for (int bin: *this)
117  if (GetBinFrom(bin) != other.GetBinFrom(bin))
118  return false;
119 
120  // Right border of the last normal bin (aka maximum) must also match
121  if (GetMaximum() != other.GetMaximum())
122  return false;
123 
124  // If all of these checks passed, the two axes have the same bin borders
125  return true;
126  }
127 
128 public:
129  /**
130  \class const_iterator
131  Random const_iterator through bins. Represents the bin index, not a bin
132  content: the axis has no notion of any content.
133  */
134  class const_iterator: public std::iterator<std::random_access_iterator_tag, int /*value*/, int /*distance*/,
135  const int * /*pointer*/, const int & /*ref*/> {
136  int fCursor = 0; ///< Current iteration position
137 
138  public:
139  const_iterator() = default;
140 
141  /// Initialize a const_iterator with its position
142  explicit const_iterator(int cursor) noexcept: fCursor(cursor) {}
143 
144  /// ++i
146  {
147  // Could check whether fCursor < fEnd - but what for?
148  ++fCursor;
149  return *this;
150  }
151 
152  /// --i
154  {
155  // Could check whether fCursor > fBegin - but what for?
156  --fCursor;
157  return *this;
158  }
159 
160  /// i++
162  {
163  const_iterator old(*this);
164  ++(*this);
165  return old;
166  }
167 
168  // i--
170  {
171  const_iterator old(*this);
172  --(*this);
173  return old;
174  }
175 
176  // i += 2
177  const_iterator &operator+=(int d) noexcept
178  {
179  fCursor += d;
180  return *this;
181  }
182 
183  // i -= 2
184  const_iterator &operator-=(int d) noexcept
185  {
186  fCursor -= d;
187  return *this;
188  }
189 
190  // i + 2
191  const_iterator operator+(int d) noexcept
192  {
193  const_iterator ret(*this);
194  ret += d;
195  return ret;
196  }
197  friend const_iterator operator+(int d, const_iterator rhs) noexcept;
198 
199  // i - 2
200  const_iterator operator-(int d) noexcept
201  {
202  const_iterator ret(*this);
203  ret -= d;
204  return ret;
205  }
206 
207  // i - j
208  int operator-(const const_iterator& j) noexcept
209  {
210  return fCursor - j.fCursor;
211  }
212 
213  // i[2]
214  int operator[](int d) noexcept
215  {
216  return fCursor + d;
217  }
218 
219  // *i
220  int operator*() const noexcept { return fCursor; }
221 
222  // i->
223  const int *operator->() const noexcept { return &fCursor; }
224 
225  friend bool operator<(const_iterator lhs, const_iterator rhs) noexcept;
226  friend bool operator>(const_iterator lhs, const_iterator rhs) noexcept;
227  friend bool operator<=(const_iterator lhs, const_iterator rhs) noexcept;
228  friend bool operator>=(const_iterator lhs, const_iterator rhs) noexcept;
229  friend bool operator==(const_iterator lhs, const_iterator rhs) noexcept;
230  friend bool operator!=(const_iterator lhs, const_iterator rhs) noexcept;
231  };
232 
233  /// FindBin() returns this bin to signal that the bin number is invalid.
234  constexpr static const int kIgnoreBin = -1;
235 
236  /// Extra bins for each EAxisOverflow value.
237  // FIXME: Purpose needs clarification
238  constexpr static const int kNOverflowBins[4] = {0, 1, 1, 2};
239 
240  /// Get the axis's title
241  const std::string &GetTitle() const { return fTitle; }
242 
243  /// Whether this axis can grow (and thus has no overflow bins).
244  virtual bool CanGrow() const noexcept = 0;
245 
246  /// Get the number of bins, excluding under- and overflow.
247  virtual int GetNBinsNoOver() const noexcept = 0;
248 
249  /// Get the number of bins, including under- and overflow.
250  int GetNBins() const noexcept { return GetNBinsNoOver() + GetNOverflowBins(); }
251 
252  /// Get the number of over- and underflow bins: 0 for growable axes, 2 otherwise.
253  int GetNOverflowBins() const noexcept
254  {
255  if (CanGrow())
256  return 0;
257  else
258  return 2;
259  };
260 
261  /// Get the bin index for the underflow bin (or the last bin outside range
262  /// if CanGrow()).
263  int GetUnderflowBin() const noexcept {
264  if (CanGrow())
265  return -1;
266  else
267  return 0;
268  }
269 
270  /// Get the bin index for the underflow bin (or the next bin outside range
271  /// if CanGrow()).
272  int GetOverflowBin() const noexcept {
273  if (CanGrow())
274  return GetNBins();
275  else
276  return GetNBins() - 1;
277  }
278 
279  /// Whether the bin index is referencing a bin lower than the axis range.
280  bool IsUnderflowBin(int bin) const noexcept { return bin <= GetUnderflowBin(); }
281 
282  /// Whether the bin index is referencing a bin higher than the axis range.
283  bool IsOverflowBin(int bin) const noexcept { return bin >= GetOverflowBin(); }
284 
285  ///\name Iterator interfaces
286  ///\{
287 
288  /// Get a const_iterator pointing to the first non-underflow bin.
289  const_iterator begin() const noexcept { return const_iterator{GetUnderflowBin() + 1}; }
290 
291  /// Get a const_iterator pointing the first bin, whether underflow or not
292  const_iterator begin_with_underflow() const noexcept { return const_iterator{0}; }
293 
294  /// Get a const_iterator pointing right beyond the last non-overflow bin
295  /// (i.e. pointing to the overflow bin, if any).
296  const_iterator end() const noexcept { return const_iterator{GetOverflowBin()}; }
297 
298  /// Get a const_iterator pointing right beyond the last bin, whether overflow or not
299  const_iterator end_with_overflow() const noexcept { return const_iterator{GetNBins()}; }
300  ///\}
301 
302  /// Find the bin index for the given coordinate.
303  /// \note Passing a bin border coordinate can either return the bin above or
304  /// below the bin border. I.e. don't do that for reliable results!
305  virtual int FindBin(double x) const noexcept = 0;
306 
307  /// Get the bin center for the given bin index.
308  /// The result of this method on an overflow or underflow bin is unspecified
309  virtual double GetBinCenter(int bin) const = 0;
310 
311  /// Get the low bin border ("left edge") for the given bin index.
312  /// The result of this method on an underflow bin is unspecified
313  virtual double GetBinFrom(int bin) const = 0;
314 
315  /// Get the high bin border ("right edge") for the given bin index.
316  /// The result of this method on an overflow bin is unspecified
317  double GetBinTo(int bin) const { return GetBinFrom(bin + 1); }
318 
319  /// Get the low end of the axis range.
320  double GetMinimum() const { return GetBinTo(GetUnderflowBin()); }
321 
322  /// Get the high end of the axis range.
323  double GetMaximum() const { return GetBinFrom(GetOverflowBin()); }
324 
325  /// Check if two axes use the same binning convention, i.e.
326  ///
327  /// - Either they are both growable or neither of them is growable.
328  /// - Minimum, maximum, and all bin borders in the middle are the same.
329  /// - Bin labels must match (exactly including order, for now).
330  bool HasSameBinningAs(const RAxisBase& other) const;
331 
332  /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
333  /// return the bin for which this is a low edge. If it's not a bin edge,
334  /// return -1.
335  virtual int GetBinIndexForLowEdge(double x) const noexcept = 0;
336 
337 private:
338  std::string fTitle; ///< Title of this axis, used for graphics / text.
339 };
340 
341 ///\name RAxisBase::const_iterator external operators
342 ///\{
343 
344 /// 2 + i
346 {
347  return rhs + d;
348 }
349 
350 /// i < j
352 {
353  return lhs.fCursor < rhs.fCursor;
354 }
355 
356 /// i > j
358 {
359  return lhs.fCursor > rhs.fCursor;
360 }
361 
362 /// i <= j
364 {
365  return lhs.fCursor <= rhs.fCursor;
366 }
367 
368 /// i >= j
370 {
371  return lhs.fCursor >= rhs.fCursor;
372 }
373 
374 /// i == j
376 {
377  return lhs.fCursor == rhs.fCursor;
378 }
379 
380 /// i != j
382 {
383  return lhs.fCursor != rhs.fCursor;
384 }
385 ///\}
386 
387 /**
388  Axis with equidistant bin borders. Defined by lower l and upper u limit and
389  the number of bins n. All bins have the same width (u-l)/n.
390 
391  This axis cannot grow; use `RAxisGrow` for that.
392  */
394 protected:
395  double fLow = 0.; ///< The lower limit of the axis
396  double fInvBinWidth = 0.; ///< The inverse of the bin width
397  unsigned int fNBinsNoOver; ///< Number of bins excluding under- and overflow.
398 
399  /// Determine the inverse bin width.
400  /// \param nbinsNoOver - number of bins without unter-/overflow
401  /// \param lowOrHigh - first axis boundary
402  /// \param lighOrLow - second axis boundary
403  static double GetInvBinWidth(int nbinsNoOver, double lowOrHigh, double highOrLow)
404  {
405  return nbinsNoOver / std::fabs(highOrLow - lowOrHigh);
406  }
407 
408  /// See RAxisBase::HasSameBinBordersAs
409  bool HasSameBinBordersAs(const RAxisBase& other) const override;
410 
411 public:
412  RAxisEquidistant() = default;
413 
414  /// Initialize a RAxisEquidistant.
415  /// \param[in] title - axis title used for graphics and text representation.
416  /// \param nbins - number of bins in the axis, excluding under- and overflow
417  /// bins.
418  /// \param low - the low axis range. Any coordinate below that is considered
419  /// as underflow. The first bin's lower edge is at this value.
420  /// \param high - the high axis range. Any coordinate above that is considered
421  /// as overflow. The last bin's higher edge is at this value.
422  explicit RAxisEquidistant(std::string_view title, int nbinsNoOver, double low, double high) noexcept
423  : RAxisBase(title)
424  , fLow(low)
425  , fInvBinWidth(GetInvBinWidth(nbinsNoOver, low, high))
426  , fNBinsNoOver(nbinsNoOver)
427  {}
428 
429  /// Initialize a RAxisEquidistant.
430  /// \param nbins - number of bins in the axis, excluding under- and overflow
431  /// bins.
432  /// \param low - the low axis range. Any coordinate below that is considered
433  /// as underflow. The first bin's lower edge is at this value.
434  /// \param high - the high axis range. Any coordinate above that is considered
435  /// as overflow. The last bin's higher edge is at this value.
436  /// \param canGrow - whether this axis can extend its range.
437  explicit RAxisEquidistant(int nbinsNoOver, double low, double high) noexcept
438  : RAxisEquidistant("", nbinsNoOver, low, high)
439  {}
440 
441  /// Convert to RAxisConfig.
442  operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetNBinsNoOver(), GetMinimum(), GetMaximum()); }
443 
444  /// Get the number of bins, excluding under- and overflow.
445  int GetNBinsNoOver() const noexcept final override { return fNBinsNoOver; }
446 
447  /// Find the bin index for the given coordinate.
448  /// \note Passing a bin border coordinate can either return the bin above or
449  /// below the bin border. I.e. don't do that for reliable results!
450  int FindBin(double x) const noexcept final override
451  {
452  double rawbin = (x - fLow) * fInvBinWidth;
453  return AdjustOverflowBinNumber(rawbin);
454  }
455 
456  /// This axis cannot grow.
457  bool CanGrow() const noexcept override { return false; }
458 
459  /// Get the width of the bins
460  double GetBinWidth() const noexcept { return 1. / fInvBinWidth; }
461 
462  /// Get the inverse of the width of the bins
463  double GetInverseBinWidth() const noexcept { return fInvBinWidth; }
464 
465  /// Get the bin center for the given bin index.
466  /// For the bin == 1 (the first bin) of 2 bins for an axis (0., 1.), this
467  /// returns 0.25.
468  /// The result of this method on an overflow or underflow bin is unspecified
469  double GetBinCenter(int bin) const final override { return fLow + (bin - *begin() + 0.5) / fInvBinWidth; }
470 
471  /// Get the low bin border for the given bin index.
472  /// For the bin == 1 (the first bin) of 2 bins for an axis (0., 1.), this
473  /// returns 0.
474  /// The result of this method on an underflow bin is unspecified
475  double GetBinFrom(int bin) const final override { return fLow + (bin - *begin()) / fInvBinWidth; }
476 
477  /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
478  /// return the bin for which this is a low edge. If it's not a bin edge,
479  /// return -1.
480  int GetBinIndexForLowEdge(double x) const noexcept final override;
481 };
482 
483 namespace Internal {
484 
485 template <>
486 struct AxisConfigToType<RAxisConfig::kEquidistant> {
488 
489  Axis_t operator()(const RAxisConfig &cfg) noexcept
490  {
491  return RAxisEquidistant(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
492  }
493 };
494 
495 } // namespace Internal
496 
497 /** An axis that can extend its range, keeping the number of its bins unchanged.
498  The axis is constructed with an initial range. Apart from its ability to
499  grow, this axis behaves like a RAxisEquidistant.
500  */
502 public:
503  /// Initialize a RAxisGrow.
504  /// \param nbins - number of bins in the axis, excluding under- and overflow
505  /// bins. This value is fixed over the lifetime of the object.
506  /// \param low - the initial value for the low axis range. Any coordinate
507  /// below that is considered as underflow. To trigger the growing of the
508  /// axis call Grow().
509  /// \param high - the initial value for the high axis range. Any coordinate
510  /// above that is considered as overflow. To trigger the growing of the
511  /// axis call Grow()
512  explicit RAxisGrow(std::string_view title, int nbins, double low, double high) noexcept
513  : RAxisEquidistant(title, nbins, low, high)
514  {}
515 
516  /// Initialize a RAxisGrow.
517  /// \param[in] title - axis title used for graphics and text representation.
518  /// \param nbins - number of bins in the axis, excluding under- and overflow
519  /// bins. This value is fixed over the lifetime of the object.
520  /// \param low - the initial value for the low axis range. Any coordinate
521  /// below that is considered as underflow. To trigger the growing of the
522  /// axis call Grow().
523  /// \param high - the initial value for the high axis range. Any coordinate
524  /// above that is considered as overflow. To trigger the growing of the
525  /// axis call Grow()
526  explicit RAxisGrow(int nbins, double low, double high) noexcept: RAxisGrow("", nbins, low, high) {}
527 
528  /// Convert to RAxisConfig.
530 
531  /// Grow this axis to make the "virtual bin" toBin in-range. This keeps the
532  /// non-affected axis limit unchanged, and extends the other axis limit such
533  /// that a number of consecutive bins are merged.
534  ///
535  /// Example, assuming an initial RAxisGrow with 10 bins from 0. to 1.:
536  /// - `Grow(0)`: that (virtual) bin spans from -0.1 to 0. To include it
537  /// in the axis range, the lower limit must be shifted. The minimal number
538  /// of bins that can be merged is 2, thus the new axis will span from
539  /// -1. to 1.
540  /// - `Grow(-1)`: that (virtual) bin spans from -0.2 to 0.1. To include it
541  /// in the axis range, the lower limit must be shifted. The minimal number
542  /// of bins that can be merged is 2, thus the new axis will span from
543  /// -1. to 1.
544  /// - `Grow(50)`: that (virtual) bin spans from 4.9 to 5.0. To include it
545  /// in the axis range, the higher limit must be shifted. Five bins need to
546  /// be merged, making the new axis range 0. to 5.0.
547  ///
548  /// \param toBin - the "virtual" bin number, as if the axis had an infinite
549  /// number of bins with the current bin width. For instance, for an axis
550  /// with ten bins in the range 0. to 1., the coordinate 2.05 has the virtual
551  /// bin index 20.
552  /// \return Returns the number of bins that were merged to reach the value.
553  /// A value of 1 means that no bins were merged (toBin was in the original
554  /// axis range).
555  int Grow(int toBin);
556 
557  /// This axis kind can increase its range.
558  bool CanGrow() const noexcept final override { return true; }
559 };
560 
561 namespace Internal {
562 
563 template <>
565  using Axis_t = RAxisGrow;
566 
567  Axis_t operator()(const RAxisConfig &cfg) noexcept
568  {
569  return RAxisGrow(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
570  }
571 };
572 
573 } // namespace Internal
574 
575 /**
576  An axis with non-equidistant bins (also known as "variable binning"). It is
577  defined by an array of bin borders - one more than the number of
578  (non-overflow-) bins it has! As an example, an axis with two bin needs three
579  bin borders:
580  - lower edge of the first bin;
581  - higher edge of the first bin, identical to the lower edge of the second
582  bin;
583  - higher edge of the second bin
584 
585  This axis cannot grow; the size of new bins would not be well defined.
586  */
587 class RAxisIrregular: public RAxisBase {
588 private:
589  /// Bin borders, one more than the number of non-overflow bins.
590  std::vector<double> fBinBorders;
591 
592 protected:
593  /// See RAxisBase::HasSameBinBordersAs
594  bool HasSameBinBordersAs(const RAxisBase& other) const override;
595 
596 public:
597  RAxisIrregular() = default;
598 
599  /// Construct a RAxisIrregular from a vector of bin borders.
600  /// \note The bin borders must be sorted in increasing order!
601  explicit RAxisIrregular(const std::vector<double> &binborders)
602  : RAxisBase(), fBinBorders(binborders)
603  {
604 #ifdef R__DO_RANGE_CHECKS
605  if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
606  R__ERROR_HERE("HIST") << "Bin borders must be sorted!";
607 #endif // R__DO_RANGE_CHECKS
608  }
609 
610  /// Construct a RAxisIrregular from a vector of bin borders.
611  /// \note The bin borders must be sorted in increasing order!
612  /// Faster, noexcept version taking an rvalue of binborders. The compiler will
613  /// know when it can take this one.
614  explicit RAxisIrregular(std::vector<double> &&binborders) noexcept
615  : RAxisBase(), fBinBorders(std::move(binborders))
616  {
617 #ifdef R__DO_RANGE_CHECKS
618  if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
619  R__ERROR_HERE("HIST") << "Bin borders must be sorted!";
620 #endif // R__DO_RANGE_CHECKS
621  }
622 
623  /// Construct a RAxisIrregular from a vector of bin borders.
624  /// \note The bin borders must be sorted in increasing order!
625  explicit RAxisIrregular(std::string_view title, const std::vector<double> &binborders)
626  : RAxisBase(title), fBinBorders(binborders)
627  {
628 #ifdef R__DO_RANGE_CHECKS
629  if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
630  R__ERROR_HERE("HIST") << "Bin borders must be sorted!";
631 #endif // R__DO_RANGE_CHECKS
632  }
633 
634  /// Construct a RAxisIrregular from a vector of bin borders.
635  /// \note The bin borders must be sorted in increasing order!
636  /// Faster, noexcept version taking an rvalue of binborders. The compiler will
637  /// know when it can take this one.
638  explicit RAxisIrregular(std::string_view title, std::vector<double> &&binborders) noexcept
639  : RAxisBase(title), fBinBorders(std::move(binborders))
640  {
641 #ifdef R__DO_RANGE_CHECKS
642  if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
643  R__ERROR_HERE("HIST") << "Bin borders must be sorted!";
644 #endif // R__DO_RANGE_CHECKS
645  }
646 
647  /// Convert to RAxisConfig.
648  operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinBorders()); }
649 
650  /// Get the number of bins, excluding under- and overflow.
651  int GetNBinsNoOver() const noexcept final override { return fBinBorders.size() - 1; }
652 
653  /// Find the bin index corresponding to coordinate x. If the coordinate is
654  /// below the axis range, return 0. If it is above, return N + 1 for an axis
655  /// with N non-overflow bins.
656  int FindBin(double x) const noexcept final override
657  {
658  const auto bBegin = fBinBorders.begin();
659  const auto bEnd = fBinBorders.end();
660  // lower_bound finds the first bin border that is >= x.
661  auto iNotLess = std::lower_bound(bBegin, bEnd, x);
662  int rawbin = iNotLess - bBegin;
663  // No need for AdjustOverflowBinNumber(rawbin) here; lower_bound() is the
664  // answer: e.g. for x < *bBegin, rawbin is 0.
665  return rawbin;
666  }
667 
668  /// Get the bin center of the bin with the given index.
669  ///
670  /// For the bin at index 0 (i.e. the underflow bin), a bin center of
671  /// `std::numeric_limits<double>::lowest()` is returned, i.e. the smallest
672  /// value that can be held in a double.
673  /// Similarly, for the bin at index N + 1 (i.e. the overflow bin), a bin
674  /// center of `std::numeric_limits<double>::max()` is returned, i.e. the
675  /// largest value that can be held in a double.
676  double GetBinCenter(int bin) const final override
677  {
678  if (IsUnderflowBin(bin))
679  return std::numeric_limits<double>::lowest();
680  if (IsOverflowBin(bin))
681  return std::numeric_limits<double>::max();
682  return 0.5 * (fBinBorders[bin - 1] + fBinBorders[bin]);
683  }
684 
685  /// Get the lower bin border for a given bin index.
686  ///
687  /// For the bin at index 0 (i.e. the underflow bin), a lower bin border of
688  /// `std::numeric_limits<double>::lowest()` is returned, i.e. the smallest
689  /// value that can be held in a double.
690  /// Similarly, for the bin at index N + 2 (i.e. after the overflow bin), a
691  /// lower bin border of `std::numeric_limits<double>::max()` is returned,
692  /// i.e. the largest value that can be held in a double.
693  double GetBinFrom(int bin) const final override
694  {
695  if (IsUnderflowBin(bin))
696  return std::numeric_limits<double>::lowest();
697  if (IsOverflowBin(bin-1))
698  return std::numeric_limits<double>::max();
699  // bin 0 is underflow;
700  // bin 1 starts at fBinBorders[0]
701  return fBinBorders[bin - 1];
702  }
703 
704  /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
705  /// return the bin for which this is a low edge. If it's not a bin edge,
706  /// return -1.
707  int GetBinIndexForLowEdge(double x) const noexcept final override;
708 
709  /// This axis cannot be extended.
710  bool CanGrow() const noexcept final override { return false; }
711 
712  /// Access to the bin borders used by this axis.
713  const std::vector<double> &GetBinBorders() const noexcept { return fBinBorders; }
714 };
715 
716 namespace Internal {
717 
718 template <>
719 struct AxisConfigToType<RAxisConfig::kIrregular> {
721 
722  Axis_t operator()(const RAxisConfig &cfg) { return RAxisIrregular(cfg.GetTitle(), cfg.GetBinBorders()); }
723 };
724 
725 } // namespace Internal
726 
727 /**
728  \class RAxisLabels
729  A RAxisGrow that has a label assigned to each bin and a bin width of 1.
730 
731  While filling still works through coordinates (i.e. arrays of doubles),
732  RAxisLabels allows to convert a string to a bin number or the bin's coordinate
733  center. The number of labels and the number of bins reported by RAxisGrow might
734  differ: the RAxisGrow will only grow when seeing a Fill(), while the RAxisLabels
735  will add a new label whenever `GetBinCenter()` is called.
736 
737  Implementation details:
738  Filling happens often; GetBinCenter() needs to be fast. Thus the unordered_map.
739  The painter needs the reverse: it wants the label for bin 0, bin 1 etc. The axis
740  should only store the bin labels once; referencing them is (due to re-allocation,
741  hashing etc) non-trivial. So instead, build a vector<string_view> for the few
742  times the axis needs to be painted.
743  */
744 class RAxisLabels: public RAxisGrow {
745 private:
746  /// Map of label (view on `fLabels`'s elements) to bin index
747  std::unordered_map<std::string, int /*bin number*/> fLabelsIndex;
748 
749 public:
750  /// Construct a RAxisLables from a `vector` of `string_view`s, with title.
751  explicit RAxisLabels(std::string_view title, const std::vector<std::string_view> &labels)
752  : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
753  {
754  for (size_t i = 0, n = labels.size(); i < n; ++i)
755  fLabelsIndex[std::string(labels[i])] = i;
756  }
757 
758  /// Construct a RAxisLables from a `vector` of `string`s, with title.
759  explicit RAxisLabels(std::string_view title, const std::vector<std::string> &labels)
760  : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
761  {
762  for (size_t i = 0, n = labels.size(); i < n; ++i)
763  fLabelsIndex[labels[i]] = i;
764  }
765 
766  /// Construct a RAxisLables from a `vector` of `string_view`s
767  explicit RAxisLabels(const std::vector<std::string_view> &labels): RAxisLabels("", labels) {}
768 
769  /// Construct a RAxisLables from a `vector` of `string`s
770  explicit RAxisLabels(const std::vector<std::string> &labels): RAxisLabels("", labels) {}
771 
772  /// Convert to RAxisConfig.
773  operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinLabels()); }
774 
775  /// Get the bin index with label.
776  int FindBinByName(const std::string &label)
777  {
778  auto insertResult = fLabelsIndex.insert({label, -1});
779  if (insertResult.second) {
780  // we have created a new label
781  int idx = fLabelsIndex.size() - 1;
782  insertResult.first->second = idx;
783  return idx;
784  }
785  return insertResult.first->second;
786  }
787 
788  /// Get the center of the bin with label.
789  double GetBinCenterByName(const std::string &label)
790  {
791  return FindBinByName(label) + 0.5; // bin *center*
792  }
793 
794  /// Build a vector of labels. The position in the vector defines the label's bin.
795  std::vector<std::string_view> GetBinLabels() const
796  {
797  std::vector<std::string_view> vec(fLabelsIndex.size());
798  for (const auto &kv: fLabelsIndex)
799  vec.at(kv.second) = kv.first;
800  return vec;
801  }
802 
803  /// Result of an RAxisLabels label set comparison
805  /// Both axes have the same labels, mapping to the same bins
807 
808  /// The other axis doesn't have some labels from this axis
810 
811  /// The other axis has some labels which this axis doesn't have
813 
814  /// The labels shared by both axes do not map into the same bins
816  };
817 
818  /// Compare the labels of this axis with those of another axis
819  LabelsCmpFlags CompareBinLabels(const RAxisLabels& other) const noexcept {
820  // This will eventually contain the results of the labels comparison
822  size_t missing_in_other = 0;
823 
824  // First, check how this axis' labels map into the other axis
825  for (const auto &kv: fLabelsIndex) {
826  auto iter = other.fLabelsIndex.find(kv.first);
827  if (iter == other.fLabelsIndex.cend()) {
828  ++missing_in_other;
829  } else if (iter->second != kv.second) {
830  result = LabelsCmpFlags(result | kLabelsCmpDisordered);
831  }
832  }
833  if (missing_in_other > 0)
834  result = LabelsCmpFlags(result | kLabelsCmpSubset);
835 
836  // If this covered all labels in the other axis, we're done
837  if (fLabelsIndex.size() == other.fLabelsIndex.size() + missing_in_other)
838  return result;
839 
840  // Otherwise, we must check the labels of the other axis too
841  for (const auto &kv: other.fLabelsIndex)
842  if (fLabelsIndex.find(kv.first) == fLabelsIndex.cend())
843  return LabelsCmpFlags(result | kLabelsCmpSuperset);
844  return result;
845  }
846 };
847 
848 namespace Internal {
849 
850 template <>
851 struct AxisConfigToType<RAxisConfig::kLabels> {
853 
854  Axis_t operator()(const RAxisConfig &cfg) { return RAxisLabels(cfg.GetTitle(), cfg.GetBinLabels()); }
855 };
856 
857 } // namespace Internal
858 
859 ///\name Axis Compatibility
860 ///\{
861 enum class EAxisCompatibility {
862  kIdentical, ///< Source and target axes are identical
863 
864  kContains, ///< The source is a subset of bins of the target axis
865 
866  /// The bins of the source axis have finer granularity, but the bin borders
867  /// are compatible. Example:
868  /// source: 0., 1., 2., 3., 4., 5., 6.; target: 0., 2., 5., 6.
869  /// Note that this is *not* a symmetrical property: only one of
870  /// CanMerge(source, target), CanMap(target, source) can return kContains.
871  kSampling,
872 
873  /// The source axis and target axis have different binning. Example:
874  /// source: 0., 1., 2., 3., 4., target: 0., 0.1, 0.2, 0.3, 0.4
876 };
877 
878 /// Whether (and how) the source axis can be merged into the target axis.
879 EAxisCompatibility CanMap(const RAxisEquidistant &target, const RAxisEquidistant &source) noexcept;
880 ///\}
881 
882 } // namespace Experimental
883 } // namespace ROOT
884 
885 #endif // ROOT7_RAxis header guard
double GetBinFrom(int bin) const final override
Get the low bin border for the given bin index.
Definition: RAxis.hxx:475
const std::vector< double > & GetBinBorders() const noexcept
Access to the bin borders used by this axis.
Definition: RAxis.hxx:713
An axis with non-equidistant bins (also known as "variable binning").
Definition: RAxis.hxx:587
virtual bool HasSameBinBordersAs(const RAxisBase &other) const
Check if two axis have the same bin borders.
Definition: RAxis.hxx:106
const_iterator begin_with_underflow() const noexcept
Get a const_iterator pointing the first bin, whether underflow or not.
Definition: RAxis.hxx:292
bool operator<(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i < j
Definition: RAxis.hxx:351
Returns the available number of logical cores.
Definition: StringConv.hxx:21
static double GetInvBinWidth(int nbinsNoOver, double lowOrHigh, double highOrLow)
Determine the inverse bin width.
Definition: RAxis.hxx:403
int FindBin(double x) const noexcept final override
Find the bin index for the given coordinate.
Definition: RAxis.hxx:450
RAxisIrregular(std::vector< double > &&binborders) noexcept
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:614
double GetBinCenter(int bin) const final override
Get the bin center for the given bin index.
Definition: RAxis.hxx:469
int Grow(int toBin)
Grow this axis to make the "virtual bin" toBin in-range.
RAxisLabels(std::string_view title, const std::vector< std::string_view > &labels)
Construct a RAxisLables from a vector of string_views, with title.
Definition: RAxis.hxx:751
const_iterator operator--(int) noexcept
Definition: RAxis.hxx:169
int AdjustOverflowBinNumber(double rawbin) const
Given rawbin (<0 for underflow, >= GetNBinsNoOver() for overflow), determine the actual bin number ta...
Definition: RAxis.hxx:82
const_iterator & operator+=(int d) noexcept
Definition: RAxis.hxx:177
RAxisIrregular(std::string_view title, const std::vector< double > &binborders)
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:625
bool operator==(const TString &t, const std::string &s)
Definition: REveTypes.cxx:13
const_iterator & operator-=(int d) noexcept
Definition: RAxis.hxx:184
bool HasSameBinBordersAs(const RAxisBase &other) const override
See RAxisBase::HasSameBinBordersAs.
Definition: RAxis.cxx:105
int GetBinIndexForLowEdge(double x) const noexcept final override
If the coordinate x is within 10 ULPs of a bin low edge coordinate, return the bin for which this is ...
Definition: RAxis.cxx:83
int GetNOverflowBins() const noexcept
Get the number of over- and underflow bins: 0 for growable axes, 2 otherwise.
Definition: RAxis.hxx:253
bool HasSameBinningAs(const RAxisBase &other) const
Check if two axes use the same binning convention, i.e.
Definition: RAxis.cxx:25
const std::string & GetTitle() const
Get the axis&#39;s title.
Definition: RAxis.hxx:241
STL namespace.
const_iterator end_with_overflow() const noexcept
Get a const_iterator pointing right beyond the last bin, whether overflow or not. ...
Definition: RAxis.hxx:299
const std::string & GetTitle() const
Get the axis&#39;s title.
const_iterator operator-(int d) noexcept
Definition: RAxis.hxx:200
bool operator!=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i != j
Definition: RAxis.hxx:381
double GetBinFrom(int bin) const final override
Get the lower bin border for a given bin index.
Definition: RAxis.hxx:693
bool CanGrow() const noexcept override
This axis cannot grow.
Definition: RAxis.hxx:457
double fLow
The lower limit of the axis.
Definition: RAxis.hxx:395
RAxisBase() noexcept=default
Default construct a RAxisBase (for use by derived classes for I/O)
std::unordered_map< std::string, int > fLabelsIndex
Map of label (view on fLabels&#39;s elements) to bin index.
Definition: RAxis.hxx:747
RAxisLabels(const std::vector< std::string > &labels)
Construct a RAxisLables from a vector of strings.
Definition: RAxis.hxx:770
double GetBinWidth() const noexcept
Get the width of the bins.
Definition: RAxis.hxx:460
RAxisEquidistant(std::string_view title, int nbinsNoOver, double low, double high) noexcept
Initialize a RAxisEquidistant.
Definition: RAxis.hxx:422
double GetMinimum() const
Get the low end of the axis range.
Definition: RAxis.hxx:320
virtual bool CanGrow() const noexcept=0
Whether this axis can grow (and thus has no overflow bins).
The bins of the source axis have finer granularity, but the bin borders are compatible.
Double_t x[n]
Definition: legend1.C:17
double GetMaximum() const
Get the high end of the axis range.
Definition: RAxis.hxx:323
RAxisIrregular(std::string_view title, std::vector< double > &&binborders) noexcept
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:638
Both axes have the same labels, mapping to the same bins.
Definition: RAxis.hxx:806
bool operator>=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i >= j
Definition: RAxis.hxx:369
const std::vector< std::string > & GetBinLabels() const noexcept
Get the bin labels; non-empty if the GetKind() == kLabels.
const int * operator->() const noexcept
Definition: RAxis.hxx:223
Axis_t operator()(const RAxisConfig &cfg) noexcept
Definition: RAxis.hxx:567
An axis that can extend its range, keeping the number of its bins unchanged.
Definition: RAxis.hxx:501
RAxisLabels(std::string_view title, const std::vector< std::string > &labels)
Construct a RAxisLables from a vector of strings, with title.
Definition: RAxis.hxx:759
EFindStatus
Status of FindBin(x)
Definition: RAxis.hxx:46
REveException operator+(const REveException &s1, const std::string &s2)
Definition: REveTypes.cxx:21
friend bool operator<=(const_iterator lhs, const_iterator rhs) noexcept
i <= j
Definition: RAxis.hxx:363
double GetInverseBinWidth() const noexcept
Get the inverse of the width of the bins.
Definition: RAxis.hxx:463
const_iterator operator++(int) noexcept
i++
Definition: RAxis.hxx:161
static constexpr const int kIgnoreBin
FindBin() returns this bin to signal that the bin number is invalid.
Definition: RAxis.hxx:234
VecExpr< UnaryOp< Fabs< T >, VecExpr< A, T, D >, T >, T, D > fabs(const VecExpr< A, T, D > &rhs)
static constexpr const int kNOverflowBins[4]
Extra bins for each EAxisOverflow value.
Definition: RAxis.hxx:238
bool operator>(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i > j
Definition: RAxis.hxx:357
const std::vector< double > & GetBinBorders() const noexcept
Get the bin borders; non-empty if the GetKind() == kIrregular.
int GetNBinsNoOver() const noexcept final override
Get the number of bins, excluding under- and overflow.
Definition: RAxis.hxx:445
std::string fTitle
Title of this axis, used for graphics / text.
Definition: RAxis.hxx:338
RAxisEquidistant(int nbinsNoOver, double low, double high) noexcept
Initialize a RAxisEquidistant.
Definition: RAxis.hxx:437
friend bool operator>(const_iterator lhs, const_iterator rhs) noexcept
i > j
Definition: RAxis.hxx:357
const_iterator(int cursor) noexcept
Initialize a const_iterator with its position.
Definition: RAxis.hxx:142
bool CanGrow() const noexcept final override
This axis kind can increase its range.
Definition: RAxis.hxx:558
std::vector< double > fBinBorders
Bin borders, one more than the number of non-overflow bins.
Definition: RAxis.hxx:590
bool CanGrow() const noexcept final override
This axis cannot be extended.
Definition: RAxis.hxx:710
int GetOverflowBin() const noexcept
Get the bin index for the underflow bin (or the next bin outside range if CanGrow()).
Definition: RAxis.hxx:272
const_iterator & operator++() noexcept
++i
Definition: RAxis.hxx:145
RAxisGrow(int nbins, double low, double high) noexcept
Initialize a RAxisGrow.
Definition: RAxis.hxx:526
bool IsUnderflowBin(int bin) const noexcept
Whether the bin index is referencing a bin lower than the axis range.
Definition: RAxis.hxx:280
bool HasSameBinBordersAs(const RAxisBase &other) const override
See RAxisBase::HasSameBinBordersAs.
Definition: RAxis.cxx:68
#define d(i)
Definition: RSha256.hxx:102
friend bool operator>=(const_iterator lhs, const_iterator rhs) noexcept
i >= j
Definition: RAxis.hxx:369
const_iterator operator+(int d) noexcept
Definition: RAxis.hxx:191
virtual int GetNBinsNoOver() const noexcept=0
Get the number of bins, excluding under- and overflow.
const_iterator end() const noexcept
Get a const_iterator pointing right beyond the last non-overflow bin (i.e.
Definition: RAxis.hxx:296
double GetBinCenter(int bin) const final override
Get the bin center of the bin with the given index.
Definition: RAxis.hxx:676
The labels shared by both axes do not map into the same bins.
Definition: RAxis.hxx:815
bool IsOverflowBin(int bin) const noexcept
Whether the bin index is referencing a bin higher than the axis range.
Definition: RAxis.hxx:283
The other axis doesn&#39;t have some labels from this axis.
Definition: RAxis.hxx:809
const_iterator & operator--() noexcept
–i
Definition: RAxis.hxx:153
unsigned int fNBinsNoOver
Number of bins excluding under- and overflow.
Definition: RAxis.hxx:397
A RAxisGrow that has a label assigned to each bin and a bin width of 1.
Definition: RAxis.hxx:744
The source is a subset of bins of the target axis.
double fInvBinWidth
The inverse of the bin width.
Definition: RAxis.hxx:396
Objects used to configure the different axis types.
Definition: RAxisConfig.hxx:35
EAxisCompatibility CanMap(const RAxisEquidistant &target, const RAxisEquidistant &source) noexcept
Whether (and how) the source axis can be merged into the target axis.
Definition: RAxis.cxx:117
friend bool operator==(const_iterator lhs, const_iterator rhs) noexcept
i == j
Definition: RAxis.hxx:375
RAxisIrregular(const std::vector< double > &binborders)
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:601
Histogram axis base class.
Definition: RAxis.hxx:43
virtual int FindBin(double x) const noexcept=0
Find the bin index for the given coordinate.
Converts a RAxisConfig of whatever kind to the corresponding RAxisBase-derived object.
int GetNBins() const noexcept
Get the number of bins, including under- and overflow.
Definition: RAxis.hxx:250
int GetBinIndexForLowEdge(double x) const noexcept final override
If the coordinate x is within 10 ULPs of a bin low edge coordinate, return the bin for which this is ...
Definition: RAxis.cxx:43
double GetBinCenterByName(const std::string &label)
Get the center of the bin with label.
Definition: RAxis.hxx:789
Coordinate could fit after growing the axis.
RAxisGrow(std::string_view title, int nbins, double low, double high) noexcept
Initialize a RAxisGrow.
Definition: RAxis.hxx:512
friend bool operator<(const_iterator lhs, const_iterator rhs) noexcept
i < j
Definition: RAxis.hxx:351
The other axis has some labels which this axis doesn&#39;t have.
Definition: RAxis.hxx:812
Source and target axes are identical.
int GetUnderflowBin() const noexcept
Get the bin index for the underflow bin (or the last bin outside range if CanGrow()).
Definition: RAxis.hxx:263
LabelsCmpFlags CompareBinLabels(const RAxisLabels &other) const noexcept
Compare the labels of this axis with those of another axis.
Definition: RAxis.hxx:819
virtual double GetBinCenter(int bin) const =0
Get the bin center for the given bin index.
LabelsCmpFlags
Result of an RAxisLabels label set comparison.
Definition: RAxis.hxx:804
double GetBinTo(int bin) const
Get the high bin border ("right edge") for the given bin index.
Definition: RAxis.hxx:317
virtual double GetBinFrom(int bin) const =0
Get the low bin border ("left edge") for the given bin index.
The source axis and target axis have different binning.
Random const_iterator through bins.
Definition: RAxis.hxx:134
const_iterator begin() const noexcept
Get a const_iterator pointing to the first non-underflow bin.
Definition: RAxis.hxx:289
friend bool operator!=(const_iterator lhs, const_iterator rhs) noexcept
i != j
Definition: RAxis.hxx:381
int FindBin(double x) const noexcept final override
Find the bin index corresponding to coordinate x.
Definition: RAxis.hxx:656
#define R__ERROR_HERE(GROUP)
Definition: RLogger.hxx:183
RAxisBase & operator=(const RAxisBase &)=default
int operator-(const const_iterator &j) noexcept
Definition: RAxis.hxx:208
static constexpr const Grow_t Grow
Tag signalling that an axis should be able to grow; used for calling the appropriate constructor like...
Definition: RAxisConfig.hxx:72
int GetNBinsNoOver() const noexcept final override
Get the number of bins, excluding under- and overflow.
Definition: RAxis.hxx:651
bool operator<=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i <= j
Definition: RAxis.hxx:363
int fCursor
Current iteration position.
Definition: RAxis.hxx:136
const Int_t n
Definition: legend1.C:16
int FindBinByName(const std::string &label)
Get the bin index with label.
Definition: RAxis.hxx:776
Axis with equidistant bin borders.
Definition: RAxis.hxx:393
RAxisLabels(const std::vector< std::string_view > &labels)
Construct a RAxisLables from a vector of string_views.
Definition: RAxis.hxx:767
virtual int GetBinIndexForLowEdge(double x) const noexcept=0
If the coordinate x is within 10 ULPs of a bin low edge coordinate, return the bin for which this is ...
std::vector< std::string_view > GetBinLabels() const
Build a vector of labels. The position in the vector defines the label&#39;s bin.
Definition: RAxis.hxx:795