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