/*==========================================================================*\ | $Id: TestableRandom.java,v 1.3 2010/02/25 19:27:24 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2007-2010 Virginia Tech | | This file is part of the Student-Library. | | The Student-Library is free software; you can redistribute it and/or | modify it under the terms of the GNU Lesser General Public License as | published by the Free Software Foundation; either version 3 of the | License, or (at your option) any later version. | | The Student-Library is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU Lesser General Public License for more details. | | You should have received a copy of the GNU Lesser General Public License | along with the Student-Library; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package student; import java.util.Random; //------------------------------------------------------------------------- /** * This subclass of {@link Random} adds extra methods useful * for testing purposes. Normally, you might generate a new random number * by calling {@link #nextInt()}, {@link #nextDouble()}, or one of the * other generation methods provided by {@link Random}. Normally, * this intentionally makes your code behave in a random way, which may * make it harder to test. This class allows you to control directly * the sequence of numbers that will be generated by such calls. * * <p>Suppose your code is written this way:</p> * <pre> * Random random = new TestableRandom(); * ... * int x = random.nextInt(64); * </pre> * * <p>You can then write test cases that look like this:</p> * <pre> * public void testSomeFeature() * { * // Set the return values for the next 6 calls to nextInt(), * // No matter which instance of TestableRandom the method is called on * TestableRandom.setNextInts(5, 10, 22, 13, 12, 47); * * // Perform tests, knowing in advance the exact sequence of numbers * // That will now be generated * } * </pre> * * <p>This class provides separate methods to preset the sequence of * booleans, ints, doubles, floats, bytes, or Gaussian-distributed doubles * that will be generated. You can pass in as many specific values to * the setNext...() methods that you like, or you can even pass in an * array:</p> * * <pre> * int[] someValues = new int[] { 1, 2, 3, 4, 5, 6, 7 }; * TestableRandom.setNextInts(someValues); * </pre> * * @author Stephen Edwards * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.3 $, $Date: 2010/02/25 19:27:24 $ */ public class TestableRandom extends Random { //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Creates a new random number generator. This constructor sets the seed * of the random number generator to a value very likely to be distinct * from any other invocation of this constructor. */ public TestableRandom() { super(); } // ---------------------------------------------------------- /** * Creates a new random number generator using a single long seed. The * seed is the initial value of the internal state of the pseudorandom * number generator which is maintained by method {@link #next(int)}. * * <p>The invocation new Random(seed) is equivalent to:</p> * <pre> * Random rnd = new Random(); * rnd.setSeed(seed); * </pre> * * @param seed the initial seed * @see Random#setSeed(long) */ public TestableRandom(long seed) { super(seed); } //~ Public static methods ................................................. // ---------------------------------------------------------- /** * This method allows one to provide a predefined series of values that * will override the results provided by {@link #nextInt()} and * {@link #nextInt(int)}. This is useful during testing, when you want * to control the results generated by a random number generator. If * you do not use this method, {@link #nextInt()} and {@link #nextInt(int)} * behave normally. * * <p>Note that the sequence of int values you provide will be shared * by all instances of this class--so, no matter how many TestableRandom * instances you have created, the sequence of random numbers generated * by calls to their methods will be determined by what you pass in * here.</p> * * <p>If previous values from an earlier call to this method have not * yet been used, they will be replaced by any parameters you provide * in the next call to this method. If previous values from an earlier * call to this method have not yet been used, and you provide no * arguments in your next call to this method, those unused values will * be replaced with nothing, so normal pseudorandom generation behavior * will resume immediately.</p> * * @param values a sequence of int values to use as the results in * subsequent calls to {@link #nextInt()} or {@link #nextInt(int)} */ public static void setNextInts(int ... values) { synchronized (nextValues) { if (values != null && values.length > 0) { nextValues.nextInts = values; } else { nextValues.nextInts = null; } nextValues.nextIntPos = 0; } } // ---------------------------------------------------------- /** * This method allows one to provide a predefined series of values that * will override the results provided by {@link #nextLong()}. This is * useful during testing, when you want to control the results generated * by a random number generator. If you do not use this method, * {@link #nextLong()} behaves normally. * * <p>Note that the sequence of long values you provide will be shared * by all instances of this class--so, no matter how many TestableRandom * instances you have created, the sequence of random numbers generated * by calls to their methods will be determined by what you pass in * here.</p> * * <p>If previous values from an earlier call to this method have not * yet been used, they will be replaced by any parameters you provide * in the next call to this method. If previous values from an earlier * call to this method have not yet been used, and you provide no * arguments in your next call to this method, those unused values will * be replaced with nothing, so normal pseudorandom generation behavior * will resume immediately.</p> * * @param values a sequence of long values to use as the results in * subsequent calls to {@link #nextLong()} */ public static void setNextLongs(long ... values) { synchronized (nextValues) { if (values != null && values.length > 0) { nextValues.nextLongs = values; } else { nextValues.nextLongs = null; } nextValues.nextLongPos = 0; } } // ---------------------------------------------------------- /** * This method allows one to provide a predefined series of values that * will override the results provided by {@link #nextBoolean()}. This is * useful during testing, when you want to control the results generated * by a random number generator. If you do not use this method, * {@link #nextBoolean()} behaves normally. * * <p>Note that the sequence of boolean values you provide will be shared * by all instances of this class--so, no matter how many TestableRandom * instances you have created, the sequence of random booleans generated * by calls to their methods will be determined by what you pass in * here.</p> * * <p>If previous values from an earlier call to this method have not * yet been used, they will be replaced by any parameters you provide * in the next call to this method. If previous values from an earlier * call to this method have not yet been used, and you provide no * arguments in your next call to this method, those unused values will * be replaced with nothing, so normal pseudorandom generation behavior * will resume immediately.</p> * * @param values a sequence of boolean values to use as the results in * subsequent calls to {@link #nextBoolean()} */ public static void setNextBooleans(boolean ... values) { synchronized (nextValues) { if (values != null && values.length > 0) { nextValues.nextBooleans = values; } else { nextValues.nextBooleans = null; } nextValues.nextBooleanPos = 0; } } // ---------------------------------------------------------- /** * This method allows one to provide a predefined series of values that * will override the results provided by {@link #nextFloat()}. This is * useful during testing, when you want to control the results generated * by a random number generator. If you do not use this method, * {@link #nextFloat()} behaves normally. * * <p>Note that the sequence of float values you provide will be shared * by all instances of this class--so, no matter how many TestableRandom * instances you have created, the sequence of random numbers generated * by calls to their methods will be determined by what you pass in * here.</p> * * <p>If previous values from an earlier call to this method have not * yet been used, they will be replaced by any parameters you provide * in the next call to this method. If previous values from an earlier * call to this method have not yet been used, and you provide no * arguments in your next call to this method, those unused values will * be replaced with nothing, so normal pseudorandom generation behavior * will resume immediately.</p> * * @param values a sequence of float values to use as the results in * subsequent calls to {@link #nextFloat()} */ public static void setNextFloats(float ... values) { synchronized (nextValues) { if (values != null && values.length > 0) { nextValues.nextFloats = values; } else { nextValues.nextFloats = null; } nextValues.nextFloatPos = 0; } } // ---------------------------------------------------------- /** * This method allows one to provide a predefined series of values that * will override the results provided by {@link #nextDouble()}. This is * useful during testing, when you want to control the results generated * by a random number generator. If you do not use this method, * {@link #nextDouble()} behaves normally. * * <p>Note that the sequence of double values you provide will be shared * by all instances of this class--so, no matter how many TestableRandom * instances you have created, the sequence of random numbers generated * by calls to their methods will be determined by what you pass in * here.</p> * * <p>If previous values from an earlier call to this method have not * yet been used, they will be replaced by any parameters you provide * in the next call to this method. If previous values from an earlier * call to this method have not yet been used, and you provide no * arguments in your next call to this method, those unused values will * be replaced with nothing, so normal pseudorandom generation behavior * will resume immediately.</p> * * @param values a sequence of double values to use as the results in * subsequent calls to {@link #nextDouble()} */ public static void setNextDoubles(double ... values) { synchronized (nextValues) { if (values != null && values.length > 0) { nextValues.nextDoubles = values; } else { nextValues.nextDoubles = null; } nextValues.nextDoublePos = 0; } } // ---------------------------------------------------------- /** * This method allows one to provide a predefined series of values that * will override the results provided by {@link #nextGaussian()}. This is * useful during testing, when you want to control the results generated * by a random number generator. If you do not use this method, * {@link #nextGaussian()} behaves normally. * * <p>Note that the sequence of double values you provide will be shared * by all instances of this class--so, no matter how many TestableRandom * instances you have created, the sequence of random numbers generated * by calls to their methods will be determined by what you pass in * here.</p> * * <p>If previous values from an earlier call to this method have not * yet been used, they will be replaced by any parameters you provide * in the next call to this method. If previous values from an earlier * call to this method have not yet been used, and you provide no * arguments in your next call to this method, those unused values will * be replaced with nothing, so normal pseudorandom generation behavior * will resume immediately.</p> * * @param values a sequence of double values to use as the results in * subsequent calls to {@link #nextGaussian()} */ public static void setNextGaussians(double ... values) { synchronized (nextValues) { if (values != null && values.length > 0) { nextValues.nextGaussians = values; } else { nextValues.nextGaussians = null; } nextValues.nextGaussianPos = 0; } } // ---------------------------------------------------------- /** * This method allows one to provide a predefined series of values that * will override the results provided by {@link #nextBytes(byte[])}. * This is useful during testing, when you want to control the results * generated by a random number generator. If you do not use this method, * {@link #nextBytes(byte[])} behaves normally. * * <p>Note that the sequence of byte values you provide will be shared * by all instances of this class--so, no matter how many TestableRandom * instances you have created, the sequence of random numbers generated * by calls to their methods will be determined by what you pass in * here.</p> * * <p>If previous values from an earlier call to this method have not * yet been used, they will be replaced by any parameters you provide * in the next call to this method. If previous values from an earlier * call to this method have not yet been used, and you provide no * arguments in your next call to this method, those unused values will * be replaced with nothing, so normal pseudorandom generation behavior * will resume immediately.</p> * * @param values a sequence of byte values to use as the results in * subsequent calls to {@link #nextBytes(byte[])} */ public static void setNextBytes(byte ... values) { synchronized (nextValues) { if (values != null && values.length > 0) { nextValues.nextBytes = values; } else { nextValues.nextBytes = null; } nextValues.nextBytePos = 0; } } //~ Public instance methods ............................................... // ---------------------------------------------------------- /** * Returns the next pseudorandom, uniformly distributed {@code int} * value from this random number generator's sequence. The general * contract of {@code nextInt} is that one {@code int} value is * pseudorandomly generated and returned. All 2<font size="-1"><sup>32 * </sup></font> possible {@code int} values are produced with * (approximately) equal probability. * * <p>If {@link #setNextInts(int...)} has been called, the next available * value from the provided sequence will be returned until that sequence * is exhausted. One all provided values have been returned, then * true pseudorandom generation will resume.</p> * * @return the next pseudorandom, uniformly distributed {@code int} * value from this random number generator's sequence */ public int nextInt() { synchronized (nextValues) { if (nextValues.nextInts != null) { int result = nextValues.nextInts[nextValues.nextIntPos++]; if (nextValues.nextIntPos >= nextValues.nextInts.length) { nextValues.nextInts = null; nextValues.nextIntPos = 0; } return result; } } return super.nextInt(); } // ---------------------------------------------------------- /** * Returns a pseudorandom, uniformly distributed {@code int} value * between 0 (inclusive) and the specified value (exclusive), drawn from * this random number generator's sequence. The general contract of * {@code nextInt} is that one {@code int} value in the specified range * is pseudorandomly generated and returned. All {@code n} possible * {@code int} values are produced with (approximately) equal * probability. * * <p>If {@link #setNextInts(int...)} has been called, the next available * value from the provided sequence (modulo n) will be returned until that * sequence is exhausted. One all provided values have been returned, then * true pseudorandom generation will resume.</p> * * @param n the bound on the random number to be returned. Must be * positive. * @return the next pseudorandom, uniformly distributed {@code int} * value between {@code 0} (inclusive) and {@code n} (exclusive) * from this random number generator's sequence * @exception IllegalArgumentException if n is not positive */ public int nextInt(int n) { if (n <= 0) { throw new IllegalArgumentException("n must be positive"); } synchronized (nextValues) { if (nextValues.nextInts != null) { int result = nextValues.nextInts[nextValues.nextIntPos++]; if (nextValues.nextIntPos >= nextValues.nextInts.length) { nextValues.nextInts = null; nextValues.nextIntPos = 0; } return result % n; } } return super.nextInt(n); } // ---------------------------------------------------------- /** * Returns the next pseudorandom, uniformly distributed {@code long} * value from this random number generator's sequence. The general * contract of {@code nextLong} is that one {@code long} value is * pseudorandomly generated and returned. * * <p>If {@link #setNextLongs(long...)} has been called, the next available * value from the provided sequence will be returned until that sequence * is exhausted. One all provided values have been returned, then * true pseudorandom generation will resume.</p> * * @return the next pseudorandom, uniformly distributed {@code long} * value from this random number generator's sequence */ public long nextLong() { synchronized (nextValues) { if (nextValues.nextLongs != null) { long result = nextValues.nextLongs[nextValues.nextLongPos++]; if (nextValues.nextLongPos >= nextValues.nextLongs.length) { nextValues.nextLongs = null; nextValues.nextLongPos = 0; } return result; } } return super.nextLong(); } // ---------------------------------------------------------- /** * Returns the next pseudorandom, uniformly distributed * {@code boolean} value from this random number generator's * sequence. The general contract of {@code nextBoolean} is that one * {@code boolean} value is pseudorandomly generated and returned. The * values {@code true} and {@code false} are produced with * (approximately) equal probability. * * <p>If {@link #setNextBooleans(boolean...)} has been called, the next * available value from the provided sequence will be returned until that * sequence is exhausted. One all provided values have been returned, then * true pseudorandom generation will resume.</p> * * @return the next pseudorandom, uniformly distributed {@code boolean} * value from this random number generator's sequence */ public boolean nextBoolean() { synchronized (nextValues) { if (nextValues.nextBooleans != null) { boolean result = nextValues.nextBooleans[nextValues.nextBooleanPos++]; if (nextValues.nextBooleanPos >= nextValues.nextBooleans.length) { nextValues.nextBooleans = null; nextValues.nextBooleanPos = 0; } return result; } } return super.nextBoolean(); } // ---------------------------------------------------------- /** * Returns the next pseudorandom, uniformly distributed * {@code double} value between {@code 0.0} and * {@code 1.0} from this random number generator's sequence. * * <p>If {@link #setNextDoubles(double...)} has been called, the next * available value from the provided sequence will be returned until that * sequence is exhausted. One all provided values have been returned, then * true pseudorandom generation will resume.</p> * * @return the next pseudorandom, uniformly distributed {@code double} * value from this random number generator's sequence */ public double nextDouble() { synchronized (nextValues) { if (nextValues.nextDoubles != null) { double result = nextValues.nextDoubles[nextValues.nextDoublePos++]; if (nextValues.nextDoublePos >= nextValues.nextDoubles.length) { nextValues.nextDoubles = null; nextValues.nextDoublePos = 0; } return result; } } return super.nextDouble(); } // ---------------------------------------------------------- /** * Returns the next pseudorandom, Gaussian ("normally") distributed * {@code double} value with mean {@code 0.0} and standard * deviation {@code 1.0} from this random number generator's sequence. * * <p>If {@link #setNextGaussians(double...)} has been called, the next * available value from the provided sequence will be returned until that * sequence is exhausted. One all provided values have been returned, then * true pseudorandom generation will resume.</p> * * @return the next pseudorandom, Gaussian ("normally") distributed * {@code double} value with mean {@code 0.0} and * standard deviation {@code 1.0} from this random number * generator's sequence */ public double nextGaussian() { synchronized (nextValues) { if (nextValues.nextGaussians != null) { double result = nextValues.nextGaussians[nextValues.nextGaussianPos++]; if (nextValues.nextGaussianPos >= nextValues.nextGaussians.length) { nextValues.nextGaussians = null; nextValues.nextGaussianPos = 0; } return result; } } return super.nextGaussian(); } // ---------------------------------------------------------- /** * Generates random bytes and places them into a user-supplied * byte array. The number of random bytes produced is equal to * the length of the byte array. * * <p>If {@link #setNextBytes(byte...)} has been called, unused * values from the provided sequence will be used to fill the provided * array until that sequence is exhausted. One all provided values have * been used up, true pseudorandom generation will resume for filling * any remaining slots in this or future calls.</p> * * @param bytes the byte array to fill with random bytes * @throws NullPointerException if the byte array is null */ public void nextBytes(byte[] bytes) { synchronized (nextValues) { if (nextValues.nextBytes != null) { int remaining = nextValues.nextBytes.length - nextValues.nextBytePos; if (remaining >= bytes.length) { System.arraycopy( nextValues.nextBytes, nextValues.nextBytePos, bytes, 0, bytes.length); nextValues.nextBytePos += bytes.length; } else { int size = nextValues.nextBytes.length - nextValues.nextBytePos; System.arraycopy( nextValues.nextBytes, nextValues.nextBytePos, bytes, 0, size); nextValues.nextBytePos += size; byte[] remainder = new byte[bytes.length - size]; super.nextBytes(remainder); System.arraycopy( remainder, 0, bytes, bytes.length - remainder.length, remainder.length); } if (nextValues.nextBytePos >= nextValues.nextBytes.length) { nextValues.nextBytes = null; nextValues.nextBytePos = 0; } return; } } super.nextBytes(bytes); } //~ Private declarations .................................................. private static class NextValues { byte[] nextBytes; int nextBytePos; int[] nextInts; int nextIntPos; long[] nextLongs; int nextLongPos; boolean[] nextBooleans; int nextBooleanPos; float[] nextFloats; int nextFloatPos; double[] nextDoubles; int nextDoublePos; double[] nextGaussians; int nextGaussianPos; } //~ Instance/static variables ............................................. private static volatile NextValues nextValues = new NextValues(); private static final long serialVersionUID = 6662016631254672060L; }