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.source32;
19  
20  import org.apache.commons.rng.core.util.NumberFactory;
21  
22  import java.util.Arrays;
23  
24  /**
25   * Middle Square Weyl Sequence Random Number Generator.
26   *
27   * <p>A fast all-purpose 32-bit generator. Memory footprint is 192 bits and the period is at least
28   * {@code 2^64}.</p>
29   *
30   * <p>Implementation is based on the paper
31   * <a href="https://arxiv.org/abs/1704.00358v3">Middle Square Weyl Sequence RNG</a>.</p>
32   *
33   * @see <a href="https://en.wikipedia.org/wiki/Middle-square_method">Middle Square Method</a>
34   * @since 1.3
35   */
36  public class MiddleSquareWeylSequence extends IntProvider {
37      /** Size of the seed array. */
38      private static final int SEED_SIZE = 3;
39      /**
40       * The default seed.
41       * This has a high quality Weyl increment (containing many bit state transitions).
42       */
43      private static final long[] DEFAULT_SEED =
44          {0x012de1babb3c4104L, 0xc8161b4202294965L, 0xb5ad4eceda1ce2a9L};
45  
46      /** State of the generator. */
47      private long x;
48      /** State of the Weyl sequence. */
49      private long w;
50      /**
51       * Increment for the Weyl sequence. This must be odd to ensure a full period.
52       *
53       * <p>This is not final to support the restore functionality.</p>
54       */
55      private long s;
56  
57      /**
58       * Creates a new instance.
59       *
60       * <p>Note: The generator output quality is highly dependent on the initial seed.
61       * If the generator is seeded poorly (for example with all zeros) it is possible the
62       * generator may output zero for many cycles before the internal state recovers randomness.
63       * The seed elements are used to set:</p>
64       *
65       * <ol>
66       *   <li>The state of the generator
67       *   <li>The state of the Weyl sequence
68       *   <li>The increment of the Weyl sequence
69       * </ol>
70       *
71       * <p>The third element is set to odd to ensure a period of at least 2<sup>64</sup>. If the
72       * increment is of low complexity then the Weyl sequence does not contribute high quality
73       * randomness. It is recommended to use a permutation of 8 hex characters for the upper
74       * and lower 32-bits of the increment.</p>
75       *
76       * <p>The state of the generator is squared during each cycle. There is a possibility that
77       * different seeds can produce the same output, for example 0 and 2<sup>32</sup> produce
78       * the same square. This can be avoided by using the high complexity Weyl increment for the
79       * state seed element.</p>
80       *
81       * @param seed Initial seed.
82       * If the length is larger than 3, only the first 3 elements will
83       * be used; if smaller, the remaining elements will be automatically set.
84       */
85      public MiddleSquareWeylSequence(long[] seed) {
86          if (seed.length < SEED_SIZE) {
87              // Complete the seed with a default to avoid
88              // low complexity Weyl increments.
89              final long[] tmp = Arrays.copyOf(seed, SEED_SIZE);
90              System.arraycopy(DEFAULT_SEED, seed.length, tmp, seed.length, SEED_SIZE - seed.length);
91              setSeedInternal(tmp);
92          } else {
93              setSeedInternal(seed);
94          }
95      }
96  
97      /**
98       * Seeds the RNG.
99       *
100      * @param seed Seed.
101      */
102     private void setSeedInternal(long[] seed) {
103         x = seed[0];
104         w = seed[1];
105         // Ensure the increment is odd to provide a maximal period Weyl sequence.
106         this.s = seed[2] | 1L;
107     }
108 
109     /** {@inheritDoc} */
110     @Override
111     protected byte[] getStateInternal() {
112         return composeStateInternal(NumberFactory.makeByteArray(new long[] {x, w, s}),
113                                     super.getStateInternal());
114     }
115 
116     /** {@inheritDoc} */
117     @Override
118     protected void setStateInternal(byte[] state) {
119         final byte[][] c = splitStateInternal(state, SEED_SIZE * 8);
120         setSeedInternal(NumberFactory.makeLongArray(c[0]));
121         super.setStateInternal(c[1]);
122     }
123 
124     /** {@inheritDoc} */
125     @Override
126     public int next() {
127         x *= x;
128         x += w += s;
129         x = (x >>> 32) | (x << 32);
130         return (int) x;
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public long nextLong() {
136         // Avoid round trip from long to int to long by performing two iterations inline
137         x *= x;
138         x += w += s;
139         final long i1 = x & 0xffffffff00000000L;
140         x = (x >>> 32) | (x << 32);
141         x *= x;
142         x += w += s;
143         final long i2 = x >>> 32;
144         x = i2 | x << 32;
145         return i1 | i2;
146     }
147 }