18 template<
typename CoordT>
23 template<
typename CoordT>
25 return std::forward<CoordT>(coord);
29 template<
typename... Args>
31 std::array<size_t,
sizeof...(Args)> hasNan{};
32 auto checkCoords = [&hasNan, &coords](
auto I) {
33 using isContinuous =
typename std::is_floating_point<
typename std::tuple_element_t<I, std::tuple<Args...>>>;
36 std::integral_constant<
bool,
37 isContinuous::value>{})));
42 return std::any_of(hasNan.begin(), hasNan.end(), [ ](
const bool i) { return i; } );
47 template <
typename... Axes>
51 std::tuple<Axes...> _axes;
52 size_t _dim =
sizeof...(Axes);
53 std::vector<size_t> _maskedIndices;
61 using IndexArr = std::array<size_t,
sizeof...(Axes)>;
71 using getAxisT = std::tuple_element_t<I, std::tuple<Axes...>>;
77 using is_CAxis =
typename std::is_floating_point<getEdgeT<I>>;
82 using all_CAxes =
typename std::conjunction<std::is_floating_point<typename Axes::EdgeT>...>;
85 std::tuple_cat(std::declval<std::tuple<typename Axes::EdgeT>>()...));
87 using Dimension = std::integral_constant<size_t,
sizeof...(Axes)>;
98 Binning(
const std::initializer_list<typename Axes::EdgeT>&...
edges)
99 : _axes(Axes(
edges)...) {
105 : _axes(Axes(std::move(
edges))...) {
117 : _axes(std::move(axes)...) {
122 Binning(
const Binning& other) : _axes(other._axes), _maskedIndices(other._maskedIndices) {}
125 Binning(
Binning&& other) : _axes(std::move(other._axes)), _maskedIndices(std::move(other._maskedIndices)) {}
129 if (
this != &other) {
131 _maskedIndices = other._maskedIndices;
138 if (
this != &other) {
139 _axes = std::move(other._axes);
140 _maskedIndices = std::move(other._maskedIndices);
169 std::vector<
size_t>
maskedBins() const noexcept {
return _maskedIndices; }
175 void maskBin(
const size_t index,
const bool status =
true);
184 void maskBins(const std::vector<
size_t>& indices, const
bool status = true);
187 void maskSlice(const
size_t dim, const
size_t idx, const
bool status = true);
190 bool isMasked(const
size_t i) const noexcept;
193 bool isVisible(const
size_t i) const noexcept;
196 size_t calcSliceSize(const
size_t pivotAxisN) const noexcept;
201 std::vector<
size_t>
sliceIndices(std::vector<std::pair<
size_t, std::vector<
size_t>>>) const noexcept;
205 std::vector<
size_t>
sliceIndices(
size_t axisN,
size_t binN) const noexcept;
211 std::vector<std::pair<size_t, std::vector<size_t>>> slicePivots;
212 auto extractMaskedBins = [&slicePivots, &axes = _axes](
auto I) {
213 using isContinuous =
typename Binning::template
is_CAxis<I>;
215 if constexpr(isContinuous::value) {
216 const auto&
axis = std::get<I>(axes);
217 slicePivots.push_back({I, {
axis.maskedBins()} });
222 MetaUtils::staticFor<Dimension::value>(extractMaskedBins);
226 std::sort(_maskedIndices.begin(), _maskedIndices.end());
227 _maskedIndices.erase( std::unique(_maskedIndices.begin(), _maskedIndices.end()),
228 _maskedIndices.end() );
236 template <
size_t... AxisNs>
238 std::decay_t<
decltype(AxisNs, std::declval<std::pair<size_t, size_t>>())>... mergeRanges)
noexcept;
264 auto _getAxesExcept() const noexcept;
267 IndexArr _getAxesSizes(const
bool includeOverflows = true) const noexcept;
270 size_t dim() const noexcept;
273 size_t numBins(const
bool includeOverflows = true, const
bool includeMaskedBins = true) const noexcept;
276 size_t numBinsAt(const
size_t axisN, const
bool includeOverflows = true) const noexcept;
285 template <
size_t I, typename E =
getEdgeT<I>>
286 std::vector<E>
edges(const
bool includeOverflows = false) const noexcept {
287 const auto&
axis = std::get<I>(_axes);
289 auto&& all_edges =
axis.edges();
290 typename std::vector<E> rtn;
291 const size_t offset = all_edges.size() - 1;
292 rtn.insert(rtn.end(), std::make_move_iterator(all_edges.begin()+1),
293 std::make_move_iterator(all_edges.begin()+offset));
305 template <
size_t I,
typename E = getEdgeT<I>>
306 std::enable_if_t<std::is_floating_point<E>::value, std::vector<E>>
307 widths(
const bool includeOverflows =
false) const noexcept {
308 const auto&
axis = std::get<I>(_axes);
309 return axis.widths(includeOverflows);
315 template <
size_t I,
typename E = getEdgeT<I>>
316 std::enable_if_t<std::is_floating_point<E>::value, E>
min() const noexcept {
317 const auto&
axis = std::get<I>(_axes);
318 assert(
axis.numBins(
true) > 2);
325 template <
size_t I,
typename E = getEdgeT<I>>
326 std::enable_if_t<std::is_floating_point<E>::value, E>
max() const noexcept {
327 const auto&
axis = std::get<I>(_axes);
328 assert(
axis.numBins(
true) > 2);
329 return axis.min(
axis.numBins(
true)-1);
333 double dVol(
const size_t index)
const;
341 void _renderYODA(std::ostream& os)
const noexcept {
343 auto edgePrinter = [&](
auto I) {
344 const auto&
axis = std::get<I>(_axes);
345 if (
axis.numBins()) {
346 os <<
"Edges(A"+ std::to_string(I+1) +
"): ";
347 axis._renderYODA(os);
352 MetaUtils::staticFor<Dimension::value>(edgePrinter);
355 if (_maskedIndices.size()) {
356 std::vector<size_t> gaps(_maskedIndices.size());
357 std::partial_sort_copy(begin(_maskedIndices), end(_maskedIndices), begin(gaps), end(gaps));
359 os <<
"MaskedBins: [";
360 for (
size_t i = 0; i < gaps.size(); ++i) {
362 os << std::to_string(gaps[i]);
369 void _renderYODA(std::ostream& os,
const size_t idx,
const int width = 13)
const {
372 auto edgePrinter = [&](
auto I) {
373 const auto&
axis = std::get<I>(_axes);
374 axis._renderYODA(os, localIndices[I], width);
377 MetaUtils::staticFor<Dimension::value>(edgePrinter);
385 template<
typename... Axes>
395 IndexArr axesSizes = _getAxesSizes();
396 for (
size_t iIndex = 0; iIndex < localIndices.size(); ++iIndex) {
397 size_t productOfBinSizes = 1;
398 for (ssize_t iBinnedAxis = iIndex - 1; iBinnedAxis >= 0; --iBinnedAxis) {
399 productOfBinSizes *= (axesSizes[iBinnedAxis]);
401 gIndex += localIndices[iIndex] * productOfBinSizes;
406 template <
typename... Axes>
407 typename Binning<Axes...>::IndexArr
409 if (globalIndex >= numBins(
true,
true))
throw RangeError(
"Global index outside bin range");
412 IndexArr axesSizes = _getAxesSizes();
414 for (ssize_t iIndex = localIndices.size()-1 ; iIndex >= 0; --iIndex){
415 size_t productOfNestedBinSizes = 1;
417 for (ssize_t iBinnedAxis = iIndex - 1; iBinnedAxis >= 0; --iBinnedAxis) {
418 productOfNestedBinSizes *= (axesSizes[iBinnedAxis]);
422 localIndices[iIndex] = globalIndex / productOfNestedBinSizes;
424 globalIndex = globalIndex % productOfNestedBinSizes;
429 template<
typename... Axes>
430 typename Binning<Axes...>::IndexArr
433 auto extractIndices = [&localIndices, &coords, &axes = _axes](
auto I) {
434 const auto& coord = std::get<I>(coords);
435 const auto& axis = std::get<I>(axes);
437 localIndices[I] = axis.index(coord);
440 MetaUtils::staticFor<Dimension::value>(extractIndices);
445 template<
typename... Axes>
447 return localToGlobalIndex(localIndicesAt(coords));
450 template<
typename... Axes>
452 IndexArr axesSizes = _getAxesSizes();
453 std::vector<bool> isCAxis;
455 auto getCAxisIndices = [&isCAxis](
auto I){
456 using isContinuous =
typename Binning::template
is_CAxis<I>;
457 isCAxis.emplace_back(isContinuous::value);
460 MetaUtils::staticFor<Dimension::value>(getCAxisIndices);
462 std::vector<std::pair<size_t, std::vector<size_t>>> slicePivots;
463 slicePivots.reserve(isCAxis.size());
465 for (
size_t axisN = 0; axisN < isCAxis.size(); ++axisN) {
467 slicePivots.push_back({axisN, {0, axesSizes[axisN]-1} });
469 slicePivots.push_back({axisN, {0} });
472 std::vector<size_t> res = sliceIndices(slicePivots);
473 std::sort(res.begin(), res.end());
474 res.erase( std::unique(res.begin(), res.end()), res.end() );
484 template<
typename... Axes>
486 std::vector<bool> CAxisIndices;
488 auto getCAxisIndices = [&CAxisIndices](
auto I){
489 using isContinuous =
typename Binning::template
is_CAxis<I>;
490 CAxisIndices.emplace_back(isContinuous::value);
493 MetaUtils::staticFor<Dimension::value>(getCAxisIndices);
494 return CAxisIndices.at(axisN) + 1;
511 template<
typename... Axes>
516 template<
typename... Axes>
522 template<
typename... Axes>
528 template<
typename... Axes>
530 for (
size_t i : indices) {
531 const auto& itEnd = this->_maskedIndices.cend();
532 const auto& res = std::find(this->_maskedIndices.cbegin(), itEnd, i);
533 if (status && res == itEnd) this->_maskedIndices.push_back(i);
534 else if (!status && res != itEnd) this->_maskedIndices.erase(res);
538 template <
typename... Axes>
541 std::vector<std::pair<size_t, std::vector<size_t>>> slicePivot{{dim,{idx}}};
546 template<
typename... Axes>
548 const auto& itEnd = _maskedIndices.cend();
549 return std::find(_maskedIndices.cbegin(), itEnd, i) != itEnd;
553 template<
typename... Axes>
555 const std::vector<size_t> overflows = calcOverflowBinsIndices();
556 const auto& itEnd = overflows.cend();
557 return std::find(overflows.cbegin(), itEnd, i) == itEnd;
560 template<
typename... Axes>
562 const IndexArr axesSizes = _getAxesSizes();
563 size_t sliceSize = 1;
564 for (
size_t iDim = 0; iDim < _dim; ++iDim) {
565 if (iDim == pivotAxisN)
567 sliceSize *= (axesSizes[iDim]);
572 template<
typename... Axes>
576 std::vector<size_t> slicesSizes;
577 slicesSizes.reserve(slicePivots.size());
578 size_t slicedIndicesVecSize = 0;
580 for (
const auto& slicePivot : slicePivots) {
581 if(slicePivot.second.size() == 0)
584 const size_t& sliceSize = calcSliceSize(slicePivot.first);
586 slicesSizes.emplace_back(sliceSize);
587 slicedIndicesVecSize += sliceSize;
590 std::vector<size_t> slicedIndices;
591 slicedIndices.reserve(slicedIndicesVecSize);
593 auto appendSliceIndices = [&slicedIndices](std::vector<size_t>&& overflowSlice) {
594 slicedIndices.insert(std::end(slicedIndices),
595 std::make_move_iterator(std::begin(overflowSlice)),
596 std::make_move_iterator(std::end(overflowSlice)));
599 for (
const auto& slicePivot : slicePivots) {
600 const size_t axisN = slicePivot.first;
601 const auto& binPivots = slicePivot.second;
603 for(
const auto& binPivot : binPivots) {
604 appendSliceIndices(sliceIndices(axisN, binPivot));
608 return slicedIndices;
611 template<
typename... Axes>
613 if(
sizeof...(Axes) == 1) {
617 const IndexArr axesSizes = _getAxesSizes();
618 const size_t sliceSize = calcSliceSize(axisN);
621 multiIdx[axisN] = binN;
622 std::vector<size_t> slicedIndices;
624 assert(axesSizes.size() != 0);
625 slicedIndices.reserve(sliceSize);
634 size_t idxShift = axisN == 0 ? 1 : 0;
636 size_t idx = idxShift;
639 slicedIndices.emplace_back(localToGlobalIndex(multiIdx));
643 while (multiIdx[idx] == axesSizes[idx] || idx == axisN) {
645 if (idx == axesSizes.size() - 1) {
646 return slicedIndices;
661 return slicedIndices;
664 template <
typename... Axes>
665 template <
size_t... AxisNs>
667 std::decay_t<
decltype(AxisNs, std::declval<std::pair<size_t, size_t>>())>... mergeRanges)
noexcept {
669 ((void)axis<AxisNs>().mergeBins(mergeRanges), ...);
675 template <
typename... Axes>
677 const typename Binning<Axes...>::template getAxisT<I>&
679 return std::get<I>(_axes);
682 template <
typename... Axes>
684 typename Binning<Axes...>::template getAxisT<I>&
686 return std::get<I>(_axes);
690 template<
typename... Axes>
693 return MetaUtils::removeTupleElement<I>(_axes);
696 template<
typename... Axes>
697 typename Binning<Axes...>::IndexArr
698 Binning<Axes...>::_getAxesSizes(
const bool includeOverflows)
const noexcept {
699 IndexArr axesSizes{};
700 auto extractSizes = [&axesSizes, &axes = _axes, &includeOverflows](
auto I) {
701 const auto& axis = std::get<I>(axes);
702 axesSizes[I] = axis.numBins(includeOverflows);
705 MetaUtils::staticFor<Dimension::value>(extractSizes);
710 template<
typename... Axes>
716 template<
typename... Axes>
720 if (_dim != other.dim())
723 std::array<bool, 1> isEqual{
true};
725 [&isEqual, &axes1 = _axes, &axes2 = other._axes](
auto I) {
726 const auto& axis_lhs = std::get<I>(axes1);
727 const auto& axis_rhs = std::get<I>(axes2);
730 isEqual[0] &= axis_lhs.hasSameEdges(axis_rhs);
733 MetaUtils::staticFor<Dimension::value>(isEqAxes);
738 template<
typename... Axes>
741 IndexArr axesSizes = _getAxesSizes(includeOverflows);
742 assert(axesSizes.size() > 0);
743 nBins = axesSizes[0];
744 if constexpr (
sizeof...(Axes) > 1) {
745 for (
size_t iDim = 1; iDim < _dim; ++iDim) {
746 nBins *= (axesSizes[iDim]);
749 const size_t maskedBins = includeMaskedBins? 0 : _maskedIndices.size();
750 return nBins - maskedBins;
753 template<
typename... Axes>
755 IndexArr axesSizes = _getAxesSizes(includeOverflows);
756 assert(axesSizes.size() > 0);
757 return axesSizes[axisN];
760 template<
typename... Axes>
761 typename Binning<Axes...>::EdgeTypesTuple
765 IndexArr indices = globalToLocalIndices(index);
767 auto setCoords = [&coords, &indices, &axes = _axes](
auto I) {
768 using isContinuous =
typename Binning::template
is_CAxis<I>;
769 const auto& axis = std::get<I>(axes);
770 const size_t idx = std::get<I>(indices);
771 if constexpr(isContinuous::value) {
772 std::get<I>(coords) = axis.mid(idx);
775 std::get<I>(coords) = axis.edge(idx);
778 MetaUtils::staticFor<Dimension::value>(setCoords);
783 template<
typename... Axes>
786 IndexArr indices = globalToLocalIndices(index);
788 auto getCAxisWidths = [&rtn, &indices, &axes = _axes](
auto I){
789 using isContinuous =
typename Binning::template
is_CAxis<I>;
790 if constexpr (isContinuous::value) {
791 const auto& axis = std::get<I>(axes);
792 const size_t idx = std::get<I>(indices);
793 rtn *= axis.width(idx);
796 MetaUtils::staticFor<Binning::Dimension::value>(getCAxisWidths);
size_t countOverflowBins(const size_t axisN) const noexcept
Count number of overflow bins.
size_t globalIndexAt(const EdgeTypesTuple &coords) const
calculates global index for a given array of input values
bool isMasked(const size_t i) const noexcept
Check if bin is masked.
void updateMaskedBins() noexcept
Helper function to extract and mask index slices corresponding to masked bins along the axes.
void clearMaskedBins() noexcept
Clear masked bins.
std::tuple_element_t< I, std::tuple< Axes... > > getAxisT
void maskBin(const size_t index, const bool status=true)
Mask/Unmask bin with global index index.
Binning(const Binning &other)
Copy constructor.
typename std::is_arithmetic< getEdgeT< I > > is_Arithmetic
Binning(std::initializer_list< typename Axes::EdgeT > &&... edges)
Constructs binning from Rvalue vectors of axes' edges.
std::array< size_t, sizeof...(Axes)> IndexArr
const getAxisT< I > & axis() const noexcept
Extracts axis corresponding to dimension. Const version.
Binning(Axes &&... axes)
Constructs binning from a sequence of Rvalue axes.
void mergeBins(std::decay_t< decltype(AxisNs, std::declval< std::pair< size_t, size_t > >())>... mergeRanges) noexcept
Binning()
Nullary constructor for unique pointers etc.
std::enable_if_t< std::is_floating_point< E >::value, std::vector< E > > widths(const bool includeOverflows=false) const noexcept
Templated version to get bin widths of axis N by reference.
IndexArr globalToLocalIndices(size_t globalIndex) const
calculates array of local indices from a global index
void maskBinAt(const EdgeTypesTuple &coords, const bool status=true) noexcept
Mask/Unmask bin at set of @ coords.
Binning(const Axes &... axes)
Constructs binning from a sequence of axes.
decltype(std::tuple_cat(std::declval< std::tuple< typename Axes::EdgeT > >()...)) EdgeTypesTuple
std::vector< size_t > sliceIndices(std::vector< std::pair< size_t, std::vector< size_t > > >) const noexcept
Calculates indices of bins located in the specified slices.
typename std::is_floating_point< getEdgeT< I > > is_CAxis
Binning & operator=(const Binning &other)
Copy assignment.
std::vector< size_t > maskedBins() const noexcept
Calculates indices of masked bins.
std::integral_constant< size_t, sizeof...(Axes)> Dimension
size_t numBins(const bool includeOverflows=true, const bool includeMaskedBins=true) const noexcept
get total number of bins in Binning
IndexArr localIndicesAt(const EdgeTypesTuple &coords) const
assembles array of local indices for an array of input values
void maskSlice(const size_t dim, const size_t idx, const bool status=true)
Mask a slice of the binning at local bin index idx along axis dimesnion dim.
Binning(Binning &&other)
Move constructor.
Binning(const std::initializer_list< typename Axes::EdgeT > &... edges)
Constructs binning from vectors of axes' edges.
typename std::conjunction< std::is_floating_point< typename Axes::EdgeT >... > all_CAxes
size_t calcSliceSize(const size_t pivotAxisN) const noexcept
Calculates size of a binning slice.
Binning & operator=(Binning &&other)
Move assignment.
std::enable_if_t< std::is_floating_point< E >::value, E > max() const noexcept
Get the highest non-overflow edge of the axis.
EdgeTypesTuple edgeTuple(const size_t index) const noexcept
Return a coordinate tuple for bin index.
bool isVisible(const size_t i) const noexcept
Check if bin is in visible range.
typename getAxisT< I >::EdgeT getEdgeT
size_t localToGlobalIndex(const IndexArr &localIndices) const
calculates global index from array of local indices
void maskBins(const std::vector< size_t > &indices, const bool status=true)
Mask/Unmask bins in list of global indices.
size_t numBinsAt(const size_t axisN, const bool includeOverflows=true) const noexcept
get number of bins at axis
bool isCompatible(const Binning< Axes... > &other) const noexcept
checks if Binnings are compatible
size_t dim() const noexcept
get dimension of Binning
std::vector< E > edges(const bool includeOverflows=false) const noexcept
Templated version to get edges of axis N by value.
std::vector< size_t > calcOverflowBinsIndices() const noexcept
Calculates indices of overflow bins.
double dVol(const size_t index) const
Return the differential volume for bin index.
std::enable_if_t< std::is_floating_point< E >::value, E > min() const noexcept
Get the lowest non-overflow edge of the axis.
Error for e.g. use of invalid bin ranges.
Anonymous namespace to limit visibility.
bool containsNan(const std::tuple< Args... > &coords)
Checks if a coordinate tuple has a nan.
double nullifyIfDiscCoord(CoordT &&, std::false_type, double null=0.0)
Nullifies coordinate if it is discrete.