1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.rng.sampling.distribution;
19
20 import org.apache.commons.rng.UniformRandomProvider;
21
22 /**
23 * Discrete uniform distribution sampler generating values of type {@code long}.
24 *
25 * <p>Sampling uses {@link UniformRandomProvider#nextLong}.</p>
26 *
27 * <p>When the range is a power of two the number of calls is 1 per sample.
28 * Otherwise a rejection algorithm is used to ensure uniformity. In the worst
29 * case scenario where the range spans half the range of a {@code long}
30 * (2<sup>63</sup> + 1) the expected number of calls is 2 per sample.</p>
31 *
32 * @since 1.4
33 */
34 public abstract class UniformLongSampler implements SharedStateLongSampler {
35 /** Underlying source of randomness. */
36 protected final UniformRandomProvider rng;
37
38 /**
39 * Discrete uniform distribution sampler when the sample value is fixed.
40 */
41 private static final class FixedUniformLongSampler extends UniformLongSampler {
42 /** The value. */
43 private final long value;
44
45 /**
46 * @param value The value.
47 */
48 FixedUniformLongSampler(long value) {
49 // No requirement for the RNG
50 super(null);
51 this.value = value;
52 }
53
54 @Override
55 public long sample() {
56 return value;
57 }
58
59 @Override
60 public String toString() {
61 // No RNG to include in the string
62 return "Uniform deviate [X=" + value + "]";
63 }
64
65 @Override
66 public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
67 // No requirement for the RNG
68 return this;
69 }
70 }
71
72 /**
73 * Discrete uniform distribution sampler when the range is a power of 2 and greater than 1.
74 * This sampler assumes the lower bound of the range is 0.
75 *
76 * <p>Note: This cannot be used when the range is 1 (2^0) as the shift would be 64-bits
77 * which is ignored by the shift operator.</p>
78 */
79 private static final class PowerOf2RangeUniformLongSampler extends UniformLongSampler {
80 /** Bit shift to apply to the long sample. */
81 private final int shift;
82
83 /**
84 * @param rng Generator of uniformly distributed random numbers.
85 * @param range Maximum range of the sample (exclusive).
86 * Must be a power of 2 greater than 2^0.
87 */
88 PowerOf2RangeUniformLongSampler(UniformRandomProvider rng,
89 long range) {
90 super(rng);
91 this.shift = Long.numberOfLeadingZeros(range) + 1;
92 }
93
94 /**
95 * @param rng Generator of uniformly distributed random numbers.
96 * @param source Source to copy.
97 */
98 PowerOf2RangeUniformLongSampler(UniformRandomProvider rng,
99 PowerOf2RangeUniformLongSampler source) {
100 super(rng);
101 this.shift = source.shift;
102 }
103
104 @Override
105 public long sample() {
106 // Use a bit shift to favour the most significant bits.
107 return rng.nextLong() >>> shift;
108 }
109
110 @Override
111 public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
112 return new PowerOf2RangeUniformLongSampler(rng, this);
113 }
114 }
115
116 /**
117 * Discrete uniform distribution sampler when the range is small
118 * enough to fit in a positive long.
119 * This sampler assumes the lower bound of the range is 0 and the range is
120 * non-zero.
121 */
122 private static final class SmallRangeUniformLongSampler extends UniformLongSampler {
123 /** Maximum range of the sample (exclusive). */
124 private final long n;
125 /** Limit of the uniform range (inclusive) to sample a positive long.
126 * This is the largest positive multiple of {@code n} minus 1:
127 * {@code floor(2^63 / n) * n - 1}.
128 * The -1 changes the limit to an inclusive bound and allows support
129 * for a power of 2 range. */
130 private final long limit;
131
132 /**
133 * @param rng Generator of uniformly distributed random numbers.
134 * @param range Maximum range of the sample (exclusive).
135 */
136 SmallRangeUniformLongSampler(UniformRandomProvider rng,
137 long range) {
138 super(rng);
139 this.n = range;
140 // Set the upper limit for the positive long.
141 // The sample must be selected from the largest multiple
142 // of 'range' that fits within a positive value:
143 // limit = floor(2^63 / range) * range
144 // = 2^63 - (2^63 % range)
145 // Sample:
146 // X in [0, limit) or X in [0, limit - 1]
147 // return X % range
148 // This is a one-off computation cost.
149 // The divide will truncate towards zero (do not use Math.floorDiv).
150 // Note: This is invalid if range is not strictly positive.
151 limit = (Long.MIN_VALUE / range) * -range - 1;
152 }
153
154 /**
155 * @param rng Generator of uniformly distributed random numbers.
156 * @param source Source to copy.
157 */
158 SmallRangeUniformLongSampler(UniformRandomProvider rng,
159 SmallRangeUniformLongSampler source) {
160 super(rng);
161 this.n = source.n;
162 this.limit = source.limit;
163 }
164
165 @Override
166 public long sample() {
167 // Note:
168 // This will have the same output as the rejection algorithm
169 // from o.a.c.rng.core.BaseProvider. The limit for the uniform
170 // positive value can be pre-computed. This ensures exactly
171 // 1 modulus operation per call.
172 for (;;) {
173 // bits in [0, limit]
174 final long bits = rng.nextLong() >>> 1;
175 if (bits <= limit) {
176 return bits % n;
177 }
178 }
179 }
180
181 @Override
182 public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
183 return new SmallRangeUniformLongSampler(rng, this);
184 }
185 }
186
187 /**
188 * Discrete uniform distribution sampler when the range between lower and upper is too large
189 * to fit in a positive long.
190 */
191 private static final class LargeRangeUniformLongSampler extends UniformLongSampler {
192 /** Lower bound. */
193 private final long lower;
194 /** Upper bound. */
195 private final long upper;
196
197 /**
198 * @param rng Generator of uniformly distributed random numbers.
199 * @param lower Lower bound (inclusive) of the distribution.
200 * @param upper Upper bound (inclusive) of the distribution.
201 */
202 LargeRangeUniformLongSampler(UniformRandomProvider rng,
203 long lower,
204 long upper) {
205 super(rng);
206 this.lower = lower;
207 this.upper = upper;
208 }
209
210 @Override
211 public long sample() {
212 // Use a simple rejection method.
213 // This is used when (upper-lower) >= Long.MAX_VALUE.
214 // This will loop on average 2 times in the worst case scenario
215 // when (upper-lower) == Long.MAX_VALUE.
216 while (true) {
217 final long r = rng.nextLong();
218 if (r >= lower &&
219 r <= upper) {
220 return r;
221 }
222 }
223 }
224
225 @Override
226 public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
227 return new LargeRangeUniformLongSampler(rng, lower, upper);
228 }
229 }
230
231 /**
232 * Adds an offset to an underlying discrete sampler.
233 */
234 private static final class OffsetUniformLongSampler extends UniformLongSampler {
235 /** The offset. */
236 private final long offset;
237 /** The long sampler. */
238 private final UniformLongSampler sampler;
239
240 /**
241 * @param offset The offset for the sample.
242 * @param sampler The discrete sampler.
243 */
244 OffsetUniformLongSampler(long offset,
245 UniformLongSampler sampler) {
246 // No requirement for the RNG
247 super(null);
248 this.offset = offset;
249 this.sampler = sampler;
250 }
251
252 @Override
253 public long sample() {
254 return offset + sampler.sample();
255 }
256
257 @Override
258 public String toString() {
259 return sampler.toString();
260 }
261
262 @Override
263 public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
264 return new OffsetUniformLongSampler(offset, sampler.withUniformRandomProvider(rng));
265 }
266 }
267
268 /**
269 * @param rng Generator of uniformly distributed random numbers.
270 */
271 UniformLongSampler(UniformRandomProvider rng) {
272 this.rng = rng;
273 }
274
275 /** {@inheritDoc} */
276 @Override
277 public String toString() {
278 return "Uniform deviate [" + rng.toString() + "]";
279 }
280
281 /** {@inheritDoc} */
282 // Redeclare the signature to return a UniformLongSampler not a SharedStateLongSampler
283 @Override
284 public abstract UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng);
285
286 /**
287 * Creates a new discrete uniform distribution sampler.
288 *
289 * @param rng Generator of uniformly distributed random numbers.
290 * @param lower Lower bound (inclusive) of the distribution.
291 * @param upper Upper bound (inclusive) of the distribution.
292 * @return the sampler
293 * @throws IllegalArgumentException if {@code lower > upper}.
294 */
295 public static UniformLongSampler of(UniformRandomProvider rng,
296 long lower,
297 long upper) {
298 if (lower > upper) {
299 throw new IllegalArgumentException(lower + " > " + upper);
300 }
301
302 // Choose the algorithm depending on the range
303
304 // Edge case for no range.
305 // This must be done first as the methods to handle lower == 0
306 // do not handle upper == 0.
307 if (upper == lower) {
308 return new FixedUniformLongSampler(lower);
309 }
310
311 // Algorithms to ignore the lower bound if it is zero.
312 if (lower == 0) {
313 return createZeroBoundedSampler(rng, upper);
314 }
315
316 final long range = (upper - lower) + 1;
317 // Check power of 2 first to handle range == 2^63.
318 if (isPowerOf2(range)) {
319 return new OffsetUniformLongSampler(lower,
320 new PowerOf2RangeUniformLongSampler(rng, range));
321 }
322 if (range <= 0) {
323 // The range is too wide to fit in a positive long (larger
324 // than 2^63); use a simple rejection method.
325 // Note: if range == 0 then the input is [Long.MIN_VALUE, Long.MAX_VALUE].
326 // No specialisation exists for this and it is handled as a large range.
327 return new LargeRangeUniformLongSampler(rng, lower, upper);
328 }
329 // Use a sample from the range added to the lower bound.
330 return new OffsetUniformLongSampler(lower,
331 new SmallRangeUniformLongSampler(rng, range));
332 }
333
334 /**
335 * Create a new sampler for the range {@code 0} inclusive to {@code upper} inclusive.
336 *
337 * <p>This can handle any positive {@code upper}.
338 *
339 * @param rng Generator of uniformly distributed random numbers.
340 * @param upper Upper bound (inclusive) of the distribution. Must be positive.
341 * @return the sampler
342 */
343 private static UniformLongSampler createZeroBoundedSampler(UniformRandomProvider rng,
344 long upper) {
345 // Note: Handle any range up to 2^63 (which is negative as a signed
346 // 64-bit long but handled as a power of 2)
347 final long range = upper + 1;
348 return isPowerOf2(range) ?
349 new PowerOf2RangeUniformLongSampler(rng, range) :
350 new SmallRangeUniformLongSampler(rng, range);
351 }
352
353 /**
354 * Checks if the value is a power of 2.
355 *
356 * <p>This returns {@code true} for the value {@code Long.MIN_VALUE} which can be
357 * handled as an unsigned long of 2^63.</p>
358 *
359 * @param value Value.
360 * @return {@code true} if a power of 2
361 */
362 private static boolean isPowerOf2(final long value) {
363 return value != 0 && (value & (value - 1)) == 0;
364 }
365 }