blob: 7760b427185801364030257575394fcd4dddafea [file] [log] [blame]
Artem Titove4ed6ea2019-01-11 11:02:19 +01001/*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "rtc_base/numerics/samples_stats_counter.h"
12
Artem Titove6f6a0c2019-02-07 12:14:35 +010013#include <math.h>
Artem Titovd2a63782019-03-21 10:37:37 +010014#include <random>
Artem Titove4ed6ea2019-01-11 11:02:19 +010015#include <vector>
16
Steve Anton2acd1632019-03-25 13:48:30 -070017#include "absl/algorithm/container.h"
Artem Titove4ed6ea2019-01-11 11:02:19 +010018#include "test/gtest.h"
19
20namespace webrtc {
21namespace {
Artem Titove6f6a0c2019-02-07 12:14:35 +010022
Artem Titove4ed6ea2019-01-11 11:02:19 +010023SamplesStatsCounter CreateStatsFilledWithIntsFrom1ToN(int n) {
24 std::vector<double> data;
25 for (int i = 1; i <= n; i++) {
26 data.push_back(i);
27 }
Steve Anton2acd1632019-03-25 13:48:30 -070028 absl::c_shuffle(data, std::mt19937(std::random_device()()));
Artem Titove4ed6ea2019-01-11 11:02:19 +010029
30 SamplesStatsCounter stats;
31 for (double v : data) {
32 stats.AddSample(v);
33 }
34 return stats;
35}
Artem Titove6f6a0c2019-02-07 12:14:35 +010036
Yves Gerey890f62b2019-04-10 17:18:48 +020037// Add n samples drawn from uniform distribution in [a;b].
38SamplesStatsCounter CreateStatsFromUniformDistribution(int n,
39 double a,
40 double b) {
41 std::mt19937 gen{std::random_device()()};
42 std::uniform_real_distribution<> dis(a, b);
43
44 SamplesStatsCounter stats;
45 for (int i = 1; i <= n; i++) {
46 stats.AddSample(dis(gen));
47 }
48 return stats;
49}
50
51class SamplesStatsCounterTest : public ::testing::TestWithParam<int> {};
52
53constexpr int SIZE_FOR_MERGE = 10;
54
Artem Titove4ed6ea2019-01-11 11:02:19 +010055} // namespace
56
Artem Titov2c5af4f2019-07-03 10:40:16 +020057TEST(SamplesStatsCounterTest, FullSimpleTest) {
Artem Titove4ed6ea2019-01-11 11:02:19 +010058 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(100);
59
Artem Titove6f6a0c2019-02-07 12:14:35 +010060 EXPECT_TRUE(!stats.IsEmpty());
61 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0);
62 EXPECT_DOUBLE_EQ(stats.GetMax(), 100.0);
63 EXPECT_DOUBLE_EQ(stats.GetAverage(), 50.5);
Artem Titove4ed6ea2019-01-11 11:02:19 +010064 for (int i = 1; i <= 100; i++) {
65 double p = i / 100.0;
Artem Titove6f6a0c2019-02-07 12:14:35 +010066 EXPECT_GE(stats.GetPercentile(p), i);
67 EXPECT_LT(stats.GetPercentile(p), i + 1);
Artem Titove4ed6ea2019-01-11 11:02:19 +010068 }
69}
70
Artem Titov2c5af4f2019-07-03 10:40:16 +020071TEST(SamplesStatsCounterTest, VarianceAndDeviation) {
Artem Titove6f6a0c2019-02-07 12:14:35 +010072 SamplesStatsCounter stats;
73 stats.AddSample(2);
74 stats.AddSample(2);
75 stats.AddSample(-1);
76 stats.AddSample(5);
77
78 EXPECT_DOUBLE_EQ(stats.GetAverage(), 2.0);
79 EXPECT_DOUBLE_EQ(stats.GetVariance(), 4.5);
80 EXPECT_DOUBLE_EQ(stats.GetStandardDeviation(), sqrt(4.5));
81}
82
Artem Titov2c5af4f2019-07-03 10:40:16 +020083TEST(SamplesStatsCounterTest, FractionPercentile) {
Artem Titove4ed6ea2019-01-11 11:02:19 +010084 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(5);
85
Artem Titove6f6a0c2019-02-07 12:14:35 +010086 EXPECT_DOUBLE_EQ(stats.GetPercentile(0.5), 3);
Artem Titove4ed6ea2019-01-11 11:02:19 +010087}
88
Artem Titov2c5af4f2019-07-03 10:40:16 +020089TEST(SamplesStatsCounterTest, TestBorderValues) {
Artem Titove4ed6ea2019-01-11 11:02:19 +010090 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(5);
91
Artem Titove6f6a0c2019-02-07 12:14:35 +010092 EXPECT_GE(stats.GetPercentile(0.01), 1);
93 EXPECT_LT(stats.GetPercentile(0.01), 2);
94 EXPECT_DOUBLE_EQ(stats.GetPercentile(1.0), 5);
Artem Titove4ed6ea2019-01-11 11:02:19 +010095}
96
Artem Titov2c5af4f2019-07-03 10:40:16 +020097TEST(SamplesStatsCounterTest, VarianceFromUniformDistribution) {
Yves Gerey890f62b2019-04-10 17:18:48 +020098 // Check variance converge to 1/12 for [0;1) uniform distribution.
99 // Acts as a sanity check for NumericStabilityForVariance test.
100 SamplesStatsCounter stats = CreateStatsFromUniformDistribution(1e6, 0, 1);
101
102 EXPECT_NEAR(stats.GetVariance(), 1. / 12, 1e-3);
103}
104
Artem Titov2c5af4f2019-07-03 10:40:16 +0200105TEST(SamplesStatsCounterTest, NumericStabilityForVariance) {
Yves Gerey890f62b2019-04-10 17:18:48 +0200106 // Same test as VarianceFromUniformDistribution,
107 // except the range is shifted to [1e9;1e9+1).
108 // Variance should also converge to 1/12.
109 // NB: Although we lose precision for the samples themselves, the fractional
110 // part still enjoys 22 bits of mantissa and errors should even out,
111 // so that couldn't explain a mismatch.
112 SamplesStatsCounter stats =
113 CreateStatsFromUniformDistribution(1e6, 1e9, 1e9 + 1);
114
115 EXPECT_NEAR(stats.GetVariance(), 1. / 12, 1e-3);
116}
117
118TEST_P(SamplesStatsCounterTest, AddSamples) {
119 int data[SIZE_FOR_MERGE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
120 // Split the data in different partitions.
121 // We have 11 distinct tests:
122 // * Empty merged with full sequence.
123 // * 1 sample merged with 9 last.
124 // * 2 samples merged with 8 last.
125 // [...]
126 // * Full merged with empty sequence.
127 // All must lead to the same result.
128 SamplesStatsCounter stats0, stats1;
129 for (int i = 0; i < GetParam(); ++i) {
130 stats0.AddSample(data[i]);
131 }
132 for (int i = GetParam(); i < SIZE_FOR_MERGE; ++i) {
133 stats1.AddSample(data[i]);
134 }
135 stats0.AddSamples(stats1);
136
137 EXPECT_EQ(stats0.GetMin(), 0);
138 EXPECT_EQ(stats0.GetMax(), 9);
139 EXPECT_DOUBLE_EQ(stats0.GetAverage(), 4.5);
140 EXPECT_DOUBLE_EQ(stats0.GetVariance(), 8.25);
141 EXPECT_DOUBLE_EQ(stats0.GetStandardDeviation(), sqrt(8.25));
142 EXPECT_DOUBLE_EQ(stats0.GetPercentile(0.1), 0.9);
143 EXPECT_DOUBLE_EQ(stats0.GetPercentile(0.5), 4.5);
144 EXPECT_DOUBLE_EQ(stats0.GetPercentile(0.9), 8.1);
145}
146
Artem Titov2c5af4f2019-07-03 10:40:16 +0200147TEST(SamplesStatsCounterTest, MultiplyRight) {
148 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(10);
149
150 EXPECT_TRUE(!stats.IsEmpty());
151 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0);
152 EXPECT_DOUBLE_EQ(stats.GetMax(), 10.0);
153 EXPECT_DOUBLE_EQ(stats.GetAverage(), 5.5);
154
155 SamplesStatsCounter multiplied_stats = stats * 10;
156 EXPECT_TRUE(!multiplied_stats.IsEmpty());
157 EXPECT_DOUBLE_EQ(multiplied_stats.GetMin(), 10.0);
158 EXPECT_DOUBLE_EQ(multiplied_stats.GetMax(), 100.0);
159 EXPECT_DOUBLE_EQ(multiplied_stats.GetAverage(), 55.0);
160 EXPECT_EQ(multiplied_stats.GetSamples().size(), stats.GetSamples().size());
161
162 // Check that origin stats were not modified.
163 EXPECT_TRUE(!stats.IsEmpty());
164 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0);
165 EXPECT_DOUBLE_EQ(stats.GetMax(), 10.0);
166 EXPECT_DOUBLE_EQ(stats.GetAverage(), 5.5);
167}
168
169TEST(SamplesStatsCounterTest, MultiplyLeft) {
170 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(10);
171
172 EXPECT_TRUE(!stats.IsEmpty());
173 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0);
174 EXPECT_DOUBLE_EQ(stats.GetMax(), 10.0);
175 EXPECT_DOUBLE_EQ(stats.GetAverage(), 5.5);
176
177 SamplesStatsCounter multiplied_stats = 10 * stats;
178 EXPECT_TRUE(!multiplied_stats.IsEmpty());
179 EXPECT_DOUBLE_EQ(multiplied_stats.GetMin(), 10.0);
180 EXPECT_DOUBLE_EQ(multiplied_stats.GetMax(), 100.0);
181 EXPECT_DOUBLE_EQ(multiplied_stats.GetAverage(), 55.0);
182 EXPECT_EQ(multiplied_stats.GetSamples().size(), stats.GetSamples().size());
183
184 // Check that origin stats were not modified.
185 EXPECT_TRUE(!stats.IsEmpty());
186 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0);
187 EXPECT_DOUBLE_EQ(stats.GetMax(), 10.0);
188 EXPECT_DOUBLE_EQ(stats.GetAverage(), 5.5);
189}
190
191TEST(SamplesStatsCounterTest, Divide) {
192 SamplesStatsCounter stats;
193 for (int i = 1; i <= 10; i++) {
194 stats.AddSample(i * 10);
195 }
196
197 EXPECT_TRUE(!stats.IsEmpty());
198 EXPECT_DOUBLE_EQ(stats.GetMin(), 10.0);
199 EXPECT_DOUBLE_EQ(stats.GetMax(), 100.0);
200 EXPECT_DOUBLE_EQ(stats.GetAverage(), 55.0);
201
202 SamplesStatsCounter divided_stats = stats / 10;
203 EXPECT_TRUE(!divided_stats.IsEmpty());
204 EXPECT_DOUBLE_EQ(divided_stats.GetMin(), 1.0);
205 EXPECT_DOUBLE_EQ(divided_stats.GetMax(), 10.0);
206 EXPECT_DOUBLE_EQ(divided_stats.GetAverage(), 5.5);
207 EXPECT_EQ(divided_stats.GetSamples().size(), stats.GetSamples().size());
208
209 // Check that origin stats were not modified.
210 EXPECT_TRUE(!stats.IsEmpty());
211 EXPECT_DOUBLE_EQ(stats.GetMin(), 10.0);
212 EXPECT_DOUBLE_EQ(stats.GetMax(), 100.0);
213 EXPECT_DOUBLE_EQ(stats.GetAverage(), 55.0);
214}
215
Yves Gerey890f62b2019-04-10 17:18:48 +0200216INSTANTIATE_TEST_SUITE_P(SamplesStatsCounterTests,
217 SamplesStatsCounterTest,
218 ::testing::Range(0, SIZE_FOR_MERGE + 1));
219
Artem Titove4ed6ea2019-01-11 11:02:19 +0100220} // namespace webrtc