4 /*****************************************************************************\
7 * Author : Chris Koeritz *
9 *******************************************************************************
10 * Copyright (c) 1997-$now By Author. This program is free software; you can *
11 * redistribute it and/or modify it under the terms of the GNU General Public *
12 * License as published by the Free Software Foundation; either version 2 of *
13 * the License or (at your option) any later version. This is online at: *
14 * http://www.fsf.org/copyleft/gpl.html *
15 * Please send any updates to: fred@gruntose.com *
16 \*****************************************************************************/
18 #include <basis/array.h>
19 #include <basis/functions.h>
21 namespace mathematics {
23 //! Maintains a list of numbers and provides the average for them.
25 The list entries can be weighted if desired. If the list grows too large,
26 the first chunk of the entries is added as a weighted average and the list's
27 size is reduced appropriately.
30 template <class contents>
34 averager(int entries = 100, bool compacting = true);
35 //!< creates an averager whose list length is restricted to "entries".
36 /*!< if "entries" that is zero, then the maximum length is used. if
37 "compacting" is true, then the entries which need to be removed during
38 a compaction will be added to the list as a weighted average. if it's
39 false, then the unfortunate entries are just eliminated. the first style
40 leads to a more steady state version of the average while the other is
41 more recent history. note that the lowest reasonable value for "entries"
42 is eight due to the compaction algorithm; lower values will work, but the
43 averager will allow the list to grow to eight anyway. */
45 void add(contents value, int count = 1);
46 //!< adds a new "value" to the averager, with an optional "count".
48 contents average() const { return average(0, length() - 1); }
49 //!< reports the overall average of the whole list.
52 //!< returns the total number of samples recorded in the average.
54 int length() const { return _averages.length(); }
55 //!< returns the current length of the averages list.
57 contents average(int start, int end) const;
58 //!< reports the average over the range from "start" to "end" inclusive.
60 struct weighted_entry { contents value; int count; };
61 //!< structure holding a weighted chunk of the average.
62 /*!< an entry in the list of averages has a "value" and a "count" that
63 measures the weight with which that "value" is considered. */
65 weighted_entry get(int index) const { return _averages.get(index); }
66 //!< accesses the entry stored at the "index" specified.
69 //!< chops off the oldest portion of the averager.
70 /*!< if this is a compacting style averager, then the older data is
71 coalesced and added as a weighted entry. */
73 void check_for_compaction();
74 //!< checks whether the averager needs to be compacted yet or not.
75 /*!< the decision is made according to the maximum allowable size in
76 "entries" passed to the constructor. if "entries" is zero, the maximum
77 allowable size is used instead. */
80 bool _do_compaction; //!< do truncated values coalesce to a weighted entry?
81 basis::array<weighted_entry> _averages; //!< current list.
82 int _entries; //!< maximum length of list.
87 //! keeps an average on a stream of integers.
88 class int_averager : public averager<int>
91 int_averager(int entries = 100, bool compacting = true)
92 : averager<int>(entries, compacting) {}
97 // implementations below...
99 const int AVERAGER_SIZE_LIMIT = 180000; // the most items we'll try to handle.
101 template <class contents>
102 averager<contents>::averager(int entries, bool compacting)
103 : _do_compaction(compacting), _averages(), _entries(entries)
105 int unit_size = sizeof(weighted_entry);
106 if (basis::negative(_entries) || !_entries)
107 _entries = int(AVERAGER_SIZE_LIMIT / unit_size);
110 template <class contents>
111 void averager<contents>::compact()
113 if (length() < 8) return; // sorry, you're too short for this wild ride.
114 int end_whacking = _averages.length() / 4;
115 if (_do_compaction) {
116 contents whacked_average = average(0, end_whacking);
117 _averages.zap(1, end_whacking);
118 _averages[0].value = whacked_average;
119 _averages[0].count = end_whacking + 1;
120 } else _averages.zap(0, end_whacking);
123 template <class contents>
124 void averager<contents>::check_for_compaction()
126 // make sure that we don't go over our allotted space.
127 int unit_size = sizeof(weighted_entry);
128 int limit = basis::minimum(AVERAGER_SIZE_LIMIT, _entries * unit_size);
129 if (int(_averages.length() + 2) * unit_size >= limit) compact();
132 template <class contents>
133 void averager<contents>::add(contents value, int count)
135 weighted_entry to_add;
136 to_add.value = value;
137 to_add.count = count;
138 check_for_compaction();
142 template <class contents>
143 contents averager<contents>::average(int start, int end) const
145 bounds_return(start, 0, length() - 1, 0);
146 bounds_return(end, start, length() - 1, 0);
147 bounds_return(end - start + 1, 1, length(), 0);
151 for (int i = start; i <= end; i++) {
152 accum += get(i).value * get(i).count;
153 count += get(i).count;
155 if (!count) count = 1;
156 return accum / count;
159 template <class contents>
160 int averager<contents>::samples() const
163 for (int i = 0; i < length(); i++) to_return += get(i).count;