001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.rng.simple;
018
019import java.io.IOException;
020import java.io.ObjectOutputStream;
021import java.io.ObjectInputStream;
022import java.util.Random;
023import org.apache.commons.rng.RestorableUniformRandomProvider;
024import org.apache.commons.rng.core.RandomProviderDefaultState;
025
026/**
027 * Subclass of {@link Random} that {@link #next(int) delegates} to a
028 * {@link RestorableUniformRandomProvider} instance but will otherwise rely
029 * on the base class for generating all the random types.
030 *
031 * <p>
032 * Legacy applications coded against the JDK's API could use this subclass
033 * of {@link Random} in order to replace its linear congruential generator
034 * by any {@link RandomSource}.
035 * </p>
036 *
037 * Caveat: Use of this class is <em>not</em> recommended for new applications.
038 * In particular, there is no guarantee that the serialized form of this class
039 * will be compatible across (even <em>minor</em>) releases of the library.
040 *
041 * @since 1.0
042 */
043public final class JDKRandomBridge extends Random {
044    /** Serializable version identifier. */
045    private static final long serialVersionUID = 20161107L;
046    /** Source. */
047    private final RandomSource source;
048    /** Delegate. */
049    private transient RestorableUniformRandomProvider delegate;
050    /** Workaround JDK's "Random" bug: https://bugs.openjdk.java.net/browse/JDK-8154225 */
051    private final transient boolean isInitialized;
052
053    /**
054     * Creates a new instance.
055     *
056     * @param source Source of randomness.
057     * @param seed Seed.  Can be {@code null}.
058     */
059    public JDKRandomBridge(RandomSource source,
060                           Object seed) {
061        this.source = source;
062        delegate = RandomSource.create(source, seed);
063        isInitialized = true;
064    }
065
066    /** {@inheritDoc} */
067    @Override
068    public synchronized void setSeed(long seed) {
069        if (isInitialized) {
070            delegate = RandomSource.create(source, seed);
071
072            // Force the clearing of the "haveNextNextGaussian" flag
073            // (cf. Javadoc of the base class); the value passed here
074            // is irrelevant (since it will not be used).
075            super.setSeed(0L);
076        }
077    }
078
079    /**
080     * Delegates the generation of 32 random bits to the
081     * {@code RandomSource} argument provided at
082     * {@link #JDKRandomBridge(RandomSource,Object) construction}.
083     * The returned value is such that if the source of randomness is
084     * {@link RandomSource#JDK}, all the generated values will be identical
085     * to those produced by the same sequence of calls on a {@link Random}
086     * instance initialized with the same seed.
087     *
088     * @param n Number of random bits which the requested value must contain.
089     * @return the value represented by the {@code n} high-order bits of a
090     * pseudo-random 32-bits integer.
091     */
092    @Override
093    protected synchronized int next(int n) {
094        return delegate.nextInt() >>> (32 - n);
095    }
096
097    /**
098     * @param out Output stream.
099     * @throws IOException if an error occurs.
100     */
101    private synchronized void writeObject(ObjectOutputStream out)
102        throws IOException {
103        // Write non-transient fields.
104        out.defaultWriteObject();
105
106        // Save current state.
107        out.writeObject(((RandomProviderDefaultState) delegate.saveState()).getState());
108   }
109
110    /**
111     * @param in Input stream.
112     * @throws IOException if an error occurs.
113     * @throws ClassNotFoundException if an error occurs.
114     */
115    private void readObject(ObjectInputStream in)
116        throws IOException,
117               ClassNotFoundException {
118        // Read non-transient fields.
119        in.defaultReadObject();
120
121        // Recreate the "delegate" from serialized info.
122        delegate = RandomSource.create(source);
123        // And restore its state.
124        delegate.restoreState(new RandomProviderDefaultState((byte[]) in.readObject()));
125    }
126}