View Javadoc
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  
18  package org.apache.commons.rng.core.source64;
19  
20  import java.util.Arrays;
21  
22  import org.apache.commons.rng.JumpableUniformRandomProvider;
23  import org.apache.commons.rng.LongJumpableUniformRandomProvider;
24  import org.apache.commons.rng.UniformRandomProvider;
25  import org.apache.commons.rng.core.util.NumberFactory;
26  
27  /**
28   * This abstract class is a base for algorithms from the Xor-Shift-Rotate family of 64-bit
29   * generators with 1024-bits of state.
30   *
31   * @see <a href="http://xoshiro.di.unimi.it/">xorshiro / xoroshiro generators</a>
32   * @since 1.3
33   */
34  abstract class AbstractXoRoShiRo1024 extends LongProvider implements LongJumpableUniformRandomProvider {
35      /** Size of the state vector. */
36      private static final int SEED_SIZE = 16;
37      /** The coefficients for the jump function. */
38      private static final long[] JUMP_COEFFICIENTS = {
39          0x931197d8e3177f17L, 0xb59422e0b9138c5fL, 0xf06a6afb49d668bbL, 0xacb8a6412c8a1401L,
40          0x12304ec85f0b3468L, 0xb7dfe7079209891eL, 0x405b7eec77d9eb14L, 0x34ead68280c44e4aL,
41          0xe0e4ba3e0ac9e366L, 0x8f46eda8348905b7L, 0x328bf4dbad90d6ffL, 0xc8fd6fb31c9effc3L,
42          0xe899d452d4b67652L, 0x45f387286ade3205L, 0x03864f454a8920bdL, 0xa68fa28725b1b384L,
43      };
44      /** The coefficients for the long jump function. */
45      private static final long[] LONG_JUMP_COEFFICIENTS = {
46          0x7374156360bbf00fL, 0x4630c2efa3b3c1f6L, 0x6654183a892786b1L, 0x94f7bfcbfb0f1661L,
47          0x27d8243d3d13eb2dL, 0x9701730f3dfb300fL, 0x2f293baae6f604adL, 0xa661831cb60cd8b6L,
48          0x68280c77d9fe008cL, 0x50554160f5ba9459L, 0x2fc20b17ec7b2a9aL, 0x49189bbdc8ec9f8fL,
49          0x92a65bca41852cc1L, 0xf46820dd0509c12aL, 0x52b00c35fbf92185L, 0x1e5b3b7f589e03c1L,
50      };
51      /** State. */
52      private final long[] state = new long[SEED_SIZE];
53      /** Index in "state" array. */
54      private int index;
55  
56      /**
57       * Creates a new instance.
58       *
59       * @param seed Initial seed.
60       * If the length is larger than 16, only the first 16 elements will
61       * be used; if smaller, the remaining elements will be automatically
62       * set. A seed containing all zeros will create a non-functional generator.
63       */
64      AbstractXoRoShiRo1024(long[] seed) {
65          setSeedInternal(seed);
66      }
67  
68      /**
69       * Creates a copy instance.
70       *
71       * @param source Source to copy.
72       */
73      protected AbstractXoRoShiRo1024(AbstractXoRoShiRo1024 source) {
74          super(source);
75          System.arraycopy(source.state, 0, state, 0, SEED_SIZE);
76          index = source.index;
77      }
78  
79      /** {@inheritDoc} */
80      @Override
81      protected byte[] getStateInternal() {
82          final long[] s = Arrays.copyOf(state, SEED_SIZE + 1);
83          s[SEED_SIZE] = index;
84  
85          return composeStateInternal(NumberFactory.makeByteArray(s),
86                                      super.getStateInternal());
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      protected void setStateInternal(byte[] s) {
92          final byte[][] c = splitStateInternal(s, (SEED_SIZE + 1) * 8);
93  
94          final long[] tmp = NumberFactory.makeLongArray(c[0]);
95          System.arraycopy(tmp, 0, state, 0, SEED_SIZE);
96          index = (int) tmp[SEED_SIZE];
97  
98          super.setStateInternal(c[1]);
99      }
100 
101     /**
102      * Seeds the RNG.
103      *
104      * @param seed Seed.
105      */
106     private void setSeedInternal(long[] seed) {
107         // Reset the whole state of this RNG (i.e. "state" and "index").
108         // Filling procedure is not part of the reference code.
109         fillState(state, seed);
110         index = 0;
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     public long next() {
116         final int q = index;
117         index = (index + 1) & 15;
118         final long s0 = state[index];
119         long s15 = state[q];
120         final long result = transform(s0, s15);
121 
122         s15 ^= s0;
123         state[q] = Long.rotateLeft(s0, 25) ^ s15 ^ (s15 << 27);
124         state[index] = Long.rotateLeft(s15, 36);
125 
126         return result;
127     }
128 
129     /**
130      * Transform the two consecutive 64-bit states of the generator to a 64-bit output.
131      * The transformation function shall vary with respect to different generators.
132      *
133      * @param s0 The current state.
134      * @param s15 The previous state.
135      * @return the output
136      */
137     protected abstract long transform(long s0, long s15);
138 
139     /**
140      * {@inheritDoc}
141      *
142      * <p>The jump size is the equivalent of 2<sup>512</sup>
143      * calls to {@link UniformRandomProvider#nextLong() nextLong()}. It can provide
144      * up to 2<sup>512</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     /**
154      * {@inheritDoc}
155      *
156      *
157      * <p>The jump size is the equivalent of 2<sup>768</sup> calls to
158      * {@link UniformRandomProvider#nextLong() nextLong()}. It can provide up to
159      * 2<sup>256</sup> non-overlapping subsequences of length 2<sup>768</sup>; each
160      * subsequence can provide up to 2<sup>256</sup> non-overlapping subsequences of
161      * length 2<sup>512</sup> using the {@link #jump()} method.</p>
162      */
163     @Override
164     public JumpableUniformRandomProvider longJump() {
165         final JumpableUniformRandomProvider copy = copy();
166         performJump(LONG_JUMP_COEFFICIENTS);
167         return copy;
168     }
169 
170     /**
171      * Create a copy.
172      *
173      * @return the copy
174      */
175     protected abstract AbstractXoRoShiRo1024 copy();
176 
177     /**
178      * Perform the jump to advance the generator state. Resets the cached state of the generator.
179      *
180      * @param jumpCoefficients the jump coefficients
181      */
182     private void performJump(long[] jumpCoefficients) {
183         final long[] newState = new long[SEED_SIZE];
184         for (final long jc : jumpCoefficients) {
185             for (int b = 0; b < 64; b++) {
186                 if ((jc & (1L << b)) != 0) {
187                     for (int i = 0; i < SEED_SIZE; i++) {
188                         newState[i] ^= state[(i + index) & 15];
189                     }
190                 }
191                 next();
192             }
193         }
194         // Note: Calling the next() function updates 'index'.
195         // The present index effectively becomes 0.
196         for (int j = 0; j < 16; j++) {
197             state[(j + index) & 15] = newState[j];
198         }
199         resetCachedState();
200     }
201 }