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