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.simple.internal;
18  
19  import java.util.Arrays;
20  import java.util.List;
21  import java.util.ArrayList;
22  import java.util.Map;
23  import java.util.concurrent.ConcurrentHashMap;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.InvocationTargetException;
26  
27  import org.apache.commons.rng.UniformRandomProvider;
28  import org.apache.commons.rng.RestorableUniformRandomProvider;
29  import org.apache.commons.rng.core.source32.JDKRandom;
30  import org.apache.commons.rng.core.source32.Well512a;
31  import org.apache.commons.rng.core.source32.Well1024a;
32  import org.apache.commons.rng.core.source32.Well19937a;
33  import org.apache.commons.rng.core.source32.Well19937c;
34  import org.apache.commons.rng.core.source32.Well44497a;
35  import org.apache.commons.rng.core.source32.Well44497b;
36  import org.apache.commons.rng.core.source32.ISAACRandom;
37  import org.apache.commons.rng.core.source32.MersenneTwister;
38  import org.apache.commons.rng.core.source32.MultiplyWithCarry256;
39  import org.apache.commons.rng.core.source32.KISSRandom;
40  import org.apache.commons.rng.core.source64.SplitMix64;
41  import org.apache.commons.rng.core.source64.XorShift1024Star;
42  import org.apache.commons.rng.core.source64.TwoCmres;
43  import org.apache.commons.rng.core.source64.MersenneTwister64;
44  
45  /**
46   * RNG builder.
47   * <p>
48   * It uses reflection to find the factory method of the RNG implementation,
49   * and performs seed type conversions.
50   * </p>
51   */
52  public final class ProviderBuilder {
53      /** Error message. */
54      private static final String INTERNAL_ERROR_MSG = "Internal error: Please file a bug report";
55      /** Length of the seed array (for random seed). */
56      private static final int RANDOM_SEED_ARRAY_SIZE = 128;
57      /** Seed converter. */
58      private static final Long2Int LONG_TO_INT = new Long2Int();
59      /** Seed converter. */
60      private static final Int2Long INT_TO_LONG = new Int2Long();
61      /** Seed converter. */
62      private static final Long2IntArray LONG_TO_INT_ARRAY = new Long2IntArray(RANDOM_SEED_ARRAY_SIZE);
63      /** Seed converter. */
64      private static final Long2LongArray LONG_TO_LONG_ARRAY = new Long2LongArray(RANDOM_SEED_ARRAY_SIZE);
65      /** Seed converter. */
66      private static final LongArray2Long LONG_ARRAY_TO_LONG = new LongArray2Long();
67      /** Seed converter. */
68      private static final IntArray2Int INT_ARRAY_TO_INT = new IntArray2Int();
69      /** Seed converter. */
70      private static final LongArray2IntArray LONG_ARRAY_TO_INT_ARRAY = new LongArray2IntArray();
71      /** Seed converter. */
72      private static final IntArray2LongArray INT_ARRAY_TO_LONG_ARRAY = new IntArray2LongArray();
73      /** Seed converter. */
74      private static final ByteArray2IntArray BYTE_ARRAY_TO_INT_ARRAY = new ByteArray2IntArray();
75      /** Seed converter. */
76      private static final ByteArray2LongArray BYTE_ARRAY_TO_LONG_ARRAY = new ByteArray2LongArray();
77      /** Map to convert "Integer" seeds. */
78      private static final Map<Class<?>, SeedConverter<Integer,?>> CONV_INT =
79          new ConcurrentHashMap<Class<?>, SeedConverter<Integer,?>>();
80      /** Map to convert "int[]" seeds. */
81      private static final Map<Class<?>, SeedConverter<int[],?>> CONV_INT_ARRAY =
82          new ConcurrentHashMap<Class<?>, SeedConverter<int[],?>>();
83      /** Map to convert "Long" seeds. */
84      private static final Map<Class<?>, SeedConverter<Long,?>> CONV_LONG =
85          new ConcurrentHashMap<Class<?>, SeedConverter<Long,?>>();
86      /** Map to convert "long[]" seeds. */
87      private static final Map<Class<?>, SeedConverter<long[],?>> CONV_LONG_ARRAY =
88          new ConcurrentHashMap<Class<?>, SeedConverter<long[],?>>();
89      /** Map to convert "byte[]" seeds. */
90      private static final Map<Class<?>, SeedConverter<byte[],?>> CONV_BYTE_ARRAY =
91          new ConcurrentHashMap<Class<?>, SeedConverter<byte[],?>>();
92  
93      static {
94          // Input seed type is "Long".
95          // Key is the implementation's "native" seed type.
96          CONV_LONG.put(Integer.class, LONG_TO_INT);
97          CONV_LONG.put(Long.class, new NoOpConverter<Long>());
98          CONV_LONG.put(int[].class, LONG_TO_INT_ARRAY);
99          CONV_LONG.put(long[].class, LONG_TO_LONG_ARRAY);
100 
101         // Input seed type is "Integer".
102         // Key is the implementation's "native" seed type.
103         CONV_INT.put(Integer.class, new NoOpConverter<Integer>());
104         CONV_INT.put(Long.class, INT_TO_LONG);
105         CONV_INT.put(int[].class, new SeedConverterComposer<Integer,Long,int[]>(INT_TO_LONG, LONG_TO_INT_ARRAY));
106         CONV_INT.put(long[].class, new SeedConverterComposer<Integer,Long,long[]>(INT_TO_LONG, LONG_TO_LONG_ARRAY));
107 
108         // Input seed type is "int[]".
109         // Key is the implementation's "native" seed type.
110         CONV_INT_ARRAY.put(Integer.class, INT_ARRAY_TO_INT);
111         CONV_INT_ARRAY.put(Long.class, new SeedConverterComposer<int[],Integer,Long>(INT_ARRAY_TO_INT, INT_TO_LONG));
112         CONV_INT_ARRAY.put(int[].class, new NoOpConverter<int[]>());
113         CONV_INT_ARRAY.put(long[].class, INT_ARRAY_TO_LONG_ARRAY);
114 
115         // Input seed type is "long[]".
116         // Key is the implementation's "native" seed type.
117         CONV_LONG_ARRAY.put(Integer.class, new SeedConverterComposer<long[],Long,Integer>(LONG_ARRAY_TO_LONG, LONG_TO_INT));
118         CONV_LONG_ARRAY.put(Long.class, LONG_ARRAY_TO_LONG);
119         CONV_LONG_ARRAY.put(int[].class, LONG_ARRAY_TO_INT_ARRAY);
120         CONV_LONG_ARRAY.put(long[].class, new NoOpConverter<long[]>());
121 
122         // Input seed type is "byte[]".
123         // Key is the implementation's "native" seed type.
124         CONV_BYTE_ARRAY.put(Integer.class, new SeedConverterComposer<byte[],int[],Integer>(BYTE_ARRAY_TO_INT_ARRAY, INT_ARRAY_TO_INT));
125         CONV_BYTE_ARRAY.put(Long.class, new SeedConverterComposer<byte[],long[],Long>(BYTE_ARRAY_TO_LONG_ARRAY, LONG_ARRAY_TO_LONG));
126         CONV_BYTE_ARRAY.put(int[].class, BYTE_ARRAY_TO_INT_ARRAY);
127         CONV_BYTE_ARRAY.put(long[].class, BYTE_ARRAY_TO_LONG_ARRAY);
128     }
129 
130     /**
131      * Class only contains static method.
132      */
133     private ProviderBuilder() {}
134 
135     /**
136      * Creates a RNG instance.
137      *
138      * @param source RNG specification.
139      * @param seed Seed value.  It can be {@code null} (in which case a
140      * random value will be used).
141      * @param args Additional arguments to the implementation's constructor.
142      * @return a new RNG instance.
143      * @throws UnsupportedOperationException if the seed type is invalid.
144      */
145     public static RestorableUniformRandomProvider create(RandomSourceInternal source,
146                                                          Object seed,
147                                                          Object[] args) {
148         // Convert seed to native type.
149         final Object nativeSeed = createSeed(source, seed);
150 
151         // Build a single array with all the arguments to be passed
152         // (in the right order) to the constructor.
153         final List<Object> all = new ArrayList<Object>();
154         all.add(nativeSeed);
155         if (args != null) {
156             all.addAll(Arrays.asList(args));
157         }
158 
159         // Instantiate.
160         return create(createConstructor(source), all.toArray());
161     }
162 
163     /**
164      * Creates a native seed from any of the supported seed types.
165      *
166      * @param source Source.
167      * @param seed Input seed.
168      * @return the native seed.
169      * @throw UnsupportedOperationException if the {@code seed} type is invalid.
170      */
171     private static Object createSeed(RandomSourceInternal source,
172                                      Object seed) {
173         Object nativeSeed = null;
174 
175         if (seed == null) {
176             // Create a random seed of the appropriate native type.
177 
178             if (source.getSeed().equals(Integer.class)) {
179                 nativeSeed = SeedFactory.createInt();
180             } else if (source.getSeed().equals(Long.class)) {
181                 nativeSeed = SeedFactory.createLong();
182             } else if (source.getSeed().equals(int[].class)) {
183                 nativeSeed = SeedFactory.createIntArray(RANDOM_SEED_ARRAY_SIZE);
184             } else if (source.getSeed().equals(long[].class)) {
185                 nativeSeed = SeedFactory.createLongArray(RANDOM_SEED_ARRAY_SIZE);
186             } else {
187                 // Source's native type is not handled.
188                 throw new IllegalStateException(INTERNAL_ERROR_MSG);
189             }
190         } else {
191             // Convert to native type.
192 
193             if (seed instanceof Integer) {
194                 nativeSeed = CONV_INT.get(source.getSeed()).convert((Integer) seed);
195             } else if (seed instanceof Long) {
196                 nativeSeed = CONV_LONG.get(source.getSeed()).convert((Long) seed);
197             } else if (seed instanceof int[]) {
198                 nativeSeed = CONV_INT_ARRAY.get(source.getSeed()).convert((int[]) seed);
199             } else if (seed instanceof long[]) {
200                 nativeSeed = CONV_LONG_ARRAY.get(source.getSeed()).convert((long[]) seed);
201             } else if (seed instanceof byte[]) {
202                 nativeSeed = CONV_BYTE_ARRAY.get(source.getSeed()).convert((byte[]) seed);
203             }
204 
205             if (nativeSeed == null) {
206                 // Since the input seed was not null, getting here means that
207                 // no suitable converter is present in the maps.
208                 throw new UnsupportedOperationException("Unrecognized seed type");
209             }
210 
211             if (!source.isNativeSeed(nativeSeed)) {
212                 // Conversion setup is wrong.
213                 throw new IllegalStateException(INTERNAL_ERROR_MSG);
214             }
215         }
216 
217         return nativeSeed;
218     }
219 
220     /**
221      * Creates a constructor.
222      *
223      * @param source RNG specification.
224      * @return a RNG constructor.
225      */
226     private static Constructor<?> createConstructor(RandomSourceInternal source) {
227         try {
228             return source.getRng().getConstructor(source.getArgs());
229         } catch (NoSuchMethodException e) {
230             // Info in "RandomSourceInternal" is inconsistent with the
231             // constructor of the implementation.
232             throw new IllegalStateException(INTERNAL_ERROR_MSG, e);
233         }
234     }
235 
236     /**
237      * Creates a RNG.
238      *
239      * @param rng RNG specification.
240      * @param args Arguments to the implementation's constructor.
241      * @return a new RNG instance.
242      */
243     private static RestorableUniformRandomProvider create(Constructor<?> rng,
244                                                           Object[] args) {
245         try {
246             return (RestorableUniformRandomProvider) rng.newInstance(args);
247         } catch (InvocationTargetException e) {
248             throw new IllegalStateException(INTERNAL_ERROR_MSG, e);
249         } catch (InstantiationException e) {
250             throw new IllegalStateException(INTERNAL_ERROR_MSG, e);
251         } catch (IllegalAccessException e) {
252             throw new IllegalStateException(INTERNAL_ERROR_MSG, e);
253         }
254     }
255 
256     /**
257      * Identifiers of the generators.
258      */
259     public enum RandomSourceInternal {
260         /** Source of randomness is {@link JDKRandom}. */
261         JDK(JDKRandom.class,
262             Long.class),
263         /** Source of randomness is {@link Well512a}. */
264         WELL_512_A(Well512a.class,
265                    int[].class),
266         /** Source of randomness is {@link Well1024a}. */
267         WELL_1024_A(Well1024a.class,
268                     int[].class),
269         /** Source of randomness is {@link Well19937a}. */
270         WELL_19937_A(Well19937a.class,
271                      int[].class),
272         /** Source of randomness is {@link Well19937c}. */
273         WELL_19937_C(Well19937c.class,
274                      int[].class),
275         /** Source of randomness is {@link Well44497a}. */
276         WELL_44497_A(Well44497a.class,
277                      int[].class),
278         /** Source of randomness is {@link Well44497b}. */
279         WELL_44497_B(Well44497b.class,
280                      int[].class),
281         /** Source of randomness is {@link MersenneTwister}. */
282         MT(MersenneTwister.class,
283            int[].class),
284         /** Source of randomness is {@link ISAACRandom}. */
285         ISAAC(ISAACRandom.class,
286               int[].class),
287         /** Source of randomness is {@link SplitMix64}. */
288         SPLIT_MIX_64(SplitMix64.class,
289                      Long.class),
290         /** Source of randomness is {@link XorShift1024Star}. */
291         XOR_SHIFT_1024_S(XorShift1024Star.class,
292                          long[].class),
293         /** Source of randomness is {@link TwoCmres}. */
294         TWO_CMRES(TwoCmres.class,
295                   Integer.class),
296         /**
297          * Source of randomness is {@link TwoCmres} with explicit selection
298          * of the two subcycle generators.
299          */
300         TWO_CMRES_SELECT(TwoCmres.class,
301                          Integer.class,
302                          Integer.TYPE,
303                          Integer.TYPE),
304         /** Source of randomness is {@link MersenneTwister64}. */
305         MT_64(MersenneTwister64.class,
306               long[].class),
307         /** Source of randomness is {@link MultiplyWithCarry256}. */
308         MWC_256(MultiplyWithCarry256.class,
309                 int[].class),
310         /** Source of randomness is {@link KISSRandom}. */
311         KISS(KISSRandom.class,
312              int[].class);
313 
314         /** Source type. */
315         private final Class<? extends UniformRandomProvider> rng;
316         /** Data needed to build the generator. */
317         private final Class<?>[] args;
318 
319         /**
320          * @param rng Source type.
321          * @param args Data needed to create a generator instance.
322          * The first element must be the native seed type.
323          */
324         RandomSourceInternal(Class<? extends UniformRandomProvider> rng,
325                              Class<?> ... args) {
326             this.rng = rng;
327             this.args = Arrays.copyOf(args, args.length);
328         }
329 
330         /**
331          * @return the source type.
332          */
333         public Class<?> getRng() {
334             return rng;
335         }
336 
337         /**
338          * @return the seed type.
339          */
340         Class<?> getSeed() {
341             return args[0];
342         }
343 
344         /**
345          * @return the data needed to build the generator.
346          */
347         Class<?>[] getArgs() {
348             return args;
349         }
350 
351         /**
352          * Checks whether the type of given {@code seed} is the native type
353          * of the implementation.
354          *
355          * @param <SEED> Seed type.
356          *
357          * @param seed Seed value.
358          * @return {@code true} if the seed can be passed to the builder
359          * for this RNG type.
360          */
361         public <SEED> boolean isNativeSeed(SEED seed) {
362             return seed == null ?
363                 false :
364                 getSeed().equals(seed.getClass());
365         }
366     }
367 }