Repository: commons-rng Updated Branches: refs/heads/master 819d8a544 -> 58f49e3cf
New interfaces: "RandomProviderState" and "RestorableUniformRandomProvider". Emphasize the "save/restore" feature of the RNG implementations. Project: http://git-wip-us.apache.org/repos/asf/commons-rng/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-rng/commit/c57e6ebd Tree: http://git-wip-us.apache.org/repos/asf/commons-rng/tree/c57e6ebd Diff: http://git-wip-us.apache.org/repos/asf/commons-rng/diff/c57e6ebd Branch: refs/heads/master Commit: c57e6ebd62f80c4d87ffe4a2636d7f0769884041 Parents: 819d8a5 Author: Gilles <er...@apache.org> Authored: Mon Oct 17 18:05:48 2016 +0200 Committer: Gilles <er...@apache.org> Committed: Mon Oct 17 18:05:48 2016 +0200 ---------------------------------------------------------------------- .../org/apache/commons/rng/ProviderBuilder.java | 12 +-- .../apache/commons/rng/RandomProviderState.java | 25 ++++++ .../org/apache/commons/rng/RandomSource.java | 59 ++---------- .../rng/RestorableUniformRandomProvider.java | 49 ++++++++++ .../commons/rng/internal/BaseProvider.java | 22 ++++- src/site/apt/userguide/rng.apt | 37 ++++---- .../commons/rng/Providers32ParametricTest.java | 2 +- .../commons/rng/Providers64ParametricTest.java | 2 +- .../rng/ProvidersCommonParametricTest.java | 94 ++++---------------- 9 files changed, 147 insertions(+), 155 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-rng/blob/c57e6ebd/src/main/java/org/apache/commons/rng/ProviderBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/rng/ProviderBuilder.java b/src/main/java/org/apache/commons/rng/ProviderBuilder.java index 76f1668..7607cf5 100644 --- a/src/main/java/org/apache/commons/rng/ProviderBuilder.java +++ b/src/main/java/org/apache/commons/rng/ProviderBuilder.java @@ -154,9 +154,9 @@ class ProviderBuilder { * @return a new RNG instance. * @throws UnsupportedOperationException if the seed type is invalid. */ - public static UniformRandomProvider create(RandomSourceInternal source, - Object seed, - Object[] args) { + public static RestorableUniformRandomProvider create(RandomSourceInternal source, + Object seed, + Object[] args) { // Convert seed to native type. final Object nativeSeed = createSeed(source, seed); @@ -252,10 +252,10 @@ class ProviderBuilder { * @param args Arguments to the implementation's constructor. * @return a new RNG instance. */ - private static UniformRandomProvider create(Constructor<?> rng, - Object[] args) { + private static RestorableUniformRandomProvider create(Constructor<?> rng, + Object[] args) { try { - return (UniformRandomProvider) rng.newInstance(args); + return (RestorableUniformRandomProvider) rng.newInstance(args); } catch (InvocationTargetException e) { throw new IllegalStateException(INTERNAL_ERROR_MSG, e); } catch (InstantiationException e) { http://git-wip-us.apache.org/repos/asf/commons-rng/blob/c57e6ebd/src/main/java/org/apache/commons/rng/RandomProviderState.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/rng/RandomProviderState.java b/src/main/java/org/apache/commons/rng/RandomProviderState.java new file mode 100644 index 0000000..f4af719 --- /dev/null +++ b/src/main/java/org/apache/commons/rng/RandomProviderState.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * Marker interface for objects that represents the state of a random + * generator. + * + * @since 1.0 + */ +public interface RandomProviderState {} http://git-wip-us.apache.org/repos/asf/commons-rng/blob/c57e6ebd/src/main/java/org/apache/commons/rng/RandomSource.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/rng/RandomSource.java b/src/main/java/org/apache/commons/rng/RandomSource.java index cb82b51..618f451 100644 --- a/src/main/java/org/apache/commons/rng/RandomSource.java +++ b/src/main/java/org/apache/commons/rng/RandomSource.java @@ -18,7 +18,6 @@ package org.apache.commons.rng; import java.util.Arrays; -import org.apache.commons.rng.internal.BaseProvider; import org.apache.commons.rng.internal.util.SeedFactory; /** @@ -306,13 +305,14 @@ public enum RandomSource { private final ProviderBuilder.RandomSourceInternal internalIdentifier; /** - * Wraps the internal state of a {@link UniformRandomProvider}. + * Wraps the internal state of the {@link RestorableUniformRandomProvider} + * instances created by this factory. * Its purpose is to store all the data needed to recover the same * state in order to restart a sequence where it left off. * External code should not try to modify the data contained in instances * of this class. */ - public static class State { + public static class State implements RandomProviderState { /** Internal state. */ private final byte[] state; @@ -375,7 +375,7 @@ public enum RandomSource { * @param source RNG type. * @return the RNG. */ - public static UniformRandomProvider create(RandomSource source) { + public static RestorableUniformRandomProvider create(RandomSource source) { return create(source, null); } @@ -426,58 +426,13 @@ public enum RandomSource { * @throws IllegalStateException if data is missing to initialize the * generator implemented by the given {@code source}. */ - public static UniformRandomProvider create(RandomSource source, - Object seed, - Object ... data) { + public static RestorableUniformRandomProvider create(RandomSource source, + Object seed, + Object ... data) { return ProviderBuilder.create(source.getInternalIdentifier(), seed, data); } /** - * Saves the state of a RNG. - * - * @param provider Provider. - * @return the current state of the given {@code provider}. - * @throws UnsupportedOperationException if the {@code provider} is - * not an object created by this factory or the underlying source of - * randomness does not support this functionality. - * - * @see #restoreState(UniformRandomProvider,State) - */ - public static State saveState(UniformRandomProvider provider) { - if (!(provider instanceof BaseProvider)) { - throw new UnsupportedOperationException(); - } else { - return new State(((BaseProvider) provider).getState()); - } - } - - /** - * Restores the state of a RNG. - * - * @param provider Provider. - * @param state State which the {@code provider} will be set to. - * This parameter must have been obtained by a call to - * {@link #saveState(UniformRandomProvider) saveState(rng)} - * where {@code rng} is either the same object as {@code provider}, - * or an object of the exact same class. - * @throws UnsupportedOperationException if the {@code provider} is - * not an object created by this factory or the underlying source of - * randomness does not support this functionality. - * @throws IllegalArgumentException if it was detected that the - * {@code state} is incompatible with the given {@code provider}. - * - * @see #saveState(UniformRandomProvider) - */ - public static void restoreState(UniformRandomProvider provider, - State state) { - if (!(provider instanceof BaseProvider)) { - throw new UnsupportedOperationException(); - } else { - ((BaseProvider) provider).setState(state.getState()); - } - } - - /** * Creates a number for use as a seed. * * @return a random number. http://git-wip-us.apache.org/repos/asf/commons-rng/blob/c57e6ebd/src/main/java/org/apache/commons/rng/RestorableUniformRandomProvider.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/rng/RestorableUniformRandomProvider.java b/src/main/java/org/apache/commons/rng/RestorableUniformRandomProvider.java new file mode 100644 index 0000000..fa72ad2 --- /dev/null +++ b/src/main/java/org/apache/commons/rng/RestorableUniformRandomProvider.java @@ -0,0 +1,49 @@ +/* + * 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; + +/** + * Applies to generators whose internal state can be saved and restored. + * + * @since 1.0 + */ +public interface RestorableUniformRandomProvider extends UniformRandomProvider { + /** + * Saves the state of a generator. + * + * @return the current state of this instance. It is a value that can + * subsequently be passed to the {@link #restore(RandomProviderState) + * restore} method. + * @throws UnsupportedOperationException if the underlying source of + * randomness does not support this functionality. + */ + RandomProviderState saveState(); + + /** + * Restores the state of a generator. + * + * @param state State which this instance will be set to. + * This parameter would usually have been obtained by a call to + * {@link #saveState() saveState} performed either on the same + * object as this one, or an object of the exact same class. + * @throws UnsupportedOperationException if the underlying source of + * randomness does not support this functionality. + * @throws IllegalArgumentException if it was detected that the + * {@code state} argument is incompatible with this intance. + */ + void restoreState(RandomProviderState state); +} http://git-wip-us.apache.org/repos/asf/commons-rng/blob/c57e6ebd/src/main/java/org/apache/commons/rng/internal/BaseProvider.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/rng/internal/BaseProvider.java b/src/main/java/org/apache/commons/rng/internal/BaseProvider.java index c9ed7b3..4f281b1 100644 --- a/src/main/java/org/apache/commons/rng/internal/BaseProvider.java +++ b/src/main/java/org/apache/commons/rng/internal/BaseProvider.java @@ -17,13 +17,15 @@ package org.apache.commons.rng.internal; -import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.RestorableUniformRandomProvider; +import org.apache.commons.rng.RandomProviderState; +import org.apache.commons.rng.RandomSource; /** * Base class with default implementation for common methods. */ public abstract class BaseProvider - implements UniformRandomProvider { + implements RestorableUniformRandomProvider { /** {@inheritDoc} */ @Override public int nextInt(int n) { @@ -59,6 +61,22 @@ public abstract class BaseProvider /** {@inheritDoc} */ @Override + public RandomProviderState saveState() { + return new RandomSource.State(getStateInternal()); + } + + /** {@inheritDoc} */ + @Override + public void restoreState(RandomProviderState state) { + if (state instanceof RandomSource.State) { + setStateInternal(((RandomSource.State) state).getState()); + } else { + throw new IllegalArgumentException("Foreign instance"); + } + } + + /** {@inheritDoc} */ + @Override public String toString() { return getClass().getName(); } http://git-wip-us.apache.org/repos/asf/commons-rng/blob/c57e6ebd/src/site/apt/userguide/rng.apt ---------------------------------------------------------------------- diff --git a/src/site/apt/userguide/rng.apt b/src/site/apt/userguide/rng.apt index a1e0845..f0901d5 100644 --- a/src/site/apt/userguide/rng.apt +++ b/src/site/apt/userguide/rng.apt @@ -172,16 +172,16 @@ UniformRandomProvider rng = RandomSource.create(RandomSource.TWO_CMRES_SELECT, s * The current state of a generator can be - {{{../apidocs/org/apache/commons/rng/RandomSource.html#saveState-org.apache.commons.rng.UniformRandomProvider-}saved}} + {{{../apidocs/org/apache/commons/rng/RestorableUniformRandomProvider.html#saveState}saved}} and - {{{../apidocs/org/apache/commons/rng/RandomSource.html#restoreState-org.apache.commons.rng.UniformRandomProvider-org.apache.commons.rng.RandomSource.State-}restored}} + {{{../apidocs/org/apache/commons/rng/RestorableUniformRandomProvider.html#restoreState-org.apache.commons.rng.RandomProviderState-}restored}} later on. +--------------------------+ -UniformRandomProvider rng = RandomSource.create(RandomSource.WELL_512_A); -RandomSource.State state = RandomSource.saveState(rng); +RestorableUniformRandomProvider rng = RandomSource.create(RandomSource.WELL_512_A); +RandomProviderState state = rng.saveState(); double x = rng.nextDouble(); -RandomSource.restoreState(rng, state); +rng.restoreState(state); double y = rng.nextDouble(); // x == y. +--------------------------+ @@ -197,23 +197,23 @@ double y = rng.nextDouble(); // x == y. +--------------------------+ RandomSource source = RandomSource.MT_64; // Known source identifier. -UniformRandomProvider rngOrig = RandomSource.create(source); // Original RNG instance. +RestorableUniformRandomProvider rngOrig = RandomSource.create(source); // Original RNG instance. // Save and serialize state. -RandomSource.State stateOrig = RandomSource.saveState(rngOrig); +RandomProviderState stateOrig = rngOrig.saveState(rngOrig); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); -oos.writeObject(stateOrig.getState()); +oos.writeObject(((RandomSource.State) stateOrig).getState()); // Deserialize state. ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); -RandomSource.State stateNew = new RandomSource.State((byte[]) ois.readObject()); +RandomProviderState stateNew = new RandomSource.State((byte[]) ois.readObject()); -UniformRandomProvider rngNew = RandomSource.create(source); // New RNG instance from the same "source". +RestorableUniformRandomProvider rngNew = RandomSource.create(source); // New RNG instance from the same "source". // Restore original state on the new instance. -RandomSource.restoreState(rngNew, stateNew); +rngNew.restoreState(stateNew); +--------------------------+ @@ -225,15 +225,18 @@ RandomSource.restoreState(rngNew, stateNew); The library's public API consists of classes and interfaces defined in package <<<org.apache.commons.rng>>>. - * <<<UniformRandomProvider>>>: provides access to a sequence of random values - uniformly distributed within some range. + * Interface <<<UniformRandomProvider>>> provides access to a sequence of + random values uniformly distributed within some range. - * <<<RandomSource>>>: determines which algorithm to use for generating the + * Enum <<<RandomSource>>> determines which algorithm to use for generating the sequence of random values. - * <<<RandomSource.State>>>: enables the save and restore functionality of - the state of a <<<UniformRandomProvider>>> instance created through one - of the <<<RandomSource>>> factory methods. + * Interfaces <<<RestorableUniformRandomProvider>>> and <<<RandomProviderState>>> + provide the "save/restore" API. + + * Class <<<RandomSource.State>>> implements the <<<RandomProviderState>>> + interface to enable "save/restore" for all <<<RestorableUniformRandomProvider>>> + instances created through the <<<RandomSource>>> factory methods. [] http://git-wip-us.apache.org/repos/asf/commons-rng/blob/c57e6ebd/src/test/java/org/apache/commons/rng/Providers32ParametricTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/rng/Providers32ParametricTest.java b/src/test/java/org/apache/commons/rng/Providers32ParametricTest.java index 972ef3d..895e798 100644 --- a/src/test/java/org/apache/commons/rng/Providers32ParametricTest.java +++ b/src/test/java/org/apache/commons/rng/Providers32ParametricTest.java @@ -27,7 +27,7 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(value=Parameterized.class) public class Providers32ParametricTest { /** RNG under test. */ - private final UniformRandomProvider generator; + private final RestorableUniformRandomProvider generator; /** * Initializes generator instance. http://git-wip-us.apache.org/repos/asf/commons-rng/blob/c57e6ebd/src/test/java/org/apache/commons/rng/Providers64ParametricTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/rng/Providers64ParametricTest.java b/src/test/java/org/apache/commons/rng/Providers64ParametricTest.java index dbd70e4..c710621 100644 --- a/src/test/java/org/apache/commons/rng/Providers64ParametricTest.java +++ b/src/test/java/org/apache/commons/rng/Providers64ParametricTest.java @@ -27,7 +27,7 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(value=Parameterized.class) public class Providers64ParametricTest { /** RNG under test. */ - private final UniformRandomProvider generator; + private final RestorableUniformRandomProvider generator; /** * Initializes generator instance. http://git-wip-us.apache.org/repos/asf/commons-rng/blob/c57e6ebd/src/test/java/org/apache/commons/rng/ProvidersCommonParametricTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/rng/ProvidersCommonParametricTest.java b/src/test/java/org/apache/commons/rng/ProvidersCommonParametricTest.java index 9e7751d..e8b6183 100644 --- a/src/test/java/org/apache/commons/rng/ProvidersCommonParametricTest.java +++ b/src/test/java/org/apache/commons/rng/ProvidersCommonParametricTest.java @@ -315,8 +315,10 @@ public class ProvidersCommonParametricTest { // state is away from its initial settings. final int n = 10000; + // Cast is OK: all instances created by this library inherit from "BaseProvider". + final RestorableUniformRandomProvider restorable = (RestorableUniformRandomProvider) generator; // Save. - final RandomSource.State state = RandomSource.saveState(generator); + final RandomProviderState state = restorable.saveState(); // Store some values. final List<Number> listOrig = makeList(n); // Discard a few more. @@ -324,7 +326,7 @@ public class ProvidersCommonParametricTest { Assert.assertTrue(listDiscard.size() != 0); Assert.assertFalse(listOrig.equals(listDiscard)); // Reset. - RandomSource.restoreState(generator, state); + restorable.restoreState(state); // Replay. final List<Number> listReplay = makeList(n); Assert.assertFalse(listOrig == listReplay); @@ -339,11 +341,15 @@ public class ProvidersCommonParametricTest { // Large "n" is not necessary here as we only test the serialization. final int n = 100; - // Save and serialize. - final RandomSource.State stateOrig = RandomSource.saveState(generator); + // Cast is OK: all instances created by this library inherit from "BaseProvider". + final RestorableUniformRandomProvider restorable = (RestorableUniformRandomProvider) generator; + + // Save. + final RandomProviderState stateOrig = restorable.saveState(); + // Serialize. ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(stateOrig.getState()); + oos.writeObject(((RandomSource.State) stateOrig).getState()); // Store some values. final List<Number> listOrig = makeList(n); @@ -356,12 +362,12 @@ public class ProvidersCommonParametricTest { // Retrieve from serialized stream. ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); - final RandomSource.State stateNew = new RandomSource.State((byte[]) ois.readObject()); + final RandomProviderState stateNew = new RandomSource.State((byte[]) ois.readObject()); Assert.assertTrue(stateOrig != stateNew); // Reset. - RandomSource.restoreState(generator, stateNew); + restorable.restoreState(stateNew); // Replay. final List<Number> listReplay = makeList(n); @@ -376,21 +382,11 @@ public class ProvidersCommonParametricTest { // We don't know what is the state of "java.lang.Random": skipping. Assume.assumeTrue(generator.toString().indexOf("JDKRandom") == -1); - final RandomSource.State state = RandomSource.saveState(new DummyGenerator()); + final RandomProviderState state = new DummyGenerator().saveState(); // Try to restore with an invalid state (wrong size). - RandomSource.restoreState(generator, state); - } - - @Test(expected=UnsupportedOperationException.class) - public void testSaveStateWrongClass() { - RandomSource.saveState(new ForeignDummyGenerator()); + ((RestorableUniformRandomProvider) generator).restoreState(state); } - @Test(expected=UnsupportedOperationException.class) - public void testRestoreStateWrongClass() { - RandomSource.restoreState(new ForeignDummyGenerator(), - new RandomSource.State(new byte[0])); - } ///// Support methods below. @@ -687,19 +683,19 @@ public class ProvidersCommonParametricTest { * @param chunkSize Size of the small buffer. * @param numChunks Number of chunks that make the large buffer. */ - static void checkNextBytesChunks(UniformRandomProvider rng, + static void checkNextBytesChunks(RestorableUniformRandomProvider rng, int chunkSize, int numChunks) { final byte[] b1 = new byte[chunkSize * numChunks]; final byte[] b2 = new byte[chunkSize]; - final RandomSource.State state = RandomSource.saveState(rng); + final RandomProviderState state = rng.saveState(); // Generate the chunks in a single call. rng.nextBytes(b1); // Reset to previous state. - RandomSource.restoreState(rng, state); + rng.restoreState(state); // Generate the chunks in consecutive calls. for (int i = 0; i < numChunks; i++) { @@ -738,57 +734,3 @@ class DummyGenerator extends org.apache.commons.rng.internal.source32.IntProvide // No state. } } - -/** - * Dummy class for checking that "save" and "restore" fail when a - * class is passed that is not providing the necessary functionality - * (i.e. a class that is not implemented by this library). - */ -class ForeignDummyGenerator implements UniformRandomProvider { - @Override - public void nextBytes(byte[] bytes) { - return; - } - - @Override - public void nextBytes(byte[] bytes, - int start, - int len) { - return; - } - - @Override - public int nextInt() { - return 0; - } - - @Override - public int nextInt(int n) { - return 0; - } - - @Override - public long nextLong() { - return 0; - } - - @Override - public long nextLong(long n) { - return 0; - } - - @Override - public boolean nextBoolean() { - return false; - } - - @Override - public float nextFloat() { - return 0; - } - - @Override - public double nextDouble() { - return 0; - } -}