ISAACRandom.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.random;

  22. import java.io.Serializable;

  23. import org.hipparchus.util.FastMath;

  24. /**
  25.  * A fast cryptographic pseudo-random number generator.
  26.  * <p>
  27.  * ISAAC (Indirection, Shift, Accumulate, Add, and Count) generates 32-bit
  28.  * random numbers.
  29.  * ISAAC has been designed to be cryptographically secure and is inspired
  30.  * by RC4.
  31.  * Cycles are guaranteed to be at least 2<sup>40</sup> values long, and they
  32.  * are 2<sup>8295</sup> values long on average.
  33.  * The results are uniformly distributed, unbiased, and unpredictable unless
  34.  * you know the seed.
  35.  * <p>
  36.  * This code is based (with minor changes and improvements) on the original
  37.  * implementation of the algorithm by Bob Jenkins.
  38.  *
  39.  * @see  <a href="http://burtleburtle.net/bob/rand/isaacafa.html">
  40.  * ISAAC: a fast cryptographic pseudo-random number generator</a>
  41.  */
  42. public class ISAACRandom extends IntRandomGenerator implements Serializable {
  43.     /** Serializable version identifier */
  44.     private static final long serialVersionUID = 20160529L;
  45.     /** Log of size of rsl[] and mem[] */
  46.     private static final int SIZE_L = 8;
  47.     /** Size of rsl[] and mem[] */
  48.     private static final int SIZE = 1 << SIZE_L;
  49.     /** Half-size of rsl[] and mem[] */
  50.     private static final int H_SIZE = SIZE >> 1;
  51.     /** For pseudo-random lookup */
  52.     private static final int MASK = SIZE - 1 << 2;
  53.     /** The golden ratio */
  54.     private static final int GLD_RATIO = 0x9e3779b9;
  55.     /** The results given to the user */
  56.     private final int[] rsl = new int[SIZE];
  57.     /** The internal state */
  58.     private final int[] mem = new int[SIZE];
  59.     /** Count through the results in rsl[] */
  60.     private int count;
  61.     /** Accumulator */
  62.     private int isaacA;
  63.     /** The last result */
  64.     private int isaacB;
  65.     /** Counter, guarantees cycle is at least 2^40 */
  66.     private int isaacC;
  67.     /** Service variable. */
  68.     private final int[] arr = new int[8];
  69.     /** Service variable. */
  70.     private int isaacX;
  71.     /** Service variable. */
  72.     private int isaacI;
  73.     /** Service variable. */
  74.     private int isaacJ;


  75.     /**
  76.      * Creates a new ISAAC random number generator.
  77.      * <br>
  78.      * The instance is initialized using a combination of the
  79.      * current time and system hash code of the instance as the seed.
  80.      */
  81.     public ISAACRandom() {
  82.         setSeed(System.currentTimeMillis() + System.identityHashCode(this));
  83.     }

  84.     /**
  85.      * Creates a new ISAAC random number generator using a single long seed.
  86.      *
  87.      * @param seed Initial seed.
  88.      */
  89.     public ISAACRandom(long seed) {
  90.         setSeed(seed);
  91.     }

  92.     /**
  93.      * Creates a new ISAAC random number generator using an int array seed.
  94.      *
  95.      * @param seed Initial seed. If {@code null}, the seed will be related
  96.      * to the current time.
  97.      */
  98.     public ISAACRandom(int[] seed) {
  99.         setSeed(seed);
  100.     }

  101.     /** {@inheritDoc} */
  102.     @Override
  103.     public void setSeed(int[] seed) {
  104.         if (seed == null) {
  105.             setSeed(System.currentTimeMillis() + System.identityHashCode(this));
  106.             return;
  107.         }
  108.         final int seedLen = seed.length;
  109.         final int rslLen = rsl.length;
  110.         System.arraycopy(seed, 0, rsl, 0, FastMath.min(seedLen, rslLen));
  111.         if (seedLen < rslLen) {
  112.             for (int j = seedLen; j < rslLen; j++) {
  113.                 long k = rsl[j - seedLen];
  114.                 rsl[j] = (int) (0x6c078965L * (k ^ k >> 30) + j & 0xffffffffL);
  115.             }
  116.         }
  117.         initState();
  118.     }

  119.     /** {@inheritDoc} */
  120.     @Override
  121.     public int nextInt() {
  122.         if (count < 0) {
  123.             isaac();
  124.             count = SIZE - 1;
  125.         }
  126.         return rsl[count--];
  127.     }

  128.     /** Generate 256 results */
  129.     private void isaac() {
  130.         isaacI = 0;
  131.         isaacJ = H_SIZE;
  132.         isaacB += ++isaacC;
  133.         while (isaacI < H_SIZE) {
  134.             isaac2();
  135.         }
  136.         isaacJ = 0;
  137.         while (isaacJ < H_SIZE) {
  138.             isaac2();
  139.         }
  140.     }

  141.     /** Intermediate internal loop. */
  142.     private void isaac2() {
  143.         isaacX = mem[isaacI];
  144.         isaacA ^= isaacA << 13;
  145.         isaacA += mem[isaacJ++];
  146.         isaac3();
  147.         isaacX = mem[isaacI];
  148.         isaacA ^= isaacA >>> 6;
  149.         isaacA += mem[isaacJ++];
  150.         isaac3();
  151.         isaacX = mem[isaacI];
  152.         isaacA ^= isaacA << 2;
  153.         isaacA += mem[isaacJ++];
  154.         isaac3();
  155.         isaacX = mem[isaacI];
  156.         isaacA ^= isaacA >>> 16;
  157.         isaacA += mem[isaacJ++];
  158.         isaac3();
  159.     }

  160.     /** Lowest level internal loop. */
  161.     private void isaac3() {
  162.         mem[isaacI] = mem[(isaacX & MASK) >> 2] + isaacA + isaacB;
  163.         isaacB = mem[(mem[isaacI] >> SIZE_L & MASK) >> 2] + isaacX;
  164.         rsl[isaacI++] = isaacB;
  165.     }

  166.     /** Initialize, or reinitialize, this instance of rand. */
  167.     private void initState() {
  168.         isaacA = 0;
  169.         isaacB = 0;
  170.         isaacC = 0;
  171.         for (int j = 0; j < arr.length; j++) {
  172.             arr[j] = GLD_RATIO;
  173.         }
  174.         for (int j = 0; j < 4; j++) {
  175.             shuffle();
  176.         }
  177.         // fill in mem[] with messy stuff
  178.         for (int j = 0; j < SIZE; j += 8) {
  179.             arr[0] += rsl[j];
  180.             arr[1] += rsl[j + 1];
  181.             arr[2] += rsl[j + 2];
  182.             arr[3] += rsl[j + 3];
  183.             arr[4] += rsl[j + 4];
  184.             arr[5] += rsl[j + 5];
  185.             arr[6] += rsl[j + 6];
  186.             arr[7] += rsl[j + 7];
  187.             shuffle();
  188.             setState(j);
  189.         }
  190.         // second pass makes all of seed affect all of mem
  191.         for (int j = 0; j < SIZE; j += 8) {
  192.             arr[0] += mem[j];
  193.             arr[1] += mem[j + 1];
  194.             arr[2] += mem[j + 2];
  195.             arr[3] += mem[j + 3];
  196.             arr[4] += mem[j + 4];
  197.             arr[5] += mem[j + 5];
  198.             arr[6] += mem[j + 6];
  199.             arr[7] += mem[j + 7];
  200.             shuffle();
  201.             setState(j);
  202.         }
  203.         isaac();
  204.         count = SIZE - 1;
  205.         clearCache();
  206.     }

  207.     /** Shuffle array. */
  208.     private void shuffle() {
  209.         arr[0] ^= arr[1] << 11;
  210.         arr[3] += arr[0];
  211.         arr[1] += arr[2];
  212.         arr[1] ^= arr[2] >>> 2;
  213.         arr[4] += arr[1];
  214.         arr[2] += arr[3];
  215.         arr[2] ^= arr[3] << 8;
  216.         arr[5] += arr[2];
  217.         arr[3] += arr[4];
  218.         arr[3] ^= arr[4] >>> 16;
  219.         arr[6] += arr[3];
  220.         arr[4] += arr[5];
  221.         arr[4] ^= arr[5] << 10;
  222.         arr[7] += arr[4];
  223.         arr[5] += arr[6];
  224.         arr[5] ^= arr[6] >>> 4;
  225.         arr[0] += arr[5];
  226.         arr[6] += arr[7];
  227.         arr[6] ^= arr[7] << 8;
  228.         arr[1] += arr[6];
  229.         arr[7] += arr[0];
  230.         arr[7] ^= arr[0] >>> 9;
  231.         arr[2] += arr[7];
  232.         arr[0] += arr[1];
  233.     }

  234.     /** Set the state by copying the internal arrays.
  235.      *
  236.      * @param start First index into {@link #mem} array.
  237.      */
  238.     private void setState(int start) {
  239.         mem[start] = arr[0];
  240.         mem[start + 1] = arr[1];
  241.         mem[start + 2] = arr[2];
  242.         mem[start + 3] = arr[3];
  243.         mem[start + 4] = arr[4];
  244.         mem[start + 5] = arr[5];
  245.         mem[start + 6] = arr[6];
  246.         mem[start + 7] = arr[7];
  247.     }
  248. }