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.  *      http://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. package org.apache.commons.rng.core.source32;

  18. import java.util.Arrays;
  19. import org.apache.commons.rng.core.util.NumberFactory;

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

  72.     /**
  73.      * Creates a new ISAAC random number generator.
  74.      *
  75.      * @param seed Initial seed
  76.      */
  77.     public ISAACRandom(int[] seed) {
  78.         setSeedInternal(seed);
  79.     }

  80.     /** {@inheritDoc} */
  81.     @Override
  82.     protected byte[] getStateInternal() {
  83.         final int[] sRsl = Arrays.copyOf(rsl, SIZE);
  84.         final int[] sMem = Arrays.copyOf(mem, SIZE);
  85.         final int[] sRem = Arrays.copyOf(new int[] {count, isaacA, isaacB, isaacC}, 4);

  86.         final int[] s = new int[2 * SIZE + sRem.length];
  87.         System.arraycopy(sRsl, 0, s, 0, SIZE);
  88.         System.arraycopy(sMem, 0, s, SIZE, SIZE);
  89.         System.arraycopy(sRem, 0, s, 2 * SIZE, sRem.length);

  90.         return composeStateInternal(NumberFactory.makeByteArray(s),
  91.                                     super.getStateInternal());
  92.     }

  93.     /** {@inheritDoc} */
  94.     @Override
  95.     protected void setStateInternal(byte[] s) {
  96.         final byte[][] c = splitStateInternal(s, (2 * SIZE + 4) * 4);

  97.         final int[] tmp = NumberFactory.makeIntArray(c[0]);
  98.         System.arraycopy(tmp, 0, rsl, 0, SIZE);
  99.         System.arraycopy(tmp, SIZE, mem, 0, SIZE);
  100.         final int offset = 2 * SIZE;
  101.         count = tmp[offset];
  102.         isaacA = tmp[offset + 1];
  103.         isaacB = tmp[offset + 2];
  104.         isaacC = tmp[offset + 3];

  105.         super.setStateInternal(c[1]);
  106.     }

  107.     /**
  108.      * Reseeds the RNG.
  109.      *
  110.      * @param seed Seed. Cannot be null.
  111.      */
  112.     private void setSeedInternal(int[] seed) {
  113.         final int seedLen = seed.length;
  114.         final int rslLen = rsl.length;
  115.         System.arraycopy(seed, 0, rsl, 0, Math.min(seedLen, rslLen));
  116.         if (seedLen < rslLen) {
  117.             for (int j = seedLen; j < rslLen; j++) {
  118.                 final long k = rsl[j - seedLen];
  119.                 rsl[j] = (int) (0x6c078965L * (k ^ k >> 30) + j & 0xffffffffL);
  120.             }
  121.         }
  122.         initState();
  123.     }

  124.     /** {@inheritDoc} */
  125.     @Override
  126.     public int next() {
  127.         if (count < 0) {
  128.             isaac();
  129.             count = SIZE - 1;
  130.         }
  131.         return rsl[count--];
  132.     }

  133.     /** Generate 256 results. */
  134.     private void isaac() {
  135.         isaacI = 0;
  136.         isaacJ = H_SIZE;
  137.         isaacB += ++isaacC;
  138.         while (isaacI < H_SIZE) {
  139.             isaac2();
  140.         }
  141.         isaacJ = 0;
  142.         while (isaacJ < H_SIZE) {
  143.             isaac2();
  144.         }
  145.     }

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

  165.     /** Lowest level internal loop. */
  166.     private void isaac3() {
  167.         mem[isaacI] = mem[(isaacX & MASK) >> 2] + isaacA + isaacB;
  168.         isaacB = mem[(mem[isaacI] >> SIZE_L & MASK) >> 2] + isaacX;
  169.         rsl[isaacI++] = isaacB;
  170.     }

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

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

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