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 11b8d250fc5731c3319e69dc441e22e8dd9474fb
Author: Alex Herbert <aherb...@apache.org>
AuthorDate: Sat Jul 17 12:03:14 2021 +0100

    Update DiscreteUniformSampler to match UniformLongSampler
    
    Fixed sampler does not require a RNG reference.
    
    Add test for a small range against a reference RNG.
    
    Add test for a power of 2 range against a reference RNG.
    
    Add test for a large range against the same RNG. Output outside the
    range should be ignored.
---
 .../distribution/DiscreteUniformSampler.java       | 16 ++--
 .../sampling/distribution/UniformLongSampler.java  |  2 +-
 .../distribution/DiscreteUniformSamplerTest.java   | 97 +++++++++++++++++-----
 .../distribution/UniformLongSamplerTest.java       |  2 +-
 4 files changed, 90 insertions(+), 27 deletions(-)

diff --git 
a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
 
b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
index 6a39aea..7190fc3 100644
--- 
a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
+++ 
b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSampler.java
@@ -87,12 +87,11 @@ public class DiscreteUniformSampler
         private final int value;
 
         /**
-         * @param rng Generator of uniformly distributed random numbers.
          * @param value The value.
          */
-        FixedDiscreteUniformSampler(UniformRandomProvider rng,
-                                    int value) {
-            super(rng);
+        FixedDiscreteUniformSampler(int value) {
+            // No requirement for the RNG
+            super(null);
             this.value = value;
         }
 
@@ -102,6 +101,12 @@ public class DiscreteUniformSampler
         }
 
         @Override
+        public String toString() {
+            // No RNG to include in the string
+            return "Uniform deviate [X=" + value + "]";
+        }
+
+        @Override
         public SharedStateDiscreteSampler 
withUniformRandomProvider(UniformRandomProvider rng) {
             // No requirement for the RNG
             return this;
@@ -305,7 +310,6 @@ public class DiscreteUniformSampler
             return offset + sampler.sample();
         }
 
