Overte C++ Documentation
MovingMinMaxAvg.h
1 //
2 // MovingMinMaxAvg.h
3 // libraries/shared/src
4 //
5 // Created by Yixin Wang on 7/8/2014
6 // Copyright 2013 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 
12 #ifndef hifi_MovingMinMaxAvg_h
13 #define hifi_MovingMinMaxAvg_h
14 
15 #include <cassert>
16 #include <limits>
17 
18 #include "RingBufferHistory.h"
19 
20 template <typename T>
21 class MinMaxAvg {
22 public:
23  MinMaxAvg()
24  : _min(std::numeric_limits<T>::max()),
25  _max(std::numeric_limits<T>::min()),
26  _average(0.0),
27  _samples(0),
28  _last(0)
29  {}
30 
31  void reset() {
32  _min = std::numeric_limits<T>::max();
33  _max = std::numeric_limits<T>::min();
34  _average = 0.0;
35  _samples = 0;
36  _last = 0;
37  }
38 
39  void update(T sample) {
40  if (sample < _min) {
41  _min = sample;
42  }
43  if (sample > _max) {
44  _max = sample;
45  }
46  double totalSamples = _samples + 1;
47  _average = _average * ((double)_samples / totalSamples)
48  + (double)sample / totalSamples;
49  _samples++;
50 
51  _last = sample;
52  }
53 
54  void update(const MinMaxAvg<T>& other) {
55  if (other._min < _min) {
56  _min = other._min;
57  }
58  if (other._max > _max) {
59  _max = other._max;
60  }
61  double totalSamples = _samples + other._samples;
62  if (totalSamples > 0) {
63  _average = _average * ((double)_samples / totalSamples)
64  + other._average * ((double)other._samples / totalSamples);
65  } else {
66  _average = 0.0f;
67  }
68  _samples += other._samples;
69  }
70 
71  T getMin() const { return _min; }
72  T getMax() const { return _max; }
73  double getAverage() const { return _average; }
74  int getSamples() const { return _samples; }
75  double getSum() const { return _samples * _average; }
76  T getLast() const { return _last; }
77 
78 private:
79  T _min;
80  T _max;
81  double _average;
82  int _samples;
83  T _last;
84 };
85 
86 template <typename T>
87 class MovingMinMaxAvg {
88 public:
89  // This class collects 3 stats (min, max, avg) over a moving window of samples.
90  // The moving window contains _windowIntervals * _intervalLength samples.
91  // Those stats are updated every _intervalLength samples collected. When that happens, _newStatsAvaialble is set
92  // to true and it's up to the user to clear that flag.
93  // For example, if you want a moving avg of the past 5000 samples updated every 100 samples, you would instantiate
94  // this class with MovingMinMaxAvg(100, 50). If you want a moving min of the past 100 samples updated on every
95  // new sample, instantiate this class with MovingMinMaxAvg(1, 100).
96 
97 
100  MovingMinMaxAvg(int intervalLength, int windowIntervals)
101  : _intervalLength(intervalLength),
102  _windowIntervals(windowIntervals),
103  _overallStats(),
104  _windowStats(),
105  _currentIntervalStats(),
106  _intervalStats(windowIntervals),
107  _newStatsAvailable(false)
108  {}
109 
110  void reset() {
111  _overallStats.reset();
112  _windowStats.reset();
113  _currentIntervalStats.reset();
114  _intervalStats.clear();
115  _newStatsAvailable = false;
116  }
117 
118  void setWindowIntervals(int windowIntervals) {
119  _windowIntervals = windowIntervals;
120  _overallStats.reset();
121  _windowStats.reset();
122  _currentIntervalStats.reset();
123  _intervalStats.setCapacity(_windowIntervals);
124  _newStatsAvailable = false;
125  }
126 
127  void update(T newSample) {
128  // update overall stats
129  _overallStats.update(newSample);
130 
131  // update the current interval stats
132  _currentIntervalStats.update(newSample);
133 
134  // if the current interval of samples is now full, record its stats into our past intervals' stats
135  // NOTE: if _intervalLength is 0 (manual mode), currentIntervalComplete() will not be called here.
136  if (_currentIntervalStats.getSamples() == _intervalLength) {
137  currentIntervalComplete();
138  }
139  }
140 
143  void currentIntervalComplete() {
144  // record current interval's stats, then reset them
145  _intervalStats.insert(_currentIntervalStats);
146  _currentIntervalStats.reset();
147 
148  // update the window's stats by combining the intervals' stats
149  typename RingBufferHistory< MinMaxAvg<T> >::Iterator i = _intervalStats.begin();
150  typename RingBufferHistory< MinMaxAvg<T> >::Iterator end = _intervalStats.end();
151  _windowStats.reset();
152  while (i != end) {
153  _windowStats.update(*i);
154  ++i;
155  }
156 
157  _newStatsAvailable = true;
158  }
159 
160  bool getNewStatsAvailableFlag() const { return _newStatsAvailable; }
161  void clearNewStatsAvailableFlag() { _newStatsAvailable = false; }
162 
163  T getMin() const { return _overallStats.getMin(); }
164  T getMax() const { return _overallStats.getMax(); }
165  double getAverage() const { return _overallStats.getAverage(); }
166  int getSamples() const { return _overallStats.getSamples(); }
167  double getSum() const { return _overallStats.getSum(); }
168 
169  T getWindowMin() const { return _windowStats.getMin(); }
170  T getWindowMax() const { return _windowStats.getMax(); }
171  double getWindowAverage() const { return _windowStats.getAverage(); }
172  int getWindowSamples() const { return _windowStats.getSamples(); }
173  double getWindowSum() const { return _windowStats.getSum(); }
174 
175  int getWindowIntervals() const { return _windowIntervals; }
176  T getCurrentIntervalMin() const { return _currentIntervalStats.getMin(); }
177  T getCurrentIntervalMax() const { return _currentIntervalStats.getMax(); }
178  double getCurrentIntervalAverage() const { return _currentIntervalStats.getAverage(); }
179  int getCurrentIntervalSamples() const { return _currentIntervalStats.getSamples(); }
180  double getCurrentIntervalSum() const { return _currentIntervalStats.getSum(); }
181  T getCurrentIntervalLastSample() const { return _currentIntervalStats.getLast(); }
182 
183  const MinMaxAvg<T>& getOverallStats() const{ return _overallStats; }
184  const MinMaxAvg<T>& getWindowStats() const{ return _windowStats; }
185  const MinMaxAvg<T>& getCurrentIntervalStats() const { return _currentIntervalStats; }
186 
187  MinMaxAvg<T> getLastCompleteIntervalStats() const {
188  const MinMaxAvg<T>* stats = _intervalStats.getNewestEntry();
189  return stats == NULL ? MinMaxAvg<T>() : *stats;
190  }
191 
192  bool isWindowFilled() const { return _intervalStats.isFilled(); }
193 
194 private:
195  int _intervalLength;
196  int _windowIntervals;
197 
198  // these are min/max/avg stats for all samples collected.
199  MinMaxAvg<T> _overallStats;
200 
201  // these are the min/max/avg stats for the samples in the moving window
202  MinMaxAvg<T> _windowStats;
203 
204  // these are the min/max/avg stats for the samples in the current interval
205  MinMaxAvg<T> _currentIntervalStats;
206 
207  // these are stored stats for the past intervals in the window
208  RingBufferHistory< MinMaxAvg<T> > _intervalStats;
209 
210  bool _newStatsAvailable;
211 };
212 
213 #endif // hifi_MovingMinMaxAvg_h