This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rng.git


The following commit(s) were added to refs/heads/master by this push:
     new e30ab4b9 RNG-188: Add Philox4x32 and Philox4x64 random number 
generators (#191)
e30ab4b9 is described below

commit e30ab4b9a618733bdf008cd884e80f665f386673
Author: Jherek Healy <[email protected]>
AuthorDate: Thu Feb 12 13:49:32 2026 +0100

    RNG-188: Add Philox4x32 and Philox4x64 random number generators (#191)
    
    Add Philox4x32 and Philox4x64 random number generators.
---
 .../commons/rng/core/source32/Philox4x32.java      | 325 +++++++++++++++++++++
 .../commons/rng/core/source64/Philox4x64.java      | 319 ++++++++++++++++++++
 .../org/apache/commons/rng/core/ProvidersList.java |   4 +
 .../commons/rng/core/source32/Philox4x32Test.java  | 188 ++++++++++++
 .../commons/rng/core/source64/Philox4x64Test.java  | 205 +++++++++++++
 .../rng/examples/jmh/RandomSourceValues.java       |   2 +
 .../rng/examples/jmh/core/BaselineSources.java     |   2 +
 .../rng/examples/jmh/core/JumpBenchmark.java       |   8 +-
 .../jmh/core/NextDoubleGenerationPerformance.java  |   1 -
 .../jmh/simple/ConstructionPerformance.java        |  31 ++
 .../apache/commons/rng/simple/RandomSource.java    |  21 +-
 .../rng/simple/internal/ProviderBuilder.java       |  12 +-
 .../RandomSourceInternalParametricTest.java        |   2 +
 13 files changed, 1114 insertions(+), 6 deletions(-)

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 00000000..bcfad35d
--- /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 <a 
href="https://www.thesalmons.org/john/random123/papers/random123sc11.pdf";>Parallel
 Random Numbers: As Easy as 1,2,3</a>
+ * @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}
+     *
+     * <p>Increments the subsequence by 1.</p>
+     * <p>The jump size is the equivalent of 4*2<sup>96</sup> calls to
+     * {@link UniformRandomProvider#nextInt() nextInt()}.
+     */
+    @Override
+    public JumpableUniformRandomProvider longJump() {
+        final Philox4x32 copy = copy();
+        counter3++;
+        rand10();
+        resetCachedState();
+        return copy;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The jump size is the equivalent of 4*2<sup>64</sup>
+     * 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 00000000..8cb15e1c
--- /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 <a 
href="https://www.thesalmons.org/john/random123/papers/random123sc11.pdf";>Parallel
 Random Numbers: As Easy as 1,2,3</a>
+ * @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}
+     *
+     * <p>Increments the subsequence by 1.</p>
+     * <p>The jump size is the equivalent of 4*2<sup>192</sup> calls to
+     * {@link UniformRandomProvider#nextLong() nextLong()}.
+     */
+    @Override
+    public JumpableUniformRandomProvider longJump() {
+        final Philox4x64 copy = copy();
+        counter3++;
+        rand10();
+        resetCachedState();
+        return copy;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The jump size is the equivalent of 4*2<sup>128</sup>
+     * 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 04faee99..47b93928 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 java.security.SecureRandom;
 
 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.PcgXshRs32;
 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 00000000..1f19c78b
--- /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<Arguments> getReferenceData() {
+        return Stream.of(
+            Arguments.of(1234L, EXPECTED_SEQUENCE_1234),
+            Arguments.of(67280421310721L, EXPECTED_SEQUENCE_DEFAULT)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = "getReferenceData")
+    void testReferenceCode(long seed, int[] expected) {
+        RandomAssert.assertEquals(expected, new Philox4x32(new int[]{(int) 
seed, (int) (seed >>> 32)}));
+    }
+
+    @Test
+    void testConstructors() {
+        Philox4x32[] rngs = new Philox4x32[]{
+            new Philox4x32(),
+            new Philox4x32(new int[]{(int) 67280421310721L, (int) 
(67280421310721L >>> 32), 0, 0, 0, 0})
+        };
+        int refValue = rngs[0].next();
+        for (int i = 1; i < rngs.length; i++) {
+            int value = rngs[i].next();
+            assertEquals(refValue, value, "Philox4x32 initialization for i=" + 
i);
+        }
+        rngs = new Philox4x32[]{
+            new Philox4x32(new int[]{1234, 0, 1}),
+            new Philox4x32(new int[]{1234, 0, 1, 0, 0, 0}),
+        };
+        refValue = rngs[0].next();
+        for (int i = 1; i < rngs.length; i++) {
+            int value = rngs[i].next();
+            assertEquals(refValue, value, "Philox4x32 initialization for i=" + 
i);
+        }
+        rngs = new Philox4x32[]{
+            new Philox4x32(new int[]{1234}),
+            new Philox4x32(new int[]{1234, 0}),
+            new Philox4x32(new int[]{1234, 0, 0}),
+            new Philox4x32(new int[]{1234, 0, 0, 0}),
+            new Philox4x32(new int[]{1234, 0, 0, 0, 0}),
+            new Philox4x32(new int[]{1234, 0, 0, 0, 0, 0}),
+            new Philox4x32(new int[]{1234, 0, 0, 0, 0, 0, 0}),
+        };
+        refValue = rngs[0].next();
+        for (int i = 1; i < rngs.length; i++) {
+            int value = rngs[i].next();
+            assertEquals(refValue, value, "Philox4x32 initialization for i=" + 
i);
+        }
+    }
+
+    @Test
+    void testJump() {
+        RandomAssert.assertJumpEquals(EXPECTED_SEQUENCE_DEFAULT, 
EXPECTED_SEQUENCE_AFTER_JUMP, new Philox4x32());
+    }
+
+    @Test
+    void testLongJump() {
+        RandomAssert.assertLongJumpEquals(EXPECTED_SEQUENCE_DEFAULT, 
EXPECTED_SEQUENCE_AFTER_LONG_JUMP, new Philox4x32());
+    }
+
+    @Test
+    void testInternalCounter() {
+        //test of incrementCounter
+        Philox4x32 rng = new Philox4x32(new int[]{(int) 67280421310721L, (int) 
(67280421310721L >>> 32), 0xffffffff, 0, 0, 0});
+        for (int i = 0; i < 4; i++) {
+            rng.next();
+        }
+        Philox4x32 rng2 = new Philox4x32(new int[]{(int) 67280421310721L, 
(int) (67280421310721L >>> 32), 0, 1, 0, 0});
+        RandomAssert.assertNextIntEquals(1, rng, rng2);
+
+        rng = new Philox4x32(new int[]{(int) 67280421310721L, (int) 
(67280421310721L >>> 32), 0xffffffff, 0xffffffff, 0, 0});
+        for (int i = 0; i < 4; i++) {
+            rng.next();
+        }
+        rng2 = new Philox4x32(new int[]{(int) 67280421310721L, (int) 
(67280421310721L >>> 32), 0, 0, 1, 0});
+        RandomAssert.assertNextIntEquals(1, rng, rng2);
+
+        rng = new Philox4x32(new int[]{(int) 67280421310721L, (int) 
(67280421310721L >>> 32), 0xffffffff, 0xffffffff, 0xffffffff, 0});
+        for (int i = 0; i < 4; i++) {
+            rng.next();
+        }
+        rng2 = new Philox4x32(new int[]{(int) 67280421310721L, (int) 
(67280421310721L >>> 32), 0, 0, 0, 1});
+        RandomAssert.assertNextIntEquals(1, rng, rng2);
+    }
+
+    @Test
+    void testLongJumpCounter() {
+        Philox4x32 rng = new Philox4x32(new int[]{1234, 0, 0xffffffff, 
0xffffffff, 0xffffffff, 0});
+        rng.longJump();
+        Philox4x32 rng2 = new Philox4x32(new int[]{1234, 0, 0xffffffff, 
0xffffffff, 0xffffffff, 1});
+        RandomAssert.assertNextIntEquals(1, rng, rng2);
+    }
+
+    @Test
+    void testJumpCounter() {
+        Philox4x32 rng = new Philox4x32(new int[]{1234, 0, 0xffffffff, 
0xffffffff, 0xffffffff, 0});
+        rng.jump();
+        Philox4x32 rng2 = new Philox4x32(new int[]{1234, 0, 0xffffffff, 
0xffffffff, 0, 1});
+        RandomAssert.assertNextIntEquals(1, rng, rng2);
+    }
+
+}
diff --git 
a/commons-rng-core/src/test/java/org/apache/commons/rng/core/source64/Philox4x64Test.java
 
b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source64/Philox4x64Test.java
new file mode 100644
index 00000000..40523bbc
--- /dev/null
+++ 
b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source64/Philox4x64Test.java
@@ -0,0 +1,205 @@
+/*
+ * 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.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 Philox4x64Test {
+    /*
+     * 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 long[] EXPECTED_SEQUENCE_1234 = {
+        6174562084317992592L, -7568142518571726206L, -5685918792241859306L,
+        6151287208724416091L, -7525285015497232737L, -2526119061336846091L,
+        -2093373494943999176L, 2505686065164099867L, 1493954073060533072L,
+        2386252059344830309L, -3981277096068706128L, 4825385527958964709L,
+        5896359280427319232L, 2130638389021018825L, 1001529696243618836L,
+        6229771985419955916L, -8030183820248387325L, 5924921954534026109L,
+        -2430661683740471500L, -7119094164204651921L, 2451935767711287279L,
+        8424479353221384040L, -5011970289299902244L, 8792348508803652203L,
+        9109768561113011588L, 24126314432238277L, -8946976403367747978L,
+        6224712922535513938L, 8733921062828259483L, 3855129282970288492L,
+        -15371244630355388L, -3103082637265535013L, -5696416329331263984L,
+        -5000982493478729316L, -3077201427991874994L, 4502749081228919907L,
+        1930363720599024367L, -7884649763770700010L, 9162677665382083018L,
+        -1491083349895074892L
+    };
+
+    private static final long[] EXPECTED_SEQUENCE_DEFAULT = {
+        7651105821017786633L, -986727441099762072L, -1758232618730818806L,
+        -6892647654339096064L, 2003912625120555464L, 847995992558080923L,
+        2561190448322591348L, 5089323078274549892L, -6215224099279536444L,
+        2839273132443259286L, -1538091565590055595L, 2262400997606952131L,
+        4794890345824897152L, 2654554423835782039L, 5232844452212050618L,
+        4968309811735346778L, -6677562093502275256L, -2345486924693103657L,
+        2546479265789531422L, 1397198500311783458L, -3029924206687987745L,
+        3915450377326980183L, -1798629713529533718L, 7813856890368443409L,
+        -7530219763187390588L, 7752320264114599504L, 4497386005519180400L,
+        8983526426341050924L, 3157770966203722859L, 6531619948763639990L,
+        -2561361262383382379L, -7341089376366770572L, 5588349311041971766L,
+        -5547961913507498237L, 557535079196835645L, -7564858493373145745L,
+        -5687482083658299050L, -6040393957990987713L, 3376696212464637986L,
+        -4460669316800568753L
+    };
+
+    private static final long[] EXPECTED_SEQUENCE_AFTER_JUMP = {
+        -8246258822028745854L, -8108712508154901372L, 2654408923912105318L,
+        -6418747939867464899L, 8695124057120477387L, -4062778777418832523L,
+        -2866609061902870832L, -1985485291750970064L, -3716513824860297891L,
+        2708966168515282018L, -8441862438570589384L, -3332504021977608920L,
+        8275431876927502767L, -37683753608778224L, 4850475723424833501L,
+        -2864632267522668999L, -6547048909303846355L, -6804759155034193445L,
+        -1607076952104749058L, 7993605125443204784L, 7601442483044023354L,
+        -7379694727972198096L, -1902536664833944445L, -908773878773086264L,
+        -7367142976738044337L, 2845297286559921499L, 5398165976383543580L,
+        2574122219286874876L, 3780790808954139828L, -7038343169285503987L,
+        1381442423564430946L, -4910467881295472851L, 839863310680617535L,
+        3700507604505113976L, 2586645934793105407L, 1058068213122536369L,
+        -1876209807038423750L, 8994121856634859944L, 4145729862086221315L,
+        -7214331765643557828L
+    };
+
+    private static final long[] EXPECTED_SEQUENCE_AFTER_LONG_JUMP = {
+        234199833207670492L, 4847236961490835302L, 4652995647109309910L,
+        -3737386356448340712L, -5273383760715124519L, -3647957810120825499L,
+        5146817817305263920L, 5710973906845063179L, -1479449555641285865L,
+        4084674574582715314L, -5547600708256898652L, -4421640461296589483L,
+        -2968992335347510287L, -4790862279320238050L, -2473190691392812606L,
+        965983568262991078L, 601327440871821012L, 8223565539892887311L,
+        7546441310634873026L, 2825517271552261878L, 1821450327999942380L,
+        1829354945050293158L, -4141883204296663957L, 2272925410140103105L,
+        6950466720264053689L, 942049061182241074L, -423320710605977014L,
+        -7153892430601162036L, -3577327671114607603L, 2251213489013696162L,
+        -869366985991136417L, 6210870867759981069L, 8104504070499194349L,
+        -5828300645374305433L, -8988635423527025878L, 2037830179166981888L,
+        600555068878135939L, -1046966376945680441L, 9153700819137910983L,
+        6246833740445288808L
+    };
+
+    /**
+     * Gets a stream of reference data. Each argument consists of the seed as 
a long array (first two longs),
+     * and the long array of the expected output from the generator.
+     *
+     * @return the reference data
+     */
+    Stream<Arguments> getReferenceData() {
+        return Stream.of(
+            Arguments.of(new long[]{1234L, 0}, EXPECTED_SEQUENCE_1234),
+            Arguments.of(new long[]{67280421310721L, 0x9E3779B97F4A7C15L}, 
EXPECTED_SEQUENCE_DEFAULT)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = "getReferenceData")
+    void testReferenceCode(long[] seed, long[] expected) {
+        RandomAssert.assertEquals(expected, new Philox4x64(seed));
+    }
+
+    @Test
+    void testJump() {
+        RandomAssert.assertJumpEquals(EXPECTED_SEQUENCE_DEFAULT, 
EXPECTED_SEQUENCE_AFTER_JUMP,
+            new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L}));
+    }
+
+    @Test
+    void testLongJump() {
+        RandomAssert.assertLongJumpEquals(EXPECTED_SEQUENCE_DEFAULT, 
EXPECTED_SEQUENCE_AFTER_LONG_JUMP, new Philox4x64());
+    }
+
+    @Test
+    void testInternalCounter() {
+        //test of incrementCounter
+        Philox4x64 rng = new Philox4x64(new long[]{67280421310721L, 1234L, 
0xffffffffffffffffL, 0, 0, 0});
+        for (int i = 0; i < 4; i++) {
+            rng.next();
+        }
+        Philox4x64 rng2 = new Philox4x64(new long[]{67280421310721L, 1234L, 0, 
1, 0, 0});
+        RandomAssert.assertNextLongEquals(1, rng, rng2);
+
+        rng = new Philox4x64(new long[]{67280421310721L, 1234L, 
0xffffffffffffffffL, 0xffffffffffffffffL, 0, 0});
+        for (int i = 0; i < 4; i++) {
+            rng.next();
+        }
+        rng2 = new Philox4x64(new long[]{67280421310721L, 1234L, 0, 0, 1, 0});
+        RandomAssert.assertNextLongEquals(1, rng, rng2);
+
+        rng = new Philox4x64(new long[]{67280421310721L, 1234L, 
0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0});
+        for (int i = 0; i < 4; i++) {
+            rng.next();
+        }
+        rng2 = new Philox4x64(new long[]{67280421310721L, 1234L, 0, 0, 0, 1});
+        RandomAssert.assertNextLongEquals(1, rng, rng2);
+    }
+
+    @Test
+    void testLongJumpCounter() {
+        Philox4x64 rng = new Philox4x64(new long[]{1234L, 0, 
0xffffffffffffffffL, 0, 0xffffffffffffffffL, 0});
+        rng.jump();
+        Philox4x64 rng2 = new Philox4x64(new long[]{1234L, 0, 
0xffffffffffffffffL, 0, 0, 1});
+        RandomAssert.assertNextLongEquals(1, rng, rng2);
+
+        rng = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 
0xffffffffffffffffL, 0xffffffffffffffffL, 0});
+        rng.jump();
+        rng2 = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 
0xffffffffffffffffL, 0, 1});
+        RandomAssert.assertNextLongEquals(1, rng, rng2);
+
+        rng = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 
0xffffffffffffffffL, 0xffffffffffffffffL, 0});
+        rng.longJump();
+        rng2 = new Philox4x64(new long[]{1234L, 0, 0xffffffffffffffffL, 
0xffffffffffffffffL, 0xffffffffffffffffL, 1});
+        RandomAssert.assertNextLongEquals(1, rng, rng2);
+    }
+
+    @Test
+    void testConstructors() {
+        Philox4x64[] rngs = new Philox4x64[]{
+            new Philox4x64(),
+            new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0, 
0, 0, 0}),
+            new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L}),
+            new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0, 
0, 0}),
+            new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0, 
0}),
+            new Philox4x64(new long[]{67280421310721L, 0x9E3779B97F4A7C15L, 0})
+        };
+        long refValue = rngs[0].next();
+        for (int i = 1; i < rngs.length; i++) {
+            long value = rngs[i].next();
+            assertEquals(refValue, value, "Philox4x64 initialization for i=" + 
i);
+        }
+
+        rngs = new Philox4x64[]{
+            new Philox4x64(new long[]{1234L, 0, 1, 0}),
+            new Philox4x64(new long[]{1234, 0, 1}),
+            new Philox4x64(new long[]{1234, 0, 1, 0, 0, 0}),
+        };
+        refValue = rngs[0].next();
+        for (int i = 1; i < rngs.length; i++) {
+            long value = rngs[i].next();
+            assertEquals(refValue, value, "Philox4x32 initialization for i=" + 
i);
+        }
+    }
+}
diff --git 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/RandomSourceValues.java
 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/RandomSourceValues.java
