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 19 import java.security.SecureRandom; 20 import java.util.concurrent.locks.ReentrantLock; 21 22 import org.apache.commons.rng.core.util.NumberFactory; 23 import org.apache.commons.rng.UniformRandomProvider; 24 import org.apache.commons.rng.core.source64.RandomLongSource; 25 import org.apache.commons.rng.core.source64.SplitMix64; 26 import org.apache.commons.rng.core.source64.XoRoShiRo1024PlusPlus; 27 28 /** 29 * Utilities related to seeding. 30 * 31 * <p> 32 * This class provides methods to generate random seeds (single values 33 * or arrays of values, of {@code int} or {@code long} types) that can 34 * be passed to the {@link org.apache.commons.rng.simple.RandomSource 35 * methods that create a generator instance}. 36 * <br> 37 * Although the seed-generating methods defined in this class will likely 38 * return different values for all calls, there is no guarantee that the 39 * produced seed will result always in a "good" sequence of numbers (even 40 * if the generator initialized with that seed is good). 41 * <br> 42 * There is <i>no guarantee</i> that sequences will not overlap. 43 * </p> 44 * 45 * @since 1.0 46 */ 47 public final class SeedFactory { 48 /** Size of the state array of "XoRoShiRo1024PlusPlus". */ 49 private static final int XO_RO_SHI_RO_1024_STATE_SIZE = 16; 50 /** Size of block to fill in an {@code int[]} seed per synchronized operation. */ 51 private static final int INT_ARRAY_BLOCK_SIZE = 8; 52 /** Size of block to fill in a {@code long[]} seed per synchronized operation. */ 53 private static final int LONG_ARRAY_BLOCK_SIZE = 4; 54 55 /** 56 * The lock to own when using the seed generator. This lock is unfair and there is no 57 * particular access order for waiting threads. 58 * 59 * <p>This is used as an alternative to {@code synchronized} statements to guard access 60 * to the seed generator.</p> 61 */ 62 private static final ReentrantLock LOCK = new ReentrantLock(false); 63 64 /** Generator with a long period. */ 65 private static final UniformRandomProvider SEED_GENERATOR; 66 67 static { 68 // Use a secure RNG so that different instances (e.g. in multiple JVM 69 // instances started in rapid succession) will have different seeds. 70 final SecureRandom seedGen = new SecureRandom(); 71 final byte[] bytes = new byte[8 * XO_RO_SHI_RO_1024_STATE_SIZE]; 72 seedGen.nextBytes(bytes); 73 final long[] seed = NumberFactory.makeLongArray(bytes); 74 // The XoRoShiRo1024PlusPlus generator cannot recover from an all zero seed and 75 // will produce low quality initial output if initialized with some zeros. 76 // Ensure it is non zero at all array positions using a SplitMix64 77 // generator (this is insensitive to a zero seed so can use the first seed value). 78 final SplitMix64 rng = new SplitMix64(seed[0]); 79 for (int i = 0; i < seed.length; i++) { 80 seed[i] = ensureNonZero(rng, seed[i]); 81 } 82 83 SEED_GENERATOR = new XoRoShiRo1024PlusPlus(seed); 84 } 85 86 /** 87 * Class contains only static methods. 88 */ 89 private SeedFactory() {} 90 91 /** 92 * Creates an {@code int} number for use as a seed. 93 * 94 * @return a random number. 95 */ 96 public static int createInt() { 97 LOCK.lock(); 98 try { 99 return SEED_GENERATOR.nextInt(); 100 } finally { 101 LOCK.unlock(); 102 } 103 } 104 105 /** 106 * Creates a {@code long} number for use as a seed. 107 * 108 * @return a random number. 109 */ 110 public static long createLong() { 111 LOCK.lock(); 112 try { 113 return SEED_GENERATOR.nextLong(); 114 } finally { 115 LOCK.unlock(); 116 } 117 } 118 119 /** 120 * Creates an array of {@code int} numbers for use as a seed. 121 * 122 * @param n Size of the array to create. 123 * @return an array of {@code n} random numbers. 124 */ 125 public static int[] createIntArray(int n) { 126 // Behaviour from v1.3 is to ensure the first position is non-zero 127 return createIntArray(n, 0, Math.min(n, 1)); 128 } 129 130 /** 131 * Creates an array of {@code int} numbers for use as a seed. 132 * Optionally ensure a sub-range of the array is not all-zero. 133 * 134 * <p>This method is package-private for use by {@link NativeSeedType}. 135 * 136 * @param n Size of the array to create. 137 * @param from The start of the not all-zero sub-range (inclusive). 138 * @param to The end of the not all-zero sub-range (exclusive). 139 * @return an array of {@code n} random numbers. 140 * @throws IndexOutOfBoundsException if the sub-range is invalid 141 * @since 1.5 142 */ 143 static int[] createIntArray(int n, int from, int to) { 144 final int[] seed = new int[n]; 145 // Compute the size that can be filled with complete blocks 146 final int blockSize = INT_ARRAY_BLOCK_SIZE * (n / INT_ARRAY_BLOCK_SIZE); 147 int i = 0; 148 while (i < blockSize) { 149 final int end = i + INT_ARRAY_BLOCK_SIZE; 150 fillIntArray(seed, i, end); 151 i = end; 152 } 153 // Final fill only if required 154 if (i != n) { 155 fillIntArray(seed, i, n); 156 } 157 ensureNonZero(seed, from, to); 158 return seed; 159 } 160 161 /** 162 * Creates an array of {@code long} numbers for use as a seed. 163 * 164 * @param n Size of the array to create. 165 * @return an array of {@code n} random numbers. 166 */ 167 public static long[] createLongArray(int n) { 168 // Behaviour from v1.3 is to ensure the first position is non-zero 169 return createLongArray(n, 0, Math.min(n, 1)); 170 } 171 172 /** 173 * Creates an array of {@code long} numbers for use as a seed. 174 * Optionally ensure a sub-range of the array is not all-zero. 175 * 176 * <p>This method is package-private for use by {@link NativeSeedType}. 177 * 178 * @param n Size of the array to create. 179 * @param from The start of the not all-zero sub-range (inclusive). 180 * @param to The end of the not all-zero sub-range (exclusive). 181 * @return an array of {@code n} random numbers. 182 * @throws IndexOutOfBoundsException if the sub-range is invalid 183 * @since 1.5 184 */ 185 static long[] createLongArray(int n, int from, int to) { 186 final long[] seed = new long[n]; 187 // Compute the size that can be filled with complete blocks 188 final int blockSize = LONG_ARRAY_BLOCK_SIZE * (n / LONG_ARRAY_BLOCK_SIZE); 189 int i = 0; 190 while (i < blockSize) { 191 final int end = i + LONG_ARRAY_BLOCK_SIZE; 192 fillLongArray(seed, i, end); 193 i = end; 194 } 195 // Final fill only if required 196 if (i != n) { 197 fillLongArray(seed, i, n); 198 } 199 ensureNonZero(seed, from, to); 200 return seed; 201 } 202 203 /** 204 * Fill the array between {@code start} inclusive and {@code end} exclusive from the 205 * seed generator. The lock is used to guard access to the generator. 206 * 207 * @param array Array data. 208 * @param start Start (inclusive). 209 * @param end End (exclusive). 210 */ 211 private static void fillIntArray(int[] array, int start, int end) { 212 LOCK.lock(); 213 try { 214 for (int i = start; i < end; i++) { 215 array[i] = SEED_GENERATOR.nextInt(); 216 } 217 } finally { 218 LOCK.unlock(); 219 } 220 } 221 222 /** 223 * Fill the array between {@code start} inclusive and {@code end} exclusive from the 224 * seed generator. The lock is used to guard access to the generator. 225 * 226 * @param array Array data. 227 * @param start Start (inclusive). 228 * @param end End (exclusive). 229 */ 230 private static void fillLongArray(long[] array, int start, int end) { 231 LOCK.lock(); 232 try { 233 for (int i = start; i < end; i++) { 234 array[i] = SEED_GENERATOR.nextLong(); 235 } 236 } finally { 237 LOCK.unlock(); 238 } 239 } 240 241 /** 242 * Creates an array of {@code byte} numbers for use as a seed using the supplied source of 243 * randomness. A sub-range can be specified that must not contain all zeros. 244 * 245 * @param source Source of randomness. 246 * @param n Size of the array to create. 247 * @param from The start of the not all-zero sub-range (inclusive). 248 * @param to The end of the not all-zero sub-range (exclusive). 249 * @return an array of {@code n} random numbers. 250 */ 251 static byte[] createByteArray(UniformRandomProvider source, 252 int n, 253 int from, 254 int to) { 255 final byte[] seed = new byte[n]; 256 source.nextBytes(seed); 257 ensureNonZero(seed, from, to, source); 258 return seed; 259 } 260 261 /** 262 * Ensure the seed is not all-zero within the specified sub-range. 263 * 264 * <p>This method will check the sub-range and if all are zero it will fill the range 265 * with a random sequence seeded from the default source of random int values. The 266 * fill ensures position {@code from} has a non-zero value; and the entire sub-range 267 * has a maximum of one zero. The method ensures any length sub-range contains 268 * non-zero bits. The output seed is suitable for generators that cannot be seeded 269 * with all zeros in the specified sub-range.</p> 270 * 271 * @param seed Seed array (modified in place). 272 * @param from The start of the not all-zero sub-range (inclusive). 273 * @param to The end of the not all-zero sub-range (exclusive). 274 * @throws IndexOutOfBoundsException if the sub-range is invalid 275 * @see #createInt() 276 */ 277 static void ensureNonZero(int[] seed, int from, int to) { 278 if (from >= to) { 279 return; 280 } 281 // No check on the range so an IndexOutOfBoundsException will occur if invalid 282 for (int i = from; i < to; i++) { 283 if (seed[i] != 0) { 284 return; 285 } 286 } 287 // Fill with non-zero values using a SplitMix-style PRNG. 288 // The range is at least 1. 289 // To ensure the first value is not zero requires the input to the mix function 290 // to be non-zero. This is ensured if the start is even since the increment is odd. 291 int x = createInt() << 1; 292 for (int i = from; i < to; i++) { 293 seed[i] = MixFunctions.murmur3(x += MixFunctions.GOLDEN_RATIO_32); 294 } 295 } 296 297 /** 298 * Ensure the seed is not all-zero within the specified sub-range. 299 * 300 * <p>This method will check the sub-range and if all are zero it will fill the range 301 * with a random sequence seeded from the default source of random long values. The 302 * fill ensures position {@code from} has a non-zero value; and the entire sub-range 303 * has a maximum of one zero. The method ensures any length sub-range contains 304 * non-zero bits. The output seed is suitable for generators that cannot be seeded 305 * with all zeros in the specified sub-range.</p> 306 * 307 * @param seed Seed array (modified in place). 308 * @param from The start of the not all-zero sub-range (inclusive). 309 * @param to The end of the not all-zero sub-range (exclusive). 310 * @throws IndexOutOfBoundsException if the sub-range is invalid 311 * @see #createLong() 312 */ 313 static void ensureNonZero(long[] seed, int from, int to) { 314 if (from >= to) { 315 return; 316 } 317 // No check on the range so an IndexOutOfBoundsException will occur if invalid 318 for (int i = from; i < to; i++) { 319 if (seed[i] != 0) { 320 return; 321 } 322 } 323 // Fill with non-zero values using a SplitMix-style PRNG. 324 // The range is at least 1. 325 // To ensure the first value is not zero requires the input to the mix function 326 // to be non-zero. This is ensured if the start is even since the increment is odd. 327 long x = createLong() << 1; 328 for (int i = from; i < to; i++) { 329 seed[i] = MixFunctions.stafford13(x += MixFunctions.GOLDEN_RATIO_64); 330 } 331 } 332 333 /** 334 * Ensure the seed is not all-zero within the specified sub-range. 335 * 336 * <p>This method will check the sub-range and if all are zero it will fill the range 337 * with a random sequence seeded from the provided source of randomness. If the range 338 * length is above 8 then the first 8 bytes in the range are ensured to not all be 339 * zero. If the range length is below 8 then the first byte in the range is ensured to 340 * be non-zero. The method ensures any length sub-range contains non-zero bits. The 341 * output seed is suitable for generators that cannot be seeded with all zeros in the 342 * specified sub-range.</p> 343 * 344 * @param seed Seed array (modified in place). 345 * @param from The start of the not all-zero sub-range (inclusive). 346 * @param to The end of the not all-zero sub-range (exclusive). 347 * @param source Source of randomness. 348 * @throws IndexOutOfBoundsException if the sub-range is invalid 349 */ 350 static void ensureNonZero(byte[] seed, int from, int to, UniformRandomProvider source) { 351 if (from >= to) { 352 return; 353 } 354 // No check on the range so an IndexOutOfBoundsException will occur if invalid 355 for (int i = from; i < to; i++) { 356 if (seed[i] != 0) { 357 return; 358 } 359 } 360 // Defend against a faulty source of randomness (which supplied all zero bytes) 361 // by filling with non-zero values using a SplitMix-style PRNG seeded from the source. 362 // The range is at least 1. 363 // To ensure the first value is not zero requires the input to the mix function 364 // to be non-zero. This is ensured if the start is even since the increment is odd. 365 long x = source.nextLong() << 1; 366 367 // Process in blocks of 8. 368 // Get the length without the final 3 bits set for a multiple of 8. 369 final int len = (to - from) & ~0x7; 370 final int end = from + len; 371 int i = from; 372 while (i < end) { 373 long v = MixFunctions.stafford13(x += MixFunctions.GOLDEN_RATIO_64); 374 for (int j = 0; j < 8; j++) { 375 seed[i++] = (byte) v; 376 v >>>= 8; 377 } 378 } 379 380 if (i < to) { 381 // The final bytes. 382 long v = MixFunctions.stafford13(x + MixFunctions.GOLDEN_RATIO_64); 383 // Note the special case where no blocks have been processed requires these 384 // bytes to be non-zero, i.e. (to - from) < 8. In this case the value 'v' will 385 // be non-zero due to the initialisation of 'x' as even. 386 // Rotate the value so the least significant byte is non-zero. The rotation 387 // in bits is rounded down to the nearest 8-bit block to ensure a byte rotation. 388 if (len == 0) { 389 v = Long.rotateRight(v, Long.numberOfTrailingZeros(v) & ~0x7); 390 } 391 while (i < to) { 392 seed[i++] = (byte) v; 393 v >>>= 8; 394 } 395 } 396 } 397 398 /** 399 * Ensure the value is non-zero. 400 * 401 * <p>This method will replace a zero with a non-zero random number from the random source.</p> 402 * 403 * @param source Source of randomness. 404 * @param value Value. 405 * @return {@code value} if non-zero; else a new random number 406 */ 407 static long ensureNonZero(RandomLongSource source, 408 long value) { 409 long result = value; 410 while (result == 0) { 411 result = source.next(); 412 } 413 return result; 414 } 415 }