1 // Copyright (C) 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 
17 #include <ditto/logger.h>
18 #include <ditto/sampler.h>
19 
20 #include <cmath>
21 #include <ctime>
22 
23 #include <algorithm>
24 #include <memory>
25 #include <numeric>
26 #include <string>
27 #include <vector>
28 
29 namespace dittosuite {
30 
31 template <class T>
StatisticsGetMin(const std::vector<T> & samples)32 T StatisticsGetMin(const std::vector<T>& samples) {
33   if (samples.empty()) LOGF("Cannot compute the min on an empty vector");
34 
35   return *std::min_element(samples.begin(), samples.end());
36 }
37 
38 template <class T>
StatisticsGetMax(const std::vector<T> & samples)39 T StatisticsGetMax(const std::vector<T>& samples) {
40   if (samples.empty()) LOGF("Cannot compute the max on an empty vector");
41 
42   return *std::max_element(samples.begin(), samples.end());
43 }
44 
45 template <class T>
StatisticsGetMean(const std::vector<T> & samples)46 T StatisticsGetMean(const std::vector<T>& samples) {
47   if (samples.empty()) LOGF("Cannot compute the mean on an empty vector");
48 
49   T result = std::accumulate(samples.begin(), samples.end(), T{});
50   return result / samples.size();
51 }
52 
53 template <class T>
StatisticsGetMedian(const std::vector<T> & samples)54 T StatisticsGetMedian(const std::vector<T>& samples) {
55   if (samples.empty()) LOGF("Cannot compute the median on an empty vector");
56 
57   auto my_vector_copy = samples;
58   auto n = samples.size();
59 
60   if (n % 2) {
61     // odd number of elements, the median is the element in the middle
62     std::nth_element(my_vector_copy.begin(), my_vector_copy.begin() + n / 2, my_vector_copy.end());
63     return my_vector_copy[n / 2];
64   } else {
65     // even number of elements, the median is the average between the two middle elements
66     std::nth_element(my_vector_copy.begin(), my_vector_copy.begin() + n / 2, my_vector_copy.end());
67     std::nth_element(my_vector_copy.begin(), my_vector_copy.begin() + (n - 1) / 2,
68                      my_vector_copy.end());
69     T result = my_vector_copy[n / 2] + my_vector_copy[(n - 1) / 2];
70     return result / 2;
71   }
72 }
73 
74 // The standard deviation sd of a population of N samples, where x_i is the
75 // i-th sample and x is the average among all the samples is computed as:
76 //
77 // sd = sqrt( sum( (x_i - x)^2 ) / N )
78 template <class T>
StatisticsGetSd(const std::vector<T> & samples)79 double StatisticsGetSd(const std::vector<T>& samples) {
80   if (samples.empty()) LOGF("Cannot compute the sd on an empty vector");
81 
82   T mean = StatisticsGetMean(samples);
83   double variance;
84 
85   variance = 0.0;
86   for (const auto& s : samples) {
87     double deviation = s - mean;
88     double deviation_square = std::pow(deviation, 2);
89     variance += deviation_square;  // TODO(lucialup): add overflow error handling
90   }
91   variance /= samples.size();
92 
93   return std::sqrt(variance);
94 }
95 
96 }  // namespace dittosuite