index 5b0bc5b0..0d475d76 100644
--- 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/RandomSourceValues.java
+++ 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/RandomSourceValues.java
@@ -70,6 +70,8 @@ public class RandomSourceValues {
             "PCG_RXS_M_XS_64",
             "PCG_MCG_XSH_RR_32",
             "PCG_MCG_XSH_RS_32",
+            "PHILOX_4X32",
+            "PHILOX_4X64",
             "MSWS",
             "SFC_32",
             "SFC_64",
diff --git 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/BaselineSources.java
 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/BaselineSources.java
index e3b21776..23b850cb 100644
--- 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/BaselineSources.java
+++ 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/BaselineSources.java
@@ -62,6 +62,8 @@ public abstract class BaselineSources {
      */
     @Param({BASELINE,
             "JDK",
+            "PHILOX_4X32",
+            "PHILOX_4X64",
             "WELL_512_A",
             "WELL_1024_A",
             "WELL_19937_A",
diff --git 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/JumpBenchmark.java
 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/JumpBenchmark.java
index 1740bd20..2d005f7d 100644
--- 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/JumpBenchmark.java
+++ 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/JumpBenchmark.java
@@ -116,7 +116,9 @@ public class JumpBenchmark {
                 "L128_X128_MIX",
                 "L128_X256_MIX",
                 "L128_X1024_MIX",
-                "L32_X64_MIX"})
+                "L32_X64_MIX",
+                "PHILOX_4X32",
+                "PHILOX_4X64"})
         private String randomSourceName;
 
         /** {@inheritDoc} */
@@ -163,7 +165,9 @@ public class JumpBenchmark {
             "L128_X1024_MIX",
             // Requires the LCG to be advanced 2^16 rather than 1 cycle which
             // can use precomputed coefficients.
-            "L32_X64_MIX"})
+            "L32_X64_MIX",
+            "PHILOX_4X32",
+            "PHILOX_4X64"})
         private String randomSourceName;
 
 
