Add running average
This commit is contained in:
97
openVulkanoCpp/Math/RunningAverage.hpp
Normal file
97
openVulkanoCpp/Math/RunningAverage.hpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user