Skip to content

Commit

Permalink
Updated tests for XorShiRo generators for zero and non-zero seeds.
Browse files Browse the repository at this point in the history
  • Loading branch information
aherbert committed May 18, 2019
1 parent 44111d3 commit 99f91db
Show file tree
Hide file tree
Showing 13 changed files with 299 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,187 @@

import org.junit.Assert;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import org.apache.commons.rng.UniformRandomProvider;

/**
* Utility class for testing random generators.
*/
public class RandomAssert {

/**
* Assert that the random generator produces the expected output.
*
* @param expected Expected output.
* @param rng Random generator.
*/
public static void assertEquals(int[] expected, UniformRandomProvider rng) {
for (int i = 0; i < expected.length; i++) {
Assert.assertEquals("Value at position " + i, expected[i], rng.nextInt());
}
}

/**
* Assert that the random generator produces the expected output.
*
* @param expected Expected output.
* @param rng Random generator.
*/
public static void assertEquals(long[] expected, UniformRandomProvider rng) {
for (int i = 0; i < expected.length; i++) {
Assert.assertEquals("Value at position " + i, expected[i], rng.nextLong());
}
}

/**
* Assert that the two random generators produce the same output for
* {@link UniformRandomProvider#nextInt()} over the given number of cycles.
*
* @param cycles Number of cycles.
* @param rng1 Random generator 1.
* @param rng2 Random generator 2.
*/
public static void assertNextIntEquals(int cycles, UniformRandomProvider rng1, UniformRandomProvider rng2) {
for (int i = 0; i < cycles; i++) {
Assert.assertEquals("Value at position " + i, rng1.nextInt(), rng2.nextInt());
}
}

/**
* Assert that the two random generators produce the same output for
* {@link UniformRandomProvider#nextLong()} over the given number of cycles.
*
* @param cycles Number of cycles.
* @param rng1 Random generator 1.
* @param rng2 Random generator 2.
*/
public static void assertNextLongEquals(int cycles, UniformRandomProvider rng1, UniformRandomProvider rng2) {
for (int i = 0; i < cycles; i++) {
Assert.assertEquals("Value at position " + i, rng1.nextLong(), rng2.nextLong());
}
}

/**
* Assert that the random generator produces zero output for
* {@link UniformRandomProvider#nextInt()} over the given number of cycles.
* This is used to test a poorly seeded generator cannot generate random output.
*
* @param rng Random generator.
* @param cycles Number of cycles.
*/
public static void assertNextIntZeroOutput(UniformRandomProvider rng, int cycles) {
for (int i = 0; i < cycles; i++) {
Assert.assertEquals("Expected the generator to output zeros", 0, rng.nextInt());
}
}

/**
* Assert that the random generator produces zero output for
* {@link UniformRandomProvider#nextLong()} over the given number of cycles.
* This is used to test a poorly seeded generator cannot generate random output.
*
* @param rng Random generator.
* @param cycles Number of cycles.
*/
public static void assertNextLongZeroOutput(UniformRandomProvider rng, int cycles) {
for (int i = 0; i < cycles; i++) {
Assert.assertEquals("Expected the generator to output zeros", 0, rng.nextLong());
}
}

/**
* Assert that following a set number of warm-up cycles the random generator produces
* at least one non-zero output for {@link UniformRandomProvider#nextLong()} over the
* given number of test cycles. This is used to test a poorly seeded generator can recover
* internal state to generate "random" output.
*
* @param rng Random generator.
* @param warmupCycles Number of warm-up cycles.
* @param testCycles Number of test cycles.
*/
public static void assertNextLongNonZeroOutput(UniformRandomProvider rng,
int warmupCycles, int testCycles) {
for (int i = 0; i < warmupCycles; i++) {
rng.nextLong();
}
for (int i = 0; i < testCycles; i++) {
if (rng.nextLong() != 0L) {
return;
}
}
Assert.fail("No non-zero output after " + (warmupCycles + testCycles) + " cycles");
}

/**
* Assert that the random generator created using an {@code int[]} seed with a
* single bit set is functional. This is tested using the
* {@link #assertNextLongNonZeroOutput(UniformRandomProvider, int, int)} using
* two times the seed size as the warm-up and test cycles.
*
* @param type Class of the generator.
* @param size Seed size.
*/
public static <T extends UniformRandomProvider> void
assertIntArrayConstructorWithSingleBitSeedIsFunctional(Class<T> type, int size) {
try {
// Find the int[] constructor
final Constructor<T> constructor = type.getConstructor(int[].class);
final int[] seed = new int[size];
for (int i = 0; i < size; i++) {
seed[i] = 1;
for (int j = 0; j < 32; j++) {
final UniformRandomProvider rng = constructor.newInstance(seed);
RandomAssert.assertNextLongNonZeroOutput(rng, 2 * size, 2 * size);
// Eventually rolls-over to reset to zero
seed[i] <<= 1;
}
Assert.assertEquals("Seed element was not reset", 0, seed[i]);
}
} catch (IllegalAccessException ex) {
Assert.fail(ex.getMessage());
} catch (NoSuchMethodException ex) {
Assert.fail(ex.getMessage());
} catch (InstantiationException ex) {
Assert.fail(ex.getMessage());
} catch (InvocationTargetException ex) {
Assert.fail(ex.getMessage());
}
}

/**
* Assert that the random generator created using a {@code long[]} seed with a
* single bit set is functional. This is tested using the
* {@link #assertNextLongNonZeroOutput(UniformRandomProvider, int, int)} using two times the seed
* size as the warm-up and test cycles.
*
* @param type Class of the generator.
* @param size Seed size.
*/
public static <T extends UniformRandomProvider> void
assertLongArrayConstructorWithSingleBitSeedIsFunctional(Class<T> type, int size) {
try {
// Find the long[] constructor
final Constructor<T> constructor = type.getConstructor(long[].class);
final long[] seed = new long[size];
for (int i = 0; i < size; i++) {
seed[i] = 1;
for (int j = 0; j < 64; j++) {
final UniformRandomProvider rng = constructor.newInstance(seed);
RandomAssert.assertNextLongNonZeroOutput(rng, 2 * size, 2 * size);
// Eventually rolls-over to reset to zero
seed[i] <<= 1;
}
Assert.assertEquals("Seed element was not reset", 0L, seed[i]);
}
} catch (IllegalAccessException ex) {
Assert.fail(ex.getMessage());
} catch (NoSuchMethodException ex) {
Assert.fail(ex.getMessage());
} catch (InstantiationException ex) {
Assert.fail(ex.getMessage());
} catch (InvocationTargetException ex) {
Assert.fail(ex.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
package org.apache.commons.rng.core.source32;

import org.apache.commons.rng.core.RandomAssert;
import org.apache.commons.rng.core.util.NumberFactory;
import org.junit.Assert;
import org.junit.Test;

public class XoRoShiRo64StarStarTest {
/** The size of the array seed. */
private static final int SEED_SIZE = 2;

@Test
public void testReferenceCode() {
/*
Expand Down Expand Up @@ -49,13 +50,13 @@ public void testReferenceCode() {
}

@Test
public void testConstructorWithZeroSeed() {
// This is allowed even though the generator is non-functional
final int size = 2;
final XoRoShiRo64StarStar rng = new XoRoShiRo64StarStar(new int[size]);
for (int i = size * 2; i-- != 0; ) {
Assert.assertEquals("Expected the generator to be broken", 0, rng.nextInt());
}
public void testConstructorWithZeroSeedIsNonFunctional() {
RandomAssert.assertNextIntZeroOutput(new XoRoShiRo64StarStar(new int[SEED_SIZE]), 2 * SEED_SIZE);
}

@Test
public void testConstructorWithSingleBitSeedIsFunctional() {
RandomAssert.assertIntArrayConstructorWithSingleBitSeedIsFunctional(XoRoShiRo64StarStar.class, SEED_SIZE);
}

@Test
Expand All @@ -71,8 +72,6 @@ public void testElementConstructor() {
};
final XoRoShiRo64StarStar rng1 = new XoRoShiRo64StarStar(seed);
final XoRoShiRo64StarStar rng2 = new XoRoShiRo64StarStar(seed[0], seed[1]);
for (int i = seed.length * 2; i-- != 0; ) {
Assert.assertEquals(rng1.nextInt(), rng2.nextInt());
}
RandomAssert.assertNextIntEquals(seed.length * 2, rng1, rng2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
package org.apache.commons.rng.core.source32;

import org.apache.commons.rng.core.RandomAssert;
import org.apache.commons.rng.core.util.NumberFactory;
import org.junit.Assert;
import org.junit.Test;

public class XoRoShiRo64StarTest {
/** The size of the array seed. */
private static final int SEED_SIZE = 2;

@Test
public void testReferenceCode() {
/*
Expand Down Expand Up @@ -49,13 +50,13 @@ public void testReferenceCode() {
}

@Test
public void testConstructorWithZeroSeed() {
// This is allowed even though the generator is non-functional
final int size = 2;
final XoRoShiRo64Star rng = new XoRoShiRo64Star(new int[size]);
for (int i = size * 2; i-- != 0; ) {
Assert.assertEquals("Expected the generator to be broken", 0, rng.nextInt());
}
public void testConstructorWithZeroSeedIsNonFunctional() {
RandomAssert.assertNextIntZeroOutput(new XoRoShiRo64Star(new int[SEED_SIZE]), 2 * SEED_SIZE);
}

@Test
public void testConstructorWithSingleBitSeedIsFunctional() {
RandomAssert.assertIntArrayConstructorWithSingleBitSeedIsFunctional(XoRoShiRo64Star.class, SEED_SIZE);
}

@Test
Expand All @@ -71,8 +72,6 @@ public void testElementConstructor() {
};
final XoRoShiRo64Star rng1 = new XoRoShiRo64Star(seed);
final XoRoShiRo64Star rng2 = new XoRoShiRo64Star(seed[0], seed[1]);
for (int i = seed.length * 2; i-- != 0; ) {
Assert.assertEquals(rng1.nextInt(), rng2.nextInt());
}
RandomAssert.assertNextIntEquals(seed.length * 2, rng1, rng2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
package org.apache.commons.rng.core.source32;

import org.apache.commons.rng.core.RandomAssert;
import org.junit.Assert;
import org.junit.Test;

public class XoShiRo128PlusTest {
/** The size of the array seed. */
private static final int SEED_SIZE = 4;

@Test
public void testReferenceCode() {
/*
Expand Down Expand Up @@ -48,13 +50,13 @@ public void testReferenceCode() {
}

@Test
public void testConstructorWithZeroSeed() {
// This is allowed even though the generator is non-functional
final int size = 4;
final XoShiRo128Plus rng = new XoShiRo128Plus(new int[size]);
for (int i = size * 2; i-- != 0; ) {
Assert.assertEquals("Expected the generator to be broken", 0, rng.nextInt());
}
public void testConstructorWithZeroSeedIsNonFunctional() {
RandomAssert.assertNextIntZeroOutput(new XoShiRo128Plus(new int[SEED_SIZE]), 2 * SEED_SIZE);
}

@Test
public void testConstructorWithSingleBitSeedIsFunctional() {
RandomAssert.assertIntArrayConstructorWithSingleBitSeedIsFunctional(XoShiRo128Plus.class, SEED_SIZE);
}

@Test
Expand All @@ -70,8 +72,6 @@ public void testElementConstructor() {
};
final XoShiRo128Plus rng1 = new XoShiRo128Plus(seed);
final XoShiRo128Plus rng2 = new XoShiRo128Plus(seed[0], seed[1], seed[2], seed[3]);
for (int i = seed.length * 2; i-- != 0; ) {
Assert.assertEquals(rng1.nextInt(), rng2.nextInt());
}
RandomAssert.assertNextIntEquals(seed.length * 2, rng1, rng2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
package org.apache.commons.rng.core.source32;

import org.apache.commons.rng.core.RandomAssert;
import org.junit.Assert;
import org.junit.Test;

public class XoShiRo128StarStarTest {
/** The size of the array seed. */
private static final int SEED_SIZE = 4;

@Test
public void testReferenceCode() {
/*
Expand Down Expand Up @@ -48,13 +50,13 @@ public void testReferenceCode() {
}

@Test
public void testConstructorWithZeroSeed() {
// This is allowed even though the generator is non-functional
final int size = 4;
final XoShiRo128StarStar rng = new XoShiRo128StarStar(new int[size]);
for (int i = size * 2; i-- != 0; ) {
Assert.assertEquals("Expected the generator to be broken", 0, rng.nextInt());
}
public void testConstructorWithZeroSeedIsNonFunctional() {
RandomAssert.assertNextIntZeroOutput(new XoShiRo128StarStar(new int[SEED_SIZE]), 2 * SEED_SIZE);
}

@Test
public void testConstructorWithSingleBitSeedIsFunctional() {
RandomAssert.assertIntArrayConstructorWithSingleBitSeedIsFunctional(XoShiRo128StarStar.class, SEED_SIZE);
}

@Test
Expand All @@ -70,8 +72,6 @@ public void testElementConstructor() {
};
final XoShiRo128StarStar rng1 = new XoShiRo128StarStar(seed);
final XoShiRo128StarStar rng2 = new XoShiRo128StarStar(seed[0], seed[1], seed[2], seed[3]);
for (int i = seed.length * 2; i-- != 0; ) {
Assert.assertEquals(rng1.nextInt(), rng2.nextInt());
}
RandomAssert.assertNextIntEquals(seed.length * 2, rng1, rng2);
}
}

0 comments on commit 99f91db

Please sign in to comment.