1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.rng.sampling.distribution;
18
19 import java.util.Locale;
20 import org.apache.commons.rng.UniformRandomProvider;
21 import org.apache.commons.rng.core.source64.LongProvider;
22 import org.apache.commons.rng.core.source64.SplitMix64;
23 import org.apache.commons.rng.sampling.RandomAssert;
24 import org.junit.jupiter.api.Assertions;
25 import org.junit.jupiter.api.Test;
26 import org.junit.jupiter.params.ParameterizedTest;
27 import org.junit.jupiter.params.provider.ValueSource;
28
29
30
31
32
33
34
35
36
37
38
39
40
41 class UniformLongSamplerTest {
42
43
44
45 @Test
46 void testConstructorThrowsWithLowerAboveUpper() {
47 final long upper = 55;
48 final long lower = upper + 1;
49 final UniformRandomProvider rng = RandomAssert.seededRNG();
50 Assertions.assertThrows(IllegalArgumentException.class,
51 () -> UniformLongSampler.of(rng, lower, upper));
52 }
53
54 @Test
55 void testSamplesWithRangeOf1() {
56 final long upper = 99;
57 final long lower = upper;
58 final UniformRandomProvider rng = RandomAssert.createRNG();
59 final UniformLongSampler sampler = UniformLongSampler.of(rng, lower, upper);
60 for (int i = 0; i < 5; i++) {
61 Assertions.assertEquals(lower, sampler.sample());
62 }
63 }
64
65
66
67
68
69 @Test
70 void testSamplesWithFullRange() {
71 final long upper = Long.MAX_VALUE;
72 final long lower = Long.MIN_VALUE;
73 final UniformRandomProvider rng1 = RandomAssert.seededRNG();
74 final UniformRandomProvider rng2 = RandomAssert.seededRNG();
75 final UniformLongSampler sampler = UniformLongSampler.of(rng2, lower, upper);
76 for (int i = 0; i < 10; i++) {
77 Assertions.assertEquals(rng1.nextLong(), sampler.sample());
78 }
79 }
80
81
82
83
84
85
86
87 @ParameterizedTest
88 @ValueSource(longs = {234293789329234L, 145, 69987, 12673586268L, 234785389445435L, Long.MAX_VALUE})
89 void testSamplesWithSmallNonPowerOf2Range(long upper) {
90 for (final long lower : new long[] {-13, 0, 13}) {
91 final long n = upper - lower + 1;
92
93 if (n < 0) {
94 continue;
95 }
96
97 Assertions.assertNotEquals(0, n & (n - 1), "Power of 2 is invalid here");
98
99
100
101
102 final long m = Long.MIN_VALUE - Long.remainderUnsigned(Long.MIN_VALUE, n);
103
104
105 Assertions.assertEquals(m, (Long.MIN_VALUE / n) * -n);
106
107
108
109
110 final UniformRandomProvider rng1 = createRngWithFullBitsOnFirstCall(m);
111 final UniformRandomProvider rng2 = createRngWithFullBitsOnFirstCall(m);
112 final UniformLongSampler sampler = UniformLongSampler.of(rng2, lower, upper);
113 for (int i = 0; i < 100; i++) {
114 Assertions.assertEquals(lower + rng1.nextLong(n), sampler.sample());
115 }
116 }
117 }
118
119
120
121
122
123
124
125
126 private static UniformRandomProvider createRngWithFullBitsOnFirstCall(long m) {
127 return new SplitMix64(0L) {
128 private int i;
129 @Override
130 public long next() {
131 int j = i++;
132 if (j == 0) {
133
134 return -1L;
135 } else if (j < 6) {
136
137
138
139 return (m + 3 - j) << 1;
140 }
141 return super.next();
142 }
143 };
144 }
145
146
147
148
149
150 @Test
151 void testSamplesWithPowerOf2Range() {
152 final UniformRandomProvider rngZeroBits = new LongProvider() {
153 @Override
154 public long next() {
155
156 return 0L;
157 }
158 };
159 final UniformRandomProvider rngAllBits = new LongProvider() {
160 @Override
161 public long next() {
162
163 return -1L;
164 }
165 };
166
167 final long lower = -3;
168 UniformLongSampler sampler;
169
170
171
172 for (int i = 0; i < 64; i++) {
173 final long range = 1L << i;
174 final long upper = lower + range - 1;
175 sampler = UniformLongSampler.of(rngZeroBits, lower, upper);
176 Assertions.assertEquals(lower, sampler.sample(), "Zero bits sample");
177 sampler = UniformLongSampler.of(rngAllBits, lower, upper);
178 Assertions.assertEquals(upper, sampler.sample(), "All bits sample");
179 }
180 }
181
182
183
184
185
186 @Test
187 void testSamplesWithPowerOf2RangeIsBitShift() {
188 final long lower = 0;
189 UniformLongSampler sampler;
190
191 for (int i = 1; i <= 63; i++) {
192
193 final long upper = (1L << i) - 1;
194 final int shift = 64 - i;
195 final UniformRandomProvider rng1 = RandomAssert.seededRNG();
196 final UniformRandomProvider rng2 = RandomAssert.seededRNG();
197 sampler = UniformLongSampler.of(rng2, lower, upper);
198 for (int j = 0; j < 10; j++) {
199 Assertions.assertEquals(rng1.nextLong() >>> shift, sampler.sample());
200 }
201 }
202 }
203
204
205
206
207
208 @Test
209 void testSamplesWithLargeNonPowerOf2RangeIsRejectionMethod() {
210
211 final long upper = Long.MAX_VALUE / 2 + 1;
212 final long lower = Long.MIN_VALUE / 2 - 1;
213 final UniformRandomProvider rng1 = RandomAssert.seededRNG();
214 final UniformRandomProvider rng2 = RandomAssert.seededRNG();
215 final UniformLongSampler sampler = UniformLongSampler.of(rng2, lower, upper);
216 for (int i = 0; i < 10; i++) {
217
218 long expected;
219 do {
220 expected = rng1.nextLong();
221 } while (expected < lower || expected > upper);
222 Assertions.assertEquals(expected, sampler.sample());
223 }
224 }
225
226 @Test
227 void testOffsetSamplesWithNonPowerOf2Range() {
228 assertOffsetSamples(257);
229 }
230
231 @Test
232 void testOffsetSamplesWithPowerOf2Range() {
233 assertOffsetSamples(256);
234 }
235
236 @Test
237 void testOffsetSamplesWithRangeOf1() {
238 assertOffsetSamples(1);
239 }
240
241 private static void assertOffsetSamples(long range) {
242 final UniformRandomProvider[] rngs = RandomAssert.createRNG(3);
243 final UniformRandomProvider rng1 = rngs[0];
244 final UniformRandomProvider rng2 = rngs[1];
245 final UniformRandomProvider rng3 = rngs[2];
246
247
248 range = range - 1;
249 final long offsetLo = -13;
250 final long offsetHi = 42;
251 final UniformLongSampler sampler = UniformLongSampler.of(rng1, 0, range);
252 final UniformLongSampler samplerLo = UniformLongSampler.of(rng2, offsetLo, offsetLo + range);
253 final UniformLongSampler samplerHi = UniformLongSampler.of(rng3, offsetHi, offsetHi + range);
254 for (int i = 0; i < 10; i++) {
255 final long sample1 = sampler.sample();
256 final long sample2 = samplerLo.sample();
257 final long sample3 = samplerHi.sample();
258 Assertions.assertEquals(sample1 + offsetLo, sample2, "Incorrect negative offset sample");
259 Assertions.assertEquals(sample1 + offsetHi, sample3, "Incorrect positive offset sample");
260 }
261 }
262
263
264
265
266 @Test
267 void testSampleUniformityWithPowerOf2Range() {
268
269
270
271 final UniformRandomProvider rng = new LongProvider() {
272 private long bits = 0;
273
274 @Override
275 public long next() {
276
277 return Long.reverse(bits++);
278 }
279 };
280
281
282 final int n = 32;
283 final int[] histogram = new int[n];
284
285 final long lower = 0;
286 final long upper = n - 1;
287
288 final UniformLongSampler sampler = UniformLongSampler.of(rng, lower, upper);
289
290 final int expected = 2;
291 for (int i = expected * n; i-- > 0;) {
292 histogram[(int) sampler.sample()]++;
293 }
294
295
296 for (int value : histogram) {
297 Assertions.assertEquals(expected, value);
298 }
299 }
300
301 @Test
302 void testSharedStateSamplerWithSmallRange() {
303 assertSharedStateSampler(5, 67);
304 }
305
306 @Test
307 void testSharedStateSamplerWithLargeRange() {
308
309
310 assertSharedStateSampler(Long.MIN_VALUE / 2 - 1, Long.MAX_VALUE / 2 + 1);
311 }
312
313 @Test
314 void testSharedStateSamplerWithPowerOf2Range() {
315 assertSharedStateSampler(0, (1L << 45) - 1);
316 }
317
318 @Test
319 void testSharedStateSamplerWithRangeOf1() {
320 assertSharedStateSampler(968757657572323L, 968757657572323L);
321 }
322
323
324
325
326
327
328
329
330 private static void assertSharedStateSampler(long lower, long upper) {
331 final UniformRandomProvider rng1 = RandomAssert.seededRNG();
332 final UniformRandomProvider rng2 = RandomAssert.seededRNG();
333 final UniformLongSampler sampler1 = UniformLongSampler.of(rng1, lower, upper);
334 final UniformLongSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
335 RandomAssert.assertProduceSameSequence(sampler1, sampler2);
336 }
337
338 @Test
339 void testToStringWithSmallRange() {
340 assertToString(5, 67);
341 }
342
343 @Test
344 void testToStringWithLargeRange() {
345 assertToString(-99999999, Long.MAX_VALUE);
346 }
347
348 @Test
349 void testToStringWithPowerOf2Range() {
350
351 assertToString(0, 31);
352 }
353
354 @Test
355 void testToStringWithRangeOf1() {
356 assertToString(9, 9);
357 }
358
359
360
361
362
363
364
365
366 private static void assertToString(long lower, long upper) {
367 final UniformRandomProvider rng = RandomAssert.seededRNG();
368 final UniformLongSampler sampler = UniformLongSampler.of(rng, lower, upper);
369 Assertions.assertTrue(sampler.toString().toLowerCase(Locale.US).contains("uniform"));
370 }
371 }