Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-15 10:14:14

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 
0012 #pragma once
0013 
0014 #include <Gaudi/MonitoringHub.h>
0015 
0016 #include <chrono>
0017 
0018 namespace Gaudi::Accumulators {
0019   template <class Rep, class Period>
0020   auto sqrt( std::chrono::duration<Rep, Period> d );
0021   template <class Rep1, class Rep2, class Period>
0022   auto operator*( const std::chrono::duration<Rep1, Period>& lhs, const std::chrono::duration<Rep2, Period>& rhs );
0023 } // namespace Gaudi::Accumulators
0024 
0025 // Fix builds with GCC 11 and C++20
0026 #if defined( __GNUC__ ) && ( __GNUC__ == 11 ) && !defined( __clang__ )
0027 namespace std::chrono {
0028   template <typename Ratio>
0029   static constexpr const char* suffix( const Ratio& ) {
0030     if constexpr ( std::ratio_equal_v<Ratio, std::ratio<1>> ) {
0031       return "s";
0032     } else if constexpr ( std::ratio_equal_v<Ratio, std::milli> ) {
0033       return "ms";
0034     } else if constexpr ( std::ratio_equal_v<Ratio, std::micro> ) {
0035       return "us";
0036     } else if constexpr ( std::ratio_equal_v<Ratio, std::nano> ) {
0037       return "ns";
0038     } else if constexpr ( std::ratio_equal_v<Ratio, std::ratio<60>> ) {
0039       return "m";
0040     } else if constexpr ( std::ratio_equal_v<Ratio, std::ratio<3600>> ) {
0041       return "h";
0042     }
0043     return "";
0044   }
0045   template <class Rep, class Period>
0046   std::ostream& operator<<( std::ostream& os, const std::chrono::duration<Rep, Period>& d ) {
0047     return os << d.count() << suffix( Period{} );
0048   }
0049 } // namespace std::chrono
0050 #endif
0051 
0052 /**
0053  * @namespace Gaudi::Accumulators
0054  *
0055  * Efficient counter implementations for Gaudi.
0056  *
0057  * A number of concepts and templated classes are defined:
0058  *
0059  * Concepts
0060  * --------
0061  *
0062  *   - Accumulator : object accumulating some data in some way.
0063  *       examples : counters, sum of squares accumulator,
0064  *                  minimum accumulator (keeps minimum of all values)
0065  *   - Atomicity : the atomicity of an accumulator. Can be none or full.
0066  *       "none" means that the accumulator is not thread safe and should
0067  *       only be used locally within a thread. "full" means that the
0068  *       accumulator is thread safe, but that you pay the price of atomics
0069  *       inside. Note that this price may be reduced by the usage of the
0070  *       Buffer class, see below.
0071  *   - InputTransform : a transformation to be applied to the input of an Accumulator.
0072  *       for example "elevation to power 2" for a sum of squares Accumulator,
0073  *       "identity" for a sum Accumulator or a minimum Accumulator,
0074  *       "constant 1" for a pure counter
0075  *   - OutputTransform : a transformation to be applied to the value of an
0076  *       Accumulator when reading it. Usually identity. You can think of
0077  *       square root for an RMS Accumulator (accumulating squares internally)
0078  *   - ValueHandler : the class handling the internal value of an Accumulator,
0079  *       and responsible for updating it with a new input. It defines the initial
0080  *       default value, the storage type used (depending on atomicity) and the
0081  *       actual update operation (sum, keep min or max typically).
0082  *   - AccumulatorSet : an Accumulator defined as a set of Accumulators with
0083  *       same InputType and Atomicity. Updating it means updating all Accumulators
0084  *       of the set with the same value. E.g. a set with Counter and Sum will
0085  *       be able to compute some average
0086  *   - Buffer : a wrapper around a thread safe Accumulator that allows to accumulate
0087  *       locally (in a non thread safe Accumulator) data before merging into the original one
0088  *       when Buffer is destructed. To be use when accumulators are updated in tight loops
0089  *   - Counter : a higher level object that is an Accumulator and provides extra methods,
0090  *       most notably a way to print itself via operator<<, a print method and a
0091  *       buffer method to retrieve a Buffer on top of it.
0092  *
0093  *
0094  * Classes and helper functions
0095  * ----------------------------
0096  *
0097  *   - many classes are directly linked to concepts :
0098  *     + Constant, Identity, Square  are InputTransforms / OutputTransforms
0099  *     + BaseValueHandler : a base class for ValueHandlers Adder, Minimum and Maximum
0100  *     + Adder, Extremum, Minimum, Maximum, Binomial are ValueHandlers
0101  *     + GenericAccumulator : implements a generic Accumulator. See class definition for details
0102  *     + AccumulatorSet binds together a set of Accumulators into a new accumulator
0103  *     + Buffer implements the local buffering of an Accumulator
0104  *
0105  *   - some classes implements the most common Accumulators :
0106  *     + MaxAccumulator : keeps the max, has a max() method
0107  *     + MinAccumulator : keeps the min, has a min() method
0108  *     + CountAccumulator : keeps count of number of values, has a nEntries() method
0109  *     + SumAccumulator : keeps the sum of all values, has a sum() method
0110  *     + IntegralAccumulator : similar to CountAccumulator and SumAccumulator for integers, has a nEntries() and a sum()
0111  * method
0112  *     + SquareAccumulator : keeps the sum of all values squared, has a sum2() method
0113  *     + TrueAccumulator : keeps a count of the number of true values, has a nTrueEntries() method
0114  *     + FalseAccumulator : keeps a count of the number of false values, has a nFalseEntries() method
0115  *     + BinomialAccumulator : set of TrueAccumulator and FalseAccumulator. Has extra
0116  *       nEntries(), efficiency() and efficiencyErr() methods
0117  *     + AveragingAccumulator : set of CountAccumulator and SumAccumulator. Has an extra mean() method
0118  *     + SigmaAccumulator : set of AveragingAccumulator and SquareAccumulator. Has extra
0119  *       methods variance(), standard_deviation() and meanErr()
0120  *     + MsgCounter: keeps a count of number of values, and prints the first N times it
0121  *       is incremented at a specified logging level
0122  *     + StatAccumulator : set of SigmaAccumulator, MinAccumulator and MaxAccumulator
0123  *
0124  *   - some classes implement the most common Counters :
0125  *     + PrintableCounter is the interface to be used for abstract counters that can
0126  *       be printed
0127  *     + BufferableCounter is the base class for counters that can be buffered. It
0128  *       provides a buffer method returning a Buffer on the current counter
0129  *     + Counter, AveragingCounter, SigmaCounter, StatCounter, BinomialCounter : standard
0130  *       counters based on the corresponding accumulators
0131  *     + StatEntity : a counter meant to be backward compatible with the old one
0132  *       and actually being a set of StatAccumulator and BinomialAccumulator.
0133  *       StatEntity should not be used and should be dropped when ancient code has been
0134  *       ported to the other counters
0135  *     + MsgCounter : a counter logging a given message up to the given number of occurrences,
0136  *       then loggin that it stops logging but still counting occurences
0137  *
0138  * (De)Serialization
0139  * -----------------
0140  *
0141  * All counters are serialized into JSON strings using nlohmann's json library (pure header)
0142  * See https://github.com/nlohmann/json for details, but here are few notes :
0143  *   - all Arithmetic types used in Counters need to be serializable with this library
0144  *   - standard types (STL included) already are
0145  *   - for custom types, one needs to implement a to_json method along these lines :
0146  * @code
0147  *   void to_json(json& j, const Person& p) {
0148  *       j = nlohmann::json{{"name", p.name}, {"type", "person:basic"}, {"address", p.address}, {"age", p.age}};
0149  *   }
0150  * @endcode
0151  * This method must be declared in your type namespace
0152  *   - for third-party type, it's slightly more complicated and one has to specialize
0153  *     a class called adl_serializer for the given type. Just follow the example given in
0154  *     this file for std::chrono::duration
0155  *
0156  * Symetrically to serialization, all counters objects can be constructed from json objects
0157  * using the same nlohmann's json library via a the fromJSON static method;
0158  *
0159  * The serialized json needs to contain a 'type' entry defining the rest of the json structure.
0160  * If the type value contains a ':' character, then the part preceding it will be considered
0161  * as a namespace. The rest is free text.
0162  *
0163  * Here is a list of types defined in this header and their corresponding fields.
0164  * Note that there are 2 kinds of fields : raw ones and derived ones, built on top of raw ones.
0165  * Note also that <prec> when present is a string defining the precision of the data when numerical. It
0166  * is described at the end of the list
0167  * for each dependent field, the computation formula is given
0168  *   - counter:Counter:<prec> : empty(bool), nEntries(integer)
0169  *   - counter:AveragingCounter:<prec> : same as counter:Counter, sum(number), mean(number) = sum/nEntries
0170  *   - counter:SigmaCounter:<prec> : same as counter:AveragingCounter, sum2(number),
0171  *                                   standard_deviation(number) = sqrt((sum2-sum*sum/nEntries)/(nEntries-1))
0172  *   - counter:StatCounter:<prec> : same as counter:SigmaCounter, min(number), max(number)
0173  *   - counter:BinomialCounter:<prec> : nTrueEntries(integer), nFalseEntries(integer),
0174  *                                      nEntries(integer) = nTrueEntries+nFalseEntries
0175  *                                      efficiency(number) = nTrueEntries/nEntries,
0176  *                                      efficiencyErr(number) = (nTrueEntries*nFalseEntries)/(nEntries*nEntries)
0177  *   - counter:MsgCounter : same as counter:Counter, level(number), max(number), msg(string)
0178  *   - statentity : union of fields of counter:StatCounter and counter:BinomialCounter. DEPRECATED
0179  *   - timer:<prec> : same fields as counter:StatCounter
0180  *   - <prec> is a string defining the type of data in the counter. It is the typeid representation
0181  *     of the type that should be used by a customer to use the data and not lose precision.
0182  *     In other terms, it's usually one char of chstijlmxyfde for respectively char, unsigned char,
0183  *     short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long,
0184  *     float, double, long double
0185  *     Note that it does not have to match the original type, just to be the same precision.
0186  *     E.g. an std::chrono::nanoseconds type can and should be mapped to its internal representation
0187  *     that happens to be long so <prec> will be l.
0188  *
0189  * Notes
0190  * -----
0191  *
0192  * All Accumulators and Counters defined above are provided in their atomic and non atomic versions
0193  *
0194  * Here is an example of the typical usage of these classes :
0195  *
0196  * @code
0197  *  AveragingCounter<> avg;
0198  *  avg += 3;
0199  *  avg += 5;
0200  *  avg += 6;
0201  *  std::cout << avg << std::endl;
0202  *
0203  *  SigmaCounter<> sig;
0204  *  sig += 3;
0205  *  sig += 5;
0206  *  sig += 6;
0207  *  std::cout << sig << std::endl;
0208  *
0209  *  AveragingCounter<float, atomicity::full> avg2;
0210  *  {
0211  *    auto buf = avg2.buffer();
0212  *    for ( int i = 0; i < 1000; i++ ) buf += i;
0213  *    // single update of original counter when buf is destroyed
0214  *  }
0215  *  std::cout << avg2 << std::endl;
0216  *
0217  *  BinomialCounter<> bin;
0218  *  bin += false;
0219  *  bin += true;
0220  *  bin += true;
0221  *  bin += false;
0222  *  bin += false;
0223  *  std::cout << bin << std::endl;
0224  *
0225  *  StatEntity se;
0226  *  se += 3;
0227  *  se += 5;
0228  *  se += 6;
0229  *  std::cout << se << std::endl;
0230  *
0231  * @endcode
0232  */
0233 
0234 #include <atomic>
0235 #include <boost/format.hpp>
0236 #include <cmath>
0237 #include <iostream>
0238 #include <limits>
0239 #include <nlohmann/json.hpp>
0240 #include <sstream>
0241 #include <tuple>
0242 #include <type_traits>
0243 #include <utility>
0244 
0245 #include <GaudiKernel/CommonMessaging.h>
0246 #include <GaudiKernel/MsgStream.h>
0247 #include <GaudiKernel/detected.h>
0248 
0249 // Json serialization for std::chrono::duration
0250 namespace nlohmann {
0251   template <class Rep, class Period>
0252   struct adl_serializer<std::chrono::duration<Rep, Period>> {
0253     static void to_json( json& j, const std::chrono::duration<Rep, Period>& d ) { j = d.count(); }
0254     static void from_json( const json& j, std::chrono::duration<Rep, Period>& d ) {
0255       d = std::chrono::duration<Rep, Period>{ j.get<Rep>() };
0256     }
0257   };
0258 } // namespace nlohmann
0259 
0260 namespace Gaudi::Accumulators {
0261 
0262   /// Defines atomicity of the accumulators
0263   enum class atomicity { none, full };
0264 
0265   /// forward declaration of sqrt for custom types
0266   template <class T>
0267   auto sqrt( T d );
0268 
0269   /**
0270    * A functor always returning the value N
0271    */
0272   template <typename T, T N>
0273   struct Constant {
0274     template <typename U>
0275     constexpr T operator()( U&& ) const noexcept {
0276       return N;
0277     }
0278   };
0279 
0280   /**
0281    * An Identity functor
0282    */
0283   struct Identity {
0284     template <typename U>
0285     constexpr decltype( auto ) operator()( U&& v ) const noexcept {
0286       return std::forward<U>( v );
0287     }
0288   };
0289 
0290   /**
0291    * A Square functor
0292    */
0293   struct Square {
0294     template <typename U>
0295     constexpr decltype( auto ) operator()( U&& v ) const noexcept {
0296       return v * v;
0297     }
0298   };
0299 
0300   /**
0301    * type_traits for checking the presence of fetch_add
0302    */
0303   template <typename T, typename = int>
0304   using has_fetch_add_ = decltype( std::declval<T&>().fetch_add( 0 ) );
0305   template <typename T>
0306   inline constexpr bool has_fetch_add_v = Gaudi::cpp17::is_detected_v<has_fetch_add_, T>;
0307 
0308   /**
0309    * type_trait for the result type of a floating point operation on the type Arithmetic
0310    */
0311   template <typename Arithmetic, typename Result = double>
0312   using fp_result_type = std::conditional_t<std::is_integral_v<Arithmetic>, Result, Arithmetic>;
0313 
0314   /**
0315    * Base type for all functors used as ValuesHandler. The base takes care of storing the value
0316    */
0317   template <typename Arithmetic, atomicity Atomicity>
0318   struct BaseValueHandler;
0319 
0320   /**
0321    * BaseValueHandler specialization in the case of atomicity none
0322    */
0323   template <typename Arithmetic>
0324   struct BaseValueHandler<Arithmetic, atomicity::none> {
0325     using OutputType   = Arithmetic;
0326     using InternalType = Arithmetic;
0327     static constexpr OutputType getValue( const InternalType& v ) noexcept { return v; }
0328     static Arithmetic exchange( InternalType& v, Arithmetic newv ) noexcept { return std::exchange( v, newv ); }
0329   };
0330 
0331   /**
0332    * BaseValueHandler specialization in the case of atomicity full
0333    */
0334   template <typename Arithmetic>
0335   struct BaseValueHandler<Arithmetic, atomicity::full> {
0336     using OutputType   = Arithmetic;
0337     using InternalType = std::atomic<Arithmetic>;
0338     static constexpr OutputType getValue( const InternalType& v ) noexcept {
0339       return v.load( std::memory_order_relaxed );
0340     }
0341     static Arithmetic exchange( InternalType& v, Arithmetic newv ) noexcept { return v.exchange( newv ); }
0342   };
0343 
0344   /**
0345    * An Adder ValueHandler
0346    * operator(a, b) means a += b. In case of full atomicity, fetch_add or compare_exchange_weak are used.
0347    */
0348   template <typename Arithmetic, atomicity Atomicity>
0349   struct Adder;
0350 
0351   /**
0352    * Adder specialization in the case of atomicity none
0353    */
0354   template <typename Arithmetic>
0355   struct Adder<Arithmetic, atomicity::none> : BaseValueHandler<Arithmetic, atomicity::none> {
0356     using typename BaseValueHandler<Arithmetic, atomicity::none>::OutputType;
0357     using typename BaseValueHandler<Arithmetic, atomicity::none>::InternalType;
0358     static constexpr OutputType DefaultValue() { return Arithmetic{}; }
0359     static void                 merge( InternalType& a, Arithmetic b ) noexcept { a += b; }
0360   };
0361 
0362   /**
0363    * generic fetch_add, also dealing with atomic types with no fetch_add member method
0364    */
0365   template <typename AtomicType, typename Arithmetic>
0366   void fetch_add( AtomicType& atVar, Arithmetic value ) {
0367     if constexpr ( has_fetch_add_v<AtomicType> ) {
0368       atVar.fetch_add( value, std::memory_order_relaxed );
0369     } else {
0370       auto current = BaseValueHandler<Arithmetic, atomicity::full>::getValue( atVar );
0371       while ( !atVar.compare_exchange_weak( current, current + value ) )
0372         ;
0373     }
0374   }
0375 
0376   /**
0377    * Adder specialization in the case of atomicity full
0378    */
0379   template <typename Arithmetic>
0380   struct Adder<Arithmetic, atomicity::full> : BaseValueHandler<Arithmetic, atomicity::full> {
0381     using typename BaseValueHandler<Arithmetic, atomicity::full>::OutputType;
0382     using typename BaseValueHandler<Arithmetic, atomicity::full>::InternalType;
0383     static constexpr OutputType DefaultValue() { return Arithmetic{}; }
0384     static void                 merge( InternalType& a, Arithmetic b ) noexcept {
0385       if constexpr ( !std::is_floating_point_v<Arithmetic> ) { // avoid comparisons for floating points
0386         if ( DefaultValue() == b ) return;                     // avoid atomic operation if b is "0"
0387       }
0388       fetch_add( a, b );
0389     }
0390   };
0391 
0392   /**
0393    * An Extremum ValueHandler, to be reused for Minimum and Maximum
0394    * operator(a, b) means if (Compare(b,a)) a = b In case of full atomicity, compare_exchange_weak is used.
0395    */
0396   template <typename Arithmetic, atomicity Atomicity, typename Compare, Arithmetic ( *Initial )()>
0397   struct Extremum;
0398 
0399   /**
0400    * Extremum specialization in the case of atomicity none
0401    */
0402   template <typename Arithmetic, typename Compare, Arithmetic ( *Initial )()>
0403   struct Extremum<Arithmetic, atomicity::none, Compare, Initial> : BaseValueHandler<Arithmetic, atomicity::none> {
0404     using typename BaseValueHandler<Arithmetic, atomicity::none>::OutputType;
0405     using typename BaseValueHandler<Arithmetic, atomicity::none>::InternalType;
0406     static constexpr OutputType DefaultValue() { return Initial(); }
0407     static void                 merge( InternalType& a, Arithmetic b ) noexcept {
0408       if ( Compare{}( b, a ) ) a = b;
0409     }
0410   };
0411 
0412   /**
0413    * Extremum specialization in the case of atomicity full
0414    */
0415   template <typename Arithmetic, typename Compare, Arithmetic ( *Initial )()>
0416   struct Extremum<Arithmetic, atomicity::full, Compare, Initial> : BaseValueHandler<Arithmetic, atomicity::full> {
0417     using typename BaseValueHandler<Arithmetic, atomicity::full>::OutputType;
0418     using typename BaseValueHandler<Arithmetic, atomicity::full>::InternalType;
0419     static constexpr OutputType DefaultValue() { return Initial(); }
0420     static void                 merge( InternalType& a, Arithmetic b ) noexcept {
0421       Arithmetic prev_value = BaseValueHandler<Arithmetic, atomicity::full>::getValue( a );
0422       while ( Compare{}( b, prev_value ) && !a.compare_exchange_weak( prev_value, b ) )
0423         ;
0424     }
0425   };
0426 
0427   /**
0428    * A Minimun ValueHandler
0429    * operator(a, b) means a = min(a, b) In case of full atomicity, compare_exchange_weak is used.
0430    */
0431   template <typename Arithmetic, atomicity Atomicity = atomicity::full>
0432   using Minimum = Extremum<Arithmetic, Atomicity, std::less<Arithmetic>, std::numeric_limits<Arithmetic>::max>;
0433 
0434   /**
0435    * An Maximum ValueHandler
0436    * operator(a, b) means a = max(a, b) In case of full atomicity, compare_exchange_weak is used.
0437    */
0438   template <typename Arithmetic, atomicity Atomicity = atomicity::full>
0439   using Maximum = Extremum<Arithmetic, Atomicity, std::greater<Arithmetic>, std::numeric_limits<Arithmetic>::lowest>;
0440 
0441   /**
0442    * constant used to disambiguate construction of an empty Accumulator
0443    * versus the copy constructor. Indeed, construction of an empty Accumulator
0444    * still takes another Accumulator and copies configuration
0445    */
0446   struct construct_empty_t {
0447     explicit construct_empty_t() = default;
0448   };
0449   constexpr construct_empty_t construct_empty{};
0450 
0451   /**
0452    * Generic Accumulator, templated by
0453    *   - InputType : the type of input data coming to this Accumulator
0454    *   - InnerType : the type of the accumulated value. May be different from InputType,
0455    *        e.g. for a pure counter where InputType is double and InnerType is unsigned long
0456    *   - Atomicity : the desired atomicity : none or full
0457    *   - InputTransform : a function to be applied to the values pushed in
0458    *        + default to identity
0459    *        + other typical functions : square for a sumn of squares, constant 1 for a count
0460    *   - OutputTransform : a function to be applied to returned value
0461    *        + default to identity
0462    *        + one could think of sqrt for and RMS accumulator, InputTransform begin square
0463    *   - ValueHandler : the handler of the internal value, that is a functor providing :
0464    *        + the way to increment the internal value (e.g. +, min, max) as the operator()
0465    *        + the type of the internal value (typically Arithmetic or std::atomic<Arithmetic>)
0466    *        + the default value for the internal type
0467    */
0468   template <typename InputTypeT, typename InnerType, atomicity Atomicity = atomicity::full,
0469             typename InputTransform = Identity, typename OutputTransform = Identity,
0470             typename ValueHandler = Adder<InnerType, Atomicity>>
0471   class GenericAccumulator {
0472     template <typename, typename, atomicity, typename, typename, typename>
0473     friend class GenericAccumulator;
0474 
0475   public:
0476     using InputType             = InputTypeT;
0477     using OutputType            = std::decay_t<std::invoke_result_t<OutputTransform, InnerType>>;
0478     using InternalType          = InnerType;
0479     using JSONStringEntriesType = std::string;
0480     GenericAccumulator operator+=( const InputType by ) {
0481       ValueHandler::merge( m_value, InputTransform{}( by ) );
0482       return *this;
0483     }
0484     GenericAccumulator() = default;
0485     /// constructor of an empty GenericAccumulator, copying the (non existent) config from another GenericAccumulator
0486     template <atomicity ato, typename VH>
0487     GenericAccumulator( construct_empty_t,
0488                         const GenericAccumulator<InputType, InnerType, ato, InputTransform, OutputTransform, VH>& )
0489         : GenericAccumulator() {}
0490     template <typename... Args>
0491     GenericAccumulator( std::in_place_t, Args&&... args ) : m_value( std::forward<Args>( args )... ) {}
0492     GenericAccumulator( const GenericAccumulator& other ) : m_value( ValueHandler::getValue( other.m_value ) ) {}
0493     GenericAccumulator& operator=( const GenericAccumulator& other ) {
0494       m_value = ValueHandler::getValue( other.m_value );
0495       return *this;
0496     }
0497     OutputType value() const { return OutputTransform{}( ValueHandler::getValue( m_value ) ); }
0498     void       reset() { reset( ValueHandler::DefaultValue() ); }
0499     template <atomicity ato, typename VH>
0500     void mergeAndReset( GenericAccumulator<InputType, InnerType, ato, InputTransform, OutputTransform, VH>& other ) {
0501       ValueHandler::merge( m_value, VH::exchange( other.m_value, VH::DefaultValue() ) );
0502     }
0503     template <atomicity ato, typename VH>
0504     void operator+( GenericAccumulator<InputType, InnerType, ato, InputTransform, OutputTransform, VH>&& other ) {
0505       ValueHandler::merge( m_value, other.m_value );
0506     }
0507 
0508   protected:
0509     GenericAccumulator( InnerType in ) : m_value( std::move( in ) ) {}
0510     auto             rawValue() const { return ValueHandler::getValue( m_value ); }
0511     void             reset( InnerType in ) { m_value = std::move( in ); }
0512     static InnerType extractJSONData( const nlohmann::json& j, const JSONStringEntriesType& entries ) {
0513       return j.at( entries ).get<InnerType>();
0514     }
0515 
0516   private:
0517     typename ValueHandler::InternalType m_value{ ValueHandler::DefaultValue() };
0518   };
0519 
0520   /**
0521    * AccumulatorSet is an Accumulator that holds a set of Accumulators templated by same Arithmetic and Atomicity
0522    * and increase them altogether
0523    * @see Gaudi::Accumulators for detailed documentation
0524    */
0525   template <typename Arithmetic, atomicity Atomicity, typename InputTypeT = Arithmetic,
0526             template <atomicity, typename> class... Bases>
0527   class AccumulatorSet : public Bases<Atomicity, Arithmetic>... {
0528   public:
0529     using InputType             = InputTypeT;
0530     using OutputType            = std::tuple<typename Bases<Atomicity, Arithmetic>::OutputType...>;
0531     using InternalType          = std::tuple<typename Bases<Atomicity, Arithmetic>::InternalType...>;
0532     using JSONStringEntriesType = std::tuple<typename Bases<Atomicity, Arithmetic>::JSONStringEntriesType...>;
0533     constexpr AccumulatorSet()  = default;
0534     /// constructor of an empty AccumulatorSet, copying the (non existent) config from another GenericAccumulator
0535     template <atomicity ato>
0536     AccumulatorSet( construct_empty_t, const AccumulatorSet<Arithmetic, ato, InputType, Bases...>& )
0537         : AccumulatorSet() {}
0538     AccumulatorSet& operator+=( const InputType by ) {
0539       ( Bases<Atomicity, Arithmetic>::operator+=( by ), ... );
0540       return *this;
0541     }
0542     OutputType value() const { return std::make_tuple( Bases<Atomicity, Arithmetic>::value()... ); }
0543     void       reset() { ( Bases<Atomicity, Arithmetic>::reset(), ... ); }
0544     template <atomicity Ato>
0545     void mergeAndReset( AccumulatorSet<Arithmetic, Ato, InputType, Bases...>& other ) {
0546       ( Bases<Atomicity, Arithmetic>::mergeAndReset( static_cast<Bases<Ato, Arithmetic>&>( other ) ), ... );
0547     }
0548     template <atomicity Ato>
0549     void operator+( AccumulatorSet<Arithmetic, Ato, InputType, Bases...>&& other ) {
0550       ( Bases<Atomicity, Arithmetic>::operator+( static_cast<Bases<Ato, Arithmetic>&&>( other ) ), ... );
0551     }
0552 
0553   protected:
0554     AccumulatorSet( const InternalType& t ) : AccumulatorSet() { reset( t ); }
0555     void reset( const InternalType& t ) {
0556       std::apply( [this]( const auto&... i ) { ( this->Bases<Atomicity, Arithmetic>::reset( i ), ... ); }, t );
0557     }
0558     static InternalType extractJSONData( const nlohmann::json& j, const JSONStringEntriesType& entries ) {
0559       return extractJSONDataHelper( j, entries, std::index_sequence_for<Bases<Atomicity, Arithmetic>...>{} );
0560     }
0561 
0562   private:
0563     template <size_t... Is>
0564     static InternalType extractJSONDataHelper( const nlohmann::json& j, const JSONStringEntriesType& entries,
0565                                                std::index_sequence<Is...> ) {
0566       return extractJSONDataHelper( j, std::get<Is>( entries )... );
0567     }
0568     static InternalType
0569     extractJSONDataHelper( const nlohmann::json& j,
0570                            typename Bases<Atomicity, Arithmetic>::JSONStringEntriesType... entries ) {
0571       return { Bases<Atomicity, Arithmetic>::extractJSONData( j, entries )... };
0572     }
0573   };
0574 
0575   /**
0576    * MaxAccumulator. A MaxAccumulator is an Accumulator storing the max value of the provided arguments
0577    * @see Gaudi::Accumulators for detailed documentation
0578    */
0579   template <atomicity Atomicity, typename Arithmetic = double>
0580   struct MaxAccumulator
0581       : GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Identity, Identity, Maximum<Arithmetic, Atomicity>> {
0582     using GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Identity, Identity,
0583                              Maximum<Arithmetic, Atomicity>>::GenericAccumulator;
0584     Arithmetic max() const { return this->value(); }
0585   };
0586 
0587   /**
0588    * MinAccumulator. A MinAccumulator is an Accumulator storing the min value of the provided arguments
0589    * @see Gaudi::Accumulators for detailed documentation
0590    */
0591   template <atomicity Atomicity, typename Arithmetic = double>
0592   struct MinAccumulator
0593       : GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Identity, Identity, Minimum<Arithmetic, Atomicity>> {
0594     using GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Identity, Identity,
0595                              Minimum<Arithmetic, Atomicity>>::GenericAccumulator;
0596     Arithmetic min() const { return this->value(); }
0597   };
0598 
0599   /**
0600    * CountAccumulator. A CountAccumulator is an Accumulator storing the number of provided values
0601    * @see Gaudi::Accumulators for detailed documentation
0602    * Note that the Arithmetic type is actually irrelevant here, it's only there
0603    * for compatibility with other accumulators when aggregating them
0604    */
0605   template <atomicity Atomicity, typename Arithmetic = double>
0606   struct CountAccumulator : GenericAccumulator<Arithmetic, unsigned long, Atomicity, Constant<unsigned long, 1UL>> {
0607     using GenericAccumulator<Arithmetic, unsigned long, Atomicity, Constant<unsigned long, 1UL>>::GenericAccumulator;
0608     CountAccumulator& operator++() {
0609       ( *this ) += Arithmetic{};
0610       return *this;
0611     }
0612     CountAccumulator operator++( int ) {
0613       auto copy = *this;
0614       ++( *this );
0615       return copy;
0616     }
0617     unsigned long nEntries() const { return this->value(); }
0618   };
0619 
0620   /**
0621    * SumAccumulator. A SumAccumulator is an Accumulator storing the sum of the provided values
0622    * @see Gaudi::Accumulators for detailed documentation
0623    */
0624   template <atomicity Atomicity, typename Arithmetic = double>
0625   struct SumAccumulator : GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Identity> {
0626     using GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Identity>::GenericAccumulator;
0627     Arithmetic sum() const { return this->value(); }
0628   };
0629 
0630   /**
0631    * IntegralAccumulator. An IntegralAccumulator is an Accumulator with an integral storage
0632    * (unsigned long, int, ...) that can be incremented via operator++ and operator+=.
0633    * @see Gaudi::Accumulators for detailed documentation
0634    */
0635   template <atomicity Atomicity, typename Arithmetic = unsigned long>
0636   struct IntegralAccumulator : GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Identity> {
0637     static_assert( std::is_integral_v<Arithmetic>,
0638                    "Invalid Arithmetic type for IntegralAccumulator. It must be an integral type" );
0639 
0640     using GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Identity>::GenericAccumulator;
0641     IntegralAccumulator& operator++() {
0642       ( *this ) += 1;
0643       return *this;
0644     }
0645     IntegralAccumulator operator++( int ) {
0646       auto copy = *this;
0647       ++( *this );
0648       return copy;
0649     }
0650     Arithmetic nEntries() const { return this->value(); }
0651     Arithmetic sum() const { return this->value(); }
0652   };
0653 
0654   /**
0655    * SquareAccumulator. A SquareAccumulator is an Accumulator storing the sum of squares of the provided values
0656    * @see Gaudi::Accumulators for detailed documentation
0657    */
0658   template <atomicity Atomicity, typename Arithmetic = double>
0659   struct SquareAccumulator : GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Square> {
0660     using GenericAccumulator<Arithmetic, Arithmetic, Atomicity, Square>::GenericAccumulator;
0661     Arithmetic sum2() const { return this->value(); }
0662   };
0663 
0664   /// helper functor for the TrueAccumulator
0665   struct TrueTo1 {
0666     unsigned int operator()( bool v ) const { return v; }
0667   };
0668 
0669   /**
0670    * TrueAccumulator. A TrueAccumulator is an Accumulator counting the number of True values in the data
0671    * data
0672    * @see Gaudi::Accumulators for detailed documentation
0673    */
0674   template <atomicity Atomicity, typename Arithmetic>
0675   struct TrueAccumulator : GenericAccumulator<Arithmetic, unsigned long, Atomicity, TrueTo1> {
0676     using GenericAccumulator<Arithmetic, unsigned long, Atomicity, TrueTo1>::GenericAccumulator;
0677     unsigned long nTrueEntries() const { return this->value(); }
0678   };
0679 
0680   /// helper functor for the FalseAccumulator
0681   struct FalseTo1 {
0682     unsigned int operator()( bool v ) const { return !v; }
0683   };
0684 
0685   /**
0686    * FalseAccumulator. A FalseAccumulator is an Accumulator counting the number of False values in the data
0687    * data
0688    * @see Gaudi::Accumulators for detailed documentation
0689    */
0690   template <atomicity Atomicity, typename Arithmetic>
0691   struct FalseAccumulator : GenericAccumulator<Arithmetic, unsigned long, Atomicity, FalseTo1> {
0692     using GenericAccumulator<Arithmetic, unsigned long, Atomicity, FalseTo1>::GenericAccumulator;
0693     unsigned long nFalseEntries() const { return this->value(); }
0694   };
0695 
0696   /**
0697    * BinomialAccumulator. A BinomialAccumulator is an Accumulator able to compute the efficiency of a process
0698    * data
0699    * @see Gaudi::Accumulators for detailed documentation
0700    */
0701   template <atomicity Atomicity, typename Arithmetic>
0702   struct BinomialAccumulator : AccumulatorSet<bool, Atomicity, bool, TrueAccumulator, FalseAccumulator> {
0703     using AccumulatorSet<bool, Atomicity, bool, TrueAccumulator, FalseAccumulator>::AccumulatorSet;
0704     unsigned long nEntries() const { return this->nTrueEntries() + this->nFalseEntries(); }
0705 
0706     template <typename Result = fp_result_type<Arithmetic>>
0707     auto efficiency() const {
0708       auto nbEntries = nEntries();
0709       if ( 1 > nbEntries ) return Result{ -1 };
0710       return static_cast<Result>( this->nTrueEntries() ) / nbEntries;
0711     }
0712     auto eff() const { return efficiency(); }
0713 
0714     template <typename Result = fp_result_type<Arithmetic>>
0715     auto efficiencyErr() const {
0716       // Note the usage of using, aiming at using the std version of sqrt by default, without preventing
0717       // more specialized versions to be used via ADL (see http://en.cppreference.com/w/cpp/language/adl)
0718       using Gaudi::Accumulators::sqrt;
0719       using std::sqrt;
0720       auto nbEntries = nEntries();
0721       if ( 1 > nbEntries ) return Result{ -1 };
0722       return sqrt( static_cast<Result>( this->nTrueEntries() * this->nFalseEntries() ) / nbEntries ) / nbEntries;
0723     }
0724     auto effErr() const { return efficiencyErr(); }
0725     using AccumulatorSet<bool, Atomicity, bool, TrueAccumulator, FalseAccumulator>::operator+=;
0726     struct binomial_t {
0727       unsigned long nPass;
0728       unsigned long nTotal;
0729     };
0730     BinomialAccumulator& operator+=( binomial_t b ) {
0731       assert( b.nPass <= b.nTotal );
0732       TrueAccumulator<atomicity::none, bool> t{ std::in_place, b.nPass };
0733       TrueAccumulator<Atomicity, bool>::mergeAndReset( t );
0734       FalseAccumulator<atomicity::none, bool> f{ std::in_place, b.nTotal - b.nPass };
0735       FalseAccumulator<Atomicity, bool>::mergeAndReset( f );
0736       return *this;
0737     }
0738   };
0739 
0740   /**
0741    * AveragingAccumulatorBase. An AveragingAccumulator is an Accumulator able to compute an average
0742    * This Base class is still templated on the counting and summing accumulators
0743    * @see Gaudi::Accumulators for detailed documentation
0744    */
0745   template <atomicity Atomicity, typename Arithmetic, template <atomicity, typename> typename CountAcc,
0746             template <atomicity, typename> typename SumAcc>
0747   struct AveragingAccumulatorBase
0748       : AccumulatorSet<Arithmetic, Atomicity, typename CountAcc<Atomicity, Arithmetic>::InputType, CountAcc, SumAcc> {
0749     static_assert( std::is_same_v<typename CountAcc<Atomicity, Arithmetic>::InputType,
0750                                   typename SumAcc<Atomicity, Arithmetic>::InputType>,
0751                    "Incompatible Counters in definition of AveragingAccumulator. Both should have identical Input" );
0752     using AccumulatorSet<Arithmetic, Atomicity, typename CountAcc<Atomicity, Arithmetic>::InputType, CountAcc,
0753                          SumAcc>::AccumulatorSet;
0754     template <typename Result = fp_result_type<Arithmetic>>
0755     auto mean() const {
0756       auto   n   = this->nEntries();
0757       Result sum = this->sum();
0758       return ( n > 0 ) ? static_cast<Result>( sum / n ) : Result{};
0759     }
0760   };
0761 
0762   /**
0763    * AveragingAccumulator. An AveragingAccumulator is an Accumulator able to compute an average
0764    * @see Gaudi::Accumulators for detailed documentation
0765    */
0766   template <atomicity Atomicity, typename Arithmetic>
0767   using AveragingAccumulator = AveragingAccumulatorBase<Atomicity, Arithmetic, CountAccumulator, SumAccumulator>;
0768 
0769   /**
0770    * SigmaAccumulatorBase. A SigmaAccumulator is an Accumulator able to compute an average and variance/rms
0771    * This Base class is still templated on the averaging and square accumulators
0772    * @see Gaudi::Accumulators for detailed documentation
0773    */
0774   template <atomicity Atomicity, typename Arithmetic, template <atomicity, typename> typename AvgAcc,
0775             template <atomicity, typename> typename SquareAcc>
0776   struct SigmaAccumulatorBase
0777       : AccumulatorSet<Arithmetic, Atomicity, typename AvgAcc<Atomicity, Arithmetic>::InputType, AvgAcc, SquareAcc> {
0778     static_assert( std::is_same_v<typename AvgAcc<Atomicity, Arithmetic>::InputType,
0779                                   typename SquareAcc<Atomicity, Arithmetic>::InputType>,
0780                    "Incompatible Counters in definition of SigmaAccumulator. Both should have identical Input" );
0781     using AccumulatorSet<Arithmetic, Atomicity, typename SquareAcc<Atomicity, Arithmetic>::InputType, AvgAcc,
0782                          SquareAcc>::AccumulatorSet;
0783     template <typename Result = fp_result_type<Arithmetic>>
0784     auto biased_sample_variance() const {
0785       auto   n   = this->nEntries();
0786       Result sum = this->sum();
0787       return ( n > 0 ) ? static_cast<Result>( ( this->sum2() - sum * ( sum / n ) ) / n ) : Result{};
0788     }
0789 
0790     template <typename Result = fp_result_type<Arithmetic>>
0791     auto unbiased_sample_variance() const {
0792       auto   n   = this->nEntries();
0793       Result sum = this->sum();
0794       return ( n > 1 ) ? static_cast<Result>( ( this->sum2() - sum * ( sum / n ) ) / ( n - 1 ) ) : Result{};
0795     }
0796 
0797     template <typename Result = fp_result_type<Arithmetic>>
0798     auto standard_deviation() const {
0799       // Note the usage of using, aiming at using the std version of sqrt by default, without preventing
0800       // more specialized versions to be used via ADL (see http://en.cppreference.com/w/cpp/language/adl)
0801       using Gaudi::Accumulators::sqrt;
0802       using std::sqrt;
0803       Result v = biased_sample_variance();
0804       return ( Result{ 0 } > v ) ? Result{} : static_cast<Result>( sqrt( v ) );
0805     }
0806     [[deprecated( "The name 'rms' has changed to standard_deviation" )]] Arithmetic rms() const {
0807       return standard_deviation();
0808     }
0809 
0810     template <typename Result = fp_result_type<Arithmetic>>
0811     auto meanErr() const {
0812       auto n = this->nEntries();
0813       if ( 0 == n ) return Result{};
0814       // Note the usage of using, aiming at using the std version of sqrt by default, without preventing
0815       // more specialized versions to be used via ADL (see http://en.cppreference.com/w/cpp/language/adl)
0816       using Gaudi::Accumulators::sqrt;
0817       using std::sqrt;
0818       Result v = biased_sample_variance();
0819       return ( Result{ 0 } > v ) ? Result{} : static_cast<Result>( sqrt( v / n ) );
0820     }
0821   };
0822 
0823   /**
0824    * SigmaAccumulator. A SigmaAccumulator is an Accumulator able to compute an average and variance/rms
0825    * @see Gaudi::Accumulators for detailed documentation
0826    */
0827   template <atomicity Atomicity, typename Arithmetic>
0828   using SigmaAccumulator = SigmaAccumulatorBase<Atomicity, Arithmetic, AveragingAccumulator, SquareAccumulator>;
0829 
0830   /**
0831    * StatAccumulator. A StatAccumulator is an Accumulator able to compute an average, variance/rms and min/max
0832    * @see Gaudi::Accumulators for detailed documentation
0833    */
0834   template <atomicity Atomicity, typename Arithmetic>
0835   using StatAccumulator =
0836       AccumulatorSet<Arithmetic, Atomicity, Arithmetic, SigmaAccumulator, MinAccumulator, MaxAccumulator>;
0837 
0838   /**
0839    * Buffer is a non atomic Accumulator which, when it goes out-of-scope,
0840    * updates the underlying thread-safe Accumulator for all previous updates in one go.
0841    * It is templated by the basic accumulator type and has same interface
0842    * @see Gaudi::Accumulators for detailed documentation
0843    */
0844   template <template <atomicity Ato, typename... Int> class ContainedAccumulator, atomicity Atomicity, typename... Args>
0845   class Buffer : public ContainedAccumulator<atomicity::none, Args...> {
0846     using prime_type = ContainedAccumulator<Atomicity, Args...>;
0847     using base_type  = ContainedAccumulator<atomicity::none, Args...>;
0848 
0849   public:
0850     Buffer() = delete;
0851     Buffer( prime_type& p ) : base_type( construct_empty, p ), m_prime( &p ) {}
0852     Buffer( const Buffer& )         = delete;
0853     void operator=( const Buffer& ) = delete;
0854     Buffer( Buffer&& other ) : base_type( std::move( other ) ), m_prime( other.m_prime ) { other.m_prime = nullptr; }
0855     void push() {
0856       if ( m_prime ) { m_prime->mergeAndReset( static_cast<base_type&>( *this ) ); }
0857     }
0858     ~Buffer() { push(); }
0859 
0860   private:
0861     prime_type* m_prime = nullptr;
0862   };
0863 
0864   /**
0865    * An empty ancester of all counters that knows how to print themselves
0866    * @see Gaudi::Accumulators for detailed documentation
0867    */
0868   struct PrintableCounter {
0869     PrintableCounter() = default;
0870     /// destructor
0871     virtual ~PrintableCounter() = default;
0872     // add tag to printout
0873     template <typename stream>
0874     stream& printImpl( stream& s, std::string_view tag ) const {
0875       s << boost::format{ " | %|-48.48s|%|50t|" } % ( std::string{ '\"' }.append( tag ).append( "\"" ) );
0876       return print( s, true );
0877     }
0878     /// prints the counter to a stream
0879     virtual std::ostream& print( std::ostream&, bool tableFormat = false ) const = 0;
0880     virtual MsgStream&    print( MsgStream&, bool tableFormat = true ) const     = 0;
0881     /// prints the counter to a stream in table format, with the given tag
0882     virtual std::ostream& print( std::ostream& o, std::string_view tag ) const { return printImpl( o, tag ); }
0883     virtual MsgStream&    print( MsgStream& o, std::string_view tag ) const { return printImpl( o, tag ); }
0884     /** hint whether we should print that counter or not.
0885         Typically empty counters may not be printed */
0886     virtual bool toBePrinted() const { return true; }
0887     /// get a string representation
0888     std::string toString() const {
0889       std::ostringstream ost;
0890       print( ost );
0891       return ost.str();
0892     }
0893   };
0894 
0895   /**
0896    * external printout operator to a stream type
0897    */
0898   inline std::ostream& operator<<( std::ostream& s, const PrintableCounter& counter ) { return counter.print( s ); }
0899   inline MsgStream&    operator<<( MsgStream& s, const PrintableCounter& counter ) { return counter.print( s ); }
0900   /**
0901    * An empty ancester of all counters that provides a buffer method that returns a buffer on itself
0902    * Also registers the counter to its owner, with default type "counter"
0903    * Due to this registration, move semantic is disabled. But copy semantic remains.
0904    * @see Gaudi::Accumulators for detailed documentation
0905    */
0906   template <atomicity Atomicity, template <atomicity Ato, typename... Int> class Accumulator, typename... Args>
0907   class BufferableCounter : public PrintableCounter, public Accumulator<Atomicity, Args...> {
0908   public:
0909     using Accumulator<Atomicity, Args...>::Accumulator;
0910     using BufferType    = Buffer<Accumulator, Atomicity, Args...>;
0911     BufferableCounter() = default;
0912     template <typename OWNER>
0913     BufferableCounter( OWNER* o, std::string const& name ) : BufferableCounter( o, name, *this ) {}
0914     BufferType buffer() { return { *this }; }
0915     BufferableCounter( BufferableCounter const& )            = delete;
0916     BufferableCounter& operator=( BufferableCounter const& ) = delete;
0917     ~BufferableCounter() {
0918       if ( m_monitoringHub ) { m_monitoringHub->removeEntity( *this ); }
0919     }
0920 
0921     inline static const std::string typeString{ "counter" };
0922 
0923   protected:
0924     template <typename OWNER, typename SELF, typename... CARGS>
0925     BufferableCounter( OWNER* o, std::string const& name, SELF& self, CARGS... args )
0926         : Accumulator<Atomicity, Args...>( args... ), m_monitoringHub( &o->serviceLocator()->monitoringHub() ) {
0927       m_monitoringHub->registerEntity( o->name(), name, self.typeString, self );
0928     }
0929 
0930   private:
0931     Monitoring::Hub* m_monitoringHub{ nullptr };
0932   };
0933 
0934   /**
0935    * A basic integral counter;
0936    * @see Gaudi::Accumulators for detailed documentation
0937    */
0938   template <atomicity Atomicity = atomicity::full, typename Arithmetic = unsigned long>
0939   struct Counter : BufferableCounter<Atomicity, IntegralAccumulator, Arithmetic> {
0940     inline static const std::string typeString{ std::string{ "counter:Counter:" } + typeid( Arithmetic ).name() };
0941     using BufferableCounter<Atomicity, IntegralAccumulator, Arithmetic>::BufferableCounter;
0942     template <typename OWNER>
0943     Counter( OWNER* o, std::string const& name )
0944         : BufferableCounter<Atomicity, IntegralAccumulator, Arithmetic>( o, name, *this ) {}
0945     Counter& operator++() { return ( *this ) += 1; }
0946     Counter& operator+=( const Arithmetic v ) {
0947       BufferableCounter<Atomicity, IntegralAccumulator, Arithmetic>::operator+=( v );
0948       return *this;
0949     }
0950     using BufferableCounter<Atomicity, IntegralAccumulator, Arithmetic>::print;
0951 
0952     template <typename stream>
0953     stream& printImpl( stream& o, bool tableFormat ) const {
0954       // Avoid printing empty counters in non DEBUG mode
0955       auto fmt = ( tableFormat ? "|%|10d| |" : "#=%|-7lu|" );
0956       return o << boost::format{ fmt } % this->nEntries();
0957     }
0958 
0959     std::ostream& print( std::ostream& o, bool tableFormat = false ) const override {
0960       return printImpl( o, tableFormat );
0961     }
0962     MsgStream&  print( MsgStream& o, bool tableFormat = false ) const override { return printImpl( o, tableFormat ); }
0963     bool        toBePrinted() const override { return this->nEntries() > 0; }
0964     friend void reset( Counter& c ) { c.reset(); }
0965     friend void mergeAndReset( Counter& c, Counter& o ) { c.mergeAndReset( o ); }
0966     friend void to_json( nlohmann::json& j, Counter const& c ) {
0967       j = { { "type", c.typeString }, { "empty", c.nEntries() == 0 }, { "nEntries", c.nEntries() } };
0968     }
0969     static Counter fromJSON( const nlohmann::json& j ) {
0970       return IntegralAccumulator<Atomicity, Arithmetic>::extractJSONData( j, { "nEntries" } );
0971     }
0972   };
0973 
0974   /**
0975    * A counter aiming at computing sum and average
0976    * @see Gaudi::Accumulators for detailed documentation
0977    */
0978   template <typename Arithmetic = double, atomicity Atomicity = atomicity::full>
0979   struct AveragingCounter : BufferableCounter<Atomicity, AveragingAccumulator, Arithmetic> {
0980     inline static const std::string typeString{ std::string{ "counter:AveragingCounter:" } +
0981                                                 typeid( Arithmetic ).name() };
0982     using BufferableCounter<Atomicity, AveragingAccumulator, Arithmetic>::BufferableCounter;
0983     template <typename OWNER>
0984     AveragingCounter( OWNER* o, std::string const& name )
0985         : BufferableCounter<Atomicity, AveragingAccumulator, Arithmetic>( o, name, *this ) {}
0986     using BufferableCounter<Atomicity, AveragingAccumulator, Arithmetic>::print;
0987 
0988     template <typename stream>
0989     stream& printImpl( stream& o, bool tableFormat ) const {
0990       auto fmt = ( tableFormat ? "|%|10d| |%|11.7g| |%|#11.5g| |" : "#=%|-7lu| Sum=%|-11.5g| Mean=%|#10.4g|" );
0991       return o << boost::format{ fmt } % this->nEntries() % this->sum() % this->mean();
0992     }
0993 
0994     std::ostream& print( std::ostream& o, bool tableFormat = false ) const override {
0995       return printImpl( o, tableFormat );
0996     }
0997     MsgStream& print( MsgStream& o, bool tableFormat = false ) const override { return printImpl( o, tableFormat ); }
0998 
0999     bool        toBePrinted() const override { return this->nEntries() > 0; }
1000     friend void reset( AveragingCounter& c ) { return c.reset(); }
1001     friend void mergeAndReset( AveragingCounter& c, AveragingCounter& o ) { c.mergeAndReset( o ); }
1002     friend void to_json( nlohmann::json& j, AveragingCounter const& c ) {
1003       j = { { "type", c.typeString },
1004             { "empty", c.nEntries() == 0 },
1005             { "nEntries", c.nEntries() },
1006             { "sum", c.sum() },
1007             { "mean", c.mean() } };
1008     }
1009     static AveragingCounter fromJSON( const nlohmann::json& j ) {
1010       return AveragingAccumulator<Atomicity, Arithmetic>::extractJSONData( j, { "nEntries", "sum" } );
1011     }
1012   };
1013   template <typename Arithmetic = double, atomicity Atomicity = atomicity::full>
1014   using SummingCounter = AveragingCounter<Arithmetic, Atomicity>;
1015 
1016   /**
1017    * A counter aiming at computing average and sum2 / variance / standard deviation
1018    * @see Gaudi::Accumulators for detailed documentation
1019    */
1020   template <typename Arithmetic = double, atomicity Atomicity = atomicity::full>
1021   struct SigmaCounter : BufferableCounter<Atomicity, SigmaAccumulator, Arithmetic> {
1022     inline static const std::string typeString{ std::string{ "counter:SigmaCounter:" } + typeid( Arithmetic ).name() };
1023     using BufferableCounter<Atomicity, SigmaAccumulator, Arithmetic>::BufferableCounter;
1024     template <typename OWNER>
1025     SigmaCounter( OWNER* o, std::string const& name )
1026         : BufferableCounter<Atomicity, SigmaAccumulator, Arithmetic>( o, name, *this ) {}
1027     using BufferableCounter<Atomicity, SigmaAccumulator, Arithmetic>::print;
1028 
1029     template <typename stream>
1030     stream& printImpl( stream& o, bool tableFormat ) const {
1031       auto fmt = ( tableFormat ? "|%|10d| |%|11.7g| |%|#11.5g| |%|#11.5g| |"
1032                                : "#=%|-7lu| Sum=%|-11.5g| Mean=%|#10.4g| +- %|-#10.5g|" );
1033       return o << boost::format{ fmt } % this->nEntries() % this->sum() % this->mean() % this->standard_deviation();
1034     }
1035 
1036     std::ostream& print( std::ostream& o, bool tableFormat = false ) const override {
1037       return printImpl( o, tableFormat );
1038     }
1039     MsgStream&  print( MsgStream& o, bool tableFormat = false ) const override { return printImpl( o, tableFormat ); }
1040     bool        toBePrinted() const override { return this->nEntries() > 0; }
1041     friend void reset( SigmaCounter& c ) { c.reset(); }
1042     friend void mergeAndReset( SigmaCounter& c, SigmaCounter& o ) { c.mergeAndReset( o ); }
1043     friend void to_json( nlohmann::json& j, SigmaCounter const& c ) {
1044       j = { { "type", c.typeString },
1045             { "empty", c.nEntries() == 0 },
1046             { "nEntries", c.nEntries() },
1047             { "sum", c.sum() },
1048             { "mean", c.mean() },
1049             { "sum2", c.sum2() },
1050             { "standard_deviation", c.standard_deviation() } };
1051     }
1052     static SigmaCounter fromJSON( const nlohmann::json& j ) {
1053       return SigmaAccumulator<Atomicity, Arithmetic>::extractJSONData( j, { { "nEntries", "sum" }, "sum2" } );
1054     }
1055   };
1056 
1057   /**
1058    * A counter aiming at computing average and sum2 / variance / standard deviation
1059    * @see Gaudi::Accumulators for detailed documentation
1060    */
1061   template <typename Arithmetic = double, atomicity Atomicity = atomicity::full>
1062   struct StatCounter : BufferableCounter<Atomicity, StatAccumulator, Arithmetic> {
1063     inline static const std::string typeString{ std::string{ "counter:StatCounter:" } + typeid( Arithmetic ).name() };
1064     using BufferableCounter<Atomicity, StatAccumulator, Arithmetic>::BufferableCounter;
1065     template <typename OWNER>
1066     StatCounter( OWNER* o, std::string const& name )
1067         : BufferableCounter<Atomicity, StatAccumulator, Arithmetic>( o, name, *this ) {}
1068     using BufferableCounter<Atomicity, StatAccumulator, Arithmetic>::print;
1069 
1070     template <typename stream>
1071     stream& printImpl( stream& o, bool tableFormat ) const {
1072       auto fmt = ( tableFormat ? "|%|10d| |%|11.7g| |%|#11.5g| |%|#11.5g| |%|#12.5g| |%|#12.5g| |"
1073                                : "#=%|-7lu| Sum=%|-11.5g| Mean=%|#10.4g| +- %|-#10.5g| Min/Max=%|#10.4g|/%|-#10.4g|" );
1074       return o << boost::format{ fmt } % this->nEntries() % this->sum() % this->mean() % this->standard_deviation() %
1075                       this->min() % this->max();
1076     }
1077 
1078     std::ostream& print( std::ostream& o, bool tableFormat = false ) const override {
1079       return printImpl( o, tableFormat );
1080     }
1081     MsgStream&  print( MsgStream& o, bool tableFormat = false ) const override { return printImpl( o, tableFormat ); }
1082     bool        toBePrinted() const override { return this->nEntries() > 0; }
1083     friend void reset( StatCounter& c ) { c.reset(); }
1084     friend void mergeAndReset( StatCounter& c, StatCounter& o ) { c.mergeAndReset( o ); }
1085     friend void to_json( nlohmann::json& j, StatCounter const& c ) {
1086       j = { { "type", c.typeString },
1087             { "empty", c.nEntries() == 0 },
1088             { "nEntries", c.nEntries() },
1089             { "sum", c.sum() },
1090             { "mean", c.mean() },
1091             { "sum2", c.sum2() },
1092             { "standard_deviation", c.standard_deviation() },
1093             { "min", c.min() },
1094             { "max", c.max() } };
1095     }
1096     static StatCounter fromJSON( const nlohmann::json& j ) {
1097       return StatAccumulator<Atomicity, Arithmetic>::extractJSONData(
1098           j, { { { "nEntries", "sum" }, "sum2" }, "min", "max" } );
1099     }
1100   };
1101 
1102   /**
1103    * A counter dealing with binomial data
1104    * @see Gaudi::Accumulators for detailed documentation
1105    */
1106   template <typename Arithmetic = double, atomicity Atomicity = atomicity::full>
1107   struct BinomialCounter : BufferableCounter<Atomicity, BinomialAccumulator, Arithmetic> {
1108     inline static const std::string typeString{ std::string{ "counter:BinomialCounter:" } +
1109                                                 typeid( Arithmetic ).name() };
1110     using BufferableCounter<Atomicity, BinomialAccumulator, Arithmetic>::BufferableCounter;
1111     template <typename OWNER>
1112     BinomialCounter( OWNER* o, std::string const& name )
1113         : BufferableCounter<Atomicity, BinomialAccumulator, Arithmetic>( o, name, *this ) {}
1114 
1115     template <typename stream>
1116     stream& printImpl( stream& o, bool tableFormat ) const {
1117       auto fmt = ( tableFormat ? "|%|10d| |%|11.5g| |(%|#9.7g| +- %|-#8.7g|)%% |"
1118                                : "#=%|-7lu| Sum=%|-11.5g| Eff=|(%|#9.7g| +- %|-#8.6g|)%%|" );
1119       return o << boost::format{ fmt } % this->nEntries() % this->nTrueEntries() % ( this->efficiency() * 100 ) %
1120                       ( this->efficiencyErr() * 100 );
1121     }
1122 
1123     std::ostream& print( std::ostream& o, bool tableFormat = false ) const override {
1124       return printImpl( o, tableFormat );
1125     }
1126     MsgStream& print( MsgStream& o, bool tableFormat = false ) const override { return printImpl( o, tableFormat ); }
1127 
1128     template <typename stream>
1129     stream& printImpl( stream& o, std::string_view tag ) const {
1130       // override default print to add a '*' in from of the name
1131       o << boost::format{ " |*%|-48.48s|%|50t|" } % ( std::string{ "\"" }.append( tag ).append( "\"" ) );
1132       return print( o, true );
1133     }
1134     /// prints the counter to a stream in table format, with the given tag
1135     std::ostream& print( std::ostream& o, std::string_view tag ) const override { return printImpl( o, tag ); }
1136     MsgStream&    print( MsgStream& o, std::string_view tag ) const override { return printImpl( o, tag ); }
1137     bool          toBePrinted() const override { return this->nEntries() > 0; }
1138     friend void   reset( BinomialCounter& c ) { c.reset(); }
1139     friend void   mergeAndReset( BinomialCounter& c, BinomialCounter& o ) { c.mergeAndReset( o ); }
1140     friend void   to_json( nlohmann::json& j, BinomialCounter const& c ) {
1141       j = { { "type", c.typeString },
1142               { "empty", c.nEntries() == 0 },
1143               { "nEntries", c.nTrueEntries() + c.nFalseEntries() },
1144               { "nTrueEntries", c.nTrueEntries() },
1145               { "nFalseEntries", c.nFalseEntries() },
1146               { "efficiency", c.efficiency() },
1147               { "efficiencyErr", c.efficiencyErr() } };
1148     }
1149     static BinomialCounter fromJSON( const nlohmann::json& j ) {
1150       return BinomialAccumulator<Atomicity, Arithmetic>::extractJSONData( j, { "nTrueEntries", "nFalseEntries" } );
1151     }
1152   };
1153 
1154   namespace details::MsgCounter {
1155     template <atomicity Atomicity>
1156     struct Handler : Adder<unsigned long, Atomicity> {
1157       using Base = Adder<unsigned long, Atomicity>;
1158       static void merge( typename Base::InternalType& orig, bool b ) {
1159         if ( b ) Base::merge( orig, 1 );
1160       }
1161     };
1162     // note that Arithmetic type is unused in this case but needs to be there in case
1163     // we want to create AccumulatorSets with this Accumulator
1164     template <atomicity Atomicity, typename Arithmetic = double>
1165     using MsgAccumulator = GenericAccumulator<bool, unsigned long, Atomicity, Identity, Identity, Handler<Atomicity>>;
1166   } // namespace details::MsgCounter
1167 
1168   template <MSG::Level level, atomicity Atomicity = atomicity::full>
1169   class MsgCounter : public PrintableCounter, public details::MsgCounter::MsgAccumulator<Atomicity> {
1170   public:
1171     inline static const std::string typeString{ "counter:MsgCounter" };
1172     template <typename OWNER>
1173     MsgCounter( OWNER* o, std::string const& ms, unsigned long nMax = 10 )
1174         : m_monitoringHub{ &o->serviceLocator()->monitoringHub() }, logger( o ), msg( ms ), max( nMax ) {
1175       m_monitoringHub->registerEntity( o->name(), ms, typeString, *this );
1176     }
1177     template <typename OWNER>
1178     MsgCounter( OWNER* o, std::string const& ms, int nMax ) : MsgCounter( o, ms, static_cast<unsigned long>( nMax ) ) {}
1179     MsgCounter& operator++() {
1180       ( *this ) += true;
1181       return *this;
1182     }
1183     MsgCounter& operator+=( const bool by ) {
1184       details::MsgCounter::MsgAccumulator<Atomicity>::operator+=( by );
1185       if ( by ) log();
1186       return *this;
1187     }
1188     MsgCounter( MsgCounter const& )            = delete;
1189     MsgCounter& operator=( MsgCounter const& ) = delete;
1190     ~MsgCounter() {
1191       if ( m_monitoringHub ) m_monitoringHub->removeEntity( *this );
1192     }
1193     template <typename stream>
1194     stream& printImpl( stream& o, bool tableFormat ) const {
1195       return o << boost::format{ tableFormat ? "|%|10d| |" : "#=%|-7lu|" } % this->value();
1196     }
1197     using PrintableCounter::print;
1198     std::ostream& print( std::ostream& os, bool tableFormat ) const override { return printImpl( os, tableFormat ); }
1199     MsgStream&    print( MsgStream& os, bool tableFormat ) const override { return printImpl( os, tableFormat ); }
1200     bool          toBePrinted() const override { return this->value() > 0; }
1201     friend void   reset( MsgCounter& c ) { c.reset(); }
1202     friend void   mergeAndReset( MsgCounter& c, MsgCounter& o ) { c.mergeAndReset( o ); }
1203     friend void   to_json( nlohmann::json& j, MsgCounter const& c ) {
1204       j = { { "type", c.typeString },  { "empty", c.value() == 0 },
1205               { "nEntries", c.value() }, { "level", level },
1206               { "max", c.max },          { "msg", c.msg } };
1207     }
1208     static MsgCounter fromJSON( const nlohmann::json& j ) {
1209       return { j.at( "msg" ).get<std::string>(), j.at( "max" ).get<unsigned long>(),
1210                j.at( "nEntries" ).get<unsigned long>() };
1211     }
1212 
1213   private:
1214     MsgCounter( std::string const& ms, unsigned long nMax, unsigned long count )
1215         : details::MsgCounter::MsgAccumulator<Atomicity>{ count }, msg( ms ), max( nMax ) {}
1216 
1217     Monitoring::Hub*           m_monitoringHub{ nullptr };
1218     const CommonMessagingBase* logger{ nullptr };
1219     std::string                msg;
1220     unsigned long              max;
1221     void                       log() {
1222       if ( this->value() <= max && logger ) {
1223         if ( this->value() == max ) {
1224           logger->msgStream( level ) << "Suppressing message: " << std::quoted( msg, '\'' ) << endmsg;
1225         } else {
1226           logger->msgStream( level ) << msg << endmsg;
1227         }
1228       }
1229     }
1230   };
1231 
1232   /**
1233    * A helper function for accumulating data from a container into a counter
1234    * This is internally using buffers so that the original counter is only
1235    * updated once.
1236    */
1237   template <typename Counter, typename Container, typename Fun>
1238   void accumulate( Counter& counter, const Container& container, Fun f = Identity{} ) {
1239     auto b = counter.buffer();
1240     for ( const auto& elem : container ) b += f( elem );
1241   }
1242 
1243 } // namespace Gaudi::Accumulators