SeedFactory.java

  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. package org.apache.commons.rng.simple.internal;

  18. import java.security.SecureRandom;
  19. import java.util.concurrent.locks.ReentrantLock;

  20. import org.apache.commons.rng.core.util.NumberFactory;
  21. import org.apache.commons.rng.UniformRandomProvider;
  22. import org.apache.commons.rng.core.source64.RandomLongSource;
  23. import org.apache.commons.rng.core.source64.SplitMix64;
  24. import org.apache.commons.rng.core.source64.XoRoShiRo1024PlusPlus;

  25. /**
  26.  * Utilities related to seeding.
  27.  *
  28.  * <p>
  29.  * This class provides methods to generate random seeds (single values
  30.  * or arrays of values, of {@code int} or {@code long} types) that can
  31.  * be passed to the {@link org.apache.commons.rng.simple.RandomSource
  32.  * methods that create a generator instance}.
  33.  * <br>
  34.  * Although the seed-generating methods defined in this class will likely
  35.  * return different values for all calls, there is no guarantee that the
  36.  * produced seed will result always in a "good" sequence of numbers (even
  37.  * if the generator initialized with that seed is good).
  38.  * <br>
  39.  * There is <i>no guarantee</i> that sequences will not overlap.
  40.  * </p>
  41.  *
  42.  * @since 1.0
  43.  */
  44. public final class SeedFactory {
  45.     /** Size of the state array of "XoRoShiRo1024PlusPlus". */
  46.     private static final int XO_RO_SHI_RO_1024_STATE_SIZE = 16;
  47.     /** Size of block to fill in an {@code int[]} seed per synchronized operation. */
  48.     private static final int INT_ARRAY_BLOCK_SIZE = 8;
  49.     /** Size of block to fill in a {@code long[]} seed per synchronized operation. */
  50.     private static final int LONG_ARRAY_BLOCK_SIZE = 4;

  51.     /**
  52.      * The lock to own when using the seed generator. This lock is unfair and there is no
  53.      * particular access order for waiting threads.
  54.      *
  55.      * <p>This is used as an alternative to {@code synchronized} statements to guard access
  56.      * to the seed generator.</p>
  57.      */
  58.     private static final ReentrantLock LOCK = new ReentrantLock(false);

  59.     /** Generator with a long period. */
  60.     private static final UniformRandomProvider SEED_GENERATOR;

  61.     static {
  62.         // Use a secure RNG so that different instances (e.g. in multiple JVM
  63.         // instances started in rapid succession) will have different seeds.
  64.         final SecureRandom seedGen = new SecureRandom();
  65.         final byte[] bytes = new byte[8 * XO_RO_SHI_RO_1024_STATE_SIZE];
  66.         seedGen.nextBytes(bytes);
  67.         final long[] seed = NumberFactory.makeLongArray(bytes);
  68.         // The XoRoShiRo1024PlusPlus generator cannot recover from an all zero seed and
  69.         // will produce low quality initial output if initialized with some zeros.
  70.         // Ensure it is non zero at all array positions using a SplitMix64
  71.         // generator (this is insensitive to a zero seed so can use the first seed value).
  72.         final SplitMix64 rng = new SplitMix64(seed[0]);
  73.         for (int i = 0; i < seed.length; i++) {
  74.             seed[i] = ensureNonZero(rng, seed[i]);
  75.         }

  76.         SEED_GENERATOR = new XoRoShiRo1024PlusPlus(seed);
  77.     }

  78.     /**
  79.      * Class contains only static methods.
  80.      */
  81.     private SeedFactory() {}

  82.     /**
  83.      * Creates an {@code int} number for use as a seed.
  84.      *
  85.      * @return a random number.
  86.      */
  87.     public static int createInt() {
  88.         LOCK.lock();
  89.         try {
  90.             return SEED_GENERATOR.nextInt();
  91.         } finally {
  92.             LOCK.unlock();
  93.         }
  94.     }

  95.     /**
  96.      * Creates a {@code long} number for use as a seed.
  97.      *
  98.      * @return a random number.
  99.      */
  100.     public static long createLong() {
  101.         LOCK.lock();
  102.         try {
  103.             return SEED_GENERATOR.nextLong();
  104.         } finally {
  105.             LOCK.unlock();
  106.         }
  107.     }

  108.     /**
  109.      * Creates an array of {@code int} numbers for use as a seed.
  110.      *
  111.      * @param n Size of the array to create.
  112.      * @return an array of {@code n} random numbers.
  113.      */
  114.     public static int[] createIntArray(int n) {
  115.         // Behaviour from v1.3 is to ensure the first position is non-zero
  116.         return createIntArray(n, 0, Math.min(n, 1));
  117.     }

  118.     /**
  119.      * Creates an array of {@code int} numbers for use as a seed.
  120.      * Optionally ensure a sub-range of the array is not all-zero.
  121.      *
  122.      * <p>This method is package-private for use by {@link NativeSeedType}.
  123.      *
  124.      * @param n Size of the array to create.
  125.      * @param from The start of the not all-zero sub-range (inclusive).
  126.      * @param to The end of the not all-zero sub-range (exclusive).
  127.      * @return an array of {@code n} random numbers.
  128.      * @throws IndexOutOfBoundsException if the sub-range is invalid
  129.      * @since 1.5
  130.      */
  131.     static int[] createIntArray(int n, int from, int to) {
  132.         final int[] seed = new int[n];
  133.         // Compute the size that can be filled with complete blocks
  134.         final int blockSize = INT_ARRAY_BLOCK_SIZE * (n / INT_ARRAY_BLOCK_SIZE);
  135.         int i = 0;
  136.         while (i < blockSize) {
  137.             final int end = i + INT_ARRAY_BLOCK_SIZE;
  138.             fillIntArray(seed, i, end);
  139.             i = end;
  140.         }
  141.         // Final fill only if required
  142.         if (i != n) {
  143.             fillIntArray(seed, i, n);
  144.         }
  145.         ensureNonZero(seed, from, to);
  146.         return seed;
  147.     }

  148.     /**
  149.      * Creates an array of {@code long} numbers for use as a seed.
  150.      *
  151.      * @param n Size of the array to create.
  152.      * @return an array of {@code n} random numbers.
  153.      */
  154.     public static long[] createLongArray(int n) {
  155.         // Behaviour from v1.3 is to ensure the first position is non-zero
  156.         return createLongArray(n, 0, Math.min(n, 1));
  157.     }

  158.     /**
  159.      * Creates an array of {@code long} numbers for use as a seed.
  160.      * Optionally ensure a sub-range of the array is not all-zero.
  161.      *
  162.      * <p>This method is package-private for use by {@link NativeSeedType}.
  163.      *
  164.      * @param n Size of the array to create.
  165.      * @param from The start of the not all-zero sub-range (inclusive).
  166.      * @param to The end of the not all-zero sub-range (exclusive).
  167.      * @return an array of {@code n} random numbers.
  168.      * @throws IndexOutOfBoundsException if the sub-range is invalid
  169.      * @since 1.5
  170.      */
  171.     static long[] createLongArray(int n, int from, int to) {
  172.         final long[] seed = new long[n];
  173.         // Compute the size that can be filled with complete blocks
  174.         final int blockSize = LONG_ARRAY_BLOCK_SIZE * (n / LONG_ARRAY_BLOCK_SIZE);
  175.         int i = 0;
  176.         while (i < blockSize) {
  177.             final int end = i + LONG_ARRAY_BLOCK_SIZE;
  178.             fillLongArray(seed, i, end);
  179.             i = end;
  180.         }
  181.         // Final fill only if required
  182.         if (i != n) {
  183.             fillLongArray(seed, i, n);
  184.         }
  185.         ensureNonZero(seed, from, to);
  186.         return seed;
  187.     }

  188.     /**
  189.      * Fill the array between {@code start} inclusive and {@code end} exclusive from the
  190.      * seed generator. The lock is used to guard access to the generator.
  191.      *
  192.      * @param array Array data.
  193.      * @param start Start (inclusive).
  194.      * @param end End (exclusive).
  195.      */
  196.     private static void fillIntArray(int[] array, int start, int end) {
  197.         LOCK.lock();
  198.         try {
  199.             for (int i = start; i < end; i++) {
  200.                 array[i] = SEED_GENERATOR.nextInt();
  201.             }
  202.         } finally {
  203.             LOCK.unlock();
  204.         }
  205.     }

  206.     /**
  207.      * Fill the array between {@code start} inclusive and {@code end} exclusive from the
  208.      * seed generator. The lock is used to guard access to the generator.
  209.      *
  210.      * @param array Array data.
  211.      * @param start Start (inclusive).
  212.      * @param end End (exclusive).
  213.      */
  214.     private static void fillLongArray(long[] array, int start, int end) {
  215.         LOCK.lock();
  216.         try {
  217.             for (int i = start; i < end; i++) {
  218.                 array[i] = SEED_GENERATOR.nextLong();
  219.             }
  220.         } finally {
  221.             LOCK.unlock();
  222.         }
  223.     }

  224.     /**
  225.      * Creates an array of {@code byte} numbers for use as a seed using the supplied source of
  226.      * randomness. A sub-range can be specified that must not contain all zeros.
  227.      *
  228.      * @param source Source of randomness.
  229.      * @param n Size of the array to create.
  230.      * @param from The start of the not all-zero sub-range (inclusive).
  231.      * @param to The end of the not all-zero sub-range (exclusive).
  232.      * @return an array of {@code n} random numbers.
  233.      */
  234.     static byte[] createByteArray(UniformRandomProvider source,
  235.                                   int n,
  236.                                   int from,
  237.                                   int to) {
  238.         final byte[] seed = new byte[n];
  239.         source.nextBytes(seed);
  240.         ensureNonZero(seed, from, to, source);
  241.         return seed;
  242.     }

  243.     /**
  244.      * Ensure the seed is not all-zero within the specified sub-range.
  245.      *
  246.      * <p>This method will check the sub-range and if all are zero it will fill the range
  247.      * with a random sequence seeded from the default source of random int values. The
  248.      * fill ensures position {@code from} has a non-zero value; and the entire sub-range
  249.      * has a maximum of one zero. The method ensures any length sub-range contains
  250.      * non-zero bits. The output seed is suitable for generators that cannot be seeded
  251.      * with all zeros in the specified sub-range.</p>
  252.      *
  253.      * @param seed Seed array (modified in place).
  254.      * @param from The start of the not all-zero sub-range (inclusive).
  255.      * @param to The end of the not all-zero sub-range (exclusive).
  256.      * @throws IndexOutOfBoundsException if the sub-range is invalid
  257.      * @see #createInt()
  258.      */
  259.     static void ensureNonZero(int[] seed, int from, int to) {
  260.         if (from >= to) {
  261.             return;
  262.         }
  263.         // No check on the range so an IndexOutOfBoundsException will occur if invalid
  264.         for (int i = from; i < to; i++) {
  265.             if (seed[i] != 0) {
  266.                 return;
  267.             }
  268.         }
  269.         // Fill with non-zero values using a SplitMix-style PRNG.
  270.         // The range is at least 1.
  271.         // To ensure the first value is not zero requires the input to the mix function
  272.         // to be non-zero. This is ensured if the start is even since the increment is odd.
  273.         int x = createInt() << 1;
  274.         for (int i = from; i < to; i++) {
  275.             x += MixFunctions.GOLDEN_RATIO_32;
  276.             seed[i] = MixFunctions.murmur3(x);
  277.         }
  278.     }

  279.     /**
  280.      * Ensure the seed is not all-zero within the specified sub-range.
  281.      *
  282.      * <p>This method will check the sub-range and if all are zero it will fill the range
  283.      * with a random sequence seeded from the default source of random long values. The
  284.      * fill ensures position {@code from} has a non-zero value; and the entire sub-range
  285.      * has a maximum of one zero. The method ensures any length sub-range contains
  286.      * non-zero bits. The output seed is suitable for generators that cannot be seeded
  287.      * with all zeros in the specified sub-range.</p>
  288.      *
  289.      * @param seed Seed array (modified in place).
  290.      * @param from The start of the not all-zero sub-range (inclusive).
  291.      * @param to The end of the not all-zero sub-range (exclusive).
  292.      * @throws IndexOutOfBoundsException if the sub-range is invalid
  293.      * @see #createLong()
  294.      */
  295.     static void ensureNonZero(long[] seed, int from, int to) {
  296.         if (from >= to) {
  297.             return;
  298.         }
  299.         // No check on the range so an IndexOutOfBoundsException will occur if invalid
  300.         for (int i = from; i < to; i++) {
  301.             if (seed[i] != 0) {
  302.                 return;
  303.             }
  304.         }
  305.         // Fill with non-zero values using a SplitMix-style PRNG.
  306.         // The range is at least 1.
  307.         // To ensure the first value is not zero requires the input to the mix function
  308.         // to be non-zero. This is ensured if the start is even since the increment is odd.
  309.         long x = createLong() << 1;
  310.         for (int i = from; i < to; i++) {
  311.             x += MixFunctions.GOLDEN_RATIO_64;
  312.             seed[i] = MixFunctions.stafford13(x);
  313.         }
  314.     }

  315.     /**
  316.      * Ensure the seed is not all-zero within the specified sub-range.
  317.      *
  318.      * <p>This method will check the sub-range and if all are zero it will fill the range
  319.      * with a random sequence seeded from the provided source of randomness. If the range
  320.      * length is above 8 then the first 8 bytes in the range are ensured to not all be
  321.      * zero. If the range length is below 8 then the first byte in the range is ensured to
  322.      * be non-zero. The method ensures any length sub-range contains non-zero bits. The
  323.      * output seed is suitable for generators that cannot be seeded with all zeros in the
  324.      * specified sub-range.</p>
  325.      *
  326.      * @param seed Seed array (modified in place).
  327.      * @param from The start of the not all-zero sub-range (inclusive).
  328.      * @param to The end of the not all-zero sub-range (exclusive).
  329.      * @param source Source of randomness.
  330.      * @throws IndexOutOfBoundsException if the sub-range is invalid
  331.      */
  332.     static void ensureNonZero(byte[] seed, int from, int to, UniformRandomProvider source) {
  333.         if (from >= to) {
  334.             return;
  335.         }
  336.         // No check on the range so an IndexOutOfBoundsException will occur if invalid
  337.         for (int i = from; i < to; i++) {
  338.             if (seed[i] != 0) {
  339.                 return;
  340.             }
  341.         }
  342.         // Defend against a faulty source of randomness (which supplied all zero bytes)
  343.         // by filling with non-zero values using a SplitMix-style PRNG seeded from the source.
  344.         // The range is at least 1.
  345.         // To ensure the first value is not zero requires the input to the mix function
  346.         // to be non-zero. This is ensured if the start is even since the increment is odd.
  347.         long x = source.nextLong() << 1;

  348.         // Process in blocks of 8.
  349.         // Get the length without the final 3 bits set for a multiple of 8.
  350.         final int len = (to - from) & ~0x7;
  351.         final int end = from + len;
  352.         int i = from;
  353.         while (i < end) {
  354.             long v = MixFunctions.stafford13(x += MixFunctions.GOLDEN_RATIO_64);
  355.             for (int j = 0; j < 8; j++) {
  356.                 seed[i++] = (byte) v;
  357.                 v >>>= 8;
  358.             }
  359.         }

  360.         if (i < to) {
  361.             // The final bytes.
  362.             long v = MixFunctions.stafford13(x + MixFunctions.GOLDEN_RATIO_64);
  363.             // Note the special case where no blocks have been processed requires these
  364.             // bytes to be non-zero, i.e. (to - from) < 8. In this case the value 'v' will
  365.             // be non-zero due to the initialisation of 'x' as even.
  366.             // Rotate the value so the least significant byte is non-zero. The rotation
  367.             // in bits is rounded down to the nearest 8-bit block to ensure a byte rotation.
  368.             if (len == 0) {
  369.                 v = Long.rotateRight(v, Long.numberOfTrailingZeros(v) & ~0x7);
  370.             }
  371.             while (i < to) {
  372.                 seed[i++] = (byte) v;
  373.                 v >>>= 8;
  374.             }
  375.         }
  376.     }

  377.     /**
  378.      * Ensure the value is non-zero.
  379.      *
  380.      * <p>This method will replace a zero with a non-zero random number from the random source.</p>
  381.      *
  382.      * @param source Source of randomness.
  383.      * @param value Value.
  384.      * @return {@code value} if non-zero; else a new random number
  385.      */
  386.     static long ensureNonZero(RandomLongSource source,
  387.                               long value) {
  388.         long result = value;
  389.         while (result == 0) {
  390.             result = source.next();
  391.         }
  392.         return result;
  393.     }
  394. }