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 2019/09/26 14:26:59 UTC

[commons-rng] branch master updated (bd4c225 -> 01b25d4)

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rng.git.


    from bd4c225  Updated user guide performance table.
     new d04c23f  RNG-117: RandomSource to support creating byte[] seed for implementing class.
     new 5b40af1  RNG-117: Update POM to allow test re-runs.
     new 98f105d  RNG-117: Track changes.
     new 01b25d4  RandomSource.create(RandomSource) can directly call ProviderBuilder.

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 commons-rng-simple/pom.xml                         | 12 +++
 .../apache/commons/rng/simple/RandomSource.java    | 49 ++++++++++-
 .../rng/simple/internal/NativeSeedType.java        | 59 ++++++++++++--
 .../rng/simple/internal/ProviderBuilder.java       | 88 ++++++++++++++++++--
 .../commons/rng/simple/internal/SeedFactory.java   | 55 +++++++++++++
 .../rng/simple/ProvidersCommonParametricTest.java  | 24 ++++++
 .../internal/NativeSeedTypeParametricTest.java     | 22 +++++
 ...pConverterTest.java => NativeSeedTypeTest.java} | 27 +++---
 .../RandomSourceInternalParametricTest.java        | 95 ++++++++++++++++++++++
 .../rng/simple/internal/SeedFactoryTest.java       | 59 ++++++++++++++
 src/changes/changes.xml                            |  6 +-
 11 files changed, 471 insertions(+), 25 deletions(-)
 copy commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/{NoOpConverterTest.java => NativeSeedTypeTest.java} (55%)


[commons-rng] 03/04: RNG-117: Track changes.

Posted by ah...@apache.org.
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 98f105dcec5498303088d891bb0d7bc44fa685e5
Author: aherbert <ah...@apache.org>
AuthorDate: Thu Sep 26 15:19:39 2019 +0100

    RNG-117: Track changes.
---
 src/changes/changes.xml | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2e28cac..35fbc54 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -75,7 +75,11 @@ re-run tests that fail, and pass the build if they succeed
 within the allotted number of reruns (the test will be marked
 as 'flaky' in the report).
 ">
-      <action dev="aherbert" type="update" issue="RNG-116">
+      <action dev="aherbert" type="add" issue="RNG-117">
+        "RandomSource": Support creating a byte[] seed suitable for the implementing
+        generator class.
+      </action>
+      <action dev="aherbert" type="add" issue="RNG-116">
         "RandomSource": Expose interfaces supported by the implementing generator class
         with methods isJumpable() and isLongJumpable().
       </action>


[commons-rng] 04/04: RandomSource.create(RandomSource) can directly call ProviderBuilder.

Posted by ah...@apache.org.
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 01b25d4d0a552bbe6a0e7b877f8b15db2585b6ed
Author: aherbert <ah...@apache.org>
AuthorDate: Thu Sep 26 15:26:55 2019 +0100

    RandomSource.create(RandomSource) can directly call ProviderBuilder.
    
    Added a test of the generator produced for this code execution path.
