Files
rippled/include/xrpl/basics/DecayingSample.h
2025-11-10 11:49:19 -05:00

137 lines
3.0 KiB
C++

#ifndef XRPL_BASICS_DECAYINGSAMPLE_H_INCLUDED
#define XRPL_BASICS_DECAYINGSAMPLE_H_INCLUDED
#include <chrono>
#include <cmath>
namespace xrpl {
/** Sampling function using exponential decay to provide a continuous value.
@tparam The number of seconds in the decay window.
*/
template <int Window, typename Clock>
class DecayingSample
{
public:
using value_type = typename Clock::duration::rep;
using time_point = typename Clock::time_point;
DecayingSample() = delete;
/**
@param now Start time of DecayingSample.
*/
explicit DecayingSample(time_point now) : m_value(value_type()), m_when(now)
{
}
/** Add a new sample.
The value is first aged according to the specified time.
*/
value_type
add(value_type value, time_point now)
{
decay(now);
m_value += value;
return m_value / Window;
}
/** Retrieve the current value in normalized units.
The samples are first aged according to the specified time.
*/
value_type
value(time_point now)
{
decay(now);
return m_value / Window;
}
private:
// Apply exponential decay based on the specified time.
void
decay(time_point now)
{
if (now == m_when)
return;
if (m_value != value_type())
{
std::size_t elapsed =
std::chrono::duration_cast<std::chrono::seconds>(now - m_when)
.count();
// A span larger than four times the window decays the
// value to an insignificant amount so just reset it.
//
if (elapsed > 4 * Window)
{
m_value = value_type();
}
else
{
while (elapsed--)
m_value -= (m_value + Window - 1) / Window;
}
}
m_when = now;
}
// Current value in exponential units
value_type m_value;
// Last time the aging function was applied
time_point m_when;
};
//------------------------------------------------------------------------------
/** Sampling function using exponential decay to provide a continuous value.
@tparam HalfLife The half life of a sample, in seconds.
*/
template <int HalfLife, class Clock>
class DecayWindow
{
public:
using time_point = typename Clock::time_point;
explicit DecayWindow(time_point now) : value_(0), when_(now)
{
}
void
add(double value, time_point now)
{
decay(now);
value_ += value;
}
double
value(time_point now)
{
decay(now);
return value_ / HalfLife;
}
private:
static_assert(HalfLife > 0, "half life must be positive");
void
decay(time_point now)
{
if (now <= when_)
return;
using namespace std::chrono;
auto const elapsed = duration<double>(now - when_).count();
value_ *= std::pow(2.0, -elapsed / HalfLife);
when_ = now;
}
double value_;
time_point when_;
};
} // namespace xrpl
#endif