You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
2.7KB

  1. use std::collections::VecDeque;
  2. #[derive(Debug, Clone)]
  3. pub struct WeightedPoint {
  4. pub time: u64,
  5. /// value * weight.
  6. ///
  7. /// when purging expired items, do not subtract `wt_val * wt`, as `wt_val`
  8. /// has already been multiplied by `wt`. Instead, simply substract `wt_val`
  9. /// from `w_sum`.
  10. pub wt_val: f64,
  11. pub wt: f64,
  12. }
  13. #[derive(Clone)]
  14. pub struct WeightedAvgWindow {
  15. size: u64,
  16. items: VecDeque<WeightedPoint>,
  17. w_sum: f64,
  18. sum_w: f64,
  19. //w_mean: f64,
  20. }
  21. impl WeightedAvgWindow {
  22. pub fn new(size: u64) -> Self {
  23. Self {
  24. size,
  25. items: Default::default(),
  26. w_sum: 0.0,
  27. sum_w: 0.0,
  28. }
  29. }
  30. /// Removes expired items and updates incremental calculations.
  31. ///
  32. /// Returns `true` if any items were removed.
  33. pub fn purge(&mut self, time: u64) -> bool {
  34. let mut n_remove = 0;
  35. {
  36. let items = &self.items;
  37. let w_sum = &mut self.w_sum;
  38. let sum_w = &mut self.sum_w;
  39. let size = self.size;
  40. for expired in items.iter().take_while(|x| time - x.time > size) {
  41. *w_sum -= expired.wt_val;
  42. *sum_w -= expired.wt;
  43. n_remove += 1;
  44. }
  45. }
  46. for _ in 0..n_remove { self.items.pop_front(); }
  47. // when items is empty, set w_sum, sum_w to 0.0
  48. let zeroer: f64 = ( ! self.items.is_empty()) as u8 as f64;
  49. self.w_sum *= zeroer;
  50. self.sum_w *= zeroer;
  51. n_remove > 0
  52. }
  53. /// Add a new item, updating incremental calculations in the process.
  54. pub fn push(&mut self, time: u64, val: f64, wt: f64) {
  55. let wt_val: f64 = val * wt;
  56. self.w_sum += wt_val;
  57. self.sum_w += wt;
  58. self.items.push_back(WeightedPoint { time, wt_val, wt });
  59. }
  60. /// Calculate the weighted mean from current state of incremental
  61. /// accumulators.
  62. ///
  63. /// Note; this value is not cached.
  64. pub fn wt_mean(&self) -> f64 {
  65. self.w_sum / self.sum_w
  66. }
  67. /// Checks whether items `is_empty` before trying to calculate.
  68. /// Returns None if items is empty.
  69. pub fn checked_wt_mean(&self) -> Option<f64> {
  70. match self.is_empty() {
  71. true => None,
  72. false => Some(self.w_sum / self.sum_w),
  73. }
  74. }
  75. /// Purge, push and get `checked_wt_mean`, all in one convenient step.
  76. pub fn update(&mut self, time: u64, val: f64, wt: f64) -> Option<f64> {
  77. self.purge(time);
  78. self.push(time, val, wt);
  79. self.checked_wt_mean()
  80. }
  81. pub fn len(&self) -> usize { self.items.len() }
  82. pub fn is_empty(&self) -> bool { self.items.is_empty() }
  83. }