001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.rng.simple.internal;
018
019import java.util.Arrays;
020import java.util.List;
021import java.util.ArrayList;
022import java.util.Map;
023import java.util.concurrent.ConcurrentHashMap;
024import java.lang.reflect.Constructor;
025import java.lang.reflect.InvocationTargetException;
026
027import org.apache.commons.rng.UniformRandomProvider;
028import org.apache.commons.rng.RestorableUniformRandomProvider;
029import org.apache.commons.rng.core.source32.JDKRandom;
030import org.apache.commons.rng.core.source32.Well512a;
031import org.apache.commons.rng.core.source32.Well1024a;
032import org.apache.commons.rng.core.source32.Well19937a;
033import org.apache.commons.rng.core.source32.Well19937c;
034import org.apache.commons.rng.core.source32.Well44497a;
035import org.apache.commons.rng.core.source32.Well44497b;
036import org.apache.commons.rng.core.source32.ISAACRandom;
037import org.apache.commons.rng.core.source32.MersenneTwister;
038import org.apache.commons.rng.core.source32.MultiplyWithCarry256;
039import org.apache.commons.rng.core.source32.KISSRandom;
040import org.apache.commons.rng.core.source64.SplitMix64;
041import org.apache.commons.rng.core.source64.XorShift1024Star;
042import org.apache.commons.rng.core.source64.TwoCmres;
043import org.apache.commons.rng.core.source64.MersenneTwister64;
044
045/**
046 * RNG builder.
047 * <p>
048 * It uses reflection to find the factory method of the RNG implementation,
049 * and performs seed type conversions.
050 * </p>
051 */
052public final class ProviderBuilder {
053    /** Error message. */
054    private static final String INTERNAL_ERROR_MSG = "Internal error: Please file a bug report";
055    /** Length of the seed array (for random seed). */
056    private static final int RANDOM_SEED_ARRAY_SIZE = 128;
057    /** Seed converter. */
058    private static final Long2Int LONG_TO_INT = new Long2Int();
059    /** Seed converter. */
060    private static final Int2Long INT_TO_LONG = new Int2Long();
061    /** Seed converter. */
062    private static final Long2IntArray LONG_TO_INT_ARRAY = new Long2IntArray(RANDOM_SEED_ARRAY_SIZE);
063    /** Seed converter. */
064    private static final Long2LongArray LONG_TO_LONG_ARRAY = new Long2LongArray(RANDOM_SEED_ARRAY_SIZE);
065    /** Seed converter. */
066    private static final LongArray2Long LONG_ARRAY_TO_LONG = new LongArray2Long();
067    /** Seed converter. */
068    private static final IntArray2Int INT_ARRAY_TO_INT = new IntArray2Int();
069    /** Seed converter. */
070    private static final LongArray2IntArray LONG_ARRAY_TO_INT_ARRAY = new LongArray2IntArray();
071    /** Seed converter. */
072    private static final IntArray2LongArray INT_ARRAY_TO_LONG_ARRAY = new IntArray2LongArray();
073    /** Seed converter. */
074    private static final ByteArray2IntArray BYTE_ARRAY_TO_INT_ARRAY = new ByteArray2IntArray();
075    /** Seed converter. */
076    private static final ByteArray2LongArray BYTE_ARRAY_TO_LONG_ARRAY = new ByteArray2LongArray();
077    /** Map to convert "Integer" seeds. */
078    private static final Map<Class<?>, SeedConverter<Integer,?>> CONV_INT =
079        new ConcurrentHashMap<Class<?>, SeedConverter<Integer,?>>();
080    /** Map to convert "int[]" seeds. */
081    private static final Map<Class<?>, SeedConverter<int[],?>> CONV_INT_ARRAY =
082        new ConcurrentHashMap<Class<?>, SeedConverter<int[],?>>();
083    /** Map to convert "Long" seeds. */
084    private static final Map<Class<?>, SeedConverter<Long,?>> CONV_LONG =
085        new ConcurrentHashMap<Class<?>, SeedConverter<Long,?>>();
086    /** Map to convert "long[]" seeds. */
087    private static final Map<Class<?>, SeedConverter<long[],?>> CONV_LONG_ARRAY =
088        new ConcurrentHashMap<Class<?>, SeedConverter<long[],?>>();
089    /** Map to convert "byte[]" seeds. */
090    private static final Map<Class<?>, SeedConverter<byte[],?>> CONV_BYTE_ARRAY =
091        new ConcurrentHashMap<Class<?>, SeedConverter<byte[],?>>();
092
093    static {
094        // Input seed type is "Long".
095        // Key is the implementation's "native" seed type.
096        CONV_LONG.put(Integer.class, LONG_TO_INT);
097        CONV_LONG.put(Long.class, new NoOpConverter<Long>());
098        CONV_LONG.put(int[].class, LONG_TO_INT_ARRAY);
099        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}