All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FixedBins.h
Go to the documentation of this file.
1 /**
2  * @file icarusalg/Utilities/FixedBins.h
3  * @brief Class with extensible fix-sized binning.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date March 28, 2021
6  */
7 
8 #ifndef ICARUSALG_UTILITIES_FIXEDBINS_H
9 #define ICARUSALG_UTILITIES_FIXEDBINS_H
10 
11 
12 // C/C++ standard libraries
13 #include <algorithm> // std::copy(), std::fill()
14 #include <vector>
15 #include <iterator> // std::next()
16 #include <utility> // std::declval(), std::move()
17 #include <cmath> // std::floor()
18 #include <cstddef> // std::size_t, std::ptrdiff_t
19 #include <cassert>
20 
21 
22 // -----------------------------------------------------------------------------
23 namespace icarus::ns::util {
24 
25  template <typename T, typename C = unsigned int> class FixedBins;
26 
27  template <typename T, typename C>
28  bool empty(FixedBins<T, C> const&) noexcept;
29  template <typename T, typename C>
30  std::size_t size(FixedBins<T, C> const&) noexcept;
31  template <typename T, typename C>
32  auto cbegin(FixedBins<T, C> const&) noexcept;
33  template <typename T, typename C>
34  auto begin(FixedBins<T, C> const&) noexcept;
35  template <typename T, typename C>
36  auto cend(FixedBins<T, C> const&) noexcept;
37  template <typename T, typename C>
38  auto end(FixedBins<T, C> const&) noexcept;
39 
40 } // namespace icarus::ns::util
41 
42 /**
43  * @brief Binned counts of data.
44  * @param T type of data on the binning axis
45  * @param C (default: `unsigned int`) data type for the bin count
46  *
47  * A `FixedBin` object holds binned counts with a binning of a fixed size and
48  * alignment. For example, an object set to have `2`-wide bins aligned to `-1`
49  * will hold counts with bins `-3` to `-1`, `-1` to `1`, `1` to 3` etc.
50  * The lower edge of the bin is included in it, while the upper edge is not.
51  *
52  * The lowest and highest limits of the binning are not fixed.
53  * As data is`add()`-ed to the object, new bins are allocated if needed, and
54  * the storage of counts is contiguous.
55  * For example, in the example above if the first added datum is `+2`, the bin
56  * `1` to `3` is allocated and given a count of `1`. At this point there is
57  * only one bin with storage. If `6` is added next, the storage is extended to
58  * cover also bins `3` to `5` and `5` to `7`, and they will be assigned counts
59  * of `0` and `1` respectively. At this point, there is storage for three bins,
60  * one of them being empty.
61  *
62  * The query interface does report which is the first bin with storage
63  * (supposedly the first non-empty bin) and which is the last one.
64  * Bin content can be asked for any value and any bin.
65  *
66  * Currently the modification interface is very limited: the only way to add
67  * entries to the bins is one by one by value (`add()`), and the only other
68  * supported modifying action is to empty all the content (`clear()`).
69  *
70  * The bin index is of type `ptrdiff_t`.
71  *
72  *
73  * @note It is possible to access the counters by a bin index. That index
74  * should be provided by `FixedBins` itself (`binWith()`, `minBin()`,
75  * etc.), since its specific value is an implementation detail that may
76  * change.
77  *
78  */
79 template <typename T, typename C /* = unsigned int */>
80 class icarus::ns::util::FixedBins {
81 
82  public:
83 
84  using Data_t = T; ///< Type on the bin axis.
85 
86  using Count_t = C; ///< Type on the bin content.
87 
88  /// Type of interval in on the bin axis.
89  using Interval_t = decltype(std::declval<T>() - std::declval<T>());
90 
91  using BinIndex_t = std::ptrdiff_t; ///< Type of bin index.
92 
93 
94  /**
95  * @brief Constructor: initializes the binning.
96  * @param width the bin width
97  * @param offset (default: 0) the border of one of the bins
98  *
99  * This constructor prepares the object to host counts in bins of the
100  * specified `width`.
101  * Optionally, the bins are aligned to the `offset` value instead than to `0`
102  * (or, more precisely, the value of a default-constructed `Data_t`).
103  *
104  * No memory is allocated just yet.
105  */
106  explicit FixedBins(Interval_t width, Data_t offset = Data_t{}) noexcept;
107 
108 
109  // --- BEGIN -- Content modification -----------------------------------------
110  /// @name Content modification
111  /// @{
112 
113  /**
114  * @brief Increases by a unit the count at the bin including `value`.
115  * @param value the value to be accounted for
116  * @return the index of the bin including the `value`.
117  */
118  BinIndex_t add(Data_t value);
119 
120  /**
121  * @brief Resets all counts to `0`.
122  *
123  * All the storage is removed from the object (although depending on the
124  * STL implementation its memory might still be allocated).
125  */
126  void clear() noexcept;
127 
128  /// @}
129  // --- END ---- Content modification -----------------------------------------
130 
131 
132  // --- BEGIN -- Query --------------------------------------------------------
133  /// @name Query interface
134  /// @{
135 
136  /// Returns how many bins currently have storage.
137  std::size_t nBins() const noexcept;
138 
139  /// Returns whether there is no storage at all.
140  bool empty() const noexcept;
141 
142  /// Returns the width of the bins.
143  Interval_t binWidth() const noexcept;
144 
145  /**
146  * @brief Returns the alignment offset of the bins.
147  * @return the alignment offset of the bins
148  *
149  * One bin is guaranteed to have its `lowerEdge()` at the value returned by
150  * `offset()`.
151  */
152  Data_t offset() const noexcept;
153 
154  /**
155  * @return the value of the lower edge of the bin with the specified `index`
156  *
157  * This value always belongs to the bin `index`.
158  */
159  Data_t lowerEdge(BinIndex_t index) const noexcept;
160 
161  /**
162  * @return the value of the upper edge of the bin with the specified `index`
163  *
164  * Note that this value always belongs to the bin `index + 1`.
165  */
166  Data_t upperEdge(BinIndex_t index) const noexcept;
167 
168  /**
169  * @brief Returns the index of the bin including the specified `value`.
170  * @param value the value that the queried bin must contain
171  * @return the index of the bin including the specified value
172  * @see `count()`, `countFor()`
173  */
174  BinIndex_t binWith(Data_t value) const noexcept;
175 
176  /**
177  * @return the span covered by the bins currently with storage
178  *
179  * Equivalent to `max() - min()` and `nBins() * binWidth()`.
180  */
181  Interval_t range() const noexcept;
182 
183  /**
184  * @return the index of the lowest bin with storage
185  *
186  * The return value is undefined if `empty()` is `true` (i.e. if no storage is
187  * allocated yet).
188  */
189  BinIndex_t minBin() const noexcept;
190 
191  /**
192  * @return the index of the highest bin with storage
193  *
194  * This value can be the same as `minBin()` if only one bin is stored.
195  *
196  * The return value is undefined if `empty()` is `true` (i.e. if no storage is
197  * allocated yet).
198  */
199  BinIndex_t maxBin() const noexcept;
200 
201  /**
202  * @return the lower limit of the lowest bin with storage
203  *
204  * The return value is undefined if `empty()` is `true` (i.e. if no storage is
205  * allocated yet).
206  */
207  Data_t min() const noexcept;
208 
209  /**
210  * @return the upper limit of the highest bin with storage
211  *
212  * The return value is undefined if `empty()` is `true` (i.e. if no storage is
213  * allocated yet).
214  */
215  Data_t max() const noexcept;
216 
217  /**
218  * @brief Returns the count of the bin with the specified `index`.
219  * @param index the index of the bin to be queried
220  * @return the count of the bin with the specified `index`
221  * @see `operator[]()`, `countFor()`
222  *
223  * If the specified bin has no storage, the returned count is `0`.
224  */
225  Count_t count(BinIndex_t index) const noexcept;
226 
227  /**
228  * @brief Returns the count of the bin including the specified `value`.
229  * @param value the value that the queried bin must contain
230  * @return the count of the bin including the specified value
231  * @see `operator[]()`, `count()`, `binWith()`
232  *
233  * If the bin with the specified value has no storage, the returned count is
234  * `0`.
235  */
236  Count_t countFor(Data_t value) const noexcept;
237 
238  /**
239  * @brief Returns the count of the bin with the specified `index`.
240  * @param index the index of the bin to be queried
241  * @return the count of the bin with the specified `index`
242  * @see `count()`
243  *
244  * If the specified bin has no storage, the returned count is `0`.
245  */
246  Count_t operator[](BinIndex_t index) const noexcept;
247 
248  /// Returns the number of bins with storage.
249  std::size_t size() const noexcept;
250 
251  /// Returns an iterator pointing to the content of the first bin with storage.
252  auto cbegin() const noexcept;
253 
254  /// Returns an iterator pointing to the content of the first bin with storage.
255  auto begin() const noexcept;
256 
257  /// Returns an iterator pointing to the content of the first bin with storage.
258  auto cend() const noexcept;
259 
260  /// Returns an iterator pointing to the content of the first bin with storage.
261  auto end() const noexcept;
262 
263  /// @}
264  // --- END ---- Query --------------------------------------------------------
265 
266 
267  private:
268 
269  using Storage_t = std::vector<Count_t>; ///< Type of storage for bin counts.
270 
271  /// Starting value of a counter, and ending value of a trilogy.
272  static constexpr Count_t CountZero = 0;
273 
274 
275  Data_t fWidth; ///< Bin width.
276  Data_t fOffset; ///< Bin offset from `0`.
277 
278  Storage_t fCounters; ///< Bin counters.
279  Data_t fMin; ///< The lower edge of the bin with storage index 0.
280  BinIndex_t fMinBin; ///< The index of bin `fCounters[0]`.
281 
282 
283  /// Returns the index in the data storage corresponding to bin `index`.
284  std::ptrdiff_t storageIndex(BinIndex_t index) const noexcept;
285 
286  /// Returns whether the specified stotage index is available.
287  bool hasStorageIndex(std::ptrdiff_t stIndex) const noexcept;
288 
289  /// Returns the number of bins passing from `ref` to `value`.
290  BinIndex_t relativeBinIndex(Data_t value, Data_t ref) const noexcept;
291 
292  /// Initializes the storage to host a single bin including `value`.
293  /// @return the storage index of the new bin (that is: `0`)
294  std::size_t initializeWith(Data_t value);
295 
296  /// Ensures the bin with the specified `index` exists and returns its storage
297  /// index. Requires some storage to exist already.
298  std::size_t allocateBin(BinIndex_t index);
299 
300 }; // icarus::ns::util::FixedBins
301 
302 // deduction guide:
303 namespace icarus::ns::util {
304  template <typename T> FixedBins(T) -> FixedBins<T>;
305 }
306 
307 
308 /* -----------------------------------------------------------------------------
309  * --- template implementation
310  * -----------------------------------------------------------------------------
311  *
312  * Implementation details
313  * -----------------------
314  *
315  * The value of `fMin` is always from the lowest non-empty bin ([20210925] there
316  * is no way to make a bin empty after it has been filled; but future changes
317  * may make this become the lowest bin which has been non-empty).
318  * Data is stored in a STL vector, so the storage index always starts with `0`.
319  * The "bin index" is defined relative to a reference bin, which is the first
320  * bin being added. The purpose of this index is to give the user something that
321  * does not move when values are added (storage indices are shifted whenever a
322  * bin is added before the current minimum).
323  * To track these indices, `fMinBin` is the bin index of the first bin in the
324  * storage.
325  *
326  */
327 template <typename T, typename C /* = unsigned int */>
329  (Interval_t width, Data_t offset /* = Data_t{} */) noexcept
330  : fWidth{ width }, fOffset{ offset }, fMinBin{}
331 {
332  assert(fWidth != Interval_t{0}); // yep, we even accept negative
333 }
334 
335 
336 // -----------------------------------------------------------------------------
337 template <typename T, typename C /* = unsigned int */>
339 
340  std::size_t const stIndex
341  = empty()? initializeWith(value): allocateBin(binWith(value));
342  ++fCounters[stIndex];
343  return stIndex;
344 } // icarus::ns::util::FixedBins<>::add()
345 
346 
347 // -----------------------------------------------------------------------------
348 template <typename T, typename C /* = unsigned int */>
349 void icarus::ns::util::FixedBins<T, C>::clear() noexcept { fCounters.clear(); }
350 
351 
352 // -----------------------------------------------------------------------------
353 template <typename T, typename C /* = unsigned int */>
355  { return fCounters.size(); }
356 
357 
358 // -----------------------------------------------------------------------------
359 template <typename T, typename C /* = unsigned int */>
361  { return fCounters.empty(); }
362 
363 
364 // -----------------------------------------------------------------------------
365 template <typename T, typename C /* = unsigned int */>
367  { return fWidth; }
368 
369 
370 // -----------------------------------------------------------------------------
371 template <typename T, typename C /* = unsigned int */>
373  { return fOffset; }
374 
375 
376 // -----------------------------------------------------------------------------
377 template <typename T, typename C /* = unsigned int */>
379  (BinIndex_t index) const noexcept -> Data_t
380  { return offset() + (minBin() + index) * binWidth(); }
381 
382 
383 // -----------------------------------------------------------------------------
384 template <typename T, typename C /* = unsigned int */>
386  (BinIndex_t index) const noexcept -> Data_t
387  { return lowerEdge(index + 1); }
388 
389 
390 // -----------------------------------------------------------------------------
391 template <typename T, typename C /* = unsigned int */>
393  -> BinIndex_t
394  { return empty()? 0: minBin() + relativeBinIndex(value, min()); }
395 
396 
397 // -----------------------------------------------------------------------------
398 template <typename T, typename C /* = unsigned int */>
400  { return binWidth() * nBins(); }
401 
402 
403 // -----------------------------------------------------------------------------
404 template <typename T, typename C /* = unsigned int */>
406  { return fMinBin; }
407 
408 // -----------------------------------------------------------------------------
409 template <typename T, typename C /* = unsigned int */>
411  { return minBin() + nBins() - 1; }
412 
413 
414 // -----------------------------------------------------------------------------
415 template <typename T, typename C /* = unsigned int */>
417  { return fMin; }
418 
419 
420 // -----------------------------------------------------------------------------
421 template <typename T, typename C /* = unsigned int */>
423  { return min() + range(); }
424 
425 
426 // -----------------------------------------------------------------------------
427 template <typename T, typename C /* = unsigned int */>
429  -> Count_t
430 {
431  auto const stIndex = storageIndex(index);
432  return hasStorageIndex(stIndex)? fCounters[stIndex]: CountZero;
433 } // icarus::ns::util::FixedBins<>::count()
434 
435 
436 // -----------------------------------------------------------------------------
437 template <typename T, typename C /* = unsigned int */>
439  -> Count_t
440  { return count(binWith(value)); }
441 
442 
443 // -----------------------------------------------------------------------------
444 template <typename T, typename C /* = unsigned int */>
446  (BinIndex_t index) const noexcept -> Count_t
447  { return count(index); }
448 
449 
450 // -----------------------------------------------------------------------------
451 template <typename T, typename C /* = unsigned int */>
453  { return nBins(); }
454 
455 
456 // -----------------------------------------------------------------------------
457 template <typename T, typename C /* = unsigned int */>
459  { return fCounters.begin(); }
460 
461 
462 // -----------------------------------------------------------------------------
463 template <typename T, typename C /* = unsigned int */>
465  { return cbegin(); }
466 
467 
468 // -----------------------------------------------------------------------------
469 template <typename T, typename C /* = unsigned int */>
471  { return fCounters.end(); }
472 
473 
474 // -----------------------------------------------------------------------------
475 template <typename T, typename C /* = unsigned int */>
477  { return cend(); }
478 
479 
480 // -----------------------------------------------------------------------------
481 template <typename T, typename C /* = unsigned int */>
483  (BinIndex_t index) const noexcept
484  { return index - fMinBin; }
485 
486 
487 // -----------------------------------------------------------------------------
488 template <typename T, typename C /* = unsigned int */>
490  (std::ptrdiff_t stIndex) const noexcept
491 {
492  return
493  (stIndex >= 0) && (static_cast<std::size_t>(stIndex) < fCounters.size());
494 } // icarus::ns::util::FixedBins<>::hasStorageIndex()
495 
496 
497 // -----------------------------------------------------------------------------
498 template <typename T, typename C /* = unsigned int */>
500  (Data_t value, Data_t ref) const noexcept -> BinIndex_t
501 {
502  using std::floor;
503  return static_cast<BinIndex_t>(floor((value - ref) / binWidth()));
504 } // icarus::ns::util::FixedBins<>::relativeBinIndex()
505 
506 
507 // -----------------------------------------------------------------------------
508 template <typename T, typename C /* = unsigned int */>
510  assert(empty());
511 
512  fMinBin = 0;
513  fMin = offset() + binWidth() * relativeBinIndex(value, offset());
514  fCounters.push_back(CountZero);
515  return static_cast<std::size_t>(0);
516 
517 } // icarus::ns::util::FixedBins<>::initializeWith()
518 
519 
520 // -----------------------------------------------------------------------------
521 template <typename T, typename C /* = unsigned int */>
523  assert(!empty());
524 
525  BinIndex_t stIndex = storageIndex(index);
526  if (stIndex < 0) {
527  // (changes the first index)
528 
529  // extend the data storage on the left, filling with zeroes
530  std::size_t const nExtend = static_cast<std::size_t>(-stIndex);
531  Storage_t data(nExtend + fCounters.size()); // uninitialized
532  auto const itOldFirst = std::next(data.begin(), nExtend);
533  std::fill(data.begin(), itOldFirst, CountZero);
534  std::copy(fCounters.cbegin(), fCounters.cend(), itOldFirst);
535  fCounters = std::move(data);
536 
537  fMinBin = index;
538  fMin -= nExtend * binWidth(); // numerically not the best choice...
539  stIndex = 0;
540  }
541  else if (static_cast<std::size_t>(stIndex) >= fCounters.size()) {
542  // (does not change the first index -- nor `stIndex`)
543  fCounters.resize(static_cast<std::size_t>(stIndex) + 1, CountZero);
544  }
545  assert(hasStorageIndex(stIndex));
546  return static_cast<std::size_t>(stIndex);
547 
548 } // icarus::ns::util::FixedBins<>::allocateBin()
549 
550 
551 // -----------------------------------------------------------------------------
552 // --- free function implementation
553 // -----------------------------------------------------------------------------
554 template <typename T, typename C>
555 bool icarus::ns::util::empty(FixedBins<T, C> const& bins) noexcept
556  { return bins.empty(); }
557 
558 
559 // -----------------------------------------------------------------------------
560 template <typename T, typename C>
561 std::size_t icarus::ns::util::size(FixedBins<T, C> const& bins) noexcept
562  { return bins.size(); }
563 
564 
565 // -----------------------------------------------------------------------------
566 template <typename T, typename C>
567 auto icarus::ns::util::cbegin(FixedBins<T, C> const& bins) noexcept
568  { return bins.cbegin(); }
569 
570 
571 // -----------------------------------------------------------------------------
572 template <typename T, typename C>
573 auto icarus::ns::util::begin(FixedBins<T, C> const& bins) noexcept
574  { return bins.begin(); }
575 
576 
577 // -----------------------------------------------------------------------------
578 template <typename T, typename C>
579 auto icarus::ns::util::cend(FixedBins<T, C> const& bins) noexcept
580  { return bins.cend(); }
581 
582 
583 // -----------------------------------------------------------------------------
584 template <typename T, typename C>
585 auto icarus::ns::util::end(FixedBins<T, C> const& bins) noexcept
586  { return bins.end(); }
587 
588 
589 // -----------------------------------------------------------------------------
590 
591 #endif // ICARUSALG_UTILITIES_FIXEDBINS_H
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
Definition: UtilFunc.cxx:42
BEGIN_PROLOG TPC Trig offset(g4 rise time) ProjectToHeight
Definition: CORSIKAGen.fcl:7
auto cend() const noexcept
Returns an iterator pointing to the content of the first bin with storage.
Definition: FixedBins.h:470
BinIndex_t relativeBinIndex(Data_t value, Data_t ref) const noexcept
Returns the number of bins passing from ref to value.
Definition: FixedBins.h:500
std::size_t size() const noexcept
Returns the number of bins with storage.
Definition: FixedBins.h:452
FixedBins(Interval_t width, Data_t offset=Data_t{}) noexcept
Constructor: initializes the binning.
Definition: FixedBins.h:329
BinIndex_t add(Data_t value)
Increases by a unit the count at the bin including value.
Definition: FixedBins.h:338
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
auto begin() const noexcept
Returns an iterator pointing to the content of the first bin with storage.
Definition: FixedBins.h:464
auto cbegin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:567
Data_t max() const noexcept
Definition: FixedBins.h:422
double Data_t
Type on the bin axis.
Definition: FixedBins.h:84
decltype(std::declval< double >()-std::declval< double >()) Interval_t
Type of interval in on the bin axis.
Definition: FixedBins.h:89
Count_t count(BinIndex_t index) const noexcept
Returns the count of the bin with the specified index.
Definition: FixedBins.h:428
Data_t min() const noexcept
Definition: FixedBins.h:416
auto cend(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:579
BinIndex_t binWith(Data_t value) const noexcept
Returns the index of the bin including the specified value.
Definition: FixedBins.h:392
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
bool empty() const noexcept
Returns whether there is no storage at all.
Definition: FixedBins.h:360
Data_t upperEdge(BinIndex_t index) const noexcept
Definition: FixedBins.h:386
Count_t countFor(Data_t value) const noexcept
Returns the count of the bin including the specified value.
Definition: FixedBins.h:438
Binned counts of data.
Definition: FixedBins.h:25
BinIndex_t maxBin() const noexcept
Definition: FixedBins.h:410
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
std::vector< Count_t > Storage_t
Type of storage for bin counts.
Definition: FixedBins.h:269
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
BinIndex_t minBin() const noexcept
Definition: FixedBins.h:405
void clear() noexcept
Resets all counts to 0.
Definition: FixedBins.h:349
Data_t offset() const noexcept
Returns the alignment offset of the bins.
Definition: FixedBins.h:372
std::ptrdiff_t BinIndex_t
Type of bin index.
Definition: FixedBins.h:91
std::size_t nBins() const noexcept
Returns how many bins currently have storage.
Definition: FixedBins.h:354
bool hasStorageIndex(std::ptrdiff_t stIndex) const noexcept
Returns whether the specified stotage index is available.
Definition: FixedBins.h:490
Interval_t range() const noexcept
Definition: FixedBins.h:399
auto begin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:573
std::size_t allocateBin(BinIndex_t index)
Definition: FixedBins.h:522
Data_t lowerEdge(BinIndex_t index) const noexcept
Definition: FixedBins.h:379
C Count_t
Type on the bin content.
Definition: FixedBins.h:86
T copy(T const &v)
std::size_t initializeWith(Data_t value)
Definition: FixedBins.h:509
temporary value
std::size_t count(Cont const &cont)
std::ptrdiff_t storageIndex(BinIndex_t index) const noexcept
Returns the index in the data storage corresponding to bin index.
Definition: FixedBins.h:483
FixedBins(T) -> FixedBins< T >
bool empty(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:555
auto cbegin() const noexcept
Returns an iterator pointing to the content of the first bin with storage.
Definition: FixedBins.h:458
auto end() const noexcept
Returns an iterator pointing to the content of the first bin with storage.
Definition: FixedBins.h:476
Interval_t binWidth() const noexcept
Returns the width of the bins.
Definition: FixedBins.h:366