/* * 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.math3.random; import java.io.Serializable; import org.apache.commons.math3.util.FastMath; /** * <a href="http://burtleburtle.net/bob/rand/isaacafa.html"> * ISAAC: a fast cryptographic pseudo-random number generator</a> * <br/> * ISAAC (Indirection, Shift, Accumulate, Add, and Count) generates 32-bit * random numbers. * ISAAC has been designed to be cryptographically secure and is inspired * by RC4. * Cycles are guaranteed to be at least 2<sup>40</sup> values long, and they * are 2<sup>8295</sup> values long on average. * The results are uniformly distributed, unbiased, and unpredictable unless * you know the seed. * <br/> * This code is based (with minor changes and improvements) on the original * implementation of the algorithm by Bob Jenkins. * <br/> * * @since 3.0 */ public class ISAACRandom extends BitsStreamGenerator implements Serializable { /** Serializable version identifier */ private static final long serialVersionUID = 7288197941165002400L; /** Log of size of rsl[] and mem[] */ private static final int SIZE_L = 8; /** Size of rsl[] and mem[] */ private static final int SIZE = 1 << SIZE_L; /** Half-size of rsl[] and mem[] */ private static final int H_SIZE = SIZE >> 1; /** For pseudo-random lookup */ private static final int MASK = SIZE - 1 << 2; /** The golden ratio */ private static final int GLD_RATIO = 0x9e3779b9; /** The results given to the user */ private final int[] rsl = new int[SIZE]; /** The internal state */ private final int[] mem = new int[SIZE]; /** Count through the results in rsl[] */ private int count; /** Accumulator */ private int isaacA; /** The last result */ private int isaacB; /** Counter, guarantees cycle is at least 2^40 */ private int isaacC; /** Service variable. */ private final int[] arr = new int[8]; /** Service variable. */ private int isaacX; /** Service variable. */ private int isaacI; /** Service variable. */ private int isaacJ; /** * Creates a new ISAAC random number generator. * <br/> * The instance is initialized using a combination of the * current time and system hash code of the instance as the seed. */ public ISAACRandom() { setSeed(System.currentTimeMillis() + System.identityHashCode(this)); } /** * Creates a new ISAAC random number generator using a single long seed. * * @param seed Initial seed. */ public ISAACRandom(long seed) { setSeed(seed); } /** * Creates a new ISAAC random number generator using an int array seed. * * @param seed Initial seed. If {@code null}, the seed will be related * to the current time. */ public ISAACRandom(int[] seed) { setSeed(seed); } /** {@inheritDoc} */ @Override public void setSeed(int seed) { setSeed(new int[]{seed}); } /** {@inheritDoc} */ @Override public void setSeed(long seed) { setSeed(new int[]{(int) (seed >>> 32), (int) (seed & 0xffffffffL)}); } /** {@inheritDoc} */ @Override public void setSeed(int[] seed) { if (seed == null) { setSeed(System.currentTimeMillis() + System.identityHashCode(this)); return; } final int seedLen = seed.length; final int rslLen = rsl.length; System.arraycopy(seed, 0, rsl, 0, FastMath.min(seedLen, rslLen)); if (seedLen < rslLen) { for (int j = seedLen; j < rslLen; j++) { long k = rsl[j - seedLen]; rsl[j] = (int) (0x6c078965L * (k ^ k >> 30) + j & 0xffffffffL); } } initState(); } /** {@inheritDoc} */ @Override protected int next(int bits) { if (count < 0) { isaac(); count = SIZE - 1; } return rsl[count--] >>> 32 - bits; } /** Generate 256 results */ private void isaac() { isaacI = 0; isaacJ = H_SIZE; isaacB += ++isaacC; while (isaacI < H_SIZE) { isaac2(); } isaacJ = 0; while (isaacJ < H_SIZE) { isaac2(); } } /** Intermediate internal loop. */ private void isaac2() { isaacX = mem[isaacI]; isaacA ^= isaacA << 13; isaacA += mem[isaacJ++]; isaac3(); isaacX = mem[isaacI]; isaacA ^= isaacA >>> 6; isaacA += mem[isaacJ++]; isaac3(); isaacX = mem[isaacI]; isaacA ^= isaacA << 2; isaacA += mem[isaacJ++]; isaac3(); isaacX = mem[isaacI]; isaacA ^= isaacA >>> 16; isaacA += mem[isaacJ++]; isaac3(); } /** Lowest level internal loop. */ private void isaac3() { mem[isaacI] = mem[(isaacX & MASK) >> 2] + isaacA + isaacB; isaacB = mem[(mem[isaacI] >> SIZE_L & MASK) >> 2] + isaacX; rsl[isaacI++] = isaacB; } /** Initialize, or reinitialize, this instance of rand. */ private void initState() { isaacA = 0; isaacB = 0; isaacC = 0; for (int j = 0; j < arr.length; j++) { arr[j] = GLD_RATIO; } for (int j = 0; j < 4; j++) { shuffle(); } // fill in mem[] with messy stuff for (int j = 0; j < SIZE; j += 8) { arr[0] += rsl[j]; arr[1] += rsl[j + 1]; arr[2] += rsl[j + 2]; arr[3] += rsl[j + 3]; arr[4] += rsl[j + 4]; arr[5] += rsl[j + 5]; arr[6] += rsl[j + 6]; arr[7] += rsl[j + 7]; shuffle(); setState(j); } // second pass makes all of seed affect all of mem for (int j = 0; j < SIZE; j += 8) { arr[0] += mem[j]; arr[1] += mem[j + 1]; arr[2] += mem[j + 2]; arr[3] += mem[j + 3]; arr[4] += mem[j + 4]; arr[5] += mem[j + 5]; arr[6] += mem[j + 6]; arr[7] += mem[j + 7]; shuffle(); setState(j); } isaac(); count = SIZE - 1; clear(); } /** Shuffle array. */ private void shuffle() { arr[0] ^= arr[1] << 11; arr[3] += arr[0]; arr[1] += arr[2]; arr[1] ^= arr[2] >>> 2; arr[4] += arr[1]; arr[2] += arr[3]; arr[2] ^= arr[3] << 8; arr[5] += arr[2]; arr[3] += arr[4]; arr[3] ^= arr[4] >>> 16; arr[6] += arr[3]; arr[4] += arr[5]; arr[4] ^= arr[5] << 10; arr[7] += arr[4]; arr[5] += arr[6]; arr[5] ^= arr[6] >>> 4; arr[0] += arr[5]; arr[6] += arr[7]; arr[6] ^= arr[7] << 8; arr[1] += arr[6]; arr[7] += arr[0]; arr[7] ^= arr[0] >>> 9; arr[2] += arr[7]; arr[0] += arr[1]; } /** Set the state by copying the internal arrays. * * @param start First index into {@link #mem} array. */ private void setState(int start) { mem[start] = arr[0]; mem[start + 1] = arr[1]; mem[start + 2] = arr[2]; mem[start + 3] = arr[3]; mem[start + 4] = arr[4]; mem[start + 5] = arr[5]; mem[start + 6] = arr[6]; mem[start + 7] = arr[7]; } }