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.core.source64;
18  
19  import java.util.stream.Stream;
20  import org.apache.commons.rng.LongJumpableUniformRandomProvider;
21  import org.apache.commons.rng.SplittableUniformRandomProvider;
22  import org.apache.commons.rng.UniformRandomProvider;
23  import org.apache.commons.rng.core.RandomAssert;
24  import org.junit.jupiter.api.Test;
25  import org.junit.jupiter.params.ParameterizedTest;
26  import org.junit.jupiter.params.provider.Arguments;
27  import org.junit.jupiter.params.provider.MethodSource;
28  
29  /**
30   * Test for {@link L64X128Mix}.
31   */
32  class L64X128MixTest extends AbstractLXMTest {
33  
34      /**
35       * Factory to create a composite LXM generator that is equivalent
36       * to the RNG under test.
37       */
38      private static class Factory implements LXMGeneratorFactory {
39          static final Factory INSTANCE = new Factory();
40  
41          @Override
42          public int lcgSeedSize() {
43              return 2;
44          }
45  
46          @Override
47          public int xbgSeedSize() {
48              return 2;
49          }
50  
51          @Override
52          public LXMGenerator create(long[] seed) {
53              return new LXMGenerator(getMix(),
54                                      new LCG64(seed[0], seed[1]),
55                                      new XBGXoRoShiRo128(seed[2], seed[3], false));
56          }
57      }
58  
59      @Override
60      LXMGeneratorFactory getFactory() {
61          return Factory.INSTANCE;
62      }
63  
64      @Override
65      LongJumpableUniformRandomProvider create(long[] seed) {
66          return new L64X128Mix(seed);
67      }
68  
69      @Override
70      Stream<Arguments> getReferenceData() {
71          /*
72           * Reference data from JDK 19:
73           * java.util.random.RandomGeneratorFactory.of("L64X128MixRandom").create(seed)
74           *
75           * Full byte[] seed created using SecureRandom.nextBytes. The seed was converted
76           * to long[] by filling the long bits sequentially starting at the most significant
77           * byte matching the method used by the JDK, e.g.
78           * long[] result = new long[seed.length / Long.BYTES];
79           * ByteBuffer.wrap(seed).order(ByteOrder.BIG_ENDIAN).asLongBuffer().get(result);
80           *
81           * Note: Seed order: LCG addition; LCG state; XBG state.
82           */
83          return Stream.of(
84              Arguments.of(
85                  new long[] {
86                      0xa2fc3db3faf20b60L, 0x0ca17f844355c30bL, 0x966393c3c699b9c4L, 0x26d0b369e961d05dL,
87                  },
88                  new long[] {
89                      0x431e95bfddd868f1L, 0x11d41649fc250a2bL, 0x386107fb4229f3c6L, 0x58809538283ec2beL,
90                      0x792d8502b636fa57L, 0xdfdd635a8d4b513bL, 0x639e33d9d46709a4L, 0xac064fae27c58ae2L,
91                      0xf503c18f9b81e221L, 0xc8e9239b41d75ac2L, 0x305035c93feb8da0L, 0xcb639286b0b77172L,
92                      0x134cb8dea2744fe3L, 0x849f85c7979eaaa8L, 0x7ec51bffd104b0b8L, 0xc9fb22d1f0ce8392L,
93                      0xdcbedf16be02f77dL, 0x8779acb6ffd5cb89L, 0x24be000a509e92ebL, 0x73b09559615c1540L,
94                      0x25f9fba5d4818f13L, 0x171607e2956fef8dL, 0x8950dc3f40d5f8cfL, 0xd11e7c72b36f1c72L,
95                      0xe687a38bb9887225L, 0x078daf1a151ba805L, 0x2aeb69f8638fda77L, 0xff50d66233a5078dL,
96                      0x9ed224880f7564a9L, 0x298f140cc162145dL, 0xeb2ba17c17e97b8fL, 0x59d785fd7c80a45bL,
97                      0x45afefc04e164b8fL, 0x6282afa6f7a13d16L, 0x59a5232c189d6c95L, 0x7c36546951518872L,
98                      0x7973a0bdd8a7d494L, 0x711c54e35cc6d30aL, 0xc931dd22fcc5e6a9L, 0x079e4436817465d5L,
99                  }),
100             Arguments.of(
101                 new long[] {
102                     0x36d2370e6207bab2L, 0xc4753d31834da409L, 0xecef76bb60e6f8daL, 0xb6b63fbeb3710b5eL,
103                 },
104                 new long[] {
105                     0x4a3b46e4c46330c6L, 0x1edb14ee973103cbL, 0x82ac31b5cfd9b725L, 0xbaa145314d88b5c1L,
106                     0xced95922c6f6fdcfL, 0xdba6a91b0fb3ecc1L, 0x3d479c0083bc99e8L, 0x4935d8779f92155cL,
107                     0xb375363b0f724142L, 0x2a849a0a4d3328bbL, 0x13d9384867b9e73dL, 0x692a97c3730699c7L,
108                     0x1110586b1ba6aaf7L, 0x8b49b5c42008a6b4L, 0xb099f56ded714e73L, 0xe47202801ea2edeeL,
109                     0x90a3d0c6c4cd6616L, 0x69e27ce6f839bffeL, 0xd5f1dacba40dfa28L, 0xcbc3b8207d22ab3cL,
110                     0xbcfebfe31140ebcfL, 0xc5ade4d27d907a6eL, 0xa4c576fd470abdacL, 0x9a06630004e2434cL,
111                     0xe5c91ffe2e649403L, 0x79e8cde35d8a01c3L, 0xfe6e6a2eac80c265L, 0x5a906723692cf576L,
112                     0xef004a00e60db421L, 0xa15ee20151bb95f6L, 0x12cb5e8a4c7adaceL, 0x80172ad9c0ed3661L,
113                     0x492c31dc6ad30a4dL, 0x0ac761509dd595efL, 0x9349f08282fd74d8L, 0xd2dcbdbb42c1cfddL,
114                     0xcc21040c1e9d367bL, 0x62b448f73d61c8a4L, 0x567ef895e846cebdL, 0x8238e9fbccabbb44L,
115                 }),
116             Arguments.of(
117                 new long[] {
118                     0xc705d150b09c1f94L, 0xa828a2ba98ac9c61L, 0x343daac9fd050fdbL, 0xce65de67934aebadL,
119                 },
120                 new long[] {
121                     0x86cbfd59d51c2b12L, 0x2015e74ab836e71fL, 0x76c59d63993224fbL, 0x5dfd3a311e5df341L,
122                     0xeb1654b961c05168L, 0xb0085df8d9127280L, 0xc9ec3f9c1ce14534L, 0x44cae5abad7cc428L,
123                     0xaac545985ef4a83dL, 0xbdffb30f032cc9aaL, 0x8309e6733ddef867L, 0x84b62aa79a9de881L,
124                     0x8ac5790bda006d61L, 0x2d66e83835309543L, 0x9a69810f00faad5eL, 0xfb52e0c7e2387ac6L,
125                     0xb7edafdec4002910L, 0x18b3feeac49b7e4eL, 0x43a4f03145408ca5L, 0x9dc51ffbaca77d03L,
126                     0xbb7ad1a943a1daceL, 0xc41841a02d67ceeaL, 0xb88033e5005f190eL, 0x79a40a956ed7d916L,
127                     0xc58bad848a86fee9L, 0x3a311ee2f4d9e712L, 0xe4b32160ddf8e17cL, 0x723d7ac41d2496aaL,
128                     0x96f96a92d33bc637L, 0x724b68f3e1d969e7L, 0x80edfd99dfc0ef39L, 0x7924f19f4d8bfb4eL,
129                     0x4fd956126608af2dL, 0xf81eb1b489993b23L, 0x05d2514191a38239L, 0x819dda6b5dd8eb2bL,
130                     0xb31d2eba66041796L, 0x196d3972d36f36b9L, 0x9ee7893afecfd4eeL, 0x2359a24a67a8e98bL,
131                 }));
132     }
133 
134     @ParameterizedTest
135     @MethodSource(value = "getReferenceData")
136     void testElementConstructor(long[] seed, long[] expected) {
137         final L64X128Mix rng1 = new L64X128Mix(seed);
138         final L64X128Mix rng2 = new L64X128Mix(seed[0], seed[1], seed[2], seed[3]);
139         RandomAssert.assertNextLongEquals(seed.length * 2, rng1, rng2);
140     }
141 
142     /**
143      * Test split with zero bits from the source. This should be robust to escape the state
144      * of all zero bits that will create an invalid state for the xor-based generator (XBG).
145      */
146     @Test
147     void testSplitWithZeroBits() {
148         final UniformRandomProvider zeroSource = () -> 0;
149         final long[] seed = new long[Factory.INSTANCE.seedSize()];
150         // Here we copy the split which sets the LCG increment to odd
151         seed[(Factory.INSTANCE.lcgSeedSize() / 2) - 1] = 1;
152         final SplittableUniformRandomProvider rng1 = new L64X128Mix(seed);
153         final SplittableUniformRandomProvider rng2 = rng1.split(zeroSource);
154         RandomAssert.assertNextLongNotEquals(seed.length * 2, rng1, rng2);
155 
156         // Since we know how the zero seed is amended
157         long z = 0;
158         for (int i = Factory.INSTANCE.lcgSeedSize(); i < seed.length; i++) {
159             seed[i] = LXMSupport.lea64(z);
160             z += LXMSupport.GOLDEN_RATIO_64;
161         }
162         final SplittableUniformRandomProvider rng3 = new L64X128Mix(seed);
163         final SplittableUniformRandomProvider rng4 = rng1.split(zeroSource);
164         RandomAssert.assertNextLongEquals(seed.length * 2, rng3, rng4);
165     }
166 }