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