diff --git 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/NextDoubleGenerationPerformance.java
 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/NextDoubleGenerationPerformance.java
index a474ffc2..545ae199 100644
--- 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/NextDoubleGenerationPerformance.java
+++ 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/core/NextDoubleGenerationPerformance.java
@@ -21,7 +21,6 @@ import org.apache.commons.rng.UniformRandomProvider;
 import org.openjdk.jmh.annotations.Benchmark;
 import org.openjdk.jmh.annotations.Scope;
 import org.openjdk.jmh.annotations.State;
-
 /**
  * Executes benchmark to compare the speed of generation of random numbers 
from the
  * various source providers for {@link UniformRandomProvider#nextDouble()}.
diff --git 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ConstructionPerformance.java
 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ConstructionPerformance.java
index ac912d85..0cdcf679 100644
--- 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ConstructionPerformance.java
+++ 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/simple/ConstructionPerformance.java
@@ -42,6 +42,7 @@ import org.apache.commons.rng.core.source32.JDKRandom;
 import org.apache.commons.rng.core.source32.KISSRandom;
 import org.apache.commons.rng.core.source32.MersenneTwister;
 import org.apache.commons.rng.core.source32.MultiplyWithCarry256;
+import org.apache.commons.rng.core.source32.Philox4x32;
 import org.apache.commons.rng.core.source32.Well1024a;
 import org.apache.commons.rng.core.source32.Well19937a;
 import org.apache.commons.rng.core.source32.Well19937c;
@@ -53,6 +54,7 @@ import 
org.apache.commons.rng.core.source32.XoRoShiRo64StarStar;
 import org.apache.commons.rng.core.source32.XoShiRo128Plus;
 import org.apache.commons.rng.core.source32.XoShiRo128StarStar;
 import org.apache.commons.rng.core.source64.MersenneTwister64;
+import org.apache.commons.rng.core.source64.Philox4x64;
 import org.apache.commons.rng.core.source64.SplitMix64;
 import org.apache.commons.rng.core.source64.TwoCmres;
 import org.apache.commons.rng.core.source64.XoRoShiRo128Plus;
@@ -301,6 +303,7 @@ public class ConstructionPerformance {
             case XO_RO_SHI_RO_64_SS:
             case XO_SHI_RO_128_PLUS:
             case XO_SHI_RO_128_SS:
+            case PHILOX_4X32:
                 return INT_ARRAY_SEEDS;
             case XOR_SHIFT_1024_S:
             case XOR_SHIFT_1024_S_PHI:
@@ -311,6 +314,7 @@ public class ConstructionPerformance {
             case XO_SHI_RO_256_SS:
             case XO_SHI_RO_512_PLUS:
             case XO_SHI_RO_512_SS:
+            case PHILOX_4X64:
                 return LONG_ARRAY_SEEDS;
             default:
                 throw new AssertionError("Unknown native seed");
@@ -368,6 +372,9 @@ public class ConstructionPerformance {
             case XO_SHI_RO_512_PLUS:
             case XO_SHI_RO_512_SS:
                 return 8;
+            case PHILOX_4X32:
+            case PHILOX_4X64:
+                return 6;
             default:
                 throw new AssertionError("Unknown native seed size");
             }
@@ -398,6 +405,7 @@ public class ConstructionPerformance {
             case XO_RO_SHI_RO_64_SS:
             case XO_SHI_RO_128_PLUS:
             case XO_SHI_RO_128_SS:
+            case PHILOX_4X32:
                 return 4; // int
             case SPLIT_MIX_64:
             case XOR_SHIFT_1024_S:
@@ -409,6 +417,7 @@ public class ConstructionPerformance {
             case XO_SHI_RO_256_SS:
             case XO_SHI_RO_512_PLUS:
             case XO_SHI_RO_512_SS:
+            case PHILOX_4X64:
                 return 8; // long
             default:
                 throw new AssertionError("Unknown native seed element byte 
size");
@@ -450,6 +459,8 @@ public class ConstructionPerformance {
             case XO_SHI_RO_256_SS: return 
RandomSourceInternal.XO_SHI_RO_256_SS;
             case XO_SHI_RO_512_PLUS: return 
RandomSourceInternal.XO_SHI_RO_512_PLUS;
             case XO_SHI_RO_512_SS: return 
RandomSourceInternal.XO_SHI_RO_512_SS;
+            case PHILOX_4X32: return RandomSourceInternal.PHILOX_4X32;
+            case PHILOX_4X64: return RandomSourceInternal.PHILOX_4X64;
             default:
                 throw new AssertionError("Unknown random source internal");
             }
@@ -793,6 +804,26 @@ public class ConstructionPerformance {
         }
     }
 
+    /**
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void newPhilox4x32(Blackhole bh) {
+        for (int i = 0; i < SEEDS; i++) {
+            bh.consume(new Philox4x32(INT_ARRAY_SEEDS[i]));
+        }
+    }
+
+    /**
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void newPhilox4x64(Blackhole bh) {
+        for (int i = 0; i < SEEDS; i++) {
+            bh.consume(new Philox4x64(LONG_ARRAY_SEEDS[i]));
+        }
+    }
+
     /**
      * Create a new instance using reflection with a cached constructor.
      *
diff --git 
a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java
 
b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java
index 2acd77f1..0cb2cc4f 100644
--- 
a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java
+++ 
b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/RandomSource.java
@@ -661,7 +661,26 @@ public enum RandomSource {
      * </ul>
      * @since 1.5
      */
-    L32_X64_MIX(ProviderBuilder.RandomSourceInternal.L32_X64_MIX);
+    L32_X64_MIX(ProviderBuilder.RandomSourceInternal.L32_X64_MIX),
+    /**
+     * Source of randomness is {@link 
org.apache.commons.rng.core.source32.Philox4x32}.
+     * <ul>
+     *  <li>Native seed type: {@code int[]}.</li>
+     *  <li>Native seed size: 6.</li>
+     * </ul>
+     * @since 1.7
+     */
+    PHILOX_4X32(ProviderBuilder.RandomSourceInternal.PHILOX_4X32),
+    /**
+     * Source of randomness is {@link 
org.apache.commons.rng.core.source64.Philox4x64}.
+     * <ul>
+     *  <li>Native seed type: {@code long[]}.</li>
+     *  <li>Native seed size: 6.</li>
+     * </ul>
+     * @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 def9a36b..a1845061 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.PcgMcgXshRs32;
 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.L64X256Mix;
 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 final class ProviderBuilder {
         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 @@ public final class ProviderBuilder {
         /** 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 f4f5957e..ec312340 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.
     }

Reply via email to