/** * {@inheritDoc} * * <p><strong>Algorithm Description</strong>: if the lower bound is excluded, scales the output of * Random.nextDouble(), but rejects 0 values (i.e., will generate another random double if * Random.nextDouble() returns 0). This is necessary to provide a symmetric output interval (both * endpoints excluded). * * @throws NumberIsTooLargeException if {@code lower >= upper} * @throws NotFiniteNumberException if one of the bounds is infinite * @throws NotANumberException if one of the bounds is NaN */ public double nextUniform(double lower, double upper, boolean lowerInclusive) throws NumberIsTooLargeException, NotFiniteNumberException, NotANumberException { if (lower >= upper) { throw new NumberIsTooLargeException( LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, lower, upper, false); } if (Double.isInfinite(lower)) { throw new NotFiniteNumberException(LocalizedFormats.INFINITE_BOUND, lower); } if (Double.isInfinite(upper)) { throw new NotFiniteNumberException(LocalizedFormats.INFINITE_BOUND, upper); } if (Double.isNaN(lower) || Double.isNaN(upper)) { throw new NotANumberException(); } final RandomGenerator generator = getRandomGenerator(); // ensure nextDouble() isn't 0.0 double u = generator.nextDouble(); while (!lowerInclusive && u <= 0.0) { u = generator.nextDouble(); } return u * upper + (1.0 - u) * lower; }
/** * {@inheritDoc} * * <p><strong>Algorithm Description:</strong> hex strings are generated using a 2-step process. * * <ol> * <li>{@code len / 2 + 1} binary bytes are generated using the underlying Random * <li>Each binary byte is translated into 2 hex digits * </ol> * * @param len the desired string length. * @return the random string. * @throws NotStrictlyPositiveException if {@code len <= 0}. */ public String nextHexString(int len) throws NotStrictlyPositiveException { if (len <= 0) { throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len); } // Get a random number generator RandomGenerator ran = getRandomGenerator(); // Initialize output buffer StringBuilder outBuffer = new StringBuilder(); // Get int(len/2)+1 random bytes byte[] randomBytes = new byte[(len / 2) + 1]; ran.nextBytes(randomBytes); // Convert each byte to 2 hex digits for (int i = 0; i < randomBytes.length; i++) { Integer c = Integer.valueOf(randomBytes[i]); /* * Add 128 to byte value to make interval 0-255 before doing hex * conversion. This guarantees <= 2 hex digits from toHexString() * toHexString would otherwise add 2^32 to negative arguments. */ String hex = Integer.toHexString(c.intValue() + 128); // Make sure we add 2 hex digits for each byte if (hex.length() == 1) { hex = "0" + hex; } outBuffer.append(hex); } return outBuffer.toString().substring(0, len); }
/** * {@inheritDoc} * * <p><strong>Algorithm Description:</strong> hex strings are generated in 40-byte segments using * a 3-step process. * * <ol> * <li>20 random bytes are generated using the underlying <code>SecureRandom</code>. * <li>SHA-1 hash is applied to yield a 20-byte binary digest. * <li>Each byte of the binary digest is converted to 2 hex digits. * </ol> * * @throws NotStrictlyPositiveException if {@code len <= 0} */ public String nextSecureHexString(int len) throws NotStrictlyPositiveException { if (len <= 0) { throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len); } // Get SecureRandom and setup Digest provider final RandomGenerator secRan = getSecRan(); MessageDigest alg = null; try { alg = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException ex) { // this should never happen throw new MathInternalError(ex); } alg.reset(); // Compute number of iterations required (40 bytes each) int numIter = (len / 40) + 1; StringBuilder outBuffer = new StringBuilder(); for (int iter = 1; iter < numIter + 1; iter++) { byte[] randomBytes = new byte[40]; secRan.nextBytes(randomBytes); alg.update(randomBytes); // Compute hash -- will create 20-byte binary hash byte[] hash = alg.digest(); // Loop over the hash, converting each byte to 2 hex digits for (int i = 0; i < hash.length; i++) { Integer c = Integer.valueOf(hash[i]); /* * Add 128 to byte value to make interval 0-255 This guarantees * <= 2 hex digits from toHexString() toHexString would * otherwise add 2^32 to negative arguments */ String hex = Integer.toHexString(c.intValue() + 128); // Keep strings uniform length -- guarantees 40 bytes if (hex.length() == 1) { hex = "0" + hex; } outBuffer.append(hex); } } return outBuffer.toString().substring(0, len); }
/** * Returns the SecureRandom used to generate secure random data. * * <p>Creates and initializes if null. Uses {@code System.currentTimeMillis() + * System.identityHashCode(this)} as the default seed. * * @return the SecureRandom used to generate secure random data, wrapped in a {@link * RandomGenerator}. */ private RandomGenerator getSecRan() { if (secRand == null) { secRand = RandomGeneratorFactory.createRandomGenerator(new SecureRandom()); secRand.setSeed(System.currentTimeMillis() + System.identityHashCode(this)); } return secRand; }
/** {@inheritDoc} */ public long nextSecureLong(final long lower, final long upper) throws NumberIsTooLargeException { if (lower >= upper) { throw new NumberIsTooLargeException( LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, lower, upper, false); } final RandomGenerator rng = getSecRan(); final long max = (upper - lower) + 1; if (max <= 0) { // the range is too wide to fit in a positive long (larger than 2^63); as it covers // more than half the long range, we use directly a simple rejection method while (true) { final long r = rng.nextLong(); if (r >= lower && r <= upper) { return r; } } } else if (max < Integer.MAX_VALUE) { // we can shift the range and generate directly a positive int return lower + rng.nextInt((int) max); } else { // we can shift the range and generate directly a positive long return lower + nextLong(rng, max); } }
/** * Returns a pseudorandom, uniformly distributed {@code long} value between 0 (inclusive) and the * specified value (exclusive), drawn from this random number generator's sequence. * * @param rng random generator to use * @param n the bound on the random number to be returned. Must be positive. * @return a pseudorandom, uniformly distributed {@code long} value between 0 (inclusive) and n * (exclusive). * @throws IllegalArgumentException if n is not positive. */ private static long nextLong(final RandomGenerator rng, final long n) throws IllegalArgumentException { if (n > 0) { final byte[] byteArray = new byte[8]; long bits; long val; do { rng.nextBytes(byteArray); bits = 0; for (final byte b : byteArray) { bits = (bits << 8) | (((long) b) & 0xffL); } bits &= 0x7fffffffffffffffL; val = bits % n; } while (bits - val + (n - 1) < 0); return val; } throw new NotStrictlyPositiveException(n); }