blob: 29c09a5dc2092968295a111e0e34eb1e3a43b2cc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.rng.examples.stress;
import org.apache.commons.rng.UniformRandomProvider;
import org.apache.commons.rng.core.source32.IntProvider;
import org.apache.commons.rng.core.source32.RandomIntSource;
import org.apache.commons.rng.core.source64.RandomLongSource;
import org.apache.commons.rng.core.util.NumberFactory;
import org.apache.commons.rng.core.source64.LongProvider;
import java.io.OutputStream;
import java.nio.ByteOrder;
import java.util.concurrent.ThreadLocalRandom;
/**
* Utility methods for a {@link UniformRandomProvider}.
*/
final class RNGUtils {
/** Name prefix for bit-reversed RNGs. */
private static final String BYTE_REVERSED = "Byte-reversed ";
/** Name prefix for byte-reversed RNGs. */
private static final String BIT_REVERSED = "Bit-reversed ";
/** Name prefix for hashcode mixed RNGs. */
private static final String HASH_CODE = "HashCode ^ ";
/** Name prefix for ThreadLocalRandom xor mixed RNGs. */
private static final String TLR_MIXED = "ThreadLocalRandom ^ ";
/** Name of xor operator for xor mixed RNGs. */
private static final String XOR = " ^ ";
/** Message for an unrecognised native output type. */
private static final String UNRECOGNISED_NATIVE_TYPE = "Unrecognised native output type: ";
/** Message when not a RandomLongSource. */
private static final String NOT_LONG_SOURCE = "Not a 64-bit long generator: ";
/** No public construction. */
private RNGUtils() {}
/**
* Wrap the random generator with a new instance that will reverse the byte order of
* the native type. The input must be either a {@link RandomIntSource} or
* {@link RandomLongSource}.
*
* @param rng The random generator.
* @return the byte reversed random generator.
* @throws ApplicationException If the input source native type is not recognised.
* @see Integer#reverseBytes(int)
* @see Long#reverseBytes(long)
*/
static UniformRandomProvider createReverseBytesProvider(final UniformRandomProvider rng) {
if (rng instanceof RandomIntSource) {
return new IntProvider() {
@Override
public int next() {
return Integer.reverseBytes(rng.nextInt());
}
@Override
public String toString() {
return BYTE_REVERSED + rng.toString();
}
};
}
if (rng instanceof RandomLongSource) {
return new LongProvider() {
@Override
public long next() {
return Long.reverseBytes(rng.nextLong());
}
@Override
public String toString() {
return BYTE_REVERSED + rng.toString();
}
};
}
throw new ApplicationException(UNRECOGNISED_NATIVE_TYPE + rng);
}
/**
* Wrap the random generator with a new instance that will reverse the bits of
* the native type. The input must be either a {@link RandomIntSource} or
* {@link RandomLongSource}.
*
* @param rng The random generator.
* @return the bit reversed random generator.
* @throws ApplicationException If the input source native type is not recognised.
* @see Integer#reverse(int)
* @see Long#reverse(long)
*/
static UniformRandomProvider createReverseBitsProvider(final UniformRandomProvider rng) {
if (rng instanceof RandomIntSource) {
return new IntProvider() {
@Override
public int next() {
return Integer.reverse(rng.nextInt());
}
@Override
public String toString() {
return BIT_REVERSED + rng.toString();
}
};
}
if (rng instanceof RandomLongSource) {
return new LongProvider() {
@Override
public long next() {
return Long.reverse(rng.nextLong());
}
@Override
public String toString() {
return BIT_REVERSED + rng.toString();
}
};
}
throw new ApplicationException(UNRECOGNISED_NATIVE_TYPE + rng);
}
/**
* Wrap the random generator with an {@link IntProvider} that will use
* {@link UniformRandomProvider#nextInt()}.
* An input {@link RandomIntSource} is returned unmodified.
*
* @param rng The random generator.
* @return the int random generator.
*/
static UniformRandomProvider createIntProvider(final UniformRandomProvider rng) {
if (!(rng instanceof RandomIntSource)) {
return new IntProvider() {
@Override
public int next() {
return rng.nextInt();
}
@Override
public String toString() {
return "Int bits " + rng.toString();
}
};
}
return rng;
}
/**
* Wrap the random generator with an {@link IntProvider} that will use the upper
* 32-bits of the {@code long} from {@link UniformRandomProvider#nextLong()}.
* The input must be a {@link RandomLongSource}.
*
* @param rng The random generator.
* @return the upper bits random generator.
* @throws ApplicationException If the input source native type is not 64-bit.
*/
static UniformRandomProvider createLongUpperBitsIntProvider(final UniformRandomProvider rng) {
if (rng instanceof RandomLongSource) {
return new IntProvider() {
@Override
public int next() {
return (int) (rng.nextLong() >>> 32);
}
@Override
public String toString() {
return "Long upper-bits " + rng.toString();
}
};
}
throw new ApplicationException(NOT_LONG_SOURCE + rng);
}
/**
* Wrap the random generator with an {@link IntProvider} that will use the lower
* 32-bits of the {@code long} from {@link UniformRandomProvider#nextLong()}.
* The input must be a {@link RandomLongSource}.
*
* @param rng The random generator.
* @return the lower bits random generator.
* @throws ApplicationException If the input source native type is not 64-bit.
*/
static UniformRandomProvider createLongLowerBitsIntProvider(final UniformRandomProvider rng) {
if (rng instanceof RandomLongSource) {
return new IntProvider() {
@Override
public int next() {
return (int) rng.nextLong();
}
@Override
public String toString() {
return "Long lower-bits " + rng.toString();
}
};
}
throw new ApplicationException(NOT_LONG_SOURCE + rng);
}
/**
* Wrap the random generator with a new instance that will combine the bits
* using a {@code xor} operation with a generated hash code. The input must be either
* a {@link RandomIntSource} or {@link RandomLongSource}.
*
* <pre>
* {@code
* System.identityHashCode(new Object()) ^ rng.nextInt()
* }
* </pre>
*
* Note: This generator will be slow.
*
* @param rng The random generator.
* @return the combined random generator.
* @throws ApplicationException If the input source native type is not recognised.
* @see System#identityHashCode(Object)
*/
static UniformRandomProvider createHashCodeProvider(final UniformRandomProvider rng) {
if (rng instanceof RandomIntSource) {
return new IntProvider() {
@Override
public int next() {
return System.identityHashCode(new Object()) ^ rng.nextInt();
}
@Override
public String toString() {
return HASH_CODE + rng.toString();
}
};
}
if (rng instanceof RandomLongSource) {
return new LongProvider() {
@Override
public long next() {
final long mix = NumberFactory.makeLong(System.identityHashCode(new Object()),
System.identityHashCode(new Object()));
return mix ^ rng.nextLong();
}
@Override
public String toString() {
return HASH_CODE + rng.toString();
}
};
}
throw new ApplicationException(UNRECOGNISED_NATIVE_TYPE + rng);
}
/**
* Wrap the random generator with a new instance that will combine the bits
* using a {@code xor} operation with the output from {@link ThreadLocalRandom}.
* The input must be either a {@link RandomIntSource} or {@link RandomLongSource}.
*
* <pre>
* {@code
* ThreadLocalRandom.current().nextInt() ^ rng.nextInt()
* }
* </pre>
*
* @param rng The random generator.
* @return the combined random generator.
* @throws ApplicationException If the input source native type is not recognised.
*/
static UniformRandomProvider createThreadLocalRandomProvider(final UniformRandomProvider rng) {
if (rng instanceof RandomIntSource) {
return new IntProvider() {
@Override
public int next() {
return ThreadLocalRandom.current().nextInt() ^ rng.nextInt();
}
@Override
public String toString() {
return TLR_MIXED + rng.toString();
}
};
}
if (rng instanceof RandomLongSource) {
return new LongProvider() {
@Override
public long next() {
return ThreadLocalRandom.current().nextLong() ^ rng.nextLong();
}
@Override
public String toString() {
return TLR_MIXED + rng.toString();
}
};
}
throw new ApplicationException(UNRECOGNISED_NATIVE_TYPE + rng);
}
/**
* Combine the two random generators using a {@code xor} operations.
* The input must be either a {@link RandomIntSource} or {@link RandomLongSource}.
* The returned type will match the native output type of {@code rng1}.
*
* <pre>
* {@code
* rng1.nextInt() ^ rng2.nextInt()
* }
* </pre>
*
* @param rng1 The first random generator.
* @param rng2 The second random generator.
* @return the combined random generator.
* @throws ApplicationException If the input source native type is not recognised.
*/
static UniformRandomProvider createXorProvider(final UniformRandomProvider rng1,
final UniformRandomProvider rng2) {
if (rng1 instanceof RandomIntSource) {
return new IntProvider() {
@Override
public int next() {
return rng1.nextInt() ^ rng2.nextInt();
}
@Override
public String toString() {
return rng1.toString() + XOR + rng2.toString();
}
};
}
if (rng1 instanceof RandomLongSource) {
return new LongProvider() {
@Override
public long next() {
return rng1.nextLong() ^ rng2.nextLong();
}
@Override
public String toString() {
return rng1.toString() + XOR + rng2.toString();
}
};
}
throw new ApplicationException(UNRECOGNISED_NATIVE_TYPE + rng1);
}
/**
* Create a new instance to write batches of byte data from the specified RNG to the
* specified output stream.
*
* <p>This will detect the native output type of the RNG and create an appropriate
* data output for the raw bytes. The input must be either a {@link RandomIntSource} or
* {@link RandomLongSource}.</p>
*
* <p>If the RNG is a {@link RandomLongSource} then the byte output can be 32-bit or 64-bit.
* If 32-bit then the 64-bit output will be written as if 2 {@code int} values were generated
* sequentially from the upper then lower 32-bits of the {@code long}. This setting is
* significant depending on the byte order. If using the Java standard big-endian
* representation the flag has no effect and the output will be the same. If using little
* endian the output bytes will be written as:</p>
*
* <pre>
* 76543210 -> 4567 0123
* </pre>
*
* @param rng The random generator.
* @param raw64 Set to true for 64-bit byte output.
* @param out Output stream.
* @param byteSize Number of bytes values to write.
* @param byteOrder Byte order.
* @return the data output
* @throws ApplicationException If the input source native type is not recognised.
*/
static RngDataOutput createDataOutput(final UniformRandomProvider rng, boolean raw64,
OutputStream out, int byteSize, ByteOrder byteOrder) {
if (rng instanceof RandomIntSource) {
return RngDataOutput.ofInt(out, byteSize / 4, byteOrder);
}
if (rng instanceof RandomLongSource) {
return raw64 ?
RngDataOutput.ofLong(out, byteSize / 8, byteOrder) :
RngDataOutput.ofLongAsInt(out, byteSize / 8, byteOrder);
}
throw new ApplicationException(UNRECOGNISED_NATIVE_TYPE + rng);
}
/**
* Parses the argument into an object suitable for the RandomSource constructor. Supports:
*
* <ul>
* <li>Integer
* </ul>
*
* @param argument the argument
* @return the object
* @throws ApplicationException If the argument is not recognised
*/
static Object parseArgument(String argument) {
try {
// Currently just support TWO_CMRES_SELECT which uses integers.
// Future RandomSource implementations may require other parsing, for example
// recognising a long by the suffix 'L'. This functionality
// could use Commons Lang NumberUtils.createNumber(String).
return Integer.parseInt(argument);
} catch (final NumberFormatException ex) {
throw new ApplicationException("Failed to parse RandomSource argument: " + argument, ex);
}
}
}