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.List;
21  import java.util.ArrayList;
22  import org.apache.commons.rng.core.util.NumberFactory;
23  
24  /**
25   * Random number generator designed by Mark D. Overton.
26   *
27   * <p>It is one of the many generators described by the author in the following article series:</p>
28   *  <ul>
29   *   <li><a href="http://www.drdobbs.com/tools/fast-high-quality-parallel-random-number/229625477">Part one</a></li>
30   *   <li><a href="http://www.drdobbs.com/tools/fast-high-quality-parallel-random-number/231000484">Part two</a></li>
31   *  </ul>
32   *
33   * @since 1.0
34   */
35  public class TwoCmres extends LongProvider {
36      /** Error message. */
37      private static final String INTERNAL_ERROR_MSG = "Internal error: Please file a bug report";
38      /** A small positive integer. */
39      private static final byte SEED_GUARD = 9;
40      /** Factory of instances of this class. Singleton. */
41      private static final Cmres.Factory FACTORY = new Cmres.Factory();
42      /** First subcycle generator. */
43      private final Cmres x;
44      /** Second subcycle generator. */
45      private final Cmres y;
46      /** State of first subcycle generator. */
47      private long xx;
48      /** State of second subcycle generator. */
49      private long yy;
50  
51      /**
52       * Creates a new instance.
53       *
54       * @param seed Initial seed.
55       * @param x First subcycle generator.
56       * @param y Second subcycle generator.
57       * @throws IllegalArgumentException if {@code x == y}.
58       */
59      private TwoCmres(int seed,
60                       Cmres x,
61                       Cmres y) {
62          if (x == y) {
63              throw new IllegalArgumentException("Subcycle generators must be different");
64          }
65          this.x = x;
66          this.y = y;
67          setSeedInternal(seed);
68      }
69  
70      /**
71       * Creates a new instance.
72       *
73       * @param seed Seed.
74       */
75      public TwoCmres(Integer seed) {
76          this(seed, 0, 1);
77      }
78  
79      /**
80       * Creates a new instance.
81       *
82       * @param seed Seed.
83       * @param i Table entry for first subcycle generator.
84       * @param j Table entry for second subcycle generator.
85       * @throws IllegalArgumentException if {@code i == j}.
86       * @throws IndexOutOfBoundsException if {@code i < 0} or
87       * {@code i >= numberOfSubcycleGenerators()}.
88       * @throws IndexOutOfBoundsException if {@code j < 0} or
89       * {@code j >= numberOfSubcycleGenerators()}.
90       */
91      public TwoCmres(Integer seed,
92                      int i,
93                      int j) {
94          this(seed, FACTORY.get(i), FACTORY.get(j));
95      }
96  
97      /** {@inheritDoc} */
98      @Override
99      public long next() {
100         xx = x.transform(xx);
101         yy = y.transform(yy);
102 
103         return xx + yy;
104     }
105 
106     /** {@inheritDoc} */
107     @Override
108     public String toString() {
109         return super.toString() + " (" + x + " + " + y + ")";
110     }
111 
112     /**
113      * @return the number of subcycle generators.
114      */
115     public static int numberOfSubcycleGenerators() {
116         return FACTORY.numberOfSubcycleGenerators();
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     protected byte[] getStateInternal() {
122         return NumberFactory.makeByteArray(new long[] { xx, yy });
123     }
124 
125     /** {@inheritDoc} */
126     @Override
127     protected void setStateInternal(byte[] s) {
128         checkStateSize(s, 16);
129 
130         final long[] state = NumberFactory.makeLongArray(s);
131         xx = state[0];
132         yy = state[1];
133     }
134 
135     /**
136      * @param seed Seed.
137      */
138     private void setSeedInternal(int seed) {
139         // The seeding procedure consists in going away from some
140         // point known to be in the cycle.
141         // The total number of calls to the "transform" method will
142         // not exceed about 130,000 (which is negligible as seeding
143         // will not occur more than once in normal usage).
144 
145         // Make two positive 16-bits integers.
146         final long s = NumberFactory.makeLong(0, seed); // s >= 0
147         final int xMax = (int) ((s & 0xffff) + SEED_GUARD);
148         final int yMax = (int) ((s >> 16) + SEED_GUARD);
149 
150         if (xMax < 0 ||
151             yMax < 0) {
152             throw new IllegalStateException(INTERNAL_ERROR_MSG);
153         }
154 
155         xx = x.getStart();
156         for (int i = xMax; i > 0; i--) {
157             xx = x.transform(xx);
158         }
159 
160         yy = y.getStart();
161         for (int i = yMax; i > 0; i--) {
162             yy = y.transform(yy);
163         }
164     }
165 
166     /**
167      * Subcycle generator.
168      * Class is immutable.
169      */
170     static class Cmres {
171         /** Separator. */
172         private static final String SEP = ", ";
173         /** Hexadecimal format. */
174         private static final String HEX_FORMAT = "0x%016xL";
175         /** Cycle start. */
176         private final int start;
177         /** Multiplier. */
178         private final long multiply;
179         /** Rotation. */
180         private final int rotate;
181 
182         /**
183          * @param multiply Multiplier.
184          * @param rotate Positive number. Must be in {@code [0, 64]}.
185          * @param start Cycle start.
186          */
187         Cmres(long multiply,
188               int rotate,
189               int start) {
190             this.multiply = multiply;
191             this.rotate = rotate;
192             this.start = start;
193         }
194 
195         /** {@inheritDoc} */
196         @Override
197         public String toString() {
198             final String m = String.format((java.util.Locale) null, HEX_FORMAT, multiply);
199             return "Cmres: [" + m + SEP + rotate + SEP + start + "]";
200         }
201 
202         /**
203          * @return the multiplier.
204          */
205         public long getMultiply() {
206             return multiply;
207         }
208 
209         /**
210          * @return the cycle start.
211          */
212         public int getStart() {
213             return start;
214         }
215 
216         /**
217          * @param state Current state.
218          * @return the new state.
219          */
220         long transform(long state) {
221             long s = state;
222             s *= multiply;
223             s = rotl(s);
224             s -= state;
225             return s;
226         }
227 
228         /**
229          * @param state State.
230          * @return the rotated state.
231          */
232         private long rotl(long state) {
233             return (state << rotate) | (state >>> (64 - rotate));
234         }
235 
236         /** Factory. */
237         static class Factory {
238             /** List of good "Cmres" subcycle generators. */
239             private static final List<Cmres> TABLE = new ArrayList<Cmres>();
240 
241             /**
242              * Populates the table.
243              * It lists parameters known to be good (provided in
244              * the article referred to above).
245              * To maintain compatibility, new entries must be added
246              * only at the end of the table.
247              */
248             static {
249                 add(0xedce446814d3b3d9L, 33, 0x13b572e7);
250                 add(0xc5b3cf786c806df7L, 33, 0x13c8e18a);
251                 add(0xdd91bbb8ab9e0e65L, 31, 0x06dd03a6);
252                 add(0x7b69342c0790221dL, 31, 0x1646bb8b);
253                 add(0x0c72c0d18614c32bL, 33, 0x06014a3d);
254                 add(0xd8d98c13bebe26c9L, 33, 0x014e8475);
255                 add(0xcb039dc328bbc40fL, 31, 0x008684bd);
256                 add(0x858c5ef3c021ed2fL, 32, 0x0dc8d622);
257                 add(0x4c8be96bfc23b127L, 33, 0x0b6b20cc);
258                 add(0x11eab77f808cf641L, 32, 0x06534421);
259                 add(0xbc9bd78810fd28fdL, 31, 0x1d9ba40d);
260                 add(0x0f1505c780688cb5L, 33, 0x0b7b7b67);
261                 add(0xadc174babc2053afL, 31, 0x267f4197);
262                 add(0x900b6b82b31686d9L, 31, 0x023c6985);
263                 // Add new entries here.
264             }
265 
266             /**
267              * @return the number of subcycle generators.
268              */
269             int numberOfSubcycleGenerators() {
270                 return TABLE.size();
271             }
272 
273             /**
274              * @param index Index into the list of available generators.
275              * @return the subcycle generator entry at index {@code index}.
276              */
277             Cmres get(int index) {
278                 if (index < 0 ||
279                     index >= TABLE.size()) {
280                     throw new IndexOutOfBoundsException("Out of interval [0, " +
281                                                         (TABLE.size() - 1) + "]");
282                 }
283 
284                 return TABLE.get(index);
285             }
286 
287             /**
288              * Adds an entry to the {@link Factory#TABLE}.
289              *
290              * @param multiply Multiplier.
291              * @param rotate Rotate.
292              * @param start Cycle start.
293              */
294             private static void add(long multiply,
295                                     int rotate,
296                                     int start) {
297                 // Sanity check: if there are duplicates, the class initialization
298                 // will fail (and the JVM will report "NoClassDefFoundError").
299                 for (Cmres sg : TABLE) {
300                     if (multiply == sg.getMultiply()) {
301                         throw new IllegalStateException(INTERNAL_ERROR_MSG);
302                     }
303                 }
304 
305                 TABLE.add(new Cmres(multiply, rotate, start));
306             }
307         }
308     }
309 }