File indexing completed on 2025-01-18 10:10:36
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #ifndef ROOT7_RAxis
0017 #define ROOT7_RAxis
0018
0019 #include <algorithm>
0020 #include <cmath>
0021 #include <limits>
0022 #include <string>
0023 #include <unordered_map>
0024 #include <vector>
0025
0026 #include "ROOT/RAxisConfig.hxx"
0027 #include <string_view>
0028 #include "ROOT/RLogger.hxx"
0029
0030 namespace ROOT {
0031 namespace Experimental {
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 class RAxisBase {
0045 protected:
0046
0047
0048
0049
0050 RAxisBase(const RAxisBase &) = default;
0051 RAxisBase(RAxisBase &&) = default;
0052 RAxisBase &operator=(const RAxisBase &) = default;
0053 RAxisBase &operator=(RAxisBase &&) = default;
0054
0055
0056
0057 RAxisBase() noexcept(noexcept(std::string())) = default;
0058
0059
0060 virtual ~RAxisBase();
0061
0062
0063
0064
0065 RAxisBase(std::string_view title) noexcept: fTitle(title) {}
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075 int AdjustOverflowBinNumber(double rawbin) const
0076 {
0077 ++rawbin;
0078
0079
0080 if (rawbin < GetFirstBin())
0081 return CanGrow() ? kInvalidBin : GetUnderflowBin();
0082
0083
0084
0085 if (rawbin >= GetLastBin() + 1)
0086 return CanGrow() ? kInvalidBin : GetOverflowBin();
0087
0088
0089 return (int)rawbin;
0090 }
0091
0092
0093
0094
0095
0096
0097
0098 virtual bool HasSameBinBordersAs(const RAxisBase& other) const {
0099
0100 if (CanGrow() != other.CanGrow())
0101 return false;
0102
0103
0104 if (GetNBinsNoOver() != other.GetNBinsNoOver())
0105 return false;
0106
0107
0108 for (int bin: *this)
0109 if (GetBinFrom(bin) != other.GetBinFrom(bin))
0110 return false;
0111
0112
0113 if (GetMaximum() != other.GetMaximum())
0114 return false;
0115
0116
0117 return true;
0118 }
0119
0120 public:
0121
0122
0123
0124
0125
0126 class const_iterator {
0127 int fCursor = 0;
0128
0129 public:
0130 using iterator_category = std::random_access_iterator_tag;
0131 using value_type = int;
0132 using difference_type = int;
0133 using pointer = const int *;
0134 using reference = const int &;
0135
0136 const_iterator() = default;
0137
0138
0139 explicit const_iterator(int cursor) noexcept: fCursor(cursor) {}
0140
0141
0142 const_iterator &operator++() noexcept
0143 {
0144
0145 ++fCursor;
0146 return *this;
0147 }
0148
0149
0150 const_iterator &operator--() noexcept
0151 {
0152
0153 --fCursor;
0154 return *this;
0155 }
0156
0157
0158 const_iterator operator++(int)noexcept
0159 {
0160 const_iterator old(*this);
0161 ++(*this);
0162 return old;
0163 }
0164
0165
0166 const_iterator operator--(int)noexcept
0167 {
0168 const_iterator old(*this);
0169 --(*this);
0170 return old;
0171 }
0172
0173
0174 const_iterator &operator+=(int d) noexcept
0175 {
0176 fCursor += d;
0177 return *this;
0178 }
0179
0180
0181 const_iterator &operator-=(int d) noexcept
0182 {
0183 fCursor -= d;
0184 return *this;
0185 }
0186
0187
0188 const_iterator operator+(int d) noexcept
0189 {
0190 const_iterator ret(*this);
0191 ret += d;
0192 return ret;
0193 }
0194 friend const_iterator operator+(int d, const_iterator rhs) noexcept;
0195
0196
0197 const_iterator operator-(int d) noexcept
0198 {
0199 const_iterator ret(*this);
0200 ret -= d;
0201 return ret;
0202 }
0203
0204
0205 int operator-(const const_iterator& j) noexcept
0206 {
0207 return fCursor - j.fCursor;
0208 }
0209
0210
0211 int operator[](int d) noexcept
0212 {
0213 return fCursor + d;
0214 }
0215
0216
0217 int operator*() const noexcept { return fCursor; }
0218
0219
0220 const int *operator->() const noexcept { return &fCursor; }
0221
0222 friend bool operator<(const_iterator lhs, const_iterator rhs) noexcept;
0223 friend bool operator>(const_iterator lhs, const_iterator rhs) noexcept;
0224 friend bool operator<=(const_iterator lhs, const_iterator rhs) noexcept;
0225 friend bool operator>=(const_iterator lhs, const_iterator rhs) noexcept;
0226 friend bool operator==(const_iterator lhs, const_iterator rhs) noexcept;
0227 friend bool operator!=(const_iterator lhs, const_iterator rhs) noexcept;
0228 };
0229
0230
0231 constexpr static const int kInvalidBin = 0;
0232
0233
0234 constexpr static const int kUnderflowBin = -1;
0235
0236
0237 constexpr static const int kOverflowBin = -2;
0238
0239
0240 const std::string &GetTitle() const { return fTitle; }
0241
0242
0243 virtual bool CanGrow() const noexcept = 0;
0244
0245
0246 virtual int GetNBinsNoOver() const noexcept = 0;
0247
0248
0249 int GetNBins() const noexcept { return GetNBinsNoOver() + GetNOverflowBins(); }
0250
0251
0252 int GetNOverflowBins() const noexcept
0253 {
0254 if (CanGrow())
0255 return 0;
0256 else
0257 return 2;
0258 };
0259
0260
0261
0262 int GetUnderflowBin() const noexcept {
0263 if (CanGrow())
0264 return kInvalidBin;
0265 else
0266 return kUnderflowBin;
0267 }
0268
0269
0270
0271 int GetOverflowBin() const noexcept {
0272 if (CanGrow())
0273 return kInvalidBin;
0274 else
0275 return kOverflowBin;
0276 }
0277
0278
0279 int GetFirstBin() const noexcept { return 1; }
0280
0281
0282 int GetLastBin() const noexcept { return GetNBinsNoOver(); }
0283
0284
0285
0286
0287
0288 const_iterator begin() const noexcept { return const_iterator{GetFirstBin()}; }
0289
0290
0291 const_iterator end() const noexcept { return const_iterator{GetLastBin() + 1}; }
0292
0293
0294
0295
0296
0297
0298 virtual int FindBin(double x) const noexcept = 0;
0299
0300
0301
0302 virtual double GetBinCenter(int bin) const = 0;
0303
0304
0305
0306 virtual double GetBinFrom(int bin) const = 0;
0307
0308
0309
0310 double GetBinTo(int bin) const {
0311 const double result = (bin == kUnderflowBin) ? GetMinimum() : GetBinFrom(bin + 1);
0312 return result;
0313 }
0314
0315
0316 double GetMinimum() const { return GetBinFrom(GetFirstBin()); }
0317
0318
0319 double GetMaximum() const { return GetBinTo(GetLastBin()); }
0320
0321
0322
0323
0324
0325
0326 bool HasSameBinningAs(const RAxisBase& other) const;
0327
0328
0329
0330
0331 virtual int GetBinIndexForLowEdge(double x) const noexcept = 0;
0332
0333 private:
0334 std::string fTitle;
0335 };
0336
0337
0338
0339
0340
0341 inline RAxisBase::const_iterator operator+(int d, RAxisBase::const_iterator rhs) noexcept
0342 {
0343 return rhs + d;
0344 }
0345
0346
0347 inline bool operator<(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
0348 {
0349 return lhs.fCursor < rhs.fCursor;
0350 }
0351
0352
0353 inline bool operator>(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
0354 {
0355 return lhs.fCursor > rhs.fCursor;
0356 }
0357
0358
0359 inline bool operator<=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
0360 {
0361 return lhs.fCursor <= rhs.fCursor;
0362 }
0363
0364
0365 inline bool operator>=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
0366 {
0367 return lhs.fCursor >= rhs.fCursor;
0368 }
0369
0370
0371 inline bool operator==(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
0372 {
0373 return lhs.fCursor == rhs.fCursor;
0374 }
0375
0376
0377 inline bool operator!=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
0378 {
0379 return lhs.fCursor != rhs.fCursor;
0380 }
0381
0382
0383
0384
0385
0386
0387
0388
0389 class RAxisEquidistant: public RAxisBase {
0390 protected:
0391 double fLow = 0.;
0392 double fInvBinWidth = 0.;
0393 unsigned int fNBinsNoOver;
0394
0395
0396
0397
0398
0399 static double GetInvBinWidth(int nbinsNoOver, double lowOrHigh, double highOrLow)
0400 {
0401 return nbinsNoOver / std::fabs(highOrLow - lowOrHigh);
0402 }
0403
0404
0405 bool HasSameBinBordersAs(const RAxisBase& other) const override;
0406
0407
0408
0409
0410
0411 double FindBinRaw(double x) const noexcept
0412 {
0413 return (x - fLow) * fInvBinWidth;
0414 }
0415
0416 public:
0417 RAxisEquidistant() = default;
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427 explicit RAxisEquidistant(std::string_view title, int nbinsNoOver, double low, double high) noexcept
0428 : RAxisBase(title)
0429 , fLow(low)
0430 , fInvBinWidth(GetInvBinWidth(nbinsNoOver, low, high))
0431 , fNBinsNoOver(nbinsNoOver)
0432 {}
0433
0434
0435
0436
0437
0438
0439
0440
0441 explicit RAxisEquidistant(int nbinsNoOver, double low, double high) noexcept
0442 : RAxisEquidistant("", nbinsNoOver, low, high)
0443 {}
0444
0445
0446 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetNBinsNoOver(), GetMinimum(), GetMaximum()); }
0447
0448
0449 int GetNBinsNoOver() const noexcept final { return fNBinsNoOver; }
0450
0451
0452
0453
0454
0455 int FindBin(double x) const noexcept final
0456 {
0457 double rawbin = FindBinRaw(x);
0458 return AdjustOverflowBinNumber(rawbin);
0459 }
0460
0461
0462 bool CanGrow() const noexcept override { return false; }
0463
0464
0465 double GetBinWidth() const noexcept { return 1. / fInvBinWidth; }
0466
0467
0468 double GetInverseBinWidth() const noexcept { return fInvBinWidth; }
0469
0470
0471
0472
0473
0474 double GetBinCenter(int bin) const final { return fLow + (bin - GetFirstBin() + 0.5) / fInvBinWidth; }
0475
0476
0477
0478
0479
0480 double GetBinFrom(int bin) const final {
0481 const double result = (bin == kOverflowBin) ? GetMaximum() : fLow + (bin - GetFirstBin()) / fInvBinWidth;
0482 return result;
0483 }
0484
0485
0486
0487
0488 int GetBinIndexForLowEdge(double x) const noexcept final ;
0489 };
0490
0491 namespace Internal {
0492
0493 template <>
0494 struct AxisConfigToType<RAxisConfig::kEquidistant> {
0495 using Axis_t = RAxisEquidistant;
0496
0497 Axis_t operator()(const RAxisConfig &cfg) noexcept
0498 {
0499 return RAxisEquidistant(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
0500 }
0501 };
0502
0503 }
0504
0505
0506
0507
0508
0509 class RAxisGrow: public RAxisEquidistant {
0510 public:
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521 explicit RAxisGrow(std::string_view title, int nbins, double low, double high) noexcept
0522 : RAxisEquidistant(title, nbins, low, high)
0523 {}
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534 explicit RAxisGrow(int nbins, double low, double high) noexcept: RAxisGrow("", nbins, low, high) {}
0535
0536
0537 operator RAxisConfig() const { return RAxisConfig(GetTitle(), RAxisConfig::Grow, GetNBinsNoOver(), GetMinimum(), GetMaximum()); }
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551
0552
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563 int Grow(int toBin);
0564
0565
0566 bool CanGrow() const noexcept final { return true; }
0567 };
0568
0569 namespace Internal {
0570
0571 template <>
0572 struct AxisConfigToType<RAxisConfig::kGrow> {
0573 using Axis_t = RAxisGrow;
0574
0575 Axis_t operator()(const RAxisConfig &cfg) noexcept
0576 {
0577 return RAxisGrow(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
0578 }
0579 };
0580
0581 }
0582
0583
0584
0585
0586
0587
0588
0589
0590
0591
0592
0593
0594
0595 class RAxisIrregular: public RAxisBase {
0596 private:
0597
0598 std::vector<double> fBinBorders;
0599
0600 protected:
0601
0602 bool HasSameBinBordersAs(const RAxisBase& other) const override;
0603
0604
0605
0606
0607
0608 double FindBinRaw(double x) const noexcept
0609 {
0610 const auto bBegin = fBinBorders.begin();
0611 const auto bEnd = fBinBorders.end();
0612
0613 auto iNotLess = std::lower_bound(bBegin, bEnd, x);
0614 return iNotLess - bBegin;
0615 }
0616
0617 public:
0618 RAxisIrregular() = default;
0619
0620
0621
0622 explicit RAxisIrregular(const std::vector<double> &binborders)
0623 : RAxisBase(), fBinBorders(binborders)
0624 {
0625 #ifdef R__DO_RANGE_CHECKS
0626 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
0627 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
0628 #endif
0629 }
0630
0631
0632
0633
0634
0635 explicit RAxisIrregular(std::vector<double> &&binborders) noexcept
0636 : RAxisBase(), fBinBorders(std::move(binborders))
0637 {
0638 #ifdef R__DO_RANGE_CHECKS
0639 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
0640 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
0641 #endif
0642 }
0643
0644
0645
0646 explicit RAxisIrregular(std::string_view title, const std::vector<double> &binborders)
0647 : RAxisBase(title), fBinBorders(binborders)
0648 {
0649 #ifdef R__DO_RANGE_CHECKS
0650 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
0651 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
0652 #endif
0653 }
0654
0655
0656
0657
0658
0659 explicit RAxisIrregular(std::string_view title, std::vector<double> &&binborders) noexcept
0660 : RAxisBase(title), fBinBorders(std::move(binborders))
0661 {
0662 #ifdef R__DO_RANGE_CHECKS
0663 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
0664 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
0665 #endif
0666 }
0667
0668
0669 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinBorders()); }
0670
0671
0672 int GetNBinsNoOver() const noexcept final { return fBinBorders.size() - 1; }
0673
0674
0675
0676
0677 int FindBin(double x) const noexcept final
0678 {
0679 int rawbin = FindBinRaw(x);
0680
0681
0682 if (rawbin < GetFirstBin())
0683 return kUnderflowBin;
0684 if (rawbin >= GetLastBin() + 1)
0685 return kOverflowBin;
0686 return rawbin;
0687 }
0688
0689
0690
0691 double GetBinCenter(int bin) const final { return 0.5 * (fBinBorders[bin - 1] + fBinBorders[bin]); }
0692
0693
0694
0695 double GetBinFrom(int bin) const final
0696 {
0697 if (bin == kOverflowBin)
0698 return fBinBorders[GetLastBin()];
0699 return fBinBorders[bin - 1];
0700 }
0701
0702
0703
0704
0705 int GetBinIndexForLowEdge(double x) const noexcept final ;
0706
0707
0708 bool CanGrow() const noexcept final { return false; }
0709
0710
0711 const std::vector<double> &GetBinBorders() const noexcept { return fBinBorders; }
0712 };
0713
0714 namespace Internal {
0715
0716 template <>
0717 struct AxisConfigToType<RAxisConfig::kIrregular> {
0718 using Axis_t = RAxisIrregular;
0719
0720 Axis_t operator()(const RAxisConfig &cfg) { return RAxisIrregular(cfg.GetTitle(), cfg.GetBinBorders()); }
0721 };
0722
0723 }
0724
0725
0726
0727
0728
0729
0730
0731
0732
0733
0734
0735
0736
0737
0738
0739
0740
0741
0742 class RAxisLabels: public RAxisGrow {
0743 private:
0744
0745 std::unordered_map<std::string, int > fLabelsIndex;
0746
0747 public:
0748
0749 explicit RAxisLabels(std::string_view title, const std::vector<std::string_view> &labels)
0750 : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
0751 {
0752 for (size_t i = 0, n = labels.size(); i < n; ++i)
0753 fLabelsIndex[std::string(labels[i])] = i;
0754 }
0755
0756
0757 explicit RAxisLabels(std::string_view title, const std::vector<std::string> &labels)
0758 : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
0759 {
0760 for (size_t i = 0, n = labels.size(); i < n; ++i)
0761 fLabelsIndex[labels[i]] = i;
0762 }
0763
0764
0765 explicit RAxisLabels(const std::vector<std::string_view> &labels): RAxisLabels("", labels) {}
0766
0767
0768 explicit RAxisLabels(const std::vector<std::string> &labels): RAxisLabels("", labels) {}
0769
0770
0771 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinLabels()); }
0772
0773
0774 int FindBinByName(const std::string &label)
0775 {
0776 auto insertResult = fLabelsIndex.insert({label, -1});
0777 if (insertResult.second) {
0778
0779 int idx = fLabelsIndex.size() - 1;
0780 insertResult.first->second = idx;
0781 return idx;
0782 }
0783 return insertResult.first->second;
0784 }
0785
0786
0787 double GetBinCenterByName(const std::string &label)
0788 {
0789 return FindBinByName(label) + 0.5;
0790 }
0791
0792
0793 std::vector<std::string_view> GetBinLabels() const
0794 {
0795 std::vector<std::string_view> vec(fLabelsIndex.size());
0796 for (const auto &kv: fLabelsIndex)
0797 vec.at(kv.second) = kv.first;
0798 return vec;
0799 }
0800
0801
0802 enum LabelsCmpFlags {
0803
0804 kLabelsCmpSame = 0,
0805
0806
0807 kLabelsCmpSubset = 0b1,
0808
0809
0810 kLabelsCmpSuperset = 0b10,
0811
0812
0813 kLabelsCmpDisordered = 0b100,
0814 };
0815
0816
0817 LabelsCmpFlags CompareBinLabels(const RAxisLabels& other) const noexcept {
0818
0819 LabelsCmpFlags result = kLabelsCmpSame;
0820 size_t missing_in_other = 0;
0821
0822
0823 for (const auto &kv: fLabelsIndex) {
0824 auto iter = other.fLabelsIndex.find(kv.first);
0825 if (iter == other.fLabelsIndex.cend()) {
0826 ++missing_in_other;
0827 } else if (iter->second != kv.second) {
0828 result = LabelsCmpFlags(result | kLabelsCmpDisordered);
0829 }
0830 }
0831 if (missing_in_other > 0)
0832 result = LabelsCmpFlags(result | kLabelsCmpSubset);
0833
0834
0835 if (fLabelsIndex.size() == other.fLabelsIndex.size() + missing_in_other)
0836 return result;
0837
0838
0839 for (const auto &kv: other.fLabelsIndex)
0840 if (fLabelsIndex.find(kv.first) == fLabelsIndex.cend())
0841 return LabelsCmpFlags(result | kLabelsCmpSuperset);
0842 return result;
0843 }
0844 };
0845
0846 namespace Internal {
0847
0848 template <>
0849 struct AxisConfigToType<RAxisConfig::kLabels> {
0850 using Axis_t = RAxisLabels;
0851
0852 Axis_t operator()(const RAxisConfig &cfg) { return RAxisLabels(cfg.GetTitle(), cfg.GetBinLabels()); }
0853 };
0854
0855 }
0856
0857
0858
0859 enum class EAxisCompatibility {
0860 kIdentical,
0861
0862 kContains,
0863
0864
0865
0866
0867
0868
0869 kSampling,
0870
0871
0872
0873 kIncompatible
0874 };
0875
0876
0877 EAxisCompatibility CanMap(const RAxisEquidistant &target, const RAxisEquidistant &source) noexcept;
0878
0879
0880 }
0881 }
0882
0883 #endif