1 #include "metrics/collection_function.hpp"
2 
3 #include <cmath>
4 
5 namespace metrics
6 {
7 
8 class FunctionMinimum : public CollectionFunction
9 {
10   public:
calculate(const std::vector<ReadingItem> & readings,Milliseconds) const11     double calculate(const std::vector<ReadingItem>& readings,
12                      Milliseconds) const override
13     {
14         return std::min_element(readings.begin(), readings.end(),
15                                 [](const auto& left, const auto& right) {
16             return std::make_tuple(!std::isfinite(left.second), left.second) <
17                    std::make_tuple(!std::isfinite(right.second), right.second);
18         })->second;
19     }
20 
calculateForStartupInterval(std::vector<ReadingItem> & readings,Milliseconds timestamp) const21     double calculateForStartupInterval(std::vector<ReadingItem>& readings,
22                                        Milliseconds timestamp) const override
23     {
24         readings.assign(
25             {ReadingItem(timestamp, calculate(readings, timestamp))});
26         return readings.back().second;
27     }
28 };
29 
30 class FunctionMaximum : public CollectionFunction
31 {
32   public:
calculate(const std::vector<ReadingItem> & readings,Milliseconds) const33     double calculate(const std::vector<ReadingItem>& readings,
34                      Milliseconds) const override
35     {
36         return std::max_element(readings.begin(), readings.end(),
37                                 [](const auto& left, const auto& right) {
38             return std::make_tuple(std::isfinite(left.second), left.second) <
39                    std::make_tuple(std::isfinite(right.second), right.second);
40         })->second;
41     }
42 
calculateForStartupInterval(std::vector<ReadingItem> & readings,Milliseconds timestamp) const43     double calculateForStartupInterval(std::vector<ReadingItem>& readings,
44                                        Milliseconds timestamp) const override
45     {
46         readings.assign(
47             {ReadingItem(timestamp, calculate(readings, timestamp))});
48         return readings.back().second;
49     }
50 };
51 
52 class FunctionAverage : public CollectionFunction
53 {
54   public:
calculate(const std::vector<ReadingItem> & readings,Milliseconds timestamp) const55     double calculate(const std::vector<ReadingItem>& readings,
56                      Milliseconds timestamp) const override
57     {
58         auto valueSum = 0.0;
59         auto timeSum = Milliseconds{0};
60         for (auto it = readings.begin(); it != std::prev(readings.end()); ++it)
61         {
62             if (std::isfinite(it->second))
63             {
64                 const auto kt = std::next(it);
65                 const auto duration = kt->first - it->first;
66                 valueSum += it->second * duration.count();
67                 timeSum += duration;
68             }
69         }
70 
71         const auto duration = timestamp - readings.back().first;
72         valueSum += readings.back().second * duration.count();
73         timeSum += duration;
74 
75         return valueSum / std::max(timeSum.count(), uint64_t{1u});
76     }
77 
calculateForStartupInterval(std::vector<ReadingItem> & readings,Milliseconds timestamp) const78     double calculateForStartupInterval(std::vector<ReadingItem>& readings,
79                                        Milliseconds timestamp) const override
80     {
81         auto result = calculate(readings, timestamp);
82         if (std::isfinite(result))
83         {
84             readings.assign({ReadingItem(readings.front().first, result),
85                              ReadingItem(timestamp, readings.back().second)});
86         }
87         return result;
88     }
89 };
90 
91 class FunctionSummation : public CollectionFunction
92 {
93     using Multiplier = std::chrono::duration<double>;
94 
95   public:
calculate(const std::vector<ReadingItem> & readings,const Milliseconds timestamp) const96     double calculate(const std::vector<ReadingItem>& readings,
97                      const Milliseconds timestamp) const override
98     {
99         auto valueSum = 0.0;
100         for (auto it = readings.begin(); it != std::prev(readings.end()); ++it)
101         {
102             if (std::isfinite(it->second))
103             {
104                 const auto kt = std::next(it);
105                 const auto multiplier =
106                     calculateMultiplier(kt->first - it->first);
107                 valueSum += it->second * multiplier.count();
108             }
109         }
110 
111         const auto multiplier =
112             calculateMultiplier(timestamp - readings.back().first);
113         valueSum += readings.back().second * multiplier.count();
114 
115         return valueSum;
116     }
117 
118     double
calculateForStartupInterval(std::vector<ReadingItem> & readings,const Milliseconds timestamp) const119         calculateForStartupInterval(std::vector<ReadingItem>& readings,
120                                     const Milliseconds timestamp) const override
121     {
122         const auto result = calculate(readings, timestamp);
123         if (readings.size() > 2 && std::isfinite(result))
124         {
125             const auto multiplier =
126                 calculateMultiplier(timestamp - readings.front().first).count();
127             if (multiplier > 0.)
128             {
129                 const auto prevValue = result / multiplier;
130                 readings.assign(
131                     {ReadingItem(readings.front().first, prevValue),
132                      ReadingItem(timestamp, readings.back().second)});
133             }
134         }
135         return result;
136     }
137 
138   private:
calculateMultiplier(Milliseconds duration)139     static constexpr Multiplier calculateMultiplier(Milliseconds duration)
140     {
141         constexpr auto m = Multiplier{Seconds{1}};
142         return Multiplier{duration / m};
143     }
144 };
145 
146 std::shared_ptr<CollectionFunction>
makeCollectionFunction(OperationType operationType)147     makeCollectionFunction(OperationType operationType)
148 {
149     using namespace std::string_literals;
150 
151     switch (operationType)
152     {
153         case OperationType::min:
154             return std::make_shared<FunctionMinimum>();
155         case OperationType::max:
156             return std::make_shared<FunctionMaximum>();
157         case OperationType::avg:
158             return std::make_shared<FunctionAverage>();
159         case OperationType::sum:
160             return std::make_shared<FunctionSummation>();
161         default:
162             throw std::runtime_error("op: "s +
163                                      utils::enumToString(operationType) +
164                                      " is not supported"s);
165     }
166 }
167 
168 } // namespace metrics
169