Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-28 07:34:53

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 #include "Acts/Utilities/AxisDefinitions.hpp"
0012 #include "Acts/Utilities/IAxis.hpp"
0013 #include "Acts/Utilities/NeighborHoodIndices.hpp"
0014 
0015 #include <algorithm>
0016 #include <cmath>
0017 #include <iostream>
0018 #include <stdexcept>
0019 #include <vector>
0020 
0021 namespace Acts {
0022 
0023 /// @brief calculate bin indices for an equidistant binning
0024 ///
0025 /// This class provides some basic functionality for calculating bin indices
0026 /// for a given equidistant binning.
0027 template <AxisBoundaryType bdt>
0028 class Axis<AxisType::Equidistant, bdt> : public IAxis {
0029  public:
0030   /// Static type identifier for this equidistant axis specialization
0031   static constexpr AxisType type = AxisType::Equidistant;
0032 
0033   /// Divide the range \f$[\text{xmin},\text{xmax})\f$ into \f$\text{nBins}\f$
0034   /// equidistant bins.
0035   ///
0036   /// @param xmin lower boundary of axis range
0037   /// @param xmax upper boundary of axis range
0038   /// @param nBins number of bins to divide the axis range into
0039   /// @param direction optional direction of the axis
0040   Axis(double xmin, double xmax, std::size_t nBins,
0041        std::optional<AxisDirection> direction = std::nullopt)
0042       : IAxis(direction),
0043         m_min(xmin),
0044         m_max(xmax),
0045         m_width((xmax - xmin) / nBins),
0046         m_bins(nBins) {
0047     if (m_min >= m_max) {
0048       std::string msg = "Axis: Invalid axis range'";
0049       msg += "', min edge (" + std::to_string(m_min) + ") ";
0050       msg += " needs to be smaller than max edge (";
0051       msg += std::to_string(m_max) + ").";
0052       throw std::invalid_argument(msg);
0053     }
0054     if (m_bins < 1u) {
0055       throw std::invalid_argument(
0056           "Axis: Invalid binning, at least one bin is needed.");
0057     }
0058   }
0059 
0060   /// Divide the range \f$[\text{xmin},\text{xmax})\f$ into \f$\text{nBins}\f$
0061   /// equidistant bins.
0062   ///
0063   /// @param typeTag boundary type tag
0064   /// @param xmin lower boundary of axis range
0065   /// @param xmax upper boundary of axis range
0066   /// @param nBins number of bins to divide the axis range into
0067   /// @param direction optional direction of the axis
0068   Axis(AxisBoundaryTypeTag<bdt> typeTag, double xmin, double xmax,
0069        std::size_t nBins, std::optional<AxisDirection> direction = std::nullopt)
0070       : Axis(xmin, xmax, nBins, direction) {
0071     static_cast<void>(typeTag);
0072   }
0073 
0074   /// returns whether the axis is equidistant
0075   /// @return bool is equidistant
0076   bool isEquidistant() const final { return true; }
0077 
0078   /// returns whether the axis is variable
0079   /// @return bool is variable
0080   bool isVariable() const final { return false; }
0081 
0082   /// returns the type of the axis
0083   /// @return @c AxisType of this axis
0084   AxisType getType() const final { return type; }
0085 
0086   /// returns the boundary type set in the template param
0087   /// @return @c AxisBoundaryType of this axis
0088   AxisBoundaryType getBoundaryType() const final { return bdt; }
0089 
0090   /// Get #size bins which neighbor the one given. Generic overload with
0091   /// symmetric size.
0092   /// @param idx requested bin index
0093   /// @param size how many neighboring bins (up/down)
0094   /// @return Set of neighboring bin indices (global)
0095   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0096                                           std::size_t size = 1) const {
0097     return neighborHoodIndices(idx,
0098                                std::make_pair(-static_cast<int>(size), size));
0099   }
0100 
0101   /// Get #size bins which neighbor the one given. This is the version for Open.
0102   /// @param idx requested bin index
0103   /// @param sizes how many neighboring bins (up/down)
0104   /// @return Set of neighboring bin indices (global)
0105   /// @note Open varies given bin and allows 0 and NBins+1 (underflow, overflow) as neighbors
0106   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0107                                           std::pair<int, int> sizes = {-1,
0108                                                                        1}) const
0109     requires(bdt == AxisBoundaryType::Open)
0110   {
0111     constexpr int min = 0;
0112     const int max = getNBins() + 1;
0113     const int itmin = std::clamp(static_cast<int>(idx + sizes.first), min, max);
0114     const int itmax =
0115         std::clamp(static_cast<int>(idx + sizes.second), min, max);
0116     return NeighborHoodIndices(itmin, itmax + 1);
0117   }
0118 
0119   /// Get #size bins which neighbor the one given. This is the version for
0120   /// Bound.
0121   /// @param idx requested bin index
0122   /// @param sizes how many neighboring bins (up/down)
0123   /// @return Set of neighboring bin indices (global)
0124   /// @note Bound varies given bin and allows 1 and NBins (regular bins) as neighbors
0125   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0126                                           std::pair<int, int> sizes = {-1,
0127                                                                        1}) const
0128     requires(bdt == AxisBoundaryType::Bound)
0129   {
0130     if (idx <= 0 || idx >= (getNBins() + 1)) {
0131       return NeighborHoodIndices();
0132     }
0133     constexpr int min = 1;
0134     const int max = getNBins();
0135     const int itmin = std::clamp(static_cast<int>(idx) + sizes.first, min, max);
0136     const int itmax =
0137         std::clamp(static_cast<int>(idx) + sizes.second, min, max);
0138     return NeighborHoodIndices(itmin, itmax + 1);
0139   }
0140 
0141   /// Get #size bins which neighbor the one given. This is the version for
0142   /// Closed (i.e. Wrapping).
0143   /// @param idx requested bin index
0144   /// @param sizes how many neighboring bins (up/down)
0145   /// @return Set of neighboring bin indices (global)
0146   /// @note Closed varies given bin and allows bins on the opposite
0147   ///       side of the axis as neighbors. (excludes underflow / overflow)
0148   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0149                                           std::pair<int, int> sizes = {-1,
0150                                                                        1}) const
0151     requires(bdt == AxisBoundaryType::Closed)
0152   {
0153     // Handle invalid indices
0154     if (idx <= 0 || idx >= (getNBins() + 1)) {
0155       return NeighborHoodIndices();
0156     }
0157 
0158     // Handle corner case where user requests more neighbours than the number
0159     // of bins on the axis. All bins are returned in this case.
0160 
0161     const int max = getNBins();
0162     sizes.first = std::clamp(sizes.first, -max, max);
0163     sizes.second = std::clamp(sizes.second, -max, max);
0164     if (std::abs(sizes.first - sizes.second) >= max) {
0165       sizes.first = 1 - idx;
0166       sizes.second = max - idx;
0167     }
0168 
0169     // If the entire index range is not covered, we must wrap the range of
0170     // targeted neighbor indices into the range of valid bin indices. This may
0171     // split the range of neighbor indices in two parts:
0172     //
0173     // Before wraparound - [        XXXXX]XXX
0174     // After wraparound  - [ XXXX   XXXX ]
0175     //
0176     const int itmin = idx + sizes.first;
0177     const int itmax = idx + sizes.second;
0178     const std::size_t itfirst = wrapBin(itmin);
0179     const std::size_t itlast = wrapBin(itmax);
0180     if (itfirst <= itlast) {
0181       return NeighborHoodIndices(itfirst, itlast + 1);
0182     } else {
0183       return NeighborHoodIndices(itfirst, max + 1, 1, itlast + 1);
0184     }
0185   }
0186 
0187   /// Converts bin index into a valid one for this axis.
0188   /// @note Open: bin index is clamped to [0, nBins+1]
0189   /// @param bin The bin to wrap
0190   /// @return valid bin index
0191   std::size_t wrapBin(int bin) const
0192     requires(bdt == AxisBoundaryType::Open)
0193   {
0194     return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
0195   }
0196 
0197   /// Converts bin index into a valid one for this axis.
0198   /// @note Bound: bin index is clamped to [1, nBins]
0199   /// @param bin The bin to wrap
0200   /// @return valid bin index
0201   std::size_t wrapBin(int bin) const
0202     requires(bdt == AxisBoundaryType::Bound)
0203   {
0204     return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
0205   }
0206 
0207   /// Converts bin index into a valid one for this axis.
0208   /// @note Closed: bin index wraps around to other side
0209   /// @param bin The bin to wrap
0210   /// @return valid bin index
0211   std::size_t wrapBin(int bin) const
0212     requires(bdt == AxisBoundaryType::Closed)
0213   {
0214     const int w = getNBins();
0215     return 1 + (w + ((bin - 1) % w)) % w;
0216     // return int(bin<1)*w - int(bin>w)*w + bin;
0217   }
0218 
0219   /// get corresponding bin index for given coordinate
0220   /// @param x input coordinate
0221   /// @return index of bin containing the given value
0222   /// @note Bin intervals are defined with closed lower bounds and open upper
0223   ///       bounds, that is \f$l <= x < u\f$ if the value @c x lies within a
0224   ///       bin with lower bound @c l and upper bound @c u.
0225   /// @note Bin indices start at @c 1. The underflow bin has the index @c 0
0226   ///       while the index <tt>nBins + 1</tt> indicates the overflow bin .
0227   std::size_t getBin(double x) const final {
0228     return wrapBin(
0229         static_cast<int>(std::floor((x - getMin()) / getBinWidth()) + 1));
0230   }
0231 
0232   /// get bin width
0233   /// @return constant width for all bins
0234   double getBinWidth(std::size_t /*bin*/ = 0) const { return m_width; }
0235 
0236   /// get lower bound of bin
0237   /// @param bin index of bin
0238   /// @return lower bin boundary
0239   ///
0240   /// @pre @c bin must be a valid bin index (excluding the underflow bin),
0241   ///      i.e. \f$1 \le \text{bin} \le \text{nBins} + 1\f$
0242   ///
0243   /// @note Bin intervals have a closed lower bound, i.e. the lower boundary
0244   ///       belongs to the bin with the given bin index.
0245   double getBinLowerBound(std::size_t bin) const {
0246     return getMin() + (bin - 1) * getBinWidth();
0247   }
0248 
0249   /// get upper bound of bin
0250   /// @param bin index of bin
0251   /// @return upper bin boundary
0252   /// @pre @c bin must be a valid bin index (excluding the overflow bin),
0253   ///      i.e. \f$0 \le \text{bin} \le \text{nBins}\f$
0254   /// @note Bin intervals have an open upper bound, i.e. the upper boundary
0255   ///       does @b not belong to the bin with the given bin index.
0256   double getBinUpperBound(std::size_t bin) const {
0257     return getMin() + bin * getBinWidth();
0258   }
0259 
0260   /// get bin center
0261   /// @param bin index of bin
0262   /// @return bin center position
0263   /// @pre @c bin must be a valid bin index (excluding under-/overflow bins),
0264   ///      i.e. \f$1 \le \text{bin} \le \text{nBins}\f$
0265   double getBinCenter(std::size_t bin) const {
0266     return getMin() + (bin - 0.5) * getBinWidth();
0267   }
0268 
0269   /// get maximum of binning range
0270   /// @return maximum of binning range
0271   double getMax() const final { return m_max; }
0272 
0273   /// get minimum of binning range
0274   /// @return minimum of binning range
0275   double getMin() const final { return m_min; }
0276 
0277   /// get total number of bins
0278   /// @return total number of bins (excluding under-/overflow bins)
0279   std::size_t getNBins() const final { return m_bins; }
0280 
0281   /// check whether value is inside axis limits
0282   /// @param x The value to check
0283   /// @return @c true if \f$\text{xmin} \le x < \text{xmax}\f$, otherwise
0284   ///         @c false
0285   /// @post If @c true is returned, the bin containing the given value is a
0286   ///       valid bin, i.e. it is neither the underflow nor the overflow bin.
0287   bool isInside(double x) const { return (m_min <= x) && (x < m_max); }
0288 
0289   /// Return a vector of bin edges
0290   /// @return Vector which contains the bin edges
0291   std::vector<double> getBinEdges() const final {
0292     std::vector<double> binEdges;
0293     for (std::size_t i = 1; i <= m_bins; i++) {
0294       binEdges.push_back(getBinLowerBound(i));
0295     }
0296     binEdges.push_back(getBinUpperBound(m_bins));
0297     return binEdges;
0298   }
0299 
0300   friend std::ostream& operator<<(std::ostream& os, const Axis& axis) {
0301     os << "Axis<Equidistant, " << bdt << ">(";
0302     os << axis.m_min << ", ";
0303     os << axis.m_max << ", ";
0304     os << axis.m_bins << ")";
0305     return os;
0306   }
0307 
0308  protected:
0309   void toStream(std::ostream& os) const final { os << *this; }
0310 
0311  private:
0312   /// minimum of binning range
0313   double m_min{};
0314   /// maximum of binning range
0315   double m_max{};
0316   /// constant bin width
0317   double m_width{};
0318   /// number of bins (excluding under-/overflow bins)
0319   std::size_t m_bins{};
0320 };
0321 
0322 /// calculate bin indices for a variable binning
0323 ///
0324 /// This class provides some basic functionality for calculating bin indices
0325 /// for a given binning with variable bin sizes.
0326 template <AxisBoundaryType bdt>
0327 class Axis<AxisType::Variable, bdt> : public IAxis {
0328  public:
0329   /// Static type identifier for this variable-width axis specialization
0330   static constexpr AxisType type = AxisType::Variable;
0331 
0332   /// Create a binning structure with @c nBins variable-sized bins from the
0333   /// given bin boundaries. @c nBins is given by the number of bin edges
0334   /// reduced by one.
0335   /// @param binEdges vector of bin edges
0336   /// @param direction optional direction of the axis
0337   /// @pre @c binEdges must be strictly sorted in ascending order.
0338   /// @pre @c binEdges must contain at least two entries.
0339   explicit Axis(std::vector<double> binEdges,
0340                 std::optional<AxisDirection> direction = std::nullopt)
0341       : IAxis(direction), m_binEdges(std::move(binEdges)) {
0342     if (m_binEdges.size() < 2) {
0343       throw std::invalid_argument(
0344           "Axis: Invalid binning, at least two bin edges are needed.");
0345     }
0346     if (!std::ranges::is_sorted(m_binEdges)) {
0347       throw std::invalid_argument(
0348           "Axis: Invalid binning, bin edges are not sorted.");
0349     }
0350   }
0351 
0352   /// Create a binning structure with @c nBins variable-sized bins from the
0353   /// given bin boundaries. @c nBins is given by the number of bin edges
0354   /// reduced by one.
0355   /// @param typeTag boundary type tag
0356   /// @param binEdges vector of bin edges
0357   /// @param direction optional direction of the axis
0358   /// @pre @c binEdges must be strictly sorted in ascending order.
0359   /// @pre @c binEdges must contain at least two entries.
0360   Axis(AxisBoundaryTypeTag<bdt> typeTag, std::vector<double> binEdges,
0361        std::optional<AxisDirection> direction = std::nullopt)
0362       : Axis(std::move(binEdges), direction) {
0363     static_cast<void>(typeTag);
0364   }
0365 
0366   /// returns whether the axis is equidistante
0367   /// @return bool is equidistant
0368   bool isEquidistant() const final { return false; }
0369 
0370   /// returns whether the axis is variable
0371   /// @return bool is variable
0372   bool isVariable() const final { return true; }
0373 
0374   /// returns the type of the axis
0375   /// @return @c AxisType of this axis
0376   AxisType getType() const final { return type; }
0377 
0378   /// returns the boundary type set in the template param
0379   /// @return @c AxisBoundaryType of this axis
0380   AxisBoundaryType getBoundaryType() const final { return bdt; }
0381 
0382   /// Get #size bins which neighbor the one given. Generic overload with
0383   /// symmetric size.
0384   /// @param idx requested bin index
0385   /// @param size how many neighboring bins
0386   /// @return Set of neighboring bin indices (global)
0387   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0388                                           std::size_t size = 1) const {
0389     return neighborHoodIndices(idx,
0390                                std::make_pair(-static_cast<int>(size), size));
0391   }
0392 
0393   /// Get #size bins which neighbor the one given. This is the version for Open.
0394   /// @param idx requested bin index
0395   /// @param sizes how many neighboring bins (up/down)
0396   /// @return Set of neighboring bin indices (global)
0397   /// @note Open varies given bin and allows 0 and NBins+1 (underflow, overflow) as neighbors
0398   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0399                                           std::pair<int, int> sizes = {-1,
0400                                                                        1}) const
0401     requires(bdt == AxisBoundaryType::Open)
0402   {
0403     constexpr int min = 0;
0404     const int max = getNBins() + 1;
0405     const int itmin = std::max(min, static_cast<int>(idx) + sizes.first);
0406     const int itmax = std::min(max, static_cast<int>(idx) + sizes.second);
0407     return NeighborHoodIndices(itmin, itmax + 1);
0408   }
0409 
0410   /// Get #size bins which neighbor the one given. This is the version for
0411   /// Bound.
0412   /// @param idx requested bin index
0413   /// @param sizes how many neighboring bins (up/down)
0414   /// @return Set of neighboring bin indices (global)
0415   /// @note Bound varies given bin and allows 1 and NBins (regular bins) as neighbors
0416   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0417                                           std::pair<int, int> sizes = {-1,
0418                                                                        1}) const
0419     requires(bdt == AxisBoundaryType::Bound)
0420   {
0421     if (idx <= 0 || idx >= (getNBins() + 1)) {
0422       return NeighborHoodIndices();
0423     }
0424     constexpr int min = 1;
0425     const int max = getNBins();
0426     const int itmin = std::max(min, static_cast<int>(idx) + sizes.first);
0427     const int itmax = std::min(max, static_cast<int>(idx) + sizes.second);
0428     return NeighborHoodIndices(itmin, itmax + 1);
0429   }
0430 
0431   /// Get #size bins which neighbor the one given. This is the version for
0432   /// Closed.
0433   /// @param idx requested bin index
0434   /// @param sizes how many neighboring bins (up/down)
0435   /// @return Set of neighboring bin indices (global)
0436   /// @note Closed varies given bin and allows bins on the opposite
0437   ///       side of the axis as neighbors. (excludes underflow / overflow)
0438   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0439                                           std::pair<int, int> sizes = {-1,
0440                                                                        1}) const
0441     requires(bdt == AxisBoundaryType::Closed)
0442   {
0443     // Handle invalid indices
0444     if (idx <= 0 || idx >= (getNBins() + 1)) {
0445       return NeighborHoodIndices();
0446     }
0447 
0448     // Handle corner case where user requests more neighbours than the number
0449     // of bins on the axis. All bins are returned in this case
0450 
0451     const int max = getNBins();
0452     sizes.first = std::clamp(sizes.first, -max, max);
0453     sizes.second = std::clamp(sizes.second, -max, max);
0454     if (std::abs(sizes.first - sizes.second) >= max) {
0455       sizes.first = 1 - idx;
0456       sizes.second = max - idx;
0457     }
0458 
0459     // If the entire index range is not covered, we must wrap the range of
0460     // targeted neighbor indices into the range of valid bin indices. This may
0461     // split the range of neighbor indices in two parts:
0462     //
0463     // Before wraparound - [        XXXXX]XXX
0464     // After wraparound  - [ XXXX   XXXX ]
0465     //
0466     const int itmin = idx + sizes.first;
0467     const int itmax = idx + sizes.second;
0468     const std::size_t itfirst = wrapBin(itmin);
0469     const std::size_t itlast = wrapBin(itmax);
0470     if (itfirst <= itlast) {
0471       return NeighborHoodIndices(itfirst, itlast + 1);
0472     } else {
0473       return NeighborHoodIndices(itfirst, max + 1, 1, itlast + 1);
0474     }
0475   }
0476 
0477   /// Converts bin index into a valid one for this axis.
0478   /// @note Open: bin index is clamped to [0, nBins+1]
0479   /// @param bin The bin to wrap
0480   /// @return valid bin index
0481   std::size_t wrapBin(int bin) const
0482     requires(bdt == AxisBoundaryType::Open)
0483   {
0484     return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
0485   }
0486 
0487   /// Converts bin index into a valid one for this axis.
0488   /// @note Bound: bin index is clamped to [1, nBins]
0489   /// @param bin The bin to wrap
0490   /// @return valid bin index
0491   std::size_t wrapBin(int bin) const
0492     requires(bdt == AxisBoundaryType::Bound)
0493   {
0494     return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
0495   }
0496 
0497   /// Converts bin index into a valid one for this axis.
0498   /// @note Closed: bin index wraps around to other side
0499   /// @param bin The bin to wrap
0500   /// @return valid bin index
0501   std::size_t wrapBin(int bin) const
0502     requires(bdt == AxisBoundaryType::Closed)
0503   {
0504     const int w = getNBins();
0505     return 1 + (w + ((bin - 1) % w)) % w;
0506     // return int(bin<1)*w - int(bin>w)*w + bin;
0507   }
0508 
0509   /// get corresponding bin index for given coordinate
0510   /// @param x input coordinate
0511   /// @return index of bin containing the given value
0512   /// @note Bin intervals are defined with closed lower bounds and open upper
0513   ///       bounds, that is \f$l <= x < u\f$ if the value @c x lies within a
0514   ///       bin with lower bound @c l and upper bound @c u.
0515   /// @note Bin indices start at @c 1. The underflow bin has the index @c 0
0516   ///       while the index <tt>nBins + 1</tt> indicates the overflow bin .
0517   std::size_t getBin(double x) const final {
0518     const auto it = std::ranges::upper_bound(m_binEdges, x);
0519     return wrapBin(
0520         static_cast<int>(std::ranges::distance(m_binEdges.begin(), it)));
0521   }
0522 
0523   /// get bin width
0524   /// @param bin index of bin
0525   /// @return width of given bin
0526   /// @pre @c bin must be a valid bin index (excluding under-/overflow bins),
0527   ///      i.e. \f$1 \le \text{bin} \le \text{nBins}\f$
0528   double getBinWidth(std::size_t bin) const {
0529     return m_binEdges.at(bin) - m_binEdges.at(bin - 1);
0530   }
0531 
0532   /// get lower bound of bin
0533   /// @param bin index of bin
0534   /// @return lower bin boundary
0535   /// @pre @c bin must be a valid bin index (excluding the underflow bin),
0536   ///      i.e. \f$1 \le \text{bin} \le \text{nBins} + 1\f$
0537   /// @note Bin intervals have a closed lower bound, i.e. the lower boundary
0538   ///       belongs to the bin with the given bin index.
0539   double getBinLowerBound(std::size_t bin) const {
0540     return m_binEdges.at(bin - 1);
0541   }
0542 
0543   /// get upper bound of bin
0544   /// @param bin index of bin
0545   /// @return upper bin boundary
0546   /// @pre @c bin must be a valid bin index (excluding the overflow bin),
0547   ///      i.e. \f$0 \le \text{bin} \le \text{nBins}\f$
0548   /// @note Bin intervals have an open upper bound, i.e. the upper boundary
0549   ///       does @b not belong to the bin with the given bin index.
0550   double getBinUpperBound(std::size_t bin) const { return m_binEdges.at(bin); }
0551 
0552   /// get bin center
0553   /// @param bin index of bin
0554   /// @return bin center position
0555   /// @pre @c bin must be a valid bin index (excluding under-/overflow bins),
0556   ///      i.e. \f$1 \le \text{bin} \le \text{nBins}\f$
0557   double getBinCenter(std::size_t bin) const {
0558     return 0.5 * (getBinLowerBound(bin) + getBinUpperBound(bin));
0559   }
0560 
0561   /// get maximum of binning range
0562   /// @return maximum of binning range
0563   double getMax() const final { return m_binEdges.back(); }
0564 
0565   /// get minimum of binning range
0566   /// @return minimum of binning range
0567   double getMin() const final { return m_binEdges.front(); }
0568 
0569   /// get total number of bins
0570   /// @return total number of bins (excluding under-/overflow bins)
0571   std::size_t getNBins() const final { return m_binEdges.size() - 1; }
0572 
0573   /// check whether value is inside axis limits
0574   /// @param x The value to check
0575   /// @return @c true if \f$\text{xmin} \le x < \text{xmax}\f$, otherwise @c false
0576   /// @post If @c true is returned, the bin containing the given value is a
0577   ///       valid bin, i.e. it is neither the underflow nor the overflow bin.
0578   bool isInside(double x) const {
0579     return (m_binEdges.front() <= x) && (x < m_binEdges.back());
0580   }
0581 
0582   /// Return a vector of bin edges
0583   /// @return Vector which contains the bin edges
0584   std::vector<double> getBinEdges() const final { return m_binEdges; }
0585 
0586   friend std::ostream& operator<<(std::ostream& os, const Axis& axis) {
0587     os << "Axis<Variable, " << bdt << ">(";
0588     os << axis.m_binEdges.front();
0589     for (std::size_t i = 1; i < axis.m_binEdges.size(); i++) {
0590       os << ", " << axis.m_binEdges[i];
0591     }
0592     os << ")";
0593     return os;
0594   }
0595 
0596  protected:
0597   void toStream(std::ostream& os) const final { os << *this; }
0598 
0599  private:
0600   /// vector of bin edges (sorted in ascending order)
0601   std::vector<double> m_binEdges;
0602 };
0603 
0604 }  // namespace Acts