diff --git a/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/Philox4x32.java b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/Philox4x32.java new file mode 100644 index 000000000..bcfad35df --- /dev/null +++ b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/Philox4x32.java @@ -0,0 +1,325 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.rng.core.source32; + +import org.apache.commons.rng.JumpableUniformRandomProvider; +import org.apache.commons.rng.LongJumpableUniformRandomProvider; +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.core.util.NumberFactory; + +import java.util.Arrays; + +/** + * This class implements the Philox4x32 128-bit counter-based generator with 10 rounds. + * Jumping in the sequence is essentially instantaneous. This generator provides subsequences for easy parallelization. + * + * @see Parallel Random Numbers: As Easy as 1,2,3 + * @since 1.7 + */ +public final class Philox4x32 extends IntProvider implements LongJumpableUniformRandomProvider { + /** + * Philox 32-bit mixing constant for counter 0. + */ + private static final int K_PHILOX_10_A = 0x9E3779B9; + /** + * Philox 32-bit mixing constant for counter 1. + */ + private static final int K_PHILOX_10_B = 0xBB67AE85; + /** + * Philox 32-bit constant for key 0. + */ + private static final int K_PHILOX_SA = 0xD2511F53; + /** + * Philox 32-bit constant for key 1. + */ + private static final int K_PHILOX_SB = 0xCD9E8D57; + /** + * Internal buffer size. + */ + private static final int PHILOX_BUFFER_SIZE = 4; + /** + * number of int variables. + */ + private static final int STATE_SIZE = 7; + + /** + * Counter 0. + */ + private int counter0; + /** + * Counter 1. + */ + private int counter1; + /** + * Counter 2. + */ + private int counter2; + /** + * Counter 3. + */ + private int counter3; + /** + * Output point. + */ + private int[] buffer = new int[PHILOX_BUFFER_SIZE]; // UINT4 + /** + * Key low bits. + */ + private int key0; + /** + * Key high bits. + */ + private int key1; + /** + * State index: which output word is next (0..3). + */ + private int bufferPosition; + + + /** + * Copy constructor. + * + * @param source Source to copy. + */ + private Philox4x32(Philox4x32 source) { + super(source); + counter0 = source.counter0; + counter1 = source.counter1; + counter2 = source.counter2; + counter3 = source.counter3; + key0 = source.key0; + key1 = source.key1; + bufferPosition = source.bufferPosition; + buffer = source.buffer.clone(); + } + + /** + * Creates a new instance with default seed. Subsequence and offset (or equivalently, the internal counter) + * are set to zero. + */ + public Philox4x32() { + this(67280421310721L); + } + + /** + * Creates a new instance with a given seed. Subsequence and offset (or equivalently, the internal counter) + * are set to zero. + * + * @param key the low 32 bits constitute the first int key of Philox, + * and the high 32 bits constitute the second int key of Philox + */ + private Philox4x32(long key) { + this(new int[]{(int) key, (int) (key >>> 32)}); + } + + /** + * Creates a new instance based on an array of int containing, key (first two ints) and + * the counter (next 4 ints, low bits = first int). The counter is not scrambled and may + * be used to create contiguous blocks with size a multiple of 4 ints. For example, + * setting seed[2] = 1 is equivalent to start with seed[2]=0 and calling {@link #next()} 4 times. + * + * @param seed an array of size 6 defining key0,key1,counter0,counter1,counter2,counter3. + * If the size is smaller, zero values are assumed. + */ + public Philox4x32(int[] seed) { + final int[] input = seed.length < 6 ? Arrays.copyOf(seed, 6) : seed; + key0 = input[0]; + key1 = input[1]; + counter0 = input[2]; + counter1 = input[3]; + counter2 = input[4]; + counter3 = input[5]; + bufferPosition = PHILOX_BUFFER_SIZE; + } + + /** + * Fetch next integer from the buffer, or regenerate the buffer using 10 rounds. + * + * @return random integer + */ + @Override + public int next() { + final int p = bufferPosition; + if (p < PHILOX_BUFFER_SIZE) { + bufferPosition = p + 1; + return buffer[p]; + } + incrementCounter(); + rand10(); + bufferPosition = 1; + return buffer[0]; + } + + /** + * Increment by one. + */ + private void incrementCounter() { + counter0++; + if (counter0 != 0) { + return; + } + + counter1++; + if (counter1 != 0) { + return; + } + + counter2++; + if (counter2 != 0) { + return; + } + + counter3++; + } + + /** + * Performs a single round of philox. + * + * @param ctr local counter, which will be updated after each call. + * @param key0 key low bits + * @param key1 key high bits + */ + private static void singleRound(int[] ctr, int key0, int key1) { + long product = (K_PHILOX_SA & 0xFFFFFFFFL) * (ctr[0] & 0xFFFFFFFFL); + final int hi0 = (int) (product >>> 32); + final int lo0 = (int) product; + product = (K_PHILOX_SB & 0xFFFFFFFFL) * (ctr[2] & 0xFFFFFFFFL); + final int hi1 = (int) (product >>> 32); + final int lo1 = (int) product; + + ctr[0] = hi1 ^ ctr[1] ^ key0; + ctr[1] = lo1; + ctr[2] = hi0 ^ ctr[3] ^ key1; + ctr[3] = lo0; + } + + /** + * Perform 10 rounds, using counter0, counter1, counter2, counter3 as starting point. + * It updates the buffer member variable, but no others. + */ + private void rand10() { + buffer[0] = counter0; + buffer[1] = counter1; + buffer[2] = counter2; + buffer[3] = counter3; + + int k0 = key0; + int k1 = key1; + + //unrolled loop for performance + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + k0 += K_PHILOX_10_A; + k1 += K_PHILOX_10_B; + singleRound(buffer, k0, k1); + } + + + /** + * {@inheritDoc} + * + *
Increments the subsequence by 1.
+ *The jump size is the equivalent of 4*296 calls to + * {@link UniformRandomProvider#nextInt() nextInt()}. + */ + @Override + public JumpableUniformRandomProvider longJump() { + final Philox4x32 copy = copy(); + counter3++; + rand10(); + resetCachedState(); + return copy; + } + + /** + * {@inheritDoc} + * + *
The jump size is the equivalent of 4*264 + * calls to {@link UniformRandomProvider#nextInt() nextInt()}. + */ + @Override + public UniformRandomProvider jump() { + final Philox4x32 copy = copy(); + if (++counter2 == 0) { + counter3++; + } + rand10(); + resetCachedState(); + return copy; + } + + /** + * {@inheritDoc} + */ + @Override + protected byte[] getStateInternal() { + return composeStateInternal(NumberFactory.makeByteArray( + new int[]{key0, key1, counter0, counter1, counter2, counter3, bufferPosition}), + super.getStateInternal()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void setStateInternal(byte[] s) { + final byte[][] c = splitStateInternal(s, STATE_SIZE * 4); + final int[] state = NumberFactory.makeIntArray(c[0]); + key0 = state[0]; + key1 = state[1]; + counter0 = state[2]; + counter1 = state[3]; + counter2 = state[4]; + counter3 = state[5]; + bufferPosition = state[6]; + super.setStateInternal(c[1]); + rand10(); //to regenerate the internal buffer + } + + /** + * Create a copy. + * + * @return the copy + */ + private Philox4x32 copy() { + return new Philox4x32(this); + } +} diff --git a/commons-rng-core/src/main/java/org/apache/commons/rng/core/source64/Philox4x64.java b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source64/Philox4x64.java new file mode 100644 index 000000000..8cb15e1c0 --- /dev/null +++ b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source64/Philox4x64.java @@ -0,0 +1,319 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.rng.core.source64; + +import org.apache.commons.rng.JumpableUniformRandomProvider; +import org.apache.commons.rng.LongJumpableUniformRandomProvider; +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.core.util.NumberFactory; + +import java.util.Arrays; + +/** + * This class implements the Philox4x64 256-bit counter-based generator with 10 rounds. + * Jumping in the sequence is essentially instantaneous. This generator provides subsequences for easy parallelization. + * + * @see Parallel Random Numbers: As Easy as 1,2,3 + * @since 1.7 + */ +public final class Philox4x64 extends LongProvider implements LongJumpableUniformRandomProvider { + /** + * Philox 32-bit mixing constant for counter 0. + */ + private static final long PHILOX_M0 = 0xD2E7470EE14C6C93L; + /** + * Philox 32-bit mixing constant for counter 1. + */ + private static final long PHILOX_M1 = 0xCA5A826395121157L; + /** + * Philox 32-bit constant for key 0. + */ + private static final long PHILOX_W0 = 0x9E3779B97F4A7C15L; + /** + * Philox 32-bit constant for key 1. + */ + private static final long PHILOX_W1 = 0xBB67AE8584CAA73BL; + /** + * Internal buffer size. + */ + private static final int PHILOX_BUFFER_SIZE = 4; + /** + * number of long variables. + */ + private static final int STATE_SIZE = 7; + + /** + * Counter 0. + */ + private long counter0; + /** + * Counter 1. + */ + private long counter1; + /** + * Counter 2. + */ + private long counter2; + /** + * Counter 3. + */ + private long counter3; + + /** + * Output point. + */ + private long[] buffer = new long[PHILOX_BUFFER_SIZE]; // UINT4 + /** + * Key low bits. + */ + private long key0; + /** + * Key high bits. + */ + private long key1; + /** + * State index: which output word is next (0..3). + */ + private int bufferPosition; + + /** + * Copy constructor. + * + * @param source Source to copy. + */ + private Philox4x64(Philox4x64 source) { + super(source); + counter0 = source.counter0; + counter1 = source.counter1; + counter2 = source.counter2; + counter3 = source.counter3; + key0 = source.key0; + key1 = source.key1; + bufferPosition = source.bufferPosition; + buffer = source.buffer.clone(); + } + + /** + * Creates a new instance with default seed. Subsequence and offset are set to zero. + */ + public Philox4x64() { + this(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0L, 0L, 0L, 0L}); + } + + + /** + * Creates a new instance given 6 long numbers containing, key (first two longs) and + * the counter (next 4, starts at first). The counter is not scrambled and may + * be used to create contiguous blocks with size a multiple of 4 longs. For example, + * setting seed[2] = 1 is equivalent to start with seed[2]=0 and calling {@link #next()} 4 times. + * + * @param keyAndCounter the first two number are the key and the next 4 number are the counter. + * if size is smaller than 6, the array is padded with 0. + */ + public Philox4x64(long[] keyAndCounter) { + final long[] input = keyAndCounter.length < 6 ? Arrays.copyOf(keyAndCounter, 6) : keyAndCounter; + key0 = input[0]; + key1 = input[1]; + counter0 = input[2]; + counter1 = input[3]; + counter2 = input[4]; + counter3 = input[5]; + bufferPosition = PHILOX_BUFFER_SIZE; + } + + /** + * Fetch next long from the buffer, or regenerate the buffer using 10 rounds. + * + * @return random 64-bit integer + */ + private long next64() { + final int p = bufferPosition; + if (bufferPosition < PHILOX_BUFFER_SIZE) { + bufferPosition = p + 1; + return buffer[p]; + } + incrementCounter(); + rand10(); + bufferPosition = 1; + return buffer[0]; + } + + /** + * Increment by one. + */ + private void incrementCounter() { + counter0++; + if (counter0 != 0) { + return; + } + + counter1++; + if (counter1 != 0) { + return; + } + + counter2++; + if (counter2 != 0) { + return; + } + + counter3++; + } + + /** + * Performs a single round of philox. + * + * @param counter local counter, which will be updated after each call. + * @param key0 key low bits + * @param key1 key high bits + */ + private static void singleRound(long[] counter, long key0, long key1) { + final long lo0 = PHILOX_M0 * counter[0]; + final long hi0 = LXMSupport.unsignedMultiplyHigh(PHILOX_M0, counter[0]); + final long lo1 = PHILOX_M1 * counter[2]; + final long hi1 = LXMSupport.unsignedMultiplyHigh(PHILOX_M1, counter[2]); + + counter[0] = hi1 ^ counter[1] ^ key0; + counter[1] = lo1; + counter[2] = hi0 ^ counter[3] ^ key1; + counter[3] = lo0; + } + + /** + * Perform 10 rounds, using counter0, counter1, counter2, counter3 as starting point. + * + */ + private void rand10() { + buffer[0] = counter0; + buffer[1] = counter1; + buffer[2] = counter2; + buffer[3] = counter3; + + long k0 = key0; + long k1 = key1; + + //unrolled loop for performance + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + k0 += PHILOX_W0; + k1 += PHILOX_W1; + singleRound(buffer, k0, k1); + } + + /** + * {@inheritDoc} + * + *
Increments the subsequence by 1.
+ *The jump size is the equivalent of 4*2192 calls to + * {@link UniformRandomProvider#nextLong() nextLong()}. + */ + @Override + public JumpableUniformRandomProvider longJump() { + final Philox4x64 copy = copy(); + counter3++; + rand10(); + resetCachedState(); + return copy; + } + + /** + * {@inheritDoc} + * + *
The jump size is the equivalent of 4*2128
+ * calls to {@link UniformRandomProvider#nextLong() nextLong()}.
+ */
+ @Override
+ public UniformRandomProvider jump() {
+ final Philox4x64 copy = copy();
+ if (++counter2 == 0) {
+ counter3++;
+ }
+ rand10();
+ resetCachedState();
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long next() {
+ return next64();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected byte[] getStateInternal() {
+ return composeStateInternal(NumberFactory.makeByteArray(
+ new long[]{key0, key1, counter0, counter1, counter2, counter3, bufferPosition}),
+ super.getStateInternal());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void setStateInternal(byte[] s) {
+ final byte[][] c = splitStateInternal(s, STATE_SIZE * 8);
+
+ final long[] state = NumberFactory.makeLongArray(c[0]);
+ key0 = state[0];
+ key1 = state[1];
+ counter0 = state[2];
+ counter1 = state[3];
+ counter2 = state[4];
+ counter3 = state[5];
+ bufferPosition = (int) state[6];
+ super.setStateInternal(c[1]);
+ rand10();
+ }
+
+ /**
+ * Create a copy.
+ *
+ * @return the copy
+ */
+ private Philox4x64 copy() {
+ return new Philox4x64(this);
+ }
+}
diff --git a/commons-rng-core/src/test/java/org/apache/commons/rng/core/ProvidersList.java b/commons-rng-core/src/test/java/org/apache/commons/rng/core/ProvidersList.java
index 04faee99c..47b93928f 100644
--- a/commons-rng-core/src/test/java/org/apache/commons/rng/core/ProvidersList.java
+++ b/commons-rng-core/src/test/java/org/apache/commons/rng/core/ProvidersList.java
@@ -23,6 +23,7 @@
import org.apache.commons.rng.core.source32.JDKRandom;
import org.apache.commons.rng.core.source32.JenkinsSmallFast32;
+import org.apache.commons.rng.core.source32.Philox4x32;
import org.apache.commons.rng.core.source32.Well512a;
import org.apache.commons.rng.core.source32.XoRoShiRo64Star;
import org.apache.commons.rng.core.source32.XoRoShiRo64StarStar;
@@ -45,6 +46,7 @@
import org.apache.commons.rng.core.source32.DotyHumphreySmallFastCounting32;
import org.apache.commons.rng.core.source32.PcgMcgXshRr32;
import org.apache.commons.rng.core.source32.PcgMcgXshRs32;
+import org.apache.commons.rng.core.source64.Philox4x64;
import org.apache.commons.rng.core.source64.SplitMix64;
import org.apache.commons.rng.core.source64.XorShift1024Star;
import org.apache.commons.rng.core.source64.XorShift1024StarPhi;
@@ -130,6 +132,7 @@ public final class ProvidersList {
LIST32.add(new JenkinsSmallFast32(g.nextInt()));
LIST32.add(new XoShiRo128PlusPlus(new int[] {g.nextInt(), g.nextInt(), g.nextInt()}));
LIST32.add(new L32X64Mix(new int[] {g.nextInt(), g.nextInt()}));
+ LIST32.add(new Philox4x32(new int[] {g.nextInt(), g.nextInt()}));
// ... add more here.
// "long"-based RNGs.
@@ -163,6 +166,7 @@ public final class ProvidersList {
LIST64.add(new L128X128Mix(new long[] {g.nextLong(), g.nextLong(), g.nextLong(), g.nextLong()}));
LIST64.add(new L128X256Mix(new long[] {g.nextLong(), g.nextLong(), g.nextLong(), g.nextLong()}));
LIST64.add(new L128X1024Mix(new long[] {g.nextLong(), g.nextLong(), g.nextLong(), g.nextLong()}));
+ LIST64.add(new Philox4x64(new long[] {g.nextLong(), g.nextLong()}));
// ... add more here.
// Do not modify the remaining statements.
diff --git a/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/Philox4x32Test.java b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/Philox4x32Test.java
new file mode 100644
index 000000000..1f19c78ba
--- /dev/null
+++ b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/Philox4x32Test.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rng.core.source32;
+
+import org.apache.commons.rng.core.RandomAssert;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class Philox4x32Test {
+
+ /*
+ * Data from python randomgen.philox.Philox(key=1234,number=4,width=32) random_raw()
+ * https://bashtage.github.io/randomgen/bit_generators/philox.html
+ */
+
+ private static final int[] EXPECTED_SEQUENCE_1234 = {
+ -1628512715, 482218876, -98078573, 343858512, 1070188760,
+ -66651592, -870905049, -1994573039, -1238984130, 599211371,
+ 1926069095, -394512546, 346514135, -352142790, 196394741,
+ -107436867, -903274039, 860026475, -1309487194, -1778049224,
+ -49503714, -1441076994, -866074276, -1339523817, -1290919251,
+ 1857369626, -1839251177, -2041498882, -1956330288, 905306810,
+ -2114083635, 200746399, 20291031, 214040874, -1628891823,
+ -1958807646, 9198301, -1607720479, -1349496224, 1418271217
+ };
+
+ private static final int[] EXPECTED_SEQUENCE_DEFAULT = {
+ 623720234, -686991347, 358698524, 234508473, 1303720625,
+ 1235930736, -75297729, 110380616, 829652807, -1101240720,
+ -1443748750, -1366075136, -1702811520, 232450464, 350957237,
+ 1425642103, 256542391, 1837662153, -448554748, 637025846,
+ -902021712, 1085962074, -1391041963, 201580325, 1416828610,
+ 599210676, -628463662, -576572235, 457140358, -1026551805,
+ -917125498, 529387774, 1254882949, 1278069784, 724938314,
+ -4044975, -1211844829, -198846304, 286548119, 2085574084
+ };
+
+ private static final int[] EXPECTED_SEQUENCE_AFTER_JUMP = {
+ -1941342745, 535234737, -1560986946, 1333403881, -467630828,
+ -1212243215, 1924495835, 1889500660, 118588722, -444471278,
+ -984974572, 2134204567, 620921081, -929199568, -44345645,
+ -346841340, -557091335, 1023562906, -1544843001, 2014718360,
+ -186712859, -874952234, -1016908504, 953606755, -1406346322,
+ -1297454974, 1426742334, 1461035068, 206733349, 1606578263,
+ -1354963004, -604654637, 782017623, 1501746828, 853947605,
+ -1380277812, 1855551741, -1023933348, -635058958, 1752530776
+ };
+
+ private static final int[] EXPECTED_SEQUENCE_AFTER_LONG_JUMP = {
+ -643973534, -1464631510, -1204127809, 380399830, 1336312468,
+ 862647039, -970571153, -1473390944, 811398823, -598244991,
+ -1474151641, -1228756553, -166611808, -231601273, -2055417682,
+ -1102476522, 1497124960, 438167652, -657449781, -404513325,
+ -621271837, -10198296, -267651022, -296539606, -1564719261,
+ -652626768, -973911394, 1388361366, 1675611708, -1270745165,
+ -620748722, -1569788343, 831908952, 1873081673, -1058521087,
+ -26171115, -1211556401, -65210719, -1194284085, 1579466740
+ };
+
+ /**
+ * Gets a stream of reference data. Each argument consists of the seed as a long (first two ints),
+ * and the int array of the expected output from the generator.
+ *
+ * @return the reference data
+ */
+ Stream
+ *
+ * @since 1.7
+ */
+ PHILOX_4X32(ProviderBuilder.RandomSourceInternal.PHILOX_4X32),
+ /**
+ * Source of randomness is {@link org.apache.commons.rng.core.source64.Philox4x64}.
+ *
+ *
+ * @since 1.7
+ */
+ PHILOX_4X64(ProviderBuilder.RandomSourceInternal.PHILOX_4X64);
+
/** Internal identifier. */
private final ProviderBuilder.RandomSourceInternal internalIdentifier;
diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
index def9a36b4..a18450616 100644
--- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
+++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
@@ -47,6 +47,7 @@
import org.apache.commons.rng.core.source32.DotyHumphreySmallFastCounting32;
import org.apache.commons.rng.core.source32.JenkinsSmallFast32;
import org.apache.commons.rng.core.source32.L32X64Mix;
+import org.apache.commons.rng.core.source32.Philox4x32;
import org.apache.commons.rng.core.source64.SplitMix64;
import org.apache.commons.rng.core.source64.XorShift1024Star;
import org.apache.commons.rng.core.source64.XorShift1024StarPhi;
@@ -74,6 +75,7 @@
import org.apache.commons.rng.core.source64.L128X1024Mix;
import org.apache.commons.rng.core.source64.L128X128Mix;
import org.apache.commons.rng.core.source64.L128X256Mix;
+import org.apache.commons.rng.core.source64.Philox4x64;
/**
* RNG builder.
@@ -270,6 +272,7 @@ public enum RandomSourceInternal {
PCG_MCG_XSH_RS_32(PcgMcgXshRs32.class,
1,
NativeSeedType.LONG),
+
/** Source of randomness is {@link MiddleSquareWeylSequence}. */
MSWS(MiddleSquareWeylSequence.class,
// Many partially zero seeds can create low quality initial output.
@@ -439,8 +442,13 @@ private long[] createMswsSeed(UniformRandomProvider source) {
/** Source of randomness is {@link L32X64Mix}. */
L32_X64_MIX(L32X64Mix.class,
4, 2, 4,
- NativeSeedType.INT_ARRAY);
-
+ NativeSeedType.INT_ARRAY),
+ /** Source of randomness is {@link Philox4x32}. */
+ PHILOX_4X32(Philox4x32.class,
+ 6, 0, 2, NativeSeedType.INT_ARRAY),
+ /** Source of randomness is {@link Philox4x64}. */
+ PHILOX_4X64(Philox4x64 .class,
+ 6, 0, 2, NativeSeedType.LONG_ARRAY);
/** Source type. */
private final Class extends UniformRandomProvider> rng;
/** Native seed size. Used for array seeds. */
diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java
index f4f5957e4..ec312340c 100644
--- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java
+++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/RandomSourceInternalParametricTest.java
@@ -106,6 +106,8 @@ class RandomSourceInternalParametricTest {
EXPECTED_SEED_BYTES.put(RandomSourceInternal.L128_X256_MIX, longBytes * 8);
EXPECTED_SEED_BYTES.put(RandomSourceInternal.L128_X1024_MIX, longBytes * 20);
EXPECTED_SEED_BYTES.put(RandomSourceInternal.L32_X64_MIX, intBytes * 4);
+ EXPECTED_SEED_BYTES.put(RandomSourceInternal.PHILOX_4X32, intBytes * 6);
+ EXPECTED_SEED_BYTES.put(RandomSourceInternal.PHILOX_4X64, longBytes * 6);
// ... add more here.
// Verify the seed byte size is reflected in the enum javadoc for RandomSource.
}