summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/random/ISAACRandom.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/math3/random/ISAACRandom.java')
-rw-r--r--src/main/java/org/apache/commons/math3/random/ISAACRandom.java279
1 files changed, 279 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/random/ISAACRandom.java b/src/main/java/org/apache/commons/math3/random/ISAACRandom.java
new file mode 100644
index 0000000..8d10af5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/ISAACRandom.java
@@ -0,0 +1,279 @@
+/*
+ * 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 org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * <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];
+ }
+}