File indexing completed on 2025-02-21 09:58:12
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027 #pragma once
0028
0029 #include <algorithm>
0030 #include <array>
0031 #include <functional>
0032 #include <initializer_list>
0033 #include <numeric>
0034 #include <stdexcept>
0035 #include <type_traits>
0036 #include <utility>
0037 #include <vector>
0038
0039 namespace dfe {
0040 namespace histogram_impl {
0041 namespace {
0042
0043
0044
0045
0046
0047
0048 template<typename T, std::size_t NDimensions>
0049 class NArray {
0050 public:
0051 using Index = std::array<std::size_t, NDimensions>;
0052
0053
0054 NArray(Index size, const T& value = T());
0055 NArray(const NArray&) = default;
0056 NArray(NArray&&) = default;
0057 NArray& operator=(const NArray&) = default;
0058 NArray& operator=(NArray&&) = default;
0059 ~NArray() = default;
0060
0061
0062 constexpr const Index& size() const { return m_size; }
0063
0064 constexpr const T& operator[](Index idx) const { return m_data[linear(idx)]; }
0065
0066 constexpr T& operator[](Index idx) { return m_data[linear(idx)]; }
0067
0068 const T& at(Index idx) const;
0069
0070 T& at(Index idx);
0071
0072 private:
0073 constexpr std::size_t linear(Index idx) const;
0074 constexpr bool within_bounds(Index idx) const;
0075
0076 Index m_size;
0077 std::vector<T> m_data;
0078 };
0079
0080 }
0081 }
0082
0083
0084 template<typename T>
0085 class UniformAxis {
0086 public:
0087 using Value = T;
0088
0089
0090
0091
0092 UniformAxis(T lower, T upper, std::size_t nbins);
0093
0094
0095 constexpr std::size_t nbins() const { return m_nbins; }
0096
0097 std::size_t index(T value) const;
0098
0099 private:
0100 std::size_t m_nbins;
0101 T m_lower;
0102 T m_upper;
0103 };
0104
0105
0106
0107
0108 template<typename T>
0109 class OverflowAxis {
0110 public:
0111 using Value = T;
0112
0113
0114
0115
0116 OverflowAxis(T lower, T upper, std::size_t nbins);
0117
0118
0119 constexpr std::size_t nbins() const { return 2 + m_ndatabins; }
0120
0121 constexpr std::size_t index(T value) const;
0122
0123 private:
0124 std::size_t m_ndatabins;
0125 T m_lower;
0126 T m_upper;
0127 };
0128
0129
0130 template<typename T>
0131 class VariableAxis {
0132 public:
0133 using Value = T;
0134
0135
0136 explicit VariableAxis(std::vector<T>&& edges);
0137
0138 VariableAxis(std::initializer_list<T> edges);
0139
0140
0141 constexpr std::size_t nbins() const { return m_edges.size() - 1; }
0142
0143 std::size_t index(T value) const;
0144
0145 private:
0146 std::vector<T> m_edges;
0147 };
0148
0149
0150
0151
0152
0153 template<typename T, typename... Axes>
0154 class Histogram {
0155 public:
0156 using Data = dfe::histogram_impl::NArray<T, sizeof...(Axes)>;
0157 using Index = typename Data::Index;
0158
0159 Histogram(Axes&&... axes);
0160
0161
0162 constexpr const Index& size() const { return m_data.size(); }
0163
0164 const T& value(Index idx) const { return m_data.at(idx); }
0165
0166
0167
0168
0169 void fill(typename Axes::Value... values, T weight = static_cast<T>(1)) {
0170
0171 m_data[index(std::index_sequence_for<Axes...>(), values...)] += weight;
0172 }
0173
0174 private:
0175 template<std::size_t... Is>
0176 constexpr Index index(
0177 std::index_sequence<Is...>, typename Axes::Value... values) const {
0178 return Index{std::get<Is>(m_axes).index(values)...};
0179 }
0180
0181 Data m_data;
0182 std::tuple<Axes...> m_axes;
0183 };
0184
0185
0186
0187 using Histogram1 = Histogram<double, OverflowAxis<double>>;
0188 using Histogram2 =
0189 Histogram<double, OverflowAxis<double>, OverflowAxis<double>>;
0190
0191
0192
0193 template<typename T, std::size_t NDimensions>
0194 inline histogram_impl::NArray<T, NDimensions>::NArray(
0195 Index size, const T& value)
0196 : m_size(size)
0197 , m_data(
0198 std::accumulate(
0199 size.begin(), size.end(), static_cast<std::size_t>(1),
0200 std::multiplies<std::size_t>()),
0201 value) {}
0202
0203
0204 template<typename T, std::size_t NDimensions>
0205 constexpr std::size_t
0206 histogram_impl::NArray<T, NDimensions>::linear(Index idx) const {
0207 std::size_t result = 0;
0208 std::size_t step = 1;
0209 for (std::size_t i = 0; i < NDimensions; ++i) {
0210 result += step * idx[i];
0211 step *= m_size[i];
0212 }
0213 return result;
0214 }
0215
0216 template<typename T, std::size_t NDimensions>
0217 constexpr bool
0218 histogram_impl::NArray<T, NDimensions>::within_bounds(Index idx) const {
0219 for (std::size_t i = 0; i < NDimensions; ++i) {
0220 if (m_size[i] <= idx[i]) {
0221 return false;
0222 }
0223 }
0224 return true;
0225 }
0226
0227 template<typename T, std::size_t NDimensions>
0228 inline const T&
0229 histogram_impl::NArray<T, NDimensions>::at(Index idx) const {
0230 if (!within_bounds(idx)) {
0231 throw std::out_of_range("NArray index is out of valid range");
0232 }
0233 return m_data[linear(idx)];
0234 }
0235
0236 template<typename T, std::size_t NDimensions>
0237 inline T&
0238 histogram_impl::NArray<T, NDimensions>::at(Index idx) {
0239 if (!within_bounds(idx)) {
0240 throw std::out_of_range("NArray index is out of valid range");
0241 }
0242 return m_data[linear(idx)];
0243 }
0244
0245
0246
0247 template<typename T>
0248 inline UniformAxis<T>::UniformAxis(T lower, T upper, std::size_t nbins)
0249 : m_nbins(nbins), m_lower(lower), m_upper(upper) {}
0250
0251 template<typename T>
0252 inline std::size_t
0253 UniformAxis<T>::index(T value) const {
0254 if (value < this->m_lower) {
0255 throw std::out_of_range("Value is smaller than lower axis limit");
0256 }
0257 if (m_upper <= value) {
0258 throw std::out_of_range("Value is equal or larger than upper axis limit");
0259 }
0260
0261 return static_cast<std::size_t>(
0262 m_nbins * (value - m_lower) / (m_upper - m_lower));
0263 }
0264
0265
0266
0267 template<typename T>
0268 inline OverflowAxis<T>::OverflowAxis(
0269 Value lower, Value upper, std::size_t nbins)
0270 : m_ndatabins(nbins), m_lower(lower), m_upper(upper) {}
0271
0272 template<typename T>
0273 constexpr std::size_t
0274 OverflowAxis<T>::index(T value) const {
0275 if (value < m_lower) {
0276 return 0;
0277 }
0278 if (m_upper <= value) {
0279 return m_ndatabins + 1;
0280 }
0281
0282 return 1
0283 + static_cast<std::size_t>(
0284 m_ndatabins * (value - m_lower) / (m_upper - m_lower));
0285 }
0286
0287
0288
0289 template<typename T>
0290 inline VariableAxis<T>::VariableAxis(std::vector<Value>&& edges)
0291 : m_edges(std::move(edges)) {
0292 if (m_edges.size() < 2) {
0293 throw std::invalid_argument("Less than two bin edges");
0294 }
0295
0296 if (!std::is_sorted(
0297 m_edges.begin(), m_edges.end(), std::less_equal<Value>())) {
0298 throw std::invalid_argument("Bin edges are not sorted or have duplicates");
0299 }
0300 }
0301
0302 template<typename T>
0303 inline VariableAxis<T>::VariableAxis(std::initializer_list<Value> edges)
0304 : VariableAxis(std::vector<Value>(edges)) {}
0305
0306 template<typename T>
0307 inline std::size_t
0308 VariableAxis<T>::index(T value) const {
0309
0310 auto it = std::upper_bound(m_edges.begin(), m_edges.end(), value);
0311 if (it == m_edges.begin()) {
0312 throw std::out_of_range("Value is smaller than lower axis limit");
0313 }
0314 if (it == m_edges.end()) {
0315 throw std::out_of_range("Value is equal or larger than upper axis limit");
0316 }
0317 return std::distance(m_edges.begin(), it) - 1;
0318 }
0319
0320
0321
0322 template<typename T, typename... Axes>
0323 inline Histogram<T, Axes...>::Histogram(Axes&&... axes)
0324
0325 : m_data(Index{axes.nbins()...}, static_cast<T>(0))
0326 , m_axes(std::move(axes)...) {}
0327
0328 }