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 java.util.SplittableRandom;
20  import org.apache.commons.rng.core.source64.SplitMix64;
21  import org.junit.jupiter.api.Assertions;
22  import org.junit.jupiter.api.Test;
23  import org.junit.jupiter.params.ParameterizedTest;
24  import org.junit.jupiter.params.provider.ValueSource;
25  
26  /**
27   * Tests for {@link MixFunctions}.
28   */
29  class MixFunctionsTest {
30      @ParameterizedTest
31      @ValueSource(longs = {0, -1, 1, 63812881278371L, -236812734617872L})
32      void testStafford13(long seed) {
33          // Reference output is from a SplitMix64 generator
34          final SplitMix64 rng = new SplitMix64(seed);
35  
36          long x = seed;
37          for (int i = 0; i < 20; i++) {
38              Assertions.assertEquals(rng.nextLong(), MixFunctions.stafford13(x += MixFunctions.GOLDEN_RATIO_64));
39          }
40      }
41  
42      @Test
43      void testMurmur3() {
44          // Reference output from the c++ function fmix32(uint32_t) in smhasher.
45          // https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
46          final int seedA = 0x012de1ba;
47          final int[] valuesA = {
48              0x2f66c8b6, 0x256c0269, 0x054ef409, 0x402425ba, 0x78ebf590, 0x76bea1db,
49              0x8bf5dcbe, 0x104ecdd4, 0x43cfc87e, 0xa33c7643, 0x4d210f56, 0xfa12093d,
50          };
51  
52          int x = seedA;
53          for (int z : valuesA) {
54              Assertions.assertEquals(z, MixFunctions.murmur3(x += MixFunctions.GOLDEN_RATIO_32));
55          }
56  
57          // Values from a seed of zero
58          final int[] values0 = {
59              0x92ca2f0e, 0x3cd6e3f3, 0x1b147dcc, 0x4c081dbf, 0x487981ab, 0xdb408c9d,
60              0x78bc1b8f, 0xd83072e5, 0x65cbdd54, 0x1f4b8cef, 0x91783bb0, 0x0231739b,
61          };
62  
63          x = 0;
64          for (int z : values0) {
65              Assertions.assertEquals(z, MixFunctions.murmur3(x += MixFunctions.GOLDEN_RATIO_32));
66          }
67      }
68  
69      /**
70       * Test the reverse of the Stafford 13 mix function.
71       */
72      @Test
73      void testUnmixStafford13() {
74          final SplittableRandom rng = new SplittableRandom();
75          for (int i = 0; i < 100; i++) {
76              final long x = rng.nextLong();
77              final long y = MixFunctions.stafford13(x);
78              Assertions.assertEquals(x, unmixStafford13(y));
79          }
80      }
81  
82      /**
83       * Reverse the Stafford 13 mix function.
84       *
85       * <p>This can be used to generate specific seed values for a SplitMix-style RNG using
86       * the Stafford 13 mix constants, for example in the SeedFactoryTest. It is left in
87       * the test source code as a reference to allow computation of test seeds.
88       *
89       * @param x Argument
90       * @return the result
91       */
92      private static long unmixStafford13(long x) {
93          // Multiplicative inverse:
94          // https://lemire.me/blog/2017/09/18/computing-the-inverse-of-odd-integers/
95          // Invert 0xbf58476d1ce4e5b9L
96          final long u1 = 0x96de1b173f119089L;
97          // Invert 0x94d049bb133111ebL
98          final long u2 = 0x319642b2d24d8ec3L;
99          // Inversion of xor right-shift operations exploits the facts:
100         // 1. A xor operation can be used to recover itself:
101         //   x ^ y = z
102         //   z ^ y = x
103         //   z ^ x = y
104         // 2. During xor right-shift of size n the top n-bits are unchanged.
105         // 3. Known values from the top bits can be used to recover the next set of n-bits.
106         // Recovery is done by xoring with the shifted argument, then doubling the right shift.
107         // This is iterated until all bits are recovered. With initial large shifts only one
108         // doubling is required.
109         x = x ^ (x >>> 31);
110         x ^= x >>> 62;
111         x *= u2;
112         x = x ^ (x >>> 27);
113         x ^= x >>> 54;
114         x *= u1;
115         x = x ^ (x >>> 30);
116         x ^= x >>> 60;
117         return x;
118     }
119 }