/* Copyright 2024 Joaquin M Lopez Munoz. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * See https://www.boost.org/libs/unordered for library home page. */ #ifndef BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP #define BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP #include #include #include #include #include #if defined(BOOST_HAS_THREADS) #include #include #endif namespace boost{ namespace unordered{ namespace detail{ namespace foa{ /* Cumulative one-pass calculation of the average, variance and deviation of * running sequences. */ struct sequence_stats_data { double m=0.0; double m_prior=0.0; double s=0.0; }; struct welfords_algorithm /* 0-based */ { template int operator()(T&& x,sequence_stats_data& d)const noexcept { static_assert( noexcept(static_cast(x)), "Argument conversion to double must not throw."); d.m_prior=d.m; d.m+=(static_cast(x)-d.m)/static_cast(n); d.s+=(n!=1)* (static_cast(x)-d.m_prior)*(static_cast(x)-d.m); return 0; /* mp11::tuple_transform requires that return type not be void */ } std::size_t n; }; struct sequence_stats_summary { double average; double variance; double deviation; }; /* Stats calculated jointly for N same-sized sequences to save the space * for count. */ template class cumulative_stats { public: struct summary { std::size_t count; std::array sequence_summary; }; void reset()noexcept{*this=cumulative_stats();} template void add(Ts&&... xs)noexcept { static_assert( sizeof...(Ts)==N,"A sample must be provided for each sequence."); if(BOOST_UNLIKELY(++n==0)){ /* wraparound */ reset(); n=1; } mp11::tuple_transform( welfords_algorithm{n}, std::forward_as_tuple(std::forward(xs)...), data); } summary get_summary()const noexcept { summary res; res.count=n; for(std::size_t i=0;i(n):0.0, /* biased */ deviation=std::sqrt(variance); res.sequence_summary[i]={average,variance,deviation}; } return res; } private: std::size_t n=0; std::array data; }; #if defined(BOOST_HAS_THREADS) template class concurrent_cumulative_stats:cumulative_stats { using super=cumulative_stats; using lock_guard=std::lock_guard; public: using summary=typename super::summary; concurrent_cumulative_stats()noexcept:super{}{} concurrent_cumulative_stats(const concurrent_cumulative_stats& x)noexcept: concurrent_cumulative_stats{x,lock_guard{x.mut}}{} concurrent_cumulative_stats& operator=(const concurrent_cumulative_stats& x)noexcept { auto x1=x; lock_guard lck{mut}; static_cast(*this)=x1; return *this; } void reset()noexcept { lock_guard lck{mut}; super::reset(); } template void add(Ts&&... xs)noexcept { lock_guard lck{mut}; super::add(std::forward(xs)...); } summary get_summary()const noexcept { lock_guard lck{mut}; return super::get_summary(); } private: concurrent_cumulative_stats(const super& x,lock_guard&&):super{x}{} mutable rw_spinlock mut; }; #else template using concurrent_cumulative_stats=cumulative_stats; #endif } /* namespace foa */ } /* namespace detail */ } /* namespace unordered */ } /* namespace boost */ #endif