All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.rng.UniformRandomProviderSupport Maven / Gradle / Ivy

/*
 * 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;

import java.util.Objects;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;

/**
 * Support for {@link UniformRandomProvider} default methods.
 *
 * @since 1.5
 */
final class UniformRandomProviderSupport {
    /** Message for an invalid stream size. */
    private static final String INVALID_STREAM_SIZE = "Invalid stream size: ";
    /** Message for an invalid upper bound (must be positive, finite and above zero). */
    private static final String INVALID_UPPER_BOUND = "Upper bound must be above zero: ";
    /** Message format for an invalid range for lower inclusive and upper exclusive. */
    private static final String INVALID_RANGE = "Invalid range: [%s, %s)";
    /** 2^32. */
    private static final long POW_32 = 1L << 32;
    /** Message when the consumer action is null. */
    private static final String NULL_ACTION = "action must not be null";

    /** No instances. */
    private UniformRandomProviderSupport() {}

    /**
     * Validate the stream size.
     *
     * @param size Stream size.
     * @throws IllegalArgumentException if {@code size} is negative.
     */
    static void validateStreamSize(long size) {
        if (size < 0) {
            throw new IllegalArgumentException(INVALID_STREAM_SIZE + size);
        }
    }

    /**
     * Validate the upper bound.
     *
     * @param bound Upper bound (exclusive) on the random number to be returned.
     * @throws IllegalArgumentException if {@code bound} is equal to or less than zero.
     */
    static void validateUpperBound(int bound) {
        if (bound <= 0) {
            throw new IllegalArgumentException(INVALID_UPPER_BOUND + bound);
        }
    }

    /**
     * Validate the upper bound.
     *
     * @param bound Upper bound (exclusive) on the random number to be returned.
     * @throws IllegalArgumentException if {@code bound} is equal to or less than zero.
     */
    static void validateUpperBound(long bound) {
        if (bound <= 0) {
            throw new IllegalArgumentException(INVALID_UPPER_BOUND + bound);
        }
    }

    /**
     * Validate the upper bound.
     *
     * @param bound Upper bound (exclusive) on the random number to be returned.
     * @throws IllegalArgumentException if {@code bound} is equal to or less than zero, or
     * is not finite
     */
    static void validateUpperBound(float bound) {
        // Negation of logic will detect NaN
        if (!(bound > 0 && bound <= Float.MAX_VALUE)) {
            throw new IllegalArgumentException(INVALID_UPPER_BOUND + bound);
        }
    }

    /**
     * Validate the upper bound.
     *
     * @param bound Upper bound (exclusive) on the random number to be returned.
     * @throws IllegalArgumentException if {@code bound} is equal to or less than zero, or
     * is not finite
     */
    static void validateUpperBound(double bound) {
        // Negation of logic will detect NaN
        if (!(bound > 0 && bound <= Double.MAX_VALUE)) {
            throw new IllegalArgumentException(INVALID_UPPER_BOUND + bound);
        }
    }

    /**
     * Validate the range between the specified {@code origin} (inclusive) and the
     * specified {@code bound} (exclusive).
     *
     * @param origin Lower bound on the random number to be returned.
     * @param bound Upper bound (exclusive) on the random number to be returned.
     * @throws IllegalArgumentException if {@code origin} is greater than or equal to
     * {@code bound}.
     */
    static void validateRange(int origin, int bound) {
        if (origin >= bound) {
            throw new IllegalArgumentException(String.format(INVALID_RANGE, origin, bound));
        }
    }

    /**
     * Validate the range between the specified {@code origin} (inclusive) and the
     * specified {@code bound} (exclusive).
     *
     * @param origin Lower bound on the random number to be returned.
     * @param bound Upper bound (exclusive) on the random number to be returned.
     * @throws IllegalArgumentException if {@code origin} is greater than or equal to
     * {@code bound}.
     */
    static void validateRange(long origin, long bound) {
        if (origin >= bound) {
            throw new IllegalArgumentException(String.format(INVALID_RANGE, origin, bound));
        }
    }

    /**
     * Validate the range between the specified {@code origin} (inclusive) and the
     * specified {@code bound} (exclusive).
     *
     * @param origin Lower bound on the random number to be returned.
     * @param bound Upper bound (exclusive) on the random number to be returned.
     * @throws IllegalArgumentException if {@code origin} is not finite, or {@code bound}
     * is not finite, or {@code origin} is greater than or equal to {@code bound}.
     */
    static void validateRange(double origin, double bound) {
        if (origin >= bound || !Double.isFinite(origin) || !Double.isFinite(bound)) {
            throw new IllegalArgumentException(String.format(INVALID_RANGE, origin, bound));
        }
    }