---
 .../main/java/org/apache/commons/rng/simple/RandomSource.java    | 2 +-
 .../apache/commons/rng/simple/ProvidersCommonParametricTest.java | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

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 e8048a4..b98f060 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
@@ -616,7 +616,7 @@ public enum RandomSource {
      * @see #create(RandomSource,Object,Object[])
      */
     public static RestorableUniformRandomProvider create(RandomSource source) {
-        return create(source, null);
+        return ProviderBuilder.create(source.getInternalIdentifier());
     }
 
     /**
diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/ProvidersCommonParametricTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/ProvidersCommonParametricTest.java
index aa403da..fd7da1c 100644
--- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/ProvidersCommonParametricTest.java
+++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/ProvidersCommonParametricTest.java
@@ -115,6 +115,15 @@ public class ProvidersCommonParametricTest {
     }
 
     @Test
+    public void testNullSeed() {
+        // Note: This is the only test that explicitly calls create() with no other arguments.
+        final UniformRandomProvider rng = originalArgs == null ?
+            RandomSource.create(originalSource) :
+            RandomSource.create(originalSource, null, originalArgs);
+        checkNextIntegerInRange(rng, 10, 10000);
+    }
+
+    @Test
     public void testEmptyIntArraySeed() {
         final int[] empty = new int[0];
         Assume.assumeTrue(originalSource.isNativeSeed(empty));


[commons-rng] 01/04: RNG-117: RandomSource to support creating byte[] seed for implementing class.

Posted by ah...@apache.org.
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 d04c23fb7c506b63ec95610268e2c84af10a93e2
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Mon Sep 23 20:55:08 2019 +0100

    RNG-117: RandomSource to support creating byte[] seed for implementing class.
    
    Added methods:
    
    public byte[] createSeed();
    public byte[] createSeed(UniformRandomProvider);
---
 .../apache/commons/rng/simple/RandomSource.java    | 47 +++++++++++
 .../rng/simple/internal/NativeSeedType.java        | 59 ++++++++++++--
 .../rng/simple/internal/ProviderBuilder.java       | 88 ++++++++++++++++++--
 .../commons/rng/simple/internal/SeedFactory.java   | 55 +++++++++++++
 .../rng/simple/ProvidersCommonParametricTest.java  | 15 ++++
 .../internal/NativeSeedTypeParametricTest.java     | 22 +++++
 .../rng/simple/internal/NativeSeedTypeTest.java    | 44 ++++++++++
 .../RandomSourceInternalParametricTest.java        | 95 ++++++++++++++++++++++
 .../rng/simple/internal/SeedFactoryTest.java       | 59 ++++++++++++++
 9 files changed, 472 insertions(+), 12 deletions(-)

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 62b83b3..e8048a4 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
@@ -490,6 +490,53 @@ public enum RandomSource {
     }
 
     /**
+     * Creates a seed suitable for the implementing class represented by this random source.
+     *
+     * <p>The seed will be created as if passing a {@code null} seed to the factory method
+     * {@link #create(RandomSource, Object, Object...)}. It will satisfy the seed size and any
+     * other seed requirements for the implementing class. The seed is converted from the native
+     * type to a byte representation.</p>
+     *
+     * <p>Usage example:</p>
+     * <pre><code>
+     *  RandomSource source = ...;
+     *  byte[] seed = source.createSeed();
+     *  UniformRandomProvider rng = RandomSource.create(source, seed);
+     * </code></pre>
+     *
+     * @return the seed
+     *
+     * @since 1.3
+     */
+    public byte[] createSeed() {
+        return internalIdentifier.createSeedBytes();
+    }
+
+    /**
+     * Creates a seed suitable for the implementing class represented by this random source
+     * using the supplied source of randomness.
+     *
+     * <p>The seed will satisfy the seed size and any other seed requirements for the
+     * implementing class.</p>
+     *
+     * <p>Usage example:</p>
+     * <pre><code>
+     *  RandomSource source = ...;
+     *  UniformRandomProvider seedRng = new JDKRandomWrapper(new SecureRandom());
+     *  byte[] seed = source.createSeed(seedRng);
+     *  UniformRandomProvider rng = RandomSource.create(source, seed);
+     * </code></pre>
+     *
+     * @param rng Source of randomness.
+     * @return the seed
+     *
+     * @since 1.3
+     */
+    public byte[] createSeed(UniformRandomProvider rng) {
+        return internalIdentifier.createSeedBytes(rng);
+    }
+
+    /**
      * Checks whether the implementing class represented by this random source
      * supports the {@link org.apache.commons.rng.JumpableUniformRandomProvider
      * JumpableUniformRandomProvider} interface. If {@code true} the instance returned
diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/NativeSeedType.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/NativeSeedType.java
index 1db19ee..0fe9da5 100644
--- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/NativeSeedType.java
+++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/NativeSeedType.java
@@ -16,6 +16,8 @@
  */
 package org.apache.commons.rng.simple.internal;
 
+import org.apache.commons.rng.core.util.NumberFactory;
+
 /**
  * The native seed type. Contains values for all native seed types and methods
  * to convert supported seed types to the native seed type.
@@ -41,7 +43,7 @@ package org.apache.commons.rng.simple.internal;
  */
 public enum NativeSeedType {
     /** The seed type is {@code Integer}. */
-    INT(Integer.class) {
+    INT(Integer.class, 4) {
         @Override
         public Integer createSeed(int size) {
             return SeedFactory.createInt();
@@ -68,7 +70,7 @@ public enum NativeSeedType {
         }
     },
     /** The seed type is {@code Long}. */
-    LONG(Long.class) {
+    LONG(Long.class, 8) {
         @Override
         public Long createSeed(int size) {
             return SeedFactory.createLong();
@@ -95,7 +97,7 @@ public enum NativeSeedType {
         }
     },
     /** The seed type is {@code int[]}. */
-    INT_ARRAY(int[].class) {
+    INT_ARRAY(int[].class, 4) {
         @Override
         public int[] createSeed(int size) {
             // Limit the number of calls to the synchronized method. The generator
@@ -124,7 +126,7 @@ public enum NativeSeedType {
         }
     },
     /** The seed type is {@code long[]}. */
-    LONG_ARRAY(long[].class) {
+    LONG_ARRAY(long[].class, 8) {
         @Override
         public long[] createSeed(int size) {
             // Limit the number of calls to the synchronized method. The generator
@@ -153,6 +155,8 @@ public enum NativeSeedType {
         }
     };
 
+    /** Error message for unrecognised seed types. */
+    private static final String UNRECOGNISED_SEED = "Unrecognized seed type: ";
     /** Maximum length of the seed array (for creating array seeds). */
     private static final int RANDOM_SEED_ARRAY_SIZE = 128;
     /** Convert {@code Long} to {@code Integer}. */
@@ -180,10 +184,20 @@ public enum NativeSeedType {
     private final Class<?> type;
 
     /**
+     * Define the number of bytes required to represent the native seed. If the type is
+     * an array then this represents the size of a single value of the type.
+     */
+    private final int bytes;
+
+    /**
+     * Instantiates a new native seed type.
+     *
      * @param type Define the class type of the native seed.
+     * @param bytes Define the number of bytes required to represent the native seed.
      */
-    NativeSeedType(Class<?> type) {
+    NativeSeedType(Class<?> type, int bytes) {
         this.type = type;
+        this.bytes = bytes;
     }
 
     /**
@@ -196,6 +210,16 @@ public enum NativeSeedType {
     }
 
     /**
+     * Gets the number of bytes required to represent the native seed type. If the type is
+     * an array then this represents the size of a single value of the type.
+     *
+     * @return the number of bytes
+     */
+    public int getBytes() {
+        return bytes;
+    }
+
+    /**
      * Creates the seed. The output seed type is determined by the native seed type. If the
      * output is an array the required size of the array can be specified.
      *
@@ -230,7 +254,7 @@ public enum NativeSeedType {
             return convert((byte[]) seed, size);
         }
 
-        throw new UnsupportedOperationException("Unrecognized seed type: " + seed);
+        throw new UnsupportedOperationException(UNRECOGNISED_SEED + seed);
     }
 
     /**
@@ -277,4 +301,27 @@ public enum NativeSeedType {
      * @return the native seed.
      */
     protected abstract Object convert(byte[] seed, int size);
+
+    /**
+     * Converts the input seed from any of the supported seed types to bytes.
+     *
+     * @param seed Input seed.
+     * @return the seed bytes.
+     * @throws UnsupportedOperationException if the {@code seed} type is invalid.
+     */
+    public static byte[] convertSeedToBytes(Object seed) {
+        if (seed instanceof Integer) {
+            return NumberFactory.makeByteArray((Integer) seed);
+        } else if (seed instanceof Long) {
+            return NumberFactory.makeByteArray((Long) seed);
+        } else if (seed instanceof int[]) {
+            return NumberFactory.makeByteArray((int[]) seed);
+        } else if (seed instanceof long[]) {
+            return NumberFactory.makeByteArray((long[]) seed);
+        } else if (seed instanceof byte[]) {
+            return (byte[]) seed;
+        }
+
+        throw new UnsupportedOperationException(UNRECOGNISED_SEED + seed);
+    }
 }
diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
index 59b9271..02af9fc 100644
--- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
+++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
@@ -272,20 +272,35 @@ public final class ProviderBuilder {
                 return super.convertSeed(seed);
             }
 
+            @Override
+            protected byte[] createByteArraySeed(UniformRandomProvider source) {
+                return NativeSeedType.convertSeedToBytes(createMswsSeed(source));
+            }
+
+            /**
+             * Creates the full length seed array from the input seed.
+             *
+             * @param seed the seed
+             * @return the seed array
+             */
+            private long[] createMswsSeed(long seed) {
+                return createMswsSeed(new SplitMix64(seed));
+            }
+
             /**
              * Creates the full length seed array from the input seed using the method
              * recommended for the generator. This is a high quality Weyl increment composed
              * of a hex character permutation.
              *
-             * @param seed the seed
+             * @param source Source of randomness.
              * @return the seed array
              */
-            private long[] createMswsSeed(long seed) {
-                final long increment = SeedUtils.createLongHexPermutation(new SplitMix64(seed));
+            private long[] createMswsSeed(UniformRandomProvider source) {
+                final long increment = SeedUtils.createLongHexPermutation(source);
                 // The initial state should not be low complexity but the Weyl
                 // state can be any number.
                 final long state = increment;
-                final long weylState = seed;
+                final long weylState = source.nextLong();
                 return new long[] {state, weylState, increment};
             }
         },
@@ -383,6 +398,16 @@ public final class ProviderBuilder {
         }
 
         /**
+         * Gets the number of seed bytes required to seed the implementing class represented by
+         * this random source.
+         *
+         * @return the number of seed bytes
+         */
+        private int getSeedByteSize() {
+            return nativeSeedSize * nativeSeedType.getBytes();
+        }
+
+        /**
          * Creates a RNG instance.
          *
          * <p>This method can be over-ridden to allow fast construction of a generator
@@ -442,8 +467,10 @@ public final class ProviderBuilder {
         /**
          * Creates a native seed.
          *
-         * <p>This method should be over-ridden to satisfy seed requirements for the generator,
-         * for example if a seed must contain non-zero bits.</p>
+         * <p>The default implementation creates a seed of the native type and, for array seeds,
+         * ensures not all bits are zero.</p>
+         *
+         * <p>This method should be over-ridden to satisfy seed requirements for the generator.</p>
          *
          * @return the native seed
          */
@@ -452,6 +479,21 @@ public final class ProviderBuilder {
         }
 
         /**
+         * Creates a {@code byte[]} seed using the provided source of randomness.
+         *
+         * <p>The default implementation creates a full-length seed and ensures not all bits
+         * are zero.</p>
+         *
+         * <p>This method should be over-ridden to satisfy seed requirements for the generator.</p>
+         *
+         * @param source Source of randomness.
+         * @return the byte[] seed
+         */
+        protected byte[] createByteArraySeed(UniformRandomProvider source) {
+            return SeedFactory.createByteArray(source, getSeedByteSize());
+        }
+
+        /**
          * Converts a seed from any of the supported seed types to a native seed.
          *
          * @param seed Input seed (must not be null).
@@ -476,6 +518,39 @@ public final class ProviderBuilder {
         }
 
         /**
+         * Creates a seed suitable for the implementing class represented by this random source.
+         *
+         * <p>It will satisfy the seed size and any other seed requirements for the
+         * implementing class. The seed is converted from the native type to bytes.</p>
+         *
+         * @return the seed bytes
+         *
+         * @since 1.3
+         */
+        public final byte[] createSeedBytes() {
+            // Custom implementations can override createSeed
+            final Object seed = createSeed();
+            return NativeSeedType.convertSeedToBytes(seed);
+        }
+
+        /**
+         * Creates a seed suitable for the implementing class represented by this random source
+         * using the supplied source of randomness.
+         *
+         * <p>It will satisfy the seed size and any other seed requirements for the
+         * implementing class. The seed is converted from the native type to bytes.</p>
+         *
+         * @param source Source of randomness.
+         * @return the seed bytes
+         *
+         * @since 1.3
+         */
+        public final byte[] createSeedBytes(UniformRandomProvider source) {
+            // Custom implementations can override createByteArraySeed
+            return createByteArraySeed(source);
+        }
+
+        /**
          * Gets the constructor.
          *
          * @return the RNG constructor.
@@ -492,6 +567,7 @@ public final class ProviderBuilder {
             }
             return constructor;
         }
+
         /**
          * Creates a constructor.
          *
diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/SeedFactory.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/SeedFactory.java
index 170c978..4b336ea 100644
--- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/SeedFactory.java
+++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/SeedFactory.java
@@ -203,6 +203,25 @@ public final class SeedFactory {
     }
 
     /**
+     * Creates an array of {@code byte} numbers for use as a seed using the supplied source of
+     * randomness. The result will not be all zeros.
+     *
+     * @param source Source of randomness.
+     * @param n Size of the array to create.
+     * @return an array of {@code n} random numbers.
+     */
+    static byte[] createByteArray(UniformRandomProvider source,
+                                  int n) {
+        final byte[] seed = new byte[n];
+        source.nextBytes(seed);
+        // If the seed is zero it is assumed the input source RNG is either broken
+        // or the seed is small and it was zero by chance. Revert to the built-in
+        // source of randomness to ensure it is non-zero.
+        ensureNonZero(seed);
+        return seed;
+    }
+
+    /**
      * Ensure the seed is non-zero at the first position in the array.
      *
      * <p>This method will replace a zero at index 0 in the array with
@@ -243,6 +262,42 @@ public final class SeedFactory {
     }
 
     /**
+     * Ensure the seed is not zero at all positions in the array.
+     *
+     * <p>This method will check all positions in the array and if all
+     * are zero it will replace index 0 in the array with
+     * a non-zero random number. The method ensures any length seed
+     * contains non-zero bits. The output seed is suitable for generators
+     * that cannot be seeded with all zeros.</p>
+     *
+     * @param seed Seed array (modified in place).
+     * @see #createInt()
+     */
+    private static void ensureNonZero(byte[] seed) {
+        // Since zero occurs 1 in 2^8 for a single byte this checks the entire array for zeros.
+        if (seed.length != 0 && isAllZero(seed)) {
+            do {
+                seed[0] = (byte) createInt();
+            } while (seed[0] == 0);
+        }
+    }
+
+    /**
+     * Test if each position in the array is zero.
+     *
+     * @param array Array data.
+     * @return true if all position are zero
+     */
+    private static boolean isAllZero(byte[] array) {
+        for (final byte value : array) {
+            if (value != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Ensure the value is non-zero.
      *
      * <p>This method will replace a zero with a non-zero random number from the random source.</p>
diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/ProvidersCommonParametricTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/ProvidersCommonParametricTest.java
index 4641937..aa403da 100644
--- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/ProvidersCommonParametricTest.java
+++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/ProvidersCommonParametricTest.java
@@ -39,6 +39,7 @@ import org.apache.commons.rng.LongJumpableUniformRandomProvider;
 import org.apache.commons.rng.RandomProviderState;
 import org.apache.commons.rng.RestorableUniformRandomProvider;
 import org.apache.commons.rng.core.RandomProviderDefaultState;
+import org.apache.commons.rng.core.source64.SplitMix64;
 
 /**
  * Tests which all generators must pass.
@@ -155,6 +156,20 @@ public class ProvidersCommonParametricTest {
         checkNextIntegerInRange(rng, 10, 10000);
     }
 
+    @Test
+    public void testRandomSourceCreateSeed() {
+        final byte[] seed = originalSource.createSeed();
+        final UniformRandomProvider rng = RandomSource.create(originalSource, seed, originalArgs);
+        checkNextIntegerInRange(rng, 10, 10000);
+    }
+
+    @Test
+    public void testRandomSourceCreateSeedFromRNG() {
+        final byte[] seed = originalSource.createSeed(new SplitMix64(RandomSource.createLong()));
+        final UniformRandomProvider rng = RandomSource.create(originalSource, seed, originalArgs);
+        checkNextIntegerInRange(rng, 10, 10000);
+    }
+
     // State save and restore tests.
 
     @Test
diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeParametricTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeParametricTest.java
index 9a2ebf3..3913341 100644
--- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeParametricTest.java
+++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeParametricTest.java
@@ -112,6 +112,28 @@ public class NativeSeedTypeParametricTest {
     }
 
     /**
+     * Test the seed can be created, converted to a byte[] and then back to the native type.
+     */
+    @Test
+    public void testConvertSeedToBytes() {
+        final int size = 3;
+        final Object seed = nativeSeedType.createSeed(size);
+        Assert.assertNotNull("Null seed", seed);
+
+        final byte[] bytes = NativeSeedType.convertSeedToBytes(seed);
+        Assert.assertNotNull("Null byte[] seed", bytes);
+
+        final Object seed2 = nativeSeedType.convertSeed(bytes, size);
+        if (type.isArray()) {
+            // This handles nested primitive arrays
+            Assert.assertArrayEquals("byte[] seed was not converted back",
+                                     new Object[] {seed}, new Object[] {seed2});
+        } else {
+            Assert.assertEquals("byte[] seed was not converted back", seed, seed2);
+        }
+    }
+
+    /**
      * Test the seed can be converted to the correct type from any of the supported input types.
      */
     @Test
diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeTest.java
new file mode 100644
index 0000000..be699ae
--- /dev/null
+++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rng.simple.internal;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link NativeSeedType} factory seed conversions.
+ */
+public class NativeSeedTypeTest {
+    /**
+     * Test the conversion throws for an unsupported type. All supported types are
+     * tested in the {@link NativeSeedTypeParametricTest}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testConvertSeedToBytesUsingNullThrows() {
+        NativeSeedType.convertSeedToBytes(null);
+    }
+
+    /**
+     * Test the conversion passes through a byte[]. This hits the edge case of a seed
+     * that can be converted that is not a native type.
+     */
+    @Test
+    public void testConvertSeedToBytesUsingByteArray() {
+        final byte[] seed = {42, 78, 99};
+        Assert.assertSame(seed, NativeSeedType.convertSeedToBytes(seed));
+    }
+}
diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java
index 69736f2..e58f4ff 100644
--- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java
+++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.rng.simple.internal;
 
+import org.apache.commons.rng.core.source64.SplitMix64;
 import org.apache.commons.rng.simple.internal.ProviderBuilder.RandomSourceInternal;
 import org.junit.Assert;
 import org.junit.Test;
@@ -23,6 +24,8 @@ import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
+import java.util.EnumMap;
+
 /**
  * Tests for the {@link ProviderBuilder.RandomSourceInternal} seed conversions. This test
  * ensures that all random sources can create a seed or convert any supported seed to the
@@ -43,6 +46,53 @@ public class RandomSourceInternalParametricTest {
         null,
         Double.valueOf(Math.PI),
     };
+    /** The expected byte size of the seed for each RandomSource. */
+    private static final EnumMap<RandomSourceInternal, Integer> EXPECTED_SEED_BYTES =
+            new EnumMap<RandomSourceInternal, Integer>(RandomSourceInternal.class);
+
+    static {
+        final int intBytes = 4;
+        final int longBytes = 8;
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.JDK, longBytes * 1);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_512_A, intBytes * 16);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_1024_A, intBytes * 32);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_19937_A, intBytes * 624);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_19937_C, intBytes * 624);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_44497_A, intBytes * 1391);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_44497_B, intBytes * 1391);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.MT, intBytes * 624);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.ISAAC, intBytes * 256);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.SPLIT_MIX_64, longBytes * 1);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XOR_SHIFT_1024_S, longBytes * 16);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.TWO_CMRES, intBytes * 1);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.TWO_CMRES_SELECT, intBytes * 1);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.MT_64, longBytes * 312);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.MWC_256, intBytes * 257);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.KISS, intBytes * 4);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XOR_SHIFT_1024_S_PHI, longBytes * 16);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_64_S, intBytes * 2);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_64_SS, intBytes * 2);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_128_PLUS, intBytes * 4);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_128_SS, intBytes * 4);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_128_PLUS, longBytes * 2);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_128_SS, longBytes * 2);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_256_PLUS, longBytes * 4);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_256_SS, longBytes * 4);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_512_PLUS, longBytes * 8);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_512_SS, longBytes * 8);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_XSH_RR_32, longBytes * 2);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_XSH_RS_32, longBytes * 2);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_RXS_M_XS_64, longBytes * 2);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_MCG_XSH_RR_32, longBytes * 1);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_MCG_XSH_RS_32, longBytes * 1);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.MSWS, longBytes * 3);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.SFC_32, intBytes * 3);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.SFC_64, longBytes * 3);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.JSF_32, intBytes * 1);
+        EXPECTED_SEED_BYTES.put(RandomSourceInternal.JSF_64, longBytes * 1);
+        // ... add more here.
+        // Verify the seed byte size is reflected in the enum javadoc for RandomSource.
+    }
 
     /** Internal identifier for the random source. */
     private final RandomSourceInternal randomSourceInternal;
