MATH-1300

Backport of changes originally introduced in "master" branch.
This commit is contained in:
Gilles 2016-01-01 00:18:12 +01:00
parent 2983ff81b1
commit c9c252bf26
1 changed files with 93 additions and 19 deletions

View File

@ -19,6 +19,7 @@ package org.apache.commons.math3.random;
import java.io.Serializable;
import org.apache.commons.math3.exception.NotStrictlyPositiveException;
import org.apache.commons.math3.exception.OutOfRangeException;
import org.apache.commons.math3.util.FastMath;
/** Base class for random number generators that generates bits streams.
@ -65,25 +66,6 @@ public abstract class BitsStreamGenerator
return next(1) != 0;
}
/** {@inheritDoc} */
public void nextBytes(byte[] bytes) {
int i = 0;
final int iEnd = bytes.length - 3;
while (i < iEnd) {
final int random = next(32);
bytes[i] = (byte) (random & 0xff);
bytes[i + 1] = (byte) ((random >> 8) & 0xff);
bytes[i + 2] = (byte) ((random >> 16) & 0xff);
bytes[i + 3] = (byte) ((random >> 24) & 0xff);
i += 4;
}
int random = next(32);
while (i < bytes.length) {
bytes[i++] = (byte) (random & 0xff);
random >>= 8;
}
}
/** {@inheritDoc} */
public double nextDouble() {
final long high = ((long) next(26)) << 26;
@ -194,4 +176,96 @@ public abstract class BitsStreamGenerator
nextGaussian = Double.NaN;
}
/**
* Generates random bytes and places them into a user-supplied array.
*
* <p>
* The array is filled with bytes extracted from random integers.
* This implies that the number of random bytes generated may be larger than
* the length of the byte array.
* </p>
*
* @param bytes Array in which to put the generated bytes. Cannot be {@code null}.
*/
@Override
public void nextBytes(byte[] bytes) {
nextBytesFill(bytes, 0, bytes.length);
}
/**
* Generates random bytes and places them into a user-supplied array.
*
* <p>
* The array is filled with bytes extracted from random integers.
* This implies that the number of random bytes generated may be larger than
* the length of the byte array.
* </p>
*
* @param bytes Array in which to put the generated bytes. Cannot be {@code null}.
* @param start Index at which to start inserting the generated bytes.
* @param len Number of bytes to insert.
* @throws OutOfRangeException if {@code start < 0} or {@code start >= bytes.length}.
* @throws OutOfRangeException if {@code len < 0} or {@code len > bytes.length - start}.
*/
public void nextBytes(byte[] bytes,
int start,
int len) {
if (start < 0 ||
start >= bytes.length) {
throw new OutOfRangeException(start, 0, bytes.length);
}
if (len < 0 ||
len > bytes.length - start) {
throw new OutOfRangeException(len, 0, bytes.length - start);
}
nextBytesFill(bytes, start, len);
}
/**
* Generates random bytes and places them into a user-supplied array.
*
* <p>
* The array is filled with bytes extracted from random integers.
* This implies that the number of random bytes generated may be larger than
* the length of the byte array.
* </p>
*
* @param bytes Array in which to put the generated bytes. Cannot be {@code null}.
* @param start Index at which to start inserting the generated bytes.
* @param len Number of bytes to insert.
*/
private void nextBytesFill(byte[] bytes,
int start,
int len) {
int index = start; // Index of first insertion.
// Index of first insertion plus multiple 4 part of length (i.e. length
// with two least significant bits unset).
final int indexLoopLimit = index + (len & 0x7ffffffc);
// Start filling in the byte array, 4 bytes at a time.
while (index < indexLoopLimit) {
final int random = next(32);
bytes[index++] = (byte) random;
bytes[index++] = (byte) (random >>> 8);
bytes[index++] = (byte) (random >>> 16);
bytes[index++] = (byte) (random >>> 24);
}
final int indexLimit = start + len; // Index of last insertion + 1.
// Fill in the remaining bytes.
if (index < indexLimit) {
int random = next(32);
while (true) {
bytes[index++] = (byte) random;
if (index < indexLimit) {
random >>>= 8;
} else {
break;
}
}
}
}
}