File indexing completed on 2025-12-13 10:32:13
0001
0002
0003
0004
0005
0006 #ifndef YODA_BINNING_H
0007 #define YODA_BINNING_H
0008 #include "YODA/BinnedAxis.h"
0009 #include "YODA/Utils/MetaUtils.h"
0010 #include <memory>
0011 #include <tuple>
0012 #include <vector>
0013 #include <array>
0014
0015 namespace YODA {
0016
0017
0018 template<typename CoordT>
0019 double nullifyIfDiscCoord(CoordT&& , std::false_type, double null = 0.0) {
0020 return null;
0021 }
0022
0023 template<typename CoordT>
0024 double nullifyIfDiscCoord(CoordT&& coord, std::true_type, double = 0.0) {
0025 return std::forward<CoordT>(coord);
0026 }
0027
0028
0029 template<typename... Args>
0030 inline bool containsNan(const std::tuple<Args...>& coords) {
0031 std::array<size_t, sizeof...(Args)> hasNan{};
0032 auto checkCoords = [&hasNan, &coords](auto I) {
0033 using isContinuous = typename std::is_floating_point<typename std::tuple_element_t<I, std::tuple<Args...>>>;
0034
0035 hasNan[I] = size_t(std::isnan(nullifyIfDiscCoord(std::move(std::get<I>(coords)),
0036 std::integral_constant<bool,
0037 isContinuous::value>{})));
0038 };
0039
0040 MetaUtils::staticFor<sizeof...(Args)>(checkCoords);
0041
0042 return std::any_of(hasNan.begin(), hasNan.end(), [ ](const bool i) { return i; } );
0043 }
0044
0045
0046
0047 template <typename... Axes>
0048 class Binning {
0049 private:
0050
0051 std::tuple<Axes...> _axes;
0052 size_t _dim = sizeof...(Axes);
0053 std::vector<size_t> _maskedIndices;
0054
0055
0056 protected:
0057
0058
0059
0060
0061 using IndexArr = std::array<size_t, sizeof...(Axes)>;
0062
0063
0064
0065 public:
0066
0067
0068
0069
0070 template <size_t I>
0071 using getAxisT = std::tuple_element_t<I, std::tuple<Axes...>>;
0072
0073 template <size_t I>
0074 using getEdgeT = typename getAxisT<I>::EdgeT;
0075
0076 template <size_t I>
0077 using is_CAxis = typename std::is_floating_point<getEdgeT<I>>;
0078
0079 template<size_t I>
0080 using is_Arithmetic = typename std::is_arithmetic<getEdgeT<I>>;
0081
0082 using all_CAxes = typename std::conjunction<std::is_floating_point<typename Axes::EdgeT>...>;
0083
0084 using EdgeTypesTuple = decltype(
0085 std::tuple_cat(std::declval<std::tuple<typename Axes::EdgeT>>()...));
0086
0087 using Dimension = std::integral_constant<size_t, sizeof...(Axes)>;
0088
0089
0090
0091
0092
0093
0094
0095 Binning() { }
0096
0097
0098 Binning(const std::initializer_list<typename Axes::EdgeT>&... edges)
0099 : _axes(Axes(edges)...) {
0100 updateMaskedBins();
0101 }
0102
0103
0104 Binning(std::initializer_list<typename Axes::EdgeT>&&... edges)
0105 : _axes(Axes(std::move(edges))...) {
0106 updateMaskedBins();
0107 }
0108
0109
0110 Binning(const Axes&... axes)
0111 : _axes(axes...) {
0112 updateMaskedBins();
0113 }
0114
0115
0116 Binning(Axes&&... axes)
0117 : _axes(std::move(axes)...) {
0118 updateMaskedBins();
0119 }
0120
0121
0122 Binning(const Binning& other) : _axes(other._axes), _maskedIndices(other._maskedIndices) {}
0123
0124
0125 Binning(Binning&& other) : _axes(std::move(other._axes)), _maskedIndices(std::move(other._maskedIndices)) {}
0126
0127
0128 Binning& operator=(const Binning& other) {
0129 if (this != &other) {
0130 _axes = other._axes;
0131 _maskedIndices = other._maskedIndices;
0132 }
0133 return *this;
0134 }
0135
0136
0137 Binning& operator=(Binning&& other) {
0138 if (this != &other) {
0139 _axes = std::move(other._axes);
0140 _maskedIndices = std::move(other._maskedIndices);
0141 }
0142 return *this;
0143 }
0144
0145
0146
0147
0148
0149
0150
0151 size_t localToGlobalIndex(const IndexArr& localIndices) const;
0152
0153
0154 IndexArr globalToLocalIndices(size_t globalIndex) const;
0155
0156
0157 IndexArr localIndicesAt(const EdgeTypesTuple& coords) const;
0158
0159
0160 size_t globalIndexAt(const EdgeTypesTuple& coords) const;
0161
0162
0163 std::vector<size_t> calcOverflowBinsIndices() const noexcept;
0164
0165
0166 size_t countOverflowBins(const size_t axisN) const noexcept;
0167
0168
0169 std::vector<size_t> maskedBins() const noexcept { return _maskedIndices; }
0170
0171
0172 void clearMaskedBins() noexcept { _maskedIndices.clear(); }
0173
0174
0175 void maskBin(const size_t index, const bool status = true);
0176
0177
0178 void maskBin(const IndexArr& localIndices, const bool status = true);
0179
0180
0181 void maskBinAt(const EdgeTypesTuple& coords, const bool status = true) noexcept;
0182
0183
0184 void maskBins(const std::vector<size_t>& indices, const bool status = true);
0185
0186
0187 void maskSlice(const size_t dim, const size_t idx, const bool status = true);
0188
0189
0190 bool isMasked(const size_t i) const noexcept;
0191
0192
0193 bool isVisible(const size_t i) const noexcept;
0194
0195
0196 size_t calcSliceSize(const size_t pivotAxisN) const noexcept;
0197
0198
0199
0200
0201 std::vector<size_t> sliceIndices(std::vector<std::pair<size_t, std::vector<size_t>>>) const noexcept;
0202
0203
0204
0205 std::vector<size_t> sliceIndices(size_t axisN, size_t binN) const noexcept;
0206
0207
0208
0209 void updateMaskedBins() noexcept {
0210
0211 std::vector<std::pair<size_t, std::vector<size_t>>> slicePivots;
0212 auto extractMaskedBins = [&slicePivots, &axes = _axes](auto I) {
0213 using isContinuous = typename Binning::template is_CAxis<I>;
0214
0215 if constexpr(isContinuous::value) {
0216 const auto& axis = std::get<I>(axes);
0217 slicePivots.push_back({I, {axis.maskedBins()} });
0218 }
0219 };
0220
0221
0222 MetaUtils::staticFor<Dimension::value>(extractMaskedBins);
0223
0224
0225 _maskedIndices = sliceIndices(slicePivots);
0226 std::sort(_maskedIndices.begin(), _maskedIndices.end());
0227 _maskedIndices.erase( std::unique(_maskedIndices.begin(), _maskedIndices.end()),
0228 _maskedIndices.end() );
0229 }
0230
0231
0232
0233
0234
0235
0236 template <size_t... AxisNs>
0237 void mergeBins(
0238 std::decay_t<decltype(AxisNs, std::declval<std::pair<size_t, size_t>>())>... mergeRanges) noexcept;
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248 bool isCompatible(const Binning<Axes...>& other) const noexcept;
0249
0250
0251
0252
0253
0254
0255
0256 template <size_t I>
0257 const getAxisT<I>& axis() const noexcept;
0258
0259
0260 template <size_t I>
0261 getAxisT<I>& axis() noexcept;
0262
0263 template<size_t I>
0264 auto _getAxesExcept() const noexcept;
0265
0266
0267 IndexArr _getAxesSizes(const bool includeOverflows = true) const noexcept;
0268
0269
0270 size_t dim() const noexcept;
0271
0272
0273 size_t numBins(const bool includeOverflows = true, const bool includeMaskedBins = true) const noexcept;
0274
0275
0276 size_t numBinsAt(const size_t axisN, const bool includeOverflows = true) const noexcept;
0277
0278
0279 EdgeTypesTuple edgeTuple(const size_t index) const noexcept;
0280
0281
0282
0283
0284
0285 template <size_t I, typename E = getEdgeT<I>>
0286 std::vector<E> edges(const bool includeOverflows = false) const noexcept {
0287 const auto& axis = std::get<I>(_axes);
0288 if (!includeOverflows && Binning::template is_CAxis<I>::value) {
0289 auto&& all_edges = axis.edges();
0290 typename std::vector<E> rtn;
0291 const size_t offset = all_edges.size() - 1;
0292 rtn.insert(rtn.end(), std::make_move_iterator(all_edges.begin()+1),
0293 std::make_move_iterator(all_edges.begin()+offset));
0294 return rtn;
0295 }
0296 return axis.edges();
0297 }
0298
0299
0300
0301
0302
0303
0304
0305 template <size_t I, typename E = getEdgeT<I>>
0306 std::enable_if_t<std::is_floating_point<E>::value, std::vector<E>>
0307 widths(const bool includeOverflows = false) const noexcept {
0308 const auto& axis = std::get<I>(_axes);
0309 return axis.widths(includeOverflows);
0310 }
0311
0312
0313
0314
0315 template <size_t I, typename E = getEdgeT<I>>
0316 std::enable_if_t<std::is_floating_point<E>::value, E> min() const noexcept {
0317 const auto& axis = std::get<I>(_axes);
0318 assert(axis.numBins(true) > 2);
0319 return axis.min(1);
0320 }
0321
0322
0323
0324
0325 template <size_t I, typename E = getEdgeT<I>>
0326 std::enable_if_t<std::is_floating_point<E>::value, E> max() const noexcept {
0327 const auto& axis = std::get<I>(_axes);
0328 assert(axis.numBins(true) > 2);
0329 return axis.min(axis.numBins(true)-1);
0330 }
0331
0332
0333 double dVol(const size_t index) const;
0334
0335
0336
0337
0338
0339
0340
0341 void _renderYODA(std::ostream& os) const noexcept {
0342
0343 auto edgePrinter = [&](auto I) {
0344 const auto& axis = std::get<I>(_axes);
0345 if (axis.numBins()) {
0346 os << "Edges(A"+ std::to_string(I+1) + "): ";
0347 axis._renderYODA(os);
0348 os << "\n";
0349 }
0350 };
0351
0352 MetaUtils::staticFor<Dimension::value>(edgePrinter);
0353
0354
0355 if (_maskedIndices.size()) {
0356 std::vector<size_t> gaps(_maskedIndices.size());
0357 std::partial_sort_copy(begin(_maskedIndices), end(_maskedIndices), begin(gaps), end(gaps));
0358
0359 os << "MaskedBins: [";
0360 for (size_t i = 0; i < gaps.size(); ++i) {
0361 if (i) os << ", ";
0362 os << std::to_string(gaps[i]);
0363 }
0364 os << "]\n";
0365 }
0366 }
0367
0368
0369 void _renderYODA(std::ostream& os, const size_t idx, const int width = 13) const {
0370 IndexArr localIndices = globalToLocalIndices(idx);
0371
0372 auto edgePrinter = [&](auto I) {
0373 const auto& axis = std::get<I>(_axes);
0374 axis._renderYODA(os, localIndices[I], width);
0375 };
0376
0377 MetaUtils::staticFor<Dimension::value>(edgePrinter);
0378 }
0379
0380
0381
0382 };
0383
0384
0385 template<typename... Axes>
0386 size_t Binning<Axes...>::localToGlobalIndex(const IndexArr& localIndices) const {
0387 size_t gIndex = 0;
0388
0389
0390
0391
0392
0393
0394
0395 IndexArr axesSizes = _getAxesSizes();
0396 for (size_t iIndex = 0; iIndex < localIndices.size(); ++iIndex) {
0397 size_t productOfBinSizes = 1;
0398 for (ssize_t iBinnedAxis = iIndex - 1; iBinnedAxis >= 0; --iBinnedAxis) {
0399 productOfBinSizes *= (axesSizes[iBinnedAxis]);
0400 }
0401 gIndex += localIndices[iIndex] * productOfBinSizes;
0402 }
0403 return gIndex;
0404 }
0405
0406 template <typename... Axes>
0407 typename Binning<Axes...>::IndexArr
0408 Binning<Axes...>::globalToLocalIndices(size_t globalIndex) const {
0409 if (globalIndex >= numBins(true, true)) throw RangeError("Global index outside bin range");
0410 IndexArr localIndices{};
0411
0412 IndexArr axesSizes = _getAxesSizes();
0413
0414 for (ssize_t iIndex = localIndices.size()-1 ; iIndex >= 0; --iIndex){
0415 size_t productOfNestedBinSizes = 1;
0416
0417 for (ssize_t iBinnedAxis = iIndex - 1; iBinnedAxis >= 0; --iBinnedAxis) {
0418 productOfNestedBinSizes *= (axesSizes[iBinnedAxis]);
0419 }
0420
0421
0422 localIndices[iIndex] = globalIndex / productOfNestedBinSizes;
0423
0424 globalIndex = globalIndex % productOfNestedBinSizes;
0425 }
0426 return localIndices;
0427 }
0428
0429 template<typename... Axes>
0430 typename Binning<Axes...>::IndexArr
0431 Binning<Axes...>::localIndicesAt(const EdgeTypesTuple& coords) const {
0432 IndexArr localIndices{};
0433 auto extractIndices = [&localIndices, &coords, &axes = _axes](auto I) {
0434 const auto& coord = std::get<I>(coords);
0435 const auto& axis = std::get<I>(axes);
0436
0437 localIndices[I] = axis.index(coord);
0438 };
0439
0440 MetaUtils::staticFor<Dimension::value>(extractIndices);
0441
0442 return localIndices;
0443 }
0444
0445 template<typename... Axes>
0446 size_t Binning<Axes...>::globalIndexAt(const EdgeTypesTuple& coords) const {
0447 return localToGlobalIndex(localIndicesAt(coords));
0448 }
0449
0450 template<typename... Axes>
0451 std::vector<size_t> Binning<Axes...>::calcOverflowBinsIndices() const noexcept {
0452 IndexArr axesSizes = _getAxesSizes();
0453 std::vector<bool> isCAxis;
0454
0455 auto getCAxisIndices = [&isCAxis](auto I){
0456 using isContinuous = typename Binning::template is_CAxis<I>;
0457 isCAxis.emplace_back(isContinuous::value);
0458 };
0459
0460 MetaUtils::staticFor<Dimension::value>(getCAxisIndices);
0461
0462 std::vector<std::pair<size_t, std::vector<size_t>>> slicePivots;
0463 slicePivots.reserve(isCAxis.size());
0464
0465 for (size_t axisN = 0; axisN < isCAxis.size(); ++axisN) {
0466 if (isCAxis[axisN])
0467 slicePivots.push_back({axisN, {0, axesSizes[axisN]-1} });
0468 else
0469 slicePivots.push_back({axisN, {0} });
0470 }
0471
0472 std::vector<size_t> res = sliceIndices(slicePivots);
0473 std::sort(res.begin(), res.end());
0474 res.erase( std::unique(res.begin(), res.end()), res.end() );
0475 return res;
0476 }
0477
0478
0479
0480
0481
0482
0483
0484 template<typename... Axes>
0485 size_t Binning<Axes...>::countOverflowBins(const size_t axisN) const noexcept {
0486 std::vector<bool> CAxisIndices;
0487
0488 auto getCAxisIndices = [&CAxisIndices](auto I){
0489 using isContinuous = typename Binning::template is_CAxis<I>;
0490 CAxisIndices.emplace_back(isContinuous::value);
0491 };
0492
0493 MetaUtils::staticFor<Dimension::value>(getCAxisIndices);
0494 return CAxisIndices.at(axisN) + 1;
0495 }
0496
0497
0498
0499
0500
0501
0502
0503
0504
0505
0506
0507
0508
0509
0510
0511 template<typename... Axes>
0512 void Binning<Axes...>::maskBin(const size_t index, const bool status) {
0513 Binning<Axes...>::maskBins({{index}}, status);
0514 }
0515
0516 template<typename... Axes>
0517 void Binning<Axes...>::maskBin(const IndexArr& localIndices, const bool status) {
0518 const size_t gIndex = Binning<Axes...>::localToGlobalIndex(localIndices);
0519 Binning<Axes...>::maskBins({{gIndex}}, status);
0520 }
0521
0522 template<typename... Axes>
0523 void Binning<Axes...>::maskBinAt(const EdgeTypesTuple& coords, const bool status) noexcept {
0524 const size_t gIndex = Binning<Axes...>::globalIndexAt(coords);
0525 Binning<Axes...>::maskBins({{gIndex}}, status);
0526 }
0527
0528 template<typename... Axes>
0529 void Binning<Axes...>::maskBins(const std::vector<size_t>& indices, const bool status) {
0530 for (size_t i : indices) {
0531 const auto& itEnd = this->_maskedIndices.cend();
0532 const auto& res = std::find(this->_maskedIndices.cbegin(), itEnd, i);
0533 if (status && res == itEnd) this->_maskedIndices.push_back(i);
0534 else if (!status && res != itEnd) this->_maskedIndices.erase(res);
0535 }
0536 }
0537
0538 template <typename... Axes>
0539 void Binning<Axes...>::maskSlice(const size_t dim, const size_t idx, const bool status) {
0540
0541 std::vector<std::pair<size_t, std::vector<size_t>>> slicePivot{{dim,{idx}}};
0542 std::vector<size_t> indices = Binning<Axes...>::sliceIndices(slicePivot);
0543 Binning<Axes...>::maskBins(indices, status);
0544 }
0545
0546 template<typename... Axes>
0547 bool Binning<Axes...>::isMasked(const size_t i) const noexcept {
0548 const auto& itEnd = _maskedIndices.cend();
0549 return std::find(_maskedIndices.cbegin(), itEnd, i) != itEnd;
0550 }
0551
0552
0553 template<typename... Axes>
0554 bool Binning<Axes...>::isVisible(const size_t i) const noexcept {
0555 const std::vector<size_t> overflows = calcOverflowBinsIndices();
0556 const auto& itEnd = overflows.cend();
0557 return std::find(overflows.cbegin(), itEnd, i) == itEnd;
0558 }
0559
0560 template<typename... Axes>
0561 size_t Binning<Axes...>::calcSliceSize(const size_t pivotAxisN) const noexcept {
0562 const IndexArr axesSizes = _getAxesSizes();
0563 size_t sliceSize = 1;
0564 for (size_t iDim = 0; iDim < _dim; ++iDim) {
0565 if (iDim == pivotAxisN)
0566 continue;
0567 sliceSize *= (axesSizes[iDim]);
0568 }
0569 return sliceSize;
0570 }
0571
0572 template<typename... Axes>
0573 std::vector<size_t>
0574 Binning<Axes...>::sliceIndices(std::vector<std::pair<size_t, std::vector<size_t>>> slicePivots) const noexcept {
0575
0576 std::vector<size_t> slicesSizes;
0577 slicesSizes.reserve(slicePivots.size());
0578 size_t slicedIndicesVecSize = 0;
0579
0580 for (const auto& slicePivot : slicePivots) {
0581 if(slicePivot.second.size() == 0)
0582 continue;
0583
0584 const size_t& sliceSize = calcSliceSize(slicePivot.first);
0585
0586 slicesSizes.emplace_back(sliceSize);
0587 slicedIndicesVecSize += sliceSize;
0588 }
0589
0590 std::vector<size_t> slicedIndices;
0591 slicedIndices.reserve(slicedIndicesVecSize);
0592
0593 auto appendSliceIndices = [&slicedIndices](std::vector<size_t>&& overflowSlice) {
0594 slicedIndices.insert(std::end(slicedIndices),
0595 std::make_move_iterator(std::begin(overflowSlice)),
0596 std::make_move_iterator(std::end(overflowSlice)));
0597 };
0598
0599 for (const auto& slicePivot : slicePivots) {
0600 const size_t axisN = slicePivot.first;
0601 const auto& binPivots = slicePivot.second;
0602
0603 for(const auto& binPivot : binPivots) {
0604 appendSliceIndices(sliceIndices(axisN, binPivot));
0605 }
0606 }
0607
0608 return slicedIndices;
0609 }
0610
0611 template<typename... Axes>
0612 std::vector<size_t> Binning<Axes...>::sliceIndices(const size_t axisN, const size_t binN) const noexcept {
0613 if(sizeof...(Axes) == 1) {
0614 return {binN};
0615 }
0616
0617 const IndexArr axesSizes = _getAxesSizes();
0618 const size_t sliceSize = calcSliceSize(axisN);
0619
0620 IndexArr multiIdx{};
0621 multiIdx[axisN] = binN;
0622 std::vector<size_t> slicedIndices;
0623
0624 assert(axesSizes.size() != 0);
0625 slicedIndices.reserve(sliceSize);
0626
0627
0628
0629
0630
0631
0632
0633
0634 size_t idxShift = axisN == 0 ? 1 : 0;
0635
0636 size_t idx = idxShift;
0637
0638 while (true) {
0639 slicedIndices.emplace_back(localToGlobalIndex(multiIdx));
0640
0641 multiIdx[idx]++;
0642
0643 while (multiIdx[idx] == axesSizes[idx] || idx == axisN) {
0644
0645 if (idx == axesSizes.size() - 1) {
0646 return slicedIndices;
0647 }
0648
0649 if(idx != axisN)
0650 multiIdx[idx] = 0;
0651
0652 idx++;
0653
0654 if(idx != axisN)
0655 multiIdx[idx]++;
0656 }
0657
0658 idx = idxShift;
0659 }
0660
0661 return slicedIndices;
0662 }
0663
0664 template <typename... Axes>
0665 template <size_t... AxisNs>
0666 void Binning<Axes...>::mergeBins(
0667 std::decay_t<decltype(AxisNs, std::declval<std::pair<size_t, size_t>>())>... mergeRanges) noexcept {
0668
0669 ((void)axis<AxisNs>().mergeBins(mergeRanges), ...);
0670 updateMaskedBins();
0671
0672 }
0673
0674
0675 template <typename... Axes>
0676 template <size_t I>
0677 const typename Binning<Axes...>::template getAxisT<I>&
0678 Binning<Axes...>::axis() const noexcept {
0679 return std::get<I>(_axes);
0680 }
0681
0682 template <typename... Axes>
0683 template <size_t I>
0684 typename Binning<Axes...>::template getAxisT<I>&
0685 Binning<Axes...>::axis() noexcept {
0686 return std::get<I>(_axes);
0687 }
0688
0689
0690 template<typename... Axes>
0691 template<size_t I>
0692 auto Binning<Axes...>::_getAxesExcept() const noexcept {
0693 return MetaUtils::removeTupleElement<I>(_axes);
0694 }
0695
0696 template<typename... Axes>
0697 typename Binning<Axes...>::IndexArr
0698 Binning<Axes...>::_getAxesSizes(const bool includeOverflows) const noexcept {
0699 IndexArr axesSizes{};
0700 auto extractSizes = [&axesSizes, &axes = _axes, &includeOverflows](auto I) {
0701 const auto& axis = std::get<I>(axes);
0702 axesSizes[I] = axis.numBins(includeOverflows);
0703 };
0704
0705 MetaUtils::staticFor<Dimension::value>(extractSizes);
0706
0707 return axesSizes;
0708 }
0709
0710 template<typename... Axes>
0711 size_t Binning<Axes...>::dim() const noexcept {
0712 return _dim;
0713 }
0714
0715
0716 template<typename... Axes>
0717 bool Binning<Axes...>::isCompatible(const Binning<Axes...>& other) const noexcept {
0718
0719
0720 if (_dim != other.dim())
0721 return false;
0722
0723 std::array<bool, 1> isEqual{true};
0724 auto isEqAxes =
0725 [&isEqual, &axes1 = _axes, &axes2 = other._axes](auto I) {
0726 const auto& axis_lhs = std::get<I>(axes1);
0727 const auto& axis_rhs = std::get<I>(axes2);
0728
0729
0730 isEqual[0] &= axis_lhs.hasSameEdges(axis_rhs);
0731 };
0732
0733 MetaUtils::staticFor<Dimension::value>(isEqAxes);
0734
0735 return isEqual[0];
0736 }
0737
0738 template<typename... Axes>
0739 size_t Binning<Axes...>::numBins(const bool includeOverflows, const bool includeMaskedBins) const noexcept {
0740 size_t nBins = 0;
0741 IndexArr axesSizes = _getAxesSizes(includeOverflows);
0742 assert(axesSizes.size() > 0);
0743 nBins = axesSizes[0];
0744 if constexpr (sizeof...(Axes) > 1) {
0745 for (size_t iDim = 1; iDim < _dim; ++iDim) {
0746 nBins *= (axesSizes[iDim]);
0747 }
0748 }
0749 const size_t maskedBins = includeMaskedBins? 0 : _maskedIndices.size();
0750 return nBins - maskedBins;
0751 }
0752
0753 template<typename... Axes>
0754 size_t Binning<Axes...>::numBinsAt(const size_t axisN, const bool includeOverflows) const noexcept {
0755 IndexArr axesSizes = _getAxesSizes(includeOverflows);
0756 assert(axesSizes.size() > 0);
0757 return axesSizes[axisN];
0758 }
0759
0760 template<typename... Axes>
0761 typename Binning<Axes...>::EdgeTypesTuple
0762 Binning<Axes...>::edgeTuple(const size_t index) const noexcept {
0763
0764 EdgeTypesTuple coords;
0765 IndexArr indices = globalToLocalIndices(index);
0766
0767 auto setCoords = [&coords, &indices, &axes = _axes](auto I) {
0768 using isContinuous = typename Binning::template is_CAxis<I>;
0769 const auto& axis = std::get<I>(axes);
0770 const size_t idx = std::get<I>(indices);
0771 if constexpr(isContinuous::value) {
0772 std::get<I>(coords) = axis.mid(idx);
0773 }
0774 else {
0775 std::get<I>(coords) = axis.edge(idx);
0776 }
0777 };
0778 MetaUtils::staticFor<Dimension::value>(setCoords);
0779
0780 return coords;
0781 }
0782
0783 template<typename... Axes>
0784 double Binning<Axes...>::dVol(const size_t index) const {
0785 double rtn = 1.0;
0786 IndexArr indices = globalToLocalIndices(index);
0787
0788 auto getCAxisWidths = [&rtn, &indices, &axes = _axes](auto I){
0789 using isContinuous = typename Binning::template is_CAxis<I>;
0790 if constexpr (isContinuous::value) {
0791 const auto& axis = std::get<I>(axes);
0792 const size_t idx = std::get<I>(indices);
0793 rtn *= axis.width(idx);
0794 }
0795 };
0796 MetaUtils::staticFor<Binning::Dimension::value>(getCAxisWidths);
0797
0798 return rtn;
0799 }
0800
0801 }
0802
0803 #endif