blob: e9a2893752f9a4cf9b00d20df7895ae418d068ef [file] [log] [blame]
Ilya Nikolaevskiy07923942023-09-19 10:05:041// Copyright 2023 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef BASE_MOVING_WINDOW_H_
6#define BASE_MOVING_WINDOW_H_
7
Stephan Hartmannab6db302023-09-20 16:04:088#include <math.h>
Ilya Nikolaevskiy07923942023-09-19 10:05:049#include <stddef.h>
10
Ilya Nikolaevskiye846edb12023-09-25 10:45:0211#include <cmath>
Ilya Nikolaevskiy07923942023-09-19 10:05:0412#include <functional>
13#include <limits>
14#include <vector>
15
16#include "base/check_op.h"
17#include "base/memory/raw_ref.h"
Ilya Nikolaevskiye846edb12023-09-25 10:45:0218#include "base/time/time.h"
Ilya Nikolaevskiy07923942023-09-19 10:05:0419
20namespace base {
21
22// Class to efficiently calculate statistics in a sliding window.
23// This class isn't thread safe.
Ilya Nikolaevskiye846edb12023-09-25 10:45:0224// Supported statistics are Min/Max/Mean/Deviation.
Ilya Nikolaevskiy07923942023-09-19 10:05:0425// You can also iterate through the items in the window.
26// The class is modular: required features must be specified in the template
27// arguments.
28// Non listed features don't consume memory or runtime cycles at all.
29//
30// Usage:
31// base::MovingWindow<int,
32// base::MovingWindowFeatures::Min,
33// base::MovingWindowFeatures::Max>
34// moving_window(window_size);
35//
36// Following convenience shortcuts are provided with predefined sets of
Ilya Nikolaevskiye846edb12023-09-25 10:45:0237// features:
38// MovingMax/MovingMin/MovingAverage/MovingAverageDeviation/MovingMinMax.
Ilya Nikolaevskiy07923942023-09-19 10:05:0439//
40// Methods:
41// Constructor:
42// MovingWindow(size_t window_size);
43//
44// Window update (available for all templates):
45// AddSample(T value) const;
46// size_t Count() const;
47// void Reset();
48//
49// Available for MovingWindowFeatures::Min:
50// T Min() const;
51//
52// Available for MovingWindowFeatures::Max:
53// T Max() const;
54//
55// Available for MovingWindowFeatures::Mean:
56// U Mean<U>() const;
57//
Ilya Nikolaevskiye846edb12023-09-25 10:45:0258// Available for MovingWindowFeatures::Deviation:
59// U Deviation<U>() const;
Ilya Nikolaevskiy07923942023-09-19 10:05:0460//
61// Available for MovingWindowFeatures::Iteration. Iterating through the window:
62// iterator begin() const;
63// iterator begin() const;
64// size_t size() const;
65
66// Features supported by the class.
67struct MovingWindowFeatures {
68 struct Min {
69 static bool has_min;
70 };
71
72 struct Max {
73 static bool has_max;
74 };
75
76 // Need to specify a type capable of holding a sum of all elements in the
77 // window.
78 template <typename SumType>
79 struct Mean {
80 static SumType has_mean;
81 };
82
83 // Need to specify a type capable of holding a sum of squares of all elements
84 // in the window.
85 template <typename SumType>
Ilya Nikolaevskiye846edb12023-09-25 10:45:0286 struct Deviation {
87 static SumType has_deviation;
Ilya Nikolaevskiy07923942023-09-19 10:05:0488 };
89
90 struct Iteration {
91 static bool has_iteration;
92 };
93};
94
95// Main template.
96template <typename T, typename... Features>
97class MovingWindow;
98
99// Convenience shortcuts.
100template <typename T>
101using MovingMax = MovingWindow<T, MovingWindowFeatures::Max>;
102
103template <typename T>
104using MovingMin = MovingWindow<T, MovingWindowFeatures::Min>;
105
106template <typename T>
107using MovingMinMax =
108 MovingWindow<T, MovingWindowFeatures::Min, MovingWindowFeatures::Max>;
109
Ilya Nikolaevskiy07923942023-09-19 10:05:04110template <typename T, typename SumType>
Ilya Nikolaevskiye846edb12023-09-25 10:45:02111using MovingAverage = MovingWindow<T, MovingWindowFeatures::Mean<SumType>>;
Ilya Nikolaevskiy07923942023-09-19 10:05:04112
Ilya Nikolaevskiye846edb12023-09-25 10:45:02113template <typename T>
114using MovingAverageDeviation =
Ilya Nikolaevskiy07923942023-09-19 10:05:04115 MovingWindow<T,
Ilya Nikolaevskiye846edb12023-09-25 10:45:02116 MovingWindowFeatures::Mean<T>,
117 MovingWindowFeatures::Deviation<double>>;
Ilya Nikolaevskiy07923942023-09-19 10:05:04118
119namespace internal {
120
121// Class responsible only for calculating maximum in the window.
122// It's reused to calculate both min and max via inverting the comparator.
123template <typename T, typename Comparator>
124class MovingExtremumBase {
125 public:
126 explicit MovingExtremumBase(size_t window_size)
127 : window_size_(window_size),
128 values_(window_size),
129 added_at_(window_size),
130 last_idx_(window_size - 1),
131 compare_(Comparator()) {}
132 ~MovingExtremumBase() = default;
133
134 // Add new sample to the stream.
Ilya Nikolaevskiye846edb12023-09-25 10:45:02135 void AddSample(const T& value, size_t total_added) {
Ilya Nikolaevskiy07923942023-09-19 10:05:04136 // Remove old elements from the back of the window;
137 while (size_ > 0 && added_at_[begin_idx_] + window_size_ <= total_added) {
138 ++begin_idx_;
139 if (begin_idx_ == window_size_) {
140 begin_idx_ = 0;
141 }
142 --size_;
143 }
144 // Remove small elements from the front of the window because they can never
145 // become the maximum in the window since the currently added element is
146 // bigger than them and will leave the window later.
147 while (size_ > 0 && compare_(values_[last_idx_], value)) {
148 if (last_idx_ == 0) {
149 last_idx_ = window_size_;
150 }
151 --last_idx_;
152 --size_;
153 }
154 DCHECK_LT(size_, window_size_);
155 ++last_idx_;
156 if (last_idx_ == window_size_) {
157 last_idx_ = 0;
158 }
159 values_[last_idx_] = value;
160 added_at_[last_idx_] = total_added;
161 ++size_;
162 }
163
164 // Get the maximum of the last `window_size` elements.
165 T Value() const {
166 DCHECK_GT(size_, 0u);
167 return values_[begin_idx_];
168 }
169
170 // Clear all samples.
171 void Reset() {
172 size_ = 0;
173 begin_idx_ = 0;
174 last_idx_ = window_size_ - 1;
175 }
176
177 private:
178 const size_t window_size_;
179 // Circular buffer with some values in the window.
180 // Only possible candidates for maximum are stored:
181 // values form a non-increasing sequence.
182 std::vector<T> values_;
183 // Circular buffer storing when numbers in `values_` were added.
184 std::vector<size_t> added_at_;
185 // Begin of the circular buffers above.
186 size_t begin_idx_ = 0;
187 // Last occupied position.
188 size_t last_idx_;
189 // How many elements are stored in the circular buffers above.
190 size_t size_ = 0;
191 // Template parameter comparator.
192 const Comparator compare_;
193};
194
195// Null implementation of the above class to be used when feature is disabled.
196template <typename T>
197struct NullExtremumImpl {
198 explicit NullExtremumImpl(size_t) {}
199 ~NullExtremumImpl() = default;
Ilya Nikolaevskiye846edb12023-09-25 10:45:02200 void AddSample(const T&, size_t) {}
Ilya Nikolaevskiy07923942023-09-19 10:05:04201 void Reset() {}
202};
203
204// Class to hold the moving window.
Ilya Nikolaevskiye846edb12023-09-25 10:45:02205// It's used to calculate replaced element for Mean/Deviation calculations.
Ilya Nikolaevskiy07923942023-09-19 10:05:04206template <typename T>
207class MovingWindowBase {
208 public:
209 explicit MovingWindowBase(size_t window_size) : values_(window_size) {}
210
211 ~MovingWindowBase() = default;
212
213 void AddSample(const T& sample) {
214 values_[cur_idx_] = sample;
215 ++cur_idx_;
216 if (cur_idx_ == values_.size()) {
217 cur_idx_ = 0;
218 }
219 }
220
221 // Is the window filled integer amount of times.
222 bool IsLastIdx() const { return cur_idx_ + 1 == values_.size(); }
223
224 void Reset() {
225 cur_idx_ = 0;
226 std::fill(values_.begin(), values_.end(), T());
227 }
228
229 T GetValue() const { return values_[cur_idx_]; }
230
231 T operator[](size_t idx) const { return values_[idx]; }
232
233 size_t Size() const { return values_.size(); }
234
235 // What index will be overwritten by a new element;
236 size_t CurIdx() const { return cur_idx_; }
237
238 private:
239 // Circular buffer.
240 std::vector<T> values_;
241 // Where the buffer begins.
242 size_t cur_idx_ = 0;
243};
244
245// Null implementation of the above class to be used when feature is disabled.
246template <typename T>
247struct NullWindowImpl {
248 explicit NullWindowImpl(size_t) {}
249 ~NullWindowImpl() = default;
250 void AddSample(const T& sample) {}
251 bool IsLastIdx() const { return false; }
252 void Reset() {}
253 T GetValue() const { return T(); }
254};
255
Ilya Nikolaevskiye846edb12023-09-25 10:45:02256// Performs division allowing the class to work with more types.
257// General template.
258template <typename SumType, typename ReturnType>
259struct DivideInternal {
260 static ReturnType Compute(const SumType& sum, const size_t count) {
261 return static_cast<ReturnType>(sum) / static_cast<ReturnType>(count);
262 }
263};
264
Ilya Nikolaevskiy07923942023-09-19 10:05:04265// Class to calculate moving mean.
266template <typename T, typename SumType, bool IsFloating>
267class MovingMeanBase {
268 public:
269 explicit MovingMeanBase(size_t window_size) : sum_() {}
270
271 ~MovingMeanBase() = default;
272
273 void AddSample(const T& sample, const T& replaced_value, bool is_last_idx) {
274 sum_ += sample - replaced_value;
275 }
276
277 template <typename ReturnType = SumType>
Ilya Nikolaevskiye846edb12023-09-25 10:45:02278 ReturnType Mean(const size_t count) const {
Ilya Nikolaevskiy07923942023-09-19 10:05:04279 if (count == 0) {
280 return ReturnType();
281 }
Ilya Nikolaevskiye846edb12023-09-25 10:45:02282 return DivideInternal<SumType, ReturnType>::Compute(sum_, count);
Ilya Nikolaevskiy07923942023-09-19 10:05:04283 }
284 void Reset() { sum_ = SumType(); }
285
286 SumType Sum() const { return sum_; }
287
288 private:
289 SumType sum_;
290};
291
292// Class to calculate moving mean.
293// Variant for float types with running sum to avoid rounding errors
294// accumulation.
295template <typename T, typename SumType>
296class MovingMeanBase<T, SumType, true> {
297 public:
298 explicit MovingMeanBase(size_t window_size) : sum_(), running_sum_() {}
299
300 ~MovingMeanBase() = default;
301
302 void AddSample(const T& sample, const T& replaced_value, bool is_last_idx) {
303 running_sum_ += sample;
304 if (is_last_idx) {
305 // Replace sum with running sum to avoid rounding errors accumulation.
306 sum_ = running_sum_;
307 running_sum_ = SumType();
308 } else {
309 sum_ += sample - replaced_value;
310 }
311 }
312
313 template <typename ReturnType = SumType>
Ilya Nikolaevskiye846edb12023-09-25 10:45:02314 ReturnType Mean(const size_t count) const {
Ilya Nikolaevskiy07923942023-09-19 10:05:04315 if (count == 0) {
316 return ReturnType();
317 }
Ilya Nikolaevskiye846edb12023-09-25 10:45:02318 return DivideInternal<SumType, ReturnType>::Compute(sum_, count);
Ilya Nikolaevskiy07923942023-09-19 10:05:04319 }
320
321 void Reset() { sum_ = running_sum_ = SumType(); }
322
323 SumType Sum() const { return sum_; }
324
325 private:
326 SumType sum_;
327 SumType running_sum_;
328};
329
330// Null implementation of the above class to be used when feature is disabled.
331template <typename T>
332struct NullMeanImpl {
333 explicit NullMeanImpl(size_t window_size) {}
334 ~NullMeanImpl() = default;
335
336 void AddSample(const T& sample, const T&, bool) {}
337
338 void Reset() {}
339};
340
Ilya Nikolaevskiye846edb12023-09-25 10:45:02341// Computs main Deviation fromula, allowing the class to work with more types.
342// Deviation is equal to mean of squared values minus squared mean value.
343// General template.
344template <typename SumType, typename ReturnType>
345struct DeivationInternal {
346 static ReturnType Compute(const SumType& sum_squares,
347 const SumType& square_of_sum,
348 const size_t count) {
349 return static_cast<ReturnType>(
350 std::sqrt((static_cast<double>(sum_squares) -
351 static_cast<double>(square_of_sum) / count) /
352 count));
353 }
354};
355
356// Class to compute square of the number.
357// General template
358template <typename T, typename SquareType>
359struct SquareInternal {
360 static SquareType Compute(const T& sample) {
361 return static_cast<SquareType>(sample) * sample;
362 }
363};
364
365// Class to calculate moving deviation.
Ilya Nikolaevskiy07923942023-09-19 10:05:04366template <typename T, typename SumType, bool IsFloating>
Ilya Nikolaevskiye846edb12023-09-25 10:45:02367class MovingDeviationBase {
Ilya Nikolaevskiy07923942023-09-19 10:05:04368 public:
Ilya Nikolaevskiye846edb12023-09-25 10:45:02369 explicit MovingDeviationBase(size_t window_size) : sum_sq_() {}
370 ~MovingDeviationBase() = default;
Ilya Nikolaevskiy07923942023-09-19 10:05:04371 void AddSample(const T& sample, const T& replaced_value, bool is_last_idx) {
Ilya Nikolaevskiye846edb12023-09-25 10:45:02372 sum_sq_ += SquareInternal<T, SumType>::Compute(sample) -
373 SquareInternal<T, SumType>::Compute(replaced_value);
Ilya Nikolaevskiy07923942023-09-19 10:05:04374 }
375
Ilya Nikolaevskiye846edb12023-09-25 10:45:02376 template <typename ReturnType, typename U>
377 ReturnType Deviation(const size_t count, const U& sum) const {
Ilya Nikolaevskiy07923942023-09-19 10:05:04378 if (count == 0) {
Ilya Nikolaevskiye846edb12023-09-25 10:45:02379 return ReturnType();
Ilya Nikolaevskiy07923942023-09-19 10:05:04380 }
Ilya Nikolaevskiye846edb12023-09-25 10:45:02381 return DeivationInternal<SumType, ReturnType>::Compute(
382 sum_sq_, SquareInternal<U, SumType>::Compute(sum), count);
Ilya Nikolaevskiy07923942023-09-19 10:05:04383 }
384 void Reset() { sum_sq_ = SumType(); }
385
386 private:
387 SumType sum_sq_;
388};
389
Ilya Nikolaevskiye846edb12023-09-25 10:45:02390// Class to calculate moving deviation.
Ilya Nikolaevskiy07923942023-09-19 10:05:04391// Variant for float types with running sum to avoid rounding errors
392// accumulation.
393template <typename T, typename SumType>
Ilya Nikolaevskiye846edb12023-09-25 10:45:02394class MovingDeviationBase<T, SumType, true> {
Ilya Nikolaevskiy07923942023-09-19 10:05:04395 public:
Ilya Nikolaevskiye846edb12023-09-25 10:45:02396 explicit MovingDeviationBase(size_t window_size)
397 : sum_sq_(), running_sum_() {}
398 ~MovingDeviationBase() = default;
Ilya Nikolaevskiy07923942023-09-19 10:05:04399 void AddSample(const T& sample, const T& replaced_value, bool is_last_idx) {
Ilya Nikolaevskiye846edb12023-09-25 10:45:02400 SumType square = SquareInternal<T, SumType>::Compute(sample);
Ilya Nikolaevskiy07923942023-09-19 10:05:04401 running_sum_ += square;
402 if (is_last_idx) {
403 // Replace sum with running sum to avoid rounding errors accumulation.
404 sum_sq_ = running_sum_;
405 running_sum_ = SumType();
406 } else {
Ilya Nikolaevskiye846edb12023-09-25 10:45:02407 sum_sq_ += square - SquareInternal<T, SumType>::Compute(replaced_value);
Ilya Nikolaevskiy07923942023-09-19 10:05:04408 }
409 }
Ilya Nikolaevskiye846edb12023-09-25 10:45:02410
411 template <typename ReturnType, typename U>
412 ReturnType Deviation(const size_t count, const U& sum) const {
Ilya Nikolaevskiy07923942023-09-19 10:05:04413 if (count == 0) {
Ilya Nikolaevskiye846edb12023-09-25 10:45:02414 return ReturnType();
Ilya Nikolaevskiy07923942023-09-19 10:05:04415 }
Ilya Nikolaevskiye846edb12023-09-25 10:45:02416 return DeivationInternal<SumType, ReturnType>::Compute(
417 sum_sq_, SquareInternal<U, SumType>::Compute(sum), count);
Ilya Nikolaevskiy07923942023-09-19 10:05:04418 }
419 void Reset() { running_sum_ = sum_sq_ = SumType(); }
420
421 private:
422 SumType sum_sq_;
423 SumType running_sum_;
424};
425
426// Null implementation of the above class to be used when feature is disabled.
427template <typename T>
Ilya Nikolaevskiye846edb12023-09-25 10:45:02428struct NullDeviationImpl {
Ilya Nikolaevskiy07923942023-09-19 10:05:04429 public:
Ilya Nikolaevskiye846edb12023-09-25 10:45:02430 explicit NullDeviationImpl(size_t window_size) {}
431 ~NullDeviationImpl() = default;
Ilya Nikolaevskiy07923942023-09-19 10:05:04432 void AddSample(const T&, const T&, bool) {}
433 void Reset() {}
434};
435
436// Template helpers.
437
438// Gets all enabled features in one struct.
439template <typename... Features>
440struct EnabledFeatures : public Features... {};
441
Ilya Nikolaevskiy07923942023-09-19 10:05:04442template <typename T>
Andrew Rayskiy7a78dea82023-11-23 17:50:12443concept has_member_min = requires { T::has_min; };
Ilya Nikolaevskiy07923942023-09-19 10:05:04444
Ilya Nikolaevskiy07923942023-09-19 10:05:04445template <typename T>
Andrew Rayskiy7a78dea82023-11-23 17:50:12446concept has_member_max = requires { T::has_max; };
Ilya Nikolaevskiy07923942023-09-19 10:05:04447
Ilya Nikolaevskiy07923942023-09-19 10:05:04448template <typename T>
Andrew Rayskiy7a78dea82023-11-23 17:50:12449concept has_member_mean = requires { T::has_mean; };
Ilya Nikolaevskiy07923942023-09-19 10:05:04450
Ilya Nikolaevskiy07923942023-09-19 10:05:04451template <typename T>
Andrew Rayskiy7a78dea82023-11-23 17:50:12452concept has_member_deviation = requires { T::has_deviation; };
Ilya Nikolaevskiy07923942023-09-19 10:05:04453
Ilya Nikolaevskiy07923942023-09-19 10:05:04454template <typename T>
Andrew Rayskiy7a78dea82023-11-23 17:50:12455concept has_member_iteration = requires { T::has_iteration; };
Ilya Nikolaevskiy07923942023-09-19 10:05:04456
457// Gets the type of the member if present.
458// Can't just use decltype, because the member might be absent.
Andrew Rayskiy7a78dea82023-11-23 17:50:12459template <typename T>
Ilya Nikolaevskiy07923942023-09-19 10:05:04460struct get_type_mean {
Andrew Rayskiy7a78dea82023-11-23 17:50:12461 using type = void;
Ilya Nikolaevskiy07923942023-09-19 10:05:04462};
463template <typename T>
Andrew Rayskiy7a78dea82023-11-23 17:50:12464 requires has_member_mean<T>
465struct get_type_mean<T> {
466 using type = decltype(T::has_mean);
Ilya Nikolaevskiy07923942023-09-19 10:05:04467};
Andrew Rayskiy7a78dea82023-11-23 17:50:12468template <typename T>
469using mean_t = typename get_type_mean<T>::type;
Ilya Nikolaevskiy07923942023-09-19 10:05:04470
Andrew Rayskiy7a78dea82023-11-23 17:50:12471template <typename T>
Ilya Nikolaevskiye846edb12023-09-25 10:45:02472struct get_type_deviation {
Andrew Rayskiy7a78dea82023-11-23 17:50:12473 using type = void;
Ilya Nikolaevskiy07923942023-09-19 10:05:04474};
475template <typename T>
Andrew Rayskiy7a78dea82023-11-23 17:50:12476 requires has_member_deviation<T>
477struct get_type_deviation<T> {
478 using type = decltype(T::has_deviation);
Ilya Nikolaevskiy07923942023-09-19 10:05:04479};
Andrew Rayskiy7a78dea82023-11-23 17:50:12480template <typename T>
481using deviation_t = typename get_type_deviation<T>::type;
Ilya Nikolaevskiye846edb12023-09-25 10:45:02482
483// Performs division allowing the class to work with more types.
484// Specific template for TimeDelta.
485template <>
486struct DivideInternal<TimeDelta, TimeDelta> {
487 static TimeDelta Compute(const TimeDelta& sum, const size_t count) {
488 return sum / count;
489 }
490};
491
492// Computs main Deviation fromula, allowing the class to work with more types.
493// Deviation is equal to mean of squared values minus squared mean value.
494// Specific template for TimeDelta.
495template <>
496struct DeivationInternal<double, TimeDelta> {
497 static TimeDelta Compute(const double sum_squares,
498 const double square_of_sum,
499 const size_t count) {
500 return Seconds(std::sqrt((sum_squares - square_of_sum / count) / count));
501 }
502};
503
504// Class to compute square of the number.
505// Specific template for TimeDelta.
506template <>
507struct SquareInternal<TimeDelta, double> {
508 static double Compute(const TimeDelta& sample) {
509 return sample.InSecondsF() * sample.InSecondsF();
510 }
511};
512
Ilya Nikolaevskiy07923942023-09-19 10:05:04513} // namespace internal
514
515// Implementation of the main class.
516template <typename T, typename... Features>
517class MovingWindow {
518 public:
519 // List of all requested features.
520 using EnabledFeatures = internal::EnabledFeatures<Features...>;
521
522 explicit MovingWindow(size_t window_size)
523 : min_impl_(window_size),
524 max_impl_(window_size),
525 mean_impl_(window_size),
Ilya Nikolaevskiye846edb12023-09-25 10:45:02526 deviation_impl_(window_size),
Ilya Nikolaevskiy07923942023-09-19 10:05:04527 window_impl_(window_size) {}
528
529 // Adds sample to the window.
Ilya Nikolaevskiye846edb12023-09-25 10:45:02530 void AddSample(const T& sample) {
Ilya Nikolaevskiy07923942023-09-19 10:05:04531 ++total_added_;
532 min_impl_.AddSample(sample, total_added_);
533 max_impl_.AddSample(sample, total_added_);
534 mean_impl_.AddSample(sample, window_impl_.GetValue(),
535 window_impl_.IsLastIdx());
Ilya Nikolaevskiye846edb12023-09-25 10:45:02536 deviation_impl_.AddSample(sample, window_impl_.GetValue(),
537 window_impl_.IsLastIdx());
Ilya Nikolaevskiy07923942023-09-19 10:05:04538 window_impl_.AddSample(sample);
539 }
540
541 // Returns amount of elementes so far in the stream (might be bigger than the
542 // window size).
543 size_t Count() const { return total_added_; }
544
Andrew Rayskiy7a78dea82023-11-23 17:50:12545 // Calculates min in the window.
546 T Min() const
547 requires internal::has_member_min<EnabledFeatures>
548 {
Ilya Nikolaevskiy07923942023-09-19 10:05:04549 return min_impl_.Value();
550 }
551
Andrew Rayskiy7a78dea82023-11-23 17:50:12552 // Calculates max in the window.
553 T Max() const
554 requires internal::has_member_max<EnabledFeatures>
555 {
Ilya Nikolaevskiy07923942023-09-19 10:05:04556 return max_impl_.Value();
557 }
558
Andrew Rayskiy7a78dea82023-11-23 17:50:12559 // Calculates mean in the window.
560 // `ReturnType` can be used to adjust the type of the calculated mean value;
561 // if not specified, uses `T` by default.
562 template <typename ReturnType = T>
563 requires internal::has_member_mean<EnabledFeatures>
Ilya Nikolaevskiy07923942023-09-19 10:05:04564 ReturnType Mean() const {
565 return mean_impl_.template Mean<ReturnType>(
566 std::min(total_added_, window_impl_.Size()));
567 }
568
Andrew Rayskiy7a78dea82023-11-23 17:50:12569 // Calculates deviation in the window.
570 // `ReturnType` can be used to adjust the type of the calculated deviation
571 // value; if not specified, uses `T` by default.
572 template <typename ReturnType = T>
573 requires internal::has_member_deviation<EnabledFeatures>
Ilya Nikolaevskiye846edb12023-09-25 10:45:02574 ReturnType Deviation() const {
Ilya Nikolaevskiy07923942023-09-19 10:05:04575 const size_t count = std::min(total_added_, window_impl_.Size());
Ilya Nikolaevskiye846edb12023-09-25 10:45:02576 return deviation_impl_.template Deviation<ReturnType>(count,
577 mean_impl_.Sum());
Ilya Nikolaevskiy07923942023-09-19 10:05:04578 }
579
580 // Resets the state to an empty window.
581 void Reset() {
582 min_impl_.Reset();
583 max_impl_.Reset();
584 mean_impl_.Reset();
Ilya Nikolaevskiye846edb12023-09-25 10:45:02585 deviation_impl_.Reset();
Ilya Nikolaevskiy07923942023-09-19 10:05:04586 window_impl_.Reset();
587 total_added_ = 0;
588 }
589
590 // iterator implementation.
591 class iterator {
592 public:
593 ~iterator() = default;
594
595 const T operator*() {
596 DCHECK_LT(idx_, window_impl_->Size());
597 return (*window_impl_)[idx_];
598 }
599
600 iterator& operator++() {
601 ++idx_;
602 // Wrap around the circular buffer.
603 if (idx_ == window_impl_->Size()) {
604 idx_ = 0;
605 }
606 // The only way to arrive to the current element is to
607 // come around after iterating through the whole window.
608 if (idx_ == window_impl_->CurIdx()) {
609 idx_ = kInvalidIndex;
610 }
611 return *this;
612 }
613
614 bool operator==(const iterator& other) const { return idx_ == other.idx_; }
615
616 private:
617 iterator(const internal::MovingWindowBase<T>& window, size_t idx)
618 : window_impl_(window), idx_(idx) {}
619
620 static const size_t kInvalidIndex = std::numeric_limits<size_t>::max();
621
622 raw_ref<const internal::MovingWindowBase<T>> window_impl_;
623 size_t idx_;
624
625 friend class MovingWindow<T, Features...>;
626 };
627
628 // Begin iterator. Template to enable only if iteration feature is requested.
Andrew Rayskiy7a78dea82023-11-23 17:50:12629 iterator begin() const
630 requires internal::has_member_iteration<EnabledFeatures>
631 {
Ilya Nikolaevskiy07923942023-09-19 10:05:04632 if (total_added_ == 0) {
633 return end();
634 }
635 // Before window is fully filled, the oldest element is at the index 0.
636 size_t idx =
637 (total_added_ < window_impl_.Size()) ? 0 : window_impl_.CurIdx();
638
639 return iterator(window_impl_, idx);
640 }
641
642 // End iterator. Template to enable only if iteration feature is requested.
Andrew Rayskiy7a78dea82023-11-23 17:50:12643 iterator end() const
644 requires internal::has_member_iteration<EnabledFeatures>
645 {
Ilya Nikolaevskiy07923942023-09-19 10:05:04646 return iterator(window_impl_, iterator::kInvalidIndex);
647 }
648
649 // Size of the collection. Template to enable only if iteration feature is
650 // requested.
Andrew Rayskiy7a78dea82023-11-23 17:50:12651 size_t size() const
652 requires internal::has_member_iteration<EnabledFeatures>
653 {
Ilya Nikolaevskiy07923942023-09-19 10:05:04654 return std::min(total_added_, window_impl_.Size());
655 }
656
657 private:
658 // Member for calculating min.
659 // Conditionally enabled on Min feature.
Andrew Rayskiy7a78dea82023-11-23 17:50:12660 std::conditional_t<internal::has_member_min<EnabledFeatures>,
661 internal::MovingExtremumBase<T, std::greater<>>,
662 internal::NullExtremumImpl<T>>
663 min_impl_;
Ilya Nikolaevskiy07923942023-09-19 10:05:04664
665 // Member for calculating min.
666 // Conditionally enabled on Min feature.
Andrew Rayskiy7a78dea82023-11-23 17:50:12667 std::conditional_t<internal::has_member_max<EnabledFeatures>,
668 internal::MovingExtremumBase<T, std::less<>>,
669 internal::NullExtremumImpl<T>>
670 max_impl_;
Ilya Nikolaevskiy07923942023-09-19 10:05:04671
Ilya Nikolaevskiye846edb12023-09-25 10:45:02672 // Type for sum value in Mean implementation. Might need to reuse deviation
673 // sum type, because enabling only deviation feature will also enable mean
674 // member (because deviation calculation depends on mean calculation).
Andrew Rayskiy7a78dea82023-11-23 17:50:12675 using MeanSumType =
676 std::conditional_t<internal::has_member_mean<EnabledFeatures>,
677 internal::mean_t<EnabledFeatures>,
678 internal::deviation_t<EnabledFeatures>>;
Ilya Nikolaevskiy07923942023-09-19 10:05:04679 // Member for calculating mean.
Ilya Nikolaevskiye846edb12023-09-25 10:45:02680 // Conditionally enabled on Mean or Deviation feature (because deviation
Ilya Nikolaevskiy07923942023-09-19 10:05:04681 // calculation depends on mean calculation).
Andrew Rayskiy7a78dea82023-11-23 17:50:12682 std::conditional_t<
683 internal::has_member_mean<EnabledFeatures> ||
684 internal::has_member_deviation<EnabledFeatures>,
Andrew Rayskiy629912382023-10-18 22:58:42685 internal::
686 MovingMeanBase<T, MeanSumType, std::is_floating_point_v<MeanSumType>>,
Andrew Rayskiy7a78dea82023-11-23 17:50:12687 internal::NullMeanImpl<T>>
688 mean_impl_;
Ilya Nikolaevskiy07923942023-09-19 10:05:04689
Ilya Nikolaevskiye846edb12023-09-25 10:45:02690 // Member for calculating deviation.
691 // Conditionally enabled on Deviation feature.
Andrew Rayskiy7a78dea82023-11-23 17:50:12692 std::conditional_t<
693 internal::has_member_deviation<EnabledFeatures>,
Ilya Nikolaevskiye846edb12023-09-25 10:45:02694 internal::MovingDeviationBase<
Ilya Nikolaevskiy07923942023-09-19 10:05:04695 T,
Andrew Rayskiy7a78dea82023-11-23 17:50:12696 internal::deviation_t<EnabledFeatures>,
697 std::is_floating_point_v<internal::deviation_t<EnabledFeatures>>>,
698 internal::NullDeviationImpl<T>>
699 deviation_impl_;
Ilya Nikolaevskiy07923942023-09-19 10:05:04700
701 // Member for storing the moving window.
Ilya Nikolaevskiye846edb12023-09-25 10:45:02702 // Conditionally enabled on Mean, Deviation or Iteration feature since
Ilya Nikolaevskiy07923942023-09-19 10:05:04703 // they need the elements in the window.
704 // Min and Max features store elements internally so they don't need this.
Andrew Rayskiy7a78dea82023-11-23 17:50:12705 std::conditional_t<internal::has_member_mean<EnabledFeatures> ||
706 internal::has_member_deviation<EnabledFeatures> ||
707 internal::has_member_iteration<EnabledFeatures>,
708 internal::MovingWindowBase<T>,
709 internal::NullWindowImpl<T>>
710 window_impl_;
Ilya Nikolaevskiy07923942023-09-19 10:05:04711 // Total number of added elements.
712 size_t total_added_ = 0;
713};
714
715} // namespace base
716
717#endif // BASE_MOVING_WINDOW_H_