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;
018
019import java.util.stream.Stream;
020
021/**
022 * Applies to generators that can be advanced an arbitrary number of steps of the output
023 * sequence in a single operation.
024 *
025 * <p>Implementations must ensure that a jump of a specified {@code distance} will advance
026 * the state cycle sufficiently that an equivalent number of sequential calls to the
027 * original provider will <strong>not overlap</strong> output from the advanced
028 * provider.</p>
029 *
030 * <p>For many applications, it suffices to jump forward by a power of two or some small
031 * multiple of a power of two, but this power of two may not be representable as a
032 * {@code long} value. To avoid the use of {@link java.math.BigInteger BigInteger} values
033 * as jump distances, double values are used instead.</p>
034 *
035 * <p>Typical usage in a multithreaded application is to create a single
036 * {@link ArbitrarilyJumpableUniformRandomProvider} and {@link #jump(double) jump} the
037 * generator forward while passing each copy generator to a worker thread. The jump
038 * {@code distance} should be sufficient to cover all expected output by each worker.
039 * Since each copy generator is also an {@link ArbitrarilyJumpableUniformRandomProvider}
040 * with care it is possible to further distribute generators within the original jump
041 * {@code distance} and use the entire state cycle in different ways.</p>
042 *
043 * @since 1.7
044 */
045public interface ArbitrarilyJumpableUniformRandomProvider extends UniformRandomProvider {
046    /**
047     * Creates a copy of the {@link ArbitrarilyJumpableUniformRandomProvider} and then advances
048     * the state cycle of the current instance by the specified {@code distance}.
049     * The copy is returned.
050     *
051     * <p>The current state will be advanced in a single operation by the equivalent of a
052     * number of sequential calls to a method that updates the state cycle of the provider.</p>
053     *
054     * <p>Repeat invocations of this method will create a series of generators
055     * that are uniformly spaced at intervals of the output sequence. Each generator provides
056     * non-overlapping output for the length specified by {@code distance} for use in parallel
057     * computations.</p>
058     *
059     * @param distance Distance to jump forward with the state cycle.
060     * @return A copy of the current state.
061     * @throws IllegalArgumentException if {@code distance} is negative,
062     * or is greater than the period of this generator.
063     */
064    ArbitrarilyJumpableUniformRandomProvider jump(double distance);
065
066    /**
067     * Creates a copy of the {@link ArbitrarilyJumpableUniformRandomProvider} and then advances
068     * the state cycle of the current instance by a distance equal to 2<sup>{@code logDistance}</sup>.
069     * The copy is returned.
070     *
071     * <p>The current state will be advanced in a single operation by the equivalent of a
072     * number of sequential calls to a method that updates the state cycle of the provider.</p>
073     *
074     * <p>Repeat invocations of this method will create a series of generators
075     * that are uniformly spaced at intervals of the output sequence. Each generator provides
076     * non-overlapping output for the length specified by 2<sup>{@code logDistance}</sup> for use
077     * in parallel computations.</p>
078     *
079     * @param logDistance Base-2 logarithm of the distance to jump forward with the state cycle.
080     * @return A copy of the current state.
081     * @throws IllegalArgumentException if 2<sup>{@code logDistance}</sup>
082     * is greater than the period of this generator.
083     */
084    ArbitrarilyJumpableUniformRandomProvider jumpPowerOfTwo(int logDistance);
085
086    /**
087     * Returns an effectively unlimited stream of new random generators, each of which
088     * implements the {@link ArbitrarilyJumpableUniformRandomProvider} interface. The
089     * generators are output at integer multiples of the specified jump {@code distance}
090     * in the generator's state cycle.
091     *
092     * @param distance Distance to jump forward with the state cycle.
093     * @return a stream of random generators.
094     * @throws IllegalArgumentException if {@code distance} is negative,
095     * or is greater than the period of this generator.
096     */
097    default Stream<ArbitrarilyJumpableUniformRandomProvider> jumps(double distance) {
098        UniformRandomProviderSupport.validateJumpDistance(distance);
099        return Stream.generate(() -> jump(distance)).sequential();
100    }
101
102    /**
103     * Returns a stream producing the given {@code streamSize} number of new random
104     * generators, each of which implements the {@link ArbitrarilyJumpableUniformRandomProvider}
105     * interface. The generators are output at integer multiples of the specified jump
106     * {@code distance} in the generator's state cycle.
107     *
108     * @param streamSize Number of objects to generate.
109     * @param distance Distance to jump forward with the state cycle.
110     * @return a stream of random generators; the stream is limited to the given
111     * {@code streamSize}.
112     * @throws IllegalArgumentException if {@code streamSize} is negative;
113     * or if {@code distance} is negative, or is greater than the period of this generator.
114     */
115    default Stream<ArbitrarilyJumpableUniformRandomProvider> jumps(long streamSize,
116                                                                   double distance) {
117        UniformRandomProviderSupport.validateStreamSize(streamSize);
118        return jumps(distance).limit(streamSize);
119    }
120}