97 lines
2.4 KiB
C++
97 lines
2.4 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "Data/Containers/RingBuffer.hpp"
|
|
|
|
#include <type_traits>
|
|
#include <concepts>
|
|
|
|
namespace OpenVulkano::Math
|
|
{
|
|
template<typename T>
|
|
concept SupportsRunningAverage = requires(T a, T b, int64_t size)
|
|
{
|
|
{ a += b } -> std::same_as<T&>;
|
|
{ a -= b } -> std::same_as<T&>;
|
|
{ a * size } -> std::convertible_to<T>;
|
|
{ a / size } -> std::convertible_to<T>;
|
|
};
|
|
|
|
template<typename T,
|
|
size_t SIZE = RING_BUFFER_DYNAMIC,
|
|
bool RECALC = std::is_floating_point_v<T>,
|
|
size_t RECALC_OP_COUNT = 100> requires SupportsRunningAverage<T>
|
|
class RunningAverage final
|
|
{
|
|
struct Empty {};
|
|
|
|
RingBuffer<T, SIZE> m_buffer;
|
|
T m_value;
|
|
[[no_unique_address]] std::conditional_t<RECALC, size_t, Empty> m_operations;
|
|
|
|
public:
|
|
RunningAverage(size_t size) requires (std::is_default_constructible_v<T> && SIZE == RING_BUFFER_DYNAMIC)
|
|
: m_buffer(size), m_value(T() * static_cast<int64_t>(size))
|
|
{
|
|
assert(size > 0);
|
|
m_buffer.Fill(T());
|
|
if constexpr (RECALC) m_operations = 0;
|
|
}
|
|
|
|
RunningAverage(size_t size, const T& def) requires (SIZE == RING_BUFFER_DYNAMIC)
|
|
: m_buffer(size), m_value(def * static_cast<int64_t>(size))
|
|
{
|
|
assert(size > 0);
|
|
m_buffer.Fill(def);
|
|
if constexpr (RECALC) m_operations = 0;
|
|
}
|
|
|
|
RunningAverage() requires (std::is_default_constructible_v<T> && SIZE != RING_BUFFER_DYNAMIC)
|
|
: m_value(T() * static_cast<int64_t>(SIZE))
|
|
{
|
|
static_assert(SIZE > 0);
|
|
m_buffer.Fill(T());
|
|
if constexpr (RECALC) m_operations = 0;
|
|
}
|
|
|
|
RunningAverage(const T& def) requires (SIZE != RING_BUFFER_DYNAMIC)
|
|
: m_value(def * static_cast<int64_t>(SIZE))
|
|
{
|
|
static_assert(SIZE > 0);
|
|
m_buffer.Fill(def);
|
|
if constexpr (RECALC) m_operations = 0;
|
|
}
|
|
|
|
void Push(const T& value)
|
|
{
|
|
m_value -= m_buffer.PushAndOverwrite(value);
|
|
m_value += value;
|
|
if constexpr (RECALC)
|
|
{
|
|
if (++m_operations >= RECALC_OP_COUNT)
|
|
{
|
|
m_operations = 0;
|
|
Recalculate();
|
|
}
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] T Get() const { return m_value / static_cast<int64_t>(m_buffer.Size()); }
|
|
|
|
void Recalculate()
|
|
{
|
|
m_value = m_buffer[0];
|
|
auto it = m_buffer.cbegin();
|
|
it++;
|
|
for(auto end = m_buffer.cend(); it != end; it++)
|
|
{
|
|
m_value += *it;
|
|
}
|
|
}
|
|
};
|
|
} |