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 org.apache.commons.rng.core.source64.SplitMix64;
20  import org.apache.commons.rng.simple.internal.ProviderBuilder.RandomSourceInternal;
21  import org.junit.Assert;
22  import org.junit.Test;
23  import org.junit.runner.RunWith;
24  import org.junit.runners.Parameterized;
25  import org.junit.runners.Parameterized.Parameters;
26  
27  import java.util.EnumMap;
28  
29  /**
30   * Tests for the {@link ProviderBuilder.RandomSourceInternal} seed conversions. This test
31   * ensures that all random sources can create a seed or convert any supported seed to the
32   * correct type for the constructor.
33   */
34  @RunWith(value = Parameterized.class)
35  public class RandomSourceInternalParametricTest {
36      /** The supported seeds for conversion to a native seed type. */
37      private static final Object[] SUPPORTED_SEEDS = {
38          Integer.valueOf(1),
39          Long.valueOf(2),
40          new int[] {3, 4, 5},
41          new long[] {6, 7, 8},
42          new byte[] {9, 10, 11},
43      };
44      /** Example unsupported seeds for conversion to a native seed type. */
45      private static final Object[] UNSUPPORTED_SEEDS = {
46          null,
47          Double.valueOf(Math.PI),
48      };
49      /** The expected byte size of the seed for each RandomSource. */
50      private static final EnumMap<RandomSourceInternal, Integer> EXPECTED_SEED_BYTES =
51              new EnumMap<RandomSourceInternal, Integer>(RandomSourceInternal.class);
52  
53      static {
54          final int intBytes = 4;
55          final int longBytes = 8;
56          EXPECTED_SEED_BYTES.put(RandomSourceInternal.JDK, longBytes * 1);
57          EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_512_A, intBytes * 16);
58          EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_1024_A, intBytes * 32);
59          EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_19937_A, intBytes * 624);
60          EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_19937_C, intBytes * 624);
61          EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_44497_A, intBytes * 1391);
62          EXPECTED_SEED_BYTES.put(RandomSourceInternal.WELL_44497_B, intBytes * 1391);
63          EXPECTED_SEED_BYTES.put(RandomSourceInternal.MT, intBytes * 624);
64          EXPECTED_SEED_BYTES.put(RandomSourceInternal.ISAAC, intBytes * 256);
65          EXPECTED_SEED_BYTES.put(RandomSourceInternal.SPLIT_MIX_64, longBytes * 1);
66          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XOR_SHIFT_1024_S, longBytes * 16);
67          EXPECTED_SEED_BYTES.put(RandomSourceInternal.TWO_CMRES, intBytes * 1);
68          EXPECTED_SEED_BYTES.put(RandomSourceInternal.TWO_CMRES_SELECT, intBytes * 1);
69          EXPECTED_SEED_BYTES.put(RandomSourceInternal.MT_64, longBytes * 312);
70          EXPECTED_SEED_BYTES.put(RandomSourceInternal.MWC_256, intBytes * 257);
71          EXPECTED_SEED_BYTES.put(RandomSourceInternal.KISS, intBytes * 4);
72          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XOR_SHIFT_1024_S_PHI, longBytes * 16);
73          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_64_S, intBytes * 2);
74          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_64_SS, intBytes * 2);
75          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_128_PLUS, intBytes * 4);
76          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_128_SS, intBytes * 4);
77          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_128_PLUS, longBytes * 2);
78          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_128_SS, longBytes * 2);
79          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_256_PLUS, longBytes * 4);
80          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_256_SS, longBytes * 4);
81          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_512_PLUS, longBytes * 8);
82          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_512_SS, longBytes * 8);
83          EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_XSH_RR_32, longBytes * 2);
84          EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_XSH_RS_32, longBytes * 2);
85          EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_RXS_M_XS_64, longBytes * 2);
86          EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_MCG_XSH_RR_32, longBytes * 1);
87          EXPECTED_SEED_BYTES.put(RandomSourceInternal.PCG_MCG_XSH_RS_32, longBytes * 1);
88          EXPECTED_SEED_BYTES.put(RandomSourceInternal.MSWS, longBytes * 3);
89          EXPECTED_SEED_BYTES.put(RandomSourceInternal.SFC_32, intBytes * 3);
90          EXPECTED_SEED_BYTES.put(RandomSourceInternal.SFC_64, longBytes * 3);
91          EXPECTED_SEED_BYTES.put(RandomSourceInternal.JSF_32, intBytes * 1);
92          EXPECTED_SEED_BYTES.put(RandomSourceInternal.JSF_64, longBytes * 1);
93          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_128_PP, intBytes * 4);
94          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_128_PP, longBytes * 2);
95          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_256_PP, longBytes * 4);
96          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_SHI_RO_512_PP, longBytes * 8);
97          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_1024_PP, longBytes * 16);
98          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_1024_S, longBytes * 16);
99          EXPECTED_SEED_BYTES.put(RandomSourceInternal.XO_RO_SHI_RO_1024_SS, longBytes * 16);
100         // ... add more here.
101         // Verify the seed byte size is reflected in the enum javadoc for RandomSource.
102     }
103 
104     /** Internal identifier for the random source. */
105     private final RandomSourceInternal randomSourceInternal;
106     /** The class type of the native seed. */
107     private final Class<?> type;
108 
109     /**
110      * Initializes the test instance.
111      *
112      * @param randomSourceInternal Internal identifier for the random source.
113      */
114     public RandomSourceInternalParametricTest(RandomSourceInternal randomSourceInternal) {
115         this.randomSourceInternal = randomSourceInternal;
116         // The first constructor argument is always the seed type
117         this.type = randomSourceInternal.getArgs()[0];
118     }
119 
120     /**
121      * Gets the supported native seed types.
122      *
123      * @return the types
124      */
125     @Parameters
126     public static Object[] getTypes() {
127         return RandomSourceInternal.values();
128     }
129 
130     /**
131      * Test the seed can be created as the correct type.
132      */
133     @Test
134     public void testCreateSeed() {
135         final Object seed = randomSourceInternal.createSeed();
136         Assert.assertNotNull(seed);
137         Assert.assertEquals("Seed was not the correct class", type, seed.getClass());
138         Assert.assertTrue("Seed was not identified as the native type", randomSourceInternal.isNativeSeed(seed));
139     }
140 
141     /**
142      * Test the seed can be converted to the correct type from any of the supported input types.
143      */
144     @Test
145     public void testConvertSupportedSeed() {
146         for (final Object input : SUPPORTED_SEEDS) {
147             final Object seed = randomSourceInternal.convertSeed(input);
148             final String msg = input.getClass() + " input seed was not converted";
149             Assert.assertNotNull(msg, seed);
150             Assert.assertEquals(msg, type, seed.getClass());
151             Assert.assertTrue(msg, randomSourceInternal.isNativeSeed(seed));
152         }
153     }
154 
155     /**
156      * Test unsupported input seed types are rejected.
157      */
158     @Test
159     public void testCannotConvertUnsupportedSeed() {
160         for (final Object input : UNSUPPORTED_SEEDS) {
161             try {
162                 randomSourceInternal.convertSeed(input);
163                 Assert.fail(input.getClass() + " input seed was not rejected as unsupported");
164             } catch (UnsupportedOperationException ex) {
165                 // This is expected
166             }
167         }
168     }
169 
170     /**
171      * Test the seed byte size is reported as the size of a int/long primitive for Int/Long
172      * seed types and a multiple of it for int[]/long[] types.
173      */
174     @Test
175     public void testCreateSeedBytesSizeIsPositiveAndMultipleOf4Or8() {
176         // This should be the full length seed
177         final byte[] seed = randomSourceInternal.createSeedBytes(new SplitMix64(12345L));
178 
179         final int size = seed.length;
180         Assert.assertNotEquals("Seed is empty", 0, size);
181 
182         if (randomSourceInternal.isNativeSeed(Integer.valueOf(0))) {
183             Assert.assertEquals("Expect 4 bytes for Integer", 4, size);
184         } else if (randomSourceInternal.isNativeSeed(Long.valueOf(0))) {
185             Assert.assertEquals("Expect 8 bytes for Long", 8, size);
186         } else if (randomSourceInternal.isNativeSeed(new int[0])) {
187             Assert.assertEquals("Expect 4n bytes for int[]", 0, size % 4);
188         } else if (randomSourceInternal.isNativeSeed(new long[0])) {
189             Assert.assertEquals("Expect 8n bytes for long[]", 0, size % 8);
190         } else {
191             Assert.fail("Unknown native seed type");
192         }
193     }
194 
195     /**
196      * Test the seed byte size against the expected value.
197      *
198      * <p>The expected values are maintained in a table and must be manually updated
199      * for new generators. This test forms an additional cross-reference check that the
200      * seed size in RandomSourceInternal has been correctly set and the size should map to
201      * the array size in the RandomSource javadoc (if applicable).
202      */
203     @Test
204     public void testCreateSeedBytes() {
205         // This should be the full length seed
206         final byte[] seed = randomSourceInternal.createSeedBytes(new SplitMix64(12345L));
207         final int size = seed.length;
208 
209         final Integer expected = EXPECTED_SEED_BYTES.get(randomSourceInternal);
210         Assert.assertNotNull("Missing expected seed byte size: " + randomSourceInternal, expected);
211         Assert.assertEquals(randomSourceInternal.toString(),
212                             expected.intValue(), size);
213     }
214 }