001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.rng.simple.internal;
018
019import java.security.SecureRandom;
020import java.util.concurrent.locks.ReentrantLock;
021
022import org.apache.commons.rng.core.util.NumberFactory;
023import org.apache.commons.rng.UniformRandomProvider;
024import org.apache.commons.rng.core.source64.RandomLongSource;
025import org.apache.commons.rng.core.source64.SplitMix64;
026import org.apache.commons.rng.core.source64.XoRoShiRo1024PlusPlus;
027
028/**
029 * Utilities related to seeding.
030 *
031 * <p>
032 * This class provides methods to generate random seeds (single values
033 * or arrays of values, of {@code int} or {@code long} types) that can
034 * be passed to the {@link org.apache.commons.rng.simple.RandomSource
035 * methods that create a generator instance}.
036 * <br>
037 * Although the seed-generating methods defined in this class will likely
038 * return different values for all calls, there is no guarantee that the
039 * produced seed will result always in a "good" sequence of numbers (even
040 * if the generator initialized with that seed is good).
041 * <br>
042 * There is <i>no guarantee</i> that sequences will not overlap.
043 * </p>
044 *
045 * @since 1.0
046 */
047public final class SeedFactory {
048    /** Size of the state array of "XoRoShiRo1024PlusPlus". */
049    private static final int XO_RO_SHI_RO_1024_STATE_SIZE = 16;
050    /** Size of block to fill in an {@code int[]} seed per synchronized operation. */
051    private static final int INT_ARRAY_BLOCK_SIZE = 8;
052    /** Size of block to fill in a {@code long[]} seed per synchronized operation. */
053    private static final int LONG_ARRAY_BLOCK_SIZE = 4;
054
055    /**
056     * The lock to own when using the seed generator. This lock is unfair and there is no
057     * particular access order for waiting threads.
058     *
059     * <p>This is used as an alternative to {@code synchronized} statements to guard access
060     * to the seed generator.</p>
061     */
062    private static final ReentrantLock LOCK = new ReentrantLock(false);
063
064    /** Generator with a long period. */
065    private static final UniformRandomProvider SEED_GENERATOR;
066
067    static {
068        // Use a secure RNG so that different instances (e.g. in multiple JVM
069        // instances started in rapid succession) will have different seeds.
070        final SecureRandom seedGen = new SecureRandom();
071        final byte[] bytes = new byte[8 * XO_RO_SHI_RO_1024_STATE_SIZE];
072        seedGen.nextBytes(bytes);
073        final long[] seed = NumberFactory.makeLongArray(bytes);
074        // The XoRoShiRo1024PlusPlus generator cannot recover from an all zero seed and
075        // will produce low quality initial output if initialized with some zeros.
076        // Ensure it is non zero at all array positions using a SplitMix64
077        // generator (this is insensitive to a zero seed so can use the first seed value).
078        final SplitMix64 rng = new SplitMix64(seed[0]);
079        for (int i = 0; i < seed.length; i++) {
080            seed[i] = ensureNonZero(rng, seed[i]);
081        }
082
083        SEED_GENERATOR = new XoRoShiRo1024PlusPlus(seed);
084    }
085
086    /**
087     * Class contains only static methods.
088     */
089    private SeedFactory() {
090        // Do nothing
091    }
092
093    /**
094     * Creates an {@code int} number for use as a seed.
095     *
096     * @return a random number.
097     */
098    public static int createInt() {
099        LOCK.lock();
100        try {
101            return SEED_GENERATOR.nextInt();
102        } finally {
103            LOCK.unlock();
104        }
105    }
106
107    /**
108     * Creates a {@code long} number for use as a seed.
109     *
110     * @return a random number.
111     */
112    public static long createLong() {
113        LOCK.lock();
114        try {
115            return SEED_GENERATOR.nextLong();
116        } finally {
117            LOCK.unlock();
118        }
119    }
120
121    /**
122     * Creates an array of {@code int} numbers for use as a seed.
123     *
124     * @param n Size of the array to create.
125     * @return an array of {@code n} random numbers.
126     */
127    public static int[] createIntArray(int n) {
128        // Behaviour from v1.3 is to ensure the first position is non-zero
129        return createIntArray(n, 0, Math.min(n, 1));
130    }
131
132    /**
133     * Creates an array of {@code int} numbers for use as a seed.
134     * Optionally ensure a sub-range of the array is not all-zero.
135     *
136     * <p>This method is package-private for use by {@link NativeSeedType}.
137     *
138     * @param n Size of the array to create.
139     * @param from The start of the not all-zero sub-range (inclusive).
140     * @param to The end of the not all-zero sub-range (exclusive).
141     * @return an array of {@code n} random numbers.
142     * @throws IndexOutOfBoundsException if the sub-range is invalid
143     * @since 1.5
144     */
145    static int[] createIntArray(int n, int from, int to) {
146        final int[] seed = new int[n];
147        // Compute the size that can be filled with complete blocks
148        final int blockSize = INT_ARRAY_BLOCK_SIZE * (n / INT_ARRAY_BLOCK_SIZE);
149        int i = 0;
150        while (i < blockSize) {
151            final int end = i + INT_ARRAY_BLOCK_SIZE;
152            fillIntArray(seed, i, end);
153            i = end;
154        }
155        // Final fill only if required
156        if (i != n) {
157            fillIntArray(seed, i, n);
158        }
159        ensureNonZero(seed, from, to);
160        return seed;
161    }
162
163    /**
164     * Creates an array of {@code long} numbers for use as a seed.
165     *
166     * @param n Size of the array to create.
167     * @return an array of {@code n} random numbers.
168     */
169    public static long[] createLongArray(int n) {
170        // Behaviour from v1.3 is to ensure the first position is non-zero
171        return createLongArray(n, 0, Math.min(n, 1));
172    }
173
174    /**
175     * Creates an array of {@code long} numbers for use as a seed.
176     * Optionally ensure a sub-range of the array is not all-zero.
177     *
178     * <p>This method is package-private for use by {@link NativeSeedType}.
179     *
180     * @param n Size of the array to create.
181     * @param from The start of the not all-zero sub-range (inclusive).
182     * @param to The end of the not all-zero sub-range (exclusive).
183     * @return an array of {@code n} random numbers.
184     * @throws IndexOutOfBoundsException if the sub-range is invalid
185     * @since 1.5
186     */
187    static long[] createLongArray(int n, int from, int to) {
188        final long[] seed = new long[n];
189        // Compute the size that can be filled with complete blocks
190        final int blockSize = LONG_ARRAY_BLOCK_SIZE * (n / LONG_ARRAY_BLOCK_SIZE);
191        int i = 0;
192        while (i < blockSize) {
193            final int end = i + LONG_ARRAY_BLOCK_SIZE;
194            fillLongArray(seed, i, end);
195            i = end;
196        }
197        // Final fill only if required
198        if (i != n) {
199            fillLongArray(seed, i, n);
200        }
201        ensureNonZero(seed, from, to);
202        return seed;
203    }
204
205    /**
206     * Fill the array between {@code start} inclusive and {@code end} exclusive from the
207     * seed generator. The lock is used to guard access to the generator.
208     *
209     * @param array Array data.
210     * @param start Start (inclusive).
211     * @param end End (exclusive).
212     */
213    private static void fillIntArray(int[] array, int start, int end) {
214        LOCK.lock();
215        try {
216            for (int i = start; i < end; i++) {
217                array[i] = SEED_GENERATOR.nextInt();
218            }
219        } finally {
220            LOCK.unlock();
221        }
222    }
223
224    /**
225     * Fill the array between {@code start} inclusive and {@code end} exclusive from the
226     * seed generator. The lock is used to guard access to the generator.
227     *
228     * @param array Array data.
229     * @param start Start (inclusive).
230     * @param end End (exclusive).
231     */
232    private static void fillLongArray(long[] array, int start, int end) {
233        LOCK.lock();
234        try {
235            for (int i = start; i < end; i++) {
236                array[i] = SEED_GENERATOR.nextLong();
237            }
238        } finally {
239            LOCK.unlock();
240        }
241    }
242
243    /**
244     * Creates an array of {@code byte} numbers for use as a seed using the supplied source of
245     * randomness. A sub-range can be specified that must not contain all zeros.
246     *
247     * @param source Source of randomness.
248     * @param n Size of the array to create.
249     * @param from The start of the not all-zero sub-range (inclusive).
250     * @param to The end of the not all-zero sub-range (exclusive).
251     * @return an array of {@code n} random numbers.
252     */
253    static byte[] createByteArray(UniformRandomProvider source,
254                                  int n,
255                                  int from,
256                                  int to) {
257        final byte[] seed = new byte[n];
258        source.nextBytes(seed);
259        ensureNonZero(seed, from, to, source);
260        return seed;
261    }
262
263    /**
264     * Ensure the seed is not all-zero within the specified sub-range.
265     *
266     * <p>This method will check the sub-range and if all are zero it will fill the range
267     * with a random sequence seeded from the default source of random int values. The
268     * fill ensures position {@code from} has a non-zero value; and the entire sub-range
269     * has a maximum of one zero. The method ensures any length sub-range contains
270     * non-zero bits. The output seed is suitable for generators that cannot be seeded
271     * with all zeros in the specified sub-range.</p>
272     *
273     * @param seed Seed array (modified in place).
274     * @param from The start of the not all-zero sub-range (inclusive).
275     * @param to The end of the not all-zero sub-range (exclusive).
276     * @throws IndexOutOfBoundsException if the sub-range is invalid
277     * @see #createInt()
278     */
279    static void ensureNonZero(int[] seed, int from, int to) {
280        if (from >= to) {
281            return;
282        }
283        // No check on the range so an IndexOutOfBoundsException will occur if invalid
284        for (int i = from; i < to; i++) {
285            if (seed[i] != 0) {
286                return;
287            }
288        }
289        // Fill with non-zero values using a SplitMix-style PRNG.
290        // The range is at least 1.
291        // To ensure the first value is not zero requires the input to the mix function
292        // to be non-zero. This is ensured if the start is even since the increment is odd.
293        int x = createInt() << 1;
294        for (int i = from; i < to; i++) {
295            x += MixFunctions.GOLDEN_RATIO_32;
296            seed[i] = MixFunctions.murmur3(x);
297        }
298    }
299
300    /**
301     * Ensure the seed is not all-zero within the specified sub-range.
302     *
303     * <p>This method will check the sub-range and if all are zero it will fill the range
304     * with a random sequence seeded from the default source of random long values. The
305     * fill ensures position {@code from} has a non-zero value; and the entire sub-range
306     * has a maximum of one zero. The method ensures any length sub-range contains
307     * non-zero bits. The output seed is suitable for generators that cannot be seeded
308     * with all zeros in the specified sub-range.</p>
309     *
310     * @param seed Seed array (modified in place).
311     * @param from The start of the not all-zero sub-range (inclusive).
312     * @param to The end of the not all-zero sub-range (exclusive).
313     * @throws IndexOutOfBoundsException if the sub-range is invalid
314     * @see #createLong()
315     */
316    static void ensureNonZero(long[] seed, int from, int to) {
317        if (from >= to) {
318            return;
319        }
320        // No check on the range so an IndexOutOfBoundsException will occur if invalid
321        for (int i = from; i < to; i++) {
322            if (seed[i] != 0) {
323                return;
324            }
325        }
326        // Fill with non-zero values using a SplitMix-style PRNG.
327        // The range is at least 1.
328        // To ensure the first value is not zero requires the input to the mix function
329        // to be non-zero. This is ensured if the start is even since the increment is odd.
330        long x = createLong() << 1;
331        for (int i = from; i < to; i++) {
332            x += MixFunctions.GOLDEN_RATIO_64;
333            seed[i] = MixFunctions.stafford13(x);
334        }
335    }
336
337    /**
338     * Ensure the seed is not all-zero within the specified sub-range.
339     *
340     * <p>This method will check the sub-range and if all are zero it will fill the range
341     * with a random sequence seeded from the provided source of randomness. If the range
342     * length is above 8 then the first 8 bytes in the range are ensured to not all be
343     * zero. If the range length is below 8 then the first byte in the range is ensured to
344     * be non-zero. The method ensures any length sub-range contains non-zero bits. The
345     * output seed is suitable for generators that cannot be seeded with all zeros in the
346     * specified sub-range.</p>
347     *
348     * @param seed Seed array (modified in place).
349     * @param from The start of the not all-zero sub-range (inclusive).
350     * @param to The end of the not all-zero sub-range (exclusive).
351     * @param source Source of randomness.
352     * @throws IndexOutOfBoundsException if the sub-range is invalid
353     */
354    static void ensureNonZero(byte[] seed, int from, int to, UniformRandomProvider source) {
355        if (from >= to) {
356            return;
357        }
358        // No check on the range so an IndexOutOfBoundsException will occur if invalid
359        for (int i = from; i < to; i++) {
360            if (seed[i] != 0) {
361                return;
362            }
363        }
364        // Defend against a faulty source of randomness (which supplied all zero bytes)
365        // by filling with non-zero values using a SplitMix-style PRNG seeded from the source.
366        // The range is at least 1.
367        // To ensure the first value is not zero requires the input to the mix function
368        // to be non-zero. This is ensured if the start is even since the increment is odd.
369        long x = source.nextLong() << 1;
370
371        // Process in blocks of 8.
372        // Get the length without the final 3 bits set for a multiple of 8.
373        final int len = (to - from) & ~0x7;
374        final int end = from + len;
375        int i = from;
376        while (i < end) {
377            x += MixFunctions.GOLDEN_RATIO_64;
378            long v = MixFunctions.stafford13(x);
379            for (int j = 0; j < 8; j++) {
380                seed[i++] = (byte) v;
381                v >>>= 8;
382            }
383        }
384
385        if (i < to) {
386            // The final bytes.
387            long v = MixFunctions.stafford13(x + MixFunctions.GOLDEN_RATIO_64);
388            // Note the special case where no blocks have been processed requires these
389            // bytes to be non-zero, i.e. (to - from) < 8. In this case the value 'v' will
390            // be non-zero due to the initialisation of 'x' as even.
391            // Rotate the value so the least significant byte is non-zero. The rotation
392            // in bits is rounded down to the nearest 8-bit block to ensure a byte rotation.
393            if (len == 0) {
394                v = Long.rotateRight(v, Long.numberOfTrailingZeros(v) & ~0x7);
395            }
396            while (i < to) {
397                seed[i++] = (byte) v;
398                v >>>= 8;
399            }
400        }
401    }
402
403    /**
404     * Ensure the value is non-zero.
405     *
406     * <p>This method will replace a zero with a non-zero random number from the random source.</p>
407     *
408     * @param source Source of randomness.
409     * @param value Value.
410     * @return {@code value} if non-zero; else a new random number
411     */
412    static long ensureNonZero(RandomLongSource source,
413                              long value) {
414        long result = value;
415        while (result == 0) {
416            result = source.next();
417        }
418        return result;
419    }
420}