You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2022/05/16 20:54:22 UTC
[commons-rng] 02/05: RNG-176: Update anonymous implementations of UniformRandomProvider
This is an automated email from the ASF dual-hosted git repository.
aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rng.git
commit d441d958581c09cef31d6a46d6b70faf1d029598
Author: aherbert <ah...@apache.org>
AuthorDate: Tue May 10 16:42:30 2022 +0100
RNG-176: Update anonymous implementations of UniformRandomProvider
Only one method is required to implement the interface. This simplifies
creation of implementations for testing edge cases.
For a delegate implementation then additional methods must be added.
This applies to RandomSource.unrestorable.
---
.../jmh/simple/ThreadLocalPerformance.java | 23 +-
.../DiscreteProbabilityCollectionSamplerTest.java | 23 +-
.../distribution/SmallMeanPoissonSamplerTest.java | 32 +--
.../ZigguratNormalizedGaussianSamplerTest.java | 15 +-
.../apache/commons/rng/simple/RandomSource.java | 125 ++++++++++-
.../apache/commons/rng/simple/RandomAssert.java | 30 ++-
.../commons/rng/simple/RandomSourceTest.java | 244 +++++++++++++++++++++
src/main/resources/pmd/pmd-ruleset.xml | 8 +-
8 files changed, 414 insertions(+), 86 deletions(-)
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ThreadLocalPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ThreadLocalPerformance.java
index 8b62fe0d..c2f6ccaf 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ThreadLocalPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ThreadLocalPerformance.java
@@ -133,28 +133,7 @@ public class ThreadLocalPerformance {
@Threads(4)
public long threadLocalRandomWrapped() {
final ThreadLocalRandom rand = ThreadLocalRandom.current();
- final UniformRandomProvider rng = new UniformRandomProvider() {
- // CHECKSTYLE: stop all
- @Override
- public void nextBytes(byte[] bytes) { /* Ignore this. */ }
- @Override
- public void nextBytes(byte[] bytes, int start, int len) { /* Ignore this. */ }
- @Override
- public int nextInt() { return rand.nextInt(); }
- @Override
- public int nextInt(int n) { return rand.nextInt(n); }
- @Override
- public long nextLong() { return rand.nextLong(); }
- @Override
- public long nextLong(long n) { return rand.nextLong(n); }
- @Override
- public boolean nextBoolean() { return rand.nextBoolean(); }
- @Override
- public float nextFloat() { return rand.nextFloat(); }
- @Override
- public double nextDouble() { return rand.nextDouble(); }
- // CHECKSTYLE: resume all
- };
+ final UniformRandomProvider rng = rand::nextLong;
long result = 0;
for (int i = 0; i < numValues; i++) {
result = result ^ rng.nextLong();
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/DiscreteProbabilityCollectionSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/DiscreteProbabilityCollectionSamplerTest.java
index d6424d95..f41d7b32 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/DiscreteProbabilityCollectionSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/DiscreteProbabilityCollectionSamplerTest.java
@@ -168,18 +168,17 @@ class DiscreteProbabilityCollectionSamplerTest {
// a probability (for the second item) that hits an edge case.
final UniformRandomProvider dummyRng = new UniformRandomProvider() {
private int count;
- // CHECKSTYLE: stop all
- public long nextLong(long n) { return 0; }
- public long nextLong() { return 0; }
- public int nextInt(int n) { return 0; }
- public int nextInt() { return 0; }
- public float nextFloat() { return 0; }
- // Return 0 then the given probability
- public double nextDouble() { return (count++ == 0) ? 0 : 1.0; }
- public void nextBytes(byte[] bytes, int start, int len) {}
- public void nextBytes(byte[] bytes) {}
- public boolean nextBoolean() { return false; }
- // CHECKSTYLE: resume all
+
+ @Override
+ public long nextLong() {
+ return 0;
+ }
+
+ @Override
+ public double nextDouble() {
+ // Return 0 then the 1.0 for the probability
+ return (count++ == 0) ? 0 : 1.0;
+ }
};
final List<Double> items = Arrays.asList(1d, 2d);
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java
index 06670474..6a55e69e 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSamplerTest.java
@@ -21,6 +21,8 @@ import org.apache.commons.rng.sampling.RandomAssert;
import org.apache.commons.rng.simple.RandomSource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
/**
* Test for the {@link SmallMeanPoissonSampler}. The tests hit edge cases for the sampler.
@@ -56,27 +58,15 @@ class SmallMeanPoissonSamplerTest {
/**
* Test the sample is bounded to 1000 * mean.
*/
- @Test
- void testSampleUpperBounds() {
- // If the nextDouble() is always 1 then the sample will hit the upper bounds
- final UniformRandomProvider rng = new UniformRandomProvider() {
- // CHECKSTYLE: stop all
- public long nextLong(long n) { return 0; }
- public long nextLong() { return 0; }
- public int nextInt(int n) { return 0; }
- public int nextInt() { return 0; }
- public float nextFloat() { return 0; }
- public double nextDouble() { return 1;}
- public void nextBytes(byte[] bytes, int start, int len) {}
- public void nextBytes(byte[] bytes) {}
- public boolean nextBoolean() { return false; }
- // CHECKSTYLE: resume all
- };
- for (double mean : new double[] {0.5, 1, 1.5, 2.2}) {
- final SharedStateDiscreteSampler sampler = SmallMeanPoissonSampler.of(rng, mean);
- final int expected = (int) Math.ceil(1000 * mean);
- Assertions.assertEquals(expected, sampler.sample());
- }
+ @ParameterizedTest
+ @ValueSource(doubles = {0.5, 1, 1.5, 2.2})
+ void testSampleUpperBounds(double mean) {
+ // If the nextDouble() is always ~1 then the sample will hit the upper bounds.
+ // nextLong() returns -1; nextDouble returns Math.nextDown(1.0).
+ final UniformRandomProvider rng = () -> -1;
+ final SharedStateDiscreteSampler sampler = SmallMeanPoissonSampler.of(rng, mean);
+ final int expected = (int) Math.ceil(1000 * mean);
+ Assertions.assertEquals(expected, sampler.sample());
}
/**
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java
index 8f8667f4..430681cf 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSamplerTest.java
@@ -31,19 +31,8 @@ class ZigguratNormalizedGaussianSamplerTest {
void testInfiniteLoop() {
// A bad implementation whose only purpose is to force access
// to the rarest branch.
- final UniformRandomProvider bad = new UniformRandomProvider() {
- // CHECKSTYLE: stop all
- public long nextLong(long n) { return 0; }
- public long nextLong() { return Long.MAX_VALUE; }
- public int nextInt(int n) { return 0; }
- public int nextInt() { return Integer.MAX_VALUE; }
- public float nextFloat() { return 1; }
- public double nextDouble() { return 1;}
- public void nextBytes(byte[] bytes, int start, int len) {}
- public void nextBytes(byte[] bytes) {}
- public boolean nextBoolean() { return false; }
- // CHECKSTYLE: resume all
- };
+ // nextLong() returns Long.MAX_VALUE
+ final UniformRandomProvider bad = () -> Long.MAX_VALUE;
// Infinite loop (in v1.1).
final ZigguratNormalizedGaussianSampler sampler = new ZigguratNormalizedGaussianSampler(bad);
diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java
index 28b49755..2432631f 100644
--- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java
+++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java
@@ -16,6 +16,9 @@
*/
package org.apache.commons.rng.simple;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
import org.apache.commons.rng.UniformRandomProvider;
import org.apache.commons.rng.RestorableUniformRandomProvider;
import org.apache.commons.rng.simple.internal.ProviderBuilder;
@@ -1015,20 +1018,36 @@ public enum RandomSource {
/**
* Wraps the given {@code delegate} generator in a new instance that
- * does not allow access to the "save/restore" functionality.
+ * only provides access to the {@link UniformRandomProvider} methods.
+ *
+ * <p>This method can be used to prevent access to any methods of the {@code delegate}
+ * that are not defined in the {@link UniformRandomProvider} interface.
+ * For example this will prevent access to the "save/restore" functionality of
+ * any {@link RestorableUniformRandomProvider} {@link #create() created}
+ * by the {@link RandomSource} factory methods, or will prevent access to the jump
+ * functionality of generators.
+ *
+ * <p>Since the method applies to more than the {@link RestorableUniformRandomProvider}
+ * interface it is left to the caller to determine if any methods require hiding,
+ * for example:
+ *
+ * <pre><code>
+ * UniformRandomProvider rng = ...;
+ * if (rng instanceof JumpableUniformRandomProvider) {
+ * rng = RandomSource.unrestorable(rng);
+ * }
+ * </code></pre>
*
* @param delegate Generator to which calls will be delegated.
- * @return a new instance whose state cannot be saved or restored.
+ * @return a new instance
*/
public static UniformRandomProvider unrestorable(final UniformRandomProvider delegate) {
return new UniformRandomProvider() {
- /** {@inheritDoc} */
@Override
public void nextBytes(byte[] bytes) {
delegate.nextBytes(bytes);
}
- /** {@inheritDoc} */
@Override
public void nextBytes(byte[] bytes,
int start,
@@ -1036,49 +1055,131 @@ public enum RandomSource {
delegate.nextBytes(bytes, start, len);
}
- /** {@inheritDoc} */
@Override
public int nextInt() {
return delegate.nextInt();
}
- /** {@inheritDoc} */
@Override
public int nextInt(int n) {
return delegate.nextInt(n);
}
- /** {@inheritDoc} */
+ @Override
+ public int nextInt(int origin, int bound) {
+ return delegate.nextInt(origin, bound);
+ }
+
@Override
public long nextLong() {
return delegate.nextLong();
}
- /** {@inheritDoc} */
@Override
public long nextLong(long n) {
return delegate.nextLong(n);
}
- /** {@inheritDoc} */
+ @Override
+ public long nextLong(long origin, long bound) {
+ return delegate.nextLong(origin, bound);
+ }
+
@Override
public boolean nextBoolean() {
return delegate.nextBoolean();
}
- /** {@inheritDoc} */
@Override
public float nextFloat() {
return delegate.nextFloat();
}
- /** {@inheritDoc} */
+ @Override
+ public float nextFloat(float bound) {
+ return delegate.nextFloat(bound);
+ }
+
+ @Override
+ public float nextFloat(float origin, float bound) {
+ return delegate.nextFloat(origin, bound);
+ }
+
@Override
public double nextDouble() {
return delegate.nextDouble();
}
- /** {@inheritDoc} */
+ @Override
+ public double nextDouble(double bound) {
+ return delegate.nextDouble(bound);
+ }
+
+ @Override
+ public double nextDouble(double origin, double bound) {
+ return delegate.nextDouble(origin, bound);
+ }
+
+ @Override
+ public IntStream ints() {
+ return delegate.ints();
+ }
+
+ @Override
+ public IntStream ints(int origin, int bound) {
+ return delegate.ints(origin, bound);
+ }
+
+ @Override
+ public IntStream ints(long streamSize) {
+ return delegate.ints(streamSize);
+ }
+
+ @Override
+ public IntStream ints(long streamSize, int origin, int bound) {
+ return delegate.ints(streamSize, origin, bound);
+ }
+
+ @Override
+ public LongStream longs() {
+ return delegate.longs();
+ }
+
+ @Override
+ public LongStream longs(long origin, long bound) {
+ return delegate.longs(origin, bound);
+ }
+
+ @Override
+ public LongStream longs(long streamSize) {
+ return delegate.longs(streamSize);
+ }
+
+ @Override
+ public LongStream longs(long streamSize, long origin, long bound) {
+ return delegate.longs(streamSize, origin, bound);
+ }
+
+ @Override
+ public DoubleStream doubles() {
+ return delegate.doubles();
+ }
+
+ @Override
+ public DoubleStream doubles(double origin, double bound) {
+ return delegate.doubles(origin, bound);
+ }
+
+ @Override
+ public DoubleStream doubles(long streamSize) {
+ return delegate.doubles(streamSize);
+ }
+
+ @Override
+ public DoubleStream doubles(long streamSize, double origin, double bound) {
+ return delegate.doubles(streamSize, origin, bound);
+ }
+
@Override
public String toString() {
return delegate.toString();
diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomAssert.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomAssert.java
index d2955282..6b44dac8 100644
--- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomAssert.java
+++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomAssert.java
@@ -46,25 +46,33 @@ public final class RandomAssert {
Assertions.assertEquals(rng1.nextInt(), rng2.nextInt());
}
for (int i = 0; i < 4; i++) {
+ final int min = 31 * i + 3;
for (int j = 0; j < 5; j++) {
final int max = 107 * i + 374 * j + 11;
Assertions.assertEquals(rng1.nextInt(max), rng2.nextInt(max));
+ Assertions.assertEquals(rng1.nextInt(min, max), rng2.nextInt(min, max));
}
}
for (int i = 0; i < 23; i++) {
Assertions.assertEquals(rng1.nextLong(), rng2.nextLong());
}
for (int i = 0; i < 4; i++) {
+ final int min = 31 * i + 3;
for (int j = 0; j < 5; j++) {
- final long max = (Long.MAX_VALUE << 2) + 107 * i + 374 * j + 11;
+ final long max = (Long.MAX_VALUE >> 2) + 107 * i + 374 * j + 11;
Assertions.assertEquals(rng1.nextLong(max), rng2.nextLong(max));
+ Assertions.assertEquals(rng1.nextLong(min, max), rng2.nextLong(min, max));
}
}
- for (int i = 0; i < 103; i++) {
+ for (int i = 0; i < 35; i++) {
Assertions.assertEquals(rng1.nextFloat(), rng2.nextFloat());
+ Assertions.assertEquals(rng1.nextFloat(12.34f), rng2.nextFloat(12.34f));
+ Assertions.assertEquals(rng1.nextFloat(1.23f, 12.34f), rng2.nextFloat(1.23f, 12.34f));
}
- for (int i = 0; i < 79; i++) {
+ for (int i = 0; i < 27; i++) {
Assertions.assertEquals(rng1.nextDouble(), rng2.nextDouble());
+ Assertions.assertEquals(rng1.nextDouble(12.34), rng2.nextDouble(12.34));
+ Assertions.assertEquals(rng1.nextDouble(1.23, 12.34), rng2.nextDouble(1.23, 12.34));
}
final int size = 345;
@@ -84,5 +92,21 @@ public final class RandomAssert {
rng2.nextBytes(a2, offset, n);
Assertions.assertArrayEquals(a1, a2);
}
+
+ // Streams
+ Assertions.assertArrayEquals(rng1.ints().limit(4).toArray(), rng2.ints().limit(4).toArray());
+ Assertions.assertArrayEquals(rng1.ints(5).toArray(), rng2.ints(5).toArray());
+ Assertions.assertArrayEquals(rng1.ints(-3, 12).limit(4).toArray(), rng2.ints(-3, 12).limit(4).toArray());
+ Assertions.assertArrayEquals(rng1.ints(5, -13, 2).toArray(), rng2.ints(5, -13, 2).toArray());
+
+ Assertions.assertArrayEquals(rng1.longs().limit(4).toArray(), rng2.longs().limit(4).toArray());
+ Assertions.assertArrayEquals(rng1.longs(5).toArray(), rng2.longs(5).toArray());
+ Assertions.assertArrayEquals(rng1.longs(-3, 12).limit(4).toArray(), rng2.longs(-3, 12).limit(4).toArray());
+ Assertions.assertArrayEquals(rng1.longs(5, -13, 2).toArray(), rng2.longs(5, -13, 2).toArray());
+
+ Assertions.assertArrayEquals(rng1.doubles().limit(4).toArray(), rng2.doubles().limit(4).toArray());
+ Assertions.assertArrayEquals(rng1.doubles(5).toArray(), rng2.doubles(5).toArray());
+ Assertions.assertArrayEquals(rng1.doubles(-3, 12).limit(4).toArray(), rng2.doubles(-3, 12).limit(4).toArray());
+ Assertions.assertArrayEquals(rng1.doubles(5, -13, 2).toArray(), rng2.doubles(5, -13, 2).toArray());
}
}
diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomSourceTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomSourceTest.java
index 5bb0c8ef..49bd9635 100644
--- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomSourceTest.java
+++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomSourceTest.java
@@ -16,7 +16,16 @@
*/
package org.apache.commons.rng.simple;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.time.Duration;
+import java.util.SplittableRandom;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+import org.apache.commons.rng.RandomProviderState;
+import org.apache.commons.rng.RestorableUniformRandomProvider;
+import org.apache.commons.rng.UniformRandomProvider;
import org.apache.commons.rng.core.source64.LongProvider;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -99,4 +108,239 @@ class RandomSourceTest {
RandomSource.MSWS.createSeed(broken);
});
}
+
+ /**
+ * Test the unrestorable method correctly delegates all methods.
+ * This includes the methods with default implementations in UniformRandomProvider, i.e.
+ * the default methods should not be used.
+ */
+ @Test
+ void testUnrestorable() {
+ // The test class should override all default methods
+ assertNoDefaultMethods(RestorableRNG.class);
+
+ final UniformRandomProvider rng1 = new RestorableRNG();
+ final RestorableRNG r = new RestorableRNG();
+ final UniformRandomProvider rng2 = RandomSource.unrestorable(r);
+ Assertions.assertNotSame(rng2, r);
+
+ // The unrestorable instance should override all default methods
+ assertNoDefaultMethods(rng2.getClass());
+
+ // Despite the newly created RNG not being a RestorableUniformRandomProvider,
+ // the method still wraps it in a new object. This is the behaviour since version 1.0.
+ // It allows the method to wrap objects that implement UniformRandomProvider (such
+ // as sub-interfaces or concrete classes) to prevent access to any additional methods.
+ Assertions.assertNotSame(rng2, RandomSource.unrestorable(rng2));
+
+ // Ensure that they generate the same values.
+ RandomAssert.assertProduceSameSequence(rng1, rng2);
+
+ // Cast must work.
+ @SuppressWarnings("unused")
+ final RestorableUniformRandomProvider restorable = (RestorableUniformRandomProvider) rng1;
+ // Cast must fail.
+ Assertions.assertThrows(ClassCastException.class, () -> {
+ @SuppressWarnings("unused")
+ RestorableUniformRandomProvider dummy = (RestorableUniformRandomProvider) rng2;
+ });
+ }
+
+ /**
+ * Assert the class has overridden all default public interface methods.
+ *
+ * @param cls the class
+ */
+ private static void assertNoDefaultMethods(Class<?> cls) {
+ for (final Method method : cls.getMethods()) {
+ if ((method.getModifiers() & Modifier.PUBLIC) != 0) {
+ Assertions.assertTrue(!method.isDefault(),
+ () -> cls.getName() + " should override method: " + method.toGenericString());
+ }
+ }
+ }
+
+ /**
+ * Class to provide a complete implementation of the {@link UniformRandomProvider} interface.
+ * This must return a different result than the default implementations in the interface.
+ */
+ private static class RestorableRNG implements RestorableUniformRandomProvider {
+ /** The source of randomness. */
+ private final SplittableRandom rng = new SplittableRandom(123);
+
+ @Override
+ public void nextBytes(byte[] bytes) {
+ nextBytes(bytes, 0, bytes.length);
+ }
+
+ @Override
+ public void nextBytes(byte[] bytes, int start, int len) {
+ RestorableUniformRandomProvider.super.nextBytes(bytes, start, len);
+ // Rotate
+ for (int i = start + len; i-- > start;) {
+ bytes[i] += 1;
+ }
+ }
+
+ @Override
+ public int nextInt() {
+ return RestorableUniformRandomProvider.super.nextInt() + 1;
+ }
+
+ @Override
+ public int nextInt(int n) {
+ final int v = RestorableUniformRandomProvider.super.nextInt(n) + 1;
+ return v == n ? 0 : v;
+ }
+
+ @Override
+ public int nextInt(int origin, int bound) {
+ final int v = RestorableUniformRandomProvider.super.nextInt(origin, bound) + 1;
+ return v == bound ? origin : v;
+ }
+
+ @Override
+ public long nextLong() {
+ // Source of randomness for all derived methods
+ return rng.nextLong();
+ }
+
+ @Override
+ public long nextLong(long n) {
+ final long v = RestorableUniformRandomProvider.super.nextLong(n) + 1;
+ return v == n ? 0 : v;
+ }
+
+ @Override
+ public long nextLong(long origin, long bound) {
+ final long v = RestorableUniformRandomProvider.super.nextLong(origin, bound) + 1;
+ return v == bound ? origin : v;
+ }
+
+ @Override
+ public boolean nextBoolean() {
+ return !RestorableUniformRandomProvider.super.nextBoolean();
+ }
+
+ @Override
+ public float nextFloat() {
+ final float v = 1 - RestorableUniformRandomProvider.super.nextFloat();
+ return v == 1 ? 0 : v;
+ }
+
+ @Override
+ public float nextFloat(float bound) {
+ final float v = Math.nextUp(RestorableUniformRandomProvider.super.nextFloat(bound));
+ return v == bound ? 0 : v;
+ }
+
+ @Override
+ public float nextFloat(float origin, float bound) {
+ final float v = Math.nextUp(RestorableUniformRandomProvider.super.nextFloat(origin, bound));
+ return v == bound ? 0 : v;
+ }
+
+ @Override
+ public double nextDouble() {
+ final double v = 1 - RestorableUniformRandomProvider.super.nextDouble();
+ return v == 1 ? 0 : v;
+ }
+
+ @Override
+ public double nextDouble(double bound) {
+ final double v = Math.nextUp(RestorableUniformRandomProvider.super.nextDouble(bound));
+ return v == bound ? 0 : v;
+ }
+
+ @Override
+ public double nextDouble(double origin, double bound) {
+ final double v = Math.nextUp(RestorableUniformRandomProvider.super.nextDouble(origin, bound));
+ return v == bound ? 0 : v;
+ }
+
+ // Stream methods must return different values than the default so we reimplement them
+
+ @Override
+ public IntStream ints() {
+ return IntStream.generate(() -> nextInt() + 1).sequential();
+ }
+
+ @Override
+ public IntStream ints(int origin, int bound) {
+ return IntStream.generate(() -> {
+ final int v = nextInt(origin, bound) + 1;
+ return v == bound ? origin : v;
+ }).sequential();
+ }
+
+ @Override
+ public IntStream ints(long streamSize) {
+ return ints().limit(streamSize);
+ }
+
+ @Override
+ public IntStream ints(long streamSize, int origin, int bound) {
+ return ints(origin, bound).limit(streamSize);
+ }
+
+ @Override
+ public LongStream longs() {
+ return LongStream.generate(() -> nextLong() + 1).sequential();
+ }
+
+ @Override
+ public LongStream longs(long origin, long bound) {
+ return LongStream.generate(() -> {
+ final long v = nextLong(origin, bound) + 1;
+ return v == bound ? origin : v;
+ }).sequential();
+ }
+
+ @Override
+ public LongStream longs(long streamSize) {
+ return longs().limit(streamSize);
+ }
+
+ @Override
+ public LongStream longs(long streamSize, long origin, long bound) {
+ return longs(origin, bound).limit(streamSize);
+ }
+
+ @Override
+ public DoubleStream doubles() {
+ return DoubleStream.generate(() -> {
+ final double v = Math.nextUp(nextDouble());
+ return v == 1 ? 0 : v;
+ }).sequential();
+ }
+
+ @Override
+ public DoubleStream doubles(double origin, double bound) {
+ return DoubleStream.generate(() -> {
+ final double v = Math.nextUp(nextDouble(origin, bound));
+ return v == bound ? origin : v;
+ }).sequential();
+ }
+
+ @Override
+ public DoubleStream doubles(long streamSize) {
+ return doubles().limit(streamSize);
+ }
+
+ @Override
+ public DoubleStream doubles(long streamSize, double origin, double bound) {
+ return doubles(origin, bound).limit(streamSize);
+ }
+
+ @Override
+ public RandomProviderState saveState() {
+ // Do nothing
+ return null;
+ }
+
+ @Override
+ public void restoreState(RandomProviderState state) {
+ // Do nothing
+ }
+ }
}
diff --git a/src/main/resources/pmd/pmd-ruleset.xml b/src/main/resources/pmd/pmd-ruleset.xml
index ae95f65f..288690f7 100644
--- a/src/main/resources/pmd/pmd-ruleset.xml
+++ b/src/main/resources/pmd/pmd-ruleset.xml
@@ -171,16 +171,18 @@
</rule>
<rule ref="category/java/design.xml/ExcessiveMethodLength">
<properties>
- <!-- The length is due to comments -->
+ <!-- The length is due to comments, or for RandomSource the wrapping of a delegate UniformRandomProvider -->
<property name="violationSuppressXPath" value="//ClassOrInterfaceDeclaration[@SimpleName='AliasMethodDiscreteSampler'
- or @SimpleName='ProbabilityDensityApproximationCommand']"/>
+ or @SimpleName='ProbabilityDensityApproximationCommand']
+ or ./ancestor::EnumDeclaration[@SimpleName='RandomSource']"/>
</properties>
</rule>
<rule ref="category/java/design.xml/ExcessiveClassLength">
<properties>
<!-- The length is due to multiple implementations as inner classes -->
<property name="violationSuppressXPath" value="//ClassOrInterfaceDeclaration[@SimpleName='MarsagliaTsangWangDiscreteSampler'
- or @SimpleName='CompositeSamplers' or @SimpleName='StableSampler' or @SimpleName='ZigguratSampler']"/>
+ or @SimpleName='CompositeSamplers' or @SimpleName='StableSampler' or @SimpleName='ZigguratSampler']
+ or ./ancestor::EnumDeclaration[@SimpleName='RandomSource']"/>
</properties>
</rule>
<rule ref="category/java/design.xml/ExcessiveParameterList">