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}