| /* |
| * 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 bit-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 source64 mode. */ |
| private static final String UNRECOGNISED_SOURCE_64_MODE = "Unrecognised source64 mode: "; |
| /** Message for an unrecognised native output type. */ |
| private static final String UNRECOGNISED_NATIVE_TYPE = "Unrecognised native output type: "; |
| /** The source64 mode for the default LongProvider caching implementation. */ |
| private static final Source64Mode SOURCE_64_DEFAULT = Source64Mode.LO_HI; |
| |
| /** No public construction. */ |
| private RNGUtils() {} |
| |
| /** |
| * Gets the source64 mode for the default caching implementation in {@link LongProvider}. |
| * |
| * @return the source64 default mode |
| */ |
| static Source64Mode getSource64Default() { |
| return SOURCE_64_DEFAULT; |
| } |
| |
| /** |
| * 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 {@link RandomLongSource} with an {@link IntProvider} that will use the |
| * specified part of {@link RandomLongSource#next()} to create the int value. |
| * |
| * @param <R> The type of the generator. |
| * @param rng The random generator. |
| * @param mode the mode |
| * @return the int random generator. |
| * @throws ApplicationException If the input source native type is not 64-bit. |
| */ |
| static <R extends RandomLongSource & UniformRandomProvider> |
| UniformRandomProvider createIntProvider(final R rng, Source64Mode mode) { |
| switch (mode) { |
| case INT: |
| return createIntProvider(rng); |
| case LO_HI: |
| return createLongLowerUpperBitsIntProvider(rng); |
| case HI_LO: |
| return createLongUpperLowerBitsIntProvider(rng); |
| case HI: |
| return createLongUpperBitsIntProvider(rng); |
| case LO: |
| return createLongLowerBitsIntProvider(rng); |
| case LONG: |
| default: |
| throw new IllegalArgumentException("Unsupported mode " + mode); |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| private 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 lower then upper |
| * 32-bits from {@link UniformRandomProvider#nextLong()}. |
| * An input {@link RandomIntSource} is returned unmodified. |
| * |
| * @param rng The random generator. |
| * @return the int random generator. |
| */ |
| private static UniformRandomProvider createLongLowerUpperBitsIntProvider(final RandomLongSource rng) { |
| return new IntProvider() { |
| private long source = -1; |
| |
| @Override |
| public int next() { |
| long next = source; |
| if (next < 0) { |
| // refill |
| next = rng.next(); |
| // store hi |
| source = next >>> 32; |
| // extract low |
| return (int) next; |
| } |
| final int v = (int) next; |
| // reset |
| source = -1; |
| return v; |
| } |
| |
| @Override |
| public String toString() { |
| return "Long lower-upper bits " + rng.toString(); |
| } |
| }; |
| } |
| |
| /** |
| * Wrap the random generator with an {@link IntProvider} that will use the lower then upper |
| * 32-bits from {@link UniformRandomProvider#nextLong()}. |
| * An input {@link RandomIntSource} is returned unmodified. |
| * |
| * @param rng The random generator. |
| * @return the int random generator. |
| */ |
| private static UniformRandomProvider createLongUpperLowerBitsIntProvider(final RandomLongSource rng) { |
| return new IntProvider() { |
| private long source = -1; |
| |
| @Override |
| public int next() { |
| long next = source; |
| if (next < 0) { |
| // refill |
| next = rng.next(); |
| // store low |
| source = next & 0xffff_ffffL; |
| // extract hi |
| return (int) (next >>> 32); |
| } |
| final int v = (int) next; |
| // reset |
| source = -1; |
| return v; |
| } |
| |
| @Override |
| public String toString() { |
| return "Long upper-lower bits " + rng.toString(); |
| } |
| }; |
| } |
| |
| /** |
| * 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. |
| */ |
| private static UniformRandomProvider createLongUpperBitsIntProvider(final RandomLongSource rng) { |
| return new IntProvider() { |
| @Override |
| public int next() { |
| return (int) (rng.next() >>> 32); |
| } |
| |
| @Override |
| public String toString() { |
| return "Long upper-bits " + rng.toString(); |
| } |
| }; |
| } |
| |
| /** |
| * 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. |
| */ |
| private static UniformRandomProvider createLongLowerBitsIntProvider(final RandomLongSource rng) { |
| return new IntProvider() { |
| @Override |
| public int next() { |
| return (int) rng.next(); |
| } |
| |
| @Override |
| public String toString() { |
| return "Long lower-bits " + rng.toString(); |
| } |
| }; |
| } |
| |
| /** |
| * 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 {@code long} (order depends on the source64 mode). This setting is |
| * significant depending on the byte order. For example for a high-low source64 mode and |
| * using the Java standard big-endian representation the output is the same as the raw 64-bit |
| * output. If using little endian the output bytes will be written as:</p> |
| * |
| * <pre> |
| * 76543210 -> 4567 0123 |
| * </pre> |
| * |
| * <h2>Note</h2> |
| * |
| * <p>The output from an implementation of RandomLongSource from the RNG core package |
| * may output the long bits as integers: in high-low order; in low-high order; using |
| * only the low bits; using only the high bits; or other combinations. This method |
| * allows testing the long output as if it were two int outputs, i.e. using the full |
| * bit output of a long provider with a stress test application that targets 32-bit |
| * random values (e.g. Test U01). |
| * |
| * <p>The results of stress testing can be used to determine if the provider |
| * implementation can use the upper, lower or both parts of the long output for int |
| * generation. In the case of the combined upper-lower output it is not expected that |
| * the order low-high or high-low is important given the stress test will consume |
| * thousands of numbers per test. The default 32-bit mode for a 64-bit source is high-low |
| * for backwards compatibility. |
| * |
| * @param rng The random generator. |
| * @param source64 The output mode for a 64-bit source |
| * @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; or if |
| * the mode for a RandomLongSource is not one of: raw; hi-lo; or lo-hi. |
| */ |
| static RngDataOutput createDataOutput(final UniformRandomProvider rng, Source64Mode source64, |
| OutputStream out, int byteSize, ByteOrder byteOrder) { |
| if (rng instanceof RandomIntSource) { |
| return RngDataOutput.ofInt(out, byteSize / 4, byteOrder); |
| } |
| if (rng instanceof RandomLongSource) { |
| switch (source64) { |
| case HI_LO: |
| return RngDataOutput.ofLongAsHLInt(out, byteSize / 8, byteOrder); |
| case LO_HI: |
| return RngDataOutput.ofLongAsLHInt(out, byteSize / 8, byteOrder); |
| case LONG: |
| return RngDataOutput.ofLong(out, byteSize / 8, byteOrder); |
| // Note other options should have already been converted to an IntProvider |
| case INT: |
| case LO: |
| case HI: |
| default: |
| throw new ApplicationException(UNRECOGNISED_SOURCE_64_MODE + source64); |
| } |
| } |
| 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); |
| } |
| } |
| } |