herald  2.0.0
sampling.h
1 // Copyright 2021 Herald Project Contributors
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 
5 #ifndef HERALD_SAMPLING_H
6 #define HERALD_SAMPLING_H
7 
8 #include "../datatype/date.h"
9 
10 #include <array>
11 #include <cstdint>
12 #include <type_traits>
13 
14 namespace herald {
15 namespace analysis {
17 namespace sampling {
18 
19 using namespace herald::datatype;
20 
24 using SampledID = std::size_t;
25 
27 template <typename ValT>
28 struct Sample {
29  using value_type = ValT;
30 
31  Date taken; // Date first for alignment reasons
32  ValT value;
33 
34  Sample() : taken(), value() {} // default ctor (required for array)
35  Sample(Date sampled, ValT v) : taken(Date{sampled.secondsSinceUnixEpoch()}), value(v) {}
36  Sample(const Sample& other) : taken(Date{other.taken.secondsSinceUnixEpoch()}), value(other.value) {} // copy ctor
37  Sample(Sample&& other) : taken(std::move(other.taken)), value(std::move(other.value)) {} // move ctor
38  template <typename... Args>
39  Sample(int secondsSinceEpoch,Args... args) : taken(Date(secondsSinceEpoch)), value(ValT(args...)) {} // initialiser list constructor
40  ~Sample() = default;
41 
42  Sample& operator=(Sample&& other) {
43  taken = std::move(other.taken);
44  value = std::move(other.value);
45  return *this;
46  }
47 
48  Sample& operator=(const Sample& other) {
49  taken = Date{other.taken.secondsSinceUnixEpoch()};
50  value = other.value;
51  return *this;
52  }
53 
54  // Note ValT MUST have comparison operators OR conversion to double defined
55 
56  // template <typename T>
57  // bool operator>(const T& other) const {
58  // return value > other;
59  // }
60 
61  // template <typename T>
62  // bool operator>=(const T& other) const {
63  // return value >= other;
64  // }
65 
66  // template <typename T>
67  // bool operator<(const T& other) const {
68  // return value < other;
69  // }
70 
71  // template <typename T>
72  // bool operator<=(const T& other) const {
73  // return value <= other;
74  // }
75 
76  // template <typename T>
77  // bool operator==(const T& other) const {
78  // return value == other;
79  // }
80 
81  // template <typename T>
82  // bool operator!=(const T& other) const {
83  // return value != other;
84  // }
85 
86  operator double() const noexcept {
87  return (double)value;
88  }
89 };
90 
92 template <typename SampleListT,
93  typename ValT = typename SampleListT::value_type>
94 struct SampleIterator;
95 
98 template <typename SampleT, // This is Sample<SampleValueT>
99  std::size_t MaxSize,
100  typename SampleValueT = typename std::remove_cv<typename SampleT::value_type>::type
101  >
102 struct SampleList {
103  using value_type = SampleT; // MUST be before the next line!
104  using difference_type = std::size_t;
106  using size_type = std::size_t;
107 
108  static constexpr std::size_t max_size = MaxSize;
109 
110  SampleList() : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) {}
111  SampleList(const SampleList&) = delete; // no shallow copies allowed
112  SampleList(SampleList&& other) noexcept : data(std::move(other.data)), oldestPosition(other.oldestPosition), newestPosition(other.newestPosition) {} // move ctor
113 
114  SampleList& operator=(SampleList&& other) noexcept {
115  std::swap(data,other.data);
116  oldestPosition = other.oldestPosition;
117  newestPosition = other.newestPosition;
118  return *this;
119  }
120 
121  // Creates a list from static initialiser list elements, using deduction guide
122  template <typename... MultiSampleT>
123  SampleList(MultiSampleT... initialiserElements) : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) {
124  appendData(initialiserElements...);
125  }
126  // This one requires specified final type, but deduces constructor to use
127  // SampleList(SampleT... initialiserElements) : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) {
128  // appendData(initialiserElements...);
129  // }
130  ~SampleList() = default;
131 
132  void push(Sample<SampleValueT> sample) {
133  incrementNewest();
134  data[newestPosition] = sample;
135  }
136 
137  void push(Date taken, SampleValueT val) {
138  incrementNewest();
139  data[newestPosition] = SampleT{taken,val};
140  }
141 
142  std::size_t size() const {
143  if (newestPosition == SIZE_MAX) return 0;
144  if (newestPosition >= oldestPosition) {
145  // not overlapping the end
146  return newestPosition - oldestPosition + 1;
147  }
148  // we've overlapped
149  return (1 + newestPosition) + (data.size() - oldestPosition);
150  }
151 
152  const SampleT& operator[](std::size_t idx) const {
153  if (newestPosition >= oldestPosition) {
154  return data[idx + oldestPosition];
155  }
156  if (idx + oldestPosition >= data.size()) {
157  // TODO handle the situation where this pos > newestPosition (i.e. gap in the middle)
158  return data[idx + oldestPosition - data.size()];
159  }
160  return data[idx + oldestPosition];
161  }
162 
163  void clearBeforeDate(const Date& before) {
164  if (SIZE_MAX == oldestPosition) return;
165  while (oldestPosition != newestPosition) {
166  if (data[oldestPosition].taken < before) {
167  ++oldestPosition;
168  if (data.size() == oldestPosition) {
169  // overflowed
170  oldestPosition = 0;
171  }
172  } else {
173  return;
174  }
175  }
176  // now we're on the last element
177  if (data[oldestPosition].taken < before) {
178  // remove last element
179  oldestPosition = SIZE_MAX;
180  newestPosition = SIZE_MAX;
181  }
182  }
183 
184  void clear() {
185  oldestPosition = SIZE_MAX;
186  newestPosition = SIZE_MAX;
187  }
188 
189  // SampleT latest() {
190  // return data[newestPosition];
191  // }
192  Date latest() {
193  return data[newestPosition].taken;
194  }
195 
198  }
199 
201  if (size() == 0) return SampleIterator<SampleList<SampleT,MaxSize>>(*this);
202  return SampleIterator<SampleList<SampleT,MaxSize>>(*this,size()); // calls this object's size() function, not the array!
203  }
204 
205 private:
206  std::array<SampleT,MaxSize> data;
207  std::size_t oldestPosition;
208  std::size_t newestPosition;
209 
210  void incrementNewest() {
211  if (SIZE_MAX == newestPosition) {
212  newestPosition = 0;
213  oldestPosition = 0;
214  } else {
215  if (newestPosition == (oldestPosition - 1)) {
216  ++oldestPosition;
217  if (oldestPosition == data.size()) {
218  oldestPosition = 0;
219  }
220  }
221  ++newestPosition;
222  }
223  if (newestPosition == data.size()) {
224  // just gone past the end of the container
225  newestPosition = 0;
226  if (0 == oldestPosition) {
227  ++oldestPosition; // erases oldest if not already removed
228  }
229  }
230  }
231 
232  template <typename LastT>
233  void appendData(LastT last) {
234  push(last);
235  }
236 
237  template <typename FirstT, typename SecondT, typename... Inits>
238  void appendData(FirstT first, SecondT second, Inits... initialiserElements) {
239  push(first);
240  appendData(second, initialiserElements...);
241  }
242 };
243 // Deduction guides
244 template <typename... ValT, typename CommonValT = typename std::common_type_t<ValT...>, typename CommonValTValue = typename CommonValT::value_type>
245 SampleList(ValT... valueList) -> SampleList<CommonValT,sizeof...(valueList),CommonValTValue>;
246 // template<typename SampleValueT>
247 // SampleList(Sample<SampleValueT>... valueList) -> SampleList<Sample<SampleValueT>,sizeof...(valueList),SampleValueT>; // TODO figure out guide when we know it's a Sample<ValT>
248 
249 template <typename SampleListT,
250  typename ValT> // from fwd decl => = typename SampleListT::value_type
252  using difference_type = std::size_t;
253  using value_type = ValT;
254  using iterator_category = std::forward_iterator_tag;
255  using pointer = value_type*;
256  using reference = value_type&;
257 
258  SampleIterator(SampleListT& sl) : list(sl), pos(0) {}
259  SampleIterator(SampleListT& sl, std::size_t from) : list(sl), pos(from) {} // used to get list.end() (size() + 1)
260  SampleIterator(const SampleIterator<SampleListT>& other) : list(other.list), pos(other.pos) {} // copy ctor
261  SampleIterator(SampleIterator<SampleListT>&& other) : list(other.list), pos(other.pos) {} // move ctor (cheaper to copy)
262  ~SampleIterator() = default;
263 
264  // always returns const for safety
265  const ValT& operator*() {
266  return list[pos];
267  }
268 
271  pos += by;
272  if (pos > list.size()) {
273  pos = list.size(); // i.e. list.end()
274  }
275  return *this;
276  }
277 
280  if (by > pos) {
281  pos = 0; // prevents underflow and a very large value of pos (as it's a std::size_t)
282  } else {
283  pos -= by;
284  }
285  return *this;
286  }
287 
288  // to allow std::distance to work
289  difference_type operator-(const SampleIterator<SampleListT>& other) {
290  return pos - other.pos;
291  }
292 
295  ++pos; // if it's one after the end of the list, then that's the same as list.end()
296  return *this; // reference to instance
297  }
298 
299  // postfix operator
300  SampleIterator<SampleListT> operator++(int) {
301  SampleIterator<SampleListT> cp = *this; // copy of instance
302  ++(*this);
303  return cp;
304  }
305 
306  bool operator==(const SampleIterator<SampleListT>& otherIter) const {
307  return pos == otherIter.pos;
308  }
309 
310  bool operator!=(const SampleIterator<SampleListT>& otherIter) const {
311  return pos != otherIter.pos;
312  }
313 
314 private:
315  SampleListT& list;
316  std::size_t pos;
317 };
318 
320 template<typename T>
321 typename SampleIterator<T>::difference_type distance(SampleIterator<T> first, SampleIterator<T> last) {
322  return last - first;
323 }
324 
325 } // end sampling namespace
326 }
327 }
328 
329 #endif
Definition: date.h:18
SampleIterator< T >::difference_type distance(SampleIterator< T > first, SampleIterator< T > last)
for std::distance
Definition: sampling.h:321
std::size_t SampledID
Definition: sampling.h:24
Contains all low-level Herald datatype implementations.
Definition: base64_string.h:14
Acts as a non-global memory arena for arbitrary classes.
Definition: aggregates.h:15
The Sample taken from an object with ID of type SampledID.
Definition: sampling.h:28
FWD DECLARATION.
Definition: sampling.h:251
SampleIterator< SampleListT > & operator-(std::size_t by)
Implement operator+(int amt) to move this iterator forward.
Definition: sampling.h:279
SampleIterator< SampleListT > & operator+(std::size_t by)
Implement operator+(int amt) to move this iterator forward.
Definition: sampling.h:270
SampleIterator< SampleListT > & operator++()
prefix operator
Definition: sampling.h:294
Definition: sampling.h:102