1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* A set abstraction for enumeration values. */
8 
9 #ifndef mozilla_RollingMean_h_
10 #define mozilla_RollingMean_h_
11 
12 #include "mozilla/Assertions.h"
13 #include "mozilla/TypeTraits.h"
14 #include "mozilla/Vector.h"
15 
16 #include <stddef.h>
17 
18 namespace mozilla {
19 
20 /**
21  * RollingMean<T> calculates a rolling mean of the values it is given. It
22  * accumulates the total as values are added and removed. The second type
23  * argument S specifies the type of the total. This may need to be a bigger
24  * type in order to maintain that the sum of all values in the average doesn't
25  * exceed the maximum input value.
26  *
27  * WARNING: Float types are not supported due to rounding errors.
28  */
29 template<typename T, typename S>
30 class RollingMean
31 {
32 private:
33   size_t mInsertIndex;
34   size_t mMaxValues;
35   Vector<T> mValues;
36   S mTotal;
37 
38 public:
39   static_assert(!IsFloatingPoint<T>::value,
40                 "floating-point types are unsupported due to rounding "
41                 "errors");
42 
43   explicit RollingMean(size_t aMaxValues)
44     : mInsertIndex(0),
45       mMaxValues(aMaxValues),
46       mTotal(0)
47   {
48     MOZ_ASSERT(aMaxValues > 0);
49   }
50 
51   RollingMean& operator=(RollingMean&& aOther)
52   {
53     MOZ_ASSERT(this != &aOther, "self-assignment is forbidden");
54     this->~RollingMean();
55     new(this) RollingMean(aOther.mMaxValues);
56     mInsertIndex = aOther.mInsertIndex;
57     mTotal = aOther.mTotal;
58     mValues.swap(aOther.mValues);
59     return *this;
60   }
61 
62   /**
63    * Insert a value into the rolling mean.
64    */
65   bool insert(T aValue)
66   {
67     MOZ_ASSERT(mValues.length() <= mMaxValues);
68 
69     if (mValues.length() == mMaxValues) {
70       mTotal = mTotal - mValues[mInsertIndex] + aValue;
71       mValues[mInsertIndex] = aValue;
72     } else {
73       if (!mValues.append(aValue)) {
74         return false;
75       }
76       mTotal = mTotal + aValue;
77     }
78 
79     mInsertIndex = (mInsertIndex + 1) % mMaxValues;
80     return true;
81   }
82 
83   /**
84    * Calculate the rolling mean.
85    */
86   T mean()
87   {
88     MOZ_ASSERT(!empty());
ffeglobal_drive(ffeglobal (* fn)(ffeglobal))89     return T(mTotal / int64_t(mValues.length()));
90   }
91 
92   bool empty()
93   {
94     return mValues.empty();
95   }
96 
97   /**
98    * Remove all values from the rolling mean.
99    */
100   void clear()
101   {
102     mValues.clear();
103     mInsertIndex = 0;
ffeglobal_new_(ffename n)104     mTotal = T(0);
105   }
106 
107   size_t maxValues()
108   {
109     return mMaxValues;
110   }
111 };
112 
113 } // namespace mozilla
114 
115 #endif // mozilla_RollingMean_h_
116