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}