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 L64X128StarStar}.
31   */
32  class L64X128StarStarTest 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          @Override
59          public Mix getMix() {
60              return AbstractLXMTest::mixStarStar;
61          }
62      }
63  
64      @Override
65      LXMGeneratorFactory getFactory() {
66          return Factory.INSTANCE;
67      }
68  
69      @Override
70      LongJumpableUniformRandomProvider create(long[] seed) {
71          return new L64X128StarStar(seed);
72      }
73  
74      @Override
75      Stream<Arguments> getReferenceData() {
76          /*
77           * Reference data from JDK 19:
78           * java.util.random.RandomGeneratorFactory.of("L64X128StarStarRandom").create(seed)
79           *
80           * Full byte[] seed created using SecureRandom.nextBytes. The seed was converted
81           * to long[] by filling the long bits sequentially starting at the most significant
82           * byte matching the method used by the JDK, e.g.
83           * long[] result = new long[seed.length / Long.BYTES];
84           * ByteBuffer.wrap(seed).order(ByteOrder.BIG_ENDIAN).asLongBuffer().get(result);
85           *
86           * Note: Seed order: LCG addition; LCG state; XBG state.
87           */
88          return Stream.of(
89              Arguments.of(
90                  new long[] {
91                      0xaee231d184911ad8L, 0x4520532a5549a510L, 0x71a6eb6f79366919L, 0x2083c44d4810bd6fL,
92                  },
93                  new long[] {
94                      0x830084a6413e9d08L, 0x9d3ad3573722e868L, 0xd64138313ff25b4fL, 0x345eda927a84a375L,
95                      0x7c20a3612b0c8fbeL, 0xab227b85595fb299L, 0x0c8a0567f166ea51L, 0xf5983403c41db8c3L,
96                      0x4eb1b4dd8d7663b0L, 0xb488ddd3113af675L, 0xd0a7849e601e7a14L, 0x881dd54f67e038a3L,
97                      0xf0f052f1c25c5140L, 0x50a63f651e394656L, 0x0e5c4b325f8e2e5fL, 0xf7768e8d250a834aL,
98                      0x6dd2857fab5822c8L, 0x261a1503b1799ed1L, 0xdc635cdfcb6b928eL, 0xfa41d9e29c8c35c9L,
99                      0xca56e6ec0e924af1L, 0xc9de942825743c7aL, 0xe975cbf2a1d60758L, 0x671044bedafa32deL,
100                     0x3b35dc7e2e583e6cL, 0x7cb03e41feecfc85L, 0x67972bdd63a30d60L, 0xf75b8f37d0b4717bL,
101                     0x0d833c09bba62056L, 0xfca3a08b9be878c3L, 0x4da8c219bd7fc0f9L, 0xeebabf6885853096L,
102                     0x65555aba641a0653L, 0x6a9f5c913a9ed878L, 0x99fbda7113e1354dL, 0x198cbfc3c00a0ca2L,
103                     0x7387aedc8b8b69d4L, 0x26c47d85244685c1L, 0x28065c272a6b0f55L, 0x6de39d4fca78f723L,
104                 }),
105             Arguments.of(
106                 new long[] {
107                     0x2c157477eb19cba9L, 0x26048e417b3694adL, 0x659d21b0cf0f005bL, 0x02bb00bb4faa6188L,
108                 },
109                 new long[] {
110                     0xb5f6cb871d993745L, 0x8261e85c8528b091L, 0x801adfbdd328d6a9L, 0x8e9089dc51c29de2L,
111                     0x2b0be99918898d84L, 0x97c205687d17d03cL, 0xf92e93c9b2f96d37L, 0xdc8f743d166b8bffL,
112                     0x99db57d3bd595c68L, 0xc93392d2fb587e46L, 0xc62445f3b2ed9294L, 0xe4252d2ec389dbe6L,
113                     0x369edad9d9b5651bL, 0xb2e0f52955f8a1bfL, 0xc9a1b6980b2c8ea8L, 0x40e4aa609c3bb19cL,
114                     0xfaede9c7002b035cL, 0xc00b77d1b7989058L, 0x243164c89302811dL, 0xcf47804a33ca4f9dL,
115                     0x15a1a6fc769a1a06L, 0x8be1729b907194ceL, 0xb943ef109365dec3L, 0x35070a31e43a522bL,
116                     0x7a29a9150d6a72b9L, 0x9c67a5bbb72d971bL, 0xf2b927381613bb56L, 0xa317e83d3332ed5aL,
117                     0x62f06a382579291cL, 0x065063eaf4967efdL, 0x4e0dbd771d7e9957L, 0xa1179b58c6d3a335L,
118                     0x30419a0f7114b910L, 0x144eb4c373807ac2L, 0xd0904239bc2793deL, 0xe426ba924a2099f2L,
119                     0xd013d7a457218234L, 0xe7ed5726a3141b1cL, 0xe5be2e06a8e623afL, 0xa9963ae2497e0c14L,
120                 }),
121             Arguments.of(
122                 new long[] {
123                     0xb84e2fb2f137f0d3L, 0x51a02981dca47a14L, 0xe176a5f236f46230L, 0xb841ae1033bb4ba1L,
124                 },
125                 new long[] {
126                     0x813bb3b8ef5bfe77L, 0x93d1da33cc598a2cL, 0x641bdf12d424ed18L, 0xe9002256c8326374L,
127                     0xbfa7b1f649eebe70L, 0xa2a50c9b58d5b8a8L, 0x102d51f8895a250cL, 0x7f645e87d4c4aeb9L,
128                     0xc63a82d7347d97c1L, 0x93b0e513a347fdc5L, 0x9fbe539e9b811777L, 0xf0197ca328174abfL,
129                     0x9e0417be835df367L, 0x7a87a1e461c874ffL, 0x913ec6fbe78ff63dL, 0x62d24838cff9bd28L,
130                     0x19009f9566b627cbL, 0xf30e7ecc3cd4f372L, 0x8513d9f72faaeea6L, 0x02190ed9019ae371L,
131                     0xe1760616b1fd4efeL, 0x58a0960d45583134L, 0x377c9c9e0872d8dbL, 0x4cf6ec0f1ecfb91bL,
132                     0x54910aebb60facc2L, 0x7637e8286fc51437L, 0x15ed477ae483d7caL, 0x7acbbd2a4d1f2901L,
133                     0x9fc5ddd926500fb6L, 0x16c98971886a86aeL, 0xc61dcc20296a9228L, 0x90b4583569802b04L,
134                     0x8ffeb5b929c2d785L, 0xf35e8bfdccc70e97L, 0xc427ca7da8efc0d9L, 0x3d8fe39fcaf4b913L,
135                     0xd350ca35762a802cL, 0x340852a8b058cf12L, 0x073fa1debb4f835fL, 0xfacc317ecd1f047bL,
136                 }));
137     }
138 
139     @ParameterizedTest
140     @MethodSource(value = "getReferenceData")
141     void testElementConstructor(long[] seed, long[] expected) {
142         final L64X128StarStar rng1 = new L64X128StarStar(seed);
143         final L64X128StarStar rng2 = new L64X128StarStar(seed[0], seed[1], seed[2], seed[3]);
144         RandomAssert.assertNextLongEquals(seed.length * 2, rng1, rng2);
145     }
146 
147     /**
148      * Test split with zero bits from the source. This should be robust to escape the state
149      * of all zero bits that will create an invalid state for the xor-based generator (XBG).
150      */
151     @Test
152     void testSplitWithZeroBits() {
153         final UniformRandomProvider zeroSource = () -> 0;
154         final long[] seed = new long[Factory.INSTANCE.seedSize()];
155         // Here we copy the split which sets the LCG increment to odd
156         seed[(Factory.INSTANCE.lcgSeedSize() / 2) - 1] = 1;
157         final SplittableUniformRandomProvider rng1 = new L64X128StarStar(seed);
158         final SplittableUniformRandomProvider rng2 = rng1.split(zeroSource);
159         RandomAssert.assertNextLongNotEquals(seed.length * 2, rng1, rng2);
160 
161         // Since we know how the zero seed is amended
162         long z = 0;
163         for (int i = Factory.INSTANCE.lcgSeedSize(); i < seed.length; i++) {
164             seed[i] = LXMSupport.lea64(z);
165             z += LXMSupport.GOLDEN_RATIO_64;
166         }
167         final SplittableUniformRandomProvider rng3 = new L64X128StarStar(seed);
168         final SplittableUniformRandomProvider rng4 = rng1.split(zeroSource);
169         RandomAssert.assertNextLongEquals(seed.length * 2, rng3, rng4);
170     }
171 }