AbstractXoShiRo128.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 org.apache.commons.rng.JumpableUniformRandomProvider;
  19. import org.apache.commons.rng.LongJumpableUniformRandomProvider;
  20. import org.apache.commons.rng.UniformRandomProvider;
  21. import org.apache.commons.rng.core.util.NumberFactory;

  22. /**
  23.  * This abstract class is a base for algorithms from the Xor-Shift-Rotate family of 32-bit
  24.  * generators with 128-bits of state.
  25.  *
  26.  * @see <a href="http://xoshiro.di.unimi.it/">xorshiro / xoroshiro generators</a>
  27.  * @since 1.3
  28.  */
  29. abstract class AbstractXoShiRo128 extends IntProvider implements LongJumpableUniformRandomProvider {
  30.     /** Size of the state vector. */
  31.     private static final int SEED_SIZE = 4;
  32.     /** The coefficients for the jump function. */
  33.     private static final int[] JUMP_COEFFICIENTS = {
  34.         0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b
  35.     };
  36.     /** The coefficients for the long jump function. */
  37.     private static final int[] LONG_JUMP_COEFFICIENTS = {
  38.         0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662
  39.     };

  40.     // State is maintained using variables rather than an array for performance

  41.     /** State 0 of the generator. */
  42.     protected int state0;
  43.     /** State 1 of the generator. */
  44.     protected int state1;
  45.     /** State 2 of the generator. */
  46.     protected int state2;
  47.     /** State 3 of the generator. */
  48.     protected int state3;

  49.     /**
  50.      * Creates a new instance.
  51.      *
  52.      * @param seed Initial seed.
  53.      * If the length is larger than 4, only the first 4 elements will
  54.      * be used; if smaller, the remaining elements will be automatically
  55.      * set. A seed containing all zeros will create a non-functional generator.
  56.      */
  57.     AbstractXoShiRo128(int[] seed) {
  58.         if (seed.length < SEED_SIZE) {
  59.             final int[] state = new int[SEED_SIZE];
  60.             fillState(state, seed);
  61.             setState(state);
  62.         } else {
  63.             setState(seed);
  64.         }
  65.     }

  66.     /**
  67.      * Creates a new instance using a 4 element seed.
  68.      * A seed containing all zeros will create a non-functional generator.
  69.      *
  70.      * @param seed0 Initial seed element 0.
  71.      * @param seed1 Initial seed element 1.
  72.      * @param seed2 Initial seed element 2.
  73.      * @param seed3 Initial seed element 3.
  74.      */
  75.     AbstractXoShiRo128(int seed0, int seed1, int seed2, int seed3) {
  76.         state0 = seed0;
  77.         state1 = seed1;
  78.         state2 = seed2;
  79.         state3 = seed3;
  80.     }

  81.     /**
  82.      * Creates a copy instance.
  83.      *
  84.      * @param source Source to copy.
  85.      */
  86.     protected AbstractXoShiRo128(AbstractXoShiRo128 source) {
  87.         super(source);
  88.         state0 = source.state0;
  89.         state1 = source.state1;
  90.         state2 = source.state2;
  91.         state3 = source.state3;
  92.     }

  93.     /**
  94.      * Copies the state from the array into the generator state.
  95.      *
  96.      * @param state the new state
  97.      */
  98.     private void setState(int[] state) {
  99.         state0 = state[0];
  100.         state1 = state[1];
  101.         state2 = state[2];
  102.         state3 = state[3];
  103.     }

  104.     /** {@inheritDoc} */
  105.     @Override
  106.     protected byte[] getStateInternal() {
  107.         return composeStateInternal(NumberFactory.makeByteArray(
  108.                                         new int[] {state0, state1, state2, state3}),
  109.                                     super.getStateInternal());
  110.     }

  111.     /** {@inheritDoc} */
  112.     @Override
  113.     protected void setStateInternal(byte[] s) {
  114.         final byte[][] c = splitStateInternal(s, SEED_SIZE * 4);

  115.         setState(NumberFactory.makeIntArray(c[0]));

  116.         super.setStateInternal(c[1]);
  117.     }

  118.     /** {@inheritDoc} */
  119.     @Override
  120.     public int next() {
  121.         final int result = nextOutput();

  122.         final int t = state1 << 9;

  123.         state2 ^= state0;
  124.         state3 ^= state1;
  125.         state1 ^= state2;
  126.         state0 ^= state3;

  127.         state2 ^= t;

  128.         state3 = Integer.rotateLeft(state3, 11);

  129.         return result;
  130.     }

  131.     /**
  132.      * Use the current state to compute the next output from the generator.
  133.      * The output function shall vary with respect to different generators.
  134.      * This method is called from {@link #next()} before the current state is updated.
  135.      *
  136.      * @return the next output
  137.      */
  138.     protected abstract int nextOutput();

  139.     /**
  140.      * {@inheritDoc}
  141.      *
  142.      * <p>The jump size is the equivalent of 2<sup>64</sup>
  143.      * calls to {@link UniformRandomProvider#nextInt() nextInt()}. It can provide
  144.      * up to 2<sup>64</sup> non-overlapping subsequences.</p>
  145.      */
  146.     @Override
  147.     public UniformRandomProvider jump() {
  148.         final UniformRandomProvider copy = copy();
  149.         performJump(JUMP_COEFFICIENTS);
  150.         return copy;
  151.     }

  152.     /**
  153.      * {@inheritDoc}
  154.      *
  155.      * <p>The jump size is the equivalent of 2<sup>96</sup> calls to
  156.      * {@link UniformRandomProvider#nextLong() nextLong()}. It can provide up to
  157.      * 2<sup>32</sup> non-overlapping subsequences of length 2<sup>96</sup>; each
  158.      * subsequence can provide up to 2<sup>32</sup> non-overlapping subsequences of
  159.      * length 2<sup>64</sup> using the {@link #jump()} method.</p>
  160.      */
  161.     @Override
  162.     public JumpableUniformRandomProvider longJump() {
  163.         final JumpableUniformRandomProvider copy = copy();
  164.         performJump(LONG_JUMP_COEFFICIENTS);
  165.         return copy;
  166.     }

  167.     /**
  168.      * Create a copy.
  169.      *
  170.      * @return the copy
  171.      */
  172.     protected abstract AbstractXoShiRo128 copy();

  173.     /**
  174.      * Perform the jump to advance the generator state. Resets the cached state of the generator.
  175.      *
  176.      * @param jumpCoefficients Jump coefficients.
  177.      */
  178.     private void performJump(int[] jumpCoefficients) {
  179.         int s0 = 0;
  180.         int s1 = 0;
  181.         int s2 = 0;
  182.         int s3 = 0;
  183.         for (final int jc : jumpCoefficients) {
  184.             for (int b = 0; b < 32; b++) {
  185.                 if ((jc & (1 << b)) != 0) {
  186.                     s0 ^= state0;
  187.                     s1 ^= state1;
  188.                     s2 ^= state2;
  189.                     s3 ^= state3;
  190.                 }
  191.                 next();
  192.             }
  193.         }
  194.         state0 = s0;
  195.         state1 = s1;
  196.         state2 = s2;
  197.         state3 = s3;
  198.         resetCachedState();
  199.     }
  200. }