-        /** {@inheritDoc} */
         @Override
         public String toString() {
             return sampler.toString();
@@ -379,7 +383,7 @@ public class DiscreteUniformSampler
         // This must be done first as the methods to handle lower == 0
         // do not handle upper == 0.
         if (upper == lower) {
-            return new FixedDiscreteUniformSampler(rng, lower);
+            return new FixedDiscreteUniformSampler(lower);
         }
 
         // Algorithms to ignore the lower bound if it is zero.
diff --git 
a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/UniformLongSampler.java
 
b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/UniformLongSampler.java
index 2eb05a1..28766ac 100644
--- 
a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/UniformLongSampler.java
+++ 
b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/UniformLongSampler.java
@@ -237,7 +237,7 @@ public abstract class UniformLongSampler implements 
SharedStateLongSampler {
     /**
      * @param rng Generator of uniformly distributed random numbers.
      */
-    private UniformLongSampler(UniformRandomProvider rng) {
+    UniformLongSampler(UniformRandomProvider rng) {
         this.rng = rng;
     }
 
diff --git 
a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
 
b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
index 053c268..98bdfa3 100644
--- 
a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
+++ 
b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteUniformSamplerTest.java
@@ -63,23 +63,49 @@ public class DiscreteUniformSamplerTest {
         final UniformRandomProvider rng1 = 
RandomSource.SPLIT_MIX_64.create(0L);
         final UniformRandomProvider rng2 = 
RandomSource.SPLIT_MIX_64.create(0L);
         final SharedStateDiscreteSampler sampler = 
DiscreteUniformSampler.of(rng2, lower, upper);
-        for (int i = 0; i < 5; i++) {
+        for (int i = 0; i < 10; i++) {
             Assert.assertEquals(rng1.nextInt(), sampler.sample());
         }
     }
 
+    /**
+     * Test samples with a non-power of 2 range.
+     * The output should be the same as the long values produced from a RNG
+     * based on o.a.c.rng.core.BaseProvider as the rejection algorithm is
+     * the same.
+     */
+    @Test
+    public void testSamplesWithSmallNonPowerOf2Range() {
+        final int upper = 257;
+        for (final int lower : new int[] {-13, 0, 13}) {
+            final int n = upper - lower + 1;
+            final UniformRandomProvider rng1 = 
RandomSource.SPLIT_MIX_64.create(0L);
+            final UniformRandomProvider rng2 = 
RandomSource.SPLIT_MIX_64.create(0L);
+            final SharedStateDiscreteSampler sampler = 
DiscreteUniformSampler.of(rng2, lower, upper);
+            for (int i = 0; i < 10; i++) {
+                Assert.assertEquals(lower + rng1.nextInt(n), sampler.sample());
+            }
+        }
+    }
+
+    /**
+     * Test samples with a power of 2 range.
+     * This tests the minimum and maximum output should be the range limits.
+     */
     @Test
     public void testSamplesWithPowerOf2Range() {
         final UniformRandomProvider rngZeroBits = new IntProvider() {
             @Override
             public int next() {
+                // No bits
                 return 0;
             }
         };
         final UniformRandomProvider rngAllBits = new IntProvider() {
             @Override
             public int next() {
-                return 0xffffffff;
+                // All bits
+                return -1;
             }
         };
 
@@ -87,7 +113,7 @@ public class DiscreteUniformSamplerTest {
         DiscreteUniformSampler sampler;
         // The upper range for a positive integer is 2^31-1. So the max 
positive power of
         // 2 is 2^30. However the sampler should handle a bit shift of 31 to 
create a range
-        // of Integer.MIN_VALUE (0x80000000) as this is a power of 2 as an 
unsigned int (2^31).
+        // of Integer.MIN_VALUE as this is a power of 2 as an unsigned int 
(2^31).
         for (int i = 0; i < 32; i++) {
             final int range = 1 << i;
             final int upper = lower + range - 1;
@@ -98,6 +124,50 @@ public class DiscreteUniformSamplerTest {
         }
     }
 
+    /**
+     * Test samples with a power of 2 range.
+     * This tests the output is created using a bit shift.
+     */
+    @Test
+    public void testSamplesWithPowerOf2RangeIsBitShift() {
+        final int lower = 0;
+        SharedStateDiscreteSampler sampler;
+        // Power of 2 sampler used for a bit shift of 1 to 31.
+        for (int i = 1; i <= 31; i++) {
+            // Upper is inclusive so subtract 1
+            final int upper = (1 << i) - 1;
+            final int shift = 32 - i;
+            final UniformRandomProvider rng1 = 
RandomSource.SPLIT_MIX_64.create(0L);
+            final UniformRandomProvider rng2 = 
RandomSource.SPLIT_MIX_64.create(0L);
+            sampler = DiscreteUniformSampler.of(rng2, lower, upper);
+            for (int j = 0; j < 10; j++) {
+                Assert.assertEquals(rng1.nextInt() >>> shift, 
sampler.sample());
+            }
+        }
+    }
+
+    /**
+     * Test samples with a large non-power of 2 range.
+     * This tests the large range algorithm uses a rejection method.
+     */
+    @Test
+    public void testSamplesWithLargeNonPowerOf2RangeIsRejectionMethod() {
+        // Create a range bigger than 2^63
+        final int upper = Integer.MAX_VALUE / 2 + 1;
+        final int lower = Integer.MIN_VALUE / 2 - 1;
+        final UniformRandomProvider rng1 = 
RandomSource.SPLIT_MIX_64.create(0L);
+        final UniformRandomProvider rng2 = 
RandomSource.SPLIT_MIX_64.create(0L);
+        final SharedStateDiscreteSampler sampler = 
DiscreteUniformSampler.of(rng2, lower, upper);
+        for (int i = 0; i < 10; i++) {
+            // Get the expected value by the rejection method
+            long expected;
+            do {
+                expected = rng1.nextInt();
+            } while (expected < lower || expected > upper);
+            Assert.assertEquals(expected, sampler.sample());
+        }
+    }
+
     @Test
     public void testOffsetSamplesWithNonPowerOf2Range() {
         assertOffsetSamples(257);
@@ -197,6 +267,8 @@ public class DiscreteUniformSamplerTest {
     @Test
     public void testSampleUniformityWithPowerOf2Range() {
         // Test using a RNG that outputs a counter of integers.
+        // The n most significant bits will be represented uniformly over a
+        // sequence that is a 2^n long.
         final UniformRandomProvider rng = new IntProvider() {
             private int bits = 0;
 
@@ -259,34 +331,22 @@ public class DiscreteUniformSamplerTest {
         Assert.assertEquals("Sample should be produced from 2nd value", 2, 
value[0]);
     }
 
-    /**
-     * Test the SharedStateSampler implementation.
-     */
     @Test
     public void testSharedStateSamplerWithSmallRange() {
         testSharedStateSampler(5, 67);
     }
 
-    /**
-     * Test the SharedStateSampler implementation.
-     */
     @Test
     public void testSharedStateSamplerWithLargeRange() {
         // Set the range so rejection below or above the threshold occurs with 
approximately p=0.25
         testSharedStateSampler(Integer.MIN_VALUE / 2 - 1, Integer.MAX_VALUE / 
2 + 1);
     }
 
-    /**
-     * Test the SharedStateSampler implementation.
-     */
     @Test
     public void testSharedStateSamplerWithPowerOf2Range() {
         testSharedStateSampler(0, 31);
     }
 
-    /**
-     * Test the SharedStateSampler implementation.
-     */
     @Test
     public void testSharedStateSamplerWithRangeOf1() {
         testSharedStateSampler(9, 9);
@@ -330,16 +390,15 @@ public class DiscreteUniformSamplerTest {
     }
 
     /**
-     * Test the toString method. This is added to ensure coverage as the 
factory constructor
-     * used in other tests does not create an instance of the wrapper class.
+     * Test the toString method contains the term "uniform". This is true of 
all samplers
+     * even for a fixed sample from a range of 1.
      *
      * @param lower Lower.
      * @param upper Upper.
      */
     private static void assertToString(int lower, int upper) {
         final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create(0L);
-        final DiscreteUniformSampler sampler =
-            new DiscreteUniformSampler(rng, lower, upper);
+        final DiscreteUniformSampler sampler = new DiscreteUniformSampler(rng, 
lower, upper);
         
Assert.assertTrue(sampler.toString().toLowerCase(Locale.US).contains("uniform"));
     }
 }
diff --git 
a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/UniformLongSamplerTest.java
 
b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/UniformLongSamplerTest.java
index 33ced6b..55b0578 100644
--- 
a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/UniformLongSamplerTest.java
+++ 
b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/UniformLongSamplerTest.java
@@ -55,7 +55,7 @@ public class UniformLongSamplerTest {
         final long lower = upper;
         final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();
         final UniformLongSampler sampler = UniformLongSampler.of(rng, lower, 
upper);
-        for (int i = 0; i < 10; i++) {
+        for (int i = 0; i < 5; i++) {
             Assert.assertEquals(lower, sampler.sample());
         }
     }

Reply via email to