blob: 278c5bb7ba8cf5435168cd53f11d2915439f701b [file] [log] [blame]
/*
* 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.core.source64;
import org.apache.commons.rng.core.util.NumberFactory;
import org.apache.commons.rng.core.BaseProvider;
/**
* Base class for all implementations that provide a {@code long}-based
* source randomness.
*/
public abstract class LongProvider
extends BaseProvider
implements RandomLongSource {
/**
* Provides a bit source for booleans.
*
* <p>A cached value from a call to {@link #nextLong()}.
*/
private long booleanSource; // Initialised as 0
/**
* The bit mask of the boolean source to obtain the boolean bit.
*
* <p>The bit mask contains a single bit set. This begins at the least
* significant bit and is gradually shifted upwards until overflow to zero.
*
* <p>When zero a new boolean source should be created and the mask set to the
* least significant bit (i.e. 1).
*/
private long booleanBitMask; // Initialised as 0
/**
* Provides a source for ints.
*
* <p>A cached value from a call to {@link #nextLong()}.
*/
private long intSource;
/** Flag to indicate an int source has been cached. */
private boolean cachedIntSource; // Initialised as false
/**
* Creates a new instance.
*/
public LongProvider() {
super();
}
/**
* Creates a new instance copying the state from the source.
*
* <p>This provides base functionality to allow a generator to create a copy, for example
* for use in the {@link org.apache.commons.rng.JumpableUniformRandomProvider
* JumpableUniformRandomProvider} interface.
*
* @param source Source to copy.
* @since 1.3
*/
protected LongProvider(LongProvider source) {
booleanSource = source.booleanSource;
booleanBitMask = source.booleanBitMask;
intSource = source.intSource;
cachedIntSource = source.cachedIntSource;
}
/**
* Reset the cached state used in the default implementation of {@link #nextBoolean()}
* and {@link #nextInt()}.
*
* <p>This should be used when the state is no longer valid, for example after a jump
* performed for the {@link org.apache.commons.rng.JumpableUniformRandomProvider
* JumpableUniformRandomProvider} interface.</p>
*
* @since 1.3
*/
protected void resetCachedState() {
booleanSource = 0L;
booleanBitMask = 0L;
intSource = 0L;
cachedIntSource = false;
}
/** {@inheritDoc} */
@Override
protected byte[] getStateInternal() {
// Pack the boolean inefficiently as a long
final long[] state = {booleanSource,
booleanBitMask,
intSource,
cachedIntSource ? 1 : 0 };
return composeStateInternal(NumberFactory.makeByteArray(state),
super.getStateInternal());
}
/** {@inheritDoc} */
@Override
protected void setStateInternal(byte[] s) {
final byte[][] c = splitStateInternal(s, 32);
final long[] state = NumberFactory.makeLongArray(c[0]);
booleanSource = state[0];
booleanBitMask = state[1];
intSource = state[2];
// Non-zero is true
cachedIntSource = state[3] != 0;
super.setStateInternal(c[1]);
}
/** {@inheritDoc} */
@Override
public long nextLong() {
return next();
}
/** {@inheritDoc} */
@Override
public int nextInt() {
// Directly store and use the long value as a source for ints
if (cachedIntSource) {
// Consume the cache value
cachedIntSource = false;
// Return the lower 32 bits
return NumberFactory.extractLo(intSource);
}
// Fill the cache
cachedIntSource = true;
intSource = nextLong();
// Return the upper 32 bits
return NumberFactory.extractHi(intSource);
}
/** {@inheritDoc} */
@Override
public double nextDouble() {
return NumberFactory.makeDouble(nextLong());
}
/** {@inheritDoc} */
@Override
public boolean nextBoolean() {
// Shift up. This will eventually overflow and become zero.
booleanBitMask <<= 1;
// The mask will either contain a single bit or none.
if (booleanBitMask == 0) {
// Set the least significant bit
booleanBitMask = 1;
// Get the next value
booleanSource = nextLong();
}
// Return if the bit is set
return (booleanSource & booleanBitMask) != 0;
}
/** {@inheritDoc} */
@Override
public float nextFloat() {
return NumberFactory.makeFloat(nextInt());
}
/** {@inheritDoc} */
@Override
public void nextBytes(byte[] bytes) {
nextBytesFill(this, bytes, 0, bytes.length);
}
/** {@inheritDoc} */
@Override
public void nextBytes(byte[] bytes,
int start,
int len) {
checkIndex(0, bytes.length - 1, start);
checkIndex(0, bytes.length - start, len);
nextBytesFill(this, bytes, start, len);
}
/**
* Generates random bytes and places them into a user-supplied array.
*
* <p>
* 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.
* </p>
*
* @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 nextBytesFill(RandomLongSource source,
byte[] bytes,
int start,
int len) {
int index = start; // Index of first insertion.
// Index of first insertion plus multiple of 8 part of length
// (i.e. length with 3 least significant bits unset).
final int indexLoopLimit = index + (len & 0x7ffffff8);
// Start filling in the byte array, 8 bytes at a time.
while (index < indexLoopLimit) {
final long random = source.next();
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);
}
final int indexLimit = start + len; // Index of last insertion + 1.
// Fill in the remaining bytes.
if (index < indexLimit) {
long random = source.next();
while (true) {
bytes[index++] = (byte) random;
if (index < indexLimit) {
random >>>= 8;
} else {
break;
}
}
}
}
}