xref: /aosp_15_r20/external/cronet/base/moving_window.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // 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 
8 #include <math.h>
9 #include <stddef.h>
10 
11 #include <cmath>
12 #include <functional>
13 #include <limits>
14 #include <vector>
15 
16 #include "base/check_op.h"
17 #include "base/memory/raw_ref.h"
18 #include "base/time/time.h"
19 
20 namespace base {
21 
22 // Class to efficiently calculate statistics in a sliding window.
23 // This class isn't thread safe.
24 // Supported statistics are Min/Max/Mean/Deviation.
25 // 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
37 // features:
38 // MovingMax/MovingMin/MovingAverage/MovingAverageDeviation/MovingMinMax.
39 //
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 //
58 // Available for MovingWindowFeatures::Deviation:
59 //    U Deviation<U>() const;
60 //
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.
67 struct 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>
86   struct Deviation {
87     static SumType has_deviation;
88   };
89 
90   struct Iteration {
91     static bool has_iteration;
92   };
93 };
94 
95 // Main template.
96 template <typename T, typename... Features>
97 class MovingWindow;
98 
99 // Convenience shortcuts.
100 template <typename T>
101 using MovingMax = MovingWindow<T, MovingWindowFeatures::Max>;
102 
103 template <typename T>
104 using MovingMin = MovingWindow<T, MovingWindowFeatures::Min>;
105 
106 template <typename T>
107 using MovingMinMax =
108     MovingWindow<T, MovingWindowFeatures::Min, MovingWindowFeatures::Max>;
109 
110 template <typename T, typename SumType>
111 using MovingAverage = MovingWindow<T, MovingWindowFeatures::Mean<SumType>>;
112 
113 template <typename T>
114 using MovingAverageDeviation =
115     MovingWindow<T,
116                  MovingWindowFeatures::Mean<T>,
117                  MovingWindowFeatures::Deviation<double>>;
118 
119 namespace 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.
123 template <typename T, typename Comparator>
124 class MovingExtremumBase {
125  public:
MovingExtremumBase(size_t window_size)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.
AddSample(const T & value,size_t total_added)135   void AddSample(const T& value, size_t total_added) {
136     // 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.
Value()165   T Value() const {
166     DCHECK_GT(size_, 0u);
167     return values_[begin_idx_];
168   }
169 
170   // Clear all samples.
Reset()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.
196 template <typename T>
197 struct NullExtremumImpl {
NullExtremumImplNullExtremumImpl198   explicit NullExtremumImpl(size_t) {}
199   ~NullExtremumImpl() = default;
AddSampleNullExtremumImpl200   void AddSample(const T&, size_t) {}
ResetNullExtremumImpl201   void Reset() {}
202 };
203 
204 // Class to hold the moving window.
205 // It's used to calculate replaced element for Mean/Deviation calculations.
206 template <typename T>
207 class MovingWindowBase {
208  public:
MovingWindowBase(size_t window_size)209   explicit MovingWindowBase(size_t window_size) : values_(window_size) {}
210 
211   ~MovingWindowBase() = default;
212 
AddSample(const T & sample)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.
IsLastIdx()222   bool IsLastIdx() const { return cur_idx_ + 1 == values_.size(); }
223 
Reset()224   void Reset() {
225     cur_idx_ = 0;
226     std::fill(values_.begin(), values_.end(), T());
227   }
228 
GetValue()229   T GetValue() const { return values_[cur_idx_]; }
230 
231   T operator[](size_t idx) const { return values_[idx]; }
232 
Size()233   size_t Size() const { return values_.size(); }
234 
235   // What index will be overwritten by a new element;
CurIdx()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.
246 template <typename T>
247 struct NullWindowImpl {
NullWindowImplNullWindowImpl248   explicit NullWindowImpl(size_t) {}
249   ~NullWindowImpl() = default;
AddSampleNullWindowImpl250   void AddSample(const T& sample) {}
IsLastIdxNullWindowImpl251   bool IsLastIdx() const { return false; }
ResetNullWindowImpl252   void Reset() {}
GetValueNullWindowImpl253   T GetValue() const { return T(); }
254 };
255 
256 // Performs division allowing the class to work with more types.
257 // General template.
258 template <typename SumType, typename ReturnType>
259 struct DivideInternal {
ComputeDivideInternal260   static ReturnType Compute(const SumType& sum, const size_t count) {
261     return static_cast<ReturnType>(sum) / static_cast<ReturnType>(count);
262   }
263 };
264 
265 // Class to calculate moving mean.
266 template <typename T, typename SumType, bool IsFloating>
267 class MovingMeanBase {
268  public:
MovingMeanBase(size_t window_size)269   explicit MovingMeanBase(size_t window_size) : sum_() {}
270 
271   ~MovingMeanBase() = default;
272 
AddSample(const T & sample,const T & replaced_value,bool is_last_idx)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>
Mean(const size_t count)278   ReturnType Mean(const size_t count) const {
279     if (count == 0) {
280       return ReturnType();
281     }
282     return DivideInternal<SumType, ReturnType>::Compute(sum_, count);
283   }
Reset()284   void Reset() { sum_ = SumType(); }
285 
Sum()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.
295 template <typename T, typename SumType>
296 class MovingMeanBase<T, SumType, true> {
297  public:
MovingMeanBase(size_t window_size)298   explicit MovingMeanBase(size_t window_size) : sum_(), running_sum_() {}
299 
300   ~MovingMeanBase() = default;
301 
AddSample(const T & sample,const T & replaced_value,bool is_last_idx)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>
Mean(const size_t count)314   ReturnType Mean(const size_t count) const {
315     if (count == 0) {
316       return ReturnType();
317     }
318     return DivideInternal<SumType, ReturnType>::Compute(sum_, count);
319   }
320 
Reset()321   void Reset() { sum_ = running_sum_ = SumType(); }
322 
Sum()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.
331 template <typename T>
332 struct NullMeanImpl {
NullMeanImplNullMeanImpl333   explicit NullMeanImpl(size_t window_size) {}
334   ~NullMeanImpl() = default;
335 
AddSampleNullMeanImpl336   void AddSample(const T& sample, const T&, bool) {}
337 
ResetNullMeanImpl338   void Reset() {}
339 };
340 
341 // 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.
344 template <typename SumType, typename ReturnType>
345 struct DeivationInternal {
ComputeDeivationInternal346   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
358 template <typename T, typename SquareType>
359 struct SquareInternal {
ComputeSquareInternal360   static SquareType Compute(const T& sample) {
361     return static_cast<SquareType>(sample) * sample;
362   }
363 };
364 
365 // Class to calculate moving deviation.
366 template <typename T, typename SumType, bool IsFloating>
367 class MovingDeviationBase {
368  public:
MovingDeviationBase(size_t window_size)369   explicit MovingDeviationBase(size_t window_size) : sum_sq_() {}
370   ~MovingDeviationBase() = default;
AddSample(const T & sample,const T & replaced_value,bool is_last_idx)371   void AddSample(const T& sample, const T& replaced_value, bool is_last_idx) {
372     sum_sq_ += SquareInternal<T, SumType>::Compute(sample) -
373                SquareInternal<T, SumType>::Compute(replaced_value);
374   }
375 
376   template <typename ReturnType, typename U>
Deviation(const size_t count,const U & sum)377   ReturnType Deviation(const size_t count, const U& sum) const {
378     if (count == 0) {
379       return ReturnType();
380     }
381     return DeivationInternal<SumType, ReturnType>::Compute(
382         sum_sq_, SquareInternal<U, SumType>::Compute(sum), count);
383   }
Reset()384   void Reset() { sum_sq_ = SumType(); }
385 
386  private:
387   SumType sum_sq_;
388 };
389 
390 // Class to calculate moving deviation.
391 // Variant for float types with running sum to avoid rounding errors
392 // accumulation.
393 template <typename T, typename SumType>
394 class MovingDeviationBase<T, SumType, true> {
395  public:
MovingDeviationBase(size_t window_size)396   explicit MovingDeviationBase(size_t window_size)
397       : sum_sq_(), running_sum_() {}
398   ~MovingDeviationBase() = default;
AddSample(const T & sample,const T & replaced_value,bool is_last_idx)399   void AddSample(const T& sample, const T& replaced_value, bool is_last_idx) {
400     SumType square = SquareInternal<T, SumType>::Compute(sample);
401     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 {
407       sum_sq_ += square - SquareInternal<T, SumType>::Compute(replaced_value);
408     }
409   }
410 
411   template <typename ReturnType, typename U>
Deviation(const size_t count,const U & sum)412   ReturnType Deviation(const size_t count, const U& sum) const {
413     if (count == 0) {
414       return ReturnType();
415     }
416     return DeivationInternal<SumType, ReturnType>::Compute(
417         sum_sq_, SquareInternal<U, SumType>::Compute(sum), count);
418   }
Reset()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.
427 template <typename T>
428 struct NullDeviationImpl {
429  public:
NullDeviationImplNullDeviationImpl430   explicit NullDeviationImpl(size_t window_size) {}
431   ~NullDeviationImpl() = default;
AddSampleNullDeviationImpl432   void AddSample(const T&, const T&, bool) {}
ResetNullDeviationImpl433   void Reset() {}
434 };
435 
436 // Template helpers.
437 
438 // Gets all enabled features in one struct.
439 template <typename... Features>
440 struct EnabledFeatures : public Features... {};
441 
442 template <typename T>
443 concept has_member_min = requires { T::has_min; };
444 
445 template <typename T>
446 concept has_member_max = requires { T::has_max; };
447 
448 template <typename T>
449 concept has_member_mean = requires { T::has_mean; };
450 
451 template <typename T>
452 concept has_member_deviation = requires { T::has_deviation; };
453 
454 template <typename T>
455 concept has_member_iteration = requires { T::has_iteration; };
456 
457 // Gets the type of the member if present.
458 // Can't just use decltype, because the member might be absent.
459 template <typename T>
460 struct get_type_mean {
461   using type = void;
462 };
463 template <typename T>
464   requires has_member_mean<T>
465 struct get_type_mean<T> {
466   using type = decltype(T::has_mean);
467 };
468 template <typename T>
469 using mean_t = typename get_type_mean<T>::type;
470 
471 template <typename T>
472 struct get_type_deviation {
473   using type = void;
474 };
475 template <typename T>
476   requires has_member_deviation<T>
477 struct get_type_deviation<T> {
478   using type = decltype(T::has_deviation);
479 };
480 template <typename T>
481 using deviation_t = typename get_type_deviation<T>::type;
482 
483 // Performs division allowing the class to work with more types.
484 // Specific template for TimeDelta.
485 template <>
486 struct 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.
495 template <>
496 struct 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.
506 template <>
507 struct SquareInternal<TimeDelta, double> {
508   static double Compute(const TimeDelta& sample) {
509     return sample.InSecondsF() * sample.InSecondsF();
510   }
511 };
512 
513 }  // namespace internal
514 
515 // Implementation of the main class.
516 template <typename T, typename... Features>
517 class 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),
526         deviation_impl_(window_size),
527         window_impl_(window_size) {}
528 
529   // Adds sample to the window.
530   void AddSample(const T& sample) {
531     ++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());
536     deviation_impl_.AddSample(sample, window_impl_.GetValue(),
537                               window_impl_.IsLastIdx());
538     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 
545   // Calculates min in the window.
546   T Min() const
547     requires internal::has_member_min<EnabledFeatures>
548   {
549     return min_impl_.Value();
550   }
551 
552   // Calculates max in the window.
553   T Max() const
554     requires internal::has_member_max<EnabledFeatures>
555   {
556     return max_impl_.Value();
557   }
558 
559   // 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>
564   ReturnType Mean() const {
565     return mean_impl_.template Mean<ReturnType>(
566         std::min(total_added_, window_impl_.Size()));
567   }
568 
569   // 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>
574   ReturnType Deviation() const {
575     const size_t count = std::min(total_added_, window_impl_.Size());
576     return deviation_impl_.template Deviation<ReturnType>(count,
577                                                           mean_impl_.Sum());
578   }
579 
580   // Resets the state to an empty window.
581   void Reset() {
582     min_impl_.Reset();
583     max_impl_.Reset();
584     mean_impl_.Reset();
585     deviation_impl_.Reset();
586     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.
629   iterator begin() const
630     requires internal::has_member_iteration<EnabledFeatures>
631   {
632     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.
643   iterator end() const
644     requires internal::has_member_iteration<EnabledFeatures>
645   {
646     return iterator(window_impl_, iterator::kInvalidIndex);
647   }
648 
649   // Size of the collection. Template to enable only if iteration feature is
650   // requested.
651   size_t size() const
652     requires internal::has_member_iteration<EnabledFeatures>
653   {
654     return std::min(total_added_, window_impl_.Size());
655   }
656 
657  private:
658   // Member for calculating min.
659   // Conditionally enabled on Min feature.
660   std::conditional_t<internal::has_member_min<EnabledFeatures>,
661                      internal::MovingExtremumBase<T, std::greater<>>,
662                      internal::NullExtremumImpl<T>>
663       min_impl_;
664 
665   // Member for calculating min.
666   // Conditionally enabled on Min feature.
667   std::conditional_t<internal::has_member_max<EnabledFeatures>,
668                      internal::MovingExtremumBase<T, std::less<>>,
669                      internal::NullExtremumImpl<T>>
670       max_impl_;
671 
672   // 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).
675   using MeanSumType =
676       std::conditional_t<internal::has_member_mean<EnabledFeatures>,
677                          internal::mean_t<EnabledFeatures>,
678                          internal::deviation_t<EnabledFeatures>>;
679   // Member for calculating mean.
680   // Conditionally enabled on Mean or Deviation feature (because deviation
681   // calculation depends on mean calculation).
682   std::conditional_t<
683       internal::has_member_mean<EnabledFeatures> ||
684           internal::has_member_deviation<EnabledFeatures>,
685       internal::
686           MovingMeanBase<T, MeanSumType, std::is_floating_point_v<MeanSumType>>,
687       internal::NullMeanImpl<T>>
688       mean_impl_;
689 
690   // Member for calculating deviation.
691   // Conditionally enabled on Deviation feature.
692   std::conditional_t<
693       internal::has_member_deviation<EnabledFeatures>,
694       internal::MovingDeviationBase<
695           T,
696           internal::deviation_t<EnabledFeatures>,
697           std::is_floating_point_v<internal::deviation_t<EnabledFeatures>>>,
698       internal::NullDeviationImpl<T>>
699       deviation_impl_;
700 
701   // Member for storing the moving window.
702   // Conditionally enabled on Mean, Deviation or Iteration feature since
703   // they need the elements in the window.
704   // Min and Max features store elements internally so they don't need this.
705   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_;
711   // Total number of added elements.
712   size_t total_added_ = 0;
713 };
714 
715 }  // namespace base
716 
717 #endif  // BASE_MOVING_WINDOW_H_
718