OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (c) 2015 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 <math.h> | |
12 | |
13 #include <limits> | |
14 #include <vector> | |
15 | |
16 #include "testing/gtest/include/gtest/gtest.h" | |
17 #include "webrtc/base/checks.h" | |
the sun
2015/11/25 10:12:37
Not needed?
terelius
2015/11/27 10:35:23
Done.
| |
18 #include "webrtc/base/random.h" | |
19 | |
20 namespace webrtc { | |
21 | |
22 namespace { | |
23 template <typename T> | |
24 T fdiv_remainder(T x, T n) { | |
25 assert(n > 0); | |
the sun
2015/11/25 10:12:37
ASSERT_GT()
terelius
2015/11/27 10:35:24
ASSERT requires a void function. In this case I th
the sun
2015/11/27 11:02:10
Don't use stdlib assert(). gtest's ASSERT_nn or EX
pbos-webrtc
2015/11/27 11:06:28
Feel free to use RTC_(D)CHECK_GT if all else fails
terelius
2015/11/27 11:46:07
ASSERT does not work since the function does not r
terelius
2015/11/27 11:46:07
Yeah, that would work. Thanks.
| |
26 T remainder = x % n; | |
27 if (remainder < 0) | |
28 remainder += n; | |
29 return remainder; | |
30 } | |
31 } // namespace | |
32 | |
33 template <typename T> | |
the sun
2015/11/25 10:12:37
Can you describe the test method, or provide a lin
terelius
2015/11/27 10:35:24
Done. This is not some sophisticated test for rand
| |
34 void UniformBucketTest(T bucket_count, int samples, Random* prng) { | |
35 std::vector<int> buckets(bucket_count, 0); | |
36 | |
37 uint64_t total_values = 1ull << (std::numeric_limits<T>::digits + | |
38 std::numeric_limits<T>::is_signed); | |
39 T upper_limit = | |
40 std::numeric_limits<T>::max() - | |
41 static_cast<T>(total_values % static_cast<uint64_t>(bucket_count)); | |
42 assert(upper_limit > std::numeric_limits<T>::max() / 2); | |
the sun
2015/11/25 10:12:38
ASSERT_GT()
...and other assert()s below.
terelius
2015/11/27 10:35:24
Done.
| |
43 | |
44 for (int i = 0; i < samples; i++) { | |
45 T sample; | |
46 do { | |
47 // We exclude a few numbers from the range so that it is divisible by | |
48 // the number of buckets. If we are unlucky and hit one of the excluded | |
49 // numbers we just resample. Note that if the number buckets is a power | |
50 // of 2, then we don't have to exclude anything. | |
51 sample = prng->Rand<T>(); | |
52 } while (sample > upper_limit); | |
53 buckets[fdiv_remainder(sample, bucket_count)]++; | |
54 } | |
55 | |
56 for (T i = 0; i < bucket_count; i++) { | |
57 // Expect the result to be within 3 standard deviations of the mean. | |
58 EXPECT_NEAR(buckets[i], samples / bucket_count, | |
59 3 * sqrt(samples / bucket_count)); | |
60 } | |
61 } | |
62 | |
63 TEST(RandomNumberGeneratorTest, BucketTestSignedChar) { | |
64 Random prng(7297352569824ull); | |
65 UniformBucketTest<signed char>(64, 640000, &prng); | |
the sun
2015/11/25 10:12:37
Are these number empiric or from some calculation?
terelius
2015/11/27 10:35:24
Neither. It is just a call that says "Generate 640
| |
66 UniformBucketTest<signed char>(11, 440000, &prng); | |
67 UniformBucketTest<signed char>(3, 270000, &prng); | |
68 } | |
69 | |
70 TEST(RandomNumberGeneratorTest, BucketTestUnsignedChar) { | |
71 Random prng(7297352569824ull); | |
72 UniformBucketTest<unsigned char>(64, 640000, &prng); | |
73 UniformBucketTest<unsigned char>(11, 440000, &prng); | |
74 UniformBucketTest<unsigned char>(3, 270000, &prng); | |
75 } | |
76 | |
77 TEST(RandomNumberGeneratorTest, BucketTestSignedShort) { | |
78 Random prng(7297352569824ull); | |
79 UniformBucketTest<int16_t>(64, 640000, &prng); | |
80 UniformBucketTest<int16_t>(11, 440000, &prng); | |
81 UniformBucketTest<int16_t>(3, 270000, &prng); | |
82 } | |
83 | |
84 TEST(RandomNumberGeneratorTest, BucketTestUnsignedShort) { | |
85 Random prng(7297352569824ull); | |
86 UniformBucketTest<uint16_t>(64, 640000, &prng); | |
87 UniformBucketTest<uint16_t>(11, 440000, &prng); | |
88 UniformBucketTest<uint16_t>(3, 270000, &prng); | |
89 } | |
90 | |
91 TEST(RandomNumberGeneratorTest, BucketTestSignedInt) { | |
92 Random prng(7297352569824ull); | |
93 UniformBucketTest<signed int>(64, 640000, &prng); | |
94 UniformBucketTest<signed int>(11, 440000, &prng); | |
95 UniformBucketTest<signed int>(3, 270000, &prng); | |
96 } | |
97 | |
98 TEST(RandomNumberGeneratorTest, BucketTestUnsignedInt) { | |
99 Random prng(7297352569824ull); | |
100 UniformBucketTest<unsigned int>(64, 640000, &prng); | |
101 UniformBucketTest<unsigned int>(11, 440000, &prng); | |
102 UniformBucketTest<unsigned int>(3, 270000, &prng); | |
103 } | |
104 | |
105 // The range of the random numbers is divided into bucket count intervals | |
106 // of consecutive numbers. Check that approximately equally many numbers | |
107 // from each inteval are generated. | |
108 void BucketTestSignedInterval(unsigned int bucket_count, | |
109 unsigned int samples, | |
110 int32_t low, | |
111 int32_t high, | |
112 int sigma_level, | |
113 Random* prng) { | |
114 std::vector<unsigned int> buckets(bucket_count, 0); | |
115 | |
116 assert(high >= low); | |
117 assert(bucket_count >= 2); | |
118 uint32_t interval = static_cast<uint32_t>(high - low + 1); | |
119 uint32_t numbers_per_bucket; | |
120 if (interval == 0) { | |
121 // The computation high - low + 1 should be 2^32 but overflowed | |
122 // Hence, bucket_count must be a power of 2 | |
123 assert((bucket_count & (bucket_count - 1)) == 0); | |
124 numbers_per_bucket = (0x80000000u / bucket_count) * 2; | |
125 } else { | |
126 assert(interval % bucket_count == 0); | |
127 numbers_per_bucket = interval / bucket_count; | |
128 } | |
129 | |
130 for (unsigned int i = 0; i < samples; i++) { | |
131 int32_t sample = prng->Rand(low, high); | |
132 EXPECT_LE(low, sample); | |
133 EXPECT_GE(high, sample); | |
134 buckets[static_cast<uint32_t>(sample - low) / numbers_per_bucket]++; | |
135 } | |
136 | |
137 for (unsigned int i = 0; i < bucket_count; i++) { | |
138 // Expect the result to be within 3 standard deviations of the mean, | |
139 // or more generally, within sigma_level standard deviations of the mean. | |
140 double mean = static_cast<double>(samples) / bucket_count; | |
141 EXPECT_NEAR(buckets[i], mean, sigma_level * sqrt(mean)); | |
142 } | |
143 } | |
144 | |
145 // The range of the random numbers is divided into bucket count intervals | |
146 // of consecutive numbers. Check that approximately equally many numbers | |
147 // from each inteval are generated. | |
148 void BucketTestUnsignedInterval(unsigned int bucket_count, | |
149 unsigned int samples, | |
150 uint32_t low, | |
151 uint32_t high, | |
152 int sigma_level, | |
153 Random* prng) { | |
154 std::vector<unsigned int> buckets(bucket_count, 0); | |
155 | |
156 assert(high >= low); | |
157 assert(bucket_count >= 2); | |
158 uint32_t interval = static_cast<uint32_t>(high - low + 1); | |
159 uint32_t numbers_per_bucket; | |
160 if (interval == 0) { | |
161 // The computation high - low + 1 should be 2^32 but overflowed | |
162 // Hence, bucket_count must be a power of 2 | |
163 assert((bucket_count & (bucket_count - 1)) == 0); | |
164 numbers_per_bucket = (0x80000000u / bucket_count) * 2; | |
165 } else { | |
166 assert(interval % bucket_count == 0); | |
167 numbers_per_bucket = interval / bucket_count; | |
168 } | |
169 | |
170 for (unsigned int i = 0; i < samples; i++) { | |
171 uint32_t sample = prng->Rand(low, high); | |
172 EXPECT_LE(low, sample); | |
173 EXPECT_GE(high, sample); | |
174 buckets[static_cast<uint32_t>(sample - low) / numbers_per_bucket]++; | |
175 } | |
176 | |
177 for (unsigned int i = 0; i < bucket_count; i++) { | |
178 // Expect the result to be within 3 standard deviations of the mean, | |
179 // or more generally, within sigma_level standard deviations of the mean. | |
180 double mean = static_cast<double>(samples) / bucket_count; | |
181 EXPECT_NEAR(buckets[i], mean, sigma_level * sqrt(mean)); | |
182 } | |
183 } | |
184 | |
185 TEST(RandomNumberGeneratorTest, UniformUnsignedInterval) { | |
186 Random prng(299792458ull); | |
187 BucketTestUnsignedInterval(2, 100000, 0, 1, 3, &prng); | |
188 BucketTestUnsignedInterval(7, 100000, 1, 14, 3, &prng); | |
189 BucketTestUnsignedInterval(11, 100000, 1000, 1010, 3, &prng); | |
190 BucketTestUnsignedInterval(100, 100000, 0, 99, 3, &prng); | |
191 BucketTestUnsignedInterval(2, 100000, 0, 4294967295, 3, &prng); | |
192 BucketTestUnsignedInterval(17, 100000, 455, 2147484110, 3, &prng); | |
193 // 99.7% of all samples will be within 3 standard deviations of the mean, | |
194 // but since we test 1000 buckets we allow an interval of 4 sigma. | |
195 BucketTestUnsignedInterval(1000, 1000000, 0, 2147483999, 4, &prng); | |
196 } | |
197 | |
198 TEST(RandomNumberGeneratorTest, UniformSignedInterval) { | |
199 Random prng(66260695729ull); | |
200 BucketTestSignedInterval(2, 100000, 0, 1, 3, &prng); | |
201 BucketTestSignedInterval(7, 100000, -2, 4, 3, &prng); | |
202 BucketTestSignedInterval(11, 100000, 1000, 1010, 3, &prng); | |
203 BucketTestSignedInterval(100, 100000, 0, 99, 3, &prng); | |
204 BucketTestSignedInterval(2, 100000, std::numeric_limits<int32_t>::min(), | |
205 std::numeric_limits<int32_t>::max(), 3, &prng); | |
206 BucketTestSignedInterval(17, 100000, -1073741826, 1073741829, 3, &prng); | |
207 // 99.7% of all samples will be within 3 standard deviations of the mean, | |
208 // but since we test 1000 buckets we allow an interval of 4 sigma. | |
209 BucketTestSignedInterval(1000, 1000000, -352, 2147483647, 4, &prng); | |
210 } | |
211 | |
212 // The range of the random numbers is divided into bucket count intervals | |
213 // of consecutive numbers. Check that approximately equally many numbers | |
214 // from each inteval are generated. | |
215 void BucketTestFloat(unsigned int bucket_count, | |
216 unsigned int samples, | |
217 int sigma_level, | |
218 Random* prng) { | |
219 assert(bucket_count >= 2); | |
220 std::vector<unsigned int> buckets(bucket_count, 0); | |
221 | |
222 for (unsigned int i = 0; i < samples; i++) { | |
223 uint32_t sample = bucket_count * prng->Rand<float>(); | |
224 EXPECT_LE(0u, sample); | |
225 EXPECT_GE(bucket_count - 1, sample); | |
226 buckets[sample]++; | |
227 } | |
228 | |
229 for (unsigned int i = 0; i < bucket_count; i++) { | |
230 // Expect the result to be within 3 standard deviations of the mean, | |
231 // or more generally, within sigma_level standard deviations of the mean. | |
232 double mean = static_cast<double>(samples) / bucket_count; | |
233 EXPECT_NEAR(buckets[i], mean, sigma_level * sqrt(mean)); | |
234 } | |
235 } | |
236 | |
237 TEST(RandomNumberGeneratorTest, UniformFloatInterval) { | |
238 Random prng(1380648813ull); | |
239 BucketTestFloat(100, 100000, 3, &prng); | |
240 // 99.7% of all samples will be within 3 standard deviations of the mean, | |
241 // but since we test 1000 buckets we allow an interval of 4 sigma. | |
242 // BucketTestSignedInterval(1000, 1000000, -352, 2147483647, 4, &prng); | |
243 } | |
244 | |
245 TEST(RandomNumberGeneratorTest, SignedHasSameBitPattern) { | |
246 Random prng_signed(66738480ull), prng_unsigned(66738480ull); | |
247 | |
248 for (int i = 0; i < 1000; i++) { | |
249 signed int s = prng_signed.Rand<signed int>(); | |
250 unsigned int u = prng_unsigned.Rand<unsigned int>(); | |
251 EXPECT_EQ(u, static_cast<unsigned int>(s)); | |
252 } | |
253 | |
254 for (int i = 0; i < 1000; i++) { | |
255 int16_t s = prng_signed.Rand<int16_t>(); | |
256 uint16_t u = prng_unsigned.Rand<uint16_t>(); | |
257 EXPECT_EQ(u, static_cast<uint16_t>(s)); | |
258 } | |
259 | |
260 for (int i = 0; i < 1000; i++) { | |
261 signed char s = prng_signed.Rand<signed char>(); | |
262 unsigned char u = prng_unsigned.Rand<unsigned char>(); | |
263 EXPECT_EQ(u, static_cast<unsigned char>(s)); | |
264 } | |
265 } | |
266 | |
267 TEST(RandomNumberGeneratorTest, Gaussian) { | |
268 const int kN = 100000; | |
269 const int kBuckets = 100; | |
270 const double kMean = 49; | |
271 const double kStddev = 10; | |
272 | |
273 Random prng(1256637061); | |
274 | |
275 std::vector<unsigned int> buckets(kBuckets, 0); | |
276 for (int i = 0; i < kN; i++) { | |
277 int index = prng.Gaussian(kMean, kStddev) + 0.5; | |
278 if (index >= 0 && index < kBuckets) { | |
279 buckets[index]++; | |
280 } | |
281 } | |
282 | |
283 const double kPi = 3.14159265358979323846; | |
284 const double kScale = 1 / (kStddev * sqrt(2.0 * kPi)); | |
285 const double kDiv = -2.0 * kStddev * kStddev; | |
286 for (int n = 0; n < kBuckets; ++n) { | |
287 // Use Simpsons rule to estimate the probability that a random gaussian | |
288 // sample is in the interval [n-0.5, n-0.5]. | |
the sun
2015/11/25 10:12:37
That's an awfully small interval.
terelius
2015/11/27 10:35:24
In this test, we draw a real number x from a gauss
the sun
2015/11/27 11:02:10
But isn't the comment wrong? "[n-0.5, n-0.5]"
terelius
2015/11/27 11:46:07
Oh, sorry. Updated comment.
| |
289 double f_left = kScale * exp((n - kMean - 0.5) * (n - kMean - 0.5) / kDiv); | |
290 double f_mid = kScale * exp((n - kMean) * (n - kMean) / kDiv); | |
291 double f_right = kScale * exp((n - kMean + 0.5) * (n - kMean + 0.5) / kDiv); | |
292 double normal_dist = (f_left + 4 * f_mid + f_right) / 6; | |
293 // Expect the number of samples to be within 3 standard deviations | |
294 // (rounded up) of the expected number of samples in the bucket. | |
295 EXPECT_NEAR(buckets[n], kN * normal_dist, 3 * sqrt(kN * normal_dist) + 1); | |
296 } | |
297 } | |
298 | |
299 } // namespace webrtc | |
OLD | NEW |