    /**
     * Checks if the sub-range from fromIndex (inclusive) to fromIndex + size (exclusive) is
     * within the bounds of range from 0 (inclusive) to length (exclusive).
     *
     * 

This function provides the functionality of * {@code java.utils.Objects.checkFromIndexSize} introduced in JDK 9. The * Objects * javadoc has been reproduced for reference. * *

The sub-range is defined to be out of bounds if any of the following inequalities * is true: *

    *
  • {@code fromIndex < 0} *
  • {@code size < 0} *
  • {@code fromIndex + size > length}, taking into account integer overflow *
  • {@code length < 0}, which is implied from the former inequalities *
* *

Note: This is not an exact implementation of the functionality of * {@code Objects.checkFromIndexSize}. The following changes have been made: *

    *
  • The method signature has been changed to avoid the return of {@code fromIndex}; * this value is not used within this package. *
  • No checks are made for {@code length < 0} as this is assumed to be derived from * an array length. *
* * @param fromIndex the lower-bound (inclusive) of the sub-interval * @param size the size of the sub-range * @param length the upper-bound (exclusive) of the range * @throws IndexOutOfBoundsException if the sub-range is out of bounds */ static void validateFromIndexSize(int fromIndex, int size, int length) { // check for any negatives (assume 'length' is positive array length), // or overflow safe length check given the values are all positive // remaining = length - fromIndex if ((fromIndex | size) < 0 || size > length - fromIndex) { throw new IndexOutOfBoundsException( // Note: %The array is filled with bytes extracted from random {@code long} values. This * implies that the number of random bytes generated may be larger than the length of * the byte array. * * @param source Source of randomness. * @param bytes Array in which to put the generated bytes. Cannot be null. * @param start Index at which to start inserting the generated bytes. * @param len Number of bytes to insert. */ static void nextBytes(UniformRandomProvider source, byte[] bytes, int start, int len) { // Index of first insertion plus multiple of 8 part of length // (i.e. length with 3 least significant bits unset). final int indexLoopLimit = start + (len & 0x7ffffff8); // Start filling in the byte array, 8 bytes at a time. int index = start; while (index < indexLoopLimit) { final long random = source.nextLong(); bytes[index++] = (byte) random; bytes[index++] = (byte) (random >>> 8); bytes[index++] = (byte) (random >>> 16); bytes[index++] = (byte) (random >>> 24); bytes[index++] = (byte) (random >>> 32); bytes[index++] = (byte) (random >>> 40); bytes[index++] = (byte) (random >>> 48); bytes[index++] = (byte) (random >>> 56); } // Index of last insertion + 1 final int indexLimit = start + len; // Fill in the remaining bytes. if (index < indexLimit) { long random = source.nextLong(); for (;;) { bytes[index++] = (byte) random; if (index == indexLimit) { break; } random >>>= 8; } } } /** * Generates an {@code int} value between 0 (inclusive) and the specified value * (exclusive). * * @param source Source of randomness. * @param n Bound on the random number to be returned. Must be strictly positive. * @return a random {@code int} value between 0 (inclusive) and {@code n} (exclusive). */ static int nextInt(UniformRandomProvider source, int n) { // Lemire (2019): Fast Random Integer Generation in an Interval // https://arxiv.org/abs/1805.10941 long m = (source.nextInt() & 0xffffffffL) * n; long l = m & 0xffffffffL; if (l < n) { // 2^32 % n final long t = POW_32 % n; while (l < t) { m = (source.nextInt() & 0xffffffffL) * n; l = m & 0xffffffffL; } } return (int) (m >>> 32); } /** * Generates an {@code int} value between the specified {@code origin} (inclusive) and * the specified {@code bound} (exclusive). * * @param source Source of randomness. * @param origin Lower bound on the random number to be returned. * @param bound Upper bound (exclusive) on the random number to be returned. Must be * above {@code origin}. * @return a random {@code int} value between {@code origin} (inclusive) and * {@code bound} (exclusive). */ static int nextInt(UniformRandomProvider source, int origin, int bound) { final int n = bound - origin; if (n > 0) { return nextInt(source, n) + origin; } // Range too large to fit in a positive integer. // Use simple rejection. int v = source.nextInt(); while (v < origin || v >= bound) { v = source.nextInt(); } return v; } /** * Generates an {@code long} value between 0 (inclusive) and the specified value * (exclusive). * * @param source Source of randomness. * @param n Bound on the random number to be returned. Must be strictly positive. * @return a random {@code long} value between 0 (inclusive) and {@code n} * (exclusive). */ static long nextLong(UniformRandomProvider source, long n) { long bits; long val; do { bits = source.nextLong() >>> 1; val = bits % n; } while (bits - val + (n - 1) < 0); return val; } /** * Generates a {@code long} value between the specified {@code origin} (inclusive) and * the specified {@code bound} (exclusive). * * @param source Source of randomness. * @param origin Lower bound on the random number to be returned. * @param bound Upper bound (exclusive) on the random number to be returned. Must be * above {@code origin}. * @return a random {@code long} value between {@code origin} (inclusive) and * {@code bound} (exclusive). */ static long nextLong(UniformRandomProvider source, long origin, long bound) { final long n = bound - origin; if (n > 0) { return nextLong(source, n) + origin; } // Range too large to fit in a positive integer. // Use simple rejection. long v = source.nextLong(); while (v < origin || v >= bound) { v = source.nextLong(); } return v; } /** * Generates a {@code float} value between 0 (inclusive) and the specified value * (exclusive). * * @param source Source of randomness. * @param bound Bound on the random number to be returned. Must be strictly positive. * @return a random {@code float} value between 0 (inclusive) and {@code bound} * (exclusive). */ static float nextFloat(UniformRandomProvider source, float bound) { float v = source.nextFloat() * bound; if (v >= bound) { // Correct rounding v = Math.nextDown(bound); } return v; } /** * Generates a {@code float} value between the specified {@code origin} (inclusive) * and the specified {@code bound} (exclusive). * * @param source Source of randomness. * @param origin Lower bound on the random number to be returned. Must be finite. * @param bound Upper bound (exclusive) on the random number to be returned. Must be * above {@code origin} and finite. * @return a random {@code float} value between {@code origin} (inclusive) and * {@code bound} (exclusive). */ static float nextFloat(UniformRandomProvider source, float origin, float bound) { float v = source.nextFloat(); // This expression allows (bound - origin) to be infinite // origin + (bound - origin) * v // == origin - origin * v + bound * v v = (1f - v) * origin + v * bound; if (v >= bound) { // Correct rounding v = Math.nextDown(bound); } return v; } /** * Generates a {@code double} value between 0 (inclusive) and the specified value * (exclusive). * * @param source Source of randomness. * @param bound Bound on the random number to be returned. Must be strictly positive. * @return a random {@code double} value between 0 (inclusive) and {@code bound} * (exclusive). */ static double nextDouble(UniformRandomProvider source, double bound) { double v = source.nextDouble() * bound; if (v >= bound) { // Correct rounding v = Math.nextDown(bound); } return v; } /** * Generates a {@code double} value between the specified {@code origin} (inclusive) * and the specified {@code bound} (exclusive). * * @param source Source of randomness. * @param origin Lower bound on the random number to be returned. Must be finite. * @param bound Upper bound (exclusive) on the random number to be returned. Must be * above {@code origin} and finite. * @return a random {@code double} value between {@code origin} (inclusive) and * {@code bound} (exclusive). */ static double nextDouble(UniformRandomProvider source, double origin, double bound) { double v = source.nextDouble(); // This expression allows (bound - origin) to be infinite // origin + (bound - origin) * v // == origin - origin * v + bound * v v = (1f - v) * origin + v * bound; if (v >= bound) { // Correct rounding v = Math.nextDown(bound); } return v; } // Spliterator support /** * Base class for spliterators for streams of values. Contains the range current position and * end position. Splitting is expected to divide the range in half and create instances * that span the two ranges. */ private static class ProviderSpliterator { /** The current position in the range. */ protected long position; /** The upper limit of the range. */ protected final long end; /** * @param start Start position of the stream (inclusive). * @param end Upper limit of the stream (exclusive). */ ProviderSpliterator(long start, long end) { position = start; this.end = end; } // Methods required by all Spliterators // See Spliterator.estimateSize() public long estimateSize() { return end - position; } // See Spliterator.characteristics() public int characteristics() { return Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.NONNULL | Spliterator.IMMUTABLE; } } /** * Spliterator for streams of SplittableUniformRandomProvider. */ static class ProviderSplitsSpliterator extends ProviderSpliterator implements Spliterator { /** Source of randomness used to initialise the new instances. */ private final SplittableUniformRandomProvider source; /** Generator to split to create new instances. */ private final SplittableUniformRandomProvider rng; /** * @param start Start position of the stream (inclusive). * @param end Upper limit of the stream (exclusive). * @param source Source of randomness used to initialise the new instances. * @param rng Generator to split to create new instances. */ ProviderSplitsSpliterator(long start, long end, SplittableUniformRandomProvider source, SplittableUniformRandomProvider rng) { super(start, end); this.source = source; this.rng = rng; } @Override public Spliterator trySplit() { final long start = position; final long middle = (start + end) >>> 1; if (middle <= start) { return null; } position = middle; return new ProviderSplitsSpliterator(start, middle, source.split(), rng); } @Override public boolean tryAdvance(Consumer action) { Objects.requireNonNull(action, NULL_ACTION); final long pos = position; if (pos < end) { // Advance before exceptions from the action are relayed to the caller position = pos + 1; action.accept(rng.split(source)); return true; } return false; } @Override public void forEachRemaining(Consumer action) { Objects.requireNonNull(action, NULL_ACTION); long pos = position; final long last = end; if (pos < last) { // Ensure forEachRemaining is called only once position = last; final SplittableUniformRandomProvider s = source; final SplittableUniformRandomProvider r = rng; do { action.accept(r.split(s)); } while (++pos < last); } } } /** * Spliterator for streams of int values that may be recursively split. */ static class ProviderIntsSpliterator extends ProviderSpliterator implements Spliterator.OfInt { /** Source of randomness. */ private final SplittableUniformRandomProvider source; /** Value generator function. */ private final ToIntFunction gen; /** * @param start Start position of the stream (inclusive). * @param end Upper limit of the stream (exclusive). * @param source Source of randomness. * @param gen Value generator function. */ ProviderIntsSpliterator(long start, long end, SplittableUniformRandomProvider source, ToIntFunction gen) { super(start, end); this.source = source; this.gen = gen; } @Override public Spliterator.OfInt trySplit() { final long start = position; final long middle = (start + end) >>> 1; if (middle <= start) { return null; } position = middle; return new ProviderIntsSpliterator(start, middle, source.split(), gen); } @Override public boolean tryAdvance(IntConsumer action) { Objects.requireNonNull(action, NULL_ACTION); final long pos = position; if (pos < end) { // Advance before exceptions from the action are relayed to the caller position = pos + 1; action.accept(gen.applyAsInt(source)); return true; } return false; } @Override public void forEachRemaining(IntConsumer action) { Objects.requireNonNull(action, NULL_ACTION); long pos = position; final long last = end; if (pos < last) { // Ensure forEachRemaining is called only once position = last; final SplittableUniformRandomProvider s = source; final ToIntFunction g = gen; do { action.accept(g.applyAsInt(s)); } while (++pos < last); } } } /** * Spliterator for streams of long values that may be recursively split. */ static class ProviderLongsSpliterator extends ProviderSpliterator implements Spliterator.OfLong { /** Source of randomness. */ private final SplittableUniformRandomProvider source; /** Value generator function. */ private final ToLongFunction gen; /** * @param start Start position of the stream (inclusive). * @param end Upper limit of the stream (exclusive). * @param source Source of randomness. * @param gen Value generator function. */ ProviderLongsSpliterator(long start, long end, SplittableUniformRandomProvider source, ToLongFunction gen) { super(start, end); this.source = source; this.gen = gen; } @Override public Spliterator.OfLong trySplit() { final long start = position; final long middle = (start + end) >>> 1; if (middle <= start) { return null; } position = middle; return new ProviderLongsSpliterator(start, middle, source.split(), gen); } @Override public boolean tryAdvance(LongConsumer action) { Objects.requireNonNull(action, NULL_ACTION); final long pos = position; if (pos < end) { // Advance before exceptions from the action are relayed to the caller position = pos + 1; action.accept(gen.applyAsLong(source)); return true; } return false; } @Override public void forEachRemaining(LongConsumer action) { Objects.requireNonNull(action, NULL_ACTION); long pos = position; final long last = end; if (pos < last) { // Ensure forEachRemaining is called only once position = last; final SplittableUniformRandomProvider s = source; final ToLongFunction g = gen; do { action.accept(g.applyAsLong(s)); } while (++pos < last); } } } /** * Spliterator for streams of double values that may be recursively split. */ static class ProviderDoublesSpliterator extends ProviderSpliterator implements Spliterator.OfDouble { /** Source of randomness. */ private final SplittableUniformRandomProvider source; /** Value generator function. */ private final ToDoubleFunction gen; /** * @param start Start position of the stream (inclusive). * @param end Upper limit of the stream (exclusive). * @param source Source of randomness. * @param gen Value generator function. */ ProviderDoublesSpliterator(long start, long end, SplittableUniformRandomProvider source, ToDoubleFunction gen) { super(start, end); this.source = source; this.gen = gen; } @Override public Spliterator.OfDouble trySplit() { final long start = position; final long middle = (start + end) >>> 1; if (middle <= start) { return null; } position = middle; return new ProviderDoublesSpliterator(start, middle, source.split(), gen); } @Override public boolean tryAdvance(DoubleConsumer action) { Objects.requireNonNull(action, NULL_ACTION); final long pos = position; if (pos < end) { // Advance before exceptions from the action are relayed to the caller position = pos + 1; action.accept(gen.applyAsDouble(source)); return true; } return false; } @Override public void forEachRemaining(DoubleConsumer action) { Objects.requireNonNull(action, NULL_ACTION); long pos = position; final long last = end; if (pos < last) { // Ensure forEachRemaining is called only once position = last; final SplittableUniformRandomProvider s = source; final ToDoubleFunction g = gen; do { action.accept(g.applyAsDouble(s)); } while (++pos < last); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy