Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:57:51

0001 /***********************************************************************************\
0002 * (c) Copyright 1998-2025 CERN for the benefit of the LHCb and ATLAS collaborations *
0003 *                                                                                   *
0004 * This software is distributed under the terms of the Apache version 2 licence,     *
0005 * copied verbatim in the file "LICENSE".                                            *
0006 *                                                                                   *
0007 * In applying this licence, CERN does not waive the privileges and immunities       *
0008 * granted to it by virtue of its status as an Intergovernmental Organization        *
0009 * or submit itself to any jurisdiction.                                             *
0010 \***********************************************************************************/
0011 #pragma once
0012 
0013 #include <Gaudi/Accumulators.h>
0014 #include <Gaudi/MonitoringHub.h>
0015 #include <GaudiKernel/HistoDef.h>
0016 
0017 #include <array>
0018 #include <cassert>
0019 #include <cmath>
0020 #include <fmt/format.h>
0021 #include <nlohmann/json.hpp>
0022 #include <string>
0023 #include <type_traits>
0024 #include <utility>
0025 #include <vector>
0026 
0027 namespace {
0028   // Helper class creating a "subtuple" type from a tuple type by keeping only
0029   // the first N items.
0030   template <typename Tuple, typename Seq>
0031   struct SubTuple;
0032   template <typename Tuple, size_t... I>
0033   struct SubTuple<Tuple, std::index_sequence<I...>> {
0034     using type = decltype( std::make_tuple( std::get<I>( std::declval<Tuple>() )... ) );
0035   };
0036   template <typename Tuple, unsigned int N>
0037   using SubTuple_t = typename SubTuple<Tuple, std::make_index_sequence<N>>::type;
0038 
0039   /// helper class to create a tuple of N identical types
0040   template <typename T, unsigned int ND, typename = std::make_integer_sequence<unsigned int, ND>>
0041   struct make_tuple;
0042   template <typename T, unsigned int ND, unsigned int... S>
0043   struct make_tuple<T, ND, std::integer_sequence<unsigned int, S...>> {
0044     template <unsigned int>
0045     using typeMap = T;
0046     using type    = std::tuple<typeMap<S>...>;
0047   };
0048   template <typename T, unsigned int ND>
0049   using make_tuple_t = typename make_tuple<T, ND>::type;
0050 
0051   /// template magic converting a tuple of Axis into the tuple of corresponding Arithmetic types
0052   template <typename AxisTupleType>
0053   struct AxisToArithmetic;
0054   template <typename... Axis>
0055   struct AxisToArithmetic<std::tuple<Axis...>> {
0056     using type = std::tuple<typename Axis::ArithmeticType...>;
0057   };
0058   template <typename AxisTupleType>
0059   using AxisToArithmetic_t = typename AxisToArithmetic<AxisTupleType>::type;
0060   template <typename ProfArithmetic, typename AxisTupleType>
0061   using ProfileAxisToArithmetic_t = decltype( std::tuple_cat( std::declval<AxisToArithmetic_t<AxisTupleType>>(),
0062                                                               std::declval<std::tuple<ProfArithmetic>>() ) );
0063 } // namespace
0064 
0065 namespace Gaudi::Accumulators {
0066 
0067   namespace details {
0068     inline void requireValidTitle( std::string_view sv ) {
0069       if ( !sv.empty() && ( std::isspace( sv.back() ) || std::isspace( sv.front() ) ) ) {
0070         throw GaudiException(
0071             fmt::format( "Histogram title \'{}\' has whitespace at front or back -- please remove", sv ),
0072             "Gaudi::Accumulators", StatusCode::FAILURE );
0073       }
0074     }
0075   } // namespace details
0076 
0077   /**
0078    * A functor to extract weight, take a pair (valueTuple, weight) as input
0079    */
0080   struct ExtractWeight {
0081     template <typename Arithmetic>
0082     constexpr decltype( auto ) operator()( const std::pair<unsigned long, Arithmetic>& v ) const noexcept {
0083       return v.second;
0084     }
0085   };
0086 
0087   /**
0088    * A Product functor, take a pair (value, weight) as input
0089    */
0090   struct WeightedProduct {
0091     template <typename Arithmetic>
0092     constexpr decltype( auto ) operator()( const std::pair<Arithmetic, Arithmetic>& v ) const noexcept {
0093       return v.first * v.second;
0094     }
0095   };
0096 
0097   /**
0098    * A WeightedSquare functor, take a pair (value, weight) as input
0099    */
0100   struct WeightedSquare {
0101     template <typename Arithmetic>
0102     constexpr decltype( auto ) operator()( const std::pair<Arithmetic, Arithmetic>& v ) const noexcept {
0103       return v.first * v.first * v.second;
0104     }
0105   };
0106 
0107   /**
0108    * An inputTransform for WeightedProfile histograms, keeping weight and replacing value by 1
0109    */
0110   struct WeightedProfileTransform {
0111     template <typename Arithmetic>
0112     constexpr decltype( auto ) operator()( const std::pair<Arithmetic, Arithmetic>& v ) const noexcept {
0113       return std::pair<unsigned int, Arithmetic>{ 1ul, v.second };
0114     }
0115   };
0116 
0117   /**
0118    * An Adder ValueHandler, taking weight into account and computing a count plus the sum of the weights
0119    * In case of full atomicity, fetch_add or compare_exchange_weak are used for each element,
0120    * that is we do not have full atomicity accross the two elements
0121    */
0122   template <typename Arithmetic, atomicity Atomicity>
0123   struct WeightedAdder {
0124     using RegularType              = std::pair<unsigned long, Arithmetic>;
0125     using AtomicType               = std::pair<std::atomic<unsigned long>, std::atomic<Arithmetic>>;
0126     using OutputType               = RegularType;
0127     static constexpr bool isAtomic = Atomicity == atomicity::full;
0128     using InternalType             = std::conditional_t<isAtomic, AtomicType, OutputType>;
0129     static constexpr OutputType getValue( const InternalType& v ) noexcept {
0130       if constexpr ( isAtomic ) {
0131         return { v.first.load( std::memory_order_relaxed ), v.second.load( std::memory_order_relaxed ) };
0132       } else {
0133         return v;
0134       }
0135     }
0136     static RegularType exchange( InternalType& v, RegularType newv ) noexcept {
0137       if constexpr ( isAtomic ) {
0138         return { v.first.exchange( newv.first ), v.second.exchange( newv.second ) };
0139       } else {
0140         return { std::exchange( v.first, newv.first ), std::exchange( v.second, newv.second ) };
0141       }
0142     }
0143     static constexpr OutputType DefaultValue() { return { 0, Arithmetic{} }; }
0144     static void                 merge( InternalType& a, RegularType b ) noexcept {
0145       if constexpr ( isAtomic ) {
0146         fetch_add( a.first, b.first );
0147         fetch_add( a.second, b.second );
0148       } else {
0149         a.first += b.first;
0150         a.second += b.second;
0151       }
0152     }
0153   };
0154 
0155   /**
0156    * WeightedCountAccumulator. A WeightedCountAccumulator is an Accumulator storing the number of provided values,
0157    * as well as the weighted version of it, aka. the sum of weights. It takes a pair (valueTuple, weight) as input
0158    * @see Gaudi::Accumulators for detailed documentation
0159    */
0160   template <atomicity Atomicity, typename Arithmetic>
0161   struct WeightedCountAccumulator
0162       : GenericAccumulator<std::pair<Arithmetic, Arithmetic>, std::pair<unsigned long, Arithmetic>, Atomicity,
0163                            WeightedProfileTransform, ExtractWeight, WeightedAdder<Arithmetic, Atomicity>> {
0164     using Base = GenericAccumulator<std::pair<Arithmetic, Arithmetic>, std::pair<unsigned long, Arithmetic>, Atomicity,
0165                                     WeightedProfileTransform, ExtractWeight, WeightedAdder<Arithmetic, Atomicity>>;
0166     using Base::Base;
0167     using Base::operator+=;
0168     /// overload of operator+= to be able to only give weight and no value
0169     WeightedCountAccumulator operator+=( const Arithmetic weight ) {
0170       *this += { 1ul, weight };
0171       return *this;
0172     }
0173     unsigned long nEntries() const { return this->rawValue().first; }
0174     Arithmetic    sumOfWeights() const { return this->rawValue().second; }
0175   };
0176 
0177   /**
0178    * WeightedSumAccumulator. A WeightedSumAccumulator is an Accumulator storing a weighted sum of values.
0179    * It takes a pair (valueTuple, weight) and basically sums the product of the last item othe 2 part of its in put pair
0180    * : weight and value
0181    * @see Gaudi::Accumulators for detailed documentation
0182    */
0183   template <atomicity Atomicity, typename Arithmetic>
0184   struct WeightedSumAccumulator
0185       : GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity, WeightedProduct> {
0186     using GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity,
0187                              WeightedProduct>::GenericAccumulator;
0188     Arithmetic sum() const { return this->value(); }
0189   };
0190 
0191   /**
0192    * WeightedSquareAccumulator. A WeightedSquareAccumulator is an Accumulator storing a weighted sum of squared values.
0193    * It basically takes a pair (value, weight) as input and sums weight*value*value
0194    * @see Gaudi::Accumulators for detailed documentation
0195    */
0196   template <atomicity Atomicity, typename Arithmetic = double>
0197   struct WeightedSquareAccumulator
0198       : GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity, WeightedSquare> {
0199     using GenericAccumulator<std::pair<Arithmetic, Arithmetic>, Arithmetic, Atomicity,
0200                              WeightedSquare>::GenericAccumulator;
0201     Arithmetic sum2() const { return this->value(); }
0202   };
0203 
0204   /**
0205    * WeightedAveragingAccumulator. An WeightedAveragingAccumulator is an Accumulator able to compute an average
0206    * This implementation takes a pair (value, weight) as input
0207    * @see Gaudi::Accumulators for detailed documentation
0208    */
0209   template <atomicity Atomicity, typename Arithmetic>
0210   using WeightedAveragingAccumulator =
0211       AveragingAccumulatorBase<Atomicity, Arithmetic, WeightedCountAccumulator, WeightedSumAccumulator>;
0212 
0213   /**
0214    * WeightedSigmaAccumulator. A WeightedSigmaAccumulator is an Accumulator able to compute an average and variance/rms
0215    * This implementation takes a pair (value, weight) as input
0216    * @see Gaudi::Accumulators for detailed documentation
0217    */
0218   template <atomicity Atomicity, typename Arithmetic>
0219   using WeightedSigmaAccumulator =
0220       SigmaAccumulatorBase<Atomicity, Arithmetic, WeightedAveragingAccumulator, WeightedSquareAccumulator>;
0221 
0222   /**
0223    * Definition of a default type of Histogram Axis
0224    * It contains number of bins, min and max value plus a title
0225    * and defines the basic type of Axis (non log)
0226    * It may also contain labels for the bins
0227    */
0228   template <typename Arithmetic>
0229   class Axis {
0230   public:
0231     using ArithmeticType = Arithmetic;
0232     Axis( unsigned int nBins = 0, Arithmetic minValue = Arithmetic{}, Arithmetic maxValue = Arithmetic{},
0233           std::string title = {}, std::vector<std::string> labels = {} )
0234         : m_title( std::move( title ) )
0235         , nBins( nBins )
0236         , m_minValue( minValue )
0237         , m_maxValue( maxValue )
0238         , m_labels( std::move( labels ) ) {
0239       details::requireValidTitle( m_title );
0240       recomputeRatio();
0241       for ( const auto& s : m_labels ) details::requireValidTitle( s );
0242     }
0243     explicit Axis( Gaudi::Histo1DDef const& def )
0244         : Axis( (unsigned int)def.bins(), def.lowEdge(), def.highEdge(), def.title() ) {}
0245 
0246     /// returns the bin number for a given value, ranging from 0 (underflow) to nBins+1 (overflow)
0247     unsigned int index( Arithmetic value ) const {
0248       // In case we use integer as Arithmetic type, we cannot use ratio for computing indices,
0249       // as ratios < 1.0 will simply be 0, so we have to pay the division in such a case
0250       int idx;
0251       if constexpr ( std::is_integral_v<Arithmetic> ) {
0252         idx = ( ( value - m_minValue ) * nBins / ( m_maxValue - m_minValue ) ) + 1;
0253       } else {
0254         idx = std::floor( ( value - m_minValue ) * m_ratio ) + 1;
0255       }
0256       return idx < 0 ? 0 : ( (unsigned int)idx > numBins() ? numBins() + 1 : (unsigned int)idx );
0257     }
0258 
0259     friend std::ostream& operator<<( std::ostream& o, Axis const& axis ) {
0260       // Note that we print python code here, as the generic toStream implementation uses this
0261       // operator to generate python code.
0262       o << "(" << axis.numBins() << ", " << axis.minValue() << ", " << axis.maxValue() << ", "
0263         << "\"" << axis.m_title << "\", (";
0264       for ( auto const& label : axis.m_labels ) { o << "\"" << label << "\", "; }
0265       return o << "))";
0266     }
0267 
0268     /// says whether the given value is within the range of the axis
0269     bool inAcceptance( Arithmetic value ) const { return value >= m_minValue && value <= m_maxValue; }
0270 
0271     // accessors
0272     unsigned int numBins() const { return nBins; }
0273     void         setNumBins( unsigned int n ) {
0274       nBins = n;
0275       recomputeRatio();
0276     }
0277     Arithmetic minValue() const { return m_minValue; }
0278     void       setMinValue( Arithmetic v ) {
0279       m_minValue = v;
0280       recomputeRatio();
0281     }
0282     Arithmetic maxValue() const { return m_maxValue; }
0283     void       setMaxValue( Arithmetic v ) {
0284       m_maxValue = v;
0285       recomputeRatio();
0286     }
0287     std::string const&             title() const { return m_title; }
0288     void                           setTitle( std::string const& t ) { m_title = t; }
0289     std::vector<std::string> const labels() const { return m_labels; }
0290 
0291   private:
0292     /// title of this axis
0293     std::string m_title;
0294 
0295   public:
0296     /// number of bins for this Axis
0297     /// FIXME : should be private and called m_nBins but will break backward compatibility with previous implementation.
0298     unsigned int nBins;
0299 
0300   private:
0301     /// min and max values on this axis
0302     Arithmetic m_minValue, m_maxValue;
0303     /**
0304      * precomputed ratio to convert a value into bin number
0305      * equal to nBins/(maxValue-minValue). Only used for floating Arithmetic
0306      */
0307     Arithmetic m_ratio;
0308     /// labels for the bins
0309     std::vector<std::string> m_labels;
0310 
0311     void recomputeRatio() {
0312       m_ratio = ( m_maxValue == m_minValue ) ? Arithmetic{} : nBins / ( m_maxValue - m_minValue );
0313     }
0314   };
0315 
0316   /// automatic conversion of the Axis type to json
0317   template <typename Arithmetic>
0318   void to_json( nlohmann::json& j, const Axis<Arithmetic>& axis ) {
0319     j = nlohmann::json{ { "nBins", axis.numBins() },
0320                         { "minValue", axis.minValue() },
0321                         { "maxValue", axis.maxValue() },
0322                         { "title", axis.title() } };
0323     if ( !axis.labels().empty() ) { j["labels"] = axis.labels(); }
0324   }
0325 
0326   /**
0327    * small class used as InputType for regular Histograms
0328    * basically a tuple of the given values, specialized in case of a single
0329    * entry so that the syntax is more natural.
0330    * NIndex should be lower than number of Arithmetic types and denotes the
0331    * number of items used as index. There can typically be one more type in
0332    * the list for profile histograms, not use as index on an axis
0333    * ValueType is the actual type used to fill the histogram, that is
0334    * the ArithmeticTuple reduced to NIndex items
0335    *
0336    * Note : the specialization is only needed to ensure backward compatibility
0337    * with previous implementation where there was only one Arithmetic type common
0338    * to all axis. It should in principal be the one and only implementation
0339    * FIXME : remove specialization when client code was adapted
0340    */
0341   template <typename Arithmetic, unsigned int NIndex>
0342   struct HistoInputType : HistoInputType<make_tuple_t<Arithmetic, NIndex>, NIndex> {
0343     using HistoInputType<make_tuple_t<Arithmetic, NIndex>, NIndex>::HistoInputType;
0344   };
0345   template <unsigned int NIndex, typename... Elements>
0346   struct HistoInputType<std::tuple<Elements...>, NIndex> : std::tuple<Elements...> {
0347     using InternalType = std::tuple<Elements...>;
0348     using ValueType    = HistoInputType<SubTuple_t<InternalType, NIndex>, NIndex>;
0349     using std::tuple<Elements...>::tuple;
0350     template <class... AxisType>
0351       requires( sizeof...( AxisType ) == NIndex )
0352     unsigned int computeIndex( std::tuple<AxisType...> const& axis ) const {
0353       return computeIndexInternal<0, std::tuple<AxisType...>>( axis );
0354     }
0355     template <class... AxisType>
0356       requires( sizeof...( AxisType ) == NIndex )
0357     static unsigned int computeTotNBins( std::tuple<AxisType...> const& axis ) {
0358       return computeTotNBinsInternal<0, std::tuple<AxisType...>>( axis );
0359     }
0360     auto forInternalCounter() const { return 1ul; }
0361     template <class... AxisType>
0362       requires( sizeof...( AxisType ) == NIndex )
0363     bool inAcceptance( std::tuple<AxisType...> const& axis ) const {
0364       return inAcceptanceInternal<0, std::tuple<AxisType...>>( axis );
0365     }
0366 
0367   private:
0368     template <int N, class Tuple>
0369     unsigned int computeIndexInternal( Tuple const& allAxis ) const {
0370       // compute global index. Bins are stored in a column first manner
0371       auto const&  axis       = std::get<N>( allAxis );
0372       unsigned int localIndex = axis.index( std::get<N>( *this ) );
0373       if constexpr ( N + 1 == NIndex )
0374         return localIndex;
0375       else
0376         return localIndex + ( axis.numBins() + 2 ) * computeIndexInternal<N + 1, Tuple>( allAxis );
0377     }
0378     template <int N, class Tuple>
0379     static unsigned int computeTotNBinsInternal( Tuple const& allAxis ) {
0380       auto const&  axis       = std::get<N>( allAxis );
0381       unsigned int localNBins = axis.numBins() + 2;
0382       if constexpr ( N + 1 == NIndex )
0383         return localNBins;
0384       else
0385         return localNBins * computeTotNBinsInternal<N + 1, Tuple>( allAxis );
0386     }
0387     template <int N, class Tuple>
0388     bool inAcceptanceInternal( Tuple const& allAxis ) const {
0389       auto const& axis        = std::get<N>( allAxis );
0390       bool        localAnswer = axis.inAcceptance( std::get<N>( *this ) );
0391       if constexpr ( N + 1 == NIndex )
0392         return localAnswer;
0393       else
0394         return localAnswer || inAcceptanceInternal<N + 1, Tuple>( allAxis );
0395     }
0396   };
0397 
0398   /**
0399    * small class used as InputType for weighted Histograms
0400    * only a pair of the InnerType and the weight.
0401    * See description of HistoInputType for more details
0402    */
0403   template <typename ArithmeticTuple, unsigned int NIndex, typename WArithmetic>
0404   struct WeightedHistoInputType : std::pair<HistoInputType<ArithmeticTuple, NIndex>, WArithmetic> {
0405     using ValueType = typename HistoInputType<ArithmeticTuple, NIndex>::ValueType;
0406     using std::pair<HistoInputType<ArithmeticTuple, NIndex>, WArithmetic>::pair;
0407     template <class... AxisType>
0408       requires( sizeof...( AxisType ) == NIndex )
0409     unsigned int computeIndex( std::tuple<AxisType...> const& axis ) const {
0410       return this->first.computeIndex( axis );
0411     }
0412     template <class... AxisType>
0413       requires( sizeof...( AxisType ) == NIndex )
0414     static unsigned int computeTotNBins( std::tuple<AxisType...> const& axis ) {
0415       return HistoInputType<ArithmeticTuple, NIndex>::computeTotNBins( axis );
0416     }
0417     auto forInternalCounter() const { return std::pair( this->first.forInternalCounter(), this->second ); }
0418     template <class... AxisType>
0419       requires( sizeof...( AxisType ) == NIndex )
0420     bool inAcceptance( std::tuple<AxisType...> const& axis ) const {
0421       return this->first.inAcceptance( axis );
0422     }
0423   };
0424 
0425   /**
0426    * Internal Accumulator class dealing with Histograming. Templates parameters are :
0427    *  - Atomicity : none or full
0428    *  - InputType : a class holding a value given as input of the Histogram,
0429    *    and able to answer questions on this value given a number of axis matching
0430    *    the type of value.
0431    *    e.g. it would hold a pair of double for a non weighted 2D histogram or
0432    *    a pair of triplet of doubles and double for the weighted 3D histogram.
0433    *    Example implementations are (Weighted)HistoInputType.
0434    *    This class must define :
0435    *      + a constructor taking a set of value to build the InputType
0436    *      + a static method `unsigned int computeTotNBins( std::tuple<AxisType...> const& )`
0437    *        able to compute the total number of bins needed with this input type and
0438    *        these axis. It will typically be the product of the number of bins for each
0439    *        dimension, potentially increased by 2 for each if underflow and overflow
0440    *        is supported
0441    *      + a type ValueType alias defining the type of the input values to give to InputType
0442    *        This type needs to implement :
0443    *        * a method
0444    *          `unsigned int computeIndex( std::tuple<AxisType...> const& ) const`
0445    *          able to compute the bin corresponding to a given value
0446    *        * a method `auto forInternalCounter() const` returning the value to be used to
0447    *          inscrease the accumulator dealing with the bin associated with the current value.
0448    *          In most simple cases, it return `Arithmetic{}` or even `1` but for weighted
0449    *          histograms, it returns a pair with the weight as second item
0450    *        * in case of usage within a RootHistogram, it should also define a method
0451    *          `bool inAcceptance( std::tuple<AxisType...> const& )` checking whether a given
0452    *          value in within the range of the accumulator
0453    *  - Arithmetic : the arithmetic type used for values stored inside the histogram
0454    *    e.g. unsigned int for regular histogram as we only count entries, or float/double
0455    *    for weighted histograms, as we store actual sums of original values
0456    *  - BaseAccumulator : the underlying accumulator used in each bin
0457    *  - AxisTupleType : the types of the axis as a tuple. Its length defines the dimension
0458    *    of the Histogram this accumulator handles.
0459    *    The constraints on the AxisType are : FIXME use concepts when available
0460    *      + that they can be copied
0461    *      + that they have a ArithmeticType type alias
0462    *      + that they have a `unsigned int numBins() const` method
0463    *      + that they have a friend operator<< using std::ostream for printing
0464    *      + that they have a friend to_json method using nlohmann library
0465    *      + that they implement whatever is needed by the computeIndex and computeTotNBins methods
0466    *         of the InputType used. Plus the inAcceptance one if Roothistograms are used
0467    *    A default Axis class is provided for most cases
0468    * This accumulator is simply an array of BaseAccumulator, one per bin.
0469    */
0470   template <atomicity Atomicity, typename InputType, typename Arithmetic,
0471             template <atomicity Ato, typename Arith> typename BaseAccumulatorT, typename AxisTupleType>
0472   class HistogramingAccumulatorInternal {
0473     template <atomicity, typename, typename, template <atomicity, typename> typename, typename>
0474     friend class HistogramingAccumulatorInternal;
0475 
0476   public:
0477     using ND                      = std::integral_constant<unsigned int, std::tuple_size_v<AxisTupleType>>;
0478     using BaseAccumulator         = BaseAccumulatorT<Atomicity, Arithmetic>;
0479     using AxisTupleArithmeticType = typename InputType::ValueType;
0480     HistogramingAccumulatorInternal( AxisTupleType axis )
0481         : m_axis{ axis }
0482         , m_totNBins{ InputType::computeTotNBins( m_axis ) }
0483         , m_value( new BaseAccumulator[m_totNBins] ) {
0484       reset();
0485     }
0486     template <atomicity ato>
0487     HistogramingAccumulatorInternal(
0488         construct_empty_t,
0489         const HistogramingAccumulatorInternal<ato, InputType, Arithmetic, BaseAccumulatorT, AxisTupleType>& other )
0490         : m_axis( other.m_axis ), m_totNBins{ other.m_totNBins }, m_value( new BaseAccumulator[m_totNBins] ) {
0491       reset();
0492     }
0493     [[deprecated( "Use `++h1[x]`, `++h2[{x,y}]`, etc. instead." )]] HistogramingAccumulatorInternal&
0494     operator+=( InputType v ) {
0495       accumulator( v.computeIndex( m_axis ) ) += v.forInternalCounter();
0496       return *this;
0497     }
0498     void reset() {
0499       for ( unsigned int index = 0; index < m_totNBins; index++ ) { accumulator( index ).reset(); }
0500     }
0501     template <atomicity ato>
0502     void mergeAndReset(
0503         HistogramingAccumulatorInternal<ato, InputType, Arithmetic, BaseAccumulatorT, AxisTupleType>& other ) {
0504       assert( m_totNBins == other.m_totNBins );
0505       for ( unsigned int index = 0; index < m_totNBins; index++ ) {
0506         accumulator( index ).mergeAndReset( other.accumulator( index ) );
0507       }
0508     }
0509     [[nodiscard]] auto operator[]( typename InputType::ValueType v ) {
0510       return Buffer<BaseAccumulatorT, Atomicity, Arithmetic>{ accumulator( v.computeIndex( m_axis ) ) };
0511     }
0512 
0513     template <unsigned int N>
0514     auto& axis() const {
0515       return std::get<N>( m_axis );
0516     }
0517     auto& axis() const { return m_axis; }
0518     auto  binValue( unsigned int i ) const { return accumulator( i ).value(); }
0519     auto  nEntries( unsigned int i ) const { return accumulator( i ).nEntries(); }
0520     auto  totNBins() const { return m_totNBins; }
0521 
0522     // FIXME These methods are there for backwrad compatibility with previous implementation
0523     // where all Axis had to be of type Axis<...> and were stored in an array
0524     // Newer code should call axis<N>().foo for whatever foo is defined in that axis type
0525     auto nBins( unsigned int i ) const { return _getAxis( i, std::integral_constant<size_t, 0>() ).numBins(); }
0526     auto minValue( unsigned int i ) const { return _getAxis( i, std::integral_constant<size_t, 0>() ).minValue(); }
0527     auto maxValue( unsigned int i ) const { return _getAxis( i, std::integral_constant<size_t, 0>() ).maxValue(); }
0528 
0529   private:
0530     BaseAccumulator& accumulator( unsigned int index ) const {
0531       assert( index < m_totNBins );
0532       assert( m_value.get() );
0533       return m_value[index];
0534     }
0535 
0536     // FIXME Only used for backward compatibility. should be dropped at some stage
0537     // Can only work if all axis have same type, which is no more the case
0538     std::tuple_element_t<0, AxisTupleType> const& _getAxis( size_t i,
0539                                                             typename std::tuple_size<AxisTupleType>::type ) const {
0540       throw std::logic_error(
0541           fmt::format( "Retrieving axis {} in Histogram of dimension {}", i, std::tuple_size_v<AxisTupleType> ) );
0542     }
0543     template <size_t N>
0544       requires( std::tuple_size_v<AxisTupleType> != N )
0545     auto& _getAxis( size_t i, std::integral_constant<size_t, N> ) const {
0546       if ( i == N ) return std::get<N>( m_axis );
0547       return _getAxis( i, std::integral_constant<size_t, N + 1>() );
0548     }
0549 
0550     /// set of Axis of this Histogram
0551     AxisTupleType m_axis;
0552     /// total number of bins in this histogram, under and overflow included
0553     unsigned int m_totNBins{};
0554     /// Histogram content
0555     std::unique_ptr<BaseAccumulator[]> m_value;
0556   };
0557 
0558   /**
0559    * Class implementing a regular histogram accumulator
0560    *
0561    * Actually only an alias to HistogramingAccumulatorInternal with proper template parameters
0562    */
0563   template <atomicity Atomicity, typename Arithmetic, typename ND, typename AxisTupleType>
0564   using HistogramingAccumulator =
0565       HistogramingAccumulatorInternal<Atomicity, HistoInputType<AxisToArithmetic_t<AxisTupleType>, ND::value>,
0566                                       unsigned long, IntegralAccumulator, AxisTupleType>;
0567 
0568   /**
0569    * Class implementing a weighted histogram accumulator
0570    *
0571    * Actually only an alias to HistogramingAccumulatorInternal with proper template parameters
0572    */
0573   template <atomicity Atomicity, typename Arithmetic, typename ND, typename AxisTupleType>
0574   using WeightedHistogramingAccumulator =
0575       HistogramingAccumulatorInternal<Atomicity,
0576                                       WeightedHistoInputType<AxisToArithmetic_t<AxisTupleType>, ND::value, Arithmetic>,
0577                                       Arithmetic, WeightedCountAccumulator, AxisTupleType>;
0578 
0579   /**
0580    * Class implementing a profile histogram accumulator
0581    *
0582    * Actually only an alias to HistogramingAccumulatorInternal with proper template parameters
0583    */
0584   template <atomicity Atomicity, typename Arithmetic, typename ND, typename AxisTupleType>
0585   using ProfileHistogramingAccumulator =
0586       HistogramingAccumulatorInternal<Atomicity,
0587                                       HistoInputType<ProfileAxisToArithmetic_t<Arithmetic, AxisTupleType>, ND::value>,
0588                                       Arithmetic, SigmaAccumulator, AxisTupleType>;
0589 
0590   /**
0591    * Class implementing a weighted profile histogram accumulator
0592    *
0593    * Actually only an alias to HistogramingAccumulatorInternal with proper template parameters
0594    */
0595   template <atomicity Atomicity, typename Arithmetic, typename ND, typename AxisTupleType>
0596   using WeightedProfileHistogramingAccumulator = HistogramingAccumulatorInternal<
0597       Atomicity, WeightedHistoInputType<ProfileAxisToArithmetic_t<Arithmetic, AxisTupleType>, ND::value, Arithmetic>,
0598       Arithmetic, WeightedSigmaAccumulator, AxisTupleType>;
0599 
0600   /**
0601    * A base counter dealing with Histograms
0602    *
0603    * Main features of that Counter :
0604    *  - can be any number of dimensions. The dimension is its first template parameter
0605    *  - for each dimension, an Axis is associated. Axis can be of any type depending
0606    *    on the underlying accumulator
0607    *  - the constructor expects one extra argument per axis, typically a tuple
0608    *    of values allowing to create the Axis objects in the back
0609    *  - the operator+= takes either an array of values (one per dimension)
0610    *    or a tuple<array of values, weight>. The value inside the bin
0611    *    corresponding to the given values is then increased by 1 or weight
0612    *  - the prefered syntax is to avoid operator+= and use operator[] to get a
0613    *    buffer on the bin you're updating. Syntax becomes :
0614    *        ++counter[{x,y}]   or   wcounter[{x,y}] += w
0615    *  - the Counter is templated by the types of the values given to
0616    *    operator+ and also by the type stored into the bins
0617    *  - the counter can be atomic or not and supports buffering. Note that
0618    *    the atomicity is classical eventual consistency. So each bin is
0619    *    atomically updated but bins are not garanted to be coherent when
0620    *    reading all of them back
0621    *  - profile histograms are also supported, operator+= takes one more
0622    *    value in the array of values in that case
0623    *
0624    * This base class is then aliases for the 4 standard cases of Histogram,
0625    * WeightedHistogram, ProfileHistogram and WeightedProfileHistogram.
0626    * For all predefined Histogram types, the axis type is a simple triplet
0627    * of values nbins, minValue, maxValue plus a title.
0628    *
0629    * Typical usage :
0630    * \code
0631    * Histogram<2, double, atomicity::full>
0632    *   counter{owner, "CounterName", "HistoTitle", {{nBins1, minVal1, maxVal1}, {nBins2, minVal2, maxVal2,
0633    * "AxisTitle"}}};
0634    * ++counter[{val1, val2}];    // prefered syntax
0635    * counter += {val1, val2};    // original syntax inherited from counters
0636    *
0637    * WeightedHistogram<2, double, atomicity::full>
0638    *   wcounter{owner, "CounterName", "HistoTitle", {{nBins1, minVal1, maxVal1}, {nBins2, minVal2, maxVal2,
0639    * "AxisTitle"}}}; wcounter[{val1, val2}] += w;    // prefered syntax wcounter += {{val1, val2}, w};  // original
0640    * syntax inherited from counters \endcode
0641    *
0642    * When serialized to json, this counter uses new types histogram:Histogram:<prec>, histogram:ProfileHistogram:<prec>,
0643    * histogram:WeightedHistogram:<prec> and histrogram:WeightedProfileHistogram:<prec>
0644    * <prec> id described in Accumulators.h and decribes here the precision of the bin data.
0645    * All these types have the same fields, namely :
0646    *   dimension(integer), title(string), empty(bool), nEntries(integer), axis(array), bins(array)
0647    * where :
0648    *     + axis is an array of tuples, one per dimension, with content (nBins(integer), minValue(number),
0649    * maxValue(number), title(string)) for the default type of Axis
0650    *     + bins is an array of values
0651    *       - The length of the array is the product of (nBins+2) for all axis
0652    *       - the +2 is because the bin 0 is the one for values below minValue and bin nBins+1 is the one for values
0653    * above maxValue bins are stored row first so we iterate first on highest dimension
0654    *       - the value is a number for non profile histograms
0655    *       - the value is of the form ( (nEntries(integer), sum(number) ), sum2(number) ) for profile histograms
0656    *         Note the pair with a pair as first entry
0657    */
0658   template <unsigned int ND, atomicity Atomicity, typename Arithmetic, const char* Type,
0659             template <atomicity, typename, typename, typename> typename Accumulator, typename AxisTupleType>
0660   class HistogramingCounterBase;
0661   template <unsigned int ND, atomicity Atomicity, typename Arithmetic, const char* Type,
0662             template <atomicity, typename, typename, typename> typename Accumulator, typename... AxisTypes>
0663   class HistogramingCounterBase<ND, Atomicity, Arithmetic, Type, Accumulator, std::tuple<AxisTypes...>>
0664       : public BufferableCounter<Atomicity, Accumulator, Arithmetic, std::integral_constant<unsigned int, ND>,
0665                                  std::tuple<AxisTypes...>> {
0666   public:
0667     using AxisTupleType    = std::tuple<AxisTypes...>;
0668     using NumberDimensions = std::integral_constant<unsigned int, ND>;
0669     using Parent           = BufferableCounter<Atomicity, Accumulator, Arithmetic, NumberDimensions, AxisTupleType>;
0670     using AccumulatorType  = Accumulator<Atomicity, Arithmetic, NumberDimensions, AxisTupleType>;
0671     using AxisTupleArithmeticType = typename AccumulatorType::AxisTupleArithmeticType;
0672     /// for backward compatibility with previous implementation, should not be used FIXME
0673     using AxisArithmeticType = typename std::tuple_element<0, AxisTupleType>::type::ArithmeticType;
0674     inline static const std::string typeString{ std::string{ Type } + ':' + typeid( Arithmetic ).name() };
0675     /// This constructor takes the axis as a tuple
0676     template <typename OWNER>
0677     HistogramingCounterBase( OWNER* owner, std::string const& name, std::string const& title, AxisTupleType axis )
0678         : Parent( owner, name, *this, axis ), m_title( title ) {
0679       details::requireValidTitle( m_title );
0680     }
0681     /// This constructor takes the axis one by one, when ND >= 2. If ND = 1, the other one can be used
0682     template <typename OWNER>
0683     HistogramingCounterBase( OWNER* owner, std::string const& name, std::string const& title, AxisTypes... allAxis )
0684         : HistogramingCounterBase( owner, name, title, std::make_tuple( allAxis... ) ) {}
0685     using Parent::print;
0686     template <typename stream>
0687     stream& printImpl( stream& o, bool /*tableFormat*/ ) const {
0688       o << ND << "D Histogram with config ";
0689       std::apply( [&o]( auto&&... args ) { ( ( o << args << "\n" ), ... ); }, this->axis() );
0690       return o;
0691     }
0692     std::ostream& print( std::ostream& o, bool tableFormat = false ) const override {
0693       return printImpl( o, tableFormat );
0694     }
0695     MsgStream&   print( MsgStream& o, bool tableFormat = false ) const override { return printImpl( o, tableFormat ); }
0696     friend void  reset( HistogramingCounterBase& c ) { c.reset(); }
0697     friend void  mergeAndReset( HistogramingCounterBase& h, HistogramingCounterBase& o ) { h.mergeAndReset( o ); }
0698     friend void  to_json( nlohmann::json& j, HistogramingCounterBase const& h ) { h.to_json( j ); }
0699     virtual void to_json( nlohmann::json& j ) const {
0700       // get all bin values and compute total nbEntries
0701       std::vector<typename AccumulatorType::BaseAccumulator::OutputType> bins;
0702       bins.reserve( this->totNBins() );
0703       unsigned long totNEntries{ 0 };
0704       for ( unsigned int i = 0; i < this->totNBins(); i++ ) {
0705         bins.push_back( this->binValue( i ) );
0706         totNEntries += this->nEntries( i );
0707       }
0708       // build json
0709       j = { { "type", std::string( Type ) + ":" + typeid( Arithmetic ).name() },
0710             { "title", m_title },
0711             { "dimension", ND },
0712             { "empty", totNEntries == 0 },
0713             { "nEntries", totNEntries },
0714             { "axis", this->axis() },
0715             { "bins", bins } };
0716     }
0717     std::string const& title() const { return m_title; }
0718 
0719   protected:
0720     std::string const m_title;
0721   };
0722 
0723   namespace naming {
0724     inline constexpr char histogramString[]                = "histogram:Histogram";
0725     inline constexpr char weightedHistogramString[]        = "histogram:WeightedHistogram";
0726     inline constexpr char profilehistogramString[]         = "histogram:ProfileHistogram";
0727     inline constexpr char weightedProfilehistogramString[] = "histogram:WeightedProfileHistogram";
0728   } // namespace naming
0729 
0730   /// standard static histograming counter. See HistogramingCounterBase for details
0731   template <unsigned int ND, atomicity Atomicity = atomicity::full, typename Arithmetic = double,
0732             typename AxisTupleType = make_tuple_t<Axis<Arithmetic>, ND>>
0733   using StaticHistogram = HistogramingCounterBase<ND, Atomicity, Arithmetic, naming::histogramString,
0734                                                   HistogramingAccumulator, AxisTupleType>;
0735 
0736   /// standard static histograming counter with weight. See HistogramingCounterBase for details
0737   template <unsigned int ND, atomicity Atomicity = atomicity::full, typename Arithmetic = double,
0738             typename AxisTupleType = make_tuple_t<Axis<Arithmetic>, ND>>
0739   using StaticWeightedHistogram = HistogramingCounterBase<ND, Atomicity, Arithmetic, naming::weightedHistogramString,
0740                                                           WeightedHistogramingAccumulator, AxisTupleType>;
0741 
0742   /// profile static histograming counter. See HistogramingCounterBase for details
0743   template <unsigned int ND, atomicity Atomicity = atomicity::full, typename Arithmetic = double,
0744             typename AxisTupleType = make_tuple_t<Axis<Arithmetic>, ND>>
0745   using StaticProfileHistogram = HistogramingCounterBase<ND, Atomicity, Arithmetic, naming::profilehistogramString,
0746                                                          ProfileHistogramingAccumulator, AxisTupleType>;
0747 
0748   /// weighted static profile histograming counter. See HistogramingCounterBase for details
0749   template <unsigned int ND, atomicity Atomicity = atomicity::full, typename Arithmetic = double,
0750             typename AxisTupleType = make_tuple_t<Axis<Arithmetic>, ND>>
0751   using StaticWeightedProfileHistogram =
0752       HistogramingCounterBase<ND, Atomicity, Arithmetic, naming::weightedProfilehistogramString,
0753                               WeightedProfileHistogramingAccumulator, AxisTupleType>;
0754 
0755 } // namespace Gaudi::Accumulators