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  package org.apache.commons.rng.core.source64;
18  
19  import org.apache.commons.rng.core.util.NumberFactory;
20  
21  /**
22   * A Permuted Congruential Generator (PCG) that is composed of a 64-bit Linear Congruential
23   * Generator (LCG) combined with the RXS-M-XS (random xorshift; multiply; xorshift) output
24   * transformation to create 64-bit output.
25   *
26   * <p>State size is 128 bits and the period is 2<sup>64</sup>.</p>
27   *
28   * @see <a href="http://www.pcg-random.org/">
29   *  PCG, A Family of Better Random Number Generators</a>
30   * @since 1.3
31   */
32  public class PcgRxsMXs64 extends LongProvider {
33      /** Size of the seed array. */
34      private static final int SEED_SIZE = 2;
35  
36      /** The state of the LCG. */
37      private long state;
38  
39      /** The increment of the LCG. */
40      private long increment;
41  
42      /**
43       * Creates a new instance.
44       *
45       * @param seed Initial seed.
46       * If the length is larger than 2, only the first 2 elements will
47       * be used; if smaller, the remaining elements will be automatically set.
48       */
49      public PcgRxsMXs64(long[] seed) {
50          if (seed.length < SEED_SIZE) {
51              final long[] tmp = new long[SEED_SIZE];
52              fillState(tmp, seed);
53              setSeedInternal(tmp);
54          } else {
55              setSeedInternal(seed);
56          }
57      }
58  
59      /**
60       * Seeds the RNG.
61       *
62       * @param seed Seed.
63       */
64      private void setSeedInternal(long[] seed) {
65          // Ensure the increment is odd to provide a maximal period LCG.
66          this.increment = (seed[1] << 1) | 1;
67          this.state = bump(seed[0] + this.increment);
68      }
69  
70      /**
71       * Provides the next state of the LCG.
72       *
73       * @param input Current state.
74       * @return next state
75       */
76      private long bump(long input) {
77          return input * 6364136223846793005L + increment;
78      }
79  
80      /** {@inheritDoc} */
81      @Override
82      public long next() {
83          final long x = state;
84          state = bump(state);
85          final long word = ((x >>> ((x >>> 59) + 5)) ^ x) * -5840758589994634535L;
86          return (word >>> 43) ^ word;
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      protected byte[] getStateInternal() {
92          // The increment is divided by 2 before saving.
93          // This transform is used in the reference PCG code; it prevents restoring from
94          // a byte state a non-odd increment that results in a sub-maximal period generator.
95          return composeStateInternal(NumberFactory.makeByteArray(
96                  new long[] {state, increment >>> 1}),
97                  super.getStateInternal());
98      }
99  
100     /** {@inheritDoc} */
101     @Override
102     protected void setStateInternal(byte[] s) {
103         final byte[][] c = splitStateInternal(s, SEED_SIZE * 8);
104         final long[] tempseed = NumberFactory.makeLongArray(c[0]);
105         state = tempseed[0];
106         // Reverse the transform performed during getState to make the increment odd again.
107         increment = tempseed[1] << 1 | 1;
108         super.setStateInternal(c[1]);
109     }
110 }