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;
19  
20  import java.util.Arrays;
21  import org.apache.commons.rng.RestorableUniformRandomProvider;
22  import org.apache.commons.rng.RandomProviderState;
23  
24  /**
25   * Base class with default implementation for common methods.
26   */
27  public abstract class BaseProvider
28      implements RestorableUniformRandomProvider {
29      /**
30       * The fractional part of the golden ratio, phi, scaled to 64-bits and rounded to odd.
31       * <pre>
32       * phi = (sqrt(5) - 1) / 2) * 2^64
33       * </pre>
34       * @see <a href="https://en.wikipedia.org/wiki/Golden_ratio">Golden ratio</a>
35       */
36      private static final long GOLDEN_RATIO_64 = 0x9e3779b97f4a7c15L;
37      /** The fractional part of the golden ratio, phi, scaled to 32-bits and rounded to odd. */
38      private static final int GOLDEN_RATIO_32 = 0x9e3779b9;
39  
40      /** {@inheritDoc} */
41      @Override
42      public RandomProviderState saveState() {
43          return new RandomProviderDefaultState(getStateInternal());
44      }
45  
46      /** {@inheritDoc} */
47      @Override
48      public void restoreState(RandomProviderState state) {
49          if (state instanceof RandomProviderDefaultState) {
50              setStateInternal(((RandomProviderDefaultState) state).getState());
51          } else {
52              throw new IllegalArgumentException("Foreign instance");
53          }
54      }
55  
56      /** {@inheritDoc} */
57      @Override
58      public String toString() {
59          return getClass().getName();
60      }
61  
62      /**
63       * Combine parent and subclass states.
64       * This method must be called by all subclasses in order to ensure
65       * that state can be restored in case some of it is stored higher
66       * up in the class hierarchy.
67       *
68       * I.e. the body of the overridden {@link #getStateInternal()},
69       * will end with a statement like the following:
70       * <pre>
71       *  <code>
72       *    return composeStateInternal(state,
73       *                                super.getStateInternal());
74       *  </code>
75       * </pre>
76       * where {@code state} is the state needed and defined by the class
77       * where the method is overridden.
78       *
79       * @param state State of the calling class.
80       * @param parentState State of the calling class' parent.
81       * @return the combined state.
82       * Bytes that belong to the local state will be stored at the
83       * beginning of the resulting array.
84       */
85      protected byte[] composeStateInternal(byte[] state,
86                                            byte[] parentState) {
87          final int len = parentState.length + state.length;
88          final byte[] c = new byte[len];
89          System.arraycopy(state, 0, c, 0, state.length);
90          System.arraycopy(parentState, 0, c, state.length, parentState.length);
91          return c;
92      }
93  
94      /**
95       * Splits the given {@code state} into a part to be consumed by the caller
96       * in order to restore its local state, while the reminder is passed to
97       * the parent class.
98       *
99       * I.e. the body of the overridden {@link #setStateInternal(byte[])},
100      * will contain statements like the following:
101      * <pre>
102      *  <code>
103      *    final byte[][] s = splitState(state, localStateLength);
104      *    // Use "s[0]" to recover the local state.
105      *    super.setStateInternal(s[1]);
106      *  </code>
107      * </pre>
108      * where {@code state} is the combined state of the calling class and of
109      * all its parents.
110      *
111      * @param state State.
112      * The local state must be stored at the beginning of the array.
113      * @param localStateLength Number of elements that will be consumed by the
114      * locally defined state.
115      * @return the local state (in slot 0) and the parent state (in slot 1).
116      * @throws IllegalStateException if {@code state.length < localStateLength}.
117      */
118     protected byte[][] splitStateInternal(byte[] state,
119                                           int localStateLength) {
120         checkStateSize(state, localStateLength);
121 
122         final byte[] local = new byte[localStateLength];
123         System.arraycopy(state, 0, local, 0, localStateLength);
124         final int parentLength = state.length - localStateLength;
125         final byte[] parent = new byte[parentLength];
126         System.arraycopy(state, localStateLength, parent, 0, parentLength);
127 
128         return new byte[][] {local, parent};
129     }
130 
131     /**
132      * Creates a snapshot of the RNG state.
133      *
134      * @return the internal state.
135      */
136     protected byte[] getStateInternal() {
137         // This class has no state (and is the top-level class that
138         // declares this method).
139         return new byte[0];
140     }
141 
142     /**
143      * Resets the RNG to the given {@code state}.
144      *
145      * @param state State (previously obtained by a call to
146      * {@link #getStateInternal()}).
147      * @throws IllegalStateException if the size of the given array is
148      * not consistent with the state defined by this class.
149      *
150      * @see #checkStateSize(byte[],int)
151      */
152     protected void setStateInternal(byte[] state) {
153         if (state.length != 0) {
154             // This class has no state.
155             throw new IllegalStateException("State not fully recovered by subclasses");
156         }
157     }
158 
159     /**
160      * Simple filling procedure.
161      * It will
162      * <ol>
163      *  <li>
164      *   fill the beginning of {@code state} by copying
165      *   {@code min(seed.length, state.length)} elements from
166      *   {@code seed},
167      *  </li>
168      *  <li>
169      *   set all remaining elements of {@code state} with non-zero
170      *   values (even if {@code seed.length < state.length}).
171      *  </li>
172      * </ol>
173      *
174      * @param state State. Must be allocated.
175      * @param seed Seed. Cannot be null.
176      */
177     protected void fillState(int[] state,
178                              int[] seed) {
179         final int stateSize = state.length;
180         final int seedSize = seed.length;
181         System.arraycopy(seed, 0, state, 0, Math.min(seedSize, stateSize));
182 
183         if (seedSize < stateSize) {
184             for (int i = seedSize; i < stateSize; i++) {
185                 state[i] = (int) (scrambleWell(state[i - seed.length], i) & 0xffffffffL);
186             }
187         }
188     }
189 
190     /**
191      * Simple filling procedure.
192      * It will
193      * <ol>
194      *  <li>
195      *   fill the beginning of {@code state} by copying
196      *   {@code min(seed.length, state.length)} elements from
197      *   {@code seed},
198      *  </li>
199      *  <li>
200      *   set all remaining elements of {@code state} with non-zero
201      *   values (even if {@code seed.length < state.length}).
202      *  </li>
203      * </ol>
204      *
205      * @param state State. Must be allocated.
206      * @param seed Seed. Cannot be null.
207      */
208     protected void fillState(long[] state,
209                              long[] seed) {
210         final int stateSize = state.length;
211         final int seedSize = seed.length;
212         System.arraycopy(seed, 0, state, 0, Math.min(seedSize, stateSize));
213 
214         if (seedSize < stateSize) {
215             for (int i = seedSize; i < stateSize; i++) {
216                 state[i] = scrambleWell(state[i - seed.length], i);
217             }
218         }
219     }
220 
221     /**
222      * Checks that the {@code state} has the {@code expected} size.
223      *
224      * @param state State.
225      * @param expected Expected length of {@code state} array.
226      * @throws IllegalStateException if {@code state.length < expected}.
227      * @deprecated Method is used internally and should be made private in
228      * some future release.
229      */
230     @Deprecated
231     protected void checkStateSize(byte[] state,
232                                   int expected) {
233         if (state.length < expected) {
234             throw new IllegalStateException("State size must be larger than " +
235                                             expected + " but was " + state.length);
236         }
237     }
238 
239     /**
240      * Checks whether {@code index} is in the range {@code [min, max]}.
241      *
242      * @param min Lower bound.
243      * @param max Upper bound.
244      * @param index Value that must lie within the {@code [min, max]} interval.
245      * @throws IndexOutOfBoundsException if {@code index} is not within the
246      * {@code [min, max]} interval.
247      */
248     protected void checkIndex(int min,
249                               int max,
250                               int index) {
251         if (index < min ||
252             index > max) {
253             throw new IndexOutOfBoundsException(index + " is out of interval [" +
254                                                 min + ", " +
255                                                 max + "]");
256         }
257     }
258 
259     /**
260      * Transformation used to scramble the initial state of
261      * a generator.
262      *
263      * @param n Seed element.
264      * @param mult Multiplier.
265      * @param shift Shift.
266      * @param add Offset.
267      * @return the transformed seed element.
268      */
269     private static long scramble(long n,
270                                  long mult,
271                                  int shift,
272                                  int add) {
273         // Code inspired from "AbstractWell" class.
274         return mult * (n ^ (n >> shift)) + add;
275     }
276 
277     /**
278      * Transformation used to scramble the initial state of
279      * a generator.
280      *
281      * @param n Seed element.
282      * @param add Offset.
283      * @return the transformed seed element.
284      * @see #scramble(long,long,int,int)
285      */
286     private static long scrambleWell(long n,
287                                      int add) {
288         // Code inspired from "AbstractWell" class.
289         return scramble(n, 1812433253L, 30, add);
290     }
291 
292     /**
293      * Extend the seed to the specified minimum length. If the seed is equal or greater than the
294      * minimum length, return the same seed unchanged. Otherwise:
295      * <ol>
296      *  <li>Create a new array of the specified length
297      *  <li>Copy all elements of the seed into the array
298      *  <li>Fill the remaining values. The additional values will have at most one occurrence
299      *   of zero. If the original seed is all zero, the first extended value will be non-zero.
300      *  </li>
301      * </ol>
302      *
303      * <p>This method can be used in constructors that must pass their seed to the super class
304      * to avoid a duplication of seed expansion to the minimum length required by the super class
305      * and the class:
306      * <pre>
307      * public RNG extends AnotherRNG {
308      *     public RNG(long[] seed) {
309      *         super(seed = extendSeed(seed, SEED_SIZE));
310      *         // Use seed for additional state ...
311      *     }
312      * }
313      * </pre>
314      *
315      * <p>Note using the state filling procedure provided in {@link #fillState(long[], long[])}
316      * is not possible as it is an instance method. Calling a seed extension routine must use a
317      * static method.
318      *
319      * <p>This method functions as if the seed has been extended using a
320      * {@link org.apache.commons.rng.core.source64.SplitMix64 SplitMix64}
321      * generator seeded with {@code seed[0]}, or zero if the input seed length is zero.
322      * <pre>
323      * if (seed.length &lt; length) {
324      *     final long[] s = Arrays.copyOf(seed, length);
325      *     final SplitMix64 rng = new SplitMix64(s[0]);
326      *     for (int i = seed.length; i &lt; length; i++) {
327      *         s[i] = rng.nextLong();
328      *     }
329      *     return s;
330      * }</pre>
331      *
332      * @param seed Input seed
333      * @param length The minimum length
334      * @return the seed
335      * @since 1.5
336      */
337     protected static long[] extendSeed(long[] seed, int length) {
338         if (seed.length < length) {
339             final long[] s = Arrays.copyOf(seed, length);
340             // Fill the rest as if using a SplitMix64 RNG
341             long x = s[0];
342             for (int i = seed.length; i < length; i++) {
343                 s[i] = stafford13(x += GOLDEN_RATIO_64);
344             }
345             return s;
346         }
347         return seed;
348     }
349 
350     /**
351      * Extend the seed to the specified minimum length. If the seed is equal or greater than the
352      * minimum length, return the same seed unchanged. Otherwise:
353      * <ol>
354      *  <li>Create a new array of the specified length
355      *  <li>Copy all elements of the seed into the array
356      *  <li>Fill the remaining values. The additional values will have at most one occurrence
357      *   of zero. If the original seed is all zero, the first extended value will be non-zero.
358      *  </li>
359      * </ol>
360      *
361      * <p>This method can be used in constructors that must pass their seed to the super class
362      * to avoid a duplication of seed expansion to the minimum length required by the super class
363      * and the class:
364      * <pre>
365      * public RNG extends AnotherRNG {
366      *     public RNG(int[] seed) {
367      *         super(seed = extendSeed(seed, SEED_SIZE));
368      *         // Use seed for additional state ...
369      *     }
370      * }
371      * </pre>
372      *
373      * <p>Note using the state filling procedure provided in {@link #fillState(int[], int[])}
374      * is not possible as it is an instance method. Calling a seed extension routine must use a
375      * static method.
376      *
377      * <p>This method functions as if the seed has been extended using a
378      * {@link org.apache.commons.rng.core.source64.SplitMix64 SplitMix64}-style 32-bit
379      * generator seeded with {@code seed[0]}, or zero if the input seed length is zero. The
380      * generator uses the 32-bit mixing function from MurmurHash3.
381      *
382      * @param seed Input seed
383      * @param length The minimum length
384      * @return the seed
385      * @since 1.5
386      */
387     protected static int[] extendSeed(int[] seed, int length) {
388         if (seed.length < length) {
389             final int[] s = Arrays.copyOf(seed, length);
390             // Fill the rest as if using a SplitMix64-style RNG for 32-bit output
391             int x = s[0];
392             for (int i = seed.length; i < length; i++) {
393                 s[i] = murmur3(x += GOLDEN_RATIO_32);
394             }
395             return s;
396         }
397         return seed;
398     }
399 
400     /**
401      * Perform variant 13 of David Stafford's 64-bit mix function.
402      * This is the mix function used in the
403      * {@link org.apache.commons.rng.core.source64.SplitMix64 SplitMix64} RNG.
404      *
405      * <p>This is ranked first of the top 14 Stafford mixers.
406      *
407      * <p>This function can be used to mix the bits of a {@code long} value to
408      * obtain a better distribution and avoid collisions between similar values.
409      *
410      * @param x the input value
411      * @return the output value
412      * @see <a href="https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html">Better
413      *      Bit Mixing - Improving on MurmurHash3&#39;s 64-bit Finalizer.</a>
414      */
415     private static long stafford13(long x) {
416         x = (x ^ (x >>> 30)) * 0xbf58476d1ce4e5b9L;
417         x = (x ^ (x >>> 27)) * 0x94d049bb133111ebL;
418         return x ^ (x >>> 31);
419     }
420 
421     /**
422      * Perform the finalising 32-bit mix function of Austin Appleby's MurmurHash3.
423      *
424      * <p>This function can be used to mix the bits of a {@code int} value to
425      * obtain a better distribution and avoid collisions between similar values.
426      *
427      * @param x the input value
428      * @return the output value
429      * @see <a href="https://github.com/aappleby/smhasher">SMHasher</a>
430      */
431     private static int murmur3(int x) {
432         x = (x ^ (x >>> 16)) * 0x85ebca6b;
433         x = (x ^ (x >>> 13)) * 0xc2b2ae35;
434         return x ^ (x >>> 16);
435     }
436 }