@@ -109,4 +159,49 @@ public class RandomSourceInternalParametricTest {
             }
         }
     }
+
+    /**
+     * Test the seed byte size is reported as the size of a int/long primitive for Int/Long
+     * seed types and a multiple of it for int[]/long[] types.
+     */
+    @Test
+    public void testCreateSeedBytesSizeIsPositiveAndMultipleOf4Or8() {
+        // This should be the full length seed
+        final byte[] seed = randomSourceInternal.createSeedBytes(new SplitMix64(12345L));
+
+        final int size = seed.length;
+        Assert.assertNotEquals("Seed is empty", 0, size);
+
+        if (randomSourceInternal.isNativeSeed(Integer.valueOf(0))) {
+            Assert.assertEquals("Expect 4 bytes for Integer", 4, size);
+        } else if (randomSourceInternal.isNativeSeed(Long.valueOf(0))) {
+            Assert.assertEquals("Expect 8 bytes for Long", 8, size);
+        } else if (randomSourceInternal.isNativeSeed(new int[0])) {
+            Assert.assertEquals("Expect 4n bytes for int[]", 0, size % 4);
+        } else if (randomSourceInternal.isNativeSeed(new long[0])) {
+            Assert.assertEquals("Expect 8n bytes for long[]", 0, size % 8);
+        } else {
+            Assert.fail("Unknown native seed type");
+        }
+    }
+
+    /**
+     * Test the seed byte size against the expected value.
+     *
+     * <p>The expected values are maintained in a table and must be manually updated
+     * for new generators. This test forms an additional cross-reference check that the
+     * seed size in RandomSourceInternal has been correctly set and the size should map to
+     * the array size in the RandomSource javadoc (if applicable).
+     */
+    @Test
+    public void testCreateSeedBytes() {
+        // This should be the full length seed
+        final byte[] seed = randomSourceInternal.createSeedBytes(new SplitMix64(12345L));
+        final int size = seed.length;
+
+        final Integer expected = EXPECTED_SEED_BYTES.get(randomSourceInternal);
+        Assert.assertNotNull("Missing expected seed byte size: " + randomSourceInternal, expected);
+        Assert.assertEquals(randomSourceInternal.toString(),
+                            expected.intValue(), size);
+    }
 }
diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/SeedFactoryTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/SeedFactoryTest.java
index 842bdaa..b936d81 100644
--- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/SeedFactoryTest.java
+++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/SeedFactoryTest.java
@@ -20,6 +20,8 @@ import java.util.Map;
 import java.util.HashMap;
 import org.junit.Assert;
 import org.junit.Test;
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.core.source32.IntProvider;
 import org.apache.commons.rng.core.source64.RandomLongSource;
 import org.apache.commons.rng.core.util.NumberFactory;
 
@@ -206,6 +208,63 @@ public class SeedFactoryTest {
     }
 
     @Test
+    public void testCreateByteArrayWithSizeZero() {
+        assertCreateByteArray(new byte[0]);
+    }
+
+    @Test
+    public void testCreateByteArrayIgnoresNonZeroPositions() {
+        final byte position = 123;
+        int n = 3;
+        for (int i = 0; i < n; i++) {
+            final byte[] expected = new byte[n];
+            expected[i] = position;
+            assertCreateByteArray(expected);
+        }
+    }
+
+    /**
+     * Assert that the SeedFactory uses the bytes exactly as generated by the
+     * {@link UniformRandomProvider#nextBytes(byte[])} method (assuming they are not all zero).
+     *
+     * @param expected the expected
+     */
+    private static void assertCreateByteArray(final byte[] expected) {
+        final UniformRandomProvider rng = new IntProvider() {
+            @Override
+            public int next() {
+                Assert.fail("This method should not be used");
+                return 0;
+            }
+
+            @Override
+            public void nextBytes(byte[] bytes) {
+                System.arraycopy(expected, 0, bytes, 0, Math.min(expected.length, bytes.length));
+            }
+        };
+
+        final byte[] seed = SeedFactory.createByteArray(rng, expected.length);
+        Assert.assertArrayEquals(expected, seed);
+    }
+
+    @Test
+    public void testCreateByteArrayWithAllZeroBytesUpdatesPosition0() {
+        final UniformRandomProvider rng = new IntProvider() {
+            @Override
+            public int next() {
+                // Deliberately produce zero
+                return 0;
+            }
+        };
+        // Test the method only replaces position 0
+        final byte[] seed = SeedFactory.createByteArray(rng, 4);
+        Assert.assertNotEquals("Zero at position 0 should be modified", 0, seed[0]);
+        for (int i = 1; i < seed.length; i++) {
+            Assert.assertEquals("Position above 0 should be unmodified", 0, seed[i]);
+        }
+    }
+
+    @Test
     public void testEnsureNonZeroIntArrayIgnoresEmptySeed() {
         final int[] seed = new int[0];
         SeedFactory.ensureNonZero(seed);


[commons-rng] 02/04: RNG-117: Update POM to allow test re-runs.

Posted by ah...@apache.org.
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 5b40af1e642667d0b121604b09901583420ee9b9
Author: aherbert <ah...@apache.org>
AuthorDate: Wed Sep 25 11:35:00 2019 +0100

    RNG-117: Update POM to allow test re-runs.
    
    The tests for creating a generator from a random seed may spuriously
    fail.
---
 commons-rng-simple/pom.xml | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/commons-rng-simple/pom.xml b/commons-rng-simple/pom.xml
index 116c7fc..a9526e0 100644
--- a/commons-rng-simple/pom.xml
+++ b/commons-rng-simple/pom.xml
@@ -59,4 +59,16 @@
     </dependency>
   </dependencies>
 
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <!-- The test for creating a generator from a random seed may spuriously fail. -->
+          <rerunFailingTestsCount>1</rerunFailingTestsCount>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